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