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