_27> ? TERMIN ! TERMIN
+
+DEFINE SSX &%%TXT& ; Send String
+ %%LEN==<.LENGTH %%TXT>
+ IFE %%LEN, JFCL ?.STOP
+ IFLE %%LEN-2, CSO %%LEN,(ASCII %%TXT) ?.STOP
+ IFLE %%LEN-17, CSO %%LEN,[ASCII %%TXT] ?.STOP
+.ELSE, CSO [%%LEN,,[ASCII %%TXT]] ?TERMIN
+
+DEFINE SLX &%%TXT& ; Send Line
+ %%LEN==<<.LENGTH %%TXT>+2>
+ IFE %%LEN-2, CSO 2,<^M_13>\<^J_4> ?.STOP
+ IFLE %%LEN-17, CSO %%LEN,[$ASC ,%%TXT,$CRLF] ?.STOP
+ .ELSE, CSO [%%LEN,,[$ASC ,%%TXT,$CRLF]] ?TERMIN
+
+DEFINE SLSX &%%TXT& ; Send Linefeed and String
+ %%LEN==<<.LENGTH %%TXT>+2>
+ IFE %%LEN-2, CSO 2,<^M_13>\<^J_4> ?.STOP
+ IFLE %%LEN-17, CSO %%LEN,[$ASC $CRLF,%%TXT,] ?.STOP
+ .ELSE, CSO [%%LEN,,[$ASC $CRLF,%%TXT,]] ?TERMIN
+
+DEFINE SLLX &%%TXT& ; Send Line Doublespaced
+ %%LEN==<<.LENGTH %%TXT>+4>
+ IFLE %%LEN-17, CSO %%LEN,[$ASC ,%%TXT,$$CRLF] ?.STOP
+ .ELSE, CSO [%%LEN,,[$ASC ,%%TXT,$$CRLF]] ?TERMIN
+
+
+DEATH: 0
+ SKIPE DEBUG
+ .VALUE
+ .LOGOUT
+ JRST DEATH+1 ; For Justin
+
+LOSE: 0 ; Tell loser what went wrong
+ JRST ERR500
+
+
+POPJS: AOS 0(P) ; Success return here
+POPJF: POPJ P, ; Error return here
+
+UUOS: AOS U.DISP ; UUO success return here
+UUOF: JRST 2,@U.DISP ; UUO error return here
+
+U.TMP: 0 ; UUO scratch
+
+
+VAR FILDEV ; From URL
+VAR FILFN1 ; From URL
+VAR FILFN2 ; From URL
+VAR FILDIR ; From URL
+
+VAR DEV ; For opening the file
+VAR FN1 ; For opening the file
+VAR FN2 ; For opening the file
+VAR DIR ; For opening the file
+
+VAR HEADER ; Set by FILOPN; XCT to begin sending data
+VAR SENDER ; Set by FILOPN; XCT to send data
+VAR TAILER ; Set by FILOPN; XCT to end sending data
+VAR FL.OPN ; Set when FILI open
+
+IPADDR: 0 ; Client IP address (permanent)
+
+NAMBUF: BLOCK 6 ; Expand DEV:DIR;FN1 FN2 here
+
+BUFLEN: 0 ; Number of characters read by READLN
+BUFFER: ; I/O scratch until end of page
+BUFSIZ==<2000-.>*5 ; Size in bytes
+IFG BUFSIZ-400,BUFSIZ==400 ; Still seeing a tendency to crash ITS
+
+DEFINE PRINT% (#X#)
+ PRINTX "X"
+TERMIN
+IF2,{
+ PRINT% BUFSIZ
+ PRINTX " bytes I/O Buffer available
+"}
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+LOC 2000 ; Pure code only from here on
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+GO: MOVE P,[-PDLSIZ,,PDL] ; Set up call stack
+ MOVE ARGP,[-ARGSIZ,,ARGS] ; Set up data stack
+ .SUSET [.SMASK,,[%PIIOC]] ; Handle IOC interrupts
+ PUSHJ P,NETOPN ; Open network sockets
+
+AGAIN: MOVE IX,[-VARSIZ,,VARS] ; All variables
+CLR:: SETZM 0(IX) ; Set to zero
+ AOBJN IX,CLR ; Next
+
+ READLN NETI,BUFFER ; Get command line
+ JSR LOSE ; or bitch
+
+ PUSHJ P,WRTLOG ; Memories are made of this
+
+ MOVE BUFFER
+ CAME [ASCII "GET /"] ; The only command we know
+ JSR LOSE
+
+ PUSHJ P,PARSE ; Get DEV: DIR; FN1 FN1
+ JSR LOSE ; or screw it
+ PUSHJ P,FILOPN ; Open and recognize
+
+ PUSHJ P,@HEADER ; Send file format header
+ PUSHJ P,@SENDER ; then copy all data
+ PUSHJ P,@TAILER ; then send the file format trailer
+
+ ; NYI: keep link open for further requests (need HTTP version)
+DONE: .CLOSE FILI, ; Clean up.
+ .NETS NETO, ; Force the output.
+ .CLOSE NETO, ; Disconnect.
+ .CLOSE NETI, ; Disconnect.
+ .LOGOUT ; Buh-bye now
+ JSR DEATH ; Give up already
+
+
+REG TRIES ; Remaining retries
+REG WAIT ; Time to sleep between tries
+REG STAT ; Connection status
+
+ ; Open network sockets NETI and NETO
+ ; Never skips; dies if unsuccessful
+NETOPN: .CALL [ SETZ ; Open network sockets
+ SIXBIT /TCPOPN/
+ %CLIMM,,NETI
+ %CLIMM,,NETO
+ %CLIMM,,PORT
+ %CLIN,,[-1]
+ %CLIN,,[-1] ((SETZ))]
+ JSR DEATH ; or die.
+ MOVEI TRIES,52 ; About half a minute
+ SETZ WAIT, ; Start quickly,
+NET1:: AOS WAIT ; but back off each time
+ MOVE WAIT ; wait
+ .SLEEP ; a little
+ .CALL [ SETZ
+ SIXBIT /WHYINT/
+ %CLIMM,,NETO
+ %CLOUT,,
+ %CLOUT,,STAT ((SETZ))]
+ .LOSE %LSSYS ; Shouldn't happen
+ CAIE STAT,%NSOPN ; If the connection is open
+ CAIN STAT,%NSRFN ; or RFNM wait on write link
+ CAIA ; then don't
+ SOJG TRIES,NET1 ; keep waiting
+ CAIG TRIES, ; If timed out
+ JSR DEATH ; then die
+ .CALL [ SETZ ; Get user IP address
+ SIXBIT /RFNAME/
+ %CLIMM,,NETI
+ %CLOUT,,
+ %CLOUT,,
+ %CLOUT,,
+ %CLOUT,,IPADDR ((SETZ))]
+ .LOSE %LSSYS ; Shouldn't happen
+ POPJ P, ; All set
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; We recognize files by array lookup, finding the three pieces that will
+;; be used to send the file. Only the non-zero pieces in a matching entry
+;; are used, and only if not already set; we'll keep looking until all
+;; three are set. The lookup structure has five parts:
+;;
+;; LH(0) Goes into (HEADER), XCT for sending the file header and prefix
+;; LH(1) Goes into (SENDER), XCT for sending the file data
+;; LH(2) Goes into (TAILER), XCT for ending the sending of the file
+;;
+;; RH(0) [FN1 ? FN2], or first 3 chars of DEV
+;; RJ(1) XCT recognizer, skips unless matching
+;; RH(2) Reserved for future use
+;;
+
+LOOKUP: HDRHTM ,,[SIXBIT /.FILE.(DIR)/]
+ SNDDIR ,,
+ ENDHTM ,,
+
+ HDRHTM ,,'DIR
+ SNDDIR ,,
+ ENDHTM ,,
+
+ HDRHTM ,,[SIXBIT /M.F.D.(FILE)/]
+ SNDMFD ,,
+ ENDHTM ,,
+
+ HDRHTT ,,
+ SNDTXT ,,IS.HTM
+ POPJF ,,
+
+ HDRTXT ,,
+ SNDTXT ,,
+ POPJF ,,
+
+ ;; NYI: binary files
+
+N.LOOK==<.-LOOKUP>/3
+
+
+
+REG X,3 ; Halfword(s) under investigation
+REG OPN ; From FL.OPN
+
+ ; Match the open file with the lookup row addressed by ARGV
+ ; If matching, set any unset header/sender/trailer fields
+ ; Skip if any fields remain unset after copying
+LOOK: MOVE OPN,FL.OPN ; Grab the file open flag
+ HRRZ X,0(ARGV) ; Get the file names
+ JUMPE X,LOOKC ; Zero matches anything
+ TRNE X,770000 ; If it has high bits set
+ JRST [ HLRZ DEV ; then it must be a device
+ CAIE X ; If doesn't match
+ POPJ P, ; then lose
+ JRST LOOKC] ; else keep looking
+ MOVE 0(X) ; If the first filename
+ JUMPE LOOK2 ; is zero, then it matches anything
+ CAME FN1 ; if it doesn't match
+ POPJ P, ; then lose
+LOOK2:: MOVE 1(X) ; If the second filename
+ JUMPE LOOKC ; is zero, then it matches anything
+ CAME FN2 ; if it doesn't match
+ POPJ P, ; then lose
+LOOKC:: HRRZ X,1(ARGV) ; Get the recognizer
+ JUMPE X,LOOKZ ; Zero matches anything
+ CAIE OPN, ; If no file open
+ PUSHJ P,@X ; or it doesn't recognize the file
+ POPJ P, ; then lose
+LOOKZ:: HLRZ X+0,0(ARGV) ; Get the header function
+ HLRZ X+1,1(ARGV) ; Get the sender function
+ HLRZ X+2,2(ARGV) ; Get the trailer function
+ MVDEF 3,[HEADER,,X] ; Copy those that are unset
+ POPJ P, ; lose if any still unset
+ JRST POPJS ; Win
+
+
+REG C ; Character to check
+REG REWIND ; 0 to seek to
+
+ ; Skip if the open file looks like it contains HTML
+IS.HTM: .IOT FILI,C ; Look at the first char
+ SETZ REWIND,
+ .ACCESS FILI,REWIND ; Rewind the file
+ CAIN C,"< ; If it looks like
+ AOS 0(P) ; then win
+ POPJ P, ; Done
+
+
+ ; Figure out what type of file we're dealing with
+ ; Sets HEADER/SENDER/TAILER and FL.OPEN
+ ; Never skips
+FILOPN: MVDEF 4,[DEV,,FILDEV] ; Use URL
+ MVDEF 4,[DEV,,DEFDEV] ; if not all set, use defaults
+ JSR LOSE ; which should all be set
+
+ SKIPE FILFN1 ; If FN1 in the URL
+ PUSHJ P,TRYOPN ; then try the file we were given
+ SKIPN FL.OPN ; If not open yet
+ PUSHJ P,DEFOPN ; then try a bunch of defaults
+
+ PUSH P,IX ; Save
+ MOVE IX,[-N.LOOK,,LOOKUP] ; Loop over the lookup structure
+OPENL:: HRRZ ARGV,IX ; Get the row address
+ ADDI IX,2 ; Three words per item
+ PUSHJ P,LOOK ; Check for a match
+ AOBJN IX,OPENL ; or keep looking
+ MOVE IX ; Remember the result
+ POP P,IX ; Restore
+ JUMPN POPJF ; If we found a match, we're done
+ JSR LOSE ; Shouldn't happen
+
+
+ ; Try to open the specified file,
+ ; set FL.OPEN or skip if unsuccessful (yes, backwards!)
+TRYOPN: MOVE FN1 ; If the filename
+ CAME [SIXBIT /..NEW./] ; doesn't look scary
+ .CALL [SETZ ; then open the target file
+ SIXBIT /OPEN/
+ %CLBIT,,.UAI
+ %CLIMM,,FILI
+ %CLIN,,DEV
+ %CLIN,,FN1
+ %CLIN,,FN2
+ %CLIN,,DIR ((SETZ))]
+ JRST POPJS ; or fail
+ SETOM FL.OPN ; We now have a file to look at
+ POPJ P, ; Win
+
+
+ ; Try opening various defaults
+ ; Never skips
+DEFOPN: PUSHJ P,TRYOPN ; Open the default
+ POPJ P, ; and win
+ SKIPE FILFN1 ; If the loser gave us a filename
+ PUSHJ P,SET404 ; then we'll have to disappoint him
+
+ MVM 2,[FN1,,[SIXBIT /.FILE.(DIR)/]] ; Try listing the dir
+ PUSHJ P,TRYOPN ; Open it
+ POPJ P, ; and win
+ SKIPE FILDIR ; If the loser gave us a directory
+ PUSHJ P,SET404 ; then we'll have to disappoint him
+
+ MVM 2,[FN1,,[SIXBIT /M.F.D.(FILE)/]] ; Try the master file directory
+ PUSHJ P,TRYOPN ; Open it
+ POPJ P, ; and win
+ PUSHJ P,SET404 ; This is very disappointing
+
+ MOVSI 'DSK ; Try the DSK: MFD
+ CAIN DEV ; Unless if we already did
+ JRST OPNZ ; then no point trying again
+ MOVEM DEV
+ PUSHJ P,TRYOPN ; This is our last hope
+ POPJ P, ; and win
+OPNZ: MOVEI POPJF ; Can't send any file
+ MOVEM SENDER ; so use null sender function
+ POPJ P, ; Give up
+
+
+ ; Flag that we need to send the dreaded 404
+SET404: MOVEI HDR404
+ MOVEM HEADER
+ MOVEI ENDHTM
+ MOVEM TAILER
+ POPJ P,
+
+
+REG FN,2 ; FNn
+TT==FN+1 ; Terminator
+REG RP ; Read pointer
+REG WP ; Write pointer
+REG C ; Character
+REG I ; FNn counter
+
+ ; Get DEV: DIR; FN1 FN2
+ ; Skips if something sensible could be parsed
+PARSE: ; First, pass by the command and slash
+ MOVE RP,[ASCBP,,BUFFER] ; Read from input buffer
+ MOVE WP,RP ; and write there too
+PAR1:: ILDB C,RP ; Look at next char
+ JUMPE C,POPJF ; Bail at end of string
+ CAIE C,"/ ; If it's not a slash
+ JRST PAR1 ; then keep looking
+
+ ; Then, take out %xx and trailing http gubbage
+PARX:: ILDB C,RP ; Get next char
+ CAIN C,"% ; If it's a hex escape
+ JRST [ SETZ C, ; clear to receive
+ GETHEX C,RP ; first hex digit
+ GETHEX C,RP ; second hex digit
+ JRST PAR2] ; skip looking for HTTP/
+ CAIN C,SPACE ; If it's a space
+ SMATCH RP,[ASCIZ "HTTP/?.?"] ; and there's a trailing HTTP/x.y
+ CAIA ; then
+ SETZ C, ; cut it off
+PAR2:: IDPB C,WP ; Write char back
+ JUMPN C,PARX ; continue until NUL
+
+ ; Finally, pick out the file name parts
+ MOVE RP,[ASCBP,,BUFFER] ; Read from start of input again
+ MOVSI I,-4 ; Count FN1 FN2 DIR
+PARN:: GETFN FN,RP ; Get next name
+ POPJ P, ; or bail
+ JUMPE FN,POPJS ; If no more, then win
+ CAIN TT,"; ; If it's a semicolon
+ JRST [ MOVEM FN,FILDIR ; then it's a directory
+ JRST PARN] ; Next
+ CAIN TT,": ; If it's a colon
+ JRST [ MOVEM FN,FILDEV ; then it's a device
+ JRST PARN] ; Next
+ AOBJP I,POPJF ; next FNn or fail if too many
+ MOVEM FN,FILDEV(I) ; Save it
+ JUMPE FN,POPJS ; Win if terminated by NUL
+ JRST PARN ; Next
+
+
+REG TYP ; Html or text
+
+ ; Send success HTTP header for html or text
+ ; Never skips
+HDRHTT: MOVEI TYP,[ASCIZ "text/html"]
+ CAIA
+HDRTXT: MOVEI TYP,[ASCIZ "text/plain"]
+ PUSH ARGP,TYP
+ MOVEI ARGV,[ASCIZ "200 ask and thou shalt receive"]
+ ;;Fall thru to SNDHDR ; HTTP header is all we need
+
+ ; Send appropriate HTTP header, using
+ ; response message ASCIZ ptr in ARGV, MIME type ASCIZ ptr in 0(ARGP)
+ ; Never skips
+SNDHDR: SSX "HTTP/1.0 "
+ ZSO 0(ARGV)
+ SLSX "Content-Type: "
+ POP ARGP,TYP
+ ZSO 0(TYP) ; Mime type
+ SLLX "" ; Double CRLF
+ POPJ P,
+
+
+ ; Begin an html message
+ ; Never skips
+HDRHTM: PUSHJ P,HDRHTT
+ SSX ""
+ PUSHJ P,NAMPRN
+ ZSOQ NAMBUF
+ SLX ""
+ POPJ P,
+
+
+ ; End an html message
+ ; Never skips
+ENDHTM: SLX ""
+ POPJ P,
+
+
+ ; Tell the loser it's not there
+ ; Include directory listing, if available
+ ; Never skips
+HDR404: MOVEI ARGV,[ASCIZ "404 no such file or directory"]
+ MOVEI [ASCIZ "text/html"]
+ PUSH ARGP,
+ PUSHJ P,SNDHDR ; HTTP header
+ SLX ""
+ SLX "404 - Page not found"
+ SLX ""
+ SSX "Unable to retrieve "
+ PUSHJ P,NAMPRN ; Expand the file name
+ ZSOQ NAMBUF ; and send it out
+ SLX "
"
+ SLX "The web site you seek
"
+ SLX "cannot be located but
"
+ SLX "endless others exist
"
+ POPJ P,
+
+
+ ; Sorry bub, you're screwed
+ ; Never returns
+ERR500: SLLX "HTTP/1.0 500 Sorry ERRROR"
+ SKIPE DEBUG
+ .VALUE ; Stop and have a look at it
+ JRST DONE ; Can't trust the stack, just bail
+
+
+VAR DIRFN1 ; Point at FN1
+VAR DIRFN2 ; Point at FN2
+VAR TAIL ; Point at remainder
+
+ ; Copy directory listing from FILI to NETO,
+ ; Making file names clickable html
+ ; Never skips
+SNDDIR: PUSH P,IX ; Save for use as header index
+ MOVEI IX,2 ; Start with the header
+DIRR:: READLN FILI,BUFFER ; Read a line
+ JRST [ SSX "" ; If we saw EOF,
+ POP P,IX ; restore
+ POPJ P,] ; and we're done
+ MOVE BUFLEN ; Check the length
+ CAIG 2 ; If it's too short
+ JRST DIRL ; it's gubbage
+ LDB [NTH BUFFER,1] ; Fetch the first char
+ CAIE SPACE ; If it's not space
+ JRST [ XCT [ JFCL ; \
+ SSX "" ; Send appropriate header
+ SSX ""](IX) ; /
+ ZSOQ BUFFER ; Send the line
+ XCT [ CAIA ; \
+ SLX "
" ; Close the header
+ SSX ""](IX); /
+ SOS IX ; Select the next header, unless 0
+ JRST DIRL] ; And we're done
+ MOVE [NTH BUFFER,5] ; Just before FN1
+ MOVEM DIRFN1 ; hold on to that
+ CSTS DIRFN1 ; Cut and check for space
+ JRST DIRR ; if so, skip it
+ MOVE [NTH BUFFER,14] ; Just before FN2
+ MOVEM DIRFN2 ; hold on to that
+ CSTS DIRFN2 ; Cut and check for space
+ JRST DIRR ; if so, skip it
+ MOVE [NTH BUFFER,23] ; Just after FN2
+ MOVEM TAIL ; That's where the end begins
+ SETZ ; Grab a NUL
+ IDPB TAIL ; Chop, hold on to the pointer
+ ZSOQ BUFFER ; Send first part
+ SSX " " ; followed by a space
+ SETZM FILFN2 ; No FN2 yet
+ PUSH ARGP,DIRFN1 ; Where to get FN1
+ MOVEI ARGV,FILFN1 ; Where to put FN1
+ PUSHJ P,SNDREF ; Send clickable FN1
+ PUSH ARGP,DIRFN2 ; Where to get FN2
+ MOVEI ARGV,FILFN2 ; Where to put FN2
+ PUSHJ P,SNDREF ; Send clickable FN2
+ ZSOQP TAIL ; Send the rest of the line
+DIRL:: SLX "" ; Followed by a CRLF
+ JRST DIRR ; Next line
+
+
+
+ ; Copy MFD listing from FILI to NETO
+ ; Never skips
+ ; NYI: nicer layout
+SNDMFD: PUSH P,IX ; Save
+ PUSHJ P,NAMPRN ; Format the name
+ SSX "SV " ; NYI: look up machine name
+ ZSOQ NAMBUF ; So much trouble for this header
+ SSX "
" ; Close header
+ SETZM FILFN1 ; Don't put FN1 in the listing
+ SETZM FILFN2 ; Don't put FN2 in the listing
+MFDR:: READLN FILI,BUFFER ; Read one directory name
+ JRST [ POP P,IX ; or, restore
+ POPJ P,] ; All done
+ MOVE IX,[ASCBP,,BUFFER] ; Point to it
+ ILDB IX ; If the first char
+ CAIE SPACE ; isn't space
+ JRST MFDR ; then skip the line
+ PUSH ARGP,IX ; Where to get DIR
+ MOVEI ARGV,FILDIR ; Where to put DIR
+ PUSHJ P,SNDREF ; Send clickable DIR
+ SLX "" ; Followed by a CRLF
+ JRST MFDR ; Next
+
+
+VAR DIRP ; BP to ASCIZ FNn
+REG FN,2 ; SIXBIT FN, and terminator
+REG FNP ; Address of where to store FNn
+
+ ; GET FNn from BP from ARGP stack, store at address in (ARGV)
+ ; Send clickable reference to NETO
+ ; Never skips
+SNDREF: POP ARGP,FNP ; Where to put FNn
+ MOVEM FNP,DIRP ; We'll need this later
+ GETFN FN,FNP ; Scan the name
+ SETZ FN, ; Drats,
+ JUMPE FN,[ ; it's gubbage
+ ZSOQP DIRP ; Send it plain
+ SSX " " ; and a space
+ POPJ P,] ; Done
+ MOVEM FN,0(ARGV) ; Save the FNn
+ SSX '' ; Close the tag
+ ZSOQP DIRP ; Send the name to click on
+ SSX " " ; Close the tag
+ POPJ P, ; All done
+
+
+REG T,2 ; Name pointer
+TT==T+1 ; Terminator character
+
+ ; Write DEV: DIR; FN1 FN2 to NAMBUF
+ ; Never skips
+NAMPRN: MOVE T,[ASCBP,,NAMBUF] ; Scratch here
+ MOVEI TT,":
+ SKIPE FILDEV ; If there's a DEV
+ PUTFN T,FILDEV ; then write DEV:
+ MOVEI TT,";
+ SKIPE FILDIR ; If there's a DIR
+ PUTFN T,FILDIR ; then write DIR;
+ SKIPE TT,FILFN2 ; If there's an FN2
+ MOVEI TT,SPACE ; then separate by space
+ SKIPE FILFN1 ; If there's an FN1
+ PUTFN T,FILFN1 ; then write FN1 (and space)
+ SETZ TT,
+ SKIPE FILFN2 ; If there's an FN2
+ PUTFN T,FILFN2 ; then write FN2, NUL
+NAM2:: SETZ ; Always NUL-
+ IDPB T ; terminate
+ POPJ P, ; Done
+
+
+REG LEN ; Remaining bytes to be copied
+REG CNT ; Number of bytes per block
+REG BP ; Pointer to data to send
+REG NB ; Counter of bytes to send per call
+
+ ; Copy file from FILI to NETO
+ ; Never skips
+ ; NYI: this might hang if file is truncated while we're sending it
+SNDTXT: .CALL [ SETZ ; Get file length
+ SIXBIT /FILLEN/
+ %CLIMM,,FILI
+ %CLOUT,,LEN ((SETZ))]
+ JSR DEATH ; Shouldn't happen
+ JUMPLE LEN,POPJF ; Bail if the file is empty
+TXT1:: MOVE CNT,LEN ; Try it all
+ CAILE CNT,BUFSIZ ; Unless if it's too much
+ MOVEI CNT,BUFSIZ ; then just some
+ MOVE BP,[ASCBP,,BUFFER] ; Point to I/O buffer
+ MOVE NB,CNT ; Read this many
+TXT2:: .CALL [ SETZ ; from the file
+ SIXBIT /SIOT/
+ %CLIMM,,FILI
+ %CLIN,,BP
+ %CLIN,,NB ((SETZ))]
+ JSR DEATH ; or lose
+ JUMPG NB,TXT2 ; Keep filling until full
+ MOVE BP,[ASCBP,,BUFFER] ; Point to I/O buffer
+ MOVE NB,CNT ; Send this many
+TXT3:: .CALL [ SETZ ; to the net
+ SIXBIT /SIOT/
+ %CLIMM,,NETO
+ %CLIN,,BP
+ %CLIN,,NB ((SETZ))]
+ JSR DEATH ; or lose
+ JUMPG NB,TXT3 ; Keep pushing until all out
+ SUB LEN,CNT ; Count it off
+ JUMPG LEN,TXT1 ; Try again if there's more
+ POPJ P, ; until done
+
+
+REG TRIES ; Number of retries left
+REG WAIT ; Sleep time after each try
+REG ERR ; file open error code
+REG BUFP ; Command buffer pointer
+REG LEN ; File length
+
+ ; Append command string to log file
+ ; Never skip
+WRTLOG: MOVEI TRIES,52 ; About half a minute
+ SETZ WAIT, ; Start quickly
+ AOS WAIT ; then back off
+LOG1:: .CALL [ SETZ ; Open the log file
+ SIXBIT /OPEN/
+ %CLBIT,,.UAO\%DOWOV ; Overwrite
+ %CLIMM,,LOGO
+ %CLIN,,[SIXBIT /DSK/]
+ %CLIN,,[SIXBIT /ACCESS/]
+ %CLIN,,[SIXBIT /LOG/]
+ %CLIN,,[SIXBIT /.WWW./]
+ %CLERR,,ERR ((SETZ))]
+ JRST [ CAIE ERR,%ENAFL ; If there was an error (not lock)
+ POPJ P, ; sorry, no log
+ MOVE WAIT ; wait
+ .SLEEP ; a little
+ SOJGE TRIES,LOG1 ; then try again
+ POPJ P,] ; Timeout -- sorry, no log
+ .CALL [ SETZ ; Get file length
+ SIXBIT /FILLEN/
+ %CLIMM,,LOGO
+ %CLOUT,,LEN ((SETZ))]
+ .LOSE %LSFIL ; Shouldn't happen
+ .ACCESS LOGO,LEN ; Seek to EOF
+
+ PUSHJ P,LOGTIM ; Log current time
+ PUSHJ P,LOGADR ; and the address of the caller
+LOG2:: MOVE BUFP,[ASCBP,,BUFFER] ; Point at command string
+LOG3:: .CALL [ SETZ
+ SIXBIT /SIOT/
+ %CLIMM,,LOGO
+ %CLIN,,BUFP
+ %CLIN,,BUFLEN ((SETZ))]
+ .IOT LOGO,["?] ; oh, blah
+
+LOGZ:: .IOT LOGO,[^M] ; CR
+ .IOT LOGO,[^J] ; LF
+ .CLOSE LOGO,
+ POPJ P, ; All done
+
+
+REG DTM,2 ; Date and time
+REG DTP ; Byte pointer to date and time
+REG SP ; Byte pointer to separators
+REG SEP ; Separator character
+REG C ; Datetime character
+
+ ; Write timestamp to LOG, as 'YYYY-MM-DD hh:mm:ss '
+ ; Never skips
+LOGTIM: .IOT LOGO,["2] ; Hardcode the century
+ .IOT LOGO,["0] ; this will have to change eventually
+ .RDATIM DTM, ; get date and time
+ EXCH DTM,DTM+1 ; We want the date first
+ MOVE DTP,[SIXBP,,DTM] ; point to the date
+ MOVE SP,[ASCBP,,[ASCIZ "-- :: "]] ; point to the separators
+TIMT:: ILDB SEP,SP ; Get a separator
+ JUMPE SEP,POPJF ; If it's NUL, the timestamp is done
+ ILDB C,DTP ; Get first digit
+ ADDI C,40 ; Convert to sixbit
+ .IOT LOGO,C ; Write it out
+ ILDB C,DTP ; Get next digit
+ ADDI C,40 ; Convert to sixbit
+ .IOT LOGO,C ; Write it out
+ .IOT LOGO,SEP ; Write the separator
+ JRST TIMT ; Next date part
+
+
+REG N1,4 ; Hundreds digit
+N2==N1+1 ; Tens digit
+N3==N2+1 ; Ones digit, reused below
+IP==N3+1 ; IP address
+C==IP-1 ; Shift digits from IP
+REG WIDTH ; Remaining field width
+REG IPP ; Byte ptr to IP# parts
+REG I ; Loop counter
+
+ ; Write IPADDR to LOG, nicely formatted and padded to fix size
+ ; Never skips
+LOGADR: MOVEI WIDTH,16. ; Print fix width
+ MOVE IP,IPADDR ; Get the address
+ JUMPE IP,ADRS ; If it's 0, then leave it blank
+ TLNE IP,740000 ; If it's more than 32 bits
+ JRST ADR8 ; then it's not an IP#
+ MOVE IPP,[401000,,IPADDR] ; Point to address parts
+ MOVEI I,4 ; Parts count
+ADRP:: ILDB N2,IPP ; Get next address part
+ IDIVI N2,10. ; Last digit in N3
+ MOVE N1,N2 ; Make room
+ IDIVI N1,10. ; Digits now in N1-N3
+ JUMPE N1,ADR2 ; Unless the first digit is a zero
+ ADDI N1,"0 ; convert to ascii
+ .IOT LOGO,N1 ; write it to the log
+ SOS WIDTH ; and count it
+ CAIA ; and keep writing
+ADR2:: JUMPE N2,ADR1 ; Unless the second digit is a zero
+ ADDI N2,"0 ; convert to ascii
+ .IOT LOGO,N2 ; write it
+ SOS WIDTH ; and count it
+ADR1:: ADDI N3,"0 ; Convert the last digit to ascii
+ .IOT LOGO,N3 ; write it
+ SOS WIDTH ; and count it
+ SOJE I,ADRS ; Unless it's the last part
+ .IOT LOGO,[".] ; write a separator
+ SOJG WIDTH,ADRP ; count, and next
+ JSR DEATH ; Shouldn't happen
+
+ADR8:: MOVEI I,12. ; 12 octal digits in 36 bits
+ SUB WIDTH,I ; takes up so much space
+ADRN:: SETZ C, ; Clear to receive
+ LSHC C,3 ; one digit from IP
+ ADDI C,"0 ; Convert to ASCII
+ .IOT LOGO,C ; and write to log
+ SOJG I,ADRN ; Next digit
+ADRS:: .IOT LOGO,[SPACE] ; Pad with spaces
+ SOJG WIDTH,ADRS ; until field width reached
+ POPJ P, ; All done
+
+CONST: ; Expand literals here
+CONSTANTS
+
+
+IFN $$TEST,{ ; Just Testing
+
+LOC 4000 ; Next page
+
+REG BP,2
+TT==BP+1
+REG FN
+
+ ; Exercise some UUOs
+UUOTST: SETOM DEBUG
+ .OPEN NETO,[.UAO,,'TTY]
+ .LOSE %LSFIL
+ IRP SSS,,[SSX,SLX,SLSX,SLLX]
+ ZSO [ASCIZ "Testing SSS"]
+ SLSX ""
+ SSS ""
+ SSS "A"
+ SSS "BC"
+ SSS "DEFGHI"
+ SLX ""
+ TERMIN
+
+ SLSX "!"
+ SETZM BUFFER+1
+ MOVEI TT,"!
+ MOVE BP,[ASCBP,,BUFFER]
+ PUTFN BP,[SIXBIT /PUTFN/]
+ ZSO BUFFER
+
+ SLSX "!"
+ SETZM BUFFER+1
+ MOVE BP,[ASCBP,,[ASCIZ "GETFN"]]
+ GETFN FN,BP
+ CAIN TT,
+ MOVEI TT,"!
+ MOVE BP,[ASCBP,,BUFFER]
+ PUTFN BP,FN
+ ZSO BUFFER
+
+ SLX ""
+ MOVE BP,[ASCBP,,[ASCIZ "FROBOZZ"]]
+ IRP PATTERN,,["FROBO","FROBOZZ","FROBOZZNIK","FROBOLL","FROBO??"]
+ SSX PATTERN
+ SMATCH BP,[ASCIZ PATTERN]
+ JRST [SLX " LOSES"
+ JRST .+2]
+ SLX " WINS"
+ TERMIN
+
+ SLLX ""
+ ZSOBP [ASCBP,,[ASCIZ /(<&> %")/]]
+ SLLX ""
+ SLLX "That's all, folks!"
+ .VALUE
+ JRST UUOTST
+
+
+ ; Get command from TTY instead of net
+WWWTST: MOVE P,[-PDLSIZ,,PDL]
+ MOVE ARGP,[-ARGSIZ,,ARGS]
+ SETOM DEBUG
+ .OPEN NETI,[.UAI,,'TTY]
+ .LOSE %LSFIL
+ .OPEN NETO,[.UAO,,'TTY]
+ .LOSE %LSFIL
+ SSX "HTTP:="
+ JRST AGAIN
+
+T.CONS:
+}
+
+END GO