apply some patch for pythons rpm from opensource
[platform/upstream/rpm.git] / lib / headerutil.c
1 /** \ingroup rpmdb
2  * \file lib/headerutil.c
3  */
4
5 #include "system.h"
6
7 #include <rpm/rpmtypes.h>
8 #include <rpm/header.h>
9 #include <rpm/rpmstring.h>
10 #include <rpm/rpmds.h>
11
12 #include "debug.h"
13
14 int headerIsSource(Header h)
15 {
16     return (!headerIsEntry(h, RPMTAG_SOURCERPM));
17 }
18
19 Header headerCopy(Header h)
20 {
21     Header nh = headerNew();
22     HeaderIterator hi;
23     struct rpmtd_s td;
24    
25     hi = headerInitIterator(h);
26     while (headerNext(hi, &td)) {
27         if (rpmtdCount(&td) > 0) {
28             (void) headerPut(nh, &td, HEADERPUT_DEFAULT);
29         }
30         rpmtdFreeData(&td);
31     }
32     headerFreeIterator(hi);
33
34     return nh;
35 }
36
37 void headerCopyTags(Header headerFrom, Header headerTo, 
38                     const rpmTagVal * tagstocopy)
39 {
40     const rpmTagVal * p;
41     struct rpmtd_s td;
42
43     if (headerFrom == headerTo)
44         return;
45
46     for (p = tagstocopy; *p != 0; p++) {
47         if (headerIsEntry(headerTo, *p))
48             continue;
49         if (!headerGet(headerFrom, *p, &td, (HEADERGET_MINMEM|HEADERGET_RAW)))
50             continue;
51         (void) headerPut(headerTo, &td, HEADERPUT_DEFAULT);
52         rpmtdFreeData(&td);
53     }
54 }
55
56 char * headerGetAsString(Header h, rpmTagVal tag)
57 {
58     char *res = NULL;
59     struct rpmtd_s td;
60
61     if (headerGet(h, tag, &td, HEADERGET_EXT)) {
62         if (rpmtdCount(&td) == 1) {
63             res = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL);
64         }
65         rpmtdFreeData(&td);
66     }
67     return res;
68 }
69
70 const char * headerGetString(Header h, rpmTagVal tag)
71 {
72     const char *res = NULL;
73     struct rpmtd_s td;
74
75     if (headerGet(h, tag, &td, HEADERGET_MINMEM)) {
76         if (rpmtdCount(&td) == 1) {
77             res = rpmtdGetString(&td);
78         }
79         rpmtdFreeData(&td);
80     }
81     return res;
82 }
83
84 uint64_t headerGetNumber(Header h, rpmTagVal tag)
85 {
86     uint64_t res = 0;
87     struct rpmtd_s td;
88
89     if (headerGet(h, tag, &td, HEADERGET_EXT)) {
90         if (rpmtdCount(&td) == 1) {
91             res = rpmtdGetNumber(&td);
92         }
93         rpmtdFreeData(&td);
94     }
95     return res;
96 }
97
98 /*
99  * Sanity check data types against tag table before putting. Assume
100  * append on all array-types.
101  */
102 static int headerPutType(Header h, rpmTagVal tag, rpmTagType reqtype,
103                         rpm_constdata_t data, rpm_count_t size)
104 {
105     struct rpmtd_s td;
106     rpmTagType type = rpmTagGetTagType(tag);
107     rpmTagReturnType retype = rpmTagGetReturnType(tag);
108     headerPutFlags flags = HEADERPUT_APPEND; 
109     int valid = 1;
110
111     /* Basic sanity checks: type must match and there must be data to put */
112     if (type != reqtype 
113         || size < 1 || data == NULL || h == NULL) {
114         valid = 0;
115     }
116
117     /*
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.
120      */
121     if (retype != RPM_ARRAY_RETURN_TYPE) {
122         flags = HEADERPUT_DEFAULT;
123         if (type != RPM_BIN_TYPE && size != 1) {
124             valid = 0;
125         }
126     }
127
128     if (valid) {
129         rpmtdReset(&td);
130         td.tag = tag;
131         td.type = type;
132         td.data = (void *) data;
133         td.count = size;
134
135         valid = headerPut(h, &td, flags);
136     }
137
138     return valid;
139 }
140         
141 int headerPutString(Header h, rpmTagVal tag, const char *val)
142 {
143     rpmTagType type = rpmTagGetTagType(tag);
144     const void *sptr = NULL;
145
146     /* string arrays expect char **, arrange that */
147     if (type == RPM_STRING_ARRAY_TYPE || type == RPM_I18NSTRING_TYPE) {
148         sptr = &val;
149     } else if (type == RPM_STRING_TYPE) {
150         sptr = val;
151     } else {
152         return 0;
153     }
154
155     return headerPutType(h, tag, type, sptr, 1);
156 }
157
158 int headerPutStringArray(Header h, rpmTagVal tag, const char **array, rpm_count_t size)
159 {
160     return headerPutType(h, tag, RPM_STRING_ARRAY_TYPE, array, size);
161 }
162
163 int headerPutChar(Header h, rpmTagVal tag, const char *val, rpm_count_t size)
164 {
165     return headerPutType(h, tag, RPM_CHAR_TYPE, val, size);
166 }
167
168 int headerPutUint8(Header h, rpmTagVal tag, const uint8_t *val, rpm_count_t size)
169 {
170     return headerPutType(h, tag, RPM_INT8_TYPE, val, size);
171 }
172
173 int headerPutUint16(Header h, rpmTagVal tag, const uint16_t *val, rpm_count_t size)
174 {
175     return headerPutType(h, tag, RPM_INT16_TYPE, val, size);
176 }
177
178 int headerPutUint32(Header h, rpmTagVal tag, const uint32_t *val, rpm_count_t size)
179 {
180     return headerPutType(h, tag, RPM_INT32_TYPE, val, size);
181 }
182
183 int headerPutUint64(Header h, rpmTagVal tag, const uint64_t *val, rpm_count_t size)
184 {
185     return headerPutType(h, tag, RPM_INT64_TYPE, val, size);
186 }
187
188 int headerPutBin(Header h, rpmTagVal tag, const uint8_t *val, rpm_count_t size)
189 {
190     return headerPutType(h, tag, RPM_BIN_TYPE, val, size);
191 }
192
193 static int dncmp(const void * a, const void * b)
194 {
195     const char *const * first = a;
196     const char *const * second = b;
197     return strcmp(*first, *second);
198 }
199
200 static void compressFilelist(Header h)
201 {
202     struct rpmtd_s fileNames;
203     char ** dirNames;
204     const char ** baseNames;
205     uint32_t * dirIndexes;
206     rpm_count_t count, realCount = 0;
207     int i;
208     int dirIndex = -1;
209
210     /*
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
213      * a bit faster.
214      */
215
216     if (headerIsEntry(h, RPMTAG_DIRNAMES)) {
217         headerDel(h, RPMTAG_OLDFILENAMES);
218         return;         /* Already converted. */
219     }
220
221     if (!headerGet(h, RPMTAG_OLDFILENAMES, &fileNames, HEADERGET_MINMEM)) 
222         return;
223     count = rpmtdCount(&fileNames);
224     if (count < 1) 
225         return;
226
227     dirNames = xmalloc(sizeof(*dirNames) * count);      /* worst case */
228     baseNames = xmalloc(sizeof(*dirNames) * count);
229     dirIndexes = xmalloc(sizeof(*dirIndexes) * count);
230
231     /* HACK. Source RPM, so just do things differently */
232     {   const char *fn = rpmtdGetString(&fileNames);
233         if (fn && *fn != '/') {
234             dirIndex = 0;
235             dirNames[dirIndex] = xstrdup("");
236             while ((i = rpmtdNext(&fileNames)) >= 0) {
237                 dirIndexes[i] = dirIndex;
238                 baseNames[i] = rpmtdGetString(&fileNames);
239                 realCount++;
240             }
241             goto exit;
242         }
243     }
244
245     /* 
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.
250      */
251     while ((i = rpmtdNext(&fileNames)) >= 0) {
252         char ** needle;
253         char savechar;
254         char * baseName;
255         size_t len;
256         char *filename = (char *) rpmtdGetString(&fileNames); /* HACK HACK */
257
258         if (filename == NULL)   /* XXX can't happen */
259             continue;
260         baseName = strrchr(filename, '/');
261         if (baseName == NULL) {
262             baseName = filename;
263         } else {
264             baseName += 1;
265         }
266         len = baseName - filename;
267         needle = dirNames;
268         savechar = *baseName;
269         *baseName = '\0';
270         if (dirIndex < 0 ||
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;
276         } else
277             dirIndexes[realCount] = needle - dirNames;
278
279         *baseName = savechar;
280         baseNames[realCount] = baseName;
281         realCount++;
282     }
283
284 exit:
285     if (count > 0) {
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);
290     }
291
292     rpmtdFreeData(&fileNames);
293     for (i = 0; i <= dirIndex; i++) {
294         free(dirNames[i]);
295     }
296     free(dirNames);
297     free(baseNames);
298     free(dirIndexes);
299
300     headerDel(h, RPMTAG_OLDFILENAMES);
301 }
302
303 static void expandFilelist(Header h)
304 {
305     struct rpmtd_s filenames;
306
307     if (!headerIsEntry(h, RPMTAG_OLDFILENAMES)) {
308         (void) headerGet(h, RPMTAG_FILENAMES, &filenames, HEADERGET_EXT);
309         if (rpmtdCount(&filenames) < 1)
310             return;
311         rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES);
312         headerPut(h, &filenames, HEADERPUT_DEFAULT);
313         rpmtdFreeData(&filenames);
314     }
315
316     (void) headerDel(h, RPMTAG_DIRNAMES);
317     (void) headerDel(h, RPMTAG_BASENAMES);
318     (void) headerDel(h, RPMTAG_DIRINDEXES);
319 }
320
321 /*
322  * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
323  * Retrofit an explicit "Provides: name = epoch:version-release.
324  */
325 static void providePackageNVR(Header h)
326 {
327     const char *name = headerGetString(h, RPMTAG_NAME);
328     char *pEVR = headerGetAsString(h, RPMTAG_EVR);
329     rpmsenseFlags pFlags = RPMSENSE_EQUAL;
330     int bingo = 1;
331     struct rpmtd_s pnames;
332     rpmds hds, nvrds;
333
334     /* Generate provides for this package name-version-release. */
335     if (!(name && pEVR))
336         return;
337
338     /*
339      * Rpm prior to 3.0.3 does not have versioned provides.
340      * If no provides at all are available, we can just add.
341      */
342     if (!headerGet(h, RPMTAG_PROVIDENAME, &pnames, HEADERGET_MINMEM)) {
343         goto exit;
344     }
345
346     /*
347      * Otherwise, fill in entries on legacy packages.
348      */
349     if (!headerIsEntry(h, RPMTAG_PROVIDEVERSION)) {
350         while (rpmtdNext(&pnames) >= 0) {
351             rpmsenseFlags fdummy = RPMSENSE_ANY;
352
353             headerPutString(h, RPMTAG_PROVIDEVERSION, "");
354             headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &fdummy, 1);
355         }
356         goto exit;
357     }
358
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) {
363         bingo = 0;
364     }
365     rpmdsFree(hds);
366     rpmdsFree(nvrds);
367     
368
369 exit:
370     if (bingo) {
371         headerPutString(h, RPMTAG_PROVIDENAME, name);
372         headerPutString(h, RPMTAG_PROVIDEVERSION, pEVR);
373         headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pFlags, 1);
374     }
375     rpmtdFreeData(&pnames);
376     free(pEVR);
377 }
378
379 static void legacyRetrofit(Header h)
380 {
381     /*
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).
386      */
387      compressFilelist(h);
388
389     /* Retrofit "Provide: name = EVR" for binary packages. */
390     if (!headerIsSource(h)) {
391         providePackageNVR(h);
392     }
393 }
394
395 int headerConvert(Header h, int op)
396 {
397     int rc = 1;
398
399     if (h == NULL)
400         return 0;
401
402     switch (op) {
403     case HEADERCONV_EXPANDFILELIST:
404         expandFilelist(h);
405         break;
406     case HEADERCONV_COMPRESSFILELIST:
407         compressFilelist(h);
408         break;
409     case HEADERCONV_RETROFIT_V3:
410         legacyRetrofit(h);
411         break;
412     default:
413         rc = 0;
414         break;
415     }
416     return rc;
417 };
418