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