fix: command line install had header memory leak.
[tools/librpm-tizen.git] / lib / query.c
1 #include "system.h"
2
3 #ifndef PATH_MAX
4 # define PATH_MAX 255
5 #endif
6
7 #include "rpmbuild.h"
8 #include <rpmurl.h>
9
10 /* ======================================================================== */
11 static char * permsString(int mode)
12 {
13     char *perms = xstrdup("----------");
14    
15     if (S_ISDIR(mode)) 
16         perms[0] = 'd';
17     else if (S_ISLNK(mode))
18         perms[0] = 'l';
19     else if (S_ISFIFO(mode)) 
20         perms[0] = 'p';
21     else if (S_ISSOCK(mode)) 
22         perms[0] = 's';
23     else if (S_ISCHR(mode))
24         perms[0] = 'c';
25     else if (S_ISBLK(mode))
26         perms[0] = 'b';
27
28     if (mode & S_IRUSR) perms[1] = 'r';
29     if (mode & S_IWUSR) perms[2] = 'w';
30     if (mode & S_IXUSR) perms[3] = 'x';
31  
32     if (mode & S_IRGRP) perms[4] = 'r';
33     if (mode & S_IWGRP) perms[5] = 'w';
34     if (mode & S_IXGRP) perms[6] = 'x';
35
36     if (mode & S_IROTH) perms[7] = 'r';
37     if (mode & S_IWOTH) perms[8] = 'w';
38     if (mode & S_IXOTH) perms[9] = 'x';
39
40     if (mode & S_ISUID)
41         perms[3] = ((mode & S_IXUSR) ? 's' : 'S'); 
42
43     if (mode & S_ISGID)
44         perms[6] = ((mode & S_IXGRP) ? 's' : 'S'); 
45
46     if (mode & S_ISVTX)
47         perms[9] = ((mode & S_IXOTH) ? 't' : 'T');
48
49     return perms;
50 }
51
52 static void printFileInfo(FILE *fp, const char * name,
53                           unsigned int size, unsigned short mode,
54                           unsigned int mtime, unsigned short rdev,
55                           const char * owner, const char * group,
56                           int uid, int gid, const char * linkto)
57 {
58     char sizefield[15];
59     char ownerfield[9], groupfield[9];
60     char timefield[100] = "";
61     time_t when = mtime;  /* important if sizeof(int_32) ! sizeof(time_t) */
62     struct tm * tm;
63     static time_t now;
64     static struct tm nowtm;
65     const char * namefield = name;
66     char * perms;
67
68     /* On first call, grab snapshot of now */
69     if (now == 0) {
70         now = time(NULL);
71         tm = localtime(&now);
72         nowtm = *tm;    /* structure assignment */
73     }
74
75     perms = permsString(mode);
76
77     if (owner) 
78         strncpy(ownerfield, owner, 8);
79     else
80         sprintf(ownerfield, "%-8d", uid);
81     ownerfield[8] = '\0';
82
83     if (group) 
84         strncpy(groupfield, group, 8);
85     else 
86         sprintf(groupfield, "%-8d", gid);
87     groupfield[8] = '\0';
88
89     /* this is normally right */
90     sprintf(sizefield, "%12u", size);
91
92     /* this knows too much about dev_t */
93
94     if (S_ISLNK(mode)) {
95         char *nf = alloca(strlen(name) + sizeof(" -> ") + strlen(linkto));
96         sprintf(nf, "%s -> %s", name, linkto);
97         namefield = nf;
98     } else if (S_ISCHR(mode)) {
99         perms[0] = 'c';
100         sprintf(sizefield, "%3u, %3u", (rdev >> 8) & 0xff, rdev & 0xFF);
101     } else if (S_ISBLK(mode)) {
102         perms[0] = 'b';
103         sprintf(sizefield, "%3u, %3u", (rdev >> 8) & 0xff, rdev & 0xFF);
104     }
105
106     /* Convert file mtime to display format */
107     tm = localtime(&when);
108     {   const char *fmt;
109         if (now > when + 6L * 30L * 24L * 60L * 60L ||  /* Old. */
110             now < when - 60L * 60L)                     /* In the future.  */
111         {
112         /* The file is fairly old or in the future.
113          * POSIX says the cutoff is 6 months old;
114          * approximate this by 6*30 days.
115          * Allow a 1 hour slop factor for what is considered "the future",
116          * to allow for NFS server/client clock disagreement.
117          * Show the year instead of the time of day.
118          */        
119             fmt = "%b %e  %Y";
120         } else {
121             fmt = "%b %e %H:%M";
122         }
123         (void)strftime(timefield, sizeof(timefield) - 1, fmt, tm);
124     }
125
126     fprintf(fp, "%s %8s %8s %10s %s %s\n", perms, ownerfield, groupfield, 
127                 sizefield, timefield, namefield);
128     if (perms) free(perms);
129 }
130
131 static int queryHeader(FILE *fp, Header h, const char * chptr)
132 {
133     char * str;
134     const char * error;
135
136     str = headerSprintf(h, chptr, rpmTagTable, rpmHeaderFormats, &error);
137     if (!str) {
138         fprintf(stderr, _("error in format: %s\n"), error);
139         return 1;
140     }
141
142     fputs(str, fp);
143
144     return 0;
145 }
146
147 int showQueryPackage(QVA_t *qva, /*@unused@*/rpmdb db, Header h)
148 {
149     FILE *fp = stdout;  /* XXX FIXME: pass as arg */
150     int queryFlags = qva->qva_flags;
151     const char *queryFormat = qva->qva_queryFormat;
152
153     const char * name, * version, * release;
154     int_32 count, type;
155     char * prefix = NULL;
156     const char ** fileList;
157     const char ** fileMD5List;
158     const char * fileStatesList;
159     const char ** fileOwnerList = NULL;
160     const char ** fileGroupList = NULL;
161     const char ** fileLinktoList;
162     int_32 * fileFlagsList, * fileMTimeList, * fileSizeList;
163     int_32 * fileUIDList = NULL;
164     int_32 * fileGIDList = NULL;
165     uint_16 * fileModeList;
166     uint_16 * fileRdevList;
167     int i;
168
169     headerNVR(h, &name, &version, &release);
170
171     if (!queryFormat && !queryFlags) {
172         fprintf(fp, "%s-%s-%s\n", name, version, release);
173     } else {
174         if (queryFormat)
175             queryHeader(fp, h, queryFormat);
176
177         if (queryFlags & QUERY_FOR_LIST) {
178             if (!headerGetEntry(h, RPMTAG_FILENAMES, &type, (void **) &fileList, 
179                  &count)) {
180                 fputs(_("(contains no files)"), fp);
181                 fputs("\n", fp);
182             } else {
183                 if (!headerGetEntry(h, RPMTAG_FILESTATES, &type, 
184                          (void **) &fileStatesList, &count)) {
185                     fileStatesList = NULL;
186                 }
187                 headerGetEntry(h, RPMTAG_FILEFLAGS, &type, 
188                          (void **) &fileFlagsList, &count);
189                 headerGetEntry(h, RPMTAG_FILESIZES, &type, 
190                          (void **) &fileSizeList, &count);
191                 headerGetEntry(h, RPMTAG_FILEMODES, &type, 
192                          (void **) &fileModeList, &count);
193                 headerGetEntry(h, RPMTAG_FILEMTIMES, &type, 
194                          (void **) &fileMTimeList, &count);
195                 headerGetEntry(h, RPMTAG_FILERDEVS, &type, 
196                          (void **) &fileRdevList, &count);
197                 headerGetEntry(h, RPMTAG_FILELINKTOS, &type, 
198                          (void **) &fileLinktoList, &count);
199                 headerGetEntry(h, RPMTAG_FILEMD5S, &type, 
200                          (void **) &fileMD5List, &count);
201
202                 if (!headerGetEntry(h, RPMTAG_FILEUIDS, &type, 
203                          (void **) &fileUIDList, &count)) {
204                     fileUIDList = NULL;
205                 } else if (!headerGetEntry(h, RPMTAG_FILEGIDS, &type, 
206                              (void **) &fileGIDList, &count)) {
207                     fileGIDList = NULL;
208                 }
209
210                 if (!headerGetEntry(h, RPMTAG_FILEUSERNAME, &type, 
211                          (void **) &fileOwnerList, &count)) {
212                     fileOwnerList = NULL;
213                 } else if (!headerGetEntry(h, RPMTAG_FILEGROUPNAME, &type, 
214                              (void **) &fileGroupList, &count)) {
215                     fileGroupList = NULL;
216                 }
217
218                 for (i = 0; i < count; i++) {
219                     if (!((queryFlags & QUERY_FOR_DOCS) || 
220                           (queryFlags & QUERY_FOR_CONFIG)) 
221                         || ((queryFlags & QUERY_FOR_DOCS) && 
222                             (fileFlagsList[i] & RPMFILE_DOC))
223                         || ((queryFlags & QUERY_FOR_CONFIG) && 
224                             (fileFlagsList[i] & RPMFILE_CONFIG))) {
225
226                         if (!rpmIsVerbose())
227                             prefix ? fputs(prefix, fp) : 0;
228
229                         if (queryFlags & QUERY_FOR_STATE) {
230                             if (fileStatesList) {
231                                 switch (fileStatesList[i]) {
232                                   case RPMFILE_STATE_NORMAL:
233                                     fputs(_("normal        "), fp); break;
234                                   case RPMFILE_STATE_REPLACED:
235                                     fputs(_("replaced      "), fp); break;
236                                   case RPMFILE_STATE_NOTINSTALLED:
237                                     fputs(_("not installed "), fp); break;
238                                   case RPMFILE_STATE_NETSHARED:
239                                     fputs(_("net shared    "), fp); break;
240                                   default:
241                                     fprintf(fp, _("(unknown %3d) "), 
242                                           (int)fileStatesList[i]);
243                                 }
244                             } else {
245                                 fputs(    _("(no state)    "), fp);
246                             }
247                         }
248                             
249                         if (queryFlags & QUERY_FOR_DUMPFILES) {
250                             fprintf(fp, "%s %d %d %s 0%o ", fileList[i],
251                                    fileSizeList[i], fileMTimeList[i],
252                                    fileMD5List[i], fileModeList[i]);
253
254                             if (fileOwnerList && fileGroupList)
255                                 fprintf(fp, "%s %s", fileOwnerList[i], 
256                                                 fileGroupList[i]);
257                             else if (fileUIDList && fileGIDList)
258                                 fprintf(fp, "%d %d", fileUIDList[i], 
259                                                 fileGIDList[i]);
260                             else {
261                                 rpmError(RPMERR_INTERNAL, _("package has "
262                                         "neither file owner or id lists"));
263                             }
264
265                             fprintf(fp, " %s %s %u ", 
266                                  fileFlagsList[i] & RPMFILE_CONFIG ? "1" : "0",
267                                  fileFlagsList[i] & RPMFILE_DOC ? "1" : "0",
268                                  (unsigned)fileRdevList[i]);
269
270                             if (strlen(fileLinktoList[i]))
271                                 fprintf(fp, "%s\n", fileLinktoList[i]);
272                             else
273                                 fprintf(fp, "X\n");
274
275                         } else if (!rpmIsVerbose()) {
276                             fputs(fileList[i], fp);
277                             fputs("\n", fp);
278                         } else if (fileOwnerList && fileGroupList) 
279                             printFileInfo(fp, fileList[i], fileSizeList[i],
280                                           fileModeList[i], fileMTimeList[i],
281                                           fileRdevList[i], fileOwnerList[i], 
282                                           fileGroupList[i], -1, 
283                                           -1, fileLinktoList[i]);
284                         else if (fileUIDList && fileGIDList) {
285                             printFileInfo(fp, fileList[i], fileSizeList[i],
286                                           fileModeList[i], fileMTimeList[i],
287                                           fileRdevList[i], NULL, 
288                                           NULL, fileUIDList[i], 
289                                           fileGIDList[i], fileLinktoList[i]);
290                         } else {
291                             rpmError(RPMERR_INTERNAL, _("package has "
292                                     "neither file owner or id lists"));
293                         }
294                     }
295                 }
296             
297                 free(fileList);
298                 free(fileLinktoList);
299                 free(fileMD5List);
300                 if (fileOwnerList) free(fileOwnerList);
301                 if (fileGroupList) free(fileGroupList);
302             }
303         }
304     }
305     return 0;   /* XXX FIXME: need real return code */
306 }
307
308 static void
309 printNewSpecfile(Spec spec)
310 {
311     struct speclines *sl = spec->sl;
312     struct spectags *st = spec->st;
313     char buf[8192];
314     int i, j;
315
316     if (sl == NULL || st == NULL)
317         return;
318
319     for (i = 0; i < st->st_ntags; i++) {
320         char *msgstr;
321         struct spectag *t;
322         t = st->st_t + i;
323
324         /* XXX Summary tag often precedes name, so build msgid now. */
325         if (t->t_msgid == NULL) {
326             char *n;
327             headerGetEntry(spec->packages->header, RPMTAG_NAME, NULL,
328                 (void **) &n, NULL);
329             sprintf(buf, "%s(%s)", n, tagName(t->t_tag));
330             t->t_msgid = xstrdup(buf);
331         }
332         msgstr = xstrdup(dgettext(specedit, t->t_msgid));
333
334         switch(t->t_tag) {
335         case RPMTAG_SUMMARY:
336         case RPMTAG_GROUP:
337             free(sl->sl_lines[t->t_startx]);
338             sl->sl_lines[t->t_startx] = NULL;
339             if (t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))
340                 continue;
341             sprintf(buf, "%s: %s\n",
342                 ((t->t_tag == RPMTAG_GROUP) ? "Group" : "Summary"),
343                 msgstr);
344             sl->sl_lines[t->t_startx] = xstrdup(buf);
345             break;
346         case RPMTAG_DESCRIPTION:
347             for (j = 1; j < t->t_nlines; j++) {
348                 free(sl->sl_lines[t->t_startx + j]);
349                 sl->sl_lines[t->t_startx + j] = NULL;
350             }
351             if (t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG)) {
352                 free(sl->sl_lines[t->t_startx]);
353                 sl->sl_lines[t->t_startx] = NULL;
354                 continue;
355             }
356             sl->sl_lines[t->t_startx + 1] = xstrdup(msgstr);
357             if (t->t_nlines > 2)
358                 sl->sl_lines[t->t_startx + 2] = xstrdup("\n\n");
359             break;
360         }
361     }
362
363     for (i = 0; i < sl->sl_nlines; i++) {
364         if (sl->sl_lines[i] == NULL)
365             continue;
366         printf("%s", sl->sl_lines[i]);
367     }
368 }
369
370 void rpmDisplayQueryTags(FILE * f) {
371     const struct headerTagTableEntry * t;
372     int i;
373     const struct headerSprintfExtension * ext = rpmHeaderFormats;
374
375     for (i = 0, t = rpmTagTable; i < rpmTagTableSize; i++, t++) {
376         fprintf(f, "%s\n", t->name + 7);
377     }
378
379     while (ext->name) {
380         if (ext->type == HEADER_EXT_TAG)
381             fprintf(f, "%s\n", ext->name + 7), ext++;
382         else if (ext->type == HEADER_EXT_MORE)
383             ext = ext->u.more;
384         else
385             ext++;
386     }
387 }
388
389 int showMatches(QVA_t *qva, rpmdb db, dbiIndexSet matches, QVF_t showPackage)
390 {
391     Header h;
392     int ec = 0;
393     int i;
394
395     for (i = 0; i < dbiIndexSetCount(matches); i++) {
396         int rc;
397         unsigned int recOffset = dbiIndexRecordOffset(matches, i);
398         if (recOffset == 0)
399             continue;
400         rpmMessage(RPMMESS_DEBUG, _("record number %u\n"), recOffset);
401             
402         h = rpmdbGetRecord(db, recOffset);
403         if (h == NULL) {
404                 fprintf(stderr, _("error: could not read database record\n"));
405                 ec = 1;
406         } else {
407                 if ((rc = showPackage(qva, db, h)) != 0)
408                     ec = rc;
409                 headerFree(h);
410         }
411     }
412     return ec;
413 }
414
415 /*
416  * XXX Eliminate linkage loop into librpmbuild.a
417  */
418 int     (*parseSpecVec) (Spec *specp, const char *specFile, const char *buildRoot,
419                 int inBuildArch, const char *passPhrase, char *cookie, int anyarch,
420                 int force) = NULL;
421 void    (*freeSpecVec) (Spec spec) = NULL;
422 char    *specedit = NULL;
423
424 int rpmQueryVerify(QVA_t *qva, enum rpmQVSources source, const char * arg,
425         rpmdb db, QVF_t showPackage)
426 {
427     dbiIndexSet matches;
428     Header h;
429     int offset;
430     int rc;
431     int isSource;
432     int recNumber;
433     int retcode = 0;
434     char *end = NULL;
435
436     switch (source) {
437       case RPMQV_RPM:
438       { FD_t fd;
439
440         fd = ufdOpen(arg, O_RDONLY, 0);
441         if (fdFileno(fd) < 0) {
442             fprintf(stderr, _("open of %s failed: %s\n"), arg,urlStrerror(arg));
443             ufdClose(fd);
444             retcode = 1;
445             break;
446         }
447
448         retcode = rpmReadPackageHeader(fd, &h, &isSource, NULL, NULL);
449
450         ufdClose(fd);
451
452         switch (retcode) {
453         case 0:
454             if (h == NULL) {
455                 fprintf(stderr, _("old format source packages cannot "
456                         "be queried\n"));
457                 retcode = 1;
458                 break;
459             }
460             retcode = showPackage(qva, db, h);
461             headerFree(h);
462             break;
463         case 1:
464             fprintf(stderr, _("%s does not appear to be a RPM package\n"), arg);
465             /*@fallthrough@*/
466         case 2:
467             fprintf(stderr, _("query of %s failed\n"), arg);
468             retcode = 1;
469             break;
470         }
471       } break;
472
473       case RPMQV_SPECFILE:
474         if (showPackage != showQueryPackage)
475             return 1;
476
477         /* XXX Eliminate linkage dependency loop */
478         if (parseSpecVec == NULL || freeSpecVec == NULL)
479             return 1;
480
481       { Spec spec = NULL;
482         Package pkg;
483         char * buildRoot = NULL;
484         int inBuildArch = 0;
485         char * passPhrase = "";
486         char *cookie = NULL;
487         int anyarch = 1;
488         int force = 1;
489
490         rc = parseSpecVec(&spec, arg, buildRoot, inBuildArch, passPhrase,
491                 cookie, anyarch, force);
492         if (rc || spec == NULL) {
493             
494             fprintf(stderr, _("query of specfile %s failed, can't parse\n"), arg);
495             if (spec != NULL) freeSpecVec(spec);
496             retcode = 1;
497             break;
498         }
499
500         if (specedit != NULL) {
501             printNewSpecfile(spec);
502             freeSpecVec(spec);
503             retcode = 0;
504             break;
505         }
506
507         for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
508             showPackage(qva, NULL, pkg->header);
509         }
510         freeSpecVec(spec);
511       } break;
512
513       case RPMQV_ALL:
514         for (offset = rpmdbFirstRecNum(db);
515              offset != 0;
516              offset = rpmdbNextRecNum(db, offset)) {
517                 h = rpmdbGetRecord(db, offset);
518                 if (h == NULL) {
519                     fprintf(stderr, _("could not read database record!\n"));
520                     return 1;
521                 }
522                 if ((rc = showPackage(qva, db, h)) != 0)
523                     retcode = rc;
524                 headerFree(h);
525         }
526         break;
527
528       case RPMQV_GROUP:
529         if (rpmdbFindByGroup(db, arg, &matches)) {
530             fprintf(stderr, _("group %s does not contain any packages\n"), arg);
531             retcode = 1;
532         } else {
533             retcode = showMatches(qva, db, matches, showPackage);
534             dbiFreeIndexRecord(matches);
535         }
536         break;
537
538       case RPMQV_WHATPROVIDES:
539         if (rpmdbFindByProvides(db, arg, &matches)) {
540             fprintf(stderr, _("no package provides %s\n"), arg);
541             retcode = 1;
542         } else {
543             retcode = showMatches(qva, db, matches, showPackage);
544             dbiFreeIndexRecord(matches);
545         }
546         break;
547
548       case RPMQV_TRIGGEREDBY:
549         if (rpmdbFindByTriggeredBy(db, arg, &matches)) {
550             fprintf(stderr, _("no package triggers %s\n"), arg);
551             retcode = 1;
552         } else {
553             retcode = showMatches(qva, db, matches, showPackage);
554             dbiFreeIndexRecord(matches);
555         }
556         break;
557
558       case RPMQV_WHATREQUIRES:
559         if (rpmdbFindByRequiredBy(db, arg, &matches)) {
560             fprintf(stderr, _("no package requires %s\n"), arg);
561             retcode = 1;
562         } else {
563             retcode = showMatches(qva, db, matches, showPackage);
564             dbiFreeIndexRecord(matches);
565         }
566         break;
567
568       case RPMQV_PATH:
569         if (rpmdbFindByFile(db, arg, &matches)) {
570             int myerrno = 0;
571             if (access(arg, F_OK) != 0)
572                 myerrno = errno;
573             switch (myerrno) {
574             default:
575                 fprintf(stderr, _("file %s: %s\n"), arg, strerror(myerrno));
576                 break;
577             case 0:
578                 fprintf(stderr, _("file %s is not owned by any package\n"), arg);
579                 break;
580             }
581             retcode = 1;
582         } else {
583             retcode = showMatches(qva, db, matches, showPackage);
584             dbiFreeIndexRecord(matches);
585         }
586         break;
587
588       case RPMQV_DBOFFSET:
589         recNumber = strtoul(arg, &end, 10);
590         if ((*end) || (end == arg) || (recNumber == ULONG_MAX)) {
591             fprintf(stderr, _("invalid package number: %s\n"), arg);
592             return 1;
593         }
594         rpmMessage(RPMMESS_DEBUG, _("package record number: %d\n"), recNumber);
595         h = rpmdbGetRecord(db, recNumber);
596         if (h == NULL)  {
597             fprintf(stderr, _("record %d could not be read\n"), recNumber);
598             retcode = 1;
599         } else {
600             retcode = showPackage(qva, db, h);
601             headerFree(h);
602         }
603         break;
604
605       case RPMQV_PACKAGE:
606         rc = rpmdbFindByLabel(db, arg, &matches);
607         if (rc == 1) {
608             retcode = 1;
609             fprintf(stderr, _("package %s is not installed\n"), arg);
610         } else if (rc == 2) {
611             retcode = 1;
612             fprintf(stderr, _("error looking for package %s\n"), arg);
613         } else {
614             retcode = showMatches(qva, db, matches, showPackage);
615             dbiFreeIndexRecord(matches);
616         }
617         break;
618     }
619    
620     return retcode;
621 }
622
623 int rpmQuery(QVA_t *qva, enum rpmQVSources source, const char * arg)
624 {
625     rpmdb db = NULL;
626     int rc;
627
628     switch (source) {
629     case RPMQV_RPM:
630     case RPMQV_SPECFILE:
631         break;
632     default:
633         if (rpmdbOpen(qva->qva_prefix, &db, O_RDONLY, 0644)) {
634             fprintf(stderr, _("rpmQuery: rpmdbOpen() failed\n"));
635             return 1;
636         }
637         break;
638     }
639
640     rc = rpmQueryVerify(qva, source, arg, db, showQueryPackage);
641
642     if (db)
643         rpmdbClose(db);
644
645     return rc;
646 }
647
648 /* ======================================================================== */
649 #define POPT_QUERYFORMAT        1000
650 #define POPT_WHATREQUIRES       1001
651 #define POPT_WHATPROVIDES       1002
652 #define POPT_QUERYBYNUMBER      1003
653 #define POPT_TRIGGEREDBY        1004
654 #define POPT_DUMP               1005
655 #define POPT_SPECFILE           1006
656
657 /* ========== Query/Verify source popt args */
658 static void rpmQVSourceArgCallback(/*@unused@*/poptContext con, /*@unused@*/enum poptCallbackReason reason,
659                              const struct poptOption * opt, /*@unused@*/const char * arg, 
660                              QVA_t *qva)
661 {
662
663     switch (opt->val) {
664       case 'a': qva->qva_source |= RPMQV_ALL; qva->qva_sourceCount++; break;
665       case 'f': qva->qva_source |= RPMQV_PATH; qva->qva_sourceCount++; break;
666       case 'g': qva->qva_source |= RPMQV_GROUP; qva->qva_sourceCount++; break;
667       case 'p': qva->qva_source |= RPMQV_RPM; qva->qva_sourceCount++; break;
668       case POPT_WHATPROVIDES: qva->qva_source |= RPMQV_WHATPROVIDES; 
669                               qva->qva_sourceCount++; break;
670       case POPT_WHATREQUIRES: qva->qva_source |= RPMQV_WHATREQUIRES; 
671                               qva->qva_sourceCount++; break;
672       case POPT_TRIGGEREDBY: qva->qva_source |= RPMQV_TRIGGEREDBY;
673                               qva->qva_sourceCount++; break;
674
675 /* XXX SPECFILE is not verify sources */
676       case POPT_SPECFILE:
677         qva->qva_source |= RPMQV_SPECFILE;
678         qva->qva_sourceCount++;
679         break;
680       case POPT_QUERYBYNUMBER:
681         qva->qva_source |= RPMQV_DBOFFSET; 
682         qva->qva_sourceCount++;
683         break;
684     }
685 }
686
687 struct poptOption rpmQVSourcePoptTable[] = {
688         { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA, 
689                 rpmQVSourceArgCallback, 0, NULL, NULL },
690         { "file", 'f', 0, 0, 'f',
691                 N_("query package owning file"), "FILE" },
692         { "group", 'g', 0, 0, 'g',
693                 N_("query packages in group"), "GROUP" },
694         { "package", 'p', 0, 0, 'p',
695                 N_("query a package file"), NULL },
696         { "querybynumber", '\0', POPT_ARGFLAG_DOC_HIDDEN, 0, 
697                 POPT_QUERYBYNUMBER, NULL, NULL },
698         { "specfile", '\0', 0, 0, POPT_SPECFILE,
699                 N_("query a spec file"), NULL },
700         { "triggeredby", '\0', 0, 0, POPT_TRIGGEREDBY, 
701                 N_("query the pacakges triggered by the package"), "PACKAGE" },
702         { "whatrequires", '\0', 0, 0, POPT_WHATREQUIRES, 
703                 N_("query the packages which require a capability"), "CAPABILITY" },
704         { "whatprovides", '\0', 0, 0, POPT_WHATPROVIDES, 
705                 N_("query the packages which provide a capability"), "CAPABILITY" },
706         { 0, 0, 0, 0, 0,        NULL, NULL }
707 };
708
709 /* ========== Query specific popt args */
710
711 static void queryArgCallback(/*@unused@*/poptContext con, /*@unused@*/enum poptCallbackReason reason,
712                              const struct poptOption * opt, const char * arg, 
713                              QVA_t *qva)
714 {
715     switch (opt->val) {
716       case 'c': qva->qva_flags |= QUERY_FOR_CONFIG | QUERY_FOR_LIST; break;
717       case 'd': qva->qva_flags |= QUERY_FOR_DOCS | QUERY_FOR_LIST; break;
718       case 'l': qva->qva_flags |= QUERY_FOR_LIST; break;
719       case 's': qva->qva_flags |= QUERY_FOR_STATE | QUERY_FOR_LIST; break;
720       case POPT_DUMP: qva->qva_flags |= QUERY_FOR_DUMPFILES | QUERY_FOR_LIST; break;
721       case 'v': rpmIncreaseVerbosity();  break;
722
723       case POPT_QUERYFORMAT:
724       { char *qf = (char *)qva->qva_queryFormat;
725         if (qf) {
726             int len = strlen(qf) + strlen(arg) + 1;
727             qf = xrealloc(qf, len);
728             strcat(qf, arg);
729         } else {
730             qf = xmalloc(strlen(arg) + 1);
731             strcpy(qf, arg);
732         }
733         qva->qva_queryFormat = qf;
734       } break;
735     }
736 }
737
738 struct poptOption rpmQueryPoptTable[] = {
739         { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA, 
740                 queryArgCallback, 0, NULL, NULL },
741         { "configfiles", 'c', 0, 0, 'c',
742                 N_("list all configuration files"), NULL },
743         { "docfiles", 'd', 0, 0, 'd',
744                 N_("list all documentation files"), NULL },
745         { "dump", '\0', 0, 0, POPT_DUMP,
746                 N_("dump basic file information"), NULL },
747         { "list", 'l', 0, 0, 'l',
748                 N_("list files in package"), NULL },
749         { "qf", '\0', POPT_ARG_STRING | POPT_ARGFLAG_DOC_HIDDEN, 0, 
750                 POPT_QUERYFORMAT, NULL, NULL },
751         { "queryformat", '\0', POPT_ARG_STRING, 0, POPT_QUERYFORMAT,
752                 N_("use the following query format"), "QUERYFORMAT" },
753         { "specedit", '\0', POPT_ARG_STRING, &specedit, 0,
754                 N_("substitute i18n sections from the following catalogue"),
755                         "METACATALOGUE" },
756         { "state", 's', 0, 0, 's',
757                 N_("display the states of the listed files"), NULL },
758         { "verbose", 'v', 0, 0, 'v',
759                 N_("display a verbose file listing"), NULL },
760         { 0, 0, 0, 0, 0,        NULL, NULL }
761 };
762
763 /* ======================================================================== */