/src/aspell/modules/speller/default/data.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 "config.hpp" |
8 | | #include "convert.hpp" |
9 | | #include "data.hpp" |
10 | | #include "data_id.hpp" |
11 | | #include "errors.hpp" |
12 | | #include "file_util.hpp" |
13 | | #include "fstream.hpp" |
14 | | #include "language.hpp" |
15 | | #include "speller_impl.hpp" |
16 | | #include "cache-t.hpp" |
17 | | #include "vararray.hpp" |
18 | | |
19 | | #include "gettext.h" |
20 | | |
21 | | namespace aspeller { |
22 | | |
23 | | GlobalCache<Dictionary> dict_cache("dictionary"); |
24 | | |
25 | | // |
26 | | // Dict impl |
27 | | // |
28 | | |
29 | | Dictionary::Id::Id(Dict * p, const FileName & fn) |
30 | | : ptr(p) |
31 | 12.0k | { |
32 | 12.0k | file_name = fn.name(); |
33 | 12.0k | #ifdef USE_FILE_INO |
34 | 12.0k | struct stat s; |
35 | | // the file ,i |
36 | 12.0k | if (file_name[0] != '\0' && stat(fn.path().c_str(), &s) == 0) { |
37 | 5.67k | ino = s.st_ino; |
38 | 5.67k | dev = s.st_dev; |
39 | 6.39k | } else { |
40 | 6.39k | ino = 0; |
41 | 6.39k | dev = 0; |
42 | 6.39k | } |
43 | 12.0k | #endif |
44 | 12.0k | } |
45 | | |
46 | | bool operator==(const Dictionary::Id & rhs, const Dictionary::Id & lhs) |
47 | 19.7k | { |
48 | 19.7k | if (rhs.ptr == 0 || lhs.ptr == 0) { |
49 | 723 | if (rhs.file_name == 0 || lhs.file_name == 0) |
50 | 0 | return false; |
51 | 723 | #ifdef USE_FILE_INO |
52 | 723 | return rhs.ino == lhs.ino && rhs.dev == lhs.dev; |
53 | | #else |
54 | | return strcmp(rhs.file_name, lhs.file_name) == 0; |
55 | | #endif |
56 | 19.0k | } else { |
57 | 19.0k | return rhs.ptr == lhs.ptr; |
58 | 19.0k | } |
59 | 19.7k | } |
60 | | |
61 | 0 | PosibErr<void> Dictionary::attach(const Language &l) { |
62 | 0 | if (lang_ && strcmp(l.name(),lang_->name()) != 0) |
63 | 0 | return make_err(mismatched_language, lang_->name(), l.name()); |
64 | 0 | if (!lang_) lang_.copy(&l); |
65 | 0 | copy(); |
66 | 0 | return no_err; |
67 | 0 | } |
68 | | |
69 | | Dictionary::Dictionary(BasicType t, const char * n) |
70 | | : Cacheable(&dict_cache), lang_(), id_(), |
71 | | basic_type(t), class_name(n), |
72 | | validate_words(true), |
73 | | affix_compressed(false), |
74 | | invisible_soundslike(false), soundslike_root_only(false), |
75 | | fast_scan(false), fast_lookup(false) |
76 | 4.97k | { |
77 | 4.97k | id_.reset(new Id(this)); |
78 | 4.97k | } |
79 | | |
80 | | Dictionary::~Dictionary() |
81 | 4.97k | { |
82 | 4.97k | } |
83 | | |
84 | 0 | const char * Dictionary::lang_name() const { |
85 | 0 | return lang_->name(); |
86 | 0 | } |
87 | | |
88 | 0 | PosibErr<void> Dictionary::check_lang(ParmString l) { |
89 | 0 | if (l != lang_->name()) |
90 | 0 | return make_err(mismatched_language, lang_->name(), l); |
91 | 0 | return no_err; |
92 | 0 | } |
93 | | |
94 | | PosibErr<void> Dictionary::set_check_lang (ParmString l, Config & config) |
95 | 5.66k | { |
96 | 5.66k | if (lang_ == 0) { |
97 | 4.97k | PosibErr<Language *> res = new_language(config, l); |
98 | 4.97k | if (res.has_err()) return res; |
99 | 4.97k | lang_.reset(res.data); |
100 | 4.97k | RET_ON_ERR(lang_->set_lang_defaults(config)); |
101 | 4.97k | set_lang_hook(config); |
102 | 4.97k | } else { |
103 | 699 | if (l != lang_->name()) |
104 | 0 | return make_err(mismatched_language, l, lang_->name()); |
105 | 699 | } |
106 | 5.66k | return no_err; |
107 | 5.66k | } |
108 | | |
109 | | void Dictionary::FileName::copy(const FileName & other) |
110 | 0 | { |
111 | 0 | path_ = other.path_; |
112 | 0 | name_ = path_.c_str() + (other.name_ - other.path_.c_str()); |
113 | 0 | } |
114 | | |
115 | | void Dictionary::FileName::clear() |
116 | 9.94k | { |
117 | 9.94k | path_ = ""; |
118 | 9.94k | name_ = path_.c_str(); |
119 | 9.94k | } |
120 | | |
121 | | void Dictionary::FileName::set(ParmString str) |
122 | 9.95k | { |
123 | 9.95k | path_ = str; |
124 | 9.95k | int i = path_.size() - 1; |
125 | 182k | while (i >= 0) { |
126 | 180k | if (path_[i] == '/' || path_[i] == '\\') { |
127 | 7.82k | ++i; |
128 | 7.82k | break; |
129 | 7.82k | } |
130 | 172k | --i; |
131 | 172k | } |
132 | 9.95k | if (i < 0) i = 0; |
133 | 9.95k | name_ = path_.c_str() + i; |
134 | 9.95k | } |
135 | | |
136 | | PosibErr<void> Dictionary::set_file_name(ParmString fn) |
137 | 4.25k | { |
138 | 4.25k | file_name_.set(fn); |
139 | 4.25k | *id_ = Id(this, file_name_); |
140 | 4.25k | return no_err; |
141 | 4.25k | } |
142 | | |
143 | | PosibErr<void> Dictionary::update_file_info(FStream & f) |
144 | 0 | { |
145 | 0 | #ifdef USE_FILE_INO |
146 | 0 | struct stat s; |
147 | 0 | int ok = fstat(f.file_no(), &s); |
148 | 0 | assert(ok == 0); |
149 | 0 | id_->ino = s.st_ino; |
150 | 0 | id_->dev = s.st_dev; |
151 | 0 | #endif |
152 | 0 | return no_err; |
153 | 0 | } |
154 | | |
155 | | // |
156 | | // BasicDict |
157 | | // |
158 | | |
159 | | class DictStringEnumeration : public StringEnumeration |
160 | | { |
161 | | ClonePtr<Dict::Enum> real_; |
162 | | public: |
163 | 0 | DictStringEnumeration(Dict::Enum * r) : real_(r) {} |
164 | | |
165 | 0 | bool at_end() const { |
166 | 0 | return real_->at_end(); |
167 | 0 | } |
168 | 0 | const char * next() { |
169 | | // FIXME: It's not this simple when affixes are involved |
170 | 0 | WordEntry * w = real_->next(); |
171 | 0 | if (!w) return 0; |
172 | 0 | return w->word; |
173 | 0 | } |
174 | 0 | StringEnumeration * clone() const { |
175 | 0 | return new DictStringEnumeration(*this); |
176 | 0 | } |
177 | 0 | void assign(const StringEnumeration * other) { |
178 | 0 | *this = *static_cast<const DictStringEnumeration *>(other); |
179 | 0 | } |
180 | | }; |
181 | | |
182 | | PosibErr<void> Dictionary::add_repl(ParmString mis, ParmString cor) |
183 | 0 | { |
184 | 0 | if (!invisible_soundslike) { |
185 | 0 | VARARRAY(char, sl, mis.size() + 1); |
186 | 0 | lang()->LangImpl::to_soundslike(sl, mis.str(), mis.size()); |
187 | 0 | return add_repl(mis, cor, sl); |
188 | 0 | } else { |
189 | 0 | return add_repl(mis, cor, ""); |
190 | 0 | } |
191 | 0 | } |
192 | | |
193 | | PosibErr<void> Dictionary::add(ParmString w) |
194 | 0 | { |
195 | 0 | if (!invisible_soundslike) { |
196 | 0 | VARARRAY(char, sl, w.size() + 1); |
197 | 0 | lang()->LangImpl::to_soundslike(sl, w.str(), w.size()); |
198 | 0 | return add(w, sl); |
199 | 0 | } else { |
200 | 0 | return add(w, ""); |
201 | 0 | } |
202 | 0 | } |
203 | | |
204 | | // |
205 | | // Default implementation; |
206 | | // |
207 | | |
208 | | PosibErr<void> Dictionary::load(ParmString, Config &, |
209 | | DictList *, SpellerImpl *) |
210 | 0 | { |
211 | 0 | return make_err(unimplemented_method, "load", class_name); |
212 | 0 | } |
213 | | |
214 | | PosibErr<void> Dictionary::merge(ParmString) |
215 | 0 | { |
216 | 0 | return make_err(unimplemented_method, "load", class_name); |
217 | 0 | } |
218 | | |
219 | | PosibErr<void> Dictionary::synchronize() |
220 | 0 | { |
221 | 0 | return make_err(unimplemented_method, "synchronize", class_name); |
222 | 0 | } |
223 | | |
224 | | PosibErr<void> Dictionary::save_noupdate() |
225 | 0 | { |
226 | 0 | return make_err(unimplemented_method, "save_noupdate", class_name); |
227 | 0 | } |
228 | | |
229 | | PosibErr<void> Dictionary::save_as(ParmString) |
230 | 0 | { |
231 | 0 | return make_err(unimplemented_method, "save_as", class_name); |
232 | 0 | } |
233 | | |
234 | | PosibErr<void> Dictionary::clear() |
235 | 0 | { |
236 | 0 | return make_err(unimplemented_method, "clear", class_name); |
237 | 0 | } |
238 | | |
239 | | StringEnumeration * Dictionary::elements() const |
240 | 0 | { |
241 | 0 | Enum * e = detailed_elements(); |
242 | 0 | if (!e) return 0; |
243 | 0 | return new DictStringEnumeration(e); |
244 | 0 | } |
245 | | |
246 | | Dict::Enum * Dictionary::detailed_elements() const |
247 | 0 | { |
248 | 0 | return 0; |
249 | 0 | } |
250 | | |
251 | | Dict::Size Dictionary::size() const |
252 | 0 | { |
253 | 0 | if (empty()) return 0; |
254 | 0 | else return 1; |
255 | 0 | } |
256 | | |
257 | | bool Dictionary::empty() const |
258 | 0 | { |
259 | 0 | return false; |
260 | 0 | } |
261 | | |
262 | | bool Dictionary::lookup (ParmString word, const SensitiveCompare *, |
263 | | WordEntry &) const |
264 | 0 | { |
265 | 0 | return false; |
266 | 0 | } |
267 | | |
268 | | bool Dictionary::clean_lookup(ParmString, WordEntry &) const |
269 | 0 | { |
270 | 0 | return false; |
271 | 0 | } |
272 | | |
273 | | bool Dictionary::soundslike_lookup(const WordEntry &, WordEntry &) const |
274 | 0 | { |
275 | 0 | return false; |
276 | 0 | } |
277 | | |
278 | | bool Dictionary::soundslike_lookup(ParmString, WordEntry &) const |
279 | 0 | { |
280 | 0 | return false; |
281 | 0 | } |
282 | | |
283 | | SoundslikeEnumeration * Dictionary::soundslike_elements() const |
284 | 0 | { |
285 | 0 | return 0; |
286 | 0 | } |
287 | | |
288 | | PosibErr<void> Dictionary::add(ParmString w, ParmString s) |
289 | 0 | { |
290 | 0 | return make_err(unimplemented_method, "add", class_name); |
291 | 0 | } |
292 | | |
293 | | PosibErr<void> Dictionary::remove(ParmString w) |
294 | 0 | { |
295 | 0 | return make_err(unimplemented_method, "remove", class_name); |
296 | 0 | } |
297 | | |
298 | | bool Dictionary::repl_lookup(const WordEntry &, WordEntry &) const |
299 | 0 | { |
300 | 0 | return false; |
301 | 0 | } |
302 | | |
303 | | bool Dictionary::repl_lookup(ParmString, WordEntry &) const |
304 | 0 | { |
305 | 0 | return false; |
306 | 0 | } |
307 | | |
308 | | PosibErr<void> Dictionary::add_repl(ParmString mis, ParmString cor, ParmString s) |
309 | 0 | { |
310 | 0 | return make_err(unimplemented_method, "add_repl", class_name); |
311 | 0 | } |
312 | | |
313 | | PosibErr<void> Dictionary::remove_repl(ParmString mis, ParmString cor) |
314 | 0 | { |
315 | 0 | return make_err(unimplemented_method, "remove_repl", class_name); |
316 | 0 | } |
317 | | |
318 | | DictsEnumeration * Dictionary::dictionaries() const |
319 | 0 | { |
320 | 0 | return 0; |
321 | 0 | } |
322 | | |
323 | 0 | #define write_conv(s) do { \ |
324 | 0 | if (!c) {o << s;} \ |
325 | 0 | else {ParmString ss(s); buf.clear(); c->convert(ss.str(), ss.size(), buf); o.write(buf.data(), buf.size());} \ |
326 | 0 | } while (false) |
327 | | |
328 | | OStream & WordEntry::write (OStream & o, |
329 | | const Language & l, |
330 | | Convert * c) const |
331 | 0 | { |
332 | 0 | CharVector buf; |
333 | 0 | write_conv(word); |
334 | 0 | if (aff && *aff) { |
335 | 0 | o << '/'; |
336 | 0 | write_conv(aff); |
337 | 0 | } |
338 | 0 | return o; |
339 | 0 | } |
340 | | |
341 | | PosibErr<Dict *> add_data_set(ParmString fn, |
342 | | Config & config, |
343 | | DictList * new_dicts, |
344 | | SpellerImpl * speller, |
345 | | ParmString dir, |
346 | | DataType allowed) |
347 | 2.85k | { |
348 | 2.85k | Dict * res = 0; |
349 | 2.85k | static const char * suffix_list[] = {"", ".multi", ".alias", |
350 | 2.85k | ".spcl", ".special", |
351 | 2.85k | ".pws", ".prepl"}; |
352 | 2.85k | FStream in; |
353 | 2.85k | const char * * suffix; |
354 | 2.85k | const char * * suffix_end |
355 | 2.85k | = suffix_list + sizeof(suffix_list)/sizeof(const char *); |
356 | 2.85k | String dict_dir = config.retrieve("dict-dir"); |
357 | 2.85k | String true_file_name; |
358 | 2.85k | Dict::FileName file_name(fn); |
359 | 2.85k | const char * d = dir; |
360 | 2.85k | do { |
361 | 2.85k | if (d == 0) d = dict_dir.c_str(); |
362 | 2.85k | suffix = suffix_list; |
363 | 2.99k | do { |
364 | 2.99k | true_file_name = add_possible_dir(d, ParmString(file_name.path()) |
365 | 2.99k | + ParmString(*suffix)); |
366 | 2.99k | in.open(true_file_name, "r").ignore_err(); |
367 | 2.99k | ++suffix; |
368 | 2.99k | } while (!in && suffix != suffix_end); |
369 | 2.85k | if (d == dict_dir.c_str()) break; |
370 | 2.11k | d = 0; |
371 | 2.11k | } while (!in); |
372 | 2.85k | if (!in) { |
373 | 22 | true_file_name = add_possible_dir(dir ? dir.str() : d, file_name.path()); |
374 | 22 | return make_err(cant_read_file, true_file_name); |
375 | 22 | } |
376 | 2.83k | DataType actual_type; |
377 | 2.83k | if ((true_file_name.size() > 5 |
378 | 2.83k | && true_file_name.substr(true_file_name.size() - 6, 6) == ".spcl") |
379 | 2.83k | || |
380 | 2.83k | (true_file_name.size() > 6 |
381 | 2.83k | && (true_file_name.substr(true_file_name.size() - 6, 6) == ".multi" |
382 | 2.83k | || true_file_name.substr(true_file_name.size() - 6, 6) == ".alias")) |
383 | 2.83k | || |
384 | 2.83k | (true_file_name.size() > 8 |
385 | 1.41k | && true_file_name.substr(true_file_name.size() - 6, 6) == ".special")) |
386 | 1.41k | { |
387 | | |
388 | 1.41k | actual_type = DT_Multi; |
389 | | |
390 | 1.41k | } else { |
391 | | |
392 | 1.41k | char head[32] = {0}; |
393 | 1.41k | in.read(head, 32); |
394 | 1.41k | if (strncmp(head, "aspell default speller rowl", 27) ==0) |
395 | 1.41k | actual_type = DT_ReadOnly; |
396 | 1 | else if (strncmp(head, "personal_repl", 13) == 0) |
397 | 0 | actual_type = DT_WritableRepl; |
398 | 1 | else if (strncmp(head, "personal_ws", 11) == 0) |
399 | 0 | actual_type = DT_Writable; |
400 | 1 | else |
401 | 1 | return make_err(bad_file_format, true_file_name); |
402 | 1.41k | } |
403 | | |
404 | 2.83k | if (actual_type & ~allowed) |
405 | 0 | return make_err(bad_file_format, true_file_name |
406 | 0 | , _("is not one of the allowed types")); |
407 | | |
408 | 2.83k | Dict::FileName id_fn(true_file_name); |
409 | 2.83k | Dict::Id id(0,id_fn); |
410 | | |
411 | 2.83k | if (speller != 0) { |
412 | 2.83k | const SpellerDict * d = speller->locate(id); |
413 | 2.83k | if (d != 0) { |
414 | 1 | return d->dict; |
415 | 1 | } |
416 | 2.83k | } |
417 | | |
418 | 2.83k | res = 0; |
419 | | |
420 | 2.83k | Lock dict_cache_lock(NULL); |
421 | | |
422 | 2.83k | if (actual_type == DT_ReadOnly) { // try to get it from the cache |
423 | 1.41k | dict_cache_lock.set(&dict_cache.lock); |
424 | 1.41k | res = dict_cache.find(id); |
425 | 1.41k | } |
426 | | |
427 | 2.83k | if (!res) { |
428 | | |
429 | 2.83k | StackPtr<Dict> w; |
430 | 2.83k | switch (actual_type) { |
431 | 1.41k | case DT_ReadOnly: |
432 | 1.41k | w = new_default_readonly_dict(); |
433 | 1.41k | break; |
434 | 1.41k | case DT_Multi: |
435 | 1.41k | w = new_default_multi_dict(); |
436 | 1.41k | break; |
437 | 0 | case DT_Writable: |
438 | 0 | w = new_default_writable_dict(config); |
439 | 0 | break; |
440 | 0 | case DT_WritableRepl: |
441 | 0 | w = new_default_replacement_dict(config); |
442 | 0 | break; |
443 | 0 | default: |
444 | 0 | abort(); |
445 | 2.83k | } |
446 | | |
447 | 2.83k | RET_ON_ERR(w->load(true_file_name, config, new_dicts, speller)); |
448 | | |
449 | 2.83k | if (actual_type == DT_ReadOnly) |
450 | 1.41k | dict_cache.add(w); |
451 | | |
452 | 2.83k | res = w.release(); |
453 | | |
454 | 2.83k | } else { // actual_type == DT_ReadOnly implied, and hence the lock |
455 | | // is already acquired |
456 | |
|
457 | 0 | res->copy_no_lock(); |
458 | | |
459 | 0 | } |
460 | | |
461 | 2.83k | dict_cache_lock.release(); |
462 | | |
463 | 2.83k | if (new_dicts) |
464 | 2.83k | new_dicts->add(res); |
465 | | |
466 | 2.83k | return res; |
467 | 2.83k | } |
468 | | |
469 | | } |
470 | | |