diff options
author | Cem Keylan <cem@ckyln.com> | 2020-10-16 17:47:01 +0300 |
---|---|---|
committer | Cem Keylan <cem@ckyln.com> | 2020-10-16 17:47:01 +0300 |
commit | 5d69c6a2661bba0a22f3ecfd517e2e9767a38346 (patch) | |
tree | 1f479b2714e127835db7f33a3bfed4c38c52f883 /usr.bin/m4 | |
parent | e2abcdca396661cbe0ae2ddb13d5c2b85682c13a (diff) | |
download | otools-5d69c6a2661bba0a22f3ecfd517e2e9767a38346.tar.gz |
add tools
Diffstat (limited to 'usr.bin/m4')
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(¯os, 10, ¯o_info); +} + +/* + * find name in the hash table + */ +ndptr +lookup(const char *name) +{ + return ohash_find(¯os, ohash_qlookup(¯os, name)); +} + +struct macro_definition * +lookup_macro_definition(const char *name) +{ + ndptr p; + + p = ohash_find(¯os, ohash_qlookup(¯os, 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(¯os, name, &end); + n = ohash_find(¯os, i); + if (n == NULL) { + n = ohash_create_entry(¯o_info, name, &end); + ohash_insert(¯os, 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(¯os, &i); n != NULL; + n = ohash_next(¯os, &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(¯os, &i); p != NULL; + p = ohash_next(¯os, &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(¯os, ohash_qlookupi(¯os, 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"); +} |