by David A. Holland of NetBSD.
--- /dev/null
+PROG= tradcpp
+SRCS= main.c \
+ files.c directive.c eval.c macro.c output.c \
+ place.c array.c utils.c
+
+.include <bsd.prog.mk>
--- /dev/null
+/*-
+ * 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 <stdlib.h>
+#include <string.h>
+
+#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--;
+}
--- /dev/null
+/*-
+ * 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 <assert.h>
+#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<num; i++) { \
+ t = T##array_get(arr, i); \
+ T##_destroy(t); \
+ } \
+ T##array_setsize(arr, 0); \
+ }
+
+
+////////////////////////////////////////////////////////////
+// basic array types
+
+DECLARRAY_BYTYPE(stringarray, char, ARRAYINLINE);
+DEFARRAY_BYTYPE(stringarray, char, ARRAYINLINE);
+
+#endif /* ARRAY_H */
--- /dev/null
+/*-
+ * 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.
+ */
+
+/*
+ * Config for predefined macros. If this doesn't do what you want you
+ * can set any or all of the CONFIG_ defines from the compiler command
+ * line; or patch the list in main.c; or both.
+ */
+
+/*
+ * Paths
+ */
+
+#ifndef CONFIG_LOCALINCLUDE
+#define CONFIG_LOCALINCLUDE "/usr/local/include"
+#endif
+
+#ifndef CONFIG_SYSTEMINCLUDE
+#define CONFIG_SYSTEMINCLUDE "/usr/include"
+#endif
+
+/*
+ * Operating system
+ */
+
+#ifndef CONFIG_OS
+#if defined(__NetBSD__)
+#define CONFIG_OS "__NetBSD__"
+#define CONFIG_OS_2 "__unix__"
+#elif defined(__FreeBSD__)
+#define CONFIG_OS "__FreeBSD__"
+#define CONFIG_OS_2 "__unix__"
+#elif defined(__OpenBSD__)
+#define CONFIG_OS "__OpenBSD__"
+#define CONFIG_OS_2 "__unix__"
+#elif defined(__DragonFly__)
+#define CONFIG_OS "__DragonFly__"
+#define CONFIG_OS_2 "__unix__"
+#elif defined(__bsdi__)
+#define CONFIG_OS "__bsdi__"
+#define CONFIG_OS_2 "__unix__"
+#elif defined(__sun)
+#define CONFIG_OS "__sun"
+#define CONFIG_OS_2 "__unix__"
+#elif defined(__sgi)
+#define CONFIG_OS "__sgi"
+#define CONFIG_OS_2 "__unix__"
+#elif defined(__SVR4)
+#define CONFIG_OS "__SVR4"
+#define CONFIG_OS_2 "__unix__"
+#elif defined(__APPLE__)
+#define CONFIG_OS "__APPLE__"
+#define CONFIG_OS_2 "__unix__"
+#elif defined(__linux__)
+#define CONFIG_OS "__linux__"
+#elif defined(__CYGWIN__)
+#define CONFIG_OS "__CYGWIN__"
+#elif defined(__INTERIX)
+#define CONFIG_OS "__INTERIX"
+#elif defined(__MINGW32)
+#define CONFIG_OS "__MINGW32"
+#else
+/* we could error... but let's instead assume generic unix */
+#define CONFIG_OS "__unix__"
+#endif
+#endif
+
+/*
+ * CPU
+ */
+
+#ifndef CONFIG_CPU
+#if defined(__x86_64__)
+#define CONFIG_CPU "__x86_64__"
+#define CONFIG_CPU_2 "__amd64__"
+#elif defined(__i386__) || defined(__i386)
+#define CONFIG_CPU "__i386__"
+#define CONFIG_CPU_2 "__i386"
+#elif defined(__sparc)
+#define CONFIG_CPU "__sparc"
+#elif defined(__mips)
+#define CONFIG_CPU "__mips"
+#elif defined(__mips__)
+#define CONFIG_CPU "__mips__"
+#elif defined(__mipsel__)
+#define CONFIG_CPU "__mipsel__"
+#elif defined(__POWERPC__)
+#define CONFIG_CPU "__POWERPC__"
+#elif defined(__POWERPC__)
+#define CONFIG_CPU "__powerpc__"
+#elif defined(__PPC__)
+#define CONFIG_CPU "__PPC__"
+#elif defined(__ppc__)
+#define CONFIG_CPU "__ppc__"
+#elif defined(__PPC64__)
+#define CONFIG_CPU "__PPC64__"
+#elif defined(__ppc64__)
+#define CONFIG_CPU "__ppc64__"
+#elif defined(__ARM__)
+#define CONFIG_CPU "__ARM__"
+#else
+/* let it go */
+#endif
+#endif
+
+/*
+ * Other stuff
+ */
+
+#ifndef CONFIG_SIZE
+#ifdef __LP64__
+#define CONFIG_SIZE "__LP64__"
+#else
+#define CONFIG_SIZE "__ILP32__"
+#endif
+#endif
+
+#ifndef CONFIG_BINFMT
+#ifdef __ELF__
+#define CONFIG_BINFMT "__ELF__"
+#endif
+#endif
+
+/*
+ * We are __TRADCPP__ by default, but if you want to masquerade as
+ * some other compiler this is a convenient place to change it.
+ */
+
+#ifndef CONFIG_COMPILER
+#define CONFIG_COMPILER "__TRADCPP__"
+#define CONFIG_COMPILER_MINOR "__TRADCPP_MINOR__"
+#endif
--- /dev/null
+/*-
+ * 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 <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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; i<numdirectives; i++) {
+ len = strlen(directives[i].name);
+ if (!strncmp(line, directives[i].name, len) &&
+ strchr(ws, line[len])) {
+ if (directives[i].ifskip && !ifstate->curtrue) {
+ 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;
+}
--- /dev/null
+/*-
+ * 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 <stddef.h>
+
+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);
+
--- /dev/null
+/*-
+ * 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 <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+//#define DEBUG
+#ifdef DEBUG
+#include <stdio.h>
+#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; i<num; i++) {
+ t = tokenarray_get(&tokens, i);
+ switch (t->tok) {
+ case T_EOF: fprintf(stderr, " <eof>"); 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; i<num_tokens_2; i++) {
+ if (expr[pos] == tokens_2[i].c1 &&
+ expr[pos+1] == tokens_2[i].c2) {
+ token(p, tokens_2[i].tok, 0);
+ return true;
+ }
+ }
+ return false;
+}
+
+static
+bool
+check_tokens_1(struct place *p, char *expr, size_t pos)
+{
+ unsigned i;
+
+ for (i=0; i<num_tokens_1; i++) {
+ if (expr[pos] == tokens_1[i].c1) {
+ token(p, tokens_1[i].tok, 0);
+ return true;
+ }
+ }
+ return false;
+}
+
+static
+void
+tokenize(struct place *p, char *expr)
+{
+ size_t pos, len;
+
+ pos = 0;
+ while (expr[pos] != '\0') {
+ len = strspn(expr+pos, ws);
+ pos += len;
+ p->column += 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;
+}
--- /dev/null
+/*-
+ * 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 <stdbool.h>
+
+bool eval(struct place *p, char *expr);
--- /dev/null
+/*-
+ * 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 <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#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<limit; i++) {
+ if (incomment) {
+ if (i+1 < limit && buf[i] == '*' && buf[i+1] == '/') {
+ i++;
+ incomment = 0;
+ }
+ } else if (!inquote && i+1 < limit &&
+ buf[i] == '/' && buf[i+1] == '*') {
+ i++;
+ incomment = 1;
+ } else if (i+1 < limit &&
+ buf[i] == '\\' && buf[i+1] != '\n') {
+ i++;
+ } else if (!inquote && (buf[i] == '"' || buf[i] == '\'')) {
+ inquote = true;
+ quote = buf[i];
+ } else if (inquote && buf[i] == quote) {
+ inquote = false;
+ } else if (buf[i] == '\n') {
+ return i;
+ }
+ }
+ return limit;
+}
+
+static
+unsigned
+countnls(const char *buf, size_t start, size_t limit)
+{
+ size_t i;
+ unsigned count = 0;
+
+ for (i=start; i<limit; i++) {
+ if (buf[i] == '\n') {
+ count++;
+ }
+ }
+ return count;
+}
+
+static
+void
+file_read(const struct placefile *pf, int fd, const char *name, bool toplevel)
+{
+ struct place linestartplace, nextlinestartplace, ptmp;
+ size_t bufend, bufmax, linestart, lineend, nextlinestart, tmp;
+ ssize_t result;
+ bool ateof = false;
+ char *buf;
+
+ place_setfilestart(&linestartplace, pf);
+ nextlinestartplace = linestartplace;
+
+ bufmax = 128;
+ bufend = 0;
+ linestart = 0;
+ lineend = 0;
+ buf = domalloc(bufmax);
+
+ while (1) {
+ if (lineend >= 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; i<num; i++) {
+ id = incdirarray_get(path, i);
+ file = mkfilename(place, id->name, 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, "<standard-input>", 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);
+ }
+}
--- /dev/null
+/*-
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*-
+ * 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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; i<num1; i++) {
+ ei1 = expansionitemarray_get(&m1->expansion, 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; i<num1; i++) {
+ p1 = stringarray_get(&m1->params, 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; i<len; i+=2) {
+ if (i==len-1) {
+ a = (unsigned char)s[i];
+ /* don't run off the end of the array */
+ }
+ else {
+ a = (unsigned char)s[i] +
+ ((uint16_t)(unsigned char)s[i+1] << 8);
+ }
+ x1 += a;
+ if (x1 < a) {
+ x1++;
+ }
+ x2 += x1;
+ if (x2 < x1) {
+ x2++;
+ }
+ }
+
+ x1 ^= 0xffff;
+ x2 ^= 0xffff;
+ return ((uint32_t)x2)*65535U + x1;
+}
+
+static
+void
+macrotable_init(void)
+{
+ unsigned i;
+
+ macroarrayarray_init(¯os);
+ macroarrayarray_setsize(¯os, 4);
+ for (i=0; i<4; i++) {
+ macroarrayarray_set(¯os, i, NULL);
+ }
+ total_macros = 0;
+ hashmask = 0x3;
+}
+
+DESTROYALL_ARRAY(macro, );
+
+static
+void
+macrotable_cleanup(void)
+{
+ struct macroarray *bucket;
+ unsigned numbuckets, i;
+
+ numbuckets = macroarrayarray_num(¯os);
+ for (i=0; i<numbuckets; i++) {
+ bucket = macroarrayarray_get(¯os, i);
+ if (bucket != NULL) {
+ macroarray_destroyall(bucket);
+ macroarray_destroy(bucket);
+ }
+ }
+ macroarrayarray_setsize(¯os, 0);
+ macroarrayarray_cleanup(¯os);
+}
+
+static
+struct macro *
+macrotable_findlen(const char *name, size_t len, bool remove)
+{
+ unsigned hash;
+ struct macroarray *bucket;
+ struct macro *m, *m2;
+ unsigned i, num;
+ size_t mlen;
+
+ hash = hashfunc(name, len);
+ bucket = macroarrayarray_get(¯os, hash & hashmask);
+ if (bucket == NULL) {
+ return NULL;
+ }
+ num = macroarray_num(bucket);
+ for (i=0; i<num; i++) {
+ m = macroarray_get(bucket, i);
+ if (hash != m->hash) {
+ 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; i<numbuckets; i++) {
+ newbucket = NULL;
+ oldbucket = macroarrayarray_get(¯os, i);
+ if (oldbucket == NULL) {
+ macroarrayarray_set(¯os, numbuckets + i, NULL);
+ continue;
+ }
+ oldnum = macroarray_num(oldbucket);
+ for (j=0; j<oldnum; j++) {
+ m = macroarray_get(oldbucket, j);
+ if (m->hash & tossbit) {
+ if (newbucket == NULL) {
+ newbucket = macroarray_create();
+ }
+ macroarray_set(oldbucket, j, NULL);
+ macroarray_add(newbucket, m, NULL);
+ }
+ }
+ for (j=k=0; j<oldnum; j++) {
+ m = macroarray_get(oldbucket, j);
+ if (m != NULL) {
+ if (k < j) {
+ macroarray_set(oldbucket, k, m);
+ }
+ k++;
+ }
+ }
+ macroarray_setsize(oldbucket, k);
+ macroarrayarray_set(¯os, numbuckets + i, newbucket);
+ }
+}
+
+static
+void
+macrotable_add(struct macro *m)
+{
+ unsigned hash;
+ struct macroarray *bucket;
+ unsigned numbuckets;
+
+ numbuckets = macroarrayarray_num(¯os);
+ if (total_macros > 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; i<num; i++) {
+ param = stringarray_get(&m->params, 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; i<num; i++) {
+ dostrfree(stringarray_get(&es->args, 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; i<num; i++) {
+ ei = expansionitemarray_get(&es->curmacro->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; i<num; i++) {
+ ei = expansionitemarray_get(&es->curmacro->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();
+}
--- /dev/null
+/*-
+ * 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 <stdbool.h>
+#include <stddef.h>
+
+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 *);
--- /dev/null
+/*-
+ * 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 <stdbool.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#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; i<num; i++) {
+ cm = array_get(&commandline_macros, i);
+ dofree(cm, sizeof(*cm));
+ }
+ array_setsize(&commandline_macros, 0);
+
+ array_cleanup(&commandline_macros);
+}
+
+static
+void
+commandline_macro_add(const struct place *p, const char *macro,
+ const struct place *p2, const char *expansion)
+{
+ struct commandline_macro *cm;
+
+ cm = domalloc(sizeof(*cm));
+ cm->where = *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; i<num; i++) {
+ cm = array_get(&commandline_macros, i);
+ if (cm->expansion != 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; i<num; i++) {
+ cf = array_get(&commandline_files, i);
+ if (cf != NULL) {
+ dofree(cf, sizeof(*cf));
+ }
+ }
+ array_setsize(&commandline_files, 0);
+
+ array_cleanup(&commandline_files);
+}
+
+static
+void
+commandline_addfile(const struct place *p, char *name, bool suppress_output)
+{
+ struct commandline_file *cf;
+
+ cf = domalloc(sizeof(*cf));
+ cf->where = *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; i<num; i++) {
+ cf = array_get(&commandline_files, i);
+ array_set(&commandline_files, i, NULL);
+ if (cf->suppress_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<num; i++) {
+ dir = stringarray_get(&incpath_quote, i);
+ files_addquotepath(dir, false);
+ }
+ files_addquotepath(NULL, false);
+
+ num = stringarray_num(&incpath_user);
+ for (i=0; i<num; i++) {
+ dir = stringarray_get(&incpath_user, i);
+ files_addquotepath(dir, false);
+ files_addbracketpath(dir, false);
+ }
+
+ if (mode.do_stdinc) {
+ if (sysroot != NULL) {
+ t = dostrdup3(sysroot, "/", CONFIG_LOCALINCLUDE);
+ freestringlater(t);
+ dir = t;
+ } else {
+ dir = CONFIG_LOCALINCLUDE;
+ }
+ files_addquotepath(dir, true);
+ files_addbracketpath(dir, true);
+
+ if (sysroot != NULL) {
+ t = dostrdup3(sysroot, "/", CONFIG_SYSTEMINCLUDE);
+ freestringlater(t);
+ dir = t;
+ } else {
+ dir = CONFIG_SYSTEMINCLUDE;
+ }
+ files_addquotepath(dir, true);
+ files_addbracketpath(dir, true);
+ }
+
+ num = stringarray_num(&incpath_system);
+ for (i=0; i<num; i++) {
+ dir = stringarray_get(&incpath_system, i);
+ files_addquotepath(dir, true);
+ files_addbracketpath(dir, true);
+ }
+
+ num = stringarray_num(&incpath_late);
+ for (i=0; i<num; i++) {
+ dir = stringarray_get(&incpath_late, i);
+ files_addquotepath(dir, false);
+ files_addbracketpath(dir, false);
+ }
+}
+
+////////////////////////////////////////////////////////////
+// silly commandline stuff
+
+static const char *commandline_prefix;
+
+static
+void
+commandline_setprefix(const struct place *p, char *prefix)
+{
+ (void)p;
+ commandline_prefix = prefix;
+}
+
+static
+void
+commandline_addincpath_user_withprefix(const struct place *p, char *dir)
+{
+ char *s;
+
+ if (commandline_prefix == NULL) {
+ complain(NULL, "-iprefix needed");
+ die();
+ }
+ s = dostrdup3(commandline_prefix, "/", dir);
+ freestringlater(s);
+ commandline_addincpath_user(p, s);
+}
+
+static
+void
+commandline_addincpath_late_withprefix(const struct place *p, char *dir)
+{
+ char *s;
+
+ if (commandline_prefix == NULL) {
+ complain(NULL, "-iprefix needed");
+ die();
+ }
+ s = dostrdup3(commandline_prefix, "/", dir);
+ freestringlater(s);
+ commandline_addincpath_late(p, s);
+}
+
+static
+void
+commandline_setstd(const struct place *p, char *std)
+{
+ (void)p;
+
+ if (!strcmp(std, "krc")) {
+ return;
+ }
+ complain(NULL, "Standard %s not supported by this preprocessor", std);
+ die();
+}
+
+static
+void
+commandline_setlang(const struct place *p, char *lang)
+{
+ (void)p;
+
+ if (!strcmp(lang, "c") || !strcmp(lang, "assembler-with-cpp")) {
+ return;
+ }
+ complain(NULL, "Language %s not supported by this preprocessor", lang);
+ die();
+}
+
+////////////////////////////////////////////////////////////
+// complex modes
+
+DEAD static
+void
+commandline_iremap(const struct place *p, char *str)
+{
+ (void)p;
+ /* XXX */
+ (void)str;
+ complain(NULL, "-iremap not supported");
+ die();
+}
+
+static
+void
+commandline_tabstop(const struct place *p, char *s)
+{
+ char *t;
+ unsigned long val;
+
+ (void)p;
+
+ t = strchr(s, '=');
+ if (t == NULL) {
+ /* should not happen */
+ complain(NULL, "Invalid tabstop");
+ die();
+ }
+ t++;
+ errno = 0;
+ val = strtoul(t, &t, 10);
+ if (errno || *t != '\0') {
+ complain(NULL, "Invalid tabstop");
+ die();
+ }
+ if (val > 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<num_ignore_options; i++) {
+ r = strcmp(opt, ignore_options[i].string);
+ if (r == 0) {
+ return true;
+ }
+ if (r < 0) {
+ break;
+ }
+ }
+ return false;
+}
+
+static
+bool
+check_flag_option(const char *opt)
+{
+ unsigned i;
+ int r;
+
+ for (i=0; i<num_flag_options; i++) {
+ r = strcmp(opt, flag_options[i].string);
+ if (r == 0) {
+ *flag_options[i].flag = flag_options[i].setto;
+ return true;
+ }
+ if (r < 0) {
+ break;
+ }
+ }
+ return false;
+}
+
+static
+bool
+check_act_option(const char *opt)
+{
+ unsigned i;
+ int r;
+
+ for (i=0; i<num_act_options; i++) {
+ r = strcmp(opt, act_options[i].string);
+ if (r == 0) {
+ act_options[i].func();
+ return true;
+ }
+ if (r < 0) {
+ break;
+ }
+ }
+ return false;
+}
+
+static
+bool
+check_prefix_option(const struct place *p, char *opt)
+{
+ unsigned i, len;
+ int r;
+
+ for (i=0; i<num_prefix_options; i++) {
+ len = strlen(prefix_options[i].string);
+ r = strncmp(opt, prefix_options[i].string, len);
+ if (r == 0) {
+ prefix_options[i].func(p, opt + len);
+ return true;
+ }
+ if (r < 0) {
+ break;
+ }
+ }
+ return false;
+}
+
+static
+bool
+check_arg_option(const char *opt, const struct place *argplace, char *arg)
+{
+ unsigned i;
+ int r;
+
+ for (i=0; i<num_arg_options; i++) {
+ r = strcmp(opt, arg_options[i].string);
+ if (r == 0) {
+ if (arg == NULL) {
+ complain(NULL,
+ "Option -%s requires an argument",
+ opt);
+ die();
+ }
+ arg_options[i].func(argplace, arg);
+ return true;
+ }
+ if (r < 0) {
+ break;
+ }
+ }
+ return false;
+}
+
+DEAD static
+void
+usage(const char *progname, const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "%s: ", progname);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "Usage: %s [options] [infile [outfile]]\n", progname);
+ fprintf(stderr, "Common options:\n");
+ fprintf(stderr, " -C Retain comments\n");
+ fprintf(stderr, " -Dmacro[=def] Predefine macro\n");
+ fprintf(stderr, " -Idir Add to include path\n");
+ fprintf(stderr, " -M Issue depend info\n");
+ fprintf(stderr, " -MD Issue depend info and output\n");
+ fprintf(stderr, " -MM -M w/o system headers\n");
+ fprintf(stderr, " -MMD -MD w/o system headers\n");
+ fprintf(stderr, " -nostdinc Drop default include path\n");
+ fprintf(stderr, " -Umacro Undefine macro\n");
+ fprintf(stderr, " -undef Undefine everything\n");
+ fprintf(stderr, " -Wall Enable all warnings\n");
+ fprintf(stderr, " -Werror Make warnings into errors\n");
+ fprintf(stderr, " -w Disable all warnings\n");
+ die();
+}
+
+////////////////////////////////////////////////////////////
+// exit and cleanup
+
+static struct stringarray freestrings;
+
+static
+void
+init(void)
+{
+ stringarray_init(&freestrings);
+
+ incpath_init();
+ commandline_macros_init();
+ commandline_files_init();
+
+ place_init();
+ files_init();
+ directive_init();
+ macros_init();
+}
+
+static
+void
+cleanup(void)
+{
+ unsigned i, num;
+
+ macros_cleanup();
+ directive_cleanup();
+ files_cleanup();
+ place_cleanup();
+
+ commandline_files_cleanup();
+ commandline_macros_cleanup();
+ incpath_cleanup();
+
+ num = stringarray_num(&freestrings);
+ for (i=0; i<num; i++) {
+ dostrfree(stringarray_get(&freestrings, i));
+ }
+ stringarray_setsize(&freestrings, 0);
+ stringarray_cleanup(&freestrings);
+}
+
+void
+die(void)
+{
+ cleanup();
+ exit(EXIT_FAILURE);
+}
+
+void
+freestringlater(char *s)
+{
+ stringarray_add(&freestrings, s, NULL);
+}
+
+////////////////////////////////////////////////////////////
+// main
+
+int
+main(int argc, char *argv[])
+{
+ const char *progname;
+ const char *inputfile = NULL;
+ const char *outputfile = NULL;
+ struct place cmdplace;
+ int i;
+
+ progname = strrchr(argv[0], '/');
+ progname = progname == NULL ? argv[0] : progname + 1;
+ complain_init(progname);
+
+ init();
+
+ for (i=1; i<argc; i++) {
+ if (argv[i][0] != '-') {
+ break;
+ }
+ place_setcommandline(&cmdplace, i, 1);
+ if (check_ignore_option(argv[i]+1)) {
+ continue;
+ }
+ if (check_flag_option(argv[i]+1)) {
+ continue;
+ }
+ if (check_act_option(argv[i]+1)) {
+ continue;
+ }
+ if (check_prefix_option(&cmdplace, argv[i]+1)) {
+ continue;
+ }
+ place_setcommandline(&cmdplace, i+1, 1);
+ if (check_arg_option(argv[i]+1, &cmdplace, argv[i+1])) {
+ i++;
+ continue;
+ }
+ usage(progname, "Invalid option %s", argv[i]);
+ }
+ if (i < argc) {
+ inputfile = argv[i++];
+ }
+ if (i < argc) {
+ outputfile = argv[i++];
+ }
+ if (i < argc) {
+ usage(progname, "Extra non-option argument %s", argv[i]);
+ }
+
+ mode.output_file = outputfile;
+
+ loadincludepath();
+ apply_builtin_macros();
+ apply_commandline_macros();
+ read_commandline_files();
+ place_setnowhere(&cmdplace);
+ file_readabsolute(&cmdplace, inputfile);
+
+ cleanup();
+ if (complain_failed()) {
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
--- /dev/null
+/*-
+ * 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 <stdbool.h>
+
+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;
--- /dev/null
+/*-
+ * 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 <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#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;
+}
--- /dev/null
+/*-
+ * 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);
--- /dev/null
+/*-
+ * 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 <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 "<nowhere>";
+ case P_BUILTIN: return "<built-in>";
+ case P_COMMANDLINE: return "<command-line>";
+ 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);
+}
--- /dev/null
+/*-
+ * 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 <stdbool.h>
+
+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);
--- /dev/null
+.\"
+.\" 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.
--- /dev/null
+/*-
+ * 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 <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#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;
+}
--- /dev/null
+/*-
+ * 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 <stdbool.h>
+#include <stddef.h>
+
+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);
--- /dev/null
+/*-
+ * 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"