sanitized string and character constant parsing
This commit is contained in:
301
tcc.c
301
tcc.c
@ -223,7 +223,7 @@ typedef struct BufferedFile {
|
|||||||
uint8_t *buf_ptr;
|
uint8_t *buf_ptr;
|
||||||
uint8_t *buf_end;
|
uint8_t *buf_end;
|
||||||
int fd;
|
int fd;
|
||||||
int line_num; /* current line number - here to simply code */
|
int line_num; /* current line number - here to simplify code */
|
||||||
int ifndef_macro; /* #ifndef macro / #endif search */
|
int ifndef_macro; /* #ifndef macro / #endif search */
|
||||||
int ifndef_macro_saved; /* saved ifndef_macro */
|
int ifndef_macro_saved; /* saved ifndef_macro */
|
||||||
int *ifdef_stack_ptr; /* ifdef_stack value at the start of the file */
|
int *ifdef_stack_ptr; /* ifdef_stack value at the start of the file */
|
||||||
@ -1738,6 +1738,18 @@ static int handle_stray1(uint8_t *p)
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* handle just the EOB case, but not stray */
|
||||||
|
#define PEEKC_EOB(c, p)\
|
||||||
|
{\
|
||||||
|
p++;\
|
||||||
|
c = *p;\
|
||||||
|
if (c == '\\') {\
|
||||||
|
file->buf_ptr = p;\
|
||||||
|
c = handle_eob();\
|
||||||
|
p = file->buf_ptr;\
|
||||||
|
}\
|
||||||
|
}
|
||||||
|
|
||||||
/* handle the complicated stray case */
|
/* handle the complicated stray case */
|
||||||
#define PEEKC(c, p)\
|
#define PEEKC(c, p)\
|
||||||
{\
|
{\
|
||||||
@ -1862,11 +1874,73 @@ static inline void skip_spaces(void)
|
|||||||
cinp();
|
cinp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* parse a string without interpreting escapes */
|
||||||
|
static uint8_t *parse_pp_string(uint8_t *p,
|
||||||
|
int sep, CString *str)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
p++;
|
||||||
|
for(;;) {
|
||||||
|
c = *p;
|
||||||
|
if (c == sep) {
|
||||||
|
break;
|
||||||
|
} else if (c == '\\') {
|
||||||
|
file->buf_ptr = p;
|
||||||
|
c = handle_eob();
|
||||||
|
p = file->buf_ptr;
|
||||||
|
if (c == CH_EOF) {
|
||||||
|
unterminated_string:
|
||||||
|
/* XXX: indicate line number of start of string */
|
||||||
|
error("missing terminating %c character", sep);
|
||||||
|
} else if (c == '\\') {
|
||||||
|
/* escape : just skip \[\r]\n */
|
||||||
|
PEEKC_EOB(c, p);
|
||||||
|
if (c == '\n') {
|
||||||
|
file->line_num++;
|
||||||
|
p++;
|
||||||
|
} else if (c == '\r') {
|
||||||
|
PEEKC_EOB(c, p);
|
||||||
|
if (c != '\n')
|
||||||
|
expect("'\n' after '\r'");
|
||||||
|
file->line_num++;
|
||||||
|
p++;
|
||||||
|
} else if (c == CH_EOF) {
|
||||||
|
goto unterminated_string;
|
||||||
|
} else {
|
||||||
|
if (str) {
|
||||||
|
cstr_ccat(str, '\\');
|
||||||
|
cstr_ccat(str, c);
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (c == '\n') {
|
||||||
|
file->line_num++;
|
||||||
|
goto add_char;
|
||||||
|
} else if (c == '\r') {
|
||||||
|
PEEKC_EOB(c, p);
|
||||||
|
if (c != '\n') {
|
||||||
|
cstr_ccat(str, '\r');
|
||||||
|
} else {
|
||||||
|
file->line_num++;
|
||||||
|
goto add_char;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
add_char:
|
||||||
|
if (str)
|
||||||
|
cstr_ccat(str, c);
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
/* skip block of text until #else, #elif or #endif. skip also pairs of
|
/* skip block of text until #else, #elif or #endif. skip also pairs of
|
||||||
#if/#endif */
|
#if/#endif */
|
||||||
void preprocess_skip(void)
|
void preprocess_skip(void)
|
||||||
{
|
{
|
||||||
int a, start_of_line, sep, c;
|
int a, start_of_line, c;
|
||||||
uint8_t *p;
|
uint8_t *p;
|
||||||
|
|
||||||
p = file->buf_ptr;
|
p = file->buf_ptr;
|
||||||
@ -1903,41 +1977,7 @@ void preprocess_skip(void)
|
|||||||
/* skip strings */
|
/* skip strings */
|
||||||
case '\"':
|
case '\"':
|
||||||
case '\'':
|
case '\'':
|
||||||
sep = c;
|
p = parse_pp_string(p, c, NULL);
|
||||||
p++;
|
|
||||||
for(;;) {
|
|
||||||
c = *p;
|
|
||||||
if (c == sep) {
|
|
||||||
break;
|
|
||||||
} else if (c == '\\') {
|
|
||||||
file->buf_ptr = p;
|
|
||||||
c = handle_eob();
|
|
||||||
p = file->buf_ptr;
|
|
||||||
if (c == CH_EOF) {
|
|
||||||
/* XXX: better error message */
|
|
||||||
error("unterminated string");
|
|
||||||
} else if (c == '\\') {
|
|
||||||
/* ignore next char */
|
|
||||||
p++;
|
|
||||||
c = *p;
|
|
||||||
if (c == '\\') {
|
|
||||||
file->buf_ptr = p;
|
|
||||||
c = handle_eob();
|
|
||||||
p = file->buf_ptr;
|
|
||||||
}
|
|
||||||
if (c == '\n')
|
|
||||||
file->line_num++;
|
|
||||||
else if (c != CH_EOF)
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
} else if (c == '\n') {
|
|
||||||
file->line_num++;
|
|
||||||
p++;
|
|
||||||
} else {
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p++;
|
|
||||||
break;
|
break;
|
||||||
/* skip comments */
|
/* skip comments */
|
||||||
case '/':
|
case '/':
|
||||||
@ -2724,55 +2764,57 @@ static void preprocess(int is_bof)
|
|||||||
parse_flags = saved_parse_flags;
|
parse_flags = saved_parse_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read a number in base b */
|
/* evaluate escape codes in a string. */
|
||||||
static int getn(int b)
|
static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long)
|
||||||
{
|
{
|
||||||
int n, t;
|
int c, n;
|
||||||
n = 0;
|
const char *p;
|
||||||
while (1) {
|
|
||||||
if (ch >= 'a' && ch <= 'f')
|
|
||||||
t = ch - 'a' + 10;
|
|
||||||
else if (ch >= 'A' && ch <= 'F')
|
|
||||||
t = ch - 'A' + 10;
|
|
||||||
else if (isnum(ch))
|
|
||||||
t = ch - '0';
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
if (t < 0 || t >= b)
|
|
||||||
break;
|
|
||||||
n = n * b + t;
|
|
||||||
inp();
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read a character for string or char constant and eval escape codes */
|
p = buf;
|
||||||
static int getq(void)
|
for(;;) {
|
||||||
{
|
c = *p;
|
||||||
int c;
|
if (c == '\0')
|
||||||
|
break;
|
||||||
redo:
|
|
||||||
c = ch;
|
|
||||||
inp();
|
|
||||||
if (c == '\\') {
|
if (c == '\\') {
|
||||||
switch(ch) {
|
p++;
|
||||||
|
/* escape */
|
||||||
|
c = *p;
|
||||||
|
switch(c) {
|
||||||
case '0': case '1': case '2': case '3':
|
case '0': case '1': case '2': case '3':
|
||||||
case '4': case '5': case '6': case '7':
|
case '4': case '5': case '6': case '7':
|
||||||
/* at most three octal digits */
|
/* at most three octal digits */
|
||||||
c = ch - '0';
|
n = c - '0';
|
||||||
inp();
|
p++;
|
||||||
if (isoct(ch)) {
|
c = *p;
|
||||||
c = c * 8 + ch - '0';
|
if (isoct(c)) {
|
||||||
inp();
|
n = n * 8 + c - '0';
|
||||||
if (isoct(ch)) {
|
p++;
|
||||||
c = c * 8 + ch - '0';
|
c = *p;
|
||||||
inp();
|
if (isoct(c)) {
|
||||||
|
n = n * 8 + c - '0';
|
||||||
|
p++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c;
|
c = n;
|
||||||
|
goto add_char_nonext;
|
||||||
case 'x':
|
case 'x':
|
||||||
inp();
|
p++;
|
||||||
return getn(16);
|
n = 0;
|
||||||
|
for(;;) {
|
||||||
|
c = *p;
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
c = c - 'a' + 10;
|
||||||
|
else if (c >= 'A' && c <= 'F')
|
||||||
|
c = c - 'A' + 10;
|
||||||
|
else if (isnum(c))
|
||||||
|
c = c - '0';
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
n = n * 16 + c;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
c = n;
|
||||||
|
goto add_char_nonext;
|
||||||
case 'a':
|
case 'a':
|
||||||
c = '\a';
|
c = '\a';
|
||||||
break;
|
break;
|
||||||
@ -2803,27 +2845,24 @@ static int getq(void)
|
|||||||
case '\"':
|
case '\"':
|
||||||
case '\\':
|
case '\\':
|
||||||
case '?':
|
case '?':
|
||||||
c = ch;
|
|
||||||
break;
|
break;
|
||||||
case '\n':
|
|
||||||
inp();
|
|
||||||
goto redo;
|
|
||||||
case '\r':
|
|
||||||
inp();
|
|
||||||
if (ch != '\n')
|
|
||||||
goto invalid_escape;
|
|
||||||
inp();
|
|
||||||
goto redo;
|
|
||||||
default:
|
default:
|
||||||
invalid_escape:
|
invalid_escape:
|
||||||
error("invalid escaped char");
|
error("invalid escaped char");
|
||||||
}
|
}
|
||||||
inp();
|
|
||||||
} else if (c == '\r' && ch == '\n') {
|
|
||||||
inp();
|
|
||||||
c = '\n';
|
|
||||||
}
|
}
|
||||||
return c;
|
p++;
|
||||||
|
add_char_nonext:
|
||||||
|
if (!is_long)
|
||||||
|
cstr_ccat(outstr, c);
|
||||||
|
else
|
||||||
|
cstr_wccat(outstr, c);
|
||||||
|
}
|
||||||
|
/* add a trailing '\0' */
|
||||||
|
if (!is_long)
|
||||||
|
cstr_ccat(outstr, '\0');
|
||||||
|
else
|
||||||
|
cstr_wccat(outstr, '\0');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we use 64 bit numbers */
|
/* we use 64 bit numbers */
|
||||||
@ -3132,7 +3171,7 @@ void parse_number(const char *p)
|
|||||||
/* return next token without macro substitution */
|
/* return next token without macro substitution */
|
||||||
static inline void next_nomacro1(void)
|
static inline void next_nomacro1(void)
|
||||||
{
|
{
|
||||||
int b, t, c;
|
int t, c, is_long;
|
||||||
TokenSym *ts;
|
TokenSym *ts;
|
||||||
uint8_t *p, *p1;
|
uint8_t *p, *p1;
|
||||||
unsigned int h;
|
unsigned int h;
|
||||||
@ -3304,11 +3343,8 @@ static inline void next_nomacro1(void)
|
|||||||
goto parse_ident_fast;
|
goto parse_ident_fast;
|
||||||
} else {
|
} else {
|
||||||
PEEKC(c, p);
|
PEEKC(c, p);
|
||||||
if (c == '\'') {
|
if (c == '\'' || c == '\"') {
|
||||||
tok = TOK_LCHAR;
|
is_long = 1;
|
||||||
goto char_const;
|
|
||||||
} else if (c == '\"') {
|
|
||||||
tok = TOK_LSTR;
|
|
||||||
goto str_const;
|
goto str_const;
|
||||||
} else {
|
} else {
|
||||||
cstr_reset(&tokcstr);
|
cstr_reset(&tokcstr);
|
||||||
@ -3357,42 +3393,51 @@ static inline void next_nomacro1(void)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '\'':
|
case '\'':
|
||||||
tok = TOK_CCHAR;
|
|
||||||
char_const:
|
|
||||||
file->buf_ptr = p;
|
|
||||||
inp();
|
|
||||||
b = getq();
|
|
||||||
/* this cast is needed if >= 128 */
|
|
||||||
if (tok == TOK_CCHAR)
|
|
||||||
b = (char)b;
|
|
||||||
tokc.i = b;
|
|
||||||
if (ch != '\'')
|
|
||||||
error("unterminated character constant");
|
|
||||||
p = file->buf_ptr;
|
|
||||||
p++;
|
|
||||||
break;
|
|
||||||
case '\"':
|
case '\"':
|
||||||
tok = TOK_STR;
|
is_long = 0;
|
||||||
str_const:
|
str_const:
|
||||||
file->buf_ptr = p;
|
{
|
||||||
inp();
|
CString str;
|
||||||
|
int sep;
|
||||||
|
|
||||||
|
sep = c;
|
||||||
|
|
||||||
|
/* parse the string */
|
||||||
|
cstr_new(&str);
|
||||||
|
p = parse_pp_string(p, sep, &str);
|
||||||
|
cstr_ccat(&str, '\0');
|
||||||
|
|
||||||
|
/* eval the escape (should be done as TOK_PPNUM) */
|
||||||
cstr_reset(&tokcstr);
|
cstr_reset(&tokcstr);
|
||||||
while (ch != '\"') {
|
parse_escape_string(&tokcstr, str.data, is_long);
|
||||||
b = getq();
|
cstr_free(&str);
|
||||||
if (ch == CH_EOF)
|
|
||||||
error("unterminated string");
|
if (sep == '\'') {
|
||||||
if (tok == TOK_STR)
|
int char_size;
|
||||||
cstr_ccat(&tokcstr, b);
|
/* XXX: make it portable */
|
||||||
|
if (!is_long)
|
||||||
|
char_size = 1;
|
||||||
else
|
else
|
||||||
cstr_wccat(&tokcstr, b);
|
char_size = sizeof(int);
|
||||||
|
if (tokcstr.size <= char_size)
|
||||||
|
error("empty character constant");
|
||||||
|
if (tokcstr.size > 2 * char_size)
|
||||||
|
warning("multi-character character constant");
|
||||||
|
if (!is_long) {
|
||||||
|
tokc.i = *(int8_t *)tokcstr.data;
|
||||||
|
tok = TOK_CCHAR;
|
||||||
|
} else {
|
||||||
|
tokc.i = *(int *)tokcstr.data;
|
||||||
|
tok = TOK_LCHAR;
|
||||||
}
|
}
|
||||||
if (tok == TOK_STR)
|
} else {
|
||||||
cstr_ccat(&tokcstr, '\0');
|
|
||||||
else
|
|
||||||
cstr_wccat(&tokcstr, '\0');
|
|
||||||
tokc.cstr = &tokcstr;
|
tokc.cstr = &tokcstr;
|
||||||
p = file->buf_ptr;
|
if (!is_long)
|
||||||
p++;
|
tok = TOK_STR;
|
||||||
|
else
|
||||||
|
tok = TOK_LSTR;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '<':
|
case '<':
|
||||||
|
|||||||
Reference in New Issue
Block a user