2261 lines · 56.3 KB · C
RawDownload
/* * C and T preprocessor, and integrated lexer * (c) Thomas Pornin 1999, 2000 * * 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. * 4. The name of the authors may not be used to endorse or promote *    products derived from this software without specific prior written *    permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT 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 AUTHORS 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. * */ /*vb*/#ifdef HAVE_MISRAvoid misra(int,...);#endif #define VERS_MAJ	1#define VERS_MIN	0/* uncomment the following if you cannot set it with a compiler flag *//* #define STAND_ALONE */ #include "tune.h"#ifdef UCPP_MMAP#ifndef _POSIX_SOURCE#define _POSIX_SOURCE	1#endif#endif#include <stdio.h>#include <string.h>#include <stdarg.h>#include <setjmp.h>#include <stddef.h>#include <limits.h>#include <time.h>#include "ucppi.h"#include "mem.h"#include "hash.h"#ifdef UCPP_MMAP#include <unistd.h>#include <sys/types.h>#include <sys/mman.h>#include <fcntl.h>#endif /* * The standard path where includes are looked for. */#ifdef STAND_ALONEstatic char *include_path_std[] = { STD_INCLUDE_PATH, 0 };#endifstatic char **include_path;static size_t include_path_nb = 0; int no_special_macros = 0;int emit_dependencies = 0, emit_defines = 0, emit_assertions = 0;FILE *emit_output; #ifdef STAND_ALONEstatic char *system_macros_def[] = { STD_MACROS, 0 };static char *system_assertions_def[] = { STD_ASSERT, 0 };#endif char *current_filename, *current_long_filename = 0;static int current_incdir = -1; #ifndef NO_UCPP_ERROR_FUNCTIONS/* * "ouch" is the name for an internal ucpp error. If AUDIT is not defined, * no code calling this function will be generated; a "ouch" may still be * emitted by getmem() (in mem.c) if MEM_CHECK is defined, but this "ouch" * does not use this function. */#endif	/* NO_UCPP_ERROR_FUNCTIONS */ /* * Some memory allocations are manually garbage-collected; essentially, * strings duplicated in the process of macro replacement. Each such * string is referenced in the garbage_fifo, which is cleared when all * nested macros have been resolved. */ struct garbage_fifo {	char **garbage;	size_t ngarb, memgarb;}; /* * throw_away() marks a string to be collected later */void throw_away(struct garbage_fifo *gf, char *n){	wan(gf->garbage, gf->ngarb, n, gf->memgarb);} /* * free marked strings */void garbage_collect(struct garbage_fifo *gf){	size_t i; 	for (i = 0; i < gf->ngarb; i ++) freemem(gf->garbage[i]);	gf->ngarb = 0;} static void init_garbage_fifo(struct garbage_fifo *gf){	gf->garbage = getmem((gf->memgarb = GARBAGE_LIST_MEMG)		* sizeof(char *));	gf->ngarb = 0;} /* * order is important: it must match the token-constants declared as an * enum in the header file. */char *operators_name[] = {	" ", "\n", " ",	"0000", "name", "bunch", "pragma", "context",	"\"dummy string\"", "'dummy char'",	"/", "/=", "-", "--", "-=", "->", "+", "++", "+=", "<", "<=", "<<",	"<<=", ">", ">=", ">>", ">>=", "=", "==",#ifdef CAST_OP	"=>",#endif	"~", "!=", "&", "&&", "&=", "|", "||", "|=", "%", "%=", "*", "*=",	"^", "^=", "!", "~=", "{", "}", "[", "]", "(", ")", ",", "?", ";",	":", ".", "...", "#", "##", " ", "ouch", "<:", ":>", "<%", "%>",	"%:", "%:%:"}; /* the ascii representation of a token */#ifdef SEMPER_FIDELIS#define tname(x)	(ttWHI((x).type) ? " " : S_TOKEN((x).type) \			? (x).name : operators_name[(x).type])#else#define tname(x)	(S_TOKEN((x).type) ? (x).name \			: operators_name[(x).type])#endif char *token_name(struct token *t){	return tname(*t);} /* * To speed up deeply nested and repeated inclusions, we: * -- use a hash table to remember where we found each file * -- remember when the file is protected by a #ifndef/#define/#endif *    construction; we can then avoid including several times a file *    when this is not necessary. * -- remember in which directory, in the include path, the file was found. */struct found_file {	char *name;		/* first field */	char *long_name;	char *protect;	int incdir;}; static struct HT *found_files = 0, *found_files_loc = 0; static struct found_file *new_found_file(void){	struct found_file *ff = getmem(sizeof(struct found_file)); 	ff->protect = 0;	ff->incdir = -1;	return ff;} static void del_found_file(void *m){	struct found_file *ff = (struct found_file *)m; 	freemem(ff->name);	freemem(ff->long_name);	if (ff->protect) freemem(ff->protect);} /* * To keep up with the #ifndef/#define/#endif protection mechanism * detection. */struct protect protect_detect;static struct protect *protect_detect_stack = 0; /* * "current_filename" is used for error reporting, and the __FILE__ macro */static void set_current_filename(char *x){	current_filename = x;} void set_init_filename(char *x, int real_file){	set_current_filename(sdup(x));	current_long_filename = 0;	current_incdir = -1;	if (real_file) {		protect_detect.macro = 0;		protect_detect.state = 1;		protect_detect.ff = new_found_file();		protect_detect.ff->name = sdup(x);		protect_detect.ff->long_name = sdup(x);		putHT(found_files_loc, protect_detect.ff);	} else {		protect_detect.state = 0;	}} static void init_found_files(void){	if (found_files) killHT(found_files);	found_files = newHT(128, cmp_struct, hash_struct, del_found_file);	if (found_files_loc) killHT(found_files_loc);	found_files_loc = newHT(128, cmp_struct, hash_struct, del_found_file);} /* * convert a Unix-style path for the host architecture */#if defined(MSDOS) || defined(ATARI)static char *convert_path(char *path){	/* on msdos systems, replace all / by \ */	char *c, *newp = sdup(path); 	for (c = newp; *c; c ++) if (*c == '/') *c = '\\';	return newp;} #elif defined AMIGAstatic char *convert_path(char *path){	char *newp = getmem(1 + strlen(path));	char *s = path, *d = newp; 	if (*s == '.') {					/* convert "." to "" */		if (*(s+1) == '\0')			s++;	}	while (*s) {		if (*s == '.') {			if (*(s+1) == '/') {				s += 2;							/* "./" is discarded */				continue;			}			else if (*(s+1) == '.') {				if (*(s+2) == '/')					s += 2;						/* "../" is converted to "/" */			}		}		*d++ = *s++;	}	*d = '\0'; 	return newp;} #else  /* no conversion */static char *convert_path(char *path){	return sdup(path);}#endif /* * Set the lexer state at the beginning of a file. */static void reinit_lexer_state(struct lexer_state *ls, int wb){#ifndef NO_UCPP_BUF	ls->input_buf = wb ? getmem(INPUT_BUF_MEMG) : 0;#ifdef UCPP_MMAP	ls->from_mmap = 0;#endif#endif	ls->ebuf = ls->pbuf = 0;	ls->nlka = 0;	ls->macfile = 0;	ls->discard = 1;	ls->last = 0;		/* we suppose '\n' is not 0 */	ls->line = 1;	ls->ltwnl = 1;	ls->oline = 1;	ls->pending_token = 0;	ls->cli = 0;	ls->copy_line[COPY_LINE_LENGTH - 1] = 0;	ls->ifnest = 0;	ls->condf[0] = ls->condf[1] = 0;} /* * Initialize the struct lexer_state, with optional input and output buffers. */void init_buf_lexer_state(struct lexer_state *ls, int wb){	reinit_lexer_state(ls, wb);#ifndef NO_UCPP_BUF	ls->output_buf = wb ? getmem(OUTPUT_BUF_MEMG) : 0;#endif	ls->sbuf = 0; 	ls->ctok = getmem(sizeof(struct token));	ls->ctok->name = getmem(ls->tknl = TOKEN_NAME_MEMG);	ls->pending_token = 0; 	ls->flags = 0;	ls->count_trigraphs = 0;	ls->gf = getmem(sizeof(struct garbage_fifo));	init_garbage_fifo(ls->gf);	ls->condcomp = 1;	ls->condnest = 0;#ifdef INMACRO_FLAG	ls->inmacro = 0;	ls->macro_count = 0;#endif} /* * Initialize the (complex) struct lexer_state. */void init_lexer_state(struct lexer_state *ls){	init_buf_lexer_state(ls, 1);} /* * Restore what is needed from a lexer_state. This is used for #include. */static void restore_lexer_state(struct lexer_state *ls,	struct lexer_state *lsbak){#ifndef NO_UCPP_BUF	freemem(ls->input_buf);	ls->input_buf = lsbak->input_buf;#ifdef UCPP_MMAP	ls->from_mmap = lsbak->from_mmap;	ls->input_buf_sav = lsbak->input_buf_sav;#endif#endif	ls->input = lsbak->input;	ls->ebuf = lsbak->ebuf;	ls->pbuf = lsbak->pbuf;	ls->nlka = lsbak->nlka;	ls->macfile = lsbak->macfile;	ls->discard = lsbak->discard;	ls->line = lsbak->line;	ls->oline = lsbak->oline;	ls->ifnest = lsbak->ifnest;	ls->condf[0] = lsbak->condf[0];	ls->condf[1] = lsbak->condf[1];} /* * file_context (and the two functions push_ and pop_) are used to save * all that is needed when including a file. */static struct file_context {	struct lexer_state ls;	char *name, *long_name;	int incdir;} *ls_stack;static size_t ls_depth = 0; static void push_file_context(struct lexer_state *ls){	struct file_context fc; 	if (ls_depth > 1023)		error(ls->line - 1, "too many nested includes");	fc.name = current_filename;	fc.long_name = current_long_filename;	fc.incdir = current_incdir;	mmv(&(fc.ls), ls, sizeof(struct lexer_state));	aol(ls_stack, ls_depth, fc, LS_STACK_MEMG);	ls_depth --;	aol(protect_detect_stack, ls_depth, protect_detect, LS_STACK_MEMG);	protect_detect.macro = 0;} static void pop_file_context(struct lexer_state *ls){#ifdef AUDIT	if (ls_depth <= 0) ouch("prepare to meet thy creator");#endif	restore_lexer_state(ls, &(ls_stack[-- ls_depth].ls));	if (protect_detect.macro) freemem(protect_detect.macro);	protect_detect = protect_detect_stack[ls_depth];	set_current_filename(ls_stack[ls_depth].name);	current_long_filename = ls_stack[ls_depth].long_name;	current_incdir = ls_stack[ls_depth].incdir;} /* * report_context() returns the list of successive includers of the * current file, ending with a dummy entry with a negative line number. * The caller is responsible for freeing the returned pointer. */struct stack_context *report_context(void){	struct stack_context *sc;	size_t i; 	sc = getmem((ls_depth + 1) * sizeof(struct stack_context));	for (i = 0; i < ls_depth; i ++) {		sc[i].name = ls_stack[ls_depth - i - 1].name;		sc[i].long_name = ls_stack[ls_depth - i - 1].long_name;		sc[i].line = ls_stack[ls_depth - i - 1].ls.line - 1;	}	sc[ls_depth].line = -1;	return sc;} /* * init_lexer_mode() is used to end initialization of a struct lexer_state * if it must be used for a lexer */void init_lexer_mode(struct lexer_state *ls){	ls->flags = DEFAULT_LEXER_FLAGS;	ls->output_fifo = getmem(sizeof(struct token_fifo));	ls->output_fifo->art = ls->output_fifo->nt = 0;	ls->toplevel_of = ls->output_fifo;	ls->save_ctok = ls->ctok;} /* * release memory used by a struct lexer_state */void free_lexer_state(struct lexer_state *ls){#ifndef NO_UCPP_BUF	if (ls->input_buf) freemem(ls->input_buf);	if (ls->output_buf) freemem(ls->output_buf);#endif	freemem(ls->ctok->name);	freemem(ls->ctok);	if (!(ls->flags & LEXER)) garbage_collect(ls->gf);	freemem(ls->gf);} /* * Print line information. */static void print_line_info(struct lexer_state *ls, unsigned long flags){	char *fn = current_long_filename ?		current_long_filename : current_filename;	char *b, *d; 	b = getmem(50 + strlen(fn));	if (flags & GCC_LINE_NUM) {		sprintf(b, "# %ld \"%s\"\n", ls->line, fn);	} else {		sprintf(b, "#line %ld \"%s\"\n", ls->line, fn);	}	for (d = b; *d; d ++) put_char(ls, (unsigned char)(*d));	freemem(b);} /* * Enter a file; this implies the possible emission of a #line directive. * The flags used are passed as second parameter instead of being * extracted from the struct lexer_state. * * As a command-line option, gcc-like directives (with only a '#', * without 'line') may be produced. */void enter_file(struct lexer_state *ls, unsigned long flags){	char *fn = current_long_filename ?		current_long_filename : current_filename; 	if (!(flags & LINE_NUM)) return;	if ((flags & LEXER) && !(flags & TEXT_OUTPUT)) {		struct token t; 		t.type = CONTEXT;

Lines 1–500 of 2261.