/*
* Copyright (c) 2006, Nils R. Weller
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Macro storage and substitution
*/
#include "macros.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include "token.h"
#include "error.h"
#include "n_libc.h"
#include "preprocess.h"
struct macro *
alloc_macro(void) {
struct macro *ret = n_xmalloc(sizeof *ret);
static struct macro nullmacro;
*ret = nullmacro;
return ret;
}
/* XXX make hash table */
static struct macro *macro_list;
static struct macro *macro_list_tail;
#define N_HASHLISTS 80
static char macro_len_tab[N_HASHLISTS];
static struct macro *macro_list_hash[N_HASHLISTS];
static struct macro *macro_list_hash_tail[N_HASHLISTS];
static struct macro *
do_lookup_macro(const char *name, size_t len, struct macro **prev, int *idx) {
struct macro *m;
size_t cmplen;
int firstch;
int lastch;
int hashidx;
size_t lastch_index;
if (len == 0) {
len = strlen(name);
}
lastch_index = len - 1;
if (len < sizeof macro_len_tab) {
if (macro_list_hash[len] == NULL) {
/*
* No macro of this length defined! (it
* seems that only 1- and 2-byte-sized
* macro names are very likely never to
* occur)
*/
return NULL;
}
hashidx = len;
} else {
hashidx = N_HASHLISTS - 1;
}
*idx = hashidx;
firstch = *name;
lastch = name[lastch_index];
*prev = NULL;
for (m = macro_list_hash[hashidx]; m != NULL; m = m->next) {
if (m->name == NULL) { /* XXX bug?!!?? */
*prev = m;
continue;
}
if (m->namelen != len) {
/* May happen with macro_hash_list[N_HASHLISTS - 1] */
continue;
}
if (firstch == *m->name
&& lastch == m->name[lastch_index]) {
if (memcmp(m->name, name, len) == 0) {
return m;
}
}
*prev = m;
}
return NULL;
}
struct macro *
lookup_macro(const char *name, size_t len) {
struct macro *prev;
int idx;
return do_lookup_macro(name, len, &prev, &idx);
}
struct macro *
put_macro(struct macro *new_macro) {
struct macro *m;
size_t len = strlen(new_macro->name);
new_macro->namelen = len;
if (strcmp(new_macro->name, "defined") == 0) {
lexerror("`defined' is not an allowed macro name");
return NULL;
}
if ((m = lookup_macro(new_macro->name, len)) != NULL) {
/*
* Oops - already defined. This is OK if all
* tokens are the same only - Let's compare!
*/
struct token *ot = m->toklist;
struct token *nt = new_macro->toklist;
int bad = 0;
for (; ot != NULL && nt != NULL;
ot = ot->next, nt = nt->next) {
/*
* XXX Whitespace probably shouldn't be
* recorded at all. Above we should cut
* all outer WS from every new define
*/
while (ot && ot->type == TOK_WS) {
ot = ot->next;
}
while (nt && nt->type == TOK_WS) {
nt = nt->next;
}
if (ot == NULL || nt == NULL) {
break;
}
if (ot->type != nt->type) {
bad = 1;
break;
} /* else if (strcmp(ot->ascii,
nt->ascii) != 0) { */
}
while (ot && ot->type == TOK_WS) ot = ot->next;
while (nt && nt->type == TOK_WS) nt = nt->next;
if (ot || nt) {
bad = 1;
}
if (bad) {
lexerror("Redefinition of macro `%s' "
"with different body",
m->name);
dump_toklist(m->toklist);
dump_toklist(new_macro->toklist);
exit(-rand());
free(new_macro->name);
free(new_macro);
return NULL;
}
/* Redefinition with same value - OK */
return m;
}
if (len < sizeof macro_len_tab) {
macro_len_tab[len] = 1;
}
if (len >= N_HASHLISTS) {
/*
* All macro names longer than N_HASHLISTS map to the last
* list
*/
len = N_HASHLISTS - 1;
}
if (macro_list_hash_tail[len] == NULL) {
macro_list_hash_tail[len] = macro_list_hash[len] = new_macro;
} else {
macro_list_hash_tail[len]->next = new_macro;
macro_list_hash_tail[len] = new_macro;
}
return new_macro;
}
int
drop_macro(const char *name) {
struct macro *m;
struct macro *prevm;
int hashidx;
if ((m = do_lookup_macro(name, 0, &prevm, &hashidx)) == NULL) {
return -1;
}
/* found - remove! */
if (prevm == NULL) {
/* Head of list */
macro_list_hash[hashidx] = macro_list_hash[hashidx]->next;
if (m == macro_list_hash_tail[hashidx]) {
/* Only node */
macro_list_hash_tail[hashidx] = NULL;
}
} else {
prevm->next = m->next;
if (m == macro_list_hash_tail[hashidx]) {
/* Last node */
macro_list_hash_tail[hashidx] = prevm;
}
}
if (m->builtin) {
lexwarning("Undefining predefined macro `%s'",
m->name);
}
free(m->name);
/*free_token_list(m->toklist);*/
free(m);
return 0;
}
struct token *
builtin_to_tok(struct macro *m, struct token **tail) {
struct token *ret = alloc_token();
if (strcmp(m->name, "__FILE__") == 0) {
ret->type = TOK_IDENTIFIER;
ret->data = ret->ascii = m->builtin;
} else if (strcmp(m->name, "__LINE__") == 0) {
ret->type = TY_INT;
ret->ascii = n_xmalloc(32);
sprintf(ret->ascii, "%d", *(int *)m->builtin);
ret->data = n_xmemdup(m->builtin, sizeof(int));
} else {
printf("BUG: Unknown builtin macro `%s'\n", m->name);
abort();
}
if (tail != NULL) {
*tail = ret;
}
return ret;
}
/*
* XXX this stuff is JUNK! The correct solution is to set maybe_funclike
* in preprocess() and ``see what happens''
*/
static int
check_funclike(FILE *in, FILE *out, struct macro *mp, int output) {
char buf[256]; /* XXX */
char *p = buf;
int ch;
size_t old_lex_chars_read = 0;
void *old_line_ptr = NULL;
fpos_t pos;
if (in == NULL) { /* XXX */
return 0;
}
if (!output) {
#define RESTORE() do { \
lex_chars_read = old_lex_chars_read; \
lex_line_ptr = old_line_ptr; \
fsetpos(in, &pos); \
} while (0)
old_lex_chars_read = lex_chars_read;
old_line_ptr = lex_line_ptr;
fgetpos(in, &pos);
}
/*
* Function-like macro
* invocation if next token
* is an opening parenthesis
*/
while ((ch = FGETC(in)) != EOF) {
/* XXX this junk shit crap doesn't handle \n right :( */
if (ch == '(') {
/* YES, is function-like! */
return 1;
} else if (!isspace(ch)) {
*p = 0;
UNGETC(ch, in);
if (!output) {
RESTORE();
} else {
x_fprintf(out, "%s%s", mp->name, buf);
}
return 0;
} else {
*p++ = ch;
}
}
*p = 0;
if (!output) {
RESTORE();
} else {
x_fprintf(out, "%s%s", mp->name, buf);
}
return 0;
}
static int
store_macro_arg(
struct macro *m,
struct macro_arg **cma0,
struct token *toklist,
int ends) {
struct macro_arg *cur_macro_arg = *cma0;
if (cur_macro_arg != NULL) {
/*
* Not all parameters in the list have
* been assigned
*/
cur_macro_arg->toklist = toklist;
if (toklist == NULL) {
lexerror("Empty macro argument");
return -1;
}
if (cur_macro_arg->next != NULL && ends) {
lexerror("Not enough arguments for "
"function-like macro");
return -1;
}
} else {
if (toklist != NULL) {
lexerror("Too many arguments for "
"function-like macro %s", m->name);
return -1;
}
}
if (ends) {
*cma0 = NULL;
} else {
*cma0 = (*cma0)->next;
}
return 0;
}
int collect_parens;
static int
collect_args(struct macro *mp, struct token **tok, int *err,
struct token **result, struct token **result_tail) {
struct token *t = *tok;
struct token *toklist = NULL;
struct token *toklist_tail = NULL;
struct macro_arg *ma = mp->arglist;
int doing_trailing_last = 0;
collect_parens = 1;
*err = 0;
/* Skip identifier and ( */
(void) next_token(&t);
(void) next_token(&t);
for (;;) {
/*
* Note that arguments to function-like macros are not
* processed before being passed (at least in GNU cpp
* and ucpp), such that;
*
* #define foo 1, 2
* #define bar(x, y) x * y
* bar(foo)
*
* ... doesn't work!
*/
if (mp->trailing_last == ma) {
doing_trailing_last = 1;
}
if (t->type == TOK_PAREN_CLOSE) {
if (--collect_parens == 0) {
store_macro_arg(mp, &ma, toklist, 1);
break;
}
append_token_copy(&toklist, &toklist_tail, t);
} else if (t->type == TOK_PAREN_OPEN) {
++collect_parens;
append_token_copy(&toklist, &toklist_tail, t);
} else if (t->type == TOK_OPERATOR
&& *(int *)t->data == TOK_OP_COMMA) {
if (collect_parens == 1 && !doing_trailing_last) {
store_macro_arg(mp, &ma, toklist, 0);
toklist = toklist_tail = NULL;
} else {
append_token_copy(&toklist, &toklist_tail, t);
}
} else {
if (t->type == TOK_WS) {
/*
* Whitespace before and after arguments
* is ignored
*/
if (t->next != NULL && toklist != NULL) {
if (t->next->type == TOK_OPERATOR
&& *(int *)t->next->data
== TOK_OP_COMMA) {
; /* ignore */
} else if (t->next->type ==
TOK_PAREN_CLOSE) {
; /* ignore */
} else {
append_token_copy(&toklist,
&toklist_tail, t);
}
}
} else {
append_token_copy(&toklist, &toklist_tail, t);
}
}
if (next_token(&t) != 0) {
/*
* Return and have main loop read remaining tokens.
* This is infinitely inferior to having a get_token()
* function :-(
*/
struct token *newlist = NULL;
struct token *newlist_tail = NULL;
for (t = *tok; t != NULL; t = t->next) {
append_token_copy(&newlist, &newlist_tail, t);
}
if (newlist) {
if (*result) {
(*result_tail)->next = newlist;
*result_tail = newlist_tail;
} else {
*result = newlist;
*result_tail = newlist_tail;
}
}
return -1;
}
}
*tok = t;
return 0;
}
struct token *
skip_ws(struct token *t) {
for (; t != NULL; t = t->next) {
if (t->type == TOK_WS) {
; /* ignore */
} else {
return t;
}
}
return NULL;
}
static struct token *
skip_ws_backwards(struct token *t) {
for (; t != NULL; t = t->prev) {
if (t->type == TOK_WS) {
; /* ignore */
} else {
return t;
}
}
return NULL;
}
static struct token *
find_hashhash(struct token *t) {
if ((t = skip_ws(t)) != NULL) {
if (t->type == TOK_HASHHASH) {
return t;
}
}
return NULL;
}
static struct token *
do_hashhash(
struct token **ret, struct token **ret_tail,
struct token *t, struct token *dt) {
struct token *t2;
struct token *successor;
char *p;
if ((t2 = skip_ws(dt->next)) == NULL) {
lexerror("Invalid `##' at end of macro "
"expansion");
return NULL;
}
if (t2->maps_to_arg != NULL) {
successor = skip_ws(t2->maps_to_arg->toklist);
} else {
successor = t2;
}
assert(successor != NULL);
p = n_xmalloc(strlen(t->ascii) +
strlen(successor->ascii)+1);
sprintf(p, "%s%s", t->ascii, successor->ascii);
store_token(ret, ret_tail, p, TOK_IDENTIFIER, 0);
/*
* Write out rest of b token list in ``a ## b''. Easier than
* setting flags and stuff in main loop
*/
if (t2->maps_to_arg) {
for (successor = successor->next;
successor != NULL;
successor = successor->next) {
append_token_copy(ret, ret_tail, successor);
}
}
return t2;
}
static int
fully_expand_macro_list(FILE *in, struct token **toklist, struct token **tres,
int dontoutput, struct token **tailp);
/*
* Expands macro body to token list and returns it. mp->args must be set
* to the arguments of the current instantiation!
*/
static struct token *
expand_function_macro(struct macro *mp, struct token **tail) {
struct macro_arg *ma;
struct token *t;
struct token *t2;
struct token *dt;
struct token *ret = NULL;
struct token *ret_tail = NULL;
char *p;
for (t = mp->toklist; t != NULL; t = t->next) {
if (t->type == TOK_HASH) {
size_t size = 0;
size_t len = 0;
if (t->next == NULL
|| (t = skip_ws(t->next)) == NULL
|| t->maps_to_arg == NULL) {
lexerror("`#' not followed by macro parameter");
return NULL;
}
/*
* Stringizing token list - be careful to
* escape all " characters!
*/
#if 0
t = t->next;
#endif
p = NULL;
store_char(NULL, 0);
store_char(&p, '"');
for (t2 = t->maps_to_arg->toklist;
t2 != NULL;
t2 = t2->next) {
char *p2 = t2->ascii;
for (; *p2 != 0; ++p2) {
if (*p2 == '"') {
store_char(&p, '\\');
}
store_char(&p, *p2);
}
}
store_char(&p, '"');
store_char(&p, 0);
store_token(&ret, &ret_tail, p,
TOK_STRING_LITERAL, 0);
} else if (t->maps_to_arg != NULL) {
struct token *last_non_white = NULL;
int not_done;
struct token *t_res;
struct token *t2_res;
struct token *tailp;
/*
* Expanding a macro parameter invocation.
* It is quite important to expand any macros
* in an argument token list because we need
* to make e.g. this work:
*
* #define foo(x) #x
* #define bar(x) foo(x)
* #define baz 123
* bar(baz);
*
* foo() has to receive expanded tokens, i.e.
* it may not see the macro ``baz''. Otherwise
* #x would yield "baz"
*/
t2_res = t->maps_to_arg->toklist;
not_done = fully_expand_macro_list(NULL, &t2_res,
&t_res, 1, &tailp);
#if 0
for (t2 = t->maps_to_arg->toklist;
t2 != NULL;
t2 = t2->next) {
append_token_copy(&ret, &ret_tail, t2);
}
#endif
for (t2 = t2_res; t2 != NULL; t2 = t2->next) {
append_token_copy(&ret, &ret_tail, t2);
}
#if 0
if (t2_res != NULL) {
if (ret == NULL) {
ret = t2_res;
} else {
ret_tail->next = t2_res;
}
if (tailp != NULL) {
ret_tail = tailp;
}
}
#endif
#if 0
kpkpkp
#define foo(x) #x
#define bar(y) foo(y)
#define baz() hehe
bar(baz())
#endif
} else {
append_token_copy(&ret, &ret_tail, t);
}
}
*tail = ret_tail;
return ret;
}
/*
* It is important that the same macro is not expanded recursively in
* an expansion chain. Consider:
*
* #define x y
* #define y x
* int x;
*
* ... here x expands to y, y expands to x, and x remains x because
* multiple expansions are not allowed. This is a little difficult
* to get right. Consider the token list:
*
* foo,foo,foo,foo
*
* ... where foo is a macro. In expand_token_list(), the ``dontexpand''
* flag in foo's ``struct macro'' must be set before a recursive call
* to itself, and unset afterwards, such that the next occurances of
* foo are expanded correctly.
*
* Invoked macros are recorded in the expanded_macros array. Before a
* token list is a reprocessed, do_macro_subst() needs this to re-mark
* all invoked macros as unexpandable, and when all is done,
* expandable.
*/
#if 0
static struct macro **expanded_macros;
static int expanded_macros_alloc;
static int expanded_macros_idx;
#endif
static void
mark_not_expandable(struct token *t, struct macro *m) {
#if 0
m->dontexpand = 1;
#endif
if (t->expanded_macros_idx >= t->expanded_macros_alloc) {
if (t->expanded_macros_alloc == 0) {
t->expanded_macros_alloc = 16;
} else {
t->expanded_macros_alloc *= 2;
}
#if 0
expanded_macros = n_xmalloc(expanded_macros_alloc *
sizeof *expanded_macros);
#endif
t->expanded_macros = n_xrealloc(t->expanded_macros,
t->expanded_macros_alloc *
sizeof *t->expanded_macros);
}
t->expanded_macros[t->expanded_macros_idx++] = m;
}
static int
check_dontexpand(struct token *t, struct macro *m) {
int i;
for (i = 0; i < t->expanded_macros_idx; ++i) {
if (t->expanded_macros[i] == m) {
return 1;
}
}
return 0;
}
#if 0
static void
mark_all_unexpandable(void) {
int i;
for (i = 0; i < expanded_macros_idx; ++i) {
expanded_macros[i]->dontexpand = 1;
}
}
#endif
static void
mark_all_expandable(struct token *t) {
int i;
for (i = 0; i < t->expanded_macros_idx; ++i) {
t->expanded_macros[i]->dontexpand = 0;
}
t->expanded_macros_idx = 0;
}
/*
* XXX if we are to keep the concept of ``clean pass'', then tokens should
* be copied copy-on-write; ie. copy only if a replacement takes place!!!
*/
static struct token *
expand_macro_list(
FILE *in,
struct token *list,
struct token **mtail,
int *clean_pass,
int *not_done,
int dontoutput, /* for #if/#elif */
struct token **tailp) {
struct token *t;
struct token *t2;
struct token *result = NULL;
struct token *ctail = NULL;
struct token *newtail;
struct token *expres;
struct token *dt;
for (t = list; t != NULL; t = t->next) {
struct macro *mp;
if (t->type != TOK_WS
&& (dt = find_hashhash(t->next)) != NULL) {
t = do_hashhash(&result, &ctail, t, dt);
if (clean_pass) *clean_pass = 0;
continue;
} else if (t->type != TOK_IDENTIFIER) {
append_token_copy(&result, &ctail, t);
continue;
} else if (strcmp(t->data, "defined") == 0) {
append_token_copy(&result, &ctail, t);
if (!dontoutput) {
continue;
}
t2 = skip_ws(t->next);
if (t2 && t2->type == TOK_PAREN_OPEN) {
struct token *t3 = skip_ws(t2->next);
if (t3 && t3->type == TOK_IDENTIFIER) {
struct token *t4 = skip_ws(t3->next);
if (t4 && t4->type == TOK_PAREN_CLOSE) {
/*
* Valid defined(ident). Invalid
* ones are ignored because
* get_sub_expr() catches them
* anyway
*/
append_token_copy(&result,
&ctail, t2);
append_token_copy(&result,
&ctail, t3);
append_token_copy(&result,
&ctail, t4);
t = t4;
}
}
} else if (t2 && t2->type == TOK_IDENTIFIER) {
/*
* defined ident
*/
append_token_copy(&result, &ctail, t2);
t = t2;
}
continue;
}
mp = lookup_macro(t->ascii, 0);
if (mp != NULL
&& !mp->dontexpand
&& !check_dontexpand(t, mp)) {
expres = NULL;
if (mp->functionlike) {
struct token *t2;
if (t->next == NULL) {
/*
* Corner case - result of macro
* expansion may or may not introduce
* invocation of function-like macro;
*
* #define foo(x) x * x
* #define bar foo
* bar(123)
*
* We have to peak at the stream!
*/
append_token_copy(&result, &ctail, t);
/* XXX check_funclike SUX */
if (check_funclike(in, NULL, mp, 0)
== 1) {
t = store_token(&result, &ctail,
0, TOK_PAREN_OPEN,
t->line);
collect_parens = 1;
*not_done = 1;
*tailp = ctail;
if (mtail) *mtail = ctail;
return result;
}
break;
}
if ((t2 = skip_ws(t->next)) != NULL
&& t2->type == TOK_PAREN_OPEN) {
/* Invocation! */
int err;
t = t2->prev;
if (clean_pass) *clean_pass = 0;
if (collect_args(mp, &t, &err, &result,
tailp) != 0) {
if (!err) {
/*
* Return what we got
* so far
*/
if (mtail != NULL) {
/* XXX */
*mtail = *tailp;
}
*not_done = 1;
return result;
}
if (mtail != NULL) {
/* XXX */
*mtail = NULL;
}
return NULL;
}
mp->dontexpand = 1;
expres = expand_function_macro(mp,
&newtail);
mp->dontexpand = 0;
} else {
t = t->next;
append_token_copy(&result, &ctail, t);
t = t->prev;
}
} else {
if (mp->builtin) {
/* __FILE__ or __LINE__ */
expres = builtin_to_tok(mp, &newtail);
} else {
if (clean_pass) *clean_pass = 0;
mp->dontexpand = 1;
expres = expand_macro_list(in,
mp->toklist, &newtail, NULL,
not_done, dontoutput, tailp);
mp->dontexpand = 0;
}
}
if (expres != NULL) {
/*
* Expansion yielded tokens. Now we have to
* record which macro these tokens came from,
* such that the same macro is not expanded
* recursively
*/
for (t2 = expres; t2 != NULL; t2 = t2->next) {
/*
* XXX hmm mark_not_expandable calls
* realloc, but the pointer to be
* realloced will be shared after
* append_token_copy(). this ok?!?
*/
mark_not_expandable(t2, mp);
}
if (result == NULL) {
result = expres;
} else {
ctail->next = expres;
}
ctail = newtail;
}
} else {
append_token_copy(&result, &ctail, t);
}
}
if (mtail != NULL) {
*mtail = ctail;
}
return result;
}
/*
* XXX this interface kinda sucks big time..but the loop is used more than
* once!
* ``in'' - file to read more data from if needed (NULL for none)
* ``toklist'' - list as input, last valid expansion as output (!not_done)
* ``tres'' - as output the expanded result list
* ``dontoutput''
* ``in_tailp'' - input tail pointer corresponding to toklist
* ``out_tailp'' - output tail pointer
* XXX I'm not sure why expand_macro_list() separates between
*/
static int
fully_expand_macro_list(FILE *in, struct token **toklist, struct token **tres,
int dontoutput, struct token **tailp) {
int clean_pass;
int not_done;
struct token *t;
struct token *t2 = *toklist;
struct token *new_tail = NULL;
do {
clean_pass = 1;
not_done = 0;
t = expand_macro_list(in, t2, &new_tail, &clean_pass, ¬_done,
dontoutput, tailp);
/*mark_all_unexpandable();*/ /* Don't expand again */
if (!not_done /* && !clean_pass */ ) {
free_token_list(t2);
}
} while (!clean_pass && (t2 = t) && !not_done);
*toklist = t2;
*tres = t;
*tailp = new_tail;
return not_done;
}
struct token *
do_macro_subst(FILE *in, FILE *out,
struct token *toklist,
struct token **tailp,
int dontoutput) {
struct token *t;
struct token *t2;
int clean_pass;
int not_done;
if (g_ignore_text && !dontoutput) {
return NULL;
}
t2 = toklist;
#if 0
static int
fully_expand_macro_list(FILE *in, struct token *toklist,
int dontoutput, struct token **tailp) {
int clean_pass;
int not_done;
struct token *t;
struct token *t2 = toklist;
do {
clean_pass = 1;
not_done = 0;
t = expand_macro_list(in, t2, NULL, &clean_pass, ¬_done,
dontoutput, tailp);
/*mark_all_unexpandable();*/ /* Don't expand again */
if (!not_done /* && !clean_pass */ ) {
free_token_list(t2);
}
} while (!clean_pass && (t2 = t) && !not_done);
return not_done;
}
#endif
not_done = fully_expand_macro_list(in, &t2, &t, dontoutput, tailp);
/*mark_all_expandable();*/
if (not_done) {
/*
* Main loop has to read some more tokens for processing
*/
return t2? t2: toklist;
}
if (dontoutput) {
if (t == NULL) {
t = toklist;
}
return t;
}
for (t2 = t; t2 != NULL; t2 = t2->next) {
/*
* Ignore initial and final whitespace
*/
if (t2->type == TOK_WS) {
if (t2 == t || t2->next == NULL) {
continue;
}
}
x_fprintf(out, "%s", t2->ascii);
#if 0
static struct macro *
check_funclike(FILE *in, FILE *out, struct macro *mp) {
#endif
}
/* XXX remember we have to return a ``struct macro''
* if the last expanded token is possibly a function-like
* macro! but we do not know whether a ( will follow, so
* perhaps in the loop above we shouldn't output the last
* token if it is such an identifier, but instead return
* the ``struct tokne'', or else peek at the input
*/
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1