Lose more ancient compatibility retrofit goo
[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     while ((i = rpmtdNext(&fileNames)) >= 0) {
67         char ** needle;
68         char savechar;
69         char * baseName;
70         size_t len;
71         const char *filename = rpmtdGetString(&fileNames);
72
73         if (filename == NULL)   /* XXX can't happen */
74             continue;
75         baseName = strrchr(filename, '/') + 1;
76         len = baseName - filename;
77         needle = dirNames;
78         savechar = *baseName;
79         *baseName = '\0';
80         if (dirIndex < 0 ||
81             (needle = bsearch(&filename, dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) {
82             char *s = xmalloc(len + 1);
83             rstrlcpy(s, filename, len + 1);
84             dirIndexes[i] = ++dirIndex;
85             dirNames[dirIndex] = s;
86         } else
87             dirIndexes[i] = needle - dirNames;
88
89         *baseName = savechar;
90         baseNames[i] = baseName;
91     }
92
93 exit:
94     if (count > 0) {
95         headerPutUint32(h, RPMTAG_DIRINDEXES, dirIndexes, count);
96         headerPutStringArray(h, RPMTAG_BASENAMES, baseNames, count);
97         headerPutStringArray(h, RPMTAG_DIRNAMES, 
98                              (const char **) dirNames, dirIndex + 1);
99     }
100
101     rpmtdFreeData(&fileNames);
102     for (i = 0; i <= dirIndex; i++) {
103         free(dirNames[i]);
104     }
105     free(dirNames);
106     free(baseNames);
107     free(dirIndexes);
108
109     xx = headerDel(h, RPMTAG_OLDFILENAMES);
110 }
111
112 static void expandFilelist(Header h)
113 {
114     struct rpmtd_s filenames;
115
116     if (!headerIsEntry(h, RPMTAG_OLDFILENAMES)) {
117         (void) headerGet(h, RPMTAG_FILENAMES, &filenames, HEADERGET_EXT);
118         if (rpmtdCount(&filenames) < 1)
119             return;
120         rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES);
121         headerPut(h, &filenames, HEADERPUT_DEFAULT);
122         rpmtdFreeData(&filenames);
123     }
124
125     (void) headerDel(h, RPMTAG_DIRNAMES);
126     (void) headerDel(h, RPMTAG_BASENAMES);
127     (void) headerDel(h, RPMTAG_DIRINDEXES);
128 }
129
130 /*
131  * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
132  * Retrofit an explicit "Provides: name = epoch:version-release.
133  */
134 static void providePackageNVR(Header h)
135 {
136     const char *name = headerGetString(h, RPMTAG_NAME);
137     char *pEVR = headerGetAsString(h, RPMTAG_EVR);
138     rpmsenseFlags pFlags = RPMSENSE_EQUAL;
139     int bingo = 1;
140     struct rpmtd_s pnames;
141     rpmds hds, nvrds;
142
143     /* Generate provides for this package name-version-release. */
144     if (!(name && pEVR))
145         return;
146
147     /*
148      * Rpm prior to 3.0.3 does not have versioned provides.
149      * If no provides at all are available, we can just add.
150      */
151     if (!headerGet(h, RPMTAG_PROVIDENAME, &pnames, HEADERGET_MINMEM)) {
152         goto exit;
153     }
154
155     /*
156      * Otherwise, fill in entries on legacy packages.
157      */
158     if (!headerIsEntry(h, RPMTAG_PROVIDEVERSION)) {
159         while (rpmtdNext(&pnames) >= 0) {
160             rpmsenseFlags fdummy = RPMSENSE_ANY;
161
162             headerPutString(h, RPMTAG_PROVIDEVERSION, "");
163             headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &fdummy, 1);
164         }
165         goto exit;
166     }
167
168     /* see if we already have this provide */
169     hds = rpmdsNew(h, RPMTAG_PROVIDENAME, 0);
170     nvrds = rpmdsSingle(RPMTAG_PROVIDENAME, name, pEVR, pFlags);
171     if (rpmdsFind(hds, nvrds) >= 0) {
172         bingo = 0;
173     }
174     rpmdsFree(hds);
175     rpmdsFree(nvrds);
176     
177
178 exit:
179     if (bingo) {
180         headerPutString(h, RPMTAG_PROVIDENAME, name);
181         headerPutString(h, RPMTAG_PROVIDEVERSION, pEVR);
182         headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pFlags, 1);
183     }
184     rpmtdFreeData(&pnames);
185     free(pEVR);
186 }
187
188 static void legacyRetrofit(Header h)
189 {
190     /*
191      * The file list was moved to a more compressed format which not
192      * only saves memory (nice), but gives fingerprinting a nice, fat
193      * speed boost (very nice). Go ahead and convert old headers to
194      * the new style (this is a noop for new headers).
195      */
196      compressFilelist(h);
197
198     /* Retrofit "Provide: name = EVR" for binary packages. */
199     if (!headerIsSource(h)) {
200         providePackageNVR(h);
201     }
202 }
203
204 int headerConvert(Header h, headerConvOps op)
205 {
206     int rc = 1;
207
208     if (h == NULL)
209         return 0;
210
211     switch (op) {
212     case HEADERCONV_EXPANDFILELIST:
213         expandFilelist(h);
214         break;
215     case HEADERCONV_COMPRESSFILELIST:
216         compressFilelist(h);
217         break;
218     case HEADERCONV_RETROFIT_V3:
219         legacyRetrofit(h);
220         break;
221     default:
222         rc = 0;
223         break;
224     }
225     return rc;
226 };
227
228 /*
229  * Backwards compatibility wrappers for legacy interfaces.
230  * Remove these some day...
231  */
232 #define _RPM_4_4_COMPAT
233 #include <rpm/rpmlegacy.h>
234
235 /* dumb macro to avoid 50 copies of this code while converting... */
236 #define TDWRAP() \
237     if (type) \
238         *type = td.type; \
239     if (p) \
240         *p = td.data; \
241     else \
242         rpmtdFreeData(&td); \
243     if (c) \
244         *c = td.count
245
246 int headerRemoveEntry(Header h, rpmTag tag)
247 {
248     return headerDel(h, tag);
249 }
250
251 static void *_headerFreeData(rpm_data_t data, rpmTagType type)
252 {
253     if (data) {
254         if (type == RPM_FORCEFREE_TYPE ||
255             type == RPM_STRING_ARRAY_TYPE ||
256             type == RPM_I18NSTRING_TYPE ||
257             type == RPM_BIN_TYPE)
258                 free(data);
259     }
260     return NULL;
261 }
262
263 void * headerFreeData(rpm_data_t data, rpmTagType type)
264 {
265     return _headerFreeData(data, type);
266 }
267
268 void * headerFreeTag(Header h, rpm_data_t data, rpmTagType type)
269 {
270     return _headerFreeData(data, type);
271 }
272
273 static int headerGetWrap(Header h, rpmTag tag,
274                 rpmTagType * type,
275                 rpm_data_t * p,
276                 rpm_count_t * c,
277                 headerGetFlags flags)
278 {
279     struct rpmtd_s td;
280     int rc;
281
282     rc = headerGet(h, tag, &td, flags);
283     TDWRAP();
284     return rc;
285 }
286
287 int headerGetEntry(Header h, rpmTag tag,
288                         rpmTagType * type,
289                         rpm_data_t * p,
290                         rpm_count_t * c)
291 {
292     return headerGetWrap(h, tag, type, p, c, HEADERGET_DEFAULT);
293 }
294
295 int headerGetEntryMinMemory(Header h, rpmTag tag,
296                         rpmTagType * type,
297                         rpm_data_t * p,
298                         rpm_count_t * c)
299 {
300     return headerGetWrap(h, tag, type, (rpm_data_t) p, c, HEADERGET_MINMEM);
301 }
302
303 /* XXX shut up compiler warning from missing prototype */
304 int headerGetRawEntry(Header h, rpmTag tag, rpmTagType * type, rpm_data_t * p,
305                 rpm_count_t * c);
306
307 int headerGetRawEntry(Header h, rpmTag tag, rpmTagType * type, rpm_data_t * p,
308                 rpm_count_t * c)
309 {
310     if (p == NULL) 
311         return headerIsEntry(h, tag);
312
313     return headerGetWrap(h, tag, type, p, c, HEADERGET_RAW);
314 }
315
316 int headerNextIterator(HeaderIterator hi,
317                 rpmTag * tag,
318                 rpmTagType * type,
319                 rpm_data_t * p,
320                 rpm_count_t * c)
321 {
322     struct rpmtd_s td;
323     int rc;
324
325     rc = headerNext(hi, &td);
326     if (tag)
327         *tag = td.tag;
328     TDWRAP();
329     return rc;
330 }
331
332 int headerModifyEntry(Header h, rpmTag tag, rpmTagType type,
333                         rpm_constdata_t p, rpm_count_t c)
334 {
335     struct rpmtd_s td = {
336         .tag = tag,
337         .type = type,
338         .data = (void *) p,
339         .count = c,
340     };
341     return headerMod(h, &td);
342 }
343
344 static int headerPutWrap(Header h, rpmTag tag, rpmTagType type,
345                 rpm_constdata_t p, rpm_count_t c, headerPutFlags flags)
346 {
347     struct rpmtd_s td = {
348         .tag = tag,
349         .type = type,
350         .data = (void *) p,
351         .count = c,
352     };
353     return headerPut(h, &td, flags);
354 }
355
356 int headerAddOrAppendEntry(Header h, rpmTag tag, rpmTagType type,
357                 rpm_constdata_t p, rpm_count_t c)
358 {
359     return headerPutWrap(h, tag, type, p, c, HEADERPUT_APPEND);
360 }
361
362 int headerAppendEntry(Header h, rpmTag tag, rpmTagType 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 headerAddEntry(Header h, rpmTag tag, rpmTagType type,
369                 rpm_constdata_t p, rpm_count_t c)
370 {
371     return headerPutWrap(h, tag, type, p, c, HEADERPUT_DEFAULT);
372 }
373 #undef _RPM_4_4_COMPAT