1 /* RPM - Copyright (C) 1995 Red Hat Software
3 * header.c - routines for managing rpm headers
6 /* Data written to file descriptors is in network byte order. */
7 /* Data read from file descriptors is expected to be in */
8 /* network byte order and is converted on the fly to host order. */
13 #define ntohl(_x) (_x)
14 #define ntohs(_x) (_x)
15 #define htonl(_x) (_x)
16 #define htons(_x) (_x)
18 #include <netinet/in.h>
19 #endif /* __LCLINT__ */
24 #define INDEX_MALLOC_SIZE 8
26 #define PARSER_BEGIN 0
27 #define PARSER_IN_ARRAY 1
28 #define PARSER_IN_EXPR 2
30 static unsigned char header_magic[4] = { 0x8e, 0xad, 0xe8, 0x01 };
32 /* handy -- this tells us alignments for defined elements as well */
33 static int typeSizes[] = {
34 /* RPM_NULL_TYPE */ -1,
35 /* RPM_CHAR_TYPE */ 1,
36 /* RPM_INT8_TYPE */ 1,
37 /* RPM_INT16_TYPE */ 2,
38 /* RPM_INT32_TYPE */ 4,
39 /* RPM_INT64_TYPE */ -1,
40 /* RPM_STRING_TYPE */ -1,
42 /* RPM_STRING_ARRAY_TYPE */ -1,
43 /* RPM_I18NSTRING_TYPE */ -1 };
46 struct indexEntry *index;
51 /*@refs@*/int usageCount;
57 int_32 offset; /* Offset from beginning of data segment,
58 only defined on disk */
63 struct entryInfo info;
64 /*@owned@*/ void * data;
65 int length; /* Computable, but why bother? */
69 /* if NULL tag element is invalid */
70 headerTagTagFunction ext;
80 struct extensionCache {
87 enum { PTOK_NONE = 0, PTOK_TAG, PTOK_ARRAY, PTOK_STRING, PTOK_COND } type;
90 /*@only@*/ struct sprintfToken * format;
93 struct sprintfTag tag;
95 /*@dependent@*/ char * string;
99 /*@only@*/ struct sprintfToken * ifFormat;
101 /*@only@*/ struct sprintfToken * elseFormat;
103 struct sprintfTag tag;
108 static void copyEntry(struct indexEntry * entry, /*@out@*/ int_32 * type,
109 /*@out@*/ void ** p, /*@out@*/ int_32 * c, int minimizeMemory)
116 *type = entry->info.type;
118 *c = entry->info.count;
123 switch (entry->info.type) {
124 case RPM_STRING_TYPE:
125 if (entry->info.count == 1) {
130 case RPM_STRING_ARRAY_TYPE:
131 case RPM_I18NSTRING_TYPE:
132 i = entry->info.count;
133 tableSize = i * sizeof(char *);
134 if (minimizeMemory) {
135 ptrEntry = *p = xmalloc(tableSize);
138 ptrEntry = *p = xmalloc(tableSize + entry->length); /* XXX memory leak */
139 chptr = ((char *) *p) + tableSize;
140 memcpy(chptr, entry->data, entry->length);
144 chptr = strchr(chptr, 0);
155 static int dataLength(int_32 type, const void * p, int_32 count, int onDisk)
157 int thisLen, length, i;
158 char ** src, * chptr;
162 case RPM_STRING_TYPE:
164 /* Special case -- p is just the string */
165 length = strlen(p) + 1;
168 /* This should not be allowed */
169 fprintf(stderr, _("grabData() RPM_STRING_TYPE count must be 1.\n"));
171 /*@notreached@*/ break;
173 case RPM_STRING_ARRAY_TYPE:
174 case RPM_I18NSTRING_TYPE:
175 /* This is like RPM_STRING_TYPE, except it's *always* an array */
176 /* Compute sum of length of all strings, including null terminators */
183 thisLen = strlen(chptr) + 1;
190 /* add one for null termination */
191 length += strlen(*src++) + 1;
197 if (typeSizes[type] != -1)
198 length = typeSizes[type] * count;
200 fprintf(stderr, _("Data type %d not supported\n"), (int) type);
210 /********************************************************************/
212 /* Header iteration and copying */
214 /********************************************************************/
216 struct headerIteratorS {
221 HeaderIterator headerInitIterator(Header h)
223 HeaderIterator hi = xmalloc(sizeof(struct headerIteratorS));
227 hi->h = headerLink(h);
232 void headerFreeIterator(HeaderIterator iter)
238 int headerNextIterator(HeaderIterator iter,
239 int_32 *tag, int_32 *type, void **p, int_32 *c)
242 int slot = iter->next_index;
244 if (slot == h->indexUsed) {
250 *tag = h->index[slot].info.tag;
252 copyEntry(h->index + slot, type, p, c, 0);
257 static int indexCmp(const void *ap, const void *bp)
261 a = ((struct indexEntry *)ap)->info.tag;
262 b = ((struct indexEntry *)bp)->info.tag;
273 void headerSort(Header h)
276 qsort(h->index, h->indexUsed, sizeof(struct indexEntry), indexCmp);
281 Header headerCopy(Header h)
283 int_32 tag, type, count;
285 HeaderIterator headerIter;
286 Header res = headerNew();
288 #if 0 /* XXX harmless, but headerInitIterator() does this anyways */
289 /* Sort the index -- not really necessary but some old apps may depend
290 on this and it certainly won't hurt anything */
293 headerIter = headerInitIterator(h);
295 while (headerNextIterator(headerIter, &tag, &type, &ptr, &count)) {
296 headerAddEntry(res, tag, type, ptr, count);
297 if (type == RPM_STRING_ARRAY_TYPE ||
298 type == RPM_I18NSTRING_TYPE) free(ptr);
303 headerFreeIterator(headerIter);
308 /********************************************************************/
310 /* Header loading and unloading */
312 /********************************************************************/
314 Header headerLoad(void *pv)
316 int_32 il; /* index length, data length */
318 const char * dataStart;
319 struct entryInfo * pe;
320 struct indexEntry * entry;
321 struct headerToken *h = xmalloc(sizeof(struct headerToken));
327 il = ntohl(*((int_32 *) p));
330 /* we can skip the data length -- we only store this to allow reading
334 h->indexAlloced = il;
336 h->index = xmalloc(sizeof(struct indexEntry) * il);
339 /* This assumes you only headerLoad() something you headerUnload()-ed */
342 pe = (struct entryInfo *) p;
343 dataStart = (char *) (pe + h->indexUsed);
345 for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++, pe++) {
346 entry->info.type = htonl(pe->type);
347 entry->info.tag = htonl(pe->tag);
348 entry->info.count = htonl(pe->count);
349 entry->info.offset = -1;
351 if (entry->info.type < RPM_MIN_TYPE ||
352 entry->info.type > RPM_MAX_TYPE) return NULL;
354 src = dataStart + htonl(pe->offset);
355 entry->length = dataLength(entry->info.type, src,
356 entry->info.count, 1);
357 entry->data = dst = xmalloc(entry->length);
359 /* copy data w/ endian conversions */
360 switch (entry->info.type) {
362 count = entry->info.count;
364 *((int_32 *)dst) = htonl(*((int_32 *)src));
365 src += sizeof(int_32);
366 dst += sizeof(int_32);
371 count = entry->info.count;
373 *((int_16 *)dst) = htons(*((int_16 *)src));
374 src += sizeof(int_16);
375 dst += sizeof(int_16);
380 memcpy(dst, src, entry->length);
388 static void *doHeaderUnload(Header h, /*@out@*/int * lengthPtr)
394 struct entryInfo * pe;
395 struct indexEntry * entry;
396 char * chptr, * src, * dataStart;
401 *lengthPtr = headerSizeof(h, 0);
402 pi = p = xmalloc(*lengthPtr);
404 *pi++ = htonl(h->indexUsed);
407 *pi++ = htonl(*lengthPtr - sizeof(int_32) - sizeof(int_32) -
408 (sizeof(struct entryInfo) * h->indexUsed));
410 pe = (struct entryInfo *) pi;
411 dataStart = chptr = (char *) (pe + h->indexUsed);
413 for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++, pe++) {
414 pe->type = htonl(entry->info.type);
415 pe->tag = htonl(entry->info.tag);
416 pe->count = htonl(entry->info.count);
419 type = entry->info.type;
420 if (typeSizes[type] > 1) {
421 diff = typeSizes[type] - ((chptr - dataStart) % typeSizes[type]);
422 if (diff != typeSizes[type]) {
423 memset(chptr, 0, diff);
428 pe->offset = htonl(chptr - dataStart);
430 /* copy data w/ endian conversions */
431 switch (entry->info.type) {
433 count = entry->info.count;
436 *((int_32 *)chptr) = htonl(*((int_32 *)src));
437 chptr += sizeof(int_32);
438 src += sizeof(int_32);
443 count = entry->info.count;
446 *((int_16 *)chptr) = htons(*((int_16 *)src));
447 chptr += sizeof(int_16);
448 src += sizeof(int_16);
453 memcpy(chptr, entry->data, entry->length);
454 chptr += entry->length;
462 void *headerUnload(Header h)
466 return doHeaderUnload(h, &length);
469 /********************************************************************/
471 /* Reading and writing headers */
473 /********************************************************************/
475 int headerWrite(FD_t fd, Header h, int magicp)
482 p = doHeaderUnload(h, &length);
485 nb = Fwrite(header_magic, sizeof(header_magic), 1, fd);
486 if (nb != sizeof(header_magic)) {
491 nb = Fwrite(&l, sizeof(l), 1, fd);
492 if (nb != sizeof(l)) {
498 nb = Fwrite(p, length, 1, fd);
508 Header headerRead(FD_t fd, int magicp)
521 if (magicp == HEADER_MAGIC_YES)
524 if (timedRead(fd, block, i * sizeof(*block)) != (i * sizeof(*block)))
528 if (magicp == HEADER_MAGIC_YES) {
530 if (memcmp(&magic, header_magic, sizeof(magic))) {
534 reserved = block[i++];
537 il = ntohl(block[i++]);
538 dl = ntohl(block[i++]);
540 totalSize = sizeof(int_32) + sizeof(int_32) +
541 (il * sizeof(struct entryInfo)) + dl;
544 * XXX Limit total size of header to 32Mb (~16 times largest known size).
546 if (totalSize > (32*1024*1024))
549 dataBlock = p = xmalloc(totalSize);
553 totalSize -= sizeof(int_32) + sizeof(int_32);
554 if (timedRead(fd, p, totalSize) != totalSize)
557 h = headerLoad(dataBlock);
564 int headerGzWrite(FD_t fd, Header h, int magicp)
571 p = doHeaderUnload(h, &length);
574 nb = Fwrite(header_magic, sizeof(header_magic), 1, fd);
575 if (nb != sizeof(header_magic)) {
580 nb = Fwrite(&l, sizeof(l), 1, fd);
581 if (nb != sizeof(l)) {
587 nb = Fwrite(p, length, 1, fd);
597 Header headerGzRead(FD_t fd, int magicp)
607 if (magicp == HEADER_MAGIC_YES) {
608 if (Fread(&magic, sizeof(magic), 1, fd) != sizeof(magic))
610 if (memcmp(&magic, header_magic, sizeof(magic))) {
614 if (Fread(&reserved, sizeof(reserved), 1, fd) != sizeof(reserved))
618 /* First read the index length (count of index entries) */
619 if (Fread(&il, sizeof(il), 1, fd) != sizeof(il))
624 /* Then read the data length (number of bytes) */
625 if (Fread(&dl, sizeof(dl), 1, fd) != sizeof(dl))
630 totalSize = sizeof(int_32) + sizeof(int_32) +
631 (il * sizeof(struct entryInfo)) + dl;
633 block = p = xmalloc(totalSize);
637 totalSize -= sizeof(int_32) + sizeof(int_32);
638 if (Fread(p, totalSize, 1, fd) != totalSize)
641 h = headerLoad(block);
648 /********************************************************************/
652 /********************************************************************/
654 void headerDump(Header h, FILE *f, int flags,
655 const struct headerTagTableEntry * tags)
658 struct indexEntry *p;
659 const struct headerTagTableEntry * tage;
663 /* First write out the length of the index (count of index entries) */
664 fprintf(f, "Entry count: %d\n", h->indexUsed);
666 /* Now write the index */
668 fprintf(f, "\n CT TAG TYPE "
670 for (i = 0; i < h->indexUsed; i++) {
671 switch (p->info.type) {
672 case RPM_NULL_TYPE: type = "NULL_TYPE"; break;
673 case RPM_CHAR_TYPE: type = "CHAR_TYPE"; break;
674 case RPM_BIN_TYPE: type = "BIN_TYPE"; break;
675 case RPM_INT8_TYPE: type = "INT8_TYPE"; break;
676 case RPM_INT16_TYPE: type = "INT16_TYPE"; break;
677 case RPM_INT32_TYPE: type = "INT32_TYPE"; break;
678 /*case RPM_INT64_TYPE: type = "INT64_TYPE"; break;*/
679 case RPM_STRING_TYPE: type = "STRING_TYPE"; break;
680 case RPM_STRING_ARRAY_TYPE: type = "STRING_ARRAY_TYPE"; break;
681 case RPM_I18NSTRING_TYPE: type = "I18N_STRING_TYPE"; break;
682 default: type = "(unknown)"; break;
686 while (tage->name && tage->val != p->info.tag) tage++;
693 fprintf(f, "Entry : %.3d (%d)%-14s %-18s 0x%.8x %.8d\n", i,
694 p->info.tag, tag, type, (unsigned) p->info.offset, (int)
697 if (flags & HEADER_DUMP_INLINE) {
699 int c = p->info.count;
702 /* Print the data inline */
703 switch (p->info.type) {
706 fprintf(f, " Data: %.3d 0x%08x (%d)\n", ct++,
707 (unsigned) *((int_32 *) dp),
708 (int) *((int_32 *) dp));
709 dp += sizeof(int_32);
715 fprintf(f, " Data: %.3d 0x%04x (%d)\n", ct++,
716 (unsigned) (*((int_16 *) dp) & 0xffff),
717 (int) *((int_16 *) dp));
718 dp += sizeof(int_16);
723 fprintf(f, " Data: %.3d 0x%02x (%d)\n", ct++,
724 (unsigned) (*((int_8 *) dp) & 0xff),
725 (int) *((int_8 *) dp));
731 fprintf(f, " Data: %.3d ", ct);
733 fprintf(f, "%02x ", (unsigned) (*(int_8 *)dp & 0xff));
745 char ch = (char) *((char *) dp);
746 fprintf(f, " Data: %.3d 0x%2x %c (%d)\n", ct++,
747 (unsigned)(ch & 0xff),
748 (isprint(ch) ? ch : ' '),
749 (int) *((char *) dp));
753 case RPM_STRING_TYPE:
754 case RPM_STRING_ARRAY_TYPE:
755 case RPM_I18NSTRING_TYPE:
757 fprintf(f, " Data: %.3d %s\n", ct++, (char *) dp);
763 fprintf(stderr, _("Data type %d not supprted\n"),
766 /*@notreached@*/ break;
773 /********************************************************************/
777 /********************************************************************/
779 static struct indexEntry *findEntry(Header h, int_32 tag, int_32 type)
781 struct indexEntry * entry, * entry2, * last;
782 struct indexEntry key;
784 if (!h->sorted) headerSort(h);
789 bsearch(&key, h->index, h->indexUsed, sizeof(struct indexEntry),
791 if (!entry) return NULL;
793 if (type == RPM_NULL_TYPE) return entry;
796 while (entry->info.tag == tag && entry->info.type != type &&
797 entry > h->index) entry--;
799 if (entry->info.tag == tag && entry->info.type == type)
802 last = h->index + h->indexUsed;
803 while (entry2->info.tag == tag && entry2->info.type != type &&
804 entry2 < last) entry2++;
806 if (entry->info.tag == tag && entry->info.type == type)
812 int headerIsEntry(Header h, int_32 tag)
814 return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
817 int headerGetRawEntry(Header h, int_32 tag, int_32 *type, void **p, int_32 *c)
819 struct indexEntry * entry;
821 if (p == NULL) return headerIsEntry(h, tag);
823 /* First find the tag */
824 entry = findEntry(h, tag, RPM_NULL_TYPE);
831 copyEntry(entry, type, p, c, 0);
836 static int headerMatchLocale(const char *td, const char *l, const char *le)
841 * The range [l,le) contains the next locale to match:
842 * ll[_CC][.EEEEE][@dddd]
844 * ll ISO language code (in lowercase).
845 * CC (optional) ISO coutnry code (in uppercase).
846 * EEEEE (optional) encoding (not really standardized).
847 * dddd (optional) dialect.
851 { const char *s, *ll, *CC, *EE, *dd;
854 /* Copy the buffer and parse out components on the fly. */
855 lbuf = alloca(le - l + 1);
856 for (s = l, ll = t = lbuf; *s; s++, t++) {
876 if (ll) /* ISO language should be lower case */
877 for (t = ll; *t; t++) *t = tolower(*t);
878 if (CC) /* ISO country code should be upper case */
879 for (t = CC; *t; t++) *t = toupper(*t);
881 /* There are a total of 16 cases to attempt to match. */
885 /* First try a complete match. */
886 if (strlen(td) == (le-l) && !strncmp(td, l, (le - l)))
889 /* Next, try stripping optional dialect and matching. */
890 for (fe = l; fe < le && *fe != '@'; fe++)
892 if (fe < le && !strncmp(td, l, (fe - l)))
895 /* Next, try stripping optional codeset and matching. */
896 for (fe = l; fe < le && *fe != '.'; fe++)
898 if (fe < le && !strncmp(td, l, (fe - l)))
901 /* Finally, try stripping optional country code and matching. */
902 for (fe = l; fe < le && *fe != '_'; fe++)
904 if (fe < le && !strncmp(td, l, (fe - l)))
910 /*@dependent@*/ static char *
911 headerFindI18NString(Header h, struct indexEntry *entry)
913 const char *lang, *l, *le;
914 struct indexEntry * table;
916 /* XXX Drepper sez' this is the order. */
917 if ((lang = getenv("LANGUAGE")) == NULL &&
918 (lang = getenv("LC_ALL")) == NULL &&
919 (lang = getenv("LC_MESSAGES")) == NULL &&
920 (lang = getenv("LANG")) == NULL)
923 if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
926 for (l = lang; *l; l = le) {
931 while (*l && *l == ':') /* skip leading colons */
935 for (le = l; *le && *le != ':'; le++) /* find end of this locale */
938 /* For each entry in the header ... */
939 for (langNum = 0, td = table->data, ed = entry->data;
940 langNum < entry->info.count;
941 langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
943 if (headerMatchLocale(td, l, le))
952 static int intGetEntry(Header h, int_32 tag, /*@out@*/int_32 *type, /*@out@*/void **p, /*@out@*/int_32 *c,
955 struct indexEntry * entry;
958 /* First find the tag */
959 entry = findEntry(h, tag, RPM_NULL_TYPE);
966 if (entry->info.type == RPM_I18NSTRING_TYPE) {
967 chptr = headerFindI18NString(h, entry);
969 if (type) *type = RPM_STRING_TYPE;
974 copyEntry(entry, type, p, c, minMem);
980 int headerGetEntryMinMemory(Header h, int_32 tag, int_32 *type, void **p,
983 return intGetEntry(h, tag, type, p, c, 1);
986 int headerGetEntry(Header h, int_32 tag, int_32 * type, void **p, int_32 * c)
988 return intGetEntry(h, tag, type, p, c, 0);
991 /********************************************************************/
993 /* Header creation and deletion */
995 /********************************************************************/
999 Header h = xmalloc(sizeof(struct headerToken));
1001 h->indexAlloced = INDEX_MALLOC_SIZE;
1002 h->index = xcalloc(h->indexAlloced, sizeof(struct indexEntry));
1011 void headerFree(Header h)
1015 if (--h->usageCount) return;
1016 for (i = 0; i < h->indexUsed; i++)
1017 free(h->index[i].data);
1020 /*@-refcounttrans@*/ free(h); /*@=refcounttrans@*/
1023 Header headerLink(Header h)
1029 int headerUsageCount(Header h)
1031 return h->usageCount;
1034 unsigned int headerSizeof(Header h, int magicp)
1042 size = sizeof(int_32); /* count of index entries */
1043 size += sizeof(int_32); /* length of data */
1044 size += sizeof(struct entryInfo) * h->indexUsed;
1049 for (i = 0; i < h->indexUsed; i++) {
1051 type = h->index[i].info.type;
1052 if (typeSizes[type] > 1) {
1053 diff = typeSizes[type] - (size % typeSizes[type]);
1054 if (diff != typeSizes[type]) {
1059 size += h->index[i].length;
1065 static void copyData(int_32 type, /*@out@*/void * dstPtr, const void * srcPtr, int_32 c,
1073 case RPM_STRING_ARRAY_TYPE:
1074 case RPM_I18NSTRING_TYPE:
1075 /* Otherwise, p is char** */
1077 src = (const char **) srcPtr;
1080 len = *src ? strlen(*src) + 1 : 0;
1081 memcpy(dst, *src, len);
1088 memcpy(dstPtr, srcPtr, dataLength);
1093 static void * grabData(int_32 type, /*@out@*/const void * p, int_32 c, int * lengthPtr)
1098 length = dataLength(type, p, c, 0);
1099 data = xmalloc(length);
1101 copyData(type, data, p, c, length);
1103 *lengthPtr = length;
1107 /********************************************************************/
1109 /* Adding and modifying entries */
1111 /********************************************************************/
1113 int headerAddEntry(Header h, int_32 tag, int_32 type, const void *p, int_32 c)
1115 struct indexEntry *entry;
1120 fprintf(stderr, _("Bad count for headerAddEntry(): %d\n"), (int) c);
1125 /* Allocate more index space if necessary */
1126 if (h->indexUsed == h->indexAlloced) {
1127 h->indexAlloced += INDEX_MALLOC_SIZE;
1128 h->index = xrealloc(h->index,
1129 h->indexAlloced * sizeof(struct indexEntry));
1132 /* Fill in the index */
1133 entry = h->index + h->indexUsed++;
1134 entry->info.tag = tag;
1135 entry->info.type = type;
1136 entry->info.count = c;
1137 entry->info.offset = -1;
1139 entry->data = grabData(type, p, c, &entry->length);
1147 headerGetLangs(Header h)
1149 char **s, *e, **table;
1152 if (!headerGetRawEntry(h, HEADER_I18NTABLE, &type, (void **)&s, &count))
1155 if ((table = (char **)xcalloc((count+1), sizeof(char *))) == NULL)
1158 for (i = 0, e = *s; i < count > 0; i++, e += strlen(e)+1) {
1161 table[count] = NULL;
1166 int headerAddI18NString(Header h, int_32 tag, const char * string, const char * lang)
1168 struct indexEntry * table, * entry;
1170 const char ** strArray;
1176 table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
1177 entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
1179 if (!table && entry) {
1180 return 0; /* this shouldn't ever happen!! */
1183 if (!table && !entry) {
1184 const char * charArray[2];
1186 if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
1187 charArray[count++] = "C";
1189 charArray[count++] = "C";
1190 charArray[count++] = lang;
1192 if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE,
1195 table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
1198 if (!lang) lang = "C";
1200 chptr = table->data;
1201 for (langNum = 0; langNum < table->info.count; langNum++) {
1202 if (!strcmp(chptr, lang)) break;
1203 chptr += strlen(chptr) + 1;
1206 if (langNum >= table->info.count) {
1207 length = strlen(lang) + 1;
1208 table->data = xrealloc(table->data, table->length + length);
1209 memcpy(((char *)table->data) + table->length, lang, length);
1210 table->length += length;
1211 table->info.count++;
1215 strArray = alloca(sizeof(*strArray) * (langNum + 1));
1216 for (i = 0; i < langNum; i++)
1218 strArray[langNum] = string;
1219 return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, strArray,
1221 } else if (langNum >= entry->info.count) {
1222 ghosts = langNum - entry->info.count;
1224 length = strlen(string) + 1 + ghosts;
1225 entry->data = xrealloc(entry->data, entry->length + length);
1227 memset(((char *)entry->data) + entry->length, '\0', ghosts);
1228 strcpy(((char *)entry->data) + entry->length + ghosts, string);
1230 entry->length += length;
1231 entry->info.count = langNum + 1;
1233 char *b, *be, *e, *ee, *t;
1236 /* Set beginning/end pointers to previous data */
1237 b = be = e = ee = entry->data;
1238 for (i = 0; i < table->info.count; i++) {
1241 ee += strlen(ee) + 1;
1246 /* Get storage for new buffer */
1248 sn = strlen(string) + 1;
1250 length = bn + sn + en;
1251 t = buf = xmalloc(length);
1253 /* Copy values into new storage */
1256 memcpy(t, string, sn);
1261 /* Replace I18N string array */
1262 entry->length -= strlen(be) + 1;
1263 entry->length += sn;
1271 /* if there are multiple entries with this tag, the first one gets replaced */
1272 int headerModifyEntry(Header h, int_32 tag, int_32 type, void *p, int_32 c)
1274 struct indexEntry *entry;
1277 /* First find the tag */
1278 entry = findEntry(h, tag, type);
1283 /* make sure entry points to the first occurence of this tag */
1284 while (entry > h->index && (entry - 1)->info.tag == tag)
1287 /* free after we've grabbed the new data in case the two are intertwined;
1288 that's a bad idea but at least we won't break */
1289 oldData = entry->data;
1291 entry->info.count = c;
1292 entry->info.type = type;
1293 entry->data = grabData(type, p, c, &entry->length);
1300 int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
1303 if (findEntry(h, tag, type)) {
1304 return headerAppendEntry(h, tag, type, p, c);
1306 return headerAddEntry(h, tag, type, p, c);
1310 int headerAppendEntry(Header h, int_32 tag, int_32 type, void * p, int_32 c)
1312 struct indexEntry *entry;
1315 /* First find the tag */
1316 entry = findEntry(h, tag, type);
1321 if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
1322 /* we can't do this */
1326 length = dataLength(type, p, c, 0);
1328 entry->data = xrealloc(entry->data, entry->length + length);
1329 copyData(type, ((char *) entry->data) + entry->length, p, c, length);
1331 entry->length += length;
1333 entry->info.count += c;
1338 int headerRemoveEntry(Header h, int_32 tag)
1340 struct indexEntry * entry, * last;
1342 entry = findEntry(h, tag, RPM_NULL_TYPE);
1343 if (!entry) return 1;
1345 /* make sure entry points to the first occurence of this tag */
1346 while (entry > h->index && (entry - 1)->info.tag == tag)
1349 /* We might be better off just counting the number of items off the
1350 end and issuing one big memcpy, but memcpy() doesn't have to work
1351 on overlapping regions thanks to ANSI <sigh>. A alloca() and two
1352 memcpy() would probably still be a win (as our moving from the
1353 end to the middle isn't very nice to the qsort() we'll have to
1354 do to make up for this!), but I'm too lazy to implement it. Just
1355 remember that this repeating this is basically nlogn thanks to this
1356 dumb implementation (but n is the best we'd do anyway) */
1358 last = h->index + h->indexUsed;
1359 while (entry->info.tag == tag && entry < last) {
1361 *(entry++) = *(--last);
1363 h->indexUsed = last - h->index;
1370 static char escapedChar(const char ch)
1373 case 'a': return '\a';
1374 case 'b': return '\b';
1375 case 'f': return '\f';
1376 case 'n': return '\n';
1377 case 'r': return '\r';
1378 case 't': return '\t';
1379 case 'v': return '\v';
1385 static void freeFormat( /*@only@*/ struct sprintfToken * format, int num)
1389 for (i = 0; i < num; i++) {
1390 switch (format[i].type) {
1392 freeFormat(format[i].u.array.format, format[i].u.array.numTokens);
1395 freeFormat(format[i].u.cond.ifFormat,
1396 format[i].u.cond.numIfTokens);
1397 freeFormat(format[i].u.cond.elseFormat,
1398 format[i].u.cond.numElseTokens);
1410 static void findTag(char * name, const struct headerTagTableEntry * tags,
1411 const struct headerSprintfExtension * extensions,
1412 /*@out@*/const struct headerTagTableEntry ** tagMatch,
1413 /*@out@*/const struct headerSprintfExtension ** extMatch)
1415 const struct headerTagTableEntry * entry;
1416 const struct headerSprintfExtension * ext;
1423 if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
1424 tagname = alloca(strlen(name) + 10);
1425 strcpy(tagname, "RPMTAG_");
1426 strcat(tagname, name);
1431 for (entry = tags; entry->name; entry++)
1432 if (!strcasecmp(entry->name, tagname)) break;
1437 ext = extensions, i =0;
1438 while (ext->type != HEADER_EXT_LAST) {
1439 if (ext->type == HEADER_EXT_TAG &&
1440 !strcasecmp(ext->name, tagname)) {
1444 if (ext->type == HEADER_EXT_MORE)
1451 if (ext->type == HEADER_EXT_TAG) {
1458 static int parseExpression(struct sprintfToken * token, char * str,
1459 const struct headerTagTableEntry * tags,
1460 const struct headerSprintfExtension * extensions,
1461 /*@out@*/char ** endPtr, /*@out@*/const char ** error);
1463 static int parseFormat(char * str, const struct headerTagTableEntry * tags,
1464 const struct headerSprintfExtension * extensions,
1465 /*@out@*/struct sprintfToken ** formatPtr, /*@out@*/int * numTokensPtr,
1466 /*@out@*/char ** endPtr, int state, /*@out@*/const char ** error)
1468 char * chptr, * start, * next, * dst;
1469 struct sprintfToken * format;
1472 const struct headerTagTableEntry * entry;
1473 const struct headerSprintfExtension * ext;
1477 /* upper limit on number of individual formats */
1479 for (chptr = str; *chptr; chptr++)
1480 if (*chptr == '%') numTokens++;
1481 numTokens = numTokens * 2 + 1;
1483 format = xcalloc(numTokens, sizeof(*format));
1484 if (endPtr) *endPtr = NULL;
1488 while (*start && !done) {
1492 if (*(start + 1) == '%') {
1493 if (currToken < 0 || format[currToken].type != PTOK_STRING) {
1495 format[currToken].type = PTOK_STRING;
1496 dst = format[currToken].u.string.string = start;
1503 break; /* out of switch */
1510 if (*start == '|') {
1514 if (parseExpression(format + currToken, start, tags,
1515 extensions, &newEnd, error)) {
1516 freeFormat(format, numTokens);
1521 format[currToken].u.tag.format = start;
1522 format[currToken].u.tag.pad = 0;
1523 format[currToken].u.tag.justOne = 0;
1524 format[currToken].u.tag.arrayCount = 0;
1527 while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
1528 if (!*chptr || *chptr == '%') {
1529 *error = _("missing { after %");
1530 freeFormat(format, numTokens);
1536 while (start < chptr) {
1537 if (isdigit(*start)) {
1538 i = strtoul(start, &start, 10);
1539 format[currToken].u.tag.pad += i;
1545 if (*start == '=') {
1546 format[currToken].u.tag.justOne = 1;
1548 } else if (*start == '#') {
1549 format[currToken].u.tag.justOne = 1;
1550 format[currToken].u.tag.arrayCount = 1;
1555 while (*next && *next != '}') next++;
1557 *error = _("missing } after %{");
1558 freeFormat(format, numTokens);
1564 while (*chptr && *chptr != ':') chptr++;
1569 *error = _("empty tag format");
1570 freeFormat(format, numTokens);
1573 format[currToken].u.tag.type = chptr;
1575 format[currToken].u.tag.type = NULL;
1579 *error = _("empty tag name");
1580 freeFormat(format, numTokens);
1585 findTag(start, tags, extensions, &entry, &ext);
1588 format[currToken].u.tag.ext = NULL;
1589 format[currToken].u.tag.tag = entry->val;
1591 format[currToken].u.tag.ext = ext->u.tagFunction;
1592 format[currToken].u.tag.extNum = ext - extensions;
1594 *error = _("unknown tag");
1595 freeFormat(format, numTokens);
1599 format[currToken].type = PTOK_TAG;
1611 if (parseFormat(start, tags, extensions,
1612 &format[currToken].u.array.format,
1613 &format[currToken].u.array.numTokens,
1614 &start, PARSER_IN_ARRAY, error)) {
1615 freeFormat(format, numTokens);
1620 *error = _("] expected at end of array");
1621 freeFormat(format, numTokens);
1627 format[currToken].type = PTOK_ARRAY;
1633 if ((*start == ']' && state != PARSER_IN_ARRAY) ||
1634 (*start == '}' && state != PARSER_IN_EXPR)) {
1636 *error = _("unexpected ]");
1638 *error = _("unexpected }");
1639 freeFormat(format, numTokens);
1648 if (currToken < 0 || format[currToken].type != PTOK_STRING) {
1650 format[currToken].type = PTOK_STRING;
1651 dst = format[currToken].u.string.string = start;
1654 if (*start == '\\') {
1656 *dst++ = escapedChar(*start++);
1667 for (i = 0; i < currToken; i++) {
1668 if (format[i].type == PTOK_STRING)
1669 format[i].u.string.len = strlen(format[i].u.string.string);
1672 *numTokensPtr = currToken;
1673 *formatPtr = format;
1678 static int parseExpression(struct sprintfToken * token, char * str,
1679 const struct headerTagTableEntry * tags,
1680 const struct headerSprintfExtension * extensions,
1681 /*@out@*/ char ** endPtr, /*@out@*/ const char ** error)
1683 char * chptr, * end;
1684 const struct headerTagTableEntry * tag;
1685 const struct headerSprintfExtension * ext;
1689 while (*chptr && *chptr != '?') chptr++;
1691 if (*chptr != '?') {
1692 *error = _("? expected in expression");
1698 if (*chptr != '{') {
1699 *error = _("{ expected after ? in expression");
1705 if (parseFormat(chptr, tags, extensions, &token->u.cond.ifFormat,
1706 &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR, error))
1709 *error = _("} expected in expression");
1710 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1711 token->u.cond.ifFormat = NULL;
1716 if (*chptr != ':' && *chptr != '|') {
1717 *error = _(": expected following ? subexpression");
1718 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1719 token->u.cond.ifFormat = NULL;
1723 if (*chptr == '|') {
1724 parseFormat(xstrdup(""), tags, extensions, &token->u.cond.elseFormat,
1725 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR,
1730 if (*chptr != '{') {
1731 *error = _("{ expected after : in expression");
1732 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1733 token->u.cond.ifFormat = NULL;
1739 if (parseFormat(chptr, tags, extensions, &token->u.cond.elseFormat,
1740 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR,
1744 *error = _("} expected in expression");
1745 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1746 token->u.cond.ifFormat = NULL;
1751 if (*chptr != '|') {
1752 *error = _("| expected at end of expression");
1753 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1754 token->u.cond.ifFormat = NULL;
1755 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
1756 token->u.cond.elseFormat = NULL;
1765 findTag(str, tags, extensions, &tag, &ext);
1768 token->u.cond.tag.ext = NULL;
1769 token->u.cond.tag.tag = tag->val;
1771 token->u.cond.tag.ext = ext->u.tagFunction;
1772 token->u.cond.tag.extNum = ext - extensions;
1774 token->u.cond.tag.ext = NULL;
1775 token->u.cond.tag.tag = -1;
1778 token->type = PTOK_COND;
1783 static int getExtension(Header h, headerTagTagFunction fn, /*@out@*/int_32 * typeptr,
1784 /*@out@*/void ** data, /*@out@*/int_32 * countptr,
1785 struct extensionCache * ext)
1788 if (fn(h, &ext->type, &ext->data, &ext->count, &ext->freeit))
1793 *typeptr = ext->type;
1795 *countptr = ext->count;
1800 static char * formatValue(struct sprintfTag * tag, Header h,
1801 const struct headerSprintfExtension * extensions,
1802 struct extensionCache * extCache, int element)
1808 unsigned int intVal;
1813 headerTagFormatFunction tagtype = NULL;
1814 const struct headerSprintfExtension * ext;
1817 if (getExtension(h, tag->ext, &type, &data, &count,
1818 extCache + tag->extNum)) {
1820 type = RPM_STRING_TYPE;
1821 data = "(none)"; /* XXX i18n? NO!, sez; gafton */
1824 if (!headerGetEntry(h, tag->tag, &type, &data, &count)){
1826 type = RPM_STRING_TYPE;
1827 data = "(none)"; /* XXX i18n? NO!, sez; gafton */
1833 if (tag->arrayCount) {
1834 if (type == RPM_STRING_ARRAY_TYPE) free(data);
1839 type = RPM_INT32_TYPE;
1843 strcat(buf, tag->format);
1847 while (ext->type != HEADER_EXT_LAST) {
1848 if (ext->type == HEADER_EXT_FORMAT &&
1849 !strcmp(ext->name, tag->type)) {
1850 tagtype = ext->u.formatFunction;
1854 if (ext->type == HEADER_EXT_MORE)
1862 case RPM_STRING_ARRAY_TYPE:
1866 val = tagtype(RPM_STRING_TYPE, strarray[element], buf, tag->pad, 0);
1872 len = strlen(strarray[element]) + tag->pad + 20;
1874 sprintf(val, buf, strarray[element]);
1877 if (mayfree) free(data);
1881 case RPM_STRING_TYPE:
1883 val = tagtype(RPM_STRING_ARRAY_TYPE, data, buf, tag->pad, 0);
1889 len = strlen(data) + tag->pad + 20;
1891 sprintf(val, buf, data);
1897 case RPM_INT16_TYPE:
1898 case RPM_INT32_TYPE:
1901 case RPM_INT8_TYPE: intVal = *(((int_8 *) data) + element); break;
1902 case RPM_INT16_TYPE: intVal = *(((uint_16 *) data) + element); break;
1903 default: /* keep -Wall quiet */
1904 case RPM_INT32_TYPE: intVal = *(((int_32 *) data) + element);
1908 val = tagtype(RPM_INT32_TYPE, &intVal, buf, tag->pad, element);
1913 len = 10 + tag->pad + 20;
1915 sprintf(val, buf, intVal);
1921 strcpy(val, _("(unknown type)"));
1928 static char * singleSprintf(Header h, struct sprintfToken * token,
1929 const struct headerSprintfExtension * extensions,
1930 struct extensionCache * extCache, int element)
1932 char * val, * thisItem;
1939 struct sprintfToken * condFormat;
1942 /* we assume the token and header have been validated already! */
1944 switch (token->type) {
1949 val = xmalloc(token->u.string.len + 1);
1950 strcpy(val, token->u.string.string);
1954 val = formatValue(&token->u.tag, h, extensions, extCache,
1955 token->u.tag.justOne ? 0 : element);
1959 if (token->u.cond.tag.ext ||
1960 headerIsEntry(h, token->u.cond.tag.tag)) {
1961 condFormat = token->u.cond.ifFormat;
1962 condNumFormats = token->u.cond.numIfTokens;
1964 condFormat = token->u.cond.elseFormat;
1965 condNumFormats = token->u.cond.numElseTokens;
1968 alloced = condNumFormats * 20;
1969 val = xmalloc(alloced ? alloced : 1);
1973 for (i = 0; i < condNumFormats; i++) {
1974 thisItem = singleSprintf(h, condFormat + i,
1975 extensions, extCache, element);
1976 thisItemLen = strlen(thisItem);
1977 if ((thisItemLen + len) >= alloced) {
1978 alloced = (thisItemLen + len) + 200;
1979 val = xrealloc(val, alloced);
1981 strcat(val, thisItem);
1990 for (i = 0; i < token->u.array.numTokens; i++) {
1991 if (token->u.array.format[i].type != PTOK_TAG ||
1992 token->u.array.format[i].u.tag.arrayCount ||
1993 token->u.array.format[i].u.tag.justOne) continue;
1995 if (token->u.array.format[i].u.tag.ext) {
1996 if (getExtension(h, token->u.array.format[i].u.tag.ext,
1997 &type, &data, &numElements,
1999 token->u.array.format[i].u.tag.extNum))
2002 if (!headerGetEntry(h, token->u.array.format[i].u.tag.tag,
2003 &type, (void **) &val, &numElements))
2005 if (type == RPM_STRING_ARRAY_TYPE) free(val);
2010 if (numElements == -1) {
2012 strcpy(val, "(none)"); /* XXX i18n? NO!, sez; gafton */
2014 alloced = numElements * token->u.array.numTokens * 20;
2015 val = xmalloc(alloced);
2019 for (j = 0; j < numElements; j++) {
2020 for (i = 0; i < token->u.array.numTokens; i++) {
2021 thisItem = singleSprintf(h, token->u.array.format + i,
2022 extensions, extCache, j);
2023 thisItemLen = strlen(thisItem);
2024 if ((thisItemLen + len) >= alloced) {
2025 alloced = (thisItemLen + len) + 200;
2026 val = xrealloc(val, alloced);
2028 strcat(val, thisItem);
2041 static struct extensionCache * allocateExtensionCache(
2042 const struct headerSprintfExtension * extensions)
2044 const struct headerSprintfExtension * ext = extensions;
2047 while (ext->type != HEADER_EXT_LAST) {
2049 if (ext->type == HEADER_EXT_MORE)
2055 return xcalloc(i, sizeof(struct extensionCache));
2058 static void freeExtensionCache(const struct headerSprintfExtension * extensions,
2059 /*@only@*/struct extensionCache * cache)
2061 const struct headerSprintfExtension * ext = extensions;
2064 while (ext->type != HEADER_EXT_LAST) {
2065 if (cache[i].freeit) free(cache[i].data);
2068 if (ext->type == HEADER_EXT_MORE)
2077 char * headerSprintf(Header h, const char * origFmt,
2078 const struct headerTagTableEntry * tags,
2079 const struct headerSprintfExtension * extensions,
2080 const char ** error)
2083 struct sprintfToken * format;
2085 char * answer, * piece;
2090 struct extensionCache * extCache;
2092 /*fmtString = escapeString(origFmt);*/
2093 fmtString = xstrdup(origFmt);
2095 if (parseFormat(fmtString, tags, extensions, &format, &numTokens,
2096 NULL, PARSER_BEGIN, error)) {
2101 extCache = allocateExtensionCache(extensions);
2103 answerAlloced = 1024;
2105 answer = xmalloc(answerAlloced);
2108 for (i = 0; i < numTokens; i++) {
2109 piece = singleSprintf(h, format + i, extensions, extCache, 0);
2111 pieceLength = strlen(piece);
2112 if ((answerLength + pieceLength) >= answerAlloced) {
2113 while ((answerLength + pieceLength) >= answerAlloced)
2114 answerAlloced += 1024;
2115 answer = xrealloc(answer, answerAlloced);
2118 strcat(answer, piece);
2119 answerLength += pieceLength;
2125 freeExtensionCache(extensions, extCache);
2131 static char * octalFormat(int_32 type, const void * data,
2132 char * formatPrefix, int padding, /*@unused@*/int element)
2136 if (type != RPM_INT32_TYPE) {
2138 strcpy(val, _("(not a number)"));
2140 val = xmalloc(20 + padding);
2141 strcat(formatPrefix, "o");
2142 sprintf(val, formatPrefix, *((int_32 *) data));
2148 static char * hexFormat(int_32 type, const void * data,
2149 char * formatPrefix, int padding, /*@unused@*/int element)
2153 if (type != RPM_INT32_TYPE) {
2155 strcpy(val, _("(not a number)"));
2157 val = xmalloc(20 + padding);
2158 strcat(formatPrefix, "x");
2159 sprintf(val, formatPrefix, *((int_32 *) data));
2165 static char * realDateFormat(int_32 type, const void * data,
2166 char * formatPrefix, int padding, /*@unused@*/int element,
2167 char * strftimeFormat)
2170 struct tm * tstruct;
2173 if (type != RPM_INT32_TYPE) {
2175 strcpy(val, _("(not a number)"));
2177 val = xmalloc(50 + padding);
2178 strcat(formatPrefix, "s");
2180 /* this is important if sizeof(int_32) ! sizeof(time_t) */
2181 { time_t dateint = *((int_32 *) data);
2182 tstruct = localtime(&dateint);
2184 (void)strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
2185 sprintf(val, formatPrefix, buf);
2191 static char * dateFormat(int_32 type, const void * data,
2192 char * formatPrefix, int padding, int element)
2194 return realDateFormat(type, data, formatPrefix, padding, element, "%c");
2197 static char * dayFormat(int_32 type, const void * data,
2198 char * formatPrefix, int padding, int element)
2200 return realDateFormat(type, data, formatPrefix, padding, element,
2204 static char * shescapeFormat(int_32 type, const void * data,
2205 char * formatPrefix, int padding, /*@unused@*/int element)
2207 char * result, * dst, * src, * buf;
2209 if (type == RPM_INT32_TYPE) {
2210 result = xmalloc(padding + 20);
2211 strcat(formatPrefix, "d");
2212 sprintf(result, formatPrefix, *((int_32 *) data));
2214 buf = alloca(strlen(data) + padding + 2);
2215 strcat(formatPrefix, "s");
2216 sprintf(buf, formatPrefix, data);
2218 result = dst = xmalloc(strlen(buf) * 4 + 3);
2220 for (src = buf; *src; src++) {
2238 const struct headerSprintfExtension headerDefaultFormats[] = {
2239 { HEADER_EXT_FORMAT, "octal", { octalFormat } },
2240 { HEADER_EXT_FORMAT, "hex", { hexFormat } },
2241 { HEADER_EXT_FORMAT, "date", { dateFormat } },
2242 { HEADER_EXT_FORMAT, "day", { dayFormat } },
2243 { HEADER_EXT_FORMAT, "shescape", { shescapeFormat } },
2244 { HEADER_EXT_LAST, NULL, { NULL } }
2247 void headerCopyTags(Header headerFrom, Header headerTo, int *tagstocopy)
2251 if (headerFrom == headerTo)
2254 for (p = tagstocopy; *p != 0; p++) {
2257 if (headerIsEntry(headerTo, *p))
2259 if (!headerGetEntry(headerFrom, *p, &type, (void **) &s, &count))
2261 headerAddEntry(headerTo, *p, type, s, count);
2263 (type == RPM_STRING_ARRAY_TYPE || type == RPM_I18NSTRING_TYPE))