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