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