/* * Routines to implement a C preprocessor */ #include #include #include #include "ansi.h" #include "files.h" #include "hash.h" #include "host.h" #include "buffer.h" #include "cpp.h" #include "cpp_hide.h" #include "cpp_eval.h" #include "allocate.h" #include "config.h" extern int translate_comments; #undef NULL #define NULL 0L #ifndef MAX_SEARCH #define MAX_SEARCH 32 #endif static cpp_file_t *open_files; static scan_position_t *curpos; unsigned char cpp_char_class[128]; static char class_initialized; #define add(buf,c) buf_add(buf, (int)(c)) #define UNHANDLED() unhandled(__FILE__, __LINE__) static buffer_t cppbuf; static buffer_initialized; static char *search_paths[MAX_SEARCH]; static int search_index; /* * The following vars control the state of conditionals */ static cpp_control_state_t control_state; #define scanning() (control_state.cur_scope == control_state.gen_scope) #define skipping() (control_state.cur_scope != control_state.gen_scope) #define parsing() (control_state._parsing) #define want_comments(x) ((translate_comments) ? x : NULL) static char *orig_file; static char *last_file; static int last_line; #define FN fname() #define LN fline() static int grok_actual_param _ANSI_PROTO_((int)); static int scan_default _ANSI_PROTO_((buffer_t*,int)); static char* fname() { if (curpos == NULL) { assert(last_file != NULL); return last_file; } if (curpos->scan_pos == 0) return NULL; return file_name(curpos->scan_pos); } static int fline() { if (curpos == NULL) { assert(last_line != 0); return last_line; } return line_number(curpos->scan_pos); } static void range_check(n, max, msg) int n, max; char *msg; { if (n >= max) { fatal(FN, LN, "%s: %d > %d", msg, n+1, max); } } static void unhandled(file, line) char *file; int line; { fatal(FN, LN, "Unhandled case %s:%d", file, line); } static void bad_directive() { if (scanning()) { error(FN, LN, "Undefined preprocessor directive"); } } static void bad_include() { error(FN,LN,"Malformed include directive"); } static void unexpected(msg) char *msg; { error(FN, LN, "Unexpected %s", msg); } static void unexpected_eof(msg) char *msg; { char buf[128]; sprintf(buf, "end of file %s", msg); unexpected(buf); } static void unexpected_eol(msg) char *msg; { char buf[128]; sprintf(buf, "end of line %s", msg); unexpected(buf); } static char* charstr(c) int c; { if (is_eof(c)) return "end of file"; if (is_eol(c)) return "end of line"; if (is_alpha(c)) return "identifier"; if (is_digit(c)) return "number"; if (is_white(c)) return "white space"; if (is_punct(c)) return "puctuation"; return "crap"; } static void expected(msg, c) char *msg; int c; { error(FN, LN, "Expected %s got %s", msg, charstr(c)); } static int levels_nested() { scan_position_t *p; int level = 0; for (p = curpos; p; p = p->scan_next) { if (p->scan_kind == scan_file) { level++; } } return level; } static void add_position_directive(buf, new_line) buffer_t *buf; int new_line; { char b[40]; sprintf(b, "\n# %d \"", LN); buf_add_str(buf, b); buf_add_str(buf, FN); sprintf(b, "\" %d", levels_nested()); buf_add_str(buf, b); if (new_line) { add(buf, '\n'); } } static void init_char_class() { int i; if (class_initialized) return; cpp_char_class[0] = END_INPUT; cpp_char_class[PARAM_START] = PARAM_START; cpp_char_class['#'] = MSTART; cpp_char_class['_'] = ALPHA; cpp_char_class['$'] = ALPHA; for (i = 'g'; i <= 'z'; i++) { cpp_char_class[i] = ALPHA; } for (i = 'G'; i <= 'Z'; i++) { cpp_char_class[i] = ALPHA; } for (i = 'A'; i <= 'F'; i++) { cpp_char_class[i] = ALPHA | XDIGIT; } for (i = 'a'; i <= 'f'; i++) { cpp_char_class[i] = ALPHA | XDIGIT; } for (i = '0'; i <= '7'; i++) { cpp_char_class[i] = DIGIT | XDIGIT; } cpp_char_class['8'] = DIGIT | XDIGIT; cpp_char_class['9'] = DIGIT | XDIGIT; cpp_char_class[' '] = WHITE; cpp_char_class[' '] = WHITE; cpp_char_class['\t'] = WHITE; cpp_char_class['\n'] = END_OF_LINE; class_initialized = 1; } static void init_cppbuf() { if (buffer_initialized) { return; } buf_init(&cppbuf); } static void rm_file_from_list(f) cpp_file_t *f; { cpp_file_t *p, *last; for (p = open_files, last = NULL; p; p = p->next_cpp_file) { if (p == f) break; last = p; } assert(p == f); if (last == NULL) { open_files = f->next_cpp_file; } else { last->next_cpp_file = f->next_cpp_file; } } static cpp_file_t* find_open_file(path) char *path; { cpp_file_t *f; for (f = open_files; f; f = f->next_cpp_file) { assert(f->next_cpp_file != f); if (! strcmp(f->path, path)) { return f; } } return NULL; } void cpp_search_path(path) char *path; { buffer_t buf; if (search_index >= MAX_SEARCH) { fatal(__FILE__,__LINE__,"Too many search paths added"); } buf_init(&buf); buf_add_str(&buf, path); for (; *path && path[1]; path++); if (*path != '/') { add(&buf, '/'); } search_paths[search_index++] = buf_get_str(&buf); } static cpp_file_t* attempt_open(path) char *path; { cpp_file_t *f; char *mapaddr; size_t fsize; int fd; f = find_open_file(path); if (f != NULL) { f->reference_count++; return f; } if ((fd = open(path, O_RDONLY)) == -1) { /* Can't access for reading */ return NULL; } if ((fsize = sizeof_file(fd)) == -1) { close(fd); return NULL; } mapaddr = (char*) map_file(fd, fsize); if (mapaddr == (char*)-1) { /* failed mmap */ close(fd); return NULL; } f = (cpp_file_t*) allocate(sizeof(cpp_file_t)); f->path = new_string(path); f->fd = fd; f->text = mapaddr; f->file_size = fsize; f->reference_count = 1; assert(open_files != f); f->next_cpp_file = open_files; open_files = f; return f; } static void push_file(buf, f) buffer_t *buf; cpp_file_t *f; { scan_position_t *pos; pos = (scan_position_t*) allocate(sizeof(scan_position_t)); pos->scan_kind = scan_file; pos->scan.file = f; pos->scan_index = 0; pos->scan_pos = add_file(f->path); pos->scan_next = curpos; curpos = pos; add_position_directive(buf,1); } int cpp_open(path) char *path; { cpp_file_t *f; init_char_class(); init_cppbuf(); control_state.skip_else = 0; control_state.cur_scope = 0; control_state.gen_scope = 0; control_state._parsing = 0; f = attempt_open(path); if (f == NULL) { return 1; } orig_file = new_string(path); push_file(&cppbuf,f); return 0; } void cpp_cleanup() { cpp_file_t *f, *next; curpos = NULL; for (f = open_files; f; f = next) { next = f->next_cpp_file; unmap_file(f->text, f->file_size); close(f->fd); deallocate(f); } open_files = NULL; buf_destroy(&cppbuf); } void cpp_set_state(newpos, new_state, savepos, save_state) scan_position_t *newpos, **savepos; cpp_control_state_t *new_state, *save_state; { *savepos = curpos; *save_state = control_state; control_state = *new_state; curpos = newpos; } static void kill_file(f) cpp_file_t *f; { unmap_file(f->text, f->file_size); close(f->fd); rm_file_from_list(f); deallocate(f); } static void finished_with(s) scan_position_t *s; { cpp_file_t *f; last_file = file_name(s->scan_pos); last_line = line_number(s->scan_pos); switch (s->scan_kind) { case scan_file: f = curpos->scan.file; deallocate(s); f->reference_count--; if (f->reference_count == 0) { kill_file(f); } break; case scan_macro_expansion: case scan_text: deallocate(s); break; default: assert(0); break; } } static void unget_char() { if (curpos == NULL) return; assert(curpos->scan_index > 0); curpos->scan_index--; } static int next_char() { register scan_position_t *cp = curpos; /* Get global curpos into a reg */ cpp_file_t *f; macro_t *m; scan_position_t *next; scan_kind_t kind; int result; top: if (cp == NULL) { return 0; } kind = cp->scan_kind; if (kind == scan_file) { f = cp->scan.file; if (cp->scan_index >= f->file_size) { next = cp->scan_next; finished_with(cp); cp = curpos = next; goto top; } result = f->text[cp->scan_index++]; } else if (kind == scan_macro_expansion) { m = cp->scan.expansion.expand_macro; if (cp->scan_index >= m->macro_body_len) { next = cp->scan_next; finished_with(cp); cp = curpos = next; goto top; } result = m->macro_body[cp->scan_index++]; } else { assert(kind == scan_text); result = cp->scan.text[cp->scan_index++]; if (result == 0) { next = cp->scan_next; finished_with(cp); cp = curpos = next; goto top; } } return result; } static void incline(sync) int sync; { if (sync) { control_state.position++; } else { /* force file position directive to be added */ control_state.position = 0; } switch (curpos->scan_kind) { case scan_file: curpos->scan_pos++; break; default: break; } } static void check_position(buf) buffer_t *buf; { if (parsing()) return; switch (curpos->scan_kind) { case scan_file: if (control_state.position != curpos->scan_pos) { control_state.position = curpos->scan_pos; add_position_directive(buf, 0); } break; default: assert(0); break; } } static void comment_start(buf) buffer_t *buf; { if (translate_comments && buf != NULL) { add(buf, '/'); add(buf, '*'); } } static void cpp_comment_start(buf) buffer_t *buf; { if (translate_comments && buf != NULL) { add(buf, '/'); add(buf, '/'); } } static int scan_white(buf, c) buffer_t *buf; int c; { while (is_white(c)) { if (buf != NULL) { add(buf, c); } c = next_char(); } return c; } static int skip_white(c) int c; { return scan_white(NULL, c); } static int skip_c_comment(buf, c) buffer_t *buf; int c; { c = next_char(); for (;;) { switch (classof(c)) { case END_INPUT: return c; case END_OF_LINE: incline(0); add(buf,c); c = next_char(); break; case PUNCT: switch (c) { case '*': add(buf,'*'); c = next_char(); if (c == '/') { add(buf,'/'); return next_char(); } break; case '\\': add(buf,c); c = next_char(); add(buf,c); if (is_eol(c)) { incline(0); } else if (is_eof(c)) { return c; } c = next_char(); break; default: add(buf,c); c = next_char(); break; } break; default: c = scan_default(buf, c); break; } } } static int skip_cpp_comment(buf, c) buffer_t *buf; int c; { buffer_t lbuf; c = next_char(); for (;;) { switch (classof(c)) { case END_INPUT: return c; case DIGIT | XDIGIT: case ALPHA: case ALPHA | XDIGIT: case WHITE: case MSTART: case PARAM_START: case PUNCT: add(buf,c); c = next_char(); break; case END_OF_LINE: add(buf,c); /* incline(0); */ /* c = next_char(); */ return c; default: UNHANDLED(); break; } } } static int scan_to_end(buf, c) buffer_t *buf; int c; { while (! (is_eol(c) || is_eof(c))) { switch (c) { case '/': c = next_char(); if (c == '*') { comment_start(buf); c = skip_c_comment(want_comments(buf), c); } else if (c == '/') { cpp_comment_start(buf); c = skip_cpp_comment(want_comments(buf), c); } else { add(buf,'/'); } break; case '\\': c = next_char(); if (is_eol(c)) { incline(0); c = next_char(); } else if (is_eof(c)) { if (buf != NULL) { add(buf, '\\'); } return c; } else if (c == '\\') { if (buf != NULL) { add(buf, '\\'); add(buf, '\\'); } c = next_char(); } else if (buf != NULL) { add(buf, '\\'); } break; default: if (buf != NULL) { add(buf, c); } c = next_char(); break; } } return c; } static int skip_to_end(c) int c; { return scan_to_end(NULL, c); } static int finish_num(buf, c, mask) buffer_t *buf; int c, mask; { while (! is_eof(c)) { if ((cpp_char_class[c] & mask) == 0) break; add(buf, c); c = next_char(); } return c; } static int maybe_magnitude(buf, c) buffer_t *buf; int c; { if (c == 'e' || c == 'E') { add(buf, c); c = next_char(); if (c == '+' || c == '-') { add(buf, c); c = next_char(); } c = finish_num(buf, c, DIGIT); } return c; } static int scan_number(buf, c) buffer_t *buf; int c; { add(buf, c); if (c == '0') { c = next_char(); if (c == 'x' || c == 'X') { add(buf, c); c = finish_num(buf, next_char(), XDIGIT); } else { c = finish_num(buf, c, DIGIT); } } else { c = finish_num(buf, next_char(), DIGIT); } if (int_modifier(c)) { do { add(buf, c); c = next_char(); } while (int_modifier(c)); return c; } if (c == '.') { add(buf, c); c = finish_num(buf, next_char(), DIGIT); c = maybe_magnitude(buf, c); if (float_modifier(c)) { add(buf, c); c = next_char(); } return c; } c = maybe_magnitude(buf, c); if (int_modifier(c)) { do { add(buf, c); c = next_char(); } while (int_modifier(c)); } return c; } static int scan_ident(buf, c) buffer_t *buf; int c; { for (;;) { add(buf, c); c = next_char(); if (!is_alpha_numeric(c)) break; } return c; } static int scan_to_del(buf, c, del) buffer_t *buf; int c, del; { for (;;) { switch (classof(c)) { case END_INPUT: case END_OF_LINE: return c; case PUNCT: switch (c) { case '\\': c = next_char(); if (is_eol(c)) { incline(0); c = next_char(); } else if (c == '\'' || c == '"' || c == '\\') { add(buf, '\\'); add(buf, c); c = next_char(); } else { add(buf, '\\'); } break; default: if (c == del) { return c; } add(buf, c); c = next_char(); break; } break; default: c = scan_default(buf,c); break; } } } static int scan_string(buf, c) buffer_t *buf; int c; { c = scan_to_del(buf, c, '"'); if (c != '"') { expected("'\"'", c); } else { add(buf, c); return next_char(); } } static int scan_char_const(buf, c) buffer_t *buf; int c; { c = scan_to_del(buf, c, '\''); if (c != '\'') { expected("single quote", c); } else { add(buf, c); return next_char(); } } static int grok_formals(formals, nformals, buf, c) char ***formals; int *nformals; buffer_t *buf; int c; { char *f[256]; char **fp; int nf = 0; int i; for (;;) { c = skip_white(c); if (! is_alpha(c)) break; c = scan_ident(buf, c); range_check(nf, 256, "Too many formal parameters"); f[nf++] = buf_get_str(buf); c = skip_white(c); if (c != ',') break; c = next_char(); } *nformals = nf; if (nf == 0) { *formals = NULL; } else { fp = (char**) malloc(sizeof(char*) * nf); for (i = 0; i < nf; i++) { fp[i] = f[i]; } *formals = fp; } return c; } static char* local_copy(buf, str, size) buffer_t *buf; char *str; int size; { int len = buf_count(buf); if (len < size) { buf_move_to(buf, str); return str; } return buf_get_str(buf); } static void grok_param(outbuf, idbuf, formals, nformals, xpect) buffer_t *outbuf, *idbuf; char *formals[]; int nformals, xpect; { char ident[128]; char *name; int i; name = local_copy(idbuf, ident, sizeof(ident)); for (i = 0; i < nformals; i++) { if (! strcmp(name, formals[i])) { add(outbuf, PARAM_START); add(outbuf, i+1); goto end_subp; } } if (xpect) { error(FN, LN, "Expected macro formal got %s", name); } buf_add_str(outbuf, name); end_subp: if (name != ident) { free(name); } } static int scan_default(buf, c) buffer_t *buf; int c; { switch (classof(c)) { case END_INPUT: return c; case END_OF_LINE: incline(buf != NULL); add(buf, c); c = next_char(); break; case MSTART: add(buf, c); c = next_char(); break; case WHITE: add(buf, c); return next_char(); case ALPHA: case ALPHA | XDIGIT: c = scan_ident(buf, c); break; case DIGIT | XDIGIT: return scan_number(buf, c); case PARAM_START: if (buf) { c = grok_actual_param(next_char()); } else { c = next_char(); c = next_char(); } break; case PUNCT: switch (c) { case '"': add(buf, c); c = scan_string(buf, next_char()); break; case '\'': add(buf, c); c = scan_char_const(buf, next_char()); break; case '/': c = next_char(); if (c == '*') { comment_start(buf); c = skip_c_comment(want_comments(buf), c); } else if (c == '/') { cpp_comment_start(buf); c = skip_cpp_comment(want_comments(buf), c); } else { add(buf,'/'); } break; case '\\': c = next_char(); switch (c) { case '\n': incline(0); c = next_char(); break; case '\\': add(buf, '\\'); add(buf, '\\'); c = next_char(); break; default: add(buf, '\\'); break; } break; default: add(buf, c); c = next_char(); break; } break; default: UNHANDLED(); break; } return c; } static int grok_define(buf, c) buffer_t *buf; int c; { buffer_t lbuf; char *macro_name; char *macro_body; char **formals; file_pos_t defpos; int nformals; int body_len; c = skip_white(c); if (!is_alpha(c)) { expected("idetifier", c); return skip_to_end(c); } defpos = curpos->scan_pos; buf_init(buf); c = scan_ident(buf, c); macro_name = buf_get_str(buf); if (c == '(') { c = grok_formals(&formals, &nformals, buf, next_char()); if (c == ')') { c = next_char(); } else { expected("')'", c); } } else { nformals = -1; } c = skip_white(c); if (nformals == -1) { buf_init(buf); c = scan_to_end(buf, c); body_len = buf_count(buf); macro_body = buf_get_str(buf); } else { buf_init(buf); while (!is_eol(c) && !is_eof(c)) { switch (classof(c)) { case MSTART: c = next_char(); if (c == '#') { c = next_char(); break; } c = skip_white(c); if (! is_alpha(c)) { expected("macro formal parameter name", c); } else { add(buf, '"'); buf_init(&lbuf); c = scan_ident(&lbuf, c); grok_param(buf, &lbuf, formals, nformals, 1); add(buf, '"'); } break; case ALPHA: case ALPHA | XDIGIT: buf_init(&lbuf); c = scan_ident(&lbuf, c); grok_param(buf, &lbuf, formals, nformals, 0); break; default: c = scan_default(buf, c); break; } } body_len = buf_count(buf); macro_body = buf_get_str(buf); } macro_def(macro_name, macro_body, body_len, nformals, defpos); return c; } static int grok_if(buf, c) buffer_t *buf; int c; { char ident[256]; char *name; cpp_eval_result_t result; assert(control_state.cur_scope >= 0); assert(control_state.gen_scope >= 0); buf_init(buf); c = scan_to_end(buf, c); name = local_copy(buf, ident, sizeof(ident)); result = cpp_eval(name); ++control_state.cur_scope; control_state.skip_else = 0; if (EVAL_FAILED(result) || !IS_EVAL_INT(result)) { ; } else { if (EVAL_INT(result) != 0) { ++control_state.gen_scope; } } end_subp: if (name != ident) { free(name); } return c; } static int grok_elif(buf, c) buffer_t *buf; int c; { char ident[256]; char *name; cpp_eval_result_t result; assert(control_state.cur_scope >= 0); assert(control_state.gen_scope >= 0); if (control_state.cur_scope == 0) { error(FN,LN,"elif directive found without a matching if directive"); return skip_to_end(c); } if (control_state.skip_else) { return skip_to_end(c); } if (scanning()) { control_state.skip_else = 1; control_state.gen_scope--; return skip_to_end(c); } if (control_state.gen_scope != control_state.cur_scope - 1) { return skip_to_end(c); } buf_init(buf); c = scan_to_end(buf, c); name = local_copy(buf, ident, sizeof(ident)); result = cpp_eval(name); /* ++control_state.cur_scope; */ /* control_state.skip_else = 0; */ if (EVAL_FAILED(result) || !IS_EVAL_INT(result)) { ; } else { if (EVAL_INT(result) != 0) { ++control_state.gen_scope; } } end_subp: if (name != ident) { free(name); } return c; } static int grok_ifdef(buf, c, sense) buffer_t *buf; int c; int sense; { char ident[128]; char *name; macro_t *m; control_state.skip_else = 0; c = skip_white(c); if (! is_alpha(c)) { expected("identifier", c); return skip_to_end(c); } c = scan_ident(buf, c); name = local_copy(buf, ident, sizeof(ident)); ++control_state.cur_scope; m = macro_find(name); if ((m != NULL && !sense) || (m == NULL && sense)) { ++control_state.gen_scope; } if (name != ident) { free(name); } return skip_to_end(c); } static int grok_else(buf, c) buffer_t *buf; int c; { int was_skipping = skipping(); assert(control_state.cur_scope >= 0); assert(control_state.gen_scope >= 0); if (control_state.cur_scope == 0) { error(FN, LN, "Unmatched else directive"); return skip_to_end(c); } else { if (! control_state.skip_else) { control_state.skip_else = 0; if (control_state.gen_scope == control_state.cur_scope) { --control_state.gen_scope; } else if (control_state.gen_scope == control_state.cur_scope -1 ) { ++control_state.gen_scope; } } } if (scanning() && was_skipping) { add_position_directive(buf, 0); } return skip_to_end(c); } static int grok_endif(buf, c) buffer_t *buf; int c; { int was_skipping = skipping(); assert(control_state.cur_scope >= 0); assert(control_state.gen_scope >= 0); if (control_state.cur_scope == 0) { error(FN, LN, "Unmatched endif directive"); return skip_to_end(c); } else { control_state.skip_else = 0; if (control_state.gen_scope == control_state.cur_scope) { --control_state.gen_scope; } --control_state.cur_scope; } if (scanning() && was_skipping) { add_position_directive(buf, 0); } return skip_to_end(c); } static void grok_pathof(buf, path) buffer_t *buf; char *path; { char *last = NULL; char *p; for (p = path; *p; p++) { if (*p == '/') { last = p+1; } } if (last != NULL) { for (p = path; p != last; p++) { add(buf, *p); } } } static int search_for_file(buf, lbuf, name, stdinc) buffer_t *buf, *lbuf; char *name; int stdinc; { char *path; cpp_file_t *f; int i; char ident[128]; if (name[0] == '/') { f = attempt_open(name); if (f != NULL) { push_file(buf, f); goto end_of_subp; } goto failed; } if (!stdinc) { f = attempt_open(name); if (f != NULL) { push_file(buf, f); goto end_of_subp; } /* ** #include "file" semantics are to search for "file" ** in the directory of the original source file. */ buf_init(lbuf); grok_pathof(lbuf, orig_file); if (buf_count(lbuf)) { buf_add_str(lbuf, name); path = local_copy(lbuf, ident, sizeof(ident)); f = attempt_open(path); if (path != ident) { free(path); } if (f != NULL) { push_file(buf, f); goto end_of_subp; } } } for (i = 0; i < search_index; i++) { buf_init(lbuf); buf_add_str(lbuf, search_paths[i]); buf_add_str(lbuf, name); path = local_copy(lbuf, ident, sizeof(ident)); f = attempt_open(path); if (path != ident) { free(path); } if (f != NULL) { push_file(buf,f); goto end_of_subp; } } buf_init(lbuf); buf_add_str(lbuf, "/usr/include/"); buf_add_str(lbuf, name); path = local_copy(lbuf, ident, sizeof(ident)); f = attempt_open(path); if (path != ident) { free(path); } if (f != NULL) { push_file(buf,f); goto end_of_subp; } failed: error(FN,LN,"Couldn't open %s", name); end_of_subp: return next_char(); } static int grok_include(buf, lbuf, c) buffer_t *buf, *lbuf; int c; { char ident[64]; char *name; int del; del = skip_white(c); switch (del) { case '<': del = '>'; /* fall through */ case '"': buf_init(lbuf); c = scan_to_del(lbuf, next_char(), del); if (c != del) { goto bad_input; } c = skip_to_end(c); unget_char(); break; default: bad_input: bad_include(); return skip_to_end(c); } name = local_copy(lbuf, ident, sizeof(ident)); c = search_for_file(buf, lbuf, name, del == '>'); if (name != ident) { free(name); } return c; } static int grok_error(buf, c) buffer_t *buf; int c; { char ident[128]; char *msg; buf_init(buf); c = skip_white(c); c = scan_to_end(buf, c); msg = local_copy(buf, ident, sizeof(ident)); error(FN,LN,msg); if (msg != ident) { free(msg); } return c; } static int grok_undef(buf, c) buffer_t *buf; int c; { char ident[128]; char *name; c = skip_white(c); if (! is_alpha(c)) { expected("macro identifier", c); return skip_to_end(c); } buf_init(buf); c = scan_ident(buf, c); name = local_copy(buf, ident, sizeof(ident)); macro_undef(name); if (name != ident) { free(name); } return skip_to_end(c); } static int scan_directive(buf, c) buffer_t *buf; int c; { #ifdef PUBLIC struct resword {char *name; int token;}; extern struct resword *cpp_rsvd(); struct resword *r; #endif buffer_t lbuf; char ident[32]; int len; int keywd; c = skip_white(c); if (is_eof(c) || is_eol(c)) { return c; } if (!is_alpha(c)) { bad_directive(); return skip_to_end(c); } buf_init(&lbuf); c = scan_ident(&lbuf, c); len = buf_count(&lbuf); if (len > sizeof(ident)) { buf_destroy(&lbuf); bad_directive(); return skip_to_end(c); } buf_move_to(&lbuf, ident); buf_destroy(&lbuf); #ifdef PUBLIC if ((r = cpp_rsvd(ident, len)) == NULL) { #else if ((keywd = cpp_rsvd(ident)) == -1) { #endif bad_directive(); return skip_to_end(c); } #ifdef PUBLIC keywd = r->token; #endif if (scanning()) { switch (keywd) { case Define: return grok_define(&lbuf, c); case Elif: return grok_elif(&lbuf, c); case Else: return grok_else(buf, c); case Endif: return grok_endif(buf, c); case Error: return grok_error(&lbuf, c); case If: return grok_if(&lbuf, c); case Ifdef: return grok_ifdef(&lbuf, c, 0); case Ifndef: return grok_ifdef(&lbuf, c, 1); case Include: return grok_include(buf, &lbuf, c); case Line: case Pragma: case Ident: return skip_to_end(c); case Undef: return grok_undef(&lbuf, c); } } else { switch (keywd) { case Define: case Include: case Pragma: case Undef: case Error: case Ident: case Line: return skip_to_end(c); case Elif: return grok_elif(&lbuf,c); case Else: return grok_else(buf, c); case Endif: return grok_endif(buf, c); case If: case Ifdef: case Ifndef: ++control_state.cur_scope; return skip_to_end(c); } } return c; } static int scan_actual(buf,c, level) buffer_t *buf; int c; int level; { for (;;) { switch (classof(c)) { case END_INPUT: unexpected_eof("in macro call"); return c; case PUNCT: switch (c) { case '(': add(buf,c); c = scan_actual(buf,next_char(),level+1); if (c != ')') { expected("')'", c); } else { add(buf, c); c = next_char(); } break; case ')': return c; case ',': if (level == 0) { return c; } add(buf, c); c = next_char(); break; case '"': add(buf, c); c = scan_string(buf, next_char()); break; case '\'': add(buf, c); c = scan_char_const(buf, next_char()); break; case '/': case '\\': c = next_char(); if (is_eof(c)) { add(buf,'\\'); return c; } switch (c) { case '\n': incline(0); c = next_char(); break; default: add(buf,'\\'); add(buf,c); c = next_char(); break; } break; default: add(buf, c); c = next_char(); break; } break; default: c = scan_default(buf,c); break; } } } static int grok_actuals(actuals, nactuals, buf, c) char ***actuals; int *nactuals; buffer_t *buf; int c; { char *a[256]; char **ap; int na = 0; int i; for (;;) { c = skip_white(c); if (is_eof(c)) { unexpected_eof("in macro call"); return c; } if (c == ')') break; buf_init(buf); c = scan_actual(buf,c,0); range_check(na, 256, "Too many actual parameters"); a[na++] = buf_get_str(buf); c = skip_white(c); if (c != ',') break; c = next_char(); } *nactuals = na; if (na == 0) { *actuals = NULL; } else { ap = (char**) malloc(sizeof(char*) * na); for (i = 0; i < na; i++) { ap[i] = a[i]; } *actuals = ap; } return c; } static int push_expansion(mac, actuals, nactuals) macro_t *mac; char **actuals; int nactuals; { scan_position_t *npos; npos = (scan_position_t*) allocate(sizeof(scan_position_t)); npos->scan_kind = scan_macro_expansion; npos->scan.expansion.expand_macro = mac; npos->scan.expansion.expand_actuals = actuals; npos->scan.expansion.expand_nactuals = nactuals; if (curpos != NULL) { npos->scan_pos = curpos->scan_pos; } npos->scan_index = 0; npos->scan_next = curpos; curpos = npos; return next_char(); } static int push_string(str) char *str; { scan_position_t *npos; npos = (scan_position_t*) allocate(sizeof(scan_position_t)); npos->scan_kind = scan_text; npos->scan.text = str; npos->scan_pos = curpos->scan_pos; npos->scan_index = 0; npos->scan_next = curpos; curpos = npos; return next_char(); } static void grok_builtin_macro(buf, mac) buffer_t *buf; macro_t *mac; { char buffer[32]; switch (mac->macro_params) { case BUILTIN_FILE: add(buf, '"'); buf_add_str(buf, FN); add(buf, '"'); break; case BUILTIN_LINE: sprintf(buffer, "%d", LN); buf_add_str(buf, buffer); break; default: assert(0); break; } } static int grok_macro_instance(buf, lbuf, c, mac) buffer_t *buf, *lbuf; int c; macro_t *mac; { char **actuals; int nactuals; if (mac->macro_params < -1) { grok_builtin_macro(buf, mac); return c; } if (mac->macro_params != -1) { buf_init(lbuf); c = scan_white(lbuf, c); if (c != '(') { buf_add_str(buf, mac->macro_name); buf_concat(buf, lbuf); return c; } buf_init(lbuf); c = grok_actuals(&actuals, &nactuals, lbuf, next_char()); if (c != ')') { expected("')'", c); } } else { unget_char(); } return push_expansion(mac, actuals, nactuals); } static int parenthesized_ident(buf, c) buffer_t *buf; int c; { for (;;) { c = skip_white(c); if (c == '(') { c = parenthesized_ident(buf, next_char()); if (c != ')') { return 0; } c = next_char(); } else if (is_alpha(c)) { do { add(buf, c); c = next_char(); } while (is_alpha_numeric(c)); } else { return c; } } } static int grok_defined(buf, lbuf, c) buffer_t *buf, *lbuf; int c; { char ident[128]; char *name; c = skip_white(c); buf_init(lbuf); if (c == '(') { c = parenthesized_ident(lbuf, next_char()); if (c != ')') { add(buf, BAD_INPUT); return c; } c = next_char(); } else { c = scan_ident(lbuf, c); } name = local_copy(lbuf, ident, sizeof(ident)); add(buf, ' '); if (macro_find(name)) { add(buf, '1'); } else { add(buf, '0'); } add(buf, ' '); if (name != ident) { free(name); } return c; } static int grok_ident(buf, c) buffer_t *buf; int c; { buffer_t lbuf; char ident[128]; char *name; macro_t *mac; buf_init(&lbuf); c = scan_ident(&lbuf, c); name = local_copy(&lbuf, ident, sizeof(ident)); if (parsing() && !strcmp(name,"defined")) { return grok_defined(buf, &lbuf, c); } if (mac = macro_find(name)) { c = grok_macro_instance(buf, &lbuf, c, mac); } else { buf_add_str(buf, name); } if (name != ident) { free(name); } return c; } static int grok_actual_param(param_ord) int param_ord; { char **actuals; char *param; int nactuals; if (curpos->scan_kind != scan_macro_expansion) { fatal(FN,LN,"bad param %s:%d %s:%d",__FILE__,__LINE__, last_file, last_line); } assert(curpos->scan_kind == scan_macro_expansion); actuals = curpos->scan.expansion.expand_actuals; nactuals = curpos->scan.expansion.expand_nactuals; if (actuals == NULL || param_ord > nactuals) { return next_char(); } param = actuals[param_ord - 1]; if (param == NULL) { return next_char(); } return push_string(param); } static int skip(buf, c) buffer_t *buf; int c; { buffer_t lbuf; assert(!parsing()); for (;;) { if (scanning()) { return c; } switch (classof(c)) { case END_INPUT: return c; case MSTART: c = scan_directive(buf, next_char()); break; case PUNCT: switch (c) { case '"': buf_init(&lbuf); c = scan_string(&lbuf, next_char()); buf_destroy(&lbuf); break; case '\'': buf_init(&lbuf); c = scan_char_const(&lbuf, next_char()); buf_destroy(&lbuf); break; case '/': c = next_char(); if (c == '*') { c = skip_c_comment(NULL,c); } else if (c == '/') { c = skip_cpp_comment(NULL,c); } break; case '\\': UNHANDLED(); break; default: c = next_char(); break; } break; default: c = scan_default(NULL, c); break; } } } static int scan(buf) buffer_t *buf; { int c; c = next_char(); for (;;) { if (skipping()) { c = skip(buf, c); } assert(scanning()); switch (classof(c)) { case END_INPUT: return -1; case WHITE: add(buf, c); return 0; case END_OF_LINE: incline(1); check_position(buf); add(buf, c); return 0; case ALPHA: case ALPHA | XDIGIT: c = grok_ident(buf, c); break; case MSTART: if (parsing()) { add(buf, BAD_INPUT); return 0; } c = scan_directive(buf, next_char()); break; default: c = scan_default(buf,c); break; } } } #define GET_FROM_BUF(buf)\ while (buf_empty(buf)) {\ if (scan(buf) == -1) break;\ }\ return buf_get(buf); int cpp_getc_from(buf) buffer_t *buf; { GET_FROM_BUF(buf); } int cpp_getc() { buffer_t *buf = &cppbuf; GET_FROM_BUF(buf); }