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