8f637567c60d435f7e9b17df618d6d17685350c7
[platform/upstream/libsolv.git] / ext / repo_deb.c
1 /*
2  * Copyright (c) 2009, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <zlib.h>
15 #include <lzma.h>
16 #include <errno.h>
17
18 #include "pool.h"
19 #include "repo.h"
20 #include "util.h"
21 #include "solver.h"     /* for GET_USERINSTALLED_ flags */
22 #include "chksum.h"
23 #include "repo_deb.h"
24
25 #ifdef _WIN32
26   #include "strfncs.h"
27 #endif
28
29 #if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
30 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
31 #endif
32
33 static unsigned char *
34 decompress_gz(unsigned char *in, int inl, int *outlp, int maxoutl)
35 {
36   z_stream strm;
37   int outl, ret;
38   unsigned char *bp, *out;
39
40   /* first skip the gz header */
41   if (inl <= 10 || in[0] != 0x1f || in[1] != 0x8b)
42     return 0;
43   if (in[2] != 8 || (in[3] & 0xe0) != 0)
44     return 0;
45   bp = in + 4;
46   bp += 6;      /* skip time, xflags and OS code */
47   if (in[3] & 0x04)
48     {
49       /* skip extra field */
50       int l = bp + 2 >= in + inl ? 0 : (bp[0] | bp[1] << 8);
51       bp += l + 2;
52     }
53   if (in[3] & 0x08)     /* orig filename */
54     while (bp < in + inl && *bp++)
55       ;
56   if (in[3] & 0x10)     /* file comment */
57     while (bp < in + inl && *bp++)
58       ;
59   if (in[3] & 0x02)     /* header crc */
60     bp += 2;
61   if (bp >= in + inl)
62     return 0;
63   inl -= bp - in;
64   in = bp;
65
66   memset(&strm, 0, sizeof(strm));
67   strm.next_in = in;
68   strm.avail_in = inl;
69   out = solv_malloc(4096);
70   strm.next_out = out;
71   strm.avail_out = 4096;
72   outl = 0;
73   ret = inflateInit2(&strm, -MAX_WBITS);
74   if (ret != Z_OK)
75     {
76       free(out);
77       return 0;
78     }
79   for (;;)
80     {
81       if (strm.avail_out == 0)
82         {
83           outl += 4096;
84           if (outl >= maxoutl)
85             {
86               inflateEnd(&strm);
87               free(out);
88               return 0;
89             }
90           out = solv_realloc(out, outl + 4096);
91           strm.next_out = out + outl;
92           strm.avail_out = 4096;
93         }
94       ret = inflate(&strm, Z_NO_FLUSH);
95       if (ret == Z_STREAM_END)
96         break;
97       if (ret != Z_OK)
98         {
99           inflateEnd(&strm);
100           free(out);
101           return 0;
102         }
103     }
104   outl += 4096 - strm.avail_out;
105   inflateEnd(&strm);
106   *outlp = outl;
107   return out;
108 }
109
110 static unsigned char *
111 decompress_xz(unsigned char *in, int inl, int *outlp, int maxoutl)
112 {
113   static lzma_stream stream_init = LZMA_STREAM_INIT;
114   lzma_stream strm;
115   int outl, ret;
116   unsigned char *out;
117
118   strm = stream_init;
119   strm.next_in = in;
120   strm.avail_in = inl;
121   out = solv_malloc(4096);
122   strm.next_out = out;
123   strm.avail_out = 4096;
124   outl = 0;
125   ret = lzma_auto_decoder(&strm, 100 << 20, 0);
126   if (ret != LZMA_OK)
127     {
128       free(out);
129       return 0;
130     }
131   for (;;)
132     {
133       if (strm.avail_out == 0)
134         {
135           outl += 4096;
136           if (outl >= maxoutl)
137             {
138               lzma_end(&strm);
139               free(out);
140               return 0;
141             }
142           out = solv_realloc(out, outl + 4096);
143           strm.next_out = out + outl;
144           strm.avail_out = 4096;
145         }
146       ret = lzma_code(&strm, LZMA_RUN);
147       if (ret == LZMA_STREAM_END)
148         break;
149       if (ret != LZMA_OK)
150         {
151           lzma_end(&strm);
152           free(out);
153           return 0;
154         }
155     }
156   outl += 4096 - strm.avail_out;
157   lzma_end(&strm);
158   *outlp = outl;
159   return out;
160 }
161
162 static Id
163 parseonedep(Pool *pool, char *p)
164 {
165   char *n, *ne, *e, *ee;
166   Id name, evr;
167   int flags;
168
169   while (*p == ' ' || *p == '\t' || *p == '\n')
170     p++;
171   if (!*p || *p == '(')
172     return 0;
173   n = p;
174   /* find end of name */
175   while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '(' && *p != '|')
176     p++;
177   ne = p;
178   while (*p == ' ' || *p == '\t' || *p == '\n')
179     p++;
180   evr = 0;
181   flags = 0;
182   e = ee = 0;
183   if (*p == '(')
184     {
185       p++;
186       while (*p == ' ' || *p == '\t' || *p == '\n')
187         p++;
188       if (*p == '>')
189         flags |= REL_GT;
190       else if (*p == '=')
191         flags |= REL_EQ;
192       else if (*p == '<')
193         flags |= REL_LT;
194       if (flags)
195         {
196           p++;
197           if (*p == '>')
198             flags |= REL_GT;
199           else if (*p == '=')
200             flags |= REL_EQ;
201           else if (*p == '<')
202             flags |= REL_LT;
203           else
204             p--;
205           p++;
206         }
207       while (*p == ' ' || *p == '\t' || *p == '\n')
208         p++;
209       e = p;
210       while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != ')')
211         p++;
212       ee = p;
213       while (*p && *p != ')')
214         p++;
215       if (*p)
216         p++;
217       while (*p == ' ' || *p == '\t' || *p == '\n')
218         p++;
219     }
220   if (ne - n > 4 && ne[-4] == ':' && !strncmp(ne - 4, ":any", 4))
221     {
222       /* multiarch annotation */
223       name = pool_strn2id(pool, n, ne - n - 4, 1);
224       name = pool_rel2id(pool, name, ARCH_ANY, REL_MULTIARCH, 1);
225     }
226   else
227     name = pool_strn2id(pool, n, ne - n, 1);
228   if (e)
229     {
230       evr = pool_strn2id(pool, e, ee - e, 1);
231       name = pool_rel2id(pool, name, evr, flags, 1);
232     }
233   if (*p == '|')
234     {
235       Id id = parseonedep(pool, p + 1);
236       if (id)
237         name = pool_rel2id(pool, name, id, REL_OR, 1);
238     }
239   return name;
240 }
241
242 static unsigned int
243 makedeps(Repo *repo, char *deps, unsigned int olddeps, Id marker)
244 {
245   Pool *pool = repo->pool;
246   char *p;
247   Id id;
248
249   while ((p = strchr(deps, ',')) != 0)
250     {
251       *p = 0;
252       olddeps = makedeps(repo, deps, olddeps, marker);
253       *p = ',';
254       deps = p + 1;
255     }
256   id = parseonedep(pool, deps);
257   if (!id)
258     return olddeps;
259   return repo_addid_dep(repo, olddeps, id, marker);
260 }
261
262
263 /* put data from control file into the solvable */
264 /* warning: does inplace changes */
265 static void
266 control2solvable(Solvable *s, Repodata *data, char *control)
267 {
268   Repo *repo = s->repo;
269   Pool *pool = repo->pool;
270   char *p, *q, *end, *tag;
271   int x, l;
272   int havesource = 0;
273   char checksum[32 * 2 + 1];
274   Id checksumtype = 0;
275   Id newtype;
276
277   p = control;
278   while (*p)
279     {
280       p = strchr(p, '\n');
281       if (!p)
282         break;
283       if (p[1] == ' ' || p[1] == '\t')
284         {
285           char *q;
286           /* continuation line */
287           q = p - 1;
288           while (q >= control && *q == ' ' && *q == '\t')
289             q--;
290           l = q + 1 - control;
291           if (l)
292             memmove(p + 1 - l, control, l);
293           control = p + 1 - l;
294           p[1] = '\n';
295           p += 2;
296           continue;
297         }
298       end = p - 1;
299       if (*p)
300         *p++ = 0;
301       /* strip trailing space */
302       while (end >= control && (*end == ' ' || *end == '\t'))
303         *end-- = 0;
304       tag = control;
305       control = p;
306       q = strchr(tag, ':');
307       if (!q || q - tag < 4)
308         continue;
309       *q++ = 0;
310       while (*q == ' ' || *q == '\t')
311         q++;
312       x = '@' + (tag[0] & 0x1f);
313       x = (x << 8) + '@' + (tag[1] & 0x1f);
314       switch(x)
315         {
316         case 'A' << 8 | 'R':
317           if (!strcasecmp(tag, "architecture"))
318             s->arch = pool_str2id(pool, q, 1);
319           break;
320         case 'B' << 8 | 'R':
321           if (!strcasecmp(tag, "breaks"))
322             s->conflicts = makedeps(repo, q, s->conflicts, 0);
323           break;
324         case 'C' << 8 | 'O':
325           if (!strcasecmp(tag, "conflicts"))
326             s->conflicts = makedeps(repo, q, s->conflicts, 0);
327           break;
328         case 'D' << 8 | 'E':
329           if (!strcasecmp(tag, "depends"))
330             s->requires = makedeps(repo, q, s->requires, -SOLVABLE_PREREQMARKER);
331           else if (!strcasecmp(tag, "description"))
332             {
333               char *ld = strchr(q, '\n');
334               if (ld)
335                 {
336                   *ld++ = 0;
337                   repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, ld);
338                 }
339               else
340                 repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, q);
341               repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, q);
342             }
343           break;
344         case 'E' << 8 | 'N':
345           if (!strcasecmp(tag, "enhances"))
346             s->enhances = makedeps(repo, q, s->enhances, 0);
347           break;
348         case 'F' << 8 | 'I':
349           if (!strcasecmp(tag, "filename"))
350             repodata_set_location(data, s - pool->solvables, 0, 0, q);
351           break;
352         case 'H' << 8 | 'O':
353           if (!strcasecmp(tag, "homepage"))
354             repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, q);
355           break;
356         case 'I' << 8 | 'N':
357           if (!strcasecmp(tag, "installed-size"))
358             repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(q, 0, 10) << 10);
359           break;
360         case 'M' << 8 | 'D':
361           if (!strcasecmp(tag, "md5sum") && !checksumtype && strlen(q) == 16 * 2)
362             {
363               strcpy(checksum, q);
364               checksumtype = REPOKEY_TYPE_MD5;
365             }
366           break;
367         case 'P' << 8 | 'A':
368           if (!strcasecmp(tag, "package"))
369             s->name = pool_str2id(pool, q, 1);
370           break;
371         case 'P' << 8 | 'R':
372           if (!strcasecmp(tag, "pre-depends"))
373             s->requires = makedeps(repo, q, s->requires, SOLVABLE_PREREQMARKER);
374           else if (!strcasecmp(tag, "provides"))
375             s->provides = makedeps(repo, q, s->provides, 0);
376           break;
377         case 'R' << 8 | 'E':
378           if (!strcasecmp(tag, "replaces"))
379             s->obsoletes = makedeps(repo, q, s->obsoletes, 0);
380           else if (!strcasecmp(tag, "recommends"))
381             s->recommends = makedeps(repo, q, s->recommends, 0);
382           break;
383         case 'S' << 8 | 'H':
384           newtype = solv_chksum_str2type(tag);
385           if (!newtype || solv_chksum_len(newtype) * 2 != strlen(q))
386             break;
387           if (!checksumtype || (newtype == REPOKEY_TYPE_SHA1 && checksumtype != REPOKEY_TYPE_SHA256) || newtype == REPOKEY_TYPE_SHA256)
388             {
389               strcpy(checksum, q);
390               checksumtype = newtype;
391             }
392           break;
393         case 'S' << 8 | 'O':
394           if (!strcasecmp(tag, "source"))
395             {
396               char *q2;
397               /* ignore version for now */
398               for (q2 = q; *q2; q2++)
399                 if (*q2 == ' ' || *q2 == '\t')
400                   {
401                     *q2 = 0;
402                     break;
403                   }
404               if (s->name && !strcmp(q, pool_id2str(pool, s->name)))
405                 repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME);
406               else
407                 repodata_set_id(data, s - pool->solvables, SOLVABLE_SOURCENAME, pool_str2id(pool, q, 1));
408               havesource = 1;
409             }
410           break;
411         case 'S' << 8 | 'T':
412           if (!strcasecmp(tag, "status"))
413             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_INSTALLSTATUS, q);
414           break;
415         case 'S' << 8 | 'U':
416           if (!strcasecmp(tag, "suggests"))
417             s->suggests = makedeps(repo, q, s->suggests, 0);
418           break;
419         case 'V' << 8 | 'E':
420           if (!strcasecmp(tag, "version"))
421             s->evr = pool_str2id(pool, q, 1);
422           break;
423         }
424     }
425   if (checksumtype)
426     repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, checksumtype, checksum);
427   if (!s->arch)
428     s->arch = ARCH_ALL;
429   if (!s->evr)
430     s->evr = ID_EMPTY;
431   if (s->name)
432     s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
433   if (s->name && !havesource)
434     repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME);
435   if (s->obsoletes)
436     {
437       /* obsoletes only count when the packages also conflict */
438       /* XXX: should not transcode here */
439       int i, j, k;
440       Id d, cid;
441       for (i = j = s->obsoletes; (d = repo->idarraydata[i]) != 0; i++)
442         {
443           if (!s->conflicts)
444             continue;
445           for (k = s->conflicts; (cid = repo->idarraydata[k]) != 0; k++)
446             {
447               if (repo->idarraydata[k] == cid)
448                 break;
449               if (ISRELDEP(cid))
450                 {
451                   Reldep *rd = GETRELDEP(pool, cid);
452                   if (rd->flags < 8 && rd->name == d)
453                     break;      /* specialize obsoletes */
454                 }
455             }
456           if (cid)
457             repo->idarraydata[j++] = cid;
458         }
459       repo->idarraydata[j] = 0;
460       if (j == s->obsoletes)
461         s->obsoletes = 0;
462     }
463 }
464
465 int
466 repo_add_debpackages(Repo *repo, FILE *fp, int flags)
467 {
468   Pool *pool = repo->pool;
469   Repodata *data;
470   char *buf, *p;
471   int bufl, l, ll;
472   Solvable *s;
473
474   data = repo_add_repodata(repo, flags);
475   buf = solv_malloc(4096);
476   bufl = 4096;
477   l = 0;
478   buf[l] = 0;
479   p = buf;
480   for (;;)
481     {
482       if (!(p = strchr(p, '\n')))
483         {
484           int l3;
485           while (l + 1024 >= bufl)
486             {
487               buf = solv_realloc(buf, bufl + 4096);
488               bufl += 4096;
489             }
490           p = buf + l;
491           ll = fread(p, 1, bufl - l - 1, fp);
492           if (ll <= 0)
493             break;
494           p[ll] = 0;
495           while ((l3 = strlen(p)) < ll)
496             p[l3] = '\n';
497           l += ll;
498           if (p != buf)
499             p--;
500           continue;
501         }
502       p++;
503       if (*p != '\n')
504         continue;
505       *p = 0;
506       ll = p - buf + 1;
507       s = pool_id2solvable(pool, repo_add_solvable(repo));
508       control2solvable(s, data, buf);
509       if (!s->name)
510         s = solvable_free(s, 1);
511       if (l > ll)
512         memmove(buf, p + 1, l - ll);
513       l -= ll;
514       p = buf;
515       buf[l] = 0;
516     }
517   if (l)
518     {
519       s = pool_id2solvable(pool, repo_add_solvable(repo));
520       control2solvable(s, data, buf);
521       if (!s->name)
522         s = solvable_free(s, 1);
523     }
524   solv_free(buf);
525   if (!(flags & REPO_NO_INTERNALIZE))
526     repodata_internalize(data);
527   return 0;
528 }
529
530 int
531 repo_add_debdb(Repo *repo, int flags)
532 {
533   FILE *fp;
534   const char *path = "/var/lib/dpkg/status";
535   if (flags & REPO_USE_ROOTDIR)
536     path = pool_prepend_rootdir_tmp(repo->pool, path);
537   if ((fp = fopen(path, "r")) == 0)
538     return pool_error(repo->pool, -1, "%s: %s", path, strerror(errno));
539   repo_add_debpackages(repo, fp, flags);
540   fclose(fp);
541   return 0;
542 }
543
544 #define CONTROL_COMP_NONE       0
545 #define CONTROL_COMP_GZIP       1
546 #define CONTROL_COMP_XZ         2
547
548 Id
549 repo_add_deb(Repo *repo, const char *deb, int flags)
550 {
551   Pool *pool = repo->pool;
552   Repodata *data;
553   unsigned char buf[4096], *bp;
554   int l, l2, vlen, clen, ctarlen;
555   int control_comp;
556   unsigned char *ctgz;
557   unsigned char pkgid[16];
558   unsigned char *ctar;
559   int gotpkgid;
560   FILE *fp;
561   Solvable *s;
562   struct stat stb;
563
564   data = repo_add_repodata(repo, flags);
565   if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, deb) : deb, "r")) == 0)
566     {
567       pool_error(pool, -1, "%s: %s", deb, strerror(errno));
568       return 0;
569     }
570   if (fstat(fileno(fp), &stb))
571     {
572       pool_error(pool, -1, "fstat: %s", strerror(errno));
573       fclose(fp);
574       return 0;
575     }
576   l = fread(buf, 1, sizeof(buf), fp);
577   if (l < 8 + 60 || (strncmp((char *)buf, "!<arch>\ndebian-binary   ", 8 + 16) != 0 && strncmp((char *)buf, "!<arch>\ndebian-binary/  ", 8 + 16) != 0))
578     {
579       pool_error(pool, -1, "%s: not a deb package", deb);
580       fclose(fp);
581       return 0;
582     }
583   vlen = atoi((char *)buf + 8 + 48);
584   if (vlen < 0 || vlen > l)
585     {
586       pool_error(pool, -1, "%s: not a deb package", deb);
587       fclose(fp);
588       return 0;
589     }
590   vlen += vlen & 1;
591   if (l < 8 + 60 + vlen + 60)
592     {
593       pool_error(pool, -1, "%s: unhandled deb package", deb);
594       fclose(fp);
595       return 0;
596     }
597   control_comp = 0;
598   if (!strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz  ", 16) || !strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz/ ", 16))
599     control_comp = CONTROL_COMP_GZIP;
600   else if (!strncmp((char *)buf + 8 + 60 + vlen, "control.tar.xz  ", 16) || !strncmp((char *)buf + 8 + 60 + vlen, "control.tar.xz/ ", 16))
601     control_comp = CONTROL_COMP_XZ;
602   else if (!strncmp((char *)buf + 8 + 60 + vlen, "control.tar     ", 16) || !strncmp((char *)buf + 8 + 60 + vlen, "control.tar/    ", 16))
603     control_comp = CONTROL_COMP_NONE;
604   else
605     {
606       pool_error(pool, -1, "%s: control.tar is not second entry", deb);
607       fclose(fp);
608       return 0;
609     }
610   /* dpkg has no actual maximum size for the control.tar member, so this
611    * just keeps from allocating arbitrarily large amounts of memory.
612    */
613   clen = atoi((char *)buf + 8 + 60 + vlen + 48);
614   if (clen <= 0 || clen >= 0x1000000)
615     {
616       pool_error(pool, -1, "%s: control.tar has illegal size", deb);
617       fclose(fp);
618       return 0;
619     }
620   ctgz = solv_calloc(1, clen + 4);
621   bp = buf + 8 + 60 + vlen + 60;
622   l -= 8 + 60 + vlen + 60;
623   if (l > clen)
624     l = clen;
625   if (l)
626     memcpy(ctgz, bp, l);
627   if (l < clen)
628     {
629       if (fread(ctgz + l, clen - l, 1, fp) != 1)
630         {
631           pool_error(pool, -1, "%s: unexpected EOF", deb);
632           solv_free(ctgz);
633           fclose(fp);
634           return 0;
635         }
636     }
637   fclose(fp);
638   gotpkgid = 0;
639   if (flags & DEBS_ADD_WITH_PKGID)
640     {
641       Chksum *chk = solv_chksum_create(REPOKEY_TYPE_MD5);
642       solv_chksum_add(chk, ctgz, clen);
643       solv_chksum_free(chk, pkgid);
644       gotpkgid = 1;
645     }
646   ctar = 0;
647   if (control_comp == CONTROL_COMP_GZIP)
648     ctar = decompress_gz(ctgz, clen, &ctarlen, 0x1000000);
649   else if (control_comp == CONTROL_COMP_XZ)
650     ctar = decompress_xz(ctgz, clen, &ctarlen, 0x1000000);
651   else
652     {
653       ctarlen = clen;
654       ctar = solv_memdup(ctgz, clen);
655     }
656   solv_free(ctgz);
657   if (!ctar)
658     {
659       pool_error(pool, -1, "%s: control.tar is corrupt", deb);
660       return 0;
661     }
662   bp = ctar;
663   l = ctarlen;
664   l2 = 0;
665   while (l > 512)
666     {
667       int j;
668       l2 = 0;
669       for (j = 124; j < 124 + 12; j++)
670         if (bp[j] >= '0' && bp[j] <= '7')
671           l2 = l2 * 8 + (bp[j] - '0');
672       if (l2 < 0 || l2 > l)
673         {
674           l2 = 0;
675           break;
676         }
677       bp[124] = 0;
678       if (!strcmp((char *)bp, "./control") || !strcmp((char *)bp, "control"))
679         break;
680       l2 = 512 + ((l2 + 511) & ~511);
681       l -= l2;
682       bp += l2;
683     }
684   if (l <= 512 || l - 512 - l2 <= 0 || l2 <= 0)
685     {
686       pool_error(pool, -1, "%s: control.tar contains no control file", deb);
687       free(ctar);
688       return 0;
689     }
690   memmove(ctar, bp + 512, l2);
691   ctar = solv_realloc(ctar, l2 + 1);
692   ctar[l2] = 0;
693   s = pool_id2solvable(pool, repo_add_solvable(repo));
694   control2solvable(s, data, (char *)ctar);
695   if (!(flags & REPO_NO_LOCATION))
696     repodata_set_location(data, s - pool->solvables, 0, 0, deb);
697   if (S_ISREG(stb.st_mode))
698     repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size);
699   if (gotpkgid)
700     repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid);
701   solv_free(ctar);
702   if (!(flags & REPO_NO_INTERNALIZE))
703     repodata_internalize(data);
704   return s - pool->solvables;
705 }
706
707 void
708 pool_deb_get_autoinstalled(Pool *pool, FILE *fp, Queue *q, int flags)
709 {
710   Id name = 0, arch = 0;
711   int autoinstalled = -1;
712   char *buf, *bp;
713   int x, l, bufl, eof = 0;
714   Id p, pp;
715
716   queue_empty(q);
717   buf = solv_malloc(4096);
718   bufl = 4096;
719   l = 0;
720   while (!eof)
721     {
722       while (bufl - l < 1024)
723         {
724           bufl += 4096;
725           if (bufl > 1024 * 64)
726             break;      /* hmm? */
727           buf = solv_realloc(buf, bufl);
728         }
729       if (!fgets(buf + l, bufl - l, fp))
730         {
731           eof = 1;
732           buf[l] = '\n';
733           buf[l + 1] = 0;
734         }
735       l = strlen(buf);
736       if (l && buf[l - 1] == '\n')
737         buf[--l] = 0;
738       if (!*buf || eof)
739         {
740           l = 0;
741           if (name && autoinstalled > 0)
742             {
743               if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
744                 queue_push2(q, name, arch);
745               else if ((flags & GET_USERINSTALLED_NAMES) != 0)
746                 queue_push(q, name);
747               else
748                 {
749                   FOR_PROVIDES(p, pp, name)
750                     {
751                       Solvable *s = pool->solvables + p;
752                       if (s->name != name)
753                         continue;
754                       if (arch && s->arch != arch)
755                         continue;
756                       queue_push(q, p);
757                     }
758                 }
759             }
760           name = arch = 0;
761           autoinstalled = -1;
762           continue;
763         }
764       /* strip trailing space */
765       while (l && (buf[l - 1] == ' ' || buf[l - 1] == '\t'))
766         buf[--l] = 0;
767       l = 0;
768
769       bp = strchr(buf, ':');
770       if (!bp || bp - buf < 4)
771         continue;
772       *bp++ = 0;
773       while (*bp == ' ' || *bp == '\t')
774         bp++;
775       x = '@' + (buf[0] & 0x1f);
776       x = (x << 8) + '@' + (buf[1] & 0x1f);
777       switch(x)
778         {
779         case 'P' << 8 | 'A':
780           if (!strcasecmp(buf, "package"))
781             name = pool_str2id(pool, bp, 1);
782           break;
783         case 'A' << 8 | 'R':
784           if (!strcasecmp(buf, "architecture"))
785             arch = pool_str2id(pool, bp, 1);
786           break;
787         case 'A' << 8 | 'U':
788           if (!strcasecmp(buf, "auto-installed"))
789             autoinstalled = atoi(bp);
790           break;
791         default:
792           break;
793         }
794     }
795 }
796