Path: utzoo!utgpu!cs.utexas.edu!sdd.hp.com!caen!dali.cs.montana.edu!rpi!crdgw1!barnett From: barnett@grymoire.crd.ge.com (Bruce Barnett) Newsgroups: alt.sources Subject: Ease 3.0: High Level Language for Sendmail (Part 6 of 6) Message-ID: Date: 23 Feb 91 07:19:20 GMT Sender: news@crdgw1.crd.ge.com Reply-To: barnett@crdgw1.ge.com Distribution: alt Organization: GE Corp. R & D, Schenectady, NY Lines: 1779 #! /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 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@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 X#include 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 $ */ 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