main.c revision 94957
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 94957 2002-04-17 17:26:32Z jmallett $";
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>
6280289Sobrien#include <stdlib.h>
631590Srgrimes#include <string.h>
6427625Scharnier#include <unistd.h>
651590Srgrimes#include "mdef.h"
661590Srgrimes#include "stdd.h"
671590Srgrimes#include "extern.h"
681590Srgrimes#include "pathnames.h"
691590Srgrimes
701590Srgrimesndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
715165Sacheunsigned char buf[BUFSIZE];              /* push-back buffer            */
725165Sacheunsigned char *bufbase = buf;            /* the base for current ilevel */
735165Sacheunsigned char *bbase[MAXINP];            /* the base for each ilevel    */
745165Sacheunsigned char *bp = buf;                 /* first available character   */
755165Sacheunsigned char *endpbb = buf+BUFSIZE;     /* end of push-back buffer     */
761590Srgrimesstae mstack[STACKMAX+1]; 	/* stack of m4 machine         */
771590Srgrimeschar strspace[STRSPMAX+1];	/* string space for evaluation */
781590Srgrimeschar *ep = strspace;		/* first free char in strspace */
791590Srgrimeschar *endest= strspace+STRSPMAX;/* end of string space	       */
801590Srgrimesint sp; 			/* current m4  stack pointer   */
811590Srgrimesint fp; 			/* m4 call frame pointer       */
821590SrgrimesFILE *infile[MAXINP];		/* input file stack (0=stdin)  */
8394957Sjmallettchar *inname[MAXINP];		/* names of these input files  */
8494957Sjmallettint inlineno[MAXINP];		/* current number in each input*/
851590SrgrimesFILE *outfile[MAXOUT];		/* diversion array(0=bitbucket)*/
861590SrgrimesFILE *active;			/* active output file pointer  */
871590Srgrimeschar *m4temp;			/* filename for diversions     */
8869030Skrischar *m4dir;			/* directory for diversions    */
891590Srgrimesint ilevel = 0; 		/* input file stack pointer    */
901590Srgrimesint oindex = 0; 		/* diversion index..	       */
911590Srgrimeschar *null = "";                /* as it says.. just a null..  */
921590Srgrimeschar *m4wraps = "";             /* m4wrap string default..     */
931590Srgrimeschar lquote = LQUOTE;		/* left quote character  (`)   */
941590Srgrimeschar rquote = RQUOTE;		/* right quote character (')   */
951590Srgrimeschar scommt = SCOMMT;		/* start character for comment */
961590Srgrimeschar ecommt = ECOMMT;		/* end character for comment   */
9794957Sjmallettint synccpp;			/* Line synchronisation for C preprocessor */
9894957Sjmallettint chscratch;			/* Scratch space for gpbc() macro */
991590Srgrimes
1001590Srgrimesstruct keyblk keywrds[] = {	/* m4 keywords to be installed */
1011590Srgrimes	"include",      INCLTYPE,
1021590Srgrimes	"sinclude",     SINCTYPE,
1031590Srgrimes	"define",       DEFITYPE,
1041590Srgrimes	"defn",         DEFNTYPE,
1051590Srgrimes	"divert",       DIVRTYPE,
1061590Srgrimes	"expr",         EXPRTYPE,
1071590Srgrimes	"eval",         EXPRTYPE,
1081590Srgrimes	"substr",       SUBSTYPE,
1091590Srgrimes	"ifelse",       IFELTYPE,
1101590Srgrimes	"ifdef",        IFDFTYPE,
1111590Srgrimes	"len",          LENGTYPE,
1121590Srgrimes	"incr",         INCRTYPE,
1131590Srgrimes	"decr",         DECRTYPE,
1141590Srgrimes	"dnl",          DNLNTYPE,
1151590Srgrimes	"changequote",  CHNQTYPE,
1161590Srgrimes	"changecom",    CHNCTYPE,
1171590Srgrimes	"index",        INDXTYPE,
1181590Srgrimes#ifdef EXTENDED
1191590Srgrimes	"paste",        PASTTYPE,
1201590Srgrimes	"spaste",       SPASTYPE,
1211590Srgrimes#endif
1221590Srgrimes	"popdef",       POPDTYPE,
1231590Srgrimes	"pushdef",      PUSDTYPE,
1241590Srgrimes	"dumpdef",      DUMPTYPE,
1251590Srgrimes	"shift",        SHIFTYPE,
1261590Srgrimes	"translit",     TRNLTYPE,
1271590Srgrimes	"undefine",     UNDFTYPE,
1281590Srgrimes	"undivert",     UNDVTYPE,
1291590Srgrimes	"divnum",       DIVNTYPE,
1301590Srgrimes	"maketemp",     MKTMTYPE,
1311590Srgrimes	"errprint",     ERRPTYPE,
1321590Srgrimes	"m4wrap",       M4WRTYPE,
1331590Srgrimes	"m4exit",       EXITTYPE,
1341590Srgrimes	"syscmd",       SYSCTYPE,
1351590Srgrimes	"sysval",       SYSVTYPE,
1361590Srgrimes
1371590Srgrimes#ifdef unix
1381590Srgrimes	"unix",         MACRTYPE,
1391590Srgrimes#else
1401590Srgrimes#ifdef vms
1411590Srgrimes	"vms",          MACRTYPE,
1421590Srgrimes#endif
1431590Srgrimes#endif
1441590Srgrimes};
1451590Srgrimes
1461590Srgrimes#define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
1471590Srgrimes
1481590Srgrimesvoid macro();
1491590Srgrimesvoid initkwds();
1501590Srgrimes
1511590Srgrimesint
1521590Srgrimesmain(argc,argv)
1531590Srgrimes	int argc;
1541590Srgrimes	char *argv[];
1551590Srgrimes{
1561590Srgrimes	register int c;
1571590Srgrimes	register int n;
1581590Srgrimes	char *p;
1591590Srgrimes	register FILE *ifp;
1601590Srgrimes
1611590Srgrimes	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1621590Srgrimes		signal(SIGINT, onintr);
1631590Srgrimes
1641590Srgrimes	initkwds();
1651590Srgrimes
16694957Sjmallett	while ((c = getopt(argc, argv, "D:U:s")) != -1)
1671590Srgrimes		switch(c) {
1681590Srgrimes
1691590Srgrimes		case 'D':               /* define something..*/
1701590Srgrimes			for (p = optarg; *p; p++)
1711590Srgrimes				if (*p == '=')
1721590Srgrimes					break;
1731590Srgrimes			if (*p)
1741590Srgrimes				*p++ = EOS;
1751590Srgrimes			dodefine(optarg, p);
1761590Srgrimes			break;
1771590Srgrimes		case 'U':               /* undefine...       */
1781590Srgrimes			remhash(optarg, TOP);
1791590Srgrimes			break;
18094957Sjmallett		case 's':
18194957Sjmallett			synccpp = 1;
18294957Sjmallett			break;
1831590Srgrimes		case '?':
1841590Srgrimes			usage();
1851590Srgrimes		}
1861590Srgrimes
1871590Srgrimes        argc -= optind;
1881590Srgrimes        argv += optind;
1891590Srgrimes
1901590Srgrimes	active = stdout;		/* default active output     */
19180289Sobrien	if ((p = strdup(_PATH_DIVDIRNAME)) == NULL)
19280289Sobrien		err(1, "strdup");
19380289Sobrien
1941590Srgrimes					/* filename for diversions   */
19580289Sobrien	m4dir = mkdtemp(p);
19675551Sgshapiro	err_set_exit(cleanup);
19769030Skris	(void) asprintf(&m4temp, "%s/%s", m4dir, _PATH_DIVNAME);
1981590Srgrimes
1991590Srgrimes	bbase[0] = bufbase;
2001590Srgrimes        if (!argc) {
2011590Srgrimes 		sp = -1;		/* stack pointer initialized */
2021590Srgrimes		fp = 0; 		/* frame pointer initialized */
2031590Srgrimes		infile[0] = stdin;	/* default input (naturally) */
20494957Sjmallett		if ((inname[0] = strdup("-")) == NULL)
20594957Sjmallett			err(1, NULL);
20694957Sjmallett		inlineno[0] = 1;
20794957Sjmallett		emitline();
2081590Srgrimes		macro();
2091590Srgrimes	} else
2101590Srgrimes		for (; argc--; ++argv) {
2111590Srgrimes			p = *argv;
2121590Srgrimes			if (p[0] == '-' && p[1] == '\0')
2131590Srgrimes				ifp = stdin;
2141590Srgrimes			else if ((ifp = fopen(p, "r")) == NULL)
21527625Scharnier				err(1, "%s", p);
2161590Srgrimes			sp = -1;
2178874Srgrimes			fp = 0;
2181590Srgrimes			infile[0] = ifp;
21994957Sjmallett			if ((inname[0] = strdup(p)) == NULL)
22094957Sjmallett				err(1, NULL);
22194957Sjmallett			inlineno[0] = 1;
22294957Sjmallett			emitline();
2231590Srgrimes			macro();
2241590Srgrimes			if (ifp != stdin)
2251590Srgrimes				(void)fclose(ifp);
2261590Srgrimes		}
2271590Srgrimes
2281590Srgrimes	if (*m4wraps) { 		/* anything for rundown ??   */
2291590Srgrimes		ilevel = 0;		/* in case m4wrap includes.. */
2301590Srgrimes		bufbase = bp = buf;	/* use the entire buffer   */
2311590Srgrimes		putback(EOF);		/* eof is a must !!	     */
2321590Srgrimes		pbstr(m4wraps); 	/* user-defined wrapup act   */
2331590Srgrimes		macro();		/* last will and testament   */
2341590Srgrimes	}
2351590Srgrimes
2361590Srgrimes	if (active != stdout)
2371590Srgrimes		active = stdout;	/* reset output just in case */
2381590Srgrimes	for (n = 1; n < MAXOUT; n++)	/* default wrap-up: undivert */
2391590Srgrimes		if (outfile[n] != NULL)
2401590Srgrimes			getdiv(n);
2411590Srgrimes					/* remove bitbucket if used  */
24275551Sgshapiro	cleanup(0);
2431590Srgrimes	return 0;
2441590Srgrimes}
2451590Srgrimes
2461590Srgrimesndptr inspect();
2471590Srgrimes
2481590Srgrimes/*
2491590Srgrimes * macro - the work horse..
2501590Srgrimes */
2511590Srgrimesvoid
2521590Srgrimesmacro() {
2531590Srgrimes	char token[MAXTOK];
2541590Srgrimes	register char *s;
2551590Srgrimes	register int t, l;
2561590Srgrimes	register ndptr p;
2571590Srgrimes	register int  nlpar;
2581590Srgrimes
2591590Srgrimes	cycle {
2605165Sache		if ((t = gpbc()) == '_' || (t != EOF && isalpha(t))) {
2611590Srgrimes			putback(t);
2621590Srgrimes			if ((p = inspect(s = token)) == nil) {
2631590Srgrimes				if (sp < 0)
2641590Srgrimes					while (*s)
2651590Srgrimes						putc(*s++, active);
2661590Srgrimes				else
2671590Srgrimes					while (*s)
2681590Srgrimes						chrsave(*s++);
2691590Srgrimes			}
2701590Srgrimes			else {
2711590Srgrimes		/*
2721590Srgrimes		 * real thing.. First build a call frame:
2731590Srgrimes		 */
2741590Srgrimes				pushf(fp);	/* previous call frm */
2751590Srgrimes				pushf(p->type); /* type of the call  */
2761590Srgrimes				pushf(0);	/* parenthesis level */
2771590Srgrimes				fp = sp;	/* new frame pointer */
2781590Srgrimes		/*
2791590Srgrimes		 * now push the string arguments:
2801590Srgrimes		 */
2811590Srgrimes				pushs(p->defn);	      /* defn string */
2821590Srgrimes				pushs(p->name);	      /* macro name  */
2831590Srgrimes				pushs(ep);	      /* start next..*/
2841590Srgrimes
2851590Srgrimes				putback(l = gpbc());
2861590Srgrimes				if (l != LPAREN)  {   /* add bracks  */
2871590Srgrimes					putback(RPAREN);
2881590Srgrimes					putback(LPAREN);
2891590Srgrimes				}
2901590Srgrimes			}
2911590Srgrimes		}
2921590Srgrimes		else if (t == EOF) {
2931590Srgrimes			if (sp > -1)
29427625Scharnier				errx(1, "unexpected end of input");
2951590Srgrimes			if (ilevel <= 0)
2961590Srgrimes				break;			/* all done thanks.. */
2971590Srgrimes			--ilevel;
2981590Srgrimes			(void) fclose(infile[ilevel+1]);
29994957Sjmallett			free(inname[ilevel+1]);
3001590Srgrimes			bufbase = bbase[ilevel];
30194957Sjmallett			emitline();
3021590Srgrimes			continue;
3031590Srgrimes		}
3041590Srgrimes	/*
3051590Srgrimes	 * non-alpha single-char token seen..
3061590Srgrimes	 * [the order of else if .. stmts is important.]
3071590Srgrimes	 */
3081590Srgrimes		else if (t == lquote) { 		/* strip quotes */
3091590Srgrimes			nlpar = 1;
3101590Srgrimes			do {
3111590Srgrimes				if ((l = gpbc()) == rquote)
3121590Srgrimes					nlpar--;
3131590Srgrimes				else if (l == lquote)
3141590Srgrimes					nlpar++;
3151590Srgrimes				else if (l == EOF)
31627625Scharnier					errx(1, "missing right quote");
3171590Srgrimes				if (nlpar > 0) {
3181590Srgrimes					if (sp < 0)
3191590Srgrimes						putc(l, active);
3201590Srgrimes					else
3211590Srgrimes						chrsave(l);
3221590Srgrimes				}
3231590Srgrimes			}
3241590Srgrimes			while (nlpar != 0);
3251590Srgrimes		}
3261590Srgrimes
3271590Srgrimes		else if (sp < 0) {		/* not in a macro at all */
3281590Srgrimes			if (t == scommt) {	/* comment handling here */
3291590Srgrimes				putc(t, active);
3301590Srgrimes				while ((t = gpbc()) != ecommt)
3311590Srgrimes					putc(t, active);
3321590Srgrimes			}
3331590Srgrimes			putc(t, active);	/* output directly..	 */
3341590Srgrimes		}
3351590Srgrimes
3361590Srgrimes		else switch(t) {
3371590Srgrimes
3381590Srgrimes		case LPAREN:
3391590Srgrimes			if (PARLEV > 0)
3401590Srgrimes				chrsave(t);
3415165Sache			while ((l = gpbc()) != EOF && isspace(l))
3421590Srgrimes				;		/* skip blank, tab, nl.. */
3431590Srgrimes			putback(l);
3441590Srgrimes			PARLEV++;
3451590Srgrimes			break;
3461590Srgrimes
3471590Srgrimes		case RPAREN:
3481590Srgrimes			if (--PARLEV > 0)
3491590Srgrimes				chrsave(t);
3501590Srgrimes			else {			/* end of argument list */
3511590Srgrimes				chrsave(EOS);
3521590Srgrimes
3531590Srgrimes				if (sp == STACKMAX)
35427625Scharnier					errx(1, "internal stack overflow");
3551590Srgrimes
3561590Srgrimes				if (CALTYP == MACRTYPE)
3571590Srgrimes					expand((char **) mstack+fp+1, sp-fp);
3581590Srgrimes				else
3591590Srgrimes					eval((char **) mstack+fp+1, sp-fp, CALTYP);
3601590Srgrimes
3611590Srgrimes				ep = PREVEP;	/* flush strspace */
3621590Srgrimes				sp = PREVSP;	/* previous sp..  */
3631590Srgrimes				fp = PREVFP;	/* rewind stack...*/
3641590Srgrimes			}
3651590Srgrimes			break;
3661590Srgrimes
3671590Srgrimes		case COMMA:
3681590Srgrimes			if (PARLEV == 1) {
3691590Srgrimes				chrsave(EOS);		/* new argument   */
3705165Sache				while ((l = gpbc()) != EOF && isspace(l))
3711590Srgrimes					;
3721590Srgrimes				putback(l);
3731590Srgrimes				pushs(ep);
3741590Srgrimes			} else
3751590Srgrimes				chrsave(t);
3761590Srgrimes			break;
3771590Srgrimes
3781590Srgrimes		default:
3791590Srgrimes			chrsave(t);			/* stack the char */
3801590Srgrimes			break;
3811590Srgrimes		}
3821590Srgrimes	}
3831590Srgrimes}
3841590Srgrimes
3851590Srgrimes/*
3861590Srgrimes * build an input token..
3871590Srgrimes * consider only those starting with _ or A-Za-z. This is a
3881590Srgrimes * combo with lookup to speed things up.
3891590Srgrimes */
3901590Srgrimesndptr
3918874Srgrimesinspect(tp)
3921590Srgrimesregister char *tp;
3931590Srgrimes{
3945165Sache	register int c;
3951590Srgrimes	register char *name = tp;
3961590Srgrimes	register char *etp = tp+MAXTOK;
3971590Srgrimes	register ndptr p;
3981590Srgrimes	register unsigned long h = 0;
3991590Srgrimes
4005165Sache	while ((c = gpbc()) != EOF && (isalnum(c) || c == '_') && tp < etp)
4011590Srgrimes		h = (h << 5) + h + (*tp++ = c);
4021590Srgrimes	putback(c);
4031590Srgrimes	if (tp == etp)
40427625Scharnier		errx(1, "token too long");
4051590Srgrimes
4061590Srgrimes	*tp = EOS;
4071590Srgrimes
4081590Srgrimes	for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
4091590Srgrimes		if (STREQ(name, p->name))
4101590Srgrimes			break;
4111590Srgrimes	return p;
4121590Srgrimes}
4131590Srgrimes
4141590Srgrimes/*
4158874Srgrimes * initkwds - initialise m4 keywords as fast as possible.
4161590Srgrimes * This very similar to install, but without certain overheads,
4178874Srgrimes * such as calling lookup. Malloc is not used for storing the
4181590Srgrimes * keyword strings, since we simply use the static  pointers
4191590Srgrimes * within keywrds block.
4201590Srgrimes */
4211590Srgrimesvoid
4221590Srgrimesinitkwds() {
4231590Srgrimes	register int i;
4241590Srgrimes	register int h;
4251590Srgrimes	register ndptr p;
4261590Srgrimes
4271590Srgrimes	for (i = 0; i < MAXKEYS; i++) {
4281590Srgrimes		h = hash(keywrds[i].knam);
42980289Sobrien		if ((p = malloc(sizeof(struct ndblock))) == NULL)
43080289Sobrien			err(1, "malloc");
4311590Srgrimes		p->nxtptr = hashtab[h];
4321590Srgrimes		hashtab[h] = p;
4331590Srgrimes		p->name = keywrds[i].knam;
4341590Srgrimes		p->defn = null;
4351590Srgrimes		p->type = keywrds[i].ktyp | STATIC;
4361590Srgrimes	}
4371590Srgrimes}
43894957Sjmallett
43994957Sjmallett/* Emit preprocessor #line directive if -s option used. */
44094957Sjmallettvoid
44194957Sjmallettemitline(void)
44294957Sjmallett{
44394957Sjmallett	if (synccpp)
44494957Sjmallett		fprintf(active, "#line %d \"%s\"\n", inlineno[ilevel],
44594957Sjmallett		    inname[ilevel]);
44694957Sjmallett}
447