Imported vendor release 0.28.0-2.1
[services/perl-BSSolv-obs-2.7.git] / packaging / BSSolv.xs
1 #ifndef _GNU_SOURCE
2 #define _GNU_SOURCE
3 #endif
4
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8
9 #define MULTI_SEMANTICS
10
11 #include "pool.h"
12 #include "repo.h"
13 #include "util.h"
14 #include "evr.h"
15 #include "hash.h"
16 #include "chksum.h"
17 #include "repo_solv.h"
18 #include "repo_write.h"
19 #include "repo_rpmdb.h"
20 #include "repo_deb.h"
21 #if 1
22 #include "repo_arch.h"
23 #endif
24
25 typedef struct _Expander {
26   Pool *pool;
27
28   Map ignored;
29   Map ignoredx;
30
31   Queue preferposq;
32   Map preferpos;
33   Map preferposx;
34
35   Map preferneg;
36   Map prefernegx;
37
38   Queue conflictsq;
39   Map conflicts;
40
41   int debug;
42   int havefileprovides;
43   int ignoreconflicts;
44   int ignoreignore;
45
46   char *debugstr;
47   int debugstrl;
48   int debugstrf;
49 } Expander;
50
51 typedef Pool *BSSolv__pool;
52 typedef Repo *BSSolv__repo;
53 typedef Expander *BSSolv__expander;
54
55 static Id buildservice_id;
56 static Id buildservice_repocookie;
57 static Id buildservice_external;
58 static Id buildservice_dodurl;
59 static Id expander_directdepsend;
60 static Id buildservice_dodcookie;
61
62 /* make sure bit n is usable */
63 #define MAPEXP(m, n) ((m)->size < (((n) + 8) >> 3) ? map_grow(m, n + 256) : 0)
64
65 #define REPOCOOKIE "buildservice repo 1.1"
66
67 static int
68 myrepowritefilter(Repo *repo, Repokey *key, void *kfdata)
69 {
70   int i;
71   if (key->name == SOLVABLE_URL)
72     return KEY_STORAGE_DROPPED;
73   if (key->name == SOLVABLE_HEADEREND)
74     return KEY_STORAGE_DROPPED;
75   if (key->name == SOLVABLE_PACKAGER)
76     return KEY_STORAGE_DROPPED;
77   if (key->name == SOLVABLE_GROUP)
78     return KEY_STORAGE_DROPPED;
79   if (key->name == SOLVABLE_LICENSE)
80     return KEY_STORAGE_DROPPED;
81   if (key->name == SOLVABLE_PKGID)
82     return KEY_STORAGE_INCORE;
83   if (key->name == SOLVABLE_CHECKSUM)
84     return KEY_STORAGE_INCORE;
85   i = repo_write_stdkeyfilter(repo, key, kfdata);
86   if (i == KEY_STORAGE_VERTICAL_OFFSET)
87     return KEY_STORAGE_DROPPED;
88   return i;
89 }
90
91 static inline char *
92 hvlookupstr(HV *hv, const char *key, int keyl)
93 {
94   SV **svp = hv_fetch(hv, key, keyl, 0);
95   if (!svp)
96     return 0;
97   return SvPV_nolen(*svp);
98 }
99
100 static inline AV *
101 hvlookupav(HV *hv, const char *key, int keyl)
102 {
103   SV *sv, **svp = hv_fetch(hv, key, keyl, 0);
104   if (!svp)
105     return 0;
106   sv = *svp;
107   if (!sv || !SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVAV)
108     return 0;
109   return (AV *)SvRV(sv);
110 }
111
112 static Id
113 makeevr(Pool *pool, char *e, char *v, char *r)
114 {
115   char *s;
116
117   if (!v)
118     return 0;
119   if (e && !strcmp(e, "0"))
120     e = 0;
121   if (e)
122     s = pool_tmpjoin(pool, e, ":", v);
123   else
124     s = v;
125   if (r)
126     s = pool_tmpjoin(pool, s, "-", r);
127   return pool_str2id(pool, s, 1);
128 }
129
130 static inline char *
131 avlookupstr(AV *av, int n)
132 {
133   SV **svp = av_fetch(av, n, 0);
134   if (!svp)
135     return 0;
136   return SvPV_nolen(*svp);
137 }
138
139 static inline Id
140 id2name(Pool *pool, Id id)
141 {
142   while (ISRELDEP(id))
143     {
144       Reldep *rd = GETRELDEP(pool, id);
145       id = rd->name;
146     }
147   return id;
148 }
149
150 static Id
151 dep2id(Pool *pool, char *s)
152 {
153   char *n;
154   Id id;
155   int flags;
156
157   if ((n = strchr(s, '|')) != 0)
158     {
159       id = dep2id(pool, n + 1);
160       *n = 0;
161       id = pool_rel2id(pool, dep2id(pool, s), id, REL_OR, 1);
162       *n = '|';
163       return id;
164     }
165   while (*s == ' ' || *s == '\t')
166     s++;
167   n = s;
168   while (*s && *s != ' ' && *s != '\t' && *s != '<' && *s != '=' && *s != '>')
169     s++;
170 #ifdef REL_MULTIARCH
171   if (s - n > 4 && s[-4] == ':' && !strncmp(s - 4, ":any", 4))
172     {
173       id = pool_strn2id(pool, n, s - n - 4, 1);
174       id = pool_rel2id(pool, id, ARCH_ANY, REL_MULTIARCH, 1);
175     }
176   else
177 #endif
178     id = pool_strn2id(pool, n, s - n, 1);
179   if (!*s)
180     return id;
181   while (*s == ' ' || *s == '\t')
182     s++;
183   flags = 0;
184   for (;;s++)
185     {
186       if (*s == '<')
187         flags |= REL_LT;
188       else if (*s == '=')
189         flags |= REL_EQ;
190       else if (*s == '>')
191         flags |= REL_GT;
192       else
193         break;
194     }
195   if (!flags)
196     return id;
197   while (*s == ' ' || *s == '\t')
198     s++;
199   n = s;
200   while (*s && *s != ' ' && *s != '\t')
201     s++;
202   return pool_rel2id(pool, id, pool_strn2id(pool, n, s - n, 1), flags, 1);
203 }
204
205 static inline Offset
206 importdeps(HV *hv, const char *key, int keyl, Repo *repo)
207 {
208   Pool *pool = repo->pool;
209   int i;
210   AV *av = hvlookupav(hv, key, keyl);
211   Offset off = 0;
212   if (av)
213     {
214       for (i = 0; i <= av_len(av); i++)
215         {
216           char *str = avlookupstr(av, i);
217           if (str)
218             off = repo_addid_dep(repo, off, dep2id(pool, str), 0);
219         }
220     }
221   return off;
222 }
223
224 void
225 exportdeps(HV *hv, const char *key, int keyl, Repo *repo, Offset off, Id skey)
226 {
227   Pool *pool = repo->pool;
228   AV *av;
229   Id id, *pp;
230   const char *str;
231
232   if (!off || !repo->idarraydata[off])
233     return;
234   pp = repo->idarraydata + off;
235   av = 0;
236   while ((id = *pp++))
237     {
238       if (id == SOLVABLE_FILEMARKER)
239         break;
240       str = pool_dep2str(pool, id);
241       if (ISRELDEP(id))
242         {
243           Reldep *rd = GETRELDEP(pool, id);
244           if (skey == SOLVABLE_CONFLICTS && rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_OTHERPROVIDERS)
245             {
246             if (!strncmp(str, "namespace:", 10))
247               str += 10;
248             }
249           if (skey == SOLVABLE_SUPPLEMENTS)
250             {
251               if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_FILESYSTEM)
252                 {
253                   if (!strncmp(str, "namespace:", 10))
254                     str += 10;
255                 }
256               else if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_MODALIAS)
257                 {
258                   if (!strncmp(str, "namespace:", 10))
259                     str += 10;
260                 }
261               else if (rd->flags == REL_AND)
262                 {
263                   /* either packageand chain or modalias */
264                   str = 0;
265                   if (ISRELDEP(rd->evr))
266                     {
267                       Reldep *mrd = GETRELDEP(pool, rd->evr);
268                       if (mrd->flags == REL_NAMESPACE && mrd->name == NAMESPACE_MODALIAS)
269                         {
270                           str = pool_tmpjoin(pool, "modalias(", pool_dep2str(pool, rd->name), ":");
271                           str = pool_tmpappend(pool, str, pool_dep2str(pool, mrd->evr), ")");
272                         }
273                       else if (mrd->flags >= 8)
274                         continue;
275                     }
276                   if (!str)
277                     {
278                       /* must be and chain */
279                       str = pool_dep2str(pool, rd->evr);
280                       for (;;)
281                         {
282                           id = rd->name;
283                           if (!ISRELDEP(id))
284                             break;
285                           rd = GETRELDEP(pool, id);
286                           if (rd->flags != REL_AND)
287                             break;
288                           str = pool_tmpjoin(pool, pool_dep2str(pool, rd->evr), ":", str);
289                         }
290                       str = pool_tmpjoin(pool, pool_dep2str(pool, id), ":", str);
291                       str = pool_tmpjoin(pool, "packageand(", str, ")");
292                     }
293                 }
294               else if (rd->flags >= 8)
295                 continue;
296             }
297         }
298       if (skey == SOLVABLE_REQUIRES)
299         {
300           if (id == SOLVABLE_PREREQMARKER)
301             continue;
302           if (*str == 'r' && !strncmp(str, "rpmlib(", 7))
303             continue;
304         }
305       if (!av)
306         av = newAV();
307       av_push(av, newSVpv(str, 0));
308     }
309   if (av)
310     (void)hv_store(hv, key, keyl, newRV_noinc((SV*)av), 0);
311 }
312
313 void
314 data2solvables(Repo *repo, Repodata *data, HV *rhv)
315 {
316   Pool *pool = repo->pool;
317   SV *sv;
318   HV *hv;
319   char *str, *key;
320   I32 keyl;
321   Id p;
322   Solvable *s;
323
324   hv_iterinit(rhv);
325   while ((sv = hv_iternextsv(rhv, &key, &keyl)) != 0)
326     {
327       if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVHV)
328         continue;
329       hv = (HV *)SvRV(sv);
330       str = hvlookupstr(hv, "name", 4);
331       if (!str)
332         continue;       /* need to have a name */
333       p = repo_add_solvable(repo);
334       s = pool_id2solvable(pool, p);
335       s->name = pool_str2id(pool, str, 1);
336       str = hvlookupstr(hv, "arch", 4);
337       if (!str)
338         str = "";       /* dummy, need to have arch */
339       s->arch = pool_str2id(pool, str, 1);
340       s->evr = makeevr(pool, hvlookupstr(hv, "epoch", 5), hvlookupstr(hv, "version", 7), hvlookupstr(hv, "release", 7));
341       str = hvlookupstr(hv, "path", 4);
342       if (str)
343         {
344           char *ss = strrchr(str, '/');
345           if (ss)
346             {
347               *ss = 0;
348               repodata_set_str(data, p, SOLVABLE_MEDIADIR, str);
349               *ss++ = '/';
350             }
351           else
352             ss = str;
353           repodata_set_str(data, p, SOLVABLE_MEDIAFILE, ss);
354         }
355       str = hvlookupstr(hv, "id", 2);
356       if (str)
357         repodata_set_str(data, p, buildservice_id, str);
358       str = hvlookupstr(hv, "source", 6);
359       if (str)
360         repodata_set_poolstr(data, p, SOLVABLE_SOURCENAME, str);
361       str = hvlookupstr(hv, "hdrmd5", 6);
362       if (str && strlen(str) == 32)
363         repodata_set_checksum(data, p, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, str);
364       s->provides    = importdeps(hv, "provides", 8, repo);
365       s->obsoletes   = importdeps(hv, "obsoletes", 9, repo);
366       s->conflicts   = importdeps(hv, "conflicts", 9, repo);
367       s->requires    = importdeps(hv, "requires", 8, repo);
368       s->recommends  = importdeps(hv, "recommends", 10, repo);
369       s->suggests    = importdeps(hv, "suggests", 8, repo);
370       s->supplements = importdeps(hv, "supplements", 11, repo);
371       s->enhances    = importdeps(hv, "enhances", 8, repo);
372       if (!s->evr && s->provides)
373         {
374           /* look for self provides */
375           Id pro, *prop = s->repo->idarraydata + s->provides;
376           while ((pro = *prop++) != 0)
377             {
378               Reldep *rd;
379               if (!ISRELDEP(pro))
380                 continue;
381               rd = GETRELDEP(pool, pro);
382               if (rd->name == s->name && rd->flags == REL_EQ)
383                 s->evr = rd->evr;
384             }
385         }
386       if (s->evr)
387         s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
388       str = hvlookupstr(hv, "checksum", 8);
389       if (str)
390         {
391           char *cp, typebuf[8];
392           Id ctype;
393           if (*str != ':' && (cp = strchr(str, ':')) != 0 && cp - str < sizeof(typebuf))
394             {
395               strncpy(typebuf, str, cp - str);
396               typebuf[cp - str] = 0;
397               ctype = solv_chksum_str2type(typebuf);
398               if (ctype)
399                 repodata_set_checksum(data, p, SOLVABLE_CHECKSUM, ctype, cp + 1);
400             }
401         }
402     }
403
404   repodata_set_str(data, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
405   str = hvlookupstr(rhv, "/url", 4);
406   if (str)
407     repodata_set_str(data, SOLVID_META, buildservice_dodurl, str);
408   str = hvlookupstr(rhv, "/dodcookie", 10);
409   if (str)
410     repodata_set_str(data, SOLVID_META, buildservice_dodcookie, str);
411 }
412
413 static SV *
414 retrieve(unsigned char **srcp, STRLEN *srclp, int depth)
415 {
416   SV *sv, *rv;
417   AV *av;
418   HV *hv;
419   unsigned char *src = *srcp;
420   STRLEN srcl = *srclp;
421   int type;
422   unsigned int i, len;
423   STRLEN size;
424
425   if (depth > 10)
426     return 0;
427   if (srcl-- == 0)
428     return 0;
429   type = *src++;
430   switch (type)
431     {
432     case 1:
433       if (srcl < 4)
434         return 0;
435       size = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
436       srcl -= 4;
437       src += 4;
438       if (srcl < size)
439         return 0;
440       sv = NEWSV(10002, size);
441       sv_setpvn(sv, (char *)src, size);
442       srcl -= size;
443       src += size;
444       break;
445     case 2:
446       if (srcl < 4)
447         return 0;
448       len = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
449       srcl -= 4;
450       src += 4;
451       if (len > srcl)
452         return 0;
453       av = newAV();
454       if (len)
455         av_extend(av, len);
456       for (i = 0; i < len; i++)
457         {
458           sv = retrieve(&src, &srcl, depth + 1);
459           if (!sv)
460             return 0;
461           if (av_store(av, i, sv) == 0)
462             return 0;
463         }
464       sv = (SV *)av;
465       break;
466     case 3:
467       if (srcl < 4)
468         return 0;
469       len = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
470       srcl -= 4;
471       src += 4;
472       if (len > srcl)
473         return 0;
474       hv = newHV();
475       if (len)
476         hv_ksplit(hv, len + 1);
477       for (i = 0; i < len; i++)
478         {
479           sv = retrieve(&src, &srcl, depth + 1);
480           if (!sv) 
481             return 0;
482           if (srcl < 4)
483             return 0;
484           size = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
485           srcl -= 4;
486           src += 4;
487           if (srcl < size)
488             return 0;
489           if (hv_store(hv, (char *)src, (U32)size, sv, 0) == 0)
490             return 0;
491           srcl -= size;
492           src += size;
493         }
494       sv = (SV *)hv;
495       break;
496     case 4:
497       rv = NEWSV(10002, 0);
498       sv = retrieve(&src, &srcl, depth + 1);
499       if (!sv) 
500         return 0;
501       sv_upgrade(rv, SVt_RV);
502       SvRV_set(rv, sv);
503       SvROK_on(rv);
504       sv = rv;
505       break;
506     case 10:
507       if (srcl-- == 0)
508         return 0;
509       size = *src++;
510       if (srcl < size)
511         return 0;
512       sv = NEWSV(10002, size);
513       sv_setpvn(sv, (char *)src, size);
514       srcl -= size;
515       src += size;
516       break;
517     default:
518       /* fprintf(stderr, "unknown tag %d\n", type); */
519       return 0;
520     }
521   *srcp = src;
522   *srclp = srcl;
523   return sv;
524 }
525
526 static void
527 expander_dbg(Expander *xp, const char *format, ...)
528 {
529   va_list args;
530   char buf[1024];
531   int l;
532   if (!xp->debug)
533     return;
534   va_start(args, format);
535   vsnprintf(buf, sizeof(buf), format, args);
536   va_end(args);
537   printf("%s", buf);
538   l = strlen(buf);
539   if (buf[0] != ' ' || (l && buf[l - 1] == '\n'))
540     fflush(stdout);
541   if (l >= xp->debugstrf)       /* >= because of trailing \0 */
542     {
543       xp->debugstr = solv_realloc(xp->debugstr, xp->debugstrl + l + 1024);
544       xp->debugstrf = l + 1024;
545     }
546   strcpy(xp->debugstr + xp->debugstrl, buf);
547   xp->debugstrl += l;
548   xp->debugstrf -= l;
549 }
550
551 static const char *
552 expander_solvid2name(Expander *xp, Id p)
553 {
554   const char *n = pool_id2str(xp->pool, xp->pool->solvables[p].name);
555   Repo *r; 
556   if (!xp->debug)
557     return n;
558   r = xp->pool->solvables[p].repo;
559   if (!r) 
560     return n;
561   return pool_tmpjoin(xp->pool, n, "@", r->name);
562 }
563
564 static inline void
565 expander_installed(Expander *xp, Id p, Map *installed, Map *conflicts, Queue *conflictsinfo, int *cidone, Queue *out, Queue *todo)
566 {
567   Pool *pool = xp->pool;
568   Solvable *s = pool->solvables + p;
569   Id req, id, *reqp, con, *conp;
570   const char *n;
571
572   MAPSET(installed, p);
573   queue_push(out, p);
574   if (MAPTST(&xp->conflicts, s->name))
575     {
576       int i;
577       for (i = 0; i < xp->conflictsq.count; i++)
578         {
579           Id p2, pp2;
580           Id id = xp->conflictsq.elements[i];
581           if (id != s->name)
582             continue;
583           id = xp->conflictsq.elements[i ^ 1];
584           FOR_PROVIDES(p2, pp2, id)
585             {
586               if (pool->solvables[p2].name == id)
587                 {
588                   MAPEXP(conflicts, pool->nsolvables);
589                   MAPSET(conflicts, p2);
590                 }
591             }
592         }
593     }
594   if (s->requires)
595     {
596       reqp = s->repo->idarraydata + s->requires;
597       while ((req = *reqp++) != 0)
598         {
599           if (req == SOLVABLE_PREREQMARKER)
600             continue;
601           id = id2name(pool, req);
602           if (!xp->ignoreignore)
603             {
604               if (MAPTST(&xp->ignored, id))
605                 continue;
606               if (MAPTST(&xp->ignoredx, id))
607                 {
608                   Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, s->name), ":", pool_id2str(pool, id)), 0);
609                   if (xid && MAPTST(&xp->ignored, xid))
610                     continue;
611                 }
612             }
613           n = pool_id2str(pool, id);
614           if (!strncmp(n, "rpmlib(", 7))
615             {
616               MAPEXP(&xp->ignored, id);
617               MAPSET(&xp->ignored, id);
618               continue;
619             }
620           if (*n == '/')
621             {
622               if (!xp->havefileprovides || pool->whatprovides[id] <= 1)
623                 {
624                   MAPEXP(&xp->ignored, id);
625                   MAPSET(&xp->ignored, id);
626                   continue;
627                 }
628             }
629           queue_push2(todo, req, p);
630         }
631     }
632   if (!xp->ignoreconflicts)
633     {
634       if (s->conflicts)
635         {
636           conp = s->repo->idarraydata + s->conflicts;
637           while ((con = *conp++) != 0)
638             {
639               Id p2, pp2;
640               FOR_PROVIDES(p2, pp2, con)
641                 {
642                   if (p2 == p)
643                     continue;
644                   MAPEXP(conflicts, pool->nsolvables);
645                   MAPSET(conflicts, p2);
646                   if (xp->debug)
647                     queue_push2(conflictsinfo, p2, p);
648                 }
649             }
650         }
651       if (s->obsoletes)
652         {
653           conp = s->repo->idarraydata + s->obsoletes;
654           while ((con = *conp++) != 0)
655             {
656               Id p2, pp2;
657               FOR_PROVIDES(p2, pp2, con)
658                 {
659                   if (p2 == p || !pool_match_nevr(pool, pool->solvables + p2, con))
660                     continue;
661                   MAPEXP(conflicts, pool->nsolvables);
662                   MAPSET(conflicts, p2);
663                   if (xp->debug)
664                     queue_push2(conflictsinfo, p2, -p);
665                 }
666             }
667         }
668       if (xp->debug)
669         *cidone = out->count;
670     }
671 }
672
673 static inline int
674 expander_checkconflicts(Expander *xp, Id p, Map *installed, Id *conflicts, int isobsoletes)
675 {
676   Pool *pool = xp->pool;
677   Id con, p2, pp2;
678
679   if (xp->ignoreconflicts)
680     return 0;
681   while ((con = *conflicts++) != 0)
682     {
683       FOR_PROVIDES(p2, pp2, con)
684         {
685           if (p == p2)
686             continue;
687           if (isobsoletes && !pool_match_nevr(pool, pool->solvables + p2, con))
688             continue;
689           if (MAPTST(installed, p2))
690             return p2;
691         }
692     }
693   return 0;
694 }
695
696 static void
697 expander_updateconflictsinfo(Expander *xp, Queue *conflictsinfo, int *cidone, Queue *out)
698 {
699   Pool *pool = xp->pool;
700   int i;
701   if (xp->ignoreconflicts)
702     return;
703   for (i = *cidone; i < out->count; i++)
704     {
705       Id p, p2, pp2;
706       Id con, *conp;
707       Solvable *s;
708       p = out->elements[i];
709       s = pool->solvables + p;
710       /* keep in sync with expander_installed! */
711       if (s->conflicts)
712         {
713           conp = s->repo->idarraydata + s->conflicts;
714           while ((con = *conp++) != 0)
715             {
716               FOR_PROVIDES(p2, pp2, con)
717                 {
718                   if (p2 == p)
719                     continue;
720                   queue_push2(conflictsinfo, p2, p);
721                 }
722             }
723         }
724       if (s->obsoletes)
725         {
726           conp = s->repo->idarraydata + s->obsoletes;
727           while ((con = *conp++) != 0)
728             {
729               FOR_PROVIDES(p2, pp2, con)
730                 {
731                   if (p2 == p || !pool_match_nevr(pool, pool->solvables + p2, con))
732                     continue;
733                   queue_push2(conflictsinfo, p2, -p);
734                 }
735             }
736         }
737     }
738   *cidone = out->count;
739 }
740
741 static inline int
742 findconflictsinfo(Queue *conflictsinfo, Id p)
743 {
744   int i;
745
746   for (i = 0; i < conflictsinfo->count; i++)
747     if (conflictsinfo->elements[i] == p)
748       return conflictsinfo->elements[i + 1];
749   return 0;
750 }
751
752 #define ERROR_NOPROVIDER                1
753 #define ERROR_CHOICE                    2
754 #define ERROR_CONFLICTINGPROVIDER       3
755 #define ERROR_CONFLICTINGPROVIDERS      4
756 #define ERROR_PROVIDERINFO              5
757 #define ERROR_PROVIDERINFO2             6
758
759
760 int
761 expander_expand(Expander *xp, Queue *in, Queue *out, Queue *inconfl)
762 {
763   Pool *pool = xp->pool;
764   Queue todo, errors, cerrors, qq, posfoundq;
765   Map installed;
766   Map conflicts;
767   Queue conflictsinfo;
768   int cidone;
769   Solvable *s;
770   Id q, p, pp;
771   int i, j, nerrors, doamb, ambcnt;
772   Id id, who, whon, pn;
773   Id conflprov, conflprovpc;
774
775   map_init(&installed, pool->nsolvables);
776   map_init(&conflicts, 0);
777   queue_init(&conflictsinfo);
778   queue_init(&todo);
779   queue_init(&qq);
780   queue_init(&errors);
781   queue_init(&cerrors);
782   queue_init(&posfoundq);
783
784   queue_empty(out);
785   cidone = 0;
786
787   if (inconfl)
788     {
789       for (i = 0; i < inconfl->count; i += 2)
790         {
791           Id con = inconfl->elements[i];
792           FOR_PROVIDES(p, pp, con)
793             {
794               if (inconfl->elements[i + 1] && !pool_match_nevr(pool, pool->solvables + p, con))
795                 continue;
796               MAPEXP(&conflicts, pool->nsolvables);
797               MAPSET(&conflicts, p);
798             }
799         }
800     }
801   /* do direct expands */
802   for (i = 0; i < in->count; i++)
803     {
804       id = in->elements[i];
805       if (id == expander_directdepsend)
806         {
807           for (i = i + 1; i < in->count; i++)
808             if (in->elements[i] != expander_directdepsend)
809               queue_push2(&todo, in->elements[i], 0);
810           break;
811         }
812       q = 0;
813       FOR_PROVIDES(p, pp, id)
814         {
815           s = pool->solvables + p;
816           if (!pool_match_nevr(pool, s, id))
817             continue;
818           if (q)
819             {
820               q = 0;
821               break;
822             }
823           q = p;
824         }
825       if (!q)
826         {
827           /* unclear, resolve later */
828           queue_push2(&todo, id, 0);
829           continue;
830         }
831       if (MAPTST(&installed, q))
832         continue;
833       if (conflicts.size && MAPTST(&conflicts, q))
834         {
835           queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
836           queue_push2(&errors, id, 0);
837           if (cidone < out->count)
838             expander_updateconflictsinfo(xp, &conflictsinfo, &cidone, out);
839           queue_push(&errors, ERROR_PROVIDERINFO2);
840           queue_push2(&errors, q, findconflictsinfo(&conflictsinfo, q));
841           continue;
842         }
843       if (pool->solvables[q].conflicts && (pp = expander_checkconflicts(xp, q, &installed, pool->solvables[q].repo->idarraydata + pool->solvables[q].conflicts, 0)) != 0)
844         {
845           queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
846           queue_push2(&errors, id, 0);
847           queue_push(&errors, ERROR_PROVIDERINFO);
848           queue_push2(&errors, q, pp);
849           continue;
850         }
851       if (pool->solvables[q].obsoletes && (pp = expander_checkconflicts(xp, q, &installed, pool->solvables[q].repo->idarraydata + pool->solvables[q].obsoletes, 1)) != 0)
852         {
853           queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
854           queue_push2(&errors, id, 0);
855           queue_push(&errors, ERROR_PROVIDERINFO);
856           queue_push2(&errors, q, -pp);
857           continue;
858         }
859       if (xp->debug)
860         expander_dbg(xp, "added %s because of %s (direct dep)\n", expander_solvid2name(xp, q), pool_dep2str(pool, id));
861       expander_installed(xp, q, &installed, &conflicts, &conflictsinfo, &cidone, out, &todo); /* unique match! */
862     }
863
864   doamb = 0;
865   ambcnt = todo.count;
866   while (todo.count)
867     {
868       id = queue_shift(&todo);
869       who = queue_shift(&todo);
870       if (ambcnt == 0)
871         {
872           if (doamb)
873             break;      /* amb pass had no progress, stop */
874           if (xp->debug)
875             expander_dbg(xp, "now doing undecided dependencies\n");
876           doamb = 1;    /* start amb pass */
877           ambcnt = todo.count;
878         }
879       else
880         ambcnt -= 2;
881 // printf("todo %s %s ambcnt %d\n", pool_id2str(pool, pool->solvables[who].name), pool_dep2str(pool, id), ambcnt);
882 // fflush(stdout);
883       whon = who ? pool->solvables[who].name : 0;
884       queue_empty(&qq);
885       conflprov = 0;
886       conflprovpc = 0;
887       FOR_PROVIDES(p, pp, id)
888         {
889           Id pc;
890           if (MAPTST(&installed, p))
891             break;
892           if (who && !xp->ignoreignore)
893             {
894               Id pn = pool->solvables[p].name;
895               if (MAPTST(&xp->ignored, pn))
896                 break;
897               if (MAPTST(&xp->ignoredx, pn))
898                 {
899                   Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
900                   if (xid && MAPTST(&xp->ignored, xid))
901                     break;
902                 }
903             }
904           if (conflicts.size && MAPTST(&conflicts, p))
905             {
906               if (xp->debug)
907                 {
908                   Id pc = findconflictsinfo(&conflictsinfo, p);
909                   if (pc)
910                     expander_dbg(xp, "ignoring provider %s of %s because installed %s %s it\n", pool_solvid2str(pool, p), pool_dep2str(pool, id), pool_solvid2str(pool, pc > 0 ? pc : -pc), pc > 0 ? "conflicts with" : "obsoletes");
911                   else
912                     expander_dbg(xp, "ignoring conflicted provider %s of %s\n", pool_solvid2str(pool, p), pool_dep2str(pool, id));
913                 }
914               conflprov = conflprov ? 1 : p;
915               conflprovpc = 0;
916               continue;
917             }
918           if (pool->solvables[p].conflicts && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].conflicts, 0)) != 0)
919             {
920               expander_dbg(xp, "ignoring provider %s of %s because it conflicts with installed %s\n", pool_solvid2str(pool, p), pool_dep2str(pool, id), pool_solvid2str(pool, pc));
921               conflprov = conflprov ? 1 : p;
922               conflprovpc = pc;
923               continue;
924             }
925           if (pool->solvables[p].obsoletes && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].obsoletes, 1)) != 0)
926             {
927               expander_dbg(xp, "ignoring provider %s of %s because it obsoletes installed %s\n", pool_solvid2str(pool, p), pool_dep2str(pool, id), pool_solvid2str(pool, pc));
928               conflprov = conflprov ? 1 : p;
929               conflprovpc = -pc;
930               continue;
931             }
932           queue_push(&qq, p);
933         }
934       if (p)
935         continue;
936       if (qq.count == 0)
937         {
938           if (!conflprov)
939             {
940               queue_push(&errors, ERROR_NOPROVIDER);
941               queue_push2(&errors, id, who);
942               continue;
943             }
944           /* more work for conflicts */
945           if (conflprov != 1)
946             {
947               /* nice, just one provider */
948               queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
949               queue_push2(&errors, id, who);
950               if (!conflprovpc)
951                 {
952                   if (cidone < out->count)
953                     expander_updateconflictsinfo(xp, &conflictsinfo, &cidone, out);
954                   conflprovpc = findconflictsinfo(&conflictsinfo, conflprov);
955                   queue_push(&errors, ERROR_PROVIDERINFO2);
956                   queue_push2(&errors, conflprov, conflprovpc);
957                 }
958               else
959                 {
960                   queue_push(&errors, ERROR_PROVIDERINFO);
961                   queue_push2(&errors, conflprov, conflprovpc);
962                 }
963               continue;
964             }
965           /* even more work if all providers conflict */
966           queue_push(&errors, ERROR_CONFLICTINGPROVIDERS);
967           queue_push2(&errors, id, who);
968           if (cidone < out->count)
969             expander_updateconflictsinfo(xp, &conflictsinfo, &cidone, out);
970           FOR_PROVIDES(p, pp, id)
971             {
972               Id pc;
973               if (conflicts.size && MAPTST(&conflicts, p))
974                 {
975                   pc = findconflictsinfo(&conflictsinfo, p);
976                   queue_push(&errors, ERROR_PROVIDERINFO2);
977                   queue_push2(&errors, p, pc);
978                   continue;
979                 }
980               if (pool->solvables[p].conflicts && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].conflicts, 0)) != 0)
981                 {
982                   queue_push(&errors, ERROR_PROVIDERINFO);
983                   queue_push2(&errors, p, pc);
984                   continue;
985                 }
986               if (pool->solvables[p].obsoletes && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].obsoletes, 1)) != 0)
987                 {
988                   queue_push(&errors, ERROR_PROVIDERINFO);
989                   queue_push2(&errors, p, -pc);
990                   continue;
991                 }
992             }
993           continue;
994         }
995       if (qq.count > 1 && !doamb)
996         {
997           /* try again later */
998           queue_push2(&todo, id, who);
999           if (xp->debug)
1000             {
1001               expander_dbg(xp, "undecided about %s:%s:", whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
1002               for (i = 0; i < qq.count; i++)
1003                 expander_dbg(xp, " %s", expander_solvid2name(xp, qq.elements[i]));
1004               expander_dbg(xp, "\n");
1005             }
1006           continue;
1007         }
1008
1009       /* prune neg prefers */
1010       if (qq.count > 1)
1011         {
1012           for (i = j = 0; i < qq.count; i++)
1013             {
1014               p = qq.elements[i];
1015               pn = pool->solvables[p].name;
1016               if (MAPTST(&xp->preferneg, pn))
1017                 continue;
1018               if (who && MAPTST(&xp->prefernegx, pn))
1019                 {
1020                   Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
1021                   if (xid && MAPTST(&xp->preferneg, xid))
1022                     continue;
1023                 }
1024               qq.elements[j++] = p;
1025             }
1026           if (j)
1027             queue_truncate(&qq, j);
1028         }
1029
1030       /* prune pos prefers */
1031       if (qq.count > 1)
1032         {
1033           queue_empty(&posfoundq);
1034           for (i = j = 0; i < qq.count; i++)
1035             {
1036               p = qq.elements[i];
1037               pn = pool->solvables[p].name;
1038               if (MAPTST(&xp->preferpos, pn))
1039                 {
1040                   queue_push2(&posfoundq, pn, p);
1041                   qq.elements[j++] = p;
1042                   continue;
1043                 }
1044               if (who && MAPTST(&xp->preferposx, pn))
1045                 {
1046                   Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
1047                   if (xid && MAPTST(&xp->preferpos, xid))
1048                     {
1049                       queue_push2(&posfoundq, xid, p);
1050                       qq.elements[j++] = p;
1051                       continue;
1052                     }
1053                 }
1054             }
1055           if (posfoundq.count == 2)
1056             {
1057               queue_empty(&qq);
1058               queue_push(&qq, posfoundq.elements[1]);
1059             }
1060           else if (posfoundq.count)
1061             {
1062               /* found a pos prefer, now find first hit */
1063               /* (prefers are ordered) */
1064               for (i = 0; i < xp->preferposq.count; i++)
1065                 {
1066                   Id xid = xp->preferposq.elements[i];
1067                   for (j = 0; j < posfoundq.count; j += 2)
1068                     if (posfoundq.elements[j] == xid)
1069                       break;
1070                   if (j < posfoundq.count)
1071                     {
1072                       queue_empty(&qq);
1073                       queue_push(&qq, posfoundq.elements[j + 1]);
1074                       break;
1075                     }
1076                 }
1077             }
1078         }
1079
1080       /* prune OR deps */
1081       if (qq.count > 1 && ISRELDEP(id) && GETRELDEP(pool, id)->flags == REL_OR)
1082         {
1083           Id rid = id;
1084           for (;;)
1085             {
1086               Reldep *rd = 0;
1087               if (ISRELDEP(rid))
1088                 {
1089                   rd = GETRELDEP(pool, rid);
1090                   if (rd->flags != REL_OR)
1091                     rd = 0;
1092                 }
1093               if (rd)
1094                 rid = rd->name;
1095               queue_empty(&qq);
1096               FOR_PROVIDES(p, pp, rid)
1097                 queue_push(&qq, p);
1098               if (qq.count)
1099                 break;
1100               if (rd)
1101                 rid = rd->evr;
1102               else
1103                 break;
1104             }
1105         }
1106       if (qq.count > 1)
1107         {
1108           queue_push(&cerrors, ERROR_CHOICE);
1109           queue_push2(&cerrors, id, who);
1110           for (i = 0; i < qq.count; i++)
1111             queue_push(&cerrors, qq.elements[i]);
1112           queue_push(&cerrors, 0);
1113           /* try again later */
1114           queue_push2(&todo, id, who);
1115           continue;
1116         }
1117       if (xp->debug)
1118         expander_dbg(xp, "added %s because of %s:%s\n", expander_solvid2name(xp, qq.elements[0]), whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
1119       expander_installed(xp, qq.elements[0], &installed, &conflicts, &conflictsinfo, &cidone, out, &todo);
1120       doamb = 0;
1121       ambcnt = todo.count;
1122       queue_empty(&cerrors);
1123     }
1124   map_free(&installed);
1125   map_free(&conflicts);
1126   queue_free(&conflictsinfo);
1127   nerrors = 0;
1128   if (errors.count || cerrors.count)
1129     {
1130       queue_empty(out);
1131       for (i = 0; i < errors.count; i += 3)
1132         {
1133           queue_push(out, errors.elements[i]);
1134           queue_push(out, errors.elements[i + 1]);
1135           queue_push(out, errors.elements[i + 2]);
1136           nerrors++;
1137         }
1138       for (i = 0; i < cerrors.count; )
1139         {
1140           queue_push(out, cerrors.elements[i]);
1141           queue_push(out, cerrors.elements[i + 1]);
1142           queue_push(out, cerrors.elements[i + 2]);
1143           i += 3;
1144           while (cerrors.elements[i])
1145             {
1146               queue_push(out, cerrors.elements[i]);
1147               i++;
1148             }
1149           queue_push(out, 0);
1150           i++;
1151           nerrors++;
1152         }
1153     }
1154   else
1155     {
1156       if (todo.count)
1157         {
1158           fprintf(stderr, "Internal expansion error!\n");
1159           queue_empty(out);
1160           queue_push(out, ERROR_NOPROVIDER);
1161           queue_push(out, 0);
1162           queue_push(out, 0);
1163         }
1164     }
1165   queue_free(&todo);
1166   queue_free(&qq);
1167   queue_free(&errors);
1168   queue_free(&cerrors);
1169   queue_free(&posfoundq);
1170   return nerrors;
1171 }
1172
1173 static void
1174 set_disttype(Pool *pool, int disttype)
1175 {
1176   pool_setdisttype(pool, disttype);
1177 #ifdef POOL_FLAG_HAVEDISTEPOCH
1178   /* make newer mandriva work, hopefully there are no ill effects... */
1179   pool_set_flag(pool, POOL_FLAG_HAVEDISTEPOCH, disttype == DISTTYPE_RPM ? 1 : 0);
1180 #endif
1181 }
1182
1183 static void
1184 set_disttype_from_location(Pool *pool, Solvable *so)
1185 {
1186   unsigned int medianr;
1187   const char *s = solvable_get_location(so, &medianr);
1188   int disttype = -1;
1189   int sl;
1190   if (!s)
1191     return;
1192   sl = strlen(s);
1193   if (disttype < 0 && sl >= 4 && !strcmp(s + sl - 4, ".rpm"))
1194     disttype = DISTTYPE_RPM;
1195 #ifdef DISTTYPE_DEB
1196   if (disttype < 0 && sl >= 4 && !strcmp(s + sl - 4, ".deb"))
1197     disttype = DISTTYPE_DEB;
1198 #endif
1199 #ifdef DISTTYPE_ARCH
1200   if (disttype < 0 && sl >= 11 && (!strcmp(s + sl - 11, ".pkg.tar.gz") || !strcmp(s + sl - 11, ".pkg.tar.xz")))
1201     disttype = DISTTYPE_ARCH;
1202 #endif
1203   if (disttype >= 0 && pool->disttype != disttype)
1204     set_disttype(pool, disttype);
1205 }
1206
1207 void
1208 create_considered(Pool *pool, Repo *repoonly, Map *considered)
1209 {
1210   Id p, pb,*best;
1211   Solvable *s, *sb;
1212   int ridx;
1213   Repo *repo;
1214   int olddisttype = -1;
1215   int dodrepo;
1216
1217   map_init(considered, pool->nsolvables);
1218   best = solv_calloc(sizeof(Id), pool->ss.nstrings);
1219   
1220   FOR_REPOS(ridx, repo)
1221     {
1222       if (repoonly && repo != repoonly)
1223         continue;
1224       dodrepo = repo_lookup_str(repo, SOLVID_META, buildservice_dodurl) != 0;
1225       FOR_REPO_SOLVABLES(repo, p, s)
1226         {
1227           if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
1228             continue;
1229           pb = best[s->name];
1230           if (pb)
1231             {
1232               sb = pool->solvables + pb;
1233               if (s->repo != sb->repo)
1234                 continue;       /* first repo wins */
1235               if (s->arch != sb->arch)
1236                 {
1237                   int r;
1238                   if (s->arch == ARCH_NOARCH || s->arch == ARCH_ALL || s->arch == ARCH_ANY)
1239                     continue;
1240                   if (sb->arch != ARCH_NOARCH && sb->arch != ARCH_ALL && sb->arch != ARCH_ANY)
1241                     {
1242                       /* the strcmp is kind of silly, but works for most archs */
1243                       r = strcmp(pool_id2str(pool, sb->arch), pool_id2str(pool, s->arch));
1244                       if (r >= 0)
1245                         continue;
1246                     }
1247                 }
1248               else if (s->evr != sb->evr)
1249                 {
1250                   /* same repo, check versions */
1251                   int r;
1252                   if (olddisttype < 0)
1253                     {
1254                       olddisttype = pool->disttype;
1255                       set_disttype_from_location(pool, s);
1256                     }
1257                   r = pool_evrcmp(pool, sb->evr, s->evr, EVRCMP_COMPARE);
1258                   if (r > 0)
1259                     continue;
1260                   else if (r == 0)
1261                     {
1262                       r = strcmp(pool_id2str(pool, sb->evr), pool_id2str(pool, s->evr));
1263                       if (r >= 0)
1264                         continue;
1265                     }
1266                 }
1267             }
1268            if (dodrepo)
1269             {
1270               /* we only consider dod packages */
1271               const char *bsid = solvable_lookup_str(s, buildservice_id);
1272               if (!bsid || strcmp(bsid, "dod") != 0)
1273                 continue;
1274             }
1275           if (pb)
1276             MAPCLR(considered, pb);
1277           best[s->name] = p;
1278           MAPSET(considered, p);
1279         }
1280       /* dodrepos have a second pass: replace dod entries with downloaded ones */
1281       if (dodrepo)
1282         {
1283           const char *bsid;
1284           FOR_REPO_SOLVABLES(repo, p, s)
1285             {
1286               if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
1287                 continue;
1288               pb = best[s->name];
1289               if (!pb || pb == p)
1290                 continue;
1291               sb = pool->solvables + pb;
1292               if (sb->repo != s->repo || sb->name != s->name || sb->arch != s->arch || sb->evr != s->evr)
1293                 continue;
1294               bsid = solvable_lookup_str(s, buildservice_id);
1295               if (bsid && strcmp(bsid, "dod") == 0)
1296                 continue;       /* not downloaded */
1297               MAPCLR(considered, pb);
1298               best[s->name] = p;
1299               MAPSET(considered, p);
1300             }
1301         }
1302     }
1303   solv_free(best);
1304   if (olddisttype >= 0 && pool->disttype != olddisttype)
1305     set_disttype(pool, olddisttype);
1306 }
1307
1308 struct metaline {
1309   char *l;
1310   int lastoff;
1311   int nslash;
1312   int killed;
1313 };
1314
1315 static int metacmp(const void *ap, const void *bp)
1316 {
1317   const struct metaline *a, *b;
1318   int r;
1319
1320   a = ap;
1321   b = bp;
1322   r = a->nslash - b->nslash;
1323   if (r)
1324     return r;
1325   r = strcmp(a->l + 34, b->l + 34);
1326   if (r)
1327     return r;
1328   r = strcmp(a->l, b->l);
1329   if (r)
1330     return r;
1331   return a - b;
1332 }
1333
1334 #ifndef REPO_NO_LOCATION
1335 # define REPO_NO_LOCATION 0
1336 #endif
1337
1338 Id
1339 repodata_addbin(Repodata *data, char *prefix, char *s, int sl, char *sid)
1340 {
1341   Id p = 0;
1342   char *path;
1343 #if REPO_NO_LOCATION == 0
1344   char *sp;
1345 #endif
1346
1347   path = solv_dupjoin(prefix, "/", s);
1348   if (sl >= 4 && !strcmp(s + sl - 4, ".rpm"))
1349     p = repo_add_rpm(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|RPM_ADD_WITH_PKGID|RPM_ADD_NO_FILELIST|RPM_ADD_NO_RPMLIBREQS);
1350   else if (sl >= 4 && !strcmp(s + sl - 4, ".deb"))
1351     p = repo_add_deb(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|DEBS_ADD_WITH_PKGID);
1352 #ifdef ARCH_ADD_WITH_PKGID
1353   else if (sl >= 11 && (!strcmp(s + sl - 11, ".pkg.tar.gz") || !strcmp(s + sl - 11, ".pkg.tar.xz")))
1354     p = repo_add_arch_pkg(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|ARCH_ADD_WITH_PKGID);
1355 #endif
1356   solv_free(path);
1357   if (!p)
1358     return 0;
1359 #if REPO_NO_LOCATION != 0
1360   repodata_set_location(data, p, 0, 0, s);
1361 #else
1362   if ((sp = strrchr(s, '/')) != 0)
1363     {
1364       *sp = 0;
1365       repodata_set_str(data, p, SOLVABLE_MEDIADIR, s);
1366       *sp = '/';
1367     }
1368   else
1369     repodata_delete_uninternalized(data, p, SOLVABLE_MEDIADIR);
1370 #endif
1371   repodata_set_str(data, p, buildservice_id, sid);
1372   return p;
1373 }
1374
1375 static int
1376 subpack_sort_cmp(const void *ap, const void *bp, void *dp)
1377 {
1378   Pool *pool = (Pool *)dp;
1379   const Id *a = ap;
1380   const Id *b = bp;
1381   int r = a[1] - b[1];
1382   if (r)
1383     return r;
1384   r = strcmp(pool_id2str(pool, a[0]), pool_id2str(pool, b[0]));
1385   return r ? r : a[0] - b[0];
1386 }
1387
1388 /* This is an OpenSSL-compatible implementation of the RSA Data Security,
1389  * Inc. MD5 Message-Digest Algorithm.
1390  *
1391  * Written by Solar Designer <solar@openwall.com> in 2001, and placed in
1392  * the public domain. */
1393
1394 #define F(x, y, z)                      ((z) ^ ((x) & ((y) ^ (z))))
1395 #define G(x, y, z)                      ((y) ^ ((z) & ((x) ^ (y))))
1396 #define H(x, y, z)                      ((x) ^ (y) ^ (z))
1397 #define I(x, y, z)                      ((y) ^ ((x) | ~(z)))
1398
1399 #define STEP(f, a, b, c, d, x, t, s) \
1400         (a) += f((b), (c), (d)) + (x) + (t); \
1401         (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
1402         (a) += (b);
1403
1404 #if defined(__i386__) || defined(__vax__)
1405 #define SET(n) \
1406         (*(MD5_u32plus *)&ptr[(n) * 4])
1407 #define GET(n) \
1408         SET(n)
1409 #else
1410 #define SET(n) \
1411         (ctx->block[(n)] = \
1412         (MD5_u32plus)ptr[(n) * 4] | \
1413         ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
1414         ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
1415         ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
1416 #define GET(n) \
1417         (ctx->block[(n)])
1418 #endif
1419
1420 typedef unsigned long MD5_u32plus;
1421
1422 typedef struct {
1423         MD5_u32plus lo, hi;
1424         MD5_u32plus a, b, c, d;
1425         unsigned char buffer[64];
1426         MD5_u32plus block[16];
1427 } MD5_CTX;
1428
1429 /*
1430  * This processes one or more 64-byte data blocks, but does NOT update
1431  * the bit counters.  There're no alignment requirements.
1432  */
1433 static void *md5_body(MD5_CTX *ctx, void *data, unsigned long size)
1434 {
1435         unsigned char *ptr;
1436         MD5_u32plus a, b, c, d;
1437         MD5_u32plus saved_a, saved_b, saved_c, saved_d;
1438
1439         ptr = data;
1440
1441         a = ctx->a;
1442         b = ctx->b;
1443         c = ctx->c;
1444         d = ctx->d;
1445
1446         do {
1447                 saved_a = a;
1448                 saved_b = b;
1449                 saved_c = c;
1450                 saved_d = d;
1451
1452 /* Round 1 */
1453                 STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
1454                 STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
1455                 STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
1456                 STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
1457                 STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
1458                 STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
1459                 STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
1460                 STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
1461                 STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
1462                 STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
1463                 STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
1464                 STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
1465                 STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
1466                 STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
1467                 STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
1468                 STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
1469
1470 /* Round 2 */
1471                 STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
1472                 STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
1473                 STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
1474                 STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
1475                 STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
1476                 STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
1477                 STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
1478                 STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
1479                 STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
1480                 STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
1481                 STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
1482                 STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
1483                 STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
1484                 STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
1485                 STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
1486                 STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
1487
1488 /* Round 3 */
1489                 STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
1490                 STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
1491                 STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
1492                 STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
1493                 STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
1494                 STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
1495                 STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
1496                 STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
1497                 STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
1498                 STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
1499                 STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
1500                 STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
1501                 STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
1502                 STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
1503                 STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
1504                 STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
1505
1506 /* Round 4 */
1507                 STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
1508                 STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
1509                 STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
1510                 STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
1511                 STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
1512                 STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
1513                 STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
1514                 STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
1515                 STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
1516                 STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
1517                 STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
1518                 STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
1519                 STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
1520                 STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
1521                 STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
1522                 STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
1523
1524                 a += saved_a;
1525                 b += saved_b;
1526                 c += saved_c;
1527                 d += saved_d;
1528
1529                 ptr += 64;
1530         } while (size -= 64);
1531
1532         ctx->a = a;
1533         ctx->b = b;
1534         ctx->c = c;
1535         ctx->d = d;
1536
1537         return ptr;
1538 }
1539
1540 static void md5_init(MD5_CTX *ctx)
1541 {
1542         ctx->a = 0x67452301;
1543         ctx->b = 0xefcdab89;
1544         ctx->c = 0x98badcfe;
1545         ctx->d = 0x10325476;
1546         ctx->lo = 0;
1547         ctx->hi = 0;
1548 }
1549
1550 static void md5_update(MD5_CTX *ctx, void *data, unsigned long size)
1551 {
1552         MD5_u32plus saved_lo;
1553         unsigned long used, free;
1554
1555         saved_lo = ctx->lo;
1556         if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
1557                 ctx->hi++;
1558         ctx->hi += size >> 29;
1559
1560         used = saved_lo & 0x3f;
1561
1562         if (used) {
1563                 free = 64 - used;
1564
1565                 if (size < free) {
1566                         memcpy(&ctx->buffer[used], data, size);
1567                         return;
1568                 }
1569
1570                 memcpy(&ctx->buffer[used], data, free);
1571                 data = (unsigned char *)data + free;
1572                 size -= free;
1573                 md5_body(ctx, ctx->buffer, 64);
1574         }
1575
1576         if (size >= 64) {
1577                 data = md5_body(ctx, data, size & ~(unsigned long)0x3f);
1578                 size &= 0x3f;
1579         }
1580
1581         memcpy(ctx->buffer, data, size);
1582 }
1583
1584 static void md5_final(MD5_CTX *ctx, unsigned char *result)
1585 {
1586         unsigned long used, free;
1587         used = ctx->lo & 0x3f;
1588         ctx->buffer[used++] = 0x80;
1589         free = 64 - used;
1590         if (free < 8) {
1591                 memset(&ctx->buffer[used], 0, free);
1592                 md5_body(ctx, ctx->buffer, 64);
1593                 used = 0;
1594                 free = 64;
1595         }
1596         memset(&ctx->buffer[used], 0, free - 8);
1597         ctx->lo <<= 3;
1598         ctx->buffer[56] = ctx->lo;
1599         ctx->buffer[57] = ctx->lo >> 8;
1600         ctx->buffer[58] = ctx->lo >> 16;
1601         ctx->buffer[59] = ctx->lo >> 24;
1602         ctx->buffer[60] = ctx->hi;
1603         ctx->buffer[61] = ctx->hi >> 8;
1604         ctx->buffer[62] = ctx->hi >> 16;
1605         ctx->buffer[63] = ctx->hi >> 24;
1606         md5_body(ctx, ctx->buffer, 64);
1607         result[0] = ctx->a;
1608         result[1] = ctx->a >> 8;
1609         result[2] = ctx->a >> 16;
1610         result[3] = ctx->a >> 24;
1611         result[4] = ctx->b;
1612         result[5] = ctx->b >> 8;
1613         result[6] = ctx->b >> 16;
1614         result[7] = ctx->b >> 24;
1615         result[8] = ctx->c;
1616         result[9] = ctx->c >> 8;
1617         result[10] = ctx->c >> 16;
1618         result[11] = ctx->c >> 24;
1619         result[12] = ctx->d;
1620         result[13] = ctx->d >> 8;
1621         result[14] = ctx->d >> 16;
1622         result[15] = ctx->d >> 24;
1623         memset(ctx, 0, sizeof(*ctx));
1624 }
1625
1626 static unsigned int buz_noise[256] =
1627 {
1628   0x9be502a4U, 0xba7180eaU, 0x324e474fU, 0x0aab8451U, 0x0ced3810U,
1629   0x2158a968U, 0x6bbd3771U, 0x75a02529U, 0x41f05c14U, 0xc2264b87U,
1630   0x1f67b359U, 0xcd2d031dU, 0x49dc0c04U, 0xa04ae45cU, 0x6ade28a7U,
1631   0x2d0254ffU, 0xdec60c7cU, 0xdef5c084U, 0x0f77ffc8U, 0x112021f6U,
1632   0x5f6d581eU, 0xe35ea3dfU, 0x3216bfb4U, 0xd5a3083dU, 0x7e63e9cdU,
1633   0xaa9208f6U, 0xda3f3978U, 0xfe0e2547U, 0x09dfb020U, 0xd97472c5U,
1634   0xbbce2edeU, 0x121aebd2U, 0x0e9fdbebU, 0x7b6f5d9cU, 0x84938e43U,
1635   0x30694f2dU, 0x86b7a7f8U, 0xefaf5876U, 0x263812e6U, 0xb6e48ddfU,
1636   0xce8ed980U, 0x4df591e1U, 0x75257b35U, 0x2f88dcffU, 0xa461fe44U,
1637   0xca613b4dU, 0xd9803f73U, 0xea056205U, 0xccca7a89U, 0x0f2dbb07U,
1638   0xc53e359eU, 0xe80d0137U, 0x2b2d2a5dU, 0xcfc1391aU, 0x2bb3b6c5U,
1639   0xb66aea3cU, 0x00ea419eU, 0xce5ada84U, 0xae1d6712U, 0x12f576baU,
1640   0x117fcbc4U, 0xa9d4c775U, 0x25b3d616U, 0xefda65a8U, 0xaff3ef5bU,
1641   0x00627e68U, 0x668d1e99U, 0x088d0eefU, 0xf8fac24dU, 0xe77457c7U,
1642   0x68d3beb4U, 0x921d2acbU, 0x9410eac9U, 0xd7f24399U, 0xcbdec497U,
1643   0x98c99ae1U, 0x65802b2cU, 0x81e1c3c4U, 0xa130bb09U, 0x17a87badU,
1644   0xa70367d6U, 0x148658d4U, 0x02f33377U, 0x8620d8b6U, 0xbdac25bdU,
1645   0xb0a6de51U, 0xd64c4571U, 0xa4185ba0U, 0xa342d70fU, 0x3f1dc4c1U,
1646   0x042dc3ceU, 0x0de89f43U, 0xa69b1867U, 0x3c064e11U, 0xad1e2c3eU,
1647   0x9660e8cdU, 0xd36b09caU, 0x4888f228U, 0x61a9ac3cU, 0xd9561118U,
1648   0x3532797eU, 0x71a35c22U, 0xecc1376cU, 0xab31e656U, 0x88bd0d35U,
1649   0x423b20ddU, 0x38e4651cU, 0x3c6397a4U, 0x4a7b12d9U, 0x08b1cf33U,
1650   0xd0604137U, 0xb035fdb8U, 0x4916da23U, 0xa9349493U, 0xd83daa9bU,
1651   0x145f7d95U, 0x868531d6U, 0xacb18f17U, 0x9cd33b6fU, 0x193e42b9U,
1652   0x26dfdc42U, 0x5069d8faU, 0x5bee24eeU, 0x5475d4c6U, 0x315b2c0cU,
1653   0xf764ef45U, 0x01b6f4ebU, 0x60ba3225U, 0x8a16777cU, 0x4c05cd28U,
1654   0x53e8c1d2U, 0xc8a76ce5U, 0x8045c1e6U, 0x61328752U, 0x2ebad322U,
1655   0x3444f3e2U, 0x91b8af11U, 0xb0cee675U, 0x55dbff5aU, 0xf7061ee0U,
1656   0x27d7d639U, 0xa4aef8c9U, 0x42ff0e4fU, 0x62755468U, 0x1c6ca3f3U,
1657   0xe4f522d1U, 0x2765fcb3U, 0xe20c8a95U, 0x3a69aea7U, 0x56ab2c4fU,
1658   0x8551e688U, 0xe0bc14c2U, 0x278676bfU, 0x893b6102U, 0xb4f0ab3bU,
1659   0xb55ddda9U, 0xa04c521fU, 0xc980088eU, 0x912aeac1U, 0x08519badU,
1660   0x991302d3U, 0x5b91a25bU, 0x696d9854U, 0x9ad8b4bfU, 0x41cb7e21U,
1661   0xa65d1e03U, 0x85791d29U, 0x89478aa7U, 0x4581e337U, 0x59bae0b1U,
1662   0xe0fc9df3U, 0x45d9002cU, 0x7837464fU, 0xda22de3aU, 0x1dc544bdU,
1663   0x601d8badU, 0x668b0abcU, 0x7a5ebfb1U, 0x3ac0b624U, 0x5ee16d7dU,
1664   0x9bfac387U, 0xbe8ef20cU, 0x8d2ae384U, 0x819dc7d5U, 0x7c4951e7U,
1665   0xe60da716U, 0x0c5b0073U, 0xb43b3d97U, 0xce9974edU, 0x0f691da9U,
1666   0x4b616d60U, 0x8fa9e819U, 0x3f390333U, 0x6f62fad6U, 0x5a32b67cU,
1667   0x3be6f1c3U, 0x05851103U, 0xff28828dU, 0xaa43a56aU, 0x075d7dd5U,
1668   0x248c4b7eU, 0x52fde3ebU, 0xf72e2edaU, 0x5da6f75fU, 0x2f5148d9U,
1669   0xcae2aeaeU, 0xfda6f3e5U, 0xff60d8ffU, 0x2adc02d2U, 0x1dbdbd4cU,
1670   0xd410ad7cU, 0x8c284aaeU, 0x392ef8e0U, 0x37d48b3aU, 0x6792fe9dU,
1671   0xad32ddfaU, 0x1545f24eU, 0x3a260f73U, 0xb724ca36U, 0xc510d751U,
1672   0x4f8df992U, 0x000b8b37U, 0x292e9b3dU, 0xa32f250fU, 0x8263d144U,
1673   0xfcae0516U, 0x1eae2183U, 0xd4af2027U, 0xc64afae3U, 0xe7b34fe4U,
1674   0xdf864aeaU, 0x80cc71c5U, 0x0e814df3U, 0x66cc5f41U, 0x853a497aU,
1675   0xa2886213U, 0x5e34a2eaU, 0x0f53ba47U, 0x718c484aU, 0xfa0f0b12U,
1676   0x33cc59ffU, 0x72b48e07U, 0x8b6f57bcU, 0x29cf886dU, 0x1950955bU,
1677   0xcd52910cU, 0x4cecef65U, 0x05c2cbfeU, 0x49df4f6aU, 0x1f4c3f34U,
1678   0xfadc1a09U, 0xf2d65a24U, 0x117f5594U, 0xde3a84e6U, 0x48db3024U,
1679   0xd10ca9b5U
1680 };
1681
1682
1683 /* 
1684  * our delta search blocksize
1685  *
1686  * smaller gives more hits, but increases the hash size
1687  *
1688  * must be a multiple of 256
1689  * must be in range [256,32767]
1690  */
1691 #define DELTA_BSIZE 1024
1692
1693 /* min store block len, smaller blocks are encoded as direct data */
1694 #define MIN_BSIZE 32
1695
1696 /* min meta block len, must be >= 10 */
1697 #define MIN_BSIZE_META 32
1698
1699 /* max meta block len, must be <= DELTA_BSIZE */
1700 #define MAX_BSIZE_META DELTA_BSIZE
1701
1702 /* number of slots per data area */
1703 #define SLOTS_PER_AREA  4095
1704
1705
1706 /* buzhash by Robert C. Uzgalis */
1707 /* General hash functions. Technical Report TR-92-01, The University
1708    of Hong Kong, 1993 */
1709
1710 static unsigned int buzhash(unsigned char *buf)
1711 {
1712   unsigned int x = 0x83d31df4U;
1713   int i;
1714   for (i = DELTA_BSIZE ; i != 0; i--)
1715     x = (x << 1) ^ (x & (1 << 31) ? 1 : 0) ^ buz_noise[*buf++];
1716   return x;
1717 }
1718
1719 static void md5block(unsigned char *buf, int len, unsigned char *out)
1720 {
1721   MD5_CTX ctx;
1722   md5_init(&ctx);
1723   md5_update(&ctx, buf, (unsigned long)len);
1724   md5_final(&ctx, out);
1725 }
1726
1727 #define HASHCHAIN_START 7
1728 #define HASHCHAIN_NEXT(h, hh, mask) (((h) + (hh)++) & (mask))
1729
1730
1731 struct deltastore {
1732   int fd;                               /* file descriptor */
1733   int rdonly;                           /* store is read only */
1734
1735   unsigned long long end;               /* store file size */
1736
1737   unsigned long long *offsets;          /* the data areas we know about */
1738   int noffsets;
1739
1740   unsigned char *hash;                  /* our hash */
1741   unsigned int hm;                      /* size of hash */
1742   unsigned int hf;                      /* hash fill */
1743   unsigned int hd;                      /* entries not in hash */
1744
1745   int freecnt;                          /* free slots in last slot area */
1746   int usedcnt;                          /* used slots in last slot area */
1747   unsigned long long slotsoffset;       /* offset of last slot area */
1748 };
1749
1750 struct deltaout {
1751   FILE *fp;
1752   struct deltastore *store;
1753
1754   /* for block coalescence */
1755   unsigned long long oldoffset;
1756   unsigned long long oldsize;
1757
1758   /* for offset encoding */
1759   unsigned long long lastoffset;
1760
1761   /* for meta block creation */
1762   int outbuf_do_meta;                           /* create meta blocks */
1763   unsigned char outbuf[MAX_BSIZE_META + 16];    /* 16 extra bytes for one encoded block */
1764   int outbuf_len;
1765   /* offset patching */
1766   unsigned long long outbuf_startoffset;
1767   int outbuf_startoffset_set;
1768   int outbuf_set_len1;
1769   int outbuf_set_len2;
1770   unsigned long long outbuf_set_offset;         /* offset we need to patch in, already encoded */
1771 };
1772
1773 static inline unsigned long long getu48(unsigned char *d)
1774 {
1775   unsigned long long x = d[0] << 8 | d[1];
1776   return (x << 32) | (d[2] << 24 | d[3] << 16 | d[4] << 8 | d[5]);
1777 }
1778
1779 static inline void putu48(unsigned char *d, unsigned long long x)
1780 {
1781   d[0] = x >> 40;
1782   d[1] = x >> 32;
1783   d[2] = x >> 24;
1784   d[3] = x >> 16;
1785   d[4] = x >> 8;
1786   d[5] = x;
1787 }
1788
1789 static inline unsigned int getu32(unsigned char *d)
1790 {
1791   return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
1792 }
1793
1794 static inline void putu32(unsigned char *d, unsigned int x)
1795 {
1796   d[0] = x >> 24;
1797   d[1] = x >> 16;
1798   d[2] = x >> 8;
1799   d[3] = x;
1800 }
1801
1802 /**
1803  **  store handling
1804  **/
1805
1806 static int
1807 finddataarea(struct deltastore *store, unsigned long long offset)
1808 {
1809   int i;
1810   for (i = 0; i < store->noffsets; i += 2)
1811     if (offset >= store->offsets[i] && offset < store->offsets[i + 1])
1812       return i;
1813   return -1;
1814 }
1815
1816 static void
1817 adddataarea(struct deltastore *store, unsigned long long start, unsigned long long end)
1818 {
1819   unsigned long long *newoffsets;
1820   if (store->noffsets && store->offsets[store->noffsets - 1] == start)
1821     {
1822       store->offsets[store->noffsets - 1] = end;
1823       return;
1824     }
1825   if (store->offsets)
1826     newoffsets = realloc(store->offsets, (store->noffsets + 2) * sizeof(unsigned long long));
1827   else
1828     newoffsets = malloc((store->noffsets + 2) * sizeof(unsigned long long));
1829   if (!newoffsets)
1830     return;
1831   newoffsets[store->noffsets++] = start;
1832   newoffsets[store->noffsets++] = end;
1833   store->offsets = newoffsets;
1834 }
1835
1836 static int
1837 addslotarea(struct deltastore *store, int cnt)
1838 {
1839   unsigned char *slots;
1840   if (!cnt || cnt > 65535)
1841     return 0;
1842   if (store->rdonly)
1843     return 0;
1844   if ((store->end & 4095) != 0)         /* pad to multiple of 4096 */
1845     {
1846       char pad[4096];
1847       int l = 4096 - (store->end & 4095);
1848       memset(pad, 0, l);
1849       if (pwrite(store->fd, pad, l, store->end) != l)
1850         {
1851           perror("pwrite pad next slotsarea");
1852           return 0;
1853         }
1854       store->end += l;
1855     }
1856   if (store->end + (cnt + 1) * 16 >= (1LL << 48))
1857     return 0;                           /* store too big! */
1858   slots = calloc(cnt + 1, 16);
1859   if (!slots)
1860     return 0;
1861   memcpy(slots, "OBSDELT", 8);
1862   slots[8] = cnt >> 8;
1863   slots[9] = cnt;
1864   /* write pointer to next slot area */
1865   if (store->end)
1866     {
1867       putu48(slots + 10, store->end);
1868       if (pwrite(store->fd, slots, 16, store->slotsoffset) != 16)
1869         {
1870           perror("pwrite update next slotsarea");
1871           free(slots);
1872           return 0;
1873         }
1874       memset(slots + 10, 0, 6);
1875     }
1876   if (pwrite(store->fd, slots, (cnt + 1) * 16, store->end) != (cnt + 1) * 16)
1877     {
1878       perror("pwrite new slotarea");
1879       free(slots);
1880       return 0;
1881     }
1882   free(slots);
1883   store->slotsoffset = store->end;
1884   store->end += (cnt + 1) * 16;
1885   store->freecnt = cnt;
1886   store->usedcnt = 0;
1887   return 1;
1888 }
1889
1890 /* 
1891  * add a new block to the store.
1892  * returns the store offset, 0 on error
1893  */
1894 static unsigned long long
1895 putinstore(struct deltastore *store, unsigned char *buf, int size, unsigned char *md5, unsigned int hx)
1896 {
1897   unsigned char md5buf[16];
1898   unsigned char hp[16];
1899   unsigned long long offset;
1900
1901   unsigned char *hash;
1902   unsigned int h, hh, hm;
1903
1904   if (!size || size > DELTA_BSIZE)
1905     return 0;
1906
1907   if (store->rdonly)
1908     return 0;
1909   if (store->freecnt == 0 && !addslotarea(store, SLOTS_PER_AREA))
1910     return 0;
1911
1912   /* write data */
1913   offset = store->end;
1914   if (offset + size >= (1LL << 48))
1915     return 0;                   /* store too big! */
1916   if (pwrite(store->fd, buf, size, store->end) != size)
1917     {
1918       perror("pwrite data");
1919       return 0;
1920     }
1921   adddataarea(store, store->end, store->end + size);
1922   store->end += size;
1923
1924   /* write slot */
1925   if (!md5)
1926     {
1927       md5block(buf, size, md5buf);
1928       md5 = md5buf;
1929     }
1930   hp[0] = size >> 8;
1931   hp[1] = size;
1932   putu48(hp + 2, offset);
1933   if (size == DELTA_BSIZE)
1934     {
1935       if (!hx)
1936         hx = buzhash(buf);
1937       putu32(hp + 8, hx);
1938       memcpy(hp + 12, md5, 4);
1939     }
1940   else
1941     {
1942       hp[0] |= 0x80;            /* small block marker */
1943       memcpy(hp + 8, md5, 8);
1944       hx = getu32(hp + 8);      /* needed later */
1945     }
1946 #if 0
1947 {
1948   int j;
1949   printf("NEW SLOT");
1950   for (j = 0; j < 16; j++)
1951     printf(" %02x", hp[j]);
1952   printf("\n");
1953 }
1954 #endif
1955   if (pwrite(store->fd, hp, 16, store->slotsoffset + (store->usedcnt + 1) * 16) != 16)
1956     {
1957       perror("pwrite slot");
1958       return 0;
1959     }
1960   store->freecnt--;
1961   store->usedcnt++;
1962
1963   /* update hash */
1964   hm = store->hm;
1965   hash = store->hash;
1966   h = hx & hm;
1967   hh = HASHCHAIN_START;
1968   while (hash[16 * h] != 0)
1969     h = HASHCHAIN_NEXT(h, hh, hm);
1970   memcpy(hash + 16 * h, hp, 16);
1971   store->hf++;
1972   return offset;
1973 }
1974
1975 /* make sure that we found the correct block */
1976 static int
1977 checkstore(struct deltastore *store, unsigned long long offset, unsigned char *buf, int size)
1978 {
1979   unsigned char buf2[4096];
1980   while (size)
1981     {
1982       int l = size > 4096 ? 4096 : size;
1983       if (pread(store->fd, buf2, l, offset) != l)
1984         return 0;
1985       if (memcmp(buf2, buf, l) != 0)
1986         return 0;
1987       size -= l;
1988       buf += l;
1989       offset += l;
1990     }
1991   return 1;
1992 }
1993
1994 /* 
1995  * try to find a (non-rolling) block in the store. If not found, add it.
1996  * returns the store offset, 0 on error
1997  */
1998 static unsigned long long
1999 reuse_or_add_block(struct deltastore *store, unsigned char *buf, int size)
2000 {
2001   unsigned char md5[16];
2002   unsigned int h, hh, hm;
2003   unsigned char *hash;
2004   unsigned long long offset;
2005
2006   if (!size || size >= DELTA_BSIZE)
2007     return 0;           /* only small non-rolling blocks for now */
2008   md5block(buf, size, md5);
2009   hm = store->hm;
2010   hash = store->hash;
2011   h = (md5[0] << 24 | md5[1] << 16 | md5[2] << 8 | md5[3]) & hm;
2012   hh = HASHCHAIN_START;
2013   while (hash[16 * h] != 0)
2014     {
2015       unsigned char *hp = hash + 16 * h;
2016       if (((hp[0] & 0x7f) << 8 | hp[1]) == size && !memcmp(hp + 8, md5, 8))
2017         {
2018           offset = getu48(hp + 2);
2019           if (checkstore(store, offset, buf, size))
2020             return offset;
2021         }
2022       h = HASHCHAIN_NEXT(h, hh, hm);
2023     }
2024   return putinstore(store, buf, size, md5, 0);
2025 }
2026
2027 /**
2028  **  block encoding
2029  **/
2030
2031 static int
2032 encodelonglong(FILE *ofp, unsigned long long x)
2033 {
2034   unsigned long long z = 1;
2035   int c;
2036   do
2037     {
2038       z = z << 7 | (x & 127);
2039       x >>= 7;
2040     }
2041   while (x);
2042   for (;;)
2043     {
2044       c = (z & 127) | 128;
2045       z >>= 7;
2046       if (z == 1)
2047         break;
2048       if (putc(c, ofp) == EOF)
2049         return 0;
2050     }
2051   if (putc(c ^ 128, ofp) == EOF)
2052     return 0;
2053   return 1;
2054 }
2055
2056 static int
2057 encodelonglong_mem(unsigned char *bp, unsigned long long x)
2058 {
2059   unsigned long long z = 1;
2060   int c;
2061   int l = 0;
2062   do
2063     {
2064       z = z << 7 | (x & 127);
2065       x >>= 7;
2066     }
2067   while (x);
2068   for (;;)
2069     {
2070       c = (z & 127) | 128;
2071       z >>= 7;
2072       if (z == 1)
2073         break;
2074       *bp++ = c;
2075       l++;
2076     }
2077   *bp = c ^ 128;;
2078   return l + 1;
2079 }
2080
2081
2082 #if 1
2083 /* fancy delta conversion, seems to work better than the simple xor */
2084 static inline unsigned long long
2085 encodeoffset(unsigned long long oldoffset, unsigned long long offset)
2086 {
2087   if (oldoffset & (1LL << 47))
2088     {
2089       offset ^= ((1LL << 48) - 1);
2090       oldoffset ^= ((1LL << 48) - 1);
2091     }
2092   if (offset < oldoffset * 2)
2093     {
2094       if (offset < oldoffset)
2095         offset = (oldoffset - offset - 1) << 1 | 1;
2096       else
2097         offset = (offset - oldoffset) << 1;
2098     }
2099   return offset;
2100 }
2101
2102 static inline unsigned long long
2103 decodeoffset(unsigned long long oldoffset, unsigned long long offset)
2104 {
2105   int neg = oldoffset & (1LL << 47) ? ((1LL << 48) - 1) : 0;
2106   oldoffset ^= neg;
2107   if (offset < oldoffset * 2)
2108     {
2109       if (offset & 1)
2110         offset = oldoffset - ((offset >> 1) + 1);
2111       else
2112         offset = oldoffset + (offset >> 1);
2113     }
2114   return offset ^ neg;
2115 }
2116
2117 #else
2118 static inline unsigned long long
2119 encodeoffset(unsigned long long oldoffset, unsigned long long offset)
2120 {
2121   return oldoffset ^ offset;
2122 }
2123
2124 static inline unsigned long long
2125 decodeoffset(unsigned long long oldoffset, unsigned long long offset)
2126 {
2127   return oldoffset ^ offset;
2128 }
2129 #endif
2130
2131 static int
2132 flushoutbuf(struct deltaout *out)
2133 {
2134   if (!out->outbuf_len)
2135     return 1;
2136   if (out->outbuf_len >= MAX_BSIZE_META)
2137     return 0;
2138
2139   if (out->outbuf_len >= MIN_BSIZE_META)
2140     {
2141       /* put as meta block into store! */
2142       int size = out->outbuf_len;
2143       unsigned long long offset;
2144 #if 0
2145       printf("META size %d\n", out->outbuf_len);
2146 #endif
2147       offset = reuse_or_add_block(out->store, out->outbuf, size);
2148       if (!offset)
2149         return 0;
2150       /* encode meta block into outbuf */
2151       if (out->outbuf_startoffset_set)
2152         out->lastoffset = out->outbuf_startoffset;
2153       out->outbuf[0] = 15;                      /* meta */
2154       out->outbuf_len = 1;
2155       out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, size);
2156       out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, encodeoffset(out->lastoffset, offset));
2157       out->lastoffset = offset + size;
2158       out->outbuf_startoffset_set = 0;
2159     }
2160
2161   if (out->outbuf_startoffset_set)
2162     {
2163       /* tricky: fix up first offset! */
2164       unsigned char buf[9];
2165       int l = encodelonglong_mem(buf, out->outbuf_set_offset);
2166       if (fwrite(out->outbuf, out->outbuf_set_len1, 1, out->fp) != 1)
2167         return 0;
2168       if (fwrite(buf, l, 1, out->fp) != 1)
2169         return 0;
2170       if (out->outbuf_set_len2 < out->outbuf_len && fwrite(out->outbuf + out->outbuf_set_len2, out->outbuf_len - out->outbuf_set_len2, 1, out->fp) != 1)
2171         return 0;
2172     }
2173   else if (fwrite(out->outbuf, out->outbuf_len, 1, out->fp) != 1)
2174     return 0;
2175   out->outbuf_len = 0;
2176   out->outbuf_startoffset_set = 0;
2177   return 1;
2178 }
2179
2180 static int
2181 encodestoreblock_real(struct deltaout *out, unsigned long long offset, unsigned long long size)
2182 {
2183 #if 0
2184   printf("BLOCK %#llx %llu\n", offset, size);
2185 #endif
2186   if (out->outbuf_do_meta)
2187     {
2188       int lastlen = out->outbuf_len;
2189       /* the following code is needed as we want to use a lastoffset of
2190        * zero if this ends up in a meta instruction. So we encode with
2191        * lastoffset zero but also store the real lastoffset and byte offsets,
2192        * in order to fix up the offset in flushbuf */
2193       int set = out->outbuf_startoffset_set;
2194       if (!set)
2195         {
2196           out->outbuf_startoffset_set = 1;
2197           out->outbuf_startoffset = out->lastoffset;
2198           out->outbuf_set_offset = encodeoffset(out->lastoffset, offset);
2199           out->lastoffset = 0;
2200         }
2201       out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, out->oldsize + 256);
2202       if (!set)
2203         out->outbuf_set_len1 = out->outbuf_len;
2204       out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, encodeoffset(out->lastoffset, offset));
2205       if (!set)
2206         out->outbuf_set_len2 = out->outbuf_len;
2207
2208       if (out->outbuf_len >= DELTA_BSIZE)
2209         {
2210           /* buffer too full. revert changes. flush outbuf. retry */
2211           out->outbuf_len = lastlen;
2212           if (!set)
2213             {
2214               out->outbuf_startoffset_set = 0;
2215               out->lastoffset = out->outbuf_startoffset;
2216             }
2217           if (!flushoutbuf(out))
2218             return 0;
2219           return encodestoreblock_real(out, offset, size);
2220         }
2221     }
2222   else
2223     {
2224       if (!encodelonglong(out->fp, size + 256))
2225         return 0;
2226       if (!encodelonglong(out->fp, encodeoffset(out->lastoffset, offset)))
2227         return 0;
2228     }
2229   out->lastoffset = offset + size;
2230   return 1;
2231 }
2232
2233 /* encode a store block instruction
2234  * we delay the real encoding so that we can join adjacent blocks
2235  */
2236 static int
2237 encodestoreblock(struct deltaout *out, unsigned long long offset, unsigned long long size)
2238 {
2239   if (out->oldoffset)
2240     {
2241       if (out->oldoffset + out->oldsize == offset)
2242         {
2243           out->oldsize += size;
2244           return 1;
2245         }
2246       if (!encodestoreblock_real(out, out->oldoffset, out->oldsize))
2247         return 0;
2248     }
2249   out->oldoffset = offset;      /* block not yet written */
2250   out->oldsize = size;
2251   return 1;
2252 }
2253
2254 /* encode a direct data instruction
2255  */
2256 static int
2257 encodedirect(struct deltaout *out, unsigned char *buf, int size)
2258 {
2259   if (!size)
2260     return 1;
2261   if (size >= 256 - 16)
2262     return 0;
2263   if (out->oldoffset)
2264     {
2265       if (!encodestoreblock(out, 0, 0)) /* flush */
2266         return 0;
2267     }
2268 #if 0
2269   printf("DIRECT %u\n", size);
2270 #endif
2271   if (out->outbuf_do_meta)
2272     {
2273       if (out->outbuf_len + 1 + size >= DELTA_BSIZE)
2274         {
2275           /* buffer too full. flush outbuf */
2276           if (!flushoutbuf(out))
2277             return 0;
2278         }
2279       out->outbuf[out->outbuf_len++] = 16 + size;
2280       memcpy(out->outbuf + out->outbuf_len, buf, size);
2281       out->outbuf_len += size;
2282     }
2283   else
2284     {
2285       if (putc(16 + size, out->fp) == EOF)
2286         return 0;
2287       if (fwrite(buf, size, 1, out->fp) != 1)
2288         return 0;
2289     }
2290   return 1;
2291 }
2292
2293 /**
2294  **  the delta algorithm
2295  **/
2296
2297 static unsigned long long
2298 extendblock(struct deltastore *store, FILE *fp, unsigned long long offset, unsigned long long areaend, unsigned long long maxextend)
2299 {
2300   unsigned char buf[1024];
2301   int c, i, bufl;
2302   unsigned long long extend = 0;
2303
2304   if (offset >= areaend)
2305     return 0;
2306   if (areaend - offset < maxextend)
2307     maxextend = areaend - offset;
2308   if (!maxextend)
2309     return 0;
2310   i = bufl = 0;
2311   for (;;)
2312     {
2313       if (i == bufl)
2314         {
2315           bufl = maxextend > 1024 ? 1024 : maxextend;
2316           if (bufl == 0)
2317             return extend;
2318           if (pread(store->fd, buf, bufl, (off_t)offset) != bufl)
2319             return extend;
2320           offset += bufl;
2321           maxextend -= bufl;
2322           i = 0;
2323         }
2324       c = getc(fp);
2325       if (c == EOF)
2326         return extend;
2327       if (buf[i++] != c)
2328         {
2329           ungetc(c, fp);
2330           return extend;
2331         }
2332       extend++;
2333     }
2334 }
2335
2336 static unsigned long long
2337 extendblock_back(struct deltastore *store, unsigned char *data, unsigned long long offset, unsigned long long areastart, unsigned long long maxextend)
2338 {
2339   unsigned char buf[1024];
2340   unsigned long long extend = 0;
2341   int bufl;
2342
2343   if (offset <= areastart)
2344     return 0;
2345   if (offset - areastart < maxextend)
2346     maxextend = offset - areastart;
2347   if (!maxextend)
2348     return 0;
2349   bufl = 0;
2350   for (;;)
2351     {
2352       if (bufl == 0)
2353         {
2354           bufl = maxextend > 1024 ? 1024 : maxextend;
2355           if (bufl == 0)
2356             return extend;
2357           offset -= bufl;
2358           if (pread(store->fd, buf, bufl, (off_t)offset) != bufl)
2359             return extend;
2360           maxextend -= bufl;
2361         }
2362       if (*--data != buf[--bufl])
2363         return extend;
2364       extend++;
2365     }
2366 }
2367
2368 static int
2369 dosimple(struct deltastore *store, struct deltaout *out, unsigned char *buf, int size)
2370 {
2371   unsigned long long offset;
2372
2373   while (size >= DELTA_BSIZE)
2374     {
2375       offset = putinstore(store, buf, DELTA_BSIZE, 0, 0);
2376       if (!offset || !encodestoreblock(out, offset, DELTA_BSIZE))
2377         return 0;
2378       size -= DELTA_BSIZE;
2379       buf += DELTA_BSIZE;
2380     }
2381   if (size < MIN_BSIZE)
2382     return encodedirect(out, buf, size);
2383   offset = reuse_or_add_block(store, buf, size);
2384   if (!offset)
2385     return 0;
2386   return encodestoreblock(out, offset, size);
2387 }
2388
2389 static int
2390 dodelta(struct deltastore *store, FILE *fp, struct deltaout *out, unsigned long long size)
2391 {
2392   unsigned char buf[DELTA_BSIZE * 16];
2393   unsigned char md5[16];
2394   unsigned long long offset, extendf, extendb;
2395   unsigned int h, hh, hm, hx;
2396   unsigned char *hash;
2397   int c, foundit, bi;
2398
2399 #if 0
2400   printf("DODELTA\n");
2401 #endif
2402   hm = store->hm;
2403   hash = store->hash;
2404   while (size)
2405     {
2406       if (size < DELTA_BSIZE)
2407         {
2408           if (fread(buf, (int)size, 1, fp) != 1)
2409             return 0;
2410           if (!dosimple(store, out, buf, (int)size))
2411             return 0;
2412           break;
2413         }
2414       /* read a block */
2415       bi = 0;
2416       if (fread(buf, DELTA_BSIZE, 1, fp) != 1)
2417         return 0;
2418       size -= DELTA_BSIZE;
2419
2420       hx = buzhash(buf);
2421       foundit = 0;
2422
2423       /* start rolling */
2424       for (;;)
2425         {
2426           int md5set = 0;
2427           /* check if the block at (buf + bi) is in the hash */
2428 #if 0
2429           if (hx != buzhash(buf + bi))
2430             abort();
2431 #endif
2432           hh = HASHCHAIN_START;
2433           for (h = hx & hm; hash[16 * h] != 0; h = HASHCHAIN_NEXT(h, hh, hm))
2434             {
2435               unsigned char *hp = hash + 16 * h;
2436               /* first check block len */
2437               if (hp[0] != (DELTA_BSIZE >> 8) || hp[1] != (DELTA_BSIZE & 0xff))
2438                 continue;
2439               /* then check complete hash value */
2440               if (hp[8] != (hx >> 24))
2441                 continue;
2442               if ((hp[8] << 24 | hp[9] << 16 | hp[10] << 8 | hp[11]) != hx)
2443                 continue;
2444               /* then check strong hash */
2445               if (!md5set)
2446                 {
2447                   md5block(buf + bi, DELTA_BSIZE, md5);
2448                   md5set = 1;
2449                 }
2450               if (memcmp(hp + 12, md5, 4) != 0)
2451                 continue;
2452               /* looks good. check data */
2453               offset = getu48(hp + 2);
2454               if (!checkstore(store, offset, buf + bi, DELTA_BSIZE))
2455                 continue;
2456               /* yes! found block in store! */
2457               /* try to extend found block */
2458               c = finddataarea(store, offset);
2459               extendf = extendb = 0;
2460               if (c >= 0)
2461                 {
2462                   extendf = extendblock(store, fp, offset + DELTA_BSIZE, store->offsets[c + 1], size);
2463                   size -= extendf;      /* extended bytes */
2464                   extendb = extendblock_back(store, buf + bi, offset, store->offsets[c], bi);
2465                   offset -= extendb;
2466                   bi -= extendb;
2467                 }
2468               /* encode data before block */
2469               if (bi)
2470                 {
2471                   if (!dosimple(store, out, buf, bi))
2472                     return 0;
2473                   bi = 0;
2474                 }
2475               /* encode */
2476               if (!encodestoreblock(out, offset, DELTA_BSIZE + extendf + extendb))
2477                 return 0;
2478               foundit = 1;
2479               break;
2480             }
2481           if (foundit)
2482             break;
2483
2484           /* not found. move block one byte */
2485           if (!size)
2486             {
2487               if (!dosimple(store, out, buf, bi + DELTA_BSIZE))
2488                 return 0;
2489               break;
2490             }
2491           c = fgetc(fp);
2492           if (c == EOF)
2493             return 0;
2494           size--;
2495           buf[DELTA_BSIZE + bi] = c;
2496           hx = (hx << 1) ^ (hx & (1 << 31) ? 1 : 0) ^ buz_noise[c];
2497           c = buf[bi++];
2498           hx ^= buz_noise[c] ^ (0x83d31df4U ^ 0x07a63be9U);
2499           if (bi == sizeof(buf) - DELTA_BSIZE)
2500             {
2501               /* trim down, but leave one block for backward extension */
2502               if (!dosimple(store, out, buf, bi - DELTA_BSIZE))
2503                 return 0;
2504               /* no overlap as the buffer is >= 4 * DELTA_BSIZE */
2505               memcpy(buf, buf + bi - DELTA_BSIZE, 2 * DELTA_BSIZE);
2506               bi = DELTA_BSIZE;
2507             }
2508         }
2509     }
2510   if (!encodestoreblock(out, 0, 0))     /* flush */
2511     return 0;
2512   if (!flushoutbuf(out))
2513     return 0;
2514   return 1;
2515 }
2516
2517 static int
2518 readdeltastore(struct deltastore *store, int fd, int rdonly, unsigned long long xsize)
2519 {
2520   unsigned char *slots;
2521   unsigned char oneslot[16];
2522   unsigned long long offset, nextoffset, lastgoodoffset;
2523   struct stat st;
2524   unsigned long long fsize;
2525   unsigned int nslots = 0, hslots;
2526   unsigned char *hash;
2527   unsigned int hm, h, hh, hf, hd;
2528   int isbad = 0;
2529   int i, lasti, cnt, maxcnt = 0;
2530   unsigned int drop = 0;
2531
2532   memset(store, 0, sizeof(*store));
2533   store->fd = fd;
2534   store->rdonly = rdonly;
2535   if (fstat(fd, &st))
2536     {
2537       perror("fstat");
2538       return 0;
2539     }
2540   fsize = (unsigned long long)st.st_size;
2541   store->end = fsize;
2542
2543   /* first pass: find number of used entries */
2544   offset = 0;
2545   lastgoodoffset = -1;
2546   for (;;)
2547     {
2548       if (offset == fsize)
2549         break;
2550       if (offset + 16 > fsize)
2551         {
2552           fprintf(stderr, "WARNING: slot area exceeds file size!\n");
2553           isbad = 1;
2554           break;
2555         }
2556       if (pread(fd, oneslot, 16, offset) != 16)
2557         return 0;
2558       if (memcmp(oneslot, "OBSDELT", 8) != 0)
2559         {
2560           fprintf(stderr, "WARNING: slot magic error!\n");
2561           isbad = 1;
2562           break;
2563         }
2564       cnt = oneslot[8] << 8 | oneslot[9];
2565       nextoffset = getu48(oneslot + 10);
2566       if (!nextoffset)
2567         nextoffset = fsize;
2568       offset += (cnt + 1) * 16;
2569       if (offset > fsize)
2570         {
2571           fprintf(stderr, "WARNING: slot area exceeds file size!\n");
2572           isbad = 1;
2573           break;
2574         }
2575       nslots += cnt;
2576       lastgoodoffset = offset - (cnt + 1) * 16;
2577       if (cnt > maxcnt)
2578         maxcnt = cnt;
2579       if (offset > nextoffset)
2580         {
2581           fprintf(stderr, "WARNING: end of slots bigger than nextoffset!\n");
2582           isbad = 1;
2583           break;
2584         }
2585       offset = nextoffset;
2586     }
2587
2588   if (isbad && !store->rdonly)
2589     {
2590       fprintf(stderr, "WARNING: fixing up bad slots!\n");
2591       if (lastgoodoffset == -1)
2592         {
2593           /* worst case: first slots area is damaged */
2594           memset(oneslot, 0, 16);
2595           memcpy(oneslot, "OBSDELT", 8);
2596           putu48(oneslot + 10, fsize);
2597           if (pwrite(store->fd, oneslot, 16, 0) != 16)
2598             {
2599               perror("pwrite repair first slots area");
2600               return 0;
2601             }
2602         }
2603       else
2604         {
2605           putu48(oneslot + 10, fsize);
2606           if (pwrite(store->fd, oneslot + 10, 6, lastgoodoffset + 10) != 6)
2607             {
2608               perror("pwrite repair bad slots area nextoffset");
2609               return 0;
2610             }
2611         }
2612       isbad = 0;
2613     }
2614
2615   slots = calloc(maxcnt + 1, 16);
2616   if (!slots)
2617     return 0;
2618
2619   /* find good hash size and allocate hash */
2620   hslots = nslots + xsize / 512;
2621   while (hslots & (hslots - 1))
2622     hslots = hslots & (hslots - 1);
2623   if (hslots < 16384)
2624     hslots = 16384;             /* 1 MByte min mem */
2625   while (hslots > 128 * 1024 * 1024)    /* 8 GByte max mem */
2626     {
2627       /* oh no. max size reached. drop half of slots */
2628       hslots >>= 1;
2629       drop += (drop ? drop : nslots) / 2;
2630     }
2631   hslots *= 4;
2632   store->hm = hm = hslots - 1;
2633   store->hash = hash = calloc(hm + 1, 16);
2634   if (!hash)
2635     {
2636       fprintf(stderr, "could not allocate hash (%u MB)\n", (hm + 1) / (1024 * 1024 / 16));
2637       free(slots);
2638       return 0;
2639     }
2640
2641   /* second pass: fill the hash */
2642   offset = 0;
2643   hf = hd = 0;
2644   for (;;)
2645     {
2646       int toread = 16 * (maxcnt + 1);
2647       if (isbad && lastgoodoffset == -1)
2648         break;
2649       if (offset >= fsize)
2650         break;
2651       if (offset + toread > fsize)
2652         toread = fsize - offset;
2653       if (pread(fd, slots, toread, offset) != toread)
2654         {
2655           free(slots);
2656           return 0;
2657         }
2658       if (memcmp(slots, "OBSDELT", 8) != 0)
2659         break;
2660       cnt = oneslot[8] << 8 | oneslot[9];
2661       offset += 16 * (cnt + 1);
2662       nextoffset = getu48(slots + 10);
2663       if (!nextoffset)
2664         nextoffset = fsize;
2665       if (offset > nextoffset)
2666         break;
2667       if (offset != nextoffset)
2668         adddataarea(store, offset, nextoffset);
2669       lasti = 0;
2670       for (i = 1; i < cnt + 1; i++)
2671         if (slots[16 * i])
2672           {
2673             unsigned char *hp = slots + 16 * i;
2674             int len = (hp[0] & 127) << 8 | hp[1];
2675             unsigned long long o = getu48(hp + 2);
2676             lasti = i;
2677             if (drop)
2678               {
2679                 drop--;
2680                 hd++;
2681               }
2682             else if (o >= offset && o + len <= nextoffset)
2683               {
2684                 /* a good one. add to hash. */
2685                 h = getu32(hp + 8) & hm;
2686                 hh = HASHCHAIN_START;
2687                 while (hash[16 * h] != 0)
2688                   h = HASHCHAIN_NEXT(h, hh, hm);
2689                 memcpy(hash + 16 * h, hp, 16);
2690                 hf++;
2691               }
2692           }
2693       store->slotsoffset = offset - 16 * (cnt + 1);
2694       store->freecnt = cnt - lasti;
2695       store->usedcnt = lasti;
2696       if (isbad && lastgoodoffset == store->slotsoffset)
2697         break;
2698       offset = nextoffset;
2699     }
2700   store->hf = hf;
2701   store->hd = hd;
2702 #if 0
2703   printf("readdeltastore: have %d entries, %d dropped, hash size %d\n", hf, hd, hm + 1);
2704 #endif
2705   free(slots);
2706   return 1;
2707 }
2708
2709 static void
2710 printdeltastorestats(struct deltastore *store)
2711 {
2712   unsigned int buckets[2048];
2713   unsigned int hm, hf, hd;
2714   unsigned char *hp;
2715   int i, j, bc = 16;
2716
2717   memset(buckets, 0, sizeof(buckets));
2718   hm = store->hm;
2719   hf = store->hf;
2720   hd = store->hd;
2721
2722   printf("store size: %llu (%u MB)\n", store->end, (unsigned int)(store->end / (1024 * 1024)));
2723   printf("hash mask: 0x%x (%u MB hash mem)\n", hm, (unsigned int)(hm / 65536) + 1);
2724   printf("hash entries set: %u (%.2f %%)\n", hf, ((double)hf * 100) / ((double)hm + 1));
2725   printf("hash entries dropped: %u (%.2f %%)\n", hd, hd ? ((double)hd * 100) / ((double)hf + (double)hd) : 0.);
2726   for (hp = store->hash;; hp += 16)
2727     {
2728       if (hp[0])
2729         buckets[((hp[0] & 0x7f) << 8 | hp[1]) / 16]++;
2730       if (!hm--)
2731         break;
2732     }
2733   for (i = 2047; i >= 1; i--)
2734     if (buckets[i])
2735       break;
2736   i++;
2737   while (i > 16)
2738     {
2739       for (j = 0; j < i; j += 2)
2740         buckets[j / 2] = buckets[j] + buckets[j + 1];
2741       i = (i + 1) / 2;
2742       bc *= 2;
2743     }
2744   printf("block stats:\n");
2745   for (j = 0; j < i; j++)
2746     printf("  size %#6x - %#6x: %10u\n", j * bc, j * bc + bc - 1, buckets[j]);
2747   printf("data areas: %d\n", store->noffsets / 2);
2748 }
2749
2750 static void
2751 freedeltastore(struct deltastore *store)
2752 {
2753   if (store->hash)
2754     free(store->hash);
2755   if (store->offsets)
2756     free(store->offsets);
2757 }
2758
2759 static void
2760 settimes(int fd, struct stat *st)
2761 {
2762   struct timeval tv[2];
2763
2764   tv[0].tv_sec = st->st_atime;
2765   tv[0].tv_usec = 0;
2766   tv[1].tv_sec = st->st_mtime;
2767   tv[1].tv_usec = 0;
2768   futimes(fd, tv);
2769 }
2770
2771 static int
2772 checkhexcomp(unsigned char *buf)
2773 {
2774   int i, hexcomp = 0;
2775   for (i = 0; i < 110; i++)
2776     {
2777       int c = *buf++;
2778       if (c >= '0' && c <= '9')
2779         continue;
2780       else if (c >= 'A' && c <= 'F')
2781         {
2782           if (!hexcomp)
2783             hexcomp = 1;
2784           if (hexcomp != 1)
2785             break;
2786         }
2787       else if (c >= 'a' && c <= 'f')
2788         {
2789           if (!hexcomp)
2790             hexcomp = 2;
2791           if (hexcomp != 2)
2792             break;
2793         }
2794       else
2795         break;
2796     }
2797   if (i < 110)
2798     return 0;
2799   return hexcomp ? hexcomp : 1;
2800 }
2801
2802 static unsigned int fromhex(unsigned char *hex)
2803 {
2804   int i;
2805   unsigned int x = 0;
2806   for (i = 0; i < 8; i++, hex++)
2807     {
2808       if (*hex >= '0' && *hex <= '9')
2809         x = x << 4 | (*hex - '0');
2810       else if (*hex >= 'a' && *hex <= 'f')
2811         x = x << 4 | (*hex - ('a' - 10));
2812       else if (*hex >= 'A' && *hex <= 'F')
2813         x = x << 4 | (*hex - ('A' - 10));
2814     }
2815   return x;
2816 }
2817
2818 static void
2819 magic_inode_increment(unsigned char *cpio)
2820 {
2821   unsigned int inode = getu32(cpio + 3);
2822   if (inode)
2823     putu32(cpio + 3, inode + 1);
2824 }
2825
2826 static int
2827 makedelta(struct deltastore *store, FILE *fp, FILE *ofp, unsigned long long fpsize)
2828 {
2829   unsigned char cpiohead[1024 + 16384];
2830   unsigned char oldcpio[1024];
2831   int trailerseen = 0;
2832   int i, j;
2833   struct deltaout out;
2834
2835   if (fpsize >= (1LL << 40))
2836     return 0;
2837
2838   /* init deltaout struct */
2839   memset(&out, 0, sizeof(out));
2840   out.fp = ofp;
2841   out.store = store;
2842   out.outbuf_do_meta = 1;               /* create meta blocks */
2843
2844   /* write our header */
2845   memset(cpiohead, 0, 32);
2846   memcpy(cpiohead, "OBScpio", 8);
2847   putu48(cpiohead + 10, fpsize);
2848   if (fwrite(cpiohead, 16, 1, ofp) != 1)
2849     return 0;
2850
2851   memset(oldcpio, 0, 1024);
2852   while (!trailerseen)
2853     {
2854       unsigned long long fsize;
2855       unsigned int hsize, nsize;
2856       int run;
2857       int hexcomp;
2858       int noff = 110;
2859       int zero;
2860
2861       /* read the header */
2862       if (fread(cpiohead, 110, 1, fp) != 1)
2863         {
2864           fprintf(stderr, "cpio header read error\n");
2865           return 0;
2866         }
2867       if (memcmp(cpiohead, "070701", 6) != 0)
2868         {
2869           fprintf(stderr, "not a newc cpio archive\n");
2870           return 0;
2871         }
2872       fsize = fromhex(cpiohead + 54);
2873       nsize = fromhex(cpiohead + 94);
2874       if (nsize > 16384)
2875         {
2876           fprintf(stderr, "filename size too big\n");
2877           return 0;
2878         }
2879       hsize = noff + nsize;
2880       if (hsize & 3)
2881         hsize += 4 - (hsize & 3);
2882       /* check if we can do hex compression */
2883       hexcomp = checkhexcomp(cpiohead);
2884       if (hexcomp)
2885         {
2886           /* do fancy hex compression */
2887           cpiohead[0] = 0x07;
2888           cpiohead[1] = 0x07;
2889           cpiohead[2] = 0x01;
2890           for (i = 3; i < 55; i += 4)
2891             {
2892               unsigned int x = fromhex(cpiohead + i * 2);
2893               putu32(cpiohead + i, x);
2894             }
2895            noff -= 55;
2896            hsize -= 55;
2897         }
2898
2899 #if 0
2900       printf("fsize = %d, nsize = %d, hsize = %d\n", fsize, nsize, hsize);
2901 #endif
2902       if (fread(cpiohead + noff, hsize - noff, 1, fp) != 1)
2903         {
2904           fprintf(stderr, "cpio header read error\n");
2905           return 0;
2906         }
2907       if (fsize == 0 && nsize == 11 && !memcmp(cpiohead + noff, "TRAILER!!!", 11))
2908         {
2909           trailerseen = 1;
2910           while (hsize < sizeof(cpiohead))
2911             {
2912               int c = getc(fp);
2913               if (c == EOF)
2914                 break;
2915               cpiohead[hsize++] = c;
2916             }
2917           if (hsize == sizeof(cpiohead))
2918             {
2919               fprintf(stderr, "excess trailer data\n");
2920               return 0;
2921             }
2922         }
2923       /* check trailing zero */
2924       for (zero = 0; zero < 4; zero++)
2925         if (cpiohead[hsize - 1 - zero] != 0)
2926           break;
2927       /* write the header */
2928       if (putc(2 + hexcomp, ofp) == EOF)
2929         return 0;
2930       for (i = 0; i < 1024 && i < hsize; i++)
2931         {
2932           cpiohead[i] ^= oldcpio[i];
2933           oldcpio[i] ^= cpiohead[i];
2934         }
2935       if (hexcomp)
2936         magic_inode_increment(oldcpio);
2937       run = 0;
2938       hsize -= zero;
2939       for (i = 0; i < hsize; i++)
2940         {
2941           if (cpiohead[i] == 0)
2942             {
2943               run++;
2944               if (i + 1 < hsize)
2945                 continue;
2946             }
2947           while (run)
2948             {
2949               int z = run > 127 ? 127 : run;
2950               if (putc(z, ofp) == EOF)
2951                 return 0;
2952               run -= z;
2953             }
2954           if (cpiohead[i] == 0)
2955             break;              /* ended in zero */
2956           for (j = i; j < hsize - 1; j++)
2957             if (cpiohead[j] == 0 && cpiohead[j + 1] == 0)
2958               break;
2959           if (j == hsize - 1)
2960             j = hsize;
2961           j -= i;
2962           if (j > 123)
2963             j = 123;
2964           if (putc(j + 128, ofp) == EOF)
2965             return 0;
2966           while (j-- > 0)
2967             {
2968               int z = cpiohead[i++];
2969               if (putc(z, ofp) == EOF)
2970                 return 0;
2971             }
2972           i--;
2973         }
2974       if (putc(zero ? zero + 251 : 0, ofp) == EOF)
2975         return 0;
2976       if (fsize)
2977         {
2978           if (!dodelta(store, fp, &out, fsize))
2979             return 0;
2980           if ((fsize & 3) != 0)
2981             {
2982               i = 4 - (fsize & 3);
2983               if (putc(4 + i, ofp) == EOF)
2984                 return 0;
2985               while (i--)
2986                 {
2987                   if (getc(fp) != 0)
2988                     {
2989                       fprintf(stderr, "non-zero padding\n");
2990                       return 0;
2991                     }
2992                 }
2993             }
2994         }
2995     }
2996   if (putc(1, ofp) == EOF)
2997     return 0;
2998   if (fflush(ofp) != 0)
2999     return 0;
3000   return 1;
3001 }
3002
3003 static unsigned long long
3004 expandobscpio_next(FILE *fp)
3005 {
3006   unsigned long long x = 0;
3007   int i;
3008   for (;;)
3009     {
3010       i = getc(fp);
3011       if (i == EOF)
3012         return (unsigned long long)(-1);
3013       if ((i & 128) == 0)
3014         return x << 7 | i;
3015       x = x << 7 | (i ^ 128);
3016     }
3017 }
3018
3019 static unsigned long long
3020 expandobscpio_next_mem(unsigned char **bp, unsigned int *lp)
3021 {
3022   unsigned long long x = 0;
3023   unsigned char *b = *bp;
3024   unsigned int l = *lp;
3025   int i;
3026   for (;;)
3027     {
3028       if (l == 0)
3029         return (unsigned long long)(-1);
3030       i = *b++;
3031       l--;
3032       if ((i & 128) == 0)
3033         {
3034           *bp = b;
3035           *lp = l;
3036           return x << 7 | i;
3037         }
3038       x = x << 7 | (i ^ 128);
3039     }
3040 }
3041
3042 static int
3043 expandobscpio_direct_mem(unsigned char **bp, unsigned int *lp, void *out, unsigned int outlen)
3044 {
3045   if (*lp < outlen)
3046     return 0;
3047   if (outlen)
3048     memcpy(out, *bp, outlen);
3049   *bp += outlen;
3050   *lp -= outlen;
3051   return 1;
3052 }
3053
3054 static int
3055 expandcpiohead(FILE *fp, FILE *ofp, unsigned char *cpio, int hexcomp)
3056 {
3057   int l = 0;
3058   int zero;
3059   for (;;)
3060     {
3061       int c = getc(fp);
3062       if (c == EOF)
3063         return 0;
3064       if (c == 0)
3065         break;
3066       if (c < 128)
3067         zero = 1;
3068       else if (c >= 252)
3069         {
3070           zero = -1;
3071           c -= 251;
3072         }
3073       else
3074         {
3075           zero = 0;
3076           c -= 128;
3077         }
3078       while (c-- > 0)
3079         {
3080           int x = zero ? 0 : getc(fp);
3081           if (x == EOF)
3082             return 0;
3083           if (l < 1024)
3084             {
3085               if (zero >= 0)
3086                 x ^= cpio[l];
3087               cpio[l++] = x;
3088             }
3089           if (hexcomp && l <= 55)
3090             {
3091               int lettershift = (hexcomp == 1 ? 'A' : 'a') - ('0' + 10);
3092               int x1 = (x >> 4) + '0';
3093               int x2 = (x & 15) + '0';
3094               if (x1 > '9')
3095                 x1 += lettershift;
3096               if (x2 > '9')
3097                 x2 += lettershift;
3098               if (putc(x1, ofp) == EOF || putc(x2, ofp) == EOF)
3099                 return 0;
3100             }
3101           else if (putc(x, ofp) == EOF)
3102             return 0;
3103         }
3104       if (zero < 0)
3105         break;
3106     }
3107   if (hexcomp)
3108     magic_inode_increment(cpio);
3109   return 1;
3110 }
3111
3112 static int
3113 expandobscpio(FILE *fp, int fdstore, FILE *ofp)
3114 {
3115   unsigned char magic[16];
3116   unsigned char metabuf[16384];
3117   unsigned char cpio[1024];
3118   unsigned long long o, l;
3119   struct stat st;
3120   unsigned long long oldoffset = 0;
3121   unsigned char *meta = 0;
3122   unsigned int metal = 0;
3123   unsigned long long oldoffset_meta = 0;
3124
3125   if (!fp || !ofp || fdstore == -1)
3126     return 0;
3127   if (fstat(fileno(fp), &st))
3128     return 0;
3129   if (fread(magic, 16, 1, fp) != 1 || memcmp(magic, "OBScpio", 7) != 0)
3130     return 0;
3131   memset(cpio, 0, sizeof(cpio));
3132   for (;;)
3133     {
3134       if (meta && !metal)
3135         {
3136           meta = 0;
3137           oldoffset = oldoffset_meta;
3138         }
3139       if (meta)
3140         l = expandobscpio_next_mem(&meta, &metal);
3141       else
3142         l = expandobscpio_next(fp);
3143       if (l == (unsigned long long)(-1))
3144         return 0;
3145 #if 0
3146 printf("NEXT %d\n", l);
3147 #endif
3148       if (l < 16)
3149         {
3150           /* first 16 reserved as instructions */
3151           if (meta)
3152             return 0;
3153           if (l == 1)                                   /* EOF */
3154             break;
3155           if (l == 2 || l == 3 || l == 4)               /* CPIO */
3156             {
3157               if (!expandcpiohead(fp, ofp, cpio, l - 2))
3158                 return 0;
3159             }
3160           else if (l == 5 || l == 6 || l == 7)          /* ZERO */
3161             {
3162               l -= 4;
3163               while (l--)
3164                 if (putc(0, ofp) == EOF)
3165                   return 0;
3166             }
3167           else if (l == 15)                             /* META */
3168             {
3169               l = expandobscpio_next(fp);
3170               if (l == (unsigned long long)(-1))
3171                 return 0;
3172               if (l < 16 || l > sizeof(metabuf))
3173                 return 0;
3174               o = expandobscpio_next(fp);
3175               if (o == (unsigned long long)(-1))
3176                 return 0;
3177               o = decodeoffset(oldoffset, o);
3178               oldoffset_meta = o + l;
3179               oldoffset = 0;
3180               if (pread(fdstore, metabuf, (size_t)l, (off_t)o) != (size_t)l)
3181                 return 0;
3182               metal = (unsigned int)l;
3183               meta = metabuf;
3184             }
3185           else
3186             return 0;
3187         }
3188       else if (l < 256)
3189         {
3190           /* direct bytes */
3191           l -= 16;
3192           if (l)
3193             {
3194               char buf[256];
3195               if (meta)
3196                 {
3197                   if (expandobscpio_direct_mem(&meta, &metal, buf, l) != 1)
3198                     return 0;
3199                 }
3200               else if (fread(buf, (int)l, 1, fp) != 1)
3201                 return 0;
3202               if (fwrite(buf, (int)l, 1, ofp) != 1)
3203                 return 0;
3204             }
3205         }
3206       else
3207         {
3208           /* bytes from the store */
3209           l -= 256;
3210           if (meta)
3211             o = expandobscpio_next_mem(&meta, &metal);
3212           else
3213             o = expandobscpio_next(fp);
3214           if (o == (unsigned long long)(-1))
3215             return 0;
3216           o = decodeoffset(oldoffset, o);
3217           oldoffset = o + l;
3218           while (l)
3219             {
3220               char buf[8192];
3221               size_t count = l > 8192 ? 8192 : l;
3222               if (pread(fdstore, buf, count, (off_t)o) != count)
3223                 return 0;
3224               if (fwrite(buf, count, 1, ofp) != 1)
3225                 return 0;
3226               o += count;
3227               l -= count;
3228             }
3229         }
3230     }
3231   if (fflush(ofp) != 0)
3232     return 0;
3233   settimes(fileno(ofp), &st);
3234   return 1;
3235 }
3236
3237 static void
3238 printobscpioinstr(FILE *fp, int fdstore, int withmeta)
3239 {
3240   unsigned char magic[16];
3241   unsigned long long oldoffset = 0, o, l;
3242   unsigned char metabuf[16384];
3243   unsigned char *meta = 0;
3244   unsigned int metal = 0;
3245   unsigned long long oldoffset_meta = 0;
3246
3247   unsigned int stats_cpio = 0;
3248   unsigned long long stats_cpio_len = 0;
3249   unsigned int stats_direct = 0;
3250   unsigned long long stats_direct_len = 0;
3251   unsigned int stats_store = 0;
3252   unsigned long long stats_store_len = 0;
3253   unsigned int stats_zero = 0;
3254   unsigned long long stats_zero_len = 0;
3255   unsigned int stats_meta = 0;
3256   unsigned long long stats_meta_len = 0;
3257   unsigned int stats_meta_store = 0;
3258   unsigned long long stats_meta_store_len = 0;
3259   unsigned int stats_meta_direct = 0;
3260   unsigned long long stats_meta_direct_len = 0;
3261
3262   if (fread(magic, 16, 1, fp) != 1 || memcmp(magic, "OBScpio", 7) != 0)
3263     return;
3264   for (;;)
3265     {
3266       if (meta && !metal)
3267         {
3268           meta = 0;
3269           oldoffset = oldoffset_meta;
3270         }
3271       if (meta)
3272         l = expandobscpio_next_mem(&meta, &metal);
3273       else
3274         l = expandobscpio_next(fp);
3275       if (l == (unsigned long long)(-1))
3276         return;
3277       if (l < 16)
3278         {
3279           if (meta)
3280             return;
3281           if (l == 1)
3282             {
3283               printf("end\n");
3284               break;
3285             }
3286           if (l == 2 || l == 3 || l == 4)       /* CPIO HEADER */
3287             {
3288               printf("cpio%d", (int)l);
3289               stats_cpio++;
3290               for (;;)
3291                 {
3292                   int c = getc(fp);
3293                   if (c == EOF)
3294                     return;
3295                   stats_cpio_len++;
3296                   if (c == 0)
3297                     {
3298                       printf(" (0)");
3299                       break;
3300                     }
3301                   if (c < 128)
3302                     printf(" [%d]", c);
3303                   else if (c >= 252)
3304                     {
3305                       printf(" (%d)", c - 251);
3306                       break;
3307                     }
3308                   else
3309                     {
3310                       c -= 128;
3311                       printf(" %d", c);
3312                       stats_cpio_len += c;
3313                       while (c--)
3314                         if (getc(fp) == EOF)
3315                           return;
3316                     }
3317                 }
3318               printf("\n");
3319             }
3320           else if (l == 5 || l == 6 || l == 7)  /* ZERO */
3321             {
3322               printf("zero %d\n", (int)l - 4);
3323               stats_zero++;
3324               stats_zero_len += (int)l - 4;
3325             }
3326           else if (l == 15)                             /* META */
3327             {
3328               l = expandobscpio_next(fp);
3329               if (l == (unsigned long long)(-1))
3330                 return;
3331               if (l < 16 || l > sizeof(metabuf))
3332                 return;
3333               o = expandobscpio_next(fp);
3334               if (o == (unsigned long long)(-1))
3335                 return;
3336               o = decodeoffset(oldoffset, o);
3337               oldoffset = o + l;
3338               printf("meta %#llx %llu\n", o, l);
3339               stats_meta++;
3340               stats_meta_len += l;
3341               if (withmeta)
3342                 {
3343                   oldoffset_meta = o + l;
3344                   oldoffset = 0;
3345                   if (pread(fdstore, metabuf, (size_t)l, (off_t)o) != (size_t)l)
3346                     return;
3347                   metal = (unsigned int)l;
3348                   meta = metabuf;
3349                 }
3350             }
3351           else
3352             return;
3353           continue;
3354         }
3355       if (meta)
3356         printf("    ");
3357       if (l < 256)
3358         {
3359           l -= 16;
3360           printf("direct %d\n", (int)l);
3361           if (meta)
3362             {
3363               stats_meta_direct++;
3364               stats_meta_direct_len += l;
3365             }
3366           else
3367             {
3368               stats_direct++;
3369               stats_direct_len += l;
3370             }
3371           if (meta)
3372             {
3373               if (l > metal)
3374                 return;
3375               metal -= l;
3376               meta += l;
3377             }
3378           else
3379             {
3380               while (l--)
3381                 if (getc(fp) == EOF)
3382                   return;
3383             }
3384           continue;
3385         }
3386       l -= 256;
3387       if (meta)
3388         o = expandobscpio_next_mem(&meta, &metal);
3389       else
3390         o = expandobscpio_next(fp);
3391       if (o == (unsigned long long)(-1))
3392         return;
3393       o = decodeoffset(oldoffset, o);
3394       oldoffset = o + l;
3395       printf("store %#llx %llu\n", o, l);
3396       if (meta)
3397         {
3398           stats_meta_store++;
3399           stats_meta_store_len += l;
3400         }
3401       else
3402         {
3403           stats_store++;
3404           stats_store_len += l;
3405         }
3406     }
3407   printf("stats cpio %u len %llu\n", stats_cpio, stats_cpio_len);
3408   printf("stats direct %u len %llu\n", stats_direct, stats_direct_len);
3409   if (withmeta)
3410     printf("stats meta_direct %u len %llu\n", stats_meta_direct, stats_meta_direct_len);
3411   printf("stats store %u len %llu\n", stats_store, stats_store_len);
3412   if (withmeta)
3413     printf("stats meta_store %u len %llu\n", stats_meta_store, stats_meta_store_len);
3414   printf("stats zero %u len %llu\n", stats_zero, stats_zero_len);
3415   printf("stats meta %u len %llu\n", stats_meta, stats_meta_len);
3416   if (withmeta)
3417     printf("stats instr %u\n", stats_cpio + stats_direct + stats_store + stats_zero + stats_meta + 1 + stats_meta_direct + stats_meta_store);
3418   printf("stats file_instr %u\n", stats_cpio + stats_direct + stats_store + stats_zero + stats_meta + 1);
3419   printf("stats file_data %lld\n", stats_cpio_len + stats_direct_len);
3420   printf("stats file_size %lld\n", (unsigned long long)ftell(fp));
3421 }
3422
3423 MODULE = BSSolv         PACKAGE = BSSolv
3424
3425 void
3426 depsort(HV *deps, SV *mapp, SV *cycp, ...)
3427     PPCODE:
3428         {
3429             int i, j, k, cy, cycstart, nv;
3430             SV *sv, **svp;
3431             Id id, *e;
3432             Id *mark;
3433             char **names;
3434             Hashtable ht;
3435             Hashval h, hh, hm;
3436             HV *mhv = 0;
3437
3438             Queue edata;
3439             Queue vedge;
3440             Queue todo;
3441             Queue cycles;
3442
3443             if (items == 3)
3444               XSRETURN_EMPTY; /* nothing to sort */
3445             if (items == 4)
3446               {
3447                 /* only one item */
3448                 char *s = SvPV_nolen(ST(3));
3449                 EXTEND(SP, 1);
3450                 sv = newSVpv(s, 0);
3451                 PUSHs(sv_2mortal(sv));
3452                 XSRETURN(1); /* nothing to sort */
3453               }
3454
3455             if (mapp && SvROK(mapp) && SvTYPE(SvRV(mapp)) == SVt_PVHV)
3456               mhv = (HV *)SvRV(mapp);
3457
3458             queue_init(&edata);
3459             queue_init(&vedge);
3460             queue_init(&todo);
3461             queue_init(&cycles);
3462
3463             hm = mkmask(items);
3464             ht = solv_calloc(hm + 1, sizeof(*ht));
3465             names = solv_calloc(items, sizeof(char *));
3466             nv = 1;
3467             for (i = 3; i < items; i++)
3468               {
3469                 char *s = SvPV_nolen(ST(i));
3470                 h = strhash(s) & hm;
3471                 hh = HASHCHAIN_START;
3472                 while ((id = ht[h]) != 0)
3473                   {
3474                     if (!strcmp(names[id], s))
3475                       break;
3476                     h = HASHCHAIN_NEXT(h, hh, hm);
3477                   }
3478                 if (id)
3479                   continue;     /* had that one before, ignore */
3480                 id = nv++;
3481                 ht[h] = id;
3482                 names[id] = s;
3483               }
3484
3485             /* we now know all vertices, create edges */
3486             queue_push(&vedge, 0);
3487             queue_push(&edata, 0);
3488             for (i = 1; i < nv; i++)
3489               {
3490                 svp = hv_fetch(deps, names[i], strlen(names[i]), 0);
3491                 sv = svp ? *svp : 0;
3492                 queue_push(&vedge, edata.count);
3493                 if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV)
3494                   {
3495                     AV *av = (AV *)SvRV(sv);
3496                     for (j = 0; j <= av_len(av); j++)
3497                       {
3498                         char *s;
3499                         STRLEN slen;
3500
3501                         svp = av_fetch(av, j, 0);
3502                         if (!svp)
3503                           continue;
3504                         sv = *svp;
3505                         s = SvPV(sv, slen);
3506                         if (!s)
3507                           continue;
3508                         if (mhv)
3509                           {
3510                             /* look up in dep map */
3511                             svp = hv_fetch(mhv, s, slen, 0);
3512                             if (svp)
3513                               {
3514                                 s = SvPV(*svp, slen);
3515                                 if (!s)
3516                                   continue;
3517                               }
3518                           }
3519                         /* look up in hash */
3520                         h = strhash(s) & hm;
3521                         hh = HASHCHAIN_START;
3522                         while ((id = ht[h]) != 0)
3523                           {
3524                             if (!strcmp(names[id], s))
3525                               break;
3526                             h = HASHCHAIN_NEXT(h, hh, hm);
3527                           }
3528                         if (!id)
3529                           continue;     /* not known, ignore */
3530                         if (id == i)
3531                           continue;     /* no self edge */
3532                         queue_push(&edata, id);
3533                       }
3534                   }
3535                 queue_push(&edata, 0);
3536               }
3537             solv_free(ht);
3538
3539             if (0)
3540               {
3541                 printf("vertexes: %d\n", vedge.count - 1);
3542                 for (i = 1; i < vedge.count; i++)
3543                   {
3544                     printf("%d %s:", i, names[i]);
3545                     Id *e = edata.elements + vedge.elements[i];
3546                     for (; *e; e++)
3547                       printf(" %d", *e);
3548                     printf("\n");
3549                   }
3550               }
3551                 
3552
3553             /* now everything is set up, sort em! */
3554             mark = solv_calloc(vedge.count, sizeof(Id));
3555             for (i = vedge.count - 1; i; i--)
3556               queue_push(&todo, i);
3557             EXTEND(SP, vedge.count - 1);
3558             while (todo.count)
3559               {
3560                 i = queue_pop(&todo);
3561                 // printf("check %d\n", i);
3562                 if (i < 0)
3563                   {
3564                     i = -i;
3565                     mark[i] = 2;
3566                     sv = newSVpv(names[i], 0);
3567                     PUSHs(sv_2mortal(sv));
3568                     continue;
3569                   }
3570                 if (mark[i] == 2)
3571                   continue;
3572                 if (mark[i] == 0)
3573                   {
3574                     int edgestovisit = 0;
3575                     Id *e = edata.elements + vedge.elements[i];
3576                     for (; *e; e++)
3577                       {
3578                         if (*e == -1)
3579                           continue;     /* broken */
3580                         if (mark[*e] == 2)
3581                           continue;
3582                         if (!edgestovisit++)
3583                           queue_push(&todo, -i);
3584                         queue_push(&todo, *e);
3585                       }
3586                     if (!edgestovisit)
3587                       {
3588                         mark[i] = 2;
3589                         sv = newSVpv(names[i], 0);
3590                         PUSHs(sv_2mortal(sv));
3591                       }
3592                     else
3593                       mark[i] = 1;
3594                     continue;
3595                   }
3596                 /* oh no, we found a cycle, record and break it */
3597                 cy = cycles.count;
3598                 for (j = todo.count - 1; j >= 0; j--)
3599                   if (todo.elements[j] == -i)
3600                     break;
3601                 cycstart = j;
3602                 // printf("cycle:\n");
3603                 for (j = cycstart; j < todo.count; j++)
3604                   if (todo.elements[j] < 0)
3605                     {
3606                       k = -todo.elements[j];
3607                       mark[k] = 0;
3608                       queue_push(&cycles, k);
3609                       // printf("  %d\n", k);
3610                     }
3611                 queue_push(&cycles, 0);
3612                 todo.elements[cycstart] = i;
3613                 /* break it */
3614                 for (k = cy; cycles.elements[k]; k++)
3615                   ;
3616                 if (!cycles.elements[k])
3617                   k = cy;
3618                 j = cycles.elements[k + 1] ? cycles.elements[k + 1] : cycles.elements[cy];
3619                 k = cycles.elements[k];
3620                 /* breaking edge from k -> j */
3621                 // printf("break %d -> %d\n", k, j);
3622                 e = edata.elements + vedge.elements[k];
3623                 for (; *e; e++)
3624                   if (*e == j)
3625                     break;
3626                 if (!*e)
3627                   abort();
3628                 *e = -1;
3629                 todo.count = cycstart + 1;
3630               }
3631
3632             /* recored cycles */
3633             if (cycles.count && cycp && SvROK(cycp) && SvTYPE(SvRV(cycp)) == SVt_PVAV)
3634               {
3635                 AV *av = (AV *)SvRV(cycp);
3636                 for (i = 0; i < cycles.count;)
3637                   {
3638                     AV *av2 = newAV();
3639                     for (; cycles.elements[i]; i++)
3640                       {
3641                         SV *sv = newSVpv(names[cycles.elements[i]], 0);
3642                         av_push(av2, sv);
3643                       }
3644                     av_push(av, newRV_noinc((SV*)av2));
3645                     i++;
3646                   }
3647               }
3648             queue_free(&cycles);
3649
3650             queue_free(&edata);
3651             queue_free(&vedge);
3652             queue_free(&todo);
3653             solv_free(mark);
3654             solv_free(names);
3655         }
3656
3657 void
3658 gen_meta(AV *subp, ...)
3659     PPCODE:
3660         {
3661             Hashtable ht;
3662             Hashval h, hh, hm;
3663             char **subpacks;
3664             struct metaline *lines, *lp;
3665             int nlines;
3666             int i, j, cycle, ns;
3667             char *s, *s2, *lo;
3668             Id id;
3669             Queue cycles;
3670             Id cycles_buf[64];
3671
3672             if (items == 1)
3673               XSRETURN_EMPTY; /* nothing to generate */
3674
3675             queue_init_buffer(&cycles, cycles_buf, sizeof(cycles_buf)/sizeof(*cycles_buf));
3676             hm = mkmask(av_len(subp) + 2);
3677             ht = solv_calloc(hm + 1, sizeof(*ht));
3678             subpacks = solv_calloc(av_len(subp) + 2, sizeof(char *));
3679             for (j = 0; j <= av_len(subp); j++)
3680               {
3681                 SV **svp = av_fetch(subp, j, 0);
3682                 if (!svp)
3683                   continue;
3684                 s = SvPV_nolen(*svp);
3685                 h = strhash(s) & hm;
3686                 hh = HASHCHAIN_START;
3687                 while ((id = ht[h]) != 0)
3688                   h = HASHCHAIN_NEXT(h, hh, hm);
3689                 ht[h] = j + 1;
3690                 subpacks[j + 1] = s;
3691               }
3692
3693             lines = solv_calloc(items - 1, sizeof(*lines));
3694             nlines = items - 1;
3695             /* lines are of the form "md5sum  pkg/pkg..." */
3696             for (i = 0, lp = lines; i < nlines; i++, lp++)
3697               {
3698                 s = SvPV_nolen(ST(i + 1));
3699                 if (strlen(s) < 35 || s[32] != ' ' || s[33] != ' ')
3700                   croak("gen_meta: bad line %s\n", s);
3701                 /* count '/' */
3702                 lp->l = s;
3703                 ns = 0;
3704                 cycle = 0;
3705                 lo = s + 34;
3706                 for (s2 = lo; *s2; s2++)
3707                   if (*s2 == '/')
3708                     {
3709                       if (!cycle)       
3710                         {
3711                           *s2 = 0;
3712                           h = strhash(lo) & hm;
3713                           hh = HASHCHAIN_START;
3714                           while ((id = ht[h]) != 0)
3715                             {
3716                               if (!strcmp(lo, subpacks[id]))
3717                                 break;
3718                               h = HASHCHAIN_NEXT(h, hh, hm);
3719                             }
3720                           *s2 = '/';
3721                           if (id)
3722                             cycle = 1 + ns;
3723                         }
3724                       ns++;
3725                       lo = s2 + 1;
3726                     }
3727                 if (!cycle)
3728                   {
3729                     h = strhash(lo) & hm;
3730                     hh = HASHCHAIN_START;
3731                     while ((id = ht[h]) != 0)
3732                       {
3733                         if (!strcmp(lo, subpacks[id]))
3734                           break;
3735                         h = HASHCHAIN_NEXT(h, hh, hm);
3736                       }
3737                     if (id)
3738                       cycle = 1 + ns;
3739                   }
3740                 if (cycle)
3741                   {
3742                     lp->killed = 1;
3743                     if (cycle > 1)      /* ignore self cycles */
3744                       queue_push(&cycles, i);
3745                   }
3746                 lp->nslash = ns;
3747                 lp->lastoff = lo - s;
3748               }
3749             solv_free(ht);
3750             solv_free(subpacks);
3751
3752             /* if we found cycles, prune em */
3753             if (cycles.count)
3754               {
3755                 char *cycledata = 0;
3756                 int cycledatalen = 0;
3757
3758                 cycledata = solv_extend(cycledata, cycledatalen, 1, 1, 255);
3759                 cycledata[0] = 0;
3760                 cycledatalen += 1;
3761                 hm = mkmask(cycles.count);
3762                 ht = solv_calloc(hm + 1, sizeof(*ht));
3763                 for (i = 0; i < cycles.count; i++)
3764                   {
3765                     char *se;
3766                     s = lines[cycles.elements[i]].l + 34;
3767                     se = strchr(s, '/');
3768                     if (se)
3769                       *se = 0;
3770                     h = strhash(s) & hm;
3771                     hh = HASHCHAIN_START;
3772                     while ((id = ht[h]) != 0)
3773                       {
3774                         if (!strcmp(s, cycledata + id))
3775                           break;
3776                         h = HASHCHAIN_NEXT(h, hh, hm);
3777                       }
3778                     if (id)
3779                       continue;
3780                     cycledata = solv_extend(cycledata, cycledatalen, strlen(s) + 1, 1, 255);
3781                     ht[h] = cycledatalen;
3782                     strcpy(cycledata + cycledatalen, s);
3783                     cycledatalen += strlen(s) + 1;
3784                     if (se)
3785                       *se = '/';
3786                   }
3787                 for (i = 0, lp = lines; i < nlines; i++, lp++)
3788                   {
3789                     if (lp->killed || !lp->nslash)
3790                       continue;
3791                     lo = strchr(lp->l + 34, '/') + 1;
3792                     for (s2 = lo; *s2; s2++)
3793                       if (*s2 == '/')
3794                         {
3795                           *s2 = 0;
3796                           h = strhash(lo) & hm;
3797                           hh = HASHCHAIN_START;
3798                           while ((id = ht[h]) != 0)
3799                             {
3800                               if (!strcmp(lo, cycledata + id))
3801                                 break;
3802                               h = HASHCHAIN_NEXT(h, hh, hm);
3803                             }
3804                           *s2 = '/';
3805                           if (id)
3806                             {
3807                               lp->killed = 1;
3808                               break;
3809                             }
3810                           lo = s2 + 1;
3811                         }
3812                     if (lp->killed)
3813                       continue;
3814                     h = strhash(lo) & hm;
3815                     hh = HASHCHAIN_START;
3816                     while ((id = ht[h]) != 0)
3817                       {
3818                         if (!strcmp(lo, cycledata + id))
3819                           break;
3820                         h = HASHCHAIN_NEXT(h, hh, hm);
3821                       }
3822                     if (id)
3823                       {
3824                         lp->killed = 1;
3825                       }
3826                   }
3827                 solv_free(ht);
3828                 cycledata = solv_free(cycledata);
3829                 queue_free(&cycles);
3830               }
3831
3832             /* cycles are pruned, now sort array */
3833             if (nlines > 1)
3834               qsort(lines, nlines, sizeof(*lines), metacmp);
3835
3836             hm = mkmask(nlines);
3837             ht = solv_calloc(hm + 1, sizeof(*ht));
3838             for (i = 0, lp = lines; i < nlines; i++, lp++)
3839               {
3840                 if (lp->killed)
3841                   continue;
3842                 s = lp->l;
3843                 h = strnhash(s, 10);
3844                 h = strhash_cont(s + lp->lastoff, h) & hm;
3845                 hh = HASHCHAIN_START;
3846                 while ((id = ht[h]) != 0)
3847                   {
3848                     struct metaline *lp2 = lines + (id - 1);
3849                     if (!strncmp(lp->l, lp2->l, 32) && !strcmp(lp->l + lp->lastoff, lp2->l + lp2->lastoff))
3850                       break;
3851                     h = HASHCHAIN_NEXT(h, hh, hm);
3852                   }
3853                 if (id)
3854                   lp->killed = 1;
3855                 else
3856                   ht[h] = i + 1;
3857               }
3858             solv_free(ht);
3859             j = 0;
3860             for (i = 0, lp = lines; i < nlines; i++, lp++)
3861               if (!lp->killed)
3862                 j++;
3863             EXTEND(SP, j);
3864             for (i = 0, lp = lines; i < nlines; i++, lp++)
3865               {
3866                 SV *sv;
3867                 if (lp->killed)
3868                   continue;
3869                 sv = newSVpv(lp->l, 0);
3870                 PUSHs(sv_2mortal(sv));
3871               }
3872             solv_free(lines);
3873         }
3874
3875 SV *
3876 thawcache(SV *sv)
3877     CODE:
3878         unsigned char *src;
3879         STRLEN srcl;
3880         if (!SvPOK(sv)) {
3881             croak("thaw: argument is not a string\n");
3882             XSRETURN_UNDEF;
3883         }
3884         src = (unsigned char *)SvPV(sv, srcl);
3885         if (srcl < 7 || src[0] != 'p' || src[1] != 's' || src[2] != 't' || src[3] != '0') {
3886             croak("thaw: argument is not a perl storable\n");
3887             XSRETURN_UNDEF;
3888         }
3889         if ((src[4] & 1) != 1) {
3890             croak("thaw: argument is not a perl storable in network order\n");
3891             XSRETURN_UNDEF;
3892         }
3893         if (src[4] < 5) {
3894             croak("thaw: argument is a perl storable with a too old version\n");
3895             XSRETURN_UNDEF;
3896         }
3897         src += 6;
3898         srcl -= 6;
3899         sv = retrieve(&src, &srcl, 0);
3900         if (sv == 0 || srcl) {
3901             croak("thaw: corrupt storable\n");
3902             XSRETURN_UNDEF;
3903         }
3904         RETVAL = newRV_noinc(sv);
3905     OUTPUT:
3906         RETVAL
3907
3908 int
3909 isobscpio(const char *file)
3910     CODE:
3911         int fd;
3912         RETVAL = 0;
3913         if ((fd = open(file, O_RDONLY)) != -1) {
3914             unsigned char magic[16];
3915             if (read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7))
3916                 RETVAL = 1;
3917             close(fd);
3918         }
3919     OUTPUT:
3920         RETVAL
3921
3922
3923 void
3924 obscpiostat(const char *file)
3925     PPCODE:
3926         {
3927             int fd;
3928             struct stat st;
3929             if ((fd = open(file, O_RDONLY)) != -1) {
3930                 if (!fstat(fd, &st)) {
3931                     unsigned char magic[16];
3932                     if (read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7)) {
3933                         st.st_size = getu48(magic + 10);
3934                     }
3935                     EXTEND(SP, 10);
3936                     PUSHs(&PL_sv_undef);
3937                     PUSHs(&PL_sv_undef);
3938                     PUSHs(sv_2mortal(newSVuv((UV)st.st_mode)));
3939                     PUSHs(sv_2mortal(newSVuv((UV)st.st_nlink)));
3940                     PUSHs(&PL_sv_undef);
3941                     PUSHs(&PL_sv_undef);
3942                     PUSHs(&PL_sv_undef);
3943 #if IVSIZE > 4
3944                     PUSHs(sv_2mortal(newSVuv((UV)st.st_size)));
3945 #else
3946                     PUSHs(sv_2mortal(newSVnv((double)st.st_size)));
3947 #endif
3948                     PUSHs(sv_2mortal(newSVuv((UV)st.st_atime)));
3949                     PUSHs(sv_2mortal(newSVuv((UV)st.st_mtime)));
3950                     PUSHs(sv_2mortal(newSVuv((UV)st.st_ctime)));
3951                 }
3952                 close(fd);
3953             }
3954         }
3955
3956 int
3957 obscpioopen(const char *file, const char *store, SV *gvrv, const char *tmpdir = 0)
3958     CODE:
3959         int fd;
3960         GV *gv;
3961         if (!SvROK(gvrv) || SvTYPE(SvRV(gvrv)) != SVt_PVGV) {
3962             croak("obscpioopen needs a GV reference\n");
3963         }
3964         if (tmpdir && strlen(tmpdir) > 200) {
3965             croak("tmpdir too long\n");
3966         }
3967         gv = (GV *)SvRV(gvrv);
3968         RETVAL = 0;
3969         if ((fd = open(file, O_RDONLY)) != -1) {
3970             unsigned char magic[16];
3971             if (read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7)) {
3972                 char template[256];
3973                 int nfd = -1;
3974                 int sfd;
3975                 if ((sfd = open(store, O_RDONLY)) != -1) {
3976                     if (tmpdir) {
3977                         strcpy(template, tmpdir);
3978                         strcat(template, "/obscpioopen-XXXXXX");
3979                     } else {
3980                         strcpy(template, "/var/tmp/obscpioopen-XXXXXX");
3981                     }
3982                     nfd = mkstemp(template);
3983                     if (nfd != -1) {
3984                         FILE *fp = 0, *nfp = 0;
3985                         unlink(template);
3986                         lseek(fd, 0, SEEK_SET);
3987                         if ((fp = fdopen(fd, "r")) == 0)
3988                             close(fd);
3989                         if ((nfp = fdopen(nfd, "w+")) == 0)
3990                             close(nfd);
3991                         if (fp && nfp && expandobscpio(fp, sfd, nfp)) {
3992                             nfd = dup(nfd);
3993                             if (fclose(nfp)) {
3994                                 close(nfd);
3995                                 nfd = -1;
3996                             }
3997                             nfp = 0;
3998                         } else {
3999                             nfd = -1;
4000                         }
4001                         if (fp)
4002                             fclose(fp);
4003                         if (nfp)
4004                             fclose(nfp);
4005                         fd = -1;
4006                     }
4007                     close(sfd);
4008                 }
4009                 if (fd != -1)
4010                     close(fd);
4011                 fd = nfd;
4012             }
4013             if (fd != -1) {
4014                 IO * io = GvIOn(gv);
4015                 PerlIO *fp;
4016
4017                 lseek(fd, 0, SEEK_SET);
4018                 fp = PerlIO_fdopen(fd, "rb");
4019                 if (fp) {
4020                     IoIFP(io) = fp;
4021                     RETVAL = 1;
4022                 }
4023             }
4024         }
4025         
4026     OUTPUT:
4027         RETVAL
4028
4029 int
4030 expandobscpio(const char *file, const char *store, const char *tmpfile)
4031     CODE:
4032         {
4033             int fd, nfd, sfd;
4034             RETVAL = 0;
4035
4036             unlink(tmpfile);
4037             if ((fd = open(file, O_RDONLY)) != -1) {
4038                 unsigned char magic[16];
4039                 if (!(read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7))) {
4040                     close(fd);
4041                     fd = -1;
4042                     if (link(file, tmpfile) == 0 && (fd = open(tmpfile, O_RDONLY)) != -1) {
4043                         if (read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7)) {
4044                             unlink(tmpfile);
4045                         } else {
4046                             close(fd);
4047                             fd = -1;
4048                             RETVAL = 1;
4049                         }
4050                     }
4051                 }
4052                 if (fd != -1) {
4053                     if ((sfd = open(store, O_RDONLY)) != -1) {
4054                         FILE *fp;
4055                         lseek(fd, 0, SEEK_SET);
4056                         if ((fp = fdopen(fd, "r")) == 0)
4057                             close(fd);
4058                         if (fp && (nfd = open(tmpfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0666)) != -1) {
4059                             FILE *nfp;
4060                             if ((nfp = fdopen(nfd, "w")) == 0)
4061                                 close(nfd);
4062                             if (nfp && expandobscpio(fp, sfd, nfp)) {
4063                                 if (!fclose(nfp))
4064                                     RETVAL = 1;
4065                                 else
4066                                     unlink(tmpfile);
4067                                 nfp = 0;
4068                             } else
4069                                 unlink(tmpfile);
4070                             if (nfp)
4071                                 fclose(nfp);
4072                         }
4073                         if (fp)
4074                           fclose(fp);
4075                         close(sfd);
4076                     } else {
4077                       close(fd);
4078                     }
4079                 }
4080             }
4081         }
4082     OUTPUT:
4083         RETVAL
4084
4085
4086 int
4087 makeobscpio(const char *in, const char *store, const char *out)
4088     CODE:
4089         {
4090             FILE *fpin, *fpout;
4091             struct stat st;
4092             int fdstore;
4093             RETVAL = 0;
4094             if ((fpin = fopen(in, "r")) == 0) { 
4095                 perror(in);
4096             } else if (fstat(fileno(fpin), &st) != 0) {
4097                 perror(in);
4098                 fclose(fpin);
4099             } else if ((fpout = fopen(out, "w")) == 0) {
4100                 perror(out);
4101                 fclose(fpin);
4102             } else if ((fdstore = open(store, O_RDWR|O_CREAT, 0666)) == -1) {
4103                 perror(store);
4104                 fclose(fpin);
4105                 fclose(fpout);
4106             } else {
4107                 int gotlock = 0;
4108                 while (!gotlock) {
4109                     if (flock(fdstore, LOCK_EX) == 0)
4110                         gotlock = 1;
4111                     else if (errno != EINTR)
4112                         break;
4113                 }
4114                 if (gotlock) {
4115                     struct deltastore store;
4116                     if (readdeltastore(&store, fdstore, 0, (unsigned long long)st.st_size)) {
4117                         int r = makedelta(&store, fpin, fpout, (unsigned long long)st.st_size);
4118 #if 0
4119   printf("after makedelta: have %d entries, hash size %d\n", store.hf, store.hm + 1);
4120 #endif
4121                         if (fsync(store.fd))
4122                           r = 0;
4123                         freedeltastore(&store);
4124                         if (r) {
4125                             settimes(fileno(fpout), &st);
4126                             RETVAL = 1;
4127                         }
4128                     }
4129                 }
4130                 close(fdstore);
4131                 fclose(fpin);
4132                 fclose(fpout);
4133             }
4134         }
4135     OUTPUT:
4136         RETVAL
4137
4138 void
4139 obscpiostorestats(const char *store)
4140     CODE:
4141         {
4142             int fdstore;
4143
4144             if ((fdstore = open(store, O_RDONLY)) == -1)
4145                 perror(store);
4146             else {
4147                 int gotlock = 0;
4148                 while (!gotlock) {
4149                     if (flock(fdstore, LOCK_EX) == 0)
4150                         gotlock = 1;
4151                     else if (errno != EINTR)
4152                         break;
4153                 }
4154                 if (gotlock) {
4155                     struct deltastore store;
4156                     if (readdeltastore(&store, fdstore, 1, (unsigned long long)0)) {
4157                         printdeltastorestats(&store);
4158                         fsync(store.fd);
4159                         freedeltastore(&store);
4160                     }
4161                 }
4162                 close(fdstore);
4163             }
4164         }
4165
4166 void
4167 obscpioinstr(const char *file, const char *store = 0)
4168     CODE:
4169         {
4170             FILE *fp;
4171             int fdstore = -1;
4172             if ((fp = fopen(file, "r")) == 0)
4173                 perror(file);
4174             else {
4175                 if (store) {
4176                     fdstore = open(store, O_RDONLY);
4177                     if (fdstore == -1)
4178                         perror(store);
4179                 }
4180                 printobscpioinstr(fp, fdstore, fdstore == -1 ? 0 : 1);
4181                 fclose(fp);
4182                 if (fdstore != -1)
4183                     close(fdstore);
4184             }
4185         }
4186
4187
4188 MODULE = BSSolv         PACKAGE = BSSolv::pool          PREFIX = pool
4189
4190 PROTOTYPES: ENABLE
4191
4192 BSSolv::pool
4193 new(char *packname = "BSSolv::pool")
4194     CODE:
4195         {
4196             Pool *pool = pool_create();
4197             set_disttype(pool, DISTTYPE_RPM);
4198             buildservice_id = pool_str2id(pool, "buildservice:id", 1);
4199             buildservice_repocookie= pool_str2id(pool, "buildservice:repocookie", 1);
4200             buildservice_external = pool_str2id(pool, "buildservice:external", 1);
4201             buildservice_dodurl = pool_str2id(pool, "buildservice:dodurl", 1);
4202             expander_directdepsend = pool_str2id(pool, "-directdepsend--", 1);
4203             buildservice_dodcookie = pool_str2id(pool, "buildservice:dodcookie", 1);
4204             pool_freeidhashes(pool);
4205             RETVAL = pool;
4206         }
4207     OUTPUT:
4208         RETVAL
4209
4210 void
4211 settype(BSSolv::pool pool, char *type)
4212     CODE:
4213         if (!strcmp(type, "rpm"))
4214           set_disttype(pool, DISTTYPE_RPM);
4215 #ifdef DISTTYPE_DEB
4216         else if (!strcmp(type, "deb"))
4217           set_disttype(pool, DISTTYPE_DEB);
4218 #endif
4219 #ifdef DISTTYPE_ARCH
4220         else if (!strcmp(type, "arch"))
4221           set_disttype(pool, DISTTYPE_ARCH);
4222 #endif
4223         else
4224           croak("settype: unknown type '%s'\n", type);
4225
4226
4227 BSSolv::repo
4228 repofromfile(BSSolv::pool pool, char *name, char *filename)
4229     CODE:
4230         FILE *fp;
4231         fp = fopen(filename, "r");
4232         if (!fp) {
4233             croak("%s: %s\n", filename, Strerror(errno));
4234             XSRETURN_UNDEF;
4235         }
4236         RETVAL = repo_create(pool, name);
4237         repo_add_solv(RETVAL, fp, 0);
4238         fclose(fp);
4239     OUTPUT:
4240         RETVAL
4241
4242 BSSolv::repo
4243 repofromstr(BSSolv::pool pool, char *name, SV *sv)
4244     CODE:
4245         FILE *fp;
4246         STRLEN len;
4247         char *buf;
4248         buf = SvPV(sv, len);
4249         if (!buf)
4250             croak("repofromstr: undef string\n");
4251         fp = fmemopen(buf, len, "r");
4252         if (!fp) {
4253             croak("fmemopen failed\n");
4254             XSRETURN_UNDEF;
4255         }
4256         RETVAL = repo_create(pool, name);
4257         repo_add_solv(RETVAL, fp, 0);
4258         fclose(fp);
4259     OUTPUT:
4260         RETVAL
4261
4262 BSSolv::repo
4263 repofrombins(BSSolv::pool pool, char *name, char *dir, ...)
4264     CODE:
4265         {
4266             int i;
4267             Repo *repo;
4268             Repodata *data;
4269             repo = repo_create(pool, name);
4270             data = repo_add_repodata(repo, 0);
4271             for (i = 3; i + 1 < items; i += 2)
4272               {
4273                 STRLEN sl;
4274                 char *s = SvPV(ST(i), sl);
4275                 char *sid = SvPV_nolen(ST(i + 1));
4276                 if (sl < 4)
4277                   continue;
4278                 if (strcmp(s + sl - 4, ".rpm")
4279                     && strcmp(s + sl - 4, ".deb")
4280 #ifdef ARCH_ADD_WITH_PKGID
4281                     && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.gz"))
4282                     && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.xz"))
4283 #endif
4284                    )
4285                   continue;
4286                 if (sl >= 10 && !strcmp(s + sl - 10, ".patch.rpm"))
4287                   continue;
4288                 if (sl >= 10 && !strcmp(s + sl - 10, ".nosrc.rpm"))
4289                   continue;
4290                 if (sl >= 8 && !strcmp(s + sl - 8, ".src.rpm"))
4291                   continue;
4292                 repodata_addbin(data, dir, s, (int)sl, sid);
4293               }
4294             repo_set_str(repo, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
4295             repo_internalize(repo);
4296             RETVAL = repo;
4297         }
4298     OUTPUT:
4299         RETVAL
4300
4301 BSSolv::repo
4302 repofromdata(BSSolv::pool pool, char *name, HV *rhv)
4303     CODE:
4304         {
4305             Repo *repo;
4306             Repodata *data;
4307             repo = repo_create(pool, name);
4308             data = repo_add_repodata(repo, 0);
4309             data2solvables(repo, data, rhv);
4310             if (name && !strcmp(name, "/external/"))
4311               repodata_set_void(data, SOLVID_META, buildservice_external);
4312             repo_internalize(repo);
4313             RETVAL = repo;
4314         }
4315     OUTPUT:
4316         RETVAL
4317
4318 void
4319 createwhatprovides(BSSolv::pool pool)
4320     CODE:
4321         if (pool->considered)
4322           {
4323             map_free(pool->considered);
4324             solv_free(pool->considered);
4325           }
4326         pool->considered = solv_calloc(sizeof(Map), 1);
4327         create_considered(pool, 0, pool->considered);
4328         pool_createwhatprovides(pool);
4329
4330 void
4331 setdebuglevel(BSSolv::pool pool, int level)
4332     CODE:
4333         pool_setdebuglevel(pool, level);
4334
4335 void
4336 whatprovides(BSSolv::pool pool, char *str)
4337     PPCODE:
4338         {
4339             Id p, pp, id;
4340             id = dep2id(pool, str);
4341             if (id)
4342               FOR_PROVIDES(p, pp, id)
4343                 XPUSHs(sv_2mortal(newSViv((IV)p)));
4344         }
4345
4346 void
4347 whatrequires(BSSolv::pool pool, char *str)
4348     PPCODE:
4349         {
4350             Id p, id;
4351             Id *pp;
4352             Solvable *s;
4353             id = dep2id(pool, str);
4354             if (id)
4355               {
4356                 for (p = 2; p < pool->nsolvables; p++)
4357                   {
4358                     if (!MAPTST(pool->considered, p))
4359                       continue;
4360                     s = pool->solvables + p;
4361                     if (!s->requires)
4362                       continue;
4363                     for (pp = s->repo->idarraydata + s->requires; *pp; pp++)
4364                       if (pool_match_dep(pool, id, *pp))
4365                         break;
4366                     if (*pp)
4367                       XPUSHs(sv_2mortal(newSViv((IV)p)));
4368                   }
4369               }
4370         }
4371
4372 void
4373 consideredpackages(BSSolv::pool pool)
4374     PPCODE:
4375         {
4376             int p, nsolv = 0;
4377             for (p = 2; p < pool->nsolvables; p++)
4378               if (MAPTST(pool->considered, p))
4379                 nsolv++;
4380             EXTEND(SP, nsolv);
4381             for (p = 2; p < pool->nsolvables; p++)
4382               if (MAPTST(pool->considered, p))
4383                 PUSHs(sv_2mortal(newSViv((IV)p)));
4384         }
4385         
4386 void
4387 allpackages(BSSolv::pool pool)
4388     PPCODE:
4389         {
4390             int p, nsolv = 0;
4391             for (p = 2; p < pool->nsolvables; p++)
4392               if (pool->solvables[p].repo)
4393                 nsolv++;
4394             EXTEND(SP, nsolv);
4395             for (p = 2; p < pool->nsolvables; p++)
4396               if (pool->solvables[p].repo)
4397                 PUSHs(sv_2mortal(newSViv((IV)p)));
4398         }
4399
4400 const char *
4401 pkg2name(BSSolv::pool pool, int p)
4402     CODE:
4403         RETVAL = pool_id2str(pool, pool->solvables[p].name);
4404     OUTPUT:
4405         RETVAL
4406
4407 const char *
4408 pkg2srcname(BSSolv::pool pool, int p)
4409     CODE:
4410         if (solvable_lookup_void(pool->solvables + p, SOLVABLE_SOURCENAME))
4411           RETVAL = pool_id2str(pool, pool->solvables[p].name);
4412         else
4413           RETVAL = solvable_lookup_str(pool->solvables + p, SOLVABLE_SOURCENAME);
4414     OUTPUT:
4415         RETVAL
4416
4417 const char *
4418 pkg2pkgid(BSSolv::pool pool, int p)
4419     CODE:
4420         {
4421             Id type;
4422             const char *s = solvable_lookup_checksum(pool->solvables + p, SOLVABLE_PKGID, &type);
4423             RETVAL = s;
4424         }
4425     OUTPUT:
4426         RETVAL
4427
4428 const char *
4429 pkg2bsid(BSSolv::pool pool, int p)
4430     CODE:
4431         RETVAL = solvable_lookup_str(pool->solvables + p, buildservice_id);
4432     OUTPUT:
4433         RETVAL
4434
4435 const char *
4436 pkg2reponame(BSSolv::pool pool, int p)
4437     CODE:
4438         {
4439             Repo *repo = pool->solvables[p].repo;
4440             RETVAL = repo ? repo->name : 0;
4441         }
4442     OUTPUT:
4443         RETVAL
4444
4445 const char *
4446 pkg2path(BSSolv::pool pool, int p)
4447     CODE:
4448         {
4449             unsigned int medianr;
4450             RETVAL = solvable_get_location(pool->solvables + p, &medianr);
4451         }
4452     OUTPUT:
4453         RETVAL
4454         
4455 const char *
4456 pkg2fullpath(BSSolv::pool pool, int p, char *myarch)
4457     CODE:
4458         {
4459             unsigned int medianr;
4460             const char *s = solvable_get_location(pool->solvables + p, &medianr);
4461             Repo *repo = pool->solvables[p].repo;
4462             s = pool_tmpjoin(pool, myarch, "/:full/", s);
4463             RETVAL = pool_tmpjoin(pool, repo->name, "/", s);
4464         }
4465     OUTPUT:
4466         RETVAL
4467
4468 int
4469 pkg2sizek(BSSolv::pool pool, int p)
4470     CODE:
4471 #ifdef SOLV_KV_NUM64
4472         RETVAL = solvable_lookup_sizek(pool->solvables + p, SOLVABLE_DOWNLOADSIZE, 0);
4473 #else
4474         RETVAL = solvable_lookup_num(pool->solvables + p, SOLVABLE_DOWNLOADSIZE, 0);
4475 #endif
4476     OUTPUT:
4477         RETVAL
4478
4479 const char *
4480 pkg2checksum(BSSolv::pool pool, int p)
4481     CODE:
4482         {
4483             Id type;
4484             const char *s = solvable_lookup_checksum(pool->solvables + p, SOLVABLE_CHECKSUM, &type);
4485             if (s)
4486                 s = pool_tmpjoin(pool, solv_chksum_type2str(type), ":", s);
4487             RETVAL = s;
4488         }
4489     OUTPUT:
4490         RETVAL
4491
4492 int
4493 verifypkgchecksum(BSSolv::pool pool, int p, char *path)
4494     CODE:
4495         {
4496             Id type;
4497             const unsigned char *cin, *cout;
4498             FILE *fp;
4499             void *cs;
4500             int cslen;
4501             char buf[4096];
4502             size_t len;
4503             int res = 0;
4504
4505             if ((cin = solvable_lookup_bin_checksum(pool->solvables + p, SOLVABLE_CHECKSUM, &type)) != 0) {
4506                 if ((fp = fopen(path, "r")) != 0) {
4507                     if ((cs = solv_chksum_create(type)) != 0) {
4508                         while ((len = fread(buf, 1, sizeof(buf), fp)) > 0)
4509                             solv_chksum_add(cs, buf, len);
4510                         if ((cout = solv_chksum_get(cs, &cslen)) != 0 && cslen && !memcmp(cin, cout, cslen))
4511                             res = 1;
4512                         solv_chksum_free(cs, 0);
4513                     }
4514                     fclose(fp);
4515                 }
4516             }
4517             RETVAL = res;
4518         }
4519     OUTPUT:
4520         RETVAL
4521
4522 HV *
4523 pkg2data(BSSolv::pool pool, int p)
4524     CODE:
4525         {
4526             Solvable *s = pool->solvables + p;
4527             Id id;
4528             const char *ss, *se;
4529             unsigned int medianr;
4530
4531             if (!s->repo)
4532                 XSRETURN_EMPTY;
4533             RETVAL = newHV();
4534             sv_2mortal((SV*)RETVAL);
4535             (void)hv_store(RETVAL, "name", 4, newSVpv(pool_id2str(pool, s->name), 0), 0);
4536             ss = pool_id2str(pool, s->evr);
4537             se = ss;
4538             while (*se >= '0' && *se <= '9')
4539               se++;
4540             if (se != ss && *se == ':' && se[1])
4541               {
4542                 (void)hv_store(RETVAL, "epoch", 5, newSVpvn(ss, se - ss), 0);
4543                 ss = se + 1;
4544               }
4545             se = strrchr(ss, '-');
4546             if (se)
4547               {
4548                 (void)hv_store(RETVAL, "version", 7, newSVpvn(ss, se - ss), 0);
4549                 (void)hv_store(RETVAL, "release", 7, newSVpv(se + 1, 0), 0);
4550               }
4551             else
4552               (void)hv_store(RETVAL, "version", 7, newSVpv(ss, 0), 0);
4553             (void)hv_store(RETVAL, "arch", 4, newSVpv(pool_id2str(pool, s->arch), 0), 0);
4554             exportdeps(RETVAL, "provides", 8, s->repo, s->provides, SOLVABLE_PROVIDES);
4555             exportdeps(RETVAL, "obsoletes", 9, s->repo, s->obsoletes, SOLVABLE_OBSOLETES);
4556             exportdeps(RETVAL, "conflicts", 9, s->repo, s->conflicts, SOLVABLE_CONFLICTS);
4557             exportdeps(RETVAL, "requires", 8, s->repo, s->requires, SOLVABLE_REQUIRES);
4558             exportdeps(RETVAL, "recommends", 10, s->repo, s->recommends, SOLVABLE_RECOMMENDS);
4559             exportdeps(RETVAL, "suggests", 8, s->repo, s->suggests, SOLVABLE_SUGGESTS);
4560             exportdeps(RETVAL, "supplements", 11, s->repo, s->supplements, SOLVABLE_SUPPLEMENTS);
4561             exportdeps(RETVAL, "enhances", 8, s->repo, s->enhances, SOLVABLE_ENHANCES);
4562             if (solvable_lookup_void(s, SOLVABLE_SOURCENAME))
4563               ss = pool_id2str(pool, s->name);
4564             else
4565               ss = solvable_lookup_str(s, SOLVABLE_SOURCENAME);
4566             if (ss)
4567               (void)hv_store(RETVAL, "source", 6, newSVpv(ss, 0), 0);
4568             ss = solvable_get_location(s, &medianr);
4569             if (ss)
4570               (void)hv_store(RETVAL, "path", 4, newSVpv(ss, 0), 0);
4571             ss = solvable_lookup_checksum(s, SOLVABLE_PKGID, &id);
4572             if (ss && id == REPOKEY_TYPE_MD5)
4573               (void)hv_store(RETVAL, "hdrmd5", 6, newSVpv(ss, 0), 0);
4574             ss = solvable_lookup_str(s, buildservice_id);
4575             if (ss)
4576               (void)hv_store(RETVAL, "id", 2, newSVpv(ss, 0), 0);
4577         }
4578     OUTPUT:
4579         RETVAL
4580
4581 void
4582 repos(BSSolv::pool pool)
4583     PPCODE:
4584         {
4585             int ridx;
4586             Repo *repo;
4587
4588             EXTEND(SP, pool->nrepos);
4589             FOR_REPOS(ridx, repo)
4590               {
4591                 SV *sv = sv_newmortal();
4592                 sv_setref_pv(sv, "BSSolv::repo", (void *)repo);
4593                 PUSHs(sv);
4594               }
4595         }
4596
4597 void
4598 preparehashes(BSSolv::pool pool, char *prp, SV *gctxprpnotreadysv = 0)
4599     PPCODE:
4600         {
4601             HV *gctxprpnotready = 0;
4602             int ridx;
4603             Repo *repo;
4604             /* generated: */
4605             HV *depislocal = newHV();
4606             HV *dep2pkg = newHV();
4607             HV *dep2src = newHV();
4608             HV *notready = newHV();
4609             HV *subpacks = newHV();
4610             const char *srcstr;
4611             const char *str;
4612             Queue subq;
4613             Id lastsrc, srcname, srctype;
4614             int i, j;
4615             Id p;
4616             Solvable *s;
4617             SV *sv, **svp;
4618
4619             if (gctxprpnotreadysv && SvROK(gctxprpnotreadysv) && SvTYPE(SvRV(gctxprpnotreadysv)) == SVt_PVHV)
4620               gctxprpnotready = (HV *)SvRV(gctxprpnotreadysv);
4621             queue_init(&subq);
4622             FOR_REPOS(ridx, repo)
4623               {
4624                 HV *prpnotready = 0;
4625                 int islocal = repo->name && !strcmp(repo->name, prp);
4626                 svp = 0;
4627                 if (repo->name && !islocal && gctxprpnotready)
4628                   svp = hv_fetch(gctxprpnotready, repo->name, strlen(repo->name), 0);
4629                 if (svp && *svp && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV)
4630                   prpnotready = (HV *)SvRV(*svp);
4631                 FOR_REPO_SOLVABLES(repo, p, s)
4632                   {
4633                     if (!MAPTST(pool->considered, p))
4634                       continue;
4635                     srctype = solvable_lookup_type(pool->solvables + p, SOLVABLE_SOURCENAME);
4636                     if (srctype == REPOKEY_TYPE_VOID)
4637                       srcname = s->name;
4638                     else if (srctype == REPOKEY_TYPE_ID)
4639                       srcname = solvable_lookup_id(pool->solvables + p, SOLVABLE_SOURCENAME);
4640                     else
4641                       {
4642                         srcstr = solvable_lookup_str(pool->solvables + p, SOLVABLE_SOURCENAME);
4643                         srcname = srcstr ? pool_str2id(pool, srcstr, 1) : 0;
4644                       }
4645                     if (!srcname || srcname == 1)
4646                       srcname = s->name;
4647                     queue_push2(&subq, s->name, srcname);
4648
4649                     str = pool_id2str(pool, s->name);
4650                     (void)hv_store(dep2pkg, str, strlen(str), newSViv((IV)p), 0);
4651                     if (islocal)
4652                       (void)hv_store(depislocal, str, strlen(str), newSViv((IV)1), 0);
4653                     srcstr = pool_id2str(pool, srcname);
4654                     (void)hv_store(dep2src, str, strlen(str), newSVpv(srcstr, 0), 0);
4655                     if (!islocal && prpnotready)
4656                       {
4657                         svp = hv_fetch(prpnotready, srcstr, strlen(srcstr), 0);
4658                         if (svp && *svp && SvTRUE(*svp))
4659                           (void)hv_store(notready, srcstr, strlen((char *)srcstr), newSViv((IV)2), 0);
4660                       }
4661                   }
4662               }
4663             solv_sort(subq.elements, subq.count / 2, sizeof(Id) * 2, subpack_sort_cmp, pool);
4664             queue_push2(&subq, 0, 0);
4665             lastsrc = 0;
4666             for (i = j = 0; i < subq.count; i += 2)
4667               {
4668                 if (subq.elements[i + 1] != lastsrc)
4669                   {
4670                     if (j < i)
4671                       {
4672                         AV *subs = newAV();
4673                         for (; j < i; j += 2)
4674                           {
4675                             str = pool_id2str(pool,  subq.elements[j]);
4676                             av_push(subs, newSVpv(str, 0));
4677                           }
4678                         str = pool_id2str(pool, lastsrc);
4679                         (void)hv_store(subpacks, str, strlen(str), newRV_noinc((SV *)subs), 0);
4680                       }
4681                     lastsrc = subq.elements[i + 1];
4682                   }
4683               }
4684             queue_free(&subq);
4685             EXTEND(SP, 5);
4686             sv = newRV_noinc((SV *)dep2pkg);
4687             PUSHs(sv_2mortal(sv));
4688             sv = newRV_noinc((SV *)dep2src);
4689             PUSHs(sv_2mortal(sv));
4690             sv = newRV_noinc((SV *)depislocal);
4691             PUSHs(sv_2mortal(sv));
4692             sv = newRV_noinc((SV *)notready);
4693             PUSHs(sv_2mortal(sv));
4694             sv = newRV_noinc((SV *)subpacks);
4695             PUSHs(sv_2mortal(sv));
4696         }
4697
4698 void
4699 DESTROY(BSSolv::pool pool)
4700     CODE:
4701         if (pool->considered)
4702           {
4703             map_free(pool->considered);
4704             pool->considered = solv_free(pool->considered);
4705           }
4706         pool_free(pool);
4707
4708
4709
4710
4711 MODULE = BSSolv         PACKAGE = BSSolv::repo          PREFIX = repo
4712
4713 void
4714 pkgnames(BSSolv::repo repo)
4715     PPCODE:
4716         {
4717             Pool *pool = repo->pool;
4718             Id p;
4719             Solvable *s;
4720             Map c;
4721         
4722             create_considered(pool, repo, &c);
4723             EXTEND(SP, 2 * repo->nsolvables);
4724             FOR_REPO_SOLVABLES(repo, p, s)
4725               {
4726                 if (!MAPTST(&c, p))
4727                   continue;
4728                 PUSHs(sv_2mortal(newSVpv(pool_id2str(pool, s->name), 0)));
4729                 PUSHs(sv_2mortal(newSViv(p)));
4730               }
4731             map_free(&c);
4732         }
4733
4734 void
4735 pkgpaths(BSSolv::repo repo)
4736     PPCODE:
4737         {
4738             Pool *pool = repo->pool;
4739             Id p;
4740             Solvable *s;
4741             Map c;
4742             const char *str;
4743             unsigned int medianr;
4744         
4745             create_considered(pool, repo, &c);
4746             EXTEND(SP, 2 * repo->nsolvables);
4747             FOR_REPO_SOLVABLES(repo, p, s)
4748               {
4749                 if (!MAPTST(&c, p))
4750                   continue;
4751                 /* ignore dod packages */
4752                 str = solvable_lookup_str(s, buildservice_id);
4753                 if (str && !strcmp(str, "dod"))
4754                   continue;
4755                 str = solvable_get_location(pool->solvables + p, &medianr);
4756                 if (!str)
4757                   continue;
4758                 PUSHs(sv_2mortal(newSVpv(str, 0)));
4759                 PUSHs(sv_2mortal(newSViv(p)));
4760               }
4761             map_free(&c);
4762         }
4763
4764 void
4765 tofile(BSSolv::repo repo, char *filename)
4766     CODE:
4767         {
4768             FILE *fp;
4769             fp = fopen(filename, "w");
4770             if (fp == 0)
4771               croak("%s: %s\n", filename, Strerror(errno));
4772             repo_write_filtered(repo, fp, myrepowritefilter, 0, 0);
4773             if (fclose(fp))
4774               croak("fclose: %s\n",  Strerror(errno));
4775         }
4776
4777 void
4778 tofile_fd(BSSolv::repo repo, int fd)
4779     CODE:
4780         {
4781             FILE *fp;
4782             int fd2;
4783             fd2 = dup(fd);
4784             if (fd2 == -1)
4785               croak("dup: %s\n", Strerror(errno));
4786             fp = fdopen(fd2, "w");
4787             if (fp == 0)
4788               {
4789                 int e = errno;
4790                 close(fd2);
4791                 croak("fdopen: %s\n", Strerror(e));
4792               }
4793             repo_write_filtered(repo, fp, myrepowritefilter, 0, 0);
4794             if (fclose(fp))
4795               {
4796                 int e = errno;
4797                 close(fd2);
4798                 croak("fclose: %s\n",  Strerror(e));
4799               }
4800         }
4801
4802 SV *
4803 tostr(BSSolv::repo repo)
4804     CODE:
4805         {
4806             FILE *fp;
4807             char *buf;
4808             size_t len;
4809             fp = open_memstream(&buf, &len);
4810             if (fp == 0)
4811               croak("open_memstream: %s\n", Strerror(errno));
4812             repo_write_filtered(repo, fp, myrepowritefilter, 0, 0);
4813             if (fclose(fp))
4814               croak("fclose: %s\n",  Strerror(errno));
4815             RETVAL = newSVpvn(buf, len);
4816             free(buf);
4817         }
4818     OUTPUT:
4819         RETVAL
4820
4821 int
4822 updatefrombins(BSSolv::repo repo, char *dir, ...)
4823     CODE:
4824         {
4825             Pool *pool = repo->pool;
4826             int i;
4827             Repodata *data = 0;
4828             Hashtable ht;
4829             Hashval h, hh, hm;
4830             int dirty = 0;
4831             Map reused;
4832             int oldend = 0;
4833             Id p, id;
4834             Solvable *s;
4835             STRLEN sl;
4836             const char *oldcookie;
4837
4838             map_init(&reused, repo->end - repo->start);
4839             if (repo_lookup_str(repo, SOLVID_META, buildservice_dodurl))
4840               {
4841                 /* this is a dod repo. keep all dod packages. */
4842                 FOR_REPO_SOLVABLES(repo, p, s)
4843                   {
4844                     const char *str = solvable_lookup_str(s, buildservice_id);
4845                     if (str && !strcmp(str, "dod"))
4846                       MAPSET(&reused, p - repo->start);
4847                   }
4848               }
4849             hm = mkmask(2 * repo->nsolvables + 1);
4850             ht = solv_calloc(hm + 1, sizeof(*ht));
4851             oldcookie = repo_lookup_str(repo, SOLVID_META, buildservice_repocookie);
4852             if (oldcookie && !strcmp(oldcookie, REPOCOOKIE))
4853               {
4854                 FOR_REPO_SOLVABLES(repo, p, s)
4855                   {
4856                     const char *str = solvable_lookup_str(s, buildservice_id);
4857                     if (!str || !strcmp(str, "dod"))
4858                       continue;
4859                     h = strhash(str) & hm;
4860                     hh = HASHCHAIN_START;
4861                     while ((id = ht[h]) != 0)
4862                       h = HASHCHAIN_NEXT(h, hh, hm);
4863                     ht[h] = p;
4864                   }
4865               }
4866             if (repo->end != repo->start)
4867               oldend = repo->end;
4868
4869             for (i = 2; i + 1 < items; i += 2)
4870               {
4871                 char *s = SvPV(ST(i), sl);
4872                 char *sid = SvPV_nolen(ST(i + 1));
4873                 if (sl < 4)
4874                   continue;
4875                 if (strcmp(s + sl - 4, ".rpm")
4876                     && strcmp(s + sl - 4, ".deb")
4877 #ifdef ARCH_ADD_WITH_PKGID
4878                     && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.gz"))
4879                     && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.xz"))
4880 #endif
4881                    )
4882                 if (sl > 10 && !strcmp(s + sl - 10, ".patch.rpm"))
4883                   continue;
4884                 if (sl > 10 && !strcmp(s + sl - 10, ".nosrc.rpm"))
4885                   continue;
4886                 if (sl > 8 && !strcmp(s + sl - 8, ".src.rpm"))
4887                   continue;
4888                 h = strhash(sid) & hm;
4889                 hh = HASHCHAIN_START;
4890                 while ((id = ht[h]) != 0)
4891                   {
4892                     const char *str = solvable_lookup_str(pool->solvables + id, buildservice_id);
4893                     if (!strcmp(str, sid))
4894                       {
4895                         /* check location */
4896                         unsigned int medianr;
4897                         str = solvable_get_location(pool->solvables + id, &medianr);
4898                         if (str[0] == '.' && str[1] == '/')
4899                           str += 2;
4900                         if (!strcmp(str, s))
4901                           break;
4902                       }
4903                     h = HASHCHAIN_NEXT(h, hh, hm);
4904                   }
4905                 if (id)
4906                   {
4907                     /* same id and location, reuse old entry */
4908                     MAPSET(&reused, id - repo->start);
4909                   }
4910                 else
4911                   {
4912                     /* add new entry */
4913                     dirty++;
4914                     if (!data)
4915                       data = repo_add_repodata(repo, 0);
4916                     repodata_addbin(data, dir, s, (int)sl, sid);
4917                   }
4918               }
4919             solv_free(ht);
4920             if (oldcookie)
4921               {
4922                 if (strcmp(oldcookie, REPOCOOKIE) != 0)
4923                   {
4924                     Repodata *firstrepodata = repo_id2repodata(repo, 1);
4925                     if (data && data != firstrepodata)
4926                       repodata_internalize(data);
4927                     data = firstrepodata;
4928                     repodata_set_str(data, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
4929                   }
4930               }
4931             else
4932               {
4933                 if (!data)
4934                   data = repo_add_repodata(repo, 0);
4935                 repodata_set_str(data, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
4936               }
4937             if (data)
4938               repodata_internalize(data);
4939             if (oldend)
4940               {
4941                 for (i = repo->start; i < oldend; i++)
4942                   {
4943                     if (pool->solvables[i].repo != repo)
4944                       continue;
4945                     if (MAPTST(&reused, i - repo->start))
4946                       continue;
4947                     if (dirty <= 0)
4948                       dirty--;
4949                     repo_free_solvable_block(repo, i, 1, 0);
4950                   }
4951               }
4952             map_free(&reused);
4953             RETVAL = dirty;
4954         }
4955     OUTPUT:
4956         RETVAL
4957
4958
4959 void
4960 getpathid(BSSolv::repo repo)
4961     PPCODE:
4962         {
4963             Id p;
4964             Solvable *s;
4965             EXTEND(SP, repo->nsolvables * 2);
4966             FOR_REPO_SOLVABLES(repo, p, s)
4967               {
4968                 unsigned int medianr;
4969                 const char *str;
4970                 str = solvable_get_location(s, &medianr);
4971                 PUSHs(sv_2mortal(newSVpv(str, 0)));
4972                 str = solvable_lookup_str(s, buildservice_id);
4973                 PUSHs(sv_2mortal(newSVpv(str, 0)));
4974               }
4975         }
4976
4977 const char *
4978 name(BSSolv::repo repo)
4979     CODE:
4980         RETVAL = repo->name;
4981     OUTPUT:
4982         RETVAL
4983
4984 int
4985 isexternal(BSSolv::repo repo)
4986     CODE:
4987         RETVAL = repo_lookup_void(repo, SOLVID_META, buildservice_external) ? 1 : 0;
4988     OUTPUT:
4989         RETVAL
4990
4991 const char *
4992 dodurl(BSSolv::repo repo)
4993     CODE:
4994         RETVAL = repo_lookup_str(repo, SOLVID_META, buildservice_dodurl);
4995     OUTPUT:
4996         RETVAL
4997
4998 const char *
4999 dodcookie(BSSolv::repo repo)
5000     CODE:
5001         RETVAL = repo_lookup_str(repo, SOLVID_META, buildservice_dodcookie);
5002     OUTPUT:
5003         RETVAL
5004
5005 void
5006 updatedoddata(BSSolv::repo repo, HV *rhv = 0)
5007     CODE:
5008         {
5009             Id p;
5010             Solvable *s;
5011             Repodata *data;
5012             /* delete old dod data */
5013             FOR_REPO_SOLVABLES(repo, p, s)
5014               {
5015                 const char *str = solvable_lookup_str(s, buildservice_id);
5016                 if (!str || !strcmp(str, "dod"))
5017                     repo_free_solvable(repo, p, 1);
5018               }
5019             data = repo_add_repodata(repo, REPO_REUSE_REPODATA);
5020             repodata_unset(data, SOLVID_META, buildservice_dodurl);
5021             repodata_unset(data, SOLVID_META, buildservice_dodcookie);
5022             /* add new data */
5023             if (rhv)
5024                 data2solvables(repo, data, rhv);
5025             repo_internalize(repo);
5026         }
5027
5028
5029 MODULE = BSSolv         PACKAGE = BSSolv::expander      PREFIX = expander
5030
5031
5032 BSSolv::expander
5033 new(char *packname = "BSSolv::expander", BSSolv::pool pool, HV *config)
5034     CODE:
5035         {
5036             SV *sv, **svp;
5037             char *str;
5038             int i, neg;
5039             Id id, id2;
5040             Expander *xp;
5041
5042             xp = calloc(sizeof(Expander), 1);
5043             xp->pool = pool;
5044             svp = hv_fetch(config, "prefer", 6, 0);
5045             sv = svp ? *svp : 0;
5046             if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV)
5047               {
5048                 AV *av = (AV *)SvRV(sv);
5049                 for (i = 0; i <= av_len(av); i++)
5050                   {
5051                     svp = av_fetch(av, i, 0);
5052                     if (!svp)
5053                       continue;
5054                     sv = *svp;
5055                     str = SvPV_nolen(sv);
5056                     if (!str)
5057                       continue;
5058                     neg = 0;
5059                     if (*str == '-')
5060                       {
5061                         neg = 1;
5062                         str++;
5063                       }
5064                     id = pool_str2id(pool, str, 1);
5065                     id2 = 0;
5066                     if ((str = strchr(str, ':')) != 0)
5067                       id2 = pool_str2id(pool, str + 1, 1);
5068                     if (neg)
5069                       {
5070                         MAPEXP(&xp->preferneg, id);
5071                         MAPSET(&xp->preferneg, id);
5072                         if (id2)
5073                           {
5074                             MAPEXP(&xp->prefernegx, id2);
5075                             MAPSET(&xp->prefernegx, id2);
5076                           }
5077                       }
5078                     else
5079                       {
5080                         queue_push(&xp->preferposq, id);
5081                         MAPEXP(&xp->preferpos, id);
5082                         MAPSET(&xp->preferpos, id);
5083                         if (id2)
5084                           {
5085                             MAPEXP(&xp->preferposx, id2);
5086                             MAPSET(&xp->preferposx, id2);
5087                           }
5088                       }
5089                   }
5090               }
5091             svp = hv_fetch(config, "ignoreh", 7, 0);
5092             sv = svp ? *svp : 0;
5093             if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV)
5094               {
5095                 HV *hv = (HV *)SvRV(sv);
5096                 HE *he;
5097
5098                 hv_iterinit(hv);
5099                 while ((he = hv_iternext(hv)) != 0)
5100                   {
5101                     I32 strl;
5102                     str = hv_iterkey(he, &strl);
5103                     if (!str)
5104                       continue;
5105                  
5106                     id = pool_str2id(pool, str, 1);
5107                     id2 = 0;
5108                     if ((str = strchr(str, ':')) != 0)
5109                       id2 = pool_str2id(pool, str + 1, 1);
5110                     MAPEXP(&xp->ignored, id);
5111                     MAPSET(&xp->ignored, id);
5112                     if (id2)
5113                       {
5114                         MAPEXP(&xp->ignoredx, id2);
5115                         MAPSET(&xp->ignoredx, id2);
5116                       }
5117                   }
5118               }
5119             svp = hv_fetch(config, "conflict", 8, 0);
5120             sv = svp ? *svp : 0;
5121             if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV)
5122               {
5123                 AV *av = (AV *)SvRV(sv);
5124                 for (i = 0; i <= av_len(av); i++)
5125                   {
5126                     char *p;
5127                     Id id2;
5128
5129                     svp = av_fetch(av, i, 0);
5130                     if (!svp)
5131                       continue;
5132                     sv = *svp;
5133                     str = SvPV_nolen(sv);
5134                     if (!str)
5135                       continue;
5136                     p = strchr(str, ':');
5137                     if (!p)
5138                       continue;
5139                     id = pool_strn2id(pool, str, p - str, 1);
5140                     str = p + 1;
5141                     while ((p = strchr(str, ',')) != 0)
5142                       {
5143                         id2 = pool_strn2id(pool, str, p - str, 1);
5144                         queue_push2(&xp->conflictsq, id, id2);
5145                         MAPEXP(&xp->conflicts, id);
5146                         MAPSET(&xp->conflicts, id);
5147                         MAPEXP(&xp->conflicts, id2);
5148                         MAPSET(&xp->conflicts, id2);
5149                         str = p + 1;
5150                       }
5151                     id2 = pool_str2id(pool, str, 1);
5152                     queue_push2(&xp->conflictsq, id, id2);
5153                     MAPEXP(&xp->conflicts, id);
5154                     MAPSET(&xp->conflicts, id);
5155                     MAPEXP(&xp->conflicts, id2);
5156                     MAPSET(&xp->conflicts, id2);
5157                   }
5158               }
5159             /* XXX: this modifies the pool, which is a bit unclean! */
5160             svp = hv_fetch(config, "fileprovides", 12, 0);
5161             sv = svp ? *svp : 0;
5162             if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV)
5163               {
5164                 HV *hv = (HV *)SvRV(sv);
5165                 I32 strl;
5166                 Queue q;
5167
5168                 xp->havefileprovides = 1;
5169                 hv_iterinit(hv);
5170                 queue_init(&q);
5171                 while ((sv = hv_iternextsv(hv, &str, &strl)) != 0)
5172                   {
5173                     AV *av;
5174                     Id p, pp;
5175                     int havenew = 0;
5176
5177                     if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVAV)
5178                       continue;
5179                     id = pool_str2id(pool, str, 1);
5180                     queue_empty(&q);
5181                     FOR_PROVIDES(p, pp, id)
5182                       queue_push(&q, p);
5183                     av = (AV *)SvRV(sv);
5184                     for (i = 0; i <= av_len(av); i++)
5185                       {
5186                         svp = av_fetch(av, i, 0);
5187                         if (!svp)
5188                           continue;
5189                         sv = *svp;
5190                         str = SvPV_nolen(sv);
5191                         if (!str)
5192                           continue;
5193                         id2 = pool_str2id(pool, str, 0);
5194                         FOR_PROVIDES(p, pp, id2)
5195                           {
5196                             int j;
5197                             for (j = 0; j < q.count; j++)
5198                               {
5199                                 if (q.elements[j] == p)
5200                                   break;
5201                                 if (q.elements[j] > p)
5202                                   {
5203                                     queue_insert(&q, j, p);
5204                                     havenew = 1;
5205                                     break;
5206                                   }
5207                               }
5208                             if (j == q.count)
5209                               {
5210                                 queue_push(&q, p);
5211                                 havenew = 1;
5212                               }
5213                           }
5214                       }
5215                     if (havenew)
5216                       pool->whatprovides[id] = pool_queuetowhatprovides(pool, &q);
5217                   }
5218                 queue_free(&q);
5219               }
5220             svp = hv_fetch(config, "expandflags:ignoreconflicts", 27, 0);
5221             sv = svp ? *svp : 0;
5222             if (sv && SvTRUE(sv))
5223               xp->ignoreconflicts = 1;
5224             svp = hv_fetch(config, "expand_dbg", 10, 0);
5225             sv = svp ? *svp : 0;
5226             if (sv && SvTRUE(sv))
5227               xp->debug = 1;
5228             sv = get_sv("Build::expand_dbg", FALSE);
5229             if (sv && SvTRUE(sv))
5230               xp->debug = 1;
5231             RETVAL = xp;
5232         }
5233     OUTPUT:
5234         RETVAL
5235
5236
5237 void
5238 expand(BSSolv::expander xp, ...)
5239     PPCODE:
5240         {
5241             Pool *pool;
5242             int i, nerrors;
5243             Id id, who, conflbuf[16];
5244             Queue revertignore, in, out, confl;
5245             int oldignoreignore = xp->ignoreignore;
5246             int ignoreignore = 0;
5247             Map oldignored, oldignoredx;
5248             int ignoremapssaved = 0;
5249
5250             queue_init(&revertignore);
5251             queue_init(&in);
5252             queue_init(&out);
5253             queue_init_buffer(&confl, conflbuf, sizeof(conflbuf)/sizeof(*conflbuf));
5254             pool = xp->pool;
5255             if (xp->debug)
5256               expander_dbg(xp, "expand args:");
5257             for (i = 1; i < items; i++)
5258               {
5259                 char *s = SvPV_nolen(ST(i));
5260                 if (xp->debug)
5261                   expander_dbg(xp, " %s", s);
5262                 if (*s == '-')
5263                   {
5264                     Id id;
5265                     if (s[1] == '-' && !strcmp(s, "--ignoreignore--"))
5266                       {
5267                         ignoreignore = 1;
5268                         continue;
5269                       }
5270                     id = pool_str2id(pool, s + 1, 1);
5271                     if (id == expander_directdepsend)
5272                       {
5273                         queue_push(&in, id);
5274                         continue;
5275                       }
5276                     queue_push(&revertignore, id);
5277                   }
5278                 else if (*s == '!')
5279                   {
5280                     Id id = dep2id(pool, s + (s[1] == '!' ? 2 : 1));
5281                     queue_push2(&confl, id, s[1] == '!' ? 1 : 0);
5282                   }
5283                 else
5284                   {
5285                     Id id = dep2id(pool, s);
5286                     queue_push(&in, id);
5287                   }
5288               }
5289             if (xp->debug)
5290               expander_dbg(xp, "\n");
5291
5292             if (ignoreignore && revertignore.count)
5293               {
5294                 /* bad: have direct ignores and project config ignores */
5295                 oldignored = xp->ignored;
5296                 oldignoredx = xp->ignoredx;
5297                 ignoremapssaved = 1;
5298                 /* clear project config maps */
5299                 memset(&xp->ignored, 0, sizeof(xp->ignored));
5300                 memset(&xp->ignoredx, 0, sizeof(xp->ignoredx));
5301               }
5302
5303             if (revertignore.count)
5304               {
5305                 /* mix direct ignores with ignores from project config */
5306                 int revertcnt = revertignore.count;
5307                 for (i = 0; i < revertcnt; i++)
5308                   {
5309                     const char *ss;
5310                     id = revertignore.elements[i];
5311                     MAPEXP(&xp->ignored, id);
5312                     if (MAPTST(&xp->ignored, id))
5313                       continue;
5314                     MAPSET(&xp->ignored, id);
5315                     queue_push(&revertignore, id);
5316                     if ((ss = strchr(pool_id2str(pool, id), ':')) != 0)
5317                       {
5318                         id = pool_str2id(pool, ss + 1, 1);
5319                         MAPEXP(&xp->ignoredx, id);
5320                         if (MAPTST(&xp->ignoredx, id))
5321                           continue;
5322                         MAPSET(&xp->ignoredx, id);
5323                         queue_push(&revertignore, -id);
5324                       }
5325                   }
5326                 queue_deleten(&revertignore, 0, revertcnt);
5327               }
5328             else if (ignoreignore)
5329               {
5330                 /* no direct ignores, disable ignore processing */
5331                 xp->ignoreignore = 1;
5332               }
5333
5334             MAPEXP(&xp->ignored, pool->ss.nstrings);
5335             MAPEXP(&xp->ignoredx, pool->ss.nstrings);
5336             MAPEXP(&xp->preferpos, pool->ss.nstrings);
5337             MAPEXP(&xp->preferposx, pool->ss.nstrings);
5338             MAPEXP(&xp->preferneg, pool->ss.nstrings);
5339             MAPEXP(&xp->prefernegx, pool->ss.nstrings);
5340             MAPEXP(&xp->conflicts, pool->ss.nstrings);
5341
5342             nerrors = expander_expand(xp, &in, &out, &confl);
5343
5344             /* revert ignores */
5345             xp->ignoreignore = oldignoreignore;
5346             if (ignoremapssaved)
5347               {
5348                 map_free(&xp->ignored);
5349                 map_free(&xp->ignoredx);
5350                 xp->ignored = oldignored;
5351                 xp->ignoredx = oldignoredx;
5352               }
5353             else
5354               {
5355                 for (i = 0; i < revertignore.count; i++)
5356                   {
5357                     id = revertignore.elements[i];
5358                     if (id > 0)
5359                       MAPCLR(&xp->ignored, id);
5360                     else
5361                       MAPCLR(&xp->ignoredx, -id);
5362                   }
5363               }
5364             queue_free(&revertignore);
5365
5366             queue_free(&in);
5367             queue_free(&confl);
5368
5369             if (nerrors)
5370               {
5371                 EXTEND(SP, nerrors + 1);
5372                 PUSHs(sv_2mortal(newSV(0)));
5373                 for (i = 0; i < out.count; )
5374                   {
5375                     SV *sv;
5376                     Id type = out.elements[i];
5377                     if (type == ERROR_NOPROVIDER)
5378                       {
5379                         id = out.elements[i + 1];
5380                         who = out.elements[i + 2];
5381                         if (who)
5382                           sv = newSVpvf("nothing provides %s needed by %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name));
5383                         else
5384                           sv = newSVpvf("nothing provides %s", pool_dep2str(pool, id));
5385                         i += 3;
5386                       }
5387                     else if (type == ERROR_CONFLICTINGPROVIDER)
5388                       {
5389                         id = out.elements[i + 1];
5390                         who = out.elements[i + 2];
5391                         if (who)
5392                           sv = newSVpvf("conflict for provider of %s needed by %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name));
5393                         else
5394                           sv = newSVpvf("conflict for provider of %s", pool_dep2str(pool, id));
5395                         i += 3;
5396                       }
5397                     else if (type == ERROR_CONFLICTINGPROVIDERS)
5398                       {
5399                         id = out.elements[i + 1];
5400                         who = out.elements[i + 2];
5401                         if (who)
5402                           sv = newSVpvf("conflict for all providers of %s needed by %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name));
5403                         else
5404                           sv = newSVpvf("conflict for all providers of %s", pool_dep2str(pool, id));
5405                         i += 3;
5406                       }
5407                     else if (type == ERROR_PROVIDERINFO)
5408                       {
5409                         Id who2 = out.elements[i + 2];
5410                         who = out.elements[i + 1];
5411                         if (who2 < 0)
5412                           sv = newSVpvf("(provider %s obsoletes installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[-who2].name));
5413                         else
5414                           sv = newSVpvf("(provider %s conflicts with installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[who2].name));
5415                         i += 3;
5416                       }
5417                     else if (type == ERROR_PROVIDERINFO2)
5418                       {
5419                         Id who2 = out.elements[i + 2];
5420                         who = out.elements[i + 1];
5421                         if (who2 < 0)
5422                           sv = newSVpvf("(provider %s is obsoleted by installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[-who2].name));
5423                         else if (who2 > 0)
5424                           sv = newSVpvf("(provider %s is conflicted by installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[who2].name));
5425                         else
5426                           sv = newSVpvf("(provider %s is conflicted by the build config)", pool_id2str(pool, pool->solvables[who].name));
5427                         i += 3;
5428                       }
5429                     else if (type == ERROR_CHOICE)
5430                       {
5431                         int j;
5432                         char *str = "";
5433                         for (j = i + 3; out.elements[j]; j++)
5434                           {
5435                             Solvable *s = pool->solvables + out.elements[j];
5436                             str = pool_tmpjoin(pool, str, " ", pool_id2str(pool, s->name));
5437                           }
5438                         if (*str)
5439                           str++;        /* skip starting ' ' */
5440                         id = out.elements[i + 1];
5441                         who = out.elements[i + 2];
5442                         if (who)
5443                           sv = newSVpvf("have choice for %s needed by %s: %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name), str);
5444                         else
5445                           sv = newSVpvf("have choice for %s: %s", pool_dep2str(pool, id), str);
5446                         i = j + 1;
5447                       }
5448                     else
5449                       croak("expander: bad error type\n");
5450                     PUSHs(sv_2mortal(sv));
5451                   }
5452               }
5453             else
5454               {
5455                 EXTEND(SP, out.count + 1);
5456                 PUSHs(sv_2mortal(newSViv((IV)1)));
5457                 for (i = 0; i < out.count; i++)
5458                   {
5459                     Solvable *s = pool->solvables + out.elements[i];
5460                     PUSHs(sv_2mortal(newSVpv(pool_id2str(pool, s->name), 0)));
5461                   }
5462               }
5463             queue_free(&out);
5464         }
5465
5466 const char *
5467 debugstr(BSSolv::expander xp)
5468     CODE:
5469         if (!xp->debugstr)
5470           xp->debugstr = calloc(1, 1);
5471         RETVAL = xp->debugstr;
5472     OUTPUT:
5473         RETVAL
5474
5475
5476 void
5477 DESTROY(BSSolv::expander xp)
5478     CODE:
5479         map_free(&xp->ignored);
5480         map_free(&xp->ignoredx);
5481         queue_free(&xp->preferposq);
5482         map_free(&xp->preferpos);
5483         map_free(&xp->preferposx);
5484         map_free(&xp->preferneg);
5485         map_free(&xp->prefernegx);
5486         queue_free(&xp->conflictsq);
5487         map_free(&xp->conflicts);
5488         solv_free(xp->debugstr);
5489         solv_free(xp);