Coverage Report

Created: 2025-10-10 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/aspell/common/config.cpp
Line
Count
Source
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
3.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
3.55k
    : name_(name)
86
3.55k
    , first_(0), insert_point_(&first_)
87
3.55k
    , committed_(true), attached_(false)
88
3.55k
    , md_info_list_index(-1)
89
3.55k
    , settings_read_in_(false)
90
3.55k
    , load_filter_hook(0)
91
3.55k
    , filter_mode_notifier(0)
92
3.55k
  {
93
3.55k
    keyinfo_begin = mainbegin;
94
3.55k
    keyinfo_end   = mainend;
95
3.55k
    extra_begin = 0;
96
3.55k
    extra_end   = 0;
97
3.55k
  }
98
99
3.55k
  Config::~Config() {
100
3.55k
    del();
101
3.55k
  }
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
3.55k
  {
174
162k
    while (first_) {
175
158k
      Entry * tmp = first_->next;
176
158k
      delete first_;
177
158k
      first_ = tmp;
178
158k
    }
179
180
3.55k
    Vector<Notifier *>::iterator i   = notifier_list.begin();
181
3.55k
    Vector<Notifier *>::iterator end = notifier_list.end();
182
183
7.10k
    for(; i != end; ++i) {
184
3.54k
      delete (*i);
185
3.54k
      *i = 0;
186
3.54k
    }
187
    
188
3.55k
    notifier_list.clear();
189
190
3.55k
#ifdef HAVE_LIBDL
191
3.55k
    filter_modules.clear();
192
3.55k
    for (Vector<Cacheable *>::iterator i = filter_modules_ptrs.begin();
193
3.55k
         i != filter_modules_ptrs.end();
194
3.55k
         ++i)
195
0
      (*i)->release();
196
3.55k
    filter_modules_ptrs.clear();
197
3.55k
#endif
198
3.55k
  }
199
200
  void Config::set_filter_modules(const ConfigModule * modbegin, 
201
          const ConfigModule * modend)
202
2.46k
  {
203
2.46k
    assert(filter_modules_ptrs.empty());
204
2.46k
    filter_modules.clear();
205
2.46k
    filter_modules.assign(modbegin, modend);
206
2.46k
  }
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
3.54k
  {
231
3.54k
    Vector<Notifier *>::iterator i   = notifier_list.begin();
232
3.54k
    Vector<Notifier *>::iterator end = notifier_list.end();
233
234
4.62k
    while (i != end && *i != n)
235
1.07k
      ++i;
236
237
3.54k
    if (i != end) {
238
    
239
0
      return false;
240
    
241
3.54k
    } else {
242
243
3.54k
      notifier_list.push_back(n);
244
3.54k
      return true;
245
246
3.54k
    }
247
3.54k
  }
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
153k
  {
298
153k
    const Entry * res = 0;
299
153k
    const Entry * cur = first_;
300
301
8.49M
    while (cur) {
302
8.34M
      if (cur->key == key && cur->action != NoOp)  res = cur;
303
8.34M
      cur = cur->next;
304
8.34M
    }
305
306
153k
    if (!res || res->action == Reset) return 0;
307
35.6k
    return res;
308
153k
  }
309
310
  bool Config::have(ParmStr key) const 
311
17.7k
  {
312
17.7k
    PosibErr<const KeyInfo *> pe = keyinfo(key);
313
17.7k
    if (pe.has_err()) {pe.ignore_err(); return false;}
314
17.7k
    return lookup(pe.data->name);
315
17.7k
  }
316
317
  PosibErr<String> Config::retrieve(ParmStr key) const
318
95.9k
  {
319
95.9k
    RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
320
95.9k
    if (ki->type == KeyInfoList) return make_err(key_not_string, ki->name);
321
322
95.9k
    const Entry * cur = lookup(ki->name);
323
324
95.9k
    return cur ? cur->value : get_default(ki);
325
95.9k
  }
326
327
  PosibErr<Config::Value> Config::retrieve_value(ParmStr key) const
328
2.18k
  {
329
2.18k
    RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
330
2.18k
    if (ki->type == KeyInfoList) return make_err(key_not_string, ki->name);
331
332
2.18k
    const Entry * cur = lookup(ki->name);
333
334
2.18k
    return cur ? Value(cur->value,cur->secure) : Value(get_default(ki), true);
335
2.18k
  }
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
30.5k
  {
361
30.5k
    RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
362
30.5k
    if (ki->type != KeyInfoBool) return make_err(key_not_bool, ki->name);
363
364
30.5k
    const Entry * cur = lookup(ki->name);
365
366
30.5k
    String value(cur ? cur->value : get_default(ki));
367
368
30.5k
    if (value == "false") return false;
369
18.4k
    else                  return true;
370
30.5k
  }
371
  
372
  PosibErr<int> Config::retrieve_int(ParmStr key) const
373
3.31k
  {
374
3.31k
    assert(committed_); // otherwise the value may not be an integer
375
                        // as it has not been verified.
376
377
3.31k
    RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
378
3.31k
    if (ki->type != KeyInfoInt) return make_err(key_not_int, ki->name);
379
380
3.31k
    const Entry * cur = lookup(ki->name);
381
382
3.31k
    String value(cur ? cur->value : get_default(ki));
383
384
3.31k
    return atoi(value.str());
385
3.31k
  }
386
387
#define RET_ON_ERR_WRAP(prefix, key, cmd)                                \
388
214k
  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
20.5k
  {
394
20.5k
    const Entry * cur = first_;
395
20.5k
    const Entry * first_to_use = 0;
396
397
1.80M
    while (cur) {
398
1.78M
      if (cur->key == ki->name && 
399
325k
          (first_to_use == 0 || 
400
317k
           cur->action == Reset || cur->action == Set 
401
243k
           || cur->action == ListClear)) 
402
83.8k
        first_to_use = cur;
403
1.78M
      cur = cur->next;
404
1.78M
    }
405
406
20.5k
    cur = first_to_use;
407
408
20.5k
    if (include_default && 
409
20.5k
        (!cur || 
410
8.06k
         !(cur->action == Set || cur->action == ListClear)))
411
15.0k
    {
412
15.0k
      String def = get_default(ki);
413
15.0k
      separate_list(def, m, true);
414
15.0k
    }
415
416
20.5k
    if (cur && cur->action == Reset) {
417
80
      cur = cur->next;
418
80
    }
419
420
20.5k
    if (cur && cur->action == Set) {
421
4.73k
      if (!include_default) m.clear();
422
4.73k
      RET_ON_ERR_WRAP("", ki->name, m.add(cur->value));
423
4.73k
      cur = cur->next;
424
4.73k
    }
425
426
20.5k
    if (cur && cur->action == ListClear) {
427
1.30k
      if (!include_default) m.clear();
428
1.30k
      cur = cur->next;
429
1.30k
    }
430
431
599k
    while (cur) {
432
579k
      if (cur->key == ki->name) {
433
209k
        if (cur->action == ListAdd)
434
116k
          RET_ON_ERR_WRAP("add-", ki->name, m.add(cur->value));
435
93.0k
        else if (cur->action == ListRemove)
436
93.0k
          RET_ON_ERR_WRAP("remove-", ki->name, m.remove(cur->value));
437
209k
      }
438
579k
      cur = cur->next;
439
579k
    }
440
20.5k
    return no_err;
441
20.5k
  }
442
443
#undef RET_ON_ERR_WRAP
444
445
  PosibErr<void> Config::retrieve_list(ParmStr key, 
446
               MutableContainer * m) const
447
20.5k
  {
448
20.5k
    RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
449
20.5k
    if (ki->type != KeyInfoList) return make_err(key_not_list, ki->name);
450
451
20.5k
    RET_ON_ERR(lookup_list(ki, *m, true));
452
453
20.5k
    return no_err;
454
20.5k
  }
455
456
  static const KeyInfo * find(ParmStr key, 
457
            const KeyInfo * i, 
458
            const KeyInfo * end) 
459
419k
  {
460
7.91M
    while (i != end) {
461
7.84M
      if (strcmp(key, i->name) == 0)
462
346k
  return i;
463
7.49M
      ++i;
464
7.49M
    }
465
72.6k
    return i;
466
419k
  }
467
468
  static const ConfigModule * find(ParmStr key, 
469
           const ConfigModule * i, 
470
           const ConfigModule * end) 
471
30.1k
  {
472
219k
    while (i != end) {
473
208k
      if (strcmp(key, i->name) == 0)
474
19.4k
  return i;
475
189k
      ++i;
476
189k
    }
477
10.7k
    return i;
478
30.1k
  }
479
480
  PosibErr<const KeyInfo *> Config::keyinfo(ParmStr key) const
481
360k
  {
482
360k
    typedef PosibErr<const KeyInfo *> Ret;
483
360k
    {
484
360k
      const KeyInfo * i;
485
360k
      i = acommon::find(key, keyinfo_begin, keyinfo_end);
486
360k
      if (i != keyinfo_end) return Ret(i);
487
      
488
31.5k
      i = acommon::find(key, extra_begin, extra_end);
489
31.5k
      if (i != extra_end) return Ret(i);
490
      
491
31.5k
      const char * s = strncmp(key, "f-", 2) == 0 ? key + 2 : key.str();
492
31.5k
      const char * h = strchr(s, '-');
493
31.5k
      if (h == 0) goto err;
494
495
24.8k
      String k(s, h - s);
496
24.8k
      const ConfigModule * j = acommon::find(k,
497
24.8k
               filter_modules.pbegin(),
498
24.8k
               filter_modules.pend());
499
      
500
24.8k
      if (j == filter_modules.pend() && load_filter_hook && committed_) {
501
        // FIXME: This isn't quite right
502
5.35k
        PosibErrBase pe = load_filter_hook(const_cast<Config *>(this), k);
503
5.35k
        pe.ignore_err();
504
5.35k
        j = acommon::find(k,
505
5.35k
                          filter_modules.pbegin(),
506
5.35k
                          filter_modules.pend());
507
5.35k
      }
508
509
24.8k
      if (j == filter_modules.pend()) goto err;
510
511
19.4k
      i = acommon::find(key, j->begin, j->end);
512
19.4k
      if (i != j->end) return Ret(i);
513
      
514
8.53k
      if (strncmp(key, "f-", 2) != 0) k = "f-";
515
855
      else                            k = "";
516
8.53k
      k += key;
517
8.53k
      i = acommon::find(k, j->begin, j->end);
518
8.53k
      if (i != j->end) return Ret(i);
519
8.53k
    }
520
13.0k
  err:  
521
13.0k
    return Ret().prim_err(unknown_key, key);
522
8.53k
  }
523
524
  static bool proc_locale_str(ParmStr lang, String & final_str)
525
12.5k
  {
526
12.5k
    if (lang == 0) return false;
527
3.14k
    const char * i = lang;
528
3.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
3.14k
  {
541
3.14k
    if (proc_locale_str(getenv("LC_MESSAGES"), str)) return;
542
3.14k
    if (proc_locale_str(getenv("LANG"), str)) return;
543
3.14k
    if (proc_locale_str(getenv("LANGUAGE"), str)) return;
544
3.14k
    str = DEFAULT_LANG;
545
3.14k
  }
546
547
#ifdef USE_LOCALE
548
549
  static void get_lang(String & final_str) 
550
3.14k
  {
551
    // FIXME: THIS IS NOT THREAD SAFE
552
3.14k
    String locale = setlocale (LC_ALL, NULL);
553
3.14k
    if (locale == "C")
554
3.14k
      setlocale (LC_ALL, "");
555
3.14k
    const char * lang = setlocale (LC_MESSAGES, NULL);
556
3.14k
    bool res = proc_locale_str(lang, final_str);
557
3.14k
    if (locale == "C")
558
3.14k
      setlocale(LC_MESSAGES, locale.c_str());
559
3.14k
    if (!res)
560
3.14k
      get_lang_env(final_str);
561
3.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
1.00k
  {
576
1.00k
    const char * codeset = nl_langinfo(CODESET);
577
1.00k
    if (ascii_encoding(c, codeset)) codeset = "none";
578
1.00k
    final_str = codeset;
579
1.00k
  }
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
117k
  {
592
117k
    bool   in_replace = false;
593
117k
    String final_str;
594
117k
    String replace;
595
117k
    const char * i = ki->def;
596
117k
    if (*i == '!') { // special cases
597
4.94k
      ++i;
598
    
599
4.94k
      if (strcmp(i, "lang") == 0) {
600
        
601
3.94k
        const Entry * entry;
602
3.94k
        if (entry = lookup("actual-lang"), entry) {
603
589
          return entry->value;
604
3.35k
        } else if (have("master")) {
605
210
    final_str = "<unknown>";
606
3.14k
  } else {
607
3.14k
    get_lang(final_str);
608
3.14k
  }
609
  
610
3.94k
      } else if (strcmp(i, "encoding") == 0) {
611
612
1.00k
        get_encoding(*this, final_str);
613
614
1.00k
      } 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
1.33M
    } else for(; *i; ++i) {
625
    
626
1.21M
      if (!in_replace) {
627
628
816k
  if (*i == '<') {
629
45.2k
    in_replace = true;
630
770k
  } else {
631
770k
    final_str += *i;
632
770k
  }
633
634
816k
      } else { // in_replace
635
      
636
402k
  if (*i == '/' || *i == ':' || *i == '|' || *i == '#' || *i == '^') {
637
17.6k
    char sep = *i;
638
17.6k
    String second;
639
17.6k
    ++i;
640
99.4k
    while (*i != '\0' && *i != '>') second += *i++;
641
17.6k
    if (sep == '/') {
642
10.1k
      String s1 = retrieve(replace);
643
10.1k
      String s2 = retrieve(second);
644
10.1k
      final_str += add_possible_dir(s1, s2);
645
10.1k
    } else if (sep == ':') {
646
1.73k
      String s1 = retrieve(replace);
647
1.73k
      final_str += add_possible_dir(s1, second);
648
5.71k
    } 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
5.71k
    } else if (sep == '^') {
655
1.62k
      String s1 = retrieve(replace);
656
1.62k
      String s2 = retrieve(second);
657
1.62k
      final_str += figure_out_dir(s1, s2);
658
4.09k
    } else { // sep == '|'
659
4.09k
      assert(replace[0] == '$');
660
4.09k
      const char * env = getenv(replace.c_str()+1);
661
4.09k
      final_str += env ? env : second;
662
4.09k
    }
663
17.6k
    replace = "";
664
17.6k
    in_replace = false;
665
666
384k
  } else if (*i == '>') {
667
668
27.6k
    final_str += retrieve(replace).data;
669
27.6k
    replace = "";
670
27.6k
    in_replace = false;
671
672
356k
  } else {
673
674
356k
    replace += *i;
675
676
356k
  }
677
678
402k
      }
679
      
680
1.21M
    }
681
117k
    return final_str;
682
117k
  }
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
176k
  do {                                      \
694
176k
    if (len == l && memcmp(s, v, l) == 0) { \
695
14.8k
      if (action) *action = a;              \
696
14.8k
      return c + 1;                         \
697
14.8k
    }                                       \
698
176k
  } while (false)
699
700
  const char * Config::base_name(const char * s, Action * action)
701
50.6k
  {
702
50.6k
    if (action) *action = Set;
703
50.6k
    const char * c = strchr(s, '-');
704
50.6k
    if (!c) return s;
705
25.7k
    unsigned len = c - s;
706
25.7k
    TEST("reset",   5, Reset);
707
22.1k
    TEST("enable",  6, Enable);
708
21.6k
    TEST("dont",    4, Disable);
709
21.4k
    TEST("disable", 7, Disable);
710
21.1k
    TEST("lset",    4, ListSet);
711
19.5k
    TEST("rem",     3, ListRemove);
712
16.8k
    TEST("remove",  6, ListRemove);
713
15.5k
    TEST("add",     3, ListAdd);
714
12.2k
    TEST("clear",   5, ListClear);
715
10.9k
    return s;
716
12.2k
  }
717
718
#undef TEST
719
720
  void separate_list(ParmStr value, AddableContainer & out, bool do_unescape)
721
18.5k
  {
722
18.5k
    unsigned len = value.size();
723
    
724
18.5k
    VARARRAY(char, buf, len + 1);
725
18.5k
    memcpy(buf, value, len + 1);
726
    
727
18.5k
    len = strlen(buf);
728
18.5k
    char * s = buf;
729
18.5k
    char * end = buf + len;
730
      
731
116k
    while (s < end)
732
97.8k
    {
733
97.8k
      if (do_unescape) while (*s == ' ' || *s == '\t') ++s;
734
97.8k
      char * b = s;
735
97.8k
      char * e = s;
736
2.03M
      while (*s != '\0') {
737
2.02M
        if (do_unescape && *s == '\\') {
738
7.97k
          ++s;
739
7.97k
          if (*s == '\0') break;
740
7.89k
          e = s;
741
7.89k
          ++s;
742
2.01M
        } else {
743
2.01M
          if (*s == ':') break;
744
1.93M
          if (!do_unescape || (*s != ' ' && *s != '\t')) e = s;
745
1.93M
          ++s;
746
1.93M
        }
747
2.02M
      }
748
97.8k
      if (s != b) {
749
96.0k
        ++e;
750
96.0k
        *e = '\0';
751
96.0k
        if (do_unescape) unescape(b);
752
      
753
96.0k
        out.add(b);
754
96.0k
      }
755
97.8k
      ++s;
756
97.8k
    }
757
18.5k
  }
758
759
  void combine_list(String & res, const StringList & in)
760
3.58k
  {
761
3.58k
    res.clear();
762
3.58k
    StringListEnumeration els = in.elements_obj();
763
3.58k
    const char * s = 0;
764
11.4k
    while ( (s = els.next()) != 0) 
765
7.81k
    {
766
562k
      for (; *s; ++s) {
767
554k
        if (*s == ':')
768
4.26k
          res.append('\\');
769
554k
        res.append(*s);
770
554k
      }
771
7.81k
      res.append(':');
772
7.81k
    }
773
3.58k
    if (!res.empty() && res.back() == ':') res.pop_back();
774
3.58k
  }
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
42.3k
  {
785
42.3k
    Config::Entry * entry = new Config::Entry(*orig_entry);
786
42.3k
    entry->value = val;
787
42.3k
    entry->action = Config::ListAdd;
788
42.3k
    config->set(entry);
789
42.3k
    return true;
790
42.3k
  }
791
792
  void Config::replace_internal(ParmStr key, ParmStr value)
793
7.53k
  {
794
7.53k
    Entry * entry = new Entry;
795
7.53k
    entry->key = key;
796
7.53k
    entry->value = value;
797
7.53k
    entry->action = Set;
798
7.53k
    entry->next = *insert_point_;
799
7.53k
    *insert_point_ = entry;
800
7.53k
    insert_point_ = &entry->next;
801
7.53k
  }
802
803
  PosibErr<void> Config::replace(ParmStr key, ParmStr value)
804
45.0k
  {
805
45.0k
    Entry * entry = new Entry;
806
45.0k
    entry->key = key;
807
45.0k
    entry->value = value;
808
45.0k
    entry->secure = true;
809
45.0k
    return set(entry);
810
45.0k
  }
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
94.5k
  {
822
94.5k
    StackPtr<Entry> entry(entry0);
823
824
94.5k
    if (entry->action == NoOp)
825
50.6k
      entry->key = base_name(entry->key.str(), &entry->action);
826
827
94.5k
    if (num_parms(entry->action) == 0 && !entry->value.empty()) 
828
731
    {
829
731
      if (entry->place_holder == -1) {
830
731
        switch (entry->action) {
831
270
        case Reset:
832
270
          return make_err(no_value_reset, entry->key);
833
212
        case Enable:
834
212
          return make_err(no_value_enable, entry->key);
835
39
        case Disable:
836
39
          return make_err(no_value_disable, entry->key);
837
210
        case ListClear:
838
210
          return make_err(no_value_clear, entry->key);
839
0
        default:
840
0
          abort(); // this shouldn't happen
841
731
        }
842
731
      } else {
843
0
        entry->place_holder = -1;
844
0
      }
845
731
    }
846
847
93.7k
    if (entry->action != ListSet) {
848
849
92.2k
      switch (entry->action) {
850
275
      case Enable:
851
275
        entry->value = "true";
852
275
        entry->action = Set;
853
275
        break;
854
501
      case Disable:
855
501
        entry->value = "false";
856
501
        entry->action = Set;
857
501
        break;
858
91.4k
      default:
859
91.4k
        ;
860
92.2k
      }
861
92.2k
      if (do_unescape) unescape(entry->value.mstr());
862
863
92.2k
      entry->next = *insert_point_;
864
92.2k
      *insert_point_ = entry;
865
92.2k
      insert_point_ = &entry->next;
866
92.2k
      entry.release();
867
92.2k
      if (committed_) RET_ON_ERR(commit(entry0)); // entry0 == entry
868
      
869
92.2k
    } else { // action == ListSet
870
871
1.55k
      Entry * ent = new Entry;
872
1.55k
      ent->key = entry->key;
873
1.55k
      ent->action = ListClear;
874
1.55k
      RET_ON_ERR(set(ent));
875
876
874
      ListAddHelper helper;
877
874
      helper.config = this;
878
874
      helper.orig_entry = entry;
879
880
874
      separate_list(entry->value.str(), helper, do_unescape);
881
874
    }
882
76.3k
    return no_err;
883
93.7k
  }
884
885
  PosibErr<void> Config::merge(const Config & other)
886
1.22k
  {
887
73.3k
    for (const Entry * src  = other.first_; src; src = src->next)
888
72.1k
    {
889
72.1k
      if (src->action == NoOp) continue;
890
58.7k
      Entry * entry = new Entry(*src);
891
58.7k
      entry->next = *insert_point_;
892
58.7k
      *insert_point_ = entry;
893
58.7k
      insert_point_ = &entry->next;
894
58.7k
      if (committed_) RET_ON_ERR(commit(entry));
895
58.7k
    }
896
1.22k
    return no_err;
897
1.22k
  }
898
899
  PosibErr<void> Config::lang_config_merge(const Config & other,
900
                                           int which, ParmStr data_encoding)
901
7.53k
  {
902
7.53k
    Conv to_utf8;
903
7.53k
    RET_ON_ERR(to_utf8.setup(*this, data_encoding, "utf-8", NormTo));
904
7.53k
    const Entry * src  = other.first_;
905
7.53k
    Entry * * ip = &first_;
906
45.8k
    while (src)
907
38.2k
    {
908
38.2k
      const KeyInfo * l_ki = other.keyinfo(src->key);
909
38.2k
      if (l_ki->other_data == which) {
910
404
        const KeyInfo * c_ki = keyinfo(src->key);
911
404
        Entry * entry = new Entry(*src);
912
404
        if (c_ki->flags & KEYINFO_UTF8)
913
0
          entry->value = to_utf8(entry->value);
914
404
        entry->next = *ip;
915
404
        *ip = entry;
916
404
        ip = &entry->next;
917
404
      }
918
38.2k
      src = src->next;
919
38.2k
    }
920
7.53k
    return no_err;
921
7.53k
  }
922
923
924
#define NOTIFY_ALL(fun)                                       \
925
136k
  do {                                                        \
926
136k
    Vector<Notifier *>::iterator   i = notifier_list.begin(); \
927
136k
    Vector<Notifier *>::iterator end = notifier_list.end();   \
928
262k
    while (i != end) {                                        \
929
131k
      RET_ON_ERR((*i)->fun);                                  \
930
131k
      ++i;                                                    \
931
126k
    }                                                         \
932
136k
  } while (false)
933
934
  PosibErr<int> Config::commit(Entry * entry, Conv * conv) 
935
150k
  {
936
150k
    PosibErr<const KeyInfo *> pe = keyinfo(entry->key);
937
150k
    {
938
150k
      if (pe.has_err()) goto error;
939
      
940
137k
      const KeyInfo * ki = pe;
941
942
137k
      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
137k
      int place_holder = entry->place_holder;
953
      
954
137k
      if (conv && ki->flags & KEYINFO_UTF8)
955
0
        entry->value = (*conv)(entry->value);
956
957
137k
      if (ki->type != KeyInfoList && list_action(entry->action)) {
958
195
        pe = make_err(key_not_list, entry->key);
959
195
        goto error;
960
195
      }
961
962
137k
      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
137k
      String value(entry->action == Reset ? get_default(ki) : entry->value);
967
      
968
137k
      switch (ki->type) {
969
        
970
1.30k
      case KeyInfoBool: {
971
972
1.30k
        bool val;
973
      
974
1.30k
        if  (value.empty() || entry->place_holder != -1) {
975
          // if entry->place_holder != -1 than IGNORE the value no
976
          // matter what it is
977
318
          entry->value = "true";
978
318
          val = true;
979
318
          place_holder = -1;
980
982
        } else if (value == "true") {
981
373
          val = true;
982
609
        } else if (value == "false") {
983
422
          val = false;
984
422
        } else {
985
187
          pe = make_err(bad_value, entry->key, value,
986
                        /* TRANSLATORS: "true" and "false" are literal
987
                         * values and should not be translated.*/
988
187
                        _("either \"true\" or \"false\""));
989
187
          goto error;
990
187
        }
991
992
1.11k
        NOTIFY_ALL(item_updated(ki, val));
993
1.11k
        break;
994
        
995
32.4k
      } case KeyInfoString:
996
        
997
32.4k
        NOTIFY_ALL(item_updated(ki, value));
998
27.1k
        break;
999
        
1000
27.1k
      case KeyInfoInt: 
1001
1.11k
      {
1002
1.11k
        int num;
1003
        
1004
1.11k
        if (sscanf(value.str(), "%i", &num) == 1 && num >= 0) {
1005
527
          NOTIFY_ALL(item_updated(ki, num));
1006
583
        } else {
1007
583
          pe = make_err(bad_value, entry->key, value, _("a positive integer"));
1008
583
          goto error;
1009
583
        }
1010
        
1011
527
        break;
1012
1.11k
      }
1013
102k
      case KeyInfoList:
1014
        
1015
102k
        NOTIFY_ALL(list_updated(ki));
1016
102k
        break;
1017
        
1018
137k
      }
1019
131k
      return place_holder;
1020
137k
    }
1021
14.0k
  error:
1022
14.0k
    entry->action = NoOp;
1023
14.0k
    if (!entry->file.empty())
1024
0
      return pe.with_file(entry->file, entry->line_num);
1025
14.0k
    else
1026
14.0k
      return (PosibErrBase &)pe;
1027
14.0k
  }
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
0
      : 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
0
    : 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
1.09k
  {
1250
1.09k
    String buf;
1251
1.09k
    DataPair dp;
1252
6.66k
    while (getdata_pair(in, dp, buf)) {
1253
5.57k
      to_lower(dp.key);
1254
5.57k
      Entry * entry = new Entry;
1255
5.57k
      entry->key = dp.key;
1256
5.57k
      entry->value = dp.value;
1257
5.57k
      entry->file = id;
1258
5.57k
      entry->line_num = dp.line_num;
1259
5.57k
      RET_ON_ERR(set(entry, true));
1260
5.57k
    }
1261
1.09k
    return no_err;
1262
1.09k
  }
1263
1264
3.54k
  PosibErr<void> Config::read_in_file(ParmStr file) {
1265
3.54k
    FStream in;
1266
3.54k
    RET_ON_ERR(in.open(file, "r"));
1267
1.09k
    return read_in(in, file);
1268
3.54k
  }
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
1.22k
  {
1278
1.22k
    if (settings_read_in_) return false;
1279
1280
1.22k
    bool was_committed = committed_;
1281
1.22k
    set_committed_state(false);
1282
1283
1.22k
    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
1.22k
    } else {
1290
1291
1.22k
      if (other) merge(*other);
1292
1293
1.22k
      const char * env = getenv("ASPELL_CONF");
1294
1.22k
      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
1.22k
      {
1300
1.22k
        insert_point_ = &first_;
1301
1.22k
        PosibErrBase pe = read_in_file(retrieve("per-conf-path"));
1302
1.22k
        if (pe.has_err() && !pe.has_err(cant_read_file)) return pe;
1303
1.22k
      }
1304
      
1305
1.22k
      {
1306
1.22k
        insert_point_ = &first_;
1307
1.22k
        PosibErrBase pe = read_in_file(retrieve("conf-path"));
1308
1.22k
        if (pe.has_err() && !pe.has_err(cant_read_file)) return pe;
1309
1.22k
      }
1310
1311
1.22k
      if (was_committed)
1312
1.22k
        RET_ON_ERR(commit_all());
1313
1314
1.16k
      settings_read_in_ = true;
1315
1.16k
    }
1316
1317
1.16k
    return true;
1318
1.22k
  }
1319
1320
  PosibErr<void> Config::commit_all(Vector<int> * phs, const char * codeset)
1321
1.22k
  {
1322
1.22k
    committed_ = true;
1323
1.22k
    Entry * uncommitted = first_;
1324
1.22k
    first_ = 0;
1325
1.22k
    insert_point_ = &first_;
1326
1.22k
    Conv to_utf8;
1327
1.22k
    if (codeset)
1328
0
      RET_ON_ERR(to_utf8.setup(*this, codeset, "utf-8", NormTo));
1329
1.22k
    PosibErr<void> ret;
1330
59.9k
    while (uncommitted) {
1331
58.7k
      Entry * cur = uncommitted;
1332
58.7k
      uncommitted = cur->next;
1333
58.7k
      cur->next = 0;
1334
58.7k
      *insert_point_ = cur;
1335
58.7k
      insert_point_ = &((*insert_point_)->next);
1336
58.7k
      PosibErr<int> pe = commit(cur, codeset ? &to_utf8 : 0);
1337
58.7k
      if (pe.has_err()) {
1338
2.59k
        if (ret.has_err())
1339
2.52k
          pe.ignore_err();
1340
64
        else
1341
64
          ret = pe;
1342
2.59k
        continue;
1343
2.59k
      }
1344
56.1k
      int place_holder = pe.data;
1345
56.1k
      if (phs && place_holder != -1 && (phs->empty() || phs->back() != place_holder))
1346
0
        phs->push_back(place_holder);
1347
56.1k
    }
1348
1.22k
    return ret;
1349
1.22k
  }
1350
1351
1.22k
  PosibErr<void> Config::set_committed_state(bool val) {
1352
1.22k
    if (val && !committed_) {
1353
0
      RET_ON_ERR(commit_all());
1354
1.22k
    } else if (!val && committed_) {
1355
1.22k
      assert(empty());
1356
1.22k
      committed_ = false;
1357
1.22k
    }
1358
1.22k
    return no_err;
1359
1.22k
  }
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
2.46k
  Config * new_basic_config() { 
1540
2.46k
    aspell_gettext_init();
1541
2.46k
    return new Config("aspell",
1542
2.46k
          config_impl_keys_begin,
1543
2.46k
          config_impl_keys_end);
1544
2.46k
  }
1545
  
1546
}
1547