Merge branch 'master' of git@git.opensuse.org:projects/zypp/sat-solver
[platform/upstream/libsolv.git] / examples / solv.c
1 /*
2  * Copyright (c) 2009, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /* solv, a little software installer demoing the sat solver library */
9
10 /* things missing:
11  * - vendor policy loading
12  * - soft locks file handling
13  * - multi version handling
14  */
15
16 #define _GNU_SOURCE
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <dirent.h>
21 #include <fnmatch.h>
22 #include <unistd.h>
23 #include <zlib.h>
24 #include <fcntl.h>
25 #include <assert.h>
26 #include <sys/utsname.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29
30 #include "pool.h"
31 #include "poolarch.h"
32 #include "repo.h"
33 #include "evr.h"
34 #include "policy.h"
35 #include "util.h"
36 #include "solver.h"
37 #include "solverdebug.h"
38 #include "chksum.h"
39 #include "repo_solv.h"
40
41 #include "repo_write.h"
42 #include "repo_rpmdb.h"
43 #include "repo_products.h"
44 #include "repo_rpmmd.h"
45 #include "repo_susetags.h"
46 #include "repo_repomdxml.h"
47 #include "repo_updateinfoxml.h"
48 #include "repo_deltainfoxml.h"
49 #include "repo_content.h"
50 #include "pool_fileconflicts.h"
51
52
53 #ifdef FEDORA
54 # define REPOINFO_PATH "/etc/yum.repos.d"
55 #else
56 # define REPOINFO_PATH "/etc/zypp/repos.d"
57 # define PRODUCTS_PATH "/etc/products.d"
58 #endif
59
60 #define SOLVCACHE_PATH "/var/cache/solv"
61
62
63 struct repoinfo {
64   Repo *repo;
65
66   char *alias;
67   char *name;
68   int enabled;
69   int autorefresh;
70   char *baseurl;
71   char *metalink;
72   char *path;
73   int type;
74   int gpgcheck;
75   int priority;
76   int keeppackages;
77
78   unsigned char extcookie[32];
79 };
80
81 #ifdef FEDORA
82 char *
83 yum_substitute(Pool *pool, char *line)
84 {
85   char *p, *p2;
86
87   p = line;
88   while ((p2 = strchr(p, '$')) != 0)
89     {
90       if (!strncmp(p2, "$releasever", 11))
91         {
92           static char *releaseevr;
93           if (!releaseevr)
94             {
95               Queue q;
96         
97               queue_init(&q);
98               rpm_installedrpmdbids(0, "Providename", "redhat-release", &q);
99               if (q.count)
100                 {
101                   void *handle, *state = 0;
102                   char *p;
103                   handle = rpm_byrpmdbid(q.elements[0], 0, &state);
104                   releaseevr = rpm_query(handle, SOLVABLE_EVR);
105                   rpm_byrpmdbid(0, 0, &state);
106                   if (p = strchr(releaseevr, '-'))
107                     *p = 0;
108                 }
109               queue_free(&q);
110               if (!releaseevr)
111                 releaseevr = strdup("?");
112             }
113           *p2 = 0;
114           p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
115           p2 = p + (p2 - line);
116           line = p;
117           p = p2 + strlen(releaseevr);
118           continue;
119         }
120       if (!strncmp(p2, "$basearch", 9))
121         {
122           static char *basearch;
123           if (!basearch)
124             {
125               struct utsname un;
126               if (uname(&un))
127                 {
128                   perror("uname");
129                   exit(1);
130                 }
131               basearch = strdup(un.machine);
132               if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
133                 basearch[1] = '3';
134             }
135           *p2 = 0;
136           p = pool_tmpjoin(pool, line, basearch, p2 + 9);
137           p2 = p + (p2 - line);
138           line = p;
139           p = p2 + strlen(basearch);
140           continue;
141         }
142       p = p2 + 1;
143     }
144   return line;
145 }
146 #endif
147
148 #define TYPE_UNKNOWN    0
149 #define TYPE_SUSETAGS   1
150 #define TYPE_RPMMD      2
151 #define TYPE_PLAINDIR   3
152
153 static int
154 read_repoinfos_sort(const void *ap, const void *bp)
155 {
156   const struct repoinfo *a = ap;
157   const struct repoinfo *b = bp;
158   return strcmp(a->alias, b->alias);
159 }
160
161 struct repoinfo *
162 read_repoinfos(Pool *pool, const char *reposdir, int *nrepoinfosp)
163 {
164   char buf[4096];
165   char buf2[4096], *kp, *vp, *kpe;
166   DIR *dir;
167   FILE *fp;
168   struct dirent *ent;
169   int l, rdlen;
170   struct repoinfo *repoinfos = 0, *cinfo;
171   int nrepoinfos = 0;
172
173   rdlen = strlen(reposdir);
174   dir = opendir(reposdir);
175   if (!dir)
176     {
177       *nrepoinfosp = 0;
178       return 0;
179     }
180   while ((ent = readdir(dir)) != 0)
181     {
182       l = strlen(ent->d_name);
183       if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
184         continue;
185       snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
186       if ((fp = fopen(buf, "r")) == 0)
187         {
188           perror(buf);
189           continue;
190         }
191       cinfo = 0;
192       while(fgets(buf2, sizeof(buf2), fp))
193         {
194           l = strlen(buf2);
195           if (l == 0)
196             continue;
197           while (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t')
198             buf2[--l] = 0;
199           kp = buf2;
200           while (*kp == ' ' || *kp == '\t')
201             kp++;
202           if (!*kp || *kp == '#')
203             continue;
204 #ifdef FEDORA
205           if (strchr(kp, '$'))
206             kp = yum_substitute(pool, kp);
207 #endif
208           if (*kp == '[')
209             {
210               vp = strrchr(kp, ']');
211               if (!vp)
212                 continue;
213               *vp = 0;
214               repoinfos = sat_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
215               cinfo = repoinfos + nrepoinfos++;
216               memset(cinfo, 0, sizeof(*cinfo));
217               cinfo->alias = strdup(kp + 1);
218               cinfo->type = TYPE_RPMMD;
219               cinfo->autorefresh = 1;
220               continue;
221             }
222           if (!cinfo)
223             continue;
224           vp = strchr(kp, '=');
225           if (!vp)
226             continue;
227           for (kpe = vp - 1; kpe >= kp; kpe--)
228             if (*kpe != ' ' && *kpe != '\t')
229               break;
230           if (kpe == kp)
231             continue;
232           vp++;
233           while (*vp == ' ' || *vp == '\t')
234             vp++;
235           kpe[1] = 0;
236           if (!strcmp(kp, "name"))
237             cinfo->name = strdup(vp);
238           else if (!strcmp(kp, "enabled"))
239             cinfo->enabled = *vp == '0' ? 0 : 1;
240           else if (!strcmp(kp, "autorefresh"))
241             cinfo->autorefresh = *vp == '0' ? 0 : 1;
242           else if (!strcmp(kp, "gpgcheck"))
243             cinfo->gpgcheck = *vp == '0' ? 0 : 1;
244           else if (!strcmp(kp, "baseurl"))
245             cinfo->baseurl = strdup(vp);
246           else if (!strcmp(kp, "mirrorlist"))
247             {
248               if (strstr(vp, "metalink"))
249                 cinfo->metalink = strdup(vp);
250             }
251           else if (!strcmp(kp, "path"))
252             {
253               if (vp && strcmp(vp, "/") != 0)
254                 cinfo->path = strdup(vp);
255             }
256           else if (!strcmp(kp, "type"))
257             {
258               if (!strcmp(vp, "yast2"))
259                 cinfo->type = TYPE_SUSETAGS;
260               else if (!strcmp(vp, "rpm-md"))
261                 cinfo->type = TYPE_RPMMD;
262               else if (!strcmp(vp, "plaindir"))
263                 cinfo->type = TYPE_PLAINDIR;
264               else
265                 cinfo->type = TYPE_UNKNOWN;
266             }
267           else if (!strcmp(kp, "priority"))
268             cinfo->priority = atoi(vp);
269           else if (!strcmp(kp, "keeppackages"))
270             cinfo->keeppackages = *vp == '0' ? 0 : 1;
271         }
272       fclose(fp);
273       cinfo = 0;
274     }
275   closedir(dir);
276   qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
277   *nrepoinfosp = nrepoinfos;
278   return repoinfos;
279 }
280
281 void
282 free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
283 {
284   int i;
285   for (i = 0; i < nrepoinfos; i++)
286     {
287       struct repoinfo *cinfo = repoinfos + i;
288       sat_free(cinfo->name);
289       sat_free(cinfo->alias);
290       sat_free(cinfo->path);
291       sat_free(cinfo->metalink);
292       sat_free(cinfo->baseurl);
293     }
294   sat_free(repoinfos);
295 }
296
297 static inline int
298 opentmpfile()
299 {
300   char tmpl[100];
301   int fd;
302
303   strcpy(tmpl, "/var/tmp/solvXXXXXX");
304   fd = mkstemp(tmpl);
305   if (fd < 0)
306     {
307       perror("mkstemp");
308       exit(1);
309     }
310   unlink(tmpl);
311   return fd;
312 }
313
314 static int
315 verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype)
316 {
317   char buf[1024];
318   unsigned char *sum;
319   void *h;
320   int l;
321
322   h = sat_chksum_create(chksumtype);
323   if (!h)
324     {
325       printf("%s: unknown checksum type\n", file);
326       return 0;
327     }
328   while ((l = read(fd, buf, sizeof(buf))) > 0)
329     sat_chksum_add(h, buf, l);
330   lseek(fd, 0, SEEK_SET);
331   l = 0;
332   sum = sat_chksum_get(h, &l);
333   if (memcmp(sum, chksum, l))
334     {
335       printf("%s: checksum mismatch\n", file);
336       return 0;
337     }
338   return 1;
339 }
340
341 char *
342 findmetalinkurl(FILE *fp)
343 {
344   char buf[4096], *bp, *ep;
345   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
346     {
347       while (*bp == ' ' || *bp == '\t')
348         bp++;
349       if (strncmp(bp, "<url", 4))
350         continue;
351       bp = strchr(bp, '>');
352       if (!bp)
353         continue;
354       bp++;
355       ep = strstr(bp, "repodata/repomd.xml</url>");
356       if (!ep)
357         continue;
358       *ep = 0;
359       if (strncmp(bp, "http", 4))
360         continue;
361       return strdup(bp);
362     }
363   return 0;
364 }
365
366 static ssize_t
367 cookie_gzread(void *cookie, char *buf, size_t nbytes)
368 {
369   return gzread((gzFile *)cookie, buf, nbytes);
370 }
371
372 static int
373 cookie_gzclose(void *cookie)
374 {
375   return gzclose((gzFile *)cookie);
376 }
377
378 FILE *
379 curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype)
380 {
381   pid_t pid;
382   int fd, l;
383   int status;
384   char url[4096];
385   const char *baseurl = cinfo->baseurl;
386
387   if (!baseurl)
388     {
389       if (!cinfo->metalink)
390         return 0;
391       if (file != cinfo->metalink)
392         {
393           FILE *fp = curlfopen(cinfo, cinfo->metalink, 0, 0, 0);
394           if (!fp)
395             return 0;
396           cinfo->baseurl = findmetalinkurl(fp);
397           fclose(fp);
398           if (!cinfo->baseurl)
399             return 0;
400           return curlfopen(cinfo, file, uncompress, chksum, chksumtype);
401         }
402       snprintf(url, sizeof(url), "%s", file);
403     }
404   else
405     {
406       l = strlen(baseurl);
407       if (l && baseurl[l - 1] == '/')
408         snprintf(url, sizeof(url), "%s%s", baseurl, file);
409       else
410         snprintf(url, sizeof(url), "%s/%s", baseurl, file);
411     }
412   fd = opentmpfile();
413   if ((pid = fork()) == (pid_t)-1)
414     {
415       perror("fork");
416       exit(1);
417     }
418   if (pid == 0)
419     {
420       if (fd != 1)
421         {
422           dup2(fd, 1);
423           close(fd);
424         }
425       execlp("curl", "curl", "-f", "-s", "-L", url, (char *)0);
426       perror("curl");
427       _exit(0);
428     }
429   while (waitpid(pid, &status, 0) != pid)
430     ;
431   if (lseek(fd, 0, SEEK_END) == 0)
432     {
433       /* empty file */
434       close(fd);
435       return 0;
436     }
437   lseek(fd, 0, SEEK_SET);
438   if (chksumtype && !verify_checksum(fd, file, chksum, chksumtype))
439     {
440       close(fd);
441       return 0;
442     }
443   if (uncompress)
444     {
445       char tmpl[100];
446       cookie_io_functions_t cio;
447       gzFile *gzf;
448
449       sprintf(tmpl, "/dev/fd/%d", fd);
450       gzf = gzopen(tmpl, "r");
451       close(fd);
452       if (!gzf)
453         return 0;
454       memset(&cio, 0, sizeof(cio));
455       cio.read = cookie_gzread;
456       cio.close = cookie_gzclose;
457       return fopencookie(gzf, "r", cio);
458     }
459   fcntl(fd, F_SETFD, FD_CLOEXEC);
460   return fdopen(fd, "r");
461 }
462
463 static void
464 cleanupgpg(char *gpgdir)
465 {
466   char cmd[256];
467   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg", gpgdir);
468   unlink(cmd);
469   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg~", gpgdir);
470   unlink(cmd);
471   snprintf(cmd, sizeof(cmd), "%s/secring.gpg", gpgdir);
472   unlink(cmd);
473   snprintf(cmd, sizeof(cmd), "%s/trustdb.gpg", gpgdir);
474   unlink(cmd);
475   snprintf(cmd, sizeof(cmd), "%s/keys", gpgdir);
476   unlink(cmd);
477   rmdir(gpgdir);
478 }
479
480 int
481 checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
482 {
483   char *gpgdir;
484   char *keysfile;
485   const char *pubkey;
486   char cmd[256];
487   FILE *kfp;
488   Solvable *s;
489   Id p;
490   off_t posfp, possigfp;
491   int r, nkeys;
492
493   gpgdir = mkdtemp(pool_tmpjoin(sigpool, "/var/tmp/solvgpg.XXXXXX", 0, 0));
494   if (!gpgdir)
495     return 0;
496   keysfile = pool_tmpjoin(sigpool, gpgdir, "/keys", 0);
497   if (!(kfp = fopen(keysfile, "w")) )
498     {
499       cleanupgpg(gpgdir);
500       return 0;
501     }
502   nkeys = 0;
503   for (p = 1, s = sigpool->solvables + p; p < sigpool->nsolvables; p++, s++)
504     {
505       if (!s->repo)
506         continue;
507       pubkey = solvable_lookup_str(s, SOLVABLE_DESCRIPTION);
508       if (!pubkey || !*pubkey)
509         continue;
510       if (fwrite(pubkey, strlen(pubkey), 1, kfp) != 1)
511         break;
512       if (fputc('\n', kfp) == EOF)      /* Just in case... */
513         break;
514       nkeys++;
515     }
516   if (fclose(kfp) || !nkeys)
517     {
518       cleanupgpg(gpgdir);
519       return 0;
520     }
521   snprintf(cmd, sizeof(cmd), "gpg2 -q --homedir %s --import %s", gpgdir, keysfile);
522   if (system(cmd))
523     {
524       fprintf(stderr, "key import error\n");
525       cleanupgpg(gpgdir);
526       return 0;
527     }
528   unlink(keysfile);
529   posfp = lseek(fileno(fp), 0, SEEK_CUR);
530   lseek(fileno(fp), 0, SEEK_SET);
531   possigfp = lseek(fileno(sigfp), 0, SEEK_CUR);
532   lseek(fileno(sigfp), 0, SEEK_SET);
533   snprintf(cmd, sizeof(cmd), "gpg -q --homedir %s --verify /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", gpgdir, fileno(sigfp), fileno(fp));
534   fcntl(fileno(fp), F_SETFD, 0);        /* clear CLOEXEC */
535   fcntl(fileno(sigfp), F_SETFD, 0);     /* clear CLOEXEC */
536   r = system(cmd);
537   lseek(fileno(sigfp), possigfp, SEEK_SET);
538   lseek(fileno(fp), posfp, SEEK_SET);
539   fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
540   fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
541   cleanupgpg(gpgdir);
542   return r == 0 ? 1 : 0;
543 }
544
545 #define CHKSUM_IDENT "1.1"
546
547 void
548 calc_checksum_fp(FILE *fp, Id chktype, unsigned char *out)
549 {
550   char buf[4096];
551   void *h = sat_chksum_create(chktype);
552   int l;
553
554   sat_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
555   while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
556     sat_chksum_add(h, buf, l);
557   rewind(fp);
558   sat_chksum_free(h, out);
559 }
560
561 void
562 calc_checksum_stat(struct stat *stb, Id chktype, unsigned char *out)
563 {
564   void *h = sat_chksum_create(chktype);
565   sat_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
566   sat_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
567   sat_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
568   sat_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
569   sat_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
570   sat_chksum_free(h, out);
571 }
572
573 void
574 setarch(Pool *pool)
575 {
576   struct utsname un;
577   if (uname(&un))
578     {
579       perror("uname");
580       exit(1);
581     }
582   pool_setarch(pool, un.machine);
583 }
584
585 char *calccachepath(Repo *repo, const char *repoext)
586 {
587   char *q, *p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", repo->name);
588   if (repoext)
589     {
590       p = pool_tmpjoin(repo->pool, p, "_", repoext);
591       p = pool_tmpjoin(repo->pool, p, ".solvx", 0);
592     }
593   else
594     p = pool_tmpjoin(repo->pool, p, ".solv", 0);
595   q = p + strlen(SOLVCACHE_PATH) + 1;
596   if (*q == '.')
597     *q = '_';
598   for (; *q; q++)
599     if (*q == '/')
600       *q = '_';
601   return p;
602 }
603
604 int
605 usecachedrepo(Repo *repo, const char *repoext, unsigned char *cookie)
606 {
607   FILE *fp;
608   unsigned char mycookie[32];
609   struct repoinfo *cinfo;
610   int flags;
611
612   cinfo = repo->appdata;
613   if (!(fp = fopen(calccachepath(repo, repoext), "r")))
614     return 0;
615   if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
616     {
617       fclose(fp);
618       return 0;
619     }
620   if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)))
621     {
622       fclose(fp);
623       return 0;
624     }
625   rewind(fp);
626
627   flags = 0;
628   if (repoext)
629     flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
630   if (repoext && strcmp(repoext, "DL") != 0)
631     flags |= REPO_LOCALPOOL;    /* no local pool for DL so that we can compare IDs */
632
633   if (repo_add_solv_flags(repo, fp, flags))
634     {
635       fclose(fp);
636       return 0;
637     }
638   if (cinfo && !repoext)
639     {
640       /* set the checksum so that we can use it with the stub loads */
641       struct stat stb;
642       if (!fstat(fileno(fp), &stb))
643         calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->extcookie);
644     }
645   fclose(fp);
646   return 1;
647 }
648
649 static int
650 myrepodatafilter(Repo *repo, Repokey *key, void *kfdata)
651 {
652   Repodata *data = kfdata;
653
654   /* XXX: special repodata selection hack */
655   if (key->name == 1 && key->size != data - repo->repodata)
656     return -1;
657   if (key->storage == KEY_STORAGE_SOLVABLE)
658     return KEY_STORAGE_DROPPED;
659   return repo_write_stdkeyfilter(repo, key, kfdata);
660 }
661
662 void
663 writecachedrepo(Repo *repo, Repodata *info, const char *repoext, unsigned char *cookie)
664 {
665   Id *addedfileprovides = 0;
666   FILE *fp;
667   int i, fd;
668   char *tmpl;
669   int myinfo = 0;
670   struct repoinfo *cinfo;
671
672   cinfo = repo->appdata;
673   mkdir(SOLVCACHE_PATH, 0755);
674   tmpl = sat_dupjoin(SOLVCACHE_PATH, "/", ".newsolv-XXXXXX");
675   fd = mkstemp(tmpl);
676   if (fd < 0)
677     {
678       free(tmpl);
679       return;
680     }
681   fchmod(fd, 0444);
682   if (!(fp = fdopen(fd, "w")))
683     {
684       close(fd);
685       unlink(tmpl);
686       free(tmpl);
687       return;
688     }
689   if (!repoext)
690     {
691       if (!info)
692         {
693           info = repo_add_repodata(repo, 0);
694           myinfo = 1;
695         }
696       pool_addfileprovides_ids(repo->pool, 0, &addedfileprovides);
697       if (addedfileprovides && *addedfileprovides)
698         {
699           for (i = 0; addedfileprovides[i]; i++)
700             repodata_add_idarray(info, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, addedfileprovides[i]);
701         }
702       sat_free(addedfileprovides);
703       repodata_internalize(info);
704       repo_write(repo, fp, repo_write_stdkeyfilter, 0, 0);
705     }
706   else
707     repo_write(repo, fp, myrepodatafilter, info, 0);
708   if (myinfo)
709     repodata_free(info);
710   if (fwrite(cookie, 32, 1, fp) != 1)
711     {
712       fclose(fp);
713       unlink(tmpl);
714       free(tmpl);
715       return;
716     }
717   if (fclose(fp))
718     {
719       unlink(tmpl);
720       free(tmpl);
721       return;
722     }
723   if (!repoext && cinfo)
724     {
725       /* set the checksum so that we can use it with the stub loads */
726       struct stat stb;
727       if (!stat(tmpl, &stb))
728         calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->extcookie);
729     }
730   if (!rename(tmpl, calccachepath(repo, repoext)))
731     unlink(tmpl);
732   free(tmpl);
733 }
734
735
736 static Pool *
737 read_sigs()
738 {
739   Pool *sigpool = pool_create();
740   Repo *repo = repo_create(sigpool, "rpmdbkeys");
741   repo_add_rpmdb_pubkeys(repo, 0, 0);
742   return sigpool;
743 }
744
745
746 /* repomd helpers */
747
748 static inline const char *
749 repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
750 {
751   Pool *pool = repo->pool;
752   Dataiterator di;
753   const char *filename;
754
755   filename = 0;
756   *chksump = 0;
757   *chksumtypep = 0;
758   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
759   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
760   if (dataiterator_step(&di))
761     {
762       dataiterator_setpos_parent(&di);
763       filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
764       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
765     }
766   dataiterator_free(&di);
767   if (filename && !*chksumtypep)
768     {
769       printf("no %s file checksum!\n", what);
770       filename = 0;
771     }
772   return filename;
773 }
774
775 int
776 repomd_add_ext(Repo *repo, Repodata *data, const char *what)
777 {
778   Pool *pool = repo->pool;
779   Dataiterator di;
780   Id chksumtype, handle;
781   const unsigned char *chksum;
782   const char *filename;
783
784   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
785   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
786   if (!dataiterator_step(&di))
787     {
788       dataiterator_free(&di);
789       return 0;
790     }
791   if (!strcmp(what, "prestodelta"))
792     what = "deltainfo";
793   dataiterator_setpos_parent(&di);
794   filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
795   chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, &chksumtype);
796   if (!filename || !chksum)
797     {
798       dataiterator_free(&di);
799       return 0;
800     }
801   handle = repodata_new_handle(data);
802   repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
803   repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
804   if (chksumtype)
805     repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
806   if (!strcmp(what, "deltainfo"))
807     {
808       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
809       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
810     }
811   if (!strcmp(what, "filelists"))
812     {
813       repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
814       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
815     }
816   dataiterator_free(&di);
817   repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
818   return 1;
819 }
820
821
822 /* susetags helpers */
823
824 static inline const char *
825 susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
826 {
827   Pool *pool = repo->pool;
828   Dataiterator di;
829   const char *filename;
830
831   filename = 0;
832   *chksump = 0;
833   *chksumtypep = 0;
834   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
835   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
836   if (dataiterator_step(&di))
837     {
838       dataiterator_setpos_parent(&di);
839       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
840       filename = what;
841     }
842   dataiterator_free(&di);
843   if (filename && !*chksumtypep)
844     {
845       printf("no %s file checksum!\n", what);
846       filename = 0;
847     }
848   return filename;
849 }
850
851 static Id susetags_langtags[] = {
852   SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
853   SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
854   SOLVABLE_EULA, REPOKEY_TYPE_STR,
855   SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
856   SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
857   SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
858   0, 0
859 };
860
861 void
862 susetags_add_ext(Repo *repo, Repodata *data)
863 {
864   Pool *pool = repo->pool;
865   Dataiterator di;
866   char ext[3];
867   Id handle, filechksumtype;
868   const unsigned char *filechksum;
869   int i;
870
871   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
872   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
873   while (dataiterator_step(&di))
874     {
875       if (strncmp(di.kv.str, "packages.", 9) != 0)
876         continue;
877       if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
878         continue;
879       ext[0] = di.kv.str[9];
880       ext[1] = di.kv.str[10];
881       ext[2] = 0;
882       if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
883         continue;
884       handle = repodata_new_handle(data);
885       repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
886       if (filechksumtype)
887         repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
888       if (!strcmp(ext, "DU"))
889         {
890           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
891           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
892         }
893       if (!strcmp(ext, "FL"))
894         {
895           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
896           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
897         }
898       else
899         {
900           for (i = 0; susetags_langtags[i]; i += 2)
901             {
902               repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, susetags_langtags[i], ext, 1));
903               repodata_add_idarray(data, handle, REPOSITORY_KEYS, susetags_langtags[i + 1]);
904             }
905         }
906       repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
907     }
908   dataiterator_free(&di);
909 }
910
911
912 static inline int
913 iscompressed(const char *name)
914 {
915   int l = strlen(name);
916   return l > 3 && !strcmp(name + l - 3, ".gz") ? 1 : 0;
917 }
918
919
920 /* load callback */
921
922 int
923 load_stub(Pool *pool, Repodata *data, void *dp)
924 {
925   const char *filename, *descrdir, *repomdtype;
926   const unsigned char *filechksum;
927   Id filechksumtype;
928   struct repoinfo *cinfo;
929   FILE *fp;
930   Id defvendor;
931   char ext[3];
932
933   cinfo = data->repo->appdata;
934
935   filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
936   if (filename)
937     {
938       /* susetags load */
939       ext[0] = filename[9];
940       ext[1] = filename[10];
941       ext[2] = 0;
942 #if 1
943       printf("[%s:%s", data->repo->name, ext);
944 #endif
945       if (usecachedrepo(data->repo, ext, cinfo->extcookie))
946         {
947           printf(" cached]\n"); fflush(stdout);
948           return 1;
949         }
950 #if 1
951       printf(" loading]\n"); fflush(stdout);
952 #endif
953       defvendor = repo_lookup_id(data->repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
954       descrdir = repo_lookup_str(data->repo, SOLVID_META, SUSETAGS_DESCRDIR);
955       if (!descrdir)
956         descrdir = "suse/setup/descr";
957       filechksumtype = 0;
958       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
959       if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype)) == 0)
960         return 0;
961       repo_add_susetags(data->repo, fp, defvendor, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
962       fclose(fp);
963       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
964       return 1;
965     }
966
967   repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
968   if (repomdtype)
969     {
970       if (!strcmp(repomdtype, "filelists"))
971         strcpy(ext, "FL");
972       else if (!strcmp(repomdtype, "deltainfo"))
973         strcpy(ext, "DL");
974       else
975         return 0;
976 #if 1
977       printf("[%s:%s", data->repo->name, ext);
978 #endif
979       if (usecachedrepo(data->repo, ext, cinfo->extcookie))
980         {
981           printf(" cached]\n");fflush(stdout);
982           return 1;
983         }
984       printf(" loading]\n"); fflush(stdout);
985       filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
986       filechksumtype = 0;
987       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
988       if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype)) == 0)
989         return 0;
990       if (!strcmp(ext, "FL"))
991         repo_add_rpmmd(data->repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
992       else if (!strcmp(ext, "DL"))
993         repo_add_deltainfoxml(data->repo, fp, REPO_USE_LOADING);
994       fclose(fp);
995       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
996       return 1;
997     }
998
999   return 0;
1000 }
1001
1002 void
1003 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
1004 {
1005   Repo *repo;
1006   struct repoinfo *cinfo;
1007   int i;
1008   FILE *fp;
1009   FILE *sigfp;
1010   const char *filename;
1011   const unsigned char *filechksum;
1012   Id filechksumtype;
1013   const char *descrdir;
1014   int defvendor;
1015   struct stat stb;
1016   unsigned char cookie[32];
1017   Pool *sigpool = 0;
1018   Repodata *data;
1019
1020   repo = repo_create(pool, "@System");
1021   printf("rpm database:");
1022   if (stat("/var/lib/rpm/Packages", &stb))
1023     memset(&stb, 0, sizeof(&stb));
1024   calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cookie);
1025   if (usecachedrepo(repo, 0, cookie))
1026     printf(" cached\n");
1027   else
1028     {
1029       FILE *ofp;
1030       printf(" reading\n");
1031       int done = 0;
1032
1033 #ifdef PRODUCTS_PATH
1034       repo_add_products(repo, PRODUCTS_PATH, 0, REPO_NO_INTERNALIZE);
1035 #endif
1036       if ((ofp = fopen(calccachepath(repo, 0), "r")) != 0)
1037         {
1038           Repo *ref = repo_create(pool, "@System.old");
1039           if (!repo_add_solv(ref, ofp))
1040             {
1041               repo_add_rpmdb(repo, ref, 0, REPO_REUSE_REPODATA);
1042               done = 1;
1043             }
1044           fclose(ofp);
1045           repo_free(ref, 1);
1046         }
1047       if (!done)
1048         repo_add_rpmdb(repo, 0, 0, REPO_REUSE_REPODATA);
1049       writecachedrepo(repo, 0, 0, cookie);
1050     }
1051   pool_set_installed(pool, repo);
1052
1053   for (i = 0; i < nrepoinfos; i++)
1054     {
1055       cinfo = repoinfos + i;
1056       if (!cinfo->enabled)
1057         continue;
1058
1059       repo = repo_create(pool, cinfo->alias);
1060       cinfo->repo = repo;
1061       repo->appdata = cinfo;
1062       repo->priority = 99 - cinfo->priority;
1063
1064       if (!cinfo->autorefresh && usecachedrepo(repo, 0, 0))
1065         {
1066           printf("repo '%s':", cinfo->alias);
1067           printf(" cached\n");
1068           continue;
1069         }
1070       switch (cinfo->type)
1071         {
1072         case TYPE_RPMMD:
1073           printf("rpmmd repo '%s':", cinfo->alias);
1074           fflush(stdout);
1075           if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0)) == 0)
1076             {
1077               printf(" no repomd.xml file, skipped\n");
1078               repo_free(repo, 1);
1079               cinfo->repo = 0;
1080               break;
1081             }
1082           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cookie);
1083           if (usecachedrepo(repo, 0, cookie))
1084             {
1085               printf(" cached\n");
1086               fclose(fp);
1087               break;
1088             }
1089           sigfp = curlfopen(cinfo, "repodata/repomd.xml.asc", 0, 0, 0);
1090 #ifndef FEDORA
1091           if (!sigfp)
1092             {
1093               printf(" unsigned, skipped\n");
1094               fclose(fp);
1095               break;
1096             }
1097 #endif
1098           if (sigfp)
1099             {
1100               if (!sigpool)
1101                 sigpool = read_sigs();
1102               if (!checksig(sigpool, fp, sigfp))
1103                 {
1104                   printf(" checksig failed, skipped\n");
1105                   fclose(sigfp);
1106                   fclose(fp);
1107                   break;
1108                 }
1109               fclose(sigfp);
1110             }
1111           repo_add_repomdxml(repo, fp, 0);
1112           fclose(fp);
1113           printf(" reading\n");
1114           filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
1115           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype)) != 0)
1116             {
1117               repo_add_rpmmd(repo, fp, 0, 0);
1118               fclose(fp);
1119             }
1120
1121           filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
1122           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype)) != 0)
1123             {
1124               repo_add_updateinfoxml(repo, fp, 0);
1125               fclose(fp);
1126             }
1127
1128           data = repo_last_repodata(repo);
1129           if (!repomd_add_ext(repo, data, "deltainfo"))
1130             repomd_add_ext(repo, data, "prestodelta");
1131           repomd_add_ext(repo, data, "filelists");
1132           repodata_internalize(data);
1133           writecachedrepo(repo, data, 0, cookie);
1134           repodata_create_stubs(data);
1135           break;
1136
1137         case TYPE_SUSETAGS:
1138           printf("susetags repo '%s':", cinfo->alias);
1139           fflush(stdout);
1140           descrdir = 0;
1141           defvendor = 0;
1142           if ((fp = curlfopen(cinfo, "content", 0, 0, 0)) == 0)
1143             {
1144               printf(" no content file, skipped\n");
1145               repo_free(repo, 1);
1146               cinfo->repo = 0;
1147               break;
1148             }
1149           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cookie);
1150           if (usecachedrepo(repo, 0, cookie))
1151             {
1152               printf(" cached\n");
1153               fclose(fp);
1154               break;
1155             }
1156           sigfp = curlfopen(cinfo, "content.asc", 0, 0, 0);
1157           if (!sigfp)
1158             {
1159               printf(" unsigned, skipped\n");
1160               fclose(fp);
1161               break;
1162             }
1163           if (sigfp)
1164             {
1165               if (!sigpool)
1166                 sigpool = read_sigs();
1167               if (!checksig(sigpool, fp, sigfp))
1168                 {
1169                   printf(" checksig failed, skipped\n");
1170                   fclose(sigfp);
1171                   fclose(fp);
1172                   break;
1173                 }
1174               fclose(sigfp);
1175             }
1176           repo_add_content(repo, fp, 0);
1177           fclose(fp);
1178           defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1179           descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1180           if (!descrdir)
1181             descrdir = "suse/setup/descr";
1182           filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
1183           if (!filename)
1184             filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
1185           if (!filename)
1186             {
1187               printf(" no packages file entry, skipped\n");
1188               break;
1189             }
1190           printf(" reading\n");
1191           if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype)) == 0)
1192             break;
1193           repo_add_susetags(repo, fp, defvendor, 0, 0);
1194           fclose(fp);
1195           data = repo_last_repodata(repo);
1196           susetags_add_ext(repo, data);
1197           repodata_internalize(data);
1198           writecachedrepo(repo, data, 0, cookie);
1199           repodata_create_stubs(data);
1200           break;
1201         default:
1202           printf("unsupported repo '%s': skipped\n", cinfo->alias);
1203           repo_free(repo, 1);
1204           cinfo->repo = 0;
1205           break;
1206         }
1207     }
1208   if (sigpool)
1209     pool_free(sigpool);
1210 }
1211
1212
1213 int
1214 str2archid(Pool *pool, char *arch)
1215 {
1216   Id id;
1217   if (!*arch)
1218     return 0;
1219   id = str2id(pool, arch, 0);
1220   if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
1221     return id;
1222   if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
1223     return 0;
1224   return id;
1225 }
1226
1227 int
1228 depglob(Pool *pool, char *name, Queue *job)
1229 {
1230   Id p, pp;
1231   Id id = str2id(pool, name, 0);
1232   int i, match = 0;
1233
1234   if (id)
1235     {
1236       FOR_PROVIDES(p, pp, id)
1237         {
1238           Solvable *s = pool->solvables + p;
1239           match = 1;
1240           if (s->name == id)
1241             {
1242               queue_push2(job, SOLVER_SOLVABLE_NAME, id);
1243               return 1;
1244             }
1245         }
1246       if (match)
1247         {
1248           printf("[using capability match for '%s']\n", name);
1249           queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
1250           return 1;
1251         }
1252     }
1253   if (strpbrk(name, "[*?") == 0)
1254     return 0;
1255   /* looks like a name glob. hard work. */
1256   for (p = 1; p < pool->nsolvables; p++)
1257     {
1258       Solvable *s = pool->solvables + p;
1259       if (!s->repo || !pool_installable(pool, s))
1260         continue;
1261       id = s->name;
1262       if (fnmatch(name, id2str(pool, id), 0) == 0)
1263         {
1264           for (i = 0; i < job->count; i += 2)
1265             if (job->elements[i] == SOLVER_SOLVABLE_NAME && job->elements[i + 1] == id)
1266               break;
1267           if (i == job->count)
1268             queue_push2(job, SOLVER_SOLVABLE_NAME, id);
1269           match = 1;
1270         }
1271     }
1272   if (match)
1273     return 1;
1274   /* looks like a dep glob. really hard work. */
1275   for (id = 1; id < pool->ss.nstrings; id++)
1276     {
1277       if (!pool->whatprovides[id])
1278         continue;
1279       if (fnmatch(name, id2str(pool, id), 0) == 0)
1280         {
1281           if (!match)
1282             printf("[using capability match for '%s']\n", name);
1283           for (i = 0; i < job->count; i += 2)
1284             if (job->elements[i] == SOLVER_SOLVABLE_PROVIDES && job->elements[i + 1] == id)
1285               break;
1286           if (i == job->count)
1287             queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
1288           match = 1;
1289         }
1290     }
1291   if (match)
1292     return 1;
1293   return 0;
1294 }
1295
1296 void
1297 addrelation(Pool *pool, Queue *job, int flags, Id evr)
1298 {
1299   int i;
1300   for (i = 0; i < job->count; i += 2)
1301     {
1302       if (job->elements[i] != SOLVER_SOLVABLE_NAME && job->elements[i] != SOLVER_SOLVABLE_PROVIDES)
1303         continue;
1304       job->elements[i + 1] = rel2id(pool, job->elements[i + 1], evr, flags, 1);
1305     }
1306 }
1307
1308 int
1309 limitevr(Pool *pool, char *evr, Queue *job, Id archid)
1310 {
1311   Queue mq;
1312   Id p, pp, evrid;
1313   int matched = 0;
1314   int i, j;
1315   Solvable *s;
1316
1317   queue_init(&mq);
1318   for (i = 0; i < job->count; i += 2)
1319     {
1320       queue_empty(&mq);
1321       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
1322         {
1323           s = pool_id2solvable(pool, p);
1324           if (archid && s->arch != archid)
1325             continue;
1326           if (evrcmp_str(pool, id2str(pool, s->evr), evr, EVRCMP_MATCH) == 0)
1327              queue_push(&mq, p);
1328         }
1329       if (mq.count)
1330         {
1331           if (!matched && i)
1332             {
1333               queue_deleten(job, 0, i);
1334               i = 0;
1335             }
1336           matched = 1;
1337           /* if all solvables have the same evr */
1338           s = pool_id2solvable(pool, mq.elements[0]);
1339           evrid = s->evr;
1340           for (j = 0; j < mq.count; j++)
1341             {
1342               s = pool_id2solvable(pool, mq.elements[j]);
1343               if (s->evr != evrid)
1344                 break;
1345             }
1346           if (j == mq.count && j > 1)
1347             {
1348               prune_to_best_arch(pool, &mq);
1349               // prune_to_highest_prio(pool, &mq);
1350               mq.count = 1;
1351             }
1352           if (mq.count > 1)
1353             {
1354               job->elements[i] = SOLVER_SOLVABLE_ONE_OF;
1355               job->elements[i + 1] = pool_queuetowhatprovides(pool, &mq);
1356             }
1357           else
1358             {
1359               job->elements[i] = SOLVER_SOLVABLE;
1360               job->elements[i + 1] = mq.elements[0];
1361             }
1362         }
1363       else if (matched)
1364         {
1365           queue_deleten(job, i, 2);
1366           i -= 2;
1367         }
1368     }
1369   queue_free(&mq);
1370   if (matched)
1371     return 1;
1372   if (!archid)
1373     {
1374       char *r;
1375       if ((r = strrchr(evr, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1376         {
1377           *r = 0;
1378           if (limitevr(pool, evr, job, archid))
1379             {
1380               *r = '.';
1381               return 1;
1382             }
1383           *r = '.';
1384         }
1385     }
1386   return 0;
1387 }
1388
1389 void
1390 mkselect(Pool *pool, char *name, Queue *job)
1391 {
1392   char *r, *r2;
1393   Id archid;
1394
1395   if ((r = strpbrk(name, "<=>")) != 0)
1396     {
1397       /* relation case, support:
1398        * depglob rel
1399        * depglob.rpm rel
1400        */
1401       int rflags = 0;
1402       int nend = r - name;
1403       for (; *r; r++)
1404         {
1405           if (*r == '<')
1406             rflags |= REL_LT;
1407           else if (*r == '=')
1408             rflags |= REL_EQ;
1409           else if (*r == '>')
1410             rflags |= REL_GT;
1411           else
1412             break;
1413         }
1414       while (*r && *r == ' ' && *r == '\t')
1415         r++;
1416       while (nend && (name[nend - 1] == ' ' || name[nend -1 ] == '\t'))
1417         nend--;
1418       name[nend] = 0;
1419       if (!*name || !*r)
1420         {
1421           fprintf(stderr, "bad relation\n");
1422           exit(1);
1423         }
1424       if (depglob(pool, name, job))
1425         {
1426           addrelation(pool, job, rflags, str2id(pool, r, 1));
1427           return;
1428         }
1429       if ((r2 = strrchr(name, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
1430         {
1431           *r2 = 0;
1432           if (depglob(pool, name, job))
1433             {
1434               *r2 = '.';
1435               addrelation(pool, job, REL_ARCH, archid);
1436               addrelation(pool, job, rflags, str2id(pool, r, 1));
1437               return;
1438             }
1439           *r2 = '.';
1440         }
1441     }
1442   else
1443     {
1444       /* no relation case, support:
1445        * depglob
1446        * depglob.arch
1447        * depglob-version-release
1448        * depglob-version-release.arch
1449        */
1450       if (depglob(pool, name, job))
1451         return;
1452       archid = 0;
1453       if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1454         {
1455           *r = 0;
1456           if (depglob(pool, name, job))
1457             {
1458               *r = '.';
1459               addrelation(pool, job, REL_ARCH, archid);
1460               return;
1461             }
1462           *r = '.';
1463         }
1464       if ((r = strrchr(name, '-')) != 0)
1465         {
1466           *r = 0;
1467           if (depglob(pool, name, job))
1468             {
1469               /* have just the version */
1470               *r = '-';
1471               if (limitevr(pool, r + 1, job, 0))
1472                 return;
1473             }
1474           if ((r2 = strrchr(name, '-')) != 0)
1475             {
1476               *r = '-';
1477               *r2 = 0;
1478               if (depglob(pool, name, job))
1479                 {
1480                   *r2 = '-';
1481                   if (limitevr(pool, r2 + 1, job, 0))
1482                     return;
1483                 }
1484               *r2 = '-';
1485             }
1486           *r = '-';
1487         }
1488     }
1489   fprintf(stderr, "nothing matches %s\n", name);
1490   exit(0);
1491 }
1492
1493
1494 int
1495 yesno(const char *str)
1496 {
1497   char inbuf[128], *ip;
1498
1499   for (;;)
1500     {
1501       printf("%s", str);
1502       fflush(stdout);
1503       *inbuf = 0;
1504       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1505         {
1506           printf("Abort.\n");
1507           exit(1);
1508         }
1509       while (*ip == ' ' || *ip == '\t')
1510         ip++;
1511       if (*ip == 'q')
1512         {
1513           printf("Abort.\n");
1514           exit(1);
1515         }
1516       if (*ip == 'y' || *ip == 'n')
1517         return *ip == 'y' ? 1 : 0;
1518     }
1519 }
1520
1521 struct fcstate {
1522   FILE **newpkgsfps;
1523   Queue *checkq;
1524   int newpkgscnt;
1525   void *rpmdbstate;
1526 };
1527
1528 static void *
1529 fc_cb(Pool *pool, Id p, void *cbdata)
1530 {
1531   struct fcstate *fcstate = cbdata;
1532   Solvable *s;
1533   Id rpmdbid;
1534   int i;
1535   FILE *fp;
1536
1537   if (!p)
1538     {
1539       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
1540       return 0;
1541     }
1542   s = pool_id2solvable(pool, p);
1543   if (pool->installed && s->repo == pool->installed)
1544     {
1545       if (!s->repo->rpmdbid)
1546         return 0;
1547       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
1548       if (!rpmdbid)
1549         return 0;
1550        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
1551     }
1552   for (i = 0; i < fcstate->newpkgscnt; i++)
1553     if (fcstate->checkq->elements[i] == p)
1554       break;
1555   if (i == fcstate->newpkgscnt)
1556     return 0;
1557   fp = fcstate->newpkgsfps[i];
1558   if (!fp)
1559     return 0;
1560   rewind(fp);
1561   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
1562 }
1563
1564 void
1565 runrpm(const char *arg, const char *name, int dupfd3)
1566 {
1567   pid_t pid;
1568   int status;
1569
1570   if ((pid = fork()) == (pid_t)-1)
1571     {
1572       perror("fork");
1573       exit(1);
1574     }
1575   if (pid == 0)
1576     {
1577       if (dupfd3 != -1 && dupfd3 != 3)
1578         {
1579           dup2(dupfd3, 3);
1580           close(dupfd3);
1581         }
1582       if (dupfd3 != -1)
1583         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
1584       if (strcmp(arg, "-e") == 0)
1585         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
1586       else
1587         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
1588       perror("rpm");
1589       _exit(0);
1590     }
1591   while (waitpid(pid, &status, 0) != pid)
1592     ;
1593   if (status)
1594     {
1595       printf("rpm failed\n");
1596       exit(1);
1597     }
1598 }
1599
1600 static Id
1601 nscallback(Pool *pool, void *data, Id name, Id evr)
1602 {
1603   if (name == NAMESPACE_PRODUCTBUDDY)
1604     {    
1605       /* SUSE specific hack: each product has an associated rpm */
1606       Solvable *s = pool->solvables + evr; 
1607       Id p, pp, cap; 
1608       
1609       cap = str2id(pool, pool_tmpjoin(pool, "product(", id2str(pool, s->name) + 8, ")"), 0);
1610       if (!cap)
1611         return 0;
1612       cap = rel2id(pool, cap, s->evr, REL_EQ, 0);
1613       if (!cap)
1614         return 0;
1615       FOR_PROVIDES(p, pp, cap) 
1616         {
1617           Solvable *ps = pool->solvables + p; 
1618           if (ps->repo == s->repo && ps->arch == s->arch)
1619             break;
1620         }
1621       return p;
1622     }
1623   return 0;
1624 }
1625
1626
1627 int
1628 main(int argc, char **argv)
1629 {
1630   Pool *pool;
1631   Repo *commandlinerepo = 0;
1632   Id p, pp;
1633   struct repoinfo *repoinfos;
1634   int nrepoinfos = 0;
1635   int i, mode, newpkgs;
1636   int needwhatprovidesrefresh;
1637   Queue job, checkq;
1638   Solver *solv = 0;
1639   Transaction *trans;
1640   char inbuf[128], *ip;
1641   int updateall = 0;
1642   int distupgrade = 0;
1643   int patchmode = 0;
1644   FILE **newpkgsfps;
1645   struct fcstate fcstate;
1646
1647   argc--;
1648   argv++;
1649   if (!argv[0])
1650     {
1651       fprintf(stderr, "Usage: solv install|erase|update|show <select>\n");
1652       exit(1);
1653     }
1654   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
1655     mode = SOLVER_INSTALL;
1656   else if (!strcmp(argv[0], "patch"))
1657     {
1658       mode = SOLVER_UPDATE;
1659       patchmode = 1;
1660     }
1661   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
1662     mode = SOLVER_ERASE;
1663   else if (!strcmp(argv[0], "show"))
1664     mode = 0;
1665   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
1666     mode = SOLVER_UPDATE;
1667   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
1668     {
1669       mode = SOLVER_UPDATE;
1670       distupgrade = 1;
1671     }
1672   else
1673     {
1674       fprintf(stderr, "Usage: solv install|erase|update|show <select>\n");
1675       exit(1);
1676     }
1677
1678   pool = pool_create();
1679   pool_setloadcallback(pool, load_stub, 0);
1680   pool->nscallback = nscallback;
1681   // pool_setdebuglevel(pool, 2);
1682   setarch(pool);
1683   repoinfos = read_repoinfos(pool, REPOINFO_PATH, &nrepoinfos);
1684   read_repos(pool, repoinfos, nrepoinfos);
1685   // FOR_REPOS(i, repo)
1686   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
1687   needwhatprovidesrefresh = 1;
1688
1689   queue_init(&job);
1690   for (i = 1; i < argc; i++)
1691     {
1692       if (mode == 0 || mode == SOLVER_INSTALL)
1693         {
1694           int l;
1695           l = strlen(argv[i]);
1696           if (l > 4 && !strcmp(argv[i] + l - 4, ".rpm") && !access(argv[i], R_OK))
1697             {
1698               FILE *fp;
1699               if (!commandlinerepo)
1700                 commandlinerepo = repo_create(pool, "@commandline");
1701               fp = fopen(argv[i], "r");
1702               repo_add_rpms(commandlinerepo, (const char **)argv + i, 1, 0);
1703               queue_push2(&job, SOLVER_SOLVABLE, commandlinerepo->end - 1);
1704               continue;
1705             }
1706         }
1707       if (needwhatprovidesrefresh)
1708         {
1709           pool_addfileprovides(pool);
1710           pool_createwhatprovides(pool);
1711           needwhatprovidesrefresh = 0;
1712         }
1713       mkselect(pool, argv[i], &job);
1714     }
1715   if (needwhatprovidesrefresh)
1716     {
1717       pool_addfileprovides(pool);
1718       pool_createwhatprovides(pool);
1719       needwhatprovidesrefresh = 0;
1720     }
1721   if (!job.count && mode == SOLVER_UPDATE)
1722     updateall = 1;
1723   else if (!job.count)
1724     {
1725       printf("no package matched\n");
1726       exit(1);
1727     }
1728
1729   if (!mode)
1730     {
1731       /* show mode, no solver needed */
1732       for (i = 0; i < job.count; i += 2)
1733         {
1734           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
1735             {
1736               Solvable *s = pool_id2solvable(pool, p);
1737               const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de");
1738               printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
1739               if (sum)
1740                 printf("    %s\n", sum);
1741             }
1742         }
1743       queue_free(&job);
1744       pool_free(pool);
1745       free_repoinfos(repoinfos, nrepoinfos);
1746       exit(0);
1747     }
1748
1749   if (updateall && patchmode)
1750     {
1751       int pruneyou = 0;
1752       Map installedmap;
1753       Solvable *s;
1754
1755       map_init(&installedmap, pool->nsolvables);
1756       if (pool->installed)
1757         FOR_REPO_SOLVABLES(pool->installed, p, s)
1758           MAPSET(&installedmap, p);
1759
1760       /* install all patches */
1761       updateall = 0;
1762       mode = SOLVER_INSTALL;
1763       for (p = 1; p < pool->nsolvables; p++)
1764         {
1765           const char *type;
1766           int r;
1767           Id p2;
1768
1769           s = pool->solvables + p;
1770           if (strncmp(id2str(pool, s->name), "patch:", 6) != 0)
1771             continue;
1772           FOR_PROVIDES(p2, pp, s->name)
1773             {
1774               Solvable *s2 = pool->solvables + p2;
1775               if (s2->name != s->name)
1776                 continue;
1777               r = evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
1778               if (r < 0 || (r == 0 && p > p2))
1779                 break;
1780             }
1781           if (p2)
1782             continue;
1783           type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
1784           if (type && !strcmp(type, "optional"))
1785             continue;
1786           r = solvable_trivial_installable_map(s, &installedmap, 0);
1787           if (r == -1)
1788             continue;
1789           if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
1790             {
1791               if (!pruneyou++)
1792                 queue_empty(&job);
1793             }
1794           else if (pruneyou)
1795             continue;
1796           queue_push2(&job, SOLVER_SOLVABLE, p);
1797         }
1798       map_free(&installedmap);
1799     }
1800
1801   // add mode
1802   for (i = 0; i < job.count; i += 2)
1803     job.elements[i] |= mode;
1804
1805   // multiversion test
1806   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae", 1));
1807   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-base", 1));
1808   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-extra", 1));
1809
1810 rerunsolver:
1811   for (;;)
1812     {
1813       Id problem, solution;
1814       int pcnt, scnt;
1815
1816       solv = solver_create(pool);
1817       solv->ignorealreadyrecommended = 1;
1818       solv->updatesystem = updateall;
1819       solv->dosplitprovides = updateall;
1820       if (updateall && distupgrade)
1821         {
1822           solv->distupgrade = 1;
1823           solv->allowdowngrade = 1;
1824           solv->allowarchchange = 1;
1825           solv->allowvendorchange = 1;
1826         }
1827       // queue_push2(&job, SOLVER_DISTUPGRADE, 3);
1828       solver_solve(solv, &job);
1829       if (!solv->problems.count)
1830         break;
1831       pcnt = solver_problem_count(solv);
1832       printf("Found %d problems:\n", pcnt);
1833       for (problem = 1; problem <= pcnt; problem++)
1834         {
1835           int take = 0;
1836           printf("Problem %d:\n", problem);
1837           solver_printprobleminfo(solv, problem);
1838           printf("\n");
1839           scnt = solver_solution_count(solv, problem);
1840           for (solution = 1; solution <= scnt; solution++)
1841             {
1842               printf("Solution %d:\n", solution);
1843               solver_printsolution(solv, problem, solution);
1844               printf("\n");
1845             }
1846           for (;;)
1847             {
1848               printf("Please choose a solution: ");
1849               fflush(stdout);
1850               *inbuf = 0;
1851               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1852                 {
1853                   printf("Abort.\n");
1854                   exit(1);
1855                 }
1856               while (*ip == ' ' || *ip == '\t')
1857                 ip++;
1858               if (*ip >= '0' && *ip <= '9')
1859                 {
1860                   take = atoi(ip);
1861                   if (take >= 1 && take <= scnt)
1862                     break;
1863                 }
1864               if (*ip == 's')
1865                 {
1866                   take = 0;
1867                   break;
1868                 }
1869               if (*ip == 'q')
1870                 {
1871                   printf("Abort.\n");
1872                   exit(1);
1873                 }
1874             }
1875           if (!take)
1876             continue;
1877           solver_take_solution(solv, problem, take, &job);
1878         }
1879       solver_free(solv);
1880       solv = 0;
1881     }
1882
1883   trans = &solv->trans;
1884   if (!trans->steps.count)
1885     {
1886       printf("Nothing to do.\n");
1887       exit(1);
1888     }
1889   printf("\n");
1890   printf("Transaction summary:\n\n");
1891   solver_printtransaction(solv);
1892
1893 #if 1
1894   if (1)
1895     {
1896       DUChanges duc[4];
1897       int i;
1898
1899       duc[0].path = "/";
1900       duc[1].path = "/usr/share/man";
1901       duc[2].path = "/sbin";
1902       duc[3].path = "/etc";
1903       transaction_calc_duchanges(trans, duc, 4);
1904       for (i = 0; i < 4; i++)
1905         printf("duchanges %s: %d K  %d\n", duc[i].path, duc[i].kbytes, duc[i].files);
1906       printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
1907     }
1908 #endif
1909
1910   if (!yesno("OK to continue (y/n)? "))
1911     {
1912       printf("Abort.\n");
1913       exit(1);
1914     }
1915
1916   queue_init(&checkq);
1917   newpkgs = transaction_installedresult(trans, &checkq);
1918   newpkgsfps = 0;
1919
1920   if (newpkgs)
1921     {
1922       printf("Downloading %d packages\n", newpkgs);
1923       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
1924       for (i = 0; i < newpkgs; i++)
1925         {
1926           unsigned int medianr;
1927           char *loc;
1928           Solvable *s;
1929           struct repoinfo *cinfo;
1930           const unsigned char *chksum;
1931           Id chksumtype;
1932           Dataiterator di;
1933
1934           p = checkq.elements[i];
1935           s = pool_id2solvable(pool, p);
1936           if (s->repo == commandlinerepo)
1937             {
1938               loc = solvable_get_location(s, &medianr);
1939               if (!(newpkgsfps[i] = fopen(loc, "r")))
1940                 {
1941                   perror(loc);
1942                   exit(1);
1943                 }
1944               putchar('.');
1945               continue;
1946             }
1947           cinfo = s->repo->appdata;
1948           if (!cinfo)
1949             {
1950               printf("%s: no repository information\n", s->repo->name);
1951               exit(1);
1952             }
1953           loc = solvable_get_location(s, &medianr);
1954           if (!loc)
1955              continue;
1956
1957           if (pool->installed && pool->installed->nsolvables)
1958             {
1959               /* try a delta first */
1960               dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, id2str(pool, s->name), SEARCH_STRING);
1961               dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
1962               while (dataiterator_step(&di))
1963                 {
1964                   Id baseevr, op;
1965
1966                   dataiterator_setpos_parent(&di);
1967                   if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
1968                       pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
1969                     continue;
1970                   baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
1971                   FOR_PROVIDES(op, pp, s->name)
1972                     {
1973                       Solvable *os = pool->solvables + op;
1974                       if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
1975                         break;
1976                     }
1977                   if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
1978                     {
1979                       /* base is installed, run sequence check */
1980                       const char *seqname;
1981                       const char *seqevr;
1982                       const char *seqnum;
1983                       const char *seq;
1984                       const char *dloc;
1985                       FILE *fp;
1986                       char cmd[128];
1987                       int newfd;
1988
1989                       seqname = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
1990                       seqevr = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR);
1991                       seqnum = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM);
1992                       seq = pool_tmpjoin(pool, seqname, "-", seqevr);
1993                       seq = pool_tmpjoin(pool, seq, "-", seqnum);
1994                       if (system(pool_tmpjoin(pool, "/usr/bin/applydeltarpm -c -s ", seq, 0)) != 0)
1995                         continue;       /* didn't match */
1996                       /* looks good, download delta */
1997                       chksumtype = 0;
1998                       chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
1999                       if (!chksumtype)
2000                         continue;       /* no way! */
2001                       dloc = pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_DIR);
2002                       dloc = pool_tmpjoin(pool, dloc, "/", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_NAME));
2003                       dloc = pool_tmpjoin(pool, dloc, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_EVR));
2004                       dloc = pool_tmpjoin(pool, dloc, ".", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_SUFFIX));
2005                       if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype)) == 0)
2006                         continue;
2007                       /* got it, now reconstruct */
2008                       newfd = opentmpfile();
2009                       sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
2010                       fcntl(fileno(fp), F_SETFD, 0);
2011                       if (system(cmd))
2012                         {
2013                           close(newfd);
2014                           fclose(fp);
2015                           continue;
2016                         }
2017                       lseek(newfd, 0, SEEK_SET);
2018                       chksumtype = 0;
2019                       chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2020                       if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
2021                         {
2022                           close(newfd);
2023                           fclose(fp);
2024                           continue;
2025                         }
2026                       newpkgsfps[i] = fdopen(newfd, "r");
2027                       fclose(fp);
2028                       break;
2029                     }
2030                 }
2031               dataiterator_free(&di);
2032             }
2033           
2034           if (newpkgsfps[i])
2035             {
2036               putchar('d');
2037               fflush(stdout);
2038               continue;         /* delta worked! */
2039             }
2040           if (cinfo->type == TYPE_SUSETAGS)
2041             {
2042               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
2043               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
2044             }
2045           chksumtype = 0;
2046           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2047           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype)) == 0)
2048             {
2049               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
2050               exit(1);
2051             }
2052           putchar('.');
2053           fflush(stdout);
2054         }
2055       putchar('\n');
2056     }
2057
2058   if (newpkgs)
2059     {
2060       Queue conflicts;
2061
2062       printf("Searching for file conflicts\n");
2063       queue_init(&conflicts);
2064       fcstate.rpmdbstate = 0;
2065       fcstate.newpkgscnt = newpkgs;
2066       fcstate.checkq = &checkq;
2067       fcstate.newpkgsfps = newpkgsfps;
2068       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fc_cb, &fcstate);
2069       if (conflicts.count)
2070         {
2071           printf("\n");
2072           for (i = 0; i < conflicts.count; i += 5)
2073             printf("file %s of package %s conflicts with package %s\n", id2str(pool, conflicts.elements[i]), solvid2str(pool, conflicts.elements[i + 1]), solvid2str(pool, conflicts.elements[i + 3]));
2074           printf("\n");
2075           if (yesno("Re-run solver (y/n/q)? "))
2076             {
2077               for (i = 0; i < newpkgs; i++)
2078                 if (newpkgsfps[i])
2079                   fclose(newpkgsfps[i]);
2080               newpkgsfps = sat_free(newpkgsfps);
2081               solver_free(solv);
2082               pool_add_fileconflicts_deps(pool, &conflicts);
2083               pool_createwhatprovides(pool);    /* Hmm... */
2084               goto rerunsolver;
2085             }
2086         }
2087       queue_free(&conflicts);
2088     }
2089
2090   printf("Committing transaction:\n\n");
2091   transaction_order(trans, 0);
2092   for (i = 0; i < trans->steps.count; i++)
2093     {
2094       const char *evr, *evrp, *nvra;
2095       Solvable *s;
2096       int j;
2097       FILE *fp;
2098
2099       p = trans->steps.elements[i];
2100       s = pool_id2solvable(pool, p);
2101       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
2102       switch(type)
2103         {
2104         case SOLVER_TRANSACTION_ERASE:
2105           printf("erase %s\n", solvid2str(pool, p));
2106           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
2107             continue;
2108           /* strip epoch from evr */
2109           evr = evrp = id2str(pool, s->evr);
2110           while (*evrp >= '0' && *evrp <= '9')
2111             evrp++;
2112           if (evrp > evr && evrp[0] == ':' && evrp[1])
2113             evr = evrp + 1;
2114           nvra = pool_tmpjoin(pool, id2str(pool, s->name), "-", evr);
2115           nvra = pool_tmpjoin(pool, nvra, ".", id2str(pool, s->arch));
2116           runrpm("-e", nvra, -1);       /* to bad that --querybynumber doesn't work */
2117           break;
2118         case SOLVER_TRANSACTION_INSTALL:
2119         case SOLVER_TRANSACTION_MULTIINSTALL:
2120           printf("install %s\n", solvid2str(pool, p));
2121           for (j = 0; j < newpkgs; j++)
2122             if (checkq.elements[j] == p)
2123               break;
2124           fp = j < newpkgs ? newpkgsfps[j] : 0;
2125           if (!fp)
2126             continue;
2127           rewind(fp);
2128           lseek(fileno(fp), 0, SEEK_SET);
2129           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
2130           fclose(fp);
2131           newpkgsfps[j] = 0;
2132           break;
2133         default:
2134           break;
2135         }
2136     }
2137
2138   for (i = 0; i < newpkgs; i++)
2139     if (newpkgsfps[i])
2140       fclose(newpkgsfps[i]);
2141   sat_free(newpkgsfps);
2142   queue_free(&checkq);
2143   solver_free(solv);
2144   queue_free(&job);
2145   pool_free(pool);
2146   free_repoinfos(repoinfos, nrepoinfos);
2147   exit(0);
2148 }