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