home *** CD-ROM | disk | FTP | other *** search
- #ifndef lint
- static char RCSid[] = "$Header: cfc.c,v 1.3 87/04/08 10:23:02 root Locked $";
- #endif
-
- /*
- * $Log: cfc.c,v $
- * Revision 1.3 87/04/08 10:23:02 root
- * Small bug fixes, compatibility option added, also warnings for
- * unrecognized flags and options. ADR.
- *
- * Revision 1.2 87/02/18 15:26:39 root
- * Fix to recognize multidigit ruleset numbers in $> (calls) in RHS. ADR.
- *
- * Revision 1.1 87/02/16 15:25:00 arnold
- * Initial revision
- *
- * Revision 1.1 87/02/16 15:25:00 arnold
- * Initial revision
- *
- */
-
- /*
- * cfc.c
- *
- * Sendmail cf file compiler.
- * Reads a raw sendmail.cf file and produces ease source.
- *
- * There are very few comments in this source. You will need both the
- * "Sendmail Installation and Operation Guide" and the paper on Ease
- * to really understand this.
- *
- * Arnold Robbins
- * Emory University Computing Center
- * 2/87
- */
-
- #include <stdio.h>
- #include <ctype.h>
-
- char buffer[BUFSIZ];
- int line = 0;
- int inruleset = 0;
-
- extern char *macro (); /* convert sendmail to ease macro names */
- extern char *mflags (); /* convert sendmail to ease mailer flag names */
- extern char *optionname (); /* convert sendmail to ease option names */
- extern char *delivoption (); /* delivery options */
- extern char *handle_option (); /* handling options */
-
- extern char *ngets (); /* buffered gets () routine */
- extern void ungets (); /* put a buffer back for getting */
-
- #define endruleset() if (inruleset) { inruleset = 0; printf ("\t}\n"); }
-
- int compat = 0; /* complain about new 4.3 options & flags */
-
- main (argc, argv)
- int argc;
- char **argv;
- {
- if (argc > 1)
- {
- if (strcmp (argv[1], "-c") == 0)
- compat = 1;
- else
- {
- fprintf (stderr, "usage: %s [ -c ]\n", argv[0]);
- fprintf (stderr, "illegal argument '%s' ignored\n",
- argv[1]);
- }
- }
-
- printf ("/******************************************************/\n");
- printf ("/* This ease file generated by cfc from a sendmail.cf */\n");
- printf ("/* file. It must be edited by hand before being fed */\n");
- printf ("/* to ease! */\n");
- printf ("/******************************************************/\n");
- printf ("\n#define eval(x)\"$&\"x /* for RUTGERS $& */\n");
- printf ("\n\nbind\n\t/* RULESET BINDINGS GO HERE (cfc) */\n\n");
-
- /*
- * For perfection, everything but the comment and rule cases
- * should do an endruleset (), but practically speaking, it is
- * usually only the mailer new ruleset definitions that end a
- * previous ruleset. Occasionally a macro, too.
- */
-
- while (ngets (buffer) != NULL)
- {
- line++;
- switch (buffer[0]) {
- case '#':
- comment ();
- continue; /* skip code to end ruleset */
- case 'S':
- endruleset ();
- ruleset ();
- continue; /* skip code to end ruleset */
- case 'R':
- rule ();
- continue; /* skip code to end ruleset */
- case 'D':
- endruleset ();
- def ();
- break;
- case 'C':
- class ();
- break;
- case 'F':
- fileclass ();
- break;
- case 'M':
- endruleset ();
- mailer ();
- break;
- case 'H':
- header ();
- break;
- case 'O':
- option ();
- break;
- case 'T':
- trusted ();
- break;
- case 'P':
- precedence ();
- break;
- default:
- other ();
- continue; /* skip code to end ruleset */
- }
- endruleset ();
- }
- endruleset (); /* just in case */
- }
-
- /* comment --- produce a comment */
-
- comment ()
- {
- static char format[] = "/* %s */\n";
- register int i = strlen (buffer) - 1;
-
- /* try to be semi-intelligent about comments */
-
- if (buffer[1] == '\0')
- printf ("\n");
- else if (isspace (buffer[1]) && buffer[i] != '#')
- {
- for (i = 1; isspace (buffer[i]); i++)
- ;
- printf (format, buffer + i);
- }
- else
- printf (format, buffer);
- }
-
- /* ruleset --- name a ruleset */
-
- ruleset ()
- {
- static int first = 1;
- register char *cp = buffer + 1;
-
- if (first)
- {
- first = 0;
- printf ("\n/* These are sample field definitons (cfc) */\n");
- printf ("\nfield\n\tzero_or_more : match (0*);\n");
- printf ("\tone_or_more : match (1*);\n");
- printf ("\texactly_one : match (1);\n");
- printf ("\tany_in_? : match (1) in ?;\n");
- printf ("\tany_not_in_? : match (0) in ?;\n\n");
- }
-
- printf ("ruleset\n\tRULESET_");
- while (*cp && ! isspace (*cp))
- {
- putchar (*cp);
- cp++;
- }
-
- printf (" {");
- if (*cp)
- printf ("\t/* %s */", cp);
- putchar ('\n');
- inruleset++;
- }
-
- /* rule --- print out a rule */
-
- rule ()
- {
- register char *cp = buffer + 1;
- register char *cp2;
- register int com = 0;
-
- /* first, split it up into LHS, RHS, COMMENT */
-
- while (*cp != '\t')
- cp++;
- *cp = '\0';
-
- cp++;
- while (*cp == '\t')
- cp++;
- cp2 = cp;
- while (*cp && *cp != '\t')
- cp++;
- if (*cp == '\t' && cp[1])
- {
- *cp = '\0';
- com++;
- cp++;
- while (*cp == '\t')
- cp++;
- }
-
- /* now print */
- lhs (buffer + 1); /* left hand side */
- if (com)
- printf ("\t/* %s */", cp);
- putchar ('\n');
- rhs (cp2); /* right hand side */
- }
-
- /* lhs --- left hand side of a production */
-
- lhs (text)
- char *text;
- {
- register char *cp = text;
- register int conditional = 0;
- register int quoting = 0;
-
- printf ("\tif (");
- for (; *cp; cp++)
- {
- switch (*cp) {
- case '$':
- if (quoting)
- {
- quoting = 0;
- putchar ('"');
- }
- switch (*++cp) {
- case '*':
- printf (" zero_or_more ");
- break;
- case '+':
- printf (" one_or_more ");
- break;
- case '-':
- printf (" exactly_one ");
- break;
- case '=':
- printf (" any_in_%c ", *++cp);
- break;
- case '~':
- printf (" any_not_in_%c ", *++cp);
- break;
- case '?':
- printf (" ifset (%s, ", macro (*++cp));
- conditional++;
- break;
- case '|':
- printf (", ");
- break;
- case '.':
- putchar (')');
- conditional--;
- break;
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- printf ("$%c", *cp);
- break;
- default:
- if (quoting)
- printf ("${%s}", macro (*cp));
- else
- printf ("$%s", macro (*cp));
- break;
- }
- break;
- default:
- if (ispunct (*cp))
- {
- if (quoting) /* end a literal */
- {
- quoting = 0;
- putchar ('"');
- }
- /* else
- do nothing */
- }
- else
- {
- if (! quoting) /* start a literal */
- {
- quoting = 1;
- putchar ('"');
- }
- /* else
- do nothing */
- }
- putchar (*cp); /* print the character */
- break;
- }
- }
- if (quoting)
- putchar ('"');
- if (conditional)
- die ("lhs");
- printf (")");
- }
-
- /* rhs --- right hand side of a production */
-
- rhs (text)
- char *text;
- {
- register char *cp = text;
- char *index ();
- register int open = 0;
- register int conditional = 0;
- register int quoting = 0;
-
- printf ("\t\t");
-
- if (*cp == '$' && index ("#@:", cp[1]) != NULL)
- ; /* not the default */
- else
- {
- printf ("retry (");
- open++;
- }
-
- for (; *cp; cp++)
- {
- switch (*cp) {
- case '$':
- if (quoting)
- {
- quoting = 0;
- putchar ('"');
- }
- switch (*++cp) {
- case '>':
- printf ("RULESET_");
- for (cp++; *cp && isdigit (*cp); cp++)
- putchar (*cp);
- cp--;
- printf (" (");
- open++;
- break;
- case '[':
- printf ("canon (");
- open++;
- break;
- case ']':
- putchar (')');
- open--;
- break;
- case '?':
- printf ("ifset (%s, ", macro (*++cp));
- conditional++;
- break;
- case '|':
- putchar (',');
- break;
- case '.':
- putchar (')');
- conditional--;
- break;
- case '#':
- printf ("resolve (mailer (");
- if (strncmp (cp+1, "local$", 6) == 0
- || strncmp (cp+1, "error$", 6) == 0)
- goto skiphost;
- loop1:
- for (cp++; *cp != '$'; cp++)
- putchar (*cp);
- *cp++;
- if (*cp != '@')
- {
- printf ("$%c", *cp);
- goto loop1;
- }
- printf ("),\n\t\t\t\thost (");
- skiphost:
- loop2:
- for (cp++; *cp != '$'; cp++)
- putchar (*cp);
- *cp++;
- if (*cp != ':')
- {
- printf ("$%c", *cp);
- goto loop2;
- }
- printf ("),\n\t\t\t\tuser (");
- for (cp++; *cp; cp++)
- putchar (*cp);
- printf ("))");
- goto out; /* string is exhausted */
- break;
- case '@':
- printf ("return (");
- open++;
- break;
- case ':':
- printf ("next (");
- open++;
- break;
- case '&': /* RUTGERS addition */
- printf ("eval (%s)", macro (*++cp));
- break;
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- printf ("$%c", *cp);
- break;
- default:
- if (quoting)
- printf ("${%s}", macro (*cp));
- else
- printf ("$%s", macro (*cp));
- break;
- }
- break;
- default:
- if (ispunct (*cp))
- {
- if (quoting) /* end a literal */
- {
- quoting = 0;
- putchar ('"');
- }
- /* else
- do nothing */
- }
- else
- {
- if (! quoting) /* start a literal */
- {
- quoting = 1;
- putchar ('"');
- }
- /* else
- do nothing */
- }
- putchar (*cp); /* print the character */
- break;
- }
- }
- out:
- if (quoting)
- putchar ('"');
- while (open--)
- putchar (')');
- printf (";\n");
- if (conditional)
- die ("rhs");
- }
-
- /* def --- define a macro */
-
- def ()
- {
- register char *mac = buffer + 1, *value = buffer + 2;
- register int conditional = 0;
-
- printf ("macro\n\t%s = \"", macro (*mac));
-
- while (*value)
- {
- switch (*value) {
- case '$':
- switch (*++value) {
- case '?':
- printf ("ifset (%s, ", macro (*++value));
- conditional++;
- break;
- case '|':
- putchar (',');
- break;
- case '.':
- putchar (')');
- conditional--;
- break;
- default:
- printf ("${%s}", macro (*value));
- break;
- }
- break;
- default:
- putchar (*value);
- break;
- }
- value++;
- }
- printf ("\";\n");
- if (conditional)
- die ("def");
- }
-
- /* class --- define a class list */
-
- class ()
- {
- register char *name = buffer + 1, *value = buffer + 2;
-
- printf ("class\n\t%c = { ", *name);
-
- while (*value && isspace (*value))
- value++;
-
- while (*value)
- {
- if (isspace (*value))
- {
- printf (", ");
- while (isspace (*value))
- value++;
- value--; /* cancel loop */
- }
- else
- putchar (*value);
- value++;
- }
- printf (" };\n");
- }
-
- /* fileclass --- define a class that is to be read from a file */
-
- fileclass ()
- {
- register char *name = buffer + 1, *value = buffer + 2;
-
- printf ("class\n\t%c = readclass (\"", *name);
- for (; *value && !isspace (*value); value++)
- putchar (*value);
- putchar ('"');
- while (*value && isspace (*value))
- value++;
- if (*value)
- printf (", \"%s\"", value);
- printf (");\n");
- }
-
- /* mailer --- convert a mailer specification */
-
- mailer ()
- {
- register char *cp = buffer + 1;
-
- printf ("mailer\n\t");
- for (; *cp != ','; cp++)
- putchar (*cp);
- cp++;
- printf (" {\n"); /* just did mailer name */
-
- #define skipname() cp++; while (*cp != '=') cp++; cp++
- #define value() for (; *cp && *cp != ','; cp++) putchar (*cp); cp++
-
- loop:
- while (*cp && isspace (*cp))
- cp++;
-
- printf ("\t\t");
- switch (*cp) {
- case 'A':
- skipname ();
- printf ("Argv = \"");
- for (; *cp && *cp != ','; cp++)
- {
- if (*cp == '$') /* XXX: assume no conditionals */
- printf ("${%s}", macro (*++cp));
- else
- putchar (*cp);
- }
- cp++; /* do manually what value does */
- putchar ('"');
- break;
-
- case 'E':
- skipname ();
- printf ("Eol = \"");
- value ();
- putchar ('"');
- break;
-
- case 'F':
- skipname ();
- printf ("Flags = { ");
- for (; *cp && *cp != ','; cp++)
- {
- printf ("%s", mflags (*cp));
- if (cp[1] && cp[1] != ',')
- printf (", ");
- }
- cp++; /* do manually what value does */
- printf (" }");
- break;
-
- case 'M':
- skipname ();
- printf ("Maxsize = \"");
- value ();
- putchar ('"');
- break;
-
- case 'P':
- skipname ();
- printf ("Path = \"");
- value ();
- putchar ('"');
- break;
-
- case 'R':
- skipname ();
- printf ("Recipient = RULESET_");
- value ();
- break;
-
- case 'S':
- skipname ();
- printf ("Sender = RULESET_");
- value ();
- break;
-
- case '\0':
- goto done;
- }
-
- if (cp[-1] && cp[-1] == ',')
- {
- printf (",\n");
- goto loop;
- }
- else
- putchar ('\n');
-
- done:
- /* handle continuation lines */
- if (ngets (buffer) != NULL)
- {
- line++;
- if (buffer[0] == '\t')
- {
- cp = buffer;
- goto loop;
- }
- else
- ungets (buffer);
- }
- else
- ungets (NULL);
-
- printf ("\t};\n");
-
- #undef value
- #undef skipname
- }
-
- /* header --- define sendmail headers */
-
- header ()
- {
- register char *cp = buffer + 1;
- register int flags = 0;
- register int conditional = 0;
-
- printf ("header\n\t");
- if (*cp == '?') /* header for mailers with these flags */
- {
- flags++;
- printf ("for (");
- for (cp++; *cp != '?'; cp++)
- {
- printf ("%s", mflags (*cp));
- if (cp[1] != '?')
- putchar (',');
- }
- printf (") {\n\t\t");
- cp++; /* skip final '?' */
- }
-
- printf ("define (\"");
- for (; *cp && ! isspace (*cp); cp++)
- putchar (*cp);
- printf ("\", \"");
-
- body:
- while (*cp)
- {
- switch (*cp) {
- case '$':
- switch (*++cp) {
- case '?':
- printf ("ifset (%s, ", macro (*++cp));
- conditional++;
- break;
- case '|':
- putchar (',');
- break;
- case '.':
- putchar (')');
- conditional--;
- break;
- default:
- printf ("${%s}", macro (*cp));
- break;
- }
- break;
- default:
- putchar (*cp);
- break;
- }
- cp++;
- }
-
- /* handle continuation lines */
- if (ngets (buffer) != NULL)
- {
- line++;
- if (buffer[0] == '\t')
- {
- printf ("\\\n");
- cp = buffer + 1;
- goto body;
- }
- else
- ungets (buffer);
- }
- else
- ungets (NULL);
-
- printf ("\");\n");
-
- if (flags)
- printf ("\t};\n");
- }
-
- /* option --- translate a sendmail option to an ease option */
-
- option ()
- {
- register char *name = buffer + 1, *value = buffer + 2;
-
- printf ("options\n\t");
- if (*name == 'd') /* delivery */
- printf ("o_delivery = %s;\n", delivoption (*value));
- else if (*name == 'e') /* handling */
- printf ("o_handling = %s;\n", handle_option (*value));
- else
- printf ("%s = \"%s\";\n", optionname (*name), value);
- }
-
- /* trusted --- define the list of trusted users */
-
- trusted ()
- {
- register char *cp = buffer + 1;
-
- while (*cp)
- {
- if (isspace (*cp))
- *cp = ',';
- cp++;
- }
- printf ("trusted\n\t{ %s };\n", buffer+1);
- }
-
- /* precedence --- define the precedence of a message class */
-
- precedence ()
- {
- register char *cp = buffer + 1;
-
- printf ("precedence\n\t");
- for (; *cp && *cp != '='; cp++)
- putchar (*cp);
- printf (" = %s;\n", ++cp);
- }
-
- /* other --- not a sendmail control line */
-
- other ()
- {
- printf ("%s\n", buffer);
- }
-
- die (routine)
- char *routine;
- {
- fprintf (stderr, "%s: malformed input line %d: fatal error\n",
- routine, line);
- exit (1);
- }
-
- /* macro --- return name for sendmail predefined macro */
-
- char *macro (c)
- char c;
- {
- static char buf[2] = { '\0', '\0' };
-
- switch (c) {
- case 'a': /* The origination date in Arpanet format */
- return ("m_odate");
-
- case 'b': /* The current date in Arpanet format */
- return ("m_adate");
-
- case 'c': /* The hop count */
- return ("m_hops");
-
- case 'd': /* The date in UNIX (ctime) format */
- return ("m_udate");
-
- case 'e': /* The SMTP entry message */
- return ("m_smtp");
-
- case 'f': /* The sender (from) address */
- return ("m_saddr");
-
- case 'g': /* The sender address relative to the recipient */
- return ("m_sreladdr");
-
- case 'h': /* The recipient host */
- return ("m_rhost");
-
- case 'i': /* The queue id */
- return ("m_qid");
-
- case 'j': /* The official domain name for this site */
- return ("m_oname");
-
- case 'l': /* The format of the UNIX from line */
- return ("m_ufrom");
-
- case 'n': /* The name of the daemon (for error messages) */
- return ("m_daemon");
-
- case 'o': /* The set of "operators" in addresses */
- return ("m_addrops");
-
- case 'p': /* Sendmail's pid */
- return ("m_pid");
-
- case 'q': /* The default format of sender address */
- return ("m_defaddr");
-
- case 'r': /* Protocol used */
- return ("m_protocol");
-
- case 's': /* Sender's host name */
- return ("m_shostname");
-
- case 't': /* A numeric representation of the current time */
- return ("m_ctime");
-
- case 'u': /* The recipient user */
- return ("m_ruser");
-
- case 'v': /* The version number of sendmail */
- return ("m_version");
-
- case 'w': /* The hostname of this site */
- return ("m_sitename");
-
- case 'x': /* The full name of the sender */
- return ("m_sname");
-
- case 'y': /* The id of the sender's tty */
- return ("m_stty");
-
- case 'z': /* The home directory of the recipient */
- return ("m_rhdir");
-
- default:
- buf[0] = c;
- return (buf);
- }
- }
-
- #define docompat(val) if (compat) goto warn; else return (val)
-
- /* mflags --- convert sendmail mailer flags to ease names */
-
- char *mflags (c)
- char c;
- {
- static char buf[2] = { '\0', '\0' };
-
- switch (c) {
- case 'f': return ("f_ffrom");
- case 'r': return ("f_rfrom");
- case 'S': return ("f_noreset");
- case 'n': return ("f_noufrom");
- case 'l': return ("f_locm");
- case 's': return ("f_strip");
- case 'm': return ("f_mult");
- case 'F': return ("f_from");
- case 'D': return ("f_date");
- case 'M': return ("f_mesg");
- case 'x': return ("f_full");
- case 'P': return ("f_return");
- case 'u': return ("f_upperu");
- case 'h': return ("f_upperh");
- case 'A': return ("f_arpa");
- case 'U': return ("f_ufrom");
- case 'e': return ("f_expensive");
- case 'X': return ("f_dot");
- case 'L': return ("f_llimit");
- case 'p': return ("f_retsmtp");
- case 'I': return ("f_smtp");
- case 'C': return ("f_addrw");
- case 'E': docompat ("f_escape");
- default:
- warn:
- fprintf (stderr,
- "warning: non standard mailer flag '%c' on line %d\n",
- c, line);
- buf[0] = c;
- return buf;
- }
- }
-
- /* optionname --- convert sendmail options to ease names */
-
- char *optionname (c)
- char c;
- {
- static char buf[2] = { '\0', '\0' };
-
- switch (c) {
- case 'A': return ("o_alias");
- case 'a': return ("o_ewait");
- case 'B': return ("o_bsub");
- case 'c': return ("o_qwait");
- case 'd': return ("o_delivery");
- case 'D': return ("o_rebuild");
- case 'e': return ("o_handling");
- case 'F': return ("o_tmode");
- case 'f': return ("o_usave");
- case 'g': return ("o_gid");
- case 'H': return ("o_fsmtp");
- case 'i': return ("o_skipd");
- case 'L': return ("o_slog");
- case 'm': return ("o_rsend");
- case 'N': return ("o_dnet");
- case 'o': return ("o_hformat");
- case 'Q': return ("o_qdir");
- case 'q': docompat ("o_qfactor");
- case 'r': return ("o_tread");
- case 'S': return ("o_flog");
- case 's': return ("o_safe");
- case 'T': return ("o_qtimeout");
- case 't': return ("o_timezone");
- case 'u': return ("o_dmuid");
- case 'v': return ("o_verbose");
- case 'W': return ("o_wizpass");
- case 'x': return ("o_loadq");
- case 'X': return ("o_loadnc");
- case 'Y': docompat ("o_newproc");
- case 'Z': docompat ("o_prifactor");
- case 'z': docompat ("o_waitfactor");
- default:
- warn:
- fprintf (stderr,
- "warning: non standard option '%c' on line %d\n",
- c, line);
- buf[0] = c;
- return buf;
- }
- }
-
- /* delivoption --- convert sendmail delivery option value to ease name */
-
- char *delivoption (c)
- char c;
- {
- static char buf[2] = { '\0', '\0' };
-
- switch (c) {
- case 'i': return ("d_interactive");
- case 'b': return ("d_background");
- case 'q': return ("d_queue");
- default:
- fprintf (stderr,
- "warning: non standard delivery option '%c' on line %d\n", c, line);
- buf[0] = c;
- return buf;
- }
- }
-
- /* handle_option --- convert sendmail handling option value to ease name */
-
- char *handle_option (c)
- char c;
- {
- static char buf[2] = { '\0', '\0' };
-
- switch (c) {
- case 'p': return ("h_print");
- case 'q': return ("h_exit");
- case 'm': return ("h_mail");
- case 'w': return ("h_write");
- case 'e': return ("h_mailz");
- default:
- fprintf (stderr,
- "warning: non standard handling option '%c' on line %d\n", c, line);
- buf[0] = c;
- return buf;
- }
- }
-
- /*
- * "buffered" i/o routines. These are necessary since
- * mail headers may have continuation lines, and we can't see if
- * a continuation line is there without getting it. If it isn't,
- * just put it back.
- */
-
- int saved = 0;
- char *saveb = NULL;
-
- /* ngets --- get a line of input from either saved buffer or stdin */
-
- char *ngets (bp)
- char *bp;
- {
- if (! saved)
- return (gets (bp));
-
- saved = 0;
- bp = saveb;
- saveb = NULL;
- return (bp);
- }
-
- /* ungets --- put a buffer back on the input, so to speak */
-
- void ungets (bp)
- char *bp;
- {
- saved = 1;
- saveb = bp;
- line--;
- }
-