/* * Copyright (c) 2002, The Tendra Project * 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 unmodified, 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 AUTHOR ``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 AUTHOR 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. * * * Crown Copyright (c) 1997 * * This TenDRA(r) Computer Program is subject to Copyright * owned by the United Kingdom Secretary of State for Defence * acting through the Defence Evaluation and Research Agency * (DERA). It is made available to Recipients with a * royalty-free licence for its use, reproduction, transfer * to other parties and amendment for any purpose not excluding * product development provided that any such use et cetera * shall be deemed to be acceptance of the following conditions:- * * (1) Its Recipients shall ensure that this Notice is * reproduced upon any copies or amended versions of it; * * (2) Any amended version of it shall be clearly marked to * show both the nature of and the organisation responsible * for the relevant amendment or amendments; * * (3) Its onward transfer from a recipient to another * party shall be deemed to be that party's acceptance of * these conditions; * * (4) DERA gives no warranty or assurance as to its * quality or suitability for any purpose and DERA accepts * no liability whatsoever in relation to any use to which * it may be put. * * $TenDRA: tendra/src/producers/common/parse/file.c,v 1.18 2005/10/22 13:47:15 stefanf Exp $ */ #include "config.h" #include "producer.h" #include #include "cstring.h" #include "fmm.h" #include "system.h" #include "c_types.h" #include "loc_ext.h" #include "error.h" #include "catalog.h" #include "option.h" #include "file.h" #include "buffer.h" #include "char.h" #include "dump.h" #include "lex.h" #include "preproc.h" #include "syntax.h" #include "token.h" #include "ustring.h" /* * INPUT AND OUTPUT FILES * * These are the files from which the lexical routines read their input * and to which the output routines write. */ FILE *input_file = NULL; FILE *output_file [OUTPUT_FILES] = { NULL, NULL, NULL, NULL, NULL }; /* * CURRENT FILE NAMES * * The variable input_name is used to hold the name of the current input * file name. This remains constant and is not, for example, changed by * #line directives. Similarly output_name holds the names of the output * files. */ string input_name = NULL; string output_name [OUTPUT_FILES] = { NULL, NULL, NULL, NULL, NULL }; /* * STANDARD FILE NAME * * The macro std_file_name checks whether the file name A is the special * string "-" used to indicate the standard input or output. The other * macros give the corresponding names used in error reports. */ #define std_file_name(A) ustrseq ((A), "-") #define stdin_name ustrlit ("") #define stdout_name ustrlit ("") /* * INTERNAL FILE BUFFER * * This buffer is used as the internal file buffer which is used to hold * any preprocessing directives arising from command-line options. It * has an associated dummy file and file name. */ BUFFER internal_buff = NULL_buff; static FILE *internal_file = NULL; static string internal_name = NULL; /* * INCLUDE FILE BUFFER * * This buffer is used to build up file names when processing #include * directives. */ BUFFER incl_buff = NULL_buff; /* * INPUT FLAGS * * These flags are used to record changes in the current file location. */ unsigned long crt_spaces = 0; unsigned long tab_width = 8; int crt_line_changed = 0; int crt_file_changed = 0; int crt_col_changed = 0; int crt_file_type = 0; int bad_crt_loc = 0; /* * INPUT BUFFERS * * These variables describe the buffers used in reading the input file. * Each buffer consists of an array of characters, preceded by space * for the storage of unread characters, and followed by an overflow * area for repeated end of file markers. The position within the * buffer is indicated by the pointer input_posn. Other pointers give * the end of the current buffer and the end of the last buffer. * Characters beyond the end of the buffer hold char_end. */ #define NO_BUFFER 4 #define CHAR_SZ sizeof (character) #define BUFF_SZ ((size_t) (BUFSIZ)) #define PENDING_SZ 16 #define OVERFLOW_SZ 16 #define TOTAL_SZ (PENDING_SZ + BUFF_SZ + OVERFLOW_SZ) typedef struct { string buff; string posn; string end; string eof; long bytes; } INPUT_BUFFER; static INPUT_BUFFER input_buff [NO_BUFFER] = { { NULL, NULL, NULL, NULL, 0 }, { NULL, NULL, NULL, NULL, 0 }, { NULL, NULL, NULL, NULL, 0 }, { NULL, NULL, NULL, NULL, 0 } }; string input_start = NULL; string input_posn = NULL; string input_end = NULL; string input_eof = NULL; string input_crt = NULL; static long input_bytes = 0; static int input_special = 0; static int started_buff = 0; unsigned long crt_buff_no = 0; /* * IS A FILE NAME A FULL PATHNAME? * * This routine checks whether the file name nm represents a full * pathname. */ int is_full_pathname(string nm) { character c = nm [0]; character q = (character) drive_sep; if (c == char_slash) return (1); if (c && q && nm [1] == q) { /* Allow for DOS drive letters */ return (is_alpha_char ((unsigned long) c)); } return (0); } /* * CONVERT A FILE NAME TO STANDARD FORMAT * * This routine modifies the file name nm to the standard format with * sensible forward slashes rather backslashes. */ string make_pathname(string nm) { character q = (character) file_sep; if (q != char_slash) { string s; nm = ustring_copy (nm); for (s = nm; *s; s++) { if (*s == q) *s = char_slash; } } return (nm); } /* * NORMALISE A FILE NAME * * This routine normalises the file name s by removing any . or .. * components. The result is only used in the printing of error * messages so it doesn't matter too much if the result isn't quite * right. */ static string normalise_pathname(string s) { character c; string p = s; int depth = 0; int changed = 0; BUFFER *bf = clear_buffer (&incl_buff, NULL); while (c = *(p++), c != 0) { if (c == char_slash) { if (p [0] == char_dot) { if (p [1] == char_slash) { /* Have '/./' */ p++; changed = 1; } else if (p [1] == char_dot && p [2] == char_slash) { /* Have '/../' */ string q = bf->posn; if (depth > 0) { string q0 = bf->start; *q = 0; q = ustrrchr (q0, char_slash); if (q && q != q0) { bf->posn = q; p += 2; changed = 1; c = 0; } } if (c) bfputc (bf, (int) c); depth--; } else { bfputc (bf, (int) c); depth++; } } else if (p [0] == char_slash) { /* Have '//' */ changed = 1; } else { bfputc (bf, (int) c); depth++; } } else { bfputc (bf, (int) c); } } bfputc (bf, 0); if (changed) s = ustring_copy (bf->start); return (s); } /* * SET CURRENT LOCATION * * This routine sets the current location from the filename nm. It returns * the result of applying make_pathname to nm. */ string set_crt_loc(string nm, int special) { string en; unsigned long date; if (special) { /* Standard input */ en = ustrlit (""); date = 0; } else { /* Simple file */ STAT_TYPE *fs; STAT_TYPE fstr; nm = make_pathname (nm); en = nm; fs = stat_func (strlit (nm), &fstr); date = stat_date (fs); } CREATE_loc (nm, en, nm, NULL, NULL_ptr (LOCATION), date, crt_loc); return (nm); } /* * OPEN INPUT FILE * * This routine opens the input file, using input_name as the name of the * file to be opened. If this is the null string then the standard input * is used. The file is opened in binary mode if bin is true. The routine * also allocates space for each of the buffers above. The routine returns * 1 if the file is opened successfully. */ int open_input(int bin) { if (input_file == NULL) { string nm = input_name; if (nm == NULL || std_file_name (nm)) { nm = set_crt_loc (stdin_name, 1); input_special = 1; if (!bin) input_file = stdin; } else { const char *mode = (bin ? "rb" : "r"); nm = set_crt_loc (nm, 0); input_special = 0; input_file = fopen (strlit (nm), mode); } input_name = nm; if (input_file == NULL) return (0); if (!started_buff) { unsigned i; for (i = 0; i < NO_BUFFER; i++) { string buff = xmalloc_nof (character, TOTAL_SZ); input_buff [i].buff = buff; } started_buff = 1; } crt_file_changed = 1; crt_line_changed = 1; crt_spaces = 0; } return (1); } /* * FREE SPACE ALLOCATED FOR INPUT BUFFERS * * This routine frees the memory allocated for the input buffers. */ void term_input(void) { free_buffer (&incl_buff); free_buffer (&internal_buff); if (started_buff) { unsigned i; for (i = 0; i < NO_BUFFER; i++) { xfree (input_buff [i].buff); input_buff [i].buff = NULL; } started_buff = 0; } input_start = NULL; input_bytes = 0; return; } /* * OPEN OUTPUT FILE * * This routine opens the nth output file, using output_name as the name * of the file to be opened. If this is the null string then the standard * output is used. The file is opened in binary mode if bin is true. The * routine returns 1 if the file is opened successfully. */ int open_output(int n, int bin) { if (output_file [n] == NULL) { string nm = output_name [n]; if (nm == NULL || std_file_name (nm)) { nm = stdout_name; output_name [n] = nm; output_file [n] = stdout; if (bin) return (0); } else { const char *mode = (bin ? "wb" : "w"); nm = make_pathname (nm); output_name [n] = nm; output_file [n] = fopen (strlit (nm), mode); if (output_file [n] == NULL) return (0); } } return (1); } /* * CLOSE INPUT FILE * * This routine closes the input file. */ void close_input(void) { FILE *fin = input_file; if (fin && !input_special) { if (ferror (fin) || fclose (fin)) { char *nm = strlit (input_name); error (ERROR_INTERNAL, "Reading error in '%s'", nm); } } input_file = NULL; return; } /* * CLOSE OUTPUT FILE * * This routine closes the nth output file. */ void close_output(int n) { FILE *fout = output_file [n]; if (fout && fout != stdout && fout != stderr) { if (ferror (fout) || fclose (fout)) { char *nm = strlit (output_name [n]); error (ERROR_INTERNAL, "Writing error in '%s'", nm); } } output_file [n] = NULL; return; } /* * FILL THE INPUT BUFFER * * This routine fills the current input buffer from the input file, setting * up the associated buffer pointers. It returns NULL to indicate that no * bytes were read. */ static string fill_buffer(void) { size_t i, n; size_t m = TOTAL_SZ; FILE *f = input_file; string p = input_start; /* Fill the buffer from the input file */ if (f == internal_file) { n = bfread (&internal_buff, p, BUFF_SZ); if (n < BUFF_SZ) m = n; } else if (f) { n = fread (p, CHAR_SZ, BUFF_SZ, f); if (n < BUFF_SZ) m = n; } else { n = 0; m = 0; } input_posn = p; input_end = p + n; input_eof = p + m; input_crt = p; input_bytes += (long) n; /* Fill the overflow area with char_end's */ for (i = n; i < n + OVERFLOW_SZ; i++) p [i] = char_end; if (n == 0) p = NULL; return (p); } /* * INITIALISE BUFFER * * This routine initialises buffer number i and makes it into the current * buffer. It returns NULL to indicate an empty file. */ string init_buffer(unsigned long i) { crt_buff_no = i; input_start = input_buff [i].buff + PENDING_SZ; input_bytes = 0; return (fill_buffer ()); } /* * RESUME BUFFER * * This routine makes buffer number i into the current buffer by restoring * the main values from those stored in the buffer. */ static void resume_buffer(unsigned long i) { INPUT_BUFFER *p = input_buff + i; input_start = p->buff + PENDING_SZ; input_posn = p->posn; input_end = p->end; input_eof = p->eof; input_crt = p->posn; input_bytes = p->bytes; return; } /* * FIND CURRENT FILE POSITION * * This routine finds the current file position and updates the pointers * in buffer i (which should be the current buffer number). */ long tell_buffer(unsigned long i) { long bytes_left; INPUT_BUFFER *p = input_buff + i; p->posn = input_posn; p->end = input_end; p->eof = input_eof; p->bytes = input_bytes; bytes_left = (long) (input_end - input_posn); if (bytes_left < 0) bytes_left = 0; return (input_bytes - bytes_left); } /* * SET CURRENT FILE POSITION * * This routine sets the current file position to n and the current buffer * to number i. started is false if the file has just been opened. */ void seek_buffer(unsigned long i, long n, int started) { int s; FILE *f = input_file; if (f == NULL) return; if (f == internal_file) { if (started) { /* Reset position to start of buffer */ internal_buff.posn = internal_buff.start; started = 0; } s = 0; } else { s = file_seek (f, n); } if (s == 0) { /* Perform seek by hand */ string p; if (started) { /* Rewind to start of file */ IGNORE file_seek (f, (long) 0); } p = init_buffer (i); while (input_bytes < n) { if (p == NULL) { char *nm = strlit (input_name); const char *msg = "Internal seek error in '%s'"; error (ERROR_INTERNAL, msg, nm); return; } p = fill_buffer (); } input_posn = input_end - (input_bytes - n); input_crt = input_posn; } else { if (s == -1) { char *nm = strlit (input_name); const char *msg = "Internal seek error in '%s'"; error (ERROR_INTERNAL, msg, nm); } input_start = input_buff [i].buff + PENDING_SZ; input_bytes = n; IGNORE fill_buffer (); } return; } /* * UPDATE THE CURRENT COLUMN POSITION * * The current column position is only updated at convenient junctures. * The variable input_crt is used to keep track of the last such location. */ void update_column(void) { string p = input_posn; if (p) { unsigned long n = (unsigned long) (p - input_crt); if (n) { crt_loc.column += n; input_crt = p; } } return; } /* * REFILL THE INPUT BUFFER * * This routine refills the input buffer, returning the first character. * It is called whenever the next character in the buffer is char_end. * It is possible that the character is really char_end, in which case * this is returned. Otherwise the buffer is refilled. Note that in order * for unread_char to work correctly with char_eof, (char_eof & 0xff) * must equal char_end. */ int refill_char(void) { int c; update_column (); do { string p = input_posn; if (p <= input_end) return (char_end); if (p > input_eof) return (char_eof); crt_loc.column += (unsigned long) (p - input_crt); IGNORE fill_buffer (); c = next_char (); } while (c == char_end); input_crt = input_posn; return (c); } /* * INCLUDE FILE SEARCH PATH * * The variable dir_path gives the list of directories searched for * #include'd files. The variable crt_dir_path is set after each #include * directive to the position in the path after that at which the included * file was found. */ INCL_DIR *dir_path = NULL; static INCL_DIR *crt_dir_path = NULL; static INCL_DIR *crt_found_path = NULL; /* * FIND A NAMED DIRECTORY * * This routine looks up a named directory called nm. It returns the * corresponding directory structure, or the null pointer if nm is not * defined. */ static INCL_DIR * find_directory(string nm) { INCL_DIR *p = dir_path; while (p != NULL) { string s = p->name; if (s && ustreq (s, nm)) return (p); p = p->next; } return (NULL); } /* * ADD A DIRECTORY TO THE SEARCH PATH * * This routine adds the directory dir with associated name nm to the * include file search path. */ void add_directory(string dir, string nm) { INCL_DIR *p = dir_path; INCL_DIR *q = xmalloc (sizeof(*q)); if (nm && find_directory (nm)) { char *s = strlit (nm); error (ERROR_WARNING, "Directory '%s' already defined", s); nm = NULL; } q->path = make_pathname (dir); q->name = nm; q->mode = NULL; q->no = LINK_NONE; q->next = NULL; if (p == NULL) { dir_path = q; crt_dir_path = q; } else { while (p->next) p = p->next; p->next = q; } return; } /* * SET A DIRECTORY COMPILATION MODE * * This routine sets the compilation mode for the directory named nm * to be p. */ void directory_mode(string nm, OPTIONS *p) { INCL_DIR *q = find_directory (nm); if (q) { if (q->mode) { report (preproc_loc, ERR_pragma_dir_mode (nm)); } if (p) q->mode = p; } else { report (preproc_loc, ERR_pragma_dir_undef (nm)); } return; } /* * LISTS OF START-UP AND END-UP FILES * * These variables give the lists of start-up and end-up files. These * are equivalent to #include "file" directives at respectively the start * and the end of the main include file. */ LIST(string) startup_files = NULL_list (string); LIST(string) endup_files = NULL_list (string); /* * SET UP INTERNAL START-UP FILE * * This routine sets up the built-in internal start-up file. */ void builtin_startup(void) { BUFFER *bf = &internal_buff; internal_name = DEREF_string (posn_file (crt_loc.posn)); internal_file = xmalloc (sizeof(FILE*)); if (bf->posn != bf->start) { /* Add to list of start-up files if necessary */ CONS_string (internal_name, startup_files, startup_files); bf->end = bf->posn; bf->posn = bf->start; } return; } /* * OPEN NEXT START-UP FILE * * This routine opens the next start-up file. It continues trying until * a start-up file is successfully opened or there are no start-up files * left. */ void open_startup(void) { LIST (string) p = startup_files; while (!IS_NULL_list (p)) { string fn = DEREF_string (HEAD_list (p)); p = TAIL_list (p); startup_files = p; preproc_loc = crt_loc; crt_file_type = 1; if (start_include (fn, char_quote, INCLUDE_STARTUP, 0)) return; } crt_file_type = 0; return; } /* * LIST OF INCLUDED FILES * * This list is used to record all the files included plus the position * of the current file within this list. */ typedef struct incl_file_tag { string name; int imported; HASHID macro; unsigned test; int state; PTR (LOCATION) from; STAT_TYPE *data; STAT_TYPE data_ref; struct incl_file_tag *next; } INCL_FILE; static INCL_FILE *included_files = NULL; static INCL_FILE *crt_included_file = NULL; /* * TABLE OF INCLUSIONS * * This table is used to hold the file positions for all the currently * active #include directives. Note that this is done as a finite * (but hopefully sufficiently large) array to detect recursive * inclusions. */ typedef struct { string name; FILE *fileptr; long offset; int special; int startup; int interface; OPTIONS *mode; INCL_DIR *path; INCL_DIR *found; INCL_FILE *incl; } INCL_BUFF; #define MAX_INCL_DEPTH 256 static INCL_BUFF position_array [MAX_INCL_DEPTH]; static INCL_BUFF *position = position_array; static unsigned long position_size = MAX_INCL_DEPTH; /* * SIMPLE INCLUSION DEPTH * * There are two approaches to suspending the current file - either * leaving the file open or closing it and reopening it later. The * latter is more efficient, but is limited by the maximum number of * files which can be opened at one time (FOPEN_MAX). Therefore this * strategy is only used for this number of files. Note that FOPEN_MAX * is at least 8 (including the 3 standard files). */ #define LAST_BUFFER_NO ((unsigned long) (NO_BUFFER - 1)) #define SIMPLE_INCL_DEPTH LAST_BUFFER_NO /* * SET MAXIMUM INCLUDE DEPTH * * This routine sets the maximum include file depth to n. */ void set_incl_depth(unsigned long n) { if (n > 10000) n = 10000; if (n > position_size) { /* Allocate more space if necessary */ unsigned long i, m; INCL_BUFF *p = xmalloc_nof (INCL_BUFF, n); INCL_BUFF *q = position; m = crt_option_value (OPT_VAL_include_depth); for (i = 0; i < m; i++) p [i] = q [i]; position_size = n; position = p; if (q != position_array) xfree (q); } option_value (OPT_VAL_include_depth) = n; return; } /* * CHECK WHETHER A FILE HAS ALREADY BEEN INCLUDED * * This routine checks whether the file with pathname nm and file * statistics fs has already been included and does not need to be * included again. It also sets crt_included_file. st is as in * start_include. */ int already_included(string nm, STAT_TYPE *fs, int st) { INCL_FILE *p = included_files; while (p != NULL) { int ok; if (ustreq (nm, p->name) && st != INCLUDE_CHECK) { /* Check file names */ ok = 1; } else { /* Check file statistics */ ok = stat_equal (fs, p->data); } if (ok) { /* Check matching file */ if (st == INCLUDE_CHECK) { /* Simple enquiry */ return (1); } crt_included_file = p; if (st == INCLUDE_IMPORT) { /* Imported file */ if (p->imported == INCLUDE_IMPORT) return (1); p->imported = INCLUDE_IMPORT; } if (p->state == 2) { /* Check protection macro */ unsigned def = check_macro (p->macro, 0); def &= PP_COND_MASK; if (def == p->test) return (1); } return (0); } p = p->next; } /* Create new imported file structure */ p = xmalloc (sizeof(*p)); if (st != INCLUDE_CHECK) crt_included_file = p; p->name = nm; p->imported = st; p->macro = NULL_hashid; p->state = 0; p->test = PP_TRUE; p->from = NULL_ptr (LOCATION); if (fs) { /* File system information available */ p->data = &(p->data_ref); p->data_ref = *fs; } else { p->data = NULL; } p->next = included_files; included_files = p; return (0); } /* * CHECK A FILE PROTECTION MACRO * * This routine checks whether the given macro identifier is a file * protection macro for the current file, that is to say, whether the * file has the form: * * #ifndef macro * .... * #endif * * prev gives the previous preprocessing directive and dir gives the * current preprocessing directive. */ void protection_macro(HASHID macro, int prev, int dir) { INCL_FILE *incl = crt_included_file; if (incl) { if (prev == lex_included) { if (incl->state == 0) { if (dir == lex_ifndef) { /* Have '#ifndef macro' at start of file */ incl->macro = macro; incl->test = PP_TRUE; incl->state = 1; return; } if (dir == lex_ifdef) { /* Have '#ifdef macro' at start of file */ incl->macro = macro; incl->test = PP_FALSE; incl->state = 1; return; } if (dir == lex_eof) { /* Start and end of file coincide */ incl->macro = NULL_hashid; incl->test = PP_TRUE; incl->state = 2; return; } } } if (prev == lex_end_condition) { if (incl->state == 1) { if (dir == lex_eof) { /* Have '#endif' at end of file */ incl->state = 2; return; } } } incl->state = 0; } return; } /* * CREATE A FILE NAME * * This routine forms a composite file name consisting of a directory * component d and a file component f. The up argument is true if the * existing file component is to be removed from d. */ static string add_pathname(string d, string f, int up) { if (d) { BUFFER *bf = clear_buffer (&incl_buff, NULL); bfputs (bf, d); if (up) { /* Remove file component */ string s = ustrrchr (bf->start, char_slash); if (s == NULL) return (f); bf->posn = s; } bfputc (bf, char_slash); bfputs (bf, f); return (bf->start); } return (f); } /* * FIND AN INCLUDE FILE * * This routine searches for, and opens, an included file named nm. The * argument q equals '"' or '>', depending on the form of the #include * directive. The argument st is one of the INCLUDE_ constants described * in file.h. next is true if the search is to restart at the current * position in the directory path. The routine returns 1 to indicate * that the file was opened successfully. */ int start_include(string nm, int q, int st, int next) { FILE *g; FILE *f = NULL; int special = 0; string file = nm; string dir = NULL; unsigned long c, m; string rfile = NULL; OPTIONS *mode = NULL; PTR (LOCATION) from; unsigned long date = 0; INCL_DIR *found = NULL; INCL_DIR *path = dir_path; INCL_FILE *incl = crt_included_file; /* Check for empty file name */ if (nm [0] == 0) { report (preproc_loc, ERR_cpp_include_empty ()); return (0); } /* Search for included file */ if (nm == internal_name) { /* Allow for command-line options */ rfile = ustrlit (""); f = internal_file; special = 1; } else if (is_full_pathname (nm)) { /* Allow for full file names */ if (st < INCLUDE_STARTUP) { report (preproc_loc, ERR_cpp_include_full (nm)); } f = fopen (strlit (file), "r"); } else if (std_file_name (nm)) { /* Allow for standard input (extension) */ file = stdin_name; rfile = ustrlit (""); f = stdin; special = 1; } else { /* Check quoted include directives */ if (q == char_quote) { file = add_pathname (input_name, nm, 1); f = fopen (strlit (file), "r"); found = crt_found_path; } /* Search directory path */ if (f == NULL) { if (next) { /* Start search at current position */ path = crt_dir_path; } while (f == NULL && path != NULL) { dir = path->path; file = add_pathname (dir, nm, 0); f = fopen (strlit (file), "r"); found = path; mode = path->mode; path = path->next; } } else { path = crt_dir_path; dir = DEREF_string (posn_dir (crt_loc.posn)); } } if (st == INCLUDE_CHECK) { /* Just testing ... */ if (f == NULL) return (0); if (!special) fclose_v (f); return (1); } /* Report unfound files */ if (f == NULL) { report (preproc_loc, ERR_cpp_include_unknown (nm)); return (0); } /* Check for multiple inclusions */ file = ustring_copy (file); if (special) { crt_included_file = NULL; } else { STAT_TYPE fstr; STAT_TYPE *fs = stat_func (strlit (file), &fstr); if (already_included (file, fs, st)) { /* Only read file if necessary */ from = crt_included_file->from; report (preproc_loc, ERR_cpp_include_dup (nm, from)); crt_included_file = incl; fclose_v (f); return (0); } date = stat_date (fs); } /* Store position of #include directive */ c = crt_option_value (OPT_VAL_include_depth); if (!incr_value (OPT_VAL_include_depth)) { /* Include depth too great */ crt_option_value (OPT_VAL_include_depth) = c; crt_included_file = incl; return (0); } g = input_file; position [c].name = input_name; position [c].special = input_special; position [c].startup = st; position [c].interface = crt_interface; position [c].mode = mode; position [c].path = crt_dir_path; position [c].found = crt_found_path; position [c].incl = incl; if (c < SIMPLE_INCL_DEPTH || input_special) { /* Store open file */ m = c + 1; position [c].fileptr = g; position [c].offset = tell_buffer (c); } else { /* Store position in closed file */ m = LAST_BUFFER_NO; position [c].fileptr = NULL; position [c].offset = tell_buffer (m); if (ferror (g) || fclose (g)) { char *gnm = strlit (input_name); error (ERROR_INTERNAL, "Reading error in '%s'", gnm); } } crt_found_path = found; crt_dir_path = path; /* Set up new file */ input_name = file; input_file = f; input_special = special; nm = (file + ustrlen (file)) - ustrlen (nm); if (rfile == NULL) { rfile = file; file = normalise_pathname (file); } if (option (OPT_include_verbose)) { report (preproc_loc, ERR_cpp_include_open (file)); } if (inclusion_dependencies != DEP_NONE) { /* All included files with an inclusion depth greater than * ignore_depth are ignored. ignore_depth is set to prevent * printing "" headers that are included from <> headers if we're * only interested in the former ones, and to prevent printing files * that are included from start-up or end-up files. */ static unsigned long ignore_depth = ULONG_MAX; if (c <= ignore_depth) ignore_depth = ULONG_MAX; if (st == INCLUDE_NORMAL && (inclusion_dependencies == DEP_ALL || q == char_quote)) { if (ignore_depth == ULONG_MAX) { print_dependency (strlit (input_name), NULL); } } else if (ignore_depth == ULONG_MAX) { ignore_depth = c; } } crt_loc.line--; crt_loc.column = 0; input_crt = input_posn; if (do_header) dump_include (&crt_loc, nm, st, q); from = MAKE_ptr (SIZE_loc); COPY_loc (from, crt_loc); CREATE_loc (file, rfile, nm, dir, from, date, crt_loc); if (crt_included_file) { /* Set inclusion position */ crt_included_file->from = from; } if (do_header) dump_start (&crt_loc, found); IGNORE init_buffer (m); start_preproc_if (); if (mode) { /* Begin new checking scope if necessary */ begin_option (NULL_id); use_mode (mode, ERROR_SERIOUS); } crt_file_changed = 2; crt_line_changed = 1; crt_spaces = 0; return (1); } /* * END AN INCLUDE FILE * * This routine is called at the end of each source file. prev is as * in protection_macro. The routine returns 1 if the end of file causes * a reversion to a previous file via a #include directive, and 0 if this * is the main source file. Note that start-up and end-up files are * spotted during this process (except the first start-up file which is * dealt with in process_file). Start-up files are at depth 1, whereas * end-up files are at depth 0. */ int end_include(int prev) { unsigned long c; PTR (LOCATION) loc; FILE *f = input_file; string nm = input_name; /* Check for protection macros */ if (!clear_preproc_if ()) prev = lex_end_condition; protection_macro (NULL_hashid, prev, lex_eof); /* Tidy up the current file */ if (f != NULL) { if (input_special) { if (f == internal_file) { free_buffer (&internal_buff); } } else { if (ferror (f) || fclose (f)) { char *fnm = strlit (nm); error (ERROR_INTERNAL, "Reading error in '%s'", fnm); } } input_file = NULL; if (do_header) dump_end (&crt_loc); } /* Check for previous file */ c = crt_option_value (OPT_VAL_include_depth); if (c == 0) { /* End of main file - deal with end-up files */ LIST (string) p = endup_files; while (!IS_NULL_list (p)) { string fn = DEREF_string (HEAD_list (p)); p = TAIL_list (p); endup_files = p; preproc_loc = crt_loc; crt_file_type = 1; if (start_include (fn, char_quote, INCLUDE_ENDUP, 0)) return (1); } crt_file_type = 0; return (0); } decr_value (OPT_VAL_include_depth); c--; /* End checking scope if necessary */ if (position [c].mode) end_option (0); /* Restore previous file position (don't destroy old value) */ loc = DEREF_ptr (posn_from (crt_loc.posn)); DEREF_loc (loc, crt_loc); crt_file_changed = 2; crt_line_changed = 1; crt_spaces = 0; /* Reopen the previous buffer */ input_name = position [c].name; input_file = position [c].fileptr; input_special = position [c].special; crt_dir_path = position [c].path; crt_found_path = position [c].found; crt_included_file = position [c].incl; crt_interface = position [c].interface; if (input_file == NULL) { /* Reopen old file */ char *str = strlit (input_name); if (str) { input_file = fopen (str, "r"); if (input_file == NULL) { const char *msg = "Internal file error in '%s'"; error (ERROR_INTERNAL, msg, str); crt_loc.line++; crt_loc.column = 0; input_crt = input_posn; return (end_include (lex_ignore_token)); } seek_buffer (LAST_BUFFER_NO, position [c].offset, 0); } } else { /* Resume old file position */ resume_buffer (c); } if (option (OPT_include_verbose)) { LOCATION ploc; int st = position [c].startup; ploc = crt_loc; if (st >= INCLUDE_STARTUP) ploc.line++; report (ploc, ERR_cpp_include_close (nm)); } if (do_header) dump_include (&crt_loc, NULL_string, INCLUDE_RESUMPTION, 0); crt_loc.line++; crt_loc.column = 0; input_crt = input_posn; /* Could be the end of a start-up file - try the next one */ if (c == 0) open_startup (); return (1); } /* * SETUP THE PRAGMA OPERATOR * * For the _Pragma operator the inclusion of a header is faked. The * operator's argument is already written into internal_buff at this * point. The string contents will be tokenised and consumed by * read_preproc_dir. If the end of the buffer is hit, end_include will * be called and the parsing is continued after the _Pragma operator. */ int setup_pragma(void) { int ret; string s = internal_name; internal_name = ustrlit ("<_Pragma>"); ret = start_include (internal_name, char_quote, INCLUDE_NORMAL, 0); internal_name = s; return (ret); }