650 lines
14 KiB
Forth
650 lines
14 KiB
Forth
|
\ MAILFIG program to handle forms for comments to FIG
|
||
|
|
||
|
\ This is an ANS Forth program requiring:
|
||
|
\ 1. The File Access word set.
|
||
|
\ 2. The words CMOVE and COMPARE from the String word set.
|
||
|
\ 3. A system dependent word GETENV to get the specified
|
||
|
\ environment string,
|
||
|
\ GETENV ( str count -- str' count' )
|
||
|
\ 4. The word STDIN to get the file ID of standard input.
|
||
|
\ 5. The words OPEN-PIPE and CLOSE-PIPE to open and close pipes to
|
||
|
\ processes. (These are communicated with via the normal File access
|
||
|
\ words).
|
||
|
\ 6. READ to write to Unix file descriptors (because of a problem with
|
||
|
\ ThisForth 94-09-12).
|
||
|
|
||
|
|
||
|
\ (c) Copyright 1994 Everett F. Carter. Permission is granted by the
|
||
|
\ author to use this software for any application provided this
|
||
|
\ copyright notice is preserved.
|
||
|
|
||
|
|
||
|
\ rcsid: @(#)mailfig.fth 1.5 10:15:52 11/6/95 EFC
|
||
|
|
||
|
|
||
|
FALSE CONSTANT ?DEBUG
|
||
|
TRUE CONSTANT ThisForth
|
||
|
FALSE CONSTANT PFE
|
||
|
|
||
|
ThisForth [IF]
|
||
|
|
||
|
|
||
|
\ =================== ANS File words for ThisForth =========================
|
||
|
|
||
|
\ file open modes
|
||
|
: R/W S" r+" ;
|
||
|
: R/O S" r" ;
|
||
|
: W/O S" w" ;
|
||
|
|
||
|
: APPEND S" a" ; \ NOT ANS, but necessary
|
||
|
|
||
|
|
||
|
: OPEN-FILE fopen DUP 0= ;
|
||
|
|
||
|
: READ-LINE ( addr u fileid -- u' flag ior )
|
||
|
STREAM
|
||
|
0 SWAP
|
||
|
0 DO
|
||
|
next-char EOL = IF LEAVE THEN
|
||
|
next-char EOF = IF LEAVE THEN
|
||
|
get-char
|
||
|
2 PICK I + C!
|
||
|
1+
|
||
|
LOOP
|
||
|
|
||
|
UNSTREAM
|
||
|
|
||
|
SWAP DROP TRUE 0
|
||
|
;
|
||
|
|
||
|
: READ-FILE ( addr u fileid -- u' flag ) \ a hack
|
||
|
STREAM
|
||
|
0 SWAP
|
||
|
0 DO
|
||
|
next-char EOF = IF LEAVE THEN
|
||
|
get-char
|
||
|
2 PICK I + C!
|
||
|
1+
|
||
|
LOOP
|
||
|
|
||
|
UNSTREAM
|
||
|
|
||
|
SWAP DROP FALSE
|
||
|
;
|
||
|
|
||
|
|
||
|
: REPOSITION-FILE ( d fid -- flag )
|
||
|
ROT ROT DROP 0
|
||
|
fseek
|
||
|
;
|
||
|
|
||
|
: WRITE-FILE ( c-addr u fileid -- ior )
|
||
|
DISPLAY TYPE
|
||
|
0 DISPLAY
|
||
|
TRUE
|
||
|
;
|
||
|
|
||
|
|
||
|
: WRITE-LINE ( c-addr u fileid -- ior )
|
||
|
DISPLAY TYPE CR
|
||
|
0 DISPLAY
|
||
|
TRUE
|
||
|
;
|
||
|
|
||
|
: CLOSE-FILE fclose ;
|
||
|
|
||
|
[THEN]
|
||
|
|
||
|
\ =========================================================================
|
||
|
|
||
|
ThisForth [IF] \ ThisForth version
|
||
|
: OPEN-APPEND
|
||
|
APPEND OPEN-FILE
|
||
|
;
|
||
|
|
||
|
[ELSE]
|
||
|
\ ANS Brute force OPEN-APPEND, depending upon what is under the hood, there may
|
||
|
\ be more efficient definitions
|
||
|
: OPEN-APPEND R/W OPEN-FILE
|
||
|
DUP 0= IF OVER FILE-SIZE
|
||
|
0= IF 3 PICK REPOSITION-FILE DROP THEN
|
||
|
THEN
|
||
|
;
|
||
|
[THEN]
|
||
|
|
||
|
|
||
|
FALSE VALUE bad-status
|
||
|
0 VALUE seq-file
|
||
|
0 VALUE log-file
|
||
|
0 VALUE seq-no
|
||
|
FALSE VALUE cc-req
|
||
|
FALSE VALUE unesc-req
|
||
|
FALSE VALUE strip-plus-req
|
||
|
|
||
|
CREATE NEW-LINE-CHARS 2 ALLOT
|
||
|
10 NEW-LINE-CHARS C!
|
||
|
\ 13 NEW-LINE-CHARS 1+ C!
|
||
|
|
||
|
|
||
|
0 VALUE buf-len
|
||
|
0 VALUE input-buffer
|
||
|
VARIABLE scan-ptr
|
||
|
|
||
|
ALIGN
|
||
|
CREATE out-buf 32 ALLOT
|
||
|
|
||
|
\ ============= A String pointer data structure =============================
|
||
|
|
||
|
: string: \ build a counted string
|
||
|
CREATE
|
||
|
0 , \ POINTER to the data
|
||
|
0 , \ the count
|
||
|
DOES>
|
||
|
DUP @ SWAP CELL+ @
|
||
|
;
|
||
|
|
||
|
|
||
|
: $! ( addr count 'str -- ) \ store a string
|
||
|
|
||
|
>BODY
|
||
|
SWAP OVER CELL+ !
|
||
|
!
|
||
|
;
|
||
|
|
||
|
: $len ( addr count -- count )
|
||
|
SWAP DROP
|
||
|
;
|
||
|
|
||
|
: $copy ( addr count 'str -- )
|
||
|
|
||
|
HERE 2 PICK ROT $! \ store string pointer to HERE
|
||
|
HERE SWAP DUP ALLOT
|
||
|
CMOVE
|
||
|
;
|
||
|
|
||
|
: $cat ( addr1 count1 addr2 count2 -- addr count )
|
||
|
2 PICK OVER + DUP >R
|
||
|
HERE >R
|
||
|
ALLOT
|
||
|
2SWAP
|
||
|
R@ SWAP DUP >R CMOVE \ move first string
|
||
|
|
||
|
R> R@ +
|
||
|
SWAP CMOVE \ move the second string
|
||
|
|
||
|
R> R>
|
||
|
;
|
||
|
|
||
|
\ the data fields
|
||
|
string: name
|
||
|
string: comments
|
||
|
string: e-mail
|
||
|
string: subject
|
||
|
string: request
|
||
|
|
||
|
\ ======================= LOCAL FILE NAMES ================================
|
||
|
|
||
|
string: SEQFILE
|
||
|
string: LOGFILE
|
||
|
string: PROGRAM
|
||
|
string: MAILER
|
||
|
string: HOSTNAME
|
||
|
string: DESTINATION
|
||
|
|
||
|
|
||
|
: init-strings
|
||
|
|
||
|
S" /usr/skip/forth/FIG/figmail.seq" ['] SEQFILE $copy
|
||
|
|
||
|
S" /usr/skip/forth/FIG/figmail.log" ['] LOGFILE $copy
|
||
|
|
||
|
S" mailfig.fth V1.5" ['] PROGRAM $copy
|
||
|
|
||
|
S" taygeta.com" ['] HOSTNAME $copy
|
||
|
|
||
|
|
||
|
\ This is the name of the mail program, we are using URL escape codes
|
||
|
\ for quotes which will be converted to actual quotes later
|
||
|
|
||
|
S" /usr/ucb/Mail -s %22FIG Comments%22 johnhall@aol.com skip@taygeta.com " ['] MAILER $copy
|
||
|
|
||
|
|
||
|
\ S" johnhall@aol.com skip@taygeta.com " ['] DESTINATION $copy
|
||
|
|
||
|
\ S" johnhall@aol.com " ['] DESTINATION $copy
|
||
|
\ DESTINATION S" skip@taygeta.com " $cat ['] DESTINATION $!
|
||
|
|
||
|
\ S" skip@taygeta.com " ['] DESTINATION $copy
|
||
|
|
||
|
;
|
||
|
|
||
|
|
||
|
\ =========================================================================
|
||
|
|
||
|
: acknowledge ( -- )
|
||
|
|
||
|
|
||
|
." <HEADER><TITLE> Mail to Forth Interest Group OK "
|
||
|
." </TITLE></HEADER> " CR
|
||
|
|
||
|
." Everything received <B>OK</B><P> "
|
||
|
." Thanks for the mail!" CR
|
||
|
|
||
|
." <P><hr>" CR
|
||
|
|
||
|
." <A HREF=http://www.taygeta.com/fig.html> "
|
||
|
." <IMG SRC=" [CHAR] " EMIT
|
||
|
." http://www.taygeta.com/icons/back.xbm" [CHAR] " EMIT
|
||
|
." ALT = " [CHAR] " EMIT ." <--" [CHAR] " EMIT ." > "
|
||
|
." Back to FIG Home page</A>. " CR
|
||
|
." <P> " CR
|
||
|
|
||
|
;
|
||
|
|
||
|
: nack ( -- )
|
||
|
|
||
|
." <HEADER><TITLE> Mail to Forth Interest Group NOT OK "
|
||
|
." </TITLE></HEADER> " CR
|
||
|
|
||
|
." Sorry, There seems to be a problem with the form as you filled it out "
|
||
|
CR CR
|
||
|
." Is perhaps your name/e-mail missing ?" CR
|
||
|
|
||
|
." <P><hr> " CR
|
||
|
." <A HREF=http://www.taygeta.com/fig/figmail.html> "
|
||
|
." <IMG SRC=" [CHAR] " EMIT
|
||
|
." http://www.taygeta.com/icons/back.xbm" [CHAR] " EMIT
|
||
|
." ALT = " [CHAR] " EMIT ." <--" [CHAR] " EMIT ." > "
|
||
|
." Back to FIG Mailer Form page</A>. " CR
|
||
|
." <P> " CR
|
||
|
|
||
|
;
|
||
|
|
||
|
: sig
|
||
|
." <P><HR><ADDRESS><CENTER> " CR
|
||
|
." Everett F. Carter Jr. -- skip@taygeta.com" CR
|
||
|
." </CENTER></ADDRESS> " CR
|
||
|
|
||
|
;
|
||
|
|
||
|
: atol ( addr count -- d )
|
||
|
>R
|
||
|
0. ROT
|
||
|
R>
|
||
|
|
||
|
>NUMBER
|
||
|
2DROP
|
||
|
;
|
||
|
|
||
|
: atoi ( addr count -- n )
|
||
|
|
||
|
atol DROP
|
||
|
;
|
||
|
|
||
|
: move-chars ( dest src count -- dest count )
|
||
|
>R OVER R@ CMOVE R>
|
||
|
;
|
||
|
|
||
|
: itoa ( n -- addr count ) \ (signed) int to counted string
|
||
|
out-buf aligned SWAP
|
||
|
DUP >R ABS S>D
|
||
|
<# #S R> SIGN #>
|
||
|
move-chars
|
||
|
;
|
||
|
|
||
|
|
||
|
|
||
|
: newline ( fileid -- flag )
|
||
|
|
||
|
NEW-LINE-CHARS 1 ROT WRITE-FILE
|
||
|
;
|
||
|
|
||
|
: update_sequence_number ( -- old_no )
|
||
|
|
||
|
SEQFILE R/W OPEN-FILE ABORT" Unable to open sequence file "
|
||
|
|
||
|
TO seq-file
|
||
|
|
||
|
\ get the current sequence number
|
||
|
PAD 16 seq-file READ-LINE ABORT" file read error "
|
||
|
DROP
|
||
|
|
||
|
PAD SWAP atoi
|
||
|
|
||
|
|
||
|
\ increment the number and store it away
|
||
|
DUP 1+
|
||
|
|
||
|
0. seq-file REPOSITION-FILE DROP
|
||
|
|
||
|
itoa seq-file WRITE-LINE DROP
|
||
|
|
||
|
seq-file CLOSE-FILE DROP
|
||
|
|
||
|
;
|
||
|
|
||
|
|
||
|
: write-env ( -- len )
|
||
|
|
||
|
S" SERVER_PROTOCOL" getenv
|
||
|
DUP 0= IF 2DROP S" HTTP/1.0" THEN TYPE
|
||
|
|
||
|
." 200 OK" CR
|
||
|
." MIME-Version: 1.0" CR
|
||
|
|
||
|
S" SERVER_SOFTWARE" getenv
|
||
|
DUP 0 > IF TYPE CR ELSE 2DROP THEN
|
||
|
|
||
|
." Content-Type: text/html" CR
|
||
|
\ ." Content-Encoding: HTML" CR
|
||
|
\ ." Content-Transfer-Encoding: HTML" CR
|
||
|
CR
|
||
|
|
||
|
S" CONTENT_LENGTH" getenv
|
||
|
DUP IF atoi ELSE 2DROP 0 THEN
|
||
|
;
|
||
|
|
||
|
|
||
|
|
||
|
: plus->space ( addr count -- ) \ convert pluses to spaces
|
||
|
|
||
|
0 ?DO I OVER + C@ [CHAR] + = IF I OVER + BL SWAP C! THEN LOOP
|
||
|
DROP
|
||
|
;
|
||
|
|
||
|
: x2c ( addr count -- n )
|
||
|
|
||
|
HEX
|
||
|
|
||
|
>R 0. ROT R>
|
||
|
>NUMBER
|
||
|
2DROP DROP
|
||
|
|
||
|
DECIMAL
|
||
|
;
|
||
|
|
||
|
: unescape-url ( addr count -- count' )
|
||
|
|
||
|
-1 SWAP
|
||
|
0 ?DO
|
||
|
1+
|
||
|
|
||
|
OVER OVER + \ get &url[x]
|
||
|
2 PICK I + C@ \ get url[y]
|
||
|
DUP ROT C! \ url[x] = url[y]
|
||
|
|
||
|
|
||
|
[CHAR] % = IF \ convert it if it is a % char
|
||
|
OVER I + 1+ 2 x2c \ convert url[y+1]
|
||
|
2 PICK 2 PICK + C! \ and store it at url[x]
|
||
|
3
|
||
|
ELSE
|
||
|
1
|
||
|
THEN
|
||
|
|
||
|
+LOOP
|
||
|
|
||
|
1+ \ adjust count
|
||
|
SWAP DROP
|
||
|
;
|
||
|
|
||
|
: skip-past-equals ( -- )
|
||
|
|
||
|
scan-ptr @ DUP buf-len SWAP ?DO
|
||
|
1+
|
||
|
input-buffer I + C@
|
||
|
[CHAR] = = IF LEAVE THEN
|
||
|
LOOP
|
||
|
scan-ptr !
|
||
|
;
|
||
|
|
||
|
: length-to-ampersand ( -- n )
|
||
|
|
||
|
0
|
||
|
buf-len scan-ptr @ ?DO
|
||
|
input-buffer I + C@
|
||
|
[CHAR] & = IF LEAVE THEN
|
||
|
1+
|
||
|
LOOP
|
||
|
|
||
|
;
|
||
|
|
||
|
: scan-in ( -- addr count | 0 )
|
||
|
|
||
|
|
||
|
skip-past-equals
|
||
|
|
||
|
length-to-ampersand
|
||
|
|
||
|
DUP 0 > IF
|
||
|
input-buffer scan-ptr @ + \ addr of first char
|
||
|
SWAP \ put count on top
|
||
|
DUP scan-ptr +!
|
||
|
THEN
|
||
|
;
|
||
|
|
||
|
\ get data from input stream (stdin)
|
||
|
\ set BAD-STATUS if it failed
|
||
|
: get-input-data ( addr len -- )
|
||
|
|
||
|
|
||
|
\ STDIN READ-FILE
|
||
|
|
||
|
\ The above SHOULD work, but with ThisForth 94-09-12
|
||
|
\ it doesn't when this is run with no tty attached (as it will be
|
||
|
\ when HTTP invokes it), so instead we are using:
|
||
|
|
||
|
0 READ
|
||
|
|
||
|
|
||
|
DUP 0 <
|
||
|
TO bad-status
|
||
|
TO buf-len
|
||
|
;
|
||
|
|
||
|
|
||
|
: scan-input-data ( -- )
|
||
|
|
||
|
0 scan-ptr !
|
||
|
|
||
|
scan-in DUP 0 > IF ['] subject $! THEN
|
||
|
scan-in DUP 0 > IF ['] comments $! THEN
|
||
|
|
||
|
scan-in DUP 0 > IF ['] name $! THEN
|
||
|
scan-in DUP 0 > IF ['] e-mail $! THEN
|
||
|
|
||
|
\ get cc request
|
||
|
scan-in DUP 0 > IF ['] request $! THEN
|
||
|
request 3 MIN S" Yes" compare 0= TO cc-req
|
||
|
|
||
|
\ get strip_plus request
|
||
|
scan-in DUP 0 > IF ['] request $! THEN
|
||
|
request 3 MIN S" Yes" compare 0= TO strip-plus-req
|
||
|
|
||
|
\ get unescape request
|
||
|
scan-in DUP 0 > IF ['] request $! THEN
|
||
|
request 3 MIN S" Yes" compare 0= TO unesc-req
|
||
|
|
||
|
|
||
|
name plus->space
|
||
|
|
||
|
strip-plus-req IF
|
||
|
subject plus->space
|
||
|
comments plus->space
|
||
|
THEN
|
||
|
|
||
|
name unescape-url name DROP SWAP ['] name $!
|
||
|
|
||
|
unesc-req IF
|
||
|
subject unescape-url subject DROP SWAP ['] subject $!
|
||
|
comments unescape-url comments DROP SWAP ['] comments $!
|
||
|
THEN
|
||
|
|
||
|
|
||
|
\ need a name or e-mail
|
||
|
name $len 0= e-mail $len 0= AND TO bad-status
|
||
|
|
||
|
|
||
|
|
||
|
;
|
||
|
|
||
|
: report-field ( addr count handle -- )
|
||
|
|
||
|
OVER 0= IF SWAP DROP SWAP DROP S" (None) " ROT THEN
|
||
|
|
||
|
WRITE-FILE DROP
|
||
|
;
|
||
|
|
||
|
: report ( handle -- )
|
||
|
|
||
|
S" Subject: " 2 PICK WRITE-FILE DROP
|
||
|
subject 2 PICK report-field
|
||
|
DUP newline DROP
|
||
|
|
||
|
S" Comments: " 2 PICK WRITE-FILE DROP
|
||
|
DUP newline DROP
|
||
|
comments 2 PICK report-field
|
||
|
|
||
|
DUP newline DROP
|
||
|
DUP newline DROP
|
||
|
|
||
|
S" Name: " 2 PICK WRITE-FILE DROP
|
||
|
name 2 PICK report-field
|
||
|
|
||
|
DUP newline DROP
|
||
|
|
||
|
S" e-mail: " 2 PICK WRITE-FILE DROP
|
||
|
e-mail 2 PICK report-field
|
||
|
|
||
|
newline DROP
|
||
|
;
|
||
|
|
||
|
|
||
|
: sendmail ( handle -- handle )
|
||
|
|
||
|
|
||
|
DUP report
|
||
|
|
||
|
S" Sequence number: " 2 PICK WRITE-FILE DROP
|
||
|
seq-no itoa 2 PICK WRITE-LINE DROP
|
||
|
|
||
|
S" Received at " 2 PICK WRITE-FILE DROP
|
||
|
|
||
|
PAD 24 timestamp 2 PICK WRITE-FILE DROP
|
||
|
S" from the WWW page on: " 2 PICK WRITE-FILE DROP
|
||
|
HOSTNAME 2 PICK WRITE-LINE DROP
|
||
|
|
||
|
S" Program: " 2 PICK WRITE-FILE DROP
|
||
|
PROGRAM 2 PICK WRITE-LINE DROP
|
||
|
|
||
|
DUP newline DROP
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
;
|
||
|
|
||
|
: mail_fig ( -- )
|
||
|
|
||
|
init-strings
|
||
|
|
||
|
\ fix the mailer string
|
||
|
MAILER unescape-url MAILER DROP SWAP ['] MAILER $!
|
||
|
|
||
|
MAILER DESTINATION $cat ['] MAILER $!
|
||
|
|
||
|
LOGFILE OPEN-APPEND ABORT" Unable to open log file "
|
||
|
TO log-file
|
||
|
|
||
|
update_sequence_number DUP TO seq-no
|
||
|
|
||
|
|
||
|
PAD 24 timestamp log-file WRITE-FILE DROP
|
||
|
|
||
|
S" Sequence number is: " log-file WRITE-FILE DROP
|
||
|
itoa log-file WRITE-FILE DROP
|
||
|
|
||
|
log-file newline DROP
|
||
|
|
||
|
write-env
|
||
|
|
||
|
?DEBUG IF
|
||
|
S" CONTENT LENGTH = " log-file WRITE-FILE DROP
|
||
|
DUP itoa log-file WRITE-FILE DROP
|
||
|
THEN
|
||
|
|
||
|
|
||
|
\ allocate space for the buffer
|
||
|
HERE TO input-buffer
|
||
|
DUP 2 + DUP TO buf-len ALLOT
|
||
|
|
||
|
\ now read characters from the input stream
|
||
|
input-buffer SWAP get-input-data
|
||
|
|
||
|
|
||
|
?DEBUG IF
|
||
|
S" BUF-LEN = " log-file WRITE-FILE DROP
|
||
|
buf-len itoa log-file WRITE-FILE DROP
|
||
|
S" status = " log-file WRITE-FILE DROP
|
||
|
bad-status itoa log-file WRITE-FILE DROP
|
||
|
log-file newline DROP
|
||
|
THEN
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
?DEBUG IF
|
||
|
input-buffer buf-len log-file WRITE-FILE DROP
|
||
|
log-file newline DROP
|
||
|
THEN
|
||
|
|
||
|
scan-input-data
|
||
|
|
||
|
|
||
|
log-file report
|
||
|
|
||
|
bad-status IF nack
|
||
|
ELSE
|
||
|
\ open the mail pipe
|
||
|
cc-req IF
|
||
|
MAILER e-mail $cat ['] MAILER $!
|
||
|
THEN
|
||
|
|
||
|
?DEBUG IF
|
||
|
S" Mailer: " log-file WRITE-FILE DROP
|
||
|
MAILER log-file WRITE-FILE DROP
|
||
|
log-file newline DROP
|
||
|
THEN
|
||
|
|
||
|
\ ." Mailer command <" MAILER TYPE ." >" CR
|
||
|
|
||
|
MAILER W/O OPEN-PIPE
|
||
|
ABORT" Unable to open pipe to mailer "
|
||
|
|
||
|
sendmail
|
||
|
CLOSE-PIPE DROP
|
||
|
acknowledge
|
||
|
THEN
|
||
|
|
||
|
|
||
|
sig
|
||
|
|
||
|
log-file newline DROP
|
||
|
log-file CLOSE-FILE DROP
|
||
|
|
||
|
;
|
||
|
|
||
|
\ auto-startup word
|
||
|
|
||
|
: startup mail_fig bye ;
|
||
|
|
||
|
PFE [IF]
|
||
|
startup
|
||
|
[THEN]
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|