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