/* vi: set sw=4 ts=4: */ #include #include #include #include #include #include #include "defs.h" #include "convert.h" #include "hzinput.h" #include "pinyin.h" #include "safestring.h" #include "cin2tab.h" #ifdef HAVE_NETINET_IN_H #include #endif typedef struct { u_int key1; u_int key2; union { u_int ch; u_char phrase[4]; }phr; /* not everybody supports anonymous union! */ u_short occur_seq; u_char type; } ITEM2; #define ITEMTABLE_MAXNUM 150000 /* 50000 Rui He Jul 21, 2003 changed to support GBK */ #define ITEMTABLE_TOTALSIZE (sizeof(ITEM2) * ITEMTABLE_MAXNUM) ITEM2 *ItemTable, *ItemTmp; #define PHRASE_BUFFER_SIZE 1536000 /* max 1.5M phrase buffer 700k phrase big enough? */ u_char *PhraseBuffer; typedef int (*CMPFUNCPTR)(const void *, const void *); int qcmp2(ITEM2* a, ITEM2* b) { if (a->key1 > b->key1) return 1; if (a->key1 < b->key1) return -1; if (a->key2 > b->key2) return 1; if (a->key2 < b->key2) return -1; if (a->phr.ch > b->phr.ch) return 1; if (a->phr.ch < b->phr.ch) return -1; return 0; } int qcmp(ITEM2* a, ITEM2* b) { if (a->key1 > b->key1) return 1; if (a->key1 < b->key1) return -1; if (a->key2 > b->key2) return 1; if (a->key2 < b->key2) return -1; return (int)a->occur_seq - (int)b->occur_seq; } #ifdef SUPPORT_ASSOCIATE typedef struct { int size; int pos; unsigned char *buf; }AssocItem; #define ASSOCIATE_TRUNK 128 #define ASSOCIATE_SIZE (128 * 192) AssocItem ascItem[ASSOCIATE_SIZE]; /* all zero */ int AddToAscTable(int Char, char *phrase, int len) { int index,i,l; unsigned char *p, *s; index = CalculateCharIndex(SysCoding, Char >> 8, Char & 0xFF); if (index < 0 || index >= ASSOCIATE_SIZE || len < 2) return -1; /* fail */ if (ascItem[index].buf == NULL) { ascItem[index].buf = (unsigned char *)malloc(ASSOCIATE_TRUNK); ascItem[index].size = ASSOCIATE_TRUNK; } else if (ascItem[index].pos + len >= ascItem[index].size) { ascItem[index].buf = (unsigned char *)realloc(ascItem[index].buf, ascItem[index].size + ASSOCIATE_TRUNK); ascItem[index].size += ASSOCIATE_TRUNK; } if (!ascItem[index].buf) { fprintf(stderr, "Out of memory for Associate Items!\n"); exit(-1); } s = ascItem[index].buf; /*starting */ p = ascItem[index].buf + ascItem[index].pos; /*end+1 */ while (s < p) { l = strlen(s); if (!strcmp(phrase, s)) return -1; /* duplicate */ s += l+1; /* skip to next string */ } for(i = 0; i < len ; i++) *p++ = *phrase++; *p++ = '\0'; ascItem[index].pos += len+1; return 0; } int WriteAssociateFile(char *filename) { AssociateHeader ascHeader; int i, min, max, offset; FILE *fp; strcpy(ascHeader.MagicNum, ASSOCIATE_MAGIC_NUMBER); ascHeader.Encoding = SysCoding; min = -1; max = -1; offset = 0; for(i = 0; i < ASSOCIATE_SIZE; i++) { ascItem[i].size = offset; /* current offset */ if (ascItem[i].pos > 0) { offset += ascItem[i].pos; if (min == -1) min = i; if (i > max) max = i; } } max += 1; /* add one for dummy */ if (min == -1) { fprintf(stderr, "Now associate phrase found! No file generated!\n"); return -1; } fp = fopen(filename, "w"); if (fp == NULL) { fprintf(stderr, "Error opening file %s\n", filename); exit(-1); } ascHeader.PhraseOffset = sizeof(ascHeader) + sizeof(int) * (max - min +1 ); ascHeader.MinIndex = (unsigned short)min; ascHeader.MaxIndex = (unsigned short)max; fprintf(stderr, "Now Generating LianXiang file %s: MinIndex=%d MaxIndex=%d...\n", filename, min, max); fwrite(&ascHeader, sizeof(ascHeader), 1, fp); for(i = min;i <= max; i++) fwrite(&ascItem[i].size, sizeof(int), 1, fp); for(i = min; i < max; i++) { if (ascItem[i].pos > 0) { fwrite(ascItem[i].buf, ascItem[i].pos, 1, fp); free(ascItem[i].buf); } } fclose(fp); return 0; } int GenerateAssociateFile(FILE *fr, int gen_lx, char *fname) { u_char *p, *s; int IndexChar = 0, len; while (!feof(fr)) { if (gen_lx == 1) { get_line(fr, LineBuf); arg = skip_space(LineBuf); if (strlen(arg) < 4 || arg[0] == '#' || arg[0] == '%') continue; } else { cmd_arg(fr,LineBuf, &cmd, &arg); if (cmd[0] == '%' || cmd[0] == '#' || strlen(cmd) < 2) continue; IndexChar = ((int)cmd[0]) << 8 | cmd[1]; } p = arg; while (*p && *p != '\r' && *p != '\n' && *p != '#') { while (*p && *p < 0x80) p++; /* go to a Chinese/Japanese/Korean char */ s = p; p = to_space(p); len = p - s; if (*p) *p++ = '\0'; /* still has something */ if (gen_lx == 1 && len >= 4) { IndexChar = ((int)s[0]) << 8 | s[1]; AddToAscTable(IndexChar, s+2, len-2); } else if (gen_lx == 2) { if (len >= 2) AddToAscTable(IndexChar, s, len); } } } return WriteAssociateFile(fname); } #endif static void HandleDifferentMbFormat(u_char **pcmd, u_char **parg, int *plen) { u_char *s, *t, tmp1, tmp2; int len = 0; s = *pcmd; t = *parg; if (!t[0]) /* ZiMa or MaZi? */ { if (s[0] > 0x80) /* ZiMa */ { t = s; while(*t > 0x80 && t[1] >= 0x40) t += 2; tmp1 = '\0'; *pcmd = t+1; /* pointing to ma */ while(*t) /*move the Ma one byte right */ { tmp2 = *t; *t++ = tmp1; tmp1 = tmp2; len++; } *t = tmp1; /* last byte */ *parg = s; /* pointing to zi */ *plen = len; /* key len */ } else /* MaZi */ { t = s; while(*t && *t < 0x80) t++; /* skip all the ma */ *parg = t; /* pointing to zi now */ *plen = (u_int)t - (u_int)s; } } else { if (s[0] > 0x80 && t[0] < 0x80) /* Zi Ma */ { *pcmd = t; *parg = s; } *plen = strlen(*pcmd); /* calculate key len */ } } static int CurIndex,ItemCount; static int phrasebuf_pointer=0; static int totalchar = 0, totalphrase = 0; static int maxphraselen = 0; static int keysperint; static int ignoredmap = 0; static void read_cin_phrase(FILE *fr, hz_input_table *table) { int i; u_char *save_arg; keysperint = 32 / table->key_len; /* 5 or 6 */ memset(ItemTable, 0, ITEMTABLE_TOTALSIZE ); CurIndex=0; for( ; ; cmd_arg(fr,LineBuf,&cmd,&arg)) { int len; u_int key1,key2; int k; if (!cmd[0]) { if (feof(fr)) break; continue; } if (cmd[0] == '%') /* skip some unknown keywords */ { if (!strcasecmp(cmd, "%chardef")) { if (!strcasecmp(arg, "end")) break; /* end */ if (!strcasecmp(arg, "begin")) continue; /* read one line more */ continue; /* continue anyway */ } if (table->KeyMap[cmd[0]] <= 0) /* not a valid input key? */ { fprintf(stderr, "Unknown CIN keyword %s in mapping section, ignored.\n", cmd); continue; } } HandleDifferentMbFormat(&cmd, &arg, &len); if (len < 1 || !cmd[0] || !arg[0]) { fprintf(stderr, "Warning: Line %d invalid mapping '%s %s' ignored.\n", lineno, cmd, arg); ignoredmap++; continue; } if (len > table->MaxPress) table->MaxPress=len; if (len > 2 * keysperint) { fprintf(stderr, "Key %s: keylen %d detected, wll take max %d keys!\n", cmd, len, 2 * keysperint); len = 2 * keysperint; } key1=0; key2=0; for(i=0;iKeyMap[cmd[i]]; if (k <= 0 && cmd[i] >= 'A' && cmd[i] <= 'Z') k = table->KeyMap[tolower(cmd[i])]; if (k == 0) { fprintf(stderr, "Line %d Error: key %c(0x%02X) of '%s %s' is not in keymap!\n", lineno, cmd[i], cmd[i], cmd, arg); ignoredmap++; continue; } if(i < keysperint /* 5 */) /* 5 * 6 bit/key = 30 bits */ { key1 |= k << ((keysperint - i - 1) * table->key_len); } else { key2 |= k << ((2 * keysperint - i - 1) * table->key_len); } } /* for */ for (; *arg ; arg = save_arg) { while(*arg == ' ' || *arg == '\t') arg++; /* skip the space */ len = 0; while(arg[len] && arg[len] != ' ' && arg[len] != '\t' && arg[len] != '\n' && arg[len] != '\r') len++; /* skip to space or end of line */ save_arg = arg+len; if (*save_arg) *save_arg++ = '\0'; /* terminate the current string */ if (len < 2 || len % 2 == 1) { #ifdef DEBUG fprintf(stderr, "Warning: Line %d invalid phrase %s(bytes=%d), ignored.\n", lineno, arg, len); #endif ignoredmap++; continue; /* len -= 1; // some error skip */ } if (!VerifyMapping(cmd, arg, len)) continue; /* skip */ memcpy(&ItemTable[CurIndex].key1,&key1,4); memcpy(&ItemTable[CurIndex].key2,&key2,4); if (len==2 || charonly) /* && (int)(*arg) > 0xA0 && (int)(*(arg+1)) > 0xA0) // FIXME!!! */ { while(len > 1) { memcpy(&ItemTable[CurIndex].phr.ch,arg,2); ItemTable[CurIndex].phr.phrase[2] = ItemTable[CurIndex].phr.phrase[3] = '\0'; ItemTable[CurIndex].type = TYPE_CHAR; /* mark it as char */ len -= 2; if (len >= 2) { ItemTable[CurIndex].occur_seq=CurIndex; CurIndex++; /* close the old one */ if (CurIndex >= ITEMTABLE_MAXNUM) break; memcpy(&ItemTable[CurIndex].key1,&key1,4); memcpy(&ItemTable[CurIndex].key2,&key2,4); arg += 2; /* move to next char */ } } } else { int m; ItemTable[CurIndex].type = TYPE_PHRASE; if (len > 4) { if ((unsigned)(phrasebuf_pointer + len + 1) >= PHRASE_BUFFER_SIZE) { fprintf(stderr, "Phrase buffer overrun: %d + %d > %d, please increase it.\n", phrasebuf_pointer, len, PHRASE_BUFFER_SIZE); abort(); } if (len > MAX_PHRASE_LENGTH) { fprintf(stderr, "Line %d Warning: Phrase '%s' Len %d ignored(MaxLen=%d).\n", lineno, arg, len/2, MAX_PHRASE_LENGTH/2); ignoredmap++; continue; } ItemTable[CurIndex].phr.phrase[0] = ((u_int)(phrasebuf_pointer) >> 24) & 0xFF; ItemTable[CurIndex].phr.phrase[1] = ((u_int)(phrasebuf_pointer) >> 16) & 0xFF; ItemTable[CurIndex].phr.phrase[2] = ((u_int)(phrasebuf_pointer) >> 8) & 0xFF; ItemTable[CurIndex].phr.phrase[3] = ((u_int)(phrasebuf_pointer)) & 0xFF; strcpy(&PhraseBuffer[phrasebuf_pointer],arg); phrasebuf_pointer += len+1; /* including the ending 0 */ } else { strcpy((unsigned char *)&(ItemTable[CurIndex].phr.ch), arg); /* <=4 */ } if (len > maxphraselen) maxphraselen = len; #ifdef SUPPORT_ASSOCIATE if (gen_lx == 3) { m = ((int)arg[0]) << 8 | arg[1]; AddToAscTable(m, arg+2, len-2); } #endif } /* else */ ItemTable[CurIndex].occur_seq=CurIndex; /* occur_seq for both char/phrase */ CurIndex++; if (CurIndex >= ITEMTABLE_MAXNUM) break; } /* while (*arg) */ if (CurIndex >= ITEMTABLE_MAXNUM) { fprintf(stderr, "\nERROR: Maximum mapping %d reached, ignoring the remaining!\n\n", ITEMTABLE_MAXNUM); break; } } /* for loop */ } static void eliminate_duplicate_phrase(hz_input_table *table) { u_short CharDef[MAX_INPUT_KEYS]; int i; ITEM2 LastItem; qsort(ItemTable,CurIndex,sizeof(ITEM2), (CMPFUNCPTR)qcmp2); memset(&LastItem, 0, sizeof(ITEM2)); ItemCount=0; for(i=0;i> ( (keysperint-1)*table->key_len /*24*/ )) & (( 1 << table->key_len)-1); if (!CharDef[kk]) { table->KeyIndex[kk]=(u_short)i; /* start index */ CharDef[kk]=1; } } table->KeyIndex[table->TotalKey]=CurIndex; /* last one is dummy, used for EndKey */ for(i=table->TotalKey-1; i>0; i--) if (!CharDef[i]) table->KeyIndex[i] = table->KeyIndex[i+1]; if (table->wildcard) /* keyCurIndex */ { /* wildcard at first char, will map to KeyIndex[1], last one will map to CurIndex */ table->KeyIndex[(int)table->wildcard] = table->KeyIndex[1]; table->KeyIndex[(int)table->wildcard+1] = CurIndex; } #ifdef DEBUG for(i = 0; i < 64; i++) fprintf(stderr, "Index %d = %d\n",i,table->KeyIndex[i]); #endif } static void write_tab_file(char *filename, hz_input_table *table) { int i; FILE *fw; if ((fw=fopen(filename,"w"))==NULL) { print_error("Failed to create output file!"); } if (table->MaxPress <= keysperint) /* can use one integer to hold all the keys */ table->ItemBytes = 8; /* ch + key1 */ else table->ItemBytes = 12; /* ch + key1 + key2 */ table->PhraseFileSize = phrasebuf_pointer; /* 0 for no file */ table->TotalEntries = CurIndex; table->PhraseBegin = sizeof(*table) + CurIndex * table->ItemBytes; #ifdef DEBUG fprintf(stderr, "PhraseFileSize=%d, PhraseBegin=%d InpTableSize=%d\n", table->PhraseFileSize, table->PhraseBegin, sizeof(*table)); #endif fwrite(table,sizeof(*table),1, fw); for(i=0;iItemBytes > 8) fwrite(&ItemTmp[i].key2, 4, 1, fw); #ifdef DEBUG #endif } if (phrasebuf_pointer > 0 ) /* has some phrases with more than 2 chars */ { fwrite(PhraseBuffer,1,phrasebuf_pointer + MAX_PHRASE_LENGTH,fw); /* add some padding at the end */ } fclose(fw); } int ConvertTableCIN(FILE *fr, hz_input_table *table, char *cinfname, char *tabfname, char *lxfname) { ItemTable = (ITEM2 *)malloc(ITEMTABLE_TOTALSIZE); ItemTmp = (ITEM2 *)malloc(ITEMTABLE_TOTALSIZE); PhraseBuffer = (u_char *)malloc(PHRASE_BUFFER_SIZE); if (!ItemTable || !ItemTmp || !PhraseBuffer) print_error("ConvertTableCIN: Out of memory error!\n"); read_cin_phrase(fr, table); eliminate_duplicate_phrase(table); write_tab_file(tabfname, table); fprintf(stdout, "%s:\t" /* BytesPerEntry=%d */ "MaxKeyNum=%d TotalInputKeys=%d KeyBitsLen=%d\n" "%s:\t%d Single Chars %d Phrases(MaxLen=%d) %d Ignored Total %d\n", cinfname, /* table->ItemBytes, */ table->MaxPress, table->TotalKey-1, table->key_len, table->cname, totalchar, totalphrase, maxphraselen/2, ignoredmap, ItemCount); #ifdef SUPPORT_ASSOCIATE if (gen_lx == 3) WriteAssociateFile(lxfname); #endif fclose(fr); return 0; }