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