diff options
Diffstat (limited to 'usr.bin/pcc/cpp')
-rw-r--r-- | usr.bin/pcc/cpp/Makefile | 17 | ||||
-rw-r--r-- | usr.bin/pcc/cpp/cpp.1 | 194 | ||||
-rw-r--r-- | usr.bin/pcc/cpp/cpp.c | 1482 | ||||
-rw-r--r-- | usr.bin/pcc/cpp/cpp.h | 120 | ||||
-rw-r--r-- | usr.bin/pcc/cpp/cpy.y | 166 | ||||
-rw-r--r-- | usr.bin/pcc/cpp/scanner.l | 817 |
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; +} |