mirror of
https://github.com/mirror/tinycc.git
synced 2025-01-15 05:20:06 +08:00
fixed \nnn char parsing, added #line, added __VA_ARGS__, added macros in #include, added \xnn parsing, added L'' and L parsing (still not OK), added 0bXX, added ISOC99 initializers handling, added declaration parsing anywhere in block
This commit is contained in:
parent
ef6e8589d1
commit
44b5c9742a
276
tcc.c
276
tcc.c
@ -233,10 +233,12 @@ enum {
|
|||||||
TOK_DEFINED,
|
TOK_DEFINED,
|
||||||
TOK_UNDEF,
|
TOK_UNDEF,
|
||||||
TOK_ERROR,
|
TOK_ERROR,
|
||||||
|
TOK_LINE,
|
||||||
TOK___LINE__,
|
TOK___LINE__,
|
||||||
TOK___FILE__,
|
TOK___FILE__,
|
||||||
TOK___DATE__,
|
TOK___DATE__,
|
||||||
TOK___TIME__,
|
TOK___TIME__,
|
||||||
|
TOK___VA_ARGS__,
|
||||||
|
|
||||||
/* special identifiers */
|
/* special identifiers */
|
||||||
TOK___FUNC__,
|
TOK___FUNC__,
|
||||||
@ -250,6 +252,7 @@ int expr_const();
|
|||||||
void expr_eq();
|
void expr_eq();
|
||||||
void expr();
|
void expr();
|
||||||
void decl();
|
void decl();
|
||||||
|
void decl_assign(int t, int c, int first);
|
||||||
int gv();
|
int gv();
|
||||||
void move_reg();
|
void move_reg();
|
||||||
void save_reg();
|
void save_reg();
|
||||||
@ -715,6 +718,8 @@ void preprocess()
|
|||||||
next_nomacro();
|
next_nomacro();
|
||||||
ps = &first;
|
ps = &first;
|
||||||
while (tok != ')') {
|
while (tok != ')') {
|
||||||
|
if (tok == TOK_DOTS)
|
||||||
|
tok = TOK___VA_ARGS__;
|
||||||
s = sym_push1(&define_stack, tok | SYM_FIELD, 0, 0);
|
s = sym_push1(&define_stack, tok | SYM_FIELD, 0, 0);
|
||||||
*ps = s;
|
*ps = s;
|
||||||
ps = &s->next;
|
ps = &s->next;
|
||||||
@ -763,6 +768,14 @@ void preprocess()
|
|||||||
minp();
|
minp();
|
||||||
}
|
}
|
||||||
*q = '\0';
|
*q = '\0';
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
if (tok != TOK_STR)
|
||||||
|
error("#include syntax error");
|
||||||
|
/* XXX: buffer overflow */
|
||||||
|
strcpy(buf, get_tok_str(tok, tokc));
|
||||||
|
c = '\"';
|
||||||
|
}
|
||||||
if (include_stack_ptr >= include_stack + INCLUDE_STACK_SIZE)
|
if (include_stack_ptr >= include_stack + INCLUDE_STACK_SIZE)
|
||||||
error("memory full");
|
error("memory full");
|
||||||
if (c == '\"') {
|
if (c == '\"') {
|
||||||
@ -800,7 +813,6 @@ void preprocess()
|
|||||||
file = f;
|
file = f;
|
||||||
filename = strdup(buf1);
|
filename = strdup(buf1);
|
||||||
line_num = 1;
|
line_num = 1;
|
||||||
}
|
|
||||||
} else if (tok == TOK_IFNDEF) {
|
} else if (tok == TOK_IFNDEF) {
|
||||||
c = 1;
|
c = 1;
|
||||||
goto do_ifdef;
|
goto do_ifdef;
|
||||||
@ -838,6 +850,19 @@ void preprocess()
|
|||||||
if (ifdef_stack_ptr == ifdef_stack)
|
if (ifdef_stack_ptr == ifdef_stack)
|
||||||
expect("#if");
|
expect("#if");
|
||||||
ifdef_stack_ptr--;
|
ifdef_stack_ptr--;
|
||||||
|
} else if (tok == TOK_LINE) {
|
||||||
|
next();
|
||||||
|
if (tok != TOK_NUM)
|
||||||
|
error("#line");
|
||||||
|
line_num = tokc;
|
||||||
|
skip_spaces();
|
||||||
|
if (ch != '\n') {
|
||||||
|
next();
|
||||||
|
if (tok != TOK_STR)
|
||||||
|
error("#line");
|
||||||
|
/* XXX: potential memory leak */
|
||||||
|
filename = strdup(get_tok_str(tok, tokc));
|
||||||
|
}
|
||||||
} else if (tok == TOK_ERROR) {
|
} else if (tok == TOK_ERROR) {
|
||||||
error("#error");
|
error("#error");
|
||||||
}
|
}
|
||||||
@ -877,7 +902,21 @@ int getq()
|
|||||||
minp();
|
minp();
|
||||||
if (c == '\\') {
|
if (c == '\\') {
|
||||||
if (isnum(ch)) {
|
if (isnum(ch)) {
|
||||||
return getn(8);
|
/* at most three octal digits */
|
||||||
|
c = ch - '0';
|
||||||
|
minp();
|
||||||
|
if (isnum(ch)) {
|
||||||
|
c = c * 8 + ch - '0';
|
||||||
|
minp();
|
||||||
|
if (isnum(ch)) {
|
||||||
|
c = c * 8 + ch - '0';
|
||||||
|
minp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
} else if (ch == 'x') {
|
||||||
|
minp();
|
||||||
|
return getn(16);
|
||||||
} else {
|
} else {
|
||||||
if (ch == 'a')
|
if (ch == 'a')
|
||||||
c = '\a';
|
c = '\a';
|
||||||
@ -926,6 +965,16 @@ void next_nomacro1()
|
|||||||
}
|
}
|
||||||
if (isid(ch)) {
|
if (isid(ch)) {
|
||||||
q = token_buf;
|
q = token_buf;
|
||||||
|
*q++ = ch;
|
||||||
|
cinp();
|
||||||
|
if (q[-1] == 'L') {
|
||||||
|
/* XXX: not supported entirely (needs different
|
||||||
|
preprocessor architecture) */
|
||||||
|
if (ch == '\'')
|
||||||
|
goto char_const;
|
||||||
|
if (ch == '\"')
|
||||||
|
goto str_const;
|
||||||
|
}
|
||||||
while (isid(ch) | isnum(ch)) {
|
while (isid(ch) | isnum(ch)) {
|
||||||
if (q >= token_buf + STRING_MAX_SIZE)
|
if (q >= token_buf + STRING_MAX_SIZE)
|
||||||
error("ident too long");
|
error("ident too long");
|
||||||
@ -944,6 +993,9 @@ void next_nomacro1()
|
|||||||
if (ch == 'x' || ch == 'X') {
|
if (ch == 'x' || ch == 'X') {
|
||||||
cinp();
|
cinp();
|
||||||
b = 16;
|
b = 16;
|
||||||
|
} else if (ch == 'b' || ch == 'B') {
|
||||||
|
cinp();
|
||||||
|
b = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tokc = getn(b);
|
tokc = getn(b);
|
||||||
@ -952,6 +1004,7 @@ void next_nomacro1()
|
|||||||
cinp();
|
cinp();
|
||||||
tok = TOK_NUM;
|
tok = TOK_NUM;
|
||||||
} else if (ch == '\'') {
|
} else if (ch == '\'') {
|
||||||
|
char_const:
|
||||||
minp();
|
minp();
|
||||||
tokc = getq();
|
tokc = getq();
|
||||||
tok = TOK_CCHAR;
|
tok = TOK_CCHAR;
|
||||||
@ -959,6 +1012,7 @@ void next_nomacro1()
|
|||||||
expect("\'");
|
expect("\'");
|
||||||
minp();
|
minp();
|
||||||
} else if (ch == '\"') {
|
} else if (ch == '\"') {
|
||||||
|
str_const:
|
||||||
minp();
|
minp();
|
||||||
q = token_buf;
|
q = token_buf;
|
||||||
while (ch != '\"') {
|
while (ch != '\"') {
|
||||||
@ -1203,7 +1257,10 @@ void macro_subst(int **tok_str, int *tok_len,
|
|||||||
len = 0;
|
len = 0;
|
||||||
str = NULL;
|
str = NULL;
|
||||||
parlevel = 0;
|
parlevel = 0;
|
||||||
while ((parlevel > 0 || (tok != ')' && tok != ',')) &&
|
while ((parlevel > 0 ||
|
||||||
|
(tok != ')' &&
|
||||||
|
(tok != ',' ||
|
||||||
|
sa->v == (TOK___VA_ARGS__ | SYM_FIELD)))) &&
|
||||||
tok != -1) {
|
tok != -1) {
|
||||||
if (tok == '(')
|
if (tok == '(')
|
||||||
parlevel++;
|
parlevel++;
|
||||||
@ -1916,7 +1973,7 @@ void inc(post, c)
|
|||||||
int struct_decl(u)
|
int struct_decl(u)
|
||||||
{
|
{
|
||||||
int a, t, b, v, size, align, maxalign, c;
|
int a, t, b, v, size, align, maxalign, c;
|
||||||
Sym *slast, *s, *ss;
|
Sym *s, *ss, **ps;
|
||||||
|
|
||||||
a = tok; /* save decl type */
|
a = tok; /* save decl type */
|
||||||
next();
|
next();
|
||||||
@ -1945,7 +2002,7 @@ int struct_decl(u)
|
|||||||
/* cannot be empty */
|
/* cannot be empty */
|
||||||
c = 0;
|
c = 0;
|
||||||
maxalign = 0;
|
maxalign = 0;
|
||||||
slast = NULL;
|
ps = &s->next;
|
||||||
while (1) {
|
while (1) {
|
||||||
if (a == TOK_ENUM) {
|
if (a == TOK_ENUM) {
|
||||||
v = tok;
|
v = tok;
|
||||||
@ -1978,8 +2035,8 @@ int struct_decl(u)
|
|||||||
}
|
}
|
||||||
if (align > maxalign)
|
if (align > maxalign)
|
||||||
maxalign = align;
|
maxalign = align;
|
||||||
ss->next = slast;
|
*ps = ss;
|
||||||
slast = ss;
|
ps = &ss->next;
|
||||||
if (tok == ';' || tok == -1)
|
if (tok == ';' || tok == -1)
|
||||||
break;
|
break;
|
||||||
skip(',');
|
skip(',');
|
||||||
@ -1990,7 +2047,6 @@ int struct_decl(u)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
skip('}');
|
skip('}');
|
||||||
s->next = slast;
|
|
||||||
/* size for struct/union, dummy for enum */
|
/* size for struct/union, dummy for enum */
|
||||||
s->c = (c + maxalign - 1) & -maxalign;
|
s->c = (c + maxalign - 1) & -maxalign;
|
||||||
}
|
}
|
||||||
@ -2215,13 +2271,13 @@ void unary()
|
|||||||
} else if (tok == TOK___FUNC__) {
|
} else if (tok == TOK___FUNC__) {
|
||||||
/* special function name identifier */
|
/* special function name identifier */
|
||||||
/* generate (char *) type */
|
/* generate (char *) type */
|
||||||
vset(VT_CONST | mk_pointer(VT_TYPE), glo);
|
vset(VT_CONST | mk_pointer(VT_BYTE), glo);
|
||||||
strcpy((void *)glo, funcname);
|
strcpy((void *)glo, funcname);
|
||||||
glo += strlen(funcname) + 1;
|
glo += strlen(funcname) + 1;
|
||||||
} else if (tok == TOK_STR) {
|
} else if (tok == TOK_STR) {
|
||||||
TokenSym *ts;
|
TokenSym *ts;
|
||||||
/* generate (char *) type */
|
/* generate (char *) type */
|
||||||
vset(VT_CONST | mk_pointer(VT_TYPE), glo);
|
vset(VT_CONST | mk_pointer(VT_BYTE), glo);
|
||||||
while (tok == TOK_STR) {
|
while (tok == TOK_STR) {
|
||||||
ts = (TokenSym *)tokc;
|
ts = (TokenSym *)tokc;
|
||||||
memcpy((void *)glo, ts->str, ts->len);
|
memcpy((void *)glo, ts->str, ts->len);
|
||||||
@ -2296,7 +2352,7 @@ void unary()
|
|||||||
s = sym_find(t);
|
s = sym_find(t);
|
||||||
if (!s) {
|
if (!s) {
|
||||||
if (tok != '(')
|
if (tok != '(')
|
||||||
error("undefined symbol");
|
error("'%s' undeclared", get_tok_str(t, 0));
|
||||||
/* for simple function calls, we tolerate undeclared
|
/* for simple function calls, we tolerate undeclared
|
||||||
external reference */
|
external reference */
|
||||||
p = anon_sym++;
|
p = anon_sym++;
|
||||||
@ -2637,9 +2693,10 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg)
|
|||||||
next();
|
next();
|
||||||
/* declarations */
|
/* declarations */
|
||||||
s = local_stack;
|
s = local_stack;
|
||||||
|
while (tok != '}') {
|
||||||
decl(VT_LOCAL);
|
decl(VT_LOCAL);
|
||||||
while (tok != '}')
|
|
||||||
block(bsym, csym, case_sym, def_sym, case_reg);
|
block(bsym, csym, case_sym, def_sym, case_reg);
|
||||||
|
}
|
||||||
/* pop locally defined symbols */
|
/* pop locally defined symbols */
|
||||||
sym_pop(&local_stack, s);
|
sym_pop(&local_stack, s);
|
||||||
next();
|
next();
|
||||||
@ -2794,10 +2851,164 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* t is the array or struct type. c is the array or struct
|
||||||
|
address. cur_index/cur_field is the pointer to the current value */
|
||||||
|
void decl_designator(int t, int c, int *cur_index, Sym **cur_field)
|
||||||
|
{
|
||||||
|
Sym *s, *f;
|
||||||
|
int notfirst, index, align;
|
||||||
|
|
||||||
|
notfirst = 0;
|
||||||
|
while (tok == '[' || tok == '.') {
|
||||||
|
if (tok == '[') {
|
||||||
|
if (!(t & VT_ARRAY))
|
||||||
|
expect("array type");
|
||||||
|
s = sym_find(((unsigned)t >> VT_STRUCT_SHIFT));
|
||||||
|
next();
|
||||||
|
index = expr_const();
|
||||||
|
if (index < 0 || (s->c >= 0 && index >= s->c))
|
||||||
|
expect("invalid index");
|
||||||
|
skip(']');
|
||||||
|
if (!notfirst)
|
||||||
|
*cur_index = index;
|
||||||
|
t = pointed_type(t);
|
||||||
|
c += index * type_size(t, &align);
|
||||||
|
} else {
|
||||||
|
if (!(t & VT_STRUCT))
|
||||||
|
expect("struct/union type");
|
||||||
|
next();
|
||||||
|
s = sym_find(((unsigned)t >> VT_STRUCT_SHIFT) | SYM_STRUCT);
|
||||||
|
tok |= SYM_FIELD;
|
||||||
|
f = s->next;
|
||||||
|
while (f) {
|
||||||
|
if (f->v == tok)
|
||||||
|
break;
|
||||||
|
f = f->next;
|
||||||
|
}
|
||||||
|
if (!f)
|
||||||
|
expect("field");
|
||||||
|
next();
|
||||||
|
if (!notfirst)
|
||||||
|
*cur_field = f;
|
||||||
|
t = f->t | (t & VT_TYPEN);
|
||||||
|
c += f->c;
|
||||||
|
}
|
||||||
|
notfirst = 1;
|
||||||
|
}
|
||||||
|
if (notfirst) {
|
||||||
|
skip('=');
|
||||||
|
} else {
|
||||||
|
if (t & VT_ARRAY) {
|
||||||
|
index = *cur_index;
|
||||||
|
t = pointed_type(t);
|
||||||
|
c += index * type_size(t, &align);
|
||||||
|
} else {
|
||||||
|
f = *cur_field;
|
||||||
|
if (!f)
|
||||||
|
error("too many field init");
|
||||||
|
t = f->t | (t & VT_TYPEN);
|
||||||
|
c += f->c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
decl_assign(t, c, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 't' contains the type and storage info. c is the address of the
|
||||||
|
object. 'first' is true if array '{' must be read (multi dimension
|
||||||
|
implicit array init handling). */
|
||||||
|
void decl_assign(int t, int c, int first)
|
||||||
|
{
|
||||||
|
int v, index, index_max, t1, n, no_oblock;
|
||||||
|
Sym *s, *f;
|
||||||
|
TokenSym *ts;
|
||||||
|
|
||||||
|
if (t & VT_ARRAY) {
|
||||||
|
s = sym_find(((unsigned)t >> VT_STRUCT_SHIFT));
|
||||||
|
n = s->c;
|
||||||
|
index_max = 0;
|
||||||
|
if (tok == TOK_STR) {
|
||||||
|
t1 = pointed_type(t);
|
||||||
|
if (!(t1 & VT_BYTE))
|
||||||
|
error("invalid type");
|
||||||
|
if ((t & VT_VALMASK) == VT_CONST) {
|
||||||
|
while (tok == TOK_STR) {
|
||||||
|
ts = (TokenSym *)tokc;
|
||||||
|
memcpy((void *)c, ts->str, ts->len);
|
||||||
|
c += ts->len;
|
||||||
|
index_max += ts->len;
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
*(char *)c++ = 0;
|
||||||
|
} else {
|
||||||
|
error("local string init not handled");
|
||||||
|
}
|
||||||
|
/* string init */
|
||||||
|
} else {
|
||||||
|
no_oblock = 0;
|
||||||
|
if (!first && tok != '{')
|
||||||
|
no_oblock = 1;
|
||||||
|
else
|
||||||
|
skip('{');
|
||||||
|
index = 0;
|
||||||
|
while (tok != '}') {
|
||||||
|
decl_designator(t, c, &index, NULL);
|
||||||
|
if (n >= 0 && index >= n)
|
||||||
|
error("index too large");
|
||||||
|
if (index > index_max)
|
||||||
|
index_max = index;
|
||||||
|
index++;
|
||||||
|
/* special test for multi dimensional arrays (may not
|
||||||
|
be strictly correct if designators are used at the
|
||||||
|
same time) */
|
||||||
|
if (index >= n && no_oblock)
|
||||||
|
break;
|
||||||
|
if (tok == '}')
|
||||||
|
break;
|
||||||
|
skip(',');
|
||||||
|
}
|
||||||
|
if (!no_oblock)
|
||||||
|
skip('}');
|
||||||
|
}
|
||||||
|
/* patch type size if needed */
|
||||||
|
if (n < 0)
|
||||||
|
s->c = index_max + 1;
|
||||||
|
} else if (t & VT_STRUCT) {
|
||||||
|
/* XXX: union needs only one init */
|
||||||
|
skip('{');
|
||||||
|
s = sym_find(((unsigned)t >> VT_STRUCT_SHIFT) | SYM_STRUCT);
|
||||||
|
f = s->next;
|
||||||
|
while (tok != '}') {
|
||||||
|
decl_designator(t, c, NULL, &f);
|
||||||
|
if (tok == '}')
|
||||||
|
break;
|
||||||
|
skip(',');
|
||||||
|
f = f->next;
|
||||||
|
}
|
||||||
|
skip('}');
|
||||||
|
} else {
|
||||||
|
if ((t & VT_VALMASK) == VT_CONST) {
|
||||||
|
v = expr_const();
|
||||||
|
printf("v=%d\n", v);
|
||||||
|
if (t & VT_BYTE)
|
||||||
|
*(char *)c = v;
|
||||||
|
else if (t & VT_SHORT)
|
||||||
|
*(short *)c = v;
|
||||||
|
else
|
||||||
|
*(int *)c = v;
|
||||||
|
} else {
|
||||||
|
vt = t;
|
||||||
|
vc = c;
|
||||||
|
vpush();
|
||||||
|
expr_eq();
|
||||||
|
vstore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 'l' is VT_LOCAL or VT_CONST to define default storage type */
|
/* 'l' is VT_LOCAL or VT_CONST to define default storage type */
|
||||||
void decl(l)
|
void decl(l)
|
||||||
{
|
{
|
||||||
int *a, t, b, size, align, v, u, n;
|
int *a, t, b, size, align, v, u, n, addr;
|
||||||
Sym *sym;
|
Sym *sym;
|
||||||
|
|
||||||
while (b = ist()) {
|
while (b = ist()) {
|
||||||
@ -2866,16 +3077,37 @@ void decl(l)
|
|||||||
u = VT_CONST;
|
u = VT_CONST;
|
||||||
u |= t;
|
u |= t;
|
||||||
size = type_size(t, &align);
|
size = type_size(t, &align);
|
||||||
|
if ((u & VT_VALMASK) == VT_LOCAL) {
|
||||||
|
/* XXX: cannot use implicit size for local
|
||||||
|
storage */
|
||||||
|
if (size < 0)
|
||||||
|
error("size must be known for locals");
|
||||||
|
loc = (loc - size) & -align;
|
||||||
|
addr = loc;
|
||||||
|
} else {
|
||||||
|
glo = (glo + align - 1) & -align;
|
||||||
|
addr = glo;
|
||||||
|
}
|
||||||
|
if (tok == '=') {
|
||||||
|
next();
|
||||||
|
/* special case for non array types */
|
||||||
|
n = 0;
|
||||||
|
if (tok == '{' && (u & (VT_ARRAY | VT_STRUCT)) == 0) {
|
||||||
|
n = 1;
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
decl_assign(u, addr, 1);
|
||||||
|
if (n)
|
||||||
|
skip('}');
|
||||||
|
}
|
||||||
|
sym_push(v, u, addr);
|
||||||
|
/* if global, add size */
|
||||||
|
if ((u & VT_VALMASK) == VT_CONST) {
|
||||||
|
/* must recompute size if it was an array
|
||||||
|
with implicit size */
|
||||||
|
size = type_size(t, &align);
|
||||||
if (size < 0)
|
if (size < 0)
|
||||||
error("invalid size");
|
error("invalid size");
|
||||||
if ((u & VT_VALMASK) == VT_LOCAL) {
|
|
||||||
/* allocate space down on the stack */
|
|
||||||
loc = (loc - size) & -align;
|
|
||||||
sym_push(v, u, loc);
|
|
||||||
} else {
|
|
||||||
/* allocate space up in the data space */
|
|
||||||
glo = (glo + align - 1) & -align;
|
|
||||||
sym_push(v, u, glo);
|
|
||||||
glo += size;
|
glo += size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2916,7 +3148,7 @@ int main(int argc, char **argv)
|
|||||||
nb_include_paths = 3;
|
nb_include_paths = 3;
|
||||||
|
|
||||||
/* add all tokens */
|
/* add all tokens */
|
||||||
p = "int\0void\0char\0if\0else\0while\0break\0return\0for\0extern\0static\0unsigned\0goto\0do\0continue\0switch\0case\0const\0volatile\0long\0register\0signed\0auto\0inline\0restrict\0float\0double\0short\0struct\0union\0typedef\0default\0enum\0sizeof\0define\0include\0ifdef\0ifndef\0elif\0endif\0defined\0undef\0error\0__LINE__\0__FILE__\0__DATE__\0__TIME__\0__func__\0main\0";
|
p = "int\0void\0char\0if\0else\0while\0break\0return\0for\0extern\0static\0unsigned\0goto\0do\0continue\0switch\0case\0const\0volatile\0long\0register\0signed\0auto\0inline\0restrict\0float\0double\0short\0struct\0union\0typedef\0default\0enum\0sizeof\0define\0include\0ifdef\0ifndef\0elif\0endif\0defined\0undef\0error\0line\0__LINE__\0__FILE__\0__DATE__\0__TIME__\0__VA_ARGS__\0__func__\0main\0";
|
||||||
while (*p) {
|
while (*p) {
|
||||||
r = p;
|
r = p;
|
||||||
while (*r++);
|
while (*r++);
|
||||||
|
Loading…
Reference in New Issue
Block a user