Main Page   Modules   Data Structures   File List   Data Fields   Globals   Related Pages  

lib/header.c

Go to the documentation of this file.
00001 
00005 /* RPM - Copyright (C) 1995-2000 Red Hat Software */
00006 
00007 /* Data written to file descriptors is in network byte order.    */
00008 /* Data read from file descriptors is expected to be in          */
00009 /* network byte order and is converted on the fly to host order. */
00010 
00011 #include "system.h"
00012 #include <langinfo.h>
00013 #include <iconv.h>
00014 
00015 #define __HEADER_PROTOTYPES__
00016 
00017 #include <header_internal.h>
00018 
00019 /* We get parseEVR() from there used in parsing the >-test in conditionals: */
00020 #include <rpmlib.h>
00021 
00022 #include "debug.h"
00023 
00024 /*@-redecl@*/   /* FIX: avoid rpmlib.h, need for debugging. */
00025 /*@observer@*/ const char *const tagName(int tag)       /*@*/;
00026 /*@=redecl@*/
00027 
00028 /*@access entryInfo @*/
00029 /*@access indexEntry @*/
00030 
00031 /*@access extensionCache @*/
00032 /*@access sprintfTag @*/
00033 /*@access sprintfToken @*/
00034 /*@access HV_t @*/
00035 
00036 #define PARSER_BEGIN    0
00037 #define PARSER_IN_ARRAY 1
00038 #define PARSER_IN_EXPR  2
00039 
00042 /*@observer@*/ /*@unchecked@*/
00043 static unsigned char header_magic[8] = {
00044         0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
00045 };
00046 
00050 /*@unchecked@*/
00051 static size_t headerMaxbytes = (32*1024*1024);
00052 
00057 #define hdrchkTags(_ntags)      ((_ntags) & 0xffff0000)
00058 
00063 #define hdrchkData(_nbytes)     ((_nbytes) & 0xff000000)
00064 
00068 /*@observer@*/ /*@unchecked@*/
00069 static int typeSizes[] =  { 
00070         0,      
00071         1,      
00072         1,      
00073         2,      
00074         4,      
00075         -1,     
00076         -1,     
00077         1,      
00078         -1,     
00079         -1      
00080 };
00081 
00082 /*@observer@*/ /*@unchecked@*/
00083 HV_t hdrVec;    /* forward reference */
00084 
00085 /* Already defined in <rpmlib.h> */
00086 #if 0
00087 
00092 /*@unused@*/ static inline /*@null@*/ void *
00093 _free(/*@only@*/ /*@null@*/ /*@out@*/ const void * p) /*@modifies *p @*/
00094 {
00095     if (p != NULL)      free((void *)p);
00096     return NULL;
00097 }
00098 #endif
00099 
00105 static
00106 Header headerLink(Header h)
00107         /*@modifies h @*/
00108 {
00109     if (h != NULL) h->nrefs++;
00110     /*@-refcounttrans -nullret @*/
00111     return h;
00112     /*@=refcounttrans =nullret @*/
00113 }
00114 
00120 static /*@null@*/
00121 Header headerUnlink(/*@killref@*/ /*@null@*/ Header h)
00122         /*@modifies h @*/
00123 {
00124     if (h != NULL) h->nrefs--;
00125     return NULL;
00126 }
00127 
00133 static /*@null@*/
00134 Header headerFree(/*@killref@*/ /*@null@*/ Header h)
00135         /*@modifies h @*/
00136 {
00137     (void) headerUnlink(h);
00138 
00139     /*@-usereleased@*/
00140     if (h == NULL || h->nrefs > 0)
00141         return NULL;    /* XXX return previous header? */
00142 
00143     if (h->index) {
00144         indexEntry entry = h->index;
00145         int i;
00146         for (i = 0; i < h->indexUsed; i++, entry++) {
00147             if ((h->flags & HEADERFLAG_ALLOCATED) && ENTRY_IS_REGION(entry)) {
00148                 if (entry->length > 0) {
00149                     int_32 * ei = entry->data;
00150                     if ((ei - 2) == h->blob) h->blob = _free(h->blob);
00151                     entry->data = NULL;
00152                 }
00153             } else if (!ENTRY_IN_REGION(entry)) {
00154                 entry->data = _free(entry->data);
00155             }
00156             entry->data = NULL;
00157         }
00158         h->index = _free(h->index);
00159     }
00160 
00161     /*@-refcounttrans@*/ h = _free(h); /*@=refcounttrans@*/
00162     return h;
00163     /*@=usereleased@*/
00164 }
00165 
00170 static
00171 Header headerNew(void)
00172         /*@*/
00173 {
00174     Header h = xcalloc(1, sizeof(*h));
00175 
00176     /*@-assignexpose@*/
00177     h->hv = *hdrVec;            /* structure assignment */
00178     /*@=assignexpose@*/
00179     h->blob = NULL;
00180     h->indexAlloced = INDEX_MALLOC_SIZE;
00181     h->indexUsed = 0;
00182     h->flags = HEADERFLAG_SORTED;
00183 
00184     h->index = (h->indexAlloced
00185         ? xcalloc(h->indexAlloced, sizeof(*h->index))
00186         : NULL);
00187 
00188     /*@-globstate -observertrans @*/
00189     h->nrefs = 0;
00190     return headerLink(h);
00191     /*@=globstate =observertrans @*/
00192 }
00193 
00196 static int indexCmp(const void * avp, const void * bvp) /*@*/
00197 {
00198     /*@-castexpose@*/
00199     indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
00200     /*@=castexpose@*/
00201     return (ap->info.tag - bp->info.tag);
00202 }
00203 
00208 static
00209 void headerSort(Header h)
00210         /*@modifies h @*/
00211 {
00212     if (!(h->flags & HEADERFLAG_SORTED)) {
00213         qsort(h->index, h->indexUsed, sizeof(*h->index), indexCmp);
00214         h->flags |= HEADERFLAG_SORTED;
00215     }
00216 }
00217 
00220 static int offsetCmp(const void * avp, const void * bvp) /*@*/
00221 {
00222     /*@-castexpose@*/
00223     indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
00224     /*@=castexpose@*/
00225     int rc = (ap->info.offset - bp->info.offset);
00226 
00227     if (rc == 0) {
00228         /* Within a region, entries sort by address. Added drips sort by tag. */
00229         if (ap->info.offset < 0)
00230             rc = (((char *)ap->data) - ((char *)bp->data));
00231         else
00232             rc = (ap->info.tag - bp->info.tag);
00233     }
00234     return rc;
00235 }
00236 
00241 static
00242 void headerUnsort(Header h)
00243         /*@modifies h @*/
00244 {
00245     qsort(h->index, h->indexUsed, sizeof(*h->index), offsetCmp);
00246 }
00247 
00254 static
00255 unsigned int headerSizeof(/*@null@*/ Header h, enum hMagic magicp)
00256         /*@modifies h @*/
00257 {
00258     indexEntry entry;
00259     unsigned int size = 0;
00260     unsigned int pad = 0;
00261     int i;
00262 
00263     if (h == NULL)
00264         return size;
00265 
00266     headerSort(h);
00267 
00268     switch (magicp) {
00269     case HEADER_MAGIC_YES:
00270         size += sizeof(header_magic);
00271         break;
00272     case HEADER_MAGIC_NO:
00273         break;
00274     }
00275 
00276     /*@-sizeoftype@*/
00277     size += 2 * sizeof(int_32); /* count of index entries */
00278     /*@=sizeoftype@*/
00279 
00280     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00281         unsigned diff;
00282         int_32 type;
00283 
00284         /* Regions go in as is ... */
00285         if (ENTRY_IS_REGION(entry)) {
00286             size += entry->length;
00287             /* XXX Legacy regions do not include the region tag and data. */
00288             /*@-sizeoftype@*/
00289             if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
00290                 size += sizeof(struct entryInfo) + entry->info.count;
00291             /*@=sizeoftype@*/
00292             continue;
00293         }
00294 
00295         /* ... and region elements are skipped. */
00296         if (entry->info.offset < 0)
00297             continue;
00298 
00299         /* Alignment */
00300         type = entry->info.type;
00301         if (typeSizes[type] > 1) {
00302             diff = typeSizes[type] - (size % typeSizes[type]);
00303             if (diff != typeSizes[type]) {
00304                 size += diff;
00305                 pad += diff;
00306             }
00307         }
00308 
00309         /*@-sizeoftype@*/
00310         size += sizeof(struct entryInfo) + entry->length;
00311         /*@=sizeoftype@*/
00312     }
00313 
00314     return size;
00315 }
00316 
00326 /*@mayexit@*/
00327 static int dataLength(int_32 type, hPTR_t p, int_32 count, int onDisk)
00328         /*@*/
00329 {
00330     int length = 0;
00331 
00332     switch (type) {
00333     case RPM_STRING_TYPE:
00334         if (count == 1) {       /* Special case -- p is just the string */
00335             length = strlen(p) + 1;
00336             break;
00337         }
00338         /* This should not be allowed */
00339         /*@-modfilesys@*/
00340         fprintf(stderr, _("dataLength() RPM_STRING_TYPE count must be 1.\n"));
00341         /*@=modfilesys@*/
00342         exit(EXIT_FAILURE);
00343         /*@notreached@*/ break;
00344 
00345     case RPM_STRING_ARRAY_TYPE:
00346     case RPM_I18NSTRING_TYPE:
00347     {   int i;
00348 
00349         /* This is like RPM_STRING_TYPE, except it's *always* an array */
00350         /* Compute sum of length of all strings, including null terminators */
00351         i = count;
00352 
00353         if (onDisk) {
00354             const char * chptr = p;
00355             int thisLen;
00356 
00357             while (i--) {
00358                 thisLen = strlen(chptr) + 1;
00359                 length += thisLen;
00360                 chptr += thisLen;
00361             }
00362         } else {
00363             const char ** src = (const char **)p;
00364             while (i--) {
00365                 /* add one for null termination */
00366                 length += strlen(*src++) + 1;
00367             }
00368         }
00369     }   break;
00370 
00371     default:
00372         if (typeSizes[type] != -1) {
00373             length = typeSizes[type] * count;
00374             break;
00375         }
00376         /*@-modfilesys@*/
00377         fprintf(stderr, _("Data type %d not supported\n"), (int) type);
00378         /*@=modfilesys@*/
00379         exit(EXIT_FAILURE);
00380         /*@notreached@*/ break;
00381     }
00382 
00383     return length;
00384 }
00385 
00411 static int regionSwab(/*@null@*/ indexEntry entry, int il, int dl,
00412                 entryInfo pe, char * dataStart, int regionid)
00413         /*@modifies *entry, *dataStart @*/
00414 {
00415     char * tprev = NULL;
00416     char * t = NULL;
00417     int tdel, tl = dl;
00418     struct indexEntry ieprev;
00419 
00420     memset(&ieprev, 0, sizeof(ieprev));
00421     for (; il > 0; il--, pe++) {
00422         struct indexEntry ie;
00423         int_32 type;
00424 
00425         ie.info.tag = ntohl(pe->tag);
00426         ie.info.type = ntohl(pe->type);
00427         if (ie.info.type < RPM_MIN_TYPE || ie.info.type > RPM_MAX_TYPE)
00428             return -1;
00429         ie.info.count = ntohl(pe->count);
00430         ie.info.offset = ntohl(pe->offset);
00431         ie.data = t = dataStart + ie.info.offset;
00432         ie.length = dataLength(ie.info.type, ie.data, ie.info.count, 1);
00433         ie.rdlen = 0;
00434 
00435         if (entry) {
00436             ie.info.offset = regionid;
00437             *entry = ie;        /* structure assignment */
00438             entry++;
00439         }
00440 
00441         /* Alignment */
00442         type = ie.info.type;
00443         if (typeSizes[type] > 1) {
00444             unsigned diff;
00445             diff = typeSizes[type] - (dl % typeSizes[type]);
00446             if (diff != typeSizes[type]) {
00447                 dl += diff;
00448                 if (ieprev.info.type == RPM_I18NSTRING_TYPE)
00449                     ieprev.length += diff;
00450             }
00451         }
00452         tdel = (tprev ? (t - tprev) : 0);
00453         if (ieprev.info.type == RPM_I18NSTRING_TYPE)
00454             tdel = ieprev.length;
00455 
00456         if (ie.info.tag >= HEADER_I18NTABLE) {
00457             tprev = t;
00458         } else {
00459             tprev = dataStart;
00460             /* XXX HEADER_IMAGE tags don't include region sub-tag. */
00461             /*@-sizeoftype@*/
00462             if (ie.info.tag == HEADER_IMAGE)
00463                 tprev -= REGION_TAG_COUNT;
00464             /*@=sizeoftype@*/
00465         }
00466 
00467         /* Perform endian conversions */
00468         switch (ntohl(pe->type)) {
00469         case RPM_INT32_TYPE:
00470         {   int_32 * it = (int_32 *)t;
00471             for (; ie.info.count > 0; ie.info.count--, it += 1)
00472                 *it = htonl(*it);
00473             t = (char *) it;
00474         }   /*@switchbreak@*/ break;
00475         case RPM_INT16_TYPE:
00476         {   int_16 * it = (int_16 *) t;
00477             for (; ie.info.count > 0; ie.info.count--, it += 1)
00478                 *it = htons(*it);
00479             t = (char *) it;
00480         }   /*@switchbreak@*/ break;
00481         default:
00482             t += ie.length;
00483             /*@switchbreak@*/ break;
00484         }
00485 
00486         dl += ie.length;
00487         tl += tdel;
00488         ieprev = ie;    /* structure assignment */
00489 
00490     }
00491     tdel = (tprev ? (t - tprev) : 0);
00492     tl += tdel;
00493 
00494     /* XXX
00495      * There are two hacks here:
00496      *  1) tl is 16b (i.e. REGION_TAG_COUNT) short while doing headerReload().
00497      *  2) the 8/98 rpm bug with inserting i18n tags needs to use tl, not dl.
00498      */
00499     /*@-sizeoftype@*/
00500     if (tl+REGION_TAG_COUNT == dl)
00501         tl += REGION_TAG_COUNT;
00502     /*@=sizeoftype@*/
00503 
00504     return dl;
00505 }
00506 
00509 static /*@only@*/ /*@null@*/ void * doHeaderUnload(Header h,
00510                 /*@out@*/ int * lengthPtr)
00511         /*@modifies h, *lengthPtr @*/
00512 {
00513     int_32 * ei = NULL;
00514     entryInfo pe;
00515     char * dataStart;
00516     char * te;
00517     unsigned pad;
00518     unsigned len;
00519     int_32 il = 0;
00520     int_32 dl = 0;
00521     indexEntry entry; 
00522     int_32 type;
00523     int i;
00524     int drlen, ndribbles;
00525     int driplen, ndrips;
00526     int legacy = 0;
00527 
00528     /* Sort entries by (offset,tag). */
00529     headerUnsort(h);
00530 
00531     /* Compute (il,dl) for all tags, including those deleted in region. */
00532     pad = 0;
00533     drlen = ndribbles = driplen = ndrips = 0;
00534     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00535         if (ENTRY_IS_REGION(entry)) {
00536             int_32 rdl = -entry->info.offset;   /* negative offset */
00537             int_32 ril = rdl/sizeof(*pe);
00538             int rid = entry->info.offset;
00539 
00540             il += ril;
00541             dl += entry->rdlen + entry->info.count;
00542             /* XXX Legacy regions do not include the region tag and data. */
00543             if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
00544                 il += 1;
00545 
00546             /* Skip rest of entries in region, but account for dribbles. */
00547             for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
00548                 if (entry->info.offset <= rid)
00549                     /*@innercontinue@*/ continue;
00550 
00551                 /* Alignment */
00552                 type = entry->info.type;
00553                 if (typeSizes[type] > 1) {
00554                     unsigned diff;
00555                     diff = typeSizes[type] - (dl % typeSizes[type]);
00556                     if (diff != typeSizes[type]) {
00557                         drlen += diff;
00558                         pad += diff;
00559                         dl += diff;
00560                     }
00561                 }
00562 
00563                 ndribbles++;
00564                 il++;
00565                 drlen += entry->length;
00566                 dl += entry->length;
00567             }
00568             i--;
00569             entry--;
00570             continue;
00571         }
00572 
00573         /* Ignore deleted drips. */
00574         if (entry->data == NULL || entry->length <= 0)
00575             continue;
00576 
00577         /* Alignment */
00578         type = entry->info.type;
00579         if (typeSizes[type] > 1) {
00580             unsigned diff;
00581             diff = typeSizes[type] - (dl % typeSizes[type]);
00582             if (diff != typeSizes[type]) {
00583                 driplen += diff;
00584                 pad += diff;
00585                 dl += diff;
00586             } else
00587                 diff = 0;
00588         }
00589 
00590         ndrips++;
00591         il++;
00592         driplen += entry->length;
00593         dl += entry->length;
00594     }
00595 
00596     /* Sanity checks on header intro. */
00597     if (hdrchkTags(il) || hdrchkData(dl))
00598         goto errxit;
00599 
00600     len = sizeof(il) + sizeof(dl) + (il * sizeof(*pe)) + dl;
00601 
00602     ei = xmalloc(len);
00603     ei[0] = htonl(il);
00604     ei[1] = htonl(dl);
00605 
00606     pe = (entryInfo) &ei[2];
00607     dataStart = te = (char *) (pe + il);
00608 
00609     pad = 0;
00610     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00611         const char * src;
00612 char *t;
00613         int count;
00614         int rdlen;
00615 
00616         if (entry->data == NULL || entry->length <= 0)
00617             continue;
00618 
00619 t = te;
00620         pe->tag = htonl(entry->info.tag);
00621         pe->type = htonl(entry->info.type);
00622         pe->count = htonl(entry->info.count);
00623 
00624         if (ENTRY_IS_REGION(entry)) {
00625             int_32 rdl = -entry->info.offset;   /* negative offset */
00626             int_32 ril = rdl/sizeof(*pe) + ndribbles;
00627             int rid = entry->info.offset;
00628 
00629             src = (char *)entry->data;
00630             rdlen = entry->rdlen;
00631 
00632             /* XXX Legacy regions do not include the region tag and data. */
00633             if (i == 0 && (h->flags & HEADERFLAG_LEGACY)) {
00634                 int_32 stei[4];
00635 
00636                 legacy = 1;
00637                 memcpy(pe+1, src, rdl);
00638                 memcpy(te, src + rdl, rdlen);
00639                 te += rdlen;
00640 
00641                 pe->offset = htonl(te - dataStart);
00642                 stei[0] = pe->tag;
00643                 stei[1] = pe->type;
00644                 stei[2] = htonl(-rdl-entry->info.count);
00645                 stei[3] = pe->count;
00646                 memcpy(te, stei, entry->info.count);
00647                 te += entry->info.count;
00648                 ril++;
00649                 rdlen += entry->info.count;
00650 
00651                 count = regionSwab(NULL, ril, 0, pe, t, 0);
00652                 if (count != rdlen)
00653                     goto errxit;
00654 
00655             } else {
00656 
00657                 memcpy(pe+1, src + sizeof(*pe), ((ril-1) * sizeof(*pe)));
00658                 memcpy(te, src + (ril * sizeof(*pe)), rdlen+entry->info.count+drlen);
00659                 te += rdlen;
00660                 {   /*@-castexpose@*/
00661                     entryInfo se = (entryInfo)src;
00662                     /*@=castexpose@*/
00663                     int off = ntohl(se->offset);
00664                     pe->offset = (off) ? htonl(te - dataStart) : htonl(off);
00665                 }
00666                 te += entry->info.count + drlen;
00667 
00668                 count = regionSwab(NULL, ril, 0, pe, t, 0);
00669                 if (count != (rdlen + entry->info.count + drlen))
00670                     goto errxit;
00671             }
00672 
00673             /* Skip rest of entries in region. */
00674             while (i < h->indexUsed && entry->info.offset <= rid+1) {
00675                 i++;
00676                 entry++;
00677             }
00678             i--;
00679             entry--;
00680             pe += ril;
00681             continue;
00682         }
00683 
00684         /* Ignore deleted drips. */
00685         if (entry->data == NULL || entry->length <= 0)
00686             continue;
00687 
00688         /* Alignment */
00689         type = entry->info.type;
00690         if (typeSizes[type] > 1) {
00691             unsigned diff;
00692             diff = typeSizes[type] - ((te - dataStart) % typeSizes[type]);
00693             if (diff != typeSizes[type]) {
00694                 memset(te, 0, diff);
00695                 te += diff;
00696                 pad += diff;
00697             }
00698         }
00699 
00700         pe->offset = htonl(te - dataStart);
00701 
00702         /* copy data w/ endian conversions */
00703         switch (entry->info.type) {
00704         case RPM_INT32_TYPE:
00705             count = entry->info.count;
00706             src = entry->data;
00707             while (count--) {
00708                 *((int_32 *)te) = htonl(*((int_32 *)src));
00709                 /*@-sizeoftype@*/
00710                 te += sizeof(int_32);
00711                 src += sizeof(int_32);
00712                 /*@=sizeoftype@*/
00713             }
00714             /*@switchbreak@*/ break;
00715 
00716         case RPM_INT16_TYPE:
00717             count = entry->info.count;
00718             src = entry->data;
00719             while (count--) {
00720                 *((int_16 *)te) = htons(*((int_16 *)src));
00721                 /*@-sizeoftype@*/
00722                 te += sizeof(int_16);
00723                 src += sizeof(int_16);
00724                 /*@=sizeoftype@*/
00725             }
00726             /*@switchbreak@*/ break;
00727 
00728         default:
00729             memcpy(te, entry->data, entry->length);
00730             te += entry->length;
00731             /*@switchbreak@*/ break;
00732         }
00733         pe++;
00734     }
00735    
00736     /* Insure that there are no memcpy underruns/overruns. */
00737     if (((char *)pe) != dataStart)
00738         goto errxit;
00739     if ((((char *)ei)+len) != te)
00740         goto errxit;
00741 
00742     if (lengthPtr)
00743         *lengthPtr = len;
00744 
00745     h->flags &= ~HEADERFLAG_SORTED;
00746     headerSort(h);
00747 
00748     return (void *) ei;
00749 
00750 errxit:
00751     /*@-usereleased@*/
00752     ei = _free(ei);
00753     /*@=usereleased@*/
00754     return (void *) ei;
00755 }
00756 
00762 static /*@only@*/ /*@null@*/
00763 void * headerUnload(Header h)
00764         /*@modifies h @*/
00765 {
00766     int length;
00767     void * uh = doHeaderUnload(h, &length);
00768     return uh;
00769 }
00770 
00778 static /*@null@*/
00779 indexEntry findEntry(/*@null@*/ Header h, int_32 tag, int_32 type)
00780         /*@modifies h @*/
00781 {
00782     indexEntry entry, entry2, last;
00783     struct indexEntry key;
00784 
00785     if (h == NULL) return NULL;
00786     if (!(h->flags & HEADERFLAG_SORTED)) headerSort(h);
00787 
00788     key.info.tag = tag;
00789 
00790     entry2 = entry = 
00791         bsearch(&key, h->index, h->indexUsed, sizeof(*h->index), indexCmp);
00792     if (entry == NULL)
00793         return NULL;
00794 
00795     if (type == RPM_NULL_TYPE)
00796         return entry;
00797 
00798     /* look backwards */
00799     while (entry->info.tag == tag && entry->info.type != type &&
00800            entry > h->index) entry--;
00801 
00802     if (entry->info.tag == tag && entry->info.type == type)
00803         return entry;
00804 
00805     last = h->index + h->indexUsed;
00806     /*@-usereleased@*/ /* FIX: entry2 = entry. Code looks bogus as well. */
00807     while (entry2->info.tag == tag && entry2->info.type != type &&
00808            entry2 < last) entry2++;
00809     /*@=usereleased@*/
00810 
00811     if (entry->info.tag == tag && entry->info.type == type)
00812         return entry;
00813 
00814     return NULL;
00815 }
00816 
00826 static
00827 int headerRemoveEntry(Header h, int_32 tag)
00828         /*@modifies h @*/
00829 {
00830     indexEntry last = h->index + h->indexUsed;
00831     indexEntry entry, first;
00832     int ne;
00833 
00834     entry = findEntry(h, tag, RPM_NULL_TYPE);
00835     if (!entry) return 1;
00836 
00837     /* Make sure entry points to the first occurence of this tag. */
00838     while (entry > h->index && (entry - 1)->info.tag == tag)  
00839         entry--;
00840 
00841     /* Free data for tags being removed. */
00842     for (first = entry; first < last; first++) {
00843         void * data;
00844         if (first->info.tag != tag)
00845             break;
00846         data = first->data;
00847         first->data = NULL;
00848         first->length = 0;
00849         if (ENTRY_IN_REGION(first))
00850             continue;
00851         data = _free(data);
00852     }
00853 
00854     ne = (first - entry);
00855     if (ne > 0) {
00856         h->indexUsed -= ne;
00857         ne = last - first;
00858         if (ne > 0)
00859             memmove(entry, first, (ne * sizeof(*entry)));
00860     }
00861 
00862     return 0;
00863 }
00864 
00870 static /*@null@*/
00871 Header headerLoad(/*@kept@*/ void * uh)
00872         /*@modifies uh @*/
00873 {
00874     int_32 * ei = (int_32 *) uh;
00875     int_32 il = ntohl(ei[0]);           /* index length */
00876     int_32 dl = ntohl(ei[1]);           /* data length */
00877     /*@-sizeoftype@*/
00878     size_t pvlen = sizeof(il) + sizeof(dl) +
00879                (il * sizeof(struct entryInfo)) + dl;
00880     /*@=sizeoftype@*/
00881     void * pv = uh;
00882     Header h = NULL;
00883     entryInfo pe;
00884     char * dataStart;
00885     indexEntry entry; 
00886     int rdlen;
00887     int i;
00888 
00889     /* Sanity checks on header intro. */
00890     if (hdrchkTags(il) || hdrchkData(dl))
00891         goto errxit;
00892 
00893     ei = (int_32 *) pv;
00894     /*@-castexpose@*/
00895     pe = (entryInfo) &ei[2];
00896     /*@=castexpose@*/
00897     dataStart = (char *) (pe + il);
00898 
00899     h = xcalloc(1, sizeof(*h));
00900     /*@-assignexpose@*/
00901     h->hv = *hdrVec;            /* structure assignment */
00902     /*@=assignexpose@*/
00903     /*@-assignexpose -kepttrans@*/
00904     h->blob = uh;
00905     /*@=assignexpose =kepttrans@*/
00906     h->indexAlloced = il + 1;
00907     h->indexUsed = il;
00908     h->index = xcalloc(h->indexAlloced, sizeof(*h->index));
00909     h->flags = HEADERFLAG_SORTED;
00910     h->nrefs = 0;
00911     h = headerLink(h);
00912 
00913     /*
00914      * XXX XFree86-libs, ash, and pdksh from Red Hat 5.2 have bogus
00915      * %verifyscript tag that needs to be diddled.
00916      */
00917     if (ntohl(pe->tag) == 15 &&
00918         ntohl(pe->type) == RPM_STRING_TYPE &&
00919         ntohl(pe->count) == 1)
00920     {
00921         pe->tag = htonl(1079);
00922     }
00923 
00924     entry = h->index;
00925     i = 0;
00926     if (!(htonl(pe->tag) < HEADER_I18NTABLE)) {
00927         h->flags |= HEADERFLAG_LEGACY;
00928         entry->info.type = REGION_TAG_TYPE;
00929         entry->info.tag = HEADER_IMAGE;
00930         /*@-sizeoftype@*/
00931         entry->info.count = REGION_TAG_COUNT;
00932         /*@=sizeoftype@*/
00933         entry->info.offset = ((char *)pe - dataStart); /* negative offset */
00934 
00935         /*@-assignexpose@*/
00936         entry->data = pe;
00937         /*@=assignexpose@*/
00938         entry->length = pvlen - sizeof(il) - sizeof(dl);
00939         rdlen = regionSwab(entry+1, il, 0, pe, dataStart, entry->info.offset);
00940 #if 0   /* XXX don't check, the 8/98 i18n bug fails here. */
00941         if (rdlen != dl)
00942             goto errxit;
00943 #endif
00944         entry->rdlen = rdlen;
00945         entry++;
00946         h->indexUsed++;
00947     } else {
00948         int nb = ntohl(pe->count);
00949         int_32 rdl;
00950         int_32 ril;
00951 
00952         h->flags &= ~HEADERFLAG_LEGACY;
00953 
00954         entry->info.type = htonl(pe->type);
00955         if (entry->info.type < RPM_MIN_TYPE || entry->info.type > RPM_MAX_TYPE)
00956             goto errxit;
00957         entry->info.count = htonl(pe->count);
00958 
00959         if (hdrchkTags(entry->info.count))
00960             goto errxit;
00961 
00962         {   int off = ntohl(pe->offset);
00963 
00964             if (hdrchkData(off))
00965                 goto errxit;
00966             if (off) {
00967                 int_32 * stei = memcpy(alloca(nb), dataStart + off, nb);
00968                 rdl = -ntohl(stei[2]);  /* negative offset */
00969                 ril = rdl/sizeof(*pe);
00970                 if (hdrchkTags(ril) || hdrchkData(rdl))
00971                     goto errxit;
00972                 entry->info.tag = htonl(pe->tag);
00973             } else {
00974                 ril = il;
00975                 /*@-sizeoftype@*/
00976                 rdl = (ril * sizeof(struct entryInfo));
00977                 /*@=sizeoftype@*/
00978                 entry->info.tag = HEADER_IMAGE;
00979             }
00980         }
00981         entry->info.offset = -rdl;      /* negative offset */
00982 
00983         /*@-assignexpose@*/
00984         entry->data = pe;
00985         /*@=assignexpose@*/
00986         entry->length = pvlen - sizeof(il) - sizeof(dl);
00987         rdlen = regionSwab(entry+1, ril-1, 0, pe+1, dataStart, entry->info.offset);
00988         if (rdlen < 0)
00989             goto errxit;
00990         entry->rdlen = rdlen;
00991 
00992         if (ril < h->indexUsed) {
00993             indexEntry newEntry = entry + ril;
00994             int ne = (h->indexUsed - ril);
00995             int rid = entry->info.offset+1;
00996             int rc;
00997 
00998             /* Load dribble entries from region. */
00999             rc = regionSwab(newEntry, ne, 0, pe+ril, dataStart, rid);
01000             if (rc < 0)
01001                 goto errxit;
01002             rdlen += rc;
01003 
01004           { indexEntry firstEntry = newEntry;
01005             int save = h->indexUsed;
01006             int j;
01007 
01008             /* Dribble entries replace duplicate region entries. */
01009             h->indexUsed -= ne;
01010             for (j = 0; j < ne; j++, newEntry++) {
01011                 (void) headerRemoveEntry(h, newEntry->info.tag);
01012                 if (newEntry->info.tag == HEADER_BASENAMES)
01013                     (void) headerRemoveEntry(h, HEADER_OLDFILENAMES);
01014             }
01015 
01016             /* If any duplicate entries were replaced, move new entries down. */
01017             if (h->indexUsed < (save - ne)) {
01018                 memmove(h->index + h->indexUsed, firstEntry,
01019                         (ne * sizeof(*entry)));
01020             }
01021             h->indexUsed += ne;
01022           }
01023         }
01024     }
01025 
01026     h->flags &= ~HEADERFLAG_SORTED;
01027     headerSort(h);
01028 
01029     /*@-globstate -observertrans @*/
01030     return h;
01031     /*@=globstate =observertrans @*/
01032 
01033 errxit:
01034     /*@-usereleased@*/
01035     if (h) {
01036         h->index = _free(h->index);
01037         /*@-refcounttrans@*/
01038         h = _free(h);
01039         /*@=refcounttrans@*/
01040     }
01041     /*@=usereleased@*/
01042     /*@-refcounttrans -globstate@*/
01043     return h;
01044     /*@=refcounttrans =globstate@*/
01045 }
01046 
01054 static /*@null@*/
01055 Header headerReload(/*@only@*/ Header h, int tag)
01056         /*@modifies h @*/
01057 {
01058     Header nh;
01059     int length;
01060     /*@-onlytrans@*/
01061     void * uh = doHeaderUnload(h, &length);
01062 
01063     h = headerFree(h);
01064     /*@=onlytrans@*/
01065     if (uh == NULL)
01066         return NULL;
01067     nh = headerLoad(uh);
01068     if (nh == NULL) {
01069         uh = _free(uh);
01070         return NULL;
01071     }
01072     if (nh->flags & HEADERFLAG_ALLOCATED)
01073         uh = _free(uh);
01074     nh->flags |= HEADERFLAG_ALLOCATED;
01075     if (ENTRY_IS_REGION(nh->index)) {
01076         if (tag == HEADER_SIGNATURES || tag == HEADER_IMMUTABLE)
01077             nh->index[0].info.tag = tag;
01078     }
01079     return nh;
01080 }
01081 
01087 static /*@null@*/
01088 Header headerCopyLoad(const void * uh)
01089         /*@*/
01090 {
01091     int_32 * ei = (int_32 *) uh;
01092     int_32 il = ntohl(ei[0]);           /* index length */
01093     int_32 dl = ntohl(ei[1]);           /* data length */
01094     /*@-sizeoftype@*/
01095     size_t pvlen = sizeof(il) + sizeof(dl) +
01096                         (il * sizeof(struct entryInfo)) + dl;
01097     /*@=sizeoftype@*/
01098     void * nuh = NULL;
01099     Header h = NULL;
01100 
01101     /* Sanity checks on header intro. */
01102     /*@-branchstate@*/
01103     if (!(hdrchkTags(il) || hdrchkData(dl)) && pvlen < headerMaxbytes) {
01104         nuh = memcpy(xmalloc(pvlen), uh, pvlen);
01105         if ((h = headerLoad(nuh)) != NULL)
01106             h->flags |= HEADERFLAG_ALLOCATED;
01107     }
01108     /*@=branchstate@*/
01109     /*@-branchstate@*/
01110     if (h == NULL)
01111         nuh = _free(nuh);
01112     /*@=branchstate@*/
01113     return h;
01114 }
01115 
01122 static /*@null@*/
01123 Header headerRead(FD_t fd, enum hMagic magicp)
01124         /*@modifies fd @*/
01125 {
01126     int_32 block[4];
01127     int_32 reserved;
01128     int_32 * ei = NULL;
01129     int_32 il;
01130     int_32 dl;
01131     int_32 magic;
01132     Header h = NULL;
01133     size_t len;
01134     int i;
01135 
01136     memset(block, 0, sizeof(block));
01137     i = 2;
01138     if (magicp == HEADER_MAGIC_YES)
01139         i += 2;
01140 
01141     /*@-type@*/ /* FIX: cast? */
01142     if (timedRead(fd, (char *)block, i*sizeof(*block)) != (i * sizeof(*block)))
01143         goto exit;
01144     /*@=type@*/
01145 
01146     i = 0;
01147 
01148     if (magicp == HEADER_MAGIC_YES) {
01149         magic = block[i++];
01150         if (memcmp(&magic, header_magic, sizeof(magic)))
01151             goto exit;
01152         reserved = block[i++];
01153     }
01154     
01155     il = ntohl(block[i]);       i++;
01156     dl = ntohl(block[i]);       i++;
01157 
01158     /*@-sizeoftype@*/
01159     len = sizeof(il) + sizeof(dl) + (il * sizeof(struct entryInfo)) + dl;
01160     /*@=sizeoftype@*/
01161 
01162     /* Sanity checks on header intro. */
01163     if (hdrchkTags(il) || hdrchkData(dl) || len > headerMaxbytes)
01164         goto exit;
01165 
01166     ei = xmalloc(len);
01167     ei[0] = htonl(il);
01168     ei[1] = htonl(dl);
01169     len -= sizeof(il) + sizeof(dl);
01170 
01171     /*@-type@*/ /* FIX: cast? */
01172     if (timedRead(fd, (char *)&ei[2], len) != len)
01173         goto exit;
01174     /*@=type@*/
01175     
01176     h = headerLoad(ei);
01177 
01178 exit:
01179     if (h) {
01180         if (h->flags & HEADERFLAG_ALLOCATED)
01181             ei = _free(ei);
01182         h->flags |= HEADERFLAG_ALLOCATED;
01183     } else if (ei)
01184         ei = _free(ei);
01185     /*@-mustmod@*/      /* FIX: timedRead macro obscures annotation */
01186     return h;
01187     /*@-mustmod@*/
01188 }
01189 
01197 static
01198 int headerWrite(FD_t fd, /*@null@*/ Header h, enum hMagic magicp)
01199         /*@globals fileSystem @*/
01200         /*@modifies fd, h, fileSystem @*/
01201 {
01202     ssize_t nb;
01203     int length;
01204     const void * uh;
01205 
01206     if (h == NULL)
01207         return 1;
01208     uh = doHeaderUnload(h, &length);
01209     if (uh == NULL)
01210         return 1;
01211     switch (magicp) {
01212     case HEADER_MAGIC_YES:
01213         /*@-sizeoftype@*/
01214         nb = Fwrite(header_magic, sizeof(char), sizeof(header_magic), fd);
01215         /*@=sizeoftype@*/
01216         if (nb != sizeof(header_magic))
01217             goto exit;
01218         break;
01219     case HEADER_MAGIC_NO:
01220         break;
01221     }
01222 
01223     /*@-sizeoftype@*/
01224     nb = Fwrite(uh, sizeof(char), length, fd);
01225     /*@=sizeoftype@*/
01226 
01227 exit:
01228     uh = _free(uh);
01229     return (nb == length ? 0 : 1);
01230 }
01231 
01238 static
01239 int headerIsEntry(/*@null@*/Header h, int_32 tag)
01240         /*@*/
01241 {
01242     /*@-mods@*/         /*@ FIX: h modified by sort. */
01243     return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
01244     /*@=mods@*/ 
01245 }
01246 
01257 static int copyEntry(const indexEntry entry,
01258                 /*@null@*/ /*@out@*/ hTYP_t type,
01259                 /*@null@*/ /*@out@*/ hPTR_t * p,
01260                 /*@null@*/ /*@out@*/ hCNT_t c,
01261                 int minMem)
01262         /*@modifies *type, *p, *c @*/
01263 {
01264     int_32 count = entry->info.count;
01265     int rc = 1;         /* XXX 1 on success. */
01266 
01267     if (p)
01268     switch (entry->info.type) {
01269     case RPM_BIN_TYPE:
01270         /*
01271          * XXX This only works for
01272          * XXX  "sealed" HEADER_IMMUTABLE/HEADER_SIGNATURES/HEADER_IMAGE.
01273          * XXX This will *not* work for unsealed legacy HEADER_IMAGE (i.e.
01274          * XXX a legacy header freshly read, but not yet unloaded to the rpmdb).
01275          */
01276         if (ENTRY_IS_REGION(entry)) {
01277             int_32 * ei = ((int_32 *)entry->data) - 2;
01278             /*@-castexpose@*/
01279             entryInfo pe = (entryInfo) (ei + 2);
01280             /*@=castexpose@*/
01281             char * dataStart = (char *) (pe + ntohl(ei[0]));
01282             int_32 rdl = -entry->info.offset;   /* negative offset */
01283             int_32 ril = rdl/sizeof(*pe);
01284 
01285             /*@-sizeoftype@*/
01286             rdl = entry->rdlen;
01287             count = 2 * sizeof(*ei) + (ril * sizeof(*pe)) + rdl;
01288             if (entry->info.tag == HEADER_IMAGE) {
01289                 ril -= 1;
01290                 pe += 1;
01291             } else {
01292                 count += REGION_TAG_COUNT;
01293                 rdl += REGION_TAG_COUNT;
01294             }
01295 
01296             *p = xmalloc(count);
01297             ei = (int_32 *) *p;
01298             ei[0] = htonl(ril);
01299             ei[1] = htonl(rdl);
01300 
01301             /*@-castexpose@*/
01302             pe = (entryInfo) memcpy(ei + 2, pe, (ril * sizeof(*pe)));
01303             /*@=castexpose@*/
01304 
01305             dataStart = (char *) memcpy(pe + ril, dataStart, rdl);
01306             /*@=sizeoftype@*/
01307 
01308             rc = regionSwab(NULL, ril, 0, pe, dataStart, 0);
01309             /* XXX 1 on success. */
01310             rc = (rc < 0) ? 0 : 1;
01311         } else {
01312             count = entry->length;
01313             *p = (!minMem
01314                 ? memcpy(xmalloc(count), entry->data, count)
01315                 : entry->data);
01316         }
01317         break;
01318     case RPM_STRING_TYPE:
01319         if (count == 1) {
01320             *p = entry->data;
01321             break;
01322         }
01323         /*@fallthrough@*/
01324     case RPM_STRING_ARRAY_TYPE:
01325     case RPM_I18NSTRING_TYPE:
01326     {   const char ** ptrEntry;
01327         /*@-sizeoftype@*/
01328         int tableSize = count * sizeof(char *);
01329         /*@=sizeoftype@*/
01330         char * t;
01331         int i;
01332 
01333         /*@-mods@*/
01334         if (minMem) {
01335             *p = xmalloc(tableSize);
01336             ptrEntry = (const char **) *p;
01337             t = entry->data;
01338         } else {
01339             t = xmalloc(tableSize + entry->length);
01340             *p = (void *)t;
01341             ptrEntry = (const char **) *p;
01342             t += tableSize;
01343             memcpy(t, entry->data, entry->length);
01344         }
01345         /*@=mods@*/
01346         for (i = 0; i < count; i++) {
01347             *ptrEntry++ = t;
01348             t = strchr(t, 0);
01349             t++;
01350         }
01351     }   break;
01352 
01353     default:
01354         *p = entry->data;
01355         break;
01356     }
01357     if (type) *type = entry->info.type;
01358     if (c) *c = count;
01359     return rc;
01360 }
01361 
01362 static int
01363 locale_match (const char *sample, const char *l_b, const char *l_e,
01364               char delim)
01365 {
01366         const char *p = l_b;
01367 
01368         for (; p < l_e && *p != delim; ++p)
01369                 ;
01370         if (p < l_e && !strncmp (sample, l_b, (p - l_b)))
01371                 return 1;
01372 
01373         return 0;
01374 }
01375 
01394 static int
01395 headerMatchLocale (const char *td, const char *l, const char *le,
01396                    int strip_lang)
01397         /*@ */
01398 {
01399         switch (strip_lang)
01400         {
01401                 case 0:
01402                         /* First try a complete match. */
01403                         if (strlen (td) == (le - l)
01404                             && !memcmp (td, l, (le - l)))
01405                                 return 1;
01406                         return 0;
01407 
01408                 case 1:
01409                         /* Next, try stripping optional dialect and matching. */
01410                         return locale_match (td, l, le, '@');
01411 
01412                 case 2:
01413                         /* Next, try stripping optional codeset and matching. */
01414                         return locale_match (td, l, le, '.');
01415 
01416                 default:
01417                         /* Finally, try stripping optional country code and matching. */
01418                         return locale_match (td, l, le, '_');
01419         }
01420 }
01421 
01422 static char *
01423 convert (char *ed, const char *td, const char *lang)
01424 {
01425         char   *saved_ctype = 0, *from_codeset = 0, *to_codeset = 0;
01426         char   *saved_ctype1, *from_codeset1, *to_codeset1, *result = 0;
01427         iconv_t icd;
01428 
01429         if (!*ed)
01430                 return ed;
01431 
01432         if ((saved_ctype1 = setlocale (LC_CTYPE, 0)) &&
01433             (saved_ctype = strdup (saved_ctype1)) &&
01434             setlocale (LC_CTYPE, lang) &&
01435             (to_codeset1 = nl_langinfo (CODESET)) &&
01436             (to_codeset = strdup (to_codeset1)) &&
01437             setlocale (LC_CTYPE, td) &&
01438             (from_codeset1 = nl_langinfo (CODESET)) &&
01439             (from_codeset = strdup (from_codeset1)) &&
01440             strcmp (from_codeset, to_codeset) &&
01441             ((icd = iconv_open (to_codeset, from_codeset)) != (iconv_t) - 1))
01442         {
01443                 size_t  insize = strlen (ed);
01444                 size_t  inbufleft = insize, outbufleft = insize * 4 + 1;
01445                 char    buf[outbufleft];
01446                 char   *inbuf = ed, *outbuf = buf;
01447 
01448                 if (iconv (icd, &inbuf, &inbufleft, &outbuf, &outbufleft) >= 0)
01449                 {
01450                         *outbuf = '\0';
01451                         if (strcmp (ed, buf))
01452                                 /* XXX memory leak */
01453                                 result = strdup (buf);
01454                 }
01455                 iconv_close (icd);
01456         }
01457 
01458         if (saved_ctype)
01459                 setlocale (LC_CTYPE, saved_ctype);
01460         from_codeset = _free (from_codeset);
01461         to_codeset = _free (to_codeset);
01462         saved_ctype = _free (saved_ctype);
01463 
01464         return result ? : ed;
01465 }
01466 
01473 /*@dependent@*/ /*@exposed@*/ static char *
01474 headerFindI18NString(Header h, indexEntry entry)
01475         /*@*/
01476 {
01477     const char *lang, *l, *le;
01478     indexEntry table;
01479     int strip_lang;
01480 
01481     /* XXX Drepper sez' this is the order. */
01482     if ((lang = getenv("LANGUAGE")) == NULL &&
01483         (lang = getenv("LC_ALL")) == NULL &&
01484         (lang = getenv("LC_MESSAGES")) == NULL &&
01485         (lang = getenv("LANG")) == NULL)
01486             return entry->data;
01487     
01488     /*@-mods@*/
01489     if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
01490         return entry->data;
01491     /*@=mods@*/
01492 
01493     for (strip_lang = 0; strip_lang < 4; strip_lang++) {
01494         for (l = lang; *l; l = le) {
01495             const char *td;
01496             char *ed;
01497             int langNum;
01498 
01499             while (*l && *l == ':')                     /* skip leading colons */
01500                 l++;
01501             if (*l == '\0')
01502                 break;
01503             for (le = l; *le && *le != ':'; le++)       /* find end of this locale */
01504                 ;
01505 
01506             /* For each entry in the header ... */
01507             for (langNum = 0, td = table->data, ed = entry->data;
01508                 langNum < entry->info.count;
01509                 langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
01510 
01511                 if (headerMatchLocale(td, l, le, strip_lang))
01512                     return convert( ed, td, lang );
01513             }
01514         }       
01515     }
01516 
01517     return gettext(entry->data);
01518 }
01519 
01530 static int intGetEntry(Header h, int_32 tag,
01531                 /*@null@*/ /*@out@*/ hTAG_t type,
01532                 /*@null@*/ /*@out@*/ hPTR_t * p,
01533                 /*@null@*/ /*@out@*/ hCNT_t c,
01534                 int minMem)
01535         /*@modifies *type, *p, *c @*/
01536 {
01537     indexEntry entry;
01538     int rc;
01539 
01540     /* First find the tag */
01541     /*@-mods@*/         /*@ FIX: h modified by sort. */
01542     entry = findEntry(h, tag, RPM_NULL_TYPE);
01543     /*@mods@*/
01544     if (entry == NULL) {
01545         if (type) type = 0;
01546         if (p) *p = NULL;
01547         if (c) *c = 0;
01548         return 0;
01549     }
01550 
01551     switch (entry->info.type) {
01552     case RPM_I18NSTRING_TYPE:
01553         rc = 1;
01554         if (type) *type = RPM_STRING_TYPE;
01555         if (c) *c = 1;
01556         /*@-dependenttrans@*/
01557         if (p) *p = headerFindI18NString(h, entry);
01558         /*@=dependenttrans@*/
01559         break;
01560     default:
01561         rc = copyEntry(entry, type, p, c, minMem);
01562         break;
01563     }
01564 
01565     /* XXX 1 on success */
01566     return ((rc == 1) ? 1 : 0);
01567 }
01568 
01576 static /*@null@*/ void * headerFreeTag(/*@unused@*/ Header h,
01577                 /*@only@*/ /*@null@*/ const void * data, rpmTagType type)
01578         /*@modifies data @*/
01579 {
01580     if (data) {
01581         /*@-branchstate@*/
01582         if (type == -1 ||
01583             type == RPM_STRING_ARRAY_TYPE ||
01584             type == RPM_I18NSTRING_TYPE ||
01585             type == RPM_BIN_TYPE)
01586                 data = _free(data);
01587         /*@=branchstate@*/
01588     }
01589     return NULL;
01590 }
01591 
01605 static
01606 int headerGetEntry(Header h, int_32 tag,
01607                         /*@null@*/ /*@out@*/ hTYP_t type,
01608                         /*@null@*/ /*@out@*/ void ** p,
01609                         /*@null@*/ /*@out@*/ hCNT_t c)
01610         /*@modifies *type, *p, *c @*/
01611 {
01612     return intGetEntry(h, tag, type, (hPTR_t *)p, c, 0);
01613 }
01614 
01627 static
01628 int headerGetEntryMinMemory(Header h, int_32 tag,
01629                         /*@null@*/ /*@out@*/ hTYP_t type,
01630                         /*@null@*/ /*@out@*/ hPTR_t * p,
01631                         /*@null@*/ /*@out@*/ hCNT_t c)
01632         /*@modifies *type, *p, *c @*/
01633 {
01634     return intGetEntry(h, tag, type, p, c, 1);
01635 }
01636 
01637 int headerGetRawEntry(Header h, int_32 tag, int_32 * type, hPTR_t * p,
01638                 int_32 * c)
01639 {
01640     indexEntry entry;
01641     int rc;
01642 
01643     if (p == NULL) return headerIsEntry(h, tag);
01644 
01645     /* First find the tag */
01646     /*@-mods@*/         /*@ FIX: h modified by sort. */
01647     entry = findEntry(h, tag, RPM_NULL_TYPE);
01648     /*@=mods@*/
01649     if (!entry) {
01650         if (p) *p = NULL;
01651         if (c) *c = 0;
01652         return 0;
01653     }
01654 
01655     rc = copyEntry(entry, type, p, c, 0);
01656 
01657     /* XXX 1 on success */
01658     return ((rc == 1) ? 1 : 0);
01659 }
01660 
01663 static void copyData(int_32 type, /*@out@*/ void * dstPtr, const void * srcPtr,
01664                 int_32 c, int dataLength)
01665         /*@modifies *dstPtr @*/
01666 {
01667     const char ** src;
01668     char * dst;
01669     int i;
01670 
01671     switch (type) {
01672     case RPM_STRING_ARRAY_TYPE:
01673     case RPM_I18NSTRING_TYPE:
01674         /* Otherwise, p is char** */
01675         i = c;
01676         src = (const char **) srcPtr;
01677         dst = dstPtr;
01678         while (i--) {
01679             if (*src) {
01680                 int len = strlen(*src) + 1;
01681                 memcpy(dst, *src, len);
01682                 dst += len;
01683             }
01684             src++;
01685         }
01686         break;
01687 
01688     default:
01689         memmove(dstPtr, srcPtr, dataLength);
01690         break;
01691     }
01692 }
01693 
01702 static void * grabData(int_32 type, hPTR_t p, int_32 c,
01703                 /*@out@*/ int * lengthPtr)
01704         /*@modifies *lengthPtr @*/
01705 {
01706     int length = dataLength(type, p, c, 0);
01707     void * data = xmalloc(length);
01708 
01709     copyData(type, data, p, c, length);
01710 
01711     if (lengthPtr)
01712         *lengthPtr = length;
01713     return data;
01714 }
01715 
01730 static
01731 int headerAddEntry(Header h, int_32 tag, int_32 type, const void * p, int_32 c)
01732         /*@modifies h @*/
01733 {
01734     indexEntry entry;
01735 
01736     /* Count must always be >= 1 for headerAddEntry. */
01737     if (c <= 0)
01738         return 0;
01739 
01740     /* Allocate more index space if necessary */
01741     if (h->indexUsed == h->indexAlloced) {
01742         h->indexAlloced += INDEX_MALLOC_SIZE;
01743         h->index = xrealloc(h->index, h->indexAlloced * sizeof(*h->index));
01744     }
01745 
01746     /* Fill in the index */
01747     entry = h->index + h->indexUsed;
01748     entry->info.tag = tag;
01749     entry->info.type = type;
01750     entry->info.count = c;
01751     entry->info.offset = 0;
01752     entry->data = grabData(type, p, c, &entry->length);
01753 
01754     if (h->indexUsed > 0 && tag < h->index[h->indexUsed-1].info.tag)
01755         h->flags &= ~HEADERFLAG_SORTED;
01756     h->indexUsed++;
01757 
01758     return 1;
01759 }
01760 
01775 static
01776 int headerAppendEntry(Header h, int_32 tag, int_32 type,
01777                 const void * p, int_32 c)
01778         /*@modifies h @*/
01779 {
01780     indexEntry entry;
01781     int length;
01782 
01783     /* First find the tag */
01784     entry = findEntry(h, tag, type);
01785     if (!entry)
01786         return 0;
01787 
01788     if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
01789         /* we can't do this */
01790         return 0;
01791     }
01792 
01793     length = dataLength(type, p, c, 0);
01794 
01795     if (ENTRY_IN_REGION(entry)) {
01796         char * t = xmalloc(entry->length + length);
01797         memcpy(t, entry->data, entry->length);
01798         entry->data = t;
01799         entry->info.offset = 0;
01800     } else
01801         entry->data = xrealloc(entry->data, entry->length + length);
01802 
01803     copyData(type, ((char *) entry->data) + entry->length, p, c, length);
01804 
01805     entry->length += length;
01806 
01807     entry->info.count += c;
01808 
01809     return 1;
01810 }
01811 
01822 static
01823 int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
01824                 const void * p, int_32 c)
01825         /*@modifies h @*/
01826 {
01827     return (findEntry(h, tag, type)
01828         ? headerAppendEntry(h, tag, type, p, c)
01829         : headerAddEntry(h, tag, type, p, c));
01830 }
01831 
01852 static
01853 int headerAddI18NString(Header h, int_32 tag, const char * string,
01854                 const char * lang)
01855         /*@modifies h @*/
01856 {
01857     indexEntry table, entry;
01858     const char ** strArray;
01859     int length;
01860     int ghosts;
01861     int i, langNum;
01862     char * buf;
01863 
01864     table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
01865     entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
01866 
01867     if (!table && entry)
01868         return 0;               /* this shouldn't ever happen!! */
01869 
01870     if (!table && !entry) {
01871         const char * charArray[2];
01872         int count = 0;
01873         if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
01874             /*@-observertrans -readonlytrans@*/
01875             charArray[count++] = "C";
01876             /*@=observertrans =readonlytrans@*/
01877         } else {
01878             /*@-observertrans -readonlytrans@*/
01879             charArray[count++] = "C";
01880             /*@=observertrans =readonlytrans@*/
01881             charArray[count++] = lang;
01882         }
01883         if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE, 
01884                         &charArray, count))
01885             return 0;
01886         table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
01887     }
01888 
01889     if (!table)
01890         return 0;
01891     /*@-branchstate@*/
01892     if (!lang) lang = "C";
01893     /*@=branchstate@*/
01894 
01895     {   const char * l = table->data;
01896         for (langNum = 0; langNum < table->info.count; langNum++) {
01897             if (!strcmp(l, lang)) break;
01898             l += strlen(l) + 1;
01899         }
01900     }
01901 
01902     if (langNum >= table->info.count) {
01903         length = strlen(lang) + 1;
01904         if (ENTRY_IN_REGION(table)) {
01905             char * t = xmalloc(table->length + length);
01906             memcpy(t, table->data, table->length);
01907             table->data = t;
01908             table->info.offset = 0;
01909         } else
01910             table->data = xrealloc(table->data, table->length + length);
01911         memmove(((char *)table->data) + table->length, lang, length);
01912         table->length += length;
01913         table->info.count++;
01914     }
01915 
01916     if (!entry) {
01917         strArray = alloca(sizeof(*strArray) * (langNum + 1));
01918         for (i = 0; i < langNum; i++)
01919             strArray[i] = "";
01920         strArray[langNum] = string;
01921         return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, strArray, 
01922                                 langNum + 1);
01923     } else if (langNum >= entry->info.count) {
01924         ghosts = langNum - entry->info.count;
01925         
01926         length = strlen(string) + 1 + ghosts;
01927         if (ENTRY_IN_REGION(entry)) {
01928             char * t = xmalloc(entry->length + length);
01929             memcpy(t, entry->data, entry->length);
01930             entry->data = t;
01931             entry->info.offset = 0;
01932         } else
01933             entry->data = xrealloc(entry->data, entry->length + length);
01934 
01935         memset(((char *)entry->data) + entry->length, '\0', ghosts);
01936         memmove(((char *)entry->data) + entry->length + ghosts, string, strlen(string)+1);
01937 
01938         entry->length += length;
01939         entry->info.count = langNum + 1;
01940     } else {
01941         char *b, *be, *e, *ee, *t;
01942         size_t bn, sn, en;
01943 
01944         /* Set beginning/end pointers to previous data */
01945         b = be = e = ee = entry->data;
01946         for (i = 0; i < table->info.count; i++) {
01947             if (i == langNum)
01948                 be = ee;
01949             ee += strlen(ee) + 1;
01950             if (i == langNum)
01951                 e  = ee;
01952         }
01953 
01954         /* Get storage for new buffer */
01955         bn = (be-b);
01956         sn = strlen(string) + 1;
01957         en = (ee-e);
01958         length = bn + sn + en;
01959         t = buf = xmalloc(length);
01960 
01961         /* Copy values into new storage */
01962         memcpy(t, b, bn);
01963         t += bn;
01964 /*@-mayaliasunique@*/
01965         memcpy(t, string, sn);
01966         t += sn;
01967         memcpy(t, e, en);
01968         t += en;
01969 /*@=mayaliasunique@*/
01970 
01971         /* Replace I18N string array */
01972         entry->length -= strlen(be) + 1;
01973         entry->length += sn;
01974         
01975         if (ENTRY_IN_REGION(entry)) {
01976             entry->info.offset = 0;
01977         } else
01978             entry->data = _free(entry->data);
01979         /*@-dependenttrans@*/
01980         entry->data = buf;
01981         /*@=dependenttrans@*/
01982     }
01983 
01984     return 0;
01985 }
01986 
01997 static
01998 int headerModifyEntry(Header h, int_32 tag, int_32 type,
01999                         const void * p, int_32 c)
02000         /*@modifies h @*/
02001 {
02002     indexEntry entry;
02003     void * oldData;
02004 
02005     /* First find the tag */
02006     entry = findEntry(h, tag, type);
02007     if (!entry)
02008         return 0;
02009 
02010     /* make sure entry points to the first occurence of this tag */
02011     while (entry > h->index && (entry - 1)->info.tag == tag)  
02012         entry--;
02013 
02014     /* free after we've grabbed the new data in case the two are intertwined;
02015        that's a bad idea but at least we won't break */
02016     oldData = entry->data;
02017 
02018     entry->info.count = c;
02019     entry->info.type = type;
02020     entry->data = grabData(type, p, c, &entry->length);
02021 
02022     /*@-branchstate@*/
02023     if (ENTRY_IN_REGION(entry)) {
02024         entry->info.offset = 0;
02025     } else
02026         oldData = _free(oldData);
02027     /*@=branchstate@*/
02028 
02029     return 1;
02030 }
02031 
02034 static char escapedChar(const char ch)  /*@*/
02035 {
02036     switch (ch) {
02037     case 'a':   return '\a';
02038     case 'b':   return '\b';
02039     case 'f':   return '\f';
02040     case 'n':   return '\n';
02041     case 'r':   return '\r';
02042     case 't':   return '\t';
02043     case 'v':   return '\v';
02044     default:    return ch;
02045     }
02046 }
02047 
02054 static /*@null@*/ sprintfToken
02055 freeFormat( /*@only@*/ /*@null@*/ sprintfToken format, int num)
02056         /*@modifies *format @*/
02057 {
02058     int i;
02059 
02060     if (format == NULL) return NULL;
02061     for (i = 0; i < num; i++) {
02062         switch (format[i].type) {
02063         case PTOK_ARRAY:
02064             format[i].u.array.format =
02065                 freeFormat(format[i].u.array.format,
02066                         format[i].u.array.numTokens);
02067             /*@switchbreak@*/ break;
02068         case PTOK_COND:
02069             format[i].u.cond.ifFormat =
02070                 freeFormat(format[i].u.cond.ifFormat, 
02071                         format[i].u.cond.numIfTokens);
02072             format[i].u.cond.elseFormat =
02073                 freeFormat(format[i].u.cond.elseFormat, 
02074                         format[i].u.cond.numElseTokens);
02075             switch (format[i].u.cond.test.type) {
02076             case TRIVIAL:
02077               /* the usual old way; format[i].test.u.tag is a struct */
02078               break;
02079             case StringTAG_String3:
02080               /* the added option */
02081               format[i].u.cond.test.u.tag_str3.headFormat =
02082                 freeFormat(format[i].u.cond.test.u.tag_str3.headFormat, 
02083                            format[i].u.cond.test.u.tag_str3.numHeadTokens);
02084               /* We don't need to free the strings; we have not allocated them.
02085                  And PTOK_STRING case is analogous and also doesn't free the string. */
02086               break;
02087             default:
02088               /* report an error */
02089               rpmMessage(RPMMESS_WARNING, _("Unknown test type in \%|?:|; perhaps some memory is leaking right now.\n"));
02090               break;
02091             }
02092             /*@switchbreak@*/ break;
02093         case PTOK_NONE:
02094         case PTOK_TAG:
02095         case PTOK_STRING:
02096         default:
02097             /*@switchbreak@*/ break;
02098         }
02099     }
02100     format = _free(format);
02101     return NULL;
02102 }
02103 
02106 static void findTag(char * name, const headerTagTableEntry tags, 
02107                     const headerSprintfExtension extensions,
02108                     /*@out@*/ headerTagTableEntry * tagMatch,
02109                     /*@out@*/ headerSprintfExtension * extMatch)
02110         /*@modifies *tagMatch, *extMatch @*/
02111 {
02112     headerTagTableEntry entry;
02113     headerSprintfExtension ext;
02114     const char * tagname;
02115 
02116     *tagMatch = NULL;
02117     *extMatch = NULL;
02118 
02119     if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
02120         char * t = alloca(strlen(name) + sizeof("RPMTAG_"));
02121         (void) stpcpy( stpcpy(t, "RPMTAG_"), name);
02122         tagname = t;
02123     } else {
02124         tagname = name;
02125     }
02126 
02127     /* Search extensions first to permit overriding header tags. */
02128     ext = extensions;
02129     while (ext->type != HEADER_EXT_LAST) {
02130         if (ext->name != NULL && ext->type == HEADER_EXT_TAG
02131         && !xstrcasecmp(ext->name, tagname))
02132             break;
02133 
02134         if (ext->type == HEADER_EXT_MORE)
02135             ext = ext->u.more;
02136         else
02137             ext++;
02138     }
02139 
02140     if (ext->type == HEADER_EXT_TAG) {
02141         *extMatch = ext;
02142         return;
02143     }
02144 
02145     /* Search header tags. */
02146     for (entry = tags; entry->name; entry++)
02147         if (entry->name && !xstrcasecmp(entry->name, tagname))
02148             break;
02149 
02150     if (entry->name) {
02151         *tagMatch = entry;
02152         return;
02153     }
02154 }
02155 
02156 /* forward ref */
02157 static int parseExpression(sprintfToken token, char * str, 
02158                 const headerTagTableEntry tags, 
02159                 const headerSprintfExtension extensions,
02160                 /*@out@*/char ** endPtr, /*@null@*/ /*@out@*/ errmsg_t * errmsg)
02161         /*@modifies str, *str, *token, *endPtr, *errmsg @*/;
02162 
02165 static int parseFormat(char * str, const headerTagTableEntry tags,
02166                 const headerSprintfExtension extensions,
02167                 /*@out@*/sprintfToken * formatPtr, /*@out@*/int * numTokensPtr,
02168                 /*@null@*/ /*@out@*/ char ** endPtr, int state,
02169                 /*@null@*/ /*@out@*/ errmsg_t * errmsg)
02170         /*@modifies str, *str, *formatPtr, *numTokensPtr, *endPtr, *errmsg @*/
02171 {
02172     char * chptr, * start, * next, * dst;
02173     sprintfToken format;
02174     int numTokens;
02175     int currToken;
02176     headerTagTableEntry tag;
02177     headerSprintfExtension ext;
02178     int i;
02179     int done = 0;
02180 
02181     /* upper limit on number of individual formats */
02182     numTokens = 0;
02183     for (chptr = str; *chptr != '\0'; chptr++)
02184         if (*chptr == '%') numTokens++;
02185     numTokens = numTokens * 2 + 1;
02186 
02187     format = xcalloc(numTokens, sizeof(*format));
02188     if (endPtr) *endPtr = NULL;
02189 
02190     /*@-infloops@*/ /* LCL: can't detect done termination */
02191     dst = start = str;
02192     currToken = -1;
02193     while (*start != '\0') {
02194         switch (*start) {
02195         case '%':
02196             /* handle %% */
02197             if (*(start + 1) == '%') {
02198                 if (currToken < 0 || format[currToken].type != PTOK_STRING) {
02199                     currToken++;
02200                     format[currToken].type = PTOK_STRING;
02201                     /*@-temptrans -assignexpose@*/
02202                     dst = format[currToken].u.string.string = start;
02203                     /*@=temptrans =assignexpose@*/
02204                 }
02205 
02206                 start++;
02207 
02208                 *dst++ = *start++;
02209 
02210                 /*@switchbreak@*/ break;
02211             } 
02212 
02213             currToken++;
02214             *dst++ = '\0';
02215             start++;
02216 
02217             if (*start == '|') {
02218                 char * newEnd;
02219 
02220                 start++;
02221                 if (parseExpression(format + currToken, start, tags, 
02222                                     extensions, &newEnd, errmsg))
02223                 {
02224                     format = freeFormat(format, numTokens);
02225                     return 1;
02226                 }
02227                 start = newEnd;
02228                 /*@switchbreak@*/ break;
02229             }
02230 
02231             /*@-assignexpose@*/
02232             format[currToken].u.tag.format = start;
02233             /*@=assignexpose@*/
02234             format[currToken].u.tag.pad = 0;
02235             format[currToken].u.tag.justOne = 0;
02236             format[currToken].u.tag.arrayCount = 0;
02237 
02238             chptr = start;
02239             while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
02240             if (!*chptr || *chptr == '%') {
02241                 /*@-observertrans -readonlytrans@*/
02242                 if (errmsg) *errmsg = _("missing { after %");
02243                 /*@=observertrans =readonlytrans@*/
02244                 format = freeFormat(format, numTokens);
02245                 return 1;
02246             }
02247 
02248             *chptr++ = '\0';
02249 
02250             while (start < chptr) {
02251                 if (xisdigit(*start)) {
02252                     i = strtoul(start, &start, 10);
02253                     format[currToken].u.tag.pad += i;
02254                 } else {
02255                     start++;
02256                 }
02257             }
02258 
02259             if (*start == '=') {
02260                 format[currToken].u.tag.justOne = 1;
02261                 start++;
02262             } else if (*start == '#') {
02263                 format[currToken].u.tag.justOne = 1;
02264                 format[currToken].u.tag.arrayCount = 1;
02265                 start++;
02266             }
02267 
02268             next = start;
02269             while (*next && *next != '}') next++;
02270             if (!*next) {
02271                 /*@-observertrans -readonlytrans@*/
02272                 if (errmsg) *errmsg = _("missing } after %{");
02273                 /*@=observertrans =readonlytrans@*/
02274                 format = freeFormat(format, numTokens);
02275                 return 1;
02276             }
02277             *next++ = '\0';
02278 
02279             chptr = start;
02280             while (*chptr && *chptr != ':') chptr++;
02281 
02282             if (*chptr != '\0') {
02283                 *chptr++ = '\0';
02284                 if (!*chptr) {
02285                     /*@-observertrans -readonlytrans@*/
02286                     if (errmsg) *errmsg = _("empty tag format");
02287                     /*@=observertrans =readonlytrans@*/
02288                     format = freeFormat(format, numTokens);
02289                     return 1;
02290                 }
02291                 /*@-assignexpose@*/
02292                 format[currToken].u.tag.type = chptr;
02293                 /*@=assignexpose@*/
02294             } else {
02295                 format[currToken].u.tag.type = NULL;
02296             }
02297             
02298             if (!*start) {
02299                 /*@-observertrans -readonlytrans@*/
02300                 if (errmsg) *errmsg = _("empty tag name");
02301                 /*@=observertrans =readonlytrans@*/
02302                 format = freeFormat(format, numTokens);
02303                 return 1;
02304             }
02305 
02306             i = 0;
02307             findTag(start, tags, extensions, &tag, &ext);
02308 
02309             if (tag) {
02310                 format[currToken].u.tag.ext = NULL;
02311                 format[currToken].u.tag.tag = tag->val;
02312             } else if (ext) {
02313                 format[currToken].u.tag.ext = ext->u.tagFunction;
02314                 format[currToken].u.tag.extNum = ext - extensions;
02315             } else {
02316                 /*@-observertrans -readonlytrans@*/
02317                 if (errmsg) *errmsg = _("unknown tag");
02318                 /*@=observertrans =readonlytrans@*/
02319                 format = freeFormat(format, numTokens);
02320                 return 1;
02321             }
02322 
02323             format[currToken].type = PTOK_TAG;
02324 
02325             start = next;
02326 
02327             /*@switchbreak@*/ break;
02328 
02329         case '[':
02330             *dst++ = '\0';
02331             *start++ = '\0';
02332             currToken++;
02333 
02334             if (parseFormat(start, tags, extensions, 
02335                             &format[currToken].u.array.format,
02336                             &format[currToken].u.array.numTokens,
02337                             &start, PARSER_IN_ARRAY, errmsg)) {
02338                 format = freeFormat(format, numTokens);
02339                 return 1;
02340             }
02341 
02342             if (!start) {
02343                 /*@-observertrans -readonlytrans@*/
02344                 if (errmsg) *errmsg = _("] expected at end of array");
02345                 /*@=observertrans =readonlytrans@*/
02346                 format = freeFormat(format, numTokens);
02347                 return 1;
02348             }
02349 
02350             dst = start;
02351 
02352             format[currToken].type = PTOK_ARRAY;
02353 
02354             /*@switchbreak@*/ break;
02355 
02356         case ']':
02357         case '}':
02358             if ((*start == ']' && state != PARSER_IN_ARRAY) ||
02359                 (*start == '}' && state != PARSER_IN_EXPR)) {
02360                 if (*start == ']') {
02361                     /*@-observertrans -readonlytrans@*/
02362                     if (errmsg) *errmsg = _("unexpected ]");
02363                     /*@=observertrans =readonlytrans@*/
02364                 } else {
02365                     /*@-observertrans -readonlytrans@*/
02366                     if (errmsg) *errmsg = _("unexpected }");
02367                     /*@=observertrans =readonlytrans@*/
02368                 }
02369                 format = freeFormat(format, numTokens);
02370                 return 1;
02371             }
02372             *start++ = '\0';
02373             if (endPtr) *endPtr = start;
02374             done = 1;
02375             /*@switchbreak@*/ break;
02376 
02377         default:
02378             if (currToken < 0 || format[currToken].type != PTOK_STRING) {
02379                 currToken++;
02380                 format[currToken].type = PTOK_STRING;
02381                 /*@-temptrans -assignexpose@*/
02382                 dst = format[currToken].u.string.string = start;
02383                 /*@=temptrans =assignexpose@*/
02384             }
02385 
02386             if (*start == '\\') {
02387                 start++;
02388                 *dst++ = escapedChar(*start++);
02389             } else {
02390                 *dst++ = *start++;
02391             }
02392             /*@switchbreak@*/ break;
02393         }
02394         if (done)
02395             break;
02396     }
02397     /*@=infloops@*/
02398 
02399     *dst = '\0';
02400 
02401     currToken++;
02402     for (i = 0; i < currToken; i++) {
02403         if (format[i].type == PTOK_STRING)
02404             format[i].u.string.len = strlen(format[i].u.string.string);
02405     }
02406 
02407     *numTokensPtr = currToken;
02408     *formatPtr = format;
02409 
02410     return 0;
02411 }
02412 
02415 static int parseExpression(sprintfToken token, char * str, 
02416                 const headerTagTableEntry tags, 
02417                 const headerSprintfExtension extensions,
02418                 /*@out@*/ char ** endPtr,
02419                 /*@null@*/ /*@out@*/ errmsg_t * errmsg)
02420 {
02421     headerTagTableEntry tag;
02422     headerSprintfExtension ext;
02423     char * chptr;
02424     char * end;
02425 
02426     if (errmsg) *errmsg = NULL;
02427     chptr = str;
02428     while (*chptr && *chptr != '?') chptr++;
02429 
02430     if (*chptr != '?') {
02431         /*@-observertrans -readonlytrans@*/
02432         if (errmsg) *errmsg = _("? expected in expression");
02433         /*@=observertrans =readonlytrans@*/
02434         return 1;
02435     }
02436 
02437     *chptr++ = '\0';;
02438 
02439     if (*chptr != '{') {
02440         /*@-observertrans -readonlytrans@*/
02441         if (errmsg) *errmsg = _("{ expected after ? in expression");
02442         /*@=observertrans =readonlytrans@*/
02443         return 1;
02444     }
02445 
02446     chptr++;
02447 
02448     if (parseFormat(chptr, tags, extensions, &token->u.cond.ifFormat, 
02449                     &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR, errmsg)) 
02450         return 1;
02451 
02452     if (!end || !*end) {
02453         /*@-observertrans -readonlytrans@*/
02454         if (errmsg) *errmsg = _("} expected in expression");
02455         /*@=observertrans =readonlytrans@*/
02456         token->u.cond.ifFormat =
02457                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02458         return 1;
02459     }
02460 
02461     chptr = end;
02462     if (*chptr != ':' && *chptr != '|') {
02463         /*@-observertrans -readonlytrans@*/
02464         if (errmsg) *errmsg = _(": expected following ? subexpression");
02465         /*@=observertrans =readonlytrans@*/
02466         token->u.cond.ifFormat =
02467                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02468         return 1;
02469     }
02470 
02471     if (*chptr == '|') {
02472         (void) parseFormat(xstrdup(""), tags, extensions,
02473                         &token->u.cond.elseFormat, 
02474                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR, 
02475                         errmsg);
02476     } else {
02477         chptr++;
02478 
02479         if (*chptr != '{') {
02480             /*@-observertrans -readonlytrans@*/
02481             if (errmsg) *errmsg = _("{ expected after : in expression");
02482             /*@=observertrans =readonlytrans@*/
02483             token->u.cond.ifFormat =
02484                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02485             return 1;
02486         }
02487 
02488         chptr++;
02489 
02490         if (parseFormat(chptr, tags, extensions, &token->u.cond.elseFormat, 
02491                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR, 
02492                         errmsg)) 
02493             return 1;
02494         if (!end || !*end) {
02495             /*@-observertrans -readonlytrans@*/
02496             if (errmsg) *errmsg = _("} expected in expression");
02497             /*@=observertrans =readonlytrans@*/
02498             token->u.cond.ifFormat =
02499                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02500             return 1;
02501         }
02502 
02503         chptr = end;
02504         if (*chptr != '|') {
02505             /*@-observertrans -readonlytrans@*/
02506             if (errmsg) *errmsg = _("| expected at end of expression");
02507             /*@=observertrans =readonlytrans@*/
02508             token->u.cond.ifFormat =
02509                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02510             token->u.cond.elseFormat =
02511                 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
02512             return 1;
02513         }
02514     }
02515         
02516     chptr++;
02517 
02518     *endPtr = chptr;
02519 
02520     { /* branching between the trivial old test for the conditional and 
02521          the added test for EVR comparison. */
02522       char * str2 = strchr(str,'>');
02523       if (str2) {
02524         char * endOfParsed = NULL;
02525         rpmMessage(RPMMESS_WARNING, _("Parsing non-standard test (>) for \%|?{}:{}|.\n"));
02526         *str2 ='\0';
02527         ++str2; /* str2 is the beginning of the second part: after the > sign. */
02528 
02529         if ( parseFormat(str, tags, extensions, 
02530                          &token->u.cond.test.u.tag_str3.headFormat, 
02531                          &token->u.cond.test.u.tag_str3.numHeadTokens, 
02532                          &endOfParsed, PARSER_IN_EXPR, errmsg) 
02533              /* this doesn't work, NULL is returned: || ( endOfParsed != str2 ) */ )
02534           {
02535             /*@-observertrans -readonlytrans@*/
02536             if (errmsg 
02537                 && ! *errmsg /* *errmsg was set to NULL at the function beginnging */ ) 
02538             *errmsg = _("the left part of >-expr finished before the > sign");
02539             /*@=observertrans =readonlytrans@*/
02540             token->u.cond.ifFormat =
02541                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02542             token->u.cond.elseFormat =
02543                 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
02544             return 1;
02545           }
02546       
02547         token->u.cond.test.type = StringTAG_String3;
02548         token->u.cond.test.u.tag_str3.predicate = &isChangeNameMoreFresh;
02549 
02550         parseEVR(str2, 
02551                  &token->u.cond.test.u.tag_str3.tail[0],  
02552                  &token->u.cond.test.u.tag_str3.tail[1],  
02553                  &token->u.cond.test.u.tag_str3.tail[2]);
02554         /* We could strdup tail[j], but it seems we don't need this: the rest of similar code
02555            doesn't perform this. And we don't have to free them. */
02556         rpmMessage(RPMMESS_DEBUG, "Will cmp with e=%s, v=%s, r=%s\n",
02557                  token->u.cond.test.u.tag_str3.tail[0],  
02558                  token->u.cond.test.u.tag_str3.tail[1],  
02559                  token->u.cond.test.u.tag_str3.tail[2]);
02560       }
02561       else {
02562         struct sprintfTag head;
02563         rpmMessage(RPMMESS_DEBUG, _("The usual way of parsing the test part for \%|?:|\n"));
02564         findTag(str, tags, extensions, &tag, &ext);
02565         if (tag) {
02566           head.ext = NULL;
02567           head.tag = tag->val;
02568         } else if (ext) {
02569           head.ext = ext->u.tagFunction;
02570           head.extNum = ext - extensions;
02571         } else {
02572           head.ext = NULL;
02573           head.tag = -1;
02574         }
02575         token->u.cond.test.type = TRIVIAL;
02576         token->u.cond.test.u.tag = head;
02577       }
02578       
02579     }
02580     
02581     token->type = PTOK_COND;
02582 
02583     return 0;
02584 }
02585 
02589 static int getExtension(Header h, headerTagTagFunction fn,
02590                 /*@out@*/ hTYP_t typeptr,
02591                 /*@out@*/ hPTR_t * data,
02592                 /*@out@*/ hCNT_t countptr,
02593                 extensionCache ext)
02594         /*@modifies *typeptr, *data, *countptr, ext @*/
02595 {
02596     if (!ext->avail) {
02597         if (fn(h, &ext->type, &ext->data, &ext->count, &ext->freeit))
02598             return 1;
02599         ext->avail = 1;
02600     }
02601 
02602     if (typeptr) *typeptr = ext->type;
02603     if (data) *data = ext->data;
02604     if (countptr) *countptr = ext->count;
02605 
02606     return 0;
02607 }
02608 
02611 static char * formatValue(sprintfTag tag, Header h, 
02612                 const headerSprintfExtension extensions,
02613                 extensionCache extCache, int element,
02614                 char ** valp, int * vallenp, int * allocedp)
02615         /*@modifies extCache, *valp, *vallenp, *allocedp @*/
02616 {
02617     char * val = NULL;
02618     int need = 0;
02619     char * t, * te;
02620     char buf[20];
02621     int_32 count, type;
02622     hPTR_t data;
02623     unsigned int intVal;
02624     const char ** strarray;
02625     int datafree = 0;
02626     int countBuf;
02627     headerTagFormatFunction tagtype = NULL;
02628     headerSprintfExtension ext;
02629 
02630     memset(buf, 0, sizeof(buf));
02631     /*@-branchstate@*/
02632     if (tag->ext) {
02633         if (getExtension(h, tag->ext, &type, &data, &count, 
02634                          extCache + tag->extNum))
02635         {
02636             count = 1;
02637             type = RPM_STRING_TYPE;     
02638             data = "(none)";
02639         }
02640     } else {
02641         if (!headerGetEntry(h, tag->tag, &type, (void **)&data, &count)) {
02642             count = 1;
02643             type = RPM_STRING_TYPE;     
02644             data = "(none)";
02645         }
02646 
02647         datafree = 1;
02648     }
02649     /*@=branchstate@*/
02650 
02651     if (tag->arrayCount) {
02652         /*@-observertrans -modobserver@*/
02653         data = headerFreeData(data, type);
02654         /*@=observertrans =modobserver@*/
02655 
02656         countBuf = count;
02657         data = &countBuf;
02658         count = 1;
02659         type = RPM_INT32_TYPE;
02660     }
02661 
02662     (void) stpcpy( stpcpy(buf, "%"), tag->format);
02663 
02664     if (tag->type) {
02665         ext = extensions;
02666         while (ext->type != HEADER_EXT_LAST) {
02667             if (ext->name != NULL && ext->type == HEADER_EXT_FORMAT
02668             && !strcmp(ext->name, tag->type))
02669             {
02670                 tagtype = ext->u.formatFunction;
02671                 break;
02672             }
02673 
02674             if (ext->type == HEADER_EXT_MORE)
02675                 ext = ext->u.more;
02676             else
02677                 ext++;
02678         }
02679     }
02680     
02681     /*@-branchstate@*/
02682     switch (type) {
02683     case RPM_STRING_ARRAY_TYPE:
02684         strarray = (const char **)data;
02685 
02686         if (tagtype)
02687             val = tagtype(RPM_STRING_TYPE, strarray[element], buf, tag->pad, 0);
02688 
02689         if (val) {
02690             need = strlen(val);
02691         } else {
02692             need = strlen(strarray[element]) + tag->pad + 20;
02693             val = xmalloc(need+1);
02694             strcat(buf, "s");
02695             /*@-formatconst@*/
02696             sprintf(val, buf, strarray[element]);
02697             /*@=formatconst@*/
02698         }
02699 
02700         /*@-observertrans -modobserver@*/
02701         if (datafree) data = _free(data);
02702         /*@=observertrans =modobserver@*/
02703 
02704         break;
02705 
02706     case RPM_STRING_TYPE:
02707         if (tagtype)
02708             val = tagtype(RPM_STRING_ARRAY_TYPE, data, buf, tag->pad,  0);
02709 
02710         if (val) {
02711             need = strlen(val);
02712         } else {
02713             need = strlen(data) + tag->pad + 20;
02714             val = xmalloc(need+1);
02715             strcat(buf, "s");
02716             /*@-formatconst@*/
02717             sprintf(val, buf, data);
02718             /*@=formatconst@*/
02719         }
02720         break;
02721 
02722     case RPM_CHAR_TYPE:
02723     case RPM_INT8_TYPE:
02724     case RPM_INT16_TYPE:
02725     case RPM_INT32_TYPE:
02726         switch (type) {
02727         case RPM_CHAR_TYPE:     
02728         case RPM_INT8_TYPE:
02729             intVal = *(((int_8 *) data) + element);
02730             /*@innerbreak@*/ break;
02731         case RPM_INT16_TYPE:
02732             intVal = *(((uint_16 *) data) + element);
02733             /*@innerbreak@*/ break;
02734         default:                /* keep -Wall quiet */
02735         case RPM_INT32_TYPE:
02736             intVal = *(((int_32 *) data) + element);
02737             /*@innerbreak@*/ break;
02738         }
02739 
02740         if (tagtype)
02741             val = tagtype(RPM_INT32_TYPE, &intVal, buf, tag->pad,  element);
02742 
02743         if (val) {
02744             need = strlen(val);
02745         } else {
02746             need = 10 + tag->pad + 20;
02747             val = xmalloc(need+1);
02748             strcat(buf, "d");
02749             /*@-formatconst@*/
02750             sprintf(val, buf, intVal);
02751             /*@=formatconst@*/
02752         }
02753         break;
02754 
02755     case RPM_BIN_TYPE:
02756         if (tagtype)
02757             val = tagtype(RPM_BIN_TYPE, data, buf, tag->pad, count);
02758 
02759         if (val) {
02760             need = count;       /* XXX broken iff RPM_BIN_TYPE extension */
02761         } else {
02762 #ifdef  NOTYET
02763             val = memcpy(xmalloc(count), data, count);
02764 #else
02765             /* XXX format string not used */
02766             static char hex[] = "0123456789abcdef";
02767             const char * s = data;
02768 
02769             need = 2*count + tag->pad;
02770             val = t = xmalloc(need+1);
02771             while (count-- > 0) {
02772                 unsigned int i;
02773                 i = *s++;
02774                 *t++ = hex[ (i >> 4) & 0xf ];
02775                 *t++ = hex[ (i     ) & 0xf ];
02776             }
02777             *t = '\0';
02778 #endif
02779         }
02780         break;
02781 
02782     default:
02783         need = sizeof("(unknown type)") - 1;
02784         val = xstrdup("(unknown type)");
02785         break;
02786     }
02787     /*@=branchstate@*/
02788 
02789     if (val && need > 0) {
02790         if (((*vallenp) + need) >= (*allocedp)) {
02791             if ((*allocedp) <= need)
02792                 (*allocedp) += need;
02793             (*allocedp) <<= 1;
02794             (*valp) = xrealloc((*valp), (*allocedp)+1); 
02795         }
02796         t = (*valp) + (*vallenp);
02797         te = stpcpy(t, val);
02798         (*vallenp) += (te - t);
02799         val = _free(val);
02800     }
02801 
02802     return ((*valp) + (*vallenp));
02803 }
02804 
02807 static char * singleSprintf(Header h, sprintfToken token,
02808                 const headerSprintfExtension extensions,
02809                 extensionCache extCache, int element,
02810                 char ** valp, int * vallenp, int * allocedp)
02811         /*@modifies h, extCache, *valp, *vallenp, *allocedp @*/
02812 {
02813     char * t, * te;
02814     int i, j;
02815     int numElements;
02816     int type;
02817     sprintfToken condFormat;
02818     int condNumFormats;
02819     int need;
02820 
02821     /* we assume the token and header have been validated already! */
02822 
02823     switch (token->type) {
02824     case PTOK_NONE:
02825         break;
02826 
02827     case PTOK_STRING:
02828         need = token->u.string.len;
02829         if (need <= 0) break;
02830         if (((*vallenp) + need) >= (*allocedp)) {
02831             if ((*allocedp) <= need)
02832                 (*allocedp) += need;
02833             (*allocedp) <<= 1;
02834             (*valp) = xrealloc((*valp), (*allocedp)+1); 
02835         }
02836         t = (*valp) + (*vallenp);
02837         te = stpcpy(t, token->u.string.string);
02838         (*vallenp) += (te - t);
02839         break;
02840 
02841     case PTOK_TAG:
02842         t = (*valp) + (*vallenp);
02843         te = formatValue(&token->u.tag, h, extensions, extCache,
02844                         (token->u.tag.justOne ? 0 : element),
02845                         valp, vallenp, allocedp);
02846         break;
02847 
02848     case PTOK_COND:
02849       {
02850         int testResult = 0; /* false by default */
02851         switch (token->u.cond.test.type) {
02852         case TRIVIAL:
02853           testResult = token->u.cond.test.u.tag.ext ||
02854             headerIsEntry(h, token->u.cond.test.u.tag.tag);
02855           break;
02856         case StringTAG_String3:
02857           { /* this piece if code is based on headerSprintf() */ 
02858             char * head = NULL; int head_en = 0;
02859             int head_alloced = 0;
02860             head = xstrdup("");
02861             for (i = 0; i < token->u.cond.test.u.tag_str3.numHeadTokens; i++)
02862               /* head_t = -- what do we need the return value for? headerSprintf() discards it. */
02863               singleSprintf(h, token->u.cond.test.u.tag_str3.headFormat + i, 
02864                             extensions, extCache,
02865                             element, &head, &head_en, &head_alloced);
02866             if (head != NULL && head_en < head_alloced)
02867               head = xrealloc(head, head_en+1); 
02868             testResult = head
02869               && token->u.cond.test.u.tag_str3.predicate(head, token->u.cond.test.u.tag_str3.tail);
02870             /* Do we free all the alloced data heer? */
02871             _free(head);
02872           }
02873           break;
02874         default:
02875           /* report an error */
02876           rpmMessage(RPMMESS_WARNING, _("Unknown test type in \%|?:|; assuming false.\n"));
02877           testResult = 0;
02878           break;
02879         }
02880 
02881         if (testResult) {
02882             condFormat = token->u.cond.ifFormat;
02883             condNumFormats = token->u.cond.numIfTokens;
02884         } else {
02885             condFormat = token->u.cond.elseFormat;
02886             condNumFormats = token->u.cond.numElseTokens;
02887         }
02888       }
02889 
02890         need = condNumFormats * 20;
02891         if (condFormat == NULL || need <= 0) break;
02892         if (((*vallenp) + need) >= (*allocedp)) {
02893             if ((*allocedp) <= need)
02894                 (*allocedp) += need;
02895             (*allocedp) <<= 1;
02896             (*valp) = xrealloc((*valp), (*allocedp)+1); 
02897         }
02898 
02899         t = (*valp) + (*vallenp);
02900         for (i = 0; i < condNumFormats; i++)
02901             te = singleSprintf(h, condFormat + i, extensions, extCache,
02902                                 element, valp, vallenp, allocedp);
02903         break;
02904 
02905     case PTOK_ARRAY:
02906         numElements = -1;
02907         for (i = 0; i < token->u.array.numTokens; i++) {
02908             if (token->u.array.format[i].type != PTOK_TAG ||
02909                 token->u.array.format[i].u.tag.arrayCount ||
02910                 token->u.array.format[i].u.tag.justOne) continue;
02911 
02912             if (token->u.array.format[i].u.tag.ext) {
02913                 const void * data;
02914                 if (getExtension(h, token->u.array.format[i].u.tag.ext,
02915                                  &type, &data, &numElements, 
02916                                  extCache + 
02917                                    token->u.array.format[i].u.tag.extNum))
02918                      continue;
02919             } else {
02920                 if (!headerGetEntry(h, token->u.array.format[i].u.tag.tag, 
02921                                     &type, NULL, &numElements))
02922                     continue;
02923             } 
02924             /*@loopbreak@*/ break;
02925         }
02926 
02927         if (numElements == -1) {
02928             need = sizeof("(none)") - 1;
02929             if (((*vallenp) + need) >= (*allocedp)) {
02930                 if ((*allocedp) <= need)
02931                     (*allocedp) += need;
02932                 (*allocedp) <<= 1;
02933                 (*valp) = xrealloc((*valp), (*allocedp)+1);     
02934             }
02935             t = (*valp) + (*vallenp);
02936             te = stpcpy(t, "(none)");
02937             (*vallenp) += (te - t);
02938         } else {
02939             need = numElements * token->u.array.numTokens * 10;
02940             if (need <= 0) break;
02941             if (((*vallenp) + need) >= (*allocedp)) {
02942                 if ((*allocedp) <= need)
02943                     (*allocedp) += need;
02944                 (*allocedp) <<= 1;
02945                 (*valp) = xrealloc((*valp), (*allocedp)+1);     
02946             }
02947 
02948             t = (*valp) + (*vallenp);
02949             for (j = 0; j < numElements; j++) {
02950                 for (i = 0; i < token->u.array.numTokens; i++)
02951                     te = singleSprintf(h, token->u.array.format + i, 
02952                                         extensions, extCache, j,
02953                                         valp, vallenp, allocedp);
02954             }
02955         }
02956         break;
02957     }
02958 
02959     return ((*valp) + (*vallenp));
02960 }
02961 
02964 static /*@only@*/ extensionCache
02965 allocateExtensionCache(const headerSprintfExtension extensions)
02966         /*@*/
02967 {
02968     headerSprintfExtension ext = extensions;
02969     int i = 0;
02970 
02971     while (ext->type != HEADER_EXT_LAST) {
02972         i++;
02973         if (ext->type == HEADER_EXT_MORE)
02974             ext = ext->u.more;
02975         else
02976             ext++;
02977     }
02978 
02979     /*@-sizeoftype@*/
02980     return xcalloc(i, sizeof(struct extensionCache));
02981     /*@=sizeoftype@*/
02982 }
02983 
02987 static /*@null@*/ extensionCache
02988 freeExtensionCache(const headerSprintfExtension extensions,
02989                         /*@only@*/ extensionCache cache)
02990         /*@*/
02991 {
02992     headerSprintfExtension ext = extensions;
02993     int i = 0;
02994 
02995     while (ext->type != HEADER_EXT_LAST) {
02996         if (cache[i].freeit) cache[i].data = _free(cache[i].data);
02997 
02998         i++;
02999         if (ext->type == HEADER_EXT_MORE)
03000             ext = ext->u.more;
03001         else
03002             ext++;
03003     }
03004 
03005     cache = _free(cache);
03006     return NULL;
03007 }
03008 
03020 static /*@only@*/ /*@null@*/
03021 char * headerSprintf(Header h, const char * fmt,
03022                      const struct headerTagTableEntry_s * tbltags,
03023                      const struct headerSprintfExtension_s * extensions,
03024                      /*@null@*/ /*@out@*/ errmsg_t * errmsg)
03025         /*@modifies *errmsg @*/
03026 {
03027     /*@-castexpose@*/   /* FIX: legacy API shouldn't change. */
03028     headerSprintfExtension exts = (headerSprintfExtension) extensions;
03029     headerTagTableEntry tags = (headerTagTableEntry) tbltags;
03030     /*@=castexpose@*/
03031     char * t;
03032     char * fmtString;
03033     sprintfToken format;
03034     int numTokens;
03035     char * val = NULL;
03036     int vallen = 0;
03037     int alloced = 0;
03038     int i;
03039     extensionCache extCache;
03040  
03041     /*fmtString = escapeString(fmt);*/
03042     fmtString = xstrdup(fmt);
03043    
03044     if (parseFormat(fmtString, tags, exts, &format, &numTokens, 
03045                     NULL, PARSER_BEGIN, errmsg)) {
03046         fmtString = _free(fmtString);
03047         return NULL;
03048     }
03049 
03050     extCache = allocateExtensionCache(exts);
03051 
03052     val = xstrdup("");
03053     for (i = 0; i < numTokens; i++) {
03054         /*@-mods@*/
03055         t = singleSprintf(h, format + i, exts, extCache, 0,
03056                 &val, &vallen, &alloced);
03057         /*@=mods@*/
03058     }
03059 
03060     if (val != NULL && vallen < alloced)
03061         val = xrealloc(val, vallen+1);  
03062 
03063     fmtString = _free(fmtString);
03064     extCache = freeExtensionCache(exts, extCache);
03065     format = _free(format);
03066 
03067     return val;
03068 }
03069 
03072 static char * octalFormat(int_32 type, hPTR_t data, 
03073                 char * formatPrefix, int padding, /*@unused@*/int element)
03074         /*@modifies formatPrefix @*/
03075 {
03076     char * val;
03077 
03078     if (type != RPM_INT32_TYPE) {
03079         val = xstrdup(_("(not a number)"));
03080     } else {
03081         val = xmalloc(20 + padding);
03082         strcat(formatPrefix, "o");
03083         /*@-formatconst@*/
03084         sprintf(val, formatPrefix, *((int_32 *) data));
03085         /*@=formatconst@*/
03086     }
03087 
03088     return val;
03089 }
03090 
03093 static char * hexFormat(int_32 type, hPTR_t data, 
03094                 char * formatPrefix, int padding, /*@unused@*/int element)
03095         /*@modifies formatPrefix @*/
03096 {
03097     char * val;
03098 
03099     if (type != RPM_INT32_TYPE) {
03100         val = xstrdup(_("(not a number)"));
03101     } else {
03102         val = xmalloc(20 + padding);
03103         strcat(formatPrefix, "x");
03104         /*@-formatconst@*/
03105         sprintf(val, formatPrefix, *((int_32 *) data));
03106         /*@=formatconst@*/
03107     }
03108 
03109     return val;
03110 }
03111 
03114 static char * realDateFormat(int_32 type, hPTR_t data, 
03115                 char * formatPrefix, int padding, /*@unused@*/int element,
03116                 const char * strftimeFormat)
03117         /*@modifies formatPrefix @*/
03118 {
03119     char * val;
03120 
03121     if (type != RPM_INT32_TYPE) {
03122         val = xstrdup(_("(not a number)"));
03123     } else {
03124         struct tm * tstruct;
03125         char buf[50];
03126 
03127         val = xmalloc(50 + padding);
03128         strcat(formatPrefix, "s");
03129 
03130         /* this is important if sizeof(int_32) ! sizeof(time_t) */
03131         {   time_t dateint = *((int_32 *) data);
03132             tstruct = localtime(&dateint);
03133         }
03134         buf[0] = '\0';
03135         if (tstruct)
03136             (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
03137         /*@-formatconst@*/
03138         sprintf(val, formatPrefix, buf);
03139         /*@=formatconst@*/
03140     }
03141 
03142     return val;
03143 }
03144 
03147 static char * dateFormat(int_32 type, hPTR_t data, 
03148                          char * formatPrefix, int padding, int element)
03149         /*@modifies formatPrefix @*/
03150 {
03151     return realDateFormat(type, data, formatPrefix, padding, element, "%c");
03152 }
03153 
03156 static char * dayFormat(int_32 type, hPTR_t data, 
03157                          char * formatPrefix, int padding, int element)
03158         /*@modifies formatPrefix @*/
03159 {
03160     return realDateFormat(type, data, formatPrefix, padding, element, 
03161                           "%a %b %d %Y");
03162 }
03163 
03166 static char * shescapeFormat(int_32 type, hPTR_t data, 
03167                 char * formatPrefix, int padding, /*@unused@*/int element)
03168         /*@modifies formatPrefix @*/
03169 {
03170     char * result, * dst, * src, * buf;
03171 
03172     if (type == RPM_INT32_TYPE) {
03173         result = xmalloc(padding + 20);
03174         strcat(formatPrefix, "d");
03175         /*@-formatconst@*/
03176         sprintf(result, formatPrefix, *((int_32 *) data));
03177         /*@=formatconst@*/
03178     } else {
03179         buf = alloca(strlen(data) + padding + 2);
03180         strcat(formatPrefix, "s");
03181         /*@-formatconst@*/
03182         sprintf(buf, formatPrefix, data);
03183         /*@=formatconst@*/
03184 
03185         result = dst = xmalloc(strlen(buf) * 4 + 3);
03186         *dst++ = '\'';
03187         for (src = buf; *src != '\0'; src++) {
03188             if (*src == '\'') {
03189                 *dst++ = '\'';
03190                 *dst++ = '\\';
03191                 *dst++ = '\'';
03192                 *dst++ = '\'';
03193             } else {
03194                 *dst++ = *src;
03195             }
03196         }
03197         *dst++ = '\'';
03198         *dst = '\0';
03199 
03200     }
03201 
03202     return result;
03203 }
03204 
03205 /*@-type@*/ /* FIX: cast? */
03206 const struct headerSprintfExtension_s headerDefaultFormats[] = {
03207     { HEADER_EXT_FORMAT, "octal", { octalFormat } },
03208     { HEADER_EXT_FORMAT, "hex", { hexFormat } },
03209     { HEADER_EXT_FORMAT, "date", { dateFormat } },
03210     { HEADER_EXT_FORMAT, "day", { dayFormat } },
03211     { HEADER_EXT_FORMAT, "shescape", { shescapeFormat } },
03212     { HEADER_EXT_LAST, NULL, { NULL } }
03213 };
03214 /*@=type@*/
03215 
03222 static
03223 void headerCopyTags(Header headerFrom, Header headerTo, hTAG_t tagstocopy)
03224         /*@modifies headerTo @*/
03225 {
03226     int * p;
03227 
03228     if (headerFrom == headerTo)
03229         return;
03230 
03231     for (p = tagstocopy; *p != 0; p++) {
03232         char *s;
03233         int_32 type;
03234         int_32 count;
03235         if (headerIsEntry(headerTo, *p))
03236             continue;
03237         if (!headerGetEntryMinMemory(headerFrom, *p, &type,
03238                                 (hPTR_t *) &s, &count))
03239             continue;
03240         (void) headerAddEntry(headerTo, *p, type, s, count);
03241         s = headerFreeData(s, type);
03242     }
03243 }
03244 
03248 struct headerIteratorS {
03249 /*@unused@*/ Header h;          
03250 /*@unused@*/ int next_index;    
03251 };
03252 
03258 static /*@null@*/
03259 HeaderIterator headerFreeIterator(/*@only@*/ HeaderIterator hi)
03260         /*@modifies hi @*/
03261 {
03262     hi->h = headerFree(hi->h);
03263     hi = _free(hi);
03264     return hi;
03265 }
03266 
03272 static
03273 HeaderIterator headerInitIterator(Header h)
03274         /*@modifies h */
03275 {
03276     HeaderIterator hi = xmalloc(sizeof(*hi));
03277 
03278     headerSort(h);
03279 
03280     hi->h = headerLink(h);
03281     hi->next_index = 0;
03282     return hi;
03283 }
03284 
03294 static
03295 int headerNextIterator(HeaderIterator hi,
03296                 /*@null@*/ /*@out@*/ hTAG_t tag,
03297                 /*@null@*/ /*@out@*/ hTYP_t type,
03298                 /*@null@*/ /*@out@*/ hPTR_t * p,
03299                 /*@null@*/ /*@out@*/ hCNT_t c)
03300         /*@modifies hi, *tag, *type, *p, *c @*/
03301 {
03302     Header h = hi->h;
03303     int slot = hi->next_index;
03304     indexEntry entry = NULL;
03305     int rc;
03306 
03307     for (slot = hi->next_index; slot < h->indexUsed; slot++) {
03308         entry = h->index + slot;
03309         if (!ENTRY_IS_REGION(entry))
03310             break;
03311     }
03312     hi->next_index = slot;
03313     if (entry == NULL || slot >= h->indexUsed)
03314         return 0;
03315     /*@-noeffect@*/     /* LCL: no clue */
03316     hi->next_index++;
03317     /*@=noeffect@*/
03318 
03319     if (tag)
03320         *tag = entry->info.tag;
03321 
03322     rc = copyEntry(entry, type, p, c, 0);
03323 
03324     /* XXX 1 on success */
03325     return ((rc == 1) ? 1 : 0);
03326 }
03327 
03333 static /*@null@*/
03334 Header headerCopy(Header h)
03335         /*@modifies h @*/
03336 {
03337     Header nh = headerNew();
03338     HeaderIterator hi;
03339     int_32 tag, type, count;
03340     hPTR_t ptr;
03341    
03342     /*@-branchstate@*/
03343     for (hi = headerInitIterator(h);
03344         headerNextIterator(hi, &tag, &type, &ptr, &count);
03345         ptr = headerFreeData((void *)ptr, type))
03346     {
03347         if (ptr) (void) headerAddEntry(nh, tag, type, ptr, count);
03348     }
03349     hi = headerFreeIterator(hi);
03350     /*@=branchstate@*/
03351 
03352     return headerReload(nh, HEADER_IMAGE);
03353 }
03354 
03355 /*@observer@*/ /*@unchecked@*/
03356 static struct HV_s hdrVec1 = {
03357     headerNew,
03358     headerFree,
03359     headerLink,
03360     headerSort,
03361     headerUnsort,
03362     headerSizeof,
03363     headerUnload,
03364     headerReload,
03365     headerCopy,
03366     headerLoad,
03367     headerCopyLoad,
03368     headerRead,
03369     headerWrite,
03370     headerIsEntry,
03371     headerFreeTag,
03372     headerGetEntry,
03373     headerGetEntryMinMemory,
03374     headerAddEntry,
03375     headerAppendEntry,
03376     headerAddOrAppendEntry,
03377     headerAddI18NString,
03378     headerModifyEntry,
03379     headerRemoveEntry,
03380     headerSprintf,
03381     headerCopyTags,
03382     headerFreeIterator,
03383     headerInitIterator,
03384     headerNextIterator,
03385     headerUnlink,
03386     NULL, NULL,
03387     1
03388 };
03389 
03390 /*@-compmempass -redef@*/
03391 /*@observer@*/ /*@unchecked@*/
03392 HV_t hdrVec = &hdrVec1;
03393 /*@=compmempass =redef@*/

Generated on Sun Feb 2 23:32:01 2003 for rpm by doxygen1.2.18