]> pd.if.org Git - zpackage/blob - src/search.c
add tracing to zpm-search
[zpackage] / src / search.c
1 #define _POSIX_C_SOURCE 200809L
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <stdarg.h>
6 #include <stdio.h>
7
8 #include <glob.h>
9
10 #include "zpm.h"
11 #include "lib/jsw/jsw_hlib.h"
12 #include "lib/jsw/jsw_atree.h"
13
14 /*
15  * find packages, and lib deps
16  */
17
18 struct pkgloc {
19         char *id;
20         char *file;
21         int info;
22         int installed;
23 };
24
25 struct search_ctl {
26         char *localdb, *repodir, *pkgdir;
27         struct zpm *zpmdb;
28         glob_t repos;
29         int matchallpkgfile;
30         int matchinstalled;
31         int suppressinstalled;
32         int onlylocalinstalled;
33         int foundonly;
34         int verbose;
35         int dbrepos;
36 };
37
38 void trace(int level, struct search_ctl *ctl, char *fmt, ...) {
39         va_list ap;
40
41         if (level <= ctl->verbose) {
42                 va_start(ap, fmt);
43                 vfprintf(stderr, fmt, ap);
44                 va_end(ap);
45         }
46 }
47
48 char *pathcat(char *dir, char *path) {
49         size_t dirlen = 0, pathlen = 0;
50         char *cat;
51
52         /* chop off trailing / on dir */
53         if (dir) {
54                 dirlen = strlen(dir);
55                 while (dirlen && dir[dirlen-1] == '/') {
56                         dirlen--;
57                 }
58         }
59
60         if (path) {
61                 pathlen = strlen(path);
62                 while (*path && *path == '/') {
63                         path++;
64                         pathlen--;
65                 }
66         }
67
68         cat = malloc(dirlen + pathlen + 2);
69         if (cat) {
70                 strncpy(cat, dir, dirlen);
71                 cat[dirlen] = '/';
72                 strcpy(cat+dirlen+1, path);
73         }
74         return cat;
75 }
76
77 char *checkfile(char *pkgstr, char *path, int orgreater) {
78         struct zpm pkgfile;
79         char *pkgid = 0;
80
81         if (!zpm_open(&pkgfile, path)) {
82                 return NULL;
83         }
84         
85         if (orgreater) {
86                 pkgid = zpm_findpkg_range(&pkgfile, pkgstr, 0, 0, 0);
87         } else {
88                 pkgid = zpm_findpkg(&pkgfile, pkgstr, NULL);
89         }
90         zpm_close(&pkgfile);
91
92         return pkgid;
93 }
94
95 char *checkfileforlib(char *soname, char *path) {
96         struct zpm pkgfile;
97         char *pkgid = 0;
98
99         if (!zpm_open(&pkgfile, path)) {
100                 return NULL;
101         }
102         
103         pkgid = zpm_findlib(&pkgfile, soname, NULL);
104         if (pkgfile.error) {
105                 fprintf(stderr, "sql error: %s\n", pkgfile.errmsg);
106         }
107         zpm_close(&pkgfile);
108
109         return pkgid;
110 }
111
112 int find_globs(struct search_ctl *opt) {
113         glob_t *repos;
114         int rv;
115
116         repos = &opt->repos;
117         repos->gl_offs = 0;
118
119         int globopts = 0;
120
121         if (opt->repodir) {
122                 char *globstr = pathcat(opt->repodir, "*.repo");
123                 rv = glob(globstr, globopts, NULL, repos);
124                 switch (rv) {
125                         case GLOB_NOSPACE:
126                                 fprintf(stderr, "glob no mem\n");
127                                 return 0;
128                         case GLOB_ABORTED:
129                                 fprintf(stderr, "glob abort\n");
130                                 return 0;
131                         case GLOB_NOMATCH:
132                                 break;
133                         case 0:
134                                 break;
135                         default:
136                                 break;
137                 }
138                 globopts = GLOB_APPEND;
139                 free(globstr);
140         }
141
142         if (opt->pkgdir) {
143                 char *globstr = pathcat(opt->pkgdir, "*.zpm");
144                 rv = glob(globstr, globopts, NULL, repos);
145                 switch (rv) {
146                         case GLOB_NOSPACE:
147                                 fprintf(stderr, "glob no mem\n");
148                                 return 0;
149                         case GLOB_ABORTED:
150                                 fprintf(stderr, "glob abort\n");
151                                 return 0;
152                         case GLOB_NOMATCH:
153                                 break;
154                         case 0:
155                                 break;
156                         default:
157                                 break;
158                 }
159                 free(globstr);
160         }
161
162         return 1;
163 }
164
165 /*
166  * find a package and packagefile that supplies a library
167  */
168 int find_lib(char *soname, struct search_ctl *opt, struct pkgloc *pkg) {
169         char *latest = 0;
170         char *installed = 0, *found = 0;
171         char *pkgfile = 0;
172         int rv;
173         unsigned int i;
174
175         if (opt->localdb) {
176                 installed = zpm_findlib(opt->zpmdb, soname, "status = 'installed'");
177                 if (installed) {
178                         if (opt->verbose > 1) {
179                                 fprintf(stderr, "library %s installed via %s\n", soname, installed);
180                         }
181                         /* we're done, the lib is installed */
182                         return 2;
183                 }
184         }
185
186         for (i = 0; i < opt->repos.gl_pathc; i++) {
187                 if (opt->verbose > 1) {
188                         fprintf(stderr, "checking %s for %s\n", opt->repos.gl_pathv[i], soname);
189                 }
190                 found = checkfileforlib(soname, opt->repos.gl_pathv[i]);
191                 if (found) {
192                         if (opt->verbose > 1) {
193                                 fprintf(stderr, "found %s\n", found);
194                         }
195                         rv = zpm_vercmp(found, latest);
196                         if (rv == 1) {
197                                 latest = found;
198                                 free(pkgfile);
199                                 pkgfile = strdup(opt->repos.gl_pathv[i]);
200                         }
201                 } else if (opt->verbose > 1) {
202                         fprintf(stderr, "not found\n");
203                 }
204         }
205
206         if (latest && pkgfile) {
207                 pkg->id = strdup(latest);
208                 pkg->file = pkgfile;
209                 return 1;
210         }
211
212         return 0;
213 }
214
215 struct pkgloc *find_atleast_package(char *pkgstr, struct search_ctl *opt, int stopifinstalled) {
216         char *latest = 0;
217         char *installed = 0, *found = 0;
218         char *pkgfile = 0;
219         struct pkgloc *pkg = 0;
220         int rv;
221         unsigned int i;
222
223         if (opt->localdb) {
224                 installed = zpm_findpkg_range(opt->zpmdb, pkgstr, 0, "status = 'installed'", 0);
225                 if (installed) {
226                         latest = installed;
227                         pkgfile = opt->zpmdb->path;
228                         if (stopifinstalled) {
229                                 pkg = malloc(sizeof *pkg);
230                                 pkg->id = strdup(latest);
231                                 pkg->file = pkgfile;
232                                 pkg->installed = 1;
233                                 return pkg;
234                         }
235                 }
236                 if (!opt->onlylocalinstalled) {
237                         found = zpm_findpkg_range(opt->zpmdb, pkgstr, 0, NULL, 0);
238                         if (found) {
239                                 rv = zpm_vercmp(found, latest);
240                                 if (rv == 1) {
241                                         latest = found;
242                                         pkgfile = opt->zpmdb->path;
243                                 }
244                         }
245                 }
246         }
247
248         for (i = 0; i < opt->repos.gl_pathc; i++) {
249                 if (!opt->matchallpkgfile
250                                 && !strstr(opt->repos.gl_pathv[i], pkgstr)
251                                 && !strstr(opt->repos.gl_pathv[i], ".repo")
252                                 ) {
253                         continue;
254                 }
255                 found = checkfile(pkgstr, opt->repos.gl_pathv[i], 1);
256                 if (found) {
257                         rv = zpm_vercmp(found, latest);
258                         if (rv == 1) {
259                                 latest = found;
260                                 pkgfile = strdup(opt->repos.gl_pathv[i]);
261                         }
262                 }
263         }
264
265         if (latest && pkgfile) {
266                 pkg = malloc(sizeof *pkg);
267                 pkg->id = strdup(latest);
268                 pkg->file = pkgfile;
269                 pkg->installed = 0;
270                 if (installed) {
271                        pkg->installed = !strcmp(latest, installed);
272                 }
273         }
274
275         return pkg;
276 }
277
278 struct pkgloc *find_package(char *pkgstr, struct search_ctl *opt) {
279         char *latest = 0;
280         char *installed = 0, *found = 0;
281         char *pkgfile = 0;
282         struct pkgloc *pkg = 0;
283         int rv;
284         unsigned int i;
285
286         if (opt->localdb) {
287                 trace(1, opt, "checking local database for installed %s\n", pkgstr);
288                 installed = zpm_findpkg(opt->zpmdb, pkgstr, "status = 'installed'");
289                 if (installed) {
290                         trace(1, opt, "found installed %s as %s\n", pkgstr, installed);
291                         latest = installed;
292                         pkgfile = opt->zpmdb->path;
293                 }
294
295                 if (!opt->onlylocalinstalled) {
296                         trace(1, opt, "checking local database for any %s\n", pkgstr);
297                         found = zpm_findpkg(opt->zpmdb, pkgstr, NULL);
298                         if (found) {
299                                 trace(1, opt, "found %s as %s\n", pkgstr, installed);
300                                 rv = zpm_vercmp(found, latest);
301                                 if (rv == 1) {
302                                         latest = found;
303                                         pkgfile = opt->zpmdb->path;
304                                 }
305                         }
306                 }
307
308 #if 0
309                 if (opt->dbrepos) {
310                         zpm_foreach_repo(opt->zpmdb, search_repo, pkg)
311                 }
312 #endif
313         }
314
315         trace(1, opt, "checking repositories\n");
316         for (i = 0; i < opt->repos.gl_pathc; i++) {
317                 trace(1, opt, "checking repo/pkg %s\n", opt->repos.gl_pathv[i]);
318                 if (!opt->matchallpkgfile
319                                 && !strstr(opt->repos.gl_pathv[i], pkgstr)
320                                 && !strstr(opt->repos.gl_pathv[i], ".repo")
321                                 ) {
322                         trace(1, opt, "skipping repo/pkg %s\n", opt->repos.gl_pathv[i]);
323                         continue;
324                 }
325                 found = checkfile(pkgstr, opt->repos.gl_pathv[i], 0);
326                 trace(1, opt, "found %s\n", found);
327                 if (found) {
328                         trace(1, opt, "comparing found %s to latest (%s)\n", found, latest);
329                         rv = zpm_vercmp(found, latest);
330                         if (rv == 1) {
331                                 latest = found;
332                                 pkgfile = strdup(opt->repos.gl_pathv[i]);
333                         }
334                         trace(1, opt, "latest %s\n", latest);
335                 }
336         }
337
338         if (latest && pkgfile) {
339                 pkg = malloc(sizeof *pkg);
340                 pkg->id = strdup(latest);
341                 pkg->file = pkgfile;
342                 pkg->installed = 0;
343                 if (installed) {
344                        pkg->installed = !strcmp(latest, installed);
345                 }
346                 trace(1, opt, "set up pkgloc = {%s, %s, %d}\n", pkg->id, pkg->file, pkg->installed);
347         }
348
349         return pkg;
350 }
351
352 char *pkglibsneeded = "\
353 with pkglibs as (\
354                 select distinct EN.needed as soname, PF.pkgid\
355                 from elfneeded EN\
356                 join packagefiles_pkgid PF on PF.hash = EN.file\
357                 ),\
358      pkgprovides as (\
359                      select distinct EL.soname, PF.pkgid\
360                      from elflibraries EL\
361                      join packagefiles_pkgid PF on PF.hash = EL.file\
362                     )\
363      select distinct PL.soname, PP.soname is not null as selfsatisfied\
364      from pkglibs PL\
365      left join pkgprovides PP on PL.pkgid = PP.pkgid and PL.soname = PP.soname\
366      where PL.pkgid = %Q\
367      ";
368
369 char *pkgdeps = "select requires from packagedeps_pkgid where pkgid = %Q";
370
371 static int afind_strcmp(const void *a, const void *b) {
372         return strcmp(a, b);
373 }
374
375 void checklibs(struct search_ctl *opts,
376                 jsw_hash_t *check, jsw_hash_t *forlibs, jsw_hash_t *nfound) {
377         char *pkgid = 0, *pkgfile = 0;
378         struct zpm src;
379
380         /* checked_libs is a list of checked sonames
381          * checked is a list of checked packages
382          * needed is list of libs for package, or list of package deps
383          */
384         jsw_atree_t *needed = 0, *checked, *checked_libs;
385
386         int libs, count;
387         jsw_atrav_t *i;
388         char *soname, *package;
389         
390         checked = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free);
391         checked_libs = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free);
392
393         while (jsw_hsize(check) > 0) {
394                 free(pkgid);
395                 free(pkgfile);
396
397                 jsw_hreset(check);
398
399                 pkgid = strdup(jsw_hkey(check));
400                 pkgfile = strdup(jsw_hitem(check));
401
402                 if (!pkgid || !pkgfile) {
403                         break;
404                 }
405
406                 jsw_herase(check, pkgid);
407
408                 if (jsw_afind(checked, pkgid)) {
409                         /* already checked this one */
410                         /* fprintf(stderr, "already checked %s\n", pkgid); */
411                         continue;
412                 }
413
414                 fprintf(stderr, "checking libs for %s\n", pkgid);
415                 /* we do this now so we catch self deps */
416                 if (!jsw_ainsert(checked, pkgid)) {
417                         fprintf(stderr, "checked insert failed\n");
418                         exit(EXIT_FAILURE);
419                 }
420
421                 if (!jsw_afind(checked, pkgid)) {
422                         /* already checked this one */
423                         fprintf(stderr, "checked find failed\n");
424                         exit(EXIT_FAILURE);
425                 }
426
427                 /* get the libraries and packages needed by this package */
428                 if (!zpm_open(&src, pkgfile)) {
429                         fprintf(stderr, "can't zpm open %s\n", pkgfile);
430                         break;
431                 }
432
433                 jsw_adelete(needed);
434                 needed = jsw_anew_str();
435                 count = zpm_packages_needed(&src, pkgid, needed);
436                 if (count) {
437                         struct pkgloc *pkg = 0;
438                         i = jsw_atnew();
439
440                         if (pkg) {
441                                 free(pkg->file);
442                                 free(pkg->id);
443                                 free(pkg);
444                         }
445
446                         for (package = jsw_atfirst(i, needed); package; package = jsw_atnext(i)) {
447                                 pkg = find_atleast_package(package, opts, 1);
448                                 if (!pkg || pkg->installed) {
449                                         continue;
450                                 }
451                                 /* look for a package at least that version */
452                                 /* if package is installed, or already
453                                  * handled, skip.  Otherwise, add it
454                                  * to the list of installed
455                                  */
456                         }
457                         if (pkg) {
458                                 free(pkg->file);
459                                 free(pkg->id);
460                                 free(pkg);
461                         }
462                 }
463
464                 jsw_adelete(needed);
465                 needed = jsw_anew_str();
466                 libs = zpm_libraries_needed(&src, pkgid, needed);
467                 zpm_close(&src);
468
469                 if (libs == 0) {
470                         continue;
471                 }
472
473                 i = jsw_atnew();
474
475                 for (soname = jsw_atfirst(i, needed); soname; soname = jsw_atnext(i)) {
476                         int found;
477                         struct pkgloc pkginfo;
478
479                         /* if it's in checked_libs, we've already looked at
480                          * this one */
481                         if (jsw_afind(checked_libs, soname)) {
482                                 if (opts->verbose > 1) {
483                                         fprintf(stderr, "already checked %s\n", soname);
484                                 }
485                                 continue;
486                         }
487 #if 0
488                         if (opts->verbose > 1) {
489                                 fprintf(stderr, "checking for %s\n", soname);
490                         }
491 #endif
492
493                         /* haven't found this soname */
494                         jsw_ainsert(checked_libs, soname);
495
496                         found = find_lib(soname, opts, &pkginfo);
497                         if (!found) {
498                                 if (!jsw_hinsert(nfound, soname, pkgid)) {
499                                         fprintf(stderr,"insert error\n");
500                                 }
501                                 continue;
502                         }
503
504                         if (found == 1) {
505                                 /* if not alreacy checked, add to check */
506                                 if (!jsw_afind(checked, pkginfo.file)) {
507                                         jsw_hinsert(check,pkginfo.id,pkginfo.file);
508                                 }
509                                 /* shouldn't be in there already */
510                                 jsw_hinsert(forlibs, pkginfo.id, pkginfo.file);
511                                 free(pkginfo.file);
512                                 free(pkginfo.id);
513                         } 
514                         /* otherwise found == 2, so just continue */
515                 }
516         }
517         free(pkgid);
518         free(pkgfile);
519 }
520
521 void print_pkghash(jsw_hash_t *hash, int json, int idonly) {
522         const char *pkgid, *file;
523         char *fmt;
524         char *sep = "\n";
525         int count = 0;
526         char *start = "";
527         char *end = "";
528
529         if (json) {
530                 sep = ", ";
531                 if (idonly) {
532                         fmt = "\"%s\"";
533                         start = "[ ";
534                         end = " ]\n";
535                 } else {
536                         fmt = "\"%s\": \"%s\"";
537                         start = "{ ";
538                         end = " }\n";
539                 }
540         } else {
541                 if (idonly) {
542                         fmt = "%s";
543                         start = "";
544                         end = "";
545                 } else {
546                         fmt = "%s:%s";
547                         start = "";
548                         end = "";
549                 }
550         }
551
552         printf("%s", start);
553
554         if (jsw_hsize(hash) > 0) {
555                 for (jsw_hreset(hash); jsw_hitem(hash); jsw_hnext(hash)) {
556                         pkgid = jsw_hkey(hash);
557                         file = jsw_hitem(hash);
558                         if (count++) {
559                                 printf("%s", sep);
560                         }
561                         if (idonly) {
562                                 printf(fmt, pkgid);
563                         } else {
564                                 printf(fmt, pkgid, file);
565                         }
566                 }
567                 if (!json) printf("\n");
568         }
569         printf("%s", end);
570 }
571
572 int main(int ac, char *av[]) {
573         int option, argn, rv = 0;
574         int findlibs = 0, json = 0, idonly = 0;
575         struct pkgloc *found;
576         jsw_hash_t *packages, *check, *forlibs, *nolib;
577         jsw_atree_t *nfound;
578         struct search_ctl opt = { 0 };
579         struct zpm localdb;
580
581         opt.dbrepos = 1;
582
583         opt.localdb = getenv("ZPMDB");
584         if (!opt.localdb) opt.localdb = "/var/lib/zpm/local.db";
585         opt.pkgdir = getenv("ZPM_PACKAGE_DIR");
586         if (!opt.pkgdir) opt.pkgdir = "/var/lib/zpm/packages";
587         opt.repodir = getenv("ZPM_REPO_DIR");
588         if (!opt.repodir) opt.repodir = "/var/lib/zpm/repo";
589
590         /* -l also find packages needed for libs
591          * -j output json
592          *  -q quiet - suppress output
593          *  -v verbose - 
594          *  -d debug - trace each step
595          *
596          *  environment:
597          *  ZPMDB - path to localdb, /var/lib/zpm/local.db
598          *  ZPM_REPO_DIR - path to *.repo files, '/var/lib/zpm/repo'
599          *  ZPM_PACKAGE_DIRS - : separated paths to *.zpm files,
600          *  '/var/lib/zpm/packages'
601          *  ZPM_ROOT_DIR :- prepends to above paths
602          *
603          *  -M (missing) invert the output and only output missing
604          *  outputs
605          *  packages found (suppress with -P)
606          *  package strings not found (suppress with -S)
607          *  library dependency packages needed and found (suppress with -N)
608          *  library dependency packages already installed (suppress with -H)
609          *  library dependency packages in install set (suppress with -I)
610          *  library dependency sonames not found (suppress with -L)
611          *
612          *  exit value is 0 if no missing libs and no bad package strings
613          *  exit value is non zero otherwise
614          */
615
616         int output = 1;
617         while ((option = getopt(ac, av, "fljqPRDvp:r:d:MiIOn")) != -1) {
618                 switch (option) {
619                         case 'l': findlibs = 1; break;
620                         case 'j': json = 1; break;
621                         case 'q': output = 0; break;
622                                   /* show installed files */
623                         case 'i': opt.matchinstalled = 1; break;
624                         case 'I': opt.suppressinstalled = 1; break;
625                                   /* only find localdb pkgs if installed */
626                         case 'O': opt.onlylocalinstalled = 1; break;
627                         case 'f': opt.foundonly = 1; break;
628                         case 'd': opt.localdb = optarg; break;
629                         case 'p': opt.pkgdir = optarg; break;
630                         case 'r': opt.repodir = optarg; break;
631                         case 'P': opt.pkgdir = 0; break;
632                         case 'R': opt.repodir = 0; break;
633                         case 'D': opt.localdb = 0; break;
634                                   /* matchallpkgfile means look at
635                                    * all .zpm files, not just ones
636                                    * that have the pkgid string prefix
637                                    */
638                         case 'M': opt.matchallpkgfile = 1; break;
639                         case 'v': opt.verbose++; break;
640                         case 'n': idonly = 1; break;
641                         default:
642                                   exit(EXIT_FAILURE);
643                                   break;
644                 }
645         }
646         argn = optind;
647
648         if (opt.localdb) {
649                 if (zpm_open(&localdb, NULL)) {
650                         opt.zpmdb = &localdb;
651                 } else {
652                         fprintf(stderr, "can't open localdb\n");
653                         return 2;
654                 }
655         }
656
657         if (!find_globs(&opt)) {
658                 fprintf(stderr, "bad globs\n");
659                 return 3;
660         }
661
662         if (opt.verbose > 1) {
663                 unsigned int i;
664                 fprintf(stderr, "globs:");
665                 for (i = 0; i < opt.repos.gl_pathc; i++) {
666                         fprintf(stderr, " %s", opt.repos.gl_pathv[i]);
667                 }
668                 fprintf(stderr, "\n");
669         }
670
671         /* direct packages asked for */
672         packages = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
673                         (itemdup_f)strdup,free,free);
674
675         /* packages we need to check for libs */
676         check = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
677                         (itemdup_f)strdup,free,free);
678
679         /* packages we will also need for library dependences */
680         forlibs = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
681                         (itemdup_f)strdup,free,free);
682
683         /* libraries we didn't find */
684         nolib = jsw_hnew(ac,NULL,afind_strcmp,(keydup_f)strdup,
685                         (itemdup_f)strdup,free,free);
686
687         /* packages asked for that we can't find */
688         nfound = jsw_anew(afind_strcmp, (dup_f)strdup, (rel_f)free);
689
690         int arg;
691         for (arg = argn; arg < ac; arg++) {
692                 found = find_package(av[arg], &opt);
693                 if (found && (opt.matchinstalled || !found->installed)) {
694                         if (found->installed && opt.suppressinstalled) {
695                                 continue;
696                         }
697                         jsw_hinsert(packages, found->id, found->file);
698                         jsw_hinsert(check, found->id, found->file);
699                         free(found->id);
700                         free(found->file);
701                         free(found);
702                 } else {
703                         jsw_ainsert(nfound, av[arg]);
704                 }
705         }
706
707         if (findlibs) {
708                 checklibs(&opt, check, forlibs, nolib);
709                 /* remove from forlibs anything already explicitly
710                  * in packages
711                  */
712         }
713
714         if (output) {
715                 if (jsw_hsize(packages)) {
716                         print_pkghash(packages, json, idonly);
717                 }
718                 if (jsw_hsize(forlibs)) {
719                         print_pkghash(forlibs, json, idonly);
720                 }
721         }
722
723         if (jsw_hsize(nolib) > 0) {
724                 for (jsw_hreset(nolib); jsw_hitem(nolib); jsw_hnext(nolib)) {
725                         const char *pkgid, *file;
726                         pkgid = jsw_hkey(nolib);
727                         file = jsw_hitem(nolib);
728                         fprintf(stderr, "no lib found %s:%s\n", pkgid, file);
729                 }
730                 rv = 1;
731         }
732
733         if (jsw_asize(nfound) > 0 && !opt.foundonly) {
734                 jsw_atrav_t *i;
735                 i = jsw_atnew();
736                 char *pkgstr;
737
738                 for (pkgstr = jsw_atfirst(i, nfound); pkgstr; pkgstr = jsw_atnext(i)) {
739                         fprintf(stderr, "%s: not found\n", pkgstr);
740                 }
741                 rv = 1;
742         }
743
744         return rv;
745 }