home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume17 / ease2 / part03 < prev    next >
Text File  |  1989-02-08  |  56KB  |  2,037 lines

  1. Subject:  v17i089:  Ease2.0, a language for sendmail.cf files, Part03/03
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4.  
  5. Submitted-by: "Arnold D. Robbins" <arnold@EMORYU1.ARPA>
  6. Posting-number: Volume 17, Issue 89
  7. Archive-name: ease2/part03
  8.  
  9. #! /bin/sh
  10. # This is a shell archive.  Remove anything before this line, then unpack
  11. # it by saving it into a file and typing "sh file".  To overwrite existing
  12. # files, type "sh file -c".  You can also feed this as standard input via
  13. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  14. # will see the following message at the end:
  15. #        "End of archive 3 (of 3)."
  16. # Contents:  cfc/cfc.c doc/ease.paper
  17. # Wrapped by rsalz@papaya.bbn.com on Wed Feb  8 16:55:44 1989
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. if test -f 'cfc/cfc.c' -a "${1}" != "-c" ; then 
  20.   echo shar: Will not clobber existing file \"'cfc/cfc.c'\"
  21. else
  22. echo shar: Extracting \"'cfc/cfc.c'\" \(21150 characters\)
  23. sed "s/^X//" >'cfc/cfc.c' <<'END_OF_FILE'
  24. X#ifndef lint
  25. Xstatic char RCSid[] = "$Header: cfc.c,v 2.0 88/06/15 15:16:48 root Exp $";
  26. X#endif
  27. X
  28. X/*
  29. X * $Log:    cfc.c,v $
  30. X * Revision 2.0  88/06/15  15:16:48  root
  31. X * Baseline release for net posting. ADR.
  32. X * 
  33. X * Revision 1.6  88/06/10  13:45:16  root
  34. X * Fix originally from Raymond A. Schnitzler (ras@sabre.bellcore.com) to
  35. X * add the (undocumented) 'P' option which sets the Postmaster address for
  36. X * receiving cc's of bad mail. ADR.
  37. X * 
  38. X * Revision 1.5  88/01/21  16:18:13  root
  39. X * Eliminated Rutgers-ism, linted, smartened Mailer Argv handling. ADR.
  40. X * 
  41. X * Revision 1.4  88/01/21  15:57:52  root
  42. X * Added the 'y' factor; missed it last time. ADR.
  43. X * 
  44. X * Revision 1.3  87/04/08  10:23:02  root
  45. X * Small bug fixes, compatibility option added, also warnings for
  46. X * unrecognized flags and options. ADR.
  47. X * 
  48. X * Revision 1.2  87/02/18  15:26:39  root
  49. X * Fix to recognize multidigit ruleset numbers in $> (calls) in RHS. ADR.
  50. X * 
  51. X * Revision 1.1  87/02/16  15:25:00  arnold
  52. X * Initial revision
  53. X * 
  54. X * Revision 1.1  87/02/16  15:25:00  arnold
  55. X * Initial revision
  56. X * 
  57. X */
  58. X
  59. X/*
  60. X * cfc.c
  61. X *
  62. X * Sendmail cf file compiler.
  63. X * Reads a raw sendmail.cf file and produces ease source.
  64. X *
  65. X * There are very few comments in this source. You will need both the
  66. X * "Sendmail Installation and Operation Guide" and the paper on Ease
  67. X * to really understand this.
  68. X *
  69. X * Arnold Robbins
  70. X * Emory University Computing Center
  71. X * 2/87
  72. X */
  73. X
  74. X#include <stdio.h>
  75. X#include <ctype.h>
  76. X
  77. Xchar buffer[BUFSIZ];
  78. Xint line = 0;
  79. Xint inruleset = 0;
  80. X
  81. Xextern char *macro ();        /* convert sendmail to ease macro names */
  82. Xextern char *mflags ();        /* convert sendmail to ease mailer flag names */
  83. Xextern char *optionname ();    /* convert sendmail to ease option names */
  84. Xextern char *delivoption ();    /* delivery options */
  85. Xextern char *handle_option ();    /* handling options */
  86. X
  87. Xextern char *ngets ();        /* buffered gets () routine */
  88. Xextern void ungets ();        /* put a buffer back for getting */
  89. X
  90. X#define endruleset()    if (inruleset) { inruleset = 0; printf ("\t}\n"); }
  91. X
  92. Xint compat = 0;            /* complain about new 4.3 options & flags */
  93. Xint undoc = 0;            /* complain about undocumented options, flags */
  94. X
  95. Xmain (argc, argv)
  96. Xint argc;
  97. Xchar **argv;
  98. X{
  99. X    extern int getopt ();
  100. X    extern int optind;
  101. X    int c;
  102. X
  103. X    while ((c = getopt (argc, argv, "cu")) != EOF) {
  104. X        switch (c) {
  105. X        case 'c':
  106. X            compat = 1;
  107. X            break;
  108. X        case 'u':
  109. X            undoc = 1;
  110. X            break;
  111. X        case '?':
  112. X        default:
  113. X            fprintf (stderr, "usage: %s [ -c ] [ -u ]\n", argv[0]);
  114. X            break;
  115. X        }
  116. X    }
  117. X
  118. X    if (optind < argc)
  119. X        fprintf (stderr,
  120. X            "warning: ignoring non-flag command line arguments\n");
  121. X
  122. X    printf ("/******************************************************/\n");
  123. X    printf ("/* This ease file generated by cfc from a sendmail.cf */\n");
  124. X    printf ("/* file. It must be edited by hand before being fed   */\n");
  125. X    printf ("/* to ease!                                           */\n");
  126. X    printf ("/******************************************************/\n");
  127. X    printf ("\n\nbind\n\t/* RULESET BINDINGS GO HERE (cfc) */\n\n");
  128. X
  129. X    /*
  130. X     * For perfection, everything but the comment and rule cases
  131. X     * should do an endruleset (), but practically speaking, it is
  132. X     * usually only the mailer new ruleset definitions that end a
  133. X     * previous ruleset. Occasionally a macro, too.
  134. X     */
  135. X
  136. X    while (ngets (buffer) != NULL)
  137. X    {
  138. X        line++;
  139. X        switch (buffer[0]) {
  140. X        case '#':
  141. X            comment ();
  142. X            continue;    /* skip code to end ruleset */
  143. X        case 'S':
  144. X            endruleset ();
  145. X            ruleset ();
  146. X            continue;    /* skip code to end ruleset */
  147. X        case 'R':
  148. X            rule ();
  149. X            continue;    /* skip code to end ruleset */
  150. X        case 'D':
  151. X            endruleset ();
  152. X            def ();
  153. X            break;
  154. X        case 'C':
  155. X            class ();
  156. X            break;
  157. X        case 'F':
  158. X            fileclass ();
  159. X            break;
  160. X        case 'M':
  161. X            endruleset ();
  162. X            mailer ();
  163. X            break;
  164. X        case 'H':
  165. X            header ();
  166. X            break;
  167. X        case 'O':
  168. X            option ();
  169. X            break;
  170. X        case 'T':
  171. X            trusted ();
  172. X            break;
  173. X        case 'P':
  174. X            precedence ();
  175. X            break;
  176. X        default:
  177. X            other ();
  178. X            continue;    /* skip code to end ruleset */
  179. X        }
  180. X        endruleset ();
  181. X    }
  182. X    endruleset ();        /* just in case */
  183. X    exit (0);
  184. X    /*NOTREACHED*/
  185. X}
  186. X
  187. X/* comment --- produce a comment */
  188. X
  189. Xcomment ()
  190. X{
  191. X    static char format[] = "/* %s */\n";
  192. X    register int i = strlen (buffer) - 1;
  193. X
  194. X    /* try to be semi-intelligent about comments */
  195. X
  196. X    if (buffer[1] == '\0')
  197. X        printf ("\n");
  198. X    else if (isspace (buffer[1]) && buffer[i] != '#')
  199. X    {
  200. X        for (i = 1; isspace (buffer[i]); i++)
  201. X            ;
  202. X        printf (format, buffer + i);
  203. X    }
  204. X    else
  205. X        printf (format, buffer);
  206. X}
  207. X
  208. X/* ruleset --- name a ruleset */
  209. X
  210. Xruleset ()
  211. X{
  212. X    static int first = 1;
  213. X    register char *cp = buffer + 1;
  214. X
  215. X    if (first)
  216. X    {
  217. X        first = 0;
  218. X        printf ("\n/* These are sample field definitons (cfc) */\n");
  219. X        printf ("\nfield\n\tzero_or_more : match (0*);\n");
  220. X        printf ("\tone_or_more : match (1*);\n");
  221. X        printf ("\texactly_one : match (1);\n");
  222. X        printf ("\tany_in_? : match (1) in ?;\n");
  223. X        printf ("\tany_not_in_? : match (0) in ?;\n\n");
  224. X    }
  225. X
  226. X    printf ("ruleset\n\tRULESET_");
  227. X    while (*cp && ! isspace (*cp))
  228. X    {
  229. X        putchar (*cp);
  230. X        cp++;
  231. X    }
  232. X
  233. X    printf (" {");
  234. X    if (*cp)
  235. X        printf ("\t/* %s */", cp);
  236. X    putchar ('\n');
  237. X    inruleset++;
  238. X}
  239. X
  240. X/* rule --- print out a rule */
  241. X
  242. Xrule ()
  243. X{
  244. X    register char *cp = buffer + 1;
  245. X    register char *cp2;
  246. X    register int com = 0;
  247. X
  248. X    /* first, split it up into LHS, RHS, COMMENT */
  249. X
  250. X    while (*cp != '\t')
  251. X        cp++;
  252. X    *cp = '\0';
  253. X
  254. X    cp++;
  255. X    while (*cp == '\t')
  256. X        cp++;
  257. X    cp2 = cp;
  258. X    while (*cp && *cp != '\t')
  259. X        cp++;
  260. X    if (*cp == '\t' && cp[1])
  261. X    {
  262. X        *cp = '\0';
  263. X        com++;
  264. X        cp++;
  265. X        while (*cp == '\t')
  266. X            cp++;
  267. X    }
  268. X
  269. X    /* now print */
  270. X    lhs (buffer + 1);    /* left hand side */
  271. X    if (com)
  272. X        printf ("\t/* %s */", cp);
  273. X    putchar ('\n');
  274. X    rhs (cp2);        /* right hand side */
  275. X}
  276. X
  277. X/* lhs --- left hand side of a production */
  278. X
  279. Xlhs (text)
  280. Xchar *text;
  281. X{
  282. X    register char *cp = text;
  283. X    register int conditional = 0;
  284. X    register int quoting = 0;
  285. X
  286. X    printf ("\tif (");
  287. X    for (; *cp; cp++)
  288. X    {
  289. X        switch (*cp) {
  290. X        case '$':
  291. X            if (quoting)
  292. X            {
  293. X                quoting = 0;
  294. X                putchar ('"');
  295. X            }
  296. X            switch (*++cp) {
  297. X            case '*':
  298. X                printf (" zero_or_more ");
  299. X                break;
  300. X            case '+':
  301. X                printf (" one_or_more ");
  302. X                break;
  303. X            case '-':
  304. X                printf (" exactly_one ");
  305. X                break;
  306. X            case '=':
  307. X                printf (" any_in_%c ", *++cp);
  308. X                break;
  309. X            case '~':
  310. X                printf (" any_not_in_%c ", *++cp);
  311. X                break;
  312. X            case '?':
  313. X                printf (" ifset (%s, ", macro (*++cp));
  314. X                conditional++;
  315. X                break;
  316. X            case '|':
  317. X                printf (", ");
  318. X                break;
  319. X            case '.':
  320. X                putchar (')');
  321. X                conditional--;
  322. X                break;
  323. X            case '1':
  324. X            case '2':
  325. X            case '3':
  326. X            case '4':
  327. X            case '5':
  328. X            case '6':
  329. X            case '7':
  330. X            case '8':
  331. X            case '9':
  332. X                printf ("$%c", *cp);
  333. X                break;
  334. X            default:
  335. X                if (quoting)
  336. X                    printf ("${%s}", macro (*cp));
  337. X                else
  338. X                    printf ("$%s", macro (*cp));
  339. X                break;
  340. X            }
  341. X            break;
  342. X        default:
  343. X            if (ispunct (*cp))
  344. X            {
  345. X                if (quoting)    /* end a literal */
  346. X                {
  347. X                    quoting = 0;
  348. X                    putchar ('"');
  349. X                }
  350. X                /* else
  351. X                    do nothing */
  352. X            }
  353. X            else
  354. X            {
  355. X                if (! quoting)    /* start a literal */
  356. X                {
  357. X                    quoting = 1;
  358. X                    putchar ('"');
  359. X                }
  360. X                /* else
  361. X                    do nothing */
  362. X            }
  363. X            putchar (*cp);    /* print the character */
  364. X            break;
  365. X        }
  366. X    }
  367. X    if (quoting)
  368. X        putchar ('"');
  369. X    if (conditional)
  370. X        die ("lhs");
  371. X    printf (")");
  372. X}
  373. X
  374. X/* rhs --- right hand side of a production */
  375. X
  376. Xrhs (text)
  377. Xchar *text;
  378. X{
  379. X    register char *cp = text;
  380. X    char *index ();
  381. X    register int open = 0;
  382. X    register int conditional = 0;
  383. X    register int quoting = 0;
  384. X
  385. X    printf ("\t\t");
  386. X
  387. X    if (*cp == '$' && index ("#@:", cp[1]) != NULL)
  388. X        ;    /* not the default */
  389. X    else
  390. X    {
  391. X        printf ("retry (");
  392. X        open++;
  393. X    }
  394. X
  395. X    for (; *cp; cp++)
  396. X    {
  397. X        switch (*cp) {
  398. X        case '$':
  399. X            if (quoting)
  400. X            {
  401. X                quoting = 0;
  402. X                putchar ('"');
  403. X            }
  404. X            switch (*++cp) {
  405. X            case '>':
  406. X                printf ("RULESET_");
  407. X                for (cp++; *cp && isdigit (*cp); cp++)
  408. X                    putchar (*cp);
  409. X                cp--;
  410. X                printf (" (");
  411. X                open++;
  412. X                break;
  413. X            case '[':
  414. X                printf ("canon (");
  415. X                open++;
  416. X                break;
  417. X            case ']':
  418. X                putchar (')');
  419. X                open--;
  420. X                break;
  421. X            case '?':
  422. X                printf ("ifset (%s, ", macro (*++cp));
  423. X                conditional++;
  424. X                break;
  425. X            case '|':
  426. X                putchar (',');
  427. X                break;
  428. X            case '.':
  429. X                putchar (')');
  430. X                conditional--;
  431. X                break;
  432. X            case '#':
  433. X                printf ("resolve (mailer (");
  434. X                if (strncmp (cp+1, "local$", 6) == 0
  435. X                    || strncmp (cp+1, "error$", 6) == 0)
  436. X                    goto skiphost;
  437. X            loop1:
  438. X                for (cp++; *cp != '$'; cp++)
  439. X                    putchar (*cp);
  440. X                cp++;
  441. X                if (*cp != '@')
  442. X                {
  443. X                    printf ("$%c", *cp);
  444. X                    goto loop1;
  445. X                }
  446. X                printf ("),\n\t\t\t\thost (");
  447. X            skiphost:
  448. X            loop2:
  449. X                for (cp++; *cp != '$'; cp++)
  450. X                    putchar (*cp);
  451. X                cp++;
  452. X                if (*cp != ':')
  453. X                {
  454. X                    printf ("$%c", *cp);
  455. X                    goto loop2;
  456. X                }
  457. X                printf ("),\n\t\t\t\tuser (");
  458. X                for (cp++; *cp; cp++)
  459. X                    putchar (*cp);
  460. X                printf ("))");
  461. X                goto out;    /* string is exhausted */
  462. X                /* break; */
  463. X            case '@':
  464. X                printf ("return (");
  465. X                open++;
  466. X                break;
  467. X            case ':':
  468. X                printf ("next (");
  469. X                open++;
  470. X                break;
  471. X            case '1':
  472. X            case '2':
  473. X            case '3':
  474. X            case '4':
  475. X            case '5':
  476. X            case '6':
  477. X            case '7':
  478. X            case '8':
  479. X            case '9':
  480. X                printf ("$%c", *cp);
  481. X                break;
  482. X            default:
  483. X                if (quoting)
  484. X                    printf ("${%s}", macro (*cp));
  485. X                else
  486. X                    printf ("$%s", macro (*cp));
  487. X                break;
  488. X            }
  489. X            break;
  490. X        default:
  491. X            if (ispunct (*cp))
  492. X            {
  493. X                if (quoting)    /* end a literal */
  494. X                {
  495. X                    quoting = 0;
  496. X                    putchar ('"');
  497. X                }
  498. X                /* else
  499. X                    do nothing */
  500. X            }
  501. X            else
  502. X            {
  503. X                if (! quoting)    /* start a literal */
  504. X                {
  505. X                    quoting = 1;
  506. X                    putchar ('"');
  507. X                }
  508. X                /* else
  509. X                    do nothing */
  510. X            }
  511. X            putchar (*cp);    /* print the character */
  512. X            break;
  513. X        }
  514. X    }
  515. Xout:
  516. X    if (quoting)
  517. X        putchar ('"');
  518. X    while (open--)
  519. X        putchar (')');
  520. X    printf (";\n");
  521. X    if (conditional)
  522. X        die ("rhs");
  523. X}
  524. X
  525. X/* def --- define a macro */
  526. X
  527. Xdef ()
  528. X{
  529. X    register char *mac = buffer + 1, *value = buffer + 2;
  530. X    register int conditional = 0;
  531. X
  532. X    printf ("macro\n\t%s = \"", macro (*mac));
  533. X
  534. X    while (*value)
  535. X    {
  536. X        switch (*value) {
  537. X        case '$':
  538. X            switch (*++value) {
  539. X            case '?':
  540. X                printf ("ifset (%s, ", macro (*++value));
  541. X                conditional++;
  542. X                break;
  543. X            case '|':
  544. X                putchar (',');
  545. X                break;
  546. X            case '.':
  547. X                putchar (')');
  548. X                conditional--;
  549. X                break;
  550. X            default:
  551. X                printf ("${%s}", macro (*value));
  552. X                break;
  553. X            }
  554. X            break;
  555. X        default:
  556. X            putchar (*value);
  557. X            break;
  558. X        }
  559. X        value++;
  560. X    }
  561. X    printf ("\";\n");
  562. X    if (conditional)
  563. X        die ("def");
  564. X}
  565. X
  566. X/* class --- define a class list */
  567. X
  568. Xclass ()
  569. X{
  570. X    register char *name = buffer + 1, *value = buffer + 2;
  571. X
  572. X    printf ("class\n\t%c = { ", *name);
  573. X
  574. X    while (*value && isspace (*value))
  575. X        value++;
  576. X
  577. X    while (*value)
  578. X    {
  579. X        if (isspace (*value))
  580. X        {
  581. X            printf (", ");
  582. X            while (isspace (*value))
  583. X                value++;
  584. X            value--;    /* cancel loop */
  585. X        }
  586. X        else
  587. X            putchar (*value);
  588. X        value++;
  589. X    }
  590. X    printf (" };\n");
  591. X}
  592. X
  593. X/* fileclass --- define a class that is to be read from a file */
  594. X
  595. Xfileclass ()
  596. X{
  597. X    register char *name = buffer + 1, *value = buffer + 2;
  598. X
  599. X    printf ("class\n\t%c = readclass (\"", *name);
  600. X    for (; *value && !isspace (*value); value++)
  601. X        putchar (*value);
  602. X    putchar ('"');
  603. X    while (*value && isspace (*value))
  604. X        value++;
  605. X    if (*value)
  606. X        printf (", \"%s\"", value);
  607. X    printf (");\n");
  608. X}
  609. X
  610. X/* mailer --- convert a mailer specification */
  611. X
  612. Xmailer ()
  613. X{
  614. X    register char *cp = buffer + 1;
  615. X
  616. X    printf ("mailer\n\t");
  617. X    for (; *cp != ','; cp++)
  618. X        putchar (*cp);
  619. X    cp++;
  620. X    printf (" {\n");    /* just did mailer name */
  621. X
  622. X#define skipname()    cp++; while (*cp != '=') cp++; cp++
  623. X#define value()        for (; *cp && *cp != ','; cp++) putchar (*cp); cp++
  624. X
  625. Xloop:
  626. X    while (*cp && isspace (*cp))
  627. X        cp++;
  628. X
  629. X    printf ("\t\t");
  630. X    switch (*cp) {
  631. X    case 'A':
  632. X        skipname ();
  633. X        printf ("Argv = \"");
  634. X        for (; *cp && *cp != ','; cp++)
  635. X        {
  636. X            if (*cp == '$')    /* XXX: assume no conditionals */
  637. X                printf ("${%s}", macro (*++cp));
  638. X            else if (*cp == '"')
  639. X                printf ("\\\"");
  640. X            else
  641. X                putchar (*cp);
  642. X        }
  643. X        cp++;    /* do manually what value does */
  644. X        putchar ('"');
  645. X        break;
  646. X
  647. X    case 'E':
  648. X        skipname ();
  649. X        printf ("Eol = \"");
  650. X        value ();
  651. X        putchar ('"');
  652. X        break;
  653. X
  654. X    case 'F':
  655. X        skipname ();
  656. X        printf ("Flags = { ");
  657. X        for (; *cp && *cp != ','; cp++)
  658. X        {
  659. X            printf ("%s", mflags (*cp));
  660. X            if (cp[1] && cp[1] != ',')
  661. X                printf (", ");
  662. X        }
  663. X        cp++;    /* do manually what value does */
  664. X        printf (" }");
  665. X        break;
  666. X
  667. X    case 'M':
  668. X        skipname ();
  669. X        printf ("Maxsize = \"");
  670. X        value ();
  671. X        putchar ('"');
  672. X        break;
  673. X
  674. X    case 'P':
  675. X        skipname ();
  676. X        printf ("Path = \"");
  677. X        value ();
  678. X        putchar ('"');
  679. X        break;
  680. X
  681. X    case 'R':
  682. X        skipname ();
  683. X        printf ("Recipient = RULESET_");
  684. X        value ();
  685. X        break;
  686. X
  687. X    case 'S':
  688. X        skipname ();
  689. X        printf ("Sender = RULESET_");
  690. X        value ();
  691. X        break;
  692. X
  693. X    case '\0':
  694. X        goto done;
  695. X    }
  696. X
  697. X    if (cp[-1] && cp[-1] == ',')
  698. X    {
  699. X        printf (",\n");
  700. X        goto loop;
  701. X    }
  702. X    else
  703. X        putchar ('\n');
  704. X
  705. Xdone:
  706. X    /* handle continuation lines */
  707. X    if (ngets (buffer) != NULL)
  708. X    {
  709. X        line++;
  710. X        if (buffer[0] == '\t')
  711. X        {
  712. X            cp = buffer;
  713. X            goto loop;
  714. X        }
  715. X        else
  716. X            ungets (buffer);
  717. X    }
  718. X    else
  719. X        ungets ((char *) NULL);
  720. X    
  721. X    printf ("\t};\n");
  722. X
  723. X#undef value
  724. X#undef skipname
  725. X}
  726. X
  727. X/* header --- define sendmail headers */
  728. X
  729. Xheader ()
  730. X{
  731. X    register char *cp = buffer + 1;
  732. X    register int flags = 0;
  733. X    register int conditional = 0;
  734. X
  735. X    printf ("header\n\t");
  736. X    if (*cp == '?')        /* header for mailers  with these flags */
  737. X    {
  738. X        flags++;
  739. X        printf ("for (");
  740. X        for (cp++; *cp != '?'; cp++)
  741. X        {
  742. X            printf ("%s", mflags (*cp));
  743. X            if (cp[1] != '?')
  744. X                putchar (',');
  745. X        }
  746. X        printf (") {\n\t\t");
  747. X        cp++;    /* skip final '?' */
  748. X    }
  749. X
  750. X    printf ("define (\"");
  751. X    for (; *cp && ! isspace (*cp); cp++)
  752. X        putchar (*cp);
  753. X    printf ("\", \"");
  754. X
  755. Xbody:
  756. X    while (*cp)
  757. X    {
  758. X        switch (*cp) {
  759. X        case '$':
  760. X            switch (*++cp) {
  761. X            case '?':
  762. X                printf ("ifset (%s, ", macro (*++cp));
  763. X                conditional++;
  764. X                break;
  765. X            case '|':
  766. X                putchar (',');
  767. X                break;
  768. X            case '.':
  769. X                putchar (')');
  770. X                conditional--;
  771. X                break;
  772. X            default:
  773. X                printf ("${%s}", macro (*cp));
  774. X                break;
  775. X            }
  776. X            break;
  777. X        default:
  778. X            putchar (*cp);
  779. X            break;
  780. X        }
  781. X        cp++;
  782. X    }
  783. X
  784. X    /* handle continuation lines */
  785. X    if (ngets (buffer) != NULL)
  786. X    {
  787. X        line++;
  788. X        if (buffer[0] == '\t')
  789. X        {
  790. X            printf ("\\\n");
  791. X            cp = buffer + 1;
  792. X            goto body;
  793. X        }
  794. X        else
  795. X            ungets (buffer);
  796. X    }
  797. X    else
  798. X        ungets ((char *) NULL);
  799. X
  800. X    printf ("\");\n");
  801. X
  802. X    if (flags)
  803. X        printf ("\t};\n");
  804. X
  805. X    if (conditional)
  806. X        die ("header");
  807. X}
  808. X
  809. X/* option --- translate a sendmail option to an ease option */
  810. X
  811. Xoption ()
  812. X{
  813. X    register char *name = buffer + 1, *value = buffer + 2;
  814. X
  815. X    printf ("options\n\t");
  816. X    if (*name == 'd')        /* delivery */
  817. X        printf ("o_delivery = %s;\n", delivoption (*value));
  818. X    else if (*name == 'e')        /* handling */
  819. X        printf ("o_handling = %s;\n", handle_option (*value));
  820. X    else
  821. X        printf ("%s = \"%s\";\n", optionname (*name), value);
  822. X}
  823. X
  824. X/* trusted --- define the list of trusted users */
  825. X
  826. Xtrusted ()
  827. X{
  828. X    register char *cp = buffer + 1;
  829. X
  830. X    while (*cp)
  831. X    {
  832. X        if (isspace (*cp))
  833. X            *cp = ',';
  834. X        cp++;
  835. X    }
  836. X    printf ("trusted\n\t{ %s };\n", buffer+1);
  837. X}
  838. X
  839. X/* precedence --- define the precedence of a message class */
  840. X
  841. Xprecedence ()
  842. X{
  843. X    register char *cp = buffer + 1;
  844. X
  845. X    printf ("precedence\n\t");
  846. X    for (; *cp && *cp != '='; cp++)
  847. X        putchar (*cp);
  848. X    printf (" = %s;\n", ++cp);
  849. X}
  850. X
  851. X/* other --- not a sendmail control line */
  852. X
  853. Xother ()
  854. X{
  855. X    printf ("%s\n", buffer);
  856. X}
  857. X
  858. Xdie (routine)
  859. Xchar *routine;
  860. X{
  861. X    fprintf (stderr, "%s: malformed input line %d: fatal error\n",
  862. X            routine, line);
  863. X    exit (1);
  864. X}
  865. X
  866. X/* macro --- return name for sendmail predefined macro */
  867. X
  868. Xchar *macro (c)
  869. Xchar c;
  870. X{
  871. X    static char buf[2] = { '\0', '\0' };
  872. X
  873. X    switch (c) {
  874. X    case 'a':    /* The origination date in Arpanet format */
  875. X        return ("m_odate");
  876. X
  877. X    case 'b':    /* The current date in Arpanet format */
  878. X        return ("m_adate");
  879. X
  880. X    case 'c':    /* The hop count */
  881. X        return ("m_hops");
  882. X
  883. X    case 'd':    /* The date in UNIX (ctime) format */
  884. X        return ("m_udate");
  885. X
  886. X    case 'e':    /* The SMTP entry message */
  887. X        return ("m_smtp");
  888. X
  889. X    case 'f':    /* The sender (from) address */
  890. X        return ("m_saddr");
  891. X
  892. X    case 'g':    /* The sender address relative to the recipient */
  893. X        return ("m_sreladdr");
  894. X
  895. X    case 'h':    /* The recipient host */
  896. X        return ("m_rhost");
  897. X
  898. X    case 'i':    /* The queue id */
  899. X        return ("m_qid");
  900. X
  901. X    case 'j':    /* The official domain name for this site */
  902. X        return ("m_oname");
  903. X
  904. X    case 'l':    /* The format of the UNIX from line */
  905. X        return ("m_ufrom");
  906. X
  907. X    case 'n':    /* The name of the daemon (for error messages) */
  908. X        return ("m_daemon");
  909. X
  910. X    case 'o':    /* The set of "operators" in addresses */
  911. X        return ("m_addrops");
  912. X
  913. X    case 'p':    /* Sendmail's pid */
  914. X        return ("m_pid");
  915. X
  916. X    case 'q':    /* The default format of sender address */
  917. X        return ("m_defaddr");
  918. X
  919. X    case 'r':    /* Protocol used */
  920. X        return ("m_protocol");
  921. X
  922. X    case 's':    /* Sender's host name */
  923. X        return ("m_shostname");
  924. X
  925. X    case 't':    /* A numeric representation of the current time */
  926. X        return ("m_ctime");
  927. X
  928. X    case 'u':    /* The recipient user */
  929. X        return ("m_ruser");
  930. X
  931. X    case 'v':    /* The version number of sendmail */
  932. X        return ("m_version");
  933. X
  934. X    case 'w':    /* The hostname of this site */
  935. X        return ("m_sitename");
  936. X
  937. X    case 'x':    /* The full name of the sender */
  938. X        return ("m_sname");
  939. X
  940. X    case 'y':    /* The id of the sender's tty */
  941. X        return ("m_stty");
  942. X
  943. X    case 'z':    /* The home directory of the recipient */
  944. X        return ("m_rhdir");
  945. X
  946. X    default:
  947. X        buf[0] = c;
  948. X        return (buf);
  949. X    }
  950. X}
  951. X
  952. X#define docompat(val)    if (compat) goto warn; else return (val)
  953. X#define dofundoc(val)    if (undoc) \
  954. Xfprintf (stderr, "warning: undocumented flag '%c' used on line %d\n", c, line);\
  955. Xreturn (val)
  956. X
  957. X/* mflags --- convert sendmail mailer flags to ease names */
  958. X
  959. Xchar *mflags (c)
  960. Xchar c;
  961. X{
  962. X    static char buf[2] = { '\0', '\0' };
  963. X
  964. X    switch (c) {
  965. X    case 'f':    return ("f_ffrom");
  966. X    case 'r':    return ("f_rfrom");
  967. X    case 'S':    return ("f_noreset");
  968. X    case 'n':    return ("f_noufrom");
  969. X    case 'l':    return ("f_locm");
  970. X    case 's':    return ("f_strip"); 
  971. X    case 'm':    return ("f_mult");
  972. X    case 'F':    return ("f_from");
  973. X    case 'D':    return ("f_date");
  974. X    case 'M':    return ("f_mesg");
  975. X    case 'x':    return ("f_full");    
  976. X    case 'P':    return ("f_return");    
  977. X    case 'u':    return ("f_upperu");    
  978. X    case 'h':    return ("f_upperh");    
  979. X    case 'A':    return ("f_arpa");    
  980. X    case 'U':    return ("f_ufrom");    
  981. X    case 'e':    return ("f_expensive");    
  982. X    case 'X':    return ("f_dot");    
  983. X    case 'L':    return ("f_llimit");    
  984. X    case 'p':    return ("f_retsmtp");    
  985. X    case 'I':    return ("f_smtp");    
  986. X    case 'C':    return ("f_addrw");    
  987. X    case 'E':    docompat ("f_escape");
  988. X    case 'R':    dofundoc ("f_rport");
  989. X    default:
  990. X    warn:
  991. X        fprintf (stderr,
  992. X            "warning: non standard mailer flag '%c' on line %d\n",
  993. X                c, line);
  994. X        buf[0] = c;
  995. X        return buf;
  996. X    }
  997. X}
  998. X
  999. X#define doOundoc(val)    if (undoc) \
  1000. Xfprintf (stderr, "warning: undocumented option '%c' used on line %d\n", c, line);\
  1001. Xreturn (val)
  1002. X
  1003. X/* optionname --- convert sendmail options to ease names */
  1004. X
  1005. Xchar *optionname (c)
  1006. Xchar c;
  1007. X{
  1008. X    static char buf[2] = { '\0', '\0' };
  1009. X
  1010. X    switch (c) {
  1011. X    case 'A':    return ("o_alias");
  1012. X    case 'a':    return ("o_ewait");
  1013. X    case 'B':    return ("o_bsub");
  1014. X    case 'C':    doOundoc ("o_checkpoint");
  1015. X    case 'c':    return ("o_qwait");
  1016. X    case 'd':    return ("o_delivery");
  1017. X    case 'D':    return ("o_rebuild");
  1018. X    case 'e':    return ("o_handling");
  1019. X    case 'F':    return ("o_tmode");
  1020. X    case 'f':    return ("o_usave");
  1021. X    case 'g':    return ("o_gid");
  1022. X    case 'H':    return ("o_fsmtp");
  1023. X    case 'i':    return ("o_skipd");
  1024. X    case 'L':    return ("o_slog");
  1025. X    case 'm':    return ("o_rsend");
  1026. X    case 'N':    return ("o_dnet");
  1027. X    case 'n':    doOundoc ("o_validate");
  1028. X    case 'o':    return ("o_hformat");
  1029. X    case 'P':       doOundoc ("o_pmaster");
  1030. X    case 'Q':    return ("o_qdir");
  1031. X    case 'q':    docompat ("o_qfactor");
  1032. X    case 'r':    return ("o_tread");
  1033. X    case 'S':    return ("o_flog");
  1034. X    case 's':    return ("o_safe");
  1035. X    case 'T':    return ("o_qtimeout");
  1036. X    case 't':    return ("o_timezone");
  1037. X    case 'u':    return ("o_dmuid");
  1038. X    case 'v':    return ("o_verbose");
  1039. X    case 'W':    return ("o_wizpass");
  1040. X    case 'x':    return ("o_loadq");
  1041. X    case 'X':    return ("o_loadnc");
  1042. X    case 'Y':    docompat ("o_newproc");
  1043. X    case 'y':    docompat ("o_recipfactor");
  1044. X    case 'Z':    docompat ("o_prifactor");
  1045. X    case 'z':    docompat ("o_waitfactor");
  1046. X    default:
  1047. X    warn:
  1048. X        fprintf (stderr,
  1049. X            "warning: non standard option '%c' on line %d\n",
  1050. X                c, line);
  1051. X        buf[0] = c;
  1052. X        return buf;
  1053. X    }
  1054. X}
  1055. X
  1056. X/* delivoption --- convert sendmail delivery option value to ease name */
  1057. X
  1058. Xchar *delivoption (c)
  1059. Xchar c;
  1060. X{
  1061. X    static char buf[2] = { '\0', '\0' };
  1062. X
  1063. X    switch (c) {
  1064. X    case 'i':    return ("d_interactive");
  1065. X    case 'b':    return ("d_background");
  1066. X    case 'q':    return ("d_queue");
  1067. X    default:
  1068. X        fprintf (stderr,
  1069. X    "warning: non standard delivery option '%c' on line %d\n", c, line);
  1070. X        buf[0] = c;
  1071. X        return buf;
  1072. X    }
  1073. X}
  1074. X
  1075. X/* handle_option --- convert sendmail handling option value to ease name */
  1076. X
  1077. Xchar *handle_option (c)
  1078. Xchar c;
  1079. X{
  1080. X    static char buf[2] = { '\0', '\0' };
  1081. X
  1082. X    switch (c) {
  1083. X    case 'p':    return ("h_print");
  1084. X    case 'q':    return ("h_exit");
  1085. X    case 'm':    return ("h_mail");
  1086. X    case 'w':    return ("h_write");
  1087. X    case 'e':    return ("h_mailz");
  1088. X    default:
  1089. X        fprintf (stderr,
  1090. X    "warning: non standard handling option '%c' on line %d\n", c, line);
  1091. X        buf[0] = c;
  1092. X        return buf;
  1093. X    }
  1094. X}
  1095. X
  1096. X/*
  1097. X * "buffered" i/o routines. These are necessary since
  1098. X * mail headers may have continuation lines, and we can't see if
  1099. X * a continuation line is there without getting it. If it isn't,
  1100. X * just put it back.
  1101. X */
  1102. X
  1103. Xint saved = 0;
  1104. Xchar *saveb = NULL;
  1105. X
  1106. X/* ngets --- get a line of input from either saved buffer or stdin */
  1107. X
  1108. Xchar *ngets (bp)
  1109. Xchar *bp;
  1110. X{
  1111. X    if (! saved)
  1112. X        return (gets (bp));
  1113. X
  1114. X    saved = 0;
  1115. X    bp = saveb;
  1116. X    saveb = NULL;
  1117. X    return (bp);
  1118. X}
  1119. X
  1120. X/* ungets --- put a buffer back on the input, so to speak */
  1121. X
  1122. Xvoid ungets (bp)
  1123. Xchar *bp;
  1124. X{
  1125. X    saved = 1;
  1126. X    saveb = bp;
  1127. X    line--;
  1128. X}
  1129. END_OF_FILE
  1130. if test 21150 -ne `wc -c <'cfc/cfc.c'`; then
  1131.     echo shar: \"'cfc/cfc.c'\" unpacked with wrong size!
  1132. fi
  1133. chmod +x 'cfc/cfc.c'
  1134. # end of 'cfc/cfc.c'
  1135. fi
  1136. if test -f 'doc/ease.paper' -a "${1}" != "-c" ; then 
  1137.   echo shar: Will not clobber existing file \"'doc/ease.paper'\"
  1138. else
  1139. echo shar: Extracting \"'doc/ease.paper'\" \(31245 characters\)
  1140. sed "s/^X//" >'doc/ease.paper' <<'END_OF_FILE'
  1141. X...
  1142. X... $Header: ease.paper,v 1.6 88/06/15 10:12:36 root Locked $
  1143. X... 
  1144. X... $Log:    ease.paper,v $
  1145. X... Revision 1.6  88/06/15  10:12:36  root
  1146. X... Some editorial cleanup, added Acknowledgements section.
  1147. X... 
  1148. X... Revision 1.5  88/01/21  17:19:35  root
  1149. X... Several editorial changes. ADR.
  1150. X... 
  1151. X... Revision 1.4  87/12/23  11:30:47  root
  1152. X... Updated list of authors. Documented extended canon() capability.
  1153. X... Integrated fluke changes in a little better. ADR.
  1154. X... 
  1155. X... Revision 1.3  87/11/04  11:33:45  root
  1156. X... Documented new keyword "while" which is equivalent to "if". ADR.
  1157. X... 
  1158. X... Revision 1.2  87/08/13  17:08:05  root
  1159. X... Changes from Jeff Stearns, fluke!jeff, for Sun. ADR.
  1160. X... 
  1161. X... Revision 1.1  87/08/13  17:05:00  root
  1162. X... Initial revision
  1163. X... 
  1164. X...
  1165. X.LP
  1166. X.TL
  1167. XEase: A Configuration Language
  1168. Xfor Sendmail
  1169. X.AU
  1170. XJames S. Schoner
  1171. X.AI
  1172. XPurdue University Computing Center
  1173. XWest Lafayette, Indiana  47907
  1174. X.AU
  1175. XJeff P. Stearns
  1176. X.AI
  1177. XJohn Fluke Manufacturing Company
  1178. XEverett, Washington  98206
  1179. X.AU
  1180. XArnold D. Robbins
  1181. X.AI
  1182. XEmory University Computing Center
  1183. XAtlanta, Georgia  30322
  1184. X.sp 2
  1185. X.I
  1186. X.ce
  1187. XABSTRACT
  1188. X.R
  1189. X.PP
  1190. XThe rapid expansion of computer networks and ensuing variation among mailing
  1191. Xaddress formats have made address interpretation an increasingly
  1192. Xcomplex task.  In the UNIX* 4.2BSD operating system, a program named 
  1193. X\fIsendmail\fR was introduced which provided a
  1194. Xgeneral internetwork mail routing facility.  This facility has significantly
  1195. Xdiminished the complexity of handling address interpretation.
  1196. X.PP
  1197. X\fISendmail\fR's address interpretation is based on a rewriting
  1198. Xsystem composed of
  1199. Xa number of rewriting rules (or productions) arranged as part of a 
  1200. Xconfiguration file.  Unfortunately, the syntactical format of a
  1201. Xconfiguration file for \fIsendmail\fR is both terse and rigid, making it
  1202. Xrather difficult to modify.  The standard format certainly serves its 
  1203. Xpurpose, but, as 
  1204. Xthe need to change these configurations increases in frequency, a more 
  1205. Xreadable format (i.e., one that is similar to the format 
  1206. Xof modern programming languages) is required to permit reasonably 
  1207. Xquick modifications to the configuration.  As a solution to this problem, 
  1208. X\fBEase\fR 
  1209. Xprovides a level of abstraction which eliminates most of the current
  1210. Xsyntactic hindrances
  1211. Xfaced by programmers who must reconfigure \fIsendmail\fR's 
  1212. Xaddress parsing scheme.  
  1213. X.PP
  1214. XAs a high-level specification format, \fBEase\fR is proving to be an 
  1215. Xexcellent alternative to \fIsendmail\fR's cryptic 
  1216. Xconfiguration file syntax.  The syntactic structures of \fBEase\fR 
  1217. Xare patterned after modern language constructs, making the language
  1218. Xeasy to learn and easy to remember.  The format of the address rewriting
  1219. Xrule is perhaps the most significant syntactical improvement.  It was 
  1220. Xundoubtedly
  1221. Xthe most needed improvement.  Nevertheless, every element of a configuration 
  1222. Xfile is structurally enhanced through the use of \fBEase\fR. 
  1223. X.FS
  1224. X*  UNIX is a registered trademark of AT&T.
  1225. X.FE
  1226. X.sp 2
  1227. X.NH
  1228. XIntroduction
  1229. X.PP
  1230. XThe \fBEase\fR language is a high-level specification format for \fIsendmail\fR's
  1231. Xconfiguration file.  The motivation for its development
  1232. Xwas to fulfill a goal of providing a readable and easily modifiable 
  1233. X\fIsendmail\fR configuration file format.  \fBEase\fR fulfills this goal by
  1234. Xshielding the programmer from the cryptic configuration specification required
  1235. Xby \fIsendmail\fR and providing a high-level language with which the programmer
  1236. Xmay specify all modifications to a configuration file.  The development 
  1237. Xof Ease coincided with
  1238. Xthe development of an \fBEase\fR translator, \fIet\fR,
  1239. Xwhich translates a configuration file written 
  1240. Xin \fBEase\fR to an
  1241. Xequivalent file of the standard format accepted by \fIsendmail\fR.
  1242. X.NH
  1243. XEase in Profile
  1244. X.PP
  1245. XAs will be seen in the next section, the syntax of \fBEase\fR is quite
  1246. Xreadable and easy to learn.  In order to acquire a relevant perspective
  1247. Xon this issue,
  1248. Xthe reader is advised to examine a raw configuration file for \fIsendmail\fR (the 
  1249. Xoutput
  1250. Xof the \fBEase\fR translator, \fIet\fR, will do nicely).  The raw syntax, while
  1251. Xquite fitting for quick translation, can prove to be a programmer's nightmare.  
  1252. X.PP
  1253. XUndoubtedly, one of the more prominent features of \fBEase\fR is the ability 
  1254. Xto attach
  1255. Xnames to address fields.  When address field names are well-chosen, a distinct,
  1256. Xself-documenting quality becomes a visible part of the address rewriting 
  1257. Xrules.  Ostensibly, address field names provide a new level of semantic 
  1258. Xabstraction.  A brief comparison of the formats can be accomplished by examining
  1259. Xthe following equivalent representations of an address pattern:
  1260. X.DS
  1261. X    user_path@host_name            (\fBEase\fR format)
  1262. X    $+@$-                    (raw format)
  1263. X.DE
  1264. XIn the above, \*Quser_path\*U represents a field of one or more address
  1265. Xtokens, and \*Qhost_name\*U represents one address token exactly.  These
  1266. Xtoken fields are represented by \*Q$+\*U and \*Q$-\*U in the raw format.  Clearly, 
  1267. Xthe \fBEase\fR format is preferable, not only for increased readability, but 
  1268. Xstructural comprehension as well.
  1269. X.PP
  1270. XOther features of \fBEase\fR include ruleset naming, long identifiers for 
  1271. Xmacros and classes, flow-of-control structures, and free formatting.  In
  1272. Xaddition, the C language preprocessor (cpp) can be used for file inclusion
  1273. Xand conditionally defined code constructs.  The next section describes
  1274. Xthe \fBEase\fR language in complete detail.
  1275. X.NH
  1276. XEase Syntax*
  1277. X.FS
  1278. X*  \fINo attempt is made to describe the complete semantic meaning 
  1279. Xassociated with all of the constructs of a sendmail configuration file.  Items 
  1280. Xnot covered in this document include the semantic distinction among rulesets, 
  1281. Xthe special uses of
  1282. Xpre-defined macros, and the method of building configuration files.  To
  1283. Xobtain this information, the reader is advised to refer to
  1284. Xthe Sendmail Installation and Operation Guide (SMM:7 in the 4.3 BSD
  1285. XUNIX System Manager's Manual),
  1286. Xby Eric Allman.\fR
  1287. X.FE
  1288. X.PP
  1289. XAt its highest level, \fBEase\fR can be viewed as a collection of 
  1290. Xblock-structures, where each block begins with a keyword and is followed by
  1291. Xzero or more related definitions and/or declarations.  There are ten distinct 
  1292. Xblock types.  The following is 
  1293. Xa list containing all ten block keywords and the block type it denotes.
  1294. X.TS
  1295. Xcenter;
  1296. Xl l .
  1297. X\fIbind\fR    -ruleset identifier bindings
  1298. X\fImacro\fR    -macro definitions
  1299. X\fIclass\fR    -class definitions
  1300. X\fIoptions\fR    -\fIsendmail\fR option definitions
  1301. X\fIprecedence\fR    -precedence definitions
  1302. X\fItrusted\fR    -trusted users
  1303. X\fIheader\fR    -mail header definitions
  1304. X\fImailer\fR    -mailer definitions
  1305. X\fIfield\fR    -address field definitions
  1306. X\fIruleset\fR    -address rewriting rules
  1307. X.TE
  1308. X.sp 1
  1309. XIn general,
  1310. X.TS
  1311. Xcenter ;
  1312. Xl .
  1313. X
  1314. X* Letters are distinguished by case,
  1315. X
  1316. XT{
  1317. X* An \fBEase\fR identifier is defined to be a letter, followed by zero or 
  1318. Xmore letters, digits, underscores (_), or dashes (-),
  1319. XT}
  1320. X
  1321. XT{
  1322. X* A literal newline or double quotation (") character may be included in 
  1323. Xany quoted string by preceding the character with a backslash (\\\\\), and
  1324. XT}
  1325. X
  1326. XT{
  1327. X* \fBEase\fR source is preprocessed by the C language preprocessor (cpp),
  1328. Xthus source comments (i.e., text enclosed by \*Q/*\*U and \*Q*/\*U) may appear 
  1329. Xanywhere as part of \fBEase\fR whitespace.
  1330. XT}
  1331. X.TE
  1332. X.PP
  1333. XFor notational convenience, this document specifies all reserved
  1334. Xwords of the \fBEase\fR language in italics.  In addition, quantities
  1335. Xenclosed in angle brackets (<..>) represent arbitrary 
  1336. Xidentifiers, strings, or numbers.  
  1337. X.NH 2
  1338. XRuleset Identifier Bindings
  1339. X.PP
  1340. XA ruleset (a set of rewriting rules) is identified solely by an integer 
  1341. Xin \fIsendmail\fR's
  1342. Xconfiguration file.  \fBEase\fR, however, allows each ruleset to be named with
  1343. Xa meaningful identifier.  Since a special numeric association for each 
  1344. Xruleset is required by the address parsing scheme of \fIsendmail\fR, a \fIbind\fR
  1345. Xblock must be present in any \fBEase\fR file which defines one or more 
  1346. Xrulesets.  A
  1347. X\fIbind\fR block consists of the keyword \fIbind\fR, followed by zero or more
  1348. Xstatements of the form:
  1349. X.TS
  1350. Xcenter box;
  1351. Xl .
  1352. X<ruleset-id> = \fIruleset\fR <ruleset-number> ;
  1353. X.TE
  1354. XThe following example, 
  1355. X.sp 1
  1356. X\fIbind\fR
  1357. X.PP
  1358. XFINAL_RW = \fIruleset\fR 4;
  1359. X.sp 1
  1360. Xspecifies that FINAL_RW, the final rewriting ruleset, is \fIsendmail\fR's ruleset 
  1361. Xnumber 4.
  1362. X.NH 2
  1363. XMacro Definitions
  1364. X.PP
  1365. XA macro is an identifier which, when referenced in the text of a program,
  1366. Xis replaced by its value, a string of zero or more characters.  The value
  1367. Xof a macro may include references to other macros, but not itself!  \fISendmail\fR
  1368. Xallows a maximum of 26 user-declared macros in its configuration file.  In 
  1369. Xaddition, there are a number of pre-declared macros which have special meaning
  1370. Xto \fIsendmail\fR (see Appendix A).  \fBEase\fR macros are defined in 
  1371. X\fImacro\fR blocks.  \fBEase\fR allows any macro to be declared 
  1372. X(which is equivalent to simply referencing it) before it is defined.  A macro
  1373. Xidentifier is replaced by its value when it is preceded by the character
  1374. X\*Q$\*U.  In addition, a macro reference inside a quoted string must always 
  1375. Xinclude braces ({}) around the macro identifier (for delimiting purposes).  
  1376. X.PP
  1377. XA \fImacro\fR block consists of the keyword \fImacro\fR, followed by zero
  1378. Xor more statements taking either of the following forms:
  1379. X.TS
  1380. Xcenter box;
  1381. Xl .
  1382. X<macro-identifier> = "<macro-value>" ;
  1383. X<macro-identifier> = \fBconditional-expression\fR ;
  1384. X.TE
  1385. XThe \fBconditional-expression\fR format will be discussed 
  1386. Xlater.  
  1387. X.sp 1
  1388. XThe following example,
  1389. X.sp 1
  1390. X\fImacro\fR
  1391. X.PP
  1392. Xfirst_name = "James";
  1393. X.PP
  1394. Xlast_name = "Schoner";
  1395. X.PP
  1396. Xwhole_name = "${first_name} ${last_name}";
  1397. X.sp 1
  1398. Xdefines the macros first_name, last_name, and whole_name, where whole_name
  1399. Xis the string, "James Schoner".
  1400. X.NH 2
  1401. XClass definitions
  1402. X.PP
  1403. XA class is denoted by an identifier representing a logical grouping of zero 
  1404. Xor more names.  Classes are used to represent the range of values a token
  1405. Xmay assume in the pattern matching of an address.  Further discussion on the
  1406. Xuse of classes will be deferred until address fields are described.
  1407. X.PP
  1408. XOne identifier may be used to distinctly represent both a macro
  1409. Xand class (i.e., the set of macro identifiers and the set of class identifiers
  1410. Xmay form a non-empty intersection).  A name, or class element, may 
  1411. Xbe an identifier or any quoted word.
  1412. X.PP
  1413. XA \fIclass\fR block consists of the keyword \fIclass\fR, followed by zero
  1414. Xor more statements taking any of the following forms:
  1415. X.TS
  1416. Xcenter box;
  1417. Xl .
  1418. X<class-identifier> = { <name1>, <name2>, <name3>, ... } ;
  1419. X<class-identifier> = \fIreadclass\fR ( "<file-name>" ) ;
  1420. X<class-identifier> = \fIreadclass\fR ( "<file-name>", "<read-format>" ) ;
  1421. X.TE
  1422. XThe second and third forms cause \fIsendmail\fR to read the names of the class 
  1423. Xfrom the named
  1424. Xfile.  The third form contains a read format, which should be a \fIscanf\fR(3)
  1425. Xpattern yielding a single string.
  1426. X.sp 1
  1427. XThe following example,
  1428. X.sp 1
  1429. X\fIclass\fR
  1430. X.PP
  1431. Xcampus_hosts = { statistics, engineering, chemistry, physics, physics-2 } ;
  1432. X.PP
  1433. Xversions     = { "1.0", "1.1", "4.0", "4.2", latest-and-greatest } ;
  1434. X.PP
  1435. Xphone_hosts  = \fIreadclass\fR ( "/tmp/phonenet.list" ) ;
  1436. X.sp 1
  1437. Xdefines the classes campus_hosts, versions, and phone_hosts.
  1438. X.NH 2
  1439. XSendmail option definitions
  1440. X.PP
  1441. XA number of options to the \fIsendmail\fR program may be specified in 
  1442. Xan \fIoptions\fR
  1443. Xblock.  For a description of the various \fIsendmail\fR options and their 
  1444. Xvalues, see Appendix B.  
  1445. X.PP
  1446. XAn
  1447. X\fIoptions\fR block consists of the keyword \fIoptions\fR, followed by zero
  1448. Xor more statements taking any of the following forms:
  1449. X.TS
  1450. Xcenter box;
  1451. Xl l .
  1452. X<option-identifier>    = "<option-value>" ;
  1453. X\fIo_delivery\fR    = \fBspecial-value\fR ;
  1454. X\fIo_handling\fR    = \fBspecial-value\fR ;
  1455. X.TE
  1456. XAll but two options (\fIo_delivery\fR and \fIo_handling\fR) use the first 
  1457. Xform.  To specify an option without a value, simply assign to it the null 
  1458. Xstring ("").  The \fBspecial-value\fR field of the second and third form
  1459. Xrefers to special values (non-quoted) which are specified in Appendix B.
  1460. X.sp 1
  1461. XThe following example,
  1462. X.sp 1
  1463. X\fIoptions\fR
  1464. X.PP
  1465. X\fIo_alias\fR = "/usr/lib/aliases" ;
  1466. X.PP
  1467. X\fIo_tmode\fR = "0600" ;
  1468. X.PP
  1469. X\fIo_delivery\fR = \fId_background\fR ;
  1470. X.sp 1
  1471. Xsets the options \fIo_alias\fR, \fIo_tmode\fR, and \fIo_delivery\fR.
  1472. X.NH 2
  1473. XPrecedence definitions
  1474. X.PP
  1475. XMessage headers may contain a \*QPrecedence:\*U field describing the precedence
  1476. Xof the message class.  Identifiers which may appear in the precedence field of
  1477. Xa message are given precedence values in a configuration file \fIprecedence\fR 
  1478. Xdefinition.  This association will be illustrated below in an example.
  1479. X.PP
  1480. XA \fIprecedence\fR block consists of the keyword \fIprecedence\fR, followed 
  1481. Xby zero or more statements of the form:
  1482. X.KS
  1483. X.TS
  1484. Xcenter box;
  1485. Xl .
  1486. X<precedence-identifier> = <precedence-integer> ;
  1487. X.TE
  1488. X.KE
  1489. XThe following example,
  1490. X.sp 1
  1491. X\fIprecedence\fR
  1492. X.PP
  1493. Xspecial-delivery = 100;
  1494. X.PP
  1495. Xjunk = -100;
  1496. X.sp 1
  1497. Xdefines the precedence level for the names \*Qspecial-delivery\*U and 
  1498. X\*Qjunk\*U.  Thus, whenever the name \*Qjunk\*U appears in 
  1499. Xa \*QPrecedence:\*U field, the corresponding message class will be set to -100.
  1500. X.NH 2
  1501. XTrusted users
  1502. X.PP
  1503. X\fISendmail\fR's \fB\-f\fR flag allows trusted users to override the sender's
  1504. Xmachine address.  Trusted users are listed in \fItrusted\fR blocks.  A 
  1505. X\fItrusted\fR block consists of the keyword \fItrusted\fR, followed 
  1506. Xby zero or more sets of users taking the form:
  1507. X.TS
  1508. Xcenter box;
  1509. Xl .
  1510. X{ <user1>, <user2>, <user3>, ... } ;
  1511. X.TE
  1512. XThe following example,
  1513. X.sp 1
  1514. X\fItrusted\fR
  1515. X.PP
  1516. X{ root, uucp, network } ;
  1517. X.PP
  1518. X{ acu, kcs, jss } ;
  1519. X.sp 1
  1520. Xspecifies that the users root, uucp, network, acu, kcs, and jss can be trusted 
  1521. Xto use the \fIsendmail\fR flag, \fB\-f\fR.
  1522. X.NH 2
  1523. XMail header definitions
  1524. X.PP
  1525. XThe format of the message headers inserted by \fIsendmail\fR is defined in one
  1526. Xor more \fIheader\fR blocks in the configuration file.  A \fIheader\fR block
  1527. Xconsists of the keyword \fIheader\fR, followed by zero or more statements
  1528. Xtaking any of the following forms:
  1529. X.TS
  1530. Xcenter box;
  1531. Xl 
  1532. Xl
  1533. Xl
  1534. Xl
  1535. Xl
  1536. Xl
  1537. Xl
  1538. Xl
  1539. Xl
  1540. Xl
  1541. Xl .
  1542. X\fIfor\fR ( <mailer-flag1>, <mailer-flag2>, ... )
  1543. X       \fIdefine\fR ( "<header-title>" , \fBheader-value\fR ) ;
  1544. X
  1545. X\fIfor\fR ( <mailer-flag1>, <mailer-flag2>, ... ) {
  1546. X       \fIdefine\fR ( "<header-title>" , \fBheader-value\fR ) ;
  1547. X       \fIdefine\fR ( "<header-title>" , \fBheader-value\fR ) ;
  1548. X       .
  1549. X       .
  1550. X} ;
  1551. X
  1552. X\fIdefine\fR ( "<header-title>" , \fBheader-value\fR ) ;
  1553. X.TE
  1554. XThe first form is used to define one header for one or more mailer
  1555. Xflags.  The second form differs from the first in that more than one
  1556. Xheader may be defined for a given set of flags.  The third form is used to 
  1557. Xdefine a header,
  1558. Xregardless of mailer flags.  Refer to Appendix C for a list of \fBEase\fR 
  1559. Xidentifiers representing mailer flags.  The header title is a simple
  1560. Xstring of characters (no macro references), whereas the \fBheader-value\fR
  1561. Xis a series of one or more strings and
  1562. X\fBconditional-expressions\fP (discussed later).
  1563. XConcatenation is implicit (as in \fIawk\fP).
  1564. X.sp 1
  1565. XThe following example,
  1566. X.DS
  1567. X\fIheader\fR
  1568. X
  1569. X    \fIdefine\fR ( "Subject:", "") ;
  1570. X
  1571. X    \fIfor\fR ( \fIf_return\fR )
  1572. X        \fIdefine\fR ( "Return-Path:", "<${\fIm_sreladdr\fR}>" ) ;
  1573. X
  1574. X    \fIfor\fR ( \fIf_date\fR ) {
  1575. X        \fIdefine\fR ( "Resent-Date:", "${\fIm_odate\fR}" ) ;
  1576. X        \fIdefine\fR ( "Date:", "${\fIm_odate\fR}" );
  1577. X    } ;
  1578. X.DE
  1579. Xdefines a \*QSubject\*U field for all mailers, regardless of their flags, a
  1580. X\*QReturn-Path\*U field for mailers whose definition specifies
  1581. Xthe flag, \fIf_return\fR, and the headers, \*QResent-Date\*U and \*QDate\*U,
  1582. Xfor mailers whose definition specifies the flag, \fIf_date\fR.
  1583. X.NH 2
  1584. XMailer Definitions
  1585. X.PP
  1586. X\fISendmail\fR's definition of a mailer (or an interface to one) occurs in a
  1587. X\fImailer\fR block.  A \fImailer\fR block consists of the keyword \fImailer\fR,
  1588. Xfollowed by zero or more statements of the form:
  1589. X.TS
  1590. Xcenter box;
  1591. Xl .
  1592. X<mailer-identifier> { \fBmailer-spec\fR } ;
  1593. X.TE
  1594. XThe field, \fBmailer-spec\fR, is a list of zero or more of the
  1595. Xfollowing attribute assignments (where successive assignment statements are
  1596. Xseparated by commas):
  1597. X.TS
  1598. Xcenter ;
  1599. Xl l 
  1600. Xl l
  1601. Xl l
  1602. Xl l
  1603. Xl l
  1604. Xl l
  1605. Xl l .
  1606. X\fIPath\fR    = \fBstring-attribute\fR
  1607. X\fIArgv\fR    = \fBstring-attribute\fR
  1608. X\fIEol\fR    = \fBstring-attribute\fR
  1609. X\fIMaxsize\fR    = \fBstring-attribute\fR
  1610. X\fIFlags\fR    = { <mailer-flag1>, <mailer-flag2>, ... } 
  1611. X\fISender\fR    = <sender-ruleset-id>
  1612. X\fIRecipient\fR    = <recipient-ruleset-id>
  1613. X.TE
  1614. XThe \fBstring-attribute\fR value can take the form of a quoted string
  1615. X(possibly containing macro references) or a \fBconditional-expression\fR 
  1616. X(discussed later).
  1617. X.sp 1
  1618. XThe following example,
  1619. X.sp 1
  1620. X\fImailer\fR
  1621. X.DS
  1622. X    local {
  1623. X        \fIPath\fR        = "/bin/mail",
  1624. X        \fIFlags\fR        = { \fIf_from\fR, \fIf_locm\fR },
  1625. X        \fISender\fR    = Sender_RW,
  1626. X        \fIRecipient\fR    = Recip_RW,
  1627. X        \fIArgv\fR        = "mail -d ${\fIm_ruser\fR}",
  1628. X        \fIMaxsize\fR    = "200000"
  1629. X    } ;
  1630. X.DE
  1631. Xdefines a mailer named \*Qlocal\*U.
  1632. X.NH 2
  1633. XAddress field definitions
  1634. X.PP
  1635. X\fISendmail\fR's address parsing scheme treats an address as a group of tokens
  1636. X(an address token is precisely defined in the Arpanet protocol RFC822).  In
  1637. Xgeneral, \fIsendmail\fR divides an address into tokens based on a list of
  1638. Xcharacters assigned as a string to the special macro \fIm_addrops\fR.  These
  1639. Xcharacters will individually be considered as tokens and will separate tokens
  1640. Xwhen parsing is performed. 
  1641. X.PP
  1642. XFor
  1643. Xthe \fBEase\fR language, there is a distinct set of address tokens (defined
  1644. Xbelow) which are used in combination to represent generic forms of 
  1645. Xaddresses.  In 
  1646. Xaddition to literal address tokens, the pattern to be matched in a rewriting 
  1647. Xrule (often referred to as the LHS) may
  1648. Xinclude field identifiers which match one of five possibilities:
  1649. X.DS
  1650. X    - zero or more tokens
  1651. X    - one or more tokens
  1652. X    - one token exactly
  1653. X    - one token which is an element of an arbitrary class \fBX\fR
  1654. X    - one token which is not an element of an arbitrary class \fBX\fR
  1655. X.DE
  1656. XA particular field type may be assigned to one or more identifiers.  Each
  1657. Xfield identifier is associated with (or defined to be) a field type in
  1658. Xa \fIfield\fR declarations block.  A \fIfield\fR declarations block consists
  1659. Xof the keyword \fIfield\fR, followed by zero or more field definitions of
  1660. Xthe form:
  1661. X.TS
  1662. Xcenter box;
  1663. Xl .
  1664. X\fBfield-id-list\fR : \fBfield-type\fR ;
  1665. X.TE
  1666. XA \fBfield-id-list\fR is a list of one or more identifiers, each separated by
  1667. Xa comma.  A \fBfield-type\fR, on the other hand, is a representation of 
  1668. Xone of the five fields 
  1669. Xdescribed above.  The syntax for each of the five forms follows:
  1670. X.DS
  1671. X    \fImatch\fR ( 0* )
  1672. X    \fImatch\fR ( 1* )
  1673. X    \fImatch\fR ( 1 )
  1674. X    \fImatch\fR ( 1 ) \fIin\fR <class-X>
  1675. X    \fImatch\fR ( 0 ) \fIin\fR <class-X>
  1676. X.DE
  1677. XThe star in the first two forms means: "or more".  Thus, the first
  1678. Xform would read: "match zero or more tokens".  The fourth form describes
  1679. Xa field where one token is matched from an arbitrary class (class-X), whereas
  1680. Xthe fifth form describes a field where one token is matched if it is not of the
  1681. Xgiven class (class-X).
  1682. X.sp 1
  1683. XIn addition, the Sun release 3.0 version of \fIsendmail\fR supports several
  1684. Xnew pattern matching operations represented by the following forms:
  1685. X.DS
  1686. X    \fImatch\fR ( 0 ) \fImap\fR <macro-identifier-X>
  1687. X    \fImatch\fR ( 1 ) \fImap\fR <macro-identifier-X>
  1688. X    \fImatch host\fR
  1689. X.DE
  1690. XThe macro \*Qmacro-identifier-X\*U should be assigned the name of the
  1691. Xrelevant YP map.
  1692. X.sp 1
  1693. XThe following example,
  1694. X.sp 1
  1695. X.DS
  1696. X\fIfield\fR
  1697. X    anypath        : \fImatch\fR ( 0* );
  1698. X    recipient_host    : \fImatch\fR ( 1 );
  1699. X    local_site        : \fImatch\fR ( 1 ) \fIin m_sitename\fR;
  1700. X    remote_site        : \fImatch\fR ( 0 ) \fIin m_sitename\fR;
  1701. X.DE
  1702. Xdefines the fields anypath, recipient_host, local_site, and remote_site.
  1703. X.NH 2
  1704. XAddress rewriting rules
  1705. X.PP
  1706. XAddress rewriting rules are grouped according to the function they perform.  For
  1707. Xexample, it is desirable to form a distinct group for those rewriting rules 
  1708. Xwhich perform transformations on recipient addresses.
  1709. X.PP
  1710. XSets of rewriting rules are defined in \fIruleset\fR blocks.  A \fIruleset\fR
  1711. Xblock consists of the keyword \fIruleset\fR, followed by zero or more
  1712. Xruleset definitions of the form:
  1713. X.TS
  1714. Xcenter box;
  1715. Xl .
  1716. X<ruleset-id> { <rewriting-rule1> <rewriting-rule2> ... }
  1717. X.TE
  1718. XThe ruleset identifier, ruleset-id, must be defined in a \fIbind\fR block, as
  1719. Xdescribed earlier.  The rewriting rules have the form:
  1720. X.DS
  1721. X    \fIif\fR ( <match-pattern> )
  1722. X        <match-action> ( <rewriting-pattern> ) ;
  1723. X.DE
  1724. Xwhere match-pattern, rewriting-pattern, and match-action are described below.
  1725. XAn alternative form is available:
  1726. X.DS
  1727. X    \fIwhile\fR ( <match-pattern> )
  1728. X        <match-action> ( <rewriting-pattern> ) ;
  1729. X.DE
  1730. Xwhich is somewhat more useful when the \*Qmatch-action\*U is \fIretry\fP
  1731. X(see below).
  1732. X.NH 3
  1733. XMatch-patterns
  1734. X.PP
  1735. XA match-pattern is a sequence of Ease address elements representing an
  1736. Xaddress format.  If the address being rewritten matches the pattern
  1737. X\*Qmatch-pattern\*U,
  1738. Xthen the address is reformatted using the pattern \*Qrewriting-pattern\*U, and 
  1739. Xthe corresponding
  1740. Xaction (\*Qmatch-action\*U) is performed.  The five distinct Ease address
  1741. Xelements which may constitute a match-pattern are as follows:
  1742. X.TS
  1743. Xcenter ;
  1744. Xl .
  1745. X1. Field Identifiers (refer to previous section)
  1746. XT{
  1747. X2. Non-alphanumeric characters (the exception is the case for literal 
  1748. Xdouble quotes, which must be preceded by a backslash (\\\\\)
  1749. XT}
  1750. X3. Macro references
  1751. X4. Quoted strings ("...")
  1752. X5. \fBConditional-expressions\fR (discussed later)
  1753. X.TE
  1754. XBelow are two sample match-patterns, each describing the same address format:
  1755. X.DS
  1756. X    user-id @ hostname . $arpa_suffix
  1757. X    user-id @ hostname ".ARPA"
  1758. X.DE
  1759. Xwhere user-id and hostname are field identifiers, and arpa_suffix is a 
  1760. Xuser-defined macro with the value \*QARPA\*U.
  1761. X.NH 3
  1762. XRewriting-patterns
  1763. X.PP
  1764. XA rewriting-pattern specifies the form in which to rewrite a matched 
  1765. Xaddress.  The seven distinct elements which may be used to form 
  1766. Xa rewriting-pattern are as follows:
  1767. X.TS
  1768. Xcenter ;
  1769. Xl .
  1770. X
  1771. XT{
  1772. X1. Non-alphanumeric characters (the exception is the case for literal
  1773. Xdouble quotes, left parentheses, or right parentheses, each of which 
  1774. Xmust be preceded by a backslash (\\\\\). 
  1775. XT}
  1776. X
  1777. XT{
  1778. X2. A call to another ruleset.  This is used to perform rewrites
  1779. Xon a suffix of the rewriting-pattern.  The proper use of this
  1780. Xfeature will be demonstrated by example below. 
  1781. XT}
  1782. X
  1783. X3. Quoted strings ("...").
  1784. X
  1785. X4. \fBConditional-expressions\fR (discussed later).
  1786. X
  1787. X5. A macro reference.
  1788. X
  1789. XT{
  1790. X6. A positional reference in the matched address.  A positional 
  1791. Xreference takes the form: $<integer-position>.  For example, 
  1792. X$3 references the value of the third \fBEase\fR address 
  1793. Xelement in the matched address.
  1794. XT}
  1795. X
  1796. XT{
  1797. X7. Canonicalized host names of the form \fIcanon\fR (<id-token-list>),
  1798. Xwhere \*Qid-token-list\*U is a list of one or more \*Qid-tokens.\*U
  1799. XAn \*Qid-token\*U is a regular identifier, a quoted identifier (with
  1800. Xdouble quotes), a macro reference yielding an identifier,
  1801. Xa numeric internet specification (see below),
  1802. Xa literal character (such as a \*Q.\*U or a \*Q[\*U), or a 
  1803. Xpositional reference in the matched address.  The canonicalization of 
  1804. Xa host name is simply a mapping to its canonical (or official) form.
  1805. XT}
  1806. X
  1807. X.TE
  1808. XBelow are two sample rewriting-patterns:
  1809. X.DS
  1810. X    $1 % $2 < @ $3 ".ARPA" >
  1811. X    OLDSTYLE_RW ( $1 )
  1812. X.DE
  1813. XThe first form specifies an address such as a%b<@c.ARPA>, where a, b, and c
  1814. Xrepresent matched identifiers or paths.  The second form specifies a call to
  1815. Xthe ruleset \*QOLDSTYLE_RW\*U, for old-style rewriting on the parameter 
  1816. X$1, which probably references the entire matched address.  This will become 
  1817. Xclear in later examples.
  1818. X.NH 3
  1819. XMatch-actions
  1820. X.PP
  1821. XWhen a ruleset is called, the address to be rewritten is compared (or matched)
  1822. Xsequentially against the match-address of each rewriting rule.  When a
  1823. Xmatch-address describes the address \fIsendmail\fR is attempting to rewrite, the
  1824. Xaddress is rewritten (or reformatted) using the rule's 
  1825. Xrewriting-pattern.  Following this rewrite, the corresponding match-action
  1826. Xis performed.  There are four match-actions:
  1827. X.TS
  1828. Xcenter ;
  1829. Xl l .
  1830. X\fIretry\fR    T{
  1831. X-a standard action which causes the rewritten address
  1832. Xto be again compared to the match-address of the current rule. 
  1833. XT}
  1834. X
  1835. X\fInext\fR    T{
  1836. X-an action which causes the rewritten address to be
  1837. Xcompared to the match-address of the next rewriting rule of the current 
  1838. Xruleset.  If the end of the list is reached, the ruleset returns the 
  1839. Xrewritten address.
  1840. XT}
  1841. X
  1842. X\fIreturn\fR    T{
  1843. X-an action which causes an immediate return of the 
  1844. Xruleset with the current rewritten address.
  1845. XT}
  1846. X
  1847. X\fIresolve\fR    T{
  1848. X-an action which specifies that the address has been
  1849. Xcompletely resolved (i.e., no further rewriting is necessary).  The 
  1850. X\fIresolve\fR action is described in more detail below. 
  1851. XT}
  1852. X.TE
  1853. X.PP
  1854. XThe match-action, \fIresolve\fR, is special in that it terminates
  1855. Xthe address rewriting altogether.  The semantic structure of \fIsendmail\fR's
  1856. Xrewriting scheme requires that a \fIresolve\fR action appear only in the 
  1857. Xruleset whose numerical binding is to the number zero.  The \fIresolve\fR action
  1858. Xmust specify three parameters: \fImailer\fR, \fIhost\fR, and \fIuser\fR.  If
  1859. Xthe \fImailer\fR is local, the \fIhost\fR parameter may be omitted.  The
  1860. X\fImailer\fR argument must be specified as a single word, macro, or positional
  1861. Xreference in the matched address.  The \fIhost\fR argument may be specified as 
  1862. Xa single word or as an expression which expands to a single word (e.g.,
  1863. X\fIhost\fR ($1 ".ARPA")).  In addition, the \fIhost\fR argument may be a
  1864. Xcanonicalization (as described above) or a numeric internet specification.  The
  1865. Xkeyword \fIhostnum\fR is used for numeric internet specifications, as in 
  1866. X\fIhostnum\fR ("128.61.1.1") or \fIhostnum\fR ( $2 ).  The \fIuser\fR 
  1867. Xspecification is a rewriting-pattern, as described above.  
  1868. X.PP
  1869. XIn general, the format of a \fIresolve\fR action will be as follows:
  1870. X.DS
  1871. X    \fIresolve\fR (    \fImailer\fR ( <mailer-name> ),
  1872. X            \fIhost\fR   ( <host-name> ),
  1873. X            \fIuser\fR   ( <user-address>)   );
  1874. X.DE
  1875. XExamples of the match-action statement are shown below:
  1876. X.DS
  1877. X\fIfield\fR
  1878. X    anypath    : \fImatch\fR (0*);
  1879. X    usr, path    : \fImatch\fR (1*);
  1880. X    hostname    : \fImatch\fR (1);
  1881. X    phone_host    : \fImatch\fR (1) \fIin\fR phonehosts;
  1882. X.DE
  1883. X.DS
  1884. X\fIruleset\fR
  1885. X
  1886. X    EXAMPLE_RW {
  1887. X    
  1888. X        \fIif\fR ( anypath < path > anypath )   /* basic RFC821/822 parse */
  1889. X            \fIretry\fR ( $2 );
  1890. X        \fIif\fR ( usr " at " path )        /* \*Qat\*U -> \*Q@\*U */
  1891. X            \fInext\fR ( $1 @ $2 );
  1892. X        \fIif\fR ( @path: usr )
  1893. X            \fIreturn\fR ( LOCAL_RW ( < @$1 > : $2 ) );
  1894. X        \fIif\fR ( anypath < @phone_host".ARPA" > anypath )
  1895. X            \fIresolve\fR (    \fImailer\fR ( tcp ),
  1896. X                    \fIhost\fR ( relay.cs.net ),
  1897. X                    \fIuser\fR ( $1 % $2 < @"relay.cs.net" > $3 ) );
  1898. X    }
  1899. X.DE
  1900. X.PP
  1901. XThe example above defines the ruleset \*QEXAMPLE_RW\*U, which contains four
  1902. Xrewriting rules.  The first rewriting rule discards all tokens of an address
  1903. Xwhich lie on either side of a pair of angle brackets (<>), thereby 
  1904. Xrewriting the address as
  1905. Xthe sequence of tokens contained within the angle brackets ($2).  Following the
  1906. Xaddress rewrite, the rule is applied again (\fIretry\fR).  When the first rule
  1907. Xfails to match the address being rewritten, the second rule is applied.  
  1908. X.PP
  1909. XThe second 
  1910. Xrule simply replaces the word \*Qat\*U by the symbol \*Q@\*U.  The \*Q\fInext\fR\*U
  1911. Xaction specifies that if a match is made, a rewrite is performed and 
  1912. Xmatching continues at the next (or following) rule.  
  1913. X.PP
  1914. XThe third rule illustrates
  1915. Xthe use of the \*Q\fIreturn\fR\*U action, which is executed if the 
  1916. Xpattern \*Q@path: usr\*U
  1917. Xdescribes the current format of the address being rewritten.  In this example,
  1918. Xthe \fIreturn\fR action returns the result of a call to ruleset \*QLOCAL_RW\*U,
  1919. Xwhich rewrites the address \*Q<@$1>:$2\*U, where $1 and $2 are substituted
  1920. Xwith the token(s) matched respectively by \*Qpath\*U and \*Qusr\*U.
  1921. X.PP
  1922. XThe fourth (and final) rule signals a resolution (and termination) of the
  1923. Xrewriting process if the given pattern is matched.  The resolution specifies
  1924. Xthat the mailer \*Qtcp\*U will be used to deliver the message to the host
  1925. X\*Qrelay.cs.net\*U.
  1926. XThe \fIuser\fR parameter specifies the final form of the address
  1927. Xwhich \fIsendmail\fR has just resolved.
  1928. X.sp 2
  1929. X.PP
  1930. XThe \fBEase\fR construct which remains to be examined is the 
  1931. X\fBconditional-expression\fR.  The \fBconditional-expression\fR provides a 
  1932. Xmethod for
  1933. Xconstructing strings based on the condition that some test macro is (or is not)
  1934. Xset.  The general form begins with the concatenation of a string and a
  1935. X\fBstring-conditional\fR:
  1936. X.DS
  1937. X    \fIconcat\fR ( <quoted-string>, \fBstring-conditional\fR )
  1938. X    \fIconcat\fR ( \fBstring-conditional\fR, <quoted-string> )
  1939. X.DE
  1940. XA \fBstring-conditional\fR assumes either of the following forms:
  1941. X.DS
  1942. X    \fIifset\fR ( <macro-name>, <ifset-string> )
  1943. X    \fIifset\fR ( <macro-name>, <ifset-string>, <notset-string> )
  1944. X.DE
  1945. XA \fBstring-conditional\fR of the first form evaluates to \*Qifset-string\*U 
  1946. Xif the macro \*Qmacro-name\*U has been assigned a value; otherwise it
  1947. Xevaluates to the null string.  The second form behaves similarly, except
  1948. Xthat the \fBstring-conditional\fR evaluates to \*Qnotset-string\*U, instead
  1949. Xof the null string, if the macro \*Qmacro-name\*U has no value.
  1950. X.sp 1
  1951. XThe following \fBconditional-expression\fR,
  1952. X.DS
  1953. X    \fIconcat\fR ( "New ", \fIifset\fR ( city, "York", "Jersey" ) )
  1954. X.DE
  1955. Xevaluates to the string "New York", if the macro \*Qcity\*U is set.  Otherwise,
  1956. Xthe \fBconditional-expression\fR evaluates to the string "New Jersey".
  1957. X.NH
  1958. XEase Translation
  1959. X.PP
  1960. XIt is important to note that \fBEase\fR is translated by a stand-alone
  1961. Xtranslator to the raw configuration file format.  No modifications were
  1962. Xmade to the \fIsendmail\fR program itself.  As a result, syntactical verification
  1963. Xof a configuration file can be performed without invoking \fIsendmail\fR.
  1964. X.PP
  1965. XThe \fBEase\fR language is translated by invoking 
  1966. Xthe C language preprocessor (cpp) with \fBEase\fR source as input, then piping
  1967. Xthe output as input to the \fBEase\fR translator (\fIet\fR).  The \fBEase\fR
  1968. Xtranslator may be invoked on the command line in one of four ways:
  1969. X.TS
  1970. Xcenter box ;
  1971. Xl l .
  1972. X\fIet\fR  <input-file>  <output-file>    [read from a file, write to a file]
  1973. X\fIet\fR  <input-file>    [read from a file, write to standard output]
  1974. X\fIet\fR  -  <output-file>    [read from standard input, write to a file]
  1975. X\fIet\fR    [read from standard input, write to standard output]
  1976. X.TE
  1977. X.NH
  1978. XConclusion
  1979. X.PP
  1980. X\fBEase\fR is currently in use at the Purdue University Computing 
  1981. XCenter.  Source code for the \fBEase\fR translator (\fIet\fR) may be
  1982. Xobtained on request by writing to:
  1983. X.DS
  1984. XU.S. Mail:
  1985. X        James S. Schoner
  1986. X        c/o Kevin S. Braunsdorf
  1987. X        Purdue University Computing Center
  1988. X        Purdue University
  1989. X        West Lafayette, Indiana  47907
  1990. X
  1991. XElectronic Mail:
  1992. X        ksb@j.cc.purdue.edu
  1993. X.DE
  1994. X.PP
  1995. XMuch of the success of this project is attributable to the constant support 
  1996. Xand insight offered by Mark Shoemaker.  To him, I owe a debt of gratitude.  In 
  1997. Xaddition, I would like to thank Kevin Smallwood, Paul Albitz, and Rich Kulawiec
  1998. Xfor their many notable suggestions and valuable insight.
  1999. X.NH
  2000. XAcknowledgements
  2001. X.PP
  2002. XArnold Robbins would like to acknowledge contributions from
  2003. XStephen Schaefer of Bowling Green State University,
  2004. XJeff Stearns of John Fluke Manufacturing Company,
  2005. XRaymond A. Schnitzler of Bellcore,
  2006. Xand
  2007. XAndrew Partan of the Corporation for Open Systems.
  2008. XThe good intentions of Rich Salz, of Bolt Beranak, and Newman,
  2009. Xare also acknowledged.
  2010. X.PP
  2011. XThe most up to date version of \fBEase\fR should be gotten from the
  2012. Xnearest USENET \fBcomp.sources.unix\fR archive site.
  2013. END_OF_FILE
  2014. if test 31245 -ne `wc -c <'doc/ease.paper'`; then
  2015.     echo shar: \"'doc/ease.paper'\" unpacked with wrong size!
  2016. fi
  2017. chmod +x 'doc/ease.paper'
  2018. # end of 'doc/ease.paper'
  2019. fi
  2020. echo shar: End of archive 3 \(of 3\).
  2021. cp /dev/null ark3isdone
  2022. MISSING=""
  2023. for I in 1 2 3 ; do
  2024.     if test ! -f ark${I}isdone ; then
  2025.     MISSING="${MISSING} ${I}"
  2026.     fi
  2027. done
  2028. if test "${MISSING}" = "" ; then
  2029.     echo You have unpacked all 3 archives.
  2030.     rm -f ark[1-9]isdone
  2031. else
  2032.     echo You still need to unpack the following archives:
  2033.     echo "        " ${MISSING}
  2034. fi
  2035. ##  End of shell archive.
  2036. exit 0
  2037.