Imported Upstream version 0.7.2
[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 <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <errno.h>
14
15 #include "pool.h"
16 #include "poolarch.h"
17 #include "poolvendor.h"
18 #include "evr.h"
19 #include "repo.h"
20 #include "repo_solv.h"
21 #include "solver.h"
22 #include "solverdebug.h"
23 #include "chksum.h"
24 #include "testcase.h"
25 #include "selection.h"
26 #include "solv_xfopen.h"
27 #if ENABLE_TESTCASE_HELIXREPO
28 #include "ext/repo_helix.h"
29 #endif
30
31 #define DISABLE_JOIN2
32 #include "tools_util.h"
33
34 static struct job2str {
35   Id job;
36   const char *str;
37 } job2str[] = {
38   { SOLVER_NOOP,           "noop" },
39   { SOLVER_INSTALL,        "install" },
40   { SOLVER_ERASE,          "erase" },
41   { SOLVER_UPDATE,         "update" },
42   { SOLVER_WEAKENDEPS,     "weakendeps" },
43   { SOLVER_MULTIVERSION,   "multiversion" },
44   { SOLVER_MULTIVERSION,   "noobsoletes" },     /* old name */
45   { SOLVER_LOCK,           "lock" },
46   { SOLVER_DISTUPGRADE,    "distupgrade" },
47   { SOLVER_VERIFY,         "verify" },
48   { SOLVER_DROP_ORPHANED,  "droporphaned" },
49   { SOLVER_USERINSTALLED,  "userinstalled" },
50   { SOLVER_ALLOWUNINSTALL, "allowuninstall" },
51   { SOLVER_FAVOR,          "favor" },
52   { SOLVER_DISFAVOR,       "disfavor" },
53   { 0, 0 }
54 };
55
56 static struct jobflags2str {
57   Id flag;
58   const char *str;
59 } jobflags2str[] = {
60   { SOLVER_WEAK,      "weak" },
61   { SOLVER_ESSENTIAL, "essential" },
62   { SOLVER_CLEANDEPS, "cleandeps" },
63   { SOLVER_ORUPDATE,  "orupdate" },
64   { SOLVER_FORCEBEST, "forcebest" },
65   { SOLVER_TARGETED,  "targeted" },
66   { SOLVER_NOTBYUSER, "notbyuser" },
67   { SOLVER_SETEV,     "setev" },
68   { SOLVER_SETEVR,    "setevr" },
69   { SOLVER_SETARCH,   "setarch" },
70   { SOLVER_SETVENDOR, "setvendor" },
71   { SOLVER_SETREPO,   "setrepo" },
72   { SOLVER_NOAUTOSET, "noautoset" },
73   { 0, 0 }
74 };
75
76 static struct resultflags2str {
77   Id flag;
78   const char *str;
79 } resultflags2str[] = {
80   { TESTCASE_RESULT_TRANSACTION,        "transaction" },
81   { TESTCASE_RESULT_PROBLEMS,           "problems" },
82   { TESTCASE_RESULT_ORPHANED,           "orphaned" },
83   { TESTCASE_RESULT_RECOMMENDED,        "recommended" },
84   { TESTCASE_RESULT_UNNEEDED,           "unneeded" },
85   { TESTCASE_RESULT_ALTERNATIVES,       "alternatives" },
86   { TESTCASE_RESULT_RULES,              "rules" },
87   { TESTCASE_RESULT_GENID,              "genid" },
88   { TESTCASE_RESULT_REASON,             "reason" },
89   { TESTCASE_RESULT_CLEANDEPS,          "cleandeps" },
90   { TESTCASE_RESULT_JOBS,               "jobs" },
91   { 0, 0 }
92 };
93
94 static struct solverflags2str {
95   Id flag;
96   const char *str;
97   int def;
98 } solverflags2str[] = {
99   { SOLVER_FLAG_ALLOW_DOWNGRADE,            "allowdowngrade", 0 },
100   { SOLVER_FLAG_ALLOW_NAMECHANGE,           "allownamechange", 1 },
101   { SOLVER_FLAG_ALLOW_ARCHCHANGE,           "allowarchchange", 0 },
102   { SOLVER_FLAG_ALLOW_VENDORCHANGE,         "allowvendorchange", 0 },
103   { SOLVER_FLAG_ALLOW_UNINSTALL,            "allowuninstall", 0 },
104   { SOLVER_FLAG_NO_UPDATEPROVIDE,           "noupdateprovide", 0 },
105   { SOLVER_FLAG_SPLITPROVIDES,              "splitprovides", 0 },
106   { SOLVER_FLAG_IGNORE_RECOMMENDED,         "ignorerecommended", 0 },
107   { SOLVER_FLAG_ADD_ALREADY_RECOMMENDED,    "addalreadyrecommended", 0 },
108   { SOLVER_FLAG_NO_INFARCHCHECK,            "noinfarchcheck", 0 },
109   { SOLVER_FLAG_KEEP_EXPLICIT_OBSOLETES,    "keepexplicitobsoletes", 0 },
110   { SOLVER_FLAG_BEST_OBEY_POLICY,           "bestobeypolicy", 0 },
111   { SOLVER_FLAG_NO_AUTOTARGET,              "noautotarget", 0 },
112   { SOLVER_FLAG_DUP_ALLOW_DOWNGRADE,        "dupallowdowngrade", 1 },
113   { SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE,       "dupallowarchchange", 1 },
114   { SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE,     "dupallowvendorchange", 1 },
115   { SOLVER_FLAG_DUP_ALLOW_NAMECHANGE,       "dupallownamechange", 1 },
116   { SOLVER_FLAG_KEEP_ORPHANS,               "keeporphans", 0 },
117   { SOLVER_FLAG_BREAK_ORPHANS,              "breakorphans", 0 },
118   { SOLVER_FLAG_FOCUS_INSTALLED,            "focusinstalled", 0 },
119   { SOLVER_FLAG_YUM_OBSOLETES,              "yumobsoletes", 0 },
120   { SOLVER_FLAG_NEED_UPDATEPROVIDE,         "needupdateprovide", 0 },
121   { SOLVER_FLAG_URPM_REORDER,               "urpmreorder", 0 },
122   { SOLVER_FLAG_FOCUS_BEST,                 "focusbest", 0 },
123   { SOLVER_FLAG_STRONG_RECOMMENDS,          "strongrecommends", 0 },
124   { SOLVER_FLAG_INSTALL_ALSO_UPDATES,       "installalsoupdates", 0 },
125   { SOLVER_FLAG_ONLY_NAMESPACE_RECOMMENDED, "onlynamespacerecommended", 0 },
126   { 0, 0, 0 }
127 };
128
129 static struct poolflags2str {
130   Id flag;
131   const char *str;
132   int def;
133 } poolflags2str[] = {
134   { POOL_FLAG_PROMOTEEPOCH,                 "promoteepoch", 0 },
135   { POOL_FLAG_FORBIDSELFCONFLICTS,          "forbidselfconflicts", 0 },
136   { POOL_FLAG_OBSOLETEUSESPROVIDES,         "obsoleteusesprovides", 0 },
137   { POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES, "implicitobsoleteusesprovides", 0 },
138   { POOL_FLAG_OBSOLETEUSESCOLORS,           "obsoleteusescolors", 0 },
139   { POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS,   "implicitobsoleteusescolors", 0 },
140   { POOL_FLAG_NOINSTALLEDOBSOLETES,         "noinstalledobsoletes", 0 },
141   { POOL_FLAG_HAVEDISTEPOCH,                "havedistepoch", 0 },
142   { POOL_FLAG_NOOBSOLETESMULTIVERSION,      "noobsoletesmultiversion", 0 },
143   { POOL_FLAG_ADDFILEPROVIDESFILTERED,      "addfileprovidesfiltered", 0 },
144   { POOL_FLAG_NOWHATPROVIDESAUX,            "nowhatprovidesaux", 0 },
145   { 0, 0, 0 }
146 };
147
148 static struct disttype2str {
149   Id type;
150   const char *str;
151 } disttype2str[] = {
152   { DISTTYPE_RPM,  "rpm" },
153   { DISTTYPE_DEB,  "deb" },
154   { DISTTYPE_ARCH, "arch" },
155   { DISTTYPE_HAIKU, "haiku" },
156   { 0, 0 }
157 };
158
159 static struct selflags2str {
160   Id flag;
161   const char *str;
162 } selflags2str[] = {
163   { SELECTION_NAME, "name" },
164   { SELECTION_PROVIDES, "provides" },
165   { SELECTION_FILELIST, "filelist" },
166   { SELECTION_CANON, "canon" },
167   { SELECTION_DOTARCH, "dotarch" },
168   { SELECTION_REL, "rel" },
169   { SELECTION_INSTALLED_ONLY, "installedonly" },
170   { SELECTION_GLOB, "glob" },
171   { SELECTION_FLAT, "flat" },
172   { SELECTION_NOCASE, "nocase" },
173   { SELECTION_SOURCE_ONLY, "sourceonly" },
174   { SELECTION_WITH_SOURCE, "withsource" },
175   { SELECTION_SKIP_KIND, "skipkind" },
176   { SELECTION_MATCH_DEPSTR, "depstr" },
177   { SELECTION_WITH_DISABLED, "withdisabled" },
178   { SELECTION_WITH_BADARCH, "withbadarch" },
179   { SELECTION_ADD, "add" },
180   { SELECTION_SUBTRACT, "subtract" },
181   { SELECTION_FILTER, "filter" },
182   { 0, 0 }
183 };
184
185 static const char *features[] = {
186 #ifdef ENABLE_LINKED_PKGS
187   "linked_packages",
188 #endif
189 #ifdef ENABLE_COMPLEX_DEPS
190   "complex_deps",
191 #endif
192 #if ENABLE_TESTCASE_HELIXREPO
193   "testcase_helixrepo",
194 #endif
195   0
196 };
197
198 typedef struct strqueue {
199   char **str;
200   int nstr;
201 } Strqueue;
202
203 #define STRQUEUE_BLOCK 63
204
205 static void
206 strqueue_init(Strqueue *q)
207 {
208   q->str = 0;
209   q->nstr = 0;
210 }
211
212 static void
213 strqueue_free(Strqueue *q)
214 {
215   int i;
216   for (i = 0; i < q->nstr; i++)
217     solv_free(q->str[i]);
218   q->str = solv_free(q->str);
219   q->nstr = 0;
220 }
221
222 static void
223 strqueue_push(Strqueue *q, const char *s)
224 {
225   q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
226   q->str[q->nstr++] = solv_strdup(s);
227 }
228
229 static void
230 strqueue_pushjoin(Strqueue *q, const char *s1, const char *s2, const char *s3)
231 {
232   q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
233   q->str[q->nstr++] = solv_dupjoin(s1, s2, s3);
234 }
235
236 static int
237 strqueue_sort_cmp(const void *ap, const void *bp, void *dp)
238 {
239   const char *a = *(const char **)ap;
240   const char *b = *(const char **)bp;
241   return strcmp(a ? a : "", b ? b : "");
242 }
243
244 static void
245 strqueue_sort(Strqueue *q)
246 {
247   if (q->nstr > 1)
248     solv_sort(q->str, q->nstr, sizeof(*q->str), strqueue_sort_cmp, 0);
249 }
250
251 static void
252 strqueue_sort_u(Strqueue *q)
253 {
254   int i, j;
255   strqueue_sort(q);
256   for (i = j = 0; i < q->nstr; i++)
257     if (!j || strqueue_sort_cmp(q->str + i, q->str + j - 1, 0) != 0)
258       q->str[j++] = q->str[i];
259   q->nstr = j;
260 }
261
262 static char *
263 strqueue_join(Strqueue *q)
264 {
265   int i, l = 0;
266   char *r, *rp;
267   for (i = 0; i < q->nstr; i++)
268     if (q->str[i])
269       l += strlen(q->str[i]) + 1;
270   l++;  /* trailing \0 */
271   r = solv_malloc(l);
272   rp = r;
273   for (i = 0; i < q->nstr; i++)
274     if (q->str[i])
275       {
276         strcpy(rp, q->str[i]);
277         rp += strlen(rp);
278         *rp++ = '\n';
279       }
280   *rp = 0;
281   return r;
282 }
283
284 static void
285 strqueue_split(Strqueue *q, const char *s)
286 {
287   const char *p;
288   if (!s)
289     return;
290   while ((p = strchr(s, '\n')) != 0)
291     {
292       q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
293       q->str[q->nstr] = solv_malloc(p - s + 1);
294       if (p > s)
295         memcpy(q->str[q->nstr], s, p - s);
296       q->str[q->nstr][p - s] = 0;
297       q->nstr++;
298       s = p + 1;
299     }
300   if (*s)
301     strqueue_push(q, s);
302 }
303
304 static void
305 strqueue_diff(Strqueue *sq1, Strqueue *sq2, Strqueue *osq)
306 {
307   int i = 0, j = 0;
308   while (i < sq1->nstr && j < sq2->nstr)
309     {
310       int r = strqueue_sort_cmp(sq1->str + i, sq2->str + j, 0);
311       if (!r)
312         i++, j++;
313       else if (r < 0)
314         strqueue_pushjoin(osq, "-", sq1->str[i++], 0);
315       else
316         strqueue_pushjoin(osq, "+", sq2->str[j++], 0);
317     }
318   while (i < sq1->nstr)
319     strqueue_pushjoin(osq, "-", sq1->str[i++], 0);
320   while (j < sq2->nstr)
321     strqueue_pushjoin(osq, "+", sq2->str[j++], 0);
322 }
323
324
325 static const char *
326 testcase_id2str(Pool *pool, Id id, int isname)
327 {
328   const char *s = pool_id2str(pool, id);
329   const char *ss;
330   char *s2, *s2p;
331   int bad = 0, paren = 0, parenbad = 0;
332
333   if (id == 0)
334     return "<NULL>";
335   if (id == 1)
336     return "\\00";
337   if (strchr("[(<=>!", *s))
338     bad++;
339   if (!strncmp(s, "namespace:", 10))
340     bad++;
341   for (ss = s + bad; *ss; ss++)
342     {
343       if (*ss == ' ' || *ss == '\\' || *(unsigned char *)ss < 32 || *ss == '(' || *ss == ')')
344         bad++;
345       if (*ss == '(')
346         paren = paren == 0 ? 1 : -1;
347       else if (*ss == ')')
348         {
349           paren = paren == 1 ? 0 : -1;
350           if (!paren)
351             parenbad += 2;
352         }
353     }
354   if (isname && ss - s > 4 && !strcmp(ss - 4, ":any"))
355     bad++;
356   if (!paren && !(bad - parenbad))
357     return s;
358
359   /* we need escaping! */
360   s2 = s2p = pool_alloctmpspace(pool, strlen(s) + bad * 2 + 1);
361   if (!strncmp(s, "namespace:", 10))
362     {
363       strcpy(s2p, "namespace\\3a");
364       s2p += 12;
365       s += 10;
366     }
367   ss = s;
368   for (; *ss; ss++)
369     {
370       *s2p++ = *ss;
371       if ((ss == s && strchr("[(<=>!", *s)) || *ss == ' ' || *ss == '\\' || *(unsigned char *)ss < 32 || *ss == '(' || *ss == ')')
372         {
373           s2p[-1] = '\\';
374           solv_bin2hex((unsigned char *)ss, 1, s2p);
375           s2p += 2;
376         }
377     }
378   *s2p = 0;
379   if (isname && s2p - s2 > 4 && !strcmp(s2p - 4, ":any"))
380     strcpy(s2p - 4, "\\3aany");
381   return s2;
382 }
383
384 struct oplist {
385   Id flags;
386   const char *opname;
387 } oplist[] = {
388   { REL_EQ, "=" },
389   { REL_GT | REL_LT | REL_EQ, "<=>" },
390   { REL_LT | REL_EQ, "<=" },
391   { REL_GT | REL_EQ, ">=" },
392   { REL_GT, ">" },
393   { REL_GT | REL_LT, "<>" },
394   { REL_AND,   "&" },
395   { REL_OR ,   "|" },
396   { REL_WITH , "+" },
397   { REL_WITHOUT , "-" },
398   { REL_NAMESPACE , "<NAMESPACE>" },
399   { REL_ARCH,       "." },
400   { REL_MULTIARCH,  "<MULTIARCH>" },
401   { REL_FILECONFLICT,  "<FILECONFLICT>" },
402   { REL_COND,  "<IF>" },
403   { REL_COMPAT,  "compat >=" },
404   { REL_KIND,  "<KIND>" },
405   { REL_ELSE, "<ELSE>" },
406   { REL_ERROR, "<ERROR>" },
407   { REL_UNLESS, "<UNLESS>" },
408   { REL_LT, "<" },
409   { 0, 0 }
410 };
411
412 static char *
413 testcase_dep2str_complex(Pool *pool, char *s, Id id, int addparens)
414 {
415   Reldep *rd;
416   const char *s2;
417   int needparens;
418   struct oplist *op;
419
420   if (!ISRELDEP(id))
421     {
422       s2 = testcase_id2str(pool, id, 1);
423       s = pool_tmpappend(pool, s, s2, 0);
424       pool_freetmpspace(pool, s2);
425       return s;
426     }
427   rd = GETRELDEP(pool, id);
428
429   /* check for special shortcuts */
430   if (rd->flags == REL_NAMESPACE && !ISRELDEP(rd->name) && !strncmp(pool_id2str(pool, rd->name), "namespace:", 10))
431     {
432       s = pool_tmpappend(pool, s, pool_id2str(pool, rd->name), "(");
433       s = testcase_dep2str_complex(pool, s, rd->evr, 0);
434       return pool_tmpappend(pool, s, ")", 0);
435     }
436   if (rd->flags == REL_MULTIARCH && !ISRELDEP(rd->name) && rd->evr == ARCH_ANY)
437     {
438       /* append special :any suffix */
439       s2 = testcase_id2str(pool, rd->name, 1);
440       s = pool_tmpappend(pool, s, s2, ":any");
441       pool_freetmpspace(pool, s2);
442       return s;
443     }
444
445   needparens = 0;
446   if (ISRELDEP(rd->name))
447     {
448       Reldep *rd2 = GETRELDEP(pool, rd->name);
449       needparens = 1;
450       if (rd->flags > 7 && rd->flags != REL_COMPAT && rd2->flags && rd2->flags <= 7)
451         needparens = 0;
452     }
453
454   if (addparens)
455     s = pool_tmpappend(pool, s, "(", 0);
456   s = testcase_dep2str_complex(pool, s, rd->name, needparens);
457
458   for (op = oplist; op->flags; op++)
459     if (rd->flags == op->flags)
460       break;
461   if (op->flags)
462     {
463       s = pool_tmpappend(pool, s, " ", op->opname);
464       s = pool_tmpappend(pool, s, " ", 0);
465     }
466   else
467     {
468       char buf[64];
469       sprintf(buf, " <%u> ", rd->flags);
470       s = pool_tmpappend(pool, s, buf, 0);
471     }
472
473   needparens = 0;
474   if (ISRELDEP(rd->evr))
475     {
476       Reldep *rd2 = GETRELDEP(pool, rd->evr);
477       needparens = 1;
478       if (rd->flags > 7 && rd2->flags && rd2->flags <= 7)
479         needparens = 0;
480       if (rd->flags == REL_AND && rd2->flags == REL_AND)
481         needparens = 0; /* chain */
482       if (rd->flags == REL_OR && rd2->flags == REL_OR)
483         needparens = 0; /* chain */
484       if (rd->flags > 0 && rd->flags < 8 && rd2->flags == REL_COMPAT)
485         needparens = 0; /* chain */
486     }
487   if (!ISRELDEP(rd->evr))
488     {
489       s2 = testcase_id2str(pool, rd->evr, 0);
490       s = pool_tmpappend(pool, s, s2, 0);
491       pool_freetmpspace(pool, s2);
492     }
493   else
494     s = (char *)testcase_dep2str_complex(pool, s, rd->evr, needparens);
495   if (addparens)
496     s = pool_tmpappend(pool, s, ")", 0);
497   return s;
498 }
499
500 const char *
501 testcase_dep2str(Pool *pool, Id id)
502 {
503   char *s;
504   if (!ISRELDEP(id))
505     return testcase_id2str(pool, id, 1);
506   s = pool_alloctmpspace(pool, 1);
507   *s = 0;
508   return testcase_dep2str_complex(pool, s, id, 0);
509 }
510
511
512 /* Convert a simple string. Also handle the :any suffix */
513 static Id
514 testcase_str2dep_simple(Pool *pool, const char **sp, int isname)
515 {
516   int haveesc = 0;
517   int paren = 0;
518   int isany = 0;
519   Id id;
520   const char *s;
521   for (s = *sp; *s; s++)
522     {
523       if (*s == '\\')
524         haveesc++;
525       if (*s == ' ' || *(unsigned char *)s < 32)
526         break;
527       if (*s == '(')
528         paren++;
529       if (*s == ')' && paren-- <= 0)
530         break;
531     }
532   if (isname && s - *sp > 4 && !strncmp(s - 4, ":any", 4))
533     {
534       isany = 1;
535       s -= 4;
536     }
537   if (!haveesc)
538     {
539       if (s - *sp == 6 && !strncmp(*sp, "<NULL>", 6))
540         id = 0;
541       else
542         id = pool_strn2id(pool, *sp, s - *sp, 1);
543     }
544   else if (s - *sp == 3 && !strncmp(*sp, "\\00", 3))
545     id = 1;
546   else
547     {
548       char buf[128], *bp, *bp2;
549       const char *sp2;
550       bp = s - *sp >= 128 ? solv_malloc(s - *sp + 1) : buf;
551       for (bp2 = bp, sp2 = *sp; sp2 < s;)
552         {
553           *bp2++ = *sp2++;
554           if (bp2[-1] == '\\')
555             solv_hex2bin(&sp2, (unsigned char *)bp2 - 1, 1);
556         }
557       *bp2 = 0;
558       id = pool_str2id(pool, bp, 1);
559       if (bp != buf)
560         solv_free(bp);
561     }
562   if (isany)
563     {
564       id = pool_rel2id(pool, id, ARCH_ANY, REL_MULTIARCH, 1);
565       s += 4;
566     }
567   *sp = s;
568   return id;
569 }
570
571
572 static Id
573 testcase_str2dep_complex(Pool *pool, const char **sp, int relop)
574 {
575   const char *s = *sp;
576   Id flags, id, id2, namespaceid = 0;
577   struct oplist *op;
578
579   while (*s == ' ' || *s == '\t')
580     s++;
581   if (!strncmp(s, "namespace:", 10))
582     {
583       /* special namespace hack */
584       const char *s2;
585       for (s2 = s + 10; *s2 && *s2 != '('; s2++)
586         ;
587       if (*s2 == '(')
588         {
589           namespaceid = pool_strn2id(pool, s, s2 - s, 1);
590           s = s2;
591         }
592     }
593   if (*s == '(')
594     {
595       s++;
596       id = testcase_str2dep_complex(pool, &s, 0);
597       if (!s || *s != ')')
598         {
599           *sp = 0;
600           return 0;
601         }
602       s++;
603     }
604   else
605     id = testcase_str2dep_simple(pool, &s, relop ? 0 : 1);
606   if (namespaceid)
607     id = pool_rel2id(pool, namespaceid, id, REL_NAMESPACE, 1);
608     
609   for (;;)
610     {
611       while (*s == ' ' || *s == '\t')
612         s++;
613       if (!*s || *s == ')' || (relop && strncmp(s, "compat >= ", 10) != 0))
614         {
615           *sp = s;
616           return id;
617         }
618
619       /* we have an op! Find the end */
620       flags = -1;
621       if (s[0] == '<' && (s[1] >= '0' && s[1] <= '9'))
622         {
623           const char *s2;
624           for (s2 = s + 1; *s2 >= '0' && *s2 <= '9'; s2++)
625             ;
626           if (*s2 == '>')
627             {
628               flags = strtoul(s + 1, 0, 10);
629               s = s2 + 1;
630             }
631         }
632       if (flags == -1)
633         {
634           for (op = oplist; op->flags; op++)
635             if (!strncmp(s, op->opname, strlen(op->opname)))
636               break;
637           if (!op->flags)
638             {
639               *sp = 0;
640               return 0;
641             }
642           flags = op->flags;
643           s += strlen(op->opname);
644         }
645       id2 = testcase_str2dep_complex(pool, &s, flags > 0 && flags < 8);
646       if (!s)
647         {
648           *sp = 0;
649           return 0;
650         }
651       id = pool_rel2id(pool, id, id2, flags, 1);
652     }
653 }
654
655 Id
656 testcase_str2dep(Pool *pool, const char *s)
657 {
658   Id id = testcase_str2dep_complex(pool, &s, 0);
659   return s && !*s ? id : 0;
660 }
661
662 /**********************************************************/
663
664 const char *
665 testcase_repoid2str(Pool *pool, Id repoid)
666 {
667   Repo *repo = pool_id2repo(pool, repoid);
668   if (repo->name)
669     {
670       char *r = pool_tmpjoin(pool, repo->name, 0, 0);
671       char *rp;
672       for (rp = r; *rp; rp++)
673         if (*rp == ' ' || *rp == '\t')
674           *rp = '_';
675       return r;
676     }
677   else
678     {
679       char buf[20];
680       sprintf(buf, "#%d", repoid);
681       return pool_tmpjoin(pool, buf, 0, 0);
682     }
683 }
684
685 const char *
686 testcase_solvid2str(Pool *pool, Id p)
687 {
688   Solvable *s = pool->solvables + p;
689   const char *n, *e, *a;
690   char *str, buf[20];
691
692   if (p == SYSTEMSOLVABLE)
693     return "@SYSTEM";
694   n = pool_id2str(pool, s->name);
695   e = pool_id2str(pool, s->evr);
696   a = pool_id2str(pool, s->arch);
697   str = pool_alloctmpspace(pool, strlen(n) + strlen(e) + strlen(a) + 3);
698   sprintf(str, "%s-%s.%s", n, e, a);
699   if (!s->repo)
700     return pool_tmpappend(pool, str, "@", 0);
701   if (s->repo->name)
702     {
703       int l = strlen(str);
704       char *str2 = pool_tmpappend(pool, str, "@", s->repo->name);
705       for (; str2[l]; l++)
706         if (str2[l] == ' ' || str2[l] == '\t')
707           str2[l] = '_';
708       return str2;
709     }
710   sprintf(buf, "@#%d", s->repo->repoid);
711   return pool_tmpappend(pool, str, buf, 0);
712 }
713
714 Repo *
715 testcase_str2repo(Pool *pool, const char *str)
716 {
717   int repoid;
718   Repo *repo = 0;
719   if (str[0] == '#' && (str[1] >= '0' && str[1] <= '9'))
720     {
721       int j;
722       repoid = 0;
723       for (j = 1; str[j] >= '0' && str[j] <= '9'; j++)
724         repoid = repoid * 10 + (str[j] - '0');
725       if (!str[j] && repoid > 0 && repoid < pool->nrepos)
726         repo = pool_id2repo(pool, repoid);
727     }
728   if (!repo)
729     {
730       FOR_REPOS(repoid, repo)
731         {
732           int i, l;
733           if (!repo->name)
734             continue;
735           l = strlen(repo->name);
736           for (i = 0; i < l; i++)
737             {
738               int c = repo->name[i];
739               if (c == ' ' || c == '\t')
740                 c = '_';
741               if (c != str[i])
742                 break;
743             }
744           if (i == l && !str[l])
745             break;
746         }
747       if (repoid >= pool->nrepos)
748         repo = 0;
749     }
750   return repo;
751 }
752
753 Id
754 testcase_str2solvid(Pool *pool, const char *str)
755 {
756   int i, l = strlen(str);
757   int repostart;
758   Repo *repo;
759   Id arch;
760
761   if (!l)
762     return 0;
763   if (*str == '@' && !strcmp(str, "@SYSTEM"))
764     return SYSTEMSOLVABLE;
765   repo = 0;
766   for (i = l - 1; i >= 0; i--)
767     if (str[i] == '@' && (repo = testcase_str2repo(pool, str + i + 1)) != 0)
768       break;
769   if (i < 0)
770     i = l;
771   repostart = i;
772   /* now find the arch (if present) */
773   arch = 0;
774   for (i = repostart - 1; i > 0; i--)
775     if (str[i] == '.')
776       {
777         arch = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
778         if (arch)
779           repostart = i;
780         break;
781       }
782   /* now find the name */
783   for (i = repostart - 1; i > 0; i--)
784     {
785       if (str[i] == '-')
786         {
787           Id nid, evrid, p, pp;
788           nid = pool_strn2id(pool, str, i, 0);
789           if (!nid)
790             continue;
791           evrid = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
792           if (!evrid)
793             continue;
794           /* first check whatprovides */
795           FOR_PROVIDES(p, pp, nid)
796             {
797               Solvable *s = pool->solvables + p;
798               if (s->name != nid || s->evr != evrid)
799                 continue;
800               if (repo && s->repo != repo)
801                 continue;
802               if (arch && s->arch != arch)
803                 continue;
804               return p;
805             }
806           /* maybe it's not installable and thus not in whatprovides. do a slow search */
807           if (repo)
808             {
809               Solvable *s;
810               FOR_REPO_SOLVABLES(repo, p, s)
811                 {
812                   if (s->name != nid || s->evr != evrid)
813                     continue;
814                   if (arch && s->arch != arch)
815                     continue;
816                   return p;
817                 }
818             }
819           else
820             {
821               FOR_POOL_SOLVABLES(p)
822                 {
823                   Solvable *s = pool->solvables + p;
824                   if (s->name != nid || s->evr != evrid)
825                     continue;
826                   if (arch && s->arch != arch)
827                     continue;
828                   return p;
829                 }
830             }
831         }
832     }
833   return 0;
834 }
835
836 const char *
837 testcase_job2str(Pool *pool, Id how, Id what)
838 {
839   char *ret;
840   const char *jobstr;
841   const char *selstr;
842   const char *pkgstr;
843   int i, o;
844   Id select = how & SOLVER_SELECTMASK;
845
846   for (i = 0; job2str[i].str; i++)
847     if ((how & SOLVER_JOBMASK) == job2str[i].job)
848       break;
849   jobstr = job2str[i].str ? job2str[i].str : "unknown";
850   if (select == SOLVER_SOLVABLE)
851     {
852       selstr = " pkg ";
853       pkgstr = testcase_solvid2str(pool, what);
854     }
855   else if (select == SOLVER_SOLVABLE_NAME)
856     {
857       selstr = " name ";
858       pkgstr = testcase_dep2str(pool, what);
859     }
860   else if (select == SOLVER_SOLVABLE_PROVIDES)
861     {
862       selstr = " provides ";
863       pkgstr = testcase_dep2str(pool, what);
864     }
865   else if (select == SOLVER_SOLVABLE_ONE_OF)
866     {
867       Id p;
868       selstr = " oneof ";
869       pkgstr = 0;
870       while ((p = pool->whatprovidesdata[what++]) != 0)
871         {
872           const char *s = testcase_solvid2str(pool, p);
873           if (pkgstr)
874             {
875               pkgstr = pool_tmpappend(pool, pkgstr, " ", s);
876               pool_freetmpspace(pool, s);
877             }
878           else
879             pkgstr = s;
880         }
881       if (!pkgstr)
882         pkgstr = "nothing";
883     }
884   else if (select == SOLVER_SOLVABLE_REPO)
885     {
886       Repo *repo = pool_id2repo(pool, what);
887       selstr = " repo ";
888       if (!repo->name)
889         {
890           char buf[20];
891           sprintf(buf, "#%d", repo->repoid);
892           pkgstr = pool_tmpjoin(pool, buf, 0, 0);
893         }
894       else
895         pkgstr = pool_tmpjoin(pool, repo->name, 0, 0);
896     }
897   else if (select == SOLVER_SOLVABLE_ALL)
898     {
899       selstr = " all ";
900       pkgstr = "packages";
901     }
902   else
903     {
904       selstr = " unknown ";
905       pkgstr = "unknown";
906     }
907   ret = pool_tmpjoin(pool, jobstr, selstr, pkgstr);
908   o = strlen(ret);
909   ret = pool_tmpappend(pool, ret, " ", 0);
910   for (i = 0; jobflags2str[i].str; i++)
911     if ((how & jobflags2str[i].flag) != 0)
912       ret = pool_tmpappend(pool, ret, ",", jobflags2str[i].str);
913   if (!ret[o + 1])
914     ret[o] = 0;
915   else
916     {
917       ret[o + 1] = '[';
918       ret = pool_tmpappend(pool, ret, "]", 0);
919     }
920   return ret;
921 }
922
923 static int
924 str2selflags(Pool *pool, char *s)       /* modifies the string! */
925 {
926   int i, selflags = 0;
927   while (s)
928     {
929       char *se = strchr(s, ',');
930       if (se)
931         *se++ = 0;
932       for (i = 0; selflags2str[i].str; i++)
933         if (!strcmp(s, selflags2str[i].str))
934           {
935             selflags |= selflags2str[i].flag;
936             break;
937           }
938       if (!selflags2str[i].str)
939         pool_error(pool, 0, "str2job: unknown selection flag '%s'", s);
940       s = se;
941     }
942   return selflags;
943 }
944
945 static int
946 str2jobflags(Pool *pool, char *s)       /* modifies the string */
947 {
948   int i, jobflags = 0;
949   while (s)
950     {
951       char *se = strchr(s, ',');
952       if (se)
953         *se++ = 0;
954       for (i = 0; jobflags2str[i].str; i++)
955         if (!strcmp(s, jobflags2str[i].str))
956           {
957             jobflags |= jobflags2str[i].flag;
958             break;
959           }
960       if (!jobflags2str[i].str)
961         pool_error(pool, 0, "str2job: unknown job flag '%s'", s);
962       s = se;
963     }
964   return jobflags;
965 }
966
967 Id
968 testcase_str2job(Pool *pool, const char *str, Id *whatp)
969 {
970   int i;
971   Id job;
972   Id what;
973   char *s;
974   char **pieces = 0;
975   int npieces = 0;
976
977   *whatp = 0;
978   /* so we can patch it */
979   s = pool_tmpjoin(pool, str, 0, 0);
980   /* split it in pieces */
981   for (;;)
982     {
983       while (*s == ' ' || *s == '\t')
984         s++;
985       if (!*s)
986         break;
987       pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
988       pieces[npieces++] = s;
989       while (*s && *s != ' ' && *s != '\t')
990         s++;
991       if (*s)
992         *s++ = 0;
993     }
994   if (npieces < 3)
995     {
996       pool_error(pool, -1, "str2job: bad line '%s'", str);
997       solv_free(pieces);
998       return -1;
999     }
1000
1001   for (i = 0; job2str[i].str; i++)
1002     if (!strcmp(pieces[0], job2str[i].str))
1003       break;
1004   if (!job2str[i].str)
1005     {
1006       pool_error(pool, -1, "str2job: unknown job '%s'", str);
1007       solv_free(pieces);
1008       return -1;
1009     }
1010   job = job2str[i].job;
1011   what = 0;
1012   if (npieces > 3)
1013     {
1014       char *flags = pieces[npieces - 1];
1015       if (*flags == '[' && flags[strlen(flags) - 1] == ']')
1016         {
1017           npieces--;
1018           flags++;
1019           flags[strlen(flags) - 1] = 0;
1020           job |= str2jobflags(pool, flags);
1021         }
1022     }
1023   if (!strcmp(pieces[1], "pkg"))
1024     {
1025       if (npieces != 3)
1026         {
1027           pool_error(pool, -1, "str2job: bad pkg selector in '%s'", str);
1028           solv_free(pieces);
1029           return -1;
1030         }
1031       job |= SOLVER_SOLVABLE;
1032       what = testcase_str2solvid(pool, pieces[2]);
1033       if (!what)
1034         {
1035           pool_error(pool, -1, "str2job: unknown package '%s'", pieces[2]);
1036           solv_free(pieces);
1037           return -1;
1038         }
1039     }
1040   else if (!strcmp(pieces[1], "name") || !strcmp(pieces[1], "provides"))
1041     {
1042       /* join em again for dep2str... */
1043       char *sp;
1044       for (sp = pieces[2]; sp < pieces[npieces - 1]; sp++)
1045         if (*sp == 0)
1046           *sp = ' ';
1047       what = 0;
1048       if (pieces[1][0] == 'p' && strncmp(pieces[2], "namespace:", 10) == 0)
1049         {
1050           char *spe = strchr(pieces[2], '(');
1051           int l = strlen(pieces[2]);
1052           if (spe && pieces[2][l - 1] == ')')
1053             {
1054               /* special namespace provides */
1055               if (strcmp(spe, "(<NULL>)") != 0)
1056                 {
1057                   pieces[2][l - 1] = 0;
1058                   what = testcase_str2dep(pool, spe + 1);
1059                   pieces[2][l - 1] = ')';
1060                 }
1061               what = pool_rel2id(pool, pool_strn2id(pool, pieces[2], spe - pieces[2], 1), what, REL_NAMESPACE, 1);
1062             }
1063         }
1064       if (!what)
1065         what = testcase_str2dep(pool, pieces[2]);
1066       if (pieces[1][0] == 'n')
1067         job |= SOLVER_SOLVABLE_NAME;
1068       else
1069         job |= SOLVER_SOLVABLE_PROVIDES;
1070     }
1071   else if (!strcmp(pieces[1], "oneof"))
1072     {
1073       Queue q;
1074       job |= SOLVER_SOLVABLE_ONE_OF;
1075       queue_init(&q);
1076       if (npieces > 2 && strcmp(pieces[2], "nothing") != 0)
1077         {
1078           for (i = 2; i < npieces; i++)
1079             {
1080               Id p = testcase_str2solvid(pool, pieces[i]);
1081               if (!p)
1082                 {
1083                   pool_error(pool, -1, "str2job: unknown package '%s'", pieces[i]);
1084                   queue_free(&q);
1085                   solv_free(pieces);
1086                   return -1;
1087                 }
1088               queue_push(&q, p);
1089             }
1090         }
1091       what = pool_queuetowhatprovides(pool, &q);
1092       queue_free(&q);
1093     }
1094   else if (!strcmp(pieces[1], "repo"))
1095     {
1096       Repo *repo;
1097       if (npieces != 3)
1098         {
1099           pool_error(pool, -1, "str2job: bad line '%s'", str);
1100           solv_free(pieces);
1101           return -1;
1102         }
1103       repo = testcase_str2repo(pool, pieces[2]);
1104       if (!repo)
1105         {
1106           pool_error(pool, -1, "str2job: unknown repo '%s'", pieces[2]);
1107           solv_free(pieces);
1108           return -1;
1109         }
1110       job |= SOLVER_SOLVABLE_REPO;
1111       what = repo->repoid;
1112     }
1113   else if (!strcmp(pieces[1], "all"))
1114     {
1115       if (npieces != 3 && strcmp(pieces[2], "packages") != 0)
1116         {
1117           pool_error(pool, -1, "str2job: bad line '%s'", str);
1118           solv_free(pieces);
1119           return -1;
1120         }
1121       job |= SOLVER_SOLVABLE_ALL;
1122       what = 0;
1123     }
1124   else
1125     {
1126       pool_error(pool, -1, "str2job: unknown selection in '%s'", str);
1127       solv_free(pieces);
1128       return -1;
1129     }
1130   *whatp = what;
1131   solv_free(pieces);
1132   return job;
1133 }
1134
1135 #define SELECTIONJOB_MATCHDEPS          1
1136 #define SELECTIONJOB_MATCHDEPID         2
1137 #define SELECTIONJOB_MATCHSOLVABLE      3
1138
1139 static int
1140 addselectionjob(Pool *pool, char **pieces, int npieces, Queue *jobqueue, int type, int keyname)
1141 {
1142   Id job;
1143   int i, r = 0;
1144   int selflags;
1145   Queue sel;
1146   char *sp;
1147
1148   for (i = 0; job2str[i].str; i++)
1149     if (!strcmp(pieces[0], job2str[i].str))
1150       break;
1151   if (!job2str[i].str)
1152     return pool_error(pool, -1, "selstr2job: unknown job '%s'", pieces[0]);
1153   job = job2str[i].job;
1154   if (npieces > 3)
1155     {
1156       char *flags = pieces[npieces - 1];
1157       if (*flags == '[' && flags[strlen(flags) - 1] == ']')
1158         {
1159           npieces--;
1160           flags++;
1161           flags[strlen(flags) - 1] = 0;
1162           job |= str2jobflags(pool, flags);
1163         }
1164     }
1165   if (npieces < 4)
1166     return pool_error(pool, -1, "selstr2job: no selection flags");
1167   selflags = str2selflags(pool, pieces[npieces - 1]);
1168   /* re-join pieces */
1169   for (sp = pieces[2]; sp < pieces[npieces - 2]; sp++)
1170     if (*sp == 0)
1171       *sp = ' ';
1172   queue_init(&sel);
1173   if (selflags & (SELECTION_ADD | SELECTION_SUBTRACT | SELECTION_FILTER))
1174     {
1175       for (i = 0; i < jobqueue->count; i += 2)
1176         queue_push2(&sel, jobqueue->elements[i] & (SOLVER_SELECTMASK | SOLVER_SETMASK), jobqueue->elements[i + 1]);
1177       queue_empty(jobqueue);
1178     }
1179   if (!type)
1180     r = selection_make(pool, &sel, pieces[2], selflags);
1181   else if (type == SELECTIONJOB_MATCHDEPS)
1182     r = selection_make_matchdeps(pool, &sel, pieces[2], selflags, keyname, 0);
1183   else if (type == SELECTIONJOB_MATCHDEPID)
1184     r = selection_make_matchdepid(pool, &sel, testcase_str2dep(pool, pieces[2]), selflags, keyname, 0);
1185   else if (type == SELECTIONJOB_MATCHSOLVABLE)
1186     r = selection_make_matchsolvable(pool, &sel, testcase_str2solvid(pool, pieces[2]), selflags, keyname, 0);
1187   for (i = 0; i < sel.count; i += 2)
1188     queue_push2(jobqueue, job | sel.elements[i], sel.elements[i + 1]);
1189   queue_free(&sel);
1190   return r;
1191 }
1192
1193 static void
1194 writedeps(Repo *repo, FILE *fp, const char *tag, Id key, Solvable *s, Offset off)
1195 {
1196   Pool *pool = repo->pool;
1197   Id id, *dp;
1198   int tagwritten = 0;
1199   const char *idstr;
1200
1201   if (!off)
1202     return;
1203   dp = repo->idarraydata + off;
1204   while ((id = *dp++) != 0)
1205     {
1206       if (key == SOLVABLE_REQUIRES && id == SOLVABLE_PREREQMARKER)
1207         {
1208           if (tagwritten)
1209             fprintf(fp, "-%s\n", tag);
1210           tagwritten = 0;
1211           tag = "Prq:";
1212           continue;
1213         }
1214       if (key == SOLVABLE_PROVIDES && id == SOLVABLE_FILEMARKER)
1215         continue;
1216       idstr = testcase_dep2str(pool, id);
1217       if (!tagwritten)
1218         {
1219           fprintf(fp, "+%s\n", tag);
1220           tagwritten = 1;
1221         }
1222       fprintf(fp, "%s\n", idstr);
1223     }
1224   if (tagwritten)
1225     fprintf(fp, "-%s\n", tag);
1226 }
1227
1228 static void
1229 writefilelist(Repo *repo, FILE *fp, const char *tag, Solvable *s)
1230 {
1231   Pool *pool = repo->pool;
1232   int tagwritten = 0;
1233   Dataiterator di;
1234
1235   dataiterator_init(&di, pool, repo, s - pool->solvables, SOLVABLE_FILELIST, 0, 0);
1236   while (dataiterator_step(&di))
1237     {
1238       const char *s = repodata_dir2str(di.data, di.kv.id, di.kv.str);
1239       if (!tagwritten)
1240         {
1241           fprintf(fp, "+%s\n", tag);
1242           tagwritten = 1;
1243         }
1244       fprintf(fp, "%s\n", s);
1245     }
1246   if (tagwritten)
1247     fprintf(fp, "-%s\n", tag);
1248   dataiterator_free(&di);
1249 }
1250
1251 int
1252 testcase_write_testtags(Repo *repo, FILE *fp)
1253 {
1254   Pool *pool = repo->pool;
1255   Solvable *s;
1256   Id p;
1257   const char *name;
1258   const char *evr;
1259   const char *arch;
1260   const char *release;
1261   const char *tmp;
1262   unsigned int ti;
1263   Queue q;
1264
1265   fprintf(fp, "=Ver: 3.0\n");
1266   queue_init(&q);
1267   FOR_REPO_SOLVABLES(repo, p, s)
1268     {
1269       name = pool_id2str(pool, s->name);
1270       evr = pool_id2str(pool, s->evr);
1271       arch = pool_id2str(pool, s->arch);
1272       release = strrchr(evr, '-');
1273       if (!release)
1274         release = evr + strlen(evr);
1275       fprintf(fp, "=Pkg: %s %.*s %s %s\n", name, (int)(release - evr), evr, *release && release[1] ? release + 1 : "-", arch);
1276       tmp = solvable_lookup_str(s, SOLVABLE_SUMMARY);
1277       if (tmp)
1278         fprintf(fp, "=Sum: %s\n", tmp);
1279       writedeps(repo, fp, "Req:", SOLVABLE_REQUIRES, s, s->requires);
1280       writedeps(repo, fp, "Prv:", SOLVABLE_PROVIDES, s, s->provides);
1281       writedeps(repo, fp, "Obs:", SOLVABLE_OBSOLETES, s, s->obsoletes);
1282       writedeps(repo, fp, "Con:", SOLVABLE_CONFLICTS, s, s->conflicts);
1283       writedeps(repo, fp, "Rec:", SOLVABLE_RECOMMENDS, s, s->recommends);
1284       writedeps(repo, fp, "Sup:", SOLVABLE_SUPPLEMENTS, s, s->supplements);
1285       writedeps(repo, fp, "Sug:", SOLVABLE_SUGGESTS, s, s->suggests);
1286       writedeps(repo, fp, "Enh:", SOLVABLE_ENHANCES, s, s->enhances);
1287       if (solvable_lookup_idarray(s, SOLVABLE_PREREQ_IGNOREINST, &q))
1288         {
1289           int i;
1290           fprintf(fp, "+Ipr:\n");
1291           for (i = 0; i < q.count; i++)
1292             fprintf(fp, "%s\n", testcase_dep2str(pool, q.elements[i]));
1293           fprintf(fp, "-Ipr:\n");
1294         }
1295       if (s->vendor)
1296         fprintf(fp, "=Vnd: %s\n", pool_id2str(pool, s->vendor));
1297       ti = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0);
1298       if (ti)
1299         fprintf(fp, "=Tim: %u\n", ti);
1300       writefilelist(repo, fp, "Fls:", s);
1301     }
1302   queue_free(&q);
1303   return 0;
1304 }
1305
1306 static inline Offset
1307 adddep(Repo *repo, Offset olddeps, char *str, Id marker)
1308 {
1309   Id id = *str == '/' ? pool_str2id(repo->pool, str, 1) : testcase_str2dep(repo->pool, str);
1310   return repo_addid_dep(repo, olddeps, id, marker);
1311 }
1312
1313 static void
1314 finish_v2_solvable(Pool *pool, Repodata *data, Solvable *s, char *filelist, int nfilelist)
1315 {
1316   if (nfilelist)
1317     {
1318       int l;
1319       Id did;
1320       for (l = 0; l < nfilelist; l += strlen(filelist + l) + 1)
1321         {
1322           char *p = strrchr(filelist + l, '/');
1323           if (!p)
1324             continue;
1325           *p++ = 0;
1326           did = repodata_str2dir(data, filelist + l, 1);
1327           p[-1] = '/';
1328           if (!did)
1329             did = repodata_str2dir(data, "/", 1);
1330           repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, did, p);
1331         }
1332     }
1333   repo_rewrite_suse_deps(s, 0);
1334 }
1335
1336 /* stripped down version of susetags parser used for testcases */
1337 int
1338 testcase_add_testtags(Repo *repo, FILE *fp, int flags)
1339 {
1340   Pool *pool = repo->pool;
1341   char *line, *linep;
1342   int aline;
1343   int tag;
1344   Repodata *data;
1345   Solvable *s;
1346   char *sp[5];
1347   unsigned int t;
1348   int intag;
1349   char *filelist = 0;
1350   int afilelist = 0;
1351   int nfilelist = 0;
1352   int tagsversion = 0;
1353   int addselfprovides = 1;      /* for compat reasons */
1354
1355   data = repo_add_repodata(repo, flags);
1356   s = 0;
1357   intag = 0;
1358
1359   aline = 1024;
1360   line = solv_malloc(aline);
1361   linep = line;
1362   for (;;)
1363     {
1364       if (linep - line + 16 > aline)
1365         {
1366           aline = linep - line;
1367           line = solv_realloc(line, aline + 512);
1368           linep = line + aline;
1369           aline += 512;
1370         }
1371       if (!fgets(linep, aline - (linep - line), fp))
1372         break;
1373       linep += strlen(linep);
1374       if (linep == line || linep[-1] != '\n')
1375         continue;
1376       linep[-1] = 0;
1377       linep = line + intag;
1378       if (intag)
1379         {
1380           if (line[intag] == '-' && !strncmp(line + 1, line + intag + 1, intag - 2))
1381             {
1382               intag = 0;
1383               linep = line;
1384               continue;
1385             }
1386         }
1387       else if (line[0] == '+' && line[1] && line[1] != ':')
1388         {
1389           char *tagend = strchr(line, ':');
1390           if (!tagend)
1391             continue;
1392           line[0] = '=';
1393           tagend[1] = ' ';
1394           intag = tagend + 2 - line;
1395           linep = line + intag;
1396           continue;
1397         }
1398       if (*line != '=' || !line[1] || !line[2] || !line[3] || line[4] != ':')
1399         continue;
1400       tag = line[1] << 16 | line[2] << 8 | line[3];
1401       /* tags that do not need a solvable */
1402       switch(tag)
1403         {
1404         case 'V' << 16 | 'e' << 8 | 'r':
1405           tagsversion = atoi(line + 6);
1406           addselfprovides = tagsversion < 3 || strstr(line + 6, "addselfprovides") != 0;
1407           continue;
1408         case 'P' << 16 | 'k' << 8 | 'g':
1409           if (s)
1410             {
1411               if (tagsversion == 2)
1412                 finish_v2_solvable(pool, data, s, filelist, nfilelist);
1413               if (addselfprovides && s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
1414                 s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
1415             }
1416           nfilelist = 0;
1417           if (split(line + 5, sp, 5) != 4)
1418             break;
1419           s = pool_id2solvable(pool, repo_add_solvable(repo));
1420           s->name = pool_str2id(pool, sp[0], 1);
1421           /* join back version and release */
1422           if (sp[2] && !(sp[2][0] == '-' && !sp[2][1]))
1423             sp[2][-1] = '-';
1424           s->evr = makeevr(pool, sp[1]);
1425           s->arch = pool_str2id(pool, sp[3], 1);
1426           continue;
1427         default:
1428           break;
1429         }
1430       if (!s)
1431         continue;
1432       /* tags that need a solvable */
1433       switch(tag)
1434         {
1435         case 'S' << 16 | 'u' << 8 | 'm':
1436           repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line + 6);
1437           break;
1438         case 'V' << 16 | 'n' << 8 | 'd':
1439           s->vendor = pool_str2id(pool, line + 6, 1);
1440           break;
1441         case 'T' << 16 | 'i' << 8 | 'm':
1442           t = atoi(line + 6);
1443           if (t)
1444             repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, t);
1445           break;
1446         case 'R' << 16 | 'e' << 8 | 'q':
1447           s->requires = adddep(repo, s->requires, line + 6, -SOLVABLE_PREREQMARKER);
1448           break;
1449         case 'P' << 16 | 'r' << 8 | 'q':
1450           s->requires = adddep(repo, s->requires, line + 6, SOLVABLE_PREREQMARKER);
1451           break;
1452         case 'P' << 16 | 'r' << 8 | 'v':
1453           /* version 2 had the file list at the end of the provides */
1454           if (tagsversion == 2)
1455             {
1456               if (line[6] == '/')
1457                 {
1458                   int l = strlen(line + 6) + 1;
1459                   if (nfilelist + l > afilelist)
1460                     {
1461                       afilelist = nfilelist + l + 512;
1462                       filelist = solv_realloc(filelist, afilelist);
1463                     }
1464                   memcpy(filelist + nfilelist, line + 6, l);
1465                   nfilelist += l;
1466                   break;
1467                 }
1468               if (nfilelist)
1469                 {
1470                   int l;
1471                   for (l = 0; l < nfilelist; l += strlen(filelist + l) + 1)
1472                     s->provides = repo_addid_dep(repo, s->provides, pool_str2id(pool, filelist + l, 1), 0);
1473                   nfilelist = 0;
1474                 }
1475             }
1476           s->provides = adddep(repo, s->provides, line + 6, 0);
1477           break;
1478         case 'F' << 16 | 'l' << 8 | 's':
1479           {
1480             char *p = strrchr(line + 6, '/');
1481             Id did;
1482             if (!p)
1483               break;
1484             *p++ = 0;
1485             did = repodata_str2dir(data, line + 6, 1);
1486             if (!did)
1487               did = repodata_str2dir(data, "/", 1);
1488             repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, did, p);
1489             break;
1490           }
1491         case 'O' << 16 | 'b' << 8 | 's':
1492           s->obsoletes = adddep(repo, s->obsoletes, line + 6, 0);
1493           break;
1494         case 'C' << 16 | 'o' << 8 | 'n':
1495           s->conflicts = adddep(repo, s->conflicts, line + 6, 0);
1496           break;
1497         case 'R' << 16 | 'e' << 8 | 'c':
1498           s->recommends = adddep(repo, s->recommends, line + 6, 0);
1499           break;
1500         case 'S' << 16 | 'u' << 8 | 'p':
1501           s->supplements = adddep(repo, s->supplements, line + 6, 0);
1502           break;
1503         case 'S' << 16 | 'u' << 8 | 'g':
1504           s->suggests = adddep(repo, s->suggests, line + 6, 0);
1505           break;
1506         case 'E' << 16 | 'n' << 8 | 'h':
1507           s->enhances = adddep(repo, s->enhances, line + 6, 0);
1508           break;
1509         case 'I' << 16 | 'p' << 8 | 'r':
1510           {
1511             Id id = line[6] == '/' ? pool_str2id(pool, line + 6, 1) : testcase_str2dep(pool, line + 6);
1512             repodata_add_idarray(data, s - pool->solvables, SOLVABLE_PREREQ_IGNOREINST, id);
1513             break;
1514           }
1515         default:
1516           break;
1517         }
1518     }
1519   if (s)
1520     {
1521       if (tagsversion == 2)
1522         finish_v2_solvable(pool, data, s, filelist, nfilelist);
1523       if (addselfprovides && s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
1524         s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
1525     }
1526   solv_free(line);
1527   solv_free(filelist);
1528   repodata_free_dircache(data);
1529   if (!(flags & REPO_NO_INTERNALIZE))
1530     repodata_internalize(data);
1531   return 0;
1532 }
1533
1534 const char *
1535 testcase_getpoolflags(Pool *pool)
1536 {
1537   const char *str = 0;
1538   int i, v;
1539   for (i = 0; poolflags2str[i].str; i++)
1540     {
1541       v = pool_get_flag(pool, poolflags2str[i].flag);
1542       if (v == poolflags2str[i].def)
1543         continue;
1544       str = pool_tmpappend(pool, str, v ? " " : " !", poolflags2str[i].str);
1545     }
1546   return str ? str + 1 : "";
1547 }
1548
1549 int
1550 testcase_setpoolflags(Pool *pool, const char *str)
1551 {
1552   const char *p = str, *s;
1553   int i, v;
1554   for (;;)
1555     {
1556       while (*p == ' ' || *p == '\t' || *p == ',')
1557         p++;
1558       v = 1;
1559       if (*p == '!')
1560         {
1561           p++;
1562           v = 0;
1563         }
1564       if (!*p)
1565         break;
1566       s = p;
1567       while (*p && *p != ' ' && *p != '\t' && *p != ',')
1568         p++;
1569       for (i = 0; poolflags2str[i].str; i++)
1570         if (!strncmp(poolflags2str[i].str, s, p - s) && poolflags2str[i].str[p - s] == 0)
1571           break;
1572       if (!poolflags2str[i].str)
1573         return pool_error(pool, 0, "setpoolflags: unknown flag '%.*s'", (int)(p - s), s);
1574       pool_set_flag(pool, poolflags2str[i].flag, v);
1575     }
1576   return 1;
1577 }
1578
1579 void
1580 testcase_resetpoolflags(Pool *pool)
1581 {
1582   int i;
1583   for (i = 0; poolflags2str[i].str; i++)
1584     pool_set_flag(pool, poolflags2str[i].flag, poolflags2str[i].def);
1585 }
1586
1587 const char *
1588 testcase_getsolverflags(Solver *solv)
1589 {
1590   Pool *pool = solv->pool;
1591   const char *str = 0;
1592   int i, v;
1593   for (i = 0; solverflags2str[i].str; i++)
1594     {
1595       v = solver_get_flag(solv, solverflags2str[i].flag);
1596       if (v == solverflags2str[i].def)
1597         continue;
1598       str = pool_tmpappend(pool, str, v ? " " : " !", solverflags2str[i].str);
1599     }
1600   return str ? str + 1 : "";
1601 }
1602
1603 int
1604 testcase_setsolverflags(Solver *solv, const char *str)
1605 {
1606   const char *p = str, *s;
1607   int i, v;
1608   for (;;)
1609     {
1610       while (*p == ' ' || *p == '\t' || *p == ',')
1611         p++;
1612       v = 1;
1613       if (*p == '!')
1614         {
1615           p++;
1616           v = 0;
1617         }
1618       if (!*p)
1619         break;
1620       s = p;
1621       while (*p && *p != ' ' && *p != '\t' && *p != ',')
1622         p++;
1623       for (i = 0; solverflags2str[i].str; i++)
1624         if (!strncmp(solverflags2str[i].str, s, p - s) && solverflags2str[i].str[p - s] == 0)
1625           break;
1626       if (!solverflags2str[i].str)
1627         return pool_error(solv->pool, 0, "setsolverflags: unknown flag '%.*s'", (int)(p - s), s);
1628       if (solver_set_flag(solv, solverflags2str[i].flag, v) == -1)
1629         return pool_error(solv->pool, 0, "setsolverflags: unsupported flag '%s'", solverflags2str[i].str);
1630     }
1631   return 1;
1632 }
1633
1634 void
1635 testcase_resetsolverflags(Solver *solv)
1636 {
1637   int i;
1638   for (i = 0; solverflags2str[i].str; i++)
1639     solver_set_flag(solv, solverflags2str[i].flag, solverflags2str[i].def);
1640 }
1641
1642 static const char *
1643 testcase_ruleid(Solver *solv, Id rid)
1644 {
1645   Strqueue sq;
1646   Queue q;
1647   int i;
1648   Chksum *chk;
1649   const unsigned char *md5;
1650   int md5l;
1651   const char *s;
1652
1653   queue_init(&q);
1654   strqueue_init(&sq);
1655   solver_ruleliterals(solv, rid, &q);
1656   for (i = 0; i < q.count; i++)
1657     {
1658       Id p = q.elements[i];
1659       s = testcase_solvid2str(solv->pool, p > 0 ? p : -p);
1660       if (p < 0)
1661         s = pool_tmpjoin(solv->pool, "!", s, 0);
1662       strqueue_push(&sq, s);
1663     }
1664   queue_free(&q);
1665   strqueue_sort_u(&sq);
1666   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1667   for (i = 0; i < sq.nstr; i++)
1668     solv_chksum_add(chk, sq.str[i], strlen(sq.str[i]) + 1);
1669   md5 = solv_chksum_get(chk, &md5l);
1670   s = pool_bin2hex(solv->pool, md5, md5l);
1671   chk = solv_chksum_free(chk, 0);
1672   strqueue_free(&sq);
1673   return s;
1674 }
1675
1676 static const char *
1677 testcase_problemid(Solver *solv, Id problem)
1678 {
1679   Strqueue sq;
1680   Queue q;
1681   Chksum *chk;
1682   const unsigned char *md5;
1683   int i, md5l;
1684   const char *s;
1685
1686   /* we build a hash of all rules that define the problem */
1687   queue_init(&q);
1688   strqueue_init(&sq);
1689   solver_findallproblemrules(solv, problem, &q);
1690   for (i = 0; i < q.count; i++)
1691     strqueue_push(&sq, testcase_ruleid(solv, q.elements[i]));
1692   queue_free(&q);
1693   strqueue_sort_u(&sq);
1694   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1695   for (i = 0; i < sq.nstr; i++)
1696     solv_chksum_add(chk, sq.str[i], strlen(sq.str[i]) + 1);
1697   md5 = solv_chksum_get(chk, &md5l);
1698   s = pool_bin2hex(solv->pool, md5, 4);
1699   chk = solv_chksum_free(chk, 0);
1700   strqueue_free(&sq);
1701   return s;
1702 }
1703
1704 static const char *
1705 testcase_solutionid(Solver *solv, Id problem, Id solution)
1706 {
1707   Id intid;
1708   Chksum *chk;
1709   const unsigned char *md5;
1710   int md5l;
1711   const char *s;
1712
1713   intid = solver_solutionelement_internalid(solv, problem, solution);
1714   /* internal stuff! handle with care! */
1715   if (intid < 0)
1716     {
1717       /* it's a job */
1718       s = testcase_job2str(solv->pool, solv->job.elements[-intid - 1], solv->job.elements[-intid]);
1719     }
1720   else
1721     {
1722       /* it's a rule */
1723       s = testcase_ruleid(solv, intid);
1724     }
1725   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1726   solv_chksum_add(chk, s, strlen(s) + 1);
1727   md5 = solv_chksum_get(chk, &md5l);
1728   s = pool_bin2hex(solv->pool, md5, 4);
1729   chk = solv_chksum_free(chk, 0);
1730   return s;
1731 }
1732
1733 static const char *
1734 testcase_alternativeid(Solver *solv, int type, Id id, Id from)
1735 {
1736   const char *s;
1737   Pool *pool = solv->pool;
1738   Chksum *chk;
1739   const unsigned char *md5;
1740   int md5l;
1741   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1742   if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
1743     {
1744       s = testcase_solvid2str(pool, from);
1745       solv_chksum_add(chk, s, strlen(s) + 1);
1746       s = testcase_dep2str(pool, id);
1747       solv_chksum_add(chk, s, strlen(s) + 1);
1748     }
1749   else if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
1750     {
1751       s = testcase_ruleid(solv, id);
1752       solv_chksum_add(chk, s, strlen(s) + 1);
1753     }
1754   md5 = solv_chksum_get(chk, &md5l);
1755   s = pool_bin2hex(pool, md5, 4);
1756   chk = solv_chksum_free(chk, 0);
1757   return s;
1758 }
1759
1760 static struct class2str {
1761   Id class;
1762   const char *str;
1763 } class2str[] = {
1764   { SOLVER_TRANSACTION_ERASE,          "erase" },
1765   { SOLVER_TRANSACTION_INSTALL,        "install" },
1766   { SOLVER_TRANSACTION_REINSTALLED,    "reinstall" },
1767   { SOLVER_TRANSACTION_DOWNGRADED,     "downgrade" },
1768   { SOLVER_TRANSACTION_CHANGED,        "change" },
1769   { SOLVER_TRANSACTION_UPGRADED,       "upgrade" },
1770   { SOLVER_TRANSACTION_OBSOLETED,      "obsolete" },
1771   { SOLVER_TRANSACTION_MULTIINSTALL,   "multiinstall" },
1772   { SOLVER_TRANSACTION_MULTIREINSTALL, "multireinstall" },
1773   { 0, 0 }
1774 };
1775
1776 static struct reason2str {
1777   Id reason;
1778   const char *str;
1779 } reason2str[] = {
1780   { SOLVER_REASON_UNRELATED,            "unrelated" },
1781   { SOLVER_REASON_UNIT_RULE,            "unit" },
1782   { SOLVER_REASON_KEEP_INSTALLED,       "keep" },
1783   { SOLVER_REASON_RESOLVE_JOB,          "job" },
1784   { SOLVER_REASON_UPDATE_INSTALLED,     "update" },
1785   { SOLVER_REASON_CLEANDEPS_ERASE,      "cleandeps" },
1786   { SOLVER_REASON_RESOLVE,              "resolve" },
1787   { SOLVER_REASON_WEAKDEP,              "weakdep" },
1788   { SOLVER_REASON_RESOLVE_ORPHAN,       "orphan" },
1789
1790   { SOLVER_REASON_RECOMMENDED,          "recommended" },
1791   { SOLVER_REASON_SUPPLEMENTED,         "supplemented" },
1792   { 0, 0 }
1793 };
1794
1795 static const char *
1796 testcase_reason2str(Id reason)
1797 {
1798   int i;
1799   for (i = 0; reason2str[i].str; i++)
1800     if (reason == reason2str[i].reason)
1801       return reason2str[i].str;
1802   return "?";
1803 }
1804
1805 static struct rclass2str {
1806   Id rclass;
1807   const char *str;
1808 } rclass2str[] = {
1809   { SOLVER_RULE_PKG, "pkg" },
1810   { SOLVER_RULE_UPDATE, "update" },
1811   { SOLVER_RULE_FEATURE, "feature" },
1812   { SOLVER_RULE_JOB, "job" },
1813   { SOLVER_RULE_DISTUPGRADE, "distupgrade" },
1814   { SOLVER_RULE_INFARCH, "infarch" },
1815   { SOLVER_RULE_CHOICE, "choice" },
1816   { SOLVER_RULE_LEARNT, "learnt" },
1817   { SOLVER_RULE_BEST, "best" },
1818   { SOLVER_RULE_YUMOBS, "yumobs" },
1819   { 0, 0 }
1820 };
1821
1822 static const char *
1823 testcase_rclass2str(Id rclass)
1824 {
1825   int i;
1826   for (i = 0; rclass2str[i].str; i++)
1827     if (rclass == rclass2str[i].rclass)
1828       return rclass2str[i].str;
1829   return "unknown";
1830 }
1831
1832 static int
1833 dump_genid(Pool *pool, Strqueue *sq, Id id, int cnt)
1834 {
1835   struct oplist *op;
1836   char cntbuf[20];
1837   const char *s;
1838
1839   if (ISRELDEP(id))
1840     {
1841       Reldep *rd = GETRELDEP(pool, id);
1842       for (op = oplist; op->flags; op++)
1843         if (rd->flags == op->flags)
1844           break;
1845       cnt = dump_genid(pool, sq, rd->name, cnt);
1846       cnt = dump_genid(pool, sq, rd->evr, cnt);
1847       sprintf(cntbuf, "genid %2d: genid ", cnt++);
1848       s = pool_tmpjoin(pool, cntbuf, "op ", op->flags ? op->opname : "unknown");
1849     }
1850   else
1851     {
1852       sprintf(cntbuf, "genid %2d: genid ", cnt++);
1853       s = pool_tmpjoin(pool, cntbuf, id ? "lit " : "null", id ? pool_id2str(pool, id) : 0);
1854     }
1855   strqueue_push(sq, s);
1856   return cnt;
1857 }
1858
1859 char *
1860 testcase_solverresult(Solver *solv, int resultflags)
1861 {
1862   Pool *pool = solv->pool;
1863   int i, j;
1864   Id p, op;
1865   const char *s;
1866   char *result;
1867   Strqueue sq;
1868
1869   strqueue_init(&sq);
1870   if ((resultflags & TESTCASE_RESULT_TRANSACTION) != 0)
1871     {
1872       Transaction *trans = solver_create_transaction(solv);
1873       Queue q;
1874
1875       queue_init(&q);
1876       for (i = 0; class2str[i].str; i++)
1877         {
1878           queue_empty(&q);
1879           transaction_classify_pkgs(trans, SOLVER_TRANSACTION_KEEP_PSEUDO, class2str[i].class, 0, 0, &q);
1880           for (j = 0; j < q.count; j++)
1881             {
1882               p = q.elements[j];
1883               op = 0;
1884               if (pool->installed && pool->solvables[p].repo == pool->installed)
1885                 op = transaction_obs_pkg(trans, p);
1886               s = pool_tmpjoin(pool, class2str[i].str, " ", testcase_solvid2str(pool, p));
1887               if (op)
1888                 s = pool_tmpjoin(pool, s, " ", testcase_solvid2str(pool, op));
1889               strqueue_push(&sq, s);
1890             }
1891         }
1892       queue_free(&q);
1893       transaction_free(trans);
1894     }
1895   if ((resultflags & TESTCASE_RESULT_PROBLEMS) != 0)
1896     {
1897       char *probprefix, *solprefix;
1898       int problem, solution, element;
1899       int pcnt, scnt;
1900
1901       pcnt = solver_problem_count(solv);
1902       for (problem = 1; problem <= pcnt; problem++)
1903         {
1904           Id rid, from, to, dep;
1905           SolverRuleinfo rinfo;
1906           rid = solver_findproblemrule(solv, problem);
1907           s = testcase_problemid(solv, problem);
1908           probprefix = solv_dupjoin("problem ", s, 0);
1909           rinfo = solver_ruleinfo(solv, rid, &from, &to, &dep);
1910           s = pool_tmpjoin(pool, probprefix, " info ", solver_problemruleinfo2str(solv, rinfo, from, to, dep));
1911           strqueue_push(&sq, s);
1912           scnt = solver_solution_count(solv, problem);
1913           for (solution = 1; solution <= scnt; solution++)
1914             {
1915               s = testcase_solutionid(solv, problem, solution);
1916               solprefix = solv_dupjoin(probprefix, " solution ", s);
1917               element = 0;
1918               while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &op)) != 0)
1919                 {
1920                   if (p == SOLVER_SOLUTION_JOB)
1921                     s = pool_tmpjoin(pool, solprefix, " deljob ", testcase_job2str(pool, solv->job.elements[op - 1], solv->job.elements[op]));
1922                   else if (p > 0 && op == 0)
1923                     s = pool_tmpjoin(pool, solprefix, " erase ", testcase_solvid2str(pool, p));
1924                   else if (p > 0 && op > 0)
1925                     {
1926                       s = pool_tmpjoin(pool, solprefix, " replace ", testcase_solvid2str(pool, p));
1927                       s = pool_tmpappend(pool, s, " ", testcase_solvid2str(pool, op));
1928                     }
1929                   else if (p < 0 && op > 0)
1930                     s = pool_tmpjoin(pool, solprefix, " allow ", testcase_solvid2str(pool, op));
1931                   else
1932                     s = pool_tmpjoin(pool, solprefix, " unknown", 0);
1933                   strqueue_push(&sq, s);
1934                 }
1935               solv_free(solprefix);
1936             }
1937           solv_free(probprefix);
1938         }
1939     }
1940
1941   if ((resultflags & TESTCASE_RESULT_ORPHANED) != 0)
1942     {
1943       Queue q;
1944
1945       queue_init(&q);
1946       solver_get_orphaned(solv, &q);
1947       for (i = 0; i < q.count; i++)
1948         {
1949           s = pool_tmpjoin(pool, "orphaned ", testcase_solvid2str(pool, q.elements[i]), 0);
1950           strqueue_push(&sq, s);
1951         }
1952       queue_free(&q);
1953     }
1954
1955   if ((resultflags & TESTCASE_RESULT_RECOMMENDED) != 0)
1956     {
1957       Queue qr, qs;
1958
1959       queue_init(&qr);
1960       queue_init(&qs);
1961       solver_get_recommendations(solv, &qr, &qs, 0);
1962       for (i = 0; i < qr.count; i++)
1963         {
1964           s = pool_tmpjoin(pool, "recommended ", testcase_solvid2str(pool, qr.elements[i]), 0);
1965           strqueue_push(&sq, s);
1966         }
1967       for (i = 0; i < qs.count; i++)
1968         {
1969           s = pool_tmpjoin(pool, "suggested ", testcase_solvid2str(pool, qs.elements[i]), 0);
1970           strqueue_push(&sq, s);
1971         }
1972       queue_free(&qr);
1973       queue_free(&qs);
1974     }
1975
1976   if ((resultflags & TESTCASE_RESULT_UNNEEDED) != 0)
1977     {
1978       Queue q, qf;
1979
1980       queue_init(&q);
1981       queue_init(&qf);
1982       solver_get_unneeded(solv, &q, 0);
1983       solver_get_unneeded(solv, &qf, 1);
1984       for (i = j = 0; i < q.count; i++)
1985         {
1986           /* we rely on qf containing a subset of q in the same order */
1987           if (j < qf.count && q.elements[i] == qf.elements[j])
1988             {
1989               s = pool_tmpjoin(pool, "unneeded_filtered ", testcase_solvid2str(pool, q.elements[i]), 0);
1990               j++;
1991             }
1992           else
1993             s = pool_tmpjoin(pool, "unneeded ", testcase_solvid2str(pool, q.elements[i]), 0);
1994           strqueue_push(&sq, s);
1995         }
1996       queue_free(&q);
1997       queue_free(&qf);
1998     }
1999   if ((resultflags & TESTCASE_RESULT_ALTERNATIVES) != 0)
2000     {
2001       char *altprefix;
2002       Queue q, rq;
2003       int cnt;
2004       Id alternative;
2005       queue_init(&q);
2006       queue_init(&rq);
2007       cnt = solver_alternatives_count(solv);
2008       for (alternative = 1; alternative <= cnt; alternative++)
2009         {
2010           Id id, from, chosen;
2011           char num[20];
2012           int type = solver_get_alternative(solv, alternative, &id, &from, &chosen, &q, 0);
2013           altprefix = solv_dupjoin("alternative ", testcase_alternativeid(solv, type, id, from), " ");
2014           strcpy(num, " 0 ");
2015           if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
2016             {
2017               char *s = pool_tmpjoin(pool, altprefix, num, testcase_solvid2str(pool, from));
2018               s = pool_tmpappend(pool, s, " recommends ", testcase_dep2str(pool, id));
2019               strqueue_push(&sq, s);
2020             }
2021           else if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
2022             {
2023               /* map choice rules back to pkg rules */
2024               if (solver_ruleclass(solv, id) == SOLVER_RULE_CHOICE)
2025                 id = solver_rule2pkgrule(solv, id);
2026               solver_allruleinfos(solv, id, &rq);
2027               for (i = 0; i < rq.count; i += 4)
2028                 {
2029                   int rtype = rq.elements[i];
2030                   if ((rtype & SOLVER_RULE_TYPEMASK) == SOLVER_RULE_JOB)
2031                     {
2032                       const char *js = testcase_job2str(pool, rq.elements[i + 2], rq.elements[i + 3]);
2033                       char *s = pool_tmpjoin(pool, altprefix, num, " job ");
2034                       s = pool_tmpappend(pool, s, js, 0);
2035                       strqueue_push(&sq, s);
2036                     }
2037                   else if (rtype == SOLVER_RULE_PKG_REQUIRES)
2038                     {
2039                       char *s = pool_tmpjoin(pool, altprefix, num, testcase_solvid2str(pool, rq.elements[i + 1]));
2040                       s = pool_tmpappend(pool, s, " requires ", testcase_dep2str(pool, rq.elements[i + 3]));
2041                       strqueue_push(&sq, s);
2042                     }
2043                 }
2044             }
2045           for (i = 0; i < q.count; i++)
2046             {
2047               Id p = q.elements[i];
2048               if (i >= 9)
2049                 num[0] = '0' + (i + 1) / 10;
2050               num[1] = '0' + (i + 1) % 10;
2051               if (-p == chosen)
2052                 s = pool_tmpjoin(pool, altprefix, num, "+ ");
2053               else if (p < 0)
2054                 s = pool_tmpjoin(pool, altprefix, num, "- ");
2055               else if (p >= 0)
2056                 s = pool_tmpjoin(pool, altprefix, num, "  ");
2057               s = pool_tmpappend(pool, s,  testcase_solvid2str(pool, p < 0 ? -p : p), 0);
2058               strqueue_push(&sq, s);
2059             }
2060           solv_free(altprefix);
2061         }
2062       queue_free(&q);
2063       queue_free(&rq);
2064     }
2065   if ((resultflags & TESTCASE_RESULT_RULES) != 0)
2066     {
2067       /* dump all rules */
2068       Id rid;
2069       SolverRuleinfo rclass;
2070       Queue q;
2071       int i;
2072
2073       queue_init(&q);
2074       for (rid = 1; (rclass = solver_ruleclass(solv, rid)) != SOLVER_RULE_UNKNOWN; rid++)
2075         {
2076           char *prefix = solv_dupjoin("rule ", testcase_rclass2str(rclass), " ");
2077           prefix = solv_dupappend(prefix, testcase_ruleid(solv, rid), 0);
2078           solver_ruleliterals(solv, rid, &q);
2079           if (rclass == SOLVER_RULE_FEATURE && q.count == 1 && q.elements[0] == -SYSTEMSOLVABLE)
2080             continue;
2081           for (i = 0; i < q.count; i++)
2082             {
2083               Id p = q.elements[i];
2084               const char *s;
2085               if (p < 0)
2086                 s = pool_tmpjoin(pool, prefix, " -", testcase_solvid2str(pool, -p));
2087               else
2088                 s = pool_tmpjoin(pool, prefix, "  ", testcase_solvid2str(pool, p));
2089               strqueue_push(&sq, s);
2090             }
2091           solv_free(prefix);
2092         }
2093       queue_free(&q);
2094     }
2095   if ((resultflags & TESTCASE_RESULT_GENID) != 0)
2096     {
2097       for (i = 0 ; i < solv->job.count; i += 2)
2098         {
2099           Id id, id2;
2100           if (solv->job.elements[i] != (SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES))
2101             continue;
2102           id = solv->job.elements[i + 1];
2103           s = testcase_dep2str(pool, id);
2104           strqueue_push(&sq, pool_tmpjoin(pool, "genid dep ", s, 0));
2105           if ((id2 = testcase_str2dep(pool, s)) != id)
2106             {
2107               s = pool_tmpjoin(pool, "genid roundtrip error: ", testcase_dep2str(pool, id2), 0);
2108               strqueue_push(&sq, s);
2109             }
2110           dump_genid(pool, &sq, id, 1);
2111         }
2112     }
2113   if ((resultflags & TESTCASE_RESULT_REASON) != 0)
2114     {
2115       Queue whyq;
2116       queue_init(&whyq);
2117       FOR_POOL_SOLVABLES(p)
2118         {
2119           Id info, p2, id;
2120           int reason = solver_describe_decision(solv, p, &info);
2121           if (reason == SOLVER_REASON_UNRELATED)
2122             continue;
2123           if (reason == SOLVER_REASON_WEAKDEP)
2124             {
2125               solver_describe_weakdep_decision(solv, p, &whyq);
2126               if (whyq.count)
2127                 {
2128                   for (i = 0; i < whyq.count; i += 3)
2129                     {
2130                       reason = whyq.elements[i];
2131                       p2 = whyq.elements[i + 1];
2132                       id = whyq.elements[i + 2];
2133                       s = pool_tmpjoin(pool, "reason ", testcase_solvid2str(pool, p), 0);
2134                       s = pool_tmpappend(pool, s, " ", testcase_reason2str(reason));
2135                       s = pool_tmpappend(pool, s, " ", testcase_dep2str(pool, id));
2136                       if (p2)
2137                         s = pool_tmpappend(pool, s, " ", testcase_solvid2str(pool, p2));
2138                       strqueue_push(&sq, s);
2139                     }
2140                   continue;
2141                 }
2142             }
2143           s = pool_tmpjoin(pool, "reason ", testcase_solvid2str(pool, p), 0);
2144           s = pool_tmpappend(pool, s, " ", testcase_reason2str(reason));
2145           if (info)
2146             s = pool_tmpappend(pool, s, " ", testcase_ruleid(solv, info));
2147           strqueue_push(&sq, s);
2148         }
2149       queue_free(&whyq);
2150     }
2151   if ((resultflags & TESTCASE_RESULT_CLEANDEPS) != 0)
2152     {
2153       Queue q;
2154
2155       queue_init(&q);
2156       solver_get_cleandeps(solv, &q);
2157       for (i = 0; i < q.count; i++)
2158         {
2159           s = pool_tmpjoin(pool, "cleandeps ", testcase_solvid2str(pool, q.elements[i]), 0);
2160           strqueue_push(&sq, s);
2161         }
2162       queue_free(&q);
2163     }
2164   if ((resultflags & TESTCASE_RESULT_JOBS) != 0)
2165     {
2166       for (i = 0; i < solv->job.count; i += 2)
2167         {
2168           s = (char *)testcase_job2str(pool, solv->job.elements[i], solv->job.elements[i + 1]);
2169           s = pool_tmpjoin(pool, "job ", s, 0);
2170           strqueue_push(&sq, s);
2171         }
2172     }
2173   strqueue_sort(&sq);
2174   result = strqueue_join(&sq);
2175   strqueue_free(&sq);
2176   return result;
2177 }
2178
2179
2180 static int
2181 testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const char *testcasename, const char *resultname)
2182 {
2183   Pool *pool = solv->pool;
2184   Repo *repo;
2185   int i;
2186   Id arch, repoid;
2187   Id lowscore;
2188   FILE *fp;
2189   Strqueue sq;
2190   char *cmd, *out;
2191   const char *s;
2192
2193   if (!testcasename)
2194     testcasename = "testcase.t";
2195   if (!resultname)
2196     resultname = "solver.result";
2197
2198   if (mkdir(dir, 0777) && errno != EEXIST)
2199     return pool_error(solv->pool, 0, "testcase_write: could not create directory '%s'", dir);
2200   strqueue_init(&sq);
2201   FOR_REPOS(repoid, repo)
2202     {
2203       const char *name = testcase_repoid2str(pool, repoid);
2204       char priobuf[50];
2205       if (repo->subpriority)
2206         sprintf(priobuf, "%d.%d", repo->priority, repo->subpriority);
2207       else
2208         sprintf(priobuf, "%d", repo->priority);
2209       out = pool_tmpjoin(pool, name, ".repo", ".gz");
2210       for (i = 0; out[i]; i++)
2211         if (out[i] == '/')
2212           out[i] = '_';
2213       cmd = pool_tmpjoin(pool, "repo ", name, " ");
2214       cmd = pool_tmpappend(pool, cmd, priobuf, " ");
2215       cmd = pool_tmpappend(pool, cmd, "testtags ", out);
2216       strqueue_push(&sq, cmd);
2217       out = pool_tmpjoin(pool, dir, "/", out);
2218       if (!(fp = solv_xfopen(out, "w")))
2219         {
2220           pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out);
2221           strqueue_free(&sq);
2222           return 0;
2223         }
2224       testcase_write_testtags(repo, fp);
2225       if (fclose(fp))
2226         {
2227           pool_error(solv->pool, 0, "testcase_write: write error");
2228           strqueue_free(&sq);
2229           return 0;
2230         }
2231     }
2232   /* hmm, this is not optimal... we currently search for the lowest score */
2233   lowscore = 0;
2234   arch = pool->solvables[SYSTEMSOLVABLE].arch;
2235   for (i = 0; i < pool->lastarch; i++)
2236     {
2237       if (pool->id2arch[i] == 1 && !lowscore)
2238         arch = i;
2239       if (pool->id2arch[i] > 0x10000 && (!lowscore || pool->id2arch[i] < lowscore))
2240         {
2241           arch = i;
2242           lowscore = pool->id2arch[i];
2243         }
2244     }
2245   cmd = pool_tmpjoin(pool, "system ", pool->lastarch ? pool_id2str(pool, arch) : "unset", 0);
2246   for (i = 0; disttype2str[i].str != 0; i++)
2247     if (pool->disttype == disttype2str[i].type)
2248       break;
2249   pool_tmpappend(pool, cmd, " ", disttype2str[i].str ? disttype2str[i].str : "unknown");
2250   if (pool->installed)
2251     cmd = pool_tmpappend(pool, cmd, " ", testcase_repoid2str(pool, pool->installed->repoid));
2252   strqueue_push(&sq, cmd);
2253   s = testcase_getpoolflags(solv->pool);
2254   if (*s)
2255     {
2256       cmd = pool_tmpjoin(pool, "poolflags ", s, 0);
2257       strqueue_push(&sq, cmd);
2258     }
2259
2260   if (pool->vendorclasses)
2261     {
2262       cmd = 0;
2263       for (i = 0; pool->vendorclasses[i]; i++)
2264         {
2265           cmd = pool_tmpappend(pool, cmd ? cmd : "vendorclass", " ", pool->vendorclasses[i]);
2266           if (!pool->vendorclasses[i + 1])
2267             {
2268               strqueue_push(&sq, cmd);
2269               cmd = 0;
2270               i++;
2271             }
2272         }
2273     }
2274
2275   /* dump disabled packages (must come before the namespace/job lines) */
2276   if (pool->considered)
2277     {
2278       Id p;
2279       FOR_POOL_SOLVABLES(p)
2280         if (!MAPTST(pool->considered, p))
2281           {
2282             cmd = pool_tmpjoin(pool, "disable pkg ", testcase_solvid2str(pool, p), 0);
2283             strqueue_push(&sq, cmd);
2284           }
2285     }
2286
2287   s = testcase_getsolverflags(solv);
2288   if (*s)
2289     {
2290       cmd = pool_tmpjoin(pool, "solverflags ", s, 0);
2291       strqueue_push(&sq, cmd);
2292     }
2293
2294   /* now dump all the ns callback values we know */
2295   if (pool->nscallback)
2296     {
2297       Id rid;
2298       int d;
2299       for (rid = 1; rid < pool->nrels; rid++)
2300         {
2301           Reldep *rd = pool->rels + rid;
2302           if (rd->flags != REL_NAMESPACE || rd->name == NAMESPACE_OTHERPROVIDERS)
2303             continue;
2304           /* evaluate all namespace ids, skip empty results */
2305           d = pool_whatprovides(pool, MAKERELDEP(rid));
2306           if (!d || !pool->whatprovidesdata[d])
2307             continue;
2308           cmd = pool_tmpjoin(pool, "namespace ", pool_id2str(pool, rd->name), "(");
2309           cmd = pool_tmpappend(pool, cmd, pool_id2str(pool, rd->evr), ")");
2310           for (;  pool->whatprovidesdata[d]; d++)
2311             cmd = pool_tmpappend(pool, cmd, " ", testcase_solvid2str(pool, pool->whatprovidesdata[d]));
2312           strqueue_push(&sq, cmd);
2313         }
2314     }
2315
2316   for (i = 0; i < solv->job.count; i += 2)
2317     {
2318       cmd = (char *)testcase_job2str(pool, solv->job.elements[i], solv->job.elements[i + 1]);
2319       cmd = pool_tmpjoin(pool, "job ", cmd, 0);
2320       strqueue_push(&sq, cmd);
2321     }
2322
2323   if ((resultflags & ~TESTCASE_RESULT_REUSE_SOLVER) != 0)
2324     {
2325       char *result;
2326       cmd = 0;
2327       for (i = 0; resultflags2str[i].str; i++)
2328         if ((resultflags & resultflags2str[i].flag) != 0)
2329           cmd = pool_tmpappend(pool, cmd, cmd ? "," : 0, resultflags2str[i].str);
2330       cmd = pool_tmpjoin(pool, "result ", cmd ? cmd : "?", 0);
2331       cmd = pool_tmpappend(pool, cmd, " ", resultname);
2332       strqueue_push(&sq, cmd);
2333       result = testcase_solverresult(solv, resultflags);
2334       if (!strcmp(resultname, "<inline>"))
2335         {
2336           int i;
2337           Strqueue rsq;
2338           strqueue_init(&rsq);
2339           strqueue_split(&rsq, result);
2340           for (i = 0; i < rsq.nstr; i++)
2341             {
2342               cmd = pool_tmpjoin(pool, "#>", rsq.str[i], 0);
2343               strqueue_push(&sq, cmd);
2344             }
2345           strqueue_free(&rsq);
2346         }
2347       else
2348         {
2349           out = pool_tmpjoin(pool, dir, "/", resultname);
2350           if (!(fp = fopen(out, "w")))
2351             {
2352               pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out);
2353               solv_free(result);
2354               strqueue_free(&sq);
2355               return 0;
2356             }
2357           if (result && *result && fwrite(result, strlen(result), 1, fp) != 1)
2358             {
2359               pool_error(solv->pool, 0, "testcase_write: write error");
2360               solv_free(result);
2361               strqueue_free(&sq);
2362               fclose(fp);
2363               return 0;
2364             }
2365           if (fclose(fp))
2366             {
2367               pool_error(solv->pool, 0, "testcase_write: write error");
2368               strqueue_free(&sq);
2369               return 0;
2370             }
2371         }
2372       solv_free(result);
2373     }
2374
2375   cmd = strqueue_join(&sq);
2376   out = pool_tmpjoin(pool, dir, "/", testcasename);
2377   if (!(fp = fopen(out, "w")))
2378     {
2379       pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out);
2380       strqueue_free(&sq);
2381       return 0;
2382     }
2383   if (*cmd && fwrite(cmd, strlen(cmd), 1, fp) != 1)
2384     {
2385       pool_error(solv->pool, 0, "testcase_write: write error");
2386       strqueue_free(&sq);
2387       fclose(fp);
2388       return 0;
2389     }
2390   if (fclose(fp))
2391     {
2392       pool_error(solv->pool, 0, "testcase_write: write error");
2393       strqueue_free(&sq);
2394       return 0;
2395     }
2396   solv_free(cmd);
2397   strqueue_free(&sq);
2398   return 1;
2399 }
2400
2401 int
2402 testcase_write(Solver *solv, const char *dir, int resultflags, const char *testcasename, const char *resultname)
2403 {
2404   Pool *pool = solv->pool;
2405   int i, r, repoid;
2406   int mangle = 1;
2407   const char **orignames;
2408
2409   /* mangle repo names so that there are no conflicts */
2410   orignames = solv_calloc(pool->nrepos, sizeof(char *));
2411   for (repoid = 1; repoid < pool->nrepos; repoid++)
2412     {
2413       Repo *repo = pool_id2repo(pool, repoid);
2414       char *buf = solv_malloc((repo->name ? strlen(repo->name) : 0) + 40);
2415       char *mp;
2416       orignames[repoid] = repo->name;
2417       if (!repo->name || !repo->name[0])
2418         sprintf(buf, "#%d", repoid);
2419       else
2420         strcpy(buf, repo->name);
2421       for (i = 0; buf[i]; i++)
2422         if (buf[i] == ' ' || buf[i] == '\t' || buf[i] == '/')
2423           buf[i] = '_';
2424       mp = buf + strlen(buf);
2425       for (;;)
2426         {
2427           for (i = 1; i < repoid; i++)
2428             if (!strcmp(buf, pool_id2repo(pool, i)->name))
2429               break;
2430           if (i == repoid)
2431             break;
2432           sprintf(mp, "_%d", mangle++);
2433         }
2434       repo->name = buf;
2435     }
2436   r = testcase_write_mangled(solv, dir, resultflags, testcasename, resultname);
2437   for (repoid = 1; repoid < pool->nrepos; repoid++)
2438     {
2439       Repo *repo = pool_id2repo(pool, repoid);
2440       solv_free((void *)repo->name);
2441       repo->name = orignames[repoid];
2442     }
2443   solv_free(orignames);
2444   return r;
2445 }
2446
2447 static char *
2448 read_inline_file(FILE *fp, char **bufp, char **bufpp, int *buflp)
2449 {
2450   char *result = solv_malloc(1024);
2451   char *rp = result;
2452   int resultl = 1024;
2453
2454   for (;;)
2455     {
2456       size_t rl;
2457       if (rp - result + 256 >= resultl)
2458         {
2459           resultl = rp - result;
2460           result = solv_realloc(result, resultl + 1024);
2461           rp = result + resultl;
2462           resultl += 1024;
2463         }
2464       if (!fgets(rp, resultl - (rp - result), fp))
2465         *rp = 0;
2466       rl = strlen(rp);
2467       if (rl && (rp == result || rp[-1] == '\n'))
2468         {
2469           if (rl > 1 && rp[0] == '#' && rp[1] == '>')
2470             {
2471               memmove(rp, rp + 2, rl - 2);
2472               rl -= 2;
2473             }
2474           else
2475             {
2476               while (rl + 16 > *buflp)
2477                 {
2478                   *bufp = solv_realloc(*bufp, *buflp + 512);
2479                   *buflp += 512;
2480                 }
2481               memmove(*bufp, rp, rl);
2482               if ((*bufp)[rl - 1] == '\n')
2483                 {
2484                   ungetc('\n', fp);
2485                   rl--;
2486                 }
2487               (*bufp)[rl] = 0;
2488               (*bufpp) = *bufp + rl;
2489               rl = 0;
2490             }
2491         }
2492       if (rl <= 0)
2493         {
2494           *rp = 0;
2495           break;
2496         }
2497       rp += rl;
2498     }
2499   return result;
2500 }
2501
2502 static char *
2503 read_file(FILE *fp)
2504 {
2505   char *result = solv_malloc(1024);
2506   char *rp = result;
2507   int resultl = 1024;
2508
2509   for (;;)
2510     {
2511       size_t rl;
2512       if (rp - result + 256 >= resultl)
2513         {
2514           resultl = rp - result;
2515           result = solv_realloc(result, resultl + 1024);
2516           rp = result + resultl;
2517           resultl += 1024;
2518         }
2519       rl = fread(rp, 1, resultl - (rp - result), fp);
2520       if (rl <= 0)
2521         {
2522           *rp = 0;
2523           break;
2524         }
2525       rp += rl;
2526     }
2527   return result;
2528 }
2529
2530 static int
2531 str2resultflags(Pool *pool, char *s)    /* modifies the string! */
2532 {
2533   int i, resultflags = 0;
2534   while (s)
2535     {
2536       char *se = strchr(s, ',');
2537       if (se)
2538         *se++ = 0;
2539       for (i = 0; resultflags2str[i].str; i++)
2540         if (!strcmp(s, resultflags2str[i].str))
2541           {
2542             resultflags |= resultflags2str[i].flag;
2543             break;
2544           }
2545       if (!resultflags2str[i].str)
2546         pool_error(pool, 0, "result: unknown flag '%s'", s);
2547       s = se;
2548     }
2549   return resultflags;
2550 }
2551
2552 Solver *
2553 testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **resultp, int *resultflagsp)
2554 {
2555   Solver *solv;
2556   char *buf, *bufp;
2557   int bufl;
2558   char *testcasedir, *s;
2559   int l;
2560   char **pieces = 0;
2561   int npieces = 0;
2562   int prepared = 0;
2563   int closefp = !fp;
2564   int poolflagsreset = 0;
2565   int missing_features = 0;
2566   Id *genid = 0;
2567   int ngenid = 0;
2568   Queue autoinstq;
2569
2570   if (resultp)
2571     *resultp = 0;
2572   if (resultflagsp)
2573     *resultflagsp = 0;
2574   if (!fp && !(fp = fopen(testcase, "r")))
2575     {
2576       pool_error(pool, 0, "testcase_read: could not open '%s'", testcase);
2577       return 0;
2578     }
2579   testcasedir = solv_strdup(testcase);
2580   if ((s = strrchr(testcasedir, '/')) != 0)
2581     s[1] = 0;
2582   else
2583     *testcasedir = 0;
2584   bufl = 1024;
2585   buf = solv_malloc(bufl);
2586   bufp = buf;
2587   solv = 0;
2588   queue_init(&autoinstq);
2589   for (;;)
2590     {
2591       if (bufp - buf + 16 > bufl)
2592         {
2593           bufl = bufp - buf;
2594           buf = solv_realloc(buf, bufl + 512);
2595           bufp = buf + bufl;
2596           bufl += 512;
2597         }
2598       if (!fgets(bufp, bufl - (bufp - buf), fp))
2599         break;
2600       bufp = buf;
2601       l = strlen(buf);
2602       if (!l || buf[l - 1] != '\n')
2603         {
2604           bufp += l;
2605           continue;
2606         }
2607       buf[--l] = 0;
2608       s = buf;
2609       while (*s && (*s == ' ' || *s == '\t'))
2610         s++;
2611       if (!*s || *s == '#')
2612         continue;
2613       npieces = 0;
2614       /* split it in pieces */
2615       for (;;)
2616         {
2617           while (*s == ' ' || *s == '\t')
2618             s++;
2619           if (!*s)
2620             break;
2621           pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
2622           pieces[npieces++] = s;
2623           while (*s && *s != ' ' && *s != '\t')
2624             s++;
2625           if (*s)
2626             *s++ = 0;
2627         }
2628       pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
2629       pieces[npieces] = 0;
2630       if (!strcmp(pieces[0], "repo") && npieces >= 4)
2631         {
2632           Repo *repo = repo_create(pool, pieces[1]);
2633           FILE *rfp;
2634           int prio, subprio;
2635           const char *rdata;
2636
2637           prepared = 0;
2638           if (!poolflagsreset)
2639             {
2640               poolflagsreset = 1;
2641               testcase_resetpoolflags(pool);    /* hmm */
2642             }
2643           if (sscanf(pieces[2], "%d.%d", &prio, &subprio) != 2)
2644             {
2645               subprio = 0;
2646               prio = atoi(pieces[2]);
2647             }
2648           repo->priority = prio;
2649           repo->subpriority = subprio;
2650           if (strcmp(pieces[3], "empty") != 0)
2651             {
2652               const char *repotype = pool_tmpjoin(pool, pieces[3], 0, 0);       /* gets overwritten in <inline> case */
2653               if (!strcmp(pieces[4], "<inline>"))
2654                 {
2655                   char *idata = read_inline_file(fp, &buf, &bufp, &bufl);
2656                   rdata = "<inline>";
2657                   rfp = solv_xfopen_buf(rdata, &idata, 0, "rf");
2658                 }
2659               else
2660                 {
2661                   rdata = pool_tmpjoin(pool, testcasedir, pieces[4], 0);
2662                   rfp = solv_xfopen(rdata, "r");
2663                 }
2664               if (!rfp)
2665                 {
2666                   pool_error(pool, 0, "testcase_read: could not open '%s'", rdata);
2667                 }
2668               else if (!strcmp(repotype, "testtags"))
2669                 {
2670                   testcase_add_testtags(repo, rfp, 0);
2671                   fclose(rfp);
2672                 }
2673               else if (!strcmp(repotype, "solv"))
2674                 {
2675                   repo_add_solv(repo, rfp, 0);
2676                   fclose(rfp);
2677                 }
2678 #if ENABLE_TESTCASE_HELIXREPO
2679               else if (!strcmp(repotype, "helix"))
2680                 {
2681                   repo_add_helix(repo, rfp, 0);
2682                   fclose(rfp);
2683                 }
2684 #endif
2685               else
2686                 {
2687                   fclose(rfp);
2688                   pool_error(pool, 0, "testcase_read: unknown repo type for repo '%s'", repo->name);
2689                 }
2690             }
2691         }
2692       else if (!strcmp(pieces[0], "system") && npieces >= 3)
2693         {
2694           int i;
2695
2696           /* must set the disttype before the arch */
2697           prepared = 0;
2698           if (strcmp(pieces[2], "*") != 0)
2699             {
2700               char *dp = pieces[2];
2701               while (dp && *dp)
2702                 {
2703                   char *dpe = strchr(dp, ',');
2704                   if (dpe)
2705                     *dpe = 0;
2706                   for (i = 0; disttype2str[i].str != 0; i++)
2707                     if (!strcmp(disttype2str[i].str, dp))
2708                       break;
2709                   if (dpe)
2710                     *dpe++ = ',';
2711                   if (disttype2str[i].str)
2712                     {
2713 #ifdef MULTI_SEMANTICS
2714                       if (pool->disttype != disttype2str[i].type)
2715                         pool_setdisttype(pool, disttype2str[i].type);
2716 #endif
2717                       if (pool->disttype == disttype2str[i].type)
2718                         break;
2719                     }
2720                   dp = dpe;
2721                 }
2722               if (!(dp && *dp))
2723                 {
2724                   pool_error(pool, 0, "testcase_read: system: could not change disttype to '%s'", pieces[2]);
2725                   missing_features = 1;
2726                 }
2727             }
2728           if (strcmp(pieces[1], "unset") == 0)
2729             pool_setarch(pool, 0);
2730           else if (pieces[1][0] == ':')
2731             pool_setarchpolicy(pool, pieces[1] + 1);
2732           else
2733             pool_setarch(pool, pieces[1]);
2734           if (npieces > 3)
2735             {
2736               Repo *repo = testcase_str2repo(pool, pieces[3]);
2737               if (!repo)
2738                 pool_error(pool, 0, "testcase_read: system: unknown repo '%s'", pieces[3]);
2739               else
2740                 pool_set_installed(pool, repo);
2741             }
2742         }
2743       else if (!strcmp(pieces[0], "job") && npieces > 1)
2744         {
2745           char *sp;
2746           Id how, what;
2747           if (prepared <= 0)
2748             {
2749               pool_addfileprovides(pool);
2750               pool_createwhatprovides(pool);
2751               prepared = 1;
2752             }
2753           if (npieces >= 3 && !strcmp(pieces[2], "selection"))
2754             {
2755               addselectionjob(pool, pieces + 1, npieces - 1, job, 0, 0);
2756               continue;
2757             }
2758           if (npieces >= 4 && !strcmp(pieces[2], "selection_matchdeps"))
2759             {
2760               pieces[2] = pieces[1];
2761               addselectionjob(pool, pieces + 2, npieces - 2, job, SELECTIONJOB_MATCHDEPS, pool_str2id(pool, pieces[3], 1));
2762               continue;
2763             }
2764           if (npieces >= 4 && !strcmp(pieces[2], "selection_matchdepid"))
2765             {
2766               pieces[2] = pieces[1];
2767               addselectionjob(pool, pieces + 2, npieces - 2, job, SELECTIONJOB_MATCHDEPID, pool_str2id(pool, pieces[3], 1));
2768               continue;
2769             }
2770           if (npieces >= 4 && !strcmp(pieces[2], "selection_matchsolvable"))
2771             {
2772               pieces[2] = pieces[1];
2773               addselectionjob(pool, pieces + 2, npieces - 2, job, SELECTIONJOB_MATCHSOLVABLE, pool_str2id(pool, pieces[3], 1));
2774               continue;
2775             }
2776           /* rejoin */
2777           for (sp = pieces[1]; sp < pieces[npieces - 1]; sp++)
2778             if (*sp == 0)
2779               *sp = ' ';
2780           how = testcase_str2job(pool, pieces[1], &what);
2781           if (how >= 0 && job)
2782             queue_push2(job, how, what);
2783         }
2784       else if (!strcmp(pieces[0], "vendorclass") && npieces > 1)
2785         {
2786           pool_addvendorclass(pool, (const char **)(pieces + 1));
2787         }
2788       else if (!strcmp(pieces[0], "namespace") && npieces > 1)
2789         {
2790           int i = strlen(pieces[1]);
2791           s = strchr(pieces[1], '(');
2792           if (!s && pieces[1][i - 1] != ')')
2793             {
2794               pool_error(pool, 0, "testcase_read: bad namespace '%s'", pieces[1]);
2795             }
2796           else
2797             {
2798               Id name, evr, id;
2799               Queue q;
2800               queue_init(&q);
2801               *s = 0;
2802               pieces[1][i - 1] = 0;
2803               name = pool_str2id(pool, pieces[1], 1);
2804               evr = pool_str2id(pool, s + 1, 1);
2805               *s = '(';
2806               pieces[1][i - 1] = ')';
2807               id = pool_rel2id(pool, name, evr, REL_NAMESPACE, 1);
2808               for (i = 2; i < npieces; i++)
2809                 queue_push(&q, testcase_str2solvid(pool, pieces[i]));
2810               /* now do the callback */
2811               if (prepared <= 0)
2812                 {
2813                   pool_addfileprovides(pool);
2814                   pool_createwhatprovides(pool);
2815                   prepared = 1;
2816                 }
2817               pool->whatprovides_rel[GETRELID(id)] = pool_queuetowhatprovides(pool, &q);
2818               queue_free(&q);
2819             }
2820         }
2821       else if (!strcmp(pieces[0], "poolflags"))
2822         {
2823           int i;
2824           if (!poolflagsreset)
2825             {
2826               poolflagsreset = 1;
2827               testcase_resetpoolflags(pool);    /* hmm */
2828             }
2829           for (i = 1; i < npieces; i++)
2830             testcase_setpoolflags(pool, pieces[i]);
2831         }
2832       else if (!strcmp(pieces[0], "solverflags") && npieces > 1)
2833         {
2834           int i;
2835           if (!solv)
2836             {
2837               solv = solver_create(pool);
2838               testcase_resetsolverflags(solv);
2839             }
2840           for (i = 1; i < npieces; i++)
2841             testcase_setsolverflags(solv, pieces[i]);
2842         }
2843       else if (!strcmp(pieces[0], "result") && npieces > 1)
2844         {
2845           char *result = 0;
2846           int resultflags = str2resultflags(pool, pieces[1]);
2847           const char *rdata;
2848           if (npieces > 2)
2849             {
2850               rdata = pool_tmpjoin(pool, testcasedir, pieces[2], 0);
2851               if (!strcmp(pieces[2], "<inline>"))
2852                 result = read_inline_file(fp, &buf, &bufp, &bufl);
2853               else
2854                 {
2855                   FILE *rfp = fopen(rdata, "r");
2856                   if (!rfp)
2857                     pool_error(pool, 0, "testcase_read: could not open '%s'", rdata);
2858                   else
2859                     {
2860                       result = read_file(rfp);
2861                       fclose(rfp);
2862                     }
2863                 }
2864             }
2865           if (resultp)
2866             *resultp = result;
2867           else
2868             solv_free(result);
2869           if (resultflagsp)
2870             *resultflagsp = resultflags;
2871         }
2872       else if (!strcmp(pieces[0], "nextjob"))
2873         {
2874           if (npieces == 2 && resultflagsp && !strcmp(pieces[1], "reusesolver"))
2875             *resultflagsp |= TESTCASE_RESULT_REUSE_SOLVER;
2876           break;
2877         }
2878       else if (!strcmp(pieces[0], "disable") && npieces == 3)
2879         {
2880           Id p;
2881           if (strcmp(pieces[1], "pkg"))
2882             {
2883               pool_error(pool, 0, "testcase_read: bad disable type '%s'", pieces[1]);
2884               continue;
2885             }
2886           if (!prepared)
2887             pool_createwhatprovides(pool);
2888           prepared = -1;
2889           if (!pool->considered)
2890             {
2891               pool->considered = solv_calloc(1, sizeof(Map));
2892               map_init(pool->considered, pool->nsolvables);
2893               map_setall(pool->considered);
2894             }
2895           p = testcase_str2solvid(pool, pieces[2]);
2896           if (p)
2897             MAPCLR(pool->considered, p);
2898           else
2899             pool_error(pool, 0, "disable: unknown package '%s'", pieces[2]);
2900         }
2901       else if (!strcmp(pieces[0], "feature"))
2902         {
2903           int i, j;
2904           for (i = 1; i < npieces; i++)
2905             {
2906               for (j = 0; features[j]; j++)
2907                 if (!strcmp(pieces[i], features[j]))
2908                   break;
2909               if (!features[j])
2910                 {
2911                   pool_error(pool, 0, "testcase_read: missing feature '%s'", pieces[i]);
2912                   missing_features++;
2913                 }
2914             }
2915           if (missing_features)
2916             break;
2917         }
2918       else if (!strcmp(pieces[0], "genid") && npieces > 1)
2919         {
2920           Id id;
2921           /* rejoin */
2922           if (npieces > 2)
2923             {
2924               char *sp;
2925               for (sp = pieces[2]; sp < pieces[npieces - 1]; sp++)
2926                 if (*sp == 0)
2927                   *sp = ' ';
2928             }
2929           genid = solv_extend(genid, ngenid, 1, sizeof(*genid), 7);
2930           if (!strcmp(pieces[1], "op") && npieces > 2)
2931             {
2932               struct oplist *op;
2933               for (op = oplist; op->flags; op++)
2934                 if (!strncmp(pieces[2], op->opname, strlen(op->opname)))
2935                   break;
2936               if (!op->flags)
2937                 {
2938                   pool_error(pool, 0, "testcase_read: genid: unknown op '%s'", pieces[2]);
2939                   break;
2940                 }
2941               if (ngenid < 2)
2942                 {
2943                   pool_error(pool, 0, "testcase_read: genid: out of stack");
2944                   break;
2945                 }
2946               ngenid -= 2;
2947               id = pool_rel2id(pool, genid[ngenid] , genid[ngenid + 1], op->flags, 1);
2948             }
2949           else if (!strcmp(pieces[1], "lit"))
2950             id = pool_str2id(pool, npieces > 2 ? pieces[2] : "", 1);
2951           else if (!strcmp(pieces[1], "null"))
2952             id = 0;
2953           else if (!strcmp(pieces[1], "dep"))
2954             id = testcase_str2dep(pool, pieces[2]);
2955           else
2956             {
2957               pool_error(pool, 0, "testcase_read: genid: unknown command '%s'", pieces[1]);
2958               break;
2959             }
2960           genid[ngenid++] = id;
2961         }
2962       else if (!strcmp(pieces[0], "autoinst") && npieces > 2)
2963         {
2964           if (strcmp(pieces[1], "name"))
2965             {
2966               pool_error(pool, 0, "testcase_read: autoinst: illegal mode");
2967               break;
2968             }
2969           queue_push(&autoinstq, pool_str2id(pool, pieces[2], 1));
2970         }
2971       else if (!strcmp(pieces[0], "evrcmp") && npieces == 3)
2972         {
2973           Id evr1 = pool_str2id(pool, pieces[1], 1);
2974           Id evr2 = pool_str2id(pool, pieces[2], 1);
2975           int r = pool_evrcmp(pool, evr1, evr2, EVRCMP_COMPARE);
2976           r = r < 0 ? REL_LT : r > 0 ? REL_GT : REL_EQ;
2977           queue_push2(job, SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, evr1, evr2, r, 1));
2978         }
2979       else
2980         {
2981           pool_error(pool, 0, "testcase_read: cannot parse command '%s'", pieces[0]);
2982         }
2983     }
2984   while (job && ngenid > 0)
2985     queue_push2(job, SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES, genid[--ngenid]);
2986   if (autoinstq.count)
2987     pool_add_userinstalled_jobs(pool, &autoinstq, job, GET_USERINSTALLED_NAMES | GET_USERINSTALLED_INVERTED);
2988   queue_free(&autoinstq);
2989   genid = solv_free(genid);
2990   buf = solv_free(buf);
2991   pieces = solv_free(pieces);
2992   solv_free(testcasedir);
2993   if (!prepared)
2994     {
2995       pool_addfileprovides(pool);
2996       pool_createwhatprovides(pool);
2997     }
2998   if (!solv)
2999     {
3000       solv = solver_create(pool);
3001       testcase_resetsolverflags(solv);
3002     }
3003   if (closefp)
3004     fclose(fp);
3005   if (missing_features)
3006     {
3007       solver_free(solv);
3008       solv = 0;
3009       if (resultflagsp)
3010         *resultflagsp = 77;     /* hack for testsolv */
3011     }
3012   return solv;
3013 }
3014
3015 char *
3016 testcase_resultdiff(const char *result1, const char *result2)
3017 {
3018   Strqueue sq1, sq2, osq;
3019   char *r;
3020   strqueue_init(&sq1);
3021   strqueue_init(&sq2);
3022   strqueue_init(&osq);
3023   strqueue_split(&sq1, result1);
3024   strqueue_split(&sq2, result2);
3025   strqueue_sort(&sq1);
3026   strqueue_sort(&sq2);
3027   strqueue_diff(&sq1, &sq2, &osq);
3028   r = osq.nstr ? strqueue_join(&osq) : 0;
3029   strqueue_free(&sq1);
3030   strqueue_free(&sq2);
3031   strqueue_free(&osq);
3032   return r;
3033 }
3034