Rename macros to profiles
[mspang/inapt.git] / inapt.cc
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <getopt.h>
4 #include <iostream>
5 #include <cstdio>
6 #include <fstream>
7 #include <set>
8 #include <apt-pkg/pkgcache.h>
9 #include <apt-pkg/cachefile.h>
10 #include <apt-pkg/dpkgdb.h>
11 #include <apt-pkg/progress.h>
12 #include <apt-pkg/init.h>
13 #include <apt-pkg/error.h>
14 #include <apt-pkg/algorithms.h>
15 #include <apt-pkg/sptr.h>
16 #include <apt-pkg/acquire-item.h>
17
18 #include "inapt.h"
19 #include "util.h"
20 #include "acqprogress.h"
21
22 using namespace std;
23
24 char *prog = NULL;
25
26 static struct option opts[] = {
27     { NULL, 0, NULL, '\0' },
28 };
29
30 bool run_install(pkgCacheFile &Cache,bool ShwKept = false,bool Ask = true,
31                      bool Safety = true)
32 {
33    if (_config->FindB("APT::Get::Purge", false) == true)
34    {
35       pkgCache::PkgIterator I = Cache->PkgBegin();
36       for (; I.end() == false; I++)
37       {
38          if (I.Purge() == false && Cache[I].Mode == pkgDepCache::ModeDelete)
39             Cache->MarkDelete(I, true);
40       }
41    }
42
43    if (Cache->BrokenCount() != 0)
44    {
45       return _error->Error("Internal error, run_install was called with broken packages!");
46    }
47
48    if (Cache->DelCount() == 0 && Cache->InstCount() == 0 &&
49        Cache->BadCount() == 0)
50       return true;
51
52    if (Cache->DelCount() != 0 && _config->FindB("APT::Get::Remove",true) == false)
53       return _error->Error(("Packages need to be removed but remove is disabled."));
54
55    pkgRecords Recs(Cache);
56    if (_error->PendingError() == true)
57       return false;
58
59    FileFd Lock;
60    if (_config->FindB("Debug::NoLocking",false) == false &&
61        _config->FindB("APT::Get::Print-URIs") == false)
62    {
63       Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
64       if (_error->PendingError() == true)
65          return _error->Error(("Unable to lock the download directory"));
66    }
67
68    unsigned int width = 80;
69    AcqTextStatus status (width, 0);
70    pkgAcquire Fetcher (&status);
71
72    pkgSourceList List;
73    if (List.ReadMainList() == false)
74       return _error->Error(("The list of sources could not be read."));
75
76    SPtr<pkgPackageManager> PM= _system->CreatePM(Cache);
77    if (PM->GetArchives(&Fetcher, &List, &Recs) == false ||
78        _error->PendingError() == true)
79       return false;
80
81    if (_error->PendingError() == true)
82       return false;
83
84    while (1)
85    {
86       bool Transient = false;
87       if (_config->FindB("APT::Get::Download",true) == false)
88       {
89          for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin(); I < Fetcher.ItemsEnd();)
90          {
91             if ((*I)->Local == true)
92             {
93                I++;
94                continue;
95             }
96
97             // Close the item and check if it was found in cache
98             (*I)->Finished();
99             if ((*I)->Complete == false)
100                Transient = true;
101
102             // Clear it out of the fetch list
103             delete *I;
104             I = Fetcher.ItemsBegin();
105          }
106       }
107
108       if (Fetcher.Run() == pkgAcquire::Failed)
109          return false;
110
111       // Print out errors
112       bool Failed = false;
113       for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin(); I != Fetcher.ItemsEnd(); I++)
114       {
115          if ((*I)->Status == pkgAcquire::Item::StatDone &&
116              (*I)->Complete == true)
117             continue;
118
119          if ((*I)->Status == pkgAcquire::Item::StatIdle)
120          {
121             Transient = true;
122             // Failed = true;
123             continue;
124          }
125
126          fprintf(stderr,("Failed to fetch %s  %s\n"),(*I)->DescURI().c_str(),
127                  (*I)->ErrorText.c_str());
128          Failed = true;
129       }
130
131       /* If we are in no download mode and missing files and there were
132          'failures' then the user must specify -m. Furthermore, there
133          is no such thing as a transient error in no-download mode! */
134       if (Transient == true &&
135           _config->FindB("APT::Get::Download",true) == false)
136       {
137          Transient = false;
138          Failed = true;
139       }
140
141       if (_config->FindB("APT::Get::Download-Only",false) == true)
142       {
143          if (Failed == true && _config->FindB("APT::Get::Fix-Missing",false) == false)
144             return _error->Error(("Some files failed to download"));
145          //c1out << _("Download complete and in download only mode") << endl;
146          return true;
147       }
148
149       if (Failed == true && _config->FindB("APT::Get::Fix-Missing",false) == false)
150       {
151          return _error->Error(("Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?"));
152       }
153
154       if (Transient == true && Failed == true)
155          return _error->Error(("--fix-missing and media swapping is not currently supported"));
156
157       // Try to deal with missing package files
158       if (Failed == true && PM->FixMissing() == false)
159       {
160          cerr << ("Unable to correct missing packages.") << endl;
161          return _error->Error(("Aborting install."));
162       }
163
164       _system->UnLock();
165       int status_fd = _config->FindI("APT::Status-Fd",-1);
166       pkgPackageManager::OrderResult Res = PM->DoInstall(status_fd);
167       if (Res == pkgPackageManager::Failed || _error->PendingError() == true)
168          return false;
169       if (Res == pkgPackageManager::Completed)
170          return true;
171
172       // Reload the fetcher object and loop again for media swapping
173       Fetcher.Shutdown();
174       if (PM->GetArchives(&Fetcher,&List,&Recs) == false)
175          return false;
176
177       _system->Lock();
178    }
179 }
180
181 static void usage() {
182     fprintf(stderr, "Usage: %s [filename]\n", prog);
183     exit(2);
184 }
185
186 static bool test_macro(const char *macro, set<string> *defines) {
187     return (*macro != '!' && defines->find(macro) != defines->end())
188             || (*macro == '!' && defines->find(macro + 1) == defines->end());
189 }
190
191 static pkgCache::PkgIterator eval_pkg(inapt_package *package, pkgCacheFile &cachef) {
192     pkgCache::PkgIterator pkg;
193
194     pkgCache *cache = cachef;
195
196     if (!pkg.end()) fatal("omg"); /* TODO */
197
198     for (std::vector<std::string>::iterator i = package->alternates.begin(); i != package->alternates.end(); i++) {
199         pkg = cache->FindPkg(*i);
200
201         /* no such package */
202         if (pkg.end())
203             continue;
204
205         /* real package */
206         if (cachef[pkg].CandidateVer)
207             break;
208
209         /* virtual package */
210         if (pkg->ProvidesList) {
211             if (!pkg.ProvidesList()->NextProvides) {
212                 pkgCache::PkgIterator tmp = pkg.ProvidesList().OwnerPkg();
213                 if (package->action == inapt_action::INSTALL) {
214                     debug("selecting %s instead of %s", tmp.Name(), pkg.Name());
215                     pkg = tmp;
216                     break;
217                 } else {
218                     debug("will not remove %s instead of virtual package %s", tmp.Name(), pkg.Name());
219                 }
220             } else {
221                 debug("%s is a virtual package", pkg.Name());
222             }
223         } else {
224             debug("%s is a virtual packages with no provides", pkg.Name());
225         }
226     }
227
228     if (pkg.end()) {
229         /* todo: report all errors at the end */
230         if (package->alternates.size() == 1) {
231             fatal("%s:%d: No such package: %s", package->filename, package->linenum, package->alternates[0].c_str());
232         } else {
233             std::vector<std::string>::iterator i = package->alternates.begin();
234             std::string message = *(i++);
235             while (i != package->alternates.end()) {
236                 message.append(", ");
237                 message.append(*(i++));
238             }
239             fatal("%s:%d: No alternative available: %s", package->filename, package->linenum, message.c_str());
240         }
241     }
242
243     return pkg;
244 }
245
246 static bool test_macros(vector<std::string> *macros, set<string> *defines) {
247     bool ok = true;
248     for (vector<std::string>::iterator j = macros->begin(); j < macros->end(); j++) {
249         if (!test_macro((*j).c_str(), defines)) {
250             ok = false;
251             break;
252         }
253     }
254     return ok;
255 }
256
257 static void eval_action(inapt_action *action, set<string> *defines, vector<inapt_package *> *final_actions) {
258     for (vector<inapt_package *>::iterator i = action->packages.begin(); i < action->packages.end(); i++) {
259         if (test_macros(&(*i)->predicates, defines))
260             final_actions->push_back(*i);
261     }
262 }
263
264 static void eval_block(inapt_block *block, set<string> *defines, vector<inapt_package *> *final_actions) {
265     if (!block)
266         return;
267
268     for (vector<inapt_action *>::iterator i = block->actions.begin(); i < block->actions.end(); i++)
269         if (test_macros(&(*i)->predicates, defines))
270             eval_action(*i, defines, final_actions);
271
272     for (vector<inapt_conditional *>::iterator i = block->children.begin(); i < block->children.end(); i++) {
273         if (test_macro((*i)->condition, defines))
274             eval_block((*i)->then_block, defines, final_actions);
275         else
276             eval_block((*i)->else_block, defines, final_actions);
277     }
278 }
279
280 static void exec_actions(std::vector<inapt_package *> *final_actions) {
281
282     pkgInitConfig(*_config);
283     pkgInitSystem(*_config, _system);
284
285      _config->Set("Debug::pkgProblemResolver", true);
286
287     OpTextProgress prog;
288     pkgCacheFile cachef;
289
290     if (cachef.Open(prog) == false) {
291         _error->DumpErrors();
292         exit(1);
293     }
294
295     pkgCache *cache = cachef;
296     pkgDepCache *DCache = cachef;
297
298     for (vector<inapt_package *>::iterator i = final_actions->begin(); i != final_actions->end(); i++)
299         (*i)->pkg = eval_pkg(*i, cachef);
300
301     for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
302         pkgCache::PkgIterator k = (*i)->pkg;
303         switch ((*i)->action) {
304             case inapt_action::INSTALL:
305                 if (!k.CurrentVer() || cachef[k].Delete()) {
306                     printf("preinstall %s %s:%d\n", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
307                     DCache->MarkInstall(k, true);
308                 }
309                 break;
310             case inapt_action::REMOVE:
311                 break;
312             default:
313                 fatal("uninitialized action");
314         }
315     }
316
317     for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
318         pkgCache::PkgIterator k = (*i)->pkg;
319         switch ((*i)->action) {
320             case inapt_action::INSTALL:
321                 if ((!k.CurrentVer() && !cachef[k].Install()) || cachef[k].Delete()) {
322                     printf("install %s %s:%d\n", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
323                     DCache->MarkInstall(k, false);
324                 }
325                 break;
326             case inapt_action::REMOVE:
327                 if ((k.CurrentVer() && !cachef[k].Delete()) || cachef[k].Install()) {
328                     printf("remove %s %s:%d\n", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
329                     DCache->MarkDelete(k, false);
330                 }
331                 break;
332             default:
333                 fatal("uninitialized action");
334         }
335     }
336
337     fprintf(stderr, "\n");
338     fprintf(stderr, "nondownloadable packages:\n");
339     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
340        if (i.CurrentVer() && !i.CurrentVer().Downloadable()) {
341                fprintf(stderr, "%s ", i.Name());
342                fprintf(stderr, "%s\n", DCache->GetCandidateVer(i).VerStr());
343        }
344     }
345
346     fprintf(stderr, "\n");
347     fprintf(stderr, "inst %lu del %lu keep %lu broken %lu bad %lu\n",
348                     DCache->InstCount(), DCache->DelCount(), DCache->KeepCount(),
349                     DCache->BrokenCount(), DCache->BadCount());
350
351     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
352        if ((*DCache)[i].Install())
353          fprintf(stderr, "inst %s\n", i.Name());
354        if ((*DCache)[i].InstBroken())
355          fprintf(stderr, "instbroken %s\n", i.Name());
356        if ((*DCache)[i].NowBroken())
357          fprintf(stderr, "nowbroken %s\n", i.Name());
358     }
359
360     fprintf(stderr, "\n");
361
362     pkgProblemResolver fix (DCache);
363
364     for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++)
365         fix.Protect((*i)->pkg);
366     fix.Resolve();
367
368     fprintf(stderr, "\n");
369     fprintf(stderr, "inst %lu del %lu keep %lu broken %lu bad %lu\n",
370                     DCache->InstCount(), DCache->DelCount(), DCache->KeepCount(),
371                     DCache->BrokenCount(), DCache->BadCount());
372     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
373        if ((*DCache)[i].Install())
374          fprintf(stderr, "inst %s\n", i.Name());
375        if ((*DCache)[i].Delete())
376          fprintf(stderr, "del %s\n", i.Name());
377        if ((*DCache)[i].InstBroken())
378          fprintf(stderr, "instbroken %s\n", i.Name());
379        if ((*DCache)[i].NowBroken())
380          fprintf(stderr, "nowbroken %s\n", i.Name());
381     }
382
383     fprintf(stderr, "\n");
384
385     run_install(cachef);
386 }
387
388 int main(int argc, char *argv[]) {
389     int opt;
390     char *filename = NULL;
391
392     set<string> defines;
393
394     prog = xstrdup(basename(argv[0]));
395     while ((opt = getopt_long(argc, argv, "p:", opts, NULL)) != -1) {
396         switch (opt) {
397             case 'p':
398                 defines.insert(optarg);
399                 break;
400             case '?':
401                 usage();
402                 break;
403             default:
404                 fatal("error parsing arguments");
405         }
406     }
407
408     if (argc - optind == 1)
409         filename = argv[optind++];
410     else if (argc - optind > 0)
411         usage();
412
413     fprintf(stderr, "defines: ");
414     for (set<string>::iterator i = defines.begin(); i != defines.end(); i++)
415         fprintf(stderr, "%s  ", i->c_str());
416     fprintf(stderr, "\n");
417
418     inapt_block context;
419     vector<inapt_package *> final_actions;
420
421     parser(filename, &context);
422     eval_block(&context, &defines, &final_actions);
423     exec_actions(&final_actions);
424 }