From 215be15f16efa82b811f473b3fc658780a37a4f2 Mon Sep 17 00:00:00 2001 From: Jon Trulson Date: Wed, 30 Oct 2019 19:17:24 -0600 Subject: [PATCH] Add tradcpp to the repository, and use it as GENCPP (generic preprocessor) With lots of experimenting, tradcpp (0.4) seems to work way better than gcc, and it's output is actually valid. We'll go with that for now. --- cde/Makefile.am | 2 +- cde/util/Makefile.am | 5 + cde/util/tradcpp/CHANGES | 41 + cde/util/tradcpp/Makefile.am | 26 + cde/util/tradcpp/Makefile.org | 12 + cde/util/tradcpp/TODO | 33 + cde/util/tradcpp/array.c | 115 +++ cde/util/tradcpp/array.h | 279 ++++++ cde/util/tradcpp/config.h | 158 ++++ cde/util/tradcpp/directive.c | 626 ++++++++++++++ cde/util/tradcpp/directive.h | 39 + cde/util/tradcpp/eval.c | 765 ++++++++++++++++ cde/util/tradcpp/eval.h | 32 + cde/util/tradcpp/files.c | 420 +++++++++ cde/util/tradcpp/files.h | 40 + cde/util/tradcpp/inlinedefs.h | 39 + cde/util/tradcpp/macro.c | 1202 ++++++++++++++++++++++++++ cde/util/tradcpp/macro.h | 49 ++ cde/util/tradcpp/main.c | 1090 +++++++++++++++++++++++ cde/util/tradcpp/make.jon.sh | 1 + cde/util/tradcpp/mode.h | 65 ++ cde/util/tradcpp/output.c | 193 +++++ cde/util/tradcpp/output.h | 31 + cde/util/tradcpp/place.c | 241 ++++++ cde/util/tradcpp/place.h | 56 ++ cde/util/tradcpp/tests/subdir/test.h | 1 + cde/util/tradcpp/tests/t01.c | 1 + cde/util/tradcpp/tests/t01.good | 1 + cde/util/tradcpp/tests/t02.c | 2 + cde/util/tradcpp/tests/t02.good | 1 + cde/util/tradcpp/tests/t03.c | 2 + cde/util/tradcpp/tests/t03.good | 1 + cde/util/tradcpp/tests/t04.c | 2 + cde/util/tradcpp/tests/t04.good | 1 + cde/util/tradcpp/tests/t05.c | 2 + cde/util/tradcpp/tests/t05.good | 1 + cde/util/tradcpp/tests/t06.c | 2 + cde/util/tradcpp/tests/t06.good | 1 + cde/util/tradcpp/tests/t07.c | 3 + cde/util/tradcpp/tests/t07.good | 1 + cde/util/tradcpp/tests/t08.c | 41 + cde/util/tradcpp/tests/t08.good | 21 + cde/util/tradcpp/tests/t09.c | 6 + cde/util/tradcpp/tests/t09.good | 0 cde/util/tradcpp/tests/t10.c | 3 + cde/util/tradcpp/tests/t10.good | 2 + cde/util/tradcpp/tests/t11.c | 2 + cde/util/tradcpp/tests/t11.good | 1 + cde/util/tradcpp/tests/t12.c | 2 + cde/util/tradcpp/tests/t12.good | 1 + cde/util/tradcpp/tests/t13.c | 4 + cde/util/tradcpp/tests/t13.good | 2 + cde/util/tradcpp/tests/t14.c | 4 + cde/util/tradcpp/tests/t14.good | 3 + cde/util/tradcpp/tests/t15.c | 3 + cde/util/tradcpp/tests/t15.good | 2 + cde/util/tradcpp/tests/t16.c | 11 + cde/util/tradcpp/tests/t16.good | 11 + cde/util/tradcpp/tests/t17.c | 2 + cde/util/tradcpp/tests/t17.good | 1 + cde/util/tradcpp/tests/t18.c | 2 + cde/util/tradcpp/tests/t18.good | 0 cde/util/tradcpp/tests/t19.c | 3 + cde/util/tradcpp/tests/t19.good | 1 + cde/util/tradcpp/tests/t20.c | 1 + cde/util/tradcpp/tests/t20.good | 0 cde/util/tradcpp/tests/t21.c | 3 + cde/util/tradcpp/tests/t21.good | 2 + cde/util/tradcpp/tests/t22.c | 2 + cde/util/tradcpp/tests/t22.good | 1 + cde/util/tradcpp/tests/t23.c | 8 + cde/util/tradcpp/tests/t23.good | 4 + cde/util/tradcpp/tests/t24.c | 67 ++ cde/util/tradcpp/tests/t24.good | 5 + cde/util/tradcpp/tests/t25.c | 4 + cde/util/tradcpp/tests/t25.good | 1 + cde/util/tradcpp/tests/t26.c | 4 + cde/util/tradcpp/tests/t26.good | 3 + cde/util/tradcpp/tests/t27.c | 29 + cde/util/tradcpp/tests/t27.good | 24 + cde/util/tradcpp/tests/t28.c | 53 ++ cde/util/tradcpp/tests/t28.good | 2 + cde/util/tradcpp/tests/t29.c | 4 + cde/util/tradcpp/tests/t29.good | 0 cde/util/tradcpp/tests/t30.c | 2 + cde/util/tradcpp/tests/t30.good | 1 + cde/util/tradcpp/tests/t31.c | 6 + cde/util/tradcpp/tests/t31.good | 3 + cde/util/tradcpp/tests/t32.c | 21 + cde/util/tradcpp/tests/t32.good | 12 + cde/util/tradcpp/tests/t33.c | 6 + cde/util/tradcpp/tests/t33.good | 3 + cde/util/tradcpp/tests/t34.c | 5 + cde/util/tradcpp/tests/t34.good | 1 + cde/util/tradcpp/tests/t35.c | 6 + cde/util/tradcpp/tests/t35.good | 1 + cde/util/tradcpp/tests/t36.c | 7 + cde/util/tradcpp/tests/t36.good | 4 + cde/util/tradcpp/tests/t37.c | 19 + cde/util/tradcpp/tests/t37.good | 9 + cde/util/tradcpp/tests/tradcpp.sh | 36 + cde/util/tradcpp/tradcpp.1 | 353 ++++++++ cde/util/tradcpp/utils.c | 246 ++++++ cde/util/tradcpp/utils.h | 72 ++ cde/util/tradcpp/version.h | 33 + 105 files changed, 6772 insertions(+), 1 deletion(-) create mode 100644 cde/util/Makefile.am create mode 100644 cde/util/tradcpp/CHANGES create mode 100644 cde/util/tradcpp/Makefile.am create mode 100644 cde/util/tradcpp/Makefile.org create mode 100644 cde/util/tradcpp/TODO create mode 100644 cde/util/tradcpp/array.c create mode 100644 cde/util/tradcpp/array.h create mode 100644 cde/util/tradcpp/config.h create mode 100644 cde/util/tradcpp/directive.c create mode 100644 cde/util/tradcpp/directive.h create mode 100644 cde/util/tradcpp/eval.c create mode 100644 cde/util/tradcpp/eval.h create mode 100644 cde/util/tradcpp/files.c create mode 100644 cde/util/tradcpp/files.h create mode 100644 cde/util/tradcpp/inlinedefs.h create mode 100644 cde/util/tradcpp/macro.c create mode 100644 cde/util/tradcpp/macro.h create mode 100644 cde/util/tradcpp/main.c create mode 100644 cde/util/tradcpp/make.jon.sh create mode 100644 cde/util/tradcpp/mode.h create mode 100644 cde/util/tradcpp/output.c create mode 100644 cde/util/tradcpp/output.h create mode 100644 cde/util/tradcpp/place.c create mode 100644 cde/util/tradcpp/place.h create mode 100644 cde/util/tradcpp/tests/subdir/test.h create mode 100644 cde/util/tradcpp/tests/t01.c create mode 100644 cde/util/tradcpp/tests/t01.good create mode 100644 cde/util/tradcpp/tests/t02.c create mode 100644 cde/util/tradcpp/tests/t02.good create mode 100644 cde/util/tradcpp/tests/t03.c create mode 100644 cde/util/tradcpp/tests/t03.good create mode 100644 cde/util/tradcpp/tests/t04.c create mode 100644 cde/util/tradcpp/tests/t04.good create mode 100644 cde/util/tradcpp/tests/t05.c create mode 100644 cde/util/tradcpp/tests/t05.good create mode 100644 cde/util/tradcpp/tests/t06.c create mode 100644 cde/util/tradcpp/tests/t06.good create mode 100644 cde/util/tradcpp/tests/t07.c create mode 100644 cde/util/tradcpp/tests/t07.good create mode 100644 cde/util/tradcpp/tests/t08.c create mode 100644 cde/util/tradcpp/tests/t08.good create mode 100644 cde/util/tradcpp/tests/t09.c create mode 100644 cde/util/tradcpp/tests/t09.good create mode 100644 cde/util/tradcpp/tests/t10.c create mode 100644 cde/util/tradcpp/tests/t10.good create mode 100644 cde/util/tradcpp/tests/t11.c create mode 100644 cde/util/tradcpp/tests/t11.good create mode 100644 cde/util/tradcpp/tests/t12.c create mode 100644 cde/util/tradcpp/tests/t12.good create mode 100644 cde/util/tradcpp/tests/t13.c create mode 100644 cde/util/tradcpp/tests/t13.good create mode 100644 cde/util/tradcpp/tests/t14.c create mode 100644 cde/util/tradcpp/tests/t14.good create mode 100644 cde/util/tradcpp/tests/t15.c create mode 100644 cde/util/tradcpp/tests/t15.good create mode 100644 cde/util/tradcpp/tests/t16.c create mode 100644 cde/util/tradcpp/tests/t16.good create mode 100644 cde/util/tradcpp/tests/t17.c create mode 100644 cde/util/tradcpp/tests/t17.good create mode 100644 cde/util/tradcpp/tests/t18.c create mode 100644 cde/util/tradcpp/tests/t18.good create mode 100644 cde/util/tradcpp/tests/t19.c create mode 100644 cde/util/tradcpp/tests/t19.good create mode 100644 cde/util/tradcpp/tests/t20.c create mode 100644 cde/util/tradcpp/tests/t20.good create mode 100644 cde/util/tradcpp/tests/t21.c create mode 100644 cde/util/tradcpp/tests/t21.good create mode 100644 cde/util/tradcpp/tests/t22.c create mode 100644 cde/util/tradcpp/tests/t22.good create mode 100644 cde/util/tradcpp/tests/t23.c create mode 100644 cde/util/tradcpp/tests/t23.good create mode 100644 cde/util/tradcpp/tests/t24.c create mode 100644 cde/util/tradcpp/tests/t24.good create mode 100644 cde/util/tradcpp/tests/t25.c create mode 100644 cde/util/tradcpp/tests/t25.good create mode 100644 cde/util/tradcpp/tests/t26.c create mode 100644 cde/util/tradcpp/tests/t26.good create mode 100644 cde/util/tradcpp/tests/t27.c create mode 100644 cde/util/tradcpp/tests/t27.good create mode 100644 cde/util/tradcpp/tests/t28.c create mode 100644 cde/util/tradcpp/tests/t28.good create mode 100644 cde/util/tradcpp/tests/t29.c create mode 100644 cde/util/tradcpp/tests/t29.good create mode 100644 cde/util/tradcpp/tests/t30.c create mode 100644 cde/util/tradcpp/tests/t30.good create mode 100644 cde/util/tradcpp/tests/t31.c create mode 100644 cde/util/tradcpp/tests/t31.good create mode 100644 cde/util/tradcpp/tests/t32.c create mode 100644 cde/util/tradcpp/tests/t32.good create mode 100644 cde/util/tradcpp/tests/t33.c create mode 100644 cde/util/tradcpp/tests/t33.good create mode 100644 cde/util/tradcpp/tests/t34.c create mode 100644 cde/util/tradcpp/tests/t34.good create mode 100644 cde/util/tradcpp/tests/t35.c create mode 100644 cde/util/tradcpp/tests/t35.good create mode 100644 cde/util/tradcpp/tests/t36.c create mode 100644 cde/util/tradcpp/tests/t36.good create mode 100644 cde/util/tradcpp/tests/t37.c create mode 100644 cde/util/tradcpp/tests/t37.good create mode 100644 cde/util/tradcpp/tests/tradcpp.sh create mode 100644 cde/util/tradcpp/tradcpp.1 create mode 100644 cde/util/tradcpp/utils.c create mode 100644 cde/util/tradcpp/utils.h create mode 100644 cde/util/tradcpp/version.h diff --git a/cde/Makefile.am b/cde/Makefile.am index 4d4c6ef15..22293e848 100644 --- a/cde/Makefile.am +++ b/cde/Makefile.am @@ -8,5 +8,5 @@ MAINTAINERCLEANFILES = Makefile.in \ config.h.in \ install-sh -SUBDIRS = lib programs doc +SUBDIRS = util lib programs doc diff --git a/cde/util/Makefile.am b/cde/util/Makefile.am new file mode 100644 index 000000000..97a96d6cc --- /dev/null +++ b/cde/util/Makefile.am @@ -0,0 +1,5 @@ +MAINTAINERCLEANFILES = Makefile.in + +SUBDIRS = tradcpp + + diff --git a/cde/util/tradcpp/CHANGES b/cde/util/tradcpp/CHANGES new file mode 100644 index 000000000..1eaaf5f31 --- /dev/null +++ b/cde/util/tradcpp/CHANGES @@ -0,0 +1,41 @@ +release 0.4 (20130713) + - Fix stupid build problem introduced in 0.3.1. + - Accept and ignore -m32, which imake issues willy-nilly on a bunch + of platforms. I thought this had already been done, but apparently + not. + - Don't use the functions. There are still people out there + using legacy systems missing them. + - Sort out some more issues pertaining to handling quoted strings. + - Add some more tests. + +release 0.3.1 (20130709) + - Don't leak memory and assert if a bad command-line option comes + after a -D or a -include foo. + - Since imake is a principal application for tradcpp and imake carefully + hides what it's doing when you run it, when rejecting an invalid option + be sure to report *what* that option is. + +release 0.3 (20130616) + - Don't eval the control expression of the first #if of a block when + already in a false block; it might not be valid. Reported by + Baptiste Daroussin. + - Don't recognize comments within character constants. + - Don't recognize macro argument parentheses or commas within strings, + or within character constants either. + +release 0.2 (20130611) + - auto-recognize more builtin PowerPC and mips macros + - pass -Wunused (partly from Baptiste Daroussin) + - allow absolute paths in include files (partly from Baptiste Daroussin) + - don't use getprogname() in the name of portability + - add tests arising from December 2010 tech-toolchain thread (one + from der Mouse, one of mine) + - clean out usage of sys/cdefs.h macros and don't use the implementation + namespace + - make -Wcomment work again + - fix handling of relative includes + - provide a man page + - other minor improvements + +release 0.1 (20130610) + - first release, works with at least some imake templates diff --git a/cde/util/tradcpp/Makefile.am b/cde/util/tradcpp/Makefile.am new file mode 100644 index 000000000..9b1f4cf99 --- /dev/null +++ b/cde/util/tradcpp/Makefile.am @@ -0,0 +1,26 @@ +MAINTAINERCLEANFILES = Makefile.in + +bin_PROGRAMS = tradcpp + +tradcpp_SOURCES = array.c \ + array.h \ + config.h \ + directive.c \ + directive.h \ + eval.c \ + eval.h \ + files.c \ + files.h \ + inlinedefs.h \ + macro.c \ + macro.h \ + main.c \ + mode.h \ + output.c \ + output.h \ + place.c \ + place.h \ + utils.c \ + utils.h \ + version.h + diff --git a/cde/util/tradcpp/Makefile.org b/cde/util/tradcpp/Makefile.org new file mode 100644 index 000000000..03f8791e8 --- /dev/null +++ b/cde/util/tradcpp/Makefile.org @@ -0,0 +1,12 @@ +# $NetBSD$ + +PROG= tradcpp +SRCS= main.c \ + files.c directive.c eval.c macro.c output.c \ + place.c array.c utils.c +WARNS= 5 + +#DBG=-g + +.include + diff --git a/cde/util/tradcpp/TODO b/cde/util/tradcpp/TODO new file mode 100644 index 000000000..cb306a14f --- /dev/null +++ b/cde/util/tradcpp/TODO @@ -0,0 +1,33 @@ +not implemented: + - mode.input_allow_dollars. + - column counts do not take tabstops into account. + - mode.output_linenumbers. + - mode.do_depend. + - mode.do_macrolist. + - mode.do_trace. + - warns.endiflabels. (they cause errors) + - warns.unused. + - the -iremap option. + - #line directives. + - $CPP_RESTRICTED + - other environment variables + +tidy up: + - get rid of inlinedefs.h + - use of places in and below macro.c is pretty bogus. + - macro code should be reworked. + +fix: + - "#if 0 && 1/0" should not crash; fix eval method. + - an unterminated comment is reported as "no newline at end of file" + (which isn't fatal by default) + - quote characters and comment delimiters that are emitted by + macros are not recognized. See: + t34 (should produce a quote and FOO Q) + t35 (similarly, this test may be redundant once it's fixed) + t36 (C(3) should produce nothing) + t37 (BC foo EC should produce nothing) + Joerg says comments like in t36 should be stripped exactly + twice, once when the macro is defined and again when it's + expanded. Note that gcc's cpp -traditional is getting t37 + wrong, and it gets t36 wrong with -C. diff --git a/cde/util/tradcpp/array.c b/cde/util/tradcpp/array.c new file mode 100644 index 000000000..09ffa45e8 --- /dev/null +++ b/cde/util/tradcpp/array.c @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#define ARRAYINLINE +#include "array.h" + +struct array * +array_create(void) +{ + struct array *a; + + a = domalloc(sizeof(*a)); + array_init(a); + return a; +} + +void +array_destroy(struct array *a) +{ + array_cleanup(a); + dofree(a, sizeof(*a)); +} + +void +array_init(struct array *a) +{ + a->num = a->max = 0; + a->v = NULL; +} + +void +array_cleanup(struct array *a) +{ + arrayassert(a->num == 0); + dofree(a->v, a->max * sizeof(a->v[0])); +#ifdef ARRAYS_CHECKED + a->v = NULL; +#endif +} + +void +array_setsize(struct array *a, unsigned num) +{ + unsigned newmax; + void **newptr; + + if (num > a->max) { + newmax = a->max; + while (num > newmax) { + newmax = newmax ? newmax*2 : 4; + } + newptr = dorealloc(a->v, a->max * sizeof(a->v[0]), + newmax * sizeof(a->v[0])); + a->v = newptr; + a->max = newmax; + } + a->num = num; +} + +void +array_insert(struct array *a, unsigned index_) +{ + unsigned movers; + + arrayassert(a->num <= a->max); + arrayassert(index_ < a->num); + + movers = a->num - index_; + + array_setsize(a, a->num + 1); + + memmove(a->v + index_+1, a->v + index_, movers*sizeof(*a->v)); +} + +void +array_remove(struct array *a, unsigned index_) +{ + unsigned movers; + + arrayassert(a->num <= a->max); + arrayassert(index_ < a->num); + + movers = a->num - (index_ + 1); + memmove(a->v + index_, a->v + index_+1, movers*sizeof(*a->v)); + a->num--; +} diff --git a/cde/util/tradcpp/array.h b/cde/util/tradcpp/array.h new file mode 100644 index 000000000..8d1b98b32 --- /dev/null +++ b/cde/util/tradcpp/array.h @@ -0,0 +1,279 @@ +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ARRAY_H +#define ARRAY_H + +#include "inlinedefs.h" // XXX +#include "utils.h" + +#define ARRAYS_CHECKED + +#ifdef ARRAYS_CHECKED +#include +#define arrayassert assert +#else +#define arrayassert(x) ((void)(x)) +#endif + +#ifndef ARRAYINLINE +#define ARRAYINLINE C99INLINE +#endif + +//////////////////////////////////////////////////////////// +// type and base operations + +struct array { + void **v; + unsigned num, max; +}; + +struct array *array_create(void); +void array_destroy(struct array *); +void array_init(struct array *); +void array_cleanup(struct array *); +ARRAYINLINE unsigned array_num(const struct array *); +ARRAYINLINE void *array_get(const struct array *, unsigned index_); +ARRAYINLINE void array_set(const struct array *, unsigned index_, void *val); +void array_setsize(struct array *, unsigned num); +ARRAYINLINE void array_add(struct array *, void *val, unsigned *index_ret); +void array_insert(struct array *a, unsigned index_); +void array_remove(struct array *a, unsigned index_); + +//////////////////////////////////////////////////////////// +// inlining for base operations + +ARRAYINLINE unsigned +array_num(const struct array *a) +{ + return a->num; +} + +ARRAYINLINE void * +array_get(const struct array *a, unsigned index_) +{ + arrayassert(index_ < a->num); + return a->v[index_]; +} + +ARRAYINLINE void +array_set(const struct array *a, unsigned index_, void *val) +{ + arrayassert(index_ < a->num); + a->v[index_] = val; +} + +ARRAYINLINE void +array_add(struct array *a, void *val, unsigned *index_ret) +{ + unsigned index_ = a->num; + array_setsize(a, index_+1); + a->v[index_] = val; + if (index_ret != NULL) { + *index_ret = index_; + } +} + +//////////////////////////////////////////////////////////// +// bits for declaring and defining typed arrays + +/* + * Usage: + * + * DECLARRAY_BYTYPE(foo, bar, INLINE) declares "struct foo", which is + * an array of pointers to "bar", plus the operations on it. + * + * DECLARRAY(foo, INLINE) is equivalent to + * DECLARRAY_BYTYPE(fooarray, struct foo, INLINE). + * + * DEFARRAY_BYTYPE and DEFARRAY are the same as DECLARRAY except that + * they define the operations. + * + * The argument INLINE can be used as follows: + * + * 1. For no inlining: + * In foo.h: + * DECLARRAY(foo, ); + * In foo.c: + * DEFARRAY(foo, ); + * + * 2. To be file-static: + * In foo.c: + * DECLARRAY(foo, static); + * DEFARRAY(foo, static); + * + * 3. To inline using C99: + * In foo.h: + * DECLARRAY(foo, inline); + * DEFARRAY(foo, inline); + * + * 4. To inline with old gcc: + * In foo.h: + * #ifndef FOO_INLINE + * #define FOO_INLINE extern inline + * #endif + * DECLARRAY(foo, ); + * DEFARRAY(foo, FOO_INLINE); + * In foo.c: + * #define FOO_INLINE + * #include "foo.h" + * + * 5. To inline such that it works both with old gcc and C99: + * In foo.h: + * #ifndef FOO_INLINE + * #define FOO_INLINE extern inline + * #endif + * DECLARRAY(foo, FOO_INLINE); + * DEFARRAY(foo, FOO_INLINE); + * In foo.c: + * #define FOO_INLINE + * #include "foo.h" + * + * The mechanism in case (4) ensures that an externally linkable + * definition exists. + */ + +#define DECLARRAY_BYTYPE(ARRAY, T, INLINE) \ + struct ARRAY { \ + struct array arr; \ + }; \ + \ + INLINE struct ARRAY *ARRAY##_create(void); \ + INLINE void ARRAY##_destroy(struct ARRAY *a); \ + INLINE void ARRAY##_init(struct ARRAY *a); \ + INLINE void ARRAY##_cleanup(struct ARRAY *a); \ + INLINE unsigned ARRAY##_num(const struct ARRAY *a); \ + INLINE T *ARRAY##_get(const struct ARRAY *a, unsigned index_); \ + INLINE void ARRAY##_set(struct ARRAY *a, unsigned index_, T *val); \ + INLINE void ARRAY##_setsize(struct ARRAY *a, unsigned num); \ + INLINE void ARRAY##_add(struct ARRAY *a, T *val, unsigned *index_ret);\ + INLINE void ARRAY##_insert(struct ARRAY *a, unsigned index_); \ + INLINE void ARRAY##_remove(struct ARRAY *a, unsigned index_) + + +#define DEFARRAY_BYTYPE(ARRAY, T, INLINE) \ + INLINE void \ + ARRAY##_init(struct ARRAY *a) \ + { \ + array_init(&a->arr); \ + } \ + \ + INLINE void \ + ARRAY##_cleanup(struct ARRAY *a) \ + { \ + array_cleanup(&a->arr); \ + } \ + \ + INLINE struct \ + ARRAY *ARRAY##_create(void) \ + { \ + struct ARRAY *a; \ + \ + a = domalloc(sizeof(*a)); \ + ARRAY##_init(a); \ + return a; \ + } \ + \ + INLINE void \ + ARRAY##_destroy(struct ARRAY *a) \ + { \ + ARRAY##_cleanup(a); \ + dofree(a, sizeof(*a)); \ + } \ + \ + INLINE unsigned \ + ARRAY##_num(const struct ARRAY *a) \ + { \ + return array_num(&a->arr); \ + } \ + \ + INLINE T * \ + ARRAY##_get(const struct ARRAY *a, unsigned index_) \ + { \ + return (T *)array_get(&a->arr, index_); \ + } \ + \ + INLINE void \ + ARRAY##_set(struct ARRAY *a, unsigned index_, T *val) \ + { \ + array_set(&a->arr, index_, (void *)val); \ + } \ + \ + INLINE void \ + ARRAY##_setsize(struct ARRAY *a, unsigned num) \ + { \ + array_setsize(&a->arr, num); \ + } \ + \ + INLINE void \ + ARRAY##_add(struct ARRAY *a, T *val, unsigned *ret) \ + { \ + array_add(&a->arr, (void *)val, ret); \ + } \ + \ + INLINE void \ + ARRAY##_insert(struct ARRAY *a, unsigned index_) \ + { \ + array_insert(&a->arr, index_); \ + } \ + \ + INLINE void \ + ARRAY##_remove(struct ARRAY *a, unsigned index_) \ + { \ + array_remove(&a->arr, index_); \ + } + +#define DECLARRAY(T, INLINE) DECLARRAY_BYTYPE(T##array, struct T, INLINE) +#define DEFARRAY(T, INLINE) DEFARRAY_BYTYPE(T##array, struct T, INLINE) + +#define DESTROYALL_ARRAY(T, INLINE) \ + void T##array_destroyall(struct T##array *arr); \ + \ + INLINE void \ + T##array_destroyall(struct T##array *arr) \ + { \ + unsigned i, num; \ + struct T *t; \ + \ + num = T##array_num(arr); \ + for (i=0; i +#include +#include +#include + +#include "utils.h" +#include "mode.h" +#include "place.h" +#include "files.h" +#include "directive.h" +#include "macro.h" +#include "eval.h" +#include "output.h" + +struct ifstate { + struct ifstate *prev; + struct place startplace; + bool curtrue; + bool evertrue; + bool seenelse; +}; + +static struct ifstate *ifstate; + +//////////////////////////////////////////////////////////// +// common parsing bits + +static +void +uncomment(char *buf) +{ + char *s, *t, *u = NULL; + bool incomment = false; + bool inesc = false; + bool inquote = false; + char quote = '\0'; + + for (s = t = buf; *s; s++) { + if (incomment) { + if (s[0] == '*' && s[1] == '/') { + s++; + incomment = false; + } + } else { + if (!inquote && s[0] == '/' && s[1] == '*') { + incomment = true; + } else { + if (inesc) { + inesc = false; + } else if (s[0] == '\\') { + inesc = true; + } else if (!inquote && + (s[0] == '"' || s[0] == '\'')) { + inquote = true; + quote = s[0]; + } else if (inquote && s[0] == quote) { + inquote = false; + } + + if (t != s) { + *t = *s; + } + if (!strchr(ws, *t)) { + u = t; + } + t++; + } + } + } + if (u) { + /* end string after last non-whitespace char */ + u[1] = '\0'; + } else { + *t = '\0'; + } +} + +static +void +oneword(const char *what, struct place *p2, char *line) +{ + size_t pos; + + pos = strcspn(line, ws); + if (line[pos] != '\0') { + p2->column += pos; + complain(p2, "Garbage after %s argument", what); + complain_fail(); + line[pos] = '\0'; + } +} + +//////////////////////////////////////////////////////////// +// if handling + +static +struct ifstate * +ifstate_create(struct ifstate *prev, struct place *p, bool startstate) +{ + struct ifstate *is; + + is = domalloc(sizeof(*is)); + is->prev = prev; + if (p != NULL) { + is->startplace = *p; + } else { + place_setbuiltin(&is->startplace, 1); + } + is->curtrue = startstate; + is->evertrue = is->curtrue; + is->seenelse = false; + return is; +} + +static +void +ifstate_destroy(struct ifstate *is) +{ + dofree(is, sizeof(*is)); +} + +static +void +ifstate_push(struct place *p, bool startstate) +{ + struct ifstate *newstate; + + newstate = ifstate_create(ifstate, p, startstate); + if (!ifstate->curtrue) { + newstate->curtrue = false; + newstate->evertrue = true; + } + ifstate = newstate; +} + +static +void +ifstate_pop(void) +{ + struct ifstate *is; + + is = ifstate; + ifstate = ifstate->prev; + ifstate_destroy(is); +} + +static +void +d_if(struct place *p, struct place *p2, char *line) +{ + char *expr; + bool val; + struct place p3 = *p2; + size_t oldlen; + + expr = macroexpand(p2, line, strlen(line), true); + + oldlen = strlen(expr); + uncomment(expr); + /* trim to fit, so the malloc debugging won't complain */ + expr = dorealloc(expr, oldlen + 1, strlen(expr) + 1); + + if (ifstate->curtrue) { + val = eval(&p3, expr); + } else { + val = 0; + } + ifstate_push(p, val); + dostrfree(expr); +} + +static +void +d_ifdef(struct place *p, struct place *p2, char *line) +{ + uncomment(line); + oneword("#ifdef", p2, line); + ifstate_push(p, macro_isdefined(line)); +} + +static +void +d_ifndef(struct place *p, struct place *p2, char *line) +{ + uncomment(line); + oneword("#ifndef", p2, line); + ifstate_push(p, !macro_isdefined(line)); +} + +static +void +d_elif(struct place *p, struct place *p2, char *line) +{ + char *expr; + struct place p3 = *p2; + size_t oldlen; + + if (ifstate->seenelse) { + complain(p, "#elif after #else"); + complain_fail(); + } + + if (ifstate->evertrue) { + ifstate->curtrue = false; + } else { + expr = macroexpand(p2, line, strlen(line), true); + + oldlen = strlen(expr); + uncomment(expr); + /* trim to fit, so the malloc debugging won't complain */ + expr = dorealloc(expr, oldlen + 1, strlen(expr) + 1); + + ifstate->curtrue = eval(&p3, expr); + ifstate->evertrue = ifstate->curtrue; + dostrfree(expr); + } +} + +static +void +d_else(struct place *p, struct place *p2, char *line) +{ + (void)p2; + (void)line; + + if (ifstate->seenelse) { + complain(p, "Multiple #else directives in one conditional"); + complain_fail(); + } + + ifstate->curtrue = !ifstate->evertrue; + ifstate->evertrue = true; + ifstate->seenelse = true; +} + +static +void +d_endif(struct place *p, struct place *p2, char *line) +{ + (void)p2; + (void)line; + + if (ifstate->prev == NULL) { + complain(p, "Unmatched #endif"); + complain_fail(); + } else { + ifstate_pop(); + } +} + +//////////////////////////////////////////////////////////// +// macros + +static +void +d_define(struct place *p, struct place *p2, char *line) +{ + size_t pos, argpos; + struct place p3, p4; + + (void)p; + + /* + * line may be: + * macro expansion + * macro(arg, arg, ...) expansion + */ + + pos = strcspn(line, " \t\f\v("); + if (line[pos] == '(') { + line[pos++] = '\0'; + argpos = pos; + pos = pos + strcspn(line+pos, "()"); + if (line[pos] == '(') { + p2->column += pos; + complain(p2, "Left parenthesis in macro parameters"); + complain_fail(); + return; + } + if (line[pos] != ')') { + p2->column += pos; + complain(p2, "Unclosed macro parameter list"); + complain_fail(); + return; + } + line[pos++] = '\0'; +#if 0 + if (!strchr(ws, line[pos])) { + p2->column += pos; + complain(p2, "Trash after macro parameter list"); + complain_fail(); + return; + } +#endif + } else if (line[pos] == '\0') { + argpos = 0; + } else { + line[pos++] = '\0'; + argpos = 0; + } + + pos += strspn(line+pos, ws); + + p3 = *p2; + p3.column += argpos; + + p4 = *p2; + p4.column += pos; + + if (argpos) { + macro_define_params(p2, line, &p3, + line + argpos, &p4, + line + pos); + } else { + macro_define_plain(p2, line, &p4, line + pos); + } +} + +static +void +d_undef(struct place *p, struct place *p2, char *line) +{ + (void)p; + + uncomment(line); + oneword("#undef", p2, line); + macro_undef(line); +} + +//////////////////////////////////////////////////////////// +// includes + +static +bool +tryinclude(struct place *p, char *line) +{ + size_t len; + + len = strlen(line); + if (len > 2 && line[0] == '"' && line[len-1] == '"') { + line[len-1] = '\0'; + file_readquote(p, line+1); + line[len-1] = '"'; + return true; + } + if (len > 2 && line[0] == '<' && line[len-1] == '>') { + line[len-1] = '\0'; + file_readbracket(p, line+1); + line[len-1] = '>'; + return true; + } + return false; +} + +static +void +d_include(struct place *p, struct place *p2, char *line) +{ + char *text; + size_t oldlen; + + uncomment(line); + if (tryinclude(p, line)) { + return; + } + text = macroexpand(p2, line, strlen(line), false); + + oldlen = strlen(text); + uncomment(text); + /* trim to fit, so the malloc debugging won't complain */ + text = dorealloc(text, oldlen + 1, strlen(text) + 1); + + if (tryinclude(p, text)) { + dostrfree(text); + return; + } + complain(p, "Illegal #include directive"); + complain(p, "Before macro expansion: #include %s", line); + complain(p, "After macro expansion: #include %s", text); + dostrfree(text); + complain_fail(); +} + +static +void +d_line(struct place *p, struct place *p2, char *line) +{ + (void)p2; + (void)line; + + /* XXX */ + complain(p, "Sorry, no #line yet"); +} + +//////////////////////////////////////////////////////////// +// messages + +static +void +d_warning(struct place *p, struct place *p2, char *line) +{ + char *msg; + + msg = macroexpand(p2, line, strlen(line), false); + complain(p, "#warning: %s", msg); + if (mode.werror) { + complain_fail(); + } + dostrfree(msg); +} + +static +void +d_error(struct place *p, struct place *p2, char *line) +{ + char *msg; + + msg = macroexpand(p2, line, strlen(line), false); + complain(p, "#error: %s", msg); + complain_fail(); + dostrfree(msg); +} + +//////////////////////////////////////////////////////////// +// other + +static +void +d_pragma(struct place *p, struct place *p2, char *line) +{ + (void)p2; + + complain(p, "#pragma %s", line); + complain_fail(); +} + +//////////////////////////////////////////////////////////// +// directive table + +static const struct { + const char *name; + bool ifskip; + void (*func)(struct place *, struct place *, char *line); +} directives[] = { + { "define", true, d_define }, + { "elif", false, d_elif }, + { "else", false, d_else }, + { "endif", false, d_endif }, + { "error", true, d_error }, + { "if", false, d_if }, + { "ifdef", false, d_ifdef }, + { "ifndef", false, d_ifndef }, + { "include", true, d_include }, + { "line", true, d_line }, + { "pragma", true, d_pragma }, + { "undef", true, d_undef }, + { "warning", true, d_warning }, +}; +static const unsigned numdirectives = HOWMANY(directives); + +static +void +directive_gotdirective(struct place *p, char *line) +{ + struct place p2; + size_t len, skip; + unsigned i; + + p2 = *p; + for (i=0; icurtrue) { + return; + } + skip = len + strspn(line+len, ws); + p2.column += skip; + line += skip; + + len = strlen(line); + len = notrailingws(line, len); + if (len < strlen(line)) { + line[len] = '\0'; + } + directives[i].func(p, &p2, line); + return; + } + } + /* ugh. allow # by itself, including with a comment after it */ + uncomment(line); + if (line[0] == '\0') { + return; + } + + skip = strcspn(line, ws); + complain(p, "Unknown directive #%.*s", (int)skip, line); + complain_fail(); +} + +/* + * Check for nested comment delimiters in LINE. + */ +static +size_t +directive_scancomments(const struct place *p, char *line, size_t len) +{ + size_t pos; + bool incomment; + struct place p2; + + p2 = *p; + incomment = 0; + for (pos = 0; pos+1 < len; pos++) { + if (line[pos] == '/' && line[pos+1] == '*') { + if (incomment) { + complain(&p2, "Warning: %c%c within comment", + '/', '*'); + if (mode.werror) { + complain_failed(); + } + } else { + incomment = true; + } + pos++; + } else if (line[pos] == '*' && line[pos+1] == '/') { + if (incomment) { + incomment = false; + } else { + /* stray end-comment; should we care? */ + } + pos++; + } + if (line[pos] == '\n') { + p2.line++; + p2.column = 0; + } else { + p2.column++; + } + } + + /* multiline comments are supposed to arrive in a single buffer */ + assert(!incomment); + return len; +} + +void +directive_gotline(struct place *p, char *line, size_t len) +{ + size_t skip; + + if (warns.nestcomment) { + directive_scancomments(p, line, len); + } + + /* check if we have a directive line (# exactly in column 0) */ + if (line[0] == '#') { + skip = 1 + strspn(line + 1, ws); + assert(skip <= len); + p->column += skip; + assert(line[len] == '\0'); + directive_gotdirective(p, line+skip /*, length = len-skip */); + p->column += len-skip; + } else if (ifstate->curtrue) { + macro_sendline(p, line, len); + p->column += len; + } +} + +void +directive_goteof(struct place *p) +{ + while (ifstate->prev != NULL) { + complain(p, "Missing #endif"); + complain(&ifstate->startplace, "...opened at this point"); + complain_failed(); + ifstate_pop(); + } + macro_sendeof(p); +} + +//////////////////////////////////////////////////////////// +// module initialization + +void +directive_init(void) +{ + ifstate = ifstate_create(NULL, NULL, true); +} + +void +directive_cleanup(void) +{ + assert(ifstate->prev == NULL); + ifstate_destroy(ifstate); + ifstate = NULL; +} diff --git a/cde/util/tradcpp/directive.h b/cde/util/tradcpp/directive.h new file mode 100644 index 000000000..d4d5676e9 --- /dev/null +++ b/cde/util/tradcpp/directive.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +struct place; + +void directive_init(void); +void directive_cleanup(void); + +void directive_gotline(struct place *p, char *line, size_t len); +void directive_goteof(struct place *p); + diff --git a/cde/util/tradcpp/eval.c b/cde/util/tradcpp/eval.c new file mode 100644 index 000000000..cb5db9913 --- /dev/null +++ b/cde/util/tradcpp/eval.c @@ -0,0 +1,765 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +//#define DEBUG +#ifdef DEBUG +#include +#endif + +#include "utils.h" +#include "array.h" +#include "mode.h" +#include "place.h" +#include "eval.h" + +/* + * e ::= + * e1 ? e2 : e3 + * e1 || e2 + * e1 && e2 + * e1 | e2 + * e1 ^ e2 + * e1 & e2 + * e1 == e2 | e1 != e2 + * e1 < e2 | e1 <= e2 | e1 > e2 | e1 >= e2 + * e1 << e2 | e1 >> e2 + * e1 + e2 | e1 - e2 + * e1 * e2 | e1 / e2 | e1 % e2 + * !e | ~e | -e | +e + * ( e ) | ident + */ + +enum tokens { + T_EOF, /* end of input */ + T_VAL, /* value */ + T_LPAREN, /* parens */ + T_RPAREN, + T_PIPEPIPE, /* operators */ + T_AMPAMP, + T_EQEQ, + T_BANGEQ, + T_LTEQ, + T_GTEQ, + T_LTLT, + T_GTGT, + T_QUES, + T_COLON, + T_PIPE, + T_CARET, + T_AMP, + T_LT, + T_GT, + T_PLUS, + T_MINUS, + T_STAR, + T_SLASH, + T_PCT, + T_BANG, + T_TILDE, +}; + +static const struct { + char c1, c2; + enum tokens tok; +} tokens_2[] = { + { '|', '|', T_PIPEPIPE }, + { '&', '&', T_AMPAMP }, + { '=', '=', T_EQEQ }, + { '!', '=', T_BANGEQ }, + { '<', '=', T_LTEQ }, + { '>', '=', T_GTEQ }, + { '<', '<', T_LTLT }, + { '>', '>', T_GTGT }, +}; +static const unsigned num_tokens_2 = HOWMANY(tokens_2); + +static const struct { + char c1; + enum tokens tok; +} tokens_1[] = { + { '?', T_QUES }, + { ':', T_COLON }, + { '|', T_PIPE }, + { '^', T_CARET }, + { '&', T_AMP }, + { '<', T_LT }, + { '>', T_GT }, + { '+', T_PLUS }, + { '-', T_MINUS }, + { '*', T_STAR }, + { '/', T_SLASH }, + { '%', T_PCT }, + { '!', T_BANG }, + { '~', T_TILDE }, + { '(', T_LPAREN }, + { ')', T_RPAREN }, +}; +static const unsigned num_tokens_1 = HOWMANY(tokens_1); + +struct token { + struct place place; + enum tokens tok; + int val; +}; +DECLARRAY(token, static UNUSED); +DEFARRAY(token, static); + +static struct tokenarray tokens; + +static +struct token * +token_create(const struct place *p, enum tokens tok, int val) +{ + struct token *t; + + t = domalloc(sizeof(*t)); + t->place = *p; + t->tok = tok; + t->val = val; + return t; +} + +static +void +token_destroy(struct token *t) +{ + dofree(t, sizeof(*t)); +} + +DESTROYALL_ARRAY(token, ); + +#ifdef DEBUG +static +void +printtokens(void) +{ + unsigned i, num; + struct token *t; + + fprintf(stderr, "tokens:"); + num = tokenarray_num(&tokens); + for (i=0; itok) { + case T_EOF: fprintf(stderr, " "); break; + case T_VAL: fprintf(stderr, " %d", t->val); break; + case T_LPAREN: fprintf(stderr, " ("); break; + case T_RPAREN: fprintf(stderr, " )"); break; + case T_PIPEPIPE: fprintf(stderr, " ||"); break; + case T_AMPAMP: fprintf(stderr, " &&"); break; + case T_EQEQ: fprintf(stderr, " =="); break; + case T_BANGEQ: fprintf(stderr, " !="); break; + case T_LTEQ: fprintf(stderr, " <="); break; + case T_GTEQ: fprintf(stderr, " >="); break; + case T_LTLT: fprintf(stderr, " <<"); break; + case T_GTGT: fprintf(stderr, " >>"); break; + case T_QUES: fprintf(stderr, " ?"); break; + case T_COLON: fprintf(stderr, " :"); break; + case T_PIPE: fprintf(stderr, " |"); break; + case T_CARET: fprintf(stderr, " ^"); break; + case T_AMP: fprintf(stderr, " &"); break; + case T_LT: fprintf(stderr, " <"); break; + case T_GT: fprintf(stderr, " >"); break; + case T_PLUS: fprintf(stderr, " +"); break; + case T_MINUS: fprintf(stderr, " -"); break; + case T_STAR: fprintf(stderr, " *"); break; + case T_SLASH: fprintf(stderr, " /"); break; + case T_PCT: fprintf(stderr, " %%"); break; + case T_BANG: fprintf(stderr, " !"); break; + case T_TILDE: fprintf(stderr, " ~"); break; + } + } + fprintf(stderr, "\n"); +} +#endif + +static +bool +isuop(enum tokens tok) +{ + switch (tok) { + case T_BANG: + case T_TILDE: + case T_MINUS: + case T_PLUS: + return true; + default: + break; + } + return false; +} + +static +bool +isbop(enum tokens tok) +{ + switch (tok) { + case T_EOF: + case T_VAL: + case T_LPAREN: + case T_RPAREN: + case T_COLON: + case T_QUES: + case T_BANG: + case T_TILDE: + return false; + default: + break; + } + return true; +} + +static +bool +isop(enum tokens tok) +{ + switch (tok) { + case T_EOF: + case T_VAL: + case T_LPAREN: + case T_RPAREN: + return false; + default: + break; + } + return true; +} + +static +int +getprec(enum tokens tok) +{ + switch (tok) { + case T_BANG: case T_TILDE: return -1; + case T_STAR: case T_SLASH: case T_PCT: return 0; + case T_PLUS: case T_MINUS: return 1; + case T_LTLT: case T_GTGT: return 2; + case T_LT: case T_LTEQ: case T_GT: case T_GTEQ: return 3; + case T_EQEQ: case T_BANGEQ: return 4; + case T_AMP: return 5; + case T_CARET: return 6; + case T_PIPE: return 7; + case T_AMPAMP: return 8; + case T_PIPEPIPE: return 9; + default: break; + } + return 10; +} + +static +bool +looser(enum tokens t1, enum tokens t2) +{ + return getprec(t1) >= getprec(t2); +} + +static +int +eval_uop(enum tokens op, int val) +{ + switch (op) { + case T_BANG: val = !val; break; + case T_TILDE: val = (int)~(unsigned)val; break; + case T_MINUS: val = -val; break; + case T_PLUS: break; + default: assert(0); break; + } + return val; +} + +static +int +eval_bop(struct place *p, int lv, enum tokens op, int rv) +{ + unsigned mask; + + switch (op) { + case T_PIPEPIPE: return lv || rv; + case T_AMPAMP: return lv && rv; + case T_PIPE: return (int)((unsigned)lv | (unsigned)rv); + case T_CARET: return (int)((unsigned)lv ^ (unsigned)rv); + case T_AMP: return (int)((unsigned)lv & (unsigned)rv); + case T_EQEQ: return lv == rv; + case T_BANGEQ: return lv != rv; + case T_LT: return lv < rv; + case T_GT: return lv > rv; + case T_LTEQ: return lv <= rv; + case T_GTEQ: return lv >= rv; + + case T_LTLT: + case T_GTGT: + if (rv < 0) { + complain(p, "Negative bit-shift"); + complain_fail(); + rv = 0; + } + if ((unsigned)rv >= CHAR_BIT * sizeof(unsigned)) { + complain(p, "Bit-shift farther than type width"); + complain_fail(); + rv = 0; + } + if (op == T_LTLT) { + return (int)((unsigned)lv << (unsigned)rv); + } + mask = ((unsigned)-1) << (CHAR_BIT * sizeof(unsigned) - rv); + lv = (int)(((unsigned)lv >> (unsigned)rv) | mask); + return lv; + + case T_MINUS: + if (rv == INT_MIN) { + if (lv == INT_MIN) { + return 0; + } + lv--; + rv++; + } + rv = -rv; + /* FALLTHROUGH */ + case T_PLUS: + if (rv > 0 && lv > (INT_MAX - rv)) { + complain(p, "Integer overflow"); + complain_fail(); + return INT_MAX; + } + if (rv < 0 && lv < (INT_MIN - rv)) { + complain(p, "Integer underflow"); + complain_fail(); + return INT_MIN; + } + return lv + rv; + + case T_STAR: + if (rv == 0) { + return 0; + } + if (rv == 1) { + return lv; + } + if (rv == -1 && lv == INT_MIN) { + lv++; + lv = -lv; + if (lv == INT_MAX) { + complain(p, "Integer overflow"); + complain_fail(); + return INT_MAX; + } + lv++; + return lv; + } + if (lv == INT_MIN && rv < 0) { + complain(p, "Integer overflow"); + complain_fail(); + return INT_MAX; + } + if (lv == INT_MIN && rv > 0) { + complain(p, "Integer underflow"); + complain_fail(); + return INT_MIN; + } + if (rv < 0) { + rv = -rv; + lv = -lv; + } + if (lv > 0 && lv > INT_MAX / rv) { + complain(p, "Integer overflow"); + complain_fail(); + return INT_MAX; + } + if (lv < 0 && lv < INT_MIN / rv) { + complain(p, "Integer underflow"); + complain_fail(); + return INT_MIN; + } + return lv * rv; + + case T_SLASH: + if (rv == 0) { + complain(p, "Division by zero"); + complain_fail(); + return 0; + } + return lv / rv; + + case T_PCT: + if (rv == 0) { + complain(p, "Modulus by zero"); + complain_fail(); + return 0; + } + return lv % rv; + + default: assert(0); break; + } + return 0; +} + +static +void +tryreduce(void) +{ + unsigned num; + struct token *t1, *t2, *t3, *t4, *t5, *t6; + + while (1) { +#ifdef DEBUG + printtokens(); +#endif + num = tokenarray_num(&tokens); + t1 = (num >= 1) ? tokenarray_get(&tokens, num-1) : NULL; + t2 = (num >= 2) ? tokenarray_get(&tokens, num-2) : NULL; + t3 = (num >= 3) ? tokenarray_get(&tokens, num-3) : NULL; + + if (num >= 3 && + t3->tok == T_LPAREN && + t2->tok == T_VAL && + t1->tok == T_RPAREN) { + /* (x) -> x */ + t2->place = t3->place; + token_destroy(t1); + token_destroy(t3); + tokenarray_remove(&tokens, num-1); + tokenarray_remove(&tokens, num-3); + continue; + } + + if (num >= 2 && + (num == 2 || isop(t3->tok) || t3->tok == T_LPAREN) && + isuop(t2->tok) && + t1->tok == T_VAL) { + /* unary operator */ + t1->val = eval_uop(t2->tok, t1->val); + t1->place = t2->place; + token_destroy(t2); + tokenarray_remove(&tokens, num-2); + continue; + } + if (num >= 2 && + (num == 2 || isop(t3->tok) || t3->tok == T_LPAREN) && + t2->tok != T_LPAREN && t2->tok != T_VAL && + t1->tok == T_VAL) { + complain(&t2->place, "Invalid unary operator"); + complain_fail(); + token_destroy(t2); + tokenarray_remove(&tokens, num-2); + continue; + } + + + t4 = (num >= 4) ? tokenarray_get(&tokens, num-4) : NULL; + + if (num >= 4 && + t4->tok == T_VAL && + isbop(t3->tok) && + t2->tok == T_VAL) { + /* binary operator */ + if (looser(t1->tok, t3->tok)) { + t4->val = eval_bop(&t3->place, + t4->val, t3->tok, t2->val); + token_destroy(t2); + token_destroy(t3); + tokenarray_remove(&tokens, num-2); + tokenarray_remove(&tokens, num-3); + continue; + } + break; + } + + t5 = (num >= 5) ? tokenarray_get(&tokens, num-5) : NULL; + t6 = (num >= 6) ? tokenarray_get(&tokens, num-6) : NULL; + + if (num >= 6 && + t6->tok == T_VAL && + t5->tok == T_QUES && + t4->tok == T_VAL && + t3->tok == T_COLON && + t2->tok == T_VAL && + !isop(t1->tok)) { + /* conditional expression */ + t6->val = t6->val ? t4->val : t2->val; + token_destroy(t2); + token_destroy(t3); + token_destroy(t4); + token_destroy(t5); + tokenarray_remove(&tokens, num-2); + tokenarray_remove(&tokens, num-3); + tokenarray_remove(&tokens, num-4); + tokenarray_remove(&tokens, num-5); + continue; + } + + if (num >= 2 && + t2->tok == T_LPAREN && + t1->tok == T_RPAREN) { + complain(&t1->place, "Value expected within ()"); + complain_fail(); + t1->tok = T_VAL; + t1->val = 0; + token_destroy(t1); + tokenarray_remove(&tokens, num-1); + continue; + } + + if (num >= 2 && + t2->tok == T_VAL && + t1->tok == T_VAL) { + complain(&t1->place, "Operator expected"); + complain_fail(); + token_destroy(t1); + tokenarray_remove(&tokens, num-1); + continue; + } + + if (num >= 2 && + isop(t2->tok) && + t1->tok == T_EOF) { + complain(&t1->place, "Value expected after operator"); + complain_fail(); + token_destroy(t2); + tokenarray_remove(&tokens, num-2); + continue; + } + + if (num == 2 && + t2->tok == T_VAL && + t1->tok == T_RPAREN) { + complain(&t1->place, "Excess right parenthesis"); + complain_fail(); + token_destroy(t1); + tokenarray_remove(&tokens, num-1); + continue; + } + + if (num == 3 && + t3->tok == T_LPAREN && + t2->tok == T_VAL && + t1->tok == T_EOF) { + complain(&t1->place, "Unclosed left parenthesis"); + complain_fail(); + token_destroy(t3); + tokenarray_remove(&tokens, num-3); + continue; + } + + if (num == 2 && + t2->tok == T_VAL && + t1->tok == T_EOF) { + /* accepting state */ + break; + } + + if (num >= 1 && + t1->tok == T_EOF) { + /* any other configuration at eof is an error */ + complain(&t1->place, "Parse error"); + complain_fail(); + break; + } + + /* otherwise, wait for more input */ + break; + } +} + +static +void +token(struct place *p, enum tokens tok, int val) +{ + struct token *t; + + t = token_create(p, tok, val); + + tokenarray_add(&tokens, t, NULL); + tryreduce(); +} + +static +int +wordval(struct place *p, char *word) +{ + unsigned long val; + char *t; + + if (word[0] >= '0' && word[0] <= '9') { + errno = 0; + val = strtoul(word, &t, 0); + if (errno) { + complain(p, "Invalid integer constant"); + complain_fail(); + return 0; + } + while (*t == 'U' || *t == 'L') { + t++; + } + if (*t != '\0') { + complain(p, "Trailing garbage after integer constant"); + complain_fail(); + return 0; + } + if (val > INT_MAX) { + complain(p, "Integer constant too large"); + complain_fail(); + return INT_MAX; + } + return val; + } + + /* if it's a symbol, warn and substitute 0. */ + if (warns.undef) { + complain(p, "Warning: value of undefined symbol %s is 0", + word); + if (mode.werror) { + complain_fail(); + } + } + return 0; +} + +static +bool +check_word(struct place *p, char *expr, size_t pos, size_t *len_ret) +{ + size_t len; + int val; + char tmp; + + if (!strchr(alnum, expr[pos])) { + return false; + } + len = strspn(expr + pos, alnum); + tmp = expr[pos + len]; + expr[pos + len] = '\0'; + val = wordval(p, expr + pos); + expr[pos + len] = tmp; + token(p, T_VAL, val); + *len_ret = len; + return true; +} + +static +bool +check_tokens_2(struct place *p, char *expr, size_t pos) +{ + unsigned i; + + for (i=0; icolumn += len; + /* trailing whitespace is supposed to have been pruned */ + assert(expr[pos] != '\0'); + if (check_word(p, expr, pos, &len)) { + pos += len; + p->column += len; + continue; + } + if (check_tokens_2(p, expr, pos)) { + pos += 2; + p->column += 2; + continue; + } + if (check_tokens_1(p, expr, pos)) { + pos++; + p->column++; + continue; + } + complain(p, "Invalid character %u in #if-expression", + (unsigned char)expr[pos]); + complain_fail(); + pos++; + p->column++; + } + token(p, T_EOF, 0); +} + +bool +eval(struct place *p, char *expr) +{ + struct token *t1, *t2; + unsigned num; + bool result; + +#ifdef DEBUG + fprintf(stderr, "eval: %s\n", expr); +#endif + + tokenarray_init(&tokens); + tokenize(p, expr); + + result = false; + num = tokenarray_num(&tokens); + if (num == 2) { + t1 = tokenarray_get(&tokens, num-1); + t2 = tokenarray_get(&tokens, num-2); + if (t2->tok == T_VAL && + t1->tok == T_EOF) { + result = t2->val != 0; + } + } + + tokenarray_destroyall(&tokens); + tokenarray_cleanup(&tokens); + return result; +} diff --git a/cde/util/tradcpp/eval.h b/cde/util/tradcpp/eval.h new file mode 100644 index 000000000..0694fb4f9 --- /dev/null +++ b/cde/util/tradcpp/eval.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +bool eval(struct place *p, char *expr); diff --git a/cde/util/tradcpp/files.c b/cde/util/tradcpp/files.c new file mode 100644 index 000000000..f0ee45eee --- /dev/null +++ b/cde/util/tradcpp/files.c @@ -0,0 +1,420 @@ +/*- + * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "array.h" +#include "mode.h" +#include "place.h" +#include "files.h" +#include "directive.h" + +struct incdir { + const char *name; + bool issystem; +}; + +DECLARRAY(incdir, static UNUSED); +DEFARRAY(incdir, static); + +static struct incdirarray quotepath, bracketpath; + +//////////////////////////////////////////////////////////// +// management + +static +struct incdir * +incdir_create(const char *name, bool issystem) +{ + struct incdir *id; + + id = domalloc(sizeof(*id)); + id->name = name; + id->issystem = issystem; + return id; +} + +static +void +incdir_destroy(struct incdir *id) +{ + dofree(id, sizeof(*id)); +} + +void +files_init(void) +{ + incdirarray_init("epath); + incdirarray_init(&bracketpath); +} + +DESTROYALL_ARRAY(incdir, ); + +void +files_cleanup(void) +{ + incdirarray_destroyall("epath); + incdirarray_cleanup("epath); + incdirarray_destroyall(&bracketpath); + incdirarray_cleanup(&bracketpath); +} + +//////////////////////////////////////////////////////////// +// path setup + +void +files_addquotepath(const char *dir, bool issystem) +{ + struct incdir *id; + + id = incdir_create(dir, issystem); + incdirarray_add("epath, id, NULL); +} + +void +files_addbracketpath(const char *dir, bool issystem) +{ + struct incdir *id; + + id = incdir_create(dir, issystem); + incdirarray_add(&bracketpath, id, NULL); +} + +//////////////////////////////////////////////////////////// +// parsing + +/* + * Find the end of the logical line. End of line characters that are + * commented out do not count. + */ +static +size_t +findeol(const char *buf, size_t start, size_t limit) +{ + size_t i; + int incomment = 0; + bool inquote = false; + char quote = '\0'; + + for (i=start; i= bufend) { + /* do not have a whole line in the buffer; read more */ + assert(bufend >= linestart); + if (linestart > 0 && bufend > linestart) { + /* slide to beginning of buffer */ + memmove(buf, buf+linestart, bufend-linestart); + bufend -= linestart; + lineend -= linestart; + linestart = 0; + } + if (bufend >= bufmax) { + /* need bigger buffer */ + buf = dorealloc(buf, bufmax, bufmax*2); + bufmax = bufmax*2; + } + + if (ateof) { + /* don't read again, in case it's a socket */ + result = 0; + } else { + result = read(fd, buf+bufend, bufmax - bufend); + } + + if (result == -1) { + /* read error */ + complain(NULL, "%s: %s", + name, strerror(errno)); + complain_fail(); + } else if (result == 0 && bufend == linestart) { + /* eof */ + ateof = true; + break; + } else if (result == 0) { + /* eof in middle of line */ + ateof = true; + ptmp = linestartplace; + ptmp.column += bufend - linestart; + complain(&ptmp, "No newline at end of file"); + if (mode.werror) { + complain_fail(); + } + assert(bufend < bufmax); + lineend = bufend++; + buf[lineend] = '\n'; + } else { + bufend += (size_t)result; + lineend = findeol(buf, linestart, bufend); + } + /* loop in case we still don't have a whole line */ + continue; + } + + /* have a line */ + assert(buf[lineend] == '\n'); + buf[lineend] = '\0'; + nextlinestart = lineend+1; + nextlinestartplace.line++; + + /* check for CR/NL */ + if (lineend > 0 && buf[lineend-1] == '\r') { + buf[lineend-1] = '\0'; + lineend--; + } + + /* check for continuation line */ + if (lineend > 0 && buf[lineend-1]=='\\') { + lineend--; + tmp = nextlinestart - lineend; + if (bufend > nextlinestart) { + memmove(buf+lineend, buf+nextlinestart, + bufend - nextlinestart); + } + bufend -= tmp; + nextlinestart -= tmp; + lineend = findeol(buf, linestart, bufend); + /* might not have a whole line, so loop */ + continue; + } + + /* line now goes from linestart to lineend */ + assert(buf[lineend] == '\0'); + + /* count how many commented-out newlines we swallowed */ + nextlinestartplace.line += countnls(buf, linestart, lineend); + + /* if the line isn't empty, process it */ + if (lineend > linestart) { + directive_gotline(&linestartplace, + buf+linestart, lineend-linestart); + } + + linestart = nextlinestart; + lineend = findeol(buf, linestart, bufend); + linestartplace = nextlinestartplace; + } + + if (toplevel) { + directive_goteof(&linestartplace); + } + dofree(buf, bufmax); +} + +//////////////////////////////////////////////////////////// +// path search + +static +char * +mkfilename(struct place *place, const char *dir, const char *file) +{ + size_t dlen, flen, rlen; + char *ret; + bool needslash = false; + + if (dir == NULL) { + dir = place_getparsedir(place); + } + + dlen = strlen(dir); + flen = strlen(file); + if (dlen > 0 && dir[dlen-1] != '/') { + needslash = true; + } + + rlen = dlen + (needslash ? 1 : 0) + flen; + ret = domalloc(rlen + 1); + strcpy(ret, dir); + if (needslash) { + strcat(ret, "/"); + } + strcat(ret, file); + return ret; +} + +static +int +file_tryopen(const char *file) +{ + int fd; + + /* XXX check for non-regular files */ + + fd = open(file, O_RDONLY); + if (fd < 0) { + if (errno != ENOENT && errno != ENOTDIR) { + complain(NULL, "%s: %s", file, strerror(errno)); + } + return -1; + } + + return fd; +} + +static +void +file_search(struct place *place, struct incdirarray *path, const char *name) +{ + unsigned i, num; + struct incdir *id; + const struct placefile *pf; + char *file; + int fd; + + assert(place != NULL); + + if (name[0] == '/') { + fd = file_tryopen(name); + if (fd >= 0) { + pf = place_addfile(place, name, true); + file_read(pf, fd, name, false); + close(fd); + return; + } + } else { + num = incdirarray_num(path); + for (i=0; iname, name); + fd = file_tryopen(file); + if (fd >= 0) { + pf = place_addfile(place, file, id->issystem); + file_read(pf, fd, file, false); + dostrfree(file); + close(fd); + return; + } + dostrfree(file); + } + } + complain(place, "Include file %s not found", name); + complain_fail(); +} + +void +file_readquote(struct place *place, const char *name) +{ + file_search(place, "epath, name); +} + +void +file_readbracket(struct place *place, const char *name) +{ + file_search(place, &bracketpath, name); +} + +void +file_readabsolute(struct place *place, const char *name) +{ + const struct placefile *pf; + int fd; + + assert(place != NULL); + + if (name == NULL) { + fd = STDIN_FILENO; + pf = place_addfile(place, "", false); + } else { + fd = file_tryopen(name); + if (fd < 0) { + complain(NULL, "%s: %s", name, strerror(errno)); + die(); + } + pf = place_addfile(place, name, false); + } + + file_read(pf, fd, name, true); + + if (name != NULL) { + close(fd); + } +} diff --git a/cde/util/tradcpp/files.h b/cde/util/tradcpp/files.h new file mode 100644 index 000000000..97b459d74 --- /dev/null +++ b/cde/util/tradcpp/files.h @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +struct place; + +void files_init(void); +void files_cleanup(void); + +void files_addquotepath(const char *dir, bool issystem); +void files_addbracketpath(const char *dir, bool issystem); + +void file_readquote(struct place *, const char *name); +void file_readbracket(struct place *, const char *name); +void file_readabsolute(struct place *, const char *name); diff --git a/cde/util/tradcpp/inlinedefs.h b/cde/util/tradcpp/inlinedefs.h new file mode 100644 index 000000000..bfcef62f0 --- /dev/null +++ b/cde/util/tradcpp/inlinedefs.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2009 David A. Holland. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Author nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(__GNUC__) && !defined(__GNUC_STDC_INLINE__) +/* gcc's non-C99 inline semantics */ +#define C99INLINE extern inline +#elif defined(__STDC__) && __STDC_VERSION__ >= 199901L +/* C99 */ +#define C99INLINE inline +#else +/* something else; static inline is safest */ +#define C99INLINE static inline +#endif diff --git a/cde/util/tradcpp/macro.c b/cde/util/tradcpp/macro.c new file mode 100644 index 000000000..210598013 --- /dev/null +++ b/cde/util/tradcpp/macro.c @@ -0,0 +1,1202 @@ +/*- + * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "array.h" +#include "mode.h" +#include "place.h" +#include "macro.h" +#include "output.h" + +struct expansionitem { + bool isstring; + union { + char *string; + unsigned param; + }; +}; +DECLARRAY(expansionitem, static UNUSED); +DEFARRAY(expansionitem, static); + +struct macro { + struct place defplace; + struct place expansionplace; + unsigned hash; + char *name; + bool hasparams; + struct stringarray params; + struct expansionitemarray expansion; + bool inuse; +}; +DECLARRAY(macro, static UNUSED); +DEFARRAY(macro, static); +DECLARRAY(macroarray, static UNUSED); +DEFARRAY(macroarray, static); + +static struct macroarrayarray macros; +static unsigned total_macros; +static unsigned hashmask; + +//////////////////////////////////////////////////////////// +// macro structure ops + +static +struct expansionitem * +expansionitem_create_string(const char *string) +{ + struct expansionitem *ei; + + ei = domalloc(sizeof(*ei)); + ei->isstring = true; + ei->string = dostrdup(string); + return ei; +} + +static +struct expansionitem * +expansionitem_create_stringlen(const char *string, size_t len) +{ + struct expansionitem *ei; + + ei = domalloc(sizeof(*ei)); + ei->isstring = true; + ei->string = dostrndup(string, len); + return ei; +} + +static +struct expansionitem * +expansionitem_create_param(unsigned param) +{ + struct expansionitem *ei; + + ei = domalloc(sizeof(*ei)); + ei->isstring = false; + ei->param = param; + return ei; +} + +static +void +expansionitem_destroy(struct expansionitem *ei) +{ + if (ei->isstring) { + dostrfree(ei->string); + } + dofree(ei, sizeof(*ei)); +} + +static +bool +expansionitem_eq(const struct expansionitem *ei1, + const struct expansionitem *ei2) +{ + if (ei1->isstring != ei2->isstring) { + return false; + } + if (ei1->isstring) { + if (strcmp(ei1->string, ei2->string) != 0) { + return false; + } + } else { + if (ei1->param != ei2->param) { + return false; + } + } + return true; +} + +static +struct macro * +macro_create(struct place *p1, const char *name, unsigned hash, + struct place *p2) +{ + struct macro *m; + + m = domalloc(sizeof(*m)); + m->defplace = *p1; + m->expansionplace = *p2; + m->hash = hash; + m->name = dostrdup(name); + m->hasparams = false; + stringarray_init(&m->params); + expansionitemarray_init(&m->expansion); + m->inuse = false; + return m; +} + +DESTROYALL_ARRAY(expansionitem, ); + +static +void +macro_destroy(struct macro *m) +{ + expansionitemarray_destroyall(&m->expansion); + expansionitemarray_cleanup(&m->expansion); + dostrfree(m->name); + dofree(m, sizeof(*m)); +} + +static +bool +macro_eq(const struct macro *m1, const struct macro *m2) +{ + unsigned num1, num2, i; + struct expansionitem *ei1, *ei2; + const char *p1, *p2; + + if (strcmp(m1->name, m2->name) != 0) { + return false; + } + + if (m1->hasparams != m2->hasparams) { + return false; + } + + num1 = expansionitemarray_num(&m1->expansion); + num2 = expansionitemarray_num(&m2->expansion); + if (num1 != num2) { + return false; + } + + for (i=0; iexpansion, i); + ei2 = expansionitemarray_get(&m2->expansion, i); + if (!expansionitem_eq(ei1, ei2)) { + return false; + } + } + + num1 = stringarray_num(&m1->params); + num2 = stringarray_num(&m2->params); + if (num1 != num2) { + return false; + } + + for (i=0; iparams, i); + p2 = stringarray_get(&m2->params, i); + if (strcmp(p1, p2) != 0) { + return false; + } + } + return true; +} + +//////////////////////////////////////////////////////////// +// macro table + +/* + * Unless I've screwed up, this is something called Fletcher's Checksum + * that showed up in Dr. Dobbs in, according to my notes, May 1992. The + * implementation is new. + */ +static +unsigned +hashfunc(const char *s, size_t len) +{ + uint16_t x1, x2, a; + size_t i; + + x1 = (uint16_t) (len >> 16); + x2 = (uint16_t) (len); + if (x1==0) { + x1++; + } + if (x2==0) { + x2++; + } + + for (i=0; ihash) { + continue; + } + mlen = strlen(m->name); + if (len == mlen && !memcmp(name, m->name, len)) { + if (remove) { + if (i < num-1) { + m2 = macroarray_get(bucket, num-1); + macroarray_set(bucket, i, m2); + } + macroarray_setsize(bucket, num-1); + total_macros--; + } + return m; + } + } + return NULL; +} + +static +struct macro * +macrotable_find(const char *name, bool remove) +{ + return macrotable_findlen(name, strlen(name), remove); +} + +static +void +macrotable_rehash(void) +{ + struct macroarray *newbucket, *oldbucket; + struct macro *m; + unsigned newmask, tossbit; + unsigned numbuckets, i; + unsigned oldnum, j, k; + + numbuckets = macroarrayarray_num(¯os); + macroarrayarray_setsize(¯os, numbuckets*2); + + assert(hashmask == numbuckets - 1); + newmask = (hashmask << 1) | 1U; + tossbit = newmask & ~hashmask; + hashmask = newmask; + + for (i=0; ihash & tossbit) { + if (newbucket == NULL) { + newbucket = macroarray_create(); + } + macroarray_set(oldbucket, j, NULL); + macroarray_add(newbucket, m, NULL); + } + } + for (j=k=0; j 0 && total_macros / numbuckets > 9) { + macrotable_rehash(); + } + + hash = hashfunc(m->name, strlen(m->name)); + bucket = macroarrayarray_get(¯os, hash & hashmask); + if (bucket == NULL) { + bucket = macroarray_create(); + macroarrayarray_set(¯os, hash & hashmask, bucket); + } + macroarray_add(bucket, m, NULL); + total_macros++; +} + +//////////////////////////////////////////////////////////// +// external macro definition interface + +static +struct macro * +macro_define_common_start(struct place *p1, const char *macro, + struct place *p2) +{ + struct macro *m; + unsigned hash; + + if (!is_identifier(macro)) { + complain(p1, "Invalid macro name %s", macro); + complain_fail(); + } + + hash = hashfunc(macro, strlen(macro)); + m = macro_create(p1, macro, hash, p2); + return m; +} + +static +void +macro_define_common_end(struct macro *m) +{ + struct macro *oldm; + bool ok; + + oldm = macrotable_find(m->name, false); + if (oldm != NULL) { + ok = macro_eq(m, oldm); + if (ok) { + /* in traditional cpp this is silent */ + //complain(&m->defplace, + // "Warning: redefinition of %s", m->name); + //complain(&oldm->defplace, + // "Previous definition was here"); + //if (mode.werror) { + // complain_fail(); + //} + } else { + complain(&m->defplace, + "Warning: non-identical redefinition of %s", + m->name); + complain(&oldm->defplace, + "Previous definition was here"); + /* in traditional cpp this is not fatal */ + if (mode.werror) { + complain_fail(); + } + } + macro_destroy(m); + return; + } + macrotable_add(m); +} + +static +void +macro_parse_parameters(struct macro *m, struct place *p, const char *params) +{ + size_t len; + const char *s; + char *param; + + while (params != NULL) { + len = strspn(params, ws); + params += len; + p->column += len; + s = strchr(params, ','); + if (s) { + len = s-params; + param = dostrndup(params, len); + s++; + } else { + len = strlen(params); + param = dostrndup(params, len); + } + notrailingws(param, strlen(param)); + if (!is_identifier(param)) { + complain(p, "Invalid macro parameter name %s", param); + complain_fail(); + } else { + stringarray_add(&m->params, param, NULL); + } + params = s; + p->column += len; + } +} + +static +bool +isparam(struct macro *m, const char *name, size_t len, unsigned *num_ret) +{ + unsigned num, i; + const char *param; + + num = stringarray_num(&m->params); + for (i=0; iparams, i); + if (strlen(param) == len && !memcmp(name, param, len)) { + *num_ret = i; + return true; + } + } + return false; +} + +static +void +macro_parse_expansion(struct macro *m, const char *buf) +{ + size_t blockstart, wordstart, pos; + struct expansionitem *ei; + unsigned param; + + pos = blockstart = 0; + while (buf[pos] != '\0') { + pos += strspn(buf+pos, ws); + if (strchr(alnum, buf[pos])) { + wordstart = pos; + pos += strspn(buf+pos, alnum); + if (isparam(m, buf+wordstart, pos-wordstart, ¶m)) { + if (wordstart > blockstart) { + ei = expansionitem_create_stringlen( + buf + blockstart, + wordstart - blockstart); + expansionitemarray_add(&m->expansion, + ei, NULL); + } + ei = expansionitem_create_param(param); + expansionitemarray_add(&m->expansion, ei,NULL); + blockstart = pos; + continue; + } + continue; + } + pos++; + } + if (pos > blockstart) { + ei = expansionitem_create_stringlen(buf + blockstart, + pos - blockstart); + expansionitemarray_add(&m->expansion, ei, NULL); + } +} + +void +macro_define_plain(struct place *p1, const char *macro, + struct place *p2, const char *expansion) +{ + struct macro *m; + struct expansionitem *ei; + + m = macro_define_common_start(p1, macro, p2); + ei = expansionitem_create_string(expansion); + expansionitemarray_add(&m->expansion, ei, NULL); + macro_define_common_end(m); +} + +void +macro_define_params(struct place *p1, const char *macro, + struct place *p2, const char *params, + struct place *p3, const char *expansion) +{ + struct macro *m; + + m = macro_define_common_start(p1, macro, p3); + m->hasparams = true; + macro_parse_parameters(m, p2, params); + macro_parse_expansion(m, expansion); + macro_define_common_end(m); +} + +void +macro_undef(const char *macro) +{ + struct macro *m; + + m = macrotable_find(macro, true); + if (m) { + macro_destroy(m); + } +} + +bool +macro_isdefined(const char *macro) +{ + struct macro *m; + + m = macrotable_find(macro, false); + return m != NULL; +} + +//////////////////////////////////////////////////////////// +// macro expansion + +struct expstate { + bool honordefined; + enum { ES_NORMAL, ES_WANTLPAREN, ES_NOARG, ES_HAVEARG } state; + struct macro *curmacro; + struct stringarray args; + unsigned argparens; + + bool tobuf; + char *buf; + size_t bufpos, bufmax; +}; + +static struct expstate mainstate; + +static void doexpand(struct expstate *es, struct place *p, + char *buf, size_t len); + +static +void +expstate_init(struct expstate *es, bool tobuf, bool honordefined) +{ + es->honordefined = honordefined; + es->state = ES_NORMAL; + es->curmacro = NULL; + stringarray_init(&es->args); + es->argparens = 0; + es->tobuf = tobuf; + es->buf = NULL; + es->bufpos = 0; + es->bufmax = 0; +} + +static +void +expstate_cleanup(struct expstate *es) +{ + assert(es->state == ES_NORMAL); + stringarray_cleanup(&es->args); + if (es->buf) { + dofree(es->buf, es->bufmax); + } +} + +static +void +expstate_destroyargs(struct expstate *es) +{ + unsigned i, num; + + num = stringarray_num(&es->args); + for (i=0; iargs, i)); + } + stringarray_setsize(&es->args, 0); +} + +static +void +expand_send(struct expstate *es, struct place *p, const char *buf, size_t len) +{ + size_t oldmax; + + if (es->tobuf) { + assert(es->bufpos <= es->bufmax); + if (es->bufpos + len > es->bufmax) { + oldmax = es->bufmax; + if (es->bufmax == 0) { + es->bufmax = 64; + } + while (es->bufpos + len > es->bufmax) { + es->bufmax *= 2; + } + es->buf = dorealloc(es->buf, oldmax, es->bufmax); + } + memcpy(es->buf + es->bufpos, buf, len); + es->bufpos += len; + assert(es->bufpos <= es->bufmax); + } else { + output(p, buf, len); + } +} + +static +void +expand_send_eof(struct expstate *es, struct place *p) +{ + if (es->tobuf) { + expand_send(es, p, "", 1); + es->bufpos--; + } else { + output_eof(); + } +} + +static +void +expand_newarg(struct expstate *es, char *buf, size_t len) +{ + char *text; + + text = dostrndup(buf, len); + stringarray_add(&es->args, text, NULL); +} + +static +void +expand_appendarg(struct expstate *es, char *buf, size_t len) +{ + unsigned num; + char *text; + size_t oldlen; + + num = stringarray_num(&es->args); + assert(num > 0); + + text = stringarray_get(&es->args, num - 1); + oldlen = strlen(text); + text = dorealloc(text, oldlen + 1, oldlen + len + 1); + memcpy(text + oldlen, buf, len); + text[oldlen+len] = '\0'; + stringarray_set(&es->args, num - 1, text); +} + +static +char * +expand_substitute(struct place *p, struct expstate *es) +{ + struct expansionitem *ei; + unsigned i, num; + size_t len; + char *arg; + char *ret; + unsigned numargs, numparams; + + numargs = stringarray_num(&es->args); + numparams = stringarray_num(&es->curmacro->params); + + if (numargs == 0 && numparams == 1) { + /* no arguments <=> one empty argument */ + stringarray_add(&es->args, dostrdup(""), NULL); + numargs++; + } + if (numargs != numparams) { + complain(p, "Wrong number of arguments for macro %s; " + "found %u, expected %u", + es->curmacro->name, numargs, numparams); + complain_fail(); + while (numargs < numparams) { + stringarray_add(&es->args, dostrdup(""), NULL); + numargs++; + } + } + + len = 0; + num = expansionitemarray_num(&es->curmacro->expansion); + for (i=0; icurmacro->expansion, i); + if (ei->isstring) { + len += strlen(ei->string); + } else { + arg = stringarray_get(&es->args, ei->param); + len += strlen(arg); + } + } + + ret = domalloc(len+1); + *ret = '\0'; + for (i=0; icurmacro->expansion, i); + if (ei->isstring) { + strcat(ret, ei->string); + } else { + arg = stringarray_get(&es->args, ei->param); + strcat(ret, arg); + } + } + + return ret; +} + +static +void +expand_domacro(struct expstate *es, struct place *p) +{ + struct macro *m; + char *newbuf, *newbuf2; + + if (es->curmacro == NULL) { + /* defined() */ + if (stringarray_num(&es->args) != 1) { + complain(p, "Too many arguments for defined()"); + complain_fail(); + expand_send(es, p, "0", 1); + return; + } + m = macrotable_find(stringarray_get(&es->args, 0), false); + expand_send(es, p, (m != NULL) ? "1" : "0", 1); + expstate_destroyargs(es); + return; + } + + assert(es->curmacro->inuse == false); + es->curmacro->inuse = true; + + newbuf = expand_substitute(p, es); + newbuf2 = macroexpand(p, newbuf, strlen(newbuf), false); + dostrfree(newbuf); + expstate_destroyargs(es); + doexpand(es, p, newbuf2, strlen(newbuf2)); + dostrfree(newbuf2); + + es->curmacro->inuse = false; +} + +/* + * The traditional behavior if a function-like macro appears without + * arguments is to pretend it isn't a macro; that is, just emit its + * name. + */ +static +void +expand_missingargs(struct expstate *es, struct place *p, bool needspace) +{ + if (es->curmacro == NULL) { + /* defined */ + expand_send(es, p, "defined", 7); + return; + } + expand_send(es, p, es->curmacro->name, strlen(es->curmacro->name)); + /* send a space in case we ate whitespace after the macro name */ + if (needspace) { + expand_send(es, p, " ", 1); + } +} + +static +void +expand_got_ws(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, p, buf, len); + break; + case ES_WANTLPAREN: + break; + case ES_NOARG: + expand_newarg(es, buf, len); + es->state = ES_HAVEARG; + break; + case ES_HAVEARG: + expand_appendarg(es, buf, len); + break; + } +} + +static +void +expand_got_word(struct expstate *es, struct place *p, char *buf, size_t len) +{ + struct macro *m; + struct expansionitem *ei; + char *newbuf; + + switch (es->state) { + case ES_NORMAL: + if (es->honordefined && + len == 7 && !memcmp(buf, "defined", 7)) { + es->curmacro = NULL; + es->state = ES_WANTLPAREN; + break; + } + m = macrotable_findlen(buf, len, false); + if (m == NULL || m->inuse) { + expand_send(es, p, buf, len); + } else if (!m->hasparams) { + m->inuse = true; + assert(expansionitemarray_num(&m->expansion) == 1); + ei = expansionitemarray_get(&m->expansion, 0); + assert(ei->isstring); + newbuf = macroexpand(p, ei->string, + strlen(ei->string), false); + doexpand(es, p, newbuf, strlen(newbuf)); + dostrfree(newbuf); + m->inuse = false; + } else { + es->curmacro = m; + es->state = ES_WANTLPAREN; + } + break; + case ES_WANTLPAREN: + if (es->curmacro != NULL) { + expand_missingargs(es, p, true); + es->state = ES_NORMAL; + /* try again */ + expand_got_word(es, p, buf, len); + } else { + /* "defined foo" means "defined(foo)" */ + expand_newarg(es, buf, len); + es->state = ES_NORMAL; + expand_domacro(es, p); + } + break; + case ES_NOARG: + expand_newarg(es, buf, len); + es->state = ES_HAVEARG; + break; + case ES_HAVEARG: + expand_appendarg(es, buf, len); + break; + } +} + +static +void +expand_got_lparen(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, p, buf, len); + break; + case ES_WANTLPAREN: + es->state = ES_NOARG; + break; + case ES_NOARG: + expand_newarg(es, buf, len); + es->state = ES_HAVEARG; + es->argparens++; + break; + case ES_HAVEARG: + expand_appendarg(es, buf, len); + es->argparens++; + break; + } +} + +static +void +expand_got_rparen(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, p, buf, len); + break; + case ES_WANTLPAREN: + expand_missingargs(es, p, false); + es->state = ES_NORMAL; + /* try again */ + expand_got_rparen(es, p, buf, len); + break; + case ES_NOARG: + assert(es->argparens == 0); + if (stringarray_num(&es->args) > 0) { + /* we are after a comma; enter an empty argument */ + expand_newarg(es, buf, 0); + } + es->state = ES_NORMAL; + expand_domacro(es, p); + break; + case ES_HAVEARG: + if (es->argparens > 0) { + es->argparens--; + expand_appendarg(es, buf, len); + } else { + es->state = ES_NORMAL; + expand_domacro(es, p); + } + break; + } +} + +static +void +expand_got_comma(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, p, buf, len); + break; + case ES_WANTLPAREN: + expand_missingargs(es, p, false); + es->state = ES_NORMAL; + /* try again */ + expand_got_comma(es, p, buf, len); + break; + case ES_NOARG: + assert(es->argparens == 0); + expand_newarg(es, buf, 0); + break; + case ES_HAVEARG: + if (es->argparens > 0) { + expand_appendarg(es, buf, len); + } else { + es->state = ES_NOARG; + } + break; + } +} + +static +void +expand_got_other(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, p, buf, len); + break; + case ES_WANTLPAREN: + expand_missingargs(es, p, false); + es->state = ES_NORMAL; + /* try again */ + expand_got_other(es, p, buf, len); + break; + case ES_NOARG: + expand_newarg(es, buf, len); + es->state = ES_HAVEARG; + break; + case ES_HAVEARG: + expand_appendarg(es, buf, len); + break; + } +} + +static +void +expand_got_eof(struct expstate *es, struct place *p) +{ + switch (es->state) { + case ES_NORMAL: + break; + case ES_WANTLPAREN: + expand_missingargs(es, p, false); + break; + case ES_NOARG: + case ES_HAVEARG: + if (es->curmacro) { + complain(p, "Unclosed argument list for macro %s", + es->curmacro->name); + } else { + complain(p, "Unclosed argument list for defined()"); + } + complain_fail(); + expstate_destroyargs(es); + break; + } + expand_send_eof(es, p); + es->state = ES_NORMAL; + es->curmacro = NULL; + es->argparens = 0; +} + +static +void +doexpand(struct expstate *es, struct place *p, char *buf, size_t len) +{ + char *s; + size_t x; + bool inquote = false; + char quote = '\0'; + + while (len > 0) { + x = strspn(buf, ws); + if (x > len) { + /* XXX gross, need strnspn */ + x = len; + } + + if (x > 0) { + expand_got_ws(es, p, buf, x); + buf += x; + len -= x; + continue; + } + + x = strspn(buf, alnum); + if (x > len) { + /* XXX gross, need strnspn */ + x = len; + } + + if (!inquote && x > 0) { + expand_got_word(es, p, buf, x); + buf += x; + len -= x; + continue; + } + + if (!inquote && len > 1 && buf[0] == '/' && buf[1] == '*') { + s = strstr(buf, "*/"); + if (s) { + x = s - buf; + } else { + x = len; + } + expand_got_ws(es, p, buf, x); + buf += x; + len -= x; + continue; + } + + if (!inquote && buf[0] == '(') { + expand_got_lparen(es, p, buf, 1); + buf++; + len--; + continue; + } + + if (!inquote && buf[0] == ')') { + expand_got_rparen(es, p, buf, 1); + buf++; + len--; + continue; + } + + if (!inquote && buf[0] == ',') { + expand_got_comma(es, p, buf, 1); + buf++; + len--; + continue; + } + + if (len > 1 && buf[0] == '\\' && + (buf[1] == '"' || buf[1] == '\'')) { + expand_got_other(es, p, buf, 2); + buf += 2; + len -= 2; + continue; + } + if (!inquote && (buf[0] == '"' || buf[0] == '\'')) { + inquote = true; + quote = buf[0]; + } else if (inquote && buf[0] == quote) { + inquote = false; + } + + expand_got_other(es, p, buf, 1); + buf++; + len--; + } +} + +char * +macroexpand(struct place *p, char *buf, size_t len, bool honordefined) +{ + struct expstate es; + char *ret; + + expstate_init(&es, true, honordefined); + doexpand(&es, p, buf, len); + expand_got_eof(&es, p); + + /* trim to fit, so the malloc debugging won't complain */ + es.buf = dorealloc(es.buf, es.bufmax, strlen(es.buf) + 1); + + ret = es.buf; + es.buf = NULL; + es.bufpos = es.bufmax = 0; + + expstate_cleanup(&es); + + return ret; +} + +void +macro_sendline(struct place *p, char *buf, size_t len) +{ + doexpand(&mainstate, p, buf, len); + output(p, "\n", 1); +} + +void +macro_sendeof(struct place *p) +{ + expand_got_eof(&mainstate, p); +} + +//////////////////////////////////////////////////////////// +// module initialization + +void +macros_init(void) +{ + macrotable_init(); + expstate_init(&mainstate, false, false); +} + +void +macros_cleanup(void) +{ + expstate_cleanup(&mainstate); + macrotable_cleanup(); +} diff --git a/cde/util/tradcpp/macro.h b/cde/util/tradcpp/macro.h new file mode 100644 index 000000000..98671e63b --- /dev/null +++ b/cde/util/tradcpp/macro.h @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +struct place; + +void macros_init(void); +void macros_cleanup(void); + +void macro_define_plain(struct place *, const char *macro, + struct place *, const char *expansion); +void macro_define_params(struct place *, const char *macro, + struct place *, const char *params, + struct place *, const char *expansion); +void macro_undef(const char *macro); +bool macro_isdefined(const char *macro); + +char *macroexpand(struct place *, char *buf, size_t len, bool honordefined); + +void macro_sendline(struct place *, char *buf, size_t len); +void macro_sendeof(struct place *); diff --git a/cde/util/tradcpp/main.c b/cde/util/tradcpp/main.c new file mode 100644 index 000000000..35f9bca13 --- /dev/null +++ b/cde/util/tradcpp/main.c @@ -0,0 +1,1090 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include "version.h" +#include "config.h" +#include "utils.h" +#include "array.h" +#include "mode.h" +#include "place.h" +#include "files.h" +#include "directive.h" +#include "macro.h" + +struct mode mode = { + .werror = false, + + .input_allow_dollars = false, + .input_tabstop = 8, + + .do_stdinc = true, + .do_stddef = true, + + .do_output = true, + .output_linenumbers = true, + .output_retain_comments = false, + .output_file = NULL, + + .do_depend = false, + .depend_report_system = false, + .depend_assume_generated = false, + .depend_issue_fakerules = false, + .depend_quote_target = true, + .depend_target = NULL, + .depend_file = NULL, + + .do_macrolist = false, + .macrolist_include_stddef = false, + .macrolist_include_expansions = false, + + .do_trace = false, + .trace_namesonly = false, + .trace_indented = false, +}; + +struct warns warns = { + .endiflabels = true, + .nestcomment = false, + .undef = false, + .unused = false, +}; + +//////////////////////////////////////////////////////////// +// commandline macros + +struct commandline_macro { + struct place where; + struct place where2; + const char *macro; + const char *expansion; +}; + +static struct array commandline_macros; + +static +void +commandline_macros_init(void) +{ + array_init(&commandline_macros); +} + +static +void +commandline_macros_cleanup(void) +{ + unsigned i, num; + struct commandline_macro *cm; + + num = array_num(&commandline_macros); + for (i=0; iwhere = *p; + cm->where2 = *p2; + cm->macro = macro; + cm->expansion = expansion; + + array_add(&commandline_macros, cm, NULL); +} + +static +void +commandline_def(const struct place *p, char *str) +{ + struct place p2; + char *val; + + if (*str == '\0') { + complain(NULL, "-D: macro name expected"); + die(); + } + + val = strchr(str, '='); + if (val != NULL) { + *val = '\0'; + val++; + } + + if (val) { + p2 = *p; + p2.column += strlen(str); + } else { + place_setbuiltin(&p2, 1); + } + commandline_macro_add(p, str, &p2, val ? val : "1"); +} + +static +void +commandline_undef(const struct place *p, char *str) +{ + if (*str == '\0') { + complain(NULL, "-D: macro name expected"); + die(); + } + commandline_macro_add(p, str, p, NULL); +} + +static +void +apply_commandline_macros(void) +{ + struct commandline_macro *cm; + unsigned i, num; + + num = array_num(&commandline_macros); + for (i=0; iexpansion != NULL) { + macro_define_plain(&cm->where, cm->macro, + &cm->where2, cm->expansion); + } else { + macro_undef(cm->macro); + } + dofree(cm, sizeof(*cm)); + } + array_setsize(&commandline_macros, 0); +} + +static +void +apply_builtin_macro(unsigned num, const char *name, const char *val) +{ + struct place p; + + place_setbuiltin(&p, num); + macro_define_plain(&p, name, &p, val); +} + +static +void +apply_builtin_macros(void) +{ + unsigned n = 1; + +#ifdef CONFIG_OS + apply_builtin_macro(n++, CONFIG_OS, "1"); +#endif +#ifdef CONFIG_OS_2 + apply_builtin_macro(n++, CONFIG_OS_2, "1"); +#endif + +#ifdef CONFIG_CPU + apply_builtin_macro(n++, CONFIG_CPU, "1"); +#endif +#ifdef CONFIG_CPU_2 + apply_builtin_macro(n++, CONFIG_CPU_2, "1"); +#endif + +#ifdef CONFIG_SIZE + apply_builtin_macro(n++, CONFIG_SIZE, "1"); +#endif +#ifdef CONFIG_BINFMT + apply_builtin_macro(n++, CONFIG_BINFMT, "1"); +#endif + +#ifdef CONFIG_COMPILER + apply_builtin_macro(n++, CONFIG_COMPILER, VERSION_MAJOR); + apply_builtin_macro(n++, CONFIG_COMPILER_MINOR, VERSION_MINOR); + apply_builtin_macro(n++, "__VERSION__", VERSION_LONG); +#endif +} + +//////////////////////////////////////////////////////////// +// extra included files + +struct commandline_file { + struct place where; + char *name; + bool suppress_output; +}; + +static struct array commandline_files; + +static +void +commandline_files_init(void) +{ + array_init(&commandline_files); +} + +static +void +commandline_files_cleanup(void) +{ + unsigned i, num; + struct commandline_file *cf; + + num = array_num(&commandline_files); + for (i=0; iwhere = *p; + cf->name = name; + cf->suppress_output = suppress_output; + array_add(&commandline_files, cf, NULL); +} + +static +void +commandline_addfile_output(const struct place *p, char *name) +{ + commandline_addfile(p, name, false); +} + +static +void +commandline_addfile_nooutput(const struct place *p, char *name) +{ + commandline_addfile(p, name, true); +} + +static +void +read_commandline_files(void) +{ + struct commandline_file *cf; + unsigned i, num; + bool save = false; + + num = array_num(&commandline_files); + for (i=0; isuppress_output) { + save = mode.do_output; + mode.do_output = false; + file_readquote(&cf->where, cf->name); + mode.do_output = save; + } else { + file_readquote(&cf->where, cf->name); + } + dofree(cf, sizeof(*cf)); + } + array_setsize(&commandline_files, 0); +} + +//////////////////////////////////////////////////////////// +// include path accumulation + +static struct stringarray incpath_quote; +static struct stringarray incpath_user; +static struct stringarray incpath_system; +static struct stringarray incpath_late; +static const char *sysroot; + +static +void +incpath_init(void) +{ + stringarray_init(&incpath_quote); + stringarray_init(&incpath_user); + stringarray_init(&incpath_system); + stringarray_init(&incpath_late); +} + +static +void +incpath_cleanup(void) +{ + stringarray_setsize(&incpath_quote, 0); + stringarray_setsize(&incpath_user, 0); + stringarray_setsize(&incpath_system, 0); + stringarray_setsize(&incpath_late, 0); + + stringarray_cleanup(&incpath_quote); + stringarray_cleanup(&incpath_user); + stringarray_cleanup(&incpath_system); + stringarray_cleanup(&incpath_late); +} + +static +void +commandline_isysroot(const struct place *p, char *dir) +{ + (void)p; + sysroot = dir; +} + +static +void +commandline_addincpath(struct stringarray *arr, char *s) +{ + if (*s == '\0') { + complain(NULL, "Empty include directory"); + die(); + } + stringarray_add(arr, s, NULL); +} + +static +void +commandline_addincpath_quote(const struct place *p, char *dir) +{ + (void)p; + commandline_addincpath(&incpath_quote, dir); +} + +static +void +commandline_addincpath_user(const struct place *p, char *dir) +{ + (void)p; + commandline_addincpath(&incpath_user, dir); +} + +static +void +commandline_addincpath_system(const struct place *p, char *dir) +{ + (void)p; + commandline_addincpath(&incpath_system, dir); +} + +static +void +commandline_addincpath_late(const struct place *p, char *dir) +{ + (void)p; + commandline_addincpath(&incpath_late, dir); +} + +static +void +loadincludepath(void) +{ + unsigned i, num; + const char *dir; + char *t; + + num = stringarray_num(&incpath_quote); + for (i=0; i 64) { + complain(NULL, "Preposterously large tabstop"); + die(); + } + mode.input_tabstop = val; +} + +/* + * macrolist + */ + +static +void +commandline_dD(void) +{ + mode.do_macrolist = true; + mode.macrolist_include_stddef = false; + mode.macrolist_include_expansions = true; +} + +static +void +commandline_dM(void) +{ + mode.do_macrolist = true; + mode.macrolist_include_stddef = true; + mode.macrolist_include_expansions = true; + mode.do_output = false; +} + +static +void +commandline_dN(void) +{ + mode.do_macrolist = true; + mode.macrolist_include_stddef = false; + mode.macrolist_include_expansions = false; +} + +/* + * include trace + */ + +static +void +commandline_dI(void) +{ + mode.do_trace = true; + mode.trace_namesonly = false; + mode.trace_indented = false; +} + +static +void +commandline_H(void) +{ + mode.do_trace = true; + mode.trace_namesonly = true; + mode.trace_indented = true; +} + +/* + * depend + */ + +static +void +commandline_setdependtarget(const struct place *p, char *str) +{ + (void)p; + mode.depend_target = str; + mode.depend_quote_target = false; +} + +static +void +commandline_setdependtarget_quoted(const struct place *p, char *str) +{ + (void)p; + mode.depend_target = str; + mode.depend_quote_target = true; +} + +static +void +commandline_setdependoutput(const struct place *p, char *str) +{ + (void)p; + mode.depend_file = str; +} + +static +void +commandline_M(void) +{ + mode.do_depend = true; + mode.depend_report_system = true; + mode.do_output = false; +} + +static +void +commandline_MM(void) +{ + mode.do_depend = true; + mode.depend_report_system = false; + mode.do_output = false; +} + +static +void +commandline_MD(void) +{ + mode.do_depend = true; + mode.depend_report_system = true; +} + +static +void +commandline_MMD(void) +{ + mode.do_depend = true; + mode.depend_report_system = false; +} + +static +void +commandline_wall(void) +{ + warns.nestcomment = true; + warns.undef = true; + warns.unused = true; +} + +static +void +commandline_wnoall(void) +{ + warns.nestcomment = false; + warns.undef = false; + warns.unused = false; +} + +static +void +commandline_wnone(void) +{ + warns.nestcomment = false; + warns.endiflabels = false; + warns.undef = false; + warns.unused = false; +} + +//////////////////////////////////////////////////////////// +// options + +struct ignore_option { + const char *string; +}; + +struct flag_option { + const char *string; + bool *flag; + bool setto; +}; + +struct act_option { + const char *string; + void (*func)(void); +}; + +struct prefix_option { + const char *string; + void (*func)(const struct place *, char *); +}; + +struct arg_option { + const char *string; + void (*func)(const struct place *, char *); +}; + +static const struct ignore_option ignore_options[] = { + { "m32" }, + { "traditional" }, +}; +static const unsigned num_ignore_options = HOWMANY(ignore_options); + +static const struct flag_option flag_options[] = { + { "C", &mode.output_retain_comments, true }, + { "CC", &mode.output_retain_comments, true }, + { "MG", &mode.depend_assume_generated, true }, + { "MP", &mode.depend_issue_fakerules, true }, + { "P", &mode.output_linenumbers, false }, + { "Wcomment", &warns.nestcomment, true }, + { "Wendif-labels", &warns.endiflabels, true }, + { "Werror", &mode.werror, true }, + { "Wno-comment", &warns.nestcomment, false }, + { "Wno-endif-labels", &warns.endiflabels, false }, + { "Wno-error", &mode.werror, false }, + { "Wno-undef", &warns.undef, false }, + { "Wno-unused-macros", &warns.unused, false }, + { "Wundef", &warns.undef, true }, + { "Wunused-macros", &warns.unused, true }, + { "fdollars-in-identifiers", &mode.input_allow_dollars, true }, + { "fno-dollars-in-identifiers", &mode.input_allow_dollars, false }, + { "nostdinc", &mode.do_stdinc, false }, + { "undef", &mode.do_stddef, false }, +}; +static const unsigned num_flag_options = HOWMANY(flag_options); + +static const struct act_option act_options[] = { + { "H", commandline_H }, + { "M", commandline_M }, + { "MD", commandline_MD }, + { "MM", commandline_MM }, + { "MMD", commandline_MMD }, + { "Wall", commandline_wall }, + { "Wno-all", commandline_wnoall }, + { "dD", commandline_dD }, + { "dI", commandline_dI }, + { "dM", commandline_dM }, + { "dN", commandline_dN }, + { "w", commandline_wnone }, +}; +static const unsigned num_act_options = HOWMANY(act_options); + +static const struct prefix_option prefix_options[] = { + { "D", commandline_def }, + { "I", commandline_addincpath_user }, + { "U", commandline_undef }, + { "ftabstop=", commandline_tabstop }, + { "std=", commandline_setstd }, +}; +static const unsigned num_prefix_options = HOWMANY(prefix_options); + +static const struct arg_option arg_options[] = { + { "MF", commandline_setdependoutput }, + { "MQ", commandline_setdependtarget_quoted }, + { "MT", commandline_setdependtarget }, + { "idirafter", commandline_addincpath_late }, + { "imacros", commandline_addfile_nooutput }, + { "include", commandline_addfile_output }, + { "iprefix", commandline_setprefix }, + { "iquote", commandline_addincpath_quote }, + { "iremap", commandline_iremap }, + { "isysroot", commandline_isysroot }, + { "isystem", commandline_addincpath_system }, + { "iwithprefix", commandline_addincpath_late_withprefix }, + { "iwithprefixbefore", commandline_addincpath_user_withprefix }, + { "x", commandline_setlang }, +}; +static const unsigned num_arg_options = HOWMANY(arg_options); + +static +bool +check_ignore_option(const char *opt) +{ + unsigned i; + int r; + + for (i=0; i + +struct mode { + bool werror; + bool input_allow_dollars; + unsigned input_tabstop; + bool do_stdinc; + bool do_stddef; + bool do_output; + bool output_linenumbers; + bool output_retain_comments; + const char *output_file; + bool do_depend; + bool depend_report_system; + bool depend_assume_generated; + bool depend_issue_fakerules; + bool depend_quote_target; + const char *depend_target; + const char *depend_file; + bool do_macrolist; + bool macrolist_include_stddef; + bool macrolist_include_expansions; + bool do_trace; + bool trace_namesonly; + bool trace_indented; +}; + +struct warns { + bool endiflabels; + bool nestcomment; + bool undef; + bool unused; +}; + +extern struct mode mode; +extern struct warns warns; diff --git a/cde/util/tradcpp/output.c b/cde/util/tradcpp/output.c new file mode 100644 index 000000000..d1d1f2d4d --- /dev/null +++ b/cde/util/tradcpp/output.c @@ -0,0 +1,193 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "utils.h" +#include "mode.h" +#include "place.h" +#include "output.h" + +static int outputfd = -1; +static bool incomment = false; +static char *linebuf; +static size_t linebufpos, linebufmax; +static struct place linebufplace; + +static +void +output_open(void) +{ + if (mode.output_file == NULL) { + outputfd = STDOUT_FILENO; + } else { + outputfd = open(mode.output_file, O_WRONLY|O_CREAT|O_TRUNC, + 0664); + if (outputfd < 0) { + complain(NULL, "%s: %s", + mode.output_file, strerror(errno)); + die(); + } + } +} + +static +void +dowrite(const char *buf, size_t len) +{ + size_t done; + ssize_t result; + static unsigned write_errors = 0; + + if (!mode.do_output) { + return; + } + + if (outputfd < 0) { + output_open(); + } + + done = 0; + while (done < len) { + result = write(outputfd, buf+done, len-done); + if (result == -1) { + complain(NULL, "%s: write: %s", + mode.output_file, strerror(errno)); + complain_failed(); + write_errors++; + if (write_errors > 5) { + complain(NULL, "%s: giving up", + mode.output_file); + die(); + } + /* XXX is this really a good idea? */ + sleep(1); + } + done += (size_t)result; + } +} + + +static +void +filter_output(const char *buf, size_t len) +{ + size_t pos, start; + bool inesc = false; + bool inquote = false; + char quote = '\0'; + + start = 0; + for (pos = 0; pos < len - 1; pos++) { + if (!inquote && buf[pos] == '/' && buf[pos+1] == '*') { + if (!incomment) { + if (pos > start) { + dowrite(buf + start, pos - start); + } + start = pos; + pos += 2; + incomment = true; + /* cancel out the loop's pos++ */ + pos--; + continue; + } + } else if (buf[pos] == '*' && buf[pos+1] == '/') { + if (incomment) { + pos += 2; + if (mode.output_retain_comments) { + dowrite(buf + start, pos - start); + } + start = pos; + incomment = false; + /* cancel out the loop's pos++ */ + pos--; + continue; + } + } + + if (incomment) { + /* nothing */ + } else if (inesc) { + inesc = false; + } else if (buf[pos] == '\\') { + inesc = true; + } else if (!inquote && (buf[pos] == '"' || buf[pos] == '\'')) { + inquote = true; + quote = buf[pos]; + } else if (inquote && buf[pos] == quote) { + inquote = false; + } + } + pos++; + + if (pos > start) { + if (!incomment || mode.output_retain_comments) { + dowrite(buf + start, pos - start); + } + } +} + +void +output(const struct place *p, const char *buf, size_t len) +{ + size_t oldmax; + + if (linebufpos + len > linebufmax) { + oldmax = linebufmax; + if (linebufmax == 0) { + linebufmax = 64; + } + while (linebufpos + len > linebufmax) { + linebufmax *= 2; + } + linebuf = dorealloc(linebuf, oldmax, linebufmax); + } + if (linebufpos == 0) { + linebufplace = *p; + } + memcpy(linebuf + linebufpos, buf, len); + linebufpos += len; + + if (len == 1 && buf[0] == '\n') { + filter_output(linebuf, linebufpos); + linebufpos = 0; + } +} + +void +output_eof(void) +{ + if (mode.output_file != NULL && outputfd >= 0) { + close(outputfd); + } + outputfd = -1; +} diff --git a/cde/util/tradcpp/output.h b/cde/util/tradcpp/output.h new file mode 100644 index 000000000..c1cb6629e --- /dev/null +++ b/cde/util/tradcpp/output.h @@ -0,0 +1,31 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +void output(const struct place *p, const char *buf, size_t len); +void output_eof(void); diff --git a/cde/util/tradcpp/place.c b/cde/util/tradcpp/place.c new file mode 100644 index 000000000..267104875 --- /dev/null +++ b/cde/util/tradcpp/place.c @@ -0,0 +1,241 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include "utils.h" +#include "array.h" +#include "place.h" + +struct placefile { + struct place includedfrom; + char *dir; + char *name; + int depth; + bool fromsystemdir; +}; +DECLARRAY(placefile, static UNUSED); +DEFARRAY(placefile, static); + +static struct placefilearray placefiles; +static bool overall_failure; + +static const char *myprogname; + +//////////////////////////////////////////////////////////// +// seenfiles + +static +struct placefile * +placefile_create(const struct place *from, const char *name, + bool fromsystemdir) +{ + struct placefile *pf; + const char *s; + size_t len; + + pf = domalloc(sizeof(*pf)); + pf->includedfrom = *from; + + s = strrchr(name, '/'); + len = (s == NULL) ? 0 : s - name; + pf->dir = dostrndup(name, len); + + pf->name = dostrdup(name); + pf->fromsystemdir = fromsystemdir; + + if (from->file != NULL) { + pf->depth = from->file->depth + 1; + } else { + pf->depth = 1; + } + return pf; +} + +static +void +placefile_destroy(struct placefile *pf) +{ + dostrfree(pf->name); + dofree(pf, sizeof(*pf)); +} + +DESTROYALL_ARRAY(placefile, ); + +const char * +place_getparsedir(const struct place *place) +{ + if (place->file == NULL) { + return "."; + } + return place->file->dir; +} + +const struct placefile * +place_addfile(const struct place *place, const char *file, bool issystem) +{ + struct placefile *pf; + + pf = placefile_create(place, file, issystem); + placefilearray_add(&placefiles, pf, NULL); + if (pf->depth > 120) { + complain(place, "Maximum include nesting depth exceeded"); + die(); + } + return pf; +} + +//////////////////////////////////////////////////////////// +// places + +void +place_setnowhere(struct place *p) +{ + p->type = P_NOWHERE; + p->file = NULL; + p->line = 0; + p->column = 0; +} + +void +place_setbuiltin(struct place *p, unsigned num) +{ + p->type = P_BUILTIN; + p->file = NULL; + p->line = num; + p->column = 1; +} + +void +place_setcommandline(struct place *p, unsigned line, unsigned column) +{ + p->type = P_COMMANDLINE; + p->file = NULL; + p->line = line; + p->column = column; +} + +void +place_setfilestart(struct place *p, const struct placefile *pf) +{ + p->type = P_FILE; + p->file = pf; + p->line = 1; + p->column = 1; +} + +static +const char * +place_getname(const struct place *p) +{ + switch (p->type) { + case P_NOWHERE: return ""; + case P_BUILTIN: return ""; + case P_COMMANDLINE: return ""; + case P_FILE: return p->file->name; + } + assert(0); + return NULL; +} + +static +void +place_printfrom(const struct place *p) +{ + const struct place *from; + + if (p->file == NULL) { + return; + } + from = &p->file->includedfrom; + if (from->type != P_NOWHERE) { + place_printfrom(from); + fprintf(stderr, "In file included from %s:%u:%u:\n", + place_getname(from), from->line, from->column); + } +} + +//////////////////////////////////////////////////////////// +// complaints + +void +complain_init(const char *pn) +{ + myprogname = pn; +} + +void +complain(const struct place *p, const char *fmt, ...) +{ + va_list ap; + + if (p != NULL) { + place_printfrom(p); + fprintf(stderr, "%s:%u:%u: ", place_getname(p), + p->line, p->column); + } else { + fprintf(stderr, "%s: ", myprogname); + } + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +void +complain_fail(void) +{ + overall_failure = true; +} + +bool +complain_failed(void) +{ + return overall_failure; +} + +//////////////////////////////////////////////////////////// +// module init and cleanup + +void +place_init(void) +{ + placefilearray_init(&placefiles); +} + +void +place_cleanup(void) +{ + placefilearray_destroyall(&placefiles); + placefilearray_cleanup(&placefiles); +} diff --git a/cde/util/tradcpp/place.h b/cde/util/tradcpp/place.h new file mode 100644 index 000000000..311645be1 --- /dev/null +++ b/cde/util/tradcpp/place.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +enum places { + P_NOWHERE, + P_BUILTIN, + P_COMMANDLINE, + P_FILE, +}; +struct place { + enum places type; + const struct placefile *file; + unsigned line; + unsigned column; +}; + +void place_init(void); +void place_cleanup(void); + +void place_setnowhere(struct place *p); +void place_setbuiltin(struct place *p, unsigned num); +void place_setcommandline(struct place *p, unsigned word, unsigned column); +void place_setfilestart(struct place *p, const struct placefile *pf); + +const char *place_getparsedir(const struct place *incplace); + +const struct placefile *place_addfile(const struct place *incplace, + const char *name, bool fromsystemdir); diff --git a/cde/util/tradcpp/tests/subdir/test.h b/cde/util/tradcpp/tests/subdir/test.h new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/cde/util/tradcpp/tests/subdir/test.h @@ -0,0 +1 @@ +hello diff --git a/cde/util/tradcpp/tests/t01.c b/cde/util/tradcpp/tests/t01.c new file mode 100644 index 000000000..ffe414b35 --- /dev/null +++ b/cde/util/tradcpp/tests/t01.c @@ -0,0 +1 @@ +glop. diff --git a/cde/util/tradcpp/tests/t01.good b/cde/util/tradcpp/tests/t01.good new file mode 100644 index 000000000..ffe414b35 --- /dev/null +++ b/cde/util/tradcpp/tests/t01.good @@ -0,0 +1 @@ +glop. diff --git a/cde/util/tradcpp/tests/t02.c b/cde/util/tradcpp/tests/t02.c new file mode 100644 index 000000000..00baa01a3 --- /dev/null +++ b/cde/util/tradcpp/tests/t02.c @@ -0,0 +1,2 @@ +#define glop flop +glop diff --git a/cde/util/tradcpp/tests/t02.good b/cde/util/tradcpp/tests/t02.good new file mode 100644 index 000000000..fca51a71d --- /dev/null +++ b/cde/util/tradcpp/tests/t02.good @@ -0,0 +1 @@ +flop diff --git a/cde/util/tradcpp/tests/t03.c b/cde/util/tradcpp/tests/t03.c new file mode 100644 index 000000000..172819809 --- /dev/null +++ b/cde/util/tradcpp/tests/t03.c @@ -0,0 +1,2 @@ +#define glop(x) flop x +glop(boo) diff --git a/cde/util/tradcpp/tests/t03.good b/cde/util/tradcpp/tests/t03.good new file mode 100644 index 000000000..6f3795d99 --- /dev/null +++ b/cde/util/tradcpp/tests/t03.good @@ -0,0 +1 @@ +flop boo diff --git a/cde/util/tradcpp/tests/t04.c b/cde/util/tradcpp/tests/t04.c new file mode 100644 index 000000000..7ef9c20e7 --- /dev/null +++ b/cde/util/tradcpp/tests/t04.c @@ -0,0 +1,2 @@ +#define string(x) "x" +string(abc) diff --git a/cde/util/tradcpp/tests/t04.good b/cde/util/tradcpp/tests/t04.good new file mode 100644 index 000000000..d1cc1b4e5 --- /dev/null +++ b/cde/util/tradcpp/tests/t04.good @@ -0,0 +1 @@ +"abc" diff --git a/cde/util/tradcpp/tests/t05.c b/cde/util/tradcpp/tests/t05.c new file mode 100644 index 000000000..b52a29fe6 --- /dev/null +++ b/cde/util/tradcpp/tests/t05.c @@ -0,0 +1,2 @@ +#define concat(a, b) a/**/b +concat(abc,def) diff --git a/cde/util/tradcpp/tests/t05.good b/cde/util/tradcpp/tests/t05.good new file mode 100644 index 000000000..0373d9336 --- /dev/null +++ b/cde/util/tradcpp/tests/t05.good @@ -0,0 +1 @@ +abcdef diff --git a/cde/util/tradcpp/tests/t06.c b/cde/util/tradcpp/tests/t06.c new file mode 100644 index 000000000..13350a4d5 --- /dev/null +++ b/cde/util/tradcpp/tests/t06.c @@ -0,0 +1,2 @@ +/*glop*/ + diff --git a/cde/util/tradcpp/tests/t06.good b/cde/util/tradcpp/tests/t06.good new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/cde/util/tradcpp/tests/t06.good @@ -0,0 +1 @@ + diff --git a/cde/util/tradcpp/tests/t07.c b/cde/util/tradcpp/tests/t07.c new file mode 100644 index 000000000..14eb50270 --- /dev/null +++ b/cde/util/tradcpp/tests/t07.c @@ -0,0 +1,3 @@ +/* + * gloop + */ diff --git a/cde/util/tradcpp/tests/t07.good b/cde/util/tradcpp/tests/t07.good new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/cde/util/tradcpp/tests/t07.good @@ -0,0 +1 @@ + diff --git a/cde/util/tradcpp/tests/t08.c b/cde/util/tradcpp/tests/t08.c new file mode 100644 index 000000000..0f052fff5 --- /dev/null +++ b/cde/util/tradcpp/tests/t08.c @@ -0,0 +1,41 @@ +/*#include */ + +int d = +#if 2 > 1 ? 0 : 0 ? 1 : 1 +1 +#else +0 +#endif +; + +int e = +#if (2 > 1 ? 0 : 0) ? 1 : 1 +1 +#else +0 +#endif +; + +int f = +#if 2 > 1 ? 0 : (0 ? 1 : 1) +1 +#else +0 +#endif +; + + +int +main() +{ + int a, b, c; + + a = 2 > 1 ? 0 : 0 ? 1 : 1; + b = (2 > 1 ? 0 : 0) ? 1 : 1; + c = 2 > 1 ? 0 : (0 ? 1 : 1); + + printf("%d %d %d\n", a, b, c); + printf("%d %d %d\n", d, e, f); + + return 0; +} diff --git a/cde/util/tradcpp/tests/t08.good b/cde/util/tradcpp/tests/t08.good new file mode 100644 index 000000000..a5dee333b --- /dev/null +++ b/cde/util/tradcpp/tests/t08.good @@ -0,0 +1,21 @@ + +int d = +0 +; +int e = +1 +; +int f = +0 +; +int +main() +{ + int a, b, c; + a = 2 > 1 ? 0 : 0 ? 1 : 1; + b = (2 > 1 ? 0 : 0) ? 1 : 1; + c = 2 > 1 ? 0 : (0 ? 1 : 1); + printf("%d %d %d\n", a, b, c); + printf("%d %d %d\n", d, e, f); + return 0; +} diff --git a/cde/util/tradcpp/tests/t09.c b/cde/util/tradcpp/tests/t09.c new file mode 100644 index 000000000..eea8518f7 --- /dev/null +++ b/cde/util/tradcpp/tests/t09.c @@ -0,0 +1,6 @@ +#define STOP */ +#define START /* + +/* + * blah blah blah STOP fnord START goop moop + */ diff --git a/cde/util/tradcpp/tests/t09.good b/cde/util/tradcpp/tests/t09.good new file mode 100644 index 000000000..e69de29bb diff --git a/cde/util/tradcpp/tests/t10.c b/cde/util/tradcpp/tests/t10.c new file mode 100644 index 000000000..6a3b44bb7 --- /dev/null +++ b/cde/util/tradcpp/tests/t10.c @@ -0,0 +1,3 @@ +#define mac(r)o +mac(3) +mac() diff --git a/cde/util/tradcpp/tests/t10.good b/cde/util/tradcpp/tests/t10.good new file mode 100644 index 000000000..207853da3 --- /dev/null +++ b/cde/util/tradcpp/tests/t10.good @@ -0,0 +1,2 @@ +o +o diff --git a/cde/util/tradcpp/tests/t11.c b/cde/util/tradcpp/tests/t11.c new file mode 100644 index 000000000..62459f245 --- /dev/null +++ b/cde/util/tradcpp/tests/t11.c @@ -0,0 +1,2 @@ +#define BOO BOO +BOO diff --git a/cde/util/tradcpp/tests/t11.good b/cde/util/tradcpp/tests/t11.good new file mode 100644 index 000000000..58265e23d --- /dev/null +++ b/cde/util/tradcpp/tests/t11.good @@ -0,0 +1 @@ +BOO diff --git a/cde/util/tradcpp/tests/t12.c b/cde/util/tradcpp/tests/t12.c new file mode 100644 index 000000000..748a61b4a --- /dev/null +++ b/cde/util/tradcpp/tests/t12.c @@ -0,0 +1,2 @@ +#define BOO(yah) BOO(yah) +BOO(yah) diff --git a/cde/util/tradcpp/tests/t12.good b/cde/util/tradcpp/tests/t12.good new file mode 100644 index 000000000..c5739adea --- /dev/null +++ b/cde/util/tradcpp/tests/t12.good @@ -0,0 +1 @@ +BOO(yah) diff --git a/cde/util/tradcpp/tests/t13.c b/cde/util/tradcpp/tests/t13.c new file mode 100644 index 000000000..43311ff16 --- /dev/null +++ b/cde/util/tradcpp/tests/t13.c @@ -0,0 +1,4 @@ +/* +#define FOO BAR +*/ +FOO diff --git a/cde/util/tradcpp/tests/t13.good b/cde/util/tradcpp/tests/t13.good new file mode 100644 index 000000000..85c94df86 --- /dev/null +++ b/cde/util/tradcpp/tests/t13.good @@ -0,0 +1,2 @@ + +FOO diff --git a/cde/util/tradcpp/tests/t14.c b/cde/util/tradcpp/tests/t14.c new file mode 100644 index 000000000..d511af5b1 --- /dev/null +++ b/cde/util/tradcpp/tests/t14.c @@ -0,0 +1,4 @@ +/* +#define FOO BAR */ +FOO +FOO diff --git a/cde/util/tradcpp/tests/t14.good b/cde/util/tradcpp/tests/t14.good new file mode 100644 index 000000000..5922e8365 --- /dev/null +++ b/cde/util/tradcpp/tests/t14.good @@ -0,0 +1,3 @@ + +FOO +FOO diff --git a/cde/util/tradcpp/tests/t15.c b/cde/util/tradcpp/tests/t15.c new file mode 100644 index 000000000..a4ca3ab8e --- /dev/null +++ b/cde/util/tradcpp/tests/t15.c @@ -0,0 +1,3 @@ +#define FOO /* BAR */ BAZ +FOO +FOO diff --git a/cde/util/tradcpp/tests/t15.good b/cde/util/tradcpp/tests/t15.good new file mode 100644 index 000000000..f013552f2 --- /dev/null +++ b/cde/util/tradcpp/tests/t15.good @@ -0,0 +1,2 @@ + BAZ + BAZ diff --git a/cde/util/tradcpp/tests/t16.c b/cde/util/tradcpp/tests/t16.c new file mode 100644 index 000000000..b42eece48 --- /dev/null +++ b/cde/util/tradcpp/tests/t16.c @@ -0,0 +1,11 @@ +#define a() x +a() +a () +#define b(p) p +x/**/b(1)/**/x +x/**/b (1)/**/x +x/**/b()/**/x +#define c(p,q) p/**/q +x/**/c(1,2)/**/x +x/**/c(1)/**/x +x/**/c()/**/x diff --git a/cde/util/tradcpp/tests/t16.good b/cde/util/tradcpp/tests/t16.good new file mode 100644 index 000000000..8120b94ee --- /dev/null +++ b/cde/util/tradcpp/tests/t16.good @@ -0,0 +1,11 @@ +x +x +x1x +x1x +xx +x12x +t16.c:10:1: Wrong number of arguments for macro c; found 1, expected 2 +x1x +t16.c:11:1: Wrong number of arguments for macro c; found 0, expected 2 +xx +FAILED diff --git a/cde/util/tradcpp/tests/t17.c b/cde/util/tradcpp/tests/t17.c new file mode 100644 index 000000000..a10530bf7 --- /dev/null +++ b/cde/util/tradcpp/tests/t17.c @@ -0,0 +1,2 @@ +#define file "subdir/test.h" +#include file diff --git a/cde/util/tradcpp/tests/t17.good b/cde/util/tradcpp/tests/t17.good new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/cde/util/tradcpp/tests/t17.good @@ -0,0 +1 @@ +hello diff --git a/cde/util/tradcpp/tests/t18.c b/cde/util/tradcpp/tests/t18.c new file mode 100644 index 000000000..4515935f2 --- /dev/null +++ b/cde/util/tradcpp/tests/t18.c @@ -0,0 +1,2 @@ +#if FOO /* ignore me */ +#endif diff --git a/cde/util/tradcpp/tests/t18.good b/cde/util/tradcpp/tests/t18.good new file mode 100644 index 000000000..e69de29bb diff --git a/cde/util/tradcpp/tests/t19.c b/cde/util/tradcpp/tests/t19.c new file mode 100644 index 000000000..b690dbcc7 --- /dev/null +++ b/cde/util/tradcpp/tests/t19.c @@ -0,0 +1,3 @@ +#define foo /* comment continues + into the next line */ baz +baz diff --git a/cde/util/tradcpp/tests/t19.good b/cde/util/tradcpp/tests/t19.good new file mode 100644 index 000000000..76018072e --- /dev/null +++ b/cde/util/tradcpp/tests/t19.good @@ -0,0 +1 @@ +baz diff --git a/cde/util/tradcpp/tests/t20.c b/cde/util/tradcpp/tests/t20.c new file mode 100644 index 000000000..372ca74e8 --- /dev/null +++ b/cde/util/tradcpp/tests/t20.c @@ -0,0 +1 @@ +#undef foo /* blah */ diff --git a/cde/util/tradcpp/tests/t20.good b/cde/util/tradcpp/tests/t20.good new file mode 100644 index 000000000..e69de29bb diff --git a/cde/util/tradcpp/tests/t21.c b/cde/util/tradcpp/tests/t21.c new file mode 100644 index 000000000..8bb30b4d4 --- /dev/null +++ b/cde/util/tradcpp/tests/t21.c @@ -0,0 +1,3 @@ +# define FOO BAR + #undef FOO /* would be wrong */ +FOO diff --git a/cde/util/tradcpp/tests/t21.good b/cde/util/tradcpp/tests/t21.good new file mode 100644 index 000000000..187c1f543 --- /dev/null +++ b/cde/util/tradcpp/tests/t21.good @@ -0,0 +1,2 @@ + #undef BAR +BAR diff --git a/cde/util/tradcpp/tests/t22.c b/cde/util/tradcpp/tests/t22.c new file mode 100644 index 000000000..4b10fe3ce --- /dev/null +++ b/cde/util/tradcpp/tests/t22.c @@ -0,0 +1,2 @@ +pa/* +*/ste diff --git a/cde/util/tradcpp/tests/t22.good b/cde/util/tradcpp/tests/t22.good new file mode 100644 index 000000000..b467804f7 --- /dev/null +++ b/cde/util/tradcpp/tests/t22.good @@ -0,0 +1 @@ +paste diff --git a/cde/util/tradcpp/tests/t23.c b/cde/util/tradcpp/tests/t23.c new file mode 100644 index 000000000..23ca40235 --- /dev/null +++ b/cde/util/tradcpp/tests/t23.c @@ -0,0 +1,8 @@ +/* +fnord1 +/* +fnord2 + */ +foo +*/ +bar diff --git a/cde/util/tradcpp/tests/t23.good b/cde/util/tradcpp/tests/t23.good new file mode 100644 index 000000000..9f9cc39b0 --- /dev/null +++ b/cde/util/tradcpp/tests/t23.good @@ -0,0 +1,4 @@ + +foo +*/ +bar diff --git a/cde/util/tradcpp/tests/t24.c b/cde/util/tradcpp/tests/t24.c new file mode 100644 index 000000000..6982e9eed --- /dev/null +++ b/cde/util/tradcpp/tests/t24.c @@ -0,0 +1,67 @@ +#if 0 +wrong +#endif + +#if 1 +right +#endif + +#if -1 +right +#endif + +#if 0 + 0 +wrong +#endif + +#if 1 + 1 +right +#endif + +#if 1 - 1 +wrong +#endif + +#if -1 + 1 +wrong +#endif + +#if 3 - 2 - 1 +wrong +#endif + +#if 3 * 2 - 6 +wrong +#endif + +#if 6 - 2 * 3 +wrong +#endif + +#if 3 - 3 && 1 +wrong +#endif + +#if 3 - 3 || 0 +wrong +#endif + +#if 1 && 0 +wrong +#endif + +#if 0 && 1 +wrong +#endif + +#if 1 || 0 +right +#endif + +#if 0 || 1 +right +#endif + +#if (0 || 1) && (0 || 0) +wrong +#endif diff --git a/cde/util/tradcpp/tests/t24.good b/cde/util/tradcpp/tests/t24.good new file mode 100644 index 000000000..d2dd3c2c1 --- /dev/null +++ b/cde/util/tradcpp/tests/t24.good @@ -0,0 +1,5 @@ +right +right +right +right +right diff --git a/cde/util/tradcpp/tests/t25.c b/cde/util/tradcpp/tests/t25.c new file mode 100644 index 000000000..16dd7bc57 --- /dev/null +++ b/cde/util/tradcpp/tests/t25.c @@ -0,0 +1,4 @@ +#define FOO foo /* +#undef FOO +#define FOO bar */ +FOO diff --git a/cde/util/tradcpp/tests/t25.good b/cde/util/tradcpp/tests/t25.good new file mode 100644 index 000000000..564d4ddac --- /dev/null +++ b/cde/util/tradcpp/tests/t25.good @@ -0,0 +1 @@ +foo diff --git a/cde/util/tradcpp/tests/t26.c b/cde/util/tradcpp/tests/t26.c new file mode 100644 index 000000000..3239b9df6 --- /dev/null +++ b/cde/util/tradcpp/tests/t26.c @@ -0,0 +1,4 @@ +#define FOO foo +FOO +"FOO" +'FOO' diff --git a/cde/util/tradcpp/tests/t26.good b/cde/util/tradcpp/tests/t26.good new file mode 100644 index 000000000..fcde2b35c --- /dev/null +++ b/cde/util/tradcpp/tests/t26.good @@ -0,0 +1,3 @@ +foo +"FOO" +'FOO' diff --git a/cde/util/tradcpp/tests/t27.c b/cde/util/tradcpp/tests/t27.c new file mode 100644 index 000000000..620136665 --- /dev/null +++ b/cde/util/tradcpp/tests/t27.c @@ -0,0 +1,29 @@ +1. +#define A(a) a +A(); + +2. +#define B(a, b) (a,b) +B(a, ); +B(, b); +B( , ); +B(a,); +B(,b); +B(,); + +3. +#define C(a, b, c) (a,b,c) +C(a, b, ); +C(a, , c); +C(, , c); +C(a, , ); +C(, b, ); +C(, , c); +C(, , ) +C(a,b,); +C(a,,c); +C(,,c); +C(a,,); +C(,b,); +C(,,c); +C(,,) diff --git a/cde/util/tradcpp/tests/t27.good b/cde/util/tradcpp/tests/t27.good new file mode 100644 index 000000000..e9b36da23 --- /dev/null +++ b/cde/util/tradcpp/tests/t27.good @@ -0,0 +1,24 @@ +1. +; +2. +(a, ); +(, b); +( , ); +(a,); +(,b); +(,); +3. +(a, b, ); +(a, , c); +(, , c); +(a, , ); +(, b, ); +(, , c); +(, , ) +(a,b,); +(a,,c); +(,,c); +(a,,); +(,b,); +(,,c); +(,,) diff --git a/cde/util/tradcpp/tests/t28.c b/cde/util/tradcpp/tests/t28.c new file mode 100644 index 000000000..7fec3bf59 --- /dev/null +++ b/cde/util/tradcpp/tests/t28.c @@ -0,0 +1,53 @@ +#if 1 +. right + +# if 1 +.. right +# elif 1 +.. wrong +# elif 0 +.. wrong +# else +.. wrong +# endif + +#elif 1 +. wrong + +# if 1 +.. wrong +# elif 1 +.. wrong +# elif 0 +.. wrong +# else +.. wrong +# endif + +#elif 0 +. wrong + +# if 1 +.. wrong +# elif 1 +.. wrong +# elif 0 +.. wrong +# else +.. wrong +# endif + +#else +. wrong + +# if 1 +.. wrong +# elif 1 +.. wrong +# elif 0 +.. wrong +# else +.. wrong +# endif + +#endif diff --git a/cde/util/tradcpp/tests/t28.good b/cde/util/tradcpp/tests/t28.good new file mode 100644 index 000000000..ac7b1e39e --- /dev/null +++ b/cde/util/tradcpp/tests/t28.good @@ -0,0 +1,2 @@ +. right +.. right diff --git a/cde/util/tradcpp/tests/t29.c b/cde/util/tradcpp/tests/t29.c new file mode 100644 index 000000000..cfd36951c --- /dev/null +++ b/cde/util/tradcpp/tests/t29.c @@ -0,0 +1,4 @@ +#if 0 +# if this is a syntax error +# endif +#endif diff --git a/cde/util/tradcpp/tests/t29.good b/cde/util/tradcpp/tests/t29.good new file mode 100644 index 000000000..e69de29bb diff --git a/cde/util/tradcpp/tests/t30.c b/cde/util/tradcpp/tests/t30.c new file mode 100644 index 000000000..77ea1267c --- /dev/null +++ b/cde/util/tradcpp/tests/t30.c @@ -0,0 +1,2 @@ +#define x(a,b,c) a;b;c; +x((,),x,",") diff --git a/cde/util/tradcpp/tests/t30.good b/cde/util/tradcpp/tests/t30.good new file mode 100644 index 000000000..2262237f5 --- /dev/null +++ b/cde/util/tradcpp/tests/t30.good @@ -0,0 +1 @@ +(,);x;","; diff --git a/cde/util/tradcpp/tests/t31.c b/cde/util/tradcpp/tests/t31.c new file mode 100644 index 000000000..b2e61faa6 --- /dev/null +++ b/cde/util/tradcpp/tests/t31.c @@ -0,0 +1,6 @@ +this line 'has /* no' comment */ in it + +#define BELCH(x) 'x' +"BELCH(123)": BELCH(123) +'BELCH(123)': BELCH(123) + diff --git a/cde/util/tradcpp/tests/t31.good b/cde/util/tradcpp/tests/t31.good new file mode 100644 index 000000000..e90ba2a4f --- /dev/null +++ b/cde/util/tradcpp/tests/t31.good @@ -0,0 +1,3 @@ +this line 'has /* no' comment */ in it +"BELCH(123)": '123' +'BELCH(123)': '123' diff --git a/cde/util/tradcpp/tests/t32.c b/cde/util/tradcpp/tests/t32.c new file mode 100644 index 000000000..9d880ca0b --- /dev/null +++ b/cde/util/tradcpp/tests/t32.c @@ -0,0 +1,21 @@ +#define foo(x) "x" +#define bar(x) 'x' +#define baz frob +foo(3) +bar(3) +foo(baz) +bar(baz) +"baz" +'baz' +"foo(baz)" +"bar(baz)" + +#define foo2(x) foo(x) +#define bar2(x) bar(x) +foo2(baz) +bar2(baz) + +#define foo3(x) foo2(x) +#define bar3(x) bar2(x) +foo3(baz) +bar3(baz) diff --git a/cde/util/tradcpp/tests/t32.good b/cde/util/tradcpp/tests/t32.good new file mode 100644 index 000000000..2b34601f7 --- /dev/null +++ b/cde/util/tradcpp/tests/t32.good @@ -0,0 +1,12 @@ +"3" +'3' +"baz" +'baz' +"baz" +'baz' +"foo(baz)" +"bar(baz)" +"baz" +'baz' +"baz" +'baz' diff --git a/cde/util/tradcpp/tests/t33.c b/cde/util/tradcpp/tests/t33.c new file mode 100644 index 000000000..01f879c06 --- /dev/null +++ b/cde/util/tradcpp/tests/t33.c @@ -0,0 +1,6 @@ +/* make sure that R gets defined and doesn't end up part of a string */ +#define Q " +#define R r +#define S " +R +Q diff --git a/cde/util/tradcpp/tests/t33.good b/cde/util/tradcpp/tests/t33.good new file mode 100644 index 000000000..5af7d8fa7 --- /dev/null +++ b/cde/util/tradcpp/tests/t33.good @@ -0,0 +1,3 @@ + +r +" diff --git a/cde/util/tradcpp/tests/t34.c b/cde/util/tradcpp/tests/t34.c new file mode 100644 index 000000000..c2cbe20b0 --- /dev/null +++ b/cde/util/tradcpp/tests/t34.c @@ -0,0 +1,5 @@ +#define Q " +#define FOO foo +Q FOO Q + + diff --git a/cde/util/tradcpp/tests/t34.good b/cde/util/tradcpp/tests/t34.good new file mode 100644 index 000000000..3dbb0196d --- /dev/null +++ b/cde/util/tradcpp/tests/t34.good @@ -0,0 +1 @@ +" foo " diff --git a/cde/util/tradcpp/tests/t35.c b/cde/util/tradcpp/tests/t35.c new file mode 100644 index 000000000..636495ea4 --- /dev/null +++ b/cde/util/tradcpp/tests/t35.c @@ -0,0 +1,6 @@ +#define Q " +#define FOO foo + +Q FOO Q 'I like "FOO" and "BAR"' + + diff --git a/cde/util/tradcpp/tests/t35.good b/cde/util/tradcpp/tests/t35.good new file mode 100644 index 000000000..396432b58 --- /dev/null +++ b/cde/util/tradcpp/tests/t35.good @@ -0,0 +1 @@ +" foo " 'I like "FOO" and "BAR"' diff --git a/cde/util/tradcpp/tests/t36.c b/cde/util/tradcpp/tests/t36.c new file mode 100644 index 000000000..5f22e2419 --- /dev/null +++ b/cde/util/tradcpp/tests/t36.c @@ -0,0 +1,7 @@ +#define C(x) //**/* x */**// +C(3) +C(abc /* def */ ghi) + +#define D(x) ///**/**/**///**/* x */**///**/**/**/// +D(3) +D(abc /* def */ ghi) diff --git a/cde/util/tradcpp/tests/t36.good b/cde/util/tradcpp/tests/t36.good new file mode 100644 index 000000000..c2f258cdf --- /dev/null +++ b/cde/util/tradcpp/tests/t36.good @@ -0,0 +1,4 @@ +/* 3 */ +/* abc ghi */ +//**/* 3 */**// +//**/* abc ghi */**// diff --git a/cde/util/tradcpp/tests/t37.c b/cde/util/tradcpp/tests/t37.c new file mode 100644 index 000000000..16af5b113 --- /dev/null +++ b/cde/util/tradcpp/tests/t37.c @@ -0,0 +1,19 @@ +#define BC //**/* +#define EC */**// + +BC +comment? +EC + +BC comment? EC + +#define FOO(x) x +FOO(abc BC def EC ghi) + +#define BAR(x, y) x y +BAR(abc BC def, ghi EC jkl) + +BC +#define BAZ baz +EC +BAZ diff --git a/cde/util/tradcpp/tests/t37.good b/cde/util/tradcpp/tests/t37.good new file mode 100644 index 000000000..e8aa6a929 --- /dev/null +++ b/cde/util/tradcpp/tests/t37.good @@ -0,0 +1,9 @@ +/* +comment? +*/ +/* comment? */ +abc /* def */ ghi +abc /* def ghi */ jkl +/* +*/ +baz diff --git a/cde/util/tradcpp/tests/tradcpp.sh b/cde/util/tradcpp/tests/tradcpp.sh new file mode 100644 index 000000000..fb3d0dccf --- /dev/null +++ b/cde/util/tradcpp/tests/tradcpp.sh @@ -0,0 +1,36 @@ +test_case() { + local name="$1" + local source="$2" + local tradcpp="$3" + local descr="Test tradcpp behavior" + atf_test_case ${name} + if [ -e "$(atf_get_srcdir)/${name}.txt" ]; then + descr=$(cat "$(atf_get_srcdir)/${name}.txt") + fi + eval "${name}_head() { \ + atf_set descr \"${descr}\"; \ + atf_set require.progs \"/usr/bin/tradcpp\"; \ + }" + local stdout="file:$(atf_get_srcdir)/${name}.good" + local options="" + local options_file="$(atf_get_srcdir)/${name}.cmdline" + if [ -e ${options_file} ]; then + options=$(cat ${options_file}) + fi + eval "${name}_body() { \ + atf_check -s eq:0 -o ${stdout} -x '${tradcpp} ${options} ${source} 2>&1 || echo FAILED'; \ + }" +} + +atf_init_test_cases() { + local tradcpp=$(make -V .OBJDIR -C $(atf_get_srcdir)/..)/tradcpp + if [ ! -x ${tradcpp} ]; then + tradcpp=/usr/bin/tradcpp + fi + cd $(atf_get_srcdir) + for testfile in t*.c; do + local name=${testfile%%.c} + test_case ${name} ${testfile} ${tradcpp} + atf_add_test_case ${name} + done +} diff --git a/cde/util/tradcpp/tradcpp.1 b/cde/util/tradcpp/tradcpp.1 new file mode 100644 index 000000000..952d8b723 --- /dev/null +++ b/cde/util/tradcpp/tradcpp.1 @@ -0,0 +1,353 @@ +.\" +.\" Copyright (c) 2013 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by David A. Holland. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd June 11, 2013 +.Dt TRADCPP 1 +.Os +.Sh NAME +.Nm tradcpp +.Nd traditional (K&R-style) C macro preprocessor +.Sh SYNOPSIS +.Nm tradcpp +.Op Fl options +.Op Ar input-file Op Ar output-file +.Sh DESCRIPTION +The +.Nm +command provides a traditional K&R-style C macro preprocessor. +It is intended to be suitable for historical Unix uses of the +preprocessor, such as +.Xr imake 1 , +particularly those that depend on preservation of whitespace. +.Pp +The chief ways in which traditional cpp differs from +Standard C are: +.Bl -bullet -offset indent +.It +Macro arguments are expanded within quoted strings. +There is no stringize operator. +.It +There is no token pasting operator; tokens can be concatenated by +placing comments between them. +This process is also not limited to valid C language tokens. +.It +Whitespace is preserved, and in particular tabs are not expanded into +spaces. +Furthermore, additional whitespace is not injected. +.El +.Sh OPTIONS +.Nm +has many options, many of which are defined for compatibility with +.Xr gcc 1 +or other compilers. +Many of the options are not yet implemented. +.\" The option lists have been sorted in what I hope is a sensible +.\" order. Please don't arbitrarily alphabetize them. +.Ss Common Options +.Bl -tag -width bubblebabble +.It Fl C +Retain comments in output. +.It Fl Dmacro[=expansion] +Provide a definition for the named macro. +If no expansion is provided, the value +.Dq 1 +is used. +Note that like many Unix compilers, +.Nm +does not accept a space between the +.Dq D +and the macro name. +.It Fl Ipath +Add the specified path to the main list of include directories. +Note that like many Unix compilers, +.Nm +does not accept a space between the +.Dq I +and the directory name. +.It Fl nostdinc +Do not search the standard system include directories. +.It Fl P +Suppress line number information in the output. +Currently line number information is not generated at all and this +option has no effect. +.It Fl Umacro +Remove any existing defintion for the named macro. +Note that like many Unix compilers, +.Nm +does not accept a space between the +.Dq U +and the macro name. +.It Fl undef +Remove all predefined macros. +.El +.Ss Warning Options +Warning options can be disabled or enabled by inserting, or not, the +string +.Dq no- +between the +.Dq W +and the warning name. +Herein the +.Dq Fl Wno- +form is shown for options that are enabled by default. +.Bl -tag -width bubblebabble +.It Fl Wall +Turn on all warnings. +The option +.Fl Wno-all +disables only the warnings that are disabled by default. +.It Fl w +Turn off all warnings. +.It Fl Werror +Make warnings into fatal errors. +.It Fl Wcomment +Warn about nested comments. +.It Fl Wno-endif-labels +Don't warn about symbols attached to #endif directives. +(The warning is currently not implemented.) +.It Fl Wundef +Warn about undefined symbols appearing in #if and #elif expressions. +.It Fl Wunused-macros +Warn about macros that are defined and never used. +Not implemented. +.El +.Ss Depend Options +.Bl -tag -width bubblebabble +.It Fl M +Generate dependency information for +.Xr make 1 +on the standard output, instead of preprocessing. +Not implemented. +.It Fl MD +Like +.Fl M +but skip system headers. +Not implemented. +.It Fl MM +Like +.Fl M +but write the dependency information to a file named after the input +file with extension +.Pa \.d +and preprocess normally to standard output. +Not implemented. +.It Fl MMD +Like +.Fl MM +but skip system headers. +Not implemented. +.It Fl MF Ar file +Send dependency output to the named file instead of the default +location. +Not implemented. +.It Fl MG +When generating dependency information, assume that missing files are +generated instead of failing. +Not implemented. +.It Fl MP +Issue dummy rules for all include files. +This prevents +.Xr make 1 +from choking if an include file is removed. +Not implemented. +.It Fl MQ Ar target +Same as +.Fl MT +except that any +.Xr make 1 +metacharacters appearing in the target are escaped. +.It Fl MT Ar target +Set the name of the +.Xr make 1 +target appearing in the generated dependency information. +The default is the name of the input file with its suffix replaced +with the suffix for object files, normally +.Pa .o . +.\" If this option is given more than once, all named targets will +.\" be emitted. +.\" (The current operating mode framework doesn't support that.) +.El +.Ss More Include Path Options +.Bl -tag -width bubblebabble +.It Fl idirafter Ar path +Add the specified path to the +.Dq afterwards +include path. +This path is searched after all directories specified with +.Fl I +and the standard system directories. +Directories on this path are treated as containing system include +files. +.It Fl imacros Ar file +Read in +.Ar file +prior to reading the main input file, and preprocess it, but throw +away the output and retain only the macro definitions. +.It Fl include Ar file +Read in and preprocess +.Ar file +prior to reading the main input file. +.It Fl iprefix Ar prefix +Set the path prefix used with the +.Fl iwithprefix +option. +.It Fl iquote Ar path +Add +.Ar path +to the list of directories searched for include directives written +with quotes. +This list is not searched for include directives written with angle +brackets. +.It Fl iremap Ar string:replacement +Substitute +.Ar replacement +for +.Ar string +in the +.Dv __FILE__ +built-in macro. +Not supported. +.It Fl isysroot Ar path +Use +.Ar path +as the +.Dq system root , +that is, the directory under which the standard system paths are found. +.It Fl isystem Ar path +Add +.Ar path +to the list of system include directories. +This list is searched after the list given with +.Ar I . +Files found on this path are treated as system headers. +.It Fl iwithprefix Ar dir +Splice +.Ar dir +onto the prefix given with +.Fl iprefix +and add this directory as if it were specified with +.Fl idirafter . +.It Fl iwithprefixbefore +Like +-Fl iwithprefix +but adds the result as if it were specified with +.Fl I . +.El +.Ss Diagnostic Options +.Bl -tag -width bubblebabble +.It Fl dD +Dump all macro definitions, except for the predefined macros, after +the normal preprocessing output. +Not implemented. +.It Fl dI +Dump all include directives along with the normal preprocessing +output. +Not implemented. +.It Fl dM +Dump all macro definitions instead of the normal preprocessing +output. +Not implemented. +.It Fl dN +Like +.Fl dD +but emits only macro names and not the expansions. +Not implemented. +.It Fl H +Output a trace of the include tree as it gets processed. +Not implemented. +.El +.Ss Other Options +.Bl -tag -width bubblebabble +.It Fl CC +Retain comments in output. +Same as +.Fl C , +accepted for compatibility with +.Xr gcc 1 . +.It Fl fdollars-in-identifiers , Fl fno-dollars-in-identifiers +Enable +.Pq or disable, respectively +the use of the dollar sign in identifiers. +Not implemented. +.It Fl ftabstop=num +Set the tab width to the specified value, for reporting column +positions in diagnostics. +The default is 8. +Not implemented. +.It Fl std=standard +Ask +.Nm +to conform to the named standard. +The default, and the only supported value, is +.Dq krc . +.It Fl traditional +This option is accepted for compatibility with +.Xr gcc 1 +and ignored. +.It Fl x Ar lang +Adjust the preprocessor for the given language. +The only values accepted for +.Ar lang +are +.Dq assembler-with-cpp +and +.Dq c , +neither of which have any effect on the behavior of +.Nm . +.El +.Sh FILES +The default list of directories searched for include files is: +.Bl -item -offset indent -compact +.It +.Pa /usr/local/include +.It +.Pa /usr/include +.El +.Sh SEE ALSO +.Xr cc 1 , +.Xr cpp 1 , +.Xr make 1 +.Sh STANDARDS +None. +The whole point of a traditional cpp is that it reflects practices +in pre-standardization implementations of C. +Some information is available from the first edition of Kernighan and +Ritchie. +Much of the rest of the behavior is based on lore, pragmatism, +material encountered in the wild, and comparison to other +implementations. +.Sh HISTORY +The original version of +.Nm +was written one evening in late 2010. +This version had some problems and was put aside. +The first working version was released in June 2013. +.\" .Sh AUTHORS +.\" .An David A. Holland +.Sh BUGS +Probably plenty. diff --git a/cde/util/tradcpp/utils.c b/cde/util/tradcpp/utils.c new file mode 100644 index 000000000..d4ab7a119 --- /dev/null +++ b/cde/util/tradcpp/utils.c @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "utils.h" + +#define MALLOCDEBUG + +const char ws[] = + " \t\f\v" +; +const char alnum[] = + "0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "_" +; + +//////////////////////////////////////////////////////////// +// malloc + +#define ROUNDUP(len, size) ((size) * (((len) + (size) - 1) / (size))) + +#ifdef MALLOCDEBUG + +struct mallocheader { + struct mallocheader *self; + size_t len; +}; + +static +size_t +adjustsize(size_t len) +{ + const size_t sz = sizeof(struct mallocheader); + return ROUNDUP(len, sz) + 2*sz; +} + +static +void * +placeheaders(void *block, size_t len) +{ + struct mallocheader *bothdr, *tophdr; + size_t roundedlen; + void *ret; + + roundedlen = ROUNDUP(len, sizeof(struct mallocheader)); + bothdr = block; + bothdr->len = len; + bothdr->self = block; + ret = bothdr + 1; + tophdr = (void *)(((unsigned char *)ret) + roundedlen); + tophdr->len = len; + tophdr->self = bothdr; + return ret; +} + +static +void * +checkheaders(void *block, size_t len) +{ + struct mallocheader *bothdr, *tophdr; + size_t roundedlen; + + if (block == NULL) { + assert(len == 0); + return block; + } + + roundedlen = ROUNDUP(len, sizeof(struct mallocheader)); + bothdr = block; + bothdr--; + assert(bothdr->self == bothdr); + assert(bothdr->len == len); + tophdr = (void *)(((unsigned char *)(bothdr + 1)) + roundedlen); + assert(tophdr->self == bothdr); + assert(tophdr->len == len); + return bothdr; +} + +#else + +#define adjustsize(len) (len) +#define placeheaders(block, len) ((void)(len), (block)) +#define checkheaders(ptr, len) ((void)(len), (ptr)) + +#endif /* MALLOCDEBUG */ + +void * +domalloc(size_t len) +{ + void *ret; + size_t blocklen; + + blocklen = adjustsize(len); + ret = malloc(blocklen); + if (ret == NULL) { + complain(NULL, "Out of memory"); + die(); + } + + return placeheaders(ret, len); +} + +void * +dorealloc(void *ptr, size_t oldlen, size_t newlen) +{ + void *ret; + void *blockptr; + size_t newblocklen; + + blockptr = checkheaders(ptr, oldlen); + newblocklen = adjustsize(newlen); + + ret = realloc(blockptr, newblocklen); + if (ret == NULL) { + complain(NULL, "Out of memory"); + die(); + } + + return placeheaders(ret, newlen); +} + +void +dofree(void *ptr, size_t len) +{ + void *blockptr; + + blockptr = checkheaders(ptr, len); + free(blockptr); +} + +//////////////////////////////////////////////////////////// +// string allocators + +char * +dostrdup(const char *s) +{ + char *ret; + size_t len; + + len = strlen(s); + ret = domalloc(len+1); + strcpy(ret, s); + return ret; +} + +char * +dostrdup2(const char *s, const char *t) +{ + char *ret; + size_t len; + + len = strlen(s) + strlen(t); + ret = domalloc(len+1); + strcpy(ret, s); + strcat(ret, t); + return ret; +} + +char * +dostrdup3(const char *s, const char *t, const char *u) +{ + char *ret; + size_t len; + + len = strlen(s) + strlen(t) + strlen(u); + ret = domalloc(len+1); + strcpy(ret, s); + strcat(ret, t); + strcat(ret, u); + return ret; +} + +char * +dostrndup(const char *s, size_t len) +{ + char *ret; + + ret = domalloc(len+1); + memcpy(ret, s, len); + ret[len] = '\0'; + return ret; +} + +void +dostrfree(char *s) +{ + dofree(s, strlen(s)+1); +} + +//////////////////////////////////////////////////////////// +// other stuff + +size_t +notrailingws(char *buf, size_t len) +{ + while (len > 0 && strchr(ws, buf[len-1])) { + buf[--len] = '\0'; + } + return len; +} + +bool +is_identifier(const char *str) +{ + size_t len; + + len = strlen(str); + if (len != strspn(str, alnum)) { + return false; + } + if (str[0] >= '0' && str[0] <= '9') { + return false; + } + return true; +} diff --git a/cde/util/tradcpp/utils.h b/cde/util/tradcpp/utils.h new file mode 100644 index 000000000..e6f1ae728 --- /dev/null +++ b/cde/util/tradcpp/utils.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +struct place; + +#if defined(__CLANG__) || defined(__GNUC__) +#define PF(a, b) __attribute__((__format__(__printf__, a, b))) +#define DEAD __attribute__((__noreturn__)) +#define UNUSED __attribute__((__unused__)) +#else +#define PF(a, b) +#define DEAD +#define UNUSED +#endif + +#define HOWMANY(arr) (sizeof(arr)/sizeof((arr)[0])) + +extern const char ws[]; +extern const char alnum[]; + + +void *domalloc(size_t len); +void *dorealloc(void *ptr, size_t oldlen, size_t newlen); +void dofree(void *ptr, size_t len); + +char *dostrdup(const char *s); +char *dostrdup2(const char *s, const char *t); +char *dostrdup3(const char *s, const char *t, const char *u); +char *dostrndup(const char *s, size_t len); +void dostrfree(char *s); + +size_t notrailingws(char *buf, size_t len); +bool is_identifier(const char *str); + +/* in place.c */ +void complain_init(const char *progname); +void complain(const struct place *, const char *fmt, ...) PF(2, 3); +void complain_fail(void); +bool complain_failed(void); + +/* in main.c */ +void freestringlater(char *s); +DEAD void die(void); diff --git a/cde/util/tradcpp/version.h b/cde/util/tradcpp/version.h new file mode 100644 index 000000000..b278c662d --- /dev/null +++ b/cde/util/tradcpp/version.h @@ -0,0 +1,33 @@ +/*- + * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define VERSION_MAJOR "0" +#define VERSION_MINOR "4" +#define VERSION_STRING "0.4" +#define VERSION_LONG "NetBSD tradcpp 0.4"