#include <sendmail.h>
SM_RCSID("@(#)$Id: macro.c,v 8.107 2007/08/06 22:29:02 ca Exp $")
#include <sm/sendmail.h>
#if MAXMACROID != (BITMAPBITS - 1)
ERROR Read the comment in conf.h
#endif
static char *MacroName[MAXMACROID + 1];
#define NEXTMACROID_L 037
#define NEXTMACROID_H 0240
#if _FFR_MORE_MACROS
static int NextMIdTable[] =
{
1,
2,
3,
4,
5,
6,
7,
8,
14,
-1,
-1,
-1,
-1,
-1,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
-1,
};
#define NEXTMACROID(mid) ( \
(mid < NEXTMACROID_L) ? (NextMIdTable[mid]) : \
((mid < NEXTMACROID_H) ? NEXTMACROID_H : (mid + 1)))
int NextMacroId = 1;
#else
int NextMacroId = 0240;
#define NEXTMACROID(mid) ((mid) + 1)
#endif
struct metamac MetaMacros[] =
{
{ '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE },
{ '=', MATCHCLASS }, { '~', MATCHNCLASS },
{ '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER },
{ '>', CALLSUBR },
{ '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI },
{ '[', HOSTBEGIN }, { ']', HOSTEND },
{ '(', LOOKUPBEGIN }, { ')', LOOKUPEND },
{ '&', MACRODEXPAND },
{ '\0', '\0' }
};
#define MACBINDING(name, mid) \
stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
MacroName[mid] = name;
void
initmacros(e)
ENVELOPE *e;
{
struct metamac *m;
int c;
char buf[5];
for (m = MetaMacros; m->metaname != '\0'; m++)
{
buf[0] = m->metaval;
buf[1] = '\0';
macdefine(&e->e_macro, A_TEMP, m->metaname, buf);
}
buf[0] = MATCHREPL;
buf[2] = '\0';
for (c = '0'; c <= '9'; c++)
{
buf[1] = c;
macdefine(&e->e_macro, A_TEMP, c, buf);
}
macdefine(&e->e_macro, A_PERM, 'n', "MAILER-DAEMON");
MACBINDING("opMode", MID_OPMODE);
}
static void doexpand __P(( char *, char *, size_t, int, ENVELOPE *));
static void
doexpand(s, buf, bufsize, explevel, e)
char *s;
char *buf;
size_t bufsize;
int explevel;
ENVELOPE *e;
{
char *xp;
char *q;
bool skipping;
bool recurse;
size_t i;
int skiplev;
int iflev;
bool quotenext;
char xbuf[MACBUFSIZE];
if (tTd(35, 24))
{
sm_dprintf("expand(");
xputs(sm_debug_file(), s);
sm_dprintf(")\n");
}
recurse = false;
skipping = false;
skiplev = 0;
iflev = 0;
quotenext = false;
if (s == NULL)
s = "";
for (xp = xbuf; *s != '\0'; s++)
{
int c;
q = NULL;
c = *s & 0377;
if (quotenext)
{
quotenext = false;
goto simpleinterpolate;
}
switch (c)
{
case CONDIF:
iflev++;
c = *++s & 0377;
if (skipping)
skiplev++;
else
{
char *mv;
mv = macvalue(c, e);
skipping = (mv == NULL || *mv == '\0');
}
continue;
case CONDELSE:
if (iflev == 0)
break;
if (skiplev == 0)
skipping = !skipping;
continue;
case CONDFI:
if (iflev == 0)
break;
iflev--;
if (skiplev == 0)
skipping = false;
if (skipping)
skiplev--;
continue;
case MACROEXPAND:
c = bitidx(*++s);
if (c != '\0')
q = macvalue(c, e);
else
{
s--;
q = NULL;
}
if (q == NULL)
continue;
break;
case METAQUOTE:
quotenext = true;
break;
}
simpleinterpolate:
if (skipping || xp >= &xbuf[sizeof(xbuf) - 1])
continue;
if (q == NULL)
*xp++ = c;
else
{
bool hiderecurse = false;
while ((c = *q++) != '\0' &&
xp < &xbuf[sizeof(xbuf) - 1])
{
if (!hiderecurse && (c & 0340) == 0200)
recurse = true;
*xp++ = c;
hiderecurse = (c & 0377) == METAQUOTE;
}
}
}
*xp = '\0';
if (tTd(35, 28))
{
sm_dprintf("expand(%d) ==> ", explevel);
xputs(sm_debug_file(), xbuf);
sm_dprintf("\n");
}
if (recurse)
{
if (explevel < MaxMacroRecursion)
{
doexpand(xbuf, buf, bufsize, explevel + 1, e);
return;
}
syserr("expand: recursion too deep (%d max)",
MaxMacroRecursion);
}
if (explevel == 0)
(void) sm_strlcpy(buf, xbuf, bufsize);
else
{
i = xp - xbuf;
if (i >= bufsize)
i = bufsize - 1;
memmove(buf, xbuf, i);
buf[i] = '\0';
}
if (tTd(35, 24))
{
sm_dprintf("expand ==> ");
xputs(sm_debug_file(), buf);
sm_dprintf("\n");
}
}
void
expand(s, buf, bufsize, e)
char *s;
char *buf;
size_t bufsize;
ENVELOPE *e;
{
doexpand(s, buf, bufsize, 0, e);
}
void
#if SM_HEAP_CHECK
macdefine_tagged(mac, vclass, id, value, file, line, grp)
#else
macdefine(mac, vclass, id, value)
#endif
MACROS_T *mac;
ARGCLASS_T vclass;
int id;
char *value;
#if SM_HEAP_CHECK
char *file;
int line;
int grp;
#endif
{
char *newvalue;
if (id < 0 || id > MAXMACROID)
return;
if (tTd(35, 9))
{
sm_dprintf("%sdefine(%s as ",
mac->mac_table[id] == NULL ? "" : "re", macname(id));
xputs(sm_debug_file(), value);
sm_dprintf(")\n");
}
if (mac->mac_rpool == NULL)
{
char *freeit = NULL;
if (mac->mac_table[id] != NULL &&
bitnset(id, mac->mac_allocated))
freeit = mac->mac_table[id];
if (value == NULL || vclass == A_HEAP)
{
sm_heap_checkptr_tagged(value, file, line);
newvalue = value;
clrbitn(id, mac->mac_allocated);
}
else
{
#if SM_HEAP_CHECK
newvalue = sm_strdup_tagged_x(value, file, line, 0);
#else
newvalue = sm_strdup_x(value);
#endif
setbitn(id, mac->mac_allocated);
}
mac->mac_table[id] = newvalue;
if (freeit != NULL)
sm_free(freeit);
}
else
{
if (value == NULL || vclass == A_PERM)
newvalue = value;
else
newvalue = sm_rpool_strdup_x(mac->mac_rpool, value);
mac->mac_table[id] = newvalue;
if (vclass == A_HEAP)
sm_free(value);
}
#if _FFR_RESET_MACRO_GLOBALS
switch (id)
{
case 'j':
PSTRSET(MyHostName, value);
break;
}
#endif
}
void
macset(mac, i, value)
MACROS_T *mac;
int i;
char *value;
{
if (i < 0 || i > MAXMACROID)
return;
if (tTd(35, 9))
{
sm_dprintf("macset(%s as ", macname(i));
xputs(sm_debug_file(), value);
sm_dprintf(")\n");
}
mac->mac_table[i] = value;
}
char *
macvalue(n, e)
int n;
ENVELOPE *e;
{
n = bitidx(n);
if (e != NULL && e->e_mci != NULL)
{
char *p = e->e_mci->mci_macro.mac_table[n];
if (p != NULL)
return p;
}
while (e != NULL)
{
char *p = e->e_macro.mac_table[n];
if (p != NULL)
return p;
if (e == e->e_parent)
break;
e = e->e_parent;
}
return GlobalMacros.mac_table[n];
}
char *
macname(n)
int n;
{
static char mbuf[2];
n = (int)(unsigned char)n;
if (n > MAXMACROID)
return "***OUT OF RANGE MACRO***";
if (n <= 0x20 || n > 0x7f)
{
char *p = MacroName[n];
if (p != NULL)
return p;
return "***UNDEFINED MACRO***";
}
mbuf[0] = n;
mbuf[1] = '\0';
return mbuf;
}
int
macid_parse(p, ep)
char *p;
char **ep;
{
int mid;
char *bp;
char mbuf[MAXMACNAMELEN + 1];
if (tTd(35, 14))
{
sm_dprintf("macid(");
xputs(sm_debug_file(), p);
sm_dprintf(") => ");
}
if (*p == '\0' || (p[0] == '{' && p[1] == '}'))
{
syserr("Name required for macro/class");
if (ep != NULL)
*ep = p;
if (tTd(35, 14))
sm_dprintf("NULL\n");
return 0;
}
if (*p != '{')
{
if (ep != NULL)
*ep = p + 1;
if (tTd(35, 14))
{
char buf[2];
buf[0] = *p;
buf[1] = '\0';
xputs(sm_debug_file(), buf);
sm_dprintf("\n");
}
return bitidx(*p);
}
bp = mbuf;
while (*++p != '\0' && *p != '}' && bp < &mbuf[sizeof(mbuf) - 1])
{
if (isascii(*p) && (isalnum(*p) || *p == '_'))
*bp++ = *p;
else
syserr("Invalid macro/class character %c", *p);
}
*bp = '\0';
mid = -1;
if (*p == '\0')
{
syserr("Unbalanced { on %s", mbuf);
}
else if (*p != '}')
{
syserr("Macro/class name ({%s}) too long (%d chars max)",
mbuf, (int) (sizeof(mbuf) - 1));
}
else if (mbuf[1] == '\0' && mbuf[0] >= 0x20)
{
mid = bitidx(mbuf[0]);
p++;
}
else
{
STAB *s;
s = stab(mbuf, ST_MACRO, ST_ENTER);
if (s->s_macro != 0)
mid = s->s_macro;
else
{
if (NextMacroId > MAXMACROID)
{
syserr("Macro/class {%s}: too many long names",
mbuf);
s->s_macro = -1;
}
else
{
MacroName[NextMacroId] = s->s_name;
s->s_macro = mid = NextMacroId;
NextMacroId = NEXTMACROID(NextMacroId);
}
}
p++;
}
if (ep != NULL)
*ep = p;
if (mid < 0 || mid > MAXMACROID)
{
syserr("Unable to assign macro/class ID (mid = 0x%x)", mid);
if (tTd(35, 14))
sm_dprintf("NULL\n");
return 0;
}
if (tTd(35, 14))
sm_dprintf("0x%x\n", mid);
return mid;
}
bool
wordinclass(str, cl)
char *str;
int cl;
{
STAB *s;
s = stab(str, ST_CLASS, ST_FIND);
return s != NULL && bitnset(bitidx(cl), s->s_class);
}