LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/avc - avc_mbyte.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 18 153 11.8 %
Date: 2024-11-21 22:18:42 Functions: 4 9 44.4 %

          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             : }

Generated by: LCOV version 1.14