Add tradcpp 0.4, a standalone traditional whitespace preserving cpp
authorjsg <jsg@openbsd.org>
Wed, 30 Jul 2014 16:33:11 +0000 (16:33 +0000)
committerjsg <jsg@openbsd.org>
Wed, 30 Jul 2014 16:33:11 +0000 (16:33 +0000)
by David A. Holland of NetBSD.

23 files changed:
libexec/tradcpp/Makefile [new file with mode: 0644]
libexec/tradcpp/array.c [new file with mode: 0644]
libexec/tradcpp/array.h [new file with mode: 0644]
libexec/tradcpp/config.h [new file with mode: 0644]
libexec/tradcpp/directive.c [new file with mode: 0644]
libexec/tradcpp/directive.h [new file with mode: 0644]
libexec/tradcpp/eval.c [new file with mode: 0644]
libexec/tradcpp/eval.h [new file with mode: 0644]
libexec/tradcpp/files.c [new file with mode: 0644]
libexec/tradcpp/files.h [new file with mode: 0644]
libexec/tradcpp/inlinedefs.h [new file with mode: 0644]
libexec/tradcpp/macro.c [new file with mode: 0644]
libexec/tradcpp/macro.h [new file with mode: 0644]
libexec/tradcpp/main.c [new file with mode: 0644]
libexec/tradcpp/mode.h [new file with mode: 0644]
libexec/tradcpp/output.c [new file with mode: 0644]
libexec/tradcpp/output.h [new file with mode: 0644]
libexec/tradcpp/place.c [new file with mode: 0644]
libexec/tradcpp/place.h [new file with mode: 0644]
libexec/tradcpp/tradcpp.1 [new file with mode: 0644]
libexec/tradcpp/utils.c [new file with mode: 0644]
libexec/tradcpp/utils.h [new file with mode: 0644]
libexec/tradcpp/version.h [new file with mode: 0644]

diff --git a/libexec/tradcpp/Makefile b/libexec/tradcpp/Makefile
new file mode 100644 (file)
index 0000000..e15598e
--- /dev/null
@@ -0,0 +1,6 @@
+PROG=  tradcpp
+SRCS=  main.c \
+       files.c directive.c eval.c macro.c output.c \
+       place.c array.c utils.c
+
+.include <bsd.prog.mk>
diff --git a/libexec/tradcpp/array.c b/libexec/tradcpp/array.c
new file mode 100644 (file)
index 0000000..09ffa45
--- /dev/null
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <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--;
+}
diff --git a/libexec/tradcpp/array.h b/libexec/tradcpp/array.h
new file mode 100644 (file)
index 0000000..8d1b98b
--- /dev/null
@@ -0,0 +1,279 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ARRAY_H
+#define ARRAY_H
+
+#include "inlinedefs.h" // XXX
+#include "utils.h"
+
+#define ARRAYS_CHECKED
+
+#ifdef ARRAYS_CHECKED
+#include <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 */
diff --git a/libexec/tradcpp/config.h b/libexec/tradcpp/config.h
new file mode 100644 (file)
index 0000000..c8ad5f1
--- /dev/null
@@ -0,0 +1,158 @@
+/*-
+ * 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
diff --git a/libexec/tradcpp/directive.c b/libexec/tradcpp/directive.c
new file mode 100644 (file)
index 0000000..5c81de2
--- /dev/null
@@ -0,0 +1,626 @@
+/*-
+ * 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;
+}
diff --git a/libexec/tradcpp/directive.h b/libexec/tradcpp/directive.h
new file mode 100644 (file)
index 0000000..d4d5676
--- /dev/null
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <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);
+
diff --git a/libexec/tradcpp/eval.c b/libexec/tradcpp/eval.c
new file mode 100644 (file)
index 0000000..cb5db99
--- /dev/null
@@ -0,0 +1,765 @@
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <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;
+}
diff --git a/libexec/tradcpp/eval.h b/libexec/tradcpp/eval.h
new file mode 100644 (file)
index 0000000..0694fb4
--- /dev/null
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+
+bool eval(struct place *p, char *expr);
diff --git a/libexec/tradcpp/files.c b/libexec/tradcpp/files.c
new file mode 100644 (file)
index 0000000..f0ee45e
--- /dev/null
@@ -0,0 +1,420 @@
+/*-
+ * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <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(&quotepath);
+       incdirarray_init(&bracketpath);
+}
+
+DESTROYALL_ARRAY(incdir, );
+
+void
+files_cleanup(void)
+{
+       incdirarray_destroyall(&quotepath);
+       incdirarray_cleanup(&quotepath);
+       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(&quotepath, 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, &quotepath, 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);
+       }
+}
diff --git a/libexec/tradcpp/files.h b/libexec/tradcpp/files.h
new file mode 100644 (file)
index 0000000..97b459d
--- /dev/null
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+struct place;
+
+void files_init(void);
+void files_cleanup(void);
+
+void files_addquotepath(const char *dir, bool issystem);
+void files_addbracketpath(const char *dir, bool issystem);
+
+void file_readquote(struct place *, const char *name);
+void file_readbracket(struct place *, const char *name);
+void file_readabsolute(struct place *, const char *name);
diff --git a/libexec/tradcpp/inlinedefs.h b/libexec/tradcpp/inlinedefs.h
new file mode 100644 (file)
index 0000000..bfcef62
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009 David A. Holland.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Author nor the names of any contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(__GNUC__) && !defined(__GNUC_STDC_INLINE__)
+/* gcc's non-C99 inline semantics */
+#define C99INLINE extern inline
+#elif defined(__STDC__) && __STDC_VERSION__ >= 199901L
+/* C99 */
+#define C99INLINE inline
+#else
+/* something else; static inline is safest */
+#define C99INLINE static inline
+#endif
diff --git a/libexec/tradcpp/macro.c b/libexec/tradcpp/macro.c
new file mode 100644 (file)
index 0000000..2105980
--- /dev/null
@@ -0,0 +1,1202 @@
+/*-
+ * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <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(&macros);
+       macroarrayarray_setsize(&macros, 4);
+       for (i=0; i<4; i++) {
+               macroarrayarray_set(&macros, i, NULL);
+       }
+       total_macros = 0;
+       hashmask = 0x3;
+}
+
+DESTROYALL_ARRAY(macro, );
+
+static
+void
+macrotable_cleanup(void)
+{
+       struct macroarray *bucket;
+       unsigned numbuckets, i;
+
+       numbuckets = macroarrayarray_num(&macros);
+       for (i=0; i<numbuckets; i++) {
+               bucket = macroarrayarray_get(&macros, i);
+               if (bucket != NULL) {
+                       macroarray_destroyall(bucket);
+                       macroarray_destroy(bucket);
+               }
+       }
+       macroarrayarray_setsize(&macros, 0);
+       macroarrayarray_cleanup(&macros);
+}
+
+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(&macros, 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(&macros);
+       macroarrayarray_setsize(&macros, 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(&macros, i);
+               if (oldbucket == NULL) {
+                       macroarrayarray_set(&macros, 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(&macros, numbuckets + i, newbucket);
+       }
+}
+
+static
+void
+macrotable_add(struct macro *m)
+{
+       unsigned hash;
+       struct macroarray *bucket;
+       unsigned numbuckets;
+
+       numbuckets = macroarrayarray_num(&macros);
+       if (total_macros > 0 && total_macros / numbuckets > 9) {
+               macrotable_rehash();
+       }
+
+       hash = hashfunc(m->name, strlen(m->name));
+       bucket = macroarrayarray_get(&macros, hash & hashmask);
+       if (bucket == NULL) {
+               bucket = macroarray_create();
+               macroarrayarray_set(&macros, 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, &param)) {
+                               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();
+}
diff --git a/libexec/tradcpp/macro.h b/libexec/tradcpp/macro.h
new file mode 100644 (file)
index 0000000..98671e6
--- /dev/null
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <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 *);
diff --git a/libexec/tradcpp/main.c b/libexec/tradcpp/main.c
new file mode 100644 (file)
index 0000000..35f9bca
--- /dev/null
@@ -0,0 +1,1090 @@
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <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;
+}
diff --git a/libexec/tradcpp/mode.h b/libexec/tradcpp/mode.h
new file mode 100644 (file)
index 0000000..60f1636
--- /dev/null
@@ -0,0 +1,65 @@
+/*-
+ * 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;
diff --git a/libexec/tradcpp/output.c b/libexec/tradcpp/output.c
new file mode 100644 (file)
index 0000000..d1d1f2d
--- /dev/null
@@ -0,0 +1,193 @@
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <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;
+}
diff --git a/libexec/tradcpp/output.h b/libexec/tradcpp/output.h
new file mode 100644 (file)
index 0000000..c1cb662
--- /dev/null
@@ -0,0 +1,31 @@
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+void output(const struct place *p, const char *buf, size_t len);
+void output_eof(void);
diff --git a/libexec/tradcpp/place.c b/libexec/tradcpp/place.c
new file mode 100644 (file)
index 0000000..2671048
--- /dev/null
@@ -0,0 +1,241 @@
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <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);
+}
diff --git a/libexec/tradcpp/place.h b/libexec/tradcpp/place.h
new file mode 100644 (file)
index 0000000..311645b
--- /dev/null
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <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);
diff --git a/libexec/tradcpp/tradcpp.1 b/libexec/tradcpp/tradcpp.1
new file mode 100644 (file)
index 0000000..952d8b7
--- /dev/null
@@ -0,0 +1,353 @@
+.\"
+.\" Copyright (c) 2013 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by David A. Holland.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd June 11, 2013
+.Dt TRADCPP 1
+.Os
+.Sh NAME
+.Nm tradcpp
+.Nd traditional (K&R-style) C macro preprocessor
+.Sh SYNOPSIS
+.Nm tradcpp
+.Op Fl options
+.Op Ar input-file Op Ar output-file
+.Sh DESCRIPTION
+The
+.Nm
+command provides a traditional K&R-style C macro preprocessor.
+It is intended to be suitable for historical Unix uses of the
+preprocessor, such as
+.Xr imake 1 ,
+particularly those that depend on preservation of whitespace.
+.Pp
+The chief ways in which traditional cpp differs from
+Standard C are:
+.Bl -bullet -offset indent
+.It
+Macro arguments are expanded within quoted strings.
+There is no stringize operator.
+.It
+There is no token pasting operator; tokens can be concatenated by
+placing comments between them.
+This process is also not limited to valid C language tokens.
+.It
+Whitespace is preserved, and in particular tabs are not expanded into
+spaces.
+Furthermore, additional whitespace is not injected.
+.El
+.Sh OPTIONS
+.Nm
+has many options, many of which are defined for compatibility with
+.Xr gcc 1
+or other compilers.
+Many of the options are not yet implemented.
+.\" The option lists have been sorted in what I hope is a sensible
+.\" order. Please don't arbitrarily alphabetize them.
+.Ss Common Options
+.Bl -tag -width bubblebabble
+.It Fl C
+Retain comments in output.
+.It Fl Dmacro[=expansion]
+Provide a definition for the named macro.
+If no expansion is provided, the value
+.Dq 1
+is used.
+Note that like many Unix compilers,
+.Nm
+does not accept a space between the
+.Dq D
+and the macro name.
+.It Fl Ipath
+Add the specified path to the main list of include directories.
+Note that like many Unix compilers,
+.Nm
+does not accept a space between the
+.Dq I
+and the directory name.
+.It Fl nostdinc
+Do not search the standard system include directories.
+.It Fl P
+Suppress line number information in the output.
+Currently line number information is not generated at all and this
+option has no effect.
+.It Fl Umacro
+Remove any existing defintion for the named macro.
+Note that like many Unix compilers,
+.Nm
+does not accept a space between the
+.Dq U
+and the macro name.
+.It Fl undef
+Remove all predefined macros.
+.El
+.Ss Warning Options
+Warning options can be disabled or enabled by inserting, or not, the
+string
+.Dq no-
+between the
+.Dq W
+and the warning name.
+Herein the
+.Dq Fl Wno-
+form is shown for options that are enabled by default.
+.Bl -tag -width bubblebabble
+.It Fl Wall
+Turn on all warnings.
+The option
+.Fl Wno-all
+disables only the warnings that are disabled by default.
+.It Fl w
+Turn off all warnings.
+.It Fl Werror
+Make warnings into fatal errors.
+.It Fl Wcomment
+Warn about nested comments.
+.It Fl Wno-endif-labels
+Don't warn about symbols attached to #endif directives.
+(The warning is currently not implemented.)
+.It Fl Wundef
+Warn about undefined symbols appearing in #if and #elif expressions.
+.It Fl Wunused-macros
+Warn about macros that are defined and never used.
+Not implemented.
+.El
+.Ss Depend Options
+.Bl -tag -width bubblebabble
+.It Fl M
+Generate dependency information for
+.Xr make 1
+on the standard output, instead of preprocessing.
+Not implemented.
+.It Fl MD
+Like
+.Fl M
+but skip system headers.
+Not implemented.
+.It Fl MM
+Like
+.Fl M
+but write the dependency information to a file named after the input
+file with extension
+.Pa \.d
+and preprocess normally to standard output.
+Not implemented.
+.It Fl MMD
+Like
+.Fl MM
+but skip system headers.
+Not implemented.
+.It Fl MF Ar file
+Send dependency output to the named file instead of the default
+location.
+Not implemented.
+.It Fl MG
+When generating dependency information, assume that missing files are
+generated instead of failing.
+Not implemented.
+.It Fl MP
+Issue dummy rules for all include files.
+This prevents
+.Xr make 1
+from choking if an include file is removed.
+Not implemented.
+.It Fl MQ Ar target
+Same as
+.Fl MT
+except that any
+.Xr make 1
+metacharacters appearing in the target are escaped.
+.It Fl MT Ar target
+Set the name of the
+.Xr make 1
+target appearing in the generated dependency information.
+The default is the name of the input file with its suffix replaced
+with the suffix for object files, normally
+.Pa .o .
+.\" If this option is given more than once, all named targets will
+.\" be emitted.
+.\" (The current operating mode framework doesn't support that.)
+.El
+.Ss More Include Path Options
+.Bl -tag -width bubblebabble
+.It Fl idirafter Ar path
+Add the specified path to the
+.Dq afterwards
+include path.
+This path is searched after all directories specified with
+.Fl I
+and the standard system directories.
+Directories on this path are treated as containing system include
+files.
+.It Fl imacros Ar file
+Read in
+.Ar file
+prior to reading the main input file, and preprocess it, but throw
+away the output and retain only the macro definitions.
+.It Fl include Ar file
+Read in and preprocess
+.Ar file
+prior to reading the main input file.
+.It Fl iprefix Ar prefix
+Set the path prefix used with the
+.Fl iwithprefix
+option.
+.It Fl iquote Ar path
+Add
+.Ar path
+to the list of directories searched for include directives written
+with quotes.
+This list is not searched for include directives written with angle
+brackets.
+.It Fl iremap Ar string:replacement
+Substitute
+.Ar replacement
+for
+.Ar string
+in the
+.Dv __FILE__
+built-in macro.
+Not supported.
+.It Fl isysroot Ar path
+Use
+.Ar path
+as the
+.Dq system root ,
+that is, the directory under which the standard system paths are found.
+.It Fl isystem Ar path
+Add
+.Ar path
+to the list of system include directories.
+This list is searched after the list given with
+.Ar I .
+Files found on this path are treated as system headers.
+.It Fl iwithprefix Ar dir
+Splice
+.Ar dir
+onto the prefix given with
+.Fl iprefix
+and add this directory as if it were specified with
+.Fl idirafter .
+.It Fl iwithprefixbefore
+Like
+-Fl iwithprefix
+but adds the result as if it were specified with
+.Fl I .
+.El
+.Ss Diagnostic Options
+.Bl -tag -width bubblebabble
+.It Fl dD
+Dump all macro definitions, except for the predefined macros, after
+the normal preprocessing output.
+Not implemented.
+.It Fl dI
+Dump all include directives along with the normal preprocessing
+output.
+Not implemented.
+.It Fl dM
+Dump all macro definitions instead of the normal preprocessing
+output.
+Not implemented.
+.It Fl dN
+Like
+.Fl dD
+but emits only macro names and not the expansions.
+Not implemented.
+.It Fl H
+Output a trace of the include tree as it gets processed.
+Not implemented.
+.El
+.Ss Other Options
+.Bl -tag -width bubblebabble
+.It Fl CC
+Retain comments in output.
+Same as
+.Fl C ,
+accepted for compatibility with
+.Xr gcc 1 .
+.It Fl fdollars-in-identifiers , Fl fno-dollars-in-identifiers
+Enable
+.Pq or disable, respectively
+the use of the dollar sign in identifiers.
+Not implemented.
+.It Fl ftabstop=num
+Set the tab width to the specified value, for reporting column
+positions in diagnostics.
+The default is 8.
+Not implemented.
+.It Fl std=standard
+Ask
+.Nm
+to conform to the named standard.
+The default, and the only supported value, is
+.Dq krc .
+.It Fl traditional
+This option is accepted for compatibility with
+.Xr gcc 1
+and ignored.
+.It Fl x Ar lang
+Adjust the preprocessor for the given language.
+The only values accepted for
+.Ar lang
+are
+.Dq assembler-with-cpp
+and
+.Dq c ,
+neither of which have any effect on the behavior of
+.Nm .
+.El
+.Sh FILES
+The default list of directories searched for include files is:
+.Bl -item -offset indent -compact
+.It
+.Pa /usr/local/include
+.It
+.Pa /usr/include
+.El
+.Sh SEE ALSO
+.Xr cc 1 ,
+.Xr cpp 1 ,
+.Xr make 1
+.Sh STANDARDS
+None.
+The whole point of a traditional cpp is that it reflects practices
+in pre-standardization implementations of C.
+Some information is available from the first edition of Kernighan and
+Ritchie.
+Much of the rest of the behavior is based on lore, pragmatism,
+material encountered in the wild, and comparison to other
+implementations.
+.Sh HISTORY
+The original version of
+.Nm
+was written one evening in late 2010.
+This version had some problems and was put aside.
+The first working version was released in June 2013.
+.\" .Sh AUTHORS
+.\" .An David A. Holland
+.Sh BUGS
+Probably plenty.
diff --git a/libexec/tradcpp/utils.c b/libexec/tradcpp/utils.c
new file mode 100644 (file)
index 0000000..d4ab7a1
--- /dev/null
@@ -0,0 +1,246 @@
+/*-
+ * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <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;
+}
diff --git a/libexec/tradcpp/utils.h b/libexec/tradcpp/utils.h
new file mode 100644 (file)
index 0000000..e6f1ae7
--- /dev/null
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <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);
diff --git a/libexec/tradcpp/version.h b/libexec/tradcpp/version.h
new file mode 100644 (file)
index 0000000..b278c66
--- /dev/null
@@ -0,0 +1,33 @@
+/*-
+ * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define VERSION_MAJOR  "0"
+#define VERSION_MINOR  "4"
+#define VERSION_STRING "0.4"
+#define VERSION_LONG   "NetBSD tradcpp 0.4"