- use repo_free_solvable instead of repo_free_solvable_block when freeing a single...
[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             l = length;
200             data = pp = solv_malloc(l + 512);
201             for (; l > 0; l -= 512, pp += 512)
202               if (readblock(th->fp, (unsigned char *)pp))
203                 {
204                   solv_free(data);
205                   return -1;
206                 }
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                 int l;
220                 for (l = 0; l < length && pp[l] >= '0' && pp[l] <= '9'; l++)
221                   ll = ll * 10 + (pp[l] - '0');
222                 if (l == length || pp[l] != ' ' || ll < 1 || ll > length || pp[ll - 1] != '\n')
223                   {
224                     solv_free(data);
225                     return -1;
226                   }
227                 length -= ll;
228                 pp += l + 1;
229                 ll -= l + 1;
230                 pp[ll - 1] = 0;
231                 if (!strncmp(pp, "path=", 5))
232                   {
233                     solv_free(th->path);
234                     th->path = solv_strdup(pp + 5);
235                   }
236                 pp += ll;
237               }
238             solv_free(data);
239             th->ispax = 1;
240             length = 0;
241             break;
242           }
243         default:
244           type = 3;     /* extension */
245           break;
246         }
247       if ((type == 1 || type == 2) && !th->path)
248         {
249           char path[157];
250           memcpy(path, th->blk, 156);
251           path[156] = 0;
252           if (!memcmp(th->blk + 257, "ustar\0\060\060", 8) && !th->path && th->blk[345])
253             {
254               /* POSIX ustar with prefix */
255               char prefix[156];
256               memcpy(prefix, th->blk + 345, 155);
257               prefix[155] = 0;
258               l = strlen(prefix);
259               if (prefix[l - 1] == '/')
260                 prefix[l - 1] = 0;
261               th->path = solv_dupjoin(prefix, "/", path);
262             }
263           else
264             th->path = solv_dupjoin(path, 0, 0);
265         }
266       if (type == 1 || type == 2)
267         {
268           l = strlen(th->path);
269           if (l && th->path[l - 1] == '/')
270             {
271               if (l > 1)
272                 th->path[l - 1] = 0;
273               type = 2;
274             }
275         }
276       if (type != 3)
277         break;
278       while (length > 0)
279         {
280           r = readblock(th->fp, th->blk);
281           if (r)
282             return r;
283           length -= 512;
284         }
285     }
286   th->type = type;
287   th->length = length;
288   return 1;
289 }
290
291 static Offset
292 adddep(Repo *repo, Offset olddeps, char *line)
293 {
294   Pool *pool = repo->pool;
295   char *p;
296   Id id;
297
298   while (*line == ' ' && *line == '\t')
299     line++;
300   p = line;
301   while (*p && *p != ' ' && *p != '\t' && *p != '<' && *p != '=' && *p != '>')
302     p++;
303   id = pool_strn2id(pool, line, p - line, 1);
304   while (*p == ' ' && *p == '\t')
305     p++;
306   if (*p == '<' || *p == '=' || *p == '>')
307     {
308       int flags = 0;
309       for (;; p++)
310         {  
311           if (*p == '<')
312             flags |= REL_LT;
313           else if (*p == '=')
314             flags |= REL_EQ;
315           else if (*p == '>')
316             flags |= REL_GT;
317           else
318             break;
319         }
320       while (*p == ' ' && *p == '\t')
321         p++;
322       line = p;
323       while (*p && *p != ' ' && *p != '\t')
324         p++;
325       id = pool_rel2id(pool, id, pool_strn2id(pool, line, p - line, 1), flags, 1);
326     }
327   return repo_addid_dep(repo, olddeps, id, 0);
328 }
329
330 Id
331 repo_add_arch_pkg(Repo *repo, const char *fn, int flags)
332 {
333   Pool *pool = repo->pool;
334   Repodata *data;
335   FILE *fp;
336   struct tarhead th;
337   char line[4096];
338   int ignoreline;
339   Solvable *s;
340   int l, fd;
341   struct stat stb;
342   void *pkgidhandle = 0;
343
344   data = repo_add_repodata(repo, flags);
345   if ((fd = open(fn, O_RDONLY, 0)) < 0)
346     {
347       pool_debug(pool, SOLV_ERROR, "repo_add_arch_pkg: %s: %s\n", fn, strerror(errno));
348       return 0;
349     }
350   if (fstat(fd, &stb))
351     {
352       pool_debug(pool, SOLV_ERROR, "repo_add_arch_pkg: %s: fstat failed\n", fn);
353       close(fd);
354       return 0;
355     }
356   if (!(fp = solv_xfopen_fd(fn, fd, "r")))
357     {
358       pool_debug(pool, SOLV_ERROR, "repo_add_arch_pkg: %s: fdopen failed\n", fn);
359       close(fd);
360       return 0;
361     }
362   s = 0;
363   inittarhead(&th, fp);
364   while (gettarhead(&th) > 0)
365     {
366       if (th.type != 1 || strcmp(th.path, ".PKGINFO") != 0)
367         {
368           skipentry(&th);
369           continue;
370         }
371       ignoreline = 0;
372       s = pool_id2solvable(pool, repo_add_solvable(repo));
373       if (flags & ARCH_ADD_WITH_PKGID)
374         pkgidhandle = solv_chksum_create(REPOKEY_TYPE_MD5);
375       while (getsentry(&th, line, sizeof(line)))
376         {
377           l = strlen(line);
378           if (l == 0)
379             continue;
380           if (pkgidhandle)
381             solv_chksum_add(pkgidhandle, line, l);
382           if (line[l - 1] != '\n')
383             {
384               ignoreline = 1;
385               continue;
386             }
387           if (ignoreline)
388             {
389               ignoreline = 0;
390               continue;
391             }
392           line[--l] = 0;
393           if (l == 0 || line[0] == '#')
394             continue;
395           if (!strncmp(line, "pkgname = ", 10))
396             s->name = pool_str2id(pool, line + 10, 1);
397           else if (!strncmp(line, "pkgver = ", 9))
398             s->evr = pool_str2id(pool, line + 9, 1);
399           else if (!strncmp(line, "pkgdesc = ", 10))
400             {
401               repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line + 10);
402               repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, line + 10);
403             }
404           else if (!strncmp(line, "url = ", 6))
405             repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, line + 6);
406           else if (!strncmp(line, "builddate = ", 12))
407             repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, strtoull(line + 12, 0, 10));
408           else if (!strncmp(line, "packager = ", 11))
409             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_PACKAGER, line + 11);
410           else if (!strncmp(line, "size = ", 7))
411             repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(line + 7, 0, 10));
412           else if (!strncmp(line, "arch = ", 7))
413             s->arch = pool_str2id(pool, line + 7, 1);
414           else if (!strncmp(line, "license = ", 10))
415             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_LICENSE, line + 10);
416           else if (!strncmp(line, "replaces = ", 11))
417             s->obsoletes = adddep(repo, s->obsoletes, line + 11);
418           else if (!strncmp(line, "group = ", 8))
419             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_GROUP, line + 8);
420           else if (!strncmp(line, "depend = ", 9))
421             s->requires = adddep(repo, s->requires, line + 9);
422           else if (!strncmp(line, "optdepend = ", 12))
423             {
424               char *p = strchr(line, ':');
425               if (p)
426                 *p = 0;
427               s->suggests = adddep(repo, s->suggests, line + 12);
428             }
429           else if (!strncmp(line, "conflict = ", 11))
430             s->conflicts = adddep(repo, s->conflicts, line + 11);
431           else if (!strncmp(line, "provides = ", 11))
432             s->provides = adddep(repo, s->provides, line + 11);
433         }
434       break;
435     }
436   freetarhead(&th);
437   fclose(fp);
438   if (s && !s->name)
439     {
440       repo_free_solvable(repo, s - pool->solvables, 1);
441       s = 0;
442     }
443   if (s)
444     {
445       if (!s->arch)
446         s->arch = ARCH_ANY;
447       if (!s->evr)
448         s->evr = ID_EMPTY;
449       s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
450       repodata_set_location(data, s - pool->solvables, 0, 0, fn);
451       repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size);
452       if (pkgidhandle)
453         {
454           unsigned char pkgid[16];
455           solv_chksum_free(pkgidhandle, pkgid);
456           repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid);
457           pkgidhandle = 0;
458         }
459     }
460   if (pkgidhandle)
461     solv_chksum_free(pkgidhandle, 0);
462   if (!(flags & REPO_NO_INTERNALIZE))
463     repodata_internalize(data);
464   return s ? s - pool->solvables : 0;
465 }
466
467 static char *getsentrynl(struct tarhead *th, char *s, int size)
468 {
469   int l;
470   if (!getsentry(th, s, size))
471     {
472       *s = 0;   /* eof */
473       return 0;
474     }
475   l = strlen(s);
476   if (!l)
477     return 0;
478   if (l && s[l - 1] == '\n')
479     {
480       s[l - 1] = 0;
481       return s;
482     }
483   while (getsentry(th, s, size))
484     {
485       l = strlen(s);
486       if (!l || s[l - 1] == '\n')
487         return 0;
488     }
489   *s = 0;       /* eof */
490   return 0;
491 }
492
493 static Hashtable
494 joinhash_init(Repo *repo, Hashmask *hmp)
495 {
496   Hashmask hm = mkmask(repo->nsolvables);
497   Hashtable ht = solv_calloc(hm + 1, sizeof(*ht));
498   Hashval h, hh;
499   Solvable *s;
500   int i;
501
502   FOR_REPO_SOLVABLES(repo, i, s)
503     {
504       hh = HASHCHAIN_START;
505       h = s->name & hm;
506       while (ht[h])
507         h = HASHCHAIN_NEXT(h, hh, hm);
508       ht[h] = i;
509     }
510   *hmp = hm;
511   return ht;
512 }
513
514 static Solvable *
515 joinhash_lookup(Repo *repo, Hashtable ht, Hashmask hm, const char *fn)
516 {
517   const char *p;
518   Id name, evr;
519   Hashval h, hh;
520
521   if ((p = strrchr(fn, '/')) != 0)
522     fn = p + 1;
523   /* here we assume that the dirname is name-evr */
524   if (!*fn)
525     return 0;
526   for (p = fn + strlen(fn) - 1; p > fn; p--)
527     {
528       while (p > fn && *p != '-')
529         p--;
530       if (p == fn)
531         return 0;
532       name = pool_strn2id(repo->pool, fn, p - fn, 0);
533       if (!name)
534         continue;
535       evr = pool_str2id(repo->pool, p + 1, 0);
536       if (!evr)
537         continue;
538       /* found valid name/evr combination, check hash */
539       hh = HASHCHAIN_START;
540       h = name & hm;
541       while (ht[h])
542         {
543           Solvable *s = repo->pool->solvables + ht[h];
544           if (s->name == name && s->evr == evr)
545             return s;
546           h = HASHCHAIN_NEXT(h, hh, hm);
547         }
548     }
549   return 0;
550 }
551
552 static void
553 adddata(Repodata *data, Solvable *s, struct tarhead *th)
554 {
555   Repo *repo = data->repo;
556   Pool *pool = repo->pool;
557   char line[4096];
558   int l;
559   int havesha256 = 0;
560
561   while (getsentry(th, line, sizeof(line)))
562     {
563       l = strlen(line);
564       if (l == 0 || line[l - 1] != '\n')
565         continue;
566       line[--l] = 0;
567       if (l <= 2 || line[0] != '%' || line[l - 1] != '%')
568         continue;
569       if (!strcmp(line, "%FILENAME%"))
570         {
571           if (getsentrynl(th, line, sizeof(line)))
572             repodata_set_location(data, s - pool->solvables, 0, 0, line);
573         }
574       else if (!strcmp(line, "%NAME%"))
575         {
576           if (getsentrynl(th, line, sizeof(line)))
577             s->name = pool_str2id(pool, line, 1);
578         }
579       else if (!strcmp(line, "%VERSION%"))
580         {
581           if (getsentrynl(th, line, sizeof(line)))
582             s->evr = pool_str2id(pool, line, 1);
583         }
584       else if (!strcmp(line, "%DESC%"))
585         {
586           if (getsentrynl(th, line, sizeof(line)))
587             {
588               repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line);
589               repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, line);
590             }
591         }
592       else if (!strcmp(line, "%GROUPS%"))
593         {
594           if (getsentrynl(th, line, sizeof(line)))
595             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_GROUP, line);
596         }
597       else if (!strcmp(line, "%CSIZE%"))
598         {
599           if (getsentrynl(th, line, sizeof(line)))
600             repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, strtoull(line, 0, 10));
601         }
602       else if (!strcmp(line, "%ISIZE%"))
603         {
604           if (getsentrynl(th, line, sizeof(line)))
605             repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(line, 0, 10));
606         }
607       else if (!strcmp(line, "%MD5SUM%"))
608         {
609           if (getsentrynl(th, line, sizeof(line)) && !havesha256)
610             repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, REPOKEY_TYPE_MD5, line);
611         }
612       else if (!strcmp(line, "%SHA256SUM%"))
613         {
614           if (getsentrynl(th, line, sizeof(line)))
615             {
616               repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, line);
617               havesha256 = 1;
618             }
619         }
620       else if (!strcmp(line, "%URL%"))
621         {
622           if (getsentrynl(th, line, sizeof(line)))
623             repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, line);
624         }
625       else if (!strcmp(line, "%LICENSE%"))
626         {
627           if (getsentrynl(th, line, sizeof(line)))
628             repodata_set_str(data, s - pool->solvables, SOLVABLE_LICENSE, line);
629         }
630       else if (!strcmp(line, "%ARCH%"))
631         {
632           if (getsentrynl(th, line, sizeof(line)))
633             s->arch = pool_str2id(pool, line, 1);
634         }
635       else if (!strcmp(line, "%BUILDDATE%"))
636         {
637           if (getsentrynl(th, line, sizeof(line)))
638             repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, strtoull(line, 0, 10));
639         }
640       else if (!strcmp(line, "%PACKAGER%"))
641         {
642           if (getsentrynl(th, line, sizeof(line)))
643             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_PACKAGER, line);
644         }
645       else if (!strcmp(line, "%REPLACES%"))
646         {
647           while (getsentrynl(th, line, sizeof(line)) && *line)
648             s->obsoletes = adddep(repo, s->obsoletes, line);
649         }
650       else if (!strcmp(line, "%DEPENDS%"))
651         {
652           while (getsentrynl(th, line, sizeof(line)) && *line)
653             s->requires = adddep(repo, s->requires, line);
654         }
655       else if (!strcmp(line, "%CONFLICTS%"))
656         {
657           while (getsentrynl(th, line, sizeof(line)) && *line)
658             s->conflicts = adddep(repo, s->conflicts, line);
659         }
660       else if (!strcmp(line, "%PROVIDES%"))
661         {
662           while (getsentrynl(th, line, sizeof(line)) && *line)
663             s->provides = adddep(repo, s->provides, line);
664         }
665       else if (!strcmp(line, "%OPTDEPENDS%"))
666         {
667           while (getsentrynl(th, line, sizeof(line)) && *line)
668             {
669               char *p = strchr(line, ':');
670               if (p && p > line)
671                 *p = 0;
672               s->suggests = adddep(repo, s->suggests, line);
673             }
674         }
675       else if (!strcmp(line, "%FILES%"))
676         {
677           while (getsentrynl(th, line, sizeof(line)) && *line)
678             {
679               char *p;
680               Id id;
681               l = strlen(line);
682               if (l > 1 && line[l - 1] == '/')
683                 line[--l] = 0;  /* remove trailing slashes */
684               if ((p = strrchr(line , '/')) != 0)
685                 {
686                   *p++ = 0;
687                   if (line[0] != '/')   /* anchor */
688                     {
689                       char tmp = *p;
690                       memmove(line + 1, line, p - 1 - line);
691                       *line = '/';
692                       *p = 0;
693                       id = repodata_str2dir(data, line, 1);
694                       *p = tmp;
695                     }
696                   else
697                     id = repodata_str2dir(data, line, 1);
698                 }
699               else
700                 {
701                   p = line;
702                   id = 0;
703                 }
704               if (!id)
705                 id = repodata_str2dir(data, "/", 1);
706               repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, id, p);
707             }
708         }
709       while (*line)
710         getsentrynl(th, line, sizeof(line));
711     }
712 }
713
714 static void
715 finishsolvable(Repo *repo, Solvable *s)
716 {
717   Pool *pool = repo->pool;
718   if (!s)
719     return;
720   if (!s->name)
721     {
722       repo_free_solvable(repo, s - pool->solvables, 1);
723       return;
724     }
725   if (!s->arch)
726     s->arch = ARCH_ANY;
727   if (!s->evr)
728     s->evr = ID_EMPTY;
729   s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
730 }
731
732 int
733 repo_add_arch_repo(Repo *repo, FILE *fp, int flags)
734 {
735   Pool *pool = repo->pool;
736   Repodata *data;
737   struct tarhead th;
738   char *lastdn = 0;
739   int lastdnlen = 0;
740   Solvable *s = 0;
741   Hashtable joinhash = 0;
742   Hashmask joinhashmask = 0;
743
744   data = repo_add_repodata(repo, flags);
745
746   if (flags & REPO_EXTEND_SOLVABLES)
747     joinhash = joinhash_init(repo, &joinhashmask);
748
749   inittarhead(&th, fp);
750   while (gettarhead(&th) > 0)
751     {
752       char *bn;
753       if (th.type != 1)
754         {
755           skipentry(&th);
756           continue;
757         }
758       bn = strrchr(th.path, '/');
759       if (!bn || (strcmp(bn + 1, "desc") != 0 && strcmp(bn + 1, "depends") != 0 && strcmp(bn + 1, "files") != 0))
760         {
761           skipentry(&th);
762           continue;
763         }
764       if ((flags & REPO_EXTEND_SOLVABLES) != 0 && (!strcmp(bn + 1, "desc") || !strcmp(bn + 1, "depends")))
765         {
766           skipentry(&th);
767           continue;     /* skip those when we're extending */
768         }
769       if (!lastdn || (bn - th.path) != lastdnlen || strncmp(lastdn, th.path, lastdnlen) != 0)
770         {
771           finishsolvable(repo, s);
772           solv_free(lastdn);
773           lastdn = solv_strdup(th.path);
774           lastdnlen = bn - th.path;
775           lastdn[lastdnlen] = 0;
776           if (flags & REPO_EXTEND_SOLVABLES)
777             {
778               s = joinhash_lookup(repo, joinhash, joinhashmask, lastdn);
779               if (!s)
780                 {
781                   skipentry(&th);
782                   continue;
783                 }
784             }
785           else
786             s = pool_id2solvable(pool, repo_add_solvable(repo));
787         }
788       adddata(data, s, &th);
789     }
790   finishsolvable(repo, s);
791   solv_free(joinhash);
792   solv_free(lastdn);
793   if (!(flags & REPO_NO_INTERNALIZE))
794     repodata_internalize(data);
795   return 0;
796 }
797
798 int
799 repo_add_arch_local(Repo *repo, const char *dir, int flags)
800 {
801   Pool *pool = repo->pool;
802   Repodata *data;
803   DIR *dp;
804   struct dirent *de;
805   char *entrydir, *file;
806   FILE *fp;
807   Solvable *s;
808
809   data = repo_add_repodata(repo, flags);
810
811   dp = opendir(dir);
812   if (dp)
813     {
814       while ((de = readdir(dp)) != 0)
815         {
816           if (!de->d_name[0] || de->d_name[0] == '.')
817             continue;
818           entrydir = solv_dupjoin(dir, "/", de->d_name);
819           file = pool_tmpjoin(repo->pool, entrydir, "/desc", 0);
820           s = 0;
821           if ((fp = fopen(file, "r")) != 0)
822             {
823               struct tarhead th;
824               inittarhead(&th, fp);
825               s = pool_id2solvable(pool, repo_add_solvable(repo));
826               adddata(data, s, &th);
827               freetarhead(&th);
828               fclose(fp);
829               file = pool_tmpjoin(repo->pool, entrydir, "/files", 0);
830               if ((fp = fopen(file, "r")) != 0)
831                 {
832                   inittarhead(&th, fp);
833                   adddata(data, s, &th);
834                   freetarhead(&th);
835                   fclose(fp);
836                 }
837             }
838           solv_free(entrydir);
839         }
840       closedir(dp);
841     }
842   if (!(flags & REPO_NO_INTERNALIZE))
843     repodata_internalize(data);
844   return 0;
845 }
846