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