enable APT in unpack/configure ordering to handle loops as well
[ntk/apt.git] / apt-pkg / aptconfiguration.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 /* ######################################################################
4
5 Provide access methods to various configuration settings,
6 setup defaults and returns validate settings.
7
8 ##################################################################### */
9 /*}}}*/
10 // Include Files /*{{{*/
11 #include <config.h>
12
13 #include <apt-pkg/aptconfiguration.h>
14 #include <apt-pkg/configuration.h>
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/fileutl.h>
17 #include <apt-pkg/macros.h>
18 #include <apt-pkg/strutl.h>
19
20 #include <sys/types.h>
21 #include <dirent.h>
22 #include <stdio.h>
23
24 #include <algorithm>
25 #include <string>
26 #include <vector>
27 /*}}}*/
28 namespace APT {
29 // getCompressionTypes - Return Vector of usbale compressiontypes /*{{{*/
30 // ---------------------------------------------------------------------
31 /* return a vector of compression types in the prefered order. */
32 std::vector<std::string>
33 const Configuration::getCompressionTypes(bool const &Cached) {
34 static std::vector<std::string> types;
35 if (types.empty() == false) {
36 if (Cached == true)
37 return types;
38 else
39 types.clear();
40 }
41
42 // setup the defaults for the compressiontypes => method mapping
43 _config->CndSet("Acquire::CompressionTypes::bz2","bzip2");
44 _config->CndSet("Acquire::CompressionTypes::xz","xz");
45 _config->CndSet("Acquire::CompressionTypes::lzma","lzma");
46 _config->CndSet("Acquire::CompressionTypes::gz","gzip");
47
48 setDefaultConfigurationForCompressors();
49
50 // accept non-list order as override setting for config settings on commandline
51 std::string const overrideOrder = _config->Find("Acquire::CompressionTypes::Order","");
52 if (overrideOrder.empty() == false)
53 types.push_back(overrideOrder);
54
55 // load the order setting into our vector
56 std::vector<std::string> const order = _config->FindVector("Acquire::CompressionTypes::Order");
57 for (std::vector<std::string>::const_iterator o = order.begin();
58 o != order.end(); ++o) {
59 if ((*o).empty() == true)
60 continue;
61 // ignore types we have no method ready to use
62 if (_config->Exists(string("Acquire::CompressionTypes::").append(*o)) == false)
63 continue;
64 // ignore types we have no app ready to use
65 string const appsetting = string("Dir::Bin::").append(*o);
66 if (_config->Exists(appsetting) == true) {
67 std::string const app = _config->FindFile(appsetting.c_str(), "");
68 if (app.empty() == false && FileExists(app) == false)
69 continue;
70 }
71 types.push_back(*o);
72 }
73
74 // move again over the option tree to add all missing compression types
75 ::Configuration::Item const *Types = _config->Tree("Acquire::CompressionTypes");
76 if (Types != 0)
77 Types = Types->Child;
78
79 for (; Types != 0; Types = Types->Next) {
80 if (Types->Tag == "Order" || Types->Tag.empty() == true)
81 continue;
82 // ignore types we already have in the vector
83 if (std::find(types.begin(),types.end(),Types->Tag) != types.end())
84 continue;
85 // ignore types we have no app ready to use
86 string const appsetting = string("Dir::Bin::").append(Types->Value);
87 if (appsetting.empty() == false && _config->Exists(appsetting) == true) {
88 std::string const app = _config->FindFile(appsetting.c_str(), "");
89 if (app.empty() == false && FileExists(app) == false)
90 continue;
91 }
92 types.push_back(Types->Tag);
93 }
94
95 // add the special "uncompressed" type
96 if (std::find(types.begin(), types.end(), "uncompressed") == types.end())
97 {
98 string const uncompr = _config->FindFile("Dir::Bin::uncompressed", "");
99 if (uncompr.empty() == true || FileExists(uncompr) == true)
100 types.push_back("uncompressed");
101 }
102
103 return types;
104 }
105 /*}}}*/
106 // GetLanguages - Return Vector of Language Codes /*{{{*/
107 // ---------------------------------------------------------------------
108 /* return a vector of language codes in the prefered order.
109 the special word "environment" will be replaced with the long and the short
110 code of the local settings and it will be insured that this will not add
111 duplicates. So in an german local the setting "environment, de_DE, en, de"
112 will result in "de_DE, de, en".
113 The special word "none" is the stopcode for the not-All code vector */
114 std::vector<std::string> const Configuration::getLanguages(bool const &All,
115 bool const &Cached, char const ** const Locale) {
116 using std::string;
117
118 // The detection is boring and has a lot of cornercases,
119 // so we cache the results to calculated it only once.
120 std::vector<string> static allCodes;
121 std::vector<string> static codes;
122
123 // we have something in the cache
124 if (codes.empty() == false || allCodes.empty() == false) {
125 if (Cached == true) {
126 if(All == true && allCodes.empty() == false)
127 return allCodes;
128 else
129 return codes;
130 } else {
131 allCodes.clear();
132 codes.clear();
133 }
134 }
135
136 // Include all Language codes we have a Translation file for in /var/lib/apt/lists
137 // so they will be all included in the Cache.
138 std::vector<string> builtin;
139 DIR *D = opendir(_config->FindDir("Dir::State::lists").c_str());
140 if (D != 0) {
141 builtin.push_back("none");
142 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D)) {
143 string const name = Ent->d_name;
144 size_t const foundDash = name.rfind("-");
145 size_t const foundUnderscore = name.rfind("_");
146 if (foundDash == string::npos || foundUnderscore == string::npos ||
147 foundDash <= foundUnderscore ||
148 name.substr(foundUnderscore+1, foundDash-(foundUnderscore+1)) != "Translation")
149 continue;
150 string const c = name.substr(foundDash+1);
151 if (unlikely(c.empty() == true) || c == "en")
152 continue;
153 // Skip unusual files, like backups or that alike
154 string::const_iterator s = c.begin();
155 for (;s != c.end(); ++s) {
156 if (isalpha(*s) == 0)
157 break;
158 }
159 if (s != c.end())
160 continue;
161 if (std::find(builtin.begin(), builtin.end(), c) != builtin.end())
162 continue;
163 builtin.push_back(c);
164 }
165 }
166 closedir(D);
167
168 // FIXME: Remove support for the old APT::Acquire::Translation
169 // it was undocumented and so it should be not very widthly used
170 string const oldAcquire = _config->Find("APT::Acquire::Translation","");
171 if (oldAcquire.empty() == false && oldAcquire != "environment") {
172 // TRANSLATORS: the two %s are APT configuration options
173 _error->Notice("Option '%s' is deprecated. Please use '%s' instead, see 'man 5 apt.conf' for details.",
174 "APT::Acquire::Translation", "Acquire::Languages");
175 if (oldAcquire != "none")
176 codes.push_back(oldAcquire);
177 codes.push_back("en");
178 allCodes = codes;
179 for (std::vector<string>::const_iterator b = builtin.begin();
180 b != builtin.end(); ++b)
181 if (std::find(allCodes.begin(), allCodes.end(), *b) == allCodes.end())
182 allCodes.push_back(*b);
183 if (All == true)
184 return allCodes;
185 else
186 return codes;
187 }
188
189 // get the environment language codes: LC_MESSAGES (and later LANGUAGE)
190 // we extract both, a long and a short code and then we will
191 // check if we actually need both (rare) or if the short is enough
192 string const envMsg = string(Locale == 0 ? std::setlocale(LC_MESSAGES, NULL) : *Locale);
193 size_t const lenShort = (envMsg.find('_') != string::npos) ? envMsg.find('_') : 2;
194 size_t const lenLong = (envMsg.find_first_of(".@") != string::npos) ? envMsg.find_first_of(".@") : (lenShort + 3);
195
196 string const envLong = envMsg.substr(0,lenLong);
197 string const envShort = envLong.substr(0,lenShort);
198
199 // It is very likely we will need the environment codes later,
200 // so let us generate them now from LC_MESSAGES and LANGUAGE
201 std::vector<string> environment;
202 if (envShort != "C") {
203 // take care of LC_MESSAGES
204 if (envLong != envShort)
205 environment.push_back(envLong);
206 environment.push_back(envShort);
207 // take care of LANGUAGE
208 const char *language_env = getenv("LANGUAGE") == 0 ? "" : getenv("LANGUAGE");
209 string envLang = Locale == 0 ? language_env : *(Locale+1);
210 if (envLang.empty() == false) {
211 std::vector<string> env = VectorizeString(envLang,':');
212 short addedLangs = 0; // add a maximum of 3 fallbacks from the environment
213 for (std::vector<string>::const_iterator e = env.begin();
214 e != env.end() && addedLangs < 3; ++e) {
215 if (unlikely(e->empty() == true) || *e == "en")
216 continue;
217 if (*e == envLong || *e == envShort)
218 continue;
219 if (std::find(environment.begin(), environment.end(), *e) != environment.end())
220 continue;
221 ++addedLangs;
222 environment.push_back(*e);
223 }
224 }
225 } else {
226 environment.push_back("en");
227 }
228
229 // Support settings like Acquire::Languages=none on the command line to
230 // override the configuration settings vector of languages.
231 string const forceLang = _config->Find("Acquire::Languages","");
232 if (forceLang.empty() == false) {
233 if (forceLang == "environment") {
234 codes = environment;
235 } else if (forceLang != "none")
236 codes.push_back(forceLang);
237 allCodes = codes;
238 for (std::vector<string>::const_iterator b = builtin.begin();
239 b != builtin.end(); ++b)
240 if (std::find(allCodes.begin(), allCodes.end(), *b) == allCodes.end())
241 allCodes.push_back(*b);
242 if (All == true)
243 return allCodes;
244 else
245 return codes;
246 }
247
248 // cornercase: LANG=C, so we use only "en" Translation
249 if (envShort == "C") {
250 allCodes = codes = environment;
251 allCodes.insert(allCodes.end(), builtin.begin(), builtin.end());
252 if (All == true)
253 return allCodes;
254 else
255 return codes;
256 }
257
258 std::vector<string> const lang = _config->FindVector("Acquire::Languages");
259 // the default setting -> "environment, en"
260 if (lang.empty() == true) {
261 codes = environment;
262 if (envShort != "en")
263 codes.push_back("en");
264 allCodes = codes;
265 for (std::vector<string>::const_iterator b = builtin.begin();
266 b != builtin.end(); ++b)
267 if (std::find(allCodes.begin(), allCodes.end(), *b) == allCodes.end())
268 allCodes.push_back(*b);
269 if (All == true)
270 return allCodes;
271 else
272 return codes;
273 }
274
275 // the configs define the order, so add the environment
276 // then needed and ensure the codes are not listed twice.
277 bool noneSeen = false;
278 for (std::vector<string>::const_iterator l = lang.begin();
279 l != lang.end(); ++l) {
280 if (*l == "environment") {
281 for (std::vector<string>::const_iterator e = environment.begin();
282 e != environment.end(); ++e) {
283 if (std::find(allCodes.begin(), allCodes.end(), *e) != allCodes.end())
284 continue;
285 if (noneSeen == false)
286 codes.push_back(*e);
287 allCodes.push_back(*e);
288 }
289 continue;
290 } else if (*l == "none") {
291 noneSeen = true;
292 continue;
293 } else if (std::find(allCodes.begin(), allCodes.end(), *l) != allCodes.end())
294 continue;
295
296 if (noneSeen == false)
297 codes.push_back(*l);
298 allCodes.push_back(*l);
299 }
300
301 for (std::vector<string>::const_iterator b = builtin.begin();
302 b != builtin.end(); ++b)
303 if (std::find(allCodes.begin(), allCodes.end(), *b) == allCodes.end())
304 allCodes.push_back(*b);
305
306 if (All == true)
307 return allCodes;
308 else
309 return codes;
310 }
311 /*}}}*/
312 // getArchitectures - Return Vector of prefered Architectures /*{{{*/
313 std::vector<std::string> const Configuration::getArchitectures(bool const &Cached) {
314 using std::string;
315
316 std::vector<string> static archs;
317 if (likely(Cached == true) && archs.empty() == false)
318 return archs;
319
320 string const arch = _config->Find("APT::Architecture");
321 archs = _config->FindVector("APT::Architectures");
322
323 if (unlikely(arch.empty() == true))
324 return archs;
325
326 // FIXME: It is a bit unclean to have debian specific code hereā€¦
327 if (archs.empty() == true) {
328 archs.push_back(arch);
329 string dpkgcall = _config->Find("Dir::Bin::dpkg", "dpkg");
330 std::vector<string> const dpkgoptions = _config->FindVector("DPkg::options");
331 for (std::vector<string>::const_iterator o = dpkgoptions.begin();
332 o != dpkgoptions.end(); ++o)
333 dpkgcall.append(" ").append(*o);
334 dpkgcall.append(" --print-foreign-architectures 2> /dev/null");
335 FILE *dpkg = popen(dpkgcall.c_str(), "r");
336 char buf[1024];
337 if(dpkg != NULL) {
338 if (fgets(buf, sizeof(buf), dpkg) != NULL) {
339 char* arch = strtok(buf, " ");
340 while (arch != NULL) {
341 for (; isspace(*arch) != 0; ++arch);
342 if (arch[0] != '\0') {
343 char const* archend = arch;
344 for (; isspace(*archend) == 0 && *archend != '\0'; ++archend);
345 archs.push_back(string(arch, (archend - arch)));
346 }
347 arch = strtok(NULL, " ");
348 }
349 }
350 pclose(dpkg);
351 }
352 return archs;
353 }
354
355 if (archs.empty() == true ||
356 std::find(archs.begin(), archs.end(), arch) == archs.end())
357 archs.insert(archs.begin(), arch);
358
359 // erase duplicates and empty strings
360 for (std::vector<string>::reverse_iterator a = archs.rbegin();
361 a != archs.rend(); ++a) {
362 if (a->empty() == true || std::find(a + 1, archs.rend(), *a) != archs.rend())
363 archs.erase(a.base()-1);
364 if (a == archs.rend())
365 break;
366 }
367
368 return archs;
369 }
370 /*}}}*/
371 // checkArchitecture - are we interested in the given Architecture? /*{{{*/
372 bool const Configuration::checkArchitecture(std::string const &Arch) {
373 if (Arch == "all")
374 return true;
375 std::vector<std::string> const archs = getArchitectures(true);
376 return (std::find(archs.begin(), archs.end(), Arch) != archs.end());
377 }
378 /*}}}*/
379 // setDefaultConfigurationForCompressors /*{{{*/
380 void Configuration::setDefaultConfigurationForCompressors() {
381 // Set default application paths to check for optional compression types
382 _config->CndSet("Dir::Bin::lzma", "/usr/bin/lzma");
383 _config->CndSet("Dir::Bin::xz", "/usr/bin/xz");
384 _config->CndSet("Dir::Bin::bzip2", "/bin/bzip2");
385 }
386 /*}}}*/
387 // getCompressors - Return Vector of usbale compressors /*{{{*/
388 // ---------------------------------------------------------------------
389 /* return a vector of compressors used by apt-ftparchive in the
390 multicompress functionality or to detect data.tar files */
391 std::vector<APT::Configuration::Compressor>
392 const Configuration::getCompressors(bool const Cached) {
393 static std::vector<APT::Configuration::Compressor> compressors;
394 if (compressors.empty() == false) {
395 if (Cached == true)
396 return compressors;
397 else
398 compressors.clear();
399 }
400
401 setDefaultConfigurationForCompressors();
402
403 compressors.push_back(Compressor(".", "", "", "", "", 1));
404 if (_config->Exists("Dir::Bin::gzip") == false || FileExists(_config->FindFile("Dir::Bin::gzip")) == true)
405 compressors.push_back(Compressor("gzip",".gz","gzip","-9n","-d",2));
406 if (_config->Exists("Dir::Bin::bzip2") == false || FileExists(_config->FindFile("Dir::Bin::bzip2")) == true)
407 compressors.push_back(Compressor("bzip2",".bz2","bzip2","-9","-d",3));
408 if (_config->Exists("Dir::Bin::lzma") == false || FileExists(_config->FindFile("Dir::Bin::lzma")) == true)
409 compressors.push_back(Compressor("lzma",".lzma","lzma","-9","-d",4));
410 if (_config->Exists("Dir::Bin::xz") == false || FileExists(_config->FindFile("Dir::Bin::xz")) == true)
411 compressors.push_back(Compressor("xz",".xz","xz","-6","-d",5));
412
413 std::vector<std::string> const comp = _config->FindVector("APT::Compressor");
414 for (std::vector<std::string>::const_iterator c = comp.begin();
415 c != comp.end(); ++c) {
416 if (*c == "." || *c == "gzip" || *c == "bzip2" || *c == "lzma" || *c == "xz")
417 continue;
418 compressors.push_back(Compressor(c->c_str(), std::string(".").append(*c).c_str(), c->c_str(), "-9", "-d", 100));
419 }
420
421 return compressors;
422 }
423 /*}}}*/
424 // getCompressorExtensions - supported data.tar extensions /*{{{*/
425 // ---------------------------------------------------------------------
426 /* */
427 std::vector<std::string> const Configuration::getCompressorExtensions() {
428 std::vector<APT::Configuration::Compressor> const compressors = getCompressors();
429 std::vector<std::string> ext;
430 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressors.begin();
431 c != compressors.end(); ++c)
432 if (c->Extension.empty() == false && c->Extension != ".")
433 ext.push_back(c->Extension);
434 return ext;
435 }
436 /*}}}*/
437 // Compressor constructor /*{{{*/
438 // ---------------------------------------------------------------------
439 /* */
440 Configuration::Compressor::Compressor(char const *name, char const *extension,
441 char const *binary,
442 char const *compressArg, char const *uncompressArg,
443 unsigned short const cost) {
444 std::string const config = string("APT:Compressor::").append(name).append("::");
445 Name = _config->Find(std::string(config).append("Name"), name);
446 Extension = _config->Find(std::string(config).append("Extension"), extension);
447 Binary = _config->Find(std::string(config).append("Binary"), binary);
448 Cost = _config->FindI(std::string(config).append("Cost"), cost);
449 std::string const compConf = std::string(config).append("CompressArg");
450 if (_config->Exists(compConf) == true)
451 CompressArgs = _config->FindVector(compConf);
452 else if (compressArg != NULL)
453 CompressArgs.push_back(compressArg);
454 std::string const uncompConf = std::string(config).append("UncompressArg");
455 if (_config->Exists(uncompConf) == true)
456 UncompressArgs = _config->FindVector(uncompConf);
457 else if (uncompressArg != NULL)
458 UncompressArgs.push_back(uncompressArg);
459 }
460 /*}}}*/
461 }