Change all the legacy interfaces to use the "concrete" integer tag types
[platform/upstream/rpm.git] / lib / legacy.c
1 /**
2  * \file lib/legacy.c
3  */
4
5 #include "system.h"
6
7 #include <rpm/header.h>
8 #include <rpm/rpmmacro.h>
9 #include <rpm/rpmstring.h>
10 #include <rpm/rpmfi.h>
11 #include <rpm/rpmds.h>
12
13 #include "debug.h"
14
15 static int dncmp(const void * a, const void * b)
16 {
17     const char *const * first = a;
18     const char *const * second = b;
19     return strcmp(*first, *second);
20 }
21
22 static void compressFilelist(Header h)
23 {
24     struct rpmtd_s fileNames;
25     char ** dirNames;
26     const char ** baseNames;
27     uint32_t * dirIndexes;
28     rpm_count_t count;
29     int xx, i;
30     int dirIndex = -1;
31
32     /*
33      * This assumes the file list is already sorted, and begins with a
34      * single '/'. That assumption isn't critical, but it makes things go
35      * a bit faster.
36      */
37
38     if (headerIsEntry(h, RPMTAG_DIRNAMES)) {
39         xx = headerDel(h, RPMTAG_OLDFILENAMES);
40         return;         /* Already converted. */
41     }
42
43     if (!headerGet(h, RPMTAG_OLDFILENAMES, &fileNames, HEADERGET_MINMEM)) 
44         return;
45     count = rpmtdCount(&fileNames);
46     if (count < 1) 
47         return;
48
49     dirNames = xmalloc(sizeof(*dirNames) * count);      /* worst case */
50     baseNames = xmalloc(sizeof(*dirNames) * count);
51     dirIndexes = xmalloc(sizeof(*dirIndexes) * count);
52
53     /* HACK. Source RPM, so just do things differently */
54     {   const char *fn = rpmtdGetString(&fileNames);
55         if (fn && *fn != '/') {
56             dirIndex = 0;
57             dirNames[dirIndex] = xstrdup("");
58             while ((i = rpmtdNext(&fileNames)) >= 0) {
59                 dirIndexes[i] = dirIndex;
60                 baseNames[i] = rpmtdGetString(&fileNames);
61             }
62             goto exit;
63         }
64     }
65
66     /* 
67      * XXX EVIL HACK, FIXME:
68      * This modifies (and then restores) a const string from rpmtd
69      * through basename retrieved from strrchr() which silently 
70      * casts away const on return.
71      */
72     while ((i = rpmtdNext(&fileNames)) >= 0) {
73         char ** needle;
74         char savechar;
75         char * baseName;
76         size_t len;
77         char *filename = (char *) rpmtdGetString(&fileNames); /* HACK HACK */
78
79         if (filename == NULL)   /* XXX can't happen */
80             continue;
81         baseName = strrchr(filename, '/') + 1;
82         len = baseName - filename;
83         needle = dirNames;
84         savechar = *baseName;
85         *baseName = '\0';
86         if (dirIndex < 0 ||
87             (needle = bsearch(&filename, dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) {
88             char *s = xmalloc(len + 1);
89             rstrlcpy(s, filename, len + 1);
90             dirIndexes[i] = ++dirIndex;
91             dirNames[dirIndex] = s;
92         } else
93             dirIndexes[i] = needle - dirNames;
94
95         *baseName = savechar;
96         baseNames[i] = baseName;
97     }
98
99 exit:
100     if (count > 0) {
101         headerPutUint32(h, RPMTAG_DIRINDEXES, dirIndexes, count);
102         headerPutStringArray(h, RPMTAG_BASENAMES, baseNames, count);
103         headerPutStringArray(h, RPMTAG_DIRNAMES, 
104                              (const char **) dirNames, dirIndex + 1);
105     }
106
107     rpmtdFreeData(&fileNames);
108     for (i = 0; i <= dirIndex; i++) {
109         free(dirNames[i]);
110     }
111     free(dirNames);
112     free(baseNames);
113     free(dirIndexes);
114
115     xx = headerDel(h, RPMTAG_OLDFILENAMES);
116 }
117
118 static void expandFilelist(Header h)
119 {
120     struct rpmtd_s filenames;
121
122     if (!headerIsEntry(h, RPMTAG_OLDFILENAMES)) {
123         (void) headerGet(h, RPMTAG_FILENAMES, &filenames, HEADERGET_EXT);
124         if (rpmtdCount(&filenames) < 1)
125             return;
126         rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES);
127         headerPut(h, &filenames, HEADERPUT_DEFAULT);
128         rpmtdFreeData(&filenames);
129     }
130
131     (void) headerDel(h, RPMTAG_DIRNAMES);
132     (void) headerDel(h, RPMTAG_BASENAMES);
133     (void) headerDel(h, RPMTAG_DIRINDEXES);
134 }
135
136 /*
137  * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
138  * Retrofit an explicit "Provides: name = epoch:version-release.
139  */
140 static void providePackageNVR(Header h)
141 {
142     const char *name = headerGetString(h, RPMTAG_NAME);
143     char *pEVR = headerGetAsString(h, RPMTAG_EVR);
144     rpmsenseFlags pFlags = RPMSENSE_EQUAL;
145     int bingo = 1;
146     struct rpmtd_s pnames;
147     rpmds hds, nvrds;
148
149     /* Generate provides for this package name-version-release. */
150     if (!(name && pEVR))
151         return;
152
153     /*
154      * Rpm prior to 3.0.3 does not have versioned provides.
155      * If no provides at all are available, we can just add.
156      */
157     if (!headerGet(h, RPMTAG_PROVIDENAME, &pnames, HEADERGET_MINMEM)) {
158         goto exit;
159     }
160
161     /*
162      * Otherwise, fill in entries on legacy packages.
163      */
164     if (!headerIsEntry(h, RPMTAG_PROVIDEVERSION)) {
165         while (rpmtdNext(&pnames) >= 0) {
166             rpmsenseFlags fdummy = RPMSENSE_ANY;
167
168             headerPutString(h, RPMTAG_PROVIDEVERSION, "");
169             headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &fdummy, 1);
170         }
171         goto exit;
172     }
173
174     /* see if we already have this provide */
175     hds = rpmdsNew(h, RPMTAG_PROVIDENAME, 0);
176     nvrds = rpmdsSingle(RPMTAG_PROVIDENAME, name, pEVR, pFlags);
177     if (rpmdsFind(hds, nvrds) >= 0) {
178         bingo = 0;
179     }
180     rpmdsFree(hds);
181     rpmdsFree(nvrds);
182     
183
184 exit:
185     if (bingo) {
186         headerPutString(h, RPMTAG_PROVIDENAME, name);
187         headerPutString(h, RPMTAG_PROVIDEVERSION, pEVR);
188         headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pFlags, 1);
189     }
190     rpmtdFreeData(&pnames);
191     free(pEVR);
192 }
193
194 static void legacyRetrofit(Header h)
195 {
196     /*
197      * The file list was moved to a more compressed format which not
198      * only saves memory (nice), but gives fingerprinting a nice, fat
199      * speed boost (very nice). Go ahead and convert old headers to
200      * the new style (this is a noop for new headers).
201      */
202      compressFilelist(h);
203
204     /* Retrofit "Provide: name = EVR" for binary packages. */
205     if (!headerIsSource(h)) {
206         providePackageNVR(h);
207     }
208 }
209
210 int headerConvert(Header h, headerConvOps op)
211 {
212     int rc = 1;
213
214     if (h == NULL)
215         return 0;
216
217     switch (op) {
218     case HEADERCONV_EXPANDFILELIST:
219         expandFilelist(h);
220         break;
221     case HEADERCONV_COMPRESSFILELIST:
222         compressFilelist(h);
223         break;
224     case HEADERCONV_RETROFIT_V3:
225         legacyRetrofit(h);
226         break;
227     default:
228         rc = 0;
229         break;
230     }
231     return rc;
232 };
233
234 /*
235  * Backwards compatibility wrappers for legacy interfaces.
236  * Remove these some day...
237  */
238 #define _RPM_4_4_COMPAT
239 #include <rpm/rpmlegacy.h>
240
241 /* dumb macro to avoid 50 copies of this code while converting... */
242 #define TDWRAP() \
243     if (type) \
244         *type = td.type; \
245     if (p) \
246         *p = td.data; \
247     else \
248         rpmtdFreeData(&td); \
249     if (c) \
250         *c = td.count
251
252 int headerRemoveEntry(Header h, rpm_tag_t tag)
253 {
254     return headerDel(h, tag);
255 }
256
257 static void *_headerFreeData(rpm_data_t data, rpm_tagtype_t type)
258 {
259     if (data) {
260         if (type == RPM_FORCEFREE_TYPE ||
261             type == RPM_STRING_ARRAY_TYPE ||
262             type == RPM_I18NSTRING_TYPE ||
263             type == RPM_BIN_TYPE)
264                 free(data);
265     }
266     return NULL;
267 }
268
269 void * headerFreeData(rpm_data_t data, rpm_tagtype_t type)
270 {
271     return _headerFreeData(data, type);
272 }
273
274 void * headerFreeTag(Header h, rpm_data_t data, rpm_tagtype_t type)
275 {
276     return _headerFreeData(data, type);
277 }
278
279 static int headerGetWrap(Header h, rpm_tag_t tag,
280                 rpm_tagtype_t * type,
281                 rpm_data_t * p,
282                 rpm_count_t * c,
283                 headerGetFlags flags)
284 {
285     struct rpmtd_s td;
286     int rc;
287
288     rc = headerGet(h, tag, &td, flags);
289     TDWRAP();
290     return rc;
291 }
292
293 int headerGetEntry(Header h, rpm_tag_t tag,
294                         rpm_tagtype_t * type,
295                         rpm_data_t * p,
296                         rpm_count_t * c)
297 {
298     return headerGetWrap(h, tag, type, p, c, HEADERGET_DEFAULT);
299 }
300
301 int headerGetEntryMinMemory(Header h, rpm_tag_t tag,
302                         rpm_tagtype_t * type,
303                         rpm_data_t * p,
304                         rpm_count_t * c)
305 {
306     return headerGetWrap(h, tag, type, (rpm_data_t) p, c, HEADERGET_MINMEM);
307 }
308
309 /* XXX shut up compiler warning from missing prototype */
310 int headerGetRawEntry(Header h, rpm_tag_t tag, rpm_tagtype_t * type, rpm_data_t * p,
311                 rpm_count_t * c);
312
313 int headerGetRawEntry(Header h, rpm_tag_t tag, rpm_tagtype_t * type, rpm_data_t * p,
314                 rpm_count_t * c)
315 {
316     if (p == NULL) 
317         return headerIsEntry(h, tag);
318
319     return headerGetWrap(h, tag, type, p, c, HEADERGET_RAW);
320 }
321
322 int headerNextIterator(HeaderIterator hi,
323                 rpm_tag_t * tag,
324                 rpm_tagtype_t * type,
325                 rpm_data_t * p,
326                 rpm_count_t * c)
327 {
328     struct rpmtd_s td;
329     int rc;
330
331     rc = headerNext(hi, &td);
332     if (tag)
333         *tag = td.tag;
334     TDWRAP();
335     return rc;
336 }
337
338 int headerModifyEntry(Header h, rpm_tag_t tag, rpm_tagtype_t type,
339                         rpm_constdata_t p, rpm_count_t c)
340 {
341     struct rpmtd_s td = {
342         .tag = tag,
343         .type = type,
344         .data = (void *) p,
345         .count = c,
346     };
347     return headerMod(h, &td);
348 }
349
350 static int headerPutWrap(Header h, rpm_tag_t tag, rpm_tagtype_t type,
351                 rpm_constdata_t p, rpm_count_t c, headerPutFlags flags)
352 {
353     struct rpmtd_s td = {
354         .tag = tag,
355         .type = type,
356         .data = (void *) p,
357         .count = c,
358     };
359     return headerPut(h, &td, flags);
360 }
361
362 int headerAddOrAppendEntry(Header h, rpm_tag_t tag, rpm_tagtype_t type,
363                 rpm_constdata_t p, rpm_count_t c)
364 {
365     return headerPutWrap(h, tag, type, p, c, HEADERPUT_APPEND);
366 }
367
368 int headerAppendEntry(Header h, rpm_tag_t tag, rpm_tagtype_t type,
369                 rpm_constdata_t p, rpm_count_t c)
370 {
371     return headerPutWrap(h, tag, type, p, c, HEADERPUT_APPEND);
372 }
373
374 int headerAddEntry(Header h, rpm_tag_t tag, rpm_tagtype_t type,
375                 rpm_constdata_t p, rpm_count_t c)
376 {
377     return headerPutWrap(h, tag, type, p, c, HEADERPUT_DEFAULT);
378 }
379 #undef _RPM_4_4_COMPAT