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