Use type-specific headerPut()'s for legacy retrofitting stuff
[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     if (headerIsSource(h)) {
58         /* HACK. Source RPM, so just do things differently */
59         dirIndex = 0;
60         dirNames[dirIndex] = xstrdup("");
61         while ((i = rpmtdNext(&fileNames)) >= 0) {
62             dirIndexes[i] = dirIndex;
63             baseNames[i] = rpmtdGetString(&fileNames);
64         }
65         goto exit;
66     }
67
68     while ((i = rpmtdNext(&fileNames)) >= 0) {
69         char ** needle;
70         char savechar;
71         char * baseName;
72         size_t len;
73         const char *filename = rpmtdGetString(&fileNames);
74
75         if (filename == NULL)   /* XXX can't happen */
76             continue;
77         baseName = strrchr(filename, '/') + 1;
78         len = baseName - filename;
79         needle = dirNames;
80         savechar = *baseName;
81         *baseName = '\0';
82         if (dirIndex < 0 ||
83             (needle = bsearch(filename, dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) {
84             char *s = xmalloc(len + 1);
85             rstrlcpy(s, filename, len + 1);
86             dirIndexes[i] = ++dirIndex;
87             dirNames[dirIndex] = s;
88         } else
89             dirIndexes[i] = needle - dirNames;
90
91         *baseName = savechar;
92         baseNames[i] = baseName;
93     }
94
95 exit:
96     if (count > 0) {
97         headerPutUint32(h, RPMTAG_DIRINDEXES, dirIndexes, count);
98         headerPutStringArray(h, RPMTAG_BASENAMES, baseNames, count);
99         headerPutStringArray(h, RPMTAG_DIRNAMES, 
100                              (const char **) dirNames, dirIndex + 1);
101     }
102
103     rpmtdFreeData(&fileNames);
104     for (i = 0; i <= dirIndex; i++) {
105         free(dirNames[i]);
106     }
107     free(dirNames);
108     free(baseNames);
109     free(dirIndexes);
110
111     xx = headerDel(h, RPMTAG_OLDFILENAMES);
112 }
113
114 void expandFilelist(Header h)
115 {
116     struct rpmtd_s filenames;
117
118     if (!headerIsEntry(h, RPMTAG_OLDFILENAMES)) {
119         (void) headerGet(h, RPMTAG_FILENAMES, &filenames, HEADERGET_EXT);
120         if (rpmtdCount(&filenames) < 1)
121             return;
122         rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES);
123         headerPut(h, &filenames, HEADERPUT_DEFAULT);
124         rpmtdFreeData(&filenames);
125     }
126
127     (void) headerDel(h, RPMTAG_DIRNAMES);
128     (void) headerDel(h, RPMTAG_BASENAMES);
129     (void) headerDel(h, RPMTAG_DIRINDEXES);
130 }
131
132 /*
133  * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
134  * Retrofit an explicit "Provides: name = epoch:version-release.
135  */
136 static void providePackageNVR(Header h)
137 {
138     const char *name;
139     char *pEVR;
140     rpmsenseFlags pFlags = RPMSENSE_EQUAL;
141     int bingo = 1;
142     struct rpmtd_s pnames;
143     rpmds hds, nvrds;
144
145     /* Generate provides for this package name-version-release. */
146     pEVR = headerGetEVR(h, &name);
147     if (!(name && pEVR))
148         return;
149
150     /*
151      * Rpm prior to 3.0.3 does not have versioned provides.
152      * If no provides at all are available, we can just add.
153      */
154     if (!headerGet(h, RPMTAG_PROVIDENAME, &pnames, HEADERGET_MINMEM)) {
155         goto exit;
156     }
157
158     /*
159      * Otherwise, fill in entries on legacy packages.
160      */
161     if (!headerIsEntry(h, RPMTAG_PROVIDEVERSION)) {
162         while (rpmtdNext(&pnames) >= 0) {
163             rpmsenseFlags fdummy = RPMSENSE_ANY;
164
165             headerPutString(h, RPMTAG_PROVIDEVERSION, "");
166             headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &fdummy, 1);
167         }
168         goto exit;
169     }
170
171     /* see if we already have this provide */
172     hds = rpmdsNew(h, RPMTAG_PROVIDENAME, 1);
173     nvrds = rpmdsSingle(RPMTAG_PROVIDENAME, name, pEVR, pFlags);
174     if (rpmdsFind(hds, nvrds) >= 0) {
175         bingo = 0;
176     }
177     rpmdsFree(hds);
178     rpmdsFree(nvrds);
179     
180
181 exit:
182     if (bingo) {
183         const char *evr = pEVR;
184         headerPutString(h, RPMTAG_PROVIDENAME, name);
185         headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
186         headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pFlags, 1);
187     }
188     rpmtdFreeData(&pnames);
189     free(pEVR);
190 }
191
192 void legacyRetrofit(Header h)
193 {
194     struct rpmtd_s dprefix;
195
196     /*
197      * We don't use these entries (and rpm >= 2 never has) and they are
198      * pretty misleading. Let's just get rid of them so they don't confuse
199      * anyone.
200      */
201     if (headerIsEntry(h, RPMTAG_FILEUSERNAME))
202         (void) headerDel(h, RPMTAG_FILEUIDS);
203     if (headerIsEntry(h, RPMTAG_FILEGROUPNAME))
204         (void) headerDel(h, RPMTAG_FILEGIDS);
205
206     /*
207      * We switched the way we do relocatable packages. We fix some of
208      * it up here, though the install code still has to be a bit 
209      * careful. This fixup makes queries give the new values though,
210      * which is quite handy.
211      */
212     if (headerGet(h, RPMTAG_DEFAULTPREFIX, &dprefix, HEADERGET_MINMEM)) {
213         const char *prefix = rpmtdGetString(&dprefix);
214         char * nprefix = stripTrailingChar(xstrdup(prefix), '/');
215         
216         headerPutString(h, RPMTAG_PREFIXES, nprefix);
217         free(nprefix);
218         rpmtdFreeData(&dprefix);
219     }
220
221     /*
222      * The file list was moved to a more compressed format which not
223      * only saves memory (nice), but gives fingerprinting a nice, fat
224      * speed boost (very nice). Go ahead and convert old headers to
225      * the new style (this is a noop for new headers).
226      */
227      compressFilelist(h);
228
229     /* XXX binary rpms always have RPMTAG_SOURCERPM, source rpms do not */
230     if (headerIsSource(h)) {
231         uint32_t one = 1;
232
233         if (!headerIsEntry(h, RPMTAG_SOURCEPACKAGE))
234             headerPutUint32(h, RPMTAG_SOURCEPACKAGE, &one, 1);
235     } else {
236         /* Retrofit "Provide: name = EVR" for binary packages. */
237         providePackageNVR(h);
238     }
239 }