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