138032Speter/*
2261363Sgshapiro * Copyright (c) 1998-2001, 2003, 2006, 2007 Proofpoint, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1464562Sgshapiro#include <sendmail.h>
1538032Speter
16266692SgshapiroSM_RCSID("@(#)$Id: macro.c,v 8.108 2013-11-22 20:51:55 ca Exp $")
1790792Sgshapiro
18168515Sgshapiro#include <sm/sendmail.h>
1971345Sgshapiro#if MAXMACROID != (BITMAPBITS - 1)
2071345Sgshapiro	ERROR Read the comment in conf.h
2171345Sgshapiro#endif /* MAXMACROID != (BITMAPBITS - 1) */
2238032Speter
2390792Sgshapirostatic char	*MacroName[MAXMACROID + 1];	/* macro id to name table */
24173340Sgshapiro
25173340Sgshapiro/*
26173340Sgshapiro**  Codes for long named macros.
27173340Sgshapiro**  See also macname():
28173340Sgshapiro	* if not ASCII printable, look up the name *
29173340Sgshapiro	if (n <= 0x20 || n > 0x7f)
30173340Sgshapiro**  First use 1 to NEXTMACROID_L, then use NEXTMACROID_H to MAXMACROID.
31173340Sgshapiro*/
32173340Sgshapiro
33173340Sgshapiro#define NEXTMACROID_L 037
34173340Sgshapiro#define NEXTMACROID_H 0240
35173340Sgshapiro
36173340Sgshapiro#if _FFR_MORE_MACROS
37173340Sgshapiro/* table for next id in non-printable ASCII range: disallow some value */
38173340Sgshapirostatic int NextMIdTable[] =
39173340Sgshapiro{
40173340Sgshapiro	/*  0  nul */	 1,
41173340Sgshapiro	/*  1  soh */	 2,
42173340Sgshapiro	/*  2  stx */	 3,
43173340Sgshapiro	/*  3  etx */	 4,
44173340Sgshapiro	/*  4  eot */	 5,
45173340Sgshapiro	/*  5  enq */	 6,
46173340Sgshapiro	/*  6  ack */	 7,
47173340Sgshapiro	/*  7  bel */	 8,
48173340Sgshapiro	/*  8  bs  */	14,
49173340Sgshapiro	/*  9  ht  */	-1,
50173340Sgshapiro	/* 10  nl  */	-1,
51173340Sgshapiro	/* 11  vt  */	-1,
52173340Sgshapiro	/* 12  np  */	-1,
53173340Sgshapiro	/* 13  cr  */	-1,
54173340Sgshapiro	/* 14  so  */	15,
55173340Sgshapiro	/* 15  si  */	16,
56173340Sgshapiro	/* 16  dle */	17,
57173340Sgshapiro	/* 17  dc1 */	18,
58173340Sgshapiro	/* 18  dc2 */	19,
59173340Sgshapiro	/* 19  dc3 */	20,
60173340Sgshapiro	/* 20  dc4 */	21,
61173340Sgshapiro	/* 21  nak */	22,
62173340Sgshapiro	/* 22  syn */	23,
63173340Sgshapiro	/* 23  etb */	24,
64173340Sgshapiro	/* 24  can */	25,
65173340Sgshapiro	/* 25  em  */	26,
66173340Sgshapiro	/* 26  sub */	27,
67173340Sgshapiro	/* 27  esc */	28,
68173340Sgshapiro	/* 28  fs  */	29,
69173340Sgshapiro	/* 29  gs  */	30,
70173340Sgshapiro	/* 30  rs  */	31,
71173340Sgshapiro	/* 31  us  */	32,
72173340Sgshapiro	/* 32  sp  */	-1,
73173340Sgshapiro};
74173340Sgshapiro
75173340Sgshapiro#define NEXTMACROID(mid)	(		\
76173340Sgshapiro	(mid < NEXTMACROID_L) ? (NextMIdTable[mid]) :	\
77173340Sgshapiro	((mid < NEXTMACROID_H) ? NEXTMACROID_H : (mid + 1)))
78173340Sgshapiro
79173340Sgshapiroint		NextMacroId = 1;	/* codes for long named macros */
80173340Sgshapiro/* see sendmail.h: Special characters in rewriting rules. */
81173340Sgshapiro#else /* _FFR_MORE_MACROS */
8290792Sgshapiroint		NextMacroId = 0240;	/* codes for long named macros */
83173340Sgshapiro#define NEXTMACROID(mid)	((mid) + 1)
84173340Sgshapiro#endif /* _FFR_MORE_MACROS */
8538032Speter
86168515Sgshapiro
8738032Speter/*
8890792Sgshapiro**  INITMACROS -- initialize the macro system
8990792Sgshapiro**
9090792Sgshapiro**	This just involves defining some macros that are actually
9190792Sgshapiro**	used internally as metasymbols to be themselves.
9290792Sgshapiro**
9390792Sgshapiro**	Parameters:
9490792Sgshapiro**		none.
9590792Sgshapiro**
9690792Sgshapiro**	Returns:
9790792Sgshapiro**		none.
9890792Sgshapiro**
9990792Sgshapiro**	Side Effects:
10090792Sgshapiro**		initializes several macros to be themselves.
10190792Sgshapiro*/
10290792Sgshapiro
10390792Sgshapirostruct metamac	MetaMacros[] =
10490792Sgshapiro{
10590792Sgshapiro	/* LHS pattern matching characters */
10690792Sgshapiro	{ '*', MATCHZANY },	{ '+', MATCHANY },	{ '-', MATCHONE },
10790792Sgshapiro	{ '=', MATCHCLASS },	{ '~', MATCHNCLASS },
10890792Sgshapiro
10990792Sgshapiro	/* these are RHS metasymbols */
11090792Sgshapiro	{ '#', CANONNET },	{ '@', CANONHOST },	{ ':', CANONUSER },
11190792Sgshapiro	{ '>', CALLSUBR },
11290792Sgshapiro
11390792Sgshapiro	/* the conditional operations */
11490792Sgshapiro	{ '?', CONDIF },	{ '|', CONDELSE },	{ '.', CONDFI },
11590792Sgshapiro
11690792Sgshapiro	/* the hostname lookup characters */
11790792Sgshapiro	{ '[', HOSTBEGIN },	{ ']', HOSTEND },
11890792Sgshapiro	{ '(', LOOKUPBEGIN },	{ ')', LOOKUPEND },
11990792Sgshapiro
12090792Sgshapiro	/* miscellaneous control characters */
12190792Sgshapiro	{ '&', MACRODEXPAND },
12290792Sgshapiro
12390792Sgshapiro	{ '\0', '\0' }
12490792Sgshapiro};
12590792Sgshapiro
12690792Sgshapiro#define MACBINDING(name, mid) \
12790792Sgshapiro		stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
12890792Sgshapiro		MacroName[mid] = name;
12990792Sgshapiro
13090792Sgshapirovoid
13190792Sgshapiroinitmacros(e)
132168515Sgshapiro	ENVELOPE *e;
13390792Sgshapiro{
134168515Sgshapiro	struct metamac *m;
135168515Sgshapiro	int c;
13690792Sgshapiro	char buf[5];
13790792Sgshapiro
13890792Sgshapiro	for (m = MetaMacros; m->metaname != '\0'; m++)
13990792Sgshapiro	{
14090792Sgshapiro		buf[0] = m->metaval;
14190792Sgshapiro		buf[1] = '\0';
14290792Sgshapiro		macdefine(&e->e_macro, A_TEMP, m->metaname, buf);
14390792Sgshapiro	}
14490792Sgshapiro	buf[0] = MATCHREPL;
14590792Sgshapiro	buf[2] = '\0';
14690792Sgshapiro	for (c = '0'; c <= '9'; c++)
14790792Sgshapiro	{
14890792Sgshapiro		buf[1] = c;
14990792Sgshapiro		macdefine(&e->e_macro, A_TEMP, c, buf);
15090792Sgshapiro	}
15190792Sgshapiro
15290792Sgshapiro	/* set defaults for some macros sendmail will use later */
15390792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'n', "MAILER-DAEMON");
15490792Sgshapiro
15590792Sgshapiro	/* set up external names for some internal macros */
15690792Sgshapiro	MACBINDING("opMode", MID_OPMODE);
15790792Sgshapiro	/*XXX should probably add equivalents for all short macros here XXX*/
15890792Sgshapiro}
159168515Sgshapiro
16090792Sgshapiro/*
161168515Sgshapiro**  EXPAND/DOEXPAND -- macro expand a string using $x escapes.
16238032Speter**
163168515Sgshapiro**	After expansion, the expansion will be in external form (that is,
164168515Sgshapiro**	there will be no sendmail metacharacters and METAQUOTEs will have
165168515Sgshapiro**	been stripped out).
166168515Sgshapiro**
16738032Speter**	Parameters:
16838032Speter**		s -- the string to expand.
16938032Speter**		buf -- the place to put the expansion.
17038032Speter**		bufsize -- the size of the buffer.
171168515Sgshapiro**		explevel -- the depth of expansion (doexpand only)
17238032Speter**		e -- envelope in which to work.
17338032Speter**
17438032Speter**	Returns:
17538032Speter**		none.
17638032Speter**
17738032Speter**	Side Effects:
17838032Speter**		none.
17938032Speter*/
18038032Speter
181168515Sgshapirostatic void doexpand __P(( char *, char *, size_t, int, ENVELOPE *));
182168515Sgshapiro
183168515Sgshapirostatic void
184168515Sgshapirodoexpand(s, buf, bufsize, explevel, e)
185168515Sgshapiro	char *s;
186168515Sgshapiro	char *buf;
18738032Speter	size_t bufsize;
188168515Sgshapiro	int explevel;
189168515Sgshapiro	ENVELOPE *e;
19038032Speter{
191168515Sgshapiro	char *xp;
192168515Sgshapiro	char *q;
19338032Speter	bool skipping;		/* set if conditionally skipping output */
19490792Sgshapiro	bool recurse;		/* set if recursion required */
19590792Sgshapiro	size_t i;
19638032Speter	int skiplev;		/* skipping nesting level */
19738032Speter	int iflev;		/* if nesting level */
198168515Sgshapiro	bool quotenext;		/* quote the following character */
19938032Speter	char xbuf[MACBUFSIZE];
20038032Speter
20138032Speter	if (tTd(35, 24))
20238032Speter	{
20390792Sgshapiro		sm_dprintf("expand(");
204132943Sgshapiro		xputs(sm_debug_file(), s);
20590792Sgshapiro		sm_dprintf(")\n");
20638032Speter	}
20738032Speter
20890792Sgshapiro	recurse = false;
20990792Sgshapiro	skipping = false;
21038032Speter	skiplev = 0;
21138032Speter	iflev = 0;
212168515Sgshapiro	quotenext = false;
21338032Speter	if (s == NULL)
21438032Speter		s = "";
21538032Speter	for (xp = xbuf; *s != '\0'; s++)
21638032Speter	{
21738032Speter		int c;
21838032Speter
21938032Speter		/*
22038032Speter		**  Check for non-ordinary (special?) character.
22138032Speter		**	'q' will be the interpolated quantity.
22238032Speter		*/
22338032Speter
22438032Speter		q = NULL;
225168515Sgshapiro		c = *s & 0377;
226168515Sgshapiro
227168515Sgshapiro		if (quotenext)
22838032Speter		{
229168515Sgshapiro			quotenext = false;
230168515Sgshapiro			goto simpleinterpolate;
231168515Sgshapiro		}
232168515Sgshapiro
233168515Sgshapiro		switch (c)
234168515Sgshapiro		{
23538032Speter		  case CONDIF:		/* see if var set */
23638032Speter			iflev++;
237168515Sgshapiro			c = *++s & 0377;
23838032Speter			if (skipping)
23938032Speter				skiplev++;
24038032Speter			else
24164562Sgshapiro			{
24264562Sgshapiro				char *mv;
24364562Sgshapiro
24464562Sgshapiro				mv = macvalue(c, e);
24564562Sgshapiro				skipping = (mv == NULL || *mv == '\0');
24664562Sgshapiro			}
24738032Speter			continue;
24838032Speter
24938032Speter		  case CONDELSE:	/* change state of skipping */
25038032Speter			if (iflev == 0)
25190792Sgshapiro				break;	/* XXX: error */
25238032Speter			if (skiplev == 0)
25338032Speter				skipping = !skipping;
25438032Speter			continue;
25538032Speter
25638032Speter		  case CONDFI:		/* stop skipping */
25738032Speter			if (iflev == 0)
25890792Sgshapiro				break;	/* XXX: error */
25938032Speter			iflev--;
26038032Speter			if (skiplev == 0)
26190792Sgshapiro				skipping = false;
26238032Speter			if (skipping)
26338032Speter				skiplev--;
26438032Speter			continue;
26538032Speter
26638032Speter		  case MACROEXPAND:	/* macro interpolation */
26771345Sgshapiro			c = bitidx(*++s);
26838032Speter			if (c != '\0')
26938032Speter				q = macvalue(c, e);
27038032Speter			else
27138032Speter			{
27238032Speter				s--;
27338032Speter				q = NULL;
27438032Speter			}
27538032Speter			if (q == NULL)
27638032Speter				continue;
27738032Speter			break;
278168515Sgshapiro
279168515Sgshapiro		  case METAQUOTE:
280168515Sgshapiro			/* next octet completely quoted */
281168515Sgshapiro			quotenext = true;
282168515Sgshapiro			break;
28338032Speter		}
28438032Speter
28538032Speter		/*
28638032Speter		**  Interpolate q or output one character
28738032Speter		*/
28838032Speter
289168515Sgshapiro  simpleinterpolate:
290168515Sgshapiro		if (skipping || xp >= &xbuf[sizeof(xbuf) - 1])
29138032Speter			continue;
29238032Speter		if (q == NULL)
29338032Speter			*xp++ = c;
29438032Speter		else
29538032Speter		{
29638032Speter			/* copy to end of q or max space remaining in buf */
297168515Sgshapiro			bool hiderecurse = false;
298168515Sgshapiro
299168515Sgshapiro			while ((c = *q++) != '\0' &&
300168515Sgshapiro				xp < &xbuf[sizeof(xbuf) - 1])
30138032Speter			{
30238032Speter				/* check for any sendmail metacharacters */
303168515Sgshapiro				if (!hiderecurse && (c & 0340) == 0200)
30490792Sgshapiro					recurse = true;
30538032Speter				*xp++ = c;
306168515Sgshapiro
307168515Sgshapiro				/* give quoted characters a free ride */
308168515Sgshapiro				hiderecurse = (c & 0377) == METAQUOTE;
30938032Speter			}
31038032Speter		}
31138032Speter	}
31238032Speter	*xp = '\0';
31338032Speter
314168515Sgshapiro	if (tTd(35, 28))
31538032Speter	{
316168515Sgshapiro		sm_dprintf("expand(%d) ==> ", explevel);
317132943Sgshapiro		xputs(sm_debug_file(), xbuf);
31890792Sgshapiro		sm_dprintf("\n");
31938032Speter	}
32038032Speter
32138032Speter	/* recurse as appropriate */
32238032Speter	if (recurse)
32338032Speter	{
32438032Speter		if (explevel < MaxMacroRecursion)
32538032Speter		{
326168515Sgshapiro			doexpand(xbuf, buf, bufsize, explevel + 1, e);
32738032Speter			return;
32838032Speter		}
32938032Speter		syserr("expand: recursion too deep (%d max)",
33038032Speter			MaxMacroRecursion);
33138032Speter	}
33238032Speter
33338032Speter	/* copy results out */
334168515Sgshapiro	if (explevel == 0)
335168515Sgshapiro		(void) sm_strlcpy(buf, xbuf, bufsize);
336168515Sgshapiro	else
337168515Sgshapiro	{
338168515Sgshapiro		/* leave in internal form */
339168515Sgshapiro		i = xp - xbuf;
340168515Sgshapiro		if (i >= bufsize)
341168515Sgshapiro			i = bufsize - 1;
342168515Sgshapiro		memmove(buf, xbuf, i);
343168515Sgshapiro		buf[i] = '\0';
344168515Sgshapiro	}
345168515Sgshapiro
346168515Sgshapiro	if (tTd(35, 24))
347168515Sgshapiro	{
348168515Sgshapiro		sm_dprintf("expand ==> ");
349168515Sgshapiro		xputs(sm_debug_file(), buf);
350168515Sgshapiro		sm_dprintf("\n");
351168515Sgshapiro	}
35238032Speter}
35390792Sgshapiro
354168515Sgshapirovoid
355168515Sgshapiroexpand(s, buf, bufsize, e)
356168515Sgshapiro	char *s;
357168515Sgshapiro	char *buf;
358168515Sgshapiro	size_t bufsize;
359168515Sgshapiro	ENVELOPE *e;
360168515Sgshapiro{
361168515Sgshapiro	doexpand(s, buf, bufsize, 0, e);
362168515Sgshapiro}
363168515Sgshapiro
36490792Sgshapiro/*
36590792Sgshapiro**  MACDEFINE -- bind a macro name to a value
36638032Speter**
36790792Sgshapiro**	Set a macro to a value, with fancy storage management.
36890792Sgshapiro**	macdefine will make a copy of the value, if required,
36990792Sgshapiro**	and will ensure that the storage for the previous value
37090792Sgshapiro**	is not leaked.
37138032Speter**
37238032Speter**	Parameters:
37390792Sgshapiro**		mac -- Macro table.
37490792Sgshapiro**		vclass -- storage class of 'value', ignored if value==NULL.
37590792Sgshapiro**			A_HEAP	means that the value was allocated by
37690792Sgshapiro**				malloc, and that macdefine owns the storage.
37790792Sgshapiro**			A_TEMP	means that value points to temporary storage,
37890792Sgshapiro**				and thus macdefine needs to make a copy.
37990792Sgshapiro**			A_PERM	means that value points to storage that
38090792Sgshapiro**				will remain allocated and unchanged for
38190792Sgshapiro**				at least the lifetime of mac.  Use A_PERM if:
38290792Sgshapiro**				-- value == NULL,
38390792Sgshapiro**				-- value points to a string literal,
38490792Sgshapiro**				-- value was allocated from mac->mac_rpool
38590792Sgshapiro**				   or (in the case of an envelope macro)
38690792Sgshapiro**				   from e->e_rpool,
38790792Sgshapiro**				-- in the case of an envelope macro,
38890792Sgshapiro**				   value is a string member of the envelope
38990792Sgshapiro**				   such as e->e_sender.
39090792Sgshapiro**		id -- Macro id.  This is a single character macro name
39190792Sgshapiro**			such as 'g', or a value returned by macid().
39290792Sgshapiro**		value -- Macro value: either NULL, or a string.
39338032Speter*/
39438032Speter
39538032Spetervoid
39690792Sgshapiro#if SM_HEAP_CHECK
39790792Sgshapiromacdefine_tagged(mac, vclass, id, value, file, line, grp)
39890792Sgshapiro#else /* SM_HEAP_CHECK */
39990792Sgshapiromacdefine(mac, vclass, id, value)
40090792Sgshapiro#endif /* SM_HEAP_CHECK */
40190792Sgshapiro	MACROS_T *mac;
40290792Sgshapiro	ARGCLASS_T vclass;
40390792Sgshapiro	int id;
40490792Sgshapiro	char *value;
40590792Sgshapiro#if SM_HEAP_CHECK
40690792Sgshapiro	char *file;
40790792Sgshapiro	int line;
40890792Sgshapiro	int grp;
40990792Sgshapiro#endif /* SM_HEAP_CHECK */
41038032Speter{
41190792Sgshapiro	char *newvalue;
41264562Sgshapiro
41390792Sgshapiro	if (id < 0 || id > MAXMACROID)
41490792Sgshapiro		return;
41590792Sgshapiro
41638032Speter	if (tTd(35, 9))
41738032Speter	{
41890792Sgshapiro		sm_dprintf("%sdefine(%s as ",
41990792Sgshapiro			mac->mac_table[id] == NULL ? "" : "re", macname(id));
420132943Sgshapiro		xputs(sm_debug_file(), value);
42190792Sgshapiro		sm_dprintf(")\n");
42238032Speter	}
42364562Sgshapiro
42490792Sgshapiro	if (mac->mac_rpool == NULL)
42590792Sgshapiro	{
42690792Sgshapiro		char *freeit = NULL;
42790792Sgshapiro
42890792Sgshapiro		if (mac->mac_table[id] != NULL &&
42990792Sgshapiro		    bitnset(id, mac->mac_allocated))
43090792Sgshapiro			freeit = mac->mac_table[id];
43190792Sgshapiro
43290792Sgshapiro		if (value == NULL || vclass == A_HEAP)
43390792Sgshapiro		{
43490792Sgshapiro			sm_heap_checkptr_tagged(value, file, line);
43590792Sgshapiro			newvalue = value;
43690792Sgshapiro			clrbitn(id, mac->mac_allocated);
43790792Sgshapiro		}
43890792Sgshapiro		else
43990792Sgshapiro		{
440132943Sgshapiro#if SM_HEAP_CHECK
44190792Sgshapiro			newvalue = sm_strdup_tagged_x(value, file, line, 0);
442132943Sgshapiro#else /* SM_HEAP_CHECK */
443132943Sgshapiro			newvalue = sm_strdup_x(value);
444132943Sgshapiro#endif /* SM_HEAP_CHECK */
44590792Sgshapiro			setbitn(id, mac->mac_allocated);
44690792Sgshapiro		}
44790792Sgshapiro		mac->mac_table[id] = newvalue;
44890792Sgshapiro		if (freeit != NULL)
44990792Sgshapiro			sm_free(freeit);
45090792Sgshapiro	}
45190792Sgshapiro	else
45290792Sgshapiro	{
45390792Sgshapiro		if (value == NULL || vclass == A_PERM)
45490792Sgshapiro			newvalue = value;
45590792Sgshapiro		else
45690792Sgshapiro			newvalue = sm_rpool_strdup_x(mac->mac_rpool, value);
45790792Sgshapiro		mac->mac_table[id] = newvalue;
45890792Sgshapiro		if (vclass == A_HEAP)
45990792Sgshapiro			sm_free(value);
46090792Sgshapiro	}
46190792Sgshapiro
46264562Sgshapiro#if _FFR_RESET_MACRO_GLOBALS
46390792Sgshapiro	switch (id)
46464562Sgshapiro	{
46564562Sgshapiro	  case 'j':
46690792Sgshapiro		PSTRSET(MyHostName, value);
46764562Sgshapiro		break;
46864562Sgshapiro	}
46964562Sgshapiro#endif /* _FFR_RESET_MACRO_GLOBALS */
47038032Speter}
47190792Sgshapiro
47290792Sgshapiro/*
47390792Sgshapiro**  MACSET -- set a named macro to a value (low level)
47490792Sgshapiro**
47590792Sgshapiro**	No fancy storage management; the caller takes full responsibility.
47690792Sgshapiro**	Often used with macget; see also macdefine.
47790792Sgshapiro**
47890792Sgshapiro**	Parameters:
47990792Sgshapiro**		mac -- Macro table.
48090792Sgshapiro**		i -- Macro name, specified as an integer offset.
48190792Sgshapiro**		value -- Macro value: either NULL, or a string.
48290792Sgshapiro*/
48390792Sgshapiro
48490792Sgshapirovoid
48590792Sgshapiromacset(mac, i, value)
48690792Sgshapiro	MACROS_T *mac;
48790792Sgshapiro	int i;
48890792Sgshapiro	char *value;
48990792Sgshapiro{
49090792Sgshapiro	if (i < 0 || i > MAXMACROID)
49190792Sgshapiro		return;
49290792Sgshapiro
49390792Sgshapiro	if (tTd(35, 9))
49490792Sgshapiro	{
49590792Sgshapiro		sm_dprintf("macset(%s as ", macname(i));
496132943Sgshapiro		xputs(sm_debug_file(), value);
49790792Sgshapiro		sm_dprintf(")\n");
49890792Sgshapiro	}
49990792Sgshapiro	mac->mac_table[i] = value;
50090792Sgshapiro}
50190792Sgshapiro
50290792Sgshapiro/*
50338032Speter**  MACVALUE -- return uninterpreted value of a macro.
50438032Speter**
50590792Sgshapiro**	Does fancy path searching.
50690792Sgshapiro**	The low level counterpart is macget.
50790792Sgshapiro**
50838032Speter**	Parameters:
50938032Speter**		n -- the name of the macro.
51090792Sgshapiro**		e -- envelope in which to start looking for the macro.
51138032Speter**
51238032Speter**	Returns:
51338032Speter**		The value of n.
51438032Speter**
51538032Speter**	Side Effects:
51638032Speter**		none.
51738032Speter*/
51838032Speter
51938032Speterchar *
52038032Spetermacvalue(n, e)
52138032Speter	int n;
522168515Sgshapiro	ENVELOPE *e;
52338032Speter{
52471345Sgshapiro	n = bitidx(n);
52590792Sgshapiro	if (e != NULL && e->e_mci != NULL)
52690792Sgshapiro	{
527168515Sgshapiro		char *p = e->e_mci->mci_macro.mac_table[n];
52890792Sgshapiro
52990792Sgshapiro		if (p != NULL)
53090792Sgshapiro			return p;
53190792Sgshapiro	}
53238032Speter	while (e != NULL)
53338032Speter	{
534168515Sgshapiro		char *p = e->e_macro.mac_table[n];
53538032Speter
53638032Speter		if (p != NULL)
53764562Sgshapiro			return p;
53873188Sgshapiro		if (e == e->e_parent)
53973188Sgshapiro			break;
54038032Speter		e = e->e_parent;
54138032Speter	}
54290792Sgshapiro	return GlobalMacros.mac_table[n];
54338032Speter}
544168515Sgshapiro
54590792Sgshapiro/*
54638032Speter**  MACNAME -- return the name of a macro given its internal id
54738032Speter**
54838032Speter**	Parameter:
54938032Speter**		n -- the id of the macro
55038032Speter**
55138032Speter**	Returns:
55238032Speter**		The name of n.
55338032Speter**
55438032Speter**	Side Effects:
55538032Speter**		none.
556168515Sgshapiro**
557168515Sgshapiro**	WARNING:
558168515Sgshapiro**		Not thread-safe.
55938032Speter*/
56038032Speter
56138032Speterchar *
56238032Spetermacname(n)
56338032Speter	int n;
56438032Speter{
56538032Speter	static char mbuf[2];
56638032Speter
567168515Sgshapiro	n = (int)(unsigned char)n;
568168515Sgshapiro	if (n > MAXMACROID)
569168515Sgshapiro		return "***OUT OF RANGE MACRO***";
570168515Sgshapiro
571168515Sgshapiro	/* if not ASCII printable, look up the name */
572168515Sgshapiro	if (n <= 0x20 || n > 0x7f)
57338032Speter	{
57438032Speter		char *p = MacroName[n];
57538032Speter
57638032Speter		if (p != NULL)
57738032Speter			return p;
57838032Speter		return "***UNDEFINED MACRO***";
57938032Speter	}
580168515Sgshapiro
581168515Sgshapiro	/* if in the ASCII graphic range, just return the id directly */
58238032Speter	mbuf[0] = n;
58338032Speter	mbuf[1] = '\0';
58438032Speter	return mbuf;
58538032Speter}
586168515Sgshapiro
58790792Sgshapiro/*
58890792Sgshapiro**  MACID_PARSE -- return id of macro identified by its name
58938032Speter**
59038032Speter**	Parameters:
59138032Speter**		p -- pointer to name string -- either a single
59238032Speter**			character or {name}.
59338032Speter**		ep -- filled in with the pointer to the byte
59438032Speter**			after the name.
59538032Speter**
59638032Speter**	Returns:
59790792Sgshapiro**		0 -- An error was detected.
598168515Sgshapiro**		1..MAXMACROID -- The internal id code for this macro.
59938032Speter**
60038032Speter**	Side Effects:
60138032Speter**		If this is a new macro name, a new id is allocated.
60290792Sgshapiro**		On error, syserr is called.
60338032Speter*/
60438032Speter
60538032Speterint
60690792Sgshapiromacid_parse(p, ep)
607168515Sgshapiro	char *p;
60838032Speter	char **ep;
60938032Speter{
61038032Speter	int mid;
611168515Sgshapiro	char *bp;
61242575Speter	char mbuf[MAXMACNAMELEN + 1];
61338032Speter
61438032Speter	if (tTd(35, 14))
61538032Speter	{
61690792Sgshapiro		sm_dprintf("macid(");
617132943Sgshapiro		xputs(sm_debug_file(), p);
61890792Sgshapiro		sm_dprintf(") => ");
61938032Speter	}
62038032Speter
62138032Speter	if (*p == '\0' || (p[0] == '{' && p[1] == '}'))
62238032Speter	{
62338032Speter		syserr("Name required for macro/class");
62438032Speter		if (ep != NULL)
62538032Speter			*ep = p;
62638032Speter		if (tTd(35, 14))
62790792Sgshapiro			sm_dprintf("NULL\n");
62871345Sgshapiro		return 0;
62938032Speter	}
63038032Speter	if (*p != '{')
63138032Speter	{
63238032Speter		/* the macro is its own code */
63338032Speter		if (ep != NULL)
63438032Speter			*ep = p + 1;
63538032Speter		if (tTd(35, 14))
636168515Sgshapiro		{
637168515Sgshapiro			char buf[2];
638168515Sgshapiro
639168515Sgshapiro			buf[0] = *p;
640168515Sgshapiro			buf[1] = '\0';
641168515Sgshapiro			xputs(sm_debug_file(), buf);
642168515Sgshapiro			sm_dprintf("\n");
643168515Sgshapiro		}
64471345Sgshapiro		return bitidx(*p);
64538032Speter	}
64638032Speter	bp = mbuf;
647168515Sgshapiro	while (*++p != '\0' && *p != '}' && bp < &mbuf[sizeof(mbuf) - 1])
64838032Speter	{
64938032Speter		if (isascii(*p) && (isalnum(*p) || *p == '_'))
65038032Speter			*bp++ = *p;
65138032Speter		else
65238032Speter			syserr("Invalid macro/class character %c", *p);
65338032Speter	}
65438032Speter	*bp = '\0';
65538032Speter	mid = -1;
65638032Speter	if (*p == '\0')
65738032Speter	{
65838032Speter		syserr("Unbalanced { on %s", mbuf);	/* missing } */
65938032Speter	}
66038032Speter	else if (*p != '}')
66138032Speter	{
66238032Speter		syserr("Macro/class name ({%s}) too long (%d chars max)",
663168515Sgshapiro			mbuf, (int) (sizeof(mbuf) - 1));
66438032Speter	}
665173340Sgshapiro	else if (mbuf[1] == '\0' && mbuf[0] >= 0x20)
66638032Speter	{
66738032Speter		/* ${x} == $x */
66871345Sgshapiro		mid = bitidx(mbuf[0]);
66938032Speter		p++;
67038032Speter	}
67138032Speter	else
67238032Speter	{
673168515Sgshapiro		STAB *s;
67438032Speter
67538032Speter		s = stab(mbuf, ST_MACRO, ST_ENTER);
67638032Speter		if (s->s_macro != 0)
67738032Speter			mid = s->s_macro;
67838032Speter		else
67938032Speter		{
68064562Sgshapiro			if (NextMacroId > MAXMACROID)
68138032Speter			{
68290792Sgshapiro				syserr("Macro/class {%s}: too many long names",
68390792Sgshapiro					mbuf);
68438032Speter				s->s_macro = -1;
68538032Speter			}
68638032Speter			else
68738032Speter			{
68838032Speter				MacroName[NextMacroId] = s->s_name;
689173340Sgshapiro				s->s_macro = mid = NextMacroId;
690173340Sgshapiro				NextMacroId = NEXTMACROID(NextMacroId);
69138032Speter			}
69238032Speter		}
69338032Speter		p++;
69438032Speter	}
69538032Speter	if (ep != NULL)
69638032Speter		*ep = p;
69771345Sgshapiro	if (mid < 0 || mid > MAXMACROID)
69871345Sgshapiro	{
69971345Sgshapiro		syserr("Unable to assign macro/class ID (mid = 0x%x)", mid);
70071345Sgshapiro		if (tTd(35, 14))
70190792Sgshapiro			sm_dprintf("NULL\n");
70271345Sgshapiro		return 0;
70371345Sgshapiro	}
70438032Speter	if (tTd(35, 14))
70590792Sgshapiro		sm_dprintf("0x%x\n", mid);
70638032Speter	return mid;
70738032Speter}
708168515Sgshapiro
70990792Sgshapiro/*
71038032Speter**  WORDINCLASS -- tell if a word is in a specific class
71138032Speter**
71238032Speter**	Parameters:
71338032Speter**		str -- the name of the word to look up.
71438032Speter**		cl -- the class name.
71538032Speter**
71638032Speter**	Returns:
71790792Sgshapiro**		true if str can be found in cl.
71890792Sgshapiro**		false otherwise.
71938032Speter*/
72038032Speter
72138032Speterbool
72238032Speterwordinclass(str, cl)
72338032Speter	char *str;
72438032Speter	int cl;
72538032Speter{
726168515Sgshapiro	STAB *s;
72738032Speter
72838032Speter	s = stab(str, ST_CLASS, ST_FIND);
72971345Sgshapiro	return s != NULL && bitnset(bitidx(cl), s->s_class);
73038032Speter}
731