Add headerConvert() "proxy" for performing various conversions on header data
[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 "lib/legacy.h"
14
15 #include "debug.h"
16
17 int _noDirTokens = 0;
18
19 static int dncmp(const void * a, const void * b)
20 {
21     const char *const * first = a;
22     const char *const * second = b;
23     return strcmp(*first, *second);
24 }
25
26 void compressFilelist(Header h)
27 {
28     struct rpmtd_s fileNames;
29     char ** dirNames;
30     const char ** baseNames;
31     uint32_t * dirIndexes;
32     rpm_count_t count;
33     int xx, i;
34     int dirIndex = -1;
35
36     /*
37      * This assumes the file list is already sorted, and begins with a
38      * single '/'. That assumption isn't critical, but it makes things go
39      * a bit faster.
40      */
41
42     if (headerIsEntry(h, RPMTAG_DIRNAMES)) {
43         xx = headerDel(h, RPMTAG_OLDFILENAMES);
44         return;         /* Already converted. */
45     }
46
47     if (!headerGet(h, RPMTAG_OLDFILENAMES, &fileNames, HEADERGET_MINMEM)) 
48         return;
49     count = rpmtdCount(&fileNames);
50     if (count < 1) 
51         return;
52
53     dirNames = xmalloc(sizeof(*dirNames) * count);      /* worst case */
54     baseNames = xmalloc(sizeof(*dirNames) * count);
55     dirIndexes = xmalloc(sizeof(*dirIndexes) * count);
56
57     /* HACK. Source RPM, so just do things differently */
58     {   const char *fn = rpmtdGetString(&fileNames);
59         if (fn && *fn != '/') {
60             dirIndex = 0;
61             dirNames[dirIndex] = xstrdup("");
62             while ((i = rpmtdNext(&fileNames)) >= 0) {
63                 dirIndexes[i] = dirIndex;
64                 baseNames[i] = rpmtdGetString(&fileNames);
65             }
66             goto exit;
67         }
68     }
69
70     while ((i = rpmtdNext(&fileNames)) >= 0) {
71         char ** needle;
72         char savechar;
73         char * baseName;
74         size_t len;
75         const char *filename = rpmtdGetString(&fileNames);
76
77         if (filename == NULL)   /* XXX can't happen */
78             continue;
79         baseName = strrchr(filename, '/') + 1;
80         len = baseName - filename;
81         needle = dirNames;
82         savechar = *baseName;
83         *baseName = '\0';
84         if (dirIndex < 0 ||
85             (needle = bsearch(&filename, dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) {
86             char *s = xmalloc(len + 1);
87             rstrlcpy(s, filename, len + 1);
88             dirIndexes[i] = ++dirIndex;
89             dirNames[dirIndex] = s;
90         } else
91             dirIndexes[i] = needle - dirNames;
92
93         *baseName = savechar;
94         baseNames[i] = baseName;
95     }
96
97 exit:
98     if (count > 0) {
99         headerPutUint32(h, RPMTAG_DIRINDEXES, dirIndexes, count);
100         headerPutStringArray(h, RPMTAG_BASENAMES, baseNames, count);
101         headerPutStringArray(h, RPMTAG_DIRNAMES, 
102                              (const char **) dirNames, dirIndex + 1);
103     }
104
105     rpmtdFreeData(&fileNames);
106     for (i = 0; i <= dirIndex; i++) {
107         free(dirNames[i]);
108     }
109     free(dirNames);
110     free(baseNames);
111     free(dirIndexes);
112
113     xx = headerDel(h, RPMTAG_OLDFILENAMES);
114 }
115
116 void expandFilelist(Header h)
117 {
118     struct rpmtd_s filenames;
119
120     if (!headerIsEntry(h, RPMTAG_OLDFILENAMES)) {
121         (void) headerGet(h, RPMTAG_FILENAMES, &filenames, HEADERGET_EXT);
122         if (rpmtdCount(&filenames) < 1)
123             return;
124         rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES);
125         headerPut(h, &filenames, HEADERPUT_DEFAULT);
126         rpmtdFreeData(&filenames);
127     }
128
129     (void) headerDel(h, RPMTAG_DIRNAMES);
130     (void) headerDel(h, RPMTAG_BASENAMES);
131     (void) headerDel(h, RPMTAG_DIRINDEXES);
132 }
133
134 /*
135  * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
136  * Retrofit an explicit "Provides: name = epoch:version-release.
137  */
138 static void providePackageNVR(Header h)
139 {
140     const char *name;
141     char *pEVR;
142     rpmsenseFlags pFlags = RPMSENSE_EQUAL;
143     int bingo = 1;
144     struct rpmtd_s pnames;
145     rpmds hds, nvrds;
146
147     /* Generate provides for this package name-version-release. */
148     pEVR = headerGetEVR(h, &name);
149     if (!(name && pEVR))
150         return;
151
152     /*
153      * Rpm prior to 3.0.3 does not have versioned provides.
154      * If no provides at all are available, we can just add.
155      */
156     if (!headerGet(h, RPMTAG_PROVIDENAME, &pnames, HEADERGET_MINMEM)) {
157         goto exit;
158     }
159
160     /*
161      * Otherwise, fill in entries on legacy packages.
162      */
163     if (!headerIsEntry(h, RPMTAG_PROVIDEVERSION)) {
164         while (rpmtdNext(&pnames) >= 0) {
165             rpmsenseFlags fdummy = RPMSENSE_ANY;
166
167             headerPutString(h, RPMTAG_PROVIDEVERSION, "");
168             headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &fdummy, 1);
169         }
170         goto exit;
171     }
172
173     /* see if we already have this provide */
174     hds = rpmdsNew(h, RPMTAG_PROVIDENAME, 1);
175     nvrds = rpmdsSingle(RPMTAG_PROVIDENAME, name, pEVR, pFlags);
176     if (rpmdsFind(hds, nvrds) >= 0) {
177         bingo = 0;
178     }
179     rpmdsFree(hds);
180     rpmdsFree(nvrds);
181     
182
183 exit:
184     if (bingo) {
185         const char *evr = pEVR;
186         headerPutString(h, RPMTAG_PROVIDENAME, name);
187         headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
188         headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pFlags, 1);
189     }
190     rpmtdFreeData(&pnames);
191     free(pEVR);
192 }
193
194 void legacyRetrofit(Header h)
195 {
196     struct rpmtd_s dprefix;
197
198     /*
199      * We don't use these entries (and rpm >= 2 never has) and they are
200      * pretty misleading. Let's just get rid of them so they don't confuse
201      * anyone.
202      */
203     if (headerIsEntry(h, RPMTAG_FILEUSERNAME))
204         (void) headerDel(h, RPMTAG_FILEUIDS);
205     if (headerIsEntry(h, RPMTAG_FILEGROUPNAME))
206         (void) headerDel(h, RPMTAG_FILEGIDS);
207
208     /*
209      * We switched the way we do relocatable packages. We fix some of
210      * it up here, though the install code still has to be a bit 
211      * careful. This fixup makes queries give the new values though,
212      * which is quite handy.
213      */
214     if (headerGet(h, RPMTAG_DEFAULTPREFIX, &dprefix, HEADERGET_MINMEM)) {
215         const char *prefix = rpmtdGetString(&dprefix);
216         char * nprefix = stripTrailingChar(xstrdup(prefix), '/');
217         
218         headerPutString(h, RPMTAG_PREFIXES, nprefix);
219         free(nprefix);
220         rpmtdFreeData(&dprefix);
221     }
222
223     /*
224      * The file list was moved to a more compressed format which not
225      * only saves memory (nice), but gives fingerprinting a nice, fat
226      * speed boost (very nice). Go ahead and convert old headers to
227      * the new style (this is a noop for new headers).
228      */
229      compressFilelist(h);
230
231     /* XXX binary rpms always have RPMTAG_SOURCERPM, source rpms do not */
232     if (headerIsSource(h)) {
233         uint32_t one = 1;
234
235         if (!headerIsEntry(h, RPMTAG_SOURCEPACKAGE))
236             headerPutUint32(h, RPMTAG_SOURCEPACKAGE, &one, 1);
237     } else {
238         /* Retrofit "Provide: name = EVR" for binary packages. */
239         providePackageNVR(h);
240     }
241 }
242
243 int headerConvert(Header h, headerConvOps op)
244 {
245     int rc = 1;
246
247     if (h == NULL)
248         return 0;
249
250     switch (op) {
251     case HEADERCONV_EXPANDFILELIST:
252         expandFilelist(h);
253         break;
254     case HEADERCONV_COMPRESSFILELIST:
255         compressFilelist(h);
256         break;
257     case HEADERCONV_RETROFIT_V3:
258         legacyRetrofit(h);
259         break;
260     default:
261         rc = 0;
262         break;
263     }
264     return rc;
265 };