1 : /* $Id: avc_mbyte.c,v 1.4 2008/07/23 20:51:38 dmorissette Exp $
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 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : **********************************************************************
30 : *
31 : * $Log: avc_mbyte.c,v $
32 : * Revision 1.4 2008/07/23 20:51:38 dmorissette
33 : * Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char
34 : * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495)
35 : *
36 : * Revision 1.3 2005/06/03 03:49:59 daniel
37 : * Update email address, website url, and copyright dates
38 : *
39 : * Revision 1.2 2000/09/22 19:45:21 daniel
40 : * Switch to MIT-style license
41 : *
42 : * Revision 1.1 2000/05/29 15:31:03 daniel
43 : * Initial revision - Japanese support
44 : *
45 : **********************************************************************/
46 :
47 : #include "avc.h"
48 :
49 : #ifdef _WIN32
50 : # include <mbctype.h>
51 : #endif
52 :
53 : static int _AVCDetectJapaneseEncoding(const GByte *pszLine);
54 : static const GByte *_AVCJapanese2ArcDBCS(AVCDBCSInfo *psDBCSInfo,
55 : const GByte *pszLine,
56 : int nMaxOutputLen);
57 : static const GByte *_AVCArcDBCS2JapaneseShiftJIS(AVCDBCSInfo *psDBCSInfo,
58 : const GByte *pszLine,
59 : int nMaxOutputLen);
60 :
61 : /*=====================================================================
62 : * Functions to handle multibyte char conversions
63 : *====================================================================*/
64 :
65 : #define IS_ASCII(c) ((c) < 0x80)
66 :
67 :
68 : /**********************************************************************
69 : * AVCAllocDBCSInfo()
70 : *
71 : * Alloc and init a new AVCDBCSInfo structure.
72 : **********************************************************************/
73 : AVCDBCSInfo *AVCAllocDBCSInfo()
74 1 : {
75 : AVCDBCSInfo *psInfo;
76 :
77 1 : psInfo = (AVCDBCSInfo*)CPLCalloc(1, sizeof(AVCDBCSInfo));
78 :
79 1 : psInfo->nDBCSCodePage = AVCGetDBCSCodePage();
80 1 : psInfo->nDBCSEncoding = AVC_CODE_UNKNOWN;
81 1 : psInfo->pszDBCSBuf = NULL;
82 1 : psInfo->nDBCSBufSize = 0;
83 :
84 1 : return psInfo;
85 : }
86 :
87 : /**********************************************************************
88 : * AVCFreeDBCSInfo()
89 : *
90 : * Release all memory associated with a AVCDBCSInfo structure.
91 : **********************************************************************/
92 : void AVCFreeDBCSInfo(AVCDBCSInfo *psInfo)
93 1 : {
94 1 : if (psInfo)
95 : {
96 1 : CPLFree(psInfo->pszDBCSBuf);
97 1 : CPLFree(psInfo);
98 : }
99 1 : }
100 :
101 : /**********************************************************************
102 : * AVCGetDBCSCodePage()
103 : *
104 : * Fetch current multibyte codepage on the system.
105 : * Returns a valid codepage number, or 0 if the codepage is single byte or
106 : * unsupported.
107 : **********************************************************************/
108 : int AVCGetDBCSCodePage()
109 1 : {
110 : #ifdef _WIN32
111 : int nCP;
112 : nCP = _getmbcp();
113 :
114 : /* Check if that's a supported codepage */
115 : if (nCP == AVC_DBCS_JAPANESE)
116 : return nCP;
117 : #endif
118 :
119 1 : return 0;
120 : }
121 :
122 :
123 : /**********************************************************************
124 : * AVCE00DetectEncoding()
125 : *
126 : * Try to detect the encoding used in the current file by examining lines
127 : * of input.
128 : *
129 : * Returns TRUE once the encoding is established, or FALSE if more lines
130 : * of input are required to establish the encoding.
131 : **********************************************************************/
132 : GBool AVCE00DetectEncoding(AVCDBCSInfo *psDBCSInfo, const GByte *pszLine)
133 0 : {
134 0 : if (psDBCSInfo == NULL || psDBCSInfo->nDBCSCodePage == 0 ||
135 : psDBCSInfo->nDBCSEncoding != AVC_CODE_UNKNOWN)
136 : {
137 : /* Either single byte codepage, or encoding has already been detected
138 : */
139 0 : return TRUE;
140 : }
141 :
142 0 : switch (psDBCSInfo->nDBCSCodePage)
143 : {
144 : case AVC_DBCS_JAPANESE:
145 0 : psDBCSInfo->nDBCSEncoding =
146 : _AVCDetectJapaneseEncoding(pszLine);
147 : break;
148 : default:
149 0 : psDBCSInfo->nDBCSEncoding = AVC_CODE_UNKNOWN;
150 0 : return TRUE; /* Codepage not supported... no need to scan more lines*/
151 : }
152 :
153 0 : if (psDBCSInfo->nDBCSEncoding != AVC_CODE_UNKNOWN)
154 0 : return TRUE; /* We detected the encoding! */
155 :
156 0 : return FALSE;
157 : }
158 :
159 :
160 : /**********************************************************************
161 : * AVCE00Convert2ArcDBCS()
162 : *
163 : * If encoding is still unknown, try to detect the encoding used in the
164 : * current file, and then convert the string to an encoding validfor output
165 : * to a coverage.
166 : *
167 : * Returns a reference to a const buffer that should not be freed by the
168 : * caller. It can be either the original string buffer or a ref. to an
169 : * internal buffer.
170 : **********************************************************************/
171 : const GByte *AVCE00Convert2ArcDBCS(AVCDBCSInfo *psDBCSInfo,
172 : const GByte *pszLine,
173 : int nMaxOutputLen)
174 0 : {
175 0 : const GByte *pszOutBuf = NULL;
176 0 : GByte *pszTmp = NULL;
177 : GBool bAllAscii;
178 :
179 0 : if (psDBCSInfo == NULL ||
180 : psDBCSInfo->nDBCSCodePage == 0 || pszLine == NULL)
181 : {
182 : /* Single byte codepage... nothing to do
183 : */
184 0 : return pszLine;
185 : }
186 :
187 : /* If string is all ASCII then there is nothing to do...
188 : */
189 0 : pszTmp = (GByte *)pszLine;
190 0 : for(bAllAscii = TRUE ; bAllAscii && pszTmp && *pszTmp; pszTmp++)
191 : {
192 0 : if ( !IS_ASCII(*pszTmp) )
193 0 : bAllAscii = FALSE;
194 : }
195 0 : if (bAllAscii)
196 0 : return pszLine;
197 :
198 : /* Make sure output buffer is large enough.
199 : * We add 2 chars to buffer size to simplify processing... no need to
200 : * check if second byte of a pair would overflow buffer.
201 : */
202 0 : if (psDBCSInfo->pszDBCSBuf == NULL ||
203 : psDBCSInfo->nDBCSBufSize < nMaxOutputLen+2)
204 : {
205 0 : psDBCSInfo->nDBCSBufSize = nMaxOutputLen+2;
206 0 : psDBCSInfo->pszDBCSBuf =
207 : (GByte *)CPLRealloc(psDBCSInfo->pszDBCSBuf,
208 : psDBCSInfo->nDBCSBufSize*
209 : sizeof(GByte));
210 : }
211 :
212 : /* Do the conversion according to current code page
213 : */
214 0 : switch (psDBCSInfo->nDBCSCodePage)
215 : {
216 : case AVC_DBCS_JAPANESE:
217 0 : pszOutBuf = _AVCJapanese2ArcDBCS(psDBCSInfo,
218 : pszLine,
219 : nMaxOutputLen);
220 0 : break;
221 : default:
222 : /* We should never get here anyways, but just in case return pszLine
223 : */
224 0 : CPLAssert( !"SHOULD NEVER GET HERE" );
225 0 : pszOutBuf = pszLine;
226 : }
227 :
228 0 : return pszOutBuf;
229 : }
230 :
231 : /**********************************************************************
232 : * AVCE00ConvertFromArcDBCS()
233 : *
234 : * Convert DBCS encoding in binary coverage files to E00 encoding.
235 : *
236 : * Returns a reference to a const buffer that should not be freed by the
237 : * caller. It can be either the original string buffer or a ref. to an
238 : * internal buffer.
239 : **********************************************************************/
240 : const GByte *AVCE00ConvertFromArcDBCS(AVCDBCSInfo *psDBCSInfo,
241 : const GByte *pszLine,
242 : int nMaxOutputLen)
243 22 : {
244 22 : const GByte *pszOutBuf = NULL;
245 : GByte *pszTmp;
246 : GBool bAllAscii;
247 :
248 22 : if (psDBCSInfo == NULL ||
249 : psDBCSInfo->nDBCSCodePage == 0 || pszLine == NULL)
250 : {
251 : /* Single byte codepage... nothing to do
252 : */
253 22 : return pszLine;
254 : }
255 :
256 : /* If string is all ASCII then there is nothing to do...
257 : */
258 0 : pszTmp = (GByte *)pszLine;
259 0 : for(bAllAscii = TRUE ; bAllAscii && pszTmp && *pszTmp; pszTmp++)
260 : {
261 0 : if ( !IS_ASCII(*pszTmp) )
262 0 : bAllAscii = FALSE;
263 : }
264 0 : if (bAllAscii)
265 0 : return pszLine;
266 :
267 : /* Make sure output buffer is large enough.
268 : * We add 2 chars to buffer size to simplify processing... no need to
269 : * check if second byte of a pair would overflow buffer.
270 : */
271 0 : if (psDBCSInfo->pszDBCSBuf == NULL ||
272 : psDBCSInfo->nDBCSBufSize < nMaxOutputLen+2)
273 : {
274 0 : psDBCSInfo->nDBCSBufSize = nMaxOutputLen+2;
275 0 : psDBCSInfo->pszDBCSBuf =
276 : (GByte *)CPLRealloc(psDBCSInfo->pszDBCSBuf,
277 : psDBCSInfo->nDBCSBufSize*
278 : sizeof(GByte));
279 : }
280 :
281 : /* Do the conversion according to current code page
282 : */
283 0 : switch (psDBCSInfo->nDBCSCodePage)
284 : {
285 : case AVC_DBCS_JAPANESE:
286 0 : pszOutBuf = _AVCArcDBCS2JapaneseShiftJIS(psDBCSInfo,
287 : pszLine,
288 : nMaxOutputLen);
289 0 : break;
290 : default:
291 : /* We should never get here anyways, but just in case return pszLine
292 : */
293 0 : pszOutBuf = pszLine;
294 : }
295 :
296 0 : return pszOutBuf;
297 : }
298 :
299 : /*=====================================================================
300 : *=====================================================================
301 : * Functions Specific to Japanese encoding (CodePage 932).
302 : *
303 : * For now we assume that we can receive only Katakana, Shift-JIS, or EUC
304 : * encoding as input. Coverages use EUC encoding in most cases, except
305 : * for Katakana characters that are prefixed with a 0x8e byte.
306 : *
307 : * Most of the Japanese conversion functions are based on information and
308 : * algorithms found at:
309 : * http://www.mars.dti.ne.jp/~torao/program/appendix/japanese-en.html
310 : *=====================================================================
311 : *====================================================================*/
312 :
313 : /**********************************************************************
314 : * _AVCDetectJapaneseEncoding()
315 : *
316 : * Scan a line of text to try to establish the type of japanese encoding
317 : *
318 : * Returns the encoding number (AVC_CODE_JAP_*), or AVC_CODE_UNKNOWN if no
319 : * specific encoding was detected.
320 : **********************************************************************/
321 :
322 : #define IS_JAP_SHIFTJIS_1(c) ((c) >= 0x81 && (c) <= 0x9f)
323 : #define IS_JAP_SHIFTJIS_2(c) (((c) >= 0x40 && (c) <= 0x7e) || \
324 : ((c) >= 0x80 && (c) <= 0xA0) )
325 : #define IS_JAP_EUC_1(c) ((c) >= 0xF0 && (c) <= 0xFE)
326 : #define IS_JAP_EUC_2(c) ((c) >= 0xFD && (c) <= 0xFE)
327 : #define IS_JAP_KANA(c) ((c) >= 0xA1 && (c) <= 0xDF)
328 :
329 : static int _AVCDetectJapaneseEncoding(const GByte *pszLine)
330 0 : {
331 0 : int nEncoding = AVC_CODE_UNKNOWN;
332 :
333 0 : for( ; nEncoding == AVC_CODE_UNKNOWN && pszLine && *pszLine; pszLine++)
334 : {
335 0 : if (IS_ASCII(*pszLine))
336 0 : continue;
337 0 : else if (IS_JAP_SHIFTJIS_1(*pszLine))
338 : {
339 0 : nEncoding = AVC_CODE_JAP_SHIFTJIS;
340 0 : break;
341 : }
342 0 : else if (IS_JAP_KANA(*pszLine) && *(pszLine+1) &&
343 : (IS_ASCII(*(pszLine+1)) ||
344 : (*(pszLine+1)>=0x80 && *(pszLine+1)<=0xA0) ) )
345 : {
346 0 : nEncoding = AVC_CODE_JAP_SHIFTJIS; /* SHIFT-JIS + Kana */
347 0 : break;
348 : }
349 0 : else if (IS_JAP_EUC_1(*pszLine))
350 : {
351 0 : nEncoding = AVC_CODE_JAP_EUC;
352 0 : break;
353 : }
354 :
355 0 : if (*(++pszLine) == '\0')
356 0 : break;
357 :
358 0 : if (IS_JAP_SHIFTJIS_2(*pszLine))
359 : {
360 0 : nEncoding = AVC_CODE_JAP_SHIFTJIS;
361 0 : break;
362 : }
363 0 : else if (IS_JAP_EUC_2(*pszLine))
364 : {
365 0 : nEncoding = AVC_CODE_JAP_EUC;
366 0 : break;
367 : }
368 : }
369 :
370 0 : return nEncoding;
371 : }
372 :
373 :
374 : /**********************************************************************
375 : * _AVCJapanese2ArcDBCS()
376 : *
377 : * Try to detect type of Japanese encoding if not done yet, and convert
378 : * string from Japanese to proper coverage DBCS encoding.
379 : **********************************************************************/
380 : static const GByte *_AVCJapanese2ArcDBCS(AVCDBCSInfo *psDBCSInfo,
381 : const GByte *pszLine,
382 : int nMaxOutputLen)
383 0 : {
384 : GByte *pszOut;
385 : int iDst;
386 :
387 0 : pszOut = psDBCSInfo->pszDBCSBuf;
388 :
389 0 : if (psDBCSInfo->nDBCSEncoding == AVC_CODE_UNKNOWN)
390 : {
391 : /* Type of encoding (Shift-JIS or EUC) not known yet... try to
392 : * detect it now.
393 : */
394 0 : psDBCSInfo->nDBCSEncoding = _AVCDetectJapaneseEncoding(pszLine);
395 :
396 : /*
397 : if (psDBCSInfo->nDBCSEncoding == AVC_CODE_JAP_SHIFTJIS)
398 : {
399 : printf("Found Japanese Shift-JIS encoding\n");
400 : }
401 : else if (psDBCSInfo->nDBCSEncoding == AVC_CODE_JAP_EUC)
402 : {
403 : printf("Found Japanese EUC encoding\n");
404 : }
405 : */
406 : }
407 :
408 0 : for(iDst=0; *pszLine && iDst < nMaxOutputLen; pszLine++)
409 : {
410 0 : if (IS_ASCII(*pszLine))
411 : {
412 : /* No transformation required for ASCII */
413 0 : pszOut[iDst++] = *pszLine;
414 : }
415 0 : else if ( psDBCSInfo->nDBCSEncoding==AVC_CODE_JAP_EUC && *(pszLine+1) )
416 : {
417 : /* This must be a pair of EUC chars and both should be in
418 : * the range 0xA1-0xFE
419 : */
420 0 : pszOut[iDst++] = *(pszLine++);
421 0 : pszOut[iDst++] = *pszLine;
422 : }
423 0 : else if ( IS_JAP_KANA(*pszLine) )
424 : {
425 : /* Katakana char. prefix it with 0x8e */
426 0 : pszOut[iDst++] = 0x8e;
427 0 : pszOut[iDst++] = *pszLine;
428 : }
429 0 : else if ( *(pszLine+1) )
430 : {
431 : /* This must be a pair of Shift-JIS chars... convert them to EUC
432 : *
433 : * If we haven't been able to establish the encoding for sure
434 : * yet, then it is possible that a pair of EUC chars could be
435 : * treated as shift-JIS here... but there is not much we can do
436 : * about that unless we scan the whole E00 input before we
437 : * start the conversion.
438 : */
439 : unsigned char leader, trailer;
440 0 : leader = *(pszLine++);
441 0 : trailer = *pszLine;
442 :
443 0 : if(leader <= 0x9F) leader -= 0x71;
444 0 : else leader -= 0xB1;
445 0 : leader = (leader << 1) + 1;
446 :
447 0 : if(trailer > 0x7F) trailer --;
448 0 : if(trailer >= 0x9E)
449 : {
450 0 : trailer -= 0x7D;
451 0 : leader ++;
452 : }
453 : else
454 : {
455 0 : trailer -= 0x1F;
456 : }
457 :
458 0 : pszOut[iDst++] = leader | 0x80;
459 0 : pszOut[iDst++] = trailer | 0x80;
460 : }
461 : else
462 : {
463 : /* We should never get here unless a double-byte pair was
464 : * truncated... but just in case...
465 : */
466 0 : pszOut[iDst++] = *pszLine;
467 : }
468 :
469 : }
470 :
471 0 : pszOut[iDst] = '\0';
472 :
473 0 : return psDBCSInfo->pszDBCSBuf;
474 : }
475 :
476 :
477 : /**********************************************************************
478 : * _AVCArcDBCS2JapaneseShiftJIS()
479 : *
480 : * Convert string from coverage DBCS (EUC) to Japanese Shift-JIS.
481 : *
482 : * We know that binary coverages use a custom EUC encoding for japanese
483 : * which is EUC + all Katakana chars are prefixed with 0x8e. So this
484 : * function just does a simple conversion.
485 : **********************************************************************/
486 : static const GByte *_AVCArcDBCS2JapaneseShiftJIS(AVCDBCSInfo *psDBCSInfo,
487 : const GByte *pszLine,
488 : int nMaxOutputLen)
489 0 : {
490 : GByte *pszOut;
491 : int iDst;
492 :
493 0 : pszOut = psDBCSInfo->pszDBCSBuf;
494 :
495 0 : for(iDst=0; *pszLine && iDst < nMaxOutputLen; pszLine++)
496 : {
497 0 : if (IS_ASCII(*pszLine))
498 : {
499 : /* No transformation required for ASCII */
500 0 : pszOut[iDst++] = *pszLine;
501 : }
502 0 : else if (*pszLine == 0x8e && *(pszLine+1))
503 : {
504 0 : pszLine++; /* Flush the 0x8e */
505 0 : pszOut[iDst++] = *pszLine;
506 : }
507 0 : else if (*(pszLine+1))
508 : {
509 : /* This is a pair of EUC chars... convert them to Shift-JIS
510 : */
511 : unsigned char leader, trailer;
512 0 : leader = *(pszLine++) & 0x7F;
513 0 : trailer = *pszLine & 0x7F;
514 :
515 0 : if((leader & 0x01) != 0) trailer += 0x1F;
516 0 : else trailer += 0x7D;
517 0 : if(trailer >= 0x7F) trailer ++;
518 :
519 0 : leader = ((leader - 0x21) >> 1) + 0x81;
520 0 : if(leader > 0x9F) leader += 0x40;
521 :
522 0 : pszOut[iDst++] = leader;
523 0 : pszOut[iDst++] = trailer;
524 : }
525 : else
526 : {
527 : /* We should never get here unless a double-byte pair was
528 : * truncated... but just in case...
529 : */
530 0 : pszOut[iDst++] = *pszLine;
531 : }
532 :
533 : }
534 :
535 0 : pszOut[iDst] = '\0';
536 :
537 0 : return psDBCSInfo->pszDBCSBuf;
538 : }
539 :
|