#include "config.h"
#include "system.h"
#include "version.h"
#include "cppdefault.h"
#include "tradcpp.h"
#include "mkdeps.h"
#include "intl.h"
typedef unsigned char U_CHAR;
static const char *progname;
size_t max_include_len;
int put_out_comments = 0;
struct deps *deps;
int print_deps = 0;
int print_deps_phony_targets = 0;
int deps_append = 0;
const char *deps_file = 0;
int deps_missing_files = 0;
int no_line_commands;
int dump_macros;
int inhibit_warnings = 0;
int inhibit_output = 0;
#if DEFAULT_SIGNED_CHAR
int flag_signed_char = 1;
#else
int flag_signed_char = 0;
#endif
int warn_comments;
int no_output;
static const char *user_label_prefix;
#define INPUT_STACK_MAX 200
struct file_name_list;
struct file_buf {
const char *fname;
int lineno;
int length;
U_CHAR *buf;
U_CHAR *bufp;
struct hashnode *macro;
struct if_stack *if_stack;
U_CHAR *free_ptr;
struct file_name_list *next_header_dir;
} instack[INPUT_STACK_MAX];
typedef struct file_buf FILE_BUF;
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; \
}
int system_include_depth = 0;
#define OUTBUF_SIZE 10
FILE_BUF outbuf;
#define check_expand(OBUF, NEEDED) do { \
if ((OBUF)->length - ((OBUF)->bufp - (OBUF)->buf) <= (NEEDED)) \
grow_outbuf ((OBUF), (NEEDED)); \
} while (0)
struct file_name_list
{
struct file_name_list *next;
const char *fname;
};
struct file_name_list *include = 0;
struct file_name_list *first_bracket_include = 0;
struct file_name_list *last_include = 0;
struct file_name_list *dont_repeat_files = 0;
struct file_name_list *all_include_files = 0;
typedef struct definition DEFINITION;
struct definition {
int nargs;
int length;
U_CHAR *expansion;
struct reflist {
struct reflist *next;
char stringify;
char raw_before;
char raw_after;
int nchars;
int argno;
} *pattern;
const U_CHAR *argnames;
};
struct answer
{
struct answer *next;
const unsigned char *answer;
size_t len;
};
union hashval {
const char *cpval;
DEFINITION *defn;
struct answer *answers;
};
enum node_type {
T_DEFINE = 1,
T_INCLUDE,
T_INCLUDE_NEXT,
T_IFDEF,
T_IFNDEF,
T_IF,
T_ELSE,
T_ELIF,
T_UNDEF,
T_LINE,
T_ENDIF,
T_ERROR,
T_WARNING,
T_ASSERT,
T_UNASSERT,
T_SPECLINE,
T_DATE,
T_FILE,
T_BASE_FILE,
T_INCLUDE_LEVEL,
T_VERSION,
T_TIME,
T_CONST,
T_MACRO,
T_SPEC_DEFINED,
T_UNUSED
};
struct hashnode {
struct hashnode *next;
struct hashnode *prev;
struct hashnode **bucket_hdr;
enum node_type type;
int length;
U_CHAR *name;
union hashval value;
};
typedef struct hashnode HASHNODE;
static HASHNODE *parse_assertion PARAMS ((const unsigned char *,
const unsigned char *,
struct answer **, int));
static struct answer **find_answer PARAMS ((HASHNODE *,
const struct answer *));
static int parse_answer PARAMS ((const unsigned char *, const unsigned char *,
struct answer **, int));
static unsigned char *canonicalize_text PARAMS ((const unsigned char *,
const unsigned char *,
const unsigned char **));
#define HASHSIZE 1403
HASHNODE *hashtab[HASHSIZE];
#define HASHSTEP(old, c) ((old << 2) + c)
#define MAKE_POS(v) (v & 0x7fffffff)
struct directive {
const int length;
void (*const func) PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
const char *const name;
const enum node_type type;
};
enum file_change_code {same_file, enter_file, leave_file};
struct argdata {
U_CHAR *raw, *expanded;
int raw_length, expand_length;
int stringified_length;
U_CHAR *free1, *free2;
char newlines;
char comments;
};
struct arglist {
struct arglist *next;
U_CHAR *name;
int length;
int argno;
};
static void do_define PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_error PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_warning PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_line PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_include PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_include_next PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_undef PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_if PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_ifdef PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_ifndef PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_else PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_elif PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_endif PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_assert PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_unassert PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
static void do_xifdef PARAMS ((U_CHAR *, U_CHAR *, enum node_type));
static struct hashnode *install PARAMS ((const U_CHAR *, int, enum node_type, int));
static int hashf PARAMS ((const U_CHAR *, int, int));
static int compare_defs PARAMS ((DEFINITION *, DEFINITION *));
static int comp_def_part PARAMS ((int, const U_CHAR *, int,
const U_CHAR *, int, int));
static void delete_macro PARAMS ((HASHNODE *));
enum msgtype { MT_WARNING = 0, MT_ERROR, MT_FATAL };
static void v_message PARAMS ((enum msgtype mtype, int line,
const char *msgid, va_list ap))
ATTRIBUTE_PRINTF (3, 0);
static int line_for_error PARAMS ((int));
#undef abort
#if (GCC_VERSION >= 2007)
#define abort() fancy_abort(__LINE__, __FUNCTION__)
#else
#define abort() fancy_abort(__LINE__, 0);
#endif
static void macroexpand PARAMS ((HASHNODE *, FILE_BUF *));
static void special_symbol PARAMS ((HASHNODE *, FILE_BUF *));
static void dump_all_macros PARAMS ((void));
static void dump_defn_1 PARAMS ((const U_CHAR *, int, int));
static void dump_arg_n PARAMS ((DEFINITION *, int));
static void conditional_skip PARAMS ((FILE_BUF *, int, enum node_type));
static void skip_if_group PARAMS ((FILE_BUF *, int));
static void output_line_command PARAMS ((FILE_BUF *, FILE_BUF *,
int, enum file_change_code));
static int eval_if_expression PARAMS ((const U_CHAR *, int));
static void output_deps PARAMS ((void));
static void initialize_builtins PARAMS ((void));
static void run_directive PARAMS ((const char *, size_t,
enum node_type));
static void make_definition PARAMS ((const char *));
static void make_undef PARAMS ((const char *));
static void make_assertion PARAMS ((const char *));
static void grow_outbuf PARAMS ((FILE_BUF *, int));
static int handle_directive PARAMS ((FILE_BUF *, FILE_BUF *));
static void process_include PARAMS ((struct file_name_list *,
const U_CHAR *, int, int, FILE_BUF *));
static void finclude PARAMS ((int, const char *,
struct file_name_list *, FILE_BUF *));
static void init_dependency_output PARAMS ((void));
static void rescan PARAMS ((FILE_BUF *, int));
static void newline_fix PARAMS ((U_CHAR *));
static void name_newline_fix PARAMS ((U_CHAR *));
static U_CHAR *macarg1 PARAMS ((U_CHAR *, const U_CHAR *, int *,
int *, int *));
static const char *macarg PARAMS ((struct argdata *));
static int discard_comments PARAMS ((U_CHAR *, int, int));
static int file_size_and_mode PARAMS ((int, int *, long *));
static U_CHAR *skip_to_end_of_comment PARAMS ((FILE_BUF *, int *));
static U_CHAR *skip_quoted_string PARAMS ((const U_CHAR *, const U_CHAR *,
int, int *, int *, int *));
int main PARAMS ((int, char **));
#define U (const unsigned char *)
static const struct directive directive_table[] = {
{ 6, do_define, "define", T_DEFINE },
{ 7, do_include, "include", T_INCLUDE },
{ 5, do_endif, "endif", T_ENDIF },
{ 5, do_ifdef, "ifdef", T_IFDEF },
{ 2, do_if, "if", T_IF, },
{ 4, do_else, "else", T_ELSE },
{ 6, do_ifndef, "ifndef", T_IFNDEF },
{ 5, do_undef, "undef", T_UNDEF },
{ 4, do_line, "line", T_LINE },
{ 4, do_elif, "elif", T_ELIF },
{ 5, do_error, "error", T_ERROR },
{ 7, do_warning, "warning", T_WARNING },
{ 12, do_include_next, "include_next", T_INCLUDE_NEXT },
{ 6, do_assert, "assert", T_ASSERT },
{ 8, do_unassert,"unassert",T_UNASSERT},
{ -1, 0, "", T_UNUSED},
};
#define SKIP_WHITE_SPACE(p) do { while (is_nvspace(*p)) p++; } while (0)
#define SKIP_ALL_WHITE_SPACE(p) do { while (is_space(*p)) p++; } while (0)
int errors = 0;
static FILE_BUF expand_to_temp_buffer PARAMS ((const U_CHAR *, const U_CHAR *, int));
static DEFINITION *collect_expansion PARAMS ((U_CHAR *, U_CHAR *, int,
struct arglist *));
struct if_stack {
struct if_stack *next;
const char *fname;
int lineno;
int if_succeeded;
enum node_type type;
};
typedef struct if_stack IF_STACK_FRAME;
IF_STACK_FRAME *if_stack = NULL;
int ignore_srcdir;
enum pending_dir_t {PD_NONE = 0, PD_DEFINE, PD_UNDEF, PD_ASSERTION, PD_FILE};
typedef struct pending_dir pending_dir;
struct pending_dir
{
const char *arg;
enum pending_dir_t type;
};
int
main (argc, argv)
int argc;
char **argv;
{
int st_mode;
long st_size;
const char *in_fname, *out_fname;
int f, i;
FILE_BUF *fp;
pending_dir *pend = (pending_dir *) xcalloc (argc, sizeof (pending_dir));
int no_standard_includes = 0;
hex_init ();
#ifdef RLIMIT_STACK
{
struct rlimit rlim;
getrlimit (RLIMIT_STACK, &rlim);
rlim.rlim_cur = rlim.rlim_max;
setrlimit (RLIMIT_STACK, &rlim);
}
#endif
progname = argv[0];
in_fname = NULL;
out_fname = NULL;
no_line_commands = 0;
dump_macros = 0;
no_output = 0;
max_include_len = cpp_GCC_INCLUDE_DIR_len + 7;
gcc_init_libintl ();
deps = deps_init ();
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 {
int c = argv[i][1];
switch (c) {
case 'E':
case '$':
break;
case 'l':
if (!strcmp (argv[i], "-lang-c++")
|| !strcmp (argv[i], "-lang-objc++"))
fatal ("-traditional is not supported in C++");
else if (!strcmp (argv[i], "-lang-c89"))
fatal ("-traditional and -ansi are mutually exclusive");
else if (!strcmp (argv[i], "-lang-objc"))
pend[i].type = PD_DEFINE, pend[i].arg = "__OBJC__";
else if (!strcmp (argv[i], "-lang-asm"))
pend[i].type = PD_DEFINE, pend[i].arg = "__ASSEMBLER__";
else if (!strcmp (argv[i], "-lang-fortran"))
pend[i].type = PD_DEFINE, pend[i].arg = "_LANGUAGE_FORTRAN";
break;
case 'i':
if (!strcmp (argv[i], "-include"))
{
if (i + 1 == argc)
fatal ("filename missing after -i option");
else
pend[i].type = PD_FILE, pend[i].arg = argv[i + 1], i++;
}
else if (!strcmp (argv[i], "-iprefix"))
i++;
else if (!strcmp (argv[i], "-isystem")
|| !strcmp (argv[i], "-iwithprefix")
|| !strcmp (argv[i], "-iwithprefixbefore")
|| !strcmp (argv[i], "-idirafter"))
goto add_include;
#ifdef FRAMEWORK_HEADERS
else if (!strcmp (argv[i], "-iframework"))
i++;
#endif
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 'w':
inhibit_warnings = 1;
break;
case 'W':
if (!strcmp (argv[i], "-Wcomments"))
warn_comments = 1;
else if (!strcmp (argv[i], "-Wcomment"))
warn_comments = 1;
else if (!strcmp (argv[i], "-Wall")) {
warn_comments = 1;
}
break;
case 'f':
if (!strcmp (argv[i], "-fleading-underscore"))
user_label_prefix = "_";
else if (!strcmp (argv[i], "-fno-leading-underscore"))
user_label_prefix = "";
else if (!strcmp (argv[i], "-fsigned-char"))
flag_signed_char = 1;
else if (!strcmp (argv[i], "-funsigned-char"))
flag_signed_char = 0;
break;
case 'M':
{
char *p = NULL;
if (!strncmp (argv[i], "-MD", 3)) {
p = argv[i] + 3;
print_deps = 2;
} else if (!strncmp (argv[i], "-MMD", 4)) {
p = argv[i] + 4;
print_deps = 1;
} else if (!strcmp (argv[i], "-M")) {
print_deps = 2;
} else if (!strcmp (argv[i], "-MM")) {
print_deps = 1;
} else if (!strcmp (argv[i], "-MG")) {
deps_missing_files = 1;
} else if (!strcmp (argv[i], "-MF")) {
p = argv[i] + 3;
} else if (!strcmp (argv[i], "-MP")) {
print_deps_phony_targets = 1;
} else if (!strcmp (argv[i], "-MQ") || !strcmp (argv[i], "-MT")) {
const char *tgt = argv[i] + 3;
int quoted = argv[i][2] == 'Q';
if (*tgt == '\0' && i + 1 == argc)
fatal ("target missing after %s option", argv[i]);
else
{
if (*tgt == '\0')
tgt = argv[++i];
deps_add_target (deps, tgt, quoted);
}
}
if (p) {
if (*p)
deps_file = p;
else if (i + 1 == argc)
fatal ("filename missing after %s option", argv[i]);
else
deps_file = argv[++i];
}
}
break;
case 'd':
dump_macros = 1;
no_output = 1;
break;
case 'v':
fprintf (stderr, "GNU traditional CPP version %s\n", version_string);
break;
case 'D':
case 'U':
case 'A':
{
char *p;
if (argv[i][2] != 0)
p = argv[i] + 2;
else if (i + 1 == argc)
fatal ("macro name missing after -%c option", c);
else
p = argv[++i];
if (c == 'D')
pend[i].type = PD_DEFINE;
else if (c == 'U')
pend[i].type = PD_UNDEF;
else
pend[i].type = PD_ASSERTION;
pend[i].arg = p;
}
break;
case 'C':
put_out_comments = 1;
break;
case 'p':
if (!strcmp (argv[i], "-pedantic"))
fatal ("-pedantic and -traditional are mutually exclusive");
break;
case 't':
if (!strcmp (argv[i], "-trigraphs"))
fatal ("-trigraphs and -traditional are mutually exclusive");
break;
case 'P':
no_line_commands = 1;
break;
case 'I':
add_include:
{
struct file_name_list *dirtmp;
if (! ignore_srcdir && !strcmp (argv[i] + 2, "-"))
ignore_srcdir = 1;
else {
dirtmp = (struct file_name_list *)
xmalloc (sizeof (struct file_name_list));
dirtmp->next = 0;
if (include == 0)
include = dirtmp;
else
last_include->next = dirtmp;
last_include = dirtmp;
if (argv[i][1] == 'I' && argv[i][2] != 0)
dirtmp->fname = argv[i] + 2;
else if (i + 1 == argc)
fatal ("directory name missing after -I option");
else
dirtmp->fname = argv[++i];
if (strlen (dirtmp->fname) > max_include_len)
max_include_len = strlen (dirtmp->fname);
if (ignore_srcdir && first_bracket_include == 0)
first_bracket_include = dirtmp;
}
}
break;
case 'n':
no_standard_includes = 1;
break;
case '\0':
if (in_fname == NULL) {
in_fname = "";
break;
} else if (out_fname == NULL) {
out_fname = "";
break;
}
default:
fatal ("invalid option `%s'", argv[i]);
}
}
}
init_dependency_output ();
if (print_deps == 0
&& (deps_missing_files || deps_file || print_deps_phony_targets))
fatal ("you must additionally specify either -M or -MM");
if (user_label_prefix == 0)
user_label_prefix = USER_LABEL_PREFIX;
if (print_deps)
{
deps_add_default_target (deps, in_fname);
deps_add_dep (deps, in_fname);
}
initialize_builtins ();
for (i = 1; i < argc; i++)
if (pend[i].type == PD_DEFINE)
make_definition (pend[i].arg);
else if (pend[i].type == PD_UNDEF)
make_undef (pend[i].arg);
else if (pend[i].type == PD_ASSERTION)
make_assertion (pend[i].arg);
if (!no_standard_includes) {
const struct default_include *di;
struct file_name_list *old_last_include = last_include;
struct file_name_list *dirtmp;
for (di = cpp_include_defaults; di->fname; di++) {
if (di->cplusplus)
continue;
dirtmp = (struct file_name_list *)
xmalloc (sizeof (struct file_name_list));
dirtmp->next = 0;
if (include == 0)
include = dirtmp;
else
last_include->next = dirtmp;
last_include = dirtmp;
dirtmp->fname = di->fname;
if (strlen (dirtmp->fname) > max_include_len)
max_include_len = strlen (dirtmp->fname);
}
if (ignore_srcdir && first_bracket_include == 0)
first_bracket_include = old_last_include->next;
}
outbuf.buf = (U_CHAR *) xmalloc (OUTBUF_SIZE);
outbuf.bufp = outbuf.buf;
outbuf.length = OUTBUF_SIZE;
no_output++;
indepth++;
for (i = 1; i < argc; i++)
if (pend[i].type == PD_FILE)
{
int fd = open (pend[i].arg, O_RDONLY, 0666);
if (fd < 0)
{
perror_with_name (pend[i].arg);
return FATAL_EXIT_CODE;
}
if (print_deps)
deps_add_dep (deps, pend[i].arg);
finclude (fd, pend[i].arg, 0, &outbuf);
}
indepth--;
no_output--;
free ((PTR) pend);
fp = &instack[++indepth];
if (in_fname == NULL || *in_fname == 0) {
in_fname = "";
f = 0;
} else if ((f = open (in_fname, O_RDONLY, 0666)) < 0)
goto sys_error;
if (file_size_and_mode (f, &st_mode, &st_size))
goto sys_error;
fp->fname = in_fname;
fp->lineno = 1;
if (!S_ISREG (st_mode)) {
int size;
int bsize;
int cnt;
U_CHAR *bufp;
bsize = 2000;
size = 0;
fp->buf = (U_CHAR *) xmalloc (bsize + 2);
bufp = fp->buf;
for (;;) {
cnt = read (f, bufp, bsize - size);
if (cnt < 0) goto sys_error;
if (cnt == 0) break;
size += cnt;
bufp += cnt;
if (bsize == size) {
bsize *= 2;
fp->buf = (U_CHAR *) xrealloc (fp->buf, bsize + 2);
bufp = fp->buf + size;
}
}
fp->length = size;
} else {
long i;
fp->length = 0;
fp->buf = (U_CHAR *) xmalloc (st_size + 2);
while (st_size > 0) {
i = read (f, fp->buf + fp->length, st_size);
if (i <= 0) {
if (i == 0) break;
goto sys_error;
}
fp->length += i;
st_size -= i;
}
}
fp->bufp = fp->buf;
fp->if_stack = if_stack;
if (fp->length > 0 && fp->buf[fp->length-1] != '\n')
fp->buf[fp->length++] = '\n';
fp->buf[fp->length] = '\0';
if (!out_fname || !strcmp (out_fname, ""))
out_fname = "stdout";
else if (! freopen (out_fname, "w", stdout))
pfatal_with_name (out_fname);
output_line_command (fp, &outbuf, 0, same_file);
rescan (&outbuf, 0);
if (dump_macros)
dump_all_macros ();
else if (! inhibit_output)
if (write (fileno (stdout), outbuf.buf, outbuf.bufp - outbuf.buf) < 0)
fatal ("I/O error on output");
if (print_deps && errors == 0)
output_deps ();
deps_free (deps);
if (ferror (stdout))
fatal ("I/O error on output");
if (errors)
exit (FATAL_EXIT_CODE);
exit (SUCCESS_EXIT_CODE);
sys_error:
pfatal_with_name (in_fname);
}
static void
init_dependency_output ()
{
char *spec, *s, *output_file;
if (print_deps == 0)
{
spec = getenv ("DEPENDENCIES_OUTPUT");
if (spec)
print_deps = 1;
else
{
spec = getenv ("SUNPRO_DEPENDENCIES");
if (spec)
print_deps = 2;
else
return;
}
s = strchr (spec, ' ');
if (s)
{
deps_add_target (deps, s + 1, 0);
output_file = (char *) xmalloc (s - spec + 1);
memcpy (output_file, spec, s - spec);
output_file[s - spec] = 0;
}
else
output_file = spec;
if (deps_file == 0)
deps_file = output_file;
deps_append = 1;
}
if (deps_file == 0 || deps_missing_files)
inhibit_output = 1;
}
static void
output_deps ()
{
FILE *deps_stream = 0;
const char *const deps_mode = deps_append ? "a" : "w";
if (deps_file == 0)
deps_stream = stdout;
else
{
deps_stream = fopen (deps_file, deps_mode);
if (deps_stream == 0)
{
error_from_errno (deps_file);
return;
}
}
deps_write (deps, deps_stream, 72);
if (print_deps_phony_targets)
deps_phony_targets (deps, deps_stream);
if (deps_file)
{
if (ferror (deps_stream) || fclose (deps_stream) != 0)
fatal ("I/O error on output");
}
}
static void
newline_fix (bp)
U_CHAR *bp;
{
U_CHAR *p = bp;
int count = 0;
while (*p++ == '\\' && *p++ == '\n')
count++;
p = bp + count * 2;
if (count == 0 || (*p != '/' && *p != '*'))
return;
while (*p == '*' || *p == '/')
*bp++ = *p++;
while (count-- > 0) {
*bp++ = '\\';
*bp++ = '\n';
}
}
static void
name_newline_fix (bp)
U_CHAR *bp;
{
U_CHAR *p = bp;
int count = 0;
while (*p++ == '\\' && *p++ == '\n')
count++;
p = bp + count * 2;
if (count == 0 || !is_idchar (*p))
return;
while (is_idchar (*p))
*bp++ = *p++;
while (count-- > 0) {
*bp++ = '\\';
*bp++ = '\n';
}
}
static void
rescan (op, output_marks)
FILE_BUF *op;
int output_marks;
{
U_CHAR c;
int ident_length = 0;
int hash = 0;
FILE_BUF *ip;
U_CHAR *ibp;
U_CHAR *limit;
U_CHAR *obp;
int redo_char = 0;
int concatenated = 0;
int start_line;
U_CHAR *beg_of_line;
U_CHAR *obufp_before_macroname = NULL;
#define POPMACRO \
do { ip->macro->type = T_MACRO; \
if (ip->free_ptr) free (ip->free_ptr); \
--indepth; } while (0)
#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; \
obufp_before_macroname += op->bufp - obp; \
obp = op->bufp; } while (0)
if (no_output && instack[indepth].fname != 0)
skip_if_group (&instack[indepth], 1);
obp = op->bufp;
RECACHE;
beg_of_line = ibp;
if (*limit != 0)
abort ();
while (1) {
c = *ibp++;
*obp++ = c;
switch (c) {
case '\\':
if (ibp >= limit)
break;
if (*ibp == '\n') {
++ibp;
++ip->lineno;
--obp;
break;
}
if (ident_length > 0)
goto specialchar;
*obp++ = *ibp++;
break;
case '#':
if (ip->macro != 0)
goto randomchar;
if (ident_length)
goto specialchar;
if (beg_of_line == 0)
goto randomchar;
if (beg_of_line + 1 != ibp)
goto randomchar;
--obp;
ip->bufp = ibp;
op->bufp = obp;
if (! handle_directive (ip, op)) {
#ifdef USE_C_ALLOCA
alloca (0);
#endif
if (no_output && instack[indepth].fname) {
skip_if_group (&instack[indepth], 1);
RECACHE;
beg_of_line = ibp;
break;
}
++obp;
goto randomchar;
}
#ifdef USE_C_ALLOCA
alloca (0);
#endif
if (no_output && instack[indepth].fname)
skip_if_group (&instack[indepth], 1);
obp = op->bufp;
RECACHE;
beg_of_line = ibp;
break;
case '\"':
case '\'':
if (ident_length)
goto specialchar;
start_line = ip->lineno;
while (1) {
if (ibp >= limit) {
if (ip->macro != 0) {
POPMACRO;
RECACHE;
continue;
}
break;
}
*obp++ = *ibp;
switch (*ibp++) {
case '\n':
++ip->lineno;
++op->lineno;
beg_of_line = ibp;
goto while2end;
case '\\':
if (ibp >= limit)
break;
if (*ibp == '\n') {
--obp;
++ibp;
++ip->lineno;
} else {
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 (ip->macro != 0)
goto randomchar;
if (ident_length)
goto specialchar;
if (*ibp != '*')
goto randomchar;
start_line = ip->lineno;
++ibp;
if (! put_out_comments)
obp--;
else
*obp++ = '*';
{
U_CHAR *before_bp = ibp;
while (ibp < limit) {
switch (*ibp++) {
case '/':
if (warn_comments && ibp < limit && *ibp == '*')
warning("`/*' within comment");
break;
case '*':
if (*ibp == '\\' && ibp[1] == '\n')
newline_fix (ibp);
if (ibp >= limit || *ibp == '/')
goto comment_end;
break;
case '\n':
++ip->lineno;
if (!put_out_comments)
*obp++ = '\n';
++op->lineno;
}
}
comment_end:
if (ibp >= limit)
error_with_line (line_for_error (start_line),
"unterminated comment");
else {
ibp++;
if (put_out_comments) {
memcpy (obp, before_bp, ibp - before_bp);
obp += ibp - before_bp;
}
}
}
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if (ident_length == 0) {
while (ibp < limit) {
while (ibp < limit && ibp[0] == '\\' && ibp[1] == '\n') {
++ip->lineno;
ibp += 2;
}
c = *ibp++;
if (! ISIDNUM (c) && c != '.') {
--ibp;
break;
}
*obp++ = c;
if (c == 'e' || c == 'E') {
while (ibp < limit && ibp[0] == '\\' && ibp[1] == '\n') {
++ip->lineno;
ibp += 2;
}
if (ibp < limit && (*ibp == '+' || *ibp == '-')) {
*obp++ = *ibp++;
break;
}
}
}
break;
}
case '_':
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
case 's': case 't': case 'u': case 'v': case 'w': case 'x':
case 'y': case 'z':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
case 'Y': case 'Z':
ident_length++;
hash = HASHSTEP (hash, c);
break;
case '\n':
if (ip->macro != 0) {
if (*ibp == '-') {
if (! concatenated) {
ident_length = 0;
hash = 0;
}
ibp++;
if (!output_marks) {
obp--;
} else {
*obp++ = '-';
}
} else if (is_space (*ibp)) {
if (ident_length > 0)
goto specialchar;
if (!output_marks) {
obp[-1] = *ibp++;
if (obp[-1] == '\n')
op->lineno++;
} else {
*obp++ = *ibp++;
}
} else abort ();
break;
}
if (ident_length > 0)
goto specialchar;
beg_of_line = ibp;
++ip->lineno;
++op->lineno;
if (ip->lineno != op->lineno) {
op->bufp = obp;
output_line_command (ip, op, 1, same_file);
check_expand (op, ip->length - (ip->bufp - ip->buf));
obp = op->bufp;
}
break;
case 0:
if (ibp <= limit)
goto randomchar;
if (ip->macro != 0) {
obp--;
ibp--;
if (ident_length && ! is_idchar (*instack[indepth - 1].bufp)) {
redo_char = 1;
goto randomchar;
}
POPMACRO;
RECACHE;
break;
}
if (ident_length == 0) {
obp--;
ibp--;
op->bufp = obp;
ip->bufp = ibp;
goto ending;
}
specialchar:
ibp--;
obp--;
redo_char = 1;
default:
randomchar:
if (ident_length > 0) {
HASHNODE *hp;
for (hp = hashtab[MAKE_POS (hash) % HASHSIZE]; hp != NULL;
hp = hp->next) {
if (hp->length == ident_length) {
int op_lineno_before_macroname;
int i = ident_length;
U_CHAR *p = hp->name;
U_CHAR *q = obp - i;
if (! redo_char)
q--;
do {
if (*p++ != *q++)
goto hashcollision;
} while (--i);
if (! redo_char) {
ibp--;
obp--;
}
obufp_before_macroname = obp - ident_length;
op_lineno_before_macroname = op->lineno;
if (hp->type == T_MACRO && hp->value.defn->nargs >= 0)
{
while (1) {
if (ibp == limit && ip->macro != 0) {
POPMACRO;
RECACHE;
}
else if (*ibp == '/' && ibp+1 != limit && ibp[1] == '*') {
if (put_out_comments) {
*obp++ = '/';
*obp++ = '*';
}
ibp += 2;
while (ibp + 1 != limit
&& !(ibp[0] == '*' && ibp[1] == '/')) {
if (*ibp == '\n') {
++ip->lineno;
++op->lineno;
}
if (put_out_comments)
*obp++ = *ibp++;
else
ibp++;
}
ibp += 2;
if (put_out_comments) {
*obp++ = '*';
*obp++ = '/';
}
}
else if (is_space (*ibp)) {
*obp++ = *ibp++;
if (ibp[-1] == '\n') {
if (ip->macro == 0) {
++ip->lineno;
++op->lineno;
} else if (!output_marks) {
obp--;
if (*ibp == '-')
ibp++;
else {
if (*ibp == '\n')
++op->lineno;
*obp++ = *ibp++;
}
} else {
*obp++ = *ibp++;
}
}
}
else break;
}
if (*ibp != '(')
break;
}
obp = obufp_before_macroname;
op->lineno = op_lineno_before_macroname;
ip->bufp = ibp;
op->bufp = obp;
macroexpand (hp, op);
obp = op->bufp;
RECACHE;
break;
}
hashcollision:
;
}
ident_length = hash = 0;
redo_char = 0;
concatenated = 0;
}
}
}
ending:
if (if_stack != ip->if_stack) {
const char *str;
switch (if_stack->type) {
case T_IF:
str = "if";
break;
case T_IFDEF:
str = "ifdef";
break;
case T_IFNDEF:
str = "ifndef";
break;
case T_ELSE:
str = "else";
break;
case T_ELIF:
str = "elif";
break;
default:
abort ();
}
error_with_line (line_for_error (if_stack->lineno),
"unterminated #%s conditional", str);
}
if_stack = ip->if_stack;
}
static FILE_BUF
expand_to_temp_buffer (buf, limit, output_marks)
const U_CHAR *buf, *limit;
int output_marks;
{
FILE_BUF *ip;
FILE_BUF obuf;
int length = limit - buf;
U_CHAR *buf1;
int odepth = indepth;
if (length < 0)
abort ();
buf1 = (U_CHAR *) alloca (length + 1);
{
const U_CHAR *p1 = buf;
U_CHAR *p2 = buf1;
while (p1 != limit)
*p2++ = *p1++;
}
buf1[length] = 0;
obuf.length = length * 2 + 100;
obuf.bufp = obuf.buf = (U_CHAR *) xmalloc (obuf.length);
obuf.fname = 0;
obuf.macro = 0;
obuf.free_ptr = 0;
CHECK_DEPTH ({return obuf;});
++indepth;
ip = &instack[indepth];
ip->fname = 0;
ip->macro = 0;
ip->free_ptr = 0;
ip->length = length;
ip->buf = ip->bufp = buf1;
ip->if_stack = if_stack;
ip->lineno = obuf.lineno = 1;
rescan (&obuf, output_marks);
--indepth;
if (indepth != odepth)
abort ();
obuf.length = obuf.bufp - obuf.buf;
return obuf;
}
static int
handle_directive (ip, op)
FILE_BUF *ip, *op;
{
U_CHAR *bp, *cp;
const struct directive *kt;
int ident_length;
U_CHAR *resume_p;
int copy_command = 0;
U_CHAR *ident, *after_ident;
bp = ip->bufp;
while (1) {
if (is_nvspace (*bp))
bp++;
else if (*bp == '/' && (newline_fix (bp + 1), bp[1]) == '*') {
ip->bufp = bp;
skip_to_end_of_comment (ip, &ip->lineno);
bp = ip->bufp;
} else if (*bp == '\\' && bp[1] == '\n') {
bp += 2; ip->lineno++;
} else break;
}
cp = bp;
while (1) {
if (is_idchar (*cp))
cp++;
else {
if (*cp == '\\' && cp[1] == '\n')
name_newline_fix (cp);
if (is_idchar (*cp))
cp++;
else break;
}
}
ident_length = cp - bp;
ident = bp;
after_ident = cp;
if (ident_length == 0 && *after_ident == '\n') {
ip->bufp = after_ident;
return 1;
}
for (kt = directive_table; kt->length > 0; kt++) {
if (kt->length == ident_length
&& !strncmp (kt->name, (const char *)ident, ident_length)) {
U_CHAR *buf;
U_CHAR *limit = ip->buf + ip->length;
int unterminated = 0;
int keep_comments = kt->type == T_DEFINE;
buf = bp = after_ident;
while (bp < limit) {
U_CHAR c = *bp++;
switch (c) {
case '\\':
if (bp < limit) {
if (*bp == '\n') {
ip->lineno++;
copy_command = 1;
}
bp++;
}
break;
case '\'':
case '\"':
bp = skip_quoted_string (bp - 1, limit, ip->lineno, &ip->lineno, ©_command, &unterminated);
if (unterminated) {
ip->bufp = bp;
goto endloop1;
}
break;
case '<':
if (kt->type != T_INCLUDE)
break;
while (*bp && *bp != '>') bp++;
break;
case '/':
if (*bp == '\\' && bp[1] == '\n')
newline_fix (bp);
if (*bp == '*') {
U_CHAR *obp = bp - 1;
ip->bufp = bp + 1;
skip_to_end_of_comment (ip, &ip->lineno);
bp = ip->bufp;
if (bp == limit || *bp == '\n') {
bp = obp;
goto endloop1;
}
if (! keep_comments)
copy_command++;
}
break;
case '\n':
--bp;
ip->bufp = bp;
goto endloop1;
}
}
ip->bufp = bp;
endloop1:
resume_p = ip->bufp;
if (copy_command) {
U_CHAR *xp = buf;
cp = (U_CHAR *) alloca (bp - buf + 5);
buf = cp;
while (xp < bp) {
U_CHAR c = *xp++;
*cp++ = c;
switch (c) {
case '\n':
break;
case '<':
if (kt->type != T_INCLUDE)
break;
while (xp < bp && c != '>') {
c = *xp++;
if (c == '\\' && xp < bp && *xp == '\n')
xp++, ip->lineno++;
else
*cp++ = c;
}
break;
case '\\':
if (*xp == '\n') {
xp++;
cp--;
if (cp != buf && is_space (cp[-1])) {
while (cp != buf && is_space(cp[-1])) cp--;
cp++;
SKIP_WHITE_SPACE (xp);
} else if (is_nvspace (*xp)) {
*cp++ = *xp++;
SKIP_WHITE_SPACE (xp);
}
} else {
*cp++ = *xp++;
}
break;
case '\'':
case '\"':
{
const U_CHAR *bp1
= skip_quoted_string (xp - 1, limit, ip->lineno, 0, 0, 0);
while (xp != bp1)
*cp++ = *xp++;
}
break;
case '/':
if (*xp == '*') {
ip->bufp = xp + 1;
skip_to_end_of_comment (ip, 0);
if (keep_comments)
while (xp != ip->bufp)
*cp++ = *xp++;
else
cp--;
xp = ip->bufp;
}
}
}
*cp = 0;
}
else
cp = bp;
ip->bufp = resume_p;
(*kt->func) (buf, cp, op);
check_expand (op, ip->length - (ip->bufp - ip->buf));
return 1;
}
}
return 0;
}
static const char *const
monthnames[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
static void
special_symbol (hp, op)
HASHNODE *hp;
FILE_BUF *op;
{
const char *buf;
time_t t;
int i, len;
int true_indepth;
FILE_BUF *ip = NULL;
static struct tm *timebuf = NULL;
int paren = 0;
for (i = indepth; i >= 0; i--)
if (instack[i].fname != NULL) {
ip = &instack[i];
break;
}
if (ip == NULL)
fatal ("not in any file?!");
switch (hp->type) {
case T_FILE:
case T_BASE_FILE:
{
const char *string;
if (hp->type == T_FILE)
string = ip->fname;
else
string = instack[0].fname;
if (string)
{
char *tmp = (char *) alloca (3 + strlen (string));
sprintf (tmp, "\"%s\"", string);
buf = tmp;
}
else
buf = "";
break;
}
case T_INCLUDE_LEVEL:
{
char *tmp = (char *) alloca (8);
true_indepth = 0;
for (i = indepth; i >= 0; i--)
if (instack[i].fname != NULL)
true_indepth++;
sprintf (tmp, "%d", true_indepth - 1);
buf = tmp;
break;
}
case T_VERSION:
{
char *tmp = (char *) alloca (3 + strlen (version_string));
sprintf (tmp, "\"%s\"", version_string);
buf = tmp;
break;
}
case T_CONST:
buf = hp->value.cpval;
break;
case T_SPECLINE:
{
char *tmp = (char *) alloca (10);
sprintf (tmp, "%d", ip->lineno);
buf = tmp;
break;
}
case T_DATE:
case T_TIME:
{
char *tmp = (char *) alloca (20);
if (timebuf == NULL) {
t = time (0);
timebuf = localtime (&t);
}
if (hp->type == T_DATE)
sprintf (tmp, "\"%s %2d %4d\"", monthnames[timebuf->tm_mon],
timebuf->tm_mday, timebuf->tm_year + 1900);
else
sprintf (tmp, "\"%02d:%02d:%02d\"", timebuf->tm_hour, timebuf->tm_min,
timebuf->tm_sec);
buf = tmp;
break;
}
case T_SPEC_DEFINED:
buf = " 0 ";
ip = &instack[indepth];
SKIP_WHITE_SPACE (ip->bufp);
if (*ip->bufp == '(') {
paren++;
ip->bufp++;
SKIP_WHITE_SPACE (ip->bufp);
}
if (!is_idstart (*ip->bufp))
goto oops;
{
HASHNODE *hp = lookup (ip->bufp, -1, -1);
if (hp && hp->type != T_UNUSED && hp->type != T_SPEC_DEFINED)
buf = " 1 ";
}
while (is_idchar (*ip->bufp))
++ip->bufp;
SKIP_WHITE_SPACE (ip->bufp);
if (paren) {
if (*ip->bufp != ')')
goto oops;
++ip->bufp;
}
break;
oops:
error ("`defined' must be followed by ident or (ident)");
break;
default:
error ("cccp error: invalid special hash type");
abort ();
}
len = strlen (buf);
check_expand (op, len);
memcpy (op->bufp, buf, len);
op->bufp += len;
}
static void
do_include (buf, limit, op)
U_CHAR *buf, *limit;
FILE_BUF *op;
{
U_CHAR *fbeg, *fend;
struct file_name_list *stackp = include;
struct file_name_list dsp[1];
int flen;
int retried = 0;
FILE_BUF trybuf;
int system_header_p = 0;
get_filename:
fbeg = buf;
SKIP_WHITE_SPACE (fbeg);
while (limit != fbeg && is_nvspace (limit[-1])) limit--;
switch (*fbeg++) {
case '\"':
fend = fbeg;
while (fend != limit && *fend != '\"')
fend++;
if (*fend == '\"' && fend + 1 == limit) {
FILE_BUF *fp;
if (ignore_srcdir) break;
for (fp = &instack[indepth]; fp >= instack; fp--)
{
size_t n;
const char *ep, *nam;
if ((nam = fp->fname) != NULL) {
dsp[0].next = stackp;
stackp = dsp;
ep = strrchr (nam, '/');
if (ep != NULL) {
char *f;
n = ep - nam;
f = (char *) alloca (n + 1);
strncpy (f, nam, n);
f[n] = '\0';
dsp[0].fname = f;
if (n > max_include_len) max_include_len = n;
} else {
dsp[0].fname = 0;
}
break;
}
}
break;
}
goto fail;
case '<':
fend = fbeg;
while (fend != limit && *fend != '>') fend++;
if (*fend == '>' && fend + 1 == limit) {
system_header_p = 1;
if (first_bracket_include)
stackp = first_bracket_include;
break;
}
goto fail;
default:
fail:
if (retried) {
error ("#include expects \"fname\" or <fname>");
return;
} else {
trybuf = expand_to_temp_buffer (buf, limit, 0);
buf = (U_CHAR *) alloca (trybuf.bufp - trybuf.buf + 1);
memcpy (buf, trybuf.buf, trybuf.bufp - trybuf.buf);
limit = buf + (trybuf.bufp - trybuf.buf);
free (trybuf.buf);
retried++;
goto get_filename;
}
}
flen = fend - fbeg;
process_include (stackp, fbeg, flen, system_header_p, op);
}
static void
do_include_next (buf, limit, op)
U_CHAR *buf, *limit;
FILE_BUF *op;
{
U_CHAR *fbeg, *fend;
struct file_name_list *stackp;
int flen;
int retried = 0;
FILE_BUF trybuf;
int system_header_p = 0;
stackp = instack[indepth].next_header_dir;
if (stackp == 0)
{
do_include (buf, limit, op);
return;
}
get_filename:
fbeg = buf;
SKIP_WHITE_SPACE (fbeg);
while (limit != fbeg && is_nvspace (limit[-1])) limit--;
switch (*fbeg++) {
case '\"':
fend = fbeg;
while (fend != limit && *fend != '\"')
fend++;
if (*fend == '\"' && fend + 1 == limit)
break;
goto fail;
case '<':
fend = fbeg;
while (fend != limit && *fend != '>') fend++;
if (*fend == '>' && fend + 1 == limit) {
system_header_p = 1;
break;
}
goto fail;
default:
fail:
if (retried) {
error ("#include expects \"fname\" or <fname>");
return;
} else {
trybuf = expand_to_temp_buffer (buf, limit, 0);
buf = (U_CHAR *) alloca (trybuf.bufp - trybuf.buf + 1);
memcpy (buf, trybuf.buf, trybuf.bufp - trybuf.buf);
limit = buf + (trybuf.bufp - trybuf.buf);
free (trybuf.buf);
retried++;
goto get_filename;
}
}
flen = fend - fbeg;
process_include (stackp, fbeg, flen, system_header_p, op);
}
static void
process_include (stackp, fbeg, flen, system_header_p, op)
struct file_name_list *stackp;
const U_CHAR *fbeg;
int flen;
int system_header_p;
FILE_BUF *op;
{
char *fname;
int f = -1;
fname = (char *) alloca (max_include_len + flen + 2);
if (IS_ABSOLUTE_PATHNAME (fbeg)) {
strncpy (fname, (const char *)fbeg, flen);
fname[flen] = 0;
f = open (fname, O_RDONLY, 0666);
} else {
for (; stackp; stackp = stackp->next) {
if (stackp->fname) {
strcpy (fname, stackp->fname);
strcat (fname, "/");
fname[strlen (fname) + flen] = 0;
} else {
fname[0] = 0;
}
strncat (fname, (const char *)fbeg, flen);
if ((f = open (fname, O_RDONLY, 0666)) >= 0)
break;
}
}
if (f < 0) {
strncpy (fname, (const char *)fbeg, flen);
fname[flen] = 0;
if (deps_missing_files
&& print_deps > (system_header_p || (system_include_depth > 0))) {
if (first_bracket_include)
stackp = first_bracket_include;
else
stackp = include;
if (!system_header_p || IS_ABSOLUTE_PATHNAME (fbeg) || !stackp->fname)
deps_add_dep (deps, fname);
else {
char *p;
int len = strlen(stackp->fname);
p = (char *) alloca (len + flen + 2);
memcpy (p, stackp->fname, len);
p[len++] = '/';
memcpy (p + len, fbeg, flen);
len += flen;
p[len] = '\0';
deps_add_dep (deps, p);
}
} else if (print_deps
&& print_deps <= (system_header_p
|| (system_include_depth > 0)))
warning ("no include path in which to find %.*s", flen, fbeg);
else
error_from_errno (fname);
} else {
struct file_name_list* ptr;
for (ptr = dont_repeat_files; ptr; ptr = ptr->next) {
if (!strcmp (ptr->fname, fname)) {
close (f);
return;
}
}
for (ptr = all_include_files; ptr; ptr = ptr->next) {
if (!strcmp (ptr->fname, fname))
break;
}
if (ptr == 0) {
ptr = (struct file_name_list *) xmalloc (sizeof (struct file_name_list));
ptr->next = all_include_files;
all_include_files = ptr;
ptr->fname = xstrdup (fname);
if (print_deps > (system_header_p || (system_include_depth > 0)))
deps_add_dep (deps, fname);
}
if (system_header_p)
system_include_depth++;
finclude (f, fname, stackp->next, op);
if (system_header_p)
system_include_depth--;
close (f);
}
}
static void
finclude (f, fname, nhd, op)
int f;
const char *fname;
struct file_name_list *nhd;
FILE_BUF *op;
{
int st_mode;
long st_size;
long i;
FILE_BUF *fp;
CHECK_DEPTH (return;);
if (file_size_and_mode (f, &st_mode, &st_size))
goto nope;
fp = &instack[indepth + 1];
memset (fp, 0, sizeof (FILE_BUF));
fp->fname = fname;
fp->length = 0;
fp->lineno = 1;
fp->if_stack = if_stack;
fp->next_header_dir = nhd;
if (S_ISREG (st_mode)) {
fp->buf = (U_CHAR *) xmalloc (st_size + 2);
fp->bufp = fp->buf;
while (st_size > 0) {
i = read (f, fp->buf + fp->length, st_size);
if (i <= 0) {
if (i == 0) break;
goto nope;
}
fp->length += i;
st_size -= i;
}
}
else {
U_CHAR *bufp;
U_CHAR *basep;
int bsize = 2000;
st_size = 0;
basep = (U_CHAR *) xmalloc (bsize + 2);
bufp = basep;
for (;;) {
i = read (f, bufp, bsize - st_size);
if (i < 0)
goto nope;
if (i == 0)
break;
st_size += i;
bufp += i;
if (bsize == st_size) {
bsize *= 2;
basep = (U_CHAR *) xrealloc (basep, bsize + 2);
bufp = basep + st_size;
}
}
fp->buf = basep;
fp->bufp = fp->buf;
fp->length = st_size;
}
close (f);
if (fp->length > 0 && fp->buf[fp->length-1] != '\n')
fp->buf[fp->length++] = '\n';
fp->buf[fp->length] = '\0';
indepth++;
output_line_command (fp, op, 0, enter_file);
rescan (op, 0);
indepth--;
instack[indepth].lineno++;
instack[indepth].bufp++;
output_line_command (&instack[indepth], op, 0, leave_file);
free (fp->buf);
return;
nope:
perror_with_name (fname);
close (f);
}
static void
do_define (buf, limit, op)
U_CHAR *buf, *limit;
FILE_BUF *op ATTRIBUTE_UNUSED;
{
U_CHAR *bp;
U_CHAR *symname;
int sym_length;
DEFINITION *defn;
int arglengths = 0;
int hashcode;
bp = buf;
while (is_nvspace (*bp))
bp++;
symname = bp;
while (is_idchar (*bp) && bp < limit) {
bp++;
}
sym_length = bp - symname;
if (sym_length == 0)
{
error ("invalid macro name");
return;
}
else if (!is_idstart (*symname)) {
U_CHAR *msg;
msg = (U_CHAR *) alloca (sym_length + 1);
memcpy (msg, symname, sym_length);
msg[sym_length] = 0;
error ("invalid macro name `%s'", msg);
return;
} else {
if (! strncmp ((const char *)symname, "defined", 7) && sym_length == 7)
{
error ("\"defined\" cannot be used as a macro name");
return;
}
}
if (*bp == '(') {
struct arglist *arg_ptrs = NULL;
int argno = 0;
bp++;
SKIP_WHITE_SPACE (bp);
while (*bp != ')') {
struct arglist *temp;
temp = (struct arglist *) alloca (sizeof (struct arglist));
temp->name = bp;
temp->next = arg_ptrs;
temp->argno = argno++;
arg_ptrs = temp;
if (!is_idstart (*bp))
warning ("parameter name starts with a digit in #define");
while (is_idchar (*bp)) {
bp++;
}
temp->length = bp - temp->name;
arglengths += temp->length + 2;
SKIP_WHITE_SPACE (bp);
if (temp->length == 0 || (*bp != ',' && *bp != ')')) {
error ("badly punctuated parameter list in #define");
return;
}
if (*bp == ',') {
bp++;
SKIP_WHITE_SPACE (bp);
}
if (bp >= limit) {
error ("unterminated parameter list in #define");
return;
}
}
++bp;
while (is_nvspace (*bp) && bp < limit)
++bp;
defn = collect_expansion (bp, limit, argno, arg_ptrs);
{
struct arglist *temp;
int i = 0;
U_CHAR *tmp = (U_CHAR *) xmalloc (arglengths + 1);
for (temp = arg_ptrs; temp; temp = temp->next) {
memcpy (&tmp[i], temp->name, temp->length);
i += temp->length;
if (temp->next != 0) {
tmp[i++] = ',';
tmp[i++] = ' ';
}
}
tmp[i] = 0;
defn->argnames = tmp;
}
} else {
while (is_nvspace (*bp) && bp < limit)
++bp;
defn = collect_expansion (bp, limit, -1, 0);
defn->argnames = (const U_CHAR *) "";
}
hashcode = hashf (symname, sym_length, HASHSIZE);
{
HASHNODE *hp;
if ((hp = lookup (symname, sym_length, hashcode)) == NULL)
hp = install (symname, sym_length, T_MACRO, hashcode);
else {
if (hp->type != T_MACRO || compare_defs (defn, hp->value.defn))
warning ("\"%.*s\" redefined", sym_length, symname);
hp->type = T_MACRO;
}
hp->value.defn = defn;
}
}
static int
compare_defs (d1, d2)
DEFINITION *d1, *d2;
{
struct reflist *a1, *a2;
U_CHAR *p1 = d1->expansion;
U_CHAR *p2 = d2->expansion;
int first = 1;
if (d1->nargs != d2->nargs)
return 1;
if (strcmp ((const char *)d1->argnames, (const char *)d2->argnames))
return 1;
for (a1 = d1->pattern, a2 = d2->pattern; a1 && a2;
a1 = a1->next, a2 = a2->next) {
if (!((a1->nchars == a2->nchars
&& ! strncmp ((const char *)p1, (const char *)p2, a1->nchars))
|| ! comp_def_part (first, p1, a1->nchars, p2, a2->nchars, 0))
|| a1->argno != a2->argno
|| a1->stringify != a2->stringify
|| a1->raw_before != a2->raw_before
|| a1->raw_after != a2->raw_after)
return 1;
first = 0;
p1 += a1->nchars;
p2 += a2->nchars;
}
if (a1 != a2)
return 1;
if (comp_def_part (first, p1, d1->length - (p1 - d1->expansion),
p2, d2->length - (p2 - d2->expansion), 1))
return 1;
return 0;
}
static int
comp_def_part (first, beg1, len1, beg2, len2, last)
int first;
const U_CHAR *beg1, *beg2;
int len1, len2;
int last;
{
const U_CHAR *end1 = beg1 + len1;
const U_CHAR *end2 = beg2 + len2;
if (first) {
while (beg1 != end1 && is_space (*beg1)) beg1++;
while (beg2 != end2 && is_space (*beg2)) beg2++;
}
if (last) {
while (beg1 != end1 && is_space (end1[-1])) end1--;
while (beg2 != end2 && is_space (end2[-1])) end2--;
}
while (beg1 != end1 && beg2 != end2) {
if (is_space (*beg1) && is_space (*beg2)) {
while (beg1 != end1 && is_space (*beg1)) beg1++;
while (beg2 != end2 && is_space (*beg2)) beg2++;
} else if (*beg1 == *beg2) {
beg1++; beg2++;
} else break;
}
return (beg1 != end1) || (beg2 != end2);
}
static DEFINITION *
collect_expansion (buf, end, nargs, arglist)
U_CHAR *buf, *end;
int nargs;
struct arglist *arglist;
{
DEFINITION *defn;
U_CHAR *p, *limit, *lastp, *exp_p;
struct reflist *endpat = NULL;
U_CHAR *concat = 0;
U_CHAR *stringify = 0;
int maxsize;
int expected_delimiter = '\0';
if (end < buf)
abort ();
limit = end;
p = buf;
while (p < limit && is_space (limit[-1])) limit--;
while (p < limit && is_space (*p)) p++;
maxsize = (sizeof (DEFINITION)
+ 2 * (end - limit) + 2 * (p - buf)
+ (limit - p) + 3);
defn = (DEFINITION *) xcalloc (1, maxsize);
defn->nargs = nargs;
exp_p = defn->expansion = (U_CHAR *) defn + sizeof (DEFINITION);
lastp = exp_p;
p = buf;
while (p < limit && is_space (*p)) {
*exp_p++ = '\n';
*exp_p++ = *p++;
}
while (p < limit) {
int skipped_arg = 0;
U_CHAR c = *p++;
*exp_p++ = c;
switch (c) {
case '\'':
case '\"':
if (expected_delimiter != '\0') {
if (c == expected_delimiter)
expected_delimiter = '\0';
} else
expected_delimiter = c;
break;
case '\\':
if (expected_delimiter != 0 && p < limit
&& (*p == expected_delimiter || *p == '\\')) {
*exp_p++ = *p++;
continue;
}
break;
case '/':
if (expected_delimiter != '\0')
break;
if (*p == '*') {
exp_p--;
p += 1;
while (p < limit && !(p[-2] == '*' && p[-1] == '/'))
p++;
}
break;
}
if (is_idchar (c) && nargs > 0) {
U_CHAR *id_beg = p - 1;
int id_len;
--exp_p;
while (p != limit && is_idchar (*p)) p++;
id_len = p - id_beg;
if (is_idstart (c)) {
struct arglist *arg;
for (arg = arglist; arg != NULL; arg = arg->next) {
struct reflist *tpat;
if (arg->name[0] == c
&& arg->length == id_len
&& strncmp ((const char *)arg->name,
(const char *)id_beg, id_len) == 0) {
tpat = (struct reflist *) xmalloc (sizeof (struct reflist));
tpat->next = NULL;
tpat->raw_before = concat == id_beg;
tpat->raw_after = 0;
tpat->stringify = expected_delimiter != '\0';
if (endpat == NULL)
defn->pattern = tpat;
else
endpat->next = tpat;
endpat = tpat;
tpat->argno = arg->argno;
tpat->nchars = exp_p - lastp;
{
U_CHAR *p1 = p;
SKIP_WHITE_SPACE (p1);
if (p1 + 2 <= limit && p1[0] == '#' && p1[1] == '#')
tpat->raw_after = 1;
}
lastp = exp_p;
skipped_arg = 1;
break;
}
}
}
if (! skipped_arg) {
U_CHAR *lim1 = p;
p = id_beg;
while (p != lim1)
*exp_p++ = *p++;
if (stringify == id_beg)
error ("# operator should be followed by a macro argument name");
}
}
}
if (limit < end) {
while (limit < end && is_space (*limit)) {
*exp_p++ = '\n';
*exp_p++ = *limit++;
}
}
*exp_p = '\0';
defn->length = exp_p - defn->expansion;
if (defn->length + 1 > maxsize)
abort ();
return defn;
}
#define FNAME_HASHSIZE 37
static void
do_line (buf, limit, op)
U_CHAR *buf, *limit;
FILE_BUF *op;
{
U_CHAR *bp;
FILE_BUF *ip = &instack[indepth];
FILE_BUF tem;
int new_lineno;
enum file_change_code file_change = same_file;
tem = expand_to_temp_buffer (buf, limit, 0);
bp = tem.buf;
SKIP_WHITE_SPACE (bp);
if (!ISDIGIT (*bp)) {
error ("invalid format #line command");
return;
}
new_lineno = atoi ((const char *)bp);
while (ISDIGIT (*bp))
bp++;
SKIP_WHITE_SPACE (bp);
if (*bp == '\"') {
static HASHNODE *fname_table[FNAME_HASHSIZE];
HASHNODE *hp, **hash_bucket;
U_CHAR *fname;
int fname_length;
fname = ++bp;
while (*bp && *bp != '\"')
bp++;
if (*bp != '\"') {
error ("invalid format #line command");
return;
}
fname_length = bp - fname;
bp++;
SKIP_WHITE_SPACE (bp);
if (*bp) {
if (*bp == '1')
file_change = enter_file;
else if (*bp == '2')
file_change = leave_file;
else {
error ("invalid format #line command");
return;
}
bp++;
SKIP_WHITE_SPACE (bp);
if (*bp) {
error ("invalid format #line command");
return;
}
}
hash_bucket =
&fname_table[hashf (fname, fname_length, FNAME_HASHSIZE)];
for (hp = *hash_bucket; hp != NULL; hp = hp->next)
if (hp->length == fname_length &&
strncmp (hp->value.cpval, (const char *)fname, fname_length) == 0) {
ip->fname = hp->value.cpval;
break;
}
if (hp == 0) {
char *q;
hp = (HASHNODE *) xcalloc (1, sizeof (HASHNODE) + fname_length + 1);
hp->next = *hash_bucket;
*hash_bucket = hp;
hp->length = fname_length;
ip->fname = hp->value.cpval = q = ((char *) hp) + sizeof (HASHNODE);
memcpy (q, fname, fname_length);
}
} else if (*bp) {
error ("invalid format #line command");
return;
}
ip->lineno = new_lineno;
output_line_command (ip, op, 0, file_change);
ip->bufp++;
check_expand (op, ip->length - (ip->bufp - ip->buf));
}
static void
do_undef (buf, limit, op)
U_CHAR *buf;
U_CHAR *limit ATTRIBUTE_UNUSED;
FILE_BUF *op ATTRIBUTE_UNUSED;
{
HASHNODE *hp;
SKIP_WHITE_SPACE (buf);
if (! strncmp ((const char *)buf, "defined", 7) && ! is_idchar (buf[7]))
warning ("undefining `defined'");
while ((hp = lookup (buf, -1, -1)) != NULL) {
if (hp->type != T_MACRO)
warning ("undefining `%s'", hp->name);
delete_macro (hp);
}
}
static int
parse_answer (buf, limit, answerp, type)
const unsigned char *buf, *limit;
struct answer **answerp;
int type;
{
const unsigned char *start;
if (buf < limit && *buf == ' ')
buf++;
if (buf == limit && type == T_UNASSERT)
return 0;
if (buf == limit || *buf++ != '(')
{
if (type == T_IF)
return 0;
error ("missing '(' after predicate");
return 1;
}
while (buf < limit && *buf == ' ')
buf++;
start = buf;
while (buf < limit && *buf != ')')
buf++;
if (buf == limit)
{
error ("missing ')' to complete answer");
return 1;
}
if (buf == start)
{
error ("predicate's answer is empty");
return 1;
}
if ((type == T_ASSERT || type == T_UNASSERT) && buf + 1 != limit)
{
error ("extra text at end of directive");
return 1;
}
if (buf[-1] == ' ')
buf--;
*answerp = (struct answer *) xmalloc (sizeof (struct answer));
(*answerp)->answer = start;
(*answerp)->len = buf - start;
return 0;
}
static HASHNODE *
parse_assertion (buf, limit, answerp, type)
const unsigned char *buf, *limit;
struct answer **answerp;
int type;
{
HASHNODE *result = 0;
const unsigned char *climit;
unsigned char *bp, *symname = canonicalize_text (buf, limit, &climit);
unsigned int len;
bp = symname;
if (bp < climit && is_idstart (*bp))
{
do
bp++;
while (bp < climit && is_idchar (*bp));
}
len = bp - symname;
*answerp = 0;
if (len == 0)
{
if (symname == climit)
error ("assertion without predicate");
else
error ("predicate must be an identifier");
}
else if (parse_answer (bp, climit, answerp, type) == 0)
{
unsigned char *sym = alloca (len + 1);
int hashcode;
sym[0] = '#';
memcpy (sym + 1, symname, len);
hashcode = hashf (sym, len + 1, HASHSIZE);
result = lookup (sym, len + 1, hashcode);
if (result == 0)
result = install (sym, len + 1, T_UNUSED, hashcode);
}
return result;
}
int
test_assertion (pbuf)
unsigned char **pbuf;
{
unsigned char *buf = *pbuf;
unsigned char *limit = buf + strlen ((char *) buf);
struct answer *answer;
HASHNODE *node;
int result = 0;
node = parse_assertion (buf, limit, &answer, T_IF);
if (node)
{
result = (node->type == T_ASSERT &&
(answer == 0 || *find_answer (node, answer) != 0));
if (is_space (*buf))
buf++;
while (is_idchar (*buf))
buf++;
if (answer)
while (*buf++ != ')')
;
*pbuf = buf;
}
return result;
}
static void
do_error (buf, limit, op)
U_CHAR *buf;
U_CHAR *limit;
FILE_BUF *op ATTRIBUTE_UNUSED;
{
error ("#error%.*s", (int) (limit - buf), buf);
}
static void
do_warning (buf, limit, op)
U_CHAR *buf;
U_CHAR *limit;
FILE_BUF *op ATTRIBUTE_UNUSED;
{
warning ("#warning%.*s", (int) (limit - buf), buf);
}
static void
do_assert (buf, limit, op)
U_CHAR *buf;
U_CHAR *limit;
FILE_BUF *op ATTRIBUTE_UNUSED;
{
struct answer *new_answer;
HASHNODE *node;
node = parse_assertion (buf, limit, &new_answer, T_ASSERT);
if (node)
{
new_answer->next = 0;
if (node->type == T_ASSERT)
{
if (*find_answer (node, new_answer))
{
free (new_answer);
warning ("\"%s\" re-asserted", node->name + 1);
return;
}
new_answer->next = node->value.answers;
}
node->type = T_ASSERT;
node->value.answers = new_answer;
}
}
static void
do_unassert (buf, limit, op)
U_CHAR *buf;
U_CHAR *limit;
FILE_BUF *op ATTRIBUTE_UNUSED;
{
HASHNODE *node;
struct answer *answer;
node = parse_assertion (buf, limit, &answer, T_UNASSERT);
if (node)
{
if (node->type == T_ASSERT)
{
if (answer)
{
struct answer **p = find_answer (node, answer), *temp;
temp = *p;
if (temp)
*p = temp->next;
if (node->value.answers == 0)
delete_macro (node);
}
else
delete_macro (node);
}
free (answer);
}
}
static struct answer **
find_answer (node, candidate)
HASHNODE *node;
const struct answer *candidate;
{
struct answer **result;
for (result = &node->value.answers; *result; result = &(*result)->next)
{
struct answer *answer = *result;
if (answer->len == candidate->len
&& !memcmp (answer->answer, candidate->answer, answer->len))
break;
}
return result;
}
static unsigned char *
canonicalize_text (buf, limit, climit)
const unsigned char *buf, *limit, **climit;
{
unsigned int len = limit - buf;
unsigned char *result = (unsigned char *) xmalloc (len), *dest;
for (dest = result; buf < limit;)
{
if (! is_space (*buf))
*dest++ = *buf++;
else
{
while (++buf < limit && is_space (*buf))
;
if (dest != result && buf != limit)
*dest++ = ' ';
}
}
*climit = dest;
return result;
}
static void
do_if (buf, limit, op)
U_CHAR *buf, *limit;
FILE_BUF *op ATTRIBUTE_UNUSED;
{
int value;
FILE_BUF *ip = &instack[indepth];
value = eval_if_expression (buf, limit - buf);
conditional_skip (ip, value == 0, T_IF);
}
static void
do_elif (buf, limit, op)
U_CHAR *buf, *limit;
FILE_BUF *op;
{
int value;
FILE_BUF *ip = &instack[indepth];
if (if_stack == instack[indepth].if_stack) {
error ("#elif not within a conditional");
return;
} else {
if (if_stack->type != T_IF && if_stack->type != T_ELIF) {
error ("#elif after #else");
fprintf (stderr, " (matches line %d", if_stack->lineno);
if (if_stack->fname != NULL && ip->fname != NULL &&
strcmp (if_stack->fname, ip->fname) != 0)
fprintf (stderr, ", file %s", if_stack->fname);
fprintf (stderr, ")\n");
}
if_stack->type = T_ELIF;
}
if (if_stack->if_succeeded)
skip_if_group (ip, 0);
else {
value = eval_if_expression (buf, limit - buf);
if (value == 0)
skip_if_group (ip, 0);
else {
++if_stack->if_succeeded;
output_line_command (ip, op, 1, same_file);
}
}
}
static int
eval_if_expression (buf, length)
const U_CHAR *buf;
int length;
{
FILE_BUF temp_obuf;
HASHNODE *save_defined;
int value;
save_defined = install (U"defined", -1, T_SPEC_DEFINED, -1);
temp_obuf = expand_to_temp_buffer (buf, buf + length, 0);
delete_macro (save_defined);
value = parse_c_expression ((const char *)temp_obuf.buf);
free (temp_obuf.buf);
return value;
}
static void
do_xifdef (buf, limit, type)
U_CHAR *buf, *limit;
enum node_type type;
{
int skip;
FILE_BUF *ip = &instack[indepth];
U_CHAR *end;
SKIP_WHITE_SPACE (buf);
while (limit != buf && is_nvspace (limit[-1])) limit--;
for (end = buf; is_idchar (*end); end++);
if (end == buf)
skip = (type == T_IFDEF);
else
skip = (lookup (buf, end-buf, -1) == NULL) ^ (type == T_IFNDEF);
conditional_skip (ip, skip, T_IF);
}
static void
do_ifdef (buf, limit, op)
U_CHAR *buf, *limit;
FILE_BUF *op ATTRIBUTE_UNUSED;
{
do_xifdef (buf, limit, T_IFDEF);
}
static void
do_ifndef (buf, limit, op)
U_CHAR *buf, *limit;
FILE_BUF *op ATTRIBUTE_UNUSED;
{
do_xifdef (buf, limit, T_IFNDEF);
}
static void
conditional_skip (ip, skip, type)
FILE_BUF *ip;
int skip;
enum node_type type;
{
IF_STACK_FRAME *temp;
temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME));
temp->fname = ip->fname;
temp->lineno = ip->lineno;
temp->next = if_stack;
if_stack = temp;
if_stack->type = type;
if (skip != 0) {
skip_if_group (ip, 0);
return;
} else {
++if_stack->if_succeeded;
output_line_command (ip, &outbuf, 1, same_file);
}
}
static void
skip_if_group (ip, any)
FILE_BUF *ip;
int any;
{
U_CHAR *bp = ip->bufp, *cp;
U_CHAR *endb = ip->buf + ip->length;
const struct directive *kt;
IF_STACK_FRAME *save_if_stack = if_stack;
U_CHAR *beg_of_line = bp;
while (bp < endb) {
switch (*bp++) {
case '/':
if (*bp == '\\' && bp[1] == '\n')
newline_fix (bp);
if (*bp == '*') {
ip->bufp = ++bp;
bp = skip_to_end_of_comment (ip, &ip->lineno);
}
break;
case '\"':
case '\'':
bp = skip_quoted_string (bp - 1, endb, ip->lineno, &ip->lineno, 0, 0);
break;
case '\\':
if (bp < endb) {
if (*bp == '\n')
++ip->lineno;
bp++;
}
break;
case '\n':
++ip->lineno;
beg_of_line = bp;
break;
case '#':
ip->bufp = bp - 1;
if (beg_of_line == 0)
break;
bp = beg_of_line;
while (1) {
if (is_nvspace (*bp))
bp++;
else if (*bp == '\\' && bp[1] == '\n')
bp += 2;
else if (*bp == '/' && bp[1] == '*') {
bp += 2;
while (!(*bp == '*' && bp[1] == '/')) {
if (*bp == '\n')
ip->lineno++;
bp++;
}
bp += 2;
}
else break;
}
if (bp != ip->bufp) {
bp = ip->bufp + 1;
break;
}
bp = ip->bufp + 1;
while (1) {
if (is_nvspace (*bp))
bp++;
else if (*bp == '\\' && bp[1] == '\n')
bp += 2;
else if (*bp == '/' && bp[1] == '*') {
bp += 2;
while (!(*bp == '*' && bp[1] == '/'))
bp++;
bp += 2;
}
else break;
}
cp = bp;
while (1) {
if (is_idchar (*bp))
bp++;
else {
if (*bp == '\\' && bp[1] == '\n')
name_newline_fix (bp);
if (is_idchar (*bp))
bp++;
else break;
}
}
for (kt = directive_table; kt->length >= 0; kt++) {
IF_STACK_FRAME *temp;
if (strncmp ((const char *)cp, kt->name, kt->length) == 0
&& !is_idchar (cp[kt->length])) {
if (any)
return;
switch (kt->type) {
case T_IF:
case T_IFDEF:
case T_IFNDEF:
temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME));
temp->next = if_stack;
if_stack = temp;
temp->lineno = ip->lineno;
temp->fname = ip->fname;
temp->type = kt->type;
break;
case T_ELSE:
case T_ENDIF:
case T_ELIF:
if (if_stack == instack[indepth].if_stack) {
error ("#%s not within a conditional", kt->name);
break;
}
else if (if_stack == save_if_stack)
return;
if (kt->type != T_ENDIF) {
if (if_stack->type == T_ELSE)
error ("#else or #elif after #else");
if_stack->type = kt->type;
break;
}
temp = if_stack;
if_stack = if_stack->next;
free (temp);
break;
default:
break;
}
break;
}
}
}
}
ip->bufp = bp;
}
static void
do_else (buf, limit, op)
U_CHAR *buf ATTRIBUTE_UNUSED;
U_CHAR *limit ATTRIBUTE_UNUSED;
FILE_BUF *op;
{
FILE_BUF *ip = &instack[indepth];
if (if_stack == instack[indepth].if_stack) {
error ("#else not within a conditional");
return;
} else {
if (if_stack->type != T_IF && if_stack->type != T_ELIF) {
error ("#else after #else");
fprintf (stderr, " (matches line %d", if_stack->lineno);
if (strcmp (if_stack->fname, ip->fname) != 0)
fprintf (stderr, ", file %s", if_stack->fname);
fprintf (stderr, ")\n");
}
if_stack->type = T_ELSE;
}
if (if_stack->if_succeeded)
skip_if_group (ip, 0);
else {
++if_stack->if_succeeded;
output_line_command (ip, op, 1, same_file);
}
}
static void
do_endif (buf, limit, op)
U_CHAR *buf ATTRIBUTE_UNUSED;
U_CHAR *limit ATTRIBUTE_UNUSED;
FILE_BUF *op;
{
if (if_stack == instack[indepth].if_stack)
error ("unbalanced #endif");
else {
IF_STACK_FRAME *temp = if_stack;
if_stack = if_stack->next;
free (temp);
output_line_command (&instack[indepth], op, 1, same_file);
}
}
static U_CHAR *
skip_to_end_of_comment (ip, line_counter)
FILE_BUF *ip;
int *line_counter;
{
U_CHAR *limit = ip->buf + ip->length;
U_CHAR *bp = ip->bufp;
FILE_BUF *op = &outbuf;
int output = put_out_comments && !line_counter;
if (output) {
*op->bufp++ = '/';
*op->bufp++ = '*';
}
while (bp < limit) {
if (output)
*op->bufp++ = *bp;
switch (*bp++) {
case '/':
if (warn_comments && bp < limit && *bp == '*')
warning("`/*' within comment");
break;
case '\n':
if (line_counter != NULL)
++*line_counter;
if (output)
++op->lineno;
break;
case '*':
if (*bp == '\\' && bp[1] == '\n')
newline_fix (bp);
if (*bp == '/') {
if (output)
*op->bufp++ = '/';
ip->bufp = ++bp;
return bp;
}
break;
}
}
ip->bufp = bp;
return bp;
}
static U_CHAR *
skip_quoted_string (bp, limit, start_line, count_newlines, backslash_newlines_p, eofp)
const U_CHAR *bp;
const U_CHAR *limit;
int start_line;
int *count_newlines;
int *backslash_newlines_p;
int *eofp;
{
U_CHAR c, match;
match = *bp++;
while (1) {
if (bp >= limit) {
error_with_line (line_for_error (start_line),
"unterminated string or character constant");
if (eofp)
*eofp = 1;
break;
}
c = *bp++;
if (c == '\\') {
while (*bp == '\\' && bp[1] == '\n') {
if (backslash_newlines_p)
*backslash_newlines_p = 1;
if (count_newlines)
++*count_newlines;
bp += 2;
}
if (*bp == '\n' && count_newlines) {
if (backslash_newlines_p)
*backslash_newlines_p = 1;
++*count_newlines;
}
bp++;
} else if (c == '\n') {
bp--;
if (eofp)
*eofp = 1;
break;
} else if (c == match)
break;
}
return (U_CHAR *) bp;
}
static void
output_line_command (ip, op, conditional, file_change)
FILE_BUF *ip, *op;
int conditional;
enum file_change_code file_change;
{
int len;
char line_cmd_buf[500];
if (no_line_commands
|| ip->fname == NULL
|| no_output) {
op->lineno = ip->lineno;
return;
}
if (conditional) {
if (ip->lineno == op->lineno)
return;
if (ip->lineno > op->lineno && ip->lineno < op->lineno + 8) {
check_expand (op, 10);
while (ip->lineno > op->lineno) {
*op->bufp++ = '\n';
op->lineno++;
}
return;
}
}
sprintf (line_cmd_buf, "# %d \"%s\"", ip->lineno, ip->fname);
if (file_change != same_file)
strcat (line_cmd_buf, file_change == enter_file ? " 1" : " 2");
if (system_include_depth > 0)
strcat (line_cmd_buf, " 3");
len = strlen (line_cmd_buf);
line_cmd_buf[len++] = '\n';
check_expand (op, len + 1);
if (op->bufp > op->buf && op->bufp[-1] != '\n')
*op->bufp++ = '\n';
memcpy (op->bufp, line_cmd_buf, len);
op->bufp += len;
op->lineno = ip->lineno;
}
static void
macroexpand (hp, op)
HASHNODE *hp;
FILE_BUF *op;
{
int nargs;
DEFINITION *defn = hp->value.defn;
U_CHAR *xbuf;
int xbuf_len;
int start_line = instack[indepth].lineno;
CHECK_DEPTH (return;);
if (hp->type != T_MACRO) {
special_symbol (hp, op);
return;
}
nargs = defn->nargs;
if (nargs >= 0) {
int i;
struct argdata *args;
const char *parse_error = 0;
args = (struct argdata *) alloca ((nargs + 1) * sizeof (struct argdata));
for (i = 0; i < nargs; i++) {
args[i].raw = args[i].expanded = (U_CHAR *) "";
args[i].raw_length = args[i].expand_length
= args[i].stringified_length = 0;
args[i].free1 = args[i].free2 = 0;
}
i = 0;
do {
++instack[indepth].bufp;
parse_error
= macarg ((i < nargs || (nargs == 0 && i == 0)) ? &args[i] : 0);
if (parse_error)
{
error_with_line (line_for_error (start_line), "%s", parse_error);
break;
}
i++;
} while (*instack[indepth].bufp != ')');
if (i == 1) {
const U_CHAR *bp = args[0].raw;
const U_CHAR *lim = bp + args[0].raw_length;
while (bp != lim && is_space (*bp)) bp++;
if (bp == lim)
i = 0;
}
if (nargs == 0 && i > 0)
error ("arguments given to macro `%s'", hp->name);
else if (i < nargs) {
if (nargs == 1 && i == 0)
;
else if (i == 0)
error ("no args to macro `%s'", hp->name);
else if (i == 1)
error ("only 1 arg to macro `%s'", hp->name);
else
error ("only %d args to macro `%s'", i, hp->name);
} else if (i > nargs)
error ("too many (%d) args to macro `%s'", i, hp->name);
++instack[indepth].bufp;
if (nargs == 0) {
xbuf = defn->expansion;
xbuf_len = defn->length;
} else {
U_CHAR *exp = defn->expansion;
int offset;
int totlen;
struct reflist *ap;
xbuf_len = defn->length;
for (ap = defn->pattern; ap != NULL; ap = ap->next) {
if (ap->stringify)
xbuf_len += args[ap->argno].stringified_length;
else
xbuf_len += args[ap->argno].raw_length;
}
xbuf = (U_CHAR *) xmalloc (xbuf_len + 1);
offset = totlen = 0;
for (ap = defn->pattern; ap != NULL; ap = ap->next) {
struct argdata *arg = &args[ap->argno];
for (i = 0; i < ap->nchars; i++)
xbuf[totlen++] = exp[offset++];
if (ap->stringify != 0) {
int arglen = arg->raw_length;
int escaped = 0;
int in_string = 0;
int c;
i = 0;
while (i < arglen
&& (c = arg->raw[i], is_space (c)))
i++;
while (i < arglen
&& (c = arg->raw[arglen - 1], is_space (c)))
arglen--;
for (; i < arglen; i++) {
c = arg->raw[i];
if (c == '\n' && arg->raw[i+1] != '\n') {
i++;
continue;
}
if (! in_string
&& (c == '\n' ? arg->raw[i+1] == '\n' : is_space (c))) {
while (1) {
if (c == '\n' && is_space (arg->raw[i+1]))
i += 2;
else if (c != '\n' && is_space (c))
i++;
else break;
c = arg->raw[i];
}
i--;
c = ' ';
}
if (escaped)
escaped = 0;
else {
if (c == '\\')
escaped = 1;
if (in_string) {
if (c == in_string)
in_string = 0;
} else if (c == '\"' || c == '\'')
in_string = c;
}
if (c == '\"' || (in_string && c == '\\'))
xbuf[totlen++] = '\\';
if (ISPRINT (c))
xbuf[totlen++] = c;
else {
sprintf ((char *) &xbuf[totlen], "\\%03o", (unsigned int) c);
totlen += 4;
}
}
} else {
const U_CHAR *p1 = arg->raw;
const U_CHAR *l1 = p1 + arg->raw_length;
if (ap->raw_before) {
while (p1 != l1 && is_space (*p1)) p1++;
while (p1 != l1 && is_idchar (*p1))
xbuf[totlen++] = *p1++;
if (p1[0] == '\n' && p1[1] == '-')
p1 += 2;
}
if (ap->raw_after) {
while (p1 != l1) {
if (is_space (l1[-1])) l1--;
else if (l1[-1] == '-') {
const U_CHAR *p2 = l1 - 1;
while (p2 != p1 && p2[-1] == '\n') p2--;
if ((l1 - 1 - p2) & 1) {
l1 -= 2;
}
else break;
}
else break;
}
}
memmove (xbuf + totlen, p1, l1 - p1);
totlen += l1 - p1;
}
if (totlen > xbuf_len)
abort ();
}
for (i = offset; i < defn->length; i++)
xbuf[totlen++] = exp[i];
xbuf[totlen] = 0;
xbuf_len = totlen;
for (i = 0; i < nargs; i++) {
if (args[i].free1 != 0)
free (args[i].free1);
if (args[i].free2 != 0)
free (args[i].free2);
}
}
} else {
xbuf = defn->expansion;
xbuf_len = defn->length;
}
{
FILE_BUF *ip2;
ip2 = &instack[++indepth];
ip2->fname = 0;
ip2->lineno = 0;
ip2->buf = xbuf;
ip2->length = xbuf_len;
ip2->bufp = xbuf;
ip2->free_ptr = (nargs > 0) ? xbuf : 0;
ip2->macro = hp;
ip2->if_stack = if_stack;
}
}
static const char *
macarg (argptr)
struct argdata *argptr;
{
FILE_BUF *ip = &instack[indepth];
int paren = 0;
int newlines = 0;
int comments = 0;
U_CHAR *bp = macarg1 (ip->bufp, ip->buf + ip->length,
&paren, &newlines, &comments);
if (!(ip->fname != 0 && (newlines != 0 || comments != 0))
&& bp != ip->buf + ip->length) {
if (argptr != 0) {
argptr->raw = ip->bufp;
argptr->raw_length = bp - ip->bufp;
}
ip->bufp = bp;
} else {
int bufsize = bp - ip->bufp;
int extra = newlines;
U_CHAR *buffer = (U_CHAR *) xmalloc (bufsize + extra + 1);
int final_start = 0;
memcpy (buffer, ip->bufp, bufsize);
ip->bufp = bp;
ip->lineno += newlines;
while (bp == ip->buf + ip->length) {
if (instack[indepth].macro == 0) {
free (buffer);
return "unterminated macro call";
}
ip->macro->type = T_MACRO;
if (ip->free_ptr)
free (ip->free_ptr);
ip = &instack[--indepth];
newlines = 0;
comments = 0;
bp = macarg1 (ip->bufp, ip->buf + ip->length, &paren,
&newlines, &comments);
final_start = bufsize;
bufsize += bp - ip->bufp;
extra += newlines;
buffer = (U_CHAR *) xrealloc (buffer, bufsize + extra + 1);
memcpy (buffer + bufsize - (bp - ip->bufp), ip->bufp, bp - ip->bufp);
ip->bufp = bp;
ip->lineno += newlines;
}
if (argptr != 0) {
argptr->raw = buffer;
argptr->raw_length = bufsize;
argptr->free1 = buffer;
argptr->newlines = newlines;
argptr->comments = comments;
if ((newlines || comments) && ip->fname != 0)
argptr->raw_length
= final_start +
discard_comments (argptr->raw + final_start,
argptr->raw_length - final_start,
newlines);
argptr->raw[argptr->raw_length] = 0;
if (argptr->raw_length > bufsize + extra)
abort ();
}
}
if (argptr != 0) {
FILE_BUF obuf;
const U_CHAR *buf, *lim;
int totlen;
obuf = expand_to_temp_buffer (argptr->raw,
argptr->raw + argptr->raw_length,
1);
argptr->expanded = obuf.buf;
argptr->expand_length = obuf.length;
argptr->free2 = obuf.buf;
buf = argptr->raw;
lim = buf + argptr->raw_length;
totlen = 0;
while (buf != lim) {
U_CHAR c = *buf++;
totlen++;
if (c == '\"' || c == '\\')
totlen++;
else if (!ISPRINT (c))
totlen += 3;
}
argptr->stringified_length = totlen;
}
return 0;
}
static U_CHAR *
macarg1 (start, limit, depthptr, newlines, comments)
U_CHAR *start;
const U_CHAR *limit;
int *depthptr, *newlines, *comments;
{
U_CHAR *bp = start;
while (bp < limit) {
switch (*bp) {
case '(':
(*depthptr)++;
break;
case ')':
if (--(*depthptr) < 0)
return bp;
break;
case '\\':
if (bp + 1 < limit)
{
bp++;
if (*bp == '\n')
++*newlines;
}
break;
case '\n':
++*newlines;
break;
case '/':
if (bp[1] == '\\' && bp[2] == '\n')
newline_fix (bp + 1);
if (bp[1] != '*' || bp + 1 >= limit)
break;
*comments = 1;
bp += 2;
while (bp + 1 < limit) {
if (bp[0] == '*'
&& bp[1] == '\\' && bp[2] == '\n')
newline_fix (bp + 1);
if (bp[0] == '*' && bp[1] == '/')
break;
if (*bp == '\n') ++*newlines;
bp++;
}
bp += 1;
break;
case '\'':
case '\"':
{
int quotec;
for (quotec = *bp++; bp + 1 < limit && *bp != quotec; bp++) {
if (*bp == '\\') {
bp++;
if (*bp == '\n')
++*newlines;
while (*bp == '\\' && bp[1] == '\n') {
bp += 2;
}
} else if (*bp == '\n') {
++*newlines;
if (quotec == '\'')
break;
}
}
}
break;
case ',':
if ((*depthptr) == 0)
return bp;
break;
}
bp++;
}
return bp;
}
static int
discard_comments (start, length, newlines)
U_CHAR *start;
int length;
int newlines;
{
U_CHAR *ibp;
U_CHAR *obp;
const U_CHAR *limit;
int c;
if (newlines > 0) {
ibp = start + length;
obp = ibp + newlines;
limit = start;
while (limit != ibp)
*--obp = *--ibp;
}
ibp = start + newlines;
limit = start + length + newlines;
obp = start;
while (ibp < limit) {
*obp++ = c = *ibp++;
switch (c) {
case '\n':
*obp++ = '\n';
break;
case '\\':
if (*ibp == '\n') {
obp--;
ibp++;
}
break;
case '/':
if (*ibp == '\\' && ibp[1] == '\n')
newline_fix (ibp);
if (ibp[0] != '*' || ibp + 1 >= limit)
break;
obp--;
ibp++;
while (ibp + 1 < limit) {
if (ibp[0] == '*'
&& ibp[1] == '\\' && ibp[2] == '\n')
newline_fix (ibp + 1);
if (ibp[0] == '*' && ibp[1] == '/')
break;
ibp++;
}
ibp += 2;
break;
case '\'':
case '\"':
{
int quotec = c;
while (ibp < limit) {
*obp++ = c = *ibp++;
if (c == quotec)
break;
if (c == '\n' && quotec == '\'')
break;
if (c == '\\' && ibp < limit) {
while (*ibp == '\\' && ibp[1] == '\n')
ibp += 2;
*obp++ = *ibp++;
}
}
}
break;
}
}
return obp - start;
}
static void
v_message (mtype, line, msgid, ap)
enum msgtype mtype;
int line;
const char *msgid;
va_list ap;
{
const char *fname = 0;
int i;
if (mtype == MT_WARNING && inhibit_warnings)
return;
for (i = indepth; i >= 0; i--)
if (instack[i].fname != NULL) {
if (line == 0)
line = instack[i].lineno;
fname = instack[i].fname;
break;
}
if (fname)
fprintf (stderr, "%s:%d: ", fname, line);
else
fprintf (stderr, "%s: ", progname);
if (mtype == MT_WARNING)
fputs (_("warning: "), stderr);
vfprintf (stderr, _(msgid), ap);
putc ('\n', stderr);
if (mtype == MT_ERROR)
errors++;
}
void
error VPARAMS ((const char *msgid, ...))
{
VA_OPEN(ap, msgid);
VA_FIXEDARG (ap, const char *, msgid);
v_message (MT_ERROR, 0, msgid, ap);
VA_CLOSE (ap);
}
void
error_with_line VPARAMS ((int line, const char *msgid, ...))
{
VA_OPEN(ap, msgid);
VA_FIXEDARG (ap, int, line);
VA_FIXEDARG (ap, const char *, msgid);
v_message (MT_ERROR, line, msgid, ap);
VA_CLOSE (ap);
}
void
error_from_errno (name)
const char *name;
{
error ("%s: %s", name, strerror (errno));
}
void
warning VPARAMS ((const char *msgid, ...))
{
VA_OPEN(ap, msgid);
VA_FIXEDARG (ap, const char *, msgid);
v_message (MT_WARNING, 0, msgid, ap);
VA_CLOSE (ap);
}
void
fatal VPARAMS ((const char *msgid, ...))
{
VA_OPEN(ap, msgid);
VA_FIXEDARG (ap, const char *, msgid);
v_message (MT_FATAL, 0, msgid, ap);
VA_CLOSE (ap);
exit (FATAL_EXIT_CODE);
}
void
fancy_abort (line, func)
int line;
const char *func;
{
fatal ("internal error in %s, at tradcpp.c:%d\n\
Please submit a full bug report.\n\
See %s for instructions.", func, line, GCCBUGURL);
}
void
perror_with_name (name)
const char *name;
{
fprintf (stderr, "%s: %s: %s\n", progname, name, strerror (errno));
errors++;
}
void
pfatal_with_name (name)
const char *name;
{
perror_with_name (name);
exit (FATAL_EXIT_CODE);
}
static int
line_for_error (line)
int line;
{
int i;
int line1 = line;
for (i = indepth; i >= 0; ) {
if (instack[i].fname != 0)
return line1;
i--;
if (i < 0)
return 0;
line1 = instack[i].lineno;
}
return 0;
}
static void
grow_outbuf (obuf, needed)
FILE_BUF *obuf;
int needed;
{
U_CHAR *p;
int minsize;
if (obuf->length - (obuf->bufp - obuf->buf) > needed)
return;
obuf->length *= 2;
minsize = (3 * needed) / 2 + (obuf->bufp - obuf->buf);
if (minsize > obuf->length)
obuf->length = minsize;
p = (U_CHAR *) xrealloc (obuf->buf, obuf->length);
obuf->bufp = p + (obuf->bufp - obuf->buf);
obuf->buf = p;
}
static HASHNODE *
install (name, len, type, hash)
const U_CHAR *name;
int len;
enum node_type type;
int hash;
{
HASHNODE *hp;
int bucket;
const U_CHAR *p;
U_CHAR *q;
if (len < 0) {
p = name;
while (is_idchar (*p))
p++;
len = p - name;
}
if (hash < 0)
hash = hashf (name, len, HASHSIZE);
hp = (HASHNODE *) xmalloc (sizeof (HASHNODE) + len + 1);
bucket = hash;
hp->bucket_hdr = &hashtab[bucket];
hp->next = hashtab[bucket];
hashtab[bucket] = hp;
hp->prev = NULL;
if (hp->next != NULL)
hp->next->prev = hp;
hp->type = type;
hp->length = len;
hp->name = q = ((U_CHAR *) hp) + sizeof (HASHNODE);
memcpy (q, name, len);
q[len] = 0;
return hp;
}
HASHNODE *
lookup (name, len, hash)
const U_CHAR *name;
int len;
int hash;
{
const U_CHAR *bp;
HASHNODE *bucket;
if (len < 0) {
for (bp = name; is_idchar (*bp); bp++) ;
len = bp - name;
}
if (hash < 0)
hash = hashf (name, len, HASHSIZE);
bucket = hashtab[hash];
while (bucket) {
if (bucket->length == len
&& strncmp ((const char *)bucket->name, (const char *)name, len) == 0)
return bucket;
bucket = bucket->next;
}
return NULL;
}
static void
delete_macro (hp)
HASHNODE *hp;
{
if (hp->prev != NULL)
hp->prev->next = hp->next;
if (hp->next != NULL)
hp->next->prev = hp->prev;
if (hp == *hp->bucket_hdr)
*hp->bucket_hdr = hp->next;
free (hp);
}
static int
hashf (name, len, hashsize)
const U_CHAR *name;
int len;
int hashsize;
{
int r = 0;
while (len--)
r = HASHSTEP (r, *name++);
return MAKE_POS (r) % hashsize;
}
static void
dump_all_macros ()
{
int bucket;
for (bucket = 0; bucket < HASHSIZE; bucket++) {
HASHNODE *hp;
for (hp = hashtab[bucket]; hp; hp= hp->next) {
if (hp->type == T_MACRO) {
DEFINITION *defn = hp->value.defn;
struct reflist *ap;
int offset;
int concat;
printf ("#define %s", hp->name);
if (defn->nargs >= 0) {
int i;
printf ("(");
for (i = 0; i < defn->nargs; i++) {
dump_arg_n (defn, i);
if (i + 1 < defn->nargs)
printf (", ");
}
printf (")");
}
printf (" ");
offset = 0;
concat = 0;
for (ap = defn->pattern; ap != NULL; ap = ap->next) {
dump_defn_1 (defn->expansion, offset, ap->nchars);
if (ap->nchars != 0)
concat = 0;
offset += ap->nchars;
if (ap->stringify)
printf (" #");
if (ap->raw_before && !concat)
printf (" ## ");
concat = 0;
dump_arg_n (defn, ap->argno);
if (ap->raw_after) {
printf (" ## ");
concat = 1;
}
}
dump_defn_1 (defn->expansion, offset, defn->length - offset);
printf ("\n");
}
}
}
}
static void
dump_defn_1 (base, start, length)
const U_CHAR *base;
int start;
int length;
{
const U_CHAR *p = base + start;
const U_CHAR *limit = base + start + length;
while (p < limit) {
if (*p != '\n')
putchar (*p);
else if (*p == '\"' || *p =='\'') {
const U_CHAR *p1 = skip_quoted_string (p, limit, 0, 0, 0, 0);
fwrite (p, p1 - p, 1, stdout);
p = p1 - 1;
}
p++;
}
}
static void
dump_arg_n (defn, argnum)
DEFINITION *defn;
int argnum;
{
const U_CHAR *p = defn->argnames;
while (argnum + 1 < defn->nargs) {
p = (const U_CHAR *) strchr ((const char *)p, ' ') + 1;
argnum++;
}
while (*p && *p != ',') {
putchar (*p);
p++;
}
}
#define DSC(x) U x, sizeof x - 1
#define install_spec(name, type) \
install(DSC(name), type, -1);
#define install_value(name, val) \
hp = install(DSC(name), T_CONST, -1); hp->value.cpval = val;
static void
initialize_builtins ()
{
HASHNODE *hp;
install_spec ("__BASE_FILE__", T_BASE_FILE);
install_spec ("__DATE__", T_DATE);
install_spec ("__FILE__", T_FILE);
install_spec ("__TIME__", T_TIME);
install_spec ("__VERSION__", T_VERSION);
install_spec ("__INCLUDE_LEVEL__", T_INCLUDE_LEVEL);
install_spec ("__LINE__", T_SPECLINE);
#ifndef NO_BUILTIN_SIZE_TYPE
install_value ("__SIZE_TYPE__", SIZE_TYPE);
#endif
#ifndef NO_BUILTIN_PTRDIFF_TYPE
install_value ("__PTRDIFF_TYPE__", PTRDIFF_TYPE);
#endif
#ifndef NO_BUILTIN_WCHAR_TYPE
install_value ("__WCHAR_TYPE__", WCHAR_TYPE);
#endif
#ifndef NO_BUILTIN_WINT_TYPE
install_value ("__WINT_TYPE__", WINT_TYPE);
#endif
install_value ("__REGISTER_PREFIX__", REGISTER_PREFIX);
install_value ("__USER_LABEL_PREFIX__", user_label_prefix);
if (flag_signed_char == 0)
install_value ("__CHAR_UNSIGNED__", "1");
}
#undef DSC
#undef install_spec
#undef install_value
static void
run_directive (str, len, type)
const char *str;
size_t len;
enum node_type type;
{
const struct directive *kt;
FILE_BUF *ip = &instack[++indepth];
ip->fname = "*command line*";
ip->buf = ip->bufp = (U_CHAR *) str;
ip->length = len;
ip->lineno = 1;
ip->macro = 0;
ip->free_ptr = 0;
ip->if_stack = if_stack;
for (kt = directive_table; kt->type != type; kt++)
;
(*kt->func) ((U_CHAR *) str, (U_CHAR *) str + len, NULL);
--indepth;
}
static void
make_definition (str)
const char *str;
{
char *buf, *p;
size_t count;
count = strlen (str);
buf = (char *) alloca (count + 2);
memcpy (buf, str, count);
p = strchr (str, '=');
if (p)
buf[p - str] = ' ';
else
{
buf[count++] = ' ';
buf[count++] = '1';
}
run_directive (buf, count, T_DEFINE);
}
static void
make_undef (str)
const char *str;
{
run_directive (str, strlen (str), T_UNDEF);
}
static void
make_assertion (str)
const char *str;
{
enum node_type type = T_ASSERT;
size_t count;
const char *p;
if (*str == '-')
{
str++;
type = T_UNASSERT;
}
count = strlen (str);
p = strchr (str, '=');
if (p)
{
char *buf = (char *) alloca (count + 1);
memcpy (buf, str, count);
buf[p - str] = '(';
buf[count++] = ')';
str = buf;
}
run_directive (str, count, type);
}
static int
file_size_and_mode (fd, mode_pointer, size_pointer)
int fd;
int *mode_pointer;
long *size_pointer;
{
struct stat sbuf;
if (fstat (fd, &sbuf) < 0) return -1;
if (mode_pointer) *mode_pointer = sbuf.st_mode;
if (size_pointer) *size_pointer = sbuf.st_size;
return 0;
}