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)
17 /*@-observertrans@*/ /* XXX FIXME */
19 #include <netinet/in.h>
20 #endif /* __LCLINT__ */
25 #define INDEX_MALLOC_SIZE 8
27 #define PARSER_BEGIN 0
28 #define PARSER_IN_ARRAY 1
29 #define PARSER_IN_EXPR 2
31 static unsigned char header_magic[4] = { 0x8e, 0xad, 0xe8, 0x01 };
33 /* handy -- this tells us alignments for defined elements as well */
34 static int typeSizes[] = {
35 /* RPM_NULL_TYPE */ -1,
36 /* RPM_CHAR_TYPE */ 1,
37 /* RPM_INT8_TYPE */ 1,
38 /* RPM_INT16_TYPE */ 2,
39 /* RPM_INT32_TYPE */ 4,
40 /* RPM_INT64_TYPE */ -1,
41 /* RPM_STRING_TYPE */ -1,
43 /* RPM_STRING_ARRAY_TYPE */ -1,
44 /* RPM_I18NSTRING_TYPE */ -1 };
47 struct indexEntry *index;
52 /*@refs@*/ int usageCount;
58 int_32 offset; /* Offset from beginning of data segment,
59 only defined on disk */
64 struct entryInfo info;
65 /*@owned@*/ void * data;
66 int length; /* Computable, but why bother? */
70 /* if NULL tag element is invalid */
71 headerTagTagFunction ext;
81 struct extensionCache {
90 enum { PTOK_NONE = 0, PTOK_TAG, PTOK_ARRAY, PTOK_STRING, PTOK_COND } type;
93 /*@only@*/ struct sprintfToken * format;
96 struct sprintfTag tag;
98 /*@dependent@*/ char * string;
102 /*@only@*/ struct sprintfToken * ifFormat;
104 /*@only@*/ struct sprintfToken * elseFormat;
106 struct sprintfTag tag;
112 static int probe_headers = 0;
113 #define HEADERPROBE(_h, _msg) if (probe_headers) headerProbe((_h), (_msg))
115 static void headerProbeAddr(Header h, const char * msg,
116 void * p, const char * imsg)
118 const char * mchkstr = NULL;
120 case MCHECK_DISABLED:
123 /*@notreached@*/ break;
134 fprintf(stderr, "*** MCHECK_%s h %p", mchkstr, h);
136 fprintf(stderr, " %s %p", imsg, p);
138 fprintf(stderr, " %s", msg);
139 fprintf(stderr, "\n");
142 static void headerProbe(Header h, const char *msg)
147 headerProbeAddr(h, msg, h, "header");
148 sprintf(imsg, "index (used %d)", h->indexUsed);
149 headerProbeAddr(h, msg, h->index, imsg);
150 for (i = 0; i < h->indexUsed; i++) {
151 sprintf(imsg, "index[%d:%d].data", i, h->indexUsed);
152 headerProbeAddr(h, msg, h->index[i].data, imsg);
155 #else /* HAVE_MCHECK_H */
156 #define HEADERPROBE(_h, _msg)
157 #endif /* HAVE_MCHECK_H */
159 static void copyEntry(struct indexEntry * entry, /*@out@*/ int_32 * type,
160 /*@out@*/ void ** p, /*@out@*/ int_32 * c, int minimizeMemory)
167 *type = entry->info.type;
169 *c = entry->info.count;
174 switch (entry->info.type) {
175 case RPM_STRING_TYPE:
176 if (entry->info.count == 1) {
181 case RPM_STRING_ARRAY_TYPE:
182 case RPM_I18NSTRING_TYPE:
183 i = entry->info.count;
184 tableSize = i * sizeof(char *);
185 if (minimizeMemory) {
186 ptrEntry = *p = xmalloc(tableSize);
189 ptrEntry = *p = xmalloc(tableSize + entry->length); /* XXX memory leak */
190 chptr = ((char *) *p) + tableSize;
191 memcpy(chptr, entry->data, entry->length);
195 chptr = strchr(chptr, 0);
206 static int dataLength(int_32 type, const void * p, int_32 count, int onDisk)
208 int thisLen, length, i;
209 char ** src, * chptr;
213 case RPM_STRING_TYPE:
215 /* Special case -- p is just the string */
216 length = strlen(p) + 1;
219 /* This should not be allowed */
220 fprintf(stderr, _("grabData() RPM_STRING_TYPE count must be 1.\n"));
222 /*@notreached@*/ break;
224 case RPM_STRING_ARRAY_TYPE:
225 case RPM_I18NSTRING_TYPE:
226 /* This is like RPM_STRING_TYPE, except it's *always* an array */
227 /* Compute sum of length of all strings, including null terminators */
234 thisLen = strlen(chptr) + 1;
241 /* add one for null termination */
242 length += strlen(*src++) + 1;
248 if (typeSizes[type] != -1)
249 length = typeSizes[type] * count;
251 fprintf(stderr, _("Data type %d not supported\n"), (int) type);
261 /********************************************************************/
263 /* Header iteration and copying */
265 /********************************************************************/
267 struct headerIteratorS {
272 HeaderIterator headerInitIterator(Header h)
274 HeaderIterator hi = xmalloc(sizeof(struct headerIteratorS));
278 hi->h = headerLink(h);
283 void headerFreeIterator(HeaderIterator iter)
289 int headerNextIterator(HeaderIterator iter,
290 int_32 *tag, int_32 *type, void **p, int_32 *c)
293 int slot = iter->next_index;
295 if (slot == h->indexUsed) {
301 *tag = h->index[slot].info.tag;
303 copyEntry(h->index + slot, type, p, c, 0);
308 static int indexCmp(const void *ap, const void *bp)
312 a = ((struct indexEntry *)ap)->info.tag;
313 b = ((struct indexEntry *)bp)->info.tag;
324 void headerSort(Header h)
327 qsort(h->index, h->indexUsed, sizeof(struct indexEntry), indexCmp);
332 Header headerCopy(Header h)
334 int_32 tag, type, count;
336 HeaderIterator headerIter;
337 Header res = headerNew();
339 #if 0 /* XXX harmless, but headerInitIterator() does this anyways */
340 /* Sort the index -- not really necessary but some old apps may depend
341 on this and it certainly won't hurt anything */
344 headerIter = headerInitIterator(h);
346 while (headerNextIterator(headerIter, &tag, &type, &ptr, &count)) {
347 headerAddEntry(res, tag, type, ptr, count);
348 if (type == RPM_STRING_ARRAY_TYPE ||
349 type == RPM_I18NSTRING_TYPE) free(ptr);
354 headerFreeIterator(headerIter);
359 /********************************************************************/
361 /* Header loading and unloading */
363 /********************************************************************/
365 Header headerLoad(void *pv)
367 int_32 il; /* index length, data length */
369 const char * dataStart;
370 struct entryInfo * pe;
371 struct indexEntry * entry;
372 struct headerToken *h = xmalloc(sizeof(struct headerToken));
378 il = ntohl(*((int_32 *) p));
381 /* we can skip the data length -- we only store this to allow reading
385 h->indexAlloced = il;
387 h->index = xmalloc(sizeof(struct indexEntry) * il);
390 /* This assumes you only headerLoad() something you headerUnload()-ed */
393 pe = (struct entryInfo *) p;
394 dataStart = (char *) (pe + h->indexUsed);
396 for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++, pe++) {
397 entry->info.type = htonl(pe->type);
398 entry->info.tag = htonl(pe->tag);
399 entry->info.count = htonl(pe->count);
400 entry->info.offset = -1;
402 if (entry->info.type < RPM_MIN_TYPE ||
403 entry->info.type > RPM_MAX_TYPE) return NULL;
405 src = dataStart + htonl(pe->offset);
406 entry->length = dataLength(entry->info.type, src,
407 entry->info.count, 1);
408 entry->data = dst = xmalloc(entry->length);
410 /* copy data w/ endian conversions */
411 switch (entry->info.type) {
413 count = entry->info.count;
415 *((int_32 *)dst) = htonl(*((int_32 *)src));
416 src += sizeof(int_32);
417 dst += sizeof(int_32);
422 count = entry->info.count;
424 *((int_16 *)dst) = htons(*((int_16 *)src));
425 src += sizeof(int_16);
426 dst += sizeof(int_16);
431 memcpy(dst, src, entry->length);
439 static void *doHeaderUnload(Header h, /*@out@*/int * lengthPtr)
445 struct entryInfo * pe;
446 struct indexEntry * entry;
447 char * chptr, * src, * dataStart;
452 *lengthPtr = headerSizeof(h, 0);
453 pi = p = xmalloc(*lengthPtr);
455 *pi++ = htonl(h->indexUsed);
458 *pi++ = htonl(*lengthPtr - sizeof(int_32) - sizeof(int_32) -
459 (sizeof(struct entryInfo) * h->indexUsed));
461 pe = (struct entryInfo *) pi;
462 dataStart = chptr = (char *) (pe + h->indexUsed);
464 for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++, pe++) {
465 pe->type = htonl(entry->info.type);
466 pe->tag = htonl(entry->info.tag);
467 pe->count = htonl(entry->info.count);
470 type = entry->info.type;
471 if (typeSizes[type] > 1) {
472 diff = typeSizes[type] - ((chptr - dataStart) % typeSizes[type]);
473 if (diff != typeSizes[type]) {
474 memset(chptr, 0, diff);
479 pe->offset = htonl(chptr - dataStart);
481 /* copy data w/ endian conversions */
482 switch (entry->info.type) {
484 count = entry->info.count;
487 *((int_32 *)chptr) = htonl(*((int_32 *)src));
488 chptr += sizeof(int_32);
489 src += sizeof(int_32);
494 count = entry->info.count;
497 *((int_16 *)chptr) = htons(*((int_16 *)src));
498 chptr += sizeof(int_16);
499 src += sizeof(int_16);
504 memcpy(chptr, entry->data, entry->length);
505 chptr += entry->length;
513 void *headerUnload(Header h)
517 return doHeaderUnload(h, &length);
520 /********************************************************************/
522 /* Reading and writing headers */
524 /********************************************************************/
526 int headerWrite(FD_t fd, Header h, int magicp)
533 p = doHeaderUnload(h, &length);
536 nb = Fwrite(header_magic, sizeof(char), sizeof(header_magic), fd);
537 if (nb != sizeof(header_magic)) {
542 nb = Fwrite(&l, sizeof(char), sizeof(l), fd);
543 if (nb != sizeof(l)) {
549 nb = Fwrite(p, sizeof(char), length, fd);
559 Header headerRead(FD_t fd, int magicp)
572 if (magicp == HEADER_MAGIC_YES)
575 if (timedRead(fd, (char *)block, i * sizeof(*block)) != (i * sizeof(*block)))
579 if (magicp == HEADER_MAGIC_YES) {
581 if (memcmp(&magic, header_magic, sizeof(magic))) {
585 reserved = block[i++];
588 il = ntohl(block[i++]);
589 dl = ntohl(block[i++]);
591 totalSize = sizeof(int_32) + sizeof(int_32) +
592 (il * sizeof(struct entryInfo)) + dl;
595 * XXX Limit total size of header to 32Mb (~16 times largest known size).
597 if (totalSize > (32*1024*1024))
600 dataBlock = p = xmalloc(totalSize);
604 totalSize -= sizeof(int_32) + sizeof(int_32);
605 if (timedRead(fd, (char *)p, totalSize) != totalSize)
608 h = headerLoad(dataBlock);
615 int headerGzWrite(FD_t fd, Header h, int magicp)
622 p = doHeaderUnload(h, &length);
625 nb = Fwrite(header_magic, sizeof(char), sizeof(header_magic), fd);
626 if (nb != sizeof(header_magic)) {
631 nb = Fwrite(&l, sizeof(char), sizeof(l), fd);
632 if (nb != sizeof(l)) {
638 nb = Fwrite(p, sizeof(char), length, fd);
648 Header headerGzRead(FD_t fd, int magicp)
658 if (magicp == HEADER_MAGIC_YES) {
659 if (Fread(&magic, sizeof(char), sizeof(magic), fd) != sizeof(magic))
661 if (memcmp(&magic, header_magic, sizeof(magic))) {
665 if (Fread(&reserved, sizeof(char), sizeof(reserved), fd) != sizeof(reserved))
669 /* First read the index length (count of index entries) */
670 if (Fread(&il, sizeof(char), sizeof(il), fd) != sizeof(il))
675 /* Then read the data length (number of bytes) */
676 if (Fread(&dl, sizeof(char), sizeof(dl), fd) != sizeof(dl))
681 totalSize = sizeof(int_32) + sizeof(int_32) +
682 (il * sizeof(struct entryInfo)) + dl;
684 block = p = xmalloc(totalSize);
688 totalSize -= sizeof(int_32) + sizeof(int_32);
689 if (Fread(p, sizeof(char), totalSize, fd) != totalSize)
692 h = headerLoad(block);
699 /********************************************************************/
703 /********************************************************************/
705 void headerDump(Header h, FILE *f, int flags,
706 const struct headerTagTableEntry * tags)
709 struct indexEntry *p;
710 const struct headerTagTableEntry * tage;
714 /* First write out the length of the index (count of index entries) */
715 fprintf(f, "Entry count: %d\n", h->indexUsed);
717 /* Now write the index */
719 fprintf(f, "\n CT TAG TYPE "
721 for (i = 0; i < h->indexUsed; i++) {
722 switch (p->info.type) {
723 case RPM_NULL_TYPE: type = "NULL_TYPE"; break;
724 case RPM_CHAR_TYPE: type = "CHAR_TYPE"; break;
725 case RPM_BIN_TYPE: type = "BIN_TYPE"; break;
726 case RPM_INT8_TYPE: type = "INT8_TYPE"; break;
727 case RPM_INT16_TYPE: type = "INT16_TYPE"; break;
728 case RPM_INT32_TYPE: type = "INT32_TYPE"; break;
729 /*case RPM_INT64_TYPE: type = "INT64_TYPE"; break;*/
730 case RPM_STRING_TYPE: type = "STRING_TYPE"; break;
731 case RPM_STRING_ARRAY_TYPE: type = "STRING_ARRAY_TYPE"; break;
732 case RPM_I18NSTRING_TYPE: type = "I18N_STRING_TYPE"; break;
733 default: type = "(unknown)"; break;
737 while (tage->name && tage->val != p->info.tag) tage++;
744 fprintf(f, "Entry : %.3d (%d)%-14s %-18s 0x%.8x %.8d\n", i,
745 p->info.tag, tag, type, (unsigned) p->info.offset, (int)
748 if (flags & HEADER_DUMP_INLINE) {
750 int c = p->info.count;
753 /* Print the data inline */
754 switch (p->info.type) {
757 fprintf(f, " Data: %.3d 0x%08x (%d)\n", ct++,
758 (unsigned) *((int_32 *) dp),
759 (int) *((int_32 *) dp));
760 dp += sizeof(int_32);
766 fprintf(f, " Data: %.3d 0x%04x (%d)\n", ct++,
767 (unsigned) (*((int_16 *) dp) & 0xffff),
768 (int) *((int_16 *) dp));
769 dp += sizeof(int_16);
774 fprintf(f, " Data: %.3d 0x%02x (%d)\n", ct++,
775 (unsigned) (*((int_8 *) dp) & 0xff),
776 (int) *((int_8 *) dp));
782 fprintf(f, " Data: %.3d ", ct);
784 fprintf(f, "%02x ", (unsigned) (*(int_8 *)dp & 0xff));
796 char ch = (char) *((char *) dp);
797 fprintf(f, " Data: %.3d 0x%2x %c (%d)\n", ct++,
798 (unsigned)(ch & 0xff),
799 (isprint(ch) ? ch : ' '),
800 (int) *((char *) dp));
804 case RPM_STRING_TYPE:
805 case RPM_STRING_ARRAY_TYPE:
806 case RPM_I18NSTRING_TYPE:
808 fprintf(f, " Data: %.3d %s\n", ct++, (char *) dp);
814 fprintf(stderr, _("Data type %d not supported\n"),
817 /*@notreached@*/ break;
824 /********************************************************************/
828 /********************************************************************/
830 static struct indexEntry *findEntry(Header h, int_32 tag, int_32 type)
832 struct indexEntry * entry, * entry2, * last;
833 struct indexEntry key;
835 if (!h->sorted) headerSort(h);
840 bsearch(&key, h->index, h->indexUsed, sizeof(struct indexEntry),
842 if (!entry) return NULL;
844 if (type == RPM_NULL_TYPE) return entry;
847 while (entry->info.tag == tag && entry->info.type != type &&
848 entry > h->index) entry--;
850 if (entry->info.tag == tag && entry->info.type == type)
853 last = h->index + h->indexUsed;
854 while (entry2->info.tag == tag && entry2->info.type != type &&
855 entry2 < last) entry2++;
857 if (entry->info.tag == tag && entry->info.type == type)
863 int headerIsEntry(Header h, int_32 tag)
865 return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
868 int headerGetRawEntry(Header h, int_32 tag, int_32 *type, void **p, int_32 *c)
870 struct indexEntry * entry;
872 if (p == NULL) return headerIsEntry(h, tag);
874 /* First find the tag */
875 entry = findEntry(h, tag, RPM_NULL_TYPE);
882 copyEntry(entry, type, p, c, 0);
887 static int headerMatchLocale(const char *td, const char *l, const char *le)
892 * The range [l,le) contains the next locale to match:
893 * ll[_CC][.EEEEE][@dddd]
895 * ll ISO language code (in lowercase).
896 * CC (optional) ISO coutnry code (in uppercase).
897 * EEEEE (optional) encoding (not really standardized).
898 * dddd (optional) dialect.
902 { const char *s, *ll, *CC, *EE, *dd;
905 /* Copy the buffer and parse out components on the fly. */
906 lbuf = alloca(le - l + 1);
907 for (s = l, ll = t = lbuf; *s; s++, t++) {
927 if (ll) /* ISO language should be lower case */
928 for (t = ll; *t; t++) *t = tolower(*t);
929 if (CC) /* ISO country code should be upper case */
930 for (t = CC; *t; t++) *t = toupper(*t);
932 /* There are a total of 16 cases to attempt to match. */
936 /* First try a complete match. */
937 if (strlen(td) == (le-l) && !strncmp(td, l, (le - l)))
940 /* Next, try stripping optional dialect and matching. */
941 for (fe = l; fe < le && *fe != '@'; fe++)
943 if (fe < le && !strncmp(td, l, (fe - l)))
946 /* Next, try stripping optional codeset and matching. */
947 for (fe = l; fe < le && *fe != '.'; fe++)
949 if (fe < le && !strncmp(td, l, (fe - l)))
952 /* Finally, try stripping optional country code and matching. */
953 for (fe = l; fe < le && *fe != '_'; fe++)
955 if (fe < le && !strncmp(td, l, (fe - l)))
961 /*@dependent@*/ static char *
962 headerFindI18NString(Header h, struct indexEntry *entry)
964 const char *lang, *l, *le;
965 struct indexEntry * table;
967 /* XXX Drepper sez' this is the order. */
968 if ((lang = getenv("LANGUAGE")) == NULL &&
969 (lang = getenv("LC_ALL")) == NULL &&
970 (lang = getenv("LC_MESSAGES")) == NULL &&
971 (lang = getenv("LANG")) == NULL)
974 if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
977 for (l = lang; *l; l = le) {
982 while (*l && *l == ':') /* skip leading colons */
986 for (le = l; *le && *le != ':'; le++) /* find end of this locale */
989 /* For each entry in the header ... */
990 for (langNum = 0, td = table->data, ed = entry->data;
991 langNum < entry->info.count;
992 langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
994 if (headerMatchLocale(td, l, le))
1003 static int intGetEntry(Header h, int_32 tag, /*@out@*/ int_32 *type,
1004 /*@out@*/ void **p, /*@out@*/ int_32 *c, int minMem)
1006 struct indexEntry * entry;
1009 HEADERPROBE(h, "intGetEntry");
1010 /* First find the tag */
1011 entry = findEntry(h, tag, RPM_NULL_TYPE);
1018 if (entry->info.type == RPM_I18NSTRING_TYPE) {
1019 chptr = headerFindI18NString(h, entry);
1021 if (type) *type = RPM_STRING_TYPE;
1026 copyEntry(entry, type, p, c, minMem);
1032 int headerGetEntryMinMemory(Header h, int_32 tag, int_32 *type, void **p,
1035 return intGetEntry(h, tag, type, p, c, 1);
1038 int headerGetEntry(Header h, int_32 tag, int_32 * type, void **p, int_32 * c)
1040 return intGetEntry(h, tag, type, p, c, 0);
1043 /********************************************************************/
1045 /* Header creation and deletion */
1047 /********************************************************************/
1051 Header h = xmalloc(sizeof(struct headerToken));
1053 h->indexAlloced = INDEX_MALLOC_SIZE;
1054 h->index = xcalloc(h->indexAlloced, sizeof(struct indexEntry));
1063 void headerFree(Header h)
1067 if (--h->usageCount) return;
1068 for (i = 0; i < h->indexUsed; i++)
1069 free(h->index[i].data);
1072 /*@-refcounttrans@*/ free(h); /*@=refcounttrans@*/
1075 Header headerLink(Header h)
1077 HEADERPROBE(h, "headerLink");
1082 int headerUsageCount(Header h)
1084 return h->usageCount;
1087 unsigned int headerSizeof(Header h, int magicp)
1095 size = sizeof(int_32); /* count of index entries */
1096 size += sizeof(int_32); /* length of data */
1097 size += sizeof(struct entryInfo) * h->indexUsed;
1102 for (i = 0; i < h->indexUsed; i++) {
1104 type = h->index[i].info.type;
1105 if (typeSizes[type] > 1) {
1106 diff = typeSizes[type] - (size % typeSizes[type]);
1107 if (diff != typeSizes[type]) {
1112 size += h->index[i].length;
1118 static void copyData(int_32 type, /*@out@*/void * dstPtr, const void * srcPtr, int_32 c,
1126 case RPM_STRING_ARRAY_TYPE:
1127 case RPM_I18NSTRING_TYPE:
1128 /* Otherwise, p is char** */
1130 src = (const char **) srcPtr;
1133 len = *src ? strlen(*src) + 1 : 0;
1134 memcpy(dst, *src, len);
1141 memcpy(dstPtr, srcPtr, dataLength);
1146 static void * grabData(int_32 type, /*@out@*/const void * p, int_32 c, int * lengthPtr)
1151 length = dataLength(type, p, c, 0);
1152 data = xmalloc(length);
1154 copyData(type, data, p, c, length);
1156 *lengthPtr = length;
1160 /********************************************************************/
1162 /* Adding and modifying entries */
1164 /********************************************************************/
1166 int headerAddEntry(Header h, int_32 tag, int_32 type, const void *p, int_32 c)
1168 struct indexEntry *entry;
1173 fprintf(stderr, _("Bad count for headerAddEntry(): %d\n"), (int) c);
1178 /* Allocate more index space if necessary */
1179 if (h->indexUsed == h->indexAlloced) {
1180 h->indexAlloced += INDEX_MALLOC_SIZE;
1181 h->index = xrealloc(h->index,
1182 h->indexAlloced * sizeof(struct indexEntry));
1185 /* Fill in the index */
1186 entry = h->index + h->indexUsed++;
1187 entry->info.tag = tag;
1188 entry->info.type = type;
1189 entry->info.count = c;
1190 entry->info.offset = -1;
1192 entry->data = grabData(type, p, c, &entry->length);
1200 headerGetLangs(Header h)
1202 char **s, *e, **table;
1205 if (!headerGetRawEntry(h, HEADER_I18NTABLE, &type, (void **)&s, &count))
1208 if ((table = (char **)xcalloc((count+1), sizeof(char *))) == NULL)
1211 for (i = 0, e = *s; i < count > 0; i++, e += strlen(e)+1) {
1214 table[count] = NULL;
1219 int headerAddI18NString(Header h, int_32 tag, const char * string, const char * lang)
1221 struct indexEntry * table, * entry;
1223 const char ** strArray;
1229 table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
1230 entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
1232 if (!table && entry) {
1233 return 0; /* this shouldn't ever happen!! */
1236 if (!table && !entry) {
1237 const char * charArray[2];
1239 if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
1240 charArray[count++] = "C";
1242 charArray[count++] = "C";
1243 charArray[count++] = lang;
1245 if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE,
1248 table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
1251 if (!lang) lang = "C";
1253 chptr = table->data;
1254 for (langNum = 0; langNum < table->info.count; langNum++) {
1255 if (!strcmp(chptr, lang)) break;
1256 chptr += strlen(chptr) + 1;
1259 if (langNum >= table->info.count) {
1260 length = strlen(lang) + 1;
1261 table->data = xrealloc(table->data, table->length + length);
1262 memcpy(((char *)table->data) + table->length, lang, length);
1263 table->length += length;
1264 table->info.count++;
1268 strArray = alloca(sizeof(*strArray) * (langNum + 1));
1269 for (i = 0; i < langNum; i++)
1271 strArray[langNum] = string;
1272 return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, strArray,
1274 } else if (langNum >= entry->info.count) {
1275 ghosts = langNum - entry->info.count;
1277 length = strlen(string) + 1 + ghosts;
1278 entry->data = xrealloc(entry->data, entry->length + length);
1280 memset(((char *)entry->data) + entry->length, '\0', ghosts);
1281 strcpy(((char *)entry->data) + entry->length + ghosts, string);
1283 entry->length += length;
1284 entry->info.count = langNum + 1;
1286 char *b, *be, *e, *ee, *t;
1289 /* Set beginning/end pointers to previous data */
1290 b = be = e = ee = entry->data;
1291 for (i = 0; i < table->info.count; i++) {
1294 ee += strlen(ee) + 1;
1299 /* Get storage for new buffer */
1301 sn = strlen(string) + 1;
1303 length = bn + sn + en;
1304 t = buf = xmalloc(length);
1306 /* Copy values into new storage */
1309 memcpy(t, string, sn);
1314 /* Replace I18N string array */
1315 entry->length -= strlen(be) + 1;
1316 entry->length += sn;
1324 /* if there are multiple entries with this tag, the first one gets replaced */
1325 int headerModifyEntry(Header h, int_32 tag, int_32 type, void *p, int_32 c)
1327 struct indexEntry *entry;
1330 /* First find the tag */
1331 entry = findEntry(h, tag, type);
1336 /* make sure entry points to the first occurence of this tag */
1337 while (entry > h->index && (entry - 1)->info.tag == tag)
1340 /* free after we've grabbed the new data in case the two are intertwined;
1341 that's a bad idea but at least we won't break */
1342 oldData = entry->data;
1344 entry->info.count = c;
1345 entry->info.type = type;
1346 entry->data = grabData(type, p, c, &entry->length);
1353 int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
1356 if (findEntry(h, tag, type)) {
1357 return headerAppendEntry(h, tag, type, p, c);
1359 return headerAddEntry(h, tag, type, p, c);
1363 int headerAppendEntry(Header h, int_32 tag, int_32 type, void * p, int_32 c)
1365 struct indexEntry *entry;
1368 /* First find the tag */
1369 entry = findEntry(h, tag, type);
1374 if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
1375 /* we can't do this */
1379 length = dataLength(type, p, c, 0);
1381 entry->data = xrealloc(entry->data, entry->length + length);
1382 copyData(type, ((char *) entry->data) + entry->length, p, c, length);
1384 entry->length += length;
1386 entry->info.count += c;
1391 int headerRemoveEntry(Header h, int_32 tag)
1393 struct indexEntry * entry, * last;
1395 entry = findEntry(h, tag, RPM_NULL_TYPE);
1396 if (!entry) return 1;
1398 /* make sure entry points to the first occurence of this tag */
1399 while (entry > h->index && (entry - 1)->info.tag == tag)
1402 /* We might be better off just counting the number of items off the
1403 end and issuing one big memcpy, but memcpy() doesn't have to work
1404 on overlapping regions thanks to ANSI <sigh>. A alloca() and two
1405 memcpy() would probably still be a win (as our moving from the
1406 end to the middle isn't very nice to the qsort() we'll have to
1407 do to make up for this!), but I'm too lazy to implement it. Just
1408 remember that this repeating this is basically nlogn thanks to this
1409 dumb implementation (but n is the best we'd do anyway) */
1411 last = h->index + h->indexUsed;
1412 while (entry->info.tag == tag && entry < last) {
1414 *(entry++) = *(--last);
1416 h->indexUsed = last - h->index;
1423 static char escapedChar(const char ch)
1426 case 'a': return '\a';
1427 case 'b': return '\b';
1428 case 'f': return '\f';
1429 case 'n': return '\n';
1430 case 'r': return '\r';
1431 case 't': return '\t';
1432 case 'v': return '\v';
1438 static void freeFormat( /*@only@*/ struct sprintfToken * format, int num)
1442 for (i = 0; i < num; i++) {
1443 switch (format[i].type) {
1445 freeFormat(format[i].u.array.format, format[i].u.array.numTokens);
1448 freeFormat(format[i].u.cond.ifFormat,
1449 format[i].u.cond.numIfTokens);
1450 freeFormat(format[i].u.cond.elseFormat,
1451 format[i].u.cond.numElseTokens);
1463 static void findTag(char * name, const struct headerTagTableEntry * tags,
1464 const struct headerSprintfExtension * extensions,
1465 /*@out@*/const struct headerTagTableEntry ** tagMatch,
1466 /*@out@*/const struct headerSprintfExtension ** extMatch)
1468 const struct headerTagTableEntry * entry;
1469 const struct headerSprintfExtension * ext;
1475 if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
1476 tagname = alloca(strlen(name) + 10);
1477 strcpy(tagname, "RPMTAG_");
1478 strcat(tagname, name);
1483 /* Search extensions first to permit overriding header tags. */
1485 while (ext->type != HEADER_EXT_LAST) {
1486 if (ext->type == HEADER_EXT_TAG && !strcasecmp(ext->name, tagname))
1489 if (ext->type == HEADER_EXT_MORE)
1495 if (ext->type == HEADER_EXT_TAG) {
1500 /* Search header tags. */
1501 for (entry = tags; entry->name; entry++)
1502 if (!strcasecmp(entry->name, tagname)) break;
1511 static int parseExpression(struct sprintfToken * token, char * str,
1512 const struct headerTagTableEntry * tags,
1513 const struct headerSprintfExtension * extensions,
1514 /*@out@*/char ** endPtr, /*@out@*/const char ** error);
1516 static int parseFormat(char * str, const struct headerTagTableEntry * tags,
1517 const struct headerSprintfExtension * extensions,
1518 /*@out@*/struct sprintfToken ** formatPtr, /*@out@*/int * numTokensPtr,
1519 /*@out@*/char ** endPtr, int state, /*@out@*/const char ** error)
1521 char * chptr, * start, * next, * dst;
1522 struct sprintfToken * format;
1525 const struct headerTagTableEntry * tag;
1526 const struct headerSprintfExtension * ext;
1530 /* upper limit on number of individual formats */
1532 for (chptr = str; *chptr; chptr++)
1533 if (*chptr == '%') numTokens++;
1534 numTokens = numTokens * 2 + 1;
1536 format = xcalloc(numTokens, sizeof(*format));
1537 if (endPtr) *endPtr = NULL;
1541 while (*start && !done) {
1545 if (*(start + 1) == '%') {
1546 if (currToken < 0 || format[currToken].type != PTOK_STRING) {
1548 format[currToken].type = PTOK_STRING;
1549 dst = format[currToken].u.string.string = start;
1556 break; /* out of switch */
1563 if (*start == '|') {
1567 if (parseExpression(format + currToken, start, tags,
1568 extensions, &newEnd, error)) {
1569 freeFormat(format, numTokens);
1574 format[currToken].u.tag.format = start;
1575 format[currToken].u.tag.pad = 0;
1576 format[currToken].u.tag.justOne = 0;
1577 format[currToken].u.tag.arrayCount = 0;
1580 while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
1581 if (!*chptr || *chptr == '%') {
1582 *error = _("missing { after %");
1583 freeFormat(format, numTokens);
1589 while (start < chptr) {
1590 if (isdigit(*start)) {
1591 i = strtoul(start, &start, 10);
1592 format[currToken].u.tag.pad += i;
1598 if (*start == '=') {
1599 format[currToken].u.tag.justOne = 1;
1601 } else if (*start == '#') {
1602 format[currToken].u.tag.justOne = 1;
1603 format[currToken].u.tag.arrayCount = 1;
1608 while (*next && *next != '}') next++;
1610 *error = _("missing } after %{");
1611 freeFormat(format, numTokens);
1617 while (*chptr && *chptr != ':') chptr++;
1622 *error = _("empty tag format");
1623 freeFormat(format, numTokens);
1626 format[currToken].u.tag.type = chptr;
1628 format[currToken].u.tag.type = NULL;
1632 *error = _("empty tag name");
1633 freeFormat(format, numTokens);
1638 findTag(start, tags, extensions, &tag, &ext);
1641 format[currToken].u.tag.ext = NULL;
1642 format[currToken].u.tag.tag = tag->val;
1644 format[currToken].u.tag.ext = ext->u.tagFunction;
1645 format[currToken].u.tag.extNum = ext - extensions;
1647 *error = _("unknown tag");
1648 freeFormat(format, numTokens);
1652 format[currToken].type = PTOK_TAG;
1664 if (parseFormat(start, tags, extensions,
1665 &format[currToken].u.array.format,
1666 &format[currToken].u.array.numTokens,
1667 &start, PARSER_IN_ARRAY, error)) {
1668 freeFormat(format, numTokens);
1673 *error = _("] expected at end of array");
1674 freeFormat(format, numTokens);
1680 format[currToken].type = PTOK_ARRAY;
1686 if ((*start == ']' && state != PARSER_IN_ARRAY) ||
1687 (*start == '}' && state != PARSER_IN_EXPR)) {
1689 *error = _("unexpected ]");
1691 *error = _("unexpected }");
1692 freeFormat(format, numTokens);
1701 if (currToken < 0 || format[currToken].type != PTOK_STRING) {
1703 format[currToken].type = PTOK_STRING;
1704 dst = format[currToken].u.string.string = start;
1707 if (*start == '\\') {
1709 *dst++ = escapedChar(*start++);
1720 for (i = 0; i < currToken; i++) {
1721 if (format[i].type == PTOK_STRING)
1722 format[i].u.string.len = strlen(format[i].u.string.string);
1725 *numTokensPtr = currToken;
1726 *formatPtr = format;
1731 static int parseExpression(struct sprintfToken * token, char * str,
1732 const struct headerTagTableEntry * tags,
1733 const struct headerSprintfExtension * extensions,
1734 /*@out@*/ char ** endPtr, /*@out@*/ const char ** error)
1736 char * chptr, * end;
1737 const struct headerTagTableEntry * tag;
1738 const struct headerSprintfExtension * ext;
1742 while (*chptr && *chptr != '?') chptr++;
1744 if (*chptr != '?') {
1745 *error = _("? expected in expression");
1751 if (*chptr != '{') {
1752 *error = _("{ expected after ? in expression");
1758 if (parseFormat(chptr, tags, extensions, &token->u.cond.ifFormat,
1759 &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR, error))
1762 *error = _("} expected in expression");
1763 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1764 token->u.cond.ifFormat = NULL;
1769 if (*chptr != ':' && *chptr != '|') {
1770 *error = _(": expected following ? subexpression");
1771 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1772 token->u.cond.ifFormat = NULL;
1776 if (*chptr == '|') {
1777 parseFormat(xstrdup(""), tags, extensions, &token->u.cond.elseFormat,
1778 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR,
1783 if (*chptr != '{') {
1784 *error = _("{ expected after : in expression");
1785 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1786 token->u.cond.ifFormat = NULL;
1792 if (parseFormat(chptr, tags, extensions, &token->u.cond.elseFormat,
1793 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR,
1797 *error = _("} expected in expression");
1798 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1799 token->u.cond.ifFormat = NULL;
1804 if (*chptr != '|') {
1805 *error = _("| expected at end of expression");
1806 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1807 token->u.cond.ifFormat = NULL;
1808 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
1809 token->u.cond.elseFormat = NULL;
1818 findTag(str, tags, extensions, &tag, &ext);
1821 token->u.cond.tag.ext = NULL;
1822 token->u.cond.tag.tag = tag->val;
1824 token->u.cond.tag.ext = ext->u.tagFunction;
1825 token->u.cond.tag.extNum = ext - extensions;
1827 token->u.cond.tag.ext = NULL;
1828 token->u.cond.tag.tag = -1;
1831 token->type = PTOK_COND;
1836 static int getExtension(Header h, headerTagTagFunction fn, /*@out@*/int_32 * typeptr,
1837 /*@out@*/void ** data, /*@out@*/int_32 * countptr,
1838 struct extensionCache * ext)
1841 if (fn(h, &ext->type, &ext->data, &ext->count, &ext->freeit))
1846 *typeptr = ext->type;
1848 *countptr = ext->count;
1853 static char * formatValue(struct sprintfTag * tag, Header h,
1854 const struct headerSprintfExtension * extensions,
1855 struct extensionCache * extCache, int element)
1861 unsigned int intVal;
1866 headerTagFormatFunction tagtype = NULL;
1867 const struct headerSprintfExtension * ext;
1870 if (getExtension(h, tag->ext, &type, &data, &count,
1871 extCache + tag->extNum)) {
1873 type = RPM_STRING_TYPE;
1874 data = "(none)"; /* XXX i18n? NO!, sez; gafton */
1877 if (!headerGetEntry(h, tag->tag, &type, &data, &count)){
1879 type = RPM_STRING_TYPE;
1880 data = "(none)"; /* XXX i18n? NO!, sez; gafton */
1886 if (tag->arrayCount) {
1887 if (type == RPM_STRING_ARRAY_TYPE) free(data);
1892 type = RPM_INT32_TYPE;
1896 strcat(buf, tag->format);
1900 while (ext->type != HEADER_EXT_LAST) {
1901 if (ext->type == HEADER_EXT_FORMAT &&
1902 !strcmp(ext->name, tag->type)) {
1903 tagtype = ext->u.formatFunction;
1907 if (ext->type == HEADER_EXT_MORE)
1915 case RPM_STRING_ARRAY_TYPE:
1919 val = tagtype(RPM_STRING_TYPE, strarray[element], buf, tag->pad, 0);
1925 len = strlen(strarray[element]) + tag->pad + 20;
1927 sprintf(val, buf, strarray[element]);
1930 if (mayfree) free(data);
1934 case RPM_STRING_TYPE:
1936 val = tagtype(RPM_STRING_ARRAY_TYPE, data, buf, tag->pad, 0);
1942 len = strlen(data) + tag->pad + 20;
1944 sprintf(val, buf, data);
1950 case RPM_INT16_TYPE:
1951 case RPM_INT32_TYPE:
1954 case RPM_INT8_TYPE: intVal = *(((int_8 *) data) + element); break;
1955 case RPM_INT16_TYPE: intVal = *(((uint_16 *) data) + element); break;
1956 default: /* keep -Wall quiet */
1957 case RPM_INT32_TYPE: intVal = *(((int_32 *) data) + element);
1961 val = tagtype(RPM_INT32_TYPE, &intVal, buf, tag->pad, element);
1966 len = 10 + tag->pad + 20;
1968 sprintf(val, buf, intVal);
1973 val = xstrdup(_("(unknown type)"));
1980 static char * singleSprintf(Header h, struct sprintfToken * token,
1981 const struct headerSprintfExtension * extensions,
1982 struct extensionCache * extCache, int element)
1984 char * val, * thisItem;
1991 struct sprintfToken * condFormat;
1994 /* we assume the token and header have been validated already! */
1996 switch (token->type) {
2001 val = xmalloc(token->u.string.len + 1);
2002 strcpy(val, token->u.string.string);
2006 val = formatValue(&token->u.tag, h, extensions, extCache,
2007 token->u.tag.justOne ? 0 : element);
2011 if (token->u.cond.tag.ext ||
2012 headerIsEntry(h, token->u.cond.tag.tag)) {
2013 condFormat = token->u.cond.ifFormat;
2014 condNumFormats = token->u.cond.numIfTokens;
2016 condFormat = token->u.cond.elseFormat;
2017 condNumFormats = token->u.cond.numElseTokens;
2020 alloced = condNumFormats * 20;
2021 val = xmalloc(alloced ? alloced : 1);
2025 for (i = 0; i < condNumFormats; i++) {
2026 thisItem = singleSprintf(h, condFormat + i,
2027 extensions, extCache, element);
2028 thisItemLen = strlen(thisItem);
2029 if ((thisItemLen + len) >= alloced) {
2030 alloced = (thisItemLen + len) + 200;
2031 val = xrealloc(val, alloced);
2033 strcat(val, thisItem);
2042 for (i = 0; i < token->u.array.numTokens; i++) {
2043 if (token->u.array.format[i].type != PTOK_TAG ||
2044 token->u.array.format[i].u.tag.arrayCount ||
2045 token->u.array.format[i].u.tag.justOne) continue;
2047 if (token->u.array.format[i].u.tag.ext) {
2048 if (getExtension(h, token->u.array.format[i].u.tag.ext,
2049 &type, &data, &numElements,
2051 token->u.array.format[i].u.tag.extNum))
2054 if (!headerGetEntry(h, token->u.array.format[i].u.tag.tag,
2055 &type, (void **) &val, &numElements))
2057 if (type == RPM_STRING_ARRAY_TYPE) free(val);
2062 if (numElements == -1) {
2063 val = xstrdup("(none)"); /* XXX i18n? NO!, sez; gafton */
2065 alloced = numElements * token->u.array.numTokens * 20;
2066 val = xmalloc(alloced);
2070 for (j = 0; j < numElements; j++) {
2071 for (i = 0; i < token->u.array.numTokens; i++) {
2072 thisItem = singleSprintf(h, token->u.array.format + i,
2073 extensions, extCache, j);
2074 thisItemLen = strlen(thisItem);
2075 if ((thisItemLen + len) >= alloced) {
2076 alloced = (thisItemLen + len) + 200;
2077 val = xrealloc(val, alloced);
2079 strcat(val, thisItem);
2092 static struct extensionCache * allocateExtensionCache(
2093 const struct headerSprintfExtension * extensions)
2095 const struct headerSprintfExtension * ext = extensions;
2098 while (ext->type != HEADER_EXT_LAST) {
2100 if (ext->type == HEADER_EXT_MORE)
2106 return xcalloc(i, sizeof(struct extensionCache));
2109 static void freeExtensionCache(const struct headerSprintfExtension * extensions,
2110 /*@only@*/struct extensionCache * cache)
2112 const struct headerSprintfExtension * ext = extensions;
2115 while (ext->type != HEADER_EXT_LAST) {
2116 if (cache[i].freeit) free(cache[i].data);
2119 if (ext->type == HEADER_EXT_MORE)
2128 char * headerSprintf(Header h, const char * origFmt,
2129 const struct headerTagTableEntry * tags,
2130 const struct headerSprintfExtension * extensions,
2131 const char ** error)
2134 struct sprintfToken * format;
2136 char * answer, * piece;
2141 struct extensionCache * extCache;
2143 /*fmtString = escapeString(origFmt);*/
2144 fmtString = xstrdup(origFmt);
2146 if (parseFormat(fmtString, tags, extensions, &format, &numTokens,
2147 NULL, PARSER_BEGIN, error)) {
2152 extCache = allocateExtensionCache(extensions);
2154 answerAlloced = 1024;
2156 answer = xmalloc(answerAlloced);
2159 for (i = 0; i < numTokens; i++) {
2160 piece = singleSprintf(h, format + i, extensions, extCache, 0);
2162 pieceLength = strlen(piece);
2163 if ((answerLength + pieceLength) >= answerAlloced) {
2164 while ((answerLength + pieceLength) >= answerAlloced)
2165 answerAlloced += 1024;
2166 answer = xrealloc(answer, answerAlloced);
2169 strcat(answer, piece);
2170 answerLength += pieceLength;
2176 freeExtensionCache(extensions, extCache);
2182 static char * octalFormat(int_32 type, const void * data,
2183 char * formatPrefix, int padding, /*@unused@*/int element)
2187 if (type != RPM_INT32_TYPE) {
2188 val = xstrdup(_("(not a number)"));
2190 val = xmalloc(20 + padding);
2191 strcat(formatPrefix, "o");
2192 sprintf(val, formatPrefix, *((int_32 *) data));
2198 static char * hexFormat(int_32 type, const void * data,
2199 char * formatPrefix, int padding, /*@unused@*/int element)
2203 if (type != RPM_INT32_TYPE) {
2204 val = xstrdup(_("(not a number)"));
2206 val = xmalloc(20 + padding);
2207 strcat(formatPrefix, "x");
2208 sprintf(val, formatPrefix, *((int_32 *) data));
2214 static char * realDateFormat(int_32 type, const void * data,
2215 char * formatPrefix, int padding, /*@unused@*/int element,
2216 char * strftimeFormat)
2219 struct tm * tstruct;
2222 if (type != RPM_INT32_TYPE) {
2223 val = xstrdup(_("(not a number)"));
2225 val = xmalloc(50 + padding);
2226 strcat(formatPrefix, "s");
2228 /* this is important if sizeof(int_32) ! sizeof(time_t) */
2229 { time_t dateint = *((int_32 *) data);
2230 tstruct = localtime(&dateint);
2232 (void)strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
2233 sprintf(val, formatPrefix, buf);
2239 static char * dateFormat(int_32 type, const void * data,
2240 char * formatPrefix, int padding, int element)
2242 return realDateFormat(type, data, formatPrefix, padding, element, "%c");
2245 static char * dayFormat(int_32 type, const void * data,
2246 char * formatPrefix, int padding, int element)
2248 return realDateFormat(type, data, formatPrefix, padding, element,
2252 static char * shescapeFormat(int_32 type, const void * data,
2253 char * formatPrefix, int padding, /*@unused@*/int element)
2255 char * result, * dst, * src, * buf;
2257 if (type == RPM_INT32_TYPE) {
2258 result = xmalloc(padding + 20);
2259 strcat(formatPrefix, "d");
2260 sprintf(result, formatPrefix, *((int_32 *) data));
2262 buf = alloca(strlen(data) + padding + 2);
2263 strcat(formatPrefix, "s");
2264 sprintf(buf, formatPrefix, data);
2266 result = dst = xmalloc(strlen(buf) * 4 + 3);
2268 for (src = buf; *src; src++) {
2286 const struct headerSprintfExtension headerDefaultFormats[] = {
2287 { HEADER_EXT_FORMAT, "octal", { octalFormat } },
2288 { HEADER_EXT_FORMAT, "hex", { hexFormat } },
2289 { HEADER_EXT_FORMAT, "date", { dateFormat } },
2290 { HEADER_EXT_FORMAT, "day", { dayFormat } },
2291 { HEADER_EXT_FORMAT, "shescape", { shescapeFormat } },
2292 { HEADER_EXT_LAST, NULL, { NULL } }
2295 void headerCopyTags(Header headerFrom, Header headerTo, int *tagstocopy)
2299 if (headerFrom == headerTo)
2302 for (p = tagstocopy; *p != 0; p++) {
2305 if (headerIsEntry(headerTo, *p))
2307 if (!headerGetEntry(headerFrom, *p, &type, (void **) &s, &count))
2309 headerAddEntry(headerTo, *p, type, s, count);
2311 (type == RPM_STRING_ARRAY_TYPE || type == RPM_I18NSTRING_TYPE))