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