Imported Upstream version 0.7.2
[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       s = solvable_free(s, 1);
448     }
449   if (s)
450     {
451       if (!s->arch)
452         s->arch = ARCH_ANY;
453       if (!s->evr)
454         s->evr = ID_EMPTY;
455       s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
456       if (!(flags & REPO_NO_LOCATION))
457         repodata_set_location(data, s - pool->solvables, 0, 0, fn);
458       if (S_ISREG(stb.st_mode))
459         repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size);
460       if (pkgidchk)
461         {
462           unsigned char pkgid[16];
463           solv_chksum_free(pkgidchk, pkgid);
464           repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid);
465           pkgidchk = 0;
466         }
467     }
468   if (pkgidchk)
469     solv_chksum_free(pkgidchk, 0);
470   if (!(flags & REPO_NO_INTERNALIZE))
471     repodata_internalize(data);
472   return s ? s - pool->solvables : 0;
473 }
474
475 static char *getsentrynl(struct tarhead *th, char *s, int size)
476 {
477   int l;
478   if (!getsentry(th, s, size))
479     {
480       *s = 0;   /* eof */
481       return 0;
482     }
483   l = strlen(s);
484   if (!l)
485     return 0;
486   if (l && s[l - 1] == '\n')
487     {
488       s[l - 1] = 0;
489       return s;
490     }
491   while (getsentry(th, s, size))
492     {
493       l = strlen(s);
494       if (!l || s[l - 1] == '\n')
495         return 0;
496     }
497   *s = 0;       /* eof */
498   return 0;
499 }
500
501 static Hashtable
502 joinhash_init(Repo *repo, Hashval *hmp)
503 {
504   Hashval hm = mkmask(repo->nsolvables);
505   Hashtable ht = solv_calloc(hm + 1, sizeof(*ht));
506   Hashval h, hh;
507   Solvable *s;
508   int i;
509
510   FOR_REPO_SOLVABLES(repo, i, s)
511     {
512       hh = HASHCHAIN_START;
513       h = s->name & hm;
514       while (ht[h])
515         h = HASHCHAIN_NEXT(h, hh, hm);
516       ht[h] = i;
517     }
518   *hmp = hm;
519   return ht;
520 }
521
522 static Solvable *
523 joinhash_lookup(Repo *repo, Hashtable ht, Hashval hm, const char *fn)
524 {
525   const char *p;
526   Id name, evr;
527   Hashval h, hh;
528
529   if ((p = strrchr(fn, '/')) != 0)
530     fn = p + 1;
531   /* here we assume that the dirname is name-evr */
532   if (!*fn)
533     return 0;
534   for (p = fn + strlen(fn) - 1; p > fn; p--)
535     {
536       while (p > fn && *p != '-')
537         p--;
538       if (p == fn)
539         return 0;
540       name = pool_strn2id(repo->pool, fn, p - fn, 0);
541       if (!name)
542         continue;
543       evr = pool_str2id(repo->pool, p + 1, 0);
544       if (!evr)
545         continue;
546       /* found valid name/evr combination, check hash */
547       hh = HASHCHAIN_START;
548       h = name & hm;
549       while (ht[h])
550         {
551           Solvable *s = repo->pool->solvables + ht[h];
552           if (s->name == name && s->evr == evr)
553             return s;
554           h = HASHCHAIN_NEXT(h, hh, hm);
555         }
556     }
557   return 0;
558 }
559
560 static void
561 adddata(Repodata *data, Solvable *s, struct tarhead *th)
562 {
563   Repo *repo = data->repo;
564   Pool *pool = repo->pool;
565   char line[4096];
566   int l;
567   int havesha256 = 0;
568
569   while (getsentry(th, line, sizeof(line)))
570     {
571       l = strlen(line);
572       if (l == 0 || line[l - 1] != '\n')
573         continue;
574       line[--l] = 0;
575       if (l <= 2 || line[0] != '%' || line[l - 1] != '%')
576         continue;
577       if (!strcmp(line, "%FILENAME%"))
578         {
579           if (getsentrynl(th, line, sizeof(line)))
580             repodata_set_location(data, s - pool->solvables, 0, 0, line);
581         }
582       else if (!strcmp(line, "%NAME%"))
583         {
584           if (getsentrynl(th, line, sizeof(line)))
585             s->name = pool_str2id(pool, line, 1);
586         }
587       else if (!strcmp(line, "%VERSION%"))
588         {
589           if (getsentrynl(th, line, sizeof(line)))
590             s->evr = pool_str2id(pool, line, 1);
591         }
592       else if (!strcmp(line, "%DESC%"))
593         {
594           if (getsentrynl(th, line, sizeof(line)))
595             {
596               repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line);
597               repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, line);
598             }
599         }
600       else if (!strcmp(line, "%GROUPS%"))
601         {
602           if (getsentrynl(th, line, sizeof(line)))
603             repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_GROUP, line);
604         }
605       else if (!strcmp(line, "%CSIZE%"))
606         {
607           if (getsentrynl(th, line, sizeof(line)))
608             repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, strtoull(line, 0, 10));
609         }
610       else if (!strcmp(line, "%ISIZE%"))
611         {
612           if (getsentrynl(th, line, sizeof(line)))
613             repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(line, 0, 10));
614         }
615       else if (!strcmp(line, "%MD5SUM%"))
616         {
617           if (getsentrynl(th, line, sizeof(line)) && !havesha256)
618             repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, REPOKEY_TYPE_MD5, line);
619         }
620       else if (!strcmp(line, "%SHA256SUM%"))
621         {
622           if (getsentrynl(th, line, sizeof(line)))
623             {
624               repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, line);
625               havesha256 = 1;
626             }
627         }
628       else if (!strcmp(line, "%URL%"))
629         {
630           if (getsentrynl(th, line, sizeof(line)))
631             repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, line);
632         }
633       else if (!strcmp(line, "%LICENSE%"))
634         {
635           if (getsentrynl(th, line, sizeof(line)))
636             repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_LICENSE, line);
637         }
638       else if (!strcmp(line, "%ARCH%"))
639         {
640           if (getsentrynl(th, line, sizeof(line)))
641             s->arch = pool_str2id(pool, line, 1);
642         }
643       else if (!strcmp(line, "%BUILDDATE%"))
644         {
645           if (getsentrynl(th, line, sizeof(line)))
646             repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, strtoull(line, 0, 10));
647         }
648       else if (!strcmp(line, "%PACKAGER%"))
649         {
650           if (getsentrynl(th, line, sizeof(line)))
651             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_PACKAGER, line);
652         }
653       else if (!strcmp(line, "%REPLACES%"))
654         {
655           while (getsentrynl(th, line, sizeof(line)) && *line)
656             s->obsoletes = adddep(repo, s->obsoletes, line);
657         }
658       else if (!strcmp(line, "%DEPENDS%"))
659         {
660           while (getsentrynl(th, line, sizeof(line)) && *line)
661             s->requires = adddep(repo, s->requires, line);
662         }
663       else if (!strcmp(line, "%CONFLICTS%"))
664         {
665           while (getsentrynl(th, line, sizeof(line)) && *line)
666             s->conflicts = adddep(repo, s->conflicts, line);
667         }
668       else if (!strcmp(line, "%PROVIDES%"))
669         {
670           while (getsentrynl(th, line, sizeof(line)) && *line)
671             s->provides = adddep(repo, s->provides, line);
672         }
673       else if (!strcmp(line, "%OPTDEPENDS%"))
674         {
675           while (getsentrynl(th, line, sizeof(line)) && *line)
676             {
677               char *p = strchr(line, ':');
678               if (p && p > line)
679                 *p = 0;
680               s->suggests = adddep(repo, s->suggests, line);
681             }
682         }
683       else if (!strcmp(line, "%FILES%"))
684         {
685           while (getsentrynl(th, line, sizeof(line)) && *line)
686             {
687               char *p;
688               Id id;
689               l = strlen(line);
690               if (l > 1 && line[l - 1] == '/')
691                 line[--l] = 0;  /* remove trailing slashes */
692               if ((p = strrchr(line , '/')) != 0)
693                 {
694                   *p++ = 0;
695                   if (line[0] != '/')   /* anchor */
696                     {
697                       char tmp = *p;
698                       memmove(line + 1, line, p - 1 - line);
699                       *line = '/';
700                       *p = 0;
701                       id = repodata_str2dir(data, line, 1);
702                       *p = tmp;
703                     }
704                   else
705                     id = repodata_str2dir(data, line, 1);
706                 }
707               else
708                 {
709                   p = line;
710                   id = 0;
711                 }
712               if (!id)
713                 id = repodata_str2dir(data, "/", 1);
714               repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, id, p);
715             }
716         }
717       while (*line)
718         getsentrynl(th, line, sizeof(line));
719     }
720 }
721
722 static void
723 finishsolvable(Repo *repo, Solvable *s)
724 {
725   Pool *pool = repo->pool;
726   if (!s)
727     return;
728   if (!s->name)
729     {
730       solvable_free(s, 1);
731       return;
732     }
733   if (!s->arch)
734     s->arch = ARCH_ANY;
735   if (!s->evr)
736     s->evr = ID_EMPTY;
737   s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
738 }
739
740 int
741 repo_add_arch_repo(Repo *repo, FILE *fp, int flags)
742 {
743   Pool *pool = repo->pool;
744   Repodata *data;
745   struct tarhead th;
746   char *lastdn = 0;
747   int lastdnlen = 0;
748   Solvable *s = 0;
749   Hashtable joinhash = 0;
750   Hashval joinhashmask = 0;
751
752   data = repo_add_repodata(repo, flags);
753
754   if (flags & REPO_EXTEND_SOLVABLES)
755     joinhash = joinhash_init(repo, &joinhashmask);
756
757   inittarhead(&th, fp);
758   while (gettarhead(&th) > 0)
759     {
760       char *bn;
761       if (th.type != 1)
762         {
763           skipentry(&th);
764           continue;
765         }
766       bn = strrchr(th.path, '/');
767       if (!bn || (strcmp(bn + 1, "desc") != 0 && strcmp(bn + 1, "depends") != 0 && strcmp(bn + 1, "files") != 0))
768         {
769           skipentry(&th);
770           continue;
771         }
772       if ((flags & REPO_EXTEND_SOLVABLES) != 0 && (!strcmp(bn + 1, "desc") || !strcmp(bn + 1, "depends")))
773         {
774           skipentry(&th);
775           continue;     /* skip those when we're extending */
776         }
777       if (!lastdn || (bn - th.path) != lastdnlen || strncmp(lastdn, th.path, lastdnlen) != 0)
778         {
779           finishsolvable(repo, s);
780           solv_free(lastdn);
781           lastdn = solv_strdup(th.path);
782           lastdnlen = bn - th.path;
783           lastdn[lastdnlen] = 0;
784           if (flags & REPO_EXTEND_SOLVABLES)
785             {
786               s = joinhash_lookup(repo, joinhash, joinhashmask, lastdn);
787               if (!s)
788                 {
789                   skipentry(&th);
790                   continue;
791                 }
792             }
793           else
794             s = pool_id2solvable(pool, repo_add_solvable(repo));
795         }
796       adddata(data, s, &th);
797     }
798   finishsolvable(repo, s);
799   solv_free(joinhash);
800   solv_free(lastdn);
801   if (!(flags & REPO_NO_INTERNALIZE))
802     repodata_internalize(data);
803   return 0;
804 }
805
806 int
807 repo_add_arch_local(Repo *repo, const char *dir, int flags)
808 {
809   Pool *pool = repo->pool;
810   Repodata *data;
811   DIR *dp;
812   struct dirent *de;
813   char *entrydir, *file;
814   FILE *fp;
815   Solvable *s;
816
817   data = repo_add_repodata(repo, flags);
818
819   if (flags & REPO_USE_ROOTDIR)
820     dir = pool_prepend_rootdir(pool, dir);
821   dp = opendir(dir);
822   if (dp)
823     {
824       while ((de = readdir(dp)) != 0)
825         {
826           if (!de->d_name[0] || de->d_name[0] == '.')
827             continue;
828           entrydir = solv_dupjoin(dir, "/", de->d_name);
829           file = pool_tmpjoin(repo->pool, entrydir, "/desc", 0);
830           s = 0;
831           if ((fp = fopen(file, "r")) != 0)
832             {
833               struct tarhead th;
834               inittarhead(&th, fp);
835               s = pool_id2solvable(pool, repo_add_solvable(repo));
836               adddata(data, s, &th);
837               freetarhead(&th);
838               fclose(fp);
839               file = pool_tmpjoin(repo->pool, entrydir, "/files", 0);
840               if ((fp = fopen(file, "r")) != 0)
841                 {
842                   inittarhead(&th, fp);
843                   adddata(data, s, &th);
844                   freetarhead(&th);
845                   fclose(fp);
846                 }
847             }
848           solv_free(entrydir);
849         }
850       closedir(dp);
851     }
852   if (!(flags & REPO_NO_INTERNALIZE))
853     repodata_internalize(data);
854   if (flags & REPO_USE_ROOTDIR)
855     solv_free((char *)dir);
856   return 0;
857 }
858