rpm/lib/header.c

2265 lines
53 KiB
C

/** \ingroup header
* \file lib/header.c
*/
/* RPM - Copyright (C) 1995 Red Hat Software */
/* Data written to file descriptors is in network byte order. */
/* Data read from file descriptors is expected to be in */
/* network byte order and is converted on the fly to host order. */
#include "system.h"
#ifdef __LCLINT__
#define ntohl(_x) (_x)
#define ntohs(_x) (_x)
#define htonl(_x) (_x)
#define htons(_x) (_x)
#else
#include <netinet/in.h>
#endif /* __LCLINT__ */
#include <rpmio.h>
#include <header.h>
#define INDEX_MALLOC_SIZE 8
#define PARSER_BEGIN 0
#define PARSER_IN_ARRAY 1
#define PARSER_IN_EXPR 2
static unsigned char header_magic[4] = { 0x8e, 0xad, 0xe8, 0x01 };
/** \ingroup header
* Alignment needs (and sizeof scalars types) for internal rpm data types.
*/
static int typeSizes[] = {
-1, /*!< RPM_NULL_TYPE */
1, /*!< RPM_CHAR_TYPE */
1, /*!< RPM_INT8_TYPE */
2, /*!< RPM_INT16_TYPE */
4, /*!< RPM_INT32_TYPE */
-1, /*!< RPM_INT64_TYPE */
-1, /*!< RPM_STRING_TYPE */
1, /*!< RPM_BIN_TYPE */
-1, /*!< RPM_STRING_ARRAY_TYPE */
-1 /*!< RPM_I18NSTRING_TYPE */
};
/**
* Description of tag data.
*/
struct entryInfo {
int_32 tag; /*!< Tag identifier. */
int_32 type; /*!< Tag data type. */
int_32 offset; /*!< Offset into data segment (ondisk only). */
int_32 count; /*!< Number of tag elements. */
};
/**
* A single tag from a Header.
*/
struct indexEntry {
struct entryInfo info; /*!< Description of tag data. */
/*@owned@*/ void * data; /*!< Location of tag data. */
int length; /*!< Computable, but why bother? */
};
/**
* The Header data structure.
*/
struct headerToken {
struct indexEntry *index; /*!< Array of tags. */
int indexUsed; /*!< Current size of tag array. */
int indexAlloced; /*!< Allocated size of tag array. */
int sorted; /*!< Header is sorted by tag value? */
/*@refs@*/ int usageCount; /*!< Reference count. */
};
/**
*/
struct sprintfTag {
/* if NULL tag element is invalid */
headerTagTagFunction ext;
int extNum;
int_32 tag;
int justOne;
int arrayCount;
/*@kept@*/ char * format;
/*@kept@*/ char * type;
int pad;
};
/**
*/
struct extensionCache {
int_32 type;
int_32 count;
int avail;
int freeit;
/*@owned@*/ const void * data;
};
/**
*/
struct sprintfToken {
enum {
PTOK_NONE = 0,
PTOK_TAG,
PTOK_ARRAY,
PTOK_STRING,
PTOK_COND
} type;
union {
struct {
/*@only@*/ struct sprintfToken * format;
int numTokens;
} array;
struct sprintfTag tag;
struct {
/*@dependent@*/ char * string;
int len;
} string;
struct {
/*@only@*/ struct sprintfToken * ifFormat;
int numIfTokens;
/*@only@*/ struct sprintfToken * elseFormat;
int numElseTokens;
struct sprintfTag tag;
} cond;
} u;
};
#if HAVE_MCHECK_H
static int probe_headers = 0;
#define HEADERPROBE(_h, _msg) if (probe_headers) headerProbe((_h), (_msg))
static void headerProbeAddr(Header h, const char * msg,
void * p, const char * imsg)
{
const char * mchkstr = NULL;
switch (mprobe(p)) {
case MCHECK_DISABLED:
case MCHECK_OK:
return;
/*@notreached@*/ break;
case MCHECK_HEAD:
mchkstr = "HEAD";
break;
case MCHECK_TAIL:
mchkstr = "TAIL";
break;
case MCHECK_FREE:
mchkstr = "FREE";
break;
}
fprintf(stderr, "*** MCHECK_%s h %p", mchkstr, h);
if (imsg && p)
fprintf(stderr, " %s %p", imsg, p);
if (msg)
fprintf(stderr, " %s", msg);
fprintf(stderr, "\n");
}
static void headerProbe(Header h, const char *msg)
{
char imsg[256];
int i;
headerProbeAddr(h, msg, h, "header");
sprintf(imsg, "index (used %d)", h->indexUsed);
headerProbeAddr(h, msg, h->index, imsg);
for (i = 0; i < h->indexUsed; i++) {
sprintf(imsg, "index[%d:%d].data", i, h->indexUsed);
headerProbeAddr(h, msg, h->index[i].data, imsg);
}
}
#else /* HAVE_MCHECK_H */
#define HEADERPROBE(_h, _msg)
#endif /* HAVE_MCHECK_H */
static void copyEntry(const struct indexEntry * entry, /*@out@*/ int_32 * type,
/*@out@*/ void ** p, /*@out@*/ int_32 * c, int minimizeMemory)
/*@modifies *type, *p, *c @*/
{
int i, tableSize;
char ** ptrEntry;
char * chptr;
if (type)
*type = entry->info.type;
if (c)
*c = entry->info.count;
if (p == NULL)
return;
/* Now look it up */
switch (entry->info.type) {
case RPM_STRING_TYPE:
if (entry->info.count == 1) {
*p = entry->data;
break;
}
/*@fallthrough@*/
case RPM_STRING_ARRAY_TYPE:
case RPM_I18NSTRING_TYPE:
i = entry->info.count;
tableSize = i * sizeof(char *);
if (minimizeMemory) {
ptrEntry = *p = xmalloc(tableSize);
chptr = entry->data;
} else {
ptrEntry = *p = xmalloc(tableSize + entry->length); /* XXX memory leak */
chptr = ((char *) *p) + tableSize;
memcpy(chptr, entry->data, entry->length);
}
while (i--) {
*ptrEntry++ = chptr;
chptr = strchr(chptr, 0);
chptr++;
}
break;
default:
*p = entry->data;
break;
}
}
static int dataLength(int_32 type, const void * p, int_32 count, int onDisk)
/*@*/
{
int thisLen, length, i;
char ** src, * chptr;
length = 0;
switch (type) {
case RPM_STRING_TYPE:
if (count == 1) {
/* Special case -- p is just the string */
length = strlen(p) + 1;
break;
}
/* This should not be allowed */
fprintf(stderr, _("grabData() RPM_STRING_TYPE count must be 1.\n"));
exit(EXIT_FAILURE);
/*@notreached@*/ break;
case RPM_STRING_ARRAY_TYPE:
case RPM_I18NSTRING_TYPE:
/* This is like RPM_STRING_TYPE, except it's *always* an array */
/* Compute sum of length of all strings, including null terminators */
i = count;
length = 0;
if (onDisk) {
chptr = (char *) p;
while (i--) {
thisLen = strlen(chptr) + 1;
length += thisLen;
chptr += thisLen;
}
} else {
src = (char **) p;
while (i--) {
/* add one for null termination */
length += strlen(*src++) + 1;
}
}
break;
default:
if (typeSizes[type] != -1)
length = typeSizes[type] * count;
else {
fprintf(stderr, _("Data type %d not supported\n"), (int) type);
exit(EXIT_FAILURE);
/*@notreached@*/
}
break;
}
return length;
}
/********************************************************************/
/* */
/* Header iteration and copying */
/* */
/********************************************************************/
/**
* Header tag iterator data structure.
*/
struct headerIteratorS {
Header h; /*!< Header being iterated. */
int next_index; /*!< Next tag index. */
};
HeaderIterator headerInitIterator(Header h)
{
HeaderIterator hi = xmalloc(sizeof(struct headerIteratorS));
headerSort(h);
hi->h = headerLink(h);
hi->next_index = 0;
return hi;
}
void headerFreeIterator(HeaderIterator iter)
{
headerFree(iter->h);
free(iter);
}
int headerNextIterator(HeaderIterator iter,
int_32 *tag, int_32 *type, void **p, int_32 *c)
{
Header h = iter->h;
int slot = iter->next_index;
if (slot == h->indexUsed)
return 0;
iter->next_index++;
if (tag)
*tag = h->index[slot].info.tag;
copyEntry(h->index + slot, type, p, c, 0);
return 1;
}
static int indexCmp(const void *ap, const void *bp) /*@*/
{
int_32 a = ((const struct indexEntry *)ap)->info.tag;
int_32 b = ((const struct indexEntry *)bp)->info.tag;
return (a - b);
}
void headerSort(Header h)
{
if (!h->sorted) {
qsort(h->index, h->indexUsed, sizeof(struct indexEntry), indexCmp);
h->sorted = 1;
}
}
Header headerCopy(Header h)
{
int_32 tag, type, count;
void *ptr;
HeaderIterator headerIter;
Header res = headerNew();
headerIter = headerInitIterator(h);
while (headerNextIterator(headerIter, &tag, &type, &ptr, &count)) {
headerAddEntry(res, tag, type, ptr, count);
if (type == RPM_STRING_ARRAY_TYPE ||
type == RPM_I18NSTRING_TYPE) free(ptr);
}
res->sorted = 1;
headerFreeIterator(headerIter);
return res;
}
/********************************************************************/
/* */
/* Header loading and unloading */
/* */
/********************************************************************/
Header headerLoad(void *pv)
{
int_32 il; /* index length, data length */
char *p = pv;
const char * dataStart;
struct entryInfo * pe;
struct indexEntry * entry;
struct headerToken *h = xmalloc(sizeof(struct headerToken));
const char * src;
char * dst;
int i;
int count;
il = ntohl(*((int_32 *) p));
p += sizeof(int_32);
/* we can skip the data length -- we only store this to allow reading
from disk */
p += sizeof(int_32);
h->indexAlloced = il;
h->indexUsed = il;
h->index = xmalloc(sizeof(struct indexEntry) * il);
h->usageCount = 1;
/* This assumes you only headerLoad() something you headerUnload()-ed */
h->sorted = 1;
pe = (struct entryInfo *) p;
dataStart = (char *) (pe + h->indexUsed);
for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++, pe++) {
entry->info.type = htonl(pe->type);
entry->info.tag = htonl(pe->tag);
entry->info.count = htonl(pe->count);
entry->info.offset = -1;
if (entry->info.type < RPM_MIN_TYPE ||
entry->info.type > RPM_MAX_TYPE) return NULL;
src = dataStart + htonl(pe->offset);
entry->length = dataLength(entry->info.type, src,
entry->info.count, 1);
entry->data = dst = xmalloc(entry->length);
/* copy data w/ endian conversions */
switch (entry->info.type) {
case RPM_INT32_TYPE:
count = entry->info.count;
while (count--) {
*((int_32 *)dst) = htonl(*((int_32 *)src));
src += sizeof(int_32);
dst += sizeof(int_32);
}
break;
case RPM_INT16_TYPE:
count = entry->info.count;
while (count--) {
*((int_16 *)dst) = htons(*((int_16 *)src));
src += sizeof(int_16);
dst += sizeof(int_16);
}
break;
default:
memcpy(dst, src, entry->length);
break;
}
}
return h;
}
static void *doHeaderUnload(Header h, /*@out@*/int * lengthPtr)
/*@modifies h, *lengthPtr @*/
{
int i;
int type, diff;
void *p;
int_32 *pi;
struct entryInfo * pe;
struct indexEntry * entry;
char * chptr, * src, * dataStart;
int count;
headerSort(h);
*lengthPtr = headerSizeof(h, 0);
pi = p = xmalloc(*lengthPtr);
*pi++ = htonl(h->indexUsed);
/* data length */
*pi++ = htonl(*lengthPtr - sizeof(int_32) - sizeof(int_32) -
(sizeof(struct entryInfo) * h->indexUsed));
pe = (struct entryInfo *) pi;
dataStart = chptr = (char *) (pe + h->indexUsed);
for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++, pe++) {
pe->type = htonl(entry->info.type);
pe->tag = htonl(entry->info.tag);
pe->count = htonl(entry->info.count);
/* Alignment */
type = entry->info.type;
if (typeSizes[type] > 1) {
diff = typeSizes[type] - ((chptr - dataStart) % typeSizes[type]);
if (diff != typeSizes[type]) {
memset(chptr, 0, diff);
chptr += diff;
}
}
pe->offset = htonl(chptr - dataStart);
/* copy data w/ endian conversions */
switch (entry->info.type) {
case RPM_INT32_TYPE:
count = entry->info.count;
src = entry->data;
while (count--) {
*((int_32 *)chptr) = htonl(*((int_32 *)src));
chptr += sizeof(int_32);
src += sizeof(int_32);
}
break;
case RPM_INT16_TYPE:
count = entry->info.count;
src = entry->data;
while (count--) {
*((int_16 *)chptr) = htons(*((int_16 *)src));
chptr += sizeof(int_16);
src += sizeof(int_16);
}
break;
default:
memcpy(chptr, entry->data, entry->length);
chptr += entry->length;
break;
}
}
return p;
}
void *headerUnload(Header h)
{
void * uh;
int length;
uh = doHeaderUnload(h, &length);
return uh;
}
/********************************************************************/
/* */
/* Reading and writing headers */
/* */
/********************************************************************/
int headerWrite(FD_t fd, Header h, enum hMagic magicp)
{
void * p;
int length;
int_32 l;
ssize_t nb;
p = doHeaderUnload(h, &length);
if (magicp) {
nb = Fwrite(header_magic, sizeof(char), sizeof(header_magic), fd);
if (nb != sizeof(header_magic)) {
free(p);
return 1;
}
l = htonl(0);
nb = Fwrite(&l, sizeof(char), sizeof(l), fd);
if (nb != sizeof(l)) {
free(p);
return 1;
}
}
nb = Fwrite(p, sizeof(char), length, fd);
if (nb != length) {
free(p);
return 1;
}
free(p);
return 0;
}
Header headerRead(FD_t fd, enum hMagic magicp)
{
int_32 block[40];
int_32 reserved;
int_32 * p;
int_32 il, dl;
int_32 magic;
Header h;
void * dataBlock;
int totalSize;
int i;
i = 2;
if (magicp == HEADER_MAGIC_YES)
i += 2;
if (timedRead(fd, (char *)block, i * sizeof(*block)) != (i * sizeof(*block)))
return NULL;
i = 0;
if (magicp == HEADER_MAGIC_YES) {
magic = block[i++];
if (memcmp(&magic, header_magic, sizeof(magic)))
return NULL;
reserved = block[i++];
}
il = ntohl(block[i++]);
dl = ntohl(block[i++]);
totalSize = sizeof(int_32) + sizeof(int_32) +
(il * sizeof(struct entryInfo)) + dl;
/*
* XXX Limit total size of header to 32Mb (~16 times largest known size).
*/
if (totalSize > (32*1024*1024))
return NULL;
dataBlock = p = xmalloc(totalSize);
*p++ = htonl(il);
*p++ = htonl(dl);
totalSize -= sizeof(int_32) + sizeof(int_32);
if (timedRead(fd, (char *)p, totalSize) != totalSize)
return NULL;
h = headerLoad(dataBlock);
free(dataBlock);
return h;
}
/********************************************************************/
/* */
/* Header dumping */
/* */
/********************************************************************/
void headerDump(Header h, FILE *f, int flags,
const struct headerTagTableEntry * tags)
{
int i;
struct indexEntry *p;
const struct headerTagTableEntry * tage;
const char *tag;
char *type;
/* First write out the length of the index (count of index entries) */
fprintf(f, "Entry count: %d\n", h->indexUsed);
/* Now write the index */
p = h->index;
fprintf(f, "\n CT TAG TYPE "
" OFSET COUNT\n");
for (i = 0; i < h->indexUsed; i++) {
switch (p->info.type) {
case RPM_NULL_TYPE: type = "NULL_TYPE"; break;
case RPM_CHAR_TYPE: type = "CHAR_TYPE"; break;
case RPM_BIN_TYPE: type = "BIN_TYPE"; break;
case RPM_INT8_TYPE: type = "INT8_TYPE"; break;
case RPM_INT16_TYPE: type = "INT16_TYPE"; break;
case RPM_INT32_TYPE: type = "INT32_TYPE"; break;
/*case RPM_INT64_TYPE: type = "INT64_TYPE"; break;*/
case RPM_STRING_TYPE: type = "STRING_TYPE"; break;
case RPM_STRING_ARRAY_TYPE: type = "STRING_ARRAY_TYPE"; break;
case RPM_I18NSTRING_TYPE: type = "I18N_STRING_TYPE"; break;
default: type = "(unknown)"; break;
}
tage = tags;
while (tage->name && tage->val != p->info.tag) tage++;
if (!tage->name)
tag = "(unknown)";
else
tag = tage->name;
fprintf(f, "Entry : %.3d (%d)%-14s %-18s 0x%.8x %.8d\n", i,
p->info.tag, tag, type, (unsigned) p->info.offset, (int)
p->info.count);
if (flags & HEADER_DUMP_INLINE) {
char *dp = p->data;
int c = p->info.count;
int ct = 0;
/* Print the data inline */
switch (p->info.type) {
case RPM_INT32_TYPE:
while (c--) {
fprintf(f, " Data: %.3d 0x%08x (%d)\n", ct++,
(unsigned) *((int_32 *) dp),
(int) *((int_32 *) dp));
dp += sizeof(int_32);
}
break;
case RPM_INT16_TYPE:
while (c--) {
fprintf(f, " Data: %.3d 0x%04x (%d)\n", ct++,
(unsigned) (*((int_16 *) dp) & 0xffff),
(int) *((int_16 *) dp));
dp += sizeof(int_16);
}
break;
case RPM_INT8_TYPE:
while (c--) {
fprintf(f, " Data: %.3d 0x%02x (%d)\n", ct++,
(unsigned) (*((int_8 *) dp) & 0xff),
(int) *((int_8 *) dp));
dp += sizeof(int_8);
}
break;
case RPM_BIN_TYPE:
while (c > 0) {
fprintf(f, " Data: %.3d ", ct);
while (c--) {
fprintf(f, "%02x ", (unsigned) (*(int_8 *)dp & 0xff));
ct++;
dp += sizeof(int_8);
if (! (ct % 8)) {
break;
}
}
fprintf(f, "\n");
}
break;
case RPM_CHAR_TYPE:
while (c--) {
char ch = (char) *((char *) dp);
fprintf(f, " Data: %.3d 0x%2x %c (%d)\n", ct++,
(unsigned)(ch & 0xff),
(isprint(ch) ? ch : ' '),
(int) *((char *) dp));
dp += sizeof(char);
}
break;
case RPM_STRING_TYPE:
case RPM_STRING_ARRAY_TYPE:
case RPM_I18NSTRING_TYPE:
while (c--) {
fprintf(f, " Data: %.3d %s\n", ct++, (char *) dp);
dp = strchr(dp, 0);
dp++;
}
break;
default:
fprintf(stderr, _("Data type %d not supported\n"),
(int) p->info.type);
exit(EXIT_FAILURE);
/*@notreached@*/ break;
}
}
p++;
}
}
/********************************************************************/
/* */
/* Entry lookup */
/* */
/********************************************************************/
static struct indexEntry *findEntry(Header h, int_32 tag, int_32 type)
/*@modifies h @*/
{
struct indexEntry * entry, * entry2, * last;
struct indexEntry key;
if (!h->sorted) headerSort(h);
key.info.tag = tag;
entry2 = entry =
bsearch(&key, h->index, h->indexUsed, sizeof(struct indexEntry),
indexCmp);
if (!entry) return NULL;
if (type == RPM_NULL_TYPE) return entry;
/* look backwards */
while (entry->info.tag == tag && entry->info.type != type &&
entry > h->index) entry--;
if (entry->info.tag == tag && entry->info.type == type)
return entry;
last = h->index + h->indexUsed;
while (entry2->info.tag == tag && entry2->info.type != type &&
entry2 < last) entry2++;
if (entry->info.tag == tag && entry->info.type == type)
return entry;
return NULL;
}
int headerIsEntry(Header h, int_32 tag)
{
return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
}
int headerGetRawEntry(Header h, int_32 tag, int_32 *type, void **p, int_32 *c)
{
struct indexEntry * entry;
if (p == NULL) return headerIsEntry(h, tag);
/* First find the tag */
entry = findEntry(h, tag, RPM_NULL_TYPE);
if (!entry) {
if (p) *p = NULL;
if (c) *c = 0;
return 0;
}
copyEntry(entry, type, p, c, 0);
return 1;
}
static int headerMatchLocale(const char *td, const char *l, const char *le)
/*@*/
{
const char *fe;
/*
* The range [l,le) contains the next locale to match:
* ll[_CC][.EEEEE][@dddd]
* where
* ll ISO language code (in lowercase).
* CC (optional) ISO coutnry code (in uppercase).
* EEEEE (optional) encoding (not really standardized).
* dddd (optional) dialect.
*/
#if 0
{ const char *s, *ll, *CC, *EE, *dd;
char *lbuf, *t.
/* Copy the buffer and parse out components on the fly. */
lbuf = alloca(le - l + 1);
for (s = l, ll = t = lbuf; *s; s++, t++) {
switch (*s) {
case '_':
*t = '\0';
CC = t + 1;
break;
case '.':
*t = '\0';
EE = t + 1;
break;
case '@':
*t = '\0';
dd = t + 1;
break;
default:
*t = *s;
break;
}
}
if (ll) /* ISO language should be lower case */
for (t = ll; *t; t++) *t = tolower(*t);
if (CC) /* ISO country code should be upper case */
for (t = CC; *t; t++) *t = toupper(*t);
/* There are a total of 16 cases to attempt to match. */
}
#endif
/* First try a complete match. */
if (strlen(td) == (le-l) && !strncmp(td, l, (le - l)))
return 1;
/* Next, try stripping optional dialect and matching. */
for (fe = l; fe < le && *fe != '@'; fe++)
;
if (fe < le && !strncmp(td, l, (fe - l)))
return 1;
/* Next, try stripping optional codeset and matching. */
for (fe = l; fe < le && *fe != '.'; fe++)
;
if (fe < le && !strncmp(td, l, (fe - l)))
return 1;
/* Finally, try stripping optional country code and matching. */
for (fe = l; fe < le && *fe != '_'; fe++)
;
if (fe < le && !strncmp(td, l, (fe - l)))
return 1;
return 0;
}
/*@dependent@*/ static char *
headerFindI18NString(Header h, struct indexEntry *entry)
{
const char *lang, *l, *le;
struct indexEntry * table;
/* XXX Drepper sez' this is the order. */
if ((lang = getenv("LANGUAGE")) == NULL &&
(lang = getenv("LC_ALL")) == NULL &&
(lang = getenv("LC_MESSAGES")) == NULL &&
(lang = getenv("LANG")) == NULL)
return entry->data;
if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
return entry->data;
for (l = lang; *l; l = le) {
const char *td;
char *ed;
int langNum;
while (*l && *l == ':') /* skip leading colons */
l++;
if (*l == '\0')
break;
for (le = l; *le && *le != ':'; le++) /* find end of this locale */
;
/* For each entry in the header ... */
for (langNum = 0, td = table->data, ed = entry->data;
langNum < entry->info.count;
langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
if (headerMatchLocale(td, l, le))
return ed;
}
}
return entry->data;
}
static int intGetEntry(Header h, int_32 tag, /*@out@*/ int_32 *type,
/*@out@*/ void **p, /*@out@*/ int_32 *c, int minMem)
/*@modifies h, *type, *p, *c @*/
{
struct indexEntry * entry;
char * chptr;
HEADERPROBE(h, "intGetEntry");
/* First find the tag */
entry = findEntry(h, tag, RPM_NULL_TYPE);
if (!entry) {
if (p) *p = NULL;
if (c) *c = 0;
return 0;
}
if (entry->info.type == RPM_I18NSTRING_TYPE) {
chptr = headerFindI18NString(h, entry);
if (type) *type = RPM_STRING_TYPE;
if (c) *c = 1;
/*@-dependenttrans@*/ *p = chptr; /*@=dependenttrans@*/
} else {
copyEntry(entry, type, p, c, minMem);
}
return 1;
}
int headerGetEntryMinMemory(Header h, int_32 tag, int_32 *type, void **p,
int_32 *c)
{
return intGetEntry(h, tag, type, p, c, 1);
}
int headerGetEntry(Header h, int_32 tag, int_32 * type, void **p, int_32 * c)
{
return intGetEntry(h, tag, type, p, c, 0);
}
/********************************************************************/
/* */
/* Header creation and deletion */
/* */
/********************************************************************/
Header headerNew()
{
Header h = xmalloc(sizeof(struct headerToken));
h->indexAlloced = INDEX_MALLOC_SIZE;
h->index = xcalloc(h->indexAlloced, sizeof(struct indexEntry));
h->indexUsed = 0;
h->sorted = 0;
h->usageCount = 1;
return (Header) h;
}
void headerFree(Header h)
{
int i;
if (--h->usageCount) return;
for (i = 0; i < h->indexUsed; i++)
free(h->index[i].data);
free(h->index);
/*@-refcounttrans@*/ free(h); /*@=refcounttrans@*/
}
Header headerLink(Header h)
{
HEADERPROBE(h, "headerLink");
h->usageCount++;
/*@-refcounttrans@*/ return h; /*@=refcounttrans@*/
}
int headerUsageCount(Header h)
{
return h->usageCount;
}
unsigned int headerSizeof(Header h, enum hMagic magicp)
{
unsigned int size;
int i, diff;
int_32 type;
headerSort(h);
size = sizeof(int_32); /* count of index entries */
size += sizeof(int_32); /* length of data */
size += sizeof(struct entryInfo) * h->indexUsed;
if (magicp)
size += 8;
for (i = 0; i < h->indexUsed; i++) {
/* Alignment */
type = h->index[i].info.type;
if (typeSizes[type] > 1) {
diff = typeSizes[type] - (size % typeSizes[type]);
if (diff != typeSizes[type])
size += diff;
}
size += h->index[i].length;
}
return size;
}
static void copyData(int_32 type, /*@out@*/ void * dstPtr, const void * srcPtr,
int_32 c, int dataLength)
/*@modifies *dstPtr @*/
{
const char ** src;
char * dst;
int i, len;
switch (type) {
case RPM_STRING_ARRAY_TYPE:
case RPM_I18NSTRING_TYPE:
/* Otherwise, p is char** */
i = c;
src = (const char **) srcPtr;
dst = dstPtr;
while (i--) {
len = *src ? strlen(*src) + 1 : 0;
memcpy(dst, *src, len);
dst += len;
src++;
}
break;
default:
memcpy(dstPtr, srcPtr, dataLength);
break;
}
}
static void * grabData(int_32 type, const void * p, int_32 c,
/*@out@*/ int * lengthPtr)
/*@modifies *lengthPtr @*/
{
int length;
void * data;
length = dataLength(type, p, c, 0);
data = xmalloc(length);
copyData(type, data, p, c, length);
*lengthPtr = length;
return data;
}
/********************************************************************/
/* */
/* Adding and modifying entries */
/* */
/********************************************************************/
int headerAddEntry(Header h, int_32 tag, int_32 type, const void *p, int_32 c)
{
struct indexEntry *entry;
h->sorted = 0;
if (c <= 0) {
fprintf(stderr, _("Bad count for headerAddEntry(): %d\n"), (int) c);
exit(EXIT_FAILURE);
/*@notreached@*/
}
/* Allocate more index space if necessary */
if (h->indexUsed == h->indexAlloced) {
h->indexAlloced += INDEX_MALLOC_SIZE;
h->index = xrealloc(h->index,
h->indexAlloced * sizeof(struct indexEntry));
}
/* Fill in the index */
entry = h->index + h->indexUsed++;
entry->info.tag = tag;
entry->info.type = type;
entry->info.count = c;
entry->info.offset = -1;
entry->data = grabData(type, p, c, &entry->length);
h->sorted = 0;
return 1;
}
char **
headerGetLangs(Header h)
{
char **s, *e, **table;
int i, type, count;
if (!headerGetRawEntry(h, HEADER_I18NTABLE, &type, (void **)&s, &count))
return NULL;
if ((table = (char **)xcalloc((count+1), sizeof(char *))) == NULL)
return NULL;
for (i = 0, e = *s; i < count > 0; i++, e += strlen(e)+1) {
table[i] = e;
}
table[count] = NULL;
return table;
}
int headerAddI18NString(Header h, int_32 tag, const char * string, const char * lang)
{
struct indexEntry * table, * entry;
char * chptr;
const char ** strArray;
int length;
int ghosts;
int i, langNum;
char * buf;
table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
if (!table && entry)
return 0; /* this shouldn't ever happen!! */
if (!table && !entry) {
errmsg_t charArray[2];
int count = 0;
if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
charArray[count++] = "C";
} else {
charArray[count++] = "C";
charArray[count++] = lang;
}
if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE,
&charArray, count))
return 0;
table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
}
if (!lang) lang = "C";
chptr = table->data;
for (langNum = 0; langNum < table->info.count; langNum++) {
if (!strcmp(chptr, lang)) break;
chptr += strlen(chptr) + 1;
}
if (langNum >= table->info.count) {
length = strlen(lang) + 1;
table->data = xrealloc(table->data, table->length + length);
memcpy(((char *)table->data) + table->length, lang, length);
table->length += length;
table->info.count++;
}
if (!entry) {
strArray = alloca(sizeof(*strArray) * (langNum + 1));
for (i = 0; i < langNum; i++)
strArray[i] = "";
strArray[langNum] = string;
return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, strArray,
langNum + 1);
} else if (langNum >= entry->info.count) {
ghosts = langNum - entry->info.count;
length = strlen(string) + 1 + ghosts;
entry->data = xrealloc(entry->data, entry->length + length);
memset(((char *)entry->data) + entry->length, '\0', ghosts);
strcpy(((char *)entry->data) + entry->length + ghosts, string);
entry->length += length;
entry->info.count = langNum + 1;
} else {
char *b, *be, *e, *ee, *t;
size_t bn, sn, en;
/* Set beginning/end pointers to previous data */
b = be = e = ee = entry->data;
for (i = 0; i < table->info.count; i++) {
if (i == langNum)
be = ee;
ee += strlen(ee) + 1;
if (i == langNum)
e = ee;
}
/* Get storage for new buffer */
bn = (be-b);
sn = strlen(string) + 1;
en = (ee-e);
length = bn + sn + en;
t = buf = xmalloc(length);
/* Copy values into new storage */
memcpy(t, b, bn);
t += bn;
memcpy(t, string, sn);
t += sn;
memcpy(t, e, en);
t += en;
/* Replace I18N string array */
entry->length -= strlen(be) + 1;
entry->length += sn;
free(entry->data);
entry->data = buf;
}
return 0;
}
/* if there are multiple entries with this tag, the first one gets replaced */
int headerModifyEntry(Header h, int_32 tag, int_32 type, void *p, int_32 c)
{
struct indexEntry *entry;
void * oldData;
/* First find the tag */
entry = findEntry(h, tag, type);
if (!entry)
return 0;
/* make sure entry points to the first occurence of this tag */
while (entry > h->index && (entry - 1)->info.tag == tag)
entry--;
/* free after we've grabbed the new data in case the two are intertwined;
that's a bad idea but at least we won't break */
oldData = entry->data;
entry->info.count = c;
entry->info.type = type;
entry->data = grabData(type, p, c, &entry->length);
free(oldData);
return 1;
}
int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
void * p, int_32 c)
{
if (findEntry(h, tag, type)) {
return headerAppendEntry(h, tag, type, p, c);
} else {
return headerAddEntry(h, tag, type, p, c);
}
}
int headerAppendEntry(Header h, int_32 tag, int_32 type, void * p, int_32 c)
{
struct indexEntry *entry;
int length;
/* First find the tag */
entry = findEntry(h, tag, type);
if (!entry)
return 0;
if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
/* we can't do this */
return 0;
}
length = dataLength(type, p, c, 0);
entry->data = xrealloc(entry->data, entry->length + length);
copyData(type, ((char *) entry->data) + entry->length, p, c, length);
entry->length += length;
entry->info.count += c;
return 1;
}
int headerRemoveEntry(Header h, int_32 tag)
{
struct indexEntry * entry, * last;
entry = findEntry(h, tag, RPM_NULL_TYPE);
if (!entry) return 1;
/* make sure entry points to the first occurence of this tag */
while (entry > h->index && (entry - 1)->info.tag == tag)
entry--;
/* We might be better off just counting the number of items off the
end and issuing one big memcpy, but memcpy() doesn't have to work
on overlapping regions thanks to ANSI <sigh>. A alloca() and two
memcpy() would probably still be a win (as our moving from the
end to the middle isn't very nice to the qsort() we'll have to
do to make up for this!), but I'm too lazy to implement it. Just
remember that this repeating this is basically nlogn thanks to this
dumb implementation (but n is the best we'd do anyway) */
last = h->index + h->indexUsed;
while (entry->info.tag == tag && entry < last) {
free(entry->data);
*(entry++) = *(--last);
}
h->indexUsed = last - h->index;
h->sorted = 0;
return 0;
}
static char escapedChar(const char ch) /*@*/
{
switch (ch) {
case 'a': return '\a';
case 'b': return '\b';
case 'f': return '\f';
case 'n': return '\n';
case 'r': return '\r';
case 't': return '\t';
case 'v': return '\v';
default: return ch;
}
}
static void freeFormat( /*@only@*/ struct sprintfToken * format, int num)
{
int i;
for (i = 0; i < num; i++) {
switch (format[i].type) {
case PTOK_ARRAY:
freeFormat(format[i].u.array.format, format[i].u.array.numTokens);
break;
case PTOK_COND:
freeFormat(format[i].u.cond.ifFormat,
format[i].u.cond.numIfTokens);
freeFormat(format[i].u.cond.elseFormat,
format[i].u.cond.numElseTokens);
break;
case PTOK_NONE:
case PTOK_TAG:
case PTOK_STRING:
default:
break;
}
}
free(format);
}
static void findTag(char * name, const struct headerTagTableEntry * tags,
const struct headerSprintfExtension * extensions,
/*@out@*/const struct headerTagTableEntry ** tagMatch,
/*@out@*/const struct headerSprintfExtension ** extMatch)
/*@modifies *tagMatch, *extMatch @*/
{
const struct headerTagTableEntry * entry;
const struct headerSprintfExtension * ext;
char * tagname;
*tagMatch = NULL;
*extMatch = NULL;
if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
tagname = alloca(strlen(name) + 10);
strcpy(tagname, "RPMTAG_");
strcat(tagname, name);
} else {
tagname = name;
}
/* Search extensions first to permit overriding header tags. */
ext = extensions;
while (ext->type != HEADER_EXT_LAST) {
if (ext->type == HEADER_EXT_TAG && !strcasecmp(ext->name, tagname))
break;
if (ext->type == HEADER_EXT_MORE)
ext = ext->u.more;
else
ext++;
}
if (ext->type == HEADER_EXT_TAG) {
*extMatch = ext;
return;
}
/* Search header tags. */
for (entry = tags; entry->name; entry++)
if (!strcasecmp(entry->name, tagname)) break;
if (entry->name) {
*tagMatch = entry;
return;
}
}
/* forward ref */
static int parseExpression(struct sprintfToken * token, char * str,
const struct headerTagTableEntry * tags,
const struct headerSprintfExtension * extensions,
/*@out@*/char ** endPtr, /*@out@*/ errmsg_t * errmsg)
/*@modifies str, *str, *token, *endPtr, *errmsg @*/;
static int parseFormat(char * str, const struct headerTagTableEntry * tags,
const struct headerSprintfExtension * extensions,
/*@out@*/struct sprintfToken ** formatPtr, /*@out@*/int * numTokensPtr,
/*@out@*/char ** endPtr, int state, /*@out@*/ errmsg_t * errmsg)
/*@modifies str, *str, *formatPtr, *numTokensPtr, *endPtr, *errmsg @*/
{
char * chptr, * start, * next, * dst;
struct sprintfToken * format;
int numTokens;
int currToken;
const struct headerTagTableEntry * tag;
const struct headerSprintfExtension * ext;
int i;
int done = 0;
/* upper limit on number of individual formats */
numTokens = 0;
for (chptr = str; *chptr; chptr++)
if (*chptr == '%') numTokens++;
numTokens = numTokens * 2 + 1;
format = xcalloc(numTokens, sizeof(*format));
if (endPtr) *endPtr = NULL;
dst = start = str;
currToken = -1;
while (*start) {
switch (*start) {
case '%':
/* handle %% */
if (*(start + 1) == '%') {
if (currToken < 0 || format[currToken].type != PTOK_STRING) {
currToken++;
format[currToken].type = PTOK_STRING;
dst = format[currToken].u.string.string = start;
}
start++;
*dst++ = *start++;
break; /* out of switch */
}
currToken++;
*dst++ = '\0';
start++;
if (*start == '|') {
char * newEnd;
start++;
if (parseExpression(format + currToken, start, tags,
extensions, &newEnd, errmsg)) {
freeFormat(format, numTokens);
return 1;
}
start = newEnd;
break; /* out of switch */
}
format[currToken].u.tag.format = start;
format[currToken].u.tag.pad = 0;
format[currToken].u.tag.justOne = 0;
format[currToken].u.tag.arrayCount = 0;
chptr = start;
while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
if (!*chptr || *chptr == '%') {
*errmsg = _("missing { after %");
freeFormat(format, numTokens);
return 1;
}
*chptr++ = '\0';
while (start < chptr) {
if (isdigit(*start)) {
i = strtoul(start, &start, 10);
format[currToken].u.tag.pad += i;
} else {
start++;
}
}
if (*start == '=') {
format[currToken].u.tag.justOne = 1;
start++;
} else if (*start == '#') {
format[currToken].u.tag.justOne = 1;
format[currToken].u.tag.arrayCount = 1;
start++;
}
next = start;
while (*next && *next != '}') next++;
if (!*next) {
*errmsg = _("missing } after %{");
freeFormat(format, numTokens);
return 1;
}
*next++ = '\0';
chptr = start;
while (*chptr && *chptr != ':') chptr++;
if (*chptr) {
*chptr++ = '\0';
if (!*chptr) {
*errmsg = _("empty tag format");
freeFormat(format, numTokens);
return 1;
}
format[currToken].u.tag.type = chptr;
} else {
format[currToken].u.tag.type = NULL;
}
if (!*start) {
*errmsg = _("empty tag name");
freeFormat(format, numTokens);
return 1;
}
i = 0;
findTag(start, tags, extensions, &tag, &ext);
if (tag) {
format[currToken].u.tag.ext = NULL;
format[currToken].u.tag.tag = tag->val;
} else if (ext) {
format[currToken].u.tag.ext = ext->u.tagFunction;
format[currToken].u.tag.extNum = ext - extensions;
} else {
*errmsg = _("unknown tag");
freeFormat(format, numTokens);
return 1;
}
format[currToken].type = PTOK_TAG;
start = next;
break;
case '[':
*dst++ = '\0';
*start++ = '\0';
currToken++;
if (parseFormat(start, tags, extensions,
&format[currToken].u.array.format,
&format[currToken].u.array.numTokens,
&start, PARSER_IN_ARRAY, errmsg)) {
freeFormat(format, numTokens);
return 1;
}
if (!start) {
*errmsg = _("] expected at end of array");
freeFormat(format, numTokens);
return 1;
}
dst = start;
format[currToken].type = PTOK_ARRAY;
break;
case ']':
case '}':
if ((*start == ']' && state != PARSER_IN_ARRAY) ||
(*start == '}' && state != PARSER_IN_EXPR)) {
if (*start == ']')
*errmsg = _("unexpected ]");
else
*errmsg = _("unexpected }");
freeFormat(format, numTokens);
return 1;
}
*start++ = '\0';
*endPtr = start;
done = 1;
break;
default:
if (currToken < 0 || format[currToken].type != PTOK_STRING) {
currToken++;
format[currToken].type = PTOK_STRING;
dst = format[currToken].u.string.string = start;
}
if (*start == '\\') {
start++;
*dst++ = escapedChar(*start++);
} else {
*dst++ = *start++;
}
break;
}
if (done)
break;
}
*dst = '\0';
currToken++;
for (i = 0; i < currToken; i++) {
if (format[i].type == PTOK_STRING)
format[i].u.string.len = strlen(format[i].u.string.string);
}
*numTokensPtr = currToken;
*formatPtr = format;
return 0;
}
static int parseExpression(struct sprintfToken * token, char * str,
const struct headerTagTableEntry * tags,
const struct headerSprintfExtension * extensions,
/*@out@*/ char ** endPtr, /*@out@*/ errmsg_t * errmsg)
/*@modifies str, *str, *token, *endPtr, *errmsg @*/
{
const struct headerTagTableEntry * tag;
const struct headerSprintfExtension * ext;
char * chptr;
char * end;
*errmsg = NULL;
chptr = str;
while (*chptr && *chptr != '?') chptr++;
if (*chptr != '?') {
*errmsg = _("? expected in expression");
return 1;
}
*chptr++ = '\0';;
if (*chptr != '{') {
*errmsg = _("{ expected after ? in expression");
return 1;
}
chptr++;
if (parseFormat(chptr, tags, extensions, &token->u.cond.ifFormat,
&token->u.cond.numIfTokens, &end, PARSER_IN_EXPR, errmsg))
return 1;
if (!*end) {
*errmsg = _("} expected in expression");
freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
token->u.cond.ifFormat = NULL;
return 1;
}
chptr = end;
if (*chptr != ':' && *chptr != '|') {
*errmsg = _(": expected following ? subexpression");
freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
token->u.cond.ifFormat = NULL;
return 1;
}
if (*chptr == '|') {
parseFormat(xstrdup(""), tags, extensions, &token->u.cond.elseFormat,
&token->u.cond.numElseTokens, &end, PARSER_IN_EXPR,
errmsg);
} else {
chptr++;
if (*chptr != '{') {
*errmsg = _("{ expected after : in expression");
freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
token->u.cond.ifFormat = NULL;
return 1;
}
chptr++;
if (parseFormat(chptr, tags, extensions, &token->u.cond.elseFormat,
&token->u.cond.numElseTokens, &end, PARSER_IN_EXPR,
errmsg))
return 1;
if (!*end) {
*errmsg = _("} expected in expression");
freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
token->u.cond.ifFormat = NULL;
return 1;
}
chptr = end;
if (*chptr != '|') {
*errmsg = _("| expected at end of expression");
freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
token->u.cond.ifFormat = NULL;
freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
token->u.cond.elseFormat = NULL;
return 1;
}
}
chptr++;
*endPtr = chptr;
findTag(str, tags, extensions, &tag, &ext);
if (tag) {
token->u.cond.tag.ext = NULL;
token->u.cond.tag.tag = tag->val;
} else if (ext) {
token->u.cond.tag.ext = ext->u.tagFunction;
token->u.cond.tag.extNum = ext - extensions;
} else {
token->u.cond.tag.ext = NULL;
token->u.cond.tag.tag = -1;
}
token->type = PTOK_COND;
return 0;
}
static int getExtension(Header h, headerTagTagFunction fn,
/*@out@*/ int_32 * typeptr, /*@out@*/ const void ** data,
/*@out@*/ int_32 * countptr, struct extensionCache * ext)
/*@modifies *typeptr, *data, *countptr, ext->avail @*/
{
if (!ext->avail) {
if (fn(h, &ext->type, &ext->data, &ext->count, &ext->freeit))
return 1;
ext->avail = 1;
}
*typeptr = ext->type;
*data = ext->data;
*countptr = ext->count;
return 0;
}
static char * formatValue(struct sprintfTag * tag, Header h,
const struct headerSprintfExtension * extensions,
struct extensionCache * extCache, int element)
/*@modifies h, extCache->avail @*/
{
int len;
char buf[20];
int_32 count, type;
const void * data;
unsigned int intVal;
char * val = NULL;
const char ** strarray;
int mayfree = 0;
int countBuf;
headerTagFormatFunction tagtype = NULL;
const struct headerSprintfExtension * ext;
if (tag->ext) {
if (getExtension(h, tag->ext, &type, &data, &count,
extCache + tag->extNum)) {
count = 1;
type = RPM_STRING_TYPE;
data = "(none)"; /* XXX i18n? NO!, sez; gafton */
}
} else {
if (!headerGetEntry(h, tag->tag, &type, (void **)&data, &count)){
count = 1;
type = RPM_STRING_TYPE;
data = "(none)"; /* XXX i18n? NO!, sez; gafton */
}
mayfree = 1;
}
if (tag->arrayCount) {
if (type == RPM_STRING_ARRAY_TYPE) free((void *)data);
countBuf = count;
data = &countBuf;
count = 1;
type = RPM_INT32_TYPE;
}
strcpy(buf, "%");
strcat(buf, tag->format);
if (tag->type) {
ext = extensions;
while (ext->type != HEADER_EXT_LAST) {
if (ext->type == HEADER_EXT_FORMAT &&
!strcmp(ext->name, tag->type)) {
tagtype = ext->u.formatFunction;
break;
}
if (ext->type == HEADER_EXT_MORE)
ext = ext->u.more;
else
ext++;
}
}
switch (type) {
case RPM_STRING_ARRAY_TYPE:
strarray = (const char **)data;
if (tagtype)
val = tagtype(RPM_STRING_TYPE, strarray[element], buf, tag->pad, 0);
if (!val) {
strcat(buf, "s");
len = strlen(strarray[element]) + tag->pad + 20;
val = xmalloc(len);
sprintf(val, buf, strarray[element]);
}
if (mayfree) free((void *)data);
break;
case RPM_STRING_TYPE:
if (tagtype)
val = tagtype(RPM_STRING_ARRAY_TYPE, data, buf, tag->pad, 0);
if (!val) {
strcat(buf, "s");
len = strlen(data) + tag->pad + 20;
val = xmalloc(len);
sprintf(val, buf, data);
}
break;
case RPM_CHAR_TYPE:
case RPM_INT8_TYPE:
case RPM_INT16_TYPE:
case RPM_INT32_TYPE:
switch (type) {
case RPM_CHAR_TYPE:
case RPM_INT8_TYPE: intVal = *(((int_8 *) data) + element); break;
case RPM_INT16_TYPE: intVal = *(((uint_16 *) data) + element); break;
default: /* keep -Wall quiet */
case RPM_INT32_TYPE: intVal = *(((int_32 *) data) + element);
}
if (tagtype)
val = tagtype(RPM_INT32_TYPE, &intVal, buf, tag->pad, element);
if (!val) {
strcat(buf, "d");
len = 10 + tag->pad + 20;
val = xmalloc(len);
sprintf(val, buf, intVal);
}
break;
default:
val = xstrdup(_("(unknown type)"));
break;
}
return val;
}
static const char * singleSprintf(Header h, struct sprintfToken * token,
const struct headerSprintfExtension * extensions,
struct extensionCache * extCache, int element)
/*@modifies h, extCache->avail @*/
{
char * val;
const char * thisItem;
int thisItemLen;
int len, alloced;
int i, j;
int numElements;
int type;
struct sprintfToken * condFormat;
int condNumFormats;
/* we assume the token and header have been validated already! */
switch (token->type) {
case PTOK_NONE:
break;
case PTOK_STRING:
val = xmalloc(token->u.string.len + 1);
strcpy(val, token->u.string.string);
break;
case PTOK_TAG:
val = formatValue(&token->u.tag, h, extensions, extCache,
token->u.tag.justOne ? 0 : element);
break;
case PTOK_COND:
if (token->u.cond.tag.ext ||
headerIsEntry(h, token->u.cond.tag.tag)) {
condFormat = token->u.cond.ifFormat;
condNumFormats = token->u.cond.numIfTokens;
} else {
condFormat = token->u.cond.elseFormat;
condNumFormats = token->u.cond.numElseTokens;
}
alloced = condNumFormats * 20;
val = xmalloc(alloced ? alloced : 1);
*val = '\0';
len = 0;
for (i = 0; i < condNumFormats; i++) {
thisItem = singleSprintf(h, condFormat + i,
extensions, extCache, element);
thisItemLen = strlen(thisItem);
if ((thisItemLen + len) >= alloced) {
alloced = (thisItemLen + len) + 200;
val = xrealloc(val, alloced);
}
strcat(val, thisItem);
len += thisItemLen;
free((void *)thisItem);
}
break;
case PTOK_ARRAY:
numElements = -1;
for (i = 0; i < token->u.array.numTokens; i++) {
if (token->u.array.format[i].type != PTOK_TAG ||
token->u.array.format[i].u.tag.arrayCount ||
token->u.array.format[i].u.tag.justOne) continue;
if (token->u.array.format[i].u.tag.ext) {
const void * data;
if (getExtension(h, token->u.array.format[i].u.tag.ext,
&type, &data, &numElements,
extCache +
token->u.array.format[i].u.tag.extNum))
continue;
} else {
if (!headerGetEntry(h, token->u.array.format[i].u.tag.tag,
&type, (void **) &val, &numElements))
continue;
if (type == RPM_STRING_ARRAY_TYPE) free(val);
}
break;
}
if (numElements == -1) {
val = xstrdup("(none)"); /* XXX i18n? NO!, sez; gafton */
} else {
alloced = numElements * token->u.array.numTokens * 20;
val = xmalloc(alloced);
*val = '\0';
len = 0;
for (j = 0; j < numElements; j++) {
for (i = 0; i < token->u.array.numTokens; i++) {
thisItem = singleSprintf(h, token->u.array.format + i,
extensions, extCache, j);
thisItemLen = strlen(thisItem);
if ((thisItemLen + len) >= alloced) {
alloced = (thisItemLen + len) + 200;
val = xrealloc(val, alloced);
}
strcat(val, thisItem);
len += thisItemLen;
free((void *)thisItem);
}
}
}
break;
}
return val;
}
static struct extensionCache * allocateExtensionCache(
const struct headerSprintfExtension * extensions)
/*@*/
{
const struct headerSprintfExtension * ext = extensions;
int i = 0;
while (ext->type != HEADER_EXT_LAST) {
i++;
if (ext->type == HEADER_EXT_MORE)
ext = ext->u.more;
else
ext++;
}
return xcalloc(i, sizeof(struct extensionCache));
}
static void freeExtensionCache(const struct headerSprintfExtension * extensions,
/*@only@*/struct extensionCache * cache)
{
const struct headerSprintfExtension * ext = extensions;
int i = 0;
while (ext->type != HEADER_EXT_LAST) {
if (cache[i].freeit) free((void *)cache[i].data);
i++;
if (ext->type == HEADER_EXT_MORE)
ext = ext->u.more;
else
ext++;
}
free(cache);
}
char * headerSprintf(Header h, const char * origFmt,
const struct headerTagTableEntry * tags,
const struct headerSprintfExtension * extensions,
errmsg_t * errmsg)
{
char * fmtString;
struct sprintfToken * format;
int numTokens;
char * answer;
int answerLength;
int answerAlloced;
int i;
struct extensionCache * extCache;
/*fmtString = escapeString(origFmt);*/
fmtString = xstrdup(origFmt);
if (parseFormat(fmtString, tags, extensions, &format, &numTokens,
NULL, PARSER_BEGIN, errmsg)) {
free(fmtString);
return NULL;
}
extCache = allocateExtensionCache(extensions);
answerAlloced = 1024;
answerLength = 0;
answer = xmalloc(answerAlloced);
*answer = '\0';
for (i = 0; i < numTokens; i++) {
const char * piece;
int pieceLength;
piece = singleSprintf(h, format + i, extensions, extCache, 0);
if (piece) {
pieceLength = strlen(piece);
if ((answerLength + pieceLength) >= answerAlloced) {
while ((answerLength + pieceLength) >= answerAlloced)
answerAlloced += 1024;
answer = xrealloc(answer, answerAlloced);
}
strcat(answer, piece);
answerLength += pieceLength;
free((void *)piece);
}
}
free(fmtString);
freeExtensionCache(extensions, extCache);
free(format);
return answer;
}
static char * octalFormat(int_32 type, const void * data,
char * formatPrefix, int padding, /*@unused@*/int element)
/*@modifies formatPrefix @*/
{
char * val;
if (type != RPM_INT32_TYPE) {
val = xstrdup(_("(not a number)"));
} else {
val = xmalloc(20 + padding);
strcat(formatPrefix, "o");
sprintf(val, formatPrefix, *((int_32 *) data));
}
return val;
}
static char * hexFormat(int_32 type, const void * data,
char * formatPrefix, int padding, /*@unused@*/int element)
/*@modifies formatPrefix @*/
{
char * val;
if (type != RPM_INT32_TYPE) {
val = xstrdup(_("(not a number)"));
} else {
val = xmalloc(20 + padding);
strcat(formatPrefix, "x");
sprintf(val, formatPrefix, *((int_32 *) data));
}
return val;
}
static char * realDateFormat(int_32 type, const void * data,
char * formatPrefix, int padding, /*@unused@*/int element,
char * strftimeFormat)
/*@modifies formatPrefix @*/
{
char * val;
struct tm * tstruct;
char buf[50];
if (type != RPM_INT32_TYPE) {
val = xstrdup(_("(not a number)"));
} else {
val = xmalloc(50 + padding);
strcat(formatPrefix, "s");
/* this is important if sizeof(int_32) ! sizeof(time_t) */
{ time_t dateint = *((int_32 *) data);
tstruct = localtime(&dateint);
}
(void)strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
sprintf(val, formatPrefix, buf);
}
return val;
}
static char * dateFormat(int_32 type, const void * data,
char * formatPrefix, int padding, int element)
/*@modifies formatPrefix @*/
{
return realDateFormat(type, data, formatPrefix, padding, element, "%c");
}
static char * dayFormat(int_32 type, const void * data,
char * formatPrefix, int padding, int element)
/*@modifies formatPrefix @*/
{
return realDateFormat(type, data, formatPrefix, padding, element,
"%a %b %d %Y");
}
static char * shescapeFormat(int_32 type, const void * data,
char * formatPrefix, int padding, /*@unused@*/int element)
/*@modifies formatPrefix @*/
{
char * result, * dst, * src, * buf;
if (type == RPM_INT32_TYPE) {
result = xmalloc(padding + 20);
strcat(formatPrefix, "d");
sprintf(result, formatPrefix, *((int_32 *) data));
} else {
buf = alloca(strlen(data) + padding + 2);
strcat(formatPrefix, "s");
sprintf(buf, formatPrefix, data);
result = dst = xmalloc(strlen(buf) * 4 + 3);
*dst++ = '\'';
for (src = buf; *src; src++) {
if (*src == '\'') {
*dst++ = '\'';
*dst++ = '\\';
*dst++ = '\'';
*dst++ = '\'';
} else {
*dst++ = *src;
}
}
*dst++ = '\'';
*dst = '\0';
}
return result;
}
const struct headerSprintfExtension headerDefaultFormats[] = {
{ HEADER_EXT_FORMAT, "octal", { octalFormat } },
{ HEADER_EXT_FORMAT, "hex", { hexFormat } },
{ HEADER_EXT_FORMAT, "date", { dateFormat } },
{ HEADER_EXT_FORMAT, "day", { dayFormat } },
{ HEADER_EXT_FORMAT, "shescape", { shescapeFormat } },
{ HEADER_EXT_LAST, NULL, { NULL } }
};
void headerCopyTags(Header headerFrom, Header headerTo, int *tagstocopy)
{
int *p;
if (headerFrom == headerTo)
return;
for (p = tagstocopy; *p != 0; p++) {
char *s;
int type, count;
if (headerIsEntry(headerTo, *p))
continue;
if (!headerGetEntry(headerFrom, *p, &type, (void **) &s, &count))
continue;
headerAddEntry(headerTo, *p, type, s, count);
if (s != NULL &&
(type == RPM_STRING_ARRAY_TYPE || type == RPM_I18NSTRING_TYPE))
free(s);
}
}