]> git.saurik.com Git - apt.git/blob - apt-pkg/aptconfiguration.cc
use HashStringList in the acquire system
[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 <dirent.h>
21 #include <stdio.h>
22 #include <fcntl.h>
23 #include <ctype.h>
24 #include <stddef.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <algorithm>
29 #include <string>
30 #include <vector>
31
32 #include <apti18n.h>
33 /*}}}*/
34 namespace APT {
35 // getCompressionTypes - Return Vector of usable compressiontypes /*{{{*/
36 // ---------------------------------------------------------------------
37 /* return a vector of compression types in the preferred order. */
38 std::vector<std::string>
39 const Configuration::getCompressionTypes(bool const &Cached) {
40 static std::vector<std::string> types;
41 if (types.empty() == false) {
42 if (Cached == true)
43 return types;
44 else
45 types.clear();
46 }
47
48 // setup the defaults for the compressiontypes => method mapping
49 _config->CndSet("Acquire::CompressionTypes::bz2","bzip2");
50 _config->CndSet("Acquire::CompressionTypes::xz","xz");
51 _config->CndSet("Acquire::CompressionTypes::lzma","lzma");
52 _config->CndSet("Acquire::CompressionTypes::gz","gzip");
53
54 setDefaultConfigurationForCompressors();
55 std::vector<APT::Configuration::Compressor> const compressors = getCompressors();
56
57 // load the order setting into our vector
58 std::vector<std::string> const order = _config->FindVector("Acquire::CompressionTypes::Order");
59 for (std::vector<std::string>::const_iterator o = order.begin();
60 o != order.end(); ++o) {
61 if ((*o).empty() == true)
62 continue;
63 // ignore types we have no method ready to use
64 std::string const method = std::string("Acquire::CompressionTypes::").append(*o);
65 if (_config->Exists(method) == false)
66 continue;
67 // ignore types we have no app ready to use
68 std::string const app = _config->Find(method);
69 std::vector<APT::Configuration::Compressor>::const_iterator c = compressors.begin();
70 for (; c != compressors.end(); ++c)
71 if (c->Name == app)
72 break;
73 if (c == compressors.end())
74 continue;
75 types.push_back(*o);
76 }
77
78 // move again over the option tree to add all missing compression types
79 ::Configuration::Item const *Types = _config->Tree("Acquire::CompressionTypes");
80 if (Types != 0)
81 Types = Types->Child;
82
83 for (; Types != 0; Types = Types->Next) {
84 if (Types->Tag == "Order" || Types->Tag.empty() == true)
85 continue;
86 // ignore types we already have in the vector
87 if (std::find(types.begin(),types.end(),Types->Tag) != types.end())
88 continue;
89 // ignore types we have no app ready to use
90 std::vector<APT::Configuration::Compressor>::const_iterator c = compressors.begin();
91 for (; c != compressors.end(); ++c)
92 if (c->Name == Types->Value)
93 break;
94 if (c == compressors.end())
95 continue;
96 types.push_back(Types->Tag);
97 }
98
99 // add the special "uncompressed" type
100 if (std::find(types.begin(), types.end(), "uncompressed") == types.end())
101 {
102 std::string const uncompr = _config->FindFile("Dir::Bin::uncompressed", "");
103 if (uncompr.empty() == true || FileExists(uncompr) == true)
104 types.push_back("uncompressed");
105 }
106
107 return types;
108 }
109 /*}}}*/
110 // GetLanguages - Return Vector of Language Codes /*{{{*/
111 // ---------------------------------------------------------------------
112 /* return a vector of language codes in the preferred order.
113 the special word "environment" will be replaced with the long and the short
114 code of the local settings and it will be insured that this will not add
115 duplicates. So in an german local the setting "environment, de_DE, en, de"
116 will result in "de_DE, de, en".
117 The special word "none" is the stopcode for the not-All code vector */
118 std::vector<std::string> const Configuration::getLanguages(bool const &All,
119 bool const &Cached, char const ** const Locale) {
120 using std::string;
121
122 // The detection is boring and has a lot of cornercases,
123 // so we cache the results to calculated it only once.
124 std::vector<string> static allCodes;
125 std::vector<string> static codes;
126
127 // we have something in the cache
128 if (codes.empty() == false || allCodes.empty() == false) {
129 if (Cached == true) {
130 if(All == true && allCodes.empty() == false)
131 return allCodes;
132 else
133 return codes;
134 } else {
135 allCodes.clear();
136 codes.clear();
137 }
138 }
139
140 // Include all Language codes we have a Translation file for in /var/lib/apt/lists
141 // so they will be all included in the Cache.
142 std::vector<string> builtin;
143 DIR *D = opendir(_config->FindDir("Dir::State::lists").c_str());
144 if (D != NULL) {
145 builtin.push_back("none");
146 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D)) {
147 string const name = SubstVar(Ent->d_name, "%5f", "_");
148 size_t const foundDash = name.rfind("-");
149 size_t const foundUnderscore = name.rfind("_", foundDash);
150 if (foundDash == string::npos || foundUnderscore == string::npos ||
151 foundDash <= foundUnderscore ||
152 name.substr(foundUnderscore+1, foundDash-(foundUnderscore+1)) != "Translation")
153 continue;
154 string const c = name.substr(foundDash+1);
155 if (unlikely(c.empty() == true) || c == "en")
156 continue;
157 // Skip unusual files, like backups or that alike
158 string::const_iterator s = c.begin();
159 for (;s != c.end(); ++s) {
160 if (isalpha(*s) == 0 && *s != '_')
161 break;
162 }
163 if (s != c.end())
164 continue;
165 if (std::find(builtin.begin(), builtin.end(), c) != builtin.end())
166 continue;
167 builtin.push_back(c);
168 }
169 closedir(D);
170 }
171
172 // FIXME: Remove support for the old APT::Acquire::Translation
173 // it was undocumented and so it should be not very widthly used
174 string const oldAcquire = _config->Find("APT::Acquire::Translation","");
175 if (oldAcquire.empty() == false && oldAcquire != "environment") {
176 // TRANSLATORS: the two %s are APT configuration options
177 _error->Notice("Option '%s' is deprecated. Please use '%s' instead, see 'man 5 apt.conf' for details.",
178 "APT::Acquire::Translation", "Acquire::Languages");
179 if (oldAcquire != "none")
180 codes.push_back(oldAcquire);
181 codes.push_back("en");
182 allCodes = codes;
183 for (std::vector<string>::const_iterator b = builtin.begin();
184 b != builtin.end(); ++b)
185 if (std::find(allCodes.begin(), allCodes.end(), *b) == allCodes.end())
186 allCodes.push_back(*b);
187 if (All == true)
188 return allCodes;
189 else
190 return codes;
191 }
192
193 // get the environment language codes: LC_MESSAGES (and later LANGUAGE)
194 // we extract both, a long and a short code and then we will
195 // check if we actually need both (rare) or if the short is enough
196 string const envMsg = string(Locale == 0 ? std::setlocale(LC_MESSAGES, NULL) : *Locale);
197 size_t const lenShort = (envMsg.find('_') != string::npos) ? envMsg.find('_') : 2;
198 size_t const lenLong = (envMsg.find_first_of(".@") != string::npos) ? envMsg.find_first_of(".@") : (lenShort + 3);
199
200 string const envLong = envMsg.substr(0,lenLong);
201 string const envShort = envLong.substr(0,lenShort);
202
203 // It is very likely we will need the environment codes later,
204 // so let us generate them now from LC_MESSAGES and LANGUAGE
205 std::vector<string> environment;
206 if (envShort != "C") {
207 // take care of LC_MESSAGES
208 if (envLong != envShort)
209 environment.push_back(envLong);
210 environment.push_back(envShort);
211 // take care of LANGUAGE
212 const char *language_env = getenv("LANGUAGE") == 0 ? "" : getenv("LANGUAGE");
213 string envLang = Locale == 0 ? language_env : *(Locale+1);
214 if (envLang.empty() == false) {
215 std::vector<string> env = VectorizeString(envLang,':');
216 short addedLangs = 0; // add a maximum of 3 fallbacks from the environment
217 for (std::vector<string>::const_iterator e = env.begin();
218 e != env.end() && addedLangs < 3; ++e) {
219 if (unlikely(e->empty() == true) || *e == "en")
220 continue;
221 if (*e == envLong || *e == envShort)
222 continue;
223 if (std::find(environment.begin(), environment.end(), *e) != environment.end())
224 continue;
225 ++addedLangs;
226 environment.push_back(*e);
227 }
228 }
229 } else {
230 // cornercase: LANG=C, so we use only "en" Translation
231 environment.push_back("en");
232 }
233
234 std::vector<string> const lang = _config->FindVector("Acquire::Languages", "environment,en");
235 // the configs define the order, so add the environment
236 // then needed and ensure the codes are not listed twice.
237 bool noneSeen = false;
238 for (std::vector<string>::const_iterator l = lang.begin();
239 l != lang.end(); ++l) {
240 if (*l == "environment") {
241 for (std::vector<string>::const_iterator e = environment.begin();
242 e != environment.end(); ++e) {
243 if (std::find(allCodes.begin(), allCodes.end(), *e) != allCodes.end())
244 continue;
245 if (noneSeen == false)
246 codes.push_back(*e);
247 allCodes.push_back(*e);
248 }
249 continue;
250 } else if (*l == "none") {
251 noneSeen = true;
252 continue;
253 } else if (std::find(allCodes.begin(), allCodes.end(), *l) != allCodes.end())
254 continue;
255
256 if (noneSeen == false)
257 codes.push_back(*l);
258 allCodes.push_back(*l);
259 }
260
261 if (allCodes.empty() == false) {
262 for (std::vector<string>::const_iterator b = builtin.begin();
263 b != builtin.end(); ++b)
264 if (std::find(allCodes.begin(), allCodes.end(), *b) == allCodes.end())
265 allCodes.push_back(*b);
266 } else {
267 // "none" was forced
268 allCodes.push_back("none");
269 }
270
271 if (All == true)
272 return allCodes;
273 else
274 return codes;
275 }
276 /*}}}*/
277 // checkLanguage - are we interested in the given Language? /*{{{*/
278 bool Configuration::checkLanguage(std::string Lang, bool const All) {
279 // the empty Language is always interesting as it is the original
280 if (Lang.empty() == true)
281 return true;
282 // filenames are encoded, so undo this
283 Lang = SubstVar(Lang, "%5f", "_");
284 std::vector<std::string> const langs = getLanguages(All, true);
285 return (std::find(langs.begin(), langs.end(), Lang) != langs.end());
286 }
287 /*}}}*/
288 // getArchitectures - Return Vector of preferred Architectures /*{{{*/
289 std::vector<std::string> const Configuration::getArchitectures(bool const &Cached) {
290 using std::string;
291
292 std::vector<string> static archs;
293 if (likely(Cached == true) && archs.empty() == false)
294 return archs;
295
296 string const arch = _config->Find("APT::Architecture");
297 archs = _config->FindVector("APT::Architectures");
298
299 if (unlikely(arch.empty() == true))
300 return archs;
301
302 // FIXME: It is a bit unclean to have debian specific code here…
303 if (archs.empty() == true) {
304 archs.push_back(arch);
305
306 // Generate the base argument list for dpkg
307 std::vector<const char *> Args;
308 string Tmp = _config->Find("Dir::Bin::dpkg","dpkg");
309 {
310 string const dpkgChrootDir = _config->FindDir("DPkg::Chroot-Directory", "/");
311 size_t dpkgChrootLen = dpkgChrootDir.length();
312 if (dpkgChrootDir != "/" && Tmp.find(dpkgChrootDir) == 0) {
313 if (dpkgChrootDir[dpkgChrootLen - 1] == '/')
314 --dpkgChrootLen;
315 Tmp = Tmp.substr(dpkgChrootLen);
316 }
317 }
318 Args.push_back(Tmp.c_str());
319
320 // Stick in any custom dpkg options
321 ::Configuration::Item const *Opts = _config->Tree("DPkg::Options");
322 if (Opts != 0) {
323 Opts = Opts->Child;
324 for (; Opts != 0; Opts = Opts->Next)
325 {
326 if (Opts->Value.empty() == true)
327 continue;
328 Args.push_back(Opts->Value.c_str());
329 }
330 }
331
332 Args.push_back("--print-foreign-architectures");
333 Args.push_back(NULL);
334
335 int external[2] = {-1, -1};
336 if (pipe(external) != 0)
337 {
338 _error->WarningE("getArchitecture", "Can't create IPC pipe for dpkg --print-foreign-architectures");
339 return archs;
340 }
341
342 pid_t dpkgMultiArch = ExecFork();
343 if (dpkgMultiArch == 0) {
344 close(external[0]);
345 std::string const chrootDir = _config->FindDir("DPkg::Chroot-Directory");
346 int const nullfd = open("/dev/null", O_RDONLY);
347 dup2(nullfd, STDIN_FILENO);
348 dup2(external[1], STDOUT_FILENO);
349 dup2(nullfd, STDERR_FILENO);
350 if (chrootDir != "/" && chroot(chrootDir.c_str()) != 0 && chdir("/") != 0)
351 _error->WarningE("getArchitecture", "Couldn't chroot into %s for dpkg --print-foreign-architectures", chrootDir.c_str());
352 execvp(Args[0], (char**) &Args[0]);
353 _error->WarningE("getArchitecture", "Can't detect foreign architectures supported by dpkg!");
354 _exit(100);
355 }
356 close(external[1]);
357
358 FILE *dpkg = fdopen(external[0], "r");
359 if(dpkg != NULL) {
360 char buf[1024];
361 while (fgets(buf, sizeof(buf), dpkg) != NULL) {
362 char* arch = strtok(buf, " ");
363 while (arch != NULL) {
364 for (; isspace(*arch) != 0; ++arch);
365 if (arch[0] != '\0') {
366 char const* archend = arch;
367 for (; isspace(*archend) == 0 && *archend != '\0'; ++archend);
368 string a(arch, (archend - arch));
369 if (std::find(archs.begin(), archs.end(), a) == archs.end())
370 archs.push_back(a);
371 }
372 arch = strtok(NULL, " ");
373 }
374 }
375 fclose(dpkg);
376 }
377 ExecWait(dpkgMultiArch, "dpkg --print-foreign-architectures", true);
378 return archs;
379 }
380
381 if (archs.empty() == true ||
382 std::find(archs.begin(), archs.end(), arch) == archs.end())
383 archs.insert(archs.begin(), arch);
384
385 // erase duplicates and empty strings
386 for (std::vector<string>::reverse_iterator a = archs.rbegin();
387 a != archs.rend(); ++a) {
388 if (a->empty() == true || std::find(a + 1, archs.rend(), *a) != archs.rend())
389 archs.erase(a.base()-1);
390 if (a == archs.rend())
391 break;
392 }
393
394 return archs;
395 }
396 /*}}}*/
397 // checkArchitecture - are we interested in the given Architecture? /*{{{*/
398 bool Configuration::checkArchitecture(std::string const &Arch) {
399 if (Arch == "all")
400 return true;
401 std::vector<std::string> const archs = getArchitectures(true);
402 return (std::find(archs.begin(), archs.end(), Arch) != archs.end());
403 }
404 /*}}}*/
405 // setDefaultConfigurationForCompressors /*{{{*/
406 void Configuration::setDefaultConfigurationForCompressors() {
407 // Set default application paths to check for optional compression types
408 _config->CndSet("Dir::Bin::bzip2", "/bin/bzip2");
409 _config->CndSet("Dir::Bin::xz", "/usr/bin/xz");
410 if (FileExists(_config->FindFile("Dir::Bin::xz")) == true) {
411 _config->Set("Dir::Bin::lzma", _config->FindFile("Dir::Bin::xz"));
412 _config->Set("APT::Compressor::lzma::Binary", "xz");
413 if (_config->Exists("APT::Compressor::lzma::CompressArg") == false) {
414 _config->Set("APT::Compressor::lzma::CompressArg::", "--format=lzma");
415 _config->Set("APT::Compressor::lzma::CompressArg::", "-9");
416 }
417 if (_config->Exists("APT::Compressor::lzma::UncompressArg") == false) {
418 _config->Set("APT::Compressor::lzma::UncompressArg::", "--format=lzma");
419 _config->Set("APT::Compressor::lzma::UncompressArg::", "-d");
420 }
421 } else {
422 _config->CndSet("Dir::Bin::lzma", "/usr/bin/lzma");
423 if (_config->Exists("APT::Compressor::lzma::CompressArg") == false) {
424 _config->Set("APT::Compressor::lzma::CompressArg::", "--suffix=");
425 _config->Set("APT::Compressor::lzma::CompressArg::", "-9");
426 }
427 if (_config->Exists("APT::Compressor::lzma::UncompressArg") == false) {
428 _config->Set("APT::Compressor::lzma::UncompressArg::", "--suffix=");
429 _config->Set("APT::Compressor::lzma::UncompressArg::", "-d");
430 }
431 }
432 }
433 /*}}}*/
434 // getCompressors - Return Vector of usealbe compressors /*{{{*/
435 // ---------------------------------------------------------------------
436 /* return a vector of compressors used by apt-ftparchive in the
437 multicompress functionality or to detect data.tar files */
438 std::vector<APT::Configuration::Compressor>
439 const Configuration::getCompressors(bool const Cached) {
440 static std::vector<APT::Configuration::Compressor> compressors;
441 if (compressors.empty() == false) {
442 if (Cached == true)
443 return compressors;
444 else
445 compressors.clear();
446 }
447
448 setDefaultConfigurationForCompressors();
449
450 compressors.push_back(Compressor(".", "", "", NULL, NULL, 1));
451 if (_config->Exists("Dir::Bin::gzip") == false || FileExists(_config->FindFile("Dir::Bin::gzip")) == true)
452 compressors.push_back(Compressor("gzip",".gz","gzip","-9n","-d",2));
453 #ifdef HAVE_ZLIB
454 else
455 compressors.push_back(Compressor("gzip",".gz","false", NULL, NULL, 2));
456 #endif
457 if (_config->Exists("Dir::Bin::bzip2") == false || FileExists(_config->FindFile("Dir::Bin::bzip2")) == true)
458 compressors.push_back(Compressor("bzip2",".bz2","bzip2","-9","-d",3));
459 #ifdef HAVE_BZ2
460 else
461 compressors.push_back(Compressor("bzip2",".bz2","false", NULL, NULL, 3));
462 #endif
463 if (_config->Exists("Dir::Bin::xz") == false || FileExists(_config->FindFile("Dir::Bin::xz")) == true)
464 compressors.push_back(Compressor("xz",".xz","xz","-6","-d",4));
465 #ifdef HAVE_LZMA
466 else
467 compressors.push_back(Compressor("xz",".xz","false", NULL, NULL, 4));
468 #endif
469 if (_config->Exists("Dir::Bin::lzma") == false || FileExists(_config->FindFile("Dir::Bin::lzma")) == true)
470 compressors.push_back(Compressor("lzma",".lzma","lzma","-9","-d",5));
471 #ifdef HAVE_LZMA
472 else
473 compressors.push_back(Compressor("lzma",".lzma","false", NULL, NULL, 5));
474 #endif
475
476 std::vector<std::string> const comp = _config->FindVector("APT::Compressor");
477 for (std::vector<std::string>::const_iterator c = comp.begin();
478 c != comp.end(); ++c) {
479 if (c->empty() || *c == "." || *c == "gzip" || *c == "bzip2" || *c == "lzma" || *c == "xz")
480 continue;
481 compressors.push_back(Compressor(c->c_str(), std::string(".").append(*c).c_str(), c->c_str(), "-9", "-d", 100));
482 }
483
484 return compressors;
485 }
486 /*}}}*/
487 // getCompressorExtensions - supported data.tar extensions /*{{{*/
488 // ---------------------------------------------------------------------
489 /* */
490 std::vector<std::string> const Configuration::getCompressorExtensions() {
491 std::vector<APT::Configuration::Compressor> const compressors = getCompressors();
492 std::vector<std::string> ext;
493 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressors.begin();
494 c != compressors.end(); ++c)
495 if (c->Extension.empty() == false && c->Extension != ".")
496 ext.push_back(c->Extension);
497 return ext;
498 }
499 /*}}}*/
500 // Compressor constructor /*{{{*/
501 // ---------------------------------------------------------------------
502 /* */
503 Configuration::Compressor::Compressor(char const *name, char const *extension,
504 char const *binary,
505 char const *compressArg, char const *uncompressArg,
506 unsigned short const cost) {
507 std::string const config = std::string("APT::Compressor::").append(name).append("::");
508 Name = _config->Find(std::string(config).append("Name"), name);
509 Extension = _config->Find(std::string(config).append("Extension"), extension);
510 Binary = _config->Find(std::string(config).append("Binary"), binary);
511 Cost = _config->FindI(std::string(config).append("Cost"), cost);
512 std::string const compConf = std::string(config).append("CompressArg");
513 if (_config->Exists(compConf) == true)
514 CompressArgs = _config->FindVector(compConf);
515 else if (compressArg != NULL)
516 CompressArgs.push_back(compressArg);
517 std::string const uncompConf = std::string(config).append("UncompressArg");
518 if (_config->Exists(uncompConf) == true)
519 UncompressArgs = _config->FindVector(uncompConf);
520 else if (uncompressArg != NULL)
521 UncompressArgs.push_back(uncompressArg);
522 }
523 /*}}}*/
524 // getBuildProfiles - return a vector of enabled build profiles /*{{{*/
525 std::vector<std::string> const Configuration::getBuildProfiles() {
526 // order is: override value (~= commandline), environment variable, list (~= config file)
527 std::string profiles_env = getenv("DEB_BUILD_PROFILES") == 0 ? "" : getenv("DEB_BUILD_PROFILES");
528 if (profiles_env.empty() == false) {
529 profiles_env = SubstVar(profiles_env, " ", ",");
530 std::string const bp = _config->Find("APT::Build-Profiles");
531 _config->Clear("APT::Build-Profiles");
532 if (bp.empty() == false)
533 _config->Set("APT::Build-Profiles", bp);
534 }
535 return _config->FindVector("APT::Build-Profiles", profiles_env);
536 }
537 std::string const Configuration::getBuildProfilesString() {
538 std::vector<std::string> profiles = getBuildProfiles();
539 if (profiles.empty() == true)
540 return "";
541 std::vector<std::string>::const_iterator p = profiles.begin();
542 std::string list = *p;
543 for (; p != profiles.end(); ++p)
544 list.append(",").append(*p);
545 return list;
546 }
547 /*}}}*/
548 }