- add repodata_write and repo_empty functions
[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 void
650 writecachedrepo(Repo *repo, Repodata *info, const char *repoext, unsigned char *cookie)
651 {
652   Id *addedfileprovides = 0;
653   FILE *fp;
654   int i, fd;
655   char *tmpl;
656   int myinfo = 0;
657   struct repoinfo *cinfo;
658   int onepiece;
659
660   cinfo = repo->appdata;
661   mkdir(SOLVCACHE_PATH, 0755);
662   tmpl = sat_dupjoin(SOLVCACHE_PATH, "/", ".newsolv-XXXXXX");
663   fd = mkstemp(tmpl);
664   if (fd < 0)
665     {
666       free(tmpl);
667       return;
668     }
669   fchmod(fd, 0444);
670   if (!(fp = fdopen(fd, "w")))
671     {
672       close(fd);
673       unlink(tmpl);
674       free(tmpl);
675       return;
676     }
677
678   onepiece = 1;
679   for (i = repo->start; i < repo->end; i++)
680    if (repo->pool->solvables[i].repo != repo)
681      break;
682   if (i < repo->end)
683     onepiece = 0;
684
685   if (!repoext)
686     {
687       if (!info)
688         {
689           info = repo_add_repodata(repo, 0);
690           myinfo = 1;
691         }
692       pool_addfileprovides_ids(repo->pool, 0, &addedfileprovides);
693       if (addedfileprovides && *addedfileprovides)
694         {
695           for (i = 0; addedfileprovides[i]; i++)
696             repodata_add_idarray(info, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, addedfileprovides[i]);
697         }
698       sat_free(addedfileprovides);
699       repodata_internalize(info);
700       repo_write(repo, fp, repo_write_stdkeyfilter, 0, 0);
701     }
702   else
703     repodata_write(info, fp, repo_write_stdkeyfilter, 0);
704   if (myinfo)
705     repodata_free(info);
706   if (fwrite(cookie, 32, 1, fp) != 1)
707     {
708       fclose(fp);
709       unlink(tmpl);
710       free(tmpl);
711       return;
712     }
713   if (fclose(fp))
714     {
715       unlink(tmpl);
716       free(tmpl);
717       return;
718     }
719   if (!repoext && cinfo)
720     {
721       /* set the checksum so that we can use it with the stub loads */
722       struct stat stb;
723       if (!stat(tmpl, &stb))
724         calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->extcookie);
725     }
726   if (onepiece)
727     {
728       /* switch to just saved repo to activate paging and save memory */
729       FILE *fp = fopen(tmpl, "r");
730       if (fp)
731         {
732           if (!repoext)
733             {
734               /* main repo */
735               repo_empty(repo, 1);
736               if (repo_add_solv(repo, fp))
737                 {
738                   /* oops, no way to recover from here */
739                   fprintf(stderr, "internal error\n");
740                   exit(1);
741                 }
742             }
743           else
744             {
745               /* make sure repodata contains complete repo */
746               /* (this is how repodata_write saves it) */
747               repodata_extend_block(info, repo->start, repo->end - repo->start);
748               info->state = REPODATA_LOADING;
749               /* no need for LOCALPOOL as pool already contains ids */
750               repo_add_solv_flags(repo, fp, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
751               info->state = REPODATA_AVAILABLE; /* in case the load failed */
752             }
753           fclose(fp);
754         }
755     }
756   if (!rename(tmpl, calccachepath(repo, repoext)))
757     unlink(tmpl);
758   free(tmpl);
759 }
760
761
762 static Pool *
763 read_sigs()
764 {
765   Pool *sigpool = pool_create();
766   Repo *repo = repo_create(sigpool, "rpmdbkeys");
767   repo_add_rpmdb_pubkeys(repo, 0, 0);
768   return sigpool;
769 }
770
771
772 /* repomd helpers */
773
774 static inline const char *
775 repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
776 {
777   Pool *pool = repo->pool;
778   Dataiterator di;
779   const char *filename;
780
781   filename = 0;
782   *chksump = 0;
783   *chksumtypep = 0;
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_setpos_parent(&di);
789       filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
790       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
791     }
792   dataiterator_free(&di);
793   if (filename && !*chksumtypep)
794     {
795       printf("no %s file checksum!\n", what);
796       filename = 0;
797     }
798   return filename;
799 }
800
801 int
802 repomd_add_ext(Repo *repo, Repodata *data, const char *what)
803 {
804   Pool *pool = repo->pool;
805   Dataiterator di;
806   Id chksumtype, handle;
807   const unsigned char *chksum;
808   const char *filename;
809
810   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
811   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
812   if (!dataiterator_step(&di))
813     {
814       dataiterator_free(&di);
815       return 0;
816     }
817   if (!strcmp(what, "prestodelta"))
818     what = "deltainfo";
819   dataiterator_setpos_parent(&di);
820   filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
821   chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, &chksumtype);
822   if (!filename || !chksum)
823     {
824       dataiterator_free(&di);
825       return 0;
826     }
827   handle = repodata_new_handle(data);
828   repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
829   repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
830   if (chksumtype)
831     repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
832   if (!strcmp(what, "deltainfo"))
833     {
834       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
835       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
836     }
837   if (!strcmp(what, "filelists"))
838     {
839       repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
840       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
841     }
842   dataiterator_free(&di);
843   repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
844   return 1;
845 }
846
847
848 /* susetags helpers */
849
850 static inline const char *
851 susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
852 {
853   Pool *pool = repo->pool;
854   Dataiterator di;
855   const char *filename;
856
857   filename = 0;
858   *chksump = 0;
859   *chksumtypep = 0;
860   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
861   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
862   if (dataiterator_step(&di))
863     {
864       dataiterator_setpos_parent(&di);
865       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
866       filename = what;
867     }
868   dataiterator_free(&di);
869   if (filename && !*chksumtypep)
870     {
871       printf("no %s file checksum!\n", what);
872       filename = 0;
873     }
874   return filename;
875 }
876
877 static Id susetags_langtags[] = {
878   SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
879   SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
880   SOLVABLE_EULA, REPOKEY_TYPE_STR,
881   SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
882   SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
883   SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
884   0, 0
885 };
886
887 void
888 susetags_add_ext(Repo *repo, Repodata *data)
889 {
890   Pool *pool = repo->pool;
891   Dataiterator di;
892   char ext[3];
893   Id handle, filechksumtype;
894   const unsigned char *filechksum;
895   int i;
896
897   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
898   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
899   while (dataiterator_step(&di))
900     {
901       if (strncmp(di.kv.str, "packages.", 9) != 0)
902         continue;
903       if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
904         continue;
905       ext[0] = di.kv.str[9];
906       ext[1] = di.kv.str[10];
907       ext[2] = 0;
908       if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
909         continue;
910       handle = repodata_new_handle(data);
911       repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
912       if (filechksumtype)
913         repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
914       if (!strcmp(ext, "DU"))
915         {
916           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
917           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
918         }
919       if (!strcmp(ext, "FL"))
920         {
921           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
922           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
923         }
924       else
925         {
926           for (i = 0; susetags_langtags[i]; i += 2)
927             {
928               repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, susetags_langtags[i], ext, 1));
929               repodata_add_idarray(data, handle, REPOSITORY_KEYS, susetags_langtags[i + 1]);
930             }
931         }
932       repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
933     }
934   dataiterator_free(&di);
935 }
936
937
938 static inline int
939 iscompressed(const char *name)
940 {
941   int l = strlen(name);
942   return l > 3 && !strcmp(name + l - 3, ".gz") ? 1 : 0;
943 }
944
945
946 /* load callback */
947
948 int
949 load_stub(Pool *pool, Repodata *data, void *dp)
950 {
951   const char *filename, *descrdir, *repomdtype;
952   const unsigned char *filechksum;
953   Id filechksumtype;
954   struct repoinfo *cinfo;
955   FILE *fp;
956   Id defvendor;
957   char ext[3];
958
959   cinfo = data->repo->appdata;
960
961   filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
962   if (filename)
963     {
964       /* susetags load */
965       ext[0] = filename[9];
966       ext[1] = filename[10];
967       ext[2] = 0;
968 #if 1
969       printf("[%s:%s", data->repo->name, ext);
970 #endif
971       if (usecachedrepo(data->repo, ext, cinfo->extcookie))
972         {
973           printf(" cached]\n"); fflush(stdout);
974           return 1;
975         }
976 #if 1
977       printf(" loading]\n"); fflush(stdout);
978 #endif
979       defvendor = repo_lookup_id(data->repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
980       descrdir = repo_lookup_str(data->repo, SOLVID_META, SUSETAGS_DESCRDIR);
981       if (!descrdir)
982         descrdir = "suse/setup/descr";
983       filechksumtype = 0;
984       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
985       if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype)) == 0)
986         return 0;
987       repo_add_susetags(data->repo, fp, defvendor, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
988       fclose(fp);
989       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
990       return 1;
991     }
992
993   repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
994   if (repomdtype)
995     {
996       if (!strcmp(repomdtype, "filelists"))
997         strcpy(ext, "FL");
998       else if (!strcmp(repomdtype, "deltainfo"))
999         strcpy(ext, "DL");
1000       else
1001         return 0;
1002 #if 1
1003       printf("[%s:%s", data->repo->name, ext);
1004 #endif
1005       if (usecachedrepo(data->repo, ext, cinfo->extcookie))
1006         {
1007           printf(" cached]\n");fflush(stdout);
1008           return 1;
1009         }
1010       printf(" loading]\n"); fflush(stdout);
1011       filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
1012       filechksumtype = 0;
1013       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
1014       if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype)) == 0)
1015         return 0;
1016       if (!strcmp(ext, "FL"))
1017         repo_add_rpmmd(data->repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1018       else if (!strcmp(ext, "DL"))
1019         repo_add_deltainfoxml(data->repo, fp, REPO_USE_LOADING);
1020       fclose(fp);
1021       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
1022       return 1;
1023     }
1024
1025   return 0;
1026 }
1027
1028 void
1029 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
1030 {
1031   Repo *repo;
1032   struct repoinfo *cinfo;
1033   int i;
1034   FILE *fp;
1035   FILE *sigfp;
1036   const char *filename;
1037   const unsigned char *filechksum;
1038   Id filechksumtype;
1039   const char *descrdir;
1040   int defvendor;
1041   struct stat stb;
1042   unsigned char cookie[32];
1043   Pool *sigpool = 0;
1044   Repodata *data;
1045
1046   repo = repo_create(pool, "@System");
1047   printf("rpm database:");
1048   if (stat("/var/lib/rpm/Packages", &stb))
1049     memset(&stb, 0, sizeof(&stb));
1050   calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cookie);
1051   if (usecachedrepo(repo, 0, cookie))
1052     printf(" cached\n");
1053   else
1054     {
1055       FILE *ofp;
1056       printf(" reading\n");
1057       int done = 0;
1058
1059 #ifdef PRODUCTS_PATH
1060       repo_add_products(repo, PRODUCTS_PATH, 0, REPO_NO_INTERNALIZE);
1061 #endif
1062       if ((ofp = fopen(calccachepath(repo, 0), "r")) != 0)
1063         {
1064           Repo *ref = repo_create(pool, "@System.old");
1065           if (!repo_add_solv(ref, ofp))
1066             {
1067               repo_add_rpmdb(repo, ref, 0, REPO_REUSE_REPODATA);
1068               done = 1;
1069             }
1070           fclose(ofp);
1071           repo_free(ref, 1);
1072         }
1073       if (!done)
1074         repo_add_rpmdb(repo, 0, 0, REPO_REUSE_REPODATA);
1075       writecachedrepo(repo, 0, 0, cookie);
1076     }
1077   pool_set_installed(pool, repo);
1078
1079   for (i = 0; i < nrepoinfos; i++)
1080     {
1081       cinfo = repoinfos + i;
1082       if (!cinfo->enabled)
1083         continue;
1084
1085       repo = repo_create(pool, cinfo->alias);
1086       cinfo->repo = repo;
1087       repo->appdata = cinfo;
1088       repo->priority = 99 - cinfo->priority;
1089
1090       if (!cinfo->autorefresh && usecachedrepo(repo, 0, 0))
1091         {
1092           printf("repo '%s':", cinfo->alias);
1093           printf(" cached\n");
1094           continue;
1095         }
1096       switch (cinfo->type)
1097         {
1098         case TYPE_RPMMD:
1099           printf("rpmmd repo '%s':", cinfo->alias);
1100           fflush(stdout);
1101           if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0)) == 0)
1102             {
1103               printf(" no repomd.xml file, skipped\n");
1104               repo_free(repo, 1);
1105               cinfo->repo = 0;
1106               break;
1107             }
1108           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cookie);
1109           if (usecachedrepo(repo, 0, cookie))
1110             {
1111               printf(" cached\n");
1112               fclose(fp);
1113               break;
1114             }
1115           sigfp = curlfopen(cinfo, "repodata/repomd.xml.asc", 0, 0, 0);
1116 #ifndef FEDORA
1117           if (!sigfp)
1118             {
1119               printf(" unsigned, skipped\n");
1120               fclose(fp);
1121               break;
1122             }
1123 #endif
1124           if (sigfp)
1125             {
1126               if (!sigpool)
1127                 sigpool = read_sigs();
1128               if (!checksig(sigpool, fp, sigfp))
1129                 {
1130                   printf(" checksig failed, skipped\n");
1131                   fclose(sigfp);
1132                   fclose(fp);
1133                   break;
1134                 }
1135               fclose(sigfp);
1136             }
1137           repo_add_repomdxml(repo, fp, 0);
1138           fclose(fp);
1139           printf(" reading\n");
1140           filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
1141           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype)) != 0)
1142             {
1143               repo_add_rpmmd(repo, fp, 0, 0);
1144               fclose(fp);
1145             }
1146
1147           filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
1148           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype)) != 0)
1149             {
1150               repo_add_updateinfoxml(repo, fp, 0);
1151               fclose(fp);
1152             }
1153
1154           data = repo_last_repodata(repo);
1155           if (!repomd_add_ext(repo, data, "deltainfo"))
1156             repomd_add_ext(repo, data, "prestodelta");
1157           repomd_add_ext(repo, data, "filelists");
1158           repodata_internalize(data);
1159           writecachedrepo(repo, data, 0, cookie);
1160           repodata_create_stubs(data);
1161           break;
1162
1163         case TYPE_SUSETAGS:
1164           printf("susetags repo '%s':", cinfo->alias);
1165           fflush(stdout);
1166           descrdir = 0;
1167           defvendor = 0;
1168           if ((fp = curlfopen(cinfo, "content", 0, 0, 0)) == 0)
1169             {
1170               printf(" no content file, skipped\n");
1171               repo_free(repo, 1);
1172               cinfo->repo = 0;
1173               break;
1174             }
1175           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cookie);
1176           if (usecachedrepo(repo, 0, cookie))
1177             {
1178               printf(" cached\n");
1179               fclose(fp);
1180               break;
1181             }
1182           sigfp = curlfopen(cinfo, "content.asc", 0, 0, 0);
1183           if (!sigfp)
1184             {
1185               printf(" unsigned, skipped\n");
1186               fclose(fp);
1187               break;
1188             }
1189           if (sigfp)
1190             {
1191               if (!sigpool)
1192                 sigpool = read_sigs();
1193               if (!checksig(sigpool, fp, sigfp))
1194                 {
1195                   printf(" checksig failed, skipped\n");
1196                   fclose(sigfp);
1197                   fclose(fp);
1198                   break;
1199                 }
1200               fclose(sigfp);
1201             }
1202           repo_add_content(repo, fp, 0);
1203           fclose(fp);
1204           defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1205           descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1206           if (!descrdir)
1207             descrdir = "suse/setup/descr";
1208           filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
1209           if (!filename)
1210             filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
1211           if (!filename)
1212             {
1213               printf(" no packages file entry, skipped\n");
1214               break;
1215             }
1216           printf(" reading\n");
1217           if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype)) == 0)
1218             break;
1219           repo_add_susetags(repo, fp, defvendor, 0, 0);
1220           fclose(fp);
1221           data = repo_last_repodata(repo);
1222           susetags_add_ext(repo, data);
1223           repodata_internalize(data);
1224           writecachedrepo(repo, data, 0, cookie);
1225           repodata_create_stubs(data);
1226           break;
1227         default:
1228           printf("unsupported repo '%s': skipped\n", cinfo->alias);
1229           repo_free(repo, 1);
1230           cinfo->repo = 0;
1231           break;
1232         }
1233     }
1234   if (sigpool)
1235     pool_free(sigpool);
1236 }
1237
1238
1239 int
1240 str2archid(Pool *pool, char *arch)
1241 {
1242   Id id;
1243   if (!*arch)
1244     return 0;
1245   id = str2id(pool, arch, 0);
1246   if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
1247     return id;
1248   if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
1249     return 0;
1250   return id;
1251 }
1252
1253 int
1254 depglob(Pool *pool, char *name, Queue *job)
1255 {
1256   Id p, pp;
1257   Id id = str2id(pool, name, 0);
1258   int i, match = 0;
1259
1260   if (id)
1261     {
1262       FOR_PROVIDES(p, pp, id)
1263         {
1264           Solvable *s = pool->solvables + p;
1265           match = 1;
1266           if (s->name == id)
1267             {
1268               queue_push2(job, SOLVER_SOLVABLE_NAME, id);
1269               return 1;
1270             }
1271         }
1272       if (match)
1273         {
1274           printf("[using capability match for '%s']\n", name);
1275           queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
1276           return 1;
1277         }
1278     }
1279
1280   if (strpbrk(name, "[*?") == 0)
1281     return 0;
1282
1283   /* looks like a name glob. hard work. */
1284   for (p = 1; p < pool->nsolvables; p++)
1285     {
1286       Solvable *s = pool->solvables + p;
1287       if (!s->repo || !pool_installable(pool, s))
1288         continue;
1289       id = s->name;
1290       if (fnmatch(name, id2str(pool, id), 0) == 0)
1291         {
1292           for (i = 0; i < job->count; i += 2)
1293             if (job->elements[i] == SOLVER_SOLVABLE_NAME && job->elements[i + 1] == id)
1294               break;
1295           if (i == job->count)
1296             queue_push2(job, SOLVER_SOLVABLE_NAME, id);
1297           match = 1;
1298         }
1299     }
1300   if (match)
1301     return 1;
1302   /* looks like a dep glob. really hard work. */
1303   for (id = 1; id < pool->ss.nstrings; id++)
1304     {
1305       if (!pool->whatprovides[id])
1306         continue;
1307       if (fnmatch(name, id2str(pool, id), 0) == 0)
1308         {
1309           if (!match)
1310             printf("[using capability match for '%s']\n", name);
1311           for (i = 0; i < job->count; i += 2)
1312             if (job->elements[i] == SOLVER_SOLVABLE_PROVIDES && job->elements[i + 1] == id)
1313               break;
1314           if (i == job->count)
1315             queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
1316           match = 1;
1317         }
1318     }
1319   if (match)
1320     return 1;
1321   return 0;
1322 }
1323
1324 void
1325 addrelation(Pool *pool, Queue *job, int flags, Id evr)
1326 {
1327   int i;
1328   for (i = 0; i < job->count; i += 2)
1329     {
1330       if (job->elements[i] != SOLVER_SOLVABLE_NAME && job->elements[i] != SOLVER_SOLVABLE_PROVIDES)
1331         continue;
1332       job->elements[i + 1] = rel2id(pool, job->elements[i + 1], evr, flags, 1);
1333     }
1334 }
1335
1336 int
1337 limitevr(Pool *pool, char *evr, Queue *job, Id archid)
1338 {
1339   Queue mq;
1340   Id p, pp, evrid;
1341   int matched = 0;
1342   int i, j;
1343   Solvable *s;
1344
1345   queue_init(&mq);
1346   for (i = 0; i < job->count; i += 2)
1347     {
1348       queue_empty(&mq);
1349       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
1350         {
1351           s = pool_id2solvable(pool, p);
1352           if (archid && s->arch != archid)
1353             continue;
1354           if (evrcmp_str(pool, id2str(pool, s->evr), evr, EVRCMP_MATCH) == 0)
1355              queue_push(&mq, p);
1356         }
1357       if (mq.count)
1358         {
1359           if (!matched && i)
1360             {
1361               queue_deleten(job, 0, i);
1362               i = 0;
1363             }
1364           matched = 1;
1365           /* if all solvables have the same evr */
1366           s = pool_id2solvable(pool, mq.elements[0]);
1367           evrid = s->evr;
1368           for (j = 0; j < mq.count; j++)
1369             {
1370               s = pool_id2solvable(pool, mq.elements[j]);
1371               if (s->evr != evrid)
1372                 break;
1373             }
1374           if (j == mq.count && j > 1)
1375             {
1376               prune_to_best_arch(pool, &mq);
1377               // prune_to_highest_prio(pool, &mq);
1378               mq.count = 1;
1379             }
1380           if (mq.count > 1)
1381             {
1382               job->elements[i] = SOLVER_SOLVABLE_ONE_OF;
1383               job->elements[i + 1] = pool_queuetowhatprovides(pool, &mq);
1384             }
1385           else
1386             {
1387               job->elements[i] = SOLVER_SOLVABLE;
1388               job->elements[i + 1] = mq.elements[0];
1389             }
1390         }
1391       else if (matched)
1392         {
1393           queue_deleten(job, i, 2);
1394           i -= 2;
1395         }
1396     }
1397   queue_free(&mq);
1398   if (matched)
1399     return 1;
1400   if (!archid)
1401     {
1402       char *r;
1403       if ((r = strrchr(evr, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1404         {
1405           *r = 0;
1406           if (limitevr(pool, evr, job, archid))
1407             {
1408               *r = '.';
1409               return 1;
1410             }
1411           *r = '.';
1412         }
1413     }
1414   return 0;
1415 }
1416
1417 void
1418 mkselect(Pool *pool, int mode, char *name, Queue *job)
1419 {
1420   char *r, *r2;
1421   Id archid;
1422
1423   if (*name == '/')
1424     {
1425       Dataiterator di;
1426       Queue q;
1427       int match = 0;
1428
1429       queue_init(&q);
1430       dataiterator_init(&di, pool, mode == SOLVER_ERASE ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, SEARCH_STRING|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
1431       while (dataiterator_step(&di))
1432         {
1433           Solvable *s = pool->solvables + di.solvid;
1434           if (!s->repo || !pool_installable(pool, s))
1435             continue;
1436           queue_push(&q, di.solvid);
1437           dataiterator_skip_solvable(&di);
1438         }
1439       dataiterator_free(&di);
1440       if (q.count)
1441         {
1442           printf("[using file list match for '%s']\n", name);
1443           match = 1;
1444           if (q.count > 1)
1445             queue_push2(job, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
1446           else
1447             queue_push2(job, SOLVER_SOLVABLE, q.elements[0]);
1448         }
1449       queue_free(&q);
1450       if (match)
1451         return;
1452     }
1453   if ((r = strpbrk(name, "<=>")) != 0)
1454     {
1455       /* relation case, support:
1456        * depglob rel
1457        * depglob.rpm rel
1458        */
1459       int rflags = 0;
1460       int nend = r - name;
1461       for (; *r; r++)
1462         {
1463           if (*r == '<')
1464             rflags |= REL_LT;
1465           else if (*r == '=')
1466             rflags |= REL_EQ;
1467           else if (*r == '>')
1468             rflags |= REL_GT;
1469           else
1470             break;
1471         }
1472       while (*r && *r == ' ' && *r == '\t')
1473         r++;
1474       while (nend && (name[nend - 1] == ' ' || name[nend -1 ] == '\t'))
1475         nend--;
1476       name[nend] = 0;
1477       if (!*name || !*r)
1478         {
1479           fprintf(stderr, "bad relation\n");
1480           exit(1);
1481         }
1482       if (depglob(pool, name, job))
1483         {
1484           addrelation(pool, job, rflags, str2id(pool, r, 1));
1485           return;
1486         }
1487       if ((r2 = strrchr(name, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
1488         {
1489           *r2 = 0;
1490           if (depglob(pool, name, job))
1491             {
1492               *r2 = '.';
1493               addrelation(pool, job, REL_ARCH, archid);
1494               addrelation(pool, job, rflags, str2id(pool, r, 1));
1495               return;
1496             }
1497           *r2 = '.';
1498         }
1499     }
1500   else
1501     {
1502       /* no relation case, support:
1503        * depglob
1504        * depglob.arch
1505        * depglob-version-release
1506        * depglob-version-release.arch
1507        */
1508       if (depglob(pool, name, job))
1509         return;
1510       archid = 0;
1511       if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1512         {
1513           *r = 0;
1514           if (depglob(pool, name, job))
1515             {
1516               *r = '.';
1517               addrelation(pool, job, REL_ARCH, archid);
1518               return;
1519             }
1520           *r = '.';
1521         }
1522       if ((r = strrchr(name, '-')) != 0)
1523         {
1524           *r = 0;
1525           if (depglob(pool, name, job))
1526             {
1527               /* have just the version */
1528               *r = '-';
1529               if (limitevr(pool, r + 1, job, 0))
1530                 return;
1531             }
1532           if ((r2 = strrchr(name, '-')) != 0)
1533             {
1534               *r = '-';
1535               *r2 = 0;
1536               if (depglob(pool, name, job))
1537                 {
1538                   *r2 = '-';
1539                   if (limitevr(pool, r2 + 1, job, 0))
1540                     return;
1541                 }
1542               *r2 = '-';
1543             }
1544           *r = '-';
1545         }
1546     }
1547   fprintf(stderr, "nothing matches '%s'\n", name);
1548   exit(1);
1549 }
1550
1551
1552 int
1553 yesno(const char *str)
1554 {
1555   char inbuf[128], *ip;
1556
1557   for (;;)
1558     {
1559       printf("%s", str);
1560       fflush(stdout);
1561       *inbuf = 0;
1562       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1563         {
1564           printf("Abort.\n");
1565           exit(1);
1566         }
1567       while (*ip == ' ' || *ip == '\t')
1568         ip++;
1569       if (*ip == 'q')
1570         {
1571           printf("Abort.\n");
1572           exit(1);
1573         }
1574       if (*ip == 'y' || *ip == 'n')
1575         return *ip == 'y' ? 1 : 0;
1576     }
1577 }
1578
1579 struct fcstate {
1580   FILE **newpkgsfps;
1581   Queue *checkq;
1582   int newpkgscnt;
1583   void *rpmdbstate;
1584 };
1585
1586 static void *
1587 fc_cb(Pool *pool, Id p, void *cbdata)
1588 {
1589   struct fcstate *fcstate = cbdata;
1590   Solvable *s;
1591   Id rpmdbid;
1592   int i;
1593   FILE *fp;
1594
1595   if (!p)
1596     {
1597       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
1598       return 0;
1599     }
1600   s = pool_id2solvable(pool, p);
1601   if (pool->installed && s->repo == pool->installed)
1602     {
1603       if (!s->repo->rpmdbid)
1604         return 0;
1605       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
1606       if (!rpmdbid)
1607         return 0;
1608        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
1609     }
1610   for (i = 0; i < fcstate->newpkgscnt; i++)
1611     if (fcstate->checkq->elements[i] == p)
1612       break;
1613   if (i == fcstate->newpkgscnt)
1614     return 0;
1615   fp = fcstate->newpkgsfps[i];
1616   if (!fp)
1617     return 0;
1618   rewind(fp);
1619   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
1620 }
1621
1622 void
1623 runrpm(const char *arg, const char *name, int dupfd3)
1624 {
1625   pid_t pid;
1626   int status;
1627
1628   if ((pid = fork()) == (pid_t)-1)
1629     {
1630       perror("fork");
1631       exit(1);
1632     }
1633   if (pid == 0)
1634     {
1635       if (dupfd3 != -1 && dupfd3 != 3)
1636         {
1637           dup2(dupfd3, 3);
1638           close(dupfd3);
1639         }
1640       if (dupfd3 != -1)
1641         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
1642       if (strcmp(arg, "-e") == 0)
1643         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
1644       else
1645         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
1646       perror("rpm");
1647       _exit(0);
1648     }
1649   while (waitpid(pid, &status, 0) != pid)
1650     ;
1651   if (status)
1652     {
1653       printf("rpm failed\n");
1654       exit(1);
1655     }
1656 }
1657
1658 static Id
1659 nscallback(Pool *pool, void *data, Id name, Id evr)
1660 {
1661   if (name == NAMESPACE_PRODUCTBUDDY)
1662     {    
1663       /* SUSE specific hack: each product has an associated rpm */
1664       Solvable *s = pool->solvables + evr; 
1665       Id p, pp, cap; 
1666       
1667       cap = str2id(pool, pool_tmpjoin(pool, "product(", id2str(pool, s->name) + 8, ")"), 0);
1668       if (!cap)
1669         return 0;
1670       cap = rel2id(pool, cap, s->evr, REL_EQ, 0);
1671       if (!cap)
1672         return 0;
1673       FOR_PROVIDES(p, pp, cap) 
1674         {
1675           Solvable *ps = pool->solvables + p; 
1676           if (ps->repo == s->repo && ps->arch == s->arch)
1677             break;
1678         }
1679       return p;
1680     }
1681   return 0;
1682 }
1683
1684
1685 int
1686 main(int argc, char **argv)
1687 {
1688   Pool *pool;
1689   Repo *commandlinerepo = 0;
1690   Id *commandlinepkgs = 0;
1691   Id p, pp;
1692   struct repoinfo *repoinfos;
1693   int nrepoinfos = 0;
1694   int i, mode, newpkgs;
1695   Queue job, checkq;
1696   Solver *solv = 0;
1697   Transaction *trans;
1698   char inbuf[128], *ip;
1699   int updateall = 0;
1700   int distupgrade = 0;
1701   int patchmode = 0;
1702   FILE **newpkgsfps;
1703   struct fcstate fcstate;
1704
1705   argc--;
1706   argv++;
1707   if (!argv[0])
1708     {
1709       fprintf(stderr, "Usage: solv install|erase|update|show <select>\n");
1710       exit(1);
1711     }
1712   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
1713     mode = SOLVER_INSTALL;
1714   else if (!strcmp(argv[0], "patch"))
1715     {
1716       mode = SOLVER_UPDATE;
1717       patchmode = 1;
1718     }
1719   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
1720     mode = SOLVER_ERASE;
1721   else if (!strcmp(argv[0], "show"))
1722     mode = 0;
1723   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
1724     mode = SOLVER_UPDATE;
1725   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
1726     {
1727       mode = SOLVER_UPDATE;
1728       distupgrade = 1;
1729     }
1730   else
1731     {
1732       fprintf(stderr, "Usage: solv install|erase|update|show <select>\n");
1733       exit(1);
1734     }
1735
1736   pool = pool_create();
1737   pool_setloadcallback(pool, load_stub, 0);
1738   pool->nscallback = nscallback;
1739   // pool_setdebuglevel(pool, 2);
1740   setarch(pool);
1741   repoinfos = read_repoinfos(pool, REPOINFO_PATH, &nrepoinfos);
1742   read_repos(pool, repoinfos, nrepoinfos);
1743
1744   if (mode == 0 || mode == SOLVER_INSTALL)
1745     {
1746       for (i = 1; i < argc; i++)
1747         {
1748           int l;
1749           l = strlen(argv[i]);
1750           if (l <= 4 || strcmp(argv[i] + l - 4, ".rpm"))
1751             continue;
1752           if (access(argv[i], R_OK))
1753             {
1754               perror(argv[i]);
1755               exit(1);
1756             }
1757           if (!commandlinepkgs)
1758             commandlinepkgs = sat_calloc(argc, sizeof(Id));
1759           if (!commandlinerepo)
1760             commandlinerepo = repo_create(pool, "@commandline");
1761           repo_add_rpms(commandlinerepo, (const char **)argv + i, 1, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
1762           commandlinepkgs[i] = commandlinerepo->end - 1;
1763         }
1764       if (commandlinerepo)
1765         repo_internalize(commandlinerepo);
1766     }
1767
1768   // FOR_REPOS(i, repo)
1769   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
1770   pool_addfileprovides(pool);
1771   pool_createwhatprovides(pool);
1772
1773   queue_init(&job);
1774   for (i = 1; i < argc; i++)
1775     {
1776       if (commandlinepkgs && commandlinepkgs[i])
1777         {
1778           queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
1779           continue;
1780         }
1781       mkselect(pool, mode, argv[i], &job);
1782     }
1783   if (!job.count && mode == SOLVER_UPDATE)
1784     updateall = 1;
1785   else if (!job.count)
1786     {
1787       printf("no package matched\n");
1788       exit(1);
1789     }
1790
1791   if (!mode)
1792     {
1793       /* show mode, no solver needed */
1794       for (i = 0; i < job.count; i += 2)
1795         {
1796           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
1797             {
1798               Solvable *s = pool_id2solvable(pool, p);
1799               const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de");
1800               printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
1801               if (sum)
1802                 printf("    %s\n", sum);
1803             }
1804         }
1805       queue_free(&job);
1806       pool_free(pool);
1807       free_repoinfos(repoinfos, nrepoinfos);
1808       sat_free(commandlinepkgs);
1809       exit(0);
1810     }
1811
1812   if (updateall && patchmode)
1813     {
1814       int pruneyou = 0;
1815       Map installedmap;
1816       Solvable *s;
1817
1818       map_init(&installedmap, pool->nsolvables);
1819       if (pool->installed)
1820         FOR_REPO_SOLVABLES(pool->installed, p, s)
1821           MAPSET(&installedmap, p);
1822
1823       /* install all patches */
1824       updateall = 0;
1825       mode = SOLVER_INSTALL;
1826       for (p = 1; p < pool->nsolvables; p++)
1827         {
1828           const char *type;
1829           int r;
1830           Id p2;
1831
1832           s = pool->solvables + p;
1833           if (strncmp(id2str(pool, s->name), "patch:", 6) != 0)
1834             continue;
1835           FOR_PROVIDES(p2, pp, s->name)
1836             {
1837               Solvable *s2 = pool->solvables + p2;
1838               if (s2->name != s->name)
1839                 continue;
1840               r = evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
1841               if (r < 0 || (r == 0 && p > p2))
1842                 break;
1843             }
1844           if (p2)
1845             continue;
1846           type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
1847           if (type && !strcmp(type, "optional"))
1848             continue;
1849           r = solvable_trivial_installable_map(s, &installedmap, 0);
1850           if (r == -1)
1851             continue;
1852           if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
1853             {
1854               if (!pruneyou++)
1855                 queue_empty(&job);
1856             }
1857           else if (pruneyou)
1858             continue;
1859           queue_push2(&job, SOLVER_SOLVABLE, p);
1860         }
1861       map_free(&installedmap);
1862     }
1863
1864   // add mode
1865   for (i = 0; i < job.count; i += 2)
1866     {
1867       if (mode == SOLVER_UPDATE)
1868         {
1869           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
1870             if (pool->installed && pool->solvables[p].repo == pool->installed)
1871               break;
1872           if (!p)
1873             {
1874               job.elements[i] |= SOLVER_INSTALL;
1875               continue;
1876             }
1877         }
1878       job.elements[i] |= mode;
1879     }
1880
1881   // multiversion test
1882   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae", 1));
1883   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-base", 1));
1884   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-extra", 1));
1885
1886 rerunsolver:
1887   for (;;)
1888     {
1889       Id problem, solution;
1890       int pcnt, scnt;
1891
1892       solv = solver_create(pool);
1893       solv->ignorealreadyrecommended = 1;
1894       solv->updatesystem = updateall;
1895       solv->dosplitprovides = updateall;
1896       if (updateall && distupgrade)
1897         {
1898           solv->distupgrade = 1;
1899           solv->allowdowngrade = 1;
1900           solv->allowarchchange = 1;
1901           solv->allowvendorchange = 1;
1902         }
1903       // queue_push2(&job, SOLVER_DISTUPGRADE, 3);
1904       solver_solve(solv, &job);
1905       if (!solv->problems.count)
1906         break;
1907       pcnt = solver_problem_count(solv);
1908       printf("Found %d problems:\n", pcnt);
1909       for (problem = 1; problem <= pcnt; problem++)
1910         {
1911           int take = 0;
1912           printf("Problem %d:\n", problem);
1913           solver_printprobleminfo(solv, problem);
1914           printf("\n");
1915           scnt = solver_solution_count(solv, problem);
1916           for (solution = 1; solution <= scnt; solution++)
1917             {
1918               printf("Solution %d:\n", solution);
1919               solver_printsolution(solv, problem, solution);
1920               printf("\n");
1921             }
1922           for (;;)
1923             {
1924               printf("Please choose a solution: ");
1925               fflush(stdout);
1926               *inbuf = 0;
1927               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1928                 {
1929                   printf("Abort.\n");
1930                   exit(1);
1931                 }
1932               while (*ip == ' ' || *ip == '\t')
1933                 ip++;
1934               if (*ip >= '0' && *ip <= '9')
1935                 {
1936                   take = atoi(ip);
1937                   if (take >= 1 && take <= scnt)
1938                     break;
1939                 }
1940               if (*ip == 's')
1941                 {
1942                   take = 0;
1943                   break;
1944                 }
1945               if (*ip == 'q')
1946                 {
1947                   printf("Abort.\n");
1948                   exit(1);
1949                 }
1950             }
1951           if (!take)
1952             continue;
1953           solver_take_solution(solv, problem, take, &job);
1954         }
1955       solver_free(solv);
1956       solv = 0;
1957     }
1958
1959   trans = &solv->trans;
1960   if (!trans->steps.count)
1961     {
1962       printf("Nothing to do.\n");
1963       exit(1);
1964     }
1965   printf("\n");
1966   printf("Transaction summary:\n\n");
1967   solver_printtransaction(solv);
1968
1969 #if 1
1970   if (1)
1971     {
1972       DUChanges duc[4];
1973       int i;
1974
1975       duc[0].path = "/";
1976       duc[1].path = "/usr/share/man";
1977       duc[2].path = "/sbin";
1978       duc[3].path = "/etc";
1979       transaction_calc_duchanges(trans, duc, 4);
1980       for (i = 0; i < 4; i++)
1981         printf("duchanges %s: %d K  %d\n", duc[i].path, duc[i].kbytes, duc[i].files);
1982       printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
1983     }
1984 #endif
1985
1986   if (!yesno("OK to continue (y/n)? "))
1987     {
1988       printf("Abort.\n");
1989       exit(1);
1990     }
1991
1992   queue_init(&checkq);
1993   newpkgs = transaction_installedresult(trans, &checkq);
1994   newpkgsfps = 0;
1995
1996   if (newpkgs)
1997     {
1998       printf("Downloading %d packages\n", newpkgs);
1999       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
2000       for (i = 0; i < newpkgs; i++)
2001         {
2002           unsigned int medianr;
2003           char *loc;
2004           Solvable *s;
2005           struct repoinfo *cinfo;
2006           const unsigned char *chksum;
2007           Id chksumtype;
2008           Dataiterator di;
2009
2010           p = checkq.elements[i];
2011           s = pool_id2solvable(pool, p);
2012           if (s->repo == commandlinerepo)
2013             {
2014               loc = solvable_get_location(s, &medianr);
2015               if (!(newpkgsfps[i] = fopen(loc, "r")))
2016                 {
2017                   perror(loc);
2018                   exit(1);
2019                 }
2020               putchar('.');
2021               continue;
2022             }
2023           cinfo = s->repo->appdata;
2024           if (!cinfo)
2025             {
2026               printf("%s: no repository information\n", s->repo->name);
2027               exit(1);
2028             }
2029           loc = solvable_get_location(s, &medianr);
2030           if (!loc)
2031              continue;
2032
2033           if (pool->installed && pool->installed->nsolvables)
2034             {
2035               /* try a delta first */
2036               dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, id2str(pool, s->name), SEARCH_STRING);
2037               dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
2038               while (dataiterator_step(&di))
2039                 {
2040                   Id baseevr, op;
2041
2042                   dataiterator_setpos_parent(&di);
2043                   if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
2044                       pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
2045                     continue;
2046                   baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
2047                   FOR_PROVIDES(op, pp, s->name)
2048                     {
2049                       Solvable *os = pool->solvables + op;
2050                       if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
2051                         break;
2052                     }
2053                   if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
2054                     {
2055                       /* base is installed, run sequence check */
2056                       const char *seqname;
2057                       const char *seqevr;
2058                       const char *seqnum;
2059                       const char *seq;
2060                       const char *dloc;
2061                       FILE *fp;
2062                       char cmd[128];
2063                       int newfd;
2064
2065                       seqname = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
2066                       seqevr = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR);
2067                       seqnum = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM);
2068                       seq = pool_tmpjoin(pool, seqname, "-", seqevr);
2069                       seq = pool_tmpjoin(pool, seq, "-", seqnum);
2070                       if (system(pool_tmpjoin(pool, "/usr/bin/applydeltarpm -c -s ", seq, 0)) != 0)
2071                         continue;       /* didn't match */
2072                       /* looks good, download delta */
2073                       chksumtype = 0;
2074                       chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
2075                       if (!chksumtype)
2076                         continue;       /* no way! */
2077                       dloc = pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_DIR);
2078                       dloc = pool_tmpjoin(pool, dloc, "/", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_NAME));
2079                       dloc = pool_tmpjoin(pool, dloc, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_EVR));
2080                       dloc = pool_tmpjoin(pool, dloc, ".", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_SUFFIX));
2081                       if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype)) == 0)
2082                         continue;
2083                       /* got it, now reconstruct */
2084                       newfd = opentmpfile();
2085                       sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
2086                       fcntl(fileno(fp), F_SETFD, 0);
2087                       if (system(cmd))
2088                         {
2089                           close(newfd);
2090                           fclose(fp);
2091                           continue;
2092                         }
2093                       lseek(newfd, 0, SEEK_SET);
2094                       chksumtype = 0;
2095                       chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2096                       if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
2097                         {
2098                           close(newfd);
2099                           fclose(fp);
2100                           continue;
2101                         }
2102                       newpkgsfps[i] = fdopen(newfd, "r");
2103                       fclose(fp);
2104                       break;
2105                     }
2106                 }
2107               dataiterator_free(&di);
2108             }
2109           
2110           if (newpkgsfps[i])
2111             {
2112               putchar('d');
2113               fflush(stdout);
2114               continue;         /* delta worked! */
2115             }
2116           if (cinfo->type == TYPE_SUSETAGS)
2117             {
2118               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
2119               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
2120             }
2121           chksumtype = 0;
2122           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2123           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype)) == 0)
2124             {
2125               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
2126               exit(1);
2127             }
2128           putchar('.');
2129           fflush(stdout);
2130         }
2131       putchar('\n');
2132     }
2133
2134   if (newpkgs)
2135     {
2136       Queue conflicts;
2137
2138       printf("Searching for file conflicts\n");
2139       queue_init(&conflicts);
2140       fcstate.rpmdbstate = 0;
2141       fcstate.newpkgscnt = newpkgs;
2142       fcstate.checkq = &checkq;
2143       fcstate.newpkgsfps = newpkgsfps;
2144       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fc_cb, &fcstate);
2145       if (conflicts.count)
2146         {
2147           printf("\n");
2148           for (i = 0; i < conflicts.count; i += 5)
2149             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]));
2150           printf("\n");
2151           if (yesno("Re-run solver (y/n/q)? "))
2152             {
2153               for (i = 0; i < newpkgs; i++)
2154                 if (newpkgsfps[i])
2155                   fclose(newpkgsfps[i]);
2156               newpkgsfps = sat_free(newpkgsfps);
2157               solver_free(solv);
2158               pool_add_fileconflicts_deps(pool, &conflicts);
2159               pool_createwhatprovides(pool);    /* Hmm... */
2160               goto rerunsolver;
2161             }
2162         }
2163       queue_free(&conflicts);
2164     }
2165
2166   printf("Committing transaction:\n\n");
2167   transaction_order(trans, 0);
2168   for (i = 0; i < trans->steps.count; i++)
2169     {
2170       const char *evr, *evrp, *nvra;
2171       Solvable *s;
2172       int j;
2173       FILE *fp;
2174
2175       p = trans->steps.elements[i];
2176       s = pool_id2solvable(pool, p);
2177       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
2178       switch(type)
2179         {
2180         case SOLVER_TRANSACTION_ERASE:
2181           printf("erase %s\n", solvid2str(pool, p));
2182           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
2183             continue;
2184           /* strip epoch from evr */
2185           evr = evrp = id2str(pool, s->evr);
2186           while (*evrp >= '0' && *evrp <= '9')
2187             evrp++;
2188           if (evrp > evr && evrp[0] == ':' && evrp[1])
2189             evr = evrp + 1;
2190           nvra = pool_tmpjoin(pool, id2str(pool, s->name), "-", evr);
2191           nvra = pool_tmpjoin(pool, nvra, ".", id2str(pool, s->arch));
2192           runrpm("-e", nvra, -1);       /* to bad that --querybynumber doesn't work */
2193           break;
2194         case SOLVER_TRANSACTION_INSTALL:
2195         case SOLVER_TRANSACTION_MULTIINSTALL:
2196           printf("install %s\n", solvid2str(pool, p));
2197           for (j = 0; j < newpkgs; j++)
2198             if (checkq.elements[j] == p)
2199               break;
2200           fp = j < newpkgs ? newpkgsfps[j] : 0;
2201           if (!fp)
2202             continue;
2203           rewind(fp);
2204           lseek(fileno(fp), 0, SEEK_SET);
2205           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
2206           fclose(fp);
2207           newpkgsfps[j] = 0;
2208           break;
2209         default:
2210           break;
2211         }
2212     }
2213
2214   for (i = 0; i < newpkgs; i++)
2215     if (newpkgsfps[i])
2216       fclose(newpkgsfps[i]);
2217   sat_free(newpkgsfps);
2218   queue_free(&checkq);
2219   solver_free(solv);
2220   queue_free(&job);
2221   pool_free(pool);
2222   free_repoinfos(repoinfos, nrepoinfos);
2223   sat_free(commandlinepkgs);
2224   exit(0);
2225 }