5 /* RPM - Copyright (C) 1995-2000 Red Hat Software */
7 /* Data written to file descriptors is in network byte order. */
8 /* Data read from file descriptors is expected to be in */
9 /* network byte order and is converted on the fly to host order. */
13 #define __HEADER_PROTOTYPES__
15 #include <header_internal.h>
19 /*@-redecl@*/ /* FIX: avoid rpmlib.h, need for debugging. */
20 /*@observer@*/ const char *const tagName(int tag) /*@*/;
23 /*@access entryInfo @*/
24 /*@access indexEntry @*/
26 /*@access extensionCache @*/
27 /*@access sprintfTag @*/
28 /*@access sprintfToken @*/
31 #define PARSER_BEGIN 0
32 #define PARSER_IN_ARRAY 1
33 #define PARSER_IN_EXPR 2
37 static unsigned char header_magic[8] = {
38 0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
42 * Maximum no. of bytes permitted in a header.
44 static size_t headerMaxbytes = (32*1024*1024);
47 * Sanity check on no. of tags.
48 * This check imposes a limit of 65K tags, more than enough.
50 #define hdrchkTags(_ntags) ((_ntags) & 0xffff0000)
53 * Sanity check on data size and/or offset.
54 * This check imposes a limit of 16Mb, more than enough.
56 #define hdrchkData(_nbytes) ((_nbytes) & 0xff000000)
59 * Alignment needs (and sizeof scalars types) for internal rpm data types.
61 static int typeSizes[] = {
62 0, /*!< RPM_NULL_TYPE */
63 1, /*!< RPM_CHAR_TYPE */
64 1, /*!< RPM_INT8_TYPE */
65 2, /*!< RPM_INT16_TYPE */
66 4, /*!< RPM_INT32_TYPE */
67 -1, /*!< RPM_INT64_TYPE */
68 -1, /*!< RPM_STRING_TYPE */
69 1, /*!< RPM_BIN_TYPE */
70 -1, /*!< RPM_STRING_ARRAY_TYPE */
71 -1 /*!< RPM_I18NSTRING_TYPE */
74 HV_t hdrVec; /* forward reference */
77 * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
78 * @param p memory to free
81 /*@unused@*/ static inline /*@null@*/ void *
82 _free(/*@only@*/ /*@null@*/ const void * p) /*@modifies *p @*/
84 if (p != NULL) free((void *)p);
90 Header h = xcalloc(1, sizeof(*h));
93 h->hv = *hdrVec; /* structure assignment */
95 h->indexAlloced = INDEX_MALLOC_SIZE;
97 h->flags = HEADERFLAG_SORTED;
100 h->index = (h->indexAlloced
101 ? xcalloc(h->indexAlloced, sizeof(*h->index))
109 Header headerFree(Header h)
111 if (h == NULL || --h->nrefs > 0)
112 return NULL; /* XXX return previous header? */
115 indexEntry entry = h->index;
117 for (i = 0; i < h->indexUsed; i++, entry++) {
118 if ((h->flags & HEADERFLAG_ALLOCATED) && ENTRY_IS_REGION(entry)) {
119 if (entry->length > 0) {
120 int_32 * ei = entry->data;
121 ei -= 2; /* XXX HACK: adjust to beginning of header. */
124 } else if (!ENTRY_IN_REGION(entry)) {
125 entry->data = _free(entry->data);
129 h->index = _free(h->index);
132 /*@-refcounttrans@*/ h = _free(h); /*@=refcounttrans@*/
136 Header headerLink(Header h)
139 /*@-refcounttrans@*/ return h; /*@=refcounttrans@*/
144 static int indexCmp(const void * avp, const void * bvp) /*@*/
147 indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
149 return (ap->info.tag - bp->info.tag);
152 void headerSort(Header h)
154 if (!(h->flags & HEADERFLAG_SORTED)) {
155 qsort(h->index, h->indexUsed, sizeof(*h->index), indexCmp);
156 h->flags |= HEADERFLAG_SORTED;
162 static int offsetCmp(const void * avp, const void * bvp) /*@*/
165 indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
167 int rc = (ap->info.offset - bp->info.offset);
170 rc = (ap->info.tag - bp->info.tag);
174 void headerUnsort(Header h)
176 qsort(h->index, h->indexUsed, sizeof(*h->index), offsetCmp);
179 unsigned int headerSizeof(Header h, enum hMagic magicp)
182 unsigned int size = 0;
183 unsigned int pad = 0;
192 case HEADER_MAGIC_YES:
193 size += sizeof(header_magic);
195 case HEADER_MAGIC_NO:
199 size += 2 * sizeof(int_32); /* count of index entries */
201 for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
205 /* Regions go in as is ... */
206 if (ENTRY_IS_REGION(entry)) {
207 size += entry->length;
208 /* XXX Legacy regions do not include the region tag and data. */
209 if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
210 size += sizeof(struct entryInfo) + entry->info.count;
214 /* ... and region elements are skipped. */
215 if (entry->info.offset < 0)
219 type = entry->info.type;
220 if (typeSizes[type] > 1) {
221 diff = typeSizes[type] - (size % typeSizes[type]);
222 if (diff != typeSizes[type]) {
228 size += sizeof(struct entryInfo) + entry->length;
235 * Return length of entry data.
236 * @param type entry data type
237 * @param p entry data
238 * @param count entry item count
239 * @param onDisk data is concatenated strings (with NUL's))?
240 * @return no. bytes in data
243 static int dataLength(int_32 type, hPTR_t p, int_32 count, int onDisk)
244 /*@modifies fileSystem @*/
249 case RPM_STRING_TYPE:
250 if (count == 1) { /* Special case -- p is just the string */
251 length = strlen(p) + 1;
254 /* This should not be allowed */
255 fprintf(stderr, _("dataLength() RPM_STRING_TYPE count must be 1.\n"));
257 /*@notreached@*/ break;
259 case RPM_STRING_ARRAY_TYPE:
260 case RPM_I18NSTRING_TYPE:
263 /* This is like RPM_STRING_TYPE, except it's *always* an array */
264 /* Compute sum of length of all strings, including null terminators */
268 const char * chptr = p;
272 thisLen = strlen(chptr) + 1;
277 const char ** src = (const char **)p;
279 /* add one for null termination */
280 length += strlen(*src++) + 1;
286 if (typeSizes[type] != -1) {
287 length = typeSizes[type] * count;
290 fprintf(stderr, _("Data type %d not supported\n"), (int) type);
292 /*@notreached@*/ break;
299 * Swap int_32 and int_16 arrays within header region.
301 * This code is way more twisty than I would like.
303 * A bug with RPM_I18NSTRING_TYPE in rpm-2.5.x (fixed in August 1998)
304 * causes the offset and length of elements in a header region to disagree
305 * regarding the total length of the region data.
307 * The "fix" is to compute the size using both offset and length and
308 * return the larger of the two numbers as the size of the region.
309 * Kinda like computing left and right Riemann sums of the data elements
310 * to determine the size of a data structure, go figger :-).
312 * There's one other twist if a header region tag is in the set to be swabbed,
313 * as the data for a header region is located after all other tag data.
315 * @param entry header entry
316 * @param il no. of entries
317 * @param dl start no. bytes of data
318 * @param pe header physical entry pointer (swapped)
319 * @param dataStart header data
320 * @param regionid region offset
321 * @return no. bytes of data in region, -1 on error
323 static int regionSwab(/*@null@*/ indexEntry entry, int il, int dl,
324 entryInfo pe, char * dataStart, int regionid)
325 /*@modifies *entry, *dataStart @*/
330 struct indexEntry ieprev;
332 memset(&ieprev, 0, sizeof(ieprev));
333 for (; il > 0; il--, pe++) {
334 struct indexEntry ie;
337 ie.info.tag = ntohl(pe->tag);
338 ie.info.type = ntohl(pe->type);
339 if (ie.info.type < RPM_MIN_TYPE || ie.info.type > RPM_MAX_TYPE)
341 ie.info.count = ntohl(pe->count);
342 ie.info.offset = ntohl(pe->offset);
343 ie.data = t = dataStart + ie.info.offset;
344 ie.length = dataLength(ie.info.type, ie.data, ie.info.count, 1);
348 ie.info.offset = regionid;
349 *entry = ie; /* structure assignment */
355 if (typeSizes[type] > 1) {
357 diff = typeSizes[type] - (dl % typeSizes[type]);
358 if (diff != typeSizes[type]) {
360 if (ieprev.info.type == RPM_I18NSTRING_TYPE)
361 ieprev.length += diff;
364 tdel = (tprev ? (t - tprev) : 0);
365 if (ieprev.info.type == RPM_I18NSTRING_TYPE)
366 tdel = ieprev.length;
368 if (ie.info.tag >= HEADER_I18NTABLE) {
372 /* XXX HEADER_IMAGE tags don't include region sub-tag. */
373 if (ie.info.tag != HEADER_IMMUTABLE)
374 tprev -= REGION_TAG_COUNT;
377 /* Perform endian conversions */
378 switch (ntohl(pe->type)) {
380 { int_32 * it = (int_32 *)t;
381 for (; ie.info.count > 0; ie.info.count--, it += 1)
386 { int_16 * it = (int_16 *) t;
387 for (; ie.info.count > 0; ie.info.count--, it += 1)
398 ieprev = ie; /* structure assignment */
401 tdel = (tprev ? (t - tprev) : 0);
405 * There are two hacks here:
406 * 1) tl is 16b (i.e. REGION_TAG_COUNT) short while doing headerReload().
407 * 2) the 8/98 rpm bug with inserting i18n tags needs to use tl, not dl.
409 if (tl+REGION_TAG_COUNT == dl)
410 tl += REGION_TAG_COUNT;
418 int headerDrips(const Header h)
423 for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
424 if (ENTRY_IS_REGION(entry)) {
425 int rid = entry->info.offset;
427 for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
428 if (entry->info.offset <= rid)
436 /* Ignore deleted drips. */
437 if (entry->data == NULL || entry->length <= 0)
446 static /*@only@*/ /*@null@*/ void * doHeaderUnload(Header h,
447 /*@out@*/ int * lengthPtr)
448 /*@modifies h, *lengthPtr @*/
461 int drlen, ndribbles;
465 /* Sort entries by (offset,tag). */
468 /* Compute (il,dl) for all tags, including those deleted in region. */
470 drlen = ndribbles = driplen = ndrips = 0;
471 for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
472 if (ENTRY_IS_REGION(entry)) {
473 int_32 rdl = -entry->info.offset; /* negative offset */
474 int_32 ril = rdl/sizeof(*pe);
475 int rid = entry->info.offset;
478 dl += entry->rdlen + entry->info.count;
479 /* XXX Legacy regions do not include the region tag and data. */
480 if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
483 /* Skip rest of entries in region, but account for dribbles. */
484 for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
485 if (entry->info.offset <= rid)
489 type = entry->info.type;
490 if (typeSizes[type] > 1) {
492 diff = typeSizes[type] - (dl % typeSizes[type]);
493 if (diff != typeSizes[type]) {
502 drlen += entry->length;
510 /* Ignore deleted drips. */
511 if (entry->data == NULL || entry->length <= 0)
515 type = entry->info.type;
516 if (typeSizes[type] > 1) {
518 diff = typeSizes[type] - (dl % typeSizes[type]);
519 if (diff != typeSizes[type]) {
529 driplen += entry->length;
533 /* Sanity checks on header intro. */
534 if (hdrchkTags(il) || hdrchkData(dl))
537 len = sizeof(il) + sizeof(dl) + (il * sizeof(*pe)) + dl;
543 pe = (entryInfo) &ei[2];
544 dataStart = te = (char *) (pe + il);
547 for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
553 if (entry->data == NULL || entry->length <= 0)
557 pe->tag = htonl(entry->info.tag);
558 pe->type = htonl(entry->info.type);
559 pe->count = htonl(entry->info.count);
561 if (ENTRY_IS_REGION(entry)) {
562 int_32 rdl = -entry->info.offset; /* negative offset */
563 int_32 ril = rdl/sizeof(*pe) + ndribbles;
564 int rid = entry->info.offset;
566 src = (char *)entry->data;
567 rdlen = entry->rdlen;
569 /* XXX Legacy regions do not include the region tag and data. */
570 if (i == 0 && (h->flags & HEADERFLAG_LEGACY)) {
574 memcpy(pe+1, src, rdl);
575 memcpy(te, src + rdl, rdlen);
578 pe->offset = htonl(te - dataStart);
581 stei[2] = htonl(-rdl-entry->info.count);
583 memcpy(te, stei, entry->info.count);
584 te += entry->info.count;
586 rdlen += entry->info.count;
588 count = regionSwab(NULL, ril, 0, pe, t, 0);
589 if (count != rdlen) goto errxit;
593 memcpy(pe+1, src + sizeof(*pe), ((ril-1) * sizeof(*pe)));
594 memcpy(te, src + (ril * sizeof(*pe)), rdlen+entry->info.count+drlen);
597 entryInfo se = (entryInfo)src;
599 int off = ntohl(se->offset);
600 pe->offset = (off) ? htonl(te - dataStart) : htonl(off);
602 te += entry->info.count + drlen;
604 count = regionSwab(NULL, ril, 0, pe, t, 0);
605 if (count != (rdlen + entry->info.count + drlen)) goto errxit;
608 /* Skip rest of entries in region. */
609 while (i < h->indexUsed && entry->info.offset <= rid+1) {
619 /* Ignore deleted drips. */
620 if (entry->data == NULL || entry->length <= 0)
624 type = entry->info.type;
625 if (typeSizes[type] > 1) {
627 diff = typeSizes[type] - ((te - dataStart) % typeSizes[type]);
628 if (diff != typeSizes[type]) {
635 pe->offset = htonl(te - dataStart);
637 /* copy data w/ endian conversions */
638 switch (entry->info.type) {
640 count = entry->info.count;
643 *((int_32 *)te) = htonl(*((int_32 *)src));
644 te += sizeof(int_32);
645 src += sizeof(int_32);
650 count = entry->info.count;
653 *((int_16 *)te) = htons(*((int_16 *)src));
654 te += sizeof(int_16);
655 src += sizeof(int_16);
660 memcpy(te, entry->data, entry->length);
667 /* Insure that there are no memcpy underruns/overruns. */
668 if (((char *)pe) != dataStart) goto errxit;
669 if ((((char *)ei)+len) != te) goto errxit;
674 h->flags &= ~HEADERFLAG_SORTED;
686 void * headerUnload(Header h)
689 void * uh = doHeaderUnload(h, &length);
693 Header headerReload(Header h, int tag)
698 void * uh = doHeaderUnload(h, &length);
709 if (nh->flags & HEADERFLAG_ALLOCATED)
711 nh->flags |= HEADERFLAG_ALLOCATED;
712 if (ENTRY_IS_REGION(nh->index)) {
713 if (tag == HEADER_SIGNATURES || tag == HEADER_IMMUTABLE)
714 nh->index[0].info.tag = tag;
719 Header headerCopy(Header h)
721 Header nh = headerNew();
723 int_32 tag, type, count;
726 for (hi = headerInitIterator(h);
727 headerNextIterator(hi, &tag, &type, &ptr, &count);
728 ptr = headerFreeData((void *)ptr, type))
730 if (ptr) (void) headerAddEntry(nh, tag, type, ptr, count);
732 hi = headerFreeIterator(hi);
734 return headerReload(nh, HEADER_IMAGE);
737 Header headerLoad(void * uh)
739 int_32 * ei = (int_32 *) uh;
740 int_32 il = ntohl(ei[0]); /* index length */
741 int_32 dl = ntohl(ei[1]); /* data length */
742 size_t pvlen = sizeof(il) + sizeof(dl) +
743 (il * sizeof(struct entryInfo)) + dl;
752 /* Sanity checks on header intro. */
753 if (hdrchkTags(il) || hdrchkData(dl))
758 pe = (entryInfo) &ei[2];
760 dataStart = (char *) (pe + il);
762 h = xcalloc(1, sizeof(*h));
764 h->hv = *hdrVec; /* structure assignment */
766 h->indexAlloced = il + 1;
768 h->index = xcalloc(h->indexAlloced, sizeof(*h->index));
769 h->flags = HEADERFLAG_SORTED;
773 * XXX XFree86-libs, ash, and pdksh from Red Hat 5.2 have bogus
774 * %verifyscript tag that needs to be diddled.
776 if (ntohl(pe->tag) == 15 &&
777 ntohl(pe->type) == RPM_STRING_TYPE &&
778 ntohl(pe->count) == 1)
780 pe->tag = htonl(1079);
785 if (!(htonl(pe->tag) < HEADER_I18NTABLE)) {
786 h->flags |= HEADERFLAG_LEGACY;
787 entry->info.type = REGION_TAG_TYPE;
788 entry->info.tag = HEADER_IMAGE;
789 entry->info.count = REGION_TAG_COUNT;
790 entry->info.offset = ((char *)pe - dataStart); /* negative offset */
795 entry->length = pvlen - sizeof(il) - sizeof(dl);
796 rdlen = regionSwab(entry+1, il, 0, pe, dataStart, entry->info.offset);
797 #if 0 /* XXX don't check, the 8/98 i18n bug fails here. */
801 entry->rdlen = rdlen;
805 int nb = ntohl(pe->count);
809 h->flags &= ~HEADERFLAG_LEGACY;
811 entry->info.type = htonl(pe->type);
812 if (entry->info.type < RPM_MIN_TYPE || entry->info.type > RPM_MAX_TYPE)
814 entry->info.count = htonl(pe->count);
816 if (hdrchkTags(entry->info.count))
819 { int off = ntohl(pe->offset);
824 int_32 * stei = memcpy(alloca(nb), dataStart + off, nb);
825 rdl = -ntohl(stei[2]); /* negative offset */
826 ril = rdl/sizeof(*pe);
827 if (hdrchkTags(ril) || hdrchkData(rdl))
829 entry->info.tag = htonl(pe->tag);
832 rdl = (ril * sizeof(struct entryInfo));
833 entry->info.tag = HEADER_IMAGE;
836 entry->info.offset = -rdl; /* negative offset */
841 entry->length = pvlen - sizeof(il) - sizeof(dl);
842 rdlen = regionSwab(entry+1, ril-1, 0, pe+1, dataStart, entry->info.offset);
845 entry->rdlen = rdlen;
847 if (ril < h->indexUsed) {
848 indexEntry newEntry = entry + ril;
849 int ne = (h->indexUsed - ril);
850 int rid = entry->info.offset+1;
853 /* Load dribble entries from region. */
854 rc = regionSwab(newEntry, ne, 0, pe+ril, dataStart, rid);
859 { indexEntry firstEntry = newEntry;
860 int save = h->indexUsed;
863 /* Dribble entries replace duplicate region entries. */
865 for (j = 0; j < ne; j++, newEntry++) {
866 (void) headerRemoveEntry(h, newEntry->info.tag);
867 if (newEntry->info.tag == HEADER_BASENAMES)
868 (void) headerRemoveEntry(h, HEADER_OLDFILENAMES);
871 /* If any duplicate entries were replaced, move new entries down. */
872 if (h->indexUsed < (save - ne)) {
873 memmove(h->index + h->indexUsed, firstEntry,
874 (ne * sizeof(*entry)));
881 h->flags &= ~HEADERFLAG_SORTED;
891 h->index = _free(h->index);
897 /*@-refcounttrans -globstate@*/
899 /*@=refcounttrans =globstate@*/
902 Header headerCopyLoad(const void * uh)
904 int_32 * ei = (int_32 *) uh;
905 int_32 il = ntohl(ei[0]); /* index length */
906 int_32 dl = ntohl(ei[1]); /* data length */
907 size_t pvlen = sizeof(il) + sizeof(dl) +
908 (il * sizeof(struct entryInfo)) + dl;
912 /* Sanity checks on header intro. */
913 if (!(hdrchkTags(il) || hdrchkData(dl)) && pvlen < headerMaxbytes) {
914 nuh = memcpy(xmalloc(pvlen), uh, pvlen);
915 if ((h = headerLoad(nuh)) != NULL)
916 h->flags |= HEADERFLAG_ALLOCATED;
923 Header headerRead(FD_t fd, enum hMagic magicp)
935 memset(block, 0, sizeof(block));
937 if (magicp == HEADER_MAGIC_YES)
940 if (timedRead(fd, (char *)block, i*sizeof(*block)) != (i * sizeof(*block)))
945 if (magicp == HEADER_MAGIC_YES) {
947 if (memcmp(&magic, header_magic, sizeof(magic)))
949 reserved = block[i++];
952 il = ntohl(block[i++]);
953 dl = ntohl(block[i++]);
955 len = sizeof(il) + sizeof(dl) + (il * sizeof(struct entryInfo)) + dl;
957 /* Sanity checks on header intro. */
958 if (hdrchkTags(il) || hdrchkData(dl) || len > headerMaxbytes)
964 len -= sizeof(il) + sizeof(dl);
966 if (timedRead(fd, (char *)&ei[2], len) != len)
973 if (h->flags & HEADERFLAG_ALLOCATED)
975 h->flags |= HEADERFLAG_ALLOCATED;
978 /*@-mustmod@*/ /* FIX: timedRead macro obscures annotation */
983 int headerWrite(FD_t fd, Header h, enum hMagic magicp)
991 uh = doHeaderUnload(h, &length);
995 case HEADER_MAGIC_YES:
996 nb = Fwrite(header_magic, sizeof(char), sizeof(header_magic), fd);
997 if (nb != sizeof(header_magic))
1000 case HEADER_MAGIC_NO:
1004 nb = Fwrite(uh, sizeof(char), length, fd);
1008 return (nb == length ? 0 : 1);
1012 * Find matching (tag,type) entry in header.
1014 * @param tag entry tag
1015 * @param type entry type
1016 * @return header entry
1019 indexEntry findEntry(/*@null@*/ Header h, int_32 tag, int_32 type)
1022 indexEntry entry, entry2, last;
1023 struct indexEntry key;
1025 if (h == NULL) return NULL;
1026 if (!(h->flags & HEADERFLAG_SORTED)) headerSort(h);
1031 bsearch(&key, h->index, h->indexUsed, sizeof(*h->index), indexCmp);
1035 if (type == RPM_NULL_TYPE)
1038 /* look backwards */
1039 while (entry->info.tag == tag && entry->info.type != type &&
1040 entry > h->index) entry--;
1042 if (entry->info.tag == tag && entry->info.type == type)
1045 last = h->index + h->indexUsed;
1046 while (entry2->info.tag == tag && entry2->info.type != type &&
1047 entry2 < last) entry2++;
1049 if (entry->info.tag == tag && entry->info.type == type)
1055 int headerIsEntry(Header h, int_32 tag)
1057 /*@-mods@*/ /*@ FIX: h modified by sort. */
1058 return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
1063 * Retrieve data from header entry.
1064 * @todo Permit retrieval of regions other than HEADER_IMUTABLE.
1065 * @param entry header entry
1066 * @retval type address of type (or NULL)
1067 * @retval p address of data (or NULL)
1068 * @retval c address of count (or NULL)
1069 * @param minMem string pointers refer to header memory?
1070 * @return 1 on success, otherwise error.
1072 static int copyEntry(const indexEntry entry,
1073 /*@null@*/ /*@out@*/ hTYP_t type,
1074 /*@null@*/ /*@out@*/ hPTR_t * p,
1075 /*@null@*/ /*@out@*/ hCNT_t c,
1077 /*@modifies *type, *p, *c @*/
1079 int_32 count = entry->info.count;
1080 int rc = 1; /* XXX 1 on success. */
1083 switch (entry->info.type) {
1085 /* XXX this only works for HEADER_IMMUTABLE */
1086 if (ENTRY_IS_REGION(entry)) {
1087 int_32 * ei = ((int_32 *)entry->data) - 2;
1089 entryInfo pe = (entryInfo) (ei + 2);
1091 char * dataStart = (char *) (pe + ntohl(ei[0]));
1092 int_32 rdl = -entry->info.offset; /* negative offset */
1093 int_32 ril = rdl/sizeof(*pe);
1095 count = 2 * sizeof(*ei) + (ril * sizeof(*pe)) +
1096 entry->rdlen + REGION_TAG_COUNT;
1097 *p = xmalloc(count);
1100 ei[1] = htonl(entry->rdlen + REGION_TAG_COUNT);
1102 pe = (entryInfo) memcpy(ei + 2, pe, (ril * sizeof(*pe)));
1104 dataStart = (char *) memcpy(pe + ril, dataStart,
1105 (entry->rdlen + REGION_TAG_COUNT));
1107 rc = regionSwab(NULL, ril, 0, pe, dataStart, 0);
1108 /* XXX 1 on success. */
1109 rc = (rc < 0) ? 0 : 1;
1111 count = entry->length;
1113 ? memcpy(xmalloc(count), entry->data, count)
1117 case RPM_STRING_TYPE:
1123 case RPM_STRING_ARRAY_TYPE:
1124 case RPM_I18NSTRING_TYPE:
1125 { const char ** ptrEntry;
1126 int tableSize = count * sizeof(char *);
1132 *p = xmalloc(tableSize);
1133 ptrEntry = (const char **) *p;
1136 t = xmalloc(tableSize + entry->length);
1138 ptrEntry = (const char **) *p;
1140 memcpy(t, entry->data, entry->length);
1143 for (i = 0; i < count; i++) {
1154 if (type) *type = entry->info.type;
1160 * Does locale match entry in header i18n table?
1163 * The range [l,le) contains the next locale to match:
1164 * ll[_CC][.EEEEE][@dddd]
1166 * ll ISO language code (in lowercase).
1167 * CC (optional) ISO coutnry code (in uppercase).
1168 * EEEEE (optional) encoding (not really standardized).
1169 * dddd (optional) dialect.
1172 * @param td header i18n table data, NUL terminated
1173 * @param l start of locale to match
1174 * @param le end of locale to match
1175 * @return 1 on match, 0 on no match
1177 static int headerMatchLocale(const char *td, const char *l, const char *le)
1184 { const char *s, *ll, *CC, *EE, *dd;
1187 /* Copy the buffer and parse out components on the fly. */
1188 lbuf = alloca(le - l + 1);
1189 for (s = l, ll = t = lbuf; *s; s++, t++) {
1209 if (ll) /* ISO language should be lower case */
1210 for (t = ll; *t; t++) *t = tolower(*t);
1211 if (CC) /* ISO country code should be upper case */
1212 for (t = CC; *t; t++) *t = toupper(*t);
1214 /* There are a total of 16 cases to attempt to match. */
1218 /* First try a complete match. */
1219 if (strlen(td) == (le-l) && !strncmp(td, l, (le - l)))
1222 /* Next, try stripping optional dialect and matching. */
1223 for (fe = l; fe < le && *fe != '@'; fe++)
1225 if (fe < le && !strncmp(td, l, (fe - l)))
1228 /* Next, try stripping optional codeset and matching. */
1229 for (fe = l; fe < le && *fe != '.'; fe++)
1231 if (fe < le && !strncmp(td, l, (fe - l)))
1234 /* Finally, try stripping optional country code and matching. */
1235 for (fe = l; fe < le && *fe != '_'; fe++)
1237 if (fe < le && !strncmp(td, l, (fe - l)))
1244 * Return i18n string from header that matches locale.
1246 * @param entry i18n string data
1247 * @return matching i18n string (or 1st string if no match)
1249 /*@dependent@*/ static char *
1250 headerFindI18NString(Header h, indexEntry entry)
1252 const char *lang, *l, *le;
1255 /* XXX Drepper sez' this is the order. */
1256 if ((lang = getenv("LANGUAGE")) == NULL &&
1257 (lang = getenv("LC_ALL")) == NULL &&
1258 (lang = getenv("LC_MESSAGES")) == NULL &&
1259 (lang = getenv("LANG")) == NULL)
1260 /*@-retalias -retexpose@*/
1262 /*@=retalias =retexpose@*/
1264 if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
1265 /*@-retalias -retexpose@*/
1267 /*@=retalias =retexpose@*/
1269 for (l = lang; *l != '\0'; l = le) {
1274 while (*l && *l == ':') /* skip leading colons */
1278 for (le = l; *le && *le != ':'; le++) /* find end of this locale */
1281 /* For each entry in the header ... */
1282 for (langNum = 0, td = table->data, ed = entry->data;
1283 langNum < entry->info.count;
1284 langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
1286 if (headerMatchLocale(td, l, le))
1292 /*@-retalias -retexpose@*/
1294 /*@=retalias =retexpose@*/
1298 * Retrieve tag data from header.
1300 * @param tag tag to retrieve
1301 * @retval type address of type (or NULL)
1302 * @retval p address of data (or NULL)
1303 * @retval c address of count (or NULL)
1304 * @param minMem string pointers reference header memory?
1305 * @return 1 on success, 0 on not found
1307 static int intGetEntry(Header h, int_32 tag,
1308 /*@null@*/ /*@out@*/ hTAG_t type,
1309 /*@null@*/ /*@out@*/ hPTR_t * p,
1310 /*@null@*/ /*@out@*/ hCNT_t c,
1312 /*@modifies *type, *p, *c @*/
1317 /* First find the tag */
1318 /*@-mods@*/ /*@ FIX: h modified by sort. */
1319 entry = findEntry(h, tag, RPM_NULL_TYPE);
1321 if (entry == NULL) {
1328 switch (entry->info.type) {
1329 case RPM_I18NSTRING_TYPE:
1331 if (type) *type = RPM_STRING_TYPE;
1333 /*@-dependenttrans@*/
1334 if (p) *p = headerFindI18NString(h, entry);
1335 /*@=dependenttrans@*/
1338 rc = copyEntry(entry, type, p, c, minMem);
1342 /* XXX 1 on success */
1343 return ((rc == 1) ? 1 : 0);
1347 * Free data allocated when retrieved from header.
1349 * @param data address of data (or NULL)
1350 * @param type type of data (or -1 to force free)
1351 * @return NULL always
1353 static /*@null@*/ void * headerFreeTag(/*@unused@*/ Header h,
1354 /*@only@*/ /*@null@*/ const void * data, rpmTagType type)
1355 /*@modifies data @*/
1360 type == RPM_STRING_ARRAY_TYPE ||
1361 type == RPM_I18NSTRING_TYPE ||
1362 type == RPM_BIN_TYPE)
1369 int headerGetEntry(Header h, int_32 tag, hTYP_t type, void **p, hCNT_t c)
1371 return intGetEntry(h, tag, type, (hPTR_t *)p, c, 0);
1374 int headerGetEntryMinMemory(Header h, int_32 tag, hTYP_t type, hPTR_t * p,
1377 return intGetEntry(h, tag, type, p, c, 1);
1380 int headerGetRawEntry(Header h, int_32 tag, int_32 * type, hPTR_t * p,
1386 if (p == NULL) return headerIsEntry(h, tag);
1388 /* First find the tag */
1389 /*@-mods@*/ /*@ FIX: h modified by sort. */
1390 entry = findEntry(h, tag, RPM_NULL_TYPE);
1398 rc = copyEntry(entry, type, p, c, 0);
1400 /* XXX 1 on success */
1401 return ((rc == 1) ? 1 : 0);
1406 static void copyData(int_32 type, /*@out@*/ void * dstPtr, const void * srcPtr,
1407 int_32 c, int dataLength)
1408 /*@modifies *dstPtr @*/
1415 case RPM_STRING_ARRAY_TYPE:
1416 case RPM_I18NSTRING_TYPE:
1417 /* Otherwise, p is char** */
1419 src = (const char **) srcPtr;
1423 int len = strlen(*src) + 1;
1424 memcpy(dst, *src, len);
1432 memmove(dstPtr, srcPtr, dataLength);
1438 * Return (malloc'ed) copy of entry data.
1439 * @param type entry data type
1440 * @param p entry data
1441 * @param c entry item count
1442 * @retval lengthPtr no. bytes in returned data
1443 * @return (malloc'ed) copy of entry data
1445 static void * grabData(int_32 type, hPTR_t p, int_32 c,
1446 /*@out@*/ int * lengthPtr)
1447 /*@modifies *lengthPtr @*/
1449 int length = dataLength(type, p, c, 0);
1450 void * data = xmalloc(length);
1452 copyData(type, data, p, c, length);
1455 *lengthPtr = length;
1459 int headerAddEntry(Header h, int_32 tag, int_32 type, hPTR_t p, int_32 c)
1463 /* Count must always be >= 1 for headerAddEntry. */
1467 /* Allocate more index space if necessary */
1468 if (h->indexUsed == h->indexAlloced) {
1469 h->indexAlloced += INDEX_MALLOC_SIZE;
1470 h->index = xrealloc(h->index, h->indexAlloced * sizeof(*h->index));
1473 /* Fill in the index */
1474 entry = h->index + h->indexUsed;
1475 entry->info.tag = tag;
1476 entry->info.type = type;
1477 entry->info.count = c;
1478 entry->info.offset = 0;
1479 entry->data = grabData(type, p, c, &entry->length);
1481 if (h->indexUsed > 0 && tag < h->index[h->indexUsed-1].info.tag)
1482 h->flags &= ~HEADERFLAG_SORTED;
1488 int headerAppendEntry(Header h, int_32 tag, int_32 type,
1494 /* First find the tag */
1495 entry = findEntry(h, tag, type);
1499 if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
1500 /* we can't do this */
1504 length = dataLength(type, p, c, 0);
1506 if (ENTRY_IN_REGION(entry)) {
1507 char * t = xmalloc(entry->length + length);
1508 memcpy(t, entry->data, entry->length);
1510 entry->info.offset = 0;
1512 entry->data = xrealloc(entry->data, entry->length + length);
1514 copyData(type, ((char *) entry->data) + entry->length, p, c, length);
1516 entry->length += length;
1518 entry->info.count += c;
1523 int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
1526 return (findEntry(h, tag, type)
1527 ? headerAppendEntry(h, tag, type, p, c)
1528 : headerAddEntry(h, tag, type, p, c));
1531 int headerAddI18NString(Header h, int_32 tag, const char * string, const char * lang)
1533 indexEntry table, entry;
1534 const char ** strArray;
1540 table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
1541 entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
1543 if (!table && entry)
1544 return 0; /* this shouldn't ever happen!! */
1546 if (!table && !entry) {
1547 const char * charArray[2];
1549 if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
1550 /*@-observertrans -readonlytrans@*/
1551 charArray[count++] = "C";
1552 /*@=observertrans =readonlytrans@*/
1554 /*@-observertrans -readonlytrans@*/
1555 charArray[count++] = "C";
1556 /*@=observertrans =readonlytrans@*/
1557 charArray[count++] = lang;
1559 if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE,
1562 table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
1567 if (!lang) lang = "C";
1569 { const char * l = table->data;
1570 for (langNum = 0; langNum < table->info.count; langNum++) {
1571 if (!strcmp(l, lang)) break;
1576 if (langNum >= table->info.count) {
1577 length = strlen(lang) + 1;
1578 if (ENTRY_IN_REGION(table)) {
1579 char * t = xmalloc(table->length + length);
1580 memcpy(t, table->data, table->length);
1582 table->info.offset = 0;
1584 table->data = xrealloc(table->data, table->length + length);
1585 memmove(((char *)table->data) + table->length, lang, length);
1586 table->length += length;
1587 table->info.count++;
1591 strArray = alloca(sizeof(*strArray) * (langNum + 1));
1592 for (i = 0; i < langNum; i++)
1594 strArray[langNum] = string;
1595 return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, strArray,
1597 } else if (langNum >= entry->info.count) {
1598 ghosts = langNum - entry->info.count;
1600 length = strlen(string) + 1 + ghosts;
1601 if (ENTRY_IN_REGION(entry)) {
1602 char * t = xmalloc(entry->length + length);
1603 memcpy(t, entry->data, entry->length);
1605 entry->info.offset = 0;
1607 entry->data = xrealloc(entry->data, entry->length + length);
1609 memset(((char *)entry->data) + entry->length, '\0', ghosts);
1611 strcpy(((char *)entry->data) + entry->length + ghosts, string);
1613 memmove(((char *)entry->data) + entry->length + ghosts, string, strlen(string)+1);
1616 entry->length += length;
1617 entry->info.count = langNum + 1;
1619 char *b, *be, *e, *ee, *t;
1622 /* Set beginning/end pointers to previous data */
1623 b = be = e = ee = entry->data;
1624 for (i = 0; i < table->info.count; i++) {
1627 ee += strlen(ee) + 1;
1632 /* Get storage for new buffer */
1634 sn = strlen(string) + 1;
1636 length = bn + sn + en;
1637 t = buf = xmalloc(length);
1639 /* Copy values into new storage */
1642 memcpy(t, string, sn);
1647 /* Replace I18N string array */
1648 entry->length -= strlen(be) + 1;
1649 entry->length += sn;
1651 if (ENTRY_IN_REGION(entry)) {
1652 entry->info.offset = 0;
1654 entry->data = _free(entry->data);
1655 /*@-dependenttrans@*/
1657 /*@=dependenttrans@*/
1663 int headerModifyEntry(Header h, int_32 tag, int_32 type, hPTR_t p, int_32 c)
1668 /* First find the tag */
1669 entry = findEntry(h, tag, type);
1673 /* make sure entry points to the first occurence of this tag */
1674 while (entry > h->index && (entry - 1)->info.tag == tag)
1677 /* free after we've grabbed the new data in case the two are intertwined;
1678 that's a bad idea but at least we won't break */
1679 oldData = entry->data;
1681 entry->info.count = c;
1682 entry->info.type = type;
1683 entry->data = grabData(type, p, c, &entry->length);
1685 if (ENTRY_IN_REGION(entry)) {
1686 entry->info.offset = 0;
1688 oldData = _free(oldData);
1693 int headerRemoveEntry(Header h, int_32 tag)
1695 indexEntry last = h->index + h->indexUsed;
1696 indexEntry entry, first;
1699 entry = findEntry(h, tag, RPM_NULL_TYPE);
1700 if (!entry) return 1;
1702 /* Make sure entry points to the first occurence of this tag. */
1703 while (entry > h->index && (entry - 1)->info.tag == tag)
1706 /* Free data for tags being removed. */
1707 for (first = entry; first < last; first++) {
1709 if (first->info.tag != tag)
1714 if (ENTRY_IN_REGION(first))
1719 ne = (first - entry);
1724 memmove(entry, first, (ne * sizeof(*entry)));
1732 static char escapedChar(const char ch) /*@*/
1735 case 'a': return '\a';
1736 case 'b': return '\b';
1737 case 'f': return '\f';
1738 case 'n': return '\n';
1739 case 'r': return '\r';
1740 case 't': return '\t';
1741 case 'v': return '\v';
1747 * Destroy headerSprintf format array.
1748 * @param format sprintf format array
1749 * @param num number of elements
1750 * @return NULL always
1752 static /*@null@*/ sprintfToken
1753 freeFormat( /*@only@*/ /*@null@*/ sprintfToken format, int num)
1754 /*@modifies *format @*/
1758 if (format == NULL) return NULL;
1759 for (i = 0; i < num; i++) {
1760 switch (format[i].type) {
1762 format[i].u.array.format =
1763 freeFormat(format[i].u.array.format,
1764 format[i].u.array.numTokens);
1767 format[i].u.cond.ifFormat =
1768 freeFormat(format[i].u.cond.ifFormat,
1769 format[i].u.cond.numIfTokens);
1770 format[i].u.cond.elseFormat =
1771 freeFormat(format[i].u.cond.elseFormat,
1772 format[i].u.cond.numElseTokens);
1781 format = _free(format);
1787 static void findTag(char * name, const headerTagTableEntry tags,
1788 const headerSprintfExtension extensions,
1789 /*@out@*/ headerTagTableEntry * tagMatch,
1790 /*@out@*/ headerSprintfExtension * extMatch)
1791 /*@modifies *tagMatch, *extMatch @*/
1793 headerTagTableEntry entry;
1794 headerSprintfExtension ext;
1795 const char * tagname;
1800 if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
1801 char * t = alloca(strlen(name) + sizeof("RPMTAG_"));
1802 (void) stpcpy( stpcpy(t, "RPMTAG_"), name);
1808 /* Search extensions first to permit overriding header tags. */
1810 while (ext->type != HEADER_EXT_LAST) {
1811 if (ext->name != NULL && ext->type == HEADER_EXT_TAG
1812 && !xstrcasecmp(ext->name, tagname))
1815 if (ext->type == HEADER_EXT_MORE)
1821 if (ext->type == HEADER_EXT_TAG) {
1826 /* Search header tags. */
1827 for (entry = tags; entry->name; entry++)
1828 if (entry->name && !xstrcasecmp(entry->name, tagname))
1838 static int parseExpression(sprintfToken token, char * str,
1839 const headerTagTableEntry tags,
1840 const headerSprintfExtension extensions,
1841 /*@out@*/char ** endPtr, /*@null@*/ /*@out@*/ errmsg_t * errmsg)
1842 /*@modifies str, *str, *token, *endPtr, *errmsg @*/;
1846 static int parseFormat(char * str, const headerTagTableEntry tags,
1847 const headerSprintfExtension extensions,
1848 /*@out@*/sprintfToken * formatPtr, /*@out@*/int * numTokensPtr,
1849 /*@null@*/ /*@out@*/ char ** endPtr, int state,
1850 /*@null@*/ /*@out@*/ errmsg_t * errmsg)
1851 /*@modifies str, *str, *formatPtr, *numTokensPtr, *endPtr, *errmsg @*/
1853 char * chptr, * start, * next, * dst;
1854 sprintfToken format;
1857 headerTagTableEntry tag;
1858 headerSprintfExtension ext;
1862 /* upper limit on number of individual formats */
1864 for (chptr = str; *chptr != '\0'; chptr++)
1865 if (*chptr == '%') numTokens++;
1866 numTokens = numTokens * 2 + 1;
1868 format = xcalloc(numTokens, sizeof(*format));
1869 if (endPtr) *endPtr = NULL;
1874 while (*start != '\0') {
1878 if (*(start + 1) == '%') {
1879 if (currToken < 0 || format[currToken].type != PTOK_STRING) {
1881 format[currToken].type = PTOK_STRING;
1882 /*@-temptrans -assignexpose@*/
1883 dst = format[currToken].u.string.string = start;
1884 /*@=temptrans =assignexpose@*/
1891 break; /* out of switch */
1898 if (*start == '|') {
1902 if (parseExpression(format + currToken, start, tags,
1903 extensions, &newEnd, errmsg))
1905 format = freeFormat(format, numTokens);
1909 break; /* out of switch */
1913 format[currToken].u.tag.format = start;
1915 format[currToken].u.tag.pad = 0;
1916 format[currToken].u.tag.justOne = 0;
1917 format[currToken].u.tag.arrayCount = 0;
1920 while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
1921 if (!*chptr || *chptr == '%') {
1922 /*@-observertrans -readonlytrans@*/
1923 if (errmsg) *errmsg = _("missing { after %");
1924 /*@=observertrans =readonlytrans@*/
1925 format = freeFormat(format, numTokens);
1931 while (start < chptr) {
1932 if (xisdigit(*start)) {
1933 i = strtoul(start, &start, 10);
1934 format[currToken].u.tag.pad += i;
1940 if (*start == '=') {
1941 format[currToken].u.tag.justOne = 1;
1943 } else if (*start == '#') {
1944 format[currToken].u.tag.justOne = 1;
1945 format[currToken].u.tag.arrayCount = 1;
1950 while (*next && *next != '}') next++;
1952 /*@-observertrans -readonlytrans@*/
1953 if (errmsg) *errmsg = _("missing } after %{");
1954 /*@=observertrans =readonlytrans@*/
1955 format = freeFormat(format, numTokens);
1961 while (*chptr && *chptr != ':') chptr++;
1963 if (*chptr != '\0') {
1966 /*@-observertrans -readonlytrans@*/
1967 if (errmsg) *errmsg = _("empty tag format");
1968 /*@=observertrans =readonlytrans@*/
1969 format = freeFormat(format, numTokens);
1973 format[currToken].u.tag.type = chptr;
1976 format[currToken].u.tag.type = NULL;
1980 /*@-observertrans -readonlytrans@*/
1981 if (errmsg) *errmsg = _("empty tag name");
1982 /*@=observertrans =readonlytrans@*/
1983 format = freeFormat(format, numTokens);
1988 findTag(start, tags, extensions, &tag, &ext);
1991 format[currToken].u.tag.ext = NULL;
1992 format[currToken].u.tag.tag = tag->val;
1994 format[currToken].u.tag.ext = ext->u.tagFunction;
1995 format[currToken].u.tag.extNum = ext - extensions;
1997 /*@-observertrans -readonlytrans@*/
1998 if (errmsg) *errmsg = _("unknown tag");
1999 /*@=observertrans =readonlytrans@*/
2000 format = freeFormat(format, numTokens);
2004 format[currToken].type = PTOK_TAG;
2015 if (parseFormat(start, tags, extensions,
2016 &format[currToken].u.array.format,
2017 &format[currToken].u.array.numTokens,
2018 &start, PARSER_IN_ARRAY, errmsg)) {
2019 format = freeFormat(format, numTokens);
2024 /*@-observertrans -readonlytrans@*/
2025 if (errmsg) *errmsg = _("] expected at end of array");
2026 /*@=observertrans =readonlytrans@*/
2027 format = freeFormat(format, numTokens);
2033 format[currToken].type = PTOK_ARRAY;
2039 if ((*start == ']' && state != PARSER_IN_ARRAY) ||
2040 (*start == '}' && state != PARSER_IN_EXPR)) {
2041 if (*start == ']') {
2042 /*@-observertrans -readonlytrans@*/
2043 if (errmsg) *errmsg = _("unexpected ]");
2044 /*@=observertrans =readonlytrans@*/
2046 /*@-observertrans -readonlytrans@*/
2047 if (errmsg) *errmsg = _("unexpected }");
2048 /*@=observertrans =readonlytrans@*/
2050 format = freeFormat(format, numTokens);
2054 if (endPtr) *endPtr = start;
2059 if (currToken < 0 || format[currToken].type != PTOK_STRING) {
2061 format[currToken].type = PTOK_STRING;
2062 /*@-temptrans -assignexpose@*/
2063 dst = format[currToken].u.string.string = start;
2064 /*@=temptrans =assignexpose@*/
2067 if (*start == '\\') {
2069 *dst++ = escapedChar(*start++);
2083 for (i = 0; i < currToken; i++) {
2084 if (format[i].type == PTOK_STRING)
2085 format[i].u.string.len = strlen(format[i].u.string.string);
2088 *numTokensPtr = currToken;
2089 *formatPtr = format;
2096 static int parseExpression(sprintfToken token, char * str,
2097 const headerTagTableEntry tags,
2098 const headerSprintfExtension extensions,
2099 /*@out@*/ char ** endPtr,
2100 /*@null@*/ /*@out@*/ errmsg_t * errmsg)
2102 headerTagTableEntry tag;
2103 headerSprintfExtension ext;
2107 if (errmsg) *errmsg = NULL;
2109 while (*chptr && *chptr != '?') chptr++;
2111 if (*chptr != '?') {
2112 /*@-observertrans -readonlytrans@*/
2113 if (errmsg) *errmsg = _("? expected in expression");
2114 /*@=observertrans =readonlytrans@*/
2120 if (*chptr != '{') {
2121 /*@-observertrans -readonlytrans@*/
2122 if (errmsg) *errmsg = _("{ expected after ? in expression");
2123 /*@=observertrans =readonlytrans@*/
2129 if (parseFormat(chptr, tags, extensions, &token->u.cond.ifFormat,
2130 &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR, errmsg))
2134 /*@-observertrans -readonlytrans@*/
2135 if (errmsg) *errmsg = _("} expected in expression");
2136 /*@=observertrans =readonlytrans@*/
2137 token->u.cond.ifFormat =
2138 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2143 if (*chptr != ':' && *chptr != '|') {
2144 /*@-observertrans -readonlytrans@*/
2145 if (errmsg) *errmsg = _(": expected following ? subexpression");
2146 /*@=observertrans =readonlytrans@*/
2147 token->u.cond.ifFormat =
2148 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2152 if (*chptr == '|') {
2153 (void) parseFormat(xstrdup(""), tags, extensions,
2154 &token->u.cond.elseFormat,
2155 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR,
2160 if (*chptr != '{') {
2161 /*@-observertrans -readonlytrans@*/
2162 if (errmsg) *errmsg = _("{ expected after : in expression");
2163 /*@=observertrans =readonlytrans@*/
2164 token->u.cond.ifFormat =
2165 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2171 if (parseFormat(chptr, tags, extensions, &token->u.cond.elseFormat,
2172 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR,
2176 /*@-observertrans -readonlytrans@*/
2177 if (errmsg) *errmsg = _("} expected in expression");
2178 /*@=observertrans =readonlytrans@*/
2179 token->u.cond.ifFormat =
2180 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2185 if (*chptr != '|') {
2186 /*@-observertrans -readonlytrans@*/
2187 if (errmsg) *errmsg = _("| expected at end of expression");
2188 /*@=observertrans =readonlytrans@*/
2189 token->u.cond.ifFormat =
2190 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2191 token->u.cond.elseFormat =
2192 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
2201 findTag(str, tags, extensions, &tag, &ext);
2204 token->u.cond.tag.ext = NULL;
2205 token->u.cond.tag.tag = tag->val;
2207 token->u.cond.tag.ext = ext->u.tagFunction;
2208 token->u.cond.tag.extNum = ext - extensions;
2210 token->u.cond.tag.ext = NULL;
2211 token->u.cond.tag.tag = -1;
2214 token->type = PTOK_COND;
2220 * @return 0 on success, 1 on failure
2222 static int getExtension(Header h, headerTagTagFunction fn,
2223 /*@out@*/ hTYP_t typeptr,
2224 /*@out@*/ hPTR_t * data,
2225 /*@out@*/ hCNT_t countptr,
2227 /*@modifies *typeptr, *data, *countptr, ext @*/
2230 if (fn(h, &ext->type, &ext->data, &ext->count, &ext->freeit))
2235 if (typeptr) *typeptr = ext->type;
2236 if (data) *data = ext->data;
2237 if (countptr) *countptr = ext->count;
2244 static char * formatValue(sprintfTag tag, Header h,
2245 const headerSprintfExtension extensions,
2246 extensionCache extCache, int element)
2247 /*@modifies extCache @*/
2253 unsigned int intVal;
2255 const char ** strarray;
2258 headerTagFormatFunction tagtype = NULL;
2259 headerSprintfExtension ext;
2262 if (getExtension(h, tag->ext, &type, &data, &count,
2263 extCache + tag->extNum))
2266 type = RPM_STRING_TYPE;
2267 data = "(none)"; /* XXX i18n? NO!, sez; gafton */
2270 if (!headerGetEntry(h, tag->tag, &type, (void **)&data, &count)) {
2272 type = RPM_STRING_TYPE;
2273 data = "(none)"; /* XXX i18n? NO!, sez; gafton */
2279 if (tag->arrayCount) {
2280 /*@-observertrans -modobserver@*/
2281 data = headerFreeData(data, type);
2282 /*@=observertrans =modobserver@*/
2287 type = RPM_INT32_TYPE;
2290 (void) stpcpy( stpcpy(buf, "%"), tag->format);
2294 while (ext->type != HEADER_EXT_LAST) {
2295 if (ext->name != NULL && ext->type == HEADER_EXT_FORMAT
2296 && !strcmp(ext->name, tag->type))
2298 tagtype = ext->u.formatFunction;
2302 if (ext->type == HEADER_EXT_MORE)
2310 case RPM_STRING_ARRAY_TYPE:
2311 strarray = (const char **)data;
2314 val = tagtype(RPM_STRING_TYPE, strarray[element], buf, tag->pad, 0);
2319 len = strlen(strarray[element]) + tag->pad + 20;
2321 sprintf(val, buf, strarray[element]);
2324 /*@-observertrans -modobserver@*/
2325 if (mayfree) data = _free(data);
2326 /*@=observertrans =modobserver@*/
2330 case RPM_STRING_TYPE:
2332 val = tagtype(RPM_STRING_ARRAY_TYPE, data, buf, tag->pad, 0);
2337 len = strlen(data) + tag->pad + 20;
2339 sprintf(val, buf, data);
2345 case RPM_INT16_TYPE:
2346 case RPM_INT32_TYPE:
2349 case RPM_INT8_TYPE: intVal = *(((int_8 *) data) + element); break;
2350 case RPM_INT16_TYPE: intVal = *(((uint_16 *) data) + element); break;
2351 default: /* keep -Wall quiet */
2352 case RPM_INT32_TYPE: intVal = *(((int_32 *) data) + element); break;
2356 val = tagtype(RPM_INT32_TYPE, &intVal, buf, tag->pad, element);
2360 len = 10 + tag->pad + 20;
2362 sprintf(val, buf, intVal);
2367 val = xstrdup(_("(unknown type)"));
2376 static const char * singleSprintf(Header h, sprintfToken token,
2377 const headerSprintfExtension extensions,
2378 extensionCache extCache, int element)
2379 /*@modifies h, extCache @*/
2382 const char * thisItem;
2388 sprintfToken condFormat;
2391 /* we assume the token and header have been validated already! */
2393 switch (token->type) {
2398 val = xmalloc(token->u.string.len + 1);
2399 strcpy(val, token->u.string.string);
2403 val = formatValue(&token->u.tag, h, extensions, extCache,
2404 token->u.tag.justOne ? 0 : element);
2408 if (token->u.cond.tag.ext ||
2409 headerIsEntry(h, token->u.cond.tag.tag)) {
2410 condFormat = token->u.cond.ifFormat;
2411 condNumFormats = token->u.cond.numIfTokens;
2413 condFormat = token->u.cond.elseFormat;
2414 condNumFormats = token->u.cond.numElseTokens;
2417 alloced = condNumFormats * 20;
2418 val = xmalloc(alloced ? alloced : 1);
2423 for (i = 0; i < condNumFormats; i++) {
2424 thisItem = singleSprintf(h, condFormat + i,
2425 extensions, extCache, element);
2426 thisItemLen = strlen(thisItem);
2427 if ((thisItemLen + len) >= alloced) {
2428 alloced = (thisItemLen + len) + 200;
2429 val = xrealloc(val, alloced);
2431 strcat(val, thisItem);
2433 thisItem = _free(thisItem);
2440 for (i = 0; i < token->u.array.numTokens; i++) {
2441 if (token->u.array.format[i].type != PTOK_TAG ||
2442 token->u.array.format[i].u.tag.arrayCount ||
2443 token->u.array.format[i].u.tag.justOne) continue;
2445 if (token->u.array.format[i].u.tag.ext) {
2447 if (getExtension(h, token->u.array.format[i].u.tag.ext,
2448 &type, &data, &numElements,
2450 token->u.array.format[i].u.tag.extNum))
2453 if (!headerGetEntry(h, token->u.array.format[i].u.tag.tag,
2454 &type, (void **) &val, &numElements))
2456 val = headerFreeData(val, type);
2458 /*@loopbreak@*/ break;
2461 if (numElements == -1) {
2462 val = xstrdup("(none)"); /* XXX i18n? NO!, sez; gafton */
2464 alloced = numElements * token->u.array.numTokens * 20;
2465 val = xmalloc(alloced);
2469 for (j = 0; j < numElements; j++) {
2470 for (i = 0; i < token->u.array.numTokens; i++) {
2471 thisItem = singleSprintf(h, token->u.array.format + i,
2472 extensions, extCache, j);
2473 thisItemLen = strlen(thisItem);
2474 if ((thisItemLen + len) >= alloced) {
2475 alloced = (thisItemLen + len) + 200;
2476 val = xrealloc(val, alloced);
2478 strcat(val, thisItem);
2480 thisItem = _free(thisItem);
2493 static /*@only@*/ extensionCache
2494 allocateExtensionCache(const headerSprintfExtension extensions)
2497 headerSprintfExtension ext = extensions;
2500 while (ext->type != HEADER_EXT_LAST) {
2502 if (ext->type == HEADER_EXT_MORE)
2508 return xcalloc(i, sizeof(struct extensionCache));
2512 * @return NULL always
2514 static /*@null@*/ extensionCache
2515 freeExtensionCache(const headerSprintfExtension extensions,
2516 /*@only@*/ extensionCache cache)
2519 headerSprintfExtension ext = extensions;
2522 while (ext->type != HEADER_EXT_LAST) {
2523 if (cache[i].freeit) cache[i].data = _free(cache[i].data);
2526 if (ext->type == HEADER_EXT_MORE)
2532 cache = _free(cache);
2536 char * headerSprintf(Header h, const char * fmt,
2537 const struct headerTagTableEntry_s * tabletags,
2538 const struct headerSprintfExtension_s * extensions,
2541 /*@-castexpose@*/ /* FIX: legacy API shouldn't change. */
2542 headerSprintfExtension exts = (headerSprintfExtension) extensions;
2543 headerTagTableEntry tags = (headerTagTableEntry) tabletags;
2546 sprintfToken format;
2552 extensionCache extCache;
2554 /*fmtString = escapeString(fmt);*/
2555 fmtString = xstrdup(fmt);
2557 if (parseFormat(fmtString, tags, exts, &format, &numTokens,
2558 NULL, PARSER_BEGIN, errmsg)) {
2559 fmtString = _free(fmtString);
2563 extCache = allocateExtensionCache(exts);
2565 answerAlloced = 1024;
2567 answer = xmalloc(answerAlloced);
2570 for (i = 0; i < numTokens; i++) {
2575 piece = singleSprintf(h, format + i, exts, extCache, 0);
2578 pieceLength = strlen(piece);
2579 if ((answerLength + pieceLength) >= answerAlloced) {
2580 while ((answerLength + pieceLength) >= answerAlloced)
2581 answerAlloced += 1024;
2582 answer = xrealloc(answer, answerAlloced);
2585 strcat(answer, piece);
2586 answerLength += pieceLength;
2587 piece = _free(piece);
2591 fmtString = _free(fmtString);
2592 extCache = freeExtensionCache(exts, extCache);
2593 format = _free(format);
2600 static char * octalFormat(int_32 type, hPTR_t data,
2601 char * formatPrefix, int padding, /*@unused@*/int element)
2602 /*@modifies formatPrefix @*/
2606 if (type != RPM_INT32_TYPE) {
2607 val = xstrdup(_("(not a number)"));
2609 val = xmalloc(20 + padding);
2610 strcat(formatPrefix, "o");
2611 sprintf(val, formatPrefix, *((int_32 *) data));
2619 static char * hexFormat(int_32 type, hPTR_t data,
2620 char * formatPrefix, int padding, /*@unused@*/int element)
2621 /*@modifies formatPrefix @*/
2625 if (type != RPM_INT32_TYPE) {
2626 val = xstrdup(_("(not a number)"));
2628 val = xmalloc(20 + padding);
2629 strcat(formatPrefix, "x");
2630 sprintf(val, formatPrefix, *((int_32 *) data));
2638 static char * realDateFormat(int_32 type, hPTR_t data,
2639 char * formatPrefix, int padding, /*@unused@*/int element,
2640 const char * strftimeFormat)
2641 /*@modifies formatPrefix @*/
2645 if (type != RPM_INT32_TYPE) {
2646 val = xstrdup(_("(not a number)"));
2648 struct tm * tstruct;
2651 val = xmalloc(50 + padding);
2652 strcat(formatPrefix, "s");
2654 /* this is important if sizeof(int_32) ! sizeof(time_t) */
2655 { time_t dateint = *((int_32 *) data);
2656 tstruct = localtime(&dateint);
2660 (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
2661 sprintf(val, formatPrefix, buf);
2669 static char * dateFormat(int_32 type, hPTR_t data,
2670 char * formatPrefix, int padding, int element)
2671 /*@modifies formatPrefix @*/
2673 return realDateFormat(type, data, formatPrefix, padding, element, "%c");
2678 static char * dayFormat(int_32 type, hPTR_t data,
2679 char * formatPrefix, int padding, int element)
2680 /*@modifies formatPrefix @*/
2682 return realDateFormat(type, data, formatPrefix, padding, element,
2688 static char * shescapeFormat(int_32 type, hPTR_t data,
2689 char * formatPrefix, int padding, /*@unused@*/int element)
2690 /*@modifies formatPrefix @*/
2692 char * result, * dst, * src, * buf;
2694 if (type == RPM_INT32_TYPE) {
2695 result = xmalloc(padding + 20);
2696 strcat(formatPrefix, "d");
2697 sprintf(result, formatPrefix, *((int_32 *) data));
2699 buf = alloca(strlen(data) + padding + 2);
2700 strcat(formatPrefix, "s");
2701 sprintf(buf, formatPrefix, data);
2703 result = dst = xmalloc(strlen(buf) * 4 + 3);
2705 for (src = buf; *src != '\0'; src++) {
2723 const struct headerSprintfExtension_s headerDefaultFormats[] = {
2724 { HEADER_EXT_FORMAT, "octal", { octalFormat } },
2725 { HEADER_EXT_FORMAT, "hex", { hexFormat } },
2726 { HEADER_EXT_FORMAT, "date", { dateFormat } },
2727 { HEADER_EXT_FORMAT, "day", { dayFormat } },
2728 { HEADER_EXT_FORMAT, "shescape", { shescapeFormat } },
2729 { HEADER_EXT_LAST, NULL, { NULL } }
2732 void headerCopyTags(Header headerFrom, Header headerTo, hTAG_t tagstocopy)
2736 if (headerFrom == headerTo)
2739 for (p = tagstocopy; *p != 0; p++) {
2743 if (headerIsEntry(headerTo, *p))
2745 if (!headerGetEntryMinMemory(headerFrom, *p, &type,
2746 (hPTR_t *) &s, &count))
2748 (void) headerAddEntry(headerTo, *p, type, s, count);
2749 s = headerFreeData(s, type);
2754 * Header tag iterator data structure.
2756 struct headerIteratorS {
2757 /*@unused@*/ Header h; /*!< Header being iterated. */
2758 /*@unused@*/ int next_index; /*!< Next tag index. */
2761 HeaderIterator headerFreeIterator(HeaderIterator hi)
2763 hi->h = headerFree(hi->h);
2768 HeaderIterator headerInitIterator(Header h)
2770 HeaderIterator hi = xmalloc(sizeof(struct headerIteratorS));
2774 hi->h = headerLink(h);
2779 int headerNextIterator(HeaderIterator hi,
2780 hTAG_t tag, hTYP_t type, hPTR_t * p, hCNT_t c)
2783 int slot = hi->next_index;
2784 indexEntry entry = NULL;
2787 for (slot = hi->next_index; slot < h->indexUsed; slot++) {
2788 entry = h->index + slot;
2789 if (!ENTRY_IS_REGION(entry))
2792 hi->next_index = slot;
2793 if (entry == NULL || slot >= h->indexUsed)
2795 /*@-noeffect@*/ /* LCL: no clue */
2800 *tag = entry->info.tag;
2802 rc = copyEntry(entry, type, p, c, 0);
2804 /* XXX 1 on success */
2805 return ((rc == 1) ? 1 : 0);
2807 static struct HV_s hdrVec1 = {
2824 headerGetEntryMinMemory,
2827 headerAddOrAppendEntry,
2828 headerAddI18NString,
2840 /*@-compmempass -redef@*/
2841 HV_t hdrVec = &hdrVec1;
2842 /*@=compmempass =redef@*/