Swap 2nd and 3rd arg to Fread/Fwrite to preserve read/write type return.
[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 ** dirList, ** baseNameList;
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_32 * dirIndexList;
168     int i;
169
170     headerNVR(h, &name, &version, &release);
171
172     if (!queryFormat && !queryFlags) {
173         fprintf(fp, "%s-%s-%s\n", name, version, release);
174     } else {
175         if (queryFormat)
176             queryHeader(fp, h, queryFormat);
177
178         if (queryFlags & QUERY_FOR_LIST) {
179             if (!headerGetEntry(h, RPMTAG_COMPFILELIST, &type, 
180                                 (void **) &baseNameList, &count)) {
181                 fputs(_("(contains no files)"), fp);
182                 fputs("\n", fp);
183             } else {
184                 if (!headerGetEntry(h, RPMTAG_FILESTATES, &type, 
185                          (void **) &fileStatesList, &count)) {
186                     fileStatesList = NULL;
187                 }
188                 headerGetEntry(h, RPMTAG_COMPDIRLIST, NULL,
189                          (void **) &dirList, NULL);
190                 headerGetEntry(h, RPMTAG_COMPFILEDIRS, NULL, 
191                          (void **) &dirIndexList, NULL);
192                 headerGetEntry(h, RPMTAG_FILEFLAGS, &type, 
193                          (void **) &fileFlagsList, &count);
194                 headerGetEntry(h, RPMTAG_FILESIZES, &type, 
195                          (void **) &fileSizeList, &count);
196                 headerGetEntry(h, RPMTAG_FILEMODES, &type, 
197                          (void **) &fileModeList, &count);
198                 headerGetEntry(h, RPMTAG_FILEMTIMES, &type, 
199                          (void **) &fileMTimeList, &count);
200                 headerGetEntry(h, RPMTAG_FILERDEVS, &type, 
201                          (void **) &fileRdevList, &count);
202                 headerGetEntry(h, RPMTAG_FILELINKTOS, &type, 
203                          (void **) &fileLinktoList, &count);
204                 headerGetEntry(h, RPMTAG_FILEMD5S, &type, 
205                          (void **) &fileMD5List, &count);
206
207                 if (!headerGetEntry(h, RPMTAG_FILEUIDS, &type, 
208                          (void **) &fileUIDList, &count)) {
209                     fileUIDList = NULL;
210                 } else if (!headerGetEntry(h, RPMTAG_FILEGIDS, &type, 
211                              (void **) &fileGIDList, &count)) {
212                     fileGIDList = NULL;
213                 }
214
215                 if (!headerGetEntry(h, RPMTAG_FILEUSERNAME, &type, 
216                          (void **) &fileOwnerList, &count)) {
217                     fileOwnerList = NULL;
218                 } else if (!headerGetEntry(h, RPMTAG_FILEGROUPNAME, &type, 
219                              (void **) &fileGroupList, &count)) {
220                     fileGroupList = NULL;
221                 }
222
223                 for (i = 0; i < count; i++) {
224                     if (!((queryFlags & QUERY_FOR_DOCS) || 
225                           (queryFlags & QUERY_FOR_CONFIG)) 
226                         || ((queryFlags & QUERY_FOR_DOCS) && 
227                             (fileFlagsList[i] & RPMFILE_DOC))
228                         || ((queryFlags & QUERY_FOR_CONFIG) && 
229                             (fileFlagsList[i] & RPMFILE_CONFIG))) {
230
231                         if (!rpmIsVerbose())
232                             prefix ? fputs(prefix, fp) : 0;
233
234                         if (queryFlags & QUERY_FOR_STATE) {
235                             if (fileStatesList) {
236                                 switch (fileStatesList[i]) {
237                                   case RPMFILE_STATE_NORMAL:
238                                     fputs(_("normal        "), fp); break;
239                                   case RPMFILE_STATE_REPLACED:
240                                     fputs(_("replaced      "), fp); break;
241                                   case RPMFILE_STATE_NOTINSTALLED:
242                                     fputs(_("not installed "), fp); break;
243                                   case RPMFILE_STATE_NETSHARED:
244                                     fputs(_("net shared    "), fp); break;
245                                   default:
246                                     fprintf(fp, _("(unknown %3d) "), 
247                                           (int)fileStatesList[i]);
248                                 }
249                             } else {
250                                 fputs(    _("(no state)    "), fp);
251                             }
252                         }
253
254                         if (queryFlags & QUERY_FOR_DUMPFILES) {
255                             fprintf(fp, "%s%s %d %d %s 0%o ", 
256                                    dirList[dirIndexList[i]], baseNameList[i],
257                                    fileSizeList[i], fileMTimeList[i],
258                                    fileMD5List[i], fileModeList[i]);
259
260                             if (fileOwnerList && fileGroupList)
261                                 fprintf(fp, "%s %s", fileOwnerList[i], 
262                                                 fileGroupList[i]);
263                             else if (fileUIDList && fileGIDList)
264                                 fprintf(fp, "%d %d", fileUIDList[i], 
265                                                 fileGIDList[i]);
266                             else {
267                                 rpmError(RPMERR_INTERNAL, _("package has "
268                                         "neither file owner or id lists"));
269                             }
270
271                             fprintf(fp, " %s %s %u ", 
272                                  fileFlagsList[i] & RPMFILE_CONFIG ? "1" : "0",
273                                  fileFlagsList[i] & RPMFILE_DOC ? "1" : "0",
274                                  (unsigned)fileRdevList[i]);
275
276                             if (strlen(fileLinktoList[i]))
277                                 fprintf(fp, "%s\n", fileLinktoList[i]);
278                             else
279                                 fprintf(fp, "X\n");
280
281                         } else if (!rpmIsVerbose()) {
282                             fputs(dirList[dirIndexList[i]], fp);
283                             fputs(baseNameList[i], fp);
284                             fputs("\n", fp);
285                         } else {
286                             char * filespec;
287
288                             filespec = xmalloc(strlen(dirList[dirIndexList[i]])
289                                               + strlen(baseNameList[i]) + 1);
290                             strcpy(filespec, dirList[dirIndexList[i]]);
291                             strcat(filespec, baseNameList[i]);
292                                         
293                             if (fileOwnerList && fileGroupList) {
294                                 printFileInfo(fp, filespec, fileSizeList[i],
295                                               fileModeList[i], fileMTimeList[i],
296                                               fileRdevList[i], 
297                                               fileOwnerList[i], 
298                                               fileGroupList[i], -1, 
299                                               -1, fileLinktoList[i]);
300                             } else if (fileUIDList && fileGIDList) {
301                                 printFileInfo(fp, filespec, fileSizeList[i],
302                                               fileModeList[i], fileMTimeList[i],
303                                               fileRdevList[i], NULL, 
304                                               NULL, fileUIDList[i], 
305                                               fileGIDList[i], 
306                                               fileLinktoList[i]);
307                             } else {
308                                 rpmError(RPMERR_INTERNAL, _("package has "
309                                         "neither file owner or id lists"));
310                             }
311
312                             free(filespec);
313                         }
314                     }
315                 }
316             
317                 free(dirList);
318                 free(baseNameList);
319                 free(fileLinktoList);
320                 free(fileMD5List);
321                 if (fileOwnerList) free(fileOwnerList);
322                 if (fileGroupList) free(fileGroupList);
323             }
324         }
325     }
326     return 0;   /* XXX FIXME: need real return code */
327 }
328
329 static void
330 printNewSpecfile(Spec spec)
331 {
332     struct speclines *sl = spec->sl;
333     struct spectags *st = spec->st;
334     char buf[8192];
335     int i, j;
336
337     if (sl == NULL || st == NULL)
338         return;
339
340     for (i = 0; i < st->st_ntags; i++) {
341         char *msgstr;
342         struct spectag *t;
343         t = st->st_t + i;
344
345         /* XXX Summary tag often precedes name, so build msgid now. */
346         if (t->t_msgid == NULL) {
347             char *n;
348             headerGetEntry(spec->packages->header, RPMTAG_NAME, NULL,
349                 (void **) &n, NULL);
350             sprintf(buf, "%s(%s)", n, tagName(t->t_tag));
351             t->t_msgid = xstrdup(buf);
352         }
353         msgstr = xstrdup(/*@-unrecog@*/ dgettext(specedit, t->t_msgid) /*@=unrecog@*/);
354
355         switch(t->t_tag) {
356         case RPMTAG_SUMMARY:
357         case RPMTAG_GROUP:
358             free(sl->sl_lines[t->t_startx]);
359             sl->sl_lines[t->t_startx] = NULL;
360             if (t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))
361                 continue;
362             sprintf(buf, "%s: %s\n",
363                 ((t->t_tag == RPMTAG_GROUP) ? "Group" : "Summary"),
364                 msgstr);
365             sl->sl_lines[t->t_startx] = xstrdup(buf);
366             break;
367         case RPMTAG_DESCRIPTION:
368             for (j = 1; j < t->t_nlines; j++) {
369                 free(sl->sl_lines[t->t_startx + j]);
370                 sl->sl_lines[t->t_startx + j] = NULL;
371             }
372             if (t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG)) {
373                 free(sl->sl_lines[t->t_startx]);
374                 sl->sl_lines[t->t_startx] = NULL;
375                 continue;
376             }
377             sl->sl_lines[t->t_startx + 1] = xstrdup(msgstr);
378             if (t->t_nlines > 2)
379                 sl->sl_lines[t->t_startx + 2] = xstrdup("\n\n");
380             break;
381         }
382     }
383
384     for (i = 0; i < sl->sl_nlines; i++) {
385         if (sl->sl_lines[i] == NULL)
386             continue;
387         printf("%s", sl->sl_lines[i]);
388     }
389 }
390
391 void rpmDisplayQueryTags(FILE * f) {
392     const struct headerTagTableEntry * t;
393     int i;
394     const struct headerSprintfExtension * ext = rpmHeaderFormats;
395
396     for (i = 0, t = rpmTagTable; i < rpmTagTableSize; i++, t++) {
397         fprintf(f, "%s\n", t->name + 7);
398     }
399
400     while (ext->name) {
401         if (ext->type == HEADER_EXT_TAG)
402             fprintf(f, "%s\n", ext->name + 7), ext++;
403         else if (ext->type == HEADER_EXT_MORE)
404             ext = ext->u.more;
405         else
406             ext++;
407     }
408 }
409
410 int showMatches(QVA_t *qva, rpmdb db, dbiIndexSet matches, QVF_t showPackage)
411 {
412     Header h;
413     int ec = 0;
414     int i;
415
416     for (i = 0; i < dbiIndexSetCount(matches); i++) {
417         int rc;
418         unsigned int recOffset = dbiIndexRecordOffset(matches, i);
419         if (recOffset == 0)
420             continue;
421         rpmMessage(RPMMESS_DEBUG, _("record number %u\n"), recOffset);
422             
423         h = rpmdbGetRecord(db, recOffset);
424         if (h == NULL) {
425                 fprintf(stderr, _("error: could not read database record\n"));
426                 ec = 1;
427         } else {
428                 if ((rc = showPackage(qva, db, h)) != 0)
429                     ec = rc;
430                 headerFree(h);
431         }
432     }
433     return ec;
434 }
435
436 /*
437  * XXX Eliminate linkage loop into librpmbuild.a
438  */
439 int     (*parseSpecVec) (Spec *specp, const char *specFile, const char *rootdir,
440                 const char *buildRoot, int inBuildArch, const char *passPhrase,
441                 char *cookie, int anyarch, int force) = NULL;
442 void    (*freeSpecVec) (Spec spec) = NULL;
443 char    *specedit = NULL;
444
445 int rpmQueryVerify(QVA_t *qva, enum rpmQVSources source, const char * arg,
446         rpmdb db, QVF_t showPackage)
447 {
448     dbiIndexSet matches;
449     Header h;
450     int offset;
451     int rc;
452     int isSource;
453     int recNumber;
454     int retcode = 0;
455     char *end = NULL;
456
457   switch (source) {
458     case RPMQV_RPM:
459     { const char *myargv[2], **argv = myargv;;
460
461       /* XXX prepare for remglob */
462       argv[0] = arg;
463       argv[1] = NULL;
464       while ((arg = *argv++) != NULL) {
465         FD_t fd = Fopen(arg, "r.ufdio");
466         if (Ferror(fd)) {
467             /* XXX Fstrerror */
468             fprintf(stderr, _("open of %s failed: %s\n"), arg,
469 #ifndef NOTYET
470                         urlStrerror(arg));
471 #else
472                         Fstrerror(fd));
473 #endif
474             Fclose(fd);
475             retcode = 1;
476             break;
477         }
478
479         retcode = rpmReadPackageHeader(fd, &h, &isSource, NULL, NULL);
480
481         Fclose(fd);
482
483         switch (retcode) {
484         case 0:
485             if (h == NULL) {
486                 fprintf(stderr, _("old format source packages cannot "
487                         "be queried\n"));
488                 retcode = 1;
489                 break;
490             }
491             retcode = showPackage(qva, db, h);
492             headerFree(h);
493             break;
494         case 1:
495             fprintf(stderr, _("%s does not appear to be a RPM package\n"), arg);
496             /*@fallthrough@*/
497         case 2:
498             fprintf(stderr, _("query of %s failed\n"), arg);
499             retcode = 1;
500             break;
501         }
502       }
503     }   break;
504
505     case RPMQV_SPECFILE:
506         if (showPackage != showQueryPackage)
507             return 1;
508
509         /* XXX Eliminate linkage dependency loop */
510         if (parseSpecVec == NULL || freeSpecVec == NULL)
511             return 1;
512
513       { Spec spec = NULL;
514         Package pkg;
515         char * buildRoot = NULL;
516         int inBuildArch = 0;
517         char * passPhrase = "";
518         char *cookie = NULL;
519         int anyarch = 1;
520         int force = 1;
521
522         rc = parseSpecVec(&spec, arg, "/", buildRoot, inBuildArch, passPhrase,
523                 cookie, anyarch, force);
524         if (rc || spec == NULL) {
525             
526             fprintf(stderr, _("query of specfile %s failed, can't parse\n"), arg);
527             if (spec != NULL) freeSpecVec(spec);
528             retcode = 1;
529             break;
530         }
531
532         if (specedit != NULL) {
533             printNewSpecfile(spec);
534             freeSpecVec(spec);
535             retcode = 0;
536             break;
537         }
538
539         for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
540             showPackage(qva, NULL, pkg->header);
541         }
542         freeSpecVec(spec);
543       } break;
544
545     case RPMQV_ALL:
546         for (offset = rpmdbFirstRecNum(db);
547              offset != 0;
548              offset = rpmdbNextRecNum(db, offset)) {
549                 h = rpmdbGetRecord(db, offset);
550                 if (h == NULL) {
551                     fprintf(stderr, _("could not read database record!\n"));
552                     return 1;
553                 }
554                 if ((rc = showPackage(qva, db, h)) != 0)
555                     retcode = rc;
556                 headerFree(h);
557         }
558         break;
559
560     case RPMQV_GROUP:
561         if (rpmdbFindByGroup(db, arg, &matches)) {
562             fprintf(stderr, _("group %s does not contain any packages\n"), arg);
563             retcode = 1;
564         } else {
565             retcode = showMatches(qva, db, matches, showPackage);
566             dbiFreeIndexRecord(matches);
567         }
568         break;
569
570     case RPMQV_WHATPROVIDES:
571         if (rpmdbFindByProvides(db, arg, &matches)) {
572             fprintf(stderr, _("no package provides %s\n"), arg);
573             retcode = 1;
574         } else {
575             retcode = showMatches(qva, db, matches, showPackage);
576             dbiFreeIndexRecord(matches);
577         }
578         break;
579
580     case RPMQV_TRIGGEREDBY:
581         if (rpmdbFindByTriggeredBy(db, arg, &matches)) {
582             fprintf(stderr, _("no package triggers %s\n"), arg);
583             retcode = 1;
584         } else {
585             retcode = showMatches(qva, db, matches, showPackage);
586             dbiFreeIndexRecord(matches);
587         }
588         break;
589
590     case RPMQV_WHATREQUIRES:
591         if (rpmdbFindByRequiredBy(db, arg, &matches)) {
592             fprintf(stderr, _("no package requires %s\n"), arg);
593             retcode = 1;
594         } else {
595             retcode = showMatches(qva, db, matches, showPackage);
596             dbiFreeIndexRecord(matches);
597         }
598         break;
599
600     case RPMQV_PATH:
601         if (rpmdbFindByFile(db, arg, &matches)) {
602             int myerrno = 0;
603             if (access(arg, F_OK) != 0)
604                 myerrno = errno;
605             switch (myerrno) {
606             default:
607                 fprintf(stderr, _("file %s: %s\n"), arg, strerror(myerrno));
608                 break;
609             case 0:
610                 fprintf(stderr, _("file %s is not owned by any package\n"), arg);
611                 break;
612             }
613             retcode = 1;
614         } else {
615             retcode = showMatches(qva, db, matches, showPackage);
616             dbiFreeIndexRecord(matches);
617         }
618         break;
619
620     case RPMQV_DBOFFSET:
621         recNumber = strtoul(arg, &end, 10);
622         if ((*end) || (end == arg) || (recNumber == ULONG_MAX)) {
623             fprintf(stderr, _("invalid package number: %s\n"), arg);
624             return 1;
625         }
626         rpmMessage(RPMMESS_DEBUG, _("package record number: %d\n"), recNumber);
627         h = rpmdbGetRecord(db, recNumber);
628         if (h == NULL)  {
629             fprintf(stderr, _("record %d could not be read\n"), recNumber);
630             retcode = 1;
631         } else {
632             retcode = showPackage(qva, db, h);
633             headerFree(h);
634         }
635         break;
636
637     case RPMQV_PACKAGE:
638         rc = rpmdbFindByLabel(db, arg, &matches);
639         if (rc == 1) {
640             retcode = 1;
641             fprintf(stderr, _("package %s is not installed\n"), arg);
642         } else if (rc == 2) {
643             retcode = 1;
644             fprintf(stderr, _("error looking for package %s\n"), arg);
645         } else {
646             retcode = showMatches(qva, db, matches, showPackage);
647             dbiFreeIndexRecord(matches);
648         }
649         break;
650   }
651    
652     return retcode;
653 }
654
655 int rpmQuery(QVA_t *qva, enum rpmQVSources source, const char * arg)
656 {
657     rpmdb db = NULL;
658     int rc;
659
660     switch (source) {
661     case RPMQV_RPM:
662     case RPMQV_SPECFILE:
663         break;
664     default:
665         if (rpmdbOpen(qva->qva_prefix, &db, O_RDONLY, 0644)) {
666             fprintf(stderr, _("rpmQuery: rpmdbOpen() failed\n"));
667             return 1;
668         }
669         break;
670     }
671
672     rc = rpmQueryVerify(qva, source, arg, db, showQueryPackage);
673
674     if (db)
675         rpmdbClose(db);
676
677     return rc;
678 }
679
680 /* ======================================================================== */
681 #define POPT_QUERYFORMAT        1000
682 #define POPT_WHATREQUIRES       1001
683 #define POPT_WHATPROVIDES       1002
684 #define POPT_QUERYBYNUMBER      1003
685 #define POPT_TRIGGEREDBY        1004
686 #define POPT_DUMP               1005
687 #define POPT_SPECFILE           1006
688
689 /* ========== Query/Verify source popt args */
690 static void rpmQVSourceArgCallback( /*@unused@*/ poptContext con,
691         /*@unused@*/ enum poptCallbackReason reason,
692         const struct poptOption * opt, /*@unused@*/ const char * arg, 
693         const void * data)
694 {
695     QVA_t *qva = (QVA_t *) data;
696
697     switch (opt->val) {
698       case 'a': qva->qva_source |= RPMQV_ALL; qva->qva_sourceCount++; break;
699       case 'f': qva->qva_source |= RPMQV_PATH; qva->qva_sourceCount++; break;
700       case 'g': qva->qva_source |= RPMQV_GROUP; qva->qva_sourceCount++; break;
701       case 'p': qva->qva_source |= RPMQV_RPM; qva->qva_sourceCount++; break;
702       case POPT_WHATPROVIDES: qva->qva_source |= RPMQV_WHATPROVIDES; 
703                               qva->qva_sourceCount++; break;
704       case POPT_WHATREQUIRES: qva->qva_source |= RPMQV_WHATREQUIRES; 
705                               qva->qva_sourceCount++; break;
706       case POPT_TRIGGEREDBY: qva->qva_source |= RPMQV_TRIGGEREDBY;
707                               qva->qva_sourceCount++; break;
708
709 /* XXX SPECFILE is not verify sources */
710       case POPT_SPECFILE:
711         qva->qva_source |= RPMQV_SPECFILE;
712         qva->qva_sourceCount++;
713         break;
714       case POPT_QUERYBYNUMBER:
715         qva->qva_source |= RPMQV_DBOFFSET; 
716         qva->qva_sourceCount++;
717         break;
718     }
719 }
720
721 struct poptOption rpmQVSourcePoptTable[] = {
722         { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA, 
723                 rpmQVSourceArgCallback, 0, NULL, NULL },
724         { "file", 'f', 0, 0, 'f',
725                 N_("query package owning file"), "FILE" },
726         { "group", 'g', 0, 0, 'g',
727                 N_("query packages in group"), "GROUP" },
728         { "package", 'p', 0, 0, 'p',
729                 N_("query a package file"), NULL },
730         { "querybynumber", '\0', POPT_ARGFLAG_DOC_HIDDEN, 0, 
731                 POPT_QUERYBYNUMBER, NULL, NULL },
732         { "specfile", '\0', 0, 0, POPT_SPECFILE,
733                 N_("query a spec file"), NULL },
734         { "triggeredby", '\0', 0, 0, POPT_TRIGGEREDBY, 
735                 N_("query the pacakges triggered by the package"), "PACKAGE" },
736         { "whatrequires", '\0', 0, 0, POPT_WHATREQUIRES, 
737                 N_("query the packages which require a capability"), "CAPABILITY" },
738         { "whatprovides", '\0', 0, 0, POPT_WHATPROVIDES, 
739                 N_("query the packages which provide a capability"), "CAPABILITY" },
740         { 0, 0, 0, 0, 0,        NULL, NULL }
741 };
742
743 /* ========== Query specific popt args */
744
745 static void queryArgCallback(/*@unused@*/poptContext con, /*@unused@*/enum poptCallbackReason reason,
746                              const struct poptOption * opt, const char * arg, 
747                              const void * data)
748 {
749     QVA_t *qva = (QVA_t *) data;
750
751     switch (opt->val) {
752       case 'c': qva->qva_flags |= QUERY_FOR_CONFIG | QUERY_FOR_LIST; break;
753       case 'd': qva->qva_flags |= QUERY_FOR_DOCS | QUERY_FOR_LIST; break;
754       case 'l': qva->qva_flags |= QUERY_FOR_LIST; break;
755       case 's': qva->qva_flags |= QUERY_FOR_STATE | QUERY_FOR_LIST; break;
756       case POPT_DUMP: qva->qva_flags |= QUERY_FOR_DUMPFILES | QUERY_FOR_LIST; break;
757       case 'v': rpmIncreaseVerbosity();  break;
758
759       case POPT_QUERYFORMAT:
760       { char *qf = (char *)qva->qva_queryFormat;
761         if (qf) {
762             int len = strlen(qf) + strlen(arg) + 1;
763             qf = xrealloc(qf, len);
764             strcat(qf, arg);
765         } else {
766             qf = xmalloc(strlen(arg) + 1);
767             strcpy(qf, arg);
768         }
769         qva->qva_queryFormat = qf;
770       } break;
771     }
772 }
773
774 struct poptOption rpmQueryPoptTable[] = {
775         { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA, 
776                 queryArgCallback, 0, NULL, NULL },
777         { "configfiles", 'c', 0, 0, 'c',
778                 N_("list all configuration files"), NULL },
779         { "docfiles", 'd', 0, 0, 'd',
780                 N_("list all documentation files"), NULL },
781         { "dump", '\0', 0, 0, POPT_DUMP,
782                 N_("dump basic file information"), NULL },
783         { "list", 'l', 0, 0, 'l',
784                 N_("list files in package"), NULL },
785         { "qf", '\0', POPT_ARG_STRING | POPT_ARGFLAG_DOC_HIDDEN, 0, 
786                 POPT_QUERYFORMAT, NULL, NULL },
787         { "queryformat", '\0', POPT_ARG_STRING, 0, POPT_QUERYFORMAT,
788                 N_("use the following query format"), "QUERYFORMAT" },
789         { "specedit", '\0', POPT_ARG_STRING, &specedit, 0,
790                 N_("substitute i18n sections from the following catalogue"),
791                         "METACATALOGUE" },
792         { "state", 's', 0, 0, 's',
793                 N_("display the states of the listed files"), NULL },
794         { "verbose", 'v', 0, 0, 'v',
795                 N_("display a verbose file listing"), NULL },
796         { 0, 0, 0, 0, 0,        NULL, NULL }
797 };
798
799 /* ======================================================================== */