- start testcase reading/writing support
[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 <limits.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include "pool.h"
16 #include "repo.h"
17 #include "solver.h"
18 #include "testcase.h"
19
20
21 static struct job2str {
22   Id job;
23   const char *str;
24 } job2str[] = {
25   { SOLVER_NOOP,          "noop" },
26   { SOLVER_INSTALL,       "install" },
27   { SOLVER_ERASE,         "erase" },
28   { SOLVER_UPDATE,        "update" },
29   { SOLVER_WEAKENDEPS,    "weakendeps" },
30   { SOLVER_NOOBSOLETES,   "noobsoletes" },
31   { SOLVER_LOCK,          "lock" },
32   { SOLVER_DISTUPGRADE,   "distupgrade" },
33   { SOLVER_VERIFY,        "verify" },
34   { SOLVER_DROP_ORPHANED, "droporphaned" },
35   { SOLVER_USERINSTALLED, "userinstalled" },
36   { 0, 0 }
37 };
38
39 static struct jobflags2str {
40   Id flags;
41   const char *str;
42 } jobflags2str[] = {
43   { SOLVER_WEAK,      "weak" },
44   { SOLVER_ESSENTIAL, "essential" },
45   { SOLVER_CLEANDEPS, "cleandeps" },
46   { SOLVER_SETEV,     "setev" },
47   { SOLVER_SETEVR,    "setevr" },
48   { SOLVER_SETARCH,   "setarch" },
49   { SOLVER_SETVENDOR, "setvendor" },
50   { SOLVER_SETREPO,   "setrepo" },
51   { SOLVER_NOAUTOSET, "noautoset" },
52   { 0, 0 }
53 };
54
55
56 static inline int
57 pool_isknownarch(Pool *pool, Id id)
58 {
59   if (!id || id == ID_EMPTY)
60     return 0;
61   if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
62     return 1;
63   if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
64     return 0;
65   return 1;
66 }
67
68 Id
69 testcase_str2dep(Pool *pool, char *s)
70 {
71   char *n, *a;
72   Id id;
73   int flags;
74
75   if ((n = strchr(s, '|')) != 0)
76     {    
77       id = testcase_str2dep(pool, n + 1);
78       *n = 0; 
79       id = pool_rel2id(pool, testcase_str2dep(pool, s), id, REL_OR, 1);
80       *n = '|'; 
81       return id;
82     }
83   while (*s == ' ' || *s == '\t')
84     s++;
85   n = s;
86   while (*s && *s != ' ' && *s != '\t' && *s != '<' && *s != '=' && *s != '>')
87     s++;
88   if ((a = strchr(n, '.')) != 0 && a + 1 < s)
89     {
90       Id archid = pool_strn2id(pool, a + 1, s - (a + 1), 0);
91       if (pool_isknownarch(pool, archid))
92         {
93           id = pool_strn2id(pool, n, a - n, 1);
94           id = pool_rel2id(pool, id, archid, REL_ARCH, 1);
95         }
96       else
97         id = pool_strn2id(pool, n, s - n, 1);
98     }
99   else
100     id = pool_strn2id(pool, n, s - n, 1);
101   if (!*s)
102     return id;
103   while (*s == ' ' || *s == '\t')
104     s++;
105   flags = 0;
106   for (;;s++)
107     {  
108       if (*s == '<')
109         flags |= REL_LT;
110       else if (*s == '=')
111         flags |= REL_EQ;
112       else if (*s == '>')
113         flags |= REL_GT;
114       else
115         break;
116     }
117   if (!flags)
118     return id;
119   while (*s == ' ' || *s == '\t')
120     s++;
121   n = s;
122   while (*s && *s != ' ' && *s != '\t')
123     s++;
124   return pool_rel2id(pool, id, pool_strn2id(pool, n, s - n, 1), flags, 1);
125 }
126
127 const char *
128 testcase_solvid2str(Pool *pool, Id p)
129 {
130   Solvable *s = pool->solvables + p;
131   const char *str = pool_solvid2str(pool, p);
132   char buf[20];
133
134   if (!s->repo)
135     return pool_tmpappend(pool, str, "@", 0);
136   if (s->repo->name)
137     {
138       int l = strlen(str);
139       char *str2 = pool_tmpappend(pool, str, "@", s->repo->name);
140       for (; str2[l]; l++)
141         if (str2[l] == ' ' || str2[l] == '\t')
142           str2[l] = '_';
143       return str2;
144     }
145   sprintf(buf, "@#%d", s->repo->repoid);
146   return pool_tmpappend(pool, str, buf, 0);
147 }
148
149 Repo *
150 testcase_str2repo(Pool *pool, const char *str)
151 {
152   int repoid;
153   Repo *repo = 0;
154   if (str[0] == '#' && (str[1] >= '0' && str[1] <= '9'))
155     {
156       int j;
157       repoid = 0;
158       for (j = 1; str[j] >= '0' && str[j] <= '9'; j++)
159         repoid = repoid * 10 + (str[j] - '0');
160       if (!str[j] && repoid > 0 && repoid < pool->nrepos)
161         repo = pool_id2repo(pool, repoid);
162     }
163   if (!repo)
164     {
165       FOR_REPOS(repoid, repo)
166         {
167           int i, l;
168           if (!repo->name)
169             continue;
170           l = strlen(repo->name);
171           for (i = 0; i < l; i++)
172             {
173               int c = repo->name[i];
174               if (c == ' ' || c == '\t')
175                 c = '_';
176               if (c != str[i])
177                 break;
178             }
179           if (i == l && !str[l])
180             break;
181         }
182       if (repoid >= pool->nrepos)
183         repo = 0;
184     }
185   return repo;
186 }
187
188 Id
189 testcase_str2solvid(Pool *pool, const char *str)
190 {
191   int i, l = strlen(str);
192   int repostart;
193   Repo *repo;
194   Id arch;
195
196   if (!l)
197     return 0;
198   repo = 0;
199   for (i = l - 1; i >= 0; i--)
200     if (str[i] == '@' && (repo = testcase_str2repo(pool, str + i + 1)) != 0)
201       break;
202   if (i < 0)
203     i = l;
204   repostart = i;
205   /* now find the arch (if present) */
206   arch = 0;
207   for (i = repostart - 1; i > 0; i--)
208     if (str[i] == '.')
209       {
210         arch = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
211         if (arch)
212           repostart = i;
213         break;
214       }
215   /* now find the name */
216   for (i = repostart - 1; i > 0; i--)
217     {
218       if (str[i] == '-')
219         {
220           Id nid, evrid, p, pp;
221           nid = pool_strn2id(pool, str, i, 0);
222           if (!nid)
223             continue;
224           evrid = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
225           if (!evrid)
226             continue;
227           FOR_PROVIDES(p, pp, nid)
228             {
229               Solvable *s = pool->solvables + p;
230               if (s->name != nid || s->evr != evrid)
231                 continue;
232               if (repo && s->repo != repo)
233                 continue;
234               if (arch && s->arch != arch)
235                 continue;
236               return p;
237             }
238         }
239     }
240   return 0;
241 }
242
243 const char *
244 testcase_job2str(Pool *pool, Id how, Id what)
245 {
246   char *ret;
247   const char *jobstr;
248   const char *selstr;
249   const char *pkgstr;
250   int i, o;
251   Id select = how & SOLVER_SELECTMASK;
252
253   for (i = 0; job2str[i].str; i++)
254     if ((how & SOLVER_JOBMASK) == job2str[i].job)
255       break;
256   jobstr = job2str[i].str ? job2str[i].str : "unknown";
257   if (select == SOLVER_SOLVABLE)
258     {
259       selstr = " pkg ";
260       pkgstr = testcase_solvid2str(pool, what);
261     }
262   else if (select == SOLVER_SOLVABLE_NAME)
263     {
264       selstr = " name ";
265       pkgstr = pool_dep2str(pool, what);
266     }
267   else if (select == SOLVER_SOLVABLE_PROVIDES)
268     {
269       selstr = " provides ";
270       pkgstr = pool_dep2str(pool, what);
271     }
272   else if (select == SOLVER_SOLVABLE_ONE_OF)
273     {
274       Id p;
275       selstr = " oneof ";
276       pkgstr = 0;
277       while ((p = pool->whatprovidesdata[what++]) != 0)
278         {
279           const char *s = testcase_solvid2str(pool, p);
280           if (pkgstr)
281             {
282               pkgstr = pool_tmpappend(pool, pkgstr, " ", s);
283               pool_freetmpspace(pool, s);
284             }
285           else
286             pkgstr = s;
287         }
288       if (!pkgstr)
289         pkgstr = "nothing";
290     }
291   else if (select == SOLVER_SOLVABLE_REPO)
292     {
293       Repo *repo = pool_id2repo(pool, what);
294       selstr = " repo ";
295       if (!repo->name)
296         {
297           char buf[20];
298           sprintf(buf, "#%d", repo->repoid);
299           pkgstr = pool_tmpjoin(pool, buf, 0, 0);
300         }
301       else
302         pkgstr = pool_tmpjoin(pool, repo->name, 0, 0);
303     }
304   else if (select == SOLVER_SOLVABLE_ALL)
305     {
306       selstr = " all ";
307       pkgstr = "packages";
308     }
309   else
310     {
311       selstr = " unknown ";
312       pkgstr = "unknown";
313     }
314   ret = pool_tmpjoin(pool, jobstr, selstr, pkgstr);
315   o = strlen(ret);
316   ret = pool_tmpappend(pool, ret, " ", 0);
317   for (i = 0; jobflags2str[i].str; i++)
318     if ((how & jobflags2str[i].flags) != 0)
319       ret = pool_tmpappend(pool, ret, ",", jobflags2str[i].str);
320   if (!ret[o + 1])
321     ret[o] = 0;
322   else
323     {
324       ret[o + 1] = '[';
325       ret = pool_tmpappend(pool, ret, "]", 0);
326     }
327   return ret;
328 }
329
330 Id
331 testcase_str2job(Pool *pool, const char *str, Id *whatp)
332 {
333   int i;
334   Id job;
335   Id what;
336   char *s;
337   char **pieces = 0;
338   int npieces = 0;
339
340   *whatp = 0;
341   /* so we can patch it */
342   s = pool_tmpjoin(pool, str, 0, 0);
343   /* split it in pieces */
344   for (;;)
345     {
346       while (*s == ' ' || *s == '\t')
347         s++;
348       if (!*s)
349         break;
350       pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
351       pieces[npieces++] = s;
352       while (*s && *s != ' ' && *s != '\t')
353         s++;
354       if (*s)
355         *s++ = 0;
356     }
357   if (npieces < 3)
358     {
359       pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
360       return 0;
361     }
362
363   for (i = 0; job2str[i].str; i++)
364     if (!strcmp(pieces[0], job2str[i].str))
365       break;
366   if (!job2str[i].str)
367     {
368       pool_debug(pool, SOLV_ERROR, "str2job: unknown job '%s'\n", str);
369       return 0;
370     }
371   job = job2str[i].job;
372   if (npieces > 3)
373     {
374       char *flags = pieces[npieces - 1];
375       char *nf;
376       if (*flags == '[' && flags[strlen(flags) - 1] == ']')
377         {
378           npieces--;
379           flags++;
380           flags[strlen(flags) - 1] = ',';
381           while (*flags)
382             {
383               for (nf = flags; *nf != ','; nf++)
384                 ;
385               *nf++ = 0;
386               for (i = 0; jobflags2str[i].str; i++)
387                 if (!strcmp(flags, jobflags2str[i].str))
388                   break;
389               if (!jobflags2str[i].str)
390                 {
391                   pool_debug(pool, SOLV_ERROR, "str2job: unknown jobflags in '%s'\n", str);
392                   return 0;
393                 }
394               job |= jobflags2str[i].flags;
395               flags = nf;
396             }
397         }
398     }
399   if (!strcmp(pieces[1], "pkg"))
400     {
401       if (npieces != 3)
402         {
403           pool_debug(pool, SOLV_ERROR, "str2job: bad pkg selector in '%s'\n", str);
404           return 0;
405         }
406       job |= SOLVER_SOLVABLE;
407       what = testcase_str2solvid(pool, pieces[2]);
408       if (!what)
409         {
410           pool_debug(pool, SOLV_ERROR, "str2job: unknown package '%s'\n", pieces[2]);
411           return 0;
412         }
413     }
414   else if (!strcmp(pieces[1], "name") || !strcmp(pieces[1], "provides"))
415     {
416       /* join em again for dep2str... */
417       char *sp;
418       for (sp = pieces[2]; sp < pieces[npieces - 1]; sp++)
419         if (*sp == 0)
420           *sp = ' ';
421       what = testcase_str2dep(pool, pieces[2]);
422       if (pieces[1][0] == 'n')
423         job |= SOLVER_SOLVABLE_NAME;
424       else
425         job |= SOLVER_SOLVABLE_PROVIDES;
426     }
427   else if (!strcmp(pieces[1], "oneof"))
428     {
429       Queue q;
430       job |= SOLVER_SOLVABLE_ONE_OF;
431       queue_init(&q);
432       if (npieces > 3 && strcmp(pieces[2], "nothing") != 0)
433         {
434           for (i = 2; i < npieces; i++)
435             {
436               Id p = testcase_str2solvid(pool, pieces[i]);
437               if (!p)
438                 {
439                   pool_debug(pool, SOLV_ERROR, "str2job: unknown package '%s'\n", pieces[i]);
440                   queue_free(&q);
441                   return 0;
442                 }
443               queue_push(&q, p);
444             }
445         }
446       what = pool_queuetowhatprovides(pool, &q);
447       queue_free(&q);
448     }
449   else if (!strcmp(pieces[1], "repo"))
450     {
451       Repo *repo;
452       if (npieces != 3)
453         {
454           pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
455           return 0;
456         }
457       repo = testcase_str2repo(pool, pieces[2]);
458       if (!repo)
459         {
460           pool_debug(pool, SOLV_ERROR, "str2job: unknown repo '%s'\n", pieces[2]);
461           return 0;
462         }
463       job |= SOLVER_SOLVABLE_REPO;
464       what = repo->repoid;
465     }
466   else if (!strcmp(pieces[1], "all"))
467     {
468       if (npieces != 3 && strcmp(pieces[2], "packages") != 0)
469         {
470           pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
471           return 0;
472         }
473       job |= SOLVER_SOLVABLE_ALL;
474       what = 0;
475     }
476   else
477     {
478       pool_debug(pool, SOLV_ERROR, "str2job: unknown selection in '%s'\n", str);
479       return 0;
480     }
481   *whatp = what;
482   return job;
483 }
484
485 static void
486 writedeps(Repo *repo, FILE *fp, const char *tag, Id key, Solvable *s, Offset off)
487 {
488   Pool *pool = repo->pool;
489   Id id, *dp;
490   int tagwritten = 0;
491   const char *idstr;
492
493   if (!off)
494     return;
495   dp = repo->idarraydata + off;
496   while ((id = *dp++) != 0)
497     {
498       if (key == SOLVABLE_REQUIRES && id == SOLVABLE_PREREQMARKER)
499         {
500           if (tagwritten)
501             fprintf(fp, "-%s\n", tag);
502           tagwritten = 0;
503           tag = "Prq:";
504           continue;
505         }
506       if (key == SOLVABLE_PROVIDES && id == SOLVABLE_FILEMARKER)
507         break;
508       idstr = pool_dep2str(pool, id);
509       if (ISRELDEP(id))
510         {
511           Reldep *rd = GETRELDEP(pool, id);
512           if (key == SOLVABLE_CONFLICTS && rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_OTHERPROVIDERS)
513             {
514               if (!strncmp(idstr, "namespace:", 10))
515                 idstr += 10;
516             }
517           if (key == SOLVABLE_SUPPLEMENTS)
518             {
519               if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_FILESYSTEM)
520                 {
521                   if (!strncmp(idstr, "namespace:", 10))
522                     idstr += 10;
523                 }
524               else if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_MODALIAS)
525                 {
526                   if (!strncmp(idstr, "namespace:", 10))
527                     idstr += 10;
528                 }
529               else if (rd->flags == REL_AND)
530                 {
531                   /* either packageand chain or modalias */
532                   idstr = 0;
533                   if (ISRELDEP(rd->evr))
534                     {
535                       Reldep *mrd = GETRELDEP(pool, rd->evr);
536                       if (mrd->flags == REL_NAMESPACE && mrd->name == NAMESPACE_MODALIAS)
537                         {
538                           idstr = pool_tmpjoin(pool, "modalias(", pool_dep2str(pool, rd->name), ":");
539                           idstr = pool_tmpappend(pool, idstr, pool_dep2str(pool, mrd->evr), ")");
540                         }
541                       else if (mrd->flags >= 8)
542                         continue;
543                     }
544                   if (!idstr)
545                     {
546                       /* must be and chain */
547                       idstr = pool_dep2str(pool, rd->evr);
548                       for (;;)
549                         {
550                           id = rd->name;
551                           if (!ISRELDEP(id))
552                             break;
553                           rd = GETRELDEP(pool, id);
554                           if (rd->flags != REL_AND)
555                             break;
556                           idstr = pool_tmpjoin(pool, pool_dep2str(pool, rd->evr), ":", idstr);
557                         }
558                       idstr = pool_tmpjoin(pool, pool_dep2str(pool, id), ":", idstr);
559                       idstr = pool_tmpjoin(pool, "packageand(", idstr, ")");
560                     }
561                 }
562               else if (rd->flags >= 8)
563                 continue;
564             }
565         }
566       if (!tagwritten)
567         {
568           fprintf(fp, "+%s\n", tag);
569           tagwritten = 1;
570         }
571       fprintf(fp, "%s\n", idstr);
572     }
573   if (key == SOLVABLE_PROVIDES)
574     {
575       /* add the filelist */
576       Dataiterator di;
577       dataiterator_init(&di, pool, repo, s - pool->solvables, SOLVABLE_FILELIST, 0, 0);
578       while (dataiterator_step(&di))
579         {
580           if (!tagwritten)
581             {
582               fprintf(fp, "+%s", tag);
583               tagwritten = 1;
584             }
585           fprintf(fp, "%s\n", repodata_dir2str(di.data, di.kv.id, di.kv.str));
586         }
587     }
588   if (tagwritten)
589     fprintf(fp, "-%s\n", tag);
590 }
591
592 int
593 testcase_write_susetags(Repo *repo, FILE *fp)
594 {
595   Pool *pool = repo->pool;
596   Solvable *s;
597   Id p;
598   const char *name;
599   const char *evr;
600   const char *arch;
601   const char *release;
602 #if 0
603   const char *chksum;
604   Id chksumtype, type;
605   unsigned int medianr;
606 #endif
607   const char *tmp;
608   unsigned int ti;
609
610   fprintf(fp, "=Ver: 2.0\n");
611   FOR_REPO_SOLVABLES(repo, p, s)
612     {
613       name = pool_id2str(pool, s->name);
614       evr = pool_id2str(pool, s->evr);
615       arch = pool_id2str(pool, s->arch);
616       release = strrchr(evr, '-');
617       if (!release)
618         release = evr + strlen(evr);
619       fprintf(fp, "=Pkg: %s %.*s %s %s\n", name, release - evr, evr, *release && release[1] ? release + 1 : "0", arch);
620       tmp = solvable_lookup_str(s, SOLVABLE_SUMMARY);
621       if (tmp)
622         fprintf(fp, "=Sum: %s\n", tmp);
623 #if 0
624       chksum = solvable_lookup_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
625       if (chksum)
626         fprintf(fp, "=Cks: %s %s\n", solv_chksum_type2str(chksumtype), chksum);
627 #endif
628       writedeps(repo, fp, "Req:", SOLVABLE_REQUIRES, s, s->requires);
629       writedeps(repo, fp, "Prv:", SOLVABLE_PROVIDES, s, s->provides);
630       writedeps(repo, fp, "Obs:", SOLVABLE_OBSOLETES, s, s->obsoletes);
631       writedeps(repo, fp, "Con:", SOLVABLE_CONFLICTS, s, s->conflicts);
632       writedeps(repo, fp, "Rec:", SOLVABLE_RECOMMENDS, s, s->recommends);
633       writedeps(repo, fp, "Sup:", SOLVABLE_SUPPLEMENTS, s, s->supplements);
634       writedeps(repo, fp, "Sug:", SOLVABLE_SUGGESTS, s, s->suggests);
635       writedeps(repo, fp, "Enh:", SOLVABLE_ENHANCES, s, s->enhances);
636 #if 0
637       tmp = solvable_lookup_str(s, SOLVABLE_GROUP);
638       if (tmp)
639         fprintf(fp, "=Grp: %s\n", tmp);
640       tmp = solvable_lookup_str(s, SOLVABLE_LICENSE);
641       if (tmp)
642         fprintf(fp, "=Lic: %s\n", tmp);
643 #endif
644       if (s->vendor)
645         fprintf(fp, "=Vnd: %s\n", pool_id2str(pool, s->vendor));
646 #if 0
647       type = solvable_lookup_type(s, SOLVABLE_SOURCENAME);
648       if (type)
649         {
650           if (type != REPOKEY_TYPE_VOID)
651             name = solvable_lookup_str(s, SOLVABLE_SOURCENAME);
652           type = solvable_lookup_type(s, SOLVABLE_SOURCEEVR);
653           if (type)
654             {
655               if (type != REPOKEY_TYPE_VOID)
656                 evr = solvable_lookup_str(s, SOLVABLE_SOURCEEVR);
657               release = strrchr(evr, '-');
658               if (!release)
659                 release = evr + strlen(evr);
660               fprintf(fp, "=Src: %s %.*s %s %s\n", name, release - evr, evr, *release && release[1] ? release + 1 : "0", solvable_lookup_str(s, SOLVABLE_SOURCEARCH));
661             }
662         }
663 #endif
664       ti = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0);
665       if (ti)
666         fprintf(fp, "=Tim: %u\n", ti);
667 #if 0
668       tmp = solvable_get_location(s, &medianr);
669       if (tmp)
670         {
671           const char *base = strrchr(tmp, '/');
672           if (!base)
673             fprintf(fp, "=Loc: %d %s\n", medianr, tmp);
674           else if (strlen(arch) == base - tmp && !strncmp(tmp, arch, base - tmp))
675             fprintf(fp, "=Loc: %d %s\n", medianr, base + 1);
676           else
677             fprintf(fp, "=Loc: %d %s %.*s\n", medianr, base + 1, base - tmp, tmp);
678         }
679 #endif
680     }
681   return 0;
682 }