/*************************************************************************
*                                                                        *
*  CHARTAB.C                                                             *
*                                                                        *
*  Copyright (C) Microsoft Corporation 1990-1994                         *
*  All Rights reserved.                                                  *
*                                                                        *
**************************************************************************
*                                                                        *
*  Module Intent                                                         *
*    Character table indexing and retrieval. The reasons this module is  *
*    not put together with ansiusa are:                                  *
*       - Like stop words, this involves indexing and retrieval          *
*       - It is word breaker independent                                 *
*                                                                        *
**************************************************************************
*                                                                        *
*  Current Owner: Binh Nguyen                                            *
*                                                                        *
*************************************************************************/
#include <mvopsys.h>
#include <mem.h>
#include <memory.h>
#include <iterror.h>
#include <_mvutil.h>
#include <mvsearch.h>
#include "common.h"

#ifdef _DEBUG
static BYTE NEAR *s_aszModule = __FILE__;      // Used by error return functions.
#endif

#define         SLASH            '/'
#define         RETURN           '\r'
#define         NEWLINE          '\n'
#define         INBUF_SIZE       256     // Maximum buffer size to store a line
#define         BYTE_MAX         256     // Maximum number of characters
#define         LIGATURE_BYTES   3       // Number of bytes/ligature

/* External variables */

extern BYTE LigatureTable[];
extern CHARMAP DefaultCMap[];

/*************************************************************************
 *
 *                           API FUNCTIONS
 *      Those functions should be exported in a .DEF file
 *************************************************************************/
PUBLIC LPV EXPORT_API FAR PASCAL MVCharTableLoad (HFPB, LSZ, PHRESULT);
PUBLIC LPV EXPORT_API FAR PASCAL MVCharTableGetDefault (PHRESULT);
PUBLIC VOID EXPORT_API FAR PASCAL MVCharTableDispose (LPVOID);
PUBLIC HRESULT EXPORT_API PASCAL FAR MVCharTableFileBuild (HFPB, LPVOID, LSZ);
PUBLIC LPV EXPORT_API FAR PASCAL MVCharTableIndexLoad(HFPB, LSZ, PHRESULT);


/*************************************************************************
 *
 *                        INTERNAL PRIVATE FUNCTIONS
 *      All of them should be declared near
 *************************************************************************/
PRIVATE LPB NEAR PASCAL GetNumber (HFPB, LPB, LPB, int FAR *, WORD FAR *);
PRIVATE LPCHARTAB PASCAL NEAR CharTableCreate (int);
PRIVATE VOID PASCAL NEAR StripCRLF (LPB, WORD);
PRIVATE VOID PASCAL NEAR GetWord (LSZ FAR *, LST);

PUBLIC VOID PASCAL FAR FreeHandle2 (HANDLE hd)
{
    if (hd) {
        _GLOBALUNLOCK(hd);
        _GLOBALFREE(hd);
    }
}

/*************************************************************************
 *      @doc    EXTERNAL API INDEX
 *
 *      @func   LPCHARTAB FAR PASCAL | MVCharTableLoad |
 *              Open an ASCII file and read in the description of the character
 *              tables. It then converts them into binary format ready to be used
 *              The format of the file is:
 *                      Size    // How many entries
 *                      char class, normalized value, sorted value, mac display value
 *                          ........
 *                      char class, normalized value, sorted value, mac display value
 *              Comments are preceded by // and ignored until EOL
 *    A description of the ligature table, if any, is to followed the
 *    charmap table
 *
 *		@parm	HFPB | hfpbIn |
 *				Handle to system file or subfile; NULL if file is external.
 *
 *      @parm   LSZ | lszFilename |
 *              DOS filen containing the description of the tables
 *
 *      @parm   PHRESULT | phr |
 *              Error buffer
 *
 *      @rdesc
 *              The function returns a pointer to the memory block containing
 *              tables, or NULL if failed. The error buffer will contain
 *              description for the cause of the failure
 *
 *      @comm   About ligature table, there are some assumptions:
 *              If there is no wcLigature, then the default table is used
 *              if (wcLigature == 0) no ligature should be used
 *              if wcLigature != 0, the author provides a ligature table.
 *************************************************************************/
PUBLIC LPVOID EXPORT_API FAR PASCAL MVCharTableLoad (HFPB hfpbIn,
	LSZ lszFilename, PHRESULT phr)
{
    register WORD j;                // Scratch index variable
    register WORD i;                // Scratch index variable
    register LPB lpbBuf;            // Pointer to input buffer
    WORD cbRead;                    // How many bytes have read (left)
    int wcTabEntries;               // Number of table entries
    int wTmp;                       // Scratch variable
    HFPB hfpb;						// Handle of char table file
	BOOL fOpenedFile;				// TRUE if we have to close the file
    BYTE Buffer [INBUF_SIZE];       // Input buffer
    LPCMAP lpCMap;                  // Pointer to character table entries
    LPCHARTAB lpCharTab = NULL;     // Pointer to general char table struct
    int wcLigature;
    LPB   lpbChar;                  // Scratch pointer

	/* Open subfile if necessary, (and system file if necessary) */
	if ((fOpenedFile = FsTypeFromHfpb(hfpb = hfpbIn) != FS_SUBFILE) &&
		(hfpb = (HANDLE)FileOpen
			(hfpbIn, lszFilename, hfpbIn ? FS_SUBFILE : REGULAR_FILE,
			READ, phr)) == 0)
	{
        SetErrCode (phr, E_FILENOTFOUND);
        return NULL;
    }

    *phr = E_BADFORMAT;

    /* Fill in the buffer */
    if ((cbRead =
			(WORD) FileRead(hfpb, lpbBuf = Buffer, INBUF_SIZE, phr)) == 0 ||
		FAILED(*phr))
	{
        goto ErrorExit;
    }

    /* Get the table size */
    lpbBuf = GetNumber (hfpb, Buffer, lpbBuf, &wcTabEntries, &cbRead);
    if (wcTabEntries == 0 || wcTabEntries > BYTE_MAX) {
        goto ErrorExit;
    }

    /* Allocate memory for the character table */
    if ((lpCharTab = CharTableCreate (wcTabEntries)) == NULL)
	{
        SetErrCode (phr, E_OUTOFMEMORY);
		if (fOpenedFile)
			FileClose (hfpb);
        return NULL;
    }
    lpCMap = (LPCMAP)lpCharTab->lpCMapTab;
    lpCharTab->wcTabEntries = (WORD) wcTabEntries;

    /* Now read in invidual char table entry */
    for (i = (WORD) wcTabEntries; i > 0; i--, lpCMap++) {
        if ((lpbBuf = GetNumber (hfpb, Buffer, lpbBuf, &wTmp,
           &cbRead)) == NULL) {
          /* Missing item */
            goto ErrorExit;
       }
        lpCMap->Class = (WORD) wTmp;
      
        if ((lpbBuf = GetNumber (hfpb, Buffer, lpbBuf, &wTmp,
           &cbRead)) == NULL) {
          /* Missing item */
            goto ErrorExit;
       }
        lpCMap->SortOrder = (WORD) wTmp;
      
        if ((lpbBuf = GetNumber (hfpb, Buffer, lpbBuf, &wTmp,
           &cbRead)) == NULL) {
          /* Missing item */
            goto ErrorExit;
       }
        lpCMap->Norm = (BYTE)wTmp;
      
        if ((lpbBuf = GetNumber (hfpb, Buffer, lpbBuf, &wTmp,
           &cbRead)) == NULL) {
          /* Missing item */
            goto ErrorExit;
       }
        lpCMap->WinCaseSensitiveNorm = (BYTE)wTmp;
      
        if ((lpbBuf = GetNumber (hfpb, Buffer, lpbBuf, &wTmp,
           &cbRead)) == NULL) {
          /* Missing item */
            goto ErrorExit;
       }
        lpCMap->MacDisplay = (BYTE)wTmp;
        
        if ((lpbBuf = GetNumber (hfpb, Buffer, lpbBuf, &wTmp,
           &cbRead)) == NULL) {
          /* Missing item */
            goto ErrorExit;
       }
       lpCMap->MacCaseSensitiveNorm = (BYTE)wTmp;
    }

    /*
     * Check for valid table, ie. all reserved characters should not
     * be modified
     */

    /* Check for valid reserved type */

    lpCMap = &(((LPCMAP)lpCharTab->lpCMapTab)[1]);
    if (lpCMap->Class != CLASS_TYPE)
        goto ErrorExit;
    
    /* Check for ligatures */
    if ((lpbBuf = GetNumber (hfpb, Buffer, lpbBuf,
        &wcLigature, &cbRead)) == NULL) {

        /* No ligature table present, use default */

        lpCharTab->wcLigature = DEF_LIGATURE_COUNT;     
        lpCharTab->fFlag = USE_DEF_LIGATURE;
        lpCharTab->lpLigature = LigatureTable;
    }
    else {
        if ((lpCharTab->wcLigature = (WORD) wcLigature) != 0) {

            /* Ligature table present */

            /* Allocate memory block. Notice that we allocate 3 extra bytes
             * They will serve as sentinels for the end of the ligature
             * table, thus eliminating the need of having to know beforehand
             * what is the size of the table
             */
            if ((lpCharTab->hLigature = _GLOBALALLOC (DLLGMEM_ZEROINIT,
                LIGATURE_BYTES * (wcLigature + 1))) == 0) {
                SetErrCode (phr, E_OUTOFMEMORY);
                goto ErrorExit;
            }
            lpbChar = lpCharTab->lpLigature =
                (LPB)_GLOBALLOCK(lpCharTab->hLigature);

            /* Read in the entries */

            for (i = (WORD) wcLigature; i > 0; i--) {
                for (j = LIGATURE_BYTES; j > 0; j--) {
                    if (lpbBuf = GetNumber (hfpb, Buffer, lpbBuf,
                        &wTmp, &cbRead)) {
                        /* Update entry */
                        *lpbChar ++ = (BYTE)wTmp;
                    }
                    else {
                        /* Missing item */
                        goto ErrorExit;
                    }
                }
            }
            lpCharTab->fFlag = LIGATURE_PROVIDED;
        }
        else
            lpCharTab->fFlag = NO_LIGATURE;
    }
   
    if (fOpenedFile)
		FileClose (hfpb);
    return ((LPV)lpCharTab);

ErrorExit:
    if (fOpenedFile)
		FileClose (hfpb);
    MVCharTableDispose (lpCharTab);
    return NULL;
}

/*************************************************************************
 *      @doc    API INDEX
 *
 *      @func   HRESULT PASCAL FAR | MVCharTableFileBuild |
 *              Incorporate the character table into the system file
 *
 *      @parm   HFPB | hfpbSysFile |
 *              Handle to system file. It is non-zero, then the system file is
 *              already open, else the function will open the system file
 *
 *      @parm   LPCHARTAB | lpCharTab |
 *              Pointer to character table information structure
 *
 *      @parm   LSZ | lszFilename |
 *              File name. If hpfbSysFile is 0, the format is:
 *              dos filename[!charfilename], else it is the name of the character
 *              file itself
 *
 *      @rdesc ERR_SUCCESS if succeeded, other errors if failed. 
 *
 *      @comm   About ligature table, there are some assumptions:
 *              If hLigature == 0 {
 *                      if (wcLigature == 0)
 *                              There is no ligature table
 *                      else
 *                              We use the default ligature table. There is no need
 *                              to write out the table data
 *              }
 *              else
 *                      The author provides a ligature table.
 *************************************************************************/

PUBLIC HRESULT EXPORT_API PASCAL FAR MVCharTableFileBuild (HFPB hfpbSysFile,
    LPCHARTAB lpCharTab, LSZ lszFilename)
{
    HFPB hfpbCharTab;                       // Pointer to final index file info.
	BOOL fCreatedFile;
    CHARTAB_HDR CharTab_hdr;        // Character table header
    BYTE Dummy[CHARTAB_HDR_SIZE]; // Dummy buffer to write 0
    FILEOFFSET foOffset;                         // File's offset
	FILEOFFSET foStart;
    WORD CharTabSize;
    WORD CharLigatureSize;
//    char szFullFilename[MAX_PATH];
    HRESULT hr = S_OK;


    if (lpCharTab == NULL || lpCharTab->wcTabEntries == 0)
    {
        /* Nothing to build */
        return E_INVALIDARG;
    }

    /* If hfpbSysFile != 0, allocate a temp HFPB to use for the system file */
    //if (!(fCloseSysFile = (char)(hfpbSysFile == 0))) {
    //  if ((hfpb = ALLOCTEMPFPB (hfpbSysFile, phr)) == NULL) 
    //      return ERR_FAILED;
    //}

    if ((fCreatedFile =
			FsTypeFromHfpb(hfpbCharTab = hfpbSysFile) != FS_SUBFILE) &&
		0 == (hfpbCharTab = FileCreate
				(hfpbSysFile, lszFilename,
						hfpbSysFile ? FS_SUBFILE : REGULAR_FILE, &hr)))
    {
        return hr;
    }

	// If we didn't open the file, we need to find out where the file seek
	// pointer is initially so that we only seek relative to that starting
	// position (i.e. the caller owns the part of the file that comes before).
	foStart = (fCreatedFile ? MakeFo(0,0) :
				FileSeek (hfpbCharTab, MakeFo (0, 0), wFSSeekCur, &hr));

	if (FAILED(hr))
		goto exit01;

    /* Write out the CharTab file header */
    CharTab_hdr.FileStamp = CHRTAB_STAMP;
    CharTab_hdr.version = CHARTABVER;

    CharTabSize = lpCharTab->wcTabEntries * sizeof(CHARMAP);
    /* the ligature table size:
     *   - is 0 if we are going to use the default table
     *   - Is non-0 if the author provides a ligature table
     */
    switch (lpCharTab->fFlag)
    {
        case NO_LIGATURE:
        case USE_DEF_LIGATURE:
            CharLigatureSize = 0;
            break;
        default:
            CharLigatureSize = lpCharTab->wcLigature * LIGATURE_BYTES;
    }

    CharTab_hdr.dwTabSize = CharTabSize + CharLigatureSize;
    CharTab_hdr.wcTabEntries = lpCharTab->wcTabEntries;
    CharTab_hdr.wcLigature = lpCharTab->wcLigature;
    CharTab_hdr.fFlag = lpCharTab->fFlag;

    MEMSET(Dummy, (BYTE)0, CHARTAB_HDR_SIZE);

    /* Write all zero to the header */
    if (FileSeekWrite(hfpbCharTab, Dummy, FoAddFo(foStart, foNil),
			CHARTAB_HDR_SIZE, &hr) != CHARTAB_HDR_SIZE)
    {
        goto exit01;
    }

    if (FileSeekWrite(hfpbCharTab, &CharTab_hdr, FoAddFo(foStart, foNil),
			sizeof(CHARTAB_HDR), &hr) != sizeof(CHARTAB_HDR))
    {
        goto exit01;
    }

    foOffset = FoAddFo(foStart, MakeFo(CHARTAB_HDR_SIZE,0L));

    /* Write out the character table buffer */
    if (FileSeekWrite(hfpbCharTab, lpCharTab->lpCMapTab, foOffset,
        CharTabSize, &hr) != CharTabSize)
    {
        goto exit01;
    }

    if (CharLigatureSize)
    {
        foOffset = FoAddDw(foOffset,CharTabSize);

        /* Write out the ligature table */
        if (FileSeekWrite(hfpbCharTab, lpCharTab->lpLigature, foOffset,
            CharLigatureSize, &hr) != CharLigatureSize)
        {
            goto exit01;
        }
    }

    hr = S_OK;

exit01:
	// Close file if we created it.
	if (fCreatedFile)
		FileClose(hfpbCharTab);  // Removed fRet= here.
    
    return hr;
}

/*************************************************************************
 *      @doc    INTERNAL
 *
 *      @func   VOID PASCAL NEAR | GetWord |
 *              This function will scan and get a word from the input buffer
 *
 *      @parm   LSZ FAR | *lplszBuf |
 *              Pointer to input buffer. The content will be updated on exit
 *
 *      @parm   LST | lstWord |
 *              Buffer to received parsed word
 *************************************************************************/
PRIVATE VOID PASCAL NEAR GetWord (LSZ FAR *lplszBuf, LST lstWord)
{
    LST  lstWordStart;
    LSZ  lszBuf = *lplszBuf;

    /* Remember the beginning of the word */
    lstWordStart = lstWord++;

    /* Skip all beginning blanks */
    while (*lszBuf == ' ')
        lszBuf++;

    /* Now keep accumulating the word's characters */
    for (;;) {
        switch (*lszBuf) {
            case 0:
            case ' ':
                goto exit0;

            case '/':
                if (*(lszBuf + 1) == '/') { 
                    /* Skip the inline comment */
                    while (*lszBuf)
                        lszBuf++;
                    goto exit0;
                }

            default:
                *lstWord++ = *lszBuf++;
        }
    }

exit0:
    *lplszBuf = lszBuf;
    *lstWordStart = (BYTE)(lstWord - lstWordStart - 1);
}

/*************************************************************************
 *      @doc    INTERNAL
 *
 *      @func   VOID PASCAL NEAR | StripCRLF |
 *              This function will change all CR, LF in the input buffer into
 *              0, all tabs into blank
 *
 *      @parm   LPB | lpbBuf |
 *              Input buffer
 *
 *      @parm   WORD | BufSize |
 *              Length of the buffer
 *************************************************************************/
PRIVATE VOID PASCAL NEAR StripCRLF (LPB lpbBuf, WORD BufSize)
{
    for (; BufSize > 0; BufSize --)
    {
        switch (*lpbBuf)
        {
            case RETURN:
            case NEWLINE:
                *lpbBuf = 0;
                break;
            case '\t':
                *lpbBuf = ' ';
                break;
        }
        lpbBuf++;
    }
}


/*************************************************************************
 *      @doc    API RETRIEVAL
 *
 *      @func   LPCHARTAB FAR PASCAL | MVCharTableIndexLoad |
 *              This function will load a character table from a
 *              system file.
 *
 *      @parm   HANDLE | hfpbSysFile |
 *              If non-zero, this is the handle of an already opened system file
 *
 *      @parm   LSZ | lszFilename |
 *              If hpfbSysFile is non-zero, this is the name of the CharTab's subfile
 *              else this is the combined filename with the format
 *              "dos_filename[CharTab_filename]"
 *              If the subfile's name is not specified, the default CharTab's file
 *              name will be used
 *
 *      @parm   PHRESULT | phr |
 *              Pointer to error buffer
 *
 *      @rdesc  If succeeded, the function will return a pointer the loaded
 *              CharTab, else NULL. The error buffer will contain information
 *              about the cause of the failure
 *
 *      @comm   About ligature table, there are some assumptions:
 *              If hLigature == 0 {
 *                      if (wcLigature == 0)
 *                              There is no ligature table
 *                      else
 *                              We use the default ligature table. There is no need
 *                              to write out the table data
 *              }
 *              else
 *                      The author provides a ligature table.
 *************************************************************************/
PUBLIC  LPVOID EXPORT_API FAR PASCAL MVCharTableIndexLoad(HFPB hfpbSysFile,
    LSZ lszFilename, PHRESULT phr)
{
    HANDLE hfpbCharTabFile;
	BOOL	fOpenedFile;
    LPCHARTAB       lpCharTab = NULL;
    CHARTAB_HDR FAR *lpCharTabHdr;
    CHARTAB_HDR CharTabHdr;
	FILEOFFSET foStart;

#if _MAC
    int MacClipMap[256], i;
    LPCMAP lpCMapEntry;
#endif

	*phr = S_OK;
    lpCharTabHdr = &CharTabHdr;

	/* Open subfile, (and system file if necessary) */
	if ((fOpenedFile =
			FsTypeFromHfpb(hfpbCharTabFile = hfpbSysFile) != FS_SUBFILE) &&
		(hfpbCharTabFile = (HANDLE)FileOpen
			(hfpbSysFile, lszFilename, hfpbSysFile ? FS_SUBFILE : REGULAR_FILE,
			READ, phr)) == 0)
	{
exit0:
		return (LPV)lpCharTab;
	}

	// If we didn't open the file, we need to find out where the file seek
	// pointer is initially so that we only seek relative to that starting
	// position (i.e. the caller owns the part of the file that comes before).
	foStart = (fOpenedFile ? MakeFo(0,0) :
				FileSeek (hfpbCharTabFile, MakeFo (0, 0), wFSSeekCur, phr));

    /* Read in the header file, and make sure that is a CharTab file */
    if (FAILED(*phr) ||
		FileSeekRead(hfpbCharTabFile, (LPV)lpCharTabHdr, FoAddFo(foStart, foNil),
        sizeof(CHARTAB_HDR), phr) != sizeof(CHARTAB_HDR)) {
exit1:
        /* Close the subfile if we opened it */
		if (fOpenedFile)
			FileClose(hfpbCharTabFile);

        /* Close the system file if we open it, the handle will be
         * released in the process */

        goto exit0;
    }

    /* MAC code. They will be optimized out */

    lpCharTabHdr->FileStamp = SWAPWORD(lpCharTabHdr->FileStamp);
    lpCharTabHdr->version = SWAPWORD(lpCharTabHdr->version);
    lpCharTabHdr->wcTabEntries = SWAPWORD(lpCharTabHdr->wcTabEntries);
    lpCharTabHdr->wcLigature = SWAPWORD(lpCharTabHdr->wcLigature);
    lpCharTabHdr->fFlag = SWAPWORD(lpCharTabHdr->fFlag);
    lpCharTabHdr->dwTabSize = SWAPLONG(lpCharTabHdr->dwTabSize);

    /* Check to see if the data read in is valid */
    if (lpCharTabHdr->FileStamp !=  CHRTAB_STAMP)
    {   // File stamp
        SetErrCode(phr, E_BADVERSION);
        goto exit1;
    }
    /* Allocate memory for the character table. Note that there may be
     * some inefficiency im memory usage, since there are 3-bytes per
     * ligature entry, and several more for each charmap entry.
     */
    if ((lpCharTab = CharTableCreate ((lpCharTabHdr->wcTabEntries +
        lpCharTabHdr->wcLigature))) == NULL) {
        SetErrCode(phr, E_OUTOFMEMORY);
        goto exit1;
    }

    lpCharTab->wcTabEntries = lpCharTabHdr->wcTabEntries;
    lpCharTab->fFlag = lpCharTabHdr->fFlag;
    lpCharTab->hLigature = 0;

    /* Read in the CharTab bitmap data */
    if (FileSeekRead(hfpbCharTabFile,
        (LPV)lpCharTab->lpCMapTab, FoAddFo(foStart, MakeFo(CHARTAB_HDR_SIZE,0)),
        (WORD)lpCharTabHdr->dwTabSize, phr) !=
        (WORD)lpCharTabHdr->dwTabSize)
    {
        MVCharTableDispose(lpCharTab);
        lpCharTab = NULL;
        goto exit1;
    }

#if _MAC
   /* Create the mapping from Mac to Windows. This is to deal with the
    * situation people entering data on a Mac and look for that string
    */
    lpCMapEntry = (LPCMAP)lpCharTab->lpCMapTab;

    for (i = lpCharTab->wcTabEntries; i > 0; i--)  
    {
        // erinfox: swap class
        lpCMapEntry[i].Class = SWAPWORD(lpCMapEntry[i].Class);

        lpCMapEntry[i].SortOrder = SWAPWORD(lpCMapEntry[i].SortOrder);
		if (lpCMapEntry[i].MacDisplay != 0)
        {
            lpCMapEntry[lpCMapEntry[i].MacDisplay].MacToWin = i;
        }
    }

    /* Change the Mac clipboard mapping. I am using the 256 value.
     * The character table hasthe mapping based on the Windows indices
     * but since the WinToMap mapping will change all the character
     * values to their corresponding Mac, the new values have to be
     * used as indices, ie. there is a need to remap the
     * MacCaseSensitiveNorm column based on the new Mac indices
     */
    MEMSET (MacClipMap, 0, 256);
    for ( i = 0; i < lpCharTab->wcTabEntries; i++)
    {
        MacClipMap[lpCMapEntry[i].MacDisplay] =
            lpCMapEntry[i].MacCaseSensitiveNorm;
    }

    /* Reset the mapping. */
    /* Change all 0's to 32 (space) to avoid truncation */
    for ( i = 0; i < lpCharTab->wcTabEntries; i++)
    {
        if ((lpCMapEntry[i].MacCaseSensitiveNorm = MacClipMap[i]) == 0)
            lpCMapEntry[i].MacCaseSensitiveNorm = 32;
    }
#endif /* _MAC */
   
    /* Look for ligature */
    switch (lpCharTab->fFlag) {
        case NO_LIGATURE:
            break;

        case USE_DEF_LIGATURE:
            lpCharTab->wcLigature = DEF_LIGATURE_COUNT;
            lpCharTab->fFlag = USE_DEF_LIGATURE;
            lpCharTab->hLigature = 0;
            lpCharTab->lpLigature = LigatureTable;
            break;

        default:
            lpCharTab->lpLigature = (LPB)lpCharTab->lpCMapTab +
                lpCharTabHdr->wcTabEntries * sizeof(CHARMAP);
            lpCharTab->wcLigature = lpCharTabHdr->wcLigature;
    }
    goto exit1;
}

/*************************************************************************
 *  @doc    API RETRIEVAL
 *  @func   LPCTAB FAR PASCAL | MVCharTableGetDefault |
 *      Retrieve the default character mapping table used by MV
 *  @parm   PHRESULT | phr |
 *      Pointer to error buffer
 *  @rdesc
 *      Pointer to character mapping table if successful, NULL otherwise.
 *      In case of error, phr will contain the error code
 *************************************************************************/
PUBLIC LPV EXPORT_API FAR PASCAL MVCharTableGetDefault (PHRESULT phr)
{
    LPCHARTAB  lpCharTab = NULL;
#if _MAC
    int i;
    LPCMAP lpCMapEntry;
#endif /* _MAC */

    /* Allocate memory for the character table. Note that there may be
     * some inefficiency im memory usage, since there are 3-bytes per
     * ligature entry, and several more for each charmap entry.
     */
    if ((lpCharTab = CharTableCreate (MAX_CHAR_COUNT +
      DEF_LIGATURE_COUNT)) == NULL) {
        SetErrCode(phr, E_OUTOFMEMORY);
        return NULL;
    }

    lpCharTab->wcTabEntries = MAX_CHAR_COUNT;
    lpCharTab->wcLigature = DEF_LIGATURE_COUNT;
    lpCharTab->fFlag = USE_DEF_LIGATURE;
    lpCharTab->hLigature = 0;
    lpCharTab->lpLigature = LigatureTable;
    lpCharTab->lpCMapTab = DefaultCMap;
   
#if _MAC
    /* Create the mapping from Mac to Windows. This is to deal with the
     * situation people entering data on a Mac and look for that string
     */
    lpCMapEntry = (LPCMAP)lpCharTab->lpCMapTab;
   
    for (i = lpCharTab->wcTabEntries; i > 0; i--)  {
        if (lpCMapEntry[i].MacDisplay != 0)  {
            lpCMapEntry[lpCMapEntry[i].MacDisplay].MacToWin = i;
        }
    }
   
    /* Create the clipboard case sensitive column */
#endif /* _MAC */
   
    return(lpCharTab);
   
}

/*************************************************************************
 *      @doc    API INDEX RETRIEVAL
 *
 *      @func   VOID FAR PASCAL | MVCharTableDispose |
 *              Free all memory associated with the character table
 *
 *      @parm   LPCHARTAB | lpCharTab |
 *              Pointer to character table structure
 *************************************************************************/
PUBLIC VOID EXPORT_API FAR PASCAL MVCharTableDispose (LPCHARTAB lpCharTab)
{
    HANDLE hBuf;

    if (lpCharTab == NULL)
        return;
    if (hBuf = lpCharTab->hLigature) {
        FreeHandle2(hBuf);
        lpCharTab->hLigature = NULL;
    }
	FreeHandle2(lpCharTab->hStruct);
}


/*************************************************************************
 *      @doc    API INDEX RETRIEVAL
 *
 *      @func   VOID FAR PASCAL | MVCharTableSetWildcards |
 *              Change the property of '*' and '?' to CLASS_WILDCARD.
 *
 *      @parm   LPCHARTAB | lpCharTab |
 *              Pointer to character table structure
 *************************************************************************/
PUBLIC VOID EXPORT_API FAR PASCAL MVCharTableSetWildcards (LPCHARTAB lpCharTab)
{
	if (lpCharTab == NULL)
		return;
		
	(lpCharTab->lpCMapTab)['*'].Class = CLASS_WILDCARD;
	(lpCharTab->lpCMapTab)['?'].Class = CLASS_WILDCARD;
}


/*************************************************************************
 *      @doc    INTERNAL
 *
 *      @func   LPB NEAR PASCAL | GetNumber |
 *              This function will try to get to a number in an ASCII and then
 *              retrieve it. It will fill up the input buffer if necessary
 *
 *      @parm   HFPB | hfpb |
 *              Handle of opened file
 *
 *      @parm   LPB | Buffer |
 *              Buffer to store the input data
 *
 *      @parm   LPB | lpbBuf |
 *              Current location pointer into the input buffer
 *
 *      @parm   int far * | lpNum |
 *              Place to store the retrieved number
 *
 *      @parm   WORD FAR * | lpcbRead |
 *              How many bytes are left in the input buffer
 *
 *      @rdesc  If succeeded, the function will return the last location
 *              of the input buffer, else NULL if failed. If succeeded, the
 *              number is stored in *lpNum, the number of bytes left in the input
 *              buffer is updated
 *************************************************************************/
PRIVATE LPB NEAR PASCAL GetNumber (HFPB hfpb, LPB Buffer, LPB lpbBuf,
    int far *lpNum, WORD FAR *lpcbRead)
{
    register WORD fSkipComment = 0;
    register WORD cbRead = *lpcbRead;
    WORD number = 0;
    BYTE fGetNum = FALSE;
	HRESULT hr;

    for (;;) {
        /* Check for empty buffer, and read in new data if necessary */
        if (cbRead == 0) {
            cbRead = (WORD) FileRead(hfpb, lpbBuf = Buffer, INBUF_SIZE, &hr);
            if (cbRead == 0 || FAILED(hr)) {
                if (fGetNum == FALSE)
                    lpbBuf = NULL;  // Return error
                break;
            }
        }

        if (*lpbBuf == RETURN || *lpbBuf == NEWLINE) {
            /* EOL, reset variables, exit if we already got a number */
            fSkipComment = 0;
            if (fGetNum)
                break;
        }
        else if (fSkipComment != 2) {
            /* We are not inside a comment, so look for a number */
            if (*lpbBuf >= '0' && *lpbBuf <= '9') {
                /* Get the number */
                number = number * 10 + *lpbBuf - '0';
                fGetNum = TRUE;
            }
            else if (*lpbBuf == SLASH) {
                if (fGetNum)
                    break;
                fSkipComment++; // Increment slash count
            }
            else {
                if (fGetNum)
                    break;
                fSkipComment = 0;
            }
        }
        cbRead--;
        lpbBuf++;
    }

    /* Update the variables */
    *lpcbRead = cbRead;
    *lpNum = number;
    return lpbBuf;
}


/*************************************************************************
 *      @doc    INTERNAL
 *
 *      @func   LPCHARTAB PASCAL NEAR | CharTableCreate |
 *              Allocate memory necessary for the character table. The amount
 *              needed is based on the number of entries
 *
 *      @parm   WORD | wcTabEntries |
 *              Number of entries in the character table
 *
 *************************************************************************/
PRIVATE LPCHARTAB PASCAL NEAR CharTableCreate (int wcTabEntries)
{
    HANDLE hMem;
    LPCHARTAB lpCharTab;

    /* Allocate memory for the character table */
    if ((hMem = _GLOBALALLOC (DLLGMEM_ZEROINIT,
        sizeof(CHARTAB) + wcTabEntries * sizeof(CHARMAP))) == NULL) {
        return NULL;
    }
    lpCharTab = (LPCHARTAB)_GLOBALLOCK(hMem);
    lpCharTab->lpCMapTab = (LPCMAP)(lpCharTab + 1);
    lpCharTab->hStruct = hMem;
    return lpCharTab;
}