Coverage Report

Created: 2025-10-27 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/aspell/modules/speller/default/speller_impl.cpp
Line
Count
Source
1
// This file is part of The New Aspell
2
// Copyright (C) 2000-2001,2011 by Kevin Atkinson under the GNU LGPL
3
// license version 2.0 or 2.1.  You should have received a copy of the
4
// LGPL license along with this library if you did not you can find it
5
// at http://www.gnu.org/.
6
7
#include <stdlib.h>
8
#include <typeinfo>
9
10
#include "clone_ptr-t.hpp"
11
#include "config.hpp"
12
#include "data.hpp"
13
#include "data_id.hpp"
14
#include "errors.hpp"
15
#include "language.hpp"
16
#include "speller_impl.hpp"
17
#include "string_list.hpp"
18
#include "suggest.hpp"
19
#include "tokenizer.hpp"
20
#include "convert.hpp"
21
#include "stack_ptr.hpp"
22
#include "istream_enumeration.hpp"
23
24
//#include "iostream.hpp"
25
26
#include "gettext.h"
27
28
namespace aspeller {
29
  //
30
  // data_access functions
31
  //
32
33
5.40k
  const char * SpellerImpl::lang_name() const {
34
5.40k
    return lang_->name();
35
5.40k
  }
36
37
  //
38
  // to lower
39
  //
40
41
  char * SpellerImpl::to_lower(char * str) 
42
0
  {
43
0
    for (char * i = str; *i; ++i)
44
0
      *i = lang_->to_lower(*i);
45
0
    return str;
46
0
  }
47
48
  //////////////////////////////////////////////////////////////////////
49
  //
50
  // Spell check methods
51
  //
52
53
0
  PosibErr<void> SpellerImpl::add_to_personal(MutableString word) {
54
0
    if (!personal_) return no_err;
55
0
    return personal_->add(word);
56
0
  }
57
  
58
0
  PosibErr<void> SpellerImpl::add_to_session(MutableString word) {
59
0
    if (!session_) return no_err;
60
0
    return session_->add(word);
61
0
  }
62
63
0
  PosibErr<void> SpellerImpl::clear_session() {
64
0
    if (!session_) return no_err;
65
0
    return session_->clear();
66
0
  }
67
68
  PosibErr<void> SpellerImpl::store_replacement(MutableString mis, 
69
                                                MutableString cor)
70
0
  {
71
0
    return SpellerImpl::store_replacement(mis,cor,true);
72
0
  }
73
74
  PosibErr<void> SpellerImpl::store_replacement(const String & mis, 
75
                                                const String & cor, 
76
                                                bool memory) 
77
0
  {
78
0
    if (ignore_repl) return no_err;
79
0
    if (!repl_) return no_err;
80
0
    String::size_type pos;
81
0
    StackPtr<StringEnumeration> sugels(intr_suggest_->suggest(mis.c_str()).elements());
82
0
    const char * first_word = sugels->next();
83
0
    CheckInfo w1, w2;
84
0
    String cor1, cor2;
85
0
    String buf;
86
0
    bool correct = false;
87
0
    pos = cor.find(' '); 
88
0
    if (pos == String::npos) {
89
0
      cor1 = cor;
90
0
      correct = check_affix(cor, w1, 0);
91
0
    } else {
92
0
      cor1 = (String)cor.substr(0,pos);
93
0
      ++pos;
94
0
      while (pos < cor.size() && cor[pos] == ' ') ++pos;
95
0
      cor2 = (String)cor.substr(pos);
96
0
      correct = check_affix(cor1, w1, 0) && check_affix(cor2, w2, 0);
97
0
    }
98
0
    if (correct) {
99
0
      String cor_orignal_casing(cor1);
100
0
      if (!cor2.empty()) {
101
0
        cor_orignal_casing += cor[pos-1];
102
0
        cor_orignal_casing += cor2;
103
0
      }
104
      // Don't try to add the empty string, causes all kinds of
105
      // problems.  Can happen if the original replacement nothing but
106
      // whitespace.
107
0
      if (cor_orignal_casing.empty()) 
108
0
        return no_err;
109
0
      if (first_word == 0 || cor != first_word) {
110
0
        lang().to_lower(buf, mis.str());
111
0
        repl_->add_repl(buf, cor_orignal_casing);
112
0
      }
113
      
114
0
      if (memory && prev_cor_repl_ == mis) 
115
0
        store_replacement(prev_mis_repl_, cor, false);
116
      
117
0
    } else { //!correct
118
      
119
0
      if (memory) {
120
0
         if (prev_cor_repl_ != mis)
121
0
          prev_mis_repl_ = mis;
122
0
        prev_cor_repl_ = cor;
123
0
       }
124
0
    }
125
0
    return no_err;
126
0
  }
127
128
  //
129
  // simple functions
130
  //
131
132
  PosibErr<const WordList *> SpellerImpl::suggest(MutableString word) 
133
19.4k
  {
134
19.4k
    return &suggest_->suggest(word);
135
19.4k
  }
136
137
  bool SpellerImpl::check_simple (ParmString w, WordEntry & w0) 
138
493k
  {
139
493k
    w0.clear(); // FIXME: is this necessary?
140
493k
    const char * x = w;
141
2.42M
    while (*x != '\0' && (x-w) < static_cast<int>(ignore_count)) ++x;
142
493k
    if (*x == '\0') {w0.word = w; return true;}
143
376k
    WS::const_iterator i   = check_ws.begin();
144
376k
    WS::const_iterator end = check_ws.end();
145
1.43M
    do {
146
1.43M
      if ((*i)->lookup(w, &s_cmp, w0)) return true;
147
1.41M
      ++i;
148
1.41M
    } while (i != end);
149
355k
    return false;
150
376k
  };
151
152
  bool SpellerImpl::check_affix(ParmString word, CheckInfo & ci, GuessInfo * gi)
153
493k
  {
154
493k
    WordEntry w;
155
493k
    bool res = check_simple(word, w);
156
493k
    if (res) {ci.word = w.word; return true;}
157
355k
    if (affix_compress) {
158
15.4k
      res = lang_->affix()->affix_check(LookupInfo(this, LookupInfo::Word), word, ci, 0);
159
15.4k
      if (res) return true;
160
15.4k
    }
161
355k
    if (affix_info && gi) {
162
217k
      lang_->affix()->affix_check(LookupInfo(this, LookupInfo::Guess), word, ci, gi);
163
217k
    }
164
355k
    return false;
165
355k
  }
166
167
  inline bool SpellerImpl::check_single(char * word, /* it WILL modify word */
168
                                        bool try_uppercase,
169
                                        CheckInfo & ci, GuessInfo * gi)
170
493k
  {
171
493k
    bool res = check_affix(word, ci, gi);
172
493k
    if (res) return true;
173
355k
    if (!try_uppercase) return false;
174
469
    char t = *word;
175
469
    *word = lang_->to_title(t);
176
469
    res = check_affix(word, ci, gi);
177
469
    *word = t;
178
469
    if (res) return true;
179
469
    return false;
180
469
  }
181
182
  CheckInfo * SpellerImpl::check_runtogether(char * word, char * word_end, 
183
                                             /* it WILL modify word */
184
                                             bool try_uppercase,
185
                                             unsigned run_together_limit,
186
                                             CheckInfo * ci, CheckInfo * ci_end,
187
                                             GuessInfo * gi)
188
264k
  {
189
264k
    if (ci >= ci_end) return NULL;
190
264k
    clear_check_info(*ci);
191
264k
    bool res = check_single(word, try_uppercase, *ci, gi);
192
264k
    if (res) return ci;
193
182k
    if (run_together_limit <= 1) return NULL;
194
16.6k
    enum {Yes, No, Unknown} is_title = try_uppercase ? Yes : Unknown;
195
16.6k
    for (char * i = word + run_together_min_; 
196
238k
         i <= word_end - run_together_min_;
197
222k
         ++i) 
198
229k
    {
199
229k
      char t = *i;
200
229k
      *i = '\0';
201
229k
      clear_check_info(*ci);
202
229k
      res = check_single(word, try_uppercase, *ci, gi);
203
229k
      if (!res) {*i = t; continue;}
204
56.7k
      if (is_title == Unknown)
205
13.2k
        is_title = lang_->case_pattern(word) == FirstUpper ? Yes : No;
206
56.7k
      *i = t;
207
56.7k
      CheckInfo * ci_last = check_runtogether(i, word_end, is_title == Yes, run_together_limit - 1, ci + 1, ci_end, 0);
208
56.7k
      if (ci_last) {
209
7.06k
        ci->compound = true;
210
7.06k
        ci->next = ci + 1;
211
7.06k
        return ci_last;
212
7.06k
      }
213
56.7k
    }
214
9.55k
    return NULL;
215
16.6k
  }
216
217
  PosibErr<bool> SpellerImpl::check(char * word, char * word_end, 
218
                                    /* it WILL modify word */
219
                                    bool try_uppercase,
220
                                    unsigned run_together_limit,
221
                                    CheckInfo * ci, CheckInfo * ci_end,
222
                                    GuessInfo * gi, CompoundInfo * cpi)
223
180k
  {
224
180k
    clear_check_info(*ci);
225
180k
    bool res = check_runtogether(word, word_end, try_uppercase, run_together_limit, ci, ci_end, gi);
226
180k
    if (res) return true;
227
    
228
109k
    CompoundWord cw = lang_->split_word(word, word_end - word, camel_case_);
229
109k
    if (cw.single()) return false;
230
16.6k
    bool ok = true;
231
16.6k
    CheckInfo * ci_prev = NULL;
232
27.2k
    do {
233
27.2k
      unsigned len = cw.word_len();
234
      
235
27.2k
      char save = word[len];
236
27.2k
      word[len] = '\0';
237
27.2k
      CheckInfo * ci_last = check_runtogether(word, word + len, try_uppercase, run_together_limit, ci, ci_end, gi);
238
27.2k
      bool found = ci_last;
239
27.2k
      word[len] = save;
240
241
27.2k
      if (!found) {
242
16.6k
        if (cpi) {
243
2.85k
          ci_last = ci;
244
2.85k
          ok = false;
245
2.85k
          ci->word.str = word;
246
2.85k
          ci->word.len = len;
247
2.85k
          ci->incorrect = true;
248
2.85k
          cpi->incorrect_count++;
249
2.85k
          if (!cpi->first_incorrect)
250
1.40k
            cpi->first_incorrect = ci;
251
13.8k
        } else {
252
13.8k
          return false;
253
13.8k
        }
254
16.6k
      }
255
256
13.4k
      if (cpi)
257
4.31k
        cpi->count++;
258
      
259
13.4k
      if (ci_prev) {
260
4.88k
        ci_prev->compound = true;
261
4.88k
        ci_prev->next = ci;
262
4.88k
      }
263
264
13.4k
      ci_prev = ci_last;
265
13.4k
      ci = ci_last + 1;
266
13.4k
      if (ci >= ci_end) {
267
156
        if (cpi) cpi->count = 0;
268
156
        return false;
269
156
      }
270
      
271
13.2k
      word = word + cw.rest_offset();
272
13.2k
      cw = lang_->split_word(cw.rest, cw.rest_len(), camel_case_);
273
      
274
13.2k
    } while (!cw.empty());
275
    
276
2.69k
    return ok;
277
    
278
    // for (;;) {
279
    //   cw = lang_->split_word(cw.rest, cw.rest_len(), camel_case_);
280
    //   if (cw.empty()) break;
281
    //   if (ci + 1 >= ci_end) {
282
    //     if (cpi) cpi->count = 0;
283
    //     return false;
284
    //   }
285
    //   if (cpi) cpi->count++;
286
    //   len = cw.word_len();
287
    //   save = word[len];
288
    //   word[len] = '\0';
289
    //   ci_last = check_runtogether(word, word + len, try_uppercase, run_together_limit, ci + 1, ci_end, gi);
290
    //   word[len] = save;
291
    //   ci->compound = true;
292
    //   ci->next = ci + 1;
293
    //   if (ci_last) {
294
    //     ci = ci_last;
295
    //   } else if (cpi) {
296
    //     ok = false;
297
    //     ci->word.str = word;
298
    //     ci->word.len = len;
299
    //     ci->incorrect = true;
300
    //     cpi->incorrect_count++;
301
    //   } else {
302
    //     return false;
303
    //   }
304
    //   word = word + cw.rest_offset();
305
    // }
306
16.6k
  }
307
308
  //////////////////////////////////////////////////////////////////////
309
  //
310
  // Word list managment methods
311
  //
312
  
313
0
  PosibErr<void> SpellerImpl::save_all_word_lists() {
314
0
    SpellerDict * i = dicts_;
315
0
    for (; i; i = i->next) {
316
0
      if  (i->save_on_saveall)
317
0
        RET_ON_ERR(i->dict->synchronize());
318
0
    }
319
0
    return no_err;
320
0
  }
321
  
322
0
  int SpellerImpl::num_wordlists() const {
323
0
    return 0; //FIXME
324
0
  }
325
326
0
  SpellerImpl::WordLists SpellerImpl::wordlists() const {
327
0
    return 0; //FIXME
328
    //return MakeEnumeration<DataSetCollection::Parms>(wls_->begin(), DataSetCollection::Parms(wls_->end()));
329
0
  }
330
331
0
  PosibErr<const WordList *> SpellerImpl::personal_word_list() const {
332
0
    const WordList * wl = static_cast<const WordList *>(personal_);
333
0
    if (!wl) return make_err(operation_not_supported_error, 
334
0
                             _("The personal word list is unavailable."));
335
0
    return wl;
336
0
  }
337
338
0
  PosibErr<const WordList *> SpellerImpl::session_word_list() const {
339
0
    const WordList * wl = static_cast<const WordList *>(session_);
340
0
    if (!wl) return make_err(operation_not_supported_error, 
341
0
                             _("The session word list is unavailable."));
342
0
    return wl;
343
0
  }
344
345
0
  PosibErr<const WordList *> SpellerImpl::main_word_list() const {
346
0
    const WordList * wl = dynamic_cast<const WordList *>(main_);
347
0
    if (!wl) return make_err(operation_not_supported_error, 
348
0
                             _("The main word list is unavailable."));
349
0
    return wl;
350
0
  }
351
352
  const SpellerDict * SpellerImpl::locate (const Dict::Id & id) const
353
16.1k
  {
354
44.9k
    for (const SpellerDict * i = dicts_; i; i = i->next)
355
28.9k
      if (i->dict->id() == id) return i;
356
16.0k
    return 0;
357
16.1k
  }
358
359
  PosibErr<void> SpellerImpl::add_dict(SpellerDict * wc)
360
7.51k
  {
361
7.51k
    Dict * w = wc->dict;
362
7.51k
    assert(locate(w->id()) == 0);
363
364
7.51k
    if (!lang_) {
365
1.08k
      lang_.copy(w->lang());
366
1.08k
      config_->replace("lang", lang_name());
367
1.08k
      config_->replace("language-tag", lang_name());
368
6.42k
    } else {
369
6.42k
      if (strcmp(lang_->name(), w->lang()->name()) != 0)
370
0
        return make_err(mismatched_language, lang_->name(), w->lang()->name());
371
6.42k
    }
372
373
    // add to master list
374
7.51k
    wc->next = dicts_;
375
7.51k
    dicts_ = wc;
376
377
    // check if it has a special_id and act accordingly
378
7.51k
    switch (wc->special_id) {
379
0
    case main_id:
380
0
      assert(main_ == 0);
381
0
      main_ = w;
382
0
      break;
383
1.07k
    case personal_id:
384
1.07k
      assert(personal_ == 0);
385
1.07k
      personal_ = w;
386
1.07k
      break;
387
1.07k
    case session_id:
388
1.07k
      assert(session_ == 0);
389
1.07k
      session_ = w;
390
1.07k
      break;
391
1.07k
    case personal_repl_id:
392
1.07k
      assert(repl_ == 0);
393
1.07k
      repl_ = w;
394
1.07k
      break;
395
4.30k
    case none_id:
396
4.30k
      break;
397
7.51k
    }
398
399
7.51k
    return no_err;
400
7.51k
  }
401
402
  //////////////////////////////////////////////////////////////////////
403
  //
404
  // Config Notifier
405
  //
406
407
  struct UpdateMember {
408
    const char * name;
409
    enum Type {String, Int, Bool, Add, Rem, RemAll};
410
    Type type;
411
    union Fun {
412
      typedef PosibErr<void> (*WithStr )(SpellerImpl *, const char *);
413
      typedef PosibErr<void> (*WithInt )(SpellerImpl *, int);
414
      typedef PosibErr<void> (*WithBool)(SpellerImpl *, bool);
415
      WithStr  with_str;
416
      WithInt  with_int;
417
      WithBool with_bool;
418
0
      Fun() {}
419
2
      Fun(WithStr  m) : with_str (m) {}
420
6
      Fun(WithInt  m) : with_int (m) {}
421
10
      Fun(WithBool m) : with_bool(m) {}
422
      PosibErr<void> call(SpellerImpl * m, const char * val) const 
423
0
        {return (*with_str) (m,val);}
424
      PosibErr<void> call(SpellerImpl * m, int val)          const 
425
0
        {return (*with_int) (m,val);}
426
      PosibErr<void> call(SpellerImpl * m, bool val)         const 
427
0
        {return (*with_bool)(m,val);}
428
    } fun;
429
    typedef SpellerImpl::ConfigNotifier CN;
430
  };
431
432
  template <typename T>
433
  PosibErr<void> callback(SpellerImpl * m, const KeyInfo * ki, T value, 
434
                          UpdateMember::Type t);
435
  
436
  class SpellerImpl::ConfigNotifier : public Notifier {
437
  private:
438
    SpellerImpl * speller_;
439
  public:
440
    ConfigNotifier(SpellerImpl * m) 
441
1.07k
      : speller_(m) 
442
1.07k
    {}
443
444
0
    PosibErr<void> item_updated(const KeyInfo * ki, int value) {
445
0
      return callback(speller_, ki, value, UpdateMember::Int);
446
0
    }
447
0
    PosibErr<void> item_updated(const KeyInfo * ki, bool value) {
448
0
      return callback(speller_, ki, value, UpdateMember::Bool);
449
0
    }
450
0
    PosibErr<void> item_updated(const KeyInfo * ki, ParmStr value) {
451
0
      return callback(speller_, ki, value, UpdateMember::String);
452
0
    }
453
454
0
    static PosibErr<void> ignore(SpellerImpl * m, int value) {
455
0
      m->ignore_count = value;
456
0
      return no_err;
457
0
    }
458
0
    static PosibErr<void> ignore_accents(SpellerImpl * m, bool value) {
459
0
      return no_err;
460
0
    }
461
0
    static PosibErr<void> ignore_case(SpellerImpl * m, bool value) {
462
0
      m->s_cmp.case_insensitive = value;
463
0
      m->s_cmp_begin.case_insensitive = value;
464
0
      m->s_cmp_middle.case_insensitive = value;
465
0
      m->s_cmp_end.case_insensitive = value;
466
0
      return no_err;
467
0
    }
468
0
    static PosibErr<void> ignore_repl(SpellerImpl * m, bool value) {
469
      
470
0
      m->ignore_repl = value;
471
0
      return no_err;
472
0
    }
473
0
    static PosibErr<void> save_repl(SpellerImpl * m, bool value) {
474
0
      // FIXME
475
0
      // m->save_on_saveall(DataSet::Id(&m->personal_repl()), value);
476
0
      abort(); return no_err;
477
0
    }
478
0
    static PosibErr<void> sug_mode(SpellerImpl * m, const char * mode) {
479
0
      RET_ON_ERR(m->suggest_->set_mode(mode));
480
0
      RET_ON_ERR(m->intr_suggest_->set_mode(mode));
481
0
      return no_err;
482
0
    }
483
0
    static PosibErr<void> run_together(SpellerImpl * m, bool value) {
484
0
      m->unconditional_run_together_ = value;
485
0
      m->run_together = m->unconditional_run_together_;
486
0
      return no_err;
487
0
    }
488
0
    static PosibErr<void> run_together_limit(SpellerImpl * m, int value) {
489
0
      if (value > 8) {
490
0
        m->config()->replace("run-together-limit", "8");
491
        // will loop back
492
0
      } else {
493
0
        m->run_together_limit_ = value;
494
0
      }
495
0
      return no_err;
496
0
    }
497
0
    static PosibErr<void> run_together_min(SpellerImpl * m, int value) {
498
0
      m->run_together_min_ = value;
499
0
      return no_err;
500
0
    }
501
0
    static PosibErr<void> camel_case(SpellerImpl * m, bool value) {
502
0
      m->camel_case_ = value;
503
0
      return no_err;
504
0
    }
505
  };
506
507
  static UpdateMember update_members[] = 
508
  {
509
    {"ignore",         UpdateMember::Int,     UpdateMember::CN::ignore}
510
    ,{"ignore-accents",UpdateMember::Bool,    UpdateMember::CN::ignore_accents}
511
    ,{"ignore-case",   UpdateMember::Bool,    UpdateMember::CN::ignore_case}
512
    ,{"ignore-repl",   UpdateMember::Bool,    UpdateMember::CN::ignore_repl}
513
    //,{"save-repl",     UpdateMember::Bool,    UpdateMember::CN::save_repl}
514
    ,{"sug-mode",      UpdateMember::String,  UpdateMember::CN::sug_mode}
515
    ,{"run-together",  
516
        UpdateMember::Bool,    
517
        UpdateMember::CN::run_together}
518
    ,{"run-together-limit",  
519
        UpdateMember::Int,    
520
        UpdateMember::CN::run_together_limit}
521
    ,{"run-together-min",  
522
        UpdateMember::Int,    
523
        UpdateMember::CN::run_together_min}
524
    ,{"camel-case",
525
        UpdateMember::Bool,    
526
        UpdateMember::CN::camel_case}
527
  };
528
529
  template <typename T>
530
  PosibErr<void> callback(SpellerImpl * m, const KeyInfo * ki, T value, 
531
                          UpdateMember::Type t) 
532
0
  {
533
0
    const UpdateMember * i
534
0
      = update_members;
535
0
    const UpdateMember * end   
536
0
      = i + sizeof(update_members)/sizeof(UpdateMember);
537
0
    while (i != end) {
538
0
      if (strcmp(ki->name, i->name) == 0) {
539
0
        if (i->type == t) {
540
0
          RET_ON_ERR(i->fun.call(m, value));
541
0
          break;
542
0
        }
543
0
      }
544
0
      ++i;
545
0
    }
546
0
    return no_err;
547
0
  }
Unexecuted instantiation: acommon::PosibErr<void> aspeller::callback<bool>(aspeller::SpellerImpl*, acommon::KeyInfo const*, bool, aspeller::UpdateMember::Type)
Unexecuted instantiation: acommon::PosibErr<void> aspeller::callback<int>(aspeller::SpellerImpl*, acommon::KeyInfo const*, int, aspeller::UpdateMember::Type)
Unexecuted instantiation: acommon::PosibErr<void> aspeller::callback<acommon::ParmString>(aspeller::SpellerImpl*, acommon::KeyInfo const*, acommon::ParmString, aspeller::UpdateMember::Type)
548
549
  //////////////////////////////////////////////////////////////////////
550
  //
551
  // SpellerImpl inititization members
552
  //
553
554
  SpellerImpl::SpellerImpl() 
555
1.10k
    : Speller(0) /* FIXME */, ignore_repl(true), 
556
1.10k
      dicts_(0), personal_(0), session_(0), repl_(0), main_(0)
557
1.10k
  {}
558
559
  inline PosibErr<void> add_dicts(SpellerImpl * sp, DictList & d)
560
1.09k
  {
561
5.38k
    for (;!d.empty(); d.pop())
562
4.28k
    {
563
4.28k
      if (!sp->locate(d.last()->id())) {
564
4.28k
        RET_ON_ERR(sp->add_dict(new SpellerDict(d.last())));
565
4.28k
      }
566
4.28k
    }
567
1.09k
    return no_err;
568
1.09k
  }
569
570
1.10k
  PosibErr<void> SpellerImpl::setup(Config * c) {
571
1.10k
    assert (config_ == 0);
572
1.10k
    config_.reset(c);
573
574
1.10k
    ignore_repl = config_->retrieve_bool("ignore-repl");
575
1.10k
    ignore_count = config_->retrieve_int("ignore");
576
577
1.10k
    DictList to_add;
578
1.10k
    RET_ON_ERR(add_data_set(config_->retrieve("master-path"), *config_, &to_add, this));
579
1.08k
    RET_ON_ERR(add_dicts(this, to_add));
580
581
1.08k
    s_cmp.lang = lang_;
582
1.08k
    s_cmp.case_insensitive = config_->retrieve_bool("ignore-case");
583
584
1.08k
    s_cmp_begin.lang = lang_; 
585
1.08k
    s_cmp_begin.case_insensitive = s_cmp.case_insensitive;
586
1.08k
    s_cmp_begin.end = false;
587
588
1.08k
    s_cmp_middle.lang = lang_;
589
1.08k
    s_cmp_middle.case_insensitive = s_cmp.case_insensitive;
590
1.08k
    s_cmp_middle.begin = false;
591
1.08k
    s_cmp_middle.end   = false;
592
593
1.08k
    s_cmp_end.lang = lang_;
594
1.08k
    s_cmp_end.case_insensitive = s_cmp.case_insensitive;
595
1.08k
    s_cmp_end.begin = false;
596
597
1.08k
    StringList extra_dicts;
598
1.08k
    config_->retrieve_list("extra-dicts", &extra_dicts);
599
1.08k
    StringListEnumeration els = extra_dicts.elements_obj();
600
1.08k
    const char * dict_name;
601
1.09k
    while ( (dict_name = els.next()) != 0) {
602
9
      RET_ON_ERR(add_data_set(dict_name,*config_, &to_add, this));
603
8
      RET_ON_ERR(add_dicts(this, to_add));
604
8
    }
605
606
1.08k
    bool use_other_dicts = config_->retrieve_bool("use-other-dicts");
607
608
1.08k
    if (use_other_dicts && !personal_)
609
1.07k
    {
610
1.07k
      Dictionary * temp;
611
1.07k
      temp = new_default_writable_dict(*config_);
612
1.07k
      PosibErrBase pe = temp->load(config_->retrieve("personal-path"),*config_);
613
1.07k
      if (pe.has_err(cant_read_file))
614
1.07k
        temp->set_check_lang(lang_name(), *config_);
615
0
      else if (pe.has_err())
616
0
        return pe;
617
1.07k
      RET_ON_ERR(add_dict(new SpellerDict(temp, *config_, personal_id)));
618
1.07k
    }
619
    
620
1.08k
    if (use_other_dicts && !session_)
621
1.07k
    {
622
1.07k
      Dictionary * temp;
623
1.07k
      temp = new_default_writable_dict(*config_);
624
1.07k
      temp->set_check_lang(lang_name(), *config_);
625
1.07k
      RET_ON_ERR(add_dict(new SpellerDict(temp, *config_, session_id)));
626
1.07k
    }
627
     
628
1.08k
    if (use_other_dicts && !repl_)
629
1.07k
    {
630
1.07k
      ReplacementDict * temp = new_default_replacement_dict(*config_);
631
1.07k
      PosibErrBase pe = temp->load(config_->retrieve("repl-path"),*config_);
632
1.07k
      if (pe.has_err(cant_read_file))
633
1.07k
        temp->set_check_lang(lang_name(), *config_);
634
0
      else if (pe.has_err())
635
0
        return pe;
636
1.07k
      RET_ON_ERR(add_dict(new SpellerDict(temp, *config_, personal_repl_id)));
637
1.07k
    }
638
639
1.08k
    StringList wordlist_files;
640
1.08k
    config_->retrieve_list("wordlists", &wordlist_files);
641
1.08k
    if (!wordlist_files.empty()) {
642
22
      Dictionary * dict = session_;
643
22
      if (!dict) {
644
16
        dict = new_default_writable_dict(*config_);
645
16
        dict->set_check_lang(lang_name(), *config_);
646
16
        RET_ON_ERR(add_dict(new SpellerDict(dict, *config_)));
647
16
      }
648
22
      const char * fn;
649
22
      StringListEnumeration els = wordlist_files.elements_obj();
650
44
      while ( (fn = els.next()) != 0) {
651
22
        FStream f;
652
22
        RET_ON_ERR(f.open(fn, "r"));
653
22
        IstreamEnumeration els(f);
654
22
        WordListIterator wl_itr(&els, lang_, 0);
655
22
        wl_itr.init_plain(*config_);
656
22
        for (;;) {
657
22
          PosibErr<bool> pe = wl_itr.adv();
658
22
          if (pe.has_err())
659
0
            return pe.with_file(fn);
660
22
          if (!pe.data) break;
661
0
          PosibErr<void> pev = dict->add(wl_itr->word);
662
0
          if (pev.has_err())
663
0
            return pev.with_file(fn);
664
0
        }
665
22
      }
666
22
    }
667
668
1.08k
    const char * sys_enc = lang_->charmap();
669
1.08k
    ConfigConvKey user_enc = config_->retrieve_value("encoding");
670
1.08k
    if (user_enc.val == "none") {
671
870
      config_->replace("encoding", sys_enc);
672
870
      user_enc = sys_enc;
673
870
    }
674
675
1.08k
    PosibErr<Convert *> conv;
676
1.08k
    conv = new_convert(*c, user_enc, sys_enc, NormFrom);
677
1.08k
    if (conv.has_err()) return conv;
678
1.07k
    to_internal_.reset(conv);
679
1.07k
    conv = new_convert(*c, sys_enc, user_enc, NormTo);
680
1.07k
    if (conv.has_err()) return conv;
681
1.07k
    from_internal_.reset(conv);
682
683
1.07k
    unconditional_run_together_ = config_->retrieve_bool("run-together");
684
1.07k
    run_together = unconditional_run_together_;
685
    
686
1.07k
    run_together_limit_  = config_->retrieve_int("run-together-limit");
687
1.07k
    if (run_together_limit_ > 8) {
688
0
      config_->replace("run-together-limit", "8");
689
0
      run_together_limit_ = 8;
690
0
    }
691
1.07k
    run_together_min_    = config_->retrieve_int("run-together-min");
692
693
1.07k
    camel_case_ = config_->retrieve_bool("camel-case");
694
695
1.07k
    config_->add_notifier(new ConfigNotifier(this));
696
697
1.07k
    config_->set_attached(true);
698
699
1.07k
    affix_info = lang_->affix();
700
701
    //
702
    // setup word set lists
703
    //
704
705
1.07k
    typedef Vector<SpellerDict *> AllWS; AllWS all_ws;
706
8.50k
    for (SpellerDict * i = dicts_; i; i = i->next) {
707
7.43k
      if (i->dict->basic_type == Dict::basic_dict ||
708
5.30k
          i->dict->basic_type == Dict::replacement_dict) {
709
5.30k
        all_ws.push_back(i);
710
5.30k
      }
711
7.43k
    }
712
    
713
1.07k
    const std::type_info * ti = 0;
714
8.51k
    while (!all_ws.empty())
715
7.44k
    {
716
7.44k
      AllWS::iterator i0 = all_ws.end();
717
7.44k
      int max = -2;
718
7.44k
      AllWS::iterator i = all_ws.begin();
719
28.5k
      for (; i != all_ws.end(); ++i)
720
21.1k
      {
721
21.1k
        const Dictionary * ws = (*i)->dict;
722
21.1k
        if (ti && *ti != typeid(*ws)) continue;
723
12.7k
        if ((int)ws->size() > max) {max = ws->size(); i0 = i;}
724
12.7k
      }
725
726
7.44k
      if (i0 == all_ws.end()) {ti = 0; continue;}
727
728
5.30k
      SpellerDict * cur = *i0;
729
730
5.30k
      all_ws.erase(i0);
731
732
5.30k
      ti = &typeid(*cur->dict);
733
734
5.30k
      if (cur->use_to_check) {
735
4.23k
        check_ws.push_back(cur->dict);
736
4.23k
        if (cur->dict->affix_compressed) affix_ws.push_back(cur->dict);
737
4.23k
      }
738
5.30k
      if (cur->use_to_suggest) {
739
5.29k
        suggest_ws.push_back(cur->dict);
740
5.29k
        if (cur->dict->affix_compressed) suggest_affix_ws.push_back(cur->dict);
741
5.29k
      }
742
5.30k
    }
743
1.07k
    fast_scan   = suggest_ws.front()->fast_scan;
744
1.07k
    fast_lookup = suggest_ws.front()->fast_lookup;
745
1.07k
    have_soundslike = lang_->have_soundslike();
746
1.07k
    have_repl = lang_->have_repl();
747
1.07k
    invisible_soundslike = suggest_ws.front()->invisible_soundslike;
748
1.07k
    soundslike_root_only = suggest_ws.front()->soundslike_root_only;
749
1.07k
    affix_compress = !affix_ws.empty();
750
751
    //
752
    // Setup suggest
753
    //
754
755
1.07k
    PosibErr<Suggest *> pe;
756
1.07k
    pe = new_default_suggest(this);
757
1.07k
    if (pe.has_err()) return pe;
758
1.07k
    suggest_.reset(pe.data);
759
1.07k
    pe = new_default_suggest(this);
760
1.07k
    if (pe.has_err()) return pe;
761
1.07k
    intr_suggest_.reset(pe.data);
762
763
1.07k
    return no_err;
764
1.07k
  }
765
766
  //////////////////////////////////////////////////////////////////////
767
  //
768
  // SpellerImpl destrution members
769
  //
770
771
1.10k
  SpellerImpl::~SpellerImpl() {
772
8.62k
    while (dicts_) {
773
7.51k
      SpellerDict * next = dicts_->next;
774
7.51k
      delete dicts_;
775
7.51k
      dicts_ = next;
776
7.51k
    }
777
1.10k
  }
778
779
  //////////////////////////////////////////////////////////////////////
780
  //
781
  // SpellerImple setup tokenizer method
782
  //
783
784
  void SpellerImpl::setup_tokenizer(Tokenizer * tok)
785
1.05k
  {
786
270k
    for (int i = 0; i != 256; ++i) 
787
269k
    {
788
269k
      tok->char_type_[i].word   = lang_->is_alpha(i);
789
269k
      tok->char_type_[i].begin  = lang_->special(i).begin;
790
269k
      tok->char_type_[i].middle = lang_->special(i).middle;
791
269k
      tok->char_type_[i].end    = lang_->special(i).end;
792
269k
    }
793
1.05k
    tok->conv_ = to_internal_;
794
1.05k
  }
795
796
797
  //////////////////////////////////////////////////////////////////////
798
  //
799
  //
800
  //
801
802
  SpellerDict::SpellerDict(Dict * d) 
803
4.28k
    : dict(d), special_id(none_id), next(0) 
804
4.28k
  {
805
4.28k
    switch (dict->basic_type) {
806
2.14k
    case Dict::basic_dict:
807
2.14k
      use_to_check = true;
808
2.14k
      use_to_suggest = true;
809
2.14k
      break;
810
0
    case Dict::replacement_dict:
811
0
      use_to_check = false;
812
0
      use_to_suggest = true;
813
2.14k
    case Dict::multi_dict:
814
2.14k
      break;
815
0
    default:
816
0
      abort();
817
4.28k
    }
818
4.28k
    save_on_saveall = false;
819
4.28k
  }
820
821
  SpellerDict::SpellerDict(Dict * w, const Config & c, SpecialId id)
822
3.22k
    : next(0) 
823
3.22k
  {
824
3.22k
    dict = w;
825
3.22k
    special_id = id;
826
3.22k
    switch (id) {
827
0
    case main_id:
828
0
      if (dict->basic_type == Dict::basic_dict) {
829
830
0
        use_to_check    = true;
831
0
        use_to_suggest  = true;
832
0
        save_on_saveall = false;
833
834
0
      } else if (dict->basic_type == Dict::replacement_dict) {
835
        
836
0
        use_to_check    = false;
837
0
        use_to_suggest  = false;
838
0
        save_on_saveall = false;
839
        
840
0
      } else {
841
        
842
0
        abort();
843
        
844
0
      }
845
0
      break;
846
1.07k
    case personal_id:
847
1.07k
      use_to_check = true;
848
1.07k
      use_to_suggest = true;
849
1.07k
      save_on_saveall = true;
850
1.07k
      break;
851
1.07k
    case session_id:
852
1.07k
      use_to_check = true;
853
1.07k
      use_to_suggest = true;
854
1.07k
      save_on_saveall = false;
855
1.07k
      break;
856
1.07k
    case personal_repl_id:
857
1.07k
      use_to_check = false;
858
1.07k
      use_to_suggest = true;
859
1.07k
      save_on_saveall = c.retrieve_bool("save-repl");
860
1.07k
      break;
861
16
    case none_id:
862
16
      break;
863
3.22k
    }
864
3.22k
  }
865
866
  extern "C"
867
  Speller * libaspell_speller_default_LTX_new_speller_class(SpellerLtHandle)
868
1.10k
  {
869
1.10k
    return new SpellerImpl();
870
1.10k
  }
871
}
872