Coverage Report

Created: 2023-12-08 06:59

/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