Coverage Report

Created: 2024-09-08 06:23

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