main.c revision 24360
11590Srgrimes/*-
21590Srgrimes * Copyright (c) 1989, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * This code is derived from software contributed to Berkeley by
61590Srgrimes * Ozan Yigit at York University.
71590Srgrimes *
81590Srgrimes * Redistribution and use in source and binary forms, with or without
91590Srgrimes * modification, are permitted provided that the following conditions
101590Srgrimes * are met:
111590Srgrimes * 1. Redistributions of source code must retain the above copyright
121590Srgrimes *    notice, this list of conditions and the following disclaimer.
131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer in the
151590Srgrimes *    documentation and/or other materials provided with the distribution.
161590Srgrimes * 3. All advertising materials mentioning features or use of this software
171590Srgrimes *    must display the following acknowledgement:
181590Srgrimes *	This product includes software developed by the University of
191590Srgrimes *	California, Berkeley and its contributors.
201590Srgrimes * 4. Neither the name of the University nor the names of its contributors
211590Srgrimes *    may be used to endorse or promote products derived from this software
221590Srgrimes *    without specific prior written permission.
231590Srgrimes *
241590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341590Srgrimes * SUCH DAMAGE.
351590Srgrimes */
361590Srgrimes
371590Srgrimes#ifndef lint
381590Srgrimesstatic char copyright[] =
391590Srgrimes"@(#) Copyright (c) 1989, 1993\n\
401590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
411590Srgrimes#endif /* not lint */
421590Srgrimes
431590Srgrimes#ifndef lint
441590Srgrimesstatic char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
451590Srgrimes#endif /* not lint */
461590Srgrimes
471590Srgrimes/*
481590Srgrimes * main.c
491590Srgrimes * Facility: m4 macro processor
501590Srgrimes * by: oz
511590Srgrimes */
521590Srgrimes
531590Srgrimes#include <sys/types.h>
541590Srgrimes#include <signal.h>
551590Srgrimes#include <errno.h>
561590Srgrimes#include <unistd.h>
571590Srgrimes#include <stdio.h>
581590Srgrimes#include <ctype.h>
591590Srgrimes#include <string.h>
601590Srgrimes#include "mdef.h"
611590Srgrimes#include "stdd.h"
621590Srgrimes#include "extern.h"
631590Srgrimes#include "pathnames.h"
641590Srgrimes
651590Srgrimesndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
665165Sacheunsigned char buf[BUFSIZE];              /* push-back buffer            */
675165Sacheunsigned char *bufbase = buf;            /* the base for current ilevel */
685165Sacheunsigned char *bbase[MAXINP];            /* the base for each ilevel    */
695165Sacheunsigned char *bp = buf;                 /* first available character   */
705165Sacheunsigned char *endpbb = buf+BUFSIZE;     /* end of push-back buffer     */
711590Srgrimesstae mstack[STACKMAX+1]; 	/* stack of m4 machine         */
721590Srgrimeschar strspace[STRSPMAX+1];	/* string space for evaluation */
731590Srgrimeschar *ep = strspace;		/* first free char in strspace */
741590Srgrimeschar *endest= strspace+STRSPMAX;/* end of string space	       */
751590Srgrimesint sp; 			/* current m4  stack pointer   */
761590Srgrimesint fp; 			/* m4 call frame pointer       */
771590SrgrimesFILE *infile[MAXINP];		/* input file stack (0=stdin)  */
781590SrgrimesFILE *outfile[MAXOUT];		/* diversion array(0=bitbucket)*/
791590SrgrimesFILE *active;			/* active output file pointer  */
801590Srgrimeschar *m4temp;			/* filename for diversions     */
811590Srgrimesint ilevel = 0; 		/* input file stack pointer    */
821590Srgrimesint oindex = 0; 		/* diversion index..	       */
831590Srgrimeschar *null = "";                /* as it says.. just a null..  */
841590Srgrimeschar *m4wraps = "";             /* m4wrap string default..     */
851590Srgrimeschar *progname;			/* name of this program        */
861590Srgrimeschar lquote = LQUOTE;		/* left quote character  (`)   */
871590Srgrimeschar rquote = RQUOTE;		/* right quote character (')   */
881590Srgrimeschar scommt = SCOMMT;		/* start character for comment */
891590Srgrimeschar ecommt = ECOMMT;		/* end character for comment   */
901590Srgrimes
911590Srgrimesstruct keyblk keywrds[] = {	/* m4 keywords to be installed */
921590Srgrimes	"include",      INCLTYPE,
931590Srgrimes	"sinclude",     SINCTYPE,
941590Srgrimes	"define",       DEFITYPE,
951590Srgrimes	"defn",         DEFNTYPE,
961590Srgrimes	"divert",       DIVRTYPE,
971590Srgrimes	"expr",         EXPRTYPE,
981590Srgrimes	"eval",         EXPRTYPE,
991590Srgrimes	"substr",       SUBSTYPE,
1001590Srgrimes	"ifelse",       IFELTYPE,
1011590Srgrimes	"ifdef",        IFDFTYPE,
1021590Srgrimes	"len",          LENGTYPE,
1031590Srgrimes	"incr",         INCRTYPE,
1041590Srgrimes	"decr",         DECRTYPE,
1051590Srgrimes	"dnl",          DNLNTYPE,
1061590Srgrimes	"changequote",  CHNQTYPE,
1071590Srgrimes	"changecom",    CHNCTYPE,
1081590Srgrimes	"index",        INDXTYPE,
1091590Srgrimes#ifdef EXTENDED
1101590Srgrimes	"paste",        PASTTYPE,
1111590Srgrimes	"spaste",       SPASTYPE,
1121590Srgrimes#endif
1131590Srgrimes	"popdef",       POPDTYPE,
1141590Srgrimes	"pushdef",      PUSDTYPE,
1151590Srgrimes	"dumpdef",      DUMPTYPE,
1161590Srgrimes	"shift",        SHIFTYPE,
1171590Srgrimes	"translit",     TRNLTYPE,
1181590Srgrimes	"undefine",     UNDFTYPE,
1191590Srgrimes	"undivert",     UNDVTYPE,
1201590Srgrimes	"divnum",       DIVNTYPE,
1211590Srgrimes	"maketemp",     MKTMTYPE,
1221590Srgrimes	"errprint",     ERRPTYPE,
1231590Srgrimes	"m4wrap",       M4WRTYPE,
1241590Srgrimes	"m4exit",       EXITTYPE,
1251590Srgrimes	"syscmd",       SYSCTYPE,
1261590Srgrimes	"sysval",       SYSVTYPE,
1271590Srgrimes
1281590Srgrimes#ifdef unix
1291590Srgrimes	"unix",         MACRTYPE,
1301590Srgrimes#else
1311590Srgrimes#ifdef vms
1321590Srgrimes	"vms",          MACRTYPE,
1331590Srgrimes#endif
1341590Srgrimes#endif
1351590Srgrimes};
1361590Srgrimes
1371590Srgrimes#define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
1381590Srgrimes
1391590Srgrimesextern int optind;
1401590Srgrimesextern char *optarg;
1411590Srgrimes
1421590Srgrimesvoid macro();
1431590Srgrimesvoid initkwds();
1441590Srgrimesextern int getopt();
1451590Srgrimes
1461590Srgrimesint
1471590Srgrimesmain(argc,argv)
1481590Srgrimes	int argc;
1491590Srgrimes	char *argv[];
1501590Srgrimes{
1511590Srgrimes	register int c;
1521590Srgrimes	register int n;
1531590Srgrimes	char *p;
1541590Srgrimes	register FILE *ifp;
1551590Srgrimes
1561590Srgrimes	progname = basename(argv[0]);
1571590Srgrimes
1581590Srgrimes	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1591590Srgrimes		signal(SIGINT, onintr);
1601590Srgrimes
1611590Srgrimes	initkwds();
1621590Srgrimes
16324360Simp	while ((c = getopt(argc, argv, "tD:U:o:")) != -1)
1641590Srgrimes		switch(c) {
1651590Srgrimes
1661590Srgrimes		case 'D':               /* define something..*/
1671590Srgrimes			for (p = optarg; *p; p++)
1681590Srgrimes				if (*p == '=')
1691590Srgrimes					break;
1701590Srgrimes			if (*p)
1711590Srgrimes				*p++ = EOS;
1721590Srgrimes			dodefine(optarg, p);
1731590Srgrimes			break;
1741590Srgrimes		case 'U':               /* undefine...       */
1751590Srgrimes			remhash(optarg, TOP);
1761590Srgrimes			break;
1771590Srgrimes		case 'o':		/* specific output   */
1781590Srgrimes		case '?':
1791590Srgrimes			usage();
1801590Srgrimes		}
1811590Srgrimes
1821590Srgrimes        argc -= optind;
1831590Srgrimes        argv += optind;
1841590Srgrimes
1851590Srgrimes	active = stdout;		/* default active output     */
1861590Srgrimes					/* filename for diversions   */
1871590Srgrimes	m4temp = mktemp(xstrdup(_PATH_DIVNAME));
1881590Srgrimes
1891590Srgrimes	bbase[0] = bufbase;
1901590Srgrimes        if (!argc) {
1911590Srgrimes 		sp = -1;		/* stack pointer initialized */
1921590Srgrimes		fp = 0; 		/* frame pointer initialized */
1931590Srgrimes		infile[0] = stdin;	/* default input (naturally) */
1941590Srgrimes		macro();
1951590Srgrimes	} else
1961590Srgrimes		for (; argc--; ++argv) {
1971590Srgrimes			p = *argv;
1981590Srgrimes			if (p[0] == '-' && p[1] == '\0')
1991590Srgrimes				ifp = stdin;
2001590Srgrimes			else if ((ifp = fopen(p, "r")) == NULL)
2011590Srgrimes				oops("%s: %s", p, strerror(errno));
2021590Srgrimes			sp = -1;
2038874Srgrimes			fp = 0;
2041590Srgrimes			infile[0] = ifp;
2051590Srgrimes			macro();
2061590Srgrimes			if (ifp != stdin)
2071590Srgrimes				(void)fclose(ifp);
2081590Srgrimes		}
2091590Srgrimes
2101590Srgrimes	if (*m4wraps) { 		/* anything for rundown ??   */
2111590Srgrimes		ilevel = 0;		/* in case m4wrap includes.. */
2121590Srgrimes		bufbase = bp = buf;	/* use the entire buffer   */
2131590Srgrimes		putback(EOF);		/* eof is a must !!	     */
2141590Srgrimes		pbstr(m4wraps); 	/* user-defined wrapup act   */
2151590Srgrimes		macro();		/* last will and testament   */
2161590Srgrimes	}
2171590Srgrimes
2181590Srgrimes	if (active != stdout)
2191590Srgrimes		active = stdout;	/* reset output just in case */
2201590Srgrimes	for (n = 1; n < MAXOUT; n++)	/* default wrap-up: undivert */
2211590Srgrimes		if (outfile[n] != NULL)
2221590Srgrimes			getdiv(n);
2231590Srgrimes					/* remove bitbucket if used  */
2241590Srgrimes	if (outfile[0] != NULL) {
2251590Srgrimes		(void) fclose(outfile[0]);
2261590Srgrimes		m4temp[UNIQUE] = '0';
2271590Srgrimes#ifdef vms
2281590Srgrimes		(void) remove(m4temp);
2291590Srgrimes#else
2301590Srgrimes		(void) unlink(m4temp);
2311590Srgrimes#endif
2321590Srgrimes	}
2331590Srgrimes
2341590Srgrimes	return 0;
2351590Srgrimes}
2361590Srgrimes
2371590Srgrimesndptr inspect();
2381590Srgrimes
2391590Srgrimes/*
2401590Srgrimes * macro - the work horse..
2411590Srgrimes */
2421590Srgrimesvoid
2431590Srgrimesmacro() {
2441590Srgrimes	char token[MAXTOK];
2451590Srgrimes	register char *s;
2461590Srgrimes	register int t, l;
2471590Srgrimes	register ndptr p;
2481590Srgrimes	register int  nlpar;
2491590Srgrimes
2501590Srgrimes	cycle {
2515165Sache		if ((t = gpbc()) == '_' || (t != EOF && isalpha(t))) {
2521590Srgrimes			putback(t);
2531590Srgrimes			if ((p = inspect(s = token)) == nil) {
2541590Srgrimes				if (sp < 0)
2551590Srgrimes					while (*s)
2561590Srgrimes						putc(*s++, active);
2571590Srgrimes				else
2581590Srgrimes					while (*s)
2591590Srgrimes						chrsave(*s++);
2601590Srgrimes			}
2611590Srgrimes			else {
2621590Srgrimes		/*
2631590Srgrimes		 * real thing.. First build a call frame:
2641590Srgrimes		 */
2651590Srgrimes				pushf(fp);	/* previous call frm */
2661590Srgrimes				pushf(p->type); /* type of the call  */
2671590Srgrimes				pushf(0);	/* parenthesis level */
2681590Srgrimes				fp = sp;	/* new frame pointer */
2691590Srgrimes		/*
2701590Srgrimes		 * now push the string arguments:
2711590Srgrimes		 */
2721590Srgrimes				pushs(p->defn);	      /* defn string */
2731590Srgrimes				pushs(p->name);	      /* macro name  */
2741590Srgrimes				pushs(ep);	      /* start next..*/
2751590Srgrimes
2761590Srgrimes				putback(l = gpbc());
2771590Srgrimes				if (l != LPAREN)  {   /* add bracks  */
2781590Srgrimes					putback(RPAREN);
2791590Srgrimes					putback(LPAREN);
2801590Srgrimes				}
2811590Srgrimes			}
2821590Srgrimes		}
2831590Srgrimes		else if (t == EOF) {
2841590Srgrimes			if (sp > -1)
2851590Srgrimes				oops("unexpected end of input", "");
2861590Srgrimes			if (ilevel <= 0)
2871590Srgrimes				break;			/* all done thanks.. */
2881590Srgrimes			--ilevel;
2891590Srgrimes			(void) fclose(infile[ilevel+1]);
2901590Srgrimes			bufbase = bbase[ilevel];
2911590Srgrimes			continue;
2921590Srgrimes		}
2931590Srgrimes	/*
2941590Srgrimes	 * non-alpha single-char token seen..
2951590Srgrimes	 * [the order of else if .. stmts is important.]
2961590Srgrimes	 */
2971590Srgrimes		else if (t == lquote) { 		/* strip quotes */
2981590Srgrimes			nlpar = 1;
2991590Srgrimes			do {
3001590Srgrimes				if ((l = gpbc()) == rquote)
3011590Srgrimes					nlpar--;
3021590Srgrimes				else if (l == lquote)
3031590Srgrimes					nlpar++;
3041590Srgrimes				else if (l == EOF)
3051590Srgrimes					oops("missing right quote", "");
3061590Srgrimes				if (nlpar > 0) {
3071590Srgrimes					if (sp < 0)
3081590Srgrimes						putc(l, active);
3091590Srgrimes					else
3101590Srgrimes						chrsave(l);
3111590Srgrimes				}
3121590Srgrimes			}
3131590Srgrimes			while (nlpar != 0);
3141590Srgrimes		}
3151590Srgrimes
3161590Srgrimes		else if (sp < 0) {		/* not in a macro at all */
3171590Srgrimes			if (t == scommt) {	/* comment handling here */
3181590Srgrimes				putc(t, active);
3191590Srgrimes				while ((t = gpbc()) != ecommt)
3201590Srgrimes					putc(t, active);
3211590Srgrimes			}
3221590Srgrimes			putc(t, active);	/* output directly..	 */
3231590Srgrimes		}
3241590Srgrimes
3251590Srgrimes		else switch(t) {
3261590Srgrimes
3271590Srgrimes		case LPAREN:
3281590Srgrimes			if (PARLEV > 0)
3291590Srgrimes				chrsave(t);
3305165Sache			while ((l = gpbc()) != EOF && isspace(l))
3311590Srgrimes				;		/* skip blank, tab, nl.. */
3321590Srgrimes			putback(l);
3331590Srgrimes			PARLEV++;
3341590Srgrimes			break;
3351590Srgrimes
3361590Srgrimes		case RPAREN:
3371590Srgrimes			if (--PARLEV > 0)
3381590Srgrimes				chrsave(t);
3391590Srgrimes			else {			/* end of argument list */
3401590Srgrimes				chrsave(EOS);
3411590Srgrimes
3421590Srgrimes				if (sp == STACKMAX)
3431590Srgrimes					oops("internal stack overflow", "");
3441590Srgrimes
3451590Srgrimes				if (CALTYP == MACRTYPE)
3461590Srgrimes					expand((char **) mstack+fp+1, sp-fp);
3471590Srgrimes				else
3481590Srgrimes					eval((char **) mstack+fp+1, sp-fp, CALTYP);
3491590Srgrimes
3501590Srgrimes				ep = PREVEP;	/* flush strspace */
3511590Srgrimes				sp = PREVSP;	/* previous sp..  */
3521590Srgrimes				fp = PREVFP;	/* rewind stack...*/
3531590Srgrimes			}
3541590Srgrimes			break;
3551590Srgrimes
3561590Srgrimes		case COMMA:
3571590Srgrimes			if (PARLEV == 1) {
3581590Srgrimes				chrsave(EOS);		/* new argument   */
3595165Sache				while ((l = gpbc()) != EOF && isspace(l))
3601590Srgrimes					;
3611590Srgrimes				putback(l);
3621590Srgrimes				pushs(ep);
3631590Srgrimes			} else
3641590Srgrimes				chrsave(t);
3651590Srgrimes			break;
3661590Srgrimes
3671590Srgrimes		default:
3681590Srgrimes			chrsave(t);			/* stack the char */
3691590Srgrimes			break;
3701590Srgrimes		}
3711590Srgrimes	}
3721590Srgrimes}
3731590Srgrimes
3741590Srgrimes/*
3751590Srgrimes * build an input token..
3761590Srgrimes * consider only those starting with _ or A-Za-z. This is a
3771590Srgrimes * combo with lookup to speed things up.
3781590Srgrimes */
3791590Srgrimesndptr
3808874Srgrimesinspect(tp)
3811590Srgrimesregister char *tp;
3821590Srgrimes{
3835165Sache	register int c;
3841590Srgrimes	register char *name = tp;
3851590Srgrimes	register char *etp = tp+MAXTOK;
3861590Srgrimes	register ndptr p;
3871590Srgrimes	register unsigned long h = 0;
3881590Srgrimes
3895165Sache	while ((c = gpbc()) != EOF && (isalnum(c) || c == '_') && tp < etp)
3901590Srgrimes		h = (h << 5) + h + (*tp++ = c);
3911590Srgrimes	putback(c);
3921590Srgrimes	if (tp == etp)
3931590Srgrimes		oops("token too long", "");
3941590Srgrimes
3951590Srgrimes	*tp = EOS;
3961590Srgrimes
3971590Srgrimes	for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
3981590Srgrimes		if (STREQ(name, p->name))
3991590Srgrimes			break;
4001590Srgrimes	return p;
4011590Srgrimes}
4021590Srgrimes
4031590Srgrimes/*
4048874Srgrimes * initkwds - initialise m4 keywords as fast as possible.
4051590Srgrimes * This very similar to install, but without certain overheads,
4068874Srgrimes * such as calling lookup. Malloc is not used for storing the
4071590Srgrimes * keyword strings, since we simply use the static  pointers
4081590Srgrimes * within keywrds block.
4091590Srgrimes */
4101590Srgrimesvoid
4111590Srgrimesinitkwds() {
4121590Srgrimes	register int i;
4131590Srgrimes	register int h;
4141590Srgrimes	register ndptr p;
4151590Srgrimes
4161590Srgrimes	for (i = 0; i < MAXKEYS; i++) {
4171590Srgrimes		h = hash(keywrds[i].knam);
4181590Srgrimes		p = (ndptr) xalloc(sizeof(struct ndblock));
4191590Srgrimes		p->nxtptr = hashtab[h];
4201590Srgrimes		hashtab[h] = p;
4211590Srgrimes		p->name = keywrds[i].knam;
4221590Srgrimes		p->defn = null;
4231590Srgrimes		p->type = keywrds[i].ktyp | STATIC;
4241590Srgrimes	}
4251590Srgrimes}
426