/*++

Copyright (c) 1991-1999,  Microsoft Corporation  All rights reserved.

Module Name:

    language.c

Abstract:

    This file contains functions necessary to parse and write the language
    specific tables to a data file.

    External Routines in this file:
      ParseLanguage
      WriteLanguage
      ParseLangException
      WriteLangException

Revision History:

    12-10-91    JulieB    Created.

--*/



//
//  Include Files.
//

#include "nlstrans.h"




//
//  Forward Declarations.
//

int
GetUpperTable(
    PLANGUAGE pLang,
    int Size);

int
GetLowerTable(
    PLANGUAGE pLang,
    int Size);

int
GetLangExceptionTable(
    PLANG_EXCEPT pLangExcept,
    int Size);
int
WriteUpper(
    PLANGUAGE pLang,
    FILE *pOutputFile);

int
WriteLower(
    PLANGUAGE pLang,
    FILE *pOutputFile);

int
WriteLangExceptionTable(
    PLANG_EXCEPT pLangExcept,
    FILE *pOutputFile);





//-------------------------------------------------------------------------//
//                            EXTERNAL ROUTINES                            //
//-------------------------------------------------------------------------//


////////////////////////////////////////////////////////////////////////////
//
//  ParseLanguage
//
//  This routine parses the input file for the language specific tables.
//  This routine is only entered when the LANGUAGE keyword is found.
//  The parsing continues until the ENDLANGUAGE keyword is found.
//
//  12-10-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int ParseLanguage(
    PLANGUAGE pLang,
    PSZ pszKeyWord)
{
    int size;                          // size of table to follow


    while (fscanf(pInputFile, "%s", pszKeyWord) == 1)
    {
        if (_stricmp(pszKeyWord, "UPPERCASE") == 0)
        {
            if (Verbose)
                printf("\n\nFound UPPERCASE keyword.\n");

            //
            //  Get size parameter.
            //
            if (GetSize(&size))
                return (1);

            //
            //  Get UPPERCASE Table.
            //
            if (GetUpperTable(pLang, size))
            {
                return (1);
            }

            //
            //  Set WriteFlags for UPPERCASE Table.
            //
            pLang->WriteFlags |= F_UPPER;
        }

        else if (_stricmp(pszKeyWord, "LOWERCASE") == 0)
        {
            if (Verbose)
                printf("\n\nFound LOWERCASE keyword.\n");

            //
            //  Get size parameter.
            //
            if (GetSize(&size))
                return (1);

            //
            //  Get LOWERCASE Table.
            //
            if (GetLowerTable(pLang, size))
            {
                return (1);
            }

            //
            //  Set WriteFlags for LOWERCASE Table.
            //
            pLang->WriteFlags |= F_LOWER;
        }

        else if (_stricmp(pszKeyWord, "ENDLANGUAGE") == 0)
        {
            if (Verbose)
                printf("\n\nFound ENDLANGUAGE keyword.\n");

            //
            //  Return success.
            //
            return (0);
        }

        else
        {
            printf("Parse Error: Invalid Instruction '%s'.\n", pszKeyWord);
            return (1);
        }
    }

    //
    //  If this point is reached, then the ENDLANGUAGE keyword was
    //  not found.  Return an error.
    //
    printf("Parse Error: Expecting ENDLANGUAGE keyword.\n");
    return (1);
}


////////////////////////////////////////////////////////////////////////////
//
//  WriteLanguage
//
//  This routine writes the language specific tables to an output file.
//
//  12-10-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WriteLanguage(
    PLANGUAGE pLang)
{
    FILE *pOutputFile;            // ptr to output file
    WORD wValue;                  // temp storage value


    //
    //  Make sure all tables are present.
    //
    if (!((pLang->WriteFlags & F_UPPER) && (pLang->WriteFlags & F_LOWER)))
    {
        printf("Write Error: All tables must be present -\n");
        printf("             Uppercase and Lowercase Tables.\n");
        return (1);
    }


    //
    //  Make sure output file can be opened for writing.
    //
    if ((pOutputFile = fopen(LANGUAGE_FILE, "w+b")) == 0)
    {
        printf("Error opening output file %s.\n", LANGUAGE_FILE);
        return (1);
    }

    if (Verbose)
        printf("\n\nWriting output file %s...\n", LANGUAGE_FILE);

    //
    //  Write IfDefault value to file.
    //
    pLang->IfDefault = 1;
    wValue = (WORD)(pLang->IfDefault);
    if (FileWrite( pOutputFile,
                   &wValue,
                   sizeof(WORD),
                   1,
                   "IfDefault" ))
    {
        return (1);
    }

    //
    //  Write UPPERCASE Table to output file.
    //
    if (WriteUpper(pLang, pOutputFile))
    {
        fclose(pOutputFile);
        return (1);
    }

    //
    //  Free UPPERCASE table structures.
    //
    Free844(pLang->pUpper);


    //
    //  Write LOWERCASE Table to output file.
    //
    if (WriteLower(pLang, pOutputFile))
    {
        fclose(pOutputFile);
        return (1);
    }

    //
    //  Free LOWERCASE table structures.
    //
    Free844(pLang->pLower);


    //
    //  Close the output file.
    //
    fclose(pOutputFile);

    //
    //  Return success.
    //
    printf("\nSuccessfully wrote output file %s\n", LANGUAGE_FILE);
    return (0);
}


////////////////////////////////////////////////////////////////////////////
//
//  ParseLangException
//
//  This routine parses the input file for the language exception specific
//  tables.  This routine is only entered when the LANGUAGE_EXCEPTION keyword
//  is found.
//
//  08-30-95    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int ParseLangException(
    PLANG_EXCEPT pLangExcept,
    PSZ pszKeyWord)
{
    int size;                          // size of table to follow


    //
    //  Get size parameter.
    //
    if (GetSize(&size))
        return (1);

    //
    //  Get EXCEPTION Table.
    //
    if (GetLangExceptionTable(pLangExcept, size))
    {
        return (1);
    }

    //
    //  Return success.
    //
    return (0);
}


////////////////////////////////////////////////////////////////////////////
//
//  WriteLangException
//
//  This routine writes the language excpetion specific tables to an output
//  file.
//
//  08-30-95    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WriteLangException(
    PLANG_EXCEPT pLangExcept)
{
    FILE *pOutputFile;                 // ptr to output file
    int ctr;                           // loop counter


    //
    //  Make sure output file can be opened for writing.
    //
    if ((pOutputFile = fopen(LANG_EXCEPT_FILE, "w+b")) == 0)
    {
        printf("Error opening output file %s.\n", LANG_EXCEPT_FILE);
        return (1);
    }

    if (Verbose)
        printf("\n\nWriting output file %s...\n", LANG_EXCEPT_FILE);


    //
    //  Write EXCEPTION Table to output file.
    //
    if (WriteLangExceptionTable(pLangExcept, pOutputFile))
    {
        fclose(pOutputFile);
        return (1);
    }

    //
    //  Free EXCEPTION header and table structures.
    //
    for (ctr = 0; ctr < pLangExcept->NumException; ctr++)
    {
        if ((pLangExcept->pExceptTbl)[ctr])
        {
            free((pLangExcept->pExceptTbl)[ctr]);
        }
    }
    free(pLangExcept->pExceptTbl);
    free(pLangExcept->pExceptHdr);


    //
    //  Close the output file.
    //
    fclose(pOutputFile);

    //
    //  Return success.
    //
    printf("\nSuccessfully wrote output file %s\n", LANG_EXCEPT_FILE);
    return (0);
}




//-------------------------------------------------------------------------//
//                            INTERNAL ROUTINES                            //
//-------------------------------------------------------------------------//


////////////////////////////////////////////////////////////////////////////
//
//  GetUpperTable
//
//  This routine gets the upper case table from the input file.  It uses
//  the size parameter to know when to stop reading from the file.  If an
//  error is encountered, a message is printed and an error is returned.
//
//  07-30-91    JulieB    Created.
//  12-10-91    JulieB    Modified for new table format.
////////////////////////////////////////////////////////////////////////////

int GetUpperTable(
    PLANGUAGE pLang,
    int Size)
{
    int LoChar;                   // lower case value
    int UpChar;                   // upper case value
    register int Ctr;             // loop counter
    int NumItems;                 // number of items returned from fscanf


    //
    //  Allocate top buffer for 8:4:4 table - 256 pointers.
    //
    if (Allocate8(&pLang->pUpper))
    {
        return (1);
    }

    //
    //  For each entry in table, read in the upper case and lower case
    //  character from input file, allocate necessary 16 word buffers
    //  based on upper case value, and store difference to lower case
    //  character.
    //
    for (Ctr = 0; Ctr < Size; Ctr++)
    {
        //
        //  Read in lower case and upper case characters.
        //
        NumItems = fscanf( pInputFile,
                           "%x %x ;%*[^\n]",
                           &LoChar,
                           &UpChar );
        if (NumItems != 2)
        {
            printf("Parse Error: Error reading UPPERCASE values.\n");
            return (1);
        }

        if (Verbose)
            printf("  Lower = %x\tUpper = %x\n", LoChar, UpChar);

        //
        //  Insert difference (UpChar - LoChar) into 8:4:4 table.
        //
        if (Insert844( pLang->pUpper,
                       (WORD)LoChar,
                       (WORD)(UpChar - LoChar),
                       &pLang->UPBuf2,
                       &pLang->UPBuf3,
                       sizeof(WORD) ))
        {
            return (1);
        }
    }

    //
    //  Return success.
    //
    return (0);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetLowerTable
//
//  This routine gets the lower case table from the input file.  It uses
//  the size parameter to know when to stop reading from the file.  If an
//  error is encountered, a message is printed and an error is returned.
//
//  07-30-91    JulieB    Created.
//  12-10-91    JulieB    Modified for new table format.
////////////////////////////////////////////////////////////////////////////

int GetLowerTable(
    PLANGUAGE pLang,
    int Size)
{
    int UpChar;                   // upper case value
    int LoChar;                   // lower case value
    register int Ctr;             // loop counter
    int NumItems;                 // number of items returned from fscanf


    //
    //  Allocate top buffer for 8:4:4 table - 256 pointers.
    //
    if (Allocate8(&pLang->pLower))
    {
        return (1);
    }

    //
    //  For each entry in table, read in the upper case and lower case
    //  character from input file, allocate necessary 16 word buffers
    //  based on lower case value, and store difference to upper case
    //  character.
    //
    for (Ctr = 0; Ctr < Size; Ctr++)
    {
        //
        //  Read in lower case and upper case characters.
        //
        NumItems = fscanf( pInputFile,
                           "%x %x ;%*[^\n]",
                           &UpChar,
                           &LoChar );
        if (NumItems != 2)
        {
            printf("Parse Error: Error reading LOWERCASE values.\n");
            return (1);
        }

        if (Verbose)
            printf("  Upper = %x\tLower = %x\n", UpChar, LoChar);

        //
        //  Insert difference (LoChar - UpChar) into 8:4:4 table.
        //
        if (Insert844( pLang->pLower,
                       (WORD)UpChar,
                       (WORD)(LoChar - UpChar),
                       &pLang->LOBuf2,
                       &pLang->LOBuf3,
                       sizeof(WORD) ))
        {
            return (1);
        }
    }

    //
    //  Return success.
    //
    return (0);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetLangExceptionTable
//
//  This routine gets the exception table from the input file.
//  It uses the size parameter to know when to stop reading from the file.
//  If an error is encountered, a message is printed and an error is returned.
//
//  08-30-95    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int GetLangExceptionTable(
    PLANG_EXCEPT pLangExcept,
    int Size)
{
    DWORD Locale;                 // locale id
    int NumUp;                    // number of entries for upper case
    int NumLo;                    // number of entries for lower case
    int TotalNum;                 // total number of exceptions for locale
    int UCP1;                     // exception code point 1
    int UCP2;                     // exception code point 2
    int Offset = 0;               // offset to store
    int Ctr;                      // loop counter
    int Ctr2;                     // loop counter
    int LcidCtr;                  // loop counter
    int NumItems;                 // number of items returned from fscanf
    int Num;                      // temp value
    char pszTemp[MAX];            // temp buffer for string


    //
    //  Allocate exception header and exception table and set size of
    //  table in language exception tables structure.
    //
    if (AllocateLangException(pLangExcept, Size))
    {
        return (1);
    }

    //
    //  For each entry in table, read in the LCID keyword, locale id,
    //  the number of upper case entries for that locale id, and the
    //  number of lower case entries for that locale id.  Then, for all
    //  entries for the locale id, read in the exception code point and
    //  the upper/lower case code point.  Store all values in the
    //  exception header and the exception table.
    //
    Ctr = 0;
    while (Ctr < Size)
    {
        //
        //  Read in the LCID keyword, locale id, and the number of
        //  entries for the locale id.
        //
        NumItems = fscanf( pInputFile,
                           " LCID %i %i %i ;%*[^\n]",
                           &Locale,
                           &NumUp,
                           &NumLo );
        if (NumItems != 3)
        {
            printf("Parse Error: Error reading EXCEPTION LCID values.\n");
            return (1);
        }

        if (Verbose)
            printf("\n  LCID = %lx\tNumUpper = %d\tNumLower = %d\n\n",
                   Locale, NumUp, NumLo);

        //
        //  Store the locale id and the number of entries in the header.
        //
        ((pLangExcept->pExceptHdr)[Ctr]).Locale = (DWORD)Locale;
        ((pLangExcept->pExceptHdr)[Ctr]).Offset = (DWORD)Offset;
        ((pLangExcept->pExceptHdr)[Ctr]).NumUpEntries = (DWORD)NumUp;
        ((pLangExcept->pExceptHdr)[Ctr]).NumLoEntries = (DWORD)NumLo;

        //
        //  See if there are any other LCIDs for this exception table.
        //
        LcidCtr = 1;
        while (NumItems = fscanf( pInputFile,
                                  " LCID %i ;%*[^\n]",
                                  &Locale ))
        {
            if (NumItems > 2)
            {
                printf("Parse Error: Error reading secondary EXCEPTION LCID values.\n");
                return (1);
            }

            if (Verbose)
                printf("\n  LCID = %lx\tNumUpper = %d\tNumLower = %d\n\n",
                       Locale, NumUp, NumLo);

            //
            //  Store the locale id and the number of entries in the header.
            //
            ((pLangExcept->pExceptHdr)[Ctr + LcidCtr]).Locale = (DWORD)Locale;
            ((pLangExcept->pExceptHdr)[Ctr + LcidCtr]).Offset = (DWORD)Offset;
            ((pLangExcept->pExceptHdr)[Ctr + LcidCtr]).NumUpEntries = (DWORD)NumUp;
            ((pLangExcept->pExceptHdr)[Ctr + LcidCtr]).NumLoEntries = (DWORD)NumLo;

            LcidCtr++;
        }

        //
        //  Add (Num times number of words in exception node) to Offset
        //  to get the offset of the next LCID entries.
        //
        TotalNum = NumUp + NumLo;
        Offset += (TotalNum * NUM_L_EXCEPT_WORDS);

        //
        //  Allocate exception nodes for current LCID.
        //
        if (AllocateLangExceptionNodes(pLangExcept, TotalNum, Ctr))
        {
            return (1);
        }

        //
        //  Read in the UPPERCASE keyword.
        //
        NumItems = fscanf(pInputFile, "%s", pszTemp);
        if ((NumItems != 1) ||
            (_stricmp(pszTemp, "UPPERCASE") != 0))
        {
            printf("Parse Error: Error reading UPPERCASE keyword for LCID %lx.\n",
                    Locale);
            return (1);
        }
        else
        {
            if (Verbose)
                printf("\n\nFound UPPERCASE keyword.\n");
        }

        //
        //  For each entry for the locale id, read in the exception code
        //  point and the upper/lower case code point.  Store the values
        //  in the exception table nodes.
        //
        for (Ctr2 = 0; Ctr2 < TotalNum; Ctr2++)
        {
            if (Ctr2 == NumUp)
            {
                //
                //  Read in the LOWERCASE keyword.
                //
                NumItems = fscanf(pInputFile, "%s", pszTemp);
                if ((NumItems != 1) ||
                    (_stricmp(pszTemp, "LOWERCASE") != 0))
                {
                    printf("Parse Error: Error reading LOWERCASE keyword for LCID %lx.\n",
                            Locale);
                    return (1);
                }
                else
                {
                    if (Verbose)
                        printf("\n\nFound LOWERCASE keyword.\n");
                }
            }

            //
            //  Read in code point and the upper/lower case code point.
            //
            NumItems = fscanf( pInputFile,
                               "%i %i ;%*[^\n]",
                               &UCP1,
                               &UCP2 );
            if (NumItems != 2)
            {
                printf("Parse Error: Error reading EXCEPTION values for LCID %lx.\n",
                        Locale);
                return (1);
            }

            if (Verbose)
                printf("    UCP1 = %x\tUCP2 = %x\n", UCP1, UCP2);

            //
            //  Store the weights in the exception table.
            //
            (((pLangExcept->pExceptTbl)[Ctr])[Ctr2]).UCP       = (WORD)UCP1;
            (((pLangExcept->pExceptTbl)[Ctr])[Ctr2]).AddAmount = (WORD)(UCP2 - UCP1);
        }

        Ctr += LcidCtr;
    }

    //
    //  Return success.
    //
    return (0);
}


////////////////////////////////////////////////////////////////////////////
//
//  WriteUpper
//
//  This routine writes the UPPERCASE information to the output file.
//
//  07-30-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WriteUpper(
    PLANGUAGE pLang,
    FILE *pOutputFile)
{
    int TblSize;                  // size of table
    WORD wValue;                  // temp storage value


    if (Verbose)
        printf("\nWriting UPPERCASE Table...\n");

    //
    //  Compute size of table.
    //
    TblSize = Compute844Size( pLang->UPBuf2,
                              pLang->UPBuf3,
                              sizeof(WORD) ) + 1;

    //
    //  Make sure the total size of the table is not greater than 64K.
    //  If it is, then the WORD offsets are too small.
    //
    if (TblSize > MAX_844_TBL_SIZE)
    {
       printf("Write Error: Size of UPPER table is greater than 64K.\n");
       return (1);
    }

    //
    //  Write the size to the output file.
    //
    wValue = (WORD)TblSize;
    if (FileWrite( pOutputFile,
                   &wValue,
                   sizeof(WORD),
                   1,
                   "UPPER size" ))
    {
        return (1);
    }

    //
    //  Write UPPERCASE 8:4:4 table to file.
    //
    if (Write844Table( pOutputFile,
                       pLang->pUpper,
                       pLang->UPBuf2,
                       TblSize - 1,
                       sizeof(WORD) ))
    {
        return (1);
    }

    //
    //  Return success.
    //
    return (0);
}


////////////////////////////////////////////////////////////////////////////
//
//  WriteLower
//
//  This routine writes the LOWERCASE information to the output file.
//
//  07-30-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WriteLower(
    PLANGUAGE pLang,
    FILE *pOutputFile)
{
    int TblSize;                  // size of table
    WORD wValue;                  // temp storage value


    if (Verbose)
        printf("\nWriting LOWERCASE Table...\n");

    //
    //  Compute size of table.
    //
    TblSize = Compute844Size( pLang->LOBuf2,
                              pLang->LOBuf3,
                              sizeof(WORD) ) + 1;

    //
    //  Make sure the total size of the table is not greater than 64K.
    //  If it is, then the WORD offsets are too small.
    //
    if (TblSize > MAX_844_TBL_SIZE)
    {
       printf("Write Error: Size of LOWER table is greater than 64K.\n");
       return (1);
    }

    //
    //  Write the size to the output file.
    //
    wValue = (WORD)TblSize;
    if (FileWrite( pOutputFile,
                   &wValue,
                   sizeof(WORD),
                   1,
                   "LOWER size" ))
    {
        return (1);
    }

    //
    //  Write LOWERCASE 8:4:4 table to file.
    //
    if (Write844Table( pOutputFile,
                       pLang->pLower,
                       pLang->LOBuf2,
                       TblSize - 1,
                       sizeof(WORD) ))
    {
        return (1);
    }

    //
    //  Return success.
    //
    return (0);
}


////////////////////////////////////////////////////////////////////////////
//
//  WriteLangExceptionTable
//
//  This routine writes the EXCEPTION information to the output file.
//
//  08-30-95    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WriteLangExceptionTable(
    PLANG_EXCEPT pLangExcept,
    FILE *pOutputFile)
{
    int TblSize;                  // size of table
    int Ctr;                      // loop counter
    WORD wValue;                  // temp storage value


    if (Verbose)
        printf("\nWriting EXCEPTION Table...\n");

    //
    //  Get the size of the table.
    //
    TblSize = pLangExcept->NumException;

    //
    //  Write the number of exception locales to the output file.
    //
    wValue = (WORD)TblSize;
    if (FileWrite( pOutputFile,
                   &wValue,
                   sizeof(DWORD),
                   1,
                   "Exception Size" ))
    {
        return (1);
    }

    //
    //  Write the exception header to the output file.
    //
    if (FileWrite( pOutputFile,
                   pLangExcept->pExceptHdr,
                   sizeof(L_EXCEPT_HDR),
                   TblSize,
                   "Exception Header" ))
    {
        return (1);
    }

    //
    //  Write the exception table to the output file.
    //
    for (Ctr = 0; Ctr < TblSize; Ctr++)
    {
        if ((pLangExcept->pExceptTbl)[Ctr])
        {
            if (FileWrite( pOutputFile,
                           (pLangExcept->pExceptTbl)[Ctr],
                           sizeof(L_EXCEPT_NODE),
                           ( ((pLangExcept->pExceptHdr)[Ctr]).NumUpEntries +
                             ((pLangExcept->pExceptHdr)[Ctr]).NumLoEntries ),
                           "Exception Table" ))
            {
                return (1);
            }
        }
    }

    //
    //  Return success.
    //
    return (0);
}
