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