2 * \file lib/headerutil.c
7 #include <rpm/rpmtypes.h>
8 #include <rpm/header.h>
9 #include <rpm/rpmstring.h>
10 #include <rpm/rpmds.h>
14 int headerIsSource(Header h)
16 return (!headerIsEntry(h, RPMTAG_SOURCERPM));
19 Header headerCopy(Header h)
21 Header nh = headerNew();
25 hi = headerInitIterator(h);
26 while (headerNext(hi, &td)) {
27 if (rpmtdCount(&td) > 0) {
28 (void) headerPut(nh, &td, HEADERPUT_DEFAULT);
32 headerFreeIterator(hi);
37 void headerCopyTags(Header headerFrom, Header headerTo,
38 const rpmTagVal * tagstocopy)
43 if (headerFrom == headerTo)
46 for (p = tagstocopy; *p != 0; p++) {
47 if (headerIsEntry(headerTo, *p))
49 if (!headerGet(headerFrom, *p, &td, (HEADERGET_MINMEM|HEADERGET_RAW)))
51 (void) headerPut(headerTo, &td, HEADERPUT_DEFAULT);
56 char * headerGetAsString(Header h, rpmTagVal tag)
61 if (headerGet(h, tag, &td, HEADERGET_EXT)) {
62 if (rpmtdCount(&td) == 1) {
63 res = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL);
70 const char * headerGetString(Header h, rpmTagVal tag)
72 const char *res = NULL;
75 if (headerGet(h, tag, &td, HEADERGET_MINMEM)) {
76 if (rpmtdCount(&td) == 1) {
77 res = rpmtdGetString(&td);
84 uint64_t headerGetNumber(Header h, rpmTagVal tag)
89 if (headerGet(h, tag, &td, HEADERGET_EXT)) {
90 if (rpmtdCount(&td) == 1) {
91 res = rpmtdGetNumber(&td);
99 * Sanity check data types against tag table before putting. Assume
100 * append on all array-types.
102 static int headerPutType(Header h, rpmTagVal tag, rpmTagType reqtype,
103 rpm_constdata_t data, rpm_count_t size)
106 rpmTagType type = rpmTagGetTagType(tag);
107 rpmTagReturnType retype = rpmTagGetReturnType(tag);
108 headerPutFlags flags = HEADERPUT_APPEND;
111 /* Basic sanity checks: type must match and there must be data to put */
113 || size < 1 || data == NULL || h == NULL) {
118 * Non-array types can't be appended to. Binary types use size
119 * for data length, for other non-array types size must be 1.
121 if (retype != RPM_ARRAY_RETURN_TYPE) {
122 flags = HEADERPUT_DEFAULT;
123 if (type != RPM_BIN_TYPE && size != 1) {
132 td.data = (void *) data;
135 valid = headerPut(h, &td, flags);
141 int headerPutString(Header h, rpmTagVal tag, const char *val)
143 rpmTagType type = rpmTagGetTagType(tag);
144 const void *sptr = NULL;
146 /* string arrays expect char **, arrange that */
147 if (type == RPM_STRING_ARRAY_TYPE || type == RPM_I18NSTRING_TYPE) {
149 } else if (type == RPM_STRING_TYPE) {
155 return headerPutType(h, tag, type, sptr, 1);
158 int headerPutStringArray(Header h, rpmTagVal tag, const char **array, rpm_count_t size)
160 return headerPutType(h, tag, RPM_STRING_ARRAY_TYPE, array, size);
163 int headerPutChar(Header h, rpmTagVal tag, const char *val, rpm_count_t size)
165 return headerPutType(h, tag, RPM_CHAR_TYPE, val, size);
168 int headerPutUint8(Header h, rpmTagVal tag, const uint8_t *val, rpm_count_t size)
170 return headerPutType(h, tag, RPM_INT8_TYPE, val, size);
173 int headerPutUint16(Header h, rpmTagVal tag, const uint16_t *val, rpm_count_t size)
175 return headerPutType(h, tag, RPM_INT16_TYPE, val, size);
178 int headerPutUint32(Header h, rpmTagVal tag, const uint32_t *val, rpm_count_t size)
180 return headerPutType(h, tag, RPM_INT32_TYPE, val, size);
183 int headerPutUint64(Header h, rpmTagVal tag, const uint64_t *val, rpm_count_t size)
185 return headerPutType(h, tag, RPM_INT64_TYPE, val, size);
188 int headerPutBin(Header h, rpmTagVal tag, const uint8_t *val, rpm_count_t size)
190 return headerPutType(h, tag, RPM_BIN_TYPE, val, size);
193 static int dncmp(const void * a, const void * b)
195 const char *const * first = a;
196 const char *const * second = b;
197 return strcmp(*first, *second);
200 static void compressFilelist(Header h)
202 struct rpmtd_s fileNames;
204 const char ** baseNames;
205 uint32_t * dirIndexes;
206 rpm_count_t count, realCount = 0;
211 * This assumes the file list is already sorted, and begins with a
212 * single '/'. That assumption isn't critical, but it makes things go
216 if (headerIsEntry(h, RPMTAG_DIRNAMES)) {
217 headerDel(h, RPMTAG_OLDFILENAMES);
218 return; /* Already converted. */
221 if (!headerGet(h, RPMTAG_OLDFILENAMES, &fileNames, HEADERGET_MINMEM))
223 count = rpmtdCount(&fileNames);
227 dirNames = xmalloc(sizeof(*dirNames) * count); /* worst case */
228 baseNames = xmalloc(sizeof(*dirNames) * count);
229 dirIndexes = xmalloc(sizeof(*dirIndexes) * count);
231 /* HACK. Source RPM, so just do things differently */
232 { const char *fn = rpmtdGetString(&fileNames);
233 if (fn && *fn != '/') {
235 dirNames[dirIndex] = xstrdup("");
236 while ((i = rpmtdNext(&fileNames)) >= 0) {
237 dirIndexes[i] = dirIndex;
238 baseNames[i] = rpmtdGetString(&fileNames);
246 * XXX EVIL HACK, FIXME:
247 * This modifies (and then restores) a const string from rpmtd
248 * through basename retrieved from strrchr() which silently
249 * casts away const on return.
251 while ((i = rpmtdNext(&fileNames)) >= 0) {
256 char *filename = (char *) rpmtdGetString(&fileNames); /* HACK HACK */
258 if (filename == NULL) /* XXX can't happen */
260 baseName = strrchr(filename, '/');
261 if (baseName == NULL) {
266 len = baseName - filename;
268 savechar = *baseName;
271 (needle = bsearch(&filename, dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) {
272 char *s = xmalloc(len + 1);
273 rstrlcpy(s, filename, len + 1);
274 dirIndexes[realCount] = ++dirIndex;
275 dirNames[dirIndex] = s;
277 dirIndexes[realCount] = needle - dirNames;
279 *baseName = savechar;
280 baseNames[realCount] = baseName;
286 headerPutUint32(h, RPMTAG_DIRINDEXES, dirIndexes, realCount);
287 headerPutStringArray(h, RPMTAG_BASENAMES, baseNames, realCount);
288 headerPutStringArray(h, RPMTAG_DIRNAMES,
289 (const char **) dirNames, dirIndex + 1);
292 rpmtdFreeData(&fileNames);
293 for (i = 0; i <= dirIndex; i++) {
300 headerDel(h, RPMTAG_OLDFILENAMES);
303 static void expandFilelist(Header h)
305 struct rpmtd_s filenames;
307 if (!headerIsEntry(h, RPMTAG_OLDFILENAMES)) {
308 (void) headerGet(h, RPMTAG_FILENAMES, &filenames, HEADERGET_EXT);
309 if (rpmtdCount(&filenames) < 1)
311 rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES);
312 headerPut(h, &filenames, HEADERPUT_DEFAULT);
313 rpmtdFreeData(&filenames);
316 (void) headerDel(h, RPMTAG_DIRNAMES);
317 (void) headerDel(h, RPMTAG_BASENAMES);
318 (void) headerDel(h, RPMTAG_DIRINDEXES);
322 * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
323 * Retrofit an explicit "Provides: name = epoch:version-release.
325 static void providePackageNVR(Header h)
327 const char *name = headerGetString(h, RPMTAG_NAME);
328 char *pEVR = headerGetAsString(h, RPMTAG_EVR);
329 rpmsenseFlags pFlags = RPMSENSE_EQUAL;
331 struct rpmtd_s pnames;
334 /* Generate provides for this package name-version-release. */
339 * Rpm prior to 3.0.3 does not have versioned provides.
340 * If no provides at all are available, we can just add.
342 if (!headerGet(h, RPMTAG_PROVIDENAME, &pnames, HEADERGET_MINMEM)) {
347 * Otherwise, fill in entries on legacy packages.
349 if (!headerIsEntry(h, RPMTAG_PROVIDEVERSION)) {
350 while (rpmtdNext(&pnames) >= 0) {
351 rpmsenseFlags fdummy = RPMSENSE_ANY;
353 headerPutString(h, RPMTAG_PROVIDEVERSION, "");
354 headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &fdummy, 1);
359 /* see if we already have this provide */
360 hds = rpmdsNew(h, RPMTAG_PROVIDENAME, 0);
361 nvrds = rpmdsSingle(RPMTAG_PROVIDENAME, name, pEVR, pFlags);
362 if (rpmdsFind(hds, nvrds) >= 0) {
371 headerPutString(h, RPMTAG_PROVIDENAME, name);
372 headerPutString(h, RPMTAG_PROVIDEVERSION, pEVR);
373 headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pFlags, 1);
375 rpmtdFreeData(&pnames);
379 static void legacyRetrofit(Header h)
382 * The file list was moved to a more compressed format which not
383 * only saves memory (nice), but gives fingerprinting a nice, fat
384 * speed boost (very nice). Go ahead and convert old headers to
385 * the new style (this is a noop for new headers).
389 /* Retrofit "Provide: name = EVR" for binary packages. */
390 if (!headerIsSource(h)) {
391 providePackageNVR(h);
395 int headerConvert(Header h, int op)
403 case HEADERCONV_EXPANDFILELIST:
406 case HEADERCONV_COMPRESSFILELIST:
409 case HEADERCONV_RETROFIT_V3: