d8707730884251a205314e79c4d129ca825c841e
[platform/upstream/rpm.git] / lib / query.c
1 /** \ingroup rpmcli
2  * \file lib/query.c
3  * Display tag values from package metadata.
4  */
5
6 #include "system.h"
7
8 #ifndef PATH_MAX
9 /*@-incondefs@*/        /* FIX: long int? */
10 # define PATH_MAX 255
11 /*@=incondefs@*/
12 #endif
13
14 #include <rpmcli.h>
15 #include <rpmbuild.h>
16
17 #include "rpmdb.h"
18 #include "rpmfi.h"
19 #include "rpmts.h"
20
21 #include "manifest.h"
22
23 #include "debug.h"
24
25 /*@access rpmdbMatchIterator@*/         /* XXX compared with NULL */
26 /*@access Header@*/                     /* XXX compared with NULL */
27 /*@access FD_t@*/                       /* XXX compared with NULL */
28
29 /**
30  */
31 static void printFileInfo(char * te, const char * name,
32                           unsigned int size, unsigned short mode,
33                           unsigned int mtime,
34                           unsigned short rdev, unsigned int nlink,
35                           const char * owner, const char * group,
36                           const char * linkto)
37         /*@modifies *te @*/
38 {
39     char sizefield[15];
40     char ownerfield[8+1], groupfield[8+1];
41     char timefield[100];
42     time_t when = mtime;  /* important if sizeof(int_32) ! sizeof(time_t) */
43     struct tm * tm;
44     static time_t now;
45     static struct tm nowtm;
46     const char * namefield = name;
47     char * perms = rpmPermsString(mode);
48
49     /* On first call, grab snapshot of now */
50     if (now == 0) {
51         now = time(NULL);
52         tm = localtime(&now);
53 /*@-boundsread@*/
54         if (tm) nowtm = *tm;    /* structure assignment */
55 /*@=boundsread@*/
56     }
57
58     strncpy(ownerfield, owner, sizeof(ownerfield));
59     ownerfield[sizeof(ownerfield)-1] = '\0';
60
61     strncpy(groupfield, group, sizeof(groupfield));
62     groupfield[sizeof(groupfield)-1] = '\0';
63
64     /* this is normally right */
65     sprintf(sizefield, "%12u", size);
66
67     /* this knows too much about dev_t */
68
69     if (S_ISLNK(mode)) {
70         char *nf = alloca(strlen(name) + sizeof(" -> ") + strlen(linkto));
71         sprintf(nf, "%s -> %s", name, linkto);
72         namefield = nf;
73     } else if (S_ISCHR(mode)) {
74         perms[0] = 'c';
75         sprintf(sizefield, "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
76                         ((unsigned)rdev & 0xff));
77     } else if (S_ISBLK(mode)) {
78         perms[0] = 'b';
79         sprintf(sizefield, "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
80                         ((unsigned)rdev & 0xff));
81     }
82
83     /* Convert file mtime to display format */
84     tm = localtime(&when);
85     timefield[0] = '\0';
86     if (tm != NULL)
87     {   const char *fmt;
88         if (now > when + 6L * 30L * 24L * 60L * 60L ||  /* Old. */
89             now < when - 60L * 60L)                     /* In the future.  */
90         {
91         /* The file is fairly old or in the future.
92          * POSIX says the cutoff is 6 months old;
93          * approximate this by 6*30 days.
94          * Allow a 1 hour slop factor for what is considered "the future",
95          * to allow for NFS server/client clock disagreement.
96          * Show the year instead of the time of day.
97          */        
98             fmt = "%b %e  %Y";
99         } else {
100             fmt = "%b %e %H:%M";
101         }
102         (void)strftime(timefield, sizeof(timefield) - 1, fmt, tm);
103     }
104
105     sprintf(te, "%s %4d %-8s%-8s %10s %s %s", perms,
106         (int)nlink, ownerfield, groupfield, sizefield, timefield, namefield);
107     perms = _free(perms);
108 }
109
110 /**
111  */
112 static inline /*@null@*/ const char * queryHeader(Header h, const char * qfmt)
113         /*@*/
114 {
115     const char * errstr = "(unkown error)";
116     const char * str;
117
118 /*@-modobserver@*/
119     str = headerSprintf(h, qfmt, rpmTagTable, rpmHeaderFormats, &errstr);
120 /*@=modobserver@*/
121     if (str == NULL)
122         rpmError(RPMERR_QFMT, _("incorrect format: %s\n"), errstr);
123     return str;
124 }
125
126 int showQueryPackage(QVA_t qva, /*@unused@*/ rpmts ts, Header h)
127 {
128     int scareMem = 1;
129     rpmfi fi = NULL;
130     char * t, * te;
131     char * prefix = NULL;
132     int rc = 0;         /* XXX FIXME: need real return code */
133     int nonewline = 0;
134     int i;
135
136     te = t = xmalloc(BUFSIZ);
137 /*@-boundswrite@*/
138     *te = '\0';
139 /*@=boundswrite@*/
140
141     if (!(qva->qva_flags & _QUERY_FOR_BITS) && qva->qva_queryFormat == NULL)
142     {
143         const char * name, * version, * release;
144         (void) headerNVR(h, &name, &version, &release);
145 /*@-boundswrite@*/
146         te = stpcpy(te, name);
147         te = stpcpy( stpcpy(te, "-"), version);
148         te = stpcpy( stpcpy(te, "-"), release);
149 /*@=boundswrite@*/
150         goto exit;
151     }
152
153     if (qva->qva_queryFormat != NULL) {
154         const char * str = queryHeader(h, qva->qva_queryFormat);
155         nonewline = 1;
156         /*@-branchstate@*/
157         if (str) {
158             size_t tb = (te - t);
159             size_t sb = strlen(str);
160
161             if (sb >= (BUFSIZ - tb)) {
162                 t = xrealloc(t, BUFSIZ+sb);
163                 te = t + tb;
164             }
165 /*@-boundswrite@*/
166             /*@-usereleased@*/
167             te = stpcpy(te, str);
168             /*@=usereleased@*/
169 /*@=boundswrite@*/
170             str = _free(str);
171         }
172         /*@=branchstate@*/
173     }
174
175     if (!(qva->qva_flags & QUERY_FOR_LIST))
176         goto exit;
177
178     fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem);
179     if (rpmfiFC(fi) <= 0) {
180 /*@-boundswrite@*/
181         te = stpcpy(te, _("(contains no files)"));
182 /*@=boundswrite@*/
183         goto exit;
184     }
185
186     fi = rpmfiInit(fi, 0);
187     if (fi != NULL)
188     while ((i = rpmfiNext(fi)) >= 0) {
189         rpmfileAttrs fflags;
190         unsigned short fmode;
191         unsigned short frdev;
192         unsigned int fmtime;
193         rpmfileState fstate;
194         size_t fsize;
195         const char * fn;
196         char fmd5[32+1];
197         const char * fuser;
198         const char * fgroup;
199         const char * flink;
200         int_32 fnlink;
201
202         fflags = rpmfiFFlags(fi);
203         fmode = rpmfiFMode(fi);
204         frdev = rpmfiFRdev(fi);
205         fmtime = rpmfiFMtime(fi);
206         fstate = rpmfiFState(fi);
207         fsize = rpmfiFSize(fi);
208         fn = rpmfiFN(fi);
209 /*@-bounds@*/
210         {   static char hex[] = "0123456789abcdef";
211             const char * s = rpmfiMD5(fi);
212             char * p = fmd5;
213             int j;
214             for (j = 0; j < 16; j++) {
215                 unsigned k = *s++;
216                 *p++ = hex[ (k >> 4) & 0xf ];
217                 *p++ = hex[ (k     ) & 0xf ];
218             }
219             *p = '\0';
220         }
221 /*@=bounds@*/
222         fuser = rpmfiFUser(fi);
223         fgroup = rpmfiFGroup(fi);
224         flink = rpmfiFLink(fi);
225         fnlink = rpmfiFNlink(fi);
226
227         /* If querying only docs, skip non-doc files. */
228         if ((qva->qva_flags & QUERY_FOR_DOCS) && !(fflags & RPMFILE_DOC))
229             continue;
230
231         /* If querying only configs, skip non-config files. */
232         if ((qva->qva_flags & QUERY_FOR_CONFIG) && !(fflags & RPMFILE_CONFIG))
233             continue;
234
235         /* If not querying %ghost, skip ghost files. */
236         if (!(qva->qva_fflags & RPMFILE_GHOST) && (fflags & RPMFILE_GHOST))
237             continue;
238
239 /*@-boundswrite@*/
240         if (!rpmIsVerbose() && prefix)
241             te = stpcpy(te, prefix);
242
243         if (qva->qva_flags & QUERY_FOR_STATE) {
244             switch (fstate) {
245             case RPMFILE_STATE_NORMAL:
246                 te = stpcpy(te, _("normal        "));
247                 /*@switchbreak@*/ break;
248             case RPMFILE_STATE_REPLACED:
249                 te = stpcpy(te, _("replaced      "));
250                 /*@switchbreak@*/ break;
251             case RPMFILE_STATE_NOTINSTALLED:
252                 te = stpcpy(te, _("not installed "));
253                 /*@switchbreak@*/ break;
254             case RPMFILE_STATE_NETSHARED:
255                 te = stpcpy(te, _("net shared    "));
256                 /*@switchbreak@*/ break;
257             case RPMFILE_STATE_MISSING:
258                 te = stpcpy(te, _("(no state)    "));
259                 /*@switchbreak@*/ break;
260             default:
261                 sprintf(te, _("(unknown %3d) "), fstate);
262                 te += strlen(te);
263                 /*@switchbreak@*/ break;
264             }
265         }
266 /*@=boundswrite@*/
267
268         if (qva->qva_flags & QUERY_FOR_DUMPFILES) {
269             sprintf(te, "%s %d %d %s 0%o ", fn, fsize, fmtime, fmd5, fmode);
270             te += strlen(te);
271
272             if (fuser && fgroup) {
273 /*@-nullpass@*/
274                 sprintf(te, "%s %s", fuser, fgroup);
275 /*@=nullpass@*/
276                 te += strlen(te);
277             } else {
278                 rpmError(RPMERR_INTERNAL,
279                         _("package has not file owner/group lists\n"));
280             }
281
282             sprintf(te, " %s %s %u ", 
283                                  fflags & RPMFILE_CONFIG ? "1" : "0",
284                                  fflags & RPMFILE_DOC ? "1" : "0",
285                                  frdev);
286             te += strlen(te);
287
288             sprintf(te, "%s", (flink && *flink ? flink : "X"));
289             te += strlen(te);
290         } else
291         if (!rpmIsVerbose()) {
292 /*@-boundswrite@*/
293             te = stpcpy(te, fn);
294 /*@=boundswrite@*/
295         }
296         else {
297
298             /* XXX Adjust directory link count and size for display output. */
299             if (S_ISDIR(fmode)) {
300                 fnlink++;
301                 fsize = 0;
302             }
303
304             if (fuser && fgroup) {
305 /*@-nullpass@*/
306                 printFileInfo(te, fn, fsize, fmode, fmtime, frdev, fnlink,
307                                         fuser, fgroup, flink);
308 /*@=nullpass@*/
309                 te += strlen(te);
310             } else {
311                 rpmError(RPMERR_INTERNAL,
312                         _("package has neither file owner or id lists\n"));
313             }
314         }
315         if (te > t) {
316 /*@-boundswrite@*/
317             *te++ = '\n';
318             *te = '\0';
319             rpmMessage(RPMMESS_NORMAL, "%s", t);
320             te = t;
321             *t = '\0';
322 /*@=boundswrite@*/
323         }
324     }
325             
326     rc = 0;
327
328 exit:
329     if (te > t) {
330         if (!nonewline) {
331 /*@-boundswrite@*/
332             *te++ = '\n';
333             *te = '\0';
334 /*@=boundswrite@*/
335         }
336         rpmMessage(RPMMESS_NORMAL, "%s", t);
337     }
338     t = _free(t);
339
340     fi = rpmfiFree(fi);
341     return rc;
342 }
343
344 /**
345  * Print copy of spec file, filling in Group/Description/Summary from specspo.
346  * @param spec          spec file control structure
347  */
348 static void
349 printNewSpecfile(Spec spec)
350         /*@globals fileSystem @*/
351         /*@modifies spec->sl->sl_lines[], fileSystem @*/
352 {
353     Header h;
354     speclines sl = spec->sl;
355     spectags st = spec->st;
356     const char * msgstr = NULL;
357     int i, j;
358
359     if (sl == NULL || st == NULL)
360         return;
361
362     /*@-branchstate@*/
363     for (i = 0; i < st->st_ntags; i++) {
364         spectag t = st->st_t + i;
365         const char * tn = tagName(t->t_tag);
366         const char * errstr;
367         char fmt[1024];
368
369         fmt[0] = '\0';
370         if (t->t_msgid == NULL)
371             h = spec->packages->header;
372         else {
373             Package pkg;
374             char *fe;
375
376 /*@-bounds@*/
377             strcpy(fmt, t->t_msgid);
378             for (fe = fmt; *fe && *fe != '('; fe++)
379                 {} ;
380             if (*fe == '(') *fe = '\0';
381 /*@=bounds@*/
382             h = NULL;
383             for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
384                 const char *pkgname;
385                 h = pkg->header;
386                 (void) headerNVR(h, &pkgname, NULL, NULL);
387                 if (!strcmp(pkgname, fmt))
388                     /*@innerbreak@*/ break;
389             }
390             if (pkg == NULL || h == NULL)
391                 h = spec->packages->header;
392         }
393
394         if (h == NULL)
395             continue;
396
397         fmt[0] = '\0';
398 /*@-boundswrite@*/
399         (void) stpcpy( stpcpy( stpcpy( fmt, "%{"), tn), "}");
400 /*@=boundswrite@*/
401         msgstr = _free(msgstr);
402
403         /* XXX this should use queryHeader(), but prints out tn as well. */
404         msgstr = headerSprintf(h, fmt, rpmTagTable, rpmHeaderFormats, &errstr);
405         if (msgstr == NULL) {
406             rpmError(RPMERR_QFMT, _("can't query %s: %s\n"), tn, errstr);
407             return;
408         }
409
410 /*@-boundswrite@*/
411         switch(t->t_tag) {
412         case RPMTAG_SUMMARY:
413         case RPMTAG_GROUP:
414             /*@-unqualifiedtrans@*/
415             sl->sl_lines[t->t_startx] = _free(sl->sl_lines[t->t_startx]);
416             /*@=unqualifiedtrans@*/
417             if (t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))
418                 continue;
419             {   char *buf = xmalloc(strlen(tn) + sizeof(": ") + strlen(msgstr));
420                 (void) stpcpy( stpcpy( stpcpy(buf, tn), ": "), msgstr);
421                 sl->sl_lines[t->t_startx] = buf;
422             }
423             /*@switchbreak@*/ break;
424         case RPMTAG_DESCRIPTION:
425             for (j = 1; j < t->t_nlines; j++) {
426                 if (*sl->sl_lines[t->t_startx + j] == '%')
427                     /*@innercontinue@*/ continue;
428                 /*@-unqualifiedtrans@*/
429                 sl->sl_lines[t->t_startx + j] =
430                         _free(sl->sl_lines[t->t_startx + j]);
431                 /*@=unqualifiedtrans@*/
432             }
433             if (t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG)) {
434                 sl->sl_lines[t->t_startx] = _free(sl->sl_lines[t->t_startx]);
435                 continue;
436             }
437             sl->sl_lines[t->t_startx + 1] = xstrdup(msgstr);
438             if (t->t_nlines > 2)
439                 sl->sl_lines[t->t_startx + 2] = xstrdup("\n\n");
440             /*@switchbreak@*/ break;
441         }
442 /*@=boundswrite@*/
443     }
444     /*@=branchstate@*/
445     msgstr = _free(msgstr);
446
447 /*@-boundsread@*/
448     for (i = 0; i < sl->sl_nlines; i++) {
449         const char * s = sl->sl_lines[i];
450         if (s == NULL)
451             continue;
452         printf("%s", s);
453         if (strchr(s, '\n') == NULL && s[strlen(s)-1] != '\n')
454             printf("\n");
455     }
456 /*@=boundsread@*/
457 }
458
459 void rpmDisplayQueryTags(FILE * fp)
460 {
461     const struct headerTagTableEntry_s * t;
462     int i;
463     const struct headerSprintfExtension_s * ext = rpmHeaderFormats;
464
465     for (i = 0, t = rpmTagTable; i < rpmTagTableSize; i++, t++)
466         if (t->name) fprintf(fp, "%s\n", t->name + 7);
467
468     while (ext->name != NULL) {
469         if (ext->type == HEADER_EXT_MORE) {
470             ext = ext->u.more;
471             continue;
472         }
473         /* XXX don't print query tags twice. */
474         for (i = 0, t = rpmTagTable; i < rpmTagTableSize; i++, t++) {
475             if (t->name == NULL)        /* XXX programmer error. */
476                 /*@innercontinue@*/ continue;
477             if (!strcmp(t->name, ext->name))
478                 /*@innerbreak@*/ break;
479         }
480         if (i >= rpmTagTableSize && ext->type == HEADER_EXT_TAG)
481             fprintf(fp, "%s\n", ext->name + 7);
482         ext++;
483     }
484 }
485
486 int rpmcliShowMatches(QVA_t qva, rpmts ts)
487 {
488     Header h;
489     int ec = 0;
490
491     while ((h = rpmdbNextIterator(qva->qva_mi)) != NULL) {
492         int rc;
493         if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
494             ec = rc;
495     }
496     qva->qva_mi = rpmdbFreeIterator(qva->qva_mi);
497     return ec;
498 }
499
500 /**
501  * Convert hex to binary nibble.
502  * @param c            hex character
503  * @return             binary nibble
504  */
505 static inline unsigned char nibble(char c)
506         /*@*/
507 {
508     if (c >= '0' && c <= '9')
509         return (c - '0');
510     if (c >= 'A' && c <= 'F')
511         return (c - 'A') + 10;
512     if (c >= 'a' && c <= 'f')
513         return (c - 'a') + 10;
514     return 0;
515 }
516
517 /*@-redecl@*/
518 /**
519  * @todo Eliminate linkage loop into librpmbuild.a
520  */
521 int     (*parseSpecVec) (Spec *specp, const char *specFile, const char *rootdir,
522                 const char *buildRoot, int recursing, const char *passPhrase,
523                 char *cookie, int anyarch, int force) = NULL;
524 /**
525  * @todo Eliminate linkage loop into librpmbuild.a
526  */
527 /*@null@*/ Spec (*freeSpecVec) (Spec spec) = NULL;
528 /*@=redecl@*/
529
530 /*@-bounds@*/ /* LCL: segfault (realpath annotation?) */
531 int rpmQueryVerify(QVA_t qva, rpmts ts, const char * arg)
532 {
533     const char ** av = NULL;
534     int res = 0;
535     Header h;
536     int rc;
537     int xx;
538     const char * s;
539     int i;
540
541     if (qva->qva_showPackage == NULL)
542         return 1;
543
544     /*@-branchstate@*/
545     switch (qva->qva_source) {
546     case RPMQV_RPM:
547     {   int ac = 0;
548         const char * fileURL = NULL;
549         rpmRC rpmrc;
550
551         rc = rpmGlob(arg, &ac, &av);
552         if (rc) return 1;
553
554 restart:
555         for (i = 0; i < ac; i++) {
556             FD_t fd;
557
558             fileURL = _free(fileURL);
559             fileURL = av[i];
560             av[i] = NULL;
561
562             /* Try to read the header from a package file. */
563             fd = Fopen(fileURL, "r.ufdio");
564             if (fd == NULL || Ferror(fd)) {
565                 rpmError(RPMERR_OPEN, _("open of %s failed: %s\n"), fileURL,
566                         Fstrerror(fd));
567                 if (fd) (void) Fclose(fd);
568                 res = 1;
569                 /*@loopbreak@*/ break;
570             }
571
572             rpmrc = rpmReadPackageFile(ts, fd, fileURL, &h);
573
574             (void) Fclose(fd);
575
576             res = 0;
577             switch (rpmrc) {
578             default:
579                 rpmError(RPMERR_QUERY, _("query of %s failed\n"), fileURL);
580                 res = 1;
581                 /*@switchbreak@*/ break;
582             case RPMRC_OK:
583                 if (h == NULL) {
584                     rpmError(RPMERR_QUERY,
585                         _("old format source packages cannot be queried\n"));
586                     res = 1;
587                     /*@switchbreak@*/ break;
588                 }
589
590                 /* Query a package file. */
591                 res = qva->qva_showPackage(qva, ts, h);
592                 h = headerFree(h);
593                 rpmtsEmpty(ts);
594                 continue;
595                 /*@notreached@*/ /*@switchbreak@*/ break;
596             case RPMRC_NOTFOUND:
597                 res = 0;
598                 /*@switchbreak@*/ break;
599             }
600             if (res)
601                 /*@loopbreak@*/ break;
602
603             /* Try to read a package manifest. */
604             fd = Fopen(fileURL, "r.fpio");
605             if (fd == NULL || Ferror(fd)) {
606                 rpmError(RPMERR_OPEN, _("open of %s failed: %s\n"), fileURL,
607                         Fstrerror(fd));
608                 if (fd) (void) Fclose(fd);
609                 res = 1;
610                 /*@loopbreak@*/ break;
611             }
612             
613             /* Read list of packages from manifest. */
614             res = rpmReadPackageManifest(fd, &ac, &av);
615             if (res != RPMRC_OK) {
616                 rpmError(RPMERR_MANIFEST, _("%s: not an rpm package (or package manifest): %s\n"),
617                         fileURL, Fstrerror(fd));
618                 res = 1;
619             }
620             (void) Fclose(fd);
621
622             /* If successful, restart the query loop. */
623             if (res == 0)
624                 goto restart;
625
626             /*@loopbreak@*/ break;
627         }
628
629         fileURL = _free(fileURL);
630         if (av) {
631             for (i = 0; i < ac; i++)
632                 av[i] = _free(av[i]);
633             av = _free(av);
634         }
635     }   break;
636
637     case RPMQV_SPECFILE:
638         if (qva->qva_showPackage != showQueryPackage)
639             return 1;
640
641         /* XXX Eliminate linkage dependency loop */
642         if (parseSpecVec == NULL || freeSpecVec == NULL)
643             return 1;
644
645       { Spec spec = NULL;
646         Package pkg;
647         char * buildRoot = NULL;
648         int recursing = 0;
649         char * passPhrase = "";
650         char *cookie = NULL;
651         int anyarch = 1;
652         int force = 1;
653
654         /*@-mods@*/ /* FIX: make spec abstract */
655         rc = parseSpecVec(&spec, arg, "/", buildRoot, recursing, passPhrase,
656                 cookie, anyarch, force);
657         /*@=mods@*/
658         if (rc || spec == NULL) {
659             rpmError(RPMERR_QUERY,
660                         _("query of specfile %s failed, can't parse\n"), arg);
661             spec = freeSpecVec(spec);
662             res = 1;
663             break;
664         }
665
666         if (specedit) {
667             printNewSpecfile(spec);
668             spec = freeSpecVec(spec);
669             res = 0;
670             break;
671         }
672
673         for (pkg = spec->packages; pkg != NULL; pkg = pkg->next)
674             xx = qva->qva_showPackage(qva, ts, pkg->header);
675         spec = freeSpecVec(spec);
676       } break;
677
678     case RPMQV_ALL:
679         qva->qva_mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
680         if (qva->qva_mi == NULL) {
681             rpmError(RPMERR_QUERYINFO, _("no packages\n"));
682             res = 1;
683         } else {
684             if (arg != NULL)
685             for (av = (const char **) arg; *av; av++) {
686                 if (!rpmdbSetIteratorRE(qva->qva_mi, RPMTAG_NAME, RPMMIRE_DEFAULT, *av))
687                     continue;
688                 qva->qva_mi = rpmdbFreeIterator(qva->qva_mi);
689                 res = 1;
690                 /*@loopbreak@*/ break;
691             }
692             if (!res)
693                 res = rpmcliShowMatches(qva, ts);
694         }
695         break;
696
697     case RPMQV_GROUP:
698         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_GROUP, arg, 0);
699         if (qva->qva_mi == NULL) {
700             rpmError(RPMERR_QUERYINFO,
701                 _("group %s does not contain any packages\n"), arg);
702             res = 1;
703         } else {
704             res = rpmcliShowMatches(qva, ts);
705         }
706         break;
707
708     case RPMQV_TRIGGEREDBY:
709         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_TRIGGERNAME, arg, 0);
710         if (qva->qva_mi == NULL) {
711             rpmError(RPMERR_QUERYINFO, _("no package triggers %s\n"), arg);
712             res = 1;
713         } else {
714             res = rpmcliShowMatches(qva, ts);
715         }
716         break;
717
718     case RPMQV_PKGID:
719     {   unsigned char MD5[16];
720         unsigned char * t;
721
722         for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
723             {};
724         if (i != 32) {
725             rpmError(RPMERR_QUERYINFO, _("malformed %s: %s\n"), "pkgid", arg);
726             return 1;
727         }
728
729         MD5[0] = '\0';
730         for (i = 0, t = MD5, s = arg; i < 16; i++, t++, s += 2)
731             *t = (nibble(s[0]) << 4) | nibble(s[1]);
732         
733         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_SIGMD5, MD5, sizeof(MD5));
734         if (qva->qva_mi == NULL) {
735             rpmError(RPMERR_QUERYINFO, _("no package matches %s: %s\n"),
736                         "pkgid", arg);
737             res = 1;
738         } else {
739             res = rpmcliShowMatches(qva, ts);
740         }
741     }   break;
742
743     case RPMQV_HDRID:
744         for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
745             {};
746         if (i != 40) {
747             rpmError(RPMERR_QUERYINFO, _("malformed %s: %s\n"), "hdrid", arg);
748             return 1;
749         }
750
751         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_SHA1HEADER, arg, 0);
752         if (qva->qva_mi == NULL) {
753             rpmError(RPMERR_QUERYINFO, _("no package matches %s: %s\n"),
754                         "hdrid", arg);
755             res = 1;
756         } else {
757             res = rpmcliShowMatches(qva, ts);
758         }
759         break;
760
761     case RPMQV_FILEID:
762     {   unsigned char MD5[16];
763         unsigned char * t;
764
765         for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
766             {};
767         if (i != 32) {
768             rpmError(RPMERR_QUERY, _("malformed %s: %s\n"), "fileid", arg);
769             return 1;
770         }
771
772         MD5[0] = '\0';
773         for (i = 0, t = MD5, s = arg; i < 16; i++, t++, s += 2)
774             *t = (nibble(s[0]) << 4) | nibble(s[1]);
775
776         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_FILEMD5S, MD5, sizeof(MD5));
777         if (qva->qva_mi == NULL) {
778             rpmError(RPMERR_QUERYINFO, _("no package matches %s: %s\n"),
779                         "fileid", arg);
780             res = 1;
781         } else {
782             res = rpmcliShowMatches(qva, ts);
783         }
784     }   break;
785
786     case RPMQV_TID:
787     {   int mybase = 10;
788         const char * myarg = arg;
789         char * end = NULL;
790         unsigned iid;
791
792         /* XXX should be in strtoul */
793         if (*myarg == '0') {
794             myarg++;
795             mybase = 8;
796             if (*myarg == 'x') {
797                 myarg++;
798                 mybase = 16;
799             }
800         }
801         iid = strtoul(myarg, &end, mybase);
802         if ((*end) || (end == arg) || (iid == ULONG_MAX)) {
803             rpmError(RPMERR_QUERY, _("malformed %s: %s\n"), "tid", arg);
804             return 1;
805         }
806         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_INSTALLTID, &iid, sizeof(iid));
807         if (qva->qva_mi == NULL) {
808             rpmError(RPMERR_QUERYINFO, _("no package matches %s: %s\n"),
809                         "tid", arg);
810             res = 1;
811         } else {
812             res = rpmcliShowMatches(qva, ts);
813         }
814     }   break;
815
816     case RPMQV_WHATREQUIRES:
817         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_REQUIRENAME, arg, 0);
818         if (qva->qva_mi == NULL) {
819             rpmError(RPMERR_QUERYINFO, _("no package requires %s\n"), arg);
820             res = 1;
821         } else {
822             res = rpmcliShowMatches(qva, ts);
823         }
824         break;
825
826     case RPMQV_WHATPROVIDES:
827         if (arg[0] != '/') {
828             qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, arg, 0);
829             if (qva->qva_mi == NULL) {
830                 rpmError(RPMERR_QUERYINFO, _("no package provides %s\n"), arg);
831                 res = 1;
832             } else {
833                 res = rpmcliShowMatches(qva, ts);
834             }
835             break;
836         }
837         /*@fallthrough@*/
838     case RPMQV_PATH:
839     {   char * fn;
840
841         for (s = arg; *s != '\0'; s++)
842             if (!(*s == '.' || *s == '/'))
843                 /*@loopbreak@*/ break;
844
845         if (*s == '\0') {
846             char fnbuf[PATH_MAX];
847             fn = realpath(arg, fnbuf);
848             if (fn)
849                 fn = xstrdup(fn);
850             else
851                 fn = xstrdup(arg);
852         } else
853             fn = xstrdup(arg);
854         (void) rpmCleanPath(fn);
855
856         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, fn, 0);
857         if (qva->qva_mi == NULL) {
858             int myerrno = 0;
859             if (access(fn, F_OK) != 0)
860                 myerrno = errno;
861             switch (myerrno) {
862             default:
863                 rpmError(RPMERR_QUERY,
864                         _("file %s: %s\n"), fn, strerror(myerrno));
865                 /*@innerbreak@*/ break;
866             case 0:
867                 rpmError(RPMERR_QUERYINFO,
868                         _("file %s is not owned by any package\n"), fn);
869                 /*@innerbreak@*/ break;
870             }
871             res = 1;
872         } else {
873             res = rpmcliShowMatches(qva, ts);
874         }
875         fn = _free(fn);
876     }   break;
877
878     case RPMQV_DBOFFSET:
879     {   int mybase = 10;
880         const char * myarg = arg;
881         char * end = NULL;
882         unsigned recOffset;
883
884         /* XXX should be in strtoul */
885         if (*myarg == '0') {
886             myarg++;
887             mybase = 8;
888             if (*myarg == 'x') {
889                 myarg++;
890                 mybase = 16;
891             }
892         }
893         recOffset = strtoul(myarg, &end, mybase);
894         if ((*end) || (end == arg) || (recOffset == ULONG_MAX)) {
895             rpmError(RPMERR_QUERYINFO, _("invalid package number: %s\n"), arg);
896             return 1;
897         }
898         rpmMessage(RPMMESS_DEBUG, _("package record number: %u\n"), recOffset);
899         /* RPMDBI_PACKAGES */
900         qva->qva_mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &recOffset, sizeof(recOffset));
901         if (qva->qva_mi == NULL) {
902             rpmError(RPMERR_QUERYINFO,
903                 _("record %u could not be read\n"), recOffset);
904             res = 1;
905         } else {
906             res = rpmcliShowMatches(qva, ts);
907         }
908     }   break;
909
910     case RPMQV_PACKAGE:
911         /* XXX HACK to get rpmdbFindByLabel out of the API */
912         qva->qva_mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0);
913         if (qva->qva_mi == NULL) {
914             rpmError(RPMERR_QUERYINFO, _("package %s is not installed\n"), arg);
915             res = 1;
916         } else {
917             res = rpmcliShowMatches(qva, ts);
918         }
919         break;
920     }
921     /*@=branchstate@*/
922    
923     return res;
924 }
925 /*@=bounds@*/
926
927 int rpmcliQuery(rpmts ts, QVA_t qva, const char ** argv)
928 {
929     const char * arg;
930     rpmVSFlags vsflags, ovsflags;
931     int ec = 0;
932
933     if (qva->qva_showPackage == NULL)
934         qva->qva_showPackage = showQueryPackage;
935
936     vsflags = rpmExpandNumeric("%{?_vsflags_query}");
937     if (qva->qva_flags & VERIFY_DIGEST)
938         vsflags |= _RPMVSF_NODIGESTS;
939     if (qva->qva_flags & VERIFY_SIGNATURE)
940         vsflags |= _RPMVSF_NOSIGNATURES;
941     if (qva->qva_flags & VERIFY_HDRCHK)
942         vsflags |= RPMVSF_NOHDRCHK;
943
944     ovsflags = rpmtsSetVSFlags(ts, vsflags);
945     if (qva->qva_source == RPMQV_ALL) {
946         /*@-nullpass@*/ /* FIX: argv can be NULL, cast to pass argv array */
947         ec = rpmQueryVerify(qva, ts, (const char *) argv);
948         /*@=nullpass@*/
949     } else {
950 /*@-boundsread@*/
951         if (argv != NULL)
952         while ((arg = *argv++) != NULL) {
953             ec += rpmQueryVerify(qva, ts, arg);
954             rpmtsEmpty(ts);
955         }
956 /*@=boundsread@*/
957     }
958     vsflags = rpmtsSetVSFlags(ts, ovsflags);
959
960     if (qva->qva_showPackage == showQueryPackage)
961         qva->qva_showPackage = NULL;
962
963     return ec;
964 }