empty file
[platform/upstream/aspell.git] / common / convert.cpp
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     Uni32 operator[] (char key) const {return data[(unsigned char)key];}
55     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     for (int i = 0; i != 256; ++i)
62       data[i] = npos;
63   }
64
65   bool ToUniLookup::insert(char key, Uni32 value)
66   {
67     if (data[(unsigned char)key] != npos) 
68       return false;
69     data[(unsigned char)key] = value;
70     return true;
71   }
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     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   {
111     for (unsigned i = 0; i != 256*4; ++i)
112       data[i].key = npos;
113     overflow_end = overflow;
114   }
115
116   inline char FromUniLookup::operator() (Uni32 k, char unknown) const
117   {
118     const UniItem * i = data + (unsigned char)k * 4;
119
120     if (i->key == k) return i->value;
121     ++i;
122     if (i->key == k) return i->value;
123     ++i;
124     if (i->key == k) return i->value;
125     ++i;
126     if (i->key == k) return i->value;
127   
128     if (i->key == npos) return unknown;
129   
130     for(i = overflow; i != overflow_end; ++i)
131       if (i->key == k) return i->value;
132
133     return unknown;
134   }
135
136   bool FromUniLookup::insert(Uni32 k, char v) 
137   {
138     UniItem * i = data + (unsigned char)k * 4;
139     UniItem * e = i + 4;
140     while (i != e && i->key != npos) {
141       if (i->key == k)
142         return false;
143       ++i;
144     }
145     if (i == e) {
146       for(i = overflow; i != overflow_end; ++i)
147         if (i->key == k) return false;
148     }
149     i->key = k;
150     i->value = v;
151     return true;
152   }
153
154   //////////////////////////////////////////////////////////////////////
155   //
156   // CharLookup
157   //
158
159   class CharLookup 
160   {
161   private:
162     int data[256];
163   public:
164     void reset();
165     char operator[] (char key) const {return data[(unsigned char)key];}
166     bool insert(char key, char value);
167   };
168
169   void CharLookup::reset() {
170     for (int i = 0; i != 256; ++i) 
171       data[i] = -1;
172   }
173
174   bool CharLookup::insert(char key, char value) 
175   {
176     if (data[(unsigned char)key] != -1)
177       return false;
178     data[(unsigned char)key] = value;
179     return true;
180   }
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       : 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   {
217   loop:
218     if (s != stop) {
219       const T * i = d->data + (static_cast<typename T::From>(*s) & d->mask);
220       for (;;) {
221         if (i->from == static_cast<typename T::From>(*s)) {
222           if (i->sub_table) {
223             // really tail recursion
224             if (i->to[1] != T::to_non_char) {def = i->to; prev = s;}
225             d = (const NormTable<T> *)(i->sub_table);
226             s++;
227             goto loop;
228           } else {
229             return NormLookupRet<T,From>(i->to, s);
230           }
231         } else {
232           i += d->height;
233           if (i >= d->end) break;
234         }
235       }
236     }
237     return NormLookupRet<T,From>(def, prev);
238   }
239
240   template <class T>
241   void free_norm_table(NormTable<T> * d)
242   {
243     for (T * cur = d->data; cur != d->end; ++cur) {
244       if (cur->sub_table) 
245         free_norm_table<T>(static_cast<NormTable<T> *>(cur->sub_table));
246     }
247     free(d);
248   }
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   {
292     to.reset();
293     from.reset();
294     
295     String dir1,dir2,file_name;
296     fill_data_dir(&config, dir1, dir2);
297     find_file(file_name,dir1,dir2,encoding,".cset");
298
299     FStream data;
300     PosibErrBase err = data.open(file_name, "r");
301     if (err.get_err()) { 
302       char mesg[300];
303       snprintf(mesg, 300, _("This could also mean that the file \"%s\" could not be opened for reading or does not exist."),
304                file_name.c_str());
305       return make_err(unknown_encoding, encoding, mesg);
306     }
307     unsigned chr;
308     Uni32 uni;
309     String line;
310     char * p;
311     do {
312       p = get_nb_line(data, line);
313     } while (*p != '/');
314     for (chr = 0; chr != 256; ++chr) {
315       p = get_nb_line(data, line);
316       if (strtoul(p, 0, 16) != chr)
317         return make_err(bad_file_format, file_name);
318       uni = strtoul(p + 3, 0, 16);
319       to.insert(chr, uni);
320       from.insert(uni, chr);
321     }
322   
323     return no_err;
324   }
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     Tally(int s, int * d) : size(s), mask(s - 1), max(0), data(d) {
338       memset(data, 0, sizeof(int)*size);
339     }
340     void add(Uni32 chr) {
341       Uni32 p = chr & mask;
342       data[p]++;
343       if (data[p] > max) max = data[p];
344     }
345   };
346
347   template <class T>
348   static PosibErr< NormTable<T> * > create_norm_table(IStream & in, String & buf)
349   {
350     const char * p = get_nb_line(in, buf);
351     assert(*p == 'N');
352     ++p;
353     int size = strtoul(p, (char **)&p, 10);
354     VARARRAY(T, d, size);
355     memset(d, 0, sizeof(T) * size);
356     int sz = 1 << (unsigned)floor(log(size <= 1 ? 1.0 : size - 1)/log(2.0));
357     VARARRAY(int, tally0_d, sz);   Tally tally0(sz,   tally0_d);
358     VARARRAY(int, tally1_d, sz*2); Tally tally1(sz*2, tally1_d);
359     VARARRAY(int, tally2_d, sz*4); Tally tally2(sz*4, tally2_d);
360     T * cur = d;
361     while (p = get_nb_line(in, buf), *p != '.') {
362       Uni32 f = strtoul(p, (char **)&p, 16);
363       cur->from = static_cast<typename T::From>(f);
364       assert(f == cur->from);
365       tally0.add(f);
366       tally1.add(f);
367       tally2.add(f);
368       ++p;
369       assert(*p == '>');
370       ++p;
371       assert(*p == ' ');
372       ++p;
373       unsigned i = 0;
374       if (*p != '-') {
375         for (;; ++i) {
376           const char * q = p;
377           Uni32 t = strtoul(p, (char **)&p, 16);
378           if (q == p) break;
379           assert(i < d->max_to);
380           cur->to[i] = static_cast<typename T::To>(t);
381           assert(t == static_cast<Uni32>(cur->to[i]));
382         } 
383       } else {
384         cur->to[0] = 0;
385         cur->to[1] = T::to_non_char;
386       }
387       if (*p == ' ') ++p;
388       if (*p == '/') cur->sub_table = create_norm_table<T>(in,buf);
389       ++cur;
390     }
391     assert(cur - d == size);
392     Tally * which = &tally0;
393     if (which->max > tally1.max) which = &tally1;
394     if (which->max > tally2.max) which = &tally2;
395     NormTable<T> * final = (NormTable<T> *)calloc(1, NormTable<T>::struct_size + 
396                                                   sizeof(T) * which->size * which->max);
397     memset(final, 0, NormTable<T>::struct_size + sizeof(T) * which->size * which->max);
398     final->mask = which->size - 1;
399     final->height = which->size;
400     final->width = which->max;
401     final->end = final->data + which->size * which->max;
402     final->size = size;
403     for (cur = d; cur != d + size; ++cur) {
404       T * dest = final->data + (cur->from & final->mask);
405       while (dest->from != 0) dest += final->height;
406       *dest = *cur;
407       if (dest->from == 0) dest->from = T::from_non_char;
408     }
409     for (T * dest = final->data; dest < final->end; dest += final->height) {
410       if (dest->from == 0 || (dest->from == T::from_non_char && dest->to[0] == 0)) {
411         dest->from = T::from_non_char;
412         dest->to[0] = T::to_non_char;
413       }
414     }
415     return final;
416   }
417
418   PosibErr<NormTables *> NormTables::get_new(const String & encoding, 
419                                              const Config * config)
420   {
421     String dir1,dir2,file_name;
422     fill_data_dir(config, dir1, dir2);
423     find_file(file_name,dir1,dir2,encoding,".cmap");
424     
425     FStream in;
426     PosibErrBase err = in.open(file_name, "r");
427     if (err.get_err()) { 
428       char mesg[300];
429       snprintf(mesg, 300, _("This could also mean that the file \"%s\" could not be opened for reading or does not exist."),
430                file_name.c_str());
431       return make_err(unknown_encoding, encoding, mesg); // FIXME
432     }
433
434     NormTables * d = new NormTables;
435     d->key = encoding;
436     String l;
437     get_nb_line(in, l);
438     remove_comments(l);
439     assert (l == "INTERNAL");
440     get_nb_line(in, l);
441     remove_comments(l);
442     assert (l == "/");
443     d->internal = create_norm_table<FromUniNormEntry>(in, l);
444     get_nb_line(in, l);
445     remove_comments(l);
446     assert (l == "STRICT");
447     char * p = get_nb_line(in, l);
448     remove_comments(l);
449     if (l == "/") {
450       d->strict_d = create_norm_table<FromUniNormEntry>(in, l);
451       d->strict = d->strict_d;
452     } else {
453       assert(*p == '=');
454       ++p; ++p;
455       assert(strcmp(p, "INTERNAL") == 0);
456       d->strict = d->internal;
457     }
458     while (get_nb_line(in, l)) {
459       remove_comments(l);
460       d->to_uni.push_back(ToUniTable());
461       ToUniTable & e = d->to_uni.back();
462       e.name.resize(l.size());
463       for (unsigned i = 0; i != l.size(); ++i)
464         e.name[i] = asc_tolower(l[i]);
465       char * p = get_nb_line(in, l);
466       remove_comments(l);
467       if (l == "/") {
468         e.ptr = e.data = create_norm_table<ToUniNormEntry>(in,l);
469       } else {
470         assert(*p == '=');
471         ++p; ++p;
472         for (char * q = p; *q; ++q) *q = asc_tolower(*q);
473         Vector<ToUniTable>::iterator i = d->to_uni.begin();
474         while (i->name != p && i != d->to_uni.end()) ++i;
475         assert(i != d->to_uni.end());
476         e.ptr = i->ptr;
477         get_nb_line(in, l);
478       }
479     }  
480     return d;
481   }
482
483   NormTables::~NormTables()
484   {
485     free_norm_table<FromUniNormEntry>(internal);
486     if (strict_d)
487       free_norm_table<FromUniNormEntry>(strict_d);
488     for (unsigned i = 0; i != to_uni.size(); ++i) {
489       if (to_uni[i].data)
490         free_norm_table<ToUniNormEntry>(to_uni[i].data);
491     }
492   }
493
494   //////////////////////////////////////////////////////////////////////
495   //////////////////////////////////////////////////////////////////////
496   //
497   //  Convert
498   //
499   //////////////////////////////////////////////////////////////////////
500   //////////////////////////////////////////////////////////////////////
501
502
503   bool operator== (const Convert & rhs, const Convert & lhs)
504   {
505     return strcmp(rhs.in_code(), lhs.in_code()) == 0
506       && strcmp(rhs.out_code(), lhs.out_code()) == 0;
507   }
508
509   //////////////////////////////////////////////////////////////////////
510   //
511   // Trivial Conversion
512   //
513
514   template <typename Chr>
515   struct DecodeDirect : public Decode 
516   {
517     void decode(const char * in0, int size, FilterCharVector & out) const {
518       const Chr * in = reinterpret_cast<const Chr *>(in0);
519       if (size == -1) {
520         for (;*in; ++in)
521           out.append(*in);
522       } else {
523         const Chr * stop = reinterpret_cast<const Chr *>(in0 +size);
524         for (;in != stop; ++in)
525           out.append(*in);
526       }
527     }
528     PosibErr<void> decode_ec(const char * in0, int size, 
529                              FilterCharVector & out, ParmStr) const {
530       DecodeDirect::decode(in0, size, out);
531       return no_err;
532     }
533   };
534
535   template <typename Chr>
536   struct EncodeDirect : public Encode
537   {
538     void encode(const FilterChar * in, const FilterChar * stop, 
539                 CharVector & out) const {
540       for (; in != stop; ++in) {
541         Chr c = in->chr;
542         if (c != in->chr) c = '?';
543         out.append(&c, sizeof(Chr));
544       }
545     }
546     PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
547                              CharVector & out, ParmStr orig) const {
548       for (; in != stop; ++in) {
549         Chr c = in->chr;
550         if (c != in->chr) {
551           char m[70];
552           snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
553           return make_err(invalid_string, orig, m);
554         }
555         out.append(&c, sizeof(Chr));
556       }
557       return no_err;
558     }
559     bool encode(FilterChar * &, FilterChar * &, FilterCharVector &) const {
560       return true;
561     }
562   };
563
564   template <typename Chr>
565   struct ConvDirect : public DirectConv
566   {
567     void convert(const char * in0, int size, CharVector & out) const {
568       if (size == -1) {
569         const Chr * in = reinterpret_cast<const Chr *>(in0);
570         for (;*in != 0; ++in)
571           out.append(in, sizeof(Chr));
572       } else {
573         out.append(in0, size);
574       }
575     }
576     PosibErr<void> convert_ec(const char * in0, int size, 
577                               CharVector & out, ParmStr) const {
578       ConvDirect::convert(in0, size, out);
579       return no_err;
580     }
581   };
582
583   //////////////////////////////////////////////////////////////////////
584   //
585   //  Lookup Conversion
586   //
587
588   struct DecodeLookup : public Decode 
589   {
590     ToUniLookup lookup;
591     PosibErr<void> init(ParmStr code, const Config & c) {
592       FromUniLookup unused;
593       return read_in_char_data(c, code, lookup, unused);
594     }
595     void decode(const char * in, int size, FilterCharVector & out) const {
596       if (size == -1) {
597         for (;*in; ++in)
598           out.append(lookup[*in]);
599       } else {
600         const char * stop = in + size;
601         for (;in != stop; ++in)
602           out.append(lookup[*in]);
603       }
604     }
605     PosibErr<void> decode_ec(const char * in, int size, 
606                              FilterCharVector & out, ParmStr) const {
607       DecodeLookup::decode(in, size, out);
608       return no_err;
609     }
610   };
611
612   struct DecodeNormLookup : public Decode 
613   {
614     typedef ToUniNormEntry E;
615     NormTable<E> * data;
616     DecodeNormLookup(NormTable<E> * d) : data(d) {}
617     // must be null terminated
618     // FIXME: Why must it be null terminated?
619     void decode(const char * in, int size, FilterCharVector & out) const {
620       const char * stop = in + size; // will word even if size -1
621       while (in != stop) {
622         if (*in == 0) {
623           if (size == -1) break;
624           out.append(0);
625           ++in;
626         } else {
627           NormLookupRet<E,const char> ret = norm_lookup<E>(data, in, stop, 0, in);
628           for (unsigned i = 0; ret.to[i] && i < E::max_to; ++i)
629             out.append(ret.to[i]);
630           in = ret.last + 1;
631         }
632       }
633     }
634     PosibErr<void> decode_ec(const char * in, int size, 
635                              FilterCharVector & out, ParmStr) const {
636       DecodeNormLookup::decode(in, size, out);
637       return no_err;
638     }
639   };
640
641   struct EncodeLookup : public Encode 
642   {
643     FromUniLookup lookup;
644     PosibErr<void> init(ParmStr code, const Config & c) 
645       {ToUniLookup unused;
646       return read_in_char_data(c, code, unused, lookup);}
647     void encode(const FilterChar * in, const FilterChar * stop, 
648                 CharVector & out) const {
649       for (; in != stop; ++in) {
650         out.append(lookup(*in));
651       }
652     }
653     PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
654                              CharVector & out, ParmStr orig) const {
655       for (; in != stop; ++in) {
656         char c = lookup(*in, '\0');
657         if (c == '\0' && in->chr != 0) {
658           char m[70];
659           snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
660           return make_err(invalid_string, orig, m);
661         }
662         out.append(c);
663       }
664       return no_err;
665     }
666     bool encode(FilterChar * & in0, FilterChar * & stop,
667                 FilterCharVector & out) const {
668       FilterChar * in = in0;
669       for (; in != stop; ++in)
670         *in = lookup(*in);
671       return true;
672     }
673   };
674
675   struct EncodeNormLookup : public Encode 
676   {
677     typedef FromUniNormEntry E;
678     NormTable<E> * data;
679     EncodeNormLookup(NormTable<E> * d) : data(d) {}
680     // *stop must equal 0
681     void encode(const FilterChar * in, const FilterChar * stop, 
682                 CharVector & out) const {
683       while (in < stop) {
684         if (*in == 0) {
685           out.append('\0');
686           ++in;
687         } else {
688           NormLookupRet<E,const FilterChar> ret = norm_lookup<E>(data, in, stop, (const byte *)"?", in);
689           for (unsigned i = 0; i < E::max_to && ret.to[i]; ++i)
690             out.append(ret.to[i]);
691           in = ret.last + 1;
692         }
693       }
694     }
695     PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
696                              CharVector & out, ParmStr orig) const {
697       while (in < stop) {
698         if (*in == 0) {
699           out.append('\0');
700           ++in;
701         } else {
702           NormLookupRet<E,const FilterChar> ret = norm_lookup<E>(data, in, stop, 0, in);
703           if (ret.to == 0) {
704             char m[70];
705             snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
706             return make_err(invalid_string, orig, m);
707           }
708           for (unsigned i = 0; i < E::max_to && ret.to[i]; ++i)
709             out.append(ret.to[i]);
710           in = ret.last + 1;
711         }
712       }
713       return no_err;
714     }
715     bool encode(FilterChar * & in, FilterChar * & stop,
716                 FilterCharVector & buf) const {
717       buf.clear();
718       while (in < stop) {
719         if (*in == 0) {
720           buf.append(FilterChar(0));
721           ++in;
722         } else {
723           NormLookupRet<E,FilterChar> ret = norm_lookup<E>(data, in, stop, (const byte *)"?", in);
724           const FilterChar * end = ret.last + 1;
725           unsigned width = 0;
726           for (; in != end; ++in) width += in->width;
727           buf.append(FilterChar(ret.to[0], width));
728           for (unsigned i = 1; i < E::max_to && ret.to[i]; ++i) {
729             buf.append(FilterChar(ret.to[i],0));
730           }
731         }
732       }
733       buf.append(0);
734       in = buf.pbegin();
735       stop = buf.pend();
736       return true;
737     }
738   };
739
740   //////////////////////////////////////////////////////////////////////
741   //
742   //  UTF8
743   //
744   
745 #define get_check_next \
746   if (in == stop) goto error;          \
747   c = *in;                             \
748   if ((c & 0xC0) != 0x80) goto error;  \
749   ++in;                                \
750   u <<= 6;                             \
751   u |= c & 0x3F;                       \
752   ++w;
753
754   static inline FilterChar from_utf8 (const char * & in, const char * stop, 
755                                       Uni32 err_char = '?')
756   {
757     Uni32 u = (Uni32)(-1);
758     FilterChar::Width w = 1;
759
760     // the first char is guaranteed not to be off the end
761     char c = *in;
762     ++in;
763
764     while (in != stop && (c & 0xC0) == 0x80) {c = *in; ++in; ++w;}
765     if ((c & 0x80) == 0x00) { // 1-byte wide
766       u = c;
767     } else if ((c & 0xE0) == 0xC0) { // 2-byte wide
768       u  = c & 0x1F;
769       get_check_next;
770     } else if ((c & 0xF0) == 0xE0) { // 3-byte wide
771       u  = c & 0x0F;
772       get_check_next;
773       get_check_next;
774     } else if ((c & 0xF8) == 0xF0) { // 4-byte wide
775       u  = c & 0x07;
776       get_check_next;
777       get_check_next;
778       get_check_next;
779     } else {
780       goto error;
781     }
782
783     return FilterChar(u, w);
784   error:
785     return FilterChar(err_char, w);
786   }
787
788   static inline void to_utf8 (FilterChar in, CharVector & out)
789   {
790     FilterChar::Chr c = in;
791     
792     if (c < 0x80) {
793       out.append(c);
794     }
795     else if (c < 0x800) {
796       out.append(0xC0 | (c>>6));
797       out.append(0x80 | (c & 0x3F));
798     }
799     else if (c < 0x10000) {
800       out.append(0xE0 | (c>>12));
801       out.append(0x80 | (c>>6 & 0x3F));
802       out.append(0x80 | (c & 0x3F));
803     }
804     else if (c < 0x200000) {
805       out.append(0xF0 | (c>>18));
806       out.append(0x80 | (c>>12 & 0x3F));
807       out.append(0x80 | (c>>6 & 0x3F));
808       out.append(0x80 | (c & 0x3F));
809     }
810   }
811   
812   struct DecodeUtf8 : public Decode 
813   {
814     ToUniLookup lookup;
815     void decode(const char * in, int size, FilterCharVector & out) const {
816       if (size == 0) return; // if size == 0 then while loop cause SIGSEGV
817       const char * stop = in + size; // this is OK even if size == -1
818       while (*in && in != stop) {
819         out.append(from_utf8(in, stop));
820       }
821     }
822     PosibErr<void> decode_ec(const char * in, int size, 
823                              FilterCharVector & out, ParmStr orig) const {
824       const char * begin = in;
825       const char * stop = in + size; // this is OK even if size == -1
826       while (*in && in != stop) {
827         FilterChar c = from_utf8(in, stop, (Uni32)-1);
828         if (c == (Uni32)-1) {
829           char m[70];
830           snprintf(m, 70, _("Invalid UTF-8 sequence at position %ld."), (long)(in - begin));
831           return make_err(invalid_string, orig, m);
832         }
833         out.append(c);
834       }
835       return no_err;
836     }
837   };
838
839   struct EncodeUtf8 : public Encode 
840   {
841     FromUniLookup lookup;
842     void encode(const FilterChar * in, const FilterChar * stop, 
843                 CharVector & out) const {
844       for (; in != stop; ++in) {
845         to_utf8(*in, out);
846       }
847     }
848     PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
849                              CharVector & out, ParmStr) const {
850       for (; in != stop; ++in) {
851         to_utf8(*in, out);
852       }
853       return no_err;
854     }
855   };
856
857   //////////////////////////////////////////////////////////////////////
858   //
859   // Cache
860   //
861
862   static GlobalCache<Decode> decode_cache("decode");
863   static GlobalCache<Encode> encode_cache("encode");
864   static GlobalCache<NormTables> norm_tables_cache("norm_tables");
865   
866   //////////////////////////////////////////////////////////////////////
867   //
868   // new_aspell_convert
869   //
870
871   void Convert::generic_convert(const char * in, int size, CharVector & out)
872   {
873     buf_.clear();
874     decode_->decode(in, size, buf_);
875     FilterChar * start = buf_.pbegin();
876     FilterChar * stop = buf_.pend();
877     if (!filter.empty())
878       filter.process(start, stop);
879     encode_->encode(start, stop, out);
880   }
881
882   const char * fix_encoding_str(ParmStr enc, String & buf)
883   {
884     buf.clear();
885     buf.reserve(enc.size() + 1);
886     for (size_t i = 0; i != enc.size(); ++i)
887       buf.push_back(asc_tolower(enc[i]));
888
889     if (strncmp(buf.c_str(), "iso8859", 7) == 0)
890       buf.insert(buf.begin() + 3, '-'); // For backwards compatibility
891     
892     if (buf == "ascii" || buf == "ansi_x3.4-1968")
893       return "iso-8859-1";
894     else if (buf == "machine unsigned 16" || buf == "utf-16")
895       return "ucs-2";
896     else if (buf == "machine unsigned 32" || buf == "utf-32")
897       return "ucs-4";
898     else
899       return buf.c_str();
900   }
901
902   bool ascii_encoding(const Config & c, ParmStr enc0)
903   {
904     if (enc0.empty()) return true;
905     if (enc0 == "ANSI_X3.4-1968" 
906         || enc0 == "ASCII" || enc0 == "ascii") return true;
907     String buf;
908     const char * enc = fix_encoding_str(enc0, buf);
909     if (strcmp(enc, "utf-8") == 0 
910         || strcmp(enc, "ucs-2") == 0 
911         || strcmp(enc, "ucs-4") == 0) return false;
912     String dir1,dir2,file_name;
913     fill_data_dir(&c, dir1, dir2);
914     file_name << dir1 << enc << ".cset";
915     if (file_exists(file_name)) return false;
916     if (dir1 == dir2) return true;
917     file_name.clear();
918     file_name << dir2 << enc << ".cset";
919     return !file_exists(file_name);
920   }
921
922   PosibErr<Convert *> internal_new_convert(const Config & c,
923                                            ParmString in, 
924                                            ParmString out,
925                                            bool if_needed,
926                                            Normalize norm)
927   {
928     String in_s;
929     in = fix_encoding_str(in, in_s);
930
931     String out_s;
932     out = fix_encoding_str(out, out_s); 
933
934     if (if_needed && in == out) return 0;
935
936     StackPtr<Convert> conv(new Convert);
937     switch (norm) {
938     case NormNone:
939       RET_ON_ERR(conv->init(c, in, out)); break;
940     case NormFrom:
941       RET_ON_ERR(conv->init_norm_from(c, in, out)); break;
942     case NormTo:
943       RET_ON_ERR(conv->init_norm_to(c, in, out)); break;
944     }
945     return conv.release();
946   }
947
948   PosibErr<Decode *> Decode::get_new(const String & key, const Config * c)
949   {
950     StackPtr<Decode> ptr;
951     if (key == "iso-8859-1")
952       ptr.reset(new DecodeDirect<Uni8>);
953     else if (key == "ucs-2")
954       ptr.reset(new DecodeDirect<Uni16>);
955     else if (key == "ucs-4")
956       ptr.reset(new DecodeDirect<Uni32>);
957     else if (key == "utf-8")
958       ptr.reset(new DecodeUtf8);
959     else
960       ptr.reset(new DecodeLookup);
961     RET_ON_ERR(ptr->init(key, *c));
962     ptr->key = key;
963     return ptr.release();
964   }
965
966   PosibErr<Encode *> Encode::get_new(const String & key, const Config * c)
967   {
968     StackPtr<Encode> ptr;
969     if (key == "iso-8859-1")
970       ptr.reset(new EncodeDirect<Uni8>);
971     else if (key == "ucs-2")
972       ptr.reset(new EncodeDirect<Uni16>);
973     else if (key == "ucs-4")
974       ptr.reset(new EncodeDirect<Uni32>);
975     else if (key == "utf-8")
976       ptr.reset(new EncodeUtf8);
977     else
978       ptr.reset(new EncodeLookup);
979     RET_ON_ERR(ptr->init(key, *c));
980     ptr->key = key;
981     return ptr.release();
982   }
983
984   Convert::~Convert() {}
985
986   PosibErr<void> Convert::init(const Config & c, ParmStr in, ParmStr out)
987   {
988     RET_ON_ERR(setup(decode_c, &decode_cache, &c, in));
989     decode_ = decode_c.get();
990     RET_ON_ERR(setup(encode_c, &encode_cache, &c, out));
991     encode_ = encode_c.get();
992
993     conv_ = 0;
994     if (in == out) {
995       if (in == "ucs-2") {
996         conv_ = new ConvDirect<Uni16>;
997       } else if (in == "ucs-4") {
998         conv_ = new ConvDirect<Uni32>;
999       } else {
1000         conv_ = new ConvDirect<char>;
1001       }
1002     }
1003
1004     if (conv_)
1005       RET_ON_ERR(conv_->init(decode_, encode_, c));
1006
1007     return no_err;
1008   }
1009
1010   
1011   PosibErr<void> Convert::init_norm_from(const Config & c, ParmStr in, ParmStr out)
1012   {
1013     if (!c.retrieve_bool("normalize") && !c.retrieve_bool("norm-required")) 
1014       return init(c,in,out);
1015
1016     RET_ON_ERR(setup(norm_tables_, &norm_tables_cache, &c, out));
1017
1018     RET_ON_ERR(setup(decode_c, &decode_cache, &c, in));
1019     decode_ = decode_c.get();
1020
1021     if (c.retrieve_bool("norm-strict")) {
1022       encode_s = new EncodeNormLookup(norm_tables_->strict);
1023       encode_ = encode_s;
1024       encode_->key = out;
1025       encode_->key += ":strict";
1026     } else {
1027       encode_s = new EncodeNormLookup(norm_tables_->internal);
1028       encode_ = encode_s;
1029       encode_->key = out;
1030       encode_->key += ":internal";
1031     }
1032     conv_ = 0;
1033
1034     return no_err;
1035   }
1036
1037   PosibErr<void> Convert::init_norm_to(const Config & c, ParmStr in, ParmStr out)
1038   {
1039     String norm_form = c.retrieve("norm-form");
1040     if ((!c.retrieve_bool("normalize") || norm_form == "none")
1041         && !c.retrieve_bool("norm-required"))
1042       return init(c,in,out);
1043     if (norm_form == "none" && c.retrieve_bool("norm-required"))
1044       norm_form = "nfc";
1045
1046     RET_ON_ERR(setup(norm_tables_, &norm_tables_cache, &c, in));
1047
1048     RET_ON_ERR(setup(encode_c, &encode_cache, &c, out));
1049     encode_ = encode_c.get();
1050
1051     NormTables::ToUni::const_iterator i = norm_tables_->to_uni.begin();
1052     for (; i != norm_tables_->to_uni.end() && i->name != norm_form; ++i);
1053     assert(i != norm_tables_->to_uni.end());
1054
1055     decode_s = new DecodeNormLookup(i->ptr);
1056     decode_ = decode_s;
1057     decode_->key = in;
1058     decode_->key += ':';
1059     decode_->key += i->name;
1060
1061     conv_ = 0;
1062
1063     return no_err;
1064   }
1065
1066   PosibErr<void> MBLen::setup(const Config &, ParmStr enc0)
1067   {
1068     String buf;
1069     const char * enc = fix_encoding_str(enc0,buf);
1070     if      (strcmp(enc, "utf-8") == 0) encoding = UTF8;
1071     else if (strcmp(enc, "ucs-2") == 0) encoding = UCS2;
1072     else if (strcmp(enc, "ucs-4") == 0) encoding = UCS4;
1073     else                                encoding = Other;
1074     return no_err;
1075   }
1076
1077   unsigned MBLen::operator()(const char * str, const char * stop)
1078   {
1079     unsigned size = 0;
1080     switch (encoding) {
1081     case Other: 
1082       return stop - str;
1083     case UTF8:
1084       for (; str != stop; ++str) {
1085         if ((*str & 0x80) == 0 || (*str & 0xC0) == 0xC0) ++size;
1086       }
1087       return size;
1088     case UCS2:
1089       return (stop - str)/2;
1090     case UCS4:
1091       return (stop - str)/4;
1092     }
1093     return 0;
1094   }
1095   
1096 }