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