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