summaryrefslogtreecommitdiff
path: root/usr.bin/pcc/cpp
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/pcc/cpp')
-rw-r--r--usr.bin/pcc/cpp/Makefile17
-rw-r--r--usr.bin/pcc/cpp/cpp.1194
-rw-r--r--usr.bin/pcc/cpp/cpp.c1482
-rw-r--r--usr.bin/pcc/cpp/cpp.h120
-rw-r--r--usr.bin/pcc/cpp/cpy.y166
-rw-r--r--usr.bin/pcc/cpp/scanner.l817
6 files changed, 2796 insertions, 0 deletions
diff --git a/usr.bin/pcc/cpp/Makefile b/usr.bin/pcc/cpp/Makefile
new file mode 100644
index 00000000000..fac8e0fa27b
--- /dev/null
+++ b/usr.bin/pcc/cpp/Makefile
@@ -0,0 +1,17 @@
+# $OpenBSD: Makefile,v 1.1 2007/10/07 17:58:51 otto Exp $
+#
+# Makefile for the cpp part of pcc.
+#
+PROG= cpp
+PREFIX= /usr/local
+BINDIR= ${PREFIX}/libexec
+MANDIR= ${PREFIX}/man/man
+TARGOS= openbsd
+
+CFLAGS+= -DCPP_DEBUG -Wall -Wmissing-prototypes -Wstrict-prototypes -Werror
+CFLAGS+= -DLIBEXECDIR=\"${PREFIX}/libexec\"
+CPPFLAGS+= -I. -I${.CURDIR}
+
+SRCS=cpy.y scanner.l cpp.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/pcc/cpp/cpp.1 b/usr.bin/pcc/cpp/cpp.1
new file mode 100644
index 00000000000..31ddb32db51
--- /dev/null
+++ b/usr.bin/pcc/cpp/cpp.1
@@ -0,0 +1,194 @@
+.\" $Id: cpp.1,v 1.1 2007/10/07 17:58:51 otto Exp $
+.\" $NetBSD$
+.\" $OpenBSD: cpp.1,v 1.1 2007/10/07 17:58:51 otto Exp $
+."\
+.\" Copyright (c) 2007 Jeremy C. Reed <reed@reedmedia.net>
+.\"
+.\" Permission to use, copy, modify, and/or distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR AND CONTRIBUTORS DISCLAIM
+.\" ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHOR AND
+.\" CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+.\" DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+.\" PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+.\" ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+.\" THIS SOFTWARE.
+.\"
+.Dd September 17, 2007
+.Dt cpp 1
+.Os
+.Sh NAME
+.Nm cpp
+.Nd C preprocessor
+.Sh SYNOPSIS
+.Nm
+.\" TODO also document -Dvar and below without spaces?
+.Op Fl CdMt
+.Op Fl D Ar macro[=value]
+.Op Fl I Ar path
+.Op Fl i Ar file
+.Op Fl S Ar path
+.Op Fl U Ar macro
+.Op Ar infile | -
+.Op Ar outfile
+.Pp
+.Sh DESCRIPTION
+The
+.Nm
+utility is a macro preprocessor used by the
+.Xr pcc 1
+compiler.
+It is used to include header files,
+expand macro definitions,
+and perform conditional compilation.
+.Pp
+The
+.Ar infile
+input file is optional.
+If not provided or the file name is
+.Qq -
+(dash),
+.Nm
+reads its initial file from standard input.
+The
+.Ar outfile
+output file is also optional.
+It writes by default to standard output.
+.Pp
+.\" TODO: document MAXARG 250 args to a macro, limited by char value
+.\" TODO: Include order:
+.\" For "..." files, first search "current" dir, then as <...> files.
+.\" For <...> files, first search -I directories, then system directories.
+.\"
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl C
+Do not discard comments.
+.It Fl D Ar macro[=value]
+Fake a definition at the beginning by using
+.Do #define
+.Ar macro=value Dc .
+If
+.Ar value
+is not set on command-line, then defines as 1.
+.\" TODO: show example
+.It Fl dM
+Print list of
+.Dq #define
+statements to standard output for all defined macros other than
+builtin macros (see below).
+The normal results of preprocessing are not outputted.
+.\" TODO this doesn't show predefined macros
+.\" other -d options are ignored
+.It Fl I Ar path
+Add
+.Ar path
+to the list of directories containing needed header files.
+This may be used to override system include directories
+(see
+.Fl S
+option).
+.Fl I
+may be specified multiple times.
+.It Fl i Ar file
+Include a file at the beginning by using
+.Do #include
+.Ar file Dc .
+.\" Note: I did not use the .In macro above
+.It Fl M
+Generate dependencies for
+.Xr make 1 .
+.\" TODO: explain and show example?
+.It Fl S Ar path
+Add
+.Ar path
+to the list of system directories containing needed header files.
+.Fl S
+may be specified multiple times.
+Note:
+.Nm
+does not have a default include directory defined.
+.\" TODO: explain difference between -I and -S
+.\" The directories listed by -I are searched first?
+.It Fl t
+Traditional cpp syntax.
+Do not define the
+.Dv __TIME__ ,
+.Dv __DATE__ ,
+and
+.Dv __STDC__
+macros.
+.\"
+.It Fl U Ar macro
+Undefine a macro at the beginning by using
+.Do #undef
+.Ar macro Dc .
+.It Fl v
+Display version.
+.It Fl V
+Verbose debugging output.
+.Fl V
+can be repeated for further details.
+.\" -V only available if cpp source built with CPP_DEBUG, which is the default.
+.It Fl ?
+Show command line usage for
+.Nm .
+.El
+.Sh Builtin Macros
+A few macros are interpreted inside the
+.Nm cpp
+program:
+.Bl -diag
+.It __DATE__
+Expands to the date in abbreviated month, day, and year format from
+.Xr ctime 3
+in quotes.
+.\" TODO: is that ctime(3) format output change according to locale?
+.It __FILE__
+Expands to the name of the current input file in quotes.
+When read from standard input, it expands to
+.Qq Ao stdin Ac .
+.It __LINE__
+Expands to the line number of the current line containing the macro.
+.It __STDC__
+Expands to the constant 1.
+This means the compiler conforms to ISO Standard C.
+.It __TIME__
+Expands to the time in hour, minutes, and seconds from
+.Xr ctime 3
+in quotes.
+.El
+.Pp
+Also see the
+.Fl t
+option.
+.Sh EXIT STATUS
+The
+.Nm
+utility exits with one of the following values:
+.Bl -tag -width Ds
+.It 0
+Successfully finished.
+.It 1
+An error occurred.
+.El
+.Sh SEE ALSO
+.Xr as 1 ,
+.Xr ccom 1 ,
+.Xr pcc 1
+.\"
+.Sh HISTORY
+The
+.Nm
+command comes from the original Portable C Compiler by S. C.
+Johnson, written in the late 70's.
+The code originates from the V6 preprocessor with some additions
+from V7 cpp and ansi/c99 support.
+.Pp
+A lot of the PCC code was rewritten by Anders Magnusson.
+.Pp
+This product includes software developed or owned by Caldera
+International, Inc.
diff --git a/usr.bin/pcc/cpp/cpp.c b/usr.bin/pcc/cpp/cpp.c
new file mode 100644
index 00000000000..92fdc6cdd23
--- /dev/null
+++ b/usr.bin/pcc/cpp/cpp.c
@@ -0,0 +1,1482 @@
+/* $OpenBSD: cpp.c,v 1.1 2007/10/07 17:58:51 otto Exp $ */
+
+/*
+ * Copyright (c) 2004 Anders Magnusson (ragge@ludd.luth.se).
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ */
+
+/*
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code and documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 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.
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed or owned by Caldera
+ * International, Inc.
+ * Neither the name of Caldera International, Inc. nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, 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 CALDERA INTERNATIONAL, INC. 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 OFLIABILITY, 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.
+ */
+/*
+ * The C preprocessor.
+ * This code originates from the V6 preprocessor with some additions
+ * from V7 cpp, and at last ansi/c99 support.
+ */
+
+#include "../config.h"
+
+#include <sys/wait.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include "cpp.h"
+#include "y.tab.h"
+
+#define MAXARG 250 /* # of args to a macro, limited by char value */
+#define SBSIZE 600000
+
+static usch sbf[SBSIZE];
+/* C command */
+
+int tflag; /* traditional cpp syntax */
+#ifdef CPP_DEBUG
+int dflag; /* debug printouts */
+#define DPRINT(x) if (dflag) printf x
+#define DDPRINT(x) if (dflag > 1) printf x
+#else
+#define DPRINT(x)
+#define DDPRINT(x)
+#endif
+
+int ofd;
+usch outbuf[CPPBUF];
+int obufp, istty;
+int Cflag, Mflag, dMflag;
+usch *Mfile;
+struct initar *initar;
+
+/* avoid recursion */
+struct recur {
+ struct recur *next;
+ struct symtab *sp;
+};
+
+/* include dirs */
+struct incs {
+ struct incs *next;
+ usch *dir;
+} *incdir[2];
+#define INCINC 0
+#define SYSINC 1
+
+static struct symtab *filloc;
+static struct symtab *linloc;
+static struct symtab *pragloc;
+int trulvl;
+int flslvl;
+int elflvl;
+int elslvl;
+usch *stringbuf = sbf;
+
+/*
+ * Macro replacement list syntax:
+ * - For object-type macros, replacement strings are stored as-is.
+ * - For function-type macros, macro args are substituted for the
+ * character WARN followed by the argument number.
+ * - The value element points to the end of the string, to simplify
+ * pushback onto the input queue.
+ *
+ * The first character (from the end) in the replacement list is
+ * the number of arguments:
+ * VARG - ends with ellipsis, next char is argcount without ellips.
+ * OBJCT - object-type macro
+ * 0 - empty parenthesis, foo()
+ * 1-> - number of args.
+ */
+
+#define VARG 0xfe /* has varargs */
+#define OBJCT 0xff
+#define WARN 1 /* SOH, not legal char */
+#define CONC 2 /* STX, not legal char */
+#define SNUFF 3 /* ETX, not legal char */
+#define NOEXP 4 /* EOT, not legal char */
+#define EXPAND 5 /* ENQ, not legal char */
+
+/* args for lookup() */
+#define FIND 0
+#define ENTER 1
+
+static void expdef(usch *proto, struct recur *, int gotwarn);
+void define(void);
+static int canexpand(struct recur *, struct symtab *np);
+void include(void);
+void line(void);
+void flbuf(void);
+void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ struct initar *it;
+ struct incs *w, *w2;
+ struct symtab *nl;
+ register int ch;
+
+ while ((ch = getopt(argc, argv, "CD:I:MS:U:d:i:tvV")) != -1)
+ switch (ch) {
+ case 'C': /* Do not discard comments */
+ Cflag++;
+ break;
+
+ case 'i': /* include */
+ case 'U': /* undef */
+ case 'D': /* define something */
+ /* XXX should not need malloc() here */
+ if ((it = malloc(sizeof(struct initar))) == NULL)
+ error("couldn't apply -%c %s", ch, optarg);
+ it->type = ch;
+ it->str = optarg;
+ it->next = initar;
+ initar = it;
+ break;
+
+ case 'M': /* Generate dependencies for make */
+ Mflag++;
+ break;
+
+ case 'S':
+ case 'I':
+ if ((w = calloc(sizeof(struct incs), 1)) == NULL)
+ error("couldn't apply -%c %s", ch, optarg);
+ w->dir = (usch *)optarg;
+ w2 = incdir[ch == 'I' ? INCINC : SYSINC];
+ if (w2 != NULL) {
+ while (w2->next)
+ w2 = w2->next;
+ w2->next = w;
+ } else
+ incdir[ch == 'I' ? INCINC : SYSINC] = w;
+ break;
+
+#ifdef CPP_DEBUG
+ case 'V':
+ dflag++;
+ break;
+#endif
+ case 'v':
+ printf("cpp: %s\n", VERSSTR);
+ break;
+ case 'd':
+ if (optarg[0] == 'M') {
+ dMflag = 1;
+ Mflag = 1;
+ }
+ /* ignore others */
+ break;
+
+ case 't':
+ tflag = 1;
+ break;
+
+ case '?':
+ usage();
+ default:
+ error("bad arg %c\n", ch);
+ }
+ argc -= optind;
+ argv += optind;
+
+ filloc = lookup((usch *)"__FILE__", ENTER);
+ linloc = lookup((usch *)"__LINE__", ENTER);
+ pragloc = lookup((usch *)"_Pragma", ENTER);
+ filloc->value = linloc->value = (usch *)""; /* Just something */
+ pragloc->value = (usch *)"";
+
+ if (tflag == 0) {
+ time_t t = time(NULL);
+ usch *n = (usch *)ctime(&t);
+
+ /*
+ * Manually move in the predefined macros.
+ */
+ nl = lookup((usch *)"__TIME__", ENTER);
+ savch(0); savch('"'); n[19] = 0; savstr(&n[11]); savch('"');
+ savch(OBJCT);
+ nl->value = stringbuf-1;
+
+ nl = lookup((usch *)"__DATE__", ENTER);
+ savch(0); savch('"'); n[24] = n[11] = 0; savstr(&n[4]);
+ savstr(&n[20]); savch('"'); savch(OBJCT);
+ nl->value = stringbuf-1;
+
+ nl = lookup((usch *)"__STDC__", ENTER);
+ savch(0); savch('1'); savch(OBJCT);
+ nl->value = stringbuf-1;
+ }
+
+ if (Mflag && !dMflag) {
+ usch *c;
+
+ if (argc < 1)
+ error("-M and no infile");
+ if ((c = (usch *)strrchr(argv[0], '/')) == NULL)
+ c = (usch *)argv[0];
+ else
+ c++;
+ Mfile = stringbuf;
+ savstr(c); savch(0);
+ if ((c = (usch *)strrchr((char *)Mfile, '.')) == NULL)
+ error("-M and no extension: ");
+ c[1] = 'o';
+ c[2] = 0;
+ }
+
+ if (argc == 2) {
+ if ((ofd = open(argv[1], O_WRONLY|O_CREAT, 0600)) < 0)
+ error("Can't creat %s", argv[1]);
+ } else
+ ofd = 1; /* stdout */
+ istty = isatty(ofd);
+
+ if (pushfile((usch *)(argc && strcmp(argv[0], "-") ? argv[0] : NULL)))
+ error("cannot open %s", argv[0]);
+
+ flbuf();
+ close(ofd);
+ return 0;
+}
+
+/*
+ * Expand the symbol nl read from input.
+ * Return a pointer to the fully expanded result.
+ * It is the responsibility of the caller to reset the heap usage.
+ */
+usch *
+gotident(struct symtab *nl)
+{
+ struct symtab *thisnl;
+ usch *osp, *ss2, *base;
+ int c;
+
+ thisnl = NULL;
+ slow = 1;
+ base = osp = stringbuf;
+ goto found;
+
+ while ((c = yylex()) != 0) {
+ switch (c) {
+ case IDENT:
+ if (flslvl)
+ break;
+ osp = stringbuf;
+
+ DPRINT(("IDENT0: %s\n", yytext));
+ nl = lookup((usch *)yytext, FIND);
+ if (nl == 0 || thisnl == 0)
+ goto found;
+ if (thisnl == nl) {
+ nl = 0;
+ goto found;
+ }
+ ss2 = stringbuf;
+ if ((c = yylex()) == WSPACE) {
+ savstr((usch *)yytext);
+ c = yylex();
+ }
+ if (c != EXPAND) {
+ unpstr((usch *)yytext);
+ if (ss2 != stringbuf)
+ unpstr(ss2);
+ unpstr(nl->namep);
+ (void)yylex(); /* get yytext correct */
+ nl = 0; /* ignore */
+ } else {
+ thisnl = NULL;
+ if (nl->value[0] == OBJCT) {
+ unpstr(nl->namep);
+ (void)yylex(); /* get yytext correct */
+ nl = 0;
+ }
+ }
+ stringbuf = ss2;
+
+found: if (nl == 0 || subst(nl, NULL) == 0) {
+ if (nl)
+ savstr(nl->namep);
+ else
+ savstr((usch *)yytext);
+ } else if (osp != stringbuf) {
+ DPRINT(("IDENT1: unput osp %p stringbuf %p\n",
+ osp, stringbuf));
+ ss2 = stringbuf;
+ cunput(EXPAND);
+ while (ss2 > osp)
+ cunput(*--ss2);
+ thisnl = nl;
+ stringbuf = osp; /* clean up heap */
+ }
+ break;
+
+ case EXPAND:
+ DPRINT(("EXPAND!\n"));
+ thisnl = NULL;
+ break;
+
+ case STRING:
+ case '\n':
+ case NUMBER:
+ case FPOINT:
+ case WSPACE:
+ savstr((usch *)yytext);
+ break;
+
+ default:
+ if (c < 256)
+ savch(c);
+ else
+ savstr((usch *)yytext);
+ break;
+ }
+ if (thisnl == NULL) {
+ slow = 0;
+ savch(0);
+ return base;
+ }
+ }
+ error("preamture EOF");
+ /* NOTREACHED */
+ return NULL; /* XXX gcc */
+}
+
+void
+line()
+{
+ static usch *lbuf;
+ static int llen;
+ int c;
+
+ slow = 1;
+ if (yylex() != WSPACE)
+ goto bad;
+ if ((c = yylex()) != IDENT || !isdigit((int)yytext[0]))
+ goto bad;
+ ifiles->lineno = atoi(yytext);
+
+ if ((c = yylex()) != '\n' && c != WSPACE)
+ goto bad;
+ if (c == '\n') {
+ slow = 0;
+ return;
+ }
+ if (yylex() != STRING)
+ goto bad;
+ c = strlen((char *)yytext);
+ if (llen < c) {
+ /* XXX may loose heap space */
+ lbuf = stringbuf;
+ stringbuf += c;
+ llen = c;
+ }
+ yytext[strlen(yytext)-1] = 0;
+ if (strlcpy((char *)lbuf, &yytext[1], SBSIZE) >= SBSIZE)
+ error("line exceeded buffer size");
+
+ ifiles->fname = lbuf;
+ if (yylex() != '\n')
+ goto bad;
+ slow = 0;
+ return;
+
+bad: error("bad line directive");
+}
+
+/*
+ * Include a file. Include order:
+ * - For <...> files, first search -I directories, then system directories.
+ * - For "..." files, first search "current" dir, then as <...> files.
+ */
+void
+include()
+{
+ struct incs *w;
+ struct symtab *nl;
+ usch *osp;
+ usch *fn, *safefn;
+ int i, c, it;
+
+ if (flslvl)
+ return;
+ osp = stringbuf;
+ slow = 1;
+again:
+ if ((c = yylex()) == WSPACE)
+ c = yylex();
+ if (c != STRING && c != '<' && c != IDENT)
+ goto bad;
+
+ if (c == IDENT) {
+ if ((nl = lookup((usch *)yytext, FIND)) == NULL)
+ goto bad;
+ if (subst(nl, NULL) == 0)
+ goto bad;
+ savch('\0');
+ unpstr(osp);
+ goto again;
+ } else if (c == '<') {
+ fn = stringbuf;
+ while ((c = yylex()) != '>' && c != '\n') {
+ if (c == '\n')
+ goto bad;
+ savstr((usch *)yytext);
+ }
+ savch('\0');
+ while ((c = yylex()) == WSPACE)
+ ;
+ if (c != '\n')
+ goto bad;
+ it = SYSINC;
+ safefn = fn;
+ } else {
+ usch *nm = stringbuf;
+
+ yytext[strlen(yytext)-1] = 0;
+ fn = (usch *)&yytext[1];
+ /* first try to open file relative to previous file */
+ /* but only if it is not an absolute path */
+ if (*fn != '/') {
+ savstr(ifiles->orgfn);
+ if ((stringbuf =
+ (usch *)strrchr((char *)nm, '/')) == NULL)
+ stringbuf = nm;
+ else
+ stringbuf++;
+ }
+ safefn = stringbuf;
+ savstr(fn); savch(0);
+ while ((c = yylex()) == WSPACE)
+ ;
+ if (c != '\n')
+ goto bad;
+ slow = 0;
+ if (pushfile(nm) == 0)
+ return;
+ /* XXX may loose stringbuf space */
+ }
+
+ /* create search path and try to open file */
+ slow = 0;
+ for (i = 0; i < 2; i++) {
+ for (w = incdir[i]; w; w = w->next) {
+ usch *nm = stringbuf;
+
+ savstr(w->dir); savch('/');
+ savstr(safefn); savch(0);
+ if (pushfile(nm) == 0)
+ return;
+ stringbuf = nm;
+ }
+ }
+ error("cannot find '%s'", safefn);
+ /* error() do not return */
+
+bad: error("bad include");
+ /* error() do not return */
+}
+
+static int
+definp(void)
+{
+ int c;
+
+ do
+ c = yylex();
+ while (c == WSPACE);
+ return c;
+}
+
+void
+define()
+{
+ struct symtab *np;
+ usch *args[MAXARG], *ubuf, *sbeg;
+ int c, i, redef;
+ int mkstr = 0, narg = -1;
+ int ellips = 0;
+ size_t len;
+
+ if (flslvl)
+ return;
+ slow = 1;
+ if (yylex() != WSPACE || yylex() != IDENT)
+ goto bad;
+
+ if (isdigit((int)yytext[0]))
+ goto bad;
+
+ np = lookup((usch *)yytext, ENTER);
+ redef = np->value != NULL;
+
+ sbeg = stringbuf;
+ if ((c = yylex()) == '(') {
+ narg = 0;
+ /* function-like macros, deal with identifiers */
+ for (;;) {
+ c = definp();
+ if (c == ')')
+ break;
+ if (c == ELLIPS) {
+ ellips = 1;
+ if (definp() != ')')
+ goto bad;
+ break;
+ }
+ if (c == IDENT) {
+ len = strlen(yytext);
+ args[narg] = alloca(len+1);
+ strlcpy((char *)args[narg], yytext, len+1);
+ narg++;
+ if ((c = definp()) == ',')
+ continue;
+ if (c == ')')
+ break;
+ goto bad;
+ }
+ goto bad;
+ }
+ c = yylex();
+ } else if (c == '\n') {
+ /* #define foo */
+ ;
+ } else if (c != WSPACE)
+ goto bad;
+
+ while (c == WSPACE)
+ c = yylex();
+
+ /* parse replacement-list, substituting arguments */
+ savch('\0');
+ while (c != '\n') {
+ switch (c) {
+ case WSPACE:
+ /* remove spaces if it surrounds a ## directive */
+ ubuf = stringbuf;
+ savstr((usch *)yytext);
+ c = yylex();
+ if (c == CONCAT) {
+ stringbuf = ubuf;
+ savch(CONC);
+ if ((c = yylex()) == WSPACE)
+ c = yylex();
+ }
+ continue;
+
+ case CONCAT:
+ /* No spaces before concat op */
+ savch(CONC);
+ if ((c = yylex()) == WSPACE)
+ c = yylex();
+ continue;
+
+ case MKSTR:
+ if (narg < 0) {
+ /* no meaning in object-type macro */
+ savch('#');
+ break;
+ }
+ /* remove spaces between # and arg */
+ savch(SNUFF);
+ if ((c = yylex()) == WSPACE)
+ c = yylex(); /* whitespace, ignore */
+ mkstr = 1;
+ if (c == VA_ARGS)
+ continue;
+
+ /* FALLTHROUGH */
+ case IDENT:
+ if (narg < 0)
+ goto id; /* just add it if object */
+ /* check if its an argument */
+ for (i = 0; i < narg; i++)
+ if (strcmp(yytext, (char *)args[i]) == 0)
+ break;
+ if (i == narg) {
+ if (mkstr)
+ error("not argument");
+ goto id;
+ }
+ savch(i);
+ savch(WARN);
+ if (mkstr)
+ savch(SNUFF), mkstr = 0;
+ break;
+
+ case VA_ARGS:
+ if (ellips == 0)
+ error("unwanted %s", yytext);
+ savch(VARG);
+ savch(WARN);
+ if (mkstr)
+ savch(SNUFF), mkstr = 0;
+ break;
+
+ default:
+id: savstr((usch *)yytext);
+ break;
+ }
+ c = yylex();
+ }
+ /* remove trailing whitespace */
+ while (stringbuf > sbeg) {
+ if (stringbuf[-1] == ' ' || stringbuf[-1] == '\t')
+ stringbuf--;
+ else
+ break;
+ }
+ if (ellips) {
+ savch(narg);
+ savch(VARG);
+ } else
+ savch(narg < 0 ? OBJCT : narg);
+ if (redef) {
+ usch *o = np->value, *n = stringbuf-1;
+
+ /* Redefinition to identical replacement-list is allowed */
+ while (*o && *o == *n)
+ o--, n--;
+ if (*o || *o != *n)
+ error("%s redefined\nprevious define: %s:%d",
+ np->namep, np->file, np->line);
+ stringbuf = sbeg; /* forget this space */
+ } else
+ np->value = stringbuf-1;
+
+#ifdef CPP_DEBUG
+ if (dflag) {
+ usch *w = np->value;
+
+ printf("!define: ");
+ if (*w == OBJCT)
+ printf("[object]");
+ else if (*w == VARG)
+ printf("[VARG%d]", *--w);
+ while (*--w) {
+ switch (*w) {
+ case WARN: printf("<%d>", *--w); break;
+ case CONC: printf("<##>"); break;
+ case SNUFF: printf("<\">"); break;
+ default: putchar(*w); break;
+ }
+ }
+ putchar('\n');
+ }
+#endif
+ slow = 0;
+ return;
+
+bad: error("bad define");
+}
+
+void
+xerror(usch *s)
+{
+ usch *t;
+
+ flbuf();
+ savch(0);
+ if (ifiles != NULL) {
+ t = sheap("%s:%d: ", ifiles->fname, ifiles->lineno);
+ write (2, t, strlen((char *)t));
+ }
+ write (2, s, strlen((char *)s));
+ write (2, "\n", 1);
+ exit(1);
+}
+
+/*
+ * store a character into the "define" buffer.
+ */
+void
+savch(c)
+{
+ if (stringbuf-sbf < SBSIZE) {
+ *stringbuf++ = c;
+ } else {
+ stringbuf = sbf; /* need space to write error message */
+ error("Too much defining");
+ }
+}
+
+/*
+ * convert _Pragma to #pragma for output.
+ */
+static void
+pragoper(void)
+{
+ usch *opb;
+ int t;
+
+ slow = 1;
+ putstr((usch *)"\n#pragma ");
+ if ((t = yylex()) == WSPACE)
+ t = yylex();
+ if (t != '(')
+ goto bad;
+ if ((t = yylex()) == WSPACE)
+ t = yylex();
+ opb = stringbuf;
+ while (t != ')') {
+ savstr((usch *)yytext);
+ t = yylex();
+ }
+ savch(0);
+ cunput(WARN);
+ unpstr(opb);
+ stringbuf = opb;
+ expmac(NULL);
+ while (stringbuf > opb)
+ cunput(*--stringbuf);
+ if ((t = yylex()) != STRING)
+ goto bad;
+ opb = (usch *)yytext;
+ if (*opb++ == 'L')
+ opb++;
+ while ((t = *opb++) != '\"') {
+ if (t == '\\' && (*opb == '\"' || *opb == '\\'))
+ t = *opb++;
+ putch(t);
+ }
+ putch('\n');
+ prtline();
+ return;
+bad: error("bad pragma operator");
+}
+
+/*
+ * substitute namep for sp->value.
+ */
+int
+subst(sp, rp)
+struct symtab *sp;
+struct recur *rp;
+{
+ struct recur rp2;
+ register usch *vp, *cp;
+ int c, rv = 0, ws;
+
+ DPRINT(("subst: %s\n", sp->namep));
+ /*
+ * First check for special macros.
+ */
+ if (sp == filloc) {
+ (void)sheap("\"%s\"", ifiles->fname);
+ return 1;
+ } else if (sp == linloc) {
+ (void)sheap("%d", ifiles->lineno);
+ return 1;
+ } else if (sp == pragloc) {
+ pragoper();
+ return 1;
+ }
+ vp = sp->value;
+
+ rp2.next = rp;
+ rp2.sp = sp;
+
+ if (*vp-- != OBJCT) {
+ int gotwarn = 0;
+
+ /* should we be here at all? */
+ /* check if identifier is followed by parentheses */
+ rv = 1;
+ ws = 0;
+ do {
+ c = yylex();
+ if (c == WARN) {
+ gotwarn++;
+ if (rp == NULL)
+ goto noid;
+ } else if (c == WSPACE || c == '\n')
+ ws = 1;
+ } while (c == WSPACE || c == '\n' || c == WARN);
+
+ cp = (usch *)yytext;
+ while (*cp)
+ cp++;
+ while (cp > (usch *)yytext)
+ cunput(*--cp);
+ DPRINT(("c %d\n", c));
+ if (c == '(' ) {
+ expdef(vp, &rp2, gotwarn);
+ return rv;
+ } else {
+ /* restore identifier */
+noid: while (gotwarn--)
+ cunput(WARN);
+ if (ws)
+ cunput(' ');
+ cp = sp->namep;
+ while (*cp)
+ cp++;
+ while (cp > sp->namep)
+ cunput(*--cp);
+ if ((c = yylex()) != IDENT)
+ error("internal sync error");
+ return 0;
+ }
+ } else {
+ cunput(WARN);
+ cp = vp;
+ while (*cp) {
+ if (*cp != CONC)
+ cunput(*cp);
+ cp--;
+ }
+ expmac(&rp2);
+ }
+ return 1;
+}
+
+/*
+ * do macro-expansion until WARN character read.
+ * read from lex buffer and store result on heap.
+ * will recurse into lookup() for recursive expansion.
+ * when returning all expansions on the token list is done.
+ */
+void
+expmac(struct recur *rp)
+{
+ struct symtab *nl;
+ int c, noexp = 0, orgexp;
+ usch *och, *stksv;
+ extern int yyleng;
+
+#ifdef CPP_DEBUG
+ if (dflag) {
+ struct recur *rp2 = rp;
+ printf("\nexpmac\n");
+ while (rp2) {
+ printf("do not expand %s\n", rp->sp->namep);
+ rp2 = rp2->next;
+ }
+ }
+#endif
+ while ((c = yylex()) != WARN) {
+ switch (c) {
+ case NOEXP: noexp++; break;
+ case EXPAND: noexp--; break;
+
+ case IDENT:
+ /*
+ * Handle argument concatenation here.
+ * If an identifier is found and directly
+ * after EXPAND or NOEXP then push the
+ * identifier back on the input stream and
+ * call yylex() again.
+ * Be careful to keep the noexp balance.
+ */
+ och = stringbuf;
+ savstr((usch *)yytext);
+ DDPRINT(("id: str %s\n", och));
+
+ orgexp = 0;
+ while ((c = yylex()) == EXPAND || c == NOEXP)
+ if (c == EXPAND)
+ orgexp--;
+ else
+ orgexp++;
+
+ DDPRINT(("id1: noexp %d orgexp %d\n", noexp, orgexp));
+ if (c == IDENT) { /* XXX numbers? */
+ DDPRINT(("id2: str %s\n", yytext));
+ /* OK to always expand here? */
+ savstr((usch *)yytext);
+ switch (orgexp) {
+ case 0: /* been EXP+NOEXP */
+ if (noexp == 0)
+ break;
+ if (noexp != 1)
+ error("case 0");
+ cunput(NOEXP);
+ noexp = 0;
+ break;
+ case -1: /* been EXP */
+ if (noexp != 1)
+ error("case -1");
+ noexp = 0;
+ break;
+ case 1:
+ if (noexp != 0)
+ error("case 1");
+ cunput(NOEXP);
+ break;
+ default:
+ error("orgexp = %d", orgexp);
+ }
+ unpstr(och);
+ stringbuf = och;
+ continue; /* New longer identifier */
+ }
+ unpstr((usch *)yytext);
+ if (orgexp == -1)
+ cunput(EXPAND);
+ else if (orgexp == 1)
+ cunput(NOEXP);
+ unpstr(och);
+ stringbuf = och;
+
+
+ yylex(); /* XXX reget last identifier */
+
+ if ((nl = lookup((usch *)yytext, FIND)) == NULL)
+ goto def;
+
+ if (canexpand(rp, nl) == 0)
+ goto def;
+ /*
+ * If noexp == 0 then expansion of any macro is
+ * allowed. If noexp == 1 then expansion of a
+ * fun-like macro is allowed iff there is an
+ * EXPAND between the identifier and the '('.
+ */
+ if (noexp == 0) {
+ if ((c = subst(nl, rp)) == 0)
+ goto def;
+ break;
+ }
+//printf("noexp1 %d nl->namep %s\n", noexp, nl->namep);
+//if (noexp > 1) goto def;
+ if (noexp != 1)
+ error("bad noexp %d", noexp);
+ stksv = NULL;
+ if ((c = yylex()) == WSPACE) {
+ stksv = alloca(yyleng+1);
+ strlcpy((char *)stksv, yytext, yyleng+1);
+ c = yylex();
+ }
+ /* only valid for expansion if fun macro */
+ if (c == EXPAND && *nl->value != OBJCT) {
+ noexp--;
+ if (subst(nl, rp))
+ break;
+ savstr(nl->namep);
+ if (stksv)
+ savstr(stksv);
+ } else {
+ unpstr((usch *)yytext);
+ if (stksv)
+ unpstr(stksv);
+ savstr(nl->namep);
+ }
+ break;
+
+ case STRING:
+ /* remove EXPAND/NOEXP from strings */
+ if (yytext[1] == NOEXP) {
+ savch('"');
+ och = (usch *)&yytext[2];
+ while (*och != EXPAND)
+ savch(*och++);
+ savch('"');
+ break;
+ }
+ /* FALLTHROUGH */
+
+def: default:
+ savstr((usch *)yytext);
+ break;
+ }
+ }
+ if (noexp)
+ error("expmac noexp=%d", noexp);
+ DPRINT(("return from expmac\n"));
+}
+
+/*
+ * expand a function-like macro.
+ * vp points to end of replacement-list
+ * reads function arguments from yylex()
+ * result is written on top of heap
+ */
+void
+expdef(vp, rp, gotwarn)
+ usch *vp;
+ struct recur *rp;
+{
+ usch **args, *sptr, *ap, *bp, *sp;
+ int narg, c, i, plev, snuff, instr;
+ int ellips = 0;
+
+ DPRINT(("expdef %s rp %s\n", vp, (rp ? (char *)rp->sp->namep : "")));
+ if ((c = yylex()) != '(')
+ error("got %c, expected )", c);
+ if (vp[1] == VARG) {
+ narg = *vp--;
+ ellips = 1;
+ } else
+ narg = vp[1];
+ args = alloca(sizeof(usch *) * (narg+ellips));
+
+ /*
+ * read arguments and store them on heap.
+ * will be removed just before return from this function.
+ */
+ sptr = stringbuf;
+ for (i = 0; i < narg && c != ')'; i++) {
+ args[i] = stringbuf;
+ plev = 0;
+ while ((c = yylex()) == WSPACE || c == '\n')
+ ;
+ for (;;) {
+ if (plev == 0 && (c == ')' || c == ','))
+ break;
+ if (c == '(')
+ plev++;
+ if (c == ')')
+ plev--;
+ savstr((usch *)yytext);
+ while ((c = yylex()) == '\n')
+ savch('\n');
+ }
+ while (args[i] < stringbuf &&
+ (stringbuf[-1] == ' ' || stringbuf[-1] == '\t'))
+ stringbuf--;
+ savch('\0');
+ }
+ if (ellips)
+ args[i] = (usch *)"";
+ if (ellips && c != ')') {
+ args[i] = stringbuf;
+ plev = 0;
+ while ((c = yylex()) == WSPACE)
+ ;
+ for (;;) {
+ if (plev == 0 && c == ')')
+ break;
+ if (c == '(')
+ plev++;
+ if (c == ')')
+ plev--;
+ savstr((usch *)yytext);
+ while ((c = yylex()) == '\n')
+ savch('\n');
+ }
+ while (args[i] < stringbuf &&
+ (stringbuf[-1] == ' ' || stringbuf[-1] == '\t'))
+ stringbuf--;
+ savch('\0');
+
+ }
+ if (narg == 0 && ellips == 0)
+ c = yylex();
+ if (c != ')' || (i != narg && ellips == 0) || (i < narg && ellips == 1))
+ error("wrong arg count");
+
+ while (gotwarn--)
+ cunput(WARN);
+
+ sp = vp;
+ instr = snuff = 0;
+
+ /*
+ * push-back replacement-list onto lex buffer while replacing
+ * arguments.
+ */
+ cunput(WARN);
+ while (*sp != 0) {
+ if (*sp == SNUFF)
+ cunput('\"'), snuff ^= 1;
+ else if (*sp == CONC)
+ ;
+ else if (*sp == WARN) {
+
+ if (sp[-1] == VARG) {
+ bp = ap = args[narg];
+ sp--;
+ } else
+ bp = ap = args[(int)*--sp];
+ if (sp[2] != CONC && !snuff && sp[-1] != CONC) {
+ cunput(WARN);
+ while (*bp)
+ bp++;
+ while (bp > ap)
+ cunput(*--bp);
+ DPRINT(("expand arg %d string %s\n", *sp, ap));
+ bp = ap = stringbuf;
+ savch(NOEXP);
+ expmac(NULL);
+ savch(EXPAND);
+ savch('\0');
+ }
+ while (*bp)
+ bp++;
+ while (bp > ap) {
+ bp--;
+ if (snuff && !instr &&
+ (*bp == ' ' || *bp == '\t' || *bp == '\n')){
+ while (*bp == ' ' || *bp == '\t' ||
+ *bp == '\n') {
+ bp--;
+ }
+ cunput(' ');
+ }
+ cunput(*bp);
+ if ((*bp == '\'' || *bp == '"')
+ && bp[-1] != '\\' && snuff) {
+ instr ^= 1;
+ if (instr == 0 && *bp == '"')
+ cunput('\\');
+ }
+ if (instr && (*bp == '\\' || *bp == '"'))
+ cunput('\\');
+ }
+ } else
+ cunput(*sp);
+ sp--;
+ }
+ stringbuf = sptr;
+
+ /* scan the input buffer (until WARN) and save result on heap */
+ expmac(rp);
+}
+
+usch *
+savstr(usch *str)
+{
+ usch *rv = stringbuf;
+
+ do {
+ if (stringbuf >= &sbf[SBSIZE]) {
+ stringbuf = sbf; /* need space to write error message */
+ error("out of macro space!");
+ }
+ } while ((*stringbuf++ = *str++));
+ stringbuf--;
+ return rv;
+}
+
+int
+canexpand(struct recur *rp, struct symtab *np)
+{
+ struct recur *w;
+
+ for (w = rp; w && w->sp != np; w = w->next)
+ ;
+ if (w != NULL)
+ return 0;
+ return 1;
+}
+
+void
+unpstr(usch *c)
+{
+ usch *d = c;
+
+ while (*d)
+ d++;
+ while (d > c) {
+ cunput(*--d);
+ }
+}
+
+void
+flbuf()
+{
+ if (obufp == 0)
+ return;
+ if (Mflag == 0 && write(ofd, outbuf, obufp) < 0)
+ error("obuf write error");
+ obufp = 0;
+}
+
+void
+putch(int ch)
+{
+ outbuf[obufp++] = ch;
+ if (obufp == CPPBUF || (istty && ch == '\n'))
+ flbuf();
+}
+
+void
+putstr(usch *s)
+{
+ for (; *s; s++) {
+ outbuf[obufp++] = *s;
+ if (obufp == CPPBUF || (istty && *s == '\n'))
+ flbuf();
+ }
+}
+
+/*
+ * convert a number to an ascii string. Store it on the heap.
+ */
+static void
+num2str(int num)
+{
+ static usch buf[12];
+ usch *b = buf;
+ int m = 0;
+
+ if (num < 0)
+ num = -num, m = 1;
+ do {
+ *b++ = num % 10 + '0', num /= 10;
+ } while (num);
+ if (m)
+ *b++ = '-';
+ while (b > buf)
+ savch(*--b);
+}
+
+/*
+ * similar to sprintf, but only handles %s and %d.
+ * saves result on heap.
+ */
+usch *
+sheap(char *fmt, ...)
+{
+ va_list ap;
+ usch *op = stringbuf;
+
+ va_start(ap, fmt);
+ for (; *fmt; fmt++) {
+ if (*fmt == '%') {
+ fmt++;
+ switch (*fmt) {
+ case 's':
+ savstr(va_arg(ap, usch *));
+ break;
+ case 'd':
+ num2str(va_arg(ap, int));
+ break;
+ case 'c':
+ savch(va_arg(ap, int));
+ break;
+ default:
+ break; /* cannot call error() here */
+ }
+ } else
+ savch(*fmt);
+ }
+ va_end(ap);
+ *stringbuf = 0;
+ return op;
+}
+
+void
+usage()
+{
+ error("Usage: cpp [-Cdt] [-Dvar=val] [-Uvar] [-Ipath] [-Spath]");
+}
+
+#ifdef notyet
+/*
+ * Symbol table stuff.
+ * The data structure used is a patricia tree implementation using only
+ * bytes to store offsets.
+ * The information stored is (lower address to higher):
+ *
+ * unsigned char bitno[2]; bit number in the string
+ * unsigned char left[3]; offset from base to left element
+ * unsigned char right[3]; offset from base to right element
+ */
+#endif
+
+/*
+ * This patricia implementation is more-or-less the same as
+ * used in ccom for string matching.
+ */
+struct tree {
+ int bitno;
+ struct tree *lr[2];
+};
+
+#define BITNO(x) ((x) & ~(LEFT_IS_LEAF|RIGHT_IS_LEAF))
+#define LEFT_IS_LEAF 0x80000000
+#define RIGHT_IS_LEAF 0x40000000
+#define IS_LEFT_LEAF(x) (((x) & LEFT_IS_LEAF) != 0)
+#define IS_RIGHT_LEAF(x) (((x) & RIGHT_IS_LEAF) != 0)
+#define P_BIT(key, bit) (key[bit >> 3] >> (bit & 7)) & 1
+#define CHECKBITS 8
+
+static struct tree *sympole;
+static int numsyms;
+
+/*
+ * Allocate a symtab struct and store the string.
+ */
+static struct symtab *
+getsymtab(usch *str)
+{
+ struct symtab *sp = malloc(sizeof(struct symtab));
+
+ if (sp == NULL)
+ error("getsymtab: couldn't allocate symtab");
+ sp->namep = savstr(str);
+ savch('\0');
+ sp->value = NULL;
+ sp->file = ifiles ? ifiles->orgfn : (usch *)"<initial>";
+ sp->line = ifiles ? ifiles->lineno : 0;
+ return sp;
+}
+
+/*
+ * Do symbol lookup in a patricia tree.
+ * Only do full string matching, no pointer optimisations.
+ */
+struct symtab *
+lookup(usch *key, int enterf)
+{
+ struct symtab *sp;
+ struct tree *w, *new, *last;
+ int len, cix, bit, fbit, svbit, ix, bitno;
+ usch *k, *m, *sm;
+
+ /* Count full string length */
+ for (k = key, len = 0; *k; k++, len++)
+ ;
+
+ switch (numsyms) {
+ case 0: /* no symbols yet */
+ if (enterf != ENTER)
+ return NULL;
+ sympole = (struct tree *)getsymtab(key);
+ numsyms++;
+ return (struct symtab *)sympole;
+
+ case 1:
+ w = sympole;
+ svbit = 0; /* XXX gcc */
+ break;
+
+ default:
+ w = sympole;
+ bitno = len * CHECKBITS;
+ for (;;) {
+ bit = BITNO(w->bitno);
+ fbit = bit > bitno ? 0 : P_BIT(key, bit);
+ svbit = fbit ? IS_RIGHT_LEAF(w->bitno) :
+ IS_LEFT_LEAF(w->bitno);
+ w = w->lr[fbit];
+ if (svbit)
+ break;
+ }
+ }
+
+ sp = (struct symtab *)w;
+
+ sm = m = sp->namep;
+ k = key;
+
+ /* Check for correct string and return */
+ for (cix = 0; *m && *k && *m == *k; m++, k++, cix += CHECKBITS)
+ ;
+ if (*m == 0 && *k == 0) {
+ if (enterf != ENTER && sp->value == NULL)
+ return NULL;
+ return sp;
+ }
+
+ if (enterf != ENTER)
+ return NULL; /* no string found and do not enter */
+
+ ix = *m ^ *k;
+ while ((ix & 1) == 0)
+ ix >>= 1, cix++;
+
+ /* Create new node */
+ if ((new = malloc(sizeof *new)) == NULL)
+ error("getree: couldn't allocate tree");
+ bit = P_BIT(key, cix);
+ new->bitno = cix | (bit ? RIGHT_IS_LEAF : LEFT_IS_LEAF);
+ new->lr[bit] = (struct tree *)getsymtab(key);
+
+ if (numsyms++ == 1) {
+ new->lr[!bit] = sympole;
+ new->bitno |= (bit ? LEFT_IS_LEAF : RIGHT_IS_LEAF);
+ sympole = new;
+ return (struct symtab *)new->lr[bit];
+ }
+
+ w = sympole;
+ last = NULL;
+ for (;;) {
+ fbit = w->bitno;
+ bitno = BITNO(w->bitno);
+ if (bitno == cix)
+ error("bitno == cix");
+ if (bitno > cix)
+ break;
+ svbit = P_BIT(key, bitno);
+ last = w;
+ w = w->lr[svbit];
+ if (fbit & (svbit ? RIGHT_IS_LEAF : LEFT_IS_LEAF))
+ break;
+ }
+
+ new->lr[!bit] = w;
+ if (last == NULL) {
+ sympole = new;
+ } else {
+ last->lr[svbit] = new;
+ last->bitno &= ~(svbit ? RIGHT_IS_LEAF : LEFT_IS_LEAF);
+ }
+ if (bitno < cix)
+ new->bitno |= (bit ? LEFT_IS_LEAF : RIGHT_IS_LEAF);
+ return (struct symtab *)new->lr[bit];
+}
+
diff --git a/usr.bin/pcc/cpp/cpp.h b/usr.bin/pcc/cpp/cpp.h
new file mode 100644
index 00000000000..25eeaccf5c3
--- /dev/null
+++ b/usr.bin/pcc/cpp/cpp.h
@@ -0,0 +1,120 @@
+/* $OpenBSD: cpp.h,v 1.1 2007/10/07 17:58:51 otto Exp $ */
+
+/*
+ * Copyright (c) 2004 Anders Magnusson (ragge@ludd.luth.se).
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 <stdio.h> /* for obuf */
+
+#include "../config.h"
+
+typedef unsigned char usch;
+#ifdef YYTEXT_POINTER
+extern char *yytext;
+#else
+extern char yytext[];
+#endif
+extern usch *stringbuf;
+
+extern int trulvl;
+extern int flslvl;
+extern int elflvl;
+extern int elslvl;
+extern int tflag, Cflag;
+extern int Mflag, dMflag;
+extern usch *Mfile;
+extern int ofd;
+
+/* args for lookup() */
+#define FIND 0
+#define ENTER 1
+
+/* buffer used internally */
+#ifndef CPPBUF
+#ifdef __pdp11__
+#define CPPBUF BUFSIZ
+#else
+#define CPPBUF 65536
+#endif
+#endif
+
+#define NAMEMAX 64 /* max len of identifier */
+
+/* definition for include file info */
+struct includ {
+ struct includ *next;
+ usch *fname; /* current fn, changed if #line found */
+ usch *orgfn; /* current fn, not changed */
+ int lineno;
+ int infil;
+ usch *curptr;
+ usch *maxread;
+ usch *buffer;
+ usch bbuf[NAMEMAX+CPPBUF+1];
+} *ifiles;
+
+/* Symbol table entry */
+struct symtab {
+ usch *namep;
+ usch *value;
+ usch *file;
+ int line;
+};
+
+struct initar {
+ struct initar *next;
+ int type;
+ char *str;
+};
+
+struct recur; /* not used outside cpp.c */
+int subst(struct symtab *, struct recur *);
+struct symtab *lookup(usch *namep, int enterf);
+usch *gotident(struct symtab *nl);
+int slow; /* scan slowly for new tokens */
+
+int pushfile(usch *fname);
+void popfile(void);
+void prtline(void);
+int yylex(void);
+void cunput(int);
+int curline(void);
+char *curfile(void);
+void setline(int);
+void setfile(char *);
+int yyparse(void);
+void yyerror(char *);
+void unpstr(usch *);
+usch *savstr(usch *str);
+void savch(int c);
+void mainscan(void);
+void putch(int);
+void putstr(usch *s);
+void line(void);
+usch *sheap(char *fmt, ...);
+void xerror(usch *);
+#define error(...) xerror(sheap(__VA_ARGS__))
+void expmac(struct recur *);
diff --git a/usr.bin/pcc/cpp/cpy.y b/usr.bin/pcc/cpp/cpy.y
new file mode 100644
index 00000000000..cb0345a8eda
--- /dev/null
+++ b/usr.bin/pcc/cpp/cpy.y
@@ -0,0 +1,166 @@
+/* $OpenBSD: cpy.y,v 1.1 2007/10/07 17:58:51 otto Exp $ */
+
+/*
+ * Copyright (c) 2004 Anders Magnusson (ragge@ludd.luth.se).
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ */
+
+/*
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code and documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 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.
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed or owned by Caldera
+ * International, Inc.
+ * Neither the name of Caldera International, Inc. nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, 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 CALDERA INTERNATIONAL, INC. 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 OFLIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+%{
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+void yyerror(char *);
+int yylex(void);
+%}
+
+%term stop
+%term EQ NE LE GE LS RS
+%term ANDAND OROR IDENT NUMBER
+/*
+ * The following terminals are not used in the yacc code.
+ */
+%term STRING FPOINT WSPACE VA_ARGS CONCAT MKSTR ELLIPS
+
+%left ','
+%right '='
+%right '?' ':'
+%left OROR
+%left ANDAND
+%left '|' '^'
+%left '&'
+%binary EQ NE
+%binary '<' '>' LE GE
+%left LS RS
+%left '+' '-'
+%left '*' '/' '%'
+%right '!' '~' UMINUS
+%left '(' '.'
+
+%union {
+ long long val;
+}
+
+%type <val> term NUMBER e
+
+%%
+S: e '\n' { return($1 != 0);}
+
+
+e: e '*' e
+ {$$ = $1 * $3;}
+ | e '/' e
+ {$$ = $1 / $3;}
+ | e '%' e
+ {$$ = $1 % $3;}
+ | e '+' e
+ {$$ = $1 + $3;}
+ | e '-' e
+ {$$ = $1 - $3;}
+ | e LS e
+ {$$ = $1 << $3;}
+ | e RS e
+ {$$ = $1 >> $3;}
+ | e '<' e
+ {$$ = $1 < $3;}
+ | e '>' e
+ {$$ = $1 > $3;}
+ | e LE e
+ {$$ = $1 <= $3;}
+ | e GE e
+ {$$ = $1 >= $3;}
+ | e EQ e
+ {$$ = $1 == $3;}
+ | e NE e
+ {$$ = $1 != $3;}
+ | e '&' e
+ {$$ = $1 & $3;}
+ | e '^' e
+ {$$ = $1 ^ $3;}
+ | e '|' e
+ {$$ = $1 | $3;}
+ | e ANDAND e
+ {$$ = $1 && $3;}
+ | e OROR e
+ {$$ = $1 || $3;}
+ | e '?' e ':' e
+ {$$ = $1 ? $3 : $5;}
+ | e ',' e
+ {$$ = $3;}
+ | term
+ {$$ = $1;}
+term:
+ '-' term %prec UMINUS
+ {$$ = -$2;}
+ | '!' term
+ {$$ = !$2;}
+ | '~' term
+ {$$ = ~$2;}
+ | '(' e ')'
+ {$$ = $2;}
+ | NUMBER
+ {$$= $1;}
+%%
+
+#include "cpp.h"
+
+void
+yyerror(char *err)
+{
+ error(err);
+}
diff --git a/usr.bin/pcc/cpp/scanner.l b/usr.bin/pcc/cpp/scanner.l
new file mode 100644
index 00000000000..d021d9991e0
--- /dev/null
+++ b/usr.bin/pcc/cpp/scanner.l
@@ -0,0 +1,817 @@
+%{
+/* $OpenBSD: scanner.l,v 1.1 2007/10/07 17:58:51 otto Exp $ */
+
+/*
+ * Copyright (c) 2004 Anders Magnusson. 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "cpp.h"
+#include "y.tab.h"
+%}
+
+%{
+static long long cvtdig(int rad);
+static int charcon(void);
+static void elsestmt(void);
+static void ifdefstmt(void);
+static void ifndefstmt(void);
+static void endifstmt(void);
+static void ifstmt(void);
+static void cpperror(void);
+static void pragmastmt(void);
+static void undefstmt(void);
+static void cpperror(void);
+static void elifstmt(void);
+//static void linestmt(void);
+static void storepb(void);
+void include(void);
+void define(void);
+
+extern int yyget_lineno (void);
+extern void yyset_lineno (int);
+
+static int inch(void);
+
+static int scale, gotdef, contr;
+int inif;
+
+#ifdef FLEX_SCANNER /* should be set by autoconf instead */
+static int
+yyinput(char *b, int m)
+{
+ int c, i;
+
+ for (i = 0; i < m; i++) {
+ if ((c = inch()) < 0)
+ break;
+ *b++ = c;
+ if (c == '\n') {
+ i++;
+ break;
+ }
+ }
+ return i;
+}
+#undef YY_INPUT
+#undef YY_BUF_SIZE
+#define YY_BUF_SIZE 32768
+#define YY_INPUT(b,r,m) (r = yyinput(b, m))
+#define fprintf(x, ...) error(__VA_ARGS__)
+#define ECHO putstr((usch *)yytext)
+#undef fileno
+#define fileno(x) 0
+
+#if YY_FLEX_SUBMINOR_VERSION >= 31
+/* Hack to avoid unneccessary warnings */
+FILE *yyget_in (void);
+FILE *yyget_out (void);
+int yyget_leng (void);
+char *yyget_text (void);
+void yyset_in (FILE * in_str );
+void yyset_out (FILE * out_str );
+int yyget_debug (void);
+void yyset_debug (int bdebug );
+int yylex_destroy (void);
+#endif
+#else /* Assume lex here */
+#undef input
+#undef unput
+#define input() inch()
+#define unput(ch) unch(ch)
+#endif
+#define PRTOUT(x) if (YYSTATE || slow) return x; if (!flslvl) putstr((usch *)yytext);
+%}
+
+D [0-9]
+L [a-zA-Z_]
+H [a-fA-F0-9]
+E [Ee][+-]?{D}+
+FS (f|F|l|L)
+IS (u|U|l|L)*
+WS [\t ]
+
+%s IFR CONTR DEF
+
+%%
+
+"\n" { int os = YYSTATE;
+ if (os != IFR)
+ BEGIN 0;
+ ifiles->lineno++;
+ if (flslvl == 0) {
+ if (ifiles->lineno == 1)
+ prtline();
+ else
+ putch('\n');
+ }
+ if ((os != 0 || slow) && !contr)
+ return '\n';
+ contr = 0;
+ }
+
+"\r" { ; /* Ignore CR's */ }
+
+<IFR>"==" { return EQ; }
+<IFR>"!=" { return NE; }
+<IFR>"<=" { return LE; }
+<IFR>"<<" { return LS; }
+<IFR>">>" { return RS; }
+<IFR>">=" { return GE; }
+<IFR>"||" { return OROR; }
+<IFR>"&&" { return ANDAND; }
+<IFR>"defined" { int p, c;
+ gotdef = 1;
+ if ((p = c = yylex()) == '(')
+ c = yylex();
+ if (c != IDENT || (p != IDENT && p != '('))
+ error("syntax error");
+ if (p == '(' && yylex() != ')')
+ error("syntax error");
+ return NUMBER;
+ }
+
+<IFR>{WS}+ { ; }
+<IFR>{L}({L}|{D})* {
+ if (gotdef) {
+ yylval.val =
+ lookup((usch *)yytext, FIND) != 0;
+ gotdef = 0;
+ return IDENT;
+ }
+ yylval.val = 0;
+ return NUMBER;
+ }
+
+[1-9][0-9]* { if (slow && !YYSTATE) return IDENT; scale = 10; goto num; }
+
+0[xX]{H}+{IS}? { scale = 16;
+ num: if (YYSTATE)
+ yylval.val = cvtdig(scale);
+ PRTOUT(NUMBER);
+ }
+0{D}+{IS}? { scale = 8; goto num; }
+{D}+{IS}? { scale = 10; goto num; }
+L?'(\\.|[^\\'])+' { if (YYSTATE)
+ yylval.val = charcon();
+ PRTOUT(NUMBER);
+ }
+
+<IFR>. { return yytext[0]; }
+
+{D}+{E}{FS}? { PRTOUT(FPOINT); }
+{D}*"."{D}+({E})?{FS}? { PRTOUT(FPOINT); }
+{D}+"."{D}*({E})?{FS}? { PRTOUT(FPOINT); }
+
+^{WS}*#{WS}* { contr = 1; BEGIN CONTR; }
+{WS}+ { PRTOUT(WSPACE); }
+
+<CONTR>"ifndef" { contr = 0; ifndefstmt(); }
+<CONTR>"ifdef" { contr = 0; ifdefstmt(); }
+<CONTR>"if" { contr = 0; storepb(); BEGIN IFR; ifstmt(); BEGIN 0; }
+<CONTR>"include" { contr = 0; BEGIN 0; include(); prtline(); }
+<CONTR>"else" { contr = 0; elsestmt(); }
+<CONTR>"endif" { contr = 0; endifstmt(); }
+<CONTR>"error" { contr = 0; if (slow) return IDENT; cpperror(); BEGIN 0; }
+<CONTR>"define" { contr = 0; BEGIN DEF; define(); BEGIN 0; }
+<CONTR>"undef" { contr = 0; if (slow) return IDENT; undefstmt(); }
+<CONTR>"line" { contr = 0; storepb(); BEGIN 0; line(); }
+<CONTR>"pragma" { contr = 0; pragmastmt(); BEGIN 0; }
+<CONTR>"elif" { contr = 0; storepb(); BEGIN IFR; elifstmt(); BEGIN 0; }
+
+
+
+"//".*$ { /* if (tflag) yyless(..) */
+ if (Cflag)
+ putstr((usch *)yytext);
+ else if (!flslvl)
+ putch(' ');
+ }
+"/*" { int c, wrn;
+ if (Cflag)
+ putstr((usch *)yytext);
+ wrn = 0;
+ more: while ((c = input()) && c != '*') {
+ if (c == '\n')
+ putch(c), ifiles->lineno++;
+ else if (c == 1) /* WARN */
+ wrn = 1;
+ else if (Cflag)
+ putch(c);
+ }
+ if (c == 0)
+ return 0;
+ if (Cflag)
+ putch(c);
+ if ((c = input()) && c != '/') {
+ if (Cflag)
+ putch('*');
+ unput(c);
+ goto more;
+ }
+ if (Cflag)
+ putch(c);
+ if (c == 0)
+ return 0;
+ if (!tflag && !Cflag && !flslvl)
+ unput(' ');
+ if (wrn)
+ unput(1);
+ }
+
+<DEF>"##" { return CONCAT; }
+<DEF>"#" { return MKSTR; }
+<DEF>"..." { return ELLIPS; }
+<DEF>"__VA_ARGS__" { return VA_ARGS; }
+
+L?\"(\\.|[^\\"])*\" { PRTOUT(STRING); }
+[a-zA-Z_0-9]+ { /* {L}({L}|{D})* */
+ struct symtab *nl;
+ if (slow)
+ return IDENT;
+ if (YYSTATE == CONTR) {
+ if (flslvl == 0) {
+ /*error("undefined control");*/
+ while (input() != '\n')
+ ;
+ unput('\n');
+ BEGIN 0;
+ goto xx;
+ } else {
+ BEGIN 0; /* do nothing */
+ }
+ }
+ if (flslvl) {
+ ; /* do nothing */
+ } else if (isdigit((int)yytext[0]) == 0 &&
+ (nl = lookup((usch *)yytext, FIND)) != 0) {
+ usch *op = stringbuf;
+ putstr(gotident(nl));
+ stringbuf = op;
+ } else
+ putstr((usch *)yytext);
+ xx: ;
+ }
+
+. { PRTOUT(yytext[0]); }
+
+%%
+
+usch *yyp, yybuf[CPPBUF];
+
+int yylex(void);
+int yywrap(void);
+
+static int
+inpch(void)
+{
+ int len;
+
+ if (ifiles->curptr < ifiles->maxread)
+ return *ifiles->curptr++;
+
+ if ((len = read(ifiles->infil, ifiles->buffer, CPPBUF)) < 0)
+ error("read error on file %s", ifiles->orgfn);
+ if (len == 0)
+ return -1;
+ ifiles->curptr = ifiles->buffer;
+ ifiles->maxread = ifiles->buffer + len;
+ return inpch();
+}
+
+#define unch(c) *--ifiles->curptr = c
+
+static int
+inch(void)
+{
+ int c;
+
+again: switch (c = inpch()) {
+ case '\\': /* continued lines */
+ if ((c = inpch()) == '\n') {
+ ifiles->lineno++;
+ putch('\n');
+ goto again;
+ }
+ unch(c);
+ return '\\';
+ case '?': /* trigraphs */
+ if ((c = inpch()) != '?') {
+ unch(c);
+ return '?';
+ }
+ switch (c = inpch()) {
+ case '=': c = '#'; break;
+ case '(': c = '['; break;
+ case ')': c = ']'; break;
+ case '<': c = '{'; break;
+ case '>': c = '}'; break;
+ case '/': c = '\\'; break;
+ case '\'': c = '^'; break;
+ case '!': c = '|'; break;
+ case '-': c = '~'; break;
+ default:
+ unch(c);
+ unch('?');
+ return '?';
+ }
+ unch(c);
+ goto again;
+ default:
+ return c;
+ }
+}
+
+/*
+ * Let the command-line args be faked defines at beginning of file.
+ */
+static void
+prinit(struct initar *it, struct includ *ic)
+{
+ char *a, *pre, *post;
+
+ if (it->next)
+ prinit(it->next, ic);
+ pre = post = NULL; /* XXX gcc */
+ switch (it->type) {
+ case 'D':
+ pre = "#define ";
+ if ((a = strchr(it->str, '=')) != NULL) {
+ *a = ' ';
+ post = "\n";
+ } else
+ post = " 1\n";
+ break;
+ case 'U':
+ pre = "#undef ";
+ post = "\n";
+ break;
+ case 'i':
+ pre = "#include \"";
+ post = "\"\n";
+ break;
+ default:
+ error("prinit");
+ }
+ strlcat((char *)ic->buffer, pre, CPPBUF+1);
+ strlcat((char *)ic->buffer, it->str, CPPBUF+1);
+ if (strlcat((char *)ic->buffer, post, CPPBUF+1) >= CPPBUF+1)
+ error("line exceeds buffer size");
+
+ ic->lineno--;
+ while (*ic->maxread)
+ ic->maxread++;
+}
+
+/*
+ * A new file included.
+ * If ifiles == NULL, this is the first file and already opened (stdin).
+ * Return 0 on success, -1 on failure to open file.
+ */
+int
+pushfile(usch *file)
+{
+ extern struct initar *initar;
+ struct includ ibuf;
+ struct includ *old;
+ struct includ *ic;
+ int c, otrulvl;
+
+ ic = &ibuf;
+ old = ifiles;
+
+ slow = 0;
+ if (file != NULL) {
+ if ((ic->infil = open((char *)file, O_RDONLY)) < 0)
+ return -1;
+ ic->orgfn = ic->fname = file;
+ } else {
+ ic->infil = 0;
+ ic->orgfn = ic->fname = (usch *)"<stdin>";
+ }
+ ic->buffer = ic->bbuf+NAMEMAX;
+ ic->curptr = ic->buffer;
+ ifiles = ic;
+ ic->lineno = 1;
+ ic->maxread = ic->curptr;
+ prtline();
+ if (initar) {
+ *ic->maxread = 0;
+ prinit(initar, ic);
+ if (dMflag)
+ write(ofd, ic->buffer, strlen((char *)ic->buffer));
+ initar = NULL;
+ }
+
+ otrulvl = trulvl;
+
+ if ((c = yylex()) != 0)
+ error("yylex returned %d", c);
+
+ if (otrulvl != trulvl || flslvl)
+ error("unterminated conditional");
+
+ ifiles = old;
+ close(ic->infil);
+ return 0;
+}
+
+/*
+ * Print current position to output file.
+ */
+void
+prtline()
+{
+ usch *s, *os = stringbuf;
+
+ if (Mflag) {
+ if (dMflag)
+ return; /* no output */
+ if (ifiles->lineno == 1) {
+ s = sheap("%s: %s\n", Mfile, ifiles->fname);
+ write(ofd, s, strlen((char *)s));
+ }
+ } else
+ putstr(sheap("# %d \"%s\"\n", ifiles->lineno, ifiles->fname));
+ stringbuf = os;
+}
+
+void
+cunput(int c)
+{
+#ifdef CPP_DEBUG
+ extern int dflag;
+ if (dflag)printf(": '%c'(%d)", c, c);
+#endif
+ unput(c);
+}
+
+int yywrap(void) { return 1; }
+
+static int
+dig2num(int c)
+{
+ if (c >= 'a')
+ c = c - 'a' + 10;
+ else if (c >= 'A')
+ c = c - 'A' + 10;
+ else
+ c = c - '0';
+ return c;
+}
+
+/*
+ * Convert some string numbers to long long.
+ * Do not care about UL trailers, should we?
+ */
+static long long
+cvtdig(int rad)
+{
+ long long rv = 0;
+ char *y = yytext;
+ int c;
+
+ c = *y++;
+ if (rad == 16)
+ y++;
+ while (isxdigit(c)) {
+ rv = rv * rad + dig2num(c);
+ c = *y++;
+ }
+ return rv;
+}
+
+static int
+charcon(void)
+{
+ usch *p = (usch *)yytext;
+ int val, c;
+
+ if (*p == 'L')
+ p++;
+ p++; /* first ' */
+ val = 0;
+ if (*p++ == '\\') {
+ switch (*p++) {
+ case 'a': val = '\a'; break;
+ case 'b': val = '\b'; break;
+ case 'f': val = '\f'; break;
+ case 'n': val = '\n'; break;
+ case 'r': val = '\r'; break;
+ case 't': val = '\t'; break;
+ case 'v': val = '\v'; break;
+ case '\"': val = '\"'; break;
+ case '\'': val = '\''; break;
+ case '\\': val = '\\'; break;
+ case 'x':
+ while (isxdigit(c = *p)) {
+ val = val * 16 + dig2num(c);
+ p++;
+ }
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ p--;
+ while (isdigit(c = *p)) {
+ val = val * 8 + (c - '0');
+ p++;
+ }
+ break;
+ default: val = p[-1];
+ }
+
+ } else
+ val = p[-1];
+ return val;
+}
+
+static void
+chknl(void)
+{
+ int t;
+
+ while ((t = yylex()) == WSPACE)
+ ;
+ if (t != '\n')
+ error("newline expected, got %d", t);
+}
+
+static void
+elsestmt(void)
+{
+ if (flslvl) {
+ if (elflvl > trulvl)
+ ;
+ else if (--flslvl!=0) {
+ flslvl++;
+ } else {
+ trulvl++;
+ prtline();
+ }
+ } else if (trulvl) {
+ flslvl++;
+ trulvl--;
+ } else
+ error("If-less else");
+ if (elslvl==trulvl+flslvl)
+ error("Too many else");
+ elslvl=trulvl+flslvl;
+ chknl();
+}
+
+static void
+ifdefstmt(void)
+{
+ if (flslvl) {
+ /* just ignore the rest of the line */
+ while (input() != '\n')
+ ;
+ unput('\n');
+ yylex();
+ flslvl++;
+ return;
+ }
+ slow = 1;
+ if (yylex() != WSPACE || yylex() != IDENT)
+ error("bad ifdef");
+ slow = 0;
+ if (flslvl == 0 && lookup((usch *)yytext, FIND) != 0)
+ trulvl++;
+ else
+ flslvl++;
+ chknl();
+}
+
+static void
+ifndefstmt(void)
+{
+ slow = 1;
+ if (yylex() != WSPACE || yylex() != IDENT)
+ error("bad ifndef");
+ slow = 0;
+ if (flslvl == 0 && lookup((usch *)yytext, FIND) == 0)
+ trulvl++;
+ else
+ flslvl++;
+ chknl();
+}
+
+static void
+endifstmt(void)
+{
+ if (flslvl) {
+ flslvl--;
+ if (flslvl == 0)
+ prtline();
+ } else if (trulvl)
+ trulvl--;
+ else
+ error("If-less endif");
+ if (flslvl == 0)
+ elflvl = 0;
+ elslvl = 0;
+ chknl();
+}
+
+/*
+ * Note! Ugly!
+ * Walk over the string s and search for defined, and replace it with
+ * spaces and a 1 or 0.
+ */
+static void
+fixdefined(usch *s)
+{
+ usch *bc, oc;
+
+ for (; *s; s++) {
+ if (*s != 'd')
+ continue;
+ if (memcmp(s, "defined", 7))
+ continue;
+ /* Ok, got defined, can scratch it now */
+ memset(s, ' ', 7);
+ s += 7;
+#define WSARG(x) (x == ' ' || x == '\t')
+ if (*s != '(' && !WSARG(*s))
+ continue;
+ while (WSARG(*s))
+ s++;
+ if (*s == '(')
+ s++;
+ while (WSARG(*s))
+ s++;
+#define IDARG(x) ((x>= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z') || (x == '_'))
+#define NUMARG(x) (x >= '0' && x <= '9')
+ if (!IDARG(*s))
+ error("bad defined arg");
+ bc = s;
+ while (IDARG(*s) || NUMARG(*s))
+ s++;
+ oc = *s;
+ *s = 0;
+ *bc = (lookup(bc, FIND) != 0) + '0';
+ memset(bc+1, ' ', s-bc-1);
+ *s = oc;
+ }
+}
+
+/*
+ * get the full line of identifiers after an #if, pushback a WARN and
+ * the line and prepare for expmac() to expand.
+ * This is done before switching state. When expmac is finished,
+ * pushback the expanded line, change state and call yyparse.
+ */
+static void
+storepb(void)
+{
+ usch *opb = stringbuf;
+ int c;
+
+ while ((c = input()) != '\n')
+ savch(c);
+ cunput('\n');
+ savch(0);
+ fixdefined(opb); /* XXX can fail if #line? */
+ cunput(1); /* WARN XXX */
+ unpstr(opb);
+ stringbuf = opb;
+ slow = 1;
+ expmac(NULL);
+ slow = 0;
+ /* line now expanded */
+ while (stringbuf > opb)
+ cunput(*--stringbuf);
+}
+
+static void
+ifstmt(void)
+{
+ if (flslvl == 0) {
+ slow = 1;
+ if (yyparse())
+ ++trulvl;
+ else
+ ++flslvl;
+ slow = 0;
+ } else
+ ++flslvl;
+}
+
+static void
+elifstmt(void)
+{
+ if (flslvl == 0)
+ elflvl = trulvl;
+ if (flslvl) {
+ if (elflvl > trulvl)
+ ;
+ else if (--flslvl!=0)
+ ++flslvl;
+ else {
+ slow = 1;
+ if (yyparse()) {
+ ++trulvl;
+ prtline();
+ } else
+ ++flslvl;
+ slow = 0;
+ }
+ } else if (trulvl) {
+ ++flslvl;
+ --trulvl;
+ } else
+ error("If-less elif");
+}
+
+static usch *
+svinp(void)
+{
+ int c;
+ usch *cp = stringbuf;
+
+ while ((c = input()) && c != '\n')
+ savch(c);
+ savch('\n');
+ savch(0);
+ BEGIN 0;
+ return cp;
+}
+
+static void
+cpperror(void)
+{
+ usch *cp;
+ int c;
+
+ if (flslvl)
+ return;
+ c = yylex();
+ if (c != WSPACE && c != '\n')
+ error("bad error");
+ cp = svinp();
+ if (flslvl)
+ stringbuf = cp;
+ else
+ error("error: %s", cp);
+}
+
+static void
+undefstmt(void)
+{
+ struct symtab *np;
+
+ slow = 1;
+ if (yylex() != WSPACE || yylex() != IDENT)
+ error("bad undef");
+ if (flslvl == 0 && (np = lookup((usch *)yytext, FIND)))
+ np->value = 0;
+ slow = 0;
+ chknl();
+}
+
+static void
+pragmastmt(void)
+{
+ int c;
+
+ slow = 1;
+ if (yylex() != WSPACE)
+ error("bad pragma");
+ if (!flslvl)
+ putstr((usch *)"#pragma ");
+ do {
+ c = input();
+ if (!flslvl)
+ putch(c); /* Do arg expansion instead? */
+ } while (c && c != '\n');
+ ifiles->lineno++;
+ prtline();
+ slow = 0;
+}