3 * Display tag values from package metadata.
9 /*@-incondefs@*/ /* FIX: long int? */
25 /*@access rpmdbMatchIterator@*/ /* XXX compared with NULL */
26 /*@access Header@*/ /* XXX compared with NULL */
27 /*@access FD_t@*/ /* XXX compared with NULL */
31 static void printFileInfo(char * te, const char * name,
32 unsigned int size, unsigned short mode,
34 unsigned short rdev, unsigned int nlink,
35 const char * owner, const char * group,
40 char ownerfield[8+1], groupfield[8+1];
42 time_t when = mtime; /* important if sizeof(int_32) ! sizeof(time_t) */
45 static struct tm nowtm;
46 const char * namefield = name;
47 char * perms = rpmPermsString(mode);
49 /* On first call, grab snapshot of now */
54 if (tm) nowtm = *tm; /* structure assignment */
58 strncpy(ownerfield, owner, sizeof(ownerfield));
59 ownerfield[sizeof(ownerfield)-1] = '\0';
61 strncpy(groupfield, group, sizeof(groupfield));
62 groupfield[sizeof(groupfield)-1] = '\0';
64 /* this is normally right */
65 sprintf(sizefield, "%12u", size);
67 /* this knows too much about dev_t */
70 char *nf = alloca(strlen(name) + sizeof(" -> ") + strlen(linkto));
71 sprintf(nf, "%s -> %s", name, linkto);
73 } else if (S_ISCHR(mode)) {
75 sprintf(sizefield, "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
76 ((unsigned)rdev & 0xff));
77 } else if (S_ISBLK(mode)) {
79 sprintf(sizefield, "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
80 ((unsigned)rdev & 0xff));
83 /* Convert file mtime to display format */
84 tm = localtime(&when);
88 if (now > when + 6L * 30L * 24L * 60L * 60L || /* Old. */
89 now < when - 60L * 60L) /* In the future. */
91 /* The file is fairly old or in the future.
92 * POSIX says the cutoff is 6 months old;
93 * approximate this by 6*30 days.
94 * Allow a 1 hour slop factor for what is considered "the future",
95 * to allow for NFS server/client clock disagreement.
96 * Show the year instead of the time of day.
102 (void)strftime(timefield, sizeof(timefield) - 1, fmt, tm);
105 sprintf(te, "%s %4d %-8s%-8s %10s %s %s", perms,
106 (int)nlink, ownerfield, groupfield, sizefield, timefield, namefield);
107 perms = _free(perms);
112 static inline /*@null@*/ const char * queryHeader(Header h, const char * qfmt)
115 const char * errstr = "(unkown error)";
119 str = headerSprintf(h, qfmt, rpmTagTable, rpmHeaderFormats, &errstr);
122 rpmError(RPMERR_QFMT, _("incorrect format: %s\n"), errstr);
126 int showQueryPackage(QVA_t qva, /*@unused@*/ rpmts ts, Header h)
131 char * prefix = NULL;
132 int rc = 0; /* XXX FIXME: need real return code */
136 te = t = xmalloc(BUFSIZ);
141 if (!(qva->qva_flags & _QUERY_FOR_BITS) && qva->qva_queryFormat == NULL)
143 const char * name, * version, * release;
144 (void) headerNVR(h, &name, &version, &release);
146 te = stpcpy(te, name);
147 te = stpcpy( stpcpy(te, "-"), version);
148 te = stpcpy( stpcpy(te, "-"), release);
153 if (qva->qva_queryFormat != NULL) {
154 const char * str = queryHeader(h, qva->qva_queryFormat);
158 size_t tb = (te - t);
159 size_t sb = strlen(str);
161 if (sb >= (BUFSIZ - tb)) {
162 t = xrealloc(t, BUFSIZ+sb);
167 te = stpcpy(te, str);
175 if (!(qva->qva_flags & QUERY_FOR_LIST))
178 fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem);
179 if (rpmfiFC(fi) <= 0) {
181 te = stpcpy(te, _("(contains no files)"));
186 fi = rpmfiInit(fi, 0);
188 while ((i = rpmfiNext(fi)) >= 0) {
190 unsigned short fmode;
191 unsigned short frdev;
202 fflags = rpmfiFFlags(fi);
203 fmode = rpmfiFMode(fi);
204 frdev = rpmfiFRdev(fi);
205 fmtime = rpmfiFMtime(fi);
206 fstate = rpmfiFState(fi);
207 fsize = rpmfiFSize(fi);
210 { static char hex[] = "0123456789abcdef";
211 const char * s = rpmfiMD5(fi);
214 for (j = 0; j < 16; j++) {
216 *p++ = hex[ (k >> 4) & 0xf ];
217 *p++ = hex[ (k ) & 0xf ];
222 fuser = rpmfiFUser(fi);
223 fgroup = rpmfiFGroup(fi);
224 flink = rpmfiFLink(fi);
225 fnlink = rpmfiFNlink(fi);
227 /* If querying only docs, skip non-doc files. */
228 if ((qva->qva_flags & QUERY_FOR_DOCS) && !(fflags & RPMFILE_DOC))
231 /* If querying only configs, skip non-config files. */
232 if ((qva->qva_flags & QUERY_FOR_CONFIG) && !(fflags & RPMFILE_CONFIG))
235 /* If not querying %ghost, skip ghost files. */
236 if (!(qva->qva_fflags & RPMFILE_GHOST) && (fflags & RPMFILE_GHOST))
240 if (!rpmIsVerbose() && prefix)
241 te = stpcpy(te, prefix);
243 if (qva->qva_flags & QUERY_FOR_STATE) {
245 case RPMFILE_STATE_NORMAL:
246 te = stpcpy(te, _("normal "));
247 /*@switchbreak@*/ break;
248 case RPMFILE_STATE_REPLACED:
249 te = stpcpy(te, _("replaced "));
250 /*@switchbreak@*/ break;
251 case RPMFILE_STATE_NOTINSTALLED:
252 te = stpcpy(te, _("not installed "));
253 /*@switchbreak@*/ break;
254 case RPMFILE_STATE_NETSHARED:
255 te = stpcpy(te, _("net shared "));
256 /*@switchbreak@*/ break;
257 case RPMFILE_STATE_MISSING:
258 te = stpcpy(te, _("(no state) "));
259 /*@switchbreak@*/ break;
261 sprintf(te, _("(unknown %3d) "), fstate);
263 /*@switchbreak@*/ break;
268 if (qva->qva_flags & QUERY_FOR_DUMPFILES) {
269 sprintf(te, "%s %d %d %s 0%o ", fn, fsize, fmtime, fmd5, fmode);
272 if (fuser && fgroup) {
274 sprintf(te, "%s %s", fuser, fgroup);
278 rpmError(RPMERR_INTERNAL,
279 _("package has not file owner/group lists\n"));
282 sprintf(te, " %s %s %u ",
283 fflags & RPMFILE_CONFIG ? "1" : "0",
284 fflags & RPMFILE_DOC ? "1" : "0",
288 sprintf(te, "%s", (flink && *flink ? flink : "X"));
291 if (!rpmIsVerbose()) {
298 /* XXX Adjust directory link count and size for display output. */
299 if (S_ISDIR(fmode)) {
304 if (fuser && fgroup) {
306 printFileInfo(te, fn, fsize, fmode, fmtime, frdev, fnlink,
307 fuser, fgroup, flink);
311 rpmError(RPMERR_INTERNAL,
312 _("package has neither file owner or id lists\n"));
319 rpmMessage(RPMMESS_NORMAL, "%s", t);
336 rpmMessage(RPMMESS_NORMAL, "%s", t);
345 * Print copy of spec file, filling in Group/Description/Summary from specspo.
346 * @param spec spec file control structure
349 printNewSpecfile(Spec spec)
350 /*@globals fileSystem @*/
351 /*@modifies spec->sl->sl_lines[], fileSystem @*/
354 speclines sl = spec->sl;
355 spectags st = spec->st;
356 const char * msgstr = NULL;
359 if (sl == NULL || st == NULL)
363 for (i = 0; i < st->st_ntags; i++) {
364 spectag t = st->st_t + i;
365 const char * tn = tagName(t->t_tag);
370 if (t->t_msgid == NULL)
371 h = spec->packages->header;
377 strcpy(fmt, t->t_msgid);
378 for (fe = fmt; *fe && *fe != '('; fe++)
380 if (*fe == '(') *fe = '\0';
383 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
386 (void) headerNVR(h, &pkgname, NULL, NULL);
387 if (!strcmp(pkgname, fmt))
388 /*@innerbreak@*/ break;
390 if (pkg == NULL || h == NULL)
391 h = spec->packages->header;
399 (void) stpcpy( stpcpy( stpcpy( fmt, "%{"), tn), "}");
401 msgstr = _free(msgstr);
403 /* XXX this should use queryHeader(), but prints out tn as well. */
404 msgstr = headerSprintf(h, fmt, rpmTagTable, rpmHeaderFormats, &errstr);
405 if (msgstr == NULL) {
406 rpmError(RPMERR_QFMT, _("can't query %s: %s\n"), tn, errstr);
414 /*@-unqualifiedtrans@*/
415 sl->sl_lines[t->t_startx] = _free(sl->sl_lines[t->t_startx]);
416 /*@=unqualifiedtrans@*/
417 if (t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))
419 { char *buf = xmalloc(strlen(tn) + sizeof(": ") + strlen(msgstr));
420 (void) stpcpy( stpcpy( stpcpy(buf, tn), ": "), msgstr);
421 sl->sl_lines[t->t_startx] = buf;
423 /*@switchbreak@*/ break;
424 case RPMTAG_DESCRIPTION:
425 for (j = 1; j < t->t_nlines; j++) {
426 if (*sl->sl_lines[t->t_startx + j] == '%')
427 /*@innercontinue@*/ continue;
428 /*@-unqualifiedtrans@*/
429 sl->sl_lines[t->t_startx + j] =
430 _free(sl->sl_lines[t->t_startx + j]);
431 /*@=unqualifiedtrans@*/
433 if (t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG)) {
434 sl->sl_lines[t->t_startx] = _free(sl->sl_lines[t->t_startx]);
437 sl->sl_lines[t->t_startx + 1] = xstrdup(msgstr);
439 sl->sl_lines[t->t_startx + 2] = xstrdup("\n\n");
440 /*@switchbreak@*/ break;
445 msgstr = _free(msgstr);
448 for (i = 0; i < sl->sl_nlines; i++) {
449 const char * s = sl->sl_lines[i];
453 if (strchr(s, '\n') == NULL && s[strlen(s)-1] != '\n')
459 void rpmDisplayQueryTags(FILE * fp)
461 const struct headerTagTableEntry_s * t;
463 const struct headerSprintfExtension_s * ext = rpmHeaderFormats;
465 for (i = 0, t = rpmTagTable; i < rpmTagTableSize; i++, t++)
466 if (t->name) fprintf(fp, "%s\n", t->name + 7);
468 while (ext->name != NULL) {
469 if (ext->type == HEADER_EXT_MORE) {
473 /* XXX don't print query tags twice. */
474 for (i = 0, t = rpmTagTable; i < rpmTagTableSize; i++, t++) {
475 if (t->name == NULL) /* XXX programmer error. */
476 /*@innercontinue@*/ continue;
477 if (!strcmp(t->name, ext->name))
478 /*@innerbreak@*/ break;
480 if (i >= rpmTagTableSize && ext->type == HEADER_EXT_TAG)
481 fprintf(fp, "%s\n", ext->name + 7);
486 int rpmcliShowMatches(QVA_t qva, rpmts ts)
491 while ((h = rpmdbNextIterator(qva->qva_mi)) != NULL) {
493 if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
496 qva->qva_mi = rpmdbFreeIterator(qva->qva_mi);
501 * Convert hex to binary nibble.
502 * @param c hex character
503 * @return binary nibble
505 static inline unsigned char nibble(char c)
508 if (c >= '0' && c <= '9')
510 if (c >= 'A' && c <= 'F')
511 return (c - 'A') + 10;
512 if (c >= 'a' && c <= 'f')
513 return (c - 'a') + 10;
519 * @todo Eliminate linkage loop into librpmbuild.a
521 int (*parseSpecVec) (Spec *specp, const char *specFile, const char *rootdir,
522 const char *buildRoot, int recursing, const char *passPhrase,
523 char *cookie, int anyarch, int force) = NULL;
525 * @todo Eliminate linkage loop into librpmbuild.a
527 /*@null@*/ Spec (*freeSpecVec) (Spec spec) = NULL;
530 /*@-bounds@*/ /* LCL: segfault (realpath annotation?) */
531 int rpmQueryVerify(QVA_t qva, rpmts ts, const char * arg)
533 const char ** av = NULL;
541 if (qva->qva_showPackage == NULL)
545 switch (qva->qva_source) {
548 const char * fileURL = NULL;
551 rc = rpmGlob(arg, &ac, &av);
555 for (i = 0; i < ac; i++) {
558 fileURL = _free(fileURL);
562 /* Try to read the header from a package file. */
563 fd = Fopen(fileURL, "r.ufdio");
564 if (fd == NULL || Ferror(fd)) {
565 rpmError(RPMERR_OPEN, _("open of %s failed: %s\n"), fileURL,
567 if (fd) (void) Fclose(fd);
569 /*@loopbreak@*/ break;
572 rpmrc = rpmReadPackageFile(ts, fd, fileURL, &h);
579 rpmError(RPMERR_QUERY, _("query of %s failed\n"), fileURL);
581 /*@switchbreak@*/ break;
584 rpmError(RPMERR_QUERY,
585 _("old format source packages cannot be queried\n"));
587 /*@switchbreak@*/ break;
590 /* Query a package file. */
591 res = qva->qva_showPackage(qva, ts, h);
595 /*@notreached@*/ /*@switchbreak@*/ break;
598 /*@switchbreak@*/ break;
601 /*@loopbreak@*/ break;
603 /* Try to read a package manifest. */
604 fd = Fopen(fileURL, "r.fpio");
605 if (fd == NULL || Ferror(fd)) {
606 rpmError(RPMERR_OPEN, _("open of %s failed: %s\n"), fileURL,
608 if (fd) (void) Fclose(fd);
610 /*@loopbreak@*/ break;
613 /* Read list of packages from manifest. */
614 res = rpmReadPackageManifest(fd, &ac, &av);
615 if (res != RPMRC_OK) {
616 rpmError(RPMERR_MANIFEST, _("%s: not an rpm package (or package manifest): %s\n"),
617 fileURL, Fstrerror(fd));
622 /* If successful, restart the query loop. */
626 /*@loopbreak@*/ break;
629 fileURL = _free(fileURL);
631 for (i = 0; i < ac; i++)
632 av[i] = _free(av[i]);
638 if (qva->qva_showPackage != showQueryPackage)
641 /* XXX Eliminate linkage dependency loop */
642 if (parseSpecVec == NULL || freeSpecVec == NULL)
647 char * buildRoot = NULL;
649 char * passPhrase = "";
654 /*@-mods@*/ /* FIX: make spec abstract */
655 rc = parseSpecVec(&spec, arg, "/", buildRoot, recursing, passPhrase,
656 cookie, anyarch, force);
658 if (rc || spec == NULL) {
659 rpmError(RPMERR_QUERY,
660 _("query of specfile %s failed, can't parse\n"), arg);
661 spec = freeSpecVec(spec);
667 printNewSpecfile(spec);
668 spec = freeSpecVec(spec);
673 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next)
674 xx = qva->qva_showPackage(qva, ts, pkg->header);
675 spec = freeSpecVec(spec);
679 qva->qva_mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
680 if (qva->qva_mi == NULL) {
681 rpmError(RPMERR_QUERYINFO, _("no packages\n"));
685 for (av = (const char **) arg; *av; av++) {
686 if (!rpmdbSetIteratorRE(qva->qva_mi, RPMTAG_NAME, RPMMIRE_DEFAULT, *av))
688 qva->qva_mi = rpmdbFreeIterator(qva->qva_mi);
690 /*@loopbreak@*/ break;
693 res = rpmcliShowMatches(qva, ts);
698 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_GROUP, arg, 0);
699 if (qva->qva_mi == NULL) {
700 rpmError(RPMERR_QUERYINFO,
701 _("group %s does not contain any packages\n"), arg);
704 res = rpmcliShowMatches(qva, ts);
708 case RPMQV_TRIGGEREDBY:
709 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_TRIGGERNAME, arg, 0);
710 if (qva->qva_mi == NULL) {
711 rpmError(RPMERR_QUERYINFO, _("no package triggers %s\n"), arg);
714 res = rpmcliShowMatches(qva, ts);
719 { unsigned char MD5[16];
722 for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
725 rpmError(RPMERR_QUERYINFO, _("malformed %s: %s\n"), "pkgid", arg);
730 for (i = 0, t = MD5, s = arg; i < 16; i++, t++, s += 2)
731 *t = (nibble(s[0]) << 4) | nibble(s[1]);
733 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_SIGMD5, MD5, sizeof(MD5));
734 if (qva->qva_mi == NULL) {
735 rpmError(RPMERR_QUERYINFO, _("no package matches %s: %s\n"),
739 res = rpmcliShowMatches(qva, ts);
744 for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
747 rpmError(RPMERR_QUERYINFO, _("malformed %s: %s\n"), "hdrid", arg);
751 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_SHA1HEADER, arg, 0);
752 if (qva->qva_mi == NULL) {
753 rpmError(RPMERR_QUERYINFO, _("no package matches %s: %s\n"),
757 res = rpmcliShowMatches(qva, ts);
762 { unsigned char MD5[16];
765 for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
768 rpmError(RPMERR_QUERY, _("malformed %s: %s\n"), "fileid", arg);
773 for (i = 0, t = MD5, s = arg; i < 16; i++, t++, s += 2)
774 *t = (nibble(s[0]) << 4) | nibble(s[1]);
776 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_FILEMD5S, MD5, sizeof(MD5));
777 if (qva->qva_mi == NULL) {
778 rpmError(RPMERR_QUERYINFO, _("no package matches %s: %s\n"),
782 res = rpmcliShowMatches(qva, ts);
788 const char * myarg = arg;
792 /* XXX should be in strtoul */
801 iid = strtoul(myarg, &end, mybase);
802 if ((*end) || (end == arg) || (iid == ULONG_MAX)) {
803 rpmError(RPMERR_QUERY, _("malformed %s: %s\n"), "tid", arg);
806 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_INSTALLTID, &iid, sizeof(iid));
807 if (qva->qva_mi == NULL) {
808 rpmError(RPMERR_QUERYINFO, _("no package matches %s: %s\n"),
812 res = rpmcliShowMatches(qva, ts);
816 case RPMQV_WHATREQUIRES:
817 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_REQUIRENAME, arg, 0);
818 if (qva->qva_mi == NULL) {
819 rpmError(RPMERR_QUERYINFO, _("no package requires %s\n"), arg);
822 res = rpmcliShowMatches(qva, ts);
826 case RPMQV_WHATPROVIDES:
828 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, arg, 0);
829 if (qva->qva_mi == NULL) {
830 rpmError(RPMERR_QUERYINFO, _("no package provides %s\n"), arg);
833 res = rpmcliShowMatches(qva, ts);
841 for (s = arg; *s != '\0'; s++)
842 if (!(*s == '.' || *s == '/'))
843 /*@loopbreak@*/ break;
846 char fnbuf[PATH_MAX];
847 fn = realpath(arg, fnbuf);
854 (void) rpmCleanPath(fn);
856 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, fn, 0);
857 if (qva->qva_mi == NULL) {
859 if (access(fn, F_OK) != 0)
863 rpmError(RPMERR_QUERY,
864 _("file %s: %s\n"), fn, strerror(myerrno));
865 /*@innerbreak@*/ break;
867 rpmError(RPMERR_QUERYINFO,
868 _("file %s is not owned by any package\n"), fn);
869 /*@innerbreak@*/ break;
873 res = rpmcliShowMatches(qva, ts);
880 const char * myarg = arg;
884 /* XXX should be in strtoul */
893 recOffset = strtoul(myarg, &end, mybase);
894 if ((*end) || (end == arg) || (recOffset == ULONG_MAX)) {
895 rpmError(RPMERR_QUERYINFO, _("invalid package number: %s\n"), arg);
898 rpmMessage(RPMMESS_DEBUG, _("package record number: %u\n"), recOffset);
899 /* RPMDBI_PACKAGES */
900 qva->qva_mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &recOffset, sizeof(recOffset));
901 if (qva->qva_mi == NULL) {
902 rpmError(RPMERR_QUERYINFO,
903 _("record %u could not be read\n"), recOffset);
906 res = rpmcliShowMatches(qva, ts);
911 /* XXX HACK to get rpmdbFindByLabel out of the API */
912 qva->qva_mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0);
913 if (qva->qva_mi == NULL) {
914 rpmError(RPMERR_QUERYINFO, _("package %s is not installed\n"), arg);
917 res = rpmcliShowMatches(qva, ts);
927 int rpmcliQuery(rpmts ts, QVA_t qva, const char ** argv)
930 rpmVSFlags vsflags, ovsflags;
933 if (qva->qva_showPackage == NULL)
934 qva->qva_showPackage = showQueryPackage;
936 vsflags = rpmExpandNumeric("%{?_vsflags_query}");
937 if (qva->qva_flags & VERIFY_DIGEST)
938 vsflags |= _RPMVSF_NODIGESTS;
939 if (qva->qva_flags & VERIFY_SIGNATURE)
940 vsflags |= _RPMVSF_NOSIGNATURES;
941 if (qva->qva_flags & VERIFY_HDRCHK)
942 vsflags |= RPMVSF_NOHDRCHK;
944 ovsflags = rpmtsSetVSFlags(ts, vsflags);
945 if (qva->qva_source == RPMQV_ALL) {
946 /*@-nullpass@*/ /* FIX: argv can be NULL, cast to pass argv array */
947 ec = rpmQueryVerify(qva, ts, (const char *) argv);
952 while ((arg = *argv++) != NULL) {
953 ec += rpmQueryVerify(qva, ts, arg);
958 vsflags = rpmtsSetVSFlags(ts, ovsflags);
960 if (qva->qva_showPackage == showQueryPackage)
961 qva->qva_showPackage = NULL;