windows-nt/Source/XPSP1/NT/sdktools/rcdll/p0prepro.c
2020-09-26 16:20:57 +08:00

934 lines
30 KiB
C

/************************************************************************/
/* */
/* RCPP - Resource Compiler Pre-Processor for NT system */
/* */
/* P0PREPRO.C - Main Preprocessor */
/* */
/* 27-Nov-90 w-BrianM Update for NT from PM SDK RCPP */
/* */
/************************************************************************/
#include "rc.h"
#include <ddeml.h>
/************************************************************************/
/* Internal constants */
/************************************************************************/
#define GOT_IF 1 /* last nesting command was an if.. */
#define GOT_ELIF 2 /* last nesting command was an if.. */
#define GOT_ELSE 3 /* last nesting command was an else */
#define GOT_ENDIF 4 /* found endif */
#define ELSE_OR_ENDIF 5 /* skip to either #else or #endif */
#define ENDIF_ONLY 6 /* skip to #endif -- #else is an error*/
int ifstack[IFSTACK_SIZE];
/************************************************************************/
/* Local Function Prototypes */
/************************************************************************/
void chk_newline(PWCHAR);
void in_standard(void);
int incr_ifstack(void);
token_t next_control(void);
unsigned long int pragma(void);
int skipto(int);
void skip_quoted(int);
PWCHAR sysinclude(void);
/************************************************************************/
/* incr_ifstack - Increment the IF nesting stack */
/************************************************************************/
int
incr_ifstack(
void
)
{
if(++Prep_ifstack >= IFSTACK_SIZE) {
strcpy (Msg_Text, GET_MSG (1052));
fatal (1052);
}
return(Prep_ifstack);
}
/************************************************************************
* SYSINCLUDE - process a system include : #include <foo>
*
* ARGUMENTS - none
*
* RETURNS - none
*
* SIDE EFFECTS - none
*
* DESCRIPTION
* Get the system include file name. Since the name is not a "string",
* the name must be built much the same as the -E option rebuilds the text
* by using the Tokstring expansion for tokens with no expansion already
*
* NOTE : IS THIS ANSI? note we're just reading chars, and not expanding
* any macros. NO, it's not. it must expand the macros.
* TODO : have it call yylex() unless and until it finds a '>' or a newline.
* (probably have to set On_pound_line to have yylex return the newline.)
*
* AUTHOR
* Ralph Ryan Sep. 1982
*
* MODIFICATIONS - none
*
*
************************************************************************/
PWCHAR
sysinclude(
void
)
{
REG int c;
REG WCHAR *p_fname;
p_fname = Reuse_W;
c = skip_cwhite();
if( c == L'\n' ) {
UNGETCH();
strcpy (Msg_Text, GET_MSG (2012));
error(2012); /* missing name after '<' */
return(NULL);
}
while( c != L'>' && c != L'\n' ) {
*p_fname++ = (WCHAR)c; /* check for buffer overflow ??? */
c = get_non_eof();
}
if( c == L'\n' ) {
UNGETCH();
strcpy (Msg_Text, GET_MSG (2013));
error(2013); /* missing '>' */
return(NULL);
}
if(p_fname != Reuse_W) {
p_fname--;
while((p_fname >= Reuse_W) && iswspace(*p_fname)) {
p_fname--;
}
p_fname++;
}
*p_fname = L'\0';
return(Reuse_W);
}
/************************************************************************
** preprocess : the scanner found a # which was the first non-white char
** on a line.
************************************************************************/
void
preprocess(
void
)
{
REG WCHAR c;
long eval;
int condition;
token_t deftok;
hln_t identifier;
unsigned long int cp;
if(Macro_depth != 0) { /* # only when not in a macro */
return;
}
switch(CHARMAP(c = skip_cwhite())) {
case LX_ID:
getid(c);
HLN_NAME(identifier) = Reuse_W;
HLN_LENGTH(identifier) = (UINT)Reuse_W_length;
HLN_HASH(identifier) = Reuse_W_hash;
break;
case LX_NL:
UNGETCH();
return;
default:
Msg_Temp = GET_MSG (2019);
SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, c);
error(2019); /* unknown preprocessor command */
skip_cnew(); /* finds a newline */
return;
}
On_pound_line = TRUE;
start:
switch(deftok = is_pkeyword(HLN_IDENTP_NAME(&identifier))) {
int old_prep;
case P0_DEFINE :
define();
break;
case P0_LINE :
old_prep = Prep;
Prep = FALSE;
yylex();
if(Basic_token != L_CINTEGER) { /* #line needs line number */
Msg_Temp = GET_MSG (2005);
SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, TS_STR(Basic_token));
error(2005); /* unknown preprocessor command */
Prep = old_prep;
skip_cnew();
On_pound_line = FALSE;
return;
}
/*
** -1 because there's a newline at the end of this line
** which will be counted later when we find it.
** the #line says the next line is the number we've given
*/
Linenumber = TR_LVALUE(yylval.yy_tree) - 1;
yylex();
Prep = old_prep;
switch(Basic_token) {
case L_STRING:
if( wcscmp(Filename, yylval.yy_string.str_ptr) != 0) {
wcsncpy(Filename,
yylval.yy_string.str_ptr,
sizeof(Filebuff) / sizeof(WCHAR)
);
}
case L_NOTOKEN:
break;
default:
Msg_Temp = GET_MSG (2130);
SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, TS_STR(Basic_token));
error(2130); /* #line needs a string */
skip_cnew();
On_pound_line = FALSE;
return;
break;
}
emit_line();
chk_newline(L"#line");
break;
case P0_INCLUDE :
old_prep = Prep;
Prep = FALSE;
InInclude = TRUE;
yylex();
InInclude = FALSE;
Prep = old_prep;
switch(Basic_token) {
case L_LT:
if((sysinclude()) == NULL) {
skip_cnew();
On_pound_line = FALSE;
return;
break; /* error already emitted */
}
yylval.yy_string.str_ptr = Reuse_W;
break;
case L_STRING:
break;
default:
Msg_Temp = GET_MSG (2006);
SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, TS_STR(Basic_token));
error(2006); /* needs file name */
skip_cnew();
On_pound_line = FALSE;
return;
break;
}
wcscpy(Reuse_Include, yylval.yy_string.str_ptr);
chk_newline(L"#include");
if( wcschr(Path_chars, *yylval.yy_string.str_ptr) ||
(wcschr(Path_chars, L':') && (yylval.yy_string.str_ptr[1] == L':'))) {
/*
** we have a string which either has a 1st char which is a path
** delimiter or, if ':' is a path delimiter (DOS), which has
** "<drive letter>:" as the first two characters. Such names
** specify a fully qualified pathnames. Do not append the search
** list, just look it up.
*/
if( ! newinput(yylval.yy_string.str_ptr, MAY_OPEN)) {
Msg_Temp = GET_MSG (1015);
SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, Reuse_W);
fatal (1015); /* can't find include file */
}
}
else if( (Basic_token != L_STRING) || (! nested_include())) {
in_standard();
}
break;
case P0_IFDEF :
case P0_IFNDEF :
if(CHARMAP(c = skip_cwhite()) != LX_ID) {
strcpy (Msg_Text, GET_MSG (1016));
fatal (1016);
}
getid(c);
eval = (get_defined()) ? TRUE : FALSE;
chk_newline((deftok == P0_IFDEF) ? L"#ifdef" : L"#ifndef");
if(deftok == P0_IFNDEF) {
eval = ( ! eval );
}
if( eval || ((condition = skipto(ELSE_OR_ENDIF)) == GOT_ELSE) ) {
/*
** expression is TRUE or when we skipped the false part
** we found a #else that will be expanded.
*/
ifstack[incr_ifstack()] = GOT_IF;
} else if(condition == GOT_ELIF) {
/* hash is wrong, but it won't be used */
HLN_NAME(identifier) = L"if"; /* sleazy HACK */
goto start;
}
break;
case P0_IF :
old_prep = Prep;
Prep = FALSE;
InIf = TRUE;
eval = do_constexpr();
InIf = FALSE;
Prep = old_prep;
chk_newline(PPifel_str /* "#if/#elif" */);
if((eval) || ((condition = skipto(ELSE_OR_ENDIF)) == GOT_ELSE) ) {
/*
** expression is TRUE or when we skipped the false part
** we found a #else that will be expanded.
*/
ifstack[incr_ifstack()] = GOT_IF;
if(Eflag && !eval)
emit_line();
} else {
/*
** here the #if's expression was false, so we skipped until we found
** an #elif. we'll restart and fake that we're processing a #if
*/
if(Eflag)
emit_line();
if(condition == GOT_ELIF) {
/* hash is wrong, but it won't be needed */
HLN_NAME(identifier) = L"if"; /* sleazy HACK */
goto start;
}
}
break;
case P0_ELIF :
/*
** here, we have found a #elif. first check to make sure that
** this is not an occurrance of a #elif with no preceding #if.
** (if Prep_ifstack < 0) then there is no preceding #if.
*/
if(Prep_ifstack-- < 0) {
strcpy (Msg_Text, GET_MSG (1018));
fatal (1018);
}
/*
** now, the preceding #if/#elif was true, and we've
** just found the next #elif. we want to skip all #else's
** and #elif's from here until we find the enclosing #endif
*/
while(skipto(ELSE_OR_ENDIF) != GOT_ENDIF) {
;
}
if(Eflag)
emit_line();
break;
case P0_ELSE : /* the preceding #if/#elif was true */
if((Prep_ifstack < 0) || (ifstack[Prep_ifstack--] != GOT_IF)) {
strcpy (Msg_Text, GET_MSG (1019));
fatal (1019); /* make sure there was one */
}
chk_newline(PPelse_str /* "#else" */);
skipto(ENDIF_ONLY);
if(Eflag)
emit_line();
break;
case P0_ENDIF : /* only way here is a lonely #endif */
if(Prep_ifstack-- < 0) {
strcpy (Msg_Text, GET_MSG (1020));
fatal (1020);
}
if(Eflag)
emit_line();
chk_newline(PPendif_str /* "#endif" */);
break;
case P0_PRAGMA :
cp = pragma();
if (cp != 0) {
if (cp == CP_WINUNICODE) {
strcpy (Msg_Text, GET_MSG (4213));
if (fWarnInvalidCodePage) {
warning(4213);
} else {
fatal(4213);
}
break;
}
if (!IsValidCodePage(cp)) {
strcpy (Msg_Text, GET_MSG (4214));
if (fWarnInvalidCodePage) {
warning(4214);
} else {
fatal(4214);
}
break;
}
if (cp != uiCodePage) {
if (!io_restart(cp)) {
strcpy (Msg_Text, GET_MSG (1121));
fatal(1121);
}
uiCodePage = cp; // can't be set until now!
}
}
break;
case P0_UNDEF :
if(CHARMAP(c = skip_cwhite()) != LX_ID) {
strcpy (Msg_Text, GET_MSG (4006));
warning(4006); /* missing identifier on #undef */
} else {
getid(c);
undefine();
}
chk_newline(L"#undef");
break;
case P0_ERROR:
{
PWCHAR p;
p = Reuse_W;
while((c = get_non_eof()) != LX_EOS) {
if(c == L'\n') {
UNGETCH();
break;
}
*p++ = c;
}
*p = L'\0';
}
Msg_Temp = GET_MSG (2189);
SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, Reuse_W);
error(2188);
chk_newline(L"#error");
break;
case P0_IDENT:
old_prep = Prep ;
Prep = FALSE;
yylex();
Prep = old_prep;
if(Basic_token != L_STRING) {
Msg_Temp = GET_MSG (4079);
SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, TS_STR(Basic_token));
warning(4079);
}
chk_newline(L"#error");
break;
case P0_NOTOKEN:
Msg_Temp = GET_MSG (1021);
SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, HLN_IDENTP_NAME(&identifier));
fatal (1021);
break;
}
On_pound_line = FALSE;
}
/************************************************************************
* SKIPTO - skip code until the end of an undefined block is reached.
*
* ARGUMENTS
* short key - skip to an ELSE or ENDIF or just an ENDIF
*
* RETURNS - none
*
* SIDE EFFECTS
* - throws away input
*
* DESCRIPTION
* The preprocessor is skipping code between failed ifdef, etc. and
* the corresponding ELSE or ENDIF (when key == ELSE_OR_ENDIF).
* Or it is skipping code between a failed ELSE and the ENDIF (when
* key == ENDIF_ONLY).
*
* AUTHOR - Ralph Ryan, Sept. 16, 1982
*
* MODIFICATIONS - none
*
************************************************************************/
int
skipto(
int key
)
{
REG int level;
REG token_t tok;
level = 0;
tok = P0_NOTOKEN;
for(;;) {
/* make sure that IF [ELSE] ENDIF s are balanced */
switch(next_control()) {
case P0_IFDEF:
case P0_IFNDEF:
case P0_IF:
level++;
break;
case P0_ELSE:
tok = P0_ELSE;
/*
** FALLTHROUGH
*/
case P0_ELIF:
/*
** we found a #else or a #elif. these have their only chance
** at being valid if they're at level 0.
** if we're at any other level,
** then this else/elif belongs to some other #if and we skip them.
** if we were looking for an endif, the we have an error.
*/
if(level != 0) {
tok = P0_NOTOKEN;
break;
}
if(key == ENDIF_ONLY) {
strcpy (Msg_Text, GET_MSG (1022));
fatal (1022); /* expected #endif */
} else if(tok == P0_ELSE) {
chk_newline(PPelse_str /* "#else" */);
return(GOT_ELSE);
} else {
return(GOT_ELIF);
}
break;
case P0_ENDIF:
if(level == 0) {
chk_newline(PPendif_str /* "#endif" */);
return(GOT_ENDIF);
} else {
level--;
}
break;
}
}
}
/*************************************************************************
** in_standard : search for the given file name in the directory list.
** Input : ptr to include file name.
** Output : fatal error if not found.
*************************************************************************/
void
in_standard(
void
)
{
int i;
int stop;
WCHAR *p_dir;
WCHAR *p_file;
WCHAR *p_tmp;
stop = Includes.li_top;
for(i = MAXLIST-1; i >= stop; i--) {
p_file = yylval.yy_string.str_ptr;
if( ((p_dir = Includes.li_defns[i])!=0) &&(wcscmp(p_dir, L"./") != 0) ) {
/*
** there is a directory to prepend and it's not './'
*/
p_tmp = Exp_ptr;
while((*p_tmp++ = *p_dir++) != 0)
;
/*
** above loop increments p_tmp past null.
** this replaces that null with a '/' if needed. Not needed if the
** last character of the directory spec is a path delimiter.
** we then point to the char after the '/'
*/
if(wcschr(Path_chars, p_dir[-2]) == 0) {
p_tmp[-1] = L'/';
} else {
--p_tmp;
}
while((*p_tmp++ = *p_file++) != 0)
;
p_file = Exp_ptr;
}
if(newinput(p_file,MAY_OPEN)) { /* this is the non-error way out */
return;
}
}
Msg_Temp = GET_MSG (1015);
SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, yylval.yy_string.str_ptr);
fatal (1015); /* can't find include file */
}
/*************************************************************************
** chk_newline : check for whitespace only before a newline.
** eat the newline.
*************************************************************************/
void
chk_newline(
PWCHAR cmd
)
{
if(skip_cwhite() != L'\n') {
Msg_Temp = GET_MSG (4067);
SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, cmd);
warning(4067); /* cmd expected newline */
skip_cnew();
} else {
UNGETCH();
}
}
/*************************************************************************
** skip_quoted : skips chars until it finds a char which matches its arg.
*************************************************************************/
void
skip_quoted(
int sc
)
{
REG WCHAR c;
for(;;) {
switch(CHARMAP(c = GETCH())) {
case LX_NL:
strcpy (Msg_Text, GET_MSG (4093));
warning(4093);
UNGETCH();
return;
break;
case LX_DQUOTE:
case LX_SQUOTE:
if(c == (WCHAR)sc)
return;
break;
case LX_EOS:
if(handle_eos() == BACKSLASH_EOS) {
SKIPCH(); /* might be /" !! */
}
break;
case LX_LEADBYTE:
get_non_eof();
break;
}
}
}
/*************************************************************************
** next_control : find a newline. find a pound sign as the first non-white.
** find an id start char, build an id look it up and return the token.
** this knows about strings/char const and such.
*************************************************************************/
token_t
next_control(
void
)
{
REG WCHAR c;
for(;;) {
c = skip_cwhite();
first_switch:
switch(CHARMAP(c)) {
case LX_NL:
Linenumber++;
// must manually write '\r' with '\n' when writing 16-bit strings
if(Prep) {
myfwrite(L"\r\n", 2 * sizeof(WCHAR), 1, OUTPUTFILE);
}
if((c = skip_cwhite()) == L'#') {
if(LX_IS_IDENT(c = skip_cwhite())) {
/*
** this is the only way to return to the caller.
*/
getid(c);
return(is_pkeyword(Reuse_W)); /* if its predefined */
}
}
goto first_switch;
break;
case LX_DQUOTE:
case LX_SQUOTE:
skip_quoted(c);
break;
case LX_EOS:
if(handle_eos() == BACKSLASH_EOS) {
SKIPCH(); /* might be \" !! */
}
break;
}
}
}
/*************************************************************************
** do_defined : does the work for the defined(id)
** should parens be counted, or just be used as delimiters (ie the
** first open paren matches the first close paren)? If this is ever
** an issue, it really means that there is not a legal identifier
** between the parens, causing an error anyway, but consider:
** #if (defined(2*(x-1))) || 1
** #endif
** It is friendlier to allow compilation to continue
*************************************************************************/
int
do_defined(
PWCHAR p_tmp
)
{
REG UINT c;
REG int value=0;
int paren_level = 0;
/*
** we want to allow:
** #define FOO defined
** #define BAR(a,b) a FOO | b
** #define SNAFOO 0
** #if FOO BAR
** print("BAR is defined");
** #endif
** #if BAR(defined, SNAFOO)
** print("FOO is defined");
** #endif
*/
if(wcscmp(p_tmp,L"defined") != 0) {
return(0);
}
if((!can_get_non_white()) && (Tiny_lexer_nesting == 0)) {
/* NL encountered */
return(value);
}
if((c = CHECKCH())== L'(') { /* assumes no other CHARMAP form of OPAREN */
*Exp_ptr++ = (WCHAR)c;
SKIPCH();
paren_level++;
if((!can_get_non_white()) && (Tiny_lexer_nesting == 0)) {
/* NL encountered */
return(value);
}
}
if(Tiny_lexer_nesting>0) {
if((CHARMAP((WCHAR)(c=CHECKCH()))==LX_MACFORMAL) || (CHARMAP((WCHAR)c)==LX_ID)) {
SKIPCH();
tl_getid((UCHAR)c);
}
} else {
if(LX_IS_IDENT(((WCHAR)(c = CHECKCH())))) {
SKIPCH();
if(Macro_depth >0) {
lex_getid((WCHAR)c);
} else {
getid((WCHAR)c);
}
value = (get_defined()) ? TRUE : FALSE;
} else {
if(paren_level==0) {
strcpy (Msg_Text, GET_MSG (2003));
error(2003);
} else {
strcpy (Msg_Text, GET_MSG (2004));
error(2004);
}
}
}
if((CHARMAP(((WCHAR)(c = CHECKCH()))) == LX_WHITE) || (CHARMAP((WCHAR)c) == LX_EOS)) {
if( ! can_get_non_white()) {
return(value);
}
}
if(paren_level) {
if((CHARMAP(((WCHAR)(c = CHECKCH()))) == LX_CPAREN)) {
SKIPCH();
paren_level--;
*Exp_ptr++ = (WCHAR)c;
}
}
if((paren_level > 0) && (Tiny_lexer_nesting == 0)) {
strcpy (Msg_Text, GET_MSG (4004));
warning(4004);
}
return(value);
}
/*************************************************************************
* NEXTIS - The lexical interface for #if expression parsing.
* If the next token does not match what is wanted, return FALSE.
* otherwise Set Currtok to L_NOTOKEN to force scanning on the next call.
* Return TRUE.
* will leave a newline as next char if it finds one.
*************************************************************************/
int
nextis(
register token_t tok
)
{
if(Currtok != L_NOTOKEN) {
if(tok == Currtok) {
Currtok = L_NOTOKEN; /* use up the token */
return(TRUE);
} else {
return(FALSE);
}
}
switch(yylex()) { /* acquire a new token */
case 0:
break;
case L_CONSTANT:
if( ! IS_INTEGRAL(TR_BTYPE(yylval.yy_tree))) {
strcpy (Msg_Text, GET_MSG (1017));
fatal (1017);
} else {
Currval = TR_LVALUE(yylval.yy_tree);
}
if(tok == L_CINTEGER) {
return(TRUE);
}
Currtok = L_CINTEGER;
break;
case L_IDENT:
Currval = do_defined(HLN_IDENTP_NAME(&yylval.yy_ident));
if(tok == L_CINTEGER) {
return(TRUE);
}
Currtok = L_CINTEGER;
break;
default:
if(tok == Basic_token) {
return(TRUE);
}
Currtok = Basic_token;
break;
}
return(FALSE);
}
/************************************************************************
** skip_cnew : reads up to and including the next newline.
************************************************************************/
void
skip_cnew(
void
)
{
for(;;) {
switch(CHARMAP(GETCH())) {
case LX_NL:
UNGETCH();
return;
case LX_SLASH:
skip_comment();
break;
case LX_EOS:
handle_eos();
break;
}
}
}
/************************************************************************
** skip_NLonly : reads up to the next newline, disallowing comments
************************************************************************/
void
skip_NLonly(
void
)
{
for(;;) {
switch(CHARMAP(GETCH())) {
case LX_NL:
UNGETCH();
return;
case LX_EOS:
handle_eos();
break;
}
}
}
/************************************************************************
** pragma : handle processing the pragma directive
** called by preprocess() after we have seen the #pragma
** and are ready to handle the keyword which follows.
************************************************************************/
unsigned long
pragma(
void
)
{
WCHAR c;
unsigned long int cp=0;
c = skip_cwhite();
if (c != L'\n') {
getid(c);
_wcsupr(Reuse_W);
if (wcscmp(L"CODE_PAGE", Reuse_W) == 0) {
if ((c = skip_cwhite()) == L'(') {
c = skip_cwhite(); // peek token
if (iswdigit(c)) {
token_t tok;
int old_prep = Prep;
Prep = FALSE;
tok = getnum(c);
Prep = old_prep;
switch(tok) {
default:
case L_CFLOAT:
case L_CDOUBLE:
case L_CLDOUBLE:
case L_FLOAT:
case L_DOUBLE:
break;
case L_CINTEGER:
case L_LONGINT:
case L_CUNSIGNED:
case L_LONGUNSIGNED:
case L_SHORT:
case L_LONG:
case L_SIGNED:
case L_UNSIGNED:
cp = TR_LVALUE(yylval.yy_tree);
break;
}
}
if (cp == 0) {
getid(c);
_wcsupr(Reuse_W);
if (wcscmp(L"DEFAULT", Reuse_W) == 0) {
cp = uiDefaultCodePage;
} else {
wsprintfA(Msg_Text, "%s%ws", GET_MSG(4212), Reuse_W);
error(4212);
}
}
if ((c = skip_cwhite()) != L')') {
UNGETCH();
strcpy (Msg_Text, GET_MSG (4211));
error(4211);
}
} else {
UNGETCH();
strcpy (Msg_Text, GET_MSG (4210));
error(4210);
}
swprintf(Reuse_W, L"#pragma code_page %d\r\n", cp);
myfwrite(Reuse_W, wcslen(Reuse_W) * sizeof(WCHAR), 1, OUTPUTFILE);
}
}
// Skip #pragma statements
while((c = get_non_eof()) != L'\n');
UNGETCH();
return cp;
}