csomh / source-git / rpm

Forked from source-git/rpm 4 years ago
Clone
2ff057
/** \ingroup header
2ff057
 * \file lib/formats.c
2ff057
 */
2ff057
2ff057
#include "system.h"
2ff057
2ff057
#include <inttypes.h>
2ff057
2ff057
#include <rpm/rpmtypes.h>
2ff057
#include <rpm/rpmtd.h>
2ff057
#include <rpm/rpmds.h>
2ff057
#include <rpm/rpmfi.h>
2ff057
#include <rpm/rpmstring.h>
2ff057
#include <rpm/rpmmacro.h>
2ff057
#include <rpm/rpmbase64.h>
2ff057
2ff057
#include "rpmio/digest.h"
2ff057
#include "lib/manifest.h"
2ff057
#include "lib/misc.h"
2ff057
#include "lib/signature.h"
2ff057
2ff057
#include "debug.h"
2ff057
2ff057
#define RPM_ANY_CLASS 255
2ff057
2ff057
typedef char * (*headerTagFormatFunction) (rpmtd td, char **emsg);
2ff057
2ff057
/** \ingroup header
2ff057
 * Define header tag output formats.
2ff057
 */
2ff057
2ff057
struct headerFmt_s {
2ff057
    rpmtdFormats fmt;	/*!< Value of extension */
2ff057
    const char *name;	/*!< Name of extension. */
2ff057
    rpmTagClass class;	/*!< Class of source data (RPM_ANY_CLASS for any) */
2ff057
    headerTagFormatFunction func;	/*!< Pointer to formatter function. */	
2ff057
};
2ff057
2ff057
static const char *classEr(rpmTagClass class)
2ff057
{
2ff057
    switch (class) {
2ff057
    case RPM_BINARY_CLASS:	 return _("(not a blob)");
2ff057
    case RPM_NUMERIC_CLASS:	 return _("(not a number)");
2ff057
    case RPM_STRING_CLASS:	 return _("(not a string)");
2ff057
    default:			 break;
2ff057
    }
2ff057
    return _("(invalid type)");
2ff057
}
2ff057
2ff057
/* barebones string representation with no extra formatting */
2ff057
static char * stringFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    char *val = NULL;
2ff057
2ff057
    switch (rpmtdClass(td)) {
2ff057
	case RPM_NUMERIC_CLASS:
2ff057
	    rasprintf(&val, "%" PRIu64, rpmtdGetNumber(td));
2ff057
	    break;
2ff057
	case RPM_STRING_CLASS: {
2ff057
	    const char *str = rpmtdGetString(td);
2ff057
	    if (str)
2ff057
		val = xstrdup(str);
2ff057
	    break;
2ff057
	}
2ff057
	case RPM_BINARY_CLASS:
2ff057
	    val = pgpHexStr(td->data, td->count);
2ff057
	    break;
2ff057
	default:
2ff057
	    *emsg = xstrdup("(unknown type)");
2ff057
	    break;
2ff057
    }
2ff057
    return val;
2ff057
}
2ff057
2ff057
/* arbitrary number format as per format arg */
2ff057
static char * numFormat(rpmtd td, const char *format)
2ff057
{
2ff057
    char * val = NULL;
2ff057
    rasprintf(&val, format, rpmtdGetNumber(td));
2ff057
    return val;
2ff057
}
2ff057
2ff057
/* octal number formatting */
2ff057
static char * octalFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    return numFormat(td, "%o");
2ff057
}
2ff057
2ff057
/* hexadecimal format */
2ff057
static char * hexFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    return numFormat(td, "%x");
2ff057
}
2ff057
2ff057
/* arbitrary date formatting as per strftimeFormat arg */
2ff057
static char * realDateFormat(rpmtd td, const char * strftimeFormat, char **emsg)
2ff057
{
2ff057
    char * val = NULL;
2ff057
    struct tm * tstruct;
2ff057
    char buf[1024];
2ff057
    time_t dateint = rpmtdGetNumber(td);
2ff057
    tstruct = localtime(&dateint);
2ff057
2ff057
    buf[0] = '\0';
2ff057
    if (tstruct)
2ff057
	if (strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct) == 0)
2ff057
	    *emsg = xstrdup("date output too long");
2ff057
    val = xstrdup(buf);
2ff057
2ff057
    return val;
2ff057
}
2ff057
2ff057
/* date formatting */
2ff057
static char * dateFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    return realDateFormat(td, _("%c"), emsg);
2ff057
}
2ff057
2ff057
/* day formatting */
2ff057
static char * dayFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    return realDateFormat(td, _("%a %b %d %Y"), emsg);
2ff057
}
2ff057
2ff057
/* shell escape formatting */
2ff057
static char * shescapeFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    char * result = NULL, * dst, * src;
2ff057
2ff057
    if (rpmtdClass(td) == RPM_NUMERIC_CLASS) {
2ff057
	rasprintf(&result, "%" PRIu64, rpmtdGetNumber(td));
2ff057
    } else if (rpmtdClass(td) == RPM_STRING_CLASS) {
2ff057
	char *buf = xstrdup(rpmtdGetString(td));;
2ff057
2ff057
	result = dst = xmalloc(strlen(buf) * 4 + 3);
2ff057
	*dst++ = '\'';
2ff057
	for (src = buf; *src != '\0'; src++) {
2ff057
	    if (*src == '\'') {
2ff057
		*dst++ = '\'';
2ff057
		*dst++ = '\\';
2ff057
		*dst++ = '\'';
2ff057
		*dst++ = '\'';
2ff057
	    } else {
2ff057
		*dst++ = *src;
2ff057
	    }
2ff057
	}
2ff057
	*dst++ = '\'';
2ff057
	*dst = '\0';
2ff057
	free(buf);
2ff057
    } else {
2ff057
	*emsg = xstrdup(_("(invalid type)"));
2ff057
    }
2ff057
2ff057
    return result;
2ff057
}
2ff057
2ff057
2ff057
/* trigger type formatting (from rpmsense flags) */
2ff057
static char * triggertypeFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    char * val;
2ff057
    uint64_t item = rpmtdGetNumber(td);
2ff057
    if (item & RPMSENSE_TRIGGERPREIN)
2ff057
	val = xstrdup("prein");
2ff057
    else if (item & RPMSENSE_TRIGGERIN)
2ff057
	val = xstrdup("in");
2ff057
    else if (item & RPMSENSE_TRIGGERUN)
2ff057
	val = xstrdup("un");
2ff057
    else if (item & RPMSENSE_TRIGGERPOSTUN)
2ff057
	val = xstrdup("postun");
2ff057
    else
2ff057
	val = xstrdup("");
2ff057
    return val;
2ff057
}
2ff057
2ff057
/* dependency type formatting (from rpmsense flags) */
2ff057
static char * deptypeFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    char *val = NULL;
2ff057
    ARGV_t sdeps = NULL;
2ff057
    uint64_t item = rpmtdGetNumber(td);
2ff057
2ff057
    if (item & RPMSENSE_SCRIPT_PRE)
2ff057
	argvAdd(&sdeps, "pre");
2ff057
    if (item & RPMSENSE_SCRIPT_POST)
2ff057
	argvAdd(&sdeps, "post");
2ff057
    if (item & RPMSENSE_SCRIPT_PREUN)
2ff057
	argvAdd(&sdeps, "preun");
2ff057
    if (item & RPMSENSE_SCRIPT_POSTUN)
2ff057
	argvAdd(&sdeps, "postun");
2ff057
    if (item & RPMSENSE_SCRIPT_VERIFY)
2ff057
	argvAdd(&sdeps, "verify");
2ff057
    if (item & RPMSENSE_INTERP)
2ff057
	argvAdd(&sdeps, "interp");
2ff057
    if (item & RPMSENSE_RPMLIB)
2ff057
	argvAdd(&sdeps, "rpmlib");
2ff057
    if ((item & RPMSENSE_FIND_REQUIRES) || (item & RPMSENSE_FIND_PROVIDES))
2ff057
	argvAdd(&sdeps, "auto");
2ff057
    if (item & RPMSENSE_PREREQ)
2ff057
	argvAdd(&sdeps, "prereq");
2ff057
    if (item & RPMSENSE_PRETRANS)
2ff057
	argvAdd(&sdeps, "pretrans");
2ff057
    if (item & RPMSENSE_POSTTRANS)
2ff057
	argvAdd(&sdeps, "posttrans");
2ff057
    if (item & RPMSENSE_CONFIG)
2ff057
	argvAdd(&sdeps, "config");
2ff057
    if (item & RPMSENSE_MISSINGOK)
2ff057
	argvAdd(&sdeps, "missingok");
2ff057
2ff057
    if (sdeps) {
2ff057
	val = argvJoin(sdeps, ",");
2ff057
    } else {
2ff057
	val = xstrdup("manual");
2ff057
    }
2ff057
2ff057
    argvFree(sdeps);
2ff057
    return val;
2ff057
}
2ff057
2ff057
/* file permissions formatting */
2ff057
static char * permsFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    return rpmPermsString(rpmtdGetNumber(td));
2ff057
}
2ff057
2ff057
/* file flags formatting */
2ff057
static char * fflagsFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    return rpmFFlagsString(rpmtdGetNumber(td), "");
2ff057
}
2ff057
2ff057
/* pubkey ascii armor formatting */
2ff057
static char * armorFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    const char * enc;
2ff057
    const unsigned char * s;
2ff057
    unsigned char * bs = NULL;
2ff057
    char *val = NULL;
2ff057
    size_t ns;
2ff057
    int atype;
2ff057
2ff057
    switch (rpmtdType(td)) {
2ff057
    case RPM_BIN_TYPE:
2ff057
	s = td->data;
2ff057
	/* XXX HACK ALERT: element field abused as no. bytes of binary data. */
2ff057
	ns = td->count;
2ff057
	atype = PGPARMOR_SIGNATURE;	/* XXX check pkt for signature */
2ff057
	break;
2ff057
    case RPM_STRING_TYPE:
2ff057
    case RPM_STRING_ARRAY_TYPE:
2ff057
	enc = rpmtdGetString(td);
2ff057
	if (rpmBase64Decode(enc, (void **)&bs, &ns)) {
2ff057
	    *emsg = xstrdup(_("(not base64)"));
2ff057
	    goto exit;
2ff057
	}
2ff057
	s = bs;
2ff057
	atype = PGPARMOR_PUBKEY;	/* XXX check pkt for pubkey */
2ff057
	break;
2ff057
    case RPM_NULL_TYPE:
2ff057
    case RPM_CHAR_TYPE:
2ff057
    case RPM_INT8_TYPE:
2ff057
    case RPM_INT16_TYPE:
2ff057
    case RPM_INT32_TYPE:
2ff057
    case RPM_INT64_TYPE:
2ff057
    case RPM_I18NSTRING_TYPE:
2ff057
    default:
2ff057
	*emsg = xstrdup(_("(invalid type)"));
2ff057
	goto exit;
2ff057
	break;
2ff057
    }
2ff057
2ff057
    /* XXX this doesn't use padding directly, assumes enough slop in retval. */
2ff057
    val = pgpArmorWrap(atype, s, ns);
2ff057
    if (atype == PGPARMOR_PUBKEY) {
2ff057
    	free(bs);
2ff057
    }
2ff057
2ff057
exit:
2ff057
    return val;
2ff057
}
2ff057
2ff057
/* base64 encoding formatting */
2ff057
static char * base64Format(rpmtd td, char **emsg)
2ff057
{
2ff057
    char * val = rpmBase64Encode(td->data, td->count, -1);
2ff057
    if (val == NULL)
2ff057
	val = xstrdup("");
2ff057
2ff057
    return val;
2ff057
}
2ff057
2ff057
/* xml formatting */
2ff057
static char * xmlFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    const char *xtag = NULL;
2ff057
    char *val = NULL;
2ff057
    char *s = NULL;
2ff057
    headerTagFormatFunction fmt = stringFormat;
2ff057
2ff057
    switch (rpmtdClass(td)) {
2ff057
    case RPM_STRING_CLASS:
2ff057
	xtag = "string";
2ff057
	break;
2ff057
    case RPM_BINARY_CLASS:
2ff057
	fmt = base64Format;
2ff057
	xtag = "base64";
2ff057
	break;
2ff057
    case RPM_NUMERIC_CLASS:
2ff057
	xtag = "integer";
2ff057
	break;
2ff057
    case RPM_NULL_TYPE:
2ff057
    default:
2ff057
	*emsg = xstrdup(_("(invalid xml type)"));
2ff057
	goto exit;
2ff057
	break;
2ff057
    }
2ff057
2ff057
    s = fmt(td, emsg);
2ff057
    if (s == NULL)
2ff057
	goto exit;
2ff057
2ff057
    if (s[0] == '\0') {
2ff057
	val = rstrscat(NULL, "\t<", xtag, "/>", NULL);
2ff057
    } else {
2ff057
	char *new_s = NULL;
2ff057
	size_t i, s_size = strlen(s);
2ff057
	
2ff057
	for (i=0; i
2ff057
	    switch (s[i]) {
2ff057
		case '<':	rstrcat(&new_s, "<");	break;
2ff057
		case '>':	rstrcat(&new_s, ">");	break;
2ff057
		case '&':	rstrcat(&new_s, "&");	break;
2ff057
		default: {
2ff057
		    char c[2] = " ";
2ff057
		    *c = s[i];
2ff057
		    rstrcat(&new_s, c);
2ff057
		    break;
2ff057
		}
2ff057
	    }
2ff057
	}
2ff057
2ff057
	val = rstrscat(NULL, "\t<", xtag, ">", new_s, "</", xtag, ">", NULL);
2ff057
	free(new_s);
2ff057
    }
2ff057
    free(s);
2ff057
2ff057
exit:
2ff057
    return val;
2ff057
}
2ff057
2ff057
/* signature fingerprint and time formatting */
2ff057
static char * pgpsigFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    char * val = NULL;
2ff057
    pgpDigParams sigp = NULL;
2ff057
2ff057
    if (pgpPrtParams(td->data, td->count, PGPTAG_SIGNATURE, &sigp)) {
2ff057
	*emsg = xstrdup(_("(not an OpenPGP signature)"));
2ff057
    } else {
2ff057
	char dbuf[BUFSIZ];
2ff057
	char *keyid = pgpHexStr(sigp->signid, sizeof(sigp->signid));
2ff057
	unsigned int dateint = sigp->time;
2ff057
	time_t date = dateint;
2ff057
	struct tm * tms = localtime(&date);
2ff057
	unsigned int key_algo = pgpDigParamsAlgo(sigp, PGPVAL_PUBKEYALGO);
2ff057
	unsigned int hash_algo = pgpDigParamsAlgo(sigp, PGPVAL_HASHALGO);
2ff057
2ff057
	if (!(tms && strftime(dbuf, sizeof(dbuf), "%c", tms) > 0)) {
2ff057
	    rasprintf(emsg, _("Invalid date %u"), dateint);
2ff057
	} else {
2ff057
	    rasprintf(&val, "%s/%s, %s, Key ID %s",
2ff057
		    pgpValString(PGPVAL_PUBKEYALGO, key_algo),
2ff057
		    pgpValString(PGPVAL_HASHALGO, hash_algo),
2ff057
		    dbuf, keyid);
2ff057
	}
2ff057
2ff057
	free(keyid);
2ff057
	pgpDigParamsFree(sigp);
2ff057
    }
2ff057
2ff057
    return val;
2ff057
}
2ff057
2ff057
/* dependency flags formatting */
2ff057
static char * depflagsFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    char * val = NULL;
2ff057
    uint64_t anint = rpmtdGetNumber(td);
2ff057
    val = xcalloc(4, 1);
2ff057
2ff057
    if (anint & RPMSENSE_LESS)
2ff057
	strcat(val, "<");
2ff057
    if (anint & RPMSENSE_GREATER)
2ff057
	strcat(val, ">");
2ff057
    if (anint & RPMSENSE_EQUAL)
2ff057
	strcat(val, "=");
2ff057
2ff057
    return val;
2ff057
}
2ff057
2ff057
/* tag container array size */
2ff057
static char * arraysizeFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    char *val = NULL;
2ff057
    rasprintf(&val, "%u", rpmtdCount(td));
2ff057
    return val;
2ff057
}
2ff057
2ff057
/* file state formatting */
2ff057
static char * fstateFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    char * val = NULL;
2ff057
    const char * str;
2ff057
    rpmfileState fstate = rpmtdGetNumber(td);
2ff057
    switch (fstate) {
2ff057
    case RPMFILE_STATE_NORMAL:
2ff057
	str = _("normal");
2ff057
	break;
2ff057
    case RPMFILE_STATE_REPLACED:
2ff057
	str = _("replaced");
2ff057
	break;
2ff057
    case RPMFILE_STATE_NOTINSTALLED:
2ff057
	str = _("not installed");
2ff057
	break;
2ff057
    case RPMFILE_STATE_NETSHARED:
2ff057
	str = _("net shared");
2ff057
	break;
2ff057
    case RPMFILE_STATE_WRONGCOLOR:
2ff057
	str = _("wrong color");
2ff057
	break;
2ff057
    case RPMFILE_STATE_MISSING:
2ff057
	str = _("missing");
2ff057
	break;
2ff057
    default:
2ff057
	str = _("(unknown)");
2ff057
	break;
2ff057
    }
2ff057
2ff057
    val = xstrdup(str);
2ff057
    return val;
2ff057
}
2ff057
2ff057
/* file verification flags formatting with optional padding */
2ff057
static char * verifyFlags(rpmtd td, const char *pad)
2ff057
{
2ff057
    return rpmVerifyString(rpmtdGetNumber(td), pad);
2ff057
}
2ff057
2ff057
static char * vflagsFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    return verifyFlags(td, "");
2ff057
}
2ff057
2ff057
static char * fstatusFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    return verifyFlags(td, ".");
2ff057
}
2ff057
2ff057
/* macro expansion formatting */
2ff057
static char * expandFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    return rpmExpand(rpmtdGetString(td), NULL);
2ff057
}
2ff057
2ff057
static char * humanFormat(rpmtd td, char **emsg, int kilo)
2ff057
{
2ff057
    const char* units[] = {"", "K", "M", "G", "T", "P", "E", "Z", "Y"};
2ff057
    int i = 0;
2ff057
    float number = rpmtdGetNumber(td);
2ff057
    int decimals = 0;
2ff057
    char * val = NULL;
2ff057
2ff057
    while (number >= kilo) {
2ff057
	number /= kilo;
2ff057
	i++;
2ff057
    }
2ff057
    if ((number > 0.05) && (number < 9.95))
2ff057
	decimals = 1;
2ff057
    rasprintf(&val, "%.*f%s", decimals, number, units[i]);
2ff057
2ff057
    return val;
2ff057
}
2ff057
2ff057
static char * humansiFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    return humanFormat(td, emsg, 1000);
2ff057
}
2ff057
2ff057
static char * humaniecFormat(rpmtd td, char **emsg)
2ff057
{
2ff057
    return humanFormat(td, emsg, 1024);
2ff057
}
2ff057
2ff057
static const struct headerFmt_s rpmHeaderFormats[] = {
2ff057
    { RPMTD_FORMAT_STRING,	"string",
2ff057
	RPM_ANY_CLASS,		stringFormat },
2ff057
    { RPMTD_FORMAT_ARMOR,	"armor",
2ff057
	RPM_ANY_CLASS,		armorFormat },
2ff057
    { RPMTD_FORMAT_BASE64,	"base64",
2ff057
	RPM_BINARY_CLASS,	base64Format },
2ff057
    { RPMTD_FORMAT_PGPSIG,	"pgpsig",
2ff057
	RPM_BINARY_CLASS,	pgpsigFormat },
2ff057
    { RPMTD_FORMAT_DEPFLAGS,	"depflags",
2ff057
	RPM_NUMERIC_CLASS, depflagsFormat },
2ff057
    { RPMTD_FORMAT_DEPTYPE,	"deptype",
2ff057
	RPM_NUMERIC_CLASS,	deptypeFormat },
2ff057
    { RPMTD_FORMAT_FFLAGS,	"fflags",
2ff057
	RPM_NUMERIC_CLASS,	fflagsFormat },
2ff057
    { RPMTD_FORMAT_PERMS,	"perms",
2ff057
	RPM_NUMERIC_CLASS,	permsFormat },
2ff057
    { RPMTD_FORMAT_PERMS,	"permissions",
2ff057
	RPM_NUMERIC_CLASS,	permsFormat },
2ff057
    { RPMTD_FORMAT_TRIGGERTYPE,	"triggertype",
2ff057
	RPM_NUMERIC_CLASS,	triggertypeFormat },
2ff057
    { RPMTD_FORMAT_XML,		"xml",
2ff057
	RPM_ANY_CLASS,		xmlFormat },
2ff057
    { RPMTD_FORMAT_OCTAL,	"octal",
2ff057
	RPM_NUMERIC_CLASS,	octalFormat },
2ff057
    { RPMTD_FORMAT_HEX,		"hex",
2ff057
	RPM_NUMERIC_CLASS,	hexFormat },
2ff057
    { RPMTD_FORMAT_DATE,	"date",
2ff057
	RPM_NUMERIC_CLASS,	dateFormat },
2ff057
    { RPMTD_FORMAT_DAY,		"day",
2ff057
	RPM_NUMERIC_CLASS,	dayFormat },
2ff057
    { RPMTD_FORMAT_SHESCAPE,	"shescape",
2ff057
	RPM_ANY_CLASS,		shescapeFormat },
2ff057
    { RPMTD_FORMAT_ARRAYSIZE,	"arraysize",
2ff057
	RPM_ANY_CLASS,		arraysizeFormat },
2ff057
    { RPMTD_FORMAT_FSTATE,	"fstate",
2ff057
	RPM_NUMERIC_CLASS,	fstateFormat },
2ff057
    { RPMTD_FORMAT_VFLAGS,	"vflags",
2ff057
	RPM_NUMERIC_CLASS,	vflagsFormat },
2ff057
    { RPMTD_FORMAT_EXPAND,	"expand",
2ff057
	RPM_STRING_CLASS,	expandFormat },
2ff057
    { RPMTD_FORMAT_FSTATUS,	"fstatus",
2ff057
	RPM_NUMERIC_CLASS,	fstatusFormat },
2ff057
    { RPMTD_FORMAT_HUMANSI,	"humansi",
2ff057
	RPM_NUMERIC_CLASS,	humansiFormat },
2ff057
    { RPMTD_FORMAT_HUMANIEC,	"humaniec",
2ff057
	RPM_NUMERIC_CLASS,	humaniecFormat },
2ff057
    { -1,			NULL, 		0,	NULL }
2ff057
};
2ff057
2ff057
headerFmt rpmHeaderFormatByName(const char *fmt)
2ff057
{
2ff057
    const struct headerFmt_s * ext;
2ff057
2ff057
    for (ext = rpmHeaderFormats; ext->name != NULL; ext++) {
2ff057
	if (rstreq(ext->name, fmt))
2ff057
	    return ext;
2ff057
    }
2ff057
    return NULL;
2ff057
}
2ff057
2ff057
headerFmt rpmHeaderFormatByValue(rpmtdFormats fmt)
2ff057
{
2ff057
    const struct headerFmt_s * ext;
2ff057
2ff057
    for (ext = rpmHeaderFormats; ext->name != NULL; ext++) {
2ff057
	if (fmt == ext->fmt)
2ff057
	    return ext;
2ff057
    }
2ff057
    return NULL;
2ff057
}
2ff057
2ff057
char *rpmHeaderFormatCall(headerFmt fmt, rpmtd td)
2ff057
{
2ff057
    char *ret = NULL;
2ff057
    char *err = NULL;
2ff057
2ff057
    if (fmt->class != RPM_ANY_CLASS && rpmtdClass(td) != fmt->class)
2ff057
	err = xstrdup(classEr(fmt->class));
2ff057
    else
2ff057
	ret = fmt->func(td, &err;;
2ff057
2ff057
    if (err) {
2ff057
	free(ret);
2ff057
	ret = err;
2ff057
    }
2ff057
    return ret;
2ff057
}