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 
lang_name() const33   const char * SpellerImpl::lang_name() const {
34     return lang_->name();
35   }
36 
37   //
38   // to lower
39   //
40 
to_lower(char * str)41   char * SpellerImpl::to_lower(char * str)
42   {
43     for (char * i = str; *i; ++i)
44       *i = lang_->to_lower(*i);
45     return str;
46   }
47 
48   //////////////////////////////////////////////////////////////////////
49   //
50   // Spell check methods
51   //
52 
add_to_personal(MutableString word)53   PosibErr<void> SpellerImpl::add_to_personal(MutableString word) {
54     if (!personal_) return no_err;
55     return personal_->add(word);
56   }
57 
add_to_session(MutableString word)58   PosibErr<void> SpellerImpl::add_to_session(MutableString word) {
59     if (!session_) return no_err;
60     return session_->add(word);
61   }
62 
clear_session()63   PosibErr<void> SpellerImpl::clear_session() {
64     if (!session_) return no_err;
65     return session_->clear();
66   }
67 
store_replacement(MutableString mis,MutableString cor)68   PosibErr<void> SpellerImpl::store_replacement(MutableString mis,
69                                                 MutableString cor)
70   {
71     return SpellerImpl::store_replacement(mis,cor,true);
72   }
73 
store_replacement(const String & mis,const String & cor,bool memory)74   PosibErr<void> SpellerImpl::store_replacement(const String & mis,
75                                                 const String & cor,
76                                                 bool memory)
77   {
78     if (ignore_repl) return no_err;
79     if (!repl_) return no_err;
80     String::size_type pos;
81     StackPtr<StringEnumeration> sugels(intr_suggest_->suggest(mis.c_str()).elements());
82     const char * first_word = sugels->next();
83     CheckInfo w1, w2;
84     String cor1, cor2;
85     String buf;
86     bool correct = false;
87     pos = cor.find(' ');
88     if (pos == String::npos) {
89       cor1 = cor;
90       correct = check_affix(cor, w1, 0);
91     } else {
92       cor1 = (String)cor.substr(0,pos);
93       ++pos;
94       while (pos < cor.size() && cor[pos] == ' ') ++pos;
95       cor2 = (String)cor.substr(pos);
96       correct = check_affix(cor1, w1, 0) && check_affix(cor2, w2, 0);
97     }
98     if (correct) {
99       String cor_orignal_casing(cor1);
100       if (!cor2.empty()) {
101         cor_orignal_casing += cor[pos-1];
102         cor_orignal_casing += cor2;
103       }
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       if (cor_orignal_casing.empty())
108         return no_err;
109       if (first_word == 0 || cor != first_word) {
110         lang().to_lower(buf, mis.str());
111         repl_->add_repl(buf, cor_orignal_casing);
112       }
113 
114       if (memory && prev_cor_repl_ == mis)
115         store_replacement(prev_mis_repl_, cor, false);
116 
117     } else { //!correct
118 
119       if (memory) {
120          if (prev_cor_repl_ != mis)
121           prev_mis_repl_ = mis;
122         prev_cor_repl_ = cor;
123        }
124     }
125     return no_err;
126   }
127 
128   //
129   // simple functions
130   //
131 
suggest(MutableString word)132   PosibErr<const WordList *> SpellerImpl::suggest(MutableString word)
133   {
134     return &suggest_->suggest(word);
135   }
136 
check_simple(ParmString w,WordEntry & w0)137   bool SpellerImpl::check_simple (ParmString w, WordEntry & w0)
138   {
139     w0.clear(); // FIXME: is this necessary?
140     const char * x = w;
141     while (*x != '\0' && (x-w) < static_cast<int>(ignore_count)) ++x;
142     if (*x == '\0') {w0.word = w; return true;}
143     WS::const_iterator i   = check_ws.begin();
144     WS::const_iterator end = check_ws.end();
145     do {
146       if ((*i)->lookup(w, &s_cmp, w0)) return true;
147       ++i;
148     } while (i != end);
149     return false;
150   };
151 
check_affix(ParmString word,CheckInfo & ci,GuessInfo * gi)152   bool SpellerImpl::check_affix(ParmString word, CheckInfo & ci, GuessInfo * gi)
153   {
154     WordEntry w;
155     bool res = check_simple(word, w);
156     if (res) {ci.word = w.word; return true;}
157     if (affix_compress) {
158       res = lang_->affix()->affix_check(LookupInfo(this, LookupInfo::Word), word, ci, 0);
159       if (res) return true;
160     }
161     if (affix_info && gi) {
162       lang_->affix()->affix_check(LookupInfo(this, LookupInfo::Guess), word, ci, gi);
163     }
164     return false;
165   }
166 
check_single(char * word,bool try_uppercase,CheckInfo & ci,GuessInfo * gi)167   inline bool SpellerImpl::check_single(char * word, /* it WILL modify word */
168                                         bool try_uppercase,
169                                         CheckInfo & ci, GuessInfo * gi)
170   {
171     bool res = check_affix(word, ci, gi);
172     if (res) return true;
173     if (!try_uppercase) return false;
174     char t = *word;
175     *word = lang_->to_title(t);
176     res = check_affix(word, ci, gi);
177     *word = t;
178     if (res) return true;
179     return false;
180   }
181 
check_runtogether(char * word,char * word_end,bool try_uppercase,unsigned run_together_limit,CheckInfo * ci,CheckInfo * ci_end,GuessInfo * gi)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   {
189     if (ci >= ci_end) return NULL;
190     clear_check_info(*ci);
191     bool res = check_single(word, try_uppercase, *ci, gi);
192     if (res) return ci;
193     if (run_together_limit <= 1) return NULL;
194     enum {Yes, No, Unknown} is_title = try_uppercase ? Yes : Unknown;
195     for (char * i = word + run_together_min_;
196          i <= word_end - run_together_min_;
197          ++i)
198     {
199       char t = *i;
200       *i = '\0';
201       clear_check_info(*ci);
202       res = check_single(word, try_uppercase, *ci, gi);
203       if (!res) {*i = t; continue;}
204       if (is_title == Unknown)
205         is_title = lang_->case_pattern(word) == FirstUpper ? Yes : No;
206       *i = t;
207       CheckInfo * ci_last = check_runtogether(i, word_end, is_title == Yes, run_together_limit - 1, ci + 1, ci_end, 0);
208       if (ci_last) {
209         ci->compound = true;
210         ci->next = ci + 1;
211         return ci_last;
212       }
213     }
214     return NULL;
215   }
216 
check(char * word,char * word_end,bool try_uppercase,unsigned run_together_limit,CheckInfo * ci,CheckInfo * ci_end,GuessInfo * gi,CompoundInfo * cpi)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   {
224     clear_check_info(*ci);
225     bool res = check_runtogether(word, word_end, try_uppercase, run_together_limit, ci, ci_end, gi);
226     if (res) return true;
227 
228     CompoundWord cw = lang_->split_word(word, word_end - word, camel_case_);
229     if (cw.single()) return false;
230     bool ok = true;
231     CheckInfo * ci_prev = NULL;
232     do {
233       unsigned len = cw.word_len();
234 
235       char save = word[len];
236       word[len] = '\0';
237       CheckInfo * ci_last = check_runtogether(word, word + len, try_uppercase, run_together_limit, ci, ci_end, gi);
238       bool found = ci_last;
239       word[len] = save;
240 
241       if (!found) {
242         if (cpi) {
243           ci_last = ci;
244           ok = false;
245           ci->word.str = word;
246           ci->word.len = len;
247           ci->incorrect = true;
248           cpi->incorrect_count++;
249           if (!cpi->first_incorrect)
250             cpi->first_incorrect = ci;
251         } else {
252           return false;
253         }
254       }
255 
256       if (cpi)
257         cpi->count++;
258 
259       if (ci_prev) {
260         ci_prev->compound = true;
261         ci_prev->next = ci;
262       }
263 
264       ci_prev = ci_last;
265       ci = ci_last + 1;
266       if (ci >= ci_end) {
267         if (cpi) cpi->count = 0;
268         return false;
269       }
270 
271       word = word + cw.rest_offset();
272       cw = lang_->split_word(cw.rest, cw.rest_len(), camel_case_);
273 
274     } while (!cw.empty());
275 
276     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   }
307 
308   //////////////////////////////////////////////////////////////////////
309   //
310   // Word list managment methods
311   //
312 
save_all_word_lists()313   PosibErr<void> SpellerImpl::save_all_word_lists() {
314     SpellerDict * i = dicts_;
315     for (; i; i = i->next) {
316       if  (i->save_on_saveall)
317         RET_ON_ERR(i->dict->synchronize());
318     }
319     return no_err;
320   }
321 
num_wordlists() const322   int SpellerImpl::num_wordlists() const {
323     return 0; //FIXME
324   }
325 
wordlists() const326   SpellerImpl::WordLists SpellerImpl::wordlists() const {
327     return 0; //FIXME
328     //return MakeEnumeration<DataSetCollection::Parms>(wls_->begin(), DataSetCollection::Parms(wls_->end()));
329   }
330 
personal_word_list() const331   PosibErr<const WordList *> SpellerImpl::personal_word_list() const {
332     const WordList * wl = static_cast<const WordList *>(personal_);
333     if (!wl) return make_err(operation_not_supported_error,
334                              _("The personal word list is unavailable."));
335     return wl;
336   }
337 
session_word_list() const338   PosibErr<const WordList *> SpellerImpl::session_word_list() const {
339     const WordList * wl = static_cast<const WordList *>(session_);
340     if (!wl) return make_err(operation_not_supported_error,
341                              _("The session word list is unavailable."));
342     return wl;
343   }
344 
main_word_list() const345   PosibErr<const WordList *> SpellerImpl::main_word_list() const {
346     const WordList * wl = dynamic_cast<const WordList *>(main_);
347     if (!wl) return make_err(operation_not_supported_error,
348                              _("The main word list is unavailable."));
349     return wl;
350   }
351 
locate(const Dict::Id & id) const352   const SpellerDict * SpellerImpl::locate (const Dict::Id & id) const
353   {
354     for (const SpellerDict * i = dicts_; i; i = i->next)
355       if (i->dict->id() == id) return i;
356     return 0;
357   }
358 
add_dict(SpellerDict * wc)359   PosibErr<void> SpellerImpl::add_dict(SpellerDict * wc)
360   {
361     Dict * w = wc->dict;
362     assert(locate(w->id()) == 0);
363 
364     if (!lang_) {
365       lang_.copy(w->lang());
366       config_->replace("lang", lang_name());
367       config_->replace("language-tag", lang_name());
368     } else {
369       if (strcmp(lang_->name(), w->lang()->name()) != 0)
370         return make_err(mismatched_language, lang_->name(), w->lang()->name());
371     }
372 
373     // add to master list
374     wc->next = dicts_;
375     dicts_ = wc;
376 
377     // check if it has a special_id and act accordingly
378     switch (wc->special_id) {
379     case main_id:
380       assert(main_ == 0);
381       main_ = w;
382       break;
383     case personal_id:
384       assert(personal_ == 0);
385       personal_ = w;
386       break;
387     case session_id:
388       assert(session_ == 0);
389       session_ = w;
390       break;
391     case personal_repl_id:
392       assert(repl_ == 0);
393       repl_ = w;
394       break;
395     case none_id:
396       break;
397     }
398 
399     return no_err;
400   }
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;
Fun()418       Fun() {}
Fun(WithStr m)419       Fun(WithStr  m) : with_str (m) {}
Fun(WithInt m)420       Fun(WithInt  m) : with_int (m) {}
Fun(WithBool m)421       Fun(WithBool m) : with_bool(m) {}
call(SpellerImpl * m,const char * val) const422       PosibErr<void> call(SpellerImpl * m, const char * val) const
423         {return (*with_str) (m,val);}
call(SpellerImpl * m,int val) const424       PosibErr<void> call(SpellerImpl * m, int val)          const
425         {return (*with_int) (m,val);}
call(SpellerImpl * m,bool val) const426       PosibErr<void> call(SpellerImpl * m, bool val)         const
427         {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:
ConfigNotifier(SpellerImpl * m)440     ConfigNotifier(SpellerImpl * m)
441       : speller_(m)
442     {}
443 
item_updated(const KeyInfo * ki,int value)444     PosibErr<void> item_updated(const KeyInfo * ki, int value) {
445       return callback(speller_, ki, value, UpdateMember::Int);
446     }
item_updated(const KeyInfo * ki,bool value)447     PosibErr<void> item_updated(const KeyInfo * ki, bool value) {
448       return callback(speller_, ki, value, UpdateMember::Bool);
449     }
item_updated(const KeyInfo * ki,ParmStr value)450     PosibErr<void> item_updated(const KeyInfo * ki, ParmStr value) {
451       return callback(speller_, ki, value, UpdateMember::String);
452     }
453 
ignore(SpellerImpl * m,int value)454     static PosibErr<void> ignore(SpellerImpl * m, int value) {
455       m->ignore_count = value;
456       return no_err;
457     }
ignore_accents(SpellerImpl * m,bool value)458     static PosibErr<void> ignore_accents(SpellerImpl * m, bool value) {
459       return no_err;
460     }
ignore_case(SpellerImpl * m,bool value)461     static PosibErr<void> ignore_case(SpellerImpl * m, bool value) {
462       m->s_cmp.case_insensitive = value;
463       m->s_cmp_begin.case_insensitive = value;
464       m->s_cmp_middle.case_insensitive = value;
465       m->s_cmp_end.case_insensitive = value;
466       return no_err;
467     }
ignore_repl(SpellerImpl * m,bool value)468     static PosibErr<void> ignore_repl(SpellerImpl * m, bool value) {
469 
470       m->ignore_repl = value;
471       return no_err;
472     }
save_repl(SpellerImpl * m,bool value)473     static PosibErr<void> save_repl(SpellerImpl * m, bool value) {
474       // FIXME
475       // m->save_on_saveall(DataSet::Id(&m->personal_repl()), value);
476       abort(); return no_err;
477     }
sug_mode(SpellerImpl * m,const char * mode)478     static PosibErr<void> sug_mode(SpellerImpl * m, const char * mode) {
479       RET_ON_ERR(m->suggest_->set_mode(mode));
480       RET_ON_ERR(m->intr_suggest_->set_mode(mode));
481       return no_err;
482     }
run_together(SpellerImpl * m,bool value)483     static PosibErr<void> run_together(SpellerImpl * m, bool value) {
484       m->unconditional_run_together_ = value;
485       m->run_together = m->unconditional_run_together_;
486       return no_err;
487     }
run_together_limit(SpellerImpl * m,int value)488     static PosibErr<void> run_together_limit(SpellerImpl * m, int value) {
489       if (value > 8) {
490         m->config()->replace("run-together-limit", "8");
491         // will loop back
492       } else {
493         m->run_together_limit_ = value;
494       }
495       return no_err;
496     }
run_together_min(SpellerImpl * m,int value)497     static PosibErr<void> run_together_min(SpellerImpl * m, int value) {
498       m->run_together_min_ = value;
499       return no_err;
500     }
camel_case(SpellerImpl * m,bool value)501     static PosibErr<void> camel_case(SpellerImpl * m, bool value) {
502       m->camel_case_ = value;
503       return no_err;
504     }
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>
callback(SpellerImpl * m,const KeyInfo * ki,T value,UpdateMember::Type t)530   PosibErr<void> callback(SpellerImpl * m, const KeyInfo * ki, T value,
531                           UpdateMember::Type t)
532   {
533     const UpdateMember * i
534       = update_members;
535     const UpdateMember * end
536       = i + sizeof(update_members)/sizeof(UpdateMember);
537     while (i != end) {
538       if (strcmp(ki->name, i->name) == 0) {
539         if (i->type == t) {
540           RET_ON_ERR(i->fun.call(m, value));
541           break;
542         }
543       }
544       ++i;
545     }
546     return no_err;
547   }
548 
549   //////////////////////////////////////////////////////////////////////
550   //
551   // SpellerImpl inititization members
552   //
553 
SpellerImpl()554   SpellerImpl::SpellerImpl()
555     : Speller(0) /* FIXME */, ignore_repl(true),
556       dicts_(0), personal_(0), session_(0), repl_(0), main_(0)
557   {}
558 
add_dicts(SpellerImpl * sp,DictList & d)559   inline PosibErr<void> add_dicts(SpellerImpl * sp, DictList & d)
560   {
561     for (;!d.empty(); d.pop())
562     {
563       if (!sp->locate(d.last()->id())) {
564         RET_ON_ERR(sp->add_dict(new SpellerDict(d.last())));
565       }
566     }
567     return no_err;
568   }
569 
setup(Config * c)570   PosibErr<void> SpellerImpl::setup(Config * c) {
571     assert (config_ == 0);
572     config_.reset(c);
573 
574     ignore_repl = config_->retrieve_bool("ignore-repl");
575     ignore_count = config_->retrieve_int("ignore");
576 
577     DictList to_add;
578     RET_ON_ERR(add_data_set(config_->retrieve("master-path"), *config_, &to_add, this));
579     RET_ON_ERR(add_dicts(this, to_add));
580 
581     s_cmp.lang = lang_;
582     s_cmp.case_insensitive = config_->retrieve_bool("ignore-case");
583 
584     s_cmp_begin.lang = lang_;
585     s_cmp_begin.case_insensitive = s_cmp.case_insensitive;
586     s_cmp_begin.end = false;
587 
588     s_cmp_middle.lang = lang_;
589     s_cmp_middle.case_insensitive = s_cmp.case_insensitive;
590     s_cmp_middle.begin = false;
591     s_cmp_middle.end   = false;
592 
593     s_cmp_end.lang = lang_;
594     s_cmp_end.case_insensitive = s_cmp.case_insensitive;
595     s_cmp_end.begin = false;
596 
597     StringList extra_dicts;
598     config_->retrieve_list("extra-dicts", &extra_dicts);
599     StringListEnumeration els = extra_dicts.elements_obj();
600     const char * dict_name;
601     while ( (dict_name = els.next()) != 0) {
602       RET_ON_ERR(add_data_set(dict_name,*config_, &to_add, this));
603       RET_ON_ERR(add_dicts(this, to_add));
604     }
605 
606     bool use_other_dicts = config_->retrieve_bool("use-other-dicts");
607 
608     if (use_other_dicts && !personal_)
609     {
610       Dictionary * temp;
611       temp = new_default_writable_dict(*config_);
612       PosibErrBase pe = temp->load(config_->retrieve("personal-path"),*config_);
613       if (pe.has_err(cant_read_file))
614         temp->set_check_lang(lang_name(), *config_);
615       else if (pe.has_err())
616         return pe;
617       RET_ON_ERR(add_dict(new SpellerDict(temp, *config_, personal_id)));
618     }
619 
620     if (use_other_dicts && !session_)
621     {
622       Dictionary * temp;
623       temp = new_default_writable_dict(*config_);
624       temp->set_check_lang(lang_name(), *config_);
625       RET_ON_ERR(add_dict(new SpellerDict(temp, *config_, session_id)));
626     }
627 
628     if (use_other_dicts && !repl_)
629     {
630       ReplacementDict * temp = new_default_replacement_dict(*config_);
631       PosibErrBase pe = temp->load(config_->retrieve("repl-path"),*config_);
632       if (pe.has_err(cant_read_file))
633         temp->set_check_lang(lang_name(), *config_);
634       else if (pe.has_err())
635         return pe;
636       RET_ON_ERR(add_dict(new SpellerDict(temp, *config_, personal_repl_id)));
637     }
638 
639     StringList wordlist_files;
640     config_->retrieve_list("wordlists", &wordlist_files);
641     if (!wordlist_files.empty()) {
642       Dictionary * dict = session_;
643       if (!dict) {
644         dict = new_default_writable_dict(*config_);
645         dict->set_check_lang(lang_name(), *config_);
646         RET_ON_ERR(add_dict(new SpellerDict(dict, *config_)));
647       }
648       const char * fn;
649       StringListEnumeration els = wordlist_files.elements_obj();
650       while ( (fn = els.next()) != 0) {
651         FStream f;
652         RET_ON_ERR(f.open(fn, "r"));
653         IstreamEnumeration els(f);
654         WordListIterator wl_itr(&els, lang_, 0);
655         wl_itr.init_plain(*config_);
656         for (;;) {
657           PosibErr<bool> pe = wl_itr.adv();
658           if (pe.has_err())
659             return pe.with_file(fn);
660           if (!pe.data) break;
661           PosibErr<void> pev = dict->add(wl_itr->word);
662           if (pev.has_err())
663             return pev.with_file(fn);
664         }
665       }
666     }
667 
668     const char * sys_enc = lang_->charmap();
669     ConfigConvKey user_enc = config_->retrieve_value("encoding");
670     if (user_enc.val == "none") {
671       config_->replace("encoding", sys_enc);
672       user_enc = sys_enc;
673     }
674 
675     PosibErr<Convert *> conv;
676     conv = new_convert(*c, user_enc, sys_enc, NormFrom);
677     if (conv.has_err()) return conv;
678     to_internal_.reset(conv);
679     conv = new_convert(*c, sys_enc, user_enc, NormTo);
680     if (conv.has_err()) return conv;
681     from_internal_.reset(conv);
682 
683     unconditional_run_together_ = config_->retrieve_bool("run-together");
684     run_together = unconditional_run_together_;
685 
686     run_together_limit_  = config_->retrieve_int("run-together-limit");
687     if (run_together_limit_ > 8) {
688       config_->replace("run-together-limit", "8");
689       run_together_limit_ = 8;
690     }
691     run_together_min_    = config_->retrieve_int("run-together-min");
692 
693     camel_case_ = config_->retrieve_bool("camel-case");
694 
695     config_->add_notifier(new ConfigNotifier(this));
696 
697     config_->set_attached(true);
698 
699     affix_info = lang_->affix();
700 
701     //
702     // setup word set lists
703     //
704 
705     typedef Vector<SpellerDict *> AllWS; AllWS all_ws;
706     for (SpellerDict * i = dicts_; i; i = i->next) {
707       if (i->dict->basic_type == Dict::basic_dict ||
708           i->dict->basic_type == Dict::replacement_dict) {
709         all_ws.push_back(i);
710       }
711     }
712 
713     const std::type_info * ti = 0;
714     while (!all_ws.empty())
715     {
716       AllWS::iterator i0 = all_ws.end();
717       int max = -2;
718       AllWS::iterator i = all_ws.begin();
719       for (; i != all_ws.end(); ++i)
720       {
721         const Dictionary * ws = (*i)->dict;
722         if (ti && *ti != typeid(*ws)) continue;
723         if ((int)ws->size() > max) {max = ws->size(); i0 = i;}
724       }
725 
726       if (i0 == all_ws.end()) {ti = 0; continue;}
727 
728       SpellerDict * cur = *i0;
729 
730       all_ws.erase(i0);
731 
732       ti = &typeid(*cur->dict);
733 
734       if (cur->use_to_check) {
735         check_ws.push_back(cur->dict);
736         if (cur->dict->affix_compressed) affix_ws.push_back(cur->dict);
737       }
738       if (cur->use_to_suggest) {
739         suggest_ws.push_back(cur->dict);
740         if (cur->dict->affix_compressed) suggest_affix_ws.push_back(cur->dict);
741       }
742     }
743     fast_scan   = suggest_ws.front()->fast_scan;
744     fast_lookup = suggest_ws.front()->fast_lookup;
745     have_soundslike = lang_->have_soundslike();
746     have_repl = lang_->have_repl();
747     invisible_soundslike = suggest_ws.front()->invisible_soundslike;
748     soundslike_root_only = suggest_ws.front()->soundslike_root_only;
749     affix_compress = !affix_ws.empty();
750 
751     //
752     // Setup suggest
753     //
754 
755     PosibErr<Suggest *> pe;
756     pe = new_default_suggest(this);
757     if (pe.has_err()) return pe;
758     suggest_.reset(pe.data);
759     pe = new_default_suggest(this);
760     if (pe.has_err()) return pe;
761     intr_suggest_.reset(pe.data);
762 
763     return no_err;
764   }
765 
766   //////////////////////////////////////////////////////////////////////
767   //
768   // SpellerImpl destrution members
769   //
770 
~SpellerImpl()771   SpellerImpl::~SpellerImpl() {
772     while (dicts_) {
773       SpellerDict * next = dicts_->next;
774       delete dicts_;
775       dicts_ = next;
776     }
777   }
778 
779   //////////////////////////////////////////////////////////////////////
780   //
781   // SpellerImple setup tokenizer method
782   //
783 
setup_tokenizer(Tokenizer * tok)784   void SpellerImpl::setup_tokenizer(Tokenizer * tok)
785   {
786     for (int i = 0; i != 256; ++i)
787     {
788       tok->char_type_[i].word   = lang_->is_alpha(i);
789       tok->char_type_[i].begin  = lang_->special(i).begin;
790       tok->char_type_[i].middle = lang_->special(i).middle;
791       tok->char_type_[i].end    = lang_->special(i).end;
792     }
793     tok->conv_ = to_internal_;
794   }
795 
796 
797   //////////////////////////////////////////////////////////////////////
798   //
799   //
800   //
801 
SpellerDict(Dict * d)802   SpellerDict::SpellerDict(Dict * d)
803     : dict(d), special_id(none_id), next(0)
804   {
805     switch (dict->basic_type) {
806     case Dict::basic_dict:
807       use_to_check = true;
808       use_to_suggest = true;
809       break;
810     case Dict::replacement_dict:
811       use_to_check = false;
812       use_to_suggest = true;
813     case Dict::multi_dict:
814       break;
815     default:
816       abort();
817     }
818     save_on_saveall = false;
819   }
820 
SpellerDict(Dict * w,const Config & c,SpecialId id)821   SpellerDict::SpellerDict(Dict * w, const Config & c, SpecialId id)
822     : next(0)
823   {
824     dict = w;
825     special_id = id;
826     switch (id) {
827     case main_id:
828       if (dict->basic_type == Dict::basic_dict) {
829 
830         use_to_check    = true;
831         use_to_suggest  = true;
832         save_on_saveall = false;
833 
834       } else if (dict->basic_type == Dict::replacement_dict) {
835 
836         use_to_check    = false;
837         use_to_suggest  = false;
838         save_on_saveall = false;
839 
840       } else {
841 
842         abort();
843 
844       }
845       break;
846     case personal_id:
847       use_to_check = true;
848       use_to_suggest = true;
849       save_on_saveall = true;
850       break;
851     case session_id:
852       use_to_check = true;
853       use_to_suggest = true;
854       save_on_saveall = false;
855       break;
856     case personal_repl_id:
857       use_to_check = false;
858       use_to_suggest = true;
859       save_on_saveall = c.retrieve_bool("save-repl");
860       break;
861     case none_id:
862       break;
863     }
864   }
865 
866   extern "C"
libaspell_speller_default_LTX_new_speller_class(SpellerLtHandle)867   Speller * libaspell_speller_default_LTX_new_speller_class(SpellerLtHandle)
868   {
869     return new SpellerImpl();
870   }
871 }
872 
873