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