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