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