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