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