sources-request@mirror.UUCP (02/11/87)
Submitted by: ksb@j.cc.purdue.edu Mod.sources: Volume 8, Issue 53 Archive-name: ease/Part01 The Ease language is a high-level specification format for sendmail's configuration file. The motivation for its development was to fulfill a goal of providing a readable and easily modifiable sendmail configuration file format. Ease fulfills this goal by shielding the programmer from the cryptic configuration specification required by sendmail and providing a high-level language with which the programmer may specify all modifications to a configuration file. The development of Ease coincided with the development of an Ease translator, et, which translates a configuration file written in Ease to an equivalent file of the standard format accepted by sendmail. Please let Kevin Braunsdorf, "ksb@j.cc.purdue.edu" know if you make any modifications to Ease; he's taking care of this now (the original author has since left Purdue) and we'd really like to keep the released version exactly in step with our local version in order to make supporting this a lot easier. Thanks, "Wombat" <rsk@j.cc.purdue.edu> # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # doc # src mkdir doc chdir doc cat << \SHAR_EOF > ap1 .DS C \s+5\fBAppendix A\fR \fBPre-Declared Macros\fR\s-5 .DE .sp 5 .TS center box; c|c|c l|l|l. \fBEase\fR Macro Raw Equivalent Meaning* = \fIm_odate\fR a The origination date in Arpanet format \fIm_adate\fR b The current date in Arpanet format \fIm_hops\fR c The hop count \fIm_udate\fR d The date in UNIX (ctime) format \fIm_smtp\fR e The SMTP entry message \fIm_saddr\fR f The sender (from) address \fIm_sreladdr\fR g The sender address relative to the recipient \fIm_rhost\fR h The recipient host \fIm_qid\fR i The queue id \fIm_oname\fR j The official domain name for this site \fIm_ufrom\fR l The format of the UNIX from line \fIm_daemon\fR n The name of the daemon (for error messages) \fIm_addrops\fR o The set of "operators" in addresses \fIm_pid\fR p Sendmail's pid \fIm_defaddr\fR q The default format of sender address \fIm_protocol\fR r Protocol used \fIm_shostname\fR s Sender's host name \fIm_ctime\fR t A numeric representation of the current time \fIm_ruser\fR u The recipient user \fIm_version\fR v The version number of sendmail \fIm_sitename\fR w The hostname of this site \fIm_sname\fR x The full name of the sender \fIm_stty\fR y The id of the sender's tty \fIm_rhdir\fR z The home directory of the recipient .TE .FS * Taken from pages 15 and 16 of the Installation and Operation Guide for Sendmail (UNIX Programmer's Manual, Volume 2c), by Eric Allman. .FE SHAR_EOF if test 1384 -ne "`wc -c ap1`" then echo shar: error transmitting ap1 '(should have been 1384 characters)' fi cat << \SHAR_EOF > ap2 .DS C \s+5\fBAppendix B\fR \fBSendmail Options\fR\s-5 .DE .sp 2 .PP For a complete description of Sendmail's options and their values, refer to Appendix B of the Installation and Operation Guide for Sendmail (UNIX Programmer's Manual, Volume 2c), by Eric Allman. .sp 2 .TS center box; c|c l|l. Sendmail Option (\fBEase\fR) Sendmail Option (Raw) Special Values = \fIo_alias\fR A \fIo_ewait\fR a \fIo_bsub\fR B \fIo_qwait\fR c \fIo_delivery\fR (special values below) d (special values below) \fId_interactive\fR i \fId_background\fR b \fId_queue\fR q \fIo_rebuild\fR D \fIo_handling\fR (special values below) e (special values below) \fIh_print\fR p \fIh_exit\fR q \fIh_mail\fR m \fIh_write\fR w \fIh_mailz\fR e \fIo_tmode\fR F \fIo_usave\fR f \fIo_gid\fR g \fIo_fsmtp\fR H \fIo_skipd\fR i \fIo_slog\fR L \fIo_rsend\fR m \fIo_dnet\fR N \fIo_hformat\fR o \fIo_qdir\fR Q \fIo_tread\fR r \fIo_flog\fR S \fIo_safe\fR s \fIo_qtimeout\fR T \fIo_timezone\fR t \fIo_dmuid\fR u \fIo_verbose\fR v \fIo_wizpass\fR W \fIo_loadq\fR x \fIo_loadnc\fR X .TE SHAR_EOF if test 1153 -ne "`wc -c ap2`" then echo shar: error transmitting ap2 '(should have been 1153 characters)' fi cat << \SHAR_EOF > ap3 .DS C \s+5\fBAppendix C\fR \fBMailer Flags\fR\s-5 .DE .sp 5 .PP For a complete description of mailer flags, refer to Appendix C of the Installation and Operation Guide for Sendmail (UNIX Programmer's Manual, Volume 2c), by Eric Allman. .sp 5 .TS center box; c|c l|l. Mailer Flag (\fBEase\fR) Mailer Flag (Raw) = \fIf_ffrom\fR f \fIf_rfrom\fR r \fIf_noreset\fR S \fIf_noufrom\fR n \fIf_locm\fR l \fIf_strip\fR s \fIf_mult\fR m \fIf_from\fR F \fIf_date\fR D \fIf_mesg\fR M \fIf_full\fR x \fIf_return\fR P \fIf_upperu\fR u \fIf_upperh\fR h \fIf_arpa\fR A \fIf_ufrom\fR U \fIf_expensive\fR e \fIf_dot\fR X \fIf_llimit\fR L \fIf_retsmtp\fR p \fIf_smtp\fR I \fIf_addrw\fR C .TE SHAR_EOF if test 689 -ne "`wc -c ap3`" then echo shar: error transmitting ap3 '(should have been 689 characters)' fi cat << \SHAR_EOF > cover .DA .sp 15 .nr PS 14 .ps 14 .DS C .DE .sp 10 .nr PS 36 .ps 36 .DS C \fBEase:\fR .DE .sp 5 .nr PS 22 .ps 22 .DS C A Configuration Language for Sendmail .DE .sp 13 .nr PS 14 .ps 14 .DS C by \fIJames S. Schoner\fR \fIPurdue University Computing Center\fR .DE SHAR_EOF if test 262 -ne "`wc -c cover`" then echo shar: error transmitting cover '(should have been 262 characters)' fi cat << \SHAR_EOF > ease.paper .LP .TL Ease: A Configuration Language for Sendmail .AU James S. Schoner .AI Purdue University Computing Center West Lafayette, Indiana 47907 .sp 2 .I .ce ABSTRACT .R .PP The rapid expansion of computer networks and ensuing variation among mailing address formats have made address interpretation an increasingly complex task. In the UNIX* 4.2BSD operating system, a program named \fIsendmail\fR was introduced which provided a general internetwork mail routing facility. This facility has significantly diminished the complexity of handling address interpretation. .PP \fISendmail\fR's address interpretation is based on a rewriting system composed of a number of rewriting rules (or productions) arranged as part of a configuration file. Unfortunately, the syntactical format of a configuration file for \fIsendmail\fR is both terse and rigid, making it rather difficult to modify. The standard format certainly serves its purpose, but, as the need to change these configurations increases in frequency, a more readable format (i.e., one that is similar to the format of modern programming languages) is required to permit reasonably quick modifications to the configuration. As a solution to this problem, \fBEase\fR provides a level of abstraction which eliminates most of the current syntactic hindrances faced by programmers who must reconfigure \fIsendmail\fR's address parsing scheme. .PP As a high-level specification format, \fBEase\fR is proving to be an excellent alternative to \fIsendmail\fR's cryptic configuration file syntax. The syntactic structures of \fBEase\fR are patterned after modern language constructs, making the language easy to learn and easy to remember. The format of the address rewriting rule is perhaps the most significant syntactical improvement. It was undoubtedly the most needed improvement. Nevertheless, every element of a configuration file is structurally enhanced through the use of \fBEase\fR. .FS * UNIX is a trademark of AT&T Bell Laboratories. .FE .sp 2 .NH Introduction .PP The \fBEase\fR language is a high-level specification format for \fIsendmail\fR's configuration file. The motivation for its development was to fulfill a goal of providing a readable and easily modifiable \fIsendmail\fR configuration file format. \fBEase\fR fulfills this goal by shielding the programmer from the cryptic configuration specification required by \fIsendmail\fR and providing a high-level language with which the programmer may specify all modifications to a configuration file. The development of Ease coincided with the development of an \fBEase\fR translator, \fIet\fR, which translates a configuration file written in \fBEase\fR to an equivalent file of the standard format accepted by \fIsendmail\fR. .NH Ease in Profile .PP As will be seen in the next section, the syntax of \fBEase\fR is quite readable and easy to learn. In order to acquire a relevant perspective on this issue, the reader is advised to examine a raw configuration file for \fIsendmail\fR (the output of the \fBEase\fR translator, \fIet\fR, will do nicely). The raw syntax, while quite fitting for quick translation, can prove to be a programmer's nightmare. .PP Undoubtedly, one of the more prominent features of \fBEase\fR is the ability to attach names to address fields. When address field names are well-chosen, a distinct, self-documenting quality becomes a visible part of the address rewriting rules. Ostensibly, address field names provide a new level of semantic abstraction. A brief comparison of the formats can be accomplished by examining the following equivalent representations of an address pattern: .DS user_path@host_name (\fBEase\fR format) $+@$- (raw format) .DE In the above, \*Quser_path\*U represents a field of one or more address tokens, and \*Qhost_name\*U represents one address token exactly. These token fields are represented by \*Q$+\*U and \*Q$-\*U in the raw format. Clearly, the \fBEase\fR format is preferable, not only for increased readability, but structural comprehension as well. .PP Other features of \fBEase\fR include ruleset naming, long identifiers for macros and classes, flow-of-control structures, and free formatting. In addition, the C language preprocessor (cpp) can be used for file inclusion and conditionally defined code constructs. The next section describes the \fBEase\fR language in complete detail. .NH Ease Syntax* .FS * \fINo attempt is made to describe the complete semantic meaning associated with all of the constructs of a sendmail configuration file. Items not covered in this document include the semantic distinction among rulesets, the special uses of pre-defined macros, and the method of building configuration files. To obtain this information, the reader is advised to refer to the Installation and Operation Guide for Sendmail (UNIX Programmer's Manual, Volume 2c), by Eric Allman.\fR .FE .PP At its highest level, \fBEase\fR can be viewed as a collection of block-structures, where each block begins with a keyword and is followed by zero or more related definitions and/or declarations. There are ten distinct block types. The following is a list containing all ten block keywords and the block type it denotes. .TS center; l l . \fIbind\fR -ruleset identifier bindings \fImacro\fR -macro definitions \fIclass\fR -class definitions \fIoptions\fR -\fIsendmail\fR option definitions \fIprecedence\fR -precedence definitions \fItrusted\fR -trusted users \fIheader\fR -mail header definitions \fImailer\fR -mailer definitions \fIfield\fR -address field definitions \fIruleset\fR -address rewriting rules .TE .sp 1 In general, .TS center ; l . * Letters are distinguished by case, T{ * An \fBEase\fR identifier is defined to be a letter, followed by zero or more letters, digits, underscores (_), or dashes (-), T} T{ * A literal newline or double quotation (") character may be included in any quoted string by preceding the character with a backslash (\\\\\), and T} T{ * \fBEase\fR source is preprocessed by the C language preprocessor (cpp), thus source comments (i.e., text enclosed by \*Q/*\*U and \*Q*/\*U) may appear anywhere as part of \fBEase\fR whitespace. T} .TE .PP For notational convenience, this document specifies all reserved words of the \fBEase\fR language in italics. In addition, quantities enclosed in angle brackets (<..>) represent arbitrary identifiers, strings, or numbers. .NH 2 Ruleset Identifier Bindings .PP A ruleset (a set of rewriting rules) is identified solely by an integer in \fIsendmail\fR's configuration file. \fBEase\fR, however, allows each ruleset to be named with a meaningful identifier. Since a special numeric association for each ruleset is required by the address parsing scheme of \fIsendmail\fR, a \fIbind\fR block must be present in any \fBEase\fR file which defines one or more rulesets. A \fIbind\fR block consists of the keyword \fIbind\fR, followed by zero or more statements of the form: .TS center box; l . <ruleset-id> = \fIruleset\fR <ruleset-number> ; .TE The following example, .sp 1 \fIbind\fR .PP FINAL_RW = \fIruleset\fR 4; .sp 1 specifies that FINAL_RW, the final rewriting ruleset, is \fIsendmail\fR's ruleset number 4. .NH 2 Macro Definitions .PP A macro is an identifier which, when referenced in the text of a program, is replaced by its value, a string of zero or more characters. The value of a macro may include references to other macros, but not itself! \fISendmail\fR allows a maximum of 26 user-declared macros in its configuration file. In addition, there are a number of pre-declared macros which have special meaning to \fIsendmail\fR (see Appendix A). \fBEase\fR macros are defined in \fImacro\fR blocks. \fBEase\fR allows any macro to be declared (which is equivalent to simply referencing it) before it is defined. A macro identifier is replaced by its value when it is preceded by the character \*Q$\*U. In addition, a macro reference inside a quoted string must always include braces ({}) around the macro identifier (for delimiting purposes). .PP A \fImacro\fR block consists of the keyword \fImacro\fR, followed by zero or more statements taking either of the following forms: .TS center box; l . <macro-identifier> = "<macro-value>" ; <macro-identifier> = \fBconditional-expression\fR ; .TE The \fBconditional-expression\fR format will be discussed later. .sp 1 The following example, .sp 1 \fImacro\fR .PP first_name = "James"; .PP last_name = "Schoner"; .PP whole_name = "${first_name} ${second_name}"; .sp 1 defines the macros first_name, last_name, and whole_name, where whole_name is the string, "James Schoner". .NH 2 Class definitions .PP A class is denoted by an identifier representing a logical grouping of zero or more names. Classes are used to represent the range of values a token may assume in the pattern matching of an address. Further discussion on the use of classes will be deferred until address fields are described. .PP One identifier may be used to distinctly represent both a macro and class (i.e., the set of macro identifiers and the set of class identifiers may form a non-empty intersection). A name, or class element, may be an identifier or any quoted word. .PP A \fIclass\fR block consists of the keyword \fIclass\fR, followed by zero or more statements taking any of the following forms: .TS center box; l . <class-identifier> = { <name1>, <name2>, <name3>, ... } ; <class-identifier> = \fIreadclass\fR ( "<file-name>" ) ; <class-identifier> = \fIreadclass\fR ( "<file-name>", "<read-format>" ) ; .TE The second and third forms cause \fIsendmail\fR to read the names of the class from the named file. The third form contains a read format, which should be a \fIscanf(3)\fR pattern yielding a single string. .sp 1 The following example, .sp 1 \fIclass\fR .PP campus_hosts = { statistics, engineering, chemistry, physics, physics-2 } ; .PP versions = { "1.0", "1.1", "4.0", "4.2", latest-and-greatest } ; .PP phone_hosts = \fIreadclass\fR ( "/tmp/phonenet.list" ) ; .sp 1 defines the classes campus_hosts, versions, and phone_hosts. .NH 2 Sendmail option definitions .PP A number of options to the \fIsendmail\fR program may be specified in an \fIoptions\fR block. For a description of the various \fIsendmail\fR options and their values, see Appendix B. .PP An \fIoptions\fR block consists of the keyword \fIoptions\fR, followed by zero or more statements taking any of the following forms: .TS center box; l l . <option-identifier> = "<option-value>" ; \fIo_delivery\fR = \fBspecial-value\fR ; \fIo_handling\fR = \fBspecial-value\fR ; .TE All but two options (\fIo_delivery\fR and \fIo_handling\fR) use the first form. To specify an option without a value, simply assign to it the null string (""). The \fBspecial-value\fR field of the second and third form refers to special values (non-quoted) which are specified in Appendix B. .sp 1 The following example, .sp 1 \fIoptions\fR .PP \fIo_alias\fR = "/usr/lib/aliases" ; .PP \fIo_tmode\fR = "0600" ; .PP \fIo_delivery\fR = d_background ; .sp 1 sets the options \fIo_alias\fR, \fIo_tmode\fR, and \fIo_delivery\fR. .NH 2 Precedence definitions .PP Message headers may contain a \*QPrecedence:\*U field describing the precedence of the message class. Identifiers which may appear in the precedence field of a message are given precedence values in a configuration file \fIprecedence\fR definition. This association will be illustrated below in an example. .PP A \fIprecedence\fR block consists of the keyword \fIprecedence\fR, followed by zero or more statements of the form: .KS .TS center box; l . <precedence-identifier> = <precedence-integer> ; .TE .KE The following example, .sp 1 \fIprecedence\fR .PP special-delivery = 100; .PP junk = -100; .sp 1 defines the precedence level for the names \*Qspecial-delivery\*U and \*Qjunk\*U. Thus, whenever the name \*Qjunk\*U appears in a \*QPrecedence:\*U field, the corresponding message class will be set to -100. .NH 2 Trusted users .PP \fISendmail\fR's \fB-f\fR flag allows trusted users to override the sender's machine address. Trusted users are listed in \fItrusted\fR blocks. A \fItrusted\fR block consists of the keyword \fItrusted\fR, followed by zero or more sets of users taking the form: .TS center box; l . { <user1>, <user2>, <user3>, ... } ; .TE The following example, .sp 1 \fItrusted\fR .PP { root, uucp, network } ; .PP { acu, kcs, jss } ; .sp 1 specifies that the users root, uucp, network, acu, kcs, and jss can be trusted to use the \fIsendmail\fR flag, \fB-f\fR. .NH 2 Mail header definitions .PP The format of the message headers inserted by \fIsendmail\fR is defined in one or more \fIheader\fR blocks in the configuration file. A \fIheader\fR block consists of the keyword \fIheader\fR, followed by zero or more statements taking any of the following forms: .TS center box; l l l l l l l l l l l . \fIfor\fR ( <mailer-flag1>, <mailer-flag2>, ... ) \fIdefine\fR ( "<header-title>" , \fBheader-value\fR ) ; \fIfor\fR ( <mailer-flag1>, <mailer-flag2>, ... ) { \fIdefine\fR ( "<header-title>" , \fBheader-value\fR ) ; \fIdefine\fR ( "<header-title>" , \fBheader-value\fR ) ; . . } ; \fIdefine\fR ( "<header-title>" , \fBheader-value\fR ) ; .TE The first form is used to define one header for one or more mailer flags. The second form differs from the first in that more than one header may be defined for a given set of flags. The third form is used to define a header, regardless of mailer flags. Refer to Appendix C for a list of \fBEase\fR identifiers representing mailer flags. The header title is a simple string of characters (no macro references), whereas the \fBheader-value\fR can be either a string of characters (possibly containing macro references) or a \fBconditional-expression\fR (discussed later). .sp 1 The following example, .DS \fIheader\fR \fIdefine\fR ( "Subject:", "") ; \fIfor\fR ( \fIf_return\fR ) \fIdefine\fR ( "Return-Path:", "<${\fIm_sreladdr\fR}>" ) ; \fIfor\fR ( \fIf_date\fR ) { \fIdefine\fR ( "Resent-Date:", "${\fIm_odate\fR}" ) ; \fIdefine\fR ( "Date:", "${\fIm_odate\fR}" ); } ; .DE defines a \*QSubject\*U field for all mailers, regardless of their flags, a \*QReturn-Path\*U field for mailers whose definition specifies the flag, \fIf_return\fR, and the headers, \*QResent-Date\*U and \*QDate\*U, for mailers whose definition specifies the flag, \fIf_date\fR. .NH 2 Mailer Definitions .PP \fISendmail\fR's definition of a mailer (or an interface to one) occurs in a \fImailer\fR block. A \fImailer\fR block consists of the keyword \fImailer\fR, followed by zero or more statements of the form: .TS center box; l . <mailer-identifier> { \fBmailer-spec\fR } ; .TE The field, \fBmailer-spec\fR, is a list of zero or more of the following attribute assignments (where successive assignment statements are separated by commas): .TS center ; l l l l l l l l l l l l l l . \fIPath\fR = \fBstring-attribute\fR \fIArgv\fR = \fBstring-attribute\fR \fIEol\fR = \fBstring-attribute\fR \fIMaxsize\fR = \fBstring-attribute\fR \fIFlags\fR = { <mailer-flag1>, <mailer-flag2>, ... } \fISender\fR = <sender-ruleset-id> \fIRecipient\fR = <recipient-ruleset-id> .TE The \fBstring-attribute\fR value can take the form of a quoted string (possibly containing macro references) or a \fBconditional-expression\fR (discussed later). .sp 1 The following example, .sp 1 \fImailer\fR .DS local { \fIPath\fR = "/bin/mail", \fIFlags\fR = { \fIf_from\fR, \fIf_locm\fR }, \fISender\fR = Sender_RW, \fIRecipient\fR = Recip_RW, \fIArgv\fR = "mail -d ${\fIm_ruser\fR}", \fIMaxsize\fR = "200000" } ; .DE defines a mailer named \*Qlocal\*U. .NH 2 Address field definitions .PP \fISendmail\fR's address parsing scheme treats an address as a group of tokens (an address token is precisely defined in the Arpanet protocol RFC822). In general, \fIsendmail\fR divides an address into tokens based on a list of characters assigned as a string to the special macro \fIm_addrops\fR. These characters will individually be considered as tokens and will separate tokens when parsing is performed. .PP For the \fBEase\fR language, there is a distinct set of address tokens (defined below) which are used in combination to represent generic forms of addresses. In addition to literal address tokens, the pattern to be matched in a rewriting rule (often refered to as the LHS) may include field identifiers which match one of five possibilities: .DS - zero or more tokens - one or more tokens - one token exactly - one token which is an element of an aribitrary class \fBX\fR - one token which is not an element of an aribitrary class \fBX\fR .DE A particular field type may be assigned to one or more identifiers. Each field identifier is associated with (or defined to be) a field type in a \fIfield\fR declarations block. A \fIfield\fR declarations block consists of the keyword \fIfield\fR, followed by zero or more field definitions of the form: .TS center box; l . \fBfield-id-list\fR : \fBfield-type\fR ; .TE A \fBfield-id-list\fR is a list of one or more identifiers, each separated by a comma. A \fBfield-type\fR, on the other hand, is a representation of one of the five fields described above. The syntax for each of the five forms follows: .DS \fImatch\fR ( 0* ) \fImatch\fR ( 1* ) \fImatch\fR ( 1 ) \fImatch\fR ( 1 ) in <class-X> \fImatch\fR ( 0 ) in <class-X> .DE The star in the first two forms means: "or more". Thus, the first form would read: "match zero or more tokens". The fourth form describes a field where one token is matched from an arbitrary class (class-X), whereas the fifth form describes a field where one token is matched if it is not of the given class (class-X). .sp 1 The following example, .sp 1 .DS \fIfield\fR anypath : \fImatch\fR ( 0* ); recipient_host : \fImatch\fR ( 1 ); local_site : \fImatch\fR ( 1 ) in \fIm_sitename\fR; remote_site : \fImatch\fR ( 0 ) in \fIm_sitename\fR; .DE defines the fields anypath, recipient_host, local_site, and remote_site. .NH 2 Address rewriting rules .PP Address rewriting rules are grouped according to the function they perform. For example, it is desirable to form a distinct group for those rewriting rules which perform transformations on recipient addresses. .PP Sets of rewriting rules are defined in \fIruleset\fR blocks. A \fIruleset\fR block consists of the keyword \fIruleset\fR, followed by zero or more ruleset definitions of the form: .TS center box; l . <ruleset-id> { <rewriting-rule1> <rewriting-rule2> ... } .TE The ruleset identifier, ruleset-id, must be defined in a \fIbind\fR block, as described earlier. The rewriting rules have the form: .DS \fIif\fR ( <match-pattern> ) <match-action> ( <rewriting-pattern> ) ; .DE where match-pattern, rewriting-pattern, and match-action are described below. .NH 3 Match-patterns .PP A match-pattern is a sequence of Ease address elements representing an address format. If the address being rewritten matches the pattern \*Qmatch-pattern\*U, then the address is reformatted using the pattern \*Qrewriting-pattern\*U, and the corresponding action (\*Qmatch-action\*U) is performed. The five distinct Ease address elements which may constitute a match-pattern are as follows: .TS center ; l . 1. Field Identifiers (refer to previous section) T{ 2. Non-alphanumeric characters (the exception is the case for literal double quotes, which must be preceded by a backslash (\\\\\) T} 3. Macro references 4. Quoted strings ("...") 5. \fBConditional-expressions\fR (discussed later) .TE Below are two sample match-patterns, each describing the same address format: .DS user-id @ hostname . $arpa_suffix user-id @ hostname ".ARPA" .DE where user-id and hostname are field identifiers, and arpa_suffix is a user-defined macro with the value \*QARPA\*U. .NH 3 Rewriting-patterns .PP A rewriting-pattern specifies the form in which to rewrite a matched address. The seven distinct elements which may be used to form a rewriting-pattern are as follows: .TS center ; l . T{ 1. Non-alphanumeric characters (the exception is the case for literal double quotes, left parentheses, or right parentheses, each of which must be preceded by a backslash (\\\\\). T} T{ 2. A call to another ruleset. This is used to perform rewrites on a suffix of the rewriting-pattern. The proper use of this feature will be demonstrated by example below. T} 3. Quoted strings ("..."). 4. \fBConditional-expressions\fR (discussed later). 5. A macro reference. T{ 6. A positional reference in the matched address. A positional reference takes the form: $<integer-position>. For example, $3 references the value of the third \fBEase\fR address element in the matched address. T} T{ 7. Canonicalized host names of the form \fIcanon\fR (<id-token>), where id-token is a regular identifier, a quoted identifier (with double quotes), a macro reference yielding an identifier, or a positional reference in the matched address. The canonicalization of a host name is simply a mapping to its canonical (or official) form. T} .TE Below are two sample rewriting-patterns: .DS $1 % $2 < @ $3 ".ARPA" > OLDSTYLE_RW ( $1 ) .DE The first form specifies an address such as a%b<@c.ARPA>, where a, b, and c represent matched identifiers or paths. The second form specifies a call to the ruleset \*QOLDSTYLE_RW\*U, for old-style rewriting on the parameter $1, which probably references the entire matched address. This will become clear in later examples. .NH 3 Match-actions .PP When a ruleset is called, the address to be rewritten is compared (or matched) sequentially against the match-address of each rewriting rule. When a match-address describes the address \fIsendmail\fR is attempting to rewrite, the address is rewritten (or reformatted) using the rule's rewriting-pattern. Following this rewrite, the corresponding match-action is performed. There are four match-actions: .TS center ; l l . \fIretry\fR T{ -a standard action which causes the rewritten address to be again compared to the match-address of the current rule. T} \fInext\fR T{ -an action which causes the rewritten address to be compared to the match-address of the next rewriting rule of the current ruleset. If the end of the list is reached, the ruleset returns the rewritten address. T} \fIreturn\fR T{ -an action which causes an immediate return of the ruleset with the current rewritten address. T} \fIresolve\fR T{ -an action which specifies that the address has been completely resolved (i.e., no further rewriting is necessary). The \fIresolve\fR action is described in more detail below. T} .TE .PP The match-action, \fIresolve\fR, is special in that it terminates the address rewriting altogether. The semantic structure of \fIsendmail\fR's rewriting scheme requires that a \fIresolve\fR action appear only in the ruleset whose numerical binding is to the number zero. The \fIresolve\fR action must specify three parameters: \fImailer\fR, \fIhost\fR, and \fIuser\fR. If the \fImailer\fR is local, the \fIhost\fR parameter may be omitted. The \fImailer\fR argument must be specified as a single word, macro, or positional reference in the matched address. The \fIhost\fR argument may be specified as a single word or as an expression which expands to a single word (i.e., \fIhost\fR ($1 ".ARPA")). In addition, the \fIhost\fR argument may be a canonicalization (as described above) or a numeric internet specification. The keyword \fIhostnum\fR is used for numeric internet specifications, as in \fIhostnum\fR ("00.00.00.00") or \fIhostnum\fR ( $2 ). The \fIuser\fR specification is a rewriting-pattern, as described above. .PP In general, the format of a \fIresolve\fR action will be as follows: .DS \fIresolve\fR ( \fImailer\fR ( <mailer-name> ), \fIhost\fR ( <host-name> ), \fIuser\fR ( <user-address>) ); .DE Examples of the match-action statement are shown below: .DS \fIfield\fR anypath : \fImatch\fR (0*); usr, path : \fImatch\fR (1*); hostname : \fImatch\fR (1); phone_host : \fImatch\fR (1) in phonehosts; .DE .DS \fIruleset\fR EXAMPLE_RW { \fIif\fR ( anypath < path > anypath ) /* basic RFC821/822 parse */ \fIretry\fR ( $2 ); \fIif\fR ( usr " at " path ) /* \*Qat\*U -> \*Q@\*U */ \fInext\fR ( $1 @ $2 ); \fIif\fR ( @path: usr ) \fIreturn\fR ( LOCAL_RW ( < @$1 > : $2 ) ); \fIif\fR ( anypath < @phone_host".ARPA" > anypath ) \fIresolve\fR ( \fImailer\fR ( tcp ), \fIhost\fR ( csnet-relay ), \fIuser\fR ( $1 % $2 < @"csnet-relay" > $3 ) ); } .DE .PP The example above defines the ruleset \*QEXAMPLE_RW\*U, which contains four rewriting rules. The first rewriting rule discards all tokens of an address which lie on either side of a pair of angle brackets (<>), thereby rewriting the address as the sequence of tokens contained within the angle brackets ($2). Following the address rewrite, the rule is applied again (\fIretry\fR). When the first rule fails to match the address being rewritten, the second rule is applied. .PP The second rule simply replaces the word \*Qat\*U by the symbol \*Q@\*U. The \*Q\fInext\fR\*U action specifies that if a match is made, a rewrite is performed and matching continues at the next (or following) rule. .PP The third rule illustrates the use of the \*Q\fIreturn\fR\*U action, which is executed if the pattern \*Q@path: usr\*U describes the current format of the address being rewritten. In this example, the \fIreturn\fR action returns the result of a call to ruleset \*QLOCAL_RW\*U, which rewrites the address \*Q<@$1>:$2\*U, where $1 and $2 are substituted with the token(s) matched respectively by \*Qpath\*U and \*Qusr\*U. .PP The fourth (and final) rule signals a resolution (and termination) of the rewriting process if the given pattern is matched. The resolution specifies that the mailer \*Qtcp\*U will be used to deliver the message to the host \*Qcsnet-relay\*U. The \fIuser\fR parameter specifies the final form of the address which \fIsendmail\fR has just resolved. .sp 2 .PP The \fBEase\fR construct which remains to be examined is the \fBconditional-expression\fR. The \fBconditional-expression\fR provides a method for constructing strings based on the condition that some test macro is (or is not) set. The general form begins with the concatenation of a string and a \fBstring-conditional\fR: .DS \fIconcat\fR ( <quoted-string>, \fBstring-conditional\fR ) \fIconcat\fR ( \fBstring-conditional\fR, <quoted-string> ) .DE A \fBstring-conditional\fR assumes either of the following forms: .DS \fIifset\fR ( <macro-name>, <ifset-string> ) \fIifset\fR ( <macro-name>, <ifset-string>, <notset-string> ) .DE A \fBstring-conditional\fR of the first form evaluates to \*Qifset-string\*U if the macro \*Qmacro-name\*U has been assigned a value; otherwise it evaluates to the null string. The second form behaves similarly, except that the \fBstring-conditional\fR evaluates to \*Qnotset-string\*U, instead of the null string, if the macro \*Qmacro-name\*U has no value. .sp 1 The following \fBconditional-expression\fR, .DS \fIconcat\fR ( "New ", \fIifset\fR ( city, "York", "Jersey" ) ) .DE evaluates to the string "New York", if the macro \*Qcity\*U is set. Otherwise, the \fBconditional-expression\fR evaluates to the string "New Jersey". .NH Ease Translation .PP It is important to note that \fBEase\fR is translated by a stand-alone translator to the raw configuration file format. No modifications were made to the \fIsendmail\fR program itself. As a result, syntactical verification of a configuration file can be performed without invoking \fIsendmail\fR. .PP The \fBEase\fR language is translated by invoking the C language preprocessor (cpp) with \fBEase\fR source as input, then piping the output as input to the \fBEase\fR translator (\fIet\fR). The \fBEase\fR translator may be invoked on the command line in one of four ways: .TS center box ; l l . \fIet\fR <input-file> <output-file> [read from a file, write to a file] \fIet\fR <input-file> [read from a file, write to standard output] \fIet\fR - <output-file> [read from standard input, write to a file] \fIet\fR [read from standard input, write to standard output] .TE .NH Conclusion .PP \fBEase\fR is currently in use at the Purdue University Computing Center. Source code for the \fBEase\fR translator (\fIet\fR) may be obtained on request by writing to: .DS U.S. Mail: James S. Schoner c/o Kevin S. Braunsdorf Purdue University Computing Center Purdue University West Lafayette, Indiana 47907 Electronic Mail: ksb@j.cc.purdue.edu .DE .PP Much of the success of this project is attributable to the constant support and insight offered by Mark Shoemaker. To him, I owe a debt of gratitude. In addition, I would like to thank Kevin Smallwood, Paul Albitz, and Rich Kulawiec for their many notable suggestions and valuable insight. SHAR_EOF if test 29063 -ne "`wc -c ease.paper`" then echo shar: error transmitting ease.paper '(should have been 29063 characters)' fi cat << \SHAR_EOF > Makefile # # Makefile for Ease document. # # James S. Schoner # Purdue University Computing Center # all: cover mainbody apen1 apen2 apen3 cover: FRC troff -ms cover mainbody: tbl ease.paper | troff -ms apen1: tbl ap1 | troff -ms apen2: tbl ap2 | troff -ms apen3: tbl ap3 | troff -ms FRC: SHAR_EOF if test 293 -ne "`wc -c Makefile`" then echo shar: error transmitting Makefile '(should have been 293 characters)' fi chdir .. mkdir src chdir src cat << \SHAR_EOF > parser.y %{ /* $Header: /usr/src/local/etc/ease/RCS/parser.y,v 1.3 85/12/10 18:02:11 jss Exp $ */ /* * parser.y -- EASE parser. * * Contains code for yacc(1) which produces a parser (y.tab.c) * for Ease, a specification format for sendmail configuration * files. * * author -- James S. Schoner, Purdue University Computing Center, * West Lafayette, Indiana 47907 * * date -- July 2, 1985 * * Copyright (c) 1985 by Purdue Research Foundation * * All rights reserved. * */ #include <stdio.h> #include "symtab.h" extern void BindID (); extern void EmitDef (); extern char *ListAppend (); extern char *MakeCond (); extern char *MakeRStr (); extern char *ConvOpt (); extern char *ConvFlg (); extern char *MacScan (); extern char *ConvMat (); extern void StartRuleset (); extern char *MakePosTok (); extern char *GetField (); extern char *Bracket (); extern char *MakeRSCall (); extern char *CheckMailer (); extern char *CheckRS (); extern char *MakeField (); extern char MakeMac (); extern void AssignType (); extern void RemoveSymbol (); extern void yyerror (); extern short RMatch; /* ruleset match flag */ char *Cbuf = " "; /* character buffer */ char *Mbuf = "$ "; /* macro buffer */ char *Tsb; /* pointer to temporary string buffer */ char *Flaglist; /* pointer to header flag list */ %} %union { /* value stack element type */ int ival; /* integer token */ char *psb; /* string token */ struct he *phe; /* pointer to hash entry */ enum opts optval; /* sendmail options */ enum flgs flgval; /* mailer flags */ enum mats mpval; /* mailer attribute parameters */ } %start config %token <phe> IDENT %token <psb> SCONST %token <ival> ICONST SEPCHAR %token BIND MACRO CLASS OPTIONS PRECEDENCE TRUSTED HEADER RULESET MAILER %token IF RETRY NEXT RETURN RESOLVE CONCAT IFSET FOR CANON READCLASS %token MPATH MFLAGS MSENDER MRECIPIENT MARGV MEOL MMAXSIZE %token AAOPT AOPT BBOPT COPT DOPT DOPTI DOPTB DOPTQ DDOPT EOPT EOPTP EOPTE %token EOPTM EOPTW EOPTZ FFOPT FOPT GOPT HHOPT IOPT LLOPT MOPT NNOPT OOPT QQOPT %token ROPT SSOPT SOPT TTOPT TOPT UOPT VOPT WWOPT XOPT XXOPT %token FFLAG RFLAG SSFLAG NFLAG LFLAG SFLAG MFLAG FFFLAG DDFLAG MMFLAG XFLAG %token PPFLAG UFLAG HFLAG AAFLAG UUFLAG EFLAG XXFLAG LLFLAG PFLAG IIFLAG CCFLAG %token ASGN COMMA LBRACE RBRACE LPAREN RPAREN SEMI DOLLAR MATCH IN HOSTNUM %token DEFINE FIELD COLON STAR HOST USER %type <psb> mval strval ifcon conval ifres elseres nameset namelist %type <psb> doptid eoptid idlist fcond dlist mflags route mdefs %type <psb> matchaddr matchtok action actionstmt mailerspec mtdef %type <psb> rwaddr rwtok ftype reftok rword cantok resolution %type <psb> userspec hword hostid dheader %type <ival> anychar %type <phe> cdef %type <optval> optid %type <flgval> flagid %type <mpval> mvar %left COMMA %left LPAREN RPAREN %nonassoc SCONST %% config : /* empty */ | config blockdef | error blockdef ; blockdef : BIND bindings | MACRO macdefs | CLASS classdefs | OPTIONS optdefs | PRECEDENCE precdefs | TRUSTED tlist | HEADER hdefs | MAILER mlist | RULESET rdef | FIELD fdefs ; bindings : /* empty */ | bindings IDENT ASGN RULESET ICONST SEMI { BindID ($2, $5, ID_RULESET); } | error SEMI { yyerrok; } ; macdefs : /* empty */ | macdefs IDENT ASGN mval SEMI { EmitDef (def_macro, $2, $4, (char *) NULL); } | error SEMI { yyerrok; } ; mval : strval %prec COMMA { $$ = $1; } | CONCAT LPAREN conval RPAREN { $$ = $3; } ; strval : SCONST { $$ = $1; } | strval SCONST { $$ = ListAppend ($1, $2, (char *) NULL); free ($1); } ; conval : strval COMMA ifcon { $$ = ListAppend ($1, $3, (char *) NULL); free ($1); free ($3); } | ifcon COMMA strval { $$ = ListAppend ($1, $3, (char *) NULL); free ($1); free ($3); } | error { $$ = NULL; } ; ifcon : IFSET LPAREN IDENT COMMA ifres RPAREN { $$ = MakeCond ($3, $5); } ; ifres : mval elseres { if ($2 != NULL) { $$ = ListAppend ($1, $2, "$|"); free ($1); free ($2); } else $$ = $1; } | error { $$ = NULL; } ; elseres : /* empty */ { $$ = NULL; } | COMMA mval { $$ = $2; } ; classdefs : /* empty */ | classdefs IDENT ASGN nameset { EmitDef (def_class, $2, $4, (char *) NULL); } | error ; nameset : LBRACE namelist RBRACE SEMI { $$ = $2; } | LBRACE RBRACE SEMI { $$ = NULL; } | LBRACE error RBRACE SEMI { $$ = NULL; } | READCLASS LPAREN strval RPAREN SEMI { $$ = MakeRStr ($3, (char *) NULL); } | READCLASS LPAREN strval COMMA strval RPAREN SEMI { $$ = MakeRStr ($3, $5); } | READCLASS LPAREN error RPAREN SEMI { $$ = NULL; } | error SEMI { $$ = NULL; yyerrok; } ; namelist : IDENT { $$ = ListAppend ($1->psb, (char *) NULL, (char *) NULL); RemoveSymbol ($1); } | strval { $$ = $1; } | namelist COMMA IDENT { $$ = ListAppend ($1, $3->psb, " "); free ($1); RemoveSymbol ($3); } | namelist COMMA strval { $$ = ListAppend ($1, $3, " "); free ($1); free ($3); } ; optdefs : /* empty */ | optdefs optid ASGN strval SEMI { EmitDef (def_option, (struct he *) NULL, ConvOpt ($2), $4); } | optdefs DOPT ASGN doptid SEMI { EmitDef (def_option, (struct he *) NULL, ConvOpt (opt_d), $4); } | optdefs EOPT ASGN eoptid SEMI { EmitDef (def_option, (struct he *) NULL, ConvOpt (opt_e), $4); } | error SEMI { yyerrok; } ; optid : AAOPT { $$ = opt_A; } | AOPT { $$ = opt_a; } | BBOPT { $$ = opt_B; } | COPT { $$ = opt_c; } | DDOPT { $$ = opt_D; } | FFOPT { $$ = opt_F; } | FOPT { $$ = opt_f; } | GOPT { $$ = opt_g; } | HHOPT { $$ = opt_H; } | IOPT { $$ = opt_i; } | LLOPT { $$ = opt_L; } | MOPT { $$ = opt_m; } | NNOPT { $$ = opt_N; } | OOPT { $$ = opt_o; } | QQOPT { $$ = opt_Q; } | ROPT { $$ = opt_r; } | SSOPT { $$ = opt_S; } | SOPT { $$ = opt_s; } | TTOPT { $$ = opt_T; } | TOPT { $$ = opt_t; } | UOPT { $$ = opt_u; } | VOPT { $$ = opt_v; } | WWOPT { $$ = opt_W; } | XOPT { $$ = opt_x; } | XXOPT { $$ = opt_X; } ; doptid : DOPTI { $$ = ConvOpt (d_opt_i); } | DOPTB { $$ = ConvOpt (d_opt_b); } | DOPTQ { $$ = ConvOpt (d_opt_q); } ; eoptid : EOPTP { $$ = ConvOpt (e_opt_p); } | EOPTE { $$ = ConvOpt (e_opt_e); } | EOPTM { $$ = ConvOpt (e_opt_m); } | EOPTW { $$ = ConvOpt (e_opt_w); } | EOPTZ { $$ = ConvOpt (e_opt_z); } ; precdefs : /* empty */ | precdefs IDENT ASGN ICONST SEMI { BindID ($2, $4, ID_PREC); EmitDef (def_prec, $2, (char *) NULL, (char *) NULL); } ; tlist : /* empty */ | tlist LBRACE IDENT idlist RBRACE SEMI { EmitDef (def_trusted, (struct he *) NULL, ListAppend ($3->psb, $4, " "), (char *) NULL); free ($4); RemoveSymbol ($3); } | tlist LBRACE RBRACE SEMI | error SEMI { yyerrok; } ; hdefs : /* empty */ | hdefs FOR fcond dheader SEMI { EmitDef (def_header, (struct he *) NULL, $3, $4); } | hdefs FOR fcond LBRACE { Flaglist = $3; } dheaders RBRACE SEMI | hdefs DEFINE dlist SEMI { EmitDef (def_header, (struct he *) NULL, (char *) NULL, $3); } | error SEMI { yyerrok; } ; fcond : LPAREN RPAREN { $$ = NULL; } | LPAREN mflags RPAREN { $$ = $2; } | LPAREN error RPAREN { $$ = NULL; } ; mflags : flagid { $$ = ListAppend (ConvFlg ($1), (char *) NULL, (char *) NULL); } | mflags COMMA flagid { $$ = ListAppend ($1, ConvFlg($3), (char *) NULL); free ($1); } ; flagid : FFLAG { $$ = flg_f; } | RFLAG { $$ = flg_r; } | SSFLAG { $$ = flg_S; } | NFLAG { $$ = flg_n; } | LFLAG { $$ = flg_l; } | SFLAG { $$ = flg_s; } | MFLAG { $$ = flg_m; } | FFFLAG { $$ = flg_F; } | DDFLAG { $$ = flg_D; } | MMFLAG { $$ = flg_M; } | XFLAG { $$ = flg_x; } | PPFLAG { $$ = flg_P; } | UFLAG { $$ = flg_u; } | HFLAG { $$ = flg_h; } | AAFLAG { $$ = flg_A; } | UUFLAG { $$ = flg_U; } | EFLAG { $$ = flg_e; } | XXFLAG { $$ = flg_X; } | LLFLAG { $$ = flg_L; } | PFLAG { $$ = flg_p; } | IIFLAG { $$ = flg_I; } | CCFLAG { $$ = flg_C; } ; dheader : /* empty */ { $$ = NULL; } | DEFINE dlist { $$ = $2; } | error { $$ = NULL; } ; dheaders : /* empty */ | dheaders DEFINE dlist SEMI { EmitDef (def_header, (struct he *) NULL, Flaglist, $3); } | error ; dlist : LPAREN strval COMMA mval RPAREN { $$ = ListAppend ($2, MacScan ($4), " "); free ($2); free ($4); } | LPAREN error RPAREN { $$ = NULL; } ; mlist : /* empty */ | mlist IDENT LBRACE mdefs RBRACE SEMI { EmitDef (def_mailer, $2, $4, (char *) NULL); } | mlist IDENT LBRACE RBRACE SEMI { EmitDef (def_mailer, $2, (char *) NULL, (char *) NULL); } | error SEMI { yyerrok; } ; mdefs : mtdef { $$ = $1; } | mdefs COMMA mtdef { $$ = ListAppend ($1, $3, ", "); free ($1); free ($3); } ; mtdef : mvar ASGN mval { $$ = ListAppend (ConvMat ($1), MacScan ($3), "="); free ($3); } | MFLAGS ASGN LBRACE mflags RBRACE { $$ = ListAppend (ConvMat (mat_flags), $4, "="); } | MSENDER ASGN IDENT { $$ = ListAppend (ConvMat (mat_sender), CheckRS ($3), "="); } | MRECIPIENT ASGN IDENT { $$ = ListAppend (ConvMat (mat_recipient), CheckRS ($3), "="); } | error { $$ = NULL; } ; mvar : MPATH { $$ = mat_path; } | MARGV { $$ = mat_argv; } | MEOL { $$ = mat_eol; } | MMAXSIZE { $$ = mat_maxsize; } ; rdef : /* empty */ | rdef IDENT { StartRuleset ($2); } rulelist ; rulelist : LBRACE ruledefs RBRACE { RMatch = FALSE; } | error { RMatch = FALSE; } ; ruledefs : /* empty */ { RMatch = TRUE; } | ruledefs IF LPAREN matchaddr RPAREN actionstmt { EmitDef (def_ruleset, (struct he *) NULL, ListAppend ($4, $6, "\t"), (char *) NULL); free ($4); free ($6); } | error SEMI { yyerrok; } ; matchaddr : /* empty */ { $$ = NULL; } | matchaddr matchtok { $$ = ListAppend ($1, $2, (char *) NULL); free ($1); } | error { $$ = NULL; } ; matchtok : IDENT { $$ = GetField ($1); } | anychar { *Cbuf = $1; $$ = ListAppend (Cbuf, (char *) NULL, (char *) NULL); } | mval { $$ = MacScan ($1); } | DOLLAR IDENT { Mbuf[1] = MakeMac ($2, ID_MACRO); $$ = ListAppend (Mbuf, (char *) NULL, (char *) NULL); } ; actionstmt : action LPAREN rwaddr RPAREN SEMI { $$ = ListAppend ($1, $3, (char *) NULL); free ($3); } | RESOLVE LPAREN resolution RPAREN SEMI { $$ = $3; } | error SEMI { $$ = NULL; yyerrok; } ; action : RETRY { $$ = NULL; } | NEXT { $$ = "$:"; } | RETURN { $$ = "$@"; } ; rwaddr : /* empty */ { $$ = NULL; } | rwaddr rwtok { $$ = ListAppend ($1, $2, (char *) NULL); free ($1); } | rwaddr IDENT LPAREN rwaddr RPAREN { $$ = ListAppend ($1, (Tsb = MakeRSCall ($2, $4)), (char *) NULL); free ($1); free ($4); free (Tsb); } | error { $$ = NULL; } ; rwtok : anychar { *Cbuf = $1; $$ = ListAppend (Cbuf, (char *) NULL, (char *) NULL); } | mval { $$ = MacScan ($1); } | cantok { $$ = $1; } | reftok { $$ = $1; } ; cantok : CANON LPAREN IDENT RPAREN { $$ = Bracket ($3->psb, TRUE); RemoveSymbol ($3); } | CANON LPAREN SCONST RPAREN { $$ = Bracket (MacScan ($3), TRUE); free ($3); } | CANON LPAREN reftok RPAREN { $$ = Bracket ($3, TRUE); free ($3); } ; reftok : DOLLAR IDENT { Mbuf[1] = MakeMac ($2, ID_MACRO); $$ = ListAppend (Mbuf, (char *) NULL, (char *) NULL); } | DOLLAR ICONST { $$ = ListAppend (MakePosTok ($2), (char *) NULL, (char *) NULL); } ; anychar : SEPCHAR { $$ = $1; } | COLON { $$ = ':'; } | STAR { $$ = '*'; } | SEMI { $$ = ';'; } | LBRACE { $$ = '{'; } | RBRACE { $$ = '}'; } | COMMA { $$ = ','; } | ASGN { $$ = '='; } ; resolution : mailerspec COMMA route { $$ = ListAppend ($1, $3, (char *) NULL); free ($1); free ($3); } | error { $$ = NULL; } ; mailerspec : MAILER LPAREN rword RPAREN { $$ = ListAppend ("$#", $3, (char *) NULL); } ; route : HOST LPAREN hword RPAREN COMMA userspec { $$ = ListAppend (Tsb = ListAppend ("$@", $3, (char *) NULL), $6, (char *) NULL); free (Tsb); free ($6); } | userspec { $$ = $1; } ; hword : hostid { $$ = $1; } | HOSTNUM LPAREN reftok RPAREN { $$ = Bracket ($3, FALSE); free ($3); } ; hostid : /* empty */ { $$ = NULL; } | hostid IDENT { $$ = ListAppend ($1, $2->psb, (char *) NULL); RemoveSymbol ($2); free ($1); } | hostid rwtok { $$ = ListAppend ($1, $2, (char *) NULL); free ($1); } ; userspec : USER LPAREN rwaddr RPAREN { $$ = ListAppend ("$:", $3, (char *) NULL); free ($3); } ; rword : IDENT { $$ = CheckMailer ($1); } | reftok { $$ = $1; } ; fdefs : /* empty */ | fdefs IDENT idlist COLON ftype SEMI { AssignType (ListAppend ($2->psb, $3, " "), $5); free ($3); } | error SEMI { yyerrok; } ; idlist : /* empty */ { $$ = NULL; } | idlist COMMA IDENT { $$ = ListAppend ($1, $3->psb, " "); free ($1); } ; ftype : MATCH LPAREN ICONST RPAREN cdef { $$ = ListAppend (MakeField ($3, $5, FALSE), (char *) NULL, (char *) NULL); } | MATCH LPAREN ICONST STAR RPAREN { $$ = ListAppend (MakeField ($3, (struct he *) NULL, TRUE), (char *) NULL, (char *) NULL); } | error { $$ = NULL; } ; cdef : /* empty */ { $$ = NULL; } | IN IDENT { $$ = $2; } ; SHAR_EOF if test 14482 -ne "`wc -c parser.y`" then echo shar: error transmitting parser.y '(should have been 14482 characters)' fi chdir .. # End of shell archive exit 0