Return to BSD News archive
Xref: sserve comp.os.386bsd.bugs:1671 alt.sources:6578
Newsgroups: comp.os.386bsd.bugs,alt.sources
Path: sserve!newshost.anu.edu.au!munnari.oz.au!news.Hawaii.Edu!ames!agate!spool.mu.edu!torn!newshub.ccs.yorku.ca!oz
From: oz@ursa.sis.yorku.ca (Ozan S. Yigit)
Subject: a much improved version of pd/bsd m4.
Message-ID: <OZ.93Oct29000532@ursa.sis.yorku.ca>
Sender: news@newshub.ccs.yorku.ca (USENET News System)
Organization: York U. Student Information Systems Project
Date: Fri, 29 Oct 1993 05:05:32 GMT
Lines: 3295
here is a much improved, 8-bit-clean version of the pd m4 which should
work with all versions of sendmail cfs. this is the base version for a
new release that is in preperation.
I would like to thank Richard A. O'Keefe for all his support.
any comments, bug reports [+/-fixes] and other improvements would be
appreciated.
... oz
---
this upper bound on 24 hours in one | electric: oz@sis.yorku.ca
day gets to me.... -- Udi Manber | or [416] 736 2100 x 33976
----------------------------------------------------------------------
# to unbundle, sh this file
echo expr.c 1>&2
sed 's/.//' >expr.c <<'//GO.SYSIN DD expr.c'
-/* File : expr.c
- Authors: Mike Lutz & Bob Harper
- Editors: Ozan Yigit & Richard A. O'Keefe
- Updated: %G%
- Purpose: arithmetic expression evaluator.
-
- expr() performs a standard recursive descent parse to evaluate any
- expression permitted byf the following grammar:
-
- expr : query EOS
- query : lor
- | lor "?" query ":" query
- lor : land { "||" land } or OR, for Pascal
- land : bor { "&&" bor } or AND, for Pascal
- bor : bxor { "|" bxor }
- bxor : band { "^" band }
- band : eql { "&" eql }
- eql : relat { eqrel relat }
- relat : shift { rel shift }
- shift : primary { shop primary }
- primary : term { addop term }
- term : unary { mulop unary }
- unary : factor
- | unop unary
- factor : constant
- | "(" query ")"
- constant: num
- | "'" CHAR "'" or '"' CHAR '"'
- num : DIGIT full ANSI C syntax
- | DIGIT num
- shop : "<<"
- | ">>"
- eqlrel : "="
- | "=="
- | "!="
- rel : "<" or <>, Pascal not-equal
- | ">"
- | "<=" or =<, for Prolog users.
- | ">="
-
- This expression evaluator was lifted from a public-domain
- C Pre-Processor included with the DECUS C Compiler distribution.
- It has been hacked somewhat to be suitable for m4.
-
- 26-Mar-1993 Changed to work in any of EBCDIC, ASCII, DEC MNCS,
- or ISO 8859/n.
-
- 26-Mar-1993 Changed to use "long int" rather than int, so that
- we get the same 32-bit arithmetic on a PC as on a Sun.
- It isn't fully portable, of course, but then on a 64-
- bit machine we _want_ 64-bit arithmetic...
- Shifting rewritten (using LONG_BIT) to give signed
- shifts even when (long) >> (long) is unsigned.
-
- 26-Mar-1993 I finally got sick of the fact that &&, ||, and ?:
- don't do conditional evaluation. What is the good
- of having eval(0&&(1/0)) crash and dump core? Now
- every function has a doit? argument.
-
- 26-Mar-1993 charcon() didn't actually accept 'abcd', which it
- should have. Fixed it.
-
- 20-Apr-1993 eval(1/0) and eval(1%0) dumped core and crashed.
- This is also true of the System V r 3.2 m4, but
- it isn't good enough for ours! Changed it so that
- x % 0 => x as per Concrete Mathematics
- x / 0 => error and return 0 from expr().
-*/
-
-#define FALSE 0
-#define TRUE 1
-
-#include <stdio.h>
-#include <setjmp.h>
-static jmp_buf expjump; /* Error exit point for expr() */
-
-static unsigned char *nxtchr; /* Parser scan pointer */
-
-#define deblank0 while ((unsigned)(*nxtchr-1) < ' ') nxtchr++
-#define deblank1 while ((unsigned)(*++nxtchr - 1) < ' ')
-#define deblank2 nxtchr++; deblank1
-
-#include "ourlims.h"
-static char digval[1+UCHAR_MAX];
-
-/* This file should work in any C implementation that doesn't have too
- many characters to fit in one table. We use a table to convert
- (unsigned) characters to numeric codes:
- 0 to 9 for '0' to '9'
- 10 to 35 for 'a' to 'z'
- 10 to 35 for 'A' to 'Z'
- 36 for '_'
- Instead of asking whether tolower(c) == 'a' we ask whether
- digval[c] == DIGIT_A, and so on. This essentially duplicates the
- chtype[] table in main.c; we should use just one table.
-*/
-#define DIGIT_A 10
-#define DIGIT_B 11
-#define DIGIT_C 12
-#define DIGIT_D 13
-#define DIGIT_E 14
-#define DIGIT_F 15
-#define DIGIT_G 16
-#define DIGIT_H 17
-#define DIGIT_I 18
-#define DIGIT_J 19
-#define DIGIT_K 20
-#define DIGIT_L 21
-#define DIGIT_M 22
-#define DIGIT_N 23
-#define DIGIT_O 24
-#define DIGIT_P 25
-#define DIGIT_Q 26
-#define DIGIT_R 27
-#define DIGIT_S 28
-#define DIGIT_T 29
-#define DIGIT_U 30
-#define DIGIT_V 31
-#define DIGIT_W 32
-#define DIGIT_X 33
-#define DIGIT_Y 34
-#define DIGIT_Z 35
-
-
-#ifdef __STDC__
-static long int query(int);
-#else
-static long int query();
-#endif
-
-
-/* experr(msg)
- prints an error message, resets environment to expr(), and
- forces expr() to return FALSE.
-*/
-void experr(msg)
- char *msg;
- {
- (void) fprintf(stderr, "m4: %s\n", msg);
- longjmp(expjump, -1); /* Force expr() to return FALSE */
- }
-
-
-/* <numcon> ::= '0x' <hex> | '0X' <hex> | '0' <oct> | <dec>
- For ANSI C, an integer may be followed by u, l, ul, or lu,
- in any mix of cases. We accept and ignore those letters;
- all the numbers are treated as long.
-*/
-static long int numcon(doit)
- int doit;
- {
- register long int v; /* current value */
- register int b; /* base (radix) */
- register int c; /* character or digit value */
-
- if (!doit) {
- do nxtchr++; while (digval[*nxtchr] <= 36);
- deblank0;
- return 0;
- }
-
- v = digval[*nxtchr++]; /* We already know it's a digit */
- if (v != 0) {
- b = 10; /* decimal number */
- } else
- if (digval[*nxtchr] == DIGIT_X) {
- nxtchr++;
- b = 16; /* hexadecimal number */
- } else {
- b = 8; /* octal number */
- }
- do {
- while (digval[c = *nxtchr++] < b) v = v*b + digval[c];
- } while (c == '_');
- while (digval[c] == DIGIT_L || digval[c] == DIGIT_U) c = *nxtchr++;
- nxtchr--; /* unread c */
- if ((unsigned)(c-1) < ' ') { deblank1; }
- return v;
- }
-
-
-/* <charcon> ::= <qt> { <char> } <qt>
- Note: multibyte constants are accepted.
- Note: BEL (\a) and ESC (\e) have the same values in EBCDIC and ASCII.
-*/
-static long int charcon(doit)
- int doit;
- {
- register int i;
- long int value;
- register int c;
- int q;
- int v[sizeof value];
-
- q = *nxtchr++; /* the quote character */
- for (i = 0; ; i++) {
- c = *nxtchr++;
- if (c == q) { /* end of literal, or doubled quote */
- if (*nxtchr != c) break;
- nxtchr++; /* doubled quote stands for one quote */
- }
- if (i == sizeof value) experr("Unterminated character constant");
- if (c == '\\') {
- switch (c = *nxtchr++) {
- case '0': case '1': case '2': case '3':
- case '4': case '5': case '6': case '7':
- c -= '0';
- if ((unsigned)(*nxtchr - '0') < 8)
- c = (c << 3) | (*nxtchr++ - '0');
- if ((unsigned)(*nxtchr - '0') < 8)
- c = (c << 3) | (*nxtchr++ - '0');
- break;
- case 'n': case 'N': c = '\n'; break;
- case 'r': case 'R': c = '\r'; break;
- case 't': case 'T': c = '\t'; break;
- case 'b': case 'B': c = '\b'; break;
- case 'f': case 'F': c = '\f'; break;
- case 'a': case 'A': c = 007; break;
- case 'e': case 'E': c = 033; break;
-#if ' ' == 64
- case 'd': case 'D': c = 045; break; /*EBCDIC DEL */
-#else
- case 'd': case 'D': c = 127; break; /* ASCII DEL */
-#endif
- default : break;
- }
- }
- v[i] = c;
- }
- deblank0;
- if (!doit) return 0;
- for (value = 0; --i >= 0; ) value = (value << CHAR_BIT) | v[i];
- return value;
- }
-
-
-/* <unary> ::= <unop> <unary> | <factor>
- <unop> ::= '!' || '~' | '-'
- <factor> ::= '(' <query> ')' | <'> <char> <'> | <"> <char> <"> | <num>
-*/
-static long int unary(doit)
- int doit;
- {
- long int v;
-
- switch (nxtchr[0]) {
- case 'n': case 'N':
- if (digval[nxtchr[1]] != DIGIT_O
- || digval[nxtchr[2]] != DIGIT_T)
- experr("Bad 'not'");
- nxtchr += 2;
- case '!': deblank1; return !unary(doit);
- case '~': deblank1; return ~unary(doit);
- case '-': deblank1; return -unary(doit);
- case '+': deblank1; return unary(doit);
- case '(': deblank1; v = query(doit);
- if (nxtchr[0] != ')') experr("Bad factor");
- deblank1; return v;
- case '\'':
- case '\"': return charcon(doit);
- case '0': case '1': case '2':
- case '3': case '4': case '5':
- case '6': case '7': case '8':
- case '9': return numcon(doit);
- default : experr("Bad constant");
- }
- return 0; /*NOTREACHED*/
- }
-
-
-/* <term> ::= <unary> { <mulop> <unary> }
- <mulop> ::= '*' | '/' || '%'
-*/
-static long int term(doit)
- int doit;
- {
- register long int vl, vr;
-
- vl = unary(doit);
- for (;;)
- switch (nxtchr[0]) {
- case '*':
- deblank1;
- vr = unary(doit);
- if (doit) vl *= vr;
- break;
- case 'd': case 'D':
- if (digval[nxtchr[1]] != DIGIT_I
- || digval[nxtchr[2]] != DIGIT_V)
- experr("Bad 'div'");
- nxtchr += 2;
- /*FALLTHROUGH*/
- case '/':
- deblank1;
- vr = unary(doit);
- if (doit) {
- if (vr == 0) experr("Division by 0");
- vl /= vr;
- }
- break;
- case 'm': case 'M':
- if (digval[nxtchr[1]] != DIGIT_O
- || digval[nxtchr[2]] != DIGIT_D)
- experr("Bad 'mod'");
- nxtchr += 2;
- /*FALLTHROUGH*/
- case '%':
- deblank1;
- vr = unary(doit);
- if (doit) {
- if (vr != 0) vl %= vr;
- }
- break;
- default:
- return vl;
- }
- /*NOTREACHED*/
- }
-
-/* <primary> ::= <term> { <addop> <term> }
- <addop> ::= '+' | '-'
-*/
-static long int primary(doit)
- int doit;
- {
- register long int vl;
-
- vl = term(doit);
- for (;;)
- if (nxtchr[0] == '+') {
- deblank1;
- if (doit) vl += term(doit); else (void)term(doit);
- } else
- if (nxtchr[0] == '-') {
- deblank1;
- if (doit) vl -= term(doit); else (void)term(doit);
- } else
- return vl;
- /*NOTREACHED*/
- }
-
-
-/* <shift> ::= <primary> { <shop> <primary> }
- <shop> ::= '<<' | '>>'
-*/
-static long int shift(doit)
- int doit;
- {
- register long int vl, vr;
-
- vl = primary(doit);
- for (;;) {
- if (nxtchr[0] == '<' && nxtchr[1] == '<') {
- deblank2;
- vr = primary(doit);
- } else
- if (nxtchr[0] == '>' && nxtchr[1] == '>') {
- deblank2;
- vr = -primary(doit);
- } else {
- return vl;
- }
- /* The following code implements shifts portably */
- /* Shifts are signed shifts, and the shift count */
- /* acts like repeated one-bit shifts, not modulo anything */
- if (doit) {
- if (vr >= LONG_BIT) {
- vl = 0;
- } else
- if (vr <= -LONG_BIT) {
- vl = -(vl < 0);
- } else
- if (vr > 0) {
- vl <<= vr;
- } else
- if (vr < 0) {
- vl = (vl >> -vr) | (-(vl < 0) << (LONG_BIT + vr));
- }
- }
- }
- /*NOTREACHED*/
- }
-
-
-/* <relat> ::= <shift> { <rel> <shift> }
- <rel> ::= '<=' | '>=' | '=<' | '=>' | '<' | '>'
- Here I rely on the fact that '<<' and '>>' are swallowed by <shift>
-*/
-static long int relat(doit)
- int doit;
- {
- register long int vl;
-
- vl = shift(doit);
- for (;;)
- switch (nxtchr[0]) {
- case '=':
- switch (nxtchr[1]) {
- case '<': /* =<, take as <= */
- deblank2;
- vl = vl <= shift(doit);
- break;
- case '>': /* =>, take as >= */
- deblank2;
- vl = vl >= shift(doit);
- break;
- default: /* == or =; OOPS */
- return vl;
- }
- break;
- case '<':
- if (nxtchr[1] == '=') { /* <= */
- deblank2;
- vl = vl <= shift(doit);
- } else
- if (nxtchr[1] == '>') { /* <> (Pascal) */
- deblank2;
- vl = vl != shift(doit);
- } else { /* < */
- deblank1;
- vl = vl < shift(doit);
- }
- break;
- case '>':
- if (nxtchr[1] == '=') { /* >= */
- deblank2;
- vl = vl >= shift(doit);
- } else { /* > */
- deblank1;
- vl = vl > shift(doit);
- }
- break;
- default:
- return vl;
- }
- /*NOTREACHED*/
- }
-
-
-/* <eql> ::= <relat> { <eqrel> <relat> }
- <eqlrel> ::= '!=' | '==' | '='
-*/
-static long int eql(doit)
- int doit;
- {
- register long int vl;
-
- vl = relat(doit);
- for (;;)
- if (nxtchr[0] == '!' && nxtchr[1] == '=') {
- deblank2;
- vl = vl != relat(doit);
- } else
- if (nxtchr[0] == '=' && nxtchr[1] == '=') {
- deblank2;
- vl = vl == relat(doit);
- } else
- if (nxtchr[0] == '=') {
- deblank1;
- vl = vl == relat(doit);
- } else
- return vl;
- /*NOTREACHED*/
- }
-
-
-/* <band> ::= <eql> { '&' <eql> }
-*/
-static long int band(doit)
- int doit;
- {
- register long int vl;
-
- vl = eql(doit);
- while (nxtchr[0] == '&' && nxtchr[1] != '&') {
- deblank1;
- if (doit) vl &= eql(doit); else (void)eql(doit);
- }
- return vl;
- }
-
-
-/* <bxor> ::= <band> { '^' <band> }
-*/
-static long int bxor(doit)
- int doit;
- {
- register long int vl;
-
- vl = band(doit);
- while (nxtchr[0] == '^') {
- deblank1;
- if (doit) vl ^= band(doit); else (void)band(doit);
- }
- return vl;
- }
-
-
-/* <bor> ::= <bxor> { '|' <bxor> }
-*/
-static long int bor(doit)
- int doit;
- {
- register long int vl;
-
- vl = bxor(doit);
- while (nxtchr[0] == '|' && nxtchr[1] != '|') {
- deblank1;
- if (doit) vl |= bxor(doit); else (void)bxor(doit);
- }
- return vl;
- }
-
-
-/* <land> ::= <bor> { '&&' <bor> }
-*/
-static long int land(doit)
- int doit;
- {
- register long int vl;
-
- vl = bor(doit);
- for (;;) {
- if (nxtchr[0] == '&') {
- if (nxtchr[1] != '&') break;
- deblank2;
- } else
- if (digval[nxtchr[0]] == DIGIT_A) {
- if (digval[nxtchr[1]] != DIGIT_N) break;
- if (digval[nxtchr[2]] != DIGIT_D) break;
- nxtchr += 2; deblank1;
- } else {
- /* neither && nor and */
- break;
- }
- vl = bor(doit && vl) != 0;
- }
- return vl;
- }
-
-
-/* <lor> ::= <land> { '||' <land> }
-*/
-static long int lor(doit)
- int doit;
- {
- register long int vl;
-
- vl = land(doit);
- for (;;) {
- if (nxtchr[0] == '|') {
- if (nxtchr[1] != '|') break;
- } else
- if (digval[nxtchr[0]] == DIGIT_O) {
- if (digval[nxtchr[1]] != DIGIT_R) break;
- } else {
- /* neither || nor or */
- break;
- }
- deblank2;
- vl = land(doit && !vl) != 0;
- }
- return vl;
- }
-
-
-/* <query> ::= <lor> [ '?' <query> ':' <query> ]
-*/
-static long int query(doit)
- int doit;
- {
- register long int bool, true_val, false_val;
-
- bool = lor(doit);
- if (*nxtchr != '?') return bool;
- deblank1;
- true_val = query(doit && bool);
- if (*nxtchr != ':') experr("Bad query");
- deblank1;
- false_val = query(doit && !bool);
- return bool ? true_val : false_val;
- }
-
-
-static void initialise_digval()
- {
- register unsigned char *s;
- register int c;
-
- for (c = 0; c <= UCHAR_MAX; c++) digval[c] = 99;
- for (c = 0, s = (unsigned char *)"0123456789";
- /*while*/ *s;
- /*doing*/ digval[*s++] = c++) /* skip */;
- for (c = 10, s = (unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- /*while*/ *s;
- /*doing*/ digval[*s++] = c++) /* skip */;
- for (c = 10, s = (unsigned char *)"abcdefghijklmnopqrstuvwxyz";
- /*while*/ *s;
- /*doing*/ digval[*s++] = c++) /* skip */;
- digval['_'] = 36;
- }
-
-
-long int expr(expbuf)
- char *expbuf;
- {
- register int rval;
-
- if (digval['1'] == 0) initialise_digval();
- nxtchr = (unsigned char *)expbuf;
- deblank0;
- if (setjmp(expjump) != 0) return FALSE;
- rval = query(TRUE);
- if (*nxtchr) experr("Ill-formed expression");
- return rval;
- }
-
//GO.SYSIN DD expr.c
echo int2str.c 1>&2
sed 's/.//' >int2str.c <<'//GO.SYSIN DD int2str.c'
-/* File : int2str.c
- Author : Richard A. O'Keefe
- Updated: 6 February 1993
- Defines: int2str()
-
- int2str(dst, radix, val)
- converts the (long) integer "val" to character form and moves it to
- the destination string "dst" followed by a terminating NUL. The
- result is normally a pointer to this NUL character, but if the radix
- is dud the result will be NullS and nothing will be changed.
-
- If radix is -2..-36, val is taken to be SIGNED.
- If radix is 2.. 36, val is taken to be UNSIGNED.
- That is, val is signed if and only if radix is. You will normally
- use radix -10 only through itoa and ltoa, for radix 2, 8, or 16
- unsigned is what you generally want.
-*/
-
-static char dig_vec[] =
- "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-
-
-char *int2str(dst, radix, val)
- register char *dst;
- register int radix;
- register long val;
- {
- char buffer[65]; /* Ready for 64-bit machines */
- register char *p;
-
- if (radix < 2 || radix > 36) { /* Not 2..36 */
- if (radix > -2 || radix < -36) return (char *)0;
- if (val < 0) {
- *dst++ = '-';
- val = -val;
- }
- radix = -radix;
- }
- /* The slightly contorted code which follows is due to the
- fact that few machines directly support unsigned long / and %.
- Certainly the VAX C compiler generates a subroutine call. In
- the interests of efficiency (hollow laugh) I let this happen
- for the first digit only; after that "val" will be in range so
- that signed integer division will do. Sorry 'bout that.
- CHECK THE CODE PRODUCED BY YOUR C COMPILER. The first % and /
- should be unsigned, the second % and / signed, but C compilers
- tend to be extraordinarily sensitive to minor details of style.
- This works on a VAX, that's all I claim for it.
- */
- p = &buffer[sizeof buffer];
- *--p = '\0';
- *--p = dig_vec[(unsigned long)val%(unsigned long)radix];
- val = (unsigned long)val/(unsigned long)radix;
- while (val != 0) *--p = dig_vec[val%radix], val /= radix;
- while (*dst++ = *p++) ;
- return dst-1;
- }
-
//GO.SYSIN DD int2str.c
echo look.c 1>&2
sed 's/.//' >look.c <<'//GO.SYSIN DD look.c'
-/* File : look.c
- Author : Ozan Yigit
- Updated: 4 May 1992
- Purpose: Hash table for M4
-*/
-
-#include "mdef.h"
-#include "extr.h"
-
-ndptr hashtab[HASHSIZE];
-
-
-/*
- * hash - get a hash value for string s
- */
-int
-hash(name)
-char *name;
-{
- register unsigned long h = 0;
-
- while (*name)
- h = (h << 5) + h + *name++;
-
- return h % HASHSIZE;
-}
-
-/*
- * lookup(name) - find name in the hash table
- */
-ndptr lookup(name)
- char *name;
- {
- register ndptr p;
-
- for (p = hashtab[hash(name)]; p != nil; p = p->nxtptr)
- if (strcmp(name, p->name) == 0)
- break;
- return p;
- }
-
-/*
- * addent(name) - hash and create an entry in the hash table.
- * The new entry is added at the front of a hash bucket.
- * BEWARE: the type and defn fields are UNDEFINED.
- */
-ndptr addent(name)
- char *name;
- {
- register ndptr p, *h;
-
- p = (ndptr)malloc(sizeof *p);
- if (p == NULL) error("m4: no more memory.");
- h = &hashtab[hash(name)];
- p->name = strsave(name);
- p->defn = null;
- p->nxtptr = *h;
- *h = p;
- return p;
- }
-
-
-/*
- * addkywd(name, type) - stores a keyword in the hash table.
- */
-void addkywd(name, type)
- char *name;
- int type;
- {
- register ndptr p = addent(name);
- p->type = type | STATIC;
- }
-
-
-/*
- * remhash(name, all)
- * remove one entry (all==0) or all entries (all!=0) for a given name
- * from the hash table. All hash table entries must have been obtained
- * from malloc(), so it is safe to free the records themselves.
- * However, the ->name and ->defn fields might point to storage which
- * was obtained from strsave() -- in which case they may be freed -- or
- * to static storage -- in which case they must not be freed. If the
- * STATIC bit is set, the fields are not to be freed.
- */
-void remhash(name, all)
- char *name;
- int all;
- {
- register ndptr p, *h;
- /* h always points to the pointer to p */
-
- h = &hashtab[hash(name)];
- while ((p = *h) != nil) {
- if (strcmp(p->name, name) == 0) {
- *h = p->nxtptr; /* delink this record */
- if (!(p->type & STATIC)) { /* free the name and defn */
- free(p->name); /* if they came from strsave */
- if (p->defn != null) free(p->defn);
- } /* otherwise leave them */
- free(p); /* free the record itself */
- if (!all) return; /* first occurrence has gone */
- } else {
- h = &(p->nxtptr);
- }
- }
- }
-
//GO.SYSIN DD look.c
echo main.c 1>&2
sed 's/.//' >main.c <<'//GO.SYSIN DD main.c'
-/* File : main.c
- Author : Ozan Yigit
- Updated: 4 May 1992
- Defines: M4 macro processor.
-*/
-
-#include "mdef.h"
-#include "extr.h"
-#include "ourlims.h"
-
-char chtype[1 - EOF + UCHAR_MAX];
-
-#define is_sym1(c) (chtype[(c)-EOF] > 10)
-#define is_sym2(c) (chtype[(c)-EOF] > 0)
-#define is_blnk(c) ((unsigned)((c)-1) < ' ')
-
-/*
- * m4 - macro processor
- *
- * 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.
- *
- * 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
- *
- * Modification History:
- *
- * Mar 26 1992 RAOK 1. Eliminated magic numbers 8, 255, 256 in favour
- * of the standard limits CHAR_BIT, UCHAR_MAX, which
- * are in the new header ourlims.h. This is part of
- * the "8-bit-clean M4" project. To the best of my
- * belief, all of the code should work in EBCDIC,
- * ASCII, DEC MNCS, ISO 8859/n, or the Mac character
- * set, as long as chars are unsigned. There are
- * still some places where signed bytes can cause
- * trouble.
- *
- * 2. Changed expr() to use long int rather than int.
- * This is so that we'd get 32-bit arithmetic on a Sun,
- * Encore, PC, Mac &c. As part of this, the code for
- * shifts has been elaborated to yield signed shifts
- * on all machines. The charcon() function didn't work
- * with multi-character literals, although it was meant
- * to. Now it does. pbrad() has been changed so that
- * eval('abcd',0) => abcd, not dcba, which was useless.
- *
- * 3. I finally got sick of the fact that &&, ||, and
- * ?: always evaluate all their arguments. This is
- * consistent with UNIX System V Release 3, but I for
- * one don't see anything to gain by having eval(0&&1/0)
- * crash when it would simply yield 0 in C. Now these
- * operators are more consistent with the C preprocessor.
- *
- * Nov 13 1992 RAOK Added the quoter facility. The purpose of this is
- * to make it easier to generate data for a variety of
- * programming languages, including sh, awk, Lisp, C.
- * There are two holes in the implementation: dumpdef
- * prints junk and undefine doesn't release everything.
- * This was mainly intended as a prototype to show that
- * it could be done.
- *
- * Jun 16 1992 RAOK Added vquote and gave changequote a 3rd argument.
- * The idea of this is to make it possible to quote
- * ANY string, including one with unbalanced ` or '.
- * I also made eval(c,0) convert decimal->ASCII, so
- * that eval(39,0) yields ' and eval(96,0) yields `.
- *
- * Apr 28 1992 RAOK Used gcc to find and fix ANSI clashes, so that
- * PD M4 could be ported to MS-DOS (Turbo C 3).
- * Main known remaining problem: use of mktemp().
- * Also, command line handling needs to be worked out.
- *
- * Mar 26 1992 RAOK PD M4 now accepts file names on the command line
- * just like UNIX M4. Warning: macro calls must NOT
- * cross file boundaries. UNIX M4 doesn't mind;
- * (m4 a b c) and (cat a b c | m4) are just the same
- * except for error messages. PD M4 will report an
- * unexpected EOF if a file ends while a macro call or
- * string is still being parsed. When there is one
- * file name argument, or none, you can't tell the
- * difference, and that's all I need.
- *
- * May 15 1991 RAOK DIVNAM was a string constant, but was changed!
- * Fixed that and a couple of other things to make
- * GCC happy. (Also made "foo$bar" get through.)
- *
- * Apr 17 1991 RAOK There was a major mistake. If you did
- * define(foo, `1 include(bar) 2') where
- * file bar held "-bar-" you would naturally
- * expect "1 -bar- 2" as the output, but you
- * got "1 2-bar-". That is, include file
- * processing was postponed until all macros
- * had been expanded. The macro gpbc() was
- * at fault. I added bb, bbstack[], and the
- * code in main.c and serv.c that maintains
- * them, in order to work around this bug.
- *
- * Apr 12 1991 RAOK inspect() didn't handle overflow well.
- * Added the automatically maintained macro
- * __FILE__, just as in C. To suppress it,
- * define NO__FILE. At some point, $# had
- * been made to return a value that was off
- * by one; it now agrees with SysV M4.
- *
- * Aug 13 1990 RAOK The System V expr() has three arguments:
- * expression [, radix:10 [, mindigits: 1]]
- * Brought in my int2str() and wrote pbrad()
- * to make this work here. With the wrong #
- * of args, acts like System V.
- *
- * Aug 11 1990 RAOK Told expr.c about the Pascal operators
- * not, div, mod, and, or
- * so that Pascal constant expressions could
- * be evaluated. (It still doesn't handle
- * floats.) Fixed a mistake in 'character's.
- *
- * Apr 23 1988 RAOK Sped it up, mainly by making putback() and
- * chrsave() into macros.
- * Finished the -o option (was half done).
- * Added the System V -e (interactive) option.
- *
- * Jan 28 1986 Oz Break the whole thing into little
- * pieces, for easier (?) maintenance.
- *
- * Dec 12 1985 Oz Optimize the code, try to squeeze
- * few microseconds out.. [didn't try very hard]
- *
- * Dec 05 1985 Oz Add getopt interface, define (-D),
- * undefine (-U) options.
- *
- * Oct 21 1985 Oz Clean up various bugs, add comment handling.
- *
- * June 7 1985 Oz Add some of SysV m4 stuff (m4wrap, pushdef,
- * popdef, decr, shift etc.).
- *
- * June 5 1985 Oz Initial cut.
- *
- * 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 nonsense 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
- * . |
- * +-------+ |
- * | <----------+
- * +-------+
- *
- * [2] We have three types of null values:
- *
- * nil - nodeblock pointer type 0
- * null - null string ("")
- * NULL - Stdio-defined NULL
- *
- */
-
-char buf[BUFSIZE]; /* push-back buffer */
-char *bp = buf; /* first available character */
-char *bb = buf; /* buffer beginning */
-char *endpbb = buf+BUFSIZE; /* end of push-back buffer */
-stae mstack[STACKMAX+1]; /* stack of m4 machine */
-char strspace[STRSPMAX+1]; /* string space for evaluation */
-char *ep = strspace; /* first free char in strspace */
-char *endest= strspace+STRSPMAX;/* end of string space */
-int sp; /* current m4 stack pointer */
-int fp; /* m4 call frame pointer */
-char *bbstack[MAXINP]; /* stack where bb is saved */
-FILE *infile[MAXINP]; /* input file stack (0=stdin) */
-FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/
-FILE *active; /* active output file pointer */
-int ilevel = 0; /* input file stack pointer */
-int oindex = 0; /* diversion index.. */
-char *null = ""; /* as it says.. just a null.. */
-char *m4wraps = ""; /* m4wrap string default.. */
-char lquote = LQUOTE; /* left quote character (`) */
-char rquote = RQUOTE; /* right quote character (') */
-char vquote = VQUOTE; /* verbatim quote character ^V */
-char scommt = SCOMMT; /* start character for comment */
-char ecommt = ECOMMT; /* end character for comment */
-int strip = 0; /* throw away comments? */
-
-/* Definitions of diversion files. The last 6 characters MUST be
- "XXXXXX" -- that is a requirement of mktemp(). The character
- '0' is to be replaced by the diversion number; we assume here
- that it is just before the Xs. If not, you will have to alter
- the definition of UNIQUE.
-*/
-
-#if unix
-static char DIVNAM[] = "/tmp/m40XXXXXX";
-#else
-#if vms
-static char DIVNAM[] = "sys$login:m40XXXXXX";
-#else
-static char DIVNAM[] = "M40XXXXXX"; /* was \M4, should it be \\M4? */
-#endif
-#endif
-int UNIQUE = sizeof DIVNAM - 7; /* where to change m4temp. */
-char *m4temp; /* filename for diversions */
-extern char *mktemp();
-
-
-void cantread(s)
- char *s;
- {
- fprintf(stderr, "m4: %s: ", s);
- error("cannot open for input.");
- }
-
-
-/* initkwds()
- initialises the hash table to contain all the m4 built-in functions.
- The original version breached module boundaries, but there did not
- seem to be any benefit in that.
-*/
-static void initkwds()
- {
- register int i;
- static struct { char *name; int type; } keyword[] =
- {
- "include", INCLTYPE,
- "sinclude", SINCTYPE,
- "define", DEFITYPE,
- "defn", DEFNTYPE,
- "divert", DIVRTYPE,
- "expr", EXPRTYPE,
- "eval", EXPRTYPE,
- "substr", SUBSTYPE,
- "ifelse", IFELTYPE,
- "ifdef", IFDFTYPE,
- "len", LENGTYPE,
- "incr", INCRTYPE,
- "decr", DECRTYPE,
- "dnl", DNLNTYPE,
- "changequote", CHNQTYPE,
- "changecom", CHNCTYPE,
- "index", INDXTYPE,
-#ifdef EXTENDED
- "paste", PASTTYPE,
- "spaste", SPASTYPE,
- "m4trim", TRIMTYPE,
- "defquote", DEFQTYPE,
-#endif
- "popdef", POPDTYPE,
- "pushdef", PUSDTYPE,
- "dumpdef", DUMPTYPE,
- "shift", SHIFTYPE,
- "translit", TRNLTYPE,
- "undefine", UNDFTYPE,
- "undivert", UNDVTYPE,
- "divnum", DIVNTYPE,
- "maketemp", MKTMTYPE,
- "errprint", ERRPTYPE,
- "m4wrap", M4WRTYPE,
- "m4exit", EXITTYPE,
-#if unix || vms
- "syscmd", SYSCTYPE,
- "sysval", SYSVTYPE,
-#endif
-#if unix
- "unix", MACRTYPE,
-#else
-#if vms
- "vms", MACRTYPE,
-#endif
-#endif
- (char*)0, 0
- };
-
- for (i = 0; keyword[i].type != 0; i++)
- addkywd(keyword[i].name, keyword[i].type);
- }
-
-
-/* inspect(Name)
- Build an input token.., considering only those which start with
- [A-Za-z_]. This is fused with lookup() to speed things up.
- name must point to an array of at least MAXTOK characters.
-*/
-ndptr inspect(name)
- char *name;
- {
- register char *tp = name;
- register char *etp = name+(MAXTOK-1);
- register int c;
- register unsigned long h = 0;
- register ndptr p;
-
- while (is_sym2(c = gpbc())) {
- if (tp == etp) error("m4: token too long");
- *tp++ = c, h = (h << 5) + h + c;
- }
- putback(c);
- *tp = EOS;
- for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
- if (strcmp(name, p->name) == 0)
- return p;
- return nil;
- }
-
-
-/*
- * macro - the work horse..
- *
- */
-void macro()
- {
- char token[MAXTOK];
- register int t;
- register FILE *op = active;
- static char ovmsg[] = "m4: internal stack overflow";
-
- for (;;) {
- t = gpbc();
- if (is_sym1(t)) {
- register char *s;
- register ndptr p;
-
- putback(t);
- if ((p = inspect(s = token)) == nil) {
- if (sp < 0) {
- while (t = *s++) putc(t, op);
- } else {
- while (t = *s++) chrsave(t);
- }
- } else {
- /* real thing.. First build a call frame */
- if (sp >= STACKMAX-6) error(ovmsg);
- mstack[1+sp].sfra = fp; /* previous call frm */
- mstack[2+sp].sfra = p->type; /* type of the call */
- mstack[3+sp].sfra = 0; /* parenthesis level */
- fp = sp+3; /* new frame pointer */
- /* now push the string arguments */
- mstack[4+sp].sstr = p->defn; /* defn string */
- mstack[5+sp].sstr = p->name; /* macro name */
- mstack[6+sp].sstr = ep; /* start next.. */
- sp += 6;
-
- t = gpbc();
- putback(t);
- if (t != LPAREN) { putback(RPAREN); putback(LPAREN); }
- }
- } else
- if (t == EOF) {
- if (sp >= 0) error("m4: unexpected end of input");
- if (--ilevel < 0) break; /* all done thanks */
-#ifndef NO__FILE
- remhash("__FILE__", TOP);
-#endif
- bb = bbstack[ilevel+1];
- (void) fclose(infile[ilevel+1]);
- } else
- /* non-alpha single-char token seen..
- [the order of else if .. stmts is important.]
- */
- if (t == lquote) { /* strip quotes */
- register int nlpar;
-
- for (nlpar = 1; ; ) {
- t = gpbc();
- if (t == rquote) {
- if (--nlpar == 0) break;
- } else
- if (t == lquote) {
- nlpar++;
- } else {
- if (t == vquote) t = gpbc();
- if (t == EOF) {
- error("m4: missing right quote");
- }
- }
- if (sp < 0) {
- putc(t, op);
- } else {
- chrsave(t);
- }
- }
- } else
- if (sp < 0) { /* not in a macro at all */
- if (t != scommt) { /* not a comment, so */
- putc(t, op); /* copy it to output */
- } else
- if (strip) { /* discard a comment */
- do {
- t = gpbc();
- } while (t != ecommt && t != EOF);
- } else { /* copy comment to output */
- do {
- putc(t, op);
- t = gpbc();
- } while (t != ecommt && t != EOF);
- putc(t, op);
- /* A note on comment handling: this is NOT robust.
- | We should do something safe with comments that
- | are missing their ecommt termination.
- */
- }
- } else
- switch (t) {
- /* There is a peculiar detail to notice here.
- Layout is _always_ discarded after left parentheses,
- but it is only discarded after commas if they separate
- arguments. For example,
- define(foo,`|$1|$2|')
- foo( a, b) => |a|b|
- foo(( a ), ( b )) => |(a )|(b )|
- foo((a, x), (b, y)) => |(a, x)|(b, y)|
- I find this counter-intuitive, and would expect the code
- for LPAREN to read something like this:
-
- if (PARLEV == 0) {
- (* top level left parenthesis: skip layout *)
- do t = gpbc(); while (is_blnk(t));
- putback(t);
- } else {
- (* left parenthesis inside an argument *)
- chrsave(t);
- }
- PARLEV++;
-
- However, it turned out that Oz wrote the actual code
- very carefully to mimic the behaviour of "real" m4;
- UNIX m4 really does skip layout after all left parens
- but only some commas in just this fashion. Sigh.
- */
- case LPAREN:
- if (PARLEV > 0) chrsave(t);
- do t = gpbc(); while (is_blnk(t)); /* skip layout */
- putback(t);
- PARLEV++;
- break;
-
- case COMMA:
- if (PARLEV == 1) {
- chrsave(EOS); /* new argument */
- if (sp >= STACKMAX) error(ovmsg);
- do t = gpbc(); while (is_blnk(t)); /* skip layout */
- putback(t);
- mstack[++sp].sstr = ep;
- } else {
- chrsave(t);
- }
- break;
-
- case RPAREN:
- if (--PARLEV > 0) {
- chrsave(t);
- } else {
- char **argv = (char **)(mstack+fp+1);
- int argc = sp-fp;
-#if unix | vms
- static int sysval;
-#endif
-
- chrsave(EOS); /* last argument */
- if (sp >= STACKMAX) error(ovmsg);
-#ifdef DEBUG
- fprintf(stderr, "argc = %d\n", argc);
- for (t = 0; t < argc; t++)
- fprintf(stderr, "argv[%d] = %s\n", t, argv[t]);
-#endif
- /* If argc == 3 and argv[2] is null, then we
- have a call like `macro_or_builtin()'. We
- adjust argc to avoid further checking..
- */
- if (argc == 3 && !argv[2][0]) argc--;
-
- switch (CALTYP & ~STATIC) {
- case MACRTYPE:
- expand(argv, argc);
- break;
-
- case DEFITYPE: /* define(..) */
- for (; argc > 2; argc -= 2, argv += 2)
- dodefine(argv[2], argc > 3 ? argv[3] : null);
- break;
-
- case PUSDTYPE: /* pushdef(..) */
- for (; argc > 2; argc -= 2, argv += 2)
- dopushdef(argv[2], argc > 3 ? argv[3] : null);
- break;
-
- case DUMPTYPE:
- dodump(argv, argc);
- break;
-
- case EXPRTYPE: /* eval(Expr) */
- { /* evaluate arithmetic expression */
- /* eval([val: 0[, radix:10 [,min: 1]]]) */
- /* excess arguments are ignored */
- /* eval() with no arguments returns 0 */
- /* this is based on V.3 behaviour */
- int min_digits = 1;
- int radix = 10;
- long int value = 0;
-
- switch (argc) {
- default:
- /* ignore excess arguments */
- case 5:
- min_digits = expr(argv[4]);
- case 4:
- radix = expr(argv[3]);
- case 3:
- value = expr(argv[2]);
- case 2:
- break;
- }
- pbrad(value, radix, min_digits);
- }
- break;
-
- case IFELTYPE: /* ifelse(X,Y,IFX=Y,Else) */
- doifelse(argv, argc);
- break;
-
- case IFDFTYPE: /* ifdef(Mac,IfDef[,IfNotDef]) */
- /* select one of two alternatives based on the existence */
- /* of another definition */
- if (argc > 3) {
- if (lookup(argv[2]) != nil) {
- pbstr(argv[3]);
- } else
- if (argc > 4) {
- pbstr(argv[4]);
- }
- }
- break;
-
- case LENGTYPE: /* len(Arg) */
- /* find the length of the argument */
- pbnum(argc > 2 ? strlen(argv[2]) : 0);
- break;
-
- case INCRTYPE: /* incr(Expr) */
- /* increment the value of the argument */
- if (argc > 2) pbnum(expr(argv[2]) + 1);
- break;
-
- case DECRTYPE: /* decr(Expr) */
- /* decrement the value of the argument */
- if (argc > 2) pbnum(expr(argv[2]) - 1);
- break;
-
-#if unix || vms
- case SYSCTYPE: /* syscmd(Command) */
- /* execute system command */
- /* Make sure m4 output is NOT interrupted */
- fflush(stdout);
- fflush(stderr);
-
- if (argc > 2) sysval = system(argv[2]);
- break;
-
- case SYSVTYPE: /* sysval() */
- /* return value of the last system call. */
- pbnum(sysval);
- break;
-#endif
-
- case INCLTYPE: /* include(File) */
- for (t = 2; t < argc; t++)
- if (!doincl(argv[t])) cantread(argv[t]);
- break;
-
- case SINCTYPE: /* sinclude(File) */
- for (t = 2; t < argc; t++)
- (void) doincl(argv[t]);
- break;
-
-#ifdef EXTENDED
- case PASTTYPE: /* paste(File) */
- for (t = 2; t < argc; t++)
- if (!dopaste(argv[t])) cantread(argv[t]);
- break;
-
- case SPASTYPE: /* spaste(File) */
- for (t = 2; t < argc; t++)
- (void) dopaste(argv[t]);
- break;
-
- case TRIMTYPE: /* m4trim(Source,..) */
- if (argc > 2) m4trim(argv, argc);
- break;
-
- case DEFQTYPE: /* defquote(Mac,...) */
- dodefqt(argv, argc);
- break;
-
- case QUTRTYPE: /* <quote>(text...) */
- doqutr(argv, argc);
- break;
-#endif
-
- case CHNQTYPE: /* changequote([Left[,Right]]) */
- dochq(argv, argc);
- break;
-
- case CHNCTYPE: /* changecom([Left[,Right]]) */
- dochc(argv, argc);
- break;
-
- case SUBSTYPE: /* substr(Source[,Offset[,Length]]) */
- /* select substring */
- if (argc > 3) dosub(argv, argc);
- break;
-
- case SHIFTYPE: /* shift(~args~) */
- /* push back all arguments except the first one */
- /* (i.e. skip argv[2]) */
- if (argc > 3) {
- for (t = argc-1; t > 3; t--) {
- pbqtd(argv[t]);
- putback(',');
- }
- pbqtd(argv[3]);
- }
- break;
-
- case DIVRTYPE: /* divert(N) */
- if (argc > 2 && (t = expr(argv[2])) != 0) {
- dodiv(t);
- } else {
- active = stdout;
- oindex = 0;
- }
- op = active;
- break;
-
- case UNDVTYPE: /* undivert(N...) */
- doundiv(argv, argc);
- op = active;
- break;
-
- case DIVNTYPE: /* divnum() */
- /* return the number of current output diversion */
- pbnum(oindex);
- break;
-
- case UNDFTYPE: /* undefine(..) */
- /* undefine a previously defined macro(s) or m4 keyword(s). */
- for (t = 2; t < argc; t++) remhash(argv[t], ALL);
- break;
-
- case POPDTYPE: /* popdef(Mac...) */
- /* remove the topmost definitions of macro(s) or m4 keyword(s). */
- for (t = 2; t < argc; t++) remhash(argv[t], TOP);
- break;
-
- case MKTMTYPE: /* maketemp(Pattern) */
- /* create a temporary file */
- if (argc > 2) pbstr(mktemp(argv[2]));
- break;
-
- case TRNLTYPE: /* translit(Source,Dom,Rng) */
- /* replace all characters in the source string that */
- /* appears in the "from" string with the corresponding */
- /* characters in the "to" string. */
-
- if (argc > 3) {
- char temp[MAXTOK];
-
- if (argc > 4)
- map(temp, argv[2], argv[3], argv[4]);
- else
- map(temp, argv[2], argv[3], null);
- pbstr(temp);
- } else if (argc > 2)
- pbstr(argv[2]);
- break;
-
- case INDXTYPE: /* index(Source,Target) */
- /* find the index of the second argument string in */
- /* the first argument string. -1 if not present. */
- pbnum(argc > 3 ? indx(argv[2], argv[3]) : -1);
- break;
-
- case ERRPTYPE: /* errprint(W,...,W) */
- /* print the arguments to stderr file */
- for (t = 2; t < argc; t++) fprintf(stderr, "%s ", argv[t]);
- fprintf(stderr, "\n");
- break;
-
- case DNLNTYPE: /* dnl() */
- /* eat upto and including newline */
- while ((t = gpbc()) != '\n' && t != EOF) ;
- break;
-
- case M4WRTYPE: /* m4wrap(AtExit) */
- /* set up for wrap-up/wind-down activity. */
- /* NB: if there are several calls to m4wrap */
- /* only the last is effective; strange, but */
- /* that's what System V does. */
- m4wraps = argc > 2 ? strsave(argv[2]) : null;
- break;
-
- case EXITTYPE: /* m4exit(Expr) */
- /* immediate exit from m4. */
- killdiv(); /* mustn't forget that one! */
- exit(argc > 2 ? expr(argv[2]) : 0);
- break;
-
- case DEFNTYPE: /* defn(Mac) */
- for (t = 2; t < argc; t++)
- dodefn(argv[t]);
- break;
-
- default:
- error("m4: major botch in eval.");
- break;
- }
-
- ep = PREVEP; /* flush strspace */
- sp = PREVSP; /* previous sp.. */
- fp = PREVFP; /* rewind stack... */
- }
- break;
-
- default:
- chrsave(t); /* stack the char */
- break;
- }
- }
- }
-
-
-int main(argc, argv)
- int argc;
- char **argv;
- {
- register int c;
- register int n;
- char *p;
-
-#ifdef SIGINT
- if (signal(SIGINT, SIG_IGN) != SIG_IGN)
- signal(SIGINT, onintr);
-#endif
-
- /* Initialise the chtype[] table.
- '0' .. '9' -> 1..10
- 'A' .. 'Z' -> 11..37
- 'a' .. 'z' -> 11..37
- '_' -> 38
- all other characters -> 0
- */
- for (c = EOF; c <= UCHAR_MAX; c++) chtype[c - EOF] = 0;
- for (c = 1, p = "0123456789"; *p; p++, c++)
- chtype[*(unsigned char *)p - EOF] = c;
- for (c = 11, p = "abcdefghijklmnopqrstuvwxyz"; *p; p++, c++)
- chtype[*(unsigned char *)p - EOF] = c;
- for (c = 11, p = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; *p; p++, c++)
- chtype[*(unsigned char *)p - EOF] = c;
- chtype['_' - EOF] = 38;
-
-#ifdef NONZEROPAGES
- /* If your system does not initialise global variables to */
- /* 0 bits, do it here. */
- for (n = 0; n < HASHSIZE; n++) hashtab[n] = nil;
- for (n = 0; n < MAXOUT; n++) outfile[n] = NULL;
-#endif
- initkwds();
-
- while ((c = getopt(argc, argv, "cetD:U:o:B:H:S:T:")) != EOF) {
- switch (c) {
-#if 0
- case 's': /* enable #line sync in output */
- fprintf(stderr, "m4: this version does not support -s\n");
- exit(2);
-#endif
-
- case 'c': /* strip comments */
- strip ^= 1;
- break;
-
- case 'e': /* interactive */
- (void) signal(SIGINT, SIG_IGN);
- setbuf(stdout, NULL);
- break;
-
- case 'D': /* define something..*/
- for (p = optarg; *p && *p != '='; p++) ;
- if (*p) *p++ = EOS;
- dodefine(optarg, p);
- break;
-
- case 'U': /* undefine... */
- remhash(optarg, TOP);
- break;
-
- case 'B': case 'H': /* System V compatibility */
- case 'S': case 'T': /* ignore them */
- break;
-
- case 'o': /* specific output */
- if (!freopen(optarg, "w", stdout)) {
- perror(optarg);
- exit(1);
- }
- break;
-
- case '?':
- default:
- usage();
- }
- }
-
- active = stdout; /* default active output */
- m4temp = mktemp(DIVNAM); /* filename for diversions */
-
- sp = -1; /* stack pointer initialized */
- fp = 0; /* frame pointer initialized */
-
- if (optind == argc) { /* no more args; read stdin */
- infile[0] = stdin; /* default input (naturally) */
-#ifndef NO__FILE
- dodefine("__FILE__", "-"); /* Helas */
-#endif
- macro(); /* process that file */
- } else /* file names in commandline */
- for (; optind < argc; optind++) {
- char *name = argv[optind]; /* next file name */
- infile[0] = fopen(name, "r");
- if (!infile[0]) cantread(name);
-#ifndef NO__FILE
- dodefine("__FILE__", name);
-#endif
- macro();
- fclose(infile[0]);
- }
-
- if (*m4wraps) { /* anything for rundown ?? */
- ilevel = 0; /* in case m4wrap includes.. */
- putback(EOF); /* eof is a must !! */
- pbstr(m4wraps); /* user-defined wrapup act */
- macro(); /* last will and testament */
- } else { /* default wrap-up: undivert */
- for (n = 1; n < MAXOUT; n++)
- if (outfile[n] != NULL) getdiv(n);
- }
-
- if (outfile[0] != NULL) { /* remove bitbucket if used */
- (void) fclose(outfile[0]);
- m4temp[UNIQUE] = '0';
-#if unix
- (void) unlink(m4temp);
-#else
- (void) remove(m4temp);
-#endif
- }
- exit(0);
- return 0;
- }
-
//GO.SYSIN DD main.c
echo misc.c 1>&2
sed 's/.//' >misc.c <<'//GO.SYSIN DD misc.c'
-/* File : misc.c
- Author : Ozan Yigit
- Updated: 26-Mar-1993
- Purpose: Miscellaneous support code for PD M4.
-*/
-
-#include "mdef.h"
-#include "extr.h"
-#include "ourlims.h"
-
-#ifdef DUFFCP
-
-/* This version of the ANSI standard function memcpy()
- uses Duff's Device (tm Tom Duff) to unroll the copying loop:
- while (count-- > 0) *to++ = *from++;
-*/
-void memcpy(to, from, count)
- register char *from, *to;
- register int count;
- {
- if (count > 0) {
- register int loops = (count+8-1) >> 3; /* div 8 round up */
-
- switch (count & (8-1)) { /* mod 8 */
- case 0: do { *to++ = *from++;
- case 7: *to++ = *from++;
- case 6: *to++ = *from++;
- case 5: *to++ = *from++;
- case 4: *to++ = *from++;
- case 3: *to++ = *from++;
- case 2: *to++ = *from++;
- case 1: *to++ = *from++;
- } while (--loops > 0);
- }
- }
- }
-
-#endif
-
-
-/* strsave(s)
- return a new malloc()ed copy of s -- same as V.3's strdup().
-*/
-char *strsave(s)
- char *s;
- {
- register int n = strlen(s)+1;
- char *p = malloc(n);
-
- if (p) memcpy(p, s, n);
- return p;
- }
-
-
-/* indx(s1, s2)
- if s1 can be decomposed as alpha || s2 || omega, return the length
- of the shortest such alpha, otherwise return -1.
-*/
-int indx(s1, s2)
- char *s1;
- char *s2;
- {
- register char *t;
- register char *m;
- register char *p;
-
- for (p = s1; *p; p++) {
- for (t = p, m = s2; *m && *m == *t; m++, t++);
- if (!*m) return p-s1;
- }
- return -1;
- }
-
-
-char pbmsg[] = "m4: too many characters pushed back";
-
-/* Xputback(c)
- push character c back onto the input stream.
- This is now macro putback() in misc.h
-*/
-void Xputback(c)
- char c;
- {
- if (bp < endpbb) *bp++ = c; else error(pbmsg);
- }
-
-
-/* pbstr(s)
- push string s back onto the input stream.
- putback() has been unfolded here to improve performance.
- Example:
- s = <ABC>
- bp = <more stuff>
- After the call:
- bp = <more stuffCBA>
- It would be more efficient if we ran the pushback buffer in the
- opposite direction
-*/
-void pbstr(s)
- register char *s;
- {
- register char *es;
- register char *zp;
-
- zp = bp;
- for (es = s; *es; ) es++; /* now es points to terminating NUL */
- bp += es-s; /* advance bp as far as it should go */
- if (bp >= endpbb) error("m4: too many characters to push back");
- while (es > s) *zp++ = *--es;
- }
-
-
-/* pbqtd(s)
- pushes string s back "quoted", doing whatever has to be done to it to
- make sure that the result will evaluate to the original value. As it
- happens, we have only to add lquote and rquote.
-*/
-void pbqtd(s)
- register char *s;
- {
- register char *es;
- register char *zp;
-
- zp = bp;
- for (es = s; *es; ) es++; /* now es points to terminating NUL */
- bp += 2+es-s; /* advance bp as far as it should go */
- if (bp >= endpbb) error("m4: too many characters to push back");
- *zp++ = rquote;
- while (es > s) *zp++ = *--es;
- *zp++ = lquote;
- }
-
-
-/* pbnum(n)
- convert a number to a (decimal) string and push it back.
- The original definition did not work for MININT; this does.
-*/
-void pbnum(n)
- int n;
- {
- register int num;
-
- num = n > 0 ? -n : n; /* MININT <= num <= 0 */
- do {
- putback('0' - (num % 10));
- } while ((num /= 10) < 0);
- if (n < 0) putback('-');
- }
-
-
-/* pbrad(n, r, m)
- converts a number n to base r ([-36..-2] U [2..36]), with at least
- m digits. If r == 10 and m == 1, this is exactly the same as pbnum.
- However, this uses the function int2str() from R.A.O'Keefe's public
- domain string library, and puts the results of that back.
- The Unix System V Release 3 version of m4 accepts radix 1;
- THIS VERSION OF M4 DOES NOT ACCEPT RADIX 1 OR -1,
- nor do we accept radix < -36 or radix > 36. At the moment such bad
- radices quietly produce nothing. The V.3 treatment of radix 1 is
- push back abs(n) "1"s, then
- if n < 0, push back one "-".
- Until I come across something which uses it, I can't bring myself to
- implement this.
-
- I have, however, found a use for radix 0. Unsurprisingly, it is
- related to radix 0 in Edinburgh Prolog.
- eval('c1c2...cn', 0, m)
- pushes back max(m-n,0) blanks and the characters c1...cn. This can
- adjust to any byte size as long as UCHAR_MAX = (1 << CHAR_BIT) - 1.
- In particular, eval(c, 0) where 0 < c <= UCHAR_MAX, pushes back the
- character with code c. Note that this has to agree with eval(); so
- both of them have to use the same byte ordering.
-*/
-void pbrad(n, r, m)
- long int n;
- int r, m;
- {
- char buffer[34];
- char *p;
- int L;
-
- if (r == 0) {
- unsigned long int x = (unsigned long)n;
- int n;
-
- for (n = 0; x; x >>= CHAR_BIT, n++) buffer[n] = x & UCHAR_MAX;
- for (L = n; --L >= 0; ) putback(buffer[L]);
- for (L = m-n; --L >= 0; ) putback(' ');
- return;
- }
- L = m - (int2str(p = buffer, -r, n)-buffer);
- if (buffer[0] == '-') L++, p++;
- if (L > 0) {
- pbstr(p);
- while (--L >= 0) putback('0');
- if (p != buffer) putback('-');
- } else {
- pbstr(buffer);
- }
- }
-
-
-char csmsg[] = "m4: string space overflow";
-
-/* chrsave(c)
- put the character c in the string space.
-*/
-void Xchrsave(c)
- char c;
- {
-#if 0
- if (sp < 0) putc(c, active); else
-#endif
- if (ep < endest) *ep++ = c; else
- error(csmsg);
- }
-
-
-/* getdiv(ind)
- read in a diversion file and then delete it.
-*/
-void getdiv(ind)
- int ind;
- {
- register int c;
- register FILE *dfil;
- register FILE *afil;
-
- afil = active;
- if (outfile[ind] == afil)
- error("m4: undivert: diversion still active.");
- (void) fclose(outfile[ind]);
- outfile[ind] = NULL;
- m4temp[UNIQUE] = '0' + ind;
- if ((dfil = fopen(m4temp, "r")) == NULL)
- error("m4: cannot undivert.");
- while ((c = getc(dfil)) != EOF) putc(c, afil);
- (void) fclose(dfil);
-
-#if vms
- if (remove(m4temp)) error("m4: cannot unlink.");
-#else
- if (unlink(m4temp) == -1) error("m4: cannot unlink.");
-#endif
- }
-
-
-/* killdiv()
- delete all the diversion files which have been created.
-*/
-void killdiv()
- {
- register int n;
-
- for (n = 0; n < MAXOUT; n++) {
- if (outfile[n] != NULL) {
- (void) fclose(outfile[n]);
- m4temp[UNIQUE] = '0' + n;
-#if unix
- (void) unlink(m4temp);
-#else
- (void) remove(m4temp);
-#endif
- }
- }
- }
-
-
-/* error(s)
- close all files, report a fatal error, and quit, letting the caller know.
-*/
-void error(s)
- char *s;
- {
- killdiv();
- fprintf(stderr, "%s\n", s);
- exit(1);
- }
-
-
-/* Interrupt handling
-*/
-static char *msg = "\ninterrupted.";
-
-#ifdef __STDC__
-void onintr(int signo)
-#else
-onintr()
-#endif
- {
- error(msg);
- }
-
-
-void usage()
- {
- fprintf(stderr, "Usage: m4 [-e] [-[BHST]int] [-Dname[=val]] [-Uname]\n");
- exit(1);
- }
-
-#ifdef GETOPT
-/* Henry Spencer's getopt() - get option letter from argv */
-
-char *optarg; /* Global argument pointer. */
-int optind = 0; /* Global argv index. */
-
-static char *scan = NULL; /* Private scan pointer. */
-
-#ifndef __STDC__
-extern char *index();
-#define strchr index
-#endif
-
-int getopt(argc, argv, optstring)
- int argc;
- char **argv;
- char *optstring;
- {
- register char c;
- register char *place;
-
- optarg = NULL;
-
- if (scan == NULL || *scan == '\0') {
- if (optind == 0) optind++;
- if (optind >= argc
- || argv[optind][0] != '-'
- || argv[optind][1] == '\0')
- return EOF;
- if (strcmp(argv[optind], "--") == 0) {
- optind++;
- return EOF;
- }
- scan = argv[optind]+1;
- optind++;
- }
- c = *scan++;
- place = strchr(optstring, c);
-
- if (place == NULL || c == ':') {
- fprintf(stderr, "%s: unknown option -%c\n", argv[0], c);
- return '?';
- }
- place++;
- if (*place == ':') {
- if (*scan != '\0') {
- optarg = scan;
- scan = NULL;
- } else {
- optarg = argv[optind];
- optind++;
- }
- }
- return c;
- }
-#endif
-
//GO.SYSIN DD misc.c
echo serv.c 1>&2
sed 's/.//' >serv.c <<'//GO.SYSIN DD serv.c'
-/* File : serv.c
- Author : Ozan Yigit
- Updated: 4 May 1992
- Defines: Principal built-in macros for PD M4.
-*/
-
-#include "mdef.h"
-#include "extr.h"
-#include "ourlims.h"
-
-#define ucArgv(n) ((unsigned char *)argv[n])
-
-/* 26-Mar-1993 Made m4trim() 8-bit clean.
-*/
-
-/* expand(<DS FN A1 ... An>)
- 0 1 2 n+1 -- initial indices in argv[]
- -1 0 1 n -- after adjusting argv++, argc--
- This expands a user-defined macro; FN is the name of the macro, DS
- is its definition string, and A1 ... An are its arguments.
-*/
-void expand(argv, argc)
- char **argv;
- int argc;
- {
- register char *t;
- register char *p;
- register int n;
-
-#ifdef DEBUG
- fprintf(stderr, "expand(%s,%d)\n", argv[1], argc);
-#endif
- argc--; /* discount definition string (-1th arg) */
- t = *argv++; /* definition string as a whole */
- for (p = t; *p++; ) ;
- p -= 2; /* points to last character of definition */
- while (p > t) { /* if definition is empty, fails at once */
- if (*--p != ARGFLAG) {
- putback(p[1]);
- } else {
- switch (p[1]) {
- case '#':
- pbnum(argc-1);
- break;
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- if ((n = p[1]-'0') < argc) pbstr(argv[n]);
- break;
- case '*': /* push all arguments back */
- for (n = argc-1; n > 1; n--) {
- pbstr(argv[n]);
- putback(',');
- }
- pbstr(argv[1]);
- break;
- case '@': /* push arguments back quoted */
- for (n = argc-1; n > 1; n--) {
- pbqtd(argv[n]);
- putback(',');
- }
- pbqtd(argv[1]);
- break;
- case '$': /* $$ => $ */
- break;
- default:
- putback(p[1]);
- putback(p[0]);
- break;
- }
- p--;
- }
- }
- if (p == t) putback(*p); /* do last character */
- }
-
-
-static char nuldefmsg[] = "m4: defining null name.";
-static char recdefmsg[] = "m4: macro defined as itself.";
-
-/* dodefine(Name, Definition)
- install Definition as the only definition of Name in the hash table.
- */
-void dodefine(name, defn)
- register char *name;
- register char *defn;
- {
- register ndptr p;
-
- if (!name || !*name) error(nuldefmsg);
- if (strcmp(name, defn) == 0) error(recdefmsg);
-#ifdef DEBUG
- fprintf(stderr, "define(%s,--)\n", name);
-#endif
- if ((p = lookup(name)) == nil) {
- p = addent(name);
- } else
- if (p->defn != null) { /* what if p->type & STATIC ? */
- free(p->defn);
- }
- p->defn = !defn || !*defn ? null : strsave(defn);
- p->type = MACRTYPE;
- }
-
-
-/* dopushdef(Name, Definition)
- install Definition as the *first* definition of Name in the hash table,
- but do not remove any existing definitions. The new definition will
- hide any old ones until a popdef() removes it.
-*/
-void dopushdef(name, defn)
- register char *name;
- register char *defn;
- {
- register ndptr p;
-
- if (!name || !*name) error(nuldefmsg);
- if (strcmp(name, defn) == 0) error(recdefmsg);
-#ifdef DEBUG
- fprintf(stderr, "pushdef(%s,--)\n", name);
-#endif
- p = addent(name);
- p->defn = !defn || !*defn ? null : strsave(defn);
- p->type = MACRTYPE;
- }
-
-
-/* dodefn(Name)
- push back a *quoted* copy of Name's definition.
-*/
-void dodefn(name)
- char *name;
- {
- register ndptr p;
-
- if ((p = lookup(name)) != nil && p->defn != null) pbqtd(p->defn);
- }
-
-
-/* dodump(<? dump>) dump all definition in the hash table
- dodump(<? dump F1 ... Fn>) dump the definitions of F1 ... Fn in that order
- The requested definitions are written to stderr. What happens to names
- which have a built-in (numeric) definition?
-*/
-void dodump(argv, argc)
- register char **argv;
- register int argc;
- {
- register int n;
- ndptr p;
- static char dumpfmt[] = "define(`%s',\t`%s')\n";
-
- if (argc > 2) {
- for (n = 2; n < argc; n++)
- if ((p = lookup(argv[n])) != nil)
- fprintf(stderr, dumpfmt, p->name, p->defn);
- } else {
- for (n = 0; n < HASHSIZE; n++)
- for (p = hashtab[n]; p != nil; p = p->nxtptr)
- fprintf(stderr, dumpfmt, p->name, p->defn);
- }
- }
-
-
-/* doifelse(<? ifelse {x y ifx=y}... [else]>)
- 0 1 2 3 4 [2 when we get to it]
-*/
-void doifelse(argv, argc)
- register char **argv;
- register int argc;
- {
- for (; argc >= 5; argv += 3, argc -= 3)
- if (strcmp(argv[2], argv[3]) == 0) {
- pbstr(argv[4]);
- return;
- }
- if (argc >= 3) pbstr(argv[2]);
- }
-
-
-/* doinclude(FileName)
- include a given file.
-*/
-int doincl(FileName)
- char *FileName;
- {
- if (ilevel+1 == MAXINP) error("m4: too many include files.");
-#ifdef DEBUG
- fprintf(stderr, "include(%s)\n", FileName);
-#endif
- if ((infile[ilevel+1] = fopen(FileName, "r")) != NULL) {
-#ifndef NO__FILE
- dopushdef("__FILE__", FileName);
-#endif
- bbstack[ilevel+1] = bb;
- bb = bp;
- ilevel++;
- return 1;
- } else {
- return 0;
- }
- }
-
-
-#ifdef EXTENDED
-/* dopaste(FileName)
- copy a given file to the output stream without any macro processing.
-*/
-int dopaste(FileName)
- char *FileName;
- {
- register FILE *pf;
- register FILE *afil = active;
- register int c;
-
- if ((pf = fopen(FileName, "r")) != NULL) {
- while ((c = getc(pf)) != EOF) putc(c, afil);
- (void) fclose(pf);
- return 1;
- } else {
- return 0;
- }
- }
-#endif
-
-
-/* dochq(<? changequote [left [right [verbatim]]]>)
- 0 1 2 3 4
- change the quote characters; to single characters only.
- Empty arguments result in no change for that parameter.
- Missing arguments result in defaults:
- changequote => ` ' ^V
- changequote(q) => q q ^V
- changequote(l,r) => l r ^V
- changequote(l,r,v) => l r v
- There isn't any way of switching the verbatim-quote off,
- but if you make it the same as the right quote it won't
- be able to do anything (we check for R, L, V in that order).
-*/
-void dochq(argv, argc)
- register char **argv;
- register int argc;
- {
- if (argc > 2) {
- if (*argv[2]) lquote = *argv[2];
- if (argc > 3) {
- if (*argv[3]) rquote = *argv[3];
- if (argc > 4 && *argv[4]) vquote = *argv[4];
- } else {
- rquote = lquote;
- }
- } else {
- lquote = LQUOTE;
- rquote = RQUOTE;
- vquote = VQUOTE;
- }
- }
-
-
-/* dochc(<? changecomment [left [right]]>)
- 0 1 2 3
- change the comment delimiters; to single characters only.
-*/
-void dochc(argv, argc)
- register char **argv;
- register int argc;
- {
- if (argc > 2) {
- if (*argv[2]) scommt = *argv[2];
- if (argc > 3) {
- if (*argv[3]) ecommt = *argv[3];
- } else {
- ecommt = ECOMMT;
- }
- } else {
- scommt = '\0'; /* assuming no nulls in input */
- ecommt = '\0';
- }
- }
-
-
-/* dodivert - divert the output to a temporary file
-*/
-void dodiv(n)
- register int n;
- {
- if (n < 0 || n >= MAXOUT) n = 0; /* bitbucket */
- if (outfile[n] == NULL) {
- m4temp[UNIQUE] = '0' + n;
- if ((outfile[n] = fopen(m4temp, "w")) == NULL)
- error("m4: cannot divert.");
- }
- oindex = n;
- active = outfile[n];
- }
-
-
-/* doundivert - undivert a specified output, or all
- * other outputs, in numerical order.
-*/
-void doundiv(argv, argc)
- register char **argv;
- register int argc;
- {
- register int ind;
- register int n;
-
- if (argc > 2) {
- for (ind = 2; ind < argc; ind++) {
- n = expr(argv[ind]);
- if (n > 0 && n < MAXOUT && outfile[n] != NULL) getdiv(n);
- }
- } else {
- for (n = 1; n < MAXOUT; n++)
- if (outfile[n] != NULL) getdiv(n);
- }
- }
-
-
-/* dosub(<? substr {offset} [{length}]>)
- The System V Interface Definition does not say what happens when the
- offset or length are out of range. I have chosen to force them into
- range, with the result that unlike the former version of this code,
- dosub cannot be tricked into SIGSEGV.
-
- BUG: This is not 8-bit clean yet.
-*/
-void dosub(argv, argc)
- char **argv;
- int argc;
- {
- register int nc; /* number of characters */
- register char *ap = argv[2]; /* target string */
- register int al = strlen(ap); /* its length */
- register int df = expr(argv[3]);/* offset */
-
- if (df < 0) df = 0; else /* force df back into the range */
- if (df > al) df = al; /* 0 <= df <= al */
- al -= df; /* now al limits nc */
-
- if (argc >= 5) { /* nc is provided */
- nc = expr(argv[4]);
- if (nc < 0) nc = 0; else /* force nc back into the range */
- if (nc > al) nc = al; /* 0 <= nc <= strlen(ap)-df */
- } else {
- nc = al; /* default is all rest of ap */
- }
- ap += df + nc;
- while (--nc >= 0) putback(*--ap);
- }
-
-
-/* map(dest, src, from, to)
- map every character of src that is specified in from
- into "to" and replace in dest. (source "src" remains untouched)
-
- This is a standard implementation of Icon's map(s,from,to) function.
- Within mapvec, we replace every character of "from" with the
- corresponding character in "to". If "to" is shorter than "from",
- then the corresponding entries are null, which means that those
- characters disappear altogether. Furthermore, imagine a call like
- map(dest, "sourcestring", "srtin", "rn..*"). In this case, `s' maps
- to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' ultimately
- maps to `*'. In order to achieve this effect in an efficient manner
- (i.e. without multiple passes over the destination string), we loop
- over mapvec, starting with the initial source character. If the
- character value (dch) in this location is different from the source
- character (sch), sch becomes dch, once again to index into mapvec,
- until the character value stabilizes (i.e. sch = dch, in other words
- mapvec[n] == n). Even if the entry in the mapvec is null for an
- ordinary character, it will stabilize, since mapvec[0] == 0 at all
- times. At the end, we restore mapvec* back to normal where
- mapvec[n] == n for 0 <= n <= 127. This strategy, along with the
- restoration of mapvec, is about 5 times faster than any algorithm
- that makes multiple passes over the destination string.
-*/
-
-void map(d, s, f, t)
- char *d, *s, *f, *t;
- {
- register unsigned char *dest = (unsigned char *)d;
- register unsigned char *src = (unsigned char *)s;
- unsigned char *from = (unsigned char *)f;
- register unsigned char *to = (unsigned char *)t;
- register unsigned char *tmp;
- register unsigned char sch, dch;
- static unsigned char mapvec[1+UCHAR_MAX] = {1};
-
- if (mapvec[0]) {
- register int i;
- for (i = 0; i <= UCHAR_MAX; i++) mapvec[i] = i;
- }
- if (src && *src) {
- /* create a mapping between "from" and "to" */
- if (to && *to)
- for (tmp = from; sch = *tmp++; ) mapvec[sch] = *to++;
- else
- for (tmp = from; sch = *tmp++; ) mapvec[sch] = '\0';
-
- while (sch = *src++) {
- while ((dch = mapvec[sch]) != sch) sch = dch;
- if (*dest = dch) dest++;
- }
- /* restore all the changed characters */
- for (tmp = from; sch = *tmp++; ) mapvec[sch] = sch;
- }
- *dest = '\0';
- }
-
-
-#ifdef EXTENDED
-
-/* m4trim(<? m4trim [string [leading [trailing [middle [rep]]]]]>)
- 0 1 2 3 4 5 6
-
- (1) Any prefix consisting of characters in the "leading" set is removed.
- The default is " \t\n".
- (2) Any suffix consisting of characters in the "trailing" set is removed.
- The default is to be the same as leading.
- (3) Any block of consecutive characters in the "middle" set is replaced
- by the rep string. The default for middle is " \t\n", and the
- default for rep is the first character of middle.
-*/
-void m4trim(argv, argc)
- char **argv;
- int argc;
- {
- static unsigned char repbuf[2] = " ";
- static unsigned char layout[] = " \t\n\r\f";
- unsigned char *string = argc > 2 ? ucArgv(2) : repbuf+1;
- unsigned char *leading = argc > 3 ? ucArgv(3) : layout;
- unsigned char *trailing = argc > 4 ? ucArgv(4) : leading;
- unsigned char *middle = argc > 5 ? ucArgv(5) : trailing;
- unsigned char *rep = argc > 6 ? ucArgv(6) :
- (repbuf[0] = *middle, repbuf);
- static unsigned char sets[1+UCHAR_MAX];
-# define PREF 1
-# define SUFF 2
-# define MIDL 4
- register int i, n;
-
- for (i = UCHAR_MAX; i >= 0; ) sets[i--] = 0;
- while (*leading) sets[*leading++] |= PREF;
- while (*trailing) sets[*trailing++] |= SUFF;
- while (*middle) sets[*middle++] |= MIDL;
-
- while (*string && sets[*string]&PREF) string++;
- n = strlen((char *)string);
- while (n > 0 && sets[string[n-1]]&SUFF) n--;
- while (n > 0) {
- i = string[--n];
- if (sets[i]&MIDL) {
- pbstr((char*)rep);
- while (n > 0 && sets[string[n-1]]&MIDL) n--;
- } else {
- putback(i);
- }
- }
- }
-
-
-/* defquote(MacroName # The name of the "quoter" macro to be defined.
- [, Opener # default: "'". The characters to place at the
- # beginning of the result.
- [, Separator # default: ",". The characters to place between
- # successive arguments.
- [, Closer # default: same as Opener. The characters to
- # place at the end of the result.
- [, Escape # default: `' The escape character to put in
- # front of things that need escaping.
- [, Default # default: simple. Possible values are
- # [lL].* = letter, corresponds to PLAIN1.
- # [dD].* = digit, corresponds to PLAIN2.
- # [sS].* = simple, corresponds to SIMPLE.
- # [eE].* = escaped,corresponds to SCAPED.
- # .*, corresponds to FANCY
- [, Letters # default: `'. The characters of type "L".
- [, Digits # default: `'. The characters of type "D".
- [, Simple # default: `'. The characters of type "S".
- [, Escaped # default: `'. The characters of type "E".
- {, Fancy # default: none. Each has the form `C'`Repr'
- # saying that the character C is to be represented
- # as Repr. Can be used for trigraphs, \n, &c.
- }]]]]]]]]])
-
- Examples:
- defquote(DOUBLEQT, ")
- defquote(SINGLEQT, ')
- After these definitions,
- DOUBLEQT(a, " b", c) => "a,"" b"",c"
- SINGLEQT("Don't`, 'he said.") => '"Don''t, he said."'
- Other examples defining quote styles for several languages will be
- provided later.
-
- A quoter is represented in M4 by a special identifying number and a
- pointer to a Quoter record. I expect that there will be few quoters
- but that they will need to go fairly fast.
-
-*/
-
-#define PLAIN1 0
-#define PLAIN2 1
-#define SIMPLE 2
-#define SCAPED 3
-#define FANCY 4
-
-struct Quoter
- {
- char *opener;
- char *separator;
- char *closer;
- char *escape;
- char *fancy[1+UCHAR_MAX];
- char class[1+UCHAR_MAX];
- };
-
-void freeQuoter(q)
- struct Quoter *q;
- {
- int i;
-
- free(q->opener);
- free(q->separator);
- free(q->closer);
- free(q->escape);
- for (i = UCHAR_MAX; i >= 0; i--)
- if (q->fancy[i]) free(q->fancy[i]);
- free((char *)q);
- }
-
-/* dodefqt(<
- 0 ?
- 1 defquote
- 2 MacroName
- [ 3 Opener
- [ 4 Separator
- [ 5 Closer
- [ 6 Escape
- [ 7 Default
- [ 8 Letters
- [ 9 Digits
- [10 Simple
- [11 Escaped
- [11+i Fancy[i] ]]]]]]]]]]>)
-*/
-
-void dodefqt(argv, argc)
- char **argv;
- int argc;
- {
- struct Quoter q, *r;
- register int i;
- register unsigned char *s;
- register int c;
- ndptr p;
-
- if (!(argc > 2 && *argv[2])) error(nuldefmsg);
- switch (argc > 7 ? argv[7][0] : '\0') {
- case 'l': case 'L': c = PLAIN1; break;
- case 'd': case 'D': c = PLAIN2; break;
- case 'e': case 'E': c = SCAPED; break;
- case 'f': case 'F': c = FANCY; break;
- default: c = SIMPLE;
- }
- for (i = UCHAR_MAX; --i >= 0; ) q.class[i] = c;
- for (i = UCHAR_MAX; --i >= 0; ) q.fancy[i] = 0;
- q.opener = strsave(argc > 3 ? argv[3] : "");
- q.separator = strsave(argc > 4 ? argv[4] : ",");
- q.closer = strsave(argc > 5 ? argv[5] : q.opener);
- q.escape = strsave(argc > 6 ? argv[6] : "");
- if (argc > 8)
- for (s = (unsigned char *)argv[8]; c = *s++; )
- q.class[c] = PLAIN1;
- if (argc > 9)
- for (s = (unsigned char *)argv[9]; c = *s++; )
- q.class[c] = PLAIN2;
- if (argc > 10)
- for (s = (unsigned char *)argv[10]; c = *s++; )
- q.class[c] = SIMPLE;
- if (argc > 11)
- for (s = (unsigned char *)argv[11]; c = *s++; )
- q.class[c] = SCAPED;
- for (i = 12; i < argc; i++) {
- s = (unsigned char *)argv[i];
- c = *s++;
- q.fancy[c] = strsave((char *)s);
- q.class[c] = FANCY;
- }
- /* Now we have to make sure that the closing quote works. */
- if ((c = q.closer[0]) && q.class[c] <= SIMPLE) {
- if (q.escape[0]) {
- q.class[c] = SCAPED;
- } else {
- char buf[3];
- buf[0] = c, buf[1] = c, buf[2] = '\0';
- q.fancy[c] = strsave(buf);
- q.class[c] = FANCY;
- }
- }
- /* We also have to make sure that the escape (if any) works. */
- if ((c = q.escape[0]) && q.class[c] <= SIMPLE) {
- q.class[c] = SCAPED;
- }
- r = (struct Quoter *)malloc(sizeof *r);
- if (r == NULL) error("m4: no more memory");
- *r = q;
- p = addent(argv[2]);
- p->defn = (char *)r;
- p->type = QUTRTYPE;
- }
-
-
-/* doqutr(<DB MN A1 ... An>)
- 0 1 2 n+1 argc
- argv[0] points to the struct Quoter.
- argv[1] points to the name of this quoting macro
- argv[2..argc-1] point to the arguments.
- This applies a user-defined quoting macro. For example, we could
- define a macro to produce Prolog identifiers:
- defquote(plid, ', , ', , simple,
- abcdefghijklmnopqrstuvwxyz,
- ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789)
-
- After doing that,
- plid(foo) => foo
- plid(*) => '*'
- plid(Don't) => 'Don''t'
- plid(foo,) => 'foo'
-*/
-void doqutr(argv, argc)
- char **argv;
- int argc;
- /* DEFINITION-BLOCK MacroName Arg1 ... Argn
- 0 1 2 n-1 argc
- */
- {
- struct Quoter *r = (struct Quoter *)argv[0];
- char *p;
- register unsigned char *b, *e;
- int i;
- register int c;
-
- for (;;) { /* does not actually loop */
- if (argc != 3) break;
- b = ucArgv(2);
- e = b + strlen((char*)b);
- if (e == b) break;
- if (r->class[*b++] != PLAIN1) break;
- while (b != e && r->class[*b] <= PLAIN2) b++;
- if (b != e) break;
- pbstr(argv[2]);
- return;
- }
-
- p = r->closer;
- if (argc < 3) {
- pbstr(p);
- } else
- for (i = argc-1; i >= 2; i--) {
- pbstr(p);
- b = ucArgv(i);
- e = b+strlen((char *)b);
- while (e != b)
- switch (r->class[c = *--e]) {
- case FANCY:
- p = r->fancy[c];
- if (p) {
- pbstr(p);
- } else {
- pbrad(c, 8, 1);
- pbstr(r->escape);
- }
- break;
- case SCAPED:
- putback(c);
- pbstr(r->escape);
- break;
- default:
- putback(c);
- break;
- }
- p = r->separator;
- }
- pbstr(r->opener);
- }
-
-#endif
//GO.SYSIN DD serv.c
echo extr.h 1>&2
sed 's/.//' >extr.h <<'//GO.SYSIN DD extr.h'
-/* Header : extr.h
- Author : Ozan Yigit
- Updated: %G%
-*/
-#ifndef putback
-
-extern ndptr hashtab[]; /* hash table for macros etc. */
-extern char buf[]; /* push-back buffer */
-extern char *bp; /* first available character */
-extern char *bb; /* current beginning of bp */
-extern char *endpbb; /* end of push-back buffer */
-extern stae mstack[]; /* stack of m4 machine */
-extern char *ep; /* first free char in strspace */
-extern char *endest; /* end of string space */
-extern int sp; /* current m4 stack pointer */
-extern int fp; /* m4 call frame pointer */
-extern char *bbstack[];
-extern FILE *infile[]; /* input file stack (0=stdin) */
-extern FILE *outfile[]; /* diversion array(0=bitbucket)*/
-extern FILE *active; /* active output file pointer */
-extern char *m4temp; /* filename for diversions */
-extern int UNIQUE; /* where to change m4temp */
-extern int ilevel; /* input file stack pointer */
-extern int oindex; /* diversion index.. */
-extern char *null; /* as it says.. just a null.. */
-extern char *m4wraps; /* m4wrap string default.. */
-extern char lquote; /* left quote character (`) */
-extern char rquote; /* right quote character (') */
-extern char vquote; /* verbatim quote character ^V */
-extern char scommt; /* start character for comment */
-extern char ecommt; /* end character for comment */
-
-/* inlined versions of chrsave() and putback() */
-
-extern char pbmsg[]; /* error message for putback */
-extern char csmsg[]; /* error message for chrsave */
-
-#define putback(c) do { if (bp >= endpbb) error(pbmsg); *bp++ = c; } while (0)
-#define chrsave(c) do { if (ep >= endest) error(csmsg); *ep++ = c; } while (0)
-
-/* getopt() interface */
-
-extern char * optarg;
-extern int optind;
-#ifdef __STDC__
-extern int getopt(int, char **, char *);
-#else
-extern int getopt();
-#endif
-
-#ifdef __STDC__
-#include <stdlib.h>
-
-/* functions from misc.c */
-
-extern char * strsave(char *);
-extern int indx(char *, char *);
-extern void pbstr(char *);
-extern void pbqtd(char *);
-extern void pbnum(int);
-extern void pbrad(long int, int, int);
-extern void getdiv(int);
-extern void killdiv();
-extern void error(char *);
-extern void onintr(int);
-extern void usage();
-
-/* functions from look.c */
-
-extern ndptr lookup(char *);
-extern ndptr addent(char *);
-extern void remhash(char *, int);
-extern void addkywd(char *, int);
-
-/* functions from int2str.c */
-
-extern char* int2str(/* char*, int, long */);
-
-/* functions from serv.c */
-
-extern void expand(char **, int);
-extern void dodefine(char *, char *);
-extern void dopushdef(char *, char *);
-extern void dodefn(char *);
-extern void dodump(char **, int);
-extern void doifelse(char **, int);
-extern int doincl(char *);
-extern void dochq(char **, int);
-extern void dochc(char **, int);
-extern void dodiv(int);
-extern void doundiv(char **, int);
-extern void dosub(char **, int);
-extern void map(char *, char *, char *, char *);
-#ifdef EXTENDED
-extern int dopaste(char *);
-extern void m4trim(char **, int);
-extern void dodefqt(char **, int);
-extern void doqutr(char **, int);
-#endif
-
-/* functions from expr.c */
-
-extern long expr(char *);
-
-#else
-
-/* functions from misc.c */
-
-extern char * malloc();
-extern char * strsave();
-extern int indx();
-extern void pbstr();
-extern void pbqtd();
-extern void pbnum();
-extern void pbrad();
-extern void getdiv();
-extern void killdiv();
-extern void error();
-extern int onintr();
-extern void usage();
-
-/* functions from look.c */
-
-extern ndptr lookup();
-extern ndptr addent();
-extern void remhash();
-extern void addkywd();
-
-/* functions from int2str.c */
-
-extern char* int2str(/* char*, int, long */);
-
-/* functions from serv.c */
-
-extern void expand();
-extern void dodefine();
-extern void dopushdef();
-extern void dodefn();
-extern void dodump();
-extern void doifelse();
-extern int doincl();
-extern void dochq();
-extern void dochc();
-extern void dodiv();
-extern void doundiv();
-extern void dosub();
-extern void map();
-#ifdef EXTENDED
-extern int dopaste();
-extern void m4trim();
-extern void dodefqt();
-extern void doqutr();
-#endif
-
-/* functions from expr.c */
-
-extern long expr();
-
-#endif
-#endif
//GO.SYSIN DD extr.h
echo mdef.h 1>&2
sed 's/.//' >mdef.h <<'//GO.SYSIN DD mdef.h'
-/* Header : mdef.h
- Author : Ozan Yigit
- Updated: 4 May 1992
-*/
-#ifndef MACRTYPE
-
-#ifndef unix
-#define unix 0
-#endif
-
-#ifndef vms
-#define vms 0
-#endif
-
-#include <stdio.h>
-#include <signal.h>
-
-#ifdef __STDC__
-#include <string.h>
-#else
-#ifdef VOID
-#define void int
-#endif
-extern int strlen();
-extern int strcmp();
-extern void memcpy();
-#endif
-
-/* m4 constants */
-
-#define MACRTYPE 1
-#define DEFITYPE 2
-#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 LINETYPE 34
-#define TRIMTYPE 35
-#define TLITTYPE 36
-#define DEFQTYPE 37 /* defquote */
-#define QUTRTYPE 38 /* quoter thus defined */
-
-#define STATIC 128
-
-/* m4 special characters */
-
-#define ARGFLAG '$'
-#define LPAREN '('
-#define RPAREN ')'
-#define LQUOTE '`'
-#define RQUOTE '\''
-#define VQUOTE ('V'&(' '- 1))
-#define COMMA ','
-#define SCOMMT '#'
-#define ECOMMT '\n'
-
-/*
- * other important constants
- */
-
-#define EOS (char) 0
-#define MAXINP 10 /* maximum include files */
-#define MAXOUT 10 /* maximum # of diversions */
-#ifdef SMALL
-#define MAXSTR 512 /* maximum size of string */
-#define BUFSIZE 4096 /* size of pushback buffer */
-#define STACKMAX 1024 /* size of call stack */
-#define STRSPMAX 4096 /* size of string space */
-#define HASHSIZE 199 /* maximum size of hashtab */
-#else
-#define MAXSTR 1024 /* maximum size of string */
-#define BUFSIZE 8192 /* size of pushback buffer */
-#define STACKMAX 2048 /* size of call stack */
-#define STRSPMAX 8192 /* size of string space */
-#define HASHSIZE 509 /* maximum size of hashtab */
-#endif
-#define MAXTOK MAXSTR /* maximum chars in a tokn */
-
-#define ALL 1
-#define TOP 0
-
-#define TRUE 1
-#define FALSE 0
-
-/* m4 data structures */
-
-typedef struct ndblock *ndptr;
-
-struct ndblock /* hashtable structure */
- {
- char *name; /* entry name.. */
- char *defn; /* definition.. */
- int type; /* type of the entry.. */
- ndptr nxtptr; /* link to next entry.. */
- };
-
-#define nil ((ndptr) 0)
-
-typedef union /* stack structure */
- { int sfra; /* frame entry */
- char *sstr; /* string entry */
- } stae;
-
-/*
- * macros for readibility and/or speed
- *
- * gpbc() - get a possibly pushed-back character
- * min() - select the minimum of two elements
- * pushf() - push a call frame entry onto stack
- * pushs() - push a string pointer onto stack
- */
-#define gpbc() bp == bb ? getc(infile[ilevel]) : *--bp
-#define min(x,y) ((x > y) ? y : x)
-#define pushf(x) if (sp < STACKMAX) mstack[++sp].sfra = (x)
-#define pushs(x) if (sp < STACKMAX) mstack[++sp].sstr = (x)
-
-/*
- * . .
- * | . | <-- sp | . |
- * +-------+ +-----+
- * | arg 3 ----------------------->| str |
- * +-------+ | . |
- * | arg 2 ---PREVEP-----+ .
- * +-------+ |
- * . | | |
- * +-------+ | +-----+
- * | plev | PARLEV +-------->| str |
- * +-------+ | . |
- * | type | CALTYP .
- * +-------+
- * | prcf ---PREVFP--+
- * +-------+ |
- * | . | PREVSP |
- * . |
- * +-------+ |
- * | <----------+
- * +-------+
- *
- */
-#define PARLEV (mstack[fp].sfra)
-#define CALTYP (mstack[fp-1].sfra)
-#define PREVEP (mstack[fp+3].sstr)
-#define PREVSP (fp-3)
-#define PREVFP (mstack[fp-2].sfra)
-
-#endif
//GO.SYSIN DD mdef.h
echo ourlims.h 1>&2
sed 's/.//' >ourlims.h <<'//GO.SYSIN DD ourlims.h'
-/* File : ourlims.h
- Author : Richard A. O'Keefe
- Defines: UCHAR_MAX, CHAR_BIT, LONG_BIT
-*/
-/* If <limits.h> is available, use that.
- Otherwise, use 8-bit byte as the default.
- If the number of characters is a power of 2, you might be able
- to use (unsigned char)(~0), but why get fancy?
-*/
-#ifdef __STDC__
-#include <limits.h>
-#else
-#define UCHAR_MAX 255
-#define CHAR_BIT 8
-#endif
-#define LONG_BIT 32
//GO.SYSIN DD ourlims.h
echo m4.1 1>&2
sed 's/.//' >m4.1 <<'//GO.SYSIN DD m4.1'
-.\"
-.\" @(#) $Id$
-.\"
-.Dd January 26, 1993
-.Dt m4 1
-.Os
-.Sh NAME
-.Nm m4
-.Nd macro language processor
-.Sh SYNOPSIS
-.Nm m4
-.Oo
-.Fl D Ns Ar name Ns Op Ar =value
-.Oc
-.Op Fl U Ns Ar name
-.Sh DESCRIPTION
-The
-.Nm m4
-utility is a macro processor that can be used as a front end to any
-language (e.g., C, ratfor, fortran, lex, and yacc).
-.Nm m4
-reads from the standard input and writes
-the processed text to the standard output.
-.Pp
-Macro calls have the form name(argument1[, argument2, ...,] argumentN).
-.Pp
-There cannot be any space following the macro name and the open
-parentheses '('. If the macro name is not followed by an open
-parentheses it is processed with no arguments.
-.Pp
-Macro names consist of a leading alphabetic or underscore
-possibly followed by alphanumeric or underscore characters, therefore
-valid macro names match this pattern [a-zA-Z_][a-zA-Z0-9_]*.
-.Pp
-In arguments to macros, leading unquoted space, tab and newline
-characters are ignored. To quote strings use left and right single
-quotes (e.g., ` this is a string with a leading space'). You can change
-the quote characters with the changequote built-in macro.
-.Pp
-The options are as follows:
-.Bl -tag -width "-Dname[=value]xxx"
-.It Fl D Ns Ar name Ns Oo
-.Ar =value
-.Oc
-Define the symbol
-.Ar name
-to have some value (or NULL).
-.It Fl "U" Ns Ar "name"
-Undefine the symbol
-.Ar name .
-.El
-.Sh SYNTAX
-.Nm m4
-provides the following built-in macros. They may be
-redefined, loosing their original meaning.
-Return values are NULL unless otherwise stated.
-.Bl -tag -width changequotexxx
-.It changecom
-Change the start and end comment sequences. The default is
-the pound sign `#' and the newline character. With no arguments
-comments are turned off. The maximum length for a comment marker is
-five characters.
-.It changequote
-Defines the quote symbols to be the first and second arguments.
-The symbols may be up to five characters long. If no arguments are
-given it restores the default open and close single quotes.
-.It decr
-Decrements the argument by 1. The argument must be a valid numeric string.
-.It define
-Define a new macro named by the first argument to have the
-value of the second argument. Each occurrence of $n (where n
-is 0 through 9) is replaced by the n'th argument. $0 is the name
-of the calling macro. Undefined arguments are replaced by a
-NULL string. $# is replaced by the number of arguments; $*
-is replaced by all arguments comma separated; $@ is the same
-as $* but all arguments are quoted against further expansion.
-.It defn
-Returns the quoted definition for each argument. This can be used to rename
-macro definitions (even for built-in macros).
-.It divert
-There are 10 output queues (numbered 0-9).
-At the end of processing
-.Nm m4
-concatenates all the queues in numerical order to produce the
-final output. Initially the output queue is 0. The divert
-macro allows you to select a new output queue (an invalid argument
-passed to divert causes output to be discarded).
-.It divnum
-Returns the current output queue number.
-.It dnl
-Discard input characters up to and including the next newline.
-.It dumpdef
-Prints the names and definitions for the named items, or for everything
-if no arguments are passed.
-.It errprint
-Prints the first argument on the standard error output stream.
-.It eval
-Computes the first argument as an arithmetic expression using 32-bit
-arithmetic. Operators are the standard C ternary, arithmetic, logical,
-shift, relational, bitwise, and parentheses operators. You can specify
-octal, decimal, and hexadecimal numbers as in C. The second argument (if
-any) specifies the radix for the result and the third argument (if
-any) specifies the minimum number of digits in the result.
-.It expr
-This is an alias for eval.
-.It ifdef
-If the macro named by the first argument is defined then return the second
-argument, otherwise the third. If there is no third argument,
-the value is NULL. The word `unix' is predefined.
-.It ifelse
-If the first argument matches the second argument then ifelse returns
-the third argument. If the match fails the three arguments are
-discarded and the next three arguments are used until there is
-zero or one arguments left, either this last argument or NULL is
-returned if no other matches were found.
-.It include
-Returns the contents of the file specified in the first argument.
-Include aborts with an error message if the file cannot be included.
-.It incr
-Increments the argument by 1. The argument must be a valid numeric string.
-.It index
-Returns the index of the second argument in the first argument (e.g.,
-index(the quick brown fox jumped, fox) returns 16). If the second
-argument is not found index returns -1.
-.It len
-Returns the number of characters in the first argument. Extra arguments
-are ignored.
-.It m4exit
-Immediately exits with the return value specified by the first argument,
-0 if none.
-.It m4wrap
-Allows you to define what happens at the final EOF, usually for cleanup
-purposes (e.g., m4wrap("cleanup(tempfile)") causes the macro cleanup to
-invoked after all other processing is done.)
-.It maketemp
-Translates the string XXXXX in the first argument with the current process
-ID leaving other characters alone. This can be used to create unique
-temporary file names.
-.It paste
-Includes the contents of the file specified by the first argument without
-any macro processing. Aborts with an error message if the file cannot be
-included.
-.It popdef
-Restores the pushdef'ed definition for each argument.
-.It pushdef
-Takes the same arguments as define, but it saves the definition on a
-stack for later retrieval by popdef.
-.It shift
-Returns all but the first argument, the remaining arguments are
-quoted and pushed back with commas in between. The quoting
-nullifies the effect of the extra scan that will subsequently be
-performed.
-.It sinclude
-Similar to include, except it ignores any errors.
-.It spaste
-Similar to spaste, except it ignores any errors.
-.It substr
-Returns a substring of the first argument starting at the offset specified
-by the second argument and the length specified by the third argument.
-If no third argument is present it returns the rest of the string.
-.It syscmd
-Passes the first argument to the shell. Nothing is returned.
-.It sysval
-Returns the return value from the last syscmd.
-.It translit
-Transliterate the characters in the first argument from the set
-given by the second argument to the set given by the third. You cannot
-use
-.Xr tr 1
-style abbreviations.
-.It undefine
-Removes the definition for the macro specified by the first argument.
-.It undivert
-Flushes the named output queues (or all queues if no arguments).
-.It unix
-A pre-defined macro for testing the OS platform.
-.El
-.Sh AUTHOR
-Ozan Yigit <oz@sis.yorku.ca> and Richard A. O'Keefe (ok@goanna.cs.rmit.OZ.AU)
//GO.SYSIN DD m4.1