rpm  4.5
header.c
Go to the documentation of this file.
1 
5 /* RPM - Copyright (C) 1995-2002 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 <rpmio_internal.h> /* XXX for fdGetOPath() */
16 #include <header_internal.h>
17 #include <rpmmacro.h>
18 
19 #include "debug.h"
20 
21 /*@unchecked@*/
22 int _hdr_debug = 0;
23 
24 /*@unchecked@*/
25 int _tagcache = 1; /* XXX Cache tag data persistently? */
26 
27 /*@access entryInfo @*/
28 /*@access indexEntry @*/
29 
30 /*@access sprintfTag @*/
31 /*@access sprintfToken @*/
32 /*@access HV_t @*/
33 
34 #define PARSER_BEGIN 0
35 #define PARSER_IN_ARRAY 1
36 #define PARSER_IN_EXPR 2
37 
40 /*@observer@*/ /*@unchecked@*/
41 static unsigned char header_magic[8] = {
42  0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
43 };
44 
48 /*@observer@*/ /*@unchecked@*/
49 static int typeAlign[16] = {
50  1,
51  1,
52  1,
53  2,
54  4,
55  8,
56  1,
57  1,
58  1,
59  1,
60  1,
61  1,
62  0,
63  0,
64  0,
65  0
66 };
67 
71 /*@observer@*/ /*@unchecked@*/
72 static int typeSizes[16] = {
73  0,
74  1,
75  1,
76  2,
77  4,
78  8,
79  -1,
80  1,
81  -1,
82  -1,
83  1,
84  1,
85  0,
86  0,
87  0,
88  0
89 };
90 
94 /*@unchecked@*/
95 static size_t headerMaxbytes = (32*1024*1024);
96 
101 #define hdrchkTags(_ntags) ((_ntags) & 0xffff0000)
102 
106 #define hdrchkType(_type) ((_type) < RPM_MIN_TYPE || (_type) > RPM_MAX_TYPE)
107 
112 #define hdrchkData(_nbytes) ((_nbytes) & 0xff000000)
113 
117 #define hdrchkAlign(_type, _off) ((_off) & (typeAlign[_type]-1))
118 
122 #define hdrchkRange(_dl, _off) ((_off) < 0 || (_off) > (_dl))
123 
124 /*@observer@*/ /*@unchecked@*/
125 HV_t hdrVec; /* forward reference */
126 
133 static /*@null@*/
134 void * headerGetStats(/*@unused@*/ Header h, /*@unused@*/ int opx)
135  /*@*/
136 {
137  rpmop op = NULL;
138  return op;
139 }
140 
146 static
148  /*@modifies h @*/
149 {
150 /*@-nullret@*/
151  if (h == NULL) return NULL;
152 /*@=nullret@*/
153 
154  h->nrefs++;
155 /*@-modfilesys@*/
156 if (_hdr_debug)
157 fprintf(stderr, "--> h %p ++ %d at %s:%u\n", h, h->nrefs, __FILE__, __LINE__);
158 /*@=modfilesys@*/
159 
160  /*@-refcounttrans @*/
161  return h;
162  /*@=refcounttrans @*/
163 }
164 
170 static /*@null@*/
171 Header headerUnlink(/*@killref@*/ /*@null@*/ Header h)
172  /*@modifies h @*/
173 {
174  if (h == NULL) return NULL;
175 /*@-modfilesys@*/
176 if (_hdr_debug)
177 fprintf(stderr, "--> h %p -- %d at %s:%u\n", h, h->nrefs, __FILE__, __LINE__);
178 /*@=modfilesys@*/
179  h->nrefs--;
180  return NULL;
181 }
182 
188 static /*@null@*/
189 Header headerFree(/*@killref@*/ /*@null@*/ Header h)
190  /*@modifies h @*/
191 {
192  (void) headerUnlink(h);
193 
194  /*@-usereleased@*/
195  if (h == NULL || h->nrefs > 0)
196  return NULL; /* XXX return previous header? */
197 
198  if (h->index) {
199  indexEntry entry = h->index;
200  int i;
201  for (i = 0; i < h->indexUsed; i++, entry++) {
202  if ((h->flags & HEADERFLAG_ALLOCATED) && ENTRY_IS_REGION(entry)) {
203  if (entry->length > 0) {
204  int_32 * ei = entry->data;
205  if ((ei - 2) == h->blob) h->blob = _free(h->blob);
206  entry->data = NULL;
207  }
208  } else if (!ENTRY_IN_REGION(entry)) {
209  entry->data = _free(entry->data);
210  }
211  entry->data = NULL;
212  }
213  h->index = _free(h->index);
214  }
215  h->origin = _free(h->origin);
216  h->baseurl = _free(h->baseurl);
217  h->digest = _free(h->digest);
218 
219  /*@-refcounttrans@*/ h = _free(h); /*@=refcounttrans@*/
220  return h;
221  /*@=usereleased@*/
222 }
223 
228 static
230  /*@*/
231 {
232  Header h = xcalloc(1, sizeof(*h));
233 
234  /*@-assignexpose@*/
235  h->hv = *hdrVec; /* structure assignment */
236  /*@=assignexpose@*/
237  h->blob = NULL;
238  h->origin = NULL;
239  h->baseurl = NULL;
240  h->digest = NULL;
241  h->instance = 0;
243  h->indexUsed = 0;
244  h->flags |= HEADERFLAG_SORTED;
245 
246  h->index = (h->indexAlloced
247  ? xcalloc(h->indexAlloced, sizeof(*h->index))
248  : NULL);
249 
250  h->nrefs = 0;
251  /*@-globstate -observertrans @*/
252  return headerLink(h);
253  /*@=globstate =observertrans @*/
254 }
255 
258 static int indexCmp(const void * avp, const void * bvp)
259  /*@*/
260 {
261  /*@-castexpose@*/
262  indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
263  /*@=castexpose@*/
264  return (ap->info.tag - bp->info.tag);
265 }
266 
271 static
273  /*@modifies h @*/
274 {
275  if (!(h->flags & HEADERFLAG_SORTED)) {
276  qsort(h->index, h->indexUsed, sizeof(*h->index), indexCmp);
277  h->flags |= HEADERFLAG_SORTED;
278  }
279 }
280 
283 static int offsetCmp(const void * avp, const void * bvp) /*@*/
284 {
285  /*@-castexpose@*/
286  indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
287  /*@=castexpose@*/
288  int rc = (ap->info.offset - bp->info.offset);
289 
290  if (rc == 0) {
291  /* Within a region, entries sort by address. Added drips sort by tag. */
292  if (ap->info.offset < 0)
293  rc = (((char *)ap->data) - ((char *)bp->data));
294  else
295  rc = (ap->info.tag - bp->info.tag);
296  }
297  return rc;
298 }
299 
304 static
306  /*@modifies h @*/
307 {
308  qsort(h->index, h->indexUsed, sizeof(*h->index), offsetCmp);
309 }
310 
317 static
318 unsigned int headerSizeof(/*@null@*/ Header h, enum hMagic magicp)
319  /*@modifies h @*/
320 {
321  indexEntry entry;
322  unsigned int size = 0;
323  unsigned int pad = 0;
324  int i;
325 
326  if (h == NULL)
327  return size;
328 
329  headerSort(h);
330 
331  switch (magicp) {
332  case HEADER_MAGIC_YES:
333  size += sizeof(header_magic);
334  break;
335  case HEADER_MAGIC_NO:
336  break;
337  }
338 
339  /*@-sizeoftype@*/
340  size += 2 * sizeof(int_32); /* count of index entries */
341  /*@=sizeoftype@*/
342 
343  for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
344  unsigned diff;
345  rpmTagType type;
346 
347  /* Regions go in as is ... */
348  if (ENTRY_IS_REGION(entry)) {
349  size += entry->length;
350  /* XXX Legacy regions do not include the region tag and data. */
351  /*@-sizeoftype@*/
352  if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
353  size += sizeof(struct entryInfo_s) + entry->info.count;
354  /*@=sizeoftype@*/
355  continue;
356  }
357 
358  /* ... and region elements are skipped. */
359  if (entry->info.offset < 0)
360  continue;
361 
362  /* Alignment */
363  type = entry->info.type;
364  if (typeSizes[type] > 1) {
365  diff = typeSizes[type] - (size % typeSizes[type]);
366  if (diff != typeSizes[type]) {
367  size += diff;
368  pad += diff;
369  }
370  }
371 
372  /*@-sizeoftype@*/
373  size += sizeof(struct entryInfo_s) + entry->length;
374  /*@=sizeoftype@*/
375  }
376 
377  return size;
378 }
379 
390  int onDisk, /*@null@*/ rpmTagData * pend)
391  /*@*/
392 {
393  const unsigned char * s = (*p).ui8p;
394  const unsigned char * se = (pend ? (*pend).ui8p : NULL);
395  int length = 0;
396 
397  switch (type) {
398  case RPM_STRING_TYPE:
399  if (count != 1)
400  return -1;
401  while (*s++) {
402  if (se && s > se)
403  return -1;
404  length++;
405  }
406  length++; /* count nul terminator too. */
407  break;
408  /* These are like RPM_STRING_TYPE, except they're *always* an array */
409  /* Compute sum of length of all strings, including nul terminators */
410  case RPM_I18NSTRING_TYPE:
412  if (onDisk) {
413  while (count--) {
414  length++; /* count nul terminator too */
415  while (*s++) {
416  if (se && s > se)
417  return -1; /* XXX change errret, use size_t */
418  length++;
419  }
420  }
421  } else {
422  const char ** av = (*p).argv;
423  while (count--) {
424  /* add one for null termination */
425  length += strlen(*av++) + 1;
426  }
427  }
428  break;
429  default:
430  if (typeSizes[type] == -1)
431  return -1; /* XXX change errret, use size_t */
432  length = typeSizes[(type & 0xf)] * count;
433  if (length < 0 || (se && (s + length) > se))
434  return -1; /* XXX change errret, use size_t */
435  break;
436  }
437 
438  return length;
439 }
440 
467 static int regionSwab(/*@null@*/ indexEntry entry, int il, int dl,
468  entryInfo pe,
469  unsigned char * dataStart,
470  /*@null@*/ const unsigned char * dataEnd,
471  int regionid)
472  /*@modifies *entry, *dataStart @*/
473 {
474  rpmTagData p;
475  rpmTagData pend;
476  unsigned char * tprev = NULL;
477  unsigned char * t = NULL;
478  int tdel = 0;
479  int tl = dl;
480  struct indexEntry_s ieprev;
481 
482  memset(&ieprev, 0, sizeof(ieprev));
483  for (; il > 0; il--, pe++) {
484  struct indexEntry_s ie;
485  rpmTagType type;
486 
487  ie.info.tag = ntohl(pe->tag);
488  ie.info.type = ntohl(pe->type);
489  ie.info.count = ntohl(pe->count);
490  ie.info.offset = ntohl(pe->offset);
491 
492  if (hdrchkType(ie.info.type))
493  return -1;
494  if (hdrchkData(ie.info.count))
495  return -1;
496  if (hdrchkData(ie.info.offset))
497  return -1;
498  if (hdrchkAlign(ie.info.type, ie.info.offset))
499  return -1;
500 
501  ie.data = t = dataStart + ie.info.offset;
502  if (dataEnd && t >= dataEnd)
503  return -1;
504 
505  p.ptr = ie.data;
506  pend.ui8p = (unsigned char *) dataEnd;
507  ie.length = dataLength(ie.info.type, &p, ie.info.count, 1, &pend);
508  if (ie.length < 0 || hdrchkData(ie.length))
509  return -1;
510 
511  ie.rdlen = 0;
512 
513  if (entry) {
514  ie.info.offset = regionid;
515  *entry = ie; /* structure assignment */
516  entry++;
517  }
518 
519  /* Alignment */
520  type = ie.info.type;
521  if (typeSizes[type] > 1) {
522  unsigned diff;
523  diff = typeSizes[type] - (dl % typeSizes[type]);
524  if (diff != typeSizes[type]) {
525  dl += diff;
526  if (ieprev.info.type == RPM_I18NSTRING_TYPE)
527  ieprev.length += diff;
528  }
529  }
530  tdel = (tprev ? (t - tprev) : 0);
531  if (ieprev.info.type == RPM_I18NSTRING_TYPE)
532  tdel = ieprev.length;
533 
534  if (ie.info.tag >= HEADER_I18NTABLE) {
535  tprev = t;
536  } else {
537  tprev = dataStart;
538  /* XXX HEADER_IMAGE tags don't include region sub-tag. */
539  /*@-sizeoftype@*/
540  if (ie.info.tag == HEADER_IMAGE)
541  tprev -= REGION_TAG_COUNT;
542  /*@=sizeoftype@*/
543  }
544 
545  /* Perform endian conversions */
546  switch (ntohl(pe->type)) {
547  case RPM_INT64_TYPE:
548  { int_64 * it = (int_64 *)t;
549  int_32 b[2];
550  for (; ie.info.count > 0; ie.info.count--, it += 1) {
551  if (dataEnd && ((unsigned char *)it) >= dataEnd)
552  return -1;
553  b[1] = htonl(((int_32 *)it)[0]);
554  b[0] = htonl(((int_32 *)it)[1]);
555  if (b[1] != ((int_32 *)it)[0])
556  memcpy(it, b, sizeof(b));
557  }
558  t = (unsigned char *) it;
559  } /*@switchbreak@*/ break;
560  case RPM_INT32_TYPE:
561  { int_32 * it = (int_32 *)t;
562  for (; ie.info.count > 0; ie.info.count--, it += 1) {
563  if (dataEnd && ((unsigned char *)it) >= dataEnd)
564  return -1;
565  *it = htonl(*it);
566  }
567  t = (unsigned char *) it;
568  } /*@switchbreak@*/ break;
569  case RPM_INT16_TYPE:
570  { int_16 * it = (int_16 *) t;
571  for (; ie.info.count > 0; ie.info.count--, it += 1) {
572  if (dataEnd && ((unsigned char *)it) >= dataEnd)
573  return -1;
574  *it = htons(*it);
575  }
576  t = (unsigned char *) it;
577  } /*@switchbreak@*/ break;
578  default:
579  t += ie.length;
580  /*@switchbreak@*/ break;
581  }
582 
583  dl += ie.length;
584  if (dataEnd && (dataStart + dl) > dataEnd) return -1;
585  tl += tdel;
586  ieprev = ie; /* structure assignment */
587 
588  }
589  tdel = (tprev ? (t - tprev) : 0);
590  tl += tdel;
591 
592  /* XXX
593  * There are two hacks here:
594  * 1) tl is 16b (i.e. REGION_TAG_COUNT) short while doing headerReload().
595  * 2) the 8/98 rpm bug with inserting i18n tags needs to use tl, not dl.
596  */
597  /*@-sizeoftype@*/
598  if (tl+REGION_TAG_COUNT == dl)
599  tl += REGION_TAG_COUNT;
600  /*@=sizeoftype@*/
601 
602  return dl;
603 }
604 
610 static /*@only@*/ /*@null@*/ void * doHeaderUnload(Header h,
611  /*@out@*/ size_t * lenp)
612  /*@modifies h, *lenp @*/
613  /*@requires maxSet(lenp) >= 0 @*/
614  /*@ensures maxRead(result) == (*lenp) @*/
615 {
616  void * sw;
617  int_32 * ei = NULL;
618  entryInfo pe;
619  unsigned char * dataStart;
620  unsigned char * te;
621  unsigned pad;
622  unsigned len = 0;
623  int_32 il = 0;
624  int_32 dl = 0;
625  indexEntry entry;
626  rpmTagType type;
627  int i;
628  int drlen, ndribbles;
629  int driplen, ndrips;
630  int legacy = 0;
631 
632  if ((sw = headerGetStats(h, 18)) != NULL) /* RPMTS_OP_HDRLOAD */
633  (void) rpmswEnter(sw, 0);
634 
635  /* Sort entries by (offset,tag). */
636  headerUnsort(h);
637 
638  /* Compute (il,dl) for all tags, including those deleted in region. */
639  pad = 0;
640  drlen = ndribbles = driplen = ndrips = 0;
641  for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
642  if (ENTRY_IS_REGION(entry)) {
643  int_32 rdl = -entry->info.offset; /* negative offset */
644  int_32 ril = rdl/sizeof(*pe);
645  int rid = entry->info.offset;
646 
647  il += ril;
648  dl += entry->rdlen + entry->info.count;
649  /* XXX Legacy regions do not include the region tag and data. */
650  if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
651  il += 1;
652 
653  /* Skip rest of entries in region, but account for dribbles. */
654  for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
655  if (entry->info.offset <= rid)
656  /*@innercontinue@*/ continue;
657 
658  /* Alignment */
659  type = entry->info.type;
660  if (typeSizes[type] > 1) {
661  unsigned diff;
662  diff = typeSizes[type] - (dl % typeSizes[type]);
663  if (diff != typeSizes[type]) {
664  drlen += diff;
665  pad += diff;
666  dl += diff;
667  }
668  }
669 
670  ndribbles++;
671  il++;
672  drlen += entry->length;
673  dl += entry->length;
674  }
675  i--;
676  entry--;
677  continue;
678  }
679 
680  /* Ignore deleted drips. */
681  if (entry->data == NULL || entry->length <= 0)
682  continue;
683 
684  /* Alignment */
685  type = entry->info.type;
686  if (typeSizes[type] > 1) {
687  unsigned diff;
688  diff = typeSizes[type] - (dl % typeSizes[type]);
689  if (diff != typeSizes[type]) {
690  driplen += diff;
691  pad += diff;
692  dl += diff;
693  } else
694  diff = 0;
695  }
696 
697  ndrips++;
698  il++;
699  driplen += entry->length;
700  dl += entry->length;
701  }
702 
703  /* Sanity checks on header intro. */
704  if (hdrchkTags(il) || hdrchkData(dl))
705  goto errxit;
706 
707  len = sizeof(il) + sizeof(dl) + (il * sizeof(*pe)) + dl;
708 
709  ei = xmalloc(len);
710  ei[0] = htonl(il);
711  ei[1] = htonl(dl);
712 
713  pe = (entryInfo) &ei[2];
714  dataStart = te = (unsigned char *) (pe + il);
715 
716  pad = 0;
717  for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
718  const char * src;
719  unsigned char *t;
720  int count;
721  int rdlen;
722 
723  if (entry->data == NULL || entry->length <= 0)
724  continue;
725 
726  t = te;
727  pe->tag = htonl(entry->info.tag);
728  pe->type = htonl(entry->info.type);
729  pe->count = htonl(entry->info.count);
730 
731  if (ENTRY_IS_REGION(entry)) {
732  int_32 rdl = -entry->info.offset; /* negative offset */
733  int_32 ril = rdl/sizeof(*pe) + ndribbles;
734  int rid = entry->info.offset;
735 
736  src = (char *)entry->data;
737  rdlen = entry->rdlen;
738 
739  /* XXX Legacy regions do not include the region tag and data. */
740  if (i == 0 && (h->flags & HEADERFLAG_LEGACY)) {
741  int_32 stei[4];
742 
743  legacy = 1;
744  memcpy(pe+1, src, rdl);
745  memcpy(te, src + rdl, rdlen);
746  te += rdlen;
747 
748  pe->offset = htonl(te - dataStart);
749  stei[0] = pe->tag;
750  stei[1] = pe->type;
751  stei[2] = htonl(-rdl-entry->info.count);
752  stei[3] = pe->count;
753  memcpy(te, stei, entry->info.count);
754  te += entry->info.count;
755  ril++;
756  rdlen += entry->info.count;
757 
758  count = regionSwab(NULL, ril, 0, pe, t, NULL, 0);
759  if (count != rdlen)
760  goto errxit;
761 
762  } else {
763 
764  memcpy(pe+1, src + sizeof(*pe), ((ril-1) * sizeof(*pe)));
765  memcpy(te, src + (ril * sizeof(*pe)), rdlen+entry->info.count+drlen);
766  te += rdlen;
767  { /*@-castexpose@*/
768  entryInfo se = (entryInfo)src;
769  /*@=castexpose@*/
770  int off = ntohl(se->offset);
771  pe->offset = (off) ? htonl(te - dataStart) : htonl(off);
772  }
773  te += entry->info.count + drlen;
774 
775  count = regionSwab(NULL, ril, 0, pe, t, NULL, 0);
776  if (count != (rdlen + entry->info.count + drlen))
777  goto errxit;
778  }
779 
780  /* Skip rest of entries in region. */
781  while (i < h->indexUsed && entry->info.offset <= rid+1) {
782  i++;
783  entry++;
784  }
785  i--;
786  entry--;
787  pe += ril;
788  continue;
789  }
790 
791  /* Ignore deleted drips. */
792  if (entry->data == NULL || entry->length <= 0)
793  continue;
794 
795  /* Alignment */
796  type = entry->info.type;
797  if (typeSizes[type] > 1) {
798  unsigned diff;
799  diff = typeSizes[type] - ((te - dataStart) % typeSizes[type]);
800  if (diff != typeSizes[type]) {
801  memset(te, 0, diff);
802  te += diff;
803  pad += diff;
804  }
805  }
806 
807  pe->offset = htonl(te - dataStart);
808 
809  /* copy data w/ endian conversions */
810  switch (entry->info.type) {
811  case RPM_INT64_TYPE:
812  { int_32 b[2];
813  count = entry->info.count;
814  src = entry->data;
815  while (count--) {
816  b[1] = htonl(((int_32 *)src)[0]);
817  b[0] = htonl(((int_32 *)src)[1]);
818  if (b[1] == ((int_32 *)src)[0])
819  memcpy(te, src, sizeof(b));
820  else
821  memcpy(te, b, sizeof(b));
822  te += sizeof(b);
823  src += sizeof(b);
824  }
825  } /*@switchbreak@*/ break;
826 
827  case RPM_INT32_TYPE:
828  count = entry->info.count;
829  src = entry->data;
830  while (count--) {
831  *((int_32 *)te) = htonl(*((int_32 *)src));
832  /*@-sizeoftype@*/
833  te += sizeof(int_32);
834  src += sizeof(int_32);
835  /*@=sizeoftype@*/
836  }
837  /*@switchbreak@*/ break;
838 
839  case RPM_INT16_TYPE:
840  count = entry->info.count;
841  src = entry->data;
842  while (count--) {
843  *((int_16 *)te) = htons(*((int_16 *)src));
844  /*@-sizeoftype@*/
845  te += sizeof(int_16);
846  src += sizeof(int_16);
847  /*@=sizeoftype@*/
848  }
849  /*@switchbreak@*/ break;
850 
851  default:
852  memcpy(te, entry->data, entry->length);
853  te += entry->length;
854  /*@switchbreak@*/ break;
855  }
856  pe++;
857  }
858 
859  /* Insure that there are no memcpy underruns/overruns. */
860  if (((unsigned char *)pe) != dataStart)
861  goto errxit;
862  if ((((unsigned char *)ei)+len) != te)
863  goto errxit;
864 
865  if (lenp)
866  *lenp = len;
867 
868  h->flags &= ~HEADERFLAG_SORTED;
869  headerSort(h);
870 
871  if (sw != NULL) (void) rpmswExit(sw, len);
872 
873  return (void *) ei;
874 
875 errxit:
876  if (sw != NULL) (void) rpmswExit(sw, len);
877  /*@-usereleased@*/
878  ei = _free(ei);
879  /*@=usereleased@*/
880  return (void *) ei;
881 }
882 
888 static /*@only@*/ /*@null@*/
890  /*@modifies h @*/
891 {
892  size_t length;
893  void * uh = doHeaderUnload(h, &length);
894  return uh;
895 }
896 
904 static /*@null@*/
905 indexEntry findEntry(/*@null@*/ Header h, int_32 tag, rpmTagType type)
906  /*@modifies h @*/
907 {
908  indexEntry entry, entry2, last;
909  struct indexEntry_s key;
910 
911  if (h == NULL) return NULL;
912  if (!(h->flags & HEADERFLAG_SORTED)) headerSort(h);
913 
914  key.info.tag = tag;
915 
916  entry2 = entry =
917  bsearch(&key, h->index, h->indexUsed, sizeof(*h->index), indexCmp);
918  if (entry == NULL)
919  return NULL;
920 
921  if (type == RPM_NULL_TYPE)
922  return entry;
923 
924  /* look backwards */
925  while (entry->info.tag == tag && entry->info.type != type &&
926  entry > h->index) entry--;
927 
928  if (entry->info.tag == tag && entry->info.type == type)
929  return entry;
930 
931  last = h->index + h->indexUsed;
932  /*@-usereleased@*/ /* FIX: entry2 = entry. Code looks bogus as well. */
933  while (entry2->info.tag == tag && entry2->info.type != type &&
934  entry2 < last) entry2++;
935  /*@=usereleased@*/
936 
937  if (entry->info.tag == tag && entry->info.type == type)
938  return entry;
939 
940  return NULL;
941 }
942 
952 static
954  /*@modifies h @*/
955 {
956  indexEntry last = h->index + h->indexUsed;
957  indexEntry entry, first;
958  int ne;
959 
960  entry = findEntry(h, tag, RPM_NULL_TYPE);
961  if (!entry) return 1;
962 
963  /* Make sure entry points to the first occurence of this tag. */
964  while (entry > h->index && (entry - 1)->info.tag == tag)
965  entry--;
966 
967  /* Free data for tags being removed. */
968  for (first = entry; first < last; first++) {
969  void * data;
970  if (first->info.tag != tag)
971  break;
972  data = first->data;
973  first->data = NULL;
974  first->length = 0;
975  if (ENTRY_IN_REGION(first))
976  continue;
977  data = _free(data);
978  }
979 
980  ne = (first - entry);
981  if (ne > 0) {
982  h->indexUsed -= ne;
983  ne = last - first;
984  if (ne > 0)
985  memmove(entry, first, (ne * sizeof(*entry)));
986  }
987 
988  return 0;
989 }
990 
996 static /*@null@*/
997 Header headerLoad(/*@kept@*/ void * uh)
998  /*@modifies uh @*/
999 {
1000  void * sw = NULL;
1001  int_32 * ei = (int_32 *) uh;
1002  int_32 il = ntohl(ei[0]); /* index length */
1003  int_32 dl = ntohl(ei[1]); /* data length */
1004  /*@-sizeoftype@*/
1005  size_t pvlen = sizeof(il) + sizeof(dl) +
1006  (il * sizeof(struct entryInfo_s)) + dl;
1007  /*@=sizeoftype@*/
1008  void * pv = uh;
1009  Header h = NULL;
1010  entryInfo pe;
1011  unsigned char * dataStart;
1012  unsigned char * dataEnd;
1013  indexEntry entry;
1014  int rdlen;
1015  int i;
1016 
1017  /* Sanity checks on header intro. */
1018  if (hdrchkTags(il) || hdrchkData(dl))
1019  goto errxit;
1020 
1021  ei = (int_32 *) pv;
1022  /*@-castexpose@*/
1023  pe = (entryInfo) &ei[2];
1024  /*@=castexpose@*/
1025  dataStart = (unsigned char *) (pe + il);
1026  dataEnd = dataStart + dl;
1027 
1028  h = xcalloc(1, sizeof(*h));
1029  if ((sw = headerGetStats(h, 18)) != NULL) /* RPMTS_OP_HDRLOAD */
1030  (void) rpmswEnter(sw, 0);
1031  /*@-assignexpose@*/
1032  h->hv = *hdrVec; /* structure assignment */
1033  /*@=assignexpose@*/
1034  /*@-assignexpose -kepttrans@*/
1035  h->blob = uh;
1036  /*@=assignexpose =kepttrans@*/
1037  h->indexAlloced = il + 1;
1038  h->indexUsed = il;
1039  h->index = xcalloc(h->indexAlloced, sizeof(*h->index));
1040  h->flags |= HEADERFLAG_SORTED;
1041  h->nrefs = 0;
1042  h = headerLink(h);
1043 
1044  entry = h->index;
1045  i = 0;
1046  if (!(htonl(pe->tag) < HEADER_I18NTABLE)) {
1047  h->flags |= HEADERFLAG_LEGACY;
1048  entry->info.type = REGION_TAG_TYPE;
1049  entry->info.tag = HEADER_IMAGE;
1050  /*@-sizeoftype@*/
1051  entry->info.count = REGION_TAG_COUNT;
1052  /*@=sizeoftype@*/
1053  entry->info.offset = ((unsigned char *)pe - dataStart); /* negative offset */
1054 
1055  /*@-assignexpose@*/
1056  entry->data = pe;
1057  /*@=assignexpose@*/
1058  entry->length = pvlen - sizeof(il) - sizeof(dl);
1059  rdlen = regionSwab(entry+1, il, 0, pe, dataStart, dataEnd, entry->info.offset);
1060 #if 0 /* XXX don't check, the 8/98 i18n bug fails here. */
1061  if (rdlen != dl)
1062  goto errxit;
1063 #endif
1064  entry->rdlen = rdlen;
1065  entry++;
1066  h->indexUsed++;
1067  } else {
1068  int_32 rdl;
1069  int_32 ril;
1070 
1071  h->flags &= ~HEADERFLAG_LEGACY;
1072 
1073  entry->info.type = htonl(pe->type);
1074  entry->info.count = htonl(pe->count);
1075 
1076  if (hdrchkType(entry->info.type))
1077  goto errxit;
1078  if (hdrchkTags(entry->info.count))
1079  goto errxit;
1080 
1081  { int off = ntohl(pe->offset);
1082 
1083  if (hdrchkData(off))
1084  goto errxit;
1085  if (off) {
1086 /*@-sizeoftype@*/
1087  size_t nb = REGION_TAG_COUNT;
1088 /*@=sizeoftype@*/
1089  int_32 * stei = memcpy(alloca(nb), dataStart + off, nb);
1090  rdl = -ntohl(stei[2]); /* negative offset */
1091  ril = rdl/sizeof(*pe);
1092  if (hdrchkTags(ril) || hdrchkData(rdl))
1093  goto errxit;
1094  entry->info.tag = htonl(pe->tag);
1095  } else {
1096  ril = il;
1097  /*@-sizeoftype@*/
1098  rdl = (ril * sizeof(struct entryInfo_s));
1099  /*@=sizeoftype@*/
1100  entry->info.tag = HEADER_IMAGE;
1101  }
1102  }
1103  entry->info.offset = -rdl; /* negative offset */
1104 
1105  /*@-assignexpose@*/
1106  entry->data = pe;
1107  /*@=assignexpose@*/
1108  entry->length = pvlen - sizeof(il) - sizeof(dl);
1109  rdlen = regionSwab(entry+1, ril-1, 0, pe+1, dataStart, dataEnd, entry->info.offset);
1110  if (rdlen < 0)
1111  goto errxit;
1112  entry->rdlen = rdlen;
1113 
1114  if (ril < h->indexUsed) {
1115  indexEntry newEntry = entry + ril;
1116  int ne = (h->indexUsed - ril);
1117  int rid = entry->info.offset+1;
1118  int rc;
1119 
1120  /* Load dribble entries from region. */
1121  rc = regionSwab(newEntry, ne, 0, pe+ril, dataStart, dataEnd, rid);
1122  if (rc < 0)
1123  goto errxit;
1124  rdlen += rc;
1125 
1126  { indexEntry firstEntry = newEntry;
1127  int save = h->indexUsed;
1128  int j;
1129 
1130  /* Dribble entries replace duplicate region entries. */
1131  h->indexUsed -= ne;
1132  for (j = 0; j < ne; j++, newEntry++) {
1133  (void) headerRemoveEntry(h, newEntry->info.tag);
1134  if (newEntry->info.tag == HEADER_BASENAMES)
1136  }
1137 
1138  /* If any duplicate entries were replaced, move new entries down. */
1139  if (h->indexUsed < (save - ne)) {
1140  memmove(h->index + h->indexUsed, firstEntry,
1141  (ne * sizeof(*entry)));
1142  }
1143  h->indexUsed += ne;
1144  }
1145  }
1146  }
1147 
1148  h->flags &= ~HEADERFLAG_SORTED;
1149  headerSort(h);
1150 
1151  if (sw != NULL) (void) rpmswExit(sw, pvlen);
1152 
1153  /*@-globstate -observertrans @*/
1154  return h;
1155  /*@=globstate =observertrans @*/
1156 
1157 errxit:
1158  if (sw != NULL) (void) rpmswExit(sw, pvlen);
1159  /*@-usereleased@*/
1160  if (h) {
1161  h->index = _free(h->index);
1162  /*@-refcounttrans@*/
1163  h = _free(h);
1164  /*@=refcounttrans@*/
1165  }
1166  /*@=usereleased@*/
1167  /*@-refcounttrans -globstate@*/
1168  return h;
1169  /*@=refcounttrans =globstate@*/
1170 }
1171 
1177 static /*@observer@*/ /*@null@*/
1178 const char * headerGetOrigin(/*@null@*/ Header h)
1179  /*@*/
1180 {
1181  return (h != NULL ? h->origin : NULL);
1182 }
1183 
1190 static
1191 int headerSetOrigin(/*@null@*/ Header h, const char * origin)
1192  /*@modifies h @*/
1193 {
1194  if (h != NULL) {
1195  h->origin = _free(h->origin);
1196  h->origin = xstrdup(origin);
1197  }
1198  return 0;
1199 }
1200 
1201 const char * headerGetBaseURL(Header h); /* XXX keep GCC quiet */
1202 const char * headerGetBaseURL(Header h)
1203 {
1204  return (h != NULL ? h->baseurl : NULL);
1205 }
1206 
1207 int headerSetBaseURL(Header h, const char * baseurl); /* XXX keep GCC quiet */
1208 int headerSetBaseURL(Header h, const char * baseurl)
1209 {
1210  if (h != NULL) {
1211  h->baseurl = _free(h->baseurl);
1212  h->baseurl = xstrdup(baseurl);
1213  }
1214  return 0;
1215 }
1216 
1217 struct stat * headerGetStatbuf(Header h); /* XXX keep GCC quiet */
1218 struct stat * headerGetStatbuf(Header h)
1219 {
1220  return &h->sb;
1221 }
1222 
1223 int headerSetStatbuf(Header h, struct stat * st); /* XXX keep GCC quiet */
1224 int headerSetStatbuf(Header h, struct stat * st)
1225 {
1226  if (h != NULL && st != NULL)
1227  memcpy(&h->sb, st, sizeof(h->sb));
1228  return 0;
1229 }
1230 
1231 const char * headerGetDigest(Header h); /* XXX keep GCC quiet. */
1232 const char * headerGetDigest(Header h)
1233 {
1234  return (h != NULL ? h->digest : NULL);
1235 }
1236 
1237 int headerSetDigest(Header h, const char * digest); /* XXX keep GCC quiet */
1238 int headerSetDigest(Header h, const char * digest)
1239 {
1240  if (h != NULL) {
1241  h->digest = _free(h->digest);
1242  h->digest = xstrdup(digest);
1243  }
1244  return 0;
1245 }
1246 
1247 static
1248 uint32_t headerGetInstance(/*@null@*/ Header h)
1249  /*@*/
1250 {
1251  return (h != NULL ? h->instance : 0);
1252 }
1253 
1254 static
1255 uint32_t headerSetInstance(/*@null@*/ Header h, uint32_t instance)
1256  /*@modifies h @*/
1257 {
1258  if (h != NULL)
1259  h->instance = instance;
1260  return 0;
1261 }
1262 
1263 uint32_t headerGetStartOff(Header h); /* XXX keep GCC quiet */
1265 {
1266  return (h != NULL ? h->startoff : 0);
1267 }
1268 
1269 uint32_t headerSetStartOff(Header h, uint32_t startoff); /* XXX keep GCC quiet */
1270 uint32_t headerSetStartOff(Header h, uint32_t startoff)
1271 {
1272  if (h != NULL)
1273  h->startoff = startoff;
1274  return 0;
1275 }
1276 
1277 uint32_t headerGetEndOff(Header h); /* XXX keep GCC quiet */
1279 {
1280  return (h != NULL ? h->endoff : 0);
1281 }
1282 
1283 uint32_t headerSetEndOff(Header h, uint32_t endoff); /* XXX keep GCC quiet. */
1284 uint32_t headerSetEndOff(Header h, uint32_t endoff)
1285 {
1286  if (h != NULL)
1287  h->endoff = endoff;
1288  return 0;
1289 }
1290 
1298 static /*@null@*/
1299 Header headerReload(/*@only@*/ Header h, int tag)
1300  /*@modifies h @*/
1301 {
1302  Header nh;
1303  size_t length;
1304  void * uh;
1305  const char * origin = (h->origin != NULL ? xstrdup(h->origin) : NULL);
1306  const char * baseurl = (h->baseurl != NULL ? xstrdup(h->baseurl) : NULL);
1307  const char * digest = (h->digest != NULL ? xstrdup(h->digest) : NULL);
1308  struct stat sb = h->sb; /* structure assignment */
1309  int_32 instance = h->instance;
1310  int xx;
1311 
1312 /*@-onlytrans@*/
1313  uh = doHeaderUnload(h, &length);
1314  h = headerFree(h);
1315 /*@=onlytrans@*/
1316  if (uh == NULL)
1317  return NULL;
1318  nh = headerLoad(uh);
1319  if (nh == NULL) {
1320  uh = _free(uh);
1321  return NULL;
1322  }
1323  if (nh->flags & HEADERFLAG_ALLOCATED)
1324  uh = _free(uh);
1325  nh->flags |= HEADERFLAG_ALLOCATED;
1326  if (ENTRY_IS_REGION(nh->index)) {
1327  if (tag == HEADER_SIGNATURES || tag == HEADER_IMMUTABLE)
1328  nh->index[0].info.tag = tag;
1329  }
1330  if (origin != NULL) {
1331  xx = headerSetOrigin(nh, origin);
1332  origin = _free(origin);
1333  }
1334  if (baseurl != NULL) {
1335  xx = headerSetBaseURL(nh, baseurl);
1336  baseurl = _free(baseurl);
1337  }
1338  if (digest != NULL) {
1339  xx = headerSetDigest(nh, digest);
1340  digest = _free(digest);
1341  }
1342  nh->sb = sb; /* structure assignment */
1343  xx = headerSetInstance(nh, instance);
1344  return nh;
1345 }
1346 
1352 static /*@null@*/
1353 Header headerCopyLoad(const void * uh)
1354  /*@*/
1355 {
1356  int_32 * ei = (int_32 *) uh;
1357  int_32 il = ntohl(ei[0]); /* index length */
1358  int_32 dl = ntohl(ei[1]); /* data length */
1359  /*@-sizeoftype@*/
1360  size_t pvlen = sizeof(il) + sizeof(dl) +
1361  (il * sizeof(struct entryInfo_s)) + dl;
1362  /*@=sizeoftype@*/
1363  void * nuh = NULL;
1364  Header h = NULL;
1365 
1366  /* Sanity checks on header intro. */
1367  if (!(hdrchkTags(il) || hdrchkData(dl)) && pvlen < headerMaxbytes) {
1368  nuh = memcpy(xmalloc(pvlen), uh, pvlen);
1369  if ((h = headerLoad(nuh)) != NULL)
1371  }
1372  if (h == NULL)
1373  nuh = _free(nuh);
1374  return h;
1375 }
1376 
1383 static /*@null@*/
1384 Header headerRead(void * _fd, enum hMagic magicp)
1385  /*@modifies _fd @*/
1386 {
1387  FD_t fd = _fd;
1388  int_32 block[4];
1389  int_32 reserved;
1390  int_32 * ei = NULL;
1391  int_32 il;
1392  int_32 dl;
1393  int_32 magic;
1394  Header h = NULL;
1395  size_t len;
1396  int i;
1397 
1398  memset(block, 0, sizeof(block));
1399  i = 2;
1400  if (magicp == HEADER_MAGIC_YES)
1401  i += 2;
1402 
1403  /*@-type@*/ /* FIX: cast? */
1404  if (timedRead(fd, (char *)block, i*sizeof(*block)) != (i * sizeof(*block)))
1405  goto exit;
1406  /*@=type@*/
1407 
1408  i = 0;
1409 
1410  if (magicp == HEADER_MAGIC_YES) {
1411  magic = block[i++];
1412  if (memcmp(&magic, header_magic, sizeof(magic)))
1413  goto exit;
1414  reserved = block[i++];
1415  }
1416 
1417  il = ntohl(block[i]); i++;
1418  dl = ntohl(block[i]); i++;
1419 
1420  /*@-sizeoftype@*/
1421  len = sizeof(il) + sizeof(dl) + (il * sizeof(struct entryInfo_s)) + dl;
1422  /*@=sizeoftype@*/
1423 
1424  /* Sanity checks on header intro. */
1425  if (hdrchkTags(il) || hdrchkData(dl) || len > headerMaxbytes)
1426  goto exit;
1427 
1428  ei = xmalloc(len);
1429  ei[0] = htonl(il);
1430  ei[1] = htonl(dl);
1431  len -= sizeof(il) + sizeof(dl);
1432 
1433  /*@-type@*/ /* FIX: cast? */
1434  if (timedRead(fd, (char *)&ei[2], len) != len)
1435  goto exit;
1436  /*@=type@*/
1437 
1438  h = headerLoad(ei);
1439 
1440  { const char * origin = fdGetOPath(fd);
1441  if (origin != NULL)
1442  (void) headerSetOrigin(h, origin);
1443  }
1444 
1445 exit:
1446  if (h) {
1447  if (h->flags & HEADERFLAG_ALLOCATED)
1448  ei = _free(ei);
1450  } else if (ei)
1451  ei = _free(ei);
1452  /*@-mustmod@*/ /* FIX: timedRead macro obscures annotation */
1453  return h;
1454  /*@-mustmod@*/
1455 }
1456 
1464 static
1465 int headerWrite(void * _fd, /*@null@*/ Header h, enum hMagic magicp)
1466  /*@globals fileSystem @*/
1467  /*@modifies fd, h, fileSystem @*/
1468 {
1469  FD_t fd = _fd;
1470  ssize_t nb;
1471  size_t length;
1472  const void * uh;
1473 
1474  if (h == NULL)
1475  return 1;
1476  uh = doHeaderUnload(h, &length);
1477  if (uh == NULL)
1478  return 1;
1479  switch (magicp) {
1480  case HEADER_MAGIC_YES:
1481  /*@-sizeoftype@*/
1482  nb = Fwrite(header_magic, sizeof(char), sizeof(header_magic), fd);
1483  /*@=sizeoftype@*/
1484  if (nb != sizeof(header_magic))
1485  goto exit;
1486  break;
1487  case HEADER_MAGIC_NO:
1488  break;
1489  }
1490 
1491  /*@-sizeoftype@*/
1492  nb = Fwrite(uh, sizeof(char), length, fd);
1493  /*@=sizeoftype@*/
1494 
1495 exit:
1496  uh = _free(uh);
1497  return (nb == length ? 0 : 1);
1498 }
1499 
1506 static
1507 int headerIsEntry(/*@null@*/Header h, int_32 tag)
1508  /*@*/
1509 {
1510  /*@-mods@*/ /*@ FIX: h modified by sort. */
1511  return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
1512  /*@=mods@*/
1513 }
1514 
1525 static int copyEntry(const indexEntry entry,
1526  /*@null@*/ /*@out@*/ rpmTagType * type,
1527  /*@null@*/ /*@out@*/ rpmTagData * p,
1528  /*@null@*/ /*@out@*/ rpmTagCount * c,
1529  int minMem)
1530  /*@modifies *type, *p, *c @*/
1531  /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
1532 {
1533  rpmTagCount count = entry->info.count;
1534  int rc = 1; /* XXX 1 on success. */
1535 
1536  if (p)
1537  switch (entry->info.type) {
1538  case RPM_BIN_TYPE:
1539  /*
1540  * XXX This only works for
1541  * XXX "sealed" HEADER_IMMUTABLE/HEADER_SIGNATURES/HEADER_IMAGE.
1542  * XXX This will *not* work for unsealed legacy HEADER_IMAGE (i.e.
1543  * XXX a legacy header freshly read, but not yet unloaded to the rpmdb).
1544  */
1545  if (ENTRY_IS_REGION(entry)) {
1546  int_32 * ei = ((int_32 *)entry->data) - 2;
1547  /*@-castexpose@*/
1548  entryInfo pe = (entryInfo) (ei + 2);
1549  /*@=castexpose@*/
1550  unsigned char * dataStart = (unsigned char *) (pe + ntohl(ei[0]));
1551  unsigned char * dataEnd;
1552  int_32 rdl = -entry->info.offset; /* negative offset */
1553  int_32 ril = rdl/sizeof(*pe);
1554 
1555  /*@-sizeoftype@*/
1556  rdl = entry->rdlen;
1557  count = 2 * sizeof(*ei) + (ril * sizeof(*pe)) + rdl;
1558  if (entry->info.tag == HEADER_IMAGE) {
1559  ril -= 1;
1560  pe += 1;
1561  } else {
1562  count += REGION_TAG_COUNT;
1563  rdl += REGION_TAG_COUNT;
1564  }
1565 
1566  (*p).i32p = ei = xmalloc(count);
1567  ei[0] = htonl(ril);
1568  ei[1] = htonl(rdl);
1569 
1570  /*@-castexpose@*/
1571  pe = (entryInfo) memcpy(ei + 2, pe, (ril * sizeof(*pe)));
1572  /*@=castexpose@*/
1573 
1574  dataStart = (unsigned char *) memcpy(pe + ril, dataStart, rdl);
1575  dataEnd = dataStart + rdl;
1576  /*@=sizeoftype@*/
1577 
1578  rc = regionSwab(NULL, ril, 0, pe, dataStart, dataEnd, 0);
1579  /* XXX 1 on success. */
1580  rc = (rc < 0) ? 0 : 1;
1581  } else {
1582  count = entry->length;
1583  (*p).ptr = (!minMem
1584  ? memcpy(xmalloc(count), entry->data, count)
1585  : entry->data);
1586  }
1587  break;
1588  case RPM_STRING_TYPE:
1589  if (count == 1) {
1590  (*p).str = entry->data;
1591  break;
1592  }
1593  /*@fallthrough@*/
1594  case RPM_I18NSTRING_TYPE:
1595  case RPM_STRING_ARRAY_TYPE:
1596  { const char ** argv;
1597  size_t nb = count * sizeof(*argv);
1598  char * t;
1599  int i;
1600 
1601  /*@-mods@*/
1602  if (minMem) {
1603  (*p).argv = argv = xmalloc(nb);
1604  t = entry->data;
1605  } else {
1606  (*p).argv = argv = xmalloc(nb + entry->length);
1607  t = (char *) &argv[count];
1608  memcpy(t, entry->data, entry->length);
1609  }
1610  /*@=mods@*/
1611  for (i = 0; i < count; i++) {
1612  argv[i] = t;
1613  t = strchr(t, 0);
1614  t++;
1615  }
1616  } break;
1617 
1618  case RPM_OPENPGP_TYPE: /* XXX W2DO? */
1619  case RPM_ASN1_TYPE: /* XXX W2DO? */
1620  default:
1621  (*p).ptr = entry->data;
1622  break;
1623  }
1624  if (type) *type = entry->info.type;
1625  if (c) *c = count;
1626  return rc;
1627 }
1628 
1647 static int headerMatchLocale(const char *td, const char *l, const char *le)
1648  /*@*/
1649 {
1650  const char *fe;
1651 
1652 
1653 #if 0
1654  { const char *s, *ll, *CC, *EE, *dd;
1655  char *lbuf, *t.
1656 
1657  /* Copy the buffer and parse out components on the fly. */
1658  lbuf = alloca(le - l + 1);
1659  for (s = l, ll = t = lbuf; *s; s++, t++) {
1660  switch (*s) {
1661  case '_':
1662  *t = '\0';
1663  CC = t + 1;
1664  break;
1665  case '.':
1666  *t = '\0';
1667  EE = t + 1;
1668  break;
1669  case '@':
1670  *t = '\0';
1671  dd = t + 1;
1672  break;
1673  default:
1674  *t = *s;
1675  break;
1676  }
1677  }
1678 
1679  if (ll) /* ISO language should be lower case */
1680  for (t = ll; *t; t++) *t = tolower(*t);
1681  if (CC) /* ISO country code should be upper case */
1682  for (t = CC; *t; t++) *t = toupper(*t);
1683 
1684  /* There are a total of 16 cases to attempt to match. */
1685  }
1686 #endif
1687 
1688  /* First try a complete match. */
1689  if (strlen(td) == (le-l) && !strncmp(td, l, (le - l)))
1690  return 1;
1691 
1692  /* Next, try stripping optional dialect and matching. */
1693  for (fe = l; fe < le && *fe != '@'; fe++)
1694  {};
1695  if (fe < le && !strncmp(td, l, (fe - l)))
1696  return 1;
1697 
1698  /* Next, try stripping optional codeset and matching. */
1699  for (fe = l; fe < le && *fe != '.'; fe++)
1700  {};
1701  if (fe < le && !strncmp(td, l, (fe - l)))
1702  return 1;
1703 
1704  /* Finally, try stripping optional country code and matching. */
1705  for (fe = l; fe < le && *fe != '_'; fe++)
1706  {};
1707  if (fe < le && !strncmp(td, l, (fe - l)))
1708  return 1;
1709 
1710  return 0;
1711 }
1712 
1719 /*@dependent@*/ /*@exposed@*/ static char *
1721  /*@*/
1722 {
1723  const char *lang, *l, *le;
1724  indexEntry table;
1725 
1726  /* XXX Drepper sez' this is the order. */
1727  if ((lang = getenv("LANGUAGE")) == NULL &&
1728  (lang = getenv("LC_ALL")) == NULL &&
1729  (lang = getenv("LC_MESSAGES")) == NULL &&
1730  (lang = getenv("LANG")) == NULL)
1731  return entry->data;
1732 
1733  /*@-mods@*/
1734  if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
1735  return entry->data;
1736  /*@=mods@*/
1737 
1738  for (l = lang; *l != '\0'; l = le) {
1739  const char *td;
1740  char *ed;
1741  int langNum;
1742 
1743  while (*l && *l == ':') /* skip leading colons */
1744  l++;
1745  if (*l == '\0')
1746  break;
1747  for (le = l; *le && *le != ':'; le++) /* find end of this locale */
1748  {};
1749 
1750  /* For each entry in the header ... */
1751  for (langNum = 0, td = table->data, ed = entry->data;
1752  langNum < entry->info.count;
1753  langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
1754 
1755  if (headerMatchLocale(td, l, le))
1756  return ed;
1757 
1758  }
1759  }
1760 
1761 /* when everything fail, try gettext */
1762  return ((entry->data != NULL) && *(char*)(entry->data)) ? _(entry->data) : entry->data;
1763 }
1764 
1776  /*@null@*/ /*@out@*/ rpmTagType * type,
1777  /*@null@*/ /*@out@*/ rpmTagData * p,
1778  /*@null@*/ /*@out@*/ rpmTagCount * c,
1779  int minMem)
1780  /*@modifies *type, *p, *c @*/
1781  /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
1782 {
1783  indexEntry entry;
1784  int rc;
1785 
1786  /* First find the tag */
1787 /*@-mods@*/ /*@ FIX: h modified by sort. */
1788  entry = findEntry(h, tag, RPM_NULL_TYPE);
1789 /*@=mods@*/
1790  if (entry == NULL) {
1791  if (type) type = 0;
1792  if (p) (*p).ptr = NULL;
1793  if (c) *c = 0;
1794  return 0;
1795  }
1796 
1797  switch (entry->info.type) {
1798  case RPM_I18NSTRING_TYPE:
1799  rc = 1;
1800  if (type) *type = RPM_STRING_TYPE;
1801  if (c) *c = 1;
1802  /*@-dependenttrans@*/
1803  if (p) (*p).str = headerFindI18NString(h, entry);
1804  /*@=dependenttrans@*/
1805  break;
1806  default:
1807  rc = copyEntry(entry, type, p, c, minMem);
1808  break;
1809  }
1810 
1811  /* XXX 1 on success */
1812  return ((rc == 1) ? 1 : 0);
1813 }
1814 
1822 static /*@null@*/ void * headerFreeTag(/*@unused@*/ Header h,
1823  /*@only@*/ /*@null@*/ const void * data, rpmTagType type)
1824  /*@modifies data @*/
1825 {
1826  if (data) {
1827  if (type == -1 ||
1828  type == RPM_STRING_ARRAY_TYPE ||
1829  type == RPM_I18NSTRING_TYPE ||
1830  type == RPM_BIN_TYPE ||
1831  type == RPM_ASN1_TYPE ||
1832  type == RPM_OPENPGP_TYPE)
1833  data = _free(data);
1834  }
1835  return NULL;
1836 }
1837 
1851 static
1853  /*@null@*/ /*@out@*/ hTYP_t type,
1854  /*@null@*/ /*@out@*/ void * p,
1855  /*@null@*/ /*@out@*/ hCNT_t c)
1856  /*@modifies *type, *p, *c @*/
1857  /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
1858 {
1859  void * sw;
1860  int rc;
1861 
1862  if ((sw = headerGetStats(h, 19)) != NULL) /* RPMTS_OP_HDRGET */
1863  (void) rpmswEnter(sw, 0);
1864  rc = intGetEntry(h, tag, (rpmTagType *)type, (rpmTagData *)p, (rpmTagCount *)c, 0);
1865  if (sw != NULL) (void) rpmswExit(sw, 0);
1866  return rc;
1867 }
1868 
1881 static
1883  /*@null@*/ /*@out@*/ hTYP_t type,
1884  /*@null@*/ /*@out@*/ void * p,
1885  /*@null@*/ /*@out@*/ hCNT_t c)
1886  /*@modifies *type, *p, *c @*/
1887  /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
1888 {
1889  void * sw;
1890  int rc;
1891 
1892  if ((sw = headerGetStats(h, 19)) != NULL) /* RPMTS_OP_HDRGET */
1893  (void) rpmswEnter(sw, 0);
1894  rc = intGetEntry(h, tag, (rpmTagType *)type, (rpmTagData *)p, (rpmTagCount *)c, 1);
1895  if (sw != NULL) (void) rpmswExit(sw, 0);
1896  return rc;
1897 }
1898 
1900 {
1901  indexEntry entry;
1902  int rc;
1903 
1904  if (p == NULL) return headerIsEntry(h, tag);
1905 
1906  /* First find the tag */
1907  /*@-mods@*/ /*@ FIX: h modified by sort. */
1908  entry = findEntry(h, tag, RPM_NULL_TYPE);
1909  /*@=mods@*/
1910  if (!entry) {
1911  if (p) *(void **)p = NULL;
1912  if (c) *c = 0;
1913  return 0;
1914  }
1915 
1916  rc = copyEntry(entry, type, p, c, 0);
1917 
1918  /* XXX 1 on success */
1919  return ((rc == 1) ? 1 : 0);
1920 }
1921 
1924 static void copyData(rpmTagType type, rpmTagData * dest, rpmTagData * src,
1925  rpmTagCount cnt, size_t len)
1926  /*@modifies *dest @*/
1927 {
1928  switch (type) {
1929  case RPM_I18NSTRING_TYPE:
1930  case RPM_STRING_ARRAY_TYPE:
1931  { const char ** av = (*src).argv;
1932  char * t = (char *) (*dest).str;
1933 
1934  while (cnt-- > 0 && len > 0) {
1935  const char * s;
1936  if ((s = *av++) == NULL)
1937  continue;
1938  do {
1939  *t++ = *s++;
1940  } while (s[-1] && --len > 0);
1941  }
1942  } break;
1943  default:
1944  memmove((*dest).ptr, (*src).ptr, len);
1945  break;
1946  }
1947 }
1948 
1957 /*@null@*/
1958 static void *
1959 grabData(rpmTagType type, rpmTagData * p, rpmTagCount c, /*@out@*/ int * lenp)
1960  /*@modifies *lenp @*/
1961  /*@requires maxSet(lengthPtr) >= 0 @*/
1962 {
1963  rpmTagData data = { .ptr = NULL };
1964  int length;
1965 
1966  length = dataLength(type, p, c, 0, NULL);
1967  if (length > 0) {
1968  data.ptr = xmalloc(length);
1969  copyData(type, &data, p, c, length);
1970  }
1971 
1972  if (lenp)
1973  *lenp = length;
1974  return data.ptr;
1975 }
1976 
1991 static
1992 int headerAddEntry(Header h, int_32 tag, int_32 type, const void * p, int_32 c)
1993  /*@modifies h @*/
1994 {
1995  indexEntry entry;
1996  rpmTagData q = { .ptr = (void *) p };
1997  rpmTagData data;
1998  int length;
1999 
2000  /* Count must always be >= 1 for headerAddEntry. */
2001  if (c <= 0)
2002  return 0;
2003 
2004  if (hdrchkType(type))
2005  return 0;
2006  if (hdrchkData(c))
2007  return 0;
2008 
2009  length = 0;
2010  data.ptr = grabData(type, &q, c, &length);
2011  if (data.ptr == NULL || length <= 0)
2012  return 0;
2013 
2014  /* Allocate more index space if necessary */
2015  if (h->indexUsed == h->indexAlloced) {
2017  h->index = xrealloc(h->index, h->indexAlloced * sizeof(*h->index));
2018  }
2019 
2020  /* Fill in the index */
2021  entry = h->index + h->indexUsed;
2022  entry->info.tag = tag;
2023  entry->info.type = type;
2024  entry->info.count = c;
2025  entry->info.offset = 0;
2026  entry->data = data.ptr;
2027  entry->length = length;
2028 
2029  if (h->indexUsed > 0 && tag < h->index[h->indexUsed-1].info.tag)
2030  h->flags &= ~HEADERFLAG_SORTED;
2031  h->indexUsed++;
2032 
2033  return 1;
2034 }
2035 
2050 static
2052  const void * p, int_32 c)
2053  /*@modifies h @*/
2054 {
2055  rpmTagData src = { .ptr = (void *) p };
2056  rpmTagData dest = { .ptr = NULL };
2057  indexEntry entry;
2058  int length;
2059 
2060  if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
2061  /* we can't do this */
2062  return 0;
2063  }
2064 
2065  /* Find the tag entry in the header. */
2066  entry = findEntry(h, tag, type);
2067  if (!entry)
2068  return 0;
2069 
2070  length = dataLength(type, &src, c, 0, NULL);
2071  if (length < 0)
2072  return 0;
2073 
2074  if (ENTRY_IN_REGION(entry)) {
2075  char * t = xmalloc(entry->length + length);
2076  memcpy(t, entry->data, entry->length);
2077  entry->data = t;
2078  entry->info.offset = 0;
2079  } else
2080  entry->data = xrealloc(entry->data, entry->length + length);
2081 
2082  dest.ptr = ((char *) entry->data) + entry->length;
2083  copyData(type, &dest, &src, c, length);
2084 
2085  entry->length += length;
2086 
2087  entry->info.count += c;
2088 
2089  return 1;
2090 }
2091 
2101 static
2103  const void * p, int_32 c)
2104  /*@modifies h @*/
2105 {
2106  return (findEntry(h, tag, type)
2107  ? headerAppendEntry(h, tag, type, p, c)
2108  : headerAddEntry(h, tag, type, p, c));
2109 }
2110 
2131 static
2132 int headerAddI18NString(Header h, int_32 tag, const char * string,
2133  const char * lang)
2134  /*@modifies h @*/
2135 {
2136  indexEntry table, entry;
2137  rpmTagData p;
2138  int length;
2139  int ghosts;
2140  int i, langNum;
2141  char * buf;
2142 
2144  entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
2145 
2146  if (!table && entry)
2147  return 0; /* this shouldn't ever happen!! */
2148 
2149  if (!table && !entry) {
2150  const char * argv[2];
2151  int count = 0;
2152  p.argv = argv;
2153  if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
2154  /*@-observertrans -readonlytrans@*/
2155  p.argv[count++] = "C";
2156  /*@=observertrans =readonlytrans@*/
2157  } else {
2158  /*@-observertrans -readonlytrans@*/
2159  p.argv[count++] = "C";
2160  /*@=observertrans =readonlytrans@*/
2161  p.argv[count++] = lang;
2162  }
2164  p.ptr, count))
2165  return 0;
2167  }
2168 
2169  if (!table)
2170  return 0;
2171  if (!lang) lang = "C";
2172 
2173  { const char * l = table->data;
2174  for (langNum = 0; langNum < table->info.count; langNum++) {
2175  if (!strcmp(l, lang)) break;
2176  l += strlen(l) + 1;
2177  }
2178  }
2179 
2180  if (langNum >= table->info.count) {
2181  length = strlen(lang) + 1;
2182  if (ENTRY_IN_REGION(table)) {
2183  char * t = xmalloc(table->length + length);
2184  memcpy(t, table->data, table->length);
2185  table->data = t;
2186  table->info.offset = 0;
2187  } else
2188  table->data = xrealloc(table->data, table->length + length);
2189  memmove(((char *)table->data) + table->length, lang, length);
2190  table->length += length;
2191  table->info.count++;
2192  }
2193 
2194  if (!entry) {
2195  p.argv = alloca(sizeof(*p.argv) * (langNum + 1));
2196  for (i = 0; i < langNum; i++)
2197  p.argv[i] = "";
2198  p.argv[langNum] = string;
2199  return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, p.ptr, langNum + 1);
2200  } else if (langNum >= entry->info.count) {
2201  ghosts = langNum - entry->info.count;
2202 
2203  length = strlen(string) + 1 + ghosts;
2204  if (ENTRY_IN_REGION(entry)) {
2205  char * t = xmalloc(entry->length + length);
2206  memcpy(t, entry->data, entry->length);
2207  entry->data = t;
2208  entry->info.offset = 0;
2209  } else
2210  entry->data = xrealloc(entry->data, entry->length + length);
2211 
2212  memset(((char *)entry->data) + entry->length, '\0', ghosts);
2213  memmove(((char *)entry->data) + entry->length + ghosts, string, strlen(string)+1);
2214 
2215  entry->length += length;
2216  entry->info.count = langNum + 1;
2217  } else {
2218  char *b, *be, *e, *ee, *t;
2219  size_t bn, sn, en;
2220 
2221  /* Set beginning/end pointers to previous data */
2222  b = be = e = ee = entry->data;
2223  for (i = 0; i < table->info.count; i++) {
2224  if (i == langNum)
2225  be = ee;
2226  ee += strlen(ee) + 1;
2227  if (i == langNum)
2228  e = ee;
2229  }
2230 
2231  /* Get storage for new buffer */
2232  bn = (be-b);
2233  sn = strlen(string) + 1;
2234  en = (ee-e);
2235  length = bn + sn + en;
2236  t = buf = xmalloc(length);
2237 
2238  /* Copy values into new storage */
2239  memcpy(t, b, bn);
2240  t += bn;
2241 /*@-mayaliasunique@*/
2242  memcpy(t, string, sn);
2243  t += sn;
2244  memcpy(t, e, en);
2245  t += en;
2246 /*@=mayaliasunique@*/
2247 
2248  /* Replace i18N string array */
2249  entry->length -= strlen(be) + 1;
2250  entry->length += sn;
2251 
2252  if (ENTRY_IN_REGION(entry)) {
2253  entry->info.offset = 0;
2254  } else
2255  entry->data = _free(entry->data);
2256  /*@-dependenttrans@*/
2257  entry->data = buf;
2258  /*@=dependenttrans@*/
2259  }
2260 
2261  return 0;
2262 }
2263 
2274 static
2276  const void * p, int_32 c)
2277  /*@modifies h @*/
2278 {
2279  indexEntry entry;
2280  rpmTagData q = { .ptr = (void *) p };
2281  rpmTagData oldData;
2282  rpmTagData newData;
2283  int length;
2284 
2285  /* First find the tag */
2286  entry = findEntry(h, tag, type);
2287  if (!entry)
2288  return 0;
2289 
2290  length = 0;
2291  newData.ptr = grabData(type, &q, c, &length);
2292  if (newData.ptr == NULL || length <= 0)
2293  return 0;
2294 
2295  /* make sure entry points to the first occurence of this tag */
2296  while (entry > h->index && (entry - 1)->info.tag == tag)
2297  entry--;
2298 
2299  /* free after we've grabbed the new data in case the two are intertwined;
2300  that's a bad idea but at least we won't break */
2301  oldData.ptr = entry->data;
2302 
2303  entry->info.count = c;
2304  entry->info.type = type;
2305  entry->data = newData.ptr;
2306  entry->length = length;
2307 
2308  if (ENTRY_IN_REGION(entry)) {
2309  entry->info.offset = 0;
2310  } else
2311  oldData.ptr = _free(oldData.ptr);
2312 
2313  return 1;
2314 }
2315 
2318 static char escapedChar(const char ch) /*@*/
2319 {
2320 /*@-modfilesys@*/
2321 if (_hdr_debug)
2322 fprintf(stderr, "\t\t\\%c\n", ch);
2323 /*@=modfilesys@*/
2324  switch (ch) {
2325  case 'a': return '\a';
2326  case 'b': return '\b';
2327  case 'f': return '\f';
2328  case 'n': return '\n';
2329  case 'r': return '\r';
2330  case 't': return '\t';
2331  case 'v': return '\v';
2332  default: return ch;
2333  }
2334 }
2335 
2340 static HE_t rpmheMark(/*@null@*/ HE_t he)
2341  /*@modifies he @*/
2342 {
2343  /* Set he->freeData as appropriate for headerGetEntry() . */
2344  if (he)
2345  switch (he->t) {
2346  default:
2347  he->freeData = 0;
2348  break;
2349  case RPM_I18NSTRING_TYPE:
2350  case RPM_STRING_ARRAY_TYPE:
2351  case RPM_BIN_TYPE:
2352  he->freeData = 1;
2353  break;
2354  }
2355  return he;
2356 }
2357 
2362 static HE_t rpmheClean(/*@null@*/ HE_t he)
2363  /*@modifies he @*/
2364 {
2365  if (he) {
2366  if (he->freeData && he->p.ptr != NULL)
2367  he->p.ptr = _free(he->p.ptr);
2368  memset(he, 0, sizeof(*he));
2369  }
2370  return he;
2371 }
2372 
2379 static /*@null@*/ sprintfToken
2380 freeFormat( /*@only@*/ /*@null@*/ sprintfToken format, int num)
2381  /*@modifies *format @*/
2382 {
2383  int i;
2384 
2385  if (format == NULL) return NULL;
2386 
2387  for (i = 0; i < num; i++) {
2388  switch (format[i].type) {
2389  case PTOK_TAG:
2390  if (_tagcache)
2391  (void) rpmheClean(&format[i].u.tag.he);
2392  format[i].u.tag.av = argvFree(format[i].u.tag.av);
2393  format[i].u.tag.params = argvFree(format[i].u.tag.params);
2394  format[i].u.tag.fmtfuncs = _free(format[i].u.tag.fmtfuncs);
2395  /*@switchbreak@*/ break;
2396  case PTOK_ARRAY:
2397  format[i].u.array.format =
2398  freeFormat(format[i].u.array.format,
2399  format[i].u.array.numTokens);
2400  /*@switchbreak@*/ break;
2401  case PTOK_COND:
2402  format[i].u.cond.ifFormat =
2403  freeFormat(format[i].u.cond.ifFormat,
2404  format[i].u.cond.numIfTokens);
2405  format[i].u.cond.elseFormat =
2406  freeFormat(format[i].u.cond.elseFormat,
2407  format[i].u.cond.numElseTokens);
2408  if (_tagcache)
2409  (void) rpmheClean(&format[i].u.cond.tag.he);
2410  format[i].u.cond.tag.av = argvFree(format[i].u.cond.tag.av);
2411  format[i].u.cond.tag.params = argvFree(format[i].u.cond.tag.params);
2412  format[i].u.cond.tag.fmtfuncs = _free(format[i].u.cond.tag.fmtfuncs);
2413  /*@switchbreak@*/ break;
2414  case PTOK_NONE:
2415  case PTOK_STRING:
2416  default:
2417  /*@switchbreak@*/ break;
2418  }
2419  }
2420  format = _free(format);
2421  return NULL;
2422 }
2423 
2428 /*@unused@*/
2430 /*@unused@*/
2432 };
2433 
2439 static /*@null@*/
2441  /*@modifies hi @*/
2442 {
2443  if (hi != NULL) {
2444  hi->h = headerFree(hi->h);
2445  hi = _free(hi);
2446  }
2447  return hi;
2448 }
2449 
2455 static
2457  /*@modifies h */
2458 {
2459  HeaderIterator hi = xmalloc(sizeof(*hi));
2460 
2461  headerSort(h);
2462 
2463  hi->h = headerLink(h);
2464  hi->next_index = 0;
2465  return hi;
2466 }
2467 
2477 static
2479  /*@null@*/ /*@out@*/ hTAG_t tag,
2480  /*@null@*/ /*@out@*/ hTYP_t type,
2481  /*@null@*/ /*@out@*/ hPTR_t * p,
2482  /*@null@*/ /*@out@*/ hCNT_t c)
2483  /*@modifies hi, *tag, *type, *p, *c @*/
2484  /*@requires maxSet(tag) >= 0 /\ maxSet(type) >= 0
2485  /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
2486 {
2487  void * sw;
2488  Header h = hi->h;
2489  int slot = hi->next_index;
2490  indexEntry entry = NULL;
2491  int rc;
2492 
2493  for (slot = hi->next_index; slot < h->indexUsed; slot++) {
2494  entry = h->index + slot;
2495  if (!ENTRY_IS_REGION(entry))
2496  break;
2497  }
2498  hi->next_index = slot;
2499  if (entry == NULL || slot >= h->indexUsed)
2500  return 0;
2501 
2502  /*@-noeffect@*/ /* LCL: no clue */
2503  hi->next_index++;
2504  /*@=noeffect@*/
2505 
2506  if ((sw = headerGetStats(h, 19)) != NULL) /* RPMTS_OP_HDRGET */
2507  (void) rpmswEnter(sw, 0);
2508 
2509  if (tag)
2510  *tag = entry->info.tag;
2511 
2512  rc = copyEntry(entry, (rpmTagType *)type, (rpmTagData *)p, (rpmTagCount *)c, 0);
2513 
2514  if (sw != NULL) (void) rpmswExit(sw, 0);
2515 
2516  /* XXX 1 on success */
2517  return ((rc == 1) ? 1 : 0);
2518 }
2519 
2525 static /*@null@*/
2527  /*@modifies h @*/
2528 {
2529  HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
2530  Header nh = headerNew();
2531  HeaderIterator hi;
2532 
2533  for (hi = headerInitIterator(h);
2534  headerNextIterator(hi, &he->tag, (hTYP_t)&he->t, (hPTR_t *)&he->p, &he->c);
2535  he->p.ptr = headerFreeData(he->p.ptr, he->t))
2536  {
2537  if (he->p.ptr) (void) headerAddEntry(nh, he->tag, he->t, he->p.ptr, he->c);
2538  }
2539  hi = headerFreeIterator(hi);
2540 
2541  return headerReload(nh, HEADER_IMAGE);
2542 }
2543 
2546 typedef struct headerSprintfArgs_s {
2548  char * fmt;
2549 /*@temp@*/
2551 /*@temp@*/
2553 /*@observer@*/ /*@null@*/
2554  const char * errmsg;
2556  int nec;
2558 /*@relnull@*/
2560 /*@owned@*/
2561  char * val;
2562  size_t vallen;
2563  size_t alloced;
2565  int i;
2566 } * headerSprintfArgs;
2567 
2573 static headerSprintfArgs hsaInit(/*@returned@*/ headerSprintfArgs hsa)
2574  /*@modifies hsa */
2575 {
2576  sprintfTag tag =
2577  (hsa->format->type == PTOK_TAG
2578  ? &hsa->format->u.tag :
2579  (hsa->format->type == PTOK_ARRAY
2580  ? &hsa->format->u.array.format->u.tag :
2581  NULL));
2582 
2583  if (hsa != NULL) {
2584  hsa->i = 0;
2585  if (tag != NULL && tag->tagno == -2)
2586  hsa->hi = headerInitIterator(hsa->h);
2587  }
2588 /*@-nullret@*/
2589  return hsa;
2590 /*@=nullret@*/
2591 }
2592 
2598 /*@null@*/
2599 static sprintfToken hsaNext(/*@returned@*/ headerSprintfArgs hsa)
2600  /*@modifies hsa */
2601 {
2602  sprintfToken fmt = NULL;
2603  sprintfTag tag =
2604  (hsa->format->type == PTOK_TAG
2605  ? &hsa->format->u.tag :
2606  (hsa->format->type == PTOK_ARRAY
2607  ? &hsa->format->u.array.format->u.tag :
2608  NULL));
2609 
2610  if (hsa != NULL && hsa->i >= 0 && hsa->i < hsa->numTokens) {
2611  fmt = hsa->format + hsa->i;
2612  if (hsa->hi == NULL) {
2613  hsa->i++;
2614  } else {
2615  HE_t he = rpmheClean(&tag->he);
2616  if (!headerNextIterator(hsa->hi, &he->tag, (hTAG_t)&he->t, (hPTR_t *)&he->p.ptr, &he->c))
2617  {
2618  tag->tagno = 0;
2619  return NULL;
2620  }
2621  he = rpmheMark(he);
2622  he->avail = 1;
2623  tag->tagno = he->tag;
2624  }
2625  }
2626 
2627 /*@-dependenttrans -onlytrans@*/
2628  return fmt;
2629 /*@=dependenttrans =onlytrans@*/
2630 }
2631 
2637 static headerSprintfArgs hsaFini(/*@returned@*/ headerSprintfArgs hsa)
2638  /*@modifies hsa */
2639 {
2640  if (hsa != NULL) {
2641  hsa->hi = headerFreeIterator(hsa->hi);
2642  hsa->i = 0;
2643  }
2644 /*@-nullret@*/
2645  return hsa;
2646 /*@=nullret@*/
2647 }
2648 
2655 /*@dependent@*/ /*@exposed@*/
2656 static char * hsaReserve(headerSprintfArgs hsa, size_t need)
2657  /*@modifies hsa */
2658 {
2659  if ((hsa->vallen + need) >= hsa->alloced) {
2660  if (hsa->alloced <= need)
2661  hsa->alloced += need;
2662  hsa->alloced <<= 1;
2663  hsa->val = xrealloc(hsa->val, hsa->alloced+1);
2664  }
2665  return hsa->val + hsa->vallen;
2666 }
2667 
2676 /*@observer@*/ /*@null@*/
2677 static const char * myTagName(headerTagTableEntry tbl, int val,
2678  /*@null@*/ int *typep)
2679  /*@modifies *typep @*/
2680 {
2681  static char name[128];
2682  const char * s;
2683  char *t;
2684 
2685  for (; tbl->name != NULL; tbl++) {
2686  if (tbl->val == val)
2687  break;
2688  }
2689  if ((s = tbl->name) == NULL)
2690  return NULL;
2691  s += sizeof("RPMTAG_") - 1;
2692  t = name;
2693  *t++ = *s++;
2694  while (*s != '\0')
2695  *t++ = xtolower(*s++);
2696  *t = '\0';
2697  if (typep)
2698  *typep = tbl->type;
2699  return name;
2700 }
2701 
2702 /*@observer@*/ /*@null@*/
2703 static int myTagType(headerTagTableEntry tbl, int val)
2704 {
2705  for (; tbl->name != NULL; tbl++) {
2706  if (tbl->val == val)
2707  break;
2708  }
2709  return (tbl->name != NULL ? tbl->type : 0);
2710 }
2711 
2719 static int myTagValue(headerTagTableEntry tbl, const char * name)
2720  /*@*/
2721 {
2722  for (; tbl->name != NULL; tbl++) {
2723  if (!xstrcasecmp(tbl->name, name))
2724  return tbl->val;
2725  }
2726  return 0;
2727 }
2728 
2736 static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
2737  /*@modifies token @*/
2738 {
2739  headerSprintfExtension exts = hsa->exts;
2741  sprintfTag stag = (token->type == PTOK_COND
2742  ? &token->u.cond.tag : &token->u.tag);
2743  int extNum;
2744 
2745  stag->fmtfuncs = NULL;
2746  stag->ext = NULL;
2747  stag->extNum = 0;
2748  stag->tagno = -1;
2749 
2750  if (!strcmp(name, "*")) {
2751  stag->tagno = -2;
2752  goto bingo;
2753  }
2754 
2755  if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
2756  char * t = alloca(strlen(name) + sizeof("RPMTAG_"));
2757  (void) stpcpy( stpcpy(t, "RPMTAG_"), name);
2758  name = t;
2759  }
2760 
2761  /* Search extensions for specific tag override. */
2762  for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
2763  ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1), extNum++)
2764  {
2765  if (ext->name == NULL || ext->type != HEADER_EXT_TAG)
2766  continue;
2767  if (!xstrcasecmp(ext->name, name)) {
2768  stag->ext = ext->u.tagFunction;
2769  stag->extNum = extNum;
2770  goto bingo;
2771  }
2772  }
2773 
2774  /* Search tag names. */
2775  stag->tagno = myTagValue(hsa->tags, name);
2776  if (stag->tagno != 0)
2777  goto bingo;
2778 
2779  return 1;
2780 
2781 bingo:
2782  /* Search extensions for specific format. */
2783  if (stag->av != NULL) {
2784  int i;
2785  stag->fmtfuncs = xcalloc(argvCount(stag->av) + 1, sizeof(*stag->fmtfuncs));
2786  for (i = 0; stag->av[i] != NULL; i++) {
2787  for (ext = exts; ext != NULL && ext->type != HEADER_EXT_LAST;
2788  ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1))
2789  {
2790  if (ext->name == NULL || ext->type != HEADER_EXT_FORMAT)
2791  continue;
2792  if (strcmp(ext->name, stag->av[i]+1))
2793  continue;
2794  stag->fmtfuncs[i] = ext->u.fmtFunction;
2795  break;
2796  }
2797  }
2798  }
2799  return 0;
2800 }
2801 
2809 char * intFormat(HE_t he, /*@null@*/ const char ** av, const char * fmt)
2810 {
2811  int ix = (he->ix > 0 ? he->ix : 0);
2812  int_64 ival = 0;
2813  const char * istr = NULL;
2814  char * b;
2815  size_t nb = 0;
2816 
2817  if (fmt == NULL || *fmt == '\0')
2818  fmt = "d";
2819 
2820  switch (he->t) {
2821  default:
2822  return xstrdup(_("(not a number)"));
2823  break;
2824  case RPM_CHAR_TYPE:
2825  case RPM_INT8_TYPE:
2826  ival = he->p.i8p[ix];
2827  break;
2828  case RPM_INT16_TYPE:
2829  ival = he->p.ui16p[ix]; /* XXX note unsigned. */
2830  break;
2831  case RPM_INT32_TYPE:
2832  ival = he->p.i32p[ix];
2833  break;
2834  case RPM_INT64_TYPE:
2835  ival = he->p.i64p[ix];
2836  break;
2837  case RPM_STRING_TYPE:
2838  istr = he->p.str;
2839  break;
2840  case RPM_STRING_ARRAY_TYPE:
2841  istr = he->p.argv[ix];
2842  break;
2843  case RPM_OPENPGP_TYPE: /* XXX W2DO? */
2844  case RPM_ASN1_TYPE: /* XXX W2DO? */
2845  case RPM_BIN_TYPE:
2846  { static char hex[] = "0123456789abcdef";
2847  const char * s = he->p.str;
2848  int c = he->c;
2849  char * t;
2850 
2851  nb = 2 * c + 1;
2852  t = b = alloca(nb+1);
2853  while (c-- > 0) {
2854  unsigned int i;
2855  i = *s++;
2856  *t++ = hex[ (i >> 4) & 0xf ];
2857  *t++ = hex[ (i ) & 0xf ];
2858  }
2859  *t = '\0';
2860  } break;
2861  }
2862 
2863  if (istr) { /* string */
2864  b = (char *)istr; /* NOCAST */
2865  } else
2866  if (nb == 0) { /* number */
2867  char myfmt[] = "%llX";
2868  myfmt[3] = *fmt;
2869  nb = 64;
2870  b = alloca(nb);
2871  snprintf(b, nb, myfmt, ival);
2872  b[nb-1] = '\0';
2873  }
2874 
2875  return xstrdup(b);
2876 }
2877 
2884 static char * octFormat(HE_t he, /*@null@*/ const char ** av)
2885  /*@*/
2886 {
2887  return intFormat(he, NULL, "o");
2888 }
2889 
2896 static char * hexFormat(HE_t he, /*@null@*/ const char ** av)
2897  /*@*/
2898 {
2899  return intFormat(he, NULL, "x");
2900 }
2901 
2908 static char * decFormat(HE_t he, /*@null@*/ const char ** av)
2909  /*@*/
2910 {
2911  return intFormat(he, NULL, "d");
2912 }
2913 
2921 static char * realDateFormat(HE_t he, /*@null@*/ const char ** av, const char * strftimeFormat)
2922  /*@*/
2923 {
2924  rpmTagData data = { .ptr = he->p.ptr };
2925  char * val;
2926 
2927  if (he->t != RPM_INT64_TYPE) {
2928  val = xstrdup(_("(not a number)"));
2929  } else {
2930  struct tm * tstruct;
2931  char buf[50];
2932 
2933  /* this is important if sizeof(int_64) ! sizeof(time_t) */
2934  { time_t dateint = data.ui64p[0];
2935  tstruct = localtime(&dateint);
2936  }
2937  buf[0] = '\0';
2938  if (tstruct)
2939  (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
2940  buf[sizeof(buf) - 1] = '\0';
2941  val = xstrdup(buf);
2942  }
2943 
2944  return val;
2945 }
2946 
2953 static char * dateFormat(HE_t he, /*@null@*/ const char ** av)
2954  /*@*/
2955 {
2956  return realDateFormat(he, NULL, _("%c"));
2957 }
2958 
2965 static char * dayFormat(HE_t he, /*@null@*/ const char ** av)
2966  /*@*/
2967 {
2968  return realDateFormat(he, NULL, _("%a %b %d %Y"));
2969 }
2970 
2977 static char * shescapeFormat(HE_t he, /*@null@*/ const char ** av)
2978  /*@*/
2979 {
2980  char * val;
2981  size_t nb;
2982 
2983  /* XXX one of these integer types is unnecessary. */
2984  if (he->t == RPM_INT32_TYPE) {
2985  nb = 20;
2986  val = xmalloc(nb);
2987  snprintf(val, nb, "%d", he->p.i32p[0]);
2988  val[nb-1] = '\0';
2989  } else if (he->t == RPM_INT64_TYPE) {
2990  nb = 40;
2991  val = xmalloc(40);
2992  snprintf(val, nb, "%lld", he->p.i64p[0]);
2993  val[nb-1] = '\0';
2994  } else if (he->t == RPM_STRING_TYPE) {
2995  const char * s = he->p.str;
2996  char * t;
2997  int c;
2998 
2999  nb = 0;
3000  for (s = he->p.str; (c = (int)*s) != 0; s++) {
3001  nb++;
3002  if (c == (int)'\'')
3003  nb += 3;
3004  }
3005  nb += 3;
3006  t = val = xmalloc(nb);
3007  *t++ = '\'';
3008  for (s = he->p.str; (c = (int)*s) != 0; s++) {
3009  if (c == (int)'\'') {
3010  *t++ = '\'';
3011  *t++ = '\\';
3012  *t++ = '\'';
3013  }
3014  *t++ = (char) c;
3015  }
3016  *t++ = '\'';
3017  *t = '\0';
3018  } else
3019  val = xstrdup(_("invalid type"));
3020 
3021  return val;
3022 }
3023 
3024 /*@-type@*/ /* FIX: cast? */
3026  { HEADER_EXT_FORMAT, "octal",
3027  { .fmtFunction = octFormat } },
3028  { HEADER_EXT_FORMAT, "oct",
3029  { .fmtFunction = octFormat } },
3030  { HEADER_EXT_FORMAT, "hex",
3031  { .fmtFunction = hexFormat } },
3032  { HEADER_EXT_FORMAT, "decimal",
3033  { .fmtFunction = decFormat } },
3034  { HEADER_EXT_FORMAT, "dec",
3035  { .fmtFunction = decFormat } },
3036  { HEADER_EXT_FORMAT, "date",
3037  { .fmtFunction = dateFormat } },
3038  { HEADER_EXT_FORMAT, "day",
3039  { .fmtFunction = dayFormat } },
3040  { HEADER_EXT_FORMAT, "shescape",
3041  { .fmtFunction = shescapeFormat } },
3042  { HEADER_EXT_LAST, NULL, { NULL } }
3043 };
3044 /*@=type@*/
3045 
3046 /* forward ref */
3055 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
3056  char * str, /*@out@*/char ** endPtr)
3057  /*@modifies hsa, str, token, *endPtr @*/
3058  /*@requires maxSet(endPtr) >= 0 @*/;
3059 
3070 static int parseFormat(headerSprintfArgs hsa, /*@null@*/ char * str,
3071  /*@out@*/ sprintfToken * formatPtr,
3072  /*@out@*/ int * numTokensPtr,
3073  /*@null@*/ /*@out@*/ char ** endPtr, int state)
3074  /*@modifies hsa, str, *formatPtr, *numTokensPtr, *endPtr @*/
3075  /*@requires maxSet(formatPtr) >= 0 /\ maxSet(numTokensPtr) >= 0
3076  /\ maxSet(endPtr) >= 0 @*/
3077 {
3078 /*@observer@*/
3079 static const char *pstates[] = {
3080 "NORMAL", "ARRAY", "EXPR", "WTF?"
3081 };
3082  char * chptr, * start, * next, * dst;
3083  sprintfToken format;
3084  sprintfToken token;
3085  unsigned numTokens;
3086  unsigned i;
3087  int done = 0;
3088  int xx;
3089 
3090 /*@-modfilesys@*/
3091 if (_hdr_debug)
3092 fprintf(stderr, "--> parseFormat(%p, \"%.20s...\", %p, %p, %p, %s)\n", hsa, str, formatPtr, numTokensPtr, endPtr, pstates[(state & 0x3)]);
3093 /*@=modfilesys@*/
3094 
3095  /* upper limit on number of individual formats */
3096  numTokens = 0;
3097  if (str != NULL)
3098  for (chptr = str; *chptr != '\0'; chptr++)
3099  if (*chptr == '%') numTokens++;
3100  numTokens = numTokens * 2 + 1;
3101 
3102  format = xcalloc(numTokens, sizeof(*format));
3103  if (endPtr) *endPtr = NULL;
3104 
3105 /*@-infloops@*/ /* LCL: can't detect done termination */
3106  dst = start = str;
3107  numTokens = 0;
3108  token = NULL;
3109  if (start != NULL)
3110  while (*start != '\0') {
3111  switch (*start) {
3112  case '%':
3113  /* handle %% */
3114  if (*(start + 1) == '%') {
3115  if (token == NULL || token->type != PTOK_STRING) {
3116  token = format + numTokens++;
3117  token->type = PTOK_STRING;
3118  /*@-temptrans -assignexpose@*/
3119  dst = token->u.string.string = start;
3120  /*@=temptrans =assignexpose@*/
3121  }
3122  start++;
3123  *dst++ = *start++;
3124  /*@switchbreak@*/ break;
3125  }
3126 
3127  token = format + numTokens++;
3128  *dst++ = '\0';
3129  start++;
3130 
3131  if (*start == '|') {
3132  char * newEnd;
3133 
3134  start++;
3135  if (parseExpression(hsa, token, start, &newEnd))
3136  {
3137  format = freeFormat(format, numTokens);
3138  return 1;
3139  }
3140  start = newEnd;
3141  /*@switchbreak@*/ break;
3142  }
3143 
3144  /*@-assignexpose@*/
3145  token->u.tag.format = start;
3146  /*@=assignexpose@*/
3147  token->u.tag.pad = 0;
3148  token->u.tag.justOne = 0;
3149  token->u.tag.arrayCount = 0;
3150 
3151  chptr = start;
3152  while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
3153  if (!*chptr || *chptr == '%') {
3154  hsa->errmsg = _("missing { after %");
3155  format = freeFormat(format, numTokens);
3156  return 1;
3157  }
3158 
3159 /*@-modfilesys@*/
3160 if (_hdr_debug)
3161 fprintf(stderr, "\tchptr *%p = NUL\n", chptr);
3162 /*@=modfilesys@*/
3163  *chptr++ = '\0';
3164 
3165  while (start < chptr) {
3166  if (xisdigit((int)*start)) {
3167  i = strtoul(start, &start, 10);
3168  token->u.tag.pad += i;
3169  start = chptr;
3170  /*@innerbreak@*/ break;
3171  } else {
3172  start++;
3173  }
3174  }
3175 
3176  if (*start == '=') {
3177  token->u.tag.justOne = 1;
3178  start++;
3179  } else if (*start == '#') {
3180  token->u.tag.justOne = 1;
3181  token->u.tag.arrayCount = 1;
3182  start++;
3183  }
3184 
3185  next = start;
3186  while (*next && *next != '}') next++;
3187  if (!*next) {
3188  hsa->errmsg = _("missing } after %{");
3189  format = freeFormat(format, numTokens);
3190  return 1;
3191  }
3192 /*@-modfilesys@*/
3193 if (_hdr_debug)
3194 fprintf(stderr, "\tnext *%p = NUL\n", next);
3195 /*@=modfilesys@*/
3196  *next++ = '\0';
3197 
3198 #define isSEP(_c) ((_c) == ':' || (_c) == '|')
3199  chptr = start;
3200  while (!(*chptr == '\0' || isSEP(*chptr))) chptr++;
3201  /* Split ":bing|bang:boom" --qf pipeline formatters (if any) */
3202  while (isSEP(*chptr)) {
3203  if (chptr[1] == '\0' || isSEP(chptr[1])) {
3204  hsa->errmsg = _("empty tag format");
3205  format = freeFormat(format, numTokens);
3206  return 1;
3207  }
3208  /* Parse the formatter parameter list. */
3209  { char * te = chptr + 1;
3210  char * t = strchr(te, '(');
3211  char c;
3212 
3213  while (!(*te == '\0' || isSEP(*te))) {
3214 #ifdef NOTYET /* XXX some means of escaping is needed */
3215  if (te[0] == '\\' && te[1] != '\0') te++;
3216 #endif
3217  te++;
3218  }
3219  c = *te; *te = '\0';
3220  /* Parse (a,b,c) parameter list. */
3221  if (t != NULL) {
3222  *t++ = '\0';
3223  if (te <= t || te[-1] != ')') {
3224  hsa->errmsg = _("malformed parameter list");
3225  format = freeFormat(format, numTokens);
3226  return 1;
3227  }
3228  te[-1] = '\0';
3229  xx = argvAdd(&token->u.tag.params, t);
3230  } else
3231  xx = argvAdd(&token->u.tag.params, "");
3232 /*@-modfilesys@*/
3233 if (_hdr_debug)
3234 fprintf(stderr, "\tformat \"%s\" params \"%s\"\n", chptr, (t ? t : ""));
3235 /*@=modfilesys@*/
3236  xx = argvAdd(&token->u.tag.av, chptr);
3237  *te = c;
3238  *chptr = '\0';
3239  chptr = te;
3240  }
3241  }
3242 #undef isSEP
3243 
3244  if (*start == '\0') {
3245  hsa->errmsg = _("empty tag name");
3246  format = freeFormat(format, numTokens);
3247  return 1;
3248  }
3249 
3250  i = 0;
3251  token->type = PTOK_TAG;
3252 
3253  if (findTag(hsa, token, start)) {
3254  hsa->errmsg = _("unknown tag");
3255  format = freeFormat(format, numTokens);
3256  return 1;
3257  }
3258 
3259  dst = start = next;
3260 /*@-modfilesys@*/
3261 if (_hdr_debug)
3262 fprintf(stderr, "\tdst = start = next %p\n", dst);
3263 /*@=modfilesys@*/
3264  /*@switchbreak@*/ break;
3265 
3266  case '[':
3267 /*@-modfilesys@*/
3268 if (_hdr_debug)
3269 fprintf(stderr, "\t%s => %s *%p = NUL\n", pstates[(state & 0x3)], pstates[PARSER_IN_ARRAY], start);
3270 /*@=modfilesys@*/
3271  *start++ = '\0';
3272  token = format + numTokens++;
3273 
3274  if (parseFormat(hsa, start,
3275  &token->u.array.format,
3276  &token->u.array.numTokens,
3277  &start, PARSER_IN_ARRAY))
3278  {
3279  format = freeFormat(format, numTokens);
3280  return 1;
3281  }
3282 
3283  if (!start) {
3284  hsa->errmsg = _("] expected at end of array");
3285  format = freeFormat(format, numTokens);
3286  return 1;
3287  }
3288 
3289  dst = start;
3290 /*@-modfilesys@*/
3291 if (_hdr_debug)
3292 fprintf(stderr, "\tdst = start %p\n", dst);
3293 /*@=modfilesys@*/
3294 
3295  token->type = PTOK_ARRAY;
3296 
3297  /*@switchbreak@*/ break;
3298 
3299  case ']':
3300  if (state != PARSER_IN_ARRAY) {
3301  hsa->errmsg = _("unexpected ]");
3302  format = freeFormat(format, numTokens);
3303  return 1;
3304  }
3305  *start++ = '\0';
3306 /*@-modfilesys@*/
3307 if (_hdr_debug)
3308 fprintf(stderr, "\t<= %s %p[-1] = NUL\n", pstates[(state & 0x3)], start);
3309 /*@=modfilesys@*/
3310  if (endPtr) *endPtr = start;
3311  done = 1;
3312  /*@switchbreak@*/ break;
3313 
3314  case '}':
3315  if (state != PARSER_IN_EXPR) {
3316  hsa->errmsg = _("unexpected }");
3317  format = freeFormat(format, numTokens);
3318  return 1;
3319  }
3320  *start++ = '\0';
3321 /*@-modfilesys@*/
3322 if (_hdr_debug)
3323 fprintf(stderr, "\t<= %s %p[-1] = NUL\n", pstates[(state & 0x3)], start);
3324 /*@=modfilesys@*/
3325  if (endPtr) *endPtr = start;
3326  done = 1;
3327  /*@switchbreak@*/ break;
3328 
3329  default:
3330  if (token == NULL || token->type != PTOK_STRING) {
3331  token = format + numTokens++;
3332  token->type = PTOK_STRING;
3333  /*@-temptrans -assignexpose@*/
3334  dst = token->u.string.string = start;
3335  /*@=temptrans =assignexpose@*/
3336  }
3337 
3338 /*@-modfilesys@*/
3339 if (_hdr_debug)
3340 fprintf(stderr, "\t*%p = *%p \"%.30s\"\n", dst, start, start);
3341 /*@=modfilesys@*/
3342  if (start[0] == '\\' && start[1] != '\0') {
3343  start++;
3344  *dst++ = escapedChar(*start);
3345  *start++ = '\0';
3346  } else {
3347  *dst++ = *start++;
3348  }
3349  if (dst < start) *dst = '\0';
3350  /*@switchbreak@*/ break;
3351  }
3352  if (done)
3353  break;
3354  }
3355 /*@=infloops@*/
3356 
3357  if (dst != NULL)
3358  *dst = '\0';
3359 
3360  for (i = 0; i < (unsigned) numTokens; i++) {
3361  token = format + i;
3362  switch(token->type) {
3363  default:
3364  /*@switchbreak@*/ break;
3365  case PTOK_STRING:
3366  token->u.string.len = strlen(token->u.string.string);
3367  /*@switchbreak@*/ break;
3368  }
3369  }
3370 
3371  if (numTokensPtr != NULL)
3372  *numTokensPtr = numTokens;
3373  if (formatPtr != NULL)
3374  *formatPtr = format;
3375 
3376  return 0;
3377 }
3378 
3380  char * str, /*@out@*/ char ** endPtr)
3381 {
3382  char * chptr;
3383  char * end;
3384 
3385 /*@-modfilesys@*/
3386 if (_hdr_debug)
3387 fprintf(stderr, "--> parseExpression(%p, %p, \"%.20s...\", %p)\n", hsa, token, str, endPtr);
3388 /*@=modfilesys@*/
3389 
3390  hsa->errmsg = NULL;
3391  chptr = str;
3392  while (*chptr && *chptr != '?') chptr++;
3393 
3394  if (*chptr != '?') {
3395  hsa->errmsg = _("? expected in expression");
3396  return 1;
3397  }
3398 
3399  *chptr++ = '\0';
3400 
3401  if (*chptr != '{') {
3402  hsa->errmsg = _("{ expected after ? in expression");
3403  return 1;
3404  }
3405 
3406  chptr++;
3407 
3408  if (parseFormat(hsa, chptr, &token->u.cond.ifFormat,
3409  &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR))
3410  return 1;
3411 
3412  /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/
3413  if (!(end && *end)) {
3414  hsa->errmsg = _("} expected in expression");
3415  token->u.cond.ifFormat =
3416  freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
3417  return 1;
3418  }
3419 
3420  chptr = end;
3421  if (*chptr != ':' && *chptr != '|') {
3422  hsa->errmsg = _(": expected following ? subexpression");
3423  token->u.cond.ifFormat =
3424  freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
3425  return 1;
3426  }
3427 
3428  if (*chptr == '|') {
3429  if (parseFormat(hsa, NULL, &token->u.cond.elseFormat,
3430  &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
3431  {
3432  token->u.cond.ifFormat =
3433  freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
3434  return 1;
3435  }
3436  } else {
3437  chptr++;
3438 
3439  if (*chptr != '{') {
3440  hsa->errmsg = _("{ expected after : in expression");
3441  token->u.cond.ifFormat =
3442  freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
3443  return 1;
3444  }
3445 
3446  chptr++;
3447 
3448  if (parseFormat(hsa, chptr, &token->u.cond.elseFormat,
3449  &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
3450  return 1;
3451 
3452  /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */
3453  if (!(end && *end)) {
3454  hsa->errmsg = _("} expected in expression");
3455  token->u.cond.ifFormat =
3456  freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
3457  return 1;
3458  }
3459 
3460  chptr = end;
3461  if (*chptr != '|') {
3462  hsa->errmsg = _("| expected at end of expression");
3463  token->u.cond.ifFormat =
3464  freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
3465  token->u.cond.elseFormat =
3466  freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
3467  return 1;
3468  }
3469  }
3470 
3471  chptr++;
3472 
3473  *endPtr = chptr;
3474 
3475  token->type = PTOK_COND;
3476 
3477  (void) findTag(hsa, token, str);
3478 
3479  return 0;
3480 }
3481 
3491  HE_t he, HE_t ec)
3492  /*@modifies he, ec @*/
3493 {
3494  int rc = 0;
3495  if (!ec->avail) {
3496  he = rpmheClean(he);
3497  rc = fn(hsa->h, he);
3498  *ec = *he; /* structure copy. */
3499  if (!rc)
3500  ec->avail = 1;
3501  } else
3502  *he = *ec; /* structure copy. */
3503  he->freeData = 0;
3504  return rc;
3505 }
3506 
3514 /*@observer@*/ /*@null@*/
3515 static char * formatValue(headerSprintfArgs hsa, sprintfTag tag, int element)
3516  /*@modifies hsa @*/
3517 {
3518  HE_t vhe = memset(alloca(sizeof(*vhe)), 0, sizeof(*vhe));
3519  HE_t he = &tag->he;
3520  char * val = NULL;
3521  size_t need = 0;
3522  char * t, * te;
3523  int_64 ival = 0;
3524  rpmTagCount countBuf;
3525  int xx;
3526 
3527  if (!he->avail) {
3528  if (tag->ext) {
3529  xx = getExtension(hsa, tag->ext, he, hsa->ec + tag->extNum);
3530  } else {
3531  he->tag = tag->tagno;
3532 #ifdef NOTYET
3533  if (_usehge) {
3534  xx = headerGetExtension(hsa->h, he->tag, &he->t, &he->p, &he->c);
3535  if (xx) /* XXX 1 on success */
3536  he->freeData = 1;
3537  } else
3538 #endif
3539  {
3540  xx = headerGetEntry(hsa->h, he->tag, (hTYP_t)&he->t, &he->p, &he->c);
3541  if (xx) /* XXX 1 on success */
3542  he = rpmheMark(he);
3543  }
3544  xx = (xx == 0); /* XXX invert headerGetEntry return. */
3545  }
3546  if (xx) {
3547  (void) rpmheClean(he);
3548  he->t = RPM_STRING_TYPE;
3549  he->p.str = "(none)";
3550  he->c = 1;
3551  }
3552  he->avail = 1;
3553  }
3554 
3555  if (tag->arrayCount) {
3556  countBuf = he->c;
3557  he = rpmheClean(he);
3558  he->t = RPM_INT32_TYPE;
3559  he->p.i32p = &countBuf;
3560  he->c = 1;
3561  he->freeData = 0;
3562  }
3563 
3564  if (he->p.ptr)
3565  switch (he->t) {
3566  default:
3567  val = xstrdup("(unknown type)");
3568  need = strlen(val) + 1;
3569  goto exit;
3570  /*@notreached@*/ break;
3571  case RPM_I18NSTRING_TYPE:
3572  case RPM_STRING_ARRAY_TYPE:
3573  vhe->t = RPM_STRING_TYPE;
3574  vhe->p.str = he->p.argv[element];
3575  vhe->c = he->c;
3576  vhe->ix = (he->t == RPM_STRING_ARRAY_TYPE || he->c > 1 ? 0 : -1);
3577  break;
3578  case RPM_STRING_TYPE:
3579  vhe->p.str = he->p.str;
3580  vhe->t = RPM_STRING_TYPE;
3581  vhe->c = 0;
3582  vhe->ix = -1;
3583  break;
3584  case RPM_CHAR_TYPE:
3585  case RPM_INT8_TYPE:
3586  case RPM_INT16_TYPE:
3587  case RPM_INT32_TYPE:
3588  case RPM_INT64_TYPE:
3589  switch (he->t) {
3590  default:
3591 assert(0); /* XXX keep gcc quiet. */
3592  break;
3593  case RPM_CHAR_TYPE:
3594  case RPM_INT8_TYPE:
3595  ival = he->p.i8p[element];
3596  break;
3597  case RPM_INT16_TYPE:
3598  ival = he->p.ui16p[element]; /* XXX note unsigned. */
3599  break;
3600  case RPM_INT32_TYPE:
3601  ival = he->p.i32p[element];
3602  break;
3603  case RPM_INT64_TYPE:
3604  ival = he->p.i64p[element];
3605  break;
3606  }
3607  vhe->t = RPM_INT64_TYPE;
3608  vhe->p.i64p = &ival;
3609  vhe->c = he->c;
3610  vhe->ix = (he->c > 1 ? 0 : -1);
3612  vhe->ix = 0;
3613  break;
3614 
3615  case RPM_OPENPGP_TYPE: /* XXX W2DO? */
3616  case RPM_ASN1_TYPE: /* XXX W2DO? */
3617  case RPM_BIN_TYPE:
3618  vhe->t = RPM_BIN_TYPE;
3619  vhe->p.ptr = he->p.ptr;
3620  vhe->c = he->c;
3621  vhe->ix = -1;
3622  break;
3623  }
3624 
3625 /*@-compmempass@*/ /* vhe->p.ui64p is stack, not owned */
3626  if (tag->fmtfuncs) {
3627  char * nval;
3628  int i;
3629  for (i = 0; tag->av[i] != NULL; i++) {
3631  ARGV_t av;
3632  if ((fmt = tag->fmtfuncs[i]) == NULL)
3633  continue;
3634  /* If !1st formatter, and transformer, not extractor, save val. */
3635  if (val != NULL && *tag->av[i] == '|') {
3636  int ix = vhe->ix;
3637  vhe = rpmheClean(vhe);
3638  vhe->tag = he->tag;
3639  vhe->t = RPM_STRING_TYPE;
3640  vhe->p.str = xstrdup(val);
3641  vhe->c = he->c;
3642  vhe->ix = ix;
3643  vhe->freeData = 1;
3644  }
3645  av = NULL;
3646  if (tag->params && tag->params[i] && *tag->params[i] != '\0')
3647  xx = argvSplit(&av, tag->params[i], ",");
3648 
3649  nval = fmt(vhe, av);
3650 
3651 /*@-modfilesys@*/
3652 if (_hdr_debug)
3653 fprintf(stderr, "\t%s(%s) %p(%p,%p) ret \"%s\"\n", tag->av[i], tag->params[i], fmt, vhe, (av ? av : NULL), val);
3654 /*@=modfilesys@*/
3655 
3656  /* Accumulate (by appending) next formmatter's return string. */
3657  if (val == NULL)
3658  val = xstrdup((nval ? nval : ""));
3659  else {
3660  char * oval = val;
3661  /* XXX using ... | ... as separator is feeble. */
3662  val = rpmExpand(val, (*val ? " | " : ""), nval, NULL);
3663  oval = _free(oval);
3664  }
3665  nval = _free(nval);
3666  av = argvFree(av);
3667  }
3668  }
3669  if (val == NULL)
3670  val = intFormat(vhe, NULL, NULL);
3671 /*@=compmempass@*/
3672 assert(val != NULL);
3673  if (val)
3674  need = strlen(val) + 1;
3675 
3676 exit:
3677  if (!_tagcache)
3678  he = rpmheClean(he);
3679 
3680  if (val && need > 0) {
3681  if (tag->format && *tag->format && tag->pad) {
3682  size_t nb;
3683  nb = strlen(tag->format) + sizeof("%s");
3684  t = alloca(nb);
3685  (void) stpcpy( stpcpy( stpcpy(t, "%"), tag->format), "s");
3686  nb = tag->pad + strlen(val) + 1;
3687  te = xmalloc(nb);
3688  (void) snprintf(te, nb, t, val);
3689  te[nb-1] = '\0';
3690  val = _free(val);
3691  val = te;
3692  need += tag->pad;
3693  }
3694  t = hsaReserve(hsa, need);
3695  te = stpcpy(t, val);
3696  hsa->vallen += (te - t);
3697  val = _free(val);
3698  }
3699 
3700  return (hsa->val + hsa->vallen);
3701 }
3702 
3710 /*@observer@*/
3712  int element)
3713  /*@modifies hsa @*/
3714 {
3715  char numbuf[64]; /* XXX big enuf for "Tag_0x01234567" */
3716  char * t, * te;
3717  int i, j;
3718  int numElements;
3719  sprintfToken spft;
3720  sprintfTag tag = NULL;
3721  HE_t he = NULL;
3722  int condNumFormats;
3723  size_t need;
3724  int xx;
3725 
3726  /* we assume the token and header have been validated already! */
3727 
3728  switch (token->type) {
3729  case PTOK_NONE:
3730  break;
3731 
3732  case PTOK_STRING:
3733  need = token->u.string.len;
3734  if (need == 0) break;
3735  t = hsaReserve(hsa, need);
3736  te = stpcpy(t, token->u.string.string);
3737  hsa->vallen += (te - t);
3738  break;
3739 
3740  case PTOK_TAG:
3741  t = hsa->val + hsa->vallen;
3742  te = formatValue(hsa, &token->u.tag,
3743  (token->u.tag.justOne ? 0 : element));
3744  if (te == NULL)
3745  return NULL;
3746  break;
3747 
3748  case PTOK_COND:
3749  if (token->u.cond.tag.ext
3750  || headerIsEntry(hsa->h, token->u.cond.tag.tagno))
3751  {
3752  spft = token->u.cond.ifFormat;
3753  condNumFormats = token->u.cond.numIfTokens;
3754  } else {
3755  spft = token->u.cond.elseFormat;
3756  condNumFormats = token->u.cond.numElseTokens;
3757  }
3758 
3759  need = condNumFormats * 20;
3760  if (spft == NULL || need == 0) break;
3761 
3762  t = hsaReserve(hsa, need);
3763  for (i = 0; i < condNumFormats; i++, spft++) {
3764  te = singleSprintf(hsa, spft, element);
3765  if (te == NULL)
3766  return NULL;
3767  }
3768  break;
3769 
3770  case PTOK_ARRAY:
3771  numElements = -1;
3772  spft = token->u.array.format;
3773  for (i = 0; i < token->u.array.numTokens; i++, spft++)
3774  {
3775  tag = &spft->u.tag;
3776  if (spft->type != PTOK_TAG || tag->arrayCount || tag->justOne)
3777  continue;
3778  he = &tag->he;
3779  if (!he->avail) {
3780  he->tag = tag->tagno;
3781  if (tag->ext)
3782  xx = getExtension(hsa, tag->ext, he, hsa->ec + tag->extNum);
3783  else {
3784 #ifdef NOTYET
3785  if (_usehge) {
3786  xx = headerGetExtension(hsa->h, he->tag, &he->t, &he->p, &he->c);
3787  if (xx)
3788  he->freeData = 1;
3789  } else
3790 #endif
3791  {
3792  xx = headerGetEntry(hsa->h, he->tag, (hTYP_t)&he->t, &he->p, &he->c);
3793  if (xx) /* XXX 1 on success */
3794  rpmheMark(he);
3795  }
3796  xx = (xx == 0); /* XXX invert headerGetEntry return. */
3797  }
3798  if (xx) {
3799  (void) rpmheClean(he);
3800  continue;
3801  }
3802  he->avail = 1;
3803  }
3804 
3805  /* Check iteration arrays are same dimension (or scalar). */
3806  switch (he->t) {
3807  default:
3808  if (numElements == -1) {
3809  numElements = he->c;
3810  /*@switchbreak@*/ break;
3811  }
3812  if (he->c == numElements)
3813  /*@switchbreak@*/ break;
3814  hsa->errmsg =
3815  _("array iterator used with different sized arrays");
3816  he = rpmheClean(he);
3817  return NULL;
3818  /*@notreached@*/ /*@switchbreak@*/ break;
3819  case RPM_OPENPGP_TYPE:
3820  case RPM_ASN1_TYPE:
3821  case RPM_BIN_TYPE:
3822  case RPM_STRING_TYPE:
3823  if (numElements == -1)
3824  numElements = 1;
3825  /*@switchbreak@*/ break;
3826  }
3827  if (!_tagcache)
3828  he = rpmheClean(he);
3829  }
3830  spft = token->u.array.format;
3831 
3832  if (numElements == -1) {
3833 #ifdef DYING /* XXX lots of pugly "(none)" lines with --conflicts. */
3834  need = sizeof("(none)\n") - 1;
3835  t = hsaReserve(hsa, need);
3836  te = stpcpy(t, "(none)\n");
3837  hsa->vallen += (te - t);
3838 #endif
3839  } else {
3840  int isxml;
3841  int isyaml;
3842 
3843  need = numElements * token->u.array.numTokens;
3844  if (need == 0) break;
3845 
3846  tag = &spft->u.tag;
3847 
3848  /* XXX Ick: +1 needed to handle :extractor |transformer marking. */
3849  isxml = (spft->type == PTOK_TAG && tag->av != NULL &&
3850  tag->av[0] != NULL && !strcmp(tag->av[0]+1, "xml"));
3851  isyaml = (spft->type == PTOK_TAG && tag->av != NULL &&
3852  tag->av[0] != NULL && !strcmp(tag->av[0]+1, "yaml"));
3853 
3854  if (isxml) {
3855  const char * tagN = myTagName(hsa->tags, tag->tagno, NULL);
3856  /* XXX display "Tag_0x01234567" for unknown tags. */
3857  if (tagN == NULL) {
3858  (void) snprintf(numbuf, sizeof(numbuf), "Tag_0x%08x",
3859  tag->tagno);
3860  numbuf[sizeof(numbuf)-1] = '\0';
3861  tagN = numbuf;
3862  }
3863  need = sizeof(" <rpmTag name=\"\">\n") + strlen(tagN);
3864  te = t = hsaReserve(hsa, need);
3865  te = stpcpy( stpcpy( stpcpy(te, " <rpmTag name=\""), tagN), "\">\n");
3866  hsa->vallen += (te - t);
3867  }
3868  if (isyaml) {
3869  int tagT = -1;
3870  const char * tagN = myTagName(hsa->tags, tag->tagno, &tagT);
3871  /* XXX display "Tag_0x01234567" for unknown tags. */
3872  if (tagN == NULL) {
3873  (void) snprintf(numbuf, sizeof(numbuf), "Tag_0x%08x",
3874  tag->tagno);
3875  numbuf[sizeof(numbuf)-1] = '\0';
3876  tagN = numbuf;
3877  tagT = numElements > 1
3879  }
3880  need = sizeof(" : - ") + strlen(tagN);
3881  te = t = hsaReserve(hsa, need);
3882  *te++ = ' ';
3883  *te++ = ' ';
3884  te = stpcpy(te, tagN);
3885  *te++ = ':';
3886  *te++ = (((tagT & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE)
3887  ? '\n' : ' ');
3888  *te = '\0';
3889  hsa->vallen += (te - t);
3890  }
3891 
3892  need = numElements * token->u.array.numTokens * 10;
3893  t = hsaReserve(hsa, need);
3894  for (j = 0; j < numElements; j++) {
3895  spft = token->u.array.format;
3896  for (i = 0; i < token->u.array.numTokens; i++, spft++) {
3897  te = singleSprintf(hsa, spft, j);
3898  if (te == NULL)
3899  return NULL;
3900  }
3901  }
3902 
3903  if (isxml) {
3904  need = sizeof(" </rpmTag>\n") - 1;
3905  te = t = hsaReserve(hsa, need);
3906  te = stpcpy(te, " </rpmTag>\n");
3907  hsa->vallen += (te - t);
3908  }
3909  if (isyaml) {
3910 #if 0
3911  need = sizeof("\n") - 1;
3912  te = t = hsaReserve(hsa, need);
3913  te = stpcpy(te, "\n");
3914  hsa->vallen += (te - t);
3915 #endif
3916  }
3917 
3918  }
3919  break;
3920  }
3921 
3922  return (hsa->val + hsa->vallen);
3923 }
3924 
3931 static /*@only@*/ HE_t
3932 rpmecNew(const headerSprintfExtension exts, /*@null@*/ int * necp)
3933  /*@*/
3934 {
3936  HE_t ec;
3937  int extNum = 0;
3938 
3939  if (exts != NULL)
3940  for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
3941  ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1), extNum++)
3942  {
3943  ;
3944  }
3945  if (necp)
3946  *necp = extNum;
3947  ec = xcalloc(extNum+1, sizeof(*ec)); /* XXX +1 unnecessary */
3948  return ec;
3949 }
3950 
3957 static /*@null@*/ HE_t
3958 rpmecFree(const headerSprintfExtension exts, /*@only@*/ HE_t ec)
3959  /*@modifies ec @*/
3960 {
3962  int extNum;
3963 
3964  for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
3965  ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1), extNum++)
3966  {
3967  (void) rpmheClean(&ec[extNum]);
3968  }
3969 
3970  ec = _free(ec);
3971  return NULL;
3972 }
3973 
3974 extern const struct headerTagTableEntry_s * rpmTagTable;
3975 
3987 static /*@only@*/ /*@null@*/
3988 char * headerSprintf(Header h, const char * fmt,
3989  const struct headerTagTableEntry_s * tags,
3990  const struct headerSprintfExtension_s * exts,
3991  /*@null@*/ /*@out@*/ errmsg_t * errmsg)
3992  /*@modifies h, *errmsg @*/
3993  /*@requires maxSet(errmsg) >= 0 @*/
3994 {
3995  headerSprintfArgs hsa = memset(alloca(sizeof(*hsa)), 0, sizeof(*hsa));
3996  sprintfToken nextfmt;
3997  sprintfTag tag;
3998  char * t, * te;
3999  int isxml;
4000  int isyaml;
4001  int need;
4002 
4003 /*@-modfilesys@*/
4004 if (_hdr_debug)
4005 fprintf(stderr, "==> headerSprintf(%p, \"%s\", %p, %p, %p)\n", h, fmt, tags, exts, errmsg);
4006 /*@=modfilesys@*/
4007 
4008  /* Set some reasonable defaults */
4009  if (tags == NULL)
4010  tags = rpmTagTable;
4011 
4012  hsa->h = headerLink(h);
4013  hsa->fmt = xstrdup(fmt);
4014 /*@-castexpose@*/ /* FIX: legacy API shouldn't change. */
4015  hsa->exts = (headerSprintfExtension) exts;
4016  hsa->tags = (headerTagTableEntry) tags;
4017 /*@=castexpose@*/
4018  hsa->errmsg = NULL;
4019 
4020  if (parseFormat(hsa, hsa->fmt, &hsa->format, &hsa->numTokens, NULL, PARSER_BEGIN))
4021  goto exit;
4022 
4023  hsa->nec = 0;
4024  hsa->ec = rpmecNew(hsa->exts, &hsa->nec);
4025  hsa->val = xstrdup("");
4026 
4027  tag =
4028  (hsa->format->type == PTOK_TAG
4029  ? &hsa->format->u.tag :
4030  (hsa->format->type == PTOK_ARRAY
4031  ? &hsa->format->u.array.format->u.tag :
4032  NULL));
4033  /* XXX Ick: +1 needed to handle :extractor |transformer marking. */
4034  isxml = (tag != NULL && tag->tagno == -2 && tag->av != NULL
4035  && tag->av[0] != NULL && !strcmp(tag->av[0]+1, "xml"));
4036  isyaml = (tag != NULL && tag->tagno == -2 && tag->av != NULL
4037  && tag->av[0] != NULL && !strcmp(tag->av[0]+1, "yaml"));
4038 
4039  if (isxml) {
4040  need = sizeof("<rpmHeader>\n") - 1;
4041  t = hsaReserve(hsa, need);
4042  te = stpcpy(t, "<rpmHeader>\n");
4043  hsa->vallen += (te - t);
4044  }
4045  if (isyaml) {
4046  need = sizeof("- !!omap\n") - 1;
4047  t = hsaReserve(hsa, need);
4048  te = stpcpy(t, "- !!omap\n");
4049  hsa->vallen += (te - t);
4050  }
4051 
4052  hsa = hsaInit(hsa);
4053  while ((nextfmt = hsaNext(hsa)) != NULL) {
4054  te = singleSprintf(hsa, nextfmt, 0);
4055  if (te == NULL) {
4056  hsa->val = _free(hsa->val);
4057  break;
4058  }
4059  }
4060  hsa = hsaFini(hsa);
4061 
4062  if (isxml) {
4063  need = sizeof("</rpmHeader>\n") - 1;
4064  t = hsaReserve(hsa, need);
4065  te = stpcpy(t, "</rpmHeader>\n");
4066  hsa->vallen += (te - t);
4067  }
4068  if (isyaml) {
4069  need = sizeof("\n") - 1;
4070  t = hsaReserve(hsa, need);
4071  te = stpcpy(t, "\n");
4072  hsa->vallen += (te - t);
4073  }
4074 
4075  if (hsa->val != NULL && hsa->vallen < hsa->alloced)
4076  hsa->val = xrealloc(hsa->val, hsa->vallen+1);
4077 
4078  hsa->ec = rpmecFree(hsa->exts, hsa->ec);
4079  hsa->nec = 0;
4080  hsa->format = freeFormat(hsa->format, hsa->numTokens);
4081 
4082 exit:
4083 /*@-dependenttrans -observertrans @*/
4084  if (errmsg)
4085  *errmsg = hsa->errmsg;
4086 /*@=dependenttrans =observertrans @*/
4087  hsa->h = headerFree(hsa->h);
4088  hsa->fmt = _free(hsa->fmt);
4089  return hsa->val;
4090 }
4091 
4098 static
4099 void headerCopyTags(Header headerFrom, Header headerTo, hTAG_t tagstocopy)
4100  /*@modifies headerTo @*/
4101 {
4102  int * tagno;
4103 
4104  if (headerFrom == headerTo)
4105  return;
4106 
4107  for (tagno = tagstocopy; *tagno != 0; tagno++) {
4108  rpmTagType t;
4109  rpmTagData p = { .ptr = NULL };
4110  rpmTagCount c;
4111  if (headerIsEntry(headerTo, *tagno))
4112  continue;
4113  if (!headerGetEntryMinMemory(headerFrom, *tagno, (hTYP_t)&t, &p, &c))
4114  continue;
4115  (void) headerAddEntry(headerTo, *tagno, t, p.ptr, c);
4116  p.ptr = headerFreeData(p.ptr, t);
4117  }
4118 }
4119 
4120 /*@observer@*/ /*@unchecked@*/
4121 static struct HV_s hdrVec1 = {
4122  headerLink,
4123  headerUnlink,
4124  headerFree,
4125  headerNew,
4126  headerSort,
4127  headerUnsort,
4128  headerSizeof,
4129  headerUnload,
4130  headerReload,
4131  headerCopy,
4132  headerLoad,
4134  headerRead,
4135  headerWrite,
4136  headerIsEntry,
4137  headerFreeTag,
4146  headerSprintf,
4155  NULL, NULL,
4156  1
4157 };
4158 
4159 /*@-compmempass -redef@*/
4160 /*@observer@*/ /*@unchecked@*/
4161 HV_t hdrVec = &hdrVec1;
4162 /*@=compmempass =redef@*/