From 0d66213fcda34368cdbd1b4a36e0cb22e040729c Mon Sep 17 00:00:00 2001 From: glass Date: Fri, 18 Jun 1993 21:50:26 +0000 Subject: [PATCH] baseline of new version of m4 supplied by Ozan Yigit, original author of the broken m4 we had. This is his stuff virgin + our Makefile. --- usr.bin/m4/Makefile | 4 +- usr.bin/m4/TEST/ack.m4 | 38 -- usr.bin/m4/TEST/hanoi.m4 | 38 -- usr.bin/m4/TEST/hash.m4 | 38 -- usr.bin/m4/TEST/sqroot.m4 | 38 -- usr.bin/m4/TEST/string.m4 | 37 -- usr.bin/m4/TEST/test.m4 | 37 -- usr.bin/m4/expr.c | 1209 +++++++++++++++++++------------------ usr.bin/m4/extr.h | 195 ++++-- usr.bin/m4/int2str.c | 58 ++ usr.bin/m4/look.c | 214 +++---- usr.bin/m4/m4.1 | 179 ++++++ usr.bin/m4/main.c | 1108 ++++++++++++++++++++++----------- usr.bin/m4/mdef.h | 123 ++-- usr.bin/m4/misc.c | 550 ++++++++++------- usr.bin/m4/ourlims.h | 16 + usr.bin/m4/serv.c | 2 +- 17 files changed, 2264 insertions(+), 1620 deletions(-) create mode 100644 usr.bin/m4/int2str.c create mode 100644 usr.bin/m4/m4.1 create mode 100644 usr.bin/m4/ourlims.h diff --git a/usr.bin/m4/Makefile b/usr.bin/m4/Makefile index 4435de773e20..b1ead05064c3 100644 --- a/usr.bin/m4/Makefile +++ b/usr.bin/m4/Makefile @@ -5,7 +5,7 @@ PROG= m4 CFLAGS+=-DEXTENDED -SRCS = main.c eval.c serv.c look.c misc.c expr.c -NOMAN= noman +SRCS = expr.c int2str.c look.c main.c misc.c serv.c +MAN1 = m4.0 .include diff --git a/usr.bin/m4/TEST/ack.m4 b/usr.bin/m4/TEST/ack.m4 index 49e2c1e3db30..e05f24139c1f 100644 --- a/usr.bin/m4/TEST/ack.m4 +++ b/usr.bin/m4/TEST/ack.m4 @@ -1,40 +1,2 @@ -# -# Copyright (c) 1989 The Regents of the University of California. -# All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Ozan Yigit. -# -# 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. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)ack.m4 5.2 (Berkeley) 4/12/91 -# - define(ack, `ifelse($1,0,incr($2),$2,0,`ack(DECR($1),1)', `ack(DECR($1), ack($1,DECR($2)))')') diff --git a/usr.bin/m4/TEST/hanoi.m4 b/usr.bin/m4/TEST/hanoi.m4 index da5eb81a880f..6a8f1b63220f 100644 --- a/usr.bin/m4/TEST/hanoi.m4 +++ b/usr.bin/m4/TEST/hanoi.m4 @@ -1,41 +1,3 @@ -# -# Copyright (c) 1989 The Regents of the University of California. -# All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Ozan Yigit. -# -# 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. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)hanoi.m4 5.2 (Berkeley) 4/12/91 -# - define(hanoi, `trans(A, B, C, $1)') define(moved,`move disk from $1 to $2 diff --git a/usr.bin/m4/TEST/hash.m4 b/usr.bin/m4/TEST/hash.m4 index 2f7cf67bfeed..feb54b8a3345 100644 --- a/usr.bin/m4/TEST/hash.m4 +++ b/usr.bin/m4/TEST/hash.m4 @@ -1,41 +1,3 @@ -# -# Copyright (c) 1989 The Regents of the University of California. -# All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Ozan Yigit. -# -# 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. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)hash.m4 5.2 (Berkeley) 4/12/91 -# - dnl This probably will not run on any m4 that cannot dnl handle char constants in eval. dnl diff --git a/usr.bin/m4/TEST/sqroot.m4 b/usr.bin/m4/TEST/sqroot.m4 index dc9802402b45..6087906980c0 100644 --- a/usr.bin/m4/TEST/sqroot.m4 +++ b/usr.bin/m4/TEST/sqroot.m4 @@ -1,41 +1,3 @@ -# -# Copyright (c) 1989 The Regents of the University of California. -# All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Ozan Yigit. -# -# 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. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)sqroot.m4 5.2 (Berkeley) 4/12/91 -# - define(square_root, `ifelse(eval($1<0),1,negative-square-root, `square_root_aux($1, 1, eval(($1+1)/2))')') diff --git a/usr.bin/m4/TEST/string.m4 b/usr.bin/m4/TEST/string.m4 index 23297b839329..0a2ed81273c9 100644 --- a/usr.bin/m4/TEST/string.m4 +++ b/usr.bin/m4/TEST/string.m4 @@ -1,40 +1,3 @@ -# -# Copyright (c) 1989 The Regents of the University of California. -# All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Ozan Yigit. -# -# 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. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)string.m4 5.2 (Berkeley) 4/12/91 -# define(string,`integer $1(len(substr($2,1))) str($1,substr($2,1),0) diff --git a/usr.bin/m4/TEST/test.m4 b/usr.bin/m4/TEST/test.m4 index 2d5e1b416642..c3400801a40a 100644 --- a/usr.bin/m4/TEST/test.m4 +++ b/usr.bin/m4/TEST/test.m4 @@ -1,41 +1,4 @@ # -# Copyright (c) 1989 The Regents of the University of California. -# All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Ozan Yigit. -# -# 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. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)test.m4 5.2 (Berkeley) 4/12/91 -# - # test file for mp (not comprehensive) # # v7 m4 does not have `decr'. diff --git a/usr.bin/m4/expr.c b/usr.bin/m4/expr.c index eb4da8e92dae..fc48eb31e02f 100644 --- a/usr.bin/m4/expr.c +++ b/usr.bin/m4/expr.c @@ -1,600 +1,617 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Ozan Yigit. - * - * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ +/* File : expr.c + Authors: Mike Lutz & Bob Harper + Editors: Ozan Yigit & Richard A. O'Keefe + Updated: %G% + Purpose: arithmetic expression evaluator. -#ifndef lint -static char sccsid[] = "@(#)expr.c 5.3 (Berkeley) 2/26/91"; -#endif /* not lint */ + expr() performs a standard recursive descent parse to evaluate any + expression permitted byf the following grammar: -#include -#include + expr : query EOS + query : lor + | lor "?" query ":" query + lor : land { "||" land } or OR, for Pascal + land : bor { "&&" bor } or AND, for Pascal + bor : bxor { "|" bxor } + bxor : band { "^" band } + band : eql { "&" eql } + eql : relat { eqrel relat } + relat : shift { rel shift } + shift : primary { shop primary } + primary : term { addop term } + term : unary { mulop unary } + unary : factor + | unop unary + factor : constant + | "(" query ")" + constant: num + | "'" CHAR "'" or '"' CHAR '"' + num : DIGIT full ANSI C syntax + | DIGIT num + shop : "<<" + | ">>" + eqlrel : "=" + | "==" + | "!=" + rel : "<" or <>, Pascal not-equal + | ">" + | "<=" or =<, for Prolog users. + | ">=" + + This expression evaluator was lifted from a public-domain + C Pre-Processor included with the DECUS C Compiler distribution. + It has been hacked somewhat to be suitable for m4. + + 26-Mar-1993 Changed to work in any of EBCDIC, ASCII, DEC MNCS, + or ISO 8859/n. + + 26-Mar-1993 Changed to use "long int" rather than int, so that + we get the same 32-bit arithmetic on a PC as on a Sun. + It isn't fully portable, of course, but then on a 64- + bit machine we _want_ 64-bit arithmetic... + Shifting rewritten (using LONG_BIT) to give signed + shifts even when (long) >> (long) is unsigned. + + 26-Mar-1993 I finally got sick of the fact that &&, ||, and ?: + don't do conditional evaluation. What is the good + of having eval(0&&(1/0)) crash and dump core? Now + every function has a doit? argument. + + 26-Mar-1993 charcon() didn't actually accept 'abcd', which it + should have. Fixed it. + + 20-Apr-1993 eval(1/0) and eval(1%0) dumped core and crashed. + This is also true of the System V r 3.2 m4, but + it isn't good enough for ours! Changed it so that + x % 0 => x as per Concrete Mathematics + x / 0 => error and return 0 from expr(). +*/ -/* - * expression evaluator: performs a standard recursive - * descent parse to evaluate any expression permissible - * within the following grammar: - * - * expr : query EOS - * query : lor - * | lor "?" query ":" query - * lor : land { "||" land } - * land : bor { "&&" bor } - * bor : bxor { "|" bxor } - * bxor : band { "^" band } - * band : eql { "&" eql } - * eql : relat { eqrel relat } - * relat : shift { rel shift } - * shift : primary { shop primary } - * primary : term { addop term } - * term : unary { mulop unary } - * unary : factor - * | unop unary - * factor : constant - * | "(" query ")" - * constant: num - * | "'" CHAR "'" - * num : DIGIT - * | DIGIT num - * shop : "<<" - * | ">>" - * eqlrel : "=" - * | "==" - * | "!=" - * rel : "<" - * | ">" - * | "<=" - * | ">=" - * - * - * This expression evaluator is lifted from a public-domain - * C Pre-Processor included with the DECUS C Compiler distribution. - * It is hacked somewhat to be suitable for m4. - * - * Originally by: Mike Lutz - * Bob Harper - */ - -#define TRUE 1 #define FALSE 0 -#define EOS (char) 0 -#define EQL 0 -#define NEQ 1 -#define LSS 2 -#define LEQ 3 -#define GTR 4 -#define GEQ 5 -#define OCTAL 8 -#define DECIMAL 10 - -static char *nxtch; /* Parser scan pointer */ - -/* - * For longjmp - */ -static jmp_buf expjump; - -/* - * macros: - * - * ungetch - Put back the last character examined. - * getch - return the next character from expr string. - */ -#define ungetch() nxtch-- -#define getch() *nxtch++ - -expr(expbuf) -char *expbuf; -{ - register int rval; - - nxtch = expbuf; - if (setjmp(expjump) != 0) - return (FALSE); - rval = query(); - if (skipws() == EOS) - return(rval); - experr("Ill-formed expression"); -} - -/* - * query : lor | lor '?' query ':' query - * - */ -query() -{ - register int bool, true_val, false_val; - - bool = lor(); - if (skipws() != '?') { - ungetch(); - return(bool); - } - - true_val = query(); - if (skipws() != ':') - experr("Bad query"); - - false_val = query(); - return(bool ? true_val : false_val); -} - -/* - * lor : land { '||' land } - * - */ -lor() -{ - register int c, vl, vr; - - vl = land(); - while ((c = skipws()) == '|' && getch() == '|') { - vr = land(); - vl = vl || vr; - } - - if (c == '|') - ungetch(); - ungetch(); - return(vl); -} - -/* - * land : bor { '&&' bor } - * - */ -land() -{ - register int c, vl, vr; - - vl = bor(); - while ((c = skipws()) == '&' && getch() == '&') { - vr = bor(); - vl = vl && vr; - } - - if (c == '&') - ungetch(); - ungetch(); - return(vl); -} - -/* - * bor : bxor { '|' bxor } - * - */ -bor() -{ - register int vl, vr, c; - - vl = bxor(); - while ((c = skipws()) == '|' && getch() != '|') { - ungetch(); - vr = bxor(); - vl |= vr; - } - - if (c == '|') - ungetch(); - ungetch(); - return(vl); -} - -/* - * bxor : band { '^' band } - * - */ -bxor() -{ - register int vl, vr; - - vl = band(); - while (skipws() == '^') { - vr = band(); - vl ^= vr; - } - - ungetch(); - return(vl); -} - -/* - * band : eql { '&' eql } - * - */ -band() -{ - register int vl, vr, c; - - vl = eql(); - while ((c = skipws()) == '&' && getch() != '&') { - ungetch(); - vr = eql(); - vl &= vr; - } - - if (c == '&') - ungetch(); - ungetch(); - return(vl); -} - -/* - * eql : relat { eqrel relat } - * - */ -eql() -{ - register int vl, vr, rel; - - vl = relat(); - while ((rel = geteql()) != -1) { - vr = relat(); - - switch (rel) { - - case EQL: - vl = (vl == vr); - break; - case NEQ: - vl = (vl != vr); - break; - } - } - return(vl); -} - -/* - * relat : shift { rel shift } - * - */ -relat() -{ - register int vl, vr, rel; - - vl = shift(); - while ((rel = getrel()) != -1) { - - vr = shift(); - switch (rel) { - - case LEQ: - vl = (vl <= vr); - break; - case LSS: - vl = (vl < vr); - break; - case GTR: - vl = (vl > vr); - break; - case GEQ: - vl = (vl >= vr); - break; - } - } - return(vl); -} - -/* - * shift : primary { shop primary } - * - */ -shift() -{ - register int vl, vr, c; - - vl = primary(); - while (((c = skipws()) == '<' || c == '>') && c == getch()) { - vr = primary(); - - if (c == '<') - vl <<= vr; - else - vl >>= vr; - } - - if (c == '<' || c == '>') - ungetch(); - ungetch(); - return(vl); -} - -/* - * primary : term { addop term } - * - */ -primary() -{ - register int c, vl, vr; - - vl = term(); - while ((c = skipws()) == '+' || c == '-') { - vr = term(); - if (c == '+') - vl += vr; - else - vl -= vr; - } - - ungetch(); - return(vl); -} - -/* - * := { } - * - */ -term() -{ - register int c, vl, vr; - - vl = unary(); - while ((c = skipws()) == '*' || c == '/' || c == '%') { - vr = unary(); - - switch (c) { - case '*': - vl *= vr; - break; - case '/': - vl /= vr; - break; - case '%': - vl %= vr; - break; - } - } - ungetch(); - return(vl); -} - -/* - * unary : factor | unop unary - * - */ -unary() -{ - register int val, c; - - if ((c = skipws()) == '!' || c == '~' || c == '-') { - val = unary(); - - switch (c) { - case '!': - return(! val); - case '~': - return(~ val); - case '-': - return(- val); - } - } - - ungetch(); - return(factor()); -} - -/* - * factor : constant | '(' query ')' - * - */ -factor() -{ - register int val; - - if (skipws() == '(') { - val = query(); - if (skipws() != ')') - experr("Bad factor"); - return(val); - } - - ungetch(); - return(constant()); -} - -/* - * constant: num | 'char' - * - */ -constant() -{ - /* - * Note: constant() handles multi-byte constants - */ - - register int i; - register int value; - register char c; - int v[sizeof (int)]; - - if (skipws() != '\'') { - ungetch(); - return(num()); - } - for (i = 0; i < sizeof(int); i++) { - if ((c = getch()) == '\'') { - ungetch(); - break; - } - if (c == '\\') { - switch (c = getch()) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - ungetch(); - c = num(); - break; - case 'n': - c = 012; - break; - case 'r': - c = 015; - break; - case 't': - c = 011; - break; - case 'b': - c = 010; - break; - case 'f': - c = 014; - break; - } - } - v[i] = c; - } - if (i == 0 || getch() != '\'') - experr("Illegal character constant"); - for (value = 0; --i >= 0;) { - value <<= 8; - value += v[i]; - } - return(value); -} - -/* - * num : digit | num digit - * - */ -num() -{ - register int rval, c, base; - int ndig; - - base = ((c = skipws()) == '0') ? OCTAL : DECIMAL; - rval = 0; - ndig = 0; - while (c >= '0' && c <= (base == OCTAL ? '7' : '9')) { - rval *= base; - rval += (c - '0'); - c = getch(); - ndig++; - } - ungetch(); - if (ndig) - return(rval); - experr("Bad constant"); -} - -/* - * eqlrel : '=' | '==' | '!=' - * - */ -geteql() -{ - register int c1, c2; - - c1 = skipws(); - c2 = getch(); - - switch (c1) { - - case '=': - if (c2 != '=') - ungetch(); - return(EQL); - - case '!': - if (c2 == '=') - return(NEQ); - ungetch(); - ungetch(); - return(-1); - - default: - ungetch(); - ungetch(); - return(-1); - } -} - -/* - * rel : '<' | '>' | '<=' | '>=' - * - */ -getrel() -{ - register int c1, c2; - - c1 = skipws(); - c2 = getch(); - - switch (c1) { - - case '<': - if (c2 == '=') - return(LEQ); - ungetch(); - return(LSS); - - case '>': - if (c2 == '=') - return(GEQ); - ungetch(); - return(GTR); - - default: - ungetch(); - ungetch(); - return(-1); - } -} - -/* - * Skip over any white space and return terminating char. - */ -skipws() -{ - register char c; - - while ((c = getch()) <= ' ' && c > EOS) - ; - return(c); -} - -/* - * Error handler - resets environment to eval(), prints an error, - * and returns FALSE. - */ -experr(msg) -char *msg; -{ - printf("mp: %s\n",msg); - longjmp(expjump, -1); /* Force eval() to return FALSE */ -} +#define TRUE 1 + +#include +#include +static jmp_buf expjump; /* Error exit point for expr() */ + +static unsigned char *nxtchr; /* Parser scan pointer */ + +#define deblank0 while ((unsigned)(*nxtchr-1) < ' ') nxtchr++ +#define deblank1 while ((unsigned)(*++nxtchr - 1) < ' ') +#define deblank2 nxtchr++; deblank1 + +#include "ourlims.h" +static char digval[1+UCHAR_MAX]; + +/* This file should work in any C implementation that doesn't have too + many characters to fit in one table. We use a table to convert + (unsigned) characters to numeric codes: + 0 to 9 for '0' to '9' + 10 to 35 for 'a' to 'z' + 10 to 35 for 'A' to 'Z' + 36 for '_' + Instead of asking whether tolower(c) == 'a' we ask whether + digval[c] == DIGIT_A, and so on. This essentially duplicates the + chtype[] table in main.c; we should use just one table. +*/ +#define DIGIT_A 10 +#define DIGIT_B 11 +#define DIGIT_C 12 +#define DIGIT_D 13 +#define DIGIT_E 14 +#define DIGIT_F 15 +#define DIGIT_G 16 +#define DIGIT_H 17 +#define DIGIT_I 18 +#define DIGIT_J 19 +#define DIGIT_K 20 +#define DIGIT_L 21 +#define DIGIT_M 22 +#define DIGIT_N 23 +#define DIGIT_O 24 +#define DIGIT_P 25 +#define DIGIT_Q 26 +#define DIGIT_R 27 +#define DIGIT_S 28 +#define DIGIT_T 29 +#define DIGIT_U 30 +#define DIGIT_V 31 +#define DIGIT_W 32 +#define DIGIT_X 33 +#define DIGIT_Y 34 +#define DIGIT_Z 35 + + +#ifdef __STDC__ +static long int query(int); +#else +static long int query(); +#endif + + +/* experr(msg) + prints an error message, resets environment to expr(), and + forces expr() to return FALSE. +*/ +void experr(msg) + char *msg; + { + (void) fprintf(stderr, "m4: %s\n", msg); + longjmp(expjump, -1); /* Force expr() to return FALSE */ + } + + +/* ::= '0x' | '0X' | '0' | + For ANSI C, an integer may be followed by u, l, ul, or lu, + in any mix of cases. We accept and ignore those letters; + all the numbers are treated as long. +*/ +static long int numcon(doit) + int doit; + { + register long int v; /* current value */ + register int b; /* base (radix) */ + register int c; /* character or digit value */ + + if (!doit) { + do nxtchr++; while (digval[*nxtchr] <= 36); + deblank0; + return 0; + } + + v = digval[*nxtchr++]; /* We already know it's a digit */ + if (v != 0) { + b = 10; /* decimal number */ + } else + if (digval[*nxtchr] == DIGIT_X) { + nxtchr++; + b = 16; /* hexadecimal number */ + } else { + b = 8; /* octal number */ + } + do { + while (digval[c = *nxtchr++] < b) v = v*b + digval[c]; + } while (c == '_'); + while (digval[c] == DIGIT_L || digval[c] == DIGIT_U) c = *nxtchr++; + nxtchr--; /* unread c */ + if ((unsigned)(c-1) < ' ') { deblank1; } + return v; + } + + +/* ::= { } + Note: multibyte constants are accepted. + Note: BEL (\a) and ESC (\e) have the same values in EBCDIC and ASCII. +*/ +static long int charcon(doit) + int doit; + { + register int i; + long int value; + register int c; + int q; + int v[sizeof value]; + + q = *nxtchr++; /* the quote character */ + for (i = 0; ; i++) { + c = *nxtchr++; + if (c == q) { /* end of literal, or doubled quote */ + if (*nxtchr != c) break; + nxtchr++; /* doubled quote stands for one quote */ + } + if (i == sizeof value) experr("Unterminated character constant"); + if (c == '\\') { + switch (c = *nxtchr++) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + c -= '0'; + if ((unsigned)(*nxtchr - '0') < 8) + c = (c << 3) | (*nxtchr++ - '0'); + if ((unsigned)(*nxtchr - '0') < 8) + c = (c << 3) | (*nxtchr++ - '0'); + break; + case 'n': case 'N': c = '\n'; break; + case 'r': case 'R': c = '\r'; break; + case 't': case 'T': c = '\t'; break; + case 'b': case 'B': c = '\b'; break; + case 'f': case 'F': c = '\f'; break; + case 'a': case 'A': c = 007; break; + case 'e': case 'E': c = 033; break; +#if ' ' == 64 + case 'd': case 'D': c = 045; break; /*EBCDIC DEL */ +#else + case 'd': case 'D': c = 127; break; /* ASCII DEL */ +#endif + default : break; + } + } + v[i] = c; + } + deblank0; + if (!doit) return 0; + for (value = 0; --i >= 0; ) value = (value << CHAR_BIT) | v[i]; + return value; + } + + +/* ::= | + ::= '!' || '~' | '-' + ::= '(' ')' | <'> <'> | <"> <"> | +*/ +static long int unary(doit) + int doit; + { + long int v; + + switch (nxtchr[0]) { + case 'n': case 'N': + if (digval[nxtchr[1]] != DIGIT_O + || digval[nxtchr[2]] != DIGIT_T) + experr("Bad 'not'"); + nxtchr += 2; + case '!': deblank1; return !unary(doit); + case '~': deblank1; return ~unary(doit); + case '-': deblank1; return -unary(doit); + case '+': deblank1; return unary(doit); + case '(': deblank1; v = query(doit); + if (nxtchr[0] != ')') experr("Bad factor"); + deblank1; return v; + case '\'': + case '\"': return charcon(doit); + case '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': + case '9': return numcon(doit); + default : experr("Bad constant"); + } + return 0; /*NOTREACHED*/ + } + + +/* ::= { } + ::= '*' | '/' || '%' +*/ +static long int term(doit) + int doit; + { + register long int vl, vr; + + vl = unary(doit); + for (;;) + switch (nxtchr[0]) { + case '*': + deblank1; + vr = unary(doit); + if (doit) vl *= vr; + break; + case 'd': case 'D': + if (digval[nxtchr[1]] != DIGIT_I + || digval[nxtchr[2]] != DIGIT_V) + experr("Bad 'div'"); + nxtchr += 2; + /*FALLTHROUGH*/ + case '/': + deblank1; + vr = unary(doit); + if (doit) { + if (vr == 0) experr("Division by 0"); + vl /= vr; + } + break; + case 'm': case 'M': + if (digval[nxtchr[1]] != DIGIT_O + || digval[nxtchr[2]] != DIGIT_D) + experr("Bad 'mod'"); + nxtchr += 2; + /*FALLTHROUGH*/ + case '%': + deblank1; + vr = unary(doit); + if (doit) { + if (vr != 0) vl %= vr; + } + break; + default: + return vl; + } + /*NOTREACHED*/ + } + +/* ::= { } + ::= '+' | '-' +*/ +static long int primary(doit) + int doit; + { + register long int vl; + + vl = term(doit); + for (;;) + if (nxtchr[0] == '+') { + deblank1; + if (doit) vl += term(doit); else (void)term(doit); + } else + if (nxtchr[0] == '-') { + deblank1; + if (doit) vl -= term(doit); else (void)term(doit); + } else + return vl; + /*NOTREACHED*/ + } + + +/* ::= { } + ::= '<<' | '>>' +*/ +static long int shift(doit) + int doit; + { + register long int vl, vr; + + vl = primary(doit); + for (;;) { + if (nxtchr[0] == '<' && nxtchr[1] == '<') { + deblank2; + vr = primary(doit); + } else + if (nxtchr[0] == '>' && nxtchr[1] == '>') { + deblank2; + vr = -primary(doit); + } else { + return vl; + } + /* The following code implements shifts portably */ + /* Shifts are signed shifts, and the shift count */ + /* acts like repeated one-bit shifts, not modulo anything */ + if (doit) { + if (vr >= LONG_BIT) { + vl = 0; + } else + if (vr <= -LONG_BIT) { + vl = -(vl < 0); + } else + if (vr > 0) { + vl <<= vr; + } else + if (vr < 0) { + vl = (vl >> -vr) | (-(vl < 0) << (LONG_BIT + vr)); + } + } + } + /*NOTREACHED*/ + } + + +/* ::= { } + ::= '<=' | '>=' | '=<' | '=>' | '<' | '>' + Here I rely on the fact that '<<' and '>>' are swallowed by +*/ +static long int relat(doit) + int doit; + { + register long int vl; + + vl = shift(doit); + for (;;) + switch (nxtchr[0]) { + case '=': + switch (nxtchr[1]) { + case '<': /* =<, take as <= */ + deblank2; + vl = vl <= shift(doit); + break; + case '>': /* =>, take as >= */ + deblank2; + vl = vl >= shift(doit); + break; + default: /* == or =; OOPS */ + return vl; + } + break; + case '<': + if (nxtchr[1] == '=') { /* <= */ + deblank2; + vl = vl <= shift(doit); + } else + if (nxtchr[1] == '>') { /* <> (Pascal) */ + deblank2; + vl = vl != shift(doit); + } else { /* < */ + deblank1; + vl = vl < shift(doit); + } + break; + case '>': + if (nxtchr[1] == '=') { /* >= */ + deblank2; + vl = vl >= shift(doit); + } else { /* > */ + deblank1; + vl = vl > shift(doit); + } + break; + default: + return vl; + } + /*NOTREACHED*/ + } + + +/* ::= { } + ::= '!=' | '==' | '=' +*/ +static long int eql(doit) + int doit; + { + register long int vl; + + vl = relat(doit); + for (;;) + if (nxtchr[0] == '!' && nxtchr[1] == '=') { + deblank2; + vl = vl != relat(doit); + } else + if (nxtchr[0] == '=' && nxtchr[1] == '=') { + deblank2; + vl = vl == relat(doit); + } else + if (nxtchr[0] == '=') { + deblank1; + vl = vl == relat(doit); + } else + return vl; + /*NOTREACHED*/ + } + + +/* ::= { '&' } +*/ +static long int band(doit) + int doit; + { + register long int vl; + + vl = eql(doit); + while (nxtchr[0] == '&' && nxtchr[1] != '&') { + deblank1; + if (doit) vl &= eql(doit); else (void)eql(doit); + } + return vl; + } + + +/* ::= { '^' } +*/ +static long int bxor(doit) + int doit; + { + register long int vl; + + vl = band(doit); + while (nxtchr[0] == '^') { + deblank1; + if (doit) vl ^= band(doit); else (void)band(doit); + } + return vl; + } + + +/* ::= { '|' } +*/ +static long int bor(doit) + int doit; + { + register long int vl; + + vl = bxor(doit); + while (nxtchr[0] == '|' && nxtchr[1] != '|') { + deblank1; + if (doit) vl |= bxor(doit); else (void)bxor(doit); + } + return vl; + } + + +/* ::= { '&&' } +*/ +static long int land(doit) + int doit; + { + register long int vl; + + vl = bor(doit); + for (;;) { + if (nxtchr[0] == '&') { + if (nxtchr[1] != '&') break; + deblank2; + } else + if (digval[nxtchr[0]] == DIGIT_A) { + if (digval[nxtchr[1]] != DIGIT_N) break; + if (digval[nxtchr[2]] != DIGIT_D) break; + nxtchr += 2; deblank1; + } else { + /* neither && nor and */ + break; + } + vl = bor(doit && vl) != 0; + } + return vl; + } + + +/* ::= { '||' } +*/ +static long int lor(doit) + int doit; + { + register long int vl; + + vl = land(doit); + for (;;) { + if (nxtchr[0] == '|') { + if (nxtchr[1] != '|') break; + } else + if (digval[nxtchr[0]] == DIGIT_O) { + if (digval[nxtchr[1]] != DIGIT_R) break; + } else { + /* neither || nor or */ + break; + } + deblank2; + vl = land(doit && !vl) != 0; + } + return vl; + } + + +/* ::= [ '?' ':' ] +*/ +static long int query(doit) + int doit; + { + register long int bool, true_val, false_val; + + bool = lor(doit); + if (*nxtchr != '?') return bool; + deblank1; + true_val = query(doit && bool); + if (*nxtchr != ':') experr("Bad query"); + deblank1; + false_val = query(doit && !bool); + return bool ? true_val : false_val; + } + + +static void initialise_digval() + { + register unsigned char *s; + register int c; + + for (c = 0; c <= UCHAR_MAX; c++) digval[c] = 99; + for (c = 0, s = (unsigned char *)"0123456789"; + /*while*/ *s; + /*doing*/ digval[*s++] = c++) /* skip */; + for (c = 10, s = (unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + /*while*/ *s; + /*doing*/ digval[*s++] = c++) /* skip */; + for (c = 10, s = (unsigned char *)"abcdefghijklmnopqrstuvwxyz"; + /*while*/ *s; + /*doing*/ digval[*s++] = c++) /* skip */; + digval['_'] = 36; + } + + +long int expr(expbuf) + char *expbuf; + { + register int rval; + + if (digval['1'] == 0) initialise_digval(); + nxtchr = (unsigned char *)expbuf; + deblank0; + if (setjmp(expjump) != 0) return FALSE; + rval = query(TRUE); + if (*nxtchr) experr("Ill-formed expression"); + return rval; + } + diff --git a/usr.bin/m4/extr.h b/usr.bin/m4/extr.h index b509a4a8544e..8b439ca0829a 100644 --- a/usr.bin/m4/extr.h +++ b/usr.bin/m4/extr.h @@ -1,59 +1,160 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Ozan Yigit. - * - * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)extr.h 5.2 (Berkeley) 6/1/90 - */ +/* Header : extr.h + Author : Ozan Yigit + Updated: %G% +*/ +#ifndef putback extern ndptr hashtab[]; /* hash table for macros etc. */ -extern char buf[]; /* push-back buffer */ +extern char buf[]; /* push-back buffer */ extern char *bp; /* first available character */ +extern char *bb; /* current beginning of bp */ extern char *endpbb; /* end of push-back buffer */ -extern stae mstack[]; /* stack of m4 machine */ +extern stae mstack[]; /* stack of m4 machine */ extern char *ep; /* first free char in strspace */ extern char *endest; /* end of string space */ -int sp; /* current m4 stack pointer */ -int fp; /* m4 call frame pointer */ +extern int sp; /* current m4 stack pointer */ +extern int fp; /* m4 call frame pointer */ +extern char *bbstack[]; extern FILE *infile[]; /* input file stack (0=stdin) */ extern FILE *outfile[]; /* diversion array(0=bitbucket)*/ extern FILE *active; /* active output file pointer */ extern char *m4temp; /* filename for diversions */ -extern int ilevel; /* input file stack pointer */ -extern int oindex; /* diversion index.. */ +extern int UNIQUE; /* where to change m4temp */ +extern int ilevel; /* input file stack pointer */ +extern int oindex; /* diversion index.. */ extern char *null; /* as it says.. just a null.. */ extern char *m4wraps; /* m4wrap string default.. */ -extern char lquote; /* left quote character (`) */ -extern char rquote; /* right quote character (') */ -extern char scommt; /* start character for comment */ -extern char ecommt; /* end character for comment */ +extern char lquote; /* left quote character (`) */ +extern char rquote; /* right quote character (') */ +extern char vquote; /* verbatim quote character ^V */ +extern char scommt; /* start character for comment */ +extern char ecommt; /* end character for comment */ + +/* inlined versions of chrsave() and putback() */ + +extern char pbmsg[]; /* error message for putback */ +extern char csmsg[]; /* error message for chrsave */ + +#define putback(c) do { if (bp >= endpbb) error(pbmsg); *bp++ = c; } while (0) +#define chrsave(c) do { if (ep >= endest) error(csmsg); *ep++ = c; } while (0) + +/* getopt() interface */ + +extern char * optarg; +extern int optind; +#ifdef __STDC__ +extern int getopt(int, char **, char *); +#else +extern int getopt(); +#endif + +#ifdef __STDC__ +#include + +/* functions from misc.c */ + +extern char * strsave(char *); +extern int indx(char *, char *); +extern void pbstr(char *); +extern void pbqtd(char *); +extern void pbnum(int); +extern void pbrad(long int, int, int); +extern void getdiv(int); +extern void killdiv(); +extern void error(char *); +extern void onintr(int); +extern void usage(); + +/* functions from look.c */ + +extern ndptr lookup(char *); +extern ndptr addent(char *); +extern void remhash(char *, int); +extern void addkywd(char *, int); + +/* functions from int2str.c */ + +extern char* int2str(/* char*, int, long */); + +/* functions from serv.c */ + +extern void expand(char **, int); +extern void dodefine(char *, char *); +extern void dopushdef(char *, char *); +extern void dodefn(char *); +extern void dodump(char **, int); +extern void doifelse(char **, int); +extern int doincl(char *); +extern void dochq(char **, int); +extern void dochc(char **, int); +extern void dodiv(int); +extern void doundiv(char **, int); +extern void dosub(char **, int); +extern void map(char *, char *, char *, char *); +#ifdef EXTENDED +extern int dopaste(char *); +extern void m4trim(char **, int); +extern void dodefqt(char **, int); +extern void doqutr(char **, int); +#endif + +/* functions from expr.c */ + +extern long expr(char *); + +#else + +/* functions from misc.c */ + +extern char * malloc(); +extern char * strsave(); +extern int indx(); +extern void pbstr(); +extern void pbqtd(); +extern void pbnum(); +extern void pbrad(); +extern void getdiv(); +extern void killdiv(); +extern void error(); +extern int onintr(); +extern void usage(); + +/* functions from look.c */ + +extern ndptr lookup(); +extern ndptr addent(); +extern void remhash(); +extern void addkywd(); + +/* functions from int2str.c */ + +extern char* int2str(/* char*, int, long */); + +/* functions from serv.c */ + +extern void expand(); +extern void dodefine(); +extern void dopushdef(); +extern void dodefn(); +extern void dodump(); +extern void doifelse(); +extern int doincl(); +extern void dochq(); +extern void dochc(); +extern void dodiv(); +extern void doundiv(); +extern void dosub(); +extern void map(); +#ifdef EXTENDED +extern int dopaste(); +extern void m4trim(); +extern void dodefqt(); +extern void doqutr(); +#endif + +/* functions from expr.c */ + +extern long expr(); + +#endif +#endif diff --git a/usr.bin/m4/int2str.c b/usr.bin/m4/int2str.c new file mode 100644 index 000000000000..5ecc85b9ad99 --- /dev/null +++ b/usr.bin/m4/int2str.c @@ -0,0 +1,58 @@ +/* File : int2str.c + Author : Richard A. O'Keefe + Updated: 6 February 1993 + Defines: int2str() + + int2str(dst, radix, val) + converts the (long) integer "val" to character form and moves it to + the destination string "dst" followed by a terminating NUL. The + result is normally a pointer to this NUL character, but if the radix + is dud the result will be NullS and nothing will be changed. + + If radix is -2..-36, val is taken to be SIGNED. + If radix is 2.. 36, val is taken to be UNSIGNED. + That is, val is signed if and only if radix is. You will normally + use radix -10 only through itoa and ltoa, for radix 2, 8, or 16 + unsigned is what you generally want. +*/ + +static char dig_vec[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + +char *int2str(dst, radix, val) + register char *dst; + register int radix; + register long val; + { + char buffer[65]; /* Ready for 64-bit machines */ + register char *p; + + if (radix < 2 || radix > 36) { /* Not 2..36 */ + if (radix > -2 || radix < -36) return (char *)0; + if (val < 0) { + *dst++ = '-'; + val = -val; + } + radix = -radix; + } + /* The slightly contorted code which follows is due to the + fact that few machines directly support unsigned long / and %. + Certainly the VAX C compiler generates a subroutine call. In + the interests of efficiency (hollow laugh) I let this happen + for the first digit only; after that "val" will be in range so + that signed integer division will do. Sorry 'bout that. + CHECK THE CODE PRODUCED BY YOUR C COMPILER. The first % and / + should be unsigned, the second % and / signed, but C compilers + tend to be extraordinarily sensitive to minor details of style. + This works on a VAX, that's all I claim for it. + */ + p = &buffer[sizeof buffer]; + *--p = '\0'; + *--p = dig_vec[(unsigned long)val%(unsigned long)radix]; + val = (unsigned long)val/(unsigned long)radix; + while (val != 0) *--p = dig_vec[val%radix], val /= radix; + while (*dst++ = *p++) ; + return dst-1; + } + diff --git a/usr.bin/m4/look.c b/usr.bin/m4/look.c index 1f95d20ab8ea..965e5901909a 100644 --- a/usr.bin/m4/look.c +++ b/usr.bin/m4/look.c @@ -1,153 +1,107 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Ozan Yigit. - * - * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ +/* File : look.c + Author : Ozan Yigit + Updated: 4 May 1992 + Purpose: Hash table for M4 +*/ -#ifndef lint -static char sccsid[] = "@(#)look.c 5.3 (Berkeley) 2/26/91"; -#endif /* not lint */ - -/* - * look.c - * Facility: m4 macro processor - * by: oz - */ - -#include -#include -#include #include "mdef.h" #include "extr.h" -/* - * hash - compute hash value using the proverbial - * hashing function. Taken from K&R. - */ -hash (name) -register char *name; -{ - register int h = 0; - while (*name) - h += *name++; - return (h % HASHSIZE); -} +ndptr hashtab[HASHSIZE]; + /* - * lookup - find name in the hash table - * + * hash - get a hash value for string s */ -ndptr lookup(name) +int +hash(name) char *name; { + register unsigned long h = 0; + + while (*name) + h = (h << 5) + h + *name++; + + return h % HASHSIZE; +} + +/* + * lookup(name) - find name in the hash table + */ +ndptr lookup(name) + char *name; + { register ndptr p; for (p = hashtab[hash(name)]; p != nil; p = p->nxtptr) - if (strcmp(name, p->name) == 0) - break; - return (p); -} + if (strcmp(name, p->name) == 0) + break; + return p; + } -/* - * addent - hash and create an entry in the hash - * table. The new entry is added in front - * of a hash bucket. +/* + * addent(name) - hash and create an entry in the hash table. + * The new entry is added at the front of a hash bucket. + * BEWARE: the type and defn fields are UNDEFINED. */ ndptr addent(name) -char *name; -{ - register int h; - ndptr p; + char *name; + { + register ndptr p, *h; - h = hash(name); - if ((p = (ndptr) malloc(sizeof(struct ndblock))) != NULL) { - p->nxtptr = hashtab[h]; - hashtab[h] = p; - p->name = strdup(name); - } - else - error("m4: no more memory."); + p = (ndptr)malloc(sizeof *p); + if (p == NULL) error("m4: no more memory."); + h = &hashtab[hash(name)]; + p->name = strsave(name); + p->defn = null; + p->nxtptr = *h; + *h = p; return p; -} + } -/* - * remhash - remove an entry from the hashtable - * + +/* + * addkywd(name, type) - stores a keyword in the hash table. */ -remhash(name, all) -char *name; -int all; -{ - register int h; - register ndptr xp, tp, mp; +void addkywd(name, type) + char *name; + int type; + { + register ndptr p = addent(name); + p->type = type | STATIC; + } - h = hash(name); - mp = hashtab[h]; - tp = nil; - while (mp != nil) { - if (strcmp(mp->name, name) == 0) { - mp = mp->nxtptr; - if (tp == nil) { - freent(hashtab[h]); - hashtab[h] = mp; - } - else { - xp = tp->nxtptr; - tp->nxtptr = mp; - freent(xp); - } - if (!all) - break; - } - else { - tp = mp; - mp = mp->nxtptr; - } - } -} -/* - * freent - free a hashtable information block - * +/* + * remhash(name, all) + * remove one entry (all==0) or all entries (all!=0) for a given name + * from the hash table. All hash table entries must have been obtained + * from malloc(), so it is safe to free the records themselves. + * However, the ->name and ->defn fields might point to storage which + * was obtained from strsave() -- in which case they may be freed -- or + * to static storage -- in which case they must not be freed. If the + * STATIC bit is set, the fields are not to be freed. */ -freent(p) -ndptr p; -{ - if (!(p->type & STATIC)) { - free(p->name); - if (p->defn != null) - free(p->defn); - } - free(p); -} +void remhash(name, all) + char *name; + int all; + { + register ndptr p, *h; + /* h always points to the pointer to p */ + + h = &hashtab[hash(name)]; + while ((p = *h) != nil) { + if (strcmp(p->name, name) == 0) { + *h = p->nxtptr; /* delink this record */ + if (!(p->type & STATIC)) { /* free the name and defn */ + free(p->name); /* if they came from strsave */ + if (p->defn != null) free(p->defn); + } /* otherwise leave them */ + free(p); /* free the record itself */ + if (!all) return; /* first occurrence has gone */ + } else { + h = &(p->nxtptr); + } + } + } diff --git a/usr.bin/m4/m4.1 b/usr.bin/m4/m4.1 new file mode 100644 index 000000000000..0cb5c59095d9 --- /dev/null +++ b/usr.bin/m4/m4.1 @@ -0,0 +1,179 @@ +.\" +.\" @(#) $Id: m4.1,v 1.1 1993/06/18 21:50:31 glass Exp $ +.\" +.Dd January 26, 1993 +.Dt m4 1 +.Os +.Sh NAME +.Nm m4 +.Nd macro language processor +.Sh SYNOPSIS +.Nm m4 +.Oo +.Fl D Ns Ar name Ns Op Ar =value +.Oc +.Op Fl U Ns Ar name +.Sh DESCRIPTION +The +.Nm m4 +utility is a macro processor that can be used as a front end to any +language (e.g., C, ratfor, fortran, lex, and yacc). +.Nm m4 +reads from the standard input and writes +the processed text to the standard output. +.Pp +Macro calls have the form name(argument1[, argument2, ...,] argumentN). +.Pp +There cannot be any space following the macro name and the open +parentheses '('. If the macro name is not followed by an open +parentheses it is processed with no arguments. +.Pp +Macro names consist of a leading alphabetic or underscore +possibly followed by alphanumeric or underscore characters, therefore +valid macro names match this pattern [a-zA-Z_][a-zA-Z0-9_]*. +.Pp +In arguments to macros, leading unquoted space, tab and newline +characters are ignored. To quote strings use left and right single +quotes (e.g., ` this is a string with a leading space'). You can change +the quote characters with the changequote built-in macro. +.Pp +The options are as follows: +.Bl -tag -width "-Dname[=value]xxx" +.It Fl D Ns Ar name Ns Oo +.Ar =value +.Oc +Define the symbol +.Ar name +to have some value (or NULL). +.It Fl "U" Ns Ar "name" +Undefine the symbol +.Ar name . +.El +.Sh SYNTAX +.Nm m4 +provides the following built-in macros. They may be +redefined, loosing their original meaning. +Return values are NULL unless otherwise stated. +.Bl -tag -width changequotexxx +.It changecom +Change the start and end comment sequences. The default is +the pound sign `#' and the newline character. With no arguments +comments are turned off. The maximum length for a comment marker is +five characters. +.It changequote +Defines the quote symbols to be the first and second arguments. +The symbols may be up to five characters long. If no arguments are +given it restores the default open and close single quotes. +.It decr +Decrements the argument by 1. The argument must be a valid numeric string. +.It define +Define a new macro named by the first argument to have the +value of the second argument. Each occurrence of $n (where n +is 0 through 9) is replaced by the n'th argument. $0 is the name +of the calling macro. Undefined arguments are replaced by a +NULL string. $# is replaced by the number of arguments; $* +is replaced by all arguments comma separated; $@ is the same +as $* but all arguments are quoted against further expansion. +.It defn +Returns the quoted definition for each argument. This can be used to rename +macro definitions (even for built-in macros). +.It divert +There are 10 output queues (numbered 0-9). +At the end of processing +.Nm m4 +concatenates all the queues in numerical order to produce the +final output. Initially the output queue is 0. The divert +macro allows you to select a new output queue (an invalid argument +passed to divert causes output to be discarded). +.It divnum +Returns the current output queue number. +.It dnl +Discard input characters up to and including the next newline. +.It dumpdef +Prints the names and definitions for the named items, or for everything +if no arguments are passed. +.It errprint +Prints the first argument on the standard error output stream. +.It eval +Computes the first argument as an arithmetic expression using 32-bit +arithmetic. Operators are the standard C ternary, arithmetic, logical, +shift, relational, bitwise, and parentheses operators. You can specify +octal, decimal, and hexadecimal numbers as in C. The second argument (if +any) specifies the radix for the result and the third argument (if +any) specifies the minimum number of digits in the result. +.It expr +This is an alias for eval. +.It ifdef +If the macro named by the first argument is defined then return the second +argument, otherwise the third. If there is no third argument, +the value is NULL. The word `unix' is predefined. +.It ifelse +If the first argument matches the second argument then ifelse returns +the third argument. If the match fails the three arguments are +discarded and the next three arguments are used until there is +zero or one arguments left, either this last argument or NULL is +returned if no other matches were found. +.It include +Returns the contents of the file specified in the first argument. +Include aborts with an error message if the file cannot be included. +.It incr +Increments the argument by 1. The argument must be a valid numeric string. +.It index +Returns the index of the second argument in the first argument (e.g., +index(the quick brown fox jumped, fox) returns 16). If the second +argument is not found index returns -1. +.It len +Returns the number of characters in the first argument. Extra arguments +are ignored. +.It m4exit +Immediately exits with the return value specified by the first argument, +0 if none. +.It m4wrap +Allows you to define what happens at the final EOF, usually for cleanup +purposes (e.g., m4wrap("cleanup(tempfile)") causes the macro cleanup to +invoked after all other processing is done.) +.It maketemp +Translates the string XXXXX in the first argument with the current process +ID leaving other characters alone. This can be used to create unique +temporary file names. +.It paste +Includes the contents of the file specified by the first argument without +any macro processing. Aborts with an error message if the file cannot be +included. +.It popdef +Restores the pushdef'ed definition for each argument. +.It pushdef +Takes the same arguments as define, but it saves the definition on a +stack for later retrieval by popdef. +.It shift +Returns all but the first argument, the remaining arguments are +quoted and pushed back with commas in between. The quoting +nullifies the effect of the extra scan that will subsequently be +performed. +.It sinclude +Similar to include, except it ignores any errors. +.It spaste +Similar to spaste, except it ignores any errors. +.It substr +Returns a substring of the first argument starting at the offset specified +by the second argument and the length specified by the third argument. +If no third argument is present it returns the rest of the string. +.It syscmd +Passes the first argument to the shell. Nothing is returned. +.It sysval +Returns the return value from the last syscmd. +.It translit +Transliterate the characters in the first argument from the set +given by the second argument to the set given by the third. You cannot +use +.Xr tr 1 +style abbreviations. +.It undefine +Removes the definition for the macro specified by the first argument. +.It undivert +Flushes the named output queues (or all queues if no arguments). +.It unix +A pre-defined macro for testing the OS platform. +.El +.Sh AUTHOR +Ozan Yigit and Richard A. O'Keefe (ok@goanna.cs.rmit.OZ.AU) diff --git a/usr.bin/m4/main.c b/usr.bin/m4/main.c index 4326b07bf9cd..2fe9ae1331aa 100644 --- a/usr.bin/m4/main.c +++ b/usr.bin/m4/main.c @@ -1,56 +1,18 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Ozan Yigit. - * - * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ +/* File : main.c + Author : Ozan Yigit + Updated: 4 May 1992 + Defines: M4 macro processor. +*/ -#ifndef lint -static char sccsid[] = "@(#)main.c 5.6 (Berkeley) 3/6/91"; -#endif /* not lint */ - -/* - * main.c - * Facility: m4 macro processor - * by: oz - */ - -#include -#include -#include -#include -#include #include "mdef.h" -#include "pathnames.h" +#include "extr.h" +#include "ourlims.h" + +char chtype[1 - EOF + UCHAR_MAX]; + +#define is_sym1(c) (chtype[(c)-EOF] > 10) +#define is_sym2(c) (chtype[(c)-EOF] > 0) +#define is_blnk(c) ((unsigned)((c)-1) < ' ') /* * m4 - macro processor @@ -83,11 +45,105 @@ static char sccsid[] = "@(#)main.c 5.6 (Berkeley) 3/6/91"; * * Modification History: * + * Mar 26 1992 RAOK 1. Eliminated magic numbers 8, 255, 256 in favour + * of the standard limits CHAR_BIT, UCHAR_MAX, which + * are in the new header ourlims.h. This is part of + * the "8-bit-clean M4" project. To the best of my + * belief, all of the code should work in EBCDIC, + * ASCII, DEC MNCS, ISO 8859/n, or the Mac character + * set, as long as chars are unsigned. There are + * still some places where signed bytes can cause + * trouble. + * + * 2. Changed expr() to use long int rather than int. + * This is so that we'd get 32-bit arithmetic on a Sun, + * Encore, PC, Mac &c. As part of this, the code for + * shifts has been elaborated to yield signed shifts + * on all machines. The charcon() function didn't work + * with multi-character literals, although it was meant + * to. Now it does. pbrad() has been changed so that + * eval('abcd',0) => abcd, not dcba, which was useless. + * + * 3. I finally got sick of the fact that &&, ||, and + * ?: always evaluate all their arguments. This is + * consistent with UNIX System V Release 3, but I for + * one don't see anything to gain by having eval(0&&1/0) + * crash when it would simply yield 0 in C. Now these + * operators are more consistent with the C preprocessor. + * + * Nov 13 1992 RAOK Added the quoter facility. The purpose of this is + * to make it easier to generate data for a variety of + * programming languages, including sh, awk, Lisp, C. + * There are two holes in the implementation: dumpdef + * prints junk and undefine doesn't release everything. + * This was mainly intended as a prototype to show that + * it could be done. + * + * Jun 16 1992 RAOK Added vquote and gave changequote a 3rd argument. + * The idea of this is to make it possible to quote + * ANY string, including one with unbalanced ` or '. + * I also made eval(c,0) convert decimal->ASCII, so + * that eval(39,0) yields ' and eval(96,0) yields `. + * + * Apr 28 1992 RAOK Used gcc to find and fix ANSI clashes, so that + * PD M4 could be ported to MS-DOS (Turbo C 3). + * Main known remaining problem: use of mktemp(). + * Also, command line handling needs to be worked out. + * + * Mar 26 1992 RAOK PD M4 now accepts file names on the command line + * just like UNIX M4. Warning: macro calls must NOT + * cross file boundaries. UNIX M4 doesn't mind; + * (m4 a b c) and (cat a b c | m4) are just the same + * except for error messages. PD M4 will report an + * unexpected EOF if a file ends while a macro call or + * string is still being parsed. When there is one + * file name argument, or none, you can't tell the + * difference, and that's all I need. + * + * May 15 1991 RAOK DIVNAM was a string constant, but was changed! + * Fixed that and a couple of other things to make + * GCC happy. (Also made "foo$bar" get through.) + * + * Apr 17 1991 RAOK There was a major mistake. If you did + * define(foo, `1 include(bar) 2') where + * file bar held "-bar-" you would naturally + * expect "1 -bar- 2" as the output, but you + * got "1 2-bar-". That is, include file + * processing was postponed until all macros + * had been expanded. The macro gpbc() was + * at fault. I added bb, bbstack[], and the + * code in main.c and serv.c that maintains + * them, in order to work around this bug. + * + * Apr 12 1991 RAOK inspect() didn't handle overflow well. + * Added the automatically maintained macro + * __FILE__, just as in C. To suppress it, + * define NO__FILE. At some point, $# had + * been made to return a value that was off + * by one; it now agrees with SysV M4. + * + * Aug 13 1990 RAOK The System V expr() has three arguments: + * expression [, radix:10 [, mindigits: 1]] + * Brought in my int2str() and wrote pbrad() + * to make this work here. With the wrong # + * of args, acts like System V. + * + * Aug 11 1990 RAOK Told expr.c about the Pascal operators + * not, div, mod, and, or + * so that Pascal constant expressions could + * be evaluated. (It still doesn't handle + * floats.) Fixed a mistake in 'character's. + * + * Apr 23 1988 RAOK Sped it up, mainly by making putback() and + * chrsave() into macros. + * Finished the -o option (was half done). + * Added the System V -e (interactive) option. + * * Jan 28 1986 Oz Break the whole thing into little * pieces, for easier (?) maintenance. * * Dec 12 1985 Oz Optimize the code, try to squeeze - * few microseconds out.. + * few microseconds out.. [didn't try very hard] * * Dec 05 1985 Oz Add getopt interface, define (-D), * undefine (-U) options. @@ -141,9 +197,9 @@ static char sccsid[] = "@(#)main.c 5.6 (Berkeley) 3/6/91"; * */ -ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */ char buf[BUFSIZE]; /* push-back buffer */ char *bp = buf; /* first available character */ +char *bb = buf; /* buffer beginning */ char *endpbb = buf+BUFSIZE; /* end of push-back buffer */ stae mstack[STACKMAX+1]; /* stack of m4 machine */ char strspace[STRSPMAX+1]; /* string space for evaluation */ @@ -151,335 +207,691 @@ char *ep = strspace; /* first free char in strspace */ char *endest= strspace+STRSPMAX;/* end of string space */ int sp; /* current m4 stack pointer */ int fp; /* m4 call frame pointer */ +char *bbstack[MAXINP]; /* stack where bb is saved */ FILE *infile[MAXINP]; /* input file stack (0=stdin) */ FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/ FILE *active; /* active output file pointer */ -char *m4temp; /* filename for diversions */ int ilevel = 0; /* input file stack pointer */ int oindex = 0; /* diversion index.. */ char *null = ""; /* as it says.. just a null.. */ char *m4wraps = ""; /* m4wrap string default.. */ char lquote = LQUOTE; /* left quote character (`) */ char rquote = RQUOTE; /* right quote character (') */ +char vquote = VQUOTE; /* verbatim quote character ^V */ char scommt = SCOMMT; /* start character for comment */ char ecommt = ECOMMT; /* end character for comment */ -struct keyblk keywrds[] = { /* m4 keywords to be installed */ - "include", INCLTYPE, - "sinclude", SINCTYPE, - "define", DEFITYPE, - "defn", DEFNTYPE, - "divert", DIVRTYPE, - "expr", EXPRTYPE, - "eval", EXPRTYPE, - "substr", SUBSTYPE, - "ifelse", IFELTYPE, - "ifdef", IFDFTYPE, - "len", LENGTYPE, - "incr", INCRTYPE, - "decr", DECRTYPE, - "dnl", DNLNTYPE, - "changequote", CHNQTYPE, - "changecom", CHNCTYPE, - "index", INDXTYPE, +int strip = 0; /* throw away comments? */ + +/* Definitions of diversion files. The last 6 characters MUST be + "XXXXXX" -- that is a requirement of mktemp(). The character + '0' is to be replaced by the diversion number; we assume here + that it is just before the Xs. If not, you will have to alter + the definition of UNIQUE. +*/ + +#if unix +static char DIVNAM[] = "/usr/tmp/m40XXXXXX"; +#else +#if vms +static char DIVNAM[] = "sys$login:m40XXXXXX"; +#else +static char DIVNAM[] = "M40XXXXXX"; /* was \M4, should it be \\M4? */ +#endif +#endif +int UNIQUE = sizeof DIVNAM - 7; /* where to change m4temp. */ +char *m4temp; /* filename for diversions */ +extern char *mktemp(); + + +void cantread(s) + char *s; + { + fprintf(stderr, "m4: %s: ", s); + error("cannot open for input."); + } + + +/* initkwds() + initialises the hash table to contain all the m4 built-in functions. + The original version breached module boundaries, but there did not + seem to be any benefit in that. +*/ +static void initkwds() + { + register int i; + static struct { char *name; int type; } keyword[] = + { + "include", INCLTYPE, + "sinclude", SINCTYPE, + "define", DEFITYPE, + "defn", DEFNTYPE, + "divert", DIVRTYPE, + "expr", EXPRTYPE, + "eval", EXPRTYPE, + "substr", SUBSTYPE, + "ifelse", IFELTYPE, + "ifdef", IFDFTYPE, + "len", LENGTYPE, + "incr", INCRTYPE, + "decr", DECRTYPE, + "dnl", DNLNTYPE, + "changequote", CHNQTYPE, + "changecom", CHNCTYPE, + "index", INDXTYPE, #ifdef EXTENDED - "paste", PASTTYPE, - "spaste", SPASTYPE, + "paste", PASTTYPE, + "spaste", SPASTYPE, + "m4trim", TRIMTYPE, + "defquote", DEFQTYPE, #endif - "popdef", POPDTYPE, - "pushdef", PUSDTYPE, - "dumpdef", DUMPTYPE, - "shift", SHIFTYPE, - "translit", TRNLTYPE, - "undefine", UNDFTYPE, - "undivert", UNDVTYPE, - "divnum", DIVNTYPE, - "maketemp", MKTMTYPE, - "errprint", ERRPTYPE, - "m4wrap", M4WRTYPE, - "m4exit", EXITTYPE, - "syscmd", SYSCTYPE, - "sysval", SYSVTYPE, - "unix", MACRTYPE, -}; + "popdef", POPDTYPE, + "pushdef", PUSDTYPE, + "dumpdef", DUMPTYPE, + "shift", SHIFTYPE, + "translit", TRNLTYPE, + "undefine", UNDFTYPE, + "undivert", UNDVTYPE, + "divnum", DIVNTYPE, + "maketemp", MKTMTYPE, + "errprint", ERRPTYPE, + "m4wrap", M4WRTYPE, + "m4exit", EXITTYPE, +#if unix || vms + "syscmd", SYSCTYPE, + "sysval", SYSVTYPE, +#endif +#if unix + "unix", MACRTYPE, +#else +#if vms + "vms", MACRTYPE, +#endif +#endif + (char*)0, 0 + }; -#define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk)) + for (i = 0; keyword[i].type != 0; i++) + addkywd(keyword[i].name, keyword[i].type); + } -extern ndptr lookup(); -extern ndptr addent(); -extern void onintr(); -extern int optind; -extern char *optarg; - -main(argc,argv) - int argc; - char **argv; -{ +/* inspect(Name) + Build an input token.., considering only those which start with + [A-Za-z_]. This is fused with lookup() to speed things up. + name must point to an array of at least MAXTOK characters. +*/ +ndptr inspect(name) + char *name; + { + register char *tp = name; + register char *etp = name+(MAXTOK-1); register int c; - register int n; - char *p; + register unsigned long h = 0; + register ndptr p; - if (signal(SIGINT, SIG_IGN) != SIG_IGN) - signal(SIGINT, onintr); -#ifdef NONZEROPAGES - initm4(); -#endif - initkwds(); - - while ((c = getopt(argc, argv, "tD:U:o:")) != EOF) - switch(c) { - - case 'D': /* define something..*/ - for (p = optarg; *p; p++) - if (*p == '=') - break; - if (*p) - *p++ = EOS; - dodefine(optarg, p); - break; - case 'U': /* undefine... */ - remhash(optarg, TOP); - break; - case 'o': /* specific output */ - case '?': - default: - usage(); - } - - infile[0] = stdin; /* default input (naturally) */ - active = stdout; /* default active output */ - m4temp = mktemp(strdup(DIVNAM));/* filename for diversions */ - - sp = -1; /* stack pointer initialized */ - fp = 0; /* frame pointer initialized */ - - macro(); /* get some work done here */ - - if (*m4wraps) { /* anything for rundown ?? */ - ilevel = 0; /* in case m4wrap includes.. */ - putback(EOF); /* eof is a must !! */ - pbstr(m4wraps); /* user-defined wrapup act */ - macro(); /* last will and testament */ + while (is_sym2(c = gpbc())) { + if (tp == etp) error("m4: token too long"); + *tp++ = c, h = (h << 5) + h + c; } + putback(c); + *tp = EOS; + for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr) + if (strcmp(name, p->name) == 0) + return p; + return nil; + } - if (active != stdout) - active = stdout; /* reset output just in case */ - for (n = 1; n < MAXOUT; n++) /* default wrap-up: undivert */ - if (outfile[n] != NULL) - getdiv(n); - /* remove bitbucket if used */ - if (outfile[0] != NULL) { - (void) fclose(outfile[0]); - m4temp[UNIQUE] = '0'; - (void) unlink(m4temp); - } - - exit(0); -} - -ndptr inspect(); /* forward ... */ /* * macro - the work horse.. * */ -macro() { +void macro() + { char token[MAXTOK]; - register char *s; - register int t, l; - register ndptr p; - register int nlpar; + register int t; + register FILE *op = active; + static char ovmsg[] = "m4: internal stack overflow"; - cycle { - if ((t = gpbc()) == '_' || isalpha(t)) { + for (;;) { + t = gpbc(); + if (is_sym1(t)) { + register char *s; + register ndptr p; + + putback(t); + if ((p = inspect(s = token)) == nil) { + if (sp < 0) { + while (t = *s++) putc(t, op); + } else { + while (t = *s++) chrsave(t); + } + } else { + /* real thing.. First build a call frame */ + if (sp >= STACKMAX-6) error(ovmsg); + mstack[1+sp].sfra = fp; /* previous call frm */ + mstack[2+sp].sfra = p->type; /* type of the call */ + mstack[3+sp].sfra = 0; /* parenthesis level */ + fp = sp+3; /* new frame pointer */ + /* now push the string arguments */ + mstack[4+sp].sstr = p->defn; /* defn string */ + mstack[5+sp].sstr = p->name; /* macro name */ + mstack[6+sp].sstr = ep; /* start next.. */ + sp += 6; + + t = gpbc(); + putback(t); + if (t != LPAREN) { putback(RPAREN); putback(LPAREN); } + } + } else + if (t == EOF) { + if (sp >= 0) error("m4: unexpected end of input"); + if (--ilevel < 0) break; /* all done thanks */ +#ifndef NO__FILE + remhash("__FILE__", TOP); +#endif + bb = bbstack[ilevel+1]; + (void) fclose(infile[ilevel+1]); + } else + /* non-alpha single-char token seen.. + [the order of else if .. stmts is important.] + */ + if (t == lquote) { /* strip quotes */ + register int nlpar; + + for (nlpar = 1; ; ) { + t = gpbc(); + if (t == rquote) { + if (--nlpar == 0) break; + } else + if (t == lquote) { + nlpar++; + } else { + if (t == vquote) t = gpbc(); + if (t == EOF) { + error("m4: missing right quote"); + } + } + if (sp < 0) { + putc(t, op); + } else { + chrsave(t); + } + } + } else + if (sp < 0) { /* not in a macro at all */ + if (t != scommt) { /* not a comment, so */ + putc(t, op); /* copy it to output */ + } else + if (strip) { /* discard a comment */ + do { + t = gpbc(); + } while (t != ecommt && t != EOF); + } else { /* copy comment to output */ + do { + putc(t, op); + t = gpbc(); + } while (t != ecommt && t != EOF); + putc(t, op); + /* A note on comment handling: this is NOT robust. + | We should do something safe with comments that + | are missing their ecommt termination. + */ + } + } else + switch (t) { + /* There is a peculiar detail to notice here. + Layout is _always_ discarded after left parentheses, + but it is only discarded after commas if they separate + arguments. For example, + define(foo,`|$1|$2|') + foo( a, b) => |a|b| + foo(( a ), ( b )) => |(a )|(b )| + foo((a, x), (b, y)) => |(a, x)|(b, y)| + I find this counter-intuitive, and would expect the code + for LPAREN to read something like this: + + if (PARLEV == 0) { + (* top level left parenthesis: skip layout *) + do t = gpbc(); while (is_blnk(t)); putback(t); - if ((p = inspect(s = token)) == nil) { - if (sp < 0) - while (*s) - putc(*s++, active); - else - while (*s) - chrsave(*s++); - } - else { - /* - * real thing.. First build a call frame: - * - */ - pushf(fp); /* previous call frm */ - pushf(p->type); /* type of the call */ - pushf(0); /* parenthesis level */ - fp = sp; /* new frame pointer */ - /* - * now push the string arguments: - * - */ - pushs(p->defn); /* defn string */ - pushs(p->name); /* macro name */ - pushs(ep); /* start next..*/ - - putback(l = gpbc()); - if (l != LPAREN) { /* add bracks */ - putback(RPAREN); - putback(LPAREN); - } - } - } - else if (t == EOF) { - if (sp > -1) - error("m4: unexpected end of input"); - if (--ilevel < 0) - break; /* all done thanks.. */ - (void) fclose(infile[ilevel+1]); - continue; - } - /* - * non-alpha single-char token seen.. - * [the order of else if .. stmts is - * important.] - * - */ - else if (t == lquote) { /* strip quotes */ - nlpar = 1; - do { - if ((l = gpbc()) == rquote) - nlpar--; - else if (l == lquote) - nlpar++; - else if (l == EOF) - error("m4: missing right quote"); - if (nlpar > 0) { - if (sp < 0) - putc(l, active); - else - chrsave(l); - } - } - while (nlpar != 0); - } - - else if (sp < 0) { /* not in a macro at all */ - if (t == scommt) { /* comment handling here */ - putc(t, active); - while ((t = gpbc()) != ecommt) - putc(t, active); - } - putc(t, active); /* output directly.. */ - } - - else switch(t) { + } else { + (* left parenthesis inside an argument *) + chrsave(t); + } + PARLEV++; + However, it turned out that Oz wrote the actual code + very carefully to mimic the behaviour of "real" m4; + UNIX m4 really does skip layout after all left parens + but only some commas in just this fashion. Sigh. + */ case LPAREN: - if (PARLEV > 0) - chrsave(t); - while (isspace(l = gpbc())) - ; /* skip blank, tab, nl.. */ - putback(l); - PARLEV++; - break; - - case RPAREN: - if (--PARLEV > 0) - chrsave(t); - else { /* end of argument list */ - chrsave(EOS); - - if (sp == STACKMAX) - error("m4: internal stack overflow"); - - if (CALTYP == MACRTYPE) - expand(mstack+fp+1, sp-fp); - else - eval(mstack+fp+1, sp-fp, CALTYP); - - ep = PREVEP; /* flush strspace */ - sp = PREVSP; /* previous sp.. */ - fp = PREVFP; /* rewind stack...*/ - } - break; + if (PARLEV > 0) chrsave(t); + do t = gpbc(); while (is_blnk(t)); /* skip layout */ + putback(t); + PARLEV++; + break; case COMMA: - if (PARLEV == 1) { - chrsave(EOS); /* new argument */ - while (isspace(l = gpbc())) - ; - putback(l); - pushs(ep); - } - break; - default: - chrsave(t); /* stack the char */ - break; - } - } -} + if (PARLEV == 1) { + chrsave(EOS); /* new argument */ + if (sp >= STACKMAX) error(ovmsg); + do t = gpbc(); while (is_blnk(t)); /* skip layout */ + putback(t); + mstack[++sp].sstr = ep; + } else { + chrsave(t); + } + break; - -/* - * build an input token.. - * consider only those starting with _ or A-Za-z. This is a - * combo with lookup to speed things up. - */ -ndptr -inspect(tp) -register char *tp; -{ - register int h = 0; - register char c; - register char *name = tp; - register char *etp = tp+MAXTOK; - register ndptr p; - - while (tp < etp && (isalnum(c = gpbc()) || c == '_')) - h += (*tp++ = c); - putback(c); - if (tp == etp) - error("m4: token too long"); - *tp = EOS; - for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr) - if (strcmp(name, p->name) == 0) - break; - return(p); -} - -#ifdef NONZEROPAGES -/* - * initm4 - initialize various tables. Useful only if your system - * does not know anything about demand-zero pages. - * - */ -initm4() -{ - register int i; - - for (i = 0; i < HASHSIZE; i++) - hashtab[i] = nil; - for (i = 0; i < MAXOUT; i++) - outfile[i] = NULL; -} + case RPAREN: + if (--PARLEV > 0) { + chrsave(t); + } else { + char **argv = (char **)(mstack+fp+1); + int argc = sp-fp; +#if unix | vms + static int sysval; #endif -/* - * initkwds - initialise m4 keywords as fast as possible. - * This very similar to install, but without certain overheads, - * such as calling lookup. Malloc is not used for storing the - * keyword strings, since we simply use the static pointers - * within keywrds block. We also assume that there is enough memory - * to at least install the keywords (i.e. malloc won't fail). - * - */ -initkwds() { - register int i; - register int h; - register ndptr p; + chrsave(EOS); /* last argument */ + if (sp >= STACKMAX) error(ovmsg); +#ifdef DEBUG + fprintf(stderr, "argc = %d\n", argc); + for (t = 0; t < argc; t++) + fprintf(stderr, "argv[%d] = %s\n", t, argv[t]); +#endif + /* If argc == 3 and argv[2] is null, then we + have a call like `macro_or_builtin()'. We + adjust argc to avoid further checking.. + */ + if (argc == 3 && !argv[2][0]) argc--; - for (i = 0; i < MAXKEYS; i++) { - h = hash(keywrds[i].knam); - p = (ndptr) malloc(sizeof(struct ndblock)); - p->nxtptr = hashtab[h]; - hashtab[h] = p; - p->name = keywrds[i].knam; - p->defn = null; - p->type = keywrds[i].ktyp | STATIC; + switch (CALTYP & ~STATIC) { + case MACRTYPE: + expand(argv, argc); + break; + + case DEFITYPE: /* define(..) */ + for (; argc > 2; argc -= 2, argv += 2) + dodefine(argv[2], argc > 3 ? argv[3] : null); + break; + + case PUSDTYPE: /* pushdef(..) */ + for (; argc > 2; argc -= 2, argv += 2) + dopushdef(argv[2], argc > 3 ? argv[3] : null); + break; + + case DUMPTYPE: + dodump(argv, argc); + break; + + case EXPRTYPE: /* eval(Expr) */ + { /* evaluate arithmetic expression */ + /* eval([val: 0[, radix:10 [,min: 1]]]) */ + /* excess arguments are ignored */ + /* eval() with no arguments returns 0 */ + /* this is based on V.3 behaviour */ + int min_digits = 1; + int radix = 10; + long int value = 0; + + switch (argc) { + default: + /* ignore excess arguments */ + case 5: + min_digits = expr(argv[4]); + case 4: + radix = expr(argv[3]); + case 3: + value = expr(argv[2]); + case 2: + break; + } + pbrad(value, radix, min_digits); + } + break; + + case IFELTYPE: /* ifelse(X,Y,IFX=Y,Else) */ + doifelse(argv, argc); + break; + + case IFDFTYPE: /* ifdef(Mac,IfDef[,IfNotDef]) */ + /* select one of two alternatives based on the existence */ + /* of another definition */ + if (argc > 3) { + if (lookup(argv[2]) != nil) { + pbstr(argv[3]); + } else + if (argc > 4) { + pbstr(argv[4]); + } + } + break; + + case LENGTYPE: /* len(Arg) */ + /* find the length of the argument */ + pbnum(argc > 2 ? strlen(argv[2]) : 0); + break; + + case INCRTYPE: /* incr(Expr) */ + /* increment the value of the argument */ + if (argc > 2) pbnum(expr(argv[2]) + 1); + break; + + case DECRTYPE: /* decr(Expr) */ + /* decrement the value of the argument */ + if (argc > 2) pbnum(expr(argv[2]) - 1); + break; + +#if unix || vms + case SYSCTYPE: /* syscmd(Command) */ + /* execute system command */ + /* Make sure m4 output is NOT interrupted */ + fflush(stdout); + fflush(stderr); + + if (argc > 2) sysval = system(argv[2]); + break; + + case SYSVTYPE: /* sysval() */ + /* return value of the last system call. */ + pbnum(sysval); + break; +#endif + + case INCLTYPE: /* include(File) */ + for (t = 2; t < argc; t++) + if (!doincl(argv[t])) cantread(argv[t]); + break; + + case SINCTYPE: /* sinclude(File) */ + for (t = 2; t < argc; t++) + (void) doincl(argv[t]); + break; + +#ifdef EXTENDED + case PASTTYPE: /* paste(File) */ + for (t = 2; t < argc; t++) + if (!dopaste(argv[t])) cantread(argv[t]); + break; + + case SPASTYPE: /* spaste(File) */ + for (t = 2; t < argc; t++) + (void) dopaste(argv[t]); + break; + + case TRIMTYPE: /* m4trim(Source,..) */ + if (argc > 2) m4trim(argv, argc); + break; + + case DEFQTYPE: /* defquote(Mac,...) */ + dodefqt(argv, argc); + break; + + case QUTRTYPE: /* (text...) */ + doqutr(argv, argc); + break; +#endif + + case CHNQTYPE: /* changequote([Left[,Right]]) */ + dochq(argv, argc); + break; + + case CHNCTYPE: /* changecom([Left[,Right]]) */ + dochc(argv, argc); + break; + + case SUBSTYPE: /* substr(Source[,Offset[,Length]]) */ + /* select substring */ + if (argc > 3) dosub(argv, argc); + break; + + case SHIFTYPE: /* shift(~args~) */ + /* push back all arguments except the first one */ + /* (i.e. skip argv[2]) */ + if (argc > 3) { + for (t = argc-1; t > 3; t--) { + pbqtd(argv[t]); + putback(','); + } + pbqtd(argv[3]); + } + break; + + case DIVRTYPE: /* divert(N) */ + if (argc > 2 && (t = expr(argv[2])) != 0) { + dodiv(t); + } else { + active = stdout; + oindex = 0; + } + op = active; + break; + + case UNDVTYPE: /* undivert(N...) */ + doundiv(argv, argc); + op = active; + break; + + case DIVNTYPE: /* divnum() */ + /* return the number of current output diversion */ + pbnum(oindex); + break; + + case UNDFTYPE: /* undefine(..) */ + /* undefine a previously defined macro(s) or m4 keyword(s). */ + for (t = 2; t < argc; t++) remhash(argv[t], ALL); + break; + + case POPDTYPE: /* popdef(Mac...) */ + /* remove the topmost definitions of macro(s) or m4 keyword(s). */ + for (t = 2; t < argc; t++) remhash(argv[t], TOP); + break; + + case MKTMTYPE: /* maketemp(Pattern) */ + /* create a temporary file */ + if (argc > 2) pbstr(mktemp(argv[2])); + break; + + case TRNLTYPE: /* translit(Source,Dom,Rng) */ + /* replace all characters in the source string that */ + /* appears in the "from" string with the corresponding */ + /* characters in the "to" string. */ + + if (argc > 3) { + char temp[MAXTOK]; + + if (argc > 4) + map(temp, argv[2], argv[3], argv[4]); + else + map(temp, argv[2], argv[3], null); + pbstr(temp); + } else if (argc > 2) + pbstr(argv[2]); + break; + + case INDXTYPE: /* index(Source,Target) */ + /* find the index of the second argument string in */ + /* the first argument string. -1 if not present. */ + pbnum(argc > 3 ? indx(argv[2], argv[3]) : -1); + break; + + case ERRPTYPE: /* errprint(W,...,W) */ + /* print the arguments to stderr file */ + for (t = 2; t < argc; t++) fprintf(stderr, "%s ", argv[t]); + fprintf(stderr, "\n"); + break; + + case DNLNTYPE: /* dnl() */ + /* eat upto and including newline */ + while ((t = gpbc()) != '\n' && t != EOF) ; + break; + + case M4WRTYPE: /* m4wrap(AtExit) */ + /* set up for wrap-up/wind-down activity. */ + /* NB: if there are several calls to m4wrap */ + /* only the last is effective; strange, but */ + /* that's what System V does. */ + m4wraps = argc > 2 ? strsave(argv[2]) : null; + break; + + case EXITTYPE: /* m4exit(Expr) */ + /* immediate exit from m4. */ + killdiv(); /* mustn't forget that one! */ + exit(argc > 2 ? expr(argv[2]) : 0); + break; + + case DEFNTYPE: /* defn(Mac) */ + for (t = 2; t < argc; t++) + dodefn(argv[t]); + break; + + default: + error("m4: major botch in eval."); + break; + } + + ep = PREVEP; /* flush strspace */ + sp = PREVSP; /* previous sp.. */ + fp = PREVFP; /* rewind stack... */ + } + break; + + default: + chrsave(t); /* stack the char */ + break; + } } -} + } + + +int main(argc, argv) + int argc; + char **argv; + { + register int c; + register int n; + char *p; + +#ifdef SIGINT + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, onintr); +#endif + + /* Initialise the chtype[] table. + '0' .. '9' -> 1..10 + 'A' .. 'Z' -> 11..37 + 'a' .. 'z' -> 11..37 + '_' -> 38 + all other characters -> 0 + */ + for (c = EOF; c <= UCHAR_MAX; c++) chtype[c - EOF] = 0; + for (c = 1, p = "0123456789"; *p; p++, c++) + chtype[*(unsigned char *)p - EOF] = c; + for (c = 11, p = "abcdefghijklmnopqrstuvwxyz"; *p; p++, c++) + chtype[*(unsigned char *)p - EOF] = c; + for (c = 11, p = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; *p; p++, c++) + chtype[*(unsigned char *)p - EOF] = c; + chtype['_' - EOF] = 38; + +#ifdef NONZEROPAGES + /* If your system does not initialise global variables to */ + /* 0 bits, do it here. */ + for (n = 0; n < HASHSIZE; n++) hashtab[n] = nil; + for (n = 0; n < MAXOUT; n++) outfile[n] = NULL; +#endif + initkwds(); + + while ((c = getopt(argc, argv, "cetD:U:o:B:H:S:T:")) != EOF) { + switch (c) { +#if 0 + case 's': /* enable #line sync in output */ + fprintf(stderr, "m4: this version does not support -s\n"); + exit(2); +#endif + + case 'c': /* strip comments */ + strip ^= 1; + break; + + case 'e': /* interactive */ + (void) signal(SIGINT, SIG_IGN); + setbuf(stdout, NULL); + break; + + case 'D': /* define something..*/ + for (p = optarg; *p && *p != '='; p++) ; + if (*p) *p++ = EOS; + dodefine(optarg, p); + break; + + case 'U': /* undefine... */ + remhash(optarg, TOP); + break; + + case 'B': case 'H': /* System V compatibility */ + case 'S': case 'T': /* ignore them */ + break; + + case 'o': /* specific output */ + if (!freopen(optarg, "w", stdout)) { + perror(optarg); + exit(1); + } + break; + + case '?': + default: + usage(); + } + } + + active = stdout; /* default active output */ + m4temp = mktemp(DIVNAM); /* filename for diversions */ + + sp = -1; /* stack pointer initialized */ + fp = 0; /* frame pointer initialized */ + + if (optind == argc) { /* no more args; read stdin */ + infile[0] = stdin; /* default input (naturally) */ +#ifndef NO__FILE + dodefine("__FILE__", "-"); /* Helas */ +#endif + macro(); /* process that file */ + } else /* file names in commandline */ + for (; optind < argc; optind++) { + char *name = argv[optind]; /* next file name */ + infile[0] = fopen(name, "r"); + if (!infile[0]) cantread(name); +#ifndef NO__FILE + dodefine("__FILE__", name); +#endif + macro(); + fclose(infile[0]); + } + + if (*m4wraps) { /* anything for rundown ?? */ + ilevel = 0; /* in case m4wrap includes.. */ + putback(EOF); /* eof is a must !! */ + pbstr(m4wraps); /* user-defined wrapup act */ + macro(); /* last will and testament */ + } else { /* default wrap-up: undivert */ + for (n = 1; n < MAXOUT; n++) + if (outfile[n] != NULL) getdiv(n); + } + + if (outfile[0] != NULL) { /* remove bitbucket if used */ + (void) fclose(outfile[0]); + m4temp[UNIQUE] = '0'; +#if unix + (void) unlink(m4temp); +#else + (void) remove(m4temp); +#endif + } + exit(0); + return 0; + } + diff --git a/usr.bin/m4/mdef.h b/usr.bin/m4/mdef.h index 4204a6d275c4..d8dc094fab7a 100644 --- a/usr.bin/m4/mdef.h +++ b/usr.bin/m4/mdef.h @@ -1,52 +1,32 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Ozan Yigit. - * - * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)mdef.h 5.6 (Berkeley) 2/26/91 - */ +/* Header : mdef.h + Author : Ozan Yigit + Updated: 4 May 1992 +*/ +#ifndef MACRTYPE -/* - * mdef.h - * Facility: m4 macro processor - * by: oz - */ +#ifndef unix +#define unix 0 +#endif -/* - * - * m4 constants.. - * - */ +#ifndef vms +#define vms 0 +#endif + +#include +#include + +#ifdef __STDC__ +#include +#else +#ifdef VOID +#define void int +#endif +extern int strlen(); +extern int strcmp(); +extern void memcpy(); +#endif + +/* m4 constants */ #define MACRTYPE 1 #define DEFITYPE 2 @@ -81,18 +61,22 @@ #define SYSVTYPE 31 #define EXITTYPE 32 #define DEFNTYPE 33 - +#define LINETYPE 34 +#define TRIMTYPE 35 +#define TLITTYPE 36 +#define DEFQTYPE 37 /* defquote */ +#define QUTRTYPE 38 /* quoter thus defined */ + #define STATIC 128 -/* - * m4 special characters - */ +/* m4 special characters */ #define ARGFLAG '$' #define LPAREN '(' #define RPAREN ')' #define LQUOTE '`' #define RQUOTE '\'' +#define VQUOTE ('V'&(' '- 1)) #define COMMA ',' #define SCOMMT '#' #define ECOMMT '\n' @@ -104,44 +88,45 @@ #define EOS (char) 0 #define MAXINP 10 /* maximum include files */ #define MAXOUT 10 /* maximum # of diversions */ -#define MAXSTR 512 /* maximum size of string */ +#ifdef SMALL +#define MAXSTR 512 /* maximum size of string */ #define BUFSIZE 4096 /* size of pushback buffer */ #define STACKMAX 1024 /* size of call stack */ #define STRSPMAX 4096 /* size of string space */ +#define HASHSIZE 199 /* maximum size of hashtab */ +#else +#define MAXSTR 1024 /* maximum size of string */ +#define BUFSIZE 8192 /* size of pushback buffer */ +#define STACKMAX 2048 /* size of call stack */ +#define STRSPMAX 8192 /* size of string space */ +#define HASHSIZE 509 /* maximum size of hashtab */ +#endif #define MAXTOK MAXSTR /* maximum chars in a tokn */ -#define HASHSIZE 199 /* maximum size of hashtab */ #define ALL 1 #define TOP 0 #define TRUE 1 #define FALSE 0 -#define cycle for(;;) -/* - * m4 data structures - */ +/* m4 data structures */ typedef struct ndblock *ndptr; -struct ndblock { /* hastable structure */ +struct ndblock /* hashtable structure */ + { char *name; /* entry name.. */ char *defn; /* definition.. */ int type; /* type of the entry.. */ ndptr nxtptr; /* link to next entry.. */ -}; + }; #define nil ((ndptr) 0) -struct keyblk { - char *knam; /* keyword name */ - int ktyp; /* keyword type */ -}; - -typedef union { /* stack structure */ - int sfra; /* frame entry */ +typedef union /* stack structure */ + { int sfra; /* frame entry */ char *sstr; /* string entry */ -} stae; + } stae; /* * macros for readibility and/or speed @@ -151,7 +136,7 @@ typedef union { /* stack structure */ * pushf() - push a call frame entry onto stack * pushs() - push a string pointer onto stack */ -#define gpbc() (bp > buf) ? *--bp : getc(infile[ilevel]) +#define gpbc() bp == bb ? getc(infile[ilevel]) : *--bp #define min(x,y) ((x > y) ? y : x) #define pushf(x) if (sp < STACKMAX) mstack[++sp].sfra = (x) #define pushs(x) if (sp < STACKMAX) mstack[++sp].sstr = (x) @@ -184,3 +169,5 @@ typedef union { /* stack structure */ #define PREVEP (mstack[fp+3].sstr) #define PREVSP (fp-3) #define PREVFP (mstack[fp-2].sfra) + +#endif diff --git a/usr.bin/m4/misc.c b/usr.bin/m4/misc.c index fae81ada3e00..6d51835befbd 100644 --- a/usr.bin/m4/misc.c +++ b/usr.bin/m4/misc.c @@ -1,211 +1,357 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Ozan Yigit. - * - * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ +/* File : misc.c + Author : Ozan Yigit + Updated: 26-Mar-1993 + Purpose: Miscellaneous support code for PD M4. +*/ -#ifndef lint -static char sccsid[] = "@(#)misc.c 5.6 (Berkeley) 2/26/91"; -#endif /* not lint */ - -/* - * misc.c - * Facility: m4 macro processor - * by: oz - */ - -#include -#include -#include -#include #include "mdef.h" -#include "extr.h" -#include "pathnames.h" - -/* - * indx - find the index of second str in the - * first str. - */ -indx(s1, s2) -char *s1; -char *s2; -{ - register char *t; - register char *p; - register char *m; - - for (p = s1; *p; p++) { - for (t = p, m = s2; *m && *m == *t; m++, t++) - ; - if (!*m) - return(p - s1); - } - return (-1); -} - -/* - * putback - push character back onto input - * - */ -putback (c) -char c; -{ - if (bp < endpbb) - *bp++ = c; - else - error("m4: too many characters pushed back"); -} - -/* - * pbstr - push string back onto input - * putback is replicated to improve - * performance. - * - */ -pbstr(s) -register char *s; -{ - register char *es; +#include "extr.h" +#include "ourlims.h" + +#ifdef DUFFCP + +/* This version of the ANSI standard function memcpy() + uses Duff's Device (tm Tom Duff) to unroll the copying loop: + while (count-- > 0) *to++ = *from++; +*/ +void memcpy(to, from, count) + register char *from, *to; + register int count; + { + if (count > 0) { + register int loops = (count+8-1) >> 3; /* div 8 round up */ + + switch (count & (8-1)) { /* mod 8 */ + case 0: do { *to++ = *from++; + case 7: *to++ = *from++; + case 6: *to++ = *from++; + case 5: *to++ = *from++; + case 4: *to++ = *from++; + case 3: *to++ = *from++; + case 2: *to++ = *from++; + case 1: *to++ = *from++; + } while (--loops > 0); + } + } + } + +#endif + + +/* strsave(s) + return a new malloc()ed copy of s -- same as V.3's strdup(). +*/ +char *strsave(s) + char *s; + { + register int n = strlen(s)+1; + char *p = malloc(n); + + if (p) memcpy(p, s, n); + return p; + } + + +/* indx(s1, s2) + if s1 can be decomposed as alpha || s2 || omega, return the length + of the shortest such alpha, otherwise return -1. +*/ +int indx(s1, s2) + char *s1; + char *s2; + { + register char *t; + register char *m; + register char *p; + + for (p = s1; *p; p++) { + for (t = p, m = s2; *m && *m == *t; m++, t++); + if (!*m) return p-s1; + } + return -1; + } + + +char pbmsg[] = "m4: too many characters pushed back"; + +/* Xputback(c) + push character c back onto the input stream. + This is now macro putback() in misc.h +*/ +void Xputback(c) + char c; + { + if (bp < endpbb) *bp++ = c; else error(pbmsg); + } + + +/* pbstr(s) + push string s back onto the input stream. + putback() has been unfolded here to improve performance. + Example: + s = + bp = + After the call: + bp = + It would be more efficient if we ran the pushback buffer in the + opposite direction +*/ +void pbstr(s) + register char *s; + { + register char *es; register char *zp; - es = s; zp = bp; + for (es = s; *es; ) es++; /* now es points to terminating NUL */ + bp += es-s; /* advance bp as far as it should go */ + if (bp >= endpbb) error("m4: too many characters to push back"); + while (es > s) *zp++ = *--es; + } - while (*es) - es++; - es--; - while (es >= s) - if (zp < endpbb) - *zp++ = *es--; - if ((bp = zp) == endpbb) - error("m4: too many characters pushed back"); -} - -/* - * pbnum - convert number to string, push back on input. - * - */ -pbnum (n) -int n; -{ - register int num; - - num = (n < 0) ? -n : n; - do { - putback(num % 10 + '0'); - } - while ((num /= 10) > 0); - if (n < 0) putback('-'); -} - -/* - * chrsave - put single char on string space - * - */ -chrsave (c) -char c; -{ -/*** if (sp < 0) - putc(c, active); - else ***/ if (ep < endest) - *ep++ = c; - else - error("m4: string space overflow"); -} - -/* - * getdiv - read in a diversion file, and - * trash it. - */ -getdiv(ind) { - register int c; - register FILE *dfil; - - if (active == outfile[ind]) - error("m4: undivert: diversion still active."); - (void) fclose(outfile[ind]); - outfile[ind] = NULL; - m4temp[UNIQUE] = ind + '0'; - if ((dfil = fopen(m4temp, "r")) == NULL) - error("m4: cannot undivert."); - else - while((c = getc(dfil)) != EOF) - putc(c, active); - (void) fclose(dfil); +/* pbqtd(s) + pushes string s back "quoted", doing whatever has to be done to it to + make sure that the result will evaluate to the original value. As it + happens, we have only to add lquote and rquote. +*/ +void pbqtd(s) + register char *s; + { + register char *es; + register char *zp; - if (unlink(m4temp) == -1) - error("m4: cannot unlink."); -} - -/* - * Very fatal error. Close all files - * and die hard. - */ -error(s) -char *s; -{ - killdiv(); - fprintf(stderr,"%s\n",s); - exit(1); -} - -/* - * Interrupt handling - */ + zp = bp; + for (es = s; *es; ) es++; /* now es points to terminating NUL */ + bp += 2+es-s; /* advance bp as far as it should go */ + if (bp >= endpbb) error("m4: too many characters to push back"); + *zp++ = rquote; + while (es > s) *zp++ = *--es; + *zp++ = lquote; + } + + +/* pbnum(n) + convert a number to a (decimal) string and push it back. + The original definition did not work for MININT; this does. +*/ +void pbnum(n) + int n; + { + register int num; + + num = n > 0 ? -n : n; /* MININT <= num <= 0 */ + do { + putback('0' - (num % 10)); + } while ((num /= 10) < 0); + if (n < 0) putback('-'); + } + + +/* pbrad(n, r, m) + converts a number n to base r ([-36..-2] U [2..36]), with at least + m digits. If r == 10 and m == 1, this is exactly the same as pbnum. + However, this uses the function int2str() from R.A.O'Keefe's public + domain string library, and puts the results of that back. + The Unix System V Release 3 version of m4 accepts radix 1; + THIS VERSION OF M4 DOES NOT ACCEPT RADIX 1 OR -1, + nor do we accept radix < -36 or radix > 36. At the moment such bad + radices quietly produce nothing. The V.3 treatment of radix 1 is + push back abs(n) "1"s, then + if n < 0, push back one "-". + Until I come across something which uses it, I can't bring myself to + implement this. + + I have, however, found a use for radix 0. Unsurprisingly, it is + related to radix 0 in Edinburgh Prolog. + eval('c1c2...cn', 0, m) + pushes back max(m-n,0) blanks and the characters c1...cn. This can + adjust to any byte size as long as UCHAR_MAX = (1 << CHAR_BIT) - 1. + In particular, eval(c, 0) where 0 < c <= UCHAR_MAX, pushes back the + character with code c. Note that this has to agree with eval(); so + both of them have to use the same byte ordering. +*/ +void pbrad(n, r, m) + long int n; + int r, m; + { + char buffer[34]; + char *p; + int L; + + if (r == 0) { + unsigned long int x = (unsigned long)n; + int n; + + for (n = 0; x; x >>= CHAR_BIT, n++) buffer[n] = x & UCHAR_MAX; + for (L = n; --L >= 0; ) putback(buffer[L]); + for (L = m-n; --L >= 0; ) putback(' '); + return; + } + L = m - (int2str(p = buffer, -r, n)-buffer); + if (buffer[0] == '-') L++, p++; + if (L > 0) { + pbstr(p); + while (--L >= 0) putback('0'); + if (p != buffer) putback('-'); + } else { + pbstr(buffer); + } + } + + +char csmsg[] = "m4: string space overflow"; + +/* chrsave(c) + put the character c in the string space. +*/ +void Xchrsave(c) + char c; + { +#if 0 + if (sp < 0) putc(c, active); else +#endif + if (ep < endest) *ep++ = c; else + error(csmsg); + } + + +/* getdiv(ind) + read in a diversion file and then delete it. +*/ +void getdiv(ind) + int ind; + { + register int c; + register FILE *dfil; + register FILE *afil; + + afil = active; + if (outfile[ind] == afil) + error("m4: undivert: diversion still active."); + (void) fclose(outfile[ind]); + outfile[ind] = NULL; + m4temp[UNIQUE] = '0' + ind; + if ((dfil = fopen(m4temp, "r")) == NULL) + error("m4: cannot undivert."); + while ((c = getc(dfil)) != EOF) putc(c, afil); + (void) fclose(dfil); + +#if vms + if (remove(m4temp)) error("m4: cannot unlink."); +#else + if (unlink(m4temp) == -1) error("m4: cannot unlink."); +#endif + } + + +/* killdiv() + delete all the diversion files which have been created. +*/ +void killdiv() + { + register int n; + + for (n = 0; n < MAXOUT; n++) { + if (outfile[n] != NULL) { + (void) fclose(outfile[n]); + m4temp[UNIQUE] = '0' + n; +#if unix + (void) unlink(m4temp); +#else + (void) remove(m4temp); +#endif + } + } + } + + +/* error(s) + close all files, report a fatal error, and quit, letting the caller know. +*/ +void error(s) + char *s; + { + killdiv(); + fprintf(stderr, "%s\n", s); + exit(1); + } + + +/* Interrupt handling +*/ static char *msg = "\ninterrupted."; - -void -onintr() { - error(msg); -} - -/* - * killdiv - get rid of the diversion files - * - */ -killdiv() { - register int n; - - for (n = 0; n < MAXOUT; n++) - if (outfile[n] != NULL) { - (void) fclose (outfile[n]); - m4temp[UNIQUE] = n + '0'; - (void) unlink (m4temp); - } -} - -usage() { - fprintf(stderr, "usage: m4 [-Dname[=val]] [-Uname]\n"); - exit(1); -} + +#ifdef __STDC__ +void onintr(int signo) +#else +onintr() +#endif + { + error(msg); + } + + +void usage() + { + fprintf(stderr, "Usage: m4 [-e] [-[BHST]int] [-Dname[=val]] [-Uname]\n"); + exit(1); + } + +#ifdef GETOPT +/* Henry Spencer's getopt() - get option letter from argv */ + +char *optarg; /* Global argument pointer. */ +int optind = 0; /* Global argv index. */ + +static char *scan = NULL; /* Private scan pointer. */ + +#ifndef __STDC__ +extern char *index(); +#define strchr index +#endif + +int getopt(argc, argv, optstring) + int argc; + char **argv; + char *optstring; + { + register char c; + register char *place; + + optarg = NULL; + + if (scan == NULL || *scan == '\0') { + if (optind == 0) optind++; + if (optind >= argc + || argv[optind][0] != '-' + || argv[optind][1] == '\0') + return EOF; + if (strcmp(argv[optind], "--") == 0) { + optind++; + return EOF; + } + scan = argv[optind]+1; + optind++; + } + c = *scan++; + place = strchr(optstring, c); + + if (place == NULL || c == ':') { + fprintf(stderr, "%s: unknown option -%c\n", argv[0], c); + return '?'; + } + place++; + if (*place == ':') { + if (*scan != '\0') { + optarg = scan; + scan = NULL; + } else { + optarg = argv[optind]; + optind++; + } + } + return c; + } +#endif + diff --git a/usr.bin/m4/ourlims.h b/usr.bin/m4/ourlims.h new file mode 100644 index 000000000000..8bc0b56c0a31 --- /dev/null +++ b/usr.bin/m4/ourlims.h @@ -0,0 +1,16 @@ +/* File : ourlims.h + Author : Richard A. O'Keefe + Defines: UCHAR_MAX, CHAR_BIT, LONG_BIT +*/ +/* If is available, use that. + Otherwise, use 8-bit byte as the default. + If the number of characters is a power of 2, you might be able + to use (unsigned char)(~0), but why get fancy? +*/ +#ifdef __STDC__ +#include +#else +#define UCHAR_MAX 255 +#define CHAR_BIT 8 +#endif +#define LONG_BIT 32 diff --git a/usr.bin/m4/serv.c b/usr.bin/m4/serv.c index 2ecd4e9c15b1..16dc1f6467d4 100644 --- a/usr.bin/m4/serv.c +++ b/usr.bin/m4/serv.c @@ -1 +1 @@ -revision 1.1.1.1 intentionally removed +revision 1.2 intentionally removed