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