home *** CD-ROM | disk | FTP | other *** search
- #ifndef lint
- static char RCSid[] = "$Header: /tmp_mnt/home/kreskin/u0/barnett/Src/Ease/ease/cfc/RCS/cfc.c,v 3.3 1991/09/09 16:34:44 barnett Exp $";
- #endif
-
- /*
- * $Log: cfc.c,v $
- * Revision 3.3 1991/09/09 16:34:44 barnett
- * Bug fixes. Better handling of those $? conditionals.
- *
- * Revision 3.2 1991/05/16 10:49:33 barnett
- * Support for IDA databases
- * More tolerant handling of unusual conditions
- * more bug fixes
- *
- * Revision 3.0 1991/02/22 19:33:07 barnett
- * Many enhancements for IDA and HP sendmail.cf files
- *
- * Revision 2.2 1991/02/21 19:19:34 barnett
- * Fixed several bugs:
- * Multiple ifsets on one line
- * Better handling of # in comments
- * Support for escaping a '* and /' in a comment field
- *
- * Revision 2.1 1990/01/30 11:38:10 jeff
- * Enhancements by Bruce Barnett 89/1/23:
- * - Added some enhancements for SunOS 4.0 and Ultrix 3.0
- * - And a log of unusual grammar constructs
- *
- * Revision 2.0 88/06/15 15:16:48 root
- * Baseline release for net posting. ADR.
- *
- * Revision 1.6 88/06/10 13:45:16 root
- * Fix originally from Raymond A. Schnitzler (ras@sabre.bellcore.com) to
- * add the (undocumented) 'P' option which sets the Postmaster address for
- * receiving cc's of bad mail. ADR.
- *
- * Revision 1.5 88/01/21 16:18:13 root
- * Eliminated Rutgers-ism, linted, smartened Mailer Argv handling. ADR.
- *
- * Revision 1.4 88/01/21 15:57:52 root
- * Added the 'y' factor; missed it last time. ADR.
- *
- * 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 */
- int undoc = 0; /* complain about undocumented options, flags */
- int ida = 0; /* IDA sendmail options */
- int sunos = 0; /* Special parsing for SunOS - bgb */
- int DECos = 0; /* Special parsing for Ultrix - bgb */
- /* NOTE: can't use 'ultrix' cause of cpp */
- int hpos = 0; /* HP/UX */
-
- char *classes = 0; /* list of classes defined */
- main (argc, argv)
- int argc;
- char **argv;
- {
- extern int getopt ();
- extern int optind;
- extern char *optarg;
- int i,c;
-
- while ((c = getopt (argc, argv, "icdhusC:")) != EOF) {
- switch (c) {
- case 'c':
- compat = 1;
- break;
- case 'u':
- undoc = 1;
- break;
- case 's':
- sunos = 1;
- break;
- case 'd':
- DECos = 1;
- break;
- case 'i':
- ida = 1;
- break;
- case 'h':
- hpos = 1;
- break;
- case 'C':
- classes = optarg;
- break;
- case '?':
- default:
- fprintf (stderr, "usage: %s [ -[ids] ] [ -c ] [ -u ] [-C classes ]\n", argv[0]);
- break;
- }
- }
-
- if (optind < argc)
- fprintf (stderr,
- "warning: ignoring non-flag command line arguments\n");
-
- printf ("/***********************************************************/\n");
- printf ("/* This ease file generated by cfc version $Revision: 3.3 $*/\n");
- printf ("/* automatically from a sendmail.cf file */\n");
- printf ("/* It may need to be edited before feeding to ease. */\n");
- printf ("/***********************************************************/\n");
- /* let's generate something that might work */
- printf ("bind \n");
- for (i=0;i<=29;i++)
- printf ("\tRULESET_%d = ruleset %d;\n",i,i);
- /* SunOS uses ruleset 30. Other sendmails only support S0 to S29 */
- if (sunos)
- printf ("\tRULESET_30 = ruleset 30;\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.
- * Also class definitions - BGB
- */
-
- 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':
- endruleset ();
- class ();
- break;
- case 'F':
- endruleset ();
- 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 */
- exit (0);
- /*NOTREACHED*/
- }
-
- /* comment --- produce a comment */
-
- comment ()
- {
- static char format[] = "/* %s */\n";
- register int i = strlen (buffer) - 1;
- register int j;
- /* try to be semi-intelligent about comments */
-
- /* if a blank line, keep as a blank line */
- if (buffer[1] == '\0')
- printf ("\n");
- else { /* non-blank comment */
- j=1;
- printf("/*");
- /* print ######## as /********* */
- while (buffer[j] == '#') {
- j++;
- printf("*");
- }
- /* print the rest of the line */
- while (buffer[j] != '\0') {
- switch (buffer[j]) {
- case '#':
- /* convert ### to *** */
- if (buffer[j+1] == '\0') {
- printf("*");
- } else if (buffer[j+1] == '#') /* a string of #### */
- while (buffer[j] == '#' && buffer[j+1] != '\0') {
- printf("*");
- j++;
- }
- else printf("#");
- break;
- case '*':
- if (buffer[j+1] == '/') {
- printf("*\\/");
- j++;
- } else printf("*");
- break;
- default:
- printf("%c", buffer[j]);
- break;
- }
- j++;
- } /* end while */
- if ( buffer[j-2] == '#' && buffer[j-1] == '#')
- printf("*/\n");
- else if ( buffer[j-2] != '#' && buffer[j-1] == '#')
- printf("*/\n");
- else if ( buffer[j-1] == ' ' || buffer[j-1] == '\t')
- printf("*/\n");
- else
- printf(" */\n");
- } /* end of non-blank */
- }
-
- /* ruleset --- name a ruleset */
-
- ruleset ()
- {
- static int first = 1;
- register char *cp = buffer + 1;
- int i;
-
- 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");
- if (classes && *classes ) {
- printf("\t/* defining classes %s */\n",classes);
- for (i=0;*(classes+i);i++) {
- printf ("\tany_in_%c : match (1) in %c;\n",*(classes+i),*(classes+i));
- printf ("\tany_not_in_%c : match (0) in %c;\n",*(classes+i),*(classes+i));
- }
- }
- /* let's make the default configuration nicer for SunOS - bgb */
- if (DECos || ida || hpos ) {
- printf ("\tany_in_myhostname : match (1) in c_myname;\n");
- }
- if (sunos) {
- /* printf ("\tany_in_V : match (1) in V;\n");
- printf ("\tany_not_in_V : match (0) in V;\n"); */
- printf ("/*\tany_in_map_? : match (1) map ?;\t*/\n");
- printf ("/*\tany_not_in_map_? : match (0) map ?;\t*/\n");
- printf ("\tany_in_etc_hosts : match host;\n");
- printf ("\tany_in_mydomainname : match (1) in c_mydomain;\n");
- printf ("\tany_in_myhostname : match (1) in c_myname;\n");
- }
- }
-
- printf ("ruleset\n\tRULESET_");
- while (cp && *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 && *cp && *cp != '\t')
- cp++;
- if (!*cp) {
- fprintf(stderr,
- "Unexpected EOL when expecting right hand side of rule\n");
- lhs(buffer+1);
- printf("\n\tMissingRightHandSide();\n");
- return;
- }
- *cp = '\0';
-
- cp++;
- while (cp && *cp && *cp == '\t')
- cp++;
- cp2 = cp;
- while (cp && *cp && *cp != '\t')
- cp++;
- if (*cp == '\t' && cp[1])
- {
- *cp = '\0';
- com++;
- cp++;
- while (cp && *cp && *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;
- register int open = 0;
- int ifset = 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 '=':
- switch(*++cp) {
- case 'w':
- if (sunos || ida || DECos ) {
- printf (" any_in_myhostname ");
- break;
- } /* else fall through */
- case 'm':
- if (sunos ) {
- printf (" any_in_mydomainname ");
- break;
- } /* else fall through */
- default :
- printf (" any_in_%c ", *cp);
- }
- break;
- case '%' :
- /* YP map for SunOS */
- if ((cp+1) && sunos && (*(cp +1) == 'y') ) {
- printf (" any_in_etc_hosts"); ++cp;
- } else {
- printf (" any_in_map_%c", *++cp);
- }
- break;
- case '~':
- printf (" any_not_in_%c ", *++cp);
- break;
- case '?':
- printf (" ifset (%s, ", macro (*++cp));
- conditional++;ifset++;
- break;
- case '|':
- if ( ! conditional) complain("in left hand side of rule, found a '$|' without a previous '$?'");
- if ( ifset) {
- /* I don't think I have to output a ", " */
- /* but let's test */
- if ( quoting )
- printf("\", \"");
- else
- printf(", ");
- } else {
- complain("in left hand side of rule, found a '$|' without a previous '$?'");
- putchar (',');
- }
- break;
- case '{':
- printf("ypalias ("); /* Ultrix */
- open++;
- break;
- case '"':
- printf("yppasswd ("); /* Ultrix */
- open++;
- break;
- case '.':
- putchar (')');
- conditional--;ifset--;
- break;
- case '#':
- /* IDA does something strange with this */
- printf("resolved(");
- open++;
- 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
- {
- /* start a literal - but ignore the first space */
- if (*cp != ' ' && ! quoting)
- {
- quoting = 1;
- putchar ('"');
- }
- /* else
- do nothing */
- }
- putchar (*cp); /* print the character */
- break;
- }
- }
- if (quoting)
- putchar ('"');
- while (open--)
- putchar (')');
- if (conditional) {
- /* the lhs was missing a $. - let's add one automatically */
- complain ("Expected '$.' on left hand side of rule - adding one anyway");
- putchar (')');
- conditional--;ifset--;
- }
- printf (")");
- }
-
- /* rhs --- right hand side of a production */
-
- rhs (text)
- char *text;
- {
- register char *cp = text;
- char *index ();
- char *cp1;
- register int open = 0;
- register int conditional = 0; /* true if in an ifset condition */
- register int quoting = 0; /* true if in a string */
- register int ifset = 0; /* true if in ifset(), like quoting */
- register int needconcat = 0; /* true if an $? on line (lookahead) */
- register int didconcat = 0; /* true if did the concat() */
- register int indbm = 0; /* true if in IDA $( $) construct */
- register int inalias = 0; /* true if in IDA $(@ $) construct */
- int canon = 0;
- int diddefault = 0;
-
- printf ("\t\t");
-
- /* Need to handle this line */
- /* R$+<@$=S> $:$1<@$2>$?R<$R>$. */
- for(cp1=cp;*cp1;cp1++) {
- if (*cp1 == '$' && (cp1+1) && *(cp1+1)== '?')
- needconcat = 1; /* there is an ifset on this line */
- }
- if (*cp == '$' && index ("#@:", cp[1]) != NULL)
- ; /* not the default */
- else
- {
- printf ("retry (");
- open++;
- }
-
- for (; *cp; cp++)
- {
- switch (*cp) {
- case '$':
- if (quoting && ! ifset )
- {
- quoting = 0;
- putchar ('"');
- }
- switch (*++cp) {
- case '>':
- printf ("RULESET_");
- for (cp++; cp && *cp && isdigit (*cp); cp++)
- putchar (*cp);
- cp--;
- printf (" (");
- open++;
- break;
- case '[':
- if (quoting) {
- putchar('"');
- quoting--;
- }
- printf (" canon (");
- open++; canon++;
- break;
- case ']':
- putchar (')');
- open--;
- if (diddefault) {
- putchar (')');
- diddefault--;
- } else
- canon--;
- break;
- case '{':
- printf ("ypmap (%s, ", macro (*++cp)); /* sunos */
- open++;
- break;
- case '}':
- putchar (')');
- open--;
- break;
- case '<':
- printf ("program (%s, ", macro (*++cp)); /* HP/UX */
- open++;
- break;
- case '?':
- if (didconcat) {
- printf ("\",");
- needconcat = 0; /* don't need this */
- }
- printf ("ifset (%s, \"", macro (*++cp));
- conditional++;
- ifset++;
- quoting++;
- break;
- case '|':
- if ( ! conditional) complain("right side of rule - found '$|' without '$?'");
- if ( ifset) {
- printf("\", \"");
- } else {
- complain("right side of rule - found '$|' without '$?'");
- putchar (',');
- }
- break;
- case '.':
- if (ifset && quoting ) {
- putchar('"'); quoting--;
- }
- if (! ifset ) complain("right side of rule - found '$.' without '$?'");
- putchar (')');
- if (open) {
- putchar(')');
- open--;
- }
- conditional--;
- ifset--;
- break;
- case '#':
- parseresolve(cp);
- goto out; /* string is exhausted */
- /* break; */
- case '@':
- if ( ! indbm) {
- printf ("return (");
- if (needconcat && cp+1 && cp+2 &&
- ! ((*(cp+1) == '$') && (*(cp+2) == '?'))){
- printf ("concat (\"");
- open++;didconcat++;
- }
- open++;
- } else {
- printf(", ");
- /* printf("\", \""); */
- }
- break;
- case ':':
- if ( canon ) {
- printf("default ( ");
- canon--;diddefault++;
- } else if ( indbm ) {
- printf("default ( ");
- indbm--;diddefault++;
- } else {
- printf ("next (");
- if (needconcat && cp+1 && cp+2 &&
- ! ((*(cp+1) == '$') && (*(cp+2) == '?'))){
- printf ("concat (\"");
- open++;didconcat++;
- }
- open++;
- }
- break;
- case '(':
- printf((*(++cp) == '@')
- ? " alias(" /* IDA alias lookup */
- : " dbm($%c, " /* IDA database lookup */
- , *cp);
- indbm++;
- open++;
- break;
- case ')':
- putchar (')');
- open--;
- if (diddefault) {
- putchar (')');
- diddefault--;
- }
- else
- indbm--;
- break;
- case '&':
- 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 (ifset ) {
- if (quoting)
- printf ("${%s}", macro (*cp));
- else
- printf ("$%s", macro (*cp));
- } else { /* not in ifset() */
- if (quoting)
- printf ("${%s}", macro (*cp));
- else
- printf ("$%s", macro (*cp));
- }
- break;
- }
- break; /* not a character that starts with a $ */
- default:
- if ( ifset && quoting ) {
- putchar(*cp);
- } else if (ifset && ! quoting) {
- if ( ispunct (*cp)) {
- putchar('"');quoting++;
- }
- putchar(*cp);
- } else { /* not ifset */
- if (ispunct (*cp))
- {
- if (quoting ) /* end a literal */
- {
- quoting = 0;
- putchar ('"');
- }
- /* else
- do nothing */
- } else {
- if (*cp != ' ' && ! quoting) /* start a literal */
- {
- quoting = 1;
- putchar ('"');
- }
- /* else
- do nothing */
- }
- putchar (*cp); /* print the character */
- }
- break;
- }
- } /* end of for */
- out:
- if (quoting)
- putchar ('"');
- while (open--)
- putchar (')');
- printf (";\n");
- if (conditional)
- die ("rhs - $? without $.");
- }
- /* parseresolve - parse this mailer/host/user mess */
- parseresolve(cp)
- char *cp;
- {
- int quoting = 0;
- int open = 0;
- int ifset = 0;
- char *addrops;
- addrops = ".:;%@!=/[]?#^,<>$"; /* should be defined from input file */
- printf ("resolve (mailer (");
- /* this is a simple (idiotic) parser (hack) that scans the right
- hand side of a $# rule
- The format is usually
- "$# mailer $@ host $: user" or
- "$# mailer $: user" or
- "$# mailer $: something with a $macro" or
- "$# $M $: user" or
- "$# $1 " (IDA sendmail )
-
- Note that there may be special constructs
- in the host field, i.e.
- "$1", "[$2]", "$w", or "$K".
- and in the user field. Esp. in the error mailer:
- "$1 < @$2 > $4", or
- "Never heard of host $2 in domain $m "
- "$n" which should become -> "$m_daemon"
- */
- /* pointing to '#' */
- cp++;
- while( *cp == ' ') cp++; /* skip blanks */
- /* output any character not a '$' */
- while (cp && *cp && *cp != '$' ) {
- /* skip spaces in the mailer field */
- if ( *cp != ' ' ) putchar(*cp);
- cp++;
- }
- parsehost:
- while (cp && *cp && *cp == ' ') cp++; /* advance to non-blank */
- if (!cp || !*cp ) goto out;
- /* currently pointing to a "$" */
- /* we may now be pointing to:
- $@ - the host name
- $: - the user
- or a macro
- or nothing?! (*cp == 0);
- /* don't look at the '$' */
- if (*cp == '$') {
- cp++; /* skip past $, expect a '@' */
- } else {
- fprintf(stderr,
- "Error: found %c when expecting a '$' on line %d\n",
- *cp,line);
- }
- if (!cp || !*cp ) goto out;
- if (*cp == ':') goto parseuser;
- if (*cp == '?' ) { /* handle $#$?G$@....$:...$|$@...$:...$. */
- cp++;
- printf ("), ifset (%s,\n\t\t\t\t(", macro (*cp++));
- ifset++;
- while (cp && *cp && *cp == ' ') cp++; /* advance to non-blank */
- if ( *cp == '$') {
- cp++;
- } else {
- fprintf(stderr,
- "Error: found %c when expecting a '$' on line %d\n",
- *cp,line);
- }
- }
- if (*cp != '@' ) {
- /* must be a macro name */
- printf ("$%s",macro(*cp++));
- /* now skip to the $@ */
- /* if (!cp || !*cp ) goto out; */
- while (cp && *cp && *cp == ' ') cp++;
- if (!cp || !*cp ) goto out;
- if (*cp == '$') cp++;
- else
- fprintf(stderr,
- "Error: found %c when expecting a '$' on line %d\n",
- *cp,line);
- if (*cp == ':') goto parseuser;
- if (*cp == '@') cp++;
- else
- fprintf(stderr,
- "Error: found %c when expecting a '@' on line %d\n",
- *cp,line);
- } else {
- /* I did see the '@' of the $@ */
- cp++;
- }
- /* print host name ($@host ) */
- if (ifset)
- printf (" host (");
- else
- printf ("),\n\t\t\t\thost (");
- for (;cp && *cp;cp++) {
- if (*cp != '$') {
- putchar (*cp);
- } else {
- /* it might be the $: */
- if (!*(cp+1)) goto out;
- if ( *(cp+1) == ':') {
- cp++; /* parseuser expects ':' */
- goto parseuser;
- } else {
- putchar(*cp++); /* print '$' */
- printf("%s", macro(*cp)); /* and next */
- }
- }
- }
- parseuser:
- printf ("),\n\t\t\t\tuser (");
- /* *cp == ':', now look for user = $n */
- /* maybe *cp == 0 */
- if ( !*cp ) goto out;
- if (*cp != ':' )
- fprintf(stderr,
- "Expected ':', found '%c' after '$' on line %d\n",*cp,line);
- /* looking at the user string */
- quoting = 0;
- for (cp++; cp && *cp; cp++) {
- if (quoting ) {
- /* if (isalnum(*cp) || isspace(*cp)) { */
- if (*cp == '"') {
- printf("\\\""); /* print "\" */
- quoting++;
- } else if (*cp == '\\') {
- printf("\\"); /* print "\" and the next character */
- putchar(*++cp);
- } else if (!index(addrops,*cp)) {
- /* not one of those address characters */
- putchar (*cp);
- } else { /* maybe it's a dollar sign? */
- quoting=0;
- printf("\" %c",*cp);
- if (*cp == '$') {
- cp++; /* This may not be used at all - but it can't hurt */
- if (*cp == '>' ) { /* IDA sendmail */
- cp++;
- printf(" RULESET_");
- while (cp && *cp && *cp >= '0' && *cp <= '9') putchar(*cp++);
- printf("(");
- open++;
- } else {
- printf("%s",macro(*cp));
- }
- }
- }
- } else { /* not quoting */
- if ( *cp == '$' ) {
- cp++;
- /* Could be $|, or $>, or $macro */
- if (*cp == '>' ) { /* IDA sendmail */
- cp++;
- printf("retry (RULESET_");
- while (cp && *cp && *cp >= '0' && *cp <= '9') putchar(*cp++);
- printf("("); open++;
- open++;
- } else if ( *cp == '|') {
- /* This is the middle of an ifset */
- if ( ! ifset ) {
- fprintf(stderr,
- "Found a '$|' in the user address without a '$?' on line %d\n",
- *cp,line);
- cp++; /* ignore */
- } else { /* I expected this */
- printf(")),\n\t\t\t\t(");
- cp++;
- goto parsehost;
- }
- } else if ( *cp == '.') {
- /* This is the END of an ifset */
- if ( ! ifset ) {
- fprintf(stderr,
- "Found a '$.' in the user address without a '$?' on line %d\n",
- *cp,line);
- cp++; /* ignore */
- } else { /* I expected this */
- /* cp++; */ /* Don't do this, the for loop increments cp */
- printf("))");
- ifset = 0;
- }
- } else {
- putchar ('$'); /* print $ */
- printf("%s",macro(*cp)); /* and macro */
- }
- } else if (*cp && (index (addrops,*cp))) {
- putchar(*cp);
- } else if (*cp == '"') {
- printf("\"\\\"");quoting++; /* print "\" */
- } else {
- quoting = 1;
- printf(" \"%c",*cp);
- }
- } /* end of quoting/not quoting */
- }
- if (quoting) printf("\"");
- out:
- if (ifset) {
- fprintf(stderr,
- "EOL while expecting '$.' on line %d\n",
- line);
- ifset = 0;
- }
- while (open--) printf(")");
- printf ("))");
- } /* end parseresolve () */
- /* def --- define a macro */
-
- def ()
- {
- register char *mac = buffer + 1, *value = buffer + 2;
- register int conditional = 0;
- register int concat = 0;
- register int quote = 0;
- register int ifset = 0;
-
-
- printf ("macro\n\t%s = ", macro (*mac));
- /* fprintf(stderr,"mac=%c, value=%s\n",*mac,value); */
-
- /* This is tricky, we want the form:
- *
- * Dq$g$?x$x$.
- * to become
- * macro
- * m_defaddr = concat ("${m_sreladdr}", ifset (m_sname," (${m_sname})"));
- * and
- * Dq$?x$x $.<$g>
- * to become
- * macro
- * m_defaddr = concat (ifset (m_sname," (${m_sname})"),"<${m_sreladdr}>" );
- *
- * One problem is the form
- * Dq$?x$x <$g>$|$g$.
- *
- *
- */
- concat = 0;
- quote = 0;
- conditional = 0;
- ifset = 0;
- if (! *value ) printf("\"\""); /* unusual error - just print " and let
- the end of the loop after the while
- clean it up */
- while (*value)
- {
- switch (*value) {
- case '$':
- switch (*++value) {
- /* $:$?E$1%$2.dnet<@$E.LOCAL>$3$|$1<@$2.dnet>$3$.
- Dq$?x$x <$g>$|$g$.
- Dq$?x$!x <$g>$|$g$.
- */
- case '?':
- /* Another special case %$&*!
- * if the start of the string is $?,
- * but the end is NOT $., then we need a concat
- */
- if (*(value+strlen(value)-1) == '.' &&
- *(value+strlen(value)-2) == '$') {
- /* just use ifset with no concat */
- printf ("ifset (%s, ", macro (*++value));
- conditional++;ifset++;
- /* Still need a quote character,
- next characters might be a $!x */
- if ((*(value+1) == '$') && (*(value+2) == '!')) {
- value++;value++;
- printf("\"${quote(%s)} ",macro(*++value));
- quote++;
- } else {
- printf("\"");quote++;
- }
- } else {
- printf ("concat( ifset (%s, \"", macro (*++value));
- conditional++;quote++;concat++;ifset++;
- }
- break;
- case '|':
- if ( ! conditional) die("def - $| without $?");
- if ( ifset) {
- printf("\", \"");
- } else {
- complain("Got $| when not in ifset\n");
- putchar (',');
- }
- break;
- case '.':
- if (quote) {
- putchar('"');quote--;
- }
- putchar (')');
- conditional--;ifset--;
- /* not EOL, must be in concat(ifset( ,) */
- if (*(value+1)) putchar(',');
- break;
- case '!': /* IDA sendmail - this code never gets executed */
- printf("quote("); concat++;
- break;
- default:
- /* see if *(value+1) == '$' and *(value+2) == '?' */
- if (!concat && (strlen(value)>2)
- && (*(value+1) == '$')
- && (*(value+2) == '?')) {
- printf ("concat (\"${%s}\", ", macro (*value));
- /* I'm gonna need a concat */
- concat++;
- } else {
- if (!quote) {
- printf("\"${%s}", macro (*value));
- quote++;
- } else {
- printf ("${%s}", macro (*value));
- }
- }
- break;
- }
- break;
- case '"' :
- if (quote) {
- printf ("\\\"");
- } else {
- printf("\"\\\"");
- quote++;
- }
- break;
- default:
- if ( ! quote ) {
- putchar('"');
- quote++;
- }
- putchar (*value);
- break;
- }
- value++;
- }
- if ( quote ) putchar('"');
- if (concat) {
- putchar (')');
- concat--;
- }
- printf (";\n");
- if (conditional)
- die ("def - $? without $.");
- }
-
- /* class --- define a class list */
-
- class ()
- {
- register char *name = buffer + 1, *value = buffer + 2;
- int havepunct;
- char *s;
-
-
- printf ("class\n\t");
- switch (name[0]) {
- case 'w' : printf("c_myname"); break;
- case 'm' : if (sunos) { printf("c_mydomain"); break; }
- /* fall through if not SunOS */
- default: printf("%c",name[0]);
- }
-
- printf (" = { ");
-
- while (value && *value && isspace (*value))
- value++;
-
- /* a class may be a series of punctuation characters e.g. IDA */
- /* also watch for spaces on the end of a line and avoid ',)' */
-
- while (value && *value)
- {
- /* get first field */
- /* look for first space or EOL */
- for (s=value,havepunct=0;*s && ! isspace (*s);s++)
- if (ispunct(*s)) havepunct = 1;
-
- /* field is from *value to *s
- if there is a punctuation char, havepunt == 1 */
- if (havepunct) putchar('"');
- while (value < s ) {
- if (*value == '"') putchar('\\'); /* escape quotes */
- if (*value == '$' ) printf ("${%s}", macro (*++value));
- else putchar(*value);
- value++;
- }
- if (havepunct) putchar('"');
- /* if not EOL, put a comma there
- but watch out for extra spaces.....
-
- so scan over spaces, then look at the next character.
- If not EOL, print ", ". */
-
- while (value && *value && isspace(*value)) value++;
- if (*value && !isspace(*value)) printf (", ");
- }
- 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 && *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 != '=') cp++; cp++
- #define value() for (; cp && *cp && *cp != ','; cp++) putchar (*cp); cp++
-
- loop:
- while (cp && *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 if (*cp == '"')
- printf ("\\\"");
- 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_");
- /* IDA has ruleset/ruleset */
- for (; *cp && *cp != ',' && *cp != '/'; cp++)
- putchar (*cp);
- if (ida && cp && (*cp == '/' )) {
- putchar (*cp++);
- printf("RULESET_");
- value ();
- } else {
- cp++ ;
- }
- break;
-
- case 'S':
- skipname ();
- printf ("Sender = RULESET_");
- /* IDA has ruleset/ruleset */
- for (; *cp && *cp != ',' && *cp != '/'; cp++)
- putchar (*cp);
- if (ida && cp && (*cp == '/' )) {
- putchar (*cp++);
- printf("RULESET_");
- value ();
- } else {
- cp++ ;
- }
- 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 ((char *) 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;
- register int concat = 0; /* true if in a concat( */
- register int needcomma = 0; /* true if a concat is needed */
- register int quote = 0;
- register int ifset = 0; /* true if in a ifset
- (may be inside a concat) */
-
- printf ("header\n\t");
- if (*cp == '?') /* header for mailers with these flags */
- {
- flags++;
- printf ("for (");
- for (cp++; 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);
- /* skip over any spaces */
- while ( cp && *cp && isspace(*cp)) cp++;
- /* but if we are now at the end of the line, we must fake
- an entry for "" */
- if ( cp && *cp ) printf ("\", "); /* don't print next " yet, see if it is a concat */
- else if (cp && ! *cp ) printf("\", \"\"");
- else if (!cp) {
- printf("\"");
- complain("I didn't expect this!\n");
- }
-
- quote = concat = conditional = ifset = needcomma = 0;
- body:
- while (cp && *cp)
- {
- switch (*cp) {
- case '$':
- switch (*++cp) {
- case '?':
- /* if we are in the middle of a quote, end it */
- if (quote) {
- printf("\"");quote--;needcomma=1;
- }
- /* if we are not in a concat, then start one */
- if ( ! concat ) {
- if (needcomma) printf(", ");
- printf("concat (");
- concat++;needcomma=0;
- } else { /* we are in a concat */
- if ( ifset ) { /* if in ifset(..) terminate */
- printf("), concat (");
- ifset--;needcomma=0;
- } else {
- /* otherwise - don't terminate the concat */
- /* but add a comma to seperate the fields */
- /* what do do if concat(X, */
- /* if so, then don't put the comma */
- /* instead - check needcomma */
- /* printf(", "); */
- }
- }
- if ( ifset ) {
- complain("found '$?' before terminating previous '$?' with a '$.'");
- }
- if (needcomma) {
- printf(", ");
- needcomma=0;
- }
- printf ("ifset (%s, \"", macro (*++cp));
- conditional++; quote++;ifset++;
- break;
- case '|':
- if (quote) {
- putchar('"');quote--;
- }
- if ( ! conditional) complain("header - $| without $?");
- if ( ifset) {
- printf(", "); needcomma=0;
- } else {
- complain("Got '$|' without matching '$?'");
- putchar (','); needcomma=0;
- }
- break;
- case '.':
- if (quote) {
- putchar('"');quote--;
- }
- if (!ifset ) {
- complain("found '$.' without matching '$?'");
- }
- putchar (')');
- conditional--;ifset--;
- if (concat) {
- /* this is messy - There may be more than one $? on a line */
- /* and the line may continue on to the next line. */
-
- if (*(cp+1)) { /* if there is more on the line */
- putchar(')');concat--;
- printf(", ");needcomma=0;
- } else {
- /* may need to print a comma - delay decision */
- /* see continuation line handler */
- putchar(')');concat--;needcomma=1;
- }
- }
- break;
- default: /* a $<letter> */
- /* see if *(cp+1) == '$' and *(cp+2) == '?' */
- if (!concat && (strlen(cp)>2)
- && (*(cp+1) == '$')
- && (*(cp+2) == '?')) {
- if ( quote) {
- printf("\", ");
- quote--;needcomma=0;
- }
- printf ("concat (\"${%s}\", ", macro (*cp));
- /* I'm in a concat */
- concat++;
- } else {
- if (!quote) {
- printf("\"${%s}", macro (*cp));
- quote++;
- } else {
- printf ("${%s}", macro (*cp));
- }
- }
- break;
- }
- break;
- case '"' :
- printf ("\\\"");
- break;
- default:
- if ( ! quote ) {
- putchar('"');
- quote++;
- }
- putchar (*cp);
- break;
- }
- cp++;
- }
-
- /* handle continuation lines */
- if (ngets (buffer) != NULL)
- {
- line++;
- if (buffer[0] == '\t')
- {
- if ( concat ) {
- printf("), ");needcomma=0;
- concat--;
- }
- if (needcomma) {
- printf(", ");
- needcomma=0;
- }
- if ( ! quote ) {
- putchar('"');
- quote++;
- }
- printf ("\\\n");
- cp = buffer + 1;
-
- goto body;
- }
- else
- ungets (buffer);
- }
- else
- ungets ((char *) NULL);
-
- if ( quote ) {
- putchar('"');
- }
- if (concat) {
- putchar(')');
- concat--;
- }
- printf (");\n");
-
- if (flags)
- printf ("\t};\n");
-
- if (conditional)
- die ("header translation problem: $? without $.");
- }
-
- /* option --- translate a sendmail option to an ease option */
-
- option ()
- {
- register char *name = buffer + 1, *value = buffer + 2;
- char *newname;
- char *newvalue;
-
- printf ("options\n\t");
- if (*name == 'd') { /* delivery */
- newvalue = delivoption (*value);
- if ( newvalue == NULL) {
- printf("/* Unknown value for delivery option */\n");
- printf("/* Supplying the default value of d_background */\n");
- printf ("o_delivery = d_background ;\n" );
- } else
- printf ("o_delivery = %s;\n", newvalue);
- } else if (*name == 'e') { /* handling */
- newvalue = handle_option(*value);
- if (newvalue == NULL ) {
- printf("/* Unknown value for delivery option */\n");
- printf("/* Supplying the default value of h_print */\n");
- printf ("o_handling = h_print ;\n");
- } else
- printf ("o_handling = %s;\n", newvalue);
- } else if (*name == 'K' ) { /* IDA Keyed Database */
- printf("asm(\"OK%s\");\n", value);
- } else {
- newname = optionname(*name);
- if (newname == NULL)
- printf("asm(\"O%c%s\");\n", *name, value);
- else
- printf ("%s = \"%s\";\n", optionname (*name), value);
- }
- }
-
- /* trusted --- define the list of trusted users */
-
- trusted ()
- {
- register char *cp = buffer + 1;
-
- if ( *cp && *cp == ' ') cp++; /* skip over spaces in the begining */
- while (cp && *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 */
- /* it may also be a blank line */
-
- other ()
- {
- printf ("%s\n", buffer);
- }
-
- die (routine)
- char *routine;
- {
- fprintf (stderr, "%s: malformed input line %d: fatal error\n",
- routine, line);
- exit (1);
- }
- complain (problem)
- char *problem;
- {
- fprintf (stderr, "Warning: malformed input line %d: %s\n",
- line, problem);
- fflush(stderr);
- fflush(stdout);
- }
-
- /* 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 'k': /* The official domain name for this site */
- return ("m_uucpname"); /* IDA */
-
- case 'l': /* The format of the UNIX from line */
- return ("m_ufrom");
-
- case 'm': /* The Domain Name (SunOS) */
- if (sunos || ida ) {
- return ("m_domain");
- } else {
- buf[0] = c;
- return (buf);
- }
-
- 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");
-
- case '"': /* you can get a quote character in some macro definitions */
- return ("\\\"");
- default:
- if (!(isalpha(c) || isdigit(c))) /* if not a digit or letter */
- fprintf (stderr,
- "warning: expected letter as macro definition, found '%c' on line %d\n",
- c, line);
-
- buf[0] = c;
- return (buf);
- }
- }
-
- #define docompat(val) if (compat) goto warn; else return (val)
- #define dofundoc(val) if (undoc) \
- fprintf (stderr, "warning: undocumented flag '%c' used on line %d\n", c, line);\
- return (val)
-
- /* mflags --- convert sendmail mailer flags to ease names */
-
- char *mflags (c)
- char c;
- {
- static char buf[2] = { '\0', '\0' };
- char tstring[100];
-
- 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 'H': return ("f_mail11"); /* Ultrix 3.0 */
- 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");
- case 'R': dofundoc ("f_rport");
- case 'B': return ("f_bsmtp"); /* IDA sendmail */
- case 'V': return ("f_relativize"); /* IDA sendmail */
- default:
- warn:
- fprintf (stderr,
- "warning: non standard mailer flag '%c' on line %d\n",
- c, line);
- sprintf(tstring, "/* unknown mailer flag: %c */", c);
- return tstring;
- }
- }
-
- #define doOundoc(val) if (undoc) \
- fprintf (stderr, "warning: undocumented option '%c' used on line %d\n", c, line);\
- return (val)
-
- /* 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 'b': return ("o_maxempty"); /* SunOS 4.0 */
- case 'C': doOundoc ("o_checkpoint");
- 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 'h': return ("o_maxhops"); /* SunOS 4.0 */
- case 'i': return ("o_skipd");
- case 'I': return ("o_nameserver"); /* HP/UX */
- /* case 'K': Keyed Database (IDA) */
- case 'L': return ("o_slog");
- case 'm': return ("o_rsend");
- case 'N': return ("o_dnet");
- case 'n': doOundoc ("o_validate");
- case 'o': return ("o_hformat");
- case 'P': doOundoc ("o_pmaster");
- case 'Q': return ("o_qdir");
- case 'q': docompat ("o_qfactor");
- case 'r': return ("o_tread");
- case 'R': return ("o_nfs"); /* SunOS 4.0 */
- 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': if (sunos ) return ("o_aliasfile"); else docompat ("o_newproc");
- case 'y': docompat ("o_recipfactor");
- case 'z': docompat ("o_prifactor");
- case 'Z': docompat ("o_waitfactor");
- case '/': return ("o_envelope"); /* IDA */
- default:
- warn:
- fprintf (stderr,
- "warning: Unknown option '%c' on line %d\n",
- c, line);
- /* buf[0] = c; */
- return NULL;
- }
- }
-
- /* 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);
- return NULL;
- }
- }
-
- /* 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");
- case '\0':
- fprintf (stderr,
- "warning: value not specified for option on line %d\n", line);
- return NULL;
- default:
- fprintf (stderr,
- "warning: non standard handling option '%c' on line %d\n", c, line);
- return NULL;
- }
- }
-
- /*
- * "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--;
- }
-