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. */
12 #include <netinet/in.h>
17 #define INDEX_MALLOC_SIZE 8
19 #define PARSER_BEGIN 0
20 #define PARSER_IN_ARRAY 1
21 #define PARSER_IN_EXPR 2
23 static unsigned char header_magic[4] = { 0x8e, 0xad, 0xe8, 0x01 };
25 /* handy -- this tells us alignments for defined elements as well */
26 static int typeSizes[] = {
27 /* RPM_NULL_TYPE */ -1,
28 /* RPM_CHAR_TYPE */ 1,
29 /* RPM_INT8_TYPE */ 1,
30 /* RPM_INT16_TYPE */ 2,
31 /* RPM_INT32_TYPE */ 4,
32 /* RPM_INT64_TYPE */ -1,
33 /* RPM_STRING_TYPE */ -1,
35 /* RPM_STRING_ARRAY_TYPE */ -1,
36 /* RPM_I18NSTRING_TYPE */ -1 };
39 struct indexEntry *index;
50 int_32 offset; /* Offset from beginning of data segment,
51 only defined on disk */
56 struct entryInfo info;
58 int length; /* Computable, but why bother? */
62 /* if NULL tag element is invalid */
63 headerTagTagFunction ext;
73 struct extensionCache {
80 enum { PTOK_NONE = 0, PTOK_TAG, PTOK_ARRAY, PTOK_STRING, PTOK_COND } type;
83 struct sprintfToken * format;
86 struct sprintfTag tag;
92 struct sprintfToken * ifFormat;
94 struct sprintfToken * elseFormat;
96 struct sprintfTag tag;
101 static void copyEntry(struct indexEntry * entry,
102 int_32 *type, void **p, int_32 *c,
110 *type = entry->info.type;
112 *c = entry->info.count;
117 switch (entry->info.type) {
118 case RPM_STRING_TYPE:
119 if (entry->info.count == 1) {
124 case RPM_STRING_ARRAY_TYPE:
125 case RPM_I18NSTRING_TYPE:
126 i = entry->info.count;
127 tableSize = i * sizeof(char *);
128 if (minimizeMemory) {
129 ptrEntry = *p = malloc(tableSize);
132 ptrEntry = *p = malloc(tableSize + entry->length);
133 chptr = ((char *) *p) + tableSize;
134 memcpy(chptr, entry->data, entry->length);
138 chptr = strchr(chptr, 0);
148 static int dataLength(int_32 type, void * p, int_32 count, int onDisk)
150 int thisLen, length, i;
151 char ** src, * chptr;
155 case RPM_STRING_TYPE:
157 /* Special case -- p is just the string */
158 length = strlen(p) + 1;
161 /* This should not be allowed */
162 fprintf(stderr, _("grabData() RPM_STRING_TYPE count must be 1.\n"));
165 case RPM_STRING_ARRAY_TYPE:
166 case RPM_I18NSTRING_TYPE:
167 /* This is like RPM_STRING_TYPE, except it's *always* an array */
168 /* Compute sum of length of all strings, including null terminators */
175 thisLen = strlen(chptr) + 1;
182 /* add one for null termination */
183 length += strlen(*src++) + 1;
189 if (typeSizes[type] != -1)
190 length = typeSizes[type] * count;
192 fprintf(stderr, _("Data type %d not supported\n"), (int) type);
200 /********************************************************************/
202 /* Header iteration and copying */
204 /********************************************************************/
206 struct headerIteratorS {
211 HeaderIterator headerInitIterator(Header h)
213 HeaderIterator hi = malloc(sizeof(struct headerIteratorS));
222 void headerFreeIterator(HeaderIterator iter)
227 int headerNextIterator(HeaderIterator iter,
228 int_32 *tag, int_32 *type, void **p, int_32 *c)
231 int slot = iter->next_index;
233 if (slot == h->indexUsed) {
239 *tag = h->index[slot].info.tag;
241 copyEntry(h->index + slot, type, p, c, 0);
246 static int indexCmp(const void *ap, const void *bp)
250 a = ((struct indexEntry *)ap)->info.tag;
251 b = ((struct indexEntry *)bp)->info.tag;
262 void headerSort(Header h)
265 qsort(h->index, h->indexUsed, sizeof(struct indexEntry), indexCmp);
270 Header headerCopy(Header h)
272 int_32 tag, type, count;
274 HeaderIterator headerIter;
275 Header res = headerNew();
277 #if 0 /* XXX harmless, but headerInitIterator() does this anyways */
278 /* Sort the index -- not really necessary but some old apps may depend
279 on this and it certainly won't hurt anything */
282 headerIter = headerInitIterator(h);
284 while (headerNextIterator(headerIter, &tag, &type, &ptr, &count)) {
285 headerAddEntry(res, tag, type, ptr, count);
286 if (type == RPM_STRING_ARRAY_TYPE ||
287 type == RPM_I18NSTRING_TYPE) free(ptr);
292 headerFreeIterator(headerIter);
297 /********************************************************************/
299 /* Header loading and unloading */
301 /********************************************************************/
303 Header headerLoad(void *pv)
305 int_32 il; /* index length, data length */
308 struct entryInfo * pe;
309 struct indexEntry * entry;
310 struct headerToken *h = malloc(sizeof(struct headerToken));
315 il = ntohl(*((int_32 *) p));
318 /* we can skip the data length -- we only store this to allow reading
322 h->indexAlloced = il;
324 h->index = malloc(il * sizeof(struct indexEntry));
327 /* This assumes you only headerLoad() something you headerUnload()-ed */
330 pe = (struct entryInfo *) p;
331 dataStart = (char *) (pe + h->indexUsed);
333 for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++, pe++) {
334 entry->info.type = htonl(pe->type);
335 entry->info.tag = htonl(pe->tag);
336 entry->info.count = htonl(pe->count);
337 entry->info.offset = -1;
339 if (entry->info.type < RPM_MIN_TYPE ||
340 entry->info.type > RPM_MAX_TYPE) return NULL;
342 src = dataStart + htonl(pe->offset);
343 entry->length = dataLength(entry->info.type, src,
344 entry->info.count, 1);
345 entry->data = dst = malloc(entry->length);
347 /* copy data w/ endian conversions */
348 switch (entry->info.type) {
350 count = entry->info.count;
352 *((int_32 *)dst) = htonl(*((int_32 *)src));
353 src += sizeof(int_32);
354 dst += sizeof(int_32);
359 count = entry->info.count;
361 *((int_16 *)dst) = htons(*((int_16 *)src));
362 src += sizeof(int_16);
363 dst += sizeof(int_16);
368 memcpy(dst, src, entry->length);
375 static void *doHeaderUnload(Header h, int * lengthPtr)
381 struct entryInfo * pe;
382 struct indexEntry * entry;
383 char * chptr, * src, * dataStart;
388 *lengthPtr = headerSizeof(h, 0);
389 pi = p = malloc(*lengthPtr);
391 *pi++ = htonl(h->indexUsed);
394 *pi++ = htonl(*lengthPtr - sizeof(int_32) - sizeof(int_32) -
395 (sizeof(struct entryInfo) * h->indexUsed));
397 pe = (struct entryInfo *) pi;
398 dataStart = chptr = (char *) (pe + h->indexUsed);
400 for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++, pe++) {
401 pe->type = htonl(entry->info.type);
402 pe->tag = htonl(entry->info.tag);
403 pe->count = htonl(entry->info.count);
406 type = entry->info.type;
407 if (typeSizes[type] > 1) {
408 diff = typeSizes[type] - ((chptr - dataStart) % typeSizes[type]);
409 if (diff != typeSizes[type]) {
410 memset(chptr, 0, diff);
415 pe->offset = htonl(chptr - dataStart);
417 /* copy data w/ endian conversions */
418 switch (entry->info.type) {
420 count = entry->info.count;
423 *((int_32 *)chptr) = htonl(*((int_32 *)src));
424 chptr += sizeof(int_32);
425 src += sizeof(int_32);
430 count = entry->info.count;
433 *((int_16 *)chptr) = htons(*((int_16 *)src));
434 chptr += sizeof(int_16);
435 src += sizeof(int_16);
440 memcpy(chptr, entry->data, entry->length);
441 chptr += entry->length;
448 void *headerUnload(Header h)
452 return doHeaderUnload(h, &length);
455 /********************************************************************/
457 /* Reading and writing headers */
459 /********************************************************************/
461 void headerWrite(FD_t fd, Header h, int magicp)
467 p = doHeaderUnload(h, &length);
470 (void)fdWrite(fd, header_magic, sizeof(header_magic));
472 (void)fdWrite(fd, &l, sizeof(l));
475 (void)fdWrite(fd, p, length);
480 Header headerRead(FD_t fd, int magicp)
493 if (magicp == HEADER_MAGIC_YES)
496 if (timedRead(fd, block, i * sizeof(*block)) != (i * sizeof(*block)))
500 if (magicp == HEADER_MAGIC_YES) {
502 if (memcmp(&magic, header_magic, sizeof(magic))) {
506 reserved = block[i++];
509 il = ntohl(block[i++]);
510 dl = ntohl(block[i++]);
512 totalSize = sizeof(int_32) + sizeof(int_32) +
513 (il * sizeof(struct entryInfo)) + dl;
515 dataBlock = p = malloc(totalSize);
519 totalSize -= sizeof(int_32) + sizeof(int_32);
520 if (timedRead(fd, p, totalSize) != totalSize)
523 h = headerLoad(dataBlock);
530 void headerGzWrite(FD_t fd, Header h, int magicp)
536 p = doHeaderUnload(h, &length);
539 gzdWrite(fd, header_magic, sizeof(header_magic));
541 gzdWrite(fd, &l, sizeof(l));
544 gzdWrite(fd, p, length);
549 Header headerGzRead(FD_t fd, int magicp)
559 if (magicp == HEADER_MAGIC_YES) {
560 if (gzdRead(fd, &magic, sizeof(magic)) != sizeof(magic))
562 if (memcmp(&magic, header_magic, sizeof(magic))) {
566 if (gzdRead(fd, &reserved, sizeof(reserved)) != sizeof(reserved))
570 /* First read the index length (count of index entries) */
571 if (gzdRead(fd, &il, sizeof(il)) != sizeof(il))
576 /* Then read the data length (number of bytes) */
577 if (gzdRead(fd, &dl, sizeof(dl)) != sizeof(dl))
582 totalSize = sizeof(int_32) + sizeof(int_32) +
583 (il * sizeof(struct entryInfo)) + dl;
585 block = p = malloc(totalSize);
589 totalSize -= sizeof(int_32) + sizeof(int_32);
590 if (gzdRead(fd, p, totalSize) != totalSize)
593 h = headerLoad(block);
600 /********************************************************************/
604 /********************************************************************/
606 void headerDump(Header h, FILE *f, int flags,
607 const struct headerTagTableEntry * tags)
610 struct indexEntry *p;
611 const struct headerTagTableEntry * tage;
614 /* First write out the length of the index (count of index entries) */
615 fprintf(f, "Entry count: %d\n", h->indexUsed);
617 /* Now write the index */
619 fprintf(f, "\n CT TAG TYPE "
621 for (i = 0; i < h->indexUsed; i++) {
622 switch (p->info.type) {
623 case RPM_NULL_TYPE: type = "NULL_TYPE"; break;
624 case RPM_CHAR_TYPE: type = "CHAR_TYPE"; break;
625 case RPM_BIN_TYPE: type = "BIN_TYPE"; break;
626 case RPM_INT8_TYPE: type = "INT8_TYPE"; break;
627 case RPM_INT16_TYPE: type = "INT16_TYPE"; break;
628 case RPM_INT32_TYPE: type = "INT32_TYPE"; break;
629 /*case RPM_INT64_TYPE: type = "INT64_TYPE"; break;*/
630 case RPM_STRING_TYPE: type = "STRING_TYPE"; break;
631 case RPM_STRING_ARRAY_TYPE: type = "STRING_ARRAY_TYPE"; break;
632 case RPM_I18NSTRING_TYPE: type = "I18N_STRING_TYPE"; break;
633 default: type = "(unknown)"; break;
637 while (tage->name && tage->val != p->info.tag) tage++;
644 fprintf(f, "Entry : %.3d (%d)%-14s %-18s 0x%.8x %.8d\n", i,
645 p->info.tag, tag, type, (unsigned) p->info.offset, (int)
648 if (flags & HEADER_DUMP_INLINE) {
650 int c = p->info.count;
653 /* Print the data inline */
654 switch (p->info.type) {
657 fprintf(f, " Data: %.3d 0x%08x (%d)\n", ct++,
658 (unsigned) *((int_32 *) dp),
659 (int) *((int_32 *) dp));
660 dp += sizeof(int_32);
666 fprintf(f, " Data: %.3d 0x%04x (%d)\n", ct++,
667 (unsigned) (*((int_16 *) dp) & 0xffff),
668 (int) *((int_16 *) dp));
669 dp += sizeof(int_16);
674 fprintf(f, " Data: %.3d 0x%02x (%d)\n", ct++,
675 (unsigned) (*((int_8 *) dp) & 0xff),
676 (int) *((int_8 *) dp));
682 fprintf(f, " Data: %.3d ", ct);
684 fprintf(f, "%02x ", (unsigned) (*(int_8 *)dp & 0xff));
696 char ch = (char) *((char *) dp);
697 fprintf(f, " Data: %.3d 0x%2x %c (%d)\n", ct++,
698 (unsigned)(ch & 0xff),
699 (isprint(ch) ? ch : ' '),
700 (char) *((char *) dp));
704 case RPM_STRING_TYPE:
705 case RPM_STRING_ARRAY_TYPE:
706 case RPM_I18NSTRING_TYPE:
708 fprintf(f, " Data: %.3d %s\n", ct++, (char *) dp);
714 fprintf(stderr, _("Data type %d not supprted\n"),
723 /********************************************************************/
727 /********************************************************************/
729 static struct indexEntry *findEntry(Header h, int_32 tag, int_32 type)
731 struct indexEntry * entry, * entry2, * last;
732 struct indexEntry key;
734 if (!h->sorted) headerSort(h);
739 bsearch(&key, h->index, h->indexUsed, sizeof(struct indexEntry),
741 if (!entry) return NULL;
743 if (type == RPM_NULL_TYPE) return entry;
746 while (entry->info.tag == tag && entry->info.type != type &&
747 entry > h->index) entry--;
749 if (entry->info.tag == tag && entry->info.type == type)
752 last = h->index + h->indexUsed;
753 while (entry2->info.tag == tag && entry2->info.type != type &&
754 entry2 < last) entry2++;
756 if (entry->info.tag == tag && entry->info.type == type)
762 int headerIsEntry(Header h, int_32 tag)
764 return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
767 int headerGetRawEntry(Header h, int_32 tag, int_32 *type, void **p, int_32 *c)
769 struct indexEntry * entry;
771 if (p == NULL) return headerIsEntry(h, tag);
773 /* First find the tag */
774 entry = findEntry(h, tag, RPM_NULL_TYPE);
781 copyEntry(entry, type, p, c, 0);
786 static int headerMatchLocale(const char *td, const char *l, const char *le)
791 * The range [l,le) contains the next locale to match:
792 * ll[_CC][.EEEEE][@dddd]
794 * ll ISO language code (in lowercase).
795 * CC (optional) ISO coutnry code (in uppercase).
796 * EEEEE (optional) encoding (not really standardized).
797 * dddd (optional) dialect.
801 { const char *s, *ll, *CC, *EE, *dd;
804 /* Copy the buffer and parse out components on the fly. */
805 lbuf = alloca(le - l + 1);
806 for (s = l, ll = t = lbuf; *s; s++, t++) {
826 if (ll) /* ISO language should be lower case */
827 for (t = ll; *t; t++) *t = tolower(*t);
828 if (CC) /* ISO country code should be upper case */
829 for (t = CC; *t; t++) *t = toupper(*t);
831 /* There are a total of 16 cases to attempt to match. */
835 /* Next, try stripping optional dialect and matching. */
836 for (fe = l; fe < le && *fe != '@'; fe++)
838 if (fe < le && !strncmp(td, l, (fe - l)))
841 /* Next, try stripping optional codeset and matching. */
842 for (fe = l; fe < le && *fe != '.'; fe++)
844 if (fe < le && !strncmp(td, l, (fe - l)))
847 /* Finally, try stripping optional country code and matching. */
848 for (fe = l; fe < le && *fe != '_'; fe++)
850 if (fe < le && !strncmp(td, l, (fe - l)))
856 static char *headerFindI18NString(Header h, struct indexEntry *entry)
858 const char *lang, *l, *le;
859 struct indexEntry * table;
861 /* XXX Drepper sez' this is the order. */
862 if ((lang = getenv("LANGUAGE")) == NULL &&
863 (lang = getenv("LC_ALL")) == NULL &&
864 (lang = getenv("LC_MESSAGES")) == NULL &&
865 (lang = getenv("LANG")) == NULL)
868 if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
871 for (l = lang; *l; l = le) {
876 while (*l && *l == ':') /* skip leading colons */
880 for (le = l; *le && *le != ':'; le++) /* find end of this locale */
883 /* For each entry in the header ... */
884 for (langNum = 0, td = table->data, ed = entry->data;
885 langNum < entry->info.count;
886 langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
888 if (headerMatchLocale(td, l, le))
897 static int intGetEntry(Header h, int_32 tag, int_32 *type, void **p, int_32 *c,
900 struct indexEntry * entry;
903 /* First find the tag */
904 entry = findEntry(h, tag, RPM_NULL_TYPE);
911 if (entry->info.type == RPM_I18NSTRING_TYPE) {
912 chptr = headerFindI18NString(h, entry);
914 if (type) *type = RPM_STRING_TYPE;
919 copyEntry(entry, type, p, c, minMem);
925 int headerGetEntryMinMemory(Header h, int_32 tag, int_32 *type, void **p,
928 return intGetEntry(h, tag, type, p, c, 1);
931 int headerGetEntry(Header h, int_32 tag, int_32 * type, void **p, int_32 * c)
933 return intGetEntry(h, tag, type, p, c, 0);
936 /********************************************************************/
938 /* Header creation and deletion */
940 /********************************************************************/
944 Header h = malloc(sizeof(struct headerToken));
946 h->index = malloc(INDEX_MALLOC_SIZE * sizeof(struct indexEntry));
947 h->indexAlloced = INDEX_MALLOC_SIZE;
956 void headerFree(Header h)
960 if (--h->usageCount) return;
961 for (i = 0; i < h->indexUsed; i++)
962 free(h->index[i].data);
968 Header headerLink(Header h)
974 unsigned int headerSizeof(Header h, int magicp)
982 size = sizeof(int_32); /* count of index entries */
983 size += sizeof(int_32); /* length of data */
984 size += sizeof(struct entryInfo) * h->indexUsed;
989 for (i = 0; i < h->indexUsed; i++) {
991 type = h->index[i].info.type;
992 if (typeSizes[type] > 1) {
993 diff = typeSizes[type] - (size % typeSizes[type]);
994 if (diff != typeSizes[type]) {
999 size += h->index[i].length;
1005 static void copyData(int_32 type, void * dstPtr, void * srcPtr, int_32 c,
1012 case RPM_STRING_ARRAY_TYPE:
1013 case RPM_I18NSTRING_TYPE:
1014 /* Otherwise, p is char** */
1016 src = (char **) srcPtr;
1019 len = *src ? strlen(*src) + 1 : 0;
1020 memcpy(dst, *src, len);
1027 memcpy(dstPtr, srcPtr, dataLength);
1031 static void * grabData(int_32 type, void * p, int_32 c, int * lengthPtr)
1036 length = dataLength(type, p, c, 0);
1037 data = malloc(length);
1039 copyData(type, data, p, c, length);
1041 *lengthPtr = length;
1045 /********************************************************************/
1047 /* Adding and modifying entries */
1049 /********************************************************************/
1051 int headerAddEntry(Header h, int_32 tag, int_32 type, void *p, int_32 c)
1053 struct indexEntry *entry;
1058 fprintf(stderr, _("Bad count for headerAddEntry(): %d\n"), (int) c);
1062 /* Allocate more index space if necessary */
1063 if (h->indexUsed == h->indexAlloced) {
1064 h->indexAlloced += INDEX_MALLOC_SIZE;
1065 h->index = realloc(h->index,
1066 h->indexAlloced * sizeof(struct indexEntry));
1069 /* Fill in the index */
1070 entry = h->index + h->indexUsed++;
1071 entry->info.tag = tag;
1072 entry->info.type = type;
1073 entry->info.count = c;
1074 entry->info.offset = -1;
1076 entry->data = grabData(type, p, c, &entry->length);
1084 headerGetLangs(Header h)
1086 char **s, *e, **table;
1089 if (!headerGetRawEntry(h, HEADER_I18NTABLE, &type, (void **)&s, &count))
1092 if ((table = (char **)calloc((count+1), sizeof(char *))) == NULL)
1095 for (i = 0, e = *s; i < count > 0; i++, e += strlen(e)+1) {
1098 table[count] = NULL;
1103 int headerAddI18NString(Header h, int_32 tag, char * string, char * lang)
1105 struct indexEntry * table, * entry;
1106 char * charArray[2];
1114 table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
1115 entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
1117 if (!table && entry) {
1118 return 0; /* this shouldn't ever happen!! */
1121 if (!table && !entry) {
1122 if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
1124 if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE,
1125 &charArray, 1)) return 0;
1128 charArray[1] = lang;
1129 if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE,
1130 &charArray, 2)) return 0;
1132 table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
1135 if (!lang) lang = "C";
1137 chptr = table->data;
1138 for (langNum = 0; langNum < table->info.count; langNum++) {
1139 if (!strcmp(chptr, lang)) break;
1140 chptr += strlen(chptr) + 1;
1143 if (langNum >= table->info.count) {
1144 length = strlen(lang) + 1;
1145 table->data = realloc(table->data, table->length + length);
1146 memcpy(((char *)table->data) + table->length, lang, length);
1147 table->length += length;
1148 table->info.count++;
1152 strArray = alloca(sizeof(*strArray) * (langNum + 1));
1153 for (i = 0; i < langNum; i++)
1155 strArray[langNum] = string;
1156 return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, strArray,
1158 } else if (langNum >= entry->info.count) {
1159 ghosts = langNum - entry->info.count;
1161 length = strlen(string) + 1 + ghosts;
1162 entry->data = realloc(entry->data, entry->length + length);
1164 memset(((char *)entry->data) + entry->length, '\0', ghosts);
1165 strcpy(((char *)entry->data) + entry->length + ghosts, string);
1167 entry->length += length;
1168 entry->info.count = langNum + 1;
1170 char *b, *be, *e, *ee, *t;
1173 /* Set beginning/end pointers to previous data */
1174 b = be = e = ee = entry->data;
1175 for (i = 0; i < table->info.count; i++) {
1178 ee += strlen(ee) + 1;
1183 /* Get storage for new buffer */
1185 sn = strlen(string) + 1;
1187 length = bn + sn + en;
1188 t = buf = malloc(length);
1190 /* Copy values into new storage */
1193 memcpy(t, string, sn);
1198 /* Replace I18N string array */
1199 entry->length -= strlen(be) + 1;
1200 entry->length += sn;
1208 /* if there are multiple entries with this tag, the first one gets replaced */
1209 int headerModifyEntry(Header h, int_32 tag, int_32 type, void *p, int_32 c)
1211 struct indexEntry *entry;
1214 /* First find the tag */
1215 entry = findEntry(h, tag, type);
1220 /* make sure entry points to the first occurence of this tag */
1221 while (entry > h->index && (entry - 1)->info.tag == tag)
1224 /* free after we've grabbed the new data in case the two are intertwined;
1225 that's a bad idea but at least we won't break */
1226 oldData = entry->data;
1228 entry->info.count = c;
1229 entry->info.type = type;
1230 entry->data = grabData(type, p, c, &entry->length);
1237 int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
1240 if (findEntry(h, tag, type)) {
1241 return headerAppendEntry(h, tag, type, p, c);
1243 return headerAddEntry(h, tag, type, p, c);
1247 int headerAppendEntry(Header h, int_32 tag, int_32 type, void * p, int_32 c)
1249 struct indexEntry *entry;
1252 /* First find the tag */
1253 entry = findEntry(h, tag, type);
1258 if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
1259 /* we can't do this */
1263 length = dataLength(type, p, c, 0);
1265 entry->data = realloc(entry->data, entry->length + length);
1266 copyData(type, ((char *) entry->data) + entry->length, p, c, length);
1268 entry->length += length;
1270 entry->info.count += c;
1275 int headerRemoveEntry(Header h, int_32 tag)
1277 struct indexEntry * entry, * last;
1279 entry = findEntry(h, tag, RPM_NULL_TYPE);
1280 if (!entry) return 1;
1282 /* make sure entry points to the first occurence of this tag */
1283 while (entry > h->index && (entry - 1)->info.tag == tag)
1286 /* We might be better off just counting the number of items off the
1287 end and issuing one big memcpy, but memcpy() doesn't have to work
1288 on overlapping regions thanks to ANSI <sigh>. A alloca() and two
1289 memcpy() would probably still be a win (as our moving from the
1290 end to the middle isn't very nice to the qsort() we'll have to
1291 do to make up for this!), but I'm too lazy to implement it. Just
1292 remember that this repeating this is basically nlogn thanks to this
1293 dumb implementation (but n is the best we'd do anyway) */
1295 last = h->index + h->indexUsed;
1296 while (entry->info.tag == tag && entry < last) {
1298 *(entry++) = *(--last);
1300 h->indexUsed = last - h->index;
1307 static char escapedChar(const char ch)
1310 case 'a': return '\a';
1311 case 'b': return '\b';
1312 case 'f': return '\f';
1313 case 'n': return '\n';
1314 case 'r': return '\r';
1315 case 't': return '\t';
1316 case 'v': return '\v';
1322 static void freeFormat(struct sprintfToken * format, int num)
1326 for (i = 0; i < num; i++) {
1327 if (format[i].type == PTOK_ARRAY)
1328 freeFormat(format[i].u.array.format, format[i].u.array.numTokens);
1329 if (format[i].type == PTOK_COND) {
1330 freeFormat(format[i].u.cond.ifFormat,
1331 format[i].u.cond.numIfTokens);
1332 freeFormat(format[i].u.cond.elseFormat,
1333 format[i].u.cond.numElseTokens);
1339 static void findTag(char * name, const struct headerTagTableEntry * tags,
1340 const struct headerSprintfExtension * extensions,
1341 const struct headerTagTableEntry ** tagMatch,
1342 const struct headerSprintfExtension ** extMatch)
1344 const struct headerTagTableEntry * entry;
1345 const struct headerSprintfExtension * ext;
1352 if (strncmp("RPMTAG_", name, 7)) {
1353 tagname = alloca(strlen(name) + 10);
1354 strcpy(tagname, "RPMTAG_");
1355 strcat(tagname, name);
1360 for (entry = tags; entry->name; entry++)
1361 if (!strcasecmp(entry->name, tagname)) break;
1366 ext = extensions, i =0;
1367 while (ext->type != HEADER_EXT_LAST) {
1368 if (ext->type == HEADER_EXT_TAG &&
1369 !strcasecmp(ext->name, tagname)) {
1373 if (ext->type == HEADER_EXT_MORE)
1380 if (ext->type == HEADER_EXT_TAG) {
1387 static int parseExpression(struct sprintfToken * token, char * str,
1388 const struct headerTagTableEntry * tags,
1389 const struct headerSprintfExtension * extensions,
1390 char ** endPtr, char ** error);
1392 static int parseFormat(char * str, const struct headerTagTableEntry * tags,
1393 const struct headerSprintfExtension * extensions,
1394 struct sprintfToken ** formatPtr, int * numTokensPtr,
1395 char ** endPtr, int state, char ** error)
1397 char * chptr, * start, * next, * dst;
1398 struct sprintfToken * format;
1401 const struct headerTagTableEntry * entry;
1402 const struct headerSprintfExtension * ext;
1406 /* upper limit on number of individual formats */
1408 for (chptr = str; *chptr; chptr++)
1409 if (*chptr == '%') numTokens++;
1410 numTokens = numTokens * 2 + 1;
1412 format = calloc(sizeof(*format), numTokens);
1413 if (endPtr) *endPtr = NULL;
1417 while (*start && !done) {
1421 if (*(start + 1) == '%') {
1422 if (currToken < 0 || format[currToken].type != PTOK_STRING) {
1424 format[currToken].type = PTOK_STRING;
1425 dst = format[currToken].u.string.string = start;
1432 break; /* out of switch */
1439 if (*start == '|') {
1443 if (parseExpression(format + currToken, start, tags,
1444 extensions, &newEnd, error)) {
1445 freeFormat(format, numTokens);
1450 format[currToken].u.tag.format = start;
1451 format[currToken].u.tag.pad = 0;
1452 format[currToken].u.tag.justOne = 0;
1453 format[currToken].u.tag.arrayCount = 0;
1456 while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
1457 if (!*chptr || *chptr == '%') {
1458 *error = _("missing { after %");
1459 freeFormat(format, numTokens);
1465 while (start < chptr) {
1466 if (isdigit(*start)) {
1467 i = strtoul(start, &start, 10);
1468 format[currToken].u.tag.pad += i;
1474 if (*start == '=') {
1475 format[currToken].u.tag.justOne = 1;
1477 } else if (*start == '#') {
1478 format[currToken].u.tag.justOne = 1;
1479 format[currToken].u.tag.arrayCount = 1;
1484 while (*next && *next != '}') next++;
1486 *error = _("missing } after %{");
1487 freeFormat(format, numTokens);
1493 while (*chptr && *chptr != ':') chptr++;
1498 *error = _("empty tag format");
1499 freeFormat(format, numTokens);
1502 format[currToken].u.tag.type = chptr;
1504 format[currToken].u.tag.type = NULL;
1508 *error = _("empty tag name");
1509 freeFormat(format, numTokens);
1514 findTag(start, tags, extensions, &entry, &ext);
1517 format[currToken].u.tag.ext = NULL;
1518 format[currToken].u.tag.tag = entry->val;
1520 format[currToken].u.tag.ext = ext->u.tagFunction;
1521 format[currToken].u.tag.extNum = ext - extensions;
1523 *error = _("unknown tag");
1524 freeFormat(format, numTokens);
1528 format[currToken].type = PTOK_TAG;
1540 if (parseFormat(start, tags, extensions,
1541 &format[currToken].u.array.format,
1542 &format[currToken].u.array.numTokens,
1543 &start, PARSER_IN_ARRAY, error)) {
1544 freeFormat(format, numTokens);
1549 *error = _("] expected at end of array");
1550 freeFormat(format, numTokens);
1556 format[currToken].type = PTOK_ARRAY;
1562 if ((*start == ']' && state != PARSER_IN_ARRAY) ||
1563 (*start == '}' && state != PARSER_IN_EXPR)) {
1565 *error = _("unexpected ]");
1567 *error = _("unexpected }");
1568 freeFormat(format, numTokens);
1577 if (currToken < 0 || format[currToken].type != PTOK_STRING) {
1579 format[currToken].type = PTOK_STRING;
1580 dst = format[currToken].u.string.string = start;
1583 if (*start == '\\') {
1585 *dst++ = escapedChar(*start++);
1595 for (i = 0; i < currToken; i++) {
1596 if (format[i].type == PTOK_STRING)
1597 format[i].u.string.len = strlen(format[i].u.string.string);
1600 *numTokensPtr = currToken;
1601 *formatPtr = format;
1606 static int parseExpression(struct sprintfToken * token, char * str,
1607 const struct headerTagTableEntry * tags,
1608 const struct headerSprintfExtension * extensions,
1609 char ** endPtr, char ** error)
1611 char * chptr, * end;
1612 const struct headerTagTableEntry * tag;
1613 const struct headerSprintfExtension * ext;
1616 while (*chptr && *chptr != '?') chptr++;
1618 if (*chptr != '?') {
1619 *error = _("? expected in expression");
1625 if (*chptr != '{') {
1626 *error = _("{ expected after ? in expression");
1632 if (parseFormat(chptr, tags, extensions, &token->u.cond.ifFormat,
1633 &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR, error))
1636 *error = _("} expected in expression");
1637 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1642 if (*chptr != ':' && *chptr != '|') {
1643 *error = _(": expected following ? subexpression");
1644 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1648 if (*chptr == '|') {
1649 parseFormat(strdup(""), tags, extensions, &token->u.cond.elseFormat,
1650 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR,
1655 if (*chptr != '{') {
1656 *error = _("{ expected after : in expression");
1657 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1663 if (parseFormat(chptr, tags, extensions, &token->u.cond.elseFormat,
1664 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR,
1668 *error = _("} expected in expression");
1669 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1674 if (*chptr != '|') {
1675 *error = _("| expected at end of expression");
1676 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1677 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
1686 findTag(str, tags, extensions, &tag, &ext);
1689 token->u.cond.tag.ext = NULL;
1690 token->u.cond.tag.tag = tag->val;
1692 token->u.cond.tag.ext = ext->u.tagFunction;
1693 token->u.cond.tag.extNum = ext - extensions;
1695 token->u.cond.tag.ext = NULL;
1696 token->u.cond.tag.tag = -1;
1699 token->type = PTOK_COND;
1704 static int getExtension(Header h, headerTagTagFunction fn, int_32 * typeptr,
1705 void ** data, int_32 * countptr,
1706 struct extensionCache * ext)
1709 if (fn(h, &ext->type, &ext->data, &ext->count, &ext->freeit))
1714 *typeptr = ext->type;
1716 *countptr = ext->count;
1721 static char * formatValue(struct sprintfTag * tag, Header h,
1722 const struct headerSprintfExtension * extensions,
1723 struct extensionCache * extCache, int element)
1729 unsigned int intVal;
1734 headerTagFormatFunction tagtype = NULL;
1735 const struct headerSprintfExtension * ext;
1738 if (getExtension(h, tag->ext, &type, &data, &count,
1739 extCache + tag->extNum)) {
1741 type = RPM_STRING_TYPE;
1742 data = "(none)"; /* XXX i18n? NO!, sez; gafton */
1745 if (!headerGetEntry(h, tag->tag, &type, &data, &count)){
1747 type = RPM_STRING_TYPE;
1748 data = "(none)"; /* XXX i18n? NO!, sez; gafton */
1754 if (tag->arrayCount) {
1755 if (type == RPM_STRING_ARRAY_TYPE) free(data);
1760 type = RPM_INT32_TYPE;
1764 strcat(buf, tag->format);
1768 while (ext->type != HEADER_EXT_LAST) {
1769 if (ext->type == HEADER_EXT_FORMAT &&
1770 !strcmp(ext->name, tag->type)) {
1771 tagtype = ext->u.formatFunction;
1775 if (ext->type == HEADER_EXT_MORE)
1783 case RPM_STRING_ARRAY_TYPE:
1787 val = tagtype(RPM_STRING_TYPE, strarray[element], buf, tag->pad, 0);
1793 len = strlen(strarray[element]) + tag->pad + 20;
1795 sprintf(val, buf, strarray[element]);
1798 if (mayfree) free(data);
1802 case RPM_STRING_TYPE:
1804 val = tagtype(RPM_STRING_ARRAY_TYPE, data, buf, tag->pad, 0);
1810 len = strlen(data) + tag->pad + 20;
1812 sprintf(val, buf, data);
1818 case RPM_INT16_TYPE:
1819 case RPM_INT32_TYPE:
1822 case RPM_INT8_TYPE: intVal = *(((int_8 *) data) + element); break;
1823 case RPM_INT16_TYPE: intVal = *(((uint_16 *) data) + element); break;
1824 default: /* keep -Wall quiet */
1825 case RPM_INT32_TYPE: intVal = *(((int_32 *) data) + element);
1829 val = tagtype(RPM_INT32_TYPE, &intVal, buf, tag->pad, element);
1834 len = 10 + tag->pad + 20;
1836 sprintf(val, buf, intVal);
1842 strcpy(val, _("(unknown type)"));
1848 static char * singleSprintf(Header h, struct sprintfToken * token,
1849 const struct headerSprintfExtension * extensions,
1850 struct extensionCache * extCache, int element)
1852 char * val, * thisItem;
1859 struct sprintfToken * condFormat;
1862 /* we assume the token and header have been validated already! */
1864 switch (token->type) {
1869 val = malloc(token->u.string.len + 1);
1870 strcpy(val, token->u.string.string);
1874 val = formatValue(&token->u.tag, h, extensions, extCache,
1875 token->u.tag.justOne ? 0 : element);
1879 if (token->u.cond.tag.ext ||
1880 headerIsEntry(h, token->u.cond.tag.tag)) {
1881 condFormat = token->u.cond.ifFormat;
1882 condNumFormats = token->u.cond.numIfTokens;
1884 condFormat = token->u.cond.elseFormat;
1885 condNumFormats = token->u.cond.numElseTokens;
1888 alloced = condNumFormats * 20;
1889 val = malloc(alloced ? alloced : 1);
1893 for (i = 0; i < condNumFormats; i++) {
1894 thisItem = singleSprintf(h, condFormat + i,
1895 extensions, extCache, element);
1896 thisItemLen = strlen(thisItem);
1897 if ((thisItemLen + len) >= alloced) {
1898 alloced = (thisItemLen + len) + 200;
1899 val = realloc(val, alloced);
1901 strcat(val, thisItem);
1910 for (i = 0; i < token->u.array.numTokens; i++) {
1911 if (token->u.array.format[i].type != PTOK_TAG ||
1912 token->u.array.format[i].u.tag.arrayCount ||
1913 token->u.array.format[i].u.tag.justOne) continue;
1915 if (token->u.array.format[i].u.tag.ext) {
1916 if (getExtension(h, token->u.array.format[i].u.tag.ext,
1917 &type, &data, &numElements,
1919 token->u.array.format[i].u.tag.extNum))
1922 if (!headerGetEntry(h, token->u.array.format[i].u.tag.tag,
1923 &type, (void **) &val, &numElements))
1925 if (type == RPM_STRING_ARRAY_TYPE) free(val);
1930 if (numElements == -1) {
1932 strcpy(val, "(none)"); /* XXX i18n? NO!, sez; gafton */
1934 alloced = numElements * token->u.array.numTokens * 20;
1935 val = malloc(alloced);
1939 for (j = 0; j < numElements; j++) {
1940 for (i = 0; i < token->u.array.numTokens; i++) {
1941 thisItem = singleSprintf(h, token->u.array.format + i,
1942 extensions, extCache, j);
1943 thisItemLen = strlen(thisItem);
1944 if ((thisItemLen + len) >= alloced) {
1945 alloced = (thisItemLen + len) + 200;
1946 val = realloc(val, alloced);
1948 strcat(val, thisItem);
1961 static struct extensionCache * allocateExtensionCache(
1962 const struct headerSprintfExtension * extensions)
1964 const struct headerSprintfExtension * ext = extensions;
1967 while (ext->type != HEADER_EXT_LAST) {
1969 if (ext->type == HEADER_EXT_MORE)
1975 return calloc(i, sizeof(struct extensionCache));
1978 static void freeExtensionCache(const struct headerSprintfExtension * extensions,
1979 struct extensionCache * cache)
1981 const struct headerSprintfExtension * ext = extensions;
1984 while (ext->type != HEADER_EXT_LAST) {
1985 if (cache[i].freeit) free(cache[i].data);
1988 if (ext->type == HEADER_EXT_MORE)
1997 char * headerSprintf(Header h, const char * origFmt,
1998 const struct headerTagTableEntry * tags,
1999 const struct headerSprintfExtension * extensions,
2003 struct sprintfToken * format;
2005 char * answer, * piece;
2010 struct extensionCache * extCache;
2012 /*fmtString = escapeString(origFmt);*/
2013 fmtString = strdup(origFmt);
2015 if (parseFormat(fmtString, tags, extensions, &format, &numTokens,
2016 NULL, PARSER_BEGIN, error)) {
2021 extCache = allocateExtensionCache(extensions);
2023 answerAlloced = 1024;
2025 answer = malloc(answerAlloced);
2028 for (i = 0; i < numTokens; i++) {
2029 piece = singleSprintf(h, format + i, extensions, extCache, 0);
2031 pieceLength = strlen(piece);
2032 if ((answerLength + pieceLength) >= answerAlloced) {
2033 while ((answerLength + pieceLength) >= answerAlloced)
2034 answerAlloced += 1024;
2035 answer = realloc(answer, answerAlloced);
2038 strcat(answer, piece);
2039 answerLength += pieceLength;
2045 freeExtensionCache(extensions, extCache);
2051 static char * octalFormat(int_32 type, const void * data,
2052 char * formatPrefix, int padding, int element)
2056 if (type != RPM_INT32_TYPE) {
2058 strcpy(val, _("(not a number)"));
2060 val = malloc(20 + padding);
2061 strcat(formatPrefix, "o");
2062 sprintf(val, formatPrefix, *((int_32 *) data));
2068 static char * hexFormat(int_32 type, const void * data,
2069 char * formatPrefix, int padding, int element)
2073 if (type != RPM_INT32_TYPE) {
2075 strcpy(val, _("(not a number)"));
2077 val = malloc(20 + padding);
2078 strcat(formatPrefix, "x");
2079 sprintf(val, formatPrefix, *((int_32 *) data));
2085 static char * realDateFormat(int_32 type, const void * data,
2086 char * formatPrefix, int padding, int element,
2087 char * strftimeFormat)
2090 struct tm * tstruct;
2093 if (type != RPM_INT32_TYPE) {
2095 strcpy(val, _("(not a number)"));
2097 val = malloc(50 + padding);
2098 strcat(formatPrefix, "s");
2100 /* this is important if sizeof(int_32) ! sizeof(time_t) */
2101 { time_t dateint = *((int_32 *) data);
2102 tstruct = localtime(&dateint);
2104 (void)strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
2105 sprintf(val, formatPrefix, buf);
2111 static char * dateFormat(int_32 type, const void * data,
2112 char * formatPrefix, int padding, int element)
2114 return realDateFormat(type, data, formatPrefix, padding, element, "%c");
2117 static char * dayFormat(int_32 type, const void * data,
2118 char * formatPrefix, int padding, int element)
2120 return realDateFormat(type, data, formatPrefix, padding, element,
2124 static char * shescapeFormat(int_32 type, const void * data,
2125 char * formatPrefix, int padding, int element)
2127 char * result, * dst, * src, * buf;
2129 if (type == RPM_INT32_TYPE) {
2130 result = malloc(padding + 20);
2131 strcat(formatPrefix, "d");
2132 sprintf(result, formatPrefix, *((int_32 *) data));
2134 buf = alloca(strlen(data) + padding + 2);
2135 strcat(formatPrefix, "s");
2136 sprintf(buf, formatPrefix, data);
2138 result = dst = malloc(strlen(buf) * 4 + 3);
2140 for (src = buf; *src; src++) {
2158 const struct headerSprintfExtension headerDefaultFormats[] = {
2159 { HEADER_EXT_FORMAT, "octal", { octalFormat } },
2160 { HEADER_EXT_FORMAT, "hex", { hexFormat } },
2161 { HEADER_EXT_FORMAT, "date", { dateFormat } },
2162 { HEADER_EXT_FORMAT, "day", { dayFormat } },
2163 { HEADER_EXT_FORMAT, "shescape", { shescapeFormat } },
2164 { HEADER_EXT_LAST, NULL, { NULL } }
2167 void headerCopyTags(Header headerFrom, Header headerTo, int *tagstocopy)
2171 if (headerFrom == headerTo)
2174 for (p = tagstocopy; *p != 0; p++) {
2177 if (headerIsEntry(headerTo, *p))
2179 if (!headerGetEntry(headerFrom, *p, &type, (void **) &s, &count))
2181 headerAddEntry(headerTo, *p, type, s, count);
2183 (type == RPM_STRING_ARRAY_TYPE || type == RPM_I18NSTRING_TYPE))