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