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