rsalz@uunet.uu.net (Rich Salz) (02/09/89)
Submitted-by: Arnold D. Robbins <arnold@EMORY.EDU> Posting-number: Volume 17, Issue 89 Archive-name: ease2/part03 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 3 (of 3)." # Contents: cfc/cfc.c doc/ease.paper # Wrapped by rsalz@papaya.bbn.com on Wed Feb 8 16:55:44 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'cfc/cfc.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'cfc/cfc.c'\" else echo shar: Extracting \"'cfc/cfc.c'\" \(21150 characters\) sed "s/^X//" >'cfc/cfc.c' <<'END_OF_FILE' X#ifndef lint Xstatic char RCSid[] = "$Header: cfc.c,v 2.0 88/06/15 15:16:48 root Exp $"; X#endif X X/* X * $Log: cfc.c,v $ X * Revision 2.0 88/06/15 15:16:48 root X * Baseline release for net posting. ADR. X * X * Revision 1.6 88/06/10 13:45:16 root X * Fix originally from Raymond A. Schnitzler (ras@sabre.bellcore.com) to X * add the (undocumented) 'P' option which sets the Postmaster address for X * receiving cc's of bad mail. ADR. X * X * Revision 1.5 88/01/21 16:18:13 root X * Eliminated Rutgers-ism, linted, smartened Mailer Argv handling. ADR. X * X * Revision 1.4 88/01/21 15:57:52 root X * Added the 'y' factor; missed it last time. ADR. X * X * Revision 1.3 87/04/08 10:23:02 root X * Small bug fixes, compatibility option added, also warnings for X * unrecognized flags and options. ADR. X * X * Revision 1.2 87/02/18 15:26:39 root X * Fix to recognize multidigit ruleset numbers in $> (calls) in RHS. ADR. X * X * Revision 1.1 87/02/16 15:25:00 arnold X * Initial revision X * X * Revision 1.1 87/02/16 15:25:00 arnold X * Initial revision X * X */ X X/* X * cfc.c X * X * Sendmail cf file compiler. X * Reads a raw sendmail.cf file and produces ease source. X * X * There are very few comments in this source. You will need both the X * "Sendmail Installation and Operation Guide" and the paper on Ease X * to really understand this. X * X * Arnold Robbins X * Emory University Computing Center X * 2/87 X */ X X#include <stdio.h> X#include <ctype.h> X Xchar buffer[BUFSIZ]; Xint line = 0; Xint inruleset = 0; X Xextern char *macro (); /* convert sendmail to ease macro names */ Xextern char *mflags (); /* convert sendmail to ease mailer flag names */ Xextern char *optionname (); /* convert sendmail to ease option names */ Xextern char *delivoption (); /* delivery options */ Xextern char *handle_option (); /* handling options */ X Xextern char *ngets (); /* buffered gets () routine */ Xextern void ungets (); /* put a buffer back for getting */ X X#define endruleset() if (inruleset) { inruleset = 0; printf ("\t}\n"); } X Xint compat = 0; /* complain about new 4.3 options & flags */ Xint undoc = 0; /* complain about undocumented options, flags */ X Xmain (argc, argv) Xint argc; Xchar **argv; X{ X extern int getopt (); X extern int optind; X int c; X X while ((c = getopt (argc, argv, "cu")) != EOF) { X switch (c) { X case 'c': X compat = 1; X break; X case 'u': X undoc = 1; X break; X case '?': X default: X fprintf (stderr, "usage: %s [ -c ] [ -u ]\n", argv[0]); X break; X } X } X X if (optind < argc) X fprintf (stderr, X "warning: ignoring non-flag command line arguments\n"); X X printf ("/******************************************************/\n"); X printf ("/* This ease file generated by cfc from a sendmail.cf */\n"); X printf ("/* file. It must be edited by hand before being fed */\n"); X printf ("/* to ease! */\n"); X printf ("/******************************************************/\n"); X printf ("\n\nbind\n\t/* RULESET BINDINGS GO HERE (cfc) */\n\n"); X X /* X * For perfection, everything but the comment and rule cases X * should do an endruleset (), but practically speaking, it is X * usually only the mailer new ruleset definitions that end a X * previous ruleset. Occasionally a macro, too. X */ X X while (ngets (buffer) != NULL) X { X line++; X switch (buffer[0]) { X case '#': X comment (); X continue; /* skip code to end ruleset */ X case 'S': X endruleset (); X ruleset (); X continue; /* skip code to end ruleset */ X case 'R': X rule (); X continue; /* skip code to end ruleset */ X case 'D': X endruleset (); X def (); X break; X case 'C': X class (); X break; X case 'F': X fileclass (); X break; X case 'M': X endruleset (); X mailer (); X break; X case 'H': X header (); X break; X case 'O': X option (); X break; X case 'T': X trusted (); X break; X case 'P': X precedence (); X break; X default: X other (); X continue; /* skip code to end ruleset */ X } X endruleset (); X } X endruleset (); /* just in case */ X exit (0); X /*NOTREACHED*/ X} X X/* comment --- produce a comment */ X Xcomment () X{ X static char format[] = "/* %s */\n"; X register int i = strlen (buffer) - 1; X X /* try to be semi-intelligent about comments */ X X if (buffer[1] == '\0') X printf ("\n"); X else if (isspace (buffer[1]) && buffer[i] != '#') X { X for (i = 1; isspace (buffer[i]); i++) X ; X printf (format, buffer + i); X } X else X printf (format, buffer); X} X X/* ruleset --- name a ruleset */ X Xruleset () X{ X static int first = 1; X register char *cp = buffer + 1; X X if (first) X { X first = 0; X printf ("\n/* These are sample field definitons (cfc) */\n"); X printf ("\nfield\n\tzero_or_more : match (0*);\n"); X printf ("\tone_or_more : match (1*);\n"); X printf ("\texactly_one : match (1);\n"); X printf ("\tany_in_? : match (1) in ?;\n"); X printf ("\tany_not_in_? : match (0) in ?;\n\n"); X } X X printf ("ruleset\n\tRULESET_"); X while (*cp && ! isspace (*cp)) X { X putchar (*cp); X cp++; X } X X printf (" {"); X if (*cp) X printf ("\t/* %s */", cp); X putchar ('\n'); X inruleset++; X} X X/* rule --- print out a rule */ X Xrule () X{ X register char *cp = buffer + 1; X register char *cp2; X register int com = 0; X X /* first, split it up into LHS, RHS, COMMENT */ X X while (*cp != '\t') X cp++; X *cp = '\0'; X X cp++; X while (*cp == '\t') X cp++; X cp2 = cp; X while (*cp && *cp != '\t') X cp++; X if (*cp == '\t' && cp[1]) X { X *cp = '\0'; X com++; X cp++; X while (*cp == '\t') X cp++; X } X X /* now print */ X lhs (buffer + 1); /* left hand side */ X if (com) X printf ("\t/* %s */", cp); X putchar ('\n'); X rhs (cp2); /* right hand side */ X} X X/* lhs --- left hand side of a production */ X Xlhs (text) Xchar *text; X{ X register char *cp = text; X register int conditional = 0; X register int quoting = 0; X X printf ("\tif ("); X for (; *cp; cp++) X { X switch (*cp) { X case '$': X if (quoting) X { X quoting = 0; X putchar ('"'); X } X switch (*++cp) { X case '*': X printf (" zero_or_more "); X break; X case '+': X printf (" one_or_more "); X break; X case '-': X printf (" exactly_one "); X break; X case '=': X printf (" any_in_%c ", *++cp); X break; X case '~': X printf (" any_not_in_%c ", *++cp); X break; X case '?': X printf (" ifset (%s, ", macro (*++cp)); X conditional++; X break; X case '|': X printf (", "); X break; X case '.': X putchar (')'); X conditional--; X break; X case '1': X case '2': X case '3': X case '4': X case '5': X case '6': X case '7': X case '8': X case '9': X printf ("$%c", *cp); X break; X default: X if (quoting) X printf ("${%s}", macro (*cp)); X else X printf ("$%s", macro (*cp)); X break; X } X break; X default: X if (ispunct (*cp)) X { X if (quoting) /* end a literal */ X { X quoting = 0; X putchar ('"'); X } X /* else X do nothing */ X } X else X { X if (! quoting) /* start a literal */ X { X quoting = 1; X putchar ('"'); X } X /* else X do nothing */ X } X putchar (*cp); /* print the character */ X break; X } X } X if (quoting) X putchar ('"'); X if (conditional) X die ("lhs"); X printf (")"); X} X X/* rhs --- right hand side of a production */ X Xrhs (text) Xchar *text; X{ X register char *cp = text; X char *index (); X register int open = 0; X register int conditional = 0; X register int quoting = 0; X X printf ("\t\t"); X X if (*cp == '$' && index ("#@:", cp[1]) != NULL) X ; /* not the default */ X else X { X printf ("retry ("); X open++; X } X X for (; *cp; cp++) X { X switch (*cp) { X case '$': X if (quoting) X { X quoting = 0; X putchar ('"'); X } X switch (*++cp) { X case '>': X printf ("RULESET_"); X for (cp++; *cp && isdigit (*cp); cp++) X putchar (*cp); X cp--; X printf (" ("); X open++; X break; X case '[': X printf ("canon ("); X open++; X break; X case ']': X putchar (')'); X open--; X break; X case '?': X printf ("ifset (%s, ", macro (*++cp)); X conditional++; X break; X case '|': X putchar (','); X break; X case '.': X putchar (')'); X conditional--; X break; X case '#': X printf ("resolve (mailer ("); X if (strncmp (cp+1, "local$", 6) == 0 X || strncmp (cp+1, "error$", 6) == 0) X goto skiphost; X loop1: X for (cp++; *cp != '$'; cp++) X putchar (*cp); X cp++; X if (*cp != '@') X { X printf ("$%c", *cp); X goto loop1; X } X printf ("),\n\t\t\t\thost ("); X skiphost: X loop2: X for (cp++; *cp != '$'; cp++) X putchar (*cp); X cp++; X if (*cp != ':') X { X printf ("$%c", *cp); X goto loop2; X } X printf ("),\n\t\t\t\tuser ("); X for (cp++; *cp; cp++) X putchar (*cp); X printf ("))"); X goto out; /* string is exhausted */ X /* break; */ X case '@': X printf ("return ("); X open++; X break; X case ':': X printf ("next ("); X open++; X break; X case '1': X case '2': X case '3': X case '4': X case '5': X case '6': X case '7': X case '8': X case '9': X printf ("$%c", *cp); X break; X default: X if (quoting) X printf ("${%s}", macro (*cp)); X else X printf ("$%s", macro (*cp)); X break; X } X break; X default: X if (ispunct (*cp)) X { X if (quoting) /* end a literal */ X { X quoting = 0; X putchar ('"'); X } X /* else X do nothing */ X } X else X { X if (! quoting) /* start a literal */ X { X quoting = 1; X putchar ('"'); X } X /* else X do nothing */ X } X putchar (*cp); /* print the character */ X break; X } X } Xout: X if (quoting) X putchar ('"'); X while (open--) X putchar (')'); X printf (";\n"); X if (conditional) X die ("rhs"); X} X X/* def --- define a macro */ X Xdef () X{ X register char *mac = buffer + 1, *value = buffer + 2; X register int conditional = 0; X X printf ("macro\n\t%s = \"", macro (*mac)); X X while (*value) X { X switch (*value) { X case '$': X switch (*++value) { X case '?': X printf ("ifset (%s, ", macro (*++value)); X conditional++; X break; X case '|': X putchar (','); X break; X case '.': X putchar (')'); X conditional--; X break; X default: X printf ("${%s}", macro (*value)); X break; X } X break; X default: X putchar (*value); X break; X } X value++; X } X printf ("\";\n"); X if (conditional) X die ("def"); X} X X/* class --- define a class list */ X Xclass () X{ X register char *name = buffer + 1, *value = buffer + 2; X X printf ("class\n\t%c = { ", *name); X X while (*value && isspace (*value)) X value++; X X while (*value) X { X if (isspace (*value)) X { X printf (", "); X while (isspace (*value)) X value++; X value--; /* cancel loop */ X } X else X putchar (*value); X value++; X } X printf (" };\n"); X} X X/* fileclass --- define a class that is to be read from a file */ X Xfileclass () X{ X register char *name = buffer + 1, *value = buffer + 2; X X printf ("class\n\t%c = readclass (\"", *name); X for (; *value && !isspace (*value); value++) X putchar (*value); X putchar ('"'); X while (*value && isspace (*value)) X value++; X if (*value) X printf (", \"%s\"", value); X printf (");\n"); X} X X/* mailer --- convert a mailer specification */ X Xmailer () X{ X register char *cp = buffer + 1; X X printf ("mailer\n\t"); X for (; *cp != ','; cp++) X putchar (*cp); X cp++; X printf (" {\n"); /* just did mailer name */ X X#define skipname() cp++; while (*cp != '=') cp++; cp++ X#define value() for (; *cp && *cp != ','; cp++) putchar (*cp); cp++ X Xloop: X while (*cp && isspace (*cp)) X cp++; X X printf ("\t\t"); X switch (*cp) { X case 'A': X skipname (); X printf ("Argv = \""); X for (; *cp && *cp != ','; cp++) X { X if (*cp == '$') /* XXX: assume no conditionals */ X printf ("${%s}", macro (*++cp)); X else if (*cp == '"') X printf ("\\\""); X else X putchar (*cp); X } X cp++; /* do manually what value does */ X putchar ('"'); X break; X X case 'E': X skipname (); X printf ("Eol = \""); X value (); X putchar ('"'); X break; X X case 'F': X skipname (); X printf ("Flags = { "); X for (; *cp && *cp != ','; cp++) X { X printf ("%s", mflags (*cp)); X if (cp[1] && cp[1] != ',') X printf (", "); X } X cp++; /* do manually what value does */ X printf (" }"); X break; X X case 'M': X skipname (); X printf ("Maxsize = \""); X value (); X putchar ('"'); X break; X X case 'P': X skipname (); X printf ("Path = \""); X value (); X putchar ('"'); X break; X X case 'R': X skipname (); X printf ("Recipient = RULESET_"); X value (); X break; X X case 'S': X skipname (); X printf ("Sender = RULESET_"); X value (); X break; X X case '\0': X goto done; X } X X if (cp[-1] && cp[-1] == ',') X { X printf (",\n"); X goto loop; X } X else X putchar ('\n'); X Xdone: X /* handle continuation lines */ X if (ngets (buffer) != NULL) X { X line++; X if (buffer[0] == '\t') X { X cp = buffer; X goto loop; X } X else X ungets (buffer); X } X else X ungets ((char *) NULL); X X printf ("\t};\n"); X X#undef value X#undef skipname X} X X/* header --- define sendmail headers */ X Xheader () X{ X register char *cp = buffer + 1; X register int flags = 0; X register int conditional = 0; X X printf ("header\n\t"); X if (*cp == '?') /* header for mailers with these flags */ X { X flags++; X printf ("for ("); X for (cp++; *cp != '?'; cp++) X { X printf ("%s", mflags (*cp)); X if (cp[1] != '?') X putchar (','); X } X printf (") {\n\t\t"); X cp++; /* skip final '?' */ X } X X printf ("define (\""); X for (; *cp && ! isspace (*cp); cp++) X putchar (*cp); X printf ("\", \""); X Xbody: X while (*cp) X { X switch (*cp) { X case '$': X switch (*++cp) { X case '?': X printf ("ifset (%s, ", macro (*++cp)); X conditional++; X break; X case '|': X putchar (','); X break; X case '.': X putchar (')'); X conditional--; X break; X default: X printf ("${%s}", macro (*cp)); X break; X } X break; X default: X putchar (*cp); X break; X } X cp++; X } X X /* handle continuation lines */ X if (ngets (buffer) != NULL) X { X line++; X if (buffer[0] == '\t') X { X printf ("\\\n"); X cp = buffer + 1; X goto body; X } X else X ungets (buffer); X } X else X ungets ((char *) NULL); X X printf ("\");\n"); X X if (flags) X printf ("\t};\n"); X X if (conditional) X die ("header"); X} X X/* option --- translate a sendmail option to an ease option */ X Xoption () X{ X register char *name = buffer + 1, *value = buffer + 2; X X printf ("options\n\t"); X if (*name == 'd') /* delivery */ X printf ("o_delivery = %s;\n", delivoption (*value)); X else if (*name == 'e') /* handling */ X printf ("o_handling = %s;\n", handle_option (*value)); X else X printf ("%s = \"%s\";\n", optionname (*name), value); X} X X/* trusted --- define the list of trusted users */ X Xtrusted () X{ X register char *cp = buffer + 1; X X while (*cp) X { X if (isspace (*cp)) X *cp = ','; X cp++; X } X printf ("trusted\n\t{ %s };\n", buffer+1); X} X X/* precedence --- define the precedence of a message class */ X Xprecedence () X{ X register char *cp = buffer + 1; X X printf ("precedence\n\t"); X for (; *cp && *cp != '='; cp++) X putchar (*cp); X printf (" = %s;\n", ++cp); X} X X/* other --- not a sendmail control line */ X Xother () X{ X printf ("%s\n", buffer); X} X Xdie (routine) Xchar *routine; X{ X fprintf (stderr, "%s: malformed input line %d: fatal error\n", X routine, line); X exit (1); X} X X/* macro --- return name for sendmail predefined macro */ X Xchar *macro (c) Xchar c; X{ X static char buf[2] = { '\0', '\0' }; X X switch (c) { X case 'a': /* The origination date in Arpanet format */ X return ("m_odate"); X X case 'b': /* The current date in Arpanet format */ X return ("m_adate"); X X case 'c': /* The hop count */ X return ("m_hops"); X X case 'd': /* The date in UNIX (ctime) format */ X return ("m_udate"); X X case 'e': /* The SMTP entry message */ X return ("m_smtp"); X X case 'f': /* The sender (from) address */ X return ("m_saddr"); X X case 'g': /* The sender address relative to the recipient */ X return ("m_sreladdr"); X X case 'h': /* The recipient host */ X return ("m_rhost"); X X case 'i': /* The queue id */ X return ("m_qid"); X X case 'j': /* The official domain name for this site */ X return ("m_oname"); X X case 'l': /* The format of the UNIX from line */ X return ("m_ufrom"); X X case 'n': /* The name of the daemon (for error messages) */ X return ("m_daemon"); X X case 'o': /* The set of "operators" in addresses */ X return ("m_addrops"); X X case 'p': /* Sendmail's pid */ X return ("m_pid"); X X case 'q': /* The default format of sender address */ X return ("m_defaddr"); X X case 'r': /* Protocol used */ X return ("m_protocol"); X X case 's': /* Sender's host name */ X return ("m_shostname"); X X case 't': /* A numeric representation of the current time */ X return ("m_ctime"); X X case 'u': /* The recipient user */ X return ("m_ruser"); X X case 'v': /* The version number of sendmail */ X return ("m_version"); X X case 'w': /* The hostname of this site */ X return ("m_sitename"); X X case 'x': /* The full name of the sender */ X return ("m_sname"); X X case 'y': /* The id of the sender's tty */ X return ("m_stty"); X X case 'z': /* The home directory of the recipient */ X return ("m_rhdir"); X X default: X buf[0] = c; X return (buf); X } X} X X#define docompat(val) if (compat) goto warn; else return (val) X#define dofundoc(val) if (undoc) \ Xfprintf (stderr, "warning: undocumented flag '%c' used on line %d\n", c, line);\ Xreturn (val) X X/* mflags --- convert sendmail mailer flags to ease names */ X Xchar *mflags (c) Xchar c; X{ X static char buf[2] = { '\0', '\0' }; X X switch (c) { X case 'f': return ("f_ffrom"); X case 'r': return ("f_rfrom"); X case 'S': return ("f_noreset"); X case 'n': return ("f_noufrom"); X case 'l': return ("f_locm"); X case 's': return ("f_strip"); X case 'm': return ("f_mult"); X case 'F': return ("f_from"); X case 'D': return ("f_date"); X case 'M': return ("f_mesg"); X case 'x': return ("f_full"); X case 'P': return ("f_return"); X case 'u': return ("f_upperu"); X case 'h': return ("f_upperh"); X case 'A': return ("f_arpa"); X case 'U': return ("f_ufrom"); X case 'e': return ("f_expensive"); X case 'X': return ("f_dot"); X case 'L': return ("f_llimit"); X case 'p': return ("f_retsmtp"); X case 'I': return ("f_smtp"); X case 'C': return ("f_addrw"); X case 'E': docompat ("f_escape"); X case 'R': dofundoc ("f_rport"); X default: X warn: X fprintf (stderr, X "warning: non standard mailer flag '%c' on line %d\n", X c, line); X buf[0] = c; X return buf; X } X} X X#define doOundoc(val) if (undoc) \ Xfprintf (stderr, "warning: undocumented option '%c' used on line %d\n", c, line);\ Xreturn (val) X X/* optionname --- convert sendmail options to ease names */ X Xchar *optionname (c) Xchar c; X{ X static char buf[2] = { '\0', '\0' }; X X switch (c) { X case 'A': return ("o_alias"); X case 'a': return ("o_ewait"); X case 'B': return ("o_bsub"); X case 'C': doOundoc ("o_checkpoint"); X case 'c': return ("o_qwait"); X case 'd': return ("o_delivery"); X case 'D': return ("o_rebuild"); X case 'e': return ("o_handling"); X case 'F': return ("o_tmode"); X case 'f': return ("o_usave"); X case 'g': return ("o_gid"); X case 'H': return ("o_fsmtp"); X case 'i': return ("o_skipd"); X case 'L': return ("o_slog"); X case 'm': return ("o_rsend"); X case 'N': return ("o_dnet"); X case 'n': doOundoc ("o_validate"); X case 'o': return ("o_hformat"); X case 'P': doOundoc ("o_pmaster"); X case 'Q': return ("o_qdir"); X case 'q': docompat ("o_qfactor"); X case 'r': return ("o_tread"); X case 'S': return ("o_flog"); X case 's': return ("o_safe"); X case 'T': return ("o_qtimeout"); X case 't': return ("o_timezone"); X case 'u': return ("o_dmuid"); X case 'v': return ("o_verbose"); X case 'W': return ("o_wizpass"); X case 'x': return ("o_loadq"); X case 'X': return ("o_loadnc"); X case 'Y': docompat ("o_newproc"); X case 'y': docompat ("o_recipfactor"); X case 'Z': docompat ("o_prifactor"); X case 'z': docompat ("o_waitfactor"); X default: X warn: X fprintf (stderr, X "warning: non standard option '%c' on line %d\n", X c, line); X buf[0] = c; X return buf; X } X} X X/* delivoption --- convert sendmail delivery option value to ease name */ X Xchar *delivoption (c) Xchar c; X{ X static char buf[2] = { '\0', '\0' }; X X switch (c) { X case 'i': return ("d_interactive"); X case 'b': return ("d_background"); X case 'q': return ("d_queue"); X default: X fprintf (stderr, X "warning: non standard delivery option '%c' on line %d\n", c, line); X buf[0] = c; X return buf; X } X} X X/* handle_option --- convert sendmail handling option value to ease name */ X Xchar *handle_option (c) Xchar c; X{ X static char buf[2] = { '\0', '\0' }; X X switch (c) { X case 'p': return ("h_print"); X case 'q': return ("h_exit"); X case 'm': return ("h_mail"); X case 'w': return ("h_write"); X case 'e': return ("h_mailz"); X default: X fprintf (stderr, X "warning: non standard handling option '%c' on line %d\n", c, line); X buf[0] = c; X return buf; X } X} X X/* X * "buffered" i/o routines. These are necessary since X * mail headers may have continuation lines, and we can't see if X * a continuation line is there without getting it. If it isn't, X * just put it back. X */ X Xint saved = 0; Xchar *saveb = NULL; X X/* ngets --- get a line of input from either saved buffer or stdin */ X Xchar *ngets (bp) Xchar *bp; X{ X if (! saved) X return (gets (bp)); X X saved = 0; X bp = saveb; X saveb = NULL; X return (bp); X} X X/* ungets --- put a buffer back on the input, so to speak */ X Xvoid ungets (bp) Xchar *bp; X{ X saved = 1; X saveb = bp; X line--; X} END_OF_FILE if test 21150 -ne `wc -c <'cfc/cfc.c'`; then echo shar: \"'cfc/cfc.c'\" unpacked with wrong size! fi chmod +x 'cfc/cfc.c' # end of 'cfc/cfc.c' fi if test -f 'doc/ease.paper' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'doc/ease.paper'\" else echo shar: Extracting \"'doc/ease.paper'\" \(31245 characters\) sed "s/^X//" >'doc/ease.paper' <<'END_OF_FILE' X... X... $Header: ease.paper,v 1.6 88/06/15 10:12:36 root Locked $ X... X... $Log: ease.paper,v $ X... Revision 1.6 88/06/15 10:12:36 root X... Some editorial cleanup, added Acknowledgements section. X... X... Revision 1.5 88/01/21 17:19:35 root X... Several editorial changes. ADR. X... X... Revision 1.4 87/12/23 11:30:47 root X... Updated list of authors. Documented extended canon() capability. X... Integrated fluke changes in a little better. ADR. X... X... Revision 1.3 87/11/04 11:33:45 root X... Documented new keyword "while" which is equivalent to "if". ADR. X... X... Revision 1.2 87/08/13 17:08:05 root X... Changes from Jeff Stearns, fluke!jeff, for Sun. ADR. X... X... Revision 1.1 87/08/13 17:05:00 root X... Initial revision X... X... X.LP X.TL XEase: A Configuration Language Xfor Sendmail X.AU XJames S. Schoner X.AI XPurdue University Computing Center XWest Lafayette, Indiana 47907 X.AU XJeff P. Stearns X.AI XJohn Fluke Manufacturing Company XEverett, Washington 98206 X.AU XArnold D. Robbins X.AI XEmory University Computing Center XAtlanta, Georgia 30322 X.sp 2 X.I X.ce XABSTRACT X.R X.PP XThe rapid expansion of computer networks and ensuing variation among mailing Xaddress formats have made address interpretation an increasingly Xcomplex task. In the UNIX* 4.2BSD operating system, a program named X\fIsendmail\fR was introduced which provided a Xgeneral internetwork mail routing facility. This facility has significantly Xdiminished the complexity of handling address interpretation. X.PP X\fISendmail\fR's address interpretation is based on a rewriting Xsystem composed of Xa number of rewriting rules (or productions) arranged as part of a Xconfiguration file. Unfortunately, the syntactical format of a Xconfiguration file for \fIsendmail\fR is both terse and rigid, making it Xrather difficult to modify. The standard format certainly serves its Xpurpose, but, as Xthe need to change these configurations increases in frequency, a more Xreadable format (i.e., one that is similar to the format Xof modern programming languages) is required to permit reasonably Xquick modifications to the configuration. As a solution to this problem, X\fBEase\fR Xprovides a level of abstraction which eliminates most of the current Xsyntactic hindrances Xfaced by programmers who must reconfigure \fIsendmail\fR's Xaddress parsing scheme. X.PP XAs a high-level specification format, \fBEase\fR is proving to be an Xexcellent alternative to \fIsendmail\fR's cryptic Xconfiguration file syntax. The syntactic structures of \fBEase\fR Xare patterned after modern language constructs, making the language Xeasy to learn and easy to remember. The format of the address rewriting Xrule is perhaps the most significant syntactical improvement. It was Xundoubtedly Xthe most needed improvement. Nevertheless, every element of a configuration Xfile is structurally enhanced through the use of \fBEase\fR. X.FS X* UNIX is a registered trademark of AT&T. X.FE X.sp 2 X.NH XIntroduction X.PP XThe \fBEase\fR language is a high-level specification format for \fIsendmail\fR's Xconfiguration file. The motivation for its development Xwas to fulfill a goal of providing a readable and easily modifiable X\fIsendmail\fR configuration file format. \fBEase\fR fulfills this goal by Xshielding the programmer from the cryptic configuration specification required Xby \fIsendmail\fR and providing a high-level language with which the programmer Xmay specify all modifications to a configuration file. The development Xof Ease coincided with Xthe development of an \fBEase\fR translator, \fIet\fR, Xwhich translates a configuration file written Xin \fBEase\fR to an Xequivalent file of the standard format accepted by \fIsendmail\fR. X.NH XEase in Profile X.PP XAs will be seen in the next section, the syntax of \fBEase\fR is quite Xreadable and easy to learn. In order to acquire a relevant perspective Xon this issue, Xthe reader is advised to examine a raw configuration file for \fIsendmail\fR (the Xoutput Xof the \fBEase\fR translator, \fIet\fR, will do nicely). The raw syntax, while Xquite fitting for quick translation, can prove to be a programmer's nightmare. X.PP XUndoubtedly, one of the more prominent features of \fBEase\fR is the ability Xto attach Xnames to address fields. When address field names are well-chosen, a distinct, Xself-documenting quality becomes a visible part of the address rewriting Xrules. Ostensibly, address field names provide a new level of semantic Xabstraction. A brief comparison of the formats can be accomplished by examining Xthe following equivalent representations of an address pattern: X.DS X user_path@host_name (\fBEase\fR format) X $+@$- (raw format) X.DE XIn the above, \*Quser_path\*U represents a field of one or more address Xtokens, and \*Qhost_name\*U represents one address token exactly. These Xtoken fields are represented by \*Q$+\*U and \*Q$-\*U in the raw format. Clearly, Xthe \fBEase\fR format is preferable, not only for increased readability, but Xstructural comprehension as well. X.PP XOther features of \fBEase\fR include ruleset naming, long identifiers for Xmacros and classes, flow-of-control structures, and free formatting. In Xaddition, the C language preprocessor (cpp) can be used for file inclusion Xand conditionally defined code constructs. The next section describes Xthe \fBEase\fR language in complete detail. X.NH XEase Syntax* X.FS X* \fINo attempt is made to describe the complete semantic meaning Xassociated with all of the constructs of a sendmail configuration file. Items Xnot covered in this document include the semantic distinction among rulesets, Xthe special uses of Xpre-defined macros, and the method of building configuration files. To Xobtain this information, the reader is advised to refer to Xthe Sendmail Installation and Operation Guide (SMM:7 in the 4.3 BSD XUNIX System Manager's Manual), Xby Eric Allman.\fR X.FE X.PP XAt its highest level, \fBEase\fR can be viewed as a collection of Xblock-structures, where each block begins with a keyword and is followed by Xzero or more related definitions and/or declarations. There are ten distinct Xblock types. The following is Xa list containing all ten block keywords and the block type it denotes. X.TS Xcenter; Xl l . X\fIbind\fR -ruleset identifier bindings X\fImacro\fR -macro definitions X\fIclass\fR -class definitions X\fIoptions\fR -\fIsendmail\fR option definitions X\fIprecedence\fR -precedence definitions X\fItrusted\fR -trusted users X\fIheader\fR -mail header definitions X\fImailer\fR -mailer definitions X\fIfield\fR -address field definitions X\fIruleset\fR -address rewriting rules X.TE X.sp 1 XIn general, X.TS Xcenter ; Xl . X X* Letters are distinguished by case, X XT{ X* An \fBEase\fR identifier is defined to be a letter, followed by zero or Xmore letters, digits, underscores (_), or dashes (-), XT} X XT{ X* A literal newline or double quotation (") character may be included in Xany quoted string by preceding the character with a backslash (\\\\\), and XT} X XT{ X* \fBEase\fR source is preprocessed by the C language preprocessor (cpp), Xthus source comments (i.e., text enclosed by \*Q/*\*U and \*Q*/\*U) may appear Xanywhere as part of \fBEase\fR whitespace. XT} X.TE X.PP XFor notational convenience, this document specifies all reserved Xwords of the \fBEase\fR language in italics. In addition, quantities Xenclosed in angle brackets (<..>) represent arbitrary Xidentifiers, strings, or numbers. X.NH 2 XRuleset Identifier Bindings X.PP XA ruleset (a set of rewriting rules) is identified solely by an integer Xin \fIsendmail\fR's Xconfiguration file. \fBEase\fR, however, allows each ruleset to be named with Xa meaningful identifier. Since a special numeric association for each Xruleset is required by the address parsing scheme of \fIsendmail\fR, a \fIbind\fR Xblock must be present in any \fBEase\fR file which defines one or more Xrulesets. A X\fIbind\fR block consists of the keyword \fIbind\fR, followed by zero or more Xstatements of the form: X.TS Xcenter box; Xl . X<ruleset-id> = \fIruleset\fR <ruleset-number> ; X.TE XThe following example, X.sp 1 X\fIbind\fR X.PP XFINAL_RW = \fIruleset\fR 4; X.sp 1 Xspecifies that FINAL_RW, the final rewriting ruleset, is \fIsendmail\fR's ruleset Xnumber 4. X.NH 2 XMacro Definitions X.PP XA macro is an identifier which, when referenced in the text of a program, Xis replaced by its value, a string of zero or more characters. The value Xof a macro may include references to other macros, but not itself! \fISendmail\fR Xallows a maximum of 26 user-declared macros in its configuration file. In Xaddition, there are a number of pre-declared macros which have special meaning Xto \fIsendmail\fR (see Appendix A). \fBEase\fR macros are defined in X\fImacro\fR blocks. \fBEase\fR allows any macro to be declared X(which is equivalent to simply referencing it) before it is defined. A macro Xidentifier is replaced by its value when it is preceded by the character X\*Q$\*U. In addition, a macro reference inside a quoted string must always Xinclude braces ({}) around the macro identifier (for delimiting purposes). X.PP XA \fImacro\fR block consists of the keyword \fImacro\fR, followed by zero Xor more statements taking either of the following forms: X.TS Xcenter box; Xl . X<macro-identifier> = "<macro-value>" ; X<macro-identifier> = \fBconditional-expression\fR ; X.TE XThe \fBconditional-expression\fR format will be discussed Xlater. X.sp 1 XThe following example, X.sp 1 X\fImacro\fR X.PP Xfirst_name = "James"; X.PP Xlast_name = "Schoner"; X.PP Xwhole_name = "${first_name} ${last_name}"; X.sp 1 Xdefines the macros first_name, last_name, and whole_name, where whole_name Xis the string, "James Schoner". X.NH 2 XClass definitions X.PP XA class is denoted by an identifier representing a logical grouping of zero Xor more names. Classes are used to represent the range of values a token Xmay assume in the pattern matching of an address. Further discussion on the Xuse of classes will be deferred until address fields are described. X.PP XOne identifier may be used to distinctly represent both a macro Xand class (i.e., the set of macro identifiers and the set of class identifiers Xmay form a non-empty intersection). A name, or class element, may Xbe an identifier or any quoted word. X.PP XA \fIclass\fR block consists of the keyword \fIclass\fR, followed by zero Xor more statements taking any of the following forms: X.TS Xcenter box; Xl . X<class-identifier> = { <name1>, <name2>, <name3>, ... } ; X<class-identifier> = \fIreadclass\fR ( "<file-name>" ) ; X<class-identifier> = \fIreadclass\fR ( "<file-name>", "<read-format>" ) ; X.TE XThe second and third forms cause \fIsendmail\fR to read the names of the class Xfrom the named Xfile. The third form contains a read format, which should be a \fIscanf\fR(3) Xpattern yielding a single string. X.sp 1 XThe following example, X.sp 1 X\fIclass\fR X.PP Xcampus_hosts = { statistics, engineering, chemistry, physics, physics-2 } ; X.PP Xversions = { "1.0", "1.1", "4.0", "4.2", latest-and-greatest } ; X.PP Xphone_hosts = \fIreadclass\fR ( "/tmp/phonenet.list" ) ; X.sp 1 Xdefines the classes campus_hosts, versions, and phone_hosts. X.NH 2 XSendmail option definitions X.PP XA number of options to the \fIsendmail\fR program may be specified in Xan \fIoptions\fR Xblock. For a description of the various \fIsendmail\fR options and their Xvalues, see Appendix B. X.PP XAn X\fIoptions\fR block consists of the keyword \fIoptions\fR, followed by zero Xor more statements taking any of the following forms: X.TS Xcenter box; Xl l . X<option-identifier> = "<option-value>" ; X\fIo_delivery\fR = \fBspecial-value\fR ; X\fIo_handling\fR = \fBspecial-value\fR ; X.TE XAll but two options (\fIo_delivery\fR and \fIo_handling\fR) use the first Xform. To specify an option without a value, simply assign to it the null Xstring (""). The \fBspecial-value\fR field of the second and third form Xrefers to special values (non-quoted) which are specified in Appendix B. X.sp 1 XThe following example, X.sp 1 X\fIoptions\fR X.PP X\fIo_alias\fR = "/usr/lib/aliases" ; X.PP X\fIo_tmode\fR = "0600" ; X.PP X\fIo_delivery\fR = \fId_background\fR ; X.sp 1 Xsets the options \fIo_alias\fR, \fIo_tmode\fR, and \fIo_delivery\fR. X.NH 2 XPrecedence definitions X.PP XMessage headers may contain a \*QPrecedence:\*U field describing the precedence Xof the message class. Identifiers which may appear in the precedence field of Xa message are given precedence values in a configuration file \fIprecedence\fR Xdefinition. This association will be illustrated below in an example. X.PP XA \fIprecedence\fR block consists of the keyword \fIprecedence\fR, followed Xby zero or more statements of the form: X.KS X.TS Xcenter box; Xl . X<precedence-identifier> = <precedence-integer> ; X.TE X.KE XThe following example, X.sp 1 X\fIprecedence\fR X.PP Xspecial-delivery = 100; X.PP Xjunk = -100; X.sp 1 Xdefines the precedence level for the names \*Qspecial-delivery\*U and X\*Qjunk\*U. Thus, whenever the name \*Qjunk\*U appears in Xa \*QPrecedence:\*U field, the corresponding message class will be set to -100. X.NH 2 XTrusted users X.PP X\fISendmail\fR's \fB\-f\fR flag allows trusted users to override the sender's Xmachine address. Trusted users are listed in \fItrusted\fR blocks. A X\fItrusted\fR block consists of the keyword \fItrusted\fR, followed Xby zero or more sets of users taking the form: X.TS Xcenter box; Xl . X{ <user1>, <user2>, <user3>, ... } ; X.TE XThe following example, X.sp 1 X\fItrusted\fR X.PP X{ root, uucp, network } ; X.PP X{ acu, kcs, jss } ; X.sp 1 Xspecifies that the users root, uucp, network, acu, kcs, and jss can be trusted Xto use the \fIsendmail\fR flag, \fB\-f\fR. X.NH 2 XMail header definitions X.PP XThe format of the message headers inserted by \fIsendmail\fR is defined in one Xor more \fIheader\fR blocks in the configuration file. A \fIheader\fR block Xconsists of the keyword \fIheader\fR, followed by zero or more statements Xtaking any of the following forms: X.TS Xcenter box; Xl Xl Xl Xl Xl Xl Xl Xl Xl Xl Xl . X\fIfor\fR ( <mailer-flag1>, <mailer-flag2>, ... ) X \fIdefine\fR ( "<header-title>" , \fBheader-value\fR ) ; X X\fIfor\fR ( <mailer-flag1>, <mailer-flag2>, ... ) { X \fIdefine\fR ( "<header-title>" , \fBheader-value\fR ) ; X \fIdefine\fR ( "<header-title>" , \fBheader-value\fR ) ; X . X . X} ; X X\fIdefine\fR ( "<header-title>" , \fBheader-value\fR ) ; X.TE XThe first form is used to define one header for one or more mailer Xflags. The second form differs from the first in that more than one Xheader may be defined for a given set of flags. The third form is used to Xdefine a header, Xregardless of mailer flags. Refer to Appendix C for a list of \fBEase\fR Xidentifiers representing mailer flags. The header title is a simple Xstring of characters (no macro references), whereas the \fBheader-value\fR Xis a series of one or more strings and X\fBconditional-expressions\fP (discussed later). XConcatenation is implicit (as in \fIawk\fP). X.sp 1 XThe following example, X.DS X\fIheader\fR X X \fIdefine\fR ( "Subject:", "") ; X X \fIfor\fR ( \fIf_return\fR ) X \fIdefine\fR ( "Return-Path:", "<${\fIm_sreladdr\fR}>" ) ; X X \fIfor\fR ( \fIf_date\fR ) { X \fIdefine\fR ( "Resent-Date:", "${\fIm_odate\fR}" ) ; X \fIdefine\fR ( "Date:", "${\fIm_odate\fR}" ); X } ; X.DE Xdefines a \*QSubject\*U field for all mailers, regardless of their flags, a X\*QReturn-Path\*U field for mailers whose definition specifies Xthe flag, \fIf_return\fR, and the headers, \*QResent-Date\*U and \*QDate\*U, Xfor mailers whose definition specifies the flag, \fIf_date\fR. X.NH 2 XMailer Definitions X.PP X\fISendmail\fR's definition of a mailer (or an interface to one) occurs in a X\fImailer\fR block. A \fImailer\fR block consists of the keyword \fImailer\fR, Xfollowed by zero or more statements of the form: X.TS Xcenter box; Xl . X<mailer-identifier> { \fBmailer-spec\fR } ; X.TE XThe field, \fBmailer-spec\fR, is a list of zero or more of the Xfollowing attribute assignments (where successive assignment statements are Xseparated by commas): X.TS Xcenter ; Xl l Xl l Xl l Xl l Xl l Xl l Xl l . X\fIPath\fR = \fBstring-attribute\fR X\fIArgv\fR = \fBstring-attribute\fR X\fIEol\fR = \fBstring-attribute\fR X\fIMaxsize\fR = \fBstring-attribute\fR X\fIFlags\fR = { <mailer-flag1>, <mailer-flag2>, ... } X\fISender\fR = <sender-ruleset-id> X\fIRecipient\fR = <recipient-ruleset-id> X.TE XThe \fBstring-attribute\fR value can take the form of a quoted string X(possibly containing macro references) or a \fBconditional-expression\fR X(discussed later). X.sp 1 XThe following example, X.sp 1 X\fImailer\fR X.DS X local { X \fIPath\fR = "/bin/mail", X \fIFlags\fR = { \fIf_from\fR, \fIf_locm\fR }, X \fISender\fR = Sender_RW, X \fIRecipient\fR = Recip_RW, X \fIArgv\fR = "mail -d ${\fIm_ruser\fR}", X \fIMaxsize\fR = "200000" X } ; X.DE Xdefines a mailer named \*Qlocal\*U. X.NH 2 XAddress field definitions X.PP X\fISendmail\fR's address parsing scheme treats an address as a group of tokens X(an address token is precisely defined in the Arpanet protocol RFC822). In Xgeneral, \fIsendmail\fR divides an address into tokens based on a list of Xcharacters assigned as a string to the special macro \fIm_addrops\fR. These Xcharacters will individually be considered as tokens and will separate tokens Xwhen parsing is performed. X.PP XFor Xthe \fBEase\fR language, there is a distinct set of address tokens (defined Xbelow) which are used in combination to represent generic forms of Xaddresses. In Xaddition to literal address tokens, the pattern to be matched in a rewriting Xrule (often referred to as the LHS) may Xinclude field identifiers which match one of five possibilities: X.DS X - zero or more tokens X - one or more tokens X - one token exactly X - one token which is an element of an arbitrary class \fBX\fR X - one token which is not an element of an arbitrary class \fBX\fR X.DE XA particular field type may be assigned to one or more identifiers. Each Xfield identifier is associated with (or defined to be) a field type in Xa \fIfield\fR declarations block. A \fIfield\fR declarations block consists Xof the keyword \fIfield\fR, followed by zero or more field definitions of Xthe form: X.TS Xcenter box; Xl . X\fBfield-id-list\fR : \fBfield-type\fR ; X.TE XA \fBfield-id-list\fR is a list of one or more identifiers, each separated by Xa comma. A \fBfield-type\fR, on the other hand, is a representation of Xone of the five fields Xdescribed above. The syntax for each of the five forms follows: X.DS X \fImatch\fR ( 0* ) X \fImatch\fR ( 1* ) X \fImatch\fR ( 1 ) X \fImatch\fR ( 1 ) \fIin\fR <class-X> X \fImatch\fR ( 0 ) \fIin\fR <class-X> X.DE XThe star in the first two forms means: "or more". Thus, the first Xform would read: "match zero or more tokens". The fourth form describes Xa field where one token is matched from an arbitrary class (class-X), whereas Xthe fifth form describes a field where one token is matched if it is not of the Xgiven class (class-X). X.sp 1 XIn addition, the Sun release 3.0 version of \fIsendmail\fR supports several Xnew pattern matching operations represented by the following forms: X.DS X \fImatch\fR ( 0 ) \fImap\fR <macro-identifier-X> X \fImatch\fR ( 1 ) \fImap\fR <macro-identifier-X> X \fImatch host\fR X.DE XThe macro \*Qmacro-identifier-X\*U should be assigned the name of the Xrelevant YP map. X.sp 1 XThe following example, X.sp 1 X.DS X\fIfield\fR X anypath : \fImatch\fR ( 0* ); X recipient_host : \fImatch\fR ( 1 ); X local_site : \fImatch\fR ( 1 ) \fIin m_sitename\fR; X remote_site : \fImatch\fR ( 0 ) \fIin m_sitename\fR; X.DE Xdefines the fields anypath, recipient_host, local_site, and remote_site. X.NH 2 XAddress rewriting rules X.PP XAddress rewriting rules are grouped according to the function they perform. For Xexample, it is desirable to form a distinct group for those rewriting rules Xwhich perform transformations on recipient addresses. X.PP XSets of rewriting rules are defined in \fIruleset\fR blocks. A \fIruleset\fR Xblock consists of the keyword \fIruleset\fR, followed by zero or more Xruleset definitions of the form: X.TS Xcenter box; Xl . X<ruleset-id> { <rewriting-rule1> <rewriting-rule2> ... } X.TE XThe ruleset identifier, ruleset-id, must be defined in a \fIbind\fR block, as Xdescribed earlier. The rewriting rules have the form: X.DS X \fIif\fR ( <match-pattern> ) X <match-action> ( <rewriting-pattern> ) ; X.DE Xwhere match-pattern, rewriting-pattern, and match-action are described below. XAn alternative form is available: X.DS X \fIwhile\fR ( <match-pattern> ) X <match-action> ( <rewriting-pattern> ) ; X.DE Xwhich is somewhat more useful when the \*Qmatch-action\*U is \fIretry\fP X(see below). X.NH 3 XMatch-patterns X.PP XA match-pattern is a sequence of Ease address elements representing an Xaddress format. If the address being rewritten matches the pattern X\*Qmatch-pattern\*U, Xthen the address is reformatted using the pattern \*Qrewriting-pattern\*U, and Xthe corresponding Xaction (\*Qmatch-action\*U) is performed. The five distinct Ease address Xelements which may constitute a match-pattern are as follows: X.TS Xcenter ; Xl . X1. Field Identifiers (refer to previous section) XT{ X2. Non-alphanumeric characters (the exception is the case for literal Xdouble quotes, which must be preceded by a backslash (\\\\\) XT} X3. Macro references X4. Quoted strings ("...") X5. \fBConditional-expressions\fR (discussed later) X.TE XBelow are two sample match-patterns, each describing the same address format: X.DS X user-id @ hostname . $arpa_suffix X user-id @ hostname ".ARPA" X.DE Xwhere user-id and hostname are field identifiers, and arpa_suffix is a Xuser-defined macro with the value \*QARPA\*U. X.NH 3 XRewriting-patterns X.PP XA rewriting-pattern specifies the form in which to rewrite a matched Xaddress. The seven distinct elements which may be used to form Xa rewriting-pattern are as follows: X.TS Xcenter ; Xl . X XT{ X1. Non-alphanumeric characters (the exception is the case for literal Xdouble quotes, left parentheses, or right parentheses, each of which Xmust be preceded by a backslash (\\\\\). XT} X XT{ X2. A call to another ruleset. This is used to perform rewrites Xon a suffix of the rewriting-pattern. The proper use of this Xfeature will be demonstrated by example below. XT} X X3. Quoted strings ("..."). X X4. \fBConditional-expressions\fR (discussed later). X X5. A macro reference. X XT{ X6. A positional reference in the matched address. A positional Xreference takes the form: $<integer-position>. For example, X$3 references the value of the third \fBEase\fR address Xelement in the matched address. XT} X XT{ X7. Canonicalized host names of the form \fIcanon\fR (<id-token-list>), Xwhere \*Qid-token-list\*U is a list of one or more \*Qid-tokens.\*U XAn \*Qid-token\*U is a regular identifier, a quoted identifier (with Xdouble quotes), a macro reference yielding an identifier, Xa numeric internet specification (see below), Xa literal character (such as a \*Q.\*U or a \*Q[\*U), or a Xpositional reference in the matched address. The canonicalization of Xa host name is simply a mapping to its canonical (or official) form. XT} X X.TE XBelow are two sample rewriting-patterns: X.DS X $1 % $2 < @ $3 ".ARPA" > X OLDSTYLE_RW ( $1 ) X.DE XThe first form specifies an address such as a%b<@c.ARPA>, where a, b, and c Xrepresent matched identifiers or paths. The second form specifies a call to Xthe ruleset \*QOLDSTYLE_RW\*U, for old-style rewriting on the parameter X$1, which probably references the entire matched address. This will become Xclear in later examples. X.NH 3 XMatch-actions X.PP XWhen a ruleset is called, the address to be rewritten is compared (or matched) Xsequentially against the match-address of each rewriting rule. When a Xmatch-address describes the address \fIsendmail\fR is attempting to rewrite, the Xaddress is rewritten (or reformatted) using the rule's Xrewriting-pattern. Following this rewrite, the corresponding match-action Xis performed. There are four match-actions: X.TS Xcenter ; Xl l . X\fIretry\fR T{ X-a standard action which causes the rewritten address Xto be again compared to the match-address of the current rule. XT} X X\fInext\fR T{ X-an action which causes the rewritten address to be Xcompared to the match-address of the next rewriting rule of the current Xruleset. If the end of the list is reached, the ruleset returns the Xrewritten address. XT} X X\fIreturn\fR T{ X-an action which causes an immediate return of the Xruleset with the current rewritten address. XT} X X\fIresolve\fR T{ X-an action which specifies that the address has been Xcompletely resolved (i.e., no further rewriting is necessary). The X\fIresolve\fR action is described in more detail below. XT} X.TE X.PP XThe match-action, \fIresolve\fR, is special in that it terminates Xthe address rewriting altogether. The semantic structure of \fIsendmail\fR's Xrewriting scheme requires that a \fIresolve\fR action appear only in the Xruleset whose numerical binding is to the number zero. The \fIresolve\fR action Xmust specify three parameters: \fImailer\fR, \fIhost\fR, and \fIuser\fR. If Xthe \fImailer\fR is local, the \fIhost\fR parameter may be omitted. The X\fImailer\fR argument must be specified as a single word, macro, or positional Xreference in the matched address. The \fIhost\fR argument may be specified as Xa single word or as an expression which expands to a single word (e.g., X\fIhost\fR ($1 ".ARPA")). In addition, the \fIhost\fR argument may be a Xcanonicalization (as described above) or a numeric internet specification. The Xkeyword \fIhostnum\fR is used for numeric internet specifications, as in X\fIhostnum\fR ("128.61.1.1") or \fIhostnum\fR ( $2 ). The \fIuser\fR Xspecification is a rewriting-pattern, as described above. X.PP XIn general, the format of a \fIresolve\fR action will be as follows: X.DS X \fIresolve\fR ( \fImailer\fR ( <mailer-name> ), X \fIhost\fR ( <host-name> ), X \fIuser\fR ( <user-address>) ); X.DE XExamples of the match-action statement are shown below: X.DS X\fIfield\fR X anypath : \fImatch\fR (0*); X usr, path : \fImatch\fR (1*); X hostname : \fImatch\fR (1); X phone_host : \fImatch\fR (1) \fIin\fR phonehosts; X.DE X.DS X\fIruleset\fR X X EXAMPLE_RW { X X \fIif\fR ( anypath < path > anypath ) /* basic RFC821/822 parse */ X \fIretry\fR ( $2 ); X \fIif\fR ( usr " at " path ) /* \*Qat\*U -> \*Q@\*U */ X \fInext\fR ( $1 @ $2 ); X \fIif\fR ( @path: usr ) X \fIreturn\fR ( LOCAL_RW ( < @$1 > : $2 ) ); X \fIif\fR ( anypath < @phone_host".ARPA" > anypath ) X \fIresolve\fR ( \fImailer\fR ( tcp ), X \fIhost\fR ( relay.cs.net ), X \fIuser\fR ( $1 % $2 < @"relay.cs.net" > $3 ) ); X } X.DE X.PP XThe example above defines the ruleset \*QEXAMPLE_RW\*U, which contains four Xrewriting rules. The first rewriting rule discards all tokens of an address Xwhich lie on either side of a pair of angle brackets (<>), thereby Xrewriting the address as Xthe sequence of tokens contained within the angle brackets ($2). Following the Xaddress rewrite, the rule is applied again (\fIretry\fR). When the first rule Xfails to match the address being rewritten, the second rule is applied. X.PP XThe second Xrule simply replaces the word \*Qat\*U by the symbol \*Q@\*U. The \*Q\fInext\fR\*U Xaction specifies that if a match is made, a rewrite is performed and Xmatching continues at the next (or following) rule. X.PP XThe third rule illustrates Xthe use of the \*Q\fIreturn\fR\*U action, which is executed if the Xpattern \*Q@path: usr\*U Xdescribes the current format of the address being rewritten. In this example, Xthe \fIreturn\fR action returns the result of a call to ruleset \*QLOCAL_RW\*U, Xwhich rewrites the address \*Q<@$1>:$2\*U, where $1 and $2 are substituted Xwith the token(s) matched respectively by \*Qpath\*U and \*Qusr\*U. X.PP XThe fourth (and final) rule signals a resolution (and termination) of the Xrewriting process if the given pattern is matched. The resolution specifies Xthat the mailer \*Qtcp\*U will be used to deliver the message to the host X\*Qrelay.cs.net\*U. XThe \fIuser\fR parameter specifies the final form of the address Xwhich \fIsendmail\fR has just resolved. X.sp 2 X.PP XThe \fBEase\fR construct which remains to be examined is the X\fBconditional-expression\fR. The \fBconditional-expression\fR provides a Xmethod for Xconstructing strings based on the condition that some test macro is (or is not) Xset. The general form begins with the concatenation of a string and a X\fBstring-conditional\fR: X.DS X \fIconcat\fR ( <quoted-string>, \fBstring-conditional\fR ) X \fIconcat\fR ( \fBstring-conditional\fR, <quoted-string> ) X.DE XA \fBstring-conditional\fR assumes either of the following forms: X.DS X \fIifset\fR ( <macro-name>, <ifset-string> ) X \fIifset\fR ( <macro-name>, <ifset-string>, <notset-string> ) X.DE XA \fBstring-conditional\fR of the first form evaluates to \*Qifset-string\*U Xif the macro \*Qmacro-name\*U has been assigned a value; otherwise it Xevaluates to the null string. The second form behaves similarly, except Xthat the \fBstring-conditional\fR evaluates to \*Qnotset-string\*U, instead Xof the null string, if the macro \*Qmacro-name\*U has no value. X.sp 1 XThe following \fBconditional-expression\fR, X.DS X \fIconcat\fR ( "New ", \fIifset\fR ( city, "York", "Jersey" ) ) X.DE Xevaluates to the string "New York", if the macro \*Qcity\*U is set. Otherwise, Xthe \fBconditional-expression\fR evaluates to the string "New Jersey". X.NH XEase Translation X.PP XIt is important to note that \fBEase\fR is translated by a stand-alone Xtranslator to the raw configuration file format. No modifications were Xmade to the \fIsendmail\fR program itself. As a result, syntactical verification Xof a configuration file can be performed without invoking \fIsendmail\fR. X.PP XThe \fBEase\fR language is translated by invoking Xthe C language preprocessor (cpp) with \fBEase\fR source as input, then piping Xthe output as input to the \fBEase\fR translator (\fIet\fR). The \fBEase\fR Xtranslator may be invoked on the command line in one of four ways: X.TS Xcenter box ; Xl l . X\fIet\fR <input-file> <output-file> [read from a file, write to a file] X\fIet\fR <input-file> [read from a file, write to standard output] X\fIet\fR - <output-file> [read from standard input, write to a file] X\fIet\fR [read from standard input, write to standard output] X.TE X.NH XConclusion X.PP X\fBEase\fR is currently in use at the Purdue University Computing XCenter. Source code for the \fBEase\fR translator (\fIet\fR) may be Xobtained on request by writing to: X.DS XU.S. Mail: X James S. Schoner X c/o Kevin S. Braunsdorf X Purdue University Computing Center X Purdue University X West Lafayette, Indiana 47907 X XElectronic Mail: X ksb@j.cc.purdue.edu X.DE X.PP XMuch of the success of this project is attributable to the constant support Xand insight offered by Mark Shoemaker. To him, I owe a debt of gratitude. In Xaddition, I would like to thank Kevin Smallwood, Paul Albitz, and Rich Kulawiec Xfor their many notable suggestions and valuable insight. X.NH XAcknowledgements X.PP XArnold Robbins would like to acknowledge contributions from XStephen Schaefer of Bowling Green State University, XJeff Stearns of John Fluke Manufacturing Company, XRaymond A. Schnitzler of Bellcore, Xand XAndrew Partan of the Corporation for Open Systems. XThe good intentions of Rich Salz, of Bolt Beranak, and Newman, Xare also acknowledged. X.PP XThe most up to date version of \fBEase\fR should be gotten from the Xnearest USENET \fBcomp.sources.unix\fR archive site. END_OF_FILE if test 31245 -ne `wc -c <'doc/ease.paper'`; then echo shar: \"'doc/ease.paper'\" unpacked with wrong size! fi chmod +x 'doc/ease.paper' # end of 'doc/ease.paper' fi echo shar: End of archive 3 \(of 3\). cp /dev/null ark3isdone MISSING="" for I in 1 2 3 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 3 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.