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