Imported Upstream version 0.6.14
[platform/upstream/libsolv.git] / examples / solv / solv.c
1 /*
2  * Copyright (c) 2009-2015, SUSE LLC.
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 it does:
11  * - understands globs for package names / dependencies
12  * - understands .arch suffix
13  * - installation of commandline packages
14  * - repository data caching
15  * - on demand loading of secondary repository data
16  * - gpg and checksum verification
17  * - file conflicts
18  * - deltarpm support
19  * - fastestmirror implementation
20  *
21  * things available in the library but missing from solv:
22  * - vendor policy loading
23  * - multi version handling
24  */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <sys/utsname.h>
30
31 #include "pool.h"
32 #include "poolarch.h"
33 #include "evr.h"
34 #include "selection.h"
35 #include "repo.h"
36 #include "solver.h"
37 #include "solverdebug.h"
38 #include "transaction.h"
39 #ifdef SUSE
40 #include "repo_autopattern.h"
41 #endif
42
43 #include "repoinfo.h"
44 #include "repoinfo_cache.h"
45 #include "repoinfo_download.h"
46
47 #if defined(ENABLE_RPMDB)
48 #include "fileprovides.h"
49 #include "fileconflicts.h"
50 #include "deltarpm.h"
51 #endif
52 #if defined(SUSE) || defined(FEDORA)
53 #include "patchjobs.h"
54 #endif
55
56 void
57 setarch(Pool *pool)
58 {
59   struct utsname un;
60   if (uname(&un))
61     {
62       perror("uname");
63       exit(1);
64     }
65   pool_setarch(pool, un.machine);
66 }
67
68
69 int
70 yesno(const char *str)
71 {
72   char inbuf[128], *ip;
73
74   for (;;)
75     {
76       printf("%s", str);
77       fflush(stdout);
78       *inbuf = 0;
79       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
80         {
81           printf("Abort.\n");
82           exit(1);
83         }
84       while (*ip == ' ' || *ip == '\t')
85         ip++;
86       if (*ip == 'q')
87         {
88           printf("Abort.\n");
89           exit(1);
90         }
91       if (*ip == 'y' || *ip == 'n')
92         return *ip == 'y' ? 1 : 0;
93     }
94 }
95
96 #ifdef SUSE
97 static Id
98 nscallback(Pool *pool, void *data, Id name, Id evr)
99 {
100 #if 0
101   if (name == NAMESPACE_LANGUAGE)
102     {
103       if (!strcmp(pool_id2str(pool, evr), "ja"))
104         return 1;
105       if (!strcmp(pool_id2str(pool, evr), "de"))
106         return 1;
107       if (!strcmp(pool_id2str(pool, evr), "en"))
108         return 1;
109       if (!strcmp(pool_id2str(pool, evr), "en_US"))
110         return 1;
111     }
112 #endif
113   return 0;
114 }
115 #endif
116
117 #ifdef SUSE
118 static void
119 add_autopackages(Pool *pool)
120 {
121   int i;
122   Repo *repo;
123   FOR_REPOS(i, repo)
124     repo_add_autopattern(repo, 0);
125 }
126 #endif
127
128 #ifdef SUSE
129 static void
130 showdiskusagechanges(Transaction *trans)
131 {
132   DUChanges duc[4];
133   int i;
134
135   /* XXX: use mountpoints here */
136   memset(duc, 0, sizeof(duc));
137   duc[0].path = "/";
138   duc[1].path = "/usr/share/man";
139   duc[2].path = "/sbin";
140   duc[3].path = "/etc";
141   transaction_calc_duchanges(trans, duc, 4);
142   for (i = 0; i < 4; i++)
143     printf("duchanges %s: %d K  %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
144 }
145 #endif
146
147 static Id
148 find_repo(const char *name, Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
149 {
150   const char *rp;
151   int i;
152
153   for (rp = name; *rp; rp++)
154     if (*rp <= '0' || *rp >= '9')
155       break;
156   if (!*rp)
157     {
158       /* repo specified by number */
159       int rnum = atoi(name);
160       for (i = 0; i < nrepoinfos; i++)
161         {
162           struct repoinfo *cinfo = repoinfos + i;
163           if (!cinfo->enabled || !cinfo->repo)
164             continue;
165           if (--rnum == 0)
166             return cinfo->repo->repoid;
167         }
168     }
169   else
170     {
171       /* repo specified by alias */
172       Repo *repo;
173       FOR_REPOS(i, repo)
174         {
175           if (!strcasecmp(name, repo->name))
176             return repo->repoid;
177         }
178     }
179   return 0;
180 }
181
182
183 #define MODE_LIST        0
184 #define MODE_INSTALL     1
185 #define MODE_ERASE       2
186 #define MODE_UPDATE      3
187 #define MODE_DISTUPGRADE 4
188 #define MODE_VERIFY      5
189 #define MODE_PATCH       6
190 #define MODE_INFO        7
191 #define MODE_REPOLIST    8
192 #define MODE_SEARCH      9
193
194 void
195 usage(int r)
196 {
197   fprintf(stderr, "Usage: solv COMMAND <select>\n");
198   fprintf(stderr, "\n");
199   fprintf(stderr, "    dist-upgrade: replace installed packages with\n");
200   fprintf(stderr, "                  versions from the repositories\n");
201   fprintf(stderr, "    erase:        erase installed packages\n");
202   fprintf(stderr, "    info:         display package information\n");
203   fprintf(stderr, "    install:      install packages\n");
204   fprintf(stderr, "    list:         list packages\n");
205   fprintf(stderr, "    repos:        list enabled repositories\n");
206   fprintf(stderr, "    search:       search name/summary/description\n");
207   fprintf(stderr, "    update:       update installed packages\n");
208   fprintf(stderr, "    verify:       check dependencies of installed packages\n");
209 #if defined(SUSE) || defined(FEDORA)
210   fprintf(stderr, "    patch:        install newest maintenance updates\n");
211 #endif
212   fprintf(stderr, "\n");
213   exit(r);
214 }
215
216 int
217 main(int argc, char **argv)
218 {
219   Pool *pool;
220   Repo *commandlinerepo = 0;
221   Id *commandlinepkgs = 0;
222   Id p;
223   struct repoinfo *repoinfos, installedrepoinfo;
224   int nrepoinfos = 0;
225   int mainmode = 0, mode = 0;
226   int i, newpkgs;
227   Queue job, checkq;
228   Solver *solv = 0;
229   Transaction *trans;
230   FILE **newpkgsfps;
231   Queue repofilter;
232   Queue kindfilter;
233   Queue archfilter;
234   int archfilter_src = 0;
235   int cleandeps = 0;
236   int forcebest = 0;
237   char *rootdir = 0;
238   char *keyname = 0;
239   int debuglevel = 0;
240
241   argc--;
242   argv++;
243   while (argc && !strcmp(argv[0], "-d"))
244     {
245       debuglevel++;
246       argc--;
247       argv++;
248     }
249   if (!argv[0])
250     usage(1);
251   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
252     {
253       mainmode = MODE_INSTALL;
254       mode = SOLVER_INSTALL;
255     }
256 #if defined(SUSE) || defined(FEDORA)
257   else if (!strcmp(argv[0], "patch"))
258     {
259       mainmode = MODE_PATCH;
260       mode = SOLVER_INSTALL;
261     }
262 #endif
263   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
264     {
265       mainmode = MODE_ERASE;
266       mode = SOLVER_ERASE;
267     }
268   else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "ls"))
269     {
270       mainmode = MODE_LIST;
271       mode = 0;
272     }
273   else if (!strcmp(argv[0], "info"))
274     {
275       mainmode = MODE_INFO;
276       mode = 0;
277     }
278   else if (!strcmp(argv[0], "search") || !strcmp(argv[0], "se"))
279     {
280       mainmode = MODE_SEARCH;
281       mode = 0;
282     }
283   else if (!strcmp(argv[0], "verify"))
284     {
285       mainmode = MODE_VERIFY;
286       mode = SOLVER_VERIFY;
287     }
288   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
289     {
290       mainmode = MODE_UPDATE;
291       mode = SOLVER_UPDATE;
292     }
293   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
294     {
295       mainmode = MODE_DISTUPGRADE;
296       mode = SOLVER_DISTUPGRADE;
297     }
298   else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
299     {
300       mainmode = MODE_REPOLIST;
301       mode = 0;
302     }
303   else
304     usage(1);
305
306   for (;;)
307     {
308       if (argc > 2 && !strcmp(argv[1], "--root"))
309         {
310           rootdir = argv[2];
311           argc -= 2;
312           argv += 2;
313         }
314       else if (argc > 1 && !strcmp(argv[1], "--clean"))
315         {
316           cleandeps = 1;
317           argc--;
318           argv++;
319         }
320       else if (argc > 1 && !strcmp(argv[1], "--best"))
321         {
322           forcebest = 1;
323           argc--;
324           argv++;
325         }
326       if (argc > 2 && !strcmp(argv[1], "--keyname"))
327         {
328           keyname = argv[2];
329           argc -= 2;
330           argv += 2;
331         }
332       else
333         break;
334     }
335
336   set_userhome();
337   pool = pool_create();
338   pool_set_rootdir(pool, rootdir);
339
340 #if 0
341   {
342     const char *langs[] = {"de_DE", "de", "en"};
343     pool_set_languages(pool, langs, sizeof(langs)/sizeof(*langs));
344   }
345 #endif
346
347   pool_setloadcallback(pool, load_stub, 0);
348 #ifdef SUSE
349   pool->nscallback = nscallback;
350 #endif
351   if (debuglevel)
352     pool_setdebuglevel(pool, debuglevel);
353   setarch(pool);
354   pool_set_flag(pool, POOL_FLAG_ADDFILEPROVIDESFILTERED, 1);
355   repoinfos = read_repoinfos(pool, &nrepoinfos);
356   sort_repoinfos(repoinfos, nrepoinfos);
357
358   if (mainmode == MODE_REPOLIST)
359     {
360       int j = 1;
361       for (i = 0; i < nrepoinfos; i++)
362         {
363           struct repoinfo *cinfo = repoinfos + i;
364           if (!cinfo->enabled)
365             continue;
366           printf("%d: %-20s %s (prio %d)\n", j++, cinfo->alias, cinfo->name, cinfo->priority);
367         }
368       exit(0);
369     }
370   memset(&installedrepoinfo, 0, sizeof(installedrepoinfo));
371   if (!read_installed_repo(&installedrepoinfo, pool))
372     exit(1);
373   read_repos(pool, repoinfos, nrepoinfos);
374
375   /* setup filters */
376   queue_init(&repofilter);
377   queue_init(&kindfilter);
378   queue_init(&archfilter);
379   while (argc > 1)
380     {
381       if (!strcmp(argv[1], "-i"))
382         {
383           queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO, pool->installed->repoid);
384           argc--;
385           argv++;
386         }
387       else if (argc > 2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "--repo")))
388         {
389           Id repoid = find_repo(argv[2], pool, repoinfos, nrepoinfos);
390           if (!repoid)
391             {
392               fprintf(stderr, "%s: no such repo\n", argv[2]);
393               exit(1);
394             }
395           /* SETVENDOR is actually wrong but useful */
396           queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO | SOLVER_SETVENDOR, repoid);
397           argc -= 2;
398           argv += 2;
399         }
400       else if (argc > 2 && !strcmp(argv[1], "--arch"))
401         {
402           if (!strcmp(argv[2], "src") || !strcmp(argv[2], "nosrc"))
403             archfilter_src = 1;
404           queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, argv[2], 1), REL_ARCH, 1));
405           argc -= 2;
406           argv += 2;
407         }
408       else if (argc > 2 && (!strcmp(argv[1], "-t") || !strcmp(argv[1], "--type")))
409         {
410           const char *kind = argv[2];
411           if (!strcmp(kind, "srcpackage"))
412             {
413               /* hey! should use --arch! */
414               queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, ARCH_SRC, REL_ARCH, 1));
415               archfilter_src = 1;
416               argc -= 2;
417               argv += 2;
418               continue;
419             }
420           if (!strcmp(kind, "package"))
421             kind = "";
422           if (!strcmp(kind, "all"))
423             queue_push2(&kindfilter, SOLVER_SOLVABLE_ALL, 0);
424           else
425             queue_push2(&kindfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, kind, 1), REL_KIND, 1));
426           argc -= 2;
427           argv += 2;
428         }
429       else
430         break;
431     }
432
433   if (mainmode == MODE_SEARCH)
434     {
435       Queue sel, q;
436       Dataiterator di;
437       if (argc != 2)
438         usage(1);
439       pool_createwhatprovides(pool);
440       queue_init(&sel);
441       dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
442       dataiterator_set_keyname(&di, SOLVABLE_NAME);
443       dataiterator_set_search(&di, 0, 0);
444       while (dataiterator_step(&di))
445         queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
446       dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
447       dataiterator_set_search(&di, 0, 0);
448       while (dataiterator_step(&di))
449         queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
450       dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
451       dataiterator_set_search(&di, 0, 0);
452       while (dataiterator_step(&di))
453         queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
454       dataiterator_free(&di);
455       if (repofilter.count)
456         selection_filter(pool, &sel, &repofilter);
457         
458       queue_init(&q);
459       selection_solvables(pool, &sel, &q);
460       queue_free(&sel);
461       for (i = 0; i < q.count; i++)
462         {
463           Solvable *s = pool_id2solvable(pool, q.elements[i]);
464           printf("  - %s [%s]: %s\n", pool_solvable2str(pool, s), s->repo->name, solvable_lookup_str(s, SOLVABLE_SUMMARY));
465         }
466       queue_free(&q);
467       exit(0);
468     }
469
470   /* process command line packages */
471   if (mainmode == MODE_LIST || mainmode == MODE_INFO || mainmode == MODE_INSTALL)
472     {
473       for (i = 1; i < argc; i++)
474         {
475           if (!is_cmdline_package((const char *)argv[i]))
476             continue;
477           if (access(argv[i], R_OK))
478             {
479               perror(argv[i]);
480               exit(1);
481             }
482           if (!commandlinepkgs)
483             commandlinepkgs = solv_calloc(argc, sizeof(Id));
484           if (!commandlinerepo)
485             commandlinerepo = repo_create(pool, "@commandline");
486           p = add_cmdline_package(commandlinerepo, (const char *)argv[i]);
487           if (!p)
488             {
489               fprintf(stderr, "could not add '%s'\n", argv[i]);
490               exit(1);
491             }
492           commandlinepkgs[i] = p;
493         }
494       if (commandlinerepo)
495         repo_internalize(commandlinerepo);
496     }
497
498 #if defined(ENABLE_RPMDB)
499   if (pool->disttype == DISTTYPE_RPM)
500     addfileprovides(pool);
501 #endif
502 #ifdef SUSE
503   add_autopackages(pool);
504 #endif
505   pool_createwhatprovides(pool);
506
507   if (keyname)
508     keyname = solv_dupjoin("solvable:", keyname, 0);
509   queue_init(&job);
510   for (i = 1; i < argc; i++)
511     {
512       Queue job2;
513       int flags, rflags;
514
515       if (commandlinepkgs && commandlinepkgs[i])
516         {
517           queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
518           continue;
519         }
520       queue_init(&job2);
521       flags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_GLOB;
522       flags |= SELECTION_CANON|SELECTION_DOTARCH|SELECTION_REL;
523       if (kindfilter.count)
524         flags |= SELECTION_SKIP_KIND;
525       if (mode == MODE_LIST || archfilter_src)
526         flags |= SELECTION_WITH_SOURCE;
527       if (argv[i][0] == '/')
528         flags |= SELECTION_FILELIST | (mode == MODE_ERASE ? SELECTION_INSTALLED_ONLY : 0);
529       if (!keyname)
530         rflags = selection_make(pool, &job2, argv[i], flags);
531       else
532         rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
533       if (repofilter.count)
534         selection_filter(pool, &job2, &repofilter);
535       if (archfilter.count)
536         selection_filter(pool, &job2, &archfilter);
537       if (kindfilter.count)
538         selection_filter(pool, &job2, &kindfilter);
539       if (!job2.count)
540         {
541           flags |= SELECTION_NOCASE;
542           if (!keyname)
543             rflags = selection_make(pool, &job2, argv[i], flags);
544           else
545             rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
546           if (repofilter.count)
547             selection_filter(pool, &job2, &repofilter);
548           if (archfilter.count)
549             selection_filter(pool, &job2, &archfilter);
550           if (kindfilter.count)
551             selection_filter(pool, &job2, &kindfilter);
552           if (job2.count)
553             printf("[ignoring case for '%s']\n", argv[i]);
554         }
555       if (!job2.count)
556         {
557           fprintf(stderr, "nothing matches '%s'\n", argv[i]);
558           exit(1);
559         }
560       if (rflags & SELECTION_FILELIST)
561         printf("[using file list match for '%s']\n", argv[i]);
562       if (rflags & SELECTION_PROVIDES)
563         printf("[using capability match for '%s']\n", argv[i]);
564       queue_insertn(&job, job.count, job2.count, job2.elements);
565       queue_free(&job2);
566     }
567   keyname = solv_free(keyname);
568
569   if (!job.count && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE || mainmode == MODE_VERIFY || repofilter.count || archfilter.count || kindfilter.count))
570     {
571       queue_push2(&job, SOLVER_SOLVABLE_ALL, 0);
572       if (repofilter.count)
573         selection_filter(pool, &job, &repofilter);
574       if (archfilter.count)
575         selection_filter(pool, &job, &archfilter);
576       if (kindfilter.count)
577         selection_filter(pool, &job, &kindfilter);
578     }
579   queue_free(&repofilter);
580   queue_free(&archfilter);
581   queue_free(&kindfilter);
582
583   if (!job.count && mainmode != MODE_PATCH)
584     {
585       printf("no package matched\n");
586       exit(1);
587     }
588
589   if (mainmode == MODE_LIST || mainmode == MODE_INFO)
590     {
591       /* list mode, no solver needed */
592       Queue q;
593       queue_init(&q);
594       for (i = 0; i < job.count; i += 2)
595         {
596           int j;
597           queue_empty(&q);
598           pool_job2solvables(pool, &q, job.elements[i], job.elements[i + 1]);
599           for (j = 0; j < q.count; j++)
600             {
601               Solvable *s = pool_id2solvable(pool, q.elements[j]);
602               if (mainmode == MODE_INFO)
603                 {
604                   const char *str;
605                   printf("Name:        %s\n", pool_solvable2str(pool, s));
606                   printf("Repo:        %s\n", s->repo->name);
607                   printf("Summary:     %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
608                   str = solvable_lookup_str(s, SOLVABLE_URL);
609                   if (str)
610                     printf("Url:         %s\n", str);
611                   str = solvable_lookup_str(s, SOLVABLE_LICENSE);
612                   if (str)
613                     printf("License:     %s\n", str);
614                   printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
615                   printf("\n");
616                 }
617               else
618                 {
619 #if 1
620                   const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de", 1);
621 #else
622                   const char *sum = solvable_lookup_str_poollang(s, SOLVABLE_SUMMARY);
623 #endif
624                   printf("  - %s [%s]\n", pool_solvable2str(pool, s), s->repo->name);
625                   if (sum)
626                     printf("    %s\n", sum);
627                 }
628             }
629         }
630       queue_free(&q);
631       queue_free(&job);
632       pool_free(pool);
633       free_repoinfos(repoinfos, nrepoinfos);
634       solv_free(commandlinepkgs);
635       exit(0);
636     }
637
638 #if defined(SUSE) || defined(FEDORA)
639   if (mainmode == MODE_PATCH)
640     add_patchjobs(pool, &job);
641 #endif
642
643   // add mode
644   for (i = 0; i < job.count; i += 2)
645     {
646       job.elements[i] |= mode;
647       if (mode == SOLVER_UPDATE && pool_isemptyupdatejob(pool, job.elements[i], job.elements[i + 1]))
648         job.elements[i] ^= SOLVER_UPDATE ^ SOLVER_INSTALL;
649       if (cleandeps)
650         job.elements[i] |= SOLVER_CLEANDEPS;
651       if (forcebest)
652         job.elements[i] |= SOLVER_FORCEBEST;
653     }
654
655   // multiversion test
656   // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae", 1));
657   // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-base", 1));
658   // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-extra", 1));
659 #if 0
660   queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
661   queue_push2(&job, SOLVER_ERASE|SOLVER_CLEANDEPS|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
662 #endif
663
664 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
665 rerunsolver:
666 #endif
667   solv = solver_create(pool);
668   solver_set_flag(solv, SOLVER_FLAG_SPLITPROVIDES, 1);
669 #ifdef FEDORA
670   solver_set_flag(solv, SOLVER_FLAG_ALLOW_VENDORCHANGE, 1);
671 #endif
672   if (mainmode == MODE_ERASE)
673     solver_set_flag(solv, SOLVER_FLAG_ALLOW_UNINSTALL, 1);      /* don't nag */
674   solver_set_flag(solv, SOLVER_FLAG_BEST_OBEY_POLICY, 1);
675
676   for (;;)
677     {
678       Id problem, solution;
679       int pcnt, scnt;
680
681       if (!solver_solve(solv, &job))
682         break;
683       pcnt = solver_problem_count(solv);
684       printf("Found %d problems:\n", pcnt);
685       for (problem = 1; problem <= pcnt; problem++)
686         {
687           int take = 0;
688           printf("Problem %d/%d:\n", problem, pcnt);
689           solver_printprobleminfo(solv, problem);
690           printf("\n");
691           scnt = solver_solution_count(solv, problem);
692           for (solution = 1; solution <= scnt; solution++)
693             {
694               printf("Solution %d:\n", solution);
695               solver_printsolution(solv, problem, solution);
696               printf("\n");
697             }
698           for (;;)
699             {
700               char inbuf[128], *ip;
701               printf("Please choose a solution: ");
702               fflush(stdout);
703               *inbuf = 0;
704               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
705                 {
706                   printf("Abort.\n");
707                   exit(1);
708                 }
709               while (*ip == ' ' || *ip == '\t')
710                 ip++;
711               if (*ip >= '0' && *ip <= '9')
712                 {
713                   take = atoi(ip);
714                   if (take >= 1 && take <= scnt)
715                     break;
716                 }
717               if (*ip == 's')
718                 {
719                   take = 0;
720                   break;
721                 }
722               if (*ip == 'q')
723                 {
724                   printf("Abort.\n");
725                   exit(1);
726                 }
727             }
728           if (!take)
729             continue;
730           solver_take_solution(solv, problem, take, &job);
731         }
732     }
733
734   trans = solver_create_transaction(solv);
735   if (!trans->steps.count)
736     {
737       printf("Nothing to do.\n");
738       transaction_free(trans);
739       solver_free(solv);
740       queue_free(&job);
741       pool_free(pool);
742       free_repoinfos(repoinfos, nrepoinfos);
743       solv_free(commandlinepkgs);
744       exit(1);
745     }
746
747   /* display transaction to the user and ask for confirmation */
748   printf("\n");
749   printf("Transaction summary:\n\n");
750   transaction_print(trans);
751 #if defined(SUSE)
752   showdiskusagechanges(trans);
753 #endif
754   printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
755   printf("\n");
756
757   if (!yesno("OK to continue (y/n)? "))
758     {
759       printf("Abort.\n");
760       transaction_free(trans);
761       solver_free(solv);
762       queue_free(&job);
763       pool_free(pool);
764       free_repoinfos(repoinfos, nrepoinfos);
765       solv_free(commandlinepkgs);
766       exit(1);
767     }
768
769   /* download all new packages */
770   queue_init(&checkq);
771   newpkgs = transaction_installedresult(trans, &checkq);
772   newpkgsfps = 0;
773   if (newpkgs)
774     {
775       int downloadsize = 0;
776       for (i = 0; i < newpkgs; i++)
777         {
778           Solvable *s;
779
780           p = checkq.elements[i];
781           s = pool_id2solvable(pool, p);
782           downloadsize += solvable_lookup_sizek(s, SOLVABLE_DOWNLOADSIZE, 0);
783         }
784       printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
785       newpkgsfps = solv_calloc(newpkgs, sizeof(*newpkgsfps));
786       for (i = 0; i < newpkgs; i++)
787         {
788           const char *loc;
789           Solvable *s;
790           struct repoinfo *cinfo;
791
792           p = checkq.elements[i];
793           s = pool_id2solvable(pool, p);
794           if (s->repo == commandlinerepo)
795             {
796               loc = solvable_lookup_location(s, 0);
797               if (!loc)
798                 continue;
799               if (!(newpkgsfps[i] = fopen(loc, "r")))
800                 {
801                   perror(loc);
802                   exit(1);
803                 }
804               putchar('.');
805               continue;
806             }
807           cinfo = s->repo->appdata;
808           if (!cinfo || cinfo->type == TYPE_INSTALLED)
809             {
810               printf("%s: no repository information\n", s->repo->name);
811               exit(1);
812             }
813           loc = solvable_lookup_location(s, 0);
814           if (!loc)
815              continue;  /* pseudo package? */
816 #if defined(ENABLE_RPMDB)
817           if (pool->installed && pool->installed->nsolvables)
818             {
819               if ((newpkgsfps[i] = trydeltadownload(s, loc)) != 0)
820                 {
821                   putchar('d');
822                   fflush(stdout);
823                   continue;             /* delta worked! */
824                 }
825             }
826 #endif
827           if ((newpkgsfps[i] = downloadpackage(s, loc)) == 0)
828             {
829               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
830               exit(1);
831             }
832           putchar('.');
833           fflush(stdout);
834         }
835       putchar('\n');
836     }
837
838 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
839   /* check for file conflicts */
840   if (newpkgs)
841     {
842       Queue conflicts;
843       queue_init(&conflicts);
844       if (checkfileconflicts(pool, &checkq, newpkgs, newpkgsfps, &conflicts))
845         {
846           if (yesno("Re-run solver (y/n/q)? "))
847             {
848               for (i = 0; i < newpkgs; i++)
849                 if (newpkgsfps[i])
850                   fclose(newpkgsfps[i]);
851               newpkgsfps = solv_free(newpkgsfps);
852               solver_free(solv);
853               solv = 0;
854               pool_add_fileconflicts_deps(pool, &conflicts);
855               queue_free(&conflicts);
856               goto rerunsolver;
857             }
858         }
859       queue_free(&conflicts);
860     }
861 #endif
862
863   /* and finally commit the transaction */
864   printf("Committing transaction:\n\n");
865   transaction_order(trans, 0);
866   for (i = 0; i < trans->steps.count; i++)
867     {
868       int j;
869       FILE *fp;
870       Id type;
871
872       p = trans->steps.elements[i];
873       type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
874       switch(type)
875         {
876         case SOLVER_TRANSACTION_ERASE:
877           printf("erase %s\n", pool_solvid2str(pool, p));
878           commit_transactionelement(pool, type, p, 0);
879           break;
880         case SOLVER_TRANSACTION_INSTALL:
881         case SOLVER_TRANSACTION_MULTIINSTALL:
882           printf("install %s\n", pool_solvid2str(pool, p));
883           for (j = 0; j < newpkgs; j++)
884             if (checkq.elements[j] == p)
885               break;
886           fp = j < newpkgs ? newpkgsfps[j] : 0;
887           if (!fp)
888             continue;
889           commit_transactionelement(pool, type, p, fp);
890           fclose(fp);
891           newpkgsfps[j] = 0;
892           break;
893         default:
894           break;
895         }
896     }
897
898   for (i = 0; i < newpkgs; i++)
899     if (newpkgsfps[i])
900       fclose(newpkgsfps[i]);
901   solv_free(newpkgsfps);
902   queue_free(&checkq);
903   transaction_free(trans);
904   solver_free(solv);
905   queue_free(&job);
906   pool_free(pool);
907   free_repoinfos(repoinfos, nrepoinfos);
908   solv_free(commandlinepkgs);
909   exit(0);
910 }