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