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) {
198 fmd5 = pgpHexStr(rpmfiMD5(fi), rpmDigestLength(PGPHASHALGO_MD5));
200 rasprintf(&add, "%s %d %d %s 0%o ", fn, (int)fsize, fmtime, fmd5, fmode);
205 if (fuser && fgroup) {
206 rasprintf(&add, "%s %s", fuser, fgroup);
211 _("package has not file owner/group lists\n"));
214 rasprintf(&add, " %s %s %u %s",
215 fflags & RPMFILE_CONFIG ? "1" : "0",
216 fflags & RPMFILE_DOC ? "1" : "0",
218 (flink && *flink ? flink : "X"));
219 rpmlog(RPMLOG_NOTICE, "%s%s\n", buf, add);
222 if (!rpmIsVerbose()) {
223 rpmlog(RPMLOG_NOTICE, "%s%s\n", buf ? buf : "", fn);
227 /* XXX Adjust directory link count and size for display output. */
228 if (S_ISDIR(fmode)) {
233 if (fuser && fgroup) {
235 rpmlog(RPMLOG_NOTICE, "%s", buf);
237 printFileInfo(fn, fsize, fmode, fmtime, frdev, fnlink,
238 fuser, fgroup, flink);
241 _("package has neither file owner or id lists\n"));
254 void rpmDisplayQueryTags(FILE * fp)
256 static const char * const tagTypeNames[] = {
257 "", "char", "int8", "int16", "int32", "int64",
258 "string", "blob", "argv", "i18nstring"
260 const char *tname, *sname;
261 rpmtd names = rpmTagGetNames(1);
263 while ((tname = rpmtdNextString(names))) {
264 sname = tname + strlen("RPMTAG_");
265 if (rpmIsVerbose()) {
266 rpmTag tag = rpmTagGetValue(sname);
267 rpmTagType type = rpmTagGetType(tag) & RPM_MASK_TYPE;
268 fprintf(fp, "%-20s %6d", sname, tag);
269 if (type > RPM_NULL_TYPE && type <= RPM_MAX_TYPE)
270 fprintf(fp, " %s", tagTypeNames[type]);
272 fprintf(fp, "%s", sname);
276 rpmtdFreeData(names);
280 static int rpmgiShowMatches(QVA_t qva, rpmts ts)
282 rpmgi gi = qva->qva_gi;
285 while (rpmgiNext(gi) == RPMRC_OK) {
290 if (h == NULL) /* XXX perhaps stricter break instead? */
292 if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
294 if (qva->qva_source == RPMQV_DBOFFSET)
297 return rpmgiNumErrors(gi);
300 int rpmcliShowMatches(QVA_t qva, rpmts ts)
305 while ((h = rpmdbNextIterator(qva->qva_mi)) != NULL) {
307 if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
309 if (qva->qva_source == RPMQV_DBOFFSET)
312 qva->qva_mi = rpmdbFreeIterator(qva->qva_mi);
316 /* LCL: segfault (realpath annotation?) */
317 int rpmQueryVerify(QVA_t qva, rpmts ts, const char * arg)
322 int provides_checked = 0;
324 (void) rpmdbCheckSignals();
326 if (qva->qva_showPackage == NULL)
329 switch (qva->qva_source) {
331 res = rpmgiShowMatches(qva, ts);
335 res = rpmgiShowMatches(qva, ts);
339 res = rpmgiShowMatches(qva, ts);
343 res = rpmgiShowMatches(qva, ts);
347 res = ((qva->qva_specQuery != NULL)
348 ? qva->qva_specQuery(ts, qva, arg) : 1);
352 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_GROUP, arg, 0);
353 if (qva->qva_mi == NULL) {
354 rpmlog(RPMLOG_NOTICE,
355 _("group %s does not contain any packages\n"), arg);
358 res = rpmcliShowMatches(qva, ts);
361 case RPMQV_TRIGGEREDBY:
362 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_TRIGGERNAME, arg, 0);
363 if (qva->qva_mi == NULL) {
364 rpmlog(RPMLOG_NOTICE, _("no package triggers %s\n"), arg);
367 res = rpmcliShowMatches(qva, ts);
371 { unsigned char MD5[16];
374 for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
377 rpmlog(RPMLOG_NOTICE, _("malformed %s: %s\n"), "pkgid", arg);
382 for (i = 0, t = MD5, s = arg; i < 16; i++, t++, s += 2)
383 *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
385 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_SIGMD5, MD5, sizeof(MD5));
386 if (qva->qva_mi == NULL) {
387 rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
391 res = rpmcliShowMatches(qva, ts);
395 for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
398 rpmlog(RPMLOG_NOTICE, _("malformed %s: %s\n"), "hdrid", arg);
402 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_SHA1HEADER, arg, 0);
403 if (qva->qva_mi == NULL) {
404 rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
408 res = rpmcliShowMatches(qva, ts);
412 { unsigned char MD5[16];
415 for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
418 rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "fileid", arg);
423 for (i = 0, t = MD5, s = arg; i < 16; i++, t++, s += 2)
424 *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
426 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_FILEMD5S, MD5, sizeof(MD5));
427 if (qva->qva_mi == NULL) {
428 rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
432 res = rpmcliShowMatches(qva, ts);
437 const char * myarg = arg;
441 /* XXX should be in strtoul */
450 iid = strtoul(myarg, &end, mybase);
451 if ((*end) || (end == arg) || (iid == ULONG_MAX)) {
452 rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "tid", arg);
455 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_INSTALLTID, &iid, sizeof(iid));
456 if (qva->qva_mi == NULL) {
457 rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
461 res = rpmcliShowMatches(qva, ts);
464 case RPMQV_WHATREQUIRES:
465 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_REQUIRENAME, arg, 0);
466 if (qva->qva_mi == NULL) {
467 rpmlog(RPMLOG_NOTICE, _("no package requires %s\n"), arg);
470 res = rpmcliShowMatches(qva, ts);
473 case RPMQV_WHATPROVIDES:
475 provides_checked = 1;
476 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, arg, 0);
477 if (qva->qva_mi == NULL) {
478 rpmlog(RPMLOG_NOTICE, _("no package provides %s\n"), arg);
481 res = rpmcliShowMatches(qva, ts);
487 for (s = arg; *s != '\0'; s++)
488 if (!(*s == '.' || *s == '/'))
492 char fnbuf[PATH_MAX];
493 fn = realpath(arg, fnbuf);
494 fn = xstrdup( (fn != NULL ? fn : arg) );
495 } else if (*arg != '/') {
496 char *curDir = rpmGetCwd();
497 fn = (char *) rpmGetPath(curDir, "/", arg, NULL);
498 curDir = _free(curDir);
501 (void) rpmCleanPath(fn);
503 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, fn, 0);
504 if (qva->qva_mi == NULL && !provides_checked)
505 qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, fn, 0);
507 if (qva->qva_mi == NULL) {
509 if (lstat(fn, &sb) != 0)
510 rpmlog(RPMLOG_ERR, _("file %s: %s\n"), fn, strerror(errno));
512 rpmlog(RPMLOG_NOTICE,
513 _("file %s is not owned by any package\n"), fn);
516 res = rpmcliShowMatches(qva, ts);
523 const char * myarg = arg;
525 unsigned long recOffset;
527 /* XXX should be in strtoul */
536 recOffset = strtoul(myarg, &end, mybase);
537 if ((*end) || (end == arg) || (recOffset == ULONG_MAX)) {
538 rpmlog(RPMLOG_NOTICE, _("invalid package number: %s\n"), arg);
541 rpmlog(RPMLOG_DEBUG, "package record number: %lu\n", recOffset);
542 /* RPMDBI_PACKAGES */
543 qva->qva_mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &recOffset, sizeof(recOffset));
544 if (qva->qva_mi == NULL) {
545 rpmlog(RPMLOG_NOTICE,
546 _("record %lu could not be read\n"), recOffset);
549 res = rpmcliShowMatches(qva, ts);
555 rpmdbMatchIterator mi;
556 mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0);
557 while (rpmdbNextIterator(mi) != NULL) {
560 rpmdbFreeIterator(mi);
562 rpmlog(RPMLOG_NOTICE, _("package %s is not installed\n"), arg);
565 qva->qva_mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0);
566 res = rpmcliShowMatches(qva, ts);
576 int rpmcliArgIter(rpmts ts, QVA_t qva, ARGV_const_t argv)
578 rpmRC rpmrc = RPMRC_NOTFOUND;
581 switch (qva->qva_source) {
583 qva->qva_gi = rpmgiNew(ts, RPMDBI_PACKAGES, NULL, 0);
584 qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts, RPMGI_NONE);
586 if (qva->qva_gi != NULL && (rpmgiGetFlags(qva->qva_gi) & RPMGI_TSADD)) /* Load the ts with headers. */
587 while ((rpmrc = rpmgiNext(qva->qva_gi)) == RPMRC_OK)
589 if (rpmrc != RPMRC_NOTFOUND)
590 return 1; /* XXX should be no. of failures. */
592 /* FIX: argv can be NULL, cast to pass argv array */
593 ec = rpmQueryVerify(qva, ts, (const char *) argv);
597 qva->qva_gi = rpmgiNew(ts, RPMDBI_ARGLIST, NULL, 0);
598 qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts, giFlags);
600 if (qva->qva_gi != NULL && (rpmgiGetFlags(qva->qva_gi) & RPMGI_TSADD)) /* Load the ts with headers. */
601 while ((rpmrc = rpmgiNext(qva->qva_gi)) == RPMRC_OK)
603 if (rpmrc != RPMRC_NOTFOUND)
604 return 1; /* XXX should be no. of failures. */
606 /* FIX: argv can be NULL, cast to pass argv array */
607 ec = rpmQueryVerify(qva, ts, NULL);
611 qva->qva_gi = rpmgiNew(ts, RPMDBI_HDLIST, NULL, 0);
612 qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts, giFlags);
614 if (qva->qva_gi != NULL && (rpmgiGetFlags(qva->qva_gi) & RPMGI_TSADD)) /* Load the ts with headers. */
615 while ((rpmrc = rpmgiNext(qva->qva_gi)) == RPMRC_OK)
617 if (rpmrc != RPMRC_NOTFOUND)
618 return 1; /* XXX should be no. of failures. */
620 /* FIX: argv can be NULL, cast to pass argv array */
621 ec = rpmQueryVerify(qva, ts, NULL);
626 ftsOpts = (RPMGI_COMFOLLOW | RPMGI_LOGICAL | RPMGI_NOSTAT);
627 qva->qva_gi = rpmgiNew(ts, RPMDBI_FTSWALK, NULL, 0);
628 qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts, giFlags);
630 if (qva->qva_gi != NULL && (rpmgiGetFlags(qva->qva_gi) & RPMGI_TSADD)) /* Load the ts with headers. */
631 while ((rpmrc = rpmgiNext(qva->qva_gi)) == RPMRC_OK)
633 if (rpmrc != RPMRC_NOTFOUND)
634 return 1; /* XXX should be no. of failures. */
636 /* FIX: argv can be NULL, cast to pass argv array */
637 ec = rpmQueryVerify(qva, ts, NULL);
641 qva->qva_gi = rpmgiNew(ts, RPMDBI_ARGLIST, NULL, 0);
642 qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts,
643 (giFlags | (RPMGI_NOGLOB|RPMGI_NOHEADER)));
644 while (rpmgiNext(qva->qva_gi) == RPMRC_OK) {
645 ec += rpmQueryVerify(qva, ts, rpmgiHdrPath(qva->qva_gi));
651 qva->qva_gi = rpmgiFree(qva->qva_gi);
656 int rpmcliQuery(rpmts ts, QVA_t qva, char * const * argv)
658 rpmVSFlags vsflags, ovsflags;
661 if (qva->qva_showPackage == NULL)
662 qva->qva_showPackage = showQueryPackage;
664 /* If --queryformat unspecified, then set default now. */
665 if (!(qva->qva_flags & _QUERY_FOR_BITS) && qva->qva_queryFormat == NULL) {
666 char * fmt = rpmExpand("%{?_query_all_fmt}\n", NULL);
667 if (fmt == NULL || strlen(fmt) <= 1) {
669 fmt = xstrdup("%{name}-%{version}-%{release}.%{arch}\n");
671 qva->qva_queryFormat = fmt;
674 vsflags = rpmExpandNumeric("%{?_vsflags_query}");
675 if (qva->qva_flags & VERIFY_DIGEST)
676 vsflags |= _RPMVSF_NODIGESTS;
677 if (qva->qva_flags & VERIFY_SIGNATURE)
678 vsflags |= _RPMVSF_NOSIGNATURES;
679 if (qva->qva_flags & VERIFY_HDRCHK)
680 vsflags |= RPMVSF_NOHDRCHK;
682 ovsflags = rpmtsSetVSFlags(ts, vsflags);
683 ec = rpmcliArgIter(ts, qva, argv);
684 vsflags = rpmtsSetVSFlags(ts, ovsflags);
686 if (qva->qva_showPackage == showQueryPackage)
687 qva->qva_showPackage = NULL;