!~Filename~:! ! Library for sending Mail on CMU-CS-C. ! BABYLC !* B A B Y L C L I B R A R Y Author: Leonard N. Zubkoff Revision: 01/21/83 01:11:39 Copyright (C) 1982, Leonard N. Zubkoff All Rights Reserved This library contains replacements for some of the BABYLM library routines for use on CMU-CS-C. Make sure to edit BABYLM to call & CMU Mail Buffer instead of & TNX Mail Buffer. These modifications also allow for mailing to personal mailing lists and to files with relative ease. A destination of the form : @ is replaced by the definition of : in :Mailing.lists. A destination of the form *@ causes the mail to be appended by XMAILR to the file (destination files with <>'s in them should be enclosed within "s); if no device and directory are specified, the user's home directory will be used. Mailing list files should be just like the header of a message regarding format (no blank lines, continuation lines), as in: Test: Zubkoff@CMU-CS-C, Zubkoff, Zubkoff@CMU-CS-A In addition the HOSTS2 library is used to handle nicknames and abbreviations to XMAILR-supported mail, and the hosts names in such mail will be enclosed in Rubouts so that XMAILR will provide the appropriate return path. Mail is now always queued to the Mail: directory, so no bit need be set in the queue file System:Xmailr.flags. Note that the Mail: directory is protected 774444 so that we must write the queue file to the user's home directory and then rename it to the Mail: directory. ! !& CMU Mail Buffer:! !S Write buffer out to Network-Mail file.! !* Allows multiple To:s, Cc:s. They are merged and auto-filled by & Process Recipient Field. The original buffer stays the same -- we do all message-massaging in a temporary buffer. This way the original buffer is available for resending if desired. ! M(M.M& Declare Load-Time Defaults) Babyl @ Flag,: 0 Babyl Header/Text Separator, * 1 line that separates header and text in recursive mail edit: |--Text follows this line--| Network Hosts Table, String Pointer to HOSTS2 Table: 0  F[D File [1[2[3[4[5[6 0F[VB 0F[VZ !* Widen bounds of message.! 1[Babyl @ Flag !* For RFC822, will use @ instead of! !* the word AT, in headers.! !* Load HOSTS2 Library if Necessary. ! 1,M(M.M & Get Library Pointer)HOSTS2 U0 !* Q0: Table or 0 if not loaded. ! Q0"N (Q0+336)UNetwork Hosts Table ' !* If already loaded, compute pointer. ! "# F[:EJ Page !* Flush library when we return. ! M(M.MLoad Library)PS:HOSTS2+336 UNetwork Hosts Table ' g(q..o( f[BBind )) !* Temporary buffer, with copy of! !* the message to send.! zj -2f= "n i ' !* Make sure message ends in a CRLF,! !* since XMAILR needs that.! q..ou3 !* 3: Message copy buffer.! fsMachine:f6u2 f[BBind q..ou4 q3[..o !* 4: Temporary buffer for mailbox! !* list. Push (so f[BBind pops and! !* kills ok) to message buffer.! QBabyl Header/Text SeparatorU1 !* 1: E.g. --Text Follows...! J :S 1 "E :I*No Text, just Header FS Err' !* Didnt find separator.! 0:L 0K !* Replace separator with blank line.! Z-. FS V Z !* Set bounds to just header. Parser! !* and header-completer need bounds.! !* Note that by having the header completer go first, the BCC and FCC! !* searches later can be assured of a CRLF before the BCC: or FCC:, since! !* the Date field is put on the top line.! Q4M(M.M & Complete TNX Header) !* Add date, from, ensure good header.! !* It ensures that date is top line,! !* and header ends with blank line.! M.M& CMU Process Recipient Field !* P: Parser.! 0[Babyl Strip Local Host !* Dont strip local host name in mail! !* ..we send (something about the pot! !* ..calling the kettle black).! !* Check the From and Sender fields: be sure they have hosts specified, and! !* be sure they use @ instead of AT for RFC822: ! q2,q4mpFrom q2,q4mpSender q4[..o hk ]..o !* Empty the recipients collected from! !* From/Sender. We only want To/etc.! q2,q4mpTo !* Parse, pull together, and auto-fill! !* the TO fields.! q2,q4mpcc !* Ditto for CCs.! q2,q4mpbcc !* Ditto for BCCs! !* 4: Now filled in with mailbox and! !* full recipient lines (though we! !* dont actually want the recipient! !* lines).! 0s Bcc: !* Set search default once.! j<:s; -4ck !* Find Bcc lines and kill! <@f @; k>> !* Kill continuation lines also! j < @f l !* Past leading whitespace on line.! 15.,1a-15."e 0lk'"# l' !* Kill the line if just blank.! .-z; > !* Go over each line in header.! 0,FS Z FS BoundariesW !* Widen to entire Message. ! !* Now we expand any mailing lists in the recipient buffer. Addresses of the form :@ are taken to be mailing list addresses. ! 0U6 !* No Mailing Lists buffer yet. ! Q..OU1 Q4U..O !* Switch to recipient buffer. ! found. ! -2C (FK+2)D !* Delete :@. ! 0L :X5 2K !* Q5: Name of Mailing List. ! ZJ !* Move to end of buffer. ! Q6"E !* If no Mailing Lists buffer yet. ! FS B Cons U6 !* Create Mailing Lists buffer. ! Q6U..O !* Select it. ! F[D File !* Save default filenames. ! ETDsk:Mailing.Lists !* Mailing Lists file. ! FS H Sname FS D Sname !* Default to Home Directory. ! ER @Y !* Insert into buffer. ! ZJ -2F= "N I ' !* Make sure file ends in a CRLF. ! F]D File ' !* Restore default filenames. ! "# Q6U..O ' !* Select Mailing Lists buffer. ! J I  J :S 5:"E Q1U..O :I*5 -- Mailing List Not Found FS Err' !* Signal an error if no such list. ! J 2D Q2,Q4MP5 !* Add new recipients from mailing list. ! Q4U..O > !* Back to recipients buffer. ! Q6 F"N FS B Kill ' !* Kill Mailing Lists buffer. ! !* We now handle any recipients of the form *""@. XMAILR will actually handle appending the text to the file. ! J <:S@2 ; W FKC .U5 !* Q5: find @. ! 0L 1A-*"E !* If * at beginning of line. ! .+1U6 !* Q6: Position after *. ! !* Delete "s and spaces. ! Q6J .,Q5 X6 .,Q5K !* Q6: File name. ! ET Dsk:Foo.txt !* Default filenames. ! FS H Sname FS D Sname !* Default to Home Directory. ! Q6 FS D FileW G(FS D File) !* Replace with expanded version. ! -2D ' !* Omit the .0 though. ! 2L> !* Else skip this recipient. ! Q1U..O !* Restore Buffer. ! 0,FS Z FS BoundariesW !* And widen to entire message. ! !* Take care of any Fcc: field in the header. ! 0s Fcc !* Set search default once.! j <:s; @f l 1a-:"e c :x2 l-k !* Get filename! etDSK:FOO.TXT fsHSnamefsDSnamew et2 !* Set default! Q..O[0 F[B Bind G0 J :S W 0,.FS BoundariesW FS MachineU2 !* Q2: Local Host Name. ! J <:S @ ;W -D-C-D> !* Remove spaces around @s. ! J <:S:@2›;W FKDI: ;> !* Fix up mailing lists recipients. ! J <:S›; -D> !* Remove Rubouts in header. ! 0f[VZ m(m.m& Append to TNX Mail File) f]VZ !* Append to file! F]B Bind ]0 '> !* For Babyl debugging, allow viewing of the message and recipient list (Q4)! !* before we write out the queue file. Can allow it to be written (C-M-Z) or! !* abort (C-]): ! 0fo..qDebugging Babyl"n :i*Debugging Babyl[..j  ]..j' Q4U..O !* Return to Recipients Buffer. !  :F:FB@$  :L  2L  !* Sort recipients by host name, each! !* being a pair of lines.! J :I2 !* 2: Initialize current host name.! <.-Z; :F:FB@ D :F~2"E :K'"# :FX2 0L 14.I G2 I ' L K> !* 2: Put recipients for one site on! !* one page and update current host.! !* The full recipient lines are! !* discarded.! 14.II  .U6 G3 !* Put message on next page.! Q6J :S "E ZJ' !* Find end of header. ! Q6,.FS BoundariesW !* Narrow to header. ! FS MachineU2 !* Q2: Host Name. ! J <:S:@2›; FKD I: ;> !* Add ; to mailing lists. ! 0,FS Z FS BoundariesW !* And widen to entire buffer. ! 0fo..qDebugging Babyl"n :i*Debugging Babyl[..j  ]..j' F[B Bind !* Temporary Buffer for File Name. ! I[--NETWORK-MAIL--].NEW- !* First part of name. ! 8[..E FS Date\ ]..E !* Time and Date in octal. ! I-Babyl-J !* Next part of file name. ! FS Uindex\ !* Job Number in decimal. ! HX*U3 !* Save File Name in Q3. ! F]B Bind !* Return to Message Buffer. ! ETDSK: FS H Sname FS D SnameW !* Default to Home Directory. ! ET3.0 !* File for XMAILR. ! F[D File ET[TECO] OUTPUT 0FS D VersionW EI !* Open to safe file. ! F]D File HP EF !* Close to XMAILR file.! ER 11.FS If Fdb&7777777777.,11.FS If Fdb EC !* Get file back, set ! !* generation-retention count to 0. ! FS I File U2 !* Q2: File we wrote. ! -10U0 !* Q0: - Retry Count. ! <%0; !* Loop until we succeed or timeout. ! 1: U1 !* Q1: Rename file. ! Q1"E 0;' 30> !* Exit if success, else wait. ! Q1"N Q1 FS Err ' !* Signal error if need be. ! @FT [Mail Queued]  !* Tell user.! 0 FS Echo Active  !& CMU Process Recipient Field:! !S Merge, reform and parse. NUMARG is recipient buffer, pre-comma NUMARG is default host name. STRARG is field name, without any colon. Bounds should be set to just the header. Point is left at the start of the field body. Recipient buffer has the parse, a pair of lines per recipient: 1st line is mailbox name, 2nd line is full (cleaned up) recipient specification. Host names are upper-case since TWENEX mailer requires it.! !* All instances of the field are merged into one, with continuation lines. Some cleaning is done (of whitespace, @s), host name is added if necessary, and field is auto-filled. Recipient Hosts Names are enclosed in Rubouts after being verified in the Network Hosts Table. If variable Babyl Strip Local Host is non-zero, then the local host name is removed. We experimented with the idea of removing all at-ITSes on any ITS, but there are names (not those in INQUIR) which are not defined on the local host. And removing the host name is not an obviously wrong thing -- the user doesnt know that it is wrong. Whereas one reason for removing any ITS was to catch this-user at another-its, and not get duplicates when reply sets up -- it wont know that FOO@AI = FOO@MC. But the user sees this and correct for it if desired, whereas cant see the alternatives problems. And getting two copies of a message isnt a disaster.! m(m.m& Declare Load-Time Defaults) Babyl ..D,:0 Babyl @ Flag,:0 Babyl Strip Local Host,* Non-zero removes local host from reformed header:1  [0[1[2[3[4[5[6[7[8[9 [..o !* save regs! QNetwork Hosts Table !* QN: Network Hosts Table ! [A[B[C !* Save Q Registers. ! qBabyl ..D[..d !* All set up for nifty parsing,! !* especially using s-expressions.! !* Find all instances of this header field and merge them into the first one! !* found. And merge all its continuation lines into a long 1-line field for! !* easier parsing. (We will fill it later.)! @fn|.-z(j 0,1a-15."e 2d')+z:j| !* Will cleanup top blank line.! j i  !* Boundary condition for search.! f[VB f[VZ !* Restore bounds before the top-line! !* cleanup goes off.! 0s  !* Initialize search string to CRLF! !* field name.! qBabyl Strip Local Host"n fsMachine:f6u7 !* 7: Local host.! fsOSTeco"e :i7 at 7 at MIT-7 ' !* With ATs for stripping.! "# f[DFile !* Not ITS. For OZ will need! !* to reset filename default.! :i7 at 7 '' !* 7: ...! "# 0u7 ' !* 7: Zero if keeping the local host.! u8 q..ou9 !* 8: recipient buffer, 9: header! !* buffer.! j <:s"e 0' @f l 0,1a-:@; > !* Find first such field.! c @f k 5-(fsHPos)f"lw0',40.i .u0 !* 0: point just before first! !* ..non-whitespace.! < l 0,1af :; 0:k > !* Merge continuations.! 0:l .u1 !* 1: end of first field.! < :s; @f l 0,1a-:"n ! ' !* Find next such field.! c 0k !* Kill field name.! < :l k 0,1af :; > !* Merge continuations.! 0fx2 q1j i, g2 .u1 !* Remove it from here and go append! > !* field body to first.! !* 1: Still end of first field.! q0j .f[VB fsZ-q1f[VZ !* Set bounds to just this field! !* body.! !* Now we parse this 1-line recipient field.! qBabyl @ Flagu0 !* 0: 0 if use at, non0 if use @, in! !* the header part. (For RFC822.)! < !* Iterate over recipients.! @f ,k .-z; .u1 i  !* Move to beginning of next! !* recipient.! !* 1: Point where recipient starts,! !* before one leading Space.! .u.0 !* .0: Possible uname start, after the! !* leading Space.! -1u6 0u2 0u.2 !* 6: <> state, 2, .2: @ flags.! < !* Iterate over tokens.! @f f"gd i 'w .-z; !* Skip over whitespace. To 1 space.! 1au5 q5"b c :o5 r' !* Dispatch if break character.! q6:"g !* If not past <>...! @flu4u3 q4j !* 3,4: bounds of sexp.! q3,q4f~at"e oAT' !* Is an AT word token.! q3-b,q4-b(q8u..o)g9 !* Add sexp to recipient buffer.! i  q9u..o ! ' !* And go get another token.! ! in recipientfsErr !* ...! !' :i*Extra < in recipientfsErr !>! q6"e .-1u6 ! ' :i*Extra > in recipientfsErr !* ...! !,! r -@f k c 0; !* Comma ends a recipient.! !AT! q0"n -2d i@ 1a-32"n i  r'' !* AT becomes @ for RFC822.! o@-AT !* To common @/AT code.! !@! q0"e -d 0a-32"n i ' iat 1a-32"n i  r'' !* @ becomes at.! "# r 0a-32"n i ' c 1a-32"n i  r'' !* @ stays @, with spaces.! !@-AT! !* Code common to @ and AT.! q2"n !* Have another @ earlier.! q0"n .-z(q2j -2dd i%)+zj !* If RFC822, change earlier @s to %s! q8u..o q.2j -d i% :l q9u..o'' !* ...! .u2 !* 2: Point after at, and a flag.! q8u..o .u.2 !* .2: Point after at in recip buf.! 0f"e !* If mailbox line empty so far we either have a null recipient or! !* a parenthesis recipient. (Parentheses usually surround! !* comments, but if there is ONLY a comment we assume it is an ITS! !* thing like (BUG FOO).)! q9u..o -2:@fll .u3 !* 3: Maybe point after (...).! -@fll .-q1-1"n :i*Null recipientfsErr' !* Not just (...) as! !* the recipient.! .-b,q3-b(q2j q8u..o)g9' !* Ok, add (...) as the recipient.! -@f k i@ q9u..o ! !* Insert @ into recipient buffer and! !* go get another token.! !(! fll !* Skip over comments, (...).! > !* End of token iteration.! q8u..o -@f k !* Select recipient buffer.! q2"e 0f"e !* We havent had a @ yet; use default.! !* If mailbox line empty so far we either have a null recipient or! !* a parenthesis recipient. (Parentheses usually surround! !* comments, but if there is ONLY a comment we assume it is an ITS! !* thing like (BUG FOO).)! q9u..o .u2 -:@fll .u3 !* 3: Maybe point after (...).! -@fll .-q1-1"n :i*Null recipientfsErr' !* Not just (...) as! !* the recipient.! .-b,q3-b(q2j q8u..o)g9' !* Ok, add (...) as the recipient.! i@ g() !* Add default host name, if none, to! !* recipient buffer.! q9u..o .-z(q6"g q6j'"# -@f, l' !* And header.! q0"e i at '"# i @ ' g())+zj !* ...! q8u..o' !* Back to recipient buffer.! !* Now we lookup the host name in either the Network Hosts Table and enclose the name in the header in Rubouts for XMAILR. ! .UA -S@W C .,QA XB !* QB: Host Name. ! 0FONB UC QC"N (QC+QN)UC ' !* QC: Official Network Host Name. ! QC"E Q9U..O :I*B -- Unknown Host Name FS Err ' !* Signal Error. ! .,QA K GC !* Insert Official Host Name. ! i  !* Finish off mailbox line.! Q9U..O .UB !* Select Header buffer. ! 0UA !* Neither comma nor right angle. ! 0A-,"E -C %A' !* QA: 1 if comma, 0 if none. ! 0A-62"E %A' !* Increment QA if right angle. ! -S @ W FKC .,QB-QA K Q1+1,.XB 177.I GC 177.I QAC !* Replace Host Name. ! Q8U..O !* Select Recipient buffer. ! GB GC I  !* Add in recipient line. ! q9u..o !* Select header buffer.! !* Maybe flush local host name, iff it is at the end of the recipient! !* field. One special case -- since we are essentially forced to always put on! !* From: User at MIT-MC, with Sender: User at MIT-OZ, since OZ not on! !* Arpanet, trim the MC one when on OZ if the MC-uname has an OZ! !* directory. So at least Babyl users will see pretty headers... and! !* Oz-to-Oz messages will generate Oz-to-Oz replies, not going through! !* MC -- because default host of OZ will be added to what the visible! !* header is, only the in-header text of messages is changed to MC, not! !* the actual list of destinations.! q7"n !* Trimming host for header reformer.! f~7 at MIT-OZ"e !* Special case for OZ. See if should! !* trim at-MC.! .-z( -@f,<>l .u3 -:s at MIT-MC( !* 3: temp! )"l fk+.-q3"e !* at-MC part ends ok.! q8[..o -2l :fb@"l r' 0x3 zj ]..o !* 3: Uname.! q3:fcu3 1:MAIL.TXT.0wec>u3 !* 3: 0 or errmsg.! q3"n f~3OPN0075-8"n 0u3'' !* 3: 0 if uname has! !* an OZ directory.! q3"e 10d''' !* Trim at-MC if has OZ-directory.! )+zj' !* Restore point.! !* Even OZ=MC case falls through to normal one, since some incoming! !* mail not from Babyl might just have simple form, @OZ: ! .-z( -@f,<>l .u3 -:s7"n fk+.-q3"e fkd '' !* 3: temp! )+zj' !* Restore point.! !* Done that recipient. XMAILR want recipients one to a line and will then handle fixing up the headers for us. ! I  > !* End of recipient iteration.! !* Done parsing this recipient field. Do minor final cleanup.! -@f, k !* Remove any trailing commas or! !* spaces.! J <:S @ ;W -D-C-D> !* Remove space around @s. ! J !* Leave point at start of field body.!  !Build HOSTS2 File:! !C Create Emacs:Hosts2.:Ej from System:Hosts2.bin. ! !* The Hosts2 Table Q-Vector has 2 elements per entry. The first element is the Name of a Host and the second is the Official Host Name of that Host. After loading library HOSTS2, the Q-Vector of Host Names is available as +336 (decimal). Note that all Names in this file are interned so only one pure string of each will exist. ! [0[1[2[3[4[5[6[7[I[N[T !* Save Q Registers. ! F[D File !* Save Default Filenames. ! F[B Bind !* Push to a temporary buffer. ! ET System:Hosts2.bin !* Setup new Default Filenames. ! ER FY EC !* Read in Hosts2 Binary File. ! 10.*5 FS Word U0 !* Q0: Address of Names Table. ! Q0*5 FS Word U1 !* Q1: Number of Entries in Table. ! (2*Q1+1)*5 FS Q Vector UT !* QT: Hosts Table Q-Vector. ! 2U:T(0) !* Store size of each Q-Vector Entry. ! 0UI !* QI: Index into Hosts Table. ! 5 FS Q Vector UN !* QN: Names Table. ! 2U:N(0) !* Store size of each Q-Vector Entry. ! (Q0+1)*5 FS Word U2 !* Q2: Number of Words per Entry. ! (Q0+2)*5 U0 !* Q0: Character of First Name Entry. ! Q1< !* For each Host Entry. ! Q0 FS Word U3 !* Q1: Host Entry ! Q3&777777. U4 !* Q4: Address of Host Name. ! Q3/1000000. U3 !* Q3: Address of Site Table. ! Q4*5 J .,(:SW .-1) X5 !* Q5: Host Name String. ! Q3*5 FS Word U3 !* Q3: LH = Address of Official Name. ! Q3/1000000. U3 !* Q3: Address of Official Host Name. ! Q3*5 J .,(:SW .-1) X6 !* Q6: Official Host Name String. ! @:FON5 U7 !* Lookup Host Name in Names Table. ! Q7"L -Q7U7 QN[..O Q7*5J 10,0I !* Create an entry if necessary. ! Q5U:N(Q7) Q5U:N(%7) ]..O ' !* And initialize it with the String. ! "# Q:N(%7) U5 ' !* Use existing entry if there is one. ! @:FON6 U7 !* Lookup Official Name in Names Table. ! Q7"L -Q7U7 QN[..O Q7*5J 10,0I !* Create an entry if necessary. ! Q6U:N(Q7) Q6U:N(%7) ]..O ' !* And initialize it with the String. ! "# Q:N(%7) U6 ' !* Use existing entry if there is one. ! Q5 U:T(%I) !* Store Host Name. ! Q6 U:T(%I) !* Store Official Host Name. ! (Q0+(Q2*5))U0> !* Loop until all Host Entries completed. ! HK !* Delete Hosts2 Binary File. ! (Q1*2+1)*5,0I !* Make space for Q-Vector. ! (FQN-5)/10 U3 !* Q3: Number of Entries in Names Table. ! 0UI !* QI: Index into Names Table. ! Q3 !* Now insert the string. ! 0UI !* Index into Hosts Table Q-Vector. ! 2,0 FS Word !* Store Size of Table. ! Q1 !* Insert pure string pointer. ! ET Emacs:Hosts.:Ej !* Prepare to read in header file. ! J ER FY EC !* Read in file. ! -5C <.FS Word:@; W5D-5C> 5C !* Delete 0 Words. ! !* Loader macro that can't find the real data. Note that the beginning of this buffer was setup so that the header of the Q-Vector lies in the second byte of a word and that the Q-Vector of interest is located at an offset of 336 characters from the start of file. ! (Q1*2+1)*5U4 !* Q4: Length of Q-Vector. ! 5,0I !* Make room for Header. ! 3760000000.U5 !* Initial value of Header Word. ! Q4+4&177.*100000.+Q5 U5 !* Low seven bits of Table Length. ! Q4+4/200.&177.*400.+Q5 U5 !* Middle seven bits of Table Length. ! Q4+4/40000.&177.*2+Q5 U5 !* High seven bits of Table Length. ! Q5,.-1FS Word !* Store new Header Word. ! ZJ .U4 -Q4 U4 !* Q4: Number of padding characters. ! Q4,0I .U4 !* Add in Padding. ! 774000000376.U5 !* Initial value of Header Word. ! Q4&177.*20000000.+Q5 U5 !* Low seven bits of File Length. ! Q4/200.&177.*100000.+Q5 U5 !* Middle seven bits of File Length. ! Q4/40000.&177.*400.+Q5 U5 !* High seven bits of File Length. ! Q5,0FS Word !* Store new Header Word. ! ET Emacs:Hosts2.:Ej !* Setup Default FileNames. ! EW @HP EF !* Write Library File. ! F]B Bind !* Return to original buffer. ! F]D File !* Restore Default Filenames. !  !* Return. !