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