Ease 3.0: High Level Language for Sendmail (Part 6 of 6)
Bruce Barnett
barnett at grymoire.crd.ge.com
Sat Feb 23 18:19:20 AEST 1991
#! /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 6 (of 6)."
# Contents: cfc/cfc.c
# Wrapped by barnett at grymoire on Sat Feb 23 01:13:56 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f cfc/cfc.c -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"cfc/cfc.c\"
else
echo shar: Extracting \"cfc/cfc.c\" \(39729 characters\)
sed "s/^X//" >cfc/cfc.c <<'END_OF_cfc/cfc.c'
X#ifndef lint
Xstatic char RCSid[] = "$Header: /home/kreskin/u0/barnett/Src/ease/cfc/RCS/cfc.c,v 3.0 1991/02/22 19:33:07 barnett Exp $";
X#endif
X
X/*
X * $Log: cfc.c,v $
X * Revision 3.0 1991/02/22 19:33:07 barnett
X * Many enhancements for IDA and HP sendmail.cf files
X *
X * Revision 2.2 1991/02/21 19:19:34 barnett
X * Fixed several bugs:
X * Multiple ifsets on one line
X * Better handling of # in comments
X * Support for escaping a '* and /' in a comment field
X *
X * Revision 2.1 1990/01/30 11:38:10 jeff
X * Enhancements by Bruce Barnett 89/1/23:
X * - Added some enhancements for SunOS 4.0 and Ultrix 3.0
X * - And a log of unusual grammar constructs
X *
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 at 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 */
Xint ida = 0; /* IDA sendmail options */
Xint sunos = 0; /* Special parsing for SunOS - bgb */
Xint DECos = 0; /* Special parsing for Ultrix - bgb */
X /* NOTE: can't use 'ultrix' cause of cpp */
Xint hpos = 0; /* HP/UX */
X
Xchar *classes = 0; /* list of classes defined */
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X extern int getopt ();
X extern int optind;
X extern char *optarg;
X int i,c;
X
X while ((c = getopt (argc, argv, "icdhusC:")) != EOF) {
X switch (c) {
X case 'c':
X compat = 1;
X break;
X case 'u':
X undoc = 1;
X break;
X case 's':
X sunos = 1;
X break;
X case 'd':
X DECos = 1;
X break;
X case 'i':
X ida = 1;
X break;
X case 'h':
X hpos = 1;
X break;
X case 'C':
X classes = optarg;
X break;
X case '?':
X default:
X fprintf (stderr, "usage: %s [ -[ids] ] [ -c ] [ -u ] [-C classes ]\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 version $Revision: 3.0 $*/\n");
X printf ("/* automatically from a sendmail.cf file */\n");
X printf ("/* It may need to be edited before feeding to ease. */\n");
X printf ("/***********************************************************/\n");
X /* let's generate something that might work */
X printf ("bind \n");
X for (i=0;i<=29;i++)
X printf ("\tRULESET_%d = ruleset %d;\n",i,i);
X /* SunOS uses ruleset 30. Other sendmails only support S0 to S29 */
X if (sunos)
X printf ("\tRULESET_30 = ruleset 30;\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 * Also class definitions - BGB
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 endruleset ();
X class ();
X break;
X case 'F':
X endruleset ();
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 register int j;
X /* try to be semi-intelligent about comments */
X
X /* if a blank line, keep as a blank line */
X if (buffer[1] == '\0')
X printf ("\n");
X else { /* non-blank comment */
X j=1;
X printf("/*");
X /* print ######## as /********* */
X while (buffer[j] == '#') {
X j++;
X printf("*");
X }
X /* print the rest of the line */
X while (buffer[j] != '\0') {
X switch (buffer[j]) {
X case '#':
X /* convert ### to *** */
X if (buffer[j+1] == '\0') {
X printf("*");
X } else if (buffer[j+1] == '#') /* a string of #### */
X while (buffer[j] == '#' && buffer[j+1] != '\0') {
X printf("*");
X j++;
X }
X else printf("#");
X break;
X case '*':
X if (buffer[j+1] == '/') {
X printf("*\\/");
X j++;
X } else printf("*");
X break;
X default:
X printf("%c", buffer[j]);
X break;
X }
X j++;
X } /* end while */
X if ( buffer[j-2] == '#' && buffer[j-1] == '#')
X printf("*/\n");
X else if ( buffer[j-2] != '#' && buffer[j-1] == '#')
X printf("*/\n");
X else if ( buffer[j-1] == ' ' || buffer[j-1] == '\t')
X printf("*/\n");
X else
X printf(" */\n");
X } /* end of non-blank */
X }
X
X/* ruleset --- name a ruleset */
X
Xruleset ()
X{
X static int first = 1;
X register char *cp = buffer + 1;
X int i;
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 if (classes && *classes ) {
X printf("\t/* defining classes %s */\n",classes);
X for (i=0;*(classes+i);i++) {
X printf ("\tany_in_%c : match (1) in %c;\n",*(classes+i),*(classes+i));
X printf ("\tany_not_in_%c : match (0) in %c;\n",*(classes+i),*(classes+i));
X }
X }
X /* let's make the default configuration nicer for SunOS - bgb */
X if (DECos || ida || hpos ) {
X printf ("\tany_in_myhostname : match (1) in c_myname;\n");
X }
X if (sunos) {
X/* printf ("\tany_in_V : match (1) in V;\n");
X printf ("\tany_not_in_V : match (0) in V;\n"); */
X printf ("/*\tany_in_map_? : match (1) map ?;\t*/\n");
X printf ("/*\tany_not_in_map_? : match (0) map ?;\t*/\n");
X printf ("\tany_in_etc_hosts : match host;\n");
X printf ("\tany_in_mydomainname : match (1) in c_mydomain;\n");
X printf ("\tany_in_myhostname : match (1) in c_myname;\n");
X }
X }
X
X printf ("ruleset\n\tRULESET_");
X while (cp && *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 && *cp && *cp != '\t')
X cp++;
X if (!*cp) {
X fprintf(stderr,
X "Unexpected EOL when expecting right hand side of rule\n");
X lhs(buffer+1);
X printf("\n\tMissingRightHandSide();\n");
X return;
X }
X *cp = '\0';
X
X cp++;
X while (cp && *cp && *cp == '\t')
X cp++;
X cp2 = cp;
X while (cp && *cp && *cp != '\t')
X cp++;
X if (*cp == '\t' && cp[1])
X {
X *cp = '\0';
X com++;
X cp++;
X while (cp && *cp && *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 register int open = 0;
X int ifset = 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 switch(*++cp) {
X case 'w':
X if (sunos || ida || DECos ) {
X printf (" any_in_myhostname ");
X break;
X } /* else fall through */
X case 'm':
X if (sunos ) {
X printf (" any_in_mydomainname ");
X break;
X } /* else fall through */
X default :
X printf (" any_in_%c ", *cp);
X }
X break;
X case '%' :
X /* YP map for SunOS */
X if ((cp+1) && sunos && (*(cp +1) == 'y') ) {
X printf (" any_in_etc_hosts"); ++cp;
X } else {
X printf (" any_in_map_%c", *++cp);
X }
X break;
X case '~':
X printf (" any_not_in_%c ", *++cp);
X break;
X case '?':
X printf (" ifset (%s, ", macro (*++cp));
X conditional++;ifset++;
X break;
X case '|':
X if ( ! conditional) die("lhs - $| without $?");
X if ( ifset) {
X printf("\", \"");
X } else {
X fprintf(stderr,"Got $| when not in ifset\n");
X putchar (',');
X }
X break;
X case '{':
X printf("ypalias ("); /* Ultrix */
X open++;
X break;
X case '"':
X printf("yppasswd ("); /* Ultrix */
X open++;
X break;
X case '.':
X putchar (')');
X conditional--;ifset--;
X break;
X case '#':
X /* IDA does something strange with this */
X printf("resolved(");
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 /* start a literal - but ignore the first space */
X if (*cp != ' ' && ! quoting)
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 while (open--)
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 char *cp1;
X register int open = 0;
X register int conditional = 0; /* true if in an ifset condition */
X register int quoting = 0; /* true if in a string */
X register int ifset = 0; /* true if in ifset(), like quoting */
X register int needconcat = 0; /* true if an $? on line (lookahead) */
X register int didconcat = 0; /* true if did the concat() */
X register int indbm = 0; /* true if in IDA $( $) construct */
X register int inalias = 0; /* true if in IDA $(@ $) construct */
X int canon = 0;
X int diddefault = 0;
X
X printf ("\t\t");
X
X /* Need to handle this line */
X /* R$+<@$=S> $:$1<@$2>$?R<$R>$. */
X for(cp1=cp;*cp1;cp1++) {
X if (*cp1 == '$' && (cp1+1) && *(cp1+1)== '?')
X needconcat = 1; /* there is an ifset on this line */
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 && ! ifset )
X {
X quoting = 0;
X putchar ('"');
X }
X switch (*++cp) {
X case '>':
X printf ("RULESET_");
X for (cp++; cp && *cp && isdigit (*cp); cp++)
X putchar (*cp);
X cp--;
X printf (" (");
X open++;
X break;
X case '[':
X if (quoting) {
X putchar('"');
X quoting--;
X }
X printf (" canon (");
X open++; canon++;
X break;
X case ']':
X putchar (')');
X open--;
X if (diddefault) {
X putchar (')');
X diddefault--;
X }
X break;
X case '{':
X printf ("ypmap (%s, ", macro (*++cp)); /* sunos */
X open++;
X break;
X case '}':
X putchar (')');
X open--;
X break;
X case '<':
X printf ("program (%s, ", macro (*++cp)); /* HP/UX */
X open++;
X break;
X case '?':
X if (didconcat) {
X printf ("\",");
X needconcat = 0; /* don't need this */
X }
X printf ("ifset (%s, \"", macro (*++cp));
X conditional++;
X ifset++;
X quoting++;
X break;
X case '|':
X if ( ! conditional) die("rhs - $| without $?");
X if ( ifset) {
X printf("\", \"");
X } else {
X fprintf(stderr,"Got $| when not in ifset\n");
X putchar (',');
X }
X break;
X case '.':
X if (ifset && quoting ) {
X putchar('"'); quoting--;
X }
X if (! ifset ) fprintf(stderr,"Got $. while not in ifset\n");
X putchar (')');
X if (open) {
X putchar(')');
X open--;
X }
X conditional--;
X ifset--;
X break;
X case '#':
X parseresolve(cp);
X goto out; /* string is exhausted */
X /* break; */
X case '@':
X if ( ! indbm) {
X printf ("return (");
X if (needconcat && cp+1 && cp+2 &&
X ! ((*(cp+1) == '$') && (*(cp+2) == '?'))){
X printf ("concat (\"");
X open++;didconcat++;
X }
X open++;
X } else {
X printf("\", \"");
X }
X break;
X case ':':
X if ( canon ) {
X printf("default ( ");
X canon--;diddefault++;
X } else if ( indbm ) {
X printf("default ( ");
X indbm--;diddefault++;
X } else {
X printf ("next (");
X if (needconcat && cp+1 && cp+2 &&
X ! ((*(cp+1) == '$') && (*(cp+2) == '?'))){
X printf ("concat (\"");
X open++;didconcat++;
X }
X open++;
X }
X break;
X case '(':
X if (*(cp+1) == '@') { /* then IDA alias lookup */
X cp++; /* point past '@' */
X printf("alias(");
X indbm++;
X open++;
X } else { /* lookup */
X printf("dbm(");
X printf("$%s, \"",macro(*cp++));
X }
X break;
X case ')':
X printf("))");
X open--;
X indbm--;
X break;
X case '&':
X printf(" eval(%s) ",macro(*(++cp)));
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 (ifset ) {
X if (quoting)
X printf ("${%s}", macro (*cp));
X else
X printf ("$%s", macro (*cp));
X } else { /* not not in ifset() */
X if (quoting)
X printf ("${%s}", macro (*cp));
X else
X printf ("$%s", macro (*cp));
X }
X break;
X }
X break; /* not a character that starts with a $ */
X default:
X if ( ifset && quoting ) {
X putchar(*cp);
X } else if (ifset && ! quoting) {
X if ( ispunct (*cp)) {
X putchar('"');quoting++;
X }
X putchar(*cp);
X } else { /* not ifset */
X if (ispunct (*cp))
X {
X if (quoting ) /* end a literal */
X {
X quoting = 0;
X putchar ('"');
X }
X /* else
X do nothing */
X } else {
X if (*cp != ' ' && ! quoting) /* start a literal */
X {
X quoting = 1;
X putchar ('"');
X }
X /* else
X do nothing */
X }
X putchar (*cp); /* print the character */
X }
X break;
X }
X } /* end of for */
Xout:
X if (quoting)
X putchar ('"');
X while (open--)
X putchar (')');
X printf (";\n");
X if (conditional)
X die ("rhs - $? without $.");
X}
X/* parseresolve - parse this mailer/host/user mess */
Xparseresolve(cp)
Xchar *cp;
X{
X int quoting = 0;
X int open = 0;
X char *addrops;
X addrops = ".:;%@!=/[]?#^,<>$"; /* should be defined from input file */
X printf ("resolve (mailer (");
X/* if (strncmp (cp+1, "local", 5) == 0
X || strncmp (cp+1, "error", 5) == 0
X || strncmp (cp+1, "LOCAL", 5) == 0
X || strncmp (cp+1, "ERROR", 5) == 0)
X goto skiphost;
Xloop1:
X*/
X /* this is a simple parser that scans the right
X hand side of a $# rule
X The format is usually
X "$# mailer $@ host $: user" or
X "$# mailer $: user" or
X "$# mailer $: something with a $macro" or
X "$# $M $: user" or
X "$# $1 " (IDA sendmail )
X
X Note that there may be special constructs
X in the host field, i.e.
X "$1", "[$2]", "$w", or "$K".
X and in the user field. Esp. in the error mailer:
X "$1 < @$2 > $4", or
X "Never heard of host $2 in domain $m "
X "$n" which should become -> "$m_daemon"
X */
X /* pointing to '#' */
X cp++;
X /* output any character not a '$' */
X while (cp && *cp && *cp != '$' ) {
X /* skip spaces in the mailer field */
X if ( *cp != ' ' ) putchar(*cp);
X cp++;
X }
X if (!cp || !*cp ) goto out;
X /* currently pointing to a "$" */
X /* we may now be pointing to:
X $@ - the host name
X $: - the user
X or a macro
X or nothing?! (*cp == 0);
X /* don't look at the '$' */
X cp++;
X if (!cp || !*cp ) goto out;
X if (*cp == ':') goto parseuser;
X if (*cp != '@' ) {
X /* must be a macro name */
X printf ("$%s",macro(*cp++));
X /* now skip to the $@ */
X if (!cp || !*cp ) goto out;
X while (cp && *cp && *cp == ' ') cp++;
X if (!cp || !*cp ) goto out;
X if (*cp == '$') cp++;
X else
X fprintf(stderr,
X "Error: found %c when expecting a '$' on line %d\n",
X *cp,line);
X if (*cp == ':') goto parseuser;
X if (*cp == '@') cp++;
X else
X fprintf(stderr,
X "Error: found %c when expecting a '@' on line %d\n",
X *cp,line);
X } else {
X /* I did see the '@' of the $@ */
X cp++;
X }
X /* print host name ($@host ) */
X printf ("),\n\t\t\t\thost (");
X for (;cp && *cp;cp++) {
X if (*cp != '$') {
X putchar (*cp);
X } else {
X /* it might be the $: */
X if (!*(cp+1)) goto out;
X if ( *(cp+1) == ':') {
X cp++; /* parseuser expects ':' */
X goto parseuser;
X } else {
X putchar(*cp++); /* print '$' */
X printf("%s", macro(*cp)); /* and next */
X }
X }
X }
X parseuser:
X printf ("),\n\t\t\t\tuser (");
X /* *cp == ':', now look for user = $n */
X /* maybe *cp == 0 */
X if ( !*cp ) goto out;
X if (*cp != ':' )
X fprintf(stderr,
X "Expected ':', found '%c' after '$' on line %d\n",*cp,line);
X /* looking at the user string */
X quoting = 0;
X for (cp++; cp && *cp; cp++) {
X if (quoting ) {
X/* if (isalnum(*cp) || isspace(*cp)) { */
X if (*cp == '"') {
X printf("\\\""); /* print "\" */
X quoting++;
X } else if (*cp == '\\') {
X printf("\\"); /* print "\" and the next character */
X putchar(*++cp);
X } else if (!index(addrops,*cp)) {
X /* not one of those address characters */
X putchar (*cp);
X } else { /* maybe it's a dollar sign? */
X quoting=0;
X printf("\" %c",*cp);
X if (*cp == '$') {
X cp++; /* This may not be used at all - but it can't hurt */
X if (*cp == '>' ) { /* IDA sendmail */
X cp++;
X printf(" RULESET_");
X while (cp && *cp && *cp >= '0' && *cp <= '9') putchar(*cp++);
X printf("(");
X open++;
X } else {
X printf("%s",macro(*cp));
X }
X }
X }
X } else { /* not quoting */
X if ( *cp == '$' ) {
X cp++;
X if (*cp == '>' ) { /* IDA sendmail */
X cp++;
X printf("retry (RULESET_");
X while (cp && *cp && *cp >= '0' && *cp <= '9') putchar(*cp++);
X printf("("); open++;
X open++;
X } else {
X putchar ('$'); /* print $ */
X printf("%s",macro(*cp)); /* and macro */
X }
X } else if (*cp && (index (addrops,*cp))) {
X putchar(*cp);
X } else if (*cp == '"') {
X printf("\"\\\"");quoting++; /* print "\" */
X } else {
X quoting = 1;
X printf(" \"%c",*cp);
X }
X } /* end of quoting/not quoting */
X }
X if (quoting) printf("\"");
X out:
X while (open--) printf(")");
X printf ("))");
X} /* end parseresolve () */
X/* def --- define a macro */
X
Xdef ()
X{
X register char *mac = buffer + 1, *value = buffer + 2;
X register int conditional = 0;
X register int concat = 0;
X register int quote = 0;
X register int ifset = 0;
X
X
X printf ("macro\n\t%s = ", macro (*mac));
X/* fprintf(stderr,"mac=%c, value=%s\n",*mac,value); */
X
X/* This is tricky, we want the form:
X *
X * Dq$g$?x$x$.
X * to become
X * macro
X * m_defaddr = concat ("${m_sreladdr}", ifset (m_sname," (${m_sname})"));
X * and
X * Dq$?x$x $.<$g>
X * to become
X * macro
X * m_defaddr = concat (ifset (m_sname," (${m_sname})"),"<${m_sreladdr}>" );
X *
X * One problem is the form
X * Dq$?x$x <$g>$|$g$.
X *
X *
X */
X concat = 0;
X quote = 0;
X conditional = 0;
X ifset = 0;
X if (! *value ) printf("\"\""); /* unusual error - just print " and let
X the end of the loop after the while
X clean it up */
X while (*value)
X {
X switch (*value) {
X case '$':
X switch (*++value) {
X /* $:$?E$1%$2.dnet<@$E.LOCAL>$3$|$1<@$2.dnet>$3$.
X Dq$?x$x <$g>$|$g$.
X Dq$?x$!x <$g>$|$g$.
X */
X case '?':
X /* Another special case %$&*!
X * if the start of the string is $?,
X * but the end is NOT $., then we need a concat
X */
X if (*(value+strlen(value)-1) == '.' &&
X *(value+strlen(value)-2) == '$') {
X /* just use ifset with no concat */
X printf ("ifset (%s, ", macro (*++value));
X conditional++;ifset++;
X /* Still need a quote character,
X next characters might be a $!x */
X if ((*(value+1) == '$') && (*(value+2) == '!')) {
X value++;value++;
X printf("\"${quote(%s)} ",macro(*++value));
X quote++;
X } else {
X printf("\"");quote++;
X }
X } else {
X printf ("concat( ifset (%s, \"", macro (*++value));
X conditional++;quote++;concat++;ifset++;
X }
X break;
X case '|':
X if ( ! conditional) die("def - $| without $?");
X if ( ifset) {
X printf("\", \"");
X } else {
X fprintf(stderr,"Got $| when not in ifset\n");
X putchar (',');
X }
X break;
X case '.':
X if (quote) {
X putchar('"');quote--;
X }
X putchar (')');
X conditional--;ifset--;
X /* not EOL, must be in concat(ifset( ,) */
X if (*(value+1)) putchar(',');
X break;
X case '!': /* IDA sendmail - this code never gets executed */
X printf("quote("); concat++;
X break;
X default:
X /* see if *(value+1) == '$' and *(value+2) == '?' */
X if (!concat && (strlen(value)>2)
X && (*(value+1) == '$')
X && (*(value+2) == '?')) {
X printf ("concat (\"${%s}\", ", macro (*value));
X /* I'm gonna need a concat */
X concat++;
X } else {
X if (!quote) {
X printf("\"${%s}", macro (*value));
X quote++;
X } else {
X printf ("${%s}", macro (*value));
X }
X }
X break;
X }
X break;
X case '"' :
X if (quote) {
X printf ("\\\"");
X } else {
X printf("\"\\\"");
X quote++;
X }
X break;
X default:
X if ( ! quote ) {
X putchar('"');
X quote++;
X }
X putchar (*value);
X break;
X }
X value++;
X }
X if ( quote ) putchar('"');
X if (concat) {
X putchar (')');
X concat--;
X }
X printf (";\n");
X if (conditional)
X die ("def - $? without $.");
X}
X
X/* class --- define a class list */
X
Xclass ()
X{
X register char *name = buffer + 1, *value = buffer + 2;
X int havepunct;
X char *s;
X
X
X printf ("class\n\t");
X switch (name[0]) {
X case 'w' : printf("c_myname"); break;
X case 'm' : if (sunos) { printf("c_mydomain"); break; }
X /* fall through if not SunOS */
X default: printf("%c",name[0]);
X }
X
X printf (" = { ");
X
X while (value && *value && isspace (*value))
X value++;
X
X /* a class may be a series of punctuation characters e.g. IDA */
X /* also watch for spaces on the end of a line and avoid ',)' */
X
X while (value && *value)
X {
X /* get first field */
X /* look for first space or EOL */
X for (s=value,havepunct=0;*s && ! isspace (*s);s++)
X if (ispunct(*s)) havepunct = 1;
X
X /* field is from *value to *s
X if there is a punctuation char, havepunt == 1 */
X if (havepunct) putchar('"');
X while (value < s ) {
X if (*value == '"') putchar('\\'); /* escape quotes */
X if (*value == '$' ) printf ("${%s}", macro (*++value));
X else putchar(*value);
X value++;
X }
X if (havepunct) putchar('"');
X /* if not EOL, put a comma there
X but watch out for extra spaces.....
X
X so scan over spaces, then look at the next character.
X If not EOL, print ", ". */
X
X while (value && *value && isspace(*value)) value++;
X if (*value && !isspace(*value)) printf (", ");
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 && *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 != '=') cp++; cp++
X#define value() for (; cp && *cp && *cp != ','; cp++) putchar (*cp); cp++
X
Xloop:
X while (cp && *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 /* IDA has ruleset/ruleset */
X for (; *cp && *cp != ',' && *cp != '/'; cp++)
X putchar (*cp);
X if (ida && cp && (*cp == '/' )) {
X putchar (*cp++);
X printf("RULESET_");
X value ();
X } else {
X cp++ ;
X }
X break;
X
X case 'S':
X skipname ();
X printf ("Sender = RULESET_");
X /* IDA has ruleset/ruleset */
X for (; *cp && *cp != ',' && *cp != '/'; cp++)
X putchar (*cp);
X if (ida && cp && (*cp == '/' )) {
X putchar (*cp++);
X printf("RULESET_");
X value ();
X } else {
X cp++ ;
X }
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 register int concat = 0;
X register int quote = 0;
X register int ifset = 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 != '?'; 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 /* skip over any spaces */
X while ( cp && *cp && isspace(*cp)) cp++;
X /* but if we are now at the end of the line, we must fake
X an entry for "" */
X if ( cp && *cp ) printf ("\", "); /* don't print next " yet, see if it is a concat */
X else if (cp && ! *cp ) printf("\", \"\"");
X else if (!cp) {
X printf("\"");
X fprintf(stderr,"I didn't expect this!\n");
X }
X
X quote = concat = conditional = ifset = 0;
Xbody:
X while (cp && *cp)
X {
X switch (*cp) {
X case '$':
X switch (*++cp) {
X case '?':
X /* if we are not in a concat, then start one */
X if ( ! concat ) {
X printf("concat (");
X concat++;
X } else { /* we are in one */
X if (quote) {
X printf("\"");quote--;
X }
X printf("), concat (");
X }
X if (quote) {
X printf("\",");quote--;
X }
X printf ("ifset (%s, \"", macro (*++cp));
X conditional++; quote++;ifset++;
X break;
X case '|':
X if ( ! conditional) die("header - $| without $?");
X if ( ifset) {
X printf("\", \"");
X } else {
X fprintf(stderr,"Got $| when not in ifset\n");
X putchar (',');
X }
X break;
X case '.':
X if (quote) {
X putchar('"');quote--;
X }
X putchar (')');
X conditional--;ifset--;
X if (concat) {
X /* this is messy - There may be more than one $? on a line */
X
X if (cp+1) { /* if there is more on the line */
X printf(", ");
X }
X }
X break;
X default: /* a $<letter> */
X /* see if *(cp+1) == '$' and *(cp+2) == '?' */
X if (!concat && (strlen(cp)>2)
X && (*(cp+1) == '$')
X && (*(cp+2) == '?')) {
X printf ("concat (\"${%s}\", ", macro (*cp));
X /* I'm gonna need a concat */
X concat++;
X } else {
X if (!quote) {
X printf("\"${%s}", macro (*cp));
X quote++;
X } else {
X printf ("${%s}", macro (*cp));
X }
X }
X break;
X }
X break;
X case '"' :
X printf ("\\\"");
X break;
X default:
X if ( ! quote ) {
X putchar('"');
X quote++;
X }
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 if ( ! quote ) {
X putchar('"');
X quote++;
X }
X printf ("\\\n");
X cp = buffer + 1;
X
X goto body;
X }
X else
X ungets (buffer);
X }
X else
X ungets ((char *) NULL);
X
X if ( quote ) {
X putchar('"');
X }
X if (concat) {
X putchar(')');
X concat--;
X }
X printf (");\n");
X
X if (flags)
X printf ("\t};\n");
X
X if (conditional)
X die ("header translation problem: $? without $.");
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 if ( *cp && *cp == ' ') cp++; /* skip over spaces in the begining */
X while (cp && *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/* it may also be a blank 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 'k': /* The official domain name for this site */
X return ("m_uucpname"); /* IDA */
X
X case 'l': /* The format of the UNIX from line */
X return ("m_ufrom");
X
X case 'm': /* The Domain Name (SunOS) */
X if (sunos) {
X return ("m_domain");
X } else {
X buf[0] = c;
X return (buf);
X }
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 case '"': /* you can get a quote charater in some macro definitions */
X return ("\\\"");
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 'H': return ("f_mail11"); /* Ultrix 3.0 */
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 case 'B': return ("f_bsmtp"); /* IDA sendmail */
X case 'V': return ("f_relativize"); /* IDA sendmail */
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 'b': return ("o_maxempty"); /* SunOS 4.0 */
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 'h': return ("o_maxhops"); /* SunOS 4.0 */
X case 'i': return ("o_skipd");
X case 'I': return ("o_nameserver"); /* HP/UX */
X/* case 'K': Keyed Database (IDA) */
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 'R': return ("o_nfs"); /* SunOS 4.0 */
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': if (sunos ) return ("o_aliasfile"); else docompat ("o_newproc");
X case 'y': docompat ("o_recipfactor");
X case 'z': docompat ("o_prifactor");
X case 'Z': docompat ("o_waitfactor");
X case '/': return ("o_envelope"); /* IDA */
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_cfc/cfc.c
if test 39729 -ne `wc -c <cfc/cfc.c`; then
echo shar: \"cfc/cfc.c\" unpacked with wrong size!
fi
chmod +x cfc/cfc.c
# end of overwriting check
fi
echo shar: End of archive 6 \(of 6\).
cp /dev/null ark6isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 6 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
--
Bruce G. Barnett barnett at crd.ge.com uunet!crdgw1!barnett
More information about the Alt.sources
mailing list