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