%macro handling
[platform/upstream/rpm.git] / query.c
1 #include "config.h"
2
3 #include <ctype.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/stat.h>
10 #include <time.h>
11 #include <sys/param.h>
12 #include <unistd.h>
13
14 #if HAVE_ALLOCA_H
15 # include <alloca.h>
16 #endif
17
18 #include "lib/messages.h"
19 #include "miscfn.h"
20 #include "rpmlib.h"
21 #include "query.h"
22 #include "url.h"
23
24 static char * permsString(int mode);
25 static void printHeader(Header h, int queryFlags, char * queryFormat);
26 static int queryPartial(Header h, char ** chptrptr, int * cntptr, 
27                         int arrayNum);
28 static int queryHeader(Header h, char * chptr);
29 static int queryArray(Header h, char ** chptrptr);
30 static void escapedChar(char ch);
31 static char * handleFormat(Header h, char * chptr, int * count, int arrayNum);
32 static void showMatches(rpmdb db, dbiIndexSet matches, int queryFlags, 
33                         char * queryFormat);
34 static int findMatches(rpmdb db, char * name, char * version, char * release,
35                        dbiIndexSet * matches);
36 static void printFileInfo(char * name, unsigned int size, unsigned short mode,
37                           unsigned int mtime, unsigned short rdev,
38                           char * owner, char * group, int uid, int gid,
39                           char * linkto);
40
41 static int queryHeader(Header h, char * chptr) {
42     int count = 0;
43
44     return queryPartial(h, &chptr, &count, -1);
45 }
46
47 static int queryArray(Header h, char ** chptrptr) {
48     int count = 0;
49     int arrayNum;
50     char * start = NULL;
51
52     /* first one */
53     start = *chptrptr;
54     if (queryPartial(h, chptrptr, &count, 0)) return 1;
55
56     arrayNum = 1;
57     while (arrayNum < count) {
58         *chptrptr = start;
59         if (queryPartial(h, chptrptr, &count, arrayNum++)) return 1;
60     }
61
62     return 0;
63 }
64
65 static int queryPartial(Header h, char ** chptrptr, int * cntptr, 
66                         int arrayNum) {
67     char * chptr = *chptrptr;
68     int count;
69     int emptyItem = 0;
70
71     while (chptr && *chptr) {
72         switch (*chptr) {
73           case '\\':
74             chptr++;
75             if (!*chptr) return 1;
76             escapedChar(*chptr++);
77             break;
78
79           case '%':
80             chptr++;
81             if (!*chptr) return 1;
82             if (*chptr == '%') {
83                 putchar('%');
84                 chptr++;
85             }
86             count = *cntptr;
87             chptr = handleFormat(h, chptr, &count, arrayNum);
88             if (!count) {
89                 count = 0;      
90                 emptyItem = 1;
91             } else if (count != -1 && !*cntptr && !emptyItem){ 
92                 *cntptr = count;
93             } else if (count != -1 && *cntptr && count != *cntptr) {
94                 fprintf(stderr, "(parallel array size mismatch)");
95                 return 1;
96             }
97             break;
98
99           case ']':
100             if (arrayNum == -1) {
101                 printf("(unexpected ']')");
102                 return 1;
103             }
104             *chptrptr = chptr + 1;
105             return 0;
106
107           case '[':
108             if (arrayNum != -1) {
109                 printf("(unexpected ']')");
110                 return 1;
111             }
112             *chptrptr = chptr + 1;
113             if (queryArray(h, chptrptr)) {
114                 return 1;
115             }
116             chptr = *chptrptr;
117             break;
118
119           default:
120             putchar(*chptr++);
121         }
122     }
123
124     *chptrptr = chptr;
125
126     return 0;
127 }
128
129 static char * handleFormat(Header h, char * chptr, int * cntptr,
130                                  int arrayNum) {
131     const char * f = chptr;
132     const char * tagptr;
133     char * end;
134     char how[20], format[20];
135     int i, tagLength;
136     char tag[100];
137     const struct headerTagTableEntry * t;
138     void * p;
139     int type;
140     int showCount = 0;
141     int notArray = 0;
142     time_t dateint;
143     struct tm * tstruct;
144     char buf[100];
145     int count;
146     int anint;
147
148     strcpy(format, "%");
149     while (*chptr && *chptr != '{') chptr++;
150     if (!*chptr || (chptr - f > (sizeof(format) - 3))) {
151         fprintf(stderr, "bad query format - %s\n", f);
152         return NULL;
153     }
154
155     strncat(format, f, chptr - f);
156
157     tagptr = ++chptr;
158     while (*chptr && *chptr != '}' && *chptr != ':' ) chptr++;
159     if (tagptr == chptr || !*chptr) {
160         fprintf(stderr, "bad query format - %s\n", f);
161         return NULL;
162     }
163
164     if (*chptr == ':') {
165         end = chptr + 1;
166         while (*end && *end != '}') end++;
167
168         if (*end != '}') {
169             fprintf(stderr, "bad query format - %s\n", f);
170             return NULL;
171         }
172         if ((end - chptr + 1) > sizeof(how)) {
173             fprintf(stderr, "bad query format - %s\n", f);
174             return NULL;
175         }
176         strncpy(how, chptr + 1, end - chptr - 1);
177         how[end - chptr - 1] = '\0';
178     } else {
179         strcpy(how, "");
180         end = chptr;
181     }
182
183     switch (*tagptr) {
184         case '=':       notArray = 1, tagptr++; break;
185         case '#':       showCount = 1, tagptr++; break;
186     }
187
188     tagLength = chptr - tagptr;
189     chptr = end + 1;
190
191     if (tagLength > (sizeof(tag) - 20)) {
192         fprintf(stderr, "query tag too long\n");
193         return NULL;
194     }
195     memset(tag, 0, sizeof(tag));
196     if (strncmp(tagptr, "RPMTAG_", 7)) {
197         strcpy(tag, "RPMTAG_");
198     }
199     strncat(tag, tagptr, tagLength);
200
201     for (i = 0, t = rpmTagTable; i < rpmTagTableSize; i++, t++) {
202         if (!strcasecmp(tag, t->name)) break;
203     }
204
205     if (i == rpmTagTableSize) {
206         fprintf(stderr, "unknown tag %s\n", tag);
207         return NULL;
208     }
209  
210     if (!headerGetEntry(h, t->val, &type, &p, &count) || !p) {
211         p = "(none)";
212         count = 1;
213         type = RPM_STRING_TYPE;
214     } else if (notArray) {
215         *cntptr = -1;
216     } else if (showCount) {
217         i = count;
218         p = &i;
219         type = RPM_INT32_TYPE;
220         count = 1;
221     } else if (count > 1 && (arrayNum == -1)) {
222         p = "(array)";
223         count = 1;
224         type = RPM_STRING_TYPE;
225     } else if ((count - 1) < arrayNum && arrayNum != -1) {
226         p = "(past array end)";
227         count = 1;
228         type = RPM_STRING_TYPE;
229     } else if (arrayNum != -1)
230         *cntptr = count;
231
232     if (arrayNum == -1) arrayNum = 0;
233
234     switch (type) {
235       case RPM_STRING_ARRAY_TYPE:
236         strcat(format, "s");
237         printf(format, ((char **) p)[arrayNum]);
238         free(p);
239         break;
240
241       case RPM_STRING_TYPE:
242         strcat(format, "s");
243         printf(format, p);
244         break;
245
246       case RPM_CHAR_TYPE:
247       case RPM_INT8_TYPE:
248         strcat(format, "d");
249         printf(format, *(((int_8 *) p) + arrayNum));
250         break;
251
252       case RPM_INT16_TYPE:
253         if (!strcmp(how, "perms") || !strcmp(how, "permissions")) {
254             strcat(format, "s");
255             printf(format, permsString(*(((int_16 *) p) + arrayNum)));
256         } else if (!strcmp(how, "octal")) {
257             strcat(format, "#o");
258             printf(format, *(((int_16 *) p) + arrayNum) & 0xFFFF);
259         } else {
260             strcat(format, "d");
261             printf(format, *(((int_16 *) p) + arrayNum));
262         }
263         break;
264
265       case RPM_INT32_TYPE:
266         if (!strcmp(how, "date")) {
267             strcat(format, "s");
268             /* this is important if sizeof(int_32) ! sizeof(time_t) */
269             dateint = *(((int_32 *) p) + arrayNum);
270             tstruct = localtime(&dateint);
271             strftime(buf, sizeof(buf) - 1, "%c", tstruct);
272             printf(format, buf);
273         } else if (!strcmp(how, "fflags")) {
274             strcat(format, "s");
275             buf[0] = '\0';
276             anint = *(((int_32 *) p) + arrayNum);
277             if (anint & RPMFILE_DOC)
278                 strcat(buf, "d");
279             if (anint & RPMFILE_CONFIG)
280                 strcat(buf, "c");
281             printf(format, buf);
282         } else if (!strcmp(how, "octal")) {
283             strcat(format, "#o");
284             printf(format, *(((int_32 *) p) + arrayNum));
285         } else if (!strcmp(how, "perms") || !strcmp(how, "permissions")) {
286             strcat(format, "s");
287             printf(format, permsString(*(((int_32 *) p) + arrayNum)));
288         } else if (!strcmp(how, "depflags")) {
289             buf[0] = '\0';
290             anint = *(((int_32 *) p) + arrayNum);
291             if (anint & RPMSENSE_LESS) 
292                 strcat(buf, "<");
293             if (anint & RPMSENSE_GREATER)
294                 strcat(buf, ">");
295             if (anint & RPMSENSE_EQUAL)
296                 strcat(buf, "=");
297             if (anint & RPMSENSE_SERIAL)
298                 strcat(buf, "S");
299         
300             strcat(format, "s");
301             printf(format, buf);
302         } else {
303             strcat(format, "d");
304             printf(format, *(((int_32 *) p) + arrayNum));
305         }
306         break;
307
308       default:
309         printf("(can't handle type %d)", type);
310         break;
311     }
312
313     return chptr;
314 }
315
316 static void escapedChar(const char ch) {
317     switch (ch) {
318       case 'a':         putchar('\a'); break;
319       case 'b':         putchar('\b'); break;
320       case 'f':         putchar('\f'); break;
321       case 'n':         putchar('\n'); break;
322       case 'r':         putchar('\r'); break;
323       case 't':         putchar('\t'); break;
324       case 'v':         putchar('\v'); break;
325
326       default:          putchar(ch); break;
327     }
328 }
329
330 static void printHeader(Header h, int queryFlags, char * queryFormat) {
331     char * name, * version, * release;
332     int_32 count, type;
333     char * prefix = NULL;
334     char ** fileList, ** fileMD5List;
335     char * fileStatesList;
336     char ** fileOwnerList = NULL;
337     char ** fileGroupList = NULL;
338     char ** fileLinktoList;
339     int_32 * fileFlagsList, * fileMTimeList, * fileSizeList;
340     int_32 * fileUIDList, * fileGIDList;
341     uint_16 * fileModeList;
342     uint_16 * fileRdevList;
343     int i;
344
345     headerGetEntry(h, RPMTAG_NAME, &type, (void **) &name, &count);
346     headerGetEntry(h, RPMTAG_VERSION, &type, (void **) &version, &count);
347     headerGetEntry(h, RPMTAG_RELEASE, &type, (void **) &release, &count);
348
349     if (!queryFormat && !queryFlags) {
350         printf("%s-%s-%s\n", name, version, release);
351     } else {
352         if (queryFormat)
353             queryHeader(h, queryFormat);
354
355         if (queryFlags & QUERY_FOR_LIST) {
356             if (!headerGetEntry(h, RPMTAG_FILENAMES, &type, (void **) &fileList, 
357                  &count)) {
358                 puts("(contains no files)");
359             } else {
360                 if (!headerGetEntry(h, RPMTAG_FILESTATES, &type, 
361                          (void **) &fileStatesList, &count)) {
362                     fileStatesList = NULL;
363                 }
364                 headerGetEntry(h, RPMTAG_FILEFLAGS, &type, 
365                          (void **) &fileFlagsList, &count);
366                 headerGetEntry(h, RPMTAG_FILESIZES, &type, 
367                          (void **) &fileSizeList, &count);
368                 headerGetEntry(h, RPMTAG_FILEMODES, &type, 
369                          (void **) &fileModeList, &count);
370                 headerGetEntry(h, RPMTAG_FILEMTIMES, &type, 
371                          (void **) &fileMTimeList, &count);
372                 headerGetEntry(h, RPMTAG_FILERDEVS, &type, 
373                          (void **) &fileRdevList, &count);
374                 headerGetEntry(h, RPMTAG_FILELINKTOS, &type, 
375                          (void **) &fileLinktoList, &count);
376                 headerGetEntry(h, RPMTAG_FILEMD5S, &type, 
377                          (void **) &fileMD5List, &count);
378
379                 if (!headerGetEntry(h, RPMTAG_FILEUIDS, &type, 
380                          (void **) &fileUIDList, &count)) {
381                     fileUIDList = NULL;
382                 } else {
383                     headerGetEntry(h, RPMTAG_FILEGIDS, &type, 
384                              (void **) &fileGIDList, &count);
385                 }
386
387                 if (!headerGetEntry(h, RPMTAG_FILEUSERNAME, &type, 
388                          (void **) &fileOwnerList, &count)) {
389                     fileOwnerList = NULL;
390                 } else {
391                     headerGetEntry(h, RPMTAG_FILEGROUPNAME, &type, 
392                              (void **) &fileGroupList, &count);
393                 }
394
395                 for (i = 0; i < count; i++) {
396                     if (!((queryFlags & QUERY_FOR_DOCS) || 
397                           (queryFlags & QUERY_FOR_CONFIG)) 
398                         || ((queryFlags & QUERY_FOR_DOCS) && 
399                             (fileFlagsList[i] & RPMFILE_DOC))
400                         || ((queryFlags & QUERY_FOR_CONFIG) && 
401                             (fileFlagsList[i] & RPMFILE_CONFIG))) {
402
403                         if (!rpmIsVerbose())
404                             prefix ? fputs(prefix, stdout) : 0;
405
406                         if (queryFlags & QUERY_FOR_STATE) {
407                             if (fileStatesList) {
408                                 switch (fileStatesList[i]) {
409                                   case RPMFILE_STATE_NORMAL:
410                                     fputs("normal        ", stdout); break;
411                                   case RPMFILE_STATE_REPLACED:
412                                     fputs("replaced      ", stdout); break;
413                                   case RPMFILE_STATE_NETSHARED:
414                                     fputs("net shared    ", stdout); break;
415                                   case RPMFILE_STATE_NOTINSTALLED:
416                                     fputs("not installed ", stdout); break;
417                                   default:
418                                     printf("(unknown %3d) ", 
419                                           fileStatesList[i]);
420                                 }
421                             } else {
422                                 fputs(    "(no state)    ", stdout);
423                             }
424                         }
425                             
426                         if (queryFlags & QUERY_FOR_DUMPFILES) {
427                             printf("%s %d %d %s 0%o ", fileList[i],
428                                    fileSizeList[i], fileMTimeList[i],
429                                    fileMD5List[i], fileModeList[i]);
430
431                             if (fileOwnerList)
432                                 printf("%s %s", fileOwnerList[i], 
433                                                 fileGroupList[i]);
434                             else if (fileUIDList)
435                                 printf("%d %d", fileUIDList[i], 
436                                                 fileGIDList[i]);
437                             else {
438                                 rpmError(RPMERR_INTERNAL, "package has "
439                                         "neither file owner or id lists");
440                             }
441
442                             printf(" %s %s %d ", 
443                                  fileFlagsList[i] & RPMFILE_CONFIG ? "1" : "0",
444                                  fileFlagsList[i] & RPMFILE_DOC ? "1" : "0",
445                                  fileRdevList[i]);
446
447                             if (strlen(fileLinktoList[i]))
448                                 printf("%s\n", fileLinktoList[i]);
449                             else
450                                 printf("X\n");
451
452                         } else if (!rpmIsVerbose()) {
453                             puts(fileList[i]);
454                         } else if (fileOwnerList) 
455                             printFileInfo(fileList[i], fileSizeList[i],
456                                           fileModeList[i], fileMTimeList[i],
457                                           fileRdevList[i], fileOwnerList[i], 
458                                           fileGroupList[i], -1, 
459                                           -1, fileLinktoList[i]);
460                         else if (fileUIDList) {
461                             printFileInfo(fileList[i], fileSizeList[i],
462                                           fileModeList[i], fileMTimeList[i],
463                                           fileRdevList[i], NULL, 
464                                           NULL, fileUIDList[i], 
465                                           fileGIDList[i], fileLinktoList[i]);
466                         } else {
467                             rpmError(RPMERR_INTERNAL, "package has "
468                                     "neither file owner or id lists");
469                         }
470                     }
471                 }
472             
473                 free(fileList);
474                 free(fileLinktoList);
475                 free(fileMD5List);
476                 if (fileOwnerList) free(fileOwnerList);
477                 if (fileGroupList) free(fileGroupList);
478             }
479         }
480     }
481 }
482
483 static char * permsString(int mode) {
484     static char perms[11];
485
486     strcpy(perms, "-----------");
487    
488     if (mode & S_ISVTX) perms[10] = 't';
489
490     if (mode & S_IRUSR) perms[1] = 'r';
491     if (mode & S_IWUSR) perms[2] = 'w';
492     if (mode & S_IXUSR) perms[3] = 'x';
493  
494     if (mode & S_IRGRP) perms[4] = 'r';
495     if (mode & S_IWGRP) perms[5] = 'w';
496     if (mode & S_IXGRP) perms[6] = 'x';
497
498     if (mode & S_IROTH) perms[7] = 'r';
499     if (mode & S_IWOTH) perms[8] = 'w';
500     if (mode & S_IXOTH) perms[9] = 'x';
501
502     if (mode & S_ISUID) {
503         if (mode & S_IXUSR) 
504             perms[3] = 's'; 
505         else
506             perms[3] = 'S'; 
507     }
508
509     if (mode & S_ISGID) {
510         if (mode & S_IXGRP) 
511             perms[6] = 's'; 
512         else
513             perms[6] = 'S'; 
514     }
515
516     if (S_ISDIR(mode)) 
517         perms[0] = 'd';
518     else if (S_ISLNK(mode)) {
519         perms[0] = 'l';
520     }
521     else if (S_ISFIFO(mode)) 
522         perms[0] = 'p';
523     else if (S_ISSOCK(mode)) 
524         perms[0] = 'l';
525     else if (S_ISCHR(mode)) {
526         perms[0] = 'c';
527     } else if (S_ISBLK(mode)) {
528         perms[0] = 'b';
529     }
530
531     return perms;
532 }
533
534 static void printFileInfo(char * name, unsigned int size, unsigned short mode,
535                           unsigned int mtime, unsigned short rdev,
536                           char * owner, char * group, int uid, int gid,
537                           char * linkto) {
538     char sizefield[15];
539     char ownerfield[9], groupfield[9];
540     char timefield[100] = "";
541     time_t themtime;
542     time_t currenttime;
543     static int thisYear = 0;
544     static int thisMonth = 0;
545     struct tm * tstruct;
546     char * namefield = name;
547     char * perms;
548
549     perms = permsString(mode);
550
551     if (!thisYear) {
552         currenttime = time(NULL);
553         tstruct = localtime(&currenttime);
554         thisYear = tstruct->tm_year;
555         thisMonth = tstruct->tm_mon;
556     }
557
558     if (owner) 
559         strncpy(ownerfield, owner, 8);
560     else
561         sprintf(ownerfield, "%-8d", uid);
562
563     if (group) 
564         strncpy(groupfield, group, 8);
565     else 
566         sprintf(groupfield, "%-8d", gid);
567
568     /* this is normally right */
569     sprintf(sizefield, "%10d", size);
570
571     /* this knows too much about dev_t */
572
573     if (S_ISLNK(mode)) {
574         namefield = alloca(strlen(name) + strlen(linkto) + 10);
575         sprintf(namefield, "%s -> %s", name, linkto);
576     } else if (S_ISCHR(mode)) {
577         perms[0] = 'c';
578         sprintf(sizefield, "%3d, %3d", rdev >> 8, rdev & 0xFF);
579     } else if (S_ISBLK(mode)) {
580         perms[0] = 'b';
581         sprintf(sizefield, "%3d, %3d", rdev >> 8, rdev & 0xFF);
582     }
583
584     /* this is important if sizeof(int_32) ! sizeof(time_t) */
585     themtime = mtime;
586     tstruct = localtime(&themtime);
587
588     if (tstruct->tm_year == thisYear || 
589         ((tstruct->tm_year + 1) == thisYear && tstruct->tm_mon > thisMonth)) 
590         strftime(timefield, sizeof(timefield) - 1, "%b %d %H:%M", tstruct);
591     else
592         strftime(timefield, sizeof(timefield) - 1, "%b %d  %Y", tstruct);
593
594     printf("%s %8s %8s %10s %s %s\n", perms, ownerfield, groupfield, 
595                 sizefield, timefield, namefield);
596 }
597
598 static void showMatches(rpmdb db, dbiIndexSet matches, int queryFlags, 
599                         char * queryFormat) {
600     int i;
601     Header h;
602
603     for (i = 0; i < matches.count; i++) {
604         if (matches.recs[i].recOffset) {
605             rpmMessage(RPMMESS_DEBUG, "querying record number %d\n",
606                         matches.recs[i].recOffset);
607             
608             h = rpmdbGetRecord(db, matches.recs[i].recOffset);
609             if (!h) {
610                 fprintf(stderr, "error: could not read database record\n");
611             } else {
612                 printHeader(h, queryFlags, queryFormat);
613                 headerFree(h);
614             }
615         }
616     }
617 }
618
619 int doQuery(char * prefix, enum querysources source, int queryFlags, 
620              char * arg, char * queryFormat) {
621     Header h;
622     int offset;
623     int fd;
624     int rc;
625     int isSource;
626     rpmdb db;
627     dbiIndexSet matches;
628     int recNumber;
629     int retcode = 0;
630     char *end = NULL;
631     struct urlContext context;
632     int isUrl = 0;
633     char path[255];
634
635     if (source != QUERY_RPM) {
636         if (rpmdbOpen(prefix, &db, O_RDONLY, 0644)) {
637             exit(1);
638         }
639     }
640
641     switch (source) {
642       case QUERY_RPM:
643         if (urlIsURL(arg)) {
644             isUrl = 1;
645             if ((fd = urlGetFd(arg, &context)) < 0) {
646                 fprintf(stderr, "open of %s failed: %s\n", arg, 
647                         ftpStrerror(fd));
648             }
649         } else if (!strcmp(arg, "-")) {
650             fd = 0;
651         } else {
652             if ((fd = open(arg, O_RDONLY)) < 0) {
653                 fprintf(stderr, "open of %s failed: %s\n", arg, 
654                         strerror(errno));
655             }
656         }
657
658         if (fd >= 0) {
659             rc = rpmReadPackageHeader(fd, &h, &isSource, NULL, NULL);
660
661             close(fd);
662             if (isUrl) {
663                 urlFinishedFd(&context);
664             }
665
666             switch (rc) {
667                 case 0:
668                     if (!h) {
669                         fprintf(stderr, "old format source packages cannot be "
670                                                 "queried\n");
671                     } else {
672                         printHeader(h, queryFlags, queryFormat);
673                         headerFree(h);
674                     }
675                     break;
676                 case 1:
677                     fprintf(stderr, "%s does not appear to be a RPM package\n", 
678                                 arg);
679                     /* fallthrough */
680                 case 2:
681                     fprintf(stderr, "query of %s failed\n", arg);
682                     retcode = 1;
683             }
684
685         }
686                 
687         break;
688
689       case QUERY_ALL:
690         offset = rpmdbFirstRecNum(db);
691         while (offset) {
692             h = rpmdbGetRecord(db, offset);
693             if (!h) {
694                 fprintf(stderr, "could not read database record!\n");
695                 return 1;
696             }
697             printHeader(h, queryFlags, queryFormat);
698             headerFree(h);
699             offset = rpmdbNextRecNum(db, offset);
700         }
701         break;
702
703       case QUERY_GROUP:
704         if (rpmdbFindByGroup(db, arg, &matches)) {
705             fprintf(stderr, "group %s does not contain any packages\n", arg);
706             retcode = 1;
707         } else {
708             showMatches(db, matches, queryFlags, queryFormat);
709             dbiFreeIndexRecord(matches);
710         }
711         break;
712
713       case QUERY_WHATPROVIDES:
714         if (rpmdbFindByProvides(db, arg, &matches)) {
715             fprintf(stderr, "no package provides %s\n", arg);
716             retcode = 1;
717         } else {
718             showMatches(db, matches, queryFlags, queryFormat);
719             dbiFreeIndexRecord(matches);
720         }
721         break;
722
723       case QUERY_WHATREQUIRES:
724         if (rpmdbFindByRequiredBy(db, arg, &matches)) {
725             fprintf(stderr, "no package requires %s\n", arg);
726             retcode = 1;
727         } else {
728             showMatches(db, matches, queryFlags, queryFormat);
729             dbiFreeIndexRecord(matches);
730         }
731         break;
732
733       case QUERY_PATH:
734         if (*arg != '/') {
735             if (realpath(arg, path) != NULL)
736                 arg = path;
737         }
738         if (rpmdbFindByFile(db, arg, &matches)) {
739             fprintf(stderr, "file %s is not owned by any package\n", arg);
740             retcode = 1;
741         } else {
742             showMatches(db, matches, queryFlags, queryFormat);
743             dbiFreeIndexRecord(matches);
744         }
745         break;
746
747       case QUERY_DBOFFSET:
748         recNumber = strtoul(arg, &end, 10);
749         if ((*end) || (end == arg) || (recNumber == ULONG_MAX)) {
750             fprintf(stderr, "invalid package number: %s\n", arg);
751             return 1;
752         }
753         rpmMessage(RPMMESS_DEBUG, "showing package: %d\n", recNumber);
754         h = rpmdbGetRecord(db, recNumber);
755
756         if (!h)  {
757             fprintf(stderr, "record %d could not be read\n", recNumber);
758             retcode = 1;
759         } else {
760             printHeader(h, queryFlags, queryFormat);
761             headerFree(h);
762         }
763         break;
764
765       case QUERY_PACKAGE:
766         rc = findPackageByLabel(db, arg, &matches);
767         if (rc == 1) {
768             retcode = 1;
769             fprintf(stderr, "package %s is not installed\n", arg);
770         } else if (rc == 2) {
771             retcode = 1;
772             fprintf(stderr, "error looking for package %s\n", arg);
773         } else {
774             showMatches(db, matches, queryFlags, queryFormat);
775             dbiFreeIndexRecord(matches);
776         }
777         break;
778     }
779    
780     if (source != QUERY_RPM) {
781         rpmdbClose(db);
782     }
783
784     return retcode;
785 }
786
787 /* 0 found matches */
788 /* 1 no matches */
789 /* 2 error */
790 int findPackageByLabel(rpmdb db, char * arg, dbiIndexSet * matches) {
791     char * localarg, * chptr;
792     char * release;
793     int rc;
794  
795     if (!strlen(arg)) return 1;
796
797     /* did they give us just a name? */
798     rc = findMatches(db, arg, NULL, NULL, matches);
799     if (rc != 1) return rc;
800
801     /* maybe a name and a release */
802     localarg = alloca(strlen(arg) + 1);
803     strcpy(localarg, arg);
804
805     chptr = (localarg + strlen(localarg)) - 1;
806     while (chptr > localarg && *chptr != '-') chptr--;
807     if (chptr == localarg) return 1;
808
809     *chptr = '\0';
810     rc = findMatches(db, localarg, chptr + 1, NULL, matches);
811     if (rc != 1) return rc;
812     
813     /* how about name-version-release? */
814
815     release = chptr + 1;
816     while (chptr > localarg && *chptr != '-') chptr--;
817     if (chptr == localarg) return 1;
818
819     *chptr = '\0';
820     return findMatches(db, localarg, chptr + 1, release, matches);
821 }
822
823 /* 0 found matches */
824 /* 1 no matches */
825 /* 2 error */
826 int findMatches(rpmdb db, char * name, char * version, char * release,
827                 dbiIndexSet * matches) {
828     int gotMatches;
829     int rc;
830     int i;
831     char * pkgRelease, * pkgVersion;
832     int count, type;
833     int goodRelease, goodVersion;
834     Header h;
835
836     if ((rc = rpmdbFindPackage(db, name, matches))) {
837         if (rc == -1) return 2; else return 1;
838     }
839
840     if (!version && !release) return 0;
841
842     gotMatches = 0;
843
844     /* make sure the version and releases match */
845     for (i = 0; i < matches->count; i++) {
846         if (matches->recs[i].recOffset) {
847             h = rpmdbGetRecord(db, matches->recs[i].recOffset);
848             if (!h) {
849                 fprintf(stderr, "error: could not read database record\n");
850                 dbiFreeIndexRecord(*matches);
851                 return 2;
852             }
853
854             headerGetEntry(h, RPMTAG_VERSION, &type, (void **) &pkgVersion, &count);
855             headerGetEntry(h, RPMTAG_RELEASE, &type, (void **) &pkgRelease, &count);
856             
857             goodRelease = goodVersion = 1;
858
859             if (release && strcmp(release, pkgRelease)) goodRelease = 0;
860             if (version && strcmp(version, pkgVersion)) goodVersion = 0;
861
862             if (goodRelease && goodVersion) 
863                 gotMatches = 1;
864             else 
865                 matches->recs[i].recOffset = 0;
866         }
867     }
868
869     if (!gotMatches) {
870         dbiFreeIndexRecord(*matches);
871         return 1;
872     }
873     
874     return 0;
875 }
876
877 void queryPrintTags(void) {
878     const struct headerTagTableEntry * t;
879     int i;
880
881     for (i = 0, t = rpmTagTable; i < rpmTagTableSize; i++, t++) {
882         printf("%s\n", t->name);
883     }
884 }