/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    mclex.c

Abstract:

    This file contains the input lexer for the Win32 Message Compiler (MC)

--*/


#include "mc.h"

char LineBuffer[ 256 ];
char *CurrentChar;
BOOLEAN ReturnCurrentToken;

PNAME_INFO KeywordNames;

typedef struct _COMMENT_INFO {
    struct _COMMENT_INFO *Next;
    char Text[ 1 ];
} COMMENT_INFO, *PCOMMENT_INFO;

PCOMMENT_INFO Comments, CurrentComment;

/*++

Routine Description:

    This fills in the Key words associated with the Message file format

Return Value:

    TRUE

--*/



BOOLEAN
McInitLexer( void )
{
    ReturnCurrentToken = FALSE;
    McAddName( &KeywordNames, "MessageIdTypedef",   MCTOK_MSGIDTYPE_KEYWORD,  NULL );
    McAddName( &KeywordNames, "SeverityNames",      MCTOK_SEVNAMES_KEYWORD,   NULL );
    McAddName( &KeywordNames, "FacilityNames",      MCTOK_FACILITYNAMES_KEYWORD,  NULL );
    McAddName( &KeywordNames, "LanguageNames",      MCTOK_LANGNAMES_KEYWORD,  NULL );
    McAddName( &KeywordNames, "MessageId",          MCTOK_MESSAGEID_KEYWORD,  NULL );
    McAddName( &KeywordNames, "Severity",           MCTOK_SEVERITY_KEYWORD,   NULL );
    McAddName( &KeywordNames, "Facility",           MCTOK_FACILITY_KEYWORD,   NULL );
    McAddName( &KeywordNames, "SymbolicName",       MCTOK_SYMBOLNAME_KEYWORD, NULL );
    McAddName( &KeywordNames, "Language",           MCTOK_LANGUAGE_KEYWORD,   NULL );
    return( TRUE );
}



BOOLEAN
McOpenInputFile( void )
{
    char SavedChar, *s, *FileName;
    BOOLEAN Result;

    s = MessageFileName;
    FileName = s;
    SavedChar = '\0';
    while (*s) {
        if (*s == '.') {
            SavedChar = '.';
            *s = '\0';
            break;
            }

        if (*s == ':' || *s == '\\' || *s == '/') {
            FileName = s+1;
            }

        s++;
        }

    strcat( HeaderFileName, FileName );
    strcat( HeaderFileName, ".h" );
    strcat( RcInclFileName, FileName );
    strcat( RcInclFileName, ".rc" );

    if (SavedChar == '\0') {
        strcpy( s, ".mc" );
        }
    else {
        *s = SavedChar;
        }

    Result = FALSE;
    MessageFileLineNumber = 0;
    LineBuffer[ 0 ] = '\0';
    CurrentChar = NULL;

    MessageFile = fopen( MessageFileName, "rb" );
    if (MessageFile == NULL) {
        McInputError( "unable to open input file", TRUE, NULL );
        }
    else {
        HeaderFile = fopen( HeaderFileName, "wb" );
        if (HeaderFile == NULL) {
            McInputError( "unable to open output file - %s", TRUE, HeaderFileName );
            }
        else {
            RcInclFile = fopen( RcInclFileName, "wb" );
            if (RcInclFile == NULL) {
                McInputError( "unable to open output file - %s", TRUE, RcInclFileName );
                }
            else {
                Result = TRUE;
                }
            }
        }

    if (!Result) {
        McCloseInputFile();
        McCloseOutputFiles();
        }
    else {
        return( TRUE );
        }
}



void
McCloseInputFile( void )
{
    if (MessageFile != NULL) {
        fclose( MessageFile );
        MessageFile = NULL;
        CurrentChar = NULL;
        LineBuffer[ 0 ] = '\0';
        }
}



void
McCloseOutputFiles( void )
{
    if (HeaderFile != NULL) {
        fclose( HeaderFile );
        }

    if (RcInclFile != NULL) {
        fclose( RcInclFile );
        }
}


void
McInputError(
    char *Message,
    BOOLEAN Error,
    PVOID Argument
    )
{
    fprintf( stderr,
             "%s (%d) : %s: ",
             MessageFileName,
             MessageFileLineNumber,
             Error ? "Error" : "Warning"
           );

    fprintf( stderr, Message, Argument );
    fprintf( stderr, "\n" );
}


/*++

Routine Description:

   This retrieves the current line then moves down to the
   next line in the message file.

Return Value:

   Returns the current line of in the file.

--*/


char *
McGetLine( void )
{
    char *s;

    if (MessageFile == NULL || feof( MessageFile )) {
        return( NULL );
        }

    if (fgets( LineBuffer, sizeof( LineBuffer ), MessageFile ) == NULL) {
        return( NULL );
        }

    s = LineBuffer + strlen( LineBuffer );
    if (s > LineBuffer && *--s == '\n') {
        if (s > LineBuffer && *--s != '\r') {
            *++s = '\r';
            *++s = '\n';
            *++s = '\0';
            }
        }

    MessageFileLineNumber++;
    return( CurrentChar = LineBuffer );
}


void
McSkipLine( void )
{
    CurrentChar = NULL;
}


/*++

Routine Description:

   This retrieves the character at the current position of the line
   buffer then advances to the next position. If the end of the line
   is reached another line is retrieve. If the end of the file is reached
   this returns with a NULL character. One is optionally able to flush
   the white space from the line.

Arguments:

   A boolean specifying whether whitespace should be consider significant.

Return Value:

   Returns the character in the current line.

--*/




char
McGetChar(
    BOOLEAN SkipWhiteSpace
    )
{
    BOOLEAN SawWhiteSpace;
    BOOLEAN SawNewLine;
    PCOMMENT_INFO p;

    SawWhiteSpace = FALSE;

tryagain:
    SawNewLine = FALSE;
    if (CurrentChar == NULL) {
        McGetLine();
        if (CurrentChar == NULL) {
            return( '\0' );
            }

        SawNewLine = TRUE;
        }

    if (SkipWhiteSpace) {
        while (*CurrentChar <= ' ') {
            SawWhiteSpace = TRUE;
            if (!*CurrentChar++) {
                CurrentChar = NULL;
                break;
                }
            }
        }

    if (SawNewLine) {
        if (CurrentChar != NULL && *CurrentChar == MCCHAR_END_OF_LINE_COMMENT) {
            p = malloc( sizeof( *p ) + strlen( ++CurrentChar ) );
            p->Next = NULL;
            strcpy( p->Text, CurrentChar );
            if (CurrentComment == NULL) {
                Comments = p;
                }
            else {
                CurrentComment->Next = p;
                }
            CurrentComment = p;

            CurrentChar = NULL;
            }
        }

    if (CurrentChar == NULL && SkipWhiteSpace) {
        goto tryagain;
        }

    if (SawWhiteSpace) {
        return( ' ' );
        }
    else {
        return( *CurrentChar++ );
        }
}


void
McFlushComments( void )
{
    PCOMMENT_INFO p;

    while (p = Comments) {
        fprintf( HeaderFile, "%s", p->Text );

        Comments = Comments->Next;
        free( p );
        }
    Comments = NULL;
    CurrentComment = NULL;

    fflush( HeaderFile );
    return;
}


void
McUnGetChar(
    char c
    )
{
    if (CurrentChar > LineBuffer) {
        *--CurrentChar = c;
        }
    else {
        LineBuffer[ 0 ] = c;
        LineBuffer[ 1 ] = '\0';
        CurrentChar = LineBuffer;
        }
}


/*++

Routine Description:

    Breaks input line into "tokens values" as defined in MC.H.

Arguments:

    A boolean designating whether keywords are required.

Return Value:

   Returns the the token corresponding to the "token value" For example
   with a token of type MCTOK_NUMBER the value would be a string
   representation of an integer.

--*/


unsigned int
McGetToken(
    BOOLEAN KeywordExpected
    )
{
    char c, *dst;

    if (ReturnCurrentToken) {
        ReturnCurrentToken = FALSE;
        if (Token == MCTOK_NAME && KeywordExpected) {
            TokenKeyword = McFindName( KeywordNames, TokenCharValue );
            if (TokenKeyword == NULL) {
                McInputError( "expected keyword - %s", TRUE, TokenCharValue );
                Token = MCTOK_END_OF_FILE;
                }
            else {
                Token = (unsigned int)TokenKeyword->Id;
                }
            }

        return( Token );
        }

    Token = MCTOK_END_OF_FILE;
    dst = TokenCharValue;
    *dst = '\0';
    TokenNumericValue = 0L;

    while (TRUE) {
        c = McGetChar( (BOOLEAN)(Token == MCTOK_END_OF_FILE) );
        if (Token == MCTOK_NUMBER) {
            if (isdigit( c ) ||
                c == 'x' ||
                (c >= 'a' && c <= 'f') ||
                (c >= 'A' && c <= 'F')
               ) {
                *dst++ = c;
                }
            else {
                McUnGetChar( c );
                *dst = '\0';

                if (!McCharToInteger( TokenCharValue, 0, &TokenNumericValue )) {
                    McInputError( "invalid number - %s", TRUE, TokenCharValue );
                    Token = MCTOK_END_OF_FILE;
                    }
                else {
                    return( Token );
                    }
                }
            }
        else
        if (Token == MCTOK_NAME) {
            if (iscsym( c )) {
                *dst++ = c;
                }
            else {
                McUnGetChar( c );
                *dst = '\0';

                if (KeywordExpected) {
                    TokenKeyword = McFindName( KeywordNames, TokenCharValue );
                    if (TokenKeyword == NULL) {
                        McInputError( "expected keyword - %s", TRUE, TokenCharValue );
                        Token = MCTOK_END_OF_FILE;
                        }
                    else {
                        Token = (unsigned int)TokenKeyword->Id;
                        }
                    }
                return( Token );
                }
            }
        else
        if (isdigit( c )) {
            *dst++ = c;
            Token = MCTOK_NUMBER;
            }
        else
        if (iscsymf( c )) {
            *dst++ = c;
            Token = MCTOK_NAME;
            }
        else
        if (c == '=') {
            *dst++ = c;
            *dst = '\0';
            Token = MCTOK_EQUAL;
            return( Token );
            }
        else
        if (c == '(') {
            *dst++ = c;
            *dst = '\0';
            Token = MCTOK_LEFT_PAREN;
            return( Token );
            }
        else
        if (c == ')') {
            *dst++ = c;
            *dst = '\0';
            Token = MCTOK_RIGHT_PAREN;
            return( Token );
            }
        else
        if (c == ':') {
            *dst++ = c;
            *dst = '\0';
            Token = MCTOK_COLON;
            return( Token );
            }
        else
        if (c == '+') {
            *dst++ = c;
            *dst = '\0';
            Token = MCTOK_PLUS;
            return( Token );
            }
        else
        if (c == ' ') {
            }
        else
        if (c == MCCHAR_END_OF_LINE_COMMENT) {
            Token = MCTOK_END_OF_LINE_COMMENT;
            strcpy( TokenCharValue, CurrentChar );
            CurrentChar = NULL;
            return( Token );
            }
        else
        if (c == '\0') {
            return( Token );
            }
        else {
            McInputError( "invalid character '%c'", TRUE, (PVOID)(ULONG)(UCHAR)c );
            }
        }
}


void
McUnGetToken( void )
{
    ReturnCurrentToken = TRUE;
}

char *
McSkipWhiteSpace(
    char *s
    )
{
    while (*s <= ' ') {
        if (!*s++) {
            s = NULL;
            break;
            }
        }

    return( s );
}
