main.c revision 69030
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
3827625Scharnierstatic const 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
4427625Scharnier#if 0
451590Srgrimesstatic char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
4627625Scharnier#endif
4727625Scharnierstatic const char rcsid[] =
4850477Speter  "$FreeBSD: head/usr.bin/m4/main.c 69030 2000-11-22 11:09:30Z kris $";
491590Srgrimes#endif /* not lint */
501590Srgrimes
511590Srgrimes/*
521590Srgrimes * main.c
531590Srgrimes * Facility: m4 macro processor
541590Srgrimes * by: oz
551590Srgrimes */
561590Srgrimes
571590Srgrimes#include <sys/types.h>
5827625Scharnier#include <ctype.h>
5927625Scharnier#include <err.h>
601590Srgrimes#include <signal.h>
611590Srgrimes#include <stdio.h>
621590Srgrimes#include <string.h>
6327625Scharnier#include <unistd.h>
641590Srgrimes#include "mdef.h"
651590Srgrimes#include "stdd.h"
661590Srgrimes#include "extern.h"
671590Srgrimes#include "pathnames.h"
681590Srgrimes
691590Srgrimesndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
705165Sacheunsigned char buf[BUFSIZE];              /* push-back buffer            */
715165Sacheunsigned char *bufbase = buf;            /* the base for current ilevel */
725165Sacheunsigned char *bbase[MAXINP];            /* the base for each ilevel    */
735165Sacheunsigned char *bp = buf;                 /* first available character   */
745165Sacheunsigned char *endpbb = buf+BUFSIZE;     /* end of push-back buffer     */
751590Srgrimesstae mstack[STACKMAX+1]; 	/* stack of m4 machine         */
761590Srgrimeschar strspace[STRSPMAX+1];	/* string space for evaluation */
771590Srgrimeschar *ep = strspace;		/* first free char in strspace */
781590Srgrimeschar *endest= strspace+STRSPMAX;/* end of string space	       */
791590Srgrimesint sp; 			/* current m4  stack pointer   */
801590Srgrimesint fp; 			/* m4 call frame pointer       */
811590SrgrimesFILE *infile[MAXINP];		/* input file stack (0=stdin)  */
821590SrgrimesFILE *outfile[MAXOUT];		/* diversion array(0=bitbucket)*/
831590SrgrimesFILE *active;			/* active output file pointer  */
841590Srgrimeschar *m4temp;			/* filename for diversions     */
8569030Skrischar *m4dir;			/* directory for diversions    */
861590Srgrimesint ilevel = 0; 		/* input file stack pointer    */
871590Srgrimesint oindex = 0; 		/* diversion index..	       */
881590Srgrimeschar *null = "";                /* as it says.. just a null..  */
891590Srgrimeschar *m4wraps = "";             /* m4wrap string default..     */
901590Srgrimeschar lquote = LQUOTE;		/* left quote character  (`)   */
911590Srgrimeschar rquote = RQUOTE;		/* right quote character (')   */
921590Srgrimeschar scommt = SCOMMT;		/* start character for comment */
931590Srgrimeschar ecommt = ECOMMT;		/* end character for comment   */
941590Srgrimes
951590Srgrimesstruct keyblk keywrds[] = {	/* m4 keywords to be installed */
961590Srgrimes	"include",      INCLTYPE,
971590Srgrimes	"sinclude",     SINCTYPE,
981590Srgrimes	"define",       DEFITYPE,
991590Srgrimes	"defn",         DEFNTYPE,
1001590Srgrimes	"divert",       DIVRTYPE,
1011590Srgrimes	"expr",         EXPRTYPE,
1021590Srgrimes	"eval",         EXPRTYPE,
1031590Srgrimes	"substr",       SUBSTYPE,
1041590Srgrimes	"ifelse",       IFELTYPE,
1051590Srgrimes	"ifdef",        IFDFTYPE,
1061590Srgrimes	"len",          LENGTYPE,
1071590Srgrimes	"incr",         INCRTYPE,
1081590Srgrimes	"decr",         DECRTYPE,
1091590Srgrimes	"dnl",          DNLNTYPE,
1101590Srgrimes	"changequote",  CHNQTYPE,
1111590Srgrimes	"changecom",    CHNCTYPE,
1121590Srgrimes	"index",        INDXTYPE,
1131590Srgrimes#ifdef EXTENDED
1141590Srgrimes	"paste",        PASTTYPE,
1151590Srgrimes	"spaste",       SPASTYPE,
1161590Srgrimes#endif
1171590Srgrimes	"popdef",       POPDTYPE,
1181590Srgrimes	"pushdef",      PUSDTYPE,
1191590Srgrimes	"dumpdef",      DUMPTYPE,
1201590Srgrimes	"shift",        SHIFTYPE,
1211590Srgrimes	"translit",     TRNLTYPE,
1221590Srgrimes	"undefine",     UNDFTYPE,
1231590Srgrimes	"undivert",     UNDVTYPE,
1241590Srgrimes	"divnum",       DIVNTYPE,
1251590Srgrimes	"maketemp",     MKTMTYPE,
1261590Srgrimes	"errprint",     ERRPTYPE,
1271590Srgrimes	"m4wrap",       M4WRTYPE,
1281590Srgrimes	"m4exit",       EXITTYPE,
1291590Srgrimes	"syscmd",       SYSCTYPE,
1301590Srgrimes	"sysval",       SYSVTYPE,
1311590Srgrimes
1321590Srgrimes#ifdef unix
1331590Srgrimes	"unix",         MACRTYPE,
1341590Srgrimes#else
1351590Srgrimes#ifdef vms
1361590Srgrimes	"vms",          MACRTYPE,
1371590Srgrimes#endif
1381590Srgrimes#endif
1391590Srgrimes};
1401590Srgrimes
1411590Srgrimes#define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
1421590Srgrimes
1431590Srgrimesvoid macro();
1441590Srgrimesvoid initkwds();
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	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1571590Srgrimes		signal(SIGINT, onintr);
1581590Srgrimes
1591590Srgrimes	initkwds();
1601590Srgrimes
16124360Simp	while ((c = getopt(argc, argv, "tD:U:o:")) != -1)
1621590Srgrimes		switch(c) {
1631590Srgrimes
1641590Srgrimes		case 'D':               /* define something..*/
1651590Srgrimes			for (p = optarg; *p; p++)
1661590Srgrimes				if (*p == '=')
1671590Srgrimes					break;
1681590Srgrimes			if (*p)
1691590Srgrimes				*p++ = EOS;
1701590Srgrimes			dodefine(optarg, p);
1711590Srgrimes			break;
1721590Srgrimes		case 'U':               /* undefine...       */
1731590Srgrimes			remhash(optarg, TOP);
1741590Srgrimes			break;
1751590Srgrimes		case 'o':		/* specific output   */
1761590Srgrimes		case '?':
1771590Srgrimes			usage();
1781590Srgrimes		}
1791590Srgrimes
1801590Srgrimes        argc -= optind;
1811590Srgrimes        argv += optind;
1821590Srgrimes
1831590Srgrimes	active = stdout;		/* default active output     */
1841590Srgrimes					/* filename for diversions   */
18569030Skris	m4dir = mkdtemp(xstrdup(_PATH_DIVDIRNAME));
18669030Skris	(void) asprintf(&m4temp, "%s/%s", m4dir, _PATH_DIVNAME);
1871590Srgrimes
1881590Srgrimes	bbase[0] = bufbase;
1891590Srgrimes        if (!argc) {
1901590Srgrimes 		sp = -1;		/* stack pointer initialized */
1911590Srgrimes		fp = 0; 		/* frame pointer initialized */
1921590Srgrimes		infile[0] = stdin;	/* default input (naturally) */
1931590Srgrimes		macro();
1941590Srgrimes	} else
1951590Srgrimes		for (; argc--; ++argv) {
1961590Srgrimes			p = *argv;
1971590Srgrimes			if (p[0] == '-' && p[1] == '\0')
1981590Srgrimes				ifp = stdin;
1991590Srgrimes			else if ((ifp = fopen(p, "r")) == NULL)
20027625Scharnier				err(1, "%s", p);
2011590Srgrimes			sp = -1;
2028874Srgrimes			fp = 0;
2031590Srgrimes			infile[0] = ifp;
2041590Srgrimes			macro();
2051590Srgrimes			if (ifp != stdin)
2061590Srgrimes				(void)fclose(ifp);
2071590Srgrimes		}
2081590Srgrimes
2091590Srgrimes	if (*m4wraps) { 		/* anything for rundown ??   */
2101590Srgrimes		ilevel = 0;		/* in case m4wrap includes.. */
2111590Srgrimes		bufbase = bp = buf;	/* use the entire buffer   */
2121590Srgrimes		putback(EOF);		/* eof is a must !!	     */
2131590Srgrimes		pbstr(m4wraps); 	/* user-defined wrapup act   */
2141590Srgrimes		macro();		/* last will and testament   */
2151590Srgrimes	}
2161590Srgrimes
2171590Srgrimes	if (active != stdout)
2181590Srgrimes		active = stdout;	/* reset output just in case */
2191590Srgrimes	for (n = 1; n < MAXOUT; n++)	/* default wrap-up: undivert */
2201590Srgrimes		if (outfile[n] != NULL)
2211590Srgrimes			getdiv(n);
2221590Srgrimes					/* remove bitbucket if used  */
2231590Srgrimes	if (outfile[0] != NULL) {
2241590Srgrimes		(void) fclose(outfile[0]);
2251590Srgrimes		m4temp[UNIQUE] = '0';
2261590Srgrimes#ifdef vms
2271590Srgrimes		(void) remove(m4temp);
2281590Srgrimes#else
2291590Srgrimes		(void) unlink(m4temp);
23069030Skris		(void) rmdir(m4dir);
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)
28527625Scharnier				errx(1, "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)
30527625Scharnier					errx(1, "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)
34327625Scharnier					errx(1, "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)
39327625Scharnier		errx(1, "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