| /* C Compatible Compiler Preprocessor (CCCP) |
| Copyright (C) 1986, 87, 89, 92-96, 1997 Free Software Foundation, Inc. |
| Written by Paul Rubin, June 1986 |
| Adapted to ANSI C, Richard Stallman, Jan 1987 |
| |
| This program is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published by the |
| Free Software Foundation; either version 2, or (at your option) any |
| later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. |
| |
| In other words, you are welcome to use, share and improve this program. |
| You are forbidden to forbid anyone else to use, share and improve |
| what you give them. Help stamp out software-hoarding! */ |
| |
| typedef unsigned char U_CHAR; |
| |
| #ifdef EMACS |
| #define NO_SHORTNAMES |
| #include "../src/config.h" |
| #ifdef open |
| #undef open |
| #undef read |
| #undef write |
| #endif /* open */ |
| #endif /* EMACS */ |
| |
| /* The macro EMACS is defined when cpp is distributed as part of Emacs, |
| for the sake of machines with limited C compilers. */ |
| #ifndef EMACS |
| #include "config.h" |
| #endif /* not EMACS */ |
| |
| #ifndef STANDARD_INCLUDE_DIR |
| #define STANDARD_INCLUDE_DIR "/usr/include" |
| #endif |
| |
| #include "pcp.h" |
| |
| /* By default, colon separates directories in a path. */ |
| #ifndef PATH_SEPARATOR |
| #define PATH_SEPARATOR ':' |
| #endif |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <ctype.h> |
| #include <stdio.h> |
| #include <signal.h> |
| |
| /* The following symbols should be autoconfigured: |
| HAVE_FCNTL_H |
| HAVE_STDLIB_H |
| HAVE_SYS_TIME_H |
| HAVE_UNISTD_H |
| STDC_HEADERS |
| TIME_WITH_SYS_TIME |
| In the mean time, we'll get by with approximations based |
| on existing GCC configuration symbols. */ |
| |
| #ifdef POSIX |
| # ifndef HAVE_STDLIB_H |
| # define HAVE_STDLIB_H 1 |
| # endif |
| # ifndef HAVE_UNISTD_H |
| # define HAVE_UNISTD_H 1 |
| # endif |
| # ifndef STDC_HEADERS |
| # define STDC_HEADERS 1 |
| # endif |
| #endif /* defined (POSIX) */ |
| |
| #if defined (POSIX) || (defined (USG) && !defined (VMS)) |
| # ifndef HAVE_FCNTL_H |
| # define HAVE_FCNTL_H 1 |
| # endif |
| #endif |
| |
| #ifndef RLIMIT_STACK |
| # include <time.h> |
| #else |
| # if TIME_WITH_SYS_TIME |
| # include <sys/time.h> |
| # include <time.h> |
| # else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| # endif |
| # include <sys/resource.h> |
| #endif |
| |
| #if HAVE_FCNTL_H |
| # include <fcntl.h> |
| #endif |
| |
| #include <errno.h> |
| |
| #if HAVE_STDLIB_H |
| # include <stdlib.h> |
| #else |
| char *getenv (); |
| #endif |
| |
| #if STDC_HEADERS |
| # include <string.h> |
| # ifndef bcmp |
| # define bcmp(a, b, n) memcmp (a, b, n) |
| # endif |
| # ifndef bcopy |
| # define bcopy(s, d, n) memcpy (d, s, n) |
| # endif |
| # ifndef bzero |
| # define bzero(d, n) memset (d, 0, n) |
| # endif |
| #else /* !STDC_HEADERS */ |
| char *index (); |
| char *rindex (); |
| |
| # if !defined (BSTRING) && (defined (USG) || defined (VMS)) |
| |
| # ifndef bcmp |
| # define bcmp my_bcmp |
| static int |
| my_bcmp (a, b, n) |
| register char *a; |
| register char *b; |
| register unsigned n; |
| { |
| while (n-- > 0) |
| if (*a++ != *b++) |
| return 1; |
| |
| return 0; |
| } |
| # endif /* !defined (bcmp) */ |
| |
| # ifndef bcopy |
| # define bcopy my_bcopy |
| static void |
| my_bcopy (s, d, n) |
| register char *s; |
| register char *d; |
| register unsigned n; |
| { |
| while (n-- > 0) |
| *d++ = *s++; |
| } |
| # endif /* !defined (bcopy) */ |
| |
| # ifndef bzero |
| # define bzero my_bzero |
| static void |
| my_bzero (b, length) |
| register char *b; |
| register unsigned length; |
| { |
| while (length-- > 0) |
| *b++ = 0; |
| } |
| # endif /* !defined (bzero) */ |
| |
| # endif /* !defined (BSTRING) && (defined (USG) || defined (VMS)) */ |
| #endif /* ! STDC_HEADERS */ |
| |
| #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) |
| # define __attribute__(x) |
| #endif |
| |
| #ifndef PROTO |
| # if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__) |
| # define PROTO(ARGS) ARGS |
| # else |
| # define PROTO(ARGS) () |
| # endif |
| #endif |
| |
| #if defined (__STDC__) && defined (HAVE_VPRINTF) |
| # include <stdarg.h> |
| # define VA_START(va_list, var) va_start (va_list, var) |
| # define PRINTF_ALIST(msg) char *msg, ... |
| # define PRINTF_DCL(msg) |
| # define PRINTF_PROTO(ARGS, m, n) PROTO (ARGS) __attribute__ ((format (__printf__, m, n))) |
| #else |
| # include <varargs.h> |
| # define VA_START(va_list, var) va_start (va_list) |
| # define PRINTF_ALIST(msg) msg, va_alist |
| # define PRINTF_DCL(msg) char *msg; va_dcl |
| # define PRINTF_PROTO(ARGS, m, n) () __attribute__ ((format (__printf__, m, n))) |
| # define vfprintf(file, msg, args) \ |
| { \ |
| char *a0 = va_arg(args, char *); \ |
| char *a1 = va_arg(args, char *); \ |
| char *a2 = va_arg(args, char *); \ |
| char *a3 = va_arg(args, char *); \ |
| fprintf (file, msg, a0, a1, a2, a3); \ |
| } |
| #endif |
| |
| #define PRINTF_PROTO_1(ARGS) PRINTF_PROTO(ARGS, 1, 2) |
| #define PRINTF_PROTO_2(ARGS) PRINTF_PROTO(ARGS, 2, 3) |
| #define PRINTF_PROTO_3(ARGS) PRINTF_PROTO(ARGS, 3, 4) |
| |
| #if HAVE_UNISTD_H |
| # include <unistd.h> |
| #endif |
| |
| /* VMS-specific definitions */ |
| #ifdef VMS |
| #include <descrip.h> |
| #define open(fname,mode,prot) VMS_open (fname,mode,prot) |
| #define fopen(fname,mode) VMS_fopen (fname,mode) |
| #define freopen(fname,mode,ofile) VMS_freopen (fname,mode,ofile) |
| #define fstat(fd,stbuf) VMS_fstat (fd,stbuf) |
| static int VMS_fstat (), VMS_stat (); |
| static int VMS_open (); |
| static FILE * VMS_fopen (); |
| static FILE * VMS_freopen (); |
| static void hack_vms_include_specification (); |
| #define INO_T_EQ(a, b) (!bcmp((char *) &(a), (char *) &(b), sizeof (a))) |
| #define INO_T_HASH(a) 0 |
| #define INCLUDE_LEN_FUDGE 12 /* leave room for VMS syntax conversion */ |
| #endif /* VMS */ |
| |
| /* Windows does not natively support inodes, and neither does MSDOS. */ |
| #if (defined (_WIN32) && ! defined (CYGWIN32)) || defined (__MSDOS__) |
| #define INO_T_EQ(a, b) 0 |
| #endif |
| |
| #ifndef O_RDONLY |
| #define O_RDONLY 0 |
| #endif |
| |
| #undef MIN |
| #undef MAX |
| #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) |
| #define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) |
| |
| /* Find the largest host integer type and set its size and type. |
| Don't blindly use `long'; on some crazy hosts it is shorter than `int'. */ |
| |
| #ifndef HOST_BITS_PER_WIDE_INT |
| |
| #if HOST_BITS_PER_LONG > HOST_BITS_PER_INT |
| #define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_LONG |
| #define HOST_WIDE_INT long |
| #else |
| #define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_INT |
| #define HOST_WIDE_INT int |
| #endif |
| |
| #endif |
| |
| #ifndef S_ISREG |
| #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) |
| #endif |
| |
| #ifndef S_ISDIR |
| #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) |
| #endif |
| |
| #ifndef INO_T_EQ |
| #define INO_T_EQ(a, b) ((a) == (b)) |
| #endif |
| |
| #ifndef INO_T_HASH |
| #define INO_T_HASH(a) (a) |
| #endif |
| |
| /* Define a generic NULL if one hasn't already been defined. */ |
| |
| #ifndef NULL |
| #define NULL 0 |
| #endif |
| |
| #ifndef GENERIC_PTR |
| #if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__) |
| #define GENERIC_PTR void * |
| #else |
| #define GENERIC_PTR char * |
| #endif |
| #endif |
| |
| #ifndef NULL_PTR |
| #define NULL_PTR ((GENERIC_PTR) 0) |
| #endif |
| |
| #ifndef INCLUDE_LEN_FUDGE |
| #define INCLUDE_LEN_FUDGE 0 |
| #endif |
| |
| /* External declarations. */ |
| |
| extern char *version_string; |
| #ifndef VMS |
| #ifndef HAVE_STRERROR |
| extern int sys_nerr; |
| #if defined(bsd4_4) |
| extern const char *const sys_errlist[]; |
| #else |
| extern char *sys_errlist[]; |
| #endif |
| #else /* HAVE_STRERROR */ |
| char *strerror (); |
| #endif |
| #else /* VMS */ |
| char *strerror (int,...); |
| #endif |
| HOST_WIDE_INT parse_escape PROTO((char **, HOST_WIDE_INT)); |
| HOST_WIDE_INT parse_c_expression PROTO((char *)); |
| |
| #ifndef errno |
| extern int errno; |
| #endif |
| |
| /* Name under which this program was invoked. */ |
| |
| static char *progname; |
| |
| /* Nonzero means use extra default include directories for C++. */ |
| |
| static int cplusplus; |
| |
| /* Nonzero means handle cplusplus style comments */ |
| |
| static int cplusplus_comments; |
| |
| /* Nonzero means handle #import, for objective C. */ |
| |
| static int objc; |
| |
| /* Nonzero means this is an assembly file, and allow |
| unknown directives, which could be comments. */ |
| |
| static int lang_asm; |
| |
| /* Current maximum length of directory names in the search path |
| for include files. (Altered as we get more of them.) */ |
| |
| static int max_include_len; |
| |
| /* Nonzero means turn NOTREACHED into #pragma NOTREACHED etc */ |
| |
| static int for_lint = 0; |
| |
| /* Nonzero means copy comments into the output file. */ |
| |
| static int put_out_comments = 0; |
| |
| /* Nonzero means don't process the ANSI trigraph sequences. */ |
| |
| static int no_trigraphs = 0; |
| |
| /* Nonzero means print the names of included files rather than |
| the preprocessed output. 1 means just the #include "...", |
| 2 means #include <...> as well. */ |
| |
| static int print_deps = 0; |
| |
| /* Nonzero if missing .h files in -M output are assumed to be generated |
| files and not errors. */ |
| |
| static int print_deps_missing_files = 0; |
| |
| /* Nonzero means print names of header files (-H). */ |
| |
| static int print_include_names = 0; |
| |
| /* Nonzero means don't output line number information. */ |
| |
| static int no_line_directives; |
| |
| /* Nonzero means output the text in failing conditionals, |
| inside #failed ... #endfailed. */ |
| |
| static int output_conditionals; |
| |
| /* dump_only means inhibit output of the preprocessed text |
| and instead output the definitions of all user-defined |
| macros in a form suitable for use as input to cccp. |
| dump_names means pass #define and the macro name through to output. |
| dump_definitions means pass the whole definition (plus #define) through |
| */ |
| |
| static enum {dump_none, dump_only, dump_names, dump_definitions} |
| dump_macros = dump_none; |
| |
| /* Nonzero means pass all #define and #undef directives which we actually |
| process through to the output stream. This feature is used primarily |
| to allow cc1 to record the #defines and #undefs for the sake of |
| debuggers which understand about preprocessor macros, but it may |
| also be useful with -E to figure out how symbols are defined, and |
| where they are defined. */ |
| static int debug_output = 0; |
| |
| /* Nonzero indicates special processing used by the pcp program. The |
| special effects of this mode are: |
| |
| Inhibit all macro expansion, except those inside #if directives. |
| |
| Process #define directives normally, and output their contents |
| to the output file. |
| |
| Output preconditions to pcp_outfile indicating all the relevant |
| preconditions for use of this file in a later cpp run. |
| */ |
| static FILE *pcp_outfile; |
| |
| /* Nonzero means we are inside an IF during a -pcp run. In this mode |
| macro expansion is done, and preconditions are output for all macro |
| uses requiring them. */ |
| static int pcp_inside_if; |
| |
| /* Nonzero means never to include precompiled files. |
| This is 1 since there's no way now to make precompiled files, |
| so it's not worth testing for them. */ |
| static int no_precomp = 1; |
| |
| /* Nonzero means give all the error messages the ANSI standard requires. */ |
| |
| int pedantic; |
| |
| /* Nonzero means try to make failure to fit ANSI C an error. */ |
| |
| static int pedantic_errors; |
| |
| /* Nonzero means don't print warning messages. -w. */ |
| |
| static int inhibit_warnings = 0; |
| |
| /* Nonzero means warn if slash-star appears in a slash-star comment, |
| or if newline-backslash appears in a slash-slash comment. */ |
| |
| static int warn_comments; |
| |
| /* Nonzero means warn if a macro argument is (or would be) |
| stringified with -traditional. */ |
| |
| static int warn_stringify; |
| |
| /* Nonzero means warn if there are any trigraphs. */ |
| |
| static int warn_trigraphs; |
| |
| /* Nonzero means warn if undefined identifiers are evaluated in an #if. */ |
| |
| int warn_undef; |
| |
| /* Nonzero means warn if #import is used. */ |
| |
| static int warn_import = 1; |
| |
| /* Nonzero means turn warnings into errors. */ |
| |
| static int warnings_are_errors; |
| |
| /* Nonzero means try to imitate old fashioned non-ANSI preprocessor. */ |
| |
| int traditional; |
| |
| /* Nonzero for the 1989 C Standard, including corrigenda and amendments. */ |
| |
| int c89; |
| |
| /* Nonzero causes output not to be done, |
| but directives such as #define that have side effects |
| are still obeyed. */ |
| |
| static int no_output; |
| |
| /* Nonzero means this file was included with a -imacros or -include |
| command line and should not be recorded as an include file. */ |
| |
| static int no_record_file; |
| |
| /* Nonzero means that we have finished processing the command line options. |
| This flag is used to decide whether or not to issue certain errors |
| and/or warnings. */ |
| |
| static int done_initializing = 0; |
| |
| /* Line where a newline was first seen in a string constant. */ |
| |
| static int multiline_string_line = 0; |
| |
| /* I/O buffer structure. |
| The `fname' field is nonzero for source files and #include files |
| and for the dummy text used for -D and -U. |
| It is zero for rescanning results of macro expansion |
| and for expanding macro arguments. */ |
| #define INPUT_STACK_MAX 400 |
| static struct file_buf { |
| char *fname; |
| /* Filename specified with #line directive. */ |
| char *nominal_fname; |
| /* Include file description. */ |
| struct include_file *inc; |
| /* Record where in the search path this file was found. |
| For #include_next. */ |
| struct file_name_list *dir; |
| int lineno; |
| int length; |
| U_CHAR *buf; |
| U_CHAR *bufp; |
| /* Macro that this level is the expansion of. |
| Included so that we can reenable the macro |
| at the end of this level. */ |
| struct hashnode *macro; |
| /* Value of if_stack at start of this file. |
| Used to prohibit unmatched #endif (etc) in an include file. */ |
| struct if_stack *if_stack; |
| /* Object to be freed at end of input at this level. */ |
| U_CHAR *free_ptr; |
| /* True if this is a header file included using <FILENAME>. */ |
| char system_header_p; |
| } instack[INPUT_STACK_MAX]; |
| |
| static int last_error_tick; /* Incremented each time we print it. */ |
| static int input_file_stack_tick; /* Incremented when the status changes. */ |
| |
| /* Current nesting level of input sources. |
| `instack[indepth]' is the level currently being read. */ |
| static int indepth = -1; |
| #define CHECK_DEPTH(code) \ |
| if (indepth >= (INPUT_STACK_MAX - 1)) \ |
| { \ |
| error_with_line (line_for_error (instack[indepth].lineno), \ |
| "macro or `#include' recursion too deep"); \ |
| code; \ |
| } |
| |
| /* Current depth in #include directives that use <...>. */ |
| static int system_include_depth = 0; |
| |
| typedef struct file_buf FILE_BUF; |
| |
| /* The output buffer. Its LENGTH field is the amount of room allocated |
| for the buffer, not the number of chars actually present. To get |
| that, subtract outbuf.buf from outbuf.bufp. */ |
| |
| #define OUTBUF_SIZE 10 /* initial size of output buffer */ |
| static FILE_BUF outbuf; |
| |
| /* Grow output buffer OBUF points at |
| so it can hold at least NEEDED more chars. */ |
| |
| #define check_expand(OBUF, NEEDED) \ |
| (((OBUF)->length - ((OBUF)->bufp - (OBUF)->buf) <= (NEEDED)) \ |
| ? grow_outbuf ((OBUF), (NEEDED)) : 0) |
| |
| struct file_name_list |
| { |
| struct file_name_list *next; |
| /* If the following is 1, it is a C-language system include |
| directory. */ |
| int c_system_include_path; |
| /* Mapping of file names for this directory. */ |
| struct file_name_map *name_map; |
| /* Non-zero if name_map is valid. */ |
| int got_name_map; |
| /* The include directory status. */ |
| struct stat st; |
| /* The include prefix: "" denotes the working directory, |
| otherwise fname must end in '/'. |
| The actual size is dynamically allocated. */ |
| char fname[1]; |
| }; |
| |
| /* #include "file" looks in source file dir, then stack. */ |
| /* #include <file> just looks in the stack. */ |
| /* -I directories are added to the end, then the defaults are added. */ |
| /* The */ |
| static struct default_include { |
| char *fname; /* The name of the directory. */ |
| int cplusplus; /* Only look here if we're compiling C++. */ |
| int cxx_aware; /* Includes in this directory don't need to |
| be wrapped in extern "C" when compiling |
| C++. */ |
| } include_defaults_array[] |
| #ifdef INCLUDE_DEFAULTS |
| = INCLUDE_DEFAULTS; |
| #else |
| = { |
| /* Pick up GNU C++ specific include files. */ |
| { GPLUSPLUS_INCLUDE_DIR, 1, 1 }, |
| { OLD_GPLUSPLUS_INCLUDE_DIR, 1, 1 }, |
| #ifdef CROSS_COMPILE |
| /* This is the dir for fixincludes. Put it just before |
| the files that we fix. */ |
| { GCC_INCLUDE_DIR, 0, 0 }, |
| /* For cross-compilation, this dir name is generated |
| automatically in Makefile.in. */ |
| { CROSS_INCLUDE_DIR, 0, 0 }, |
| #ifdef TOOL_INCLUDE_DIR |
| /* This is another place that the target system's headers might be. */ |
| { TOOL_INCLUDE_DIR, 0, 0 }, |
| #endif |
| #else /* not CROSS_COMPILE */ |
| #ifdef LOCAL_INCLUDE_DIR |
| /* This should be /usr/local/include and should come before |
| the fixincludes-fixed header files. */ |
| { LOCAL_INCLUDE_DIR, 0, 1 }, |
| #endif |
| #ifdef TOOL_INCLUDE_DIR |
| /* This is here ahead of GCC_INCLUDE_DIR because assert.h goes here. |
| Likewise, behind LOCAL_INCLUDE_DIR, where glibc puts its assert.h. */ |
| { TOOL_INCLUDE_DIR, 0, 0 }, |
| #endif |
| /* This is the dir for fixincludes. Put it just before |
| the files that we fix. */ |
| { GCC_INCLUDE_DIR, 0, 0 }, |
| /* Some systems have an extra dir of include files. */ |
| #ifdef SYSTEM_INCLUDE_DIR |
| { SYSTEM_INCLUDE_DIR, 0, 0 }, |
| #endif |
| { STANDARD_INCLUDE_DIR, 0, 0 }, |
| #endif /* not CROSS_COMPILE */ |
| { 0, 0, 0 } |
| }; |
| #endif /* no INCLUDE_DEFAULTS */ |
| |
| /* The code looks at the defaults through this pointer, rather than through |
| the constant structure above. This pointer gets changed if an environment |
| variable specifies other defaults. */ |
| static struct default_include *include_defaults = include_defaults_array; |
| |
| static struct file_name_list *include = 0; /* First dir to search */ |
| /* First dir to search for <file> */ |
| /* This is the first element to use for #include <...>. |
| If it is 0, use the entire chain for such includes. */ |
| static struct file_name_list *first_bracket_include = 0; |
| /* This is the first element in the chain that corresponds to |
| a directory of system header files. */ |
| static struct file_name_list *first_system_include = 0; |
| static struct file_name_list *last_include = 0; /* Last in chain */ |
| |
| /* Chain of include directories to put at the end of the other chain. */ |
| static struct file_name_list *after_include = 0; |
| static struct file_name_list *last_after_include = 0; /* Last in chain */ |
| |
| /* Chain to put at the start of the system include files. */ |
| static struct file_name_list *before_system = 0; |
| static struct file_name_list *last_before_system = 0; /* Last in chain */ |
| |
| /* Directory prefix that should replace `/usr' in the standard |
| include file directories. */ |
| static char *include_prefix; |
| |
| /* Maintain and search list of included files. */ |
| |
| struct include_file { |
| struct include_file *next; /* for include_hashtab */ |
| struct include_file *next_ino; /* for include_ino_hashtab */ |
| char *fname; |
| /* If the following is the empty string, it means #pragma once |
| was seen in this include file, or #import was applied to the file. |
| Otherwise, if it is nonzero, it is a macro name. |
| Don't include the file again if that macro is defined. */ |
| U_CHAR *control_macro; |
| /* Nonzero if the dependency on this include file has been output. */ |
| int deps_output; |
| struct stat st; |
| }; |
| |
| /* Hash tables of files already included with #include or #import. |
| include_hashtab is by full name; include_ino_hashtab is by inode number. */ |
| |
| #define INCLUDE_HASHSIZE 61 |
| static struct include_file *include_hashtab[INCLUDE_HASHSIZE]; |
| static struct include_file *include_ino_hashtab[INCLUDE_HASHSIZE]; |
| |
| /* Global list of strings read in from precompiled files. This list |
| is kept in the order the strings are read in, with new strings being |
| added at the end through stringlist_tailp. We use this list to output |
| the strings at the end of the run. |
| */ |
| static STRINGDEF *stringlist; |
| static STRINGDEF **stringlist_tailp = &stringlist; |
| |
| |
| /* Structure returned by create_definition */ |
| typedef struct macrodef MACRODEF; |
| struct macrodef |
| { |
| struct definition *defn; |
| U_CHAR *symnam; |
| int symlen; |
| }; |
| |
| enum sharp_token_type { |
| NO_SHARP_TOKEN = 0, /* token not present */ |
| |
| SHARP_TOKEN = '#', /* token spelled with # only */ |
| WHITE_SHARP_TOKEN, /* token spelled with # and white space */ |
| |
| PERCENT_COLON_TOKEN = '%', /* token spelled with %: only */ |
| WHITE_PERCENT_COLON_TOKEN /* token spelled with %: and white space */ |
| }; |
| |
| /* Structure allocated for every #define. For a simple replacement |
| such as |
| #define foo bar , |
| nargs = -1, the `pattern' list is null, and the expansion is just |
| the replacement text. Nargs = 0 means a functionlike macro with no args, |
| e.g., |
| #define getchar() getc (stdin) . |
| When there are args, the expansion is the replacement text with the |
| args squashed out, and the reflist is a list describing how to |
| build the output from the input: e.g., "3 chars, then the 1st arg, |
| then 9 chars, then the 3rd arg, then 0 chars, then the 2nd arg". |
| The chars here come from the expansion. Whatever is left of the |
| expansion after the last arg-occurrence is copied after that arg. |
| Note that the reflist can be arbitrarily long--- |
| its length depends on the number of times the arguments appear in |
| the replacement text, not how many args there are. Example: |
| #define f(x) x+x+x+x+x+x+x would have replacement text "++++++" and |
| pattern list |
| { (0, 1), (1, 1), (1, 1), ..., (1, 1), NULL } |
| where (x, y) means (nchars, argno). */ |
| |
| typedef struct definition DEFINITION; |
| struct definition { |
| int nargs; |
| int length; /* length of expansion string */ |
| int predefined; /* True if the macro was builtin or */ |
| /* came from the command line */ |
| U_CHAR *expansion; |
| int line; /* Line number of definition */ |
| char *file; /* File of definition */ |
| char rest_args; /* Nonzero if last arg. absorbs the rest */ |
| struct reflist { |
| struct reflist *next; |
| |
| enum sharp_token_type stringify; /* set if a # operator before arg */ |
| enum sharp_token_type raw_before; /* set if a ## operator before arg */ |
| enum sharp_token_type raw_after; /* set if a ## operator after arg */ |
| |
| char rest_args; /* Nonzero if this arg. absorbs the rest */ |
| int nchars; /* Number of literal chars to copy before |
| this arg occurrence. */ |
| int argno; /* Number of arg to substitute (origin-0) */ |
| } *pattern; |
| union { |
| /* Names of macro args, concatenated in reverse order |
| with comma-space between them. |
| The only use of this is that we warn on redefinition |
| if this differs between the old and new definitions. */ |
| U_CHAR *argnames; |
| } args; |
| }; |
| |
| /* different kinds of things that can appear in the value field |
| of a hash node. Actually, this may be useless now. */ |
| union hashval { |
| char *cpval; |
| DEFINITION *defn; |
| KEYDEF *keydef; |
| }; |
| |
| /* |
| * special extension string that can be added to the last macro argument to |
| * allow it to absorb the "rest" of the arguments when expanded. Ex: |
| * #define wow(a, b...) process (b, a, b) |
| * { wow (1, 2, 3); } -> { process (2, 3, 1, 2, 3); } |
| * { wow (one, two); } -> { process (two, one, two); } |
| * if this "rest_arg" is used with the concat token '##' and if it is not |
| * supplied then the token attached to with ## will not be outputted. Ex: |
| * #define wow (a, b...) process (b ## , a, ## b) |
| * { wow (1, 2); } -> { process (2, 1, 2); } |
| * { wow (one); } -> { process (one); { |
| */ |
| static char rest_extension[] = "..."; |
| #define REST_EXTENSION_LENGTH (sizeof (rest_extension) - 1) |
| |
| /* The structure of a node in the hash table. The hash table |
| has entries for all tokens defined by #define directives (type T_MACRO), |
| plus some special tokens like __LINE__ (these each have their own |
| type, and the appropriate code is run when that type of node is seen. |
| It does not contain control words like "#define", which are recognized |
| by a separate piece of code. */ |
| |
| /* different flavors of hash nodes --- also used in keyword table */ |
| enum node_type { |
| T_DEFINE = 1, /* the `#define' keyword */ |
| T_INCLUDE, /* the `#include' keyword */ |
| T_INCLUDE_NEXT, /* the `#include_next' keyword */ |
| T_IMPORT, /* the `#import' keyword */ |
| T_IFDEF, /* the `#ifdef' keyword */ |
| T_IFNDEF, /* the `#ifndef' keyword */ |
| T_IF, /* the `#if' keyword */ |
| T_ELSE, /* `#else' */ |
| T_PRAGMA, /* `#pragma' */ |
| T_ELIF, /* `#elif' */ |
| T_UNDEF, /* `#undef' */ |
| T_LINE, /* `#line' */ |
| T_ERROR, /* `#error' */ |
| T_WARNING, /* `#warning' */ |
| T_ENDIF, /* `#endif' */ |
| T_SCCS, /* `#sccs', used on system V. */ |
| T_IDENT, /* `#ident', used on system V. */ |
| T_ASSERT, /* `#assert', taken from system V. */ |
| T_UNASSERT, /* `#unassert', taken from system V. */ |
| T_SPECLINE, /* special symbol `__LINE__' */ |
| T_DATE, /* `__DATE__' */ |
| T_FILE, /* `__FILE__' */ |
| T_BASE_FILE, /* `__BASE_FILE__' */ |
| T_INCLUDE_LEVEL, /* `__INCLUDE_LEVEL__' */ |
| T_VERSION, /* `__VERSION__' */ |
| T_SIZE_TYPE, /* `__SIZE_TYPE__' */ |
| T_PTRDIFF_TYPE, /* `__PTRDIFF_TYPE__' */ |
| T_WCHAR_TYPE, /* `__WCHAR_TYPE__' */ |
| T_USER_LABEL_PREFIX_TYPE, /* `__USER_LABEL_PREFIX__' */ |
| T_REGISTER_PREFIX_TYPE, /* `__REGISTER_PREFIX__' */ |
| T_IMMEDIATE_PREFIX_TYPE, /* `__IMMEDIATE_PREFIX__' */ |
| T_TIME, /* `__TIME__' */ |
| T_CONST, /* Constant value, used by `__STDC__' */ |
| T_MACRO, /* macro defined by `#define' */ |
| T_DISABLED, /* macro temporarily turned off for rescan */ |
| T_SPEC_DEFINED, /* special `defined' macro for use in #if statements */ |
| T_PCSTRING, /* precompiled string (hashval is KEYDEF *) */ |
| T_UNUSED /* Used for something not defined. */ |
| }; |
| |
| struct hashnode { |
| struct hashnode *next; /* double links for easy deletion */ |
| struct hashnode *prev; |
| struct hashnode **bucket_hdr; /* also, a back pointer to this node's hash |
| chain is kept, in case the node is the head |
| of the chain and gets deleted. */ |
| enum node_type type; /* type of special token */ |
| int length; /* length of token, for quick comparison */ |
| U_CHAR *name; /* the actual name */ |
| union hashval value; /* pointer to expansion, or whatever */ |
| }; |
| |
| typedef struct hashnode HASHNODE; |
| |
| /* Some definitions for the hash table. The hash function MUST be |
| computed as shown in hashf () below. That is because the rescan |
| loop computes the hash value `on the fly' for most tokens, |
| in order to avoid the overhead of a lot of procedure calls to |
| the hashf () function. Hashf () only exists for the sake of |
| politeness, for use when speed isn't so important. */ |
| |
| #define HASHSIZE 1403 |
| static HASHNODE *hashtab[HASHSIZE]; |
| #define HASHSTEP(old, c) ((old << 2) + c) |
| #define MAKE_POS(v) (v & 0x7fffffff) /* make number positive */ |
| |
| /* Symbols to predefine. */ |
| |
| #ifdef CPP_PREDEFINES |
| static char *predefs = CPP_PREDEFINES; |
| #else |
| static char *predefs = ""; |
| #endif |
| |
| /* We let tm.h override the types used here, to handle trivial differences |
| such as the choice of unsigned int or long unsigned int for size_t. |
| When machines start needing nontrivial differences in the size type, |
| it would be best to do something here to figure out automatically |
| from other information what type to use. */ |
| |
| /* The string value for __SIZE_TYPE__. */ |
| |
| #ifndef SIZE_TYPE |
| #define SIZE_TYPE "long unsigned int" |
| #endif |
| |
| /* The string value for __PTRDIFF_TYPE__. */ |
| |
| #ifndef PTRDIFF_TYPE |
| #define PTRDIFF_TYPE "long int" |
| #endif |
| |
| /* The string value for __WCHAR_TYPE__. */ |
| |
| #ifndef WCHAR_TYPE |
| #define WCHAR_TYPE "int" |
| #endif |
| char * wchar_type = WCHAR_TYPE; |
| #undef WCHAR_TYPE |
| |
| /* The string value for __USER_LABEL_PREFIX__ */ |
| |
| #ifndef USER_LABEL_PREFIX |
| #define USER_LABEL_PREFIX "" |
| #endif |
| |
| /* The string value for __REGISTER_PREFIX__ */ |
| |
| #ifndef REGISTER_PREFIX |
| #define REGISTER_PREFIX "" |
| #endif |
| |
| /* The string value for __IMMEDIATE_PREFIX__ */ |
| |
| #ifndef IMMEDIATE_PREFIX |
| #define IMMEDIATE_PREFIX "" |
| #endif |
| |
| /* In the definition of a #assert name, this structure forms |
| a list of the individual values asserted. |
| Each value is itself a list of "tokens". |
| These are strings that are compared by name. */ |
| |
| struct tokenlist_list { |
| struct tokenlist_list *next; |
| struct arglist *tokens; |
| }; |
| |
| struct assertion_hashnode { |
| struct assertion_hashnode *next; /* double links for easy deletion */ |
| struct assertion_hashnode *prev; |
| /* also, a back pointer to this node's hash |
| chain is kept, in case the node is the head |
| of the chain and gets deleted. */ |
| struct assertion_hashnode **bucket_hdr; |
| int length; /* length of token, for quick comparison */ |
| U_CHAR *name; /* the actual name */ |
| /* List of token-sequences. */ |
| struct tokenlist_list *value; |
| }; |
| |
| typedef struct assertion_hashnode ASSERTION_HASHNODE; |
| |
| /* Some definitions for the hash table. The hash function MUST be |
| computed as shown in hashf below. That is because the rescan |
| loop computes the hash value `on the fly' for most tokens, |
| in order to avoid the overhead of a lot of procedure calls to |
| the hashf function. hashf only exists for the sake of |
| politeness, for use when speed isn't so important. */ |
| |
| #define ASSERTION_HASHSIZE 37 |
| static ASSERTION_HASHNODE *assertion_hashtab[ASSERTION_HASHSIZE]; |
| |
| /* Nonzero means inhibit macroexpansion of what seem to be |
| assertion tests, in rescan. For #if. */ |
| static int assertions_flag; |
| |
| /* `struct directive' defines one #-directive, including how to handle it. */ |
| |
| #define DO_PROTO PROTO((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *)) |
| |
| struct directive { |
| int length; /* Length of name */ |
| int (*func) DO_PROTO; /* Function to handle directive */ |
| char *name; /* Name of directive */ |
| enum node_type type; /* Code which describes which directive. */ |
| char angle_brackets; /* Nonzero => <...> is special. */ |
| char traditional_comments; /* Nonzero: keep comments if -traditional. */ |
| char pass_thru; /* Copy directive to output: |
| if 1, copy if dumping definitions; |
| if 2, always copy, after preprocessing. */ |
| }; |
| |
| /* These functions are declared to return int instead of void since they |
| are going to be placed in the table and some old compilers have trouble with |
| pointers to functions returning void. */ |
| |
| static int do_assert DO_PROTO; |
| static int do_define DO_PROTO; |
| static int do_elif DO_PROTO; |
| static int do_else DO_PROTO; |
| static int do_endif DO_PROTO; |
| static int do_error DO_PROTO; |
| static int do_ident DO_PROTO; |
| static int do_if DO_PROTO; |
| static int do_include DO_PROTO; |
| static int do_line DO_PROTO; |
| static int do_pragma DO_PROTO; |
| #ifdef SCCS_DIRECTIVE |
| static int do_sccs DO_PROTO; |
| #endif |
| static int do_unassert DO_PROTO; |
| static int do_undef DO_PROTO; |
| static int do_warning DO_PROTO; |
| static int do_xifdef DO_PROTO; |
| |
| /* Here is the actual list of #-directives, most-often-used first. */ |
| |
| static struct directive directive_table[] = { |
| { 6, do_define, "define", T_DEFINE, 0, 1, 1}, |
| { 2, do_if, "if", T_IF}, |
| { 5, do_xifdef, "ifdef", T_IFDEF}, |
| { 6, do_xifdef, "ifndef", T_IFNDEF}, |
| { 5, do_endif, "endif", T_ENDIF}, |
| { 4, do_else, "else", T_ELSE}, |
| { 4, do_elif, "elif", T_ELIF}, |
| { 4, do_line, "line", T_LINE}, |
| { 7, do_include, "include", T_INCLUDE, 1}, |
| { 12, do_include, "include_next", T_INCLUDE_NEXT, 1}, |
| { 6, do_include, "import", T_IMPORT, 1}, |
| { 5, do_undef, "undef", T_UNDEF}, |
| { 5, do_error, "error", T_ERROR}, |
| { 7, do_warning, "warning", T_WARNING}, |
| #ifdef SCCS_DIRECTIVE |
| { 4, do_sccs, "sccs", T_SCCS}, |
| #endif |
| { 6, do_pragma, "pragma", T_PRAGMA, 0, 0, 2}, |
| { 5, do_ident, "ident", T_IDENT}, |
| { 6, do_assert, "assert", T_ASSERT}, |
| { 8, do_unassert, "unassert", T_UNASSERT}, |
| { -1, 0, "", T_UNUSED}, |
| }; |
| |
| /* When a directive handler is called, |
| this points to the # (or the : of the %:) that started the directive. */ |
| U_CHAR *directive_start; |
| |
| /* table to tell if char can be part of a C identifier. */ |
| U_CHAR is_idchar[256]; |
| /* table to tell if char can be first char of a c identifier. */ |
| U_CHAR is_idstart[256]; |
| /* table to tell if c is horizontal space. */ |
| static U_CHAR is_hor_space[256]; |
| /* table to tell if c is horizontal or vertical space. */ |
| U_CHAR is_space[256]; |
| /* names of some characters */ |
| static char *char_name[256]; |
| |
| #define SKIP_WHITE_SPACE(p) do { while (is_hor_space[*p]) p++; } while (0) |
| #define SKIP_ALL_WHITE_SPACE(p) do { while (is_space[*p]) p++; } while (0) |
| |
| static int errors = 0; /* Error counter for exit code */ |
| |
| /* Name of output file, for error messages. */ |
| static char *out_fname; |
| |
| |
| /* Stack of conditionals currently in progress |
| (including both successful and failing conditionals). */ |
| |
| struct if_stack { |
| struct if_stack *next; /* for chaining to the next stack frame */ |
| char *fname; /* copied from input when frame is made */ |
| int lineno; /* similarly */ |
| int if_succeeded; /* true if a leg of this if-group |
| has been passed through rescan */ |
| U_CHAR *control_macro; /* For #ifndef at start of file, |
| this is the macro name tested. */ |
| enum node_type type; /* type of last directive seen in this group */ |
| }; |
| typedef struct if_stack IF_STACK_FRAME; |
| static IF_STACK_FRAME *if_stack = NULL; |
| |
| /* Buffer of -M output. */ |
| static char *deps_buffer; |
| |
| /* Number of bytes allocated in above. */ |
| static int deps_allocated_size; |
| |
| /* Number of bytes used. */ |
| static int deps_size; |
| |
| /* Number of bytes since the last newline. */ |
| static int deps_column; |
| |
| /* Nonzero means -I- has been seen, |
| so don't look for #include "foo" the source-file directory. */ |
| static int ignore_srcdir; |
| |
| static int safe_read PROTO((int, char *, int)); |
| static void safe_write PROTO((int, char *, int)); |
| |
| int main PROTO((int, char **)); |
| |
| static void path_include PROTO((char *)); |
| |
| static U_CHAR *index0 PROTO((U_CHAR *, int, size_t)); |
| |
| static void trigraph_pcp PROTO((FILE_BUF *)); |
| |
| static void newline_fix PROTO((U_CHAR *)); |
| static void name_newline_fix PROTO((U_CHAR *)); |
| |
| static char *get_lintcmd PROTO((U_CHAR *, U_CHAR *, U_CHAR **, int *, int *)); |
| |
| static void rescan PROTO((FILE_BUF *, int)); |
| |
| static FILE_BUF expand_to_temp_buffer PROTO((U_CHAR *, U_CHAR *, int, int)); |
| |
| static int handle_directive PROTO((FILE_BUF *, FILE_BUF *)); |
| |
| static struct tm *timestamp PROTO((void)); |
| static void special_symbol PROTO((HASHNODE *, FILE_BUF *)); |
| |
| static int is_system_include PROTO((char *)); |
| static char *base_name PROTO((char *)); |
| static int absolute_filename PROTO((char *)); |
| static size_t simplify_filename PROTO((char *)); |
| |
| static char *read_filename_string PROTO((int, FILE *)); |
| static struct file_name_map *read_name_map PROTO((char *)); |
| static int open_include_file PROTO((char *, struct file_name_list *, U_CHAR *, struct include_file **)); |
| static char *remap_include_file PROTO((char *, struct file_name_list *)); |
| static int lookup_ino_include PROTO((struct include_file *)); |
| |
| static void finclude PROTO((int, struct include_file *, FILE_BUF *, int, struct file_name_list *)); |
| static void record_control_macro PROTO((struct include_file *, U_CHAR *)); |
| |
| static char *check_precompiled PROTO((int, struct stat *, char *, char **)); |
| static int check_preconditions PROTO((char *)); |
| static void pcfinclude PROTO((U_CHAR *, U_CHAR *, U_CHAR *, FILE_BUF *)); |
| static void pcstring_used PROTO((HASHNODE *)); |
| static void write_output PROTO((void)); |
| static void pass_thru_directive PROTO((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *)); |
| |
| static MACRODEF create_definition PROTO((U_CHAR *, U_CHAR *, FILE_BUF *)); |
| |
| static int check_macro_name PROTO((U_CHAR *, char *)); |
| static int compare_defs PROTO((DEFINITION *, DEFINITION *)); |
| static int comp_def_part PROTO((int, U_CHAR *, int, U_CHAR *, int, int)); |
| |
| static DEFINITION *collect_expansion PROTO((U_CHAR *, U_CHAR *, int, struct arglist *)); |
| |
| int check_assertion PROTO((U_CHAR *, int, int, struct arglist *)); |
| static int compare_token_lists PROTO((struct arglist *, struct arglist *)); |
| |
| static struct arglist *read_token_list PROTO((U_CHAR **, U_CHAR *, int *)); |
| static void free_token_list PROTO((struct arglist *)); |
| |
| static ASSERTION_HASHNODE *assertion_install PROTO((U_CHAR *, int, int)); |
| static ASSERTION_HASHNODE *assertion_lookup PROTO((U_CHAR *, int, int)); |
| static void delete_assertion PROTO((ASSERTION_HASHNODE *)); |
| |
| static void do_once PROTO((void)); |
| |
| static HOST_WIDE_INT eval_if_expression PROTO((U_CHAR *, int)); |
| static void conditional_skip PROTO((FILE_BUF *, int, enum node_type, U_CHAR *, FILE_BUF *)); |
| static void skip_if_group PROTO((FILE_BUF *, int, FILE_BUF *)); |
| static void validate_else PROTO((U_CHAR *, U_CHAR *)); |
| |
| static U_CHAR *skip_to_end_of_comment PROTO((FILE_BUF *, int *, int)); |
| static U_CHAR *skip_quoted_string PROTO((U_CHAR *, U_CHAR *, int, int *, int *, int *)); |
| static char *quote_string PROTO((char *, char *)); |
| static U_CHAR *skip_paren_group PROTO((FILE_BUF *)); |
| |
| /* Last arg to output_line_directive. */ |
| enum file_change_code {same_file, enter_file, leave_file}; |
| static void output_line_directive PROTO((FILE_BUF *, FILE_BUF *, int, enum file_change_code)); |
| |
| static void macroexpand PROTO((HASHNODE *, FILE_BUF *)); |
| |
| struct argdata; |
| static char *macarg PROTO((struct argdata *, int)); |
| |
| static U_CHAR *macarg1 PROTO((U_CHAR *, U_CHAR *, int *, int *, int *, int)); |
| |
| static int discard_comments PROTO((U_CHAR *, int, int)); |
| |
| static int change_newlines PROTO((U_CHAR *, int)); |
| |
| char *my_strerror PROTO((int)); |
| void error PRINTF_PROTO_1((char *, ...)); |
| static void verror PROTO((char *, va_list)); |
| static void error_from_errno PROTO((char *)); |
| void warning PRINTF_PROTO_1((char *, ...)); |
| static void vwarning PROTO((char *, va_list)); |
| static void error_with_line PRINTF_PROTO_2((int, char *, ...)); |
| static void verror_with_line PROTO((int, char *, va_list)); |
| static void vwarning_with_line PROTO((int, char *, va_list)); |
| static void warning_with_line PRINTF_PROTO_2((int, char *, ...)); |
| void pedwarn PRINTF_PROTO_1((char *, ...)); |
| void pedwarn_with_line PRINTF_PROTO_2((int, char *, ...)); |
| static void pedwarn_with_file_and_line PRINTF_PROTO_3((char *, int, char *, ...)); |
| |
| static void print_containing_files PROTO((void)); |
| |
| static int line_for_error PROTO((int)); |
| static int grow_outbuf PROTO((FILE_BUF *, int)); |
| |
| static HASHNODE *install PROTO((U_CHAR *, int, enum node_type, char *, int)); |
| HASHNODE *lookup PROTO((U_CHAR *, int, int)); |
| static void delete_macro PROTO((HASHNODE *)); |
| static int hashf PROTO((U_CHAR *, int, int)); |
| |
| static void dump_single_macro PROTO((HASHNODE *, FILE *)); |
| static void dump_all_macros PROTO((void)); |
| static void dump_defn_1 PROTO((U_CHAR *, int, int, FILE *)); |
| static void dump_arg_n PROTO((DEFINITION *, int, FILE *)); |
| |
| static void initialize_char_syntax PROTO((void)); |
| static void initialize_builtins PROTO((FILE_BUF *, FILE_BUF *)); |
| |
| static void make_definition PROTO((char *, FILE_BUF *)); |
| static void make_undef PROTO((char *, FILE_BUF *)); |
| |
| static void make_assertion PROTO((char *, char *)); |
| |
| static struct file_name_list *new_include_prefix PROTO((struct file_name_list *, char *, char *)); |
| static void append_include_chain PROTO((struct file_name_list *, struct file_name_list *)); |
| |
| static void deps_output PROTO((char *, int)); |
| |
| static void fatal PRINTF_PROTO_1((char *, ...)) __attribute__ ((noreturn)); |
| void fancy_abort PROTO((void)) __attribute__ ((noreturn)); |
| static void perror_with_name PROTO((char *)); |
| static void pfatal_with_name PROTO((char *)) __attribute__ ((noreturn)); |
| static void pipe_closed PROTO((int)) __attribute__ ((noreturn)); |
| |
| static void memory_full PROTO((void)) __attribute__ ((noreturn)); |
| GENERIC_PTR xmalloc PROTO((size_t)); |
| static GENERIC_PTR xrealloc PROTO((GENERIC_PTR, size_t)); |
| static GENERIC_PTR xcalloc PROTO((size_t, size_t)); |
| static char *savestring PROTO((char *)); |
| |
| /* Read LEN bytes at PTR from descriptor DESC, for file FILENAME, |
| retrying if necessary. If MAX_READ_LEN is defined, read at most |
| that bytes at a time. Return a negative value if an error occurs, |
| otherwise return the actual number of bytes read, |
| which must be LEN unless end-of-file was reached. */ |
| |
| static int |
| safe_read (desc, ptr, len) |
| int desc; |
| char *ptr; |
| int len; |
| { |
| int left, rcount, nchars; |
| |
| left = len; |
| while (left > 0) { |
| rcount = left; |
| #ifdef MAX_READ_LEN |
| if (rcount > MAX_READ_LEN) |
| rcount = MAX_READ_LEN; |
| #endif |
| nchars = read (desc, ptr, rcount); |
| if (nchars < 0) |
| { |
| #ifdef EINTR |
| if (errno == EINTR) |
| continue; |
| #endif |
| return nchars; |
| } |
| if (nchars == 0) |
| break; |
| ptr += nchars; |
| left -= nchars; |
| } |
| return len - left; |
| } |
| |
| /* Write LEN bytes at PTR to descriptor DESC, |
| retrying if necessary, and treating any real error as fatal. |
| If MAX_WRITE_LEN is defined, write at most that many bytes at a time. */ |
| |
| static void |
| safe_write (desc, ptr, len) |
| int desc; |
| char *ptr; |
| int len; |
| { |
| int wcount, written; |
| |
| while (len > 0) { |
| wcount = len; |
| #ifdef MAX_WRITE_LEN |
| if (wcount > MAX_WRITE_LEN) |
| wcount = MAX_WRITE_LEN; |
| #endif |
| written = write (desc, ptr, wcount); |
| if (written < 0) |
| { |
| #ifdef EINTR |
| if (errno == EINTR) |
| continue; |
| #endif |
| pfatal_with_name (out_fname); |
| } |
| ptr += written; |
| len -= written; |
| } |
| } |
| |
| int |
| main (argc, argv) |
| int argc; |
| char **argv; |
| { |
| struct stat st; |
| char *in_fname; |
| char *cp; |
| int f, i; |
| FILE_BUF *fp; |
| char **pend_files = (char **) xmalloc (argc * sizeof (char *)); |
| char **pend_defs = (char **) xmalloc (argc * sizeof (char *)); |
| char **pend_undefs = (char **) xmalloc (argc * sizeof (char *)); |
| char **pend_assertions = (char **) xmalloc (argc * sizeof (char *)); |
| char **pend_includes = (char **) xmalloc (argc * sizeof (char *)); |
| |
| /* Record the option used with each element of pend_assertions. |
| This is preparation for supporting more than one option for making |
| an assertion. */ |
| char **pend_assertion_options = (char **) xmalloc (argc * sizeof (char *)); |
| int inhibit_predefs = 0; |
| int no_standard_includes = 0; |
| int no_standard_cplusplus_includes = 0; |
| int missing_newline = 0; |
| |
| /* Non-0 means don't output the preprocessed program. */ |
| int inhibit_output = 0; |
| /* Non-0 means -v, so print the full set of include dirs. */ |
| int verbose = 0; |
| |
| /* File name which deps are being written to. |
| This is 0 if deps are being written to stdout. */ |
| char *deps_file = 0; |
| /* Fopen file mode to open deps_file with. */ |
| char *deps_mode = "a"; |
| /* Stream on which to print the dependency information. */ |
| FILE *deps_stream = 0; |
| /* Target-name to write with the dependency information. */ |
| char *deps_target = 0; |
| |
| #ifdef RLIMIT_STACK |
| /* Get rid of any avoidable limit on stack size. */ |
| { |
| struct rlimit rlim; |
| |
| /* Set the stack limit huge so that alloca (particularly stringtab |
| in dbxread.c) does not fail. */ |
| getrlimit (RLIMIT_STACK, &rlim); |
| rlim.rlim_cur = rlim.rlim_max; |
| setrlimit (RLIMIT_STACK, &rlim); |
| } |
| #endif /* RLIMIT_STACK defined */ |
| |
| #ifdef SIGPIPE |
| signal (SIGPIPE, pipe_closed); |
| #endif |
| |
| progname = base_name (argv[0]); |
| |
| #ifdef VMS |
| { |
| /* Remove extension from PROGNAME. */ |
| char *p; |
| char *s = progname = savestring (progname); |
| |
| if ((p = rindex (s, ';')) != 0) *p = '\0'; /* strip version number */ |
| if ((p = rindex (s, '.')) != 0 /* strip type iff ".exe" */ |
| && (p[1] == 'e' || p[1] == 'E') |
| && (p[2] == 'x' || p[2] == 'X') |
| && (p[3] == 'e' || p[3] == 'E') |
| && !p[4]) |
| *p = '\0'; |
| } |
| #endif |
| |
| in_fname = NULL; |
| out_fname = NULL; |
| |
| /* Initialize is_idchar. */ |
| initialize_char_syntax (); |
| |
| no_line_directives = 0; |
| no_trigraphs = 1; |
| dump_macros = dump_none; |
| no_output = 0; |
| cplusplus = 0; |
| cplusplus_comments = 1; |
| |
| bzero ((char *) pend_files, argc * sizeof (char *)); |
| bzero ((char *) pend_defs, argc * sizeof (char *)); |
| bzero ((char *) pend_undefs, argc * sizeof (char *)); |
| bzero ((char *) pend_assertions, argc * sizeof (char *)); |
| bzero ((char *) pend_includes, argc * sizeof (char *)); |
| |
| /* Process switches and find input file name. */ |
| |
| for (i = 1; i < argc; i++) { |
| if (argv[i][0] != '-') { |
| if (out_fname != NULL) |
| fatal ("Usage: %s [switches] input output", argv[0]); |
| else if (in_fname != NULL) |
| out_fname = argv[i]; |
| else |
| in_fname = argv[i]; |
| } else { |
| switch (argv[i][1]) { |
| |
| case 'i': |
| if (!strcmp (argv[i], "-include")) { |
| if (i + 1 == argc) |
| fatal ("Filename missing after `-include' option"); |
| else |
| simplify_filename (pend_includes[i] = argv[++i]); |
| } |
| if (!strcmp (argv[i], "-imacros")) { |
| if (i + 1 == argc) |
| fatal ("Filename missing after `-imacros' option"); |
| else |
| simplify_filename (pend_files[i] = argv[++i]); |
| } |
| if (!strcmp (argv[i], "-iprefix")) { |
| if (i + 1 == argc) |
| fatal ("Filename missing after `-iprefix' option"); |
| else |
| include_prefix = argv[++i]; |
| } |
| if (!strcmp (argv[i], "-ifoutput")) { |
| output_conditionals = 1; |
| } |
| if (!strcmp (argv[i], "-isystem")) { |
| struct file_name_list *dirtmp; |
| |
| if (! (dirtmp = new_include_prefix (NULL_PTR, "", argv[++i]))) |
| break; |
| dirtmp->c_system_include_path = 1; |
| |
| if (before_system == 0) |
| before_system = dirtmp; |
| else |
| last_before_system->next = dirtmp; |
| last_before_system = dirtmp; /* Tail follows the last one */ |
| } |
| /* Add directory to end of path for includes, |
| with the default prefix at the front of its name. */ |
| if (!strcmp (argv[i], "-iwithprefix")) { |
| struct file_name_list *dirtmp; |
| char *prefix; |
| |
| if (include_prefix != 0) |
| prefix = include_prefix; |
| else { |
| prefix = savestring (GCC_INCLUDE_DIR); |
| /* Remove the `include' from /usr/local/lib/gcc.../include. */ |
| if (!strcmp (prefix + strlen (prefix) - 8, "/include")) |
| prefix[strlen (prefix) - 7] = 0; |
| } |
| |
| if (! (dirtmp = new_include_prefix (NULL_PTR, prefix, argv[++i]))) |
| break; |
| |
| if (after_include == 0) |
| after_include = dirtmp; |
| else |
| last_after_include->next = dirtmp; |
| last_after_include = dirtmp; /* Tail follows the last one */ |
| } |
| /* Add directory to main path for includes, |
| with the default prefix at the front of its name. */ |
| if (!strcmp (argv[i], "-iwithprefixbefore")) { |
| struct file_name_list *dirtmp; |
| char *prefix; |
| |
| if (include_prefix != 0) |
| prefix = include_prefix; |
| else { |
| prefix = savestring (GCC_INCLUDE_DIR); |
| /* Remove the `include' from /usr/local/lib/gcc.../include. */ |
| if (!strcmp (prefix + strlen (prefix) - 8, "/include")) |
| prefix[strlen (prefix) - 7] = 0; |
| } |
| |
| dirtmp = new_include_prefix (NULL_PTR, prefix, argv[++i]); |
| append_include_chain (dirtmp, dirtmp); |
| } |
| /* Add directory to end of path for includes. */ |
| if (!strcmp (argv[i], "-idirafter")) { |
| struct file_name_list *dirtmp; |
| |
| if (! (dirtmp = new_include_prefix (NULL_PTR, "", argv[++i]))) |
| break; |
| |
| if (after_include == 0) |
| after_include = dirtmp; |
| else |
| last_after_include->next = dirtmp; |
| last_after_include = dirtmp; /* Tail follows the last one */ |
| } |
| break; |
| |
| case 'o': |
| if (out_fname != NULL) |
| fatal ("Output filename specified twice"); |
| if (i + 1 == argc) |
| fatal ("Filename missing after -o option"); |
| out_fname = argv[++i]; |
| if (!strcmp (out_fname, "-")) |
| out_fname = ""; |
| break; |
| |
| case 'p': |
| if (!strcmp (argv[i], "-pedantic")) |
| pedantic = 1; |
| else if (!strcmp (argv[i], "-pedantic-errors")) { |
| pedantic = 1; |
| pedantic_errors = 1; |
| } else if (!strcmp (argv[i], "-pcp")) { |
| char *pcp_fname; |
| if (i + 1 == argc) |
| fatal ("Filename missing after -pcp option"); |
| pcp_fname = argv[++i]; |
| pcp_outfile |
| = ((pcp_fname[0] != '-' || pcp_fname[1] != '\0') |
| ? fopen (pcp_fname, "w") |
| : stdout); |
| if (pcp_outfile == 0) |
| pfatal_with_name (pcp_fname); |
| no_precomp = 1; |
| } |
| break; |
| |
| case 't': |
| if (!strcmp (argv[i], "-traditional")) { |
| traditional = 1; |
| cplusplus_comments = 0; |
| } else if (!strcmp (argv[i], "-trigraphs")) { |
| no_trigraphs = 0; |
| } |
| break; |
| |
| case 'l': |
| if (! strcmp (argv[i], "-lang-c")) |
| cplusplus = 0, cplusplus_comments = 1, c89 = 0, objc = 0; |
| if (! strcmp (argv[i], "-lang-c89")) |
| cplusplus = 0, cplusplus_comments = 0, c89 = 1, objc = 0; |
| if (! strcmp (argv[i], "-lang-c++")) |
| cplusplus = 1, cplusplus_comments = 1, c89 = 0, objc = 0; |
| if (! strcmp (argv[i], "-lang-objc")) |
| cplusplus = 0, cplusplus_comments = 1, c89 = 0, objc = 1; |
| if (! strcmp (argv[i], "-lang-objc++")) |
| cplusplus = 1, cplusplus_comments = 1, c89 = 0, objc = 1; |
| if (! strcmp (argv[i], "-lang-asm")) |
| lang_asm = 1; |
| if (! strcmp (argv[i], "-lint")) |
| for_lint = 1; |
| break; |
| |
| case '+': |
| cplusplus = 1, cplusplus_comments = 1; |
| break; |
| |
| case 'w': |
| inhibit_warnings = 1; |
| break; |
| |
| case 'W': |
| if (!strcmp (argv[i], "-Wtrigraphs")) |
| warn_trigraphs = 1; |
| else if (!strcmp (argv[i], "-Wno-trigraphs")) |
| warn_trigraphs = 0; |
| else if (!strcmp (argv[i], "-Wcomment")) |
| warn_comments = 1; |
| else if (!strcmp (argv[i], "-Wno-comment")) |
| warn_comments = 0; |
| else if (!strcmp (argv[i], "-Wcomments")) |
| warn_comments = 1; |
| else if (!strcmp (argv[i], "-Wno-comments")) |
| warn_comments = 0; |
| else if (!strcmp (argv[i], "-Wtraditional")) |
| warn_stringify = 1; |
| else if (!strcmp (argv[i], "-Wno-traditional")) |
| warn_stringify = 0; |
| else if (!strcmp (argv[i], "-Wundef")) |
| warn_undef = 1; |
| else if (!strcmp (argv[i], "-Wno-undef")) |
| warn_undef = 0; |
| else if (!strcmp (argv[i], "-Wimport")) |
| warn_import = 1; |
| else if (!strcmp (argv[i], "-Wno-import")) |
| warn_import = 0; |
| else if (!strcmp (argv[i], "-Werror")) |
| warnings_are_errors = 1; |
| else if (!strcmp (argv[i], "-Wno-error")) |
| warnings_are_errors = 0; |
| else if (!strcmp (argv[i], "-Wall")) |
| { |
| warn_trigraphs = 1; |
| warn_comments = 1; |
| } |
| break; |
| |
| case 'M': |
| /* The style of the choices here is a bit mixed. |
| The chosen scheme is a hybrid of keeping all options in one string |
| and specifying each option in a separate argument: |
| -M|-MM|-MD file|-MMD file [-MG]. An alternative is: |
| -M|-MM|-MD file|-MMD file|-MG|-MMG; or more concisely: |
| -M[M][G][D file]. This is awkward to handle in specs, and is not |
| as extensible. */ |
| /* ??? -MG must be specified in addition to one of -M or -MM. |
| This can be relaxed in the future without breaking anything. |
| The converse isn't true. */ |
| |
| /* -MG isn't valid with -MD or -MMD. This is checked for later. */ |
| if (!strcmp (argv[i], "-MG")) |
| { |
| print_deps_missing_files = 1; |
| break; |
| } |
| if (!strcmp (argv[i], "-M")) |
| print_deps = 2; |
| else if (!strcmp (argv[i], "-MM")) |
| print_deps = 1; |
| else if (!strcmp (argv[i], "-MD")) |
| print_deps = 2; |
| else if (!strcmp (argv[i], "-MMD")) |
| print_deps = 1; |
| /* For -MD and -MMD options, write deps on file named by next arg. */ |
| if (!strcmp (argv[i], "-MD") |
| || !strcmp (argv[i], "-MMD")) { |
| if (i + 1 == argc) |
| fatal ("Filename missing after %s option", argv[i]); |
| i++; |
| deps_file = argv[i]; |
| deps_mode = "w"; |
| } else { |
| /* For -M and -MM, write deps on standard output |
| and suppress the usual output. */ |
| deps_stream = stdout; |
| inhibit_output = 1; |
| } |
| break; |
| |
| case 'd': |
| { |
| char *p = argv[i] + 2; |
| char c; |
| while ((c = *p++)) { |
| /* Arg to -d specifies what parts of macros to dump */ |
| switch (c) { |
| case 'M': |
| dump_macros = dump_only; |
| no_output = 1; |
| break; |
| case 'N': |
| dump_macros = dump_names; |
| break; |
| case 'D': |
| dump_macros = dump_definitions; |
| break; |
| } |
| } |
| } |
| break; |
| |
| case 'g': |
| if (argv[i][2] == '3') |
| debug_output = 1; |
| break; |
| |
| case 'v': |
| fprintf (stderr, "GNU CPP version %s", version_string); |
| #ifdef TARGET_VERSION |
| TARGET_VERSION; |
| #endif |
| fprintf (stderr, "\n"); |
| verbose = 1; |
| break; |
| |
| case 'H': |
| print_include_names = 1; |
| break; |
| |
| case 'D': |
| if (argv[i][2] != 0) |
| pend_defs[i] = argv[i] + 2; |
| else if (i + 1 == argc) |
| fatal ("Macro name missing after -D option"); |
| else |
| i++, pend_defs[i] = argv[i]; |
| break; |
| |
| case 'A': |
| { |
| char *p; |
| |
| if (argv[i][2] != 0) |
| p = argv[i] + 2; |
| else if (i + 1 == argc) |
| fatal ("Assertion missing after -A option"); |
| else |
| p = argv[++i]; |
| |
| if (!strcmp (p, "-")) { |
| /* -A- eliminates all predefined macros and assertions. |
| Let's include also any that were specified earlier |
| on the command line. That way we can get rid of any |
| that were passed automatically in from GCC. */ |
| int j; |
| inhibit_predefs = 1; |
| for (j = 0; j < i; j++) |
| pend_defs[j] = pend_assertions[j] = 0; |
| } else { |
| pend_assertions[i] = p; |
| pend_assertion_options[i] = "-A"; |
| } |
| } |
| break; |
| |
| case 'U': /* JF #undef something */ |
| if (argv[i][2] != 0) |
| pend_undefs[i] = argv[i] + 2; |
| else if (i + 1 == argc) |
| fatal ("Macro name missing after -U option"); |
| else |
| pend_undefs[i] = argv[i+1], i++; |
| break; |
| |
| case 'C': |
| put_out_comments = 1; |
| break; |
| |
| case 'E': /* -E comes from cc -E; ignore it. */ |
| break; |
| |
| case 'P': |
| no_line_directives = 1; |
| break; |
| |
| case '$': /* Don't include $ in identifiers. */ |
| is_idchar['$'] = is_idstart['$'] = 0; |
| break; |
| |
| case 'I': /* Add directory to path for includes. */ |
| { |
| struct file_name_list *dirtmp; |
| |
| if (! ignore_srcdir && !strcmp (argv[i] + 2, "-")) { |
| ignore_srcdir = 1; |
| /* Don't use any preceding -I directories for #include <...>. */ |
| first_bracket_include = 0; |
| } |
| else { |
| dirtmp = new_include_prefix (last_include, "", |
| argv[i][2] ? argv[i] + 2 : argv[++i]); |
| append_include_chain (dirtmp, dirtmp); |
| } |
| } |
| break; |
| |
| case 'n': |
| if (!strcmp (argv[i], "-nostdinc")) |
| /* -nostdinc causes no default include directories. |
| You must specify all include-file directories with -I. */ |
| no_standard_includes = 1; |
| else if (!strcmp (argv[i], "-nostdinc++")) |
| /* -nostdinc++ causes no default C++-specific include directories. */ |
| no_standard_cplusplus_includes = 1; |
| else if (!strcmp (argv[i], "-noprecomp")) |
| no_precomp = 1; |
| break; |
| |
| case 'u': |
| /* Sun compiler passes undocumented switch "-undef". |
| Let's assume it means to inhibit the predefined symbols. */ |
| inhibit_predefs = 1; |
| break; |
| |
| case '\0': /* JF handle '-' as file name meaning stdin or stdout */ |
| if (in_fname == NULL) { |
| in_fname = ""; |
| break; |
| } else if (out_fname == NULL) { |
| out_fname = ""; |
| break; |
| } /* else fall through into error */ |
| |
| default: |
| fatal ("Invalid option `%s'", argv[i]); |
| } |
| } |
| } |
| |
| /* Add dirs from CPATH after dirs from -I. */ |
| /* There seems to be confusion about what CPATH should do, |
| so for the moment it is not documented. */ |
| /* Some people say that CPATH should replace the standard include dirs, |
| but that seems pointless: it comes before them, so it overrides them |
| anyway. */ |
| cp = getenv ("CPATH"); |
| if (cp && ! no_standard_includes) |
| path_include (cp); |
| |
| /* Initialize output buffer */ |
| |
| outbuf.buf = (U_CHAR *) xmalloc (OUTBUF_SIZE); |
| outbuf.bufp = outbuf.buf; |
| outbuf.length = OUTBUF_SIZE; |
| |
| /* Do partial setup of input buffer for the sake of generating |
| early #line directives (when -g is in effect). */ |
| |
| fp = &instack[++indepth]; |
| if (in_fname == NULL) |
| in_fname = ""; |
| fp->nominal_fname = fp->fname = in_fname; |
| fp->lineno = 0; |
| |
| /* In C++, wchar_t is a distinct basic type, and we can expect |
| __wchar_t to be defined by cc1plus. */ |
| if (cplusplus) |
| wchar_type = "__wchar_t"; |
| |
| /* Install __LINE__, etc. Must follow initialize_char_syntax |
| and option processing. */ |
| initialize_builtins (fp, &outbuf); |
| |
| /* Do standard #defines and assertions |
| that identify system and machine type. */ |
| |
| if (!inhibit_predefs) { |
| char *p = (char *) alloca (strlen (predefs) + 1); |
| strcpy (p, predefs); |
| while (*p) { |
| char *q; |
| while (*p == ' ' || *p == '\t') |
| p++; |
| /* Handle -D options. */ |
| if (p[0] == '-' && p[1] == 'D') { |
| q = &p[2]; |
| while (*p && *p != ' ' && *p != '\t') |
| p++; |
| if (*p != 0) |
| *p++= 0; |
| if (debug_output) |
| output_line_directive (fp, &outbuf, 0, same_file); |
| make_definition (q, &outbuf); |
| while (*p == ' ' || *p == '\t') |
| p++; |
| } else if (p[0] == '-' && p[1] == 'A') { |
| /* Handle -A options (assertions). */ |
| char *assertion; |
| char *past_name; |
| char *value; |
| char *past_value; |
| char *termination; |
| int save_char; |
| |
| assertion = &p[2]; |
| past_name = assertion; |
| /* Locate end of name. */ |
| while (*past_name && *past_name != ' ' |
| && *past_name != '\t' && *past_name != '(') |
| past_name++; |
| /* Locate `(' at start of value. */ |
| value = past_name; |
| while (*value && (*value == ' ' || *value == '\t')) |
| value++; |
| if (*value++ != '(') |
| abort (); |
| while (*value && (*value == ' ' || *value == '\t')) |
| value++; |
| past_value = value; |
| /* Locate end of value. */ |
| while (*past_value && *past_value != ' ' |
| && *past_value != '\t' && *past_value != ')') |
| past_value++; |
| termination = past_value; |
| while (*termination && (*termination == ' ' || *termination == '\t')) |
| termination++; |
| if (*termination++ != ')') |
| abort (); |
| if (*termination && *termination != ' ' && *termination != '\t') |
| abort (); |
| /* Temporarily null-terminate the value. */ |
| save_char = *termination; |
| *termination = '\0'; |
| /* Install the assertion. */ |
| make_assertion ("-A", assertion); |
| *termination = (char) save_char; |
| p = termination; |
| while (*p == ' ' || *p == '\t') |
| p++; |
| } else { |
| abort (); |
| } |
| } |
| } |
| |
| /* Now handle the command line options. */ |
| |
| /* Do -U's, -D's and -A's in the order they were seen. */ |
| for (i = 1; i < argc; i++) { |
| if (pend_undefs[i]) { |
| if (debug_output) |
| output_line_directive (fp, &outbuf, 0, same_file); |
| make_undef (pend_undefs[i], &outbuf); |
| } |
| if (pend_defs[i]) { |
| if (debug_output) |
| output_line_directive (fp, &outbuf, 0, same_file); |
| make_definition (pend_defs[i], &outbuf); |
| } |
| if (pend_assertions[i]) |
| make_assertion (pend_assertion_options[i], pend_assertions[i]); |
| } |
| |
| done_initializing = 1; |
| |
| { /* Read the appropriate environment variable and if it exists |
| replace include_defaults with the listed path. */ |
| char *epath = 0; |
| switch ((objc << 1) + cplusplus) |
| { |
| case 0: |
| epath = getenv ("C_INCLUDE_PATH"); |
| break; |
| case 1: |
| epath = getenv ("CPLUS_INCLUDE_PATH"); |
| break; |
| case 2: |
| epath = getenv ("OBJC_INCLUDE_PATH"); |
| break; |
| case 3: |
| epath = getenv ("OBJCPLUS_INCLUDE_PATH"); |
| break; |
| } |
| /* If the environment var for this language is set, |
| add to the default list of include directories. */ |
| if (epath) { |
| int num_dirs; |
| char *startp, *endp; |
| |
| for (num_dirs = 1, startp = epath; *startp; startp++) |
| if (*startp == PATH_SEPARATOR) |
| num_dirs++; |
| include_defaults |
| = (struct default_include *) xmalloc ((num_dirs |
| * sizeof (struct default_include)) |
| + sizeof (include_defaults_array)); |
| startp = endp = epath; |
| num_dirs = 0; |
| while (1) { |
| char c = *endp++; |
| if (c == PATH_SEPARATOR || !c) { |
| endp[-1] = 0; |
| include_defaults[num_dirs].fname |
| = startp == endp ? "." : savestring (startp); |
| endp[-1] = c; |
| include_defaults[num_dirs].cplusplus = cplusplus; |
| include_defaults[num_dirs].cxx_aware = 1; |
| num_dirs++; |
| if (!c) |
| break; |
| startp = endp; |
| } |
| } |
| /* Put the usual defaults back in at the end. */ |
| bcopy ((char *) include_defaults_array, |
| (char *) &include_defaults[num_dirs], |
| sizeof (include_defaults_array)); |
| } |
| } |
| |
| append_include_chain (before_system, last_before_system); |
| first_system_include = before_system; |
| |
| /* Unless -fnostdinc, |
| tack on the standard include file dirs to the specified list */ |
| if (!no_standard_includes) { |
| struct default_include *p = include_defaults; |
| char *specd_prefix = include_prefix; |
| char *default_prefix = savestring (GCC_INCLUDE_DIR); |
| int default_len = 0; |
| /* Remove the `include' from /usr/local/lib/gcc.../include. */ |
| if (!strcmp (default_prefix + strlen (default_prefix) - 8, "/include")) { |
| default_len = strlen (default_prefix) - 7; |
| default_prefix[default_len] = 0; |
| } |
| /* Search "translated" versions of GNU directories. |
| These have /usr/local/lib/gcc... replaced by specd_prefix. */ |
| if (specd_prefix != 0 && default_len != 0) |
| for (p = include_defaults; p->fname; p++) { |
| /* Some standard dirs are only for C++. */ |
| if (!p->cplusplus || (cplusplus && !no_standard_cplusplus_includes)) { |
| /* Does this dir start with the prefix? */ |
| if (!strncmp (p->fname, default_prefix, default_len)) { |
| /* Yes; change prefix and add to search list. */ |
| struct file_name_list *new |
| = new_include_prefix (NULL_PTR, specd_prefix, |
| p->fname + default_len); |
| if (new) { |
| new->c_system_include_path = !p->cxx_aware; |
| append_include_chain (new, new); |
| if (first_system_include == 0) |
| first_system_include = new; |
| } |
| } |
| } |
| } |
| /* Search ordinary names for GNU include directories. */ |
| for (p = include_defaults; p->fname; p++) { |
| /* Some standard dirs are only for C++. */ |
| if (!p->cplusplus || (cplusplus && !no_standard_cplusplus_includes)) { |
| struct file_name_list *new |
| = new_include_prefix (NULL_PTR, "", p->fname); |
| if (new) { |
| new->c_system_include_path = !p->cxx_aware; |
| append_include_chain (new, new); |
| if (first_system_include == 0) |
| first_system_include = new; |
| } |
| } |
| } |
| } |
| |
| /* Tack the after_include chain at the end of the include chain. */ |
| append_include_chain (after_include, last_after_include); |
| if (first_system_include == 0) |
| first_system_include = after_include; |
| |
| /* With -v, print the list of dirs to search. */ |
| if (verbose) { |
| struct file_name_list *p; |
| fprintf (stderr, "#include \"...\" search starts here:\n"); |
| for (p = include; p; p = p->next) { |
| if (p == first_bracket_include) |
| fprintf (stderr, "#include <...> search starts here:\n"); |
| if (!p->fname[0]) |
| fprintf (stderr, " .\n"); |
| else if (!strcmp (p->fname, "/") || !strcmp (p->fname, "//")) |
| fprintf (stderr, " %s\n", p->fname); |
| else |
| /* Omit trailing '/'. */ |
| fprintf (stderr, " %.*s\n", (int) strlen (p->fname) - 1, p->fname); |
| } |
| fprintf (stderr, "End of search list.\n"); |
| } |
| |
| /* -MG doesn't select the form of output and must be specified with one of |
| -M or -MM. -MG doesn't make sense with -MD or -MMD since they don't |
| inhibit compilation. */ |
| if (print_deps_missing_files && (print_deps == 0 || !inhibit_output)) |
| fatal ("-MG must be specified with one of -M or -MM"); |
| |
| /* Either of two environment variables can specify output of deps. |
| Its value is either "OUTPUT_FILE" or "OUTPUT_FILE DEPS_TARGET", |
| where OUTPUT_FILE is the file to write deps info to |
| and DEPS_TARGET is the target to mention in the deps. */ |
| |
| if (print_deps == 0 |
| && (getenv ("SUNPRO_DEPENDENCIES") != 0 |
| || getenv ("DEPENDENCIES_OUTPUT") != 0)) { |
| char *spec = getenv ("DEPENDENCIES_OUTPUT"); |
| char *s; |
| char *output_file; |
| |
| if (spec == 0) { |
| spec = getenv ("SUNPRO_DEPENDENCIES"); |
| print_deps = 2; |
| } |
| else |
| print_deps = 1; |
| |
| s = spec; |
| /* Find the space before the DEPS_TARGET, if there is one. */ |
| /* This should use index. (mrs) */ |
| while (*s != 0 && *s != ' ') s++; |
| if (*s != 0) { |
| deps_target = s + 1; |
| output_file = xmalloc (s - spec + 1); |
| bcopy (spec, output_file, s - spec); |
| output_file[s - spec] = 0; |
| } |
| else { |
| deps_target = 0; |
| output_file = spec; |
| } |
| |
| deps_file = output_file; |
| deps_mode = "a"; |
| } |
| |
| /* For -M, print the expected object file name |
| as the target of this Make-rule. */ |
| if (print_deps) { |
| deps_allocated_size = 200; |
| deps_buffer = xmalloc (deps_allocated_size); |
| deps_buffer[0] = 0; |
| deps_size = 0; |
| deps_column = 0; |
| |
| if (deps_target) { |
| deps_output (deps_target, ':'); |
| } else if (*in_fname == 0) { |
| deps_output ("-", ':'); |
| } else { |
| char *p, *q; |
| int len; |
| |
| q = base_name (in_fname); |
| |
| /* Copy remainder to mungable area. */ |
| p = (char *) alloca (strlen(q) + 8); |
| strcpy (p, q); |
| |
| /* Output P, but remove known suffixes. */ |
| len = strlen (p); |
| q = p + len; |
| if (len >= 2 |
| && p[len - 2] == '.' |
| && index("cCsSm", p[len - 1])) |
| q = p + (len - 2); |
| else if (len >= 3 |
| && p[len - 3] == '.' |
| && p[len - 2] == 'c' |
| && p[len - 1] == 'c') |
| q = p + (len - 3); |
| else if (len >= 4 |
| && p[len - 4] == '.' |
| && p[len - 3] == 'c' |
| && p[len - 2] == 'x' |
| && p[len - 1] == 'x') |
| q = p + (len - 4); |
| else if (len >= 4 |
| && p[len - 4] == '.' |
| && p[len - 3] == 'c' |
| && p[len - 2] == 'p' |
| && p[len - 1] == 'p') |
| q = p + (len - 4); |
| |
| /* Supply our own suffix. */ |
| #ifndef VMS |
| strcpy (q, ".o"); |
| #else |
| strcpy (q, ".obj"); |
| #endif |
| |
| deps_output (p, ':'); |
| deps_output (in_fname, ' '); |
| } |
| } |
| |
| /* Scan the -imacros files before the main input. |
| Much like #including them, but with no_output set |
| so that only their macro definitions matter. */ |
| |
| no_output++; no_record_file++; |
| for (i = 1; i < argc; i++) |
| if (pend_files[i]) { |
| struct include_file *inc; |
| int fd = open_include_file (pend_files[i], NULL_PTR, NULL_PTR, &inc); |
| if (fd < 0) { |
| perror_with_name (pend_files[i]); |
| return FATAL_EXIT_CODE; |
| } |
| finclude (fd, inc, &outbuf, 0, NULL_PTR); |
| } |
| no_output--; no_record_file--; |
| |
| /* Copy the entire contents of the main input file into |
| the stacked input buffer previously allocated for it. */ |
| |
| /* JF check for stdin */ |
| if (in_fname == NULL || *in_fname == 0) { |
| in_fname = ""; |
| f = 0; |
| } else if ((f = open (in_fname, O_RDONLY, 0666)) < 0) |
| goto perror; |
| |
| if (fstat (f, &st) != 0) |
| pfatal_with_name (in_fname); |
| fp->nominal_fname = fp->fname = in_fname; |
| fp->lineno = 1; |
| fp->system_header_p = 0; |
| /* JF all this is mine about reading pipes and ttys */ |
| if (! S_ISREG (st.st_mode)) { |
| /* Read input from a file that is not a normal disk file. |
| We cannot preallocate a buffer with the correct size, |
| so we must read in the file a piece at the time and make it bigger. */ |
| int size; |
| int bsize; |
| int cnt; |
| |
| if (S_ISDIR (st.st_mode)) |
| fatal ("Input file `%s' is a directory", in_fname); |
| |
| bsize = 2000; |
| size = 0; |
| fp->buf = (U_CHAR *) xmalloc (bsize + 2); |
| for (;;) { |
| cnt = safe_read (f, (char *) fp->buf + size, bsize - size); |
| if (cnt < 0) goto perror; /* error! */ |
| size += cnt; |
| if (size != bsize) break; /* End of file */ |
| bsize *= 2; |
| fp->buf = (U_CHAR *) xrealloc (fp->buf, bsize + 2); |
| } |
| fp->length = size; |
| } else { |
| /* Read a file whose size we can determine in advance. |
| For the sake of VMS, st.st_size is just an upper bound. */ |
| fp->buf = (U_CHAR *) xmalloc (st.st_size + 2); |
| fp->length = safe_read (f, (char *) fp->buf, st.st_size); |
| if (fp->length < 0) goto perror; |
| } |
| fp->bufp = fp->buf; |
| fp->if_stack = if_stack; |
| |
| /* Make sure data ends with a newline. And put a null after it. */ |
| |
| if ((fp->length > 0 && fp->buf[fp->length - 1] != '\n') |
| /* Backslash-newline at end is not good enough. */ |
| || (fp->length > 1 && fp->buf[fp->length - 2] == '\\')) { |
| fp->buf[fp->length++] = '\n'; |
| missing_newline = 1; |
| } |
| fp->buf[fp->length] = '\0'; |
| |
| /* Unless inhibited, convert trigraphs in the input. */ |
| |
| if (!no_trigraphs) |
| trigraph_pcp (fp); |
| |
| /* Now that we know the input file is valid, open the output. */ |
| |
| if (!out_fname || !strcmp (out_fname, "")) |
| out_fname = "stdout"; |
| else if (! freopen (out_fname, "w", stdout)) |
| pfatal_with_name (out_fname); |
| |
| output_line_directive (fp, &outbuf, 0, same_file); |
| |
| /* Scan the -include files before the main input. */ |
| |
| no_record_file++; |
| for (i = 1; i < argc; i++) |
| if (pend_includes[i]) { |
| struct include_file *inc; |
| int fd = open_include_file (pend_includes[i], NULL_PTR, NULL_PTR, &inc); |
| if (fd < 0) { |
| perror_with_name (pend_includes[i]); |
| return FATAL_EXIT_CODE; |
| } |
| finclude (fd, inc, &outbuf, 0, NULL_PTR); |
| } |
| no_record_file--; |
| |
| /* Scan the input, processing macros and directives. */ |
| |
| rescan (&outbuf, 0); |
| |
| if (missing_newline) |
| fp->lineno--; |
| |
| if (pedantic && missing_newline) |
| pedwarn ("file does not end in newline"); |
| |
| /* Now we have processed the entire input |
| Write whichever kind of output has been requested. */ |
| |
| if (dump_macros == dump_only) |
| dump_all_macros (); |
| else if (! inhibit_output) { |
| write_output (); |
| } |
| |
| if (print_deps) { |
| /* Don't actually write the deps file if compilation has failed. */ |
| if (errors == 0) { |
| if (deps_file && ! (deps_stream = fopen (deps_file, deps_mode))) |
| pfatal_with_name (deps_file); |
| fputs (deps_buffer, deps_stream); |
| putc ('\n', deps_stream); |
| if (deps_file) { |
| if (ferror (deps_stream) || fclose (deps_stream) != 0) |
| fatal ("I/O error on output"); |
| } |
| } |
| } |
| |
| if (pcp_outfile && pcp_outfile != stdout |
| && (ferror (pcp_outfile) || fclose (pcp_outfile) != 0)) |
| fatal ("I/O error on `-pcp' output"); |
| |
| if (ferror (stdout) || fclose (stdout) != 0) |
| fatal ("I/O error on output"); |
| |
| if (errors) |
| exit (FATAL_EXIT_CODE); |
| exit (SUCCESS_EXIT_CODE); |
| |
| perror: |
| pfatal_with_name (in_fname); |
| return 0; |
| } |
| |
| /* Given a colon-separated list of file names PATH, |
| add all the names to the search path for include files. */ |
| |
| static void |
| path_include (path) |
| char *path; |
| { |
| char *p; |
| |
| p = path; |
| |
| if (*p) |
| while (1) { |
| char *q = p; |
| char c; |
| struct file_name_list *dirtmp; |
| |
| /* Find the end of this name. */ |
| while ((c = *q++) != PATH_SEPARATOR && c) |
| continue; |
| |
| q[-1] = 0; |
| dirtmp = new_include_prefix (last_include, "", p == q ? "." : p); |
| q[-1] = c; |
| append_include_chain (dirtmp, dirtmp); |
| |
| /* Advance past this name. */ |
| p = q; |
| if (! c) |
| break; |
| } |
| } |
| |
| /* Return the address of the first character in S that equals C. |
| S is an array of length N, possibly containing '\0's, and followed by '\0'. |
| Return 0 if there is no such character. Assume that C itself is not '\0'. |
| If we knew we could use memchr, we could just invoke memchr (S, C, N), |
| but unfortunately memchr isn't autoconfigured yet. */ |
| |
| static U_CHAR * |
| index0 (s, c, n) |
| U_CHAR *s; |
| int c; |
| size_t n; |
| { |
| char *p = (char *) s; |
| for (;;) { |
| char *q = index (p, c); |
| if (q) |
| return (U_CHAR *) q; |
| else { |
| size_t l = strlen (p); |
| if (l == n) |
| return 0; |
| l++; |
| p += l; |
| n -= l; |
| } |
| } |
| } |
| |
| /* Pre-C-Preprocessor to translate ANSI trigraph idiocy in BUF |
| before main CCCP processing. Name `pcp' is also in honor of the |
| drugs the trigraph designers must have been on. |
| |
| Using an extra pass through the buffer takes a little extra time, |
| but is infinitely less hairy than trying to handle trigraphs inside |
| strings, etc. everywhere, and also makes sure that trigraphs are |
| only translated in the top level of processing. */ |
| |
| static void |
| trigraph_pcp (buf) |
| FILE_BUF *buf; |
| { |
| register U_CHAR c, *fptr, *bptr, *sptr, *lptr; |
| int len; |
| |
| fptr = bptr = sptr = buf->buf; |
| lptr = fptr + buf->length; |
| while ((sptr = index0 (sptr, '?', (size_t) (lptr - sptr))) != NULL) { |
| if (*++sptr != '?') |
| continue; |
| switch (*++sptr) { |
| case '=': |
| c = '#'; |
| break; |
| case '(': |
| c = '['; |
| break; |
| case '/': |
| c = '\\'; |
| break; |
| case ')': |
| c = ']'; |
| break; |
| case '\'': |
| c = '^'; |
| break; |
| case '<': |
| c = '{'; |
| break; |
| case '!': |
| c = '|'; |
| break; |
| case '>': |
| c = '}'; |
| break; |
| case '-': |
| c = '~'; |
| break; |
| case '?': |
| sptr--; |
| continue; |
| default: |
| continue; |
| } |
| len = sptr - fptr - 2; |
| |
| /* BSD doc says bcopy () works right for overlapping strings. In ANSI |
| C, this will be memmove (). */ |
| if (bptr != fptr && len > 0) |
| bcopy ((char *) fptr, (char *) bptr, len); |
| |
| bptr += len; |
| *bptr++ = c; |
| fptr = ++sptr; |
| } |
| len = buf->length - (fptr - buf->buf); |
| if (bptr != fptr && len > 0) |
| bcopy ((char *) fptr, (char *) bptr, len); |
| buf->length -= fptr - bptr; |
| buf->buf[buf->length] = '\0'; |
| if (warn_trigraphs && fptr != bptr) |
| warning_with_line (0, "%lu trigraph(s) encountered", |
| (unsigned long) (fptr - bptr) / 2); |
| } |
| |
| /* Move all backslash-newline pairs out of embarrassing places. |
| Exchange all such pairs following BP |
| with any potentially-embarrassing characters that follow them. |
| Potentially-embarrassing characters are / and * |
| (because a backslash-newline inside a comment delimiter |
| would cause it not to be recognized). */ |
| |
| static void |
| newline_fix (bp) |
| U_CHAR *bp; |
| { |
| register U_CHAR *p = bp; |
| |
| /* First count the backslash-newline pairs here. */ |
| |
| while (p[0] == '\\' && p[1] == '\n') |
| p += 2; |
| |
| /* What follows the backslash-newlines is not embarrassing. */ |
| |
| if (*p != '/' && *p != '*') |
| return; |
| |
| /* Copy all potentially embarrassing characters |
| that follow the backslash-newline pairs |
| down to where the pairs originally started. */ |
| |
| while (*p == '*' || *p == '/') |
| *bp++ = *p++; |
| |
| /* Now write the same number of pairs after the embarrassing chars. */ |
| while (bp < p) { |
| *bp++ = '\\'; |
| *bp++ = '\n'; |
| } |
| } |
| |
| /* Like newline_fix but for use within a directive-name. |
| Move any backslash-newlines up past any following symbol constituents. */ |
| |
| static void |
| name_newline_fix (bp) |
| U_CHAR *bp; |
| { |
| register U_CHAR *p = bp; |
| |
| /* First count the backslash-newline pairs here. */ |
| while (p[0] == '\\' && p[1] == '\n') |
| p += 2; |
| |
| /* What follows the backslash-newlines is not embarrassing. */ |
| |
| if (!is_idchar[*p]) |
| return; |
| |
| /* Copy all potentially embarrassing characters |
| that follow the backslash-newline pairs |
| down to where the pairs originally started. */ |
| |
| while (is_idchar[*p]) |
| *bp++ = *p++; |
| |
| /* Now write the same number of pairs after the embarrassing chars. */ |
| while (bp < p) { |
| *bp++ = '\\'; |
| *bp++ = '\n'; |
| } |
| } |
| |
| /* Look for lint commands in comments. |
| |
| When we come in here, ibp points into a comment. Limit is as one expects. |
| scan within the comment -- it should start, after lwsp, with a lint command. |
| If so that command is returned as a (constant) string. |
| |
| Upon return, any arg will be pointed to with argstart and will be |
| arglen long. Note that we don't parse that arg since it will just |
| be printed out again. */ |
| |
| static char * |
| get_lintcmd (ibp, limit, argstart, arglen, cmdlen) |
| register U_CHAR *ibp; |
| register U_CHAR *limit; |
| U_CHAR **argstart; /* point to command arg */ |
| int *arglen, *cmdlen; /* how long they are */ |
| { |
| HOST_WIDE_INT linsize; |
| register U_CHAR *numptr; /* temp for arg parsing */ |
| |
| *arglen = 0; |
| |
| SKIP_WHITE_SPACE (ibp); |
| |
| if (ibp >= limit) return NULL; |
| |
| linsize = limit - ibp; |
| |
| /* Oh, I wish C had lexical functions... hell, I'll just open-code the set */ |
| if ((linsize >= 10) && !bcmp (ibp, "NOTREACHED", 10)) { |
| *cmdlen = 10; |
| return "NOTREACHED"; |
| } |
| if ((linsize >= 8) && !bcmp (ibp, "ARGSUSED", 8)) { |
| *cmdlen = 8; |
| return "ARGSUSED"; |
| } |
| if ((linsize >= 11) && !bcmp (ibp, "LINTLIBRARY", 11)) { |
| *cmdlen = 11; |
| return "LINTLIBRARY"; |
| } |
| if ((linsize >= 7) && !bcmp (ibp, "VARARGS", 7)) { |
| *cmdlen = 7; |
| ibp += 7; linsize -= 7; |
| if ((linsize == 0) || ! isdigit (*ibp)) return "VARARGS"; |
| |
| /* OK, read a number */ |
| for (numptr = *argstart = ibp; (numptr < limit) && isdigit (*numptr); |
| numptr++); |
| *arglen = numptr - *argstart; |
| return "VARARGS"; |
| } |
| return NULL; |
| } |
| |
| /* |
| * The main loop of the program. |
| * |
| * Read characters from the input stack, transferring them to the |
| * output buffer OP. |
| * |
| * Macros are expanded and push levels on the input stack. |
| * At the end of such a level it is popped off and we keep reading. |
| * At the end of any other kind of level, we return. |
| * #-directives are handled, except within macros. |
| * |
| * If OUTPUT_MARKS is nonzero, keep Newline markers found in the input |
| * and insert them when appropriate. This is set while scanning macro |
| * arguments before substitution. It is zero when scanning for final output. |
| * There are three types of Newline markers: |
| * * Newline - follows a macro name that was not expanded |
| * because it appeared inside an expansion of the same macro. |
| * This marker prevents future expansion of that identifier. |
| * When the input is rescanned into the final output, these are deleted. |
| * These are also deleted by ## concatenation. |
| * * Newline Space (or Newline and any other whitespace character) |
| * stands for a place that tokens must be separated or whitespace |
| * is otherwise desirable, but where the ANSI standard specifies there |
| * is no whitespace. This marker turns into a Space (or whichever other |
| * whitespace char appears in the marker) in the final output, |
| * but it turns into nothing in an argument that is stringified with #. |
| * Such stringified arguments are the only place where the ANSI standard |
| * specifies with precision that whitespace may not appear. |
| * |
| * During this function, IP->bufp is kept cached in IBP for speed of access. |
| * Likewise, OP->bufp is kept in OBP. Before calling a subroutine |
| * IBP, IP and OBP must be copied back to memory. IP and IBP are |
| * copied back with the RECACHE macro. OBP must be copied back from OP->bufp |
| * explicitly, and before RECACHE, since RECACHE uses OBP. |
| */ |
| |
| static void |
| rescan (op, output_marks) |
| FILE_BUF *op; |
| int output_marks; |
| { |
| /* Character being scanned in main loop. */ |
| register U_CHAR c; |
| |
| /* Length of pending accumulated identifier. */ |
| register int ident_length = 0; |
| |
| /* Hash code of pending accumulated identifier. */ |
| register int hash = 0; |
| |
| /* Current input level (&instack[indepth]). */ |
| FILE_BUF *ip; |
| |
| /* Pointer for scanning input. */ |
| register U_CHAR *ibp; |
| |
| /* Pointer to end of input. End of scan is controlled by LIMIT. */ |
| register U_CHAR *limit; |
| |
| /* Pointer for storing output. */ |
| register U_CHAR *obp; |
| |
| /* REDO_CHAR is nonzero if we are processing an identifier |
| after backing up over the terminating character. |
| Sometimes we process an identifier without backing up over |
| the terminating character, if the terminating character |
| is not special. Backing up is done so that the terminating character |
| will be dispatched on again once the identifier is dealt with. */ |
| int redo_char = 0; |
| |
| /* 1 if within an identifier inside of which a concatenation |
| marker (Newline -) has been seen. */ |
| int concatenated = 0; |
| |
| /* While scanning a comment or a string constant, |
| this records the line it started on, for error messages. */ |
| int start_line; |
| |
| /* Record position of last `real' newline. */ |
| U_CHAR *beg_of_line; |
| |
| /* Pop the innermost input stack level, assuming it is a macro expansion. */ |
| |
| #define POPMACRO \ |
| do { ip->macro->type = T_MACRO; \ |
| if (ip->free_ptr) free (ip->free_ptr); \ |
| --indepth; } while (0) |
| |
| /* Reload `rescan's local variables that describe the current |
| level of the input stack. */ |
| |
| #define RECACHE \ |
| do { ip = &instack[indepth]; \ |
| ibp = ip->bufp; \ |
| limit = ip->buf + ip->length; \ |
| op->bufp = obp; \ |
| check_expand (op, limit - ibp); \ |
| beg_of_line = 0; \ |
| obp = op->bufp; } while (0) |
| |
| if (no_output && instack[indepth].fname != 0) |
| skip_if_group (&instack[indepth], 1, NULL); |
| |
| obp = op->bufp; |
| RECACHE; |
| |
| beg_of_line = ibp; |
| |
| /* Our caller must always put a null after the end of |
| the input at each input stack level. */ |
| if (*limit != 0) |
| abort (); |
| |
| while (1) { |
| c = *ibp++; |
| *obp++ = c; |
| |
| switch (c) { |
| case '\\': |
| if (*ibp == '\n' && !ip->macro) { |
| /* At the top level, always merge lines ending with backslash-newline, |
| even in middle of identifier. But do not merge lines in a macro, |
| since backslash might be followed by a newline-space marker. */ |
| ++ibp; |
| ++ip->lineno; |
| --obp; /* remove backslash from obuf */ |
| break; |
| } |
| /* If ANSI, backslash is just another character outside a string. */ |
| if (!traditional) |
| goto randomchar; |
| /* Otherwise, backslash suppresses specialness of following char, |
| so copy it here to prevent the switch from seeing it. |
| But first get any pending identifier processed. */ |
| if (ident_length > 0) |
| goto specialchar; |
| if (ibp < limit) |
| *obp++ = *ibp++; |
| break; |
| |
| case '%': |
| if (ident_length || ip->macro || traditional) |
| goto randomchar; |
| while (*ibp == '\\' && ibp[1] == '\n') { |
| ibp += 2; |
| ++ip->lineno; |
| } |
| if (*ibp != ':') |
| break; |
| /* Treat this %: digraph as if it were #. */ |
| /* Fall through. */ |
| |
| case '#': |
| if (assertions_flag) { |
| if (ident_length) |
| goto specialchar; |
| /* Copy #foo (bar lose) without macro expansion. */ |
| obp[-1] = '#'; /* In case it was '%'. */ |
| SKIP_WHITE_SPACE (ibp); |
| while (is_idchar[*ibp]) |
| *obp++ = *ibp++; |
| SKIP_WHITE_SPACE (ibp); |
| if (*ibp == '(') { |
| ip->bufp = ibp; |
| skip_paren_group (ip); |
| bcopy ((char *) ibp, (char *) obp, ip->bufp - ibp); |
| obp += ip->bufp - ibp; |
| ibp = ip->bufp; |
| } |
| break; |
| } |
| |
| /* If this is expanding a macro definition, don't recognize |
| preprocessing directives. */ |
| if (ip->macro != 0) |
| goto randomchar; |
| /* If this is expand_into_temp_buffer, |
| don't recognize them either. Warn about them |
| only after an actual newline at this level, |
| not at the beginning of the input level. */ |
| if (! ip->fname) { |
| if (ip->buf != beg_of_line) |
| warning ("preprocessing directive not recognized within macro arg"); |
| goto randomchar; |
| } |
| if (ident_length) |
| goto specialchar; |
| |
| |
| /* # keyword: a # must be first nonblank char on the line */ |
| if (beg_of_line == 0) |
| goto randomchar; |
| { |
| U_CHAR *bp; |
| |
| /* Scan from start of line, skipping whitespace, comments |
| and backslash-newlines, and see if we reach this #. |
| If not, this # is not special. */ |
| bp = beg_of_line; |
| /* If -traditional, require # to be at beginning of line. */ |
| if (!traditional) { |
| while (1) { |
| if (is_hor_space[*bp]) |
| bp++; |
| else if (*bp == '\\' && bp[1] == '\n') |
| bp += 2; |
| else if (*bp == '/' && bp[1] == '*') { |
| bp += 2; |
| while (!(*bp == '*' && bp[1] == '/')) |
| bp++; |
| bp += 2; |
| } |
| /* There is no point in trying to deal with C++ // comments here, |
| because if there is one, then this # must be part of the |
| comment and we would never reach here. */ |
| else break; |
| } |
| if (c == '%') { |
| if (bp[0] != '%') |
| break; |
| while (bp[1] == '\\' && bp[2] == '\n') |
| bp += 2; |
| if (bp + 1 != ibp) |
| break; |
| /* %: appears at start of line; skip past the ':' too. */ |
| bp++; |
| ibp++; |
| } |
| } |
| if (bp + 1 != ibp) |
| goto randomchar; |
| } |
| |
| /* This # can start a directive. */ |
| |
| --obp; /* Don't copy the '#' */ |
| |
| ip->bufp = ibp; |
| op->bufp = obp; |
| if (! handle_directive (ip, op)) { |
| #ifdef USE_C_ALLOCA |
| alloca (0); |
| #endif |
| /* Not a known directive: treat it as ordinary text. |
| IP, OP, IBP, etc. have not been changed. */ |
| if (no_output && instack[indepth].fname) { |
| /* If not generating expanded output, |
| what we do with ordinary text is skip it. |
| Discard everything until next # directive. */ |
| skip_if_group (&instack[indepth], 1, 0); |
| RECACHE; |
| beg_of_line = ibp; |
| break; |
| } |
| *obp++ = '#'; /* Copy # (even if it was originally %:). */ |
| /* Don't expand an identifier that could be a macro directive. |
| (Section 3.8.3 of the ANSI C standard) */ |
| SKIP_WHITE_SPACE (ibp); |
| if (is_idstart[*ibp]) |
| { |
| *obp++ = *ibp++; |
| while (is_idchar[*ibp]) |
| *obp++ = *ibp++; |
| } |
| goto randomchar; |
| } |
| #ifdef USE_C_ALLOCA |
| alloca (0); |
| #endif |
| /* A # directive has been successfully processed. */ |
| /* If not generating expanded output, ignore everything until |
| next # directive. */ |
| if (no_output && instack[indepth].fname) |
| skip_if_group (&instack[indepth], 1, 0); |
| obp = op->bufp; |
| RECACHE; |
| beg_of_line = ibp; |
| break; |
| |
| case '\"': /* skip quoted string */ |
| case '\'': |
| /* A single quoted string is treated like a double -- some |
| programs (e.g., troff) are perverse this way */ |
| |
| /* Handle any pending identifier; |
| but the L in L'...' or L"..." is not an identifier. */ |
| if (ident_length |
| && ! (ident_length == 1 && hash == HASHSTEP (0, 'L'))) |
| goto specialchar; |
| |
| start_line = ip->lineno; |
| |
| /* Skip ahead to a matching quote. */ |
| |
| while (1) { |
| if (ibp >= limit) { |
| if (ip->macro != 0) { |
| /* try harder: this string crosses a macro expansion boundary. |
| This can happen naturally if -traditional. |
| Otherwise, only -D can make a macro with an unmatched quote. */ |
| POPMACRO; |
| RECACHE; |
| continue; |
| } |
| if (!traditional) { |
| error_with_line (line_for_error (start_line), |
| "unterminated string or character constant"); |
| error_with_line (multiline_string_line, |
| "possible real start of unterminated constant"); |
| multiline_string_line = 0; |
| } |
| break; |
| } |
| *obp++ = *ibp; |
| switch (*ibp++) { |
| case '\n': |
| ++ip->lineno; |
| ++op->lineno; |
| /* Traditionally, end of line ends a string constant with no error. |
| So exit the loop and record the new line. */ |
| if (traditional) { |
| beg_of_line = ibp; |
| goto while2end; |
| } |
| if (c == '\'') { |
| error_with_line (line_for_error (start_line), |
| "unterminated character constant"); |
| goto while2end; |
| } |
| if (multiline_string_line == 0) { |
| if (pedantic) |
| pedwarn_with_line (line_for_error (start_line), |
| "string constant runs past end of line"); |
| multiline_string_line = ip->lineno - 1; |
| } |
| break; |
| |
| case '\\': |
| if (ibp >= limit) |
| break; |
| if (*ibp == '\n') { |
| /* Backslash newline is replaced by nothing at all, |
| but keep the line counts correct. */ |
| --obp; |
| ++ibp; |
| ++ip->lineno; |
| } else { |
| /* ANSI stupidly requires that in \\ the second \ |
| is *not* prevented from combining with a newline. */ |
| while (*ibp == '\\' && ibp[1] == '\n') { |
| ibp += 2; |
| ++ip->lineno; |
| } |
| *obp++ = *ibp++; |
| } |
| break; |
| |
| case '\"': |
| case '\'': |
| if (ibp[-1] == c) |
| goto while2end; |
| break; |
| } |
| } |
| while2end: |
| break; |
| |
| case '/': |
| if (*ibp == '\\' && ibp[1] == '\n') |
| newline_fix (ibp); |
| |
| if (*ibp != '*' |
| && !(cplusplus_comments && *ibp == '/')) |
| goto randomchar; |
| if (ip->macro != 0) |
| goto randomchar; |
| if (ident_length) |
| goto specialchar; |
| |
| if (*ibp == '/') { |
| /* C++ style comment... */ |
| start_line = ip->lineno; |
| |
| /* Comments are equivalent to spaces. */ |
| if (! put_out_comments) |
| obp[-1] = ' '; |
| |
| { |
| U_CHAR *before_bp = ibp; |
| |
| while (++ibp < limit) { |
| if (*ibp == '\n') { |
| if (ibp[-1] != '\\') { |
| if (put_out_comments) { |
| bcopy ((char *) before_bp, (char *) obp, ibp - before_bp); |
| obp += ibp - before_bp; |
| } |
| break; |
| } |
| if (warn_comments) |
| warning ("multiline `//' comment"); |
| ++ip->lineno; |
| /* Copy the newline into the output buffer, in order to |
| avoid the pain of a #line every time a multiline comment |
| is seen. */ |
| if (!put_out_comments) |
| *obp++ = '\n'; |
| ++op->lineno; |
| } |
| } |
| break; |
| } |
| } |
| |
| /* Ordinary C comment. Skip it, optionally copying it to output. */ |
| |
| start_line = ip->lineno; |
| |
| ++ibp; /* Skip the star. */ |
| |
| /* If this cpp is for lint, we peek inside the comments: */ |
| if (for_lint) { |
| U_CHAR *argbp; |
| int cmdlen, arglen; |
| char *lintcmd = get_lintcmd (ibp, limit, &argbp, &arglen, &cmdlen); |
| |
| if (lintcmd != NULL) { |
| op->bufp = obp; |
| check_expand (op, cmdlen + arglen + 14); |
| obp = op->bufp; |
| /* I believe it is always safe to emit this newline: */ |
| obp[-1] = '\n'; |
| bcopy ("#pragma lint ", (char *) obp, 13); |
| obp += 13; |
| bcopy (lintcmd, (char *) obp, cmdlen); |
| obp += cmdlen; |
| |
| if (arglen != 0) { |
| *(obp++) = ' '; |
| bcopy (argbp, (char *) obp, arglen); |
| obp += arglen; |
| } |
| |
| /* OK, now bring us back to the state we were in before we entered |
| this branch. We need #line because the #pragma's newline always |
| messes up the line count. */ |
| op->bufp = obp; |
| output_line_directive (ip, op, 0, same_file); |
| check_expand (op, limit - ibp + 2); |
| obp = op->bufp; |
| *(obp++) = '/'; |
| } |
| } |
| |
| /* Comments are equivalent to spaces. |
| Note that we already output the slash; we might not want it. |
| For -traditional, a comment is equivalent to nothing. */ |
| if (! put_out_comments) { |
| if (traditional) |
| obp--; |
| else |
| obp[-1] = ' '; |
| } |
| else |
| *obp++ = '*'; |
| |
| { |
| U_CHAR *before_bp = ibp; |
| |
| for (;;) { |
| switch (*ibp++) { |
| case '*': |
| if (ibp[-2] == '/' && warn_comments) |
| warning ("`/*' within comment"); |
| if (*ibp == '\\' && ibp[1] == '\n') |
| newline_fix (ibp); |
| if (*ibp == '/') |
| goto comment_end; |
| break; |
| |
| case '\n': |
| ++ip->lineno; |
| /* Copy the newline into the output buffer, in order to |
| avoid the pain of a #line every time a multiline comment |
| is seen. */ |
| if (!put_out_comments) |
| *obp++ = '\n'; |
| ++op->lineno; |
| break; |
| |
| case 0: |
| if (limit < ibp) { |
| error_with_line (line_for_error (start_line), |
| "unterminated comment"); |
| goto limit_reached; |
| } |
| break; |
| } |
| } |
| comment_end: |
| |
| ibp++; |
| if (put_out_comments) { |
| bcopy ((char *) before_bp, (char *) obp, ibp - before_bp); |
| obp += ibp - before_bp; |
| } |
| } |
| break; |
| |
| case '$': |
| if (! is_idchar['$']) |
| goto randomchar; |
| if (pedantic) |
| pedwarn ("`$' in identifier"); |
| goto letter; |
| |
| case '0': case '1': case '2': case '3': case '4': |
| case '5': case '6': case '7': case '8': case '9': |
| /* If digit is not part of identifier, it starts a number, |
| which means that following letters are not an identifier. |
| "0x5" does not refer to an identifier "x5". |
| So copy all alphanumerics that follow without accumulating |
| as an identifier. Periods also, for sake of "3.e7". */ |
| |
| if (ident_length == 0) { |
| for (;;) { |
| while (ibp[0] == '\\' && ibp[1] == '\n') { |
| ++ip->lineno; |
| ibp += 2; |
| } |
| c = *ibp++; |
| if (!is_idchar[c] && c != '.') { |
| --ibp; |
| break; |
| } |
| *obp++ = c; |
| /* A sign can be part of a preprocessing number |
| if it follows an `e' or `p'. */ |
| if (c == 'e' || c == 'E' || c == 'p' || c == 'P') { |
| while (ibp[0] == '\\' && ibp[1] == '\n') { |
| ++ip->lineno; |
| ibp += 2; |
| } |
| if (*ibp == '+' || *ibp == '-') { |
| |