/* vi: set sw=4 ts=4: */ /* * CCE - Console Chinese Environment - * Copyright (C) 1998-2003 Rui He (rhe@3eti.com) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #include #include #include #include #include #include "defs.h" #include "convert.h" #include "errors.h" #ifdef HAVE_SYS_MMAN_H #include #endif int CalculateCharIndex(int coding, unsigned int High, unsigned int Low) { if (coding == CODE_UNICODE) return (High << 8 | Low); if (High == 0xFF || Low == 0xFF || Low == 0x7F) return -1; switch(coding) { case CODE_2022: return -1; /* not supported yet */ case CODE_SJIS: /* SJIS: {81-9F E0-EF} {40-7E 80-FC} */ if (High < 0x81 || (High > 0x9F && High < 0xE0) || High > /* 0xFC */ 0xEF || Low < 0x40 || Low > 0xFC) return -1; High -= (High >= 0xE0 ? 0xC1 : 0x81); Low -= (Low > 0x7F ? 0x41: 0x40); /* excluding 0x7F */ return High * (94*2) + Low; case CODE_GB: case CODE_KSC: case CODE_EUC: if (High < 0xA1 || Low < 0xA1) return -1; /* A1-FE */ return (High - 0xA1) * 94 + (Low - 0xA1); case CODE_UHC: /*41-5A/61-7A/81-FE 126 * 178: 41-5A(0-0x19) 61-7A(0x1A-0x33) 81-FE(0x34-0xB1) */ if (Low >= 0x81) Low -= 0x4D; else if (Low > 0x7A) return -1; else if (Low >= 0x61) Low -= 0x47; else if (Low > 0x5A) return -1; else if (Low >= 0x41) Low -= 0x41; else return -1; return (High - 0x81) * 178 + Low; case CODE_BIG5: if (High < 0xA1 || Low < 0x40 || (Low > 0x7E && Low < 0xA1)) return -1; if (Low < 0xA1) Low -= 0x40; else Low -= 0x62; return (High - 0xA1) * 157 + Low; case CODE_GBK: if (High < 0x81 || Low < 0x40) return -1; if (Low > 0x7F) Low--; /* skip the 0x7F invalid code */ return (High - 0x81) * 190 + (Low - 0x40); default: return -1; } } int ConvertIndexToChar(int coding, int index, u_char buf[]) { int High, Low; switch(coding) { case CODE_2022: return -1; /* not supported yet */ case CODE_SJIS: /* SJIS: {81-9F E0-EF} {40-7E 80-FC} */ High = index / (94*2) + 0x81; if (High >= 0xA0) High += 0xE0 - 0xA0; Low = index % (94 * 2) + 0x40; if (Low >= 0x7F) Low++; break; case CODE_GB: case CODE_KSC: case CODE_EUC: High = (index / 94) + 0xA1; Low = (index % 94) + 0xA1; break; case CODE_UHC: /*41-5A/61-7A/81-FE 126 * 178: 41-5A(0-0x19) 61-7A(0x1A-0x33) 81-FE(0x34-0xB1) */ High = index / 178 + 0x81; Low = index % 178; if (Low >= 0x34) Low += 0x81-0x34; else if (Low >= 0x1A) Low += 0x61 - 0x1A; else Low += 0x41; break; case CODE_BIG5: High = index / 157 + 0xA1; Low = index % 157 + 0x40; if (Low >= 0x7F) Low += 0xA1 - 0x7F; /* to 0xA1-0xFE */ break; case CODE_GBK: High = index / 190 + 0x81; Low = index % 190 + 0x40; if (Low >= 0x7F) Low++; /* skip 0x7F */ break; case CODE_UNICODE: High = index >> 8; Low = index & 0xFF; break; default: return -1; } if (High == 0xFF || Low == 0xFF || Low == 0x7F) return -1; if (buf) { buf[0] = (u_char)High; buf[1] = (u_char)Low; } return High << 8 | Low; } int LastCodePoint[CODE_NUM] = { 0x0000, 0xFEFE, 0xEFFC, /* 2022, EUC, SJIS */ 0xFEFE, 0xFEFE, 0xFEFE, /* Big5, GB, GBK */ 0xFEFE, 0xFEFE, 0xFFFF, /* KSC, UHC, Unicode */ }; int GetEncodingSpace(int coding) { return CalculateCharIndex(coding, LastCodePoint[coding] >> 8, LastCodePoint[coding] & 0xFF)+1; } #if !defined(CONVTABLE_USE_MMAP) #include "gencceconv.c" #else /* CONVTABLE_USE_MMAP */ ConvTableHeader *Header; unsigned short *ConvertTable[CODE_NUM][CODE_NUM]; unsigned int ConvertTableSize[CODE_NUM][CODE_NUM]; int MmapConvertTable(char *filename) { int fd = -1, size, i, j; unsigned char *p; if (Header) return (int)Header; /* already mapped(>0) or tried(-1) */ if ((fd = open(filename, O_RDONLY|CCE_OPENFLAG)) < 0 || read(fd, &size, sizeof(int)) != sizeof(int) || size <= 0 || (p = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { if (fd >= 0) close(fd); Header = (ConvTableHeader *)-1; /* tried, but failed 0 not try yet */ return -1; } Header = (ConvTableHeader *)p; for(i = 0; i < CODE_NUM; i++) for(j = 0; j < CODE_NUM; j++) { if (Header->ConvertTableOffset[i][j] > 0) ConvertTable[i][j] = (unsigned short *)(p + Header->ConvertTableOffset[i][j]); else ConvertTable[i][j] = NULL; } memcpy( &ConvertTableSize, Header->ConvertTableSize, sizeof(ConvertTableSize)); #ifdef DEBUG fprintf(stderr, "Mmapped CCE conversion table %s size %d at address %p\r\n", filename, size, p); #endif close(fd); /* won't affect mmapped region */ return 0; } int MunmapConvertTable(void) { int ret = -1; if (Header != NULL && Header != (ConvTableHeader*)-1 && Header->size > 0) { #ifdef DEBUG fprintf(stderr, "Munmapped CCE conversion table size %d at address %p\r\n", Header->size, Header); #endif ret = munmap(Header, Header->size); if (ret == 0) Header = NULL; /* success, clear it */ } return ret; } #endif /* CONVTABLE_USE_MMAP */ int ShiftJIS2JIS(unsigned int ch, unsigned int cl, unsigned char *OutCh, unsigned char *OutCl) { ch -= (ch > 0x9F) ? 0xB1: 0x71; ch = (ch << 1) + 1; if (cl > 0x9E) { cl = cl - 0x7E; /* [9F-FC] -> [21-7E] */ ch ++; /* [22-7E, even] */ } else { if (cl > 0x7E) cl --; /* [80-9E] -> [7F-9D] */ cl -= 0x1F; /* [40-7E 7F-9D] => [21-7E] */ } ch |= 0x80; cl |= 0x80; /* set the high bit */ if (OutCh) *OutCh = ch & 0xFF; if (OutCl) *OutCl = cl & 0xFF; /* just for safety */ return (ch << 8) | cl; } int JIS2ShiftJIS(unsigned int ch, unsigned int cl, unsigned char *OutCh, unsigned char *OutCl) { ch &= 0x7F; cl &= 0x7F; /* make sure it's in 21-7E range */ if (ch & 1) /* odd [21-5F 60-7E] => [40-7E 80-9E] 94 codepoints */ cl += (cl > 0x5F ? 0x20 : 0x1F); else cl += 0x7E; /* even, [21-7E] => [9F->FC] 94 codepoints */ ch = ((ch - 0x21) >> 1) + 0x81; if (ch > 0x9F) ch += 0x40; if (OutCh) *OutCh = ch; if (OutCl) *OutCl = cl; return (ch << 8) | cl; } int ConvertEncoding_Char(int inCoding, int outCoding, unsigned char High, unsigned char Low, unsigned char *OutHigh, unsigned char *OutLow) { int OutChar = -1, index; int finalOutCoding = -1; index = CalculateCharIndex(inCoding, High, Low); if (index >= 0 && inCoding == CODE_SJIS) { if (outCoding != CODE_SJIS) /* output is different */ { ShiftJIS2JIS(High, Low, &High, &Low); inCoding = CODE_EUC; index = CalculateCharIndex(inCoding, High, Low); } } else if (outCoding == CODE_SJIS) { finalOutCoding = CODE_SJIS; outCoding = CODE_EUC; } else if (outCoding == CODE_UHC) { finalOutCoding = CODE_UHC; outCoding = CODE_KSC; /* only 1 path from KSC->UHC */ } do { if (index < 0) return -1; /* input char encoding invalid */ if (inCoding == outCoding || (inCoding == CODE_GB && outCoding == CODE_GBK) || (inCoding == CODE_KSC && outCoding == CODE_UHC) ) { OutChar = (High << 8 | Low ); } else if ((inCoding == CODE_GBK && outCoding == CODE_GB) || (inCoding == CODE_UHC && outCoding == CODE_KSC)) { if (High < 0xA1 || Low < 0xA1) /* both GB/GBK KSC/UHC doesn't allow 0xFF */ return -1; OutChar = (High << 8 | Low); } else /* now we need to use the ConvertTable */ { #ifdef CONVTABLE_USE_MMAP if (Header == NULL) MmapConvertTable(CCELIB "/" CONV_TABLE_FILE); #endif if (ConvertTable[inCoding][outCoding] ) /* there is a convert table exist! */ { if ( index >= (int)ConvertTableSize[inCoding][outCoding] || /* NULL is 0 */ (OutChar = (int)ConvertTable[inCoding][outCoding][index]) <= 0 ) { return -1; } } else /* do direct ConvertTable exist, lets go to GB2312 first */ { if (inCoding == CODE_GBK) /* no table for GBK->GB */ { if (High < 0xA1 || Low < 0xA1) return -1; } else { int TmpOutChar; if ( index >= (int)ConvertTableSize[inCoding][CODE_GB] || (TmpOutChar = (int)ConvertTable[inCoding][CODE_GB][index]) <= 0 ) return -1; /* can't convert to GB either! failed! */ High = TmpOutChar >> 8; Low = TmpOutChar & 0xFF; } inCoding = CODE_GB; index = CalculateCharIndex(CODE_GB, High, Low); } } }while(OutChar < 0); /* OutChar > 0 terminate condition */ if (outCoding == CODE_EUC && finalOutCoding == CODE_SJIS) return JIS2ShiftJIS(OutChar >> 8, OutChar & 0xFF, OutHigh, OutLow); if (OutHigh) *OutHigh = (unsigned char)(OutChar >> 8); if (OutLow) *OutLow = (unsigned char)(OutChar & 0xFF); return OutChar; }