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: 2025-01-18 12:42:00 Functions: 4 9 44.4 %

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

Generated by: LCOV version 1.14