3 * Display tag values from package metadata.
12 #include <rpm/rpmcli.h>
14 #include <rpm/header.h>
15 #include <rpm/rpmdb.h>
16 #include <rpm/rpmfi.h>
17 #include <rpm/rpmgi.h>
18 #include <rpm/rpmts.h>
19 #include <rpm/rpmlog.h>
20 #include <rpm/rpmfileutil.h> /* rpmCleanPath */
22 #include "lib/manifest.h"
29 static void printFileInfo(const char * name,
30 rpm_off_t size, unsigned short mode,
32 unsigned short rdev, unsigned int nlink,
33 const char * owner, const char * group,
37 char ownerfield[8+1], groupfield[8+1];
39 time_t when = mtime; /* important if sizeof(int32_t) ! sizeof(time_t) */
42 static struct tm nowtm;
43 char * perms = rpmPermsString(mode);
46 /* On first call, grab snapshot of now */
50 if (tm) nowtm = *tm; /* structure assignment */
53 rstrlcpy(ownerfield, owner, sizeof(ownerfield));
54 rstrlcpy(groupfield, group, sizeof(groupfield));
56 /* this is normally right */
57 sprintf(sizefield, "%12u", size);
59 /* this knows too much about dev_t */
62 rasprintf(&link, "%s -> %s", name, linkto);
63 } else if (S_ISCHR(mode)) {
65 sprintf(sizefield, "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
66 ((unsigned)rdev & 0xff));
67 } else if (S_ISBLK(mode)) {
69 sprintf(sizefield, "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
70 ((unsigned)rdev & 0xff));
73 /* Convert file mtime to display format */
74 tm = localtime(&when);
78 if (now > when + 6L * 30L * 24L * 60L * 60L || /* Old. */
79 now < when - 60L * 60L) /* In the future. */
81 /* The file is fairly old or in the future.
82 * POSIX says the cutoff is 6 months old;
83 * approximate this by 6*30 days.
84 * Allow a 1 hour slop factor for what is considered "the future",
85 * to allow for NFS server/client clock disagreement.
86 * Show the year instead of the time of day.
92 (void)strftime(timefield, sizeof(timefield) - 1, fmt, tm);
95 rpmlog(RPMLOG_NOTICE, "%s %4d %-8s%-8s %10s %s %s\n", perms,
96 (int)nlink, ownerfield, groupfield, sizefield, timefield,
102 int showQueryPackage(QVA_t qva, rpmts ts, Header h)
106 int rc = 0; /* XXX FIXME: need real return code */
109 if (qva->qva_queryFormat != NULL) {
111 char *str = headerFormat(h, qva->qva_queryFormat, &errstr);
114 rpmlog(RPMLOG_NOTICE, "%s", str);
117 rpmlog(RPMLOG_ERR, _("incorrect format: %s\n"), errstr);
121 if (!(qva->qva_flags & QUERY_FOR_LIST))
124 fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem);
125 if (rpmfiFC(fi) <= 0) {
126 rpmlog(RPMLOG_NOTICE, _("(contains no files)\n"));
130 fi = rpmfiInit(fi, 0);
132 while ((i = rpmfiNext(fi)) >= 0) {
135 unsigned short frdev;
146 fflags = rpmfiFFlags(fi);
147 fmode = rpmfiFMode(fi);
148 frdev = rpmfiFRdev(fi);
149 fmtime = rpmfiFMtime(fi);
150 fstate = rpmfiFState(fi);
151 fsize = rpmfiFSize(fi);
153 fuser = rpmfiFUser(fi);
154 fgroup = rpmfiFGroup(fi);
155 flink = rpmfiFLink(fi);
156 fnlink = rpmfiFNlink(fi);
158 /* If querying only docs, skip non-doc files. */
159 if ((qva->qva_flags & QUERY_FOR_DOCS) && !(fflags & RPMFILE_DOC))
162 /* If querying only configs, skip non-config files. */
163 if ((qva->qva_flags & QUERY_FOR_CONFIG) && !(fflags & RPMFILE_CONFIG))
166 /* If not querying %ghost, skip ghost files. */
167 if ((qva->qva_fflags & RPMFILE_GHOST) && (fflags & RPMFILE_GHOST))
170 if (qva->qva_flags & QUERY_FOR_STATE) {
172 case RPMFILE_STATE_NORMAL:
173 rstrcat(&buf, _("normal "));
175 case RPMFILE_STATE_REPLACED:
176 rstrcat(&buf, _("replaced "));
178 case RPMFILE_STATE_NOTINSTALLED:
179 rstrcat(&buf, _("not installed "));
181 case RPMFILE_STATE_NETSHARED:
182 rstrcat(&buf, _("net shared "));
184 case RPMFILE_STATE_WRONGCOLOR:
185 rstrcat(&buf, _("wrong color "));
187 case RPMFILE_STATE_MISSING:
188 rstrcat(&buf, _("(no state) "));
191 rasprintf(&buf, _("(unknown %3d) "), fstate);
196 if (qva->qva_flags & QUERY_FOR_DUMPFILES) {
199 const unsigned char *digest = rpmfiFDigest(fi, NULL, &diglen);
200 fdigest = pgpHexStr(digest, diglen);
202 rasprintf(&add, "%s %d %d %s 0%o ",
203 fn, (int)fsize, fmtime, fdigest, fmode);
208 if (fuser && fgroup) {
209 rasprintf(&add, "%s %s", fuser, fgroup);
214 _("package has not file owner/group lists\n"));
217 rasprintf(&add, " %s %s %u %s",
218 fflags & RPMFILE_CONFIG ? "1" : "0",
219 fflags & RPMFILE_DOC ? "1" : "0",
221 (flink && *flink ? flink : "X"));
222 rpmlog(RPMLOG_NOTICE, "%s%s\n", buf, add);
225 if (!rpmIsVerbose()) {
226 rpmlog(RPMLOG_NOTICE, "%s%s\n", buf ? buf : "", fn);
230 /* XXX Adjust directory link count and size for display output. */
231 if (S_ISDIR(fmode)) {
236 if (fuser && fgroup) {
238 rpmlog(RPMLOG_NOTICE, "%s", buf);
240 printFileInfo(fn, fsize, fmode, fmtime, frdev, fnlink,
241 fuser, fgroup, flink);
244 _("package has neither file owner or id lists\n"));
257 void rpmDisplayQueryTags(FILE * fp)
259 static const char * const tagTypeNames[] = {
260 "", "char", "int8", "int16", "int32", "int64",
261 "string", "blob", "argv", "i18nstring"
263 const char *tname, *sname;
264 rpmtd names = rpmTagGetNames(1);
266 while ((tname = rpmtdNextString(names))) {
267 sname = tname + strlen("RPMTAG_");
268 if (rpmIsVerbose()) {
269 rpmTag tag = rpmTagGetValue(sname);
270 rpmTagType type = rpmTagGetType(tag) & RPM_MASK_TYPE;
271 fprintf(fp, "%-20s %6d", sname, tag);
272 if (type > RPM_NULL_TYPE && type <= RPM_MAX_TYPE)
273 fprintf(fp, " %s", tagTypeNames[type]);
275 fprintf(fp, "%s", sname);
279 rpmtdFreeData(names);
283 static int rpmgiShowMatches(QVA_t qva, rpmts ts)
285 rpmgi gi = qva->qva_gi;
288 while (rpmgiNext(gi) == RPMRC_OK) {
293 if (h == NULL) /* XXX perhaps stricter break instead? */
295 if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
297 if (qva->qva_source == RPMQV_DBOFFSET)
300 return rpmgiNumErrors(gi);
303 int rpmcliShowMatches(QVA_t qva, rpmts ts)
308 while ((h = rpmdbNextIterator(qva->qva_mi)) != NULL) {
310 if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
312 if (qva->qva_source == RPMQV_DBOFFSET)
315 qva->qva_mi = rpmdbFreeIterator(qva->qva_mi);
319 /* LCL: segfault (realpath annotation?) */
320 int rpmQueryVerify(QVA_t qva, rpmts ts, const char * arg)
325 int provides_checked = 0;
327 (void) rpmdbCheckSignals();
329 if (qva->qva_showPackage == NULL)
332 switch (qva->qva_source) {
334 res = rpmgiShowMatches(qva, ts);
338 res = rpmgiShowMatches(qva, ts);
342 res = rpmgiShowMatches(qva, ts);
346 res = rpmgiShowMatches(qva, ts);
350 res = ((qva->qva_specQuery != NULL)
351 ? qva->qva_specQuery(ts, qva, arg) : 1);
355 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_GROUP, arg, 0);
356 if (qva->qva_mi == NULL) {
357 rpmlog(RPMLOG_NOTICE,
358 _("group %s does not contain any packages\n"), arg);
361 res = rpmcliShowMatches(qva, ts);
364 case RPMQV_TRIGGEREDBY:
365 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_TRIGGERNAME, arg, 0);
366 if (qva->qva_mi == NULL) {
367 rpmlog(RPMLOG_NOTICE, _("no package triggers %s\n"), arg);
370 res = rpmcliShowMatches(qva, ts);
374 { unsigned char MD5[16];
377 for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
380 rpmlog(RPMLOG_NOTICE, _("malformed %s: %s\n"), "pkgid", arg);
385 for (i = 0, t = MD5, s = arg; i < 16; i++, t++, s += 2)
386 *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
388 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_SIGMD5, MD5, sizeof(MD5));
389 if (qva->qva_mi == NULL) {
390 rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
394 res = rpmcliShowMatches(qva, ts);
398 for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
401 rpmlog(RPMLOG_NOTICE, _("malformed %s: %s\n"), "hdrid", arg);
405 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_SHA1HEADER, arg, 0);
406 if (qva->qva_mi == NULL) {
407 rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
411 res = rpmcliShowMatches(qva, ts);
415 { unsigned char MD5[16];
418 for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
421 rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "fileid", arg);
426 for (i = 0, t = MD5, s = arg; i < 16; i++, t++, s += 2)
427 *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
429 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_FILEMD5S, MD5, sizeof(MD5));
430 if (qva->qva_mi == NULL) {
431 rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
435 res = rpmcliShowMatches(qva, ts);
440 const char * myarg = arg;
444 /* XXX should be in strtoul */
453 iid = strtoul(myarg, &end, mybase);
454 if ((*end) || (end == arg) || (iid == ULONG_MAX)) {
455 rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "tid", arg);
458 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_INSTALLTID, &iid, sizeof(iid));
459 if (qva->qva_mi == NULL) {
460 rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
464 res = rpmcliShowMatches(qva, ts);
467 case RPMQV_WHATREQUIRES:
468 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_REQUIRENAME, arg, 0);
469 if (qva->qva_mi == NULL) {
470 rpmlog(RPMLOG_NOTICE, _("no package requires %s\n"), arg);
473 res = rpmcliShowMatches(qva, ts);
476 case RPMQV_WHATPROVIDES:
478 provides_checked = 1;
479 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, arg, 0);
480 if (qva->qva_mi == NULL) {
481 rpmlog(RPMLOG_NOTICE, _("no package provides %s\n"), arg);
484 res = rpmcliShowMatches(qva, ts);
490 for (s = arg; *s != '\0'; s++)
491 if (!(*s == '.' || *s == '/'))
495 char fnbuf[PATH_MAX];
496 fn = realpath(arg, fnbuf);
497 fn = xstrdup( (fn != NULL ? fn : arg) );
498 } else if (*arg != '/') {
499 char *curDir = rpmGetCwd();
500 fn = (char *) rpmGetPath(curDir, "/", arg, NULL);
501 curDir = _free(curDir);
504 (void) rpmCleanPath(fn);
506 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, fn, 0);
507 if (qva->qva_mi == NULL && !provides_checked)
508 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, fn, 0);
510 if (qva->qva_mi == NULL) {
512 if (lstat(fn, &sb) != 0)
513 rpmlog(RPMLOG_ERR, _("file %s: %s\n"), fn, strerror(errno));
515 rpmlog(RPMLOG_NOTICE,
516 _("file %s is not owned by any package\n"), fn);
519 res = rpmcliShowMatches(qva, ts);
526 const char * myarg = arg;
528 unsigned long recOffset;
530 /* XXX should be in strtoul */
539 recOffset = strtoul(myarg, &end, mybase);
540 if ((*end) || (end == arg) || (recOffset == ULONG_MAX)) {
541 rpmlog(RPMLOG_NOTICE, _("invalid package number: %s\n"), arg);
544 rpmlog(RPMLOG_DEBUG, "package record number: %lu\n", recOffset);
545 /* RPMDBI_PACKAGES */
546 qva->qva_mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &recOffset, sizeof(recOffset));
547 if (qva->qva_mi == NULL) {
548 rpmlog(RPMLOG_NOTICE,
549 _("record %lu could not be read\n"), recOffset);
552 res = rpmcliShowMatches(qva, ts);
558 rpmdbMatchIterator mi;
559 mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0);
560 while (rpmdbNextIterator(mi) != NULL) {
563 rpmdbFreeIterator(mi);
565 rpmlog(RPMLOG_NOTICE, _("package %s is not installed\n"), arg);
568 qva->qva_mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0);
569 res = rpmcliShowMatches(qva, ts);
579 int rpmcliArgIter(rpmts ts, QVA_t qva, ARGV_const_t argv)
581 rpmRC rpmrc = RPMRC_NOTFOUND;
584 switch (qva->qva_source) {
586 qva->qva_gi = rpmgiNew(ts, RPMDBI_PACKAGES, NULL, 0);
587 qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts, RPMGI_NONE);
589 if (qva->qva_gi != NULL && (rpmgiGetFlags(qva->qva_gi) & RPMGI_TSADD)) /* Load the ts with headers. */
590 while ((rpmrc = rpmgiNext(qva->qva_gi)) == RPMRC_OK)
592 if (rpmrc != RPMRC_NOTFOUND)
593 return 1; /* XXX should be no. of failures. */
595 /* FIX: argv can be NULL, cast to pass argv array */
596 ec = rpmQueryVerify(qva, ts, (const char *) argv);
600 qva->qva_gi = rpmgiNew(ts, RPMDBI_ARGLIST, NULL, 0);
601 qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts, giFlags);
603 if (qva->qva_gi != NULL && (rpmgiGetFlags(qva->qva_gi) & RPMGI_TSADD)) /* Load the ts with headers. */
604 while ((rpmrc = rpmgiNext(qva->qva_gi)) == RPMRC_OK)
606 if (rpmrc != RPMRC_NOTFOUND)
607 return 1; /* XXX should be no. of failures. */
609 /* FIX: argv can be NULL, cast to pass argv array */
610 ec = rpmQueryVerify(qva, ts, NULL);
614 qva->qva_gi = rpmgiNew(ts, RPMDBI_HDLIST, NULL, 0);
615 qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts, giFlags);
617 if (qva->qva_gi != NULL && (rpmgiGetFlags(qva->qva_gi) & RPMGI_TSADD)) /* Load the ts with headers. */
618 while ((rpmrc = rpmgiNext(qva->qva_gi)) == RPMRC_OK)
620 if (rpmrc != RPMRC_NOTFOUND)
621 return 1; /* XXX should be no. of failures. */
623 /* FIX: argv can be NULL, cast to pass argv array */
624 ec = rpmQueryVerify(qva, ts, NULL);
629 ftsOpts = (RPMGI_COMFOLLOW | RPMGI_LOGICAL | RPMGI_NOSTAT);
630 qva->qva_gi = rpmgiNew(ts, RPMDBI_FTSWALK, NULL, 0);
631 qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts, giFlags);
633 if (qva->qva_gi != NULL && (rpmgiGetFlags(qva->qva_gi) & RPMGI_TSADD)) /* Load the ts with headers. */
634 while ((rpmrc = rpmgiNext(qva->qva_gi)) == RPMRC_OK)
636 if (rpmrc != RPMRC_NOTFOUND)
637 return 1; /* XXX should be no. of failures. */
639 /* FIX: argv can be NULL, cast to pass argv array */
640 ec = rpmQueryVerify(qva, ts, NULL);
644 qva->qva_gi = rpmgiNew(ts, RPMDBI_ARGLIST, NULL, 0);
645 qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts,
646 (giFlags | (RPMGI_NOGLOB|RPMGI_NOHEADER)));
647 while (rpmgiNext(qva->qva_gi) == RPMRC_OK) {
648 ec += rpmQueryVerify(qva, ts, rpmgiHdrPath(qva->qva_gi));
654 qva->qva_gi = rpmgiFree(qva->qva_gi);
659 int rpmcliQuery(rpmts ts, QVA_t qva, char * const * argv)
661 rpmVSFlags vsflags, ovsflags;
664 if (qva->qva_showPackage == NULL)
665 qva->qva_showPackage = showQueryPackage;
667 /* If --queryformat unspecified, then set default now. */
668 if (!(qva->qva_flags & _QUERY_FOR_BITS) && qva->qva_queryFormat == NULL) {
669 char * fmt = rpmExpand("%{?_query_all_fmt}\n", NULL);
670 if (fmt == NULL || strlen(fmt) <= 1) {
672 fmt = xstrdup("%{name}-%{version}-%{release}.%{arch}\n");
674 qva->qva_queryFormat = fmt;
677 vsflags = rpmExpandNumeric("%{?_vsflags_query}");
678 if (qva->qva_flags & VERIFY_DIGEST)
679 vsflags |= _RPMVSF_NODIGESTS;
680 if (qva->qva_flags & VERIFY_SIGNATURE)
681 vsflags |= _RPMVSF_NOSIGNATURES;
682 if (qva->qva_flags & VERIFY_HDRCHK)
683 vsflags |= RPMVSF_NOHDRCHK;
685 ovsflags = rpmtsSetVSFlags(ts, vsflags);
686 ec = rpmcliArgIter(ts, qva, argv);
687 vsflags = rpmtsSetVSFlags(ts, ovsflags);
689 if (qva->qva_showPackage == showQueryPackage)
690 qva->qva_showPackage = NULL;