/* 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" #define MAX_FREQ_2BYTES 65535 char LineBuf[LINE_BUFFER_LEN]; int lineno; u_char *cmd, *arg; int OldCoding; /* source encoding */ int NewCoding; /* target encoding */ int YinFormat; /* 1 pinyin or 2 zhuyin(libtabe format) */ char CurCINPath[CCE_PATHNAME_MAX]; void print_error(char *fmt,...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr,"\n"); exit(-1); } u_char *skip_space(u_char *s) { while ((*s==' ' || *s=='\t') /* && *s */) s++; return s; } u_char *to_space(u_char *s) { while (*s && *s!=' ' && *s!='\t' && *s!='\r' && *s != '\n') s++; return s; } void del_nl_space(u_char *s) { u_char *t; int len=strlen(s); if (!*s) return; t=s+len-1; while ( t >= s && (*t=='\n' || *t==' ' || *t=='\t' || *t == '\r' ) ) t--; t[1]='\0'; } void get_line(FILE *fr, u_char *buf) { while (!feof(fr)) { buf[0] = '\0'; /* empty string, file end */ fgets(buf, LINE_BUFFER_LEN, fr); lineno++; if (*skip_space(buf) == '#') continue; else return; } } void cmd_arg(FILE *fr, u_char *s, u_char **cmd, u_char **arg) { char *t; get_line(fr, s); if (!*s) { *cmd=*arg=s; return; } /* all zero */ s=skip_space(s); t=to_space(s); *cmd=s; if (!(*t)) /* empty */ { *arg=t; return; } *t++ = '\0'; t=skip_space(t); del_nl_space(t); *arg=t; } int IsGB2312(unsigned char ch, unsigned char cl) { return (ch >= 0xA1 && ch <= 0xFE && cl >= 0xA1 && cl <= 0xFE); } int IsGBK(unsigned char ch, unsigned char cl) { return (ch >= 0x81 && ch <= 0xFE && cl >= 0x40 && cl <= 0xFE && cl != 0x7F); } int IsBig5(unsigned char ch, unsigned char cl) { return (ch >= 0xA1 && ch <= 0xFE && ((cl >= 0x40 && cl <= 0x7E) || (cl >= 0xA1 && cl <= 0xFE))); } int IsJIS(unsigned char ch, unsigned char cl) { return (ch >= 0xA1 && ch <= 0xFE && cl >= 0xA1 && cl <= 0xFE); } int IsKSC(unsigned char ch, unsigned char cl) { return (ch >= 0xA1 && ch <= 0xFE && cl >= 0xA1 && cl <= 0xFE); } static char *Coding[] = { "Not Checked", /* "ISO-2022-JP", */ "EUC-JP", "Shift-JIS", "Big5", "GB2312", "GBK", "KSC"}; int SysCoding = CODE_GB; int VerifyCoding(unsigned char ch, unsigned char cl) { int valid; switch(SysCoding) { case CODE_GB: valid = IsGB2312(ch, cl); break; case CODE_GBK: valid = IsGBK(ch, cl); break; case CODE_BIG5: valid = IsBig5(ch, cl); break; case CODE_EUC: valid = IsJIS(ch, cl); break; case CODE_KSC: valid = IsKSC(ch, cl); break; case SYSCODE_NOCHECK: return 1; /* don't check */ default: valid = ( ch >= 0x80 && cl >= 0x21); break; } if (!valid) { return 0; } return 1; } int VerifyMapping(char *key, char *str, int len) { int i; int valid = 1; for(i = 0; i < len; i += 2) { if (!VerifyCoding(str[i], str[i+1])) { valid = 0; break; } } if (!valid) { fprintf(stderr, "Line %d: Mapping '%s %s' is not a valid %s string!\n", lineno, key, str, Coding[SysCoding]); return 0; } return 1; } int identify_encoding(u_char *arg) { if (!strncasecmp(arg, "gbk", 3)) return CODE_GBK; if (!strncasecmp(arg, "gb", 2)) return CODE_GB; if (!strncasecmp(arg, "big", 3) || !strncasecmp(arg, "b5", 2)) return CODE_BIG5; if (!strncasecmp(arg, "jis", 3)) return CODE_EUC; if (!strncasecmp(arg, "ks", 2)) return CODE_KSC; return -1; } static int TotalKeyNum; static u_char wildcardchar = '\0'; #define HEX_STR "0123456789abcdef" #define OCT_STR "01234567" static int get_keys_from_string(u_char *str, u_char *dest, int num) { int count, i, len; int value; u_char *hexstr = HEX_STR; u_char *octstr = OCT_STR; i = 0; while(str[i] && str[i] != ' ' && str[i] != '\t' && str[i] != '\r' && str[i] != '\n') { str[i] = tolower(str[i]);/* A-Z -> a-z */ i++; } str[i] = '\0'; /* terminate it */ len = i; dest[0] = '\0'; count = 0; for(i = 0; i < len; i++) { if (count >= num) return count; if (str[i] == '\\') { if (str[i+1] == 'x') /* \xab */ { if (len-i <=3 || !strchr(hexstr, str[i+2]) || !strchr(hexstr, str[i+3])) fprintf(stderr, "Unknown key \\x%c%c, ignored !\n", str[i+2] ? str[i+2] : ' ', str[i+3] ? str[i+3] : ' '); else { value = (u_int)strchr(hexstr, str[i+2]) - (u_int)hexstr; value = value * 16 + (u_int)strchr(hexstr, str[i+3]) - (u_int)hexstr; dest[count++] = (u_char)(value); } i += 3; /* skip \xab */ continue; } else if (strchr(octstr, str[i+1])) /* \0-7 */ { if (len-i <=3 || !strchr(octstr, str[i+2]) || !strchr(octstr, str[i+3])) { fprintf(stderr, "Unknown key \\%c%c%c, ignored !\n", str[i+1], str[i+2] ? str[i+2] : ' ', str[i+3] ? str[i+3] : ' '); } else { value = (str[i+1] - '0')*64 + (str[i+2] - '0')*8 + (str[i+3] - '0'); dest[count++] = (u_char)(value); } i += 3; /* skip \xab */ continue; } else if (str[i+1] == '\\') { dest[count++] = str[i+1]; } else { fprintf(stderr, "Unknown key \\%c, not supported!\n", str[i+1]); } i++; /* skip the */ } else { dest[count++] = str[i]; } } dest[count] = '\0'; #ifdef DEBUG fprintf(stderr, "Keys(str=%s, len=%d): ", str, len); for(i = 0; i < count; i++) fprintf(stderr, "%02X ", dest[i]); fprintf(stderr, "\n"); #endif return count; } void read_cin_options(FILE *fr, hz_input_table *table) { while(!feof(fr) && strcasecmp(cmd, CIN_KEYNAME_STR) && strcasecmp(cmd, CIN_CHARDEF_STR) ) /* not equal to keyname & chardef */ { if (!strcasecmp(cmd, "%gen_inp")) { /* skip the first line, for XCIN compatibility */ } else if ((!strcasecmp(cmd, "%encoding") || !strcasecmp(cmd, "%coding")) && (*arg)) { int coding = identify_encoding(arg); if (coding >= 0) table->encoding = coding; } else if (!strcasecmp(cmd,"%ename") && (*arg) ) { safe_strncpy(table->ename, arg, sizeof (table->ename)); } else if ((!strcasecmp(cmd,"%prompt") || !strcasecmp(cmd, "%cname")) && (*arg) ) { safe_strncpy(table->cname, arg, sizeof (table->cname)); } else if (strcasecmp(cmd,"%selkey") == 0 && (*arg) ) { safe_strncpy(table->selkey, arg, sizeof (table->selkey)); } else if (strcasecmp(cmd, "%endkey") == 0) { } else if (strcasecmp(cmd, "%pageprevkey") == 0) { get_keys_from_string(arg, table->pageprevkey, MAX_CHANGEPAGE_KEYS); } else if (strcasecmp(cmd, "%pagenextkey") == 0) { get_keys_from_string(arg, table->pagenextkey, MAX_CHANGEPAGE_KEYS); } else if (strcasecmp(cmd, "%selfirstkey") == 0) { get_keys_from_string(arg, table->selfirstkey, MAX_SELFIRST_KEYS); } else if (strcasecmp(cmd, "%wildcardkey") == 0 && (*arg)) { if (arg[0] > ' ' && arg[0] < 127) /* make sure it's a printable char */ wildcardchar = arg[0]; /* SPACE and DEL is not allowed */ } else if (strcasecmp(cmd, "%maxpress") == 0 && (*arg)) { } else if ((strcasecmp(cmd,"%last_full") == 0 || strcasecmp(cmd, "%auto_select") == 0 ) && (*arg)) { if ( arg[0]=='0' || arg[0] == 'n' || arg[0] == 'N') table->auto_select = 0; } else if (strcasecmp(cmd, "%prefix_match") == 0 && (*arg)) { if ( arg[0]=='0' || arg[0]=='n' || arg[0]=='N') table->prefix_match_enable = 0; } else if (strcasecmp(cmd, "%key_prompt") == 0 && (*arg)) { if (arg[0] == '1' || arg[0] == 'y' || arg[0] == 'Y') table->key_prompt = 1; } else if (strcasecmp(cmd,"%dupsel") == 0 && (*arg) ) { table->MaxDupSel=atoi(arg); } else if ((strcasecmp(cmd,"%keylen") == 0 || strcasecmp(cmd, "%key_len") == 0) && (*arg) ) { table->key_len = atoi(arg); if (table->key_len != 5 && table->key_len != 6) { fprintf(stderr, "Warning: unrecognized key length %d! Using default 6.\n", table->key_len); table->key_len = 6; } } else if (strcasecmp(cmd, "%input_type") == 0 && (*arg) ) { if (strcasecmp(arg, "intcode") == 0) table->InputType = INPUT_TYPE_INTCODE; else if (strcasecmp(arg, "pinyin") == 0 || strcmp(arg, "zhiyin") == 0) table->InputType = INPUT_TYPE_PINYIN; else if (strcasecmp(arg, "zhuyin") == 0 || strcasecmp(arg, "phone") == 0) table->InputType = INPUT_TYPE_ZHUYIN; } else { if (cmd[0] == '%') /* added this one, otherwise too many? */ fprintf(stderr, "Unknown CIN keyword %s, ignored\n", cmd); else if (cmd[0] > ' ') break; /* if meet non-% ASCII stuff, go out directly */ } cmd_arg(fr,LineBuf,&cmd, &arg); } if (feof(fr)) print_error("It is not a valid CCE input method file.\n"); if (table->MaxDupSel <= 0) { table->MaxDupSel = strlen(table->selkey); if (table->MaxDupSel <= 0) print_error("No %%dupsel entry found and %%selkey entry error!"); #ifdef DEBUG fprintf(stderr, "Warning: no %%dupsel entry found, using %d\n", table->MaxDupSel); #endif } if (table->MaxDupSel > MAX_SELECT_KEYS) { fprintf(stderr, "Warning: %%dupsel is %d, using maximum allowed value %d\n", table->MaxDupSel, MAX_SELECT_KEYS); table->MaxDupSel = MAX_SELECT_KEYS; table->selkey[table->MaxDupSel] = '\0'; /* terminated the selkey string */ } } static void get_input_keys(u_char *p, u_char TmpKeyName[][2], hz_input_table *table) { int i; table->keyname_bytes = 1; /* always ASCII */ TotalKeyNum = strlen(p); if (TotalKeyNum > MAX_INPUT_KEYS-3) TotalKeyNum = MAX_INPUT_KEYS-3; for(i = 0; i < TotalKeyNum; i++) { table->KeyMap[p[i]] = i+1; TmpKeyName[i+1][0] = p[i]; TmpKeyName[i+1][1] = '\0'; } } void read_cin_keyname(FILE *fr, hz_input_table *table) { int i, j, k; u_char TmpKeyName[MAX_INPUT_KEYS][2]; if (strcasecmp(cmd, CIN_KEYNAME_STR)) { fprintf(stderr, "Line %d: %" CIN_KEYNAME_STR " unfound, using default a-z.\n", lineno); get_input_keys(DEFAULT_INPUT_KEYS, TmpKeyName, table); } else if (strcasecmp(arg,"begin")) { get_input_keys(arg, TmpKeyName, table); } else /* multiple lines format */ { TotalKeyNum=0; while(!feof(fr)) { cmd_arg(fr,LineBuf,&cmd, &arg); if (cmd[0] == '%' && cmd[1] > ' ' ) { if (strcasecmp(cmd, CIN_KEYNAME_STR) || strcasecmp(arg, "end")) fprintf(stderr, "Line %d: '%" CIN_KEYNAME_STR " end' expected.\n", lineno); if (!strcasecmp(cmd, CIN_KEYNAME_STR)) cmd_arg(fr, LineBuf, &cmd, &arg); /* read-one-more, that's the tradition */ break; /* keyname end */ } if (!cmd[0] || !arg[0]) continue; /* ignore incomplete line */ k = (int)cmd[0]; if (table->KeyMap[k] > 0) { fprintf(stderr, "Line %d: key %c is already used\n",lineno, k); continue; /* ignore 2nd one */ } if (k == wildcardchar) { fprintf(stderr,"Line %d: key %c is same as the wildcard key", lineno, k); wildcardchar = '\0'; } table->KeyMap[k] = ++TotalKeyNum; if (TotalKeyNum > MAX_INPUT_KEYS-3) print_error("Error, at most %d key can be defined!\n", MAX_INPUT_KEYS-3); strncpy(TmpKeyName[TotalKeyNum], arg, 2); /* we copy 2 bytes, for ASCII 2nd byte is 0 */ if ((arg[1] && table->keyname_bytes == 1 && TotalKeyNum > 1) || (arg[1] == '\0' && table->keyname_bytes == 2)) print_error("%d: You should either use ASCII or Chinese as keyname, can't use both!", lineno); if (arg[1]) table->keyname_bytes = 2; } } /* else */ fprintf(stderr, "InputKeys: \t"); j = 0; for(i = 0; i < 128; i++) if (table->KeyMap[i] > 0) { j++; /* increase the keymap (starting from 1) */ table->KeyName[j][0] = TmpKeyName[table->KeyMap[i]][0]; table->KeyName[j][1] = TmpKeyName[table->KeyMap[i]][1]; table->KeyName[j][2] = i; /*back map to the input key */ table->KeyMap[i] = j; /* reorder keymap according to ASCII */ fprintf(stderr, "%c", i); } if (wildcardchar > 0) fprintf(stderr, " (WildCard %c)", wildcardchar); fprintf(stderr, "\nSelectKeys:\t%s\n", table->selkey); ++TotalKeyNum; if (wildcardchar > 0) { table->KeyMap[(int)wildcardchar] = ++TotalKeyNum; table->KeyName[TotalKeyNum][0] = wildcardchar; table->KeyName[TotalKeyNum][1] = '\0'; table->KeyName[TotalKeyNum][2] = '\0'; table->wildcard = TotalKeyNum; } table->KeyMap[32] = 0; /* SPACE = 32, to index 1 */ table->KeyName[0][0] = ' '; table->KeyName[0][1] = (table->keyname_bytes == 2 ? ' ' : '\0'); table->KeyName[0][2] = '\0'; if (TotalKeyNum >= 32 && table->key_len <= 5) { fprintf(stderr, "Warning: Using %d input keys, key length needs to be 6!\n", TotalKeyNum); table->key_len = 6; } table->TotalKey = TotalKeyNum; /* include space */ } void read_msmb_format(FILE *fr, hz_input_table *table) { u_char UsedCodes[128]; UsedCodes[0] = '\0'; table->MaxDupSel = strlen(table->selkey); while(!feof(fr)) { cmd_arg(fr, LineBuf, &cmd, &arg); if (!strcasecmp(cmd, MSMB_TEXT_STR) || !cmd[0]) break; /* exit */ if (!strncasecmp(cmd, "Name=", strlen("Name=") )) safe_strncpy(table->cname, cmd+strlen("Name="), sizeof(table->cname)); else if (!strncasecmp(cmd, "UsedCodes=", strlen("UsedCodes=") )) { safe_strncpy(UsedCodes, cmd+strlen("UsedCodes="), sizeof(UsedCodes)); } else if (!strncasecmp(cmd, "WildChar=", strlen("WildChar="))) { wildcardchar = cmd[strlen("WildChar=")]; } } if (!UsedCodes[0]) { fprintf(stderr, "No 'UsedCodes=' found, assuming a-z.\n"); strncpy(UsedCodes, DEFAULT_INPUT_KEYS, sizeof(UsedCodes)); } cmd = CIN_KEYNAME_STR; arg = UsedCodes; /* hopefully it's not 'begin' :) */ read_cin_keyname(fr, table); } u_char *FilterFormatOptions(u_char *p, u_char *str) { p = skip_space(p); /* skip space */ if (!*p || *p == '#' || *p == '\r' || *p == '\n') return PTR_CONTINUE; if (*p != '%') return p; /* process it */ if (!strncasecmp(p, str, strlen(str))) { p += strlen(str); p = skip_space(p); if (!strncasecmp(p, "begin", 5)) return PTR_CONTINUE; /* read one line more */ else if (!strncasecmp(p, "end", 3)) return PTR_BREAK; /* end */ } else if (!strncasecmp(p, ENCODING_STR, strlen(ENCODING_STR))) { int coding; p += strlen(ENCODING_STR); p = skip_space(p); coding = identify_encoding(p); if (coding >= 0) OldCoding = coding; } else if (!strncasecmp(p, FORMAT_STR, strlen(FORMAT_STR))) { p += strlen(FORMAT_STR); p = skip_space(p); if (!strncasecmp(p, "zhuyin", 6) || !strncasecmp(p, "libtabe", 7)) YinFormat = ZHUYIN_FORMAT; else if (!strncasecmp(p, "pinyin", 6)) YinFormat = PINYIN_FORMAT; } return PTR_CONTINUE; /* skip this line */ } static int get_freq(char *f, int mode) { int freq; if (f == NULL || mode < 0) return 0; sscanf(f, "%d", &freq); if (freq < 0) freq = 0; switch (mode) { case 2: if (freq > MAX_FREQ_2BYTES - 5000) { freq = (MAX_FREQ_2BYTES - 5000) + freq/500; } if (YinFormat == PINYIN_FORMAT) { if (freq <= 255) freq *= 100; else { fprintf(stderr, "ERROR: Assuming pinyin format's freq is 0-255, but freq=%d!\n", freq); fprintf(stderr, "ERROR: Please modify the source code!\n"); exit(1); } } if (freq > MAX_FREQ_2BYTES - 16) freq = MAX_FREQ_2BYTES - 16; /* leave some space at top */ break; case 1: if (freq > 200*250) freq = 250; else if (freq < 250) freq /= 5; /* 0-50 */ else freq = freq / 250 + 50; /* 50-250 */ break; case 0: if (freq > 250) freq = 250; break; default: break; } return freq; } u_char *FilterFormatCharFreq(u_char *p, u_char *str, u_char **cp, int *lenp, int *freqp, int freqmode ) { int i, len, freq = 0; u_char *c, *f; p = FilterFormatOptions(p, str); if (p == PTR_CONTINUE || p == PTR_BREAK) return p; len = 0; c = p; while(p[0] >= 0x80 && p[1] > 0) /* all the CJK characters, first byte >= 0x80 */ { p += 2; len++; } if (!*p || len <= 0) return PTR_CONTINUE; /* continue */ *p++ = '\0'; for(i = 0; i < len; i++) { if (ConvertEncoding_Char(OldCoding, NewCoding, c[2*i], c[2*i+1], c+2*i, c+2*i+1) <= 0 || (NewCoding == CODE_BIG5 && c[2*i] >= 0xFA) ) { #ifdef DEBUG fprintf(stderr, "Phrase %s can't be converted into target encoding, ignored.\n", c); #endif return NULL; } } *cp = c; *lenp = len; p = skip_space(p); /* freq is optional */ f = p; while(*p >= '0' && *p <= '9') p++; if (*p) *p++ = '\0'; freq = get_freq(f, freqmode); /*sscanf(f, "%d", &freq); */ *freqp = freq; return p; } int LoadFileSection( u_char *cmd, u_char *arg, u_char *section, FILE *fr, int (*LoadFunc)(FILE *)) { FILE *fp; char tmpF[CCE_PATHNAME_MAX]; if (!cmd[0] || !arg[0] || strcasecmp(cmd, section)) return -1; if (!strcasecmp(arg, "begin")) /* built-in */ { (*LoadFunc)(fr); } else { if ( (fp = fopen(arg, "r")) == NULL) /* for text file, ok in DOS? */ { if (strlen(arg) + strlen(CurCINPath) < CCE_PATHNAME_MAX) { strcpy(tmpF, CurCINPath); strcat(tmpF, arg); /* won't overflow */ fp = fopen(tmpF, "r"); } } if (fp == NULL) print_error("Failed to open file %s", arg); (*LoadFunc)(fp); fclose(fp); } return 0; }