1 /** \ingroup rpmcli
2  * \file lib/query.c
3  * Display tag values from package metadata.
4  */
5 
6 #include "system.h"
7 
8 #include <errno.h>
9 #include <inttypes.h>
10 #include <ctype.h>
11 
12 #include <rpm/rpmcli.h>
13 #include <rpm/header.h>
14 #include <rpm/rpmdb.h>
15 #include <rpm/rpmfi.h>
16 #include <rpm/rpmts.h>
17 #include <rpm/rpmsq.h>
18 #include <rpm/rpmlog.h>
19 #include <rpm/rpmfileutil.h>	/* rpmCleanPath */
20 
21 #include "lib/rpmgi.h"
22 #include "lib/manifest.h"
23 
24 #include "debug.h"
25 
26 
27 /**
28  */
printFileInfo(const char * name,rpm_loff_t size,unsigned short mode,unsigned int mtime,unsigned short rdev,unsigned int nlink,const char * owner,const char * group,const char * linkto,time_t now)29 static void printFileInfo(const char * name,
30 			  rpm_loff_t size, unsigned short mode,
31 			  unsigned int mtime,
32 			  unsigned short rdev, unsigned int nlink,
33 			  const char * owner, const char * group,
34 			  const char * linkto, time_t now)
35 {
36     char sizefield[21];
37     char ownerfield[8+1], groupfield[8+1];
38     char timefield[100];
39     time_t when = mtime;  /* important if sizeof(int32_t) ! sizeof(time_t) */
40     struct tm * tm, _tm;
41     char * perms = rpmPermsString(mode);
42     char *link = NULL;
43 
44     rstrlcpy(ownerfield, owner, sizeof(ownerfield));
45     rstrlcpy(groupfield, group, sizeof(groupfield));
46 
47     /* this is normally right */
48     snprintf(sizefield, sizeof(sizefield), "%20" PRIu64, size);
49 
50     /* this knows too much about dev_t */
51 
52     if (S_ISLNK(mode)) {
53 	rasprintf(&link, "%s -> %s", name, linkto);
54     } else if (S_ISCHR(mode)) {
55 	perms[0] = 'c';
56 	snprintf(sizefield, sizeof(sizefield), "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
57 			((unsigned)rdev & 0xff));
58     } else if (S_ISBLK(mode)) {
59 	perms[0] = 'b';
60 	snprintf(sizefield, sizeof(sizefield), "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
61 			((unsigned)rdev & 0xff));
62     }
63 
64     /* Convert file mtime to display format */
65     tm = localtime_r(&when, &_tm);
66     timefield[0] = '\0';
67     if (tm != NULL)
68     {	const char *fmt;
69 	if (now > when + 6L * 30L * 24L * 60L * 60L ||	/* Old. */
70 	    now < when - 60L * 60L)			/* In the future.  */
71 	{
72 	/* The file is fairly old or in the future.
73 	 * POSIX says the cutoff is 6 months old;
74 	 * approximate this by 6*30 days.
75 	 * Allow a 1 hour slop factor for what is considered "the future",
76 	 * to allow for NFS server/client clock disagreement.
77 	 * Show the year instead of the time of day.
78 	 */
79 	    fmt = "%b %e  %Y";
80 	} else {
81 	    fmt = "%b %e %H:%M";
82 	}
83 	(void)strftime(timefield, sizeof(timefield) - 1, fmt, tm);
84     }
85 
86     rpmlog(RPMLOG_NOTICE, "%s %4d %-8s %-8s %10s %s %s\n", perms,
87 	(int)nlink, ownerfield, groupfield, sizefield, timefield,
88 	link ? link : name);
89     free(perms);
90     free(link);
91 }
92 
showQueryPackage(QVA_t qva,rpmts ts,Header h)93 int showQueryPackage(QVA_t qva, rpmts ts, Header h)
94 {
95     rpmfi fi = NULL;
96     rpmfiFlags fiflags =  (RPMFI_NOHEADER | RPMFI_FLAGS_QUERY);
97     int rc = 0;		/* XXX FIXME: need real return code */
98     time_t now = 0;
99 
100     if (qva->qva_queryFormat != NULL) {
101 	const char *errstr;
102 	char *str = headerFormat(h, qva->qva_queryFormat, &errstr);
103 
104 	if ( str != NULL ) {
105 	    rpmlog(RPMLOG_NOTICE, "%s", str);
106 	    free(str);
107 	} else {
108 	    rpmlog(RPMLOG_ERR, _("incorrect format: %s\n"), errstr);
109 	}
110     }
111 
112     /* Inclusion flags traditionally imply list mode */
113     if (qva->qva_incattr)
114 	qva->qva_flags |= QUERY_FOR_LIST;
115 
116     if (!(qva->qva_flags & QUERY_FOR_LIST))
117 	goto exit;
118 
119     if (!(qva->qva_flags & QUERY_FOR_DUMPFILES))
120 	fiflags |= RPMFI_NOFILEDIGESTS;
121 
122     fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, fiflags);
123     if (rpmfiFC(fi) <= 0) {
124 	rpmlog(RPMLOG_NOTICE, _("(contains no files)\n"));
125 	goto exit;
126     }
127 
128     fi = rpmfiInit(fi, 0);
129     while (rpmfiNext(fi) >= 0) {
130 	rpmfileAttrs fflags = rpmfiFFlags(fi);
131 	rpm_mode_t fmode = rpmfiFMode(fi);
132 	rpm_rdev_t frdev = rpmfiFRdev(fi);
133 	rpm_time_t fmtime = rpmfiFMtime(fi);
134 	rpmfileState fstate = rpmfiFState(fi);
135 	rpm_loff_t fsize = rpmfiFSize(fi);
136 	const char *fn = rpmfiFN(fi);
137 	const char *fuser = rpmfiFUser(fi);
138 	const char *fgroup = rpmfiFGroup(fi);
139 	const char *flink = rpmfiFLink(fi);
140 	char *buf = NULL;
141 
142 	/* If filtering by inclusion, skip non-matching (eg --configfiles) */
143 	if (qva->qva_incattr && !(fflags & qva->qva_incattr))
144 	    continue;
145 
146 	/* Skip on attributes (eg from --noghost) */
147 	if (fflags & qva->qva_excattr)
148 	    continue;
149 
150 	if (qva->qva_flags & QUERY_FOR_STATE) {
151 	    switch (fstate) {
152 	    case RPMFILE_STATE_NORMAL:
153 		rstrcat(&buf, _("normal        "));
154 		break;
155 	    case RPMFILE_STATE_REPLACED:
156 		rstrcat(&buf, _("replaced      "));
157 		break;
158 	    case RPMFILE_STATE_NOTINSTALLED:
159 		rstrcat(&buf, _("not installed "));
160 		break;
161 	    case RPMFILE_STATE_NETSHARED:
162 		rstrcat(&buf, _("net shared    "));
163 		break;
164 	    case RPMFILE_STATE_WRONGCOLOR:
165 		rstrcat(&buf, _("wrong color   "));
166 		break;
167 	    case RPMFILE_STATE_MISSING:
168 		rstrcat(&buf, _("(no state)    "));
169 		break;
170 	    default:
171 		rasprintf(&buf, _("(unknown %3d) "), fstate);
172 		break;
173 	    }
174 	}
175 
176 	if (qva->qva_flags & QUERY_FOR_DUMPFILES) {
177 	    char *add, *fdigest;
178 	    fdigest = rpmfiFDigestHex(fi, NULL);
179 	    rasprintf(&add, "%s %" PRIu64 " %d %s 0%o ",
180 		      fn, fsize, fmtime, fdigest ? fdigest : "", fmode);
181 	    rstrcat(&buf, add);
182 	    free(add);
183 	    free(fdigest);
184 
185 	    if (fuser && fgroup) {
186 		rasprintf(&add, "%s %s", fuser, fgroup);
187 		rstrcat(&buf, add);
188 		free(add);
189 	    } else {
190 		rpmlog(RPMLOG_ERR,
191 			_("package has not file owner/group lists\n"));
192 	    }
193 
194 	    rasprintf(&add, " %s %s %u %s",
195 				 fflags & RPMFILE_CONFIG ? "1" : "0",
196 				 fflags & RPMFILE_DOC ? "1" : "0",
197 				 frdev,
198 				 (flink && *flink ? flink : "X"));
199 	    rpmlog(RPMLOG_NOTICE, "%s%s\n", buf, add);
200 	    free(add);
201 	} else
202 	if (!rpmIsVerbose()) {
203 	    rpmlog(RPMLOG_NOTICE, "%s%s\n", buf ? buf : "", fn);
204 	}
205 	else {
206 	    uint32_t fnlink = rpmfiFNlink(fi);
207 
208 	    /* XXX Adjust directory link count and size for display output. */
209 	    if (S_ISDIR(fmode)) {
210 		fnlink++;
211 		fsize = 0;
212 	    }
213 
214 	    if (fuser && fgroup) {
215 		/* On first call, grab snapshot of now */
216 		if (now == 0)
217 		    now = time(NULL);
218 		if (buf) {
219 		    rpmlog(RPMLOG_NOTICE, "%s", buf);
220 		}
221 		printFileInfo(fn, fsize, fmode, fmtime, frdev, fnlink,
222 					fuser, fgroup, flink, now);
223 	    } else {
224 		rpmlog(RPMLOG_ERR,
225 			_("package has neither file owner or id lists\n"));
226 	    }
227 	}
228 	free(buf);
229     }
230 
231     rc = 0;
232 
233 exit:
234     rpmfiFree(fi);
235     return rc;
236 }
237 
rpmDisplayQueryTags(FILE * fp)238 void rpmDisplayQueryTags(FILE * fp)
239 {
240     static const char * const tagTypeNames[] = {
241 	"", "char", "int8", "int16", "int32", "int64",
242 	"string", "blob", "argv", "i18nstring"
243     };
244     const char *tname, *sname;
245     rpmtd names = rpmtdNew();
246     (void) rpmTagGetNames(names, 1);
247 
248     while ((tname = rpmtdNextString(names))) {
249 	sname = tname + strlen("RPMTAG_");
250 	if (rpmIsVerbose()) {
251 	    rpmTagVal tag = rpmTagGetValue(sname);
252 	    rpmTagType type = rpmTagGetTagType(tag);
253 	    fprintf(fp, "%-20s %6d", sname, tag);
254 	    if (type > RPM_NULL_TYPE && type <= RPM_MAX_TYPE)
255 		fprintf(fp, " %s", tagTypeNames[type]);
256 	} else {
257 	    fprintf(fp, "%s", sname);
258 	}
259 	fprintf(fp, "\n");
260     }
261     rpmtdFree(names);
262 }
263 
rpmgiShowMatches(QVA_t qva,rpmts ts,rpmgi gi)264 static int rpmgiShowMatches(QVA_t qva, rpmts ts, rpmgi gi)
265 {
266     int ec = 0;
267     Header h;
268 
269     while ((h = rpmgiNext(gi)) != NULL) {
270 	int rc;
271 
272 	rpmsqPoll();
273 	if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
274 	    ec = rc;
275 	headerFree(h);
276     }
277     return ec + rpmgiNumErrors(gi);
278 }
279 
rpmcliShowMatches(QVA_t qva,rpmts ts,rpmdbMatchIterator mi)280 static int rpmcliShowMatches(QVA_t qva, rpmts ts, rpmdbMatchIterator mi)
281 {
282     Header h;
283     int ec = 0;
284 
285     if (mi == NULL)
286 	return 1;
287 
288     while ((h = rpmdbNextIterator(mi)) != NULL) {
289 	int rc;
290 	rpmsqPoll();
291 	if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
292 	    ec = rc;
293     }
294     return ec;
295 }
296 
matchesIterator(rpmts ts,rpmDbiTag dbi,const void * arg,size_t arglen)297 static rpmdbMatchIterator matchesIterator(rpmts ts, rpmDbiTag dbi,
298 					const void *arg, size_t arglen)
299 {
300     unsigned int matches = 0;
301     rpmdbMatchIterator mi = rpmtsInitIterator(ts, dbi, arg, arglen);
302     while (rpmdbNextIterator(mi) != NULL)
303 	matches++;
304     mi = rpmdbFreeIterator(mi);
305     if (matches)
306 	mi = rpmtsInitIterator(ts, dbi, arg, arglen);
307     return mi;
308 }
309 
initQueryIterator(QVA_t qva,rpmts ts,const char * arg)310 static rpmdbMatchIterator initQueryIterator(QVA_t qva, rpmts ts, const char * arg)
311 {
312     const char * s;
313     int i;
314     rpmdbMatchIterator mi = NULL;
315 
316     (void) rpmsqPoll();
317 
318     if (qva->qva_showPackage == NULL)
319 	goto exit;
320 
321     switch (qva->qva_source) {
322     case RPMQV_GROUP:
323 	mi = rpmtsInitIterator(ts, RPMDBI_GROUP, arg, 0);
324 	if (mi == NULL) {
325 	    rpmlog(RPMLOG_NOTICE,
326 		_("group %s does not contain any packages\n"), arg);
327 	}
328 	break;
329 
330     case RPMQV_TRIGGEREDBY:
331 	mi = rpmtsInitIterator(ts, RPMDBI_TRIGGERNAME, arg, 0);
332 	if (mi == NULL) {
333 	    rpmlog(RPMLOG_NOTICE, _("no package triggers %s\n"), arg);
334 	}
335 	break;
336 
337     case RPMQV_PKGID:
338     {	unsigned char MD5[16];
339 	unsigned char * t;
340 
341 	for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
342 	    {};
343 	if (i != 32) {
344 	    rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "pkgid", arg);
345 	    goto exit;
346 	}
347 
348 	MD5[0] = '\0';
349         for (i = 0, t = MD5, s = arg; i < 16; i++, t++, s += 2)
350             *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
351 
352 	mi = rpmtsInitIterator(ts, RPMDBI_SIGMD5, MD5, sizeof(MD5));
353 	if (mi == NULL) {
354 	    rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
355 			"pkgid", arg);
356 	}
357     }	break;
358 
359     case RPMQV_HDRID:
360 	for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
361 	    {};
362 	if (i != 40) {
363 	    rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "hdrid", arg);
364 	    goto exit;
365 	}
366 
367 	mi = rpmtsInitIterator(ts, RPMDBI_SHA1HEADER, arg, 0);
368 	if (mi == NULL) {
369 	    rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
370 			"hdrid", arg);
371 	}
372 	break;
373 
374     case RPMQV_TID:
375     {	char * end = NULL;
376 	rpm_tid_t iid = strtoul(arg, &end, 0);
377 
378 	if ((*end) || (end == arg) || (iid == UINT_MAX)) {
379 	    rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "tid", arg);
380 	    goto exit;
381 	}
382 	mi = rpmtsInitIterator(ts, RPMDBI_INSTALLTID, &iid, sizeof(iid));
383 	if (mi == NULL) {
384 	    rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
385 			"tid", arg);
386 	}
387     }	break;
388 
389     case RPMQV_WHATCONFLICTS:
390 	mi = rpmtsInitIterator(ts, RPMDBI_CONFLICTNAME, arg, 0);
391 	if (mi == NULL) {
392 	    rpmlog(RPMLOG_NOTICE, _("no package conflicts %s\n"), arg);
393 	}
394 	break;
395 
396     case RPMQV_WHATOBSOLETES:
397 	mi = rpmtsInitIterator(ts, RPMDBI_OBSOLETENAME, arg, 0);
398 	if (mi == NULL) {
399 	    rpmlog(RPMLOG_NOTICE, _("no package obsoletes %s\n"), arg);
400 	}
401 	break;
402 
403     case RPMQV_WHATREQUIRES:
404 	mi = rpmtsInitIterator(ts, RPMDBI_REQUIRENAME, arg, 0);
405 	if (mi == NULL) {
406 	    rpmlog(RPMLOG_NOTICE, _("no package requires %s\n"), arg);
407 	}
408 	break;
409 
410     case RPMQV_WHATRECOMMENDS:
411 	mi = rpmtsInitIterator(ts, RPMDBI_RECOMMENDNAME, arg, 0);
412 	if (mi == NULL) {
413 	    rpmlog(RPMLOG_NOTICE, _("no package recommends %s\n"), arg);
414 	}
415 	break;
416 
417     case RPMQV_WHATSUGGESTS:
418 	mi = rpmtsInitIterator(ts, RPMDBI_SUGGESTNAME, arg, 0);
419 	if (mi == NULL) {
420 	    rpmlog(RPMLOG_NOTICE, _("no package suggests %s\n"), arg);
421 	}
422 	break;
423 
424     case RPMQV_WHATSUPPLEMENTS:
425 	mi = rpmtsInitIterator(ts, RPMDBI_SUPPLEMENTNAME, arg, 0);
426 	if (mi == NULL) {
427 	    rpmlog(RPMLOG_NOTICE, _("no package supplements %s\n"), arg);
428 	}
429 	break;
430 
431     case RPMQV_WHATENHANCES:
432 	mi = rpmtsInitIterator(ts, RPMDBI_ENHANCENAME, arg, 0);
433 	if (mi == NULL) {
434 	    rpmlog(RPMLOG_NOTICE, _("no package enhances %s\n"), arg);
435 	}
436 	break;
437 
438     case RPMQV_WHATPROVIDES:
439 	if (arg[0] != '/' && arg[0] != '.') {
440 	    mi = rpmtsInitIterator(ts, RPMDBI_PROVIDENAME, arg, 0);
441 	    if (mi == NULL) {
442 		rpmlog(RPMLOG_NOTICE, _("no package provides %s\n"), arg);
443 	    }
444 	    break;
445 	}
446 	/* fallthrough on absolute and relative paths */
447     case RPMQV_PATH:
448     {   char * fn;
449 
450 	for (s = arg; *s != '\0'; s++)
451 	    if (!(*s == '.' || *s == '/'))
452 		break;
453 
454 	if (*s == '\0') {
455 	    char fnbuf[PATH_MAX];
456 	    fn = realpath(arg, fnbuf);
457 	    fn = xstrdup( (fn != NULL ? fn : arg) );
458 	} else if (*arg != '/') {
459 	    char *curDir = rpmGetCwd();
460 	    fn = (char *) rpmGetPath(curDir, "/", arg, NULL);
461 	    free(curDir);
462 	} else
463 	    fn = xstrdup(arg);
464 	(void) rpmCleanPath(fn);
465 
466 	/* XXX Add a switch to enable former BASENAMES behavior? */
467 	mi = rpmtsInitIterator(ts, RPMDBI_INSTFILENAMES, fn, 0);
468 	if (mi == NULL)
469 	    mi = rpmtsInitIterator(ts, RPMDBI_PROVIDENAME, fn, 0);
470 
471 	if (mi == NULL) {
472 	    struct stat sb;
473 	    if (lstat(fn, &sb) != 0)
474 		rpmlog(RPMLOG_ERR, _("file %s: %s\n"), fn, strerror(errno));
475 	    else
476 		rpmlog(RPMLOG_NOTICE,
477 			_("file %s is not owned by any package\n"), fn);
478 	}
479 
480 	free(fn);
481     }	break;
482 
483     case RPMQV_DBOFFSET:
484     {	char * end = NULL;
485 	unsigned int recOffset = strtoul(arg, &end, 0);
486 
487 	if ((*end) || (end == arg) || (recOffset == UINT_MAX)) {
488 	    rpmlog(RPMLOG_ERR, _("invalid package number: %s\n"), arg);
489 	    goto exit;
490 	}
491 	rpmlog(RPMLOG_DEBUG, "package record number: %u\n", recOffset);
492 	/* RPMDBI_PACKAGES */
493 	mi = matchesIterator(ts, RPMDBI_PACKAGES, &recOffset, sizeof(recOffset));
494 	if (mi == NULL) {
495 	    rpmlog(RPMLOG_ERR, _("record %u could not be read\n"), recOffset);
496 	}
497     }	break;
498 
499     case RPMQV_PACKAGE:
500     {
501 	mi = matchesIterator(ts, RPMDBI_LABEL, arg, 0);
502 
503 	if (mi == NULL && !rpmFileHasSuffix(arg, ".rpm"))
504 	    rpmlog(RPMLOG_NOTICE, _("package %s is not installed\n"), arg);
505 	break;
506     }
507     default:
508 	break;
509     }
510 
511 exit:
512     return mi;
513 }
514 
515 /*
516  * Initialize db iterator with optional filters.  By default patterns
517  * applied to package name, others can be specified with <tagname>=<pattern>
518  */
initFilterIterator(rpmts ts,ARGV_const_t argv)519 static rpmdbMatchIterator initFilterIterator(rpmts ts, ARGV_const_t argv)
520 {
521     rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
522 
523     for (ARGV_const_t arg = argv; arg && *arg != NULL; arg++) {
524 	rpmTagVal tag = RPMTAG_NAME;
525 	char a[strlen(*arg)+1], *ae;
526 	const char *pat = a;
527 
528 	strcpy(a, *arg);
529 
530 	/* Parse for "tag=pattern" args. */
531 	if ((ae = strchr(a, '=')) != NULL) {
532 	    *ae++ = '\0';
533 	    tag = rpmTagGetValue(a);
534 	    if (tag == RPMTAG_NOT_FOUND) {
535 		rpmlog(RPMLOG_ERR, _("unknown tag: \"%s\"\n"), a);
536 		mi = rpmdbFreeIterator(mi);
537 		break;
538 	    }
539 	    pat = ae;
540 	}
541 
542 	rpmdbSetIteratorRE(mi, tag, RPMMIRE_DEFAULT, pat);
543     }
544 
545     return mi;
546 }
547 
rpmcliArgIter(rpmts ts,QVA_t qva,ARGV_const_t argv)548 int rpmcliArgIter(rpmts ts, QVA_t qva, ARGV_const_t argv)
549 {
550     int ec = 0;
551 
552     switch (qva->qva_source) {
553     case RPMQV_ALL: {
554 	rpmdbMatchIterator mi = initFilterIterator(ts, argv);
555 	ec = rpmcliShowMatches(qva, ts, mi);
556 	rpmdbFreeIterator(mi);
557 	break;
558     }
559     case RPMQV_RPM: {
560 	rpmgi gi = rpmgiNew(ts, giFlags, argv);
561 	ec = rpmgiShowMatches(qva, ts, gi);
562 	rpmgiFree(gi);
563 	break;
564     }
565     case RPMQV_SPECRPMS:
566     case RPMQV_SPECBUILTRPMS:
567     case RPMQV_SPECSRPM: {
568 	char *target = rpmExpand("%{_target}", NULL);
569 	for (ARGV_const_t arg = argv; arg && *arg; arg++) {
570 	    ec += ((qva->qva_specQuery != NULL)
571 		    ? qva->qva_specQuery(ts, qva, *arg) : 1);
572 	    rpmFreeMacros(NULL);
573 	    rpmReadConfigFiles(rpmcliRcfile, target);
574 	}
575 	free(target);
576 	break;
577     }
578     default:
579 	for (ARGV_const_t arg = argv; arg && *arg; arg++) {
580 	    int ecLocal;
581 	    rpmdbMatchIterator mi = initQueryIterator(qva, ts, *arg);
582 	    ecLocal = rpmcliShowMatches(qva, ts, mi);
583 	    if (mi == NULL && qva->qva_source == RPMQV_PACKAGE) {
584 		if (rpmFileHasSuffix(*arg, ".rpm")) {
585 		    char * const argFirst[2] = { arg[0], NULL };
586 		    rpmgi gi = rpmgiNew(ts, giFlags, argFirst);
587 		    ecLocal = rpmgiShowMatches(qva, ts, gi);
588 		    rpmgiFree(gi);
589 		}
590 	    }
591 	    ec += ecLocal;
592 	    rpmdbFreeIterator(mi);
593 	}
594 	break;
595     }
596 
597     return ec;
598 }
599 
rpmcliQuery(rpmts ts,QVA_t qva,char * const * argv)600 int rpmcliQuery(rpmts ts, QVA_t qva, char * const * argv)
601 {
602     rpmVSFlags vsflags, ovsflags;
603     int ec = 0;
604 
605     if (qva->qva_showPackage == NULL)
606 	qva->qva_showPackage = showQueryPackage;
607 
608     /* If --queryformat unspecified, then set default now. */
609     if (!(qva->qva_flags & _QUERY_FOR_BITS) && !(qva->qva_incattr) &&
610 					    qva->qva_queryFormat == NULL) {
611 	char * fmt = rpmExpand("%{?_query_all_fmt}\n", NULL);
612 	if (fmt == NULL || strlen(fmt) <= 1) {
613 	    free(fmt);
614 	    fmt = xstrdup("%{nvra}\n");
615 	}
616 	qva->qva_queryFormat = fmt;
617     }
618 
619     vsflags = rpmExpandNumeric("%{?_vsflags_query}");
620     vsflags |= rpmcliVSFlags;
621 
622     ovsflags = rpmtsSetVSFlags(ts, vsflags);
623     ec = rpmcliArgIter(ts, qva, argv);
624     rpmtsSetVSFlags(ts, ovsflags);
625 
626     if (qva->qva_showPackage == showQueryPackage)
627 	qva->qva_showPackage = NULL;
628 
629     return ec;
630 }
631