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