/*
 * PROJECT:         ReactOS Build Tools [Keyboard Layout Compiler]
 * LICENSE:         BSD - See COPYING.BSD in the top level directory
 * FILE:            tools/kbdtool/parser.c
 * PURPOSE:         Parsing Logic
 * PROGRAMMERS:     ReactOS Foundation
 */

/* INCLUDES *******************************************************************/

#include "kbdtool.h"

/* GLOBALS ********************************************************************/

/* Internal parser data about everything that was parsed */
CHAR gBuf[256];
CHAR gKBDName[10];
CHAR gCopyright[256];
CHAR gDescription[256];
CHAR gCompany[256];
CHAR gLocaleName[256];
CHAR gVKeyName[32];
ULONG gID = 0;
ULONG gKbdLayoutVersion;
LAYOUT g_Layout;
ULONG gLineCount;

/* Table of keywords the parser recognizes */
PCHAR KeyWordList[KEYWORD_COUNT] =
{
    "KBD",
    "VERSION",
    "COPYRIGHT",
    "COMPANY",
    "LOCALENAME",
    "MODIIFERS",
    "SHIFTSTATE",
    "ATTRIBUTES",
    "LAYOUT",
    "DEADKEY",
    "LIGATURE",
    "KEYNAME",
    "KEYNAME_EXT",
    "KEYNAME_DEAD",
    "DESCRIPTIONS",
    "LANGUAGENAMES",
    "ENDKBD",
};

/* FUNCTIONS ******************************************************************/

ULONG
isKeyWord(PCHAR p)
{
    ULONG i;

    /* Check if we know this keyword */
    for (i = 0; i < KEYWORD_COUNT; i++) if (strcmp(KeyWordList[i], p) == 0) break;

    /* If we didn't find anything, i will be KEYWORD_COUNT, which is invalid */
    return i;
}

PCHAR
getVKName(IN ULONG VirtualKey,
          IN BOOLEAN Prefix)
{
    ULONG i;

    /* Loop for standard virtual key */
    if (((VirtualKey >= 'A') && (VirtualKey <= 'Z')) ||
        ((VirtualKey >= '0') && (VirtualKey <= '9')))
    {
        /* Fill out the name */
        gVKeyName[0] = '\'';
        gVKeyName[1] = VirtualKey;
        gVKeyName[2] = '\'';
        gVKeyName[3] = '\0';
        return gVKeyName;
    }

    /* Check if a prefix is required */
    if (Prefix)
    {
        /* Add it */
        strcpy(gVKeyName, "VK_");
    }
    else
    {
        /* Otherwise, don't add anything */
        strcpy(gVKeyName, "");
    }

    /* Loop all virtual keys */
    for (i = 0; i < 36; i++)
    {
        /* Check if this key matches */
        if (VKName[i].VirtualKey == VirtualKey)
        {
            /* Copy the key's name into the buffer */
            strcat(gVKeyName, VKName[i].Name);
            return gVKeyName;
        }
    }

    /* If we got here, then we failed, so print out an error name */
    strcpy(gVKeyName, "#ERROR#");
    return gVKeyName;
}

ULONG
getVKNum(IN PCHAR p)
{
    ULONG Length;
    ULONG i;
    ULONG KeyNumber;

    /* Compute the length of the string */
    Length = strlen(p);
    if (!Length) return -1;

    /* Check if this is is a simple key */
    if (Length == 1)
    {
        /* If it's a number, return it now */
        if ((*p >= '0') && (*p <= '9')) return *p;

        /* Otherwise, convert the letter to upper case */
        *p = toupper(*p);

        /* And make sure it's a valid letter */
        if ((*p >= 'A') && (*p <='Z')) return *p;

        /* Otherwise, fail */
        return -1;
    }

    /* Otherwise, scan our virtual key names */
    for (i = 0; i < 36; i++)
    {
        /* Check if we have a match */
        if (!strcmp(VKName[i].Name, p)) return VKName[i].VirtualKey;
    }

    /*  Check if this is a hex string */
    if ((*p == '0') && ((*(p + 1) == 'x') || (*(p + 1) == 'X')))
    {
        /* Get the key number from the hex string */
        *(p + 1) = 'x';
        if (sscanf(p, "0x%x", &KeyNumber) == 1) return KeyNumber;
    }

    /* No hope: fail */
    return -1;
}

UCHAR
getCharacterInfo(IN PCHAR State,
                 OUT PULONG EntryChar,
                 OUT PCHAR LigatureChar)
{
    ULONG Length;
    ULONG CharInfo = CHAR_NORMAL_KEY;
    UCHAR StateChar;
    ULONG CharCode;

    /* Calculate the length of the state */
    Length = strlen(State);

    /* Check if this is at least a simple key state */
    if (Length > 1)
    {
        /* Read the first character and check if it's a dead key */
        StateChar = State[Length - 1];
        if (StateChar == '@')
        {
            /* This is a dead key */
            CharInfo = CHAR_DEAD_KEY;
        }
        else if (StateChar == '%')
        {
            /* This is another key */
            CharInfo = CHAR_OTHER_KEY;
        }
    }

    /* Check if this is a numerical key state */
    if ((Length - 1) >= 2)
    {
        /* Scan for extended character code entry */
        if ((sscanf(State, "%6x", &CharCode) == 1) &&
            (((Length == 5) && (State[0] == '0')) ||
             ((Length == 6) && ((State[0] == '0') && (State[1] == '0')))))
        {
            /* Handle a ligature key */
            CharInfo = CHAR_LIGATURE_KEY;

            /* Not yet handled */
            printf("Ligatured character entries not yet supported!\n");
            exit(1);
        }
        else
        {
            /* Get the normal character entry */
            if (sscanf(State, "%4x", &CharCode) == 1)
            {
                /* Does the caller want the key? */
                if (EntryChar) *EntryChar = CharCode;
            }
            else
            {
                /* The entry is totally invalid */
                if (Verbose) printf("An unparseable character entry '%s' was found.\n", State);
                if (EntryChar) *EntryChar = 0;
                CharInfo = CHAR_INVALID_KEY;
            }
        }
    }
    else
    {
        /* Save the key if the caller requested it */
        if (EntryChar) *EntryChar = *State;
    }

    /* Return the type of character this is */
    return CharInfo;
}

BOOLEAN
NextLine(PCHAR LineBuffer,
         ULONG BufferSize,
         FILE *File)
{
    PCHAR p, pp;

    /* Scan each line */
    while (fgets(LineBuffer, BufferSize, File))
    {
        /* Remember it */
        gLineCount++;

        /* Reset the pointer at the beginning of the line */
        p = LineBuffer;

        /* Now bypass all whitespace (and tabspace) */
        while ((*p) && ((*p == ' ') || (*p == '\t'))) p++;

        /* If this is an old-style comment, skip the line */
        if (*p == ';')  continue;

        /* Otherwise, check for new-style comment */
        pp = strstr(p, "//");
        if (pp)
        {
            /* We have a comment, so terminate there (unless the whole line is one) */
            if (pp == p) continue;
            *pp = '\0';
        }
        else
        {
            /* No comment, so find the new line and terminate there */
            p = strchr(p, '\n');
            if (p) *p = '\0';
        }

        /* We have a line! */
        return TRUE;
    }

    /* No line found */
    return FALSE;
}

ULONG
SkipLines(VOID)
{
    ULONG KeyWord;
    CHAR KeyWordChars[32];

    /* Scan each line, skipping it if it's not a keyword */
    while (NextLine(gBuf, sizeof(gBuf), gfpInput))
    {
        /* Read a single word */
        if (sscanf(gBuf, "%s", KeyWordChars) == 1)
        {
            /* If the word is a keyword, stop skipping lines */
            KeyWord = isKeyWord(KeyWordChars);
            if (KeyWord < KEYWORD_COUNT) return KeyWord;
        }
    }

    /* We skipped all the possible lines, not finding anything */
    return KEYWORD_COUNT;
}

ULONG
DoKBD(VOID)
{
    /* On Unicode files, we need to find the Unicode marker (FEEF) */
    ASSERT(UnicodeFile == FALSE);

    /* Initial values */
    *gKBDName = '\0';
    *gDescription = '\0';

    /* Scan for the values */
    if (sscanf(gBuf, "KBD %8s \"%40[^\"]\" %d", gKBDName, gDescription, &gID) < 2)
    {
        /* Couldn't find them */
        printf("Unable to read keyboard name or description.\n");
        exit(1);
    }

    /* Debug only */
    DPRINT1("KBD Name: [%8s] Description: [%40s] ID: [%d]\n", gKBDName, gDescription, gID);
    return SkipLines();
}

ULONG
DoVERSION(VOID)
{
    /* Scan for the value */
    if (sscanf(gBuf, "VERSION %d", &gKbdLayoutVersion) < 1)
    {
        /* Couldn't find them */
        printf("Unable to read keyboard version information.\n");
    }

    /* Debug only */
    DPRINT1("VERSION [%d]\n", gKbdLayoutVersion);
    return SkipLines();
}

ULONG
DoCOPYRIGHT(VOID)
{
    /* Initial values */
    *gCopyright = '\0';

    /* Scan for the value */
    if (sscanf(gBuf, "COPYRIGHT \"%40[^\"]\"", gCopyright) < 1)
    {
        /* Couldn't find them */
        printf("Unable to read the specified COPYRIGHT string.\n");
    }

    /* Debug only */
    DPRINT1("COPYRIGHT [%40s]\n", gCopyright);
    return SkipLines();
}

ULONG
DoCOMPANY(VOID)
{
    /* Initial values */
    *gCompany = '\0';

    /* Scan for the value */
    if (sscanf(gBuf, "COMPANY \"%85[^\"]\"", gCompany) < 1)
    {
        /* Couldn't find them */
        printf("Unable to read the specified COMPANY name.\n");
    }

    /* Debug only */
    DPRINT1("COMPANY [%85s]\n", gCompany);
    return SkipLines();
}

ULONG
DoLOCALENAME(VOID)
{
    /* Initial values */
    *gLocaleName = '\0';

    /* Scan for the value */
    if (sscanf(gBuf, "LOCALENAME \"%40[^\"]\"", gLocaleName) < 1)
    {
        /* Couldn't find them */
        printf("Unable to read the specified COPYRIGHT string.\n");
    }

    /* Debug only */
    DPRINT1("LOCALENAME [%40s]\n", gLocaleName);
    return SkipLines();
}

ULONG
DoDESCRIPTIONS(IN PKEYNAME* DescriptionData)
{
    ULONG KeyWord = 0;
    CHAR Token[32];
    ULONG LanguageCode;
    PCHAR p, pp;
    PKEYNAME Description;

    /* Assume nothing */
    *DescriptionData = 0;

    /* Start scanning */
    while (NextLine(gBuf, 256, gfpInput))
    {
        /* Search for token */
        if (sscanf(gBuf, "%s", Token) != 1) continue;

        /* Make sure it's not just a comment */
        if (*Token == ';') continue;

        /* Make sure it's not a keyword */
        KeyWord = isKeyWord(Token);
        if (KeyWord < KEYWORD_COUNT) break;

        /* Now scan for the language code */
        if (sscanf(Token, " %4x", &LanguageCode) != 1)
        {
            /* Skip */
            printf("An invalid LANGID was specified.\n");
            continue;
        }

        /* Now get the actual description */
        if (sscanf(gBuf, " %*4x %s[^\n]", Token) != 1)
        {
            /* Skip */
            printf("A language description is missing.\n");
            continue;
        }

        /* Get the description string and find the ending */
        p = strstr(gBuf, Token);
        pp = strchr(p, '\n');
        if (!pp) pp = strchr(p, '\r');

        /* Terminate the description string here */
        if (pp) *pp = 0;

        /* Now allocate the description */
        Description = malloc(sizeof(KEYNAME));
        if (!Description)
        {
            /* Fail */
            printf("Unable to allocate the KEYNAME struct (out of memory?).\n");
            exit(1);
        }

        /* Fill out the structure */
        Description->Code = LanguageCode;
        Description->Name = strdup(p);
        Description->Next = NULL;

        /* Debug only */
        DPRINT1("LANGID: [%4x] Description: [%s]\n", Description->Code, Description->Name);

        /* Point to it and advance the pointer */
        *DescriptionData = Description;
        DescriptionData = &Description->Next;
    }

    /* We are done */
    return KeyWord;
}

ULONG
DoLANGUAGENAMES(IN PKEYNAME* LanguageData)
{
    ULONG KeyWord = 0;
    CHAR Token[32];
    ULONG LanguageCode;
    PCHAR p, pp;
    PKEYNAME Language;

    /* Assume nothing */
    *LanguageData = 0;

    /* Start scanning */
    while (NextLine(gBuf, 256, gfpInput))
    {
        /* Search for token */
        if (sscanf(gBuf, "%s", Token) != 1) continue;

        /* Make sure it's not just a comment */
        if (*Token == ';') continue;

        /* Make sure it's not a keyword */
        KeyWord = isKeyWord(Token);
        if (KeyWord < KEYWORD_COUNT) break;

        /* Now scan for the language code */
        if (sscanf(Token, " %4x", &LanguageCode) != 1)
        {
            /* Skip */
            printf("An invalid LANGID was specified.\n");
            continue;
        }

        /* Now get the actual language */
        if (sscanf(gBuf, " %*4x %s[^\n]", Token) != 1)
        {
            /* Skip */
            printf("A language name is missing\n");
            continue;
        }

        /* Get the language string and find the ending */
        p = strstr(gBuf, Token);
        pp = strchr(p, '\n');
        if (!pp) pp = strchr(p, '\r');

        /* Terminate the language string here */
        if (pp) *pp = 0;

        /* Now allocate the language */
        Language = malloc(sizeof(KEYNAME));
        if (!Language)
        {
            /* Fail */
            printf("Unable to allocate the KEYNAME struct (out of memory?).\n");
            exit(1);
        }

        /* Fill out the structure */
        Language->Code = LanguageCode;
        Language->Name = strdup(p);
        Language->Next = NULL;

        /* Debug only */
        DPRINT1("LANGID: [%4x] Name: [%s]\n", Language->Code, Language->Name);

        /* Point to it and advance the pointer */
        *LanguageData = Language;
        LanguageData = &Language->Next;
    }

    /* We are done */
    return KeyWord;
}

ULONG
DoKEYNAME(IN PKEYNAME* KeyNameData)
{
    ULONG KeyWord = 0;
    CHAR Token[32];
    ULONG CharacterCode;
    PCHAR p, pp;
    PKEYNAME KeyName;

    /* Assume nothing */
    *KeyNameData = 0;

    /* Start scanning */
    while (NextLine(gBuf, 256, gfpInput))
    {
        /* Search for token */
        if (sscanf(gBuf, "%s", Token) != 1) continue;

        /* Make sure it's not just a comment */
        if (*Token == ';') continue;

        /* Make sure it's not a keyword */
        KeyWord = isKeyWord(Token);
        if (KeyWord < KEYWORD_COUNT) break;

        /* Now scan for the character code */
        if (sscanf(Token, " %4x", &CharacterCode) != 1)
        {
            /* Skip */
            printf("An invalid character code was specified.\n");
            continue;
        }

        /* Now get the actual key name */
        if (sscanf(gBuf, " %*4x %s[^\n]", Token) != 1)
        {
            /* Skip */
            printf("A key name is missing\n");
            continue;
        }

        /* Get the key name string and find the ending */
        p = strstr(gBuf, Token);
        pp = strchr(p, '\n');
        if (!pp) pp = strchr(p, '\r');

        /* Terminate the key name string here */
        if (pp) *pp = 0;

        /* Now allocate the language */
        KeyName = malloc(sizeof(KEYNAME));
        if (!KeyName)
        {
            /* Fail */
            printf("Unable to allocate the KEYNAME struct (out of memory?).\n");
            exit(1);
        }

        /* Fill out the structure */
        KeyName->Code = CharacterCode;
        KeyName->Name = strdup(p);
        KeyName->Next = NULL;

        /* Debug only */
        DPRINT1("CHARCODE: [%4x] Name: [%s]\n", KeyName->Code, KeyName->Name);

        /* Point to it and advance the pointer */
        *KeyNameData = KeyName;
        KeyNameData = &KeyName->Next;
    }

    /* We are done */
    return KeyWord;
}

ULONG
DoSHIFTSTATE(IN PULONG StateCount,
             IN OUT PULONG ShiftStates)
{
    ULONG KeyWord;
    ULONG i;
    ULONG ShiftState;
    CHAR Token[32];

    /* Reset the shift states */
    for (i = 0; i < 8; i++) ShiftStates[i] = -1;

    /* Start with no states */
    *StateCount = 0;

    /* Scan for shift states */
    while (NextLine(gBuf, 256, gfpInput))
    {
        /* Search for token */
        if (sscanf(gBuf, "%s", Token) != 1) continue;

        /* Make sure it's not a keyword */
        KeyWord = isKeyWord(Token);
        if (KeyWord < KEYWORD_COUNT) break;

        /* Now scan for the shift state */
        if (sscanf(gBuf, " %1s[012367]", Token) != 1)
        {
            /* We failed -- should we warn? */
            if (Verbose) printf("An invalid shift state '%s' was found (use 0, 1, 2, 3, 6, or 7.)\n", Token);
            continue;
        }

        /* Now read the state */
        ShiftState = atoi(Token);

        /* Scan existing states */
        for (i = 0; i < *StateCount; i++)
        {
            /* Check for duplicate */
            if ((ShiftStates[i] == ShiftState) && (Verbose))
            {
                /* Warn user */
                printf("The state '%d' was duplicated for this Virtual Key.\n", ShiftStates[i]);
                break;
            }
        }

        /* Make sure we won't overflow */
        if (*StateCount < 8)
        {
            /* Save this state */
            ShiftStates[(*StateCount)++] = ShiftState;
        }
        else
        {
            /* Too many states -- should we warn? */
            if (Verbose) printf("There were too many states (you defined %d).\n", *StateCount);
        }
    }

    /* Debug only */
    DPRINT1("Found %d Shift States: [", *StateCount);
    for (i = 0; i < *StateCount; i++) DPRINT1("%d ", ShiftStates[i]);
    DPRINT1("]\n");

    /* We are done */
    return KeyWord;
}

ULONG
DoLIGATURE(PVOID LigatureData)
{
    printf("LIGATURE support is not yet implemented. Please bug Arch to fix it\n");
    return SkipLines();
}

ULONG
DoATTRIBUTES(PVOID AttributeData)
{
    printf("ATTRIBUTES support is not yet implemented. Please bug Arch to fix it\n");
    return SkipLines();
}

ULONG
DoMODIFIERS(VOID)
{
    printf("MODIFIERS support is not yet implemented. Please bug Arch to fix it\n");
    return SkipLines();
}

ULONG
DoDEADKEY(PVOID DeadKeyData)
{
    printf("DEADKEY support is not yet implemented. Please bug Arch to fix it\n");
    return SkipLines();
}

ULONG
DoLAYOUT(IN PLAYOUT LayoutData,
         IN PVOID LigatureData,
         IN PULONG ShiftStates,
         IN ULONG StateCount)
{
    CHAR Token[32];
    CHAR Cap[8];
    ULONG KeyWord;
    ULONG ScanCode, CurrentCode;
    ULONG TokenCount;
    ULONG VirtualKey;
    ULONG i;
    ULONG Count;
    BOOLEAN FullEntry;
    CHAR State[8][8];
    ULONG ScanCodeCount = -1;
    PLAYOUTENTRY Entry;
    UCHAR CharacterType;
    CHAR LigatureChar;

    /* Zero out the layout */
    memset(LayoutData, 0, sizeof(LAYOUT));

    /* Read each line */
    Entry = &LayoutData->Entry[0];
    while (NextLine(gBuf, 256, gfpInput))
    {
        /* Search for token */
        if (sscanf(gBuf, "%s", Token) != 1) continue;

        /* Make sure it's not just a comment */
        if (*Token == ';') continue;

        /* Make sure it's not a keyword */
        KeyWord = isKeyWord(Token);
        if (KeyWord < KEYWORD_COUNT) break;

        /* Now read the entry */
        TokenCount = sscanf(gBuf, " %x %s %s", &ScanCode, Token, Cap);
        if (TokenCount == 3)
        {
            /* Full entry with cap */
            FullEntry = TRUE;
        }
        else if (TokenCount != 2)
        {
            /* Fail, invalid LAYOUT entry */
            printf("There are not enough columns in the layout list.\n");
            exit(1);
        }
        else
        {
            /* Simplified layout with no cap */
            FullEntry = FALSE;
        }

        /* One more */
        DPRINT1("RAW ENTRY: [%x %s %s]\n", ScanCode, Token, Cap);
        Entry++;
        if (++ScanCodeCount >= 110)
        {
            /* Too many! */
            printf("ScanCode %02x - too many scancodes here to parse.\n", ScanCode);
            exit(1);
        }

        /* Fill out this entry */
        Entry->ScanCode = ScanCode;
        Entry->LineCount = gLineCount;

        /* Loop scancode table */
        for (i = 0; i < 110; i++)
        {
            /* Get the current code */
            CurrentCode = ScVk[i].ScanCode;
            if (CurrentCode == 0xFFFF)
            {
                /* New code */
                if (Verbose) printf("A new scancode is being defined: 0x%2X, %s\n", Entry->ScanCode, Token);

                /* Fill out the entry */
                Entry->VirtualKey = getVKNum(Token);
                break;
            }
            else if (ScanCode == CurrentCode)
            {
                /* Make sure we didn't already process it */
                if (ScVk[i].Processed)
                {
                    /* Fail */
                    printf("Scancode %X was previously defined.\n", ScanCode);
                    exit(1);
                }

                /* Check if there is a valid virtual key */
                if (ScVk[i].VirtualKey == 0xFFFF)
                {
                    /* Fail */
                    printf("The Scancode you tried to use (%X) is reserved.\n", ScanCode);
                    exit(1);
                }

                /* Fill out the entry */
                Entry->OriginalVirtualKey = ScVk[i].VirtualKey;
                Entry->Name = ScVk[i].Name;
                break;
            }
        }

        /* The entry is now processed */
        Entry->Processed = TRUE;
        ScVk[i].Processed = TRUE;

        /* Get the virtual key from the entry */
        VirtualKey = getVKNum(Token);
        Entry->VirtualKey = VirtualKey;
        DPRINT1("ENTRY: [%x %x %x %s] with ",
                Entry->VirtualKey, Entry->OriginalVirtualKey, Entry->ScanCode, Entry->Name);

        /* Make sure it's valid */
        if (VirtualKey == 0xFFFF)
        {
            /* Warn the user */
            if (Verbose) printf("An invalid Virtual Key '%s' was defined.\n", Token);
            continue;
        }

        /* Is this a full entry */
        if (FullEntry)
        {
            /* Do we have SGCAP data? Set cap mode to 2 */
            if (!strcmp(Cap, "SGCAP")) *Cap = '2';

            /* Read the cap mode */
            if (sscanf(Cap, "%1d[012]", &Entry->Cap) != 1)
            {
                /* Invalid cap mode */
                printf("invalid Cap specified (%s). Must be 0, 1, or 2.\n", Cap);
                exit(1);
            }
        }

        /* Read the states */
        Count = sscanf(gBuf,
                       " %*s %*s %*s %s %s %s %s %s %s %s %s",
                       State[0],
                       State[1],
                       State[2],
                       State[3],
                       State[4],
                       State[5],
                       State[6],
                       State[7]);
        Entry->StateCount = Count;
        DPRINT1("%d STATES: [", Count);

        /* Check if there are less than 2 states */
        if ((Count < 2) && (FullEntry))
        {
            /* Fail */
            printf("You must have at least 2 characters.\n");
            exit(1);
        }

        /* Loop all states */
        for (i = 0; i < Count; i++)
        {
            /* Check if this is an undefined state */
            DPRINT1("%s ", State[i]);
            if (!strcmp(State[i], "-1"))
            {
                /* No data for this state */
                Entry->CharData[i] = -1;
                continue;
            }

            /* Otherwise, check what kind of character this is */
            CharacterType = getCharacterInfo(State[i],
                                             &Entry->CharData[i],
                                             &LigatureChar);
            if (CharacterType == CHAR_DEAD_KEY)
            {
                /* Save it as such */
                Entry->DeadCharData[i] = 1;
            }
            else if (CharacterType == CHAR_OTHER_KEY)
            {
                /* Save it as such */
                Entry->OtherCharData[i] = 1;
            }
        }

        /* Check for sanity checks */
        DPRINT1("]\n");
        if (SanityCheck)
        {
            /* Not yet handled... */
            printf("Sanity checks not yet handled!\n");
            exit(1);
        }

        /* Check if we had SGCAP data */
        if (Entry->Cap & 2)
        {
            /* Not yet handled... */
            printf("SGCAP state not yet handled!\n");
            exit(1);
        }
    }
    
    /* Check if we have found any ScanCode in the file */

    if (ScanCodeCount == -1)
    {
        printf("No ScanCode found!\n");
        exit(1);
    }

    /* Process the scan code table */
    Entry = &LayoutData->Entry[ScanCodeCount];
    for (i = 0; i < 110; i++)
    {
        /* Get the scan code */
        CurrentCode = ScVk[i].ScanCode;
        if (CurrentCode == 0xFFFF) break;

        /* Check if this entry had been processed */
        if (ScVk[i].Processed)
        {
            /* Skip it */
            ScVk[i].Processed = FALSE;
        }
        else
        {
            /* Do we have too many? */
            if (++ScanCodeCount >= 110)
            {
                /* Fail */
                printf("ScanCode %02x - too many scancodes here to parse.\n", CurrentCode);
                exit(1);
            }

            /* Build an entry for it */
            Entry++;
            Entry->ScanCode = CurrentCode;
            Entry->VirtualKey = ScVk[i].VirtualKey;
            Entry->OriginalVirtualKey = ScVk[i].VirtualKey;
            Entry->Name = ScVk[i].Name;
            Entry->Processed = TRUE;
            Entry->LineCount = 0;
            DPRINT1("AUTOMATIC ENTRY: [%x %x %s]\n",
                    Entry->VirtualKey, Entry->ScanCode, Entry->Name);
        }
    }

    /* Skip what's left */
    return KeyWord;
}

ULONG
DoParsing(VOID)
{
    ULONG KeyWords[KEYWORD_COUNT];
    ULONG KeyWord;
    ULONG StateCount;
    ULONG ShiftStates[8];
    PKEYNAME DescriptionData = NULL, LanguageData = NULL;
    PKEYNAME KeyNameData = NULL, KeyNameExtData = NULL, KeyNameDeadData = NULL;
    PVOID AttributeData = NULL, LigatureData = NULL, DeadKeyData = NULL;

    /* Parse keywords */
    gLineCount = 0;
    KeyWord = SkipLines();
    if (KeyWord >= KEYWORD_COUNT)
    {
        /* Invalid keyword count, fail */
        fclose(gfpInput);
        printf("No keywords were found in '%s'.\n", gpszFileName);
        exit(1);
    }

    /* Now parse the keywords */
    memset(KeyWords, 0, sizeof(KeyWords));
    while (KeyWord < (KEYWORD_COUNT - 1))
    {
        /* Save this keyword */
        KeyWords[KeyWord]++;

        /* Check for duplicate entires, other than DEADKEY, which is okay */
        if ((KeyWord != 9) && (KeyWords[KeyWord] > 1) && (Verbose))
        {
            /* On a verbose run, warn the user */
            printf("The '%s' keyword appeared multiple times.\n",
                   KeyWordList[KeyWord]);
        }

        /* Now parse this keyword */
        switch (KeyWord)
        {
            /* KBD */
            case 0:

                DPRINT1("Found KBD section\n");
                KeyWord = DoKBD();
                break;

            /* VERSION */
            case 1:

                DPRINT1("Found VERSION section\n");
                KeyWord = DoVERSION();
                break;

            /* COPYRIGHT */
            case 2:

                DPRINT1("Found COPYRIGHT section\n");
                KeyWord = DoCOPYRIGHT();
                break;

            /* COMPANY */
            case 3:

                DPRINT1("Found COMPANY section\n");
                KeyWord = DoCOMPANY();
                break;

            /* LOCALENAME */
            case 4:

                DPRINT1("Found LOCALENAME section\n");
                KeyWord = DoLOCALENAME();
                break;

            /* MODIFIERS */
            case 5:

                DPRINT1("Found MODIFIERS section\n");
                KeyWord = DoMODIFIERS();
                break;

            /* SHIFTSTATE */
            case 6:

                DPRINT1("Found SHIFTSTATE section\n");
                KeyWord = DoSHIFTSTATE(&StateCount, ShiftStates);
                if (StateCount < 2)
                {
                    /* Fail */
                    fclose(gfpInput);
                    printf("ERROR");
                    exit(1);
                }
                break;

            /* ATTRIBUTES */
            case 7:

                DPRINT1("Found ATTRIBUTES section\n");
                KeyWord = DoATTRIBUTES(&AttributeData);
                break;

            /* LAYOUT */
            case 8:

                DPRINT1("Found LAYOUT section\n");
                KeyWord = DoLAYOUT(&g_Layout,
                                   &LigatureData,
                                   ShiftStates,
                                   StateCount);
                break;

            /* DEADKEY */
            case 9:

                DPRINT1("Found DEADKEY section\n");
                KeyWord = DoDEADKEY(&DeadKeyData);
                break;

            /* LIGATURE */
            case 10:

                DPRINT1("Found LIGATURE section\n");
                KeyWord = DoLIGATURE(&LigatureData);
                break;

            /* KEYNAME */
            case 11:

                DPRINT1("Found KEYNAME section\n");
                KeyWord = DoKEYNAME(&KeyNameData);
                break;

            /* KEYNAME_EXT */
            case 12:

                DPRINT1("Found KEYNAME_EXT section\n");
                KeyWord = DoKEYNAME(&KeyNameExtData);
                break;

            /* KEYNAME_DEAD */
            case 13:

                DPRINT1("Found KEYNAME_DEAD section\n");
                KeyWord = DoKEYNAME(&KeyNameDeadData);
                break;

            /* DESCRIPTIONS */
            case 14:

                DPRINT1("Found DESCRIPTIONS section\n");
                KeyWord = DoDESCRIPTIONS(&DescriptionData);
                break;

            /* LANGUAGENAMES */
            case 15:

                DPRINT1("Found LANGUAGENAMES section\n");
                KeyWord = DoLANGUAGENAMES(&LanguageData);
                break;

            /* ENDKBD */
            case 16:

                DPRINT1("Found ENDKBD section\n");
                KeyWord = SkipLines();
                break;


            default:
                break;
        }
    }

    /* We are done */
    fclose(gfpInput);

    /* Now enter the output phase */
    return DoOutput(StateCount,
                    ShiftStates,
                    DescriptionData,
                    LanguageData,
                    AttributeData,
                    DeadKeyData,
                    LigatureData,
                    KeyNameData,
                    KeyNameExtData,
                    KeyNameDeadData);
}
/* EOF */