Blame lib/tagexts.c

2ff057
/** \ingroup header
2ff057
 * \file lib/formats.c
2ff057
 */
2ff057
2ff057
#include "system.h"
2ff057
2ff057
#include <rpm/rpmtypes.h>
2ff057
#include <rpm/rpmlib.h>
2ff057
#include <rpm/rpmmacro.h>	/* XXX for %_i18ndomains */
2ff057
#include <rpm/rpmfi.h>
2ff057
#include <rpm/rpmstring.h>
2ff057
#include <rpm/rpmlog.h>
2ff057
#include "lib/misc.h"		/* tag function proto */
2ff057
2ff057
#include "debug.h"
2ff057
2ff057
struct headerTagFunc_s {
2ff057
    rpmTag tag;		/*!< Tag of extension. */
2ff057
    headerTagTagFunction func;	/*!< Pointer to formatter function. */	
2ff057
};
2ff057
2ff057
/** \ingroup rpmfi
2ff057
 * Retrieve file names from header.
2ff057
 *
2ff057
 * The representation of file names in package headers changed in rpm-4.0.
2ff057
 * Originally, file names were stored as an array of absolute paths.
2ff057
 * In rpm-4.0, file names are stored as separate arrays of dirname's and
2ff057
 * basename's, * with a dirname index to associate the correct dirname
2ff057
 * with each basname.
2ff057
 *
2ff057
 * This function is used to retrieve file names independent of how the
2ff057
 * file names are represented in the package header.
2ff057
 * 
2ff057
 * @param h		header
2ff057
 * @param tagN		RPMTAG_BASENAMES | PMTAG_ORIGBASENAMES
2ff057
 * @param withstate	take file state into account?
2ff057
 * @retval td		tag data container
2ff057
 * @return		1 on success
2ff057
 */
2ff057
static int fnTag(Header h, rpmTag tagN, int withstate, rpmtd td)
2ff057
{
2ff057
    const char **baseNames, **dirNames;
2ff057
    const char *fileStates = NULL;
2ff057
    uint32_t *dirIndexes;
2ff057
    rpm_count_t count, retcount, dncount;
2ff057
    size_t size = 0;
2ff057
    rpmTag dirNameTag = RPMTAG_DIRNAMES;
2ff057
    rpmTag dirIndexesTag = RPMTAG_DIRINDEXES;
2ff057
    int i, j;
2ff057
    int rc = 0; /* assume failure */
2ff057
    struct rpmtd_s bnames, dnames, dixs, fstates;
2ff057
2ff057
    if (tagN == RPMTAG_ORIGBASENAMES) {
2ff057
	dirNameTag = RPMTAG_ORIGDIRNAMES;
2ff057
	dirIndexesTag = RPMTAG_ORIGDIRINDEXES;
2ff057
    }
2ff057
2ff057
    if (!headerGet(h, tagN, &bnames, HEADERGET_MINMEM)) {
2ff057
	return 0;		/* no file list */
2ff057
    }
2ff057
2ff057
    (void) headerGet(h, dirNameTag, &dnames, HEADERGET_MINMEM);
2ff057
    (void) headerGet(h, dirIndexesTag, &dixs, HEADERGET_MINMEM);
2ff057
2ff057
    retcount = count = rpmtdCount(&bnames);
2ff057
    dncount = rpmtdCount(&dnames);
2ff057
2ff057
    /* Basic sanity checking for our interrelated tags  */
2ff057
    if (rpmtdCount(&dixs) != count || dncount < 1 || dncount > count)
2ff057
	td->flags |= RPMTD_INVALID;
2ff057
2ff057
    if (withstate) {
2ff057
	/* no recorded states means no installed files */
2ff057
	if (!headerGet(h, RPMTAG_FILESTATES, &fstates, HEADERGET_MINMEM))
2ff057
	    goto exit;
2ff057
	if (rpmtdCount(&fstates) != count)
2ff057
	    td->flags |= RPMTD_INVALID;
2ff057
	fileStates = fstates.data;
2ff057
    }
2ff057
2ff057
    if (td->flags & RPMTD_INVALID)
2ff057
	goto exit;
2ff057
2ff057
    baseNames = bnames.data;
2ff057
    dirNames = dnames.data;
2ff057
    dirIndexes = dixs.data;
2ff057
2ff057
    /*
2ff057
     * fsm, psm and rpmfi assume the data is stored in a single allocation
2ff057
     * block, until those assumptions are removed we need to jump through
2ff057
     * a few hoops here and precalculate sizes etc
2ff057
     */
2ff057
    for (i = 0; i < count; i++) {
2ff057
	if (fileStates && !RPMFILE_IS_INSTALLED(fileStates[i])) {
2ff057
	    retcount--;
2ff057
	    continue;
2ff057
	}
2ff057
	/* Sanity check  directory indexes are within bounds */
2ff057
	if (dirIndexes[i] >= dncount) {
2ff057
	    td->flags |= RPMTD_INVALID;
2ff057
	    break;
2ff057
	}
2ff057
	size += strlen(baseNames[i]) + strlen(dirNames[dirIndexes[i]]) + 1;
2ff057
    }
2ff057
2ff057
    if (!(td->flags & RPMTD_INVALID)) {
2ff057
	char **fileNames = xmalloc(size + (sizeof(*fileNames) * retcount));
2ff057
	char *t = ((char *) fileNames) + (sizeof(*fileNames) * retcount);
2ff057
	for (i = 0, j = 0; i < count; i++) {
2ff057
	    if (fileStates && !RPMFILE_IS_INSTALLED(fileStates[i]))
2ff057
		continue;
2ff057
	    fileNames[j++] = t;
2ff057
	    t = stpcpy( stpcpy(t, dirNames[dirIndexes[i]]), baseNames[i]);
2ff057
	    *t++ = '\0';
2ff057
	}
2ff057
2ff057
	td->data = fileNames;
2ff057
	td->count = retcount;
2ff057
	td->type = RPM_STRING_ARRAY_TYPE;
2ff057
	td->flags |= RPMTD_ALLOCED;
2ff057
	rc = 1;
2ff057
    }
2ff057
2ff057
exit:
2ff057
    rpmtdFreeData(&bnames);
2ff057
    rpmtdFreeData(&dnames);
2ff057
    rpmtdFreeData(&dixs);
2ff057
    /* only safe if the headerGet() on file states was actually called */
2ff057
    if (fileStates)
2ff057
	rpmtdFreeData(&fstates);
2ff057
2ff057
    return rc;
2ff057
}
2ff057
2ff057
static int filedepTag(Header h, rpmTag tagN, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    rpmfi fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, RPMFI_NOHEADER);
2ff057
    rpmds ds = NULL;
2ff057
    char **fdeps = NULL;
2ff057
    int numfiles;
2ff057
    char deptype = 'R';
2ff057
    int fileix;
2ff057
    int rc = 0;
2ff057
2ff057
    numfiles = rpmfiFC(fi);
2ff057
    if (numfiles <= 0) {
2ff057
	goto exit;
2ff057
    }
2ff057
2ff057
    if (tagN == RPMTAG_PROVIDENAME)
2ff057
	deptype = 'P';
2ff057
    else if (tagN == RPMTAG_REQUIRENAME)
2ff057
	deptype = 'R';
2ff057
2ff057
    ds = rpmdsNew(h, tagN, 0);
2ff057
    fdeps = xmalloc(numfiles * sizeof(*fdeps));
2ff057
2ff057
    while ((fileix = rpmfiNext(fi)) >= 0) {
2ff057
	ARGV_t deps = NULL;
2ff057
	const uint32_t * ddict = NULL;
2ff057
	int ndx = rpmfiFDepends(fi, &ddict);
2ff057
	if (ddict != NULL) {
2ff057
	    while (ndx-- > 0) {
2ff057
		const char * DNEVR;
2ff057
		unsigned dix = *ddict++;
2ff057
		char mydt = ((dix >> 24) & 0xff);
2ff057
		if (mydt != deptype)
2ff057
		    continue;
2ff057
		dix &= 0x00ffffff;
2ff057
		(void) rpmdsSetIx(ds, dix-1);
2ff057
		if (rpmdsNext(ds) < 0)
2ff057
		    continue;
2ff057
		DNEVR = rpmdsDNEVR(ds);
2ff057
		if (DNEVR != NULL) {
2ff057
		    argvAdd(&deps, DNEVR + 2);
2ff057
		}
2ff057
	    }
2ff057
	}
2ff057
	fdeps[fileix] = deps ? argvJoin(deps, " ") : xstrdup("");
2ff057
	argvFree(deps);
2ff057
    }
2ff057
    td->data = fdeps;
2ff057
    td->count = numfiles;
2ff057
    td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED;
2ff057
    td->type = RPM_STRING_ARRAY_TYPE;
2ff057
    rc = 1;
2ff057
2ff057
exit:
2ff057
    rpmfiFree(fi);
2ff057
    rpmdsFree(ds);
2ff057
    return rc;
2ff057
}
2ff057
2ff057
typedef enum tMode_e {
2ff057
    NORMALTRIGGER     = 0,
2ff057
    FILETRIGGER       = 1,
2ff057
    TRANSFILETRIGGER  = 2,
2ff057
} tMode;
2ff057
2ff057
/**
2ff057
 * Retrieve trigger info.
2ff057
 * @param mode		type of trigger (see tMode_e)
2ff057
 * @param h		header
2ff057
 * @retval td		tag data container
2ff057
 * @param hgflags	header get flags
2ff057
 * @return		1 on success
2ff057
 */
2ff057
static int triggercondsTagFor(tMode mode, Header h, rpmtd td,
2ff057
				headerGetFlags hgflags)
2ff057
{
2ff057
    uint32_t * indices;
2ff057
    int i, j;
2ff057
    char ** conds;
2ff057
    struct rpmtd_s nametd, indextd, flagtd, versiontd, scripttd;
2ff057
    int hgeflags = HEADERGET_MINMEM;
2ff057
    rpmTagVal triggername, triggerindex, triggerflags;
2ff057
    rpmTagVal triggerversion, triggerscripts;
2ff057
2ff057
    switch (mode) {
2ff057
	case NORMALTRIGGER:
2ff057
	    triggername = RPMTAG_TRIGGERNAME;
2ff057
	    triggerindex = RPMTAG_TRIGGERINDEX;
2ff057
	    triggerflags = RPMTAG_TRIGGERFLAGS;
2ff057
	    triggerversion = RPMTAG_TRIGGERVERSION;
2ff057
	    triggerscripts = RPMTAG_TRIGGERSCRIPTS;
2ff057
	    break;
2ff057
	case FILETRIGGER:
2ff057
	    triggername = RPMTAG_FILETRIGGERNAME;
2ff057
	    triggerindex = RPMTAG_FILETRIGGERINDEX;
2ff057
	    triggerflags = RPMTAG_FILETRIGGERFLAGS;
2ff057
	    triggerversion = RPMTAG_FILETRIGGERVERSION;
2ff057
	    triggerscripts = RPMTAG_FILETRIGGERSCRIPTS;
2ff057
	    break;
2ff057
	case TRANSFILETRIGGER:
2ff057
	    triggername = RPMTAG_TRANSFILETRIGGERNAME;
2ff057
	    triggerindex = RPMTAG_TRANSFILETRIGGERINDEX;
2ff057
	    triggerflags = RPMTAG_TRANSFILETRIGGERFLAGS;
2ff057
	    triggerversion = RPMTAG_TRANSFILETRIGGERVERSION;
2ff057
	    triggerscripts = RPMTAG_TRANSFILETRIGGERSCRIPTS;
2ff057
	    break;
2ff057
	default:
2ff057
	    return 0;
2ff057
    }
2ff057
2ff057
    if (!headerGet(h, triggername, &nametd, hgeflags)) {
2ff057
	return 0;
2ff057
    }
2ff057
2ff057
    headerGet(h, triggerindex, &indextd, hgeflags);
2ff057
    headerGet(h, triggerflags, &flagtd, hgeflags);
2ff057
    headerGet(h, triggerversion, &versiontd, hgeflags);
2ff057
    headerGet(h, triggerscripts, &scripttd, hgeflags);
2ff057
2ff057
    td->type = RPM_STRING_ARRAY_TYPE;
2ff057
    td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED;
2ff057
    td->data = conds = xmalloc(sizeof(*conds) * rpmtdCount(&scripttd));
2ff057
    td->count = rpmtdCount(&scripttd);
2ff057
2ff057
    indices = indextd.data;
2ff057
2ff057
    while ((i = rpmtdNext(&scripttd)) >= 0) {
2ff057
	rpm_flag_t *flag;
2ff057
	char *flagStr, *item;
2ff057
	ARGV_t items = NULL;
2ff057
2ff057
	rpmtdInit(&nametd); rpmtdInit(&flagtd); rpmtdInit(&versiontd);
2ff057
	while ((j = rpmtdNext(&nametd)) >= 0) {
2ff057
	    /* flag and version arrays match name array size always */
2ff057
	    rpmtdNext(&flagtd); rpmtdNext(&versiontd);
2ff057
2ff057
	    if (indices[j] != i)
2ff057
		continue;
2ff057
2ff057
	    flag = rpmtdGetUint32(&flagtd);
2ff057
	    if (flag && *flag & RPMSENSE_SENSEMASK) {
2ff057
		flagStr = rpmtdFormat(&flagtd, RPMTD_FORMAT_DEPFLAGS, NULL);
2ff057
		rasprintf(&item, "%s %s %s", rpmtdGetString(&nametd),
2ff057
					     flagStr,
2ff057
					     rpmtdGetString(&versiontd));
2ff057
		free(flagStr);
2ff057
	    } else {
2ff057
		item = xstrdup(rpmtdGetString(&nametd));
2ff057
	    }
2ff057
2ff057
	    argvAdd(&items, item);
2ff057
	    free(item);
2ff057
	}
2ff057
2ff057
	conds[i] = argvJoin(items, ", ");
2ff057
	argvFree(items);
2ff057
    }
2ff057
2ff057
    rpmtdFreeData(&nametd);
2ff057
    rpmtdFreeData(&versiontd);
2ff057
    rpmtdFreeData(&flagtd);
2ff057
    rpmtdFreeData(&indextd);
2ff057
    rpmtdFreeData(&scripttd);
2ff057
    return 1;
2ff057
}
2ff057
2ff057
static int triggercondsTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return triggercondsTagFor(NORMALTRIGGER, h, td, hgflags);
2ff057
}
2ff057
2ff057
static int filetriggercondsTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return triggercondsTagFor(FILETRIGGER, h, td, hgflags);
2ff057
}
2ff057
2ff057
static int transfiletriggercondsTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return triggercondsTagFor(TRANSFILETRIGGER, h, td, hgflags);
2ff057
}
2ff057
2ff057
/**
2ff057
 * Retrieve trigger type info.
2ff057
 * @param mode		type of trigger (see tMode_e)
2ff057
 * @param h		header
2ff057
 * @retval td		tag data container
2ff057
 * @param hgflags	header get flags
2ff057
 * @return		1 on success
2ff057
 */
2ff057
static int triggertypeTagFor(tMode mode, Header h, rpmtd td,
2ff057
				headerGetFlags hgflags)
2ff057
{
2ff057
    int i;
2ff057
    char ** conds;
2ff057
    struct rpmtd_s indices, flags, scripts;
2ff057
    rpmTagVal triggerindex, triggerflags, triggerscripts;
2ff057
2ff057
    switch (mode) {
2ff057
	case NORMALTRIGGER:
2ff057
	    triggerindex = RPMTAG_TRIGGERINDEX;
2ff057
	    triggerflags = RPMTAG_TRIGGERFLAGS;
2ff057
	    triggerscripts = RPMTAG_TRIGGERSCRIPTS;
2ff057
	    break;
2ff057
	case FILETRIGGER:
2ff057
	    triggerindex = RPMTAG_FILETRIGGERINDEX;
2ff057
	    triggerflags = RPMTAG_FILETRIGGERFLAGS;
2ff057
	    triggerscripts = RPMTAG_FILETRIGGERSCRIPTS;
2ff057
	    break;
2ff057
	case TRANSFILETRIGGER:
2ff057
	    triggerindex = RPMTAG_TRANSFILETRIGGERINDEX;
2ff057
	    triggerflags = RPMTAG_TRANSFILETRIGGERFLAGS;
2ff057
	    triggerscripts = RPMTAG_TRANSFILETRIGGERSCRIPTS;
2ff057
	    break;
2ff057
	default:
2ff057
	   return 0;
2ff057
    }
2ff057
2ff057
    if (!headerGet(h, triggerindex, &indices, HEADERGET_MINMEM)) {
2ff057
	return 0;
2ff057
    }
2ff057
2ff057
    headerGet(h, triggerflags, &flags, HEADERGET_MINMEM);
2ff057
    headerGet(h, triggerscripts, &scripts, HEADERGET_MINMEM);
2ff057
2ff057
    td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED;
2ff057
    td->count = rpmtdCount(&scripts);
2ff057
    td->data = conds = xmalloc(sizeof(*conds) * td->count);
2ff057
    td->type = RPM_STRING_ARRAY_TYPE;
2ff057
2ff057
    while ((i = rpmtdNext(&scripts)) >= 0) {
2ff057
	rpm_flag_t *flag;
2ff057
	rpmtdInit(&indices); rpmtdInit(&flags);
2ff057
2ff057
	while (rpmtdNext(&indices) >= 0 && rpmtdNext(&flags) >= 0) {
2ff057
	    if (*rpmtdGetUint32(&indices) != i) 
2ff057
		continue;
2ff057
2ff057
	    flag = rpmtdGetUint32(&flags);
2ff057
	    if (*flag & RPMSENSE_TRIGGERPREIN)
2ff057
		conds[i] = xstrdup("prein");
2ff057
	    else if (*flag & RPMSENSE_TRIGGERIN)
2ff057
		conds[i] = xstrdup("in");
2ff057
	    else if (*flag & RPMSENSE_TRIGGERUN)
2ff057
		conds[i] = xstrdup("un");
2ff057
	    else if (*flag & RPMSENSE_TRIGGERPOSTUN)
2ff057
		conds[i] = xstrdup("postun");
2ff057
	    else
2ff057
		conds[i] = xstrdup("");
2ff057
	    break;
2ff057
	}
2ff057
    }
2ff057
    rpmtdFreeData(&indices);
2ff057
    rpmtdFreeData(&flags);
2ff057
    rpmtdFreeData(&scripts);
2ff057
2ff057
    return 1;
2ff057
}
2ff057
2ff057
static int triggertypeTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return triggertypeTagFor(NORMALTRIGGER, h, td, hgflags);
2ff057
}
2ff057
2ff057
static int filetriggertypeTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return triggertypeTagFor(FILETRIGGER, h, td, hgflags);
2ff057
}
2ff057
2ff057
static int transfiletriggertypeTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return triggertypeTagFor(TRANSFILETRIGGER, h, td, hgflags);
2ff057
}
2ff057
2ff057
/**
2ff057
 * Retrieve installed file paths.
2ff057
 * @param h		header
2ff057
 * @retval td		tag data container
2ff057
 * @param hgflags	header get flags
2ff057
 * @return		1 on success
2ff057
 */
2ff057
static int instfilenamesTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return fnTag(h, RPMTAG_BASENAMES, 1, td);
2ff057
}
2ff057
2ff057
/**
2ff057
 * Retrieve file paths.
2ff057
 * @param h		header
2ff057
 * @retval td		tag data container
2ff057
 * @param hgflags	header get flags
2ff057
 * @return		1 on success
2ff057
 */
2ff057
static int filenamesTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return fnTag(h, RPMTAG_BASENAMES, 0, td);
2ff057
}
2ff057
2ff057
/**
2ff057
 * Retrieve original file paths (wrt relocation).
2ff057
 * @param h		header
2ff057
 * @retval td		tag data container
2ff057
 * @param hgflags	header get flags
2ff057
 * @return		1 on success
2ff057
 */
2ff057
static int origfilenamesTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return fnTag(h, RPMTAG_ORIGBASENAMES, 0, td);
2ff057
}
2ff057
2ff057
/*
2ff057
 * Attempt to generate libmagic-style file class if missing from header:
2ff057
 * we can easily generate this for symlinks and other special types.
2ff057
 * Always return malloced strings to simplify life in fileclassTag().
2ff057
 */
2ff057
static char *makeFClass(rpmfi fi)
2ff057
{
2ff057
    char *fclass = NULL;
2ff057
    const char *hc = rpmfiFClass(fi);
2ff057
2ff057
    if (hc != NULL && hc[0] != '\0') {
2ff057
	fclass = xstrdup(hc);
2ff057
    } else {
2ff057
	switch (rpmfiFMode(fi) & S_IFMT) {
2ff057
	case S_IFBLK:
2ff057
	    fclass = xstrdup("block special");
2ff057
	    break;
2ff057
	case S_IFCHR:
2ff057
	    fclass = xstrdup("character special");
2ff057
	    break;
2ff057
	case S_IFDIR:
2ff057
	    fclass = xstrdup("directory");
2ff057
	    break;
2ff057
	case S_IFIFO:
2ff057
	    fclass = xstrdup("fifo (named pipe)");
2ff057
	    break;
2ff057
	case S_IFSOCK:
2ff057
	    fclass = xstrdup("socket");
2ff057
	    break;
2ff057
	case S_IFLNK:
2ff057
	    fclass = rstrscat(NULL, "symbolic link to `",
2ff057
			      rpmfiFLink(fi), "'", NULL);
2ff057
	    break;
2ff057
	}
2ff057
    }
2ff057
2ff057
    return (fclass != NULL) ? fclass : xstrdup("");
2ff057
}
2ff057
2ff057
/**
2ff057
 * Retrieve/generate file classes.
2ff057
 * @param h		header
2ff057
 * @retval td		tag data container
2ff057
 * @param hgflags	header get flags
2ff057
 * @return		1 on success
2ff057
 */
2ff057
static int fileclassTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    rpmfi fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, RPMFI_NOHEADER);
2ff057
    int numfiles = rpmfiFC(fi);
2ff057
2ff057
    if (numfiles > 0) {
2ff057
	char **fclasses = xmalloc(numfiles * sizeof(*fclasses));
2ff057
	int ix;
2ff057
2ff057
	rpmfiInit(fi, 0);
2ff057
	while ((ix = rpmfiNext(fi)) >= 0) {
2ff057
	    fclasses[ix] = makeFClass(fi);
2ff057
	}
2ff057
2ff057
	td->data = fclasses;
2ff057
	td->count = numfiles;
2ff057
	td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED;
2ff057
	td->type = RPM_STRING_ARRAY_TYPE;
2ff057
    }
2ff057
2ff057
    rpmfiFree(fi);
2ff057
    return (numfiles > 0); 
2ff057
}
2ff057
2ff057
/**
2ff057
 * Retrieve file provides.
2ff057
 * @param h		header
2ff057
 * @retval td		tag data container
2ff057
 * @param hgflags	header get flags
2ff057
 * @return		1 on success
2ff057
 */
2ff057
static int fileprovideTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return filedepTag(h, RPMTAG_PROVIDENAME, td, hgflags);
2ff057
}
2ff057
2ff057
/**
2ff057
 * Retrieve file requires.
2ff057
 * @param h		header
2ff057
 * @retval td		tag data container
2ff057
 * @param hgflags	header get flags
2ff057
 * @return		1 on success
2ff057
 */
2ff057
static int filerequireTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return filedepTag(h, RPMTAG_REQUIRENAME, td, hgflags);
2ff057
}
2ff057
2ff057
/**
2ff057
 * Retrieve i18n text.
2ff057
 * @param h		header
2ff057
 * @param tag		tag
2ff057
 * @retval td		tag data container
2ff057
 * @param hgflags	header get flags
2ff057
 * @return		1 on success
2ff057
 */
2ff057
static int i18nTag(Header h, rpmTag tag, rpmtd td, headerGetFlags hgflags)
2ff057
{
Packit Service 4d7b66
    int rc = headerGet(h, tag, td, HEADERGET_ALLOC);
2ff057
#if defined(ENABLE_NLS)
Packit Service 4d7b66
    if (rc) {
Packit Service 4d7b66
	static const char * const _macro_i18ndomains = "%{?_i18ndomains}";
Packit Service 4d7b66
	char *de, *dstring = rpmExpand(_macro_i18ndomains, NULL);
Packit Service 4d7b66
	const char *domain;
Packit Service 2eebcb
Packit Service e9a89b
	for (domain = dstring; domain != NULL; domain = de) {
Packit Service 4d7b66
	    const char *msgid = td->data;
Packit Service 4d7b66
	    const char *msg = NULL;
Packit Service 4d7b66
2ff057
	    de = strchr(domain, ':');
2ff057
	    if (de) *de++ = '\0';
Packit Service 4d7b66
	    msg = dgettext(domain, td->data);
Packit Service 4d7b66
	    if (msg != msgid) {
Packit Service 4d7b66
		free(td->data);
Packit Service 4d7b66
		td->data = xstrdup(msg);
Packit Service 4d7b66
		break;
Packit Service 4d7b66
	    }
Packit Service e9a89b
	}
Packit Service 4d7b66
	free(dstring);
2ff057
    }
2ff057
#endif
2ff057
2ff057
    return rc;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Retrieve summary text.
2ff057
 * @param h		header
2ff057
 * @retval td		tag data container
2ff057
 * @param hgflags	header get flags
2ff057
 * @return		1 on success
2ff057
 */
2ff057
static int summaryTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return i18nTag(h, RPMTAG_SUMMARY, td, hgflags);
2ff057
}
2ff057
2ff057
/**
2ff057
 * Retrieve description text.
2ff057
 * @param h		header
2ff057
 * @retval td		tag data container
2ff057
 * @param hgflags	header get flags
2ff057
 * @return		1 on success
2ff057
 */
2ff057
static int descriptionTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return i18nTag(h, RPMTAG_DESCRIPTION, td, hgflags);
2ff057
}
2ff057
2ff057
/**
2ff057
 * Retrieve group text.
2ff057
 * @param h		header
2ff057
 * @retval td		tag data container
2ff057
 * @param hgflags	header get flags
2ff057
 * @return		1 on success
2ff057
 */
2ff057
static int groupTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return i18nTag(h, RPMTAG_GROUP, td, hgflags);
2ff057
}
2ff057
2ff057
/*
2ff057
 * Helper to convert 32bit tag to 64bit version.
2ff057
 * If header has new 64bit tag then just return the data,
2ff057
 * otherwise convert 32bit old tag data to 64bit values.
2ff057
 * For consistency, always return malloced data.
2ff057
 */
2ff057
static int get64(Header h, rpmtd td, rpmTag newtag, rpmTag oldtag)
2ff057
{
2ff057
    int rc;
2ff057
2ff057
    if (headerIsEntry(h, newtag)) {
2ff057
	rc = headerGet(h, newtag, td, HEADERGET_ALLOC);
2ff057
    } else {
2ff057
	struct rpmtd_s olddata;
2ff057
	uint32_t *d32 = NULL;
2ff057
	uint64_t *d64 = NULL;
2ff057
2ff057
	headerGet(h, oldtag, &olddata, HEADERGET_MINMEM);
2ff057
	if (rpmtdType(&olddata) == RPM_INT32_TYPE) {
2ff057
	    td->type = RPM_INT64_TYPE;
2ff057
	    td->count = olddata.count;
2ff057
	    td->flags = RPMTD_ALLOCED;
2ff057
	    td->data = xmalloc(sizeof(*d64) * td->count);
2ff057
	    d64 = td->data;
2ff057
	    while ((d32 = rpmtdNextUint32(&olddata))) {
2ff057
		*d64++ = *d32;
2ff057
	    }
2ff057
	} 
2ff057
	rpmtdFreeData(&olddata);
2ff057
	rc = d64 ? 1 : 0;
2ff057
    }
2ff057
2ff057
    return rc;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Retrieve file sizes as 64bit regardless of how they're stored.
2ff057
 * @param h		header
2ff057
 * @retval td		tag data container
2ff057
 * @param hgflags	header get flags
2ff057
 * @return		1 on success
2ff057
 */
2ff057
static int longfilesizesTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return get64(h, td, RPMTAG_LONGFILESIZES, RPMTAG_FILESIZES);
2ff057
}
2ff057
2ff057
static int longarchivesizeTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return get64(h, td, RPMTAG_LONGARCHIVESIZE, RPMTAG_ARCHIVESIZE);
2ff057
}
2ff057
2ff057
static int longsizeTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return get64(h, td, RPMTAG_LONGSIZE, RPMTAG_SIZE);
2ff057
}
2ff057
2ff057
static int longsigsizeTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return get64(h, td, RPMTAG_LONGSIGSIZE, RPMTAG_SIGSIZE);
2ff057
}
2ff057
2ff057
static int numberTag(rpmtd td, uint32_t val)
2ff057
{
2ff057
    uint32_t *tval = xmalloc(sizeof(*tval));
2ff057
2ff057
    tval[0] = val;
2ff057
    td->type = RPM_INT32_TYPE;
2ff057
    td->count = 1;
2ff057
    td->data = tval;
2ff057
    td->flags = RPMTD_ALLOCED;
2ff057
    return 1; /* this cannot fail */
2ff057
}
2ff057
2ff057
static int dbinstanceTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return numberTag(td, headerGetInstance(h));
2ff057
}
2ff057
2ff057
static int headercolorTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    rpm_color_t *fcolor, hcolor = 0;
2ff057
    struct rpmtd_s fcolors;
2ff057
2ff057
    headerGet(h, RPMTAG_FILECOLORS, &fcolors, HEADERGET_MINMEM);
2ff057
    while ((fcolor = rpmtdNextUint32(&fcolors)) != NULL) {
2ff057
	hcolor |= *fcolor;
2ff057
    }
2ff057
    rpmtdFreeData(&fcolors);
2ff057
    hcolor &= 0x0f;
2ff057
2ff057
    return numberTag(td, hcolor);
2ff057
}
2ff057
2ff057
enum nevraFlags_e {
2ff057
    NEVRA_NAME		= (1 << 0),
2ff057
    NEVRA_EPOCH		= (1 << 1),
2ff057
    NEVRA_VERSION	= (1 << 2),
2ff057
    NEVRA_RELEASE	= (1 << 3),
2ff057
    NEVRA_ARCH		= (1 << 4)
2ff057
};
2ff057
typedef rpmFlags nevraFlags;
2ff057
2ff057
static int getNEVRA(Header h, rpmtd td, nevraFlags flags)
2ff057
{
2ff057
    const char *val = NULL;
2ff057
    char *res = xstrdup("");
2ff057
2ff057
    if ((flags & NEVRA_NAME)) {
2ff057
	val = headerGetString(h, RPMTAG_NAME);
2ff057
	if (val) rstrscat(&res, val, "-", NULL);
2ff057
    }
2ff057
    if ((flags & NEVRA_EPOCH)) {
2ff057
	char *e = headerGetAsString(h, RPMTAG_EPOCH);
2ff057
	if (e) rstrscat(&res, e, ":", NULL);
2ff057
	free(e);
2ff057
    }
2ff057
    if ((flags & NEVRA_VERSION)) {
2ff057
	val = headerGetString(h, RPMTAG_VERSION);
2ff057
	if (val) rstrscat(&res, val, "-", NULL);
2ff057
    }
2ff057
    if ((flags & NEVRA_RELEASE)) {
2ff057
	val = headerGetString(h, RPMTAG_RELEASE);
2ff057
	if (val) rstrscat(&res, val, NULL);
2ff057
    }
2ff057
    if ((flags & NEVRA_ARCH)) {
2ff057
	val = headerGetString(h, RPMTAG_ARCH);
2ff057
	if (headerIsSource(h) && val == NULL) val = "src";
2ff057
	if (val) rstrscat(&res, ".", val, NULL);
2ff057
    }
2ff057
2ff057
    td->type = RPM_STRING_TYPE;
2ff057
    td->data = res;
2ff057
    td->count = 1;
2ff057
    td->flags = RPMTD_ALLOCED;
2ff057
2ff057
    return 1;
2ff057
}
2ff057
2ff057
static int evrTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return getNEVRA(h, td, NEVRA_EPOCH|NEVRA_VERSION|NEVRA_RELEASE);
2ff057
}
2ff057
2ff057
static int nvrTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return getNEVRA(h, td, NEVRA_NAME|NEVRA_VERSION|NEVRA_RELEASE);
2ff057
}
2ff057
2ff057
static int nvraTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return getNEVRA(h, td, NEVRA_NAME|NEVRA_VERSION|NEVRA_RELEASE|NEVRA_ARCH);
2ff057
}
2ff057
2ff057
static int nevrTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return getNEVRA(h, td, NEVRA_NAME|NEVRA_EPOCH|NEVRA_VERSION|NEVRA_RELEASE);
2ff057
}
2ff057
2ff057
static int nevraTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return getNEVRA(h, td, NEVRA_NAME|NEVRA_EPOCH|NEVRA_VERSION|NEVRA_RELEASE|NEVRA_ARCH);
2ff057
}
2ff057
2ff057
static int verboseTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    if (rpmIsVerbose()) {
2ff057
	td->type = RPM_INT32_TYPE;
2ff057
	td->count = 1;
2ff057
	td->data = &(td->count);
2ff057
	td->flags = RPMTD_NONE;
2ff057
	return 1;
2ff057
    } else {
2ff057
	return 0;
2ff057
    }
2ff057
}
2ff057
2ff057
static int epochnumTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    /* For consistency, always return malloced data */
2ff057
    if (!headerGet(h, RPMTAG_EPOCH, td, HEADERGET_ALLOC)) {
2ff057
	uint32_t *e = malloc(sizeof(*e));
2ff057
	*e = 0;
2ff057
	td->data = e;
2ff057
	td->type = RPM_INT32_TYPE;
2ff057
	td->count = 1;
2ff057
	td->flags = RPMTD_ALLOCED;
2ff057
    }
2ff057
    td->tag = RPMTAG_EPOCHNUM;
2ff057
    return 1;
2ff057
}
2ff057
2ff057
static int depnevrsTag(Header h, rpmtd td, headerGetFlags hgflags,
2ff057
			rpmTagVal tag)
2ff057
{
2ff057
    rpmds ds = rpmdsNew(h, tag, 0);
2ff057
    int ndeps = rpmdsCount(ds);
2ff057
2ff057
    if (ndeps > 0) {
2ff057
	char **deps = xmalloc(sizeof(*deps) * ndeps);
2ff057
	int i;
2ff057
	while ((i = rpmdsNext(ds)) >= 0) {
2ff057
	    deps[i] = rpmdsNewDNEVR(NULL, ds);
2ff057
	}
2ff057
	td->data = deps;
2ff057
	td->type = RPM_STRING_ARRAY_TYPE;
2ff057
	td->count = ndeps;
2ff057
	td->flags |= (RPMTD_ALLOCED | RPMTD_PTR_ALLOCED);
2ff057
    }
2ff057
    rpmdsFree(ds);
2ff057
    return (ndeps > 0);
2ff057
}
2ff057
2ff057
#define RPMSENSE_STRONG (1 << 27)
2ff057
2ff057
static int depnevrsTagFiltered(Header h, rpmtd td, headerGetFlags hgflags,
2ff057
			rpmTagVal tag, int strong)
2ff057
{
2ff057
    rpmds ds = rpmdsNew(h, tag, 0);
2ff057
    int ndeps = rpmdsCount(ds);
2ff057
2ff057
    if (ndeps > 0) {
2ff057
	char **deps = xmalloc(sizeof(*deps) * ndeps);
2ff057
	ndeps = 0;
2ff057
	while (rpmdsNext(ds) >= 0) {
2ff057
	    if ((rpmdsFlags(ds) & RPMSENSE_STRONG) == (strong ? RPMSENSE_STRONG : 0))
2ff057
		deps[ndeps++] = rpmdsNewDNEVR(NULL, ds);
2ff057
	}
2ff057
	if (ndeps) {
2ff057
	    td->data = deps;
2ff057
	    td->type = RPM_STRING_ARRAY_TYPE;
2ff057
	    td->count = ndeps;
2ff057
	    td->flags |= (RPMTD_ALLOCED | RPMTD_PTR_ALLOCED);
2ff057
	} else {
2ff057
	    _free(deps);
2ff057
        }
2ff057
    }
2ff057
    rpmdsFree(ds);
2ff057
    return (ndeps > 0);
2ff057
}
2ff057
2ff057
static int requirenevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return depnevrsTag(h, td, hgflags, RPMTAG_REQUIRENAME);
2ff057
}
2ff057
2ff057
static int recommendnevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return depnevrsTag(h, td, hgflags, RPMTAG_RECOMMENDNAME) ||
2ff057
           depnevrsTagFiltered(h, td, hgflags, RPMTAG_OLDSUGGESTSNAME, 1);
2ff057
}
2ff057
2ff057
static int suggestnevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return depnevrsTag(h, td, hgflags, RPMTAG_SUGGESTNAME) ||
2ff057
           depnevrsTagFiltered(h, td, hgflags, RPMTAG_OLDSUGGESTSNAME, 0);
2ff057
}
2ff057
2ff057
static int supplementnevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return depnevrsTag(h, td, hgflags, RPMTAG_SUPPLEMENTNAME) ||
2ff057
           depnevrsTagFiltered(h, td, hgflags, RPMTAG_OLDENHANCESNAME, 1);
2ff057
}
2ff057
2ff057
static int enhancenevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return depnevrsTag(h, td, hgflags, RPMTAG_ENHANCENAME) ||
2ff057
           depnevrsTagFiltered(h, td, hgflags, RPMTAG_OLDENHANCESNAME, 0);
2ff057
}
2ff057
2ff057
static int providenevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return depnevrsTag(h, td, hgflags, RPMTAG_PROVIDENAME);
2ff057
}
2ff057
2ff057
static int obsoletenevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return depnevrsTag(h, td, hgflags, RPMTAG_OBSOLETENAME);
2ff057
}
2ff057
2ff057
static int conflictnevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    return depnevrsTag(h, td, hgflags, RPMTAG_CONFLICTNAME);
2ff057
}
2ff057
2ff057
static int filenlinksTag(Header h, rpmtd td, headerGetFlags hgflags)
2ff057
{
2ff057
    rpmfi fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, RPMFI_NOHEADER);
2ff057
    rpm_count_t fc = rpmfiFC(fi);
2ff057
2ff057
    if (fc > 0) {
2ff057
	uint32_t *nlinks = xmalloc(fc * sizeof(*nlinks));
2ff057
	int ix;
2ff057
	while ((ix = rpmfiNext(fi)) >= 0) {
2ff057
	    nlinks[ix] = rpmfiFNlink(fi);
2ff057
	}
2ff057
	td->data = nlinks;
2ff057
	td->type = RPM_INT32_TYPE;
2ff057
	td->count = fc;
2ff057
	td->flags = RPMTD_ALLOCED;
2ff057
    }
2ff057
2ff057
    rpmfiFree(fi);
2ff057
    return (fc > 0);
2ff057
}
2ff057
2ff057
static const struct headerTagFunc_s rpmHeaderTagExtensions[] = {
2ff057
    { RPMTAG_GROUP,		groupTag },
2ff057
    { RPMTAG_DESCRIPTION,	descriptionTag },
2ff057
    { RPMTAG_SUMMARY,		summaryTag },
2ff057
    { RPMTAG_FILECLASS,		fileclassTag },
2ff057
    { RPMTAG_FILENAMES,		filenamesTag },
2ff057
    { RPMTAG_ORIGFILENAMES,	origfilenamesTag },
2ff057
    { RPMTAG_FILEPROVIDE,	fileprovideTag },
2ff057
    { RPMTAG_FILEREQUIRE,	filerequireTag },
2ff057
    { RPMTAG_TRIGGERCONDS,	triggercondsTag },
2ff057
    { RPMTAG_FILETRIGGERCONDS,	filetriggercondsTag },
2ff057
    { RPMTAG_TRANSFILETRIGGERCONDS,	transfiletriggercondsTag },
2ff057
    { RPMTAG_TRIGGERTYPE,	triggertypeTag },
2ff057
    { RPMTAG_FILETRIGGERTYPE,	filetriggertypeTag },
2ff057
    { RPMTAG_TRANSFILETRIGGERTYPE,	transfiletriggertypeTag },
2ff057
    { RPMTAG_LONGFILESIZES,	longfilesizesTag },
2ff057
    { RPMTAG_LONGARCHIVESIZE,	longarchivesizeTag },
2ff057
    { RPMTAG_LONGSIZE,		longsizeTag },
2ff057
    { RPMTAG_LONGSIGSIZE,	longsigsizeTag },
2ff057
    { RPMTAG_DBINSTANCE,	dbinstanceTag },
2ff057
    { RPMTAG_EVR,		evrTag },
2ff057
    { RPMTAG_NVR,		nvrTag },
2ff057
    { RPMTAG_NEVR,		nevrTag },
2ff057
    { RPMTAG_NVRA,		nvraTag },
2ff057
    { RPMTAG_NEVRA,		nevraTag },
2ff057
    { RPMTAG_HEADERCOLOR,	headercolorTag },
2ff057
    { RPMTAG_VERBOSE,		verboseTag },
2ff057
    { RPMTAG_EPOCHNUM,		epochnumTag },
2ff057
    { RPMTAG_INSTFILENAMES,	instfilenamesTag },
2ff057
    { RPMTAG_REQUIRENEVRS,	requirenevrsTag },
2ff057
    { RPMTAG_RECOMMENDNEVRS,	recommendnevrsTag},
2ff057
    { RPMTAG_SUGGESTNEVRS,	suggestnevrsTag},
2ff057
    { RPMTAG_SUPPLEMENTNEVRS,	supplementnevrsTag},
2ff057
    { RPMTAG_ENHANCENEVRS,	enhancenevrsTag},
2ff057
    { RPMTAG_PROVIDENEVRS,	providenevrsTag },
2ff057
    { RPMTAG_OBSOLETENEVRS,	obsoletenevrsTag },
2ff057
    { RPMTAG_CONFLICTNEVRS,	conflictnevrsTag },
2ff057
    { RPMTAG_FILENLINKS,	filenlinksTag },
2ff057
    { 0, 			NULL }
2ff057
};
2ff057
2ff057
headerTagTagFunction rpmHeaderTagFunc(rpmTagVal tag)
2ff057
{
2ff057
    const struct headerTagFunc_s * ext;
2ff057
    headerTagTagFunction func = NULL;
2ff057
2ff057
    for (ext = rpmHeaderTagExtensions; ext->func != NULL; ext++) {
2ff057
	if (ext->tag == tag) {
2ff057
	    func = ext->func;
2ff057
	    break;
2ff057
	}
2ff057
    }
2ff057
    return func;
2ff057
}
2ff057