- just search the installed repo in rm mode
[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
1254   if (strpbrk(name, "[*?") == 0)
1255     return 0;
1256
1257   /* looks like a name glob. hard work. */
1258   for (p = 1; p < pool->nsolvables; p++)
1259     {
1260       Solvable *s = pool->solvables + p;
1261       if (!s->repo || !pool_installable(pool, s))
1262         continue;
1263       id = s->name;
1264       if (fnmatch(name, id2str(pool, id), 0) == 0)
1265         {
1266           for (i = 0; i < job->count; i += 2)
1267             if (job->elements[i] == SOLVER_SOLVABLE_NAME && job->elements[i + 1] == id)
1268               break;
1269           if (i == job->count)
1270             queue_push2(job, SOLVER_SOLVABLE_NAME, id);
1271           match = 1;
1272         }
1273     }
1274   if (match)
1275     return 1;
1276   /* looks like a dep glob. really hard work. */
1277   for (id = 1; id < pool->ss.nstrings; id++)
1278     {
1279       if (!pool->whatprovides[id])
1280         continue;
1281       if (fnmatch(name, id2str(pool, id), 0) == 0)
1282         {
1283           if (!match)
1284             printf("[using capability match for '%s']\n", name);
1285           for (i = 0; i < job->count; i += 2)
1286             if (job->elements[i] == SOLVER_SOLVABLE_PROVIDES && job->elements[i + 1] == id)
1287               break;
1288           if (i == job->count)
1289             queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
1290           match = 1;
1291         }
1292     }
1293   if (match)
1294     return 1;
1295   return 0;
1296 }
1297
1298 void
1299 addrelation(Pool *pool, Queue *job, int flags, Id evr)
1300 {
1301   int i;
1302   for (i = 0; i < job->count; i += 2)
1303     {
1304       if (job->elements[i] != SOLVER_SOLVABLE_NAME && job->elements[i] != SOLVER_SOLVABLE_PROVIDES)
1305         continue;
1306       job->elements[i + 1] = rel2id(pool, job->elements[i + 1], evr, flags, 1);
1307     }
1308 }
1309
1310 int
1311 limitevr(Pool *pool, char *evr, Queue *job, Id archid)
1312 {
1313   Queue mq;
1314   Id p, pp, evrid;
1315   int matched = 0;
1316   int i, j;
1317   Solvable *s;
1318
1319   queue_init(&mq);
1320   for (i = 0; i < job->count; i += 2)
1321     {
1322       queue_empty(&mq);
1323       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
1324         {
1325           s = pool_id2solvable(pool, p);
1326           if (archid && s->arch != archid)
1327             continue;
1328           if (evrcmp_str(pool, id2str(pool, s->evr), evr, EVRCMP_MATCH) == 0)
1329              queue_push(&mq, p);
1330         }
1331       if (mq.count)
1332         {
1333           if (!matched && i)
1334             {
1335               queue_deleten(job, 0, i);
1336               i = 0;
1337             }
1338           matched = 1;
1339           /* if all solvables have the same evr */
1340           s = pool_id2solvable(pool, mq.elements[0]);
1341           evrid = s->evr;
1342           for (j = 0; j < mq.count; j++)
1343             {
1344               s = pool_id2solvable(pool, mq.elements[j]);
1345               if (s->evr != evrid)
1346                 break;
1347             }
1348           if (j == mq.count && j > 1)
1349             {
1350               prune_to_best_arch(pool, &mq);
1351               // prune_to_highest_prio(pool, &mq);
1352               mq.count = 1;
1353             }
1354           if (mq.count > 1)
1355             {
1356               job->elements[i] = SOLVER_SOLVABLE_ONE_OF;
1357               job->elements[i + 1] = pool_queuetowhatprovides(pool, &mq);
1358             }
1359           else
1360             {
1361               job->elements[i] = SOLVER_SOLVABLE;
1362               job->elements[i + 1] = mq.elements[0];
1363             }
1364         }
1365       else if (matched)
1366         {
1367           queue_deleten(job, i, 2);
1368           i -= 2;
1369         }
1370     }
1371   queue_free(&mq);
1372   if (matched)
1373     return 1;
1374   if (!archid)
1375     {
1376       char *r;
1377       if ((r = strrchr(evr, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1378         {
1379           *r = 0;
1380           if (limitevr(pool, evr, job, archid))
1381             {
1382               *r = '.';
1383               return 1;
1384             }
1385           *r = '.';
1386         }
1387     }
1388   return 0;
1389 }
1390
1391 void
1392 mkselect(Pool *pool, int mode, char *name, Queue *job)
1393 {
1394   char *r, *r2;
1395   Id archid;
1396
1397   if (*name == '/')
1398     {
1399       Dataiterator di;
1400       Queue q;
1401       int match = 0;
1402
1403       queue_init(&q);
1404       dataiterator_init(&di, pool, mode == SOLVER_ERASE ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, SEARCH_STRING|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
1405       while (dataiterator_step(&di))
1406         {
1407           Solvable *s = pool->solvables + di.solvid;
1408           if (!s->repo || !pool_installable(pool, s))
1409             continue;
1410           queue_push(&q, di.solvid);
1411           dataiterator_skip_solvable(&di);
1412         }
1413       dataiterator_free(&di);
1414       if (q.count)
1415         {
1416           printf("[using file list match for '%s']\n", name);
1417           match = 1;
1418           if (q.count > 1)
1419             queue_push2(job, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
1420           else
1421             queue_push2(job, SOLVER_SOLVABLE, q.elements[0]);
1422         }
1423       queue_free(&q);
1424       if (match)
1425         return;
1426     }
1427   if ((r = strpbrk(name, "<=>")) != 0)
1428     {
1429       /* relation case, support:
1430        * depglob rel
1431        * depglob.rpm rel
1432        */
1433       int rflags = 0;
1434       int nend = r - name;
1435       for (; *r; r++)
1436         {
1437           if (*r == '<')
1438             rflags |= REL_LT;
1439           else if (*r == '=')
1440             rflags |= REL_EQ;
1441           else if (*r == '>')
1442             rflags |= REL_GT;
1443           else
1444             break;
1445         }
1446       while (*r && *r == ' ' && *r == '\t')
1447         r++;
1448       while (nend && (name[nend - 1] == ' ' || name[nend -1 ] == '\t'))
1449         nend--;
1450       name[nend] = 0;
1451       if (!*name || !*r)
1452         {
1453           fprintf(stderr, "bad relation\n");
1454           exit(1);
1455         }
1456       if (depglob(pool, name, job))
1457         {
1458           addrelation(pool, job, rflags, str2id(pool, r, 1));
1459           return;
1460         }
1461       if ((r2 = strrchr(name, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
1462         {
1463           *r2 = 0;
1464           if (depglob(pool, name, job))
1465             {
1466               *r2 = '.';
1467               addrelation(pool, job, REL_ARCH, archid);
1468               addrelation(pool, job, rflags, str2id(pool, r, 1));
1469               return;
1470             }
1471           *r2 = '.';
1472         }
1473     }
1474   else
1475     {
1476       /* no relation case, support:
1477        * depglob
1478        * depglob.arch
1479        * depglob-version-release
1480        * depglob-version-release.arch
1481        */
1482       if (depglob(pool, name, job))
1483         return;
1484       archid = 0;
1485       if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1486         {
1487           *r = 0;
1488           if (depglob(pool, name, job))
1489             {
1490               *r = '.';
1491               addrelation(pool, job, REL_ARCH, archid);
1492               return;
1493             }
1494           *r = '.';
1495         }
1496       if ((r = strrchr(name, '-')) != 0)
1497         {
1498           *r = 0;
1499           if (depglob(pool, name, job))
1500             {
1501               /* have just the version */
1502               *r = '-';
1503               if (limitevr(pool, r + 1, job, 0))
1504                 return;
1505             }
1506           if ((r2 = strrchr(name, '-')) != 0)
1507             {
1508               *r = '-';
1509               *r2 = 0;
1510               if (depglob(pool, name, job))
1511                 {
1512                   *r2 = '-';
1513                   if (limitevr(pool, r2 + 1, job, 0))
1514                     return;
1515                 }
1516               *r2 = '-';
1517             }
1518           *r = '-';
1519         }
1520     }
1521   fprintf(stderr, "nothing matches '%s'\n", name);
1522   exit(1);
1523 }
1524
1525
1526 int
1527 yesno(const char *str)
1528 {
1529   char inbuf[128], *ip;
1530
1531   for (;;)
1532     {
1533       printf("%s", str);
1534       fflush(stdout);
1535       *inbuf = 0;
1536       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1537         {
1538           printf("Abort.\n");
1539           exit(1);
1540         }
1541       while (*ip == ' ' || *ip == '\t')
1542         ip++;
1543       if (*ip == 'q')
1544         {
1545           printf("Abort.\n");
1546           exit(1);
1547         }
1548       if (*ip == 'y' || *ip == 'n')
1549         return *ip == 'y' ? 1 : 0;
1550     }
1551 }
1552
1553 struct fcstate {
1554   FILE **newpkgsfps;
1555   Queue *checkq;
1556   int newpkgscnt;
1557   void *rpmdbstate;
1558 };
1559
1560 static void *
1561 fc_cb(Pool *pool, Id p, void *cbdata)
1562 {
1563   struct fcstate *fcstate = cbdata;
1564   Solvable *s;
1565   Id rpmdbid;
1566   int i;
1567   FILE *fp;
1568
1569   if (!p)
1570     {
1571       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
1572       return 0;
1573     }
1574   s = pool_id2solvable(pool, p);
1575   if (pool->installed && s->repo == pool->installed)
1576     {
1577       if (!s->repo->rpmdbid)
1578         return 0;
1579       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
1580       if (!rpmdbid)
1581         return 0;
1582        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
1583     }
1584   for (i = 0; i < fcstate->newpkgscnt; i++)
1585     if (fcstate->checkq->elements[i] == p)
1586       break;
1587   if (i == fcstate->newpkgscnt)
1588     return 0;
1589   fp = fcstate->newpkgsfps[i];
1590   if (!fp)
1591     return 0;
1592   rewind(fp);
1593   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
1594 }
1595
1596 void
1597 runrpm(const char *arg, const char *name, int dupfd3)
1598 {
1599   pid_t pid;
1600   int status;
1601
1602   if ((pid = fork()) == (pid_t)-1)
1603     {
1604       perror("fork");
1605       exit(1);
1606     }
1607   if (pid == 0)
1608     {
1609       if (dupfd3 != -1 && dupfd3 != 3)
1610         {
1611           dup2(dupfd3, 3);
1612           close(dupfd3);
1613         }
1614       if (dupfd3 != -1)
1615         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
1616       if (strcmp(arg, "-e") == 0)
1617         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
1618       else
1619         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
1620       perror("rpm");
1621       _exit(0);
1622     }
1623   while (waitpid(pid, &status, 0) != pid)
1624     ;
1625   if (status)
1626     {
1627       printf("rpm failed\n");
1628       exit(1);
1629     }
1630 }
1631
1632 static Id
1633 nscallback(Pool *pool, void *data, Id name, Id evr)
1634 {
1635   if (name == NAMESPACE_PRODUCTBUDDY)
1636     {    
1637       /* SUSE specific hack: each product has an associated rpm */
1638       Solvable *s = pool->solvables + evr; 
1639       Id p, pp, cap; 
1640       
1641       cap = str2id(pool, pool_tmpjoin(pool, "product(", id2str(pool, s->name) + 8, ")"), 0);
1642       if (!cap)
1643         return 0;
1644       cap = rel2id(pool, cap, s->evr, REL_EQ, 0);
1645       if (!cap)
1646         return 0;
1647       FOR_PROVIDES(p, pp, cap) 
1648         {
1649           Solvable *ps = pool->solvables + p; 
1650           if (ps->repo == s->repo && ps->arch == s->arch)
1651             break;
1652         }
1653       return p;
1654     }
1655   return 0;
1656 }
1657
1658
1659 int
1660 main(int argc, char **argv)
1661 {
1662   Pool *pool;
1663   Repo *commandlinerepo = 0;
1664   Id *commandlinepkgs = 0;
1665   Id p, pp;
1666   struct repoinfo *repoinfos;
1667   int nrepoinfos = 0;
1668   int i, mode, newpkgs;
1669   Queue job, checkq;
1670   Solver *solv = 0;
1671   Transaction *trans;
1672   char inbuf[128], *ip;
1673   int updateall = 0;
1674   int distupgrade = 0;
1675   int patchmode = 0;
1676   FILE **newpkgsfps;
1677   struct fcstate fcstate;
1678
1679   argc--;
1680   argv++;
1681   if (!argv[0])
1682     {
1683       fprintf(stderr, "Usage: solv install|erase|update|show <select>\n");
1684       exit(1);
1685     }
1686   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
1687     mode = SOLVER_INSTALL;
1688   else if (!strcmp(argv[0], "patch"))
1689     {
1690       mode = SOLVER_UPDATE;
1691       patchmode = 1;
1692     }
1693   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
1694     mode = SOLVER_ERASE;
1695   else if (!strcmp(argv[0], "show"))
1696     mode = 0;
1697   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
1698     mode = SOLVER_UPDATE;
1699   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
1700     {
1701       mode = SOLVER_UPDATE;
1702       distupgrade = 1;
1703     }
1704   else
1705     {
1706       fprintf(stderr, "Usage: solv install|erase|update|show <select>\n");
1707       exit(1);
1708     }
1709
1710   pool = pool_create();
1711   pool_setloadcallback(pool, load_stub, 0);
1712   pool->nscallback = nscallback;
1713   // pool_setdebuglevel(pool, 2);
1714   setarch(pool);
1715   repoinfos = read_repoinfos(pool, REPOINFO_PATH, &nrepoinfos);
1716   read_repos(pool, repoinfos, nrepoinfos);
1717
1718   if (mode == 0 || mode == SOLVER_INSTALL)
1719     {
1720       for (i = 1; i < argc; i++)
1721         {
1722           int l;
1723           l = strlen(argv[i]);
1724           if (l <= 4 || strcmp(argv[i] + l - 4, ".rpm"))
1725             continue;
1726           if (access(argv[i], R_OK))
1727             {
1728               perror(argv[i]);
1729               exit(1);
1730             }
1731           if (!commandlinepkgs)
1732             commandlinepkgs = sat_calloc(argc, sizeof(Id));
1733           if (!commandlinerepo)
1734             commandlinerepo = repo_create(pool, "@commandline");
1735           repo_add_rpms(commandlinerepo, (const char **)argv + i, 1, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
1736           commandlinepkgs[i] = commandlinerepo->end - 1;
1737         }
1738       if (commandlinerepo)
1739         repo_internalize(commandlinerepo);
1740     }
1741
1742   // FOR_REPOS(i, repo)
1743   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
1744   pool_addfileprovides(pool);
1745   pool_createwhatprovides(pool);
1746
1747   queue_init(&job);
1748   for (i = 1; i < argc; i++)
1749     {
1750       if (commandlinepkgs && commandlinepkgs[i])
1751         {
1752           queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
1753           continue;
1754         }
1755       mkselect(pool, mode, argv[i], &job);
1756     }
1757   if (!job.count && mode == SOLVER_UPDATE)
1758     updateall = 1;
1759   else if (!job.count)
1760     {
1761       printf("no package matched\n");
1762       exit(1);
1763     }
1764
1765   if (!mode)
1766     {
1767       /* show mode, no solver needed */
1768       for (i = 0; i < job.count; i += 2)
1769         {
1770           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
1771             {
1772               Solvable *s = pool_id2solvable(pool, p);
1773               const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de");
1774               printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
1775               if (sum)
1776                 printf("    %s\n", sum);
1777             }
1778         }
1779       queue_free(&job);
1780       pool_free(pool);
1781       free_repoinfos(repoinfos, nrepoinfos);
1782       sat_free(commandlinepkgs);
1783       exit(0);
1784     }
1785
1786   if (updateall && patchmode)
1787     {
1788       int pruneyou = 0;
1789       Map installedmap;
1790       Solvable *s;
1791
1792       map_init(&installedmap, pool->nsolvables);
1793       if (pool->installed)
1794         FOR_REPO_SOLVABLES(pool->installed, p, s)
1795           MAPSET(&installedmap, p);
1796
1797       /* install all patches */
1798       updateall = 0;
1799       mode = SOLVER_INSTALL;
1800       for (p = 1; p < pool->nsolvables; p++)
1801         {
1802           const char *type;
1803           int r;
1804           Id p2;
1805
1806           s = pool->solvables + p;
1807           if (strncmp(id2str(pool, s->name), "patch:", 6) != 0)
1808             continue;
1809           FOR_PROVIDES(p2, pp, s->name)
1810             {
1811               Solvable *s2 = pool->solvables + p2;
1812               if (s2->name != s->name)
1813                 continue;
1814               r = evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
1815               if (r < 0 || (r == 0 && p > p2))
1816                 break;
1817             }
1818           if (p2)
1819             continue;
1820           type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
1821           if (type && !strcmp(type, "optional"))
1822             continue;
1823           r = solvable_trivial_installable_map(s, &installedmap, 0);
1824           if (r == -1)
1825             continue;
1826           if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
1827             {
1828               if (!pruneyou++)
1829                 queue_empty(&job);
1830             }
1831           else if (pruneyou)
1832             continue;
1833           queue_push2(&job, SOLVER_SOLVABLE, p);
1834         }
1835       map_free(&installedmap);
1836     }
1837
1838   // add mode
1839   for (i = 0; i < job.count; i += 2)
1840     {
1841       if (mode == SOLVER_UPDATE)
1842         {
1843           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
1844             if (pool->installed && pool->solvables[p].repo == pool->installed)
1845               break;
1846           if (!p)
1847             {
1848               job.elements[i] |= SOLVER_INSTALL;
1849               continue;
1850             }
1851         }
1852       job.elements[i] |= mode;
1853     }
1854
1855   // multiversion test
1856   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae", 1));
1857   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-base", 1));
1858   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-extra", 1));
1859
1860 rerunsolver:
1861   for (;;)
1862     {
1863       Id problem, solution;
1864       int pcnt, scnt;
1865
1866       solv = solver_create(pool);
1867       solv->ignorealreadyrecommended = 1;
1868       solv->updatesystem = updateall;
1869       solv->dosplitprovides = updateall;
1870       if (updateall && distupgrade)
1871         {
1872           solv->distupgrade = 1;
1873           solv->allowdowngrade = 1;
1874           solv->allowarchchange = 1;
1875           solv->allowvendorchange = 1;
1876         }
1877       // queue_push2(&job, SOLVER_DISTUPGRADE, 3);
1878       solver_solve(solv, &job);
1879       if (!solv->problems.count)
1880         break;
1881       pcnt = solver_problem_count(solv);
1882       printf("Found %d problems:\n", pcnt);
1883       for (problem = 1; problem <= pcnt; problem++)
1884         {
1885           int take = 0;
1886           printf("Problem %d:\n", problem);
1887           solver_printprobleminfo(solv, problem);
1888           printf("\n");
1889           scnt = solver_solution_count(solv, problem);
1890           for (solution = 1; solution <= scnt; solution++)
1891             {
1892               printf("Solution %d:\n", solution);
1893               solver_printsolution(solv, problem, solution);
1894               printf("\n");
1895             }
1896           for (;;)
1897             {
1898               printf("Please choose a solution: ");
1899               fflush(stdout);
1900               *inbuf = 0;
1901               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1902                 {
1903                   printf("Abort.\n");
1904                   exit(1);
1905                 }
1906               while (*ip == ' ' || *ip == '\t')
1907                 ip++;
1908               if (*ip >= '0' && *ip <= '9')
1909                 {
1910                   take = atoi(ip);
1911                   if (take >= 1 && take <= scnt)
1912                     break;
1913                 }
1914               if (*ip == 's')
1915                 {
1916                   take = 0;
1917                   break;
1918                 }
1919               if (*ip == 'q')
1920                 {
1921                   printf("Abort.\n");
1922                   exit(1);
1923                 }
1924             }
1925           if (!take)
1926             continue;
1927           solver_take_solution(solv, problem, take, &job);
1928         }
1929       solver_free(solv);
1930       solv = 0;
1931     }
1932
1933   trans = &solv->trans;
1934   if (!trans->steps.count)
1935     {
1936       printf("Nothing to do.\n");
1937       exit(1);
1938     }
1939   printf("\n");
1940   printf("Transaction summary:\n\n");
1941   solver_printtransaction(solv);
1942
1943 #if 1
1944   if (1)
1945     {
1946       DUChanges duc[4];
1947       int i;
1948
1949       duc[0].path = "/";
1950       duc[1].path = "/usr/share/man";
1951       duc[2].path = "/sbin";
1952       duc[3].path = "/etc";
1953       transaction_calc_duchanges(trans, duc, 4);
1954       for (i = 0; i < 4; i++)
1955         printf("duchanges %s: %d K  %d\n", duc[i].path, duc[i].kbytes, duc[i].files);
1956       printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
1957     }
1958 #endif
1959
1960   if (!yesno("OK to continue (y/n)? "))
1961     {
1962       printf("Abort.\n");
1963       exit(1);
1964     }
1965
1966   queue_init(&checkq);
1967   newpkgs = transaction_installedresult(trans, &checkq);
1968   newpkgsfps = 0;
1969
1970   if (newpkgs)
1971     {
1972       printf("Downloading %d packages\n", newpkgs);
1973       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
1974       for (i = 0; i < newpkgs; i++)
1975         {
1976           unsigned int medianr;
1977           char *loc;
1978           Solvable *s;
1979           struct repoinfo *cinfo;
1980           const unsigned char *chksum;
1981           Id chksumtype;
1982           Dataiterator di;
1983
1984           p = checkq.elements[i];
1985           s = pool_id2solvable(pool, p);
1986           if (s->repo == commandlinerepo)
1987             {
1988               loc = solvable_get_location(s, &medianr);
1989               if (!(newpkgsfps[i] = fopen(loc, "r")))
1990                 {
1991                   perror(loc);
1992                   exit(1);
1993                 }
1994               putchar('.');
1995               continue;
1996             }
1997           cinfo = s->repo->appdata;
1998           if (!cinfo)
1999             {
2000               printf("%s: no repository information\n", s->repo->name);
2001               exit(1);
2002             }
2003           loc = solvable_get_location(s, &medianr);
2004           if (!loc)
2005              continue;
2006
2007           if (pool->installed && pool->installed->nsolvables)
2008             {
2009               /* try a delta first */
2010               dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, id2str(pool, s->name), SEARCH_STRING);
2011               dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
2012               while (dataiterator_step(&di))
2013                 {
2014                   Id baseevr, op;
2015
2016                   dataiterator_setpos_parent(&di);
2017                   if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
2018                       pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
2019                     continue;
2020                   baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
2021                   FOR_PROVIDES(op, pp, s->name)
2022                     {
2023                       Solvable *os = pool->solvables + op;
2024                       if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
2025                         break;
2026                     }
2027                   if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
2028                     {
2029                       /* base is installed, run sequence check */
2030                       const char *seqname;
2031                       const char *seqevr;
2032                       const char *seqnum;
2033                       const char *seq;
2034                       const char *dloc;
2035                       FILE *fp;
2036                       char cmd[128];
2037                       int newfd;
2038
2039                       seqname = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
2040                       seqevr = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR);
2041                       seqnum = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM);
2042                       seq = pool_tmpjoin(pool, seqname, "-", seqevr);
2043                       seq = pool_tmpjoin(pool, seq, "-", seqnum);
2044                       if (system(pool_tmpjoin(pool, "/usr/bin/applydeltarpm -c -s ", seq, 0)) != 0)
2045                         continue;       /* didn't match */
2046                       /* looks good, download delta */
2047                       chksumtype = 0;
2048                       chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
2049                       if (!chksumtype)
2050                         continue;       /* no way! */
2051                       dloc = pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_DIR);
2052                       dloc = pool_tmpjoin(pool, dloc, "/", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_NAME));
2053                       dloc = pool_tmpjoin(pool, dloc, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_EVR));
2054                       dloc = pool_tmpjoin(pool, dloc, ".", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_SUFFIX));
2055                       if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype)) == 0)
2056                         continue;
2057                       /* got it, now reconstruct */
2058                       newfd = opentmpfile();
2059                       sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
2060                       fcntl(fileno(fp), F_SETFD, 0);
2061                       if (system(cmd))
2062                         {
2063                           close(newfd);
2064                           fclose(fp);
2065                           continue;
2066                         }
2067                       lseek(newfd, 0, SEEK_SET);
2068                       chksumtype = 0;
2069                       chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2070                       if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
2071                         {
2072                           close(newfd);
2073                           fclose(fp);
2074                           continue;
2075                         }
2076                       newpkgsfps[i] = fdopen(newfd, "r");
2077                       fclose(fp);
2078                       break;
2079                     }
2080                 }
2081               dataiterator_free(&di);
2082             }
2083           
2084           if (newpkgsfps[i])
2085             {
2086               putchar('d');
2087               fflush(stdout);
2088               continue;         /* delta worked! */
2089             }
2090           if (cinfo->type == TYPE_SUSETAGS)
2091             {
2092               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
2093               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
2094             }
2095           chksumtype = 0;
2096           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2097           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype)) == 0)
2098             {
2099               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
2100               exit(1);
2101             }
2102           putchar('.');
2103           fflush(stdout);
2104         }
2105       putchar('\n');
2106     }
2107
2108   if (newpkgs)
2109     {
2110       Queue conflicts;
2111
2112       printf("Searching for file conflicts\n");
2113       queue_init(&conflicts);
2114       fcstate.rpmdbstate = 0;
2115       fcstate.newpkgscnt = newpkgs;
2116       fcstate.checkq = &checkq;
2117       fcstate.newpkgsfps = newpkgsfps;
2118       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fc_cb, &fcstate);
2119       if (conflicts.count)
2120         {
2121           printf("\n");
2122           for (i = 0; i < conflicts.count; i += 5)
2123             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]));
2124           printf("\n");
2125           if (yesno("Re-run solver (y/n/q)? "))
2126             {
2127               for (i = 0; i < newpkgs; i++)
2128                 if (newpkgsfps[i])
2129                   fclose(newpkgsfps[i]);
2130               newpkgsfps = sat_free(newpkgsfps);
2131               solver_free(solv);
2132               pool_add_fileconflicts_deps(pool, &conflicts);
2133               pool_createwhatprovides(pool);    /* Hmm... */
2134               goto rerunsolver;
2135             }
2136         }
2137       queue_free(&conflicts);
2138     }
2139
2140   printf("Committing transaction:\n\n");
2141   transaction_order(trans, 0);
2142   for (i = 0; i < trans->steps.count; i++)
2143     {
2144       const char *evr, *evrp, *nvra;
2145       Solvable *s;
2146       int j;
2147       FILE *fp;
2148
2149       p = trans->steps.elements[i];
2150       s = pool_id2solvable(pool, p);
2151       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
2152       switch(type)
2153         {
2154         case SOLVER_TRANSACTION_ERASE:
2155           printf("erase %s\n", solvid2str(pool, p));
2156           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
2157             continue;
2158           /* strip epoch from evr */
2159           evr = evrp = id2str(pool, s->evr);
2160           while (*evrp >= '0' && *evrp <= '9')
2161             evrp++;
2162           if (evrp > evr && evrp[0] == ':' && evrp[1])
2163             evr = evrp + 1;
2164           nvra = pool_tmpjoin(pool, id2str(pool, s->name), "-", evr);
2165           nvra = pool_tmpjoin(pool, nvra, ".", id2str(pool, s->arch));
2166           runrpm("-e", nvra, -1);       /* to bad that --querybynumber doesn't work */
2167           break;
2168         case SOLVER_TRANSACTION_INSTALL:
2169         case SOLVER_TRANSACTION_MULTIINSTALL:
2170           printf("install %s\n", solvid2str(pool, p));
2171           for (j = 0; j < newpkgs; j++)
2172             if (checkq.elements[j] == p)
2173               break;
2174           fp = j < newpkgs ? newpkgsfps[j] : 0;
2175           if (!fp)
2176             continue;
2177           rewind(fp);
2178           lseek(fileno(fp), 0, SEEK_SET);
2179           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
2180           fclose(fp);
2181           newpkgsfps[j] = 0;
2182           break;
2183         default:
2184           break;
2185         }
2186     }
2187
2188   for (i = 0; i < newpkgs; i++)
2189     if (newpkgsfps[i])
2190       fclose(newpkgsfps[i]);
2191   sat_free(newpkgsfps);
2192   queue_free(&checkq);
2193   solver_free(solv);
2194   queue_free(&job);
2195   pool_free(pool);
2196   free_repoinfos(repoinfos, nrepoinfos);
2197   sat_free(commandlinepkgs);
2198   exit(0);
2199 }