\begindata{text,538478712} \textdsversion{12} \template{default} \define{global } \chapter{1 The Spec File Programming Language} \section{1.1 About this Document} Everything in this chapter has been made obsolete by a Lisp-like language called Flames, the Filtering Language for the Andrew MEssage System. A document describing Flames is in: /usr/andrew/doc/ams/Flames.pgr This document is about Spec and is written for people who already have spec files and want to know how they work, in hope that it might help in translating the spec files into Flames. People have found, though, that it is easier to forget about Spec entirely and just re-write their Mailbox processing files in Flames. If you are using EZ to read this document, you can open an interactive Table of Contents window by choosing \bold{Table of Contents} from the \italic{Page} menu card. Clicking on a heading in the Table of Contents window that appears scrolls this document to bring the heading into view. \begindata{bp,539690888} \enddata{bp,539690888} \view{bpv,539690888,1566,0,0} \section{1.2 About Spec Files} Spec files are files which contain programs, in a very idiosyncratic language, which describe how messages in a mailbox are to be placed into message directories. In general, the spec file for a mailbox should be stored as ".MS.spec" in the parent of the mailbox directory. However, you can debug spec files stored in other locations, using the cui "check" command ("check "). It is strongly recommended that you debug your new spec files for working message domains by using a test directory as your mailbox and some other file as your spec file, using the "check" command, rather than by simply putting your changes into the deployed spec file and seeing what happens. \begindata{bp,539690568} \enddata{bp,539690568} \view{bpv,539690568,1567,0,0} \section{1.3 The Spec File Language Structure} The spec file language is a stack-oriented language. If you don't know what a stack is, give up now. Really. Just give up. Or at least, go read an introductory computer science text and find out what a stack is. You will be utterly unable to program in this language if you don't understand what a stack is. The spec file language is also a terrible language. It was intended as only an interim language, to get the bboards running until a serious programming language (Flames) could be put in its place. Most operations are performed on the top of the stack, but the language is not that consistent; some are performed via arguments after the command name. Think of each command as an idiosyncratic language of its own, and you'll be on the right track. As of ms version 3.21 (corresponding to messages 3.5 and cui 3.15), it is impossible for a spec file to put the same message in a directory more than once. That is, if you put the following two lines in a spec file, the second line will be ignored: \example{ $addtodir /foo/bar/baz $addtodir /foo/bar/baz } The program is always run\italic{ implicitly} over a single message. That is, your program is run once for each message in the mailbox, and since it only deals with one message at a time, it never needs to mention that message by name. The message is just magically \italic{there}. Like the language itself. Good luck. \begindata{bp,539690504} \enddata{bp,539690504} \view{bpv,539690504,1568,0,0} \section{1.4 The Spec File Language Syntax} Leading white space (spaces and tabs) in a spec file program line will be ignored. All other white space is highly significant, often in ill-defined ways. Every line in the spec file is interpreted as a command. If a line begins with a dollar sign ("$"), it is a special command; otherwise, it is interpreted as an implicit "push" command, and the line is simply pushed onto the stack. One special command is the "$#" command. This is the comment command. It must be followed by a space, but everything after that is ignored. You can use it for comments. You should do so. You will need them. Here are the remaining commands, for what they're worth: \description{ '$bogus' -- this is a pre-defined invalid command. I cannot imagine why anyone, including me, would ever want this command, since it behaves exactly like any other invalid command. That is, badly. '$$ ' -- this command can be used to push a line onto the stack if it begins with a dollar sign, but only if the dollar sign is followed by a space. That is, "$$ foo" will push "$ foo" onto the stack, but "$$foo" will give you an error. As far as I can tell, this is useless too. (You can tell I had fun deciding which commands to document first.) If you really want to push something literally, use the $push command. '$# ' -- this can be used to push anything onto the stack, even if it starts with a dollar sign, even if it the dollar sign is not followed by a space. Mind-boggling. '$pop ' -- this command will pop n items off the stack, effectively throwing them away. If n is not supplied, or if the argument to pop is non-numeric, one item will be popped. (Thus a cryptic spec file could deliberately say "pop ten" or "pop until you're blue in the face", instead of "pop 1". Security through obscurity. Isn't that a great feature?) '$clear' -- this command clears the stack. '$exec' -- this command pops the top element of the stack and treats it as a command in the spec file language. This was added to help make sure that the language was Turing equivalent, a very important feature if you want to use spec files to solve the Tower of Hanoi puzzle. '$stop' -- this command tells the server that whatever else it believes, it is done reading the spec file. There is an implicit '$stop' command at the end of every spec file, so you don't need one there. '$header ' -- This command pushes the contents of the named header onto the stack. Thus if you say '$header to', the contents of the message's "To:" header will be pushed on to the stack. If there is no such header, the null string will be pushed. The header name you give should be all lower case, and the comparison will then be performed in a case-insensitive manner. If you have any upper case letters in the header name of the $header command, you lose. For sure. '$lcheader ' -- Well, hmm, $header didn't do quite what I wanted, so we now have $lcheader. It is just like $header, except it maps the entire contents of the header to lower case before pushing it onto the stack. '$dupheaders ' -- Well, hmm, what if there are two or more headers with the same name? For example, you often see messages with two or more "Received:" headers. What if you want all of them? This command will act just like $header, except that it will get all such headers, putting the contents of each onto the stack in one giant string, separated by commas. '$lcdupheaders ' -- Well, hmm, that wasn't quite it either. This one is just like $dupheaders, except that it maps the contents to lower case before pushing them onto the stack. '$lcsendingheaders' -- Well, it was a pain to look at all the different headers that might be relevant to a piece of mail's specified destination address. So this pushes, in one giant string, the combined contents (separated by commas) of the "To", "CC", "Apparently-To", and "Resent-To" headers onto the stack, mapped to lower case. Note that there is no "$sendingheaders" command, only "$lcsendingheaders". This is not a bug, I really felt the world would come to an end if I implemented a "$sendingheaders" command. '$extractmaps ' -- You're gonna love it. This one takes whatever is on the stack -- presumably the result of one of the many variations on the $header command -- and looks through it for addresses beginning with the specified prefix. (No, Craig, it does not use the parsing library to correctly separate the addresses.) It then pushes back onto the stack the portions of the matching addresses which trail the prefix. Is that clear? No? Well, how about an example? Okay. If the top of the stack is "bb#foo.bar, yellow.rose.of.texas, bb#mumble.frob", and you execute the command "$extractmaps bb#", then the old top of the stack is popped, and the new string pushed onto the stack is "foo.bar, mumble.frob". Just what you wanted. '$extractliberally ' -- Even better. This is just like extractmaps, except that it treats spaces, right parentheses, newline, and semi-colons the same way it treats commas -- as delimiters between objects which might be the names of bboards to post on. '$authuser' -- This will push the user id of the authentic sender onto the stack. This does NOT look at the From or related headers, but only at the Vice authentication values and the delivery system's authentication traces. If this pushes a user id onto the stack, you can really believe it. If the user was unauthenticated, the string "" will be pushed. If the user was root or a non-local user, the string "root" will be pushed. If the numeric user id is not in /etc/passwd, then the string "" will be pushed. '$nonempty' -- this command will push a 1 onto the stack if the stack is not empty and the top element is not the null string, otherwise it will push a zero. It will NOT pop the top of the stack, so that the value you are checking for non-emptiness will remain where it was. '$contains' -- this command will push a 1 onto the stack if the top element of the stack contains the next-to-top element as an exact substring. Both elements are removed from the stack. '$containsoneof' -- this command will push a 1 onto the stack if the top element of the stack contains any word in the next-to-top element on the stack as an exact substring. Both elements are removed from the stack. "Words" for this purpose are separated by spaces. '$if