/src/aspell/common/config.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // This file is part of The New Aspell |
2 | | // Copyright (C) 2001 by Kevin Atkinson under the GNU LGPL license |
3 | | // version 2.0 or 2.1. You should have received a copy of the LGPL |
4 | | // license along with this library if you did not you can find |
5 | | // it at http://www.gnu.org/. |
6 | | |
7 | | //#include <stdio.h> |
8 | | //#define DEBUG {fprintf(stderr,"File: %s(%i)\n",__FILE__,__LINE__);} |
9 | | #include <string.h> |
10 | | #include <stdlib.h> |
11 | | #include "ndebug.hpp" |
12 | | #include <assert.h> |
13 | | |
14 | | #include "dirs.h" |
15 | | #include "settings.h" |
16 | | |
17 | | #ifdef USE_LOCALE |
18 | | # include <locale.h> |
19 | | #endif |
20 | | |
21 | | #ifdef HAVE_LANGINFO_CODESET |
22 | | # include <langinfo.h> |
23 | | #endif |
24 | | |
25 | | #include "cache.hpp" |
26 | | #include "asc_ctype.hpp" |
27 | | #include "config.hpp" |
28 | | #include "errors.hpp" |
29 | | #include "file_util.hpp" |
30 | | #include "fstream.hpp" |
31 | | #include "getdata.hpp" |
32 | | #include "itemize.hpp" |
33 | | #include "mutable_container.hpp" |
34 | | #include "posib_err.hpp" |
35 | | #include "string_map.hpp" |
36 | | #include "stack_ptr.hpp" |
37 | | #include "char_vector.hpp" |
38 | | #include "convert.hpp" |
39 | | #include "vararray.hpp" |
40 | | #include "string_list.hpp" |
41 | | |
42 | | #include "gettext.h" |
43 | | |
44 | | #include "iostream.hpp" |
45 | | |
46 | 2.14k | #define DEFAULT_LANG "en_US" |
47 | | |
48 | | // NOTE: All filter options are now stored with he "f-" prefix. However |
49 | | // during lookup, the non prefix version is also recognized. |
50 | | |
51 | | // The "place_holder" field in Entry and the "Vector<int>" parameter of |
52 | | // commit_all are there to deal with the fact that with spacing |
53 | | // options on the command line such as "--key what" it cannot be |
54 | | // determined if "what" should be a value of "key" or if it should |
55 | | // be treated as an independent arg. This is because "key" may |
56 | | // be a filter option. Filter options KeyInfo are not loaded until |
57 | | // after a commit which is not done at the time the options are being |
58 | | // read in from the command line. (If the command line arguments are |
59 | | // read in after the other settings are read in and committed, then any |
60 | | // options setting any of the config files will be ignored. Thus, the |
61 | | // command line must be parsed and options must be added in an |
62 | | // uncommitted state). So the solution is to assume it is an |
63 | | // independent arg until told otherwise, the position in the arg array |
64 | | // is stored along with the value in the "place_holder" field. When |
65 | | // the config class is finally committed and it is determined that |
66 | | // "what" is really a value for key the stored arg position is pushed |
67 | | // onto the Vector<int> so it can be removed from the arg array. In |
68 | | // the case of a "lset-*" this will happen in multiple config |
69 | | // "Entry"s, so care is taken to only add the arg position once. |
70 | | |
71 | | namespace acommon { |
72 | | |
73 | | const char * const keyinfo_type_name[4] = { |
74 | | N_("string"), N_("integer"), N_("boolean"), N_("list") |
75 | | }; |
76 | | |
77 | | const int Config::num_parms_[9] = {1, 1, 0, 0, 0, |
78 | | 1, 1, 1, 0}; |
79 | | |
80 | | typedef Notifier * NotifierPtr; |
81 | | |
82 | | Config::Config(ParmStr name, |
83 | | const KeyInfo * mainbegin, |
84 | | const KeyInfo * mainend) |
85 | | : name_(name) |
86 | | , first_(0), insert_point_(&first_) |
87 | | , committed_(true), attached_(false) |
88 | | , md_info_list_index(-1) |
89 | | , settings_read_in_(false) |
90 | | , load_filter_hook(0) |
91 | | , filter_mode_notifier(0) |
92 | 2.30k | { |
93 | 2.30k | keyinfo_begin = mainbegin; |
94 | 2.30k | keyinfo_end = mainend; |
95 | 2.30k | extra_begin = 0; |
96 | 2.30k | extra_end = 0; |
97 | 2.30k | } |
98 | | |
99 | 2.30k | Config::~Config() { |
100 | 2.30k | del(); |
101 | 2.30k | } |
102 | | |
103 | | Config::Config(const Config & other) |
104 | 0 | { |
105 | 0 | copy(other); |
106 | 0 | } |
107 | | |
108 | | Config & Config::operator= (const Config & other) |
109 | 0 | { |
110 | 0 | del(); |
111 | 0 | copy(other); |
112 | 0 | return *this; |
113 | 0 | } |
114 | | |
115 | 0 | Config * Config::clone() const { |
116 | 0 | return new Config(*this); |
117 | 0 | } |
118 | | |
119 | 0 | void Config::assign(const Config * other) { |
120 | 0 | *this = *(const Config *)(other); |
121 | 0 | } |
122 | | |
123 | | void Config::copy(const Config & other) |
124 | 0 | { |
125 | 0 | name_ = other.name_; |
126 | |
|
127 | 0 | committed_ = other.committed_; |
128 | 0 | attached_ = other.attached_; |
129 | 0 | settings_read_in_ = other.settings_read_in_; |
130 | |
|
131 | 0 | keyinfo_begin = other.keyinfo_begin; |
132 | 0 | keyinfo_end = other.keyinfo_end; |
133 | 0 | extra_begin = other.extra_begin; |
134 | 0 | extra_end = other.extra_end; |
135 | 0 | filter_modules = other.filter_modules; |
136 | |
|
137 | 0 | #ifdef HAVE_LIBDL |
138 | 0 | filter_modules_ptrs = other.filter_modules_ptrs; |
139 | 0 | for (Vector<Cacheable *>::iterator i = filter_modules_ptrs.begin(); |
140 | 0 | i != filter_modules_ptrs.end(); |
141 | 0 | ++i) |
142 | 0 | (*i)->copy(); |
143 | 0 | #endif |
144 | |
|
145 | 0 | md_info_list_index = other.md_info_list_index; |
146 | |
|
147 | 0 | insert_point_ = 0; |
148 | 0 | Entry * const * src = &other.first_; |
149 | 0 | Entry * * dest = &first_; |
150 | 0 | while (*src) |
151 | 0 | { |
152 | 0 | *dest = new Entry(**src); |
153 | 0 | if (src == other.insert_point_) |
154 | 0 | insert_point_ = dest; |
155 | 0 | src = &((*src)->next); |
156 | 0 | dest = &((*dest)->next); |
157 | 0 | } |
158 | 0 | if (insert_point_ == 0) |
159 | 0 | insert_point_ = dest; |
160 | 0 | *dest = 0; |
161 | |
|
162 | 0 | Vector<Notifier *>::const_iterator i = other.notifier_list.begin(); |
163 | 0 | Vector<Notifier *>::const_iterator end = other.notifier_list.end(); |
164 | |
|
165 | 0 | for(; i != end; ++i) { |
166 | 0 | Notifier * tmp = (*i)->clone(this); |
167 | 0 | if (tmp != 0) |
168 | 0 | notifier_list.push_back(tmp); |
169 | 0 | } |
170 | 0 | } |
171 | | |
172 | | void Config::del() |
173 | 2.30k | { |
174 | 111k | while (first_) { |
175 | 109k | Entry * tmp = first_->next; |
176 | 109k | delete first_; |
177 | 109k | first_ = tmp; |
178 | 109k | } |
179 | | |
180 | 2.30k | Vector<Notifier *>::iterator i = notifier_list.begin(); |
181 | 2.30k | Vector<Notifier *>::iterator end = notifier_list.end(); |
182 | | |
183 | 4.59k | for(; i != end; ++i) { |
184 | 2.29k | delete (*i); |
185 | 2.29k | *i = 0; |
186 | 2.29k | } |
187 | | |
188 | 2.30k | notifier_list.clear(); |
189 | | |
190 | 2.30k | #ifdef HAVE_LIBDL |
191 | 2.30k | filter_modules.clear(); |
192 | 2.30k | for (Vector<Cacheable *>::iterator i = filter_modules_ptrs.begin(); |
193 | 2.30k | i != filter_modules_ptrs.end(); |
194 | 2.30k | ++i) |
195 | 0 | (*i)->release(); |
196 | 2.30k | filter_modules_ptrs.clear(); |
197 | 2.30k | #endif |
198 | 2.30k | } |
199 | | |
200 | | void Config::set_filter_modules(const ConfigModule * modbegin, |
201 | | const ConfigModule * modend) |
202 | 1.58k | { |
203 | 1.58k | assert(filter_modules_ptrs.empty()); |
204 | 0 | filter_modules.clear(); |
205 | 1.58k | filter_modules.assign(modbegin, modend); |
206 | 1.58k | } |
207 | | |
208 | | void Config::set_extra(const KeyInfo * begin, |
209 | | const KeyInfo * end) |
210 | 0 | { |
211 | 0 | extra_begin = begin; |
212 | 0 | extra_end = end; |
213 | 0 | } |
214 | | |
215 | | // |
216 | | // |
217 | | // |
218 | | |
219 | | |
220 | | // |
221 | | // Notifier methods |
222 | | // |
223 | | |
224 | | NotifierEnumeration * Config::notifiers() const |
225 | 0 | { |
226 | 0 | return new NotifierEnumeration(notifier_list); |
227 | 0 | } |
228 | | |
229 | | bool Config::add_notifier(Notifier * n) |
230 | 2.29k | { |
231 | 2.29k | Vector<Notifier *>::iterator i = notifier_list.begin(); |
232 | 2.29k | Vector<Notifier *>::iterator end = notifier_list.end(); |
233 | | |
234 | 3.00k | while (i != end && *i != n) |
235 | 707 | ++i; |
236 | | |
237 | 2.29k | if (i != end) { |
238 | | |
239 | 0 | return false; |
240 | | |
241 | 2.29k | } else { |
242 | | |
243 | 2.29k | notifier_list.push_back(n); |
244 | 2.29k | return true; |
245 | | |
246 | 2.29k | } |
247 | 2.29k | } |
248 | | |
249 | | bool Config::remove_notifier(const Notifier * n) |
250 | 0 | { |
251 | 0 | Vector<Notifier *>::iterator i = notifier_list.begin(); |
252 | 0 | Vector<Notifier *>::iterator end = notifier_list.end(); |
253 | |
|
254 | 0 | while (i != end && *i != n) |
255 | 0 | ++i; |
256 | |
|
257 | 0 | if (i == end) { |
258 | | |
259 | 0 | return false; |
260 | | |
261 | 0 | } else { |
262 | |
|
263 | 0 | delete *i; |
264 | 0 | notifier_list.erase(i); |
265 | 0 | return true; |
266 | |
|
267 | 0 | } |
268 | 0 | } |
269 | | |
270 | | bool Config::replace_notifier(const Notifier * o, |
271 | | Notifier * n) |
272 | 0 | { |
273 | 0 | Vector<Notifier *>::iterator i = notifier_list.begin(); |
274 | 0 | Vector<Notifier *>::iterator end = notifier_list.end(); |
275 | |
|
276 | 0 | while (i != end && *i != o) |
277 | 0 | ++i; |
278 | |
|
279 | 0 | if (i == end) { |
280 | | |
281 | 0 | return false; |
282 | | |
283 | 0 | } else { |
284 | |
|
285 | 0 | delete *i; |
286 | 0 | *i = n; |
287 | 0 | return true; |
288 | |
|
289 | 0 | } |
290 | 0 | } |
291 | | |
292 | | // |
293 | | // retrieve methods |
294 | | // |
295 | | |
296 | | const Config::Entry * Config::lookup(const char * key) const |
297 | 104k | { |
298 | 104k | const Entry * res = 0; |
299 | 104k | const Entry * cur = first_; |
300 | | |
301 | 6.71M | while (cur) { |
302 | 6.60M | if (cur->key == key && cur->action != NoOp) res = cur; |
303 | 6.60M | cur = cur->next; |
304 | 6.60M | } |
305 | | |
306 | 104k | if (!res || res->action == Reset) return 0; |
307 | 23.4k | return res; |
308 | 104k | } |
309 | | |
310 | | bool Config::have(ParmStr key) const |
311 | 11.7k | { |
312 | 11.7k | PosibErr<const KeyInfo *> pe = keyinfo(key); |
313 | 11.7k | if (pe.has_err()) {pe.ignore_err(); return false;} |
314 | 11.7k | return lookup(pe.data->name); |
315 | 11.7k | } |
316 | | |
317 | | PosibErr<String> Config::retrieve(ParmStr key) const |
318 | 66.0k | { |
319 | 66.0k | RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki); |
320 | 66.0k | if (ki->type == KeyInfoList) return make_err(key_not_string, ki->name); |
321 | | |
322 | 66.0k | const Entry * cur = lookup(ki->name); |
323 | | |
324 | 66.0k | return cur ? cur->value : get_default(ki); |
325 | 66.0k | } |
326 | | |
327 | | PosibErr<Config::Value> Config::retrieve_value(ParmStr key) const |
328 | 1.43k | { |
329 | 1.43k | RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki); |
330 | 1.43k | if (ki->type == KeyInfoList) return make_err(key_not_string, ki->name); |
331 | | |
332 | 1.43k | const Entry * cur = lookup(ki->name); |
333 | | |
334 | 1.43k | return cur ? Value(cur->value,cur->secure) : Value(get_default(ki), true); |
335 | 1.43k | } |
336 | | |
337 | | PosibErr<String> Config::retrieve_any(ParmStr key) const |
338 | 0 | { |
339 | 0 | RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki); |
340 | | |
341 | 0 | if (ki->type != KeyInfoList) { |
342 | 0 | const Entry * cur = lookup(ki->name); |
343 | 0 | return cur ? cur->value : get_default(ki); |
344 | 0 | } else { |
345 | 0 | StringList sl; |
346 | 0 | RET_ON_ERR(retrieve_list(key, &sl)); |
347 | 0 | StringListEnumeration els = sl.elements_obj(); |
348 | 0 | const char * s; |
349 | 0 | String val; |
350 | 0 | while ( (s = els.next()) != 0 ) { |
351 | 0 | val += s; |
352 | 0 | val += '\n'; |
353 | 0 | } |
354 | 0 | val.pop_back(); |
355 | 0 | return val; |
356 | 0 | } |
357 | 0 | } |
358 | | |
359 | | PosibErr<bool> Config::retrieve_bool(ParmStr key) const |
360 | 20.0k | { |
361 | 20.0k | RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki); |
362 | 20.0k | if (ki->type != KeyInfoBool) return make_err(key_not_bool, ki->name); |
363 | | |
364 | 20.0k | const Entry * cur = lookup(ki->name); |
365 | | |
366 | 20.0k | String value(cur ? cur->value : get_default(ki)); |
367 | | |
368 | 20.0k | if (value == "false") return false; |
369 | 12.0k | else return true; |
370 | 20.0k | } |
371 | | |
372 | | PosibErr<int> Config::retrieve_int(ParmStr key) const |
373 | 2.18k | { |
374 | 2.18k | assert(committed_); // otherwise the value may not be an integer |
375 | | // as it has not been verified. |
376 | | |
377 | 2.18k | RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki); |
378 | 2.18k | if (ki->type != KeyInfoInt) return make_err(key_not_int, ki->name); |
379 | | |
380 | 2.18k | const Entry * cur = lookup(ki->name); |
381 | | |
382 | 2.18k | String value(cur ? cur->value : get_default(ki)); |
383 | | |
384 | 2.18k | return atoi(value.str()); |
385 | 2.18k | } |
386 | | |
387 | | #define RET_ON_ERR_WRAP(prefix, key, cmd) \ |
388 | 156k | do{PosibErrBase pe(cmd);if(pe.has_err())return pe.with_key(prefix, strncmp(key, "f-", 2) == 0 ? key + 2 : key);}while(false) |
389 | | |
390 | | PosibErr<void> Config::lookup_list(const KeyInfo * ki, |
391 | | MutableContainer & m, |
392 | | bool include_default) const |
393 | 12.5k | { |
394 | 12.5k | const Entry * cur = first_; |
395 | 12.5k | const Entry * first_to_use = 0; |
396 | | |
397 | 1.04M | while (cur) { |
398 | 1.03M | if (cur->key == ki->name && |
399 | 1.03M | (first_to_use == 0 || |
400 | 181k | cur->action == Reset || cur->action == Set |
401 | 181k | || cur->action == ListClear)) |
402 | 6.83k | first_to_use = cur; |
403 | 1.03M | cur = cur->next; |
404 | 1.03M | } |
405 | | |
406 | 12.5k | cur = first_to_use; |
407 | | |
408 | 12.5k | if (include_default && |
409 | 12.5k | (!cur || |
410 | 12.5k | !(cur->action == Set || cur->action == ListClear))) |
411 | 9.99k | { |
412 | 9.99k | String def = get_default(ki); |
413 | 9.99k | separate_list(def, m, true); |
414 | 9.99k | } |
415 | | |
416 | 12.5k | if (cur && cur->action == Reset) { |
417 | 67 | cur = cur->next; |
418 | 67 | } |
419 | | |
420 | 12.5k | if (cur && cur->action == Set) { |
421 | 1.82k | if (!include_default) m.clear(); |
422 | 1.82k | RET_ON_ERR_WRAP("", ki->name, m.add(cur->value)); |
423 | 1.82k | cur = cur->next; |
424 | 1.82k | } |
425 | | |
426 | 12.5k | if (cur && cur->action == ListClear) { |
427 | 799 | if (!include_default) m.clear(); |
428 | 799 | cur = cur->next; |
429 | 799 | } |
430 | | |
431 | 308k | while (cur) { |
432 | 295k | if (cur->key == ki->name) { |
433 | 154k | if (cur->action == ListAdd) |
434 | 84.5k | RET_ON_ERR_WRAP("add-", ki->name, m.add(cur->value)); |
435 | 69.9k | else if (cur->action == ListRemove) |
436 | 69.9k | RET_ON_ERR_WRAP("remove-", ki->name, m.remove(cur->value)); |
437 | 154k | } |
438 | 295k | cur = cur->next; |
439 | 295k | } |
440 | 12.5k | return no_err; |
441 | 12.5k | } |
442 | | |
443 | | #undef RET_ON_ERR_WRAP |
444 | | |
445 | | PosibErr<void> Config::retrieve_list(ParmStr key, |
446 | | MutableContainer * m) const |
447 | 12.5k | { |
448 | 12.5k | RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki); |
449 | 12.5k | if (ki->type != KeyInfoList) return make_err(key_not_list, ki->name); |
450 | | |
451 | 12.5k | RET_ON_ERR(lookup_list(ki, *m, true)); |
452 | | |
453 | 12.5k | return no_err; |
454 | 12.5k | } |
455 | | |
456 | | static const KeyInfo * find(ParmStr key, |
457 | | const KeyInfo * i, |
458 | | const KeyInfo * end) |
459 | 289k | { |
460 | 5.35M | while (i != end) { |
461 | 5.30M | if (strcmp(key, i->name) == 0) |
462 | 236k | return i; |
463 | 5.06M | ++i; |
464 | 5.06M | } |
465 | 53.2k | return i; |
466 | 289k | } |
467 | | |
468 | | static const ConfigModule * find(ParmStr key, |
469 | | const ConfigModule * i, |
470 | | const ConfigModule * end) |
471 | 23.0k | { |
472 | 160k | while (i != end) { |
473 | 153k | if (strcmp(key, i->name) == 0) |
474 | 16.2k | return i; |
475 | 137k | ++i; |
476 | 137k | } |
477 | 6.83k | return i; |
478 | 23.0k | } |
479 | | |
480 | | PosibErr<const KeyInfo *> Config::keyinfo(ParmStr key) const |
481 | 243k | { |
482 | 243k | typedef PosibErr<const KeyInfo *> Ret; |
483 | 243k | { |
484 | 243k | const KeyInfo * i; |
485 | 243k | i = acommon::find(key, keyinfo_begin, keyinfo_end); |
486 | 243k | if (i != keyinfo_end) return Ret(i); |
487 | | |
488 | 23.0k | i = acommon::find(key, extra_begin, extra_end); |
489 | 23.0k | if (i != extra_end) return Ret(i); |
490 | | |
491 | 23.0k | const char * s = strncmp(key, "f-", 2) == 0 ? key + 2 : key.str(); |
492 | 23.0k | const char * h = strchr(s, '-'); |
493 | 23.0k | if (h == 0) goto err; |
494 | | |
495 | 19.6k | String k(s, h - s); |
496 | 19.6k | const ConfigModule * j = acommon::find(k, |
497 | 19.6k | filter_modules.pbegin(), |
498 | 19.6k | filter_modules.pend()); |
499 | | |
500 | 19.6k | if (j == filter_modules.pend() && load_filter_hook && committed_) { |
501 | | // FIXME: This isn't quite right |
502 | 3.41k | PosibErrBase pe = load_filter_hook(const_cast<Config *>(this), k); |
503 | 3.41k | pe.ignore_err(); |
504 | 3.41k | j = acommon::find(k, |
505 | 3.41k | filter_modules.pbegin(), |
506 | 3.41k | filter_modules.pend()); |
507 | 3.41k | } |
508 | | |
509 | 19.6k | if (j == filter_modules.pend()) goto err; |
510 | | |
511 | 16.2k | i = acommon::find(key, j->begin, j->end); |
512 | 16.2k | if (i != j->end) return Ret(i); |
513 | | |
514 | 6.75k | if (strncmp(key, "f-", 2) != 0) k = "f-"; |
515 | 61 | else k = ""; |
516 | 6.75k | k += key; |
517 | 6.75k | i = acommon::find(k, j->begin, j->end); |
518 | 6.75k | if (i != j->end) return Ret(i); |
519 | 6.75k | } |
520 | 7.18k | err: |
521 | 7.18k | return Ret().prim_err(unknown_key, key); |
522 | 6.75k | } |
523 | | |
524 | | static bool proc_locale_str(ParmStr lang, String & final_str) |
525 | 8.57k | { |
526 | 8.57k | if (lang == 0) return false; |
527 | 2.14k | const char * i = lang; |
528 | 2.14k | if (!(asc_islower(i[0]) && asc_islower(i[1]))) return false; |
529 | 0 | final_str.assign(i, 2); |
530 | 0 | i += 2; |
531 | 0 | if (! (i[0] == '_' || i[0] == '-')) return true; |
532 | 0 | i += 1; |
533 | 0 | if (!(asc_isupper(i[0]) && asc_isupper(i[1]))) return true; |
534 | 0 | final_str += '_'; |
535 | 0 | final_str.append(i, 2); |
536 | 0 | return true; |
537 | 0 | } |
538 | | |
539 | | static void get_lang_env(String & str) |
540 | 2.14k | { |
541 | 2.14k | if (proc_locale_str(getenv("LC_MESSAGES"), str)) return; |
542 | 2.14k | if (proc_locale_str(getenv("LANG"), str)) return; |
543 | 2.14k | if (proc_locale_str(getenv("LANGUAGE"), str)) return; |
544 | 2.14k | str = DEFAULT_LANG; |
545 | 2.14k | } |
546 | | |
547 | | #ifdef USE_LOCALE |
548 | | |
549 | | static void get_lang(String & final_str) |
550 | 2.14k | { |
551 | | // FIXME: THIS IS NOT THREAD SAFE |
552 | 2.14k | String locale = setlocale (LC_ALL, NULL); |
553 | 2.14k | if (locale == "C") |
554 | 2.14k | setlocale (LC_ALL, ""); |
555 | 2.14k | const char * lang = setlocale (LC_MESSAGES, NULL); |
556 | 2.14k | bool res = proc_locale_str(lang, final_str); |
557 | 2.14k | if (locale == "C") |
558 | 2.14k | setlocale(LC_MESSAGES, locale.c_str()); |
559 | 2.14k | if (!res) |
560 | 2.14k | get_lang_env(final_str); |
561 | 2.14k | } |
562 | | |
563 | | #else |
564 | | |
565 | | static inline void get_lang(String & str) |
566 | | { |
567 | | get_lang_env(str); |
568 | | } |
569 | | |
570 | | #endif |
571 | | |
572 | | #if defined USE_LOCALE && defined HAVE_LANGINFO_CODESET |
573 | | |
574 | | static inline void get_encoding(const Config & c, String & final_str) |
575 | 888 | { |
576 | 888 | const char * codeset = nl_langinfo(CODESET); |
577 | 888 | if (ascii_encoding(c, codeset)) codeset = "none"; |
578 | 888 | final_str = codeset; |
579 | 888 | } |
580 | | |
581 | | #else |
582 | | |
583 | | static inline void get_encoding(const Config &, String & final_str) |
584 | | { |
585 | | final_str = "none"; |
586 | | } |
587 | | |
588 | | #endif |
589 | | |
590 | | String Config::get_default(const KeyInfo * ki) const |
591 | 81.4k | { |
592 | 81.4k | bool in_replace = false; |
593 | 81.4k | String final_str; |
594 | 81.4k | String replace; |
595 | 81.4k | const char * i = ki->def; |
596 | 81.4k | if (*i == '!') { // special cases |
597 | 3.78k | ++i; |
598 | | |
599 | 3.78k | if (strcmp(i, "lang") == 0) { |
600 | | |
601 | 2.89k | const Entry * entry; |
602 | 2.89k | if (entry = lookup("actual-lang"), entry) { |
603 | 614 | return entry->value; |
604 | 2.27k | } else if (have("master")) { |
605 | 136 | final_str = "<unknown>"; |
606 | 2.14k | } else { |
607 | 2.14k | get_lang(final_str); |
608 | 2.14k | } |
609 | | |
610 | 2.89k | } else if (strcmp(i, "encoding") == 0) { |
611 | | |
612 | 888 | get_encoding(*this, final_str); |
613 | | |
614 | 888 | } else if (strcmp(i, "special") == 0) { |
615 | | |
616 | | // do nothing |
617 | |
|
618 | 0 | } else { |
619 | | |
620 | 0 | abort(); // this should not happen |
621 | | |
622 | 0 | } |
623 | | |
624 | 918k | } else for(; *i; ++i) { |
625 | | |
626 | 841k | if (!in_replace) { |
627 | | |
628 | 553k | if (*i == '<') { |
629 | 32.1k | in_replace = true; |
630 | 521k | } else { |
631 | 521k | final_str += *i; |
632 | 521k | } |
633 | | |
634 | 553k | } else { // in_replace |
635 | | |
636 | 287k | if (*i == '/' || *i == ':' || *i == '|' || *i == '#' || *i == '^') { |
637 | 11.7k | char sep = *i; |
638 | 11.7k | String second; |
639 | 11.7k | ++i; |
640 | 67.3k | while (*i != '\0' && *i != '>') second += *i++; |
641 | 11.7k | if (sep == '/') { |
642 | 6.77k | String s1 = retrieve(replace); |
643 | 6.77k | String s2 = retrieve(second); |
644 | 6.77k | final_str += add_possible_dir(s1, s2); |
645 | 6.77k | } else if (sep == ':') { |
646 | 1.24k | String s1 = retrieve(replace); |
647 | 1.24k | final_str += add_possible_dir(s1, second); |
648 | 3.75k | } else if (sep == '#') { |
649 | 0 | String s1 = retrieve(replace); |
650 | 0 | assert(second.size() == 1); |
651 | 0 | unsigned int s = 0; |
652 | 0 | while (s != s1.size() && s1[s] != second[0]) ++s; |
653 | 0 | final_str.append(s1, s); |
654 | 3.75k | } else if (sep == '^') { |
655 | 1.41k | String s1 = retrieve(replace); |
656 | 1.41k | String s2 = retrieve(second); |
657 | 1.41k | final_str += figure_out_dir(s1, s2); |
658 | 2.34k | } else { // sep == '|' |
659 | 2.34k | assert(replace[0] == '$'); |
660 | 0 | const char * env = getenv(replace.c_str()+1); |
661 | 2.34k | final_str += env ? env : second; |
662 | 2.34k | } |
663 | 0 | replace = ""; |
664 | 11.7k | in_replace = false; |
665 | | |
666 | 275k | } else if (*i == '>') { |
667 | | |
668 | 20.3k | final_str += retrieve(replace).data; |
669 | 20.3k | replace = ""; |
670 | 20.3k | in_replace = false; |
671 | | |
672 | 255k | } else { |
673 | | |
674 | 255k | replace += *i; |
675 | | |
676 | 255k | } |
677 | | |
678 | 287k | } |
679 | | |
680 | 841k | } |
681 | 80.8k | return final_str; |
682 | 81.4k | } |
683 | | |
684 | | PosibErr<String> Config::get_default(ParmStr key) const |
685 | 0 | { |
686 | 0 | RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki); |
687 | 0 | return get_default(ki); |
688 | 0 | } |
689 | | |
690 | | |
691 | | |
692 | | #define TEST(v,l,a) \ |
693 | 131k | do { \ |
694 | 131k | if (len == l && memcmp(s, v, l) == 0) { \ |
695 | 12.8k | if (action) *action = a; \ |
696 | 12.8k | return c + 1; \ |
697 | 12.8k | } \ |
698 | 131k | } while (false) |
699 | | |
700 | | const char * Config::base_name(const char * s, Action * action) |
701 | 33.2k | { |
702 | 33.2k | if (action) *action = Set; |
703 | 33.2k | const char * c = strchr(s, '-'); |
704 | 33.2k | if (!c) return s; |
705 | 19.1k | unsigned len = c - s; |
706 | 19.1k | TEST("reset", 5, Reset); |
707 | 16.8k | TEST("enable", 6, Enable); |
708 | 16.6k | TEST("dont", 4, Disable); |
709 | 16.1k | TEST("disable", 7, Disable); |
710 | 16.0k | TEST("lset", 4, ListSet); |
711 | 15.2k | TEST("rem", 3, ListRemove); |
712 | 13.0k | TEST("remove", 6, ListRemove); |
713 | 11.2k | TEST("add", 3, ListAdd); |
714 | 7.36k | TEST("clear", 5, ListClear); |
715 | 6.31k | return s; |
716 | 7.36k | } |
717 | | |
718 | | #undef TEST |
719 | | |
720 | | void separate_list(ParmStr value, AddableContainer & out, bool do_unescape) |
721 | 11.5k | { |
722 | 11.5k | unsigned len = value.size(); |
723 | | |
724 | 11.5k | VARARRAY(char, buf, len + 1); |
725 | 11.5k | memcpy(buf, value, len + 1); |
726 | | |
727 | 11.5k | len = strlen(buf); |
728 | 11.5k | char * s = buf; |
729 | 11.5k | char * end = buf + len; |
730 | | |
731 | 68.3k | while (s < end) |
732 | 56.8k | { |
733 | 56.8k | if (do_unescape) while (*s == ' ' || *s == '\t') ++s; |
734 | 56.8k | char * b = s; |
735 | 56.8k | char * e = s; |
736 | 1.31M | while (*s != '\0') { |
737 | 1.30M | if (do_unescape && *s == '\\') { |
738 | 2.01k | ++s; |
739 | 2.01k | if (*s == '\0') break; |
740 | 2.01k | e = s; |
741 | 2.01k | ++s; |
742 | 1.30M | } else { |
743 | 1.30M | if (*s == ':') break; |
744 | 1.25M | if (!do_unescape || (*s != ' ' && *s != '\t')) e = s; |
745 | 1.25M | ++s; |
746 | 1.25M | } |
747 | 1.30M | } |
748 | 56.8k | if (s != b) { |
749 | 54.9k | ++e; |
750 | 54.9k | *e = '\0'; |
751 | 54.9k | if (do_unescape) unescape(b); |
752 | | |
753 | 54.9k | out.add(b); |
754 | 54.9k | } |
755 | 56.8k | ++s; |
756 | 56.8k | } |
757 | 11.5k | } |
758 | | |
759 | | void combine_list(String & res, const StringList & in) |
760 | 1.72k | { |
761 | 1.72k | res.clear(); |
762 | 1.72k | StringListEnumeration els = in.elements_obj(); |
763 | 1.72k | const char * s = 0; |
764 | 4.51k | while ( (s = els.next()) != 0) |
765 | 2.78k | { |
766 | 340k | for (; *s; ++s) { |
767 | 337k | if (*s == ':') |
768 | 304 | res.append('\\'); |
769 | 337k | res.append(*s); |
770 | 337k | } |
771 | 2.78k | res.append(':'); |
772 | 2.78k | } |
773 | 1.72k | if (!res.empty() && res.back() == ':') res.pop_back(); |
774 | 1.72k | } |
775 | | |
776 | | struct ListAddHelper : public AddableContainer |
777 | | { |
778 | | Config * config; |
779 | | Config::Entry * orig_entry; |
780 | | PosibErr<bool> add(ParmStr val); |
781 | | }; |
782 | | |
783 | | PosibErr<bool> ListAddHelper::add(ParmStr val) |
784 | 29.4k | { |
785 | 29.4k | Config::Entry * entry = new Config::Entry(*orig_entry); |
786 | 29.4k | entry->value = val; |
787 | 29.4k | entry->action = Config::ListAdd; |
788 | 29.4k | config->set(entry); |
789 | 29.4k | return true; |
790 | 29.4k | } |
791 | | |
792 | | void Config::replace_internal(ParmStr key, ParmStr value) |
793 | 4.97k | { |
794 | 4.97k | Entry * entry = new Entry; |
795 | 4.97k | entry->key = key; |
796 | 4.97k | entry->value = value; |
797 | 4.97k | entry->action = Set; |
798 | 4.97k | entry->next = *insert_point_; |
799 | 4.97k | *insert_point_ = entry; |
800 | 4.97k | insert_point_ = &entry->next; |
801 | 4.97k | } |
802 | | |
803 | | PosibErr<void> Config::replace(ParmStr key, ParmStr value) |
804 | 29.5k | { |
805 | 29.5k | Entry * entry = new Entry; |
806 | 29.5k | entry->key = key; |
807 | 29.5k | entry->value = value; |
808 | 29.5k | entry->secure = true; |
809 | 29.5k | return set(entry); |
810 | 29.5k | } |
811 | | |
812 | | PosibErr<void> Config::remove(ParmStr key) |
813 | 0 | { |
814 | 0 | Entry * entry = new Entry; |
815 | 0 | entry->key = key; |
816 | 0 | entry->action = Reset; |
817 | 0 | return set(entry); |
818 | 0 | } |
819 | | |
820 | | PosibErr<void> Config::set(Entry * entry0, bool do_unescape) |
821 | 63.4k | { |
822 | 63.4k | StackPtr<Entry> entry(entry0); |
823 | | |
824 | 63.4k | if (entry->action == NoOp) |
825 | 33.2k | entry->key = base_name(entry->key.str(), &entry->action); |
826 | | |
827 | 63.4k | if (num_parms(entry->action) == 0 && !entry->value.empty()) |
828 | 503 | { |
829 | 503 | if (entry->place_holder == -1) { |
830 | 503 | switch (entry->action) { |
831 | 61 | case Reset: |
832 | 61 | return make_err(no_value_reset, entry->key); |
833 | 128 | case Enable: |
834 | 128 | return make_err(no_value_enable, entry->key); |
835 | 150 | case Disable: |
836 | 150 | return make_err(no_value_disable, entry->key); |
837 | 164 | case ListClear: |
838 | 164 | return make_err(no_value_clear, entry->key); |
839 | 0 | default: |
840 | 0 | abort(); // this shouldn't happen |
841 | 503 | } |
842 | 503 | } else { |
843 | 0 | entry->place_holder = -1; |
844 | 0 | } |
845 | 503 | } |
846 | | |
847 | 62.9k | if (entry->action != ListSet) { |
848 | | |
849 | 62.1k | switch (entry->action) { |
850 | 128 | case Enable: |
851 | 128 | entry->value = "true"; |
852 | 128 | entry->action = Set; |
853 | 128 | break; |
854 | 401 | case Disable: |
855 | 401 | entry->value = "false"; |
856 | 401 | entry->action = Set; |
857 | 401 | break; |
858 | 61.6k | default: |
859 | 61.6k | ; |
860 | 62.1k | } |
861 | 62.1k | if (do_unescape) unescape(entry->value.mstr()); |
862 | | |
863 | 62.1k | entry->next = *insert_point_; |
864 | 62.1k | *insert_point_ = entry; |
865 | 62.1k | insert_point_ = &entry->next; |
866 | 62.1k | entry.release(); |
867 | 62.1k | if (committed_) RET_ON_ERR(commit(entry0)); // entry0 == entry |
868 | | |
869 | 62.1k | } else { // action == ListSet |
870 | | |
871 | 770 | Entry * ent = new Entry; |
872 | 770 | ent->key = entry->key; |
873 | 770 | ent->action = ListClear; |
874 | 770 | RET_ON_ERR(set(ent)); |
875 | | |
876 | 486 | ListAddHelper helper; |
877 | 486 | helper.config = this; |
878 | 486 | helper.orig_entry = entry; |
879 | | |
880 | 486 | separate_list(entry->value.str(), helper, do_unescape); |
881 | 486 | } |
882 | 53.8k | return no_err; |
883 | 62.9k | } |
884 | | |
885 | | PosibErr<void> Config::merge(const Config & other) |
886 | 790 | { |
887 | 50.0k | for (const Entry * src = other.first_; src; src = src->next) |
888 | 49.3k | { |
889 | 49.3k | if (src->action == NoOp) continue; |
890 | 41.9k | Entry * entry = new Entry(*src); |
891 | 41.9k | entry->next = *insert_point_; |
892 | 41.9k | *insert_point_ = entry; |
893 | 41.9k | insert_point_ = &entry->next; |
894 | 41.9k | if (committed_) RET_ON_ERR(commit(entry)); |
895 | 41.9k | } |
896 | 790 | return no_err; |
897 | 790 | } |
898 | | |
899 | | PosibErr<void> Config::lang_config_merge(const Config & other, |
900 | | int which, ParmStr data_encoding) |
901 | 4.97k | { |
902 | 4.97k | Conv to_utf8; |
903 | 4.97k | RET_ON_ERR(to_utf8.setup(*this, data_encoding, "utf-8", NormTo)); |
904 | 4.97k | const Entry * src = other.first_; |
905 | 4.97k | Entry * * ip = &first_; |
906 | 30.1k | while (src) |
907 | 25.1k | { |
908 | 25.1k | const KeyInfo * l_ki = other.keyinfo(src->key); |
909 | 25.1k | if (l_ki->other_data == which) { |
910 | 194 | const KeyInfo * c_ki = keyinfo(src->key); |
911 | 194 | Entry * entry = new Entry(*src); |
912 | 194 | if (c_ki->flags & KEYINFO_UTF8) |
913 | 0 | entry->value = to_utf8(entry->value); |
914 | 194 | entry->next = *ip; |
915 | 194 | *ip = entry; |
916 | 194 | ip = &entry->next; |
917 | 194 | } |
918 | 25.1k | src = src->next; |
919 | 25.1k | } |
920 | 4.97k | return no_err; |
921 | 4.97k | } |
922 | | |
923 | | |
924 | | #define NOTIFY_ALL(fun) \ |
925 | 96.2k | do { \ |
926 | 96.2k | Vector<Notifier *>::iterator i = notifier_list.begin(); \ |
927 | 96.2k | Vector<Notifier *>::iterator end = notifier_list.end(); \ |
928 | 186k | while (i != end) { \ |
929 | 92.5k | RET_ON_ERR((*i)->fun); \ |
930 | 92.5k | ++i; \ |
931 | 90.6k | } \ |
932 | 96.2k | } while (false) |
933 | | |
934 | | PosibErr<int> Config::commit(Entry * entry, Conv * conv) |
935 | 104k | { |
936 | 104k | PosibErr<const KeyInfo *> pe = keyinfo(entry->key); |
937 | 104k | { |
938 | 104k | if (pe.has_err()) goto error; |
939 | | |
940 | 96.8k | const KeyInfo * ki = pe; |
941 | | |
942 | 96.8k | entry->key = ki->name; |
943 | | |
944 | | // FIXME: This is the correct thing to do but it causes problems |
945 | | // with changing a filter mode in "pipe" mode and probably |
946 | | // elsewhere. |
947 | | //if (attached_ && !(ki->flags & KEYINFO_MAY_CHANGE)) { |
948 | | // pe = make_err(cant_change_value, entry->key); |
949 | | // goto error; |
950 | | //} |
951 | | |
952 | 96.8k | int place_holder = entry->place_holder; |
953 | | |
954 | 96.8k | if (conv && ki->flags & KEYINFO_UTF8) |
955 | 0 | entry->value = (*conv)(entry->value); |
956 | | |
957 | 96.8k | if (ki->type != KeyInfoList && list_action(entry->action)) { |
958 | 272 | pe = make_err(key_not_list, entry->key); |
959 | 272 | goto error; |
960 | 272 | } |
961 | | |
962 | 96.5k | if (!ki->def) // if null this key should never have values |
963 | | // directly added to it |
964 | 0 | return make_err(aerror_cant_change_value, entry->key); |
965 | | |
966 | 96.5k | String value(entry->action == Reset ? get_default(ki) : entry->value); |
967 | | |
968 | 96.5k | switch (ki->type) { |
969 | | |
970 | 1.18k | case KeyInfoBool: { |
971 | | |
972 | 1.18k | bool val; |
973 | | |
974 | 1.18k | if (value.empty() || entry->place_holder != -1) { |
975 | | // if entry->place_holder != -1 than IGNORE the value no |
976 | | // matter what it is |
977 | 183 | entry->value = "true"; |
978 | 183 | val = true; |
979 | 183 | place_holder = -1; |
980 | 997 | } else if (value == "true") { |
981 | 208 | val = true; |
982 | 789 | } else if (value == "false") { |
983 | 788 | val = false; |
984 | 788 | } else { |
985 | 1 | pe = make_err(bad_value, entry->key, value, |
986 | | /* TRANSLATORS: "true" and "false" are literal |
987 | | * values and should not be translated.*/ |
988 | 1 | _("either \"true\" or \"false\"")); |
989 | 1 | goto error; |
990 | 1 | } |
991 | | |
992 | 1.17k | NOTIFY_ALL(item_updated(ki, val)); |
993 | 1.17k | break; |
994 | | |
995 | 18.9k | } case KeyInfoString: |
996 | | |
997 | 18.9k | NOTIFY_ALL(item_updated(ki, value)); |
998 | 17.0k | break; |
999 | | |
1000 | 17.0k | case KeyInfoInt: |
1001 | 670 | { |
1002 | 670 | int num; |
1003 | | |
1004 | 670 | if (sscanf(value.str(), "%i", &num) == 1 && num >= 0) { |
1005 | 318 | NOTIFY_ALL(item_updated(ki, num)); |
1006 | 352 | } else { |
1007 | 352 | pe = make_err(bad_value, entry->key, value, _("a positive integer")); |
1008 | 352 | goto error; |
1009 | 352 | } |
1010 | | |
1011 | 318 | break; |
1012 | 670 | } |
1013 | 75.7k | case KeyInfoList: |
1014 | | |
1015 | 75.7k | NOTIFY_ALL(list_updated(ki)); |
1016 | 75.7k | break; |
1017 | | |
1018 | 96.5k | } |
1019 | 94.2k | return place_holder; |
1020 | 96.5k | } |
1021 | 7.80k | error: |
1022 | 7.80k | entry->action = NoOp; |
1023 | 7.80k | if (!entry->file.empty()) |
1024 | 0 | return pe.with_file(entry->file, entry->line_num); |
1025 | 7.80k | else |
1026 | 7.80k | return (PosibErrBase &)pe; |
1027 | 7.80k | } |
1028 | | |
1029 | | #undef NOTIFY_ALL |
1030 | | |
1031 | | |
1032 | | ///////////////////////////////////////////////////////////////////// |
1033 | | ///////////////////////////////////////////////////////////////////// |
1034 | | |
1035 | | class PossibleElementsEmul : public KeyInfoEnumeration |
1036 | | { |
1037 | | private: |
1038 | | bool include_extra; |
1039 | | bool include_modules; |
1040 | | bool module_changed; |
1041 | | const Config * cd; |
1042 | | const KeyInfo * i; |
1043 | | const ConfigModule * m; |
1044 | | public: |
1045 | | PossibleElementsEmul(const Config * d, bool ic, bool im) |
1046 | | : include_extra(ic), include_modules(im), |
1047 | 0 | module_changed(false), cd(d), i(d->keyinfo_begin), m(0) {} |
1048 | | |
1049 | 0 | KeyInfoEnumeration * clone() const { |
1050 | 0 | return new PossibleElementsEmul(*this); |
1051 | 0 | } |
1052 | | |
1053 | 0 | void assign(const KeyInfoEnumeration * other) { |
1054 | 0 | *this = *(const PossibleElementsEmul *)(other); |
1055 | 0 | } |
1056 | | |
1057 | 0 | virtual bool active_filter_module_changed(void) { |
1058 | 0 | return module_changed; |
1059 | 0 | } |
1060 | | |
1061 | 0 | const char * active_filter_module_name(void){ |
1062 | 0 | if (m != 0) |
1063 | 0 | return m->name; |
1064 | 0 | return ""; |
1065 | 0 | } |
1066 | | |
1067 | 0 | virtual const char * active_filter_module_desc(void) { |
1068 | 0 | if (m != 0) |
1069 | 0 | return m->desc; |
1070 | 0 | return ""; |
1071 | 0 | } |
1072 | | |
1073 | 0 | const KeyInfo * next() { |
1074 | 0 | if (i == cd->keyinfo_end) { |
1075 | 0 | if (include_extra) |
1076 | 0 | i = cd->extra_begin; |
1077 | 0 | else |
1078 | 0 | i = cd->extra_end; |
1079 | 0 | } |
1080 | | |
1081 | 0 | module_changed = false; |
1082 | 0 | if (i == cd->extra_end) { |
1083 | 0 | m = cd->filter_modules.pbegin(); |
1084 | 0 | if (!include_modules || m == cd->filter_modules.pend()) return 0; |
1085 | 0 | else { |
1086 | 0 | i = m->begin; |
1087 | 0 | module_changed = true; |
1088 | 0 | } |
1089 | 0 | } |
1090 | | |
1091 | 0 | if (m == 0){ |
1092 | 0 | return i++; |
1093 | 0 | } |
1094 | | |
1095 | 0 | if (m == cd->filter_modules.pend()){ |
1096 | 0 | return 0; |
1097 | 0 | } |
1098 | | |
1099 | 0 | while (i == m->end) { |
1100 | 0 | ++m; |
1101 | 0 | if (m == cd->filter_modules.pend()) return 0; |
1102 | 0 | else { |
1103 | 0 | i = m->begin; |
1104 | 0 | module_changed = true; |
1105 | 0 | } |
1106 | 0 | } |
1107 | | |
1108 | 0 | return i++; |
1109 | 0 | } |
1110 | | |
1111 | 0 | bool at_end() const { |
1112 | 0 | return (m == cd->filter_modules.pend()); |
1113 | 0 | } |
1114 | | }; |
1115 | | |
1116 | | KeyInfoEnumeration * |
1117 | | Config::possible_elements(bool include_extra, bool include_modules) const |
1118 | 0 | { |
1119 | 0 | return new PossibleElementsEmul(this, include_extra, include_modules); |
1120 | 0 | } |
1121 | | |
1122 | | struct ListDefaultDump : public AddableContainer |
1123 | | { |
1124 | | OStream & out; |
1125 | | bool first; |
1126 | | const char * first_prefix; |
1127 | | unsigned num_blanks; |
1128 | | ListDefaultDump(OStream & o); |
1129 | | PosibErr<bool> add(ParmStr d); |
1130 | | }; |
1131 | | |
1132 | | ListDefaultDump::ListDefaultDump(OStream & o) |
1133 | | : out(o), first(false) |
1134 | 0 | { |
1135 | 0 | first_prefix = _("# default: "); |
1136 | 0 | num_blanks = strlen(first_prefix) - 1; |
1137 | 0 | } |
1138 | | |
1139 | | PosibErr<bool> ListDefaultDump::add(ParmStr d) |
1140 | 0 | { |
1141 | 0 | if (first) { |
1142 | 0 | out.write(first_prefix); |
1143 | 0 | } else { |
1144 | 0 | out.put('#'); |
1145 | 0 | for (unsigned i = 0; i != num_blanks; ++i) |
1146 | 0 | out.put(' '); |
1147 | 0 | } |
1148 | 0 | VARARRAY(char, buf, d.size() * 2 + 1); |
1149 | 0 | escape(buf, d); |
1150 | 0 | out.printl(buf); |
1151 | 0 | first = false; |
1152 | 0 | return true; |
1153 | 0 | } |
1154 | | |
1155 | | class ListDump : public MutableContainer |
1156 | | { |
1157 | | OStream & out; |
1158 | | const char * name; |
1159 | | public: |
1160 | | ListDump(OStream & o, ParmStr n) |
1161 | 0 | : out(o), name(n) {} |
1162 | | PosibErr<bool> add(ParmStr d); |
1163 | | PosibErr<bool> remove(ParmStr d); |
1164 | | PosibErr<void> clear(); |
1165 | | }; |
1166 | | |
1167 | 0 | PosibErr<bool> ListDump::add(ParmStr d) { |
1168 | 0 | VARARRAY(char, buf, d.size() * 2 + 1); |
1169 | 0 | escape(buf, d); |
1170 | 0 | out.printf("add-%s %s\n", name, buf); |
1171 | 0 | return true; |
1172 | 0 | } |
1173 | 0 | PosibErr<bool> ListDump::remove(ParmStr d) { |
1174 | 0 | VARARRAY(char, buf, d.size() * 2 + 1); |
1175 | 0 | escape(buf, d); |
1176 | 0 | out.printf("remove-%s %s\n", name, buf); |
1177 | 0 | return true; |
1178 | 0 | } |
1179 | 0 | PosibErr<void> ListDump::clear() { |
1180 | 0 | out.printf("clear-%s\n", name); |
1181 | 0 | return no_err; |
1182 | 0 | } |
1183 | | |
1184 | | void Config::write_to_stream(OStream & out, |
1185 | | bool include_extra) |
1186 | 0 | { |
1187 | 0 | KeyInfoEnumeration * els = possible_elements(include_extra); |
1188 | 0 | const KeyInfo * i; |
1189 | 0 | String buf; |
1190 | 0 | String obuf; |
1191 | 0 | String def; |
1192 | 0 | bool have_value; |
1193 | |
|
1194 | 0 | while ((i = els->next()) != 0) { |
1195 | 0 | if (i->desc == 0) continue; |
1196 | | |
1197 | 0 | if (els->active_filter_module_changed()) { |
1198 | 0 | out.printf(_("\n" |
1199 | 0 | "#######################################################################\n" |
1200 | 0 | "#\n" |
1201 | 0 | "# Filter: %s\n" |
1202 | 0 | "# %s\n" |
1203 | 0 | "#\n" |
1204 | 0 | "# configured as follows:\n" |
1205 | 0 | "\n"), |
1206 | 0 | els->active_filter_module_name(), |
1207 | 0 | _(els->active_filter_module_desc())); |
1208 | 0 | } |
1209 | |
|
1210 | 0 | obuf.clear(); |
1211 | 0 | have_value = false; |
1212 | |
|
1213 | 0 | obuf.printf("# %s (%s)\n# %s\n", |
1214 | 0 | i->name, _(keyinfo_type_name[i->type]), _(i->desc)); |
1215 | 0 | if (i->def != 0) { |
1216 | 0 | if (i->type != KeyInfoList) { |
1217 | 0 | buf.resize(strlen(i->def) * 2 + 1); |
1218 | 0 | escape(buf.data(), i->def); |
1219 | 0 | obuf.printf("# default: %s", buf.data()); |
1220 | 0 | def = get_default(i); |
1221 | 0 | if (def != i->def) { |
1222 | 0 | buf.resize(def.size() * 2 + 1); |
1223 | 0 | escape(buf.data(), def.str()); |
1224 | 0 | obuf.printf(" = %s", buf.data()); |
1225 | 0 | } |
1226 | 0 | obuf << '\n'; |
1227 | 0 | const Entry * entry = lookup(i->name); |
1228 | 0 | if (entry) { |
1229 | 0 | have_value = true; |
1230 | 0 | buf.resize(entry->value.size() * 2 + 1); |
1231 | 0 | escape(buf.data(), entry->value.str()); |
1232 | 0 | obuf.printf("%s %s\n", i->name, buf.data()); |
1233 | 0 | } |
1234 | 0 | } else { |
1235 | 0 | unsigned s = obuf.size(); |
1236 | 0 | ListDump ld(obuf, i->name); |
1237 | 0 | lookup_list(i, ld, false); |
1238 | 0 | have_value = s != obuf.size(); |
1239 | 0 | } |
1240 | 0 | } |
1241 | 0 | obuf << '\n'; |
1242 | 0 | if (!(i->flags & KEYINFO_HIDDEN) || have_value) |
1243 | 0 | out.write(obuf); |
1244 | 0 | } |
1245 | 0 | delete els; |
1246 | 0 | } |
1247 | | |
1248 | | PosibErr<void> Config::read_in(IStream & in, ParmStr id) |
1249 | 720 | { |
1250 | 720 | String buf; |
1251 | 720 | DataPair dp; |
1252 | 4.37k | while (getdata_pair(in, dp, buf)) { |
1253 | 3.65k | to_lower(dp.key); |
1254 | 3.65k | Entry * entry = new Entry; |
1255 | 3.65k | entry->key = dp.key; |
1256 | 3.65k | entry->value = dp.value; |
1257 | 3.65k | entry->file = id; |
1258 | 3.65k | entry->line_num = dp.line_num; |
1259 | 3.65k | RET_ON_ERR(set(entry, true)); |
1260 | 3.65k | } |
1261 | 720 | return no_err; |
1262 | 720 | } |
1263 | | |
1264 | 2.29k | PosibErr<void> Config::read_in_file(ParmStr file) { |
1265 | 2.29k | FStream in; |
1266 | 2.29k | RET_ON_ERR(in.open(file, "r")); |
1267 | 720 | return read_in(in, file); |
1268 | 2.29k | } |
1269 | | |
1270 | 0 | PosibErr<void> Config::read_in_string(ParmStr str, const char * what) { |
1271 | 0 | StringIStream in(str); |
1272 | 0 | return read_in(in, what); |
1273 | 0 | } |
1274 | | |
1275 | | |
1276 | | PosibErr<bool> Config::read_in_settings(const Config * other) |
1277 | 790 | { |
1278 | 790 | if (settings_read_in_) return false; |
1279 | | |
1280 | 790 | bool was_committed = committed_; |
1281 | 790 | set_committed_state(false); |
1282 | | |
1283 | 790 | if (other && other->settings_read_in_) { |
1284 | |
|
1285 | 0 | assert(empty()); |
1286 | 0 | del(); // to clean up any notifiers and similar stuff |
1287 | 0 | copy(*other); |
1288 | |
|
1289 | 790 | } else { |
1290 | | |
1291 | 790 | if (other) merge(*other); |
1292 | | |
1293 | 790 | const char * env = getenv("ASPELL_CONF"); |
1294 | 790 | if (env != 0) { |
1295 | 0 | insert_point_ = &first_; |
1296 | 0 | RET_ON_ERR(read_in_string(env, _("ASPELL_CONF env var"))); |
1297 | 0 | } |
1298 | | |
1299 | 790 | { |
1300 | 790 | insert_point_ = &first_; |
1301 | 790 | PosibErrBase pe = read_in_file(retrieve("per-conf-path")); |
1302 | 790 | if (pe.has_err() && !pe.has_err(cant_read_file)) return pe; |
1303 | 790 | } |
1304 | | |
1305 | 790 | { |
1306 | 790 | insert_point_ = &first_; |
1307 | 790 | PosibErrBase pe = read_in_file(retrieve("conf-path")); |
1308 | 790 | if (pe.has_err() && !pe.has_err(cant_read_file)) return pe; |
1309 | 790 | } |
1310 | | |
1311 | 790 | if (was_committed) |
1312 | 790 | RET_ON_ERR(commit_all()); |
1313 | | |
1314 | 760 | settings_read_in_ = true; |
1315 | 760 | } |
1316 | | |
1317 | 760 | return true; |
1318 | 790 | } |
1319 | | |
1320 | | PosibErr<void> Config::commit_all(Vector<int> * phs, const char * codeset) |
1321 | 790 | { |
1322 | 790 | committed_ = true; |
1323 | 790 | Entry * uncommitted = first_; |
1324 | 790 | first_ = 0; |
1325 | 790 | insert_point_ = &first_; |
1326 | 790 | Conv to_utf8; |
1327 | 790 | if (codeset) |
1328 | 0 | RET_ON_ERR(to_utf8.setup(*this, codeset, "utf-8", NormTo)); |
1329 | 790 | PosibErr<void> ret; |
1330 | 42.6k | while (uncommitted) { |
1331 | 41.9k | Entry * cur = uncommitted; |
1332 | 41.9k | uncommitted = cur->next; |
1333 | 41.9k | cur->next = 0; |
1334 | 41.9k | *insert_point_ = cur; |
1335 | 41.9k | insert_point_ = &((*insert_point_)->next); |
1336 | 41.9k | PosibErr<int> pe = commit(cur, codeset ? &to_utf8 : 0); |
1337 | 41.9k | if (pe.has_err()) { |
1338 | 987 | if (ret.has_err()) |
1339 | 957 | pe.ignore_err(); |
1340 | 30 | else |
1341 | 30 | ret = pe; |
1342 | 987 | continue; |
1343 | 987 | } |
1344 | 40.9k | int place_holder = pe.data; |
1345 | 40.9k | if (phs && place_holder != -1 && (phs->empty() || phs->back() != place_holder)) |
1346 | 0 | phs->push_back(place_holder); |
1347 | 40.9k | } |
1348 | 790 | return ret; |
1349 | 790 | } |
1350 | | |
1351 | 790 | PosibErr<void> Config::set_committed_state(bool val) { |
1352 | 790 | if (val && !committed_) { |
1353 | 0 | RET_ON_ERR(commit_all()); |
1354 | 790 | } else if (!val && committed_) { |
1355 | 790 | assert(empty()); |
1356 | 0 | committed_ = false; |
1357 | 790 | } |
1358 | 790 | return no_err; |
1359 | 790 | } |
1360 | | |
1361 | | |
1362 | | #ifdef ENABLE_WIN32_RELOCATABLE |
1363 | | # define HOME_DIR "<prefix>" |
1364 | | # define PERSONAL "<lang>.pws" |
1365 | | # define REPL "<lang>.prepl" |
1366 | | #else |
1367 | | # define HOME_DIR "<$HOME|./>" |
1368 | | # define PERSONAL ".aspell.<lang>.pws" |
1369 | | # define REPL ".aspell.<lang>.prepl" |
1370 | | #endif |
1371 | | |
1372 | | static const KeyInfo config_keys[] = { |
1373 | | // the description should be under 50 chars |
1374 | | {"actual-dict-dir", KeyInfoString, "<dict-dir^master>", 0} |
1375 | | , {"actual-lang", KeyInfoString, "", 0} |
1376 | | , {"conf", KeyInfoString, "aspell.conf", |
1377 | | /* TRANSLATORS: The remaining strings in config.cpp should be kept |
1378 | | under 50 characters, begin with a lower case character and not |
1379 | | include any trailing punctuation marks. */ |
1380 | | N_("main configuration file")} |
1381 | | , {"conf-dir", KeyInfoString, CONF_DIR, |
1382 | | N_("location of main configuration file")} |
1383 | | , {"conf-path", KeyInfoString, "<conf-dir/conf>", 0} |
1384 | | , {"data-dir", KeyInfoString, DATA_DIR, |
1385 | | N_("location of language data files")} |
1386 | | , {"dict-alias", KeyInfoList, "", |
1387 | | N_("create dictionary aliases")} |
1388 | | , {"dict-dir", KeyInfoString, DICT_DIR, |
1389 | | N_("location of the main word list")} |
1390 | | , {"encoding", KeyInfoString, "!encoding", |
1391 | | N_("encoding to expect data to be in"), KEYINFO_COMMON} |
1392 | | , {"filter", KeyInfoList , "url", |
1393 | | N_("add or removes a filter"), KEYINFO_MAY_CHANGE} |
1394 | | , {"filter-path", KeyInfoList, DICT_DIR, |
1395 | | N_("path(s) aspell looks for filters")} |
1396 | | //, {"option-path", KeyInfoList, DATA_DIR, |
1397 | | // N_("path(s) aspell looks for options descriptions")} |
1398 | | , {"mode", KeyInfoString, "url", |
1399 | | N_("filter mode"), KEYINFO_COMMON} |
1400 | | , {"extra-dicts", KeyInfoList, "", |
1401 | | N_("extra dictionaries to use")} |
1402 | | , {"wordlists", KeyInfoList, "", |
1403 | | N_("files with list of extra words to accept")} |
1404 | | , {"home-dir", KeyInfoString, HOME_DIR, |
1405 | | N_("location for personal files")} |
1406 | | , {"ignore", KeyInfoInt , "1", |
1407 | | N_("ignore words <= n chars"), KEYINFO_MAY_CHANGE} |
1408 | | , {"ignore-accents" , KeyInfoBool, "false", |
1409 | | /* TRANSLATORS: It is OK if this is longer than 50 chars */ |
1410 | | N_("ignore accents when checking words -- CURRENTLY IGNORED"), KEYINFO_MAY_CHANGE | KEYINFO_HIDDEN} |
1411 | | , {"ignore-case", KeyInfoBool , "false", |
1412 | | N_("ignore case when checking words"), KEYINFO_MAY_CHANGE} |
1413 | | , {"ignore-repl", KeyInfoBool , "false", |
1414 | | N_("ignore commands to store replacement pairs"), KEYINFO_MAY_CHANGE} |
1415 | | , {"jargon", KeyInfoString, "", |
1416 | | N_("extra information for the word list"), KEYINFO_HIDDEN} |
1417 | | , {"keyboard", KeyInfoString, "standard", |
1418 | | N_("keyboard definition to use for typo analysis")} |
1419 | | , {"lang", KeyInfoString, "<language-tag>", |
1420 | | N_("language code"), KEYINFO_COMMON} |
1421 | | , {"language-tag", KeyInfoString, "!lang", |
1422 | | N_("deprecated, use lang instead"), KEYINFO_HIDDEN} |
1423 | | , {"local-data-dir", KeyInfoString, "<actual-dict-dir>", |
1424 | | N_("location of local language data files") } |
1425 | | , {"master", KeyInfoString, "<lang>", |
1426 | | N_("base name of the main dictionary to use"), KEYINFO_COMMON} |
1427 | | , {"master-flags", KeyInfoString, "", 0} |
1428 | | , {"master-path", KeyInfoString, "<dict-dir/master>", 0} |
1429 | | , {"module", KeyInfoString, "default", |
1430 | | N_("set module name"), KEYINFO_HIDDEN} |
1431 | | , {"module-search-order", KeyInfoList, "", |
1432 | | N_("search order for modules"), KEYINFO_HIDDEN} |
1433 | | , {"normalize", KeyInfoBool, "true", |
1434 | | N_("enable Unicode normalization")} |
1435 | | , {"norm-required", KeyInfoBool, "false", |
1436 | | N_("Unicode normalization required for current lang")} |
1437 | | , {"norm-form", KeyInfoString, "nfc", |
1438 | | /* TRANSLATORS: the values after the ':' are literal |
1439 | | values and should not be translated. */ |
1440 | | N_("Unicode normalization form: none, nfd, nfc, comp")} |
1441 | | , {"norm-strict", KeyInfoBool, "false", |
1442 | | N_("avoid lossy conversions when normalization")} |
1443 | | , {"per-conf", KeyInfoString, ".aspell.conf", |
1444 | | N_("personal configuration file")} |
1445 | | , {"per-conf-path", KeyInfoString, "<home-dir/per-conf>", 0} |
1446 | | , {"personal", KeyInfoString, PERSONAL, |
1447 | | N_("personal dictionary file name")} |
1448 | | , {"personal-path", KeyInfoString, "<home-dir/personal>", 0} |
1449 | | , {"prefix", KeyInfoString, PREFIX, |
1450 | | N_("prefix directory")} |
1451 | | , {"repl", KeyInfoString, REPL, |
1452 | | N_("replacements list file name") } |
1453 | | , {"repl-path", KeyInfoString, "<home-dir/repl>", 0} |
1454 | | , {"run-together", KeyInfoBool, "false", |
1455 | | N_("consider run-together words legal"), KEYINFO_MAY_CHANGE} |
1456 | | , {"run-together-limit", KeyInfoInt, "2", |
1457 | | N_("maximum number that can be strung together"), KEYINFO_MAY_CHANGE} |
1458 | | , {"run-together-min", KeyInfoInt, "3", |
1459 | | N_("minimal length of interior words"), KEYINFO_MAY_CHANGE} |
1460 | | , {"camel-case", KeyInfoBool, "false", |
1461 | | N_("consider camel case words legal"), KEYINFO_MAY_CHANGE} |
1462 | | , {"save-repl", KeyInfoBool , "true", |
1463 | | N_("save replacement pairs on save all")} |
1464 | | , {"set-prefix", KeyInfoBool, "true", |
1465 | | N_("set the prefix based on executable location")} |
1466 | | , {"size", KeyInfoString, "+60", |
1467 | | N_("size of the word list")} |
1468 | | , {"spelling", KeyInfoString, "", |
1469 | | N_("no longer used"), KEYINFO_HIDDEN} |
1470 | | , {"sug-mode", KeyInfoString, "normal", |
1471 | | N_("suggestion mode"), KEYINFO_MAY_CHANGE | KEYINFO_COMMON} |
1472 | | , {"sug-typo-analysis", KeyInfoBool, "true", |
1473 | | /* TRANSLATORS: "sug-mode" is a literal value and should not be |
1474 | | translated. */ |
1475 | | N_("use typo analysis, override sug-mode default")} |
1476 | | , {"sug-repl-table", KeyInfoBool, "true", |
1477 | | N_("use replacement tables, override sug-mode default")} |
1478 | | , {"sug-split-char", KeyInfoList, "\\ :-", |
1479 | | N_("characters to insert when a word is split"), KEYINFO_UTF8} |
1480 | | , {"use-other-dicts", KeyInfoBool, "true", |
1481 | | N_("use personal, replacement & session dictionaries")} |
1482 | | , {"variety", KeyInfoList, "", |
1483 | | N_("extra information for the word list")} |
1484 | | , {"word-list-path", KeyInfoList, DATA_DIR, |
1485 | | N_("search path for word list information files"), KEYINFO_HIDDEN} |
1486 | | , {"warn", KeyInfoBool, "true", |
1487 | | N_("enable warnings")} |
1488 | | |
1489 | | |
1490 | | // |
1491 | | // These options are generally used when creating dictionaries |
1492 | | // and may also be specified in the language data file |
1493 | | // |
1494 | | |
1495 | | , {"affix-char", KeyInfoString, "/", // FIXME: Implement |
1496 | | /* TRANSLATORS: It is OK if this is longer than 50 chars */ |
1497 | | N_("indicator for affix flags in word lists -- CURRENTLY IGNORED"), KEYINFO_UTF8 | KEYINFO_HIDDEN} |
1498 | | , {"affix-compress", KeyInfoBool, "false", |
1499 | | N_("use affix compression when creating dictionaries")} |
1500 | | , {"clean-affixes", KeyInfoBool, "true", |
1501 | | N_("remove invalid affix flags")} |
1502 | | , {"clean-words", KeyInfoBool, "false", |
1503 | | N_("attempts to clean words so that they are valid")} |
1504 | | , {"invisible-soundslike", KeyInfoBool, "false", |
1505 | | N_("compute soundslike on demand rather than storing")} |
1506 | | , {"partially-expand", KeyInfoBool, "false", |
1507 | | N_("partially expand affixes for better suggestions")} |
1508 | | , {"skip-invalid-words", KeyInfoBool, "true", |
1509 | | N_("skip invalid words")} |
1510 | | , {"validate-affixes", KeyInfoBool, "true", |
1511 | | N_("check if affix flags are valid")} |
1512 | | , {"validate-words", KeyInfoBool, "true", |
1513 | | N_("check if words are valid")} |
1514 | | |
1515 | | // |
1516 | | // These options are specific to the "aspell" utility. They are |
1517 | | // here so that they can be specified in configuration files. |
1518 | | // |
1519 | | , {"backup", KeyInfoBool, "true", |
1520 | | N_("create a backup file by appending \".bak\"")} |
1521 | | , {"byte-offsets", KeyInfoBool, "false", |
1522 | | N_("use byte offsets instead of character offsets")} |
1523 | | , {"guess", KeyInfoBool, "false", |
1524 | | N_("create missing root/affix combinations"), KEYINFO_MAY_CHANGE} |
1525 | | , {"keymapping", KeyInfoString, "aspell", |
1526 | | N_("keymapping for check mode: \"aspell\" or \"ispell\"")} |
1527 | | , {"reverse", KeyInfoBool, "false", |
1528 | | N_("reverse the order of the suggest list")} |
1529 | | , {"suggest", KeyInfoBool, "true", |
1530 | | N_("suggest possible replacements"), KEYINFO_MAY_CHANGE} |
1531 | | , {"time" , KeyInfoBool, "false", |
1532 | | N_("time load time and suggest time in pipe mode"), KEYINFO_MAY_CHANGE} |
1533 | | }; |
1534 | | |
1535 | | const KeyInfo * config_impl_keys_begin = config_keys; |
1536 | | const KeyInfo * config_impl_keys_end |
1537 | | = config_keys + sizeof(config_keys)/sizeof(KeyInfo); |
1538 | | |
1539 | 1.58k | Config * new_basic_config() { |
1540 | 1.58k | aspell_gettext_init(); |
1541 | 1.58k | return new Config("aspell", |
1542 | 1.58k | config_impl_keys_begin, |
1543 | 1.58k | config_impl_keys_end); |
1544 | 1.58k | } |
1545 | | |
1546 | | } |
1547 | | |