a0c45ce4785b962fd7e4b21acc42bdbfce279f3f
[platform/upstream/libsolv.git] / ext / repo_arch.c
1 /*
2  * Copyright (c) 2012, Novell Inc.
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 <sys/stat.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <dirent.h>
17
18 #include "pool.h"
19 #include "repo.h"
20 #include "util.h"
21 #include "chksum.h"
22 #include "solv_xfopen.h"
23 #include "repo_arch.h"
24
25 static long long parsenum(unsigned char *p, int cnt)
26 {
27   long long x = 0;
28   if (!cnt)
29     return -1;
30   if (*p & 0x80)
31     {
32       /* binary format */
33       x = *p & 0x40 ? (-1 << 8 | *p)  : (*p ^ 0x80);
34       while (--cnt > 0)
35         x = (x << 8) | *p++;
36       return x;
37     }
38   while (cnt > 0 && (*p == ' ' || *p == '\t'))
39     cnt--, p++;
40   if (*p == '-')
41     return -1;
42   for (; cnt > 0 && *p >= '0' && *p < '8'; cnt--, p++)
43     x = (x << 3) | (*p - '0');
44   return x;
45 }
46
47 static int readblock(FILE *fp, unsigned char *blk)
48 {
49   int r, l = 0;
50   while (l < 512)
51     {
52       r = fread(blk + l, 1, 512 - l, fp);
53       if (r <= 0)
54         return -1;
55       l += r;
56     }
57   return 0;
58 }
59
60 struct tarhead {
61   FILE *fp;
62   unsigned char blk[512];
63   int type;
64   long long length;
65   char *path;
66   int eof;
67   int ispax;
68   int off;
69   int end;
70 };
71
72 static char *getsentry(struct tarhead *th, char *s, int size)
73 {
74   char *os = s;
75   if (th->eof || size <= 1)
76     return 0;
77   size--;       /* terminating 0 */
78   for (;;)
79     {
80       int i;
81       for (i = th->off; i < th->end; i++)
82         {
83           *s++ = th->blk[i];
84           size--;
85           if (!size || th->blk[i] == '\n')
86             {
87               th->off = i + 1;
88               *s = 0;
89               return os;
90             }
91         }
92       th->off = i;
93       if (!th->path)
94         {
95           /* fake entry */
96           th->end = fread(th->blk, 1, 512, th->fp);
97           if (th->end <= 0)
98             {
99               th->eof = 1;
100               return 0;
101             }
102           th->off = 0;
103           continue;
104         }
105       if (th->length <= 0)
106         return 0;
107       if (readblock(th->fp, th->blk))
108         {
109           th->eof = 1;
110           return 0;
111         }
112       th->off = 0;
113       th->end = th->length > 512 ? 512 : th->length;
114       th->length -= th->end;
115     }
116 }
117
118 static void skipentry(struct tarhead *th)
119 {
120   for (; th->length > 0; th->length -= 512)
121     {
122       if (readblock(th->fp, th->blk))
123         {
124           th->eof = 1;
125           th->length = 0;
126           return;
127         }
128     }
129   th->length = 0;
130   th->off = th->end = 0;
131 }
132
133 static void inittarhead(struct tarhead *th, FILE *fp)
134 {
135   memset(th, 0, sizeof(*th));
136   th->fp = fp;
137 }
138
139 static void freetarhead(struct tarhead *th)
140 {
141   solv_free(th->path);
142 }
143
144 static int gettarhead(struct tarhead *th)
145 {
146   int l, type;
147   long long length;
148
149   th->path = solv_free(th->path);
150   th->ispax = 0;
151   th->type = 0;
152   th->length = 0;
153   th->off = 0;
154   th->end = 0;
155   if (th->eof)
156     return 0;
157   for (;;)
158     {
159       int r = readblock(th->fp, th->blk);
160       if (r)
161         {
162           if (feof(th->fp))
163             {
164               th->eof = 1;
165               return 0;
166             }
167           return -1;
168         }
169       if (th->blk[0] == 0)
170         {
171           th->eof = 1;
172           return 0;
173         }
174       length = parsenum(th->blk + 124, 12);
175       if (length < 0)
176         return -1;
177       type = 0;
178       switch (th->blk[156])
179         {
180         case 'S': case '0':
181           type = 1;     /* file */
182           break;
183         case '1':
184           /* hard link, special length magic... */
185           if (!th->ispax)
186             length = 0;
187           break;
188         case '5':
189           type = 2;     /* dir */
190           break;
191         case '2': case '3': case '4': case '6':
192           length = 0;
193           break;
194         case 'X': case 'x': case 'L':
195           {
196             char *data, *pp;
197             if (length < 1 || length >= 1024 * 1024)
198               return -1;
199             data = pp = solv_malloc(length + 512);
200             for (l = length; l > 0; l -= 512, pp += 512)
201               if (readblock(th->fp, (unsigned char *)pp))
202                 {
203                   solv_free(data);
204                   return -1;
205                 }
206             data[length] = 0;
207             type = 3;           /* extension */
208             if (th->blk[156] == 'L')
209               {
210                 solv_free(th->path);
211                 th->path = data;
212                 length = 0;
213                 break;
214               }
215             pp = data;
216             while (length > 0)
217               {
218                 int ll = 0;
219                 for (l = 0; l < length && pp[l] >= '0' && pp[l] <= '9'; l++)
220                   ll = ll * 10 + (pp[l] - '0');
221                 if (l == length || pp[l] != ' ' || ll < 1 || ll > length || pp[ll - 1] != '\n')
222                   {
223                     solv_free(data);
224                     return -1;
225                   }
226                 length -= ll;
227                 pp += l + 1;
228                 ll -= l + 1;
229                 pp[ll - 1] = 0;
230                 if (!strncmp(pp, "path=", 5))
231                   {
232                     solv_free(th->path);
233                     th->path = solv_strdup(pp + 5);
234                   }
235                 pp += ll;
236               }
237             solv_free(data);
238             th->ispax = 1;
239             length = 0;
240             break;
241           }
242         default:
243           type = 3;     /* extension */
244           break;
245         }
246       if ((type == 1 || type == 2) && !th->path)
247         {
248           char path[157];
249           memcpy(path, th->blk, 156);
250           path[156] = 0;
251           if (!memcmp(th->blk + 257, "ustar\0\060\060", 8) && !th->path && th->blk[345])
252             {
253               /* POSIX ustar with prefix */
254               char prefix[156];
255               memcpy(prefix, th->blk + 345, 155);
256               prefix[155] = 0;
257               l = strlen(prefix);
258               if (l && prefix[l - 1] == '/')
259                 prefix[l - 1] = 0;
260               th->path = solv_dupjoin(prefix, "/", path);
261             }
262           else
263             th->path = solv_dupjoin(path, 0, 0);
264         }
265       if (type == 1 || type == 2)
266         {
267           l = strlen(th->path);
268           if (l && th->path[l - 1] == '/')
269             {
270               if (l > 1)
271                 th->path[l - 1] = 0;
272               type = 2;
273             }
274         }
275       if (type != 3)
276         break;
277       while (length > 0)
278         {
279           r = readblock(th->fp, th->blk);
280           if (r)
281             return r;
282           length -= 512;
283         }
284     }
285   th->type = type;
286   th->length = length;
287   return 1;
288 }
289
290 static Offset
291 adddep(Repo *repo, Offset olddeps, char *line)
292 {
293   Pool *pool = repo->pool;
294   char *p;
295   Id id;
296
297   while (*line == ' ' || *line == '\t')
298     line++;
299   p = line;
300   while (*p && *p != ' ' && *p != '\t' && *p != '<' && *p != '=' && *p != '>')
301     p++;
302   id = pool_strn2id(pool, line, p - line, 1);
303   while (*p == ' ' || *p == '\t')
304     p++;
305   if (*p == '<' || *p == '=' || *p == '>')
306     {
307       int flags = 0;
308       for (;; p++)
309         {
310           if (*p == '<')
311             flags |= REL_LT;
312           else if (*p == '=')
313             flags |= REL_EQ;
314           else if (*p == '>')
315             flags |= REL_GT;
316           else
317             break;
318         }
319       while (*p == ' ' || *p == '\t')
320         p++;
321       line = p;
322       while (*p && *p != ' ' && *p != '\t')
323         p++;
324       id = pool_rel2id(pool, id, pool_strn2id(pool, line, p - line, 1), flags, 1);
325     }
326   return repo_addid_dep(repo, olddeps, id, 0);
327 }
328
329 Id
330 repo_add_arch_pkg(Repo *repo, const char *fn, int flags)
331 {
332   Pool *pool = repo->pool;
333   Repodata *data;
334   FILE *fp;
335   struct tarhead th;
336   char line[4096];
337   int ignoreline;
338   Solvable *s;
339   int l, fd;
340   struct stat stb;
341   Chksum *pkgidchk = 0;
342
343   data = repo_add_repodata(repo, flags);
344   if ((fd = open(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, fn) : fn, O_RDONLY, 0)) < 0)
345     {
346       pool_error(pool, -1, "%s: %s", fn, strerror(errno));
347       return 0;
348     }
349   if (fstat(fd, &stb))
350     {
351       pool_error(pool, -1, "%s: fstat: %s", fn, strerror(errno));
352       close(fd);
353       return 0;
354     }
355   if (!(fp = solv_xfopen_fd(fn, fd, "r")))
356     {
357       pool_error(pool, -1, "%s: fdopen failed", fn);
358       close(fd);
359       return 0;
360     }
361   s = 0;
362   inittarhead(&th, fp);
363   while (gettarhead(&th) > 0)
364     {
365       if (th.type != 1 || strcmp(th.path, ".PKGINFO") != 0)
366         {
367           skipentry(&th);
368           continue;
369         }
370       ignoreline = 0;
371       s = pool_id2solvable(pool, repo_add_solvable(repo));
372       if (flags & ARCH_ADD_WITH_PKGID)
373         pkgidchk = solv_chksum_create(REPOKEY_TYPE_MD5);
374       while (getsentry(&th, line, sizeof(line)))
375         {
376           l = strlen(line);
377           if (l == 0)
378             continue;
379           if (pkgidchk)
380             solv_chksum_add(pkgidchk, line, l);
381           if (line[l - 1] != '\n')
382             {
383               ignoreline = 1;
384               continue;
385             }
386           if (ignoreline)
387             {
388               ignoreline = 0;
389               continue;
390             }
391           line[--l] = 0;
392           if (l == 0 || line[0] == '#')
393             continue;
394           if (!strncmp(line, "pkgname = ", 10))
395             s->name = pool_str2id(pool, line + 10, 1);
396           else if (!strncmp(line, "pkgver = ", 9))
397             s->evr = pool_str2id(pool, line + 9, 1);
398           else if (!strncmp(line, "pkgdesc = ", 10))
399             {
400               repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line + 10);
401               repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, line + 10);
402             }
403           else if (!strncmp(line, "url = ", 6))
404             repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, line + 6);
405           else if (!strncmp(line, "builddate = ", 12))
406             repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, strtoull(line + 12, 0, 10));
407           else if (!strncmp(line, "packager = ", 11))
408             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_PACKAGER, line + 11);
409           else if (!strncmp(line, "size = ", 7))
410             repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(line + 7, 0, 10));
411           else if (!strncmp(line, "arch = ", 7))
412             s->arch = pool_str2id(pool, line + 7, 1);
413           else if (!strncmp(line, "license = ", 10))
414             repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_LICENSE, line + 10);
415           else if (!strncmp(line, "replaces = ", 11))
416             s->obsoletes = adddep(repo, s->obsoletes, line + 11);
417           else if (!strncmp(line, "group = ", 8))
418             repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_GROUP, line + 8);
419           else if (!strncmp(line, "depend = ", 9))
420             s->requires = adddep(repo, s->requires, line + 9);
421           else if (!strncmp(line, "optdepend = ", 12))
422             {
423               char *p = strchr(line, ':');
424               if (p)
425                 *p = 0;
426               s->suggests = adddep(repo, s->suggests, line + 12);
427             }
428           else if (!strncmp(line, "conflict = ", 11))
429             s->conflicts = adddep(repo, s->conflicts, line + 11);
430           else if (!strncmp(line, "provides = ", 11))
431             s->provides = adddep(repo, s->provides, line + 11);
432         }
433       break;
434     }
435   freetarhead(&th);
436   fclose(fp);
437   if (!s)
438     {
439       pool_error(pool, -1, "%s: not an arch package", fn);
440       if (pkgidchk)
441         solv_chksum_free(pkgidchk, 0);
442       return 0;
443     }
444   if (s && !s->name)
445     {
446       pool_error(pool, -1, "%s: package has no name", fn);
447       repo_free_solvable(repo, s - pool->solvables, 1);
448       s = 0;
449     }
450   if (s)
451     {
452       if (!s->arch)
453         s->arch = ARCH_ANY;
454       if (!s->evr)
455         s->evr = ID_EMPTY;
456       s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
457       if (!(flags & REPO_NO_LOCATION))
458         repodata_set_location(data, s - pool->solvables, 0, 0, fn);
459       if (S_ISREG(stb.st_mode))
460         repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size);
461       if (pkgidchk)
462         {
463           unsigned char pkgid[16];
464           solv_chksum_free(pkgidchk, pkgid);
465           repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid);
466           pkgidchk = 0;
467         }
468     }
469   if (pkgidchk)
470     solv_chksum_free(pkgidchk, 0);
471   if (!(flags & REPO_NO_INTERNALIZE))
472     repodata_internalize(data);
473   return s ? s - pool->solvables : 0;
474 }
475
476 static char *getsentrynl(struct tarhead *th, char *s, int size)
477 {
478   int l;
479   if (!getsentry(th, s, size))
480     {
481       *s = 0;   /* eof */
482       return 0;
483     }
484   l = strlen(s);
485   if (!l)
486     return 0;
487   if (l && s[l - 1] == '\n')
488     {
489       s[l - 1] = 0;
490       return s;
491     }
492   while (getsentry(th, s, size))
493     {
494       l = strlen(s);
495       if (!l || s[l - 1] == '\n')
496         return 0;
497     }
498   *s = 0;       /* eof */
499   return 0;
500 }
501
502 static Hashtable
503 joinhash_init(Repo *repo, Hashval *hmp)
504 {
505   Hashval hm = mkmask(repo->nsolvables);
506   Hashtable ht = solv_calloc(hm + 1, sizeof(*ht));
507   Hashval h, hh;
508   Solvable *s;
509   int i;
510
511   FOR_REPO_SOLVABLES(repo, i, s)
512     {
513       hh = HASHCHAIN_START;
514       h = s->name & hm;
515       while (ht[h])
516         h = HASHCHAIN_NEXT(h, hh, hm);
517       ht[h] = i;
518     }
519   *hmp = hm;
520   return ht;
521 }
522
523 static Solvable *
524 joinhash_lookup(Repo *repo, Hashtable ht, Hashval hm, const char *fn)
525 {
526   const char *p;
527   Id name, evr;
528   Hashval h, hh;
529
530   if ((p = strrchr(fn, '/')) != 0)
531     fn = p + 1;
532   /* here we assume that the dirname is name-evr */
533   if (!*fn)
534     return 0;
535   for (p = fn + strlen(fn) - 1; p > fn; p--)
536     {
537       while (p > fn && *p != '-')
538         p--;
539       if (p == fn)
540         return 0;
541       name = pool_strn2id(repo->pool, fn, p - fn, 0);
542       if (!name)
543         continue;
544       evr = pool_str2id(repo->pool, p + 1, 0);
545       if (!evr)
546         continue;
547       /* found valid name/evr combination, check hash */
548       hh = HASHCHAIN_START;
549       h = name & hm;
550       while (ht[h])
551         {
552           Solvable *s = repo->pool->solvables + ht[h];
553           if (s->name == name && s->evr == evr)
554             return s;
555           h = HASHCHAIN_NEXT(h, hh, hm);
556         }
557     }
558   return 0;
559 }
560
561 static void
562 adddata(Repodata *data, Solvable *s, struct tarhead *th)
563 {
564   Repo *repo = data->repo;
565   Pool *pool = repo->pool;
566   char line[4096];
567   int l;
568   int havesha256 = 0;
569
570   while (getsentry(th, line, sizeof(line)))
571     {
572       l = strlen(line);
573       if (l == 0 || line[l - 1] != '\n')
574         continue;
575       line[--l] = 0;
576       if (l <= 2 || line[0] != '%' || line[l - 1] != '%')
577         continue;
578       if (!strcmp(line, "%FILENAME%"))
579         {
580           if (getsentrynl(th, line, sizeof(line)))
581             repodata_set_location(data, s - pool->solvables, 0, 0, line);
582         }
583       else if (!strcmp(line, "%NAME%"))
584         {
585           if (getsentrynl(th, line, sizeof(line)))
586             s->name = pool_str2id(pool, line, 1);
587         }
588       else if (!strcmp(line, "%VERSION%"))
589         {
590           if (getsentrynl(th, line, sizeof(line)))
591             s->evr = pool_str2id(pool, line, 1);
592         }
593       else if (!strcmp(line, "%DESC%"))
594         {
595           if (getsentrynl(th, line, sizeof(line)))
596             {
597               repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line);
598               repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, line);
599             }
600         }
601       else if (!strcmp(line, "%GROUPS%"))
602         {
603           if (getsentrynl(th, line, sizeof(line)))
604             repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_GROUP, line);
605         }
606       else if (!strcmp(line, "%CSIZE%"))
607         {
608           if (getsentrynl(th, line, sizeof(line)))
609             repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, strtoull(line, 0, 10));
610         }
611       else if (!strcmp(line, "%ISIZE%"))
612         {
613           if (getsentrynl(th, line, sizeof(line)))
614             repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(line, 0, 10));
615         }
616       else if (!strcmp(line, "%MD5SUM%"))
617         {
618           if (getsentrynl(th, line, sizeof(line)) && !havesha256)
619             repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, REPOKEY_TYPE_MD5, line);
620         }
621       else if (!strcmp(line, "%SHA256SUM%"))
622         {
623           if (getsentrynl(th, line, sizeof(line)))
624             {
625               repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, line);
626               havesha256 = 1;
627             }
628         }
629       else if (!strcmp(line, "%URL%"))
630         {
631           if (getsentrynl(th, line, sizeof(line)))
632             repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, line);
633         }
634       else if (!strcmp(line, "%LICENSE%"))
635         {
636           if (getsentrynl(th, line, sizeof(line)))
637             repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_LICENSE, line);
638         }
639       else if (!strcmp(line, "%ARCH%"))
640         {
641           if (getsentrynl(th, line, sizeof(line)))
642             s->arch = pool_str2id(pool, line, 1);
643         }
644       else if (!strcmp(line, "%BUILDDATE%"))
645         {
646           if (getsentrynl(th, line, sizeof(line)))
647             repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, strtoull(line, 0, 10));
648         }
649       else if (!strcmp(line, "%PACKAGER%"))
650         {
651           if (getsentrynl(th, line, sizeof(line)))
652             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_PACKAGER, line);
653         }
654       else if (!strcmp(line, "%REPLACES%"))
655         {
656           while (getsentrynl(th, line, sizeof(line)) && *line)
657             s->obsoletes = adddep(repo, s->obsoletes, line);
658         }
659       else if (!strcmp(line, "%DEPENDS%"))
660         {
661           while (getsentrynl(th, line, sizeof(line)) && *line)
662             s->requires = adddep(repo, s->requires, line);
663         }
664       else if (!strcmp(line, "%CONFLICTS%"))
665         {
666           while (getsentrynl(th, line, sizeof(line)) && *line)
667             s->conflicts = adddep(repo, s->conflicts, line);
668         }
669       else if (!strcmp(line, "%PROVIDES%"))
670         {
671           while (getsentrynl(th, line, sizeof(line)) && *line)
672             s->provides = adddep(repo, s->provides, line);
673         }
674       else if (!strcmp(line, "%OPTDEPENDS%"))
675         {
676           while (getsentrynl(th, line, sizeof(line)) && *line)
677             {
678               char *p = strchr(line, ':');
679               if (p && p > line)
680                 *p = 0;
681               s->suggests = adddep(repo, s->suggests, line);
682             }
683         }
684       else if (!strcmp(line, "%FILES%"))
685         {
686           while (getsentrynl(th, line, sizeof(line)) && *line)
687             {
688               char *p;
689               Id id;
690               l = strlen(line);
691               if (l > 1 && line[l - 1] == '/')
692                 line[--l] = 0;  /* remove trailing slashes */
693               if ((p = strrchr(line , '/')) != 0)
694                 {
695                   *p++ = 0;
696                   if (line[0] != '/')   /* anchor */
697                     {
698                       char tmp = *p;
699                       memmove(line + 1, line, p - 1 - line);
700                       *line = '/';
701                       *p = 0;
702                       id = repodata_str2dir(data, line, 1);
703                       *p = tmp;
704                     }
705                   else
706                     id = repodata_str2dir(data, line, 1);
707                 }
708               else
709                 {
710                   p = line;
711                   id = 0;
712                 }
713               if (!id)
714                 id = repodata_str2dir(data, "/", 1);
715               repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, id, p);
716             }
717         }
718       while (*line)
719         getsentrynl(th, line, sizeof(line));
720     }
721 }
722
723 static void
724 finishsolvable(Repo *repo, Solvable *s)
725 {
726   Pool *pool = repo->pool;
727   if (!s)
728     return;
729   if (!s->name)
730     {
731       repo_free_solvable(repo, s - pool->solvables, 1);
732       return;
733     }
734   if (!s->arch)
735     s->arch = ARCH_ANY;
736   if (!s->evr)
737     s->evr = ID_EMPTY;
738   s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
739 }
740
741 int
742 repo_add_arch_repo(Repo *repo, FILE *fp, int flags)
743 {
744   Pool *pool = repo->pool;
745   Repodata *data;
746   struct tarhead th;
747   char *lastdn = 0;
748   int lastdnlen = 0;
749   Solvable *s = 0;
750   Hashtable joinhash = 0;
751   Hashval joinhashmask = 0;
752
753   data = repo_add_repodata(repo, flags);
754
755   if (flags & REPO_EXTEND_SOLVABLES)
756     joinhash = joinhash_init(repo, &joinhashmask);
757
758   inittarhead(&th, fp);
759   while (gettarhead(&th) > 0)
760     {
761       char *bn;
762       if (th.type != 1)
763         {
764           skipentry(&th);
765           continue;
766         }
767       bn = strrchr(th.path, '/');
768       if (!bn || (strcmp(bn + 1, "desc") != 0 && strcmp(bn + 1, "depends") != 0 && strcmp(bn + 1, "files") != 0))
769         {
770           skipentry(&th);
771           continue;
772         }
773       if ((flags & REPO_EXTEND_SOLVABLES) != 0 && (!strcmp(bn + 1, "desc") || !strcmp(bn + 1, "depends")))
774         {
775           skipentry(&th);
776           continue;     /* skip those when we're extending */
777         }
778       if (!lastdn || (bn - th.path) != lastdnlen || strncmp(lastdn, th.path, lastdnlen) != 0)
779         {
780           finishsolvable(repo, s);
781           solv_free(lastdn);
782           lastdn = solv_strdup(th.path);
783           lastdnlen = bn - th.path;
784           lastdn[lastdnlen] = 0;
785           if (flags & REPO_EXTEND_SOLVABLES)
786             {
787               s = joinhash_lookup(repo, joinhash, joinhashmask, lastdn);
788               if (!s)
789                 {
790                   skipentry(&th);
791                   continue;
792                 }
793             }
794           else
795             s = pool_id2solvable(pool, repo_add_solvable(repo));
796         }
797       adddata(data, s, &th);
798     }
799   finishsolvable(repo, s);
800   solv_free(joinhash);
801   solv_free(lastdn);
802   if (!(flags & REPO_NO_INTERNALIZE))
803     repodata_internalize(data);
804   return 0;
805 }
806
807 int
808 repo_add_arch_local(Repo *repo, const char *dir, int flags)
809 {
810   Pool *pool = repo->pool;
811   Repodata *data;
812   DIR *dp;
813   struct dirent *de;
814   char *entrydir, *file;
815   FILE *fp;
816   Solvable *s;
817
818   data = repo_add_repodata(repo, flags);
819
820   if (flags & REPO_USE_ROOTDIR)
821     dir = pool_prepend_rootdir(pool, dir);
822   dp = opendir(dir);
823   if (dp)
824     {
825       while ((de = readdir(dp)) != 0)
826         {
827           if (!de->d_name[0] || de->d_name[0] == '.')
828             continue;
829           entrydir = solv_dupjoin(dir, "/", de->d_name);
830           file = pool_tmpjoin(repo->pool, entrydir, "/desc", 0);
831           s = 0;
832           if ((fp = fopen(file, "r")) != 0)
833             {
834               struct tarhead th;
835               inittarhead(&th, fp);
836               s = pool_id2solvable(pool, repo_add_solvable(repo));
837               adddata(data, s, &th);
838               freetarhead(&th);
839               fclose(fp);
840               file = pool_tmpjoin(repo->pool, entrydir, "/files", 0);
841               if ((fp = fopen(file, "r")) != 0)
842                 {
843                   inittarhead(&th, fp);
844                   adddata(data, s, &th);
845                   freetarhead(&th);
846                   fclose(fp);
847                 }
848             }
849           solv_free(entrydir);
850         }
851       closedir(dp);
852     }
853   if (!(flags & REPO_NO_INTERNALIZE))
854     repodata_internalize(data);
855   if (flags & REPO_USE_ROOTDIR)
856     solv_free((char *)dir);
857   return 0;
858 }
859