1380 lines
40 KiB
C
1380 lines
40 KiB
C
|
|
/****************************************************************************/
|
|
/* */
|
|
/* RCL.C - */
|
|
/* */
|
|
/* Windows 3.0 Resource Compiler - Lexical analyzer */
|
|
/* */
|
|
/* */
|
|
/****************************************************************************/
|
|
|
|
#include "rc.h"
|
|
|
|
|
|
#define EOLCHAR L';'
|
|
#define STRCHAR L'"'
|
|
#define CHRCHAR L'\''
|
|
#define SGNCHAR L'-'
|
|
#define iswhite( c ) ((c != SYMUSESTART) && (c != SYMDEFSTART) &&\
|
|
((WCHAR)c <= L' ') ? TRUE : FALSE)
|
|
|
|
static WCHAR curChar;
|
|
static WCHAR curCharFTB; /* Cur char From Token Buf */
|
|
static PWCHAR CurPtrTB;
|
|
static PFILE inpfh;
|
|
static int curLin, curCol;
|
|
|
|
extern BOOL bExternParse;
|
|
|
|
|
|
/* Must be sorted */
|
|
KEY keyList[] =
|
|
{
|
|
{ L"ALT", TKALT },
|
|
{ L"ASCII", TKASCII },
|
|
{ L"AUTO3STATE", TKAUTO3 },
|
|
{ L"AUTOCHECKBOX", TKAUTOCHECK },
|
|
{ L"AUTORADIOBUTTON", TKAUTORADIO },
|
|
{ L"BEGIN", BEGIN },
|
|
{ L"BEDIT", TKBEDIT },
|
|
{ L"BITMAP", TKBITMAP },
|
|
{ L"BLOCK", TKBLOCK },
|
|
{ L"BUTTON", TKBUTTON },
|
|
{ L"CAPTION", TKCAPTION },
|
|
{ L"CHARACTERISTICS", TKCHARACTERISTICS },
|
|
{ L"CHECKBOX", TKCHECKBOX },
|
|
{ L"CHECKED", TKCHECKED },
|
|
{ L"CLASS", TKCLASS },
|
|
{ L"COMBOBOX", TKCOMBOBOX },
|
|
{ L"CONTROL", TKCONTROL },
|
|
{ L"CTEXT", TKCTEXT },
|
|
{ L"DEFPUSHBUTTON", TKDEFPUSHBUTTON },
|
|
{ L"DISCARDABLE", TKDISCARD },
|
|
{ L"DLGINCLUDE", TKDLGINCLUDE },
|
|
{ L"DLGINIT", TKDLGINIT },
|
|
{ L"EDIT", TKEDIT },
|
|
{ L"EDITTEXT", TKEDITTEXT },
|
|
{ L"END", END },
|
|
{ L"EXSTYLE", TKEXSTYLE },
|
|
{ L"FILEFLAGS", TKFILEFLAGS },
|
|
{ L"FILEFLAGSMASK", TKFILEFLAGSMASK },
|
|
{ L"FILEOS", TKFILEOS },
|
|
{ L"FILESUBTYPE", TKFILESUBTYPE },
|
|
{ L"FILETYPE", TKFILETYPE },
|
|
{ L"FILEVERSION", TKFILEVERSION },
|
|
{ L"FIXED", TKFIXED },
|
|
{ L"FONT", TKFONT },
|
|
{ L"GRAYED", TKGRAYED },
|
|
{ L"GROUPBOX", TKGROUPBOX },
|
|
{ L"HEDIT", TKHEDIT },
|
|
{ L"HELP", TKHELP },
|
|
{ L"ICON", TKICON },
|
|
{ L"IEDIT", TKIEDIT },
|
|
{ L"IMPURE", TKIMPURE },
|
|
{ L"INACTIVE", TKINACTIVE },
|
|
{ L"LANGUAGE", TKLANGUAGE },
|
|
{ L"LISTBOX", TKLISTBOX },
|
|
{ L"LOADONCALL", TKLOADONCALL },
|
|
{ L"LTEXT", TKLTEXT },
|
|
{ L"MENU", TKMENU },
|
|
{ L"MENUBARBREAK", TKBREAKWBAR },
|
|
{ L"MENUBREAK", TKBREAK },
|
|
{ L"MENUITEM", TKMENUITEM },
|
|
{ L"MESAGETABLE", TKMESSAGETABLE },
|
|
{ L"MOVEABLE", TKMOVEABLE },
|
|
{ L"NOINVERT", TKNOINVERT },
|
|
{ L"NONSHARED", TKIMPURE },
|
|
{ L"NOT", TKNOT },
|
|
{ L"OWNERDRAW", TKOWNERDRAW },
|
|
{ L"POPUP", TKPOPUP },
|
|
{ L"PRELOAD", TKPRELOAD },
|
|
{ L"PRODUCTVERSION", TKPRODUCTVERSION },
|
|
{ L"PURE", TKPURE },
|
|
{ L"PUSHBOX", TKPUSHBOX },
|
|
{ L"PUSHBUTTON", TKPUSHBUTTON },
|
|
{ L"RADIOBUTTON", TKRADIOBUTTON },
|
|
{ L"RCDATA", TKRCDATA },
|
|
{ L"RTEXT", TKRTEXT },
|
|
{ L"SCROLLBAR", TKSCROLLBAR },
|
|
{ L"SEPARATOR", TKSEPARATOR },
|
|
{ L"SHARED", TKPURE },
|
|
{ L"SHIFT", TKSHIFT },
|
|
{ L"STATE3", TK3STATE },
|
|
{ L"STATIC", TKSTATIC },
|
|
{ L"STYLE", TKSTYLE },
|
|
{ L"USERBUTTON", TKUSERBUTTON },
|
|
{ L"VALUE", TKVALUE },
|
|
{ L"VERSION", TKVERSION },
|
|
{ L"VIRTKEY", TKVIRTKEY },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
|
|
SKEY skeyList[] =
|
|
{
|
|
{ L',', COMMA },
|
|
{ L'|', OR },
|
|
{ L'(', LPAREN },
|
|
{ L')', RPAREN },
|
|
{ L'{', BEGIN },
|
|
{ L'}', END },
|
|
{ L'~', TILDE },
|
|
{ L'+', TKPLUS },
|
|
{ L'-', TKMINUS },
|
|
{ L'&', AND },
|
|
{ L'=', EQUAL },
|
|
{ EOFMARK, EOFMARK },
|
|
{ L'\000', 0 }
|
|
};
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* LexInit() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
int
|
|
LexInit(
|
|
PFILE fh
|
|
)
|
|
{
|
|
/* zero errors so far */
|
|
errorCount = 0;
|
|
curLin = 1;
|
|
curCol = 0;
|
|
inpfh = fh;
|
|
|
|
/* Read initial character */
|
|
OurGetChar();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* GetCharFTB() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
WCHAR
|
|
GetCharFTB(
|
|
void
|
|
)
|
|
{
|
|
return(curCharFTB = *CurPtrTB++);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* OurGetChar() - */
|
|
/* */
|
|
/* Read a character, treating semicolon as an end of line comment char */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
WCHAR
|
|
OurGetChar(
|
|
void
|
|
)
|
|
{
|
|
if ((LitChar() != EOFMARK) && (curChar == CHCOMMENT))
|
|
// if comment, HARD LOOP until EOLN
|
|
while ((LitChar() != EOFMARK) && (curChar != CHNEWLINE));
|
|
|
|
return(curChar);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* FileChar() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
int
|
|
FileChar(
|
|
void
|
|
)
|
|
{
|
|
static WCHAR rgchLine[MAXSTR];
|
|
static int ibNext = MAXSTR;
|
|
int cch, ch;
|
|
|
|
if (ibNext >= MAXSTR) {
|
|
ibNext = 0;
|
|
cch = MyRead (inpfh, rgchLine, MAXSTR * sizeof(WCHAR));
|
|
if (cch < (MAXSTR * sizeof(WCHAR))) {
|
|
fclose(inpfh);
|
|
// NULL terminate the input buffer
|
|
*(rgchLine + (cch / sizeof(WCHAR))) = L'\0';
|
|
}
|
|
}
|
|
|
|
if ((ch = rgchLine[ibNext]) != 0)
|
|
ibNext++;
|
|
|
|
return(ch);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* CopyToken() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
CopyToken(
|
|
PTOKEN ptgt_token,
|
|
PTOKEN psrc_token
|
|
)
|
|
{
|
|
ptgt_token->longval = psrc_token->longval;
|
|
ptgt_token->row = psrc_token->row;
|
|
ptgt_token->col = psrc_token->col;
|
|
ptgt_token->flongval = psrc_token->flongval;
|
|
ptgt_token->val = psrc_token->val;
|
|
ptgt_token->type = psrc_token->type;
|
|
ptgt_token->realtype = psrc_token->realtype;
|
|
|
|
wcscpy(ptgt_token->sym.name, psrc_token->sym.name);
|
|
wcscpy(ptgt_token->sym.file, psrc_token->sym.file);
|
|
ptgt_token->sym.line = psrc_token->sym.line;
|
|
ptgt_token->sym.nID = psrc_token->sym.nID;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* LitChar() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* Read a literal character, without interpreting EOL comments */
|
|
|
|
WCHAR
|
|
LitChar(
|
|
void
|
|
)
|
|
{
|
|
static int fNewLine = TRUE;
|
|
int fIgnore = FALSE;
|
|
int fBackSlash = FALSE;
|
|
int fDot;
|
|
PWCHAR pch;
|
|
WCHAR buf[ _MAX_PATH ];
|
|
TOKEN token_save;
|
|
|
|
for (; ; ) {
|
|
switch (curChar = (WCHAR)FileChar()) {
|
|
case 0:
|
|
curChar = EOFMARK;
|
|
goto char_return;
|
|
|
|
case 0xFEFF: // skip Byte Order Mark
|
|
continue;
|
|
|
|
case SYMDEFSTART:
|
|
{
|
|
int fNewLineSave = fNewLine;
|
|
GetSymbolDef(TRUE, curChar);
|
|
fNewLine = fNewLineSave;
|
|
break;
|
|
}
|
|
|
|
case CHCARRIAGE:
|
|
curChar = CHSPACE;
|
|
if (!fIgnore)
|
|
goto char_return;
|
|
break;
|
|
|
|
case CHNEWLINE:
|
|
fNewLine = TRUE;
|
|
curLin++;
|
|
{
|
|
static long lTotalLin = 0;
|
|
if ((lTotalLin++ & RC_COMPILE_UPDATE) == 0)
|
|
UpdateStatus(2, lTotalLin);
|
|
}
|
|
|
|
if (!fIgnore)
|
|
goto char_return;
|
|
break;
|
|
|
|
/* skip whitespace before #line - don't clear fNewLine */
|
|
case CHSPACE:
|
|
case CHTAB:
|
|
if (!fIgnore)
|
|
goto char_return;
|
|
break;
|
|
|
|
case CHDIRECTIVE:
|
|
if (fNewLine) {
|
|
WCHAR tch;
|
|
|
|
fDot = FALSE;
|
|
|
|
/* also, leave fNewLine set, since we read thru \n */
|
|
|
|
/* read the 'line' part */
|
|
if ((tch = (WCHAR)FileChar()) != L'l') {
|
|
if (tch == L'p') {
|
|
if (FileChar() != L'r')
|
|
goto DirectiveError;
|
|
if (FileChar() != L'a')
|
|
goto DirectiveError;
|
|
if (FileChar() != L'g')
|
|
goto DirectiveError;
|
|
if (FileChar() != L'm')
|
|
goto DirectiveError;
|
|
if (FileChar() != L'a')
|
|
goto DirectiveError;
|
|
|
|
/*
|
|
** This is very specific, as any #pragma will
|
|
** be a code_page pragma written by p0prepro.c.
|
|
*/
|
|
CopyToken( &token_save, &token );
|
|
|
|
GetToken(FALSE); /* get #pragma and ignore */
|
|
GetToken(FALSE); /* get code_page and ignore */
|
|
GetToken(TOKEN_NOEXPRESSION); /* get codepage value only*/
|
|
/* don't check return value */
|
|
uiCodePage = token.val; /* assume ok */
|
|
/* read through end of line */
|
|
while (curChar != CHNEWLINE) {
|
|
curChar = (WCHAR)FileChar();
|
|
}
|
|
CopyToken( &token, &token_save );
|
|
continue;
|
|
} else {
|
|
goto DirectiveError;
|
|
}
|
|
}
|
|
if (FileChar() != L'i')
|
|
goto DirectiveError;
|
|
if (FileChar() != L'n')
|
|
goto DirectiveError;
|
|
if (FileChar() != L'e')
|
|
goto DirectiveError;
|
|
|
|
/* up to filename, grabbing line number as we go */
|
|
/* note that curChar first contains '#', because */
|
|
/* we don't read a new character into curChar */
|
|
curLin = 0;
|
|
do {
|
|
if (curChar >= L'0' && curChar <= L'9') {
|
|
curLin *= 10;
|
|
curLin += curChar - L'0';
|
|
}
|
|
curChar = (WCHAR)FileChar();
|
|
} while (curChar != CHQUOTE && curChar != CHNEWLINE);
|
|
|
|
/* don't change curFile or fIgnore if this is just a
|
|
* #line <lineno>
|
|
*/
|
|
if (curChar == CHNEWLINE)
|
|
break;
|
|
|
|
/* read the filename. detect the presence of .c or .h */
|
|
pch = buf;
|
|
do {
|
|
curChar = (WCHAR)FileChar();
|
|
switch (towlower(curChar)) {
|
|
|
|
/* treat backslash like normal char, set flag. */
|
|
case L'\\':
|
|
if (fBackSlash) {
|
|
fBackSlash = FALSE;
|
|
} else {
|
|
fBackSlash = TRUE;
|
|
fIgnore = FALSE;
|
|
fDot = FALSE;
|
|
*pch++ = curChar;
|
|
}
|
|
break;
|
|
|
|
/* line format sanity check: no embedded newlines */
|
|
case CHNEWLINE:
|
|
case 0:
|
|
DirectiveError:
|
|
LexError1(2101);
|
|
|
|
/* stop reading filename when we hit a quote */
|
|
case CHQUOTE:
|
|
break;
|
|
|
|
/* if we see a ., prepare to find extension */
|
|
case CHEXTENSION:
|
|
fBackSlash = FALSE;
|
|
fDot = TRUE;
|
|
*pch++ = curChar;
|
|
break;
|
|
|
|
/* if there's a C or H after a '.', its not RCINCLUDE'd */
|
|
case CHCSOURCE:
|
|
case CHCHEADER:
|
|
fBackSlash = FALSE;
|
|
fIgnore = fDot;
|
|
fDot = FALSE;
|
|
*pch++ = curChar;
|
|
break;
|
|
|
|
/* any other character in a file means the next character
|
|
won't be after a dot, and the last char up to now
|
|
wasn't C or H.
|
|
*/
|
|
|
|
default:
|
|
fIgnore = FALSE;
|
|
fDot = FALSE;
|
|
*pch++ = curChar;
|
|
break;
|
|
}
|
|
} while (curChar != CHQUOTE);
|
|
*pch = 0;
|
|
WideCharToMultiByte(uiCodePage, 0, buf, -1, (LPSTR) curFile, _MAX_PATH, NULL, NULL);
|
|
|
|
/* read through end of line */
|
|
do {
|
|
curChar = (WCHAR)FileChar();
|
|
} while (curChar != CHNEWLINE);
|
|
|
|
break;
|
|
}
|
|
/* else, fall through, treat like normal char */
|
|
|
|
default:
|
|
fNewLine = FALSE;
|
|
if (!fIgnore)
|
|
goto char_return;
|
|
}
|
|
}
|
|
|
|
char_return:
|
|
if (bExternParse)
|
|
*((WCHAR*) GetSpace(sizeof(WCHAR))) = curChar;
|
|
|
|
return curChar;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* GetStr() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
VOID
|
|
GetStr(
|
|
void
|
|
)
|
|
{
|
|
PWCHAR s;
|
|
WCHAR ch;
|
|
WCHAR temptok[ MAXSTR ];
|
|
SHORT i = 0;
|
|
int inc;
|
|
UCHAR Octal_Num;
|
|
UCHAR HexNum;
|
|
|
|
/* token type is string literal */
|
|
token.realtype = STRLIT;
|
|
|
|
/*
|
|
** NB: FloydR
|
|
** The use of token.realtype is a hack for RCDATA.
|
|
**
|
|
** When we converted RC to be Unicode-based, all the
|
|
** separate "case STRLIT:" code was removed, and the LSTRLIT
|
|
** cases took over for them. Alternatively, we could have
|
|
** left the STRLIT case, but removed the code it accessed
|
|
** and move the STRLIT case prior/after the LSTRLIT case,
|
|
** since they were now identical. They were removed in favor
|
|
** of smaller/faster code.
|
|
**
|
|
** However, RCDATA still had a need to discern the difference,
|
|
** so I added token.realtype, set it to STRLIT in GetStr(),
|
|
** set it to LSTRLIT in GetLStr() (below), and check it in
|
|
** GetRCData() in rctg.c.
|
|
**
|
|
*/
|
|
token.type = LSTRLIT;
|
|
token.val = 0;
|
|
s = tokenbuf;
|
|
|
|
/* read string until " or EOF */
|
|
while (LitChar() != EOFMARK) {
|
|
if (curChar == STRCHAR)
|
|
if (OurGetChar() != STRCHAR)
|
|
goto gotstr;
|
|
|
|
if (token.val++ == MAXSTR)
|
|
LexError1(2102); //"string literal too long"
|
|
else
|
|
*s++ = curChar;
|
|
}
|
|
if (curChar == EOFMARK)
|
|
LexError1(2103); //"unexpected end of file in string literal"
|
|
|
|
gotstr:
|
|
*s++ = 0;
|
|
s = tokenbuf;
|
|
|
|
/* process escape characters in the string */
|
|
|
|
while (*s != 0) {
|
|
if (*s == L'\\') {
|
|
s++;
|
|
if (*s == L'\\')
|
|
temptok[i++] = L'\\';
|
|
else if (*s == L'T' || *s == L't')
|
|
temptok[i++] = L'\011'; /* Tab */
|
|
else if (*s == 0x0a) /* continuation slash */
|
|
; /* ignore and let it go trough the s++ at the end so we skip the 0x0a char*/
|
|
else if (*s == L'A' || *s == L'a')
|
|
temptok[i++] = L'\010'; /* Right Align */
|
|
else if (*s == L'n')
|
|
temptok[i++] = fMacRsrcs ? 13 : 10; /* linefeed */
|
|
else if (*s == L'r')
|
|
temptok[i++] = fMacRsrcs ? 10 : 13; /* carriage return */
|
|
else if (*s == L'"')
|
|
temptok[i++] = L'"'; /* quote character */
|
|
else if (*s == L'X' || *s == L'x') { /* Hexidecimal digit */
|
|
USHORT wCount;
|
|
|
|
HexNum = 0;
|
|
++s;
|
|
for (wCount = 2 ;
|
|
wCount && iswxdigit((ch=(WCHAR)towupper(*s)));
|
|
--wCount) {
|
|
if (ch >= L'A')
|
|
inc = ch - L'A' + 10;
|
|
else
|
|
inc = ch - L'0';
|
|
HexNum = HexNum * 16 + inc;
|
|
s++;
|
|
}
|
|
MultiByteToWideChar(uiCodePage, MB_PRECOMPOSED, (LPCSTR) &HexNum, 1, &temptok[i], 1);
|
|
i++;
|
|
s--;
|
|
} else if (*s >= L'0' && *s <= L'7') { /* octal character */
|
|
USHORT wCount;
|
|
|
|
Octal_Num = 0;
|
|
for (wCount = 3; wCount && *s >= L'0' && *s <= L'7'; --wCount) {
|
|
Octal_Num = (Octal_Num * 8 + (*s - L'0'));
|
|
s++;
|
|
}
|
|
MultiByteToWideChar(uiCodePage, MB_PRECOMPOSED, (LPCSTR) &Octal_Num, 1, &temptok[i], 1);
|
|
i++;
|
|
s--;
|
|
}
|
|
else {
|
|
temptok[i++] = L'\\';
|
|
s--;
|
|
}
|
|
} else
|
|
temptok[i++] = *s;
|
|
s++;
|
|
}
|
|
|
|
/* zero terminate */
|
|
temptok[i] = L'\0';
|
|
memcpy ( tokenbuf, temptok, sizeof(WCHAR)*(i + 1));
|
|
token.val = (USHORT)i;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* GetLStr() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
VOID
|
|
GetLStr(
|
|
void
|
|
)
|
|
{
|
|
PWCHAR s;
|
|
WCHAR ch;
|
|
WCHAR temptok[ MAXSTR ];
|
|
SHORT i = 0;
|
|
int inc;
|
|
int Octal_Num;
|
|
int HexNum;
|
|
|
|
/* token type is string literal */
|
|
token.realtype = token.type = LSTRLIT;
|
|
token.val = 0;
|
|
s = tokenbuf;
|
|
|
|
/* read string until " or EOF */
|
|
while (LitChar() != EOFMARK) {
|
|
if (curChar == STRCHAR)
|
|
if (OurGetChar() != STRCHAR)
|
|
goto gotstr;
|
|
|
|
if (token.val++ == MAXSTR)
|
|
LexError1(2102); //"string literal too long"
|
|
else
|
|
*s++ = curChar;
|
|
}
|
|
if (curChar == EOFMARK)
|
|
LexError1(2103); //"unexpected end of file in string literal"
|
|
if (token.val >= 256) {
|
|
SendError("\n");
|
|
SET_MSG(Msg_Text, sizeof(Msg_Text), GET_MSG(4205), curFile, token.row);
|
|
SendError(Msg_Text);
|
|
}
|
|
|
|
gotstr:
|
|
*s++ = 0;
|
|
s = tokenbuf;
|
|
|
|
/* process escape characters in the string */
|
|
|
|
while (*s != 0) {
|
|
if (*s == L'\\') {
|
|
s++;
|
|
if (*s == L'\\')
|
|
temptok[i++] = L'\\';
|
|
else if (*s == L'T' || *s == L't')
|
|
temptok[i++] = L'\011'; /* Tab */
|
|
else if (*s == L'A' || *s == L'a')
|
|
temptok[i++] = L'\010'; /* Right Align */
|
|
else if (*s == L'n')
|
|
temptok[i++] = fMacRsrcs ? 13 : 10; /* linefeed */
|
|
else if (*s == L'r')
|
|
temptok[i++] = fMacRsrcs ? 10 : 13; /* carriage return */
|
|
else if (*s == L'"')
|
|
temptok[i++] = L'"'; /* quote character */
|
|
else if (*s == L'X' || *s == L'x') { /* Hexidecimal digit */
|
|
USHORT wCount;
|
|
|
|
HexNum = 0;
|
|
++s;
|
|
for (wCount = 4 ;
|
|
wCount && iswxdigit((ch=(WCHAR)towupper(*s)));
|
|
--wCount) {
|
|
if (ch >= L'A')
|
|
inc = ch - L'A' + 10;
|
|
else
|
|
inc = ch - L'0';
|
|
HexNum = HexNum * 16 + inc;
|
|
s++;
|
|
}
|
|
temptok[i++] = (WCHAR)HexNum;
|
|
s--;
|
|
}
|
|
else if (*s >= L'0' && *s <= L'7') { /* octal character */
|
|
USHORT wCount;
|
|
|
|
Octal_Num = 0;
|
|
for (wCount = 7; wCount && *s >= L'0' && *s <= L'7'; --wCount) {
|
|
Octal_Num = (Octal_Num * 8 + (*s - L'0'));
|
|
s++;
|
|
}
|
|
temptok[i++] = (WCHAR)Octal_Num;
|
|
s--;
|
|
}
|
|
|
|
}
|
|
else
|
|
temptok[i++] = *s;
|
|
s++;
|
|
}
|
|
|
|
/* zero terminate */
|
|
temptok[i] = L'\0';
|
|
token.val = (USHORT)i;
|
|
memcpy ( tokenbuf, temptok, sizeof(WCHAR)*(i + 1));
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* GetToken() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
int
|
|
GetToken(
|
|
int fReportError
|
|
)
|
|
{
|
|
for (; ; ) {
|
|
/* skip whitespace */
|
|
while (iswhite( curChar))
|
|
OurGetChar();
|
|
|
|
/* take care of 'random' symbols use */
|
|
if (curChar == SYMUSESTART)
|
|
GetSymbol(fReportError, curChar);
|
|
token.sym.name[0] = L'\0';
|
|
|
|
/* remember location of token */
|
|
token.row = curLin;
|
|
token.col = curCol;
|
|
|
|
/* determine if token is EOF, number, string, or keyword */
|
|
token.type = EOFMARK;
|
|
switch (curChar) {
|
|
case EOFMARK:
|
|
break;
|
|
|
|
case SGNCHAR:
|
|
case L'~':
|
|
if (fReportError & TOKEN_NOEXPRESSION)
|
|
GetNumNoExpression();
|
|
else
|
|
GetNum();
|
|
break;
|
|
|
|
case STRCHAR:
|
|
GetStr();
|
|
break;
|
|
|
|
default:
|
|
if (curChar == L'(' && !(fReportError & TOKEN_NOEXPRESSION))
|
|
GetNum();
|
|
else if (iswdigit( curChar)) {
|
|
if (fReportError & TOKEN_NOEXPRESSION)
|
|
GetNumNoExpression();
|
|
else
|
|
GetNum();
|
|
|
|
if (curChar == SYMUSESTART)
|
|
GetSymbol(fReportError, curChar);
|
|
} else {
|
|
if (!GetKwd( fReportError))
|
|
continue;
|
|
if (token.type == TKLSTR) {
|
|
GetLStr();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return token.type;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* GetXNum() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* get hexadecimal number */
|
|
|
|
LONG
|
|
GetXNum(
|
|
void
|
|
)
|
|
{
|
|
LONG n = 0;
|
|
|
|
while (iswxdigit (GetCharFTB()))
|
|
n = n * 16 + ( ((curCharFTB = (WCHAR)towupper(curCharFTB)) >= L'A') ?
|
|
(WCHAR)(curCharFTB - L'A' + 10) :
|
|
(WCHAR)(curCharFTB - L'0'));
|
|
return (n);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* GetONum() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* get octal number */
|
|
|
|
LONG
|
|
GetONum(
|
|
void
|
|
)
|
|
{
|
|
LONG n = 0;
|
|
|
|
while (GetCharFTB() >= L'0' && curCharFTB <= L'7')
|
|
n = n * 8 + (curCharFTB - L'0');
|
|
return (n);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* GetDNum() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* get decimal number */
|
|
|
|
LONG
|
|
GetDNum(
|
|
void
|
|
)
|
|
{
|
|
LONG n = 0;
|
|
|
|
while (iswdigit(curCharFTB)) {
|
|
n = n * 10 + (curCharFTB - L'0');
|
|
GetCharFTB();
|
|
}
|
|
return (n);
|
|
}
|
|
|
|
|
|
PWSTR
|
|
GetWord(
|
|
PWSTR pStr
|
|
)
|
|
{
|
|
WCHAR ch;
|
|
PSKEY pskey;
|
|
|
|
*pStr++ = curCharFTB = curChar;
|
|
while (TRUE) {
|
|
ch = OurGetChar();
|
|
|
|
if (ch <= L' ')
|
|
goto FoundBreak;
|
|
|
|
switch (ch) {
|
|
case EOFMARK:
|
|
case EOLCHAR:
|
|
case STRCHAR:
|
|
case CHRCHAR:
|
|
goto FoundBreak;
|
|
|
|
default:
|
|
for (pskey = skeyList; pskey->skwd; pskey++)
|
|
if (pskey->skwd == ch)
|
|
goto FoundBreak;
|
|
}
|
|
|
|
*pStr++ = ch;
|
|
}
|
|
|
|
FoundBreak:
|
|
*pStr = 0;
|
|
|
|
return(pStr);
|
|
}
|
|
|
|
|
|
/* GetNumFTB
|
|
* This function was previously added as a hack to handle converting
|
|
* radices. I'm treating this as a (ugly) black box to read a number.
|
|
*/
|
|
|
|
VOID
|
|
GetNumFTB(
|
|
void
|
|
)
|
|
{
|
|
int signFlag;
|
|
USHORT wNotFlag;
|
|
LONG n;
|
|
|
|
/* Small hack to support NOT: If we have a tilde, skip whitespace
|
|
* before the number.
|
|
*/
|
|
if (curChar == L'~')
|
|
while (iswhite(curChar))
|
|
OurGetChar();
|
|
|
|
/* Get the entire number in tokenbuf before computing radix */
|
|
GetWord(tokenbuf);
|
|
|
|
/* Skip the first char. It is already in curCharFTB */
|
|
CurPtrTB = tokenbuf + 1;
|
|
|
|
/* mark token type as numeric literal */
|
|
token.type = NUMLIT;
|
|
|
|
/* find sign of number */
|
|
if (curCharFTB == SGNCHAR) {
|
|
signFlag = TRUE;
|
|
GetCharFTB();
|
|
} else {
|
|
signFlag = FALSE;
|
|
}
|
|
|
|
/* Check for a NOT (~) */
|
|
if (curCharFTB == L'~') {
|
|
wNotFlag = TRUE;
|
|
GetCharFTB();
|
|
} else {
|
|
wNotFlag = FALSE;
|
|
}
|
|
|
|
/* determine radix of number */
|
|
if (curCharFTB == L'0') {
|
|
GetCharFTB();
|
|
if (curCharFTB == L'x')
|
|
n = GetXNum();
|
|
else if (curCharFTB == L'o')
|
|
n = GetONum();
|
|
else
|
|
n = GetDNum();
|
|
} else {
|
|
n = GetDNum();
|
|
}
|
|
|
|
/* find size of number */
|
|
if ((curCharFTB == L'L') || (curCharFTB == L'l')) {
|
|
token.flongval = TRUE;
|
|
GetCharFTB();
|
|
} else {
|
|
token.flongval = FALSE;
|
|
}
|
|
|
|
/* account for sign */
|
|
if (signFlag)
|
|
n = -n;
|
|
|
|
/* Account for the NOT */
|
|
if (wNotFlag)
|
|
n = ~n;
|
|
|
|
/* Set longval regardless of flongval because Dialog Styles
|
|
* always have to be be long
|
|
*/
|
|
token.longval = n;
|
|
token.val = (USHORT)n;
|
|
}
|
|
|
|
|
|
/* ----- Static information needed for parsing ----- */
|
|
static int wLongFlag;
|
|
static int nParenCount;
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* GetNum() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
VOID
|
|
GetNum(
|
|
void
|
|
)
|
|
{
|
|
LONG lValue;
|
|
|
|
/* Initialize */
|
|
wLongFlag = 0;
|
|
nParenCount = 0;
|
|
|
|
/* Return the number */
|
|
lValue = GetExpression();
|
|
|
|
/* Make sure we had matched parens */
|
|
if (nParenCount)
|
|
ParseError1(1013); //"Mismatched parentheses"
|
|
|
|
/* Return as the proper token */
|
|
if (wLongFlag)
|
|
token.flongval = TRUE;
|
|
token.type = NUMLIT;
|
|
token.longval = lValue;
|
|
token.val = (USHORT)lValue;
|
|
}
|
|
|
|
|
|
/* GetNumNoExpression
|
|
* Gets a number without doing expression parsing on it.
|
|
*/
|
|
|
|
VOID
|
|
GetNumNoExpression(
|
|
VOID
|
|
)
|
|
{
|
|
/* Call the single number parser */
|
|
GetNumFTB();
|
|
}
|
|
|
|
|
|
/* GetExpression
|
|
* Gets an expression, which is defined as any number of
|
|
* operators and operands inside one set of parens.
|
|
*/
|
|
|
|
LONG
|
|
GetExpression(
|
|
VOID
|
|
)
|
|
{
|
|
LONG op1;
|
|
LONG op2;
|
|
WCHAR byOperator;
|
|
UINT wFlags;
|
|
|
|
/* Get the first operand */
|
|
op1 = GetOperand();
|
|
|
|
/* take care of symbol use */
|
|
if (curChar == SYMUSESTART) {
|
|
GetSymbol(TRUE, curChar);
|
|
token.sym.nID = token.val;
|
|
}
|
|
|
|
/* Loop until end of expression */
|
|
for (; ; ) {
|
|
/* Get the operator */
|
|
wFlags = GetOperator(&byOperator);
|
|
|
|
/* If this is a right paren, dec the count */
|
|
if (byOperator == L')') {
|
|
/* Bring the paren count back down */
|
|
--nParenCount;
|
|
|
|
/* Skip the paren and any trailing whitespace */
|
|
OurGetChar();
|
|
SkipWhitespace();
|
|
}
|
|
|
|
/* If this isn't an operator, we're done with the expression */
|
|
if (!wFlags) {
|
|
token.sym.nID = (unsigned)op1;
|
|
return op1;
|
|
}
|
|
token.sym.name[0] = L'\0';
|
|
|
|
/* Get the second operand */
|
|
op2 = GetOperand();
|
|
|
|
/* Compute the value of the expression */
|
|
switch (byOperator) {
|
|
case L'+':
|
|
op1 += op2;
|
|
break;
|
|
|
|
case L'-':
|
|
op1 -= op2;
|
|
break;
|
|
|
|
case L'&':
|
|
op1 &= op2;
|
|
break;
|
|
|
|
case L'|':
|
|
op1 |= op2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* GetOperand
|
|
* Gets an operand, which may either be a single number or may
|
|
* be an entire expression.
|
|
*/
|
|
|
|
LONG
|
|
GetOperand(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
/* Check to see if we need to descend a level */
|
|
if (curChar == L'(') {
|
|
/* Bump paren count so we can match them up */
|
|
++nParenCount;
|
|
|
|
/* Skip past the paren char */
|
|
OurGetChar();
|
|
SkipWhitespace();
|
|
|
|
/* Return the value of the computed expression for the operand */
|
|
return GetExpression();
|
|
}
|
|
|
|
/* If this isn't a number, return an error */
|
|
if (curChar != L'-' && curChar != L'~' && !iswdigit(curChar)) {
|
|
GetKwd(FALSE);
|
|
ParseError2(2237, tokenbuf);
|
|
return 0;
|
|
}
|
|
|
|
/* Get the number in the token structure */
|
|
GetNumFTB();
|
|
|
|
/* See if we need to force the result long */
|
|
if (token.flongval)
|
|
wLongFlag = TRUE;
|
|
|
|
/* Skip trailing whitespace */
|
|
SkipWhitespace();
|
|
|
|
/* Return the value */
|
|
return token.longval;
|
|
}
|
|
|
|
|
|
/* GetOperator
|
|
* Gets the next character and decides if it should be an operator.
|
|
* If it should, it returns TRUE, which causes the expression
|
|
* parser to continue. Otherwise, it returns FALSE which causes
|
|
* the expression parser to pop up a level.
|
|
*/
|
|
|
|
int
|
|
GetOperator(
|
|
PWCHAR pOperator
|
|
)
|
|
{
|
|
static WCHAR byOps[] = L"+-|&";
|
|
PWCHAR pOp;
|
|
|
|
/* take care of symbol use */
|
|
if (curChar == SYMUSESTART)
|
|
GetSymbol(TRUE, curChar);
|
|
|
|
/* See if this character is an operator */
|
|
pOp = wcschr(byOps, curChar);
|
|
*pOperator = curChar;
|
|
|
|
/* If we didn't find it, get out */
|
|
if (!pOp)
|
|
return FALSE;
|
|
|
|
/* Otherwise, read trailing whitespace */
|
|
OurGetChar();
|
|
SkipWhitespace();
|
|
|
|
/* Return the operator */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* SkipWhitespace
|
|
* Skips past whitespace in the current stream.
|
|
*/
|
|
|
|
VOID
|
|
SkipWhitespace(
|
|
VOID
|
|
)
|
|
{
|
|
while (iswhite(curChar))
|
|
OurGetChar();
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* GetKwd() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
int
|
|
GetKwd(
|
|
int fReportError
|
|
)
|
|
{
|
|
PSKEY sk;
|
|
|
|
/* see if a special character */
|
|
|
|
for (sk = &skeyList[ 0 ]; sk->skwd; sk++) {
|
|
if (curChar == sk->skwd) {
|
|
token.type = (UCHAR)sk->skwdval;
|
|
token.val = 0;
|
|
OurGetChar();
|
|
return (token.type >= FIRSTKWD);
|
|
}
|
|
}
|
|
|
|
/* else read characters up to the next seperator */
|
|
GetWord(tokenbuf);
|
|
|
|
// Check for TKLSTR -- new for NT
|
|
if (!tokenbuf[1] && (towupper(tokenbuf[0]) == L'L') && (curChar == STRCHAR)) {
|
|
token.type = TKLSTR;
|
|
return TRUE;
|
|
}
|
|
|
|
/* look up keyword in table */
|
|
if ((token.val = FindKwd( tokenbuf)) != (USHORT)-1) {
|
|
token.type = (UCHAR)token.val;
|
|
} else if (fReportError) {
|
|
LexError2(2104, (PCHAR)tokenbuf); //"undefined keyword or key name: %ws"
|
|
return FALSE;
|
|
}
|
|
else
|
|
token.type = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* FindKwd() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
USHORT
|
|
FindKwd(
|
|
PWCHAR str
|
|
)
|
|
{
|
|
PKEY k;
|
|
int t;
|
|
|
|
/* linear search the keyword table for the key */
|
|
for (k = &keyList[0]; k->kwd; k++)
|
|
if (!(t = _wcsicmp( str, k->kwd)))
|
|
return k->kwdval;
|
|
else if (t < 0)
|
|
break;
|
|
|
|
/* if not found, return -1 as keyword id */
|
|
return (USHORT)-1;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* LexError1() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
void
|
|
LexError1(
|
|
int iMsg
|
|
)
|
|
{
|
|
SET_MSG(Msg_Text, sizeof(Msg_Text), GET_MSG(iMsg), curFile, curLin);
|
|
SendError(Msg_Text);
|
|
quit("\n");
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* LexError2() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
void
|
|
LexError2(
|
|
int iMsg,
|
|
PCHAR str
|
|
)
|
|
{
|
|
SET_MSG(Msg_Text, sizeof(Msg_Text), GET_MSG(iMsg), curFile, curLin, str);
|
|
SendError(Msg_Text);
|
|
quit("\n");
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* GetNameOrd() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* For reading in resource names and types. */
|
|
int
|
|
GetNameOrd(
|
|
void
|
|
)
|
|
{
|
|
PWCHAR pch;
|
|
int fString;
|
|
|
|
/* get space delimited string */
|
|
if (!GetGenText())
|
|
return FALSE;
|
|
|
|
/* convert to upper case */
|
|
_wcsupr(tokenbuf);
|
|
|
|
/* is it a string or number */
|
|
for (pch=tokenbuf,fString=0 ; *pch ; pch++ )
|
|
if (!iswdigit(*pch))
|
|
fString = 1;
|
|
|
|
/* determine if ordinal */
|
|
if (tokenbuf[0] == L'0' && tokenbuf[1] == L'X') {
|
|
int HexNum;
|
|
int inc;
|
|
USHORT wCount;
|
|
PWCHAR s;
|
|
|
|
HexNum = 0;
|
|
s = &tokenbuf[2];
|
|
for (wCount = 4 ; wCount && iswxdigit(*s) ; --wCount) {
|
|
if (*s >= L'A')
|
|
inc = *s - L'A' + 10;
|
|
else
|
|
inc = *s - L'0';
|
|
HexNum = HexNum * 16 + inc;
|
|
s++;
|
|
}
|
|
token.val = (USHORT)HexNum;
|
|
} else if (fString) {
|
|
token.val = 0;
|
|
} else {
|
|
token.val = (USHORT)wcsatoi(tokenbuf);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/* GetGenText() - */
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* returns a pointer to a string of generic text */
|
|
|
|
PWCHAR
|
|
GetGenText(
|
|
void
|
|
)
|
|
{
|
|
PWCHAR s;
|
|
|
|
s = tokenbuf;
|
|
|
|
/* skip white space */
|
|
while (iswhite(curChar))
|
|
OurGetChar();
|
|
|
|
if (curChar == EOFMARK) {
|
|
token.type = EOFMARK;
|
|
return NULL;
|
|
}
|
|
|
|
/* random symbol */
|
|
if (curChar == SYMUSESTART)
|
|
GetSymbol(TRUE, curChar);
|
|
token.sym.name[0] = L'\0';
|
|
|
|
/* read space delimited string */
|
|
*s++ = curChar;
|
|
while (( LitChar() != EOFMARK) && ( !iswhite(curChar)))
|
|
*s++ = curChar;
|
|
*s++ = 0; /* put a \0 on the end of the string */
|
|
|
|
OurGetChar(); /* read in the next character */
|
|
if (curChar == EOFMARK)
|
|
token.type = EOFMARK;
|
|
|
|
if (curChar == SYMUSESTART) {
|
|
GetSymbol(TRUE, curChar);
|
|
token.sym.nID = token.val;
|
|
}
|
|
|
|
return (tokenbuf);
|
|
}
|