Bump to aspell 0.60.8
[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 # define sanity(check) \
348     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   {
353     char mesg[500];
354     snprintf(mesg, 500, "%s:%d: %s: Assertion \"%s\" failed.",
355              file,  line, func, check_str);
356     return make_err(bad_input_error, mesg);
357   }
358 # define CREATE_NORM_TABLE(T, in, buf, res) \
359   do { PosibErr<NormTable<T> *> pe( create_norm_table<T>(in,buf) );\
360        if (pe.has_err()) return PosibErrBase(pe); \
361        res = pe.data; } while(false)
362
363   template <class T>
364   static PosibErr< NormTable<T> * > create_norm_table(IStream & in, String & buf)
365   {
366     const char FUNC[] = "create_norm_table";
367     const char * p = get_nb_line(in, buf);
368     sanity(*p == 'N');
369     ++p;
370     int size = strtoul(p, (char **)&p, 10);
371     VARARRAY(T, d, size);
372     memset(d, 0, sizeof(T) * size);
373     int sz = 1 << (unsigned)floor(log(size <= 1 ? 1.0 : size - 1)/log(2.0));
374     VARARRAY(int, tally0_d, sz);   Tally tally0(sz,   tally0_d);
375     VARARRAY(int, tally1_d, sz*2); Tally tally1(sz*2, tally1_d);
376     VARARRAY(int, tally2_d, sz*4); Tally tally2(sz*4, tally2_d);
377     T * cur = d;
378     while (p = get_nb_line(in, buf), *p != '.') {
379       Uni32 f = strtoul(p, (char **)&p, 16);
380       cur->from = static_cast<typename T::From>(f);
381       sanity(f == cur->from);
382       tally0.add(f);
383       tally1.add(f);
384       tally2.add(f);
385       ++p;
386       sanity(*p == '>');
387       ++p;
388       sanity(*p == ' ');
389       ++p;
390       unsigned i = 0;
391       if (*p != '-') {
392         for (;; ++i) {
393           const char * q = p;
394           Uni32 t = strtoul(p, (char **)&p, 16);
395           if (q == p) break;
396           sanity(i < d->max_to);
397           cur->to[i] = static_cast<typename T::To>(t);
398           sanity(t == static_cast<Uni32>(cur->to[i]));
399         } 
400       } else {
401         cur->to[0] = 0;
402         cur->to[1] = T::to_non_char;
403       }
404       if (*p == ' ') ++p;
405       if (*p == '/') CREATE_NORM_TABLE(T, in, buf, cur->sub_table);
406       ++cur;
407     }
408     sanity(cur - d == size);
409     Tally * which = &tally0;
410     if (which->max > tally1.max) which = &tally1;
411     if (which->max > tally2.max) which = &tally2;
412     NormTable<T> * final = (NormTable<T> *)calloc(1, NormTable<T>::struct_size + 
413                                                   sizeof(T) * which->size * which->max);
414     memset(final, 0, NormTable<T>::struct_size + sizeof(T) * which->size * which->max);
415     final->mask = which->size - 1;
416     final->height = which->size;
417     final->width = which->max;
418     final->end = final->data + which->size * which->max;
419     final->size = size;
420     for (cur = d; cur != d + size; ++cur) {
421       T * dest = final->data + (cur->from & final->mask);
422       while (dest->from != 0) dest += final->height;
423       *dest = *cur;
424       if (dest->from == 0) dest->from = T::from_non_char;
425     }
426     for (T * dest = final->data; dest < final->end; dest += final->height) {
427       if (dest->from == 0 || (dest->from == T::from_non_char && dest->to[0] == 0)) {
428         dest->from = T::from_non_char;
429         dest->to[0] = T::to_non_char;
430       }
431     }
432     return final;
433   }
434
435   static PosibErr<void> init_norm_tables(FStream & in, NormTables * d) 
436   {
437     const char FUNC[] = "init_norm_tables";
438     String l;
439     get_nb_line(in, l);
440     remove_comments(l);
441     sanity (l == "INTERNAL");
442     get_nb_line(in, l);
443     remove_comments(l);
444     sanity (l == "/");
445     CREATE_NORM_TABLE(FromUniNormEntry, in, l, d->internal);
446     get_nb_line(in, l);
447     remove_comments(l);
448     sanity (l == "STRICT");
449     char * p = get_nb_line(in, l);
450     remove_comments(l);
451     if (l == "/") {
452       CREATE_NORM_TABLE(FromUniNormEntry, in, l, d->strict_d);
453       d->strict = d->strict_d;
454     } else {
455       sanity(*p == '=');
456       ++p; ++p;
457       sanity(strcmp(p, "INTERNAL") == 0);
458       d->strict = d->internal;
459     }
460     while (get_nb_line(in, l)) {
461       remove_comments(l);
462       d->to_uni.push_back(NormTables::ToUniTable());
463       NormTables::ToUniTable & e = d->to_uni.back();
464       e.name.resize(l.size());
465       for (unsigned i = 0; i != l.size(); ++i)
466         e.name[i] = asc_tolower(l[i]);
467       char * p = get_nb_line(in, l);
468       remove_comments(l);
469       if (l == "/") {
470         CREATE_NORM_TABLE(ToUniNormEntry, in, l, e.data);
471         e.ptr = e.data;
472       } else {
473         sanity(*p == '=');
474         ++p; ++p;
475         for (char * q = p; *q; ++q) *q = asc_tolower(*q);
476         Vector<NormTables::ToUniTable>::iterator i = d->to_uni.begin();
477         while (i->name != p && i != d->to_uni.end()) ++i;
478         sanity(i != d->to_uni.end());
479         e.ptr = i->ptr;
480         get_nb_line(in, l);
481       }
482     }  
483     return no_err;
484   }
485
486   PosibErr<NormTables *> NormTables::get_new(const String & encoding, 
487                                              const Config * config)
488   {
489     String dir1,dir2,file_name;
490     fill_data_dir(config, dir1, dir2);
491     find_file(file_name,dir1,dir2,encoding,".cmap");
492     
493     FStream in;
494     PosibErrBase err = in.open(file_name, "r");
495     if (err.get_err()) { 
496       char mesg[300];
497       snprintf(mesg, 300, _("This could also mean that the file \"%s\" could not be opened for reading or does not exist."),
498                file_name.c_str());
499       return make_err(unknown_encoding, encoding, mesg); // FIXME
500     }
501
502     NormTables * d = new NormTables;
503     d->key = encoding;
504     err = init_norm_tables(in, d);
505     if (err.has_err()) {
506       return make_err(bad_file_format, file_name, err.get_err()->mesg);
507     }
508
509     return d;
510
511   }
512
513   NormTables::~NormTables()
514   {
515     free_norm_table<FromUniNormEntry>(internal);
516     if (strict_d)
517       free_norm_table<FromUniNormEntry>(strict_d);
518     for (unsigned i = 0; i != to_uni.size(); ++i) {
519       if (to_uni[i].data)
520         free_norm_table<ToUniNormEntry>(to_uni[i].data);
521     }
522   }
523
524   //////////////////////////////////////////////////////////////////////
525   //////////////////////////////////////////////////////////////////////
526   //
527   //  Convert
528   //
529   //////////////////////////////////////////////////////////////////////
530   //////////////////////////////////////////////////////////////////////
531
532
533   bool operator== (const Convert & rhs, const Convert & lhs)
534   {
535     return strcmp(rhs.in_code(), lhs.in_code()) == 0
536       && strcmp(rhs.out_code(), lhs.out_code()) == 0;
537   }
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     DecodeDirect() {type_width = sizeof(Chr);}
551     void decode(const char * in0, int size, FilterCharVector & out) const {
552       const Chr * in = reinterpret_cast<const Chr *>(in0);
553       if (size == -sizeof(Chr)) {
554         for (;*in; ++in)
555           out.append(*in, sizeof(Chr));
556       } else if (size <= -1) {
557         fprintf(stderr, "%s\n", unsupported_null_term_wide_string_msg);
558         abort();
559       } else {
560         const Chr * stop = reinterpret_cast<const Chr *>(in0) + size/sizeof(Chr);
561         for (;in != stop; ++in)
562           out.append(*in, sizeof(Chr));
563       }
564     }
565     PosibErr<void> decode_ec(const char * in0, int size, 
566                              FilterCharVector & out, ParmStr) const {
567       DecodeDirect::decode(in0, size, out);
568       return no_err;
569     }
570   };
571
572   template <typename Chr>
573   struct EncodeDirect : public Encode
574   {
575     EncodeDirect() {type_width = sizeof(Chr);}
576     void encode(const FilterChar * in, const FilterChar * stop, 
577                 CharVector & out) const {
578       for (; in != stop; ++in) {
579         Chr c = in->chr;
580         if (c != in->chr) c = '?';
581         out.append(&c, sizeof(Chr));
582       }
583     }
584     PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
585                              CharVector & out, ParmStr orig) const {
586       for (; in != stop; ++in) {
587         Chr c = in->chr;
588         if (c != in->chr) {
589           char m[70];
590           snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
591           return make_err(invalid_string, orig, m);
592         }
593         
594         out.append(&c, sizeof(Chr));
595       }
596       return no_err;
597     }
598     bool encode(FilterChar * &, FilterChar * &, FilterCharVector &) const {
599       return true;
600     }
601   };
602
603   template <typename Chr>
604   struct ConvDirect : public DirectConv
605   {
606     ConvDirect() {type_width = sizeof(Chr);}
607     void convert(const char * in0, int size, CharVector & out) const {
608       if (size == -sizeof(Chr)) {
609         const Chr * in = reinterpret_cast<const Chr *>(in0);
610         for (;*in != 0; ++in)
611           out.append(in, sizeof(Chr));
612       } else if (size <= -1) {
613         fprintf(stderr, "%s\n", unsupported_null_term_wide_string_msg);
614         abort();
615       } else {
616         out.append(in0, size);
617       }
618     }
619     PosibErr<void> convert_ec(const char * in0, int size, 
620                               CharVector & out, ParmStr) const {
621       ConvDirect::convert(in0, size, out);
622       return no_err;
623     }
624   };
625
626   //////////////////////////////////////////////////////////////////////
627   //
628   //  Lookup Conversion
629   //
630
631   struct DecodeLookup : public Decode 
632   {
633     ToUniLookup lookup;
634     PosibErr<void> init(ParmStr code, const Config & c) {
635       FromUniLookup unused;
636       return read_in_char_data(c, code, lookup, unused);
637     }
638     void decode(const char * in, int size, FilterCharVector & out) const {
639       if (size == -1) {
640         for (;*in; ++in)
641           out.append(lookup[*in]);
642       } else {
643         const char * stop = in + size;
644         for (;in != stop; ++in)
645           out.append(lookup[*in]);
646       }
647     }
648     PosibErr<void> decode_ec(const char * in, int size, 
649                              FilterCharVector & out, ParmStr) const {
650       DecodeLookup::decode(in, size, out);
651       return no_err;
652     }
653   };
654
655   struct DecodeNormLookup : public Decode 
656   {
657     typedef ToUniNormEntry E;
658     NormTable<E> * data;
659     DecodeNormLookup(NormTable<E> * d) : data(d) {}
660     // must be null terminated
661     // FIXME: Why must it be null terminated?
662     void decode(const char * in, int size, FilterCharVector & out) const {
663       const char * stop = in + size; // will work even if size -1
664       while (in != stop) {
665         if (*in == 0) {
666           if (size == -1) break;
667           out.append(0);
668           ++in;
669         } else {
670           NormLookupRet<E,const char> ret = norm_lookup<E>(data, in, stop, 0, in);
671           for (unsigned i = 0; ret.to[i] && i < E::max_to; ++i)
672             out.append(ret.to[i]);
673           in = ret.last + 1;
674         }
675       }
676     }
677     PosibErr<void> decode_ec(const char * in, int size, 
678                              FilterCharVector & out, ParmStr) const {
679       DecodeNormLookup::decode(in, size, out);
680       return no_err;
681     }
682   };
683
684   struct EncodeLookup : public Encode 
685   {
686     FromUniLookup lookup;
687     PosibErr<void> init(ParmStr code, const Config & c) 
688       {ToUniLookup unused;
689       return read_in_char_data(c, code, unused, lookup);}
690     void encode(const FilterChar * in, const FilterChar * stop, 
691                 CharVector & out) const {
692       for (; in != stop; ++in) {
693         out.append(lookup(*in));
694       }
695     }
696     PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
697                              CharVector & out, ParmStr orig) const {
698       for (; in != stop; ++in) {
699         char c = lookup(*in, '\0');
700         if (c == '\0' && in->chr != 0) {
701           char m[70];
702           snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
703           return make_err(invalid_string, orig, m);
704         }
705         out.append(c);
706       }
707       return no_err;
708     }
709     bool encode(FilterChar * & in0, FilterChar * & stop,
710                 FilterCharVector & out) const {
711       FilterChar * in = in0;
712       for (; in != stop; ++in)
713         *in = lookup(*in);
714       return true;
715     }
716   };
717
718   struct EncodeNormLookup : public Encode 
719   {
720     typedef FromUniNormEntry E;
721     NormTable<E> * data;
722     EncodeNormLookup(NormTable<E> * d) : data(d) {}
723     // *stop must equal 0
724     void encode(const FilterChar * in, const FilterChar * stop, 
725                 CharVector & out) const {
726       while (in < stop) {
727         if (*in == 0) {
728           out.append('\0');
729           ++in;
730         } else {
731           NormLookupRet<E,const FilterChar> ret = norm_lookup<E>(data, in, stop, (const byte *)"?", in);
732           for (unsigned i = 0; i < E::max_to && ret.to[i]; ++i)
733             out.append(ret.to[i]);
734           in = ret.last + 1;
735         }
736       }
737     }
738     PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
739                              CharVector & out, ParmStr orig) const {
740       while (in < stop) {
741         if (*in == 0) {
742           out.append('\0');
743           ++in;
744         } else {
745           NormLookupRet<E,const FilterChar> ret = norm_lookup<E>(data, in, stop, 0, in);
746           if (ret.to == 0) {
747             char m[70];
748             snprintf(m, 70, _("The Unicode code point U+%04X is unsupported."), in->chr);
749             return make_err(invalid_string, orig, m);
750           }
751           for (unsigned i = 0; i < E::max_to && ret.to[i]; ++i)
752             out.append(ret.to[i]);
753           in = ret.last + 1;
754         }
755       }
756       return no_err;
757     }
758     bool encode(FilterChar * & in, FilterChar * & stop,
759                 FilterCharVector & buf) const {
760       buf.clear();
761       while (in < stop) {
762         if (*in == 0) {
763           buf.append(FilterChar(0));
764           ++in;
765         } else {
766           NormLookupRet<E,FilterChar> ret = norm_lookup<E>(data, in, stop, (const byte *)"?", in);
767           const FilterChar * end = ret.last + 1;
768           unsigned width = 0;
769           for (; in != end; ++in) width += in->width;
770           buf.append(FilterChar(ret.to[0], width));
771           for (unsigned i = 1; i < E::max_to && ret.to[i]; ++i) {
772             buf.append(FilterChar(ret.to[i],0));
773           }
774         }
775       }
776       buf.append(0);
777       in = buf.pbegin();
778       stop = buf.pend();
779       return true;
780     }
781   };
782
783   //////////////////////////////////////////////////////////////////////
784   //
785   //  UTF8
786   //
787   
788 #define get_check_next \
789   if (in == stop) goto error;          \
790   c = *in;                             \
791   if ((c & 0xC0/*1100 0000*/) != 0x80/*10xx xxxx*/) goto error;\
792   ++in;                                \
793   u <<= 6;                             \
794   u |= c & 0x3F/*0011 1111*/;          \
795   ++w;
796
797   static inline FilterChar from_utf8 (const char * & in, const char * stop = 0,
798                                       Uni32 err_char = '?')
799   {
800     Uni32 u = (Uni32)(-1);
801     FilterChar::Width w = 1;
802
803     // the first char is guaranteed not to be off the end
804     char c = *in;
805     ++in;
806
807     if ((c & 0x80/*1000 0000*/) == 0x00/*0xxx xxx*/) {
808       u = c;
809     } else if ((c & 0xE0/*1110 0000*/) == 0xC0/*110x xxxx*/) { // 2-byte wide
810       u  = c & 0x1F/*0001 1111*/;
811       get_check_next;
812     } else if ((c & 0xF0/*1111 0000*/) == 0xE0/*1110 xxxx*/) { // 3-byte wide
813       u  = c & 0x0F/*0000 1111*/;
814       get_check_next;
815       get_check_next;
816     } else if ((c & 0xF8/*1111 1000*/) == 0xF0/*1111 0xxx*/) { // 4-byte wide
817       u  = c & 0x07/*0000 0111*/;
818       get_check_next;
819       get_check_next;
820       get_check_next;
821     } else {
822       goto error;
823     }
824
825     return FilterChar(u, w);
826   error:
827     return FilterChar(err_char, w);
828   }
829
830   static inline void to_utf8 (FilterChar in, CharVector & out)
831   {
832     FilterChar::Chr c = in;
833     
834     if (c < 0x80) {
835       out.append(c);
836     }
837     else if (c < 0x800) {
838       out.append(0xC0 | (c>>6));
839       out.append(0x80 | (c & 0x3F));
840     }
841     else if (c < 0x10000) {
842       out.append(0xE0 | (c>>12));
843       out.append(0x80 | (c>>6 & 0x3F));
844       out.append(0x80 | (c & 0x3F));
845     }
846     else if (c < 0x200000) {
847       out.append(0xF0 | (c>>18));
848       out.append(0x80 | (c>>12 & 0x3F));
849       out.append(0x80 | (c>>6 & 0x3F));
850       out.append(0x80 | (c & 0x3F));
851     }
852   }
853   
854   struct DecodeUtf8 : public Decode 
855   {
856     ToUniLookup lookup;
857     void decode(const char * in, int size, FilterCharVector & out) const {
858       if (size == -1) {
859         while (*in)
860           out.append(from_utf8(in));
861       } else {
862         const char * stop = in + size;
863         while (in != stop)
864           out.append(from_utf8(in, stop));
865       }
866     }
867     PosibErr<void> decode_ec(const char * in, int size, 
868                              FilterCharVector & out, ParmStr orig) const {
869       const char * begin = in;
870       if (size == -1) {
871         while (*in) {
872           FilterChar c = from_utf8(in, 0, (Uni32)-1);
873           if (c == (Uni32)-1) goto error;
874           out.append(c);
875         }
876       } else {
877         const char * stop = in + size;
878         while (in != stop) {
879           FilterChar c = from_utf8(in, stop, (Uni32)-1);
880           if (c == (Uni32)-1) goto error;
881           out.append(c);
882         }
883       }
884       return no_err;
885     error:
886       char m[70];
887       snprintf(m, 70, _("Invalid UTF-8 sequence at position %ld."), (long)(in - begin));
888       return make_err(invalid_string, orig, m);
889     }
890   };
891
892   struct EncodeUtf8 : public Encode 
893   {
894     FromUniLookup lookup;
895     void encode(const FilterChar * in, const FilterChar * stop, 
896                 CharVector & out) const {
897       for (; in != stop; ++in) {
898         to_utf8(*in, out);
899       }
900     }
901     PosibErr<void> encode_ec(const FilterChar * in, const FilterChar * stop, 
902                              CharVector & out, ParmStr) const {
903       for (; in != stop; ++in) {
904         to_utf8(*in, out);
905       }
906       return no_err;
907     }
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   {
926     buf_.clear();
927     decode_->decode(in, size, buf_);
928     FilterChar * start = buf_.pbegin();
929     FilterChar * stop = buf_.pend();
930     if (!filter.empty())
931       filter.process(start, stop);
932     encode_->encode(start, stop, out);
933   }
934
935   const char * fix_encoding_str(ParmStr enc, String & buf)
936   {
937     buf.clear();
938     buf.reserve(enc.size() + 1);
939     for (size_t i = 0; i != enc.size(); ++i)
940       buf.push_back(asc_tolower(enc[i]));
941
942     if (strncmp(buf.c_str(), "iso8859", 7) == 0)
943       buf.insert(buf.begin() + 3, '-'); // For backwards compatibility
944     
945     if (buf == "ascii" || buf == "ansi_x3.4-1968")
946       return "iso-8859-1";
947     else if (buf == "machine unsigned 16" || buf == "utf-16")
948       return "ucs-2";
949     else if (buf == "machine unsigned 32" || buf == "utf-32")
950       return "ucs-4";
951     else
952       return buf.c_str();
953   }
954
955   bool ascii_encoding(const Config & c, ParmStr enc0)
956   {
957     if (enc0.empty()) return true;
958     if (enc0 == "ANSI_X3.4-1968" 
959         || enc0 == "ASCII" || enc0 == "ascii") return true;
960     String buf;
961     const char * enc = fix_encoding_str(enc0, buf);
962     if (strcmp(enc, "utf-8") == 0 
963         || strcmp(enc, "ucs-2") == 0 
964         || strcmp(enc, "ucs-4") == 0) return false;
965     String dir1,dir2,file_name;
966     fill_data_dir(&c, dir1, dir2);
967     file_name << dir1 << enc << ".cset";
968     if (file_exists(file_name)) return false;
969     if (dir1 == dir2) return true;
970     file_name.clear();
971     file_name << dir2 << enc << ".cset";
972     return !file_exists(file_name);
973   }
974
975   PosibErr<Convert *> internal_new_convert(const Config & c,
976                                            ConvKey in, 
977                                            ConvKey out,
978                                            bool if_needed,
979                                            Normalize norm)
980   {
981     String in_s;
982     in.val = fix_encoding_str(in.val, in_s);
983
984     String out_s;
985     out.val = fix_encoding_str(out.val, out_s); 
986
987     if (if_needed && in.val == out.val) return 0;
988
989     StackPtr<Convert> conv(new Convert);
990     switch (norm) {
991     case NormNone:
992       RET_ON_ERR(conv->init(c, in, out)); break;
993     case NormFrom:
994       RET_ON_ERR(conv->init_norm_from(c, in, out)); break;
995     case NormTo:
996       RET_ON_ERR(conv->init_norm_to(c, in, out)); break;
997     }
998     return conv.release();
999   }
1000
1001   PosibErr<Decode *> Decode::get_new(const ConvKey & k, const Config * c)
1002   {
1003     StackPtr<Decode> ptr;
1004     if (k.val == "iso-8859-1") {
1005       ptr.reset(new DecodeDirect<Uni8>);
1006     } else if (k.val == "ucs-2") {
1007       if (k.allow_ucs)
1008         ptr.reset(new DecodeDirect<Uni16>);
1009       else
1010         return make_err(encoding_not_supported, k.val);
1011     } else if (k.val == "ucs-4") {
1012       if (k.allow_ucs)
1013         ptr.reset(new DecodeDirect<Uni32>);
1014       else
1015         return make_err(encoding_not_supported, k.val);
1016     } else if (k.val == "utf-8") {
1017       ptr.reset(new DecodeUtf8);
1018     } else {
1019       ptr.reset(new DecodeLookup);
1020     }
1021     RET_ON_ERR(ptr->init(k.val, *c));
1022     ptr->key = k.val;
1023     return ptr.release();
1024   }
1025
1026   PosibErr<Encode *> Encode::get_new(const ConvKey & k, const Config * c)
1027   {
1028     StackPtr<Encode> ptr;
1029     if (k.val == "iso-8859-1") {
1030       ptr.reset(new EncodeDirect<Uni8>);
1031     } else if (k.val == "ucs-2" && k.allow_ucs) {
1032       if (k.allow_ucs)
1033         ptr.reset(new EncodeDirect<Uni16>);
1034       else
1035         return make_err(encoding_not_supported, k.val);
1036     } else if (k.val == "ucs-4" && k.allow_ucs) {
1037       if (k.allow_ucs)
1038         ptr.reset(new EncodeDirect<Uni32>);
1039       else
1040         return make_err(encoding_not_supported, k.val);
1041     } else if (k.val == "utf-8") {
1042       ptr.reset(new EncodeUtf8);
1043     } else {
1044       ptr.reset(new EncodeLookup);
1045     }
1046     RET_ON_ERR(ptr->init(k.val, *c));
1047     ptr->key = k.val;
1048     return ptr.release();
1049   }
1050
1051   Convert::~Convert() {}
1052
1053   PosibErr<void> Convert::init(const Config & c, const ConvKey & in, const ConvKey & out)
1054   {
1055     RET_ON_ERR(setup(decode_c, &decode_cache, &c, in));
1056     decode_ = decode_c.get();
1057     RET_ON_ERR(setup(encode_c, &encode_cache, &c, out));
1058     encode_ = encode_c.get();
1059
1060     conv_ = 0;
1061     if (in.val == out.val) {
1062       if (in.val == "ucs-2") {
1063         if (in.allow_ucs) {
1064           conv_ = new ConvDirect<Uni16>;
1065         } else {
1066           return make_err(encoding_not_supported, in.val);
1067         }
1068       } else if (in.val == "ucs-4") {
1069         if (in.allow_ucs) {
1070           conv_ = new ConvDirect<Uni32>;
1071         } else {
1072           return make_err(encoding_not_supported, in.val);
1073         }
1074       } else {
1075         conv_ = new ConvDirect<char>;
1076       }
1077     }
1078
1079     if (conv_)
1080       RET_ON_ERR(conv_->init(decode_, encode_, c));
1081
1082     return no_err;
1083   }
1084
1085   
1086   PosibErr<void> Convert::init_norm_from(const Config & c, const ConvKey & in, const ConvKey & out)
1087   {
1088     if (!c.retrieve_bool("normalize") && !c.retrieve_bool("norm-required")) 
1089       return init(c,in,out);
1090
1091     RET_ON_ERR(setup(norm_tables_, &norm_tables_cache, &c, out.val));
1092
1093     RET_ON_ERR(setup(decode_c, &decode_cache, &c, in));
1094     decode_ = decode_c.get();
1095
1096     if (c.retrieve_bool("norm-strict")) {
1097       encode_s = new EncodeNormLookup(norm_tables_->strict);
1098       encode_ = encode_s;
1099       encode_->key = out.val;
1100       encode_->key += ":strict";
1101     } else {
1102       encode_s = new EncodeNormLookup(norm_tables_->internal);
1103       encode_ = encode_s;
1104       encode_->key = out.val;
1105       encode_->key += ":internal";
1106     }
1107     conv_ = 0;
1108
1109     return no_err;
1110   }
1111
1112   PosibErr<void> Convert::init_norm_to(const Config & c, const ConvKey & in, const ConvKey & out)
1113   {
1114     String norm_form = c.retrieve("norm-form");
1115     if ((!c.retrieve_bool("normalize") || norm_form == "none")
1116         && !c.retrieve_bool("norm-required"))
1117       return init(c,in,out);
1118     if (norm_form == "none" && c.retrieve_bool("norm-required"))
1119       norm_form = "nfc";
1120
1121     RET_ON_ERR(setup(norm_tables_, &norm_tables_cache, &c, in.val));
1122
1123     RET_ON_ERR(setup(encode_c, &encode_cache, &c, out));
1124     encode_ = encode_c.get();
1125
1126     NormTables::ToUni::const_iterator i = norm_tables_->to_uni.begin();
1127     for (; i != norm_tables_->to_uni.end() && i->name != norm_form; ++i);
1128     if (i == norm_tables_->to_uni.end())
1129       return make_err(aerror_bad_value, "norm-form", norm_form, "one of none, nfd, nfc, or comp");
1130
1131     decode_s = new DecodeNormLookup(i->ptr);
1132     decode_ = decode_s;
1133     decode_->key = in.val;
1134     decode_->key += ':';
1135     decode_->key += i->name;
1136
1137     conv_ = 0;
1138
1139     return no_err;
1140   }
1141
1142   PosibErr<void> MBLen::setup(const Config &, ParmStr enc0)
1143   {
1144     String buf;
1145     const char * enc = fix_encoding_str(enc0,buf);
1146     if      (strcmp(enc, "utf-8") == 0) encoding = UTF8;
1147     else if (strcmp(enc, "ucs-2") == 0) encoding = UCS2;
1148     else if (strcmp(enc, "ucs-4") == 0) encoding = UCS4;
1149     else                                encoding = Other;
1150     return no_err;
1151   }
1152
1153   unsigned MBLen::operator()(const char * str, const char * stop)
1154   {
1155     unsigned size = 0;
1156     switch (encoding) {
1157     case Other: 
1158       return stop - str;
1159     case UTF8:
1160       for (; str != stop; ++str) {
1161         if ((*str & 0x80) == 0 || (*str & 0xC0) == 0xC0) ++size;
1162       }
1163       return size;
1164     case UCS2:
1165       return (stop - str)/2;
1166     case UCS4:
1167       return (stop - str)/4;
1168     }
1169     return 0;
1170   }
1171
1172   PosibErr<void> unsupported_null_term_wide_string_err_(const char * func) {
1173     static bool reported_to_stderr = false;
1174     PosibErr<void> err = make_err(other_error, unsupported_null_term_wide_string_msg);
1175     if (!reported_to_stderr) {
1176       CERR.printf("ERROR: %s: %s\n", func, unsupported_null_term_wide_string_msg);
1177       reported_to_stderr = true;
1178     }
1179     return err;
1180   }
1181
1182   void unsupported_null_term_wide_string_abort_(const char * func) {
1183     CERR.printf("%s: %s\n", func, unsupported_null_term_wide_string_msg);
1184     abort();
1185   }
1186  
1187 }