7 #include "build/rpmbuild.h"
11 /* ======================================================================== */
12 static char * permsString(int mode)
14 char *perms = strdup("----------");
18 else if (S_ISLNK(mode))
20 else if (S_ISFIFO(mode))
22 else if (S_ISSOCK(mode))
24 else if (S_ISCHR(mode))
26 else if (S_ISBLK(mode))
29 if (mode & S_IRUSR) perms[1] = 'r';
30 if (mode & S_IWUSR) perms[2] = 'w';
31 if (mode & S_IXUSR) perms[3] = 'x';
33 if (mode & S_IRGRP) perms[4] = 'r';
34 if (mode & S_IWGRP) perms[5] = 'w';
35 if (mode & S_IXGRP) perms[6] = 'x';
37 if (mode & S_IROTH) perms[7] = 'r';
38 if (mode & S_IWOTH) perms[8] = 'w';
39 if (mode & S_IXOTH) perms[9] = 'x';
42 perms[3] = ((mode & S_IXUSR) ? 's' : 'S');
45 perms[6] = ((mode & S_IXGRP) ? 's' : 'S');
48 perms[9] = ((mode & S_IXOTH) ? 't' : 'T');
53 static void printFileInfo(FILE *fp, const char * name,
54 unsigned int size, unsigned short mode,
55 unsigned int mtime, unsigned short rdev,
56 const char * owner, const char * group,
57 int uid, int gid, const char * linkto)
60 char ownerfield[9], groupfield[9];
61 char timefield[100] = "";
62 time_t when = mtime; /* important if sizeof(int_32) ! sizeof(time_t) */
65 static struct tm nowtm;
66 const char * namefield = name;
69 /* On first call, grab snapshot of now */
73 nowtm = *tm; /* structure assignment */
76 perms = permsString(mode);
79 strncpy(ownerfield, owner, 8);
81 sprintf(ownerfield, "%-8d", uid);
85 strncpy(groupfield, group, 8);
87 sprintf(groupfield, "%-8d", gid);
90 /* this is normally right */
91 sprintf(sizefield, "%12u", size);
93 /* this knows too much about dev_t */
96 char *nf = alloca(strlen(name) + sizeof(" -> ") + strlen(linkto));
97 sprintf(nf, "%s -> %s", name, linkto);
99 } else if (S_ISCHR(mode)) {
101 sprintf(sizefield, "%3u, %3u", (rdev >> 8) & 0xff, rdev & 0xFF);
102 } else if (S_ISBLK(mode)) {
104 sprintf(sizefield, "%3u, %3u", (rdev >> 8) & 0xff, rdev & 0xFF);
107 /* Convert file mtime to display format */
108 tm = localtime(&when);
110 if (now > when + 6L * 30L * 24L * 60L * 60L || /* Old. */
111 now < when - 60L * 60L) /* In the future. */
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.
124 (void)strftime(timefield, sizeof(timefield) - 1, fmt, tm);
127 fprintf(fp, "%s %8s %8s %10s %s %s\n", perms, ownerfield, groupfield,
128 sizefield, timefield, namefield);
129 if (perms) free(perms);
132 static int queryHeader(FILE *fp, Header h, const char * chptr)
137 str = headerSprintf(h, chptr, rpmTagTable, rpmHeaderFormats, &error);
139 fprintf(stderr, _("error in format: %s\n"), error);
148 int showQueryPackage(QVA_t *qva, rpmdb db, Header h)
150 FILE *fp = stdout; /* XXX FIXME: pass as arg */
151 int queryFlags = qva->qva_flags;
152 const char *queryFormat = qva->qva_queryFormat;
154 const char * name, * version, * release;
156 char * prefix = NULL;
157 char ** fileList, ** fileMD5List;
158 char * fileStatesList;
159 char ** fileOwnerList = NULL;
160 char ** fileGroupList = NULL;
161 char ** fileLinktoList;
162 int_32 * fileFlagsList, * fileMTimeList, * fileSizeList;
163 int_32 * fileUIDList, * fileGIDList;
164 uint_16 * fileModeList;
165 uint_16 * fileRdevList;
168 headerNVR(h, &name, &version, &release);
170 if (!queryFormat && !queryFlags) {
171 fprintf(fp, "%s-%s-%s\n", name, version, release);
174 queryHeader(fp, h, queryFormat);
176 if (queryFlags & QUERY_FOR_LIST) {
177 if (!headerGetEntry(h, RPMTAG_FILENAMES, &type, (void **) &fileList,
179 fputs(_("(contains no files)"), fp);
182 if (!headerGetEntry(h, RPMTAG_FILESTATES, &type,
183 (void **) &fileStatesList, &count)) {
184 fileStatesList = NULL;
186 headerGetEntry(h, RPMTAG_FILEFLAGS, &type,
187 (void **) &fileFlagsList, &count);
188 headerGetEntry(h, RPMTAG_FILESIZES, &type,
189 (void **) &fileSizeList, &count);
190 headerGetEntry(h, RPMTAG_FILEMODES, &type,
191 (void **) &fileModeList, &count);
192 headerGetEntry(h, RPMTAG_FILEMTIMES, &type,
193 (void **) &fileMTimeList, &count);
194 headerGetEntry(h, RPMTAG_FILERDEVS, &type,
195 (void **) &fileRdevList, &count);
196 headerGetEntry(h, RPMTAG_FILELINKTOS, &type,
197 (void **) &fileLinktoList, &count);
198 headerGetEntry(h, RPMTAG_FILEMD5S, &type,
199 (void **) &fileMD5List, &count);
201 if (!headerGetEntry(h, RPMTAG_FILEUIDS, &type,
202 (void **) &fileUIDList, &count)) {
205 headerGetEntry(h, RPMTAG_FILEGIDS, &type,
206 (void **) &fileGIDList, &count);
209 if (!headerGetEntry(h, RPMTAG_FILEUSERNAME, &type,
210 (void **) &fileOwnerList, &count)) {
211 fileOwnerList = NULL;
213 headerGetEntry(h, RPMTAG_FILEGROUPNAME, &type,
214 (void **) &fileGroupList, &count);
217 for (i = 0; i < count; i++) {
218 if (!((queryFlags & QUERY_FOR_DOCS) ||
219 (queryFlags & QUERY_FOR_CONFIG))
220 || ((queryFlags & QUERY_FOR_DOCS) &&
221 (fileFlagsList[i] & RPMFILE_DOC))
222 || ((queryFlags & QUERY_FOR_CONFIG) &&
223 (fileFlagsList[i] & RPMFILE_CONFIG))) {
226 prefix ? fputs(prefix, fp) : 0;
228 if (queryFlags & QUERY_FOR_STATE) {
229 if (fileStatesList) {
230 switch (fileStatesList[i]) {
231 case RPMFILE_STATE_NORMAL:
232 fputs(_("normal "), fp); break;
233 case RPMFILE_STATE_REPLACED:
234 fputs(_("replaced "), fp); break;
235 case RPMFILE_STATE_NOTINSTALLED:
236 fputs(_("not installed "), fp); break;
237 case RPMFILE_STATE_NETSHARED:
238 fputs(_("net shared "), fp); break;
240 fprintf(fp, _("(unknown %3d) "),
244 fputs( _("(no state) "), fp);
248 if (queryFlags & QUERY_FOR_DUMPFILES) {
249 fprintf(fp, "%s %d %d %s 0%o ", fileList[i],
250 fileSizeList[i], fileMTimeList[i],
251 fileMD5List[i], fileModeList[i]);
254 fprintf(fp, "%s %s", fileOwnerList[i],
256 else if (fileUIDList)
257 fprintf(fp, "%d %d", fileUIDList[i],
260 rpmError(RPMERR_INTERNAL, _("package has "
261 "neither file owner or id lists"));
264 fprintf(fp, " %s %s %u ",
265 fileFlagsList[i] & RPMFILE_CONFIG ? "1" : "0",
266 fileFlagsList[i] & RPMFILE_DOC ? "1" : "0",
267 (unsigned)fileRdevList[i]);
269 if (strlen(fileLinktoList[i]))
270 fprintf(fp, "%s\n", fileLinktoList[i]);
274 } else if (!rpmIsVerbose()) {
275 fputs(fileList[i], fp);
277 } else if (fileOwnerList)
278 printFileInfo(fp, fileList[i], fileSizeList[i],
279 fileModeList[i], fileMTimeList[i],
280 fileRdevList[i], fileOwnerList[i],
281 fileGroupList[i], -1,
282 -1, fileLinktoList[i]);
283 else if (fileUIDList) {
284 printFileInfo(fp, fileList[i], fileSizeList[i],
285 fileModeList[i], fileMTimeList[i],
286 fileRdevList[i], NULL,
287 NULL, fileUIDList[i],
288 fileGIDList[i], fileLinktoList[i]);
290 rpmError(RPMERR_INTERNAL, _("package has "
291 "neither file owner or id lists"));
297 free(fileLinktoList);
299 if (fileOwnerList) free(fileOwnerList);
300 if (fileGroupList) free(fileGroupList);
304 return 0; /* XXX FIXME: need real return code */
308 printNewSpecfile(Spec spec)
310 struct speclines *sl = spec->sl;
311 struct spectags *st = spec->st;
315 if (sl == NULL || st == NULL)
318 for (i = 0; i < st->st_ntags; i++) {
323 /* XXX Summary tag often precedes name, so build msgid now. */
324 if (t->t_msgid == NULL) {
326 headerGetEntry(spec->packages->header, RPMTAG_NAME, NULL,
328 sprintf(buf, "%s(%s)", n, tagName(t->t_tag));
329 t->t_msgid = strdup(buf);
331 msgstr = strdup(dgettext(specedit, t->t_msgid));
336 FREE(sl->sl_lines[t->t_startx]);
337 if (t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))
339 sprintf(buf, "%s: %s\n",
340 ((t->t_tag == RPMTAG_GROUP) ? "Group" : "Summary"),
342 sl->sl_lines[t->t_startx] = strdup(buf);
344 case RPMTAG_DESCRIPTION:
345 for (j = 1; j < t->t_nlines; j++)
346 FREE(sl->sl_lines[t->t_startx + j]);
347 if (t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG)) {
348 FREE(sl->sl_lines[t->t_startx]);
351 sl->sl_lines[t->t_startx + 1] = strdup(msgstr);
353 sl->sl_lines[t->t_startx + 2] = strdup("\n\n");
358 for (i = 0; i < sl->sl_nlines; i++) {
359 if (sl->sl_lines[i] == NULL)
361 printf("%s", sl->sl_lines[i]);
365 void rpmDisplayQueryTags(FILE * f) {
366 const struct headerTagTableEntry * t;
368 const struct headerSprintfExtension * ext = rpmHeaderFormats;
370 for (i = 0, t = rpmTagTable; i < rpmTagTableSize; i++, t++) {
371 fprintf(f, "%s\n", t->name + 7);
375 if (ext->type == HEADER_EXT_TAG)
376 fprintf(f, "%s\n", ext->name + 7), ext++;
377 else if (ext->type == HEADER_EXT_MORE)
384 int showMatches(QVA_t *qva, rpmdb db, dbiIndexSet matches, QVF_t showPackage)
390 for (i = 0; i < dbiIndexSetCount(matches); i++) {
392 unsigned int recOffset = dbiIndexRecordOffset(matches, i);
395 rpmMessage(RPMMESS_DEBUG, _("record number %u\n"), recOffset);
397 h = rpmdbGetRecord(db, recOffset);
399 fprintf(stderr, _("error: could not read database record\n"));
402 if ((rc = showPackage(qva, db, h)) != 0)
411 * XXX Eliminate linkage loop into librpmbuild.a
413 int (*parseSpecVec) (Spec *specp, const char *specFile, const char *buildRoot,
414 int inBuildArch, const char *passPhrase, char *cookie, int anyarch,
416 void (*freeSpecVec) (Spec spec) = NULL;
417 char *specedit = NULL;
419 int rpmQueryVerify(QVA_t *qva, enum rpmQVSources source, const char * arg,
420 rpmdb db, QVF_t showPackage)
435 fd = ufdOpen(arg, O_RDONLY, 0);
436 if (fdFileno(fd) < 0) {
437 fprintf(stderr, _("open of %s failed: %s\n"), arg,urlStrerror(arg));
443 retcode = rpmReadPackageHeader(fd, &h, &isSource, NULL, NULL);
450 fprintf(stderr, _("old format source packages cannot "
455 retcode = showPackage(qva, db, h);
459 fprintf(stderr, _("%s does not appear to be a RPM package\n"), arg);
462 fprintf(stderr, _("query of %s failed\n"), arg);
469 if (showPackage != showQueryPackage)
472 /* XXX Eliminate linkage dependency loop */
473 if (parseSpecVec == NULL || freeSpecVec == NULL)
478 char * buildRoot = NULL;
480 char * passPhrase = "";
485 rc = parseSpecVec(&spec, arg, buildRoot, inBuildArch, passPhrase,
486 cookie, anyarch, force);
487 if (rc || spec == NULL) {
489 fprintf(stderr, _("query of specfile %s failed, can't parse\n"), arg);
490 if (spec != NULL) freeSpecVec(spec);
495 if (specedit != NULL) {
496 printNewSpecfile(spec);
502 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
503 showPackage(qva, NULL, pkg->header);
509 for (offset = rpmdbFirstRecNum(db);
511 offset = rpmdbNextRecNum(db, offset)) {
512 h = rpmdbGetRecord(db, offset);
514 fprintf(stderr, _("could not read database record!\n"));
517 if ((rc = showPackage(qva, db, h)) != 0)
524 if (rpmdbFindByGroup(db, arg, &matches)) {
525 fprintf(stderr, _("group %s does not contain any packages\n"), arg);
528 retcode = showMatches(qva, db, matches, showPackage);
529 dbiFreeIndexRecord(matches);
533 case RPMQV_WHATPROVIDES:
534 if (rpmdbFindByProvides(db, arg, &matches)) {
535 fprintf(stderr, _("no package provides %s\n"), arg);
538 retcode = showMatches(qva, db, matches, showPackage);
539 dbiFreeIndexRecord(matches);
543 case RPMQV_TRIGGEREDBY:
544 if (rpmdbFindByTriggeredBy(db, arg, &matches)) {
545 fprintf(stderr, _("no package triggers %s\n"), arg);
548 retcode = showMatches(qva, db, matches, showPackage);
549 dbiFreeIndexRecord(matches);
553 case RPMQV_WHATREQUIRES:
554 if (rpmdbFindByRequiredBy(db, arg, &matches)) {
555 fprintf(stderr, _("no package requires %s\n"), arg);
558 retcode = showMatches(qva, db, matches, showPackage);
559 dbiFreeIndexRecord(matches);
564 if (rpmdbFindByFile(db, arg, &matches)) {
566 if (access(arg, F_OK) != 0)
570 fprintf(stderr, _("file %s: %s\n"), arg, strerror(myerrno));
573 fprintf(stderr, _("file %s is not owned by any package\n"), arg);
578 retcode = showMatches(qva, db, matches, showPackage);
579 dbiFreeIndexRecord(matches);
584 recNumber = strtoul(arg, &end, 10);
585 if ((*end) || (end == arg) || (recNumber == ULONG_MAX)) {
586 fprintf(stderr, _("invalid package number: %s\n"), arg);
589 rpmMessage(RPMMESS_DEBUG, _("package record number: %d\n"), recNumber);
590 h = rpmdbGetRecord(db, recNumber);
592 fprintf(stderr, _("record %d could not be read\n"), recNumber);
595 retcode = showPackage(qva, db, h);
601 rc = rpmdbFindByLabel(db, arg, &matches);
604 fprintf(stderr, _("package %s is not installed\n"), arg);
605 } else if (rc == 2) {
607 fprintf(stderr, _("error looking for package %s\n"), arg);
609 retcode = showMatches(qva, db, matches, showPackage);
610 dbiFreeIndexRecord(matches);
618 int rpmQuery(QVA_t *qva, enum rpmQVSources source, const char * arg)
628 if (rpmdbOpen(qva->qva_prefix, &db, O_RDONLY, 0644)) {
629 fprintf(stderr, _("rpmQuery: rpmdbOpen() failed\n"));
635 rc = rpmQueryVerify(qva, source, arg, db, showQueryPackage);
643 /* ======================================================================== */
644 #define POPT_QUERYFORMAT 1000
645 #define POPT_WHATREQUIRES 1001
646 #define POPT_WHATPROVIDES 1002
647 #define POPT_QUERYBYNUMBER 1003
648 #define POPT_TRIGGEREDBY 1004
649 #define POPT_DUMP 1005
650 #define POPT_SPECFILE 1006
652 /* ========== Query/Verify source popt args */
653 static void rpmQVSourceArgCallback(poptContext con, enum poptCallbackReason reason,
654 const struct poptOption * opt, const char * arg,
659 case 'a': qva->qva_source |= RPMQV_ALL; qva->qva_sourceCount++; break;
660 case 'f': qva->qva_source |= RPMQV_PATH; qva->qva_sourceCount++; break;
661 case 'g': qva->qva_source |= RPMQV_GROUP; qva->qva_sourceCount++; break;
662 case 'p': qva->qva_source |= RPMQV_RPM; qva->qva_sourceCount++; break;
663 case POPT_WHATPROVIDES: qva->qva_source |= RPMQV_WHATPROVIDES;
664 qva->qva_sourceCount++; break;
665 case POPT_WHATREQUIRES: qva->qva_source |= RPMQV_WHATREQUIRES;
666 qva->qva_sourceCount++; break;
667 case POPT_TRIGGEREDBY: qva->qva_source |= RPMQV_TRIGGEREDBY;
668 qva->qva_sourceCount++; break;
670 /* XXX SPECFILE is not verify sources */
672 qva->qva_source |= RPMQV_SPECFILE;
673 qva->qva_sourceCount++;
675 case POPT_QUERYBYNUMBER:
676 qva->qva_source |= RPMQV_DBOFFSET;
677 qva->qva_sourceCount++;
682 struct poptOption rpmQVSourcePoptTable[] = {
683 { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA,
684 rpmQVSourceArgCallback, 0, NULL, NULL },
685 { "file", 'f', 0, 0, 'f',
686 N_("query package owning file"), "FILE" },
687 { "group", 'g', 0, 0, 'g',
688 N_("query packages in group"), "GROUP" },
689 { "package", 'p', 0, 0, 'p',
690 N_("query a package file"), NULL },
691 { "querybynumber", '\0', POPT_ARGFLAG_DOC_HIDDEN, 0,
692 POPT_QUERYBYNUMBER, NULL, NULL },
693 { "specfile", '\0', 0, 0, POPT_SPECFILE,
694 N_("query a spec file"), NULL },
695 { "triggeredby", '\0', 0, 0, POPT_TRIGGEREDBY,
696 N_("query the pacakges triggered by the package"), "PACKAGE" },
697 { "whatrequires", '\0', 0, 0, POPT_WHATREQUIRES,
698 N_("query the packages which require a capability"), "CAPABILITY" },
699 { "whatprovides", '\0', 0, 0, POPT_WHATPROVIDES,
700 N_("query the packages which provide a capability"), "CAPABILITY" },
701 { 0, 0, 0, 0, 0, NULL, NULL }
704 /* ========== Query specific popt args */
706 static void queryArgCallback(poptContext con, enum poptCallbackReason reason,
707 const struct poptOption * opt, const char * arg,
711 case 'c': qva->qva_flags |= QUERY_FOR_CONFIG | QUERY_FOR_LIST; break;
712 case 'd': qva->qva_flags |= QUERY_FOR_DOCS | QUERY_FOR_LIST; break;
713 case 'l': qva->qva_flags |= QUERY_FOR_LIST; break;
714 case 's': qva->qva_flags |= QUERY_FOR_STATE | QUERY_FOR_LIST; break;
715 case POPT_DUMP: qva->qva_flags |= QUERY_FOR_DUMPFILES | QUERY_FOR_LIST; break;
716 case 'v': rpmIncreaseVerbosity(); break;
718 case POPT_QUERYFORMAT:
719 { char *qf = (char *)qva->qva_queryFormat;
721 int len = strlen(qf) + strlen(arg) + 1;
722 qf = realloc(qf, len);
725 qf = malloc(strlen(arg) + 1);
728 qva->qva_queryFormat = qf;
733 struct poptOption rpmQueryPoptTable[] = {
734 { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA,
735 queryArgCallback, 0, NULL, NULL },
736 { "configfiles", 'c', 0, 0, 'c',
737 N_("list all configuration files"), NULL },
738 { "docfiles", 'd', 0, 0, 'd',
739 N_("list all documentation files"), NULL },
740 { "dump", '\0', 0, 0, POPT_DUMP,
741 N_("dump basic file information"), NULL },
742 { "list", 'l', 0, 0, 'l',
743 N_("list files in package"), NULL },
744 { "qf", '\0', POPT_ARG_STRING | POPT_ARGFLAG_DOC_HIDDEN, 0,
745 POPT_QUERYFORMAT, NULL, NULL },
746 { "queryformat", '\0', POPT_ARG_STRING, 0, POPT_QUERYFORMAT,
747 N_("use the following query format"), "QUERYFORMAT" },
748 { "specedit", '\0', POPT_ARG_STRING, &specedit, 0,
749 N_("substitute i18n sections from the following catalogue"),
751 { "state", 's', 0, 0, 's',
752 N_("display the states of the listed files"), NULL },
753 { "verbose", 'v', 0, 0, 'v',
754 N_("display a verbose file listing"), NULL },
755 { 0, 0, 0, 0, 0, NULL, NULL }
758 /* ======================================================================== */