Coverage Report

Created: 2026-10-13 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/aspell/common/convert.cpp
Line
Count
Source
1
// This file is part of The New Aspell
2
// Copyright (C) 2001 by Kevin Atkinson under the GNU LGPL license
3
// version 2.0 or 2.1.  You should have received a copy of the LGPL
4
// license along with this library if you did not you can find
5
// it at http://www.gnu.org/.
6
7
#include <assert.h>
8
#include <string.h>
9
#include <math.h>
10
11
#include "asc_ctype.hpp"
12
#include "convert.hpp"
13
#include "fstream.hpp"
14
#include "getdata.hpp"
15
#include "config.hpp"
16
#include "errors.hpp"
17
#include "stack_ptr.hpp"
18
#include "cache-t.hpp"
19
#include "file_util.hpp"
20
#include "file_data_util.hpp"
21
#include "vararray.hpp"
22
23
#include "iostream.hpp"
24
25
#include "gettext.h"
26
27
namespace acommon {
28
29
  typedef unsigned char  byte;
30
  typedef unsigned char  Uni8;
31
  typedef unsigned short Uni16;
32
  typedef unsigned int   Uni32;
33
34
35
  //////////////////////////////////////////////////////////////////////
36
  //////////////////////////////////////////////////////////////////////
37
  //
38
  // Lookups
39
  //
40
  //////////////////////////////////////////////////////////////////////
41
  //////////////////////////////////////////////////////////////////////
42
43
  //////////////////////////////////////////////////////////////////////
44
  //
45
  // ToUniLookup
46
  //
47
48
  class ToUniLookup 
49
  {
50
    Uni32 data[256];
51
    static const Uni32 npos = (Uni32)(-1);
52
  public:
53
    void reset();
54
1.43M
    Uni32 operator[] (char key) const {return data[(unsigned char)key];}
55
0
    bool have(char key) const {return data[(unsigned char)key] != npos;}
56
    bool insert(char key, Uni32 value);
57
  };
58
59
  void ToUniLookup::reset() 
60
40
  {
61
10.2k
    for (int i = 0; i != 256; ++i)
62
10.2k
      data[i] = npos;
63
40
  }
64
65
  bool ToUniLookup::insert(char key, Uni32 value)
66
8.70k
  {
67
8.70k
    if (data[(unsigned char)key] != npos) 
68
0
      return false;
69
8.70k
    data[(unsigned char)key] = value;
70
8.70k
    return true;
71
8.70k
  }
72
73
  //////////////////////////////////////////////////////////////////////
74
  //
75
  // FromUniLookup
76
  //
77
78
  // Assumes that the maximum number of items in the table is 256
79
  // Also assumes (unsigned char)i == i % 256
80
81
  // Based on the iso-8859-* character sets it is very fast, almost all
82
  // lookups involving no more than 2 comparisons.
83
  // NO looks ups involded more than 3 compassions.
84
  // Also, no division (or modules) is done whatsoever.
85
86
87
  struct UniItem {
88
    Uni32 key;
89
    char  value;
90
  };
91
92
  class FromUniLookup 
93
  {
94
  private:
95
    static const Uni32 npos = (Uni32)(-1);
96
    UniItem * overflow_end;
97
  
98
    UniItem data[256*4];
99
100
    UniItem overflow[256]; // you can never be too careful;
101
  
102
  public:
103
794
    FromUniLookup() {}
104
    void reset();
105
    inline char operator() (Uni32 key, char unknown = '?') const;
106
    bool insert(Uni32 key, char value);
107
  };
108
109
  void FromUniLookup::reset()
110
40
  {
111
41.0k
    for (unsigned i = 0; i != 256*4; ++i)
112
40.9k
      data[i].key = npos;
113
40
    overflow_end = overflow;
114
40
  }
115
116
  inline char FromUniLookup::operator() (Uni32 k, char unknown) const
117
20.2k
  {
118
20.2k
    const UniItem * i = data + (unsigned char)k * 4;
119
120
20.2k
    if (i->key == k) return i->value;
121
0
    ++i;
122
0
    if (i->key == k) return i->value;
123
0
    ++i;
124
0
    if (i->key == k) return i->value;
125
0
    ++i;
126
0
    if (i->key == k) return i->value;
127
  
128
0
    if (i->key == npos) return unknown;
129
  
130
0
    for(i = overflow; i != overflow_end; ++i)
131
0
      if (i->key == k) return i->value;
132
133
0
    return unknown;
134
0
  }
135
136
  bool FromUniLookup::insert(Uni32 k, char v) 
137
8.70k
  {
138
8.70k
    UniItem * i = data + (unsigned char)k * 4;
139
8.70k
    UniItem * e = i + 4;
140
10.0k
    while (i != e && i->key != npos) {
141
1.30k
      if (i->key == k)
142
0
        return false;
143
1.30k
      ++i;
144
1.30k
    }
145
8.70k
    if (i == e) {
146
0
      for(i = overflow; i != overflow_end; ++i)
147
0
        if (i->key == k) return false;
148
0
    }
149
8.70k
    i->key = k;
150
8.70k
    i->value = v;
151
8.70k
    return true;
152
8.70k
  }
153
154
  //////////////////////////////////////////////////////////////////////
155
  //
156
  // CharLookup
157
  //
158
159
  class CharLookup 
160
  {
161
  private:
162
    int data[256];
163
  public:
164
    void reset();
165
0
    char operator[] (char key) const {return data[(unsigned char)key];}
166
    bool insert(char key, char value);
167
  };
168
169
0
  void CharLookup::reset() {
170
0
    for (int i = 0; i != 256; ++i) 
171
0
      data[i] = -1;
172
0
  }
173
174
  bool CharLookup::insert(char key, char value) 
175
0
  {
176
0
    if (data[(unsigned char)key] != -1)
177
0
      return false;
178
0
    data[(unsigned char)key] = value;
179
0
    return true;
180
0
  }
181
182
  //////////////////////////////////////////////////////////////////////
183
  //
184
  // NormLookup
185
  //
186
187
  template <class T>
188
  struct NormTable
189
  {
190
    static const unsigned struct_size;
191
    unsigned mask;
192
    unsigned height;
193
    unsigned width;
194
    unsigned size;
195
    T * end;
196
    T data[1]; // hack for data[]
197
  };
198
199
  template <class T>
200
  const unsigned NormTable<T>::struct_size = sizeof(NormTable<T>) - 1;
201
202
  template <class T, class From>
203
  struct NormLookupRet
204
  {
205
    const typename T::To   * to;
206
    const From * last;
207
    NormLookupRet(const typename T::To * t, From * l) 
208
38.9M
      : to(t), last(l) {}
acommon::NormLookupRet<acommon::FromUniNormEntry, acommon::FilterChar const>::NormLookupRet(unsigned char const*, acommon::FilterChar const*)
Line
Count
Source
208
14.8M
      : to(t), last(l) {}
acommon::NormLookupRet<acommon::FromUniNormEntry, acommon::FilterChar>::NormLookupRet(unsigned char const*, acommon::FilterChar*)
Line
Count
Source
208
21.8M
      : to(t), last(l) {}
acommon::NormLookupRet<acommon::ToUniNormEntry, char const>::NormLookupRet(unsigned short const*, char const*)
Line
Count
Source
208
2.24M
      : to(t), last(l) {}
209
  };
210
  
211
  template <class T, class From>
212
  static inline NormLookupRet<T,From> norm_lookup(const NormTable<T> * d, 
213
                                                  From * s, From * stop,
214
                                                  const typename T::To * def,
215
                                                  From * prev) 
216
38.9M
  {
217
58.4M
  loop:
218
58.4M
    if (s != stop) {
219
58.4M
      const T * i = d->data + (static_cast<typename T::From>(*s) & d->mask);
220
64.0M
      for (;;) {
221
64.0M
        if (i->from == static_cast<typename T::From>(*s)) {
222
38.8M
          if (i->sub_table) {
223
            // really tail recursion
224
19.5M
            if (i->to[1] != T::to_non_char) {def = i->to; prev = s;}
225
19.5M
            d = (const NormTable<T> *)(i->sub_table);
226
19.5M
            s++;
227
19.5M
            goto loop;
228
19.5M
          } else {
229
19.3M
            return NormLookupRet<T,From>(i->to, s);
230
19.3M
          }
231
38.8M
        } else {
232
25.2M
          i += d->height;
233
25.2M
          if (i >= d->end) break;
234
25.2M
        }
235
64.0M
      }
236
58.4M
    }
237
19.6M
    return NormLookupRet<T,From>(def, prev);
238
58.4M
  }
convert.cpp:acommon::NormLookupRet<acommon::FromUniNormEntry, acommon::FilterChar const> acommon::norm_lookup<acommon::FromUniNormEntry, acommon::FilterChar const>(acommon::NormTable<acommon::FromUniNormEntry> const*, acommon::FilterChar const*, acommon::FilterChar const*, acommon::FromUniNormEntry::To const*, acommon::FilterChar const*)
Line
Count
Source
216
14.8M
  {
217
24.2M
  loop:
218
24.2M
    if (s != stop) {
219
24.2M
      const T * i = d->data + (static_cast<typename T::From>(*s) & d->mask);
220
26.9M
      for (;;) {
221
26.9M
        if (i->from == static_cast<typename T::From>(*s)) {
222
14.8M
          if (i->sub_table) {
223
            // really tail recursion
224
9.38M
            if (i->to[1] != T::to_non_char) {def = i->to; prev = s;}
225
9.38M
            d = (const NormTable<T> *)(i->sub_table);
226
9.38M
            s++;
227
9.38M
            goto loop;
228
9.38M
          } else {
229
5.43M
            return NormLookupRet<T,From>(i->to, s);
230
5.43M
          }
231
14.8M
        } else {
232
12.1M
          i += d->height;
233
12.1M
          if (i >= d->end) break;
234
12.1M
        }
235
26.9M
      }
236
24.2M
    }
237
9.38M
    return NormLookupRet<T,From>(def, prev);
238
24.2M
  }
convert.cpp:acommon::NormLookupRet<acommon::FromUniNormEntry, acommon::FilterChar> acommon::norm_lookup<acommon::FromUniNormEntry, acommon::FilterChar>(acommon::NormTable<acommon::FromUniNormEntry> const*, acommon::FilterChar*, acommon::FilterChar*, acommon::FromUniNormEntry::To const*, acommon::FilterChar*)
Line
Count
Source
216
21.8M
  {
217
32.0M
  loop:
218
32.0M
    if (s != stop) {
219
32.0M
      const T * i = d->data + (static_cast<typename T::From>(*s) & d->mask);
220
34.8M
      for (;;) {
221
34.8M
        if (i->from == static_cast<typename T::From>(*s)) {
222
21.7M
          if (i->sub_table) {
223
            // really tail recursion
224
10.1M
            if (i->to[1] != T::to_non_char) {def = i->to; prev = s;}
225
10.1M
            d = (const NormTable<T> *)(i->sub_table);
226
10.1M
            s++;
227
10.1M
            goto loop;
228
11.6M
          } else {
229
11.6M
            return NormLookupRet<T,From>(i->to, s);
230
11.6M
          }
231
21.7M
        } else {
232
13.0M
          i += d->height;
233
13.0M
          if (i >= d->end) break;
234
13.0M
        }
235
34.8M
      }
236
32.0M
    }
237
10.2M
    return NormLookupRet<T,From>(def, prev);
238
32.0M
  }
convert.cpp:acommon::NormLookupRet<acommon::ToUniNormEntry, char const> acommon::norm_lookup<acommon::ToUniNormEntry, char const>(acommon::NormTable<acommon::ToUniNormEntry> const*, char const*, char const*, acommon::ToUniNormEntry::To const*, char const*)
Line
Count
Source
216
2.24M
  {
217
2.24M
  loop:
218
2.24M
    if (s != stop) {
219
2.24M
      const T * i = d->data + (static_cast<typename T::From>(*s) & d->mask);
220
2.24M
      for (;;) {
221
2.24M
        if (i->from == static_cast<typename T::From>(*s)) {
222
2.24M
          if (i->sub_table) {
223
            // really tail recursion
224
0
            if (i->to[1] != T::to_non_char) {def = i->to; prev = s;}
225
0
            d = (const NormTable<T> *)(i->sub_table);
226
0
            s++;
227
0
            goto loop;
228
2.24M
          } else {
229
2.24M
            return NormLookupRet<T,From>(i->to, s);
230
2.24M
          }
231
2.24M
        } else {
232
0
          i += d->height;
233
0
          if (i >= d->end) break;
234
0
        }
235
2.24M
      }
236
2.24M
    }
237
0
    return NormLookupRet<T,From>(def, prev);
238
2.24M
  }
239
240
  template <class T>
241
  void free_norm_table(NormTable<T> * d)
242
25.4k
  {
243
3.81M
    for (T * cur = d->data; cur != d->end; ++cur) {
244
3.78M
      if (cur->sub_table) 
245
22.6k
        free_norm_table<T>(static_cast<NormTable<T> *>(cur->sub_table));
246
3.78M
    }
247
25.4k
    free(d);
248
25.4k
  }
void acommon::free_norm_table<acommon::FromUniNormEntry>(acommon::NormTable<acommon::FromUniNormEntry>*)
Line
Count
Source
242
24.0k
  {
243
3.45M
    for (T * cur = d->data; cur != d->end; ++cur) {
244
3.42M
      if (cur->sub_table) 
245
22.6k
        free_norm_table<T>(static_cast<NormTable<T> *>(cur->sub_table));
246
3.42M
    }
247
24.0k
    free(d);
248
24.0k
  }
void acommon::free_norm_table<acommon::ToUniNormEntry>(acommon::NormTable<acommon::ToUniNormEntry>*)
Line
Count
Source
242
1.41k
  {
243
363k
    for (T * cur = d->data; cur != d->end; ++cur) {
244
361k
      if (cur->sub_table) 
245
0
        free_norm_table<T>(static_cast<NormTable<T> *>(cur->sub_table));
246
361k
    }
247
1.41k
    free(d);
248
1.41k
  }
249
250
  struct FromUniNormEntry
251
  {
252
    typedef Uni32 From;
253
    Uni32 from;
254
    typedef byte To;
255
    byte  to[4];
256
    static const From from_non_char = (From)(-1);
257
    static const To   to_non_char   = 0x10;
258
    static const unsigned max_to = 4;
259
    void * sub_table;
260
  } 
261
#ifdef __GNUC__    
262
    __attribute__ ((aligned (16)))
263
#endif
264
  ;
265
266
  struct ToUniNormEntry
267
  {
268
    typedef byte From;
269
    byte from;
270
    typedef Uni16 To;
271
    Uni16 to[3];
272
    static const From from_non_char = 0x10;
273
    static const To   to_non_char   = 0x10;
274
    static const unsigned max_to = 3;
275
    void * sub_table;
276
  } 
277
#ifdef __GNUC__    
278
    __attribute__ ((aligned (16)))
279
#endif
280
  ;
281
  
282
  //////////////////////////////////////////////////////////////////////
283
  //
284
  // read in char data
285
  //
286
287
  PosibErr<void> read_in_char_data (const Config & config,
288
                                    ParmStr encoding,
289
                                    ToUniLookup & to,
290
                                    FromUniLookup & from)
291
40
  {
292
40
    to.reset();
293
40
    from.reset();
294
    
295
40
    String dir1,dir2,file_name;
296
40
    fill_data_dir(&config, dir1, dir2);
297
40
    find_file(file_name,dir1,dir2,encoding,".cset");
298
299
40
    FStream data;
300
40
    PosibErrBase err = data.open(file_name, "r");
301
40
    if (err.get_err()) { 
302
6
      char mesg[300];
303
6
      snprintf(mesg, 300, _("This could also mean that the file \"%s\" could not be opened for reading or does not exist."),
304
6
               file_name.c_str());
305
6
      return make_err(unknown_encoding, encoding, mesg);
306
6
    }
307
34
    unsigned chr;
308
34
    Uni32 uni;
309
34
    String line;
310
34
    char * p;
311
102
    do {
312
102
      p = get_nb_line(data, line);
313
102
    } while (*p != '/');
314
8.73k
    for (chr = 0; chr != 256; ++chr) {
315
8.70k
      p = get_nb_line(data, line);
316
8.70k
      if (strtoul(p, 0, 16) != chr)
317
0
        return make_err(bad_file_format, file_name);
318
8.70k
      uni = strtoul(p + 3, 0, 16);
319
8.70k
      to.insert(chr, uni);
320
8.70k
      from.insert(uni, chr);
321
8.70k
    }
322
  
323
34
    return no_err;
324
34
  }
325
326
  //////////////////////////////////////////////////////////////////////
327
  //
328
  // read in norm data
329
  //
330
331
  struct Tally 
332
  {
333
    int size;
334
    Uni32 mask;
335
    int max;
336
    int * data;
337
76.3k
    Tally(int s, int * d) : size(s), mask(s - 1), max(0), data(d) {
338
76.3k
      memset(data, 0, sizeof(int)*size);
339
76.3k
    }
340
3.89M
    void add(Uni32 chr) {
341
3.89M
      Uni32 p = chr & mask;
342
3.89M
      data[p]++;
343
3.89M
      if (data[p] > max) max = data[p];
344
3.89M
    }
345
  };
346
347
# define sanity(check) \
348
7.96M
    if (!(check)) return sanity_fail(__FILE__, FUNC, __LINE__, #check)
349
350
  static PosibErrBase sanity_fail(const char * file, const char * func, 
351
                                  unsigned line, const char * check_str) 
352
0
  {
353
0
    char mesg[500];
354
0
    snprintf(mesg, 500, "%s:%d: %s: Assertion \"%s\" failed.",
355
0
             file,  line, func, check_str);
356
0
    return make_err(bad_input_error, mesg);
357
0
  }
358
# define CREATE_NORM_TABLE(T, in, buf, res) \
359
25.4k
  do { PosibErr<NormTable<T> *> pe( create_norm_table<T>(in,buf) );\
360
25.4k
       if (pe.has_err()) return PosibErrBase(pe); \
361
25.4k
       res = pe.data; } while(false)
362
363
  template <class T>
364
  static PosibErr< NormTable<T> * > create_norm_table(IStream & in, String & buf)
365
25.4k
  {
366
25.4k
    const char FUNC[] = "create_norm_table";
367
25.4k
    const char * p = get_nb_line(in, buf);
368
25.4k
    sanity(*p == 'N');
369
25.4k
    ++p;
370
25.4k
    int size = strtoul(p, (char **)&p, 10);
371
25.4k
    VARARRAY(T, d, size);
372
25.4k
    memset(d, 0, sizeof(T) * size);
373
25.4k
    int sz = 1 << (unsigned)floor(log(size <= 1 ? 1.0 : size - 1)/log(2.0));
374
25.4k
    VARARRAY(int, tally0_d, sz);   Tally tally0(sz,   tally0_d);
375
25.4k
    VARARRAY(int, tally1_d, sz*2); Tally tally1(sz*2, tally1_d);
376
25.4k
    VARARRAY(int, tally2_d, sz*4); Tally tally2(sz*4, tally2_d);
377
25.4k
    T * cur = d;
378
1.32M
    while (p = get_nb_line(in, buf), *p != '.') {
379
1.29M
      sanity(cur - d < size);
380
1.29M
      Uni32 f = strtoul(p, (char **)&p, 16);
381
1.29M
      cur->from = static_cast<typename T::From>(f);
382
1.29M
      sanity(f == cur->from);
383
1.29M
      tally0.add(f);
384
1.29M
      tally1.add(f);
385
1.29M
      tally2.add(f);
386
1.29M
      ++p;
387
1.29M
      sanity(*p == '>');
388
1.29M
      ++p;
389
1.29M
      sanity(*p == ' ');
390
1.29M
      ++p;
391
1.29M
      unsigned i = 0;
392
1.29M
      if (*p != '-') {
393
2.65M
        for (;; ++i) {
394
2.65M
          const char * q = p;
395
2.65M
          Uni32 t = strtoul(p, (char **)&p, 16);
396
2.65M
          if (q == p) break;
397
1.36M
          sanity(i < d->max_to);
398
1.36M
          cur->to[i] = static_cast<typename T::To>(t);
399
1.36M
          sanity(t == static_cast<Uni32>(cur->to[i]));
400
1.36M
        } 
401
1.29M
      } else {
402
0
        cur->to[0] = 0;
403
0
        cur->to[1] = T::to_non_char;
404
0
      }
405
1.29M
      if (*p == ' ') ++p;
406
1.29M
      if (*p == '/') CREATE_NORM_TABLE(T, in, buf, cur->sub_table);
407
1.29M
      ++cur;
408
1.29M
    }
409
25.4k
    sanity(cur - d == size);
410
25.4k
    Tally * which = &tally0;
411
25.4k
    if (which->max > tally1.max) which = &tally1;
412
25.4k
    if (which->max > tally2.max) which = &tally2;
413
25.4k
    NormTable<T> * final = (NormTable<T> *)calloc(1, NormTable<T>::struct_size + 
414
25.4k
                                                  sizeof(T) * which->size * which->max);
415
25.4k
    memset(final, 0, NormTable<T>::struct_size + sizeof(T) * which->size * which->max);
416
25.4k
    final->mask = which->size - 1;
417
25.4k
    final->height = which->size;
418
25.4k
    final->width = which->max;
419
25.4k
    final->end = final->data + which->size * which->max;
420
25.4k
    final->size = size;
421
1.32M
    for (cur = d; cur != d + size; ++cur) {
422
1.29M
      T * dest = final->data + (cur->from & final->mask);
423
1.35M
      while (dest->from != 0) dest += final->height;
424
1.29M
      *dest = *cur;
425
1.29M
      if (dest->from == 0) dest->from = T::from_non_char;
426
1.29M
    }
427
60.0k
    for (T * dest = final->data; dest < final->end; dest += final->height) {
428
34.6k
      if (dest->from == 0 || (dest->from == T::from_non_char && dest->to[0] == 0)) {
429
2.82k
        dest->from = T::from_non_char;
430
2.82k
        dest->to[0] = T::to_non_char;
431
2.82k
      }
432
34.6k
    }
433
25.4k
    return final;
434
25.4k
  }
convert.cpp:acommon::PosibErr<acommon::NormTable<acommon::FromUniNormEntry>*> acommon::create_norm_table<acommon::FromUniNormEntry>(acommon::IStream&, acommon::String&)
Line
Count
Source
365
24.0k
  {
366
24.0k
    const char FUNC[] = "create_norm_table";
367
24.0k
    const char * p = get_nb_line(in, buf);
368
24.0k
    sanity(*p == 'N');
369
24.0k
    ++p;
370
24.0k
    int size = strtoul(p, (char **)&p, 10);
371
24.0k
    VARARRAY(T, d, size);
372
24.0k
    memset(d, 0, sizeof(T) * size);
373
24.0k
    int sz = 1 << (unsigned)floor(log(size <= 1 ? 1.0 : size - 1)/log(2.0));
374
24.0k
    VARARRAY(int, tally0_d, sz);   Tally tally0(sz,   tally0_d);
375
24.0k
    VARARRAY(int, tally1_d, sz*2); Tally tally1(sz*2, tally1_d);
376
24.0k
    VARARRAY(int, tally2_d, sz*4); Tally tally2(sz*4, tally2_d);
377
24.0k
    T * cur = d;
378
960k
    while (p = get_nb_line(in, buf), *p != '.') {
379
936k
      sanity(cur - d < size);
380
936k
      Uni32 f = strtoul(p, (char **)&p, 16);
381
936k
      cur->from = static_cast<typename T::From>(f);
382
936k
      sanity(f == cur->from);
383
936k
      tally0.add(f);
384
936k
      tally1.add(f);
385
936k
      tally2.add(f);
386
936k
      ++p;
387
936k
      sanity(*p == '>');
388
936k
      ++p;
389
936k
      sanity(*p == ' ');
390
936k
      ++p;
391
936k
      unsigned i = 0;
392
936k
      if (*p != '-') {
393
1.89M
        for (;; ++i) {
394
1.89M
          const char * q = p;
395
1.89M
          Uni32 t = strtoul(p, (char **)&p, 16);
396
1.89M
          if (q == p) break;
397
960k
          sanity(i < d->max_to);
398
960k
          cur->to[i] = static_cast<typename T::To>(t);
399
960k
          sanity(t == static_cast<Uni32>(cur->to[i]));
400
960k
        } 
401
936k
      } else {
402
0
        cur->to[0] = 0;
403
0
        cur->to[1] = T::to_non_char;
404
0
      }
405
936k
      if (*p == ' ') ++p;
406
936k
      if (*p == '/') CREATE_NORM_TABLE(T, in, buf, cur->sub_table);
407
936k
      ++cur;
408
936k
    }
409
24.0k
    sanity(cur - d == size);
410
24.0k
    Tally * which = &tally0;
411
24.0k
    if (which->max > tally1.max) which = &tally1;
412
24.0k
    if (which->max > tally2.max) which = &tally2;
413
24.0k
    NormTable<T> * final = (NormTable<T> *)calloc(1, NormTable<T>::struct_size + 
414
24.0k
                                                  sizeof(T) * which->size * which->max);
415
24.0k
    memset(final, 0, NormTable<T>::struct_size + sizeof(T) * which->size * which->max);
416
24.0k
    final->mask = which->size - 1;
417
24.0k
    final->height = which->size;
418
24.0k
    final->width = which->max;
419
24.0k
    final->end = final->data + which->size * which->max;
420
24.0k
    final->size = size;
421
960k
    for (cur = d; cur != d + size; ++cur) {
422
936k
      T * dest = final->data + (cur->from & final->mask);
423
989k
      while (dest->from != 0) dest += final->height;
424
936k
      *dest = *cur;
425
936k
      if (dest->from == 0) dest->from = T::from_non_char;
426
936k
    }
427
57.2k
    for (T * dest = final->data; dest < final->end; dest += final->height) {
428
33.2k
      if (dest->from == 0 || (dest->from == T::from_non_char && dest->to[0] == 0)) {
429
1.41k
        dest->from = T::from_non_char;
430
1.41k
        dest->to[0] = T::to_non_char;
431
1.41k
      }
432
33.2k
    }
433
24.0k
    return final;
434
24.0k
  }
convert.cpp:acommon::PosibErr<acommon::NormTable<acommon::ToUniNormEntry>*> acommon::create_norm_table<acommon::ToUniNormEntry>(acommon::IStream&, acommon::String&)
Line
Count
Source
365
1.41k
  {
366
1.41k
    const char FUNC[] = "create_norm_table";
367
1.41k
    const char * p = get_nb_line(in, buf);
368
1.41k
    sanity(*p == 'N');
369
1.41k
    ++p;
370
1.41k
    int size = strtoul(p, (char **)&p, 10);
371
1.41k
    VARARRAY(T, d, size);
372
1.41k
    memset(d, 0, sizeof(T) * size);
373
1.41k
    int sz = 1 << (unsigned)floor(log(size <= 1 ? 1.0 : size - 1)/log(2.0));
374
1.41k
    VARARRAY(int, tally0_d, sz);   Tally tally0(sz,   tally0_d);
375
1.41k
    VARARRAY(int, tally1_d, sz*2); Tally tally1(sz*2, tally1_d);
376
1.41k
    VARARRAY(int, tally2_d, sz*4); Tally tally2(sz*4, tally2_d);
377
1.41k
    T * cur = d;
378
363k
    while (p = get_nb_line(in, buf), *p != '.') {
379
361k
      sanity(cur - d < size);
380
361k
      Uni32 f = strtoul(p, (char **)&p, 16);
381
361k
      cur->from = static_cast<typename T::From>(f);
382
361k
      sanity(f == cur->from);
383
361k
      tally0.add(f);
384
361k
      tally1.add(f);
385
361k
      tally2.add(f);
386
361k
      ++p;
387
361k
      sanity(*p == '>');
388
361k
      ++p;
389
361k
      sanity(*p == ' ');
390
361k
      ++p;
391
361k
      unsigned i = 0;
392
361k
      if (*p != '-') {
393
761k
        for (;; ++i) {
394
761k
          const char * q = p;
395
761k
          Uni32 t = strtoul(p, (char **)&p, 16);
396
761k
          if (q == p) break;
397
399k
          sanity(i < d->max_to);
398
399k
          cur->to[i] = static_cast<typename T::To>(t);
399
399k
          sanity(t == static_cast<Uni32>(cur->to[i]));
400
399k
        } 
401
361k
      } else {
402
0
        cur->to[0] = 0;
403
0
        cur->to[1] = T::to_non_char;
404
0
      }
405
361k
      if (*p == ' ') ++p;
406
361k
      if (*p == '/') CREATE_NORM_TABLE(T, in, buf, cur->sub_table);
407
361k
      ++cur;
408
361k
    }
409
1.41k
    sanity(cur - d == size);
410
1.41k
    Tally * which = &tally0;
411
1.41k
    if (which->max > tally1.max) which = &tally1;
412
1.41k
    if (which->max > tally2.max) which = &tally2;
413
1.41k
    NormTable<T> * final = (NormTable<T> *)calloc(1, NormTable<T>::struct_size + 
414
1.41k
                                                  sizeof(T) * which->size * which->max);
415
1.41k
    memset(final, 0, NormTable<T>::struct_size + sizeof(T) * which->size * which->max);
416
1.41k
    final->mask = which->size - 1;
417
1.41k
    final->height = which->size;
418
1.41k
    final->width = which->max;
419
1.41k
    final->end = final->data + which->size * which->max;
420
1.41k
    final->size = size;
421
363k
    for (cur = d; cur != d + size; ++cur) {
422
361k
      T * dest = final->data + (cur->from & final->mask);
423
361k
      while (dest->from != 0) dest += final->height;
424
361k
      *dest = *cur;
425
361k
      if (dest->from == 0) dest->from = T::from_non_char;
426
361k
    }
427
2.82k
    for (T * dest = final->data; dest < final->end; dest += final->height) {
428
1.41k
      if (dest->from == 0 || (dest->from == T::from_non_char && dest->to[0] == 0)) {
429
1.41k
        dest->from = T::from_non_char;
430
1.41k
        dest->to[0] = T::to_non_char;
431
1.41k
      }
432
1.41k
    }
433
1.41k
    return final;
434
1.41k
  }
435
436
  static PosibErr<void> init_norm_tables(FStream & in, NormTables * d) 
437
707
  {
438
707
    const char FUNC[] = "init_norm_tables";
439
707
    String l;
440
707
    get_nb_line(in, l);
441
707
    remove_comments(l);
442
707
    sanity (l == "INTERNAL");
443
707
    get_nb_line(in, l);
444
707
    remove_comments(l);
445
707
    sanity (l == "/");
446
707
    CREATE_NORM_TABLE(FromUniNormEntry, in, l, d->internal);
447
707
    get_nb_line(in, l);
448
707
    remove_comments(l);
449
707
    sanity (l == "STRICT");
450
707
    char * p = get_nb_line(in, l);
451
707
    remove_comments(l);
452
707
    if (l == "/") {
453
707
      CREATE_NORM_TABLE(FromUniNormEntry, in, l, d->strict_d);
454
707
      d->strict = d->strict_d;
455
707
    } else {
456
0
      sanity(*p == '=');
457
0
      ++p; ++p;
458
0
      sanity(strcmp(p, "INTERNAL") == 0);
459
0
      d->strict = d->internal;
460
0
    }
461
2.82k
    while (get_nb_line(in, l)) {
462
2.12k
      remove_comments(l);
463
2.12k
      d->to_uni.push_back(NormTables::ToUniTable());
464
2.12k
      NormTables::ToUniTable & e = d->to_uni.back();
465
2.12k
      e.name.resize(l.size());
466
9.19k
      for (unsigned i = 0; i != l.size(); ++i)
467
7.07k
        e.name[i] = asc_tolower(l[i]);
468
2.12k
      char * p = get_nb_line(in, l);
469
2.12k
      remove_comments(l);
470
2.12k
      if (l == "/") {
471
1.41k
        CREATE_NORM_TABLE(ToUniNormEntry, in, l, e.data);
472
1.41k
        e.ptr = e.data;
473
1.41k
      } else {
474
707
        sanity(*p == '=');
475
707
        ++p; ++p;
476
2.82k
        for (char * q = p; *q; ++q) *q = asc_tolower(*q);
477
707
        Vector<NormTables::ToUniTable>::iterator i = d->to_uni.begin();
478
1.41k
        while (i->name != p && i != d->to_uni.end()) ++i;
479
707
        sanity(i != d->to_uni.end());
480
707
        e.ptr = i->ptr;
481
707
        get_nb_line(in, l);
482
707
      }
483
2.12k
    }  
484
707
    return no_err;
485
707
  }
486
487
  PosibErr<NormTables *> NormTables::get_new(const String & encoding, 
488
                                             const Config * config)
489
707
  {
490
707
    String dir1,dir2,file_name;
491
707
    fill_data_dir(config, dir1, dir2);
492
707
    find_file(file_name,dir1,dir2,encoding,".cmap");
493
    
494
707
    FStream in;
495
707
    PosibErrBase err = in.open(file_name, "r");
496
707
    if (err.get_err()) { 
497
0
      char mesg[300];
498
0
      snprintf(mesg, 300, _("This could also mean that the file \"%s\" could not be opened for reading or does not exist."),
499
0
               file_name.c_str());
500
0
      return make_err(unknown_encoding, encoding, mesg); // FIXME
501
0
    }
502
503
707
    NormTables * d = new NormTables;
504
707
    d->key = encoding;
505
707
    err = init_norm_tables(in, d);
506
707
    if (err.has_err()) {
507
0
      return make_err(bad_file_format, file_name, err.get_err()->mesg);
508
0
    }
509
510
707
    return d;
511
512
707
  }
513
514
  NormTables::~NormTables()
515
707
  {
516
707
    free_norm_table<FromUniNormEntry>(internal);
517
707
    if (strict_d)
518
707
      free_norm_table<FromUniNormEntry>(strict_d);
519
2.82k
    for (unsigned i = 0; i != to_uni.size(); ++i) {
520
2.12k
      if (to_uni[i].data)
521
1.41k
        free_norm_table<ToUniNormEntry>(to_uni[i].data);
522
2.12k
    }
523
707
  }
524
525
  //////////////////////////////////////////////////////////////////////
526
  //////////////////////////////////////////////////////////////////////
527
  //
528
  //  Convert
529
  //
530
  //////////////////////////////////////////////////////////////////////
531
  //////////////////////////////////////////////////////////////////////
532
533
534
  bool operator== (const Convert & rhs, const Convert & lhs)
535
0
  {
536
0
    return strcmp(rhs.in_code(), lhs.in_code()) == 0
537
0
      && strcmp(rhs.out_code(), lhs.out_code()) == 0;
538
0
  }
539
540
  //////////////////////////////////////////////////////////////////////
541
  //
542
  // Trivial Conversion
543
  //
544
545
  const char * unsupported_null_term_wide_string_msg =
546
    "Null-terminated wide-character strings unsupported when used this way.";
547
548
  template <typename Chr>
549
  struct DecodeDirect : public Decode 
550
  {
551
759
    DecodeDirect() {type_width = sizeof(Chr);}
acommon::DecodeDirect<unsigned char>::DecodeDirect()
Line
Count
Source
551
675
    DecodeDirect() {type_width = sizeof(Chr);}
acommon::DecodeDirect<unsigned short>::DecodeDirect()
Line
Count
Source
551
16
    DecodeDirect() {type_width = sizeof(Chr);}
acommon::DecodeDirect<unsigned int>::DecodeDirect()
Line
Count
Source
551
68
    DecodeDirect() {type_width = sizeof(Chr);}
552
173k
    void decode(const char * in0, int size, FilterCharVector & out) const {
553
173k
      const Chr * in = reinterpret_cast<const Chr *>(in0);
554
173k
      if (size == -sizeof(Chr)) {
555
635k
        for (;*in; ++in)
556
473k
          out.append(*in, sizeof(Chr));
557
162k
      } else if (size <= -1) {
558
0
        fprintf(stderr, "%s\n", unsupported_null_term_wide_string_msg);
559
0
        abort();
560
11.7k
      } else {
561
11.7k
        const Chr * stop = reinterpret_cast<const Chr *>(in0) + size/sizeof(Chr);
562
37.1M
        for (;in != stop; ++in)
563
37.1M
          out.append(*in, sizeof(Chr));
564
11.7k
      }
565
173k
    }
acommon::DecodeDirect<unsigned char>::decode(char const*, int, acommon::FilterCharVector&) const
Line
Count
Source
552
171k
    void decode(const char * in0, int size, FilterCharVector & out) const {
553
171k
      const Chr * in = reinterpret_cast<const Chr *>(in0);
554
171k
      if (size == -sizeof(Chr)) {
555
635k
        for (;*in; ++in)
556
473k
          out.append(*in, sizeof(Chr));
557
162k
      } else if (size <= -1) {
558
0
        fprintf(stderr, "%s\n", unsupported_null_term_wide_string_msg);
559
0
        abort();
560
9.33k
      } else {
561
9.33k
        const Chr * stop = reinterpret_cast<const Chr *>(in0) + size/sizeof(Chr);
562
36.9M
        for (;in != stop; ++in)
563
36.9M
          out.append(*in, sizeof(Chr));
564
9.33k
      }
565
171k
    }
acommon::DecodeDirect<unsigned short>::decode(char const*, int, acommon::FilterCharVector&) const
Line
Count
Source
552
1.02k
    void decode(const char * in0, int size, FilterCharVector & out) const {
553
1.02k
      const Chr * in = reinterpret_cast<const Chr *>(in0);
554
1.02k
      if (size == -sizeof(Chr)) {
555
0
        for (;*in; ++in)
556
0
          out.append(*in, sizeof(Chr));
557
1.02k
      } else if (size <= -1) {
558
0
        fprintf(stderr, "%s\n", unsupported_null_term_wide_string_msg);
559
0
        abort();
560
1.02k
      } else {
561
1.02k
        const Chr * stop = reinterpret_cast<const Chr *>(in0) + size/sizeof(Chr);
562
73.4k
        for (;in != stop; ++in)
563
72.3k
          out.append(*in, sizeof(Chr));
564
1.02k
      }
565
1.02k
    }
acommon::DecodeDirect<unsigned int>::decode(char const*, int, acommon::FilterCharVector&) const
Line
Count
Source
552
1.40k
    void decode(const char * in0, int size, FilterCharVector & out) const {
553
1.40k
      const Chr * in = reinterpret_cast<const Chr *>(in0);
554
1.40k
      if (size == -sizeof(Chr)) {
555
0
        for (;*in; ++in)
556
0
          out.append(*in, sizeof(Chr));
557
1.40k
      } else if (size <= -1) {
558
0
        fprintf(stderr, "%s\n", unsupported_null_term_wide_string_msg);
559
0
        abort();
560
1.40k
      } else {
561
1.40k
        const Chr * stop = reinterpret_cast<const Chr *>(in0) + size/sizeof(Chr);
562
167k
        for (;in != stop; ++in)
563
166k
          out.append(*in, sizeof(Chr));
564
1.40k
      }
565
1.40k
    }
566
    PosibErr<void> decode_ec(const char * in0, int size, 
567
0
                             FilterCharVector & out, ParmStr) const {
568
0
      DecodeDirect::decode(in0, size, out);
569
0
      return no_err;
570
0
    }
Unexecuted instantiation: acommon::DecodeDirect<unsigned char>::decode_ec(char const*, int, acommon::FilterCharVector&, acommon::ParmString const&) const
Unexecuted instantiation: acommon::DecodeDirect<unsigned short>::decode_ec(char const*, int, acommon::FilterCharVector&, acommon::ParmString const&) const
Unexecuted instantiation: acommon::DecodeDirect<unsigned int>::decode_ec(char const*, int, acommon::FilterCharVector&, acommon::ParmString const&) const
571
  };
572
573
  template <typename Chr>
574
  struct EncodeDirect : public Encode
575
  {
576
786
    EncodeDirect() {type_width = sizeof(Chr);}
acommon::EncodeDirect<unsigned char>::EncodeDirect()
Line
Count
Source
576
675
    EncodeDirect() {type_width = sizeof(Chr);}
acommon::EncodeDirect<unsigned short>::EncodeDirect()
Line
Count
Source
576
16
    EncodeDirect() {type_width = sizeof(Chr);}
acommon::EncodeDirect<unsigned int>::EncodeDirect()
Line
Count
Source
576
95
    EncodeDirect() {type_width = sizeof(Chr);}
577
    void encode(const FilterChar * in, const FilterChar * stop, 
578
650k
                CharVector & out) const {
579
3.49M
      for (; in != stop; ++in) {
580
2.84M
        Chr c = in->chr;
581
2.84M
        if (c != in->chr) c = '?';
582
2.84M
        out.append(&c, sizeof(Chr));
583
2.84M
      }
584
650k
    }
acommon::EncodeDirect<unsigned char>::encode(acommon::FilterChar const*, acommon::FilterChar const*, acommon::String&) const
Line
Count
Source
578
488k
                CharVector & out) const {
579
2.86M
      for (; in != stop; ++in) {
580
2.37M
        Chr c = in->chr;
581
2.37M
        if (c != in->chr) c = '?';
582
2.37M
        out.append(&c, sizeof(Chr));
583
2.37M
      }
584
488k
    }
acommon::EncodeDirect<unsigned short>::encode(acommon::FilterChar const*, acommon::FilterChar const*, acommon::String&) const
Line
Count
Source
578
80.8k
                CharVector & out) const {
579
361k
      for (; in != stop; ++in) {
580
280k
        Chr c = in->chr;
581
280k
        if (c != in->chr) c = '?';
582
280k
        out.append(&c, sizeof(Chr));
583
280k
      }
584
80.8k
    }
acommon::EncodeDirect<unsigned int>::encode(acommon::FilterChar const*, acommon::FilterChar const*, acommon::String&) const
Line
Count
Source
578
80.6k
                CharVector & out) const {
579
271k
      for (; in != stop; ++in) {
580
190k
        Chr c = in->chr;
581
190k
        if (c != in->chr) c = '?';
582
190k
        out.append(&c, sizeof(Chr));
583
190k
      }
584
80.6k
    }
585
    PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
586
2.81k
                             CharVector & out, ParmStr orig) const {
587
16.1k
      for (; in != stop; ++in) {
588
13.3k
        Chr c = in->chr;
589
13.3k
        if (c != in->chr) {
590
0
          char m[70];
591
0
          snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
592
0
          return make_err(invalid_string, orig, m);
593
0
        }
594
        
595
13.3k
        out.append(&c, sizeof(Chr));
596
13.3k
      }
597
2.81k
      return no_err;
598
2.81k
    }
acommon::EncodeDirect<unsigned char>::encode_ec(acommon::FilterChar const*, acommon::FilterChar const*, acommon::String&, acommon::ParmString const&) const
Line
Count
Source
586
1.08k
                             CharVector & out, ParmStr orig) const {
587
3.25k
      for (; in != stop; ++in) {
588
2.17k
        Chr c = in->chr;
589
2.17k
        if (c != in->chr) {
590
0
          char m[70];
591
0
          snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
592
0
          return make_err(invalid_string, orig, m);
593
0
        }
594
        
595
2.17k
        out.append(&c, sizeof(Chr));
596
2.17k
      }
597
1.08k
      return no_err;
598
1.08k
    }
Unexecuted instantiation: acommon::EncodeDirect<unsigned short>::encode_ec(acommon::FilterChar const*, acommon::FilterChar const*, acommon::String&, acommon::ParmString const&) const
acommon::EncodeDirect<unsigned int>::encode_ec(acommon::FilterChar const*, acommon::FilterChar const*, acommon::String&, acommon::ParmString const&) const
Line
Count
Source
586
1.73k
                             CharVector & out, ParmStr orig) const {
587
12.9k
      for (; in != stop; ++in) {
588
11.1k
        Chr c = in->chr;
589
11.1k
        if (c != in->chr) {
590
0
          char m[70];
591
0
          snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
592
0
          return make_err(invalid_string, orig, m);
593
0
        }
594
        
595
11.1k
        out.append(&c, sizeof(Chr));
596
11.1k
      }
597
1.73k
      return no_err;
598
1.73k
    }
599
47
    bool encode(FilterChar * &, FilterChar * &, FilterCharVector &) const {
600
47
      return true;
601
47
    }
acommon::EncodeDirect<unsigned char>::encode(acommon::FilterChar*&, acommon::FilterChar*&, acommon::FilterCharVector&) const
Line
Count
Source
599
47
    bool encode(FilterChar * &, FilterChar * &, FilterCharVector &) const {
600
47
      return true;
601
47
    }
Unexecuted instantiation: acommon::EncodeDirect<unsigned short>::encode(acommon::FilterChar*&, acommon::FilterChar*&, acommon::FilterCharVector&) const
Unexecuted instantiation: acommon::EncodeDirect<unsigned int>::encode(acommon::FilterChar*&, acommon::FilterChar*&, acommon::FilterCharVector&) const
602
  };
603
604
  template <typename Chr>
605
  struct ConvDirect : public DirectConv
606
  {
607
38
    ConvDirect() {type_width = sizeof(Chr);}
Unexecuted instantiation: acommon::ConvDirect<unsigned short>::ConvDirect()
Unexecuted instantiation: acommon::ConvDirect<unsigned int>::ConvDirect()
acommon::ConvDirect<char>::ConvDirect()
Line
Count
Source
607
38
    ConvDirect() {type_width = sizeof(Chr);}
608
15.7k
    void convert(const char * in0, int size, CharVector & out) const {
609
15.7k
      if (size == -sizeof(Chr)) {
610
15.4k
        const Chr * in = reinterpret_cast<const Chr *>(in0);
611
64.2k
        for (;*in != 0; ++in)
612
48.8k
          out.append(in, sizeof(Chr));
613
15.4k
      } else if (size <= -1) {
614
0
        fprintf(stderr, "%s\n", unsupported_null_term_wide_string_msg);
615
0
        abort();
616
278
      } else {
617
278
        out.append(in0, size);
618
278
      }
619
15.7k
    }
Unexecuted instantiation: acommon::ConvDirect<unsigned short>::convert(char const*, int, acommon::String&) const
Unexecuted instantiation: acommon::ConvDirect<unsigned int>::convert(char const*, int, acommon::String&) const
acommon::ConvDirect<char>::convert(char const*, int, acommon::String&) const
Line
Count
Source
608
15.7k
    void convert(const char * in0, int size, CharVector & out) const {
609
15.7k
      if (size == -sizeof(Chr)) {
610
15.4k
        const Chr * in = reinterpret_cast<const Chr *>(in0);
611
64.2k
        for (;*in != 0; ++in)
612
48.8k
          out.append(in, sizeof(Chr));
613
15.4k
      } else if (size <= -1) {
614
0
        fprintf(stderr, "%s\n", unsupported_null_term_wide_string_msg);
615
0
        abort();
616
278
      } else {
617
278
        out.append(in0, size);
618
278
      }
619
15.7k
    }
620
    PosibErr<void> convert_ec(const char * in0, int size, 
621
0
                              CharVector & out, ParmStr) const {
622
0
      ConvDirect::convert(in0, size, out);
623
0
      return no_err;
624
0
    }
Unexecuted instantiation: acommon::ConvDirect<unsigned short>::convert_ec(char const*, int, acommon::String&, acommon::ParmString const&) const
Unexecuted instantiation: acommon::ConvDirect<unsigned int>::convert_ec(char const*, int, acommon::String&, acommon::ParmString const&) const
Unexecuted instantiation: acommon::ConvDirect<char>::convert_ec(char const*, int, acommon::String&, acommon::ParmString const&) const
625
  };
626
627
  //////////////////////////////////////////////////////////////////////
628
  //
629
  //  Lookup Conversion
630
  //
631
632
  struct DecodeLookup : public Decode 
633
  {
634
    ToUniLookup lookup;
635
23
    PosibErr<void> init(ParmStr code, const Config & c) {
636
23
      FromUniLookup unused;
637
23
      return read_in_char_data(c, code, lookup, unused);
638
23
    }
639
161
    void decode(const char * in, int size, FilterCharVector & out) const {
640
161
      if (size == -1) {
641
0
        for (;*in; ++in)
642
0
          out.append(lookup[*in]);
643
161
      } else {
644
161
        const char * stop = in + size;
645
1.43M
        for (;in != stop; ++in)
646
1.43M
          out.append(lookup[*in]);
647
161
      }
648
161
    }
649
    PosibErr<void> decode_ec(const char * in, int size, 
650
0
                             FilterCharVector & out, ParmStr) const {
651
0
      DecodeLookup::decode(in, size, out);
652
0
      return no_err;
653
0
    }
654
  };
655
656
  struct DecodeNormLookup : public Decode 
657
  {
658
    typedef ToUniNormEntry E;
659
    NormTable<E> * data;
660
6.32k
    DecodeNormLookup(NormTable<E> * d) : data(d) {}
661
    // must be null terminated
662
    // FIXME: Why must it be null terminated?
663
491k
    void decode(const char * in, int size, FilterCharVector & out) const {
664
491k
      const char * stop = in + size; // will work even if size -1
665
2.73M
      while (in != stop) {
666
2.73M
        if (*in == 0) {
667
491k
          if (size == -1) break;
668
0
          out.append(0);
669
0
          ++in;
670
2.24M
        } else {
671
2.24M
          NormLookupRet<E,const char> ret = norm_lookup<E>(data, in, stop, 0, in);
672
4.49M
          for (unsigned i = 0; i < E::max_to && ret.to[i]; ++i)
673
2.24M
            out.append(ret.to[i]);
674
2.24M
          in = ret.last + 1;
675
2.24M
        }
676
2.73M
      }
677
491k
    }
678
    PosibErr<void> decode_ec(const char * in, int size, 
679
0
                             FilterCharVector & out, ParmStr) const {
680
0
      DecodeNormLookup::decode(in, size, out);
681
0
      return no_err;
682
0
    }
683
  };
684
685
  struct EncodeLookup : public Encode 
686
  {
687
    FromUniLookup lookup;
688
    PosibErr<void> init(ParmStr code, const Config & c) 
689
17
      {ToUniLookup unused;
690
17
      return read_in_char_data(c, code, unused, lookup);}
691
    void encode(const FilterChar * in, const FilterChar * stop, 
692
5.61k
                CharVector & out) const {
693
25.8k
      for (; in != stop; ++in) {
694
20.2k
        out.append(lookup(*in));
695
20.2k
      }
696
5.61k
    }
697
    PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
698
0
                             CharVector & out, ParmStr orig) const {
699
0
      for (; in != stop; ++in) {
700
0
        char c = lookup(*in, '\0');
701
0
        if (c == '\0' && in->chr != 0) {
702
0
          char m[70];
703
0
          snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
704
0
          return make_err(invalid_string, orig, m);
705
0
        }
706
0
        out.append(c);
707
0
      }
708
0
      return no_err;
709
0
    }
710
    bool encode(FilterChar * & in0, FilterChar * & stop,
711
0
                FilterCharVector & out) const {
712
0
      FilterChar * in = in0;
713
0
      for (; in != stop; ++in)
714
0
        *in = lookup(*in);
715
0
      return true;
716
0
    }
717
  };
718
719
  struct EncodeNormLookup : public Encode 
720
  {
721
    typedef FromUniNormEntry E;
722
    NormTable<E> * data;
723
2.10k
    EncodeNormLookup(NormTable<E> * d) : data(d) {}
724
    // *stop must equal 0
725
    void encode(const FilterChar * in, const FilterChar * stop, 
726
8.82k
                CharVector & out) const {
727
14.7M
      while (in < stop) {
728
14.7M
        if (*in == 0) {
729
0
          out.append('\0');
730
0
          ++in;
731
14.7M
        } else {
732
14.7M
          NormLookupRet<E,const FilterChar> ret = norm_lookup<E>(data, in, stop, (const byte *)"?", in);
733
29.6M
          for (unsigned i = 0; i < E::max_to && ret.to[i]; ++i)
734
14.8M
            out.append(ret.to[i]);
735
14.7M
          in = ret.last + 1;
736
14.7M
        }
737
14.7M
      }
738
8.82k
    }
739
    PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
740
16.0k
                             CharVector & out, ParmStr orig) const {
741
48.0k
      while (in < stop) {
742
32.0k
        if (*in == 0) {
743
0
          out.append('\0');
744
0
          ++in;
745
32.0k
        } else {
746
32.0k
          NormLookupRet<E,const FilterChar> ret = norm_lookup<E>(data, in, stop, 0, in);
747
32.0k
          if (ret.to == 0) {
748
0
            char m[70];
749
0
            snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
750
0
            return make_err(invalid_string, orig, m);
751
0
          }
752
64.0k
          for (unsigned i = 0; i < E::max_to && ret.to[i]; ++i)
753
32.0k
            out.append(ret.to[i]);
754
32.0k
          in = ret.last + 1;
755
32.0k
        }
756
32.0k
      }
757
16.0k
      return no_err;
758
16.0k
    }
759
    bool encode(FilterChar * & in, FilterChar * & stop,
760
688
                FilterCharVector & buf) const {
761
688
      buf.clear();
762
23.1M
      while (in < stop) {
763
23.1M
        if (*in == 0) {
764
1.30M
          buf.append(FilterChar(0));
765
1.30M
          ++in;
766
21.8M
        } else {
767
21.8M
          NormLookupRet<E,FilterChar> ret = norm_lookup<E>(data, in, stop, (const byte *)"?", in);
768
21.8M
          const FilterChar * end = ret.last + 1;
769
21.8M
          unsigned width = 0;
770
43.7M
          for (; in != end; ++in) width += in->width;
771
21.8M
          buf.append(FilterChar(ret.to[0], width));
772
21.8M
          for (unsigned i = 1; i < E::max_to && ret.to[i]; ++i) {
773
34.3k
            buf.append(FilterChar(ret.to[i],0));
774
34.3k
          }
775
21.8M
        }
776
23.1M
      }
777
688
      buf.append(0);
778
688
      in = buf.pbegin();
779
688
      stop = buf.pend();
780
688
      return true;
781
688
    }
782
  };
783
784
  //////////////////////////////////////////////////////////////////////
785
  //
786
  //  UTF8
787
  //
788
  
789
#define get_check_next \
790
592
  if (in == stop) goto error;          \
791
592
  c = *in;                             \
792
592
  if ((c & 0xC0/*1100 0000*/) != 0x80/*10xx xxxx*/) goto error;\
793
592
  ++in;                                \
794
589
  u <<= 6;                             \
795
589
  u |= c & 0x3F/*0011 1111*/;          \
796
589
  ++w;
797
798
  static inline FilterChar from_utf8 (const char * & in, const char * stop = 0,
799
                                      Uni32 err_char = '?')
800
46.8k
  {
801
46.8k
    Uni32 u = (Uni32)(-1);
802
46.8k
    FilterChar::Width w = 1;
803
804
    // the first char is guaranteed not to be off the end
805
46.8k
    char c = *in;
806
46.8k
    ++in;
807
808
46.8k
    if ((c & 0x80/*1000 0000*/) == 0x00/*0xxx xxx*/) {
809
46.5k
      u = c;
810
46.5k
    } else if ((c & 0xE0/*1110 0000*/) == 0xC0/*110x xxxx*/) { // 2-byte wide
811
154
      u  = c & 0x1F/*0001 1111*/;
812
305
      get_check_next;
813
305
    } else if ((c & 0xF0/*1111 0000*/) == 0xE0/*1110 xxxx*/) { // 3-byte wide
814
120
      u  = c & 0x0F/*0000 1111*/;
815
240
      get_check_next;
816
240
      get_check_next;
817
240
    } else if ((c & 0xF8/*1111 1000*/) == 0xF0/*1111 0xxx*/) { // 4-byte wide
818
66
      u  = c & 0x07/*0000 0111*/;
819
132
      get_check_next;
820
132
      get_check_next;
821
132
      get_check_next;
822
132
    } else {
823
1
      goto error;
824
1
    }
825
826
46.8k
    return FilterChar(u, w);
827
4
  error:
828
4
    return FilterChar(err_char, w);
829
46.8k
  }
830
831
  static inline void to_utf8 (FilterChar in, CharVector & out)
832
0
  {
833
0
    FilterChar::Chr c = in;
834
    
835
0
    if (c < 0x80) {
836
0
      out.append(c);
837
0
    }
838
0
    else if (c < 0x800) {
839
0
      out.append(0xC0 | (c>>6));
840
0
      out.append(0x80 | (c & 0x3F));
841
0
    }
842
0
    else if (c < 0x10000) {
843
0
      out.append(0xE0 | (c>>12));
844
0
      out.append(0x80 | (c>>6 & 0x3F));
845
0
      out.append(0x80 | (c & 0x3F));
846
0
    }
847
0
    else if (c < 0x200000) {
848
0
      out.append(0xF0 | (c>>18));
849
0
      out.append(0x80 | (c>>12 & 0x3F));
850
0
      out.append(0x80 | (c>>6 & 0x3F));
851
0
      out.append(0x80 | (c & 0x3F));
852
0
    }
853
0
  }
854
  
855
  struct DecodeUtf8 : public Decode 
856
  {
857
    ToUniLookup lookup;
858
0
    void decode(const char * in, int size, FilterCharVector & out) const {
859
0
      if (size == -1) {
860
0
        while (*in)
861
0
          out.append(from_utf8(in));
862
0
      } else {
863
0
        const char * stop = in + size;
864
0
        while (in != stop)
865
0
          out.append(from_utf8(in, stop));
866
0
      }
867
0
    }
868
    PosibErr<void> decode_ec(const char * in, int size, 
869
18.8k
                             FilterCharVector & out, ParmStr orig) const {
870
18.8k
      const char * begin = in;
871
18.8k
      if (size == -1) {
872
14.3k
        while (*in) {
873
12.6k
          FilterChar c = from_utf8(in, 0, (Uni32)-1);
874
12.6k
          if (c == (Uni32)-1) goto error;
875
12.6k
          out.append(c);
876
12.6k
        }
877
17.1k
      } else {
878
17.1k
        const char * stop = in + size;
879
51.3k
        while (in != stop) {
880
34.2k
          FilterChar c = from_utf8(in, stop, (Uni32)-1);
881
34.2k
          if (c == (Uni32)-1) goto error;
882
34.2k
          out.append(c);
883
34.2k
        }
884
17.1k
      }
885
18.8k
      return no_err;
886
4
    error:
887
4
      char m[70];
888
4
      snprintf(m, 70, _("Invalid UTF-8 sequence at position %ld."), (long)(in - begin));
889
4
      return make_err(invalid_string, orig, m);
890
18.8k
    }
891
  };
892
893
  struct EncodeUtf8 : public Encode 
894
  {
895
    FromUniLookup lookup;
896
    void encode(const FilterChar * in, const FilterChar * stop, 
897
0
                CharVector & out) const {
898
0
      for (; in != stop; ++in) {
899
0
        to_utf8(*in, out);
900
0
      }
901
0
    }
902
    PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
903
0
                             CharVector & out, ParmStr) const {
904
0
      for (; in != stop; ++in) {
905
0
        to_utf8(*in, out);
906
0
      }
907
0
      return no_err;
908
0
    }
909
  };
910
911
  //////////////////////////////////////////////////////////////////////
912
  //
913
  // Cache
914
  //
915
916
  static GlobalCache<Decode> decode_cache("decode");
917
  static GlobalCache<Encode> encode_cache("encode");
918
  static GlobalCache<NormTables> norm_tables_cache("norm_tables");
919
  
920
  //////////////////////////////////////////////////////////////////////
921
  //
922
  // new_aspell_convert
923
  //
924
925
  void Convert::generic_convert(const char * in, int size, CharVector & out)
926
1.78k
  {
927
1.78k
    buf_.clear();
928
1.78k
    decode_->decode(in, size, buf_);
929
1.78k
    FilterChar * start = buf_.pbegin();
930
1.78k
    FilterChar * stop = buf_.pend();
931
1.78k
    if (!filter.empty())
932
1.78k
      filter.process(start, stop);
933
1.78k
    encode_->encode(start, stop, out);
934
1.78k
  }
935
936
  const char * fix_encoding_str(ParmStr enc, String & buf)
937
32.3k
  {
938
32.3k
    buf.clear();
939
32.3k
    buf.reserve(enc.size() + 1);
940
320k
    for (size_t i = 0; i != enc.size(); ++i)
941
288k
      buf.push_back(asc_tolower(enc[i]));
942
943
32.3k
    if (strncmp(buf.c_str(), "iso8859", 7) == 0)
944
2.24k
      buf.insert(buf.begin() + 3, '-'); // For backwards compatibility
945
    
946
32.3k
    if (buf == "ascii" || buf == "ansi_x3.4-1968")
947
0
      return "iso-8859-1";
948
32.3k
    else if (buf == "machine unsigned 16" || buf == "utf-16")
949
32
      return "ucs-2";
950
32.3k
    else if (buf == "machine unsigned 32" || buf == "utf-32")
951
136
      return "ucs-4";
952
32.1k
    else
953
32.1k
      return buf.c_str();
954
32.3k
  }
955
956
  bool ascii_encoding(const Config & c, ParmStr enc0)
957
1.75k
  {
958
1.75k
    if (enc0.empty()) return true;
959
1.75k
    if (enc0 == "ANSI_X3.4-1968" 
960
1.75k
        || enc0 == "ASCII" || enc0 == "ascii") return true;
961
0
    String buf;
962
0
    const char * enc = fix_encoding_str(enc0, buf);
963
0
    if (strcmp(enc, "utf-8") == 0 
964
0
        || strcmp(enc, "ucs-2") == 0 
965
0
        || strcmp(enc, "ucs-4") == 0) return false;
966
0
    String dir1,dir2,file_name;
967
0
    fill_data_dir(&c, dir1, dir2);
968
0
    file_name << dir1 << enc << ".cset";
969
0
    if (file_exists(file_name)) return false;
970
0
    if (dir1 == dir2) return true;
971
0
    file_name.clear();
972
0
    file_name << dir2 << enc << ".cset";
973
0
    return !file_exists(file_name);
974
0
  }
975
976
  PosibErr<Convert *> internal_new_convert(const Config & c,
977
                                           ConvKey in, 
978
                                           ConvKey out,
979
                                           bool if_needed,
980
                                           Normalize norm)
981
15.0k
  {
982
15.0k
    String in_s;
983
15.0k
    in.val = fix_encoding_str(in.val, in_s);
984
985
15.0k
    String out_s;
986
15.0k
    out.val = fix_encoding_str(out.val, out_s); 
987
988
15.0k
    if (if_needed && in.val == out.val) return 0;
989
990
9.02k
    StackPtr<Convert> conv(new Convert);
991
9.02k
    switch (norm) {
992
27
    case NormNone:
993
27
      RET_ON_ERR(conv->init(c, in, out)); break;
994
2.25k
    case NormFrom:
995
2.25k
      RET_ON_ERR(conv->init_norm_from(c, in, out)); break;
996
6.73k
    case NormTo:
997
6.73k
      RET_ON_ERR(conv->init_norm_to(c, in, out)); break;
998
9.02k
    }
999
9.01k
    return conv.release();
1000
9.02k
  }
1001
1002
  PosibErr<Decode *> Decode::get_new(const ConvKey & k, const Config * c)
1003
1.53k
  {
1004
1.53k
    StackPtr<Decode> ptr;
1005
1.53k
    if (k.val == "iso-8859-1") {
1006
675
      ptr.reset(new DecodeDirect<Uni8>);
1007
861
    } else if (k.val == "ucs-2") {
1008
16
      if (k.allow_ucs)
1009
16
        ptr.reset(new DecodeDirect<Uni16>);
1010
0
      else
1011
0
        return make_err(encoding_not_supported, k.val);
1012
845
    } else if (k.val == "ucs-4") {
1013
68
      if (k.allow_ucs)
1014
68
        ptr.reset(new DecodeDirect<Uni32>);
1015
0
      else
1016
0
        return make_err(encoding_not_supported, k.val);
1017
777
    } else if (k.val == "utf-8") {
1018
754
      ptr.reset(new DecodeUtf8);
1019
754
    } else {
1020
23
      ptr.reset(new DecodeLookup);
1021
23
    }
1022
1.53k
    RET_ON_ERR(ptr->init(k.val, *c));
1023
1.53k
    ptr->key = k.val;
1024
1.53k
    return ptr.release();
1025
1.53k
  }
1026
1027
  PosibErr<Encode *> Encode::get_new(const ConvKey & k, const Config * c)
1028
1.55k
  {
1029
1.55k
    StackPtr<Encode> ptr;
1030
1.55k
    if (k.val == "iso-8859-1") {
1031
675
      ptr.reset(new EncodeDirect<Uni8>);
1032
882
    } else if (k.val == "ucs-2" && k.allow_ucs) {
1033
16
      if (k.allow_ucs)
1034
16
        ptr.reset(new EncodeDirect<Uni16>);
1035
0
      else
1036
0
        return make_err(encoding_not_supported, k.val);
1037
866
    } else if (k.val == "ucs-4" && k.allow_ucs) {
1038
95
      if (k.allow_ucs)
1039
95
        ptr.reset(new EncodeDirect<Uni32>);
1040
0
      else
1041
0
        return make_err(encoding_not_supported, k.val);
1042
771
    } else if (k.val == "utf-8") {
1043
754
      ptr.reset(new EncodeUtf8);
1044
754
    } else {
1045
17
      ptr.reset(new EncodeLookup);
1046
17
    }
1047
1.55k
    RET_ON_ERR(ptr->init(k.val, *c));
1048
1.55k
    ptr->key = k.val;
1049
1.55k
    return ptr.release();
1050
1.55k
  }
1051
1052
9.02k
  Convert::~Convert() {}
1053
1054
  PosibErr<void> Convert::init(const Config & c, const ConvKey & in, const ConvKey & out)
1055
583
  {
1056
583
    RET_ON_ERR(setup(decode_c, &decode_cache, &c, in));
1057
583
    decode_ = decode_c.get();
1058
583
    RET_ON_ERR(setup(encode_c, &encode_cache, &c, out));
1059
583
    encode_ = encode_c.get();
1060
1061
583
    conv_ = 0;
1062
583
    if (in.val == out.val) {
1063
38
      if (in.val == "ucs-2") {
1064
0
        if (in.allow_ucs) {
1065
0
          conv_ = new ConvDirect<Uni16>;
1066
0
        } else {
1067
0
          return make_err(encoding_not_supported, in.val);
1068
0
        }
1069
38
      } else if (in.val == "ucs-4") {
1070
0
        if (in.allow_ucs) {
1071
0
          conv_ = new ConvDirect<Uni32>;
1072
0
        } else {
1073
0
          return make_err(encoding_not_supported, in.val);
1074
0
        }
1075
38
      } else {
1076
38
        conv_ = new ConvDirect<char>;
1077
38
      }
1078
38
    }
1079
1080
583
    if (conv_)
1081
38
      RET_ON_ERR(conv_->init(decode_, encode_, c));
1082
1083
583
    return no_err;
1084
583
  }
1085
1086
  
1087
  PosibErr<void> Convert::init_norm_from(const Config & c, const ConvKey & in, const ConvKey & out)
1088
2.25k
  {
1089
2.25k
    if (!c.retrieve_bool("normalize") && !c.retrieve_bool("norm-required")) 
1090
141
      return init(c,in,out);
1091
1092
2.11k
    RET_ON_ERR(setup(norm_tables_, &norm_tables_cache, &c, out.val));
1093
1094
2.11k
    RET_ON_ERR(setup(decode_c, &decode_cache, &c, in));
1095
2.10k
    decode_ = decode_c.get();
1096
1097
2.10k
    if (c.retrieve_bool("norm-strict")) {
1098
9
      encode_s = new EncodeNormLookup(norm_tables_->strict);
1099
9
      encode_ = encode_s;
1100
9
      encode_->key = out.val;
1101
9
      encode_->key += ":strict";
1102
2.10k
    } else {
1103
2.10k
      encode_s = new EncodeNormLookup(norm_tables_->internal);
1104
2.10k
      encode_ = encode_s;
1105
2.10k
      encode_->key = out.val;
1106
2.10k
      encode_->key += ":internal";
1107
2.10k
    }
1108
2.10k
    conv_ = 0;
1109
1110
2.10k
    return no_err;
1111
2.11k
  }
1112
1113
  PosibErr<void> Convert::init_norm_to(const Config & c, const ConvKey & in, const ConvKey & out)
1114
6.73k
  {
1115
6.73k
    String norm_form = c.retrieve("norm-form");
1116
6.73k
    if ((!c.retrieve_bool("normalize") || norm_form == "none")
1117
415
        && !c.retrieve_bool("norm-required"))
1118
415
      return init(c,in,out);
1119
6.32k
    if (norm_form == "none" && c.retrieve_bool("norm-required"))
1120
0
      norm_form = "nfc";
1121
1122
6.32k
    RET_ON_ERR(setup(norm_tables_, &norm_tables_cache, &c, in.val));
1123
1124
6.32k
    RET_ON_ERR(setup(encode_c, &encode_cache, &c, out));
1125
6.32k
    encode_ = encode_c.get();
1126
1127
6.32k
    NormTables::ToUni::const_iterator i = norm_tables_->to_uni.begin();
1128
12.6k
    for (; i != norm_tables_->to_uni.end() && i->name != norm_form; ++i);
1129
6.32k
    if (i == norm_tables_->to_uni.end())
1130
0
      return make_err(aerror_bad_value, "norm-form", norm_form, "one of none, nfd, nfc, or comp");
1131
1132
6.32k
    decode_s = new DecodeNormLookup(i->ptr);
1133
6.32k
    decode_ = decode_s;
1134
6.32k
    decode_->key = in.val;
1135
6.32k
    decode_->key += ':';
1136
6.32k
    decode_->key += i->name;
1137
1138
6.32k
    conv_ = 0;
1139
1140
6.32k
    return no_err;
1141
6.32k
  }
1142
1143
  PosibErr<void> MBLen::setup(const Config &, ParmStr enc0)
1144
0
  {
1145
0
    String buf;
1146
0
    const char * enc = fix_encoding_str(enc0,buf);
1147
0
    if      (strcmp(enc, "utf-8") == 0) encoding = UTF8;
1148
0
    else if (strcmp(enc, "ucs-2") == 0) encoding = UCS2;
1149
0
    else if (strcmp(enc, "ucs-4") == 0) encoding = UCS4;
1150
0
    else                                encoding = Other;
1151
0
    return no_err;
1152
0
  }
1153
1154
  unsigned MBLen::operator()(const char * str, const char * stop)
1155
0
  {
1156
0
    unsigned size = 0;
1157
0
    switch (encoding) {
1158
0
    case Other: 
1159
0
      return stop - str;
1160
0
    case UTF8:
1161
0
      for (; str != stop; ++str) {
1162
0
        if ((*str & 0x80) == 0 || (*str & 0xC0) == 0xC0) ++size;
1163
0
      }
1164
0
      return size;
1165
0
    case UCS2:
1166
0
      return (stop - str)/2;
1167
0
    case UCS4:
1168
0
      return (stop - str)/4;
1169
0
    }
1170
0
    return 0;
1171
0
  }
1172
1173
0
  PosibErr<void> unsupported_null_term_wide_string_err_(const char * func) {
1174
0
    static bool reported_to_stderr = false;
1175
0
    PosibErr<void> err = make_err(other_error, unsupported_null_term_wide_string_msg);
1176
0
    if (!reported_to_stderr) {
1177
0
      CERR.printf("ERROR: %s: %s\n", func, unsupported_null_term_wide_string_msg);
1178
0
      reported_to_stderr = true;
1179
0
    }
1180
0
    return err;
1181
0
  }
1182
1183
0
  void unsupported_null_term_wide_string_abort_(const char * func) {
1184
0
    CERR.printf("%s: %s\n", func, unsupported_null_term_wide_string_msg);
1185
0
    abort();
1186
0
  }
1187
 
1188
}