// This file is part of The New Aspell // Copyright (C) 2000-2001 by Kevin Atkinson under the GNU LGPL // license version 2.0 or 2.1. You should have received a copy of the // LGPL license along with this library if you did not you can find it // at http://www.gnu.org/. #include "config.hpp" #include "convert.hpp" #include "data.hpp" #include "data_id.hpp" #include "errors.hpp" #include "file_util.hpp" #include "fstream.hpp" #include "language.hpp" #include "speller_impl.hpp" #include "cache-t.hpp" #include "vararray.hpp" #include "gettext.h" namespace aspeller { GlobalCache dict_cache("dictionary"); // // Dict impl // Dictionary::Id::Id(Dict * p, const FileName & fn) : ptr(p) { file_name = fn.name; #ifdef USE_FILE_INO struct stat s; // the file ,i if (file_name[0] != '\0' && stat(fn.path.c_str(), &s) == 0) { ino = s.st_ino; dev = s.st_dev; } else { ino = 0; dev = 0; } #endif } bool operator==(const Dictionary::Id & rhs, const Dictionary::Id & lhs) { if (rhs.ptr == 0 || lhs.ptr == 0) { if (rhs.file_name == 0 || lhs.file_name == 0) return false; #ifdef USE_FILE_INO return rhs.ino == lhs.ino && rhs.dev == lhs.dev; #else return strcmp(rhs.file_name, lhs.file_name) == 0; #endif } else { return rhs.ptr == lhs.ptr; } } PosibErr Dictionary::attach(const Language &l) { if (lang_ && strcmp(l.name(),lang_->name()) != 0) return make_err(mismatched_language, lang_->name(), l.name()); if (!lang_) lang_.copy(&l); copy(); return no_err; } Dictionary::Dictionary(BasicType t, const char * n) : Cacheable(&dict_cache), lang_(), id_(), basic_type(t), class_name(n), affix_compressed(false), invisible_soundslike(false), soundslike_root_only(false), fast_scan(false), fast_lookup(false) { id_.reset(new Id(this)); } Dictionary::~Dictionary() { } const char * Dictionary::lang_name() const { return lang_->name(); } PosibErr Dictionary::check_lang(ParmString l) { if (l != lang_->name()) return make_err(mismatched_language, lang_->name(), l); return no_err; } PosibErr Dictionary::set_check_lang (ParmString l, Config & config) { if (lang_ == 0) { PosibErr res = new_language(config, l); if (res.has_err()) return res; lang_.reset(res.data); lang_->set_lang_defaults(config); set_lang_hook(config); } else { if (l != lang_->name()) return make_err(mismatched_language, l, lang_->name()); } return no_err; } void Dictionary::FileName::copy(const FileName & other) { const_cast(path) = other.path; const_cast(name) = path.c_str() + (other.name - other.path.c_str()); } void Dictionary::FileName::clear() { path = ""; name = path.c_str(); } void Dictionary::FileName::set(ParmString str) { path = str; int i = path.size() - 1; while (i >= 0) { if (path[i] == '/' || path[i] == '\\') { ++i; break; } --i; } name = path.c_str() + i; } PosibErr Dictionary::set_file_name(ParmString fn) { file_name_.set(fn); *id_ = Id(this, file_name_); return no_err; } PosibErr Dictionary::update_file_info(FStream & f) { #ifdef USE_FILE_INO struct stat s; int ok = fstat(f.file_no(), &s); assert(ok == 0); id_->ino = s.st_ino; id_->dev = s.st_dev; #endif return no_err; } // // BasicDict // class DictStringEnumeration : public StringEnumeration { ClonePtr real_; public: DictStringEnumeration(Dict::Enum * r) : real_(r) {} bool at_end() const { return real_->at_end(); } const char * next() { // FIXME: It's not this simple when affixes are involved WordEntry * w = real_->next(); if (!w) return 0; return w->word; } StringEnumeration * clone() const { return new DictStringEnumeration(*this); } void assign(const StringEnumeration * other) { *this = *static_cast(other); } }; PosibErr Dictionary::add_repl(ParmString mis, ParmString cor) { if (!invisible_soundslike) { VARARRAY(char, sl, mis.size() + 1); lang()->LangImpl::to_soundslike(sl, mis.str(), mis.size()); return add_repl(mis, cor, sl); } else { return add_repl(mis, cor, ""); } } PosibErr Dictionary::add(ParmString w) { if (!invisible_soundslike) { VARARRAY(char, sl, w.size() + 1); lang()->LangImpl::to_soundslike(sl, w.str(), w.size()); return add(w, sl); } else { return add(w, ""); } } // // Default implementation; // PosibErr Dictionary::load(ParmString, Config &, DictList *, SpellerImpl *) { return make_err(unimplemented_method, "load", class_name); } PosibErr Dictionary::merge(ParmString) { return make_err(unimplemented_method, "load", class_name); } PosibErr Dictionary::synchronize() { return make_err(unimplemented_method, "synchronize", class_name); } PosibErr Dictionary::save_noupdate() { return make_err(unimplemented_method, "save_noupdate", class_name); } PosibErr Dictionary::save_as(ParmString) { return make_err(unimplemented_method, "save_as", class_name); } PosibErr Dictionary::clear() { return make_err(unimplemented_method, "clear", class_name); } StringEnumeration * Dictionary::elements() const { Enum * e = detailed_elements(); if (!e) return 0; return new DictStringEnumeration(e); } Dict::Enum * Dictionary::detailed_elements() const { return 0; } Dict::Size Dictionary::size() const { if (empty()) return 0; else return 1; } bool Dictionary::empty() const { return false; } bool Dictionary::lookup (ParmString word, const SensitiveCompare *, WordEntry &) const { return false; } bool Dictionary::clean_lookup(ParmString, WordEntry &) const { return false; } bool Dictionary::soundslike_lookup(const WordEntry &, WordEntry &) const { return false; } bool Dictionary::soundslike_lookup(ParmString, WordEntry &) const { return false; } SoundslikeEnumeration * Dictionary::soundslike_elements() const { return 0; } PosibErr Dictionary::add(ParmString w, ParmString s) { return make_err(unimplemented_method, "add", class_name); } PosibErr Dictionary::remove(ParmString w) { return make_err(unimplemented_method, "remove", class_name); } bool Dictionary::repl_lookup(const WordEntry &, WordEntry &) const { return false; } bool Dictionary::repl_lookup(ParmString, WordEntry &) const { return false; } PosibErr Dictionary::add_repl(ParmString mis, ParmString cor, ParmString s) { return make_err(unimplemented_method, "add_repl", class_name); } PosibErr Dictionary::remove_repl(ParmString mis, ParmString cor) { return make_err(unimplemented_method, "remove_repl", class_name); } DictsEnumeration * Dictionary::dictionaries() const { return 0; } #define write_conv(s) do { \ if (!c) {o << s;} \ else {ParmString ss(s); buf.clear(); c->convert(ss.str(), ss.size(), buf); o.write(buf.data(), buf.size());} \ } while (false) OStream & WordEntry::write (OStream & o, const Language & l, Convert * c) const { CharVector buf; write_conv(word); if (aff && *aff) { o << '/'; write_conv(aff); } return o; } PosibErr add_data_set(ParmString fn, Config & config, DictList * new_dicts, SpellerImpl * speller, ParmString dir, DataType allowed) { Dict * res = 0; static const char * suffix_list[] = {"", ".multi", ".alias", ".spcl", ".special", ".pws", ".prepl"}; FStream in; const char * * suffix; const char * * suffix_end = suffix_list + sizeof(suffix_list)/sizeof(const char *); String dict_dir = config.retrieve("dict-dir"); String true_file_name; Dict::FileName file_name(fn); const char * d = dir; do { if (d == 0) d = dict_dir.c_str(); suffix = suffix_list; do { true_file_name = add_possible_dir(d, ParmString(file_name.path) + ParmString(*suffix)); in.open(true_file_name, "r").ignore_err(); ++suffix; } while (!in && suffix != suffix_end); if (d == dict_dir.c_str()) break; d = 0; } while (!in); if (!in) { true_file_name = add_possible_dir(dir ? dir.str() : d, file_name.path); return make_err(cant_read_file, true_file_name); } DataType actual_type; if ((true_file_name.size() > 5 && true_file_name.substr(true_file_name.size() - 6, 6) == ".spcl") || (true_file_name.size() > 6 && (true_file_name.substr(true_file_name.size() - 6, 6) == ".multi" || true_file_name.substr(true_file_name.size() - 6, 6) == ".alias")) || (true_file_name.size() > 8 && true_file_name.substr(true_file_name.size() - 6, 6) == ".special")) { actual_type = DT_Multi; } else { char head[32] = {0}; in.read(head, 32); if (strncmp(head, "aspell default speller rowl", 27) ==0) actual_type = DT_ReadOnly; else if (strncmp(head, "personal_repl", 13) == 0) actual_type = DT_WritableRepl; else if (strncmp(head, "personal_ws", 11) == 0) actual_type = DT_Writable; else return make_err(bad_file_format, true_file_name); } if (actual_type & ~allowed) return make_err(bad_file_format, true_file_name , _("is not one of the allowed types")); Dict::FileName id_fn(true_file_name); Dict::Id id(0,id_fn); if (speller != 0) { const SpellerDict * d = speller->locate(id); if (d != 0) { return d->dict; } } res = 0; if (actual_type == DT_ReadOnly) { // try to get it from the cache res = dict_cache.find(id); } if (!res) { StackPtr w; switch (actual_type) { case DT_ReadOnly: w = new_default_readonly_dict(); break; case DT_Multi: w = new_default_multi_dict(); break; case DT_Writable: w = new_default_writable_dict(); break; case DT_WritableRepl: w = new_default_replacement_dict(); break; default: abort(); } RET_ON_ERR(w->load(true_file_name, config, new_dicts, speller)); if (actual_type == DT_ReadOnly) dict_cache.add(w); res = w.release(); } else { res->copy(); } if (new_dicts) new_dicts->add(res); return res; } }