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