Imported Upstream version 0.6.20
[platform/upstream/libsolv.git] / ext / testcase.c
1 /*
2  * Copyright (c) 2012, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <limits.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <errno.h>
16
17 #include "pool.h"
18 #include "poolarch.h"
19 #include "poolvendor.h"
20 #include "repo.h"
21 #include "repo_solv.h"
22 #include "solver.h"
23 #include "solverdebug.h"
24 #include "chksum.h"
25 #include "testcase.h"
26 #include "selection.h"
27 #include "solv_xfopen.h"
28
29 #define DISABLE_JOIN2
30 #include "tools_util.h"
31
32 static struct job2str {
33   Id job;
34   const char *str;
35 } job2str[] = {
36   { SOLVER_NOOP,           "noop" },
37   { SOLVER_INSTALL,        "install" },
38   { SOLVER_ERASE,          "erase" },
39   { SOLVER_UPDATE,         "update" },
40   { SOLVER_WEAKENDEPS,     "weakendeps" },
41   { SOLVER_MULTIVERSION,   "multiversion" },
42   { SOLVER_MULTIVERSION,   "noobsoletes" },     /* old name */
43   { SOLVER_LOCK,           "lock" },
44   { SOLVER_DISTUPGRADE,    "distupgrade" },
45   { SOLVER_VERIFY,         "verify" },
46   { SOLVER_DROP_ORPHANED,  "droporphaned" },
47   { SOLVER_USERINSTALLED,  "userinstalled" },
48   { SOLVER_ALLOWUNINSTALL, "allowuninstall" },
49   { 0, 0 }
50 };
51
52 static struct jobflags2str {
53   Id flag;
54   const char *str;
55 } jobflags2str[] = {
56   { SOLVER_WEAK,      "weak" },
57   { SOLVER_ESSENTIAL, "essential" },
58   { SOLVER_CLEANDEPS, "cleandeps" },
59   { SOLVER_ORUPDATE,  "orupdate" },
60   { SOLVER_FORCEBEST, "forcebest" },
61   { SOLVER_TARGETED,  "targeted" },
62   { SOLVER_NOTBYUSER, "notbyuser" },
63   { SOLVER_SETEV,     "setev" },
64   { SOLVER_SETEVR,    "setevr" },
65   { SOLVER_SETARCH,   "setarch" },
66   { SOLVER_SETVENDOR, "setvendor" },
67   { SOLVER_SETREPO,   "setrepo" },
68   { SOLVER_NOAUTOSET, "noautoset" },
69   { 0, 0 }
70 };
71
72 static struct resultflags2str {
73   Id flag;
74   const char *str;
75 } resultflags2str[] = {
76   { TESTCASE_RESULT_TRANSACTION,        "transaction" },
77   { TESTCASE_RESULT_PROBLEMS,           "problems" },
78   { TESTCASE_RESULT_ORPHANED,           "orphaned" },
79   { TESTCASE_RESULT_RECOMMENDED,        "recommended" },
80   { TESTCASE_RESULT_UNNEEDED,           "unneeded" },
81   { TESTCASE_RESULT_ALTERNATIVES,       "alternatives" },
82   { TESTCASE_RESULT_RULES,              "rules" },
83   { TESTCASE_RESULT_GENID,              "genid" },
84   { 0, 0 }
85 };
86
87 static struct solverflags2str {
88   Id flag;
89   const char *str;
90   int def;
91 } solverflags2str[] = {
92   { SOLVER_FLAG_ALLOW_DOWNGRADE,            "allowdowngrade", 0 },
93   { SOLVER_FLAG_ALLOW_NAMECHANGE,           "allownamechange", 1 },
94   { SOLVER_FLAG_ALLOW_ARCHCHANGE,           "allowarchchange", 0 },
95   { SOLVER_FLAG_ALLOW_VENDORCHANGE,         "allowvendorchange", 0 },
96   { SOLVER_FLAG_ALLOW_UNINSTALL,            "allowuninstall", 0 },
97   { SOLVER_FLAG_NO_UPDATEPROVIDE,           "noupdateprovide", 0 },
98   { SOLVER_FLAG_SPLITPROVIDES,              "splitprovides", 0 },
99   { SOLVER_FLAG_IGNORE_RECOMMENDED,         "ignorerecommended", 0 },
100   { SOLVER_FLAG_ADD_ALREADY_RECOMMENDED,    "addalreadyrecommended", 0 },
101   { SOLVER_FLAG_NO_INFARCHCHECK,            "noinfarchcheck", 0 },
102   { SOLVER_FLAG_KEEP_EXPLICIT_OBSOLETES,    "keepexplicitobsoletes", 0 },
103   { SOLVER_FLAG_BEST_OBEY_POLICY,           "bestobeypolicy", 0 },
104   { SOLVER_FLAG_NO_AUTOTARGET,              "noautotarget", 0 },
105   { SOLVER_FLAG_DUP_ALLOW_DOWNGRADE,        "dupallowdowngrade", 1 },
106   { SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE,       "dupallowarchchange", 1 },
107   { SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE,     "dupallowvendorchange", 1 },
108   { SOLVER_FLAG_DUP_ALLOW_NAMECHANGE,       "dupallownamechange", 1 },
109   { SOLVER_FLAG_KEEP_ORPHANS,               "keeporphans", 0 },
110   { SOLVER_FLAG_BREAK_ORPHANS,              "breakorphans", 0 },
111   { SOLVER_FLAG_FOCUS_INSTALLED,            "focusinstalled", 0 },
112   { SOLVER_FLAG_YUM_OBSOLETES,              "yumobsoletes", 0 },
113   { SOLVER_FLAG_NEED_UPDATEPROVIDE,         "needupdateprovide", 0 },
114   { 0, 0, 0 }
115 };
116
117 static struct poolflags2str {
118   Id flag;
119   const char *str;
120   int def;
121 } poolflags2str[] = {
122   { POOL_FLAG_PROMOTEEPOCH,                 "promoteepoch", 0 },
123   { POOL_FLAG_FORBIDSELFCONFLICTS,          "forbidselfconflicts", 0 },
124   { POOL_FLAG_OBSOLETEUSESPROVIDES,         "obsoleteusesprovides", 0 },
125   { POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES, "implicitobsoleteusesprovides", 0 },
126   { POOL_FLAG_OBSOLETEUSESCOLORS,           "obsoleteusescolors", 0 },
127   { POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS,   "implicitobsoleteusescolors", 0 },
128   { POOL_FLAG_NOINSTALLEDOBSOLETES,         "noinstalledobsoletes", 0 },
129   { POOL_FLAG_HAVEDISTEPOCH,                "havedistepoch", 0 },
130   { POOL_FLAG_NOOBSOLETESMULTIVERSION,      "noobsoletesmultiversion", 0 },
131   { POOL_FLAG_ADDFILEPROVIDESFILTERED,      "addfileprovidesfiltered", 0 },
132   { POOL_FLAG_NOWHATPROVIDESAUX,            "nowhatprovidesaux", 0 },
133   { 0, 0, 0 }
134 };
135
136 static struct disttype2str {
137   Id type;
138   const char *str;
139 } disttype2str[] = {
140   { DISTTYPE_RPM,  "rpm" },
141   { DISTTYPE_DEB,  "deb" },
142   { DISTTYPE_ARCH, "arch" },
143   { DISTTYPE_HAIKU, "haiku" },
144   { 0, 0 }
145 };
146
147 static struct selflags2str {
148   Id flag;
149   const char *str;
150 } selflags2str[] = {
151   { SELECTION_NAME, "name" },
152   { SELECTION_PROVIDES, "provides" },
153   { SELECTION_FILELIST, "filelist" },
154   { SELECTION_CANON, "canon" },
155   { SELECTION_DOTARCH, "dotarch" },
156   { SELECTION_REL, "rel" },
157   { SELECTION_INSTALLED_ONLY, "installedonly" },
158   { SELECTION_GLOB, "glob" },
159   { SELECTION_FLAT, "flat" },
160   { SELECTION_NOCASE, "nocase" },
161   { SELECTION_SOURCE_ONLY, "sourceonly" },
162   { SELECTION_WITH_SOURCE, "withsource" },
163   { 0, 0 }
164 };
165
166 static const char *features[] = {
167 #ifdef ENABLE_LINKED_PKGS
168   "linked_packages",
169 #endif
170 #ifdef ENABLE_COMPLEX_DEPS
171   "complex_deps",
172 #endif
173   0
174 };
175
176 typedef struct strqueue {
177   char **str;
178   int nstr;
179 } Strqueue;
180
181 #define STRQUEUE_BLOCK 63
182
183 static void
184 strqueue_init(Strqueue *q)
185 {
186   q->str = 0;
187   q->nstr = 0;
188 }
189
190 static void
191 strqueue_free(Strqueue *q)
192 {
193   int i;
194   for (i = 0; i < q->nstr; i++)
195     solv_free(q->str[i]);
196   q->str = solv_free(q->str);
197   q->nstr = 0;
198 }
199
200 static void
201 strqueue_push(Strqueue *q, const char *s)
202 {
203   q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
204   q->str[q->nstr++] = solv_strdup(s);
205 }
206
207 static void
208 strqueue_pushjoin(Strqueue *q, const char *s1, const char *s2, const char *s3)
209 {
210   q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
211   q->str[q->nstr++] = solv_dupjoin(s1, s2, s3);
212 }
213
214 static int
215 strqueue_sort_cmp(const void *ap, const void *bp, void *dp)
216 {
217   const char *a = *(const char **)ap;
218   const char *b = *(const char **)bp;
219   return strcmp(a ? a : "", b ? b : "");
220 }
221
222 static void
223 strqueue_sort(Strqueue *q)
224 {
225   if (q->nstr > 1)
226     solv_sort(q->str, q->nstr, sizeof(*q->str), strqueue_sort_cmp, 0);
227 }
228
229 static void
230 strqueue_sort_u(Strqueue *q)
231 {
232   int i, j;
233   strqueue_sort(q);
234   for (i = j = 0; i < q->nstr; i++)
235     if (!j || strqueue_sort_cmp(q->str + i, q->str + j - 1, 0) != 0)
236       q->str[j++] = q->str[i];
237   q->nstr = j;
238 }
239
240 static char *
241 strqueue_join(Strqueue *q)
242 {
243   int i, l = 0;
244   char *r, *rp;
245   for (i = 0; i < q->nstr; i++)
246     if (q->str[i])
247       l += strlen(q->str[i]) + 1;
248   l++;  /* trailing \0 */
249   r = solv_malloc(l);
250   rp = r;
251   for (i = 0; i < q->nstr; i++)
252     if (q->str[i])
253       {
254         strcpy(rp, q->str[i]);
255         rp += strlen(rp);
256         *rp++ = '\n';
257       }
258   *rp = 0;
259   return r;
260 }
261
262 static void
263 strqueue_split(Strqueue *q, const char *s)
264 {
265   const char *p;
266   if (!s)
267     return;
268   while ((p = strchr(s, '\n')) != 0)
269     {
270       q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
271       q->str[q->nstr] = solv_malloc(p - s + 1);
272       if (p > s)
273         memcpy(q->str[q->nstr], s, p - s);
274       q->str[q->nstr][p - s] = 0;
275       q->nstr++;
276       s = p + 1;
277     }
278   if (*s)
279     strqueue_push(q, s);
280 }
281
282 static void
283 strqueue_diff(Strqueue *sq1, Strqueue *sq2, Strqueue *osq)
284 {
285   int i = 0, j = 0;
286   while (i < sq1->nstr && j < sq2->nstr)
287     {
288       int r = strqueue_sort_cmp(sq1->str + i, sq2->str + j, 0);
289       if (!r)
290         i++, j++;
291       else if (r < 0)
292         strqueue_pushjoin(osq, "-", sq1->str[i++], 0);
293       else
294         strqueue_pushjoin(osq, "+", sq2->str[j++], 0);
295     }
296   while (i < sq1->nstr)
297     strqueue_pushjoin(osq, "-", sq1->str[i++], 0);
298   while (j < sq2->nstr)
299     strqueue_pushjoin(osq, "+", sq2->str[j++], 0);
300 }
301
302
303 static const char *
304 testcase_id2str(Pool *pool, Id id, int isname)
305 {
306   const char *s = pool_id2str(pool, id);
307   const char *ss;
308   char *s2, *s2p;
309   int bad = 0, paren = 0, parenbad = 0;
310
311   if (id == 0)
312     return "<NULL>";
313   if (id == 1)
314     return "\\00";
315   if (strchr("[(<=>!", *s))
316     bad++;
317   if (!strncmp(s, "namespace:", 10))
318     bad++;
319   for (ss = s + bad; *ss; ss++)
320     {
321       if (*ss == ' ' || *ss == '\\' || *(unsigned char *)ss < 32 || *ss == '(' || *ss == ')')
322         bad++;
323       if (*ss == '(')
324         paren = paren == 0 ? 1 : -1;
325       else if (*ss == ')')
326         {
327           paren = paren == 1 ? 0 : -1;
328           if (!paren)
329             parenbad += 2;
330         }
331     }
332   if (isname && ss - s > 4 && !strcmp(ss - 4, ":any"))
333     bad++;
334   if (!paren && !(bad - parenbad))
335     return s;
336
337   /* we need escaping! */
338   s2 = s2p = pool_alloctmpspace(pool, strlen(s) + bad * 2 + 1);
339   if (!strncmp(s, "namespace:", 10))
340     {
341       strcpy(s2p, "namespace\\3a");
342       s2p += 12;
343       s += 10;
344     }
345   ss = s;
346   for (; *ss; ss++)
347     {
348       *s2p++ = *ss;
349       if ((ss == s && strchr("[(<=>!", *s)) || *ss == ' ' || *ss == '\\' || *(unsigned char *)ss < 32 || *ss == '(' || *ss == ')')
350         {
351           s2p[-1] = '\\';
352           solv_bin2hex((unsigned char *)ss, 1, s2p);
353           s2p += 2;
354         }
355     }
356   *s2p = 0;
357   if (isname && s2p - s2 > 4 && !strcmp(s2p - 4, ":any"))
358     strcpy(s2p - 4, "\\3aany");
359   return s2;
360 }
361
362 struct oplist {
363   Id flags;
364   const char *opname;
365 } oplist[] = {
366   { REL_EQ, "=" },
367   { REL_GT | REL_LT | REL_EQ, "<=>" },
368   { REL_LT | REL_EQ, "<=" },
369   { REL_GT | REL_EQ, ">=" },
370   { REL_GT, ">" },
371   { REL_GT | REL_LT, "<>" },
372   { REL_AND,   "&" },
373   { REL_OR ,   "|" },
374   { REL_WITH , "+" },
375   { REL_NAMESPACE , "<NAMESPACE>" },
376   { REL_ARCH,       "." },
377   { REL_MULTIARCH,  "<MULTIARCH>" },
378   { REL_FILECONFLICT,  "<FILECONFLICT>" },
379   { REL_COND,  "<IF>" },
380   { REL_COMPAT,  "compat >=" },
381   { REL_KIND,  "<KIND>" },
382   { REL_ELSE, "<ELSE>" },
383   { REL_LT, "<" },
384   { 0, 0 }
385 };
386
387 static const char *
388 testcase_dep2str_complex(Pool *pool, Id id, int addparens)
389 {
390   Reldep *rd;
391   char *s;
392   const char *s2;
393   int needparens;
394   struct oplist *op;
395
396   if (!ISRELDEP(id))
397     return testcase_id2str(pool, id, 1);
398   rd = GETRELDEP(pool, id);
399
400   /* check for special shortcuts */
401   if (rd->flags == REL_NAMESPACE && !ISRELDEP(rd->name) && !strncmp(pool_id2str(pool, rd->name), "namespace:", 10))
402     {
403       const char *ns = pool_id2str(pool, rd->name);
404       int nslen = strlen(ns);
405       /* special namespace formatting */
406       const char *evrs = testcase_dep2str_complex(pool, rd->evr, 0);
407       s = pool_tmpappend(pool, evrs, ns, "()");
408       memmove(s + nslen + 1, s, strlen(s) - nslen - 2);
409       memcpy(s, ns, nslen);
410       s[nslen] = '(';
411       return s;
412     }
413   if (rd->flags == REL_MULTIARCH && !ISRELDEP(rd->name) && rd->evr == ARCH_ANY)
414     {
415       /* special :any suffix */
416       const char *ns = testcase_id2str(pool, rd->name, 1);
417       return pool_tmpappend(pool, ns, ":any", 0);
418     }
419
420   needparens = 0;
421   if (ISRELDEP(rd->name))
422     {
423       Reldep *rd2 = GETRELDEP(pool, rd->name);
424       needparens = 1;
425       if (rd->flags > 7 && rd->flags != REL_COMPAT && rd2->flags && rd2->flags <= 7)
426         needparens = 0;
427     }
428   s = (char *)testcase_dep2str_complex(pool, rd->name, needparens);
429
430   if (addparens)
431     {
432       s = pool_tmpappend(pool, s, "(", 0);
433       memmove(s + 1, s, strlen(s + 1));
434       s[0] = '(';
435     }
436   for (op = oplist; op->flags; op++)
437     if (rd->flags == op->flags)
438       break;
439   if (op->flags)
440     {
441       s = pool_tmpappend(pool, s, " ", op->opname);
442       s = pool_tmpappend(pool, s, " ", 0);
443     }
444   else
445     {
446       char buf[64];
447       sprintf(buf, " <%u> ", rd->flags);
448       s = pool_tmpappend(pool, s, buf, 0);
449     }
450
451   needparens = 0;
452   if (ISRELDEP(rd->evr))
453     {
454       Reldep *rd2 = GETRELDEP(pool, rd->evr);
455       needparens = 1;
456       if (rd->flags > 7 && rd2->flags && rd2->flags <= 7)
457         needparens = 0;
458       if (rd->flags == REL_AND && rd2->flags == REL_AND)
459         needparens = 0; /* chain */
460       if (rd->flags == REL_OR && rd2->flags == REL_OR)
461         needparens = 0; /* chain */
462       if (rd->flags > 0 && rd->flags < 8 && rd2->flags == REL_COMPAT)
463         needparens = 0; /* chain */
464     }
465   if (!ISRELDEP(rd->evr))
466     s2 = testcase_id2str(pool, rd->evr, 0);
467   else
468     s2 = testcase_dep2str_complex(pool, rd->evr, needparens);
469   if (addparens)
470     s = pool_tmpappend(pool, s, s2, ")");
471   else
472     s = pool_tmpappend(pool, s, s2, 0);
473   pool_freetmpspace(pool, s2);
474   return s;
475 }
476
477 const char *
478 testcase_dep2str(Pool *pool, Id id)
479 {
480   return testcase_dep2str_complex(pool, id, 0);
481 }
482
483
484 /* Convert a simple string. Also handle the :any suffix */
485 static Id
486 testcase_str2dep_simple(Pool *pool, const char **sp, int isname)
487 {
488   int haveesc = 0;
489   int paren = 0;
490   int isany = 0;
491   Id id;
492   const char *s;
493   for (s = *sp; *s; s++)
494     {
495       if (*s == '\\')
496         haveesc++;
497       if (*s == ' ' || *(unsigned char *)s < 32)
498         break;
499       if (*s == '(')
500         paren++;
501       if (*s == ')' && paren-- <= 0)
502         break;
503     }
504   if (isname && s - *sp > 4 && !strncmp(s - 4, ":any", 4))
505     {
506       isany = 1;
507       s -= 4;
508     }
509   if (!haveesc)
510     {
511       if (s - *sp == 6 && !strncmp(*sp, "<NULL>", 6))
512         id = 0;
513       else
514         id = pool_strn2id(pool, *sp, s - *sp, 1);
515     }
516   else if (s - *sp == 3 && !strncmp(*sp, "\\00", 3))
517     id = 1;
518   else
519     {
520       char buf[128], *bp, *bp2;
521       const char *sp2;
522       bp = s - *sp >= 128 ? solv_malloc(s - *sp + 1) : buf;
523       for (bp2 = bp, sp2 = *sp; sp2 < s;)
524         {
525           *bp2++ = *sp2++;
526           if (bp2[-1] == '\\')
527             solv_hex2bin(&sp2, (unsigned char *)bp2 - 1, 1);
528         }
529       *bp2 = 0;
530       id = pool_str2id(pool, bp, 1);
531       if (bp != buf)
532         solv_free(bp);
533     }
534   if (isany)
535     {
536       id = pool_rel2id(pool, id, ARCH_ANY, REL_MULTIARCH, 1);
537       s += 4;
538     }
539   *sp = s;
540   return id;
541 }
542
543
544 static Id
545 testcase_str2dep_complex(Pool *pool, const char **sp, int relop)
546 {
547   const char *s = *sp;
548   Id flags, id, id2, namespaceid = 0;
549   struct oplist *op;
550
551   while (*s == ' ' || *s == '\t')
552     s++;
553   if (!strncmp(s, "namespace:", 10))
554     {
555       /* special namespace hack */
556       const char *s2;
557       for (s2 = s + 10; *s2 && *s2 != '('; s2++)
558         ;
559       if (*s2 == '(')
560         {
561           namespaceid = pool_strn2id(pool, s, s2 - s, 1);
562           s = s2;
563         }
564     }
565   if (*s == '(')
566     {
567       s++;
568       id = testcase_str2dep_complex(pool, &s, 0);
569       if (!s || *s != ')')
570         {
571           *sp = 0;
572           return 0;
573         }
574       s++;
575     }
576   else
577     id = testcase_str2dep_simple(pool, &s, relop ? 0 : 1);
578   if (namespaceid)
579     id = pool_rel2id(pool, namespaceid, id, REL_NAMESPACE, 1);
580     
581   for (;;)
582     {
583       while (*s == ' ' || *s == '\t')
584         s++;
585       if (!*s || *s == ')' || (relop && strncmp(s, "compat >= ", 10) != 0))
586         {
587           *sp = s;
588           return id;
589         }
590
591       /* we have an op! Find the end */
592       flags = -1;
593       if (s[0] == '<' && (s[1] >= '0' && s[1] <= '9'))
594         {
595           const char *s2;
596           for (s2 = s + 1; *s2 >= '0' && *s2 <= '9'; s2++)
597             ;
598           if (*s2 == '>')
599             {
600               flags = strtoul(s + 1, 0, 10);
601               s = s2 + 1;
602             }
603         }
604       if (flags == -1)
605         {
606           for (op = oplist; op->flags; op++)
607             if (!strncmp(s, op->opname, strlen(op->opname)))
608               break;
609           if (!op->flags)
610             {
611               *sp = 0;
612               return 0;
613             }
614           flags = op->flags;
615           s += strlen(op->opname);
616         }
617       id2 = testcase_str2dep_complex(pool, &s, flags > 0 && flags < 8);
618       if (!s)
619         {
620           *sp = 0;
621           return 0;
622         }
623       id = pool_rel2id(pool, id, id2, flags, 1);
624     }
625 }
626
627 Id
628 testcase_str2dep(Pool *pool, const char *s)
629 {
630   Id id = testcase_str2dep_complex(pool, &s, 0);
631   return s && !*s ? id : 0;
632 }
633
634 /**********************************************************/
635
636 const char *
637 testcase_repoid2str(Pool *pool, Id repoid)
638 {
639   Repo *repo = pool_id2repo(pool, repoid);
640   if (repo->name)
641     {
642       char *r = pool_tmpjoin(pool, repo->name, 0, 0);
643       char *rp;
644       for (rp = r; *rp; rp++)
645         if (*rp == ' ' || *rp == '\t')
646           *rp = '_';
647       return r;
648     }
649   else
650     {
651       char buf[20];
652       sprintf(buf, "#%d", repoid);
653       return pool_tmpjoin(pool, buf, 0, 0);
654     }
655 }
656
657 const char *
658 testcase_solvid2str(Pool *pool, Id p)
659 {
660   Solvable *s = pool->solvables + p;
661   const char *n, *e, *a;
662   char *str, buf[20];
663
664   if (p == SYSTEMSOLVABLE)
665     return "@SYSTEM";
666   n = pool_id2str(pool, s->name);
667   e = pool_id2str(pool, s->evr);
668   a = pool_id2str(pool, s->arch);
669   str = pool_alloctmpspace(pool, strlen(n) + strlen(e) + strlen(a) + 3);
670   sprintf(str, "%s-%s.%s", n, e, a);
671   if (!s->repo)
672     return pool_tmpappend(pool, str, "@", 0);
673   if (s->repo->name)
674     {
675       int l = strlen(str);
676       char *str2 = pool_tmpappend(pool, str, "@", s->repo->name);
677       for (; str2[l]; l++)
678         if (str2[l] == ' ' || str2[l] == '\t')
679           str2[l] = '_';
680       return str2;
681     }
682   sprintf(buf, "@#%d", s->repo->repoid);
683   return pool_tmpappend(pool, str, buf, 0);
684 }
685
686 Repo *
687 testcase_str2repo(Pool *pool, const char *str)
688 {
689   int repoid;
690   Repo *repo = 0;
691   if (str[0] == '#' && (str[1] >= '0' && str[1] <= '9'))
692     {
693       int j;
694       repoid = 0;
695       for (j = 1; str[j] >= '0' && str[j] <= '9'; j++)
696         repoid = repoid * 10 + (str[j] - '0');
697       if (!str[j] && repoid > 0 && repoid < pool->nrepos)
698         repo = pool_id2repo(pool, repoid);
699     }
700   if (!repo)
701     {
702       FOR_REPOS(repoid, repo)
703         {
704           int i, l;
705           if (!repo->name)
706             continue;
707           l = strlen(repo->name);
708           for (i = 0; i < l; i++)
709             {
710               int c = repo->name[i];
711               if (c == ' ' || c == '\t')
712                 c = '_';
713               if (c != str[i])
714                 break;
715             }
716           if (i == l && !str[l])
717             break;
718         }
719       if (repoid >= pool->nrepos)
720         repo = 0;
721     }
722   return repo;
723 }
724
725 Id
726 testcase_str2solvid(Pool *pool, const char *str)
727 {
728   int i, l = strlen(str);
729   int repostart;
730   Repo *repo;
731   Id arch;
732
733   if (!l)
734     return 0;
735   if (*str == '@' && !strcmp(str, "@SYSTEM"))
736     return SYSTEMSOLVABLE;
737   repo = 0;
738   for (i = l - 1; i >= 0; i--)
739     if (str[i] == '@' && (repo = testcase_str2repo(pool, str + i + 1)) != 0)
740       break;
741   if (i < 0)
742     i = l;
743   repostart = i;
744   /* now find the arch (if present) */
745   arch = 0;
746   for (i = repostart - 1; i > 0; i--)
747     if (str[i] == '.')
748       {
749         arch = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
750         if (arch)
751           repostart = i;
752         break;
753       }
754   /* now find the name */
755   for (i = repostart - 1; i > 0; i--)
756     {
757       if (str[i] == '-')
758         {
759           Id nid, evrid, p, pp;
760           nid = pool_strn2id(pool, str, i, 0);
761           if (!nid)
762             continue;
763           evrid = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
764           if (!evrid)
765             continue;
766           FOR_PROVIDES(p, pp, nid)
767             {
768               Solvable *s = pool->solvables + p;
769               if (s->name != nid || s->evr != evrid)
770                 continue;
771               if (repo && s->repo != repo)
772                 continue;
773               if (arch && s->arch != arch)
774                 continue;
775               return p;
776             }
777         }
778     }
779   return 0;
780 }
781
782 const char *
783 testcase_job2str(Pool *pool, Id how, Id what)
784 {
785   char *ret;
786   const char *jobstr;
787   const char *selstr;
788   const char *pkgstr;
789   int i, o;
790   Id select = how & SOLVER_SELECTMASK;
791
792   for (i = 0; job2str[i].str; i++)
793     if ((how & SOLVER_JOBMASK) == job2str[i].job)
794       break;
795   jobstr = job2str[i].str ? job2str[i].str : "unknown";
796   if (select == SOLVER_SOLVABLE)
797     {
798       selstr = " pkg ";
799       pkgstr = testcase_solvid2str(pool, what);
800     }
801   else if (select == SOLVER_SOLVABLE_NAME)
802     {
803       selstr = " name ";
804       pkgstr = testcase_dep2str(pool, what);
805     }
806   else if (select == SOLVER_SOLVABLE_PROVIDES)
807     {
808       selstr = " provides ";
809       pkgstr = testcase_dep2str(pool, what);
810     }
811   else if (select == SOLVER_SOLVABLE_ONE_OF)
812     {
813       Id p;
814       selstr = " oneof ";
815       pkgstr = 0;
816       while ((p = pool->whatprovidesdata[what++]) != 0)
817         {
818           const char *s = testcase_solvid2str(pool, p);
819           if (pkgstr)
820             {
821               pkgstr = pool_tmpappend(pool, pkgstr, " ", s);
822               pool_freetmpspace(pool, s);
823             }
824           else
825             pkgstr = s;
826         }
827       if (!pkgstr)
828         pkgstr = "nothing";
829     }
830   else if (select == SOLVER_SOLVABLE_REPO)
831     {
832       Repo *repo = pool_id2repo(pool, what);
833       selstr = " repo ";
834       if (!repo->name)
835         {
836           char buf[20];
837           sprintf(buf, "#%d", repo->repoid);
838           pkgstr = pool_tmpjoin(pool, buf, 0, 0);
839         }
840       else
841         pkgstr = pool_tmpjoin(pool, repo->name, 0, 0);
842     }
843   else if (select == SOLVER_SOLVABLE_ALL)
844     {
845       selstr = " all ";
846       pkgstr = "packages";
847     }
848   else
849     {
850       selstr = " unknown ";
851       pkgstr = "unknown";
852     }
853   ret = pool_tmpjoin(pool, jobstr, selstr, pkgstr);
854   o = strlen(ret);
855   ret = pool_tmpappend(pool, ret, " ", 0);
856   for (i = 0; jobflags2str[i].str; i++)
857     if ((how & jobflags2str[i].flag) != 0)
858       ret = pool_tmpappend(pool, ret, ",", jobflags2str[i].str);
859   if (!ret[o + 1])
860     ret[o] = 0;
861   else
862     {
863       ret[o + 1] = '[';
864       ret = pool_tmpappend(pool, ret, "]", 0);
865     }
866   return ret;
867 }
868
869 static int
870 str2selflags(Pool *pool, char *s)       /* modifies the string! */
871 {
872   int i, selflags = 0;
873   while (s)
874     {
875       char *se = strchr(s, ',');
876       if (se)
877         *se++ = 0;
878       for (i = 0; selflags2str[i].str; i++)
879         if (!strcmp(s, selflags2str[i].str))
880           {
881             selflags |= selflags2str[i].flag;
882             break;
883           }
884       if (!selflags2str[i].str)
885         pool_debug(pool, SOLV_ERROR, "str2job: unknown selection flag '%s'\n", s);
886       s = se;
887     }
888   return selflags;
889 }
890
891 static int
892 str2jobflags(Pool *pool, char *s)       /* modifies the string */
893 {
894   int i, jobflags = 0;
895   while (s)
896     {
897       char *se = strchr(s, ',');
898       if (se)
899         *se++ = 0;
900       for (i = 0; jobflags2str[i].str; i++)
901         if (!strcmp(s, jobflags2str[i].str))
902           {
903             jobflags |= jobflags2str[i].flag;
904             break;
905           }
906       if (!jobflags2str[i].str)
907         pool_debug(pool, SOLV_ERROR, "str2job: unknown job flag '%s'\n", s);
908       s = se;
909     }
910   return jobflags;
911 }
912
913 Id
914 testcase_str2job(Pool *pool, const char *str, Id *whatp)
915 {
916   int i;
917   Id job;
918   Id what;
919   char *s;
920   char **pieces = 0;
921   int npieces = 0;
922
923   *whatp = 0;
924   /* so we can patch it */
925   s = pool_tmpjoin(pool, str, 0, 0);
926   /* split it in pieces */
927   for (;;)
928     {
929       while (*s == ' ' || *s == '\t')
930         s++;
931       if (!*s)
932         break;
933       pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
934       pieces[npieces++] = s;
935       while (*s && *s != ' ' && *s != '\t')
936         s++;
937       if (*s)
938         *s++ = 0;
939     }
940   if (npieces < 3)
941     {
942       pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
943       solv_free(pieces);
944       return -1;
945     }
946
947   for (i = 0; job2str[i].str; i++)
948     if (!strcmp(pieces[0], job2str[i].str))
949       break;
950   if (!job2str[i].str)
951     {
952       pool_debug(pool, SOLV_ERROR, "str2job: unknown job '%s'\n", str);
953       solv_free(pieces);
954       return -1;
955     }
956   job = job2str[i].job;
957   what = 0;
958   if (npieces > 3)
959     {
960       char *flags = pieces[npieces - 1];
961       if (*flags == '[' && flags[strlen(flags) - 1] == ']')
962         {
963           npieces--;
964           flags++;
965           flags[strlen(flags) - 1] = 0;
966           job |= str2jobflags(pool, flags);
967         }
968     }
969   if (!strcmp(pieces[1], "pkg"))
970     {
971       if (npieces != 3)
972         {
973           pool_debug(pool, SOLV_ERROR, "str2job: bad pkg selector in '%s'\n", str);
974           solv_free(pieces);
975           return -1;
976         }
977       job |= SOLVER_SOLVABLE;
978       what = testcase_str2solvid(pool, pieces[2]);
979       if (!what)
980         {
981           pool_debug(pool, SOLV_ERROR, "str2job: unknown package '%s'\n", pieces[2]);
982           solv_free(pieces);
983           return -1;
984         }
985     }
986   else if (!strcmp(pieces[1], "name") || !strcmp(pieces[1], "provides"))
987     {
988       /* join em again for dep2str... */
989       char *sp;
990       for (sp = pieces[2]; sp < pieces[npieces - 1]; sp++)
991         if (*sp == 0)
992           *sp = ' ';
993       what = 0;
994       if (pieces[1][0] == 'p' && strncmp(pieces[2], "namespace:", 10) == 0)
995         {
996           char *spe = strchr(pieces[2], '(');
997           int l = strlen(pieces[2]);
998           if (spe && pieces[2][l - 1] == ')')
999             {
1000               /* special namespace provides */
1001               if (strcmp(spe, "(<NULL>)") != 0)
1002                 {
1003                   pieces[2][l - 1] = 0;
1004                   what = testcase_str2dep(pool, spe + 1);
1005                   pieces[2][l - 1] = ')';
1006                 }
1007               what = pool_rel2id(pool, pool_strn2id(pool, pieces[2], spe - pieces[2], 1), what, REL_NAMESPACE, 1);
1008             }
1009         }
1010       if (!what)
1011         what = testcase_str2dep(pool, pieces[2]);
1012       if (pieces[1][0] == 'n')
1013         job |= SOLVER_SOLVABLE_NAME;
1014       else
1015         job |= SOLVER_SOLVABLE_PROVIDES;
1016     }
1017   else if (!strcmp(pieces[1], "oneof"))
1018     {
1019       Queue q;
1020       job |= SOLVER_SOLVABLE_ONE_OF;
1021       queue_init(&q);
1022       if (npieces > 3 && strcmp(pieces[2], "nothing") != 0)
1023         {
1024           for (i = 2; i < npieces; i++)
1025             {
1026               Id p = testcase_str2solvid(pool, pieces[i]);
1027               if (!p)
1028                 {
1029                   pool_debug(pool, SOLV_ERROR, "str2job: unknown package '%s'\n", pieces[i]);
1030                   queue_free(&q);
1031                   solv_free(pieces);
1032                   return -1;
1033                 }
1034               queue_push(&q, p);
1035             }
1036         }
1037       what = pool_queuetowhatprovides(pool, &q);
1038       queue_free(&q);
1039     }
1040   else if (!strcmp(pieces[1], "repo"))
1041     {
1042       Repo *repo;
1043       if (npieces != 3)
1044         {
1045           pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
1046           solv_free(pieces);
1047           return -1;
1048         }
1049       repo = testcase_str2repo(pool, pieces[2]);
1050       if (!repo)
1051         {
1052           pool_debug(pool, SOLV_ERROR, "str2job: unknown repo '%s'\n", pieces[2]);
1053           solv_free(pieces);
1054           return -1;
1055         }
1056       job |= SOLVER_SOLVABLE_REPO;
1057       what = repo->repoid;
1058     }
1059   else if (!strcmp(pieces[1], "all"))
1060     {
1061       if (npieces != 3 && strcmp(pieces[2], "packages") != 0)
1062         {
1063           pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
1064           solv_free(pieces);
1065           return -1;
1066         }
1067       job |= SOLVER_SOLVABLE_ALL;
1068       what = 0;
1069     }
1070   else
1071     {
1072       pool_debug(pool, SOLV_ERROR, "str2job: unknown selection in '%s'\n", str);
1073       solv_free(pieces);
1074       return -1;
1075     }
1076   *whatp = what;
1077   solv_free(pieces);
1078   return job;
1079 }
1080
1081 int
1082 addselectionjob(Pool *pool, char **pieces, int npieces, Queue *jobqueue)
1083 {
1084   Id job;
1085   int i, r;
1086   int selflags;
1087   Queue sel;
1088
1089   for (i = 0; job2str[i].str; i++)
1090     if (!strcmp(pieces[0], job2str[i].str))
1091       break;
1092   if (!job2str[i].str)
1093     {
1094       pool_debug(pool, SOLV_ERROR, "selstr2job: unknown job '%s'\n", pieces[0]);
1095       return -1;
1096     }
1097   job = job2str[i].job;
1098   if (npieces > 3)
1099     {
1100       char *flags = pieces[npieces - 1];
1101       if (*flags == '[' && flags[strlen(flags) - 1] == ']')
1102         {
1103           npieces--;
1104           flags++;
1105           flags[strlen(flags) - 1] = 0;
1106           job |= str2jobflags(pool, flags);
1107         }
1108     }
1109   if (npieces < 4)
1110     {
1111       pool_debug(pool, SOLV_ERROR, "selstr2job: no selection flags\n");
1112       return -1;
1113     }
1114   selflags = str2selflags(pool, pieces[3]);
1115   queue_init(&sel);
1116   r = selection_make(pool, &sel, pieces[2], selflags);
1117   for (i = 0; i < sel.count; i += 2)
1118     queue_push2(jobqueue, job | sel.elements[i], sel.elements[i + 1]);
1119   queue_free(&sel);
1120   return r;
1121 }
1122
1123 static void
1124 writedeps(Repo *repo, FILE *fp, const char *tag, Id key, Solvable *s, Offset off)
1125 {
1126   Pool *pool = repo->pool;
1127   Id id, *dp;
1128   int tagwritten = 0;
1129   const char *idstr;
1130
1131   if (!off)
1132     return;
1133   dp = repo->idarraydata + off;
1134   while ((id = *dp++) != 0)
1135     {
1136       if (key == SOLVABLE_REQUIRES && id == SOLVABLE_PREREQMARKER)
1137         {
1138           if (tagwritten)
1139             fprintf(fp, "-%s\n", tag);
1140           tagwritten = 0;
1141           tag = "Prq:";
1142           continue;
1143         }
1144       if (key == SOLVABLE_PROVIDES && id == SOLVABLE_FILEMARKER)
1145         continue;
1146       idstr = testcase_dep2str(pool, id);
1147       if (!tagwritten)
1148         {
1149           fprintf(fp, "+%s\n", tag);
1150           tagwritten = 1;
1151         }
1152       fprintf(fp, "%s\n", idstr);
1153     }
1154   if (tagwritten)
1155     fprintf(fp, "-%s\n", tag);
1156 }
1157
1158 static void
1159 writefilelist(Repo *repo, FILE *fp, const char *tag, Solvable *s)
1160 {
1161   Pool *pool = repo->pool;
1162   int tagwritten = 0;
1163   Dataiterator di;
1164
1165   dataiterator_init(&di, pool, repo, s - pool->solvables, SOLVABLE_FILELIST, 0, 0);
1166   while (dataiterator_step(&di))
1167     {
1168       const char *s = repodata_dir2str(di.data, di.kv.id, di.kv.str);
1169       if (!tagwritten)
1170         {
1171           fprintf(fp, "+%s\n", tag);
1172           tagwritten = 1;
1173         }
1174       fprintf(fp, "%s\n", s);
1175     }
1176   if (tagwritten)
1177     fprintf(fp, "-%s\n", tag);
1178   dataiterator_free(&di);
1179 }
1180
1181 int
1182 testcase_write_testtags(Repo *repo, FILE *fp)
1183 {
1184   Pool *pool = repo->pool;
1185   Solvable *s;
1186   Id p;
1187   const char *name;
1188   const char *evr;
1189   const char *arch;
1190   const char *release;
1191   const char *tmp;
1192   unsigned int ti;
1193   Queue q;
1194
1195   fprintf(fp, "=Ver: 3.0\n");
1196   queue_init(&q);
1197   FOR_REPO_SOLVABLES(repo, p, s)
1198     {
1199       name = pool_id2str(pool, s->name);
1200       evr = pool_id2str(pool, s->evr);
1201       arch = pool_id2str(pool, s->arch);
1202       release = strrchr(evr, '-');
1203       if (!release)
1204         release = evr + strlen(evr);
1205       fprintf(fp, "=Pkg: %s %.*s %s %s\n", name, (int)(release - evr), evr, *release && release[1] ? release + 1 : "-", arch);
1206       tmp = solvable_lookup_str(s, SOLVABLE_SUMMARY);
1207       if (tmp)
1208         fprintf(fp, "=Sum: %s\n", tmp);
1209       writedeps(repo, fp, "Req:", SOLVABLE_REQUIRES, s, s->requires);
1210       writedeps(repo, fp, "Prv:", SOLVABLE_PROVIDES, s, s->provides);
1211       writedeps(repo, fp, "Obs:", SOLVABLE_OBSOLETES, s, s->obsoletes);
1212       writedeps(repo, fp, "Con:", SOLVABLE_CONFLICTS, s, s->conflicts);
1213       writedeps(repo, fp, "Rec:", SOLVABLE_RECOMMENDS, s, s->recommends);
1214       writedeps(repo, fp, "Sup:", SOLVABLE_SUPPLEMENTS, s, s->supplements);
1215       writedeps(repo, fp, "Sug:", SOLVABLE_SUGGESTS, s, s->suggests);
1216       writedeps(repo, fp, "Enh:", SOLVABLE_ENHANCES, s, s->enhances);
1217       if (solvable_lookup_idarray(s, SOLVABLE_PREREQ_IGNOREINST, &q))
1218         {
1219           int i;
1220           fprintf(fp, "+Ipr:\n");
1221           for (i = 0; i < q.count; i++)
1222             fprintf(fp, "%s\n", testcase_dep2str(pool, q.elements[i]));
1223           fprintf(fp, "-Ipr:\n");
1224         }
1225       if (s->vendor)
1226         fprintf(fp, "=Vnd: %s\n", pool_id2str(pool, s->vendor));
1227       ti = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0);
1228       if (ti)
1229         fprintf(fp, "=Tim: %u\n", ti);
1230       writefilelist(repo, fp, "Fls:", s);
1231     }
1232   queue_free(&q);
1233   return 0;
1234 }
1235
1236 static inline Offset
1237 adddep(Repo *repo, Offset olddeps, char *str, Id marker)
1238 {
1239   Id id = *str == '/' ? pool_str2id(repo->pool, str, 1) : testcase_str2dep(repo->pool, str);
1240   return repo_addid_dep(repo, olddeps, id, marker);
1241 }
1242
1243 static void
1244 finish_v2_solvable(Pool *pool, Repodata *data, Solvable *s, char *filelist, int nfilelist)
1245 {
1246   if (nfilelist)
1247     {
1248       int l;
1249       Id did;
1250       for (l = 0; l < nfilelist; l += strlen(filelist + l) + 1)
1251         {
1252           char *p = strrchr(filelist + l, '/');
1253           if (!p)
1254             continue;
1255           *p++ = 0;
1256           did = repodata_str2dir(data, filelist + l, 1);
1257           p[-1] = '/';
1258           if (!did)
1259             did = repodata_str2dir(data, "/", 1);
1260           repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, did, p);
1261         }
1262     }
1263   s->supplements = repo_fix_supplements(s->repo, s->provides, s->supplements, 0);
1264   s->conflicts = repo_fix_conflicts(s->repo, s->conflicts);
1265 }
1266
1267 /* stripped down version of susetags parser used for testcases */
1268 int
1269 testcase_add_testtags(Repo *repo, FILE *fp, int flags)
1270 {
1271   Pool *pool = repo->pool;
1272   char *line, *linep;
1273   int aline;
1274   int tag;
1275   Repodata *data;
1276   Solvable *s;
1277   char *sp[5];
1278   unsigned int t;
1279   int intag;
1280   char *filelist = 0;
1281   int afilelist = 0;
1282   int nfilelist = 0;
1283   int tagsversion = 0;
1284   int addselfprovides = 1;      /* for compat reasons */
1285
1286   data = repo_add_repodata(repo, flags);
1287   s = 0;
1288   intag = 0;
1289
1290   aline = 1024;
1291   line = solv_malloc(aline);
1292   linep = line;
1293   for (;;)
1294     {
1295       if (linep - line + 16 > aline)
1296         {
1297           aline = linep - line;
1298           line = solv_realloc(line, aline + 512);
1299           linep = line + aline;
1300           aline += 512;
1301         }
1302       if (!fgets(linep, aline - (linep - line), fp))
1303         break;
1304       linep += strlen(linep);
1305       if (linep == line || linep[-1] != '\n')
1306         continue;
1307       linep[-1] = 0;
1308       linep = line + intag;
1309       if (intag)
1310         {
1311           if (line[intag] == '-' && !strncmp(line + 1, line + intag + 1, intag - 2))
1312             {
1313               intag = 0;
1314               linep = line;
1315               continue;
1316             }
1317         }
1318       else if (line[0] == '+' && line[1] && line[1] != ':')
1319         {
1320           char *tagend = strchr(line, ':');
1321           if (!tagend)
1322             continue;
1323           line[0] = '=';
1324           tagend[1] = ' ';
1325           intag = tagend + 2 - line;
1326           linep = line + intag;
1327           continue;
1328         }
1329       if (*line != '=' || !line[1] || !line[2] || !line[3] || line[4] != ':')
1330         continue;
1331       tag = line[1] << 16 | line[2] << 8 | line[3];
1332       switch(tag)
1333         {
1334         case 'V' << 16 | 'e' << 8 | 'r':
1335           tagsversion = atoi(line + 6);
1336           addselfprovides = tagsversion < 3 || strstr(line + 6, "addselfprovides") != 0;
1337           break;
1338         case 'P' << 16 | 'k' << 8 | 'g':
1339           if (s)
1340             {
1341               if (tagsversion == 2)
1342                 finish_v2_solvable(pool, data, s, filelist, nfilelist);
1343               if (addselfprovides && s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
1344                 s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
1345             }
1346           nfilelist = 0;
1347           if (split(line + 5, sp, 5) != 4)
1348             break;
1349           s = pool_id2solvable(pool, repo_add_solvable(repo));
1350           s->name = pool_str2id(pool, sp[0], 1);
1351           /* join back version and release */
1352           if (sp[2] && !(sp[2][0] == '-' && !sp[2][1]))
1353             sp[2][-1] = '-';
1354           s->evr = makeevr(pool, sp[1]);
1355           s->arch = pool_str2id(pool, sp[3], 1);
1356           break;
1357         case 'S' << 16 | 'u' << 8 | 'm':
1358           repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line + 6);
1359           break;
1360         case 'V' << 16 | 'n' << 8 | 'd':
1361           s->vendor = pool_str2id(pool, line + 6, 1);
1362           break;
1363         case 'T' << 16 | 'i' << 8 | 'm':
1364           t = atoi(line + 6);
1365           if (t)
1366             repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, t);
1367           break;
1368         case 'R' << 16 | 'e' << 8 | 'q':
1369           s->requires = adddep(repo, s->requires, line + 6, -SOLVABLE_PREREQMARKER);
1370           break;
1371         case 'P' << 16 | 'r' << 8 | 'q':
1372           s->requires = adddep(repo, s->requires, line + 6, SOLVABLE_PREREQMARKER);
1373           break;
1374         case 'P' << 16 | 'r' << 8 | 'v':
1375           /* version 2 had the file list at the end of the provides */
1376           if (tagsversion == 2)
1377             {
1378               if (line[6] == '/')
1379                 {
1380                   int l = strlen(line + 6) + 1;
1381                   if (nfilelist + l > afilelist)
1382                     {
1383                       afilelist = nfilelist + l + 512;
1384                       filelist = solv_realloc(filelist, afilelist);
1385                     }
1386                   memcpy(filelist + nfilelist, line + 6, l);
1387                   nfilelist += l;
1388                   break;
1389                 }
1390               if (nfilelist)
1391                 {
1392                   int l;
1393                   for (l = 0; l < nfilelist; l += strlen(filelist + l) + 1)
1394                     s->provides = repo_addid_dep(repo, s->provides, pool_str2id(pool, filelist + l, 1), 0);
1395                   nfilelist = 0;
1396                 }
1397             }
1398           s->provides = adddep(repo, s->provides, line + 6, 0);
1399           break;
1400         case 'F' << 16 | 'l' << 8 | 's':
1401           {
1402             char *p = strrchr(line + 6, '/');
1403             Id did;
1404             if (!p)
1405               break;
1406             *p++ = 0;
1407             did = repodata_str2dir(data, line + 6, 1);
1408             if (!did)
1409               did = repodata_str2dir(data, "/", 1);
1410             repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, did, p);
1411             break;
1412           }
1413         case 'O' << 16 | 'b' << 8 | 's':
1414           s->obsoletes = adddep(repo, s->obsoletes, line + 6, 0);
1415           break;
1416         case 'C' << 16 | 'o' << 8 | 'n':
1417           s->conflicts = adddep(repo, s->conflicts, line + 6, 0);
1418           break;
1419         case 'R' << 16 | 'e' << 8 | 'c':
1420           s->recommends = adddep(repo, s->recommends, line + 6, 0);
1421           break;
1422         case 'S' << 16 | 'u' << 8 | 'p':
1423           s->supplements = adddep(repo, s->supplements, line + 6, 0);
1424           break;
1425         case 'S' << 16 | 'u' << 8 | 'g':
1426           s->suggests = adddep(repo, s->suggests, line + 6, 0);
1427           break;
1428         case 'E' << 16 | 'n' << 8 | 'h':
1429           s->enhances = adddep(repo, s->enhances, line + 6, 0);
1430           break;
1431         case 'I' << 16 | 'p' << 8 | 'r':
1432           {
1433             Id id = line[6] == '/' ? pool_str2id(pool, line + 6, 1) : testcase_str2dep(pool, line + 6);
1434             repodata_add_idarray(data, s - pool->solvables, SOLVABLE_PREREQ_IGNOREINST, id);
1435             break;
1436           }
1437         default:
1438           break;
1439         }
1440     }
1441   if (s)
1442     {
1443       if (tagsversion == 2)
1444         finish_v2_solvable(pool, data, s, filelist, nfilelist);
1445       if (addselfprovides && s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
1446         s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
1447     }
1448   solv_free(line);
1449   solv_free(filelist);
1450   repodata_free_dircache(data);
1451   if (!(flags & REPO_NO_INTERNALIZE))
1452     repodata_internalize(data);
1453   return 0;
1454 }
1455
1456 const char *
1457 testcase_getpoolflags(Pool *pool)
1458 {
1459   const char *str = 0;
1460   int i, v;
1461   for (i = 0; poolflags2str[i].str; i++)
1462     {
1463       v = pool_get_flag(pool, poolflags2str[i].flag);
1464       if (v == poolflags2str[i].def)
1465         continue;
1466       str = pool_tmpappend(pool, str, v ? " " : " !", poolflags2str[i].str);
1467     }
1468   return str ? str + 1 : "";
1469 }
1470
1471 int
1472 testcase_setpoolflags(Pool *pool, const char *str)
1473 {
1474   const char *p = str, *s;
1475   int i, v;
1476   for (;;)
1477     {
1478       while (*p == ' ' || *p == '\t' || *p == ',')
1479         p++;
1480       v = 1;
1481       if (*p == '!')
1482         {
1483           p++;
1484           v = 0;
1485         }
1486       if (!*p)
1487         break;
1488       s = p;
1489       while (*p && *p != ' ' && *p != '\t' && *p != ',')
1490         p++;
1491       for (i = 0; poolflags2str[i].str; i++)
1492         if (!strncmp(poolflags2str[i].str, s, p - s) && poolflags2str[i].str[p - s] == 0)
1493           break;
1494       if (!poolflags2str[i].str)
1495         {
1496           pool_debug(pool, SOLV_ERROR, "setpoolflags: unknown flag '%.*s'\n", (int)(p - s), s);
1497           return 0;
1498         }
1499       pool_set_flag(pool, poolflags2str[i].flag, v);
1500     }
1501   return 1;
1502 }
1503
1504 void
1505 testcase_resetpoolflags(Pool *pool)
1506 {
1507   int i;
1508   for (i = 0; poolflags2str[i].str; i++)
1509     pool_set_flag(pool, poolflags2str[i].flag, poolflags2str[i].def);
1510 }
1511
1512 const char *
1513 testcase_getsolverflags(Solver *solv)
1514 {
1515   Pool *pool = solv->pool;
1516   const char *str = 0;
1517   int i, v;
1518   for (i = 0; solverflags2str[i].str; i++)
1519     {
1520       v = solver_get_flag(solv, solverflags2str[i].flag);
1521       if (v == solverflags2str[i].def)
1522         continue;
1523       str = pool_tmpappend(pool, str, v ? " " : " !", solverflags2str[i].str);
1524     }
1525   return str ? str + 1 : "";
1526 }
1527
1528 int
1529 testcase_setsolverflags(Solver *solv, const char *str)
1530 {
1531   const char *p = str, *s;
1532   int i, v;
1533   for (;;)
1534     {
1535       while (*p == ' ' || *p == '\t' || *p == ',')
1536         p++;
1537       v = 1;
1538       if (*p == '!')
1539         {
1540           p++;
1541           v = 0;
1542         }
1543       if (!*p)
1544         break;
1545       s = p;
1546       while (*p && *p != ' ' && *p != '\t' && *p != ',')
1547         p++;
1548       for (i = 0; solverflags2str[i].str; i++)
1549         if (!strncmp(solverflags2str[i].str, s, p - s) && solverflags2str[i].str[p - s] == 0)
1550           break;
1551       if (!solverflags2str[i].str)
1552         {
1553           pool_debug(solv->pool, SOLV_ERROR, "setsolverflags: unknown flag '%.*s'\n", (int)(p - s), s);
1554           return 0;
1555         }
1556       solver_set_flag(solv, solverflags2str[i].flag, v);
1557     }
1558   return 1;
1559 }
1560
1561 void
1562 testcase_resetsolverflags(Solver *solv)
1563 {
1564   int i;
1565   for (i = 0; solverflags2str[i].str; i++)
1566     solver_set_flag(solv, solverflags2str[i].flag, solverflags2str[i].def);
1567 }
1568
1569 static const char *
1570 testcase_ruleid(Solver *solv, Id rid)
1571 {
1572   Strqueue sq;
1573   Queue q;
1574   int i;
1575   Chksum *chk;
1576   const unsigned char *md5;
1577   int md5l;
1578   const char *s;
1579
1580   queue_init(&q);
1581   strqueue_init(&sq);
1582   solver_ruleliterals(solv, rid, &q);
1583   for (i = 0; i < q.count; i++)
1584     {
1585       Id p = q.elements[i];
1586       s = testcase_solvid2str(solv->pool, p > 0 ? p : -p);
1587       if (p < 0)
1588         s = pool_tmpjoin(solv->pool, "!", s, 0);
1589       strqueue_push(&sq, s);
1590     }
1591   queue_free(&q);
1592   strqueue_sort_u(&sq);
1593   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1594   for (i = 0; i < sq.nstr; i++)
1595     solv_chksum_add(chk, sq.str[i], strlen(sq.str[i]) + 1);
1596   md5 = solv_chksum_get(chk, &md5l);
1597   s = pool_bin2hex(solv->pool, md5, md5l);
1598   chk = solv_chksum_free(chk, 0);
1599   strqueue_free(&sq);
1600   return s;
1601 }
1602
1603 static const char *
1604 testcase_problemid(Solver *solv, Id problem)
1605 {
1606   Strqueue sq;
1607   Queue q;
1608   Chksum *chk;
1609   const unsigned char *md5;
1610   int i, md5l;
1611   const char *s;
1612
1613   /* we build a hash of all rules that define the problem */
1614   queue_init(&q);
1615   strqueue_init(&sq);
1616   solver_findallproblemrules(solv, problem, &q);
1617   for (i = 0; i < q.count; i++)
1618     strqueue_push(&sq, testcase_ruleid(solv, q.elements[i]));
1619   queue_free(&q);
1620   strqueue_sort_u(&sq);
1621   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1622   for (i = 0; i < sq.nstr; i++)
1623     solv_chksum_add(chk, sq.str[i], strlen(sq.str[i]) + 1);
1624   md5 = solv_chksum_get(chk, &md5l);
1625   s = pool_bin2hex(solv->pool, md5, 4);
1626   chk = solv_chksum_free(chk, 0);
1627   strqueue_free(&sq);
1628   return s;
1629 }
1630
1631 static const char *
1632 testcase_solutionid(Solver *solv, Id problem, Id solution)
1633 {
1634   Id intid;
1635   Chksum *chk;
1636   const unsigned char *md5;
1637   int md5l;
1638   const char *s;
1639
1640   intid = solver_solutionelement_internalid(solv, problem, solution);
1641   /* internal stuff! handle with care! */
1642   if (intid < 0)
1643     {
1644       /* it's a job */
1645       s = testcase_job2str(solv->pool, solv->job.elements[-intid - 1], solv->job.elements[-intid]);
1646     }
1647   else
1648     {
1649       /* it's a rule */
1650       s = testcase_ruleid(solv, intid);
1651     }
1652   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1653   solv_chksum_add(chk, s, strlen(s) + 1);
1654   md5 = solv_chksum_get(chk, &md5l);
1655   s = pool_bin2hex(solv->pool, md5, 4);
1656   chk = solv_chksum_free(chk, 0);
1657   return s;
1658 }
1659
1660 static const char *
1661 testcase_alternativeid(Solver *solv, int type, Id id, Id from)
1662 {
1663   const char *s;
1664   Pool *pool = solv->pool;
1665   Chksum *chk;
1666   const unsigned char *md5;
1667   int md5l;
1668   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1669   if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
1670     {
1671       s = testcase_solvid2str(pool, from);
1672       solv_chksum_add(chk, s, strlen(s) + 1);
1673       s = testcase_dep2str(pool, id);
1674       solv_chksum_add(chk, s, strlen(s) + 1);
1675     }
1676   else if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
1677     {
1678       s = testcase_ruleid(solv, id);
1679       solv_chksum_add(chk, s, strlen(s) + 1);
1680     }
1681   md5 = solv_chksum_get(chk, &md5l);
1682   s = pool_bin2hex(pool, md5, 4);
1683   chk = solv_chksum_free(chk, 0);
1684   return s;
1685 }
1686
1687 static struct class2str {
1688   Id class;
1689   const char *str;
1690 } class2str[] = {
1691   { SOLVER_TRANSACTION_ERASE,          "erase" },
1692   { SOLVER_TRANSACTION_INSTALL,        "install" },
1693   { SOLVER_TRANSACTION_REINSTALLED,    "reinstall" },
1694   { SOLVER_TRANSACTION_DOWNGRADED,     "downgrade" },
1695   { SOLVER_TRANSACTION_CHANGED,        "change" },
1696   { SOLVER_TRANSACTION_UPGRADED,       "upgrade" },
1697   { SOLVER_TRANSACTION_OBSOLETED,      "obsolete" },
1698   { SOLVER_TRANSACTION_MULTIINSTALL,   "multiinstall" },
1699   { SOLVER_TRANSACTION_MULTIREINSTALL, "multireinstall" },
1700   { 0, 0 }
1701 };
1702
1703 static int
1704 dump_genid(Pool *pool, Strqueue *sq, Id id, int cnt)
1705 {
1706   struct oplist *op;
1707   char cntbuf[20];
1708   const char *s;
1709
1710   if (ISRELDEP(id))
1711     {
1712       Reldep *rd = GETRELDEP(pool, id);
1713       for (op = oplist; op->flags; op++)
1714         if (rd->flags == op->flags)
1715           break;
1716       cnt = dump_genid(pool, sq, rd->name, cnt);
1717       cnt = dump_genid(pool, sq, rd->evr, cnt);
1718       sprintf(cntbuf, "genid %2d: genid ", cnt++);
1719       s = pool_tmpjoin(pool, cntbuf, "op ", op->flags ? op->opname : "unknown");
1720     }
1721   else
1722     {
1723       sprintf(cntbuf, "genid %2d: genid ", cnt++);
1724       s = pool_tmpjoin(pool, cntbuf, id ? "lit " : "null", id ? pool_id2str(pool, id) : 0);
1725     }
1726   strqueue_push(sq, s);
1727   return cnt;
1728 }
1729
1730 char *
1731 testcase_solverresult(Solver *solv, int resultflags)
1732 {
1733   Pool *pool = solv->pool;
1734   int i, j;
1735   Id p, op;
1736   const char *s;
1737   char *result;
1738   Strqueue sq;
1739
1740   strqueue_init(&sq);
1741   if ((resultflags & TESTCASE_RESULT_TRANSACTION) != 0)
1742     {
1743       Transaction *trans = solver_create_transaction(solv);
1744       Queue q;
1745
1746       queue_init(&q);
1747       for (i = 0; class2str[i].str; i++)
1748         {
1749           queue_empty(&q);
1750           transaction_classify_pkgs(trans, SOLVER_TRANSACTION_KEEP_PSEUDO, class2str[i].class, 0, 0, &q);
1751           for (j = 0; j < q.count; j++)
1752             {
1753               p = q.elements[j];
1754               op = 0;
1755               if (pool->installed && pool->solvables[p].repo == pool->installed)
1756                 op = transaction_obs_pkg(trans, p);
1757               s = pool_tmpjoin(pool, class2str[i].str, " ", testcase_solvid2str(pool, p));
1758               if (op)
1759                 s = pool_tmpjoin(pool, s, " ", testcase_solvid2str(pool, op));
1760               strqueue_push(&sq, s);
1761             }
1762         }
1763       queue_free(&q);
1764       transaction_free(trans);
1765     }
1766   if ((resultflags & TESTCASE_RESULT_PROBLEMS) != 0)
1767     {
1768       char *probprefix, *solprefix;
1769       int problem, solution, element;
1770       int pcnt, scnt;
1771
1772       pcnt = solver_problem_count(solv);
1773       for (problem = 1; problem <= pcnt; problem++)
1774         {
1775           Id rid, from, to, dep;
1776           SolverRuleinfo rinfo;
1777           rid = solver_findproblemrule(solv, problem);
1778           s = testcase_problemid(solv, problem);
1779           probprefix = solv_dupjoin("problem ", s, 0);
1780           rinfo = solver_ruleinfo(solv, rid, &from, &to, &dep);
1781           s = pool_tmpjoin(pool, probprefix, " info ", solver_problemruleinfo2str(solv, rinfo, from, to, dep));
1782           strqueue_push(&sq, s);
1783           scnt = solver_solution_count(solv, problem);
1784           for (solution = 1; solution <= scnt; solution++)
1785             {
1786               s = testcase_solutionid(solv, problem, solution);
1787               solprefix = solv_dupjoin(probprefix, " solution ", s);
1788               element = 0;
1789               while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &op)) != 0)
1790                 {
1791                   if (p == SOLVER_SOLUTION_JOB)
1792                     s = pool_tmpjoin(pool, solprefix, " deljob ", testcase_job2str(pool, solv->job.elements[op - 1], solv->job.elements[op]));
1793                   else if (p > 0 && op == 0)
1794                     s = pool_tmpjoin(pool, solprefix, " erase ", testcase_solvid2str(pool, p));
1795                   else if (p > 0 && op > 0)
1796                     {
1797                       s = pool_tmpjoin(pool, solprefix, " replace ", testcase_solvid2str(pool, p));
1798                       s = pool_tmpappend(pool, s, " ", testcase_solvid2str(pool, op));
1799                     }
1800                   else if (p < 0 && op > 0)
1801                     s = pool_tmpjoin(pool, solprefix, " allow ", testcase_solvid2str(pool, op));
1802                   else
1803                     s = pool_tmpjoin(pool, solprefix, " unknown", 0);
1804                   strqueue_push(&sq, s);
1805                 }
1806               solv_free(solprefix);
1807             }
1808           solv_free(probprefix);
1809         }
1810     }
1811
1812   if ((resultflags & TESTCASE_RESULT_ORPHANED) != 0)
1813     {
1814       Queue q;
1815
1816       queue_init(&q);
1817       solver_get_orphaned(solv, &q);
1818       for (i = 0; i < q.count; i++)
1819         {
1820           s = pool_tmpjoin(pool, "orphaned ", testcase_solvid2str(pool, q.elements[i]), 0);
1821           strqueue_push(&sq, s);
1822         }
1823       queue_free(&q);
1824     }
1825
1826   if ((resultflags & TESTCASE_RESULT_RECOMMENDED) != 0)
1827     {
1828       Queue qr, qs;
1829
1830       queue_init(&qr);
1831       queue_init(&qs);
1832       solver_get_recommendations(solv, &qr, &qs, 0);
1833       for (i = 0; i < qr.count; i++)
1834         {
1835           s = pool_tmpjoin(pool, "recommended ", testcase_solvid2str(pool, qr.elements[i]), 0);
1836           strqueue_push(&sq, s);
1837         }
1838       for (i = 0; i < qs.count; i++)
1839         {
1840           s = pool_tmpjoin(pool, "suggested ", testcase_solvid2str(pool, qs.elements[i]), 0);
1841           strqueue_push(&sq, s);
1842         }
1843       queue_free(&qr);
1844       queue_free(&qs);
1845     }
1846
1847   if ((resultflags & TESTCASE_RESULT_UNNEEDED) != 0)
1848     {
1849       Queue q, qf;
1850
1851       queue_init(&q);
1852       queue_init(&qf);
1853       solver_get_unneeded(solv, &q, 0);
1854       solver_get_unneeded(solv, &qf, 1);
1855       for (i = j = 0; i < q.count; i++)
1856         {
1857           /* we rely on qf containing a subset of q in the same order */
1858           if (j < qf.count && q.elements[i] == qf.elements[j])
1859             {
1860               s = pool_tmpjoin(pool, "unneeded_filtered ", testcase_solvid2str(pool, q.elements[i]), 0);
1861               j++;
1862             }
1863           else
1864             s = pool_tmpjoin(pool, "unneeded ", testcase_solvid2str(pool, q.elements[i]), 0);
1865           strqueue_push(&sq, s);
1866         }
1867       queue_free(&q);
1868       queue_free(&qf);
1869     }
1870   if ((resultflags & TESTCASE_RESULT_ALTERNATIVES) != 0)
1871     {
1872       char *altprefix;
1873       Queue q, rq;
1874       int cnt;
1875       Id alternative;
1876       queue_init(&q);
1877       queue_init(&rq);
1878       cnt = solver_alternatives_count(solv);
1879       for (alternative = 1; alternative <= cnt; alternative++)
1880         {
1881           Id id, from, chosen;
1882           char num[20];
1883           int type = solver_get_alternative(solv, alternative, &id, &from, &chosen, &q, 0);
1884           altprefix = solv_dupjoin("alternative ", testcase_alternativeid(solv, type, id, from), " ");
1885           strcpy(num, " 0 ");
1886           if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
1887             {
1888               char *s = pool_tmpjoin(pool, altprefix, num, testcase_solvid2str(pool, from));
1889               s = pool_tmpappend(pool, s, " recommends ", testcase_dep2str(pool, id));
1890               strqueue_push(&sq, s);
1891             }
1892           else if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
1893             {
1894               /* map choice rules back to pkg rules */
1895               if (solver_ruleclass(solv, id) == SOLVER_RULE_CHOICE)
1896                 id = solver_rule2pkgrule(solv, id);
1897               solver_allruleinfos(solv, id, &rq);
1898               for (i = 0; i < rq.count; i += 4)
1899                 {
1900                   int rtype = rq.elements[i];
1901                   if ((rtype & SOLVER_RULE_TYPEMASK) == SOLVER_RULE_JOB)
1902                     {
1903                       const char *js = testcase_job2str(pool, rq.elements[i + 2], rq.elements[i + 3]);
1904                       char *s = pool_tmpjoin(pool, altprefix, num, " job ");
1905                       s = pool_tmpappend(pool, s, js, 0);
1906                       strqueue_push(&sq, s);
1907                     }
1908                   else if (rtype == SOLVER_RULE_PKG_REQUIRES)
1909                     {
1910                       char *s = pool_tmpjoin(pool, altprefix, num, testcase_solvid2str(pool, rq.elements[i + 1]));
1911                       s = pool_tmpappend(pool, s, " requires ", testcase_dep2str(pool, rq.elements[i + 3]));
1912                       strqueue_push(&sq, s);
1913                     }
1914                 }
1915             }
1916           for (i = 0; i < q.count; i++)
1917             {
1918               Id p = q.elements[i];
1919               if (i >= 9)
1920                 num[0] = '0' + (i + 1) / 10;
1921               num[1] = '0' + (i + 1) % 10;
1922               if (-p == chosen)
1923                 s = pool_tmpjoin(pool, altprefix, num, "+ ");
1924               else if (p < 0)
1925                 s = pool_tmpjoin(pool, altprefix, num, "- ");
1926               else if (p >= 0)
1927                 s = pool_tmpjoin(pool, altprefix, num, "  ");
1928               s = pool_tmpappend(pool, s,  testcase_solvid2str(pool, p < 0 ? -p : p), 0);
1929               strqueue_push(&sq, s);
1930             }
1931           solv_free(altprefix);
1932         }
1933       queue_free(&q);
1934       queue_free(&rq);
1935     }
1936   if ((resultflags & TESTCASE_RESULT_RULES) != 0)
1937     {
1938       /* dump all rules */
1939       Id rid;
1940       SolverRuleinfo rclass;
1941       Queue q;
1942       int i;
1943
1944       queue_init(&q);
1945       for (rid = 1; (rclass = solver_ruleclass(solv, rid)) != SOLVER_RULE_UNKNOWN; rid++)
1946         {
1947           char *prefix;
1948           switch (rclass)
1949             {
1950             case SOLVER_RULE_PKG:
1951               prefix = "pkg ";
1952               break;
1953             case SOLVER_RULE_UPDATE:
1954               prefix = "update ";
1955               break;
1956             case SOLVER_RULE_FEATURE:
1957               prefix = "feature ";
1958               break;
1959             case SOLVER_RULE_JOB:
1960               prefix = "job ";
1961               break;
1962             case SOLVER_RULE_DISTUPGRADE:
1963               prefix = "distupgrade ";
1964               break;
1965             case SOLVER_RULE_INFARCH:
1966               prefix = "infarch ";
1967               break;
1968             case SOLVER_RULE_CHOICE:
1969               prefix = "choice ";
1970               break;
1971             case SOLVER_RULE_LEARNT:
1972               prefix = "learnt ";
1973               break;
1974             case SOLVER_RULE_BEST:
1975               prefix = "best ";
1976               break;
1977             case SOLVER_RULE_YUMOBS:
1978               prefix = "yumobs ";
1979               break;
1980             default:
1981               prefix = "unknown ";
1982               break;
1983             }
1984           prefix = solv_dupjoin("rule ", prefix, testcase_ruleid(solv, rid));
1985           solver_ruleliterals(solv, rid, &q);
1986           if (rclass == SOLVER_RULE_FEATURE && q.count == 1 && q.elements[0] == -SYSTEMSOLVABLE)
1987             continue;
1988           for (i = 0; i < q.count; i++)
1989             {
1990               Id p = q.elements[i];
1991               const char *s;
1992               if (p < 0)
1993                 s = pool_tmpjoin(pool, prefix, " -", testcase_solvid2str(pool, -p));
1994               else
1995                 s = pool_tmpjoin(pool, prefix, "  ", testcase_solvid2str(pool, p));
1996               strqueue_push(&sq, s);
1997             }
1998           solv_free(prefix);
1999         }
2000       queue_free(&q);
2001     }
2002   if ((resultflags & TESTCASE_RESULT_GENID) != 0)
2003     {
2004       for (i = 0 ; i < solv->job.count; i += 2)
2005         {
2006           Id id, id2;
2007           if (solv->job.elements[i] != (SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES))
2008             continue;
2009           id = solv->job.elements[i + 1];
2010           s = testcase_dep2str(pool, id);
2011           strqueue_push(&sq, pool_tmpjoin(pool, "genid dep ", s, 0));
2012           if ((id2 = testcase_str2dep(pool, s)) != id)
2013             {
2014               s = pool_tmpjoin(pool, "genid roundtrip error: ", testcase_dep2str(pool, id2), 0);
2015               strqueue_push(&sq, s);
2016             }
2017           dump_genid(pool, &sq, id, 1);
2018         }
2019     }
2020   strqueue_sort(&sq);
2021   result = strqueue_join(&sq);
2022   strqueue_free(&sq);
2023   return result;
2024 }
2025
2026
2027 int
2028 testcase_write(Solver *solv, const char *dir, int resultflags, const char *testcasename, const char *resultname)
2029 {
2030   Pool *pool = solv->pool;
2031   Repo *repo;
2032   int i;
2033   Id arch, repoid;
2034   Id lowscore;
2035   FILE *fp;
2036   Strqueue sq;
2037   char *cmd, *out;
2038   const char *s;
2039
2040   if (!testcasename)
2041     testcasename = "testcase.t";
2042   if (!resultname)
2043     resultname = "solver.result";
2044
2045   if (mkdir(dir, 0777) && errno != EEXIST)
2046     {
2047       pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not create directory '%s'\n", dir);
2048       return 0;
2049     }
2050   strqueue_init(&sq);
2051   FOR_REPOS(repoid, repo)
2052     {
2053       const char *name = testcase_repoid2str(pool, repoid);
2054       char priobuf[50];
2055       if (repo->subpriority)
2056         sprintf(priobuf, "%d.%d", repo->priority, repo->subpriority);
2057       else
2058         sprintf(priobuf, "%d", repo->priority);
2059       out = pool_tmpjoin(pool, name, ".repo", ".gz");
2060       cmd = pool_tmpjoin(pool, "repo ", name, " ");
2061       cmd = pool_tmpappend(pool, cmd, priobuf, " ");
2062       cmd = pool_tmpappend(pool, cmd, "testtags ", out);
2063       strqueue_push(&sq, cmd);
2064       out = pool_tmpjoin(pool, dir, "/", out);
2065       if (!(fp = solv_xfopen(out, "w")))
2066         {
2067           pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
2068           strqueue_free(&sq);
2069           return 0;
2070         }
2071       testcase_write_testtags(repo, fp);
2072       if (fclose(fp))
2073         {
2074           pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
2075           strqueue_free(&sq);
2076           return 0;
2077         }
2078     }
2079   /* hmm, this is not optimal... we currently search for the lowest score */
2080   lowscore = 0;
2081   arch = pool->solvables[SYSTEMSOLVABLE].arch;
2082   for (i = 0; i < pool->lastarch; i++)
2083     {
2084       if (pool->id2arch[i] == 1 && !lowscore)
2085         arch = i;
2086       if (pool->id2arch[i] > 0x10000 && (!lowscore || pool->id2arch[i] < lowscore))
2087         {
2088           arch = i;
2089           lowscore = pool->id2arch[i];
2090         }
2091     }
2092   cmd = pool_tmpjoin(pool, "system ", pool->lastarch ? pool_id2str(pool, arch) : "unset", 0);
2093   for (i = 0; disttype2str[i].str != 0; i++)
2094     if (pool->disttype == disttype2str[i].type)
2095       break;
2096   pool_tmpappend(pool, cmd, " ", disttype2str[i].str ? disttype2str[i].str : "unknown");
2097   if (pool->installed)
2098     cmd = pool_tmpappend(pool, cmd, " ", testcase_repoid2str(pool, pool->installed->repoid));
2099   strqueue_push(&sq, cmd);
2100   s = testcase_getpoolflags(solv->pool);
2101   if (*s)
2102     {
2103       cmd = pool_tmpjoin(pool, "poolflags ", s, 0);
2104       strqueue_push(&sq, cmd);
2105     }
2106
2107   if (pool->vendorclasses)
2108     {
2109       cmd = 0;
2110       for (i = 0; pool->vendorclasses[i]; i++)
2111         {
2112           cmd = pool_tmpappend(pool, cmd ? cmd : "vendorclass", " ", pool->vendorclasses[i]);
2113           if (!pool->vendorclasses[i + 1])
2114             {
2115               strqueue_push(&sq, cmd);
2116               cmd = 0;
2117               i++;
2118             }
2119         }
2120     }
2121
2122   /* dump disabled packages (must come before the namespace/job lines) */
2123   if (pool->considered)
2124     {
2125       Id p;
2126       FOR_POOL_SOLVABLES(p)
2127         if (!MAPTST(pool->considered, p))
2128           {
2129             cmd = pool_tmpjoin(pool, "disable pkg ", testcase_solvid2str(pool, p), 0);
2130             strqueue_push(&sq, cmd);
2131           }
2132     }
2133
2134   s = testcase_getsolverflags(solv);
2135   if (*s)
2136     {
2137       cmd = pool_tmpjoin(pool, "solverflags ", s, 0);
2138       strqueue_push(&sq, cmd);
2139     }
2140
2141   /* now dump all the ns callback values we know */
2142   if (pool->nscallback)
2143     {
2144       Id rid;
2145       int d;
2146       for (rid = 1; rid < pool->nrels; rid++)
2147         {
2148           Reldep *rd = pool->rels + rid;
2149           if (rd->flags != REL_NAMESPACE || rd->name == NAMESPACE_OTHERPROVIDERS)
2150             continue;
2151           /* evaluate all namespace ids, skip empty results */
2152           d = pool_whatprovides(pool, MAKERELDEP(rid));
2153           if (!d || !pool->whatprovidesdata[d])
2154             continue;
2155           cmd = pool_tmpjoin(pool, "namespace ", pool_id2str(pool, rd->name), "(");
2156           cmd = pool_tmpappend(pool, cmd, pool_id2str(pool, rd->evr), ")");
2157           for (;  pool->whatprovidesdata[d]; d++)
2158             cmd = pool_tmpappend(pool, cmd, " ", testcase_solvid2str(pool, pool->whatprovidesdata[d]));
2159           strqueue_push(&sq, cmd);
2160         }
2161     }
2162
2163   for (i = 0; i < solv->job.count; i += 2)
2164     {
2165       cmd = (char *)testcase_job2str(pool, solv->job.elements[i], solv->job.elements[i + 1]);
2166       cmd = pool_tmpjoin(pool, "job ", cmd, 0);
2167       strqueue_push(&sq, cmd);
2168     }
2169
2170   if (resultflags)
2171     {
2172       char *result;
2173       cmd = 0;
2174       for (i = 0; resultflags2str[i].str; i++)
2175         if ((resultflags & resultflags2str[i].flag) != 0)
2176           cmd = pool_tmpappend(pool, cmd, cmd ? "," : 0, resultflags2str[i].str);
2177       cmd = pool_tmpjoin(pool, "result ", cmd ? cmd : "?", 0);
2178       cmd = pool_tmpappend(pool, cmd, " ", resultname);
2179       strqueue_push(&sq, cmd);
2180       result = testcase_solverresult(solv, resultflags);
2181       if (!strcmp(resultname, "<inline>"))
2182         {
2183           int i;
2184           Strqueue rsq;
2185           strqueue_init(&rsq);
2186           strqueue_split(&rsq, result);
2187           for (i = 0; i < rsq.nstr; i++)
2188             {
2189               cmd = pool_tmpjoin(pool, "#>", rsq.str[i], 0);
2190               strqueue_push(&sq, cmd);
2191             }
2192           strqueue_free(&rsq);
2193         }
2194       else
2195         {
2196           out = pool_tmpjoin(pool, dir, "/", resultname);
2197           if (!(fp = fopen(out, "w")))
2198             {
2199               pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
2200               solv_free(result);
2201               strqueue_free(&sq);
2202               return 0;
2203             }
2204           if (result && *result && fwrite(result, strlen(result), 1, fp) != 1)
2205             {
2206               pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
2207               solv_free(result);
2208               strqueue_free(&sq);
2209               fclose(fp);
2210               return 0;
2211             }
2212           if (fclose(fp))
2213             {
2214               pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
2215               strqueue_free(&sq);
2216               return 0;
2217             }
2218         }
2219       solv_free(result);
2220     }
2221
2222   cmd = strqueue_join(&sq);
2223   out = pool_tmpjoin(pool, dir, "/", testcasename);
2224   if (!(fp = fopen(out, "w")))
2225     {
2226       pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
2227       strqueue_free(&sq);
2228       return 0;
2229     }
2230   if (*cmd && fwrite(cmd, strlen(cmd), 1, fp) != 1)
2231     {
2232       pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
2233       strqueue_free(&sq);
2234       fclose(fp);
2235       return 0;
2236     }
2237   if (fclose(fp))
2238     {
2239       pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
2240       strqueue_free(&sq);
2241       return 0;
2242     }
2243   solv_free(cmd);
2244   strqueue_free(&sq);
2245   return 1;
2246 }
2247
2248 static char *
2249 read_inline_file(FILE *fp, char **bufp, char **bufpp, int *buflp)
2250 {
2251   char *result = solv_malloc(1024);
2252   char *rp = result;
2253   int resultl = 1024;
2254
2255   for (;;)
2256     {
2257       size_t rl;
2258       if (rp - result + 256 >= resultl)
2259         {
2260           resultl = rp - result;
2261           result = solv_realloc(result, resultl + 1024);
2262           rp = result + resultl;
2263           resultl += 1024;
2264         }
2265       if (!fgets(rp, resultl - (rp - result), fp))
2266         *rp = 0;
2267       rl = strlen(rp);
2268       if (rl && (rp == result || rp[-1] == '\n'))
2269         {
2270           if (rl > 1 && rp[0] == '#' && rp[1] == '>')
2271             {
2272               memmove(rp, rp + 2, rl - 2);
2273               rl -= 2;
2274             }
2275           else
2276             {
2277               while (rl + 16 > *buflp)
2278                 {
2279                   *bufp = solv_realloc(*bufp, *buflp + 512);
2280                   *buflp += 512;
2281                 }
2282               memmove(*bufp, rp, rl);
2283               if ((*bufp)[rl - 1] == '\n')
2284                 {
2285                   ungetc('\n', fp);
2286                   rl--;
2287                 }
2288               (*bufp)[rl] = 0;
2289               (*bufpp) = *bufp + rl;
2290               rl = 0;
2291             }
2292         }
2293       if (rl <= 0)
2294         {
2295           *rp = 0;
2296           break;
2297         }
2298       rp += rl;
2299     }
2300   return result;
2301 }
2302
2303 static char *
2304 read_file(FILE *fp)
2305 {
2306   char *result = solv_malloc(1024);
2307   char *rp = result;
2308   int resultl = 1024;
2309
2310   for (;;)
2311     {
2312       size_t rl;
2313       if (rp - result + 256 >= resultl)
2314         {
2315           resultl = rp - result;
2316           result = solv_realloc(result, resultl + 1024);
2317           rp = result + resultl;
2318           resultl += 1024;
2319         }
2320       rl = fread(rp, 1, resultl - (rp - result), fp);
2321       if (rl <= 0)
2322         {
2323           *rp = 0;
2324           break;
2325         }
2326       rp += rl;
2327     }
2328   return result;
2329 }
2330
2331 static int
2332 str2resultflags(Pool *pool, char *s)    /* modifies the string! */
2333 {
2334   int i, resultflags = 0;
2335   while (s)
2336     {
2337       char *se = strchr(s, ',');
2338       if (se)
2339         *se++ = 0;
2340       for (i = 0; resultflags2str[i].str; i++)
2341         if (!strcmp(s, resultflags2str[i].str))
2342           {
2343             resultflags |= resultflags2str[i].flag;
2344             break;
2345           }
2346       if (!resultflags2str[i].str)
2347         pool_debug(pool, SOLV_ERROR, "result: unknown flag '%s'\n", s);
2348       s = se;
2349     }
2350   return resultflags;
2351 }
2352
2353 Solver *
2354 testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **resultp, int *resultflagsp)
2355 {
2356   Solver *solv;
2357   char *buf, *bufp;
2358   int bufl;
2359   char *testcasedir, *s;
2360   int l;
2361   char **pieces = 0;
2362   int npieces = 0;
2363   int prepared = 0;
2364   int closefp = !fp;
2365   int poolflagsreset = 0;
2366   int missing_features = 0;
2367   Id *genid = 0;
2368   int ngenid = 0;
2369
2370   if (!fp && !(fp = fopen(testcase, "r")))
2371     {
2372       pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", testcase);
2373       return 0;
2374     }
2375   testcasedir = solv_strdup(testcase);
2376   if ((s = strrchr(testcasedir, '/')) != 0)
2377     s[1] = 0;
2378   else
2379     *testcasedir = 0;
2380   bufl = 1024;
2381   buf = solv_malloc(bufl);
2382   bufp = buf;
2383   solv = 0;
2384   for (;;)
2385     {
2386       if (bufp - buf + 16 > bufl)
2387         {
2388           bufl = bufp - buf;
2389           buf = solv_realloc(buf, bufl + 512);
2390           bufp = buf + bufl;
2391           bufl += 512;
2392         }
2393       if (!fgets(bufp, bufl - (bufp - buf), fp))
2394         break;
2395       bufp = buf;
2396       l = strlen(buf);
2397       if (!l || buf[l - 1] != '\n')
2398         {
2399           bufp += l;
2400           continue;
2401         }
2402       buf[--l] = 0;
2403       s = buf;
2404       while (*s && (*s == ' ' || *s == '\t'))
2405         s++;
2406       if (!*s || *s == '#')
2407         continue;
2408       npieces = 0;
2409       /* split it in pieces */
2410       for (;;)
2411         {
2412           while (*s == ' ' || *s == '\t')
2413             s++;
2414           if (!*s)
2415             break;
2416           pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
2417           pieces[npieces++] = s;
2418           while (*s && *s != ' ' && *s != '\t')
2419             s++;
2420           if (*s)
2421             *s++ = 0;
2422         }
2423       pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
2424       pieces[npieces] = 0;
2425       if (!strcmp(pieces[0], "repo") && npieces >= 4)
2426         {
2427           Repo *repo = repo_create(pool, pieces[1]);
2428           FILE *rfp;
2429           int prio, subprio;
2430           const char *rdata;
2431
2432           prepared = 0;
2433           if (!poolflagsreset)
2434             {
2435               poolflagsreset = 1;
2436               testcase_resetpoolflags(pool);    /* hmm */
2437             }
2438           if (sscanf(pieces[2], "%d.%d", &prio, &subprio) != 2)
2439             {
2440               subprio = 0;
2441               prio = atoi(pieces[2]);
2442             }
2443           repo->priority = prio;
2444           repo->subpriority = subprio;
2445           if (strcmp(pieces[3], "empty") != 0)
2446             {
2447               const char *repotype = pool_tmpjoin(pool, pieces[3], 0, 0);       /* gets overwritten in <inline> case */
2448               if (!strcmp(pieces[4], "<inline>"))
2449                 {
2450                   char *idata = read_inline_file(fp, &buf, &bufp, &bufl);
2451                   rdata = "<inline>";
2452                   rfp = solv_xfopen_buf(rdata, &idata, 0, "rf");
2453                 }
2454               else
2455                 {
2456                   rdata = pool_tmpjoin(pool, testcasedir, pieces[4], 0);
2457                   rfp = solv_xfopen(rdata, "r");
2458                 }
2459               if (!rfp)
2460                 {
2461                   pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", rdata);
2462                 }
2463               else if (!strcmp(repotype, "testtags"))
2464                 {
2465                   testcase_add_testtags(repo, rfp, 0);
2466                   fclose(rfp);
2467                 }
2468               else if (!strcmp(repotype, "solv"))
2469                 {
2470                   repo_add_solv(repo, rfp, 0);
2471                   fclose(rfp);
2472                 }
2473 #if 0
2474               else if (!strcmp(repotype, "helix"))
2475                 {
2476                   extern int repo_add_helix(Repo *repo, FILE *fp, int flags);
2477                   repo_add_helix(repo, rfp, 0);
2478                   fclose(rfp);
2479                 }
2480 #endif
2481               else
2482                 {
2483                   fclose(rfp);
2484                   pool_debug(pool, SOLV_ERROR, "testcase_read: unknown repo type for repo '%s'\n", repo->name);
2485                 }
2486             }
2487         }
2488       else if (!strcmp(pieces[0], "system") && npieces >= 3)
2489         {
2490           int i;
2491
2492           /* must set the disttype before the arch */
2493           prepared = 0;
2494           if (strcmp(pieces[2], "*") != 0)
2495             {
2496               char *dp = pieces[2];
2497               while (dp && *dp)
2498                 {
2499                   char *dpe = strchr(dp, ',');
2500                   if (dpe)
2501                     *dpe = 0;
2502                   for (i = 0; disttype2str[i].str != 0; i++)
2503                     if (!strcmp(disttype2str[i].str, dp))
2504                       break;
2505                   if (dpe)
2506                     *dpe++ = ',';
2507                   if (disttype2str[i].str)
2508                     {
2509 #ifdef MULTI_SEMANTICS
2510                       if (pool->disttype != disttype2str[i].type)
2511                         pool_setdisttype(pool, disttype2str[i].type);
2512 #endif
2513                       if (pool->disttype == disttype2str[i].type)
2514                         break;
2515                     }
2516                   dp = dpe;
2517                 }
2518               if (!(dp && *dp))
2519                 {
2520                   pool_debug(pool, SOLV_ERROR, "testcase_read: system: could not change disttype to '%s'\n", pieces[2]);
2521                   missing_features = 1;
2522                 }
2523             }
2524           if (strcmp(pieces[1], "unset") == 0)
2525             pool_setarch(pool, 0);
2526           else if (pieces[1][0] == ':')
2527             pool_setarchpolicy(pool, pieces[1] + 1);
2528           else
2529             pool_setarch(pool, pieces[1]);
2530           if (npieces > 3)
2531             {
2532               Repo *repo = testcase_str2repo(pool, pieces[3]);
2533               if (!repo)
2534                 pool_debug(pool, SOLV_ERROR, "testcase_read: system: unknown repo '%s'\n", pieces[3]);
2535               else
2536                 pool_set_installed(pool, repo);
2537             }
2538         }
2539       else if (!strcmp(pieces[0], "job") && npieces > 1)
2540         {
2541           char *sp;
2542           Id how, what;
2543           if (prepared <= 0)
2544             {
2545               pool_addfileprovides(pool);
2546               pool_createwhatprovides(pool);
2547               prepared = 1;
2548             }
2549           if (npieces >= 3 && !strcmp(pieces[2], "selection"))
2550             {
2551               addselectionjob(pool, pieces + 1, npieces - 1, job);
2552               continue;
2553             }
2554           /* rejoin */
2555           for (sp = pieces[1]; sp < pieces[npieces - 1]; sp++)
2556             if (*sp == 0)
2557               *sp = ' ';
2558           how = testcase_str2job(pool, pieces[1], &what);
2559           if (how >= 0 && job)
2560             queue_push2(job, how, what);
2561         }
2562       else if (!strcmp(pieces[0], "vendorclass") && npieces > 1)
2563         {
2564           pool_addvendorclass(pool, (const char **)(pieces + 1));
2565         }
2566       else if (!strcmp(pieces[0], "namespace") && npieces > 1)
2567         {
2568           int i = strlen(pieces[1]);
2569           s = strchr(pieces[1], '(');
2570           if (!s && pieces[1][i - 1] != ')')
2571             {
2572               pool_debug(pool, SOLV_ERROR, "testcase_read: bad namespace '%s'\n", pieces[1]);
2573             }
2574           else
2575             {
2576               Id name, evr, id;
2577               Queue q;
2578               queue_init(&q);
2579               *s = 0;
2580               pieces[1][i - 1] = 0;
2581               name = pool_str2id(pool, pieces[1], 1);
2582               evr = pool_str2id(pool, s + 1, 1);
2583               *s = '(';
2584               pieces[1][i - 1] = ')';
2585               id = pool_rel2id(pool, name, evr, REL_NAMESPACE, 1);
2586               for (i = 2; i < npieces; i++)
2587                 queue_push(&q, testcase_str2solvid(pool, pieces[i]));
2588               /* now do the callback */
2589               if (prepared <= 0)
2590                 {
2591                   pool_addfileprovides(pool);
2592                   pool_createwhatprovides(pool);
2593                   prepared = 1;
2594                 }
2595               pool->whatprovides_rel[GETRELID(id)] = pool_queuetowhatprovides(pool, &q);
2596               queue_free(&q);
2597             }
2598         }
2599       else if (!strcmp(pieces[0], "poolflags"))
2600         {
2601           int i;
2602           if (!poolflagsreset)
2603             {
2604               poolflagsreset = 1;
2605               testcase_resetpoolflags(pool);    /* hmm */
2606             }
2607           for (i = 1; i < npieces; i++)
2608             testcase_setpoolflags(pool, pieces[i]);
2609         }
2610       else if (!strcmp(pieces[0], "solverflags") && npieces > 1)
2611         {
2612           int i;
2613           if (!solv)
2614             {
2615               solv = solver_create(pool);
2616               testcase_resetsolverflags(solv);
2617             }
2618           for (i = 1; i < npieces; i++)
2619             testcase_setsolverflags(solv, pieces[i]);
2620         }
2621       else if (!strcmp(pieces[0], "result") && npieces > 1)
2622         {
2623           char *result = 0;
2624           int resultflags = str2resultflags(pool, pieces[1]);
2625           const char *rdata;
2626           if (npieces > 2)
2627             {
2628               rdata = pool_tmpjoin(pool, testcasedir, pieces[2], 0);
2629               if (!strcmp(pieces[2], "<inline>"))
2630                 result = read_inline_file(fp, &buf, &bufp, &bufl);
2631               else
2632                 {
2633                   FILE *rfp = fopen(rdata, "r");
2634                   if (!rfp)
2635                     pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", rdata);
2636                   else
2637                     {
2638                       result = read_file(rfp);
2639                       fclose(rfp);
2640                     }
2641                 }
2642             }
2643           if (resultp)
2644             *resultp = result;
2645           else
2646             solv_free(result);
2647           if (resultflagsp)
2648             *resultflagsp = resultflags;
2649         }
2650       else if (!strcmp(pieces[0], "nextjob") && npieces == 1)
2651         {
2652           break;
2653         }
2654       else if (!strcmp(pieces[0], "disable") && npieces == 3)
2655         {
2656           Id p;
2657           if (strcmp(pieces[1], "pkg"))
2658             {
2659               pool_debug(pool, SOLV_ERROR, "testcase_read: bad disable type '%s'\n", pieces[1]);
2660               continue;
2661             }
2662           if (!prepared)
2663             pool_createwhatprovides(pool);
2664           prepared = -1;
2665           if (!pool->considered)
2666             {
2667               pool->considered = solv_calloc(1, sizeof(Map));
2668               map_init(pool->considered, pool->nsolvables);
2669               map_setall(pool->considered);
2670             }
2671           p = testcase_str2solvid(pool, pieces[2]);
2672           if (p)
2673             MAPCLR(pool->considered, p);
2674           else
2675             pool_debug(pool, SOLV_ERROR, "disable: unknown package '%s'\n", pieces[2]);
2676         }
2677       else if (!strcmp(pieces[0], "feature"))
2678         {
2679           int i, j;
2680           for (i = 1; i < npieces; i++)
2681             {
2682               for (j = 0; features[j]; j++)
2683                 if (!strcmp(pieces[i], features[j]))
2684                   break;
2685               if (!features[j])
2686                 {
2687                   pool_debug(pool, SOLV_ERROR, "testcase_read: missing feature '%s'\n", pieces[i]);
2688                   missing_features++;
2689                 }
2690             }
2691           if (missing_features)
2692             break;
2693         }
2694       else if (!strcmp(pieces[0], "genid") && npieces > 1)
2695         {
2696           Id id;
2697           /* rejoin */
2698           if (npieces > 2)
2699             {
2700               char *sp;
2701               for (sp = pieces[2]; sp < pieces[npieces - 1]; sp++)
2702                 if (*sp == 0)
2703                   *sp = ' ';
2704             }
2705           genid = solv_extend(genid, ngenid, 1, sizeof(*genid), 7);
2706           if (!strcmp(pieces[1], "op") && npieces > 2)
2707             {
2708               struct oplist *op;
2709               for (op = oplist; op->flags; op++)
2710                 if (!strncmp(pieces[2], op->opname, strlen(op->opname)))
2711                   break;
2712               if (!op->flags)
2713                 {
2714                   pool_debug(pool, SOLV_ERROR, "testcase_read: genid: unknown op '%s'\n", pieces[2]);
2715                   break;
2716                 }
2717               if (ngenid < 2)
2718                 {
2719                   pool_debug(pool, SOLV_ERROR, "testcase_read: genid: out of stack\n");
2720                   break;
2721                 }
2722               ngenid -= 2;
2723               id = pool_rel2id(pool, genid[ngenid] , genid[ngenid + 1], op->flags, 1);
2724             }
2725           else if (!strcmp(pieces[1], "lit"))
2726             id = pool_str2id(pool, npieces > 2 ? pieces[2] : "", 1);
2727           else if (!strcmp(pieces[1], "null"))
2728             id = 0;
2729           else if (!strcmp(pieces[1], "dep"))
2730             id = testcase_str2dep(pool, pieces[2]);
2731           else
2732             {
2733               pool_debug(pool, SOLV_ERROR, "testcase_read: genid: unknown command '%s'\n", pieces[1]);
2734               break;
2735             }
2736           genid[ngenid++] = id;
2737         }
2738       else
2739         {
2740           pool_debug(pool, SOLV_ERROR, "testcase_read: cannot parse command '%s'\n", pieces[0]);
2741         }
2742     }
2743   while (job && ngenid > 0)
2744     queue_push2(job, SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES, genid[--ngenid]);
2745   genid = solv_free(genid);
2746   buf = solv_free(buf);
2747   pieces = solv_free(pieces);
2748   solv_free(testcasedir);
2749   if (!prepared)
2750     {
2751       pool_addfileprovides(pool);
2752       pool_createwhatprovides(pool);
2753     }
2754   if (!solv)
2755     {
2756       solv = solver_create(pool);
2757       testcase_resetsolverflags(solv);
2758     }
2759   if (closefp)
2760     fclose(fp);
2761   if (missing_features)
2762     {
2763       solver_free(solv);
2764       solv = 0;
2765       if (resultflagsp)
2766         *resultflagsp = 77;     /* hack for testsolv */
2767     }
2768   return solv;
2769 }
2770
2771 char *
2772 testcase_resultdiff(const char *result1, const char *result2)
2773 {
2774   Strqueue sq1, sq2, osq;
2775   char *r;
2776   strqueue_init(&sq1);
2777   strqueue_init(&sq2);
2778   strqueue_init(&osq);
2779   strqueue_split(&sq1, result1);
2780   strqueue_split(&sq2, result2);
2781   strqueue_sort(&sq1);
2782   strqueue_sort(&sq2);
2783   strqueue_diff(&sq1, &sq2, &osq);
2784   r = osq.nstr ? strqueue_join(&osq) : 0;
2785   strqueue_free(&sq1);
2786   strqueue_free(&sq2);
2787   strqueue_free(&osq);
2788   return r;
2789 }
2790