Blame lib/query.c

2ff057
/** \ingroup rpmcli
2ff057
 * \file lib/query.c
2ff057
 * Display tag values from package metadata.
2ff057
 */
2ff057
2ff057
#include "system.h"
2ff057
2ff057
#include <errno.h>
2ff057
#include <inttypes.h>
2ff057
#include <ctype.h>
2ff057
2ff057
#include <rpm/rpmcli.h>
2ff057
#include <rpm/header.h>
2ff057
#include <rpm/rpmdb.h>
2ff057
#include <rpm/rpmfi.h>
2ff057
#include <rpm/rpmts.h>
2ff057
#include <rpm/rpmsq.h>
2ff057
#include <rpm/rpmlog.h>
2ff057
#include <rpm/rpmfileutil.h>	/* rpmCleanPath */
2ff057
2ff057
#include "lib/rpmgi.h"
2ff057
#include "lib/manifest.h"
2ff057
2ff057
#include "debug.h"
2ff057
2ff057
2ff057
/**
2ff057
 */
2ff057
static void printFileInfo(const char * name,
2ff057
			  rpm_loff_t size, unsigned short mode,
2ff057
			  unsigned int mtime,
2ff057
			  unsigned short rdev, unsigned int nlink,
2ff057
			  const char * owner, const char * group,
2ff057
			  const char * linkto, time_t now)
2ff057
{
2ff057
    char sizefield[21];
2ff057
    char ownerfield[8+1], groupfield[8+1];
2ff057
    char timefield[100];
2ff057
    time_t when = mtime;  /* important if sizeof(int32_t) ! sizeof(time_t) */
2ff057
    struct tm * tm;
2ff057
    char * perms = rpmPermsString(mode);
2ff057
    char *link = NULL;
2ff057
2ff057
    rstrlcpy(ownerfield, owner, sizeof(ownerfield));
2ff057
    rstrlcpy(groupfield, group, sizeof(groupfield));
2ff057
2ff057
    /* this is normally right */
2ff057
    snprintf(sizefield, sizeof(sizefield), "%20" PRIu64, size);
2ff057
2ff057
    /* this knows too much about dev_t */
2ff057
2ff057
    if (S_ISLNK(mode)) {
2ff057
	rasprintf(&link, "%s -> %s", name, linkto);
2ff057
    } else if (S_ISCHR(mode)) {
2ff057
	perms[0] = 'c';
2ff057
	snprintf(sizefield, sizeof(sizefield), "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
2ff057
			((unsigned)rdev & 0xff));
2ff057
    } else if (S_ISBLK(mode)) {
2ff057
	perms[0] = 'b';
2ff057
	snprintf(sizefield, sizeof(sizefield), "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
2ff057
			((unsigned)rdev & 0xff));
2ff057
    }
2ff057
2ff057
    /* Convert file mtime to display format */
2ff057
    tm = localtime(&when);
2ff057
    timefield[0] = '\0';
2ff057
    if (tm != NULL)
2ff057
    {	const char *fmt;
2ff057
	if (now > when + 6L * 30L * 24L * 60L * 60L ||	/* Old. */
2ff057
	    now < when - 60L * 60L)			/* In the future.  */
2ff057
	{
2ff057
	/* The file is fairly old or in the future.
2ff057
	 * POSIX says the cutoff is 6 months old;
2ff057
	 * approximate this by 6*30 days.
2ff057
	 * Allow a 1 hour slop factor for what is considered "the future",
2ff057
	 * to allow for NFS server/client clock disagreement.
2ff057
	 * Show the year instead of the time of day.
2ff057
	 */        
2ff057
	    fmt = "%b %e  %Y";
2ff057
	} else {
2ff057
	    fmt = "%b %e %H:%M";
2ff057
	}
2ff057
	(void)strftime(timefield, sizeof(timefield) - 1, fmt, tm);
2ff057
    }
2ff057
2ff057
    rpmlog(RPMLOG_NOTICE, "%s %4d %-8s%-8s %10s %s %s\n", perms,
2ff057
	(int)nlink, ownerfield, groupfield, sizefield, timefield, 
2ff057
	link ? link : name);
2ff057
    free(perms);
2ff057
    free(link);
2ff057
}
2ff057
2ff057
int showQueryPackage(QVA_t qva, rpmts ts, Header h)
2ff057
{
2ff057
    rpmfi fi = NULL;
2ff057
    rpmfiFlags fiflags =  (RPMFI_NOHEADER | RPMFI_FLAGS_QUERY);
2ff057
    int rc = 0;		/* XXX FIXME: need real return code */
2ff057
    time_t now = 0;
2ff057
2ff057
    if (qva->qva_queryFormat != NULL) {
2ff057
	const char *errstr;
2ff057
	char *str = headerFormat(h, qva->qva_queryFormat, &errstr);
2ff057
2ff057
	if ( str != NULL ) {
2ff057
	    rpmlog(RPMLOG_NOTICE, "%s", str);
2ff057
	    free(str);
2ff057
	} else {
2ff057
	    rpmlog(RPMLOG_ERR, _("incorrect format: %s\n"), errstr);
2ff057
	}
2ff057
    }
2ff057
2ff057
    if (!(qva->qva_flags & QUERY_FOR_LIST))
2ff057
	goto exit;
2ff057
2ff057
    if (!(qva->qva_flags & QUERY_FOR_DUMPFILES))
2ff057
	fiflags |= RPMFI_NOFILEDIGESTS;
2ff057
2ff057
    fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, fiflags);
2ff057
    if (rpmfiFC(fi) <= 0) {
2ff057
	rpmlog(RPMLOG_NOTICE, _("(contains no files)\n"));
2ff057
	goto exit;
2ff057
    }
2ff057
2ff057
    fi = rpmfiInit(fi, 0);
2ff057
    while (rpmfiNext(fi) >= 0) {
2ff057
	rpmfileAttrs fflags = rpmfiFFlags(fi);
2ff057
	rpm_mode_t fmode = rpmfiFMode(fi);
2ff057
	rpm_rdev_t frdev = rpmfiFRdev(fi);
2ff057
	rpm_time_t fmtime = rpmfiFMtime(fi);
2ff057
	rpmfileState fstate = rpmfiFState(fi);
2ff057
	rpm_loff_t fsize = rpmfiFSize(fi);
2ff057
	const char *fn = rpmfiFN(fi);
2ff057
	const char *fuser = rpmfiFUser(fi);
2ff057
	const char *fgroup = rpmfiFGroup(fi);
2ff057
	const char *flink = rpmfiFLink(fi);
2ff057
	char *buf = NULL;
2ff057
2ff057
	/* If querying only docs, skip non-doc files. */
2ff057
	if ((qva->qva_flags & QUERY_FOR_DOCS) && !(fflags & RPMFILE_DOC))
2ff057
	    continue;
2ff057
2ff057
	/* If querying only configs, skip non-config files. */
2ff057
	if ((qva->qva_flags & QUERY_FOR_CONFIG) && !(fflags & RPMFILE_CONFIG))
2ff057
	    continue;
2ff057
2ff057
	/* If querying only licenses, skip non-license files. */
2ff057
	if ((qva->qva_flags & QUERY_FOR_LICENSE) && !(fflags & RPMFILE_LICENSE))
2ff057
	    continue;
2ff057
2ff057
	/* If querying only ... yes we know the drill, and this is dumb. */
2ff057
	if ((qva->qva_flags & QUERY_FOR_ARTIFACT) && !(fflags & RPMFILE_ARTIFACT))
2ff057
	    continue;
2ff057
2ff057
	/* Skip on attributes (eg from --noghost) */
2ff057
	if (fflags & qva->qva_fflags)
2ff057
	    continue;
2ff057
2ff057
	if (qva->qva_flags & QUERY_FOR_STATE) {
2ff057
	    switch (fstate) {
2ff057
	    case RPMFILE_STATE_NORMAL:
2ff057
		rstrcat(&buf, _("normal        "));
2ff057
		break;
2ff057
	    case RPMFILE_STATE_REPLACED:
2ff057
		rstrcat(&buf, _("replaced      "));
2ff057
		break;
2ff057
	    case RPMFILE_STATE_NOTINSTALLED:
2ff057
		rstrcat(&buf, _("not installed "));
2ff057
		break;
2ff057
	    case RPMFILE_STATE_NETSHARED:
2ff057
		rstrcat(&buf, _("net shared    "));
2ff057
		break;
2ff057
	    case RPMFILE_STATE_WRONGCOLOR:
2ff057
		rstrcat(&buf, _("wrong color   "));
2ff057
		break;
2ff057
	    case RPMFILE_STATE_MISSING:
2ff057
		rstrcat(&buf, _("(no state)    "));
2ff057
		break;
2ff057
	    default:
2ff057
		rasprintf(&buf, _("(unknown %3d) "), fstate);
2ff057
		break;
2ff057
	    }
2ff057
	}
2ff057
2ff057
	if (qva->qva_flags & QUERY_FOR_DUMPFILES) {
2ff057
	    char *add, *fdigest;
2ff057
	    fdigest = rpmfiFDigestHex(fi, NULL);
2ff057
	    rasprintf(&add, "%s %" PRIu64 " %d %s 0%o ", 
2ff057
		      fn, fsize, fmtime, fdigest ? fdigest : "", fmode);
2ff057
	    rstrcat(&buf, add);
2ff057
	    free(add);
2ff057
	    free(fdigest);
2ff057
2ff057
	    if (fuser && fgroup) {
2ff057
		rasprintf(&add, "%s %s", fuser, fgroup);
2ff057
		rstrcat(&buf, add);
2ff057
		free(add);
2ff057
	    } else {
2ff057
		rpmlog(RPMLOG_ERR,
2ff057
			_("package has not file owner/group lists\n"));
2ff057
	    }
2ff057
2ff057
	    rasprintf(&add, " %s %s %u %s",
2ff057
				 fflags & RPMFILE_CONFIG ? "1" : "0",
2ff057
				 fflags & RPMFILE_DOC ? "1" : "0",
2ff057
				 frdev,
2ff057
				 (flink && *flink ? flink : "X"));
2ff057
	    rpmlog(RPMLOG_NOTICE, "%s%s\n", buf, add);
2ff057
	    free(add);
2ff057
	} else
2ff057
	if (!rpmIsVerbose()) {
2ff057
	    rpmlog(RPMLOG_NOTICE, "%s%s\n", buf ? buf : "", fn);
2ff057
	}
2ff057
	else {
2ff057
	    uint32_t fnlink = rpmfiFNlink(fi);
2ff057
2ff057
	    /* XXX Adjust directory link count and size for display output. */
2ff057
	    if (S_ISDIR(fmode)) {
2ff057
		fnlink++;
2ff057
		fsize = 0;
2ff057
	    }
2ff057
2ff057
	    if (fuser && fgroup) {
2ff057
		/* On first call, grab snapshot of now */
2ff057
		if (now == 0)
2ff057
		    now = time(NULL);
2ff057
		if (buf) {
2ff057
		    rpmlog(RPMLOG_NOTICE, "%s", buf);
2ff057
		}
2ff057
		printFileInfo(fn, fsize, fmode, fmtime, frdev, fnlink,
2ff057
					fuser, fgroup, flink, now);
2ff057
	    } else {
2ff057
		rpmlog(RPMLOG_ERR,
2ff057
			_("package has neither file owner or id lists\n"));
2ff057
	    }
2ff057
	}
2ff057
	free(buf);
2ff057
    }
2ff057
2ff057
    rc = 0;
2ff057
2ff057
exit:
2ff057
    rpmfiFree(fi);
2ff057
    return rc;
2ff057
}
2ff057
2ff057
void rpmDisplayQueryTags(FILE * fp)
2ff057
{
2ff057
    static const char * const tagTypeNames[] = {
2ff057
	"", "char", "int8", "int16", "int32", "int64",
2ff057
	"string", "blob", "argv", "i18nstring"
2ff057
    };
2ff057
    const char *tname, *sname;
2ff057
    rpmtd names = rpmtdNew();
2ff057
    (void) rpmTagGetNames(names, 1);
2ff057
2ff057
    while ((tname = rpmtdNextString(names))) {
2ff057
	sname = tname + strlen("RPMTAG_");
2ff057
	if (rpmIsVerbose()) {
2ff057
	    rpmTagVal tag = rpmTagGetValue(sname);
2ff057
	    rpmTagType type = rpmTagGetTagType(tag);
2ff057
	    fprintf(fp, "%-20s %6d", sname, tag);
2ff057
	    if (type > RPM_NULL_TYPE && type <= RPM_MAX_TYPE)
2ff057
		fprintf(fp, " %s", tagTypeNames[type]);
2ff057
	} else {
2ff057
	    fprintf(fp, "%s", sname);
2ff057
	}
2ff057
	fprintf(fp, "\n");
2ff057
    }
2ff057
    rpmtdFree(names);
2ff057
}
2ff057
2ff057
static int rpmgiShowMatches(QVA_t qva, rpmts ts, rpmgi gi)
2ff057
{
2ff057
    int ec = 0;
2ff057
    Header h;
2ff057
2ff057
    while ((h = rpmgiNext(gi)) != NULL) {
2ff057
	int rc;
2ff057
2ff057
	rpmsqPoll();
2ff057
	if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
2ff057
	    ec = rc;
2ff057
	headerFree(h);
2ff057
    }
2ff057
    return ec + rpmgiNumErrors(gi);
2ff057
}
2ff057
2ff057
static int rpmcliShowMatches(QVA_t qva, rpmts ts, rpmdbMatchIterator mi)
2ff057
{
2ff057
    Header h;
2ff057
    int ec = 0;
2ff057
2ff057
    if (mi == NULL)
2ff057
	return 1;
2ff057
2ff057
    while ((h = rpmdbNextIterator(mi)) != NULL) {
2ff057
	int rc;
2ff057
	rpmsqPoll();
2ff057
	if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
2ff057
	    ec = rc;
2ff057
    }
2ff057
    return ec;
2ff057
}
2ff057
2ff057
static rpmdbMatchIterator initQueryIterator(QVA_t qva, rpmts ts, const char * arg)
2ff057
{
2ff057
    const char * s;
2ff057
    int i;
2ff057
    rpmdbMatchIterator mi = NULL;
2ff057
2ff057
    (void) rpmsqPoll();
2ff057
2ff057
    if (qva->qva_showPackage == NULL)
2ff057
	goto exit;
2ff057
2ff057
    switch (qva->qva_source) {
2ff057
    case RPMQV_GROUP:
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_GROUP, arg, 0);
2ff057
	if (mi == NULL) {
2ff057
	    rpmlog(RPMLOG_NOTICE,
2ff057
		_("group %s does not contain any packages\n"), arg);
2ff057
	}
2ff057
	break;
2ff057
2ff057
    case RPMQV_TRIGGEREDBY:
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_TRIGGERNAME, arg, 0);
2ff057
	if (mi == NULL) {
2ff057
	    rpmlog(RPMLOG_NOTICE, _("no package triggers %s\n"), arg);
2ff057
	}
2ff057
	break;
2ff057
2ff057
    case RPMQV_PKGID:
2ff057
    {	unsigned char MD5[16];
2ff057
	unsigned char * t;
2ff057
2ff057
	for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
2ff057
	    {};
2ff057
	if (i != 32) {
2ff057
	    rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "pkgid", arg);
2ff057
	    goto exit;
2ff057
	}
2ff057
2ff057
	MD5[0] = '\0';
2ff057
        for (i = 0, t = MD5, s = arg; i < 16; i++, t++, s += 2)
2ff057
            *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
2ff057
	
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_SIGMD5, MD5, sizeof(MD5));
2ff057
	if (mi == NULL) {
2ff057
	    rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
2ff057
			"pkgid", arg);
2ff057
	}
2ff057
    }	break;
2ff057
2ff057
    case RPMQV_HDRID:
2ff057
	for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
2ff057
	    {};
2ff057
	if (i != 40) {
2ff057
	    rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "hdrid", arg);
2ff057
	    goto exit;
2ff057
	}
2ff057
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_SHA1HEADER, arg, 0);
2ff057
	if (mi == NULL) {
2ff057
	    rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
2ff057
			"hdrid", arg);
2ff057
	}
2ff057
	break;
2ff057
2ff057
    case RPMQV_TID:
2ff057
    {	char * end = NULL;
2ff057
	rpm_tid_t iid = strtoul(arg, &end, 0);
2ff057
2ff057
	if ((*end) || (end == arg) || (iid == UINT_MAX)) {
2ff057
	    rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "tid", arg);
2ff057
	    goto exit;
2ff057
	}
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_INSTALLTID, &iid, sizeof(iid));
2ff057
	if (mi == NULL) {
2ff057
	    rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
2ff057
			"tid", arg);
2ff057
	}
2ff057
    }	break;
2ff057
2ff057
    case RPMQV_WHATCONFLICTS:
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_CONFLICTNAME, arg, 0);
2ff057
	if (mi == NULL) {
2ff057
	    rpmlog(RPMLOG_NOTICE, _("no package conflicts %s\n"), arg);
2ff057
	}
2ff057
	break;
2ff057
2ff057
    case RPMQV_WHATOBSOLETES:
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_OBSOLETENAME, arg, 0);
2ff057
	if (mi == NULL) {
2ff057
	    rpmlog(RPMLOG_NOTICE, _("no package obsoletes %s\n"), arg);
2ff057
	}
2ff057
	break;
2ff057
2ff057
    case RPMQV_WHATREQUIRES:
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_REQUIRENAME, arg, 0);
2ff057
	if (mi == NULL) {
2ff057
	    rpmlog(RPMLOG_NOTICE, _("no package requires %s\n"), arg);
2ff057
	}
2ff057
	break;
2ff057
2ff057
    case RPMQV_WHATRECOMMENDS:
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_RECOMMENDNAME, arg, 0);
2ff057
	if (mi == NULL) {
2ff057
	    rpmlog(RPMLOG_NOTICE, _("no package recommends %s\n"), arg);
2ff057
	}
2ff057
	break;
2ff057
2ff057
    case RPMQV_WHATSUGGESTS:
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_SUGGESTNAME, arg, 0);
2ff057
	if (mi == NULL) {
2ff057
	    rpmlog(RPMLOG_NOTICE, _("no package suggests %s\n"), arg);
2ff057
	}
2ff057
	break;
2ff057
2ff057
    case RPMQV_WHATSUPPLEMENTS:
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_SUPPLEMENTNAME, arg, 0);
2ff057
	if (mi == NULL) {
2ff057
	    rpmlog(RPMLOG_NOTICE, _("no package supplements %s\n"), arg);
2ff057
	}
2ff057
	break;
2ff057
2ff057
    case RPMQV_WHATENHANCES:
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_ENHANCENAME, arg, 0);
2ff057
	if (mi == NULL) {
2ff057
	    rpmlog(RPMLOG_NOTICE, _("no package enhances %s\n"), arg);
2ff057
	}
2ff057
	break;
2ff057
2ff057
    case RPMQV_WHATPROVIDES:
2ff057
	if (arg[0] != '/' && arg[0] != '.') {
2ff057
	    mi = rpmtsInitIterator(ts, RPMDBI_PROVIDENAME, arg, 0);
2ff057
	    if (mi == NULL) {
2ff057
		rpmlog(RPMLOG_NOTICE, _("no package provides %s\n"), arg);
2ff057
	    }
2ff057
	    break;
2ff057
	}
2ff057
	/* fallthrough on absolute and relative paths */
2ff057
    case RPMQV_PATH:
2ff057
    {   char * fn;
2ff057
2ff057
	for (s = arg; *s != '\0'; s++)
2ff057
	    if (!(*s == '.' || *s == '/'))
2ff057
		break;
2ff057
2ff057
	if (*s == '\0') {
2ff057
	    char fnbuf[PATH_MAX];
2ff057
	    fn = realpath(arg, fnbuf);
2ff057
	    fn = xstrdup( (fn != NULL ? fn : arg) );
2ff057
	} else if (*arg != '/') {
2ff057
	    char *curDir = rpmGetCwd();
2ff057
	    fn = (char *) rpmGetPath(curDir, "/", arg, NULL);
2ff057
	    free(curDir);
2ff057
	} else
2ff057
	    fn = xstrdup(arg);
2ff057
	(void) rpmCleanPath(fn);
2ff057
2ff057
	/* XXX Add a switch to enable former BASENAMES behavior? */
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_INSTFILENAMES, fn, 0);
2ff057
	if (mi == NULL)
2ff057
	    mi = rpmtsInitIterator(ts, RPMDBI_PROVIDENAME, fn, 0);
2ff057
2ff057
	if (mi == NULL) {
2ff057
	    struct stat sb;
2ff057
	    if (lstat(fn, &sb) != 0)
2ff057
		rpmlog(RPMLOG_ERR, _("file %s: %s\n"), fn, strerror(errno));
2ff057
	    else
2ff057
		rpmlog(RPMLOG_NOTICE,
2ff057
			_("file %s is not owned by any package\n"), fn);
2ff057
	}
2ff057
2ff057
	free(fn);
2ff057
    }	break;
2ff057
2ff057
    case RPMQV_DBOFFSET:
2ff057
    {	char * end = NULL;
2ff057
	unsigned int recOffset = strtoul(arg, &end, 0);
2ff057
2ff057
	if ((*end) || (end == arg) || (recOffset == UINT_MAX)) {
2ff057
	    rpmlog(RPMLOG_ERR, _("invalid package number: %s\n"), arg);
2ff057
	    goto exit;
2ff057
	}
2ff057
	rpmlog(RPMLOG_DEBUG, "package record number: %u\n", recOffset);
2ff057
	/* RPMDBI_PACKAGES */
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &recOffset, sizeof(recOffset));
2ff057
	if (mi == NULL) {
2ff057
	    rpmlog(RPMLOG_ERR, _("record %u could not be read\n"), recOffset);
2ff057
	}
2ff057
    }	break;
2ff057
2ff057
    case RPMQV_PACKAGE:
2ff057
    {
2ff057
	int matches = 0;
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0);
2ff057
	while (rpmdbNextIterator(mi) != NULL) {
2ff057
	    matches++;
2ff057
	}
2ff057
	mi = rpmdbFreeIterator(mi);
2ff057
	if (! matches) {
2ff057
	    size_t l = strlen(arg);
2ff057
	    if (!(l > 4 && !strcmp(arg + l - 4, ".rpm")))
2ff057
		rpmlog(RPMLOG_NOTICE, _("package %s is not installed\n"), arg);
2ff057
	} else {
2ff057
	    mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0);
2ff057
	}
2ff057
	break;
2ff057
    }
2ff057
    default:
2ff057
	break;
2ff057
    }
2ff057
2ff057
exit:
2ff057
    return mi;
2ff057
}
2ff057
2ff057
/*
2ff057
 * Initialize db iterator with optional filters.  By default patterns
2ff057
 * applied to package name, others can be specified with <tagname>=<pattern>
2ff057
 */
2ff057
static rpmdbMatchIterator initFilterIterator(rpmts ts, ARGV_const_t argv)
2ff057
{
2ff057
    rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
2ff057
2ff057
    for (ARGV_const_t arg = argv; arg && *arg != NULL; arg++) {
2ff057
	rpmTagVal tag = RPMTAG_NAME;
2ff057
	char a[strlen(*arg)+1], *ae;
2ff057
	const char *pat = a;
2ff057
2ff057
	strcpy(a, *arg);
2ff057
2ff057
	/* Parse for "tag=pattern" args. */
2ff057
	if ((ae = strchr(a, '=')) != NULL) {
2ff057
	    *ae++ = '\0';
2ff057
	    tag = rpmTagGetValue(a);
2ff057
	    if (tag == RPMTAG_NOT_FOUND) {
2ff057
		rpmlog(RPMLOG_ERR, _("unknown tag: \"%s\"\n"), a);
2ff057
		mi = rpmdbFreeIterator(mi);
2ff057
		break;
2ff057
	    }
2ff057
	    pat = ae;
2ff057
	}
2ff057
2ff057
	rpmdbSetIteratorRE(mi, tag, RPMMIRE_DEFAULT, pat);
2ff057
    }
2ff057
2ff057
    return mi;
2ff057
}
2ff057
2ff057
int rpmcliArgIter(rpmts ts, QVA_t qva, ARGV_const_t argv)
2ff057
{
2ff057
    int ec = 0;
2ff057
2ff057
    switch (qva->qva_source) {
2ff057
    case RPMQV_ALL: {
2ff057
	rpmdbMatchIterator mi = initFilterIterator(ts, argv);
2ff057
	ec = rpmcliShowMatches(qva, ts, mi);
2ff057
	rpmdbFreeIterator(mi);
2ff057
	break;
2ff057
    }
2ff057
    case RPMQV_RPM: {
2ff057
	rpmgi gi = rpmgiNew(ts, giFlags, argv);
2ff057
	ec = rpmgiShowMatches(qva, ts, gi);
2ff057
	rpmgiFree(gi);
2ff057
	break;
2ff057
    }
2ff057
    case RPMQV_SPECRPMS:
2ff057
    case RPMQV_SPECBUILTRPMS:
2ff057
    case RPMQV_SPECSRPM:
2ff057
	for (ARGV_const_t arg = argv; arg && *arg; arg++) {
2ff057
	    ec += ((qva->qva_specQuery != NULL)
2ff057
		    ? qva->qva_specQuery(ts, qva, *arg) : 1);
2ff057
	}
2ff057
	break;
2ff057
    default:
2ff057
	for (ARGV_const_t arg = argv; arg && *arg; arg++) {
2ff057
	    rpmdbMatchIterator mi = initQueryIterator(qva, ts, *arg);
2ff057
	    ec += rpmcliShowMatches(qva, ts, mi);
2ff057
	    if (mi == NULL && qva->qva_source == RPMQV_PACKAGE) {
2ff057
		size_t l = strlen(*arg);
2ff057
		if (l > 4 && !strcmp(*arg + l - 4, ".rpm")) {
2ff057
		    rpmgi gi = rpmgiNew(ts, giFlags, argv);
2ff057
		    ec += rpmgiShowMatches(qva, ts, gi);
2ff057
		    rpmgiFree(gi);
2ff057
		}
2ff057
	    }
2ff057
	    rpmdbFreeIterator(mi);
2ff057
	}
2ff057
	break;
2ff057
    }
2ff057
2ff057
    return ec;
2ff057
}
2ff057
2ff057
int rpmcliQuery(rpmts ts, QVA_t qva, char * const * argv)
2ff057
{
2ff057
    rpmVSFlags vsflags, ovsflags;
2ff057
    int ec = 0;
2ff057
2ff057
    if (qva->qva_showPackage == NULL)
2ff057
	qva->qva_showPackage = showQueryPackage;
2ff057
2ff057
    /* If --queryformat unspecified, then set default now. */
2ff057
    if (!(qva->qva_flags & _QUERY_FOR_BITS) && qva->qva_queryFormat == NULL) {
2ff057
	char * fmt = rpmExpand("%{?_query_all_fmt}\n", NULL);
2ff057
	if (fmt == NULL || strlen(fmt) <= 1) {
2ff057
	    free(fmt);
2ff057
	    fmt = xstrdup("%{nvra}\n");
2ff057
	}
2ff057
	qva->qva_queryFormat = fmt;
2ff057
    }
2ff057
2ff057
    vsflags = rpmExpandNumeric("%{?_vsflags_query}");
2ff057
    if (rpmcliQueryFlags & VERIFY_DIGEST)
2ff057
	vsflags |= _RPMVSF_NODIGESTS;
2ff057
    if (rpmcliQueryFlags & VERIFY_SIGNATURE)
2ff057
	vsflags |= _RPMVSF_NOSIGNATURES;
2ff057
    if (rpmcliQueryFlags & VERIFY_HDRCHK)
2ff057
	vsflags |= RPMVSF_NOHDRCHK;
2ff057
    vsflags |= rpmcliVSFlags;
2ff057
2ff057
    ovsflags = rpmtsSetVSFlags(ts, vsflags);
2ff057
    ec = rpmcliArgIter(ts, qva, argv);
2ff057
    rpmtsSetVSFlags(ts, ovsflags);
2ff057
2ff057
    if (qva->qva_showPackage == showQueryPackage)
2ff057
	qva->qva_showPackage = NULL;
2ff057
2ff057
    return ec;
2ff057
}