Coverage Report

Created: 2026-07-16 06:06

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
442k
    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
61
  {
61
15.6k
    for (int i = 0; i != 256; ++i)
62
15.6k
      data[i] = npos;
63
61
  }
64
65
  bool ToUniLookup::insert(char key, Uni32 value)
66
11.2k
  {
67
11.2k
    if (data[(unsigned char)key] != npos) 
68
0
      return false;
69
11.2k
    data[(unsigned char)key] = value;
70
11.2k
    return true;
71
11.2k
  }
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
1.02k
    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
61
  {
111
62.5k
    for (unsigned i = 0; i != 256*4; ++i)
112
62.4k
      data[i].key = npos;
113
61
    overflow_end = overflow;
114
61
  }
115
116
  inline char FromUniLookup::operator() (Uni32 k, char unknown) const
117
26.2k
  {
118
26.2k
    const UniItem * i = data + (unsigned char)k * 4;
119
120
26.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
11.2k
  {
138
11.2k
    UniItem * i = data + (unsigned char)k * 4;
139
11.2k
    UniItem * e = i + 4;
140
12.6k
    while (i != e && i->key != npos) {
141
1.40k
      if (i->key == k)
142
0
        return false;
143
1.40k
      ++i;
144
1.40k
    }
145
11.2k
    if (i == e) {
146
0
      for(i = overflow; i != overflow_end; ++i)
147
0
        if (i->key == k) return false;
148
0
    }
149
11.2k
    i->key = k;
150
11.2k
    i->value = v;
151
11.2k
    return true;
152
11.2k
  }
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
32.1M
      : to(t), last(l) {}
acommon::NormLookupRet<acommon::FromUniNormEntry, acommon::FilterChar const>::NormLookupRet(unsigned char const*, acommon::FilterChar const*)
Line
Count
Source
208
11.6M
      : to(t), last(l) {}
acommon::NormLookupRet<acommon::FromUniNormEntry, acommon::FilterChar>::NormLookupRet(unsigned char const*, acommon::FilterChar*)
Line
Count
Source
208
18.3M
      : to(t), last(l) {}
acommon::NormLookupRet<acommon::ToUniNormEntry, char const>::NormLookupRet(unsigned short const*, char const*)
Line
Count
Source
208
2.20M
      : 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
32.1M
  {
217
41.6M
  loop:
218
41.6M
    if (s != stop) {
219
41.6M
      const T * i = d->data + (static_cast<typename T::From>(*s) & d->mask);
220
48.7M
      for (;;) {
221
48.7M
        if (i->from == static_cast<typename T::From>(*s)) {
222
32.1M
          if (i->sub_table) {
223
            // really tail recursion
224
9.44M
            if (i->to[1] != T::to_non_char) {def = i->to; prev = s;}
225
9.44M
            d = (const NormTable<T> *)(i->sub_table);
226
9.44M
            s++;
227
9.44M
            goto loop;
228
22.6M
          } else {
229
22.6M
            return NormLookupRet<T,From>(i->to, s);
230
22.6M
          }
231
32.1M
        } else {
232
16.6M
          i += d->height;
233
16.6M
          if (i >= d->end) break;
234
16.6M
        }
235
48.7M
      }
236
41.6M
    }
237
9.52M
    return NormLookupRet<T,From>(def, prev);
238
41.6M
  }
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
11.6M
  {
217
16.2M
  loop:
218
16.2M
    if (s != stop) {
219
16.2M
      const T * i = d->data + (static_cast<typename T::From>(*s) & d->mask);
220
19.7M
      for (;;) {
221
19.7M
        if (i->from == static_cast<typename T::From>(*s)) {
222
11.6M
          if (i->sub_table) {
223
            // really tail recursion
224
4.61M
            if (i->to[1] != T::to_non_char) {def = i->to; prev = s;}
225
4.61M
            d = (const NormTable<T> *)(i->sub_table);
226
4.61M
            s++;
227
4.61M
            goto loop;
228
7.04M
          } else {
229
7.04M
            return NormLookupRet<T,From>(i->to, s);
230
7.04M
          }
231
11.6M
        } else {
232
8.07M
          i += d->height;
233
8.07M
          if (i >= d->end) break;
234
8.07M
        }
235
19.7M
      }
236
16.2M
    }
237
4.61M
    return NormLookupRet<T,From>(def, prev);
238
16.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
18.3M
  {
217
23.1M
  loop:
218
23.1M
    if (s != stop) {
219
23.1M
      const T * i = d->data + (static_cast<typename T::From>(*s) & d->mask);
220
26.8M
      for (;;) {
221
26.8M
        if (i->from == static_cast<typename T::From>(*s)) {
222
18.2M
          if (i->sub_table) {
223
            // really tail recursion
224
4.83M
            if (i->to[1] != T::to_non_char) {def = i->to; prev = s;}
225
4.83M
            d = (const NormTable<T> *)(i->sub_table);
226
4.83M
            s++;
227
4.83M
            goto loop;
228
13.4M
          } else {
229
13.4M
            return NormLookupRet<T,From>(i->to, s);
230
13.4M
          }
231
18.2M
        } else {
232
8.61M
          i += d->height;
233
8.61M
          if (i >= d->end) break;
234
8.61M
        }
235
26.8M
      }
236
23.1M
    }
237
4.91M
    return NormLookupRet<T,From>(def, prev);
238
23.1M
  }
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.20M
  {
217
2.20M
  loop:
218
2.20M
    if (s != stop) {
219
2.20M
      const T * i = d->data + (static_cast<typename T::From>(*s) & d->mask);
220
2.20M
      for (;;) {
221
2.20M
        if (i->from == static_cast<typename T::From>(*s)) {
222
2.20M
          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.20M
          } else {
229
2.20M
            return NormLookupRet<T,From>(i->to, s);
230
2.20M
          }
231
2.20M
        } else {
232
0
          i += d->height;
233
0
          if (i >= d->end) break;
234
0
        }
235
2.20M
      }
236
2.20M
    }
237
0
    return NormLookupRet<T,From>(def, prev);
238
2.20M
  }
239
240
  template <class T>
241
  void free_norm_table(NormTable<T> * d)
242
32.9k
  {
243
4.93M
    for (T * cur = d->data; cur != d->end; ++cur) {
244
4.89M
      if (cur->sub_table) 
245
29.2k
        free_norm_table<T>(static_cast<NormTable<T> *>(cur->sub_table));
246
4.89M
    }
247
32.9k
    free(d);
248
32.9k
  }
void acommon::free_norm_table<acommon::FromUniNormEntry>(acommon::NormTable<acommon::FromUniNormEntry>*)
Line
Count
Source
242
31.0k
  {
243
4.46M
    for (T * cur = d->data; cur != d->end; ++cur) {
244
4.42M
      if (cur->sub_table) 
245
29.2k
        free_norm_table<T>(static_cast<NormTable<T> *>(cur->sub_table));
246
4.42M
    }
247
31.0k
    free(d);
248
31.0k
  }
void acommon::free_norm_table<acommon::ToUniNormEntry>(acommon::NormTable<acommon::ToUniNormEntry>*)
Line
Count
Source
242
1.82k
  {
243
469k
    for (T * cur = d->data; cur != d->end; ++cur) {
244
467k
      if (cur->sub_table) 
245
0
        free_norm_table<T>(static_cast<NormTable<T> *>(cur->sub_table));
246
467k
    }
247
1.82k
    free(d);
248
1.82k
  }
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
61
  {
292
61
    to.reset();
293
61
    from.reset();
294
    
295
61
    String dir1,dir2,file_name;
296
61
    fill_data_dir(&config, dir1, dir2);
297
61
    find_file(file_name,dir1,dir2,encoding,".cset");
298
299
61
    FStream data;
300
61
    PosibErrBase err = data.open(file_name, "r");
301
61
    if (err.get_err()) { 
302
17
      char mesg[300];
303
17
      snprintf(mesg, 300, _("This could also mean that the file \"%s\" could not be opened for reading or does not exist."),
304
17
               file_name.c_str());
305
17
      return make_err(unknown_encoding, encoding, mesg);
306
17
    }
307
44
    unsigned chr;
308
44
    Uni32 uni;
309
44
    String line;
310
44
    char * p;
311
132
    do {
312
132
      p = get_nb_line(data, line);
313
132
    } while (*p != '/');
314
11.3k
    for (chr = 0; chr != 256; ++chr) {
315
11.2k
      p = get_nb_line(data, line);
316
11.2k
      if (strtoul(p, 0, 16) != chr)
317
0
        return make_err(bad_file_format, file_name);
318
11.2k
      uni = strtoul(p + 3, 0, 16);
319
11.2k
      to.insert(chr, uni);
320
11.2k
      from.insert(uni, chr);
321
11.2k
    }
322
  
323
44
    return no_err;
324
44
  }
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
98.7k
    Tally(int s, int * d) : size(s), mask(s - 1), max(0), data(d) {
338
98.7k
      memset(data, 0, sizeof(int)*size);
339
98.7k
    }
340
5.03M
    void add(Uni32 chr) {
341
5.03M
      Uni32 p = chr & mask;
342
5.03M
      data[p]++;
343
5.03M
      if (data[p] > max) max = data[p];
344
5.03M
    }
345
  };
346
347
# define sanity(check) \
348
8.62M
    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
32.9k
  do { PosibErr<NormTable<T> *> pe( create_norm_table<T>(in,buf) );\
360
32.9k
       if (pe.has_err()) return PosibErrBase(pe); \
361
32.9k
       res = pe.data; } while(false)
362
363
  template <class T>
364
  static PosibErr< NormTable<T> * > create_norm_table(IStream & in, String & buf)
365
32.9k
  {
366
32.9k
    const char FUNC[] = "create_norm_table";
367
32.9k
    const char * p = get_nb_line(in, buf);
368
32.9k
    sanity(*p == 'N');
369
32.9k
    ++p;
370
32.9k
    int size = strtoul(p, (char **)&p, 10);
371
32.9k
    VARARRAY(T, d, size);
372
32.9k
    memset(d, 0, sizeof(T) * size);
373
32.9k
    int sz = 1 << (unsigned)floor(log(size <= 1 ? 1.0 : size - 1)/log(2.0));
374
32.9k
    VARARRAY(int, tally0_d, sz);   Tally tally0(sz,   tally0_d);
375
32.9k
    VARARRAY(int, tally1_d, sz*2); Tally tally1(sz*2, tally1_d);
376
32.9k
    VARARRAY(int, tally2_d, sz*4); Tally tally2(sz*4, tally2_d);
377
32.9k
    T * cur = d;
378
1.71M
    while (p = get_nb_line(in, buf), *p != '.') {
379
1.67M
      Uni32 f = strtoul(p, (char **)&p, 16);
380
1.67M
      cur->from = static_cast<typename T::From>(f);
381
1.67M
      sanity(f == cur->from);
382
1.67M
      tally0.add(f);
383
1.67M
      tally1.add(f);
384
1.67M
      tally2.add(f);
385
1.67M
      ++p;
386
1.67M
      sanity(*p == '>');
387
1.67M
      ++p;
388
1.67M
      sanity(*p == ' ');
389
1.67M
      ++p;
390
1.67M
      unsigned i = 0;
391
1.67M
      if (*p != '-') {
392
3.43M
        for (;; ++i) {
393
3.43M
          const char * q = p;
394
3.43M
          Uni32 t = strtoul(p, (char **)&p, 16);
395
3.43M
          if (q == p) break;
396
1.75M
          sanity(i < d->max_to);
397
1.75M
          cur->to[i] = static_cast<typename T::To>(t);
398
1.75M
          sanity(t == static_cast<Uni32>(cur->to[i]));
399
1.75M
        } 
400
1.67M
      } else {
401
0
        cur->to[0] = 0;
402
0
        cur->to[1] = T::to_non_char;
403
0
      }
404
1.67M
      if (*p == ' ') ++p;
405
1.67M
      if (*p == '/') CREATE_NORM_TABLE(T, in, buf, cur->sub_table);
406
1.67M
      ++cur;
407
1.67M
    }
408
32.9k
    sanity(cur - d == size);
409
32.9k
    Tally * which = &tally0;
410
32.9k
    if (which->max > tally1.max) which = &tally1;
411
32.9k
    if (which->max > tally2.max) which = &tally2;
412
32.9k
    NormTable<T> * final = (NormTable<T> *)calloc(1, NormTable<T>::struct_size + 
413
32.9k
                                                  sizeof(T) * which->size * which->max);
414
32.9k
    memset(final, 0, NormTable<T>::struct_size + sizeof(T) * which->size * which->max);
415
32.9k
    final->mask = which->size - 1;
416
32.9k
    final->height = which->size;
417
32.9k
    final->width = which->max;
418
32.9k
    final->end = final->data + which->size * which->max;
419
32.9k
    final->size = size;
420
1.71M
    for (cur = d; cur != d + size; ++cur) {
421
1.67M
      T * dest = final->data + (cur->from & final->mask);
422
1.74M
      while (dest->from != 0) dest += final->height;
423
1.67M
      *dest = *cur;
424
1.67M
      if (dest->from == 0) dest->from = T::from_non_char;
425
1.67M
    }
426
77.6k
    for (T * dest = final->data; dest < final->end; dest += final->height) {
427
44.7k
      if (dest->from == 0 || (dest->from == T::from_non_char && dest->to[0] == 0)) {
428
3.65k
        dest->from = T::from_non_char;
429
3.65k
        dest->to[0] = T::to_non_char;
430
3.65k
      }
431
44.7k
    }
432
32.9k
    return final;
433
32.9k
  }
convert.cpp:acommon::PosibErr<acommon::NormTable<acommon::FromUniNormEntry>*> acommon::create_norm_table<acommon::FromUniNormEntry>(acommon::IStream&, acommon::String&)
Line
Count
Source
365
31.0k
  {
366
31.0k
    const char FUNC[] = "create_norm_table";
367
31.0k
    const char * p = get_nb_line(in, buf);
368
31.0k
    sanity(*p == 'N');
369
31.0k
    ++p;
370
31.0k
    int size = strtoul(p, (char **)&p, 10);
371
31.0k
    VARARRAY(T, d, size);
372
31.0k
    memset(d, 0, sizeof(T) * size);
373
31.0k
    int sz = 1 << (unsigned)floor(log(size <= 1 ? 1.0 : size - 1)/log(2.0));
374
31.0k
    VARARRAY(int, tally0_d, sz);   Tally tally0(sz,   tally0_d);
375
31.0k
    VARARRAY(int, tally1_d, sz*2); Tally tally1(sz*2, tally1_d);
376
31.0k
    VARARRAY(int, tally2_d, sz*4); Tally tally2(sz*4, tally2_d);
377
31.0k
    T * cur = d;
378
1.24M
    while (p = get_nb_line(in, buf), *p != '.') {
379
1.21M
      Uni32 f = strtoul(p, (char **)&p, 16);
380
1.21M
      cur->from = static_cast<typename T::From>(f);
381
1.21M
      sanity(f == cur->from);
382
1.21M
      tally0.add(f);
383
1.21M
      tally1.add(f);
384
1.21M
      tally2.add(f);
385
1.21M
      ++p;
386
1.21M
      sanity(*p == '>');
387
1.21M
      ++p;
388
1.21M
      sanity(*p == ' ');
389
1.21M
      ++p;
390
1.21M
      unsigned i = 0;
391
1.21M
      if (*p != '-') {
392
2.45M
        for (;; ++i) {
393
2.45M
          const char * q = p;
394
2.45M
          Uni32 t = strtoul(p, (char **)&p, 16);
395
2.45M
          if (q == p) break;
396
1.24M
          sanity(i < d->max_to);
397
1.24M
          cur->to[i] = static_cast<typename T::To>(t);
398
1.24M
          sanity(t == static_cast<Uni32>(cur->to[i]));
399
1.24M
        } 
400
1.21M
      } else {
401
0
        cur->to[0] = 0;
402
0
        cur->to[1] = T::to_non_char;
403
0
      }
404
1.21M
      if (*p == ' ') ++p;
405
1.21M
      if (*p == '/') CREATE_NORM_TABLE(T, in, buf, cur->sub_table);
406
1.21M
      ++cur;
407
1.21M
    }
408
31.0k
    sanity(cur - d == size);
409
31.0k
    Tally * which = &tally0;
410
31.0k
    if (which->max > tally1.max) which = &tally1;
411
31.0k
    if (which->max > tally2.max) which = &tally2;
412
31.0k
    NormTable<T> * final = (NormTable<T> *)calloc(1, NormTable<T>::struct_size + 
413
31.0k
                                                  sizeof(T) * which->size * which->max);
414
31.0k
    memset(final, 0, NormTable<T>::struct_size + sizeof(T) * which->size * which->max);
415
31.0k
    final->mask = which->size - 1;
416
31.0k
    final->height = which->size;
417
31.0k
    final->width = which->max;
418
31.0k
    final->end = final->data + which->size * which->max;
419
31.0k
    final->size = size;
420
1.24M
    for (cur = d; cur != d + size; ++cur) {
421
1.21M
      T * dest = final->data + (cur->from & final->mask);
422
1.27M
      while (dest->from != 0) dest += final->height;
423
1.21M
      *dest = *cur;
424
1.21M
      if (dest->from == 0) dest->from = T::from_non_char;
425
1.21M
    }
426
74.0k
    for (T * dest = final->data; dest < final->end; dest += final->height) {
427
42.9k
      if (dest->from == 0 || (dest->from == T::from_non_char && dest->to[0] == 0)) {
428
1.82k
        dest->from = T::from_non_char;
429
1.82k
        dest->to[0] = T::to_non_char;
430
1.82k
      }
431
42.9k
    }
432
31.0k
    return final;
433
31.0k
  }
convert.cpp:acommon::PosibErr<acommon::NormTable<acommon::ToUniNormEntry>*> acommon::create_norm_table<acommon::ToUniNormEntry>(acommon::IStream&, acommon::String&)
Line
Count
Source
365
1.82k
  {
366
1.82k
    const char FUNC[] = "create_norm_table";
367
1.82k
    const char * p = get_nb_line(in, buf);
368
1.82k
    sanity(*p == 'N');
369
1.82k
    ++p;
370
1.82k
    int size = strtoul(p, (char **)&p, 10);
371
1.82k
    VARARRAY(T, d, size);
372
1.82k
    memset(d, 0, sizeof(T) * size);
373
1.82k
    int sz = 1 << (unsigned)floor(log(size <= 1 ? 1.0 : size - 1)/log(2.0));
374
1.82k
    VARARRAY(int, tally0_d, sz);   Tally tally0(sz,   tally0_d);
375
1.82k
    VARARRAY(int, tally1_d, sz*2); Tally tally1(sz*2, tally1_d);
376
1.82k
    VARARRAY(int, tally2_d, sz*4); Tally tally2(sz*4, tally2_d);
377
1.82k
    T * cur = d;
378
469k
    while (p = get_nb_line(in, buf), *p != '.') {
379
467k
      Uni32 f = strtoul(p, (char **)&p, 16);
380
467k
      cur->from = static_cast<typename T::From>(f);
381
467k
      sanity(f == cur->from);
382
467k
      tally0.add(f);
383
467k
      tally1.add(f);
384
467k
      tally2.add(f);
385
467k
      ++p;
386
467k
      sanity(*p == '>');
387
467k
      ++p;
388
467k
      sanity(*p == ' ');
389
467k
      ++p;
390
467k
      unsigned i = 0;
391
467k
      if (*p != '-') {
392
984k
        for (;; ++i) {
393
984k
          const char * q = p;
394
984k
          Uni32 t = strtoul(p, (char **)&p, 16);
395
984k
          if (q == p) break;
396
516k
          sanity(i < d->max_to);
397
516k
          cur->to[i] = static_cast<typename T::To>(t);
398
516k
          sanity(t == static_cast<Uni32>(cur->to[i]));
399
516k
        } 
400
467k
      } else {
401
0
        cur->to[0] = 0;
402
0
        cur->to[1] = T::to_non_char;
403
0
      }
404
467k
      if (*p == ' ') ++p;
405
467k
      if (*p == '/') CREATE_NORM_TABLE(T, in, buf, cur->sub_table);
406
467k
      ++cur;
407
467k
    }
408
1.82k
    sanity(cur - d == size);
409
1.82k
    Tally * which = &tally0;
410
1.82k
    if (which->max > tally1.max) which = &tally1;
411
1.82k
    if (which->max > tally2.max) which = &tally2;
412
1.82k
    NormTable<T> * final = (NormTable<T> *)calloc(1, NormTable<T>::struct_size + 
413
1.82k
                                                  sizeof(T) * which->size * which->max);
414
1.82k
    memset(final, 0, NormTable<T>::struct_size + sizeof(T) * which->size * which->max);
415
1.82k
    final->mask = which->size - 1;
416
1.82k
    final->height = which->size;
417
1.82k
    final->width = which->max;
418
1.82k
    final->end = final->data + which->size * which->max;
419
1.82k
    final->size = size;
420
469k
    for (cur = d; cur != d + size; ++cur) {
421
467k
      T * dest = final->data + (cur->from & final->mask);
422
467k
      while (dest->from != 0) dest += final->height;
423
467k
      *dest = *cur;
424
467k
      if (dest->from == 0) dest->from = T::from_non_char;
425
467k
    }
426
3.65k
    for (T * dest = final->data; dest < final->end; dest += final->height) {
427
1.82k
      if (dest->from == 0 || (dest->from == T::from_non_char && dest->to[0] == 0)) {
428
1.82k
        dest->from = T::from_non_char;
429
1.82k
        dest->to[0] = T::to_non_char;
430
1.82k
      }
431
1.82k
    }
432
1.82k
    return final;
433
1.82k
  }
434
435
  static PosibErr<void> init_norm_tables(FStream & in, NormTables * d) 
436
914
  {
437
914
    const char FUNC[] = "init_norm_tables";
438
914
    String l;
439
914
    get_nb_line(in, l);
440
914
    remove_comments(l);
441
914
    sanity (l == "INTERNAL");
442
914
    get_nb_line(in, l);
443
914
    remove_comments(l);
444
914
    sanity (l == "/");
445
914
    CREATE_NORM_TABLE(FromUniNormEntry, in, l, d->internal);
446
914
    get_nb_line(in, l);
447
914
    remove_comments(l);
448
914
    sanity (l == "STRICT");
449
914
    char * p = get_nb_line(in, l);
450
914
    remove_comments(l);
451
914
    if (l == "/") {
452
914
      CREATE_NORM_TABLE(FromUniNormEntry, in, l, d->strict_d);
453
914
      d->strict = d->strict_d;
454
914
    } else {
455
0
      sanity(*p == '=');
456
0
      ++p; ++p;
457
0
      sanity(strcmp(p, "INTERNAL") == 0);
458
0
      d->strict = d->internal;
459
0
    }
460
3.65k
    while (get_nb_line(in, l)) {
461
2.74k
      remove_comments(l);
462
2.74k
      d->to_uni.push_back(NormTables::ToUniTable());
463
2.74k
      NormTables::ToUniTable & e = d->to_uni.back();
464
2.74k
      e.name.resize(l.size());
465
11.8k
      for (unsigned i = 0; i != l.size(); ++i)
466
9.14k
        e.name[i] = asc_tolower(l[i]);
467
2.74k
      char * p = get_nb_line(in, l);
468
2.74k
      remove_comments(l);
469
2.74k
      if (l == "/") {
470
1.82k
        CREATE_NORM_TABLE(ToUniNormEntry, in, l, e.data);
471
1.82k
        e.ptr = e.data;
472
1.82k
      } else {
473
914
        sanity(*p == '=');
474
914
        ++p; ++p;
475
3.65k
        for (char * q = p; *q; ++q) *q = asc_tolower(*q);
476
914
        Vector<NormTables::ToUniTable>::iterator i = d->to_uni.begin();
477
1.82k
        while (i->name != p && i != d->to_uni.end()) ++i;
478
914
        sanity(i != d->to_uni.end());
479
914
        e.ptr = i->ptr;
480
914
        get_nb_line(in, l);
481
914
      }
482
2.74k
    }  
483
914
    return no_err;
484
914
  }
485
486
  PosibErr<NormTables *> NormTables::get_new(const String & encoding, 
487
                                             const Config * config)
488
914
  {
489
914
    String dir1,dir2,file_name;
490
914
    fill_data_dir(config, dir1, dir2);
491
914
    find_file(file_name,dir1,dir2,encoding,".cmap");
492
    
493
914
    FStream in;
494
914
    PosibErrBase err = in.open(file_name, "r");
495
914
    if (err.get_err()) { 
496
0
      char mesg[300];
497
0
      snprintf(mesg, 300, _("This could also mean that the file \"%s\" could not be opened for reading or does not exist."),
498
0
               file_name.c_str());
499
0
      return make_err(unknown_encoding, encoding, mesg); // FIXME
500
0
    }
501
502
914
    NormTables * d = new NormTables;
503
914
    d->key = encoding;
504
914
    err = init_norm_tables(in, d);
505
914
    if (err.has_err()) {
506
0
      return make_err(bad_file_format, file_name, err.get_err()->mesg);
507
0
    }
508
509
914
    return d;
510
511
914
  }
512
513
  NormTables::~NormTables()
514
914
  {
515
914
    free_norm_table<FromUniNormEntry>(internal);
516
914
    if (strict_d)
517
914
      free_norm_table<FromUniNormEntry>(strict_d);
518
3.65k
    for (unsigned i = 0; i != to_uni.size(); ++i) {
519
2.74k
      if (to_uni[i].data)
520
1.82k
        free_norm_table<ToUniNormEntry>(to_uni[i].data);
521
2.74k
    }
522
914
  }
523
524
  //////////////////////////////////////////////////////////////////////
525
  //////////////////////////////////////////////////////////////////////
526
  //
527
  //  Convert
528
  //
529
  //////////////////////////////////////////////////////////////////////
530
  //////////////////////////////////////////////////////////////////////
531
532
533
  bool operator== (const Convert & rhs, const Convert & lhs)
534
0
  {
535
0
    return strcmp(rhs.in_code(), lhs.in_code()) == 0
536
0
      && strcmp(rhs.out_code(), lhs.out_code()) == 0;
537
0
  }
538
539
  //////////////////////////////////////////////////////////////////////
540
  //
541
  // Trivial Conversion
542
  //
543
544
  const char * unsupported_null_term_wide_string_msg =
545
    "Null-terminated wide-character strings unsupported when used this way.";
546
547
  template <typename Chr>
548
  struct DecodeDirect : public Decode 
549
  {
550
965
    DecodeDirect() {type_width = sizeof(Chr);}
acommon::DecodeDirect<unsigned char>::DecodeDirect()
Line
Count
Source
550
829
    DecodeDirect() {type_width = sizeof(Chr);}
acommon::DecodeDirect<unsigned short>::DecodeDirect()
Line
Count
Source
550
28
    DecodeDirect() {type_width = sizeof(Chr);}
acommon::DecodeDirect<unsigned int>::DecodeDirect()
Line
Count
Source
550
108
    DecodeDirect() {type_width = sizeof(Chr);}
551
210k
    void decode(const char * in0, int size, FilterCharVector & out) const {
552
210k
      const Chr * in = reinterpret_cast<const Chr *>(in0);
553
210k
      if (size == -sizeof(Chr)) {
554
890k
        for (;*in; ++in)
555
693k
          out.append(*in, sizeof(Chr));
556
196k
      } else if (size <= -1) {
557
0
        fprintf(stderr, "%s\n", unsupported_null_term_wide_string_msg);
558
0
        abort();
559
14.0k
      } else {
560
14.0k
        const Chr * stop = reinterpret_cast<const Chr *>(in0) + size/sizeof(Chr);
561
32.7M
        for (;in != stop; ++in)
562
32.7M
          out.append(*in, sizeof(Chr));
563
14.0k
      }
564
210k
    }
acommon::DecodeDirect<unsigned char>::decode(char const*, int, acommon::FilterCharVector&) const
Line
Count
Source
551
208k
    void decode(const char * in0, int size, FilterCharVector & out) const {
552
208k
      const Chr * in = reinterpret_cast<const Chr *>(in0);
553
208k
      if (size == -sizeof(Chr)) {
554
890k
        for (;*in; ++in)
555
693k
          out.append(*in, sizeof(Chr));
556
196k
      } else if (size <= -1) {
557
0
        fprintf(stderr, "%s\n", unsupported_null_term_wide_string_msg);
558
0
        abort();
559
11.7k
      } else {
560
11.7k
        const Chr * stop = reinterpret_cast<const Chr *>(in0) + size/sizeof(Chr);
561
32.0M
        for (;in != stop; ++in)
562
32.0M
          out.append(*in, sizeof(Chr));
563
11.7k
      }
564
208k
    }
acommon::DecodeDirect<unsigned short>::decode(char const*, int, acommon::FilterCharVector&) const
Line
Count
Source
551
1.85k
    void decode(const char * in0, int size, FilterCharVector & out) const {
552
1.85k
      const Chr * in = reinterpret_cast<const Chr *>(in0);
553
1.85k
      if (size == -sizeof(Chr)) {
554
0
        for (;*in; ++in)
555
0
          out.append(*in, sizeof(Chr));
556
1.85k
      } else if (size <= -1) {
557
0
        fprintf(stderr, "%s\n", unsupported_null_term_wide_string_msg);
558
0
        abort();
559
1.85k
      } else {
560
1.85k
        const Chr * stop = reinterpret_cast<const Chr *>(in0) + size/sizeof(Chr);
561
500k
        for (;in != stop; ++in)
562
498k
          out.append(*in, sizeof(Chr));
563
1.85k
      }
564
1.85k
    }
acommon::DecodeDirect<unsigned int>::decode(char const*, int, acommon::FilterCharVector&) const
Line
Count
Source
551
483
    void decode(const char * in0, int size, FilterCharVector & out) const {
552
483
      const Chr * in = reinterpret_cast<const Chr *>(in0);
553
483
      if (size == -sizeof(Chr)) {
554
0
        for (;*in; ++in)
555
0
          out.append(*in, sizeof(Chr));
556
483
      } else if (size <= -1) {
557
0
        fprintf(stderr, "%s\n", unsupported_null_term_wide_string_msg);
558
0
        abort();
559
483
      } else {
560
483
        const Chr * stop = reinterpret_cast<const Chr *>(in0) + size/sizeof(Chr);
561
163k
        for (;in != stop; ++in)
562
163k
          out.append(*in, sizeof(Chr));
563
483
      }
564
483
    }
565
    PosibErr<void> decode_ec(const char * in0, int size, 
566
0
                             FilterCharVector & out, ParmStr) const {
567
0
      DecodeDirect::decode(in0, size, out);
568
0
      return no_err;
569
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
570
  };
571
572
  template <typename Chr>
573
  struct EncodeDirect : public Encode
574
  {
575
999
    EncodeDirect() {type_width = sizeof(Chr);}
acommon::EncodeDirect<unsigned char>::EncodeDirect()
Line
Count
Source
575
829
    EncodeDirect() {type_width = sizeof(Chr);}
acommon::EncodeDirect<unsigned short>::EncodeDirect()
Line
Count
Source
575
28
    EncodeDirect() {type_width = sizeof(Chr);}
acommon::EncodeDirect<unsigned int>::EncodeDirect()
Line
Count
Source
575
142
    EncodeDirect() {type_width = sizeof(Chr);}
576
    void encode(const FilterChar * in, const FilterChar * stop, 
577
884k
                CharVector & out) const {
578
3.88M
      for (; in != stop; ++in) {
579
3.00M
        Chr c = in->chr;
580
3.00M
        if (c != in->chr) c = '?';
581
3.00M
        out.append(&c, sizeof(Chr));
582
3.00M
      }
583
884k
    }
acommon::EncodeDirect<unsigned char>::encode(acommon::FilterChar const*, acommon::FilterChar const*, acommon::String&) const
Line
Count
Source
577
688k
                CharVector & out) const {
578
2.99M
      for (; in != stop; ++in) {
579
2.31M
        Chr c = in->chr;
580
2.31M
        if (c != in->chr) c = '?';
581
2.31M
        out.append(&c, sizeof(Chr));
582
2.31M
      }
583
688k
    }
acommon::EncodeDirect<unsigned short>::encode(acommon::FilterChar const*, acommon::FilterChar const*, acommon::String&) const
Line
Count
Source
577
173k
                CharVector & out) const {
578
829k
      for (; in != stop; ++in) {
579
656k
        Chr c = in->chr;
580
656k
        if (c != in->chr) c = '?';
581
656k
        out.append(&c, sizeof(Chr));
582
656k
      }
583
173k
    }
acommon::EncodeDirect<unsigned int>::encode(acommon::FilterChar const*, acommon::FilterChar const*, acommon::String&) const
Line
Count
Source
577
22.6k
                CharVector & out) const {
578
57.7k
      for (; in != stop; ++in) {
579
35.0k
        Chr c = in->chr;
580
35.0k
        if (c != in->chr) c = '?';
581
35.0k
        out.append(&c, sizeof(Chr));
582
35.0k
      }
583
22.6k
    }
584
    PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
585
3.00k
                             CharVector & out, ParmStr orig) const {
586
8.90k
      for (; in != stop; ++in) {
587
5.90k
        Chr c = in->chr;
588
5.90k
        if (c != in->chr) {
589
0
          char m[70];
590
0
          snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
591
0
          return make_err(invalid_string, orig, m);
592
0
        }
593
        
594
5.90k
        out.append(&c, sizeof(Chr));
595
5.90k
      }
596
3.00k
      return no_err;
597
3.00k
    }
acommon::EncodeDirect<unsigned char>::encode_ec(acommon::FilterChar const*, acommon::FilterChar const*, acommon::String&, acommon::ParmString const&) const
Line
Count
Source
585
1.13k
                             CharVector & out, ParmStr orig) const {
586
3.39k
      for (; in != stop; ++in) {
587
2.26k
        Chr c = in->chr;
588
2.26k
        if (c != in->chr) {
589
0
          char m[70];
590
0
          snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
591
0
          return make_err(invalid_string, orig, m);
592
0
        }
593
        
594
2.26k
        out.append(&c, sizeof(Chr));
595
2.26k
      }
596
1.13k
      return no_err;
597
1.13k
    }
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
585
1.87k
                             CharVector & out, ParmStr orig) const {
586
5.51k
      for (; in != stop; ++in) {
587
3.63k
        Chr c = in->chr;
588
3.63k
        if (c != in->chr) {
589
0
          char m[70];
590
0
          snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
591
0
          return make_err(invalid_string, orig, m);
592
0
        }
593
        
594
3.63k
        out.append(&c, sizeof(Chr));
595
3.63k
      }
596
1.87k
      return no_err;
597
1.87k
    }
598
49
    bool encode(FilterChar * &, FilterChar * &, FilterCharVector &) const {
599
49
      return true;
600
49
    }
acommon::EncodeDirect<unsigned char>::encode(acommon::FilterChar*&, acommon::FilterChar*&, acommon::FilterCharVector&) const
Line
Count
Source
598
49
    bool encode(FilterChar * &, FilterChar * &, FilterCharVector &) const {
599
49
      return true;
600
49
    }
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
601
  };
602
603
  template <typename Chr>
604
  struct ConvDirect : public DirectConv
605
  {
606
28
    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
606
28
    ConvDirect() {type_width = sizeof(Chr);}
607
7.92k
    void convert(const char * in0, int size, CharVector & out) const {
608
7.92k
      if (size == -sizeof(Chr)) {
609
7.73k
        const Chr * in = reinterpret_cast<const Chr *>(in0);
610
32.9k
        for (;*in != 0; ++in)
611
25.2k
          out.append(in, sizeof(Chr));
612
7.73k
      } else if (size <= -1) {
613
0
        fprintf(stderr, "%s\n", unsupported_null_term_wide_string_msg);
614
0
        abort();
615
191
      } else {
616
191
        out.append(in0, size);
617
191
      }
618
7.92k
    }
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
607
7.92k
    void convert(const char * in0, int size, CharVector & out) const {
608
7.92k
      if (size == -sizeof(Chr)) {
609
7.73k
        const Chr * in = reinterpret_cast<const Chr *>(in0);
610
32.9k
        for (;*in != 0; ++in)
611
25.2k
          out.append(in, sizeof(Chr));
612
7.73k
      } else if (size <= -1) {
613
0
        fprintf(stderr, "%s\n", unsupported_null_term_wide_string_msg);
614
0
        abort();
615
191
      } else {
616
191
        out.append(in0, size);
617
191
      }
618
7.92k
    }
619
    PosibErr<void> convert_ec(const char * in0, int size, 
620
0
                              CharVector & out, ParmStr) const {
621
0
      ConvDirect::convert(in0, size, out);
622
0
      return no_err;
623
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
624
  };
625
626
  //////////////////////////////////////////////////////////////////////
627
  //
628
  //  Lookup Conversion
629
  //
630
631
  struct DecodeLookup : public Decode 
632
  {
633
    ToUniLookup lookup;
634
39
    PosibErr<void> init(ParmStr code, const Config & c) {
635
39
      FromUniLookup unused;
636
39
      return read_in_char_data(c, code, lookup, unused);
637
39
    }
638
333
    void decode(const char * in, int size, FilterCharVector & out) const {
639
333
      if (size == -1) {
640
0
        for (;*in; ++in)
641
0
          out.append(lookup[*in]);
642
333
      } else {
643
333
        const char * stop = in + size;
644
442k
        for (;in != stop; ++in)
645
442k
          out.append(lookup[*in]);
646
333
      }
647
333
    }
648
    PosibErr<void> decode_ec(const char * in, int size, 
649
0
                             FilterCharVector & out, ParmStr) const {
650
0
      DecodeLookup::decode(in, size, out);
651
0
      return no_err;
652
0
    }
653
  };
654
655
  struct DecodeNormLookup : public Decode 
656
  {
657
    typedef ToUniNormEntry E;
658
    NormTable<E> * data;
659
8.18k
    DecodeNormLookup(NormTable<E> * d) : data(d) {}
660
    // must be null terminated
661
    // FIXME: Why must it be null terminated?
662
692k
    void decode(const char * in, int size, FilterCharVector & out) const {
663
692k
      const char * stop = in + size; // will work even if size -1
664
2.89M
      while (in != stop) {
665
2.89M
        if (*in == 0) {
666
692k
          if (size == -1) break;
667
0
          out.append(0);
668
0
          ++in;
669
2.20M
        } else {
670
2.20M
          NormLookupRet<E,const char> ret = norm_lookup<E>(data, in, stop, 0, in);
671
4.40M
          for (unsigned i = 0; ret.to[i] && i < E::max_to; ++i)
672
2.20M
            out.append(ret.to[i]);
673
2.20M
          in = ret.last + 1;
674
2.20M
        }
675
2.89M
      }
676
692k
    }
677
    PosibErr<void> decode_ec(const char * in, int size, 
678
0
                             FilterCharVector & out, ParmStr) const {
679
0
      DecodeNormLookup::decode(in, size, out);
680
0
      return no_err;
681
0
    }
682
  };
683
684
  struct EncodeLookup : public Encode 
685
  {
686
    FromUniLookup lookup;
687
    PosibErr<void> init(ParmStr code, const Config & c) 
688
22
      {ToUniLookup unused;
689
22
      return read_in_char_data(c, code, unused, lookup);}
690
    void encode(const FilterChar * in, const FilterChar * stop, 
691
6.98k
                CharVector & out) const {
692
33.2k
      for (; in != stop; ++in) {
693
26.2k
        out.append(lookup(*in));
694
26.2k
      }
695
6.98k
    }
696
    PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
697
0
                             CharVector & out, ParmStr orig) const {
698
0
      for (; in != stop; ++in) {
699
0
        char c = lookup(*in, '\0');
700
0
        if (c == '\0' && in->chr != 0) {
701
0
          char m[70];
702
0
          snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
703
0
          return make_err(invalid_string, orig, m);
704
0
        }
705
0
        out.append(c);
706
0
      }
707
0
      return no_err;
708
0
    }
709
    bool encode(FilterChar * & in0, FilterChar * & stop,
710
0
                FilterCharVector & out) const {
711
0
      FilterChar * in = in0;
712
0
      for (; in != stop; ++in)
713
0
        *in = lookup(*in);
714
0
      return true;
715
0
    }
716
  };
717
718
  struct EncodeNormLookup : public Encode 
719
  {
720
    typedef FromUniNormEntry E;
721
    NormTable<E> * data;
722
2.71k
    EncodeNormLookup(NormTable<E> * d) : data(d) {}
723
    // *stop must equal 0
724
    void encode(const FilterChar * in, const FilterChar * stop, 
725
11.2k
                CharVector & out) const {
726
11.6M
      while (in < stop) {
727
11.6M
        if (*in == 0) {
728
0
          out.append('\0');
729
0
          ++in;
730
11.6M
        } else {
731
11.6M
          NormLookupRet<E,const FilterChar> ret = norm_lookup<E>(data, in, stop, (const byte *)"?", in);
732
23.2M
          for (unsigned i = 0; i < E::max_to && ret.to[i]; ++i)
733
11.6M
            out.append(ret.to[i]);
734
11.6M
          in = ret.last + 1;
735
11.6M
        }
736
11.6M
      }
737
11.2k
    }
738
    PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
739
20.4k
                             CharVector & out, ParmStr orig) const {
740
61.2k
      while (in < stop) {
741
40.8k
        if (*in == 0) {
742
0
          out.append('\0');
743
0
          ++in;
744
40.8k
        } else {
745
40.8k
          NormLookupRet<E,const FilterChar> ret = norm_lookup<E>(data, in, stop, 0, in);
746
40.8k
          if (ret.to == 0) {
747
0
            char m[70];
748
0
            snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
749
0
            return make_err(invalid_string, orig, m);
750
0
          }
751
81.6k
          for (unsigned i = 0; i < E::max_to && ret.to[i]; ++i)
752
40.8k
            out.append(ret.to[i]);
753
40.8k
          in = ret.last + 1;
754
40.8k
        }
755
40.8k
      }
756
20.4k
      return no_err;
757
20.4k
    }
758
    bool encode(FilterChar * & in, FilterChar * & stop,
759
877
                FilterCharVector & buf) const {
760
877
      buf.clear();
761
20.5M
      while (in < stop) {
762
20.5M
        if (*in == 0) {
763
2.21M
          buf.append(FilterChar(0));
764
2.21M
          ++in;
765
18.3M
        } else {
766
18.3M
          NormLookupRet<E,FilterChar> ret = norm_lookup<E>(data, in, stop, (const byte *)"?", in);
767
18.3M
          const FilterChar * end = ret.last + 1;
768
18.3M
          unsigned width = 0;
769
36.6M
          for (; in != end; ++in) width += in->width;
770
18.3M
          buf.append(FilterChar(ret.to[0], width));
771
18.3M
          for (unsigned i = 1; i < E::max_to && ret.to[i]; ++i) {
772
48.5k
            buf.append(FilterChar(ret.to[i],0));
773
48.5k
          }
774
18.3M
        }
775
20.5M
      }
776
877
      buf.append(0);
777
877
      in = buf.pbegin();
778
877
      stop = buf.pend();
779
877
      return true;
780
877
    }
781
  };
782
783
  //////////////////////////////////////////////////////////////////////
784
  //
785
  //  UTF8
786
  //
787
  
788
#define get_check_next \
789
656
  if (in == stop) goto error;          \
790
656
  c = *in;                             \
791
656
  if ((c & 0xC0/*1100 0000*/) != 0x80/*10xx xxxx*/) goto error;\
792
656
  ++in;                                \
793
653
  u <<= 6;                             \
794
653
  u |= c & 0x3F/*0011 1111*/;          \
795
653
  ++w;
796
797
  static inline FilterChar from_utf8 (const char * & in, const char * stop = 0,
798
                                      Uni32 err_char = '?')
799
48.1k
  {
800
48.1k
    Uni32 u = (Uni32)(-1);
801
48.1k
    FilterChar::Width w = 1;
802
803
    // the first char is guaranteed not to be off the end
804
48.1k
    char c = *in;
805
48.1k
    ++in;
806
807
48.1k
    if ((c & 0x80/*1000 0000*/) == 0x00/*0xxx xxx*/) {
808
47.8k
      u = c;
809
47.8k
    } else if ((c & 0xE0/*1110 0000*/) == 0xC0/*110x xxxx*/) { // 2-byte wide
810
149
      u  = c & 0x1F/*0001 1111*/;
811
298
      get_check_next;
812
298
    } else if ((c & 0xF0/*1111 0000*/) == 0xE0/*1110 xxxx*/) { // 3-byte wide
813
83
      u  = c & 0x0F/*0000 1111*/;
814
166
      get_check_next;
815
166
      get_check_next;
816
166
    } else if ((c & 0xF8/*1111 1000*/) == 0xF0/*1111 0xxx*/) { // 4-byte wide
817
115
      u  = c & 0x07/*0000 0111*/;
818
229
      get_check_next;
819
229
      get_check_next;
820
226
      get_check_next;
821
224
    } else {
822
2
      goto error;
823
2
    }
824
825
48.1k
    return FilterChar(u, w);
826
5
  error:
827
5
    return FilterChar(err_char, w);
828
48.1k
  }
829
830
  static inline void to_utf8 (FilterChar in, CharVector & out)
831
0
  {
832
0
    FilterChar::Chr c = in;
833
    
834
0
    if (c < 0x80) {
835
0
      out.append(c);
836
0
    }
837
0
    else if (c < 0x800) {
838
0
      out.append(0xC0 | (c>>6));
839
0
      out.append(0x80 | (c & 0x3F));
840
0
    }
841
0
    else if (c < 0x10000) {
842
0
      out.append(0xE0 | (c>>12));
843
0
      out.append(0x80 | (c>>6 & 0x3F));
844
0
      out.append(0x80 | (c & 0x3F));
845
0
    }
846
0
    else if (c < 0x200000) {
847
0
      out.append(0xF0 | (c>>18));
848
0
      out.append(0x80 | (c>>12 & 0x3F));
849
0
      out.append(0x80 | (c>>6 & 0x3F));
850
0
      out.append(0x80 | (c & 0x3F));
851
0
    }
852
0
  }
853
  
854
  struct DecodeUtf8 : public Decode 
855
  {
856
    ToUniLookup lookup;
857
0
    void decode(const char * in, int size, FilterCharVector & out) const {
858
0
      if (size == -1) {
859
0
        while (*in)
860
0
          out.append(from_utf8(in));
861
0
      } else {
862
0
        const char * stop = in + size;
863
0
        while (in != stop)
864
0
          out.append(from_utf8(in, stop));
865
0
      }
866
0
    }
867
    PosibErr<void> decode_ec(const char * in, int size, 
868
23.4k
                             FilterCharVector & out, ParmStr orig) const {
869
23.4k
      const char * begin = in;
870
23.4k
      if (size == -1) {
871
6.97k
        while (*in) {
872
5.09k
          FilterChar c = from_utf8(in, 0, (Uni32)-1);
873
5.09k
          if (c == (Uni32)-1) goto error;
874
5.09k
          out.append(c);
875
5.09k
        }
876
21.5k
      } else {
877
21.5k
        const char * stop = in + size;
878
64.6k
        while (in != stop) {
879
43.0k
          FilterChar c = from_utf8(in, stop, (Uni32)-1);
880
43.0k
          if (c == (Uni32)-1) goto error;
881
43.0k
          out.append(c);
882
43.0k
        }
883
21.5k
      }
884
23.4k
      return no_err;
885
5
    error:
886
5
      char m[70];
887
5
      snprintf(m, 70, _("Invalid UTF-8 sequence at position %ld."), (long)(in - begin));
888
5
      return make_err(invalid_string, orig, m);
889
23.4k
    }
890
  };
891
892
  struct EncodeUtf8 : public Encode 
893
  {
894
    FromUniLookup lookup;
895
    void encode(const FilterChar * in, const FilterChar * stop, 
896
0
                CharVector & out) const {
897
0
      for (; in != stop; ++in) {
898
0
        to_utf8(*in, out);
899
0
      }
900
0
    }
901
    PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
902
0
                             CharVector & out, ParmStr) const {
903
0
      for (; in != stop; ++in) {
904
0
        to_utf8(*in, out);
905
0
      }
906
0
      return no_err;
907
0
    }
908
  };
909
910
  //////////////////////////////////////////////////////////////////////
911
  //
912
  // Cache
913
  //
914
915
  static GlobalCache<Decode> decode_cache("decode");
916
  static GlobalCache<Encode> encode_cache("encode");
917
  static GlobalCache<NormTables> norm_tables_cache("norm_tables");
918
  
919
  //////////////////////////////////////////////////////////////////////
920
  //
921
  // new_aspell_convert
922
  //
923
924
  void Convert::generic_convert(const char * in, int size, CharVector & out)
925
1.79k
  {
926
1.79k
    buf_.clear();
927
1.79k
    decode_->decode(in, size, buf_);
928
1.79k
    FilterChar * start = buf_.pbegin();
929
1.79k
    FilterChar * stop = buf_.pend();
930
1.79k
    if (!filter.empty())
931
1.79k
      filter.process(start, stop);
932
1.79k
    encode_->encode(start, stop, out);
933
1.79k
  }
934
935
  const char * fix_encoding_str(ParmStr enc, String & buf)
936
41.4k
  {
937
41.4k
    buf.clear();
938
41.4k
    buf.reserve(enc.size() + 1);
939
446k
    for (size_t i = 0; i != enc.size(); ++i)
940
404k
      buf.push_back(asc_tolower(enc[i]));
941
942
41.4k
    if (strncmp(buf.c_str(), "iso8859", 7) == 0)
943
2.89k
      buf.insert(buf.begin() + 3, '-'); // For backwards compatibility
944
    
945
41.4k
    if (buf == "ascii" || buf == "ansi_x3.4-1968")
946
0
      return "iso-8859-1";
947
41.4k
    else if (buf == "machine unsigned 16" || buf == "utf-16")
948
56
      return "ucs-2";
949
41.4k
    else if (buf == "machine unsigned 32" || buf == "utf-32")
950
216
      return "ucs-4";
951
41.2k
    else
952
41.2k
      return buf.c_str();
953
41.4k
  }
954
955
  bool ascii_encoding(const Config & c, ParmStr enc0)
956
2.15k
  {
957
2.15k
    if (enc0.empty()) return true;
958
2.15k
    if (enc0 == "ANSI_X3.4-1968" 
959
2.15k
        || enc0 == "ASCII" || enc0 == "ascii") return true;
960
0
    String buf;
961
0
    const char * enc = fix_encoding_str(enc0, buf);
962
0
    if (strcmp(enc, "utf-8") == 0 
963
0
        || strcmp(enc, "ucs-2") == 0 
964
0
        || strcmp(enc, "ucs-4") == 0) return false;
965
0
    String dir1,dir2,file_name;
966
0
    fill_data_dir(&c, dir1, dir2);
967
0
    file_name << dir1 << enc << ".cset";
968
0
    if (file_exists(file_name)) return false;
969
0
    if (dir1 == dir2) return true;
970
0
    file_name.clear();
971
0
    file_name << dir2 << enc << ".cset";
972
0
    return !file_exists(file_name);
973
0
  }
974
975
  PosibErr<Convert *> internal_new_convert(const Config & c,
976
                                           ConvKey in, 
977
                                           ConvKey out,
978
                                           bool if_needed,
979
                                           Normalize norm)
980
19.2k
  {
981
19.2k
    String in_s;
982
19.2k
    in.val = fix_encoding_str(in.val, in_s);
983
984
19.2k
    String out_s;
985
19.2k
    out.val = fix_encoding_str(out.val, out_s); 
986
987
19.2k
    if (if_needed && in.val == out.val) return 0;
988
989
11.5k
    StackPtr<Convert> conv(new Convert);
990
11.5k
    switch (norm) {
991
34
    case NormNone:
992
34
      RET_ON_ERR(conv->init(c, in, out)); break;
993
2.88k
    case NormFrom:
994
2.88k
      RET_ON_ERR(conv->init_norm_from(c, in, out)); break;
995
8.65k
    case NormTo:
996
8.65k
      RET_ON_ERR(conv->init_norm_to(c, in, out)); break;
997
11.5k
    }
998
11.5k
    return conv.release();
999
11.5k
  }
1000
1001
  PosibErr<Decode *> Decode::get_new(const ConvKey & k, const Config * c)
1002
1.97k
  {
1003
1.97k
    StackPtr<Decode> ptr;
1004
1.97k
    if (k.val == "iso-8859-1") {
1005
829
      ptr.reset(new DecodeDirect<Uni8>);
1006
1.14k
    } else if (k.val == "ucs-2") {
1007
28
      if (k.allow_ucs)
1008
28
        ptr.reset(new DecodeDirect<Uni16>);
1009
0
      else
1010
0
        return make_err(encoding_not_supported, k.val);
1011
1.11k
    } else if (k.val == "ucs-4") {
1012
108
      if (k.allow_ucs)
1013
108
        ptr.reset(new DecodeDirect<Uni32>);
1014
0
      else
1015
0
        return make_err(encoding_not_supported, k.val);
1016
1.00k
    } else if (k.val == "utf-8") {
1017
967
      ptr.reset(new DecodeUtf8);
1018
967
    } else {
1019
39
      ptr.reset(new DecodeLookup);
1020
39
    }
1021
1.97k
    RET_ON_ERR(ptr->init(k.val, *c));
1022
1.95k
    ptr->key = k.val;
1023
1.95k
    return ptr.release();
1024
1.97k
  }
1025
1026
  PosibErr<Encode *> Encode::get_new(const ConvKey & k, const Config * c)
1027
1.98k
  {
1028
1.98k
    StackPtr<Encode> ptr;
1029
1.98k
    if (k.val == "iso-8859-1") {
1030
829
      ptr.reset(new EncodeDirect<Uni8>);
1031
1.15k
    } else if (k.val == "ucs-2" && k.allow_ucs) {
1032
28
      if (k.allow_ucs)
1033
28
        ptr.reset(new EncodeDirect<Uni16>);
1034
0
      else
1035
0
        return make_err(encoding_not_supported, k.val);
1036
1.13k
    } else if (k.val == "ucs-4" && k.allow_ucs) {
1037
142
      if (k.allow_ucs)
1038
142
        ptr.reset(new EncodeDirect<Uni32>);
1039
0
      else
1040
0
        return make_err(encoding_not_supported, k.val);
1041
989
    } else if (k.val == "utf-8") {
1042
967
      ptr.reset(new EncodeUtf8);
1043
967
    } else {
1044
22
      ptr.reset(new EncodeLookup);
1045
22
    }
1046
1.98k
    RET_ON_ERR(ptr->init(k.val, *c));
1047
1.98k
    ptr->key = k.val;
1048
1.98k
    return ptr.release();
1049
1.98k
  }
1050
1051
11.5k
  Convert::~Convert() {}
1052
1053
  PosibErr<void> Convert::init(const Config & c, const ConvKey & in, const ConvKey & out)
1054
666
  {
1055
666
    RET_ON_ERR(setup(decode_c, &decode_cache, &c, in));
1056
663
    decode_ = decode_c.get();
1057
663
    RET_ON_ERR(setup(encode_c, &encode_cache, &c, out));
1058
663
    encode_ = encode_c.get();
1059
1060
663
    conv_ = 0;
1061
663
    if (in.val == out.val) {
1062
28
      if (in.val == "ucs-2") {
1063
0
        if (in.allow_ucs) {
1064
0
          conv_ = new ConvDirect<Uni16>;
1065
0
        } else {
1066
0
          return make_err(encoding_not_supported, in.val);
1067
0
        }
1068
28
      } else if (in.val == "ucs-4") {
1069
0
        if (in.allow_ucs) {
1070
0
          conv_ = new ConvDirect<Uni32>;
1071
0
        } else {
1072
0
          return make_err(encoding_not_supported, in.val);
1073
0
        }
1074
28
      } else {
1075
28
        conv_ = new ConvDirect<char>;
1076
28
      }
1077
28
    }
1078
1079
663
    if (conv_)
1080
28
      RET_ON_ERR(conv_->init(decode_, encode_, c));
1081
1082
663
    return no_err;
1083
663
  }
1084
1085
  
1086
  PosibErr<void> Convert::init_norm_from(const Config & c, const ConvKey & in, const ConvKey & out)
1087
2.88k
  {
1088
2.88k
    if (!c.retrieve_bool("normalize") && !c.retrieve_bool("norm-required")) 
1089
156
      return init(c,in,out);
1090
1091
2.72k
    RET_ON_ERR(setup(norm_tables_, &norm_tables_cache, &c, out.val));
1092
1093
2.72k
    RET_ON_ERR(setup(decode_c, &decode_cache, &c, in));
1094
2.71k
    decode_ = decode_c.get();
1095
1096
2.71k
    if (c.retrieve_bool("norm-strict")) {
1097
27
      encode_s = new EncodeNormLookup(norm_tables_->strict);
1098
27
      encode_ = encode_s;
1099
27
      encode_->key = out.val;
1100
27
      encode_->key += ":strict";
1101
2.68k
    } else {
1102
2.68k
      encode_s = new EncodeNormLookup(norm_tables_->internal);
1103
2.68k
      encode_ = encode_s;
1104
2.68k
      encode_->key = out.val;
1105
2.68k
      encode_->key += ":internal";
1106
2.68k
    }
1107
2.71k
    conv_ = 0;
1108
1109
2.71k
    return no_err;
1110
2.72k
  }
1111
1112
  PosibErr<void> Convert::init_norm_to(const Config & c, const ConvKey & in, const ConvKey & out)
1113
8.65k
  {
1114
8.65k
    String norm_form = c.retrieve("norm-form");
1115
8.65k
    if ((!c.retrieve_bool("normalize") || norm_form == "none")
1116
476
        && !c.retrieve_bool("norm-required"))
1117
476
      return init(c,in,out);
1118
8.18k
    if (norm_form == "none" && c.retrieve_bool("norm-required"))
1119
0
      norm_form = "nfc";
1120
1121
8.18k
    RET_ON_ERR(setup(norm_tables_, &norm_tables_cache, &c, in.val));
1122
1123
8.18k
    RET_ON_ERR(setup(encode_c, &encode_cache, &c, out));
1124
8.18k
    encode_ = encode_c.get();
1125
1126
8.18k
    NormTables::ToUni::const_iterator i = norm_tables_->to_uni.begin();
1127
16.3k
    for (; i != norm_tables_->to_uni.end() && i->name != norm_form; ++i);
1128
8.18k
    if (i == norm_tables_->to_uni.end())
1129
0
      return make_err(aerror_bad_value, "norm-form", norm_form, "one of none, nfd, nfc, or comp");
1130
1131
8.18k
    decode_s = new DecodeNormLookup(i->ptr);
1132
8.18k
    decode_ = decode_s;
1133
8.18k
    decode_->key = in.val;
1134
8.18k
    decode_->key += ':';
1135
8.18k
    decode_->key += i->name;
1136
1137
8.18k
    conv_ = 0;
1138
1139
8.18k
    return no_err;
1140
8.18k
  }
1141
1142
  PosibErr<void> MBLen::setup(const Config &, ParmStr enc0)
1143
0
  {
1144
0
    String buf;
1145
0
    const char * enc = fix_encoding_str(enc0,buf);
1146
0
    if      (strcmp(enc, "utf-8") == 0) encoding = UTF8;
1147
0
    else if (strcmp(enc, "ucs-2") == 0) encoding = UCS2;
1148
0
    else if (strcmp(enc, "ucs-4") == 0) encoding = UCS4;
1149
0
    else                                encoding = Other;
1150
0
    return no_err;
1151
0
  }
1152
1153
  unsigned MBLen::operator()(const char * str, const char * stop)
1154
0
  {
1155
0
    unsigned size = 0;
1156
0
    switch (encoding) {
1157
0
    case Other: 
1158
0
      return stop - str;
1159
0
    case UTF8:
1160
0
      for (; str != stop; ++str) {
1161
0
        if ((*str & 0x80) == 0 || (*str & 0xC0) == 0xC0) ++size;
1162
0
      }
1163
0
      return size;
1164
0
    case UCS2:
1165
0
      return (stop - str)/2;
1166
0
    case UCS4:
1167
0
      return (stop - str)/4;
1168
0
    }
1169
0
    return 0;
1170
0
  }
1171
1172
0
  PosibErr<void> unsupported_null_term_wide_string_err_(const char * func) {
1173
0
    static bool reported_to_stderr = false;
1174
0
    PosibErr<void> err = make_err(other_error, unsupported_null_term_wide_string_msg);
1175
0
    if (!reported_to_stderr) {
1176
0
      CERR.printf("ERROR: %s: %s\n", func, unsupported_null_term_wide_string_msg);
1177
0
      reported_to_stderr = true;
1178
0
    }
1179
0
    return err;
1180
0
  }
1181
1182
0
  void unsupported_null_term_wide_string_abort_(const char * func) {
1183
0
    CERR.printf("%s: %s\n", func, unsupported_null_term_wide_string_msg);
1184
0
    abort();
1185
0
  }
1186
 
1187
}