aboutsummaryrefslogtreecommitdiff
path: root/usr.bin/m4
diff options
context:
space:
mode:
authorCem Keylan <cem@ckyln.com>2020-10-16 17:47:01 +0300
committerCem Keylan <cem@ckyln.com>2020-10-16 17:47:01 +0300
commit5d69c6a2661bba0a22f3ecfd517e2e9767a38346 (patch)
tree1f479b2714e127835db7f33a3bfed4c38c52f883 /usr.bin/m4
parente2abcdca396661cbe0ae2ddb13d5c2b85682c13a (diff)
downloadotools-5d69c6a2661bba0a22f3ecfd517e2e9767a38346.tar.gz
add tools
Diffstat (limited to 'usr.bin/m4')
-rw-r--r--usr.bin/m4/CVS/Entries18
-rw-r--r--usr.bin/m4/CVS/Repository1
-rw-r--r--usr.bin/m4/CVS/Root1
-rw-r--r--usr.bin/m4/Makefile17
-rw-r--r--usr.bin/m4/NOTES64
-rw-r--r--usr.bin/m4/PSD.doc/CVS/Entries3
-rw-r--r--usr.bin/m4/PSD.doc/CVS/Repository1
-rw-r--r--usr.bin/m4/PSD.doc/CVS/Root1
-rw-r--r--usr.bin/m4/PSD.doc/Makefile11
-rw-r--r--usr.bin/m4/PSD.doc/m4.ms967
-rw-r--r--usr.bin/m4/TEST/CVS/Entries7
-rw-r--r--usr.bin/m4/TEST/CVS/Repository1
-rw-r--r--usr.bin/m4/TEST/CVS/Root1
-rw-r--r--usr.bin/m4/TEST/ack.m438
-rw-r--r--usr.bin/m4/TEST/hanoi.m443
-rw-r--r--usr.bin/m4/TEST/hash.m453
-rw-r--r--usr.bin/m4/TEST/sqroot.m443
-rw-r--r--usr.bin/m4/TEST/string.m443
-rw-r--r--usr.bin/m4/TEST/test.m4241
-rw-r--r--usr.bin/m4/eval.c1033
-rw-r--r--usr.bin/m4/expr.c43
-rw-r--r--usr.bin/m4/extern.h182
-rw-r--r--usr.bin/m4/gnum4.c692
-rw-r--r--usr.bin/m4/look.c337
-rw-r--r--usr.bin/m4/m4.1524
-rw-r--r--usr.bin/m4/main.c642
-rw-r--r--usr.bin/m4/mdef.h237
-rw-r--r--usr.bin/m4/misc.c467
-rw-r--r--usr.bin/m4/parser.c756
-rw-r--r--usr.bin/m4/parser.tab.h13
-rw-r--r--usr.bin/m4/parser.y84
-rw-r--r--usr.bin/m4/pathnames.h38
-rw-r--r--usr.bin/m4/stdd.h55
-rw-r--r--usr.bin/m4/tokenizer.c191
-rw-r--r--usr.bin/m4/trace.c196
35 files changed, 7044 insertions, 0 deletions
diff --git a/usr.bin/m4/CVS/Entries b/usr.bin/m4/CVS/Entries
new file mode 100644
index 0000000..382e47b
--- /dev/null
+++ b/usr.bin/m4/CVS/Entries
@@ -0,0 +1,18 @@
+/Makefile/1.16/Sun Jul 9 14:04:50 2017//
+/NOTES/1.1.1.1/Wed Oct 18 08:45:35 1995//
+/eval.c/1.78/Fri Jun 28 05:35:34 2019//
+/expr.c/1.18/Tue Sep 7 19:58:09 2010//
+/extern.h/1.55/Thu Jun 15 13:48:42 2017//
+/gnum4.c/1.52/Mon Aug 21 21:41:13 2017//
+/look.c/1.24/Sun Dec 21 09:33:12 2014//
+/m4.1/1.64/Thu Jun 15 13:48:42 2017//
+/main.c/1.87/Thu Jun 15 13:48:42 2017//
+/mdef.h/1.33/Tue Nov 3 16:21:47 2015//
+/misc.c/1.47/Thu Jun 15 13:48:42 2017//
+/parser.y/1.7/Thu Apr 12 17:00:11 2012//
+/pathnames.h/1.6/Tue Nov 3 16:21:47 2015//
+/stdd.h/1.6/Tue Sep 7 19:58:09 2010//
+/tokenizer.l/1.10/Sat Jun 17 01:55:16 2017//
+/trace.c/1.16/Tue Sep 7 19:58:09 2010//
+D/PSD.doc////
+D/TEST////
diff --git a/usr.bin/m4/CVS/Repository b/usr.bin/m4/CVS/Repository
new file mode 100644
index 0000000..534b442
--- /dev/null
+++ b/usr.bin/m4/CVS/Repository
@@ -0,0 +1 @@
+src/usr.bin/m4
diff --git a/usr.bin/m4/CVS/Root b/usr.bin/m4/CVS/Root
new file mode 100644
index 0000000..3811072
--- /dev/null
+++ b/usr.bin/m4/CVS/Root
@@ -0,0 +1 @@
+/cvs
diff --git a/usr.bin/m4/Makefile b/usr.bin/m4/Makefile
new file mode 100644
index 0000000..acef575
--- /dev/null
+++ b/usr.bin/m4/Makefile
@@ -0,0 +1,17 @@
+# $OpenBSD: Makefile,v 1.16 2017/07/09 14:04:50 espie Exp $
+
+# -DEXTENDED
+# if you want the paste & spaste macros.
+
+PROG= m4
+CFLAGS+=-DEXTENDED -I.
+CDIAGFLAGS=-W -Wall -Wstrict-prototypes -pedantic \
+ -Wno-unused -Wno-char-subscripts -Wno-sign-compare
+
+LDADD= -lm -lutil
+DPADD= ${LIBM} ${LIBUTIL}
+
+SRCS= eval.c expr.c look.c main.c misc.c gnum4.c trace.c tokenizer.l parser.y
+MAN= m4.1
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/m4/NOTES b/usr.bin/m4/NOTES
new file mode 100644
index 0000000..d60f80e
--- /dev/null
+++ b/usr.bin/m4/NOTES
@@ -0,0 +1,64 @@
+m4 - macro processor
+
+PD m4 is based on the macro tool distributed with the software
+tools (VOS) package, and described in the "SOFTWARE TOOLS" and
+"SOFTWARE TOOLS IN PASCAL" books. It has been expanded to include
+most of the command set of SysV m4, the standard UN*X macro processor.
+
+Since both PD m4 and UN*X m4 are based on SOFTWARE TOOLS macro,
+there may be certain implementation similarities between
+the two. The PD m4 was produced without ANY references to m4
+sources.
+
+written by: Ozan S. Yigit
+
+References:
+
+ Software Tools distribution: macro
+
+ Kernighan, Brian W. and P. J. Plauger, SOFTWARE
+ TOOLS IN PASCAL, Addison-Wesley, Mass. 1981
+
+ Kernighan, Brian W. and P. J. Plauger, SOFTWARE
+ TOOLS, Addison-Wesley, Mass. 1976
+
+ Kernighan, Brian W. and Dennis M. Ritchie,
+ THE M4 MACRO PROCESSOR, Unix Programmer's Manual,
+ Seventh Edition, Vol. 2, Bell Telephone Labs, 1979
+
+ System V man page for M4
+
+
+Implementation Notes:
+
+[1] PD m4 uses a different (and simpler) stack mechanism than the one
+ described in Software Tools and Software Tools in Pascal books.
+ The triple stack thing is replaced with a single stack containing
+ the call frames and the arguments. Each frame is back-linked to a
+ previous stack frame, which enables us to rewind the stack after
+ each nested call is completed. Each argument is a character pointer
+ to the beginning of the argument string within the string space.
+ The only exceptions to this are (*) arg 0 and arg 1, which are
+ the macro definition and macro name strings, stored dynamically
+ for the hash table.
+
+ . .
+ | . | <-- sp | . |
+ +-------+ +-----+
+ | arg 3 ------------------------------->| str |
+ +-------+ | . |
+ | arg 2 --------------+ .
+ +-------+ |
+ * | | |
+ +-------+ | +-----+
+ | plev | <-- fp +---------------->| str |
+ +-------+ | . |
+ | type | .
+ +-------+
+ | prcf -----------+ plev: paren level
+ +-------+ | type: call type
+ | . | | prcf: prev. call frame
+ . |
+ +-------+ |
+ | <----------+
+ +-------+
diff --git a/usr.bin/m4/PSD.doc/CVS/Entries b/usr.bin/m4/PSD.doc/CVS/Entries
new file mode 100644
index 0000000..8644d2d
--- /dev/null
+++ b/usr.bin/m4/PSD.doc/CVS/Entries
@@ -0,0 +1,3 @@
+/Makefile/1.4/Sun Feb 1 14:43:10 2004//
+/m4.ms/1.2/Thu Jun 26 16:18:48 2003//
+D
diff --git a/usr.bin/m4/PSD.doc/CVS/Repository b/usr.bin/m4/PSD.doc/CVS/Repository
new file mode 100644
index 0000000..d480cc8
--- /dev/null
+++ b/usr.bin/m4/PSD.doc/CVS/Repository
@@ -0,0 +1 @@
+src/usr.bin/m4/PSD.doc
diff --git a/usr.bin/m4/PSD.doc/CVS/Root b/usr.bin/m4/PSD.doc/CVS/Root
new file mode 100644
index 0000000..3811072
--- /dev/null
+++ b/usr.bin/m4/PSD.doc/CVS/Root
@@ -0,0 +1 @@
+/cvs
diff --git a/usr.bin/m4/PSD.doc/Makefile b/usr.bin/m4/PSD.doc/Makefile
new file mode 100644
index 0000000..12bff06
--- /dev/null
+++ b/usr.bin/m4/PSD.doc/Makefile
@@ -0,0 +1,11 @@
+# $OpenBSD: Makefile,v 1.4 2004/02/01 14:43:10 jmc Exp $
+
+
+DIR= psd/17.m4
+SRCS= m4.ms
+MACROS= -ms
+
+paper.txt: ${SRCS}
+ ${ROFF} -Tascii ${SRCS} > ${.TARGET}
+
+.include <bsd.doc.mk>
diff --git a/usr.bin/m4/PSD.doc/m4.ms b/usr.bin/m4/PSD.doc/m4.ms
new file mode 100644
index 0000000..1163ee4
--- /dev/null
+++ b/usr.bin/m4/PSD.doc/m4.ms
@@ -0,0 +1,967 @@
+.\" $OpenBSD: m4.ms,v 1.2 2003/06/26 16:18:48 mickey Exp $
+.\"
+.\" Copyright (C) Caldera International Inc. 2001-2002.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code and documentation 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 or owned by Caldera
+.\" International, Inc.
+.\" 4. Neither the name of Caldera International, Inc. nor the names of other
+.\" contributors may be used to endorse or promote products derived from
+.\" this software without specific prior written permission.
+.\"
+.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY 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.
+.\"
+.\" @(#)m4.ms 6.3 (Berkeley) 6/5/93
+.\"
+.EH 'PSD:17-%''The M4 Macro Processor'
+.OH 'The M4 Macro Processor''PSD:17-%'
+.if n .ls 2
+.tr _\(em
+.tr *\(**
+.de UC
+\&\\$3\s-1\\$1\\s0\&\\$2
+..
+.de IT
+.if n .ul
+\&\\$3\f2\\$1\fP\&\\$2
+..
+.de UL
+.if n .ul
+\&\\$3\f3\\$1\fP\&\\$2
+..
+.de P1
+.DS I 3n
+.if n .ls 2
+.nf
+.if n .ta 5 10 15 20 25 30 35 40 45 50 55 60
+.if t .ta .4i .8i 1.2i 1.6i 2i 2.4i 2.8i 3.2i 3.6i 4i 4.4i 4.8i 5.2i 5.6i
+.if t .tr -\(mi|\(bv'\(fm^\(no*\(**
+.tr `\(ga'\(aa
+.if t .tr _\(ul
+.ft 3
+.lg 0
+..
+.de P2
+.ps \\n(PS
+.vs \\n(VSp
+.ft R
+.if n .ls 2
+.tr --||''^^!!
+.if t .tr _\(em
+.fi
+.lg
+.DE
+.if t .tr _\(em
+..
+.hw semi-colon
+.hw estab-lished
+.hy 14
+. \"2=not last lines; 4= no -xx; 8=no xx-
+. \"special chars in programs
+. \" start of text
+.\".RP
+.....TR 59
+.....TM 77-1273-6 39199 39199-11
+.ND "July 1, 1977"
+.TL
+The M4 Macro Processor
+.AU "MH 2C-518" 6021
+Brian W. Kernighan
+.AU "MH 2C-517" 3770
+Dennis M. Ritchie
+.AI
+.MH
+.AB
+.PP
+M4 is a macro processor available on
+.UX
+and
+.UC GCOS .
+Its primary use has been as a
+front end for Ratfor for those
+cases where parameterless macros
+are not adequately powerful.
+It has also been used for languages as disparate as C and Cobol.
+M4 is particularly suited for functional languages like Fortran, PL/I and C
+since macros are specified in a functional notation.
+.PP
+M4 provides features seldom found even in much larger
+macro processors,
+including
+.IP " \(bu"
+arguments
+.IP " \(bu"
+condition testing
+.IP " \(bu"
+arithmetic capabilities
+.IP " \(bu"
+string and substring functions
+.IP " \(bu"
+file manipulation
+.LP
+.PP
+This paper is a user's manual for M4.
+.AE
+.CS 6 0 6 0 0 1
+.if t .2C
+.SH
+Introduction
+.PP
+A macro processor is a useful way to enhance a programming language,
+to make it more palatable
+or more readable,
+or to tailor it to a particular application.
+The
+.UL #define
+statement in C
+and the analogous
+.UL define
+in Ratfor
+are examples of the basic facility provided by
+any macro processor _
+replacement of text by other text.
+.PP
+The M4 macro processor is an extension of a macro processor called M3
+which was written by D. M. Ritchie
+for the AP-3 minicomputer;
+M3 was in turn based on a macro processor implemented for [1].
+Readers unfamiliar with the basic ideas of macro processing
+may wish to read some of the discussion there.
+.PP
+M4 is a suitable front end for Ratfor and C,
+and has also been used successfully with Cobol.
+Besides the straightforward replacement of one string of text by another,
+it provides
+macros with arguments,
+conditional macro expansion,
+arithmetic,
+file manipulation,
+and some specialized string processing functions.
+.PP
+The basic operation of M4
+is to copy its input to its output.
+As the input is read, however, each alphanumeric ``token''
+(that is, string of letters and digits) is checked.
+If it is the name of a macro,
+then the name of the macro is replaced by its defining text,
+and the resulting string is pushed back onto the
+input to be rescanned.
+Macros may be called with arguments, in which case the arguments are collected
+and substituted into the right places in the defining text
+before it is rescanned.
+.PP
+M4 provides a collection of about twenty built-in
+macros
+which perform various useful operations;
+in addition, the user can define new macros.
+Built-ins and user-defined macros work exactly the same way, except that
+some of the built-in macros have side effects
+on the state of the process.
+.SH
+Usage
+.PP
+On
+.UC UNIX ,
+use
+.P1
+m4 [files]
+.P2
+Each argument file is processed in order;
+if there are no arguments, or if an argument
+is `\-',
+the standard input is read at that point.
+The processed text is written on the standard output,
+which may be captured for subsequent processing with
+.P1
+m4 [files] >outputfile
+.P2
+On
+.UC GCOS ,
+usage is identical, but the program is called
+.UL \&./m4 .
+.SH
+Defining Macros
+.PP
+The primary built-in function of M4
+is
+.UL define ,
+which is used to define new macros.
+The input
+.P1
+define(name, stuff)
+.P2
+causes the string
+.UL name
+to be defined as
+.UL stuff .
+All subsequent occurrences of
+.UL name
+will be replaced by
+.UL stuff .
+.UL name
+must be alphanumeric and must begin with a letter
+(the underscore \(ul counts as a letter).
+.UL stuff
+is any text that contains balanced parentheses;
+it may stretch over multiple lines.
+.PP
+Thus, as a typical example,
+.P1
+define(N, 100)
+ ...
+if (i > N)
+.P2
+defines
+.UL N
+to be 100, and uses this ``symbolic constant'' in a later
+.UL if
+statement.
+.PP
+The left parenthesis must immediately follow the word
+.UL define ,
+to signal that
+.UL define
+has arguments.
+If a macro or built-in name is not followed immediately by `(',
+it is assumed to have no arguments.
+This is the situation for
+.UL N
+above;
+it is actually a macro with no arguments,
+and thus when it is used there need be no (...) following it.
+.PP
+You should also notice that a macro name is only recognized as such
+if it appears surrounded by non-alphanumerics.
+For example, in
+.P1
+define(N, 100)
+ ...
+if (NNN > 100)
+.P2
+the variable
+.UL NNN
+is absolutely unrelated to the defined macro
+.UL N ,
+even though it contains a lot of
+.UL N 's.
+.PP
+Things may be defined in terms of other things.
+For example,
+.P1
+define(N, 100)
+define(M, N)
+.P2
+defines both M and N to be 100.
+.PP
+What happens if
+.UL N
+is redefined?
+Or, to say it another way, is
+.UL M
+defined as
+.UL N
+or as 100?
+In M4,
+the latter is true _
+.UL M
+is 100, so even if
+.UL N
+subsequently changes,
+.UL M
+does not.
+.PP
+This behavior arises because
+M4 expands macro names into their defining text as soon as it possibly can.
+Here, that means that when the string
+.UL N
+is seen as the arguments of
+.UL define
+are being collected, it is immediately replaced by 100;
+it's just as if you had said
+.P1
+define(M, 100)
+.P2
+in the first place.
+.PP
+If this isn't what you really want, there are two ways out of it.
+The first, which is specific to this situation,
+is to interchange the order of the definitions:
+.P1
+define(M, N)
+define(N, 100)
+.P2
+Now
+.UL M
+is defined to be the string
+.UL N ,
+so when you ask for
+.UL M
+later, you'll always get the value of
+.UL N
+at that time
+(because the
+.UL M
+will be replaced by
+.UL N
+which will be replaced by 100).
+.SH
+Quoting
+.PP
+The more general solution is to delay the expansion of
+the arguments of
+.UL define
+by
+.ul
+quoting
+them.
+Any text surrounded by the single quotes \(ga and \(aa
+is not expanded immediately, but has the quotes stripped off.
+If you say
+.P1
+define(N, 100)
+define(M, `N')
+.P2
+the quotes around the
+.UL N
+are stripped off as the argument is being collected,
+but they have served their purpose, and
+.UL M
+is defined as
+the string
+.UL N ,
+not 100.
+The general rule is that M4 always strips off
+one level of single quotes whenever it evaluates
+something.
+This is true even outside of
+macros.
+If you want the word
+.UL define
+to appear in the output,
+you have to quote it in the input,
+as in
+.P1
+ `define' = 1;
+.P2
+.PP
+As another instance of the same thing, which is a bit more surprising,
+consider redefining
+.UL N :
+.P1
+define(N, 100)
+ ...
+define(N, 200)
+.P2
+Perhaps regrettably, the
+.UL N
+in the second definition is
+evaluated as soon as it's seen;
+that is, it is
+replaced by
+100, so it's as if you had written
+.P1
+define(100, 200)
+.P2
+This statement is ignored by M4, since you can only define things that look
+like names, but it obviously doesn't have the effect you wanted.
+To really redefine
+.UL N ,
+you must delay the evaluation by quoting:
+.P1
+define(N, 100)
+ ...
+define(`N', 200)
+.P2
+In M4,
+it is often wise to quote the first argument of a macro.
+.PP
+If \` and \' are not convenient for some reason,
+the quote characters can be changed with the built-in
+.UL changequote :
+.P1
+changequote([, ])
+.P2
+makes the new quote characters the left and right brackets.
+You can restore the original characters with just
+.P1
+changequote
+.P2
+.PP
+There are two additional built-ins related to
+.UL define .
+.UL undefine
+removes the definition of some macro or built-in:
+.P1
+undefine(`N')
+.P2
+removes the definition of
+.UL N .
+(Why are the quotes absolutely necessary?)
+Built-ins can be removed with
+.UL undefine ,
+as in
+.P1
+undefine(`define')
+.P2
+but once you remove one, you can never get it back.
+.PP
+The built-in
+.UL ifdef
+provides a way to determine if a macro is currently defined.
+In particular, M4 has pre-defined the names
+.UL unix
+and
+.UL gcos
+on the corresponding systems, so you can
+tell which one you're using:
+.P1
+ifdef(`unix', `define(wordsize,16)' )
+ifdef(`gcos', `define(wordsize,36)' )
+.P2
+makes a definition appropriate for the particular machine.
+Don't forget the quotes!
+.PP
+.UL ifdef
+actually permits three arguments;
+if the name is undefined, the value of
+.UL ifdef
+is then the third argument, as in
+.P1
+ifdef(`unix', on UNIX, not on UNIX)
+.P2
+.SH
+Arguments
+.PP
+So far we have discussed the simplest form of macro processing _
+replacing one string by another (fixed) string.
+User-defined macros may also have arguments, so different invocations
+can have different results.
+Within the replacement text for a macro
+(the second argument of its
+.UL define )
+any occurrence of
+.UL $n
+will be replaced by the
+.UL n th
+argument when the macro
+is actually used.
+Thus, the macro
+.UL bump ,
+defined as
+.P1
+define(bump, $1 = $1 + 1)
+.P2
+generates code to increment its argument by 1:
+.P1
+bump(x)
+.P2
+is
+.P1
+x = x + 1
+.P2
+.PP
+A macro can have as many arguments as you want,
+but only the first nine are accessible,
+through
+.UL $1
+to
+.UL $9 .
+(The macro name itself is
+.UL $0 ,
+although that is less commonly used.)
+Arguments that are not supplied are replaced by null strings,
+so
+we can define a macro
+.UL cat
+which simply concatenates its arguments, like this:
+.P1
+define(cat, $1$2$3$4$5$6$7$8$9)
+.P2
+Thus
+.P1
+cat(x, y, z)
+.P2
+is equivalent to
+.P1
+xyz
+.P2
+.UL $4
+through
+.UL $9
+are null, since no corresponding arguments were provided.
+.PP
+.PP
+Leading unquoted blanks, tabs, or newlines that occur during argument collection
+are discarded.
+All other white space is retained.
+Thus
+.P1
+define(a, b c)
+.P2
+defines
+.UL a
+to be
+.UL b\ \ \ c .
+.PP
+Arguments are separated by commas, but parentheses are counted properly,
+so a comma ``protected'' by parentheses does not terminate an argument.
+That is, in
+.P1
+define(a, (b,c))
+.P2
+there are only two arguments;
+the second is literally
+.UL (b,c) .
+And of course a bare comma or parenthesis can be inserted by quoting it.
+.SH
+Arithmetic Built-ins
+.PP
+M4 provides two built-in functions for doing arithmetic
+on integers (only).
+The simplest is
+.UL incr ,
+which increments its numeric argument by 1.
+Thus to handle the common programming situation
+where you want a variable to be defined as ``one more than N'',
+write
+.P1
+define(N, 100)
+define(N1, `incr(N)')
+.P2
+Then
+.UL N1
+is defined as one more than the current value of
+.UL N .
+.PP
+The more general mechanism for arithmetic is a built-in
+called
+.UL eval ,
+which is capable of arbitrary arithmetic on integers.
+It provides the operators
+(in decreasing order of precedence)
+.DS
+unary + and \(mi
+** or ^ (exponentiation)
+* / % (modulus)
++ \(mi
+== != < <= > >=
+! (not)
+& or && (logical and)
+\(or or \(or\(or (logical or)
+.DE
+Parentheses may be used to group operations where needed.
+All the operands of
+an expression given to
+.UL eval
+must ultimately be numeric.
+The numeric value of a true relation
+(like 1>0)
+is 1, and false is 0.
+The precision in
+.UL eval
+is
+32 bits on
+.UC UNIX
+and 36 bits on
+.UC GCOS .
+.PP
+As a simple example, suppose we want
+.UL M
+to be
+.UL 2**N+1 .
+Then
+.P1
+define(N, 3)
+define(M, `eval(2**N+1)')
+.P2
+As a matter of principle, it is advisable
+to quote the defining text for a macro
+unless it is very simple indeed
+(say just a number);
+it usually gives the result you want,
+and is a good habit to get into.
+.SH
+File Manipulation
+.PP
+You can include a new file in the input at any time by
+the built-in function
+.UL include :
+.P1
+include(filename)
+.P2
+inserts the contents of
+.UL filename
+in place of the
+.UL include
+command.
+The contents of the file is often a set of definitions.
+The value
+of
+.UL include
+(that is, its replacement text)
+is the contents of the file;
+this can be captured in definitions, etc.
+.PP
+It is a fatal error if the file named in
+.UL include
+cannot be accessed.
+To get some control over this situation, the alternate form
+.UL sinclude
+can be used;
+.UL sinclude
+(``silent include'')
+says nothing and continues if it can't access the file.
+.PP
+It is also possible to divert the output of M4 to temporary files during processing,
+and output the collected material upon command.
+M4 maintains nine of these diversions, numbered 1 through 9.
+If you say
+.P1
+divert(n)
+.P2
+all subsequent output is put onto the end of a temporary file
+referred to as
+.UL n .
+Diverting to this file is stopped by another
+.UL divert
+command;
+in particular,
+.UL divert
+or
+.UL divert(0)
+resumes the normal output process.
+.PP
+Diverted text is normally output all at once
+at the end of processing,
+with the diversions output in numeric order.
+It is possible, however, to bring back diversions
+at any time,
+that is, to append them to the current diversion.
+.P1
+undivert
+.P2
+brings back all diversions in numeric order, and
+.UL undivert
+with arguments brings back the selected diversions
+in the order given.
+The act of undiverting discards the diverted stuff,
+as does diverting into a diversion
+whose number is not between 0 and 9 inclusive.
+.PP
+The value of
+.UL undivert
+is
+.ul
+not
+the diverted stuff.
+Furthermore, the diverted material is
+.ul
+not
+rescanned for macros.
+.PP
+The built-in
+.UL divnum
+returns the number of the currently active diversion.
+This is zero during normal processing.
+.SH
+System Command
+.PP
+You can run any program in the local operating system
+with the
+.UL syscmd
+built-in.
+For example,
+.P1
+syscmd(date)
+.P2
+on
+.UC UNIX
+runs the
+.UL date
+command.
+Normally
+.UL syscmd
+would be used to create a file
+for a subsequent
+.UL include .
+.PP
+To facilitate making unique file names, the built-in
+.UL maketemp
+is provided, with specifications identical to the system function
+.ul
+mktemp:
+a string of XXXXX in the argument is replaced
+by the process id of the current process.
+.SH
+Conditionals
+.PP
+There is a built-in called
+.UL ifelse
+which enables you to perform arbitrary conditional testing.
+In the simplest form,
+.P1
+ifelse(a, b, c, d)
+.P2
+compares the two strings
+.UL a
+and
+.UL b .
+If these are identical,
+.UL ifelse
+returns
+the string
+.UL c ;
+otherwise it returns
+.UL d .
+Thus we might define a macro called
+.UL compare
+which compares two strings and returns ``yes'' or ``no''
+if they are the same or different.
+.P1
+define(compare, `ifelse($1, $2, yes, no)')
+.P2
+Note the quotes,
+which prevent too-early evaluation of
+.UL ifelse .
+.PP
+If the fourth argument is missing, it is treated as empty.
+.PP
+.UL ifelse
+can actually have any number of arguments,
+and thus provides a limited form of multi-way decision capability.
+In the input
+.P1
+ifelse(a, b, c, d, e, f, g)
+.P2
+if the string
+.UL a
+matches the string
+.UL b ,
+the result is
+.UL c .
+Otherwise, if
+.UL d
+is the same as
+.UL e ,
+the result is
+.UL f .
+Otherwise the result is
+.UL g .
+If the final argument
+is omitted, the result is null,
+so
+.P1
+ifelse(a, b, c)
+.P2
+is
+.UL c
+if
+.UL a
+matches
+.UL b ,
+and null otherwise.
+.SH
+String Manipulation
+.PP
+The built-in
+.UL len
+returns the length of the string that makes up its argument.
+Thus
+.P1
+len(abcdef)
+.P2
+is 6, and
+.UL len((a,b))
+is 5.
+.PP
+The built-in
+.UL substr
+can be used to produce substrings of strings.
+.UL substr(s,\ i,\ n)
+returns the substring of
+.UL s
+that starts at the
+.UL i th
+position
+(origin zero),
+and is
+.UL n
+characters long.
+If
+.UL n
+is omitted, the rest of the string is returned,
+so
+.P1
+substr(`now is the time', 1)
+.P2
+is
+.P1
+ow is the time
+.P2
+If
+.UL i
+or
+.UL n
+are out of range, various sensible things happen.
+.PP
+.UL index(s1,\ s2)
+returns the index (position) in
+.UL s1
+where the string
+.UL s2
+occurs, or \-1
+if it doesn't occur.
+As with
+.UL substr ,
+the origin for strings is 0.
+.PP
+The built-in
+.UL translit
+performs character transliteration.
+.P1
+translit(s, f, t)
+.P2
+modifies
+.UL s
+by replacing any character found in
+.UL f
+by the corresponding character of
+.UL t .
+That is,
+.P1
+translit(s, aeiou, 12345)
+.P2
+replaces the vowels by the corresponding digits.
+If
+.UL t
+is shorter than
+.UL f ,
+characters which don't have an entry in
+.UL t
+are deleted; as a limiting case,
+if
+.UL t
+is not present at all,
+characters from
+.UL f
+are deleted from
+.UL s .
+So
+.P1
+translit(s, aeiou)
+.P2
+deletes vowels from
+.UL s .
+.PP
+There is also a built-in called
+.UL dnl
+which deletes all characters that follow it up to
+and including the next newline;
+it is useful mainly for throwing away
+empty lines that otherwise tend to clutter up M4 output.
+For example, if you say
+.P1
+define(N, 100)
+define(M, 200)
+define(L, 300)
+.P2
+the newline at the end of each line is not part of the definition,
+so it is copied into the output, where it may not be wanted.
+If you add
+.UL dnl
+to each of these lines, the newlines will disappear.
+.PP
+Another way to achieve this, due to J. E. Weythman,
+is
+.P1
+divert(-1)
+ define(...)
+ ...
+divert
+.P2
+.SH
+Printing
+.PP
+The built-in
+.UL errprint
+writes its arguments out on the standard error file.
+Thus you can say
+.P1
+errprint(`fatal error')
+.P2
+.PP
+.UL dumpdef
+is a debugging aid which
+dumps the current definitions of defined terms.
+If there are no arguments, you get everything;
+otherwise you get the ones you name as arguments.
+Don't forget to quote the names!
+.SH
+Summary of Built-ins
+.PP
+Each entry is preceded by the
+page number where it is described.
+.DS
+.tr '\'`\`
+.ta .25i
+3 changequote(L, R)
+1 define(name, replacement)
+4 divert(number)
+4 divnum
+5 dnl
+5 dumpdef(`name', `name', ...)
+5 errprint(s, s, ...)
+4 eval(numeric expression)
+3 ifdef(`name', this if true, this if false)
+5 ifelse(a, b, c, d)
+4 include(file)
+3 incr(number)
+5 index(s1, s2)
+5 len(string)
+4 maketemp(...XXXXX...)
+4 sinclude(file)
+5 substr(string, position, number)
+4 syscmd(s)
+5 translit(str, from, to)
+3 undefine(`name')
+4 undivert(number,number,...)
+.DE
+.SH
+Acknowledgements
+.PP
+We are indebted to Rick Becker, John Chambers,
+Doug McIlroy,
+and especially Jim Weythman,
+whose pioneering use of M4 has led to several valuable improvements.
+We are also deeply grateful to Weythman for several substantial contributions
+to the code.
+.SG
+.SH
+References
+.LP
+.IP [1]
+B. W. Kernighan and P. J. Plauger,
+.ul
+Software Tools,
+Addison-Wesley, Inc., 1976.
diff --git a/usr.bin/m4/TEST/CVS/Entries b/usr.bin/m4/TEST/CVS/Entries
new file mode 100644
index 0000000..390dc32
--- /dev/null
+++ b/usr.bin/m4/TEST/CVS/Entries
@@ -0,0 +1,7 @@
+/ack.m4/1.3/Tue Jun 3 02:56:11 2003//
+/hanoi.m4/1.3/Tue Jun 3 02:56:11 2003//
+/hash.m4/1.3/Tue Jun 3 02:56:11 2003//
+/sqroot.m4/1.3/Tue Jun 3 02:56:11 2003//
+/string.m4/1.3/Tue Jun 3 02:56:11 2003//
+/test.m4/1.3/Tue Jun 3 02:56:11 2003//
+D
diff --git a/usr.bin/m4/TEST/CVS/Repository b/usr.bin/m4/TEST/CVS/Repository
new file mode 100644
index 0000000..dbd70ae
--- /dev/null
+++ b/usr.bin/m4/TEST/CVS/Repository
@@ -0,0 +1 @@
+src/usr.bin/m4/TEST
diff --git a/usr.bin/m4/TEST/CVS/Root b/usr.bin/m4/TEST/CVS/Root
new file mode 100644
index 0000000..3811072
--- /dev/null
+++ b/usr.bin/m4/TEST/CVS/Root
@@ -0,0 +1 @@
+/cvs
diff --git a/usr.bin/m4/TEST/ack.m4 b/usr.bin/m4/TEST/ack.m4
new file mode 100644
index 0000000..632e207
--- /dev/null
+++ b/usr.bin/m4/TEST/ack.m4
@@ -0,0 +1,38 @@
+# $OpenBSD: ack.m4,v 1.3 2003/06/03 02:56:11 millert Exp $
+# $NetBSD: ack.m4,v 1.4 1995/09/28 05:37:54 tls Exp $
+#
+# Copyright (c) 1989, 1993
+# 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. 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 8.1 (Berkeley) 6/6/93
+#
+
+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
new file mode 100644
index 0000000..008ac14
--- /dev/null
+++ b/usr.bin/m4/TEST/hanoi.m4
@@ -0,0 +1,43 @@
+# $OpenBSD: hanoi.m4,v 1.3 2003/06/03 02:56:11 millert Exp $
+# $NetBSD: hanoi.m4,v 1.4 1995/09/28 05:37:56 tls Exp $
+#
+# Copyright (c) 1989, 1993
+# 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. 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 8.1 (Berkeley) 6/6/93
+#
+
+define(hanoi, `trans(A, B, C, $1)')
+
+define(moved,`move disk from $1 to $2
+')
+
+define(trans, `ifelse($4,1,`moved($1,$2)',
+ `trans($1,$3,$2,DECR($4))moved($1,$2)trans($3,$2,$1,DECR($4))')')
diff --git a/usr.bin/m4/TEST/hash.m4 b/usr.bin/m4/TEST/hash.m4
new file mode 100644
index 0000000..f46eb9e
--- /dev/null
+++ b/usr.bin/m4/TEST/hash.m4
@@ -0,0 +1,53 @@
+# $OpenBSD: hash.m4,v 1.3 2003/06/03 02:56:11 millert Exp $
+# $NetBSD: hash.m4,v 1.4 1995/09/28 05:37:58 tls Exp $
+#
+# Copyright (c) 1989, 1993
+# 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. 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 8.1 (Berkeley) 6/6/93
+#
+
+dnl This probably will not run on any m4 that cannot
+dnl handle char constants in eval.
+dnl
+changequote(<,>) define(HASHVAL,99) dnl
+define(hash,<eval(str(substr($1,1),0)%HASHVAL)>) dnl
+define(str,
+ <ifelse($1,",$2,
+ <str(substr(<$1>,1),<eval($2+'substr($1,0,1)')>)>)
+ >) dnl
+define(KEYWORD,<$1,hash($1),>) dnl
+define(TSTART,
+<struct prehash {
+ char *keyword;
+ int hashval;
+} keytab[] = {>) dnl
+define(TEND,< "",0
+};>) dnl
diff --git a/usr.bin/m4/TEST/sqroot.m4 b/usr.bin/m4/TEST/sqroot.m4
new file mode 100644
index 0000000..fa01674
--- /dev/null
+++ b/usr.bin/m4/TEST/sqroot.m4
@@ -0,0 +1,43 @@
+# $OpenBSD: sqroot.m4,v 1.3 2003/06/03 02:56:11 millert Exp $
+# $NetBSD: sqroot.m4,v 1.4 1995/09/28 05:38:01 tls Exp $
+#
+# Copyright (c) 1989, 1993
+# 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. 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 8.1 (Berkeley) 6/6/93
+#
+
+define(square_root,
+ `ifelse(eval($1<0),1,negative-square-root,
+ `square_root_aux($1, 1, eval(($1+1)/2))')')
+define(square_root_aux,
+ `ifelse($3, $2, $3,
+ $3, eval($1/$2), $3,
+ `square_root_aux($1, $3, eval(($3+($1/$3))/2))')')
diff --git a/usr.bin/m4/TEST/string.m4 b/usr.bin/m4/TEST/string.m4
new file mode 100644
index 0000000..32d3a71
--- /dev/null
+++ b/usr.bin/m4/TEST/string.m4
@@ -0,0 +1,43 @@
+# $OpenBSD: string.m4,v 1.3 2003/06/03 02:56:11 millert Exp $
+# $NetBSD: string.m4,v 1.4 1995/09/28 05:38:03 tls Exp $
+#
+# Copyright (c) 1989, 1993
+# 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. 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 8.1 (Berkeley) 6/6/93
+#
+
+define(string,`integer $1(len(substr($2,1)))
+str($1,substr($2,1),0)
+data $1(len(substr($2,1)))/EOS/
+')
+
+define(str,`ifelse($2,",,data $1(incr($3))/`LET'substr($2,0,1)/
+`str($1,substr($2,1),incr($3))')')
diff --git a/usr.bin/m4/TEST/test.m4 b/usr.bin/m4/TEST/test.m4
new file mode 100644
index 0000000..dd20317
--- /dev/null
+++ b/usr.bin/m4/TEST/test.m4
@@ -0,0 +1,241 @@
+# $OpenBSD: test.m4,v 1.3 2003/06/03 02:56:11 millert Exp $
+# $NetBSD: test.m4,v 1.4 1995/09/28 05:38:05 tls Exp $
+#
+# Copyright (c) 1989, 1993
+# 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. 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 8.1 (Berkeley) 6/6/93
+#
+
+# test file for mp (not comprehensive)
+#
+# v7 m4 does not have `decr'.
+#
+define(DECR,`eval($1-1)')
+#
+# include string macros
+#
+include(string.m4)
+#
+# create some fortrash strings for an even uglier language
+#
+string(TEXT, "text")
+string(DATA, "data")
+string(BEGIN, "begin")
+string(END, "end")
+string(IF, "if")
+string(THEN, "then")
+string(ELSE, "else")
+string(CASE, "case")
+string(REPEAT, "repeat")
+string(WHILE, "while")
+string(DEFAULT, "default")
+string(UNTIL, "until")
+string(FUNCTION, "function")
+string(PROCEDURE, "procedure")
+string(EXTERNAL, "external")
+string(FORWARD, "forward")
+string(TYPE, "type")
+string(VAR, "var")
+string(CONST, "const")
+string(PROGRAM, "program")
+string(INPUT, "input")
+string(OUTPUT, "output")
+#
+divert(2)
+diversion #1
+divert(3)
+diversion #2
+divert(4)
+diversion #3
+divert(5)
+diversion #4
+divert(0)
+define(abc,xxx)
+ifdef(`abc',defined,undefined)
+#
+# v7 m4 does this wrong. The right output is
+# this is A vEry lon sEntEnCE
+# see m4 documentation for translit.
+#
+translit(`this is a very long sentence', abcdefg, ABCDEF)
+#
+# include towers-of-hanoi
+#
+include(hanoi.m4)
+#
+# some reasonable set of disks
+#
+hanoi(6)
+#
+# include ackermann's function
+#
+include(ack.m4)
+#
+# something like (3,3) will blow away un*x m4.
+#
+ack(2,3)
+#
+# include a square_root function for fixed nums
+#
+include(sqroot.m4)
+#
+# some square roots.
+#
+square_root(15)
+square_root(100)
+square_root(-4)
+square_root(21372)
+#
+# some textual material for enjoyment.
+#
+[taken from the 'Clemson University Computer Newsletter',
+ September 1981, pp. 6-7]
+
+I am a wizard in the magical Kingdom of Transformation and I
+slay dragons for a living. Actually, I am a systems programmer.
+One of the problems with systems programming is explaining to
+non-computer enthusiasts what that is. All of the terms I use to
+describe my job are totally meaningless to them. Usually my response
+to questions about my work is to say as little as possible. For
+instance, if someone asks what happened at work this week, I say
+"Nothing much" and then I change the subject.
+
+With the assistance of my brother, a mechanical engineer, I have devised
+an analogy that everyone can understand. The analogy describes the
+"Kingdom of Transformation" where travelers wander and are magically
+transformed. This kingdom is the computer and the travelers are information.
+The purpose of the computer is to change information to a more meaningful
+forma. The law of conservation applies here: The computer never creates
+and never intentionally destroys data. With no further ado, let us travel
+to the Kingdom of Transformation:
+
+In a land far, far away, there is a magical kingdom called the Kingdom of
+Transformation. A king rules over this land and employs a Council of
+Wizardry. The main purpose of this kingdom is to provide a way for
+neighboring kingdoms to transform citizens into more useful citizens. This
+is done by allowing the citizens to enter the kingdom at one of its ports
+and to travel any of the many routes in the kingdom. They are magically
+transformed along the way. The income of the Kingdom of Transformation
+comes from the many toll roads within its boundaries.
+
+The Kingdom of Transformation was created when several kingdoms got
+together and discovered a mutual need for new talents and abilities for
+citizens. They employed CTK, Inc. (Creators of Transformation, Inc.) to
+create this kingdom. CTK designed the country, its transportation routes,
+and its laws of transformation, and created the major highway system.
+
+Hazards
+=======
+
+Because magic is not truly controllable, CTK invariably, but unknowingly,
+creates dragons. Dragons are huge fire-breathing beasts which sometimes
+injure or kill travelers. Fortunately, they do not travel, but always
+remain near their den.
+
+Other hazards also exist which are potentially harmful. As the roads
+become older and more weatherbeaten, pot-holes will develop, trees will
+fall on travelers, etc. CTK maintenance men are called to fix these
+problems.
+
+Wizards
+=======
+
+The wizards play a major role in creating and maintaining the kingdom but
+get little credit for their work because it is performed secretly. The
+wizards do not wan the workers or travelers to learn their incantations
+because many laws would be broken and chaos would result.
+
+CTK's grand design is always general enough to be applicable in many
+different situations. As a result, it is often difficult to use. The
+first duty of the wizards is to tailor the transformation laws so as to be
+more beneficial and easier to use in their particular environment.
+
+After creation of the kingdom, a major duty of the wizards is to search for
+and kill dragons. If travelers do not return on time or if they return
+injured, the ruler of the country contacts the wizards. If the wizards
+determine that the injury or death occurred due to the traveler's
+negligence, they provide the traveler's country with additional warnings.
+If not, they must determine if the cause was a road hazard or a dragon. If
+the suspect a road hazard, they call in a CTK maintenance man to locate the
+hazard and to eliminate it, as in repairing the pothole in the road. If
+they think that cause was a dragon, then they must find and slay it.
+
+The most difficult part of eliminating a dragon is finding it. Sometimes
+the wizard magically knows where the dragon's lair it, but often the wizard
+must send another traveler along the same route and watch to see where he
+disappears. This sounds like a failsafe method for finding dragons (and a
+suicide mission for thr traveler) but the second traveler does not always
+disappear. Some dragons eat any traveler who comes too close; others are
+very picky.
+
+The wizards may call in CTK who designed the highway system and
+transformation laws to help devise a way to locate the dragon. CTK also
+helps provide the right spell or incantation to slay the dragon. (There is
+no general spell to slay dragons; each dragon must be eliminated with a
+different spell.)
+
+Because neither CTK nor wizards are perfect, spells to not always work
+correctly. At best, nothing happens when the wrong spell is uttered. At
+worst, the dragon becomes a much larger dragon or multiplies into several
+smaller ones. In either case, new spells must be found.
+
+If all existing dragons are quiet (i.e. have eaten sufficiently), wizards
+have time to do other things. They hide in castles and practice spells and
+incatations. They also devise shortcuts for travelers and new laws of
+transformation.
+
+Changes in the Kingdom
+======================
+
+As new transformation kingdoms are created and old ones are maintained,
+CTK, Inc. is constantly learning new things. It learns ways to avoid
+creating some of the dragons that they have previously created. It also
+discovers new and better laws of transformation. As a result, CTK will
+periodically create a new grand design which is far better than the old.
+The wizards determine when is a good time to implement this new design.
+This is when the tourist season is slow or when no important travelers
+(VIPs) are to arrive. The kingdom must be closed for the actual
+implementation and is leter reopened as a new and better place to go.
+
+A final question you might ask is what happens when the number of tourists
+becomes too great for the kingdom to handle in a reasonable period of time
+(i.e., the tourist lines at the ports are too long). The Kingdom of
+Transformation has three options: (1) shorten the paths that a tourist must
+travel, or (2) convince CTK to develop a faster breed of horses so that the
+travelers can finish sooner, or (3) annex more territories so that the
+kingdom can handle more travelers.
+
+Thus ends the story of the Kingdom of Transformation. I hope this has
+explained my job to you: I slay dragons for a living.
+
+#
+#should do an automatic undivert..
+#
diff --git a/usr.bin/m4/eval.c b/usr.bin/m4/eval.c
new file mode 100644
index 0000000..fe9fbde
--- /dev/null
+++ b/usr.bin/m4/eval.c
@@ -0,0 +1,1033 @@
+/* $OpenBSD: eval.c,v 1.78 2019/06/28 05:35:34 deraadt Exp $ */
+/* $NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the 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.
+ */
+
+/*
+ * eval.c
+ * Facility: m4 macro processor
+ * by: oz
+ */
+
+#include <sys/types.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h>
+#include "mdef.h"
+#include "stdd.h"
+#include "extern.h"
+#include "pathnames.h"
+
+static void dodefn(const char *);
+static void dopushdef(const char *, const char *);
+static void dodump(const char *[], int);
+static void dotrace(const char *[], int, int);
+static void doifelse(const char *[], int);
+static int doincl(const char *);
+#ifdef EXTENDED
+static int dopaste(const char *);
+#endif
+static void dochq(const char *[], int);
+static void dochc(const char *[], int);
+static void dom4wrap(const char *);
+static void dodiv(int);
+static void doundiv(const char *[], int);
+static void dosub(const char *[], int);
+static void map(char *, const char *, const char *, const char *);
+static const char *handledash(char *, char *, const char *);
+static void expand_builtin(const char *[], int, int);
+static void expand_macro(const char *[], int);
+static void dump_one_def(const char *, struct macro_definition *);
+
+unsigned long expansion_id;
+
+/*
+ * eval - eval all macros and builtins calls
+ * argc - number of elements in argv.
+ * argv - element vector :
+ * argv[0] = definition of a user
+ * macro or NULL if built-in.
+ * argv[1] = name of the macro or
+ * built-in.
+ * argv[2] = parameters to user-defined
+ * . macro or built-in.
+ * .
+ *
+ * A call in the form of macro-or-builtin() will result in:
+ * argv[0] = nullstr
+ * argv[1] = macro-or-builtin
+ * argv[2] = nullstr
+ *
+ * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
+ */
+void
+eval(const char *argv[], int argc, int td, int is_traced)
+{
+ size_t mark = SIZE_MAX;
+
+ expansion_id++;
+ if (td & RECDEF)
+ m4errx(1, "expanding recursive definition for %s.", argv[1]);
+ if (is_traced)
+ mark = trace(argv, argc, infile+ilevel);
+ if (td == MACRTYPE)
+ expand_macro(argv, argc);
+ else
+ expand_builtin(argv, argc, td);
+ if (mark != SIZE_MAX)
+ finish_trace(mark);
+}
+
+/*
+ * expand_builtin - evaluate built-in macros.
+ */
+void
+expand_builtin(const char *argv[], int argc, int td)
+{
+ int c, n;
+ const char *errstr;
+ int ac;
+ static int sysval = 0;
+
+#ifdef DEBUG
+ printf("argc = %d\n", argc);
+ for (n = 0; n < argc; n++)
+ printf("argv[%d] = %s\n", n, argv[n]);
+ fflush(stdout);
+#endif
+
+ /*
+ * if argc == 3 and argv[2] is null, then we
+ * have macro-or-builtin() type call. We adjust
+ * argc to avoid further checking..
+ */
+ /* we keep the initial value for those built-ins that differentiate
+ * between builtin() and builtin.
+ */
+ ac = argc;
+
+ if (argc == 3 && !*(argv[2]) && !mimic_gnu)
+ argc--;
+
+ switch (td & TYPEMASK) {
+
+ case DEFITYPE:
+ if (argc > 2)
+ dodefine(argv[2], (argc > 3) ? argv[3] : null);
+ break;
+
+ case PUSDTYPE:
+ if (argc > 2)
+ dopushdef(argv[2], (argc > 3) ? argv[3] : null);
+ break;
+
+ case DUMPTYPE:
+ dodump(argv, argc);
+ break;
+
+ case TRACEONTYPE:
+ dotrace(argv, argc, 1);
+ break;
+
+ case TRACEOFFTYPE:
+ dotrace(argv, argc, 0);
+ break;
+
+ case EXPRTYPE:
+ /*
+ * doexpr - evaluate arithmetic
+ * expression
+ */
+ {
+ int base = 10;
+ int maxdigits = 0;
+
+ if (argc > 3) {
+ base = strtonum(argv[3], 2, 36, &errstr);
+ if (errstr) {
+ m4errx(1, "expr: base is %s: %s.",
+ errstr, argv[3]);
+ }
+ }
+ if (argc > 4) {
+ maxdigits = strtonum(argv[4], 0, INT_MAX, &errstr);
+ if (errstr) {
+ m4errx(1, "expr: maxdigits is %s: %s.",
+ errstr, argv[4]);
+ }
+ }
+ if (argc > 2)
+ pbnumbase(expr(argv[2]), base, maxdigits);
+ break;
+ }
+
+ case IFELTYPE:
+ doifelse(argv, argc);
+ break;
+
+ case IFDFTYPE:
+ /*
+ * doifdef - select one of two
+ * alternatives based on the existence of
+ * another definition
+ */
+ if (argc > 3) {
+ if (lookup_macro_definition(argv[2]) != NULL)
+ pbstr(argv[3]);
+ else if (argc > 4)
+ pbstr(argv[4]);
+ }
+ break;
+
+ case LENGTYPE:
+ /*
+ * dolen - find the length of the
+ * argument
+ */
+ pbnum((argc > 2) ? strlen(argv[2]) : 0);
+ break;
+
+ case INCRTYPE:
+ /*
+ * doincr - increment the value of the
+ * argument
+ */
+ if (argc > 2) {
+ n = strtonum(argv[2], INT_MIN, INT_MAX-1, &errstr);
+ if (errstr != NULL)
+ m4errx(1, "incr: argument is %s: %s.",
+ errstr, argv[2]);
+ pbnum(n + 1);
+ }
+ break;
+
+ case DECRTYPE:
+ /*
+ * dodecr - decrement the value of the
+ * argument
+ */
+ if (argc > 2) {
+ n = strtonum(argv[2], INT_MIN+1, INT_MAX, &errstr);
+ if (errstr)
+ m4errx(1, "decr: argument is %s: %s.",
+ errstr, argv[2]);
+ pbnum(n - 1);
+ }
+ break;
+
+ case SYSCTYPE:
+ /*
+ * dosys - execute system command
+ */
+ if (argc > 2) {
+ fflush(stdout);
+ sysval = system(argv[2]);
+ }
+ break;
+
+ case SYSVTYPE:
+ /*
+ * dosysval - return value of the last
+ * system call.
+ *
+ */
+ pbnum(sysval);
+ break;
+
+ case ESYSCMDTYPE:
+ if (argc > 2)
+ doesyscmd(argv[2]);
+ break;
+ case INCLTYPE:
+ if (argc > 2) {
+ if (!doincl(argv[2])) {
+ if (mimic_gnu) {
+ warn("%s at line %lu: include(%s)",
+ CURRENT_NAME, CURRENT_LINE, argv[2]);
+ exit_code = 1;
+ if (fatal_warns) {
+ killdiv();
+ exit(exit_code);
+ }
+ } else
+ err(1, "%s at line %lu: include(%s)",
+ CURRENT_NAME, CURRENT_LINE, argv[2]);
+ }
+ }
+ break;
+
+ case SINCTYPE:
+ if (argc > 2)
+ (void) doincl(argv[2]);
+ break;
+#ifdef EXTENDED
+ case PASTTYPE:
+ if (argc > 2)
+ if (!dopaste(argv[2]))
+ err(1, "%s at line %lu: paste(%s)",
+ CURRENT_NAME, CURRENT_LINE, argv[2]);
+ break;
+
+ case SPASTYPE:
+ if (argc > 2)
+ (void) dopaste(argv[2]);
+ break;
+ case FORMATTYPE:
+ doformat(argv, argc);
+ break;
+#endif
+ case CHNQTYPE:
+ dochq(argv, ac);
+ break;
+
+ case CHNCTYPE:
+ dochc(argv, argc);
+ break;
+
+ case SUBSTYPE:
+ /*
+ * dosub - select substring
+ *
+ */
+ if (argc > 3)
+ dosub(argv, argc);
+ break;
+
+ case SHIFTYPE:
+ /*
+ * doshift - push back all arguments
+ * except the first one (i.e. skip
+ * argv[2])
+ */
+ if (argc > 3) {
+ for (n = argc - 1; n > 3; n--) {
+ pbstr(rquote);
+ pbstr(argv[n]);
+ pbstr(lquote);
+ pushback(COMMA);
+ }
+ pbstr(rquote);
+ pbstr(argv[3]);
+ pbstr(lquote);
+ }
+ break;
+
+ case DIVRTYPE:
+ if (argc > 2) {
+ n = strtonum(argv[2], INT_MIN, INT_MAX, &errstr);
+ if (errstr)
+ m4errx(1, "divert: argument is %s: %s.",
+ errstr, argv[2]);
+ if (n != 0) {
+ dodiv(n);
+ break;
+ }
+ }
+ active = stdout;
+ oindex = 0;
+ break;
+
+ case UNDVTYPE:
+ doundiv(argv, argc);
+ break;
+
+ case DIVNTYPE:
+ /*
+ * dodivnum - return the number of
+ * current output diversion
+ */
+ pbnum(oindex);
+ break;
+
+ case UNDFTYPE:
+ /*
+ * doundefine - undefine a previously
+ * defined macro(s) or m4 keyword(s).
+ */
+ if (argc > 2)
+ for (n = 2; n < argc; n++)
+ macro_undefine(argv[n]);
+ break;
+
+ case POPDTYPE:
+ /*
+ * dopopdef - remove the topmost
+ * definitions of macro(s) or m4
+ * keyword(s).
+ */
+ if (argc > 2)
+ for (n = 2; n < argc; n++)
+ macro_popdef(argv[n]);
+ break;
+
+ case MKTMTYPE:
+ /*
+ * dotemp - create a temporary file
+ */
+ if (argc > 2) {
+ int fd;
+ char *temp;
+
+ temp = xstrdup(argv[2]);
+
+ fd = mkstemp(temp);
+ if (fd == -1)
+ err(1,
+ "%s at line %lu: couldn't make temp file %s",
+ CURRENT_NAME, CURRENT_LINE, argv[2]);
+ close(fd);
+ pbstr(temp);
+ free(temp);
+ }
+ break;
+
+ case TRNLTYPE:
+ /*
+ * dotranslit - 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;
+
+ temp = xalloc(strlen(argv[2])+1, NULL);
+ if (argc > 4)
+ map(temp, argv[2], argv[3], argv[4]);
+ else
+ map(temp, argv[2], argv[3], null);
+ pbstr(temp);
+ free(temp);
+ } else if (argc > 2)
+ pbstr(argv[2]);
+ break;
+
+ case INDXTYPE:
+ /*
+ * doindex - 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:
+ /*
+ * doerrp - print the arguments to stderr
+ * file
+ */
+ if (argc > 2) {
+ for (n = 2; n < argc; n++)
+ fprintf(stderr, "%s ", argv[n]);
+ fprintf(stderr, "\n");
+ }
+ break;
+
+ case DNLNTYPE:
+ /*
+ * dodnl - eat-up-to and including
+ * newline
+ */
+ while ((c = gpbc()) != '\n' && c != EOF)
+ ;
+ break;
+
+ case M4WRTYPE:
+ /*
+ * dom4wrap - set up for
+ * wrap-up/wind-down activity
+ */
+ if (argc > 2)
+ dom4wrap(argv[2]);
+ break;
+
+ case EXITTYPE:
+ /*
+ * doexit - immediate exit from m4.
+ */
+ killdiv();
+ exit((argc > 2) ? atoi(argv[2]) : 0);
+ break;
+
+ case DEFNTYPE:
+ if (argc > 2)
+ for (n = 2; n < argc; n++)
+ dodefn(argv[n]);
+ break;
+
+ case INDIRTYPE: /* Indirect call */
+ if (argc > 2)
+ doindir(argv, argc);
+ break;
+
+ case BUILTINTYPE: /* Builtins only */
+ if (argc > 2)
+ dobuiltin(argv, argc);
+ break;
+
+ case PATSTYPE:
+ if (argc > 2)
+ dopatsubst(argv, argc);
+ break;
+ case REGEXPTYPE:
+ if (argc > 2)
+ doregexp(argv, argc);
+ break;
+ case LINETYPE:
+ doprintlineno(infile+ilevel);
+ break;
+ case FILENAMETYPE:
+ doprintfilename(infile+ilevel);
+ break;
+ case SELFTYPE:
+ pbstr(rquote);
+ pbstr(argv[1]);
+ pbstr(lquote);
+ break;
+ default:
+ m4errx(1, "eval: major botch.");
+ break;
+ }
+}
+
+/*
+ * expand_macro - user-defined macro expansion
+ */
+void
+expand_macro(const char *argv[], int argc)
+{
+ const char *t;
+ const char *p;
+ int n;
+ int argno;
+
+ t = argv[0]; /* defn string as a whole */
+ p = t;
+ while (*p)
+ p++;
+ p--; /* last character of defn */
+ while (p > t) {
+ if (*(p - 1) != ARGFLAG)
+ PUSHBACK(*p);
+ else {
+ switch (*p) {
+
+ case '#':
+ pbnum(argc - 2);
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if ((argno = *p - '0') < argc - 1)
+ pbstr(argv[argno + 1]);
+ break;
+ case '*':
+ if (argc > 2) {
+ for (n = argc - 1; n > 2; n--) {
+ pbstr(argv[n]);
+ pushback(COMMA);
+ }
+ pbstr(argv[2]);
+ }
+ break;
+ case '@':
+ if (argc > 2) {
+ for (n = argc - 1; n > 2; n--) {
+ pbstr(rquote);
+ pbstr(argv[n]);
+ pbstr(lquote);
+ pushback(COMMA);
+ }
+ pbstr(rquote);
+ pbstr(argv[2]);
+ pbstr(lquote);
+ }
+ break;
+ default:
+ PUSHBACK(*p);
+ PUSHBACK('$');
+ break;
+ }
+ p--;
+ }
+ p--;
+ }
+ if (p == t) /* do last character */
+ PUSHBACK(*p);
+}
+
+
+/*
+ * dodefine - install definition in the table
+ */
+void
+dodefine(const char *name, const char *defn)
+{
+ if (!*name && !mimic_gnu)
+ m4errx(1, "null definition.");
+ else
+ macro_define(name, defn);
+}
+
+/*
+ * dodefn - push back a quoted definition of
+ * the given name.
+ */
+static void
+dodefn(const char *name)
+{
+ struct macro_definition *p;
+
+ if ((p = lookup_macro_definition(name)) != NULL) {
+ if ((p->type & TYPEMASK) == MACRTYPE) {
+ pbstr(rquote);
+ pbstr(p->defn);
+ pbstr(lquote);
+ } else {
+ pbstr(p->defn);
+ pbstr(BUILTIN_MARKER);
+ }
+ }
+}
+
+/*
+ * dopushdef - install a definition in the hash table
+ * without removing a previous definition. Since
+ * each new entry is entered in *front* of the
+ * hash bucket, it hides a previous definition from
+ * lookup.
+ */
+static void
+dopushdef(const char *name, const char *defn)
+{
+ if (!*name && !mimic_gnu)
+ m4errx(1, "null definition.");
+ else
+ macro_pushdef(name, defn);
+}
+
+/*
+ * dump_one_def - dump the specified definition.
+ */
+static void
+dump_one_def(const char *name, struct macro_definition *p)
+{
+ if (!traceout)
+ traceout = stderr;
+ if (mimic_gnu) {
+ if ((p->type & TYPEMASK) == MACRTYPE)
+ fprintf(traceout, "%s:\t%s\n", name, p->defn);
+ else {
+ fprintf(traceout, "%s:\t<%s>\n", name, p->defn);
+ }
+ } else
+ fprintf(traceout, "`%s'\t`%s'\n", name, p->defn);
+}
+
+/*
+ * dodumpdef - dump the specified definitions in the hash
+ * table to stderr. If nothing is specified, the entire
+ * hash table is dumped.
+ */
+static void
+dodump(const char *argv[], int argc)
+{
+ int n;
+ struct macro_definition *p;
+
+ if (argc > 2) {
+ for (n = 2; n < argc; n++)
+ if ((p = lookup_macro_definition(argv[n])) != NULL)
+ dump_one_def(argv[n], p);
+ } else
+ macro_for_all(dump_one_def);
+}
+
+/*
+ * dotrace - mark some macros as traced/untraced depending upon on.
+ */
+static void
+dotrace(const char *argv[], int argc, int on)
+{
+ int n;
+
+ if (argc > 2) {
+ for (n = 2; n < argc; n++)
+ mark_traced(argv[n], on);
+ } else
+ mark_traced(NULL, on);
+}
+
+/*
+ * doifelse - select one of two alternatives - loop.
+ */
+static void
+doifelse(const char *argv[], int argc)
+{
+ while (argc > 4) {
+ if (STREQ(argv[2], argv[3])) {
+ pbstr(argv[4]);
+ break;
+ } else if (argc == 6) {
+ pbstr(argv[5]);
+ break;
+ } else {
+ argv += 3;
+ argc -= 3;
+ }
+ }
+}
+
+/*
+ * doinclude - include a given file.
+ */
+static int
+doincl(const char *ifile)
+{
+ if (ilevel + 1 == MAXINP)
+ m4errx(1, "too many include files.");
+ if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
+ ilevel++;
+ bbase[ilevel] = bufbase = bp;
+ return (1);
+ } else
+ return (0);
+}
+
+#ifdef EXTENDED
+/*
+ * dopaste - include a given file without any
+ * macro processing.
+ */
+static int
+dopaste(const char *pfile)
+{
+ FILE *pf;
+ int c;
+
+ if ((pf = fopen(pfile, "r")) != NULL) {
+ if (synch_lines)
+ fprintf(active, "#line 1 \"%s\"\n", pfile);
+ while ((c = getc(pf)) != EOF)
+ putc(c, active);
+ (void) fclose(pf);
+ emit_synchline();
+ return (1);
+ } else
+ return (0);
+}
+#endif
+
+/*
+ * dochq - change quote characters
+ */
+static void
+dochq(const char *argv[], int ac)
+{
+ if (ac == 2) {
+ lquote[0] = LQUOTE; lquote[1] = EOS;
+ rquote[0] = RQUOTE; rquote[1] = EOS;
+ } else {
+ strlcpy(lquote, argv[2], sizeof(lquote));
+ if (ac > 3) {
+ strlcpy(rquote, argv[3], sizeof(rquote));
+ } else {
+ rquote[0] = ECOMMT; rquote[1] = EOS;
+ }
+ }
+}
+
+/*
+ * dochc - change comment characters
+ */
+static void
+dochc(const char *argv[], int argc)
+{
+/* XXX Note that there is no difference between no argument and a single
+ * empty argument.
+ */
+ if (argc == 2) {
+ scommt[0] = EOS;
+ ecommt[0] = EOS;
+ } else {
+ strlcpy(scommt, argv[2], sizeof(scommt));
+ if (argc == 3) {
+ ecommt[0] = ECOMMT; ecommt[1] = EOS;
+ } else {
+ strlcpy(ecommt, argv[3], sizeof(ecommt));
+ }
+ }
+}
+
+/*
+ * dom4wrap - expand text at EOF
+ */
+static void
+dom4wrap(const char *text)
+{
+ if (wrapindex >= maxwraps) {
+ if (maxwraps == 0)
+ maxwraps = 16;
+ else
+ maxwraps *= 2;
+ m4wraps = xreallocarray(m4wraps, maxwraps, sizeof(*m4wraps),
+ "too many m4wraps");
+ }
+ m4wraps[wrapindex++] = xstrdup(text);
+}
+
+/*
+ * dodivert - divert the output to a temporary file
+ */
+static void
+dodiv(int n)
+{
+ int fd;
+
+ oindex = n;
+ if (n >= maxout) {
+ if (mimic_gnu)
+ resizedivs(n + 10);
+ else
+ n = 0; /* bitbucket */
+ }
+
+ if (n < 0)
+ n = 0; /* bitbucket */
+ if (outfile[n] == NULL) {
+ char fname[] = _PATH_DIVNAME;
+
+ if ((fd = mkstemp(fname)) == -1 ||
+ unlink(fname) == -1 ||
+ (outfile[n] = fdopen(fd, "w+")) == NULL)
+ err(1, "%s: cannot divert", fname);
+ }
+ active = outfile[n];
+}
+
+/*
+ * doundivert - undivert a specified output, or all
+ * other outputs, in numerical order.
+ */
+static void
+doundiv(const char *argv[], int argc)
+{
+ int ind;
+ int n;
+
+ if (argc > 2) {
+ for (ind = 2; ind < argc; ind++) {
+ const char *errstr;
+ n = strtonum(argv[ind], 1, INT_MAX, &errstr);
+ if (errstr) {
+ if (errno == EINVAL && mimic_gnu)
+ getdivfile(argv[ind]);
+ } else {
+ if (n < maxout && outfile[n] != NULL)
+ getdiv(n);
+ }
+ }
+ }
+ else
+ for (n = 1; n < maxout; n++)
+ if (outfile[n] != NULL)
+ getdiv(n);
+}
+
+/*
+ * dosub - select substring
+ */
+static void
+dosub(const char *argv[], int argc)
+{
+ const char *ap, *fc, *k;
+ int nc;
+
+ ap = argv[2]; /* target string */
+#ifdef EXPR
+ fc = ap + expr(argv[3]); /* first char */
+#else
+ fc = ap + atoi(argv[3]); /* first char */
+#endif
+ nc = strlen(fc);
+ if (argc >= 5)
+#ifdef EXPR
+ nc = min(nc, expr(argv[4]));
+#else
+ nc = min(nc, atoi(argv[4]));
+#endif
+ if (fc >= ap && fc < ap + strlen(ap))
+ for (k = fc + nc - 1; k >= fc; k--)
+ pushback(*k);
+}
+
+/*
+ * map:
+ * map every character of s1 that is specified in from
+ * into s3 and replace in s. (source s1 remains untouched)
+ *
+ * This is derived from the a standard implementation of map(s,from,to)
+ * function of ICON language. Within mapvec, we replace every character
+ * of "from" with the corresponding character in "to".
+ * If "to" is shorter than "from", than the corresponding entries are null,
+ * which means that those characters dissapear altogether.
+ */
+static void
+map(char *dest, const char *src, const char *from, const char *to)
+{
+ const char *tmp;
+ unsigned char sch, dch;
+ static char frombis[257];
+ static char tobis[257];
+ int i;
+ char seen[256];
+ static unsigned char mapvec[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
+ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
+ 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
+ 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
+ 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
+ 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
+ 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
+ 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
+ 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
+ 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
+ };
+
+ if (*src) {
+ if (mimic_gnu) {
+ /*
+ * expand character ranges on the fly
+ */
+ from = handledash(frombis, frombis + 256, from);
+ to = handledash(tobis, tobis + 256, to);
+ }
+ tmp = from;
+ /*
+ * create a mapping between "from" and
+ * "to"
+ */
+ for (i = 0; i < 256; i++)
+ seen[i] = 0;
+ while (*from) {
+ if (!seen[(unsigned char)(*from)]) {
+ mapvec[(unsigned char)(*from)] = (unsigned char)(*to);
+ seen[(unsigned char)(*from)] = 1;
+ }
+ from++;
+ if (*to)
+ to++;
+ }
+
+ while (*src) {
+ sch = (unsigned char)(*src++);
+ dch = mapvec[sch];
+ if ((*dest = (char)dch))
+ dest++;
+ }
+ /*
+ * restore all the changed characters
+ */
+ while (*tmp) {
+ mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
+ tmp++;
+ }
+ }
+ *dest = '\0';
+}
+
+
+/*
+ * handledash:
+ * use buffer to copy the src string, expanding character ranges
+ * on the way.
+ */
+static const char *
+handledash(char *buffer, char *end, const char *src)
+{
+ char *p;
+
+ p = buffer;
+ while(*src) {
+ if (src[1] == '-' && src[2]) {
+ unsigned char i;
+ if ((unsigned char)src[0] <= (unsigned char)src[2]) {
+ for (i = (unsigned char)src[0];
+ i <= (unsigned char)src[2]; i++) {
+ *p++ = i;
+ if (p == end) {
+ *p = '\0';
+ return buffer;
+ }
+ }
+ } else {
+ for (i = (unsigned char)src[0];
+ i >= (unsigned char)src[2]; i--) {
+ *p++ = i;
+ if (p == end) {
+ *p = '\0';
+ return buffer;
+ }
+ }
+ }
+ src += 3;
+ } else
+ *p++ = *src++;
+ if (p == end)
+ break;
+ }
+ *p = '\0';
+ return buffer;
+}
diff --git a/usr.bin/m4/expr.c b/usr.bin/m4/expr.c
new file mode 100644
index 0000000..6e0b45f
--- /dev/null
+++ b/usr.bin/m4/expr.c
@@ -0,0 +1,43 @@
+/* $OpenBSD: expr.c,v 1.18 2010/09/07 19:58:09 marco Exp $ */
+/*
+ * Copyright (c) 2004 Marc Espie <espie@cvs.openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <stdint.h>
+#include <stdio.h>
+#include <stddef.h>
+#include "mdef.h"
+#include "extern.h"
+
+int32_t end_result;
+const char *copy_toeval;
+
+extern void yy_scan_string(const char *);
+extern int yyparse(void);
+
+int
+yyerror(const char *msg)
+{
+ fprintf(stderr, "m4: %s in expr %s\n", msg, copy_toeval);
+ return(0);
+}
+
+int
+expr(const char *toeval)
+{
+ copy_toeval = toeval;
+ yy_scan_string(toeval);
+ yyparse();
+ return end_result;
+}
diff --git a/usr.bin/m4/extern.h b/usr.bin/m4/extern.h
new file mode 100644
index 0000000..0c07599
--- /dev/null
+++ b/usr.bin/m4/extern.h
@@ -0,0 +1,182 @@
+/* $OpenBSD: extern.h,v 1.55 2017/06/15 13:48:42 bcallah Exp $ */
+/* $NetBSD: extern.h,v 1.3 1996/01/13 23:25:24 pk Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the 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.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+/* eval.c */
+extern void eval(const char *[], int, int, int);
+extern void dodefine(const char *, const char *);
+extern unsigned long expansion_id;
+
+/* expr.c */
+extern int expr(const char *);
+
+/* gnum4.c */
+extern void addtoincludepath(const char *);
+extern struct input_file *fopen_trypath(struct input_file *, const char *);
+extern void doindir(const char *[], int);
+extern void dobuiltin(const char *[], int);
+extern void dopatsubst(const char *[], int);
+extern void doregexp(const char *[], int);
+
+extern void doprintlineno(struct input_file *);
+extern void doprintfilename(struct input_file *);
+
+extern void doesyscmd(const char *);
+extern void getdivfile(const char *);
+extern void doformat(const char *[], int);
+
+extern void m4_warnx(const char *, ...);
+
+/* look.c */
+
+#define FLAG_UNTRACED 0
+#define FLAG_TRACED 1
+#define FLAG_NO_TRACE 2
+
+extern void init_macros(void);
+extern ndptr lookup(const char *);
+extern void mark_traced(const char *, int);
+extern struct ohash macros;
+
+extern struct macro_definition *lookup_macro_definition(const char *);
+extern void macro_define(const char *, const char *);
+extern void macro_pushdef(const char *, const char *);
+extern void macro_popdef(const char *);
+extern void macro_undefine(const char *);
+extern void setup_builtin(const char *, unsigned int);
+extern void macro_for_all(void (*)(const char *, struct macro_definition *));
+#define macro_getdef(p) ((p)->d)
+#define macro_name(p) ((p)->name)
+#define macro_builtin_type(p) ((p)->builtin_type)
+#define is_traced(p) ((p)->trace_flags == FLAG_NO_TRACE ? (trace_flags & TRACE_ALL) : (p)->trace_flags)
+
+extern ndptr macro_getbuiltin(const char *);
+
+/* main.c */
+extern void outputstr(const char *);
+extern void do_emit_synchline(void);
+extern int exit_code;
+#define emit_synchline() do { if (synch_lines) do_emit_synchline(); } while(0)
+
+/* misc.c */
+extern void chrsave(int);
+extern char *compute_prevep(void);
+extern void getdiv(int);
+extern ptrdiff_t indx(const char *, const char *);
+extern void initspaces(void);
+extern void killdiv(void);
+extern void onintr(int);
+extern void pbnum(int);
+extern void pbnumbase(int, int, int);
+extern void pbunsigned(unsigned long);
+extern void pbstr(const char *);
+extern void pushback(int);
+extern void *xalloc(size_t, const char *, ...);
+extern void *xcalloc(size_t, size_t, const char *, ...);
+extern void *xrealloc(void *, size_t, const char *, ...);
+extern void *xreallocarray(void *, size_t, size_t, const char *, ...);
+extern char *xstrdup(const char *);
+extern void usage(void);
+extern void resizedivs(int);
+extern size_t buffer_mark(void);
+extern void dump_buffer(FILE *, size_t);
+extern void _Noreturn m4errx(int, const char *, ...);
+
+extern int obtain_char(struct input_file *);
+extern void set_input(struct input_file *, FILE *, const char *);
+extern void release_input(struct input_file *);
+
+/* speeded-up versions of chrsave/pushback */
+#define PUSHBACK(c) \
+ do { \
+ if (bp >= endpbb) \
+ enlarge_bufspace(); \
+ *bp++ = (c); \
+ } while(0)
+
+#define CHRSAVE(c) \
+ do { \
+ if (ep >= endest) \
+ enlarge_strspace(); \
+ *ep++ = (c); \
+ } while(0)
+
+/* and corresponding exposure for local symbols */
+extern void enlarge_bufspace(void);
+extern void enlarge_strspace(void);
+extern unsigned char *endpbb;
+extern char *endest;
+
+/* trace.c */
+extern unsigned int trace_flags;
+#define TRACE_ALL 512
+extern void trace_file(const char *);
+extern size_t trace(const char **, int, struct input_file *);
+extern void finish_trace(size_t);
+extern void set_trace_flags(const char *);
+extern FILE *traceout;
+
+extern stae *mstack; /* stack of m4 machine */
+extern char *sstack; /* shadow stack, for string space extension */
+extern FILE *active; /* active output file pointer */
+extern struct input_file infile[];/* input file stack (0=stdin) */
+extern FILE **outfile; /* diversion array(0=bitbucket) */
+extern int maxout; /* maximum number of diversions */
+extern int fp; /* m4 call frame pointer */
+extern int ilevel; /* input file stack pointer */
+extern int oindex; /* diversion index. */
+extern int sp; /* current m4 stack pointer */
+extern unsigned char *bp; /* first available character */
+extern unsigned char *buf; /* push-back buffer */
+extern unsigned char *bufbase; /* buffer base for this ilevel */
+extern unsigned char *bbase[]; /* buffer base per ilevel */
+extern char ecommt[MAXCCHARS+1];/* end character for comment */
+extern char *ep; /* first free char in strspace */
+extern char lquote[MAXCCHARS+1];/* left quote character (`) */
+extern char **m4wraps; /* m4wrap string default. */
+extern int maxwraps; /* size of m4wraps array */
+extern int wrapindex; /* current index in m4wraps */
+
+extern char *null; /* as it says.. just a null. */
+extern char rquote[MAXCCHARS+1];/* right quote character (') */
+extern char scommt[MAXCCHARS+1];/* start character for comment */
+extern int synch_lines; /* line synchronisation directives */
+
+extern int mimic_gnu; /* behaves like gnu-m4 */
+extern int prefix_builtins; /* prefix builtin macros with m4_ */
+extern int error_warns; /* make warnings cause exit_code = 1 */
+extern int fatal_warns; /* make warnings fatal */
+
diff --git a/usr.bin/m4/gnum4.c b/usr.bin/m4/gnum4.c
new file mode 100644
index 0000000..1de66f2
--- /dev/null
+++ b/usr.bin/m4/gnum4.c
@@ -0,0 +1,692 @@
+/* $OpenBSD: gnum4.c,v 1.52 2017/08/21 21:41:13 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1999 Marc Espie
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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.
+ */
+
+/*
+ * functions needed to support gnu-m4 extensions, including a fake freezing
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <err.h>
+#include <paths.h>
+#include <regex.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include "mdef.h"
+#include "stdd.h"
+#include "extern.h"
+
+
+int mimic_gnu = 0;
+
+/*
+ * Support for include path search
+ * First search in the current directory.
+ * If not found, and the path is not absolute, include path kicks in.
+ * First, -I options, in the order found on the command line.
+ * Then M4PATH env variable
+ */
+
+struct path_entry {
+ char *name;
+ struct path_entry *next;
+} *first, *last;
+
+static struct path_entry *new_path_entry(const char *);
+static void ensure_m4path(void);
+static struct input_file *dopath(struct input_file *, const char *);
+
+static struct path_entry *
+new_path_entry(const char *dirname)
+{
+ struct path_entry *n;
+
+ n = malloc(sizeof(struct path_entry));
+ if (!n)
+ errx(1, "out of memory");
+ n->name = xstrdup(dirname);
+ n->next = 0;
+ return n;
+}
+
+void
+addtoincludepath(const char *dirname)
+{
+ struct path_entry *n;
+
+ n = new_path_entry(dirname);
+
+ if (last) {
+ last->next = n;
+ last = n;
+ }
+ else
+ last = first = n;
+}
+
+static void
+ensure_m4path()
+{
+ static int envpathdone = 0;
+ char *envpath;
+ char *sweep;
+ char *path;
+
+ if (envpathdone)
+ return;
+ envpathdone = TRUE;
+ envpath = getenv("M4PATH");
+ if (!envpath)
+ return;
+ /* for portability: getenv result is read-only */
+ envpath = xstrdup(envpath);
+ for (sweep = envpath;
+ (path = strsep(&sweep, ":")) != NULL;)
+ addtoincludepath(path);
+ free(envpath);
+}
+
+static
+struct input_file *
+dopath(struct input_file *i, const char *filename)
+{
+ char path[PATH_MAX];
+ struct path_entry *pe;
+ FILE *f;
+
+ for (pe = first; pe; pe = pe->next) {
+ snprintf(path, sizeof(path), "%s/%s", pe->name, filename);
+ if ((f = fopen(path, "r")) != 0) {
+ set_input(i, f, path);
+ return i;
+ }
+ }
+ return NULL;
+}
+
+struct input_file *
+fopen_trypath(struct input_file *i, const char *filename)
+{
+ FILE *f;
+
+ f = fopen(filename, "r");
+ if (f != NULL) {
+ set_input(i, f, filename);
+ return i;
+ }
+ if (filename[0] == '/')
+ return NULL;
+
+ ensure_m4path();
+
+ return dopath(i, filename);
+}
+
+void
+doindir(const char *argv[], int argc)
+{
+ ndptr n;
+ struct macro_definition *p;
+
+ n = lookup(argv[2]);
+ if (n == NULL || (p = macro_getdef(n)) == NULL)
+ m4errx(1, "indir: undefined macro %s.", argv[2]);
+ argv[1] = p->defn;
+
+ eval(argv+1, argc-1, p->type, is_traced(n));
+}
+
+void
+dobuiltin(const char *argv[], int argc)
+{
+ ndptr p;
+
+ argv[1] = NULL;
+ p = macro_getbuiltin(argv[2]);
+ if (p != NULL)
+ eval(argv+1, argc-1, macro_builtin_type(p), is_traced(p));
+ else
+ m4errx(1, "unknown builtin %s.", argv[2]);
+}
+
+
+/* We need some temporary buffer space, as pb pushes BACK and substitution
+ * proceeds forward... */
+static char *buffer;
+static size_t bufsize = 0;
+static size_t current = 0;
+
+static void addchars(const char *, size_t);
+static void addchar(int);
+static char *twiddle(const char *);
+static char *getstring(void);
+static void exit_regerror(int, regex_t *, const char *);
+static void do_subst(const char *, regex_t *, const char *, const char *,
+ regmatch_t *);
+static void do_regexpindex(const char *, regex_t *, const char *, regmatch_t *);
+static void do_regexp(const char *, regex_t *, const char *, const char *,
+ regmatch_t *);
+static void add_sub(int, const char *, regex_t *, regmatch_t *);
+static void add_replace(const char *, regex_t *, const char *, regmatch_t *);
+#define addconstantstring(s) addchars((s), sizeof(s)-1)
+
+static void
+addchars(const char *c, size_t n)
+{
+ if (n == 0)
+ return;
+ while (current + n > bufsize) {
+ if (bufsize == 0)
+ bufsize = 1024;
+ else if (bufsize <= SIZE_MAX/2) {
+ bufsize *= 2;
+ } else {
+ errx(1, "size overflow");
+ }
+ buffer = xrealloc(buffer, bufsize, NULL);
+ }
+ memcpy(buffer+current, c, n);
+ current += n;
+}
+
+static void
+addchar(int c)
+{
+ if (current +1 > bufsize) {
+ if (bufsize == 0)
+ bufsize = 1024;
+ else
+ bufsize *= 2;
+ buffer = xrealloc(buffer, bufsize, NULL);
+ }
+ buffer[current++] = c;
+}
+
+static char *
+getstring(void)
+{
+ addchar('\0');
+ current = 0;
+ return buffer;
+}
+
+
+static void
+exit_regerror(int er, regex_t *re, const char *source)
+{
+ size_t errlen;
+ char *errbuf;
+
+ errlen = regerror(er, re, NULL, 0);
+ errbuf = xalloc(errlen,
+ "malloc in regerror: %lu", (unsigned long)errlen);
+ regerror(er, re, errbuf, errlen);
+ m4errx(1, "regular expression error in %s: %s.", source, errbuf);
+}
+
+/* warnx() plus check to see if we need to change exit code or exit.
+ * -E flag functionality.
+ */
+void
+m4_warnx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarnx(fmt, ap);
+ va_end(ap);
+
+ if (fatal_warns)
+ exit(1);
+ if (error_warns)
+ exit_code = 1;
+}
+
+static void
+add_sub(int n, const char *string, regex_t *re, regmatch_t *pm)
+{
+ if (n > re->re_nsub)
+ m4_warnx("No subexpression %d", n);
+ /* Subexpressions that did not match are
+ * not an error. */
+ else if (pm[n].rm_so != -1 &&
+ pm[n].rm_eo != -1) {
+ addchars(string + pm[n].rm_so,
+ pm[n].rm_eo - pm[n].rm_so);
+ }
+}
+
+/* Add replacement string to the output buffer, recognizing special
+ * constructs and replacing them with substrings of the original string.
+ */
+static void
+add_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
+{
+ const char *p;
+
+ for (p = replace; *p != '\0'; p++) {
+ if (*p == '&' && !mimic_gnu) {
+ add_sub(0, string, re, pm);
+ continue;
+ }
+ if (*p == '\\') {
+ if (p[1] == '\\') {
+ addchar(p[1]);
+ p++;
+ continue;
+ }
+ if (p[1] == '&') {
+ if (mimic_gnu)
+ add_sub(0, string, re, pm);
+ else
+ addchar(p[1]);
+ p++;
+ continue;
+ }
+ if (isdigit((unsigned char)p[1])) {
+ add_sub(*(++p) - '0', string, re, pm);
+ continue;
+ }
+ }
+ addchar(*p);
+ }
+}
+
+static void
+do_subst(const char *string, regex_t *re, const char *source,
+ const char *replace, regmatch_t *pm)
+{
+ int error;
+ int flags = 0;
+ const char *last_match = NULL;
+
+ while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) {
+ if (pm[0].rm_eo != 0) {
+ if (string[pm[0].rm_eo-1] == '\n')
+ flags = 0;
+ else
+ flags = REG_NOTBOL;
+ }
+
+ /* NULL length matches are special... We use the `vi-mode'
+ * rule: don't allow a NULL-match at the last match
+ * position.
+ */
+ if (pm[0].rm_so == pm[0].rm_eo &&
+ string + pm[0].rm_so == last_match) {
+ if (*string == '\0')
+ return;
+ addchar(*string);
+ if (*string++ == '\n')
+ flags = 0;
+ else
+ flags = REG_NOTBOL;
+ continue;
+ }
+ last_match = string + pm[0].rm_so;
+ addchars(string, pm[0].rm_so);
+ add_replace(string, re, replace, pm);
+ string += pm[0].rm_eo;
+ }
+ if (error != REG_NOMATCH)
+ exit_regerror(error, re, source);
+ pbstr(string);
+}
+
+static void
+do_regexp(const char *string, regex_t *re, const char *source,
+ const char *replace, regmatch_t *pm)
+{
+ int error;
+
+ switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
+ case 0:
+ add_replace(string, re, replace, pm);
+ pbstr(getstring());
+ break;
+ case REG_NOMATCH:
+ break;
+ default:
+ exit_regerror(error, re, source);
+ }
+}
+
+static void
+do_regexpindex(const char *string, regex_t *re, const char *source,
+ regmatch_t *pm)
+{
+ int error;
+
+ switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
+ case 0:
+ pbunsigned(pm[0].rm_so);
+ break;
+ case REG_NOMATCH:
+ pbnum(-1);
+ break;
+ default:
+ exit_regerror(error, re, source);
+ }
+}
+
+/* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2
+ * says. So we twiddle with the regexp before passing it to regcomp.
+ */
+static char *
+twiddle(const char *p)
+{
+ /* + at start of regexp is a normal character for Gnu m4 */
+ if (*p == '^') {
+ addchar(*p);
+ p++;
+ }
+ if (*p == '+') {
+ addchar('\\');
+ }
+ /* This could use strcspn for speed... */
+ while (*p != '\0') {
+ if (*p == '\\') {
+ switch(p[1]) {
+ case '(':
+ case ')':
+ case '|':
+ addchar(p[1]);
+ break;
+ case 'w':
+ addconstantstring("[_a-zA-Z0-9]");
+ break;
+ case 'W':
+ addconstantstring("[^_a-zA-Z0-9]");
+ break;
+ case '<':
+ addconstantstring("[[:<:]]");
+ break;
+ case '>':
+ addconstantstring("[[:>:]]");
+ break;
+ default:
+ addchars(p, 2);
+ break;
+ }
+ p+=2;
+ continue;
+ }
+ if (*p == '(' || *p == ')' || *p == '|')
+ addchar('\\');
+
+ addchar(*p);
+ p++;
+ }
+ return getstring();
+}
+
+/* patsubst(string, regexp, opt replacement) */
+/* argv[2]: string
+ * argv[3]: regexp
+ * argv[4]: opt rep
+ */
+void
+dopatsubst(const char *argv[], int argc)
+{
+ if (argc <= 3) {
+ m4_warnx("Too few arguments to patsubst");
+ return;
+ }
+ /* special case: empty regexp */
+ if (argv[3][0] == '\0') {
+ const char *s;
+ size_t len;
+ if (argc > 4 && argv[4])
+ len = strlen(argv[4]);
+ else
+ len = 0;
+ for (s = argv[2]; *s != '\0'; s++) {
+ addchars(argv[4], len);
+ addchar(*s);
+ }
+ } else {
+ int error;
+ regex_t re;
+ regmatch_t *pmatch;
+ int mode = REG_EXTENDED;
+ const char *source;
+ size_t l = strlen(argv[3]);
+
+ if (!mimic_gnu ||
+ (argv[3][0] == '^') ||
+ (l > 0 && argv[3][l-1] == '$'))
+ mode |= REG_NEWLINE;
+
+ source = mimic_gnu ? twiddle(argv[3]) : argv[3];
+ error = regcomp(&re, source, mode);
+ if (error != 0)
+ exit_regerror(error, &re, source);
+
+ pmatch = xreallocarray(NULL, re.re_nsub+1, sizeof(regmatch_t),
+ NULL);
+ do_subst(argv[2], &re, source,
+ argc > 4 && argv[4] != NULL ? argv[4] : "", pmatch);
+ free(pmatch);
+ regfree(&re);
+ }
+ pbstr(getstring());
+}
+
+void
+doregexp(const char *argv[], int argc)
+{
+ int error;
+ regex_t re;
+ regmatch_t *pmatch;
+ const char *source;
+
+ if (argc <= 3) {
+ m4_warnx("Too few arguments to regexp");
+ return;
+ }
+ /* special gnu case */
+ if (argv[3][0] == '\0' && mimic_gnu) {
+ if (argc == 4 || argv[4] == NULL)
+ return;
+ else
+ pbstr(argv[4]);
+ }
+ source = mimic_gnu ? twiddle(argv[3]) : argv[3];
+ error = regcomp(&re, source, REG_EXTENDED|REG_NEWLINE);
+ if (error != 0)
+ exit_regerror(error, &re, source);
+
+ pmatch = xreallocarray(NULL, re.re_nsub+1, sizeof(regmatch_t), NULL);
+ if (argc == 4 || argv[4] == NULL)
+ do_regexpindex(argv[2], &re, source, pmatch);
+ else
+ do_regexp(argv[2], &re, source, argv[4], pmatch);
+ free(pmatch);
+ regfree(&re);
+}
+
+void
+doformat(const char *argv[], int argc)
+{
+ const char *format = argv[2];
+ int pos = 3;
+ int left_padded;
+ long width;
+ size_t l;
+ const char *thisarg;
+ char temp[2];
+ long extra;
+
+ while (*format != 0) {
+ if (*format != '%') {
+ addchar(*format++);
+ continue;
+ }
+
+ format++;
+ if (*format == '%') {
+ addchar(*format++);
+ continue;
+ }
+ if (*format == 0) {
+ addchar('%');
+ break;
+ }
+
+ if (*format == '*') {
+ format++;
+ if (pos >= argc)
+ m4errx(1,
+ "Format with too many format specifiers.");
+ width = strtol(argv[pos++], NULL, 10);
+ } else {
+ width = strtol(format, (char **)&format, 10);
+ }
+ if (width < 0) {
+ left_padded = 1;
+ width = -width;
+ } else {
+ left_padded = 0;
+ }
+ if (*format == '.') {
+ format++;
+ if (*format == '*') {
+ format++;
+ if (pos >= argc)
+ m4errx(1,
+ "Format with too many format specifiers.");
+ extra = strtol(argv[pos++], NULL, 10);
+ } else {
+ extra = strtol(format, (char **)&format, 10);
+ }
+ } else {
+ extra = LONG_MAX;
+ }
+ if (pos >= argc)
+ m4errx(1, "Format with too many format specifiers.");
+ switch(*format) {
+ case 's':
+ thisarg = argv[pos++];
+ break;
+ case 'c':
+ temp[0] = strtoul(argv[pos++], NULL, 10);
+ temp[1] = 0;
+ thisarg = temp;
+ break;
+ default:
+ m4errx(1, "Unsupported format specification: %s.",
+ argv[2]);
+ }
+ format++;
+ l = strlen(thisarg);
+ if (l > extra)
+ l = extra;
+ if (!left_padded) {
+ while (l < width--)
+ addchar(' ');
+ }
+ addchars(thisarg, l);
+ if (left_padded) {
+ while (l < width--)
+ addchar(' ');
+ }
+ }
+ pbstr(getstring());
+}
+
+void
+doesyscmd(const char *cmd)
+{
+ int p[2];
+ pid_t cpid;
+ char *argv[4];
+ int cc;
+ int status;
+
+ /* Follow gnu m4 documentation: first flush buffers. */
+ fflush(NULL);
+
+ argv[0] = "sh";
+ argv[1] = "-c";
+ argv[2] = (char *)cmd;
+ argv[3] = NULL;
+
+ /* Just set up standard output, share stderr and stdin with m4 */
+ if (pipe(p) == -1)
+ err(1, "bad pipe");
+ switch(cpid = fork()) {
+ case -1:
+ err(1, "bad fork");
+ /* NOTREACHED */
+ case 0:
+ (void) close(p[0]);
+ (void) dup2(p[1], 1);
+ (void) close(p[1]);
+ execv(_PATH_BSHELL, argv);
+ exit(1);
+ default:
+ /* Read result in two stages, since m4's buffer is
+ * pushback-only. */
+ (void) close(p[1]);
+ do {
+ char result[BUFSIZE];
+ cc = read(p[0], result, sizeof result);
+ if (cc > 0)
+ addchars(result, cc);
+ } while (cc > 0 || (cc == -1 && errno == EINTR));
+
+ (void) close(p[0]);
+ while (waitpid(cpid, &status, 0) == -1) {
+ if (errno != EINTR)
+ break;
+ }
+ pbstr(getstring());
+ }
+}
+
+void
+getdivfile(const char *name)
+{
+ FILE *f;
+ int c;
+
+ f = fopen(name, "r");
+ if (!f)
+ return;
+
+ while ((c = getc(f))!= EOF)
+ putc(c, active);
+ (void) fclose(f);
+}
diff --git a/usr.bin/m4/look.c b/usr.bin/m4/look.c
new file mode 100644
index 0000000..5feb041
--- /dev/null
+++ b/usr.bin/m4/look.c
@@ -0,0 +1,337 @@
+/* $OpenBSD: look.c,v 1.24 2014/12/21 09:33:12 espie Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the 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.
+ */
+
+/*
+ * look.c
+ * Facility: m4 macro processor
+ * by: oz
+ */
+
+#include <sys/cdefs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <ohash.h>
+#include "mdef.h"
+#include "stdd.h"
+#include "extern.h"
+
+static void *hash_calloc(size_t, size_t, void *);
+static void hash_free(void *, void *);
+static void *element_alloc(size_t, void *);
+static void setup_definition(struct macro_definition *, const char *,
+ const char *);
+static void free_definition(char *);
+static void keep(char *);
+static int string_in_use(const char *);
+
+static struct ohash_info macro_info = {
+ offsetof(struct ndblock, name),
+ NULL, hash_calloc, hash_free, element_alloc };
+
+struct ohash macros;
+
+/* Support routines for hash tables. */
+void *
+hash_calloc(size_t n, size_t s, void *u UNUSED)
+{
+ void *storage = xcalloc(n, s, "hash alloc");
+ return storage;
+}
+
+void
+hash_free(void *p, void *u UNUSED)
+{
+ free(p);
+}
+
+void *
+element_alloc(size_t s, void *u UNUSED)
+{
+ return xalloc(s, "element alloc");
+}
+
+void
+init_macros()
+{
+ ohash_init(&macros, 10, &macro_info);
+}
+
+/*
+ * find name in the hash table
+ */
+ndptr
+lookup(const char *name)
+{
+ return ohash_find(&macros, ohash_qlookup(&macros, name));
+}
+
+struct macro_definition *
+lookup_macro_definition(const char *name)
+{
+ ndptr p;
+
+ p = ohash_find(&macros, ohash_qlookup(&macros, name));
+ if (p)
+ return p->d;
+ else
+ return NULL;
+}
+
+static void
+setup_definition(struct macro_definition *d, const char *defn, const char *name)
+{
+ ndptr p;
+
+ if (strncmp(defn, BUILTIN_MARKER, sizeof(BUILTIN_MARKER)-1) == 0 &&
+ (p = macro_getbuiltin(defn+sizeof(BUILTIN_MARKER)-1)) != NULL) {
+ d->type = macro_builtin_type(p);
+ d->defn = xstrdup(defn+sizeof(BUILTIN_MARKER)-1);
+ } else {
+ if (!*defn)
+ d->defn = null;
+ else
+ d->defn = xstrdup(defn);
+ d->type = MACRTYPE;
+ }
+ if (STREQ(name, defn))
+ d->type |= RECDEF;
+}
+
+static ndptr
+create_entry(const char *name)
+{
+ const char *end = NULL;
+ unsigned int i;
+ ndptr n;
+
+ i = ohash_qlookupi(&macros, name, &end);
+ n = ohash_find(&macros, i);
+ if (n == NULL) {
+ n = ohash_create_entry(&macro_info, name, &end);
+ ohash_insert(&macros, i, n);
+ n->trace_flags = FLAG_NO_TRACE;
+ n->builtin_type = MACRTYPE;
+ n->d = NULL;
+ }
+ return n;
+}
+
+void
+macro_define(const char *name, const char *defn)
+{
+ ndptr n = create_entry(name);
+ if (n->d != NULL) {
+ if (n->d->defn != null)
+ free_definition(n->d->defn);
+ } else {
+ n->d = xalloc(sizeof(struct macro_definition), NULL);
+ n->d->next = NULL;
+ }
+ setup_definition(n->d, defn, name);
+}
+
+void
+macro_pushdef(const char *name, const char *defn)
+{
+ ndptr n;
+ struct macro_definition *d;
+
+ n = create_entry(name);
+ d = xalloc(sizeof(struct macro_definition), NULL);
+ d->next = n->d;
+ n->d = d;
+ setup_definition(n->d, defn, name);
+}
+
+void
+macro_undefine(const char *name)
+{
+ ndptr n = lookup(name);
+ if (n != NULL) {
+ struct macro_definition *r, *r2;
+
+ for (r = n->d; r != NULL; r = r2) {
+ r2 = r->next;
+ if (r->defn != null)
+ free(r->defn);
+ free(r);
+ }
+ n->d = NULL;
+ }
+}
+
+void
+macro_popdef(const char *name)
+{
+ ndptr n = lookup(name);
+
+ if (n != NULL) {
+ struct macro_definition *r = n->d;
+ if (r != NULL) {
+ n->d = r->next;
+ if (r->defn != null)
+ free(r->defn);
+ free(r);
+ }
+ }
+}
+
+void
+macro_for_all(void (*f)(const char *, struct macro_definition *))
+{
+ ndptr n;
+ unsigned int i;
+
+ for (n = ohash_first(&macros, &i); n != NULL;
+ n = ohash_next(&macros, &i))
+ if (n->d != NULL)
+ f(n->name, n->d);
+}
+
+void
+setup_builtin(const char *name, unsigned int type)
+{
+ ndptr n;
+ char *name2;
+
+ if (prefix_builtins) {
+ name2 = xalloc(strlen(name)+3+1, NULL);
+ memcpy(name2, "m4_", 3);
+ memcpy(name2 + 3, name, strlen(name)+1);
+ } else
+ name2 = xstrdup(name);
+
+ n = create_entry(name2);
+ n->builtin_type = type;
+ n->d = xalloc(sizeof(struct macro_definition), NULL);
+ n->d->defn = name2;
+ n->d->type = type;
+ n->d->next = NULL;
+}
+
+void
+mark_traced(const char *name, int on)
+{
+ ndptr p;
+ unsigned int i;
+
+ if (name == NULL) {
+ if (on)
+ trace_flags |= TRACE_ALL;
+ else
+ trace_flags &= ~TRACE_ALL;
+ for (p = ohash_first(&macros, &i); p != NULL;
+ p = ohash_next(&macros, &i))
+ p->trace_flags = FLAG_NO_TRACE;
+ } else {
+ p = create_entry(name);
+ p->trace_flags = on;
+ }
+}
+
+ndptr
+macro_getbuiltin(const char *name)
+{
+ ndptr p;
+
+ p = lookup(name);
+ if (p == NULL || p->builtin_type == MACRTYPE)
+ return NULL;
+ else
+ return p;
+}
+
+/* XXX things are slightly more complicated than they seem.
+ * a macro may actually be "live" (in the middle of an expansion
+ * on the stack.
+ * So we actually may need to place it in an array for later...
+ */
+
+static int kept_capacity = 0;
+static int kept_size = 0;
+static char **kept = NULL;
+
+static void
+keep(char *ptr)
+{
+ if (kept_capacity <= kept_size) {
+ if (kept_capacity)
+ kept_capacity *= 2;
+ else
+ kept_capacity = 50;
+ kept = xreallocarray(kept, kept_capacity,
+ sizeof(char *), "Out of memory while saving %d strings\n",
+ kept_capacity);
+ }
+ kept[kept_size++] = ptr;
+}
+
+static int
+string_in_use(const char *ptr)
+{
+ int i;
+ for (i = 0; i <= sp; i++) {
+ if (sstack[i] == STORAGE_MACRO && mstack[i].sstr == ptr)
+ return 1;
+ }
+ return 0;
+}
+
+
+static void
+free_definition(char *ptr)
+{
+ int i;
+
+ /* first try to free old strings */
+ for (i = 0; i < kept_size; i++) {
+ if (!string_in_use(kept[i])) {
+ kept_size--;
+ free(kept[i]);
+ if (i != kept_size)
+ kept[i] = kept[kept_size];
+ i--;
+ }
+ }
+
+ /* then deal with us */
+ if (string_in_use(ptr))
+ keep(ptr);
+ else
+ free(ptr);
+}
+
diff --git a/usr.bin/m4/m4.1 b/usr.bin/m4/m4.1
new file mode 100644
index 0000000..d389c15
--- /dev/null
+++ b/usr.bin/m4/m4.1
@@ -0,0 +1,524 @@
+.\" @(#) $OpenBSD: m4.1,v 1.64 2017/06/15 13:48:42 bcallah Exp $
+.\"
+.\" Copyright (c) 1989, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Ozan Yigit at York University.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the 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.
+.\"
+.Dd $Mdocdate: June 15 2017 $
+.Dt M4 1
+.Os
+.Sh NAME
+.Nm m4
+.Nd macro language processor
+.Sh SYNOPSIS
+.Nm
+.Op Fl EgPs
+.Oo
+.Sm off
+.Fl D Ar name Op No = Ar value
+.Sm on
+.Oc
+.Op Fl d Ar flags
+.Op Fl I Ar dirname
+.Op Fl o Ar filename
+.Op Fl t Ar macro
+.Op Fl U Ns Ar name
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility is a macro processor that can be used as a front end to any
+language (e.g., C, ratfor, fortran, lex, and yacc).
+If no input files are given,
+.Nm
+reads from the standard input,
+otherwise files specified on the command line are
+processed in the given order.
+Input files can be regular files, files in the m4 include paths, or a
+single dash
+.Pq Sq - ,
+denoting standard input.
+.Nm
+writes
+the processed text to the standard output, unless told otherwise.
+.Pp
+Macro calls have the form name(argument1[, argument2, ..., argumentN]).
+.Pp
+There cannot be any space following the macro name and the open
+parenthesis
+.Pq Sq \&( .
+If the macro name is not followed by an open
+parenthesis it is processed with no arguments.
+.Pp
+Macro names consist of a leading alphabetic or underscore
+possibly followed by alphanumeric or underscore characters, e.g.,
+valid macro names match the pattern
+.Dq [a-zA-Z_][a-zA-Z0-9_]* .
+.Pp
+In arguments to macros, leading unquoted space, tab, and newline
+.Pq Sq \en
+characters are ignored.
+To quote strings, use left and right single quotes
+.Pq e.g., Sq \ \&this is a string with a leading space .
+You can change the quote characters with the
+.Ic changequote
+built-in macro.
+.Pp
+Most built-ins don't make any sense without arguments, and hence are not
+recognized as special when not followed by an open parenthesis.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl D Ns Ar name Ns Op = Ns Ar value
+Define the symbol
+.Ar name
+to have some value (or
+.Dv NULL ) .
+.It Fl d Ar "flags"
+Set trace flags.
+.Ar flags
+may hold the following:
+.Bl -tag -width Ds
+.It Ar a
+print macro arguments.
+.It Ar c
+print macro expansion over several lines.
+.It Ar e
+print result of macro expansion.
+.It Ar f
+print filename location.
+.It Ar l
+print line number.
+.It Ar q
+quote arguments and expansion with the current quotes.
+.It Ar t
+start with all macros traced.
+.It Ar x
+number macro expansions.
+.It Ar V
+turn on all options.
+.El
+.Pp
+By default, trace is set to
+.Qq eq .
+.It Fl E
+Set warnings to be fatal.
+When a single
+.Fl E
+flag is specified, if warnings are issued, execution continues but
+.Nm
+will exit with a non-zero exit status.
+When multiple
+.Fl E
+flags are specified, execution will halt upon issuing the first warning and
+.Nm
+will exit with a non-zero exit status.
+This behaviour matches GNU-m4 1.4.9 and later.
+.It Fl g
+Activate GNU-m4 compatibility mode.
+In this mode, translit handles simple character
+ranges (e.g., a-z), regular expressions mimic emacs behavior,
+multiple m4wrap calls are handled as a stack,
+the number of diversions is unlimited,
+empty names for macro definitions are allowed,
+and eval understands
+.Sq 0rbase:value
+numbers.
+.It Fl I Ar "dirname"
+Add directory
+.Ar dirname
+to the include path.
+.It Fl o Ar filename
+Send trace output to
+.Ar filename .
+.It Fl P
+Prefix all built-in macros with
+.Sq m4_ .
+For example, instead of writing
+.Ic define ,
+use
+.Ic m4_define .
+.It Fl s
+Output line synchronization directives, suitable for
+.Xr cpp 1 .
+.It Fl t Ar macro
+Turn tracing on for
+.Ar macro .
+.It Fl "U" Ns Ar "name"
+Undefine the symbol
+.Ar name .
+.El
+.Sh SYNTAX
+.Nm
+provides the following built-in macros.
+They may be redefined, losing their original meaning.
+Return values are null unless otherwise stated.
+.Bl -tag -width changequote
+.It Fn builtin name
+Calls a built-in by its
+.Fa name ,
+overriding possible redefinitions.
+.It Fn changecom startcomment endcomment
+Changes the start comment and end comment sequences.
+Comment sequences may be up to five characters long.
+The default values are the hash sign
+and the newline character.
+.Bd -literal -offset indent
+# This is a comment
+.Ed
+.Pp
+With no arguments, comments are turned off.
+With one single argument, the end comment sequence is set
+to the newline character.
+.It Fn changequote beginquote endquote
+Defines the open quote and close quote sequences.
+Quote sequences may be up to five characters long.
+The default values are the backquote character and the quote
+character.
+.Bd -literal -offset indent
+`Here is a quoted string'
+.Ed
+.Pp
+With no arguments, the default quotes are restored.
+With one single argument, the close quote sequence is set
+to the newline character.
+.It Fn decr arg
+Decrements the argument
+.Fa arg
+by 1.
+The argument
+.Fa arg
+must be a valid numeric string.
+.It Fn define name value
+Define a new macro named by the first argument
+.Fa name
+to have the
+value of the second argument
+.Fa value .
+Each occurrence of
+.Sq $n
+(where
+.Ar n
+is 0 through 9) is replaced by the
+.Ar n Ns 'th
+argument.
+.Sq $0
+is the name of the calling macro.
+Undefined arguments are replaced by a null string.
+.Sq $#
+is replaced by the number of arguments;
+.Sq $*
+is replaced by all arguments comma separated;
+.Sq $@
+is the same as
+.Sq $*
+but all arguments are quoted against further expansion.
+.It Fn defn name ...
+Returns the quoted definition for each argument.
+This can be used to rename
+macro definitions (even for built-in macros).
+.It Fn divert num
+There are 10 output queues (numbered 0-9).
+At the end of processing
+.Nm
+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 Ic divnum
+Returns the current output queue number.
+.It Ic dnl
+Discard input characters up to and including the next newline.
+.It Fn dumpdef name ...
+Prints the names and definitions for the named items, or for everything
+if no arguments are passed.
+.It Fn errprint msg
+Prints the first argument on the standard error output stream.
+.It Fn esyscmd cmd
+Passes its first argument to a shell and returns the shell's standard output.
+Note that the shell shares its standard input and standard error with
+.Nm .
+.It Fn eval expr[,radix[,minimum]]
+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 optional second argument
+.Fa radix
+specifies the radix for the result and the optional third argument
+.Fa minimum
+specifies the minimum number of digits in the result.
+.It Fn expr expr
+This is an alias for
+.Ic eval .
+.It Fn format formatstring arg1 ...
+Returns
+.Fa formatstring
+with escape sequences substituted with
+.Fa arg1
+and following arguments, in a way similar to
+.Xr printf 3 .
+This built-in is only available in GNU-m4 compatibility mode, and the only
+parameters implemented are there for autoconf compatibility:
+left-padding flag, an optional field width, a maximum field width,
+*-specified field widths, and the %s and %c data type.
+.It Fn ifdef name yes no
+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
+.Dv NULL .
+The word
+.Qq unix
+is predefined.
+.It Fn ifelse a b yes ...
+If the first argument
+.Fa a
+matches the second argument
+.Fa b
+then
+.Fn ifelse
+returns
+the third argument
+.Fa yes .
+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
+.Dv NULL
+is returned if no other matches were found.
+.It Fn include name
+Returns the contents of the file specified in the first argument.
+If the file is not found as is, look through the include path:
+first the directories specified with
+.Fl I
+on the command line, then the environment variable
+.Ev M4PATH ,
+as a colon-separated list of directories.
+Include aborts with an error message if the file cannot be included.
+.It Fn incr arg
+Increments the argument by 1.
+The argument must be a valid numeric string.
+.It Fn index string substring
+Returns the index of the second argument in the first argument (e.g.,
+.Ic index(the quick brown fox jumped, fox)
+returns 16).
+If the second
+argument is not found index returns \-1.
+.It Fn indir macro arg1 ...
+Indirectly calls the macro whose name is passed as the first argument,
+with the remaining arguments passed as first, ... arguments.
+.It Fn len arg
+Returns the number of characters in the first argument.
+Extra arguments
+are ignored.
+.It Fn m4exit code
+Immediately exits with the return value specified by the first argument,
+0 if none.
+.It Fn m4wrap todo
+Allows you to define what happens at the final
+.Dv EOF ,
+usually for cleanup purposes (e.g.,
+.Ic m4wrap("cleanup(tempfile)")
+causes the macro cleanup to be
+invoked after all other processing is done).
+.Pp
+Multiple calls to
+.Fn m4wrap
+get inserted in sequence at the final
+.Dv EOF .
+.It Fn maketemp template
+Like
+.Ic mkstemp .
+.It Fn mkstemp template
+Invokes
+.Xr mkstemp 3
+on the first argument, and returns the modified string.
+This can be used to create unique
+temporary file names.
+.It Fn paste file
+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 Fn patsubst string regexp replacement
+Substitutes a regular expression in a string with a replacement string.
+Usual substitution patterns apply: an ampersand
+.Pq Sq \&&
+is replaced by the string matching the regular expression.
+The string
+.Sq \e# ,
+where
+.Sq #
+is a digit, is replaced by the corresponding back-reference.
+.It Fn popdef arg ...
+Restores the
+.Ic pushdef Ns ed
+definition for each argument.
+.It Fn pushdef macro def
+Takes the same arguments as
+.Ic define ,
+but it saves the definition on a
+stack for later retrieval by
+.Fn popdef .
+.It Fn regexp string regexp replacement
+Finds a regular expression in a string.
+If no further arguments are given,
+it returns the first match position or \-1 if no match.
+If a third argument
+is provided, it returns the replacement string, with sub-patterns replaced.
+.It Fn shift arg1 ...
+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 Fn sinclude file
+Similar to
+.Ic include ,
+except it ignores any errors.
+.It Fn spaste file
+Similar to
+.Fn paste ,
+except it ignores any errors.
+.It Fn substr string offset length
+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 Fn syscmd cmd
+Passes the first argument to the shell.
+Nothing is returned.
+.It Ic sysval
+Returns the return value from the last
+.Ic syscmd .
+.It Fn traceon arg ...
+Enables tracing of macro expansions for the given arguments, or for all
+macros if no argument is given.
+.It Fn traceoff arg ...
+Disables tracing of macro expansions for the given arguments, or for all
+macros if no argument is given.
+.It Fn translit string mapfrom mapto
+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 Fn undefine name1 ...
+Removes the definition for the macros specified by its arguments.
+.It Fn undivert arg ...
+Flushes the named output queues (or all queues if no arguments).
+.It Ic unix
+A pre-defined macro for testing the OS platform.
+.It Ic __line__
+Returns the current file's line number.
+.It Ic __file__
+Returns the current file's name.
+.El
+.Sh EXIT STATUS
+.Ex -std m4
+.Pp
+But note that the
+.Ic m4exit
+macro can modify the exit status, as can the
+.Fl E
+flag.
+.Sh STANDARDS
+The
+.Nm
+utility is compliant with the
+.St -p1003.1-2008
+specification.
+.Pp
+The flags
+.Op Fl dEgIPot
+and the macros
+.Ic builtin ,
+.Ic esyscmd ,
+.Ic expr ,
+.Ic format ,
+.Ic indir ,
+.Ic paste ,
+.Ic patsubst ,
+.Ic regexp ,
+.Ic spaste ,
+.Ic unix ,
+.Ic __line__ ,
+and
+.Ic __file__
+are extensions to that specification.
+.Pp
+.Ic maketemp
+is not supposed to be a synonym for
+.Ic mkstemp ,
+but instead to be an insecure temporary file name creation function.
+It is marked by
+.St -p1003.1-2008
+as being obsolescent and should not be used if portability is a concern.
+.Pp
+The output format of
+.Ic traceon
+and
+.Ic dumpdef
+are not specified in any standard,
+are likely to change and should not be relied upon.
+The current format of tracing is closely modelled on
+.Nm gnu-m4 ,
+to allow
+.Nm autoconf
+to work.
+.Pp
+The built-ins
+.Ic pushdef
+and
+.Ic popdef
+handle macro definitions as a stack.
+However,
+.Ic define
+interacts with the stack in an undefined way.
+In this implementation,
+.Ic define
+replaces the top-most definition only.
+Other implementations may erase all definitions on the stack instead.
+.Pp
+All built-ins do expand without arguments in many other
+.Nm .
+.Pp
+Many other
+.Nm
+have dire size limitations with respect to buffer sizes.
+.Sh AUTHORS
+.An -nosplit
+.An Ozan Yigit Aq Mt oz@sis.yorku.ca
+and
+.An Richard A. O'Keefe Aq Mt ok@goanna.cs.rmit.OZ.AU .
+.Pp
+GNU-m4 compatibility extensions by
+.An Marc Espie Aq Mt espie@cvs.openbsd.org .
diff --git a/usr.bin/m4/main.c b/usr.bin/m4/main.c
new file mode 100644
index 0000000..4e664c0
--- /dev/null
+++ b/usr.bin/m4/main.c
@@ -0,0 +1,642 @@
+/* $OpenBSD: main.c,v 1.87 2017/06/15 13:48:42 bcallah Exp $ */
+/* $NetBSD: main.c,v 1.12 1997/02/08 23:54:49 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the 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.
+ */
+
+/*
+ * main.c
+ * Facility: m4 macro processor
+ * by: oz
+ */
+
+#include <sys/cdefs.h>
+#include <assert.h>
+#include <signal.h>
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <ohash.h>
+#include "mdef.h"
+#include "stdd.h"
+#include "extern.h"
+#include "pathnames.h"
+
+stae *mstack; /* stack of m4 machine */
+char *sstack; /* shadow stack, for string space extension */
+static size_t STACKMAX; /* current maximum size of stack */
+int sp; /* current m4 stack pointer */
+int fp; /* m4 call frame pointer */
+struct input_file infile[MAXINP];/* input file stack (0=stdin) */
+FILE **outfile; /* diversion array(0=bitbucket)*/
+int maxout;
+FILE *active; /* active output file pointer */
+int ilevel = 0; /* input file stack pointer */
+int oindex = 0; /* diversion index.. */
+char *null = ""; /* as it says.. just a null.. */
+char **m4wraps = NULL; /* m4wraps array. */
+int maxwraps = 0; /* size of m4wraps array */
+int wrapindex = 0; /* current offset in m4wraps */
+char lquote[MAXCCHARS+1] = {LQUOTE}; /* left quote character (`) */
+char rquote[MAXCCHARS+1] = {RQUOTE}; /* right quote character (') */
+char scommt[MAXCCHARS+1] = {SCOMMT}; /* start character for comment */
+char ecommt[MAXCCHARS+1] = {ECOMMT}; /* end character for comment */
+int synch_lines = 0; /* line synchronisation for C preprocessor */
+int prefix_builtins = 0; /* -P option to prefix builtin keywords */
+int error_warns = 0; /* -E option to make warnings exit_code = 1 */
+int fatal_warns = 0; /* -E -E option to make warnings fatal */
+
+struct keyblk {
+ char *knam; /* keyword name */
+ int ktyp; /* keyword type */
+};
+
+struct keyblk keywrds[] = { /* m4 keywords to be installed */
+ { "include", INCLTYPE },
+ { "sinclude", SINCTYPE },
+ { "define", DEFITYPE },
+ { "defn", DEFNTYPE },
+ { "divert", DIVRTYPE | NOARGS },
+ { "expr", EXPRTYPE },
+ { "eval", EXPRTYPE },
+ { "substr", SUBSTYPE },
+ { "ifelse", IFELTYPE },
+ { "ifdef", IFDFTYPE },
+ { "len", LENGTYPE },
+ { "incr", INCRTYPE },
+ { "decr", DECRTYPE },
+ { "dnl", DNLNTYPE | NOARGS },
+ { "changequote", CHNQTYPE | NOARGS },
+ { "changecom", CHNCTYPE | NOARGS },
+ { "index", INDXTYPE },
+#ifdef EXTENDED
+ { "paste", PASTTYPE },
+ { "spaste", SPASTYPE },
+ /* Newer extensions, needed to handle gnu-m4 scripts */
+ { "indir", INDIRTYPE},
+ { "builtin", BUILTINTYPE},
+ { "patsubst", PATSTYPE},
+ { "regexp", REGEXPTYPE},
+ { "esyscmd", ESYSCMDTYPE},
+ { "__file__", FILENAMETYPE | NOARGS},
+ { "__line__", LINETYPE | NOARGS},
+#endif
+ { "popdef", POPDTYPE },
+ { "pushdef", PUSDTYPE },
+ { "dumpdef", DUMPTYPE | NOARGS },
+ { "shift", SHIFTYPE | NOARGS },
+ { "translit", TRNLTYPE },
+ { "undefine", UNDFTYPE },
+ { "undivert", UNDVTYPE | NOARGS },
+ { "divnum", DIVNTYPE | NOARGS },
+ { "maketemp", MKTMTYPE },
+ { "mkstemp", MKTMTYPE },
+ { "errprint", ERRPTYPE | NOARGS },
+ { "m4wrap", M4WRTYPE | NOARGS },
+ { "m4exit", EXITTYPE | NOARGS },
+ { "syscmd", SYSCTYPE },
+ { "sysval", SYSVTYPE | NOARGS },
+ { "traceon", TRACEONTYPE | NOARGS },
+ { "traceoff", TRACEOFFTYPE | NOARGS },
+
+ { "unix", SELFTYPE | NOARGS },
+};
+
+#define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk))
+
+extern int optind;
+extern char *optarg;
+
+#define MAXRECORD 50
+static struct position {
+ char *name;
+ unsigned long line;
+} quotes[MAXRECORD], paren[MAXRECORD];
+
+static void record(struct position *, int);
+static void dump_stack(struct position *, int);
+
+static void macro(void);
+static void initkwds(void);
+static ndptr inspect(int, char *);
+static int do_look_ahead(int, const char *);
+static void reallyoutputstr(const char *);
+static void reallyputchar(int);
+
+static void enlarge_stack(void);
+
+int main(int, char *[]);
+
+int exit_code = 0;
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ int n;
+ char *p;
+
+ if (pledge("stdio rpath wpath cpath tmppath proc exec", NULL) == -1)
+ err(1, "pledge");
+
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, onintr);
+
+ init_macros();
+ initspaces();
+ STACKMAX = INITSTACKMAX;
+
+ mstack = xreallocarray(NULL, STACKMAX, sizeof(stae), NULL);
+ sstack = xalloc(STACKMAX, NULL);
+
+ maxout = 0;
+ outfile = NULL;
+ resizedivs(MAXOUT);
+
+ while ((c = getopt(argc, argv, "gst:d:D:EU:o:I:P")) != -1)
+ switch(c) {
+
+ case 'D': /* define something..*/
+ for (p = optarg; *p; p++)
+ if (*p == '=')
+ break;
+ if (*p)
+ *p++ = EOS;
+ dodefine(optarg, p);
+ break;
+ case 'E': /* like GNU m4 1.4.9+ */
+ if (error_warns == 0)
+ error_warns = 1;
+ else
+ fatal_warns = 1;
+ break;
+ case 'I':
+ addtoincludepath(optarg);
+ break;
+ case 'P':
+ prefix_builtins = 1;
+ break;
+ case 'U': /* undefine... */
+ macro_popdef(optarg);
+ break;
+ case 'g':
+ mimic_gnu = 1;
+ break;
+ case 'd':
+ set_trace_flags(optarg);
+ break;
+ case 's':
+ synch_lines = 1;
+ break;
+ case 't':
+ mark_traced(optarg, 1);
+ break;
+ case 'o':
+ trace_file(optarg);
+ break;
+ case '?':
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ initkwds();
+ if (mimic_gnu)
+ setup_builtin("format", FORMATTYPE);
+
+ active = stdout; /* default active output */
+ bbase[0] = bufbase;
+ if (!argc) {
+ sp = -1; /* stack pointer initialized */
+ fp = 0; /* frame pointer initialized */
+ set_input(infile+0, stdin, "stdin");
+ /* default input (naturally) */
+ macro();
+ } else
+ for (; argc--; ++argv) {
+ p = *argv;
+ if (p[0] == '-' && p[1] == EOS)
+ set_input(infile, stdin, "stdin");
+ else if (fopen_trypath(infile, p) == NULL)
+ err(1, "%s", p);
+ sp = -1;
+ fp = 0;
+ macro();
+ release_input(infile);
+ }
+
+ if (wrapindex) {
+ int i;
+
+ ilevel = 0; /* in case m4wrap includes.. */
+ bufbase = bp = buf; /* use the entire buffer */
+ if (mimic_gnu) {
+ while (wrapindex != 0) {
+ for (i = 0; i < wrapindex; i++)
+ pbstr(m4wraps[i]);
+ wrapindex =0;
+ macro();
+ }
+ } else {
+ for (i = 0; i < wrapindex; i++) {
+ pbstr(m4wraps[i]);
+ macro();
+ }
+ }
+ }
+
+ 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]);
+ }
+
+ return exit_code;
+}
+
+/*
+ * Look ahead for `token'.
+ * (on input `t == token[0]')
+ * Used for comment and quoting delimiters.
+ * Returns 1 if `token' present; copied to output.
+ * 0 if `token' not found; all characters pushed back
+ */
+static int
+do_look_ahead(int t, const char *token)
+{
+ int i;
+
+ assert((unsigned char)t == (unsigned char)token[0]);
+
+ for (i = 1; *++token; i++) {
+ t = gpbc();
+ if (t == EOF || (unsigned char)t != (unsigned char)*token) {
+ pushback(t);
+ while (--i)
+ pushback(*--token);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+#define LOOK_AHEAD(t, token) (t != EOF && \
+ (unsigned char)(t)==(unsigned char)(token)[0] && \
+ do_look_ahead(t,token))
+
+/*
+ * macro - the work horse..
+ */
+static void
+macro(void)
+{
+ char token[MAXTOK+1];
+ int t, l;
+ ndptr p;
+ int nlpar;
+
+ cycle {
+ t = gpbc();
+
+ if (LOOK_AHEAD(t,lquote)) { /* strip quotes */
+ nlpar = 0;
+ record(quotes, nlpar++);
+ /*
+ * Opening quote: scan forward until matching
+ * closing quote has been found.
+ */
+ do {
+
+ l = gpbc();
+ if (LOOK_AHEAD(l,rquote)) {
+ if (--nlpar > 0)
+ outputstr(rquote);
+ } else if (LOOK_AHEAD(l,lquote)) {
+ record(quotes, nlpar++);
+ outputstr(lquote);
+ } else if (l == EOF) {
+ if (nlpar == 1)
+ warnx("unclosed quote:");
+ else
+ warnx("%d unclosed quotes:", nlpar);
+ dump_stack(quotes, nlpar);
+ exit(1);
+ } else {
+ if (nlpar > 0) {
+ if (sp < 0)
+ reallyputchar(l);
+ else
+ CHRSAVE(l);
+ }
+ }
+ }
+ while (nlpar != 0);
+ } else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
+ reallyoutputstr(scommt);
+
+ for(;;) {
+ t = gpbc();
+ if (LOOK_AHEAD(t, ecommt)) {
+ reallyoutputstr(ecommt);
+ break;
+ }
+ if (t == EOF)
+ break;
+ reallyputchar(t);
+ }
+ } else if (t == '_' || isalpha(t)) {
+ p = inspect(t, token);
+ if (p != NULL)
+ pushback(l = gpbc());
+ if (p == NULL || (l != LPAREN &&
+ (macro_getdef(p)->type & NEEDARGS) != 0))
+ outputstr(token);
+ else {
+ /*
+ * real thing.. First build a call frame:
+ */
+ pushf(fp); /* previous call frm */
+ pushf(macro_getdef(p)->type); /* type of the call */
+ pushf(is_traced(p));
+ pushf(0); /* parenthesis level */
+ fp = sp; /* new frame pointer */
+ /*
+ * now push the string arguments:
+ */
+ pushdef(p); /* defn string */
+ pushs1((char *)macro_name(p)); /* macro name */
+ pushs(ep); /* start next..*/
+
+ if (l != LPAREN && PARLEV == 0) {
+ /* no bracks */
+ chrsave(EOS);
+
+ if (sp == STACKMAX)
+ errx(1, "internal stack overflow");
+ eval((const char **) mstack+fp+1, 2,
+ CALTYP, TRACESTATUS);
+
+ ep = PREVEP; /* flush strspace */
+ sp = PREVSP; /* previous sp.. */
+ fp = PREVFP; /* rewind stack...*/
+ }
+ }
+ } else if (t == EOF) {
+ if (!mimic_gnu /* you can puke right there */
+ && sp > -1 && ilevel <= 0) {
+ warnx( "unexpected end of input, unclosed parenthesis:");
+ dump_stack(paren, PARLEV);
+ exit(1);
+ }
+ if (ilevel <= 0)
+ break; /* all done thanks.. */
+ release_input(infile+ilevel--);
+ emit_synchline();
+ bufbase = bbase[ilevel];
+ continue;
+ } else if (sp < 0) { /* not in a macro at all */
+ reallyputchar(t); /* output directly.. */
+ }
+
+ else switch(t) {
+
+ case LPAREN:
+ if (PARLEV > 0)
+ chrsave(t);
+ while (isspace(l = gpbc())) /* skip blank, tab, nl.. */
+ if (PARLEV > 0)
+ chrsave(l);
+ pushback(l);
+ record(paren, PARLEV++);
+ break;
+
+ case RPAREN:
+ if (--PARLEV > 0)
+ chrsave(t);
+ else { /* end of argument list */
+ chrsave(EOS);
+
+ if (sp == STACKMAX)
+ errx(1, "internal stack overflow");
+
+ eval((const char **) mstack+fp+1, sp-fp,
+ CALTYP, TRACESTATUS);
+
+ ep = PREVEP; /* flush strspace */
+ sp = PREVSP; /* previous sp.. */
+ fp = PREVFP; /* rewind stack...*/
+ }
+ break;
+
+ case COMMA:
+ if (PARLEV == 1) {
+ chrsave(EOS); /* new argument */
+ while (isspace(l = gpbc()))
+ ;
+ pushback(l);
+ pushs(ep);
+ } else
+ chrsave(t);
+ break;
+
+ default:
+ if (LOOK_AHEAD(t, scommt)) {
+ char *p;
+ for (p = scommt; *p; p++)
+ chrsave(*p);
+ for(;;) {
+ t = gpbc();
+ if (LOOK_AHEAD(t, ecommt)) {
+ for (p = ecommt; *p; p++)
+ chrsave(*p);
+ break;
+ }
+ if (t == EOF)
+ break;
+ CHRSAVE(t);
+ }
+ } else
+ CHRSAVE(t); /* stack the char */
+ break;
+ }
+ }
+}
+
+/*
+ * output string directly, without pushing it for reparses.
+ */
+void
+outputstr(const char *s)
+{
+ if (sp < 0)
+ reallyoutputstr(s);
+ else
+ while (*s)
+ CHRSAVE(*s++);
+}
+
+void
+reallyoutputstr(const char *s)
+{
+ if (synch_lines) {
+ while (*s) {
+ fputc(*s, active);
+ if (*s++ == '\n') {
+ infile[ilevel].synch_lineno++;
+ if (infile[ilevel].synch_lineno !=
+ infile[ilevel].lineno)
+ do_emit_synchline();
+ }
+ }
+ } else
+ fputs(s, active);
+}
+
+void
+reallyputchar(int c)
+{
+ putc(c, active);
+ if (synch_lines && c == '\n') {
+ infile[ilevel].synch_lineno++;
+ if (infile[ilevel].synch_lineno != infile[ilevel].lineno)
+ do_emit_synchline();
+ }
+}
+
+/*
+ * build an input token..
+ * consider only those starting with _ or A-Za-z.
+ */
+static ndptr
+inspect(int c, char *tp)
+{
+ char *name = tp;
+ char *etp = tp+MAXTOK;
+ ndptr p;
+
+ *tp++ = c;
+
+ while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
+ *tp++ = c;
+ if (c != EOF)
+ PUSHBACK(c);
+ *tp = EOS;
+ /* token is too long, it won't match anything, but it can still
+ * be output. */
+ if (tp == ep) {
+ outputstr(name);
+ while (isalnum(c = gpbc()) || c == '_') {
+ if (sp < 0)
+ reallyputchar(c);
+ else
+ CHRSAVE(c);
+ }
+ *name = EOS;
+ return NULL;
+ }
+
+ p = ohash_find(&macros, ohash_qlookupi(&macros, name, (const char **)&tp));
+ if (p == NULL)
+ return NULL;
+ if (macro_getdef(p) == NULL)
+ return NULL;
+ return p;
+}
+
+/*
+ * 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.
+ */
+static void
+initkwds(void)
+{
+ unsigned int type;
+ int i;
+
+ for (i = 0; i < MAXKEYS; i++) {
+ type = keywrds[i].ktyp & TYPEMASK;
+ if ((keywrds[i].ktyp & NOARGS) == 0)
+ type |= NEEDARGS;
+ setup_builtin(keywrds[i].knam, type);
+ }
+}
+
+static void
+record(struct position *t, int lev)
+{
+ if (lev < MAXRECORD) {
+ t[lev].name = CURRENT_NAME;
+ t[lev].line = CURRENT_LINE;
+ }
+}
+
+static void
+dump_stack(struct position *t, int lev)
+{
+ int i;
+
+ for (i = 0; i < lev; i++) {
+ if (i == MAXRECORD) {
+ fprintf(stderr, " ...\n");
+ break;
+ }
+ fprintf(stderr, " %s at line %lu\n",
+ t[i].name, t[i].line);
+ }
+}
+
+
+static void
+enlarge_stack(void)
+{
+ STACKMAX += STACKMAX/2;
+ mstack = xreallocarray(mstack, STACKMAX, sizeof(stae),
+ "Evaluation stack overflow (%lu)",
+ (unsigned long)STACKMAX);
+ sstack = xrealloc(sstack, STACKMAX,
+ "Evaluation stack overflow (%lu)",
+ (unsigned long)STACKMAX);
+}
diff --git a/usr.bin/m4/mdef.h b/usr.bin/m4/mdef.h
new file mode 100644
index 0000000..f0d9d39
--- /dev/null
+++ b/usr.bin/m4/mdef.h
@@ -0,0 +1,237 @@
+/* $OpenBSD: mdef.h,v 1.33 2015/11/03 16:21:47 deraadt Exp $ */
+/* $NetBSD: mdef.h,v 1.7 1996/01/13 23:25:27 pk Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the 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 8.1 (Berkeley) 6/6/93
+ */
+
+#ifdef __GNUC__
+# define UNUSED __attribute__((__unused__))
+#else
+# define UNUSED
+#endif
+
+#define MACRTYPE 1
+#define DEFITYPE 2
+#define EXPRTYPE 3
+#define SUBSTYPE 4
+#define IFELTYPE 5
+#define LENGTYPE 6
+#define CHNQTYPE 7
+#define SYSCTYPE 8
+#define UNDFTYPE 9
+#define INCLTYPE 10
+#define SINCTYPE 11
+#define PASTTYPE 12
+#define SPASTYPE 13
+#define INCRTYPE 14
+#define IFDFTYPE 15
+#define PUSDTYPE 16
+#define POPDTYPE 17
+#define SHIFTYPE 18
+#define DECRTYPE 19
+#define DIVRTYPE 20
+#define UNDVTYPE 21
+#define DIVNTYPE 22
+#define MKTMTYPE 23
+#define ERRPTYPE 24
+#define M4WRTYPE 25
+#define TRNLTYPE 26
+#define DNLNTYPE 27
+#define DUMPTYPE 28
+#define CHNCTYPE 29
+#define INDXTYPE 30
+#define SYSVTYPE 31
+#define EXITTYPE 32
+#define DEFNTYPE 33
+#define SELFTYPE 34
+#define INDIRTYPE 35
+#define BUILTINTYPE 36
+#define PATSTYPE 37
+#define FILENAMETYPE 38
+#define LINETYPE 39
+#define REGEXPTYPE 40
+#define ESYSCMDTYPE 41
+#define TRACEONTYPE 42
+#define TRACEOFFTYPE 43
+#define FORMATTYPE 44
+
+#define BUILTIN_MARKER "__builtin_"
+
+#define TYPEMASK 63 /* Keep bits really corresponding to a type. */
+#define RECDEF 256 /* Pure recursive def, don't expand it */
+#define NOARGS 512 /* builtin needs no args */
+#define NEEDARGS 1024 /* mark builtin that need args with this */
+
+/*
+ * m4 special characters
+ */
+
+#define ARGFLAG '$'
+#define LPAREN '('
+#define RPAREN ')'
+#define LQUOTE '`'
+#define RQUOTE '\''
+#define COMMA ','
+#define SCOMMT '#'
+#define ECOMMT '\n'
+
+/*
+ * other important constants
+ */
+
+#define EOS '\0'
+#define MAXINP 10 /* maximum include files */
+#define MAXOUT 10 /* maximum # of diversions */
+#define BUFSIZE 4096 /* starting size of pushback buffer */
+#define INITSTACKMAX 4096 /* starting size of call stack */
+#define STRSPMAX 4096 /* starting size of string space */
+#define MAXTOK 512 /* maximum chars in a tokn */
+#define MAXCCHARS 5 /* max size of comment/quote delim */
+
+#define ALL 1
+#define TOP 0
+
+#define TRUE 1
+#define FALSE 0
+#define cycle for(;;)
+
+/*
+ * m4 data structures
+ */
+
+typedef struct ndblock *ndptr;
+
+struct macro_definition {
+ struct macro_definition *next;
+ char *defn; /* definition.. */
+ unsigned int type; /* type of the entry.. */
+};
+
+
+struct ndblock { /* hashtable structure */
+ unsigned int builtin_type;
+ unsigned int trace_flags;
+ struct macro_definition *d;
+ char name[1]; /* entry name.. */
+};
+
+typedef union { /* stack structure */
+ int sfra; /* frame entry */
+ char *sstr; /* string entry */
+} stae;
+
+struct input_file {
+ FILE *file;
+ char *name;
+ unsigned long lineno;
+ unsigned long synch_lineno; /* used for -s */
+ int c;
+};
+
+#define STORAGE_STRSPACE 0
+#define STORAGE_MACRO 1
+#define STORAGE_OTHER 2
+
+#define CURRENT_NAME (infile[ilevel].name)
+#define CURRENT_LINE (infile[ilevel].lineno)
+/*
+ * macros for readibility and/or speed
+ *
+ * gpbc() - get a possibly pushed-back character
+ * pushf() - push a call frame entry onto stack
+ * pushs() - push a string pointer onto stack
+ */
+#define gpbc() (bp > bufbase) ? *--bp : obtain_char(infile+ilevel)
+#define pushf(x) \
+ do { \
+ if (++sp == STACKMAX) \
+ enlarge_stack();\
+ mstack[sp].sfra = (x); \
+ sstack[sp] = STORAGE_OTHER; \
+ } while (0)
+
+#define pushs(x) \
+ do { \
+ if (++sp == STACKMAX) \
+ enlarge_stack();\
+ mstack[sp].sstr = (x); \
+ sstack[sp] = STORAGE_STRSPACE; \
+ } while (0)
+
+#define pushs1(x) \
+ do { \
+ if (++sp == STACKMAX) \
+ enlarge_stack();\
+ mstack[sp].sstr = (x); \
+ sstack[sp] = STORAGE_OTHER; \
+ } while (0)
+
+#define pushdef(p) \
+ do { \
+ if (++sp == STACKMAX) \
+ enlarge_stack();\
+ mstack[sp].sstr = macro_getdef(p)->defn;\
+ sstack[sp] = STORAGE_MACRO; \
+ } while (0)
+
+
+/*
+ * . .
+ * | . | <-- sp | . |
+ * +-------+ +-----+
+ * | arg 3 ----------------------->| str |
+ * +-------+ | . |
+ * | arg 2 ---PREVEP-----+ .
+ * +-------+ |
+ * . | | |
+ * +-------+ | +-----+
+ * | plev | PARLEV +-------->| str |
+ * +-------+ | . |
+ * | type | CALTYP .
+ * +-------+
+ * | prcf ---PREVFP--+
+ * +-------+ |
+ * | . | PREVSP |
+ * . |
+ * +-------+ |
+ * | <----------+
+ * +-------+
+ *
+ */
+#define PARLEV (mstack[fp].sfra)
+#define CALTYP (mstack[fp-2].sfra)
+#define TRACESTATUS (mstack[fp-1].sfra)
+#define PREVEP (mstack[fp+3].sstr)
+#define PREVSP (fp-4)
+#define PREVFP (mstack[fp-3].sfra)
diff --git a/usr.bin/m4/misc.c b/usr.bin/m4/misc.c
new file mode 100644
index 0000000..0b4f80b
--- /dev/null
+++ b/usr.bin/m4/misc.c
@@ -0,0 +1,467 @@
+/* $OpenBSD: misc.c,v 1.47 2017/06/15 13:48:42 bcallah Exp $ */
+/* $NetBSD: misc.c,v 1.6 1995/09/28 05:37:41 tls Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the 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.
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <err.h>
+#include "mdef.h"
+#include "stdd.h"
+#include "extern.h"
+#include "pathnames.h"
+
+
+char *ep; /* first free char in strspace */
+static char *strspace; /* string space for evaluation */
+char *endest; /* end of string space */
+static size_t strsize = STRSPMAX;
+static size_t bufsize = BUFSIZE;
+
+unsigned char *buf; /* push-back buffer */
+unsigned char *bufbase; /* the base for current ilevel */
+unsigned char *bbase[MAXINP]; /* the base for each ilevel */
+unsigned char *bp; /* first available character */
+unsigned char *endpbb; /* end of push-back buffer */
+
+
+/*
+ * find the index of second str in the first str.
+ */
+ptrdiff_t
+indx(const char *s1, const char *s2)
+{
+ char *t;
+
+ t = strstr(s1, s2);
+ if (t == NULL)
+ return (-1);
+ else
+ return (t - s1);
+}
+/*
+ * pushback - push character back onto input
+ */
+void
+pushback(int c)
+{
+ if (c == EOF)
+ return;
+ if (bp >= endpbb)
+ enlarge_bufspace();
+ *bp++ = c;
+}
+
+/*
+ * pbstr - push string back onto input
+ * pushback is replicated to improve
+ * performance.
+ */
+void
+pbstr(const char *s)
+{
+ size_t n;
+
+ n = strlen(s);
+ while (endpbb - bp <= n)
+ enlarge_bufspace();
+ while (n > 0)
+ *bp++ = s[--n];
+}
+
+/*
+ * pbnum - convert number to string, push back on input.
+ */
+void
+pbnum(int n)
+{
+ pbnumbase(n, 10, 0);
+}
+
+void
+pbnumbase(int n, int base, int d)
+{
+ static char digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ int num;
+ int printed = 0;
+
+ if (base > 36)
+ m4errx(1, "base %d > 36: not supported.", base);
+
+ if (base < 2)
+ m4errx(1, "bad base %d for conversion.", base);
+
+ num = (n < 0) ? -n : n;
+ do {
+ pushback(digits[num % base]);
+ printed++;
+ }
+ while ((num /= base) > 0);
+
+ if (n < 0)
+ printed++;
+ while (printed++ < d)
+ pushback('0');
+
+ if (n < 0)
+ pushback('-');
+}
+
+/*
+ * pbunsigned - convert unsigned long to string, push back on input.
+ */
+void
+pbunsigned(unsigned long n)
+{
+ do {
+ pushback(n % 10 + '0');
+ }
+ while ((n /= 10) > 0);
+}
+
+void
+initspaces()
+{
+ int i;
+
+ strspace = xalloc(strsize+1, NULL);
+ ep = strspace;
+ endest = strspace+strsize;
+ buf = xalloc(bufsize, NULL);
+ bufbase = buf;
+ bp = buf;
+ endpbb = buf + bufsize;
+ for (i = 0; i < MAXINP; i++)
+ bbase[i] = buf;
+}
+
+void
+enlarge_strspace()
+{
+ char *newstrspace;
+ int i;
+
+ strsize *= 2;
+ newstrspace = malloc(strsize + 1);
+ if (!newstrspace)
+ errx(1, "string space overflow");
+ memcpy(newstrspace, strspace, strsize/2);
+ for (i = 0; i <= sp; i++)
+ if (sstack[i] == STORAGE_STRSPACE)
+ mstack[i].sstr = (mstack[i].sstr - strspace)
+ + newstrspace;
+ ep = (ep-strspace) + newstrspace;
+ free(strspace);
+ strspace = newstrspace;
+ endest = strspace + strsize;
+}
+
+void
+enlarge_bufspace()
+{
+ unsigned char *newbuf;
+ int i;
+
+ bufsize += bufsize/2;
+ newbuf = xrealloc(buf, bufsize, "too many characters pushed back");
+ for (i = 0; i < MAXINP; i++)
+ bbase[i] = (bbase[i]-buf)+newbuf;
+ bp = (bp-buf)+newbuf;
+ bufbase = (bufbase-buf)+newbuf;
+ buf = newbuf;
+ endpbb = buf+bufsize;
+}
+
+/*
+ * chrsave - put single char on string space
+ */
+void
+chrsave(int c)
+{
+ if (ep >= endest)
+ enlarge_strspace();
+ *ep++ = c;
+}
+
+/*
+ * read in a diversion file, and dispose it.
+ */
+void
+getdiv(int n)
+{
+ int c;
+
+ if (active == outfile[n])
+ m4errx(1, "undivert: diversion still active.");
+ rewind(outfile[n]);
+ while ((c = getc(outfile[n])) != EOF)
+ putc(c, active);
+ (void) fclose(outfile[n]);
+ outfile[n] = NULL;
+}
+
+void
+onintr(int signo)
+{
+#define intrmessage "m4: interrupted.\n"
+ write(STDERR_FILENO, intrmessage, sizeof(intrmessage)-1);
+ _exit(1);
+}
+
+/*
+ * killdiv - get rid of the diversion files
+ */
+void
+killdiv()
+{
+ int n;
+
+ for (n = 0; n < maxout; n++)
+ if (outfile[n] != NULL) {
+ (void) fclose(outfile[n]);
+ }
+}
+
+extern char *__progname;
+
+void
+m4errx(int eval, const char *fmt, ...)
+{
+ fprintf(stderr, "%s: ", __progname);
+ fprintf(stderr, "%s at line %lu: ", CURRENT_NAME, CURRENT_LINE);
+ if (fmt != NULL) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+ fprintf(stderr, "\n");
+ exit(eval);
+}
+
+/*
+ * resizedivs: allocate more diversion files */
+void
+resizedivs(int n)
+{
+ int i;
+
+ outfile = xreallocarray(outfile, n, sizeof(FILE *),
+ "too many diverts %d", n);
+ for (i = maxout; i < n; i++)
+ outfile[i] = NULL;
+ maxout = n;
+}
+
+void *
+xalloc(size_t n, const char *fmt, ...)
+{
+ void *p = malloc(n);
+
+ if (p == NULL) {
+ if (fmt == NULL)
+ err(1, "malloc");
+ else {
+ va_list va;
+
+ va_start(va, fmt);
+ verr(1, fmt, va);
+ va_end(va);
+ }
+ }
+ return p;
+}
+
+void *
+xcalloc(size_t n, size_t s, const char *fmt, ...)
+{
+ void *p = calloc(n, s);
+
+ if (p == NULL) {
+ if (fmt == NULL)
+ err(1, "calloc");
+ else {
+ va_list va;
+
+ va_start(va, fmt);
+ verr(1, fmt, va);
+ va_end(va);
+ }
+ }
+ return p;
+}
+
+void *
+xrealloc(void *old, size_t n, const char *fmt, ...)
+{
+ char *p = realloc(old, n);
+
+ if (p == NULL) {
+ free(old);
+ if (fmt == NULL)
+ err(1, "realloc");
+ else {
+ va_list va;
+
+ va_start(va, fmt);
+ verr(1, fmt, va);
+ va_end(va);
+ }
+ }
+ return p;
+}
+
+void *
+xreallocarray(void *old, size_t s1, size_t s2, const char *fmt, ...)
+{
+ void *p = reallocarray(old, s1, s2);
+
+ if (p == NULL) {
+ free(old);
+ if (fmt == NULL)
+ err(1, "reallocarray");
+ else {
+ va_list va;
+
+ va_start(va, fmt);
+ verr(1, fmt, va);
+ va_end(va);
+ }
+ }
+ return p;
+}
+
+char *
+xstrdup(const char *s)
+{
+ char *p = strdup(s);
+ if (p == NULL)
+ err(1, "strdup");
+ return p;
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: m4 [-EgPs] [-Dname[=value]] [-d flags] "
+ "[-I dirname] [-o filename]\n"
+ "\t[-t macro] [-Uname] [file ...]\n");
+ exit(1);
+}
+
+int
+obtain_char(struct input_file *f)
+{
+ if (f->c == EOF)
+ return EOF;
+
+ f->c = fgetc(f->file);
+ if (f->c == '\n')
+ f->lineno++;
+
+ return f->c;
+}
+
+void
+set_input(struct input_file *f, FILE *real, const char *name)
+{
+ f->file = real;
+ f->lineno = 1;
+ f->c = 0;
+ f->name = xstrdup(name);
+ emit_synchline();
+}
+
+void
+do_emit_synchline()
+{
+ fprintf(active, "#line %lu \"%s\"\n",
+ infile[ilevel].lineno, infile[ilevel].name);
+ infile[ilevel].synch_lineno = infile[ilevel].lineno;
+}
+
+void
+release_input(struct input_file *f)
+{
+ if (ferror(f->file))
+ errx(1, "Fatal error reading from %s\n", f->name);
+ if (f->file != stdin)
+ fclose(f->file);
+ f->c = EOF;
+ /*
+ * XXX can't free filename, as there might still be
+ * error information pointing to it.
+ */
+}
+
+void
+doprintlineno(struct input_file *f)
+{
+ pbunsigned(f->lineno);
+}
+
+void
+doprintfilename(struct input_file *f)
+{
+ pbstr(rquote);
+ pbstr(f->name);
+ pbstr(lquote);
+}
+
+/*
+ * buffer_mark/dump_buffer: allows one to save a mark in a buffer,
+ * and later dump everything that was added since then to a file.
+ */
+size_t
+buffer_mark()
+{
+ return bp - buf;
+}
+
+
+void
+dump_buffer(FILE *f, size_t m)
+{
+ unsigned char *s;
+
+ for (s = bp; s-buf > m;)
+ fputc(*--s, f);
+}
diff --git a/usr.bin/m4/parser.c b/usr.bin/m4/parser.c
new file mode 100644
index 0000000..85e191c
--- /dev/null
+++ b/usr.bin/m4/parser.c
@@ -0,0 +1,756 @@
+/* original parser id follows */
+/* yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93" */
+/* (use YYMAJOR/YYMINOR for ifdefs dependent on parser version) */
+
+#define YYBYACC 1
+#define YYMAJOR 2
+#define YYMINOR 0
+#define YYPATCH 20200910
+
+#define YYEMPTY (-1)
+#define yyclearin (yychar = YYEMPTY)
+#define yyerrok (yyerrflag = 0)
+#define YYRECOVERING() (yyerrflag != 0)
+#define YYENOMEM (-2)
+#define YYEOF 0
+#define YYPREFIX "yy"
+
+#define YYPURE 0
+
+#line 2 "usr.bin/m4/parser.y"
+/* $OpenBSD: parser.y,v 1.7 2012/04/12 17:00:11 espie Exp $ */
+/*
+ * Copyright (c) 2004 Marc Espie <espie@cvs.openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#define YYSTYPE int32_t
+extern int32_t end_result;
+extern int yylex(void);
+extern int yyerror(const char *);
+#line 45 "usr.bin/m4/parser.c"
+
+#if ! defined(YYSTYPE) && ! defined(YYSTYPE_IS_DECLARED)
+/* Default: YYSTYPE is the semantic value type. */
+typedef int YYSTYPE;
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+/* compatibility with bison */
+#ifdef YYPARSE_PARAM
+/* compatibility with FreeBSD */
+# ifdef YYPARSE_PARAM_TYPE
+# define YYPARSE_DECL() yyparse(YYPARSE_PARAM_TYPE YYPARSE_PARAM)
+# else
+# define YYPARSE_DECL() yyparse(void *YYPARSE_PARAM)
+# endif
+#else
+# define YYPARSE_DECL() yyparse(void)
+#endif
+
+/* Parameters sent to lex. */
+#ifdef YYLEX_PARAM
+# define YYLEX_DECL() yylex(void *YYLEX_PARAM)
+# define YYLEX yylex(YYLEX_PARAM)
+#else
+# define YYLEX_DECL() yylex(void)
+# define YYLEX yylex()
+#endif
+
+#if !(defined(yylex) || defined(YYSTATE))
+int YYLEX_DECL();
+#endif
+
+/* Parameters sent to yyerror. */
+#ifndef YYERROR_DECL
+#define YYERROR_DECL() yyerror(const char *s)
+#endif
+#ifndef YYERROR_CALL
+#define YYERROR_CALL(msg) yyerror(msg)
+#endif
+
+extern int YYPARSE_DECL();
+
+#define NUMBER 257
+#define ERROR 258
+#define LOR 259
+#define LAND 260
+#define EQ 261
+#define NE 262
+#define LE 263
+#define GE 264
+#define LSHIFT 265
+#define RSHIFT 266
+#define EXPONENT 267
+#define UMINUS 268
+#define UPLUS 269
+#define YYERRCODE 256
+typedef short YYINT;
+static const YYINT yylhs[] = { -1,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1,
+};
+static const YYINT yylen[] = { 2,
+ 1, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 2, 2, 2, 2, 1,
+};
+static const YYINT yydefred[] = { 0,
+ 26, 0, 0, 0, 0, 0, 0, 0, 23, 22,
+ 24, 25, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 21, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0,
+};
+static const YYINT yydgoto[] = { 7,
+ 8,
+};
+static const YYINT yysindex[] = { 95,
+ 0, 95, 95, 95, 95, 95, 0, 397, 0, 0,
+ 0, 0, 383, 95, 95, 95, 95, 95, 95, 95,
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
+ 95, 95, 0, 428, 471, 482, 185, 437, 493, 493,
+ -10, -10, -10, -10, -23, -23, -34, -34, -267, -267,
+ -267, -267,
+};
+static const YYINT yyrindex[] = { 0,
+ 0, 0, 0, 0, 0, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 11, 62, 23, 101, 308, 201, 243,
+ 124, 130, 144, 155, 79, 116, 51, 67, 1, 12,
+ 28, 40,
+};
+static const YYINT yygindex[] = { 0,
+ 582,
+};
+#define YYTABLESIZE 760
+static const YYINT yytable[] = { 32,
+ 5, 1, 31, 0, 0, 0, 0, 29, 0, 0,
+ 20, 6, 30, 31, 0, 0, 0, 0, 29, 27,
+ 0, 28, 18, 30, 0, 0, 31, 7, 0, 0,
+ 0, 29, 27, 0, 28, 0, 30, 5, 5, 4,
+ 0, 5, 5, 5, 0, 5, 0, 5, 6, 6,
+ 2, 20, 6, 6, 6, 0, 6, 0, 6, 0,
+ 5, 19, 5, 18, 7, 7, 3, 0, 7, 7,
+ 7, 6, 7, 6, 7, 0, 4, 4, 8, 0,
+ 4, 4, 4, 0, 4, 0, 4, 7, 2, 7,
+ 0, 2, 0, 2, 5, 2, 0, 0, 0, 4,
+ 17, 4, 19, 0, 3, 6, 0, 3, 0, 3,
+ 2, 3, 2, 0, 0, 9, 8, 0, 0, 8,
+ 0, 7, 0, 10, 5, 0, 3, 4, 3, 12,
+ 0, 0, 0, 4, 6, 6, 0, 2, 8, 3,
+ 8, 17, 0, 11, 2, 0, 18, 0, 0, 0,
+ 0, 7, 0, 9, 13, 0, 9, 0, 0, 0,
+ 3, 10, 0, 4, 10, 0, 0, 12, 0, 0,
+ 12, 0, 8, 0, 2, 9, 0, 9, 0, 0,
+ 0, 11, 0, 10, 11, 10, 0, 0, 0, 12,
+ 3, 12, 13, 0, 17, 13, 0, 0, 0, 0,
+ 14, 0, 8, 11, 0, 11, 0, 0, 0, 9,
+ 0, 0, 0, 0, 13, 0, 13, 10, 0, 0,
+ 5, 31, 18, 12, 17, 0, 29, 27, 0, 28,
+ 0, 30, 32, 0, 0, 0, 0, 11, 14, 9,
+ 0, 14, 15, 32, 21, 0, 23, 10, 13, 0,
+ 0, 0, 0, 12, 25, 26, 32, 0, 0, 5,
+ 5, 5, 5, 5, 5, 5, 5, 11, 0, 20,
+ 6, 6, 6, 6, 6, 6, 6, 6, 13, 0,
+ 15, 18, 18, 15, 0, 0, 7, 7, 7, 7,
+ 7, 7, 7, 7, 14, 0, 0, 0, 4, 4,
+ 4, 4, 4, 4, 4, 4, 0, 16, 0, 2,
+ 2, 2, 2, 2, 2, 2, 2, 0, 0, 0,
+ 19, 19, 0, 0, 14, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 0, 0, 15, 8, 8, 8,
+ 8, 8, 8, 8, 8, 16, 0, 0, 16, 0,
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 17,
+ 17, 0, 0, 0, 0, 0, 15, 0, 0, 0,
+ 0, 0, 0, 0, 9, 9, 9, 9, 9, 9,
+ 9, 9, 10, 10, 10, 10, 10, 10, 12, 12,
+ 12, 12, 12, 12, 0, 0, 0, 0, 0, 0,
+ 0, 16, 11, 11, 11, 11, 11, 11, 0, 0,
+ 0, 0, 0, 13, 13, 13, 13, 13, 13, 31,
+ 18, 0, 0, 33, 29, 27, 0, 28, 0, 30,
+ 0, 16, 0, 31, 18, 0, 0, 0, 29, 27,
+ 0, 28, 21, 30, 23, 19, 20, 22, 24, 25,
+ 26, 32, 0, 0, 0, 0, 21, 0, 23, 14,
+ 14, 14, 14, 0, 31, 18, 0, 0, 0, 29,
+ 27, 0, 28, 31, 30, 0, 17, 0, 29, 27,
+ 0, 28, 0, 30, 0, 0, 0, 21, 0, 23,
+ 17, 0, 0, 0, 0, 0, 21, 0, 23, 0,
+ 0, 15, 15, 15, 15, 0, 16, 31, 18, 0,
+ 0, 0, 29, 27, 0, 28, 0, 30, 31, 18,
+ 16, 17, 0, 29, 27, 0, 28, 0, 30, 31,
+ 21, 0, 23, 0, 29, 27, 0, 28, 0, 30,
+ 0, 21, 0, 23, 0, 0, 0, 0, 0, 0,
+ 0, 16, 21, 0, 23, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 17, 0, 16, 16, 0, 0,
+ 0, 0, 0, 0, 0, 17, 0, 0, 0, 0,
+ 0, 0, 0, 9, 10, 11, 12, 13, 0, 0,
+ 0, 0, 0, 0, 16, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 14, 15, 19, 20, 22, 24, 25, 26, 32,
+ 0, 0, 0, 0, 0, 14, 15, 19, 20, 22,
+ 24, 25, 26, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 15, 19, 20,
+ 22, 24, 25, 26, 32, 0, 0, 19, 20, 22,
+ 24, 25, 26, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 19, 20, 22, 24, 25, 26, 32, 0, 0,
+ 0, 0, 19, 20, 22, 24, 25, 26, 32, 0,
+ 0, 0, 0, 0, 0, 22, 24, 25, 26, 32,
+};
+static const YYINT yycheck[] = { 267,
+ 0, 0, 37, -1, -1, -1, -1, 42, -1, -1,
+ 0, 0, 47, 37, -1, -1, -1, -1, 42, 43,
+ -1, 45, 0, 47, -1, -1, 37, 0, -1, -1,
+ -1, 42, 43, -1, 45, -1, 47, 37, 38, 0,
+ -1, 41, 42, 43, -1, 45, -1, 47, 37, 38,
+ 0, 41, 41, 42, 43, -1, 45, -1, 47, -1,
+ 60, 0, 62, 41, 37, 38, 0, -1, 41, 42,
+ 43, 60, 45, 62, 47, -1, 37, 38, 0, -1,
+ 41, 42, 43, -1, 45, -1, 47, 60, 38, 62,
+ -1, 41, -1, 43, 94, 45, -1, -1, -1, 60,
+ 0, 62, 41, -1, 38, 94, -1, 41, -1, 43,
+ 60, 45, 62, -1, -1, 0, 38, -1, -1, 41,
+ -1, 94, -1, 0, 124, -1, 60, 33, 62, 0,
+ -1, -1, -1, 94, 40, 124, -1, 43, 60, 45,
+ 62, 41, -1, 0, 94, -1, 124, -1, -1, -1,
+ -1, 124, -1, 38, 0, -1, 41, -1, -1, -1,
+ 94, 38, -1, 124, 41, -1, -1, 38, -1, -1,
+ 41, -1, 94, -1, 124, 60, -1, 62, -1, -1,
+ -1, 38, -1, 60, 41, 62, -1, -1, -1, 60,
+ 124, 62, 38, -1, 94, 41, -1, -1, -1, -1,
+ 0, -1, 124, 60, -1, 62, -1, -1, -1, 94,
+ -1, -1, -1, -1, 60, -1, 62, 94, -1, -1,
+ 126, 37, 38, 94, 124, -1, 42, 43, -1, 45,
+ -1, 47, 267, -1, -1, -1, -1, 94, 38, 124,
+ -1, 41, 0, 267, 60, -1, 62, 124, 94, -1,
+ -1, -1, -1, 124, 265, 266, 267, -1, -1, 259,
+ 260, 261, 262, 263, 264, 265, 266, 124, -1, 259,
+ 259, 260, 261, 262, 263, 264, 265, 266, 124, -1,
+ 38, 259, 260, 41, -1, -1, 259, 260, 261, 262,
+ 263, 264, 265, 266, 94, -1, -1, -1, 259, 260,
+ 261, 262, 263, 264, 265, 266, -1, 0, -1, 259,
+ 260, 261, 262, 263, 264, 265, 266, -1, -1, -1,
+ 259, 260, -1, -1, 124, 259, 260, 261, 262, 263,
+ 264, 265, 266, -1, -1, -1, 94, 259, 260, 261,
+ 262, 263, 264, 265, 266, 38, -1, -1, 41, -1,
+ -1, 257, -1, -1, -1, -1, -1, -1, -1, 259,
+ 260, -1, -1, -1, -1, -1, 124, -1, -1, -1,
+ -1, -1, -1, -1, 259, 260, 261, 262, 263, 264,
+ 265, 266, 259, 260, 261, 262, 263, 264, 259, 260,
+ 261, 262, 263, 264, -1, -1, -1, -1, -1, -1,
+ -1, 94, 259, 260, 261, 262, 263, 264, -1, -1,
+ -1, -1, -1, 259, 260, 261, 262, 263, 264, 37,
+ 38, -1, -1, 41, 42, 43, -1, 45, -1, 47,
+ -1, 124, -1, 37, 38, -1, -1, -1, 42, 43,
+ -1, 45, 60, 47, 62, 261, 262, 263, 264, 265,
+ 266, 267, -1, -1, -1, -1, 60, -1, 62, 259,
+ 260, 261, 262, -1, 37, 38, -1, -1, -1, 42,
+ 43, -1, 45, 37, 47, -1, 94, -1, 42, 43,
+ -1, 45, -1, 47, -1, -1, -1, 60, -1, 62,
+ 94, -1, -1, -1, -1, -1, 60, -1, 62, -1,
+ -1, 259, 260, 261, 262, -1, 124, 37, 38, -1,
+ -1, -1, 42, 43, -1, 45, -1, 47, 37, 38,
+ 124, 94, -1, 42, 43, -1, 45, -1, 47, 37,
+ 60, -1, 62, -1, 42, 43, -1, 45, -1, 47,
+ -1, 60, -1, 62, -1, -1, -1, -1, -1, -1,
+ -1, 124, 60, -1, 62, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 94, -1, 259, 260, -1, -1,
+ -1, -1, -1, -1, -1, 94, -1, -1, -1, -1,
+ -1, -1, -1, 2, 3, 4, 5, 6, -1, -1,
+ -1, -1, -1, -1, 124, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+ -1, -1, -1, -1, -1, 259, 260, 261, 262, 263,
+ 264, 265, 266, 267, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 260, 261, 262,
+ 263, 264, 265, 266, 267, -1, -1, 261, 262, 263,
+ 264, 265, 266, 267, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 261, 262, 263, 264, 265, 266, 267, -1, -1,
+ -1, -1, 261, 262, 263, 264, 265, 266, 267, -1,
+ -1, -1, -1, -1, -1, 263, 264, 265, 266, 267,
+};
+#define YYFINAL 7
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 269
+#define YYUNDFTOKEN 273
+#define YYTRANSLATE(a) ((a) > YYMAXTOKEN ? YYUNDFTOKEN : (a))
+#if YYDEBUG
+static const char *const yyname[] = {
+
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+"'!'",0,0,0,"'%'","'&'",0,"'('","')'","'*'","'+'",0,"'-'",0,"'/'",0,0,0,0,0,0,0,
+0,0,0,0,0,"'<'",0,"'>'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,"'^'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'|'",0,
+"'~'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,"NUMBER","ERROR","LOR","LAND","EQ","NE","LE","GE",
+"LSHIFT","RSHIFT","EXPONENT","UMINUS","UPLUS",0,0,0,"illegal-symbol",
+};
+static const char *const yyrule[] = {
+"$accept : top",
+"top : expr",
+"expr : expr '+' expr",
+"expr : expr '-' expr",
+"expr : expr EXPONENT expr",
+"expr : expr '*' expr",
+"expr : expr '/' expr",
+"expr : expr '%' expr",
+"expr : expr LSHIFT expr",
+"expr : expr RSHIFT expr",
+"expr : expr '<' expr",
+"expr : expr '>' expr",
+"expr : expr LE expr",
+"expr : expr GE expr",
+"expr : expr EQ expr",
+"expr : expr NE expr",
+"expr : expr '&' expr",
+"expr : expr '^' expr",
+"expr : expr '|' expr",
+"expr : expr LAND expr",
+"expr : expr LOR expr",
+"expr : '(' expr ')'",
+"expr : '-' expr",
+"expr : '+' expr",
+"expr : '!' expr",
+"expr : '~' expr",
+"expr : NUMBER",
+
+};
+#endif
+
+#if YYDEBUG
+int yydebug;
+#endif
+
+int yyerrflag;
+int yychar;
+YYSTYPE yyval;
+YYSTYPE yylval;
+int yynerrs;
+
+/* define the initial stack-sizes */
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 10000
+#define YYMAXDEPTH 10000
+#endif
+#endif
+
+#define YYINITSTACKSIZE 200
+
+typedef struct {
+ unsigned stacksize;
+ YYINT *s_base;
+ YYINT *s_mark;
+ YYINT *s_last;
+ YYSTYPE *l_base;
+ YYSTYPE *l_mark;
+} YYSTACKDATA;
+/* variables for the parser stack */
+static YYSTACKDATA yystack;
+#line 84 "usr.bin/m4/parser.y"
+
+#line 389 "usr.bin/m4/parser.c"
+
+#if YYDEBUG
+#include <stdio.h> /* needed for printf */
+#endif
+
+#include <stdlib.h> /* needed for malloc, etc */
+#include <string.h> /* needed for memset */
+
+/* allocate initial stack or double stack size, up to YYMAXDEPTH */
+static int yygrowstack(YYSTACKDATA *data)
+{
+ int i;
+ unsigned newsize;
+ YYINT *newss;
+ YYSTYPE *newvs;
+
+ if ((newsize = data->stacksize) == 0)
+ newsize = YYINITSTACKSIZE;
+ else if (newsize >= YYMAXDEPTH)
+ return YYENOMEM;
+ else if ((newsize *= 2) > YYMAXDEPTH)
+ newsize = YYMAXDEPTH;
+
+ i = (int) (data->s_mark - data->s_base);
+ newss = (YYINT *)realloc(data->s_base, newsize * sizeof(*newss));
+ if (newss == 0)
+ return YYENOMEM;
+
+ data->s_base = newss;
+ data->s_mark = newss + i;
+
+ newvs = (YYSTYPE *)realloc(data->l_base, newsize * sizeof(*newvs));
+ if (newvs == 0)
+ return YYENOMEM;
+
+ data->l_base = newvs;
+ data->l_mark = newvs + i;
+
+ data->stacksize = newsize;
+ data->s_last = data->s_base + newsize - 1;
+ return 0;
+}
+
+#if YYPURE || defined(YY_NO_LEAKS)
+static void yyfreestack(YYSTACKDATA *data)
+{
+ free(data->s_base);
+ free(data->l_base);
+ memset(data, 0, sizeof(*data));
+}
+#else
+#define yyfreestack(data) /* nothing */
+#endif
+
+#define YYABORT goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+
+int
+YYPARSE_DECL()
+{
+ int yym, yyn, yystate;
+#if YYDEBUG
+ const char *yys;
+
+ if ((yys = getenv("YYDEBUG")) != 0)
+ {
+ yyn = *yys;
+ if (yyn >= '0' && yyn <= '9')
+ yydebug = yyn - '0';
+ }
+#endif
+
+ yym = 0;
+ yyn = 0;
+ yynerrs = 0;
+ yyerrflag = 0;
+ yychar = YYEMPTY;
+ yystate = 0;
+
+#if YYPURE
+ memset(&yystack, 0, sizeof(yystack));
+#endif
+
+ if (yystack.s_base == NULL && yygrowstack(&yystack) == YYENOMEM) goto yyoverflow;
+ yystack.s_mark = yystack.s_base;
+ yystack.l_mark = yystack.l_base;
+ yystate = 0;
+ *yystack.s_mark = 0;
+
+yyloop:
+ if ((yyn = yydefred[yystate]) != 0) goto yyreduce;
+ if (yychar < 0)
+ {
+ yychar = YYLEX;
+ if (yychar < 0) yychar = YYEOF;
+#if YYDEBUG
+ if (yydebug)
+ {
+ if ((yys = yyname[YYTRANSLATE(yychar)]) == NULL) yys = yyname[YYUNDFTOKEN];
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ }
+ if (((yyn = yysindex[yystate]) != 0) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == (YYINT) yychar)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, shifting to state %d\n",
+ YYPREFIX, yystate, yytable[yyn]);
+#endif
+ if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack) == YYENOMEM) goto yyoverflow;
+ yystate = yytable[yyn];
+ *++yystack.s_mark = yytable[yyn];
+ *++yystack.l_mark = yylval;
+ yychar = YYEMPTY;
+ if (yyerrflag > 0) --yyerrflag;
+ goto yyloop;
+ }
+ if (((yyn = yyrindex[yystate]) != 0) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == (YYINT) yychar)
+ {
+ yyn = yytable[yyn];
+ goto yyreduce;
+ }
+ if (yyerrflag != 0) goto yyinrecovery;
+
+ YYERROR_CALL("syntax error");
+
+ goto yyerrlab; /* redundant goto avoids 'unused label' warning */
+yyerrlab:
+ ++yynerrs;
+
+yyinrecovery:
+ if (yyerrflag < 3)
+ {
+ yyerrflag = 3;
+ for (;;)
+ {
+ if (((yyn = yysindex[*yystack.s_mark]) != 0) && (yyn += YYERRCODE) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == (YYINT) YYERRCODE)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yystack.s_mark, yytable[yyn]);
+#endif
+ if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack) == YYENOMEM) goto yyoverflow;
+ yystate = yytable[yyn];
+ *++yystack.s_mark = yytable[yyn];
+ *++yystack.l_mark = yylval;
+ goto yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: error recovery discarding state %d\n",
+ YYPREFIX, *yystack.s_mark);
+#endif
+ if (yystack.s_mark <= yystack.s_base) goto yyabort;
+ --yystack.s_mark;
+ --yystack.l_mark;
+ }
+ }
+ }
+ else
+ {
+ if (yychar == YYEOF) goto yyabort;
+#if YYDEBUG
+ if (yydebug)
+ {
+ if ((yys = yyname[YYTRANSLATE(yychar)]) == NULL) yys = yyname[YYUNDFTOKEN];
+ printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ yychar = YYEMPTY;
+ goto yyloop;
+ }
+
+yyreduce:
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+ YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+ yym = yylen[yyn];
+ if (yym > 0)
+ yyval = yystack.l_mark[1-yym];
+ else
+ memset(&yyval, 0, sizeof yyval);
+
+ switch (yyn)
+ {
+case 1:
+#line 43 "usr.bin/m4/parser.y"
+ { end_result = yystack.l_mark[0]; }
+break;
+case 2:
+#line 45 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] + yystack.l_mark[0]; }
+break;
+case 3:
+#line 46 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] - yystack.l_mark[0]; }
+break;
+case 4:
+#line 47 "usr.bin/m4/parser.y"
+ { yyval = pow(yystack.l_mark[-2], yystack.l_mark[0]); }
+break;
+case 5:
+#line 48 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] * yystack.l_mark[0]; }
+break;
+case 6:
+#line 49 "usr.bin/m4/parser.y"
+ {
+ if (yystack.l_mark[0] == 0) {
+ yyerror("division by zero");
+ exit(1);
+ }
+ yyval = yystack.l_mark[-2] / yystack.l_mark[0];
+ }
+break;
+case 7:
+#line 56 "usr.bin/m4/parser.y"
+ {
+ if (yystack.l_mark[0] == 0) {
+ yyerror("modulo zero");
+ exit(1);
+ }
+ yyval = yystack.l_mark[-2] % yystack.l_mark[0];
+ }
+break;
+case 8:
+#line 63 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] << yystack.l_mark[0]; }
+break;
+case 9:
+#line 64 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] >> yystack.l_mark[0]; }
+break;
+case 10:
+#line 65 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] < yystack.l_mark[0]; }
+break;
+case 11:
+#line 66 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] > yystack.l_mark[0]; }
+break;
+case 12:
+#line 67 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] <= yystack.l_mark[0]; }
+break;
+case 13:
+#line 68 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] >= yystack.l_mark[0]; }
+break;
+case 14:
+#line 69 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] == yystack.l_mark[0]; }
+break;
+case 15:
+#line 70 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] != yystack.l_mark[0]; }
+break;
+case 16:
+#line 71 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] & yystack.l_mark[0]; }
+break;
+case 17:
+#line 72 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] ^ yystack.l_mark[0]; }
+break;
+case 18:
+#line 73 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] | yystack.l_mark[0]; }
+break;
+case 19:
+#line 74 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] && yystack.l_mark[0]; }
+break;
+case 20:
+#line 75 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-2] || yystack.l_mark[0]; }
+break;
+case 21:
+#line 76 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[-1]; }
+break;
+case 22:
+#line 77 "usr.bin/m4/parser.y"
+ { yyval = -yystack.l_mark[0]; }
+break;
+case 23:
+#line 78 "usr.bin/m4/parser.y"
+ { yyval = yystack.l_mark[0]; }
+break;
+case 24:
+#line 79 "usr.bin/m4/parser.y"
+ { yyval = !yystack.l_mark[0]; }
+break;
+case 25:
+#line 80 "usr.bin/m4/parser.y"
+ { yyval = ~yystack.l_mark[0]; }
+break;
+#line 700 "usr.bin/m4/parser.c"
+ }
+ yystack.s_mark -= yym;
+ yystate = *yystack.s_mark;
+ yystack.l_mark -= yym;
+ yym = yylhs[yyn];
+ if (yystate == 0 && yym == 0)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+ yystate = YYFINAL;
+ *++yystack.s_mark = YYFINAL;
+ *++yystack.l_mark = yyval;
+ if (yychar < 0)
+ {
+ yychar = YYLEX;
+ if (yychar < 0) yychar = YYEOF;
+#if YYDEBUG
+ if (yydebug)
+ {
+ if ((yys = yyname[YYTRANSLATE(yychar)]) == NULL) yys = yyname[YYUNDFTOKEN];
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, YYFINAL, yychar, yys);
+ }
+#endif
+ }
+ if (yychar == YYEOF) goto yyaccept;
+ goto yyloop;
+ }
+ if (((yyn = yygindex[yym]) != 0) && (yyn += yystate) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == (YYINT) yystate)
+ yystate = yytable[yyn];
+ else
+ yystate = yydgoto[yym];
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yystack.s_mark, yystate);
+#endif
+ if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack) == YYENOMEM) goto yyoverflow;
+ *++yystack.s_mark = (YYINT) yystate;
+ *++yystack.l_mark = yyval;
+ goto yyloop;
+
+yyoverflow:
+ YYERROR_CALL("yacc stack overflow");
+
+yyabort:
+ yyfreestack(&yystack);
+ return (1);
+
+yyaccept:
+ yyfreestack(&yystack);
+ return (0);
+}
diff --git a/usr.bin/m4/parser.tab.h b/usr.bin/m4/parser.tab.h
new file mode 100644
index 0000000..82c3c10
--- /dev/null
+++ b/usr.bin/m4/parser.tab.h
@@ -0,0 +1,13 @@
+#define NUMBER 257
+#define ERROR 258
+#define LOR 259
+#define LAND 260
+#define EQ 261
+#define NE 262
+#define LE 263
+#define GE 264
+#define LSHIFT 265
+#define RSHIFT 266
+#define EXPONENT 267
+#define UMINUS 268
+#define UPLUS 269
diff --git a/usr.bin/m4/parser.y b/usr.bin/m4/parser.y
new file mode 100644
index 0000000..fedded1
--- /dev/null
+++ b/usr.bin/m4/parser.y
@@ -0,0 +1,84 @@
+%{
+/* $OpenBSD: parser.y,v 1.7 2012/04/12 17:00:11 espie Exp $ */
+/*
+ * Copyright (c) 2004 Marc Espie <espie@cvs.openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#define YYSTYPE int32_t
+extern int32_t end_result;
+extern int yylex(void);
+extern int yyerror(const char *);
+%}
+%token NUMBER
+%token ERROR
+%left LOR
+%left LAND
+%left '|'
+%left '^'
+%left '&'
+%left EQ NE
+%left '<' LE '>' GE
+%left LSHIFT RSHIFT
+%left '+' '-'
+%left '*' '/' '%'
+%right EXPONENT
+%right UMINUS UPLUS '!' '~'
+
+%%
+
+top : expr { end_result = $1; }
+ ;
+expr : expr '+' expr { $$ = $1 + $3; }
+ | expr '-' expr { $$ = $1 - $3; }
+ | expr EXPONENT expr { $$ = pow($1, $3); }
+ | expr '*' expr { $$ = $1 * $3; }
+ | expr '/' expr {
+ if ($3 == 0) {
+ yyerror("division by zero");
+ exit(1);
+ }
+ $$ = $1 / $3;
+ }
+ | expr '%' expr {
+ if ($3 == 0) {
+ yyerror("modulo zero");
+ exit(1);
+ }
+ $$ = $1 % $3;
+ }
+ | expr LSHIFT expr { $$ = $1 << $3; }
+ | expr RSHIFT expr { $$ = $1 >> $3; }
+ | expr '<' expr { $$ = $1 < $3; }
+ | expr '>' expr { $$ = $1 > $3; }
+ | expr LE expr { $$ = $1 <= $3; }
+ | expr GE expr { $$ = $1 >= $3; }
+ | expr EQ expr { $$ = $1 == $3; }
+ | expr NE expr { $$ = $1 != $3; }
+ | expr '&' expr { $$ = $1 & $3; }
+ | expr '^' expr { $$ = $1 ^ $3; }
+ | expr '|' expr { $$ = $1 | $3; }
+ | expr LAND expr { $$ = $1 && $3; }
+ | expr LOR expr { $$ = $1 || $3; }
+ | '(' expr ')' { $$ = $2; }
+ | '-' expr %prec UMINUS { $$ = -$2; }
+ | '+' expr %prec UPLUS { $$ = $2; }
+ | '!' expr { $$ = !$2; }
+ | '~' expr { $$ = ~$2; }
+ | NUMBER
+ ;
+%%
+
diff --git a/usr.bin/m4/pathnames.h b/usr.bin/m4/pathnames.h
new file mode 100644
index 0000000..85ed258
--- /dev/null
+++ b/usr.bin/m4/pathnames.h
@@ -0,0 +1,38 @@
+/* $OpenBSD: pathnames.h,v 1.6 2015/11/03 16:21:47 deraadt Exp $ */
+/* $NetBSD: pathnames.h,v 1.6 1995/09/29 00:27:55 cgd Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the 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.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define _PATH_DIVNAME "/tmp/m4.0XXXXXXXXXX" /* unix diversion files */
diff --git a/usr.bin/m4/stdd.h b/usr.bin/m4/stdd.h
new file mode 100644
index 0000000..58d42b6
--- /dev/null
+++ b/usr.bin/m4/stdd.h
@@ -0,0 +1,55 @@
+/* $OpenBSD: stdd.h,v 1.6 2010/09/07 19:58:09 marco Exp $ */
+/* $NetBSD: stdd.h,v 1.2 1995/09/28 05:37:50 tls Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the 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.
+ *
+ * @(#)stdd.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * standard defines
+ */
+
+#define max(a,b) ((a) > (b)? (a): (b))
+#define min(a,b) ((a) < (b)? (a): (b))
+
+#define iswhite(c) ((c) == ' ' || (c) == '\t')
+
+/*
+ * STREQ is an optimised strcmp(a,b)==0
+ * STREQN is an optimised strncmp(a,b,n)==0; assumes n > 0
+ */
+#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0)
+#define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, n) == 0)
+
+#define YES 1
+#define NO 0
diff --git a/usr.bin/m4/tokenizer.c b/usr.bin/m4/tokenizer.c
new file mode 100644
index 0000000..fa19fc6
--- /dev/null
+++ b/usr.bin/m4/tokenizer.c
@@ -0,0 +1,191 @@
+/* $OpenBSD: tokenizer.l,v 1.10 2017/06/17 01:55:16 bcallah Exp $ */
+/*
+ * Copyright (c) 2004 Marc Espie <espie@cvs.openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "parser.tab.h"
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+extern void m4_warnx(const char *, ...);
+extern int mimic_gnu;
+extern int32_t yylval;
+static const char *yypos;
+
+void
+yy_scan_string(const char *s)
+{
+ yypos = s;
+}
+
+static int32_t
+number(const char *yytext, size_t yylen)
+{
+ long l;
+
+ errno = 0;
+ l = strtol(yytext, NULL, 0);
+ if (((l == LONG_MAX || l == LONG_MIN) && errno == ERANGE) ||
+ l > INT32_MAX || l < INT32_MIN)
+ m4_warnx("numeric overflow in expr: %.*s", (int)yylen, yytext);
+ return l;
+}
+
+static int32_t
+parse_radix(const char *yytext, size_t yylen)
+{
+ long base;
+ char *next;
+ long l;
+ int d;
+
+ l = 0;
+ base = strtol(yytext+2, &next, 0);
+ if (base > 36 || next == NULL) {
+ m4_warnx("error in number %.*s", (int)yylen, yytext);
+ } else {
+ next++;
+ while (*next != 0) {
+ if (*next >= '0' && *next <= '9')
+ d = *next - '0';
+ else if (*next >= 'a' && *next <= 'z')
+ d = *next - 'a' + 10;
+ else {
+ assert(*next >= 'A' && *next <= 'Z');
+ d = *next - 'A' + 10;
+ }
+ if (d >= base) {
+ m4_warnx("error in number %.*s", (int)yylen, yytext);
+ return 0;
+ }
+ l = base * l + d;
+ next++;
+ }
+ }
+ return l;
+}
+
+static int
+isodigit(int c)
+{
+ return c >= '0' && c <= '7';
+}
+
+int yylex(void)
+{
+ const char *start;
+
+next:
+ start = yypos;
+ switch (*yypos) {
+ case ' ':
+ case '\t':
+ case '\n':
+ ++yypos;
+ goto next;
+ case '<':
+ switch (yypos[1]) {
+ case '=':
+ yypos += 2;
+ return LE;
+ case '<':
+ yypos += 2;
+ return LSHIFT;
+ }
+ break;
+ case '>':
+ switch (yypos[1]) {
+ case '=':
+ yypos += 2;
+ return GE;
+ case '>':
+ yypos += 2;
+ return RSHIFT;
+ }
+ break;
+ case '=':
+ if (yypos[1] != '=')
+ break;
+ yypos += 2;
+ return EQ;
+ case '!':
+ if (yypos[1] != '=')
+ break;
+ yypos += 2;
+ return NE;
+ case '&':
+ if (yypos[1] != '&')
+ break;
+ yypos += 2;
+ return LAND;
+ case '|':
+ if (yypos[1] != '|')
+ break;
+ yypos += 2;
+ return LOR;
+ case '*':
+ if (!mimic_gnu || yypos[1] != '*')
+ break;
+ yypos += 2;
+ return EXPONENT;
+ case '0':
+ switch (*++yypos) {
+ case 'x':
+ case 'X':
+ if (!isxdigit(*++yypos))
+ return ERROR;
+ do ++yypos;
+ while (isxdigit(*yypos));
+ break;
+ case 'r':
+ case 'R':
+ if (!mimic_gnu)
+ break;
+ if (!isdigit(*++yypos))
+ return ERROR;
+ do ++yypos;
+ while (isdigit(*yypos));
+ if (*yypos != ':')
+ return ERROR;
+ if (!isalnum(*++yypos))
+ return ERROR;
+ do ++yypos;
+ while (isalnum(*yypos));
+ yylval = parse_radix(start, yypos - start);
+ return NUMBER;
+ default:
+ do ++yypos;
+ while (isodigit(*yypos));
+ break;
+ }
+ yylval = number(start, yypos - start);
+ return NUMBER;
+ case '\0':
+ return '\0';
+ }
+ if (isdigit(*yypos)) {
+ do ++yypos;
+ while (isdigit(*yypos));
+ yylval = number(start, yypos - start);
+ return NUMBER;
+ }
+
+ return *yypos++;
+}
diff --git a/usr.bin/m4/trace.c b/usr.bin/m4/trace.c
new file mode 100644
index 0000000..edf5887
--- /dev/null
+++ b/usr.bin/m4/trace.c
@@ -0,0 +1,196 @@
+/* $OpenBSD: trace.c,v 1.16 2010/09/07 19:58:09 marco Exp $ */
+/*
+ * Copyright (c) 2001 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT 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 OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "mdef.h"
+#include "stdd.h"
+#include "extern.h"
+
+FILE *traceout;
+
+#define TRACE_ARGS 1
+#define TRACE_EXPANSION 2
+#define TRACE_QUOTE 4
+#define TRACE_FILENAME 8
+#define TRACE_LINENO 16
+#define TRACE_CONT 32
+#define TRACE_ID 64
+#define TRACE_NEWFILE 128 /* not implemented yet */
+#define TRACE_INPUT 256 /* not implemented yet */
+
+static unsigned int letter_to_flag(int);
+static void print_header(struct input_file *);
+static int frame_level(void);
+
+
+unsigned int trace_flags = TRACE_QUOTE | TRACE_EXPANSION;
+
+void
+trace_file(const char *name)
+{
+
+ if (traceout && traceout != stderr)
+ fclose(traceout);
+ traceout = fopen(name, "w");
+ if (!traceout)
+ err(1, "can't open %s", name);
+}
+
+static unsigned int
+letter_to_flag(int c)
+{
+ switch(c) {
+ case 'a':
+ return TRACE_ARGS;
+ case 'e':
+ return TRACE_EXPANSION;
+ case 'q':
+ return TRACE_QUOTE;
+ case 'c':
+ return TRACE_CONT;
+ case 'x':
+ return TRACE_ID;
+ case 'f':
+ return TRACE_FILENAME;
+ case 'l':
+ return TRACE_LINENO;
+ case 'p':
+ return TRACE_NEWFILE;
+ case 'i':
+ return TRACE_INPUT;
+ case 't':
+ return TRACE_ALL;
+ case 'V':
+ return ~0;
+ default:
+ return 0;
+ }
+}
+
+void
+set_trace_flags(const char *s)
+{
+ char mode = 0;
+ unsigned int f = 0;
+
+ if (*s == '+' || *s == '-')
+ mode = *s++;
+ while (*s)
+ f |= letter_to_flag(*s++);
+ switch(mode) {
+ case 0:
+ trace_flags = f;
+ break;
+ case '+':
+ trace_flags |= f;
+ break;
+ case '-':
+ trace_flags &= ~f;
+ break;
+ }
+}
+
+static int
+frame_level()
+{
+ int level;
+ int framep;
+
+ for (framep = fp, level = 0; framep != 0;
+ level++,framep = mstack[framep-3].sfra)
+ ;
+ return level;
+}
+
+static void
+print_header(struct input_file *inp)
+{
+ fprintf(traceout, "m4trace:");
+ if (trace_flags & TRACE_FILENAME)
+ fprintf(traceout, "%s:", inp->name);
+ if (trace_flags & TRACE_LINENO)
+ fprintf(traceout, "%lu:", inp->lineno);
+ fprintf(traceout, " -%d- ", frame_level());
+ if (trace_flags & TRACE_ID)
+ fprintf(traceout, "id %lu: ", expansion_id);
+}
+
+size_t
+trace(const char *argv[], int argc, struct input_file *inp)
+{
+ if (!traceout)
+ traceout = stderr;
+ print_header(inp);
+ if (trace_flags & TRACE_CONT) {
+ fprintf(traceout, "%s ...\n", argv[1]);
+ print_header(inp);
+ }
+ fprintf(traceout, "%s", argv[1]);
+ if ((trace_flags & TRACE_ARGS) && argc > 2) {
+ char delim[3];
+ int i;
+
+ delim[0] = LPAREN;
+ delim[1] = EOS;
+ for (i = 2; i < argc; i++) {
+ fprintf(traceout, "%s%s%s%s", delim,
+ (trace_flags & TRACE_QUOTE) ? lquote : "",
+ argv[i],
+ (trace_flags & TRACE_QUOTE) ? rquote : "");
+ delim[0] = COMMA;
+ delim[1] = ' ';
+ delim[2] = EOS;
+ }
+ fprintf(traceout, "%c", RPAREN);
+ }
+ if (trace_flags & TRACE_CONT) {
+ fprintf(traceout, " -> ???\n");
+ print_header(inp);
+ fprintf(traceout, argc > 2 ? "%s(...)" : "%s", argv[1]);
+ }
+ if (trace_flags & TRACE_EXPANSION)
+ return buffer_mark();
+ else {
+ fprintf(traceout, "\n");
+ return SIZE_MAX;
+ }
+}
+
+void
+finish_trace(size_t mark)
+{
+ fprintf(traceout, " -> ");
+ if (trace_flags & TRACE_QUOTE)
+ fprintf(traceout, "%s", lquote);
+ dump_buffer(traceout, mark);
+ if (trace_flags & TRACE_QUOTE)
+ fprintf(traceout, "%s", rquote);
+ fprintf(traceout, "\n");
+}