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