Merge pull request #41 from dmacvicar/master
[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   { 0, 0 }
49 };
50
51 static struct jobflags2str {
52   Id flag;
53   const char *str;
54 } jobflags2str[] = {
55   { SOLVER_WEAK,      "weak" },
56   { SOLVER_ESSENTIAL, "essential" },
57   { SOLVER_CLEANDEPS, "cleandeps" },
58   { SOLVER_ORUPDATE,  "orupdate" },
59   { SOLVER_FORCEBEST, "forcebest" },
60   { SOLVER_TARGETED,  "targeted" },
61   { SOLVER_SETEV,     "setev" },
62   { SOLVER_SETEVR,    "setevr" },
63   { SOLVER_SETARCH,   "setarch" },
64   { SOLVER_SETVENDOR, "setvendor" },
65   { SOLVER_SETREPO,   "setrepo" },
66   { SOLVER_NOAUTOSET, "noautoset" },
67   { 0, 0 }
68 };
69
70 static struct resultflags2str {
71   Id flag;
72   const char *str;
73 } resultflags2str[] = {
74   { TESTCASE_RESULT_TRANSACTION,        "transaction" },
75   { TESTCASE_RESULT_PROBLEMS,           "problems" },
76   { TESTCASE_RESULT_ORPHANED,           "orphaned" },
77   { TESTCASE_RESULT_RECOMMENDED,        "recommended" },
78   { TESTCASE_RESULT_UNNEEDED,           "unneeded" },
79   { 0, 0 }
80 };
81
82 static struct solverflags2str {
83   Id flag;
84   const char *str;
85   int def;
86 } solverflags2str[] = {
87   { SOLVER_FLAG_ALLOW_DOWNGRADE,            "allowdowngrade", 0 },
88   { SOLVER_FLAG_ALLOW_NAMECHANGE,           "allownamechange", 1 },
89   { SOLVER_FLAG_ALLOW_ARCHCHANGE,           "allowarchchange", 0 },
90   { SOLVER_FLAG_ALLOW_VENDORCHANGE,         "allowvendorchange", 0 },
91   { SOLVER_FLAG_ALLOW_UNINSTALL,            "allowuninstall", 0 },
92   { SOLVER_FLAG_NO_UPDATEPROVIDE,           "noupdateprovide", 0 },
93   { SOLVER_FLAG_SPLITPROVIDES,              "splitprovides", 0 },
94   { SOLVER_FLAG_IGNORE_RECOMMENDED,         "ignorerecommended", 0 },
95   { SOLVER_FLAG_ADD_ALREADY_RECOMMENDED,    "addalreadyrecommended", 0 },
96   { SOLVER_FLAG_NO_INFARCHCHECK,            "noinfarchcheck", 0 },
97   { SOLVER_FLAG_KEEP_EXPLICIT_OBSOLETES,    "keepexplicitobsoletes", 0 },
98   { SOLVER_FLAG_BEST_OBEY_POLICY,           "bestobeypolicy", 0 },
99   { SOLVER_FLAG_NO_AUTOTARGET,              "noautotarget", 0 },
100   { 0, 0, 0 }
101 };
102
103 static struct poolflags2str {
104   Id flag;
105   const char *str;
106   int def;
107 } poolflags2str[] = {
108   { POOL_FLAG_PROMOTEEPOCH,                 "promoteepoch", 0 },
109   { POOL_FLAG_FORBIDSELFCONFLICTS,          "forbidselfconflicts", 0 },
110   { POOL_FLAG_OBSOLETEUSESPROVIDES,         "obsoleteusesprovides", 0 },
111   { POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES, "implicitobsoleteusesprovides", 0 },
112   { POOL_FLAG_OBSOLETEUSESCOLORS,           "obsoleteusescolors", 0 },
113   { POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS,   "implicitobsoleteusescolors", 0 },
114   { POOL_FLAG_NOINSTALLEDOBSOLETES,         "noinstalledobsoletes", 0 },
115   { POOL_FLAG_HAVEDISTEPOCH,                "havedistepoch", 0 },
116   { POOL_FLAG_NOOBSOLETESMULTIVERSION,      "noobsoletesmultiversion", 0 },
117   { POOL_FLAG_ADDFILEPROVIDESFILTERED,      "addfileprovidesfiltered", 0 },
118   { 0, 0, 0 }
119 };
120
121 static struct disttype2str {
122   Id type;
123   const char *str;
124 } disttype2str[] = {
125   { DISTTYPE_RPM,  "rpm" },
126   { DISTTYPE_DEB,  "deb" },
127   { DISTTYPE_ARCH, "arch" },
128   { 0, 0 }
129 };
130
131 static struct selflags2str {
132   Id flag;
133   const char *str;
134 } selflags2str[] = {
135   { SELECTION_NAME, "name" },
136   { SELECTION_PROVIDES, "provides" },
137   { SELECTION_FILELIST, "filelist" },
138   { SELECTION_CANON, "canon" },
139   { SELECTION_DOTARCH, "dotarch" },
140   { SELECTION_REL, "rel" },
141   { SELECTION_INSTALLED_ONLY, "installedonly" },
142   { SELECTION_GLOB, "glob" },
143   { SELECTION_FLAT, "flat" },
144   { SELECTION_NOCASE, "nocase" },
145   { SELECTION_SOURCE_ONLY, "sourceonly" },
146   { SELECTION_WITH_SOURCE, "withsource" },
147   { 0, 0 }
148 };
149
150
151 typedef struct strqueue {
152   char **str;
153   int nstr;
154 } Strqueue;
155
156 #define STRQUEUE_BLOCK 63
157
158 static void
159 strqueue_init(Strqueue *q)
160 {
161   q->str = 0;
162   q->nstr = 0;
163 }
164
165 static void
166 strqueue_free(Strqueue *q)
167 {
168   int i;
169   for (i = 0; i < q->nstr; i++)
170     solv_free(q->str[i]);
171   q->str = solv_free(q->str);
172   q->nstr = 0;
173 }
174
175 static void
176 strqueue_push(Strqueue *q, const char *s)
177 {
178   q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
179   q->str[q->nstr++] = solv_strdup(s);
180 }
181
182 static void
183 strqueue_pushjoin(Strqueue *q, const char *s1, const char *s2, const char *s3)
184 {
185   q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
186   q->str[q->nstr++] = solv_dupjoin(s1, s2, s3);
187 }
188
189 static int
190 strqueue_sort_cmp(const void *ap, const void *bp, void *dp)
191 {
192   const char *a = *(const char **)ap;
193   const char *b = *(const char **)bp;
194   return strcmp(a ? a : "", b ? b : "");
195 }
196
197 static void
198 strqueue_sort(Strqueue *q)
199 {
200   if (q->nstr > 1)
201     solv_sort(q->str, q->nstr, sizeof(*q->str), strqueue_sort_cmp, 0);
202 }
203
204 static void
205 strqueue_sort_u(Strqueue *q)
206 {
207   int i, j;
208   strqueue_sort(q);
209   for (i = j = 0; i < q->nstr; i++)
210     if (!j || strqueue_sort_cmp(q->str + i, q->str + j - 1, 0) != 0)
211       q->str[j++] = q->str[i];
212   q->nstr = j;
213 }
214
215 static char *
216 strqueue_join(Strqueue *q)
217 {
218   int i, l = 0;
219   char *r, *rp;
220   for (i = 0; i < q->nstr; i++)
221     if (q->str[i])
222       l += strlen(q->str[i]) + 1;
223   l++;  /* trailing \0 */
224   r = solv_malloc(l);
225   rp = r;
226   for (i = 0; i < q->nstr; i++)
227     if (q->str[i])
228       {
229         strcpy(rp, q->str[i]);
230         rp += strlen(rp);
231         *rp++ = '\n';
232       }
233   *rp = 0;
234   return r;
235 }
236
237 static void
238 strqueue_split(Strqueue *q, const char *s)
239 {
240   const char *p;
241   if (!s)
242     return;
243   while ((p = strchr(s, '\n')) != 0)
244     {
245       q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
246       q->str[q->nstr] = solv_malloc(p - s + 1);
247       if (p > s)
248         memcpy(q->str[q->nstr], s, p - s);
249       q->str[q->nstr][p - s] = 0;
250       q->nstr++;
251       s = p + 1;
252     }
253   if (*s)
254     strqueue_push(q, s);
255 }
256
257 static void
258 strqueue_diff(Strqueue *sq1, Strqueue *sq2, Strqueue *osq)
259 {
260   int i = 0, j = 0;
261   while (i < sq1->nstr && j < sq2->nstr)
262     {
263       int r = strqueue_sort_cmp(sq1->str + i, sq2->str + j, 0);
264       if (!r)
265         i++, j++;
266       else if (r < 0)
267         strqueue_pushjoin(osq, "-", sq1->str[i++], 0);
268       else
269         strqueue_pushjoin(osq, "+", sq2->str[j++], 0);
270     }
271   while (i < sq1->nstr)
272     strqueue_pushjoin(osq, "-", sq1->str[i++], 0);
273   while (j < sq2->nstr)
274     strqueue_pushjoin(osq, "+", sq2->str[j++], 0);
275 }
276
277 static inline int
278 pool_isknownarch(Pool *pool, Id id)
279 {
280   if (!id || id == ID_EMPTY)
281     return 0;
282   if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
283     return 1;
284   if (!pool->id2arch || (id > pool->lastarch || !pool->id2arch[id]))
285     return 0;
286   return 1;
287 }
288
289 Id
290 testcase_str2dep(Pool *pool, char *s)
291 {
292   char *n, *a;
293   Id id, evr;
294   int flags;
295
296   if ((n = strchr(s, '|')) != 0)
297     {
298       id = testcase_str2dep(pool, n + 1);
299       *n = 0;
300       id = pool_rel2id(pool, testcase_str2dep(pool, s), id, REL_OR, 1);
301       *n = '|';
302       return id;
303     }
304   while (*s == ' ' || *s == '\t')
305     s++;
306   n = s;
307   while (*s && *s != ' ' && *s != '\t' && *s != '<' && *s != '=' && *s != '>')
308     {
309       if (*s == '(')
310         {
311           while (*s && *s != ')')
312             s++;
313         }
314       else
315         s++;
316     }
317   if ((a = strchr(n, '.')) != 0 && a + 1 < s && s[-1] != ')')
318     {
319       Id archid = pool_strn2id(pool, a + 1, s - (a + 1), 0);
320       if (pool_isknownarch(pool, archid))
321         {
322           id = pool_strn2id(pool, n, a - n, 1);
323           id = pool_rel2id(pool, id, archid, REL_ARCH, 1);
324         }
325       else
326         id = pool_strn2id(pool, n, s - n, 1);
327     }
328   else if (s - n > 4 && s[-4] == ':' && !strncmp(s - 4, ":any", 4))
329     {
330       id = pool_strn2id(pool, n, s - n - 4, 1);
331       id = pool_rel2id(pool, id, ARCH_ANY, REL_MULTIARCH, 1);
332     }
333   else
334     id = pool_strn2id(pool, n, s - n, 1);
335   if (!*s)
336     return id;
337   while (*s == ' ' || *s == '\t')
338     s++;
339   flags = 0;
340   if (*s == '!' && s[1] == '=') /* support != as synonym for <> */
341     {
342       flags = REL_LT | REL_GT;
343       s += 2;
344     }
345   for (;;s++)
346     {
347       if (*s == '<')
348         flags |= REL_LT;
349       else if (*s == '=')
350         flags |= REL_EQ;
351       else if (*s == '>')
352         flags |= REL_GT;
353       else
354         break;
355     }
356   if (!flags)
357     return id;
358   while (*s == ' ' || *s == '\t')
359     s++;
360   n = s;
361   while (*s && *s != ' ' && *s != '\t')
362     s++;
363   evr = pool_strn2id(pool, n, s - n, 1);
364   if (*s == ' ' && !strcmp(s, " compat >= "))
365     {
366       s += 11;
367       while (*s == ' ' || *s == '\t')
368         s++;
369       n = s;
370       while (*s && *s != ' ' && *s != '\t')
371         s++;
372       evr = pool_rel2id(pool, evr, pool_strn2id(pool, n, s - n, 1), REL_COMPAT, 1);
373     }
374   return pool_rel2id(pool, id, evr, flags, 1);
375 }
376
377 const char *
378 testcase_repoid2str(Pool *pool, Id repoid)
379 {
380   Repo *repo = pool_id2repo(pool, repoid);
381   if (repo->name)
382     {
383       char *r = pool_tmpjoin(pool, repo->name, 0, 0);
384       char *rp;
385       for (rp = r; *rp; rp++)
386         if (*rp == ' ' || *rp == '\t')
387           *rp = '_';
388       return r;
389     }
390   else
391     {
392       char buf[20];
393       sprintf(buf, "#%d", repoid);
394       return pool_tmpjoin(pool, buf, 0, 0);
395     }
396 }
397
398 const char *
399 testcase_solvid2str(Pool *pool, Id p)
400 {
401   Solvable *s = pool->solvables + p;
402   const char *n, *e, *a;
403   char *str, buf[20];
404
405   if (p == SYSTEMSOLVABLE)
406     return "@SYSTEM";
407   n = pool_id2str(pool, s->name);
408   e = pool_id2str(pool, s->evr);
409   a = pool_id2str(pool, s->arch);
410   str = pool_alloctmpspace(pool, strlen(n) + strlen(e) + strlen(a) + 3);
411   sprintf(str, "%s-%s.%s", n, e, a);
412   if (!s->repo)
413     return pool_tmpappend(pool, str, "@", 0);
414   if (s->repo->name)
415     {
416       int l = strlen(str);
417       char *str2 = pool_tmpappend(pool, str, "@", s->repo->name);
418       for (; str2[l]; l++)
419         if (str2[l] == ' ' || str2[l] == '\t')
420           str2[l] = '_';
421       return str2;
422     }
423   sprintf(buf, "@#%d", s->repo->repoid);
424   return pool_tmpappend(pool, str, buf, 0);
425 }
426
427 Repo *
428 testcase_str2repo(Pool *pool, const char *str)
429 {
430   int repoid;
431   Repo *repo = 0;
432   if (str[0] == '#' && (str[1] >= '0' && str[1] <= '9'))
433     {
434       int j;
435       repoid = 0;
436       for (j = 1; str[j] >= '0' && str[j] <= '9'; j++)
437         repoid = repoid * 10 + (str[j] - '0');
438       if (!str[j] && repoid > 0 && repoid < pool->nrepos)
439         repo = pool_id2repo(pool, repoid);
440     }
441   if (!repo)
442     {
443       FOR_REPOS(repoid, repo)
444         {
445           int i, l;
446           if (!repo->name)
447             continue;
448           l = strlen(repo->name);
449           for (i = 0; i < l; i++)
450             {
451               int c = repo->name[i];
452               if (c == ' ' || c == '\t')
453                 c = '_';
454               if (c != str[i])
455                 break;
456             }
457           if (i == l && !str[l])
458             break;
459         }
460       if (repoid >= pool->nrepos)
461         repo = 0;
462     }
463   return repo;
464 }
465
466 Id
467 testcase_str2solvid(Pool *pool, const char *str)
468 {
469   int i, l = strlen(str);
470   int repostart;
471   Repo *repo;
472   Id arch;
473
474   if (!l)
475     return 0;
476   if (*str == '@' && !strcmp(str, "@SYSTEM"))
477     return SYSTEMSOLVABLE;
478   repo = 0;
479   for (i = l - 1; i >= 0; i--)
480     if (str[i] == '@' && (repo = testcase_str2repo(pool, str + i + 1)) != 0)
481       break;
482   if (i < 0)
483     i = l;
484   repostart = i;
485   /* now find the arch (if present) */
486   arch = 0;
487   for (i = repostart - 1; i > 0; i--)
488     if (str[i] == '.')
489       {
490         arch = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
491         if (arch)
492           repostart = i;
493         break;
494       }
495   /* now find the name */
496   for (i = repostart - 1; i > 0; i--)
497     {
498       if (str[i] == '-')
499         {
500           Id nid, evrid, p, pp;
501           nid = pool_strn2id(pool, str, i, 0);
502           if (!nid)
503             continue;
504           evrid = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
505           if (!evrid)
506             continue;
507           FOR_PROVIDES(p, pp, nid)
508             {
509               Solvable *s = pool->solvables + p;
510               if (s->name != nid || s->evr != evrid)
511                 continue;
512               if (repo && s->repo != repo)
513                 continue;
514               if (arch && s->arch != arch)
515                 continue;
516               return p;
517             }
518         }
519     }
520   return 0;
521 }
522
523 const char *
524 testcase_job2str(Pool *pool, Id how, Id what)
525 {
526   char *ret;
527   const char *jobstr;
528   const char *selstr;
529   const char *pkgstr;
530   int i, o;
531   Id select = how & SOLVER_SELECTMASK;
532
533   for (i = 0; job2str[i].str; i++)
534     if ((how & SOLVER_JOBMASK) == job2str[i].job)
535       break;
536   jobstr = job2str[i].str ? job2str[i].str : "unknown";
537   if (select == SOLVER_SOLVABLE)
538     {
539       selstr = " pkg ";
540       pkgstr = testcase_solvid2str(pool, what);
541     }
542   else if (select == SOLVER_SOLVABLE_NAME)
543     {
544       selstr = " name ";
545       pkgstr = pool_dep2str(pool, what);
546     }
547   else if (select == SOLVER_SOLVABLE_PROVIDES)
548     {
549       selstr = " provides ";
550       pkgstr = pool_dep2str(pool, what);
551     }
552   else if (select == SOLVER_SOLVABLE_ONE_OF)
553     {
554       Id p;
555       selstr = " oneof ";
556       pkgstr = 0;
557       while ((p = pool->whatprovidesdata[what++]) != 0)
558         {
559           const char *s = testcase_solvid2str(pool, p);
560           if (pkgstr)
561             {
562               pkgstr = pool_tmpappend(pool, pkgstr, " ", s);
563               pool_freetmpspace(pool, s);
564             }
565           else
566             pkgstr = s;
567         }
568       if (!pkgstr)
569         pkgstr = "nothing";
570     }
571   else if (select == SOLVER_SOLVABLE_REPO)
572     {
573       Repo *repo = pool_id2repo(pool, what);
574       selstr = " repo ";
575       if (!repo->name)
576         {
577           char buf[20];
578           sprintf(buf, "#%d", repo->repoid);
579           pkgstr = pool_tmpjoin(pool, buf, 0, 0);
580         }
581       else
582         pkgstr = pool_tmpjoin(pool, repo->name, 0, 0);
583     }
584   else if (select == SOLVER_SOLVABLE_ALL)
585     {
586       selstr = " all ";
587       pkgstr = "packages";
588     }
589   else
590     {
591       selstr = " unknown ";
592       pkgstr = "unknown";
593     }
594   ret = pool_tmpjoin(pool, jobstr, selstr, pkgstr);
595   o = strlen(ret);
596   ret = pool_tmpappend(pool, ret, " ", 0);
597   for (i = 0; jobflags2str[i].str; i++)
598     if ((how & jobflags2str[i].flag) != 0)
599       ret = pool_tmpappend(pool, ret, ",", jobflags2str[i].str);
600   if (!ret[o + 1])
601     ret[o] = 0;
602   else
603     {
604       ret[o + 1] = '[';
605       ret = pool_tmpappend(pool, ret, "]", 0);
606     }
607   return ret;
608 }
609
610 static int
611 str2selflags(Pool *pool, char *s)       /* modifies the string! */
612 {
613   int i, selflags = 0;
614   while (s)
615     {
616       char *se = strchr(s, ',');
617       if (se)
618         *se++ = 0;
619       for (i = 0; selflags2str[i].str; i++)
620         if (!strcmp(s, selflags2str[i].str))
621           {
622             selflags |= selflags2str[i].flag;
623             break;
624           }
625       if (!selflags2str[i].str)
626         pool_debug(pool, SOLV_ERROR, "str2job: unknown selection flag '%s'\n", s);
627       s = se;
628     }
629   return selflags;
630 }
631
632 static int
633 str2jobflags(Pool *pool, char *s)       /* modifies the string */
634 {
635   int i, jobflags = 0;
636   while (s)
637     {
638       char *se = strchr(s, ',');
639       if (se)
640         *se++ = 0;
641       for (i = 0; jobflags2str[i].str; i++)
642         if (!strcmp(s, jobflags2str[i].str))
643           {
644             jobflags |= jobflags2str[i].flag;
645             break;
646           }
647       if (!jobflags2str[i].str)
648         pool_debug(pool, SOLV_ERROR, "str2job: unknown job flag '%s'\n", s);
649       s = se;
650     }
651   return jobflags;
652 }
653
654 Id
655 testcase_str2job(Pool *pool, const char *str, Id *whatp)
656 {
657   int i;
658   Id job;
659   Id what;
660   char *s;
661   char **pieces = 0;
662   int npieces = 0;
663
664   *whatp = 0;
665   /* so we can patch it */
666   s = pool_tmpjoin(pool, str, 0, 0);
667   /* split it in pieces */
668   for (;;)
669     {
670       while (*s == ' ' || *s == '\t')
671         s++;
672       if (!*s)
673         break;
674       pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
675       pieces[npieces++] = s;
676       while (*s && *s != ' ' && *s != '\t')
677         s++;
678       if (*s)
679         *s++ = 0;
680     }
681   if (npieces < 3)
682     {
683       pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
684       solv_free(pieces);
685       return -1;
686     }
687
688   for (i = 0; job2str[i].str; i++)
689     if (!strcmp(pieces[0], job2str[i].str))
690       break;
691   if (!job2str[i].str)
692     {
693       pool_debug(pool, SOLV_ERROR, "str2job: unknown job '%s'\n", str);
694       solv_free(pieces);
695       return -1;
696     }
697   job = job2str[i].job;
698   what = 0;
699   if (npieces > 3)
700     {
701       char *flags = pieces[npieces - 1];
702       if (*flags == '[' && flags[strlen(flags) - 1] == ']')
703         {
704           npieces--;
705           flags++;
706           flags[strlen(flags) - 1] = 0;
707           job |= str2jobflags(pool, flags);
708         }
709     }
710   if (!strcmp(pieces[1], "pkg"))
711     {
712       if (npieces != 3)
713         {
714           pool_debug(pool, SOLV_ERROR, "str2job: bad pkg selector in '%s'\n", str);
715           solv_free(pieces);
716           return -1;
717         }
718       job |= SOLVER_SOLVABLE;
719       what = testcase_str2solvid(pool, pieces[2]);
720       if (!what)
721         {
722           pool_debug(pool, SOLV_ERROR, "str2job: unknown package '%s'\n", pieces[2]);
723           solv_free(pieces);
724           return -1;
725         }
726     }
727   else if (!strcmp(pieces[1], "name") || !strcmp(pieces[1], "provides"))
728     {
729       /* join em again for dep2str... */
730       char *sp;
731       for (sp = pieces[2]; sp < pieces[npieces - 1]; sp++)
732         if (*sp == 0)
733           *sp = ' ';
734       what = 0;
735       if (pieces[1][0] == 'p' && strncmp(pieces[2], "namespace:", 10) == 0)
736         {
737           char *spe = strchr(pieces[2], '(');
738           int l = strlen(pieces[2]);
739           if (spe && pieces[2][l - 1] == ')')
740             {
741               /* special namespace provides */
742               if (strcmp(spe, "(<NULL>)") != 0)
743                 {
744                   pieces[2][l - 1] = 0;
745                   what = testcase_str2dep(pool, spe + 1);
746                   pieces[2][l - 1] = ')';
747                 }
748               what = pool_rel2id(pool, pool_strn2id(pool, pieces[2], spe - pieces[2], 1), what, REL_NAMESPACE, 1);
749             }
750         }
751       if (!what)
752         what = testcase_str2dep(pool, pieces[2]);
753       if (pieces[1][0] == 'n')
754         job |= SOLVER_SOLVABLE_NAME;
755       else
756         job |= SOLVER_SOLVABLE_PROVIDES;
757     }
758   else if (!strcmp(pieces[1], "oneof"))
759     {
760       Queue q;
761       job |= SOLVER_SOLVABLE_ONE_OF;
762       queue_init(&q);
763       if (npieces > 3 && strcmp(pieces[2], "nothing") != 0)
764         {
765           for (i = 2; i < npieces; i++)
766             {
767               Id p = testcase_str2solvid(pool, pieces[i]);
768               if (!p)
769                 {
770                   pool_debug(pool, SOLV_ERROR, "str2job: unknown package '%s'\n", pieces[i]);
771                   queue_free(&q);
772                   solv_free(pieces);
773                   return -1;
774                 }
775               queue_push(&q, p);
776             }
777         }
778       what = pool_queuetowhatprovides(pool, &q);
779       queue_free(&q);
780     }
781   else if (!strcmp(pieces[1], "repo"))
782     {
783       Repo *repo;
784       if (npieces != 3)
785         {
786           pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
787           solv_free(pieces);
788           return -1;
789         }
790       repo = testcase_str2repo(pool, pieces[2]);
791       if (!repo)
792         {
793           pool_debug(pool, SOLV_ERROR, "str2job: unknown repo '%s'\n", pieces[2]);
794           solv_free(pieces);
795           return -1;
796         }
797       job |= SOLVER_SOLVABLE_REPO;
798       what = repo->repoid;
799     }
800   else if (!strcmp(pieces[1], "all"))
801     {
802       if (npieces != 3 && strcmp(pieces[2], "packages") != 0)
803         {
804           pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
805           solv_free(pieces);
806           return -1;
807         }
808       job |= SOLVER_SOLVABLE_ALL;
809       what = 0;
810     }
811   else
812     {
813       pool_debug(pool, SOLV_ERROR, "str2job: unknown selection in '%s'\n", str);
814       solv_free(pieces);
815       return -1;
816     }
817   *whatp = what;
818   solv_free(pieces);
819   return job;
820 }
821
822 int
823 addselectionjob(Pool *pool, char **pieces, int npieces, Queue *jobqueue)
824 {
825   Id job;
826   int i, r;
827   int selflags;
828   Queue sel;
829
830   for (i = 0; job2str[i].str; i++)
831     if (!strcmp(pieces[0], job2str[i].str))
832       break;
833   if (!job2str[i].str)
834     {
835       pool_debug(pool, SOLV_ERROR, "selstr2job: unknown job '%s'\n", pieces[0]);
836       return -1;
837     }
838   job = job2str[i].job;
839   if (npieces > 3)
840     {
841       char *flags = pieces[npieces - 1];
842       if (*flags == '[' && flags[strlen(flags) - 1] == ']')
843         {
844           npieces--;
845           flags++;
846           flags[strlen(flags) - 1] = 0;
847           job |= str2jobflags(pool, flags);
848         }
849     }
850   if (npieces < 4)
851     {
852       pool_debug(pool, SOLV_ERROR, "selstr2job: no selection flags\n");
853       return -1;
854     }
855   selflags = str2selflags(pool, pieces[3]);
856   queue_init(&sel);
857   r = selection_make(pool, &sel, pieces[2], selflags);
858   for (i = 0; i < sel.count; i += 2)
859     queue_push2(jobqueue, job | sel.elements[i], sel.elements[i + 1]);
860   queue_free(&sel);
861   return r;
862 }
863
864 static void
865 writedeps(Repo *repo, FILE *fp, const char *tag, Id key, Solvable *s, Offset off)
866 {
867   Pool *pool = repo->pool;
868   Id id, *dp, *prvdp;
869   int tagwritten = 0;
870   const char *idstr;
871
872   if (!off)
873     return;
874   dp = repo->idarraydata + off;
875   prvdp = 0;
876   while ((id = *dp++) != 0)
877     {
878       if (key == SOLVABLE_REQUIRES && id == SOLVABLE_PREREQMARKER)
879         {
880           if (tagwritten)
881             fprintf(fp, "-%s\n", tag);
882           tagwritten = 0;
883           tag = "Prq:";
884           continue;
885         }
886       if (key == SOLVABLE_PROVIDES && id == SOLVABLE_FILEMARKER)
887         {
888           prvdp = dp;
889           continue;
890         }
891       idstr = pool_dep2str(pool, id);
892       if (ISRELDEP(id))
893         {
894           Reldep *rd = GETRELDEP(pool, id);
895           if (key == SOLVABLE_CONFLICTS && rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_OTHERPROVIDERS)
896             {
897               if (!strncmp(idstr, "namespace:", 10))
898                 idstr += 10;
899             }
900           if (key == SOLVABLE_SUPPLEMENTS)
901             {
902               if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_FILESYSTEM)
903                 {
904                   if (!strncmp(idstr, "namespace:", 10))
905                     idstr += 10;
906                 }
907               else if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_MODALIAS)
908                 {
909                   if (!strncmp(idstr, "namespace:", 10))
910                     idstr += 10;
911                 }
912               else if (rd->flags == REL_AND)
913                 {
914                   /* either packageand chain or modalias */
915                   idstr = 0;
916                   if (ISRELDEP(rd->evr))
917                     {
918                       Reldep *mrd = GETRELDEP(pool, rd->evr);
919                       if (mrd->flags == REL_NAMESPACE && mrd->name == NAMESPACE_MODALIAS)
920                         {
921                           idstr = pool_tmpjoin(pool, "modalias(", pool_dep2str(pool, rd->name), ":");
922                           idstr = pool_tmpappend(pool, idstr, pool_dep2str(pool, mrd->evr), ")");
923                         }
924                       else if (mrd->flags >= 8)
925                         continue;
926                     }
927                   if (!idstr)
928                     {
929                       /* must be and chain */
930                       idstr = pool_dep2str(pool, rd->evr);
931                       for (;;)
932                         {
933                           id = rd->name;
934                           if (!ISRELDEP(id))
935                             break;
936                           rd = GETRELDEP(pool, id);
937                           if (rd->flags != REL_AND)
938                             break;
939                           idstr = pool_tmpjoin(pool, pool_dep2str(pool, rd->evr), ":", idstr);
940                         }
941                       idstr = pool_tmpjoin(pool, pool_dep2str(pool, id), ":", idstr);
942                       idstr = pool_tmpjoin(pool, "packageand(", idstr, ")");
943                     }
944                 }
945               else if (rd->flags >= 8)
946                 continue;
947             }
948         }
949       if (!tagwritten)
950         {
951           fprintf(fp, "+%s\n", tag);
952           tagwritten = 1;
953         }
954       fprintf(fp, "%s\n", idstr);
955     }
956   if (key == SOLVABLE_PROVIDES)
957     {
958       /* add the filelist */
959       Dataiterator di;
960       dataiterator_init(&di, pool, repo, s - pool->solvables, SOLVABLE_FILELIST, 0, 0);
961       while (dataiterator_step(&di))
962         {
963           const char *s = repodata_dir2str(di.data, di.kv.id, di.kv.str);
964           if (prvdp)
965             {
966               Id id = pool_str2id(pool, s, 0);
967               if (id)
968                 {
969                   for (dp = prvdp; *dp; dp++)
970                     if (*dp == id)
971                       break;
972                   if (*dp)
973                     continue;   /* already included */
974                 }
975             }
976           if (!tagwritten)
977             {
978               fprintf(fp, "+%s", tag);
979               tagwritten = 1;
980             }
981           fprintf(fp, "%s\n", s);
982         }
983     }
984   if (tagwritten)
985     fprintf(fp, "-%s\n", tag);
986 }
987
988 int
989 testcase_write_testtags(Repo *repo, FILE *fp)
990 {
991   Pool *pool = repo->pool;
992   Solvable *s;
993   Id p;
994   const char *name;
995   const char *evr;
996   const char *arch;
997   const char *release;
998   const char *tmp;
999   unsigned int ti;
1000
1001   fprintf(fp, "=Ver: 2.0\n");
1002   FOR_REPO_SOLVABLES(repo, p, s)
1003     {
1004       name = pool_id2str(pool, s->name);
1005       evr = pool_id2str(pool, s->evr);
1006       arch = pool_id2str(pool, s->arch);
1007       release = strrchr(evr, '-');
1008       if (!release)
1009         release = evr + strlen(evr);
1010       fprintf(fp, "=Pkg: %s %.*s %s %s\n", name, (int)(release - evr), evr, *release && release[1] ? release + 1 : "-", arch);
1011       tmp = solvable_lookup_str(s, SOLVABLE_SUMMARY);
1012       if (tmp)
1013         fprintf(fp, "=Sum: %s\n", tmp);
1014       writedeps(repo, fp, "Req:", SOLVABLE_REQUIRES, s, s->requires);
1015       writedeps(repo, fp, "Prv:", SOLVABLE_PROVIDES, s, s->provides);
1016       writedeps(repo, fp, "Obs:", SOLVABLE_OBSOLETES, s, s->obsoletes);
1017       writedeps(repo, fp, "Con:", SOLVABLE_CONFLICTS, s, s->conflicts);
1018       writedeps(repo, fp, "Rec:", SOLVABLE_RECOMMENDS, s, s->recommends);
1019       writedeps(repo, fp, "Sup:", SOLVABLE_SUPPLEMENTS, s, s->supplements);
1020       writedeps(repo, fp, "Sug:", SOLVABLE_SUGGESTS, s, s->suggests);
1021       writedeps(repo, fp, "Enh:", SOLVABLE_ENHANCES, s, s->enhances);
1022       if (s->vendor)
1023         fprintf(fp, "=Vnd: %s\n", pool_id2str(pool, s->vendor));
1024       ti = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0);
1025       if (ti)
1026         fprintf(fp, "=Tim: %u\n", ti);
1027     }
1028   return 0;
1029 }
1030
1031 static inline Offset
1032 adddep(Repo *repo, Offset olddeps, char *str, Id marker)
1033 {
1034   Id id = *str == '/' ? pool_str2id(repo->pool, str, 1) : testcase_str2dep(repo->pool, str);
1035   return repo_addid_dep(repo, olddeps, id, marker);
1036 }
1037
1038 static void
1039 finish_solvable(Pool *pool, Repodata *data, Solvable *s, char *filelist, int nfilelist)
1040 {
1041   if (nfilelist)
1042     {
1043       int l;
1044       Id did;
1045       for (l = 0; l < nfilelist; l += strlen(filelist + l) + 1)
1046         {
1047           char *p = strrchr(filelist + l, '/');
1048           if (!p)
1049             continue;
1050           *p++ = 0;
1051           did = repodata_str2dir(data, filelist + l, 1);
1052           p[-1] = '/';
1053           if (!did)
1054             did = repodata_str2dir(data, "/", 1);
1055           repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, did, p);
1056         }
1057     }
1058   if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
1059     s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
1060   s->supplements = repo_fix_supplements(s->repo, s->provides, s->supplements, 0);
1061   s->conflicts = repo_fix_conflicts(s->repo, s->conflicts);
1062 }
1063
1064 /* stripped down version of susetags parser used for testcases */
1065 int
1066 testcase_add_testtags(Repo *repo, FILE *fp, int flags)
1067 {
1068   Pool *pool = repo->pool;
1069   char *line, *linep;
1070   int aline;
1071   int tag;
1072   Repodata *data;
1073   Solvable *s;
1074   char *sp[5];
1075   unsigned int t;
1076   int intag;
1077   char *filelist = 0;
1078   int afilelist = 0;
1079   int nfilelist = 0;
1080
1081   data = repo_add_repodata(repo, flags);
1082   s = 0;
1083   intag = 0;
1084
1085   aline = 1024;
1086   line = solv_malloc(aline);
1087   linep = line;
1088   for (;;)
1089     {
1090       if (linep - line + 16 > aline)
1091         {
1092           aline = linep - line;
1093           line = solv_realloc(line, aline + 512);
1094           linep = line + aline;
1095           aline += 512;
1096         }
1097       if (!fgets(linep, aline - (linep - line), fp))
1098         break;
1099       linep += strlen(linep);
1100       if (linep == line || linep[-1] != '\n')
1101         continue;
1102       linep[-1] = 0;
1103       linep = line + intag;
1104       if (intag)
1105         {
1106           if (line[intag] == '-' && !strncmp(line + 1, line + intag + 1, intag - 2))
1107             {
1108               intag = 0;
1109               linep = line;
1110               continue;
1111             }
1112         }
1113       else if (line[0] == '+' && line[1] && line[1] != ':')
1114         {
1115           char *tagend = strchr(line, ':');
1116           if (!tagend)
1117             continue;
1118           line[0] = '=';
1119           tagend[1] = ' ';
1120           intag = tagend + 2 - line;
1121           linep = line + intag;
1122           continue;
1123         }
1124       if (*line != '=' || !line[1] || !line[2] || !line[3] || line[4] != ':')
1125         continue;
1126       tag = line[1] << 16 | line[2] << 8 | line[3];
1127       switch(tag)
1128         {
1129         case 'P' << 16 | 'k' << 8 | 'g':
1130           if (s)
1131             finish_solvable(pool, data, s, filelist, nfilelist);
1132           nfilelist = 0;
1133           if (split(line + 5, sp, 5) != 4)
1134             break;
1135           s = pool_id2solvable(pool, repo_add_solvable(repo));
1136           s->name = pool_str2id(pool, sp[0], 1);
1137           /* join back version and release */
1138           if (sp[2] && !(sp[2][0] == '-' && !sp[2][1]))
1139             sp[2][-1] = '-';
1140           s->evr = makeevr(pool, sp[1]);
1141           s->arch = pool_str2id(pool, sp[3], 1);
1142           break;
1143         case 'S' << 16 | 'u' << 8 | 'm':
1144           repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line + 6);
1145           break;
1146         case 'V' << 16 | 'n' << 8 | 'd':
1147           s->vendor = pool_str2id(pool, line + 6, 1);
1148           break;
1149         case 'T' << 16 | 'i' << 8 | 'm':
1150           t = atoi(line + 6);
1151           if (t)
1152             repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, t);
1153           break;
1154         case 'R' << 16 | 'e' << 8 | 'q':
1155           s->requires = adddep(repo, s->requires, line + 6, -SOLVABLE_PREREQMARKER);
1156           break;
1157         case 'P' << 16 | 'r' << 8 | 'q':
1158           s->requires = adddep(repo, s->requires, line + 6, SOLVABLE_PREREQMARKER);
1159           break;
1160         case 'P' << 16 | 'r' << 8 | 'v':
1161           if (line[6] == '/')
1162             {
1163               int l = strlen(line + 6) + 1;
1164               if (nfilelist + l > afilelist)
1165                 {
1166                   afilelist = nfilelist + l + 512;
1167                   filelist = solv_realloc(filelist, afilelist);
1168                 }
1169               memcpy(filelist + nfilelist, line + 6, l);
1170               nfilelist += l;
1171               break;
1172             }
1173           if (nfilelist)
1174             {
1175               int l;
1176               for (l = 0; l < nfilelist; l += strlen(filelist + l) + 1)
1177                 s->provides = repo_addid_dep(repo, s->provides, pool_str2id(pool, filelist + l, 1), 0);
1178               nfilelist = 0;
1179             }
1180           s->provides = adddep(repo, s->provides, line + 6, 0);
1181           break;
1182         case 'O' << 16 | 'b' << 8 | 's':
1183           s->obsoletes = adddep(repo, s->obsoletes, line + 6, 0);
1184           break;
1185         case 'C' << 16 | 'o' << 8 | 'n':
1186           s->conflicts = adddep(repo, s->conflicts, line + 6, 0);
1187           break;
1188         case 'R' << 16 | 'e' << 8 | 'c':
1189           s->recommends = adddep(repo, s->recommends, line + 6, 0);
1190           break;
1191         case 'S' << 16 | 'u' << 8 | 'p':
1192           s->supplements = adddep(repo, s->supplements, line + 6, 0);
1193           break;
1194         case 'S' << 16 | 'u' << 8 | 'g':
1195           s->suggests = adddep(repo, s->suggests, line + 6, 0);
1196           break;
1197         case 'E' << 16 | 'n' << 8 | 'h':
1198           s->enhances = adddep(repo, s->enhances, line + 6, 0);
1199           break;
1200         default:
1201           break;
1202         }
1203     }
1204   if (s)
1205     finish_solvable(pool, data, s, filelist, nfilelist);
1206   solv_free(line);
1207   solv_free(filelist);
1208   repodata_free_dircache(data);
1209   if (!(flags & REPO_NO_INTERNALIZE))
1210     repodata_internalize(data);
1211   return 0;
1212 }
1213
1214 const char *
1215 testcase_getpoolflags(Pool *pool)
1216 {
1217   const char *str = 0;
1218   int i, v;
1219   for (i = 0; poolflags2str[i].str; i++)
1220     {
1221       v = pool_get_flag(pool, poolflags2str[i].flag);
1222       if (v == poolflags2str[i].def)
1223         continue;
1224       str = pool_tmpappend(pool, str, v ? " " : " !", poolflags2str[i].str);
1225     }
1226   return str ? str + 1 : "";
1227 }
1228
1229 int
1230 testcase_setpoolflags(Pool *pool, const char *str)
1231 {
1232   const char *p = str, *s;
1233   int i, v;
1234   for (;;)
1235     {
1236       while (*p == ' ' || *p == '\t' || *p == ',')
1237         p++;
1238       v = 1;
1239       if (*p == '!')
1240         {
1241           p++;
1242           v = 0;
1243         }
1244       if (!*p)
1245         break;
1246       s = p;
1247       while (*p && *p != ' ' && *p != '\t' && *p != ',')
1248         p++;
1249       for (i = 0; poolflags2str[i].str; i++)
1250         if (!strncmp(poolflags2str[i].str, s, p - s) && poolflags2str[i].str[p - s] == 0)
1251           break;
1252       if (!poolflags2str[i].str)
1253         {
1254           pool_debug(pool, SOLV_ERROR, "setpoolflags: unknown flag '%.*s'\n", (int)(p - s), s);
1255           return 0;
1256         }
1257       pool_set_flag(pool, poolflags2str[i].flag, v);
1258     }
1259   return 1;
1260 }
1261
1262 void
1263 testcase_resetpoolflags(Pool *pool)
1264 {
1265   int i;
1266   for (i = 0; poolflags2str[i].str; i++)
1267     pool_set_flag(pool, poolflags2str[i].flag, poolflags2str[i].def);
1268 }
1269
1270 const char *
1271 testcase_getsolverflags(Solver *solv)
1272 {
1273   Pool *pool = solv->pool;
1274   const char *str = 0;
1275   int i, v;
1276   for (i = 0; solverflags2str[i].str; i++)
1277     {
1278       v = solver_get_flag(solv, solverflags2str[i].flag);
1279       if (v == solverflags2str[i].def)
1280         continue;
1281       str = pool_tmpappend(pool, str, v ? " " : " !", solverflags2str[i].str);
1282     }
1283   return str ? str + 1 : "";
1284 }
1285
1286 int
1287 testcase_setsolverflags(Solver *solv, const char *str)
1288 {
1289   const char *p = str, *s;
1290   int i, v;
1291   for (;;)
1292     {
1293       while (*p == ' ' || *p == '\t' || *p == ',')
1294         p++;
1295       v = 1;
1296       if (*p == '!')
1297         {
1298           p++;
1299           v = 0;
1300         }
1301       if (!*p)
1302         break;
1303       s = p;
1304       while (*p && *p != ' ' && *p != '\t' && *p != ',')
1305         p++;
1306       for (i = 0; solverflags2str[i].str; i++)
1307         if (!strncmp(solverflags2str[i].str, s, p - s) && solverflags2str[i].str[p - s] == 0)
1308           break;
1309       if (!solverflags2str[i].str)
1310         {
1311           pool_debug(solv->pool, SOLV_ERROR, "setsolverflags: unknown flag '%.*s'\n", (int)(p - s), s);
1312           return 0;
1313         }
1314       solver_set_flag(solv, solverflags2str[i].flag, v);
1315     }
1316   return 1;
1317 }
1318
1319 void
1320 testcase_resetsolverflags(Solver *solv)
1321 {
1322   int i;
1323   for (i = 0; solverflags2str[i].str; i++)
1324     solver_set_flag(solv, solverflags2str[i].flag, solverflags2str[i].def);
1325 }
1326
1327 static const char *
1328 testcase_ruleid(Solver *solv, Id rid)
1329 {
1330   Strqueue sq;
1331   Queue q;
1332   int i;
1333   Chksum *chk;
1334   const unsigned char *md5;
1335   int md5l;
1336   const char *s;
1337
1338   queue_init(&q);
1339   strqueue_init(&sq);
1340   solver_ruleliterals(solv, rid, &q);
1341   for (i = 0; i < q.count; i++)
1342     {
1343       Id p = q.elements[i];
1344       s = testcase_solvid2str(solv->pool, p > 0 ? p : -p);
1345       if (p < 0)
1346         s = pool_tmpjoin(solv->pool, "!", s, 0);
1347       strqueue_push(&sq, s);
1348     }
1349   queue_free(&q);
1350   strqueue_sort_u(&sq);
1351   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1352   for (i = 0; i < sq.nstr; i++)
1353     solv_chksum_add(chk, sq.str[i], strlen(sq.str[i]) + 1);
1354   md5 = solv_chksum_get(chk, &md5l);
1355   s = pool_bin2hex(solv->pool, md5, md5l);
1356   chk = solv_chksum_free(chk, 0);
1357   strqueue_free(&sq);
1358   return s;
1359 }
1360
1361 static const char *
1362 testcase_problemid(Solver *solv, Id problem)
1363 {
1364   Strqueue sq;
1365   Queue q;
1366   Chksum *chk;
1367   const unsigned char *md5;
1368   int i, md5l;
1369   const char *s;
1370
1371   /* we build a hash of all rules that define the problem */
1372   queue_init(&q);
1373   strqueue_init(&sq);
1374   solver_findallproblemrules(solv, problem, &q);
1375   for (i = 0; i < q.count; i++)
1376     strqueue_push(&sq, testcase_ruleid(solv, q.elements[i]));
1377   queue_free(&q);
1378   strqueue_sort_u(&sq);
1379   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1380   for (i = 0; i < sq.nstr; i++)
1381     solv_chksum_add(chk, sq.str[i], strlen(sq.str[i]) + 1);
1382   md5 = solv_chksum_get(chk, &md5l);
1383   s = pool_bin2hex(solv->pool, md5, 4);
1384   chk = solv_chksum_free(chk, 0);
1385   strqueue_free(&sq);
1386   return s;
1387 }
1388
1389 static const char *
1390 testcase_solutionid(Solver *solv, Id problem, Id solution)
1391 {
1392   Id intid;
1393   Chksum *chk;
1394   const unsigned char *md5;
1395   int md5l;
1396   const char *s;
1397
1398   intid = solver_solutionelement_internalid(solv, problem, solution);
1399   /* internal stuff! handle with care! */
1400   if (intid < 0)
1401     {
1402       /* it's a job */
1403       s = testcase_job2str(solv->pool, solv->job.elements[-intid - 1], solv->job.elements[-intid]);
1404     }
1405   else
1406     {
1407       /* it's a rule */
1408       s = testcase_ruleid(solv, intid);
1409     }
1410   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1411   solv_chksum_add(chk, s, strlen(s) + 1);
1412   md5 = solv_chksum_get(chk, &md5l);
1413   s = pool_bin2hex(solv->pool, md5, 4);
1414   chk = solv_chksum_free(chk, 0);
1415   return s;
1416 }
1417
1418 static struct class2str {
1419   Id class;
1420   const char *str;
1421 } class2str[] = {
1422   { SOLVER_TRANSACTION_ERASE,          "erase" },
1423   { SOLVER_TRANSACTION_INSTALL,        "install" },
1424   { SOLVER_TRANSACTION_REINSTALLED,    "reinstall" },
1425   { SOLVER_TRANSACTION_DOWNGRADED,     "downgrade" },
1426   { SOLVER_TRANSACTION_CHANGED,        "change" },
1427   { SOLVER_TRANSACTION_UPGRADED,       "upgrade" },
1428   { SOLVER_TRANSACTION_OBSOLETED,      "obsolete" },
1429   { SOLVER_TRANSACTION_MULTIINSTALL,   "multiinstall" },
1430   { SOLVER_TRANSACTION_MULTIREINSTALL, "multireinstall" },
1431   { 0, 0 }
1432 };
1433
1434 char *
1435 testcase_solverresult(Solver *solv, int resultflags)
1436 {
1437   Pool *pool = solv->pool;
1438   int i, j;
1439   Id p, op;
1440   const char *s;
1441   char *result;
1442   Strqueue sq;
1443
1444   strqueue_init(&sq);
1445   if ((resultflags & TESTCASE_RESULT_TRANSACTION) != 0)
1446     {
1447       Transaction *trans = solver_create_transaction(solv);
1448       Queue q;
1449
1450       queue_init(&q);
1451       for (i = 0; class2str[i].str; i++)
1452         {
1453           queue_empty(&q);
1454           transaction_classify_pkgs(trans, SOLVER_TRANSACTION_KEEP_PSEUDO, class2str[i].class, 0, 0, &q);
1455           for (j = 0; j < q.count; j++)
1456             {
1457               p = q.elements[j];
1458               op = 0;
1459               if (pool->installed && pool->solvables[p].repo == pool->installed)
1460                 op = transaction_obs_pkg(trans, p);
1461               s = pool_tmpjoin(pool, class2str[i].str, " ", testcase_solvid2str(pool, p));
1462               if (op)
1463                 s = pool_tmpjoin(pool, s, " ", testcase_solvid2str(pool, op));
1464               strqueue_push(&sq, s);
1465             }
1466         }
1467       queue_free(&q);
1468       transaction_free(trans);
1469     }
1470   if ((resultflags & TESTCASE_RESULT_PROBLEMS) != 0)
1471     {
1472       char *probprefix, *solprefix;
1473       int problem, solution, element;
1474       int pcnt, scnt;
1475
1476       pcnt = solver_problem_count(solv);
1477       for (problem = 1; problem <= pcnt; problem++)
1478         {
1479           Id rid, from, to, dep;
1480           SolverRuleinfo rinfo;
1481           rid = solver_findproblemrule(solv, problem);
1482           s = testcase_problemid(solv, problem);
1483           probprefix = solv_dupjoin("problem ", s, 0);
1484           rinfo = solver_ruleinfo(solv, rid, &from, &to, &dep);
1485           s = pool_tmpjoin(pool, probprefix, " info ", solver_problemruleinfo2str(solv, rinfo, from, to, dep));
1486           strqueue_push(&sq, s);
1487           scnt = solver_solution_count(solv, problem);
1488           for (solution = 1; solution <= scnt; solution++)
1489             {
1490               s = testcase_solutionid(solv, problem, solution);
1491               solprefix = solv_dupjoin(probprefix, " solution ", s);
1492               element = 0;
1493               while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &op)) != 0)
1494                 {
1495                   if (p == SOLVER_SOLUTION_JOB)
1496                     s = pool_tmpjoin(pool, solprefix, " deljob ", testcase_job2str(pool, solv->job.elements[op - 1], solv->job.elements[op]));
1497                   else if (p > 0 && op == 0)
1498                     s = pool_tmpjoin(pool, solprefix, " erase ", testcase_solvid2str(pool, p));
1499                   else if (p > 0 && op > 0)
1500                     {
1501                       s = pool_tmpjoin(pool, solprefix, " replace ", testcase_solvid2str(pool, p));
1502                       s = pool_tmpappend(pool, s, " ", testcase_solvid2str(pool, op));
1503                     }
1504                   else if (p < 0 && op > 0)
1505                     s = pool_tmpjoin(pool, solprefix, " allow ", testcase_solvid2str(pool, op));
1506                   else
1507                     s = pool_tmpjoin(pool, solprefix, " unknown", 0);
1508                   strqueue_push(&sq, s);
1509                 }
1510               solv_free(solprefix);
1511             }
1512           solv_free(probprefix);
1513         }
1514     }
1515
1516   if ((resultflags & TESTCASE_RESULT_ORPHANED) != 0)
1517     {
1518       Queue q;
1519
1520       queue_init(&q);
1521       solver_get_orphaned(solv, &q);
1522       for (i = 0; i < q.count; i++)
1523         {
1524           s = pool_tmpjoin(pool, "orphaned ", testcase_solvid2str(pool, q.elements[i]), 0);
1525           strqueue_push(&sq, s);
1526         }
1527       queue_free(&q);
1528     }
1529
1530   if ((resultflags & TESTCASE_RESULT_RECOMMENDED) != 0)
1531     {
1532       Queue qr, qs;
1533
1534       queue_init(&qr);
1535       queue_init(&qs);
1536       solver_get_recommendations(solv, &qr, &qs, 0);
1537       for (i = 0; i < qr.count; i++)
1538         {
1539           s = pool_tmpjoin(pool, "recommended ", testcase_solvid2str(pool, qr.elements[i]), 0);
1540           strqueue_push(&sq, s);
1541         }
1542       for (i = 0; i < qs.count; i++)
1543         {
1544           s = pool_tmpjoin(pool, "suggested ", testcase_solvid2str(pool, qs.elements[i]), 0);
1545           strqueue_push(&sq, s);
1546         }
1547       queue_free(&qr);
1548       queue_free(&qs);
1549     }
1550
1551   if ((resultflags & TESTCASE_RESULT_UNNEEDED) != 0)
1552     {
1553       Queue q;
1554
1555       queue_init(&q);
1556       solver_get_unneeded(solv, &q, 0);
1557       for (i = 0; i < q.count; i++)
1558         {
1559           s = pool_tmpjoin(pool, "unneeded ", testcase_solvid2str(pool, q.elements[i]), 0);
1560           strqueue_push(&sq, s);
1561         }
1562       queue_free(&q);
1563     }
1564
1565   strqueue_sort(&sq);
1566   result = strqueue_join(&sq);
1567   strqueue_free(&sq);
1568   return result;
1569 }
1570
1571
1572 int
1573 testcase_write(Solver *solv, char *dir, int resultflags, const char *testcasename, const char *resultname)
1574 {
1575   Pool *pool = solv->pool;
1576   Repo *repo;
1577   int i;
1578   Id arch, repoid;
1579   Id lowscore;
1580   FILE *fp;
1581   Strqueue sq;
1582   char *cmd, *out;
1583   const char *s;
1584
1585   if (!testcasename)
1586     testcasename = "testcase.t";
1587   if (!resultname)
1588     resultname = "solver.result";
1589
1590   if (mkdir(dir, 0777) && errno != EEXIST)
1591     {
1592       pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not create directory '%s'\n", dir);
1593       return 0;
1594     }
1595   strqueue_init(&sq);
1596   FOR_REPOS(repoid, repo)
1597     {
1598       const char *name = testcase_repoid2str(pool, repoid);
1599       char priobuf[50];
1600       if (repo->subpriority)
1601         sprintf(priobuf, "%d.%d", repo->priority, repo->subpriority);
1602       else
1603         sprintf(priobuf, "%d", repo->priority);
1604       out = pool_tmpjoin(pool, name, ".repo", ".gz");
1605       cmd = pool_tmpjoin(pool, "repo ", name, " ");
1606       cmd = pool_tmpappend(pool, cmd, priobuf, " ");
1607       cmd = pool_tmpappend(pool, cmd, "testtags ", out);
1608       strqueue_push(&sq, cmd);
1609       out = pool_tmpjoin(pool, dir, "/", out);
1610       if (!(fp = solv_xfopen(out, "w")))
1611         {
1612           pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
1613           strqueue_free(&sq);
1614           return 0;
1615         }
1616       testcase_write_testtags(repo, fp);
1617       if (fclose(fp))
1618         {
1619           pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
1620           strqueue_free(&sq);
1621           return 0;
1622         }
1623     }
1624   /* hmm, this is not optimal... we currently search for the lowest score */
1625   lowscore = 0;
1626   arch = pool->solvables[SYSTEMSOLVABLE].arch;
1627   for (i = 0; i < pool->lastarch; i++)
1628     {
1629       if (pool->id2arch[i] == 1 && !lowscore)
1630         arch = i;
1631       if (pool->id2arch[i] > 0x10000 && (!lowscore || pool->id2arch[i] < lowscore))
1632         {
1633           arch = i;
1634           lowscore = pool->id2arch[i];
1635         }
1636     }
1637   cmd = pool_tmpjoin(pool, "system ", pool->lastarch ? pool_id2str(pool, arch) : "unset", 0);
1638   for (i = 0; disttype2str[i].str != 0; i++)
1639     if (pool->disttype == disttype2str[i].type)
1640       break;
1641   pool_tmpappend(pool, cmd, " ", disttype2str[i].str ? disttype2str[i].str : "unknown");
1642   if (pool->installed)
1643     cmd = pool_tmpappend(pool, cmd, " ", testcase_repoid2str(pool, pool->installed->repoid));
1644   strqueue_push(&sq, cmd);
1645   s = testcase_getpoolflags(solv->pool);
1646   if (*s)
1647     {
1648       cmd = pool_tmpjoin(pool, "poolflags ", s, 0);
1649       strqueue_push(&sq, cmd);
1650     }
1651
1652   if (pool->vendorclasses)
1653     {
1654       cmd = 0;
1655       for (i = 0; pool->vendorclasses[i]; i++)
1656         {
1657           cmd = pool_tmpappend(pool, cmd ? cmd : "vendorclass", " ", pool->vendorclasses[i]);
1658           if (!pool->vendorclasses[i + 1])
1659             {
1660               strqueue_push(&sq, cmd);
1661               cmd = 0;
1662               i++;
1663             }
1664         }
1665     }
1666
1667   s = testcase_getsolverflags(solv);
1668   if (*s)
1669     {
1670       cmd = pool_tmpjoin(pool, "solverflags ", s, 0);
1671       strqueue_push(&sq, cmd);
1672     }
1673
1674   /* now dump all the ns callback values we know */
1675   if (pool->nscallback)
1676     {
1677       Id rid;
1678       int d;
1679       for (rid = 1; rid < pool->nrels; rid++)
1680         {
1681           Reldep *rd = pool->rels + rid;
1682           if (rd->flags != REL_NAMESPACE || rd->name == NAMESPACE_OTHERPROVIDERS)
1683             continue;
1684           /* evaluate all namespace ids, skip empty results */
1685           d = pool_whatprovides(pool, MAKERELDEP(rid));
1686           if (!d || !pool->whatprovidesdata[d])
1687             continue;
1688           cmd = pool_tmpjoin(pool, "namespace ", pool_id2str(pool, rd->name), "(");
1689           cmd = pool_tmpappend(pool, cmd, pool_id2str(pool, rd->evr), ")");
1690           for (;  pool->whatprovidesdata[d]; d++)
1691             cmd = pool_tmpappend(pool, cmd, " ", testcase_solvid2str(pool, pool->whatprovidesdata[d]));
1692           strqueue_push(&sq, cmd);
1693         }
1694     }
1695
1696   for (i = 0; i < solv->job.count; i += 2)
1697     {
1698       cmd = (char *)testcase_job2str(pool, solv->job.elements[i], solv->job.elements[i + 1]);
1699       cmd = pool_tmpjoin(pool, "job ", cmd, 0);
1700       strqueue_push(&sq, cmd);
1701     }
1702
1703   if (resultflags)
1704     {
1705       char *result;
1706       cmd = 0;
1707       for (i = 0; resultflags2str[i].str; i++)
1708         if ((resultflags & resultflags2str[i].flag) != 0)
1709           cmd = pool_tmpappend(pool, cmd, cmd ? "," : 0, resultflags2str[i].str);
1710       cmd = pool_tmpjoin(pool, "result ", cmd ? cmd : "?", 0);
1711       cmd = pool_tmpappend(pool, cmd, " ", resultname);
1712       strqueue_push(&sq, cmd);
1713       result = testcase_solverresult(solv, resultflags);
1714       if (!strcmp(resultname, "<inline>"))
1715         {
1716           int i;
1717           Strqueue rsq;
1718           strqueue_init(&rsq);
1719           strqueue_split(&rsq, result);
1720           for (i = 0; i < rsq.nstr; i++)
1721             {
1722               cmd = pool_tmpjoin(pool, "#>", rsq.str[i], 0);
1723               strqueue_push(&sq, cmd);
1724             }
1725           strqueue_free(&rsq);
1726         }
1727       else
1728         {
1729           out = pool_tmpjoin(pool, dir, "/", resultname);
1730           if (!(fp = fopen(out, "w")))
1731             {
1732               pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
1733               solv_free(result);
1734               strqueue_free(&sq);
1735               return 0;
1736             }
1737           if (result && *result && fwrite(result, strlen(result), 1, fp) != 1)
1738             {
1739               pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
1740               solv_free(result);
1741               strqueue_free(&sq);
1742               fclose(fp);
1743               return 0;
1744             }
1745           if (fclose(fp))
1746             {
1747               pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
1748               strqueue_free(&sq);
1749               return 0;
1750             }
1751         }
1752       solv_free(result);
1753     }
1754
1755   cmd = strqueue_join(&sq);
1756   out = pool_tmpjoin(pool, dir, "/", testcasename);
1757   if (!(fp = fopen(out, "w")))
1758     {
1759       pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
1760       strqueue_free(&sq);
1761       return 0;
1762     }
1763   if (*cmd && fwrite(cmd, strlen(cmd), 1, fp) != 1)
1764     {
1765       pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
1766       strqueue_free(&sq);
1767       fclose(fp);
1768       return 0;
1769     }
1770   if (fclose(fp))
1771     {
1772       pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
1773       strqueue_free(&sq);
1774       return 0;
1775     }
1776   solv_free(cmd);
1777   strqueue_free(&sq);
1778   return 1;
1779 }
1780
1781 static char *
1782 read_inline_file(FILE *fp, char **bufp, char **bufpp, int *buflp)
1783 {
1784   char *result = solv_malloc(1024);
1785   char *rp = result;
1786   int resultl = 1024;
1787
1788   for (;;)
1789     {
1790       size_t rl;
1791       if (rp - result + 256 >= resultl)
1792         {
1793           resultl = rp - result;
1794           result = solv_realloc(result, resultl + 1024);
1795           rp = result + resultl;
1796           resultl += 1024;
1797         }
1798       if (!fgets(rp, resultl - (rp - result), fp))
1799         *rp = 0;
1800       rl = strlen(rp);
1801       if (rl && (rp == result || rp[-1] == '\n'))
1802         {
1803           if (rl > 1 && rp[0] == '#' && rp[1] == '>')
1804             {
1805               memmove(rp, rp + 2, rl - 2);
1806               rl -= 2;
1807             }
1808           else
1809             {
1810               while (rl + 16 > *buflp)
1811                 {
1812                   *bufp = solv_realloc(*bufp, *buflp + 512);
1813                   *buflp += 512;
1814                 }
1815               memmove(*bufp, rp, rl);
1816               if ((*bufp)[rl - 1] == '\n')
1817                 {
1818                   ungetc('\n', fp);
1819                   rl--;
1820                 }
1821               (*bufp)[rl] = 0;
1822               (*bufpp) = *bufp + rl;
1823               rl = 0;
1824             }
1825         }
1826       if (rl <= 0)
1827         {
1828           *rp = 0;
1829           break;
1830         }
1831       rp += rl;
1832     }
1833   return result;
1834 }
1835
1836 static char *
1837 read_file(FILE *fp)
1838 {
1839   char *result = solv_malloc(1024);
1840   char *rp = result;
1841   int resultl = 1024;
1842
1843   for (;;)
1844     {
1845       size_t rl;
1846       if (rp - result + 256 >= resultl)
1847         {
1848           resultl = rp - result;
1849           result = solv_realloc(result, resultl + 1024);
1850           rp = result + resultl;
1851           resultl += 1024;
1852         }
1853       rl = fread(rp, 1, resultl - (rp - result), fp);
1854       if (rl <= 0)
1855         {
1856           *rp = 0;
1857           break;
1858         }
1859       rp += rl;
1860     }
1861   return result;
1862 }
1863
1864 static int
1865 str2resultflags(Pool *pool, char *s)    /* modifies the string! */
1866 {
1867   int i, resultflags = 0;
1868   while (s)
1869     {
1870       char *se = strchr(s, ',');
1871       if (se)
1872         *se++ = 0;
1873       for (i = 0; resultflags2str[i].str; i++)
1874         if (!strcmp(s, resultflags2str[i].str))
1875           {
1876             resultflags |= resultflags2str[i].flag;
1877             break;
1878           }
1879       if (!resultflags2str[i].str)
1880         pool_debug(pool, SOLV_ERROR, "result: unknown flag '%s'\n", s);
1881       s = se;
1882     }
1883   return resultflags;
1884 }
1885
1886 Solver *
1887 testcase_read(Pool *pool, FILE *fp, char *testcase, Queue *job, char **resultp, int *resultflagsp)
1888 {
1889   Solver *solv;
1890   char *buf, *bufp;
1891   int bufl;
1892   char *testcasedir, *s;
1893   int l;
1894   char **pieces = 0;
1895   int npieces = 0;
1896   int prepared = 0;
1897   int closefp = !fp;
1898   int poolflagsreset = 0;
1899
1900   if (!fp && !(fp = fopen(testcase, "r")))
1901     {
1902       pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", testcase);
1903       return 0;
1904     }
1905   testcasedir = solv_strdup(testcase);
1906   if ((s = strrchr(testcasedir, '/')) != 0)
1907     s[1] = 0;
1908   else
1909     *testcasedir = 0;
1910   bufl = 1024;
1911   buf = solv_malloc(bufl);
1912   bufp = buf;
1913   solv = 0;
1914   for (;;)
1915     {
1916       if (bufp - buf + 16 > bufl)
1917         {
1918           bufl = bufp - buf;
1919           buf = solv_realloc(buf, bufl + 512);
1920           bufp = buf + bufl;
1921           bufl += 512;
1922         }
1923       if (!fgets(bufp, bufl - (bufp - buf), fp))
1924         break;
1925       bufp = buf;
1926       l = strlen(buf);
1927       if (!l || buf[l - 1] != '\n')
1928         {
1929           bufp += l;
1930           continue;
1931         }
1932       buf[--l] = 0;
1933       s = buf;
1934       while (*s && (*s == ' ' || *s == '\t'))
1935         s++;
1936       if (!*s || *s == '#')
1937         continue;
1938       npieces = 0;
1939       /* split it in pieces */
1940       for (;;)
1941         {
1942           while (*s == ' ' || *s == '\t')
1943             s++;
1944           if (!*s)
1945             break;
1946           pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
1947           pieces[npieces++] = s;
1948           while (*s && *s != ' ' && *s != '\t')
1949             s++;
1950           if (*s)
1951             *s++ = 0;
1952         }
1953       pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
1954       pieces[npieces] = 0;
1955       if (!strcmp(pieces[0], "repo") && npieces >= 4)
1956         {
1957           Repo *repo = repo_create(pool, pieces[1]);
1958           FILE *rfp;
1959           int prio, subprio;
1960           const char *rdata;
1961
1962           prepared = 0;
1963           if (!poolflagsreset)
1964             {
1965               poolflagsreset = 1;
1966               testcase_resetpoolflags(pool);    /* hmm */
1967             }
1968           if (sscanf(pieces[2], "%d.%d", &prio, &subprio) != 2)
1969             {
1970               subprio = 0;
1971               prio = atoi(pieces[2]);
1972             }
1973           repo->priority = prio;
1974           repo->subpriority = subprio;
1975           if (strcmp(pieces[3], "empty") != 0)
1976             {
1977               const char *repotype = pool_tmpjoin(pool, pieces[3], 0, 0);       /* gets overwritten in <inline> case */
1978               if (!strcmp(pieces[4], "<inline>"))
1979                 {
1980                   char *idata = read_inline_file(fp, &buf, &bufp, &bufl);
1981                   rdata = "<inline>";
1982                   rfp = solv_xfopen_buf(rdata, &idata, 0, "rf");
1983                 }
1984               else
1985                 {
1986                   rdata = pool_tmpjoin(pool, testcasedir, pieces[4], 0);
1987                   rfp = solv_xfopen(rdata, "r");
1988                 }
1989               if (!rfp)
1990                 {
1991                   pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", rdata);
1992                 }
1993               else if (!strcmp(repotype, "testtags"))
1994                 {
1995                   testcase_add_testtags(repo, rfp, 0);
1996                   fclose(rfp);
1997                 }
1998               else if (!strcmp(repotype, "solv"))
1999                 {
2000                   repo_add_solv(repo, rfp, 0);
2001                   fclose(rfp);
2002                 }
2003 #if 0
2004               else if (!strcmp(repotype, "helix"))
2005                 {
2006                   extern int repo_add_helix(Repo *repo, FILE *fp, int flags);
2007                   repo_add_helix(repo, rfp, 0);
2008                   fclose(rfp);
2009                 }
2010 #endif
2011               else
2012                 {
2013                   fclose(rfp);
2014                   pool_debug(pool, SOLV_ERROR, "testcase_read: unknown repo type for repo '%s'\n", repo->name);
2015                 }
2016             }
2017         }
2018       else if (!strcmp(pieces[0], "system") && npieces >= 3)
2019         {
2020           int i;
2021           prepared = 0;
2022           /* must set the disttype before the arch */
2023           for (i = 0; disttype2str[i].str != 0; i++)
2024             if (!strcmp(disttype2str[i].str, pieces[2]))
2025               break;
2026           if (!disttype2str[i].str)
2027             pool_debug(pool, SOLV_ERROR, "testcase_read: system: unknown disttype '%s'\n", pieces[2]);
2028           else if (pool->disttype != disttype2str[i].type)
2029             {
2030 #ifdef MULTI_SEMANTICS
2031               pool_setdisttype(pool, disttype2str[i].type);
2032 #else
2033               pool_debug(pool, SOLV_ERROR, "testcase_read: system: cannot change disttype to '%s'\n", pieces[2]);
2034 #endif
2035             }
2036           if (strcmp(pieces[1], "unset") == 0)
2037             pool_setarch(pool, 0);
2038           else if (pieces[1][0] == ':')
2039             pool_setarchpolicy(pool, pieces[1] + 1);
2040           else
2041             pool_setarch(pool, pieces[1]);
2042           if (npieces > 3)
2043             {
2044               Repo *repo = testcase_str2repo(pool, pieces[3]);
2045               if (!repo)
2046                 pool_debug(pool, SOLV_ERROR, "testcase_read: system: unknown repo '%s'\n", pieces[3]);
2047               else
2048                 pool_set_installed(pool, repo);
2049             }
2050         }
2051       else if (!strcmp(pieces[0], "job") && npieces > 1)
2052         {
2053           char *sp;
2054           Id how, what;
2055           if (!prepared)
2056             {
2057               pool_addfileprovides(pool);
2058               pool_createwhatprovides(pool);
2059               prepared = 1;
2060             }
2061           if (npieces >= 3 && !strcmp(pieces[2], "selection"))
2062             {
2063               addselectionjob(pool, pieces + 1, npieces - 1, job);
2064               continue;
2065             }
2066           /* rejoin */
2067           for (sp = pieces[1]; sp < pieces[npieces - 1]; sp++)
2068             if (*sp == 0)
2069               *sp = ' ';
2070           how = testcase_str2job(pool, pieces[1], &what);
2071           if (how >= 0 && job)
2072             queue_push2(job, how, what);
2073         }
2074       else if (!strcmp(pieces[0], "vendorclass") && npieces > 1)
2075         {
2076           pool_addvendorclass(pool, (const char **)(pieces + 1));
2077         }
2078       else if (!strcmp(pieces[0], "namespace") && npieces > 1)
2079         {
2080           int i = strlen(pieces[1]);
2081           s = strchr(pieces[1], '(');
2082           if (!s && pieces[1][i - 1] != ')')
2083             {
2084               pool_debug(pool, SOLV_ERROR, "testcase_read: bad namespace '%s'\n", pieces[1]);
2085             }
2086           else
2087             {
2088               Id name, evr, id;
2089               Queue q;
2090               queue_init(&q);
2091               *s = 0;
2092               pieces[1][i - 1] = 0;
2093               name = pool_str2id(pool, pieces[1], 1);
2094               evr = pool_str2id(pool, s + 1, 1);
2095               *s = '(';
2096               pieces[1][i - 1] = ')';
2097               id = pool_rel2id(pool, name, evr, REL_NAMESPACE, 1);
2098               for (i = 2; i < npieces; i++)
2099                 queue_push(&q, testcase_str2solvid(pool, pieces[i]));
2100               /* now do the callback */
2101               if (!prepared)
2102                 {
2103                   pool_addfileprovides(pool);
2104                   pool_createwhatprovides(pool);
2105                   prepared = 1;
2106                 }
2107               pool->whatprovides_rel[GETRELID(id)] = pool_queuetowhatprovides(pool, &q);
2108               queue_free(&q);
2109             }
2110         }
2111       else if (!strcmp(pieces[0], "poolflags"))
2112         {
2113           int i;
2114           if (!poolflagsreset)
2115             {
2116               poolflagsreset = 1;
2117               testcase_resetpoolflags(pool);    /* hmm */
2118             }
2119           for (i = 1; i < npieces; i++)
2120             testcase_setpoolflags(pool, pieces[i]);
2121         }
2122       else if (!strcmp(pieces[0], "solverflags") && npieces > 1)
2123         {
2124           int i;
2125           if (!solv)
2126             {
2127               solv = solver_create(pool);
2128               testcase_resetsolverflags(solv);
2129             }
2130           for (i = 1; i < npieces; i++)
2131             testcase_setsolverflags(solv, pieces[i]);
2132         }
2133       else if (!strcmp(pieces[0], "result") && npieces > 2)
2134         {
2135           char *result = 0;
2136           int resultflags = str2resultflags(pool, pieces[1]);
2137           const char *rdata = pool_tmpjoin(pool, testcasedir, pieces[2], 0);
2138           if (!strcmp(pieces[2], "<inline>"))
2139             result = read_inline_file(fp, &buf, &bufp, &bufl);
2140           else
2141             {
2142               FILE *rfp = fopen(rdata, "r");
2143               if (!rfp)
2144                 pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", rdata);
2145               else
2146                 {
2147                   result = read_file(rfp);
2148                   fclose(rfp);
2149                 }
2150             }
2151           if (resultp)
2152             *resultp = result;
2153           else
2154             solv_free(result);
2155           if (resultflagsp)
2156             *resultflagsp = resultflags;
2157         }
2158       else if (!strcmp(pieces[0], "nextjob") && npieces == 1)
2159         {
2160           break;
2161         }
2162       else
2163         {
2164           pool_debug(pool, SOLV_ERROR, "testcase_read: cannot parse command '%s'\n", pieces[0]);
2165         }
2166     }
2167   buf = solv_free(buf);
2168   pieces = solv_free(pieces);
2169   solv_free(testcasedir);
2170   if (!prepared)
2171     {
2172       pool_addfileprovides(pool);
2173       pool_createwhatprovides(pool);
2174     }
2175   if (!solv)
2176     {
2177       solv = solver_create(pool);
2178       testcase_resetsolverflags(solv);
2179     }
2180   if (closefp)
2181     fclose(fp);
2182   return solv;
2183 }
2184
2185 char *
2186 testcase_resultdiff(char *result1, char *result2)
2187 {
2188   Strqueue sq1, sq2, osq;
2189   char *r;
2190   strqueue_init(&sq1);
2191   strqueue_init(&sq2);
2192   strqueue_init(&osq);
2193   strqueue_split(&sq1, result1);
2194   strqueue_split(&sq2, result2);
2195   strqueue_sort(&sq1);
2196   strqueue_sort(&sq2);
2197   strqueue_diff(&sq1, &sq2, &osq);
2198   r = osq.nstr ? strqueue_join(&osq) : 0;
2199   strqueue_free(&sq1);
2200   strqueue_free(&sq2);
2201   strqueue_free(&osq);
2202   return r;
2203 }
2204