Imported Upstream version 0.7.7
[platform/upstream/libsolv.git] / tools / repo2solv.c
1 /*
2  * Copyright (c) 2018, SUSE LLC.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 #include <sys/types.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <dirent.h>
14 #include <sys/stat.h>
15 #include <sys/wait.h>
16 #include <errno.h>
17
18 #include "pool.h"
19 #include "repo.h"
20
21 #ifdef ENABLE_RPMPKG
22 #include "repo_rpmdb.h"
23 #endif
24
25 #ifdef ENABLE_RPMMD
26 #include "repo_repomdxml.h"
27 #include "repo_rpmmd.h"
28 #include "repo_updateinfoxml.h"
29 #include "repo_deltainfoxml.h"
30 #endif
31
32 #ifdef ENABLE_SUSEREPO
33 #include "repo_content.h"
34 #include "repo_susetags.h"
35 #endif
36
37 #ifdef SUSE
38 #include "repo_autopattern.h"
39 #endif
40 #ifdef ENABLE_APPDATA
41 #include "repo_appdata.h"
42 #endif
43 #include "common_write.h"
44 #include "solv_xfopen.h"
45
46
47 #ifdef SUSE
48 int add_auto = 0;
49 #endif
50 #ifdef ENABLE_APPDATA
51 int add_appdata = 0;
52 #endif
53 int recursive = 0;
54 int add_filelist = 0;
55 int add_changelog = 0;
56 int filtered_filelist = 0;
57
58
59 #define REPO_PLAINDIR           1
60 #define REPO_RPMMD              2
61 #define REPO_RPMMD_REPODATA     3
62 #define REPO_SUSETAGS           4
63
64 int
65 autodetect_repotype(Pool *pool, const char *dir)
66 {
67   struct stat stb;
68   char *tmp;
69   FILE *fp;
70
71   tmp = pool_tmpjoin(pool, dir, "/repomd.xml", 0);
72   if (stat(tmp, &stb) == 0)
73     return REPO_RPMMD;
74   tmp = pool_tmpjoin(pool, dir, "/repodata/repomd.xml", 0);
75   if (stat(tmp, &stb) == 0)
76     return REPO_RPMMD_REPODATA;
77   tmp = pool_tmpjoin(pool, dir, "/content", 0);
78   if ((fp = fopen(tmp, "r")) != 0)
79     {
80       char buf[512], *descrdir = 0;
81       while (fgets(buf, sizeof(buf), fp))
82         {
83           int l = strlen(buf);
84           char *bp = buf;
85           if (buf[l - 1] != '\n')
86             {
87               int c;
88               while ((c = getc(fp)) != EOF && c != '\n')
89                 ;
90               continue;
91             }
92           while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
93             l--;
94           buf[l] = 0;
95           while (*bp == ' ' || *bp == '\t')
96             bp++;
97           if (strncmp(bp, "DESCRDIR", 8) != 0 || (bp[8] != ' ' && bp[8] != '\t'))
98             continue;
99           bp += 9;
100           while (*bp == ' ' || *bp == '\t')
101             bp++;
102           descrdir = bp;
103           break;
104         }
105       fclose(fp);
106       if (descrdir)
107         {
108           tmp = pool_tmpjoin(pool, dir, "/", descrdir);
109           if (stat(tmp, &stb) == 0 && S_ISDIR(stb.st_mode))
110             return REPO_SUSETAGS;
111         }
112     }
113   tmp = pool_tmpjoin(pool, dir, "/suse/setup/descr", 0);
114   if (stat(tmp, &stb) == 0 && S_ISDIR(stb.st_mode))
115     return REPO_SUSETAGS;
116   return REPO_PLAINDIR;
117 }
118
119
120 #ifdef ENABLE_RPMPKG
121
122 int
123 read_plaindir_repo(Repo *repo, const char *dir)
124 {
125   Pool *pool = repo->pool;
126   Repodata *data;
127   int c;
128   FILE *fp;
129   int wstatus;
130   int fds[2];
131   pid_t pid;
132   char *buf = 0;
133   char *buf_end = 0;
134   char *bp = 0;
135   char *rpm;
136   int res = 0;
137   Id p;
138
139   /* run find command */
140   if (pipe(fds))
141     {
142       perror("pipe");
143       exit(1);
144     }
145   while ((pid = fork()) == (pid_t)-1)
146     {
147       if (errno != EAGAIN)
148         {
149           perror("fork");
150           exit(1);
151         }
152       sleep(3);
153     }
154   if (pid == 0)
155     {
156       if (chdir(dir))
157         {
158           perror(dir);
159           _exit(1);
160         }
161       close(fds[0]);
162       if (fds[1] != 1)
163         {
164           if (dup2(fds[1], 1) == -1)
165             {
166               perror("dup2");
167               _exit(1);
168             }
169           close(fds[1]);
170         }
171       if (recursive)
172         execl("/usr/bin/find", "/usr/bin/find", ".", "-name", ".", "-o", "-name", ".*", "-prune", "-o", "-name", "*.delta.rpm", "-o", "-name", "*.patch.rpm", "-o", "-name", "*.rpm", "-a", "-type", "f", "-print0", (char *)0);
173       else
174         execl("/usr/bin/find", "/usr/bin/find", ".", "-maxdepth", "1", "-name", ".", "-o", "-name", ".*", "-prune", "-o", "-name", "*.delta.rpm", "-o", "-name", "*.patch.rpm", "-o", "-name", "*.rpm", "-a", "-type", "f", "-print0", (char *)0);
175       perror("/usr/bin/find");
176       _exit(1);
177     }
178   close(fds[1]);
179   if ((fp = fdopen(fds[0], "r")) == 0)
180     {
181       perror("fdopen");
182       exit(1);
183     }
184   data = repo_add_repodata(repo, 0);
185   bp = buf;
186   while ((c = getc(fp)) != EOF)
187     {
188       if (bp == buf_end)
189         {
190           size_t len = bp - buf;
191           buf = solv_realloc(buf, len + 4096);
192           bp = buf + len;
193           buf_end = bp + 4096;
194         }
195       *bp++ = c;
196       if (c)
197         continue;
198       bp = buf;
199       rpm = solv_dupjoin(dir, "/", bp[0] == '.' && bp[1] == '/' ? bp + 2 : bp);
200       if ((p = repo_add_rpm(repo, rpm, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|(filtered_filelist ? RPM_ADD_FILTERED_FILELIST : 0))) == 0)
201         {
202           fprintf(stderr, "%s: %s\n", rpm, pool_errstr(pool));
203 #if 0
204           res = 1;
205 #endif
206         }
207       else
208         repodata_set_location(data, p, 0, 0, bp[0] == '.' && bp[1] == '/' ? bp + 2 : bp);
209       solv_free(rpm);
210     }
211   solv_free(buf);
212   fclose(fp);
213   while (waitpid(pid, &wstatus, 0) == -1)
214     {
215       if (errno == EINTR)
216         continue;
217       perror("waitpid");
218       exit(1);
219     }
220   if (wstatus)
221     {
222       fprintf(stderr, "find: exit status %d\n", (wstatus >> 8) | (wstatus & 255) << 8);
223 #if 0
224       res = 1;
225 #endif
226     }
227   repo_internalize(repo);
228   return res;
229 }
230
231 #else
232
233 int
234 read_plaindir_repo(Repo *repo, const char *dir)
235 {
236   fprintf(stderr, "plaindir repo type is not supported\n");
237   exit(1);
238 }
239
240 #endif
241
242 #ifdef ENABLE_SUSEREPO
243
244 static const char *
245 susetags_find(char **files, int nfiles, const char *what)
246 {
247   int i;
248   size_t l = strlen(what);
249   for (i = 0; i < nfiles; i++)
250     {
251       char *fn = files[i];
252       if (strncmp(fn, what, l) != 0)
253         continue;
254       if (fn[l] == 0)
255         return fn;
256       if (fn[l] != '.')
257         continue;
258       if (strchr(fn + l + 1, '.') != 0)
259         continue;
260       if (solv_xfopen_iscompressed(fn) <= 0)
261         continue;
262       return fn;
263     }
264   return 0;
265 }
266
267 static FILE *
268 susetags_open(const char *dir, const char *filename, char **tmpp, int missingok)
269 {
270   FILE *fp;
271   if (!filename)
272     {
273       *tmpp = 0;
274       return 0;
275     }
276   *tmpp = solv_dupjoin(dir, "/", filename);
277   if ((fp = solv_xfopen(*tmpp, "r")) == 0)
278     {
279       if (!missingok)
280         {
281           perror(*tmpp);
282           exit(1);
283         }
284       *tmpp = solv_free(*tmpp);
285       return 0;
286     }
287   return fp;
288 }
289
290 static void
291 susetags_extend(Repo *repo, const char *dir, char **files, int nfiles, char *what, Id defvendor, char *language, int missingok)
292 {
293   const char *filename;
294   FILE *fp;
295   char *tmp;
296
297   filename = susetags_find(files, nfiles, what);
298   if (!filename)
299     return;
300   if ((fp = susetags_open(dir, filename, &tmp, missingok)) != 0)
301     {
302       if (repo_add_susetags(repo, fp, defvendor, language, REPO_EXTEND_SOLVABLES))
303         {
304           fprintf(stderr, "%s: %s\n", tmp, pool_errstr(repo->pool));
305           exit(1);
306         }
307       fclose(fp);
308       solv_free(tmp);
309     }
310 }
311
312 static void
313 susetags_extend_languages(Repo *repo, const char *dir, char **files, int nfiles, Id defvendor, int missingok)
314 {
315   int i;
316   for (i = 0; i < nfiles; i++)
317     {
318       char *fn = files[i];
319       char lang[64], *p;
320       if (strncmp(fn, "packages.", 9) != 0)
321         continue;
322       if (strlen(fn + 9) + 1 >= sizeof(lang))
323         continue;
324       strncpy(lang, fn + 9, sizeof(lang) - 1);
325       lang[sizeof(lang) - 1] = 0;
326       p = strrchr(lang, '.');
327       if (p)
328         {
329           if (solv_xfopen_iscompressed(lang) <= 0)
330             continue;
331           *p = 0;
332         }
333       if (strchr(lang, '.'))
334         continue;
335       if (!strcmp(lang, "en"))
336         continue;       /* already did that one */
337       if (!strcmp(lang, "DU"))
338         continue;       /* disk usage */
339       if (!strcmp(lang, "FL"))
340         continue;       /* file list */
341       if (!strcmp(lang, "DL"))
342         continue;       /* deltas */
343       susetags_extend(repo, dir, files, nfiles, fn, defvendor, lang, missingok);
344     }
345 }
346
347 static int
348 susetags_dircmp(const void *ap, const void *bp, void *dp)
349 {
350   return strcmp(*(const char **)ap, *(const char **)bp);
351 }
352
353 int
354 read_susetags_repo(Repo *repo, const char *dir)
355 {
356   Pool *pool = repo->pool;
357   const char *filename;
358   char *ddir;
359   char *tmp;
360   FILE *fp;
361   Id defvendor = 0;
362   const char *descrdir = 0;
363   char **files = 0;
364   int nfiles = 0;
365   DIR *dp;
366   struct dirent *de;
367
368   /* read content file */
369   repo_add_repodata(repo, 0);
370   tmp = solv_dupjoin(dir, "/content", 0);
371   if ((fp = fopen(tmp, "r")) != 0)
372     {
373       if (repo_add_content(repo, fp, REPO_REUSE_REPODATA))
374         {
375           fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
376           exit(1);
377         }
378       fclose(fp);
379       descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
380       defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
381     }
382   if (!descrdir)
383     descrdir = "suse/setup/descr";
384   tmp = solv_free(tmp);
385
386   /* get content of descrdir directory */
387   ddir = solv_dupjoin(dir, "/", descrdir);
388   if ((dp = opendir(ddir)) == 0)
389     {
390       perror(ddir);
391       exit(1);
392     }
393   while ((de = readdir(dp)) != 0)
394     {
395       if (de->d_name[0] == 0 || de->d_name[0] == '.')
396         continue;
397       files = solv_extend(files, nfiles, 1, sizeof(char *), 63);
398       files[nfiles++] = solv_strdup(de->d_name);
399     }
400   closedir(dp);
401   if (nfiles > 1)
402     solv_sort(files, nfiles, sizeof(char *), susetags_dircmp, 0);
403   
404   /* add packages */
405   filename = susetags_find(files, nfiles, "packages");
406   if (filename && (fp = susetags_open(ddir, filename, &tmp, 1)) != 0)
407     {
408       if (repo_add_susetags(repo, fp, defvendor, 0, SUSETAGS_RECORD_SHARES))
409         {
410           fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
411           exit(1);
412         }
413       fclose(fp);
414       tmp = solv_free(tmp);
415
416       /* now extend the packages */
417       susetags_extend(repo, ddir, files, nfiles, "packages.DU", defvendor, 0, 1);
418       susetags_extend(repo, ddir, files, nfiles, "packages.en", defvendor, 0, 1);
419       susetags_extend_languages(repo, ddir, files, nfiles, defvendor, 1);
420       if (add_filelist)
421         susetags_extend(repo, ddir, files, nfiles, "packages.FL", defvendor, 0, 1);
422     }
423
424   /* add deltas */
425   filename = susetags_find(files, nfiles, "packages.DL");
426   if (filename && (fp = susetags_open(ddir, filename, &tmp, 1)) != 0)
427     {
428       if (repo_add_susetags(repo, fp, defvendor, 0, 0))
429         {
430           fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
431           exit(1);
432         }
433       fclose(fp);
434       tmp = solv_free(tmp);
435     }
436
437   /* add legacy patterns */
438   tmp = solv_dupjoin(ddir, "/patterns", 0);
439   if ((fp = fopen(tmp, "r")) != 0)
440     {
441       char pbuf[4096];
442
443       repo_add_repodata(repo, 0);
444       while (fgets(pbuf, sizeof(pbuf), fp))
445         {
446           char *p;
447           FILE *pfp;
448           if (strchr(pbuf, '/') != 0)
449             continue;
450           if ((p = strchr(pbuf, '\n')) != 0)
451             *p = 0;
452           if (*pbuf == 0)
453             continue;
454           solv_free(tmp);
455           tmp = solv_dupjoin(ddir, "/", pbuf);
456           if ((pfp = solv_xfopen(tmp, "r")) != 0)
457             {
458               if (repo_add_susetags(repo, pfp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA))
459                 {
460                   fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
461                   exit(1);
462                 }
463               fclose(pfp);
464             }
465         }
466       fclose(fp);
467     }
468   tmp = solv_free(tmp);
469  
470 #ifdef ENABLE_APPDATA
471   /* appdata */
472   filename = add_appdata ? susetags_find(files, nfiles, "appdata.xml") : 0;
473   if (filename && (fp = susetags_open(ddir, filename, &tmp, 1)) != 0)
474     {
475       if (repo_add_appdata(repo, fp, 0))
476         {
477           fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
478           exit(1);
479         }
480       fclose(fp);
481       tmp = solv_free(tmp);
482     }
483 #endif
484
485   while (nfiles > 0)
486     solv_free(files[--nfiles]);
487   solv_free(files);
488   solv_free(ddir);
489   repo_internalize(repo);
490   return 0;
491 }
492
493 #else
494
495 int
496 read_susetags_repo(Repo *repo, const char *dir)
497 {
498   fprintf(stderr, "susetags repo type is not supported\n");
499   exit(1);
500 }
501
502 #endif
503
504
505 #ifdef ENABLE_RPMMD
506
507 # ifdef ENABLE_ZCHUNK_COMPRESSION
508
509 static int
510 repomd_exists(const char *dir, const char *filename)
511 {
512   char *path;
513   struct stat stb;
514   int r;
515   
516   if (!filename)
517     return 0;
518   path = solv_dupjoin(dir, "/", filename);
519   r = stat(path, &stb) == 0;
520   solv_free(path);
521   return r;
522 }
523
524 # endif
525
526 static const char *
527 repomd_find(Repo *repo, const char *dir, const char *what, int findzchunk)
528 {
529   Pool *pool = repo->pool;
530   Dataiterator di;
531   const char *filename;
532
533 # ifdef ENABLE_ZCHUNK_COMPRESSION
534   if (findzchunk)
535     {
536       char *what_zck = solv_dupjoin(what, "_zck", 0);
537       filename = repomd_find(repo, dir, what_zck, 0);
538       solv_free(what_zck);
539       if (filename && repomd_exists(dir, filename))
540         return filename;
541     }
542 # endif
543   filename = 0;
544   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
545   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
546   if (dataiterator_step(&di))
547     {
548       dataiterator_setpos_parent(&di);
549       filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
550     }
551   dataiterator_free(&di);
552   if (filename && strncmp(filename, "repodata/", 9) == 0)
553     filename += 9;
554   return filename;
555 }
556
557 static FILE *
558 repomd_open(const char *dir, const char *filename, char **tmpp, int missingok)
559 {
560   FILE *fp;
561   if (!filename)
562     {
563       *tmpp = 0;
564       return 0;
565     }
566   *tmpp = solv_dupjoin(dir, "/", filename);
567   if ((fp = solv_xfopen(*tmpp, "r")) == 0)
568     {
569       if (!missingok)
570         {
571           perror(*tmpp);
572           exit(1);
573         }
574       *tmpp = solv_free(*tmpp);
575       return 0;
576     }
577   return fp;
578 }
579
580 static void
581 repomd_extend(Repo *repo, const char *dir, const char *what, const char *language, int missingok)
582 {
583   const char *filename;
584   FILE *fp;
585   char *tmp;
586
587   filename = repomd_find(repo, dir, what, 1);
588   if (!filename)
589     return;
590   fp = repomd_open(dir, filename, &tmp, missingok);
591   if (fp)
592     {
593       if (repo_add_rpmmd(repo, fp, language, REPO_EXTEND_SOLVABLES))
594         {
595           fprintf(stderr, "%s: %s\n", tmp, pool_errstr(repo->pool));
596           exit(1);
597         }
598       fclose(fp);
599     }
600   solv_free(tmp);
601 }
602
603 static void
604 repomd_extend_languages(Repo *repo, const char *dir, int missingok)
605 {
606   char **susedatas = 0;
607   int nsusedatas = 0, i;
608   Dataiterator di;
609   dataiterator_init(&di, repo->pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, "susedata.", SEARCH_STRINGSTART);
610   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
611   while (dataiterator_step(&di))
612     {
613       char *str = solv_strdup(di.kv.str);
614       size_t l = strlen(str);
615       if (l > 4 && !strcmp(str + l - 4, "_zck"))
616         str[l - 4] = 0;
617       for (i = 0; i < nsusedatas; i++)
618         if (!strcmp(susedatas[i], str))
619           break;
620       if (i < nsusedatas)
621         {
622           solv_free(str);
623           continue;     /* already have that entry */
624         }
625       susedatas = solv_extend(susedatas, nsusedatas, 1, sizeof(char *), 15);
626       susedatas[nsusedatas++] = str;
627     }
628   dataiterator_free(&di);
629   for (i = 0; i < nsusedatas; i++)
630     {
631       repomd_extend(repo, dir, susedatas[i], susedatas[i] + 9, missingok);
632       susedatas[i] = solv_free(susedatas[i]);
633     }
634   solv_free(susedatas);
635 }
636
637 static void
638 add_rpmmd_file(Repo *repo, const char *dir, const char *filename, int missingok)
639 {
640   FILE *fp;
641   char *tmp;
642
643   fp = repomd_open(dir, filename, &tmp, missingok);
644   if (!fp)
645     return;
646   if (repo_add_rpmmd(repo, fp, 0, 0))
647     {
648       fprintf(stderr, "%s: %s\n", tmp, pool_errstr(repo->pool));
649       exit(1);
650     }
651   fclose(fp);
652   solv_free(tmp);
653 }
654
655 int
656 read_rpmmd_repo(Repo *repo, const char *dir)
657 {
658   Pool *pool = repo->pool;
659   FILE *fp;
660   char *tmp = 0;
661   const char *filename;
662
663   /* add repomd.xml and suseinfo.xml */
664   fp = repomd_open(dir, "repomd.xml", &tmp, 0);
665   if (repo_add_repomdxml(repo, fp, 0))
666     {
667       fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
668       exit(1);
669     }
670   fclose(fp);
671   tmp = solv_free(tmp);
672   filename = repomd_find(repo, dir, "suseinfo", 0);
673   if (filename && (fp = repomd_open(dir, filename, &tmp, 0)) != 0)
674     {
675       if (repo_add_repomdxml(repo, fp, REPO_REUSE_REPODATA))
676         {
677           fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
678           exit(1);
679         }
680       fclose(fp);
681       tmp = solv_free(tmp);
682     }
683   
684   /* first all primary packages */
685   filename = repomd_find(repo, dir, "primary", 1);
686   if (filename)
687     {
688       add_rpmmd_file(repo, dir, filename, 0);
689       repomd_extend(repo, dir, "susedata", 0, 1);
690       repomd_extend_languages(repo, dir, 1);
691       if (add_filelist)
692         repomd_extend(repo, dir, "filelists", 0, 1);
693       if (add_changelog)
694         repomd_extend(repo, dir, "other", 0, 1);
695     }
696
697   /* some legacy stuff */
698   filename = repomd_find(repo, dir, "products", 0);
699   if (!filename)
700     filename = repomd_find(repo, dir, "product", 0);
701   if (filename)
702     add_rpmmd_file(repo, dir, filename, 1);
703   filename = repomd_find(repo, dir, "patterns", 0);
704     add_rpmmd_file(repo, dir, filename, 1);
705   
706   /* updateinfo */
707   filename = repomd_find(repo, dir, "updateinfo", 1);
708   if (filename && (fp = repomd_open(dir, filename, &tmp, 0)) != 0)
709     {
710       if (repo_add_updateinfoxml(repo, fp, 0))
711         {
712           fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
713           exit(1);
714         }
715       fclose(fp);
716       tmp = solv_free(tmp);
717     }
718
719   /* deltainfo */
720   filename = repomd_find(repo, dir, "deltainfo", 1);
721   if (!filename)
722     filename = repomd_find(repo, dir, "prestodelta", 1);
723   if (filename && (fp = repomd_open(dir, filename, &tmp, 1)) != 0)
724     {
725       if (repo_add_deltainfoxml(repo, fp, 0))
726         {
727           fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
728           exit(1);
729         }
730       fclose(fp);
731       tmp = solv_free(tmp);
732     }
733
734 #ifdef ENABLE_APPDATA
735   /* appdata */
736   filename = add_appdata ? repomd_find(repo, dir, "appdata", 1) : 0;
737   if (filename && (fp = repomd_open(dir, filename, &tmp, 1)) != 0)
738     {
739       if (repo_add_appdata(repo, fp, 0))
740         {
741           fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
742           exit(1);
743         }
744       fclose(fp);
745       tmp = solv_free(tmp);
746     }
747 #endif
748
749   repo_internalize(repo);
750   return 0;
751 }
752
753 #else
754
755 int
756 read_rpmmd_repo(Repo *repo, const char *dir)
757 {
758   fprintf(stderr, "rpmmd repo type is not supported\n");
759   exit(1);
760 }
761
762 #endif
763
764 static void
765 usage(int status)
766 {
767   fprintf(stderr, "\nUsage:\n"
768           "repo2solv [-R] [-X] [-A] [-o <out.solv>] <dir>\n"
769           "  Convert a repository in <dir> to a solv file\n"
770           "  -h : print help & exit\n"
771           "  -o <out.solv>: write to this file instead of stdout\n"
772           "  -F : add filelist\n"
773           "  -R : also search subdirectories for rpms\n"
774           "  -X : generate pattern/product pseudo packages\n"
775           "  -A : add appdata packages\n"
776          );
777    exit(status);
778 }
779
780 int
781 main(int argc, char **argv)
782 {
783   int c, res;
784   int repotype = 0;
785   char *outfile = 0;
786   char *dir;
787   struct stat stb;
788   
789   Pool *pool = pool_create();
790   Repo *repo = repo_create(pool, "<repo>");
791
792   while ((c = getopt(argc, argv, "hAXRFCo:")) >= 0)
793     {
794       switch(c)
795         {
796         case 'h':
797           usage(0);
798           break;
799         case 'X':
800 #ifdef SUSE
801           add_auto = 1;
802 #endif
803           break;
804         case 'A':
805 #ifdef ENABLE_APPDATA
806           add_appdata = 1;
807 #endif
808           break;
809         case 'R':
810           repotype = REPO_PLAINDIR;
811           recursive = 1;
812           break;
813         case 'F':
814           add_filelist = 1;
815           break;
816         case 'C':
817           add_changelog = 1;
818           break;
819         case 'o':
820           outfile = optarg;
821           break;
822         default:
823           usage(1);
824           break;
825         }
826     }
827   if (optind + 1 != argc)
828     usage(1);
829   dir = argv[optind];
830   if (stat(dir, &stb))
831     {
832       perror(dir);
833       exit(1);
834     }
835   if (!S_ISDIR(stb.st_mode))
836     {
837       fprintf(stderr, "%s: not a directory\n", dir);
838       exit(1);
839     }
840   dir = solv_strdup(dir);
841   if (repotype == 0)
842     repotype = autodetect_repotype(pool, dir);
843
844   switch (repotype)
845     {
846     case REPO_RPMMD:
847       res = read_rpmmd_repo(repo, dir);
848       break;
849     case REPO_RPMMD_REPODATA:
850       dir = solv_dupappend(dir, "/repodata", 0);
851       res = read_rpmmd_repo(repo, dir);
852       break;
853     case REPO_SUSETAGS:
854       res = read_susetags_repo(repo, dir);
855       break;
856     case REPO_PLAINDIR:
857       res = read_plaindir_repo(repo, dir);
858       break;
859     default:
860       fprintf(stderr, "unknown repotype %d\n", repotype);
861       exit(1);
862     }
863   if (outfile && freopen(outfile, "w", stdout) == 0)
864     {
865       perror(outfile);
866       exit(1);
867     }
868 #ifdef SUSE
869   if (add_auto)
870     repo_add_autopattern(repo, 0);
871 #endif
872   tool_write(repo, stdout);
873   pool_free(pool);
874   solv_free(dir);
875   exit(res);
876 }