- fix: extra newline in many error messages (#23947).
[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(char * te, 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     sprintf(te, "%s %4d %-8s%-8s %10s %s %s", perms,
140         (int)nlink, ownerfield, groupfield, sizefield, timefield, namefield);
141     if (perms) free(perms);
142 }
143
144 static inline const char * queryHeader(Header h, const char * qfmt)
145 {
146     const char * errstr;
147     const char * str;
148
149     str = headerSprintf(h, qfmt, rpmTagTable, rpmHeaderFormats, &errstr);
150     if (str == NULL)
151         rpmError(RPMERR_QFMT, _("incorrect format: %s\n"), errstr);
152     return str;
153 }
154
155 static int countLinks(int_16 * fileRdevList, int_32 * fileInodeList, int nfiles,
156                 int xfile)
157 {
158     int nlink = 0;
159
160     /* XXX rpm-3.3.12 has not RPMTAG_FILEINODES */
161     if (!(fileRdevList && fileInodeList && nfiles > 0))
162         return 1;
163     while (nfiles-- > 0) {
164         if (fileRdevList[nfiles] != fileRdevList[xfile])
165             continue;
166         if (fileInodeList[nfiles] != fileInodeList[xfile])
167             continue;
168         nlink++;
169     }
170     return nlink;
171 }
172
173 int showQueryPackage(QVA_t *qva, /*@unused@*/rpmdb rpmdb, Header h)
174 {
175     char * t, * te;
176     
177     int queryFlags = qva->qva_flags;
178     const char *queryFormat = qva->qva_queryFormat;
179
180     int_32 count, type;
181     char * prefix = NULL;
182     const char ** dirNames = NULL;
183     const char ** baseNames = NULL;
184     const char ** fileMD5List = NULL;
185     const char ** fileOwnerList = NULL;
186     const char ** fileGroupList = NULL;
187     const char ** fileLinktoList = NULL;
188     const char * fileStatesList;
189     int_32 * fileFlagsList, * fileMTimeList, * fileSizeList;
190     int_32 * fileUIDList = NULL;
191     int_32 * fileGIDList = NULL;
192     int_32 * fileInodeList = NULL;
193     uint_16 * fileModeList;
194     uint_16 * fileRdevList;
195     int_32 * dirIndexes;
196     int rc = 0;         /* XXX FIXME: need real return code */
197     int nonewline = 0;
198     int i;
199
200     te = t = xmalloc(BUFSIZ);
201     *te = '\0';
202
203     if (!queryFormat && !queryFlags) {
204         const char * name, * version, * release;
205         headerNVR(h, &name, &version, &release);
206         te = stpcpy(te, name);
207         te = stpcpy( stpcpy(te, "-"), version);
208         te = stpcpy( stpcpy(te, "-"), release);
209         goto exit;
210     }
211
212     if (queryFormat) {
213         const char * str = queryHeader(h, queryFormat);
214         nonewline = 1;
215         if (str) {
216             size_t tb = (te - t);
217             size_t sb = strlen(str);
218
219             if (sb >= (BUFSIZ - tb)) {
220                 t = xrealloc(t, BUFSIZ+sb);
221                 te = t + tb;
222             }
223             te = stpcpy(te, str);
224             free((void *)str);
225         }
226     }
227
228     if (!(queryFlags & QUERY_FOR_LIST))
229         goto exit;
230
231     if (!headerGetEntry(h, RPMTAG_BASENAMES, &type, 
232                                 (void **) &baseNames, &count)) {
233         te = stpcpy(te, _("(contains no files)"));
234         goto exit;
235     }
236     if (!headerGetEntry(h, RPMTAG_FILESTATES, &type, 
237                          (void **) &fileStatesList, &count)) {
238         fileStatesList = NULL;
239     }
240     headerGetEntry(h, RPMTAG_DIRNAMES, NULL, (void **) &dirNames, NULL);
241     headerGetEntry(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
242     headerGetEntry(h, RPMTAG_FILEFLAGS, &type, (void **)&fileFlagsList, &count);
243     headerGetEntry(h, RPMTAG_FILESIZES, &type, (void **) &fileSizeList, &count);
244     headerGetEntry(h, RPMTAG_FILEMODES, &type, (void **) &fileModeList, &count);
245     headerGetEntry(h, RPMTAG_FILEMTIMES, &type, (void **)&fileMTimeList,&count);
246     headerGetEntry(h, RPMTAG_FILERDEVS, &type, (void **) &fileRdevList, &count);
247     headerGetEntry(h, RPMTAG_FILEINODES, &type, (void **)&fileInodeList,&count);
248     headerGetEntry(h, RPMTAG_FILELINKTOS,&type,(void **)&fileLinktoList,&count);
249     headerGetEntry(h, RPMTAG_FILEMD5S, &type, (void **) &fileMD5List, &count);
250
251     if (!headerGetEntry(h, RPMTAG_FILEUIDS, &type, 
252                          (void **) &fileUIDList, &count)) {
253         fileUIDList = NULL;
254     } else if (!headerGetEntry(h, RPMTAG_FILEGIDS, &type, 
255                              (void **) &fileGIDList, &count)) {
256         fileGIDList = NULL;
257     }
258
259     if (!headerGetEntry(h, RPMTAG_FILEUSERNAME, &type, 
260                          (void **) &fileOwnerList, &count)) {
261         fileOwnerList = NULL;
262     } else if (!headerGetEntry(h, RPMTAG_FILEGROUPNAME, &type, 
263                              (void **) &fileGroupList, &count)) {
264         fileGroupList = NULL;
265     }
266
267     for (i = 0; i < count; i++) {
268         /* If querying only docs, skip non-doc files. */
269         if ((queryFlags & QUERY_FOR_DOCS)
270           && !(fileFlagsList[i] & RPMFILE_DOC))
271             continue;
272         /* If querying only configs, skip non-config files. */
273         if ((queryFlags & QUERY_FOR_CONFIG)
274           && !(fileFlagsList[i] & RPMFILE_CONFIG))
275             continue;
276
277         if (!rpmIsVerbose() && prefix)
278             te = stpcpy(te, prefix);
279
280         if (queryFlags & QUERY_FOR_STATE) {
281             if (fileStatesList) {
282                 switch (fileStatesList[i]) {
283                 case RPMFILE_STATE_NORMAL:
284                     te = stpcpy(te, _("normal        ")); break;
285                 case RPMFILE_STATE_REPLACED:
286                     te = stpcpy(te, _("replaced      ")); break;
287                 case RPMFILE_STATE_NOTINSTALLED:
288                     te = stpcpy(te, _("not installed ")); break;
289                 case RPMFILE_STATE_NETSHARED:
290                     te = stpcpy(te, _("net shared    ")); break;
291                 default:
292                     sprintf(te, _("(unknown %3d) "), (int)fileStatesList[i]);
293                     te += strlen(te);
294                     break;
295                 }
296             } else {
297                 te = stpcpy(te, _("(no state)    "));
298             }
299         }
300
301         if (queryFlags & QUERY_FOR_DUMPFILES) {
302             sprintf(te, "%s%s %d %d %s 0%o ", 
303                                    dirNames[dirIndexes[i]], baseNames[i],
304                                    fileSizeList[i], fileMTimeList[i],
305                                    fileMD5List[i], fileModeList[i]);
306             te += strlen(te);
307
308             if (fileOwnerList && fileGroupList) {
309                 sprintf(te, "%s %s", fileOwnerList[i], fileGroupList[i]);
310                 te += strlen(te);
311             } else if (fileUIDList && fileGIDList) {
312                 sprintf(te, "%d %d", fileUIDList[i], fileGIDList[i]);
313                 te += strlen(te);
314             } else {
315                 rpmError(RPMERR_INTERNAL,
316                         _("package has neither file owner or id lists\n"));
317             }
318
319             sprintf(te, " %s %s %u ", 
320                                  fileFlagsList[i] & RPMFILE_CONFIG ? "1" : "0",
321                                  fileFlagsList[i] & RPMFILE_DOC ? "1" : "0",
322                                  (unsigned)fileRdevList[i]);
323             te += strlen(te);
324
325             if (strlen(fileLinktoList[i]))
326                 sprintf(te, "%s", fileLinktoList[i]);
327             else
328                 sprintf(te, "X");
329             te += strlen(te);
330         } else if (!rpmIsVerbose()) {
331             te = stpcpy(te, dirNames[dirIndexes[i]]);
332             te = stpcpy(te, baseNames[i]);
333         } else {
334             char * filespec;
335             int nlink;
336
337             filespec = xmalloc(strlen(dirNames[dirIndexes[i]])
338                                               + strlen(baseNames[i]) + 1);
339             strcpy(filespec, dirNames[dirIndexes[i]]);
340             strcat(filespec, baseNames[i]);
341                                         
342             nlink = countLinks(fileRdevList, fileInodeList, count, i);
343             if (fileOwnerList && fileGroupList) {
344                 printFileInfo(te, filespec, fileSizeList[i],
345                                               fileModeList[i], fileMTimeList[i],
346                                               fileRdevList[i], nlink,
347                                               fileOwnerList[i], 
348                                               fileGroupList[i], -1, 
349                                               -1, fileLinktoList[i]);
350                 te += strlen(te);
351             } else if (fileUIDList && fileGIDList) {
352                 printFileInfo(te, filespec, fileSizeList[i],
353                                               fileModeList[i], fileMTimeList[i],
354                                               fileRdevList[i], nlink,
355                                               NULL, NULL, fileUIDList[i], 
356                                               fileGIDList[i], 
357                                               fileLinktoList[i]);
358                 te += strlen(te);
359             } else {
360                 rpmError(RPMERR_INTERNAL,
361                         _("package has neither file owner or id lists\n"));
362             }
363
364             free(filespec);
365         }
366         if (te > t) {
367             *te++ = '\n';
368             *te = '\0';
369             rpmMessage(RPMMESS_NORMAL, "%s", t);
370             te = t;
371             *t = '\0';
372         }
373     }
374             
375     rc = 0;
376
377 exit:
378     if (te > t) {
379         if (!nonewline) {
380             *te++ = '\n';
381             *te = '\0';
382         }
383         rpmMessage(RPMMESS_NORMAL, "%s", t);
384     }
385     if (t) free(t);
386     if (dirNames) free(dirNames);
387     if (baseNames) free(baseNames);
388     if (fileLinktoList) free(fileLinktoList);
389     if (fileMD5List) free(fileMD5List);
390     if (fileOwnerList) free(fileOwnerList);
391     if (fileGroupList) free(fileGroupList);
392     return rc;
393 }
394
395 static void
396 printNewSpecfile(Spec spec)
397 {
398     Header h = spec->packages->header;
399     struct speclines *sl = spec->sl;
400     struct spectags *st = spec->st;
401     const char * msgstr = NULL;
402     int i, j;
403
404     if (sl == NULL || st == NULL)
405         return;
406
407     for (i = 0; i < st->st_ntags; i++) {
408         struct spectag * t = st->st_t + i;
409         const char * tn = tagName(t->t_tag);
410         const char * errstr;
411         char fmt[128];
412
413         fmt[0] = '\0';
414         (void) stpcpy( stpcpy( stpcpy( fmt, "%{"), tn), "}\n");
415         if (msgstr) free((void *)msgstr);
416         msgstr = headerSprintf(h, fmt, rpmTagTable, rpmHeaderFormats, &errstr);
417         if (msgstr == NULL) {
418             rpmError(RPMERR_QFMT, _("can't query %s: %s\n"), tn, errstr);
419             return;
420         }
421
422         switch(t->t_tag) {
423         case RPMTAG_SUMMARY:
424         case RPMTAG_GROUP:
425             free(sl->sl_lines[t->t_startx]);
426             sl->sl_lines[t->t_startx] = NULL;
427             if (t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))
428                 continue;
429             {   char *buf = xmalloc(strlen(tn) + sizeof(": ") + strlen(msgstr));
430                 (void) stpcpy( stpcpy( stpcpy(buf, tn), ": "), msgstr);
431                 sl->sl_lines[t->t_startx] = buf;
432             }
433             break;
434         case RPMTAG_DESCRIPTION:
435             for (j = 1; j < t->t_nlines; j++) {
436                 free(sl->sl_lines[t->t_startx + j]);
437                 sl->sl_lines[t->t_startx + j] = NULL;
438             }
439             if (t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG)) {
440                 free(sl->sl_lines[t->t_startx]);
441                 sl->sl_lines[t->t_startx] = NULL;
442                 continue;
443             }
444             sl->sl_lines[t->t_startx + 1] = xstrdup(msgstr);
445             if (t->t_nlines > 2)
446                 sl->sl_lines[t->t_startx + 2] = xstrdup("\n\n");
447             break;
448         }
449     }
450     if (msgstr) free((void *)msgstr);
451
452     for (i = 0; i < sl->sl_nlines; i++) {
453         if (sl->sl_lines[i] == NULL)
454             continue;
455         printf("%s", sl->sl_lines[i]);
456     }
457 }
458
459 void rpmDisplayQueryTags(FILE * f)
460 {
461     const struct headerTagTableEntry * t;
462     int i;
463     const struct headerSprintfExtension * ext = rpmHeaderFormats;
464
465     for (i = 0, t = rpmTagTable; i < rpmTagTableSize; i++, t++) {
466         fprintf(f, "%s\n", t->name + 7);
467     }
468
469     while (ext->name) {
470         if (ext->type == HEADER_EXT_MORE) {
471             ext = ext->u.more;
472             continue;
473         }
474         /* XXX don't print query tags twice. */
475         for (i = 0, t = rpmTagTable; i < rpmTagTableSize; i++, t++) {
476             if (!strcmp(t->name, ext->name))
477                 break;
478         }
479         if (i >= rpmTagTableSize && ext->type == HEADER_EXT_TAG)
480             fprintf(f, "%s\n", ext->name + 7);
481         ext++;
482     }
483 }
484
485 int showMatches(QVA_t *qva, rpmdbMatchIterator mi, QVF_t showPackage)
486 {
487     Header h;
488     int ec = 0;
489
490     while ((h = rpmdbNextIterator(mi)) != NULL) {
491         int rc;
492         if ((rc = showPackage(qva, rpmdbGetIteratorRpmDB(mi), h)) != 0)
493             ec = rc;
494     }
495     rpmdbFreeIterator(mi);
496     return ec;
497 }
498
499 /**
500  * @todo Eliminate linkage loop into librpmbuild.a
501  */
502 int     (*parseSpecVec) (Spec *specp, const char *specFile, const char *rootdir,
503                 const char *buildRoot, int inBuildArch, const char *passPhrase,
504                 char *cookie, int anyarch, int force) = NULL;
505 /**
506  * @todo Eliminate linkage loop into librpmbuild.a
507  */
508 void    (*freeSpecVec) (Spec spec) = NULL;
509
510 int rpmQueryVerify(QVA_t *qva, rpmQVSources source, const char * arg,
511         rpmdb rpmdb, QVF_t showPackage)
512 {
513     rpmdbMatchIterator mi = NULL;
514     Header h;
515     int rc;
516     int isSource;
517     int retcode = 0;
518     char *end = NULL;
519
520     switch (source) {
521     case RPMQV_RPM:
522     {   int argc = 0;
523         const char ** argv = NULL;
524         int i;
525
526         rc = rpmGlob(arg, &argc, &argv);
527         if (rc)
528             return 1;
529         for (i = 0; i < argc; i++) {
530             FD_t fd;
531             fd = Fopen(argv[i], "r.ufdio");
532             if (Ferror(fd)) {
533                 /* XXX Fstrerror */
534                 rpmError(RPMERR_OPEN, _("open of %s failed: %s\n"), argv[i],
535 #ifndef NOTYET
536                         urlStrerror(argv[i])
537 #else
538                         Fstrerror(fd)
539 #endif
540                 );
541                 if (fd) Fclose(fd);
542                 retcode = 1;
543                 break;
544             }
545
546             retcode = rpmReadPackageHeader(fd, &h, &isSource, NULL, NULL);
547
548             Fclose(fd);
549
550             switch (retcode) {
551             case 0:
552                 if (h == NULL) {
553                     rpmError(RPMERR_QUERY,
554                         _("old format source packages cannot be queried\n"));
555                     retcode = 1;
556                     break;
557                 }
558                 retcode = showPackage(qva, rpmdb, h);
559                 headerFree(h);
560                 break;
561             case 1:
562                 rpmError(RPMERR_QUERY,
563                         _("%s does not appear to be a RPM package\n"), argv[i]);
564                 /*@fallthrough@*/
565             case 2:
566                 rpmError(RPMERR_QUERY,
567                         _("query of %s failed\n"), argv[i]);
568                 retcode = 1;
569                 break;
570             }
571         }
572         if (argv) {
573             for (i = 0; i < argc; i++)
574                 free((void *)argv[i]);
575             free((void *)argv);
576         }
577     }   break;
578
579     case RPMQV_SPECFILE:
580         if (showPackage != showQueryPackage)
581             return 1;
582
583         /* XXX Eliminate linkage dependency loop */
584         if (parseSpecVec == NULL || freeSpecVec == NULL)
585             return 1;
586
587       { Spec spec = NULL;
588         Package pkg;
589         char * buildRoot = NULL;
590         int inBuildArch = 0;
591         char * passPhrase = "";
592         char *cookie = NULL;
593         int anyarch = 1;
594         int force = 1;
595
596         rc = parseSpecVec(&spec, arg, "/", buildRoot, inBuildArch, passPhrase,
597                 cookie, anyarch, force);
598         if (rc || spec == NULL) {
599             
600             rpmError(RPMERR_QUERY,
601                         _("query of specfile %s failed, can't parse\n"), arg);
602             if (spec != NULL) freeSpecVec(spec);
603             retcode = 1;
604             break;
605         }
606
607         if (specedit) {
608             printNewSpecfile(spec);
609             freeSpecVec(spec);
610             retcode = 0;
611             break;
612         }
613
614         for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
615             showPackage(qva, NULL, pkg->header);
616         }
617         freeSpecVec(spec);
618       } break;
619
620     case RPMQV_ALL:
621         /* RPMDBI_PACKAGES */
622         mi = rpmdbInitIterator(rpmdb, RPMDBI_PACKAGES, NULL, 0);
623         if (mi == NULL) {
624             rpmError(RPMERR_QUERYINFO, _("no packages\n"));
625             retcode = 1;
626         } else {
627             retcode = showMatches(qva, mi, showPackage);
628         }
629         break;
630
631     case RPMQV_GROUP:
632         mi = rpmdbInitIterator(rpmdb, RPMTAG_GROUP, arg, 0);
633         if (mi == NULL) {
634             rpmError(RPMERR_QUERYINFO,
635                 _("group %s does not contain any packages\n"), arg);
636             retcode = 1;
637         } else {
638             retcode = showMatches(qva, mi, showPackage);
639         }
640         break;
641
642     case RPMQV_TRIGGEREDBY:
643         mi = rpmdbInitIterator(rpmdb, RPMTAG_TRIGGERNAME, arg, 0);
644         if (mi == NULL) {
645             rpmError(RPMERR_QUERYINFO, _("no package triggers %s\n"), arg);
646             retcode = 1;
647         } else {
648             retcode = showMatches(qva, mi, showPackage);
649         }
650         break;
651
652     case RPMQV_WHATREQUIRES:
653         mi = rpmdbInitIterator(rpmdb, RPMTAG_REQUIRENAME, arg, 0);
654         if (mi == NULL) {
655             rpmError(RPMERR_QUERYINFO, _("no package requires %s\n"), arg);
656             retcode = 1;
657         } else {
658             retcode = showMatches(qva, mi, showPackage);
659         }
660         break;
661
662     case RPMQV_WHATPROVIDES:
663         if (arg[0] != '/') {
664             mi = rpmdbInitIterator(rpmdb, RPMTAG_PROVIDENAME, arg, 0);
665             if (mi == NULL) {
666                 rpmError(RPMERR_QUERYINFO, _("no package provides %s\n"), arg);
667                 retcode = 1;
668             } else {
669                 retcode = showMatches(qva, mi, showPackage);
670             }
671             break;
672         }
673         /*@fallthrough@*/
674     case RPMQV_PATH:
675     {   const char * s;
676         char * fn;
677
678         for (s = arg; *s; s++)
679             if (!(*s == '.' || *s == '/')) break;
680
681         if (*s == '\0') {
682             char fnbuf[PATH_MAX];
683             fn = /*@-unrecog@*/ realpath(arg, fnbuf) /*@=unrecog@*/;
684             fn = xstrdup( (fn ? fn : arg) );
685         } else
686             fn = xstrdup(arg);
687         (void) rpmCleanPath(fn);
688
689         mi = rpmdbInitIterator(rpmdb, RPMTAG_BASENAMES, fn, 0);
690         if (mi == NULL) {
691             int myerrno = 0;
692             if (access(fn, F_OK) != 0)
693                 myerrno = errno;
694             switch (myerrno) {
695             default:
696                 rpmError(RPMERR_QUERY,
697                         _("file %s: %s\n"), fn, strerror(myerrno));
698                 break;
699             case 0:
700                 rpmError(RPMERR_QUERYINFO,
701                         _("file %s is not owned by any package\n"), fn);
702                 break;
703             }
704             retcode = 1;
705         } else {
706             retcode = showMatches(qva, mi, showPackage);
707         }
708         free((void *)fn);
709     }   break;
710
711     case RPMQV_DBOFFSET:
712     {   int mybase = 10;
713         const char * myarg = arg;
714         int recOffset;
715
716         /* XXX should be in strtoul */
717         if (*myarg == '0') {
718             myarg++;
719             mybase = 8;
720             if (*myarg == 'x') {
721                 myarg++;
722                 mybase = 16;
723             }
724         }
725         recOffset = strtoul(myarg, &end, mybase);
726         if ((*end) || (end == arg) || (recOffset == ULONG_MAX)) {
727             rpmError(RPMERR_QUERY, _("invalid package number: %s\n"), arg);
728             return 1;
729         }
730         rpmMessage(RPMMESS_DEBUG, _("package record number: %u\n"), recOffset);
731         /* RPMDBI_PACKAGES */
732         mi = rpmdbInitIterator(rpmdb, RPMDBI_PACKAGES, &recOffset, sizeof(recOffset));
733         if (mi == NULL) {
734             rpmError(RPMERR_QUERY,
735                 _("record %d could not be read\n"), recOffset);
736             retcode = 1;
737         } else {
738             retcode = showMatches(qva, mi, showPackage);
739         }
740     }   break;
741
742     case RPMQV_PACKAGE:
743         /* XXX HACK to get rpmdbFindByLabel out of the API */
744         mi = rpmdbInitIterator(rpmdb, RPMDBI_LABEL, arg, 0);
745         if (mi == NULL) {
746             rpmError(RPMERR_QUERYINFO, _("package %s is not installed\n"), arg);
747             retcode = 1;
748         } else {
749             retcode = showMatches(qva, mi, showPackage);
750         }
751         break;
752     }
753    
754     return retcode;
755 }
756
757 int rpmQuery(QVA_t *qva, rpmQVSources source, const char * arg)
758 {
759     rpmdb rpmdb = NULL;
760     int rc;
761
762     switch (source) {
763     case RPMQV_RPM:
764     case RPMQV_SPECFILE:
765         break;
766     default:
767         if (rpmdbOpen(qva->qva_prefix, &rpmdb, O_RDONLY, 0644))
768             return 1;
769         break;
770     }
771
772     rc = rpmQueryVerify(qva, source, arg, rpmdb, showQueryPackage);
773
774     if (rpmdb)
775         rpmdbClose(rpmdb);
776
777     return rc;
778 }