From: jsg Date: Wed, 30 Jul 2014 16:33:11 +0000 (+0000) Subject: Add tradcpp 0.4, a standalone traditional whitespace preserving cpp X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=a9b3ff1afb41a291f9dcc0a73ee1d8bfbfc4f686;p=openbsd Add tradcpp 0.4, a standalone traditional whitespace preserving cpp by David A. Holland of NetBSD. --- diff --git a/libexec/tradcpp/Makefile b/libexec/tradcpp/Makefile new file mode 100644 index 00000000000..e15598edc5b --- /dev/null +++ b/libexec/tradcpp/Makefile @@ -0,0 +1,6 @@ +PROG= tradcpp +SRCS= main.c \ + files.c directive.c eval.c macro.c output.c \ + place.c array.c utils.c + +.include diff --git a/libexec/tradcpp/array.c b/libexec/tradcpp/array.c new file mode 100644 index 00000000000..09ffa45e8ff --- /dev/null +++ b/libexec/tradcpp/array.c @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#define ARRAYINLINE +#include "array.h" + +struct array * +array_create(void) +{ + struct array *a; + + a = domalloc(sizeof(*a)); + array_init(a); + return a; +} + +void +array_destroy(struct array *a) +{ + array_cleanup(a); + dofree(a, sizeof(*a)); +} + +void +array_init(struct array *a) +{ + a->num = a->max = 0; + a->v = NULL; +} + +void +array_cleanup(struct array *a) +{ + arrayassert(a->num == 0); + dofree(a->v, a->max * sizeof(a->v[0])); +#ifdef ARRAYS_CHECKED + a->v = NULL; +#endif +} + +void +array_setsize(struct array *a, unsigned num) +{ + unsigned newmax; + void **newptr; + + if (num > a->max) { + newmax = a->max; + while (num > newmax) { + newmax = newmax ? newmax*2 : 4; + } + newptr = dorealloc(a->v, a->max * sizeof(a->v[0]), + newmax * sizeof(a->v[0])); + a->v = newptr; + a->max = newmax; + } + a->num = num; +} + +void +array_insert(struct array *a, unsigned index_) +{ + unsigned movers; + + arrayassert(a->num <= a->max); + arrayassert(index_ < a->num); + + movers = a->num - index_; + + array_setsize(a, a->num + 1); + + memmove(a->v + index_+1, a->v + index_, movers*sizeof(*a->v)); +} + +void +array_remove(struct array *a, unsigned index_) +{ + unsigned movers; + + arrayassert(a->num <= a->max); + arrayassert(index_ < a->num); + + movers = a->num - (index_ + 1); + memmove(a->v + index_, a->v + index_+1, movers*sizeof(*a->v)); + a->num--; +} diff --git a/libexec/tradcpp/array.h b/libexec/tradcpp/array.h new file mode 100644 index 00000000000..8d1b98b3273 --- /dev/null +++ b/libexec/tradcpp/array.h @@ -0,0 +1,279 @@ +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ARRAY_H +#define ARRAY_H + +#include "inlinedefs.h" // XXX +#include "utils.h" + +#define ARRAYS_CHECKED + +#ifdef ARRAYS_CHECKED +#include +#define arrayassert assert +#else +#define arrayassert(x) ((void)(x)) +#endif + +#ifndef ARRAYINLINE +#define ARRAYINLINE C99INLINE +#endif + +//////////////////////////////////////////////////////////// +// type and base operations + +struct array { + void **v; + unsigned num, max; +}; + +struct array *array_create(void); +void array_destroy(struct array *); +void array_init(struct array *); +void array_cleanup(struct array *); +ARRAYINLINE unsigned array_num(const struct array *); +ARRAYINLINE void *array_get(const struct array *, unsigned index_); +ARRAYINLINE void array_set(const struct array *, unsigned index_, void *val); +void array_setsize(struct array *, unsigned num); +ARRAYINLINE void array_add(struct array *, void *val, unsigned *index_ret); +void array_insert(struct array *a, unsigned index_); +void array_remove(struct array *a, unsigned index_); + +//////////////////////////////////////////////////////////// +// inlining for base operations + +ARRAYINLINE unsigned +array_num(const struct array *a) +{ + return a->num; +} + +ARRAYINLINE void * +array_get(const struct array *a, unsigned index_) +{ + arrayassert(index_ < a->num); + return a->v[index_]; +} + +ARRAYINLINE void +array_set(const struct array *a, unsigned index_, void *val) +{ + arrayassert(index_ < a->num); + a->v[index_] = val; +} + +ARRAYINLINE void +array_add(struct array *a, void *val, unsigned *index_ret) +{ + unsigned index_ = a->num; + array_setsize(a, index_+1); + a->v[index_] = val; + if (index_ret != NULL) { + *index_ret = index_; + } +} + +//////////////////////////////////////////////////////////// +// bits for declaring and defining typed arrays + +/* + * Usage: + * + * DECLARRAY_BYTYPE(foo, bar, INLINE) declares "struct foo", which is + * an array of pointers to "bar", plus the operations on it. + * + * DECLARRAY(foo, INLINE) is equivalent to + * DECLARRAY_BYTYPE(fooarray, struct foo, INLINE). + * + * DEFARRAY_BYTYPE and DEFARRAY are the same as DECLARRAY except that + * they define the operations. + * + * The argument INLINE can be used as follows: + * + * 1. For no inlining: + * In foo.h: + * DECLARRAY(foo, ); + * In foo.c: + * DEFARRAY(foo, ); + * + * 2. To be file-static: + * In foo.c: + * DECLARRAY(foo, static); + * DEFARRAY(foo, static); + * + * 3. To inline using C99: + * In foo.h: + * DECLARRAY(foo, inline); + * DEFARRAY(foo, inline); + * + * 4. To inline with old gcc: + * In foo.h: + * #ifndef FOO_INLINE + * #define FOO_INLINE extern inline + * #endif + * DECLARRAY(foo, ); + * DEFARRAY(foo, FOO_INLINE); + * In foo.c: + * #define FOO_INLINE + * #include "foo.h" + * + * 5. To inline such that it works both with old gcc and C99: + * In foo.h: + * #ifndef FOO_INLINE + * #define FOO_INLINE extern inline + * #endif + * DECLARRAY(foo, FOO_INLINE); + * DEFARRAY(foo, FOO_INLINE); + * In foo.c: + * #define FOO_INLINE + * #include "foo.h" + * + * The mechanism in case (4) ensures that an externally linkable + * definition exists. + */ + +#define DECLARRAY_BYTYPE(ARRAY, T, INLINE) \ + struct ARRAY { \ + struct array arr; \ + }; \ + \ + INLINE struct ARRAY *ARRAY##_create(void); \ + INLINE void ARRAY##_destroy(struct ARRAY *a); \ + INLINE void ARRAY##_init(struct ARRAY *a); \ + INLINE void ARRAY##_cleanup(struct ARRAY *a); \ + INLINE unsigned ARRAY##_num(const struct ARRAY *a); \ + INLINE T *ARRAY##_get(const struct ARRAY *a, unsigned index_); \ + INLINE void ARRAY##_set(struct ARRAY *a, unsigned index_, T *val); \ + INLINE void ARRAY##_setsize(struct ARRAY *a, unsigned num); \ + INLINE void ARRAY##_add(struct ARRAY *a, T *val, unsigned *index_ret);\ + INLINE void ARRAY##_insert(struct ARRAY *a, unsigned index_); \ + INLINE void ARRAY##_remove(struct ARRAY *a, unsigned index_) + + +#define DEFARRAY_BYTYPE(ARRAY, T, INLINE) \ + INLINE void \ + ARRAY##_init(struct ARRAY *a) \ + { \ + array_init(&a->arr); \ + } \ + \ + INLINE void \ + ARRAY##_cleanup(struct ARRAY *a) \ + { \ + array_cleanup(&a->arr); \ + } \ + \ + INLINE struct \ + ARRAY *ARRAY##_create(void) \ + { \ + struct ARRAY *a; \ + \ + a = domalloc(sizeof(*a)); \ + ARRAY##_init(a); \ + return a; \ + } \ + \ + INLINE void \ + ARRAY##_destroy(struct ARRAY *a) \ + { \ + ARRAY##_cleanup(a); \ + dofree(a, sizeof(*a)); \ + } \ + \ + INLINE unsigned \ + ARRAY##_num(const struct ARRAY *a) \ + { \ + return array_num(&a->arr); \ + } \ + \ + INLINE T * \ + ARRAY##_get(const struct ARRAY *a, unsigned index_) \ + { \ + return (T *)array_get(&a->arr, index_); \ + } \ + \ + INLINE void \ + ARRAY##_set(struct ARRAY *a, unsigned index_, T *val) \ + { \ + array_set(&a->arr, index_, (void *)val); \ + } \ + \ + INLINE void \ + ARRAY##_setsize(struct ARRAY *a, unsigned num) \ + { \ + array_setsize(&a->arr, num); \ + } \ + \ + INLINE void \ + ARRAY##_add(struct ARRAY *a, T *val, unsigned *ret) \ + { \ + array_add(&a->arr, (void *)val, ret); \ + } \ + \ + INLINE void \ + ARRAY##_insert(struct ARRAY *a, unsigned index_) \ + { \ + array_insert(&a->arr, index_); \ + } \ + \ + INLINE void \ + ARRAY##_remove(struct ARRAY *a, unsigned index_) \ + { \ + array_remove(&a->arr, index_); \ + } + +#define DECLARRAY(T, INLINE) DECLARRAY_BYTYPE(T##array, struct T, INLINE) +#define DEFARRAY(T, INLINE) DEFARRAY_BYTYPE(T##array, struct T, INLINE) + +#define DESTROYALL_ARRAY(T, INLINE) \ + void T##array_destroyall(struct T##array *arr); \ + \ + INLINE void \ + T##array_destroyall(struct T##array *arr) \ + { \ + unsigned i, num; \ + struct T *t; \ + \ + num = T##array_num(arr); \ + for (i=0; i +#include +#include +#include + +#include "utils.h" +#include "mode.h" +#include "place.h" +#include "files.h" +#include "directive.h" +#include "macro.h" +#include "eval.h" +#include "output.h" + +struct ifstate { + struct ifstate *prev; + struct place startplace; + bool curtrue; + bool evertrue; + bool seenelse; +}; + +static struct ifstate *ifstate; + +//////////////////////////////////////////////////////////// +// common parsing bits + +static +void +uncomment(char *buf) +{ + char *s, *t, *u = NULL; + bool incomment = false; + bool inesc = false; + bool inquote = false; + char quote = '\0'; + + for (s = t = buf; *s; s++) { + if (incomment) { + if (s[0] == '*' && s[1] == '/') { + s++; + incomment = false; + } + } else { + if (!inquote && s[0] == '/' && s[1] == '*') { + incomment = true; + } else { + if (inesc) { + inesc = false; + } else if (s[0] == '\\') { + inesc = true; + } else if (!inquote && + (s[0] == '"' || s[0] == '\'')) { + inquote = true; + quote = s[0]; + } else if (inquote && s[0] == quote) { + inquote = false; + } + + if (t != s) { + *t = *s; + } + if (!strchr(ws, *t)) { + u = t; + } + t++; + } + } + } + if (u) { + /* end string after last non-whitespace char */ + u[1] = '\0'; + } else { + *t = '\0'; + } +} + +static +void +oneword(const char *what, struct place *p2, char *line) +{ + size_t pos; + + pos = strcspn(line, ws); + if (line[pos] != '\0') { + p2->column += pos; + complain(p2, "Garbage after %s argument", what); + complain_fail(); + line[pos] = '\0'; + } +} + +//////////////////////////////////////////////////////////// +// if handling + +static +struct ifstate * +ifstate_create(struct ifstate *prev, struct place *p, bool startstate) +{ + struct ifstate *is; + + is = domalloc(sizeof(*is)); + is->prev = prev; + if (p != NULL) { + is->startplace = *p; + } else { + place_setbuiltin(&is->startplace, 1); + } + is->curtrue = startstate; + is->evertrue = is->curtrue; + is->seenelse = false; + return is; +} + +static +void +ifstate_destroy(struct ifstate *is) +{ + dofree(is, sizeof(*is)); +} + +static +void +ifstate_push(struct place *p, bool startstate) +{ + struct ifstate *newstate; + + newstate = ifstate_create(ifstate, p, startstate); + if (!ifstate->curtrue) { + newstate->curtrue = false; + newstate->evertrue = true; + } + ifstate = newstate; +} + +static +void +ifstate_pop(void) +{ + struct ifstate *is; + + is = ifstate; + ifstate = ifstate->prev; + ifstate_destroy(is); +} + +static +void +d_if(struct place *p, struct place *p2, char *line) +{ + char *expr; + bool val; + struct place p3 = *p2; + size_t oldlen; + + expr = macroexpand(p2, line, strlen(line), true); + + oldlen = strlen(expr); + uncomment(expr); + /* trim to fit, so the malloc debugging won't complain */ + expr = dorealloc(expr, oldlen + 1, strlen(expr) + 1); + + if (ifstate->curtrue) { + val = eval(&p3, expr); + } else { + val = 0; + } + ifstate_push(p, val); + dostrfree(expr); +} + +static +void +d_ifdef(struct place *p, struct place *p2, char *line) +{ + uncomment(line); + oneword("#ifdef", p2, line); + ifstate_push(p, macro_isdefined(line)); +} + +static +void +d_ifndef(struct place *p, struct place *p2, char *line) +{ + uncomment(line); + oneword("#ifndef", p2, line); + ifstate_push(p, !macro_isdefined(line)); +} + +static +void +d_elif(struct place *p, struct place *p2, char *line) +{ + char *expr; + struct place p3 = *p2; + size_t oldlen; + + if (ifstate->seenelse) { + complain(p, "#elif after #else"); + complain_fail(); + } + + if (ifstate->evertrue) { + ifstate->curtrue = false; + } else { + expr = macroexpand(p2, line, strlen(line), true); + + oldlen = strlen(expr); + uncomment(expr); + /* trim to fit, so the malloc debugging won't complain */ + expr = dorealloc(expr, oldlen + 1, strlen(expr) + 1); + + ifstate->curtrue = eval(&p3, expr); + ifstate->evertrue = ifstate->curtrue; + dostrfree(expr); + } +} + +static +void +d_else(struct place *p, struct place *p2, char *line) +{ + (void)p2; + (void)line; + + if (ifstate->seenelse) { + complain(p, "Multiple #else directives in one conditional"); + complain_fail(); + } + + ifstate->curtrue = !ifstate->evertrue; + ifstate->evertrue = true; + ifstate->seenelse = true; +} + +static +void +d_endif(struct place *p, struct place *p2, char *line) +{ + (void)p2; + (void)line; + + if (ifstate->prev == NULL) { + complain(p, "Unmatched #endif"); + complain_fail(); + } else { + ifstate_pop(); + } +} + +//////////////////////////////////////////////////////////// +// macros + +static +void +d_define(struct place *p, struct place *p2, char *line) +{ + size_t pos, argpos; + struct place p3, p4; + + (void)p; + + /* + * line may be: + * macro expansion + * macro(arg, arg, ...) expansion + */ + + pos = strcspn(line, " \t\f\v("); + if (line[pos] == '(') { + line[pos++] = '\0'; + argpos = pos; + pos = pos + strcspn(line+pos, "()"); + if (line[pos] == '(') { + p2->column += pos; + complain(p2, "Left parenthesis in macro parameters"); + complain_fail(); + return; + } + if (line[pos] != ')') { + p2->column += pos; + complain(p2, "Unclosed macro parameter list"); + complain_fail(); + return; + } + line[pos++] = '\0'; +#if 0 + if (!strchr(ws, line[pos])) { + p2->column += pos; + complain(p2, "Trash after macro parameter list"); + complain_fail(); + return; + } +#endif + } else if (line[pos] == '\0') { + argpos = 0; + } else { + line[pos++] = '\0'; + argpos = 0; + } + + pos += strspn(line+pos, ws); + + p3 = *p2; + p3.column += argpos; + + p4 = *p2; + p4.column += pos; + + if (argpos) { + macro_define_params(p2, line, &p3, + line + argpos, &p4, + line + pos); + } else { + macro_define_plain(p2, line, &p4, line + pos); + } +} + +static +void +d_undef(struct place *p, struct place *p2, char *line) +{ + (void)p; + + uncomment(line); + oneword("#undef", p2, line); + macro_undef(line); +} + +//////////////////////////////////////////////////////////// +// includes + +static +bool +tryinclude(struct place *p, char *line) +{ + size_t len; + + len = strlen(line); + if (len > 2 && line[0] == '"' && line[len-1] == '"') { + line[len-1] = '\0'; + file_readquote(p, line+1); + line[len-1] = '"'; + return true; + } + if (len > 2 && line[0] == '<' && line[len-1] == '>') { + line[len-1] = '\0'; + file_readbracket(p, line+1); + line[len-1] = '>'; + return true; + } + return false; +} + +static +void +d_include(struct place *p, struct place *p2, char *line) +{ + char *text; + size_t oldlen; + + uncomment(line); + if (tryinclude(p, line)) { + return; + } + text = macroexpand(p2, line, strlen(line), false); + + oldlen = strlen(text); + uncomment(text); + /* trim to fit, so the malloc debugging won't complain */ + text = dorealloc(text, oldlen + 1, strlen(text) + 1); + + if (tryinclude(p, text)) { + dostrfree(text); + return; + } + complain(p, "Illegal #include directive"); + complain(p, "Before macro expansion: #include %s", line); + complain(p, "After macro expansion: #include %s", text); + dostrfree(text); + complain_fail(); +} + +static +void +d_line(struct place *p, struct place *p2, char *line) +{ + (void)p2; + (void)line; + + /* XXX */ + complain(p, "Sorry, no #line yet"); +} + +//////////////////////////////////////////////////////////// +// messages + +static +void +d_warning(struct place *p, struct place *p2, char *line) +{ + char *msg; + + msg = macroexpand(p2, line, strlen(line), false); + complain(p, "#warning: %s", msg); + if (mode.werror) { + complain_fail(); + } + dostrfree(msg); +} + +static +void +d_error(struct place *p, struct place *p2, char *line) +{ + char *msg; + + msg = macroexpand(p2, line, strlen(line), false); + complain(p, "#error: %s", msg); + complain_fail(); + dostrfree(msg); +} + +//////////////////////////////////////////////////////////// +// other + +static +void +d_pragma(struct place *p, struct place *p2, char *line) +{ + (void)p2; + + complain(p, "#pragma %s", line); + complain_fail(); +} + +//////////////////////////////////////////////////////////// +// directive table + +static const struct { + const char *name; + bool ifskip; + void (*func)(struct place *, struct place *, char *line); +} directives[] = { + { "define", true, d_define }, + { "elif", false, d_elif }, + { "else", false, d_else }, + { "endif", false, d_endif }, + { "error", true, d_error }, + { "if", false, d_if }, + { "ifdef", false, d_ifdef }, + { "ifndef", false, d_ifndef }, + { "include", true, d_include }, + { "line", true, d_line }, + { "pragma", true, d_pragma }, + { "undef", true, d_undef }, + { "warning", true, d_warning }, +}; +static const unsigned numdirectives = HOWMANY(directives); + +static +void +directive_gotdirective(struct place *p, char *line) +{ + struct place p2; + size_t len, skip; + unsigned i; + + p2 = *p; + for (i=0; icurtrue) { + return; + } + skip = len + strspn(line+len, ws); + p2.column += skip; + line += skip; + + len = strlen(line); + len = notrailingws(line, len); + if (len < strlen(line)) { + line[len] = '\0'; + } + directives[i].func(p, &p2, line); + return; + } + } + /* ugh. allow # by itself, including with a comment after it */ + uncomment(line); + if (line[0] == '\0') { + return; + } + + skip = strcspn(line, ws); + complain(p, "Unknown directive #%.*s", (int)skip, line); + complain_fail(); +} + +/* + * Check for nested comment delimiters in LINE. + */ +static +size_t +directive_scancomments(const struct place *p, char *line, size_t len) +{ + size_t pos; + bool incomment; + struct place p2; + + p2 = *p; + incomment = 0; + for (pos = 0; pos+1 < len; pos++) { + if (line[pos] == '/' && line[pos+1] == '*') { + if (incomment) { + complain(&p2, "Warning: %c%c within comment", + '/', '*'); + if (mode.werror) { + complain_failed(); + } + } else { + incomment = true; + } + pos++; + } else if (line[pos] == '*' && line[pos+1] == '/') { + if (incomment) { + incomment = false; + } else { + /* stray end-comment; should we care? */ + } + pos++; + } + if (line[pos] == '\n') { + p2.line++; + p2.column = 0; + } else { + p2.column++; + } + } + + /* multiline comments are supposed to arrive in a single buffer */ + assert(!incomment); + return len; +} + +void +directive_gotline(struct place *p, char *line, size_t len) +{ + size_t skip; + + if (warns.nestcomment) { + directive_scancomments(p, line, len); + } + + /* check if we have a directive line (# exactly in column 0) */ + if (line[0] == '#') { + skip = 1 + strspn(line + 1, ws); + assert(skip <= len); + p->column += skip; + assert(line[len] == '\0'); + directive_gotdirective(p, line+skip /*, length = len-skip */); + p->column += len-skip; + } else if (ifstate->curtrue) { + macro_sendline(p, line, len); + p->column += len; + } +} + +void +directive_goteof(struct place *p) +{ + while (ifstate->prev != NULL) { + complain(p, "Missing #endif"); + complain(&ifstate->startplace, "...opened at this point"); + complain_failed(); + ifstate_pop(); + } + macro_sendeof(p); +} + +//////////////////////////////////////////////////////////// +// module initialization + +void +directive_init(void) +{ + ifstate = ifstate_create(NULL, NULL, true); +} + +void +directive_cleanup(void) +{ + assert(ifstate->prev == NULL); + ifstate_destroy(ifstate); + ifstate = NULL; +} diff --git a/libexec/tradcpp/directive.h b/libexec/tradcpp/directive.h new file mode 100644 index 00000000000..d4d5676e95f --- /dev/null +++ b/libexec/tradcpp/directive.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +struct place; + +void directive_init(void); +void directive_cleanup(void); + +void directive_gotline(struct place *p, char *line, size_t len); +void directive_goteof(struct place *p); + diff --git a/libexec/tradcpp/eval.c b/libexec/tradcpp/eval.c new file mode 100644 index 00000000000..cb5db991364 --- /dev/null +++ b/libexec/tradcpp/eval.c @@ -0,0 +1,765 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +//#define DEBUG +#ifdef DEBUG +#include +#endif + +#include "utils.h" +#include "array.h" +#include "mode.h" +#include "place.h" +#include "eval.h" + +/* + * e ::= + * e1 ? e2 : e3 + * e1 || e2 + * e1 && e2 + * e1 | e2 + * e1 ^ e2 + * e1 & e2 + * e1 == e2 | e1 != e2 + * e1 < e2 | e1 <= e2 | e1 > e2 | e1 >= e2 + * e1 << e2 | e1 >> e2 + * e1 + e2 | e1 - e2 + * e1 * e2 | e1 / e2 | e1 % e2 + * !e | ~e | -e | +e + * ( e ) | ident + */ + +enum tokens { + T_EOF, /* end of input */ + T_VAL, /* value */ + T_LPAREN, /* parens */ + T_RPAREN, + T_PIPEPIPE, /* operators */ + T_AMPAMP, + T_EQEQ, + T_BANGEQ, + T_LTEQ, + T_GTEQ, + T_LTLT, + T_GTGT, + T_QUES, + T_COLON, + T_PIPE, + T_CARET, + T_AMP, + T_LT, + T_GT, + T_PLUS, + T_MINUS, + T_STAR, + T_SLASH, + T_PCT, + T_BANG, + T_TILDE, +}; + +static const struct { + char c1, c2; + enum tokens tok; +} tokens_2[] = { + { '|', '|', T_PIPEPIPE }, + { '&', '&', T_AMPAMP }, + { '=', '=', T_EQEQ }, + { '!', '=', T_BANGEQ }, + { '<', '=', T_LTEQ }, + { '>', '=', T_GTEQ }, + { '<', '<', T_LTLT }, + { '>', '>', T_GTGT }, +}; +static const unsigned num_tokens_2 = HOWMANY(tokens_2); + +static const struct { + char c1; + enum tokens tok; +} tokens_1[] = { + { '?', T_QUES }, + { ':', T_COLON }, + { '|', T_PIPE }, + { '^', T_CARET }, + { '&', T_AMP }, + { '<', T_LT }, + { '>', T_GT }, + { '+', T_PLUS }, + { '-', T_MINUS }, + { '*', T_STAR }, + { '/', T_SLASH }, + { '%', T_PCT }, + { '!', T_BANG }, + { '~', T_TILDE }, + { '(', T_LPAREN }, + { ')', T_RPAREN }, +}; +static const unsigned num_tokens_1 = HOWMANY(tokens_1); + +struct token { + struct place place; + enum tokens tok; + int val; +}; +DECLARRAY(token, static UNUSED); +DEFARRAY(token, static); + +static struct tokenarray tokens; + +static +struct token * +token_create(const struct place *p, enum tokens tok, int val) +{ + struct token *t; + + t = domalloc(sizeof(*t)); + t->place = *p; + t->tok = tok; + t->val = val; + return t; +} + +static +void +token_destroy(struct token *t) +{ + dofree(t, sizeof(*t)); +} + +DESTROYALL_ARRAY(token, ); + +#ifdef DEBUG +static +void +printtokens(void) +{ + unsigned i, num; + struct token *t; + + fprintf(stderr, "tokens:"); + num = tokenarray_num(&tokens); + for (i=0; itok) { + case T_EOF: fprintf(stderr, " "); break; + case T_VAL: fprintf(stderr, " %d", t->val); break; + case T_LPAREN: fprintf(stderr, " ("); break; + case T_RPAREN: fprintf(stderr, " )"); break; + case T_PIPEPIPE: fprintf(stderr, " ||"); break; + case T_AMPAMP: fprintf(stderr, " &&"); break; + case T_EQEQ: fprintf(stderr, " =="); break; + case T_BANGEQ: fprintf(stderr, " !="); break; + case T_LTEQ: fprintf(stderr, " <="); break; + case T_GTEQ: fprintf(stderr, " >="); break; + case T_LTLT: fprintf(stderr, " <<"); break; + case T_GTGT: fprintf(stderr, " >>"); break; + case T_QUES: fprintf(stderr, " ?"); break; + case T_COLON: fprintf(stderr, " :"); break; + case T_PIPE: fprintf(stderr, " |"); break; + case T_CARET: fprintf(stderr, " ^"); break; + case T_AMP: fprintf(stderr, " &"); break; + case T_LT: fprintf(stderr, " <"); break; + case T_GT: fprintf(stderr, " >"); break; + case T_PLUS: fprintf(stderr, " +"); break; + case T_MINUS: fprintf(stderr, " -"); break; + case T_STAR: fprintf(stderr, " *"); break; + case T_SLASH: fprintf(stderr, " /"); break; + case T_PCT: fprintf(stderr, " %%"); break; + case T_BANG: fprintf(stderr, " !"); break; + case T_TILDE: fprintf(stderr, " ~"); break; + } + } + fprintf(stderr, "\n"); +} +#endif + +static +bool +isuop(enum tokens tok) +{ + switch (tok) { + case T_BANG: + case T_TILDE: + case T_MINUS: + case T_PLUS: + return true; + default: + break; + } + return false; +} + +static +bool +isbop(enum tokens tok) +{ + switch (tok) { + case T_EOF: + case T_VAL: + case T_LPAREN: + case T_RPAREN: + case T_COLON: + case T_QUES: + case T_BANG: + case T_TILDE: + return false; + default: + break; + } + return true; +} + +static +bool +isop(enum tokens tok) +{ + switch (tok) { + case T_EOF: + case T_VAL: + case T_LPAREN: + case T_RPAREN: + return false; + default: + break; + } + return true; +} + +static +int +getprec(enum tokens tok) +{ + switch (tok) { + case T_BANG: case T_TILDE: return -1; + case T_STAR: case T_SLASH: case T_PCT: return 0; + case T_PLUS: case T_MINUS: return 1; + case T_LTLT: case T_GTGT: return 2; + case T_LT: case T_LTEQ: case T_GT: case T_GTEQ: return 3; + case T_EQEQ: case T_BANGEQ: return 4; + case T_AMP: return 5; + case T_CARET: return 6; + case T_PIPE: return 7; + case T_AMPAMP: return 8; + case T_PIPEPIPE: return 9; + default: break; + } + return 10; +} + +static +bool +looser(enum tokens t1, enum tokens t2) +{ + return getprec(t1) >= getprec(t2); +} + +static +int +eval_uop(enum tokens op, int val) +{ + switch (op) { + case T_BANG: val = !val; break; + case T_TILDE: val = (int)~(unsigned)val; break; + case T_MINUS: val = -val; break; + case T_PLUS: break; + default: assert(0); break; + } + return val; +} + +static +int +eval_bop(struct place *p, int lv, enum tokens op, int rv) +{ + unsigned mask; + + switch (op) { + case T_PIPEPIPE: return lv || rv; + case T_AMPAMP: return lv && rv; + case T_PIPE: return (int)((unsigned)lv | (unsigned)rv); + case T_CARET: return (int)((unsigned)lv ^ (unsigned)rv); + case T_AMP: return (int)((unsigned)lv & (unsigned)rv); + case T_EQEQ: return lv == rv; + case T_BANGEQ: return lv != rv; + case T_LT: return lv < rv; + case T_GT: return lv > rv; + case T_LTEQ: return lv <= rv; + case T_GTEQ: return lv >= rv; + + case T_LTLT: + case T_GTGT: + if (rv < 0) { + complain(p, "Negative bit-shift"); + complain_fail(); + rv = 0; + } + if ((unsigned)rv >= CHAR_BIT * sizeof(unsigned)) { + complain(p, "Bit-shift farther than type width"); + complain_fail(); + rv = 0; + } + if (op == T_LTLT) { + return (int)((unsigned)lv << (unsigned)rv); + } + mask = ((unsigned)-1) << (CHAR_BIT * sizeof(unsigned) - rv); + lv = (int)(((unsigned)lv >> (unsigned)rv) | mask); + return lv; + + case T_MINUS: + if (rv == INT_MIN) { + if (lv == INT_MIN) { + return 0; + } + lv--; + rv++; + } + rv = -rv; + /* FALLTHROUGH */ + case T_PLUS: + if (rv > 0 && lv > (INT_MAX - rv)) { + complain(p, "Integer overflow"); + complain_fail(); + return INT_MAX; + } + if (rv < 0 && lv < (INT_MIN - rv)) { + complain(p, "Integer underflow"); + complain_fail(); + return INT_MIN; + } + return lv + rv; + + case T_STAR: + if (rv == 0) { + return 0; + } + if (rv == 1) { + return lv; + } + if (rv == -1 && lv == INT_MIN) { + lv++; + lv = -lv; + if (lv == INT_MAX) { + complain(p, "Integer overflow"); + complain_fail(); + return INT_MAX; + } + lv++; + return lv; + } + if (lv == INT_MIN && rv < 0) { + complain(p, "Integer overflow"); + complain_fail(); + return INT_MAX; + } + if (lv == INT_MIN && rv > 0) { + complain(p, "Integer underflow"); + complain_fail(); + return INT_MIN; + } + if (rv < 0) { + rv = -rv; + lv = -lv; + } + if (lv > 0 && lv > INT_MAX / rv) { + complain(p, "Integer overflow"); + complain_fail(); + return INT_MAX; + } + if (lv < 0 && lv < INT_MIN / rv) { + complain(p, "Integer underflow"); + complain_fail(); + return INT_MIN; + } + return lv * rv; + + case T_SLASH: + if (rv == 0) { + complain(p, "Division by zero"); + complain_fail(); + return 0; + } + return lv / rv; + + case T_PCT: + if (rv == 0) { + complain(p, "Modulus by zero"); + complain_fail(); + return 0; + } + return lv % rv; + + default: assert(0); break; + } + return 0; +} + +static +void +tryreduce(void) +{ + unsigned num; + struct token *t1, *t2, *t3, *t4, *t5, *t6; + + while (1) { +#ifdef DEBUG + printtokens(); +#endif + num = tokenarray_num(&tokens); + t1 = (num >= 1) ? tokenarray_get(&tokens, num-1) : NULL; + t2 = (num >= 2) ? tokenarray_get(&tokens, num-2) : NULL; + t3 = (num >= 3) ? tokenarray_get(&tokens, num-3) : NULL; + + if (num >= 3 && + t3->tok == T_LPAREN && + t2->tok == T_VAL && + t1->tok == T_RPAREN) { + /* (x) -> x */ + t2->place = t3->place; + token_destroy(t1); + token_destroy(t3); + tokenarray_remove(&tokens, num-1); + tokenarray_remove(&tokens, num-3); + continue; + } + + if (num >= 2 && + (num == 2 || isop(t3->tok) || t3->tok == T_LPAREN) && + isuop(t2->tok) && + t1->tok == T_VAL) { + /* unary operator */ + t1->val = eval_uop(t2->tok, t1->val); + t1->place = t2->place; + token_destroy(t2); + tokenarray_remove(&tokens, num-2); + continue; + } + if (num >= 2 && + (num == 2 || isop(t3->tok) || t3->tok == T_LPAREN) && + t2->tok != T_LPAREN && t2->tok != T_VAL && + t1->tok == T_VAL) { + complain(&t2->place, "Invalid unary operator"); + complain_fail(); + token_destroy(t2); + tokenarray_remove(&tokens, num-2); + continue; + } + + + t4 = (num >= 4) ? tokenarray_get(&tokens, num-4) : NULL; + + if (num >= 4 && + t4->tok == T_VAL && + isbop(t3->tok) && + t2->tok == T_VAL) { + /* binary operator */ + if (looser(t1->tok, t3->tok)) { + t4->val = eval_bop(&t3->place, + t4->val, t3->tok, t2->val); + token_destroy(t2); + token_destroy(t3); + tokenarray_remove(&tokens, num-2); + tokenarray_remove(&tokens, num-3); + continue; + } + break; + } + + t5 = (num >= 5) ? tokenarray_get(&tokens, num-5) : NULL; + t6 = (num >= 6) ? tokenarray_get(&tokens, num-6) : NULL; + + if (num >= 6 && + t6->tok == T_VAL && + t5->tok == T_QUES && + t4->tok == T_VAL && + t3->tok == T_COLON && + t2->tok == T_VAL && + !isop(t1->tok)) { + /* conditional expression */ + t6->val = t6->val ? t4->val : t2->val; + token_destroy(t2); + token_destroy(t3); + token_destroy(t4); + token_destroy(t5); + tokenarray_remove(&tokens, num-2); + tokenarray_remove(&tokens, num-3); + tokenarray_remove(&tokens, num-4); + tokenarray_remove(&tokens, num-5); + continue; + } + + if (num >= 2 && + t2->tok == T_LPAREN && + t1->tok == T_RPAREN) { + complain(&t1->place, "Value expected within ()"); + complain_fail(); + t1->tok = T_VAL; + t1->val = 0; + token_destroy(t1); + tokenarray_remove(&tokens, num-1); + continue; + } + + if (num >= 2 && + t2->tok == T_VAL && + t1->tok == T_VAL) { + complain(&t1->place, "Operator expected"); + complain_fail(); + token_destroy(t1); + tokenarray_remove(&tokens, num-1); + continue; + } + + if (num >= 2 && + isop(t2->tok) && + t1->tok == T_EOF) { + complain(&t1->place, "Value expected after operator"); + complain_fail(); + token_destroy(t2); + tokenarray_remove(&tokens, num-2); + continue; + } + + if (num == 2 && + t2->tok == T_VAL && + t1->tok == T_RPAREN) { + complain(&t1->place, "Excess right parenthesis"); + complain_fail(); + token_destroy(t1); + tokenarray_remove(&tokens, num-1); + continue; + } + + if (num == 3 && + t3->tok == T_LPAREN && + t2->tok == T_VAL && + t1->tok == T_EOF) { + complain(&t1->place, "Unclosed left parenthesis"); + complain_fail(); + token_destroy(t3); + tokenarray_remove(&tokens, num-3); + continue; + } + + if (num == 2 && + t2->tok == T_VAL && + t1->tok == T_EOF) { + /* accepting state */ + break; + } + + if (num >= 1 && + t1->tok == T_EOF) { + /* any other configuration at eof is an error */ + complain(&t1->place, "Parse error"); + complain_fail(); + break; + } + + /* otherwise, wait for more input */ + break; + } +} + +static +void +token(struct place *p, enum tokens tok, int val) +{ + struct token *t; + + t = token_create(p, tok, val); + + tokenarray_add(&tokens, t, NULL); + tryreduce(); +} + +static +int +wordval(struct place *p, char *word) +{ + unsigned long val; + char *t; + + if (word[0] >= '0' && word[0] <= '9') { + errno = 0; + val = strtoul(word, &t, 0); + if (errno) { + complain(p, "Invalid integer constant"); + complain_fail(); + return 0; + } + while (*t == 'U' || *t == 'L') { + t++; + } + if (*t != '\0') { + complain(p, "Trailing garbage after integer constant"); + complain_fail(); + return 0; + } + if (val > INT_MAX) { + complain(p, "Integer constant too large"); + complain_fail(); + return INT_MAX; + } + return val; + } + + /* if it's a symbol, warn and substitute 0. */ + if (warns.undef) { + complain(p, "Warning: value of undefined symbol %s is 0", + word); + if (mode.werror) { + complain_fail(); + } + } + return 0; +} + +static +bool +check_word(struct place *p, char *expr, size_t pos, size_t *len_ret) +{ + size_t len; + int val; + char tmp; + + if (!strchr(alnum, expr[pos])) { + return false; + } + len = strspn(expr + pos, alnum); + tmp = expr[pos + len]; + expr[pos + len] = '\0'; + val = wordval(p, expr + pos); + expr[pos + len] = tmp; + token(p, T_VAL, val); + *len_ret = len; + return true; +} + +static +bool +check_tokens_2(struct place *p, char *expr, size_t pos) +{ + unsigned i; + + for (i=0; icolumn += len; + /* trailing whitespace is supposed to have been pruned */ + assert(expr[pos] != '\0'); + if (check_word(p, expr, pos, &len)) { + pos += len; + p->column += len; + continue; + } + if (check_tokens_2(p, expr, pos)) { + pos += 2; + p->column += 2; + continue; + } + if (check_tokens_1(p, expr, pos)) { + pos++; + p->column++; + continue; + } + complain(p, "Invalid character %u in #if-expression", + (unsigned char)expr[pos]); + complain_fail(); + pos++; + p->column++; + } + token(p, T_EOF, 0); +} + +bool +eval(struct place *p, char *expr) +{ + struct token *t1, *t2; + unsigned num; + bool result; + +#ifdef DEBUG + fprintf(stderr, "eval: %s\n", expr); +#endif + + tokenarray_init(&tokens); + tokenize(p, expr); + + result = false; + num = tokenarray_num(&tokens); + if (num == 2) { + t1 = tokenarray_get(&tokens, num-1); + t2 = tokenarray_get(&tokens, num-2); + if (t2->tok == T_VAL && + t1->tok == T_EOF) { + result = t2->val != 0; + } + } + + tokenarray_destroyall(&tokens); + tokenarray_cleanup(&tokens); + return result; +} diff --git a/libexec/tradcpp/eval.h b/libexec/tradcpp/eval.h new file mode 100644 index 00000000000..0694fb4f9f7 --- /dev/null +++ b/libexec/tradcpp/eval.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +bool eval(struct place *p, char *expr); diff --git a/libexec/tradcpp/files.c b/libexec/tradcpp/files.c new file mode 100644 index 00000000000..f0ee45eee55 --- /dev/null +++ b/libexec/tradcpp/files.c @@ -0,0 +1,420 @@ +/*- + * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "array.h" +#include "mode.h" +#include "place.h" +#include "files.h" +#include "directive.h" + +struct incdir { + const char *name; + bool issystem; +}; + +DECLARRAY(incdir, static UNUSED); +DEFARRAY(incdir, static); + +static struct incdirarray quotepath, bracketpath; + +//////////////////////////////////////////////////////////// +// management + +static +struct incdir * +incdir_create(const char *name, bool issystem) +{ + struct incdir *id; + + id = domalloc(sizeof(*id)); + id->name = name; + id->issystem = issystem; + return id; +} + +static +void +incdir_destroy(struct incdir *id) +{ + dofree(id, sizeof(*id)); +} + +void +files_init(void) +{ + incdirarray_init("epath); + incdirarray_init(&bracketpath); +} + +DESTROYALL_ARRAY(incdir, ); + +void +files_cleanup(void) +{ + incdirarray_destroyall("epath); + incdirarray_cleanup("epath); + incdirarray_destroyall(&bracketpath); + incdirarray_cleanup(&bracketpath); +} + +//////////////////////////////////////////////////////////// +// path setup + +void +files_addquotepath(const char *dir, bool issystem) +{ + struct incdir *id; + + id = incdir_create(dir, issystem); + incdirarray_add("epath, id, NULL); +} + +void +files_addbracketpath(const char *dir, bool issystem) +{ + struct incdir *id; + + id = incdir_create(dir, issystem); + incdirarray_add(&bracketpath, id, NULL); +} + +//////////////////////////////////////////////////////////// +// parsing + +/* + * Find the end of the logical line. End of line characters that are + * commented out do not count. + */ +static +size_t +findeol(const char *buf, size_t start, size_t limit) +{ + size_t i; + int incomment = 0; + bool inquote = false; + char quote = '\0'; + + for (i=start; i= bufend) { + /* do not have a whole line in the buffer; read more */ + assert(bufend >= linestart); + if (linestart > 0 && bufend > linestart) { + /* slide to beginning of buffer */ + memmove(buf, buf+linestart, bufend-linestart); + bufend -= linestart; + lineend -= linestart; + linestart = 0; + } + if (bufend >= bufmax) { + /* need bigger buffer */ + buf = dorealloc(buf, bufmax, bufmax*2); + bufmax = bufmax*2; + } + + if (ateof) { + /* don't read again, in case it's a socket */ + result = 0; + } else { + result = read(fd, buf+bufend, bufmax - bufend); + } + + if (result == -1) { + /* read error */ + complain(NULL, "%s: %s", + name, strerror(errno)); + complain_fail(); + } else if (result == 0 && bufend == linestart) { + /* eof */ + ateof = true; + break; + } else if (result == 0) { + /* eof in middle of line */ + ateof = true; + ptmp = linestartplace; + ptmp.column += bufend - linestart; + complain(&ptmp, "No newline at end of file"); + if (mode.werror) { + complain_fail(); + } + assert(bufend < bufmax); + lineend = bufend++; + buf[lineend] = '\n'; + } else { + bufend += (size_t)result; + lineend = findeol(buf, linestart, bufend); + } + /* loop in case we still don't have a whole line */ + continue; + } + + /* have a line */ + assert(buf[lineend] == '\n'); + buf[lineend] = '\0'; + nextlinestart = lineend+1; + nextlinestartplace.line++; + + /* check for CR/NL */ + if (lineend > 0 && buf[lineend-1] == '\r') { + buf[lineend-1] = '\0'; + lineend--; + } + + /* check for continuation line */ + if (lineend > 0 && buf[lineend-1]=='\\') { + lineend--; + tmp = nextlinestart - lineend; + if (bufend > nextlinestart) { + memmove(buf+lineend, buf+nextlinestart, + bufend - nextlinestart); + } + bufend -= tmp; + nextlinestart -= tmp; + lineend = findeol(buf, linestart, bufend); + /* might not have a whole line, so loop */ + continue; + } + + /* line now goes from linestart to lineend */ + assert(buf[lineend] == '\0'); + + /* count how many commented-out newlines we swallowed */ + nextlinestartplace.line += countnls(buf, linestart, lineend); + + /* if the line isn't empty, process it */ + if (lineend > linestart) { + directive_gotline(&linestartplace, + buf+linestart, lineend-linestart); + } + + linestart = nextlinestart; + lineend = findeol(buf, linestart, bufend); + linestartplace = nextlinestartplace; + } + + if (toplevel) { + directive_goteof(&linestartplace); + } + dofree(buf, bufmax); +} + +//////////////////////////////////////////////////////////// +// path search + +static +char * +mkfilename(struct place *place, const char *dir, const char *file) +{ + size_t dlen, flen, rlen; + char *ret; + bool needslash = false; + + if (dir == NULL) { + dir = place_getparsedir(place); + } + + dlen = strlen(dir); + flen = strlen(file); + if (dlen > 0 && dir[dlen-1] != '/') { + needslash = true; + } + + rlen = dlen + (needslash ? 1 : 0) + flen; + ret = domalloc(rlen + 1); + strcpy(ret, dir); + if (needslash) { + strcat(ret, "/"); + } + strcat(ret, file); + return ret; +} + +static +int +file_tryopen(const char *file) +{ + int fd; + + /* XXX check for non-regular files */ + + fd = open(file, O_RDONLY); + if (fd < 0) { + if (errno != ENOENT && errno != ENOTDIR) { + complain(NULL, "%s: %s", file, strerror(errno)); + } + return -1; + } + + return fd; +} + +static +void +file_search(struct place *place, struct incdirarray *path, const char *name) +{ + unsigned i, num; + struct incdir *id; + const struct placefile *pf; + char *file; + int fd; + + assert(place != NULL); + + if (name[0] == '/') { + fd = file_tryopen(name); + if (fd >= 0) { + pf = place_addfile(place, name, true); + file_read(pf, fd, name, false); + close(fd); + return; + } + } else { + num = incdirarray_num(path); + for (i=0; iname, name); + fd = file_tryopen(file); + if (fd >= 0) { + pf = place_addfile(place, file, id->issystem); + file_read(pf, fd, file, false); + dostrfree(file); + close(fd); + return; + } + dostrfree(file); + } + } + complain(place, "Include file %s not found", name); + complain_fail(); +} + +void +file_readquote(struct place *place, const char *name) +{ + file_search(place, "epath, name); +} + +void +file_readbracket(struct place *place, const char *name) +{ + file_search(place, &bracketpath, name); +} + +void +file_readabsolute(struct place *place, const char *name) +{ + const struct placefile *pf; + int fd; + + assert(place != NULL); + + if (name == NULL) { + fd = STDIN_FILENO; + pf = place_addfile(place, "", false); + } else { + fd = file_tryopen(name); + if (fd < 0) { + complain(NULL, "%s: %s", name, strerror(errno)); + die(); + } + pf = place_addfile(place, name, false); + } + + file_read(pf, fd, name, true); + + if (name != NULL) { + close(fd); + } +} diff --git a/libexec/tradcpp/files.h b/libexec/tradcpp/files.h new file mode 100644 index 00000000000..97b459d7446 --- /dev/null +++ b/libexec/tradcpp/files.h @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +struct place; + +void files_init(void); +void files_cleanup(void); + +void files_addquotepath(const char *dir, bool issystem); +void files_addbracketpath(const char *dir, bool issystem); + +void file_readquote(struct place *, const char *name); +void file_readbracket(struct place *, const char *name); +void file_readabsolute(struct place *, const char *name); diff --git a/libexec/tradcpp/inlinedefs.h b/libexec/tradcpp/inlinedefs.h new file mode 100644 index 00000000000..bfcef62f02d --- /dev/null +++ b/libexec/tradcpp/inlinedefs.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2009 David A. Holland. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Author nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(__GNUC__) && !defined(__GNUC_STDC_INLINE__) +/* gcc's non-C99 inline semantics */ +#define C99INLINE extern inline +#elif defined(__STDC__) && __STDC_VERSION__ >= 199901L +/* C99 */ +#define C99INLINE inline +#else +/* something else; static inline is safest */ +#define C99INLINE static inline +#endif diff --git a/libexec/tradcpp/macro.c b/libexec/tradcpp/macro.c new file mode 100644 index 00000000000..21059801333 --- /dev/null +++ b/libexec/tradcpp/macro.c @@ -0,0 +1,1202 @@ +/*- + * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "array.h" +#include "mode.h" +#include "place.h" +#include "macro.h" +#include "output.h" + +struct expansionitem { + bool isstring; + union { + char *string; + unsigned param; + }; +}; +DECLARRAY(expansionitem, static UNUSED); +DEFARRAY(expansionitem, static); + +struct macro { + struct place defplace; + struct place expansionplace; + unsigned hash; + char *name; + bool hasparams; + struct stringarray params; + struct expansionitemarray expansion; + bool inuse; +}; +DECLARRAY(macro, static UNUSED); +DEFARRAY(macro, static); +DECLARRAY(macroarray, static UNUSED); +DEFARRAY(macroarray, static); + +static struct macroarrayarray macros; +static unsigned total_macros; +static unsigned hashmask; + +//////////////////////////////////////////////////////////// +// macro structure ops + +static +struct expansionitem * +expansionitem_create_string(const char *string) +{ + struct expansionitem *ei; + + ei = domalloc(sizeof(*ei)); + ei->isstring = true; + ei->string = dostrdup(string); + return ei; +} + +static +struct expansionitem * +expansionitem_create_stringlen(const char *string, size_t len) +{ + struct expansionitem *ei; + + ei = domalloc(sizeof(*ei)); + ei->isstring = true; + ei->string = dostrndup(string, len); + return ei; +} + +static +struct expansionitem * +expansionitem_create_param(unsigned param) +{ + struct expansionitem *ei; + + ei = domalloc(sizeof(*ei)); + ei->isstring = false; + ei->param = param; + return ei; +} + +static +void +expansionitem_destroy(struct expansionitem *ei) +{ + if (ei->isstring) { + dostrfree(ei->string); + } + dofree(ei, sizeof(*ei)); +} + +static +bool +expansionitem_eq(const struct expansionitem *ei1, + const struct expansionitem *ei2) +{ + if (ei1->isstring != ei2->isstring) { + return false; + } + if (ei1->isstring) { + if (strcmp(ei1->string, ei2->string) != 0) { + return false; + } + } else { + if (ei1->param != ei2->param) { + return false; + } + } + return true; +} + +static +struct macro * +macro_create(struct place *p1, const char *name, unsigned hash, + struct place *p2) +{ + struct macro *m; + + m = domalloc(sizeof(*m)); + m->defplace = *p1; + m->expansionplace = *p2; + m->hash = hash; + m->name = dostrdup(name); + m->hasparams = false; + stringarray_init(&m->params); + expansionitemarray_init(&m->expansion); + m->inuse = false; + return m; +} + +DESTROYALL_ARRAY(expansionitem, ); + +static +void +macro_destroy(struct macro *m) +{ + expansionitemarray_destroyall(&m->expansion); + expansionitemarray_cleanup(&m->expansion); + dostrfree(m->name); + dofree(m, sizeof(*m)); +} + +static +bool +macro_eq(const struct macro *m1, const struct macro *m2) +{ + unsigned num1, num2, i; + struct expansionitem *ei1, *ei2; + const char *p1, *p2; + + if (strcmp(m1->name, m2->name) != 0) { + return false; + } + + if (m1->hasparams != m2->hasparams) { + return false; + } + + num1 = expansionitemarray_num(&m1->expansion); + num2 = expansionitemarray_num(&m2->expansion); + if (num1 != num2) { + return false; + } + + for (i=0; iexpansion, i); + ei2 = expansionitemarray_get(&m2->expansion, i); + if (!expansionitem_eq(ei1, ei2)) { + return false; + } + } + + num1 = stringarray_num(&m1->params); + num2 = stringarray_num(&m2->params); + if (num1 != num2) { + return false; + } + + for (i=0; iparams, i); + p2 = stringarray_get(&m2->params, i); + if (strcmp(p1, p2) != 0) { + return false; + } + } + return true; +} + +//////////////////////////////////////////////////////////// +// macro table + +/* + * Unless I've screwed up, this is something called Fletcher's Checksum + * that showed up in Dr. Dobbs in, according to my notes, May 1992. The + * implementation is new. + */ +static +unsigned +hashfunc(const char *s, size_t len) +{ + uint16_t x1, x2, a; + size_t i; + + x1 = (uint16_t) (len >> 16); + x2 = (uint16_t) (len); + if (x1==0) { + x1++; + } + if (x2==0) { + x2++; + } + + for (i=0; ihash) { + continue; + } + mlen = strlen(m->name); + if (len == mlen && !memcmp(name, m->name, len)) { + if (remove) { + if (i < num-1) { + m2 = macroarray_get(bucket, num-1); + macroarray_set(bucket, i, m2); + } + macroarray_setsize(bucket, num-1); + total_macros--; + } + return m; + } + } + return NULL; +} + +static +struct macro * +macrotable_find(const char *name, bool remove) +{ + return macrotable_findlen(name, strlen(name), remove); +} + +static +void +macrotable_rehash(void) +{ + struct macroarray *newbucket, *oldbucket; + struct macro *m; + unsigned newmask, tossbit; + unsigned numbuckets, i; + unsigned oldnum, j, k; + + numbuckets = macroarrayarray_num(¯os); + macroarrayarray_setsize(¯os, numbuckets*2); + + assert(hashmask == numbuckets - 1); + newmask = (hashmask << 1) | 1U; + tossbit = newmask & ~hashmask; + hashmask = newmask; + + for (i=0; ihash & tossbit) { + if (newbucket == NULL) { + newbucket = macroarray_create(); + } + macroarray_set(oldbucket, j, NULL); + macroarray_add(newbucket, m, NULL); + } + } + for (j=k=0; j 0 && total_macros / numbuckets > 9) { + macrotable_rehash(); + } + + hash = hashfunc(m->name, strlen(m->name)); + bucket = macroarrayarray_get(¯os, hash & hashmask); + if (bucket == NULL) { + bucket = macroarray_create(); + macroarrayarray_set(¯os, hash & hashmask, bucket); + } + macroarray_add(bucket, m, NULL); + total_macros++; +} + +//////////////////////////////////////////////////////////// +// external macro definition interface + +static +struct macro * +macro_define_common_start(struct place *p1, const char *macro, + struct place *p2) +{ + struct macro *m; + unsigned hash; + + if (!is_identifier(macro)) { + complain(p1, "Invalid macro name %s", macro); + complain_fail(); + } + + hash = hashfunc(macro, strlen(macro)); + m = macro_create(p1, macro, hash, p2); + return m; +} + +static +void +macro_define_common_end(struct macro *m) +{ + struct macro *oldm; + bool ok; + + oldm = macrotable_find(m->name, false); + if (oldm != NULL) { + ok = macro_eq(m, oldm); + if (ok) { + /* in traditional cpp this is silent */ + //complain(&m->defplace, + // "Warning: redefinition of %s", m->name); + //complain(&oldm->defplace, + // "Previous definition was here"); + //if (mode.werror) { + // complain_fail(); + //} + } else { + complain(&m->defplace, + "Warning: non-identical redefinition of %s", + m->name); + complain(&oldm->defplace, + "Previous definition was here"); + /* in traditional cpp this is not fatal */ + if (mode.werror) { + complain_fail(); + } + } + macro_destroy(m); + return; + } + macrotable_add(m); +} + +static +void +macro_parse_parameters(struct macro *m, struct place *p, const char *params) +{ + size_t len; + const char *s; + char *param; + + while (params != NULL) { + len = strspn(params, ws); + params += len; + p->column += len; + s = strchr(params, ','); + if (s) { + len = s-params; + param = dostrndup(params, len); + s++; + } else { + len = strlen(params); + param = dostrndup(params, len); + } + notrailingws(param, strlen(param)); + if (!is_identifier(param)) { + complain(p, "Invalid macro parameter name %s", param); + complain_fail(); + } else { + stringarray_add(&m->params, param, NULL); + } + params = s; + p->column += len; + } +} + +static +bool +isparam(struct macro *m, const char *name, size_t len, unsigned *num_ret) +{ + unsigned num, i; + const char *param; + + num = stringarray_num(&m->params); + for (i=0; iparams, i); + if (strlen(param) == len && !memcmp(name, param, len)) { + *num_ret = i; + return true; + } + } + return false; +} + +static +void +macro_parse_expansion(struct macro *m, const char *buf) +{ + size_t blockstart, wordstart, pos; + struct expansionitem *ei; + unsigned param; + + pos = blockstart = 0; + while (buf[pos] != '\0') { + pos += strspn(buf+pos, ws); + if (strchr(alnum, buf[pos])) { + wordstart = pos; + pos += strspn(buf+pos, alnum); + if (isparam(m, buf+wordstart, pos-wordstart, ¶m)) { + if (wordstart > blockstart) { + ei = expansionitem_create_stringlen( + buf + blockstart, + wordstart - blockstart); + expansionitemarray_add(&m->expansion, + ei, NULL); + } + ei = expansionitem_create_param(param); + expansionitemarray_add(&m->expansion, ei,NULL); + blockstart = pos; + continue; + } + continue; + } + pos++; + } + if (pos > blockstart) { + ei = expansionitem_create_stringlen(buf + blockstart, + pos - blockstart); + expansionitemarray_add(&m->expansion, ei, NULL); + } +} + +void +macro_define_plain(struct place *p1, const char *macro, + struct place *p2, const char *expansion) +{ + struct macro *m; + struct expansionitem *ei; + + m = macro_define_common_start(p1, macro, p2); + ei = expansionitem_create_string(expansion); + expansionitemarray_add(&m->expansion, ei, NULL); + macro_define_common_end(m); +} + +void +macro_define_params(struct place *p1, const char *macro, + struct place *p2, const char *params, + struct place *p3, const char *expansion) +{ + struct macro *m; + + m = macro_define_common_start(p1, macro, p3); + m->hasparams = true; + macro_parse_parameters(m, p2, params); + macro_parse_expansion(m, expansion); + macro_define_common_end(m); +} + +void +macro_undef(const char *macro) +{ + struct macro *m; + + m = macrotable_find(macro, true); + if (m) { + macro_destroy(m); + } +} + +bool +macro_isdefined(const char *macro) +{ + struct macro *m; + + m = macrotable_find(macro, false); + return m != NULL; +} + +//////////////////////////////////////////////////////////// +// macro expansion + +struct expstate { + bool honordefined; + enum { ES_NORMAL, ES_WANTLPAREN, ES_NOARG, ES_HAVEARG } state; + struct macro *curmacro; + struct stringarray args; + unsigned argparens; + + bool tobuf; + char *buf; + size_t bufpos, bufmax; +}; + +static struct expstate mainstate; + +static void doexpand(struct expstate *es, struct place *p, + char *buf, size_t len); + +static +void +expstate_init(struct expstate *es, bool tobuf, bool honordefined) +{ + es->honordefined = honordefined; + es->state = ES_NORMAL; + es->curmacro = NULL; + stringarray_init(&es->args); + es->argparens = 0; + es->tobuf = tobuf; + es->buf = NULL; + es->bufpos = 0; + es->bufmax = 0; +} + +static +void +expstate_cleanup(struct expstate *es) +{ + assert(es->state == ES_NORMAL); + stringarray_cleanup(&es->args); + if (es->buf) { + dofree(es->buf, es->bufmax); + } +} + +static +void +expstate_destroyargs(struct expstate *es) +{ + unsigned i, num; + + num = stringarray_num(&es->args); + for (i=0; iargs, i)); + } + stringarray_setsize(&es->args, 0); +} + +static +void +expand_send(struct expstate *es, struct place *p, const char *buf, size_t len) +{ + size_t oldmax; + + if (es->tobuf) { + assert(es->bufpos <= es->bufmax); + if (es->bufpos + len > es->bufmax) { + oldmax = es->bufmax; + if (es->bufmax == 0) { + es->bufmax = 64; + } + while (es->bufpos + len > es->bufmax) { + es->bufmax *= 2; + } + es->buf = dorealloc(es->buf, oldmax, es->bufmax); + } + memcpy(es->buf + es->bufpos, buf, len); + es->bufpos += len; + assert(es->bufpos <= es->bufmax); + } else { + output(p, buf, len); + } +} + +static +void +expand_send_eof(struct expstate *es, struct place *p) +{ + if (es->tobuf) { + expand_send(es, p, "", 1); + es->bufpos--; + } else { + output_eof(); + } +} + +static +void +expand_newarg(struct expstate *es, char *buf, size_t len) +{ + char *text; + + text = dostrndup(buf, len); + stringarray_add(&es->args, text, NULL); +} + +static +void +expand_appendarg(struct expstate *es, char *buf, size_t len) +{ + unsigned num; + char *text; + size_t oldlen; + + num = stringarray_num(&es->args); + assert(num > 0); + + text = stringarray_get(&es->args, num - 1); + oldlen = strlen(text); + text = dorealloc(text, oldlen + 1, oldlen + len + 1); + memcpy(text + oldlen, buf, len); + text[oldlen+len] = '\0'; + stringarray_set(&es->args, num - 1, text); +} + +static +char * +expand_substitute(struct place *p, struct expstate *es) +{ + struct expansionitem *ei; + unsigned i, num; + size_t len; + char *arg; + char *ret; + unsigned numargs, numparams; + + numargs = stringarray_num(&es->args); + numparams = stringarray_num(&es->curmacro->params); + + if (numargs == 0 && numparams == 1) { + /* no arguments <=> one empty argument */ + stringarray_add(&es->args, dostrdup(""), NULL); + numargs++; + } + if (numargs != numparams) { + complain(p, "Wrong number of arguments for macro %s; " + "found %u, expected %u", + es->curmacro->name, numargs, numparams); + complain_fail(); + while (numargs < numparams) { + stringarray_add(&es->args, dostrdup(""), NULL); + numargs++; + } + } + + len = 0; + num = expansionitemarray_num(&es->curmacro->expansion); + for (i=0; icurmacro->expansion, i); + if (ei->isstring) { + len += strlen(ei->string); + } else { + arg = stringarray_get(&es->args, ei->param); + len += strlen(arg); + } + } + + ret = domalloc(len+1); + *ret = '\0'; + for (i=0; icurmacro->expansion, i); + if (ei->isstring) { + strcat(ret, ei->string); + } else { + arg = stringarray_get(&es->args, ei->param); + strcat(ret, arg); + } + } + + return ret; +} + +static +void +expand_domacro(struct expstate *es, struct place *p) +{ + struct macro *m; + char *newbuf, *newbuf2; + + if (es->curmacro == NULL) { + /* defined() */ + if (stringarray_num(&es->args) != 1) { + complain(p, "Too many arguments for defined()"); + complain_fail(); + expand_send(es, p, "0", 1); + return; + } + m = macrotable_find(stringarray_get(&es->args, 0), false); + expand_send(es, p, (m != NULL) ? "1" : "0", 1); + expstate_destroyargs(es); + return; + } + + assert(es->curmacro->inuse == false); + es->curmacro->inuse = true; + + newbuf = expand_substitute(p, es); + newbuf2 = macroexpand(p, newbuf, strlen(newbuf), false); + dostrfree(newbuf); + expstate_destroyargs(es); + doexpand(es, p, newbuf2, strlen(newbuf2)); + dostrfree(newbuf2); + + es->curmacro->inuse = false; +} + +/* + * The traditional behavior if a function-like macro appears without + * arguments is to pretend it isn't a macro; that is, just emit its + * name. + */ +static +void +expand_missingargs(struct expstate *es, struct place *p, bool needspace) +{ + if (es->curmacro == NULL) { + /* defined */ + expand_send(es, p, "defined", 7); + return; + } + expand_send(es, p, es->curmacro->name, strlen(es->curmacro->name)); + /* send a space in case we ate whitespace after the macro name */ + if (needspace) { + expand_send(es, p, " ", 1); + } +} + +static +void +expand_got_ws(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, p, buf, len); + break; + case ES_WANTLPAREN: + break; + case ES_NOARG: + expand_newarg(es, buf, len); + es->state = ES_HAVEARG; + break; + case ES_HAVEARG: + expand_appendarg(es, buf, len); + break; + } +} + +static +void +expand_got_word(struct expstate *es, struct place *p, char *buf, size_t len) +{ + struct macro *m; + struct expansionitem *ei; + char *newbuf; + + switch (es->state) { + case ES_NORMAL: + if (es->honordefined && + len == 7 && !memcmp(buf, "defined", 7)) { + es->curmacro = NULL; + es->state = ES_WANTLPAREN; + break; + } + m = macrotable_findlen(buf, len, false); + if (m == NULL || m->inuse) { + expand_send(es, p, buf, len); + } else if (!m->hasparams) { + m->inuse = true; + assert(expansionitemarray_num(&m->expansion) == 1); + ei = expansionitemarray_get(&m->expansion, 0); + assert(ei->isstring); + newbuf = macroexpand(p, ei->string, + strlen(ei->string), false); + doexpand(es, p, newbuf, strlen(newbuf)); + dostrfree(newbuf); + m->inuse = false; + } else { + es->curmacro = m; + es->state = ES_WANTLPAREN; + } + break; + case ES_WANTLPAREN: + if (es->curmacro != NULL) { + expand_missingargs(es, p, true); + es->state = ES_NORMAL; + /* try again */ + expand_got_word(es, p, buf, len); + } else { + /* "defined foo" means "defined(foo)" */ + expand_newarg(es, buf, len); + es->state = ES_NORMAL; + expand_domacro(es, p); + } + break; + case ES_NOARG: + expand_newarg(es, buf, len); + es->state = ES_HAVEARG; + break; + case ES_HAVEARG: + expand_appendarg(es, buf, len); + break; + } +} + +static +void +expand_got_lparen(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, p, buf, len); + break; + case ES_WANTLPAREN: + es->state = ES_NOARG; + break; + case ES_NOARG: + expand_newarg(es, buf, len); + es->state = ES_HAVEARG; + es->argparens++; + break; + case ES_HAVEARG: + expand_appendarg(es, buf, len); + es->argparens++; + break; + } +} + +static +void +expand_got_rparen(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, p, buf, len); + break; + case ES_WANTLPAREN: + expand_missingargs(es, p, false); + es->state = ES_NORMAL; + /* try again */ + expand_got_rparen(es, p, buf, len); + break; + case ES_NOARG: + assert(es->argparens == 0); + if (stringarray_num(&es->args) > 0) { + /* we are after a comma; enter an empty argument */ + expand_newarg(es, buf, 0); + } + es->state = ES_NORMAL; + expand_domacro(es, p); + break; + case ES_HAVEARG: + if (es->argparens > 0) { + es->argparens--; + expand_appendarg(es, buf, len); + } else { + es->state = ES_NORMAL; + expand_domacro(es, p); + } + break; + } +} + +static +void +expand_got_comma(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, p, buf, len); + break; + case ES_WANTLPAREN: + expand_missingargs(es, p, false); + es->state = ES_NORMAL; + /* try again */ + expand_got_comma(es, p, buf, len); + break; + case ES_NOARG: + assert(es->argparens == 0); + expand_newarg(es, buf, 0); + break; + case ES_HAVEARG: + if (es->argparens > 0) { + expand_appendarg(es, buf, len); + } else { + es->state = ES_NOARG; + } + break; + } +} + +static +void +expand_got_other(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, p, buf, len); + break; + case ES_WANTLPAREN: + expand_missingargs(es, p, false); + es->state = ES_NORMAL; + /* try again */ + expand_got_other(es, p, buf, len); + break; + case ES_NOARG: + expand_newarg(es, buf, len); + es->state = ES_HAVEARG; + break; + case ES_HAVEARG: + expand_appendarg(es, buf, len); + break; + } +} + +static +void +expand_got_eof(struct expstate *es, struct place *p) +{ + switch (es->state) { + case ES_NORMAL: + break; + case ES_WANTLPAREN: + expand_missingargs(es, p, false); + break; + case ES_NOARG: + case ES_HAVEARG: + if (es->curmacro) { + complain(p, "Unclosed argument list for macro %s", + es->curmacro->name); + } else { + complain(p, "Unclosed argument list for defined()"); + } + complain_fail(); + expstate_destroyargs(es); + break; + } + expand_send_eof(es, p); + es->state = ES_NORMAL; + es->curmacro = NULL; + es->argparens = 0; +} + +static +void +doexpand(struct expstate *es, struct place *p, char *buf, size_t len) +{ + char *s; + size_t x; + bool inquote = false; + char quote = '\0'; + + while (len > 0) { + x = strspn(buf, ws); + if (x > len) { + /* XXX gross, need strnspn */ + x = len; + } + + if (x > 0) { + expand_got_ws(es, p, buf, x); + buf += x; + len -= x; + continue; + } + + x = strspn(buf, alnum); + if (x > len) { + /* XXX gross, need strnspn */ + x = len; + } + + if (!inquote && x > 0) { + expand_got_word(es, p, buf, x); + buf += x; + len -= x; + continue; + } + + if (!inquote && len > 1 && buf[0] == '/' && buf[1] == '*') { + s = strstr(buf, "*/"); + if (s) { + x = s - buf; + } else { + x = len; + } + expand_got_ws(es, p, buf, x); + buf += x; + len -= x; + continue; + } + + if (!inquote && buf[0] == '(') { + expand_got_lparen(es, p, buf, 1); + buf++; + len--; + continue; + } + + if (!inquote && buf[0] == ')') { + expand_got_rparen(es, p, buf, 1); + buf++; + len--; + continue; + } + + if (!inquote && buf[0] == ',') { + expand_got_comma(es, p, buf, 1); + buf++; + len--; + continue; + } + + if (len > 1 && buf[0] == '\\' && + (buf[1] == '"' || buf[1] == '\'')) { + expand_got_other(es, p, buf, 2); + buf += 2; + len -= 2; + continue; + } + if (!inquote && (buf[0] == '"' || buf[0] == '\'')) { + inquote = true; + quote = buf[0]; + } else if (inquote && buf[0] == quote) { + inquote = false; + } + + expand_got_other(es, p, buf, 1); + buf++; + len--; + } +} + +char * +macroexpand(struct place *p, char *buf, size_t len, bool honordefined) +{ + struct expstate es; + char *ret; + + expstate_init(&es, true, honordefined); + doexpand(&es, p, buf, len); + expand_got_eof(&es, p); + + /* trim to fit, so the malloc debugging won't complain */ + es.buf = dorealloc(es.buf, es.bufmax, strlen(es.buf) + 1); + + ret = es.buf; + es.buf = NULL; + es.bufpos = es.bufmax = 0; + + expstate_cleanup(&es); + + return ret; +} + +void +macro_sendline(struct place *p, char *buf, size_t len) +{ + doexpand(&mainstate, p, buf, len); + output(p, "\n", 1); +} + +void +macro_sendeof(struct place *p) +{ + expand_got_eof(&mainstate, p); +} + +//////////////////////////////////////////////////////////// +// module initialization + +void +macros_init(void) +{ + macrotable_init(); + expstate_init(&mainstate, false, false); +} + +void +macros_cleanup(void) +{ + expstate_cleanup(&mainstate); + macrotable_cleanup(); +} diff --git a/libexec/tradcpp/macro.h b/libexec/tradcpp/macro.h new file mode 100644 index 00000000000..98671e63bc9 --- /dev/null +++ b/libexec/tradcpp/macro.h @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +struct place; + +void macros_init(void); +void macros_cleanup(void); + +void macro_define_plain(struct place *, const char *macro, + struct place *, const char *expansion); +void macro_define_params(struct place *, const char *macro, + struct place *, const char *params, + struct place *, const char *expansion); +void macro_undef(const char *macro); +bool macro_isdefined(const char *macro); + +char *macroexpand(struct place *, char *buf, size_t len, bool honordefined); + +void macro_sendline(struct place *, char *buf, size_t len); +void macro_sendeof(struct place *); diff --git a/libexec/tradcpp/main.c b/libexec/tradcpp/main.c new file mode 100644 index 00000000000..35f9bca1326 --- /dev/null +++ b/libexec/tradcpp/main.c @@ -0,0 +1,1090 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include "version.h" +#include "config.h" +#include "utils.h" +#include "array.h" +#include "mode.h" +#include "place.h" +#include "files.h" +#include "directive.h" +#include "macro.h" + +struct mode mode = { + .werror = false, + + .input_allow_dollars = false, + .input_tabstop = 8, + + .do_stdinc = true, + .do_stddef = true, + + .do_output = true, + .output_linenumbers = true, + .output_retain_comments = false, + .output_file = NULL, + + .do_depend = false, + .depend_report_system = false, + .depend_assume_generated = false, + .depend_issue_fakerules = false, + .depend_quote_target = true, + .depend_target = NULL, + .depend_file = NULL, + + .do_macrolist = false, + .macrolist_include_stddef = false, + .macrolist_include_expansions = false, + + .do_trace = false, + .trace_namesonly = false, + .trace_indented = false, +}; + +struct warns warns = { + .endiflabels = true, + .nestcomment = false, + .undef = false, + .unused = false, +}; + +//////////////////////////////////////////////////////////// +// commandline macros + +struct commandline_macro { + struct place where; + struct place where2; + const char *macro; + const char *expansion; +}; + +static struct array commandline_macros; + +static +void +commandline_macros_init(void) +{ + array_init(&commandline_macros); +} + +static +void +commandline_macros_cleanup(void) +{ + unsigned i, num; + struct commandline_macro *cm; + + num = array_num(&commandline_macros); + for (i=0; iwhere = *p; + cm->where2 = *p2; + cm->macro = macro; + cm->expansion = expansion; + + array_add(&commandline_macros, cm, NULL); +} + +static +void +commandline_def(const struct place *p, char *str) +{ + struct place p2; + char *val; + + if (*str == '\0') { + complain(NULL, "-D: macro name expected"); + die(); + } + + val = strchr(str, '='); + if (val != NULL) { + *val = '\0'; + val++; + } + + if (val) { + p2 = *p; + p2.column += strlen(str); + } else { + place_setbuiltin(&p2, 1); + } + commandline_macro_add(p, str, &p2, val ? val : "1"); +} + +static +void +commandline_undef(const struct place *p, char *str) +{ + if (*str == '\0') { + complain(NULL, "-D: macro name expected"); + die(); + } + commandline_macro_add(p, str, p, NULL); +} + +static +void +apply_commandline_macros(void) +{ + struct commandline_macro *cm; + unsigned i, num; + + num = array_num(&commandline_macros); + for (i=0; iexpansion != NULL) { + macro_define_plain(&cm->where, cm->macro, + &cm->where2, cm->expansion); + } else { + macro_undef(cm->macro); + } + dofree(cm, sizeof(*cm)); + } + array_setsize(&commandline_macros, 0); +} + +static +void +apply_builtin_macro(unsigned num, const char *name, const char *val) +{ + struct place p; + + place_setbuiltin(&p, num); + macro_define_plain(&p, name, &p, val); +} + +static +void +apply_builtin_macros(void) +{ + unsigned n = 1; + +#ifdef CONFIG_OS + apply_builtin_macro(n++, CONFIG_OS, "1"); +#endif +#ifdef CONFIG_OS_2 + apply_builtin_macro(n++, CONFIG_OS_2, "1"); +#endif + +#ifdef CONFIG_CPU + apply_builtin_macro(n++, CONFIG_CPU, "1"); +#endif +#ifdef CONFIG_CPU_2 + apply_builtin_macro(n++, CONFIG_CPU_2, "1"); +#endif + +#ifdef CONFIG_SIZE + apply_builtin_macro(n++, CONFIG_SIZE, "1"); +#endif +#ifdef CONFIG_BINFMT + apply_builtin_macro(n++, CONFIG_BINFMT, "1"); +#endif + +#ifdef CONFIG_COMPILER + apply_builtin_macro(n++, CONFIG_COMPILER, VERSION_MAJOR); + apply_builtin_macro(n++, CONFIG_COMPILER_MINOR, VERSION_MINOR); + apply_builtin_macro(n++, "__VERSION__", VERSION_LONG); +#endif +} + +//////////////////////////////////////////////////////////// +// extra included files + +struct commandline_file { + struct place where; + char *name; + bool suppress_output; +}; + +static struct array commandline_files; + +static +void +commandline_files_init(void) +{ + array_init(&commandline_files); +} + +static +void +commandline_files_cleanup(void) +{ + unsigned i, num; + struct commandline_file *cf; + + num = array_num(&commandline_files); + for (i=0; iwhere = *p; + cf->name = name; + cf->suppress_output = suppress_output; + array_add(&commandline_files, cf, NULL); +} + +static +void +commandline_addfile_output(const struct place *p, char *name) +{ + commandline_addfile(p, name, false); +} + +static +void +commandline_addfile_nooutput(const struct place *p, char *name) +{ + commandline_addfile(p, name, true); +} + +static +void +read_commandline_files(void) +{ + struct commandline_file *cf; + unsigned i, num; + bool save = false; + + num = array_num(&commandline_files); + for (i=0; isuppress_output) { + save = mode.do_output; + mode.do_output = false; + file_readquote(&cf->where, cf->name); + mode.do_output = save; + } else { + file_readquote(&cf->where, cf->name); + } + dofree(cf, sizeof(*cf)); + } + array_setsize(&commandline_files, 0); +} + +//////////////////////////////////////////////////////////// +// include path accumulation + +static struct stringarray incpath_quote; +static struct stringarray incpath_user; +static struct stringarray incpath_system; +static struct stringarray incpath_late; +static const char *sysroot; + +static +void +incpath_init(void) +{ + stringarray_init(&incpath_quote); + stringarray_init(&incpath_user); + stringarray_init(&incpath_system); + stringarray_init(&incpath_late); +} + +static +void +incpath_cleanup(void) +{ + stringarray_setsize(&incpath_quote, 0); + stringarray_setsize(&incpath_user, 0); + stringarray_setsize(&incpath_system, 0); + stringarray_setsize(&incpath_late, 0); + + stringarray_cleanup(&incpath_quote); + stringarray_cleanup(&incpath_user); + stringarray_cleanup(&incpath_system); + stringarray_cleanup(&incpath_late); +} + +static +void +commandline_isysroot(const struct place *p, char *dir) +{ + (void)p; + sysroot = dir; +} + +static +void +commandline_addincpath(struct stringarray *arr, char *s) +{ + if (*s == '\0') { + complain(NULL, "Empty include directory"); + die(); + } + stringarray_add(arr, s, NULL); +} + +static +void +commandline_addincpath_quote(const struct place *p, char *dir) +{ + (void)p; + commandline_addincpath(&incpath_quote, dir); +} + +static +void +commandline_addincpath_user(const struct place *p, char *dir) +{ + (void)p; + commandline_addincpath(&incpath_user, dir); +} + +static +void +commandline_addincpath_system(const struct place *p, char *dir) +{ + (void)p; + commandline_addincpath(&incpath_system, dir); +} + +static +void +commandline_addincpath_late(const struct place *p, char *dir) +{ + (void)p; + commandline_addincpath(&incpath_late, dir); +} + +static +void +loadincludepath(void) +{ + unsigned i, num; + const char *dir; + char *t; + + num = stringarray_num(&incpath_quote); + for (i=0; i 64) { + complain(NULL, "Preposterously large tabstop"); + die(); + } + mode.input_tabstop = val; +} + +/* + * macrolist + */ + +static +void +commandline_dD(void) +{ + mode.do_macrolist = true; + mode.macrolist_include_stddef = false; + mode.macrolist_include_expansions = true; +} + +static +void +commandline_dM(void) +{ + mode.do_macrolist = true; + mode.macrolist_include_stddef = true; + mode.macrolist_include_expansions = true; + mode.do_output = false; +} + +static +void +commandline_dN(void) +{ + mode.do_macrolist = true; + mode.macrolist_include_stddef = false; + mode.macrolist_include_expansions = false; +} + +/* + * include trace + */ + +static +void +commandline_dI(void) +{ + mode.do_trace = true; + mode.trace_namesonly = false; + mode.trace_indented = false; +} + +static +void +commandline_H(void) +{ + mode.do_trace = true; + mode.trace_namesonly = true; + mode.trace_indented = true; +} + +/* + * depend + */ + +static +void +commandline_setdependtarget(const struct place *p, char *str) +{ + (void)p; + mode.depend_target = str; + mode.depend_quote_target = false; +} + +static +void +commandline_setdependtarget_quoted(const struct place *p, char *str) +{ + (void)p; + mode.depend_target = str; + mode.depend_quote_target = true; +} + +static +void +commandline_setdependoutput(const struct place *p, char *str) +{ + (void)p; + mode.depend_file = str; +} + +static +void +commandline_M(void) +{ + mode.do_depend = true; + mode.depend_report_system = true; + mode.do_output = false; +} + +static +void +commandline_MM(void) +{ + mode.do_depend = true; + mode.depend_report_system = false; + mode.do_output = false; +} + +static +void +commandline_MD(void) +{ + mode.do_depend = true; + mode.depend_report_system = true; +} + +static +void +commandline_MMD(void) +{ + mode.do_depend = true; + mode.depend_report_system = false; +} + +static +void +commandline_wall(void) +{ + warns.nestcomment = true; + warns.undef = true; + warns.unused = true; +} + +static +void +commandline_wnoall(void) +{ + warns.nestcomment = false; + warns.undef = false; + warns.unused = false; +} + +static +void +commandline_wnone(void) +{ + warns.nestcomment = false; + warns.endiflabels = false; + warns.undef = false; + warns.unused = false; +} + +//////////////////////////////////////////////////////////// +// options + +struct ignore_option { + const char *string; +}; + +struct flag_option { + const char *string; + bool *flag; + bool setto; +}; + +struct act_option { + const char *string; + void (*func)(void); +}; + +struct prefix_option { + const char *string; + void (*func)(const struct place *, char *); +}; + +struct arg_option { + const char *string; + void (*func)(const struct place *, char *); +}; + +static const struct ignore_option ignore_options[] = { + { "m32" }, + { "traditional" }, +}; +static const unsigned num_ignore_options = HOWMANY(ignore_options); + +static const struct flag_option flag_options[] = { + { "C", &mode.output_retain_comments, true }, + { "CC", &mode.output_retain_comments, true }, + { "MG", &mode.depend_assume_generated, true }, + { "MP", &mode.depend_issue_fakerules, true }, + { "P", &mode.output_linenumbers, false }, + { "Wcomment", &warns.nestcomment, true }, + { "Wendif-labels", &warns.endiflabels, true }, + { "Werror", &mode.werror, true }, + { "Wno-comment", &warns.nestcomment, false }, + { "Wno-endif-labels", &warns.endiflabels, false }, + { "Wno-error", &mode.werror, false }, + { "Wno-undef", &warns.undef, false }, + { "Wno-unused-macros", &warns.unused, false }, + { "Wundef", &warns.undef, true }, + { "Wunused-macros", &warns.unused, true }, + { "fdollars-in-identifiers", &mode.input_allow_dollars, true }, + { "fno-dollars-in-identifiers", &mode.input_allow_dollars, false }, + { "nostdinc", &mode.do_stdinc, false }, + { "undef", &mode.do_stddef, false }, +}; +static const unsigned num_flag_options = HOWMANY(flag_options); + +static const struct act_option act_options[] = { + { "H", commandline_H }, + { "M", commandline_M }, + { "MD", commandline_MD }, + { "MM", commandline_MM }, + { "MMD", commandline_MMD }, + { "Wall", commandline_wall }, + { "Wno-all", commandline_wnoall }, + { "dD", commandline_dD }, + { "dI", commandline_dI }, + { "dM", commandline_dM }, + { "dN", commandline_dN }, + { "w", commandline_wnone }, +}; +static const unsigned num_act_options = HOWMANY(act_options); + +static const struct prefix_option prefix_options[] = { + { "D", commandline_def }, + { "I", commandline_addincpath_user }, + { "U", commandline_undef }, + { "ftabstop=", commandline_tabstop }, + { "std=", commandline_setstd }, +}; +static const unsigned num_prefix_options = HOWMANY(prefix_options); + +static const struct arg_option arg_options[] = { + { "MF", commandline_setdependoutput }, + { "MQ", commandline_setdependtarget_quoted }, + { "MT", commandline_setdependtarget }, + { "idirafter", commandline_addincpath_late }, + { "imacros", commandline_addfile_nooutput }, + { "include", commandline_addfile_output }, + { "iprefix", commandline_setprefix }, + { "iquote", commandline_addincpath_quote }, + { "iremap", commandline_iremap }, + { "isysroot", commandline_isysroot }, + { "isystem", commandline_addincpath_system }, + { "iwithprefix", commandline_addincpath_late_withprefix }, + { "iwithprefixbefore", commandline_addincpath_user_withprefix }, + { "x", commandline_setlang }, +}; +static const unsigned num_arg_options = HOWMANY(arg_options); + +static +bool +check_ignore_option(const char *opt) +{ + unsigned i; + int r; + + for (i=0; i + +struct mode { + bool werror; + bool input_allow_dollars; + unsigned input_tabstop; + bool do_stdinc; + bool do_stddef; + bool do_output; + bool output_linenumbers; + bool output_retain_comments; + const char *output_file; + bool do_depend; + bool depend_report_system; + bool depend_assume_generated; + bool depend_issue_fakerules; + bool depend_quote_target; + const char *depend_target; + const char *depend_file; + bool do_macrolist; + bool macrolist_include_stddef; + bool macrolist_include_expansions; + bool do_trace; + bool trace_namesonly; + bool trace_indented; +}; + +struct warns { + bool endiflabels; + bool nestcomment; + bool undef; + bool unused; +}; + +extern struct mode mode; +extern struct warns warns; diff --git a/libexec/tradcpp/output.c b/libexec/tradcpp/output.c new file mode 100644 index 00000000000..d1d1f2d4d94 --- /dev/null +++ b/libexec/tradcpp/output.c @@ -0,0 +1,193 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "utils.h" +#include "mode.h" +#include "place.h" +#include "output.h" + +static int outputfd = -1; +static bool incomment = false; +static char *linebuf; +static size_t linebufpos, linebufmax; +static struct place linebufplace; + +static +void +output_open(void) +{ + if (mode.output_file == NULL) { + outputfd = STDOUT_FILENO; + } else { + outputfd = open(mode.output_file, O_WRONLY|O_CREAT|O_TRUNC, + 0664); + if (outputfd < 0) { + complain(NULL, "%s: %s", + mode.output_file, strerror(errno)); + die(); + } + } +} + +static +void +dowrite(const char *buf, size_t len) +{ + size_t done; + ssize_t result; + static unsigned write_errors = 0; + + if (!mode.do_output) { + return; + } + + if (outputfd < 0) { + output_open(); + } + + done = 0; + while (done < len) { + result = write(outputfd, buf+done, len-done); + if (result == -1) { + complain(NULL, "%s: write: %s", + mode.output_file, strerror(errno)); + complain_failed(); + write_errors++; + if (write_errors > 5) { + complain(NULL, "%s: giving up", + mode.output_file); + die(); + } + /* XXX is this really a good idea? */ + sleep(1); + } + done += (size_t)result; + } +} + + +static +void +filter_output(const char *buf, size_t len) +{ + size_t pos, start; + bool inesc = false; + bool inquote = false; + char quote = '\0'; + + start = 0; + for (pos = 0; pos < len - 1; pos++) { + if (!inquote && buf[pos] == '/' && buf[pos+1] == '*') { + if (!incomment) { + if (pos > start) { + dowrite(buf + start, pos - start); + } + start = pos; + pos += 2; + incomment = true; + /* cancel out the loop's pos++ */ + pos--; + continue; + } + } else if (buf[pos] == '*' && buf[pos+1] == '/') { + if (incomment) { + pos += 2; + if (mode.output_retain_comments) { + dowrite(buf + start, pos - start); + } + start = pos; + incomment = false; + /* cancel out the loop's pos++ */ + pos--; + continue; + } + } + + if (incomment) { + /* nothing */ + } else if (inesc) { + inesc = false; + } else if (buf[pos] == '\\') { + inesc = true; + } else if (!inquote && (buf[pos] == '"' || buf[pos] == '\'')) { + inquote = true; + quote = buf[pos]; + } else if (inquote && buf[pos] == quote) { + inquote = false; + } + } + pos++; + + if (pos > start) { + if (!incomment || mode.output_retain_comments) { + dowrite(buf + start, pos - start); + } + } +} + +void +output(const struct place *p, const char *buf, size_t len) +{ + size_t oldmax; + + if (linebufpos + len > linebufmax) { + oldmax = linebufmax; + if (linebufmax == 0) { + linebufmax = 64; + } + while (linebufpos + len > linebufmax) { + linebufmax *= 2; + } + linebuf = dorealloc(linebuf, oldmax, linebufmax); + } + if (linebufpos == 0) { + linebufplace = *p; + } + memcpy(linebuf + linebufpos, buf, len); + linebufpos += len; + + if (len == 1 && buf[0] == '\n') { + filter_output(linebuf, linebufpos); + linebufpos = 0; + } +} + +void +output_eof(void) +{ + if (mode.output_file != NULL && outputfd >= 0) { + close(outputfd); + } + outputfd = -1; +} diff --git a/libexec/tradcpp/output.h b/libexec/tradcpp/output.h new file mode 100644 index 00000000000..c1cb6629eab --- /dev/null +++ b/libexec/tradcpp/output.h @@ -0,0 +1,31 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +void output(const struct place *p, const char *buf, size_t len); +void output_eof(void); diff --git a/libexec/tradcpp/place.c b/libexec/tradcpp/place.c new file mode 100644 index 00000000000..267104875c5 --- /dev/null +++ b/libexec/tradcpp/place.c @@ -0,0 +1,241 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include "utils.h" +#include "array.h" +#include "place.h" + +struct placefile { + struct place includedfrom; + char *dir; + char *name; + int depth; + bool fromsystemdir; +}; +DECLARRAY(placefile, static UNUSED); +DEFARRAY(placefile, static); + +static struct placefilearray placefiles; +static bool overall_failure; + +static const char *myprogname; + +//////////////////////////////////////////////////////////// +// seenfiles + +static +struct placefile * +placefile_create(const struct place *from, const char *name, + bool fromsystemdir) +{ + struct placefile *pf; + const char *s; + size_t len; + + pf = domalloc(sizeof(*pf)); + pf->includedfrom = *from; + + s = strrchr(name, '/'); + len = (s == NULL) ? 0 : s - name; + pf->dir = dostrndup(name, len); + + pf->name = dostrdup(name); + pf->fromsystemdir = fromsystemdir; + + if (from->file != NULL) { + pf->depth = from->file->depth + 1; + } else { + pf->depth = 1; + } + return pf; +} + +static +void +placefile_destroy(struct placefile *pf) +{ + dostrfree(pf->name); + dofree(pf, sizeof(*pf)); +} + +DESTROYALL_ARRAY(placefile, ); + +const char * +place_getparsedir(const struct place *place) +{ + if (place->file == NULL) { + return "."; + } + return place->file->dir; +} + +const struct placefile * +place_addfile(const struct place *place, const char *file, bool issystem) +{ + struct placefile *pf; + + pf = placefile_create(place, file, issystem); + placefilearray_add(&placefiles, pf, NULL); + if (pf->depth > 120) { + complain(place, "Maximum include nesting depth exceeded"); + die(); + } + return pf; +} + +//////////////////////////////////////////////////////////// +// places + +void +place_setnowhere(struct place *p) +{ + p->type = P_NOWHERE; + p->file = NULL; + p->line = 0; + p->column = 0; +} + +void +place_setbuiltin(struct place *p, unsigned num) +{ + p->type = P_BUILTIN; + p->file = NULL; + p->line = num; + p->column = 1; +} + +void +place_setcommandline(struct place *p, unsigned line, unsigned column) +{ + p->type = P_COMMANDLINE; + p->file = NULL; + p->line = line; + p->column = column; +} + +void +place_setfilestart(struct place *p, const struct placefile *pf) +{ + p->type = P_FILE; + p->file = pf; + p->line = 1; + p->column = 1; +} + +static +const char * +place_getname(const struct place *p) +{ + switch (p->type) { + case P_NOWHERE: return ""; + case P_BUILTIN: return ""; + case P_COMMANDLINE: return ""; + case P_FILE: return p->file->name; + } + assert(0); + return NULL; +} + +static +void +place_printfrom(const struct place *p) +{ + const struct place *from; + + if (p->file == NULL) { + return; + } + from = &p->file->includedfrom; + if (from->type != P_NOWHERE) { + place_printfrom(from); + fprintf(stderr, "In file included from %s:%u:%u:\n", + place_getname(from), from->line, from->column); + } +} + +//////////////////////////////////////////////////////////// +// complaints + +void +complain_init(const char *pn) +{ + myprogname = pn; +} + +void +complain(const struct place *p, const char *fmt, ...) +{ + va_list ap; + + if (p != NULL) { + place_printfrom(p); + fprintf(stderr, "%s:%u:%u: ", place_getname(p), + p->line, p->column); + } else { + fprintf(stderr, "%s: ", myprogname); + } + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +void +complain_fail(void) +{ + overall_failure = true; +} + +bool +complain_failed(void) +{ + return overall_failure; +} + +//////////////////////////////////////////////////////////// +// module init and cleanup + +void +place_init(void) +{ + placefilearray_init(&placefiles); +} + +void +place_cleanup(void) +{ + placefilearray_destroyall(&placefiles); + placefilearray_cleanup(&placefiles); +} diff --git a/libexec/tradcpp/place.h b/libexec/tradcpp/place.h new file mode 100644 index 00000000000..311645be145 --- /dev/null +++ b/libexec/tradcpp/place.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +enum places { + P_NOWHERE, + P_BUILTIN, + P_COMMANDLINE, + P_FILE, +}; +struct place { + enum places type; + const struct placefile *file; + unsigned line; + unsigned column; +}; + +void place_init(void); +void place_cleanup(void); + +void place_setnowhere(struct place *p); +void place_setbuiltin(struct place *p, unsigned num); +void place_setcommandline(struct place *p, unsigned word, unsigned column); +void place_setfilestart(struct place *p, const struct placefile *pf); + +const char *place_getparsedir(const struct place *incplace); + +const struct placefile *place_addfile(const struct place *incplace, + const char *name, bool fromsystemdir); diff --git a/libexec/tradcpp/tradcpp.1 b/libexec/tradcpp/tradcpp.1 new file mode 100644 index 00000000000..952d8b7232c --- /dev/null +++ b/libexec/tradcpp/tradcpp.1 @@ -0,0 +1,353 @@ +.\" +.\" Copyright (c) 2013 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by David A. Holland. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd June 11, 2013 +.Dt TRADCPP 1 +.Os +.Sh NAME +.Nm tradcpp +.Nd traditional (K&R-style) C macro preprocessor +.Sh SYNOPSIS +.Nm tradcpp +.Op Fl options +.Op Ar input-file Op Ar output-file +.Sh DESCRIPTION +The +.Nm +command provides a traditional K&R-style C macro preprocessor. +It is intended to be suitable for historical Unix uses of the +preprocessor, such as +.Xr imake 1 , +particularly those that depend on preservation of whitespace. +.Pp +The chief ways in which traditional cpp differs from +Standard C are: +.Bl -bullet -offset indent +.It +Macro arguments are expanded within quoted strings. +There is no stringize operator. +.It +There is no token pasting operator; tokens can be concatenated by +placing comments between them. +This process is also not limited to valid C language tokens. +.It +Whitespace is preserved, and in particular tabs are not expanded into +spaces. +Furthermore, additional whitespace is not injected. +.El +.Sh OPTIONS +.Nm +has many options, many of which are defined for compatibility with +.Xr gcc 1 +or other compilers. +Many of the options are not yet implemented. +.\" The option lists have been sorted in what I hope is a sensible +.\" order. Please don't arbitrarily alphabetize them. +.Ss Common Options +.Bl -tag -width bubblebabble +.It Fl C +Retain comments in output. +.It Fl Dmacro[=expansion] +Provide a definition for the named macro. +If no expansion is provided, the value +.Dq 1 +is used. +Note that like many Unix compilers, +.Nm +does not accept a space between the +.Dq D +and the macro name. +.It Fl Ipath +Add the specified path to the main list of include directories. +Note that like many Unix compilers, +.Nm +does not accept a space between the +.Dq I +and the directory name. +.It Fl nostdinc +Do not search the standard system include directories. +.It Fl P +Suppress line number information in the output. +Currently line number information is not generated at all and this +option has no effect. +.It Fl Umacro +Remove any existing defintion for the named macro. +Note that like many Unix compilers, +.Nm +does not accept a space between the +.Dq U +and the macro name. +.It Fl undef +Remove all predefined macros. +.El +.Ss Warning Options +Warning options can be disabled or enabled by inserting, or not, the +string +.Dq no- +between the +.Dq W +and the warning name. +Herein the +.Dq Fl Wno- +form is shown for options that are enabled by default. +.Bl -tag -width bubblebabble +.It Fl Wall +Turn on all warnings. +The option +.Fl Wno-all +disables only the warnings that are disabled by default. +.It Fl w +Turn off all warnings. +.It Fl Werror +Make warnings into fatal errors. +.It Fl Wcomment +Warn about nested comments. +.It Fl Wno-endif-labels +Don't warn about symbols attached to #endif directives. +(The warning is currently not implemented.) +.It Fl Wundef +Warn about undefined symbols appearing in #if and #elif expressions. +.It Fl Wunused-macros +Warn about macros that are defined and never used. +Not implemented. +.El +.Ss Depend Options +.Bl -tag -width bubblebabble +.It Fl M +Generate dependency information for +.Xr make 1 +on the standard output, instead of preprocessing. +Not implemented. +.It Fl MD +Like +.Fl M +but skip system headers. +Not implemented. +.It Fl MM +Like +.Fl M +but write the dependency information to a file named after the input +file with extension +.Pa \.d +and preprocess normally to standard output. +Not implemented. +.It Fl MMD +Like +.Fl MM +but skip system headers. +Not implemented. +.It Fl MF Ar file +Send dependency output to the named file instead of the default +location. +Not implemented. +.It Fl MG +When generating dependency information, assume that missing files are +generated instead of failing. +Not implemented. +.It Fl MP +Issue dummy rules for all include files. +This prevents +.Xr make 1 +from choking if an include file is removed. +Not implemented. +.It Fl MQ Ar target +Same as +.Fl MT +except that any +.Xr make 1 +metacharacters appearing in the target are escaped. +.It Fl MT Ar target +Set the name of the +.Xr make 1 +target appearing in the generated dependency information. +The default is the name of the input file with its suffix replaced +with the suffix for object files, normally +.Pa .o . +.\" If this option is given more than once, all named targets will +.\" be emitted. +.\" (The current operating mode framework doesn't support that.) +.El +.Ss More Include Path Options +.Bl -tag -width bubblebabble +.It Fl idirafter Ar path +Add the specified path to the +.Dq afterwards +include path. +This path is searched after all directories specified with +.Fl I +and the standard system directories. +Directories on this path are treated as containing system include +files. +.It Fl imacros Ar file +Read in +.Ar file +prior to reading the main input file, and preprocess it, but throw +away the output and retain only the macro definitions. +.It Fl include Ar file +Read in and preprocess +.Ar file +prior to reading the main input file. +.It Fl iprefix Ar prefix +Set the path prefix used with the +.Fl iwithprefix +option. +.It Fl iquote Ar path +Add +.Ar path +to the list of directories searched for include directives written +with quotes. +This list is not searched for include directives written with angle +brackets. +.It Fl iremap Ar string:replacement +Substitute +.Ar replacement +for +.Ar string +in the +.Dv __FILE__ +built-in macro. +Not supported. +.It Fl isysroot Ar path +Use +.Ar path +as the +.Dq system root , +that is, the directory under which the standard system paths are found. +.It Fl isystem Ar path +Add +.Ar path +to the list of system include directories. +This list is searched after the list given with +.Ar I . +Files found on this path are treated as system headers. +.It Fl iwithprefix Ar dir +Splice +.Ar dir +onto the prefix given with +.Fl iprefix +and add this directory as if it were specified with +.Fl idirafter . +.It Fl iwithprefixbefore +Like +-Fl iwithprefix +but adds the result as if it were specified with +.Fl I . +.El +.Ss Diagnostic Options +.Bl -tag -width bubblebabble +.It Fl dD +Dump all macro definitions, except for the predefined macros, after +the normal preprocessing output. +Not implemented. +.It Fl dI +Dump all include directives along with the normal preprocessing +output. +Not implemented. +.It Fl dM +Dump all macro definitions instead of the normal preprocessing +output. +Not implemented. +.It Fl dN +Like +.Fl dD +but emits only macro names and not the expansions. +Not implemented. +.It Fl H +Output a trace of the include tree as it gets processed. +Not implemented. +.El +.Ss Other Options +.Bl -tag -width bubblebabble +.It Fl CC +Retain comments in output. +Same as +.Fl C , +accepted for compatibility with +.Xr gcc 1 . +.It Fl fdollars-in-identifiers , Fl fno-dollars-in-identifiers +Enable +.Pq or disable, respectively +the use of the dollar sign in identifiers. +Not implemented. +.It Fl ftabstop=num +Set the tab width to the specified value, for reporting column +positions in diagnostics. +The default is 8. +Not implemented. +.It Fl std=standard +Ask +.Nm +to conform to the named standard. +The default, and the only supported value, is +.Dq krc . +.It Fl traditional +This option is accepted for compatibility with +.Xr gcc 1 +and ignored. +.It Fl x Ar lang +Adjust the preprocessor for the given language. +The only values accepted for +.Ar lang +are +.Dq assembler-with-cpp +and +.Dq c , +neither of which have any effect on the behavior of +.Nm . +.El +.Sh FILES +The default list of directories searched for include files is: +.Bl -item -offset indent -compact +.It +.Pa /usr/local/include +.It +.Pa /usr/include +.El +.Sh SEE ALSO +.Xr cc 1 , +.Xr cpp 1 , +.Xr make 1 +.Sh STANDARDS +None. +The whole point of a traditional cpp is that it reflects practices +in pre-standardization implementations of C. +Some information is available from the first edition of Kernighan and +Ritchie. +Much of the rest of the behavior is based on lore, pragmatism, +material encountered in the wild, and comparison to other +implementations. +.Sh HISTORY +The original version of +.Nm +was written one evening in late 2010. +This version had some problems and was put aside. +The first working version was released in June 2013. +.\" .Sh AUTHORS +.\" .An David A. Holland +.Sh BUGS +Probably plenty. diff --git a/libexec/tradcpp/utils.c b/libexec/tradcpp/utils.c new file mode 100644 index 00000000000..d4ab7a119ba --- /dev/null +++ b/libexec/tradcpp/utils.c @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "utils.h" + +#define MALLOCDEBUG + +const char ws[] = + " \t\f\v" +; +const char alnum[] = + "0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "_" +; + +//////////////////////////////////////////////////////////// +// malloc + +#define ROUNDUP(len, size) ((size) * (((len) + (size) - 1) / (size))) + +#ifdef MALLOCDEBUG + +struct mallocheader { + struct mallocheader *self; + size_t len; +}; + +static +size_t +adjustsize(size_t len) +{ + const size_t sz = sizeof(struct mallocheader); + return ROUNDUP(len, sz) + 2*sz; +} + +static +void * +placeheaders(void *block, size_t len) +{ + struct mallocheader *bothdr, *tophdr; + size_t roundedlen; + void *ret; + + roundedlen = ROUNDUP(len, sizeof(struct mallocheader)); + bothdr = block; + bothdr->len = len; + bothdr->self = block; + ret = bothdr + 1; + tophdr = (void *)(((unsigned char *)ret) + roundedlen); + tophdr->len = len; + tophdr->self = bothdr; + return ret; +} + +static +void * +checkheaders(void *block, size_t len) +{ + struct mallocheader *bothdr, *tophdr; + size_t roundedlen; + + if (block == NULL) { + assert(len == 0); + return block; + } + + roundedlen = ROUNDUP(len, sizeof(struct mallocheader)); + bothdr = block; + bothdr--; + assert(bothdr->self == bothdr); + assert(bothdr->len == len); + tophdr = (void *)(((unsigned char *)(bothdr + 1)) + roundedlen); + assert(tophdr->self == bothdr); + assert(tophdr->len == len); + return bothdr; +} + +#else + +#define adjustsize(len) (len) +#define placeheaders(block, len) ((void)(len), (block)) +#define checkheaders(ptr, len) ((void)(len), (ptr)) + +#endif /* MALLOCDEBUG */ + +void * +domalloc(size_t len) +{ + void *ret; + size_t blocklen; + + blocklen = adjustsize(len); + ret = malloc(blocklen); + if (ret == NULL) { + complain(NULL, "Out of memory"); + die(); + } + + return placeheaders(ret, len); +} + +void * +dorealloc(void *ptr, size_t oldlen, size_t newlen) +{ + void *ret; + void *blockptr; + size_t newblocklen; + + blockptr = checkheaders(ptr, oldlen); + newblocklen = adjustsize(newlen); + + ret = realloc(blockptr, newblocklen); + if (ret == NULL) { + complain(NULL, "Out of memory"); + die(); + } + + return placeheaders(ret, newlen); +} + +void +dofree(void *ptr, size_t len) +{ + void *blockptr; + + blockptr = checkheaders(ptr, len); + free(blockptr); +} + +//////////////////////////////////////////////////////////// +// string allocators + +char * +dostrdup(const char *s) +{ + char *ret; + size_t len; + + len = strlen(s); + ret = domalloc(len+1); + strcpy(ret, s); + return ret; +} + +char * +dostrdup2(const char *s, const char *t) +{ + char *ret; + size_t len; + + len = strlen(s) + strlen(t); + ret = domalloc(len+1); + strcpy(ret, s); + strcat(ret, t); + return ret; +} + +char * +dostrdup3(const char *s, const char *t, const char *u) +{ + char *ret; + size_t len; + + len = strlen(s) + strlen(t) + strlen(u); + ret = domalloc(len+1); + strcpy(ret, s); + strcat(ret, t); + strcat(ret, u); + return ret; +} + +char * +dostrndup(const char *s, size_t len) +{ + char *ret; + + ret = domalloc(len+1); + memcpy(ret, s, len); + ret[len] = '\0'; + return ret; +} + +void +dostrfree(char *s) +{ + dofree(s, strlen(s)+1); +} + +//////////////////////////////////////////////////////////// +// other stuff + +size_t +notrailingws(char *buf, size_t len) +{ + while (len > 0 && strchr(ws, buf[len-1])) { + buf[--len] = '\0'; + } + return len; +} + +bool +is_identifier(const char *str) +{ + size_t len; + + len = strlen(str); + if (len != strspn(str, alnum)) { + return false; + } + if (str[0] >= '0' && str[0] <= '9') { + return false; + } + return true; +} diff --git a/libexec/tradcpp/utils.h b/libexec/tradcpp/utils.h new file mode 100644 index 00000000000..e6f1ae7286f --- /dev/null +++ b/libexec/tradcpp/utils.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +struct place; + +#if defined(__CLANG__) || defined(__GNUC__) +#define PF(a, b) __attribute__((__format__(__printf__, a, b))) +#define DEAD __attribute__((__noreturn__)) +#define UNUSED __attribute__((__unused__)) +#else +#define PF(a, b) +#define DEAD +#define UNUSED +#endif + +#define HOWMANY(arr) (sizeof(arr)/sizeof((arr)[0])) + +extern const char ws[]; +extern const char alnum[]; + + +void *domalloc(size_t len); +void *dorealloc(void *ptr, size_t oldlen, size_t newlen); +void dofree(void *ptr, size_t len); + +char *dostrdup(const char *s); +char *dostrdup2(const char *s, const char *t); +char *dostrdup3(const char *s, const char *t, const char *u); +char *dostrndup(const char *s, size_t len); +void dostrfree(char *s); + +size_t notrailingws(char *buf, size_t len); +bool is_identifier(const char *str); + +/* in place.c */ +void complain_init(const char *progname); +void complain(const struct place *, const char *fmt, ...) PF(2, 3); +void complain_fail(void); +bool complain_failed(void); + +/* in main.c */ +void freestringlater(char *s); +DEAD void die(void); diff --git a/libexec/tradcpp/version.h b/libexec/tradcpp/version.h new file mode 100644 index 00000000000..b278c662d11 --- /dev/null +++ b/libexec/tradcpp/version.h @@ -0,0 +1,33 @@ +/*- + * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define VERSION_MAJOR "0" +#define VERSION_MINOR "4" +#define VERSION_STRING "0.4" +#define VERSION_LONG "NetBSD tradcpp 0.4"