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