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