Coverage Report

Created: 2023-12-08 06:59

/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