552 lines
13 KiB
C
552 lines
13 KiB
C
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
mclex.c
|
|
|
|
Abstract:
|
|
|
|
This file contains the input lexer for the Win32 Message Compiler (MC)
|
|
|
|
Author:
|
|
|
|
Steve Wood (stevewo) 21-Aug-1991
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "mc.h"
|
|
|
|
#define MAXLINELENGTH 8192
|
|
|
|
WCHAR LineBuffer[ MAXLINELENGTH ];
|
|
WCHAR *CurrentChar;
|
|
BOOL ReturnCurrentToken;
|
|
|
|
PNAME_INFO KeywordNames;
|
|
|
|
typedef struct _COMMENT_INFO {
|
|
struct _COMMENT_INFO *Next;
|
|
WCHAR Text[ 1 ];
|
|
} COMMENT_INFO, *PCOMMENT_INFO;
|
|
|
|
PCOMMENT_INFO Comments, CurrentComment;
|
|
|
|
BOOL
|
|
McInitLexer( void )
|
|
{
|
|
ReturnCurrentToken = FALSE;
|
|
McAddName( &KeywordNames, L"MessageIdTypedef", MCTOK_MSGIDTYPE_KEYWORD, NULL );
|
|
McAddName( &KeywordNames, L"SeverityNames", MCTOK_SEVNAMES_KEYWORD, NULL );
|
|
McAddName( &KeywordNames, L"FacilityNames", MCTOK_FACILITYNAMES_KEYWORD, NULL );
|
|
McAddName( &KeywordNames, L"LanguageNames", MCTOK_LANGNAMES_KEYWORD, NULL );
|
|
McAddName( &KeywordNames, L"MessageId", MCTOK_MESSAGEID_KEYWORD, NULL );
|
|
McAddName( &KeywordNames, L"Severity", MCTOK_SEVERITY_KEYWORD, NULL );
|
|
McAddName( &KeywordNames, L"Facility", MCTOK_FACILITY_KEYWORD, NULL );
|
|
McAddName( &KeywordNames, L"SymbolicName", MCTOK_SYMBOLNAME_KEYWORD, NULL );
|
|
McAddName( &KeywordNames, L"Language", MCTOK_LANGUAGE_KEYWORD, NULL );
|
|
McAddName( &KeywordNames, L"OutputBase", MCTOK_OUTBASE_KEYWORD, NULL );
|
|
McAddName( &KeywordNames, L"MessageIdTypedefMacro", MCTOK_MSGTYPEDEF_KEYWORD, NULL );
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOL
|
|
McOpenInputFile( void )
|
|
{
|
|
char *PatchExt, *s, *FilePart;
|
|
BOOL Result;
|
|
|
|
PatchExt = NULL;
|
|
s = MessageFileName + strlen( MessageFileName );
|
|
FilePart = MessageFileName;
|
|
// while (--s > MessageFileName) {
|
|
while ((s = CharPrev(MessageFileName, s)) > MessageFileName) {
|
|
if (*s == '.' && PatchExt == NULL) {
|
|
PatchExt = s;
|
|
*PatchExt = '\0';
|
|
} else
|
|
if (*s == ':' || *s == '\\' || *s == '/') {
|
|
FilePart = s+1;
|
|
break;
|
|
}
|
|
}
|
|
MessageFileNameNoExt = malloc( strlen( FilePart ) + 1 );
|
|
if (!MessageFileNameNoExt) {
|
|
McInputErrorA( "Out of memory capturing file name", TRUE, NULL );
|
|
return FALSE;
|
|
}
|
|
|
|
strcpy( MessageFileNameNoExt, FilePart );
|
|
|
|
strcat( DebugFileName, MessageFileNameNoExt );
|
|
strcat( DebugFileName, ".dbg" );
|
|
strcat( HeaderFileName, MessageFileNameNoExt );
|
|
strcat( HeaderFileName, "." );
|
|
strcat( HeaderFileName, HeaderFileExt );
|
|
strcat( RcInclFileName, MessageFileNameNoExt );
|
|
strcat( RcInclFileName, ".rc" );
|
|
|
|
if (PatchExt == NULL) {
|
|
strcat( MessageFileName, ".mc" );
|
|
} else {
|
|
*PatchExt = '.';
|
|
}
|
|
|
|
MessageFileLineNumber = 0;
|
|
LineBuffer[ 0 ] = L'\0';
|
|
CurrentChar = NULL;
|
|
|
|
Result = FALSE;
|
|
MessageFile = fopen( MessageFileName, "rb" );
|
|
if (MessageFile == NULL) {
|
|
McInputErrorA( "unable to open input file", TRUE, NULL );
|
|
} else {
|
|
|
|
if (GenerateDebugFile) {
|
|
DebugFile = fopen( DebugFileName, "wb" );
|
|
if (DebugFile == NULL) {
|
|
McInputErrorA( "unable to open output file - %s", TRUE, DebugFileName );
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
HeaderFile = fopen( HeaderFileName, "wb" );
|
|
if (HeaderFile == NULL) {
|
|
McInputErrorA( "unable to open output file - %s", TRUE, HeaderFileName );
|
|
} else {
|
|
RcInclFile = fopen( RcInclFileName, "wb" );
|
|
if (RcInclFile == NULL) {
|
|
McInputErrorA( "unable to open output file - %s", TRUE, RcInclFileName );
|
|
} else {
|
|
Result = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
fail:
|
|
if (!Result) {
|
|
McCloseInputFile();
|
|
McCloseOutputFiles(Result);
|
|
}
|
|
|
|
return( Result );
|
|
}
|
|
|
|
|
|
void
|
|
McCloseInputFile( void )
|
|
{
|
|
if (MessageFile != NULL) {
|
|
fclose( MessageFile );
|
|
MessageFile = NULL;
|
|
CurrentChar = NULL;
|
|
LineBuffer[ 0 ] = L'\0';
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
McClearArchiveBit( LPSTR Name )
|
|
{
|
|
DWORD Attributes;
|
|
|
|
Attributes = GetFileAttributes(Name);
|
|
if (Attributes != -1 && (Attributes & FILE_ATTRIBUTE_ARCHIVE)) {
|
|
SetFileAttributes(Name, Attributes & ~FILE_ATTRIBUTE_ARCHIVE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
McCloseOutputFiles(
|
|
BOOL Success
|
|
)
|
|
{
|
|
if (DebugFile != NULL) {
|
|
fclose( DebugFile );
|
|
if (!Success) {
|
|
_unlink(DebugFileName);
|
|
} else {
|
|
McClearArchiveBit(DebugFileName);
|
|
}
|
|
}
|
|
|
|
if (HeaderFile != NULL) {
|
|
fclose( HeaderFile );
|
|
if (!Success) {
|
|
_unlink(HeaderFileName);
|
|
} else {
|
|
McClearArchiveBit(HeaderFileName);
|
|
}
|
|
}
|
|
|
|
if (RcInclFile != NULL) {
|
|
fclose( RcInclFile );
|
|
if (!Success) {
|
|
_unlink(RcInclFileName);
|
|
} else {
|
|
McClearArchiveBit(RcInclFileName);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
McInputErrorA(
|
|
char *Message,
|
|
BOOL Error,
|
|
PVOID Argument
|
|
)
|
|
{
|
|
if (Error) {
|
|
InputErrorCount += 1;
|
|
}
|
|
|
|
fprintf( stderr,
|
|
"%s(%d) : %s : ",
|
|
MessageFileName,
|
|
MessageFileLineNumber,
|
|
Error ? "error" : "warning"
|
|
);
|
|
|
|
fprintf( stderr, Message, Argument );
|
|
fprintf( stderr, "\n" );
|
|
}
|
|
|
|
|
|
void
|
|
McInputErrorW(
|
|
WCHAR *Message,
|
|
BOOL Error,
|
|
PVOID Argument
|
|
)
|
|
{
|
|
WCHAR buffer[ 256 * 2 ];
|
|
|
|
fprintf( stderr,
|
|
"%s(%d) : %s : ",
|
|
MessageFileName,
|
|
MessageFileLineNumber,
|
|
Error ? "error" : "warning"
|
|
);
|
|
|
|
if (Error) {
|
|
InputErrorCount += 1;
|
|
}
|
|
|
|
swprintf( buffer, Message, Argument );
|
|
wcscat( buffer, L"\n" );
|
|
|
|
if (UnicodeInput) {
|
|
DWORD dwMode;
|
|
DWORD cbWritten;
|
|
HANDLE fh;
|
|
|
|
fh = (HANDLE) _get_osfhandle( _fileno( stderr ) );
|
|
if (GetConsoleMode( fh, &dwMode ))
|
|
WriteConsoleW( fh, buffer, wcslen( buffer ), &cbWritten, NULL );
|
|
else
|
|
fwprintf( stderr, buffer );
|
|
} else {
|
|
BYTE chBuf[ 256 * 2 ];
|
|
|
|
memset( chBuf, 0, sizeof( chBuf ) );
|
|
WideCharToMultiByte( CP_OEMCP, 0, buffer, -1, chBuf, sizeof(chBuf), NULL, NULL );
|
|
fprintf( stderr, chBuf );
|
|
}
|
|
}
|
|
|
|
|
|
WCHAR *
|
|
McGetLine( void )
|
|
{
|
|
WCHAR *s;
|
|
|
|
if (MessageFile == NULL || feof( MessageFile )) {
|
|
return( NULL );
|
|
}
|
|
|
|
if (fgetsW( LineBuffer,
|
|
(sizeof( LineBuffer ) / sizeof( WCHAR )) - 1,
|
|
MessageFile ) == NULL) {
|
|
return( NULL );
|
|
}
|
|
|
|
s = LineBuffer + wcslen( LineBuffer );
|
|
if (s > LineBuffer && *--s == L'\n') {
|
|
if (s > LineBuffer && *--s != L'\r') {
|
|
*++s = L'\r';
|
|
*++s = L'\n';
|
|
*++s = L'\0';
|
|
}
|
|
}
|
|
|
|
MessageFileLineNumber++;
|
|
return( CurrentChar = LineBuffer );
|
|
}
|
|
|
|
|
|
void
|
|
McSkipLine( void )
|
|
{
|
|
CurrentChar = NULL;
|
|
}
|
|
|
|
|
|
WCHAR
|
|
McGetChar(
|
|
BOOL SkipWhiteSpace
|
|
)
|
|
{
|
|
BOOL SawWhiteSpace;
|
|
BOOL SawNewLine;
|
|
PCOMMENT_INFO p;
|
|
|
|
SawWhiteSpace = FALSE;
|
|
|
|
tryagain:
|
|
SawNewLine = FALSE;
|
|
if (CurrentChar == NULL) {
|
|
McGetLine();
|
|
if (CurrentChar == NULL) {
|
|
return( L'\0' );
|
|
}
|
|
|
|
SawNewLine = TRUE;
|
|
}
|
|
|
|
if (SkipWhiteSpace) {
|
|
while (*CurrentChar <= L' ') {
|
|
SawWhiteSpace = TRUE;
|
|
if (!*CurrentChar++) {
|
|
CurrentChar = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SawNewLine) {
|
|
if (CurrentChar != NULL) {
|
|
|
|
/* Check for Byte Order Mark during Unicode input */
|
|
if (UnicodeInput) {
|
|
if (*CurrentChar == 0xFEFF) {
|
|
CurrentChar++;
|
|
}
|
|
}
|
|
|
|
if (*CurrentChar == MCCHAR_END_OF_LINE_COMMENT) {
|
|
p = malloc( sizeof( *p ) + wcslen( ++CurrentChar ) * sizeof( WCHAR ));
|
|
if (!p) {
|
|
McInputErrorA( "Out of memory reading chars", TRUE, NULL );
|
|
return 0;
|
|
}
|
|
p->Next = NULL;
|
|
wcscpy( 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( L' ' );
|
|
} else {
|
|
return( *CurrentChar++ );
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
McFlushComments( void )
|
|
{
|
|
PCOMMENT_INFO p;
|
|
|
|
while (p = Comments) {
|
|
fprintf( HeaderFile, "%ws", p->Text );
|
|
|
|
Comments = Comments->Next;
|
|
free( p );
|
|
}
|
|
Comments = NULL;
|
|
CurrentComment = NULL;
|
|
|
|
fflush( HeaderFile );
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
McUnGetChar(
|
|
WCHAR c
|
|
)
|
|
{
|
|
if (CurrentChar > LineBuffer) {
|
|
*--CurrentChar = c;
|
|
} else {
|
|
LineBuffer[ 0 ] = c;
|
|
LineBuffer[ 1 ] = L'\0';
|
|
CurrentChar = LineBuffer;
|
|
}
|
|
}
|
|
|
|
|
|
unsigned int
|
|
McGetToken(
|
|
BOOL KeywordExpected
|
|
)
|
|
{
|
|
WCHAR c, *dst;
|
|
|
|
if (ReturnCurrentToken) {
|
|
ReturnCurrentToken = FALSE;
|
|
if (Token == MCTOK_NAME && KeywordExpected) {
|
|
TokenKeyword = McFindName( KeywordNames, TokenCharValue );
|
|
if (TokenKeyword == NULL) {
|
|
McInputErrorW( L"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 = L'\0';
|
|
TokenNumericValue = 0L;
|
|
|
|
while (TRUE) {
|
|
c = McGetChar( (BOOL)(Token == MCTOK_END_OF_FILE) );
|
|
if (Token == MCTOK_NUMBER) {
|
|
if (iswdigit( c ) ||
|
|
c == L'x' ||
|
|
(c >= L'a' && c <= L'f') ||
|
|
(c >= L'A' && c <= L'F')
|
|
) {
|
|
*dst++ = c;
|
|
} else {
|
|
McUnGetChar( c );
|
|
*dst = L'\0';
|
|
|
|
if (!McCharToInteger( TokenCharValue, 0, &TokenNumericValue )) {
|
|
McInputErrorW( L"invalid number - %s", TRUE, TokenCharValue );
|
|
Token = MCTOK_END_OF_FILE;
|
|
} else {
|
|
return( Token );
|
|
}
|
|
}
|
|
} else
|
|
if (Token == MCTOK_NAME) {
|
|
if (iswcsym( c )) {
|
|
*dst++ = c;
|
|
} else {
|
|
McUnGetChar( c );
|
|
*dst = L'\0';
|
|
|
|
if (KeywordExpected) {
|
|
TokenKeyword = McFindName( KeywordNames, TokenCharValue );
|
|
if (TokenKeyword == NULL) {
|
|
McInputErrorW( L"expected keyword - %s", TRUE, TokenCharValue );
|
|
Token = MCTOK_END_OF_FILE;
|
|
} else {
|
|
Token = (unsigned int)TokenKeyword->Id;
|
|
}
|
|
}
|
|
return( Token );
|
|
}
|
|
} else
|
|
if (iswdigit( c )) {
|
|
*dst++ = c;
|
|
Token = MCTOK_NUMBER;
|
|
} else
|
|
if (iswcsymf( c )) {
|
|
*dst++ = c;
|
|
Token = MCTOK_NAME;
|
|
} else
|
|
if (c == L'=') {
|
|
*dst++ = c;
|
|
*dst = L'\0';
|
|
Token = MCTOK_EQUAL;
|
|
return( Token );
|
|
} else
|
|
if (c == L'(') {
|
|
*dst++ = c;
|
|
*dst = L'\0';
|
|
Token = MCTOK_LEFT_PAREN;
|
|
return( Token );
|
|
} else
|
|
if (c == L')') {
|
|
*dst++ = c;
|
|
*dst = L'\0';
|
|
Token = MCTOK_RIGHT_PAREN;
|
|
return( Token );
|
|
} else
|
|
if (c == L':') {
|
|
*dst++ = c;
|
|
*dst = L'\0';
|
|
Token = MCTOK_COLON;
|
|
return( Token );
|
|
} else
|
|
if (c == L'+') {
|
|
*dst++ = c;
|
|
*dst = L'\0';
|
|
Token = MCTOK_PLUS;
|
|
return( Token );
|
|
} else
|
|
if (c == L' ') {
|
|
} else
|
|
if (c == MCCHAR_END_OF_LINE_COMMENT) {
|
|
Token = MCTOK_END_OF_LINE_COMMENT;
|
|
wcscpy( TokenCharValue, CurrentChar );
|
|
CurrentChar = NULL;
|
|
return( Token );
|
|
} else
|
|
if (c == L'\0') {
|
|
return( Token );
|
|
} else {
|
|
McInputErrorW( L"invalid character (0x%02x)", TRUE, (PVOID)UlongToPtr((UCHAR)c) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
McUnGetToken( void )
|
|
{
|
|
ReturnCurrentToken = TRUE;
|
|
}
|
|
|
|
WCHAR *
|
|
McSkipWhiteSpace(
|
|
WCHAR *s
|
|
)
|
|
{
|
|
while (*s <= L' ') {
|
|
if (!*s++) {
|
|
s = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return( s );
|
|
}
|