Line data Source code
1 : /* $Id$ 2 : * 3 : * Name: avc_mbyte.c 4 : * Project: Arc/Info vector coverage (AVC) E00->BIN conversion library 5 : * Language: ANSI C 6 : * Purpose: Functions to handle multibyte character conversions. 7 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca 8 : * 9 : ********************************************************************** 10 : * Copyright (c) 1999-2005, Daniel Morissette 11 : * 12 : * SPDX-License-Identifier: MIT 13 : **********************************************************************/ 14 : 15 : #include "avc.h" 16 : 17 : #ifdef _WIN32 18 : #include <mbctype.h> 19 : #endif 20 : 21 : static int _AVCDetectJapaneseEncoding(const GByte *pszLine); 22 : static const GByte *_AVCJapanese2ArcDBCS(AVCDBCSInfo *psDBCSInfo, 23 : const GByte *pszLine, 24 : int nMaxOutputLen); 25 : static const GByte *_AVCArcDBCS2JapaneseShiftJIS(AVCDBCSInfo *psDBCSInfo, 26 : const GByte *pszLine, 27 : int nMaxOutputLen); 28 : 29 : /*===================================================================== 30 : * Functions to handle multibyte char conversions 31 : *====================================================================*/ 32 : 33 : #define IS_ASCII(c) ((c) < 0x80) 34 : 35 : /********************************************************************** 36 : * AVCAllocDBCSInfo() 37 : * 38 : * Alloc and init a new AVCDBCSInfo structure. 39 : **********************************************************************/ 40 3 : AVCDBCSInfo *AVCAllocDBCSInfo(void) 41 : { 42 : AVCDBCSInfo *psInfo; 43 : 44 3 : psInfo = (AVCDBCSInfo *)CPLCalloc(1, sizeof(AVCDBCSInfo)); 45 : 46 3 : psInfo->nDBCSCodePage = AVCGetDBCSCodePage(); 47 3 : psInfo->nDBCSEncoding = AVC_CODE_UNKNOWN; 48 3 : psInfo->pszDBCSBuf = nullptr; 49 3 : psInfo->nDBCSBufSize = 0; 50 : 51 3 : return psInfo; 52 : } 53 : 54 : /********************************************************************** 55 : * AVCFreeDBCSInfo() 56 : * 57 : * Release all memory associated with a AVCDBCSInfo structure. 58 : **********************************************************************/ 59 3 : void AVCFreeDBCSInfo(AVCDBCSInfo *psInfo) 60 : { 61 3 : if (psInfo) 62 : { 63 3 : CPLFree(psInfo->pszDBCSBuf); 64 3 : CPLFree(psInfo); 65 : } 66 3 : } 67 : 68 : /********************************************************************** 69 : * AVCGetDBCSCodePage() 70 : * 71 : * Fetch current multibyte codepage on the system. 72 : * Returns a valid codepage number, or 0 if the codepage is single byte or 73 : * unsupported. 74 : **********************************************************************/ 75 3 : int AVCGetDBCSCodePage(void) 76 : { 77 : #ifdef _WIN32 78 : int nCP; 79 : nCP = _getmbcp(); 80 : 81 : /* Check if that's a supported codepage */ 82 : if (nCP == AVC_DBCS_JAPANESE) 83 : return nCP; 84 : #endif 85 : 86 3 : return 0; 87 : } 88 : 89 : /********************************************************************** 90 : * AVCE00DetectEncoding() 91 : * 92 : * Try to detect the encoding used in the current file by examining lines 93 : * of input. 94 : * 95 : * Returns TRUE once the encoding is established, or FALSE if more lines 96 : * of input are required to establish the encoding. 97 : **********************************************************************/ 98 0 : GBool AVCE00DetectEncoding(AVCDBCSInfo *psDBCSInfo, const GByte *pszLine) 99 : { 100 0 : if (psDBCSInfo == nullptr || psDBCSInfo->nDBCSCodePage == 0 || 101 0 : psDBCSInfo->nDBCSEncoding != AVC_CODE_UNKNOWN) 102 : { 103 : /* Either single byte codepage, or encoding has already been detected 104 : */ 105 0 : return TRUE; 106 : } 107 : 108 0 : switch (psDBCSInfo->nDBCSCodePage) 109 : { 110 0 : case AVC_DBCS_JAPANESE: 111 0 : psDBCSInfo->nDBCSEncoding = _AVCDetectJapaneseEncoding(pszLine); 112 0 : break; 113 0 : default: 114 0 : psDBCSInfo->nDBCSEncoding = AVC_CODE_UNKNOWN; 115 0 : return TRUE; /* Codepage not supported... no need to scan more 116 : lines*/ 117 : } 118 : 119 0 : if (psDBCSInfo->nDBCSEncoding != AVC_CODE_UNKNOWN) 120 0 : return TRUE; /* We detected the encoding! */ 121 : 122 0 : return FALSE; 123 : } 124 : 125 : /********************************************************************** 126 : * AVCE00Convert2ArcDBCS() 127 : * 128 : * If encoding is still unknown, try to detect the encoding used in the 129 : * current file, and then convert the string to an encoding validfor output 130 : * to a coverage. 131 : * 132 : * Returns a reference to a const buffer that should not be freed by the 133 : * caller. It can be either the original string buffer or a ref. to an 134 : * internal buffer. 135 : **********************************************************************/ 136 0 : const GByte *AVCE00Convert2ArcDBCS(AVCDBCSInfo *psDBCSInfo, 137 : const GByte *pszLine, int nMaxOutputLen) 138 : { 139 0 : const GByte *pszOutBuf = nullptr; 140 0 : GByte *pszTmp = nullptr; 141 : GBool bAllAscii; 142 : 143 0 : if (psDBCSInfo == nullptr || psDBCSInfo->nDBCSCodePage == 0 || 144 : pszLine == nullptr) 145 : { 146 : /* Single byte codepage... nothing to do 147 : */ 148 0 : return pszLine; 149 : } 150 : 151 : /* If string is all ASCII then there is nothing to do... 152 : */ 153 0 : pszTmp = (GByte *)pszLine; 154 0 : for (bAllAscii = TRUE; bAllAscii && pszTmp && *pszTmp; pszTmp++) 155 : { 156 0 : if (!IS_ASCII(*pszTmp)) 157 0 : bAllAscii = FALSE; 158 : } 159 0 : if (bAllAscii) 160 0 : return pszLine; 161 : 162 : /* Make sure output buffer is large enough. 163 : * We add 2 chars to buffer size to simplify processing... no need to 164 : * check if second byte of a pair would overflow buffer. 165 : */ 166 0 : if (psDBCSInfo->pszDBCSBuf == nullptr || 167 0 : psDBCSInfo->nDBCSBufSize < nMaxOutputLen + 2) 168 : { 169 0 : psDBCSInfo->nDBCSBufSize = nMaxOutputLen + 2; 170 0 : psDBCSInfo->pszDBCSBuf = (GByte *)CPLRealloc( 171 0 : psDBCSInfo->pszDBCSBuf, psDBCSInfo->nDBCSBufSize * sizeof(GByte)); 172 : } 173 : 174 : /* Do the conversion according to current code page 175 : */ 176 0 : switch (psDBCSInfo->nDBCSCodePage) 177 : { 178 0 : case AVC_DBCS_JAPANESE: 179 : pszOutBuf = 180 0 : _AVCJapanese2ArcDBCS(psDBCSInfo, pszLine, nMaxOutputLen); 181 0 : break; 182 0 : default: 183 : /* We should never get here anyways, but just in case return pszLine 184 : */ 185 0 : CPLAssert(FALSE); /* Should never get here. */ 186 : pszOutBuf = pszLine; 187 : } 188 : 189 0 : return pszOutBuf; 190 : } 191 : 192 : /********************************************************************** 193 : * AVCE00ConvertFromArcDBCS() 194 : * 195 : * Convert DBCS encoding in binary coverage files to E00 encoding. 196 : * 197 : * Returns a reference to a const buffer that should not be freed by the 198 : * caller. It can be either the original string buffer or a ref. to an 199 : * internal buffer. 200 : **********************************************************************/ 201 286 : const GByte *AVCE00ConvertFromArcDBCS(AVCDBCSInfo *psDBCSInfo, 202 : const GByte *pszLine, int nMaxOutputLen) 203 : { 204 286 : const GByte *pszOutBuf = nullptr; 205 : GByte *pszTmp; 206 : GBool bAllAscii; 207 : 208 286 : if (psDBCSInfo == nullptr || psDBCSInfo->nDBCSCodePage == 0 || 209 : pszLine == nullptr) 210 : { 211 : /* Single byte codepage... nothing to do 212 : */ 213 286 : return pszLine; 214 : } 215 : 216 : /* If string is all ASCII then there is nothing to do... 217 : */ 218 0 : pszTmp = (GByte *)pszLine; 219 0 : for (bAllAscii = TRUE; bAllAscii && pszTmp && *pszTmp; pszTmp++) 220 : { 221 0 : if (!IS_ASCII(*pszTmp)) 222 0 : bAllAscii = FALSE; 223 : } 224 0 : if (bAllAscii) 225 0 : return pszLine; 226 : 227 : /* Make sure output buffer is large enough. 228 : * We add 2 chars to buffer size to simplify processing... no need to 229 : * check if second byte of a pair would overflow buffer. 230 : */ 231 0 : if (psDBCSInfo->pszDBCSBuf == nullptr || 232 0 : psDBCSInfo->nDBCSBufSize < nMaxOutputLen + 2) 233 : { 234 0 : psDBCSInfo->nDBCSBufSize = nMaxOutputLen + 2; 235 0 : psDBCSInfo->pszDBCSBuf = (GByte *)CPLRealloc( 236 0 : psDBCSInfo->pszDBCSBuf, psDBCSInfo->nDBCSBufSize * sizeof(GByte)); 237 : } 238 : 239 : /* Do the conversion according to current code page 240 : */ 241 0 : switch (psDBCSInfo->nDBCSCodePage) 242 : { 243 0 : case AVC_DBCS_JAPANESE: 244 0 : pszOutBuf = _AVCArcDBCS2JapaneseShiftJIS(psDBCSInfo, pszLine, 245 : nMaxOutputLen); 246 0 : break; 247 0 : default: 248 : /* We should never get here anyways, but just in case return pszLine 249 : */ 250 0 : pszOutBuf = pszLine; 251 : } 252 : 253 0 : return pszOutBuf; 254 : } 255 : 256 : /*===================================================================== 257 : *===================================================================== 258 : * Functions Specific to Japanese encoding (CodePage 932). 259 : * 260 : * For now we assume that we can receive only Katakana, Shift-JIS, or EUC 261 : * encoding as input. Coverages use EUC encoding in most cases, except 262 : * for Katakana characters that are prefixed with a 0x8e byte. 263 : * 264 : * Most of the Japanese conversion functions are based on information and 265 : * algorithms found at: 266 : * http://www.mars.dti.ne.jp/~torao/program/appendix/japanese-en.html 267 : *===================================================================== 268 : *====================================================================*/ 269 : 270 : /********************************************************************** 271 : * _AVCDetectJapaneseEncoding() 272 : * 273 : * Scan a line of text to try to establish the type of japanese encoding 274 : * 275 : * Returns the encoding number (AVC_CODE_JAP_*), or AVC_CODE_UNKNOWN if no 276 : * specific encoding was detected. 277 : **********************************************************************/ 278 : 279 : #define IS_JAP_SHIFTJIS_1(c) ((c) >= 0x81 && (c) <= 0x9f) 280 : #define IS_JAP_SHIFTJIS_2(c) \ 281 : (((c) >= 0x40 && (c) <= 0x7e) || ((c) >= 0x80 && (c) <= 0xA0)) 282 : #define IS_JAP_EUC_1(c) ((c) >= 0xF0 && (c) <= 0xFE) 283 : #define IS_JAP_EUC_2(c) ((c) >= 0xFD && (c) <= 0xFE) 284 : #define IS_JAP_KANA(c) ((c) >= 0xA1 && (c) <= 0xDF) 285 : 286 0 : static int _AVCDetectJapaneseEncoding(const GByte *pszLine) 287 : { 288 0 : int nEncoding = AVC_CODE_UNKNOWN; 289 : 290 0 : for (; nEncoding == AVC_CODE_UNKNOWN && pszLine && *pszLine; pszLine++) 291 : { 292 0 : if (IS_ASCII(*pszLine)) 293 0 : continue; 294 0 : else if (IS_JAP_SHIFTJIS_1(*pszLine)) 295 : { 296 0 : nEncoding = AVC_CODE_JAP_SHIFTJIS; 297 0 : break; 298 : } 299 0 : else if (IS_JAP_KANA(*pszLine) && *(pszLine + 1) && 300 0 : *(pszLine + 1) <= 0xA0) 301 : { 302 0 : nEncoding = AVC_CODE_JAP_SHIFTJIS; /* SHIFT-JIS + Kana */ 303 0 : break; 304 : } 305 0 : else if (IS_JAP_EUC_1(*pszLine)) 306 : { 307 0 : nEncoding = AVC_CODE_JAP_EUC; 308 0 : break; 309 : } 310 : 311 0 : if (*(++pszLine) == '\0') 312 0 : break; 313 : 314 0 : if (IS_JAP_SHIFTJIS_2(*pszLine)) 315 : { 316 0 : nEncoding = AVC_CODE_JAP_SHIFTJIS; 317 0 : break; 318 : } 319 0 : else if (IS_JAP_EUC_2(*pszLine)) 320 : { 321 0 : nEncoding = AVC_CODE_JAP_EUC; 322 0 : break; 323 : } 324 : } 325 : 326 0 : return nEncoding; 327 : } 328 : 329 : /********************************************************************** 330 : * _AVCJapanese2ArcDBCS() 331 : * 332 : * Try to detect type of Japanese encoding if not done yet, and convert 333 : * string from Japanese to proper coverage DBCS encoding. 334 : **********************************************************************/ 335 0 : static const GByte *_AVCJapanese2ArcDBCS(AVCDBCSInfo *psDBCSInfo, 336 : const GByte *pszLine, 337 : int nMaxOutputLen) 338 : { 339 : GByte *pszOut; 340 : int iDst; 341 : 342 0 : pszOut = psDBCSInfo->pszDBCSBuf; 343 : 344 0 : if (psDBCSInfo->nDBCSEncoding == AVC_CODE_UNKNOWN) 345 : { 346 : /* Type of encoding (Shift-JIS or EUC) not known yet... try to 347 : * detect it now. 348 : */ 349 0 : psDBCSInfo->nDBCSEncoding = _AVCDetectJapaneseEncoding(pszLine); 350 : 351 : #if 0 352 : if (psDBCSInfo->nDBCSEncoding == AVC_CODE_JAP_SHIFTJIS) 353 : { 354 : printf("Found Japanese Shift-JIS encoding\n");/*ok*/ 355 : } 356 : else if (psDBCSInfo->nDBCSEncoding == AVC_CODE_JAP_EUC) 357 : { 358 : printf("Found Japanese EUC encoding\n");/*ok*/ 359 : } 360 : #endif 361 : } 362 : 363 0 : for (iDst = 0; *pszLine && iDst < nMaxOutputLen; pszLine++) 364 : { 365 0 : if (IS_ASCII(*pszLine)) 366 : { 367 : /* No transformation required for ASCII */ 368 0 : pszOut[iDst++] = *pszLine; 369 : } 370 0 : else if (psDBCSInfo->nDBCSEncoding == AVC_CODE_JAP_EUC && 371 0 : *(pszLine + 1)) 372 : { 373 : /* This must be a pair of EUC chars and both should be in 374 : * the range 0xA1-0xFE 375 : */ 376 0 : pszOut[iDst++] = *(pszLine++); 377 0 : pszOut[iDst++] = *pszLine; 378 : } 379 0 : else if (IS_JAP_KANA(*pszLine)) 380 : { 381 : /* Katakana char. prefix it with 0x8e */ 382 0 : pszOut[iDst++] = 0x8e; 383 0 : pszOut[iDst++] = *pszLine; 384 : } 385 0 : else if (*(pszLine + 1)) 386 : { 387 : /* This must be a pair of Shift-JIS chars... convert them to EUC 388 : * 389 : * If we haven't been able to establish the encoding for sure 390 : * yet, then it is possible that a pair of EUC chars could be 391 : * treated as shift-JIS here... but there is not much we can do 392 : * about that unless we scan the whole E00 input before we 393 : * start the conversion. 394 : */ 395 : unsigned char leader, trailer; 396 0 : leader = *(pszLine++); 397 0 : trailer = *pszLine; 398 : 399 0 : if (leader <= 0x9F) 400 0 : leader -= 0x71; 401 : else 402 0 : leader -= 0xB1; 403 0 : leader = (leader << 1) + 1; 404 : 405 0 : if (trailer > 0x7F) 406 0 : trailer--; 407 0 : if (trailer >= 0x9E) 408 : { 409 0 : trailer -= 0x7D; 410 0 : leader++; 411 : } 412 : else 413 : { 414 0 : trailer -= 0x1F; 415 : } 416 : 417 0 : pszOut[iDst++] = leader | 0x80; 418 0 : pszOut[iDst++] = trailer | 0x80; 419 : } 420 : else 421 : { 422 : /* We should never get here unless a double-byte pair was 423 : * truncated... but just in case... 424 : */ 425 0 : pszOut[iDst++] = *pszLine; 426 : } 427 : } 428 : 429 0 : pszOut[iDst] = '\0'; 430 : 431 0 : return psDBCSInfo->pszDBCSBuf; 432 : } 433 : 434 : /********************************************************************** 435 : * _AVCArcDBCS2JapaneseShiftJIS() 436 : * 437 : * Convert string from coverage DBCS (EUC) to Japanese Shift-JIS. 438 : * 439 : * We know that binary coverages use a custom EUC encoding for japanese 440 : * which is EUC + all Katakana chars are prefixed with 0x8e. So this 441 : * function just does a simple conversion. 442 : **********************************************************************/ 443 0 : static const GByte *_AVCArcDBCS2JapaneseShiftJIS(AVCDBCSInfo *psDBCSInfo, 444 : const GByte *pszLine, 445 : int nMaxOutputLen) 446 : { 447 : GByte *pszOut; 448 : int iDst; 449 : 450 0 : pszOut = psDBCSInfo->pszDBCSBuf; 451 : 452 0 : for (iDst = 0; *pszLine && iDst < nMaxOutputLen; pszLine++) 453 : { 454 0 : if (IS_ASCII(*pszLine)) 455 : { 456 : /* No transformation required for ASCII */ 457 0 : pszOut[iDst++] = *pszLine; 458 : } 459 0 : else if (*pszLine == 0x8e && *(pszLine + 1)) 460 : { 461 0 : pszLine++; /* Flush the 0x8e */ 462 0 : pszOut[iDst++] = *pszLine; 463 : } 464 0 : else if (*(pszLine + 1)) 465 : { 466 : /* This is a pair of EUC chars... convert them to Shift-JIS 467 : */ 468 : unsigned char leader, trailer; 469 0 : leader = *(pszLine++) & 0x7F; 470 0 : trailer = *pszLine & 0x7F; 471 : 472 0 : if ((leader & 0x01) != 0) 473 0 : trailer += 0x1F; 474 : else 475 0 : trailer += 0x7D; 476 0 : if (trailer >= 0x7F) 477 0 : trailer++; 478 : 479 0 : leader = ((leader - 0x21) >> 1) + 0x81; 480 0 : if (leader > 0x9F) 481 0 : leader += 0x40; 482 : 483 0 : pszOut[iDst++] = leader; 484 0 : pszOut[iDst++] = trailer; 485 : } 486 : else 487 : { 488 : /* We should never get here unless a double-byte pair was 489 : * truncated... but just in case... 490 : */ 491 0 : pszOut[iDst++] = *pszLine; 492 : } 493 : } 494 : 495 0 : pszOut[iDst] = '\0'; 496 : 497 0 : return psDBCSInfo->pszDBCSBuf; 498 : }