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