~ubuntu-branches/ubuntu/edgy/rpm/edgy

« back to all changes in this revision

Viewing changes to lib/header.c

  • Committer: Bazaar Package Importer
  • Author(s): Joey Hess
  • Date: 2002-01-22 20:56:57 UTC
  • Revision ID: james.westby@ubuntu.com-20020122205657-l74j50mr9z8ofcl5
Tags: upstream-4.0.3
ImportĀ upstreamĀ versionĀ 4.0.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** \ingroup header
 
2
 * \file lib/header.c
 
3
 */
 
4
 
 
5
/* RPM - Copyright (C) 1995-2000 Red Hat Software */
 
6
 
 
7
/* Data written to file descriptors is in network byte order.    */
 
8
/* Data read from file descriptors is expected to be in          */
 
9
/* network byte order and is converted on the fly to host order. */
 
10
 
 
11
#include "system.h"
 
12
 
 
13
#define __HEADER_PROTOTYPES__
 
14
 
 
15
#include <header_internal.h>
 
16
 
 
17
#include "debug.h"
 
18
 
 
19
/*@-redecl@*/   /* FIX: avoid rpmlib.h, need for debugging. */
 
20
/*@observer@*/ const char *const tagName(int tag)       /*@*/;
 
21
/*@=redecl@*/
 
22
 
 
23
/*@access entryInfo @*/
 
24
/*@access indexEntry @*/
 
25
 
 
26
/*@access extensionCache @*/
 
27
/*@access sprintfTag @*/
 
28
/*@access sprintfToken @*/
 
29
/*@access HV_t @*/
 
30
 
 
31
#define PARSER_BEGIN    0
 
32
#define PARSER_IN_ARRAY 1
 
33
#define PARSER_IN_EXPR  2
 
34
 
 
35
/** \ingroup header
 
36
 */
 
37
static unsigned char header_magic[8] = {
 
38
        0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
 
39
};
 
40
 
 
41
/** \ingroup header
 
42
 * Maximum no. of bytes permitted in a header.
 
43
 */
 
44
static size_t headerMaxbytes = (32*1024*1024);
 
45
 
 
46
/**
 
47
 * Sanity check on no. of tags.
 
48
 * This check imposes a limit of 65K tags, more than enough.
 
49
 */ 
 
50
#define hdrchkTags(_ntags)      ((_ntags) & 0xffff0000)
 
51
 
 
52
/**
 
53
 * Sanity check on data size and/or offset.
 
54
 * This check imposes a limit of 16Mb, more than enough.
 
55
 */ 
 
56
#define hdrchkData(_nbytes)     ((_nbytes) & 0xff000000)
 
57
 
 
58
/** \ingroup header
 
59
 * Alignment needs (and sizeof scalars types) for internal rpm data types.
 
60
 */
 
61
static int typeSizes[] =  { 
 
62
        0,      /*!< RPM_NULL_TYPE */
 
63
        1,      /*!< RPM_CHAR_TYPE */
 
64
        1,      /*!< RPM_INT8_TYPE */
 
65
        2,      /*!< RPM_INT16_TYPE */
 
66
        4,      /*!< RPM_INT32_TYPE */
 
67
        -1,     /*!< RPM_INT64_TYPE */
 
68
        -1,     /*!< RPM_STRING_TYPE */
 
69
        1,      /*!< RPM_BIN_TYPE */
 
70
        -1,     /*!< RPM_STRING_ARRAY_TYPE */
 
71
        -1      /*!< RPM_I18NSTRING_TYPE */
 
72
};
 
73
 
 
74
HV_t hdrVec;    /* forward reference */
 
75
 
 
76
/**
 
77
 * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
 
78
 * @param p             memory to free
 
79
 * @return              NULL always
 
80
 */
 
81
/*@unused@*/ static inline /*@null@*/ void *
 
82
_free(/*@only@*/ /*@null@*/ const void * p) /*@modifies *p @*/
 
83
{
 
84
    if (p != NULL)      free((void *)p);
 
85
    return NULL;
 
86
}
 
87
 
 
88
Header headerNew()
 
89
{
 
90
    Header h = xcalloc(1, sizeof(*h));
 
91
 
 
92
    /*@-assignexpose@*/
 
93
    h->hv = *hdrVec;            /* structure assignment */
 
94
    /*@=assignexpose@*/
 
95
    h->blob = NULL;
 
96
    h->indexAlloced = INDEX_MALLOC_SIZE;
 
97
    h->indexUsed = 0;
 
98
    h->flags = HEADERFLAG_SORTED;
 
99
    h->nrefs = 1;
 
100
 
 
101
    h->index = (h->indexAlloced
 
102
        ? xcalloc(h->indexAlloced, sizeof(*h->index))
 
103
        : NULL);
 
104
 
 
105
    /*@-globstate@*/
 
106
    return h;
 
107
    /*@=globstate@*/
 
108
}
 
109
 
 
110
Header headerFree(Header h)
 
111
{
 
112
    if (h == NULL || --h->nrefs > 0)
 
113
        return NULL;    /* XXX return previous header? */
 
114
 
 
115
    if (h->index) {
 
116
        indexEntry entry = h->index;
 
117
        int i;
 
118
        for (i = 0; i < h->indexUsed; i++, entry++) {
 
119
            if ((h->flags & HEADERFLAG_ALLOCATED) && ENTRY_IS_REGION(entry)) {
 
120
                if (entry->length > 0) {
 
121
                    int_32 * ei = entry->data;
 
122
                    if ((ei - 2) == h->blob) h->blob = _free(h->blob);
 
123
                    entry->data = NULL;
 
124
                }
 
125
            } else if (!ENTRY_IN_REGION(entry)) {
 
126
                entry->data = _free(entry->data);
 
127
            }
 
128
            entry->data = NULL;
 
129
        }
 
130
        h->index = _free(h->index);
 
131
    }
 
132
 
 
133
    /*@-refcounttrans@*/ h = _free(h); /*@=refcounttrans@*/
 
134
    return h;
 
135
}
 
136
 
 
137
Header headerLink(Header h)
 
138
{
 
139
    h->nrefs++;
 
140
    /*@-refcounttrans@*/ return h; /*@=refcounttrans@*/
 
141
}
 
142
 
 
143
/**
 
144
 */
 
145
static int indexCmp(const void * avp, const void * bvp) /*@*/
 
146
{
 
147
    /*@-castexpose@*/
 
148
    indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
 
149
    /*@=castexpose@*/
 
150
    return (ap->info.tag - bp->info.tag);
 
151
}
 
152
 
 
153
void headerSort(Header h)
 
154
{
 
155
    if (!(h->flags & HEADERFLAG_SORTED)) {
 
156
        qsort(h->index, h->indexUsed, sizeof(*h->index), indexCmp);
 
157
        h->flags |= HEADERFLAG_SORTED;
 
158
    }
 
159
}
 
160
 
 
161
/**
 
162
 */
 
163
static int offsetCmp(const void * avp, const void * bvp) /*@*/
 
164
{
 
165
    /*@-castexpose@*/
 
166
    indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
 
167
    /*@=castexpose@*/
 
168
    int rc = (ap->info.offset - bp->info.offset);
 
169
 
 
170
    if (rc == 0) {
 
171
        /* Within a region, entries sort by address. Added drips sort by tag. */
 
172
        if (ap->info.offset < 0)
 
173
            rc = (((char *)ap->data) - ((char *)bp->data));
 
174
        else
 
175
            rc = (ap->info.tag - bp->info.tag);
 
176
    }
 
177
    return rc;
 
178
}
 
179
 
 
180
void headerUnsort(Header h)
 
181
{
 
182
    qsort(h->index, h->indexUsed, sizeof(*h->index), offsetCmp);
 
183
}
 
184
 
 
185
unsigned int headerSizeof(Header h, enum hMagic magicp)
 
186
{
 
187
    indexEntry entry;
 
188
    unsigned int size = 0;
 
189
    unsigned int pad = 0;
 
190
    int i;
 
191
 
 
192
    if (h == NULL)
 
193
        return size;
 
194
 
 
195
    headerSort(h);
 
196
 
 
197
    switch (magicp) {
 
198
    case HEADER_MAGIC_YES:
 
199
        size += sizeof(header_magic);
 
200
        break;
 
201
    case HEADER_MAGIC_NO:
 
202
        break;
 
203
    }
 
204
 
 
205
    size += 2 * sizeof(int_32); /* count of index entries */
 
206
 
 
207
    for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
 
208
        unsigned diff;
 
209
        int_32 type;
 
210
 
 
211
        /* Regions go in as is ... */
 
212
        if (ENTRY_IS_REGION(entry)) {
 
213
            size += entry->length;
 
214
            /* XXX Legacy regions do not include the region tag and data. */
 
215
            if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
 
216
                size += sizeof(struct entryInfo) + entry->info.count;
 
217
            continue;
 
218
        }
 
219
 
 
220
        /* ... and region elements are skipped. */
 
221
        if (entry->info.offset < 0)
 
222
            continue;
 
223
 
 
224
        /* Alignment */
 
225
        type = entry->info.type;
 
226
        if (typeSizes[type] > 1) {
 
227
            diff = typeSizes[type] - (size % typeSizes[type]);
 
228
            if (diff != typeSizes[type]) {
 
229
                size += diff;
 
230
                pad += diff;
 
231
            }
 
232
        }
 
233
 
 
234
        size += sizeof(struct entryInfo) + entry->length;
 
235
    }
 
236
 
 
237
    return size;
 
238
}
 
239
 
 
240
/**
 
241
 * Return length of entry data.
 
242
 * @param type          entry data type
 
243
 * @param p             entry data
 
244
 * @param count         entry item count
 
245
 * @param onDisk        data is concatenated strings (with NUL's))?
 
246
 * @return              no. bytes in data
 
247
 */
 
248
/*@mayexit@*/
 
249
static int dataLength(int_32 type, hPTR_t p, int_32 count, int onDisk)
 
250
        /*@modifies fileSystem @*/
 
251
{
 
252
    int length = 0;
 
253
 
 
254
    switch (type) {
 
255
    case RPM_STRING_TYPE:
 
256
        if (count == 1) {       /* Special case -- p is just the string */
 
257
            length = strlen(p) + 1;
 
258
            break;
 
259
        }
 
260
        /* This should not be allowed */
 
261
        fprintf(stderr, _("dataLength() RPM_STRING_TYPE count must be 1.\n"));
 
262
        exit(EXIT_FAILURE);
 
263
        /*@notreached@*/ break;
 
264
 
 
265
    case RPM_STRING_ARRAY_TYPE:
 
266
    case RPM_I18NSTRING_TYPE:
 
267
    {   int i;
 
268
 
 
269
        /* This is like RPM_STRING_TYPE, except it's *always* an array */
 
270
        /* Compute sum of length of all strings, including null terminators */
 
271
        i = count;
 
272
 
 
273
        if (onDisk) {
 
274
            const char * chptr = p;
 
275
            int thisLen;
 
276
 
 
277
            while (i--) {
 
278
                thisLen = strlen(chptr) + 1;
 
279
                length += thisLen;
 
280
                chptr += thisLen;
 
281
            }
 
282
        } else {
 
283
            const char ** src = (const char **)p;
 
284
            while (i--) {
 
285
                /* add one for null termination */
 
286
                length += strlen(*src++) + 1;
 
287
            }
 
288
        }
 
289
    }   break;
 
290
 
 
291
    default:
 
292
        if (typeSizes[type] != -1) {
 
293
            length = typeSizes[type] * count;
 
294
            break;
 
295
        }
 
296
        fprintf(stderr, _("Data type %d not supported\n"), (int) type);
 
297
        exit(EXIT_FAILURE);
 
298
        /*@notreached@*/ break;
 
299
    }
 
300
 
 
301
    return length;
 
302
}
 
303
 
 
304
/** \ingroup header
 
305
 * Swap int_32 and int_16 arrays within header region.
 
306
 *
 
307
 * This code is way more twisty than I would like.
 
308
 *
 
309
 * A bug with RPM_I18NSTRING_TYPE in rpm-2.5.x (fixed in August 1998)
 
310
 * causes the offset and length of elements in a header region to disagree
 
311
 * regarding the total length of the region data.
 
312
 *
 
313
 * The "fix" is to compute the size using both offset and length and
 
314
 * return the larger of the two numbers as the size of the region.
 
315
 * Kinda like computing left and right Riemann sums of the data elements
 
316
 * to determine the size of a data structure, go figger :-).
 
317
 *
 
318
 * There's one other twist if a header region tag is in the set to be swabbed,
 
319
 * as the data for a header region is located after all other tag data.
 
320
 *
 
321
 * @param entry         header entry
 
322
 * @param il            no. of entries
 
323
 * @param dl            start no. bytes of data
 
324
 * @param pe            header physical entry pointer (swapped)
 
325
 * @param dataStart     header data
 
326
 * @param regionid      region offset
 
327
 * @return              no. bytes of data in region, -1 on error
 
328
 */
 
329
static int regionSwab(/*@null@*/ indexEntry entry, int il, int dl,
 
330
                entryInfo pe, char * dataStart, int regionid)
 
331
        /*@modifies *entry, *dataStart @*/
 
332
{
 
333
    char * tprev = NULL;
 
334
    char * t = NULL;
 
335
    int tdel, tl = dl;
 
336
    struct indexEntry ieprev;
 
337
 
 
338
    memset(&ieprev, 0, sizeof(ieprev));
 
339
    for (; il > 0; il--, pe++) {
 
340
        struct indexEntry ie;
 
341
        int_32 type;
 
342
 
 
343
        ie.info.tag = ntohl(pe->tag);
 
344
        ie.info.type = ntohl(pe->type);
 
345
        if (ie.info.type < RPM_MIN_TYPE || ie.info.type > RPM_MAX_TYPE)
 
346
            return -1;
 
347
        ie.info.count = ntohl(pe->count);
 
348
        ie.info.offset = ntohl(pe->offset);
 
349
        ie.data = t = dataStart + ie.info.offset;
 
350
        ie.length = dataLength(ie.info.type, ie.data, ie.info.count, 1);
 
351
        ie.rdlen = 0;
 
352
 
 
353
        if (entry) {
 
354
            ie.info.offset = regionid;
 
355
            *entry = ie;        /* structure assignment */
 
356
            entry++;
 
357
        }
 
358
 
 
359
        /* Alignment */
 
360
        type = ie.info.type;
 
361
        if (typeSizes[type] > 1) {
 
362
            unsigned diff;
 
363
            diff = typeSizes[type] - (dl % typeSizes[type]);
 
364
            if (diff != typeSizes[type]) {
 
365
                dl += diff;
 
366
                if (ieprev.info.type == RPM_I18NSTRING_TYPE)
 
367
                    ieprev.length += diff;
 
368
            }
 
369
        }
 
370
        tdel = (tprev ? (t - tprev) : 0);
 
371
        if (ieprev.info.type == RPM_I18NSTRING_TYPE)
 
372
            tdel = ieprev.length;
 
373
 
 
374
        if (ie.info.tag >= HEADER_I18NTABLE) {
 
375
            tprev = t;
 
376
        } else {
 
377
            tprev = dataStart;
 
378
            /* XXX HEADER_IMAGE tags don't include region sub-tag. */
 
379
            if (ie.info.tag != HEADER_IMMUTABLE)
 
380
                tprev -= REGION_TAG_COUNT;
 
381
        }
 
382
 
 
383
        /* Perform endian conversions */
 
384
        switch (ntohl(pe->type)) {
 
385
        case RPM_INT32_TYPE:
 
386
        {   int_32 * it = (int_32 *)t;
 
387
            for (; ie.info.count > 0; ie.info.count--, it += 1)
 
388
                *it = htonl(*it);
 
389
            t = (char *) it;
 
390
        }   break;
 
391
        case RPM_INT16_TYPE:
 
392
        {   int_16 * it = (int_16 *) t;
 
393
            for (; ie.info.count > 0; ie.info.count--, it += 1)
 
394
                *it = htons(*it);
 
395
            t = (char *) it;
 
396
        }   break;
 
397
        default:
 
398
            t += ie.length;
 
399
            break;
 
400
        }
 
401
 
 
402
        dl += ie.length;
 
403
        tl += tdel;
 
404
        ieprev = ie;    /* structure assignment */
 
405
 
 
406
    }
 
407
    tdel = (tprev ? (t - tprev) : 0);
 
408
    tl += tdel;
 
409
 
 
410
    /* XXX
 
411
     * There are two hacks here:
 
412
     *  1) tl is 16b (i.e. REGION_TAG_COUNT) short while doing headerReload().
 
413
     *  2) the 8/98 rpm bug with inserting i18n tags needs to use tl, not dl.
 
414
     */
 
415
    if (tl+REGION_TAG_COUNT == dl)
 
416
        tl += REGION_TAG_COUNT;
 
417
 
 
418
    return dl;
 
419
}
 
420
 
 
421
#if 0
 
422
int headerDrips(const Header h)
 
423
{
 
424
    indexEntry entry; 
 
425
    int i;
 
426
 
 
427
    for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
 
428
        if (ENTRY_IS_REGION(entry)) {
 
429
            int rid = entry->info.offset;
 
430
 
 
431
            for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
 
432
                if (entry->info.offset <= rid)
 
433
                    continue;
 
434
            }
 
435
            i--;
 
436
            entry--;
 
437
            continue;
 
438
        }
 
439
 
 
440
        /* Ignore deleted drips. */
 
441
        if (entry->data == NULL || entry->length <= 0)
 
442
            continue;
 
443
    }
 
444
    return 0;
 
445
}
 
446
#endif
 
447
 
 
448
/** \ingroup header
 
449
 */
 
450
static /*@only@*/ /*@null@*/ void * doHeaderUnload(Header h,
 
451
                /*@out@*/ int * lengthPtr)
 
452
        /*@modifies h, *lengthPtr @*/
 
453
{
 
454
    int_32 * ei = NULL;
 
455
    entryInfo pe;
 
456
    char * dataStart;
 
457
    char * te;
 
458
    unsigned pad;
 
459
    unsigned len;
 
460
    int_32 il = 0;
 
461
    int_32 dl = 0;
 
462
    indexEntry entry; 
 
463
    int_32 type;
 
464
    int i;
 
465
    int drlen, ndribbles;
 
466
    int driplen, ndrips;
 
467
    int legacy = 0;
 
468
 
 
469
    /* Sort entries by (offset,tag). */
 
470
    headerUnsort(h);
 
471
 
 
472
    /* Compute (il,dl) for all tags, including those deleted in region. */
 
473
    pad = 0;
 
474
    drlen = ndribbles = driplen = ndrips = 0;
 
475
    for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
 
476
        if (ENTRY_IS_REGION(entry)) {
 
477
            int_32 rdl = -entry->info.offset;   /* negative offset */
 
478
            int_32 ril = rdl/sizeof(*pe);
 
479
            int rid = entry->info.offset;
 
480
 
 
481
            il += ril;
 
482
            dl += entry->rdlen + entry->info.count;
 
483
            /* XXX Legacy regions do not include the region tag and data. */
 
484
            if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
 
485
                il += 1;
 
486
 
 
487
            /* Skip rest of entries in region, but account for dribbles. */
 
488
            for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
 
489
                if (entry->info.offset <= rid)
 
490
                    continue;
 
491
 
 
492
                /* Alignment */
 
493
                type = entry->info.type;
 
494
                if (typeSizes[type] > 1) {
 
495
                    unsigned diff;
 
496
                    diff = typeSizes[type] - (dl % typeSizes[type]);
 
497
                    if (diff != typeSizes[type]) {
 
498
                        drlen += diff;
 
499
                        pad += diff;
 
500
                        dl += diff;
 
501
                    }
 
502
                }
 
503
 
 
504
                ndribbles++;
 
505
                il++;
 
506
                drlen += entry->length;
 
507
                dl += entry->length;
 
508
            }
 
509
            i--;
 
510
            entry--;
 
511
            continue;
 
512
        }
 
513
 
 
514
        /* Ignore deleted drips. */
 
515
        if (entry->data == NULL || entry->length <= 0)
 
516
            continue;
 
517
 
 
518
        /* Alignment */
 
519
        type = entry->info.type;
 
520
        if (typeSizes[type] > 1) {
 
521
            unsigned diff;
 
522
            diff = typeSizes[type] - (dl % typeSizes[type]);
 
523
            if (diff != typeSizes[type]) {
 
524
                driplen += diff;
 
525
                pad += diff;
 
526
                dl += diff;
 
527
            } else
 
528
                diff = 0;
 
529
        }
 
530
 
 
531
        ndrips++;
 
532
        il++;
 
533
        driplen += entry->length;
 
534
        dl += entry->length;
 
535
    }
 
536
 
 
537
    /* Sanity checks on header intro. */
 
538
    if (hdrchkTags(il) || hdrchkData(dl))
 
539
        goto errxit;
 
540
 
 
541
    len = sizeof(il) + sizeof(dl) + (il * sizeof(*pe)) + dl;
 
542
 
 
543
    ei = xmalloc(len);
 
544
    ei[0] = htonl(il);
 
545
    ei[1] = htonl(dl);
 
546
 
 
547
    pe = (entryInfo) &ei[2];
 
548
    dataStart = te = (char *) (pe + il);
 
549
 
 
550
    pad = 0;
 
551
    for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
 
552
        const char * src;
 
553
char *t;
 
554
        int count;
 
555
        int rdlen;
 
556
 
 
557
        if (entry->data == NULL || entry->length <= 0)
 
558
            continue;
 
559
 
 
560
t = te;
 
561
        pe->tag = htonl(entry->info.tag);
 
562
        pe->type = htonl(entry->info.type);
 
563
        pe->count = htonl(entry->info.count);
 
564
 
 
565
        if (ENTRY_IS_REGION(entry)) {
 
566
            int_32 rdl = -entry->info.offset;   /* negative offset */
 
567
            int_32 ril = rdl/sizeof(*pe) + ndribbles;
 
568
            int rid = entry->info.offset;
 
569
 
 
570
            src = (char *)entry->data;
 
571
            rdlen = entry->rdlen;
 
572
 
 
573
            /* XXX Legacy regions do not include the region tag and data. */
 
574
            if (i == 0 && (h->flags & HEADERFLAG_LEGACY)) {
 
575
                int_32 stei[4];
 
576
 
 
577
                legacy = 1;
 
578
                memcpy(pe+1, src, rdl);
 
579
                memcpy(te, src + rdl, rdlen);
 
580
                te += rdlen;
 
581
 
 
582
                pe->offset = htonl(te - dataStart);
 
583
                stei[0] = pe->tag;
 
584
                stei[1] = pe->type;
 
585
                stei[2] = htonl(-rdl-entry->info.count);
 
586
                stei[3] = pe->count;
 
587
                memcpy(te, stei, entry->info.count);
 
588
                te += entry->info.count;
 
589
                ril++;
 
590
                rdlen += entry->info.count;
 
591
 
 
592
                count = regionSwab(NULL, ril, 0, pe, t, 0);
 
593
                if (count != rdlen)
 
594
                    goto errxit;
 
595
 
 
596
            } else {
 
597
 
 
598
                memcpy(pe+1, src + sizeof(*pe), ((ril-1) * sizeof(*pe)));
 
599
                memcpy(te, src + (ril * sizeof(*pe)), rdlen+entry->info.count+drlen);
 
600
                te += rdlen;
 
601
                {   /*@-castexpose@*/
 
602
                    entryInfo se = (entryInfo)src;
 
603
                    /*@=castexpose@*/
 
604
                    int off = ntohl(se->offset);
 
605
                    pe->offset = (off) ? htonl(te - dataStart) : htonl(off);
 
606
                }
 
607
                te += entry->info.count + drlen;
 
608
 
 
609
                count = regionSwab(NULL, ril, 0, pe, t, 0);
 
610
                if (count != (rdlen + entry->info.count + drlen))
 
611
                    goto errxit;
 
612
            }
 
613
 
 
614
            /* Skip rest of entries in region. */
 
615
            while (i < h->indexUsed && entry->info.offset <= rid+1) {
 
616
                i++;
 
617
                entry++;
 
618
            }
 
619
            i--;
 
620
            entry--;
 
621
            pe += ril;
 
622
            continue;
 
623
        }
 
624
 
 
625
        /* Ignore deleted drips. */
 
626
        if (entry->data == NULL || entry->length <= 0)
 
627
            continue;
 
628
 
 
629
        /* Alignment */
 
630
        type = entry->info.type;
 
631
        if (typeSizes[type] > 1) {
 
632
            unsigned diff;
 
633
            diff = typeSizes[type] - ((te - dataStart) % typeSizes[type]);
 
634
            if (diff != typeSizes[type]) {
 
635
                memset(te, 0, diff);
 
636
                te += diff;
 
637
                pad += diff;
 
638
            }
 
639
        }
 
640
 
 
641
        pe->offset = htonl(te - dataStart);
 
642
 
 
643
        /* copy data w/ endian conversions */
 
644
        switch (entry->info.type) {
 
645
        case RPM_INT32_TYPE:
 
646
            count = entry->info.count;
 
647
            src = entry->data;
 
648
            while (count--) {
 
649
                *((int_32 *)te) = htonl(*((int_32 *)src));
 
650
                te += sizeof(int_32);
 
651
                src += sizeof(int_32);
 
652
            }
 
653
            break;
 
654
 
 
655
        case RPM_INT16_TYPE:
 
656
            count = entry->info.count;
 
657
            src = entry->data;
 
658
            while (count--) {
 
659
                *((int_16 *)te) = htons(*((int_16 *)src));
 
660
                te += sizeof(int_16);
 
661
                src += sizeof(int_16);
 
662
            }
 
663
            break;
 
664
 
 
665
        default:
 
666
            memcpy(te, entry->data, entry->length);
 
667
            te += entry->length;
 
668
            break;
 
669
        }
 
670
        pe++;
 
671
    }
 
672
   
 
673
    /* Insure that there are no memcpy underruns/overruns. */
 
674
    if (((char *)pe) != dataStart)
 
675
        goto errxit;
 
676
    if ((((char *)ei)+len) != te)
 
677
        goto errxit;
 
678
 
 
679
    if (lengthPtr)
 
680
        *lengthPtr = len;
 
681
 
 
682
    h->flags &= ~HEADERFLAG_SORTED;
 
683
    headerSort(h);
 
684
 
 
685
    return (void *) ei;
 
686
 
 
687
errxit:
 
688
    /*@-usereleased@*/
 
689
    ei = _free(ei);
 
690
    /*@=usereleased@*/
 
691
    return (void *) ei;
 
692
}
 
693
 
 
694
void * headerUnload(Header h)
 
695
{
 
696
    int length;
 
697
    void * uh = doHeaderUnload(h, &length);
 
698
    return uh;
 
699
}
 
700
 
 
701
Header headerReload(Header h, int tag)
 
702
{
 
703
    Header nh;
 
704
    int length;
 
705
    /*@-onlytrans@*/
 
706
    void * uh = doHeaderUnload(h, &length);
 
707
 
 
708
    h = headerFree(h);
 
709
    /*@=onlytrans@*/
 
710
    if (uh == NULL)
 
711
        return NULL;
 
712
    nh = headerLoad(uh);
 
713
    if (nh == NULL) {
 
714
        uh = _free(uh);
 
715
        return NULL;
 
716
    }
 
717
    if (nh->flags & HEADERFLAG_ALLOCATED)
 
718
        uh = _free(uh);
 
719
    nh->flags |= HEADERFLAG_ALLOCATED;
 
720
    if (ENTRY_IS_REGION(nh->index)) {
 
721
        if (tag == HEADER_SIGNATURES || tag == HEADER_IMMUTABLE)
 
722
            nh->index[0].info.tag = tag;
 
723
    }
 
724
    return nh;
 
725
}
 
726
 
 
727
Header headerCopy(Header h)
 
728
{
 
729
    Header nh = headerNew();
 
730
    HeaderIterator hi;
 
731
    int_32 tag, type, count;
 
732
    hPTR_t ptr;
 
733
   
 
734
    for (hi = headerInitIterator(h);
 
735
        headerNextIterator(hi, &tag, &type, &ptr, &count);
 
736
        ptr = headerFreeData((void *)ptr, type))
 
737
    {
 
738
        if (ptr) (void) headerAddEntry(nh, tag, type, ptr, count);
 
739
    }
 
740
    hi = headerFreeIterator(hi);
 
741
 
 
742
    return headerReload(nh, HEADER_IMAGE);
 
743
}
 
744
 
 
745
Header headerLoad(void * uh)
 
746
{
 
747
    int_32 * ei = (int_32 *) uh;
 
748
    int_32 il = ntohl(ei[0]);           /* index length */
 
749
    int_32 dl = ntohl(ei[1]);           /* data length */
 
750
    size_t pvlen = sizeof(il) + sizeof(dl) +
 
751
               (il * sizeof(struct entryInfo)) + dl;
 
752
    void * pv = uh;
 
753
    Header h = NULL;
 
754
    entryInfo pe;
 
755
    char * dataStart;
 
756
    indexEntry entry; 
 
757
    int rdlen;
 
758
    int i;
 
759
 
 
760
    /* Sanity checks on header intro. */
 
761
    if (hdrchkTags(il) || hdrchkData(dl))
 
762
        goto errxit;
 
763
 
 
764
    ei = (int_32 *) pv;
 
765
    /*@-castexpose@*/
 
766
    pe = (entryInfo) &ei[2];
 
767
    /*@=castexpose@*/
 
768
    dataStart = (char *) (pe + il);
 
769
 
 
770
    h = xcalloc(1, sizeof(*h));
 
771
    /*@-assignexpose@*/
 
772
    h->hv = *hdrVec;            /* structure assignment */
 
773
    /*@=assignexpose@*/
 
774
    /*@-assignexpose -kepttrans@*/
 
775
    h->blob = uh;
 
776
    /*@=assignexpose =kepttrans@*/
 
777
    h->indexAlloced = il + 1;
 
778
    h->indexUsed = il;
 
779
    h->index = xcalloc(h->indexAlloced, sizeof(*h->index));
 
780
    h->flags = HEADERFLAG_SORTED;
 
781
    h->nrefs = 1;
 
782
 
 
783
    /*
 
784
     * XXX XFree86-libs, ash, and pdksh from Red Hat 5.2 have bogus
 
785
     * %verifyscript tag that needs to be diddled.
 
786
     */
 
787
    if (ntohl(pe->tag) == 15 &&
 
788
        ntohl(pe->type) == RPM_STRING_TYPE &&
 
789
        ntohl(pe->count) == 1)
 
790
    {
 
791
        pe->tag = htonl(1079);
 
792
    }
 
793
 
 
794
    entry = h->index;
 
795
    i = 0;
 
796
    if (!(htonl(pe->tag) < HEADER_I18NTABLE)) {
 
797
        h->flags |= HEADERFLAG_LEGACY;
 
798
        entry->info.type = REGION_TAG_TYPE;
 
799
        entry->info.tag = HEADER_IMAGE;
 
800
        entry->info.count = REGION_TAG_COUNT;
 
801
        entry->info.offset = ((char *)pe - dataStart); /* negative offset */
 
802
 
 
803
        /*@-assignexpose@*/
 
804
        entry->data = pe;
 
805
        /*@=assignexpose@*/
 
806
        entry->length = pvlen - sizeof(il) - sizeof(dl);
 
807
        rdlen = regionSwab(entry+1, il, 0, pe, dataStart, entry->info.offset);
 
808
#if 0   /* XXX don't check, the 8/98 i18n bug fails here. */
 
809
        if (rdlen != dl)
 
810
            goto errxit;
 
811
#endif
 
812
        entry->rdlen = rdlen;
 
813
        entry++;
 
814
        h->indexUsed++;
 
815
    } else {
 
816
        int nb = ntohl(pe->count);
 
817
        int_32 rdl;
 
818
        int_32 ril;
 
819
 
 
820
        h->flags &= ~HEADERFLAG_LEGACY;
 
821
 
 
822
        entry->info.type = htonl(pe->type);
 
823
        if (entry->info.type < RPM_MIN_TYPE || entry->info.type > RPM_MAX_TYPE)
 
824
            goto errxit;
 
825
        entry->info.count = htonl(pe->count);
 
826
 
 
827
        if (hdrchkTags(entry->info.count))
 
828
            goto errxit;
 
829
 
 
830
        {   int off = ntohl(pe->offset);
 
831
 
 
832
            if (hdrchkData(off))
 
833
                goto errxit;
 
834
            if (off) {
 
835
                int_32 * stei = memcpy(alloca(nb), dataStart + off, nb);
 
836
                rdl = -ntohl(stei[2]);  /* negative offset */
 
837
                ril = rdl/sizeof(*pe);
 
838
                if (hdrchkTags(ril) || hdrchkData(rdl))
 
839
                    goto errxit;
 
840
                entry->info.tag = htonl(pe->tag);
 
841
            } else {
 
842
                ril = il;
 
843
                rdl = (ril * sizeof(struct entryInfo));
 
844
                entry->info.tag = HEADER_IMAGE;
 
845
            }
 
846
        }
 
847
        entry->info.offset = -rdl;      /* negative offset */
 
848
 
 
849
        /*@-assignexpose@*/
 
850
        entry->data = pe;
 
851
        /*@=assignexpose@*/
 
852
        entry->length = pvlen - sizeof(il) - sizeof(dl);
 
853
        rdlen = regionSwab(entry+1, ril-1, 0, pe+1, dataStart, entry->info.offset);
 
854
        if (rdlen < 0)
 
855
            goto errxit;
 
856
        entry->rdlen = rdlen;
 
857
 
 
858
        if (ril < h->indexUsed) {
 
859
            indexEntry newEntry = entry + ril;
 
860
            int ne = (h->indexUsed - ril);
 
861
            int rid = entry->info.offset+1;
 
862
            int rc;
 
863
 
 
864
            /* Load dribble entries from region. */
 
865
            rc = regionSwab(newEntry, ne, 0, pe+ril, dataStart, rid);
 
866
            if (rc < 0)
 
867
                goto errxit;
 
868
            rdlen += rc;
 
869
 
 
870
          { indexEntry firstEntry = newEntry;
 
871
            int save = h->indexUsed;
 
872
            int j;
 
873
 
 
874
            /* Dribble entries replace duplicate region entries. */
 
875
            h->indexUsed -= ne;
 
876
            for (j = 0; j < ne; j++, newEntry++) {
 
877
                (void) headerRemoveEntry(h, newEntry->info.tag);
 
878
                if (newEntry->info.tag == HEADER_BASENAMES)
 
879
                    (void) headerRemoveEntry(h, HEADER_OLDFILENAMES);
 
880
            }
 
881
 
 
882
            /* If any duplicate entries were replaced, move new entries down. */
 
883
            if (h->indexUsed < (save - ne)) {
 
884
                memmove(h->index + h->indexUsed, firstEntry,
 
885
                        (ne * sizeof(*entry)));
 
886
            }
 
887
            h->indexUsed += ne;
 
888
          }
 
889
        }
 
890
    }
 
891
 
 
892
    h->flags &= ~HEADERFLAG_SORTED;
 
893
    headerSort(h);
 
894
 
 
895
    /*@-globstate@*/
 
896
    return h;
 
897
    /*@=globstate@*/
 
898
 
 
899
errxit:
 
900
    /*@-usereleased@*/
 
901
    if (h) {
 
902
        h->index = _free(h->index);
 
903
        /*@-refcounttrans@*/
 
904
        h = _free(h);
 
905
        /*@=refcounttrans@*/
 
906
    }
 
907
    /*@=usereleased@*/
 
908
    /*@-refcounttrans -globstate@*/
 
909
    return h;
 
910
    /*@=refcounttrans =globstate@*/
 
911
}
 
912
 
 
913
Header headerCopyLoad(const void * uh)
 
914
{
 
915
    int_32 * ei = (int_32 *) uh;
 
916
    int_32 il = ntohl(ei[0]);           /* index length */
 
917
    int_32 dl = ntohl(ei[1]);           /* data length */
 
918
    size_t pvlen = sizeof(il) + sizeof(dl) +
 
919
                        (il * sizeof(struct entryInfo)) + dl;
 
920
    void * nuh = NULL;
 
921
    Header h = NULL;
 
922
 
 
923
    /* Sanity checks on header intro. */
 
924
    if (!(hdrchkTags(il) || hdrchkData(dl)) && pvlen < headerMaxbytes) {
 
925
        nuh = memcpy(xmalloc(pvlen), uh, pvlen);
 
926
        if ((h = headerLoad(nuh)) != NULL)
 
927
            h->flags |= HEADERFLAG_ALLOCATED;
 
928
    }
 
929
    if (h == NULL)
 
930
        nuh = _free(nuh);
 
931
    return h;
 
932
}
 
933
 
 
934
Header headerRead(FD_t fd, enum hMagic magicp)
 
935
{
 
936
    int_32 block[4];
 
937
    int_32 reserved;
 
938
    int_32 * ei = NULL;
 
939
    int_32 il;
 
940
    int_32 dl;
 
941
    int_32 magic;
 
942
    Header h = NULL;
 
943
    size_t len;
 
944
    int i;
 
945
 
 
946
    memset(block, 0, sizeof(block));
 
947
    i = 2;
 
948
    if (magicp == HEADER_MAGIC_YES)
 
949
        i += 2;
 
950
 
 
951
    if (timedRead(fd, (char *)block, i*sizeof(*block)) != (i * sizeof(*block)))
 
952
        goto exit;
 
953
 
 
954
    i = 0;
 
955
 
 
956
    if (magicp == HEADER_MAGIC_YES) {
 
957
        magic = block[i++];
 
958
        if (memcmp(&magic, header_magic, sizeof(magic)))
 
959
            goto exit;
 
960
        reserved = block[i++];
 
961
    }
 
962
    
 
963
    il = ntohl(block[i++]);
 
964
    dl = ntohl(block[i++]);
 
965
 
 
966
    len = sizeof(il) + sizeof(dl) + (il * sizeof(struct entryInfo)) + dl;
 
967
 
 
968
    /* Sanity checks on header intro. */
 
969
    if (hdrchkTags(il) || hdrchkData(dl) || len > headerMaxbytes)
 
970
        goto exit;
 
971
 
 
972
    ei = xmalloc(len);
 
973
    ei[0] = htonl(il);
 
974
    ei[1] = htonl(dl);
 
975
    len -= sizeof(il) + sizeof(dl);
 
976
 
 
977
    if (timedRead(fd, (char *)&ei[2], len) != len)
 
978
        goto exit;
 
979
    
 
980
    h = headerLoad(ei);
 
981
 
 
982
exit:
 
983
    if (h) {
 
984
        if (h->flags & HEADERFLAG_ALLOCATED)
 
985
            ei = _free(ei);
 
986
        h->flags |= HEADERFLAG_ALLOCATED;
 
987
    } else if (ei)
 
988
        ei = _free(ei);
 
989
    /*@-mustmod@*/      /* FIX: timedRead macro obscures annotation */
 
990
    return h;
 
991
    /*@-mustmod@*/
 
992
}
 
993
 
 
994
int headerWrite(FD_t fd, Header h, enum hMagic magicp)
 
995
{
 
996
    ssize_t nb;
 
997
    int length;
 
998
    const void * uh;
 
999
 
 
1000
    if (h == NULL)
 
1001
        return 1;
 
1002
    uh = doHeaderUnload(h, &length);
 
1003
    if (uh == NULL)
 
1004
        return 1;
 
1005
    switch (magicp) {
 
1006
    case HEADER_MAGIC_YES:
 
1007
        nb = Fwrite(header_magic, sizeof(char), sizeof(header_magic), fd);
 
1008
        if (nb != sizeof(header_magic))
 
1009
            goto exit;
 
1010
        break;
 
1011
    case HEADER_MAGIC_NO:
 
1012
        break;
 
1013
    }
 
1014
 
 
1015
    nb = Fwrite(uh, sizeof(char), length, fd);
 
1016
 
 
1017
exit:
 
1018
    uh = _free(uh);
 
1019
    return (nb == length ? 0 : 1);
 
1020
}
 
1021
 
 
1022
/**
 
1023
 * Find matching (tag,type) entry in header.
 
1024
 * @param h             header
 
1025
 * @param tag           entry tag
 
1026
 * @param type          entry type
 
1027
 * @return              header entry
 
1028
 */
 
1029
static /*@null@*/
 
1030
indexEntry findEntry(/*@null@*/ Header h, int_32 tag, int_32 type)
 
1031
        /*@modifies h @*/
 
1032
{
 
1033
    indexEntry entry, entry2, last;
 
1034
    struct indexEntry key;
 
1035
 
 
1036
    if (h == NULL) return NULL;
 
1037
    if (!(h->flags & HEADERFLAG_SORTED)) headerSort(h);
 
1038
 
 
1039
    key.info.tag = tag;
 
1040
 
 
1041
    entry2 = entry = 
 
1042
        bsearch(&key, h->index, h->indexUsed, sizeof(*h->index), indexCmp);
 
1043
    if (entry == NULL)
 
1044
        return NULL;
 
1045
 
 
1046
    if (type == RPM_NULL_TYPE)
 
1047
        return entry;
 
1048
 
 
1049
    /* look backwards */
 
1050
    while (entry->info.tag == tag && entry->info.type != type &&
 
1051
           entry > h->index) entry--;
 
1052
 
 
1053
    if (entry->info.tag == tag && entry->info.type == type)
 
1054
        return entry;
 
1055
 
 
1056
    last = h->index + h->indexUsed;
 
1057
    while (entry2->info.tag == tag && entry2->info.type != type &&
 
1058
           entry2 < last) entry2++;
 
1059
 
 
1060
    if (entry->info.tag == tag && entry->info.type == type)
 
1061
        return entry;
 
1062
 
 
1063
    return NULL;
 
1064
}
 
1065
 
 
1066
int headerIsEntry(Header h, int_32 tag)
 
1067
{
 
1068
    /*@-mods@*/         /*@ FIX: h modified by sort. */
 
1069
    return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
 
1070
    /*@=mods@*/ 
 
1071
}
 
1072
 
 
1073
/** \ingroup header
 
1074
 * Retrieve data from header entry.
 
1075
 * @todo Permit retrieval of regions other than HEADER_IMUTABLE.
 
1076
 * @param entry         header entry
 
1077
 * @retval type         address of type (or NULL)
 
1078
 * @retval p            address of data (or NULL)
 
1079
 * @retval c            address of count (or NULL)
 
1080
 * @param minMem        string pointers refer to header memory?
 
1081
 * @return              1 on success, otherwise error.
 
1082
 */
 
1083
static int copyEntry(const indexEntry entry,
 
1084
                /*@null@*/ /*@out@*/ hTYP_t type,
 
1085
                /*@null@*/ /*@out@*/ hPTR_t * p,
 
1086
                /*@null@*/ /*@out@*/ hCNT_t c,
 
1087
                int minMem)
 
1088
        /*@modifies *type, *p, *c @*/
 
1089
{
 
1090
    int_32 count = entry->info.count;
 
1091
    int rc = 1;         /* XXX 1 on success. */
 
1092
 
 
1093
    if (p)
 
1094
    switch (entry->info.type) {
 
1095
    case RPM_BIN_TYPE:
 
1096
        /* XXX this only works for HEADER_IMMUTABLE */
 
1097
        if (ENTRY_IS_REGION(entry)) {
 
1098
            int_32 * ei = ((int_32 *)entry->data) - 2;
 
1099
            /*@-castexpose@*/
 
1100
            entryInfo pe = (entryInfo) (ei + 2);
 
1101
            /*@=castexpose@*/
 
1102
            char * dataStart = (char *) (pe + ntohl(ei[0]));
 
1103
            int_32 rdl = -entry->info.offset;   /* negative offset */
 
1104
            int_32 ril = rdl/sizeof(*pe);
 
1105
 
 
1106
            count = 2 * sizeof(*ei) + (ril * sizeof(*pe)) +
 
1107
                        entry->rdlen + REGION_TAG_COUNT;
 
1108
            *p = xmalloc(count);
 
1109
            ei = (int_32 *) *p;
 
1110
            ei[0] = htonl(ril);
 
1111
            ei[1] = htonl(entry->rdlen + REGION_TAG_COUNT);
 
1112
            /*@-castexpose@*/
 
1113
            pe = (entryInfo) memcpy(ei + 2, pe, (ril * sizeof(*pe)));
 
1114
            /*@=castexpose@*/
 
1115
            dataStart = (char *) memcpy(pe + ril, dataStart,
 
1116
                                        (entry->rdlen + REGION_TAG_COUNT));
 
1117
 
 
1118
            rc = regionSwab(NULL, ril, 0, pe, dataStart, 0);
 
1119
            /* XXX 1 on success. */
 
1120
            rc = (rc < 0) ? 0 : 1;
 
1121
        } else {
 
1122
            count = entry->length;
 
1123
            *p = (!minMem
 
1124
                ? memcpy(xmalloc(count), entry->data, count)
 
1125
                : entry->data);
 
1126
        }
 
1127
        break;
 
1128
    case RPM_STRING_TYPE:
 
1129
        if (count == 1) {
 
1130
            *p = entry->data;
 
1131
            break;
 
1132
        }
 
1133
        /*@fallthrough@*/
 
1134
    case RPM_STRING_ARRAY_TYPE:
 
1135
    case RPM_I18NSTRING_TYPE:
 
1136
    {   const char ** ptrEntry;
 
1137
        int tableSize = count * sizeof(char *);
 
1138
        char * t;
 
1139
        int i;
 
1140
 
 
1141
        /*@-mods@*/
 
1142
        if (minMem) {
 
1143
            *p = xmalloc(tableSize);
 
1144
            ptrEntry = (const char **) *p;
 
1145
            t = entry->data;
 
1146
        } else {
 
1147
            t = xmalloc(tableSize + entry->length);
 
1148
            *p = (void *)t;
 
1149
            ptrEntry = (const char **) *p;
 
1150
            t += tableSize;
 
1151
            memcpy(t, entry->data, entry->length);
 
1152
        }
 
1153
        /*@=mods@*/
 
1154
        for (i = 0; i < count; i++) {
 
1155
            *ptrEntry++ = t;
 
1156
            t = strchr(t, 0);
 
1157
            t++;
 
1158
        }
 
1159
    }   break;
 
1160
 
 
1161
    default:
 
1162
        *p = entry->data;
 
1163
        break;
 
1164
    }
 
1165
    if (type) *type = entry->info.type;
 
1166
    if (c) *c = count;
 
1167
    return rc;
 
1168
}
 
1169
 
 
1170
/**
 
1171
 * Does locale match entry in header i18n table?
 
1172
 * 
 
1173
 * \verbatim
 
1174
 * The range [l,le) contains the next locale to match:
 
1175
 *    ll[_CC][.EEEEE][@dddd]
 
1176
 * where
 
1177
 *    ll        ISO language code (in lowercase).
 
1178
 *    CC        (optional) ISO coutnry code (in uppercase).
 
1179
 *    EEEEE     (optional) encoding (not really standardized).
 
1180
 *    dddd      (optional) dialect.
 
1181
 * \endverbatim
 
1182
 *
 
1183
 * @param td            header i18n table data, NUL terminated
 
1184
 * @param l             start of locale to match
 
1185
 * @param le            end of locale to match
 
1186
 * @return              1 on match, 0 on no match
 
1187
 */
 
1188
static int headerMatchLocale(const char *td, const char *l, const char *le)
 
1189
        /*@*/
 
1190
{
 
1191
    const char *fe;
 
1192
 
 
1193
 
 
1194
#if 0
 
1195
  { const char *s, *ll, *CC, *EE, *dd;
 
1196
    char *lbuf, *t.
 
1197
 
 
1198
    /* Copy the buffer and parse out components on the fly. */
 
1199
    lbuf = alloca(le - l + 1);
 
1200
    for (s = l, ll = t = lbuf; *s; s++, t++) {
 
1201
        switch (*s) {
 
1202
        case '_':
 
1203
            *t = '\0';
 
1204
            CC = t + 1;
 
1205
            break;
 
1206
        case '.':
 
1207
            *t = '\0';
 
1208
            EE = t + 1;
 
1209
            break;
 
1210
        case '@':
 
1211
            *t = '\0';
 
1212
            dd = t + 1;
 
1213
            break;
 
1214
        default:
 
1215
            *t = *s;
 
1216
            break;
 
1217
        }
 
1218
    }
 
1219
 
 
1220
    if (ll)     /* ISO language should be lower case */
 
1221
        for (t = ll; *t; t++)   *t = tolower(*t);
 
1222
    if (CC)     /* ISO country code should be upper case */
 
1223
        for (t = CC; *t; t++)   *t = toupper(*t);
 
1224
 
 
1225
    /* There are a total of 16 cases to attempt to match. */
 
1226
  }
 
1227
#endif
 
1228
 
 
1229
    /* First try a complete match. */
 
1230
    if (strlen(td) == (le-l) && !strncmp(td, l, (le - l)))
 
1231
        return 1;
 
1232
 
 
1233
    /* Next, try stripping optional dialect and matching.  */
 
1234
    for (fe = l; fe < le && *fe != '@'; fe++)
 
1235
        {};
 
1236
    if (fe < le && !strncmp(td, l, (fe - l)))
 
1237
        return 1;
 
1238
 
 
1239
    /* Next, try stripping optional codeset and matching.  */
 
1240
    for (fe = l; fe < le && *fe != '.'; fe++)
 
1241
        {};
 
1242
    if (fe < le && !strncmp(td, l, (fe - l)))
 
1243
        return 1;
 
1244
 
 
1245
    /* Finally, try stripping optional country code and matching. */
 
1246
    for (fe = l; fe < le && *fe != '_'; fe++)
 
1247
        {};
 
1248
    if (fe < le && !strncmp(td, l, (fe - l)))
 
1249
        return 1;
 
1250
 
 
1251
    return 0;
 
1252
}
 
1253
 
 
1254
/**
 
1255
 * Return i18n string from header that matches locale.
 
1256
 * @param h             header
 
1257
 * @param entry         i18n string data
 
1258
 * @return              matching i18n string (or 1st string if no match)
 
1259
 */
 
1260
/*@dependent@*/ static char *
 
1261
headerFindI18NString(Header h, indexEntry entry)
 
1262
{
 
1263
    const char *lang, *l, *le;
 
1264
    indexEntry table;
 
1265
 
 
1266
    /* XXX Drepper sez' this is the order. */
 
1267
    if ((lang = getenv("LANGUAGE")) == NULL &&
 
1268
        (lang = getenv("LC_ALL")) == NULL &&
 
1269
        (lang = getenv("LC_MESSAGES")) == NULL &&
 
1270
        (lang = getenv("LANG")) == NULL)
 
1271
            /*@-retalias -retexpose@*/
 
1272
            return entry->data;
 
1273
            /*@=retalias =retexpose@*/
 
1274
    
 
1275
    if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
 
1276
        /*@-retalias -retexpose@*/
 
1277
        return entry->data;
 
1278
        /*@=retalias =retexpose@*/
 
1279
 
 
1280
    for (l = lang; *l != '\0'; l = le) {
 
1281
        const char *td;
 
1282
        char *ed;
 
1283
        int langNum;
 
1284
 
 
1285
        while (*l && *l == ':')                 /* skip leading colons */
 
1286
            l++;
 
1287
        if (*l == '\0')
 
1288
            break;
 
1289
        for (le = l; *le && *le != ':'; le++)   /* find end of this locale */
 
1290
            {};
 
1291
 
 
1292
        /* For each entry in the header ... */
 
1293
        for (langNum = 0, td = table->data, ed = entry->data;
 
1294
             langNum < entry->info.count;
 
1295
             langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
 
1296
 
 
1297
                if (headerMatchLocale(td, l, le))
 
1298
                    return ed;
 
1299
 
 
1300
        }
 
1301
    }
 
1302
 
 
1303
    /*@-retalias -retexpose@*/
 
1304
    return entry->data;
 
1305
    /*@=retalias =retexpose@*/
 
1306
}
 
1307
 
 
1308
/**
 
1309
 * Retrieve tag data from header.
 
1310
 * @param h             header
 
1311
 * @param tag           tag to retrieve
 
1312
 * @retval type         address of type (or NULL)
 
1313
 * @retval p            address of data (or NULL)
 
1314
 * @retval c            address of count (or NULL)
 
1315
 * @param minMem        string pointers reference header memory?
 
1316
 * @return              1 on success, 0 on not found
 
1317
 */
 
1318
static int intGetEntry(Header h, int_32 tag,
 
1319
                /*@null@*/ /*@out@*/ hTAG_t type,
 
1320
                /*@null@*/ /*@out@*/ hPTR_t * p,
 
1321
                /*@null@*/ /*@out@*/ hCNT_t c,
 
1322
                int minMem)
 
1323
        /*@modifies *type, *p, *c @*/
 
1324
{
 
1325
    indexEntry entry;
 
1326
    int rc;
 
1327
 
 
1328
    /* First find the tag */
 
1329
    /*@-mods@*/         /*@ FIX: h modified by sort. */
 
1330
    entry = findEntry(h, tag, RPM_NULL_TYPE);
 
1331
    /*@mods@*/
 
1332
    if (entry == NULL) {
 
1333
        if (type) type = 0;
 
1334
        if (p) *p = NULL;
 
1335
        if (c) *c = 0;
 
1336
        return 0;
 
1337
    }
 
1338
 
 
1339
    switch (entry->info.type) {
 
1340
    case RPM_I18NSTRING_TYPE:
 
1341
        rc = 1;
 
1342
        if (type) *type = RPM_STRING_TYPE;
 
1343
        if (c) *c = 1;
 
1344
        /*@-dependenttrans@*/
 
1345
        if (p) *p = headerFindI18NString(h, entry);
 
1346
        /*@=dependenttrans@*/
 
1347
        break;
 
1348
    default:
 
1349
        rc = copyEntry(entry, type, p, c, minMem);
 
1350
        break;
 
1351
    }
 
1352
 
 
1353
    /* XXX 1 on success */
 
1354
    return ((rc == 1) ? 1 : 0);
 
1355
}
 
1356
 
 
1357
/** \ingroup header
 
1358
 * Free data allocated when retrieved from header.
 
1359
 * @param h             header
 
1360
 * @param data          address of data (or NULL)
 
1361
 * @param type          type of data (or -1 to force free)
 
1362
 * @return              NULL always
 
1363
 */
 
1364
static /*@null@*/ void * headerFreeTag(/*@unused@*/ Header h,
 
1365
                /*@only@*/ /*@null@*/ const void * data, rpmTagType type)
 
1366
        /*@modifies data @*/
 
1367
{
 
1368
    if (data) {
 
1369
        /*@-branchstate@*/
 
1370
        if (type == -1 ||
 
1371
            type == RPM_STRING_ARRAY_TYPE ||
 
1372
            type == RPM_I18NSTRING_TYPE ||
 
1373
            type == RPM_BIN_TYPE)
 
1374
                data = _free(data);
 
1375
        /*@=branchstate@*/
 
1376
    }
 
1377
    return NULL;
 
1378
}
 
1379
 
 
1380
int headerGetEntry(Header h, int_32 tag, hTYP_t type, void **p, hCNT_t c)
 
1381
{
 
1382
    return intGetEntry(h, tag, type, (hPTR_t *)p, c, 0);
 
1383
}
 
1384
 
 
1385
int headerGetEntryMinMemory(Header h, int_32 tag, hTYP_t type, hPTR_t * p, 
 
1386
                            hCNT_t c)
 
1387
{
 
1388
    return intGetEntry(h, tag, type, p, c, 1);
 
1389
}
 
1390
 
 
1391
int headerGetRawEntry(Header h, int_32 tag, int_32 * type, hPTR_t * p,
 
1392
                int_32 * c)
 
1393
{
 
1394
    indexEntry entry;
 
1395
    int rc;
 
1396
 
 
1397
    if (p == NULL) return headerIsEntry(h, tag);
 
1398
 
 
1399
    /* First find the tag */
 
1400
    /*@-mods@*/         /*@ FIX: h modified by sort. */
 
1401
    entry = findEntry(h, tag, RPM_NULL_TYPE);
 
1402
    /*@=mods@*/
 
1403
    if (!entry) {
 
1404
        if (p) *p = NULL;
 
1405
        if (c) *c = 0;
 
1406
        return 0;
 
1407
    }
 
1408
 
 
1409
    rc = copyEntry(entry, type, p, c, 0);
 
1410
 
 
1411
    /* XXX 1 on success */
 
1412
    return ((rc == 1) ? 1 : 0);
 
1413
}
 
1414
 
 
1415
/**
 
1416
 */
 
1417
static void copyData(int_32 type, /*@out@*/ void * dstPtr, const void * srcPtr,
 
1418
                int_32 c, int dataLength)
 
1419
        /*@modifies *dstPtr @*/
 
1420
{
 
1421
    const char ** src;
 
1422
    char * dst;
 
1423
    int i;
 
1424
 
 
1425
    switch (type) {
 
1426
    case RPM_STRING_ARRAY_TYPE:
 
1427
    case RPM_I18NSTRING_TYPE:
 
1428
        /* Otherwise, p is char** */
 
1429
        i = c;
 
1430
        src = (const char **) srcPtr;
 
1431
        dst = dstPtr;
 
1432
        while (i--) {
 
1433
            if (*src) {
 
1434
                int len = strlen(*src) + 1;
 
1435
                memcpy(dst, *src, len);
 
1436
                dst += len;
 
1437
            }
 
1438
            src++;
 
1439
        }
 
1440
        break;
 
1441
 
 
1442
    default:
 
1443
        memmove(dstPtr, srcPtr, dataLength);
 
1444
        break;
 
1445
    }
 
1446
}
 
1447
 
 
1448
/**
 
1449
 * Return (malloc'ed) copy of entry data.
 
1450
 * @param type          entry data type
 
1451
 * @param p             entry data
 
1452
 * @param c             entry item count
 
1453
 * @retval lengthPtr    no. bytes in returned data
 
1454
 * @return              (malloc'ed) copy of entry data
 
1455
 */
 
1456
static void * grabData(int_32 type, hPTR_t p, int_32 c,
 
1457
                /*@out@*/ int * lengthPtr)
 
1458
        /*@modifies *lengthPtr @*/
 
1459
{
 
1460
    int length = dataLength(type, p, c, 0);
 
1461
    void * data = xmalloc(length);
 
1462
 
 
1463
    copyData(type, data, p, c, length);
 
1464
 
 
1465
    if (lengthPtr)
 
1466
        *lengthPtr = length;
 
1467
    return data;
 
1468
}
 
1469
 
 
1470
int headerAddEntry(Header h, int_32 tag, int_32 type, hPTR_t p, int_32 c)
 
1471
{
 
1472
    indexEntry entry;
 
1473
 
 
1474
    /* Count must always be >= 1 for headerAddEntry. */
 
1475
    if (c <= 0)
 
1476
        return 0;
 
1477
 
 
1478
    /* Allocate more index space if necessary */
 
1479
    if (h->indexUsed == h->indexAlloced) {
 
1480
        h->indexAlloced += INDEX_MALLOC_SIZE;
 
1481
        h->index = xrealloc(h->index, h->indexAlloced * sizeof(*h->index));
 
1482
    }
 
1483
 
 
1484
    /* Fill in the index */
 
1485
    entry = h->index + h->indexUsed;
 
1486
    entry->info.tag = tag;
 
1487
    entry->info.type = type;
 
1488
    entry->info.count = c;
 
1489
    entry->info.offset = 0;
 
1490
    entry->data = grabData(type, p, c, &entry->length);
 
1491
 
 
1492
    if (h->indexUsed > 0 && tag < h->index[h->indexUsed-1].info.tag)
 
1493
        h->flags &= ~HEADERFLAG_SORTED;
 
1494
    h->indexUsed++;
 
1495
 
 
1496
    return 1;
 
1497
}
 
1498
 
 
1499
int headerAppendEntry(Header h, int_32 tag, int_32 type,
 
1500
                        hPTR_t p, int_32 c)
 
1501
{
 
1502
    indexEntry entry;
 
1503
    int length;
 
1504
 
 
1505
    /* First find the tag */
 
1506
    entry = findEntry(h, tag, type);
 
1507
    if (!entry)
 
1508
        return 0;
 
1509
 
 
1510
    if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
 
1511
        /* we can't do this */
 
1512
        return 0;
 
1513
    }
 
1514
 
 
1515
    length = dataLength(type, p, c, 0);
 
1516
 
 
1517
    if (ENTRY_IN_REGION(entry)) {
 
1518
        char * t = xmalloc(entry->length + length);
 
1519
        memcpy(t, entry->data, entry->length);
 
1520
        entry->data = t;
 
1521
        entry->info.offset = 0;
 
1522
    } else
 
1523
        entry->data = xrealloc(entry->data, entry->length + length);
 
1524
 
 
1525
    copyData(type, ((char *) entry->data) + entry->length, p, c, length);
 
1526
 
 
1527
    entry->length += length;
 
1528
 
 
1529
    entry->info.count += c;
 
1530
 
 
1531
    return 1;
 
1532
}
 
1533
 
 
1534
int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
 
1535
                           hPTR_t p, int_32 c)
 
1536
{
 
1537
    return (findEntry(h, tag, type)
 
1538
        ? headerAppendEntry(h, tag, type, p, c)
 
1539
        : headerAddEntry(h, tag, type, p, c));
 
1540
}
 
1541
 
 
1542
int headerAddI18NString(Header h, int_32 tag, const char * string, const char * lang)
 
1543
{
 
1544
    indexEntry table, entry;
 
1545
    const char ** strArray;
 
1546
    int length;
 
1547
    int ghosts;
 
1548
    int i, langNum;
 
1549
    char * buf;
 
1550
 
 
1551
    table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
 
1552
    entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
 
1553
 
 
1554
    if (!table && entry)
 
1555
        return 0;               /* this shouldn't ever happen!! */
 
1556
 
 
1557
    if (!table && !entry) {
 
1558
        const char * charArray[2];
 
1559
        int count = 0;
 
1560
        if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
 
1561
            /*@-observertrans -readonlytrans@*/
 
1562
            charArray[count++] = "C";
 
1563
            /*@=observertrans =readonlytrans@*/
 
1564
        } else {
 
1565
            /*@-observertrans -readonlytrans@*/
 
1566
            charArray[count++] = "C";
 
1567
            /*@=observertrans =readonlytrans@*/
 
1568
            charArray[count++] = lang;
 
1569
        }
 
1570
        if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE, 
 
1571
                        &charArray, count))
 
1572
            return 0;
 
1573
        table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
 
1574
    }
 
1575
 
 
1576
    if (!table)
 
1577
        return 0;
 
1578
    if (!lang) lang = "C";
 
1579
 
 
1580
    {   const char * l = table->data;
 
1581
        for (langNum = 0; langNum < table->info.count; langNum++) {
 
1582
            if (!strcmp(l, lang)) break;
 
1583
            l += strlen(l) + 1;
 
1584
        }
 
1585
    }
 
1586
 
 
1587
    if (langNum >= table->info.count) {
 
1588
        length = strlen(lang) + 1;
 
1589
        if (ENTRY_IN_REGION(table)) {
 
1590
            char * t = xmalloc(table->length + length);
 
1591
            memcpy(t, table->data, table->length);
 
1592
            table->data = t;
 
1593
            table->info.offset = 0;
 
1594
        } else
 
1595
            table->data = xrealloc(table->data, table->length + length);
 
1596
        memmove(((char *)table->data) + table->length, lang, length);
 
1597
        table->length += length;
 
1598
        table->info.count++;
 
1599
    }
 
1600
 
 
1601
    if (!entry) {
 
1602
        strArray = alloca(sizeof(*strArray) * (langNum + 1));
 
1603
        for (i = 0; i < langNum; i++)
 
1604
            strArray[i] = "";
 
1605
        strArray[langNum] = string;
 
1606
        return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, strArray, 
 
1607
                                langNum + 1);
 
1608
    } else if (langNum >= entry->info.count) {
 
1609
        ghosts = langNum - entry->info.count;
 
1610
        
 
1611
        length = strlen(string) + 1 + ghosts;
 
1612
        if (ENTRY_IN_REGION(entry)) {
 
1613
            char * t = xmalloc(entry->length + length);
 
1614
            memcpy(t, entry->data, entry->length);
 
1615
            entry->data = t;
 
1616
            entry->info.offset = 0;
 
1617
        } else
 
1618
            entry->data = xrealloc(entry->data, entry->length + length);
 
1619
 
 
1620
        memset(((char *)entry->data) + entry->length, '\0', ghosts);
 
1621
        memmove(((char *)entry->data) + entry->length + ghosts, string, strlen(string)+1);
 
1622
 
 
1623
        entry->length += length;
 
1624
        entry->info.count = langNum + 1;
 
1625
    } else {
 
1626
        char *b, *be, *e, *ee, *t;
 
1627
        size_t bn, sn, en;
 
1628
 
 
1629
        /* Set beginning/end pointers to previous data */
 
1630
        b = be = e = ee = entry->data;
 
1631
        for (i = 0; i < table->info.count; i++) {
 
1632
            if (i == langNum)
 
1633
                be = ee;
 
1634
            ee += strlen(ee) + 1;
 
1635
            if (i == langNum)
 
1636
                e  = ee;
 
1637
        }
 
1638
 
 
1639
        /* Get storage for new buffer */
 
1640
        bn = (be-b);
 
1641
        sn = strlen(string) + 1;
 
1642
        en = (ee-e);
 
1643
        length = bn + sn + en;
 
1644
        t = buf = xmalloc(length);
 
1645
 
 
1646
        /* Copy values into new storage */
 
1647
        memcpy(t, b, bn);
 
1648
        t += bn;
 
1649
        memcpy(t, string, sn);
 
1650
        t += sn;
 
1651
        memcpy(t, e, en);
 
1652
        t += en;
 
1653
 
 
1654
        /* Replace I18N string array */
 
1655
        entry->length -= strlen(be) + 1;
 
1656
        entry->length += sn;
 
1657
        
 
1658
        if (ENTRY_IN_REGION(entry)) {
 
1659
            entry->info.offset = 0;
 
1660
        } else
 
1661
            entry->data = _free(entry->data);
 
1662
        /*@-dependenttrans@*/
 
1663
        entry->data = buf;
 
1664
        /*@=dependenttrans@*/
 
1665
    }
 
1666
 
 
1667
    return 0;
 
1668
}
 
1669
 
 
1670
int headerModifyEntry(Header h, int_32 tag, int_32 type, hPTR_t p, int_32 c)
 
1671
{
 
1672
    indexEntry entry;
 
1673
    void * oldData;
 
1674
 
 
1675
    /* First find the tag */
 
1676
    entry = findEntry(h, tag, type);
 
1677
    if (!entry)
 
1678
        return 0;
 
1679
 
 
1680
    /* make sure entry points to the first occurence of this tag */
 
1681
    while (entry > h->index && (entry - 1)->info.tag == tag)  
 
1682
        entry--;
 
1683
 
 
1684
    /* free after we've grabbed the new data in case the two are intertwined;
 
1685
       that's a bad idea but at least we won't break */
 
1686
    oldData = entry->data;
 
1687
 
 
1688
    entry->info.count = c;
 
1689
    entry->info.type = type;
 
1690
    entry->data = grabData(type, p, c, &entry->length);
 
1691
 
 
1692
    if (ENTRY_IN_REGION(entry)) {
 
1693
        entry->info.offset = 0;
 
1694
    } else
 
1695
        oldData = _free(oldData);
 
1696
 
 
1697
    return 1;
 
1698
}
 
1699
 
 
1700
int headerRemoveEntry(Header h, int_32 tag)
 
1701
{
 
1702
    indexEntry last = h->index + h->indexUsed;
 
1703
    indexEntry entry, first;
 
1704
    int ne;
 
1705
 
 
1706
    entry = findEntry(h, tag, RPM_NULL_TYPE);
 
1707
    if (!entry) return 1;
 
1708
 
 
1709
    /* Make sure entry points to the first occurence of this tag. */
 
1710
    while (entry > h->index && (entry - 1)->info.tag == tag)  
 
1711
        entry--;
 
1712
 
 
1713
    /* Free data for tags being removed. */
 
1714
    for (first = entry; first < last; first++) {
 
1715
        void * data;
 
1716
        if (first->info.tag != tag)
 
1717
            break;
 
1718
        data = first->data;
 
1719
        first->data = NULL;
 
1720
        first->length = 0;
 
1721
        if (ENTRY_IN_REGION(first))
 
1722
            continue;
 
1723
        data = _free(data);
 
1724
    }
 
1725
 
 
1726
    ne = (first - entry);
 
1727
    if (ne > 0) {
 
1728
        h->indexUsed -= ne;
 
1729
        ne = last - first;
 
1730
        if (ne > 0)
 
1731
            memmove(entry, first, (ne * sizeof(*entry)));
 
1732
    }
 
1733
 
 
1734
    return 0;
 
1735
}
 
1736
 
 
1737
/**
 
1738
 */
 
1739
static char escapedChar(const char ch)  /*@*/
 
1740
{
 
1741
    switch (ch) {
 
1742
    case 'a':   return '\a';
 
1743
    case 'b':   return '\b';
 
1744
    case 'f':   return '\f';
 
1745
    case 'n':   return '\n';
 
1746
    case 'r':   return '\r';
 
1747
    case 't':   return '\t';
 
1748
    case 'v':   return '\v';
 
1749
    default:    return ch;
 
1750
    }
 
1751
}
 
1752
 
 
1753
/**
 
1754
 * Destroy headerSprintf format array.
 
1755
 * @param format        sprintf format array
 
1756
 * @param num           number of elements
 
1757
 * @return              NULL always
 
1758
 */
 
1759
static /*@null@*/ sprintfToken
 
1760
freeFormat( /*@only@*/ /*@null@*/ sprintfToken format, int num)
 
1761
        /*@modifies *format @*/
 
1762
{
 
1763
    int i;
 
1764
 
 
1765
    if (format == NULL) return NULL;
 
1766
    for (i = 0; i < num; i++) {
 
1767
        switch (format[i].type) {
 
1768
        case PTOK_ARRAY:
 
1769
            format[i].u.array.format =
 
1770
                freeFormat(format[i].u.array.format,
 
1771
                        format[i].u.array.numTokens);
 
1772
            break;
 
1773
        case PTOK_COND:
 
1774
            format[i].u.cond.ifFormat =
 
1775
                freeFormat(format[i].u.cond.ifFormat, 
 
1776
                        format[i].u.cond.numIfTokens);
 
1777
            format[i].u.cond.elseFormat =
 
1778
                freeFormat(format[i].u.cond.elseFormat, 
 
1779
                        format[i].u.cond.numElseTokens);
 
1780
            break;
 
1781
        case PTOK_NONE:
 
1782
        case PTOK_TAG:
 
1783
        case PTOK_STRING:
 
1784
        default:
 
1785
            break;
 
1786
        }
 
1787
    }
 
1788
    format = _free(format);
 
1789
    return NULL;
 
1790
}
 
1791
 
 
1792
/**
 
1793
 */
 
1794
static void findTag(char * name, const headerTagTableEntry tags, 
 
1795
                    const headerSprintfExtension extensions,
 
1796
                    /*@out@*/ headerTagTableEntry * tagMatch,
 
1797
                    /*@out@*/ headerSprintfExtension * extMatch)
 
1798
        /*@modifies *tagMatch, *extMatch @*/
 
1799
{
 
1800
    headerTagTableEntry entry;
 
1801
    headerSprintfExtension ext;
 
1802
    const char * tagname;
 
1803
 
 
1804
    *tagMatch = NULL;
 
1805
    *extMatch = NULL;
 
1806
 
 
1807
    if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
 
1808
        char * t = alloca(strlen(name) + sizeof("RPMTAG_"));
 
1809
        (void) stpcpy( stpcpy(t, "RPMTAG_"), name);
 
1810
        tagname = t;
 
1811
    } else {
 
1812
        tagname = name;
 
1813
    }
 
1814
 
 
1815
    /* Search extensions first to permit overriding header tags. */
 
1816
    ext = extensions;
 
1817
    while (ext->type != HEADER_EXT_LAST) {
 
1818
        if (ext->name != NULL && ext->type == HEADER_EXT_TAG
 
1819
        && !xstrcasecmp(ext->name, tagname))
 
1820
            break;
 
1821
 
 
1822
        if (ext->type == HEADER_EXT_MORE)
 
1823
            ext = ext->u.more;
 
1824
        else
 
1825
            ext++;
 
1826
    }
 
1827
 
 
1828
    if (ext->type == HEADER_EXT_TAG) {
 
1829
        *extMatch = ext;
 
1830
        return;
 
1831
    }
 
1832
 
 
1833
    /* Search header tags. */
 
1834
    for (entry = tags; entry->name; entry++)
 
1835
        if (entry->name && !xstrcasecmp(entry->name, tagname))
 
1836
            break;
 
1837
 
 
1838
    if (entry->name) {
 
1839
        *tagMatch = entry;
 
1840
        return;
 
1841
    }
 
1842
}
 
1843
 
 
1844
/* forward ref */
 
1845
static int parseExpression(sprintfToken token, char * str, 
 
1846
                const headerTagTableEntry tags, 
 
1847
                const headerSprintfExtension extensions,
 
1848
                /*@out@*/char ** endPtr, /*@null@*/ /*@out@*/ errmsg_t * errmsg)
 
1849
        /*@modifies str, *str, *token, *endPtr, *errmsg @*/;
 
1850
 
 
1851
/**
 
1852
 */
 
1853
static int parseFormat(char * str, const headerTagTableEntry tags,
 
1854
                const headerSprintfExtension extensions,
 
1855
                /*@out@*/sprintfToken * formatPtr, /*@out@*/int * numTokensPtr,
 
1856
                /*@null@*/ /*@out@*/ char ** endPtr, int state,
 
1857
                /*@null@*/ /*@out@*/ errmsg_t * errmsg)
 
1858
        /*@modifies str, *str, *formatPtr, *numTokensPtr, *endPtr, *errmsg @*/
 
1859
{
 
1860
    char * chptr, * start, * next, * dst;
 
1861
    sprintfToken format;
 
1862
    int numTokens;
 
1863
    int currToken;
 
1864
    headerTagTableEntry tag;
 
1865
    headerSprintfExtension ext;
 
1866
    int i;
 
1867
    int done = 0;
 
1868
 
 
1869
    /* upper limit on number of individual formats */
 
1870
    numTokens = 0;
 
1871
    for (chptr = str; *chptr != '\0'; chptr++)
 
1872
        if (*chptr == '%') numTokens++;
 
1873
    numTokens = numTokens * 2 + 1;
 
1874
 
 
1875
    format = xcalloc(numTokens, sizeof(*format));
 
1876
    if (endPtr) *endPtr = NULL;
 
1877
 
 
1878
    /*@-infloops@*/
 
1879
    dst = start = str;
 
1880
    currToken = -1;
 
1881
    while (*start != '\0') {
 
1882
        switch (*start) {
 
1883
        case '%':
 
1884
            /* handle %% */
 
1885
            if (*(start + 1) == '%') {
 
1886
                if (currToken < 0 || format[currToken].type != PTOK_STRING) {
 
1887
                    currToken++;
 
1888
                    format[currToken].type = PTOK_STRING;
 
1889
                    /*@-temptrans -assignexpose@*/
 
1890
                    dst = format[currToken].u.string.string = start;
 
1891
                    /*@=temptrans =assignexpose@*/
 
1892
                }
 
1893
 
 
1894
                start++;
 
1895
 
 
1896
                *dst++ = *start++;
 
1897
 
 
1898
                break; /* out of switch */
 
1899
            } 
 
1900
 
 
1901
            currToken++;
 
1902
            *dst++ = '\0';
 
1903
            start++;
 
1904
 
 
1905
            if (*start == '|') {
 
1906
                char * newEnd;
 
1907
 
 
1908
                start++;
 
1909
                if (parseExpression(format + currToken, start, tags, 
 
1910
                                    extensions, &newEnd, errmsg))
 
1911
                {
 
1912
                    format = freeFormat(format, numTokens);
 
1913
                    return 1;
 
1914
                }
 
1915
                start = newEnd;
 
1916
                break; /* out of switch */
 
1917
            }
 
1918
 
 
1919
            /*@-assignexpose@*/
 
1920
            format[currToken].u.tag.format = start;
 
1921
            /*@=assignexpose@*/
 
1922
            format[currToken].u.tag.pad = 0;
 
1923
            format[currToken].u.tag.justOne = 0;
 
1924
            format[currToken].u.tag.arrayCount = 0;
 
1925
 
 
1926
            chptr = start;
 
1927
            while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
 
1928
            if (!*chptr || *chptr == '%') {
 
1929
                /*@-observertrans -readonlytrans@*/
 
1930
                if (errmsg) *errmsg = _("missing { after %");
 
1931
                /*@=observertrans =readonlytrans@*/
 
1932
                format = freeFormat(format, numTokens);
 
1933
                return 1;
 
1934
            }
 
1935
 
 
1936
            *chptr++ = '\0';
 
1937
 
 
1938
            while (start < chptr) {
 
1939
                if (xisdigit(*start)) {
 
1940
                    i = strtoul(start, &start, 10);
 
1941
                    format[currToken].u.tag.pad += i;
 
1942
                } else {
 
1943
                    start++;
 
1944
                }
 
1945
            }
 
1946
 
 
1947
            if (*start == '=') {
 
1948
                format[currToken].u.tag.justOne = 1;
 
1949
                start++;
 
1950
            } else if (*start == '#') {
 
1951
                format[currToken].u.tag.justOne = 1;
 
1952
                format[currToken].u.tag.arrayCount = 1;
 
1953
                start++;
 
1954
            }
 
1955
 
 
1956
            next = start;
 
1957
            while (*next && *next != '}') next++;
 
1958
            if (!*next) {
 
1959
                /*@-observertrans -readonlytrans@*/
 
1960
                if (errmsg) *errmsg = _("missing } after %{");
 
1961
                /*@=observertrans =readonlytrans@*/
 
1962
                format = freeFormat(format, numTokens);
 
1963
                return 1;
 
1964
            }
 
1965
            *next++ = '\0';
 
1966
 
 
1967
            chptr = start;
 
1968
            while (*chptr && *chptr != ':') chptr++;
 
1969
 
 
1970
            if (*chptr != '\0') {
 
1971
                *chptr++ = '\0';
 
1972
                if (!*chptr) {
 
1973
                    /*@-observertrans -readonlytrans@*/
 
1974
                    if (errmsg) *errmsg = _("empty tag format");
 
1975
                    /*@=observertrans =readonlytrans@*/
 
1976
                    format = freeFormat(format, numTokens);
 
1977
                    return 1;
 
1978
                }
 
1979
                /*@-assignexpose@*/
 
1980
                format[currToken].u.tag.type = chptr;
 
1981
                /*@=assignexpose@*/
 
1982
            } else {
 
1983
                format[currToken].u.tag.type = NULL;
 
1984
            }
 
1985
            
 
1986
            if (!*start) {
 
1987
                /*@-observertrans -readonlytrans@*/
 
1988
                if (errmsg) *errmsg = _("empty tag name");
 
1989
                /*@=observertrans =readonlytrans@*/
 
1990
                format = freeFormat(format, numTokens);
 
1991
                return 1;
 
1992
            }
 
1993
 
 
1994
            i = 0;
 
1995
            findTag(start, tags, extensions, &tag, &ext);
 
1996
 
 
1997
            if (tag) {
 
1998
                format[currToken].u.tag.ext = NULL;
 
1999
                format[currToken].u.tag.tag = tag->val;
 
2000
            } else if (ext) {
 
2001
                format[currToken].u.tag.ext = ext->u.tagFunction;
 
2002
                format[currToken].u.tag.extNum = ext - extensions;
 
2003
            } else {
 
2004
                /*@-observertrans -readonlytrans@*/
 
2005
                if (errmsg) *errmsg = _("unknown tag");
 
2006
                /*@=observertrans =readonlytrans@*/
 
2007
                format = freeFormat(format, numTokens);
 
2008
                return 1;
 
2009
            }
 
2010
 
 
2011
            format[currToken].type = PTOK_TAG;
 
2012
 
 
2013
            start = next;
 
2014
 
 
2015
            break;
 
2016
 
 
2017
        case '[':
 
2018
            *dst++ = '\0';
 
2019
            *start++ = '\0';
 
2020
            currToken++;
 
2021
 
 
2022
            if (parseFormat(start, tags, extensions, 
 
2023
                            &format[currToken].u.array.format,
 
2024
                            &format[currToken].u.array.numTokens,
 
2025
                            &start, PARSER_IN_ARRAY, errmsg)) {
 
2026
                format = freeFormat(format, numTokens);
 
2027
                return 1;
 
2028
            }
 
2029
 
 
2030
            if (!start) {
 
2031
                /*@-observertrans -readonlytrans@*/
 
2032
                if (errmsg) *errmsg = _("] expected at end of array");
 
2033
                /*@=observertrans =readonlytrans@*/
 
2034
                format = freeFormat(format, numTokens);
 
2035
                return 1;
 
2036
            }
 
2037
 
 
2038
            dst = start;
 
2039
 
 
2040
            format[currToken].type = PTOK_ARRAY;
 
2041
 
 
2042
            break;
 
2043
 
 
2044
        case ']':
 
2045
        case '}':
 
2046
            if ((*start == ']' && state != PARSER_IN_ARRAY) ||
 
2047
                (*start == '}' && state != PARSER_IN_EXPR)) {
 
2048
                if (*start == ']') {
 
2049
                    /*@-observertrans -readonlytrans@*/
 
2050
                    if (errmsg) *errmsg = _("unexpected ]");
 
2051
                    /*@=observertrans =readonlytrans@*/
 
2052
                } else {
 
2053
                    /*@-observertrans -readonlytrans@*/
 
2054
                    if (errmsg) *errmsg = _("unexpected }");
 
2055
                    /*@=observertrans =readonlytrans@*/
 
2056
                }
 
2057
                format = freeFormat(format, numTokens);
 
2058
                return 1;
 
2059
            }
 
2060
            *start++ = '\0';
 
2061
            if (endPtr) *endPtr = start;
 
2062
            done = 1;
 
2063
            break;
 
2064
 
 
2065
        default:
 
2066
            if (currToken < 0 || format[currToken].type != PTOK_STRING) {
 
2067
                currToken++;
 
2068
                format[currToken].type = PTOK_STRING;
 
2069
                /*@-temptrans -assignexpose@*/
 
2070
                dst = format[currToken].u.string.string = start;
 
2071
                /*@=temptrans =assignexpose@*/
 
2072
            }
 
2073
 
 
2074
            if (*start == '\\') {
 
2075
                start++;
 
2076
                *dst++ = escapedChar(*start++);
 
2077
            } else {
 
2078
                *dst++ = *start++;
 
2079
            }
 
2080
            break;
 
2081
        }
 
2082
        if (done)
 
2083
            break;
 
2084
    }
 
2085
    /*@=infloops@*/
 
2086
 
 
2087
    *dst = '\0';
 
2088
 
 
2089
    currToken++;
 
2090
    for (i = 0; i < currToken; i++) {
 
2091
        if (format[i].type == PTOK_STRING)
 
2092
            format[i].u.string.len = strlen(format[i].u.string.string);
 
2093
    }
 
2094
 
 
2095
    *numTokensPtr = currToken;
 
2096
    *formatPtr = format;
 
2097
 
 
2098
    return 0;
 
2099
}
 
2100
 
 
2101
/**
 
2102
 */
 
2103
static int parseExpression(sprintfToken token, char * str, 
 
2104
                const headerTagTableEntry tags, 
 
2105
                const headerSprintfExtension extensions,
 
2106
                /*@out@*/ char ** endPtr,
 
2107
                /*@null@*/ /*@out@*/ errmsg_t * errmsg)
 
2108
{
 
2109
    headerTagTableEntry tag;
 
2110
    headerSprintfExtension ext;
 
2111
    char * chptr;
 
2112
    char * end;
 
2113
 
 
2114
    if (errmsg) *errmsg = NULL;
 
2115
    chptr = str;
 
2116
    while (*chptr && *chptr != '?') chptr++;
 
2117
 
 
2118
    if (*chptr != '?') {
 
2119
        /*@-observertrans -readonlytrans@*/
 
2120
        if (errmsg) *errmsg = _("? expected in expression");
 
2121
        /*@=observertrans =readonlytrans@*/
 
2122
        return 1;
 
2123
    }
 
2124
 
 
2125
    *chptr++ = '\0';;
 
2126
 
 
2127
    if (*chptr != '{') {
 
2128
        /*@-observertrans -readonlytrans@*/
 
2129
        if (errmsg) *errmsg = _("{ expected after ? in expression");
 
2130
        /*@=observertrans =readonlytrans@*/
 
2131
        return 1;
 
2132
    }
 
2133
 
 
2134
    chptr++;
 
2135
 
 
2136
    if (parseFormat(chptr, tags, extensions, &token->u.cond.ifFormat, 
 
2137
                    &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR, errmsg)) 
 
2138
        return 1;
 
2139
 
 
2140
    if (!*end) {
 
2141
        /*@-observertrans -readonlytrans@*/
 
2142
        if (errmsg) *errmsg = _("} expected in expression");
 
2143
        /*@=observertrans =readonlytrans@*/
 
2144
        token->u.cond.ifFormat =
 
2145
                freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
 
2146
        return 1;
 
2147
    }
 
2148
 
 
2149
    chptr = end;
 
2150
    if (*chptr != ':' && *chptr != '|') {
 
2151
        /*@-observertrans -readonlytrans@*/
 
2152
        if (errmsg) *errmsg = _(": expected following ? subexpression");
 
2153
        /*@=observertrans =readonlytrans@*/
 
2154
        token->u.cond.ifFormat =
 
2155
                freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
 
2156
        return 1;
 
2157
    }
 
2158
 
 
2159
    if (*chptr == '|') {
 
2160
        (void) parseFormat(xstrdup(""), tags, extensions,
 
2161
                        &token->u.cond.elseFormat, 
 
2162
                        &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR, 
 
2163
                        errmsg);
 
2164
    } else {
 
2165
        chptr++;
 
2166
 
 
2167
        if (*chptr != '{') {
 
2168
            /*@-observertrans -readonlytrans@*/
 
2169
            if (errmsg) *errmsg = _("{ expected after : in expression");
 
2170
            /*@=observertrans =readonlytrans@*/
 
2171
            token->u.cond.ifFormat =
 
2172
                freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
 
2173
            return 1;
 
2174
        }
 
2175
 
 
2176
        chptr++;
 
2177
 
 
2178
        if (parseFormat(chptr, tags, extensions, &token->u.cond.elseFormat, 
 
2179
                        &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR, 
 
2180
                        errmsg)) 
 
2181
            return 1;
 
2182
        if (!*end) {
 
2183
            /*@-observertrans -readonlytrans@*/
 
2184
            if (errmsg) *errmsg = _("} expected in expression");
 
2185
            /*@=observertrans =readonlytrans@*/
 
2186
            token->u.cond.ifFormat =
 
2187
                freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
 
2188
            return 1;
 
2189
        }
 
2190
 
 
2191
        chptr = end;
 
2192
        if (*chptr != '|') {
 
2193
            /*@-observertrans -readonlytrans@*/
 
2194
            if (errmsg) *errmsg = _("| expected at end of expression");
 
2195
            /*@=observertrans =readonlytrans@*/
 
2196
            token->u.cond.ifFormat =
 
2197
                freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
 
2198
            token->u.cond.elseFormat =
 
2199
                freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
 
2200
            return 1;
 
2201
        }
 
2202
    }
 
2203
        
 
2204
    chptr++;
 
2205
 
 
2206
    *endPtr = chptr;
 
2207
 
 
2208
    findTag(str, tags, extensions, &tag, &ext);
 
2209
 
 
2210
    if (tag) {
 
2211
        token->u.cond.tag.ext = NULL;
 
2212
        token->u.cond.tag.tag = tag->val;
 
2213
    } else if (ext) {
 
2214
        token->u.cond.tag.ext = ext->u.tagFunction;
 
2215
        token->u.cond.tag.extNum = ext - extensions;
 
2216
    } else {
 
2217
        token->u.cond.tag.ext = NULL;
 
2218
        token->u.cond.tag.tag = -1;
 
2219
    }
 
2220
        
 
2221
    token->type = PTOK_COND;
 
2222
 
 
2223
    return 0;
 
2224
}
 
2225
 
 
2226
/**
 
2227
 * @return              0 on success, 1 on failure
 
2228
 */
 
2229
static int getExtension(Header h, headerTagTagFunction fn,
 
2230
                /*@out@*/ hTYP_t typeptr,
 
2231
                /*@out@*/ hPTR_t * data,
 
2232
                /*@out@*/ hCNT_t countptr,
 
2233
                extensionCache ext)
 
2234
        /*@modifies *typeptr, *data, *countptr, ext @*/
 
2235
{
 
2236
    if (!ext->avail) {
 
2237
        if (fn(h, &ext->type, &ext->data, &ext->count, &ext->freeit))
 
2238
            return 1;
 
2239
        ext->avail = 1;
 
2240
    }
 
2241
 
 
2242
    if (typeptr) *typeptr = ext->type;
 
2243
    if (data) *data = ext->data;
 
2244
    if (countptr) *countptr = ext->count;
 
2245
 
 
2246
    return 0;
 
2247
}
 
2248
 
 
2249
/**
 
2250
 */
 
2251
static char * formatValue(sprintfTag tag, Header h, 
 
2252
                const headerSprintfExtension extensions,
 
2253
                extensionCache extCache, int element)
 
2254
        /*@modifies extCache @*/
 
2255
{
 
2256
    int len;
 
2257
    char buf[20];
 
2258
    int_32 count, type;
 
2259
    hPTR_t data;
 
2260
    unsigned int intVal;
 
2261
    char * val = NULL;
 
2262
    const char ** strarray;
 
2263
    int mayfree = 0;
 
2264
    int countBuf;
 
2265
    headerTagFormatFunction tagtype = NULL;
 
2266
    headerSprintfExtension ext;
 
2267
 
 
2268
    memset(buf, 0, sizeof(buf));
 
2269
    if (tag->ext) {
 
2270
        if (getExtension(h, tag->ext, &type, &data, &count, 
 
2271
                         extCache + tag->extNum))
 
2272
        {
 
2273
            count = 1;
 
2274
            type = RPM_STRING_TYPE;     
 
2275
            data = "(none)";            /* XXX i18n? NO!, sez; gafton */
 
2276
        }
 
2277
    } else {
 
2278
        if (!headerGetEntry(h, tag->tag, &type, (void **)&data, &count)) {
 
2279
            count = 1;
 
2280
            type = RPM_STRING_TYPE;     
 
2281
            data = "(none)";            /* XXX i18n? NO!, sez; gafton */
 
2282
        }
 
2283
 
 
2284
        mayfree = 1;
 
2285
    }
 
2286
 
 
2287
    if (tag->arrayCount) {
 
2288
        /*@-observertrans -modobserver@*/
 
2289
        data = headerFreeData(data, type);
 
2290
        /*@=observertrans =modobserver@*/
 
2291
 
 
2292
        countBuf = count;
 
2293
        data = &countBuf;
 
2294
        count = 1;
 
2295
        type = RPM_INT32_TYPE;
 
2296
    }
 
2297
 
 
2298
    (void) stpcpy( stpcpy(buf, "%"), tag->format);
 
2299
 
 
2300
    if (tag->type) {
 
2301
        ext = extensions;
 
2302
        while (ext->type != HEADER_EXT_LAST) {
 
2303
            if (ext->name != NULL && ext->type == HEADER_EXT_FORMAT
 
2304
            && !strcmp(ext->name, tag->type))
 
2305
            {
 
2306
                tagtype = ext->u.formatFunction;
 
2307
                break;
 
2308
            }
 
2309
 
 
2310
            if (ext->type == HEADER_EXT_MORE)
 
2311
                ext = ext->u.more;
 
2312
            else
 
2313
                ext++;
 
2314
        }
 
2315
    }
 
2316
    
 
2317
    switch (type) {
 
2318
    case RPM_STRING_ARRAY_TYPE:
 
2319
        strarray = (const char **)data;
 
2320
 
 
2321
        if (tagtype)
 
2322
            val = tagtype(RPM_STRING_TYPE, strarray[element], buf, tag->pad, 0);
 
2323
 
 
2324
        if (!val) {
 
2325
            strcat(buf, "s");
 
2326
 
 
2327
            len = strlen(strarray[element]) + tag->pad + 20;
 
2328
            val = xmalloc(len);
 
2329
            /*@-formatconst@*/
 
2330
            sprintf(val, buf, strarray[element]);
 
2331
            /*@=formatconst@*/
 
2332
        }
 
2333
 
 
2334
        /*@-observertrans -modobserver@*/
 
2335
        if (mayfree) data = _free(data);
 
2336
        /*@=observertrans =modobserver@*/
 
2337
 
 
2338
        break;
 
2339
 
 
2340
    case RPM_STRING_TYPE:
 
2341
        if (tagtype)
 
2342
            val = tagtype(RPM_STRING_ARRAY_TYPE, data, buf, tag->pad,  0);
 
2343
 
 
2344
        if (!val) {
 
2345
            strcat(buf, "s");
 
2346
 
 
2347
            len = strlen(data) + tag->pad + 20;
 
2348
            val = xmalloc(len);
 
2349
            /*@-formatconst@*/
 
2350
            sprintf(val, buf, data);
 
2351
            /*@=formatconst@*/
 
2352
        }
 
2353
        break;
 
2354
 
 
2355
    case RPM_CHAR_TYPE:
 
2356
    case RPM_INT8_TYPE:
 
2357
    case RPM_INT16_TYPE:
 
2358
    case RPM_INT32_TYPE:
 
2359
        switch (type) {
 
2360
        case RPM_CHAR_TYPE:     
 
2361
        case RPM_INT8_TYPE:     intVal = *(((int_8 *) data) + element); break;
 
2362
        case RPM_INT16_TYPE:    intVal = *(((uint_16 *) data) + element); break;
 
2363
        default:                /* keep -Wall quiet */
 
2364
        case RPM_INT32_TYPE:    intVal = *(((int_32 *) data) + element); break;
 
2365
        }
 
2366
 
 
2367
        if (tagtype)
 
2368
            val = tagtype(RPM_INT32_TYPE, &intVal, buf, tag->pad,  element);
 
2369
 
 
2370
        if (!val) {
 
2371
            strcat(buf, "d");
 
2372
            len = 10 + tag->pad + 20;
 
2373
            val = xmalloc(len);
 
2374
            /*@-formatconst@*/
 
2375
            sprintf(val, buf, intVal);
 
2376
            /*@=formatconst@*/
 
2377
        }
 
2378
        break;
 
2379
 
 
2380
    default:
 
2381
        val = xstrdup(_("(unknown type)"));
 
2382
        break;
 
2383
    }
 
2384
 
 
2385
    return val;
 
2386
}
 
2387
 
 
2388
/**
 
2389
 */
 
2390
static const char * singleSprintf(Header h, sprintfToken token,
 
2391
                const headerSprintfExtension extensions,
 
2392
                extensionCache extCache, int element)
 
2393
        /*@modifies h, extCache @*/
 
2394
{
 
2395
    char * val;
 
2396
    const char * thisItem;
 
2397
    int thisItemLen;
 
2398
    int len, alloced;
 
2399
    int i, j;
 
2400
    int numElements;
 
2401
    int type;
 
2402
    sprintfToken condFormat;
 
2403
    int condNumFormats;
 
2404
 
 
2405
    /* we assume the token and header have been validated already! */
 
2406
 
 
2407
    switch (token->type) {
 
2408
    case PTOK_NONE:
 
2409
        break;
 
2410
 
 
2411
    case PTOK_STRING:
 
2412
        val = xmalloc(token->u.string.len + 1);
 
2413
        strcpy(val, token->u.string.string);
 
2414
        break;
 
2415
 
 
2416
    case PTOK_TAG:
 
2417
        val = formatValue(&token->u.tag, h, extensions, extCache,
 
2418
                          token->u.tag.justOne ? 0 : element);
 
2419
        break;
 
2420
 
 
2421
    case PTOK_COND:
 
2422
        if (token->u.cond.tag.ext ||
 
2423
            headerIsEntry(h, token->u.cond.tag.tag)) {
 
2424
            condFormat = token->u.cond.ifFormat;
 
2425
            condNumFormats = token->u.cond.numIfTokens;
 
2426
        } else {
 
2427
            condFormat = token->u.cond.elseFormat;
 
2428
            condNumFormats = token->u.cond.numElseTokens;
 
2429
        }
 
2430
 
 
2431
        alloced = condNumFormats * 20;
 
2432
        val = xmalloc(alloced ? alloced : 1);
 
2433
        *val = '\0';
 
2434
        len = 0;
 
2435
 
 
2436
        if (condFormat)
 
2437
        for (i = 0; i < condNumFormats; i++) {
 
2438
            thisItem = singleSprintf(h, condFormat + i, 
 
2439
                                     extensions, extCache, element);
 
2440
            thisItemLen = strlen(thisItem);
 
2441
            if ((thisItemLen + len) >= alloced) {
 
2442
                alloced = (thisItemLen + len) + 200;
 
2443
                val = xrealloc(val, alloced);   
 
2444
            }
 
2445
            strcat(val, thisItem);
 
2446
            len += thisItemLen;
 
2447
            thisItem = _free(thisItem);
 
2448
        }
 
2449
 
 
2450
        break;
 
2451
 
 
2452
    case PTOK_ARRAY:
 
2453
        numElements = -1;
 
2454
        for (i = 0; i < token->u.array.numTokens; i++) {
 
2455
            if (token->u.array.format[i].type != PTOK_TAG ||
 
2456
                token->u.array.format[i].u.tag.arrayCount ||
 
2457
                token->u.array.format[i].u.tag.justOne) continue;
 
2458
 
 
2459
            if (token->u.array.format[i].u.tag.ext) {
 
2460
                const void * data;
 
2461
                if (getExtension(h, token->u.array.format[i].u.tag.ext,
 
2462
                                 &type, &data, &numElements, 
 
2463
                                 extCache + 
 
2464
                                   token->u.array.format[i].u.tag.extNum))
 
2465
                     continue;
 
2466
            } else {
 
2467
                if (!headerGetEntry(h, token->u.array.format[i].u.tag.tag, 
 
2468
                                    &type, (void **) &val, &numElements))
 
2469
                    continue;
 
2470
                val = headerFreeData(val, type);
 
2471
            } 
 
2472
            /*@loopbreak@*/ break;
 
2473
        }
 
2474
 
 
2475
        if (numElements == -1) {
 
2476
            val = xstrdup("(none)");    /* XXX i18n? NO!, sez; gafton */
 
2477
        } else {
 
2478
            alloced = numElements * token->u.array.numTokens * 20;
 
2479
            val = xmalloc(alloced);
 
2480
            *val = '\0';
 
2481
            len = 0;
 
2482
 
 
2483
            for (j = 0; j < numElements; j++) {
 
2484
                for (i = 0; i < token->u.array.numTokens; i++) {
 
2485
                    thisItem = singleSprintf(h, token->u.array.format + i, 
 
2486
                                             extensions, extCache, j);
 
2487
                    thisItemLen = strlen(thisItem);
 
2488
                    if ((thisItemLen + len) >= alloced) {
 
2489
                        alloced = (thisItemLen + len) + 200;
 
2490
                        val = xrealloc(val, alloced);   
 
2491
                    }
 
2492
                    strcat(val, thisItem);
 
2493
                    len += thisItemLen;
 
2494
                    thisItem = _free(thisItem);
 
2495
                }
 
2496
            }
 
2497
        }
 
2498
           
 
2499
        break;
 
2500
    }
 
2501
 
 
2502
    return val;
 
2503
}
 
2504
 
 
2505
/**
 
2506
 */
 
2507
static /*@only@*/ extensionCache
 
2508
allocateExtensionCache(const headerSprintfExtension extensions)
 
2509
        /*@*/
 
2510
{
 
2511
    headerSprintfExtension ext = extensions;
 
2512
    int i = 0;
 
2513
 
 
2514
    while (ext->type != HEADER_EXT_LAST) {
 
2515
        i++;
 
2516
        if (ext->type == HEADER_EXT_MORE)
 
2517
            ext = ext->u.more;
 
2518
        else
 
2519
            ext++;
 
2520
    }
 
2521
 
 
2522
    return xcalloc(i, sizeof(struct extensionCache));
 
2523
}
 
2524
 
 
2525
/**
 
2526
 * @return              NULL always
 
2527
 */
 
2528
static /*@null@*/ extensionCache
 
2529
freeExtensionCache(const headerSprintfExtension extensions,
 
2530
                        /*@only@*/ extensionCache cache)
 
2531
        /*@*/
 
2532
{
 
2533
    headerSprintfExtension ext = extensions;
 
2534
    int i = 0;
 
2535
 
 
2536
    while (ext->type != HEADER_EXT_LAST) {
 
2537
        if (cache[i].freeit) cache[i].data = _free(cache[i].data);
 
2538
 
 
2539
        i++;
 
2540
        if (ext->type == HEADER_EXT_MORE)
 
2541
            ext = ext->u.more;
 
2542
        else
 
2543
            ext++;
 
2544
    }
 
2545
 
 
2546
    cache = _free(cache);
 
2547
    return NULL;
 
2548
}
 
2549
 
 
2550
char * headerSprintf(Header h, const char * fmt, 
 
2551
                const struct headerTagTableEntry_s * tabletags,
 
2552
                const struct headerSprintfExtension_s * extensions,
 
2553
                errmsg_t * errmsg)
 
2554
{
 
2555
    /*@-castexpose@*/   /* FIX: legacy API shouldn't change. */
 
2556
    headerSprintfExtension exts = (headerSprintfExtension) extensions;
 
2557
    headerTagTableEntry tags = (headerTagTableEntry) tabletags;
 
2558
    /*@=castexpose@*/
 
2559
    char * fmtString;
 
2560
    sprintfToken format;
 
2561
    int numTokens;
 
2562
    char * answer;
 
2563
    int answerLength;
 
2564
    int answerAlloced;
 
2565
    int i;
 
2566
    extensionCache extCache;
 
2567
 
 
2568
    /*fmtString = escapeString(fmt);*/
 
2569
    fmtString = xstrdup(fmt);
 
2570
   
 
2571
    if (parseFormat(fmtString, tags, exts, &format, &numTokens, 
 
2572
                    NULL, PARSER_BEGIN, errmsg)) {
 
2573
        fmtString = _free(fmtString);
 
2574
        return NULL;
 
2575
    }
 
2576
 
 
2577
    extCache = allocateExtensionCache(exts);
 
2578
 
 
2579
    answerAlloced = 1024;
 
2580
    answerLength = 0;
 
2581
    answer = xmalloc(answerAlloced);
 
2582
    *answer = '\0';
 
2583
 
 
2584
    for (i = 0; i < numTokens; i++) {
 
2585
        const char * piece;
 
2586
        int pieceLength;
 
2587
 
 
2588
        /*@-mods@*/
 
2589
        piece = singleSprintf(h, format + i, exts, extCache, 0);
 
2590
        /*@=mods@*/
 
2591
        if (piece) {
 
2592
            pieceLength = strlen(piece);
 
2593
            if ((answerLength + pieceLength) >= answerAlloced) {
 
2594
                while ((answerLength + pieceLength) >= answerAlloced) 
 
2595
                    answerAlloced += 1024;
 
2596
                answer = xrealloc(answer, answerAlloced);
 
2597
            }
 
2598
 
 
2599
            strcat(answer, piece);
 
2600
            answerLength += pieceLength;
 
2601
            piece = _free(piece);
 
2602
        }
 
2603
    }
 
2604
 
 
2605
    fmtString = _free(fmtString);
 
2606
    extCache = freeExtensionCache(exts, extCache);
 
2607
    format = _free(format);
 
2608
 
 
2609
    return answer;
 
2610
}
 
2611
 
 
2612
/**
 
2613
 */
 
2614
static char * octalFormat(int_32 type, hPTR_t data, 
 
2615
                char * formatPrefix, int padding, /*@unused@*/int element)
 
2616
        /*@modifies formatPrefix @*/
 
2617
{
 
2618
    char * val;
 
2619
 
 
2620
    if (type != RPM_INT32_TYPE) {
 
2621
        val = xstrdup(_("(not a number)"));
 
2622
    } else {
 
2623
        val = xmalloc(20 + padding);
 
2624
        strcat(formatPrefix, "o");
 
2625
        /*@-formatconst@*/
 
2626
        sprintf(val, formatPrefix, *((int_32 *) data));
 
2627
        /*@=formatconst@*/
 
2628
    }
 
2629
 
 
2630
    return val;
 
2631
}
 
2632
 
 
2633
/**
 
2634
 */
 
2635
static char * hexFormat(int_32 type, hPTR_t data, 
 
2636
                char * formatPrefix, int padding, /*@unused@*/int element)
 
2637
        /*@modifies formatPrefix @*/
 
2638
{
 
2639
    char * val;
 
2640
 
 
2641
    if (type != RPM_INT32_TYPE) {
 
2642
        val = xstrdup(_("(not a number)"));
 
2643
    } else {
 
2644
        val = xmalloc(20 + padding);
 
2645
        strcat(formatPrefix, "x");
 
2646
        /*@-formatconst@*/
 
2647
        sprintf(val, formatPrefix, *((int_32 *) data));
 
2648
        /*@=formatconst@*/
 
2649
    }
 
2650
 
 
2651
    return val;
 
2652
}
 
2653
 
 
2654
/**
 
2655
 */
 
2656
static char * realDateFormat(int_32 type, hPTR_t data, 
 
2657
                char * formatPrefix, int padding, /*@unused@*/int element,
 
2658
                const char * strftimeFormat)
 
2659
        /*@modifies formatPrefix @*/
 
2660
{
 
2661
    char * val;
 
2662
 
 
2663
    if (type != RPM_INT32_TYPE) {
 
2664
        val = xstrdup(_("(not a number)"));
 
2665
    } else {
 
2666
        struct tm * tstruct;
 
2667
        char buf[50];
 
2668
 
 
2669
        val = xmalloc(50 + padding);
 
2670
        strcat(formatPrefix, "s");
 
2671
 
 
2672
        /* this is important if sizeof(int_32) ! sizeof(time_t) */
 
2673
        {   time_t dateint = *((int_32 *) data);
 
2674
            tstruct = localtime(&dateint);
 
2675
        }
 
2676
        buf[0] = '\0';
 
2677
        if (tstruct)
 
2678
            (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
 
2679
        /*@-formatconst@*/
 
2680
        sprintf(val, formatPrefix, buf);
 
2681
        /*@=formatconst@*/
 
2682
    }
 
2683
 
 
2684
    return val;
 
2685
}
 
2686
 
 
2687
/**
 
2688
 */
 
2689
static char * dateFormat(int_32 type, hPTR_t data, 
 
2690
                         char * formatPrefix, int padding, int element)
 
2691
        /*@modifies formatPrefix @*/
 
2692
{
 
2693
    return realDateFormat(type, data, formatPrefix, padding, element, "%c");
 
2694
}
 
2695
 
 
2696
/**
 
2697
 */
 
2698
static char * dayFormat(int_32 type, hPTR_t data, 
 
2699
                         char * formatPrefix, int padding, int element)
 
2700
        /*@modifies formatPrefix @*/
 
2701
{
 
2702
    return realDateFormat(type, data, formatPrefix, padding, element, 
 
2703
                          "%a %b %d %Y");
 
2704
}
 
2705
 
 
2706
/**
 
2707
 */
 
2708
static char * shescapeFormat(int_32 type, hPTR_t data, 
 
2709
                char * formatPrefix, int padding, /*@unused@*/int element)
 
2710
        /*@modifies formatPrefix @*/
 
2711
{
 
2712
    char * result, * dst, * src, * buf;
 
2713
 
 
2714
    if (type == RPM_INT32_TYPE) {
 
2715
        result = xmalloc(padding + 20);
 
2716
        strcat(formatPrefix, "d");
 
2717
        /*@-formatconst@*/
 
2718
        sprintf(result, formatPrefix, *((int_32 *) data));
 
2719
        /*@=formatconst@*/
 
2720
    } else {
 
2721
        buf = alloca(strlen(data) + padding + 2);
 
2722
        strcat(formatPrefix, "s");
 
2723
        /*@-formatconst@*/
 
2724
        sprintf(buf, formatPrefix, data);
 
2725
        /*@=formatconst@*/
 
2726
 
 
2727
        result = dst = xmalloc(strlen(buf) * 4 + 3);
 
2728
        *dst++ = '\'';
 
2729
        for (src = buf; *src != '\0'; src++) {
 
2730
            if (*src == '\'') {
 
2731
                *dst++ = '\'';
 
2732
                *dst++ = '\\';
 
2733
                *dst++ = '\'';
 
2734
                *dst++ = '\'';
 
2735
            } else {
 
2736
                *dst++ = *src;
 
2737
            }
 
2738
        }
 
2739
        *dst++ = '\'';
 
2740
        *dst = '\0';
 
2741
 
 
2742
    }
 
2743
 
 
2744
    return result;
 
2745
}
 
2746
 
 
2747
const struct headerSprintfExtension_s headerDefaultFormats[] = {
 
2748
    { HEADER_EXT_FORMAT, "octal", { octalFormat } },
 
2749
    { HEADER_EXT_FORMAT, "hex", { hexFormat } },
 
2750
    { HEADER_EXT_FORMAT, "date", { dateFormat } },
 
2751
    { HEADER_EXT_FORMAT, "day", { dayFormat } },
 
2752
    { HEADER_EXT_FORMAT, "shescape", { shescapeFormat } },
 
2753
    { HEADER_EXT_LAST, NULL, { NULL } }
 
2754
};
 
2755
 
 
2756
void headerCopyTags(Header headerFrom, Header headerTo, hTAG_t tagstocopy)
 
2757
{
 
2758
    int * p;
 
2759
 
 
2760
    if (headerFrom == headerTo)
 
2761
        return;
 
2762
 
 
2763
    for (p = tagstocopy; *p != 0; p++) {
 
2764
        char *s;
 
2765
        int_32 type;
 
2766
        int_32 count;
 
2767
        if (headerIsEntry(headerTo, *p))
 
2768
            continue;
 
2769
        if (!headerGetEntryMinMemory(headerFrom, *p, &type,
 
2770
                                (hPTR_t *) &s, &count))
 
2771
            continue;
 
2772
        (void) headerAddEntry(headerTo, *p, type, s, count);
 
2773
        s = headerFreeData(s, type);
 
2774
    }
 
2775
}
 
2776
 
 
2777
/**
 
2778
 * Header tag iterator data structure.
 
2779
 */
 
2780
struct headerIteratorS {
 
2781
/*@unused@*/ Header h;          /*!< Header being iterated. */
 
2782
/*@unused@*/ int next_index;    /*!< Next tag index. */
 
2783
};
 
2784
 
 
2785
HeaderIterator headerFreeIterator(HeaderIterator hi)
 
2786
{
 
2787
    hi->h = headerFree(hi->h);
 
2788
    hi = _free(hi);
 
2789
    return hi;
 
2790
}
 
2791
 
 
2792
HeaderIterator headerInitIterator(Header h)
 
2793
{
 
2794
    HeaderIterator hi = xmalloc(sizeof(struct headerIteratorS));
 
2795
 
 
2796
    headerSort(h);
 
2797
 
 
2798
    hi->h = headerLink(h);
 
2799
    hi->next_index = 0;
 
2800
    return hi;
 
2801
}
 
2802
 
 
2803
int headerNextIterator(HeaderIterator hi,
 
2804
                 hTAG_t tag, hTYP_t type, hPTR_t * p, hCNT_t c)
 
2805
{
 
2806
    Header h = hi->h;
 
2807
    int slot = hi->next_index;
 
2808
    indexEntry entry = NULL;
 
2809
    int rc;
 
2810
 
 
2811
    for (slot = hi->next_index; slot < h->indexUsed; slot++) {
 
2812
        entry = h->index + slot;
 
2813
        if (!ENTRY_IS_REGION(entry))
 
2814
            break;
 
2815
    }
 
2816
    hi->next_index = slot;
 
2817
    if (entry == NULL || slot >= h->indexUsed)
 
2818
        return 0;
 
2819
    /*@-noeffect@*/     /* LCL: no clue */
 
2820
    hi->next_index++;
 
2821
    /*@=noeffect@*/
 
2822
 
 
2823
    if (tag)
 
2824
        *tag = entry->info.tag;
 
2825
 
 
2826
    rc = copyEntry(entry, type, p, c, 0);
 
2827
 
 
2828
    /* XXX 1 on success */
 
2829
    return ((rc == 1) ? 1 : 0);
 
2830
}
 
2831
static struct HV_s hdrVec1 = {
 
2832
    headerNew,
 
2833
    headerFree,
 
2834
    headerLink,
 
2835
    headerSort,
 
2836
    headerUnsort,
 
2837
    headerSizeof,
 
2838
    headerUnload,
 
2839
    headerReload,
 
2840
    headerCopy,
 
2841
    headerLoad,
 
2842
    headerCopyLoad,
 
2843
    headerRead,
 
2844
    headerWrite,
 
2845
    headerIsEntry,
 
2846
    headerFreeTag,
 
2847
    headerGetEntry,
 
2848
    headerGetEntryMinMemory,
 
2849
    headerAddEntry,
 
2850
    headerAppendEntry,
 
2851
    headerAddOrAppendEntry,
 
2852
    headerAddI18NString,
 
2853
    headerModifyEntry,
 
2854
    headerRemoveEntry,
 
2855
    headerSprintf,
 
2856
    headerCopyTags,
 
2857
    headerFreeIterator,
 
2858
    headerInitIterator,
 
2859
    headerNextIterator,
 
2860
    NULL, NULL,
 
2861
    1
 
2862
};
 
2863
 
 
2864
/*@-compmempass -redef@*/
 
2865
HV_t hdrVec = &hdrVec1;
 
2866
/*@=compmempass =redef@*/