Coverage Report

Created: 2025-05-21 07:12

/src/aspell/modules/speller/default/speller_impl.cpp
Line
Count
Source (jump to first uncovered line)
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
4.29k
  const char * SpellerImpl::lang_name() const {
34
4.29k
    return lang_->name();
35
4.29k
  }
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
10.9k
  {
134
10.9k
    return &suggest_->suggest(word);
135
10.9k
  }
136
137
  bool SpellerImpl::check_simple (ParmString w, WordEntry & w0) 
138
491k
  {
139
491k
    w0.clear(); // FIXME: is this necessary?
140
491k
    const char * x = w;
141
2.80M
    while (*x != '\0' && (x-w) < static_cast<int>(ignore_count)) ++x;
142
491k
    if (*x == '\0') {w0.word = w; return true;}
143
384k
    WS::const_iterator i   = check_ws.begin();
144
384k
    WS::const_iterator end = check_ws.end();
145
1.51M
    do {
146
1.51M
      if ((*i)->lookup(w, &s_cmp, w0)) return true;
147
1.50M
      ++i;
148
1.50M
    } while (i != end);
149
377k
    return false;
150
384k
  };
151
152
  bool SpellerImpl::check_affix(ParmString word, CheckInfo & ci, GuessInfo * gi)
153
491k
  {
154
491k
    WordEntry w;
155
491k
    bool res = check_simple(word, w);
156
491k
    if (res) {ci.word = w.word; return true;}
157
377k
    if (affix_compress) {
158
6.09k
      res = lang_->affix()->affix_check(LookupInfo(this, LookupInfo::Word), word, ci, 0);
159
6.09k
      if (res) return true;
160
6.09k
    }
161
377k
    if (affix_info && gi) {
162
230k
      lang_->affix()->affix_check(LookupInfo(this, LookupInfo::Guess), word, ci, gi);
163
230k
    }
164
377k
    return false;
165
377k
  }
166
167
  inline bool SpellerImpl::check_single(char * word, /* it WILL modify word */
168
                                        bool try_uppercase,
169
                                        CheckInfo & ci, GuessInfo * gi)
170
491k
  {
171
491k
    bool res = check_affix(word, ci, gi);
172
491k
    if (res) return true;
173
376k
    if (!try_uppercase) return false;
174
721
    char t = *word;
175
721
    *word = lang_->to_title(t);
176
721
    res = check_affix(word, ci, gi);
177
721
    *word = t;
178
721
    if (res) return true;
179
721
    return false;
180
721
  }
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
219k
  {
189
219k
    if (ci >= ci_end) return NULL;
190
219k
    clear_check_info(*ci);
191
219k
    bool res = check_single(word, try_uppercase, *ci, gi);
192
219k
    if (res) return ci;
193
171k
    if (run_together_limit <= 1) return NULL;
194
17.3k
    enum {Yes, No, Unknown} is_title = try_uppercase ? Yes : Unknown;
195
17.3k
    for (char * i = word + run_together_min_; 
196
279k
         i <= word_end - run_together_min_;
197
262k
         ++i) 
198
271k
    {
199
271k
      char t = *i;
200
271k
      *i = '\0';
201
271k
      clear_check_info(*ci);
202
271k
      res = check_single(word, try_uppercase, *ci, gi);
203
271k
      if (!res) {*i = t; continue;}
204
65.5k
      if (is_title == Unknown)
205
15.7k
        is_title = lang_->case_pattern(word) == FirstUpper ? Yes : No;
206
65.5k
      *i = t;
207
65.5k
      CheckInfo * ci_last = check_runtogether(i, word_end, is_title == Yes, run_together_limit - 1, ci + 1, ci_end, 0);
208
65.5k
      if (ci_last) {
209
9.00k
        ci->compound = true;
210
9.00k
        ci->next = ci + 1;
211
9.00k
        return ci_last;
212
9.00k
      }
213
65.5k
    }
214
8.32k
    return NULL;
215
17.3k
  }
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
115k
  {
224
115k
    clear_check_info(*ci);
225
115k
    bool res = check_runtogether(word, word_end, try_uppercase, run_together_limit, ci, ci_end, gi);
226
115k
    if (res) return true;
227
    
228
80.5k
    CompoundWord cw = lang_->split_word(word, word_end - word, camel_case_);
229
80.5k
    if (cw.single()) return false;
230
24.0k
    bool ok = true;
231
24.0k
    CheckInfo * ci_prev = NULL;
232
38.9k
    do {
233
38.9k
      unsigned len = cw.word_len();
234
      
235
38.9k
      char save = word[len];
236
38.9k
      word[len] = '\0';
237
38.9k
      CheckInfo * ci_last = check_runtogether(word, word + len, try_uppercase, run_together_limit, ci, ci_end, gi);
238
38.9k
      bool found = ci_last;
239
38.9k
      word[len] = save;
240
241
38.9k
      if (!found) {
242
25.1k
        if (cpi) {
243
3.96k
          ci_last = ci;
244
3.96k
          ok = false;
245
3.96k
          ci->word.str = word;
246
3.96k
          ci->word.len = len;
247
3.96k
          ci->incorrect = true;
248
3.96k
          cpi->incorrect_count++;
249
3.96k
          if (!cpi->first_incorrect)
250
1.49k
            cpi->first_incorrect = ci;
251
21.1k
        } else {
252
21.1k
          return false;
253
21.1k
        }
254
25.1k
      }
255
256
17.8k
      if (cpi)
257
5.57k
        cpi->count++;
258
      
259
17.8k
      if (ci_prev) {
260
7.89k
        ci_prev->compound = true;
261
7.89k
        ci_prev->next = ci;
262
7.89k
      }
263
264
17.8k
      ci_prev = ci_last;
265
17.8k
      ci = ci_last + 1;
266
17.8k
      if (ci >= ci_end) {
267
428
        if (cpi) cpi->count = 0;
268
428
        return false;
269
428
      }
270
      
271
17.3k
      word = word + cw.rest_offset();
272
17.3k
      cw = lang_->split_word(cw.rest, cw.rest_len(), camel_case_);
273
      
274
17.3k
    } while (!cw.empty());
275
    
276
2.44k
    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
24.0k
  }
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
12.7k
  {
354
35.4k
    for (const SpellerDict * i = dicts_; i; i = i->next)
355
22.7k
      if (i->dict->id() == id) return i;
356
12.7k
    return 0;
357
12.7k
  }
358
359
  PosibErr<void> SpellerImpl::add_dict(SpellerDict * wc)
360
5.95k
  {
361
5.95k
    Dict * w = wc->dict;
362
5.95k
    assert(locate(w->id()) == 0);
363
364
5.95k
    if (!lang_) {
365
863
      lang_.copy(w->lang());
366
863
      config_->replace("lang", lang_name());
367
863
      config_->replace("language-tag", lang_name());
368
5.09k
    } else {
369
5.09k
      if (strcmp(lang_->name(), w->lang()->name()) != 0)
370
1
        return make_err(mismatched_language, lang_->name(), w->lang()->name());
371
5.09k
    }
372
373
    // add to master list
374
5.95k
    wc->next = dicts_;
375
5.95k
    dicts_ = wc;
376
377
    // check if it has a special_id and act accordingly
378
5.95k
    switch (wc->special_id) {
379
0
    case main_id:
380
0
      assert(main_ == 0);
381
0
      main_ = w;
382
0
      break;
383
854
    case personal_id:
384
854
      assert(personal_ == 0);
385
854
      personal_ = w;
386
854
      break;
387
854
    case session_id:
388
854
      assert(session_ == 0);
389
854
      session_ = w;
390
854
      break;
391
854
    case personal_repl_id:
392
854
      assert(repl_ == 0);
393
854
      repl_ = w;
394
854
      break;
395
3.39k
    case none_id:
396
3.39k
      break;
397
5.95k
    }
398
399
5.95k
    return no_err;
400
5.95k
  }
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
851
      : speller_(m) 
442
851
    {}
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
881
    : Speller(0) /* FIXME */, ignore_repl(true), 
556
881
      dicts_(0), personal_(0), session_(0), repl_(0), main_(0)
557
881
  {}
558
559
  inline PosibErr<void> add_dicts(SpellerImpl * sp, DictList & d)
560
865
  {
561
4.25k
    for (;!d.empty(); d.pop())
562
3.38k
    {
563
3.38k
      if (!sp->locate(d.last()->id())) {
564
3.38k
        RET_ON_ERR(sp->add_dict(new SpellerDict(d.last())));
565
3.38k
      }
566
3.38k
    }
567
864
    return no_err;
568
865
  }
569
570
881
  PosibErr<void> SpellerImpl::setup(Config * c) {
571
881
    assert (config_ == 0);
572
881
    config_.reset(c);
573
574
881
    ignore_repl = config_->retrieve_bool("ignore-repl");
575
881
    ignore_count = config_->retrieve_int("ignore");
576
577
881
    DictList to_add;
578
881
    RET_ON_ERR(add_data_set(config_->retrieve("master-path"), *config_, &to_add, this));
579
863
    RET_ON_ERR(add_dicts(this, to_add));
580
581
863
    s_cmp.lang = lang_;
582
863
    s_cmp.case_insensitive = config_->retrieve_bool("ignore-case");
583
584
863
    s_cmp_begin.lang = lang_; 
585
863
    s_cmp_begin.case_insensitive = s_cmp.case_insensitive;
586
863
    s_cmp_begin.end = false;
587
588
863
    s_cmp_middle.lang = lang_;
589
863
    s_cmp_middle.case_insensitive = s_cmp.case_insensitive;
590
863
    s_cmp_middle.begin = false;
591
863
    s_cmp_middle.end   = false;
592
593
863
    s_cmp_end.lang = lang_;
594
863
    s_cmp_end.case_insensitive = s_cmp.case_insensitive;
595
863
    s_cmp_end.begin = false;
596
597
863
    StringList extra_dicts;
598
863
    config_->retrieve_list("extra-dicts", &extra_dicts);
599
863
    StringListEnumeration els = extra_dicts.elements_obj();
600
863
    const char * dict_name;
601
864
    while ( (dict_name = els.next()) != 0) {
602
3
      RET_ON_ERR(add_data_set(dict_name,*config_, &to_add, this));
603
2
      RET_ON_ERR(add_dicts(this, to_add));
604
2
    }
605
606
861
    bool use_other_dicts = config_->retrieve_bool("use-other-dicts");
607
608
861
    if (use_other_dicts && !personal_)
609
854
    {
610
854
      Dictionary * temp;
611
854
      temp = new_default_writable_dict(*config_);
612
854
      PosibErrBase pe = temp->load(config_->retrieve("personal-path"),*config_);
613
854
      if (pe.has_err(cant_read_file))
614
854
        temp->set_check_lang(lang_name(), *config_);
615
0
      else if (pe.has_err())
616
0
        return pe;
617
854
      RET_ON_ERR(add_dict(new SpellerDict(temp, *config_, personal_id)));
618
854
    }
619
    
620
861
    if (use_other_dicts && !session_)
621
854
    {
622
854
      Dictionary * temp;
623
854
      temp = new_default_writable_dict(*config_);
624
854
      temp->set_check_lang(lang_name(), *config_);
625
854
      RET_ON_ERR(add_dict(new SpellerDict(temp, *config_, session_id)));
626
854
    }
627
     
628
861
    if (use_other_dicts && !repl_)
629
854
    {
630
854
      ReplacementDict * temp = new_default_replacement_dict(*config_);
631
854
      PosibErrBase pe = temp->load(config_->retrieve("repl-path"),*config_);
632
854
      if (pe.has_err(cant_read_file))
633
854
        temp->set_check_lang(lang_name(), *config_);
634
0
      else if (pe.has_err())
635
0
        return pe;
636
854
      RET_ON_ERR(add_dict(new SpellerDict(temp, *config_, personal_repl_id)));
637
854
    }
638
639
861
    StringList wordlist_files;
640
861
    config_->retrieve_list("wordlists", &wordlist_files);
641
861
    if (!wordlist_files.empty()) {
642
7
      Dictionary * dict = session_;
643
7
      if (!dict) {
644
7
        dict = new_default_writable_dict(*config_);
645
7
        dict->set_check_lang(lang_name(), *config_);
646
7
        RET_ON_ERR(add_dict(new SpellerDict(dict, *config_)));
647
7
      }
648
7
      const char * fn;
649
7
      StringListEnumeration els = wordlist_files.elements_obj();
650
14
      while ( (fn = els.next()) != 0) {
651
7
        FStream f;
652
7
        RET_ON_ERR(f.open(fn, "r"));
653
7
        IstreamEnumeration els(f);
654
7
        WordListIterator wl_itr(&els, lang_, 0);
655
7
        wl_itr.init_plain(*config_);
656
7
        for (;;) {
657
7
          PosibErr<bool> pe = wl_itr.adv();
658
7
          if (pe.has_err())
659
0
            return pe.with_file(fn);
660
7
          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
7
      }
666
7
    }
667
668
861
    const char * sys_enc = lang_->charmap();
669
861
    ConfigConvKey user_enc = config_->retrieve_value("encoding");
670
861
    if (user_enc.val == "none") {
671
737
      config_->replace("encoding", sys_enc);
672
737
      user_enc = sys_enc;
673
737
    }
674
675
861
    PosibErr<Convert *> conv;
676
861
    conv = new_convert(*c, user_enc, sys_enc, NormFrom);
677
861
    if (conv.has_err()) return conv;
678
851
    to_internal_.reset(conv);
679
851
    conv = new_convert(*c, sys_enc, user_enc, NormTo);
680
851
    if (conv.has_err()) return conv;
681
851
    from_internal_.reset(conv);
682
683
851
    unconditional_run_together_ = config_->retrieve_bool("run-together");
684
851
    run_together = unconditional_run_together_;
685
    
686
851
    run_together_limit_  = config_->retrieve_int("run-together-limit");
687
851
    if (run_together_limit_ > 8) {
688
0
      config_->replace("run-together-limit", "8");
689
0
      run_together_limit_ = 8;
690
0
    }
691
851
    run_together_min_    = config_->retrieve_int("run-together-min");
692
693
851
    camel_case_ = config_->retrieve_bool("camel-case");
694
695
851
    config_->add_notifier(new ConfigNotifier(this));
696
697
851
    config_->set_attached(true);
698
699
851
    affix_info = lang_->affix();
700
701
    //
702
    // setup word set lists
703
    //
704
705
851
    typedef Vector<SpellerDict *> AllWS; AllWS all_ws;
706
6.72k
    for (SpellerDict * i = dicts_; i; i = i->next) {
707
5.87k
      if (i->dict->basic_type == Dict::basic_dict ||
708
5.87k
          i->dict->basic_type == Dict::replacement_dict) {
709
4.20k
        all_ws.push_back(i);
710
4.20k
      }
711
5.87k
    }
712
    
713
851
    const std::type_info * ti = 0;
714
6.75k
    while (!all_ws.empty())
715
5.90k
    {
716
5.90k
      AllWS::iterator i0 = all_ws.end();
717
5.90k
      int max = -2;
718
5.90k
      AllWS::iterator i = all_ws.begin();
719
22.6k
      for (; i != all_ws.end(); ++i)
720
16.7k
      {
721
16.7k
        const Dictionary * ws = (*i)->dict;
722
16.7k
        if (ti && *ti != typeid(*ws)) continue;
723
10.0k
        if ((int)ws->size() > max) {max = ws->size(); i0 = i;}
724
10.0k
      }
725
726
5.90k
      if (i0 == all_ws.end()) {ti = 0; continue;}
727
728
4.20k
      SpellerDict * cur = *i0;
729
730
4.20k
      all_ws.erase(i0);
731
732
4.20k
      ti = &typeid(*cur->dict);
733
734
4.20k
      if (cur->use_to_check) {
735
3.35k
        check_ws.push_back(cur->dict);
736
3.35k
        if (cur->dict->affix_compressed) affix_ws.push_back(cur->dict);
737
3.35k
      }
738
4.20k
      if (cur->use_to_suggest) {
739
4.20k
        suggest_ws.push_back(cur->dict);
740
4.20k
        if (cur->dict->affix_compressed) suggest_affix_ws.push_back(cur->dict);
741
4.20k
      }
742
4.20k
    }
743
851
    fast_scan   = suggest_ws.front()->fast_scan;
744
851
    fast_lookup = suggest_ws.front()->fast_lookup;
745
851
    have_soundslike = lang_->have_soundslike();
746
851
    have_repl = lang_->have_repl();
747
851
    invisible_soundslike = suggest_ws.front()->invisible_soundslike;
748
851
    soundslike_root_only = suggest_ws.front()->soundslike_root_only;
749
851
    affix_compress = !affix_ws.empty();
750
751
    //
752
    // Setup suggest
753
    //
754
755
851
    PosibErr<Suggest *> pe;
756
851
    pe = new_default_suggest(this);
757
851
    if (pe.has_err()) return pe;
758
847
    suggest_.reset(pe.data);
759
847
    pe = new_default_suggest(this);
760
847
    if (pe.has_err()) return pe;
761
847
    intr_suggest_.reset(pe.data);
762
763
847
    return no_err;
764
847
  }
765
766
  //////////////////////////////////////////////////////////////////////
767
  //
768
  // SpellerImpl destrution members
769
  //
770
771
881
  SpellerImpl::~SpellerImpl() {
772
6.83k
    while (dicts_) {
773
5.95k
      SpellerDict * next = dicts_->next;
774
5.95k
      delete dicts_;
775
5.95k
      dicts_ = next;
776
5.95k
    }
777
881
  }
778
779
  //////////////////////////////////////////////////////////////////////
780
  //
781
  // SpellerImple setup tokenizer method
782
  //
783
784
  void SpellerImpl::setup_tokenizer(Tokenizer * tok)
785
832
  {
786
213k
    for (int i = 0; i != 256; ++i) 
787
212k
    {
788
212k
      tok->char_type_[i].word   = lang_->is_alpha(i);
789
212k
      tok->char_type_[i].begin  = lang_->special(i).begin;
790
212k
      tok->char_type_[i].middle = lang_->special(i).middle;
791
212k
      tok->char_type_[i].end    = lang_->special(i).end;
792
212k
    }
793
832
    tok->conv_ = to_internal_;
794
832
  }
795
796
797
  //////////////////////////////////////////////////////////////////////
798
  //
799
  //
800
  //
801
802
  SpellerDict::SpellerDict(Dict * d) 
803
3.38k
    : dict(d), special_id(none_id), next(0) 
804
3.38k
  {
805
3.38k
    switch (dict->basic_type) {
806
1.69k
    case Dict::basic_dict:
807
1.69k
      use_to_check = true;
808
1.69k
      use_to_suggest = true;
809
1.69k
      break;
810
0
    case Dict::replacement_dict:
811
0
      use_to_check = false;
812
0
      use_to_suggest = true;
813
1.69k
    case Dict::multi_dict:
814
1.69k
      break;
815
0
    default:
816
0
      abort();
817
3.38k
    }
818
3.38k
    save_on_saveall = false;
819
3.38k
  }
820
821
  SpellerDict::SpellerDict(Dict * w, const Config & c, SpecialId id)
822
2.56k
    : next(0) 
823
2.56k
  {
824
2.56k
    dict = w;
825
2.56k
    special_id = id;
826
2.56k
    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
854
    case personal_id:
847
854
      use_to_check = true;
848
854
      use_to_suggest = true;
849
854
      save_on_saveall = true;
850
854
      break;
851
854
    case session_id:
852
854
      use_to_check = true;
853
854
      use_to_suggest = true;
854
854
      save_on_saveall = false;
855
854
      break;
856
854
    case personal_repl_id:
857
854
      use_to_check = false;
858
854
      use_to_suggest = true;
859
854
      save_on_saveall = c.retrieve_bool("save-repl");
860
854
      break;
861
7
    case none_id:
862
7
      break;
863
2.56k
    }
864
2.56k
  }
865
866
  extern "C"
867
  Speller * libaspell_speller_default_LTX_new_speller_class(SpellerLtHandle)
868
881
  {
869
881
    return new SpellerImpl();
870
881
  }
871
}
872