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