switched exists() to rpmfileexists()
[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 "build/rpmbuild.h"
8 #include "popt/popt.h"
9 #include "url.h"
10
11 static char * permsString(int mode);
12 static void printHeader(Header h, int queryFlags, char * queryFormat);
13 static void showMatches(rpmdb db, dbiIndexSet matches, int queryFlags, 
14                         char * queryFormat);
15 static void printFileInfo(char * name, unsigned int size, unsigned short mode,
16                           unsigned int mtime, unsigned short rdev,
17                           char * owner, char * group, int uid, int gid,
18                           char * linkto);
19
20 #define POPT_QUERYFORMAT        1000
21 #define POPT_WHATREQUIRES       1001
22 #define POPT_WHATPROVIDES       1002
23 #define POPT_QUERYBYNUMBER      1003
24 #define POPT_TRIGGEREDBY        1004
25 #define POPT_DUMP               1005
26 #define POPT_SPECFILE           1006
27
28 static void queryArgCallback(poptContext con, enum poptCallbackReason reason,
29                              const struct poptOption * opt, const char * arg, 
30                              struct rpmQueryArguments * data);
31
32 struct poptOption rpmQuerySourcePoptTable[] = {
33         { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA, 
34                 queryArgCallback, 0, NULL, NULL },
35         { "file", 'f', 0, 0, 'f', "query package owning file", "FILE" },
36         { "group", 'g', 0, 0, 'g', "query packages in group", "GROUP" },
37         { "package", 'p', 0, 0, 'p', "query a package file", NULL },
38         { "specfile", '\0', 0, 0, POPT_SPECFILE, "query a spec file", NULL },
39         { "triggeredby", '\0', 0, 0, POPT_TRIGGEREDBY, 
40                 "query the pacakges triggered by the package", "PACKAGE" },
41         { "whatrequires", '\0', 0, 0, POPT_WHATREQUIRES, 
42                 "query the packages which require a capability", "CAPABILITY" },
43         { "whatprovides", '\0', 0, 0, POPT_WHATPROVIDES, 
44                 "query the packages which provide a capability", "CAPABILITY" },
45         { 0, 0, 0, 0, 0,        NULL, NULL }
46 };
47
48 struct poptOption rpmQueryPoptTable[] = {
49         { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA, 
50                 queryArgCallback, 0, NULL, NULL },
51         { "configfiles", 'c', 0, 0, 'c', "list all configuration files", NULL },
52         { "docfiles", 'd', 0, 0, 'd', "list all documetnation files", NULL },
53         { "dump", '\0', 0, 0, POPT_DUMP, "dump basic file information", NULL },
54         { "list", 'l', 0, 0, 'l', "list files in package", NULL },
55         { "qf", '\0', POPT_ARG_STRING | POPT_ARGFLAG_DOC_HIDDEN, 0, 
56                 POPT_QUERYFORMAT, NULL, NULL },
57         { "querybynumber", '\0', POPT_ARGFLAG_DOC_HIDDEN, 0, 
58                 POPT_QUERYBYNUMBER, NULL, NULL },
59         { "queryformat", '\0', POPT_ARG_STRING, 0, POPT_QUERYFORMAT,
60                 "use the following query format", "QUERYFORMAT" },
61         { "state", 's', 0, 0, 's', "display the states of the listed files", NULL },
62         { "verbose", 'v', 0, 0, 'v', "display a verbose file listing", NULL },
63         { 0, 0, 0, 0, 0,        NULL, NULL }
64 };
65
66 static void queryArgCallback(poptContext con, enum poptCallbackReason reason,
67                              const struct poptOption * opt, const char * arg, 
68                              struct rpmQueryArguments * data) {
69     int len;
70
71     switch (opt->val) {
72       case 'c': data->flags |= QUERY_FOR_CONFIG | QUERY_FOR_LIST; break;
73       case 'd': data->flags |= QUERY_FOR_DOCS | QUERY_FOR_LIST; break;
74       case 'l': data->flags |= QUERY_FOR_LIST; break;
75       case 's': data->flags |= QUERY_FOR_STATE | QUERY_FOR_LIST; break;
76       case POPT_DUMP: data->flags |= QUERY_FOR_DUMPFILES | QUERY_FOR_LIST; break;
77
78       case 'a': data->source |= QUERY_ALL; data->sourceCount++; break;
79       case 'f': data->source |= QUERY_PATH; data->sourceCount++; break;
80       case 'g': data->source |= QUERY_GROUP; data->sourceCount++; break;
81       case 'p': data->source |= QUERY_RPM; data->sourceCount++; break;
82       case 'v': rpmIncreaseVerbosity();  break;
83       case POPT_SPECFILE: data->source |= QUERY_SPECFILE; data->sourceCount++; break;
84       case POPT_WHATPROVIDES: data->source |= QUERY_WHATPROVIDES; 
85                               data->sourceCount++; break;
86       case POPT_WHATREQUIRES: data->source |= QUERY_WHATREQUIRES; 
87                               data->sourceCount++; break;
88       case POPT_QUERYBYNUMBER: data->source |= QUERY_DBOFFSET; 
89                               data->sourceCount++; break;
90       case POPT_TRIGGEREDBY: data->source |= QUERY_TRIGGEREDBY;
91                               data->sourceCount++; break;
92
93       case POPT_QUERYFORMAT:
94         if (data->queryFormat) {
95             len = strlen(data->queryFormat) + strlen(arg) + 1;
96             data->queryFormat = realloc(data->queryFormat, len);
97             strcat(data->queryFormat, arg);
98         } else {
99             data->queryFormat = malloc(strlen(arg) + 1);
100             strcpy(data->queryFormat, arg);
101         }
102         break;
103     }
104 }
105
106 static int queryHeader(Header h, char * chptr) {
107     char * str;
108     char * error;
109
110     str = headerSprintf(h, chptr, rpmTagTable, rpmHeaderFormats, &error);
111     if (!str) {
112         fprintf(stderr, _("error in format: %s\n"), error);
113         return 1;
114     }
115
116     fputs(str, stdout);
117
118     return 0;
119 }
120
121 static void printHeader(Header h, int queryFlags, char * queryFormat) {
122     char * name, * version, * release;
123     int_32 count, type;
124     char * prefix = NULL;
125     char ** fileList, ** fileMD5List;
126     char * fileStatesList;
127     char ** fileOwnerList = NULL;
128     char ** fileGroupList = NULL;
129     char ** fileLinktoList;
130     int_32 * fileFlagsList, * fileMTimeList, * fileSizeList;
131     int_32 * fileUIDList, * fileGIDList;
132     uint_16 * fileModeList;
133     uint_16 * fileRdevList;
134     int i;
135
136     headerGetEntry(h, RPMTAG_NAME, &type, (void **) &name, &count);
137     headerGetEntry(h, RPMTAG_VERSION, &type, (void **) &version, &count);
138     headerGetEntry(h, RPMTAG_RELEASE, &type, (void **) &release, &count);
139
140     if (!queryFormat && !queryFlags) {
141         fprintf(stdout, "%s-%s-%s\n", name, version, release);
142     } else {
143         if (queryFormat)
144             queryHeader(h, queryFormat);
145
146         if (queryFlags & QUERY_FOR_LIST) {
147             if (!headerGetEntry(h, RPMTAG_FILENAMES, &type, (void **) &fileList, 
148                  &count)) {
149                 fputs(_("(contains no files)"), stdout);
150                 fputs("\n", stdout);
151             } else {
152                 if (!headerGetEntry(h, RPMTAG_FILESTATES, &type, 
153                          (void **) &fileStatesList, &count)) {
154                     fileStatesList = NULL;
155                 }
156                 headerGetEntry(h, RPMTAG_FILEFLAGS, &type, 
157                          (void **) &fileFlagsList, &count);
158                 headerGetEntry(h, RPMTAG_FILESIZES, &type, 
159                          (void **) &fileSizeList, &count);
160                 headerGetEntry(h, RPMTAG_FILEMODES, &type, 
161                          (void **) &fileModeList, &count);
162                 headerGetEntry(h, RPMTAG_FILEMTIMES, &type, 
163                          (void **) &fileMTimeList, &count);
164                 headerGetEntry(h, RPMTAG_FILERDEVS, &type, 
165                          (void **) &fileRdevList, &count);
166                 headerGetEntry(h, RPMTAG_FILELINKTOS, &type, 
167                          (void **) &fileLinktoList, &count);
168                 headerGetEntry(h, RPMTAG_FILEMD5S, &type, 
169                          (void **) &fileMD5List, &count);
170
171                 if (!headerGetEntry(h, RPMTAG_FILEUIDS, &type, 
172                          (void **) &fileUIDList, &count)) {
173                     fileUIDList = NULL;
174                 } else {
175                     headerGetEntry(h, RPMTAG_FILEGIDS, &type, 
176                              (void **) &fileGIDList, &count);
177                 }
178
179                 if (!headerGetEntry(h, RPMTAG_FILEUSERNAME, &type, 
180                          (void **) &fileOwnerList, &count)) {
181                     fileOwnerList = NULL;
182                 } else {
183                     headerGetEntry(h, RPMTAG_FILEGROUPNAME, &type, 
184                              (void **) &fileGroupList, &count);
185                 }
186
187                 for (i = 0; i < count; i++) {
188                     if (!((queryFlags & QUERY_FOR_DOCS) || 
189                           (queryFlags & QUERY_FOR_CONFIG)) 
190                         || ((queryFlags & QUERY_FOR_DOCS) && 
191                             (fileFlagsList[i] & RPMFILE_DOC))
192                         || ((queryFlags & QUERY_FOR_CONFIG) && 
193                             (fileFlagsList[i] & RPMFILE_CONFIG))) {
194
195                         if (!rpmIsVerbose())
196                             prefix ? fputs(prefix, stdout) : 0;
197
198                         if (queryFlags & QUERY_FOR_STATE) {
199                             if (fileStatesList) {
200                                 switch (fileStatesList[i]) {
201                                   case RPMFILE_STATE_NORMAL:
202                                     fputs(_("normal        "), stdout); break;
203                                   case RPMFILE_STATE_REPLACED:
204                                     fputs(_("replaced      "), stdout); break;
205                                   case RPMFILE_STATE_NETSHARED:
206                                     fputs(_("net shared    "), stdout); break;
207                                   case RPMFILE_STATE_NOTINSTALLED:
208                                     fputs(_("not installed "), stdout); break;
209                                   default:
210                                     fprintf(stdout, _("(unknown %3d) "), 
211                                           fileStatesList[i]);
212                                 }
213                             } else {
214                                 fputs(    _("(no state)    "), stdout);
215                             }
216                         }
217                             
218                         if (queryFlags & QUERY_FOR_DUMPFILES) {
219                             fprintf(stdout, "%s %d %d %s 0%o ", fileList[i],
220                                    fileSizeList[i], fileMTimeList[i],
221                                    fileMD5List[i], fileModeList[i]);
222
223                             if (fileOwnerList)
224                                 fprintf(stdout, "%s %s", fileOwnerList[i], 
225                                                 fileGroupList[i]);
226                             else if (fileUIDList)
227                                 fprintf(stdout, "%d %d", fileUIDList[i], 
228                                                 fileGIDList[i]);
229                             else {
230                                 rpmError(RPMERR_INTERNAL, _("package has "
231                                         "neither file owner or id lists"));
232                             }
233
234                             fprintf(stdout, " %s %s %u ", 
235                                  fileFlagsList[i] & RPMFILE_CONFIG ? "1" : "0",
236                                  fileFlagsList[i] & RPMFILE_DOC ? "1" : "0",
237                                  (unsigned)fileRdevList[i]);
238
239                             if (strlen(fileLinktoList[i]))
240                                 fprintf(stdout, "%s\n", fileLinktoList[i]);
241                             else
242                                 fprintf(stdout, "X\n");
243
244                         } else if (!rpmIsVerbose()) {
245                             fputs(fileList[i], stdout);
246                             fputs("\n", stdout);
247                         } else if (fileOwnerList) 
248                             printFileInfo(fileList[i], fileSizeList[i],
249                                           fileModeList[i], fileMTimeList[i],
250                                           fileRdevList[i], fileOwnerList[i], 
251                                           fileGroupList[i], -1, 
252                                           -1, fileLinktoList[i]);
253                         else if (fileUIDList) {
254                             printFileInfo(fileList[i], fileSizeList[i],
255                                           fileModeList[i], fileMTimeList[i],
256                                           fileRdevList[i], NULL, 
257                                           NULL, fileUIDList[i], 
258                                           fileGIDList[i], fileLinktoList[i]);
259                         } else {
260                             rpmError(RPMERR_INTERNAL, _("package has "
261                                     "neither file owner or id lists"));
262                         }
263                     }
264                 }
265             
266                 free(fileList);
267                 free(fileLinktoList);
268                 free(fileMD5List);
269                 if (fileOwnerList) free(fileOwnerList);
270                 if (fileGroupList) free(fileGroupList);
271             }
272         }
273     }
274 }
275
276 static char * permsString(int mode) {
277     static char perms[11];
278
279     strcpy(perms, "----------");
280    
281     if (mode & S_IRUSR) perms[1] = 'r';
282     if (mode & S_IWUSR) perms[2] = 'w';
283     if (mode & S_IXUSR) perms[3] = 'x';
284  
285     if (mode & S_IRGRP) perms[4] = 'r';
286     if (mode & S_IWGRP) perms[5] = 'w';
287     if (mode & S_IXGRP) perms[6] = 'x';
288
289     if (mode & S_IROTH) perms[7] = 'r';
290     if (mode & S_IWOTH) perms[8] = 'w';
291     if (mode & S_IXOTH) perms[9] = 'x';
292
293
294     if (mode & S_ISVTX)
295         perms[9] = ((mode & S_IXOTH) ? 't' : 'T');
296
297     if (mode & S_ISUID) {
298         if (mode & S_IXUSR) 
299             perms[3] = 's'; 
300         else
301             perms[3] = 'S'; 
302     }
303
304     if (mode & S_ISGID) {
305         if (mode & S_IXGRP) 
306             perms[6] = 's'; 
307         else
308             perms[6] = 'S'; 
309     }
310
311     if (S_ISDIR(mode)) 
312         perms[0] = 'd';
313     else if (S_ISLNK(mode)) {
314         perms[0] = 'l';
315     }
316     else if (S_ISFIFO(mode)) 
317         perms[0] = 'p';
318     else if (S_ISSOCK(mode)) 
319         perms[0] = 's';
320     else if (S_ISCHR(mode)) {
321         perms[0] = 'c';
322     } else if (S_ISBLK(mode)) {
323         perms[0] = 'b';
324     }
325
326     return perms;
327 }
328
329 static void printFileInfo(char * name, unsigned int size, unsigned short mode,
330                           unsigned int mtime, unsigned short rdev,
331                           char * owner, char * group, int uid, int gid,
332                           char * linkto) {
333     char sizefield[15];
334     char ownerfield[9], groupfield[9];
335     char timefield[100] = "";
336     time_t themtime;
337     time_t currenttime;
338     static int thisYear = 0;
339     static int thisMonth = 0;
340     struct tm * tstruct;
341     char * namefield = name;
342     char * perms;
343
344     perms = permsString(mode);
345
346     if (!thisYear) {
347         currenttime = time(NULL);
348         tstruct = localtime(&currenttime);
349         thisYear = tstruct->tm_year;
350         thisMonth = tstruct->tm_mon;
351     }
352
353     ownerfield[8] = groupfield[8] = '\0';
354
355     if (owner) 
356         strncpy(ownerfield, owner, 8);
357     else
358         sprintf(ownerfield, "%-8d", uid);
359
360     if (group) 
361         strncpy(groupfield, group, 8);
362     else 
363         sprintf(groupfield, "%-8d", gid);
364
365     /* this is normally right */
366     sprintf(sizefield, "%10u", size);
367
368     /* this knows too much about dev_t */
369
370     if (S_ISLNK(mode)) {
371         namefield = alloca(strlen(name) + strlen(linkto) + 10);
372         sprintf(namefield, "%s -> %s", name, linkto);
373     } else if (S_ISCHR(mode)) {
374         perms[0] = 'c';
375         sprintf(sizefield, "%3u, %3u", (rdev >> 8) & 0xff, rdev & 0xFF);
376     } else if (S_ISBLK(mode)) {
377         perms[0] = 'b';
378         sprintf(sizefield, "%3u, %3u", (rdev >> 8) & 0xff, rdev & 0xFF);
379     }
380
381     /* this is important if sizeof(int_32) ! sizeof(time_t) */
382     themtime = mtime;
383     tstruct = localtime(&themtime);
384
385     if (tstruct->tm_year == thisYear || 
386       ((tstruct->tm_year + 1) == thisYear && tstruct->tm_mon > thisMonth)) 
387         (void)strftime(timefield, sizeof(timefield) - 1, "%b %d %H:%M",tstruct);
388     else
389         (void)strftime(timefield, sizeof(timefield) - 1, "%b %d  %Y", tstruct);
390
391     fprintf(stdout, "%s %8s %8s %10s %s %s\n", perms, ownerfield, groupfield, 
392                 sizefield, timefield, namefield);
393 }
394
395 static void showMatches(rpmdb db, dbiIndexSet matches, int queryFlags, 
396                         char * queryFormat) {
397     int i;
398     Header h;
399
400     for (i = 0; i < dbiIndexSetCount(matches); i++) {
401         unsigned int recOffset = dbiIndexRecordOffset(matches, i);
402         if (recOffset) {
403             rpmMessage(RPMMESS_DEBUG, _("querying record number %d\n"),
404                         recOffset);
405             
406             h = rpmdbGetRecord(db, recOffset);
407             if (h == NULL) {
408                 fprintf(stderr, _("error: could not read database record\n"));
409             } else {
410                 printHeader(h, queryFlags, queryFormat);
411                 headerFree(h);
412             }
413         }
414     }
415 }
416
417 int rpmQuery(char * prefix, enum rpmQuerySources source, int queryFlags, 
418              char * arg, char * queryFormat) {
419     Header h;
420     int offset;
421     int rc;
422     int isSource;
423     rpmdb db;
424     dbiIndexSet matches;
425     int recNumber;
426     int retcode = 0;
427     char *end = NULL;
428     struct urlContext context;
429     char path[PATH_MAX];
430
431     switch (source) {
432     default:
433         if (rpmdbOpen(prefix, &db, O_RDONLY, 0644)) {
434             fprintf(stderr, _("rpmQuery: rpmdbOpen() failed\n"));
435             exit(1);
436         }
437         break;
438     case QUERY_RPM:
439     case QUERY_SPECFILE:
440         break;
441     }
442
443     switch (source) {
444       case QUERY_RPM:
445       { FD_t fd = NULL;
446         int isUrl = 0;
447         if (urlIsURL(arg)) {
448             int fdno;
449             isUrl = 1;
450             if ((fdno = urlGetFd(arg, &context)) < 0) {
451                 fprintf(stderr, _("open of %s failed: %s\n"), arg, 
452                         ftpStrerror(fdno));
453             }
454             fd = fdDup(fdno);
455             close(fdno);
456         } else if (!strcmp(arg, "-")) {
457             fd = fdDup(STDIN_FILENO);
458         } else {
459             if (fdFileno(fd = fdOpen( arg, O_RDONLY, 0)) < 0) {
460                 fprintf(stderr, _("open of %s failed: %s\n"), arg, 
461                         strerror(errno));
462             }
463         }
464
465         if (fd != NULL && fdFileno(fd) >= 0) {
466             rc = rpmReadPackageHeader(fd, &h, &isSource, NULL, NULL);
467
468             fdClose(fd);
469             if (isUrl) {
470                 urlFinishedFd(&context);
471             }
472
473             switch (rc) {
474                 case 0:
475                     if (h == NULL) {
476                         fprintf(stderr, _("old format source packages cannot "
477                                 "be queried\n"));
478                     } else {
479                         printHeader(h, queryFlags, queryFormat);
480                         headerFree(h);
481                     }
482                     break;
483                 case 1:
484                     fprintf(stderr, 
485                             _("%s does not appear to be a RPM package\n"), 
486                             arg);
487                     /* fallthrough */
488                 case 2:
489                     fprintf(stderr, _("query of %s failed\n"), arg);
490                     retcode = 1;
491             }
492         }
493       } break;
494
495       case QUERY_SPECFILE:
496       { Spec spec = NULL;
497         Package pkg;
498         char * buildRoot = NULL;
499         int inBuildArch = 0;
500         char * passPhrase = "";
501         char *cookie = NULL;
502         int anyarch = 1;
503         int force = 1;
504         rc = parseSpec(&spec, arg, buildRoot, inBuildArch, passPhrase, cookie,
505             anyarch, force);
506         if (rc || spec == NULL) {
507             
508             fprintf(stderr, _("query of specfile %s failed, can't parse\n"), arg);
509             if (spec != NULL) freeSpec(spec);
510             retcode = 1;
511             break;
512         }
513         for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
514 #if 0
515             char *binRpm, *errorString;
516             binRpm = headerSprintf(pkg->header, rpmGetVar(RPMVAR_RPMFILENAME),
517                 rpmTagTable, rpmHeaderFormats, &errorString);
518             if (!(pkg == spec->packages && pkg->next == NULL))
519                 fprintf(stdout, "====== %s\n", binRpm);
520             free(binRpm);
521 #endif
522             printHeader(pkg->header, queryFlags, queryFormat);
523         }
524         freeSpec(spec);
525       } break;
526
527       case QUERY_ALL:
528         offset = rpmdbFirstRecNum(db);
529         while (offset) {
530             h = rpmdbGetRecord(db, offset);
531             if (h == NULL) {
532                 fprintf(stderr, _("could not read database record!\n"));
533                 return 1;
534             }
535             printHeader(h, queryFlags, queryFormat);
536             headerFree(h);
537             offset = rpmdbNextRecNum(db, offset);
538         }
539         break;
540
541       case QUERY_GROUP:
542         if (rpmdbFindByGroup(db, arg, &matches)) {
543             fprintf(stderr, _("group %s does not contain any packages\n"), arg);
544             retcode = 1;
545         } else {
546             showMatches(db, matches, queryFlags, queryFormat);
547             dbiFreeIndexRecord(matches);
548         }
549         break;
550
551       case QUERY_WHATPROVIDES:
552         if (rpmdbFindByProvides(db, arg, &matches)) {
553             fprintf(stderr, _("no package provides %s\n"), arg);
554             retcode = 1;
555         } else {
556             showMatches(db, matches, queryFlags, queryFormat);
557             dbiFreeIndexRecord(matches);
558         }
559         break;
560
561       case QUERY_TRIGGEREDBY:
562         if (rpmdbFindByTriggeredBy(db, arg, &matches)) {
563             fprintf(stderr, _("no package triggers %s\n"), arg);
564             retcode = 1;
565         } else {
566             showMatches(db, matches, queryFlags, queryFormat);
567             dbiFreeIndexRecord(matches);
568         }
569         break;
570
571       case QUERY_WHATREQUIRES:
572         if (rpmdbFindByRequiredBy(db, arg, &matches)) {
573             fprintf(stderr, _("no package requires %s\n"), arg);
574             retcode = 1;
575         } else {
576             showMatches(db, matches, queryFlags, queryFormat);
577             dbiFreeIndexRecord(matches);
578         }
579         break;
580
581       case QUERY_PATH:
582         if (*arg != '/') {
583                 /* Using realpath on the arg isn't correct if the arg is a symlink,
584                  * especially if the symlink is a dangling link.  What we should
585                  * instead do is use realpath() on `.' and then append arg to
586                  * it.
587                  */
588             if (realpath(".", path) != NULL) {
589                 if (path[strlen(path)] != '/') {
590                         if (strncat(path, "/", PATH_MAX - strlen(path) - 1) == NULL) {
591                         fprintf(stderr, _("maximum path length exceeded\n"));
592                         return 1;
593                         }
594                 }
595                 /* now append the original file name to the real path */
596                 if (strncat(path, arg, PATH_MAX - strlen(path) - 1) == NULL) {
597                         fprintf(stderr, _("maximum path length exceeded\n"));
598                         return 1;
599                 }
600                 arg = path;
601             }
602         }
603         if (rpmdbFindByFile(db, arg, &matches)) {
604             int myerrno = 0;
605             if (access(arg, F_OK) != 0)
606                 myerrno = errno;
607             switch (myerrno) {
608             default:
609                 fprintf(stderr, _("file %s: %s\n"), arg, strerror(myerrno));
610                 break;
611             case 0:
612                 fprintf(stderr, _("file %s is not owned by any package\n"), arg);
613                 break;
614             }
615             retcode = 1;
616         } else {
617             showMatches(db, matches, queryFlags, queryFormat);
618             dbiFreeIndexRecord(matches);
619         }
620         break;
621
622       case QUERY_DBOFFSET:
623         recNumber = strtoul(arg, &end, 10);
624         if ((*end) || (end == arg) || (recNumber == ULONG_MAX)) {
625             fprintf(stderr, _("invalid package number: %s\n"), arg);
626             return 1;
627         }
628         rpmMessage(RPMMESS_DEBUG, _("showing package: %d\n"), recNumber);
629         h = rpmdbGetRecord(db, recNumber);
630         if (h == NULL)  {
631             fprintf(stderr, _("record %d could not be read\n"), recNumber);
632             retcode = 1;
633         } else {
634             printHeader(h, queryFlags, queryFormat);
635             headerFree(h);
636         }
637         break;
638
639       case QUERY_PACKAGE:
640         rc = rpmdbFindByLabel(db, arg, &matches);
641         if (rc == 1) {
642             retcode = 1;
643             fprintf(stderr, _("package %s is not installed\n"), arg);
644         } else if (rc == 2) {
645             retcode = 1;
646             fprintf(stderr, _("error looking for package %s\n"), arg);
647         } else {
648             showMatches(db, matches, queryFlags, queryFormat);
649             dbiFreeIndexRecord(matches);
650         }
651         break;
652     }
653    
654     switch (source) {
655     default:
656         rpmdbClose(db);
657         break;
658     case QUERY_RPM:
659     case QUERY_SPECFILE:
660         break;
661     }
662
663     return retcode;
664 }
665
666 void rpmDisplayQueryTags(FILE * f) {
667     const struct headerTagTableEntry * t;
668     int i;
669     const struct headerSprintfExtension * ext = rpmHeaderFormats;
670
671     for (i = 0, t = rpmTagTable; i < rpmTagTableSize; i++, t++) {
672         fprintf(f, "%s\n", t->name + 7);
673     }
674
675     while (ext->name) {
676         if (ext->type == HEADER_EXT_TAG)
677             fprintf(f, "%s\n", ext->name + 7), ext++;
678         else if (ext->type == HEADER_EXT_MORE)
679             ext = ext->u.more;
680         else
681             ext++;
682     }
683 }