NetBSD/usr.sbin/sendmail/src/macro.c
1997-07-13 19:42:13 +00:00

458 lines
9.6 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 1983, 1995-1997 Eric P. Allman
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static char sccsid[] = "@(#)macro.c 8.18 (Berkeley) 2/1/97";
#endif /* not lint */
# include "sendmail.h"
char *MacroName[256]; /* macro id to name table */
int NextMacroId = 0240; /* codes for long named macros */
/*
** EXPAND -- macro expand a string using $x escapes.
**
** Parameters:
** s -- the string to expand.
** buf -- the place to put the expansion.
** bufsize -- the size of the buffer.
** e -- envelope in which to work.
**
** Returns:
** none.
**
** Side Effects:
** none.
*/
void
expand(s, buf, bufsize, e)
register char *s;
register char *buf;
size_t bufsize;
register ENVELOPE *e;
{
register char *xp;
register char *q;
bool skipping; /* set if conditionally skipping output */
bool recurse = FALSE; /* set if recursion required */
int i;
int skiplev; /* skipping nesting level */
int iflev; /* if nesting level */
char xbuf[BUFSIZ];
static int explevel = 0;
if (tTd(35, 24))
{
printf("expand(");
xputs(s);
printf(")\n");
}
skipping = FALSE;
skiplev = 0;
iflev = 0;
if (s == NULL)
s = "";
for (xp = xbuf; *s != '\0'; s++)
{
int c;
/*
** Check for non-ordinary (special?) character.
** 'q' will be the interpolated quantity.
*/
q = NULL;
c = *s;
switch (c & 0377)
{
case CONDIF: /* see if var set */
iflev++;
c = *++s;
if (skipping)
skiplev++;
else
skipping = macvalue(c, e) == NULL;
continue;
case CONDELSE: /* change state of skipping */
if (iflev == 0)
break;
if (skiplev == 0)
skipping = !skipping;
continue;
case CONDFI: /* stop skipping */
if (iflev == 0)
break;
iflev--;
if (skiplev == 0)
skipping = FALSE;
if (skipping)
skiplev--;
continue;
case MACROEXPAND: /* macro interpolation */
c = *++s & 0377;
if (c != '\0')
q = macvalue(c, e);
else
{
s--;
q = NULL;
}
if (q == NULL)
continue;
break;
}
/*
** Interpolate q or output one character
*/
if (skipping || xp >= &xbuf[sizeof xbuf - 1])
continue;
if (q == NULL)
*xp++ = c;
else
{
/* copy to end of q or max space remaining in buf */
while ((c = *q++) != '\0' && xp < &xbuf[sizeof xbuf - 1])
{
/* check for any sendmail metacharacters */
if ((c & 0340) == 0200)
recurse = TRUE;
*xp++ = c;
}
}
}
*xp = '\0';
if (tTd(35, 24))
{
printf("expand ==> ");
xputs(xbuf);
printf("\n");
}
/* recurse as appropriate */
if (recurse)
{
if (explevel < MaxMacroRecursion)
{
explevel++;
expand(xbuf, buf, bufsize, e);
explevel--;
return;
}
syserr("expand: recursion too deep (%d max)",
MaxMacroRecursion);
}
/* copy results out */
i = xp - xbuf;
if (i >= bufsize)
i = bufsize - 1;
bcopy(xbuf, buf, i);
buf[i] = '\0';
}
/*
** DEFINE -- define a macro.
**
** this would be better done using a #define macro.
**
** Parameters:
** n -- the macro name.
** v -- the macro value.
** e -- the envelope to store the definition in.
**
** Returns:
** none.
**
** Side Effects:
** e->e_macro[n] is defined.
**
** Notes:
** There is one macro for each ASCII character,
** although they are not all used. The currently
** defined macros are:
**
** $a date in ARPANET format (preferring the Date: line
** of the message)
** $b the current date (as opposed to the date as found
** the message) in ARPANET format
** $c hop count
** $d (current) date in UNIX (ctime) format
** $e the SMTP entry message+
** $f raw from address
** $g translated from address
** $h to host
** $i queue id
** $j official SMTP hostname, used in messages+
** $k UUCP node name
** $l UNIX-style from line+
** $m The domain part of our full name.
** $n name of sendmail ("MAILER-DAEMON" on local
** net typically)+
** $o delimiters ("operators") for address tokens+
** $p my process id in decimal
** $q the string that becomes an address -- this is
** normally used to combine $g & $x.
** $r protocol used to talk to sender
** $s sender's host name
** $t the current time in seconds since 1/1/1970
** $u to user
** $v version number of sendmail
** $w our host name (if it can be determined)
** $x signature (full name) of from person
** $y the tty id of our terminal
** $z home directory of to person
** $_ RFC1413 authenticated sender address
**
** Macros marked with + must be defined in the
** configuration file and are used internally, but
** are not set.
**
** There are also some macros that can be used
** arbitrarily to make the configuration file
** cleaner. In general all upper-case letters
** are available.
*/
void
define(n, v, e)
int n;
char *v;
register ENVELOPE *e;
{
if (tTd(35, 9))
{
printf("%sdefine(%s as ",
(e->e_macro[n & 0377] == NULL) ? "" : "re", macname(n));
xputs(v);
printf(")\n");
}
e->e_macro[n & 0377] = v;
}
/*
** MACVALUE -- return uninterpreted value of a macro.
**
** Parameters:
** n -- the name of the macro.
**
** Returns:
** The value of n.
**
** Side Effects:
** none.
*/
char *
macvalue(n, e)
int n;
register ENVELOPE *e;
{
n &= 0377;
while (e != NULL)
{
register char *p = e->e_macro[n];
if (p != NULL)
return (p);
e = e->e_parent;
}
return (NULL);
}
/*
** MACNAME -- return the name of a macro given its internal id
**
** Parameter:
** n -- the id of the macro
**
** Returns:
** The name of n.
**
** Side Effects:
** none.
*/
char *
macname(n)
int n;
{
static char mbuf[2];
n &= 0377;
if (bitset(0200, n))
{
char *p = MacroName[n];
if (p != NULL)
return p;
return "***UNDEFINED MACRO***";
}
mbuf[0] = n;
mbuf[1] = '\0';
return mbuf;
}
/*
** MACID -- return id of macro identified by its name
**
** Parameters:
** p -- pointer to name string -- either a single
** character or {name}.
** ep -- filled in with the pointer to the byte
** after the name.
**
** Returns:
** The internal id code for this macro. This will
** fit into a single byte.
**
** Side Effects:
** If this is a new macro name, a new id is allocated.
*/
int
macid(p, ep)
register char *p;
char **ep;
{
int mid;
register char *bp;
char mbuf[21];
if (tTd(35, 14))
{
printf("macid(");
xputs(p);
printf(") => ");
}
if (*p == '\0' || (p[0] == '{' && p[1] == '}'))
{
syserr("Name required for macro/class");
if (ep != NULL)
*ep = p;
if (tTd(35, 14))
printf("NULL\n");
return '\0';
}
if (*p != '{')
{
/* the macro is its own code */
if (ep != NULL)
*ep = p + 1;
if (tTd(35, 14))
printf("%c\n", *p);
return *p;
}
bp = mbuf;
while (*++p != '\0' && *p != '}' && bp < &mbuf[sizeof mbuf])
{
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); /* missing } */
}
else if (*p != '}')
{
syserr("Macro/class name ({%s}) too long (%d chars max)",
mbuf, sizeof mbuf - 1);
}
else if (mbuf[1] == '\0')
{
/* ${x} == $x */
mid = mbuf[0];
p++;
}
else
{
register STAB *s;
s = stab(mbuf, ST_MACRO, ST_ENTER);
if (s->s_macro != 0)
mid = s->s_macro;
else
{
if (NextMacroId > 0377)
{
syserr("Macro/class {%s}: too many long names", mbuf);
s->s_macro = -1;
}
else
{
MacroName[NextMacroId] = s->s_name;
s->s_macro = mid = NextMacroId++;
}
}
p++;
}
if (ep != NULL)
*ep = p;
if (tTd(35, 14))
printf("0x%x\n", mid);
return mid;
}
/*
** WORDINCLASS -- tell if a word is in a specific class
**
** Parameters:
** str -- the name of the word to look up.
** cl -- the class name.
**
** Returns:
** TRUE if str can be found in cl.
** FALSE otherwise.
*/
bool
wordinclass(str, cl)
char *str;
int cl;
{
register STAB *s;
s = stab(str, ST_CLASS, ST_FIND);
return s != NULL && bitnset(cl & 0xff, s->s_class);
}