Avoid using legacy rpmio interfaces
[apt.git] / tools / gensrclist.cc
1 /*
2  * $Id: gensrclist.cc,v 1.8 2003/01/30 17:18:21 niemeyer Exp $
3  */
4 #include <alloca.h>
5 #include <ctype.h>
6 #include <dirent.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <rpm/rpmlib.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15 #include <assert.h>
16
17 #include <map>
18 #include <list>
19 #include <iostream>
20
21 #include <apt-pkg/error.h>
22 #include <apt-pkg/tagfile.h>
23 #include <apt-pkg/rpmhandler.h>
24 #include <apt-pkg/configuration.h>
25 #include <config.h>
26
27 #include "cached_md5.h"
28
29 #if RPM_VERSION >= 0x040100
30 #include <rpm/rpmts.h>
31 #endif
32  
33 using namespace std;
34
35 int tags[] =  {
36        RPMTAG_NAME,
37        RPMTAG_EPOCH,
38        RPMTAG_VERSION,
39        RPMTAG_RELEASE,
40        RPMTAG_GROUP,
41        RPMTAG_ARCH,
42        RPMTAG_PACKAGER,
43        RPMTAG_SOURCERPM,
44        RPMTAG_SIZE,
45        RPMTAG_VENDOR,
46        RPMTAG_OS,
47        
48        RPMTAG_DESCRIPTION, 
49        RPMTAG_SUMMARY, 
50        /*RPMTAG_HEADERI18NTABLE*/ HEADER_I18NTABLE,
51        
52        RPMTAG_REQUIREFLAGS, 
53        RPMTAG_REQUIRENAME,
54        RPMTAG_REQUIREVERSION
55 };
56 int numTags = sizeof(tags) / sizeof(int);
57
58 #if defined(__APPLE__) || defined(__FREEBSD__)
59 int selectDirent(struct dirent *ent)
60 #else
61 int selectDirent(const struct dirent *ent)
62 #endif
63 {
64    int state = 0;
65    const char *p = ent->d_name;
66    
67    while (1) {
68       if (*p == '.') {
69           state = 1;
70       } else if (state == 1 && *p == 'r')
71           state++;
72       else if (state == 2 && *p == 'p')
73           state++;
74       else if (state == 3 && *p == 'm')
75           state++;
76       else if (state == 4 && *p == '\0')
77           return 1;
78       else if (*p == '\0')
79           return 0;
80       else
81           state = 0;
82       p++;
83    }
84 }
85
86 bool readRPMTable(char *file, map<string, list<char*>* > &table)
87 {
88    FILE *indexf;
89    char buf[512];
90    string srpm;
91    
92    indexf = fopen(file, "r");
93    if (!indexf) {
94       cerr << "gensrclist: could not open file " << file << " for reading: "
95           << strerror(errno) << endl;
96       return false;
97    }
98    
99    while (fgets(buf, 512, indexf)) {
100       char *f;
101       
102       buf[strlen(buf)-1] = '\0';
103       f = strchr(buf, ' ');
104       *f = '\0';
105       f++;
106       
107       srpm = string(buf);
108       
109       if (table.find(srpm) != table.end()) {
110          list<char*> *l = table[srpm];
111          l->push_front(strdup(f));
112       } else {
113          list<char*> *l = new list<char*>;
114          l->push_front(strdup(f));
115          table[srpm] = l;
116       }
117    }
118    
119    fclose(indexf);
120    
121    return true;
122 }
123
124
125 void usage()
126 {
127    cerr << "gensrclist " << VERSION << endl;
128    cerr << "usage: gensrclist [<options>] <dir> <suffix> <srpm index>" << endl;
129    cerr << "options:" << endl;
130 //   cerr << " --mapi         ???????????????????" << endl;
131    cerr << " --flat          use a flat directory structure, where RPMS and SRPMS"<<endl;
132    cerr << "                 are in the same directory level"<<endl;
133    cerr << " --meta <suffix> create source package file list with given suffix" << endl;
134    cerr << " --append        append to the source package file list, don't overwrite" << endl;
135    cerr << " --progress      show a progress bar" << endl;
136    cerr << " --cachedir=DIR  use a custom directory for package md5sum cache"<<endl;
137 }
138
139 #if RPM_VERSION >= 0x040000
140 extern "C" {
141 // No prototype from rpm after 4.0.
142 int headerGetRawEntry(Header h, int_32 tag, int_32 * type,
143                       void *p, int_32 *c);
144 }
145 #endif
146
147 int main(int argc, char ** argv) 
148 {
149    char buf[300];
150    char cwd[PATH_MAX];
151    string srpmdir;
152    FD_t outfd, fd;
153    struct dirent **dirEntries;
154    int rc, i;
155    Header h;
156    int_32 size[1];
157    int entry_no, entry_cur;
158    CachedMD5 *md5cache;
159    map<string, list<char*>* > rpmTable; // table that maps srpm -> generated rpm
160    bool mapi = false;
161    bool progressBar = false;
162    bool flatStructure = false;
163    char *arg_dir, *arg_suffix, *arg_srpmindex;
164    const char *srcListSuffix = NULL;
165    bool srcListAppend = false;
166
167    putenv("LC_ALL="); // Is this necessary yet (after i18n was supported)?
168    for (i = 1; i < argc; i++) {
169       if (strcmp(argv[i], "--mapi") == 0) {
170          mapi = true;
171       } else if (strcmp(argv[i], "--flat") == 0) {
172          flatStructure = true;
173       } else if (strcmp(argv[i], "--progress") == 0) {
174          progressBar = true;
175       } else if (strcmp(argv[i], "--append") == 0) {
176          srcListAppend = true;
177       } else if (strcmp(argv[i], "--meta") == 0) {
178          i++;
179          if (i < argc) {
180             srcListSuffix = argv[i];
181          } else {
182             cout << "gensrclist: argument missing for option --meta"<<endl;
183             exit(1);
184          }
185       } else if (strcmp(argv[i], "--cachedir") == 0) {
186          i++;
187          if (i < argc) {
188             _config->Set("Dir::Cache", argv[i]);
189          } else {
190             cout << "genpkglist: argument missing for option --cachedir"<<endl;
191             exit(1);
192          }
193       } else {
194          break;
195       }
196    }
197    if (argc - i == 3) {
198       arg_dir = argv[i++];
199       arg_suffix = argv[i++];
200       arg_srpmindex = argv[i++];
201    }
202    else {
203       usage();
204       exit(1);
205    }
206    
207    if (!readRPMTable(arg_srpmindex, rpmTable))
208        exit(1);
209    
210    md5cache = new CachedMD5(string(arg_dir)+string(arg_suffix), "gensrclist");
211
212    if(getcwd(cwd, PATH_MAX) == 0)
213    {
214       cerr << argv[0] << ":" << strerror(errno) << endl;
215       exit(1);
216    }
217    
218    if (*arg_dir != '/') {
219       strcpy(buf, cwd);
220       strcat(buf, "/");
221       strcat(buf, arg_dir);
222    } else
223        strcpy(buf, arg_dir);
224    
225    strcat(buf, "/SRPMS.");
226    strcat(buf, arg_suffix);
227    
228    srpmdir = "SRPMS." + string(arg_suffix);
229 #ifdef OLD_FLATSCHEME
230    if (flatStructure) {
231       // add the last component of the directory to srpmdir
232       // that will cancel the effect of the .. used in sourcelist.cc
233       // when building the directory from where to fetch srpms in apt
234       char *prefix;
235       prefix = strrchr(arg_dir, '/');
236       if (prefix == NULL)
237          prefix = arg_dir;
238       else
239          prefix++;
240       if (*prefix != 0 && *(prefix+strlen(prefix)-1) == '/')
241          srpmdir = string(prefix) + srpmdir;
242       else
243          srpmdir = string(prefix) + "/" + srpmdir;
244    }
245 #else
246    if (!flatStructure)
247       srpmdir = "../"+srpmdir;
248 #ifndef REMOVE_THIS_SOMEDAY
249    /* This code is here just so that code in rpmsrcrecords.cc in versions
250     * prior to 0.5.15cnc4 is able to detect if that's a "new" style SRPM
251     * directory scheme, or an old style. Someday, when 0.5.15cnc4 will be
252     * history, this code may be safely removed. */
253    else
254       srpmdir = "./"+srpmdir;
255 #endif
256 #endif
257    
258    entry_no = scandir(buf, &dirEntries, selectDirent, alphasort);
259    if (entry_no < 0) { 
260       cerr << "gensrclist: error opening directory " << buf << ":"
261           << strerror(errno) << endl;
262       return 1;
263    }
264
265    if(chdir(buf) != 0)
266    {
267       cerr << argv[0] << ":" << strerror(errno) << endl;
268       exit(1);
269    }
270    
271    if (srcListSuffix != NULL)
272       sprintf(buf, "%s/srclist.%s", cwd, srcListSuffix);
273    else
274       sprintf(buf, "%s/srclist.%s", cwd, arg_suffix);
275    
276    if (srcListAppend == true && FileExists(buf)) {
277       outfd = Fopen(buf, "a");
278    } else {
279       unlink(buf);
280       outfd = Fopen(buf, "w+");
281    }
282    if (!outfd) {
283       cerr << "gensrclist: error creating file" << buf << ":"
284           << strerror(errno);
285       return 1;
286    }
287
288 #if RPM_VERSION >= 0x040100
289    rpmReadConfigFiles(NULL, NULL);
290    rpmts ts = rpmtsCreate();
291    rpmtsSetVSFlags(ts, (rpmVSFlags_e)-1);
292 #else
293    Header sigs;
294 #endif   
295   
296    for (entry_cur = 0; entry_cur < entry_no; entry_cur++) {
297       struct stat sb;
298
299       if (progressBar) {
300          if (entry_cur)
301             printf("\b\b\b\b\b\b\b\b\b\b");
302          printf(" %04i/%04i", entry_cur + 1, entry_no);
303          fflush(stdout);
304       }
305
306       if (stat(dirEntries[entry_cur]->d_name, &sb) < 0) {
307          cerr << "\nWarning: " << strerror(errno) << ": " << 
308                  dirEntries[entry_cur]->d_name << endl;
309          continue;
310       }
311       
312       fd = Fopen(dirEntries[entry_cur]->d_name, "r");
313          
314       if (!fd) {
315          cerr << "\nWarning: " << strerror(errno) << ": " <<
316                  dirEntries[entry_cur]->d_name << endl;
317          continue;
318       }
319
320       size[0] = sb.st_size;
321          
322 #if RPM_VERSION >= 0x040100
323       rc = rpmReadPackageFile(ts, fd, dirEntries[entry_cur]->d_name, &h);
324       if (rc == RPMRC_OK || rc == RPMRC_NOTTRUSTED || rc == RPMRC_NOKEY) {
325 #else
326       rc = rpmReadPackageInfo(fd, &sigs, &h);
327       if (rc == 0) {
328 #endif
329             Header newHeader;
330             int i;
331             bool foundInIndex;
332             
333             newHeader = headerNew();
334             
335             // the std tags
336             for (i = 0; i < numTags; i++) {
337                int type, count;
338                void *data;
339                int res;
340                
341                // Copy raw entry, so that internationalized strings
342                // will get copied correctly.
343                res = headerGetRawEntry(h, tags[i], &type, &data, &count);
344                if (res != 1)
345                   continue;
346                headerAddEntry(newHeader, tags[i], type, data, count);
347             }
348             
349             
350             // our additional tags
351             headerAddEntry(newHeader, CRPMTAG_DIRECTORY, RPM_STRING_TYPE,
352                            srpmdir.c_str(), 1);
353             
354             headerAddEntry(newHeader, CRPMTAG_FILENAME, RPM_STRING_TYPE, 
355                            dirEntries[entry_cur]->d_name, 1);
356             headerAddEntry(newHeader, CRPMTAG_FILESIZE, RPM_INT32_TYPE,
357                            size, 1);
358             
359             {
360                char md5[34];
361                
362                md5cache->MD5ForFile(dirEntries[entry_cur]->d_name, sb.st_mtime, md5);
363                
364                headerAddEntry(newHeader, CRPMTAG_MD5, RPM_STRING_TYPE,
365                               md5, 1);
366             }
367             
368             foundInIndex = false;
369             {
370                int count = 0;
371                char **l = NULL;
372                list<char*> *rpmlist = rpmTable[string(dirEntries[entry_cur]->d_name)];
373                
374                if (rpmlist) {
375                   l = new char *[rpmlist->size()];
376                   
377                   foundInIndex = true;
378                   
379                   for (list<char*>::const_iterator i = rpmlist->begin();
380                        i != rpmlist->end();
381                        i++) {
382                      l[count++] = *i;
383                   }
384                }
385                
386                if (count) {
387                   headerAddEntry(newHeader, CRPMTAG_BINARY,
388                                  RPM_STRING_ARRAY_TYPE, l, count);
389                }
390             }
391             if (foundInIndex || !mapi)
392                 headerWrite(outfd, newHeader, HEADER_MAGIC_YES);
393             
394             headerFree(newHeader);
395             headerFree(h);
396 #if RPM_VERSION < 0x040100
397             rpmFreeSignature(sigs);
398 #endif
399       } else {
400          cerr << "\nWarning: Skipping malformed RPM: " <<
401                  dirEntries[entry_cur]->d_name << endl;
402       }
403       Fclose(fd);
404    } 
405    
406    Fclose(outfd);
407
408 #if RPM_VERSION >= 0x040100
409    ts = rpmtsFree(ts);
410 #endif   
411    
412    delete md5cache;
413    
414    return 0;
415 }
416
417 // vim:sts=3:sw=3