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