Tizen 2.1 base
[external/enchant.git] / src / myspell / hashmgr.cxx
1 #include "license.hunspell"
2 #include "license.myspell"
3
4 #include <stdlib.h> 
5 #include <string.h>
6 #include <stdio.h> 
7 #include <ctype.h>
8
9 #include "hashmgr.hxx"
10 #include "csutil.hxx"
11 #include "atypes.hxx"
12
13 // build a hash table from a munched word list
14
15 HashMgr::HashMgr(const char * tpath, const char * apath, const char * key)
16 {
17   tablesize = 0;
18   tableptr = NULL;
19   flag_mode = FLAG_CHAR;
20   complexprefixes = 0;
21   utf8 = 0;
22   langnum = 0;
23   lang = NULL;
24   enc = NULL;
25   csconv = 0;
26   ignorechars = NULL;
27   ignorechars_utf16 = NULL;
28   ignorechars_utf16_len = 0;
29   numaliasf = 0;
30   aliasf = NULL;
31   numaliasm = 0;
32   aliasm = NULL;
33   forbiddenword = FORBIDDENWORD; // forbidden word signing flag
34   load_config(apath, key);
35   int ec = load_tables(tpath, key);
36   if (ec) {
37     /* error condition - what should we do here */
38     HUNSPELL_WARNING(stderr, "Hash Manager Error : %d\n",ec);
39     if (tableptr) {
40       free(tableptr);
41       tableptr = NULL;
42     }
43     tablesize = 0;
44   }
45 }
46
47
48 HashMgr::~HashMgr()
49 {
50   if (tableptr) {
51     // now pass through hash table freeing up everything
52     // go through column by column of the table
53     for (int i=0; i < tablesize; i++) {
54       struct hentry * pt = tableptr[i];
55       struct hentry * nt = NULL;
56       while(pt) {
57         nt = pt->next;
58         if (pt->astr && (!aliasf || TESTAFF(pt->astr, ONLYUPCASEFLAG, pt->alen))) free(pt->astr);
59         free(pt);
60         pt = nt;
61       }
62     }
63     free(tableptr);
64   }
65   tablesize = 0;
66
67   if (aliasf) {
68     for (int j = 0; j < (numaliasf); j++) free(aliasf[j]);
69     free(aliasf);
70     aliasf = NULL;
71     if (aliasflen) {
72       free(aliasflen);
73       aliasflen = NULL;
74     }
75   }
76   if (aliasm) {
77     for (int j = 0; j < (numaliasm); j++) free(aliasm[j]);
78     free(aliasm);
79     aliasm = NULL;
80   }  
81
82 #ifndef OPENOFFICEORG
83 #ifndef MOZILLA_CLIENT
84   if (utf8) free_utf_tbl();
85 #endif
86 #endif
87
88   if (enc) free(enc);
89   if (lang) free(lang);
90   
91   if (ignorechars) free(ignorechars);
92   if (ignorechars_utf16) free(ignorechars_utf16);
93
94 #ifdef MOZILLA_CLIENT
95     delete [] csconv;
96 #endif
97 }
98
99 // lookup a root word in the hashtable
100
101 struct hentry * HashMgr::lookup(const char *word) const
102 {
103     struct hentry * dp;
104     if (tableptr) {
105        dp = tableptr[hash(word)];
106        if (!dp) return NULL;
107        for (  ;  dp != NULL;  dp = dp->next) {
108           if (strcmp(word, dp->word) == 0) return dp;
109        }
110     }
111     return NULL;
112 }
113
114 // add a word to the hash table (private)
115 int HashMgr::add_word(const char * word, int wbl, int wcl, unsigned short * aff,
116     int al, const char * desc, bool onlyupcase)
117 {
118     bool upcasehomonym = false;
119     int descl = desc ? (aliasm ? sizeof(short) : strlen(desc) + 1) : 0;
120     // variable-length hash record with word and optional fields
121     struct hentry* hp = 
122         (struct hentry *) malloc (sizeof(struct hentry) + wbl + descl);
123     if (!hp) return 1;
124     char * hpw = hp->word;
125     strcpy(hpw, word);
126     if (ignorechars != NULL) {
127       if (utf8) {
128         remove_ignored_chars_utf(hpw, ignorechars_utf16, ignorechars_utf16_len);
129       } else {
130         remove_ignored_chars(hpw, ignorechars);
131       }
132     }
133     if (complexprefixes) {
134         if (utf8) reverseword_utf(hpw); else reverseword(hpw);
135     }
136
137     int i = hash(hpw);
138
139     hp->blen = (unsigned char) wbl;
140     hp->clen = (unsigned char) wcl;
141     hp->alen = (short) al;
142     hp->astr = aff;
143     hp->next = NULL;      
144     hp->next_homonym = NULL;
145
146     // store the description string or its pointer
147     if (desc) {
148         hp->var = H_OPT;
149         if (aliasm) {
150             hp->var += H_OPT_ALIASM;
151             store_pointer(hpw + wbl + 1, get_aliasm(atoi(desc)));
152         } else {
153             strcpy(hpw + wbl + 1, desc);
154             if (complexprefixes) {
155                 if (utf8) reverseword_utf(HENTRY_DATA(hp));
156                 else reverseword(HENTRY_DATA(hp));
157             }
158         }
159         if (strstr(HENTRY_DATA(hp), MORPH_PHON)) hp->var += H_OPT_PHON;
160     } else hp->var = 0;
161
162        struct hentry * dp = tableptr[i];
163        if (!dp) {
164          tableptr[i] = hp;
165          return 0;
166        }
167        while (dp->next != NULL) {
168          if ((!dp->next_homonym) && (strcmp(hp->word, dp->word) == 0)) {
169             // remove hidden onlyupcase homonym
170             if (!onlyupcase) {
171                 if ((dp->astr) && TESTAFF(dp->astr, ONLYUPCASEFLAG, dp->alen)) {
172                     free(dp->astr);
173                     dp->astr = hp->astr;
174                     dp->alen = hp->alen;
175                     free(hp);
176                     return 0;
177                 } else {
178                     dp->next_homonym = hp;
179                 }
180             } else {
181                 upcasehomonym = true;
182             }
183          }
184          dp=dp->next;
185        }
186        if (strcmp(hp->word, dp->word) == 0) {
187             // remove hidden onlyupcase homonym
188             if (!onlyupcase) {
189                 if ((dp->astr) && TESTAFF(dp->astr, ONLYUPCASEFLAG, dp->alen)) {
190                     free(dp->astr);
191                     dp->astr = hp->astr;
192                     dp->alen = hp->alen;
193                     free(hp);
194                     return 0;
195                 } else {
196                     dp->next_homonym = hp;
197                 }
198             } else {
199                 upcasehomonym = true;
200             }
201        }
202        if (!upcasehomonym) {
203             dp->next = hp;
204        } else {
205             // remove hidden onlyupcase homonym
206             if (hp->astr) free(hp->astr);
207             free(hp);
208        }
209     return 0;
210 }     
211
212 int HashMgr::add_hidden_capitalized_word(char * word, int wbl, int wcl,
213     unsigned short * flags, int al, char * dp, int captype)
214 {
215     // add inner capitalized forms to handle the following allcap forms:
216     // Mixed caps: OpenOffice.org -> OPENOFFICE.ORG
217     // Allcaps with suffixes: CIA's -> CIA'S    
218     if (((captype == HUHCAP) || (captype == HUHINITCAP) ||
219       ((captype == ALLCAP) && (flags != NULL))) &&
220       !((flags != NULL) && TESTAFF(flags, forbiddenword, al))) {
221           unsigned short * flags2 = (unsigned short *) malloc (sizeof(unsigned short) * (al+1));
222           if (!flags2) return 1;
223           if (al) memcpy(flags2, flags, al * sizeof(unsigned short));
224           flags2[al] = ONLYUPCASEFLAG;
225           if (utf8) {
226               char st[BUFSIZE];
227               w_char w[BUFSIZE];
228               int wlen = u8_u16(w, BUFSIZE, word);
229               mkallsmall_utf(w, wlen, langnum);
230               mkallcap_utf(w, 1, langnum);
231               u16_u8(st, BUFSIZE, w, wlen);
232               return add_word(st,wbl,wcl,flags2,al+1,dp, true);
233            } else {
234                mkallsmall(word, csconv);
235                mkinitcap(word, csconv);
236                return add_word(word,wbl,wcl,flags2,al+1,dp, true);
237            }
238     }
239     return 0;
240 }
241
242 // detect captype and modify word length for UTF-8 encoding
243 int HashMgr::get_clen_and_captype(const char * word, int wbl, int * captype) {
244     int len;
245     if (utf8) {
246       w_char dest_utf[BUFSIZE];
247       len = u8_u16(dest_utf, BUFSIZE, word);
248       *captype = get_captype_utf8(dest_utf, len, langnum);
249     } else {
250       len = wbl;
251       *captype = get_captype((char *) word, len, csconv);
252     }
253     return len;
254 }
255
256 // remove word (personal dictionary function for standalone applications)
257 int HashMgr::remove(const char * word)
258 {
259     struct hentry * dp = lookup(word);
260     while (dp) {
261         if (dp->alen == 0 || !TESTAFF(dp->astr, forbiddenword, dp->alen)) {
262             unsigned short * flags =
263                 (unsigned short *) malloc(sizeof(short) * (dp->alen + 1));
264             if (!flags) return 1;
265             for (int i = 0; i < dp->alen; i++) flags[i] = dp->astr[i];
266             flags[dp->alen] = forbiddenword;
267             dp->astr = flags;
268             dp->alen++;
269             flag_qsort(flags, 0, dp->alen);
270         }
271         dp = dp->next_homonym;
272     }
273     return 0;
274 }
275
276 /* remove forbidden flag to add a personal word to the hash */
277 int HashMgr::remove_forbidden_flag(const char * word) {
278     struct hentry * dp = lookup(word);
279     if (!dp) return 1;
280     while (dp) {
281          if (dp->astr && TESTAFF(dp->astr, forbiddenword, dp->alen)) {
282             if (dp->alen == 1) dp->alen = 0; // XXX forbidden words of personal dic.
283             else {
284                 unsigned short * flags2 =
285                     (unsigned short *) malloc(sizeof(short) * (dp->alen - 1));
286                 if (!flags2) return 1;
287                 int i, j = 0;
288                 for (i = 0; i < dp->alen; i++) {
289                     if (dp->astr[i] != forbiddenword) flags2[j++] = dp->astr[i];
290                 }
291                 dp->alen--;
292                 dp->astr = flags2; // XXX allowed forbidden words
293             }
294          }
295          dp = dp->next_homonym;
296        }
297    return 0;
298 }
299
300 // add a custom dic. word to the hash table (public)
301 int HashMgr::add(const char * word)
302 {
303     unsigned short * flags = NULL;
304     int al = 0;
305     if (remove_forbidden_flag(word)) {
306         int captype;
307         int wbl = strlen(word);
308         int wcl = get_clen_and_captype(word, wbl, &captype);
309         add_word(word, wbl, wcl, flags, al, NULL, false);
310         return add_hidden_capitalized_word((char *) word, wbl, wcl, flags, al, NULL, captype);
311     }
312     return 0;
313 }
314
315 int HashMgr::add_with_affix(const char * word, const char * example)
316 {
317     // detect captype and modify word length for UTF-8 encoding
318     struct hentry * dp = lookup(example);
319     remove_forbidden_flag(word);
320     if (dp && dp->astr) {
321         int captype;
322         int wbl = strlen(word);
323         int wcl = get_clen_and_captype(word, wbl, &captype);
324         if (aliasf) {
325             add_word(word, wbl, wcl, dp->astr, dp->alen, NULL, false);  
326         } else {
327             unsigned short * flags = (unsigned short *) malloc (dp->alen * sizeof(short));
328             if (flags) {
329                 memcpy((void *) flags, (void *) dp->astr, dp->alen * sizeof(short));
330                 add_word(word, wbl, wcl, flags, dp->alen, NULL, false);
331             } else return 1;
332         }
333         return add_hidden_capitalized_word((char *) word, wbl, wcl, dp->astr, dp->alen, NULL, captype);
334     }
335     return 1;
336 }
337
338 // walk the hash table entry by entry - null at end
339 // initialize: col=-1; hp = NULL; hp = walk_hashtable(&col, hp);
340 struct hentry * HashMgr::walk_hashtable(int &col, struct hentry * hp) const
341 {  
342   if (hp && hp->next != NULL) return hp->next;
343   for (col++; col < tablesize; col++) {
344     if (tableptr[col]) return tableptr[col];
345   }
346   // null at end and reset to start
347   col = -1;
348   return NULL;
349 }
350
351 // load a munched word list and build a hash table on the fly
352 int HashMgr::load_tables(const char * tpath, const char * key)
353 {
354   int al;
355   char * ap;
356   char * dp;
357   char * dp2;
358   unsigned short * flags;
359   char * ts;
360
361   // open dictionary file
362   FileMgr * dict = new FileMgr(tpath, key);
363   if (dict == NULL) return 1;
364
365   // first read the first line of file to get hash table size */
366   if (!(ts = dict->getline())) {
367     HUNSPELL_WARNING(stderr, "error: empty dic file\n");
368     delete dict;
369     return 2;
370   }
371   mychomp(ts);
372
373   /* remove byte order mark */
374   if (strncmp(ts,"\xEF\xBB\xBF",3) == 0) {
375     memmove(ts, ts+3, strlen(ts+3)+1);
376     // warning: dic file begins with byte order mark: possible incompatibility with old Hunspell versions
377   }
378
379   tablesize = atoi(ts);
380   if (tablesize == 0) {
381     HUNSPELL_WARNING(stderr, "error: line 1: missing or bad word count in the dic file\n");
382     delete dict;
383     return 4;
384   }
385   tablesize = tablesize + 5 + USERWORD;
386   if ((tablesize %2) == 0) tablesize++;
387
388   // allocate the hash table
389   tableptr = (struct hentry **) malloc(tablesize * sizeof(struct hentry *));
390   if (! tableptr) {
391     delete dict;
392     return 3;
393   }
394   for (int i=0; i<tablesize; i++) tableptr[i] = NULL;
395
396   // loop through all words on much list and add to hash
397   // table and create word and affix strings
398
399   while ((ts = dict->getline())) {
400     mychomp(ts);
401     // split each line into word and morphological description
402     dp = ts;
403     while ((dp = strchr(dp, ':'))) {
404         if ((dp > ts + 3) && (*(dp - 3) == ' ' || *(dp - 3) == '\t')) {
405             for (dp -= 4; dp >= ts && (*dp == ' ' || *dp == '\t'); dp--);
406             if (dp < ts) { // missing word
407                 dp = NULL;
408             } else {
409                 *(dp + 1) = '\0';
410                 dp = dp + 2;
411             }
412             break;
413         }
414         dp++;
415     }
416
417     // tabulator is the old morphological field separator
418     dp2 = strchr(ts, '\t');
419     if (dp2 && (!dp || dp2 < dp)) {
420         *dp2 = '\0';
421         dp = dp2 + 1;
422     }
423
424     // split each line into word and affix char strings
425     // "\/" signs slash in words (not affix separator)
426     // "/" at beginning of the line is word character (not affix separator)
427     ap = strchr(ts,'/');
428     while (ap) {
429         if (ap == ts) {
430             ap++;
431             continue;
432         } else if (*(ap - 1) != '\\') break;
433         // replace "\/" with "/"
434         for (char * sp = ap - 1; *sp; *sp = *(sp + 1), sp++);
435         ap = strchr(ap,'/');
436     }
437
438     if (ap) {
439       *ap = '\0';
440       if (aliasf) {
441         int index = atoi(ap + 1);
442         al = get_aliasf(index, &flags, dict);
443         if (!al) {
444             HUNSPELL_WARNING(stderr, "error: line %d: bad flag vector alias\n", dict->getlinenum());
445             *ap = '\0';
446         }
447       } else {
448         al = decode_flags(&flags, ap + 1, dict);
449         if (al == -1) {
450             HUNSPELL_WARNING(stderr, "Can't allocate memory.\n");
451             delete dict;
452             return 6;
453         }
454         flag_qsort(flags, 0, al);
455       }
456     } else {
457       al = 0;
458       ap = NULL;
459       flags = NULL;
460     }
461
462     int captype;
463     int wbl = strlen(ts);
464     int wcl = get_clen_and_captype(ts, wbl, &captype);
465     // add the word and its index plus its capitalized form optionally
466     if (add_word(ts,wbl,wcl,flags,al,dp, false) ||
467         add_hidden_capitalized_word(ts, wbl, wcl, flags, al, dp, captype)) {
468         delete dict;
469         return 5;
470     }
471   }
472
473   delete dict;
474   return 0;
475 }
476
477 // the hash function is a simple load and rotate
478 // algorithm borrowed
479
480 int HashMgr::hash(const char * word) const
481 {
482     long  hv = 0;
483     for (int i=0; i < 4  &&  *word != 0; i++)
484         hv = (hv << 8) | (*word++);
485     while (*word != 0) {
486       ROTATE(hv,ROTATE_LEN);
487       hv ^= (*word++);
488     }
489     return (unsigned long) hv % tablesize;
490 }
491
492 int HashMgr::decode_flags(unsigned short ** result, char * flags, FileMgr * af) {
493     int len;
494     if (*flags == '\0') {
495         *result = NULL;
496         return 0;
497     }
498     switch (flag_mode) {
499       case FLAG_LONG: { // two-character flags (1x2yZz -> 1x 2y Zz)
500         len = strlen(flags);
501         if (len%2 == 1) HUNSPELL_WARNING(stderr, "error: line %d: bad flagvector\n", af->getlinenum());
502         len /= 2;
503         *result = (unsigned short *) malloc(len * sizeof(short));
504         if (!*result) return -1;
505         for (int i = 0; i < len; i++) {
506             (*result)[i] = (((unsigned short) flags[i * 2]) << 8) + (unsigned short) flags[i * 2 + 1]; 
507         }
508         break;
509       }
510       case FLAG_NUM: { // decimal numbers separated by comma (4521,23,233 -> 4521 23 233)
511         int i;
512         len = 1;
513         char * src = flags; 
514         unsigned short * dest;
515         char * p;
516         for (p = flags; *p; p++) {
517           if (*p == ',') len++;
518         }
519         *result = (unsigned short *) malloc(len * sizeof(short));
520         if (!*result) return -1;
521         dest = *result;
522         for (p = flags; *p; p++) {
523           if (*p == ',') {
524             i = atoi(src);
525             if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: line %d: flag id %d is too large (max: %d)\n",
526               af->getlinenum(), i, DEFAULTFLAGS - 1);
527             *dest = (unsigned short) i;
528             if (*dest == 0) HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n", af->getlinenum());
529             src = p + 1;
530             dest++;
531           }
532         }
533         i = atoi(src);
534         if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: line %d: flag id %d is too large (max: %d)\n",
535           af->getlinenum(), i, DEFAULTFLAGS - 1);
536         *dest = (unsigned short) i;
537         if (*dest == 0) HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n", af->getlinenum());
538         break;
539       }    
540       case FLAG_UNI: { // UTF-8 characters
541         w_char w[BUFSIZE/2];
542         len = u8_u16(w, BUFSIZE/2, flags);
543         *result = (unsigned short *) malloc(len * sizeof(short));
544         if (!*result) return -1;
545         memcpy(*result, w, len * sizeof(short));
546         break;
547       }
548       default: { // Ispell's one-character flags (erfg -> e r f g)
549         unsigned short * dest;
550         len = strlen(flags);
551         *result = (unsigned short *) malloc(len * sizeof(short));
552         if (!*result) return -1;
553         dest = *result;
554         for (unsigned char * p = (unsigned char *) flags; *p; p++) {
555           *dest = (unsigned short) *p;
556           dest++;
557         }
558       }
559     }
560     return len;
561 }
562
563 unsigned short HashMgr::decode_flag(const char * f) {
564     unsigned short s = 0;
565     int i;
566     switch (flag_mode) {
567       case FLAG_LONG:
568         s = ((unsigned short) f[0] << 8) + (unsigned short) f[1];
569         break;
570       case FLAG_NUM:
571         i = atoi(f);
572         if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: flag id %d is too large (max: %d)\n", i, DEFAULTFLAGS - 1);
573         s = (unsigned short) i;
574         break;
575       case FLAG_UNI:
576         u8_u16((w_char *) &s, 1, f);
577         break;
578       default:
579         s = (unsigned short) *((unsigned char *)f);
580     }
581     if (s == 0) HUNSPELL_WARNING(stderr, "error: 0 is wrong flag id\n");
582     return s;
583 }
584
585 char * HashMgr::encode_flag(unsigned short f) {
586     unsigned char ch[10];
587     if (f==0) return mystrdup("(NULL)");
588     if (flag_mode == FLAG_LONG) {
589         ch[0] = (unsigned char) (f >> 8);
590         ch[1] = (unsigned char) (f - ((f >> 8) << 8));
591         ch[2] = '\0';
592     } else if (flag_mode == FLAG_NUM) {
593         sprintf((char *) ch, "%d", f);
594     } else if (flag_mode == FLAG_UNI) {
595         u16_u8((char *) &ch, 10, (w_char *) &f, 1);
596     } else {
597         ch[0] = (unsigned char) (f);
598         ch[1] = '\0';
599     }
600     return mystrdup((char *) ch);
601 }
602
603 // read in aff file and set flag mode
604 int  HashMgr::load_config(const char * affpath, const char * key)
605 {
606   char * line; // io buffers
607   int firstline = 1;
608  
609   // open the affix file
610   FileMgr * afflst = new FileMgr(affpath, key);
611   if (!afflst) {
612     HUNSPELL_WARNING(stderr, "Error - could not open affix description file %s\n",affpath);
613     return 1;
614   }
615
616     // read in each line ignoring any that do not
617     // start with a known line type indicator
618
619     while ((line = afflst->getline())) {
620         mychomp(line);
621
622        /* remove byte order mark */
623        if (firstline) {
624          firstline = 0;
625          if (strncmp(line,"\xEF\xBB\xBF",3) == 0) memmove(line, line+3, strlen(line+3)+1);
626        }
627
628         /* parse in the try string */
629         if ((strncmp(line,"FLAG",4) == 0) && isspace(line[4])) {
630             if (flag_mode != FLAG_CHAR) {
631                 HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of the FLAG affix file parameter\n", afflst->getlinenum());
632             }
633             if (strstr(line, "long")) flag_mode = FLAG_LONG;
634             if (strstr(line, "num")) flag_mode = FLAG_NUM;
635             if (strstr(line, "UTF-8")) flag_mode = FLAG_UNI;
636             if (flag_mode == FLAG_CHAR) {
637                 HUNSPELL_WARNING(stderr, "error: line %d: FLAG needs `num', `long' or `UTF-8' parameter\n", afflst->getlinenum());
638             }
639         }
640         if (strncmp(line,"FORBIDDENWORD",13) == 0) {
641           char * st = NULL;
642           if (parse_string(line, &st, afflst->getlinenum())) {
643              delete afflst;
644              return 1;
645           }
646           forbiddenword = decode_flag(st);
647           free(st);
648         }
649         if (strncmp(line, "SET", 3) == 0) {
650           if (parse_string(line, &enc, afflst->getlinenum())) {
651              delete afflst;
652              return 1;
653           }         
654           if (strcmp(enc, "UTF-8") == 0) {
655             utf8 = 1;
656 #ifndef OPENOFFICEORG
657 #ifndef MOZILLA_CLIENT
658             initialize_utf_tbl();
659 #endif
660 #endif
661           } else csconv = get_current_cs(enc);
662         }
663         if (strncmp(line, "LANG", 4) == 0) {
664           if (parse_string(line, &lang, afflst->getlinenum())) {
665              delete afflst;
666              return 1;
667           }         
668           langnum = get_lang_num(lang);
669         }
670
671        /* parse in the ignored characters (for example, Arabic optional diacritics characters */
672        if (strncmp(line,"IGNORE",6) == 0) {
673           if (parse_array(line, &ignorechars, &ignorechars_utf16,
674                  &ignorechars_utf16_len, utf8, afflst->getlinenum())) {
675              delete afflst;
676              return 1;
677           }
678        }
679
680        if ((strncmp(line,"AF",2) == 0) && isspace(line[2])) {
681           if (parse_aliasf(line, afflst)) {
682              delete afflst;
683              return 1;
684           }
685        }
686
687        if ((strncmp(line,"AM",2) == 0) && isspace(line[2])) {
688           if (parse_aliasm(line, afflst)) {
689              delete afflst;
690              return 1;
691           }
692        }
693
694        if (strncmp(line,"COMPLEXPREFIXES",15) == 0) complexprefixes = 1;
695        if (((strncmp(line,"SFX",3) == 0) || (strncmp(line,"PFX",3) == 0)) && isspace(line[3])) break;
696     }
697     if (csconv == NULL) csconv = get_current_cs(SPELL_ENCODING);
698     delete afflst;
699     return 0;
700 }
701
702 /* parse in the ALIAS table */
703 int  HashMgr::parse_aliasf(char * line, FileMgr * af)
704 {
705    if (numaliasf != 0) {
706       HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
707       return 1;
708    }
709    char * tp = line;
710    char * piece;
711    int i = 0;
712    int np = 0;
713    piece = mystrsep(&tp, 0);
714    while (piece) {
715        if (*piece != '\0') {
716           switch(i) {
717              case 0: { np++; break; }
718              case 1: { 
719                        numaliasf = atoi(piece);
720                        if (numaliasf < 1) {
721                           numaliasf = 0;
722                           aliasf = NULL;
723                           aliasflen = NULL;
724                           HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
725                           return 1;
726                        }
727                        aliasf = (unsigned short **) malloc(numaliasf * sizeof(unsigned short *));
728                        aliasflen = (unsigned short *) malloc(numaliasf * sizeof(short));
729                        if (!aliasf || !aliasflen) {
730                           numaliasf = 0;
731                           if (aliasf) free(aliasf);
732                           if (aliasflen) free(aliasflen);
733                           aliasf = NULL;
734                           aliasflen = NULL;
735                           return 1;
736                        }
737                        np++;
738                        break;
739                      }
740              default: break;
741           }
742           i++;
743        }
744        piece = mystrsep(&tp, 0);
745    }
746    if (np != 2) {
747       numaliasf = 0;
748       free(aliasf);
749       free(aliasflen);
750       aliasf = NULL;
751       aliasflen = NULL;
752       HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
753       return 1;
754    } 
755  
756    /* now parse the numaliasf lines to read in the remainder of the table */
757    char * nl;
758    for (int j=0; j < numaliasf; j++) {
759         if (!(nl = af->getline())) return 1;
760         mychomp(nl);
761         tp = nl;
762         i = 0;
763         aliasf[j] = NULL;
764         aliasflen[j] = 0;
765         piece = mystrsep(&tp, 0);
766         while (piece) {
767            if (*piece != '\0') {
768                switch(i) {
769                   case 0: {
770                              if (strncmp(piece,"AF",2) != 0) {
771                                  numaliasf = 0;
772                                  free(aliasf);
773                                  free(aliasflen);
774                                  aliasf = NULL;
775                                  aliasflen = NULL;
776                                  HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
777                                  return 1;
778                              }
779                              break;
780                           }
781                   case 1: {
782                             aliasflen[j] = (unsigned short) decode_flags(&(aliasf[j]), piece, af);
783                             flag_qsort(aliasf[j], 0, aliasflen[j]);
784                             break; 
785                           }
786                   default: break;
787                }
788                i++;
789            }
790            piece = mystrsep(&tp, 0);
791         }
792         if (!aliasf[j]) {
793              free(aliasf);
794              free(aliasflen);
795              aliasf = NULL;
796              aliasflen = NULL;
797              numaliasf = 0;
798              HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
799              return 1;
800         }
801    }
802    return 0;
803 }
804
805 int HashMgr::is_aliasf() {
806     return (aliasf != NULL);
807 }
808
809 int HashMgr::get_aliasf(int index, unsigned short ** fvec, FileMgr * af) {
810     if ((index > 0) && (index <= numaliasf)) {
811         *fvec = aliasf[index - 1];
812         return aliasflen[index - 1];
813     }
814     HUNSPELL_WARNING(stderr, "error: line %d: bad flag alias index: %d\n", af->getlinenum(), index);
815     *fvec = NULL;
816     return 0;
817 }
818
819 /* parse morph alias definitions */
820 int  HashMgr::parse_aliasm(char * line, FileMgr * af)
821 {
822    if (numaliasm != 0) {
823       HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
824       return 1;
825    }
826    char * tp = line;
827    char * piece;
828    int i = 0;
829    int np = 0;
830    piece = mystrsep(&tp, 0);
831    while (piece) {
832        if (*piece != '\0') {
833           switch(i) {
834              case 0: { np++; break; }
835              case 1: { 
836                        numaliasm = atoi(piece);
837                        if (numaliasm < 1) {
838                           HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
839                           return 1;
840                        }
841                        aliasm = (char **) malloc(numaliasm * sizeof(char *));
842                        if (!aliasm) {
843                           numaliasm = 0;
844                           return 1;
845                        }
846                        np++;
847                        break;
848                      }
849              default: break;
850           }
851           i++;
852        }
853        piece = mystrsep(&tp, 0);
854    }
855    if (np != 2) {
856       numaliasm = 0;
857       free(aliasm);
858       aliasm = NULL;
859       HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
860       return 1;
861    } 
862
863    /* now parse the numaliasm lines to read in the remainder of the table */
864    char * nl = line;
865    for (int j=0; j < numaliasm; j++) {
866         if (!(nl = af->getline())) return 1;
867         mychomp(nl);
868         tp = nl;
869         i = 0;
870         aliasm[j] = NULL;
871         piece = mystrsep(&tp, ' ');
872         while (piece) {
873            if (*piece != '\0') {
874                switch(i) {
875                   case 0: {
876                              if (strncmp(piece,"AM",2) != 0) {
877                                  HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
878                                  numaliasm = 0;
879                                  free(aliasm);
880                                  aliasm = NULL;
881                                  return 1;
882                              }
883                              break;
884                           }
885                   case 1: {
886                             // add the remaining of the line
887                             if (*tp) {
888                                 *(tp - 1) = ' ';
889                                 tp = tp + strlen(tp);
890                             }
891                             if (complexprefixes) {
892                                 if (utf8) reverseword_utf(piece);
893                                     else reverseword(piece);
894                             }
895                             aliasm[j] = mystrdup(piece);
896                             if (!aliasm[j]) {
897                                  numaliasm = 0;
898                                  free(aliasm);
899                                  aliasm = NULL;
900                                  return 1;
901                             }
902                             break; }
903                   default: break;
904                }
905                i++;
906            }
907            piece = mystrsep(&tp, ' ');
908         }
909         if (!aliasm[j]) {
910              numaliasm = 0;
911              free(aliasm);
912              aliasm = NULL;
913              HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
914              return 1;
915         }
916    }
917    return 0;
918 }
919
920 int HashMgr::is_aliasm() {
921     return (aliasm != NULL);
922 }
923
924 char * HashMgr::get_aliasm(int index) {
925     if ((index > 0) && (index <= numaliasm)) return aliasm[index - 1];
926     HUNSPELL_WARNING(stderr, "error: bad morph. alias index: %d\n", index);
927     return NULL;
928 }