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