;SRCA:<BBOARD>BBOARD.MAC.93 6-Dec-85 12:53:07, Edit UT-3 by CAW
; Add changes from UTEXAS
; Add switch for no MM or PUSH - /SECURE. To prevent a user from
; getting below this program.
; THE FOLLOWING IS UTEXAS ONLY
; Make /MORE default, output blank lines before header
; Eliminate More? prompt in the middle of a message
; <GERGELY.EXP>BBOARD.MAC.91, 3-May-85 08:22:05, Edit by GERGELY
;Increase command line interpretation to 10 lines
;<SU-UTILITIES>BBOARD.MAC.72, 18-Dec-85 12:09:28, Edit by PIERRE
; Add the ability to remove PUSHing ability when PUSHSW==0
;<SU-UTILITIES>BBOARD.MAC.71, 1-Aug-85 16:18:12, Edit by FMF
; Make use POBOX: instead of PS:
; Note that SYSTEM:MAIL.TXT is replaced with POBOX:<SYSTEM>MAIL.TXT
;<SU-UTILITIES>BBOARD.MAC.70, 14-Jun-85 09:04:30, Edit by FMF
; Make sure "X" in More? really exits
;<SU-UTILITIES>BBOARD.MAC.69, 8-Jun-85 15:30:12, Edit by FMF
; Turn on output (undo ^O) before More?
;<SU-UTILITIES>BBOARD.MAC.68, 3-Jun-85 15:33:09, Edit by FMF
; X in More? exits all bboards
;<SU-UTILITIES>BBOARD.MAC.67, 31-May-85 14:09:46, Edit by FRANK
; Give help for all commands in "More?"
;<SU-UTILITIES>BBOARD.MAC.66, 28-May-85 13:10:30, Edit by FMF
; "^" synonym for "B"
; In MORPMT, "P" for push, "H" to redisplay header
;<SU-UTILITIES>BBOARD.MAC.65, 3-May-85 11:45:56, Edit by FMF
; From Gergely at DREA:
; More generous command line input
;<SU-UTILITIES>BBOARD.MAC.64, 2-May-85 10:59:02, Edit by FMF
; Make sure initial message about old bboards seen if default bboard
; Fix the way site compilation switches are done
; Define WHITSW to give lots of whitespace around and in headers
; From Gergely at DREA:
; WMSR uses ARMYSW
; Fix Datamedia 2500 clear line sequence
; Fondle counts to calls of MORCHK
; Clean out "More? " on a negative answer
;<SU-UTILITIES>BBOARD.MAC.63, 1-May-85 13:34:27, Edit by FMF
;<SU-UTILITIES>BBOARD.MAC.62, 1-May-85 13:23:44, Edit by FMF
; Change terminal type definitions to include just standard DEC types and
; define site-dependent types in site switches
; Change JHLT and JERR to DEFINEs from OPDEFs
; Changes from Gergely at DREA:
; Output name of the bboard in the error routine
; SMSGSW to treat system messages differently
; DREA wants a little extra space before the subject line
; From WSMR:
; SHDRSW to select "standard" headers
; Add WSMRSW
;<SU-UTILITIES>BBOARD.MAC.61, 30-Apr-85 09:35:02, Edit by FMF
; From Gergely at DREA:
; Fix error-checking in PRSMSG
;<SU-UTILITIES>BBOARD.MAC.60, 30-Apr-85 08:40:56, Edit by FMF
; ERJMP after the JFNS% in OPNDAT
;<SU-UTILITIES>BBOARD.MAC.59, 29-Apr-85 13:30:33, Edit by FMF
; Fix problem that SCROLL-MORE would set /LAST
;<SU-UTILITIES>BBOARD.MAC.58, 28-Apr-85 18:08:19, Edit by FMF
; Bugfixes from Gergely at DREA:
; Don't open the .IDX file until we really need to use it
; Fix where /CHECK reports whether there are bboards available
;<SU-UTILITIES>BBOARD.MAC.57, 24-Apr-85 14:40:07, Edit by FMF
; ^R is a synonym for H (repeat header)
;<SU-UTILITIES>BBOARD.MAC.56, 24-Apr-85 14:38:12, Edit by FMF
; LOTS and GSB have the same selections as other Stanford sites
;<SU-UTILITIES>BBOARD.MAC.55, 24-Apr-85 10:42:00, Edit by FMF
; Don't use AC16...it trashes the last read date
;<SU-UTILITIES>BBOARD.MAC.54, 23-Apr-85 16:55:53, Edit by FMF
; Clean up code
; Flush ancient edit history
; Don't print out extra CRLF before message header
;<SU-UTILITIES>BBOARD.MAC.52, 18-Apr-85 14:04:06, Edit by FMF
; Enable DREA edits for Sierra
; Make sure no leading tab if header on next line
; Allow DEL for non-confirmation in "More?"
;<GERGELY.EXP>BBOARD.MAC.100, 11-Apr-85 17:17:13, Edit by GERGELY
CODORG=1000 ;Beginning of CODE PSECT
DATORG=7000 ;Beginning of DATA PSECT
IFNDEF RUTSW,<RUTSW==0> ;Rutgers University
IFNDEF FAIRSW,<FAIRSW==0> ;Fairchild AI Lab
IFNDEF DREASW,<DREASW==0> ;Defense Research Establishment Atlantic
IFNDEF ARMYSW,<ARMYSW==0> ;WSMR
IFNDEF STANSW,<STANSW==0> ;Stanford University
IFNDEF UTEXSW,<UTEXSW==0> ;[UT-2] University o' Texas
;Compilation flags
IFNDEF FACSW,<
FACSW==0 ;Default to no /FACULTY or /CLASS
IFN RUTSW,<FACSW==1> ;Rutgers wants it
>;IFNDEF FACSW
IFNDEF OLDSW,<
OLDSW==1 ;Want old messages (beyond a month)
IFN DREASW!ARMYSW,<OLDSW==0> ;Those who don't want it
>;IFNDEF OLDSW
IFNDEF ENDMSW,<
ENDMSW==1 ;Most people want end message
IFN ARMYSW!STANSW,<ENDMSW==0> ;Doesn't want the end message
>;IFNDEF ENDMSW
IFNDEF NEWRSW,<
NEWRSW==1 ;Most people want new readers treated specially
IFN FAIRSW,<NEWRSW==0> ;Doesn't want them treated specially
>;IFNDEF NEWRSW
IFNDEF SMSGSW,<
SMSGSW==0 ;Most don't want special system message stuff
IFN DREASW,<SMSGSW==1> ;Those who want system messages differently
>;IFNDEF SMSGSW
IFNDEF WHITSW,<
WHITSW==0 ;Most people don't like lots of whitespace
IFN DREASW!UTEXSW,<WHITSW==1> ;But DREA and Texas want it
>;IFNDEF WHITSW
IFNDEF PUSHSW,<
PUSHSW==1 ;Most people want to be able to push
>;IFNDEF PUSHSW
IFE PUSHSW,<IF1,<PRINTX %Assembling without PUSH command>>
;Macro definitions
;Dynamically generate a flag. Argument is flag name, no argument initializes
DEFINE FLAG. (LABEL) <
IFB <LABEL>,<
..FLAG==1B0 ;;Start off with the leftmost bit
>;IFB
IFNB <LABEL>,<
IFE ..FLAG,<
IF2,<
PRINTX Too many flags, not creating LABEL
>;IF2
>;IFE
IFN ..FLAG,<
LABEL==..FLAG ;;Create the flag
..FLAG==..FLAG_<-1> ;;Shift to the right one bit
>;IFN
>;IFNB
>;FLAG.
;Define first word in table
DEFINE TABTOP <
TABLC==.
0 ;;We'll fill this in later
>;TABTOP
DEFINE MAKPTR (AC) <
TLC AC,-1 ;;Change -1 to 0 in left half
TLCN AC,-1 ;;Check if result is 0 and then change back
HRLI AC,(<POINT 7,0>) ;;Yes
>;MAKPTR
PG.RD==0 ;Offset for last read message
PG.WRT==1000 ;Offset for last write message
;Flag bit definitions
FLAG. ;Initialize flags macro
FLAG. F%NAM ;Type file name at start
FLAG. F%ACT ;Type file name if reading file
FLAG. F%PAUS ;Want to pause afterwards
FLAG. F%MSGB ;Some msgs were output for this bboard
FLAG. F%MSGS ;Some msgs were output for any bboard
FLAG. F%LAST ;He wants last message
FLAG. F%DEF ;It is the default bboard
FLAG. F%ERR ;Marks errors
FLAG. F%QUIT ;Says he wants to quit
FLAG. F%SYS ;Wants to see system messages
FLAG. F%1ST ;Have shown first message (Differs from F%MSGB
; when going to the last msg)
FLAG. F%CMA ;Have seen a comma in last thing
FLAG. F%CHK ;Add the /CHECK switch
FLAG. F%MORE ;Add the /MORE switch
FLAG. F%SCMR ;Add the /SCROLL-MORE switch
FLAG. F%EXIT ;Really wants to be done with all bboards
FLAG. F%SECU ;[UT-3] On to indicate no PUSH or MM
MMRPAG==20 ;Read data page in MM
MMWPAG==40 ;Write data page
BBNMAX==^D50 ;Maximum number of bboards
BAKMAX==^D100 ;How many back ups are allowed
INTCH1==1 ;Interrupt channel 1
IFE OLDSW,<
DAYAGO==^D30 ;Max old msg
>;IFE OLDSW
JBSA==120 ;Here is the word set by the loader
OPDEF SKPA [TRNA]
;Block definitions
PSECT DATA,DATORG
;Impure storage
PDLL==50 ;Max size of PDL
PDL: BLOCK PDLL
TTYPE: -1 ;Current terminal type
CURJFN: BLOCK 1 ;The current JFN
CURFIL: BLOCK 20 ;The current filename
FILNMS==0 ;Will count to BBNMAX
FILNAM:
REPEAT BBNMAX,<
FILNMS==FILNMS+^D8*4
EXP FILNMS+FILNAM ;Where FILNAM to be stored
>;REPEAT BBNMAX
REPEAT BBNMAX,< ;Enough space for all names
BLOCK ^D8*4 ;Space to save file specified by user
>;REPEAT BBNMAX
;Build device and directory from BBD: if exists, and default filenames
DEVICE: BLOCK 2
DIRECT: BLOCK 10
DEFDAT: BLOCK 10
DEFBBD: BLOCK 10
;Level table for ^C trap stuff
LEVTAB: PC1
0
0
PC1: BLOCK 1
;Channel table for ^C trap
CHNTAB: 0
1,,CTRLC ;Channel 1 is ^C
BLOCK ^D34 ;Rest of channels unused
;COMND% CSB
CMDBLN==:<^D80*10>/5+1 ;Room for six line command
ATMBLN==:CMDBLN
CMDBUF::BLOCK CMDBLN
CMDACS::BLOCK 20 ;Saved AC's from beginning of command line
ATMBUF::BLOCK ATMBLN ;Holds last parsed field
SBK:: BLOCK 20 ;COMND% state block
CJFNBK::BLOCK 20 ;GTJFN% block for COMND%
REPARA::0 ;Reparse address for COMND%
CMDFRM::0 ;Marks bottom of stack
CMDPDL::BLOCK 200 ;Room to save PDL
MESLN==30
VALUES:
ERMES: BLOCK MESLN
;Data for Fork handling/MM calling
RSCSIZ==^D150-1
ERRSIZ==^D100-1
FORK: BLOCK 1 ;Fork handle
FRKJFN: BLOCK 1 ;JFN on program being run
JOBNAM: BLOCK 1 ;Jobname (so we can restore it)
ERRFLG: BLOCK 1 ;0 = no error, -1 = run error, 1 = fork error
RSCBUF: BLOCK <<RSCSIZ/5>+1> ;Buffer for text to rescan
INTTIM: BLOCK 1 ;-1 or date and time to start at
NEWTIM: BLOCK 1 ;New time to reset to
OLDTIM: BLOCK 1 ;Time to compare msgs with
IFE OLDSW,<
LNGAGO: BLOCK 1 ;Stores a date of 30 days ago
>;IFE OLDSW
SAVHD: BLOCK 1
HDCNT: BLOCK 1
CURPOS: BLOCK 1
NUMCHR: BLOCK 1
;Data for backup capabilities
STRTPT: BLOCK 1 ;Byte pointer at first shown msg
BAKPTR: BLOCK BAKMAX ;Place to store byte pointers
BAKCNT: BLOCK 1 ;Index into BAKPTR
STRTCN: BLOCK 1 ;Count at initial message for today
DATPAG: BLOCK 1 ;Page to map data stuff
DATPG1: BLOCK 1 ;Page for last file write
TXTPAG: BLOCK 1 ;Where .TXT file will be mapped
TXTPGS: BLOCK 1 ;Number of pages in .TXT file
TXTPTR: BLOCK 1 ;Pointer into in-core .TXT file
TXTEOF: BLOCK 1 ;Address of last word in TXT file
EOF: BLOCK 1 ;Tells when EOF has been reached
MSGPTR: BLOCK 1 ;Pointer to this message
HDRPTR: BLOCK 1 ;Pointer to header of the message
DJFN: BLOCK 1 ;JFN of data file
IJFN: BLOCK 1 ;Input JFN of message file
BBOFIL: BLOCK BBNMAX ;0 or jfn of BBOARD file
BBDFIL: BLOCK BBNMAX ;0 or jfn of .DAT file
DASHPT: BLOCK 1
MSGLEN: BLOCK 1
MSGCNT: BLOCK 1 ;Message number in the file
DATE: BLOCK 30 ;Place to put date
BUFSIZ==200 ;Max num of chars in buf
BUF: BLOCK <BUFSIZ/5+1>
TMPBUF=BUF ;Use the same buffer for both
FDB: BLOCK .FBLEN
;For building the keyword table
BLDJFN: BLOCK 1
FILKEY: BLOCK 40
KEYWDS: BLOCK 40*10*2
FLDDAT: BLOCK 40
FLDFRM: BLOCK 40
FLDSUB: BLOCK 100
HEAD1: BLOCK 200
LINBUF: BLOCK 40 ;Should hold .gt. big tty line
NLINES: BLOCK 1 ;Number of lines on tty
NCOLS: BLOCK 1 ;Number of columns on tty
SAVMOD: BLOCK 1 ;Save terminal mode
ENDPS ;End of impure data
PSECT CODE,CODORG
;Pure storage
IFN PUSHSW,<
HELP: ASCIZ \
Legal commands are:
<SPACE>, <CR>, or Y Show the rest of this bulletin
<DEL> or N Skip this bulletin, go to the next one
B Back up one message
E or Q Quit this bulletin board, and go to the next one
H Show the header again
M Call MM on this bulletin board
P Push to a new Exec
R Restart this bulletin board
X Exit BBOARD, skipping all remaining bulletin boards
\
>;IFN PUSHSW
IFE PUSHSW,<
HELP: ASCIZ \
Legal commands are:
<SPACE>, <CR>, or Y Show the rest of this bulletin
<DEL> or N Skip this bulletin, go to the next one
B Back up one message
E or Q Quit this bulletin board, and go to the next one
H Show the header again
R Restart this bulletin board
X Exit BBOARD, skipping all remaining bulletin boards
\
>;IFE PUSHSW
;Matching targets for From:, Subject:, and Date: fields, and where to store
DEFINE FLDLST <
XX <Date:>,FLDDAT
XX <DATE:>,FLDDAT
XX <From:>,FLDFRM
XX <FROM:>,FLDFRM
XX <Sende>,FLDFRM
XX <SENDE>,FLDFRM
XX <Subje>,FLDSUB
XX <SUBJE>,FLDSUB
XX <Re:>,FLDSUB
XX <RE:>,FLDSUB
>;FLDLST
;Get the target table
DEFINE XX (STR,LOC) <
ASCII \STR\
>;XX
FIELDS: FLDLST ;Expand it
FLDNUM==.-FIELDS ;Length of this block
;The destination table
DEFINE XX (STR,LOC) <
LOC
>;XX
DEST: FLDLST ;Expand it
HEAD1+FLDNUM ;This is for when it is unknown
SWTTAB: TABTOP ;Switches to BBOARD
T <ACTION> ;Want file name output before reading
T <CHECK> ;Check if there are new messages
IFN FACSW,<
T <CLASS> ;Want the class bboard
>;IFN FACSW
T <DATE> ;Set the date and time
IFN FACSW,<
T <FACULTY>,.FAC ;Want the class bboard
>;IFN FACSW
T <FROM> ;See message from this time forth
T <LAST> ;Want to see the last message at least
T <MORE> ;Use more processing
T <NAME> ;Want the file name output at the start
IFN UTEXSW,< ;[UT-2]
T <NOMORE>,.SMORE ;[UT-2] Don't use more processing
>;IFN UTEXSW
T <PAUSE> ;Want to pause at the end of the bboards
T <SCROLL-MORE>,.SMORE ;Use scroll more
T <SECURE>,,.SECUR ;[UT-3] /SECURE switch
T <SET-TIME>,.DATE ;Set date and time
T <USE-TIME>,.FROM ;See messages from this time forth
TABBOT
PGMTAB: TABTOP ;Program name keyword table
T <BBOARD>,1
T <ERUN>,0
T <R>,0
T <REENTER>,0
T <RUN>,0
T <START>,0
T <TBBD>,1 ;For test purposes
T <TBBOARD>,1
TABBOT
;Define BBoard commands
;Macro to define a (1-character) BBoard command. SWITCH indicates
;whether or not to update time-last-read when the command is given.
DEFINE BBCMD (CHAR,ROUTINE,SWITCH) <
IFIDN <SWITCH>,<UPDATE>,<.UPD==-1>
IFDIF <SWITCH>,<UPDATE>,<.UPD==0>
RELOC CMD+CHAR ;Get to appropriate spot in CMD table
.UPD,,ROUTINE ;If uppercase letter, do same for lowercase
IFLE "A"-CHAR,<IFGE "Z"-CHAR,<BBCMD (CHAR!40,ROUTINE,SWITCH)>>
>;BBCMD
CMD: REPEAT .CHDEL,<CONFM> ;Reprompt on any command not define here
BBCMD .CHBSP,NOCR,UPDATE ;Backspace = skip message and update
BBCMD .CHLFD,CFMOK,UPDATE ;Linefeed = show message and update
BBCMD .CHCRT,OKSKLF,UPDATE ;Carriage return = eat lf, show msg, update
BBCMD .CHSPC,OKCR,UPDATE ;Space = show msg and update
BBCMD "?",HELPEM
BBCMD "E",DONEQ
BBCMD "Q",DONEQ
BBCMD "X",DONEX
BBCMD "Y",OKCR,UPDATE
BBCMD "N",NOCR,UPDATE
IFN PUSHSW,<
BBCMD "P",.PUSH
BBCMD "M",.MM
>;IFN PUSHSW
BBCMD "H",HEADER
BBCMD .CHCNR,HEADER
BBCMD "R",RESTRT
BBCMD "B",BACKUP
BBCMD "^",BACKUP
BBCMD .CHDEL,NOCR,UPDATE ;Delete = skip msg and update
RELOC CMD+200 ;Make sure we are at right spot
;Defaults for BBOARD file
SYSDAT: ASCIZ \POBOX:<SYSTEM>MAIL.IDX\ ;SYSTEM data file name
SYSBBD: ASCIZ \POBOX:<SYSTEM>MAIL.TXT\ ;SYSTEM BBOARD file name
;Define a terminal type in the table
DEFINE TRMTYP (NUM,STR) <
RELOC CLRLIN+NUM ;;Go to the right position in the table
STR ;;And expand the string
>;TRMTYP
;This entry point is only done by the system when a new user logs on for
;the first time. Must clear the old user's date/time
SYSALL: SKIPA A,[1] ;Show all system messages (can't be 0)
;Normal system message entry point
SYSMSG: SETO A, ;Show normal system messages
MOVEM A,NEWTIM ;Remember which
IFE SMSGSW,<
MOVX F,F%SYS ;Say we want the system msgs
>;IFE SMSGSW
IFN SMSGSW,<
IFE ENDMSW,<
MOVX F,F%SYS!F%NAM!F%SCMR!F%SECU ;We want the system messages (special mode)
>;IFE ENDMSW
IFN ENDMSW,<
MOVX F,F%SYS!F%NAM!F%SCMR!F%PAUS!F%SECU
;If want ending message, be sure to pause
>;IFN ENDMSW
>;IFN SMSGSW
JRST GO
;Normal entry point
START: SETZ F, ;Clear flags
SETOM NEWTIM ;No new time specified yet
GO: RESET% ;Traditional, true?
MOVE P,[IOWD PDLL,PDL] ;Set stack up
CALL INIT ;Do intializations
TXNN F,F%SYS ;Don't rescan if for SYSTEM:
CALL RESCAN ;Rescan command line for date/time
;Loop for each bboard
DOBBDS: SETZ BBN, ;Start off with first
NXTBBD: CALL DOBBD ;Go do the next bboard
TXNE F,F%EXIT ;Done with everything
JRST BYE ;Yes
SKIPN BBOFIL+1(BBN) ;Is there another bboard?
JRST BYE ;No, then give it all up
TXNE F,F%MSGB ;Yes, did we show something
CALL OCRLF ;Yes, go to a new line
AOJA BBN,NXTBBD ;Go on to next bboard
;DOBBD - Get the bboard BBN
DOBBD: CALL BBDINI ;Initialize for this bboard
CALL OPNDAT ;Open the data file, A <- JFN
IFNSK.
CALL DATFAI ;Exit on errors
JRST DOBBDX ;But be sure to clean up
ENDIF.
CALL GTDATU ;Get the dates for this user
CALL CPFTIM ;Compare file times
JRST DONEMG ;File was too old (or error)
CALL OPNBBD ;Open bboard file
JRST DOBBDX ;It didn't open
DOMSGS: CALL DOMSG ;Do the next message
SKPA ;If have more to do...
JRST DOMSGS ;Do them
TXNE F,F%CHK ;If we're checking
JRST DOBBDX ;Then we're done
TXNN F,F%ERR ;In case of error
TXNE F,F%MSGB ;Or he saw some messages
SKPA ;Then done with this bboard
TXNN F,F%LAST ;Or if he didn't want last msg
JRST DONEMG ;Then done with this bboard
TXO F,F%MSGB ;Say you have seen some
CALL LAST ;So you can back up to last one
JHLT ;In case of error, stop
JRST DOMSGS ;Loop for next message
JHLT ;If can't find it, just halt
DONEMG: TXNN F,F%QUIT ;Did he quit?
CALL ENDMSG ;No, so show a message
DOBBDX: CALL CLNUP ;Close open files and such
RET ;Done with this bboard
;OPNDAT - Open the data file
;Returns +2, successful with JFN of data file in A
; +1 if not
OPNDAT: SKIPE A,BBDFIL(BBN) ;If we have jfn on data file,
JRST OPNDA1 ;Go open it
HRRZ B,BBOFIL(BBN) ;Get the JFN of the bboard file
JUMPE B,OPNDA0 ;If nothing there, go around
HRROI A,CURFIL ;Create a subname of the file
MOVX C,FLD(.JSAOF,JS%DEV)!FLD(.JSAOF,JS%DIR)!FLD(.JSAOF,JS%NAM)!JS%PAF
JFNS% ;Output all but the extension
ERJMP R ;Failed, say so
HRROI B,[ASCIZ \.IDX\] ;Want to add the extension
SETZ C,
SOUT%
IDPB C,A ;Make sure it's null terminated
MOVX A,GJ%SHT!GJ%OLD ;Short form, old file
HRROI B,CURFIL ;Point to the name we made
GTJFN% ;Try to get a JFN on it
IFNJE.
MOVEM A,BBDFIL(BBN) ;If we win, save it away
JRST OPNDA1 ;And go on
ENDIF.
OPNDA0: MOVX A,GJ%OLD!GJ%SHT ;Try to get data file
HRROI B,DEFDAT ;Use the default data file name
TXNE F,F%SYS ;If wants SYSTEM bboard,
HRROI B,SYSDAT ;Give it
GTJFN%
RET ;Take the error return
OPNDA1: MOVX B,FLD(^D36,OF%BSZ)!OF%RD!OF%WR!OF%THW
;Want to read and write it in Thawed mode
OPENF%
RET ;Take the error return
HRRZM A,DJFN ;Stores the JFN for DATA file here
RETSKP ;Other take the normal exit
;GTDATU - Get the dates for this user
;Maps in the page of the data file that contains dates for this user
;Returns X/ first of the two word date field
GTDATU: GJINF% ;Get dir number
TLZ A,-1 ;I said dir number...
;;;Fix this to use the real operator number!
CAIN A,5 ;If it's operator,
HALTF% ;Don't bother (continue if really wanted)
IDIVI A,1000 ;Calculate page no and index
PUSH P,A
ADDI A,MMRPAG ;Offset to MM IDX file date
MOVE X,DATPAG ;Page to use
LSH X,^D9
ADDI X,(B) ;X/ address of d&t of last bboard read time
HRL A,DJFN ;Map in that page
MOVE B,DATPAG ;Map in that page to here
HRLI B,.FHSLF
MOVX C,PM%RD!PM%WR
PMAP%
HRRS PG.RD(X) ;Make sure it exists
POP P,A
ADDI A,MMWPAG ;Add the offset for the write time
HRL A,DJFN ;Map in that page
MOVE B,DATPG1 ;Map in that page to here
HRLI B,.FHSLF
MOVX C,PM%RD!PM%WR
PMAP%
HRRS PG.WRT(X) ;Make sure it exists
MOVE A,NEWTIM ;If the user specified to use a new-time
CAMN A,[-1] ;If not -1, then specified
RET ;Otherwise exit now
MOVEM A,PG.RD(X) ;Else set file and msg times
MOVEM A,PG.WRT(X) ;To what he wants
RET ;Then return
;CPFTIM - Compare times of access with times of file
;Takes INTTIM/ -1 or time input by user
; PG.RD(X) and PG.WRT(X)/ last access times
; BBOFIL(BBN)/ if <> 0, bboard JFN
;Returns +1, nothing to read
; +2, something to read
; IJFN/ JFN of bboard file
; FDB+.FBWRT/ file write date
CPFTIM:
IFE OLDSW,< ;If wants only msgs in last month
SKIPL A,INTTIM ;If no initial time given
IFSKP.
MOVE A,PG.RD(X) ;Get his old time
CAMGE A,LNGAGO ;OK if more recent than 1 month
MOVE A,LNGAGO ;Otherwise use 1 month ago
ENDIF.
>;IFE OLDSW
IFN OLDSW,<
SKIPGE A,INTTIM ;If no initial time given
MOVE A,PG.RD(X) ;Use previously stored time
>;IFN OLDSW
MOVEM A,OLDTIM ;To compare with new times
SKIPE A,BBOFIL(BBN) ;If we already have jfn on bboard file,
JRST CPFTI1 ;Don't need another
MOVX A,GJ%OLD!GJ%SHT ;Try to get at mail file
HRROI B,DEFBBD ;Use the default name
TXNE F,F%SYS ;If wants SYSTEM bboard
HRROI B,SYSBBD ;Give it to him
GTJFN%
RET ;No file or something, fine
IFN SMSGSW,<
TXNN F,F%SYS ;Don't set default if system bboard
>;IFN SMSGSW
TXO F,F%DEF ;Say it is default bboard
CPFTI1: HRRZM A,IJFN ;Save JFN
MOVSI B,.FBLEN ;Get date of last write of mail file
MOVEI C,FDB
GTFDB%
ERJMP R ;Go back +1 if we failed
MOVE T,FDB+.FBWRT ;Pick up the write date and time
IFN NEWRSW,<
SKIPN OLDTIM ;Check for new readers
CALL NEWRDR ;And handle them
>;IFN NEWRSW
TXNE F,F%ACT ;want filename printed on reading start?
CALL PRTNAM ;Print the name of the bboard
SKIPGE INTTIM ;Initial time given?
IFSKP.
CAMLE T,INTTIM ;Yes, compare to that, instead
AOS (P) ;If not too old, do good return
RET ;File too old, just be done with it
ENDIF.
MOVE A,OLDTIM ;Get the old time
SKIPE PG.WRT(X) ;If no entry, then assume read
CAMLE A,PG.WRT(X) ;Greater than last write?
MOVEM A,PG.WRT(X) ;Update the last write
CAMG T,PG.WRT(X) ;Get file if not too old
TXNE F,F%LAST ;If /LAST, get file even if too old
AOS (P) ;Take good skip return
RET ;File too old, just be done with it
;OPNBBD - Open the BBOARD file
;Takes IJFN/ bboard file's JFN
;Returns +1, an error happened
; +2, no error, file opened
OPNBBD: MOVE A,IJFN ;Get input JFN
MOVX B,FLD(7,OF%BSZ)!OF%RD!OF%PDT ;Don't update the read date
OPENF%
ERJMP OPNERR ;In case of error during opening BBD
;Check to make sure file is a good one
HRLZ A,IJFN
FFUFP%
IFJER.
CAIE A,FFUFX3 ;In case of error
CALLRET PRSFAI ;Not an empty file, complain
RET ;But if empty, just exit without complaining
ENDIF.
TRNN A,-1
IFSKP.
HRROI A,[ASCIZ \Text does not start on first page of file\]
ESOUT%
RET ;Take the error return
ENDIF.
CALLRET MAPTXT ;Map in the .TXT file
;OPNERR - Check out error during opening of BBD file
;Takes IJFN/ BBoard file's JFN
;Returns +1, Real error
; +2, File was deleted and permanent (like a mail file), then do
OPNERR: MOVE A,IJFN ;Pick up JFN
MOVE B,[1,,.FBCTL] ;In FDB, get descriptor bits
MOVEI C,T ;Store result here
GTFDB%
ERJMP OPNER1 ;If this fails, just report open err
TXC T,FB%PRM!FB%DEL ;Look for perm, deleted file
TXNN T,FB%PRM!FB%DEL
RET ;Deleted and permanent. Then done without error
OPNER1: HRROI A,[ASCIZ \Can't OPEN the bboard mail file\]
CALLRET ESOERR ;Take the error return
;MAPTXT - Maps the text file into core
;Returns +1, failure
; +2, success
MAPTXT: LOAD A,FB%PGC,FDB+.FBBYV ;Page count
MOVEM A,TXTPGS
HRRZ A,IJFN ;Make sure file long enough
FFFFP%
HRRZS A
CAMLE A,TXTPGS ;Take the bigger count
MOVEM A,TXTPGS
MOVEI A,^D36 ;Bits per word
LOAD B,FB%BSZ,FDB+.FBBYV ;Bits per byte
CAIG B,^D36
IFSKP.
HRROI A,[ASCIZ \BBoard file has bad byte size\]
JRST ERROUT
ENDIF.
SKIPN B ;Don't zerodivide
SKIPA A,[1]
IDIVI A,(B) ;Bytes per word
MOVE B,FDB+.FBSIZ ;Calculate CEILING(bytes/bytes per word)
IDIVI B,(A)
MOVEM B,TXTEOF
ADDI B,777 ;Get the page count we will actually use
LSH B,-9
CAMG B,TXTPGS ;Check number of pages for consistency
IFSKP.
HRROI A,[ASCIZ \BBoard size in bytes won't fit in page count\]
JRST ERROUT
ENDIF.
MOVEM B,TXTPGS
MOVEI A,1000 ;Check for enough space
SUB A,TXTPAG
CAMLE A,TXTPGS
IFSKP.
HRROI A,[ASCIZ \BBoard file too big\]
JRST ERROUT
ENDIF.
GOTCOR: HRLZ A,IJFN ;Map in the text file
MOVE B,TXTPAG
HRLI B,.FHSLF
MOVE C,TXTPGS
TXO C,PM%CNT!PM%RD
PMAP%
MOVE A,TXTPAG ;Init TXTPTR
LSH A,^D9
XMOVEI A,(A) ;Offset end of file pointer according to
ADDM A,TXTEOF ;Global address of TXTPAG
HRLI A,(<POINT 7,0>)
MOVEM A,TXTPTR
SETZM EOF ;Not at end of file
RETSKP ;Skip return if no error
;BBDINI - Initialize for a new BBoard file
;Clears message count, all flags, back pointers.
BBDINI: SETZM MSGCNT ;Clear the count of messages
TXZ F,F%DEF!F%MSGB!F%ERR!F%QUIT!F%1ST
;Say it isn't default bboard
;Clear the flag for this file
; saying nothing has been shown
;Say no error
;Say he hasn't quit
;Say haven't seen first msg
; CALLRET BAKCLR ;Clear the back pointers and exit
;BAKCLR - Clear the back pointer array for this bboard
BAKCLR: SETOM BAKPTR ;Set them to -1
MOVE A,[BAKPTR,,BAKPTR+1] ;Set to BLT
BLT A,BAKPTR+BAKMAX-1 ;Set them all to -1
MOVEI A,BAKMAX-1 ;Start off at the top and count down
MOVEM A,BAKCNT ;Set the counter to point at top
RET
;DOMSG - Handle the next message
;Takes IJFN (or TXTPTR)/ pointer to message about to be parsed. Pointing right
; at normal TOPS-20 MM-type header (date,time,count,flags)
DOMSG: CALL SAVBAK ;Save the pointer for backing up
RET ;Error during RFPTR
AOS MSGCNT ;Increment message count
CALL PRSMSG ;Go parse the message
RET ;Error during Parsing
RET ;Done with messages
CALL CHKMSG ;Check to see if to show or not
IFNSK.
CALL SKPMSG ;Do not show
RET ;Strange error
RETSKP ;Go to next
ENDIF.
IFXN. F,F%CHK ;Check for new messages?
HRROI A,[ASCIZ \[New mail in bboard:\]
CALL TYPNAM
MOVEI A,"]"
PBOUT%
CALL OCRLF
RET
ENDIF.
TXZ F,F%ERR ;Clear out any error flag
CALLRET SHOWIT ;Show it to user
;PRSMSG - Parse a message
;Returns +1, parse error
; +2, EOF
; +3, Success, DAT/ date
; T/ length
; B/ flag bits
PRSMSG: SETZ B, ;Parse standard format time
MOVE A,TXTPTR ;Get the pointer to the text
CALL INTIME ;Go read the time
IFNSK.
SKIPE EOF ;On an error, check for EOF
JRST RET2 ;EOF is ok. Do skip return
JRST PRSFAI ;Not EOF, must be garbage
ENDIF.
MOVEM B,DAT ;Save date&time of this msg in dat
MOVEI C,^D10 ;Parse length of message
CALL NUMIN ;Get a number from the file (NIN)
JRST PRSFAI
MOVEI T,(B) ;Save length in t
MOVEM T,MSGLEN ;Save length of msg
MOVEM T,HDCNT ;Save this as head count in case EOH not found
MOVEI C,(T) ;Check if count puts us beyond eof
IDIVI C,5 ;Number of words in msg
MOVEI D,TXTPTR ;File pointer
ADDI D,(C)
CAMG D,TXTEOF
IFSKP.
HRROI A,[ASCIZ \Message extends beyond end of file\]
JRST ERROUT
ENDIF.
MOVEI C,^D8 ;Parse flag bits
CALL NUMIN ;Get number from the file
IFNSK.
CAIE C,IFIXX3 ;If the 400000,,000000 bit isn't set
JRST PRSFAI ;It's an error
ENDIF.
MOVEM A,TXTPTR ;Save the pointer to the text
JRST RET3 ;Normal exit
;CHKMSG - Decide if to skip or show message
;Takes B/ MM Flag bits DAT/ message's date/time
;Returns +1, message not to be shown
; +2, message to be shown
CHKMSG: TRNE B,2 ;If deleted message
RET ;Skip over msg
TXNN F,F%MSGB ;If msgs already seen
CAMLE DAT,OLDTIM ;Or if after initial or stored time
AOS (P) ;Show all (skip return)
RET
;SKPMSG - Skip over the message
;Takes T bytes to skip
;Returns +1, error
; +2, all ok
SKPMSG: MOVE B,TXTPTR ;Get the file/core pointer
MOVEI A,(T) ;Increment by byte count
ADJBP A,B ;Adjust the byte pointer from B
MOVE B,A ;Restore into B
MOVEM B,TXTPTR ;Set the file/core pointer
RETSKP ;And try the next msg
;SHOWIT - Show the message
;Takes IJFN or TXTPTR/ pointer to just after the date/byte count/flags line
; T/ number of bytes in the message
;Returns +2, Go on to next message
; +1, exit this bboard
SHOWIT: CALL SHOINI ;Initialize
CALL GTHEAD ;Get the header
JRST NOHEAD ;If it fails, show the entire thing
CALL GTFLDS ;Get the fields from the header
IFNSK.
NOHEAD: TXNE F,F%ERR ;If an error,
RET ;Then take error return. Else
MOVE B,HDRPTR ;Show the entire header
;HDCNT contains the entire header
JRST SHOWI1
ENDIF.
HRROI B,HEAD1+FLDNUM ;Show the interesting stuff
SHOWI1: MOVEM B,SAVHD ;Save the header for showing again
CALL OUTON ;Turn off ^O
MOVEI A,5 ;Say to check for 5 lines
IFE UTEXSW,< ;[UT-2] We don't like this
CALL MORCHK ;Go do it
>;IFE UTEXSW
IFN WHITSW,<
CALL OCRLF ;Make sure to have a blank line
>;IFN WHITSW
MOVEI A,.PRIOU ;Output to primary output
MOVE B,SAVHD ;Restore header
MOVE C,HDCNT ;No more than this to show
SETZ D, ;Don't stop for any byte but null
SOUT% ;Show the header
CALL CONFM ;What does he want to do?
RET ;Exit the bboard
JRST RET2 ;Go on to next
JRST [MOVE B,SAVHD ;Reshow header
JRST SHOWI1]
TXNE F,F%MORE ;Want more processing?
TXNE F,F%SCMR ;Yes, want scroll more?
SKPA ;Yes
CALL $BLANK ;Here if more and not scroll, clear screen
MOVE C,MSGLEN ;Number of characters in entire msg
SUB C,HDCNT ;Minus number of characters in header
MOVEI A,.PRIOU ;Output to the primary output
MOVE B,T ;Pick up the pointer to the body
MOVN C,C ;Now make count negative for byte count
SKIPN C ;Have some characters to print?
IFSKP. ;Yes
IFXN. F,F%MORE ;Want more processing?
CALL MORE ;Yes, do "sout" with more processing
ELSE.
SOUT% ;Else do it the boring way
ENDIF.
ENDIF.
TXNE F,F%QUIT ;Want to exit?
RET ;Yes, make sure we do
JRST RET2 ;Otherwise go to the next message
;MORE - routine to do "sout" with unix-style more processing
;Takes: B/ pointer to source string
; C/ number of characters in string
;Returns: +1 always
MORE: ACVAR <SRC,COUNT,COLCNT,LINCNT>
MAKPTR B ;Make sure it's a byte pointer
MOVEM B,SRC
SKIPG C
MOVMS C ;Make positive
MOVEM C,COUNT
MOVEI A,.PRIOU ;Get tty designator
RFPOS% ;Read row,,column
HRRZM B,COLCNT ;Put column position in counter
HLRZM B,LINCNT ;Put row position in counter
MORE0: MOVE A,SRC ;Get pointer at input buffer
MOVE B,COUNT ;Get character count
CALL GETLIN ;Try to get a line of text
RET ;Couldn't, so quit
MOVEM B,COUNT ;Save updated char count
MOVEM A,SRC ;Save updated pointer
AOS LINCNT ;Bump lines-printed count
IFE UTEXSW,<
CAMLE LINCNT,NLINES ;Printed enough lines?
IFSKP. ;No
>;End of IFE UTEXSW
HRROI A,LINBUF
CALL PRTLIN ;Dump the line out
JRST MORE0 ;And go for more
IFE UTEXSW,<
ENDIF.
>;END of IFE UTEXSW
CALL MORPMT ;Prompt "More?"
IFNSK.
CALL BLKLIN ;Make sure to clear the line
RET ;Didn't want more
ENDIF.
IFXN. F,F%SCMR
CALL BLKLIN
ELSE.
CALL $BLANK ;Blank screen
ENDIF.
SETZM LINCNT ;Say we are at top of screen
HRROI A,LINBUF
CALL PRTLIN ;Dump the line out
AOS LINCNT
JRST MORE0 ;Go for more
ENDAV.
;GETLIN - get a line of characters to print into LINBUF
;Takes: A/ pointer to text buffer
; B/ count of characters remaining in string
;Returns: +2 if successful, +1 if not
; B/ updated count of characters remaining in string
GETLIN: SAVEAC <E>
MOVE E,B
SKIPG E ;Any characters left?
RET ;Nope, non-skip return
MOVE B,[POINT 7,LINBUF] ;Point to destination buffer
MOVE D,NCOLS
SOS D ;D now counts down to 0 for EOL
GETLN0: ILDB C,A ;Get character
SKIPE C
IFSKP.
SETZM E ;Say no more chars left
JRST GETLN1 ;Quit if we hit a null
ENDIF.
IDPB C,B ;Put it in output
CAIE C,.CHCRT ;Is it CR?
IFSKP.
IBP A ;Advance over presumed LF
MOVEI C,.CHLFD ;Presume it into output
IDPB C,B
SUBI E,2 ;Account for characters read
JRST GETLN1 ;Exit from GETLIN
ENDIF.
SOSG D ;Bump off another column
IFSKP. ;More left?
SOSLE E ;Haven't exhausted buffer
JRST GETLN0 ;Loop back for them or EOL
ELSE. ;Nope, no more left
SOS E
ENDIF.
;Here to punt - we hit EOL or EOF or something
GETLN1: SETZ C, ;Mark off that we ate another char
IDPB C,B ;Tie off return string
MOVE B,E ;Restore updated count
RETSKP
PRTLIN: PSOUT%
RET
;Prompt with "More?" at bottom of screen
;Returns: +2 if user wants next screen, +1 to skip to next message
MORPMT: CALL OUTON ;Turn on output
MOVEI A,.CTTRM ;For our terminal
RFMOD% ;Get the terminal mode
MOVEM B,SAVMOD ;And save it
TXZ B,TT%DAM!TT%PGM ;Want binary mode
SFMOD% ;Set it
STPAR%
MOVEI B,.MOXOF
MOVEI C,.MOOFF
MTOPR%
MOVEI A,.PRIOU ;Start on a fresh line
RFPOS% ;If not already
TXNE B,.RHALF
CALL OCRLF
MORPM0: HRROI A,[ASCIZ/More? /]
CALL PRTLIN
MORPM1: MOVEI A,.PRIIN ;For our terminal
CFIBF% ;Clear the input buffer
PBIN% ;Snarf a char
ANDI A,177 ;Just in case parity or 8-bit
CAIN A,.CHSPC ;Space?
JRST MRETSK ;Next screenful
CAIN A,.CHCRT ;CR?
JRST MRETSK ;Next screenful
CAIE A,"?" ;'?'
IFSKP. ;Print short help text
IFN PUSHSW,<
HRROI A,[ASCIZ \
Type <SPACE> or <CR> to continue reading this message
Q, N, or <DEL> to skip to next message
X to exit all bboards
H to display header again
P to Push to a new Exec
\]
>;IFN PUSHSW
IFE PUSHSW,<
HRROI A,[ASCIZ \
Type <SPACE> or <CR> to continue reading this message
Q, N, or <DEL> to skip to next message
X to exit all bboards
H to display header again
\]
>;IFE PUSHSW
PSOUT% ;Don't use TMSG because of <>'s
JRST MORPM0 ;Loop back
ENDIF.
CAIL A,"a"
CAILE A,"z"
SKPA
TRZ A,40 ;Uppercase lowercase letters
CAIN A,"Q" ;Quit?
JRST MRET ;Non-skip return
CAIN A,"Q"-100 ;Check for real losers (^Q)
JRST MRETSK
CAIN A,"N"
JRST MRET
CAIN A,.CHDEL ;Delete?
JRST MRET ;Yes, synonym for "N"
CAIE A,"X" ;Exit all bboards?
IFSKP. ;Yes...
TXO F,F%QUIT!F%EXIT ;Say we want to quit and exit
JRST MRET ;And go back
ENDIF.
IFN PUSHSW,<
CAIE A,"P" ;Push?
IFSKP.
PBOUT% ;Yes, output the char first
CALL MRET ;Restore terminal mode
CALL .PUSH ;Do the push
NOP ;Account for multiple returns
NOP
NOP
JRST MORPMT
ENDIF.
>;IFN PUSHSW
CAIE A,"H" ;Want to see the header again?
IFSKP. ;Yes...
TMSG <
> ;Start off with a CRLF
CALL MRET ;Restore terminal mode
MOVEI A,.PRIOU ;To the terminal
MOVE B,SAVHD ;Get the pointer to the header
MOVE C,HDCNT ;How many words we can handle
SETZ D, ;Stop on null
SOUT% ;Output it
TMSG <
>
JRST MORPMT ;Prompt again for more
ENDIF.
MOVX A,.CHBEL ;Beep the loser
PBOUT%
JRST MORPM1 ;Go for anohter char
MRETSK: AOS (P) ;Skip return
MRET: MOVEI A,.CTTRM ;Non-skip return
MOVEI B,.MOXOF
MOVEI C,.MOONX
MTOPR%
MOVE B,SAVMOD ;Get our terminal mode again
STPAR%
SFMOD%
RET
;SHOINI - Initialize to show the message
;Takes T/ number of bytes in msg
;Returns T/ start of msg, W/ where HEAD is stored
SHOINI: ADJBP T,TXTPTR ;Update TXTPTR to point after this message
EXCH T,TXTPTR ;Save it away and get pointer to this msg
MOVEM T,MSGPTR ;Save this pointer
TXO F,F%MSGS!F%MSGB ;Set this flag each new msg
TXOE F,F%1ST ;First message for this bboard?
RET ;No - skip all this
MOVE A,BAKCNT ;Pick up index to bakptr
MOVE A,BAKPTR(A) ;Pick up byte count
MOVEM A,STRTPT ;Save the starting point
MOVE A,MSGCNT ;Save the message count here
MOVEM A,STRTCN
TXNE F,F%ACT ;say /ACTION?
CALL OCRLF ;Yes, get back to margin
IFXN. F,F%NAM ;Say /NAME?
CALL OUTON ;Make sure we see the banner
MOVEI A,4 ;Say to check for 4 lines
CALL MORCHK ;Go do it
CALL OCRLF ;With a separator line
HRROI A,[ASCIZ \Messages from:\] ;Yes, type it then
CALL TYPNAM ;And the file name (if one specified)
CALL OCRLF
ENDIF.
RET
;GTHEAD - Process the header
;Count characters in header while looking for the end (first blank line)
;Takes T/ start of header, W/ where to store the header
; MSGLEN/ total length
;Returns +1, error
; +2, header where W pointed to
; T/ after header
; HDCNT/ number of characters in header
GTHEAD: MOVEM T,HDRPTR ;Save pointer to header
MOVEI C,1 ;Count the chars in head
SETZ V, ;Say previous char was not LF
HEADLP: CAMN C,MSGLEN ;In case no blank line at all
RET ;If we run out, go on
ILDB U,T
CAIN U,15 ;<CR>?
CAIE V,12 ;Yes, after <LF>?
SKIPA V,U ;No, Set V to this char for next loop
JRST PARSHD ;Found blank line, exit
AOJA C,HEADLP ;Count the chars in head
PARSHD: ILDB U,T ;Snarf up <LF>
CAIE U,12 ;It had better be a LF!
CALLRET PRSFAI ;If not, complain and take error exit
ADDI C,1 ;Add 1 for the LF
MOVEM C,HDCNT ;Count of chars in header
RETSKP
;GTFLDS - Get the fields from the header
;Returns +1, can't get the header set up right (so show all)
; +2, all ok HEAD1+FLDNUM/ pointer to header info
GTFLDS: CALL GTFLDI ;Do initialization
FLDLP: CALL FLDNAM ;Get the field name before the :
IFNSK.
TXZE F,F%ERR ;Whoops, was it real error?
RET ;Yes, so act as if no header
SKIPE NUMCHR ;Is there more to the header?
JRST FLDLP ;Yes, go to next line
JRST FLDEND ;No, just ran out
ENDIF.
JUMPE C,FLDLP ;If nothing else on line, forget this one
CALL FLDMAT ;Go match up that field
CALL FLDVAL ;Get the value for the field (after :)
RET ;In case of error, act as if no header
SKIPE FLDSUB ;Check wheter we have SUBJECT:
SKIPN FLDDAT ;And DATE:
JRST NOFLDS ;Not yet, try for more
SKIPE FLDFRM ;And FROM:
CALLRET GOTFLD ;Have all fields, will process them
NOFLDS: ILDB E,A ;Pick up the LF at end
CAIE E,.CHLFD ;Check to make sure LF
CALLRET PRSFAI ;Ooops....
JRST FLDLP ;Try for more
;We ran out of header without finding all fields. Maybe enough?
FLDEND: SKIPE FLDDAT ;Did we get date
SKIPN FLDFRM ;And sender? Maybe no subject?
RET ;We failed to find all!
CALLRET GOTFLD ;Found enough
;GTFLDI - Initialization for GTFLDS
;Takes HEAD/ header part
; HDCNT/ number of chars in it
;Returns CURPOS/ pointer to start of the header
; NUMCHR/ number of characters in the header
; FLDDAT, FLDFRM, and FLDSUB/ 0
GTFLDI: MOVE A,HDRPTR ;Set A to point to beginning of header
MOVEM A,CURPOS ;Save so we always know where we were
MOVE C,HDCNT ;# of chars in header
MOVEM C,NUMCHR ;Save this
SETZM FLDDAT ;Clear first word of fields
SETZM FLDFRM ;So we know when they're filled
SETZM FLDSUB
RET
;FLDNAM - Get a field name (the stuff before the :) in the header
;Takes CURPOS/ start of the line
; NUMCHR/ number of characters left in the header
;Returns +1, line is not as expected (possible error flag)
; +2, Success, HEAD1+FLDNUM/ with line up to :
; C is non-zero if : was found on the line
;Have to do all this in case each line doesn't have a colon
FLDNAM: MOVE A,CURPOS ;Restore where we start
HRROI B,HEAD1+FLDNUM ;Gets Date: or whatever
MOVEI D,.CHLFD ;Grab next line
MOVE C,NUMCHR ;Count of characters left
JUMPE C,NERRET ;In case no chars left, go back +1
SIN% ;Grab the line
ERJMP ERRRET ;An error?
MOVEM A,CURPOS ;Save A
MOVE A,NUMCHR ;Pick up old count
SUBI A,(C) ;Get the characters read
MOVEM C,NUMCHR ;Save C
MOVEI C,(A) ;The number of characters read
HRROI A,HEAD1+FLDNUM ;Read from the line just grabbed
HRROI B,HEAD1+FLDNUM ;Output to here
;Note that we are now just scanning until the :
;The end of the this patch (mostly)
MOVEI D,":" ;Grab up to :
SIN% ;Grab the string to :
ERJMP ERRRET ;Give up on errors
JUMPN C,RET2 ;If was a colon, take normal skip rtn
NERRET: TXZA F,F%ERR ;Otherwise take non-skip rtn but no error
ERRRET: TXO F,F%ERR ;On an error set flag
RET ;And return
;FLDMAT - Find the match to the field
;Takes HEAD1+FLDNUM/ string with the field name
;Returns HEAD1(G)/ matching field
FLDMAT: MOVE E,HEAD1+FLDNUM ;Pick up first word
SETZ G, ;Will look through for possible fields
CAME E,HEAD1(G) ;Look until equal
;Note that this terminates when FIELDS+F = HDTMP
AOJA G,.-1 ;Keep looping
RET
;FLDVAL - Get the value after the colon
;Takes A/ pointer to just after the colon
; C/ count of characters left in the line
; DEST(G) where to store the value (as a string)
;Returns +1, give up on doing header
; +2, good value with value at DEST(G)
FLDVAL: MOVE B,A ;Save the byte pointer
ILDB E,A ;Check next byte
SOJE C,RET1 ;Quit if end
CAIE E,.CHTAB ;Flush tabs
CAIN E,.CHSPC ;and blanks
JRST FLDVAL
MOVE A,B ;Restore the byte pointer
ADDI C,1 ;Readjust counter too
HRRO B,DEST(G) ;Pick up the destination according to field
SKIPE (B) ;Have we already gotten this?
HRROI B,HEAD1+FLDNUM ;Yes. Maybe Sender and From or just messed up
;In any case, don't overwrite
MOVEI D,.CHCRT ;Stop at a CR
SIN% ;Grab another string
ERJMP RET1 ;Give up if it fails
SETZ E, ;Want to terminate string with null
DPB E,B ;So overwrite last byte (CR)
RETSKP
;GOTFLD - Builds the summary line beginning at HEAD1+FLDNUM
;Takes FLDDAT, FLDFRM, FLDSUB/ where those fields begin
;Returns HEAD1+FLDNUM/ concatenated string.
GOTFLD: ACVAR <PTOT>
SETZ PTOT, ;An accumulator
IFN SHDRSW,<
HRROI A,[ASCIZ \Date: \]
HRROI B,HEAD1+FLDNUM ;Output it to here
SETZ C, ;Will transfer all of date
SIN%
JHLT
>;IFN SHDRSW
HRROI A,FLDDAT ;Start off with date
IFE SHDRSW,<
HRROI B,HEAD1+FLDNUM ;Output it to here
>;IFE SHDRSW
SETZ C, ;Will transfer all of date
SIN%
JHLT ;This shouldn't fail
IFE SHDRSW,<
PUSH P,B
HRROI B,FLDDAT ;Pointer to the date
CALL SUBBP
IDIVI A,10
AOS A
IMULI A,10
MOVE PTOT,A
POP P,B
MOVEI G,.CHTAB ;Use a tab between fields
IDPB G,B ;Write it out
>;IFE SHDRSW
IFN SHDRSW,<
HRROI A,[ASCIZ \
From: \]
SIN%
JHLT
>;IFN SHDRSW
HRROI A,FLDFRM ;Now give who from
SIN%
JHLT
IFE SHDRSW,<
PUSH P,B
HRROI B,FLDFRM
CALL SUBBP
ADD PTOT,A
MOVE A,PTOT
IDIVI A,10
AOS A
IMULI A,10
MOVE PTOT,A
MOVEI A,.NULIO
HRROI B,FLDSUB ;Subject
SETZB C,D
SOUT%
JHLT
MOVE A,B
HRROI B,FLDSUB
CALL SUBBP
ADD PTOT,A
POP P,B
CAIG PTOT,^D72
IFSKP.
MOVE A,B
IFN WHITSW,<
FMSG <
> ;DREA wants the extra space on the left
>;IFN WHITSW
IFE WHITSW,<
FMSG <
>
>;IFE WHITSW
MOVE B,A
ELSE.
IDPB G,B ;Only drop in the tab if on the same line
ENDIF.
>;IFE SHDRSW
IFN SHDRSW,<
HRROI A,[ASCIZ \
Subj: \]
SIN%
JHLT
>;IFN SHDRSW
HRROI A,FLDSUB ;And the subject field
SIN%
JHLT
RETSKP ;Now go print this out
ENDAV.
;CONFM - Tell the user how long the msg is and process his response
;Returns +1, an error occured (with error flag set) or
; user wants to quit (with F%QUIT set)
; +2 user wants to skip the message
; +3 user wants to see the header again
; +4 user wants to see the message
CONFM: TMSG <
(>
MOVEI A,.PRIOU ;To the terminal
MOVE B,MSGLEN ;How long the message is
SUB B,HDCNT ;But make it how long the text is
MOVEI C,^D10 ;Radix decimal
NOUT% ;Output it
CAI
TMSG < chars; more?) >
MOVEI A,.PRIIN
CFIBF% ;Clear the input buffer
PBIN% ;Input the char
MOVE B,CMD(A) ;Pick up the command to do
TLNE B,-1 ;Left half negative for update, else zero
CALL SAVDAT ;Update the date of last message seen
JRST (B) ;Go execute the code for it
;Routines for handling responses to MORE?
;See CONFM for exit details
;For blank
CFMOK: MOVEI A,.PRIIN ;Pretend to be one line lower so
RFPOS% ;That the next header won't scroll
IFE SHDRSW,<
ADD B,[(1)] ;Part of the message off the screen
>;IFE SHDRSW
IFN SHDRSW,<
ADD B,[(3)] ;Part of the message off the screen
>;IFN SHDRSW
SFPOS%
RET4: AOS (P)
RET3: AOS (P)
RET2: AOS (P)
RET1: RET
;For <LF>
OKSKLF: PBIN% ;Eat the LF
JRST CFMOK
;For <BS>
NOCR: CALL OUTON ;Turn on output and
TXZ F,F%MSGS ;Don't pause if he said NO
AOS (P) ;Want to take a skip return
OCRLF: TMSG <
> ;Output a CRLF and return
RET ;Exit to skip message
;For H
HEADER: CALL OCRLF ;Go to next line
JRST RET3
;For ?
HELPEM: CALL OUTON
HRROI A,HELP
PSOUT%
JRST RET3 ;Exit to show header again
;For Q
DONEQ: CALL OUTON ;A precaution
TXO F,F%QUIT ;Say he wants to quit
RET ;Uncurl our way up the stack
;For X
DONEX: CALL OUTON ;A precaution
TXO F,F%QUIT!F%EXIT ;Say he wants to quit and exit
RET ;Uncurl our way up the stack
;For R
RESTRT: CALL OCRLF
MOVE B,STRTPT ;Pick up starting point
MOVEM B,TXTPTR ;Set the pointer to B
MOVE A,STRTCN ;Pick up starting count
SOS A ;Already added one, so subtract it
MOVEM A,MSGCNT ;And reset MSGCNT
CALL BAKCLR ;Clear the back pointers
JRST RET2 ;Go to next message
;For B - backup to previous messages
BACKUP: CALL OCRLF ;Output a crlf
LAST: ;Come here when backing up to last msg
;LAST presumes it does a RET2 or RET3
PUSH P,BAKCNT ;Save present BAKCNT
CALL DECBAK ;Decrement the BAKCNT
POP P,A ;Get old BAKCNT into A
MOVE B,BAKPTR(B) ;Pick up previous one
CAME B,[-1] ;Was it cleared
IFSKP.
MOVEM A,BAKCNT ;Oops! Too far. Don't do it.
TMSG <
Can not back up further. Try "R" to restart this BBoard.
> ;Warn user
JRST RET3 ;And continue by showing header again
ENDIF.
SETOM BAKPTR(A) ;Say we were never at present one
MOVEM B,TXTPTR ;Set the file pointer to B
SOS MSGCNT ;Decrement message count
SOS MSGCNT ;and once more for good measure
CALL DECBAK ;Decrement BAKCNT to previous so
; next advance will be correct
JRST RET2 ;Exit as though skipping present msg
;Do a PUSH when "P" is read
;See CONFM for exit details.
PUSH: TXNE F,F%SECUR ;[UT-3] Don't push if SECUREd
JRST [SECURD: TMSG <
% Can't run MM or Push
> ;[UT-3] Say nothing will happen
JRST RET3] ;[UT-3] Reprompt for this msg
TMSG <ush
[Type POP to continue reading messages]
>
MOVEI A,0 ;Invoke an inferior exec
HRROI B,[ASCIZ \SYSTEM:EXEC.EXE\]
CALL $RUN
NOP
JRST RET3 ;Reprompt for this msg
;Call MM when "M" is read
;See CONFM for exit details
MM: TXNE F,F%SECUR ;[UT-3] Can't run MM if /SECUREd
JRST SECURD ;[UT-3] Say so and exit
PBOUT% ;Cough, hack... make "M" echo as "MM"
; or "m" as "mm"
TMSG <
[Type QUIT to get back to BBoard]
Please wait a moment for MM to start up...
>
HRROI A,RSCBUF ;First part of command to MM
HRROI B,[ASCIZ \MM EXAMINE \]
MOVEI E,RSCSIZ
CALL $SCOPY
MOVE B,IJFN ;Input JFN
SETZB C,D ;Use full format, no prefix
JFNS% ;Give it dev:<dir>file.ext.n
HRROI B,[ASCIZ \
Header \]
CALL $SCOPY ;Once it gets the file, pick the msg
MOVE B,MSGCNT ;By its number
CALL DECOUT
MOVEI D,.CHLFD ;End the line
JUMPG E,MM1
MOVEI D,.CHCNR ;No room, overwrite preceding with ^R
ADD A,[7B5]
MM1: IDPB D,A
MOVEI D,.CHNUL ;Terminate string
IDPB D,A
HRROI A,RSCBUF ;Now invoke MM
HRROI B,[ASCIZ \SYS:MM.EXE\]
CALL $RUN
NOP
JRST RET3 ;Reprompt for current msg
;$RUN - Run a program as an inferior
;Takes A/ Ptr to string to rescan (0 means don't setup rescan)
; B/ Ptr to program name
;Returns +1, failure, error code in errflg (-1 = run error, 1 = fork error)
; +2, success
$RUN: CALL ACSAVE ;Save everything
SETZM ERRFLG ;Assume no error
SETZM FORK ;Make sure no fork
GETNM% ;Save jobname
MOVEM A,JOBNAM
MOVX A,GJ%OLD!GJ%SHT ;Look up the file
GTJFN%
ERJMP SERR
MOVEM A,FRKJFN
MOVEI A,.FHSLF ;Enable all possible capabilities
RPCAP%
MOVE C,B
EPCAP%
MOVX A,CR%CAP ;Create a new fork with all capabilities
CFORK%
ERJMP RSERR
MOVEM A,FORK ;Save fork handle
MOVSS A ;Put handle in left half
HRR A,FRKJFN ;JFN in right half
GET% ;Read program into fork
ERJMP KRSERR
SKIPN A,A-17(P) ;Recover the rescan buffer pointer
JUMPE A,$RUN1 ;Don't rescan anything if it's 0
RSCAN% ;Rescan it
ERJMP KRSERR ;Bad arg
MOVEI A,.RSINI ;Put it into input stream
RSCAN%
ERJMP KRSERR ;Can't happen
$RUN1: MOVE A,FORK ;Retrieve handle
MOVEI B,0 ;Normal start address
SFRKV% ;Start the fork
ERJMP KRSERR
RFORK% ;Resume, in case it didn't get going
ERJMP KRSERR
WFORK% ;Sleep until fork is finished
ERJMP KRSERR
HRRZ A,FORK ;Short form rfsts to see if fork succeeded
RFSTS%
ERJMP KRSERR
LOAD A,RF%STS,A
CAIE A,.RFHLT ;Better be normal halt condition
JRST INFERR
KRSR: MOVE A,FORK ;Kill!
KFORK%
ERJMP RSERR
RSR: HRRZ A,FRKJFN ;Release the jfn
RLJFN%
ERJMP SR ;Probably released by KFORK%
SR: MOVE A,JOBNAM ;set back original jobname
SETNM%
SKIPN ERRFLG ;If have had an error, skip the skip
AOS (P) ;Increment return address to return +2
RET
SERR: SETOM ERRFLG ;Run error
MOVEI B,.FHSLF
CALL ERRPRT
JRST SR ;Go set jobname
RSERR: SETOM ERRFLG ;Run error
MOVEI B,.FHSLF
CALL ERRPRT
JRST RSR ;Release jfn and set jobname
KRSERR: SETOM ERRFLG ;Run error
MOVEI B,.FHSLF
CALL ERRPRT
JRST KRSR ;Kill fork, release jfn, and set jobname
INFERR: AOS ERRFLG ;Fork error
MOVE B,FORK
CALL ERRPRT
JRST KRSR ;Kill fork, release jfn, and set jobname
;$SCOPY - Copy strings
;Takes A/ destination string pointer
; B/ source string pointer
; E/ chars remaining in destination buffer
;Returns updated string pointers in A and B, number of characters remaining in
;in destination buffer in C and E, last byte transferred in D
$SCOPY: JUMPLE E,RET1 ;A no-op if destination buffer full
MOVEI C,(E) ;Remember destination buff size
MOVEI D,0 ;End on null byte
CALL $SOUT ;Output to buffer
MOVEI E,(C) ;Update destination buff size
RET
;$SOUT - Copy one string to the end of another. Essentially core-to-core SOUT,
;except never copies break char.
;Takes AC1/ destination pointer
; AC2/ source pointer
; AC3/ maximum byte count
; AC4/ break char
;Returns: +1 always, updated pointers in AC1 and AC2, updated count in AC3,
; last byte transferred in AC4
$SOUT: PUSH P,E
MAKPTR A ;Make fake pointers real
MAKPTR B
JUMPE C,$SOUT0 ;Zero byte count, zerobyte terminates
JUMPG C,$SOUT1 ;Positive byte count, use count and break
$SOUT2: ILDB E,B ;Negative byte count, use count only
IDPB E,A
AOJL C,$SOUT2 ;Loop while count negative
$SOUT3: MOVEI D,(E)
POP P,E
RET
$SOUT4: IDPB E,A ;Zero byte count, zerobyte terminates
$SOUT0: ILDB E,B
JUMPN E,$SOUT4 ;Loop while not a zerobyte
JRST $SOUT3
$SOUT1: ILDB E,B ;Positive byte count, use break char and count
CAIN E,(D) ;Check for break char
JRST $SOUT3
IDPB E,A
SOJG C,$SOUT1 ;Loop while count positive
JRST $SOUT3
;DECOUT - Outputs a decimal number.
;Takes A/ destination string pointer
; B/ number
; E/ count of bytes remaining in destination buffer
DECOUT: IDIVI B,^D10 ;Fetch rightmost digit into C.
PUSH P,C ;Store retval in LH of retaddr.
CAIE B,0 ;Have we reached 0 yet?
CALL DECOUT ;No, call recursively for next digit.
POP P,C ;Fetch retval.
JUMPE E,RET1 ;Check for overflow.
SUBI E,1 ;Ok, account for new char.
ADDI C,"0" ;Convert digit to ascii.
IDPB C,A ;Send digit.
RET ;Return, back to DECOUT, or eventually, out.
;ACSAVE - Saves all acs on stack with automatic restore
ACSAVE: ADJSP P,17 ;Allocate 17 more spaces on stack
MOVEM 16,16-16(P) ;Save ac 16 at top of stack
MOVEI 16,0-16(P) ;16/ 0,,<space on stack for ac 0>
BLT 16,15-16(P) ;Save acs 0 through 15
MOVE 16,16-16(P) ;AC, what ac? i didn't use any ac
CALL @-1-16(P) ;Call caller
SKPA ;Caller returned +1, skip incrementing
AOS -2-16(P) ;Returned +2, increment return for skip
MOVSI 16,0-16(P) ;16/ <space on stack with ac 0>,,0
BLT 16,16 ;Restore acs 0 through 16
ADJSP P,-20 ;Deallocate space allocated, plus caller
RET ;Return to caller's caller
;Error routines
DATFAI: HRROI A,[ASCIZ \Can't get at BBOARD's MAIL.IDX - \]
CALLRET ESOERR
PRSFAI: HRROI A,[ASCIZ \Bboard mail file format funny - \]
CALLRET ESOERR
PTRERR: HRROI A,[ASCIZ \Error during file pointer manipulation - \]
; CALLRET ESOERR ;Falls through
ESOERR: ESOUT% ;Print error message
; CALLRET ERRF ;Falls through
ERRF: TXO F,F%ERR ;Say there was an error
JSMSG0: MOVEI B,.FHSLF ;Error in self
CALLRET ERRPRT ;Go print last error for this fork
ERROUT: ESOUT% ;Print error message
CALLRET ERRRET ;Exit with error flag
STOP: MOVX A,.FHSLF ;Current file handle
CLZFF% ;Close anything we can
ERJMP .+1
HRROI A,-1 ;-1 for all JFNS
RLJFN% ;Release anything we can
ERJMP .+1
RESET% ;Reset
HALTF% ;Must halt here... Can't continue
HRROI A,[ASCIZ \Can't continue\]
ESOUT%
JRST STOP
;ERRPRT - Prints latest system error for specified process
;Takes B/ fork handle (.FHSLF for current process)
ERRPRT: MOVEI A,.PRIOU ;Output to primary output
HRLOS B ;Tell which fork from parameter
SETZ C,
ERSTR%
ERJMP .+2
ERJMP .+1
TMSG < --> ;Get ready to output which bboard bombed
CALL TYPNAM ;Do it
CALLRET OCRLF ;End the output line
;ENDMSG - Print the message at the end of a bboard
ENDMSG:
IFN ENDMSW,< ;Some don't want to know when bboard done
IFXE. F,F%MSGB ;Skip output if no msgs offered, clear flg
HRROI A,[ASCIZ \ - nothing new
\] ;Message for /ACTION
TXNE F,F%ACT ;Did we request that?
PSOUT% ;Yes, say we're finished reading
RET ;Then continue
ENDIF.
MOVEI A,5 ;Say to check for 5 lines
CALL MORCHK ;Go do it
CALL OCRLF
TMSG <(End of> ;The old message
SETZ A, ;No optional leading string
TXNE F,F%NAM!F%ACT ;Want name output?
CALL TYPNAM ;Type out the filename (if specified)
TMSG < bulletins.)
>
>;IFN ENDMSW
RET
;CLNUP - Clean up after doing a bboard
;You get here when you have seen all the messages there are.
; Thus you can safely ignore this file until its write date
; changes. Note that Q and E don't get here.
CLNUP: MOVE A,FDB+.FBWRT ;Get file write date
TXNN F,F%QUIT!F%CHK ;Don't update if quitting or checking
MOVEM A,PG.WRT(X) ;Save it
SETO A, ;Unmap data page
MOVE B,DATPAG ;Page for DATA file
HRLI B,.FHSLF
SETZ C,
PMAP%
SETO A, ;Unmap data page
MOVE B,DATPG1 ;Page for DATA file
HRLI B,.FHSLF
SETZ C,
PMAP%
HRR B,TXTPAG ;Pages for the TXT file
MOVE C,TXTPGS
TXO C,PM%CNT
PMAP%
MOVE A,BBOFIL(BBN) ;Close the files (but retain others)
CLOSF%
TRN
MOVE A,BBDFIL(BBN)
CLOSF%
TRN
RET
;BYE - End of running BBOARD program. Say bye-bye
BYE: TXNE F,F%PAUS ;Does user want to pause
TXNN F,F%MSGS ;Only do that if any msgs output
JRST STOP ;Didn't want to pause or didn't need to
TXNE F,F%QUIT ;If he last said Quit,
JRST STOP ;Just halt
CALL OUTON ;Make sure we see this
MOVEI A,3 ;Allow for 3 lines
CALL MORCHK
IFE SMSGSW,<
TMSG <
[Bboards done, press any key to proceed...]> ;Be less confusing
>;IFE SMSGSW
IFN SMSGSW,<
TXNN F,F%SYS
IFSKP.
TMSG <
[SYSTEM Bulletins done, press any key to proceed...]> ;Be less confusing
ELSE.
TMSG <
[Bboards done, press any key to proceed...]> ;Be less confusing
ENDIF.
>;IFN SMSGSW
MOVEI A,.PRIIN ;For the primary input
CFIBF% ;Clear input buffer
PBIN% ;Now read a char
MOVEI A,.PRIIN ;For the primary input
CFIBF% ;Clear input buffer
JRST STOP
;INTIME - Jacket routine for IDTIM%
;Takes A/ pointer to the date/time in the Date/Byte count/Flags line
;Returns +1 - error
; +2 - success, B/ time of the message
INTIME: IDTIM% ;Read the time from mapped memory
TDZA D,D ;Error? ok, we won't skip
MOVEI D,1
SETZM EOF ;Clear any EOF flag
MOVEI C,@A ;Find out how far it went
CAMGE C,TXTEOF ;But set it if went too far
JRST INTIM2
SETOM EOF
RET
INTIM2: SKIPE D
AOS (P)
RET
;NUMIN - Translates a string into a number
;Takes A/ address of source string
; B/ positive number
; C/ radix (two through ten)
;Returns +1, error
; +2, success, @A/ updated, D/ break char
NUMIN: ILDB D,A
CAIE D," " ;Skip spaces
CAIN D,.CHTAB ;And tabs
JRST NUMIN
CAIL D,"0" ;Better be a digit
CAIL D,"0"(C)
IFNSK.
HRROI A,[ASCIZ \Error during parsing a number\]
ESOUT%
RET
ENDIF.
MOVEI B,-"0"(D) ;Convert first digit
NUMLP: ILDB D,A ;Read another
CAIL D,"0" ;Digit or break char?
CAIL D,"0"(C)
JRST NUMRET
IMULI B,(C) ;Update number
ADDI B,-"0"(D)
JRST NUMLP
NUMRET: CAIE D,.CHCRT ;On <CR> need to check for <LF>
JRST RET2
PUSH P,B ;This one's our retval
PUSH P,C ;Might as well not destroy the radix
MOVE B,A ;Look at next byte
ILDB D,B
CAIN D,.CHLFD ;If it's a <LF> then skip it, else return <CR>
MOVEM B,A
POP P,C
POP P,B
MOVEI D,.CHCRT
RETSKP
;CTLCON - Turns on Control-C interrupt catching
CTLCON: MOVEI A,.FHSLF ;Set up ^C trap
MOVE B,[LEVTAB,,CHNTAB] ;Interrupt tables
SIR%
EIR% ;Enable system
MOVX B,1B<INTCH1> ;Activate channels
AIC%
MOVE A,[.TICCC,,INTCH1] ;Assign ^C to channel 1
ATI%
ERJMP .+1 ;Don't care if no ^C's allowed
RET
;Jump here on a Control-C
CTRLC: PUSH P,A ;Save A
MOVEI A,.PRIOU
CFOBF% ;Clear output buffer
TMSG <^C
> ;Inform user
CIS% ;Clear system
HALTF% ;Stop for now
POP P,A ;Restore A
SOS PC1 ;This is just a stop-gap measure!
JRST @PC1 ;But continue if he wants to
;INIT - One-time initialization
INIT: CALL CTLCON ;Trap on Control-C
IFN UTEXSW,< ;[UT-2]
CALL .MORE ;[UT-2] Blanking is default
>;IFN UTEXSW
HRROI A,[ASCIZ \POBOX:\] ;See if POBOX: is defined here
STDEV% ;Is it?
IFJER. ;Nope...
MOVEI A,.CLNJB ;Want to create the logical
HRROI B,[ASCIZ \POBOX:\] ;The logical
HRROI C,[ASCIZ \PS:\] ;What to make it
CRLNM% ;Create it
ERJMP .+1 ;Ignore errors
ENDIF.
MOVE A,[FIELDS,,HEAD1] ;Move from FIELDS to HEAD1
BLT A,HEAD1+FLDNUM-1 ;Move FLDNUM (=8) of them
SETZM BBOFIL ;Zero out the files list from possible
MOVE BBN,[BBOFIL,,BBOFIL+1] ;Previous session (i.e. make this
BLT BBN,BBDFIL+BBNMAX-1 ;Program serially re-usable)
SETZM BBN ;Init flags
MOVEI A,.PRIOU ;For the current terminal
SETZ B,
SFPOS% ;Fake the monitor
HRROI A,[ASCIZ \BBD:\] ;See if BBD: exists
STDEV%
TDZA X,X ;Use X as a flag saying "bbd: exists or not"
SETO X, ;Zero means doesn't exist. This routine only
JUMPE X,INIT0 ;If it doesn't exist, go around
MOVX A,GJ%SHT!GJ%OFG ;Get parse only JFN
HRROI B,[ASCIZ \BBD:\]
GTJFN%
ERJMP INIT0
HRRZ B,A ;Get JFN in right place
HRROI A,DEVICE ;Store device name for GTJFN% later
MOVX C,FLD(.JSAOF,JS%DEV)
JFNS%
HRROI A,DIRECT
MOVX C,FLD(.JSAOF,JS%DIR)
JFNS%
HRROI A,DEFBBD
HRROI B,[ASCIZ \BBD:MAIL.TXT\]
SETZ C,
SOUT%
HRROI A,DEFDAT
HRROI B,[ASCIZ \BBD:MAIL.IDX\]
SOUT%
JRST INIT1 ;And go on
INIT0: HRROI A,DEVICE
HRROI B,[ASCIZ \PS\]
SETZ C,
SOUT%
HRROI A,DIRECT ;Default names if BBD: not defined
HRROI B,[ASCIZ \BBOARD\]
SOUT%
HRROI A,DEFBBD
HRROI B,[ASCIZ \POBOX:<BBOARD>MAIL.TXT\]
SETZ C,
SOUT%
HRROI A,DEFDAT
HRROI B,[ASCIZ \POBOX:<BBOARD>MAIL.IDX\]
SOUT%
INIT1: SKIPN X ;BBD?
SKIPA B,[POINT 7,[ASCIZ \POBOX:<BBOARD>\]]
HRROI B,[ASCIZ \BBD:\]
HRRO A,FILNAM(BBN) ;Set up the default filename
SETZ C,
SOUT%
SETOM INTTIM ;Say no time seen yet
IFE OLDSW,<
GTAD% ;Get the present time
SUB A,[DAYAGO,,0] ;Calculate 30 days ago
MOVEM A,LNGAGO ;Store for later usage
>;IFE OLDSW
CALL BLDKEY ;Build the list of keywords for comnd
;Figure out where to put DATPAG
HLRZ A,.JBSA ;Find end of file
ADDI A,777 ;Find first free page after everything
LSH A,-<^D9>
MOVEM A,DATPAG ;Here it is
ADDI A,1
MOVEM A,DATPG1 ;Data page for write access is next
;TXTPAG is the next page
ADDI A,1
MOVEM A,TXTPAG
MOVEI A,.PRIOU ;From our tty
MOVEI B,.MORLW ;Read the line width
MTOPR%
SKIPN C ;Does luser have width set?
MOVEI C,^D80 ;Nope, assume ^D80 for them
MOVEM C,NCOLS ;Save it away
MOVEI B,.MORLL ;Read the page length
MTOPR%
SKIPN C ;Luser have a length set?
MOVEI C,^D24 ;Assume ^D24 for them
SOS C ;Avoid fencepost
MOVEM C,NLINES ;Save it away
RET
;NEWRDR - Tell new users how to see older messages
;Returns PG.RD(X) and PG.WRT(X)/ time representing yesterday at 12:01AM
NEWRDR: TXNE F,F%CHK ;If checking, no message
RET ;Skip it
PUSH P,A ;Save JFN
MOVEI A,^D8 ;Say to check for 8 lines
CALL MORCHK ;Go do it
HRROI A,[ASCIZ \
Messages on BBoard older than yesterday are suppressed for new readers. Use
BBOARD 1-JAN-80\]
IFXN. F,F%DEF
PSOUT% ;If using the default, be sure to displaly msg
ELSE.
CALL TYPNAM ;If not the default, also display the name
ENDIF.
TMSG <
to read older messages. From now on, you will get all new messages.
> ;Explain to neophytes
GTAD% ;Get current time and date
LSH A,-<^D18> ;Get day into right half
SUBI A,1 ;Go to yesterday at 12:00:01AM
LSH A,^D18 ;Realign
MOVEM A,PG.RD(X) ;Set all his times to this
MOVEM A,PG.WRT(X)
MOVEM A,OLDTIM ;Set it as time to compare with
POP P,A ;Restore jfn
RET
;PRTNAM - Print the name of the bboard file
;Takes FILNAM(BBN)/ File name
PRTNAM: PUSH P,A ;Save JFN
PUSH P,B ;If not in the first column
MOVEI A,3 ;Say to check for 3 lines
CALL MORCHK ;Go do it
MOVEI A,.PRIOU ;Then put in a crlf
RFPOS%
TXNE B,.RHALF
CALL OCRLF
HRROI A,[ASCIZ \Reading file:\]
CALL TYPNAM ;Type file name
CALL OCRLF ;Tie off with a nice, clean crlf
POP P,B ;Restore B
POP P,A ;Restore JFN
RET
;TYPNAM - Routine to type out the file name (and leader) if one was specified
;Takes A/ either 0 or a string to print before the file name
TYPNAM: SKIPE A ;A leader?
PSOUT% ;Yes, then type it
IFE SMSGSW,<
TXNE F,F%DEF ;Default BBD is a special case
>;IFE SMSGSW
IFN SMSGSW,<
TXNE F,F%DEF!F%SYS ;Default BBD and SYSBBD are a special case
>;IFN SMSGSW
JRST TYPNA0
SKIPN FILNAM(BBN) ;If nothing here, then done
RET
MOVEI A," " ;Output a leading space
PBOUT%
HRRO A,FILNAM(BBN) ;And not the file name specified
TXNE F,F%DEF ;Use a nicer name
TYPNA0: HRROI A,[ASCIZ \ BBoard\]
IFN SMSGSW,<
TXNE F,F%SYS ;System messages?
HRROI A,[ASCIZ \ SYSTEM:\] ;Yes
>;IFN SMSGSW
PSOUT%
RET
;SAVBAK - Save the present file pointer
;Normally does a skip return. Non-skip return on a pointer error
SAVBAK: MOVE B,TXTPTR ;Get the file pointer
SOSL C,BAKCNT ;Find where to stash it
IFSKP.
MOVEI C,BAKMAX-1 ;If we have to wrap around
MOVEM C,BAKCNT ;Then reset things
ENDIF.
MOVEM B,BAKPTR(C) ;Stash it in right spot
RETSKP
;DECBAK - Decrement BAKCNT by one
;Returns B/ new value for BAKCNT
DECBAK: MOVE B,BAKCNT ;Get the position in the back pointer array
MOVEI B,1(B) ;Get the previous position
CAIL B,BAKMAX ;Need to wrap around?
MOVEI B,0 ;Yes.
MOVEM B,BAKCNT ;Reset count
RET
;SAVDAT - Save date, if needed
;Takes DAT/ date/time of present msg
; (X)/ date/time of newest message read
;Returns (X)/ updated if necessary
SAVDAT: CAMLE DAT,(X) ;If date and time gt saved time,
MOVEM DAT,(X) ;Save it
RET
;OUTON - Clears any ^O condition on primary output
OUTON: PUSH P,A ;Don't want ^O suppression
PUSH P,B ;Use no AC's
MOVEI A,.PRIOU
RFMOD%
TXZE B,TT%OSP ;Maybe don't have to set mode
SFMOD%
POP P,B
POP P,A
RET
RESCAN: MOVEI A,.RSINI ;Select rescan buffer
RSCAN%
JSERR
JUMPE A,R ;Return if nothing there
CALL CMDINI ;Init COMND% package
PROMPT (BBoard>) ;Init COMAND% (does .CMINI)
MOVEI A,[FLDDB. .CMKEY,,PGMTAB]
CALL RFLDE ;Collect program name, start, etc.
IFNSK.
HRROI A,[ASCIZ \Unrecognized program name\]
ESOUT%
JRST ERREAT ;Garbage...
ENDIF.
HRRZ B,(B) ;Get indicator
JUMPE B,EAT ;If not program name, forget this line
RSCLUP: HRLI A,DFLTBK ;Build a blt pointer
HRRI A,CJFNBK ;For defaults
BLT A,CJFNBK+DFLTLN-1 ;Set up default bboard file
MOVEI A,[FLDDB. .CMTAD,,<CM%IDA!CM%ITM>,,,[ ;Date and time
FLDDB. .CMTAD,,CM%IDA,,,[ ;Or just date
FLDDB. .CMTAD,,CM%ITM,,,[ ;Or just time
FLDDB. .CMCFM,,,,,[ ;Or confirm
FLDDB. .CMSWI,,SWTTAB,,,[ ;Or a switch
FLDDB. .CMTOK,CM%SDH,<-1,,[ASCIZ |/|]>,,,[ ;Or a slash
FLDDB. .CMCMA,,,,,[ ;Might be comma
FLDDB. .CMKEY,,FILKEY,,,[ ;Or keyword
FLDDB. .CMFIL,,]]]]]]]]] ;Or filename (.CMFIL must be last)
CALL RFLDE
JRST ERREAT ;What's wrong?
LOAD A,CM%FNC,<(C)> ;Get function which matched
CAIN A,.CMCMA ;Was it a comma?
JRST RSCCMA ;Yes, process it
TXZ F,F%CMA ;Clear "seen a comma before" bit
CAIN A,.CMTAD ;Did we get a date?
JRST RSCTAD ;Yes, go process that
CAIN A,.CMFIL ;Did we get a filespec?
JRST RSCFIL ;Yes, do that up
CAIN A,.CMKEY ;Did we get a keyword?
JRST RSCKEY ;Yes, do that up
CAIE A,.CMTOK ;A / (bad switch)
IFSKP.
MOVX A,-1 ;Backup .CMPTR to point to the /
ADJBP A,SBK+.CMPTR
MOVEM A,SBK+.CMPTR
JRST ERREAT ;Simulate a bad parse
ENDIF.
CAIE A,.CMSWI ;A switch?
RET ;No, must be a confirm - return
MOVE A,(B) ;Get the switch routine address
CALL (A) ;Go do it
JRST RSCLUP ;Go parse more
IFN FACSW,<
FAC: MOVX A,GJ%SHT!GJ%OLD ;Short old GTJFN%
HRROI B,[ASCIZ \<FACULTY>MAIL.TXT\] ;Faculty MAIL.TXT
GTJFN% ;Find it
JHLT ;Quit if cant find
MOVEM A,BBOFIL(BBN) ;Save JFN allowing for more than 1
MOVX A,GJ%SHT!GJ%OLD ;Short and old
HRROI B,[ASCIZ \<FACULTY>MAIL.IDX\] ;MAIL.IDX too
GTJFN% ;Find it
JHLT
MOVEM A,BBDFIL(BBN) ;Save JFN allowing for more than 1
HRRO A,FILNAM(BBN)
HRROI B,[ASCIZ \POBOX:<FACULTY>\] ;Save name for end message
SETZ C,
SOUT%
RET
CLASS: SETO A,
SETZM ATMBUF+1 ;Clear
SETZM ATMBUF+2
HRROI B,ATMBUF
GACCT% ;Get users account
MOVEI A,"<" ;Start of file name
DPB A,[POINT 7,BUF,6] ;Start build of file name
MOVX A,GJ%SHT!GJ%OLD
HRROI B,[ASCIZ \<ACCOUNTS>FACULTY.LIST\]
GTJFN% ;FACULTY/CLASS file
ERJMP QERA ;Go on err
MOVX B,FLD(7,OF%BSZ)!OF%RD
OPENF% ;Open it up
ERJMP QERA ;Go on err
QLPB: MOVE B,[POINT 7,BUF,6] ;Build area for file name
MOVEI C,^D40 ;Max len
MOVEI D,^D13 ;CR
SIN% ;Read in faculty name
CAIN C,^D40 ;EOF?
JRST QERA ;Yes, err
MOVE E,B ;Save
BIN% ;Skip LF
MOVE B,E ;Restore
MOVEI C,"." ;Put dot in file name
DPB C,B
MOVEI C,^D40 ;Max len
MOVEI D,^D13 ;CR
SIN% ;Read in associated account
SETZ C,
DPB C,B ;Terminate
SETZM DASHPT ;Indicate no subsection # found
MOVE D,[POINT 7,ATMBUF] ;Ptr to logged in acct
QLPA: ILDB B,E ;Compare accts
ILDB C,D ;Damn what a stupid instr. set
TRZ B,40 ;make upper case
TRZ C,40 ;make upper case
CAME B,C ;Same?
JRST NXTENT ;No, br
CAIN B,^O15 ;Start of sub-section #(upper case -)
MOVEM E,DASHPT ;Yes, save
JUMPN B,QLPA ;Loop if not fini
CLOSF% ;Close file
NOP
QLPC: MOVE B,E ;Restore pointer
MOVEI A,">" ;Overlay CR
DPB A,B ;With end of deirectory name symbol
MOVE A,B ;Get end of file name so far
MOVE E,B ;Save ptr to name so far
HRROI B,[ASCIZ \MAIL.TXT\]
SETZ C, ;No max
SOUT% ;Finish building file name
IDPB C,A ;Drop in a closing null
MOVX A,GJ%OLD!GJ%SHT ;Short old JFN
HRROI B,BUF ;Name of MAIL.TXT file
GTJFN% ;JFN
ERJMP QERA ;Go on err
MOVEM A,BBOFIL(BBN) ;Save JFN allowing for more than 1
MOVE A,E ;Restore ptr
HRROI B,[ASCIZ \MAIL.IDX\] ;Build MAIL.IDX filename
SETZ C,
SOUT%
IDPB C,A ;Drop in a null
MOVX A,GJ%OLD!GJ%SHT ;Short old JFN
HRROI B,BUF ;Name of MAIL.IDX file
GTJFN% ;JFN
ERJMP QERA ;Go on err
MOVEM A,BBDFIL(BBN) ;Save JFN allowing for more than 1
HRRO A,FILNAM(BBN)
HRROI B,ATMBUF ;Get account
SETZ C,
SOUT% ;Save for end message
RET
NXTENT: MOVE B,E ;Read in area
MOVEI C,^D150 ;Max len
MOVEI D,^D13 ;CR
SIN% ;Read in faculty name
SIN% ;Skip quota info
SIN%
SIN%
SIN%
BIN%
JRST QLPB ;Loop
QERA: SKIPN E,DASHPT ;Was there a section #
JRST QSKC ;No, give up
SETZM DASHPT ;Only once
JRST QLPC ;Yes, try stripping it off
QSKC: HRROI A,[ASCIZ \Can't get class BBoard\]
ESOUT% ;Errmsg out
JSHLT ;Quit
>;End of IFN FACSW
;We've rescanned a filename
RSCFIL: SKIPN BBOFIL(BBN) ;No comma between file names
IFSKP.
TMSG <
% Error in specifying bboard file name (Missed comma?). Continuing
> ;Tell him he goofed
ENDIF.
MOVEM B,CURJFN
RSCWFL: CAIL BBN,BBNMAX ;Create the loop for wildcards
JRST RSFLUP ;If at maximum, then go to the file incrementer
HRROI A,CURFIL ;Create the filename
HRRZ B,CURJFN ;From the current Jfn
MOVX C,JS%SPC
JFNS%
SETZ C, ;Get a null
IDPB C,A ;Drop it in
MOVX A,GJ%OLD!GJ%SHT ;Short form, old file
HRROI B,CURFIL
GTJFN%
ERJMP RSFLUP ;File doesn't exist
MOVEM A,BBOFIL(BBN) ;Save bboard file name
HRROI A,CURFIL ;Create a subname of the file
HRRZ B,CURJFN
MOVX C,FLD(.JSAOF,JS%DEV)!FLD(.JSAOF,JS%DIR)!FLD(.JSAOF,JS%NAM)!JS%PAF
JFNS% ;All but extension
MOVE E,BBN ;Create a pointer
CFILLP: SOJL E,CFILDN ;Check if we have this name
HRRO A,FILNAM(E) ;Before through the tables
HRROI B,CURFIL
STCMP%
JUMPN A,CFILLP ;If no match than continue
HRRZ A,BBOFIL(BBN) ;Got a match means we do
RLJFN% ;Release the current JFN
JFCL
SETZM A,BBOFIL(BBN) ;Zero the entry
JRST RSFLUP
CFILDN: HRROI A,BUF ;Point to buffer
HRROI B,CURFIL ;Create the data file
SETZ C,
SOUT%
HRROI B,[ASCIZ \.IDX\] ;Here is the extension
SOUT% ;Complete filename
IDPB C,A ;Drop in a null
HRRO A,FILNAM(BBN) ;Copy the file name specified here to
HRROI B,CURFIL ;From the one already created
SOUT%
IDPB C,A ;Drop in a null
MOVX A,GJ%OLD!GJ%SHT ;Old file, short GTJFN% form
HRROI B,BUF ;Point to name we constructed
GTJFN% ;Get a JFN on it
IFJER. ;On error
HRRZ A,BBOFIL(BBN) ;Release the current JFN
ENDIF.
RLJFN% ;Release the JFN
ERJMP .+1
RSFLUP: MOVE A,CURJFN ;Get the next file in the list
GNJFN%
IFNJE.
SKIPE BBOFIL(BBN) ;If we have an entry
AOS BBN ;Then update the counter
JRST RSCWFL ;Loop back and continue
ENDIF.
SKIPN BBOFIL(BBN) ;If no entry when we are done
SOS BBN ;The kill the last one
JRST RSCLUP
;Handle built keyword parse
RSCKEY: SKIPN BBOFIL(BBN) ;No comma between file names
IFSKP.
TMSG <
% Error in specifying bboard keyword (Missed comma?). Continuing
> ;Tell him he goofed
ENDIF.
HLRO B,(B) ;B had addr, addr had keywd-addr,,0
PUSH P,B ;Save for future reference
HRLI A,DFLTBK ;Build a blt pointer
HRRI A,CJFNBK ;For defaults
BLT A,CJFNBK+DFLTLN-1 ;Set up default bboard file
MOVX A,GJ%OLD
MOVEM A,CJFNBK+.GJGEN
MOVE A,[.NULIO,,.NULIO]
MOVEM A,CJFNBK+.GJSRC
MOVEI A,CJFNBK ;Where to find our data
GTJFN%
ERCAL [HRROI A,[ASCIZ \Can't GTJFN% \]
ESOUT%
HRRO A,(P) ;Output keyword
PSOUT%
HRROI A,[ASCIZ \ bboard file \]
PSOUT%
JSHLT] ;Die
MOVEM A,BBOFIL(BBN)
HRRO A,FILNAM(BBN) ;Copy the file name specified here
HRRO B,(P) ;Use saved keyword, not user's input
SETZB C,D
SOUT%
HRROI B,[ASCIZ \IDX\] ;Here is the extension
MOVEM B,CJFNBK+.GJEXT ;Make it the default extension
POP P,B ;Restore keyword as filename
MOVX A,GJ%OLD
MOVEM A,CJFNBK+.GJGEN
MOVE A,[.NULIO,,.NULIO]
MOVEM A,CJFNBK+.GJSRC
MOVEI A,CJFNBK ;Re-use GTJFN% block from before
GTJFN%
ERCAL [HRROI A,[ASCIZ \Can't GTJFN \]
ESOUT%
HRRO A,B ;Output keyword
PSOUT%
HRROI A,[ASCIZ \ bboard data file \]
PSOUT%
JSHLT] ;Die
MOVEM A,BBDFIL(BBN) ;Save data file jfn allow for 2+
JRST RSCLUP
;Handle a comma
;Want to say if a bare comma (no file before it) then act like
; the default file was read. Otherwise look for next
; file
;Right now, will just loop back
;now, we check if we had ",," and insert appropriate jfns if so
RSCCMA: TXNE F,F%CMA ;Already seen a comma?
IFSKP. ;No...
TXO F,F%CMA
CAIGE BBN,BBNMAX ;See if will be too many
IFSKP.
HRROI A,[ASCIZ \Too many bboards requested\]
ESOUT% ;Complain if too many
CALL ERRF
JRST STOP
ELSE. ;Else see if will be too many
AOS BBN
ENDIF.
JRST RSCLUP ;And go on
ENDIF.
TXZ F,F%CMA
MOVX A,GJ%OLD!GJ%SHT ;Try to get data file
HRROI B,DEFDAT ;Use the default data file name
GTJFN%
ERCAL [HRROI A,[ASCIZ \Can't GTJFN \]
ESOUT%
HRROI A,DEFDAT ;Output default filename
PSOUT%
JSHLT] ;And die
MOVEM A,BBDFIL(BBN) ;Save data file jfn
MOVE B,A
HRRO A,FILNAM(BBN) ;Keep file name here
MOVX C,FLD(.JSAOF,JS%NAM) ;Name only
JFNS% ;Output string
ERCAL [CALL ERRF ;Die with error
JRST STOP]
SETZ C, ;Get a null
IDPB C,A ;Drop it in
MOVX A,GJ%OLD!GJ%SHT ;Try to get .TXT file
HRROI B,DEFBBD ;Default bboard
GTJFN%
ERCAL [HRROI A,[ASCIZ \Can't GTJFN \]
ESOUT%
HRROI A,DEFBBD ;Output default filename
PSOUT%
JSHLT] ;And die
MOVEM A,BBOFIL(BBN) ;Save text file JFN
AOS BBN ;Bump up pointer
JRST RSCLUP
;Set magic flags according to switches given
ACTIO: TXOA F,F%ACT ;Set flag
NAME: TXO F,F%NAM ;Set the flag
RET
CHECK: TXOA F,F%CHK ;Light the right bit
PAUSE: TXO F,F%PAUS ;Set pause flag
RET
SMORE: TXO F,F%SCMR ;Light the bit
MORE: TXOA F,F%MORE ;Light the bit
LAST: TXO F,F%LAST
RET
;Scan the next token(s) for time and/or date
TAD: MOVEI A,[FLDDB. .CMTAD,,<CM%IDA!CM%ITM>,,,[
FLDDB. .CMTAD,,CM%IDA,,,[
FLDDB. .CMTAD,,CM%ITM,,,]]]
CALL RFLDE
IFNSK.
HRROI A,[ASCIZ \Expecting a date/time\]
ESOUT%
ADJSP P,-2 ;Pop off the return address here
JRST ERREAT
ENDIF.
RET
;Handle the /SET-TIME switch (or /DATE)
DATE: CALL .TAD ;Get the time and day
MOVEM B,NEWTIM ;Save the time to set to
RET
;Handle the /USE-TIME switch (or /FROM)
FROM: CALL .TAD ;Get the time and day
MOVEM B,INTTIM ;Save date and time to start at
RET
;[UT-3] Handle the /SECURE switch
SECUR: TXO F,F%SECU ;[UT-3] Indicate we want to be secure
RET
;We've rescanned a date and time without a switch
RSCTAD: MOVEM B,INTTIM ;Save date and time to start at
JRST RSCLUP
;Error routines for parsing
ERREAT: CALL OUTON ;Make sure it's seen
HRROI A,[ASCIZ \Can't parse: \]
ESOUT%
MOVE A,SBK+.CMPTR ;The unparsed stuff
PSOUT%
TMSG <
Expected command line of the form
where bbdnam1 is the name of a file (the directory defaults to BBD: and the
extension defaults to TXT). All the items are optional. The allowed switches
are: /NAME, /PAUSE, /LAST, /USE-TIME:{date} {time}, /FROM:{date} {time}
/SET-TIME:{date} {time}, /DATE:{date} {time}, /ACTION, /CHECK, /MORE,
/SCROLL-MORE, /SECURE> ;[UT-3] Added /SECURE
IFN FACSW,<
TMSG <, /CLASS, /FACULTY>
>;IFN FACSW
CALL OCRLF ;End the line
MOVEI A,.PRIIN ;Clear input buffer
CFIBF%
JRST EAT1
EAT: MOVEI A,[FLDDB. .CMTXT] ;Swallow to eol
CALL RFIELD
JFCL
EAT1: SETOM INTTIM ;Forget any time specified
SETZM BBOFIL(BBN) ;And any files
SETZM BBDFIL(BBN)
RET
;Build the keyword table by GNJFNing down the world
BLDKEY: HRLI A,DFLTBK ;Build a blt pointer
HRRI A,CJFNBK ;For defaults
BLT A,CJFNBK+DFLTLN-1 ;Set up default bboard file
MOVX A,GJ%OLD!GJ%IFG!GJ%FLG+1 ;Default generation of 1
MOVEM A,CJFNBK+.GJGEN
HRROI A,[ASCIZ \*\]
MOVEM A,CJFNBK+.GJNAM ;Make it *.TXT (.GJEXT is already .TXT)
MOVE A,[.NULIO,,.NULIO]
MOVEM A,CJFNBK+.GJSRC
MOVEI A,CJFNBK ;Where to find our data
SETZ B,
GTJFN%
JSHLT
MOVEM A,BLDJFN ;Store JFN
MOVEI X,1 ;Count of keywords
MOVE W,[POINT 7,KEYWDS] ;Beginning of spare string space
BLDLP: MOVE A,W ;Get next keyword space
HRLZM A,FILKEY(X) ;Put address in left half of table
HRRZ B,BLDJFN
MOVX C,FLD(.JSAOF,JS%NAM) ;Output only filename
JFNS%
MOVEM A,W ;Put back string pointer
IBP W ;Move over null for paranoia
AOS W ;Increment to next word boundary
HRLI W,(<POINT 7,>) ;Make initial pointer again
MOVE A,BLDJFN ;Get JFN back
GNJFN%
ERJMP ENDBLD
AOJA X,BLDLP ;Go get more
ENDBLD: MOVEM X,FILKEY ;When no more, make x,,x in filkey for comnd
HRLM X,FILKEY
RET ;And return
;Command parsing routines
P1=10 ;P1-P4 supposedly preserved through calls
P2=11
P3=12
P4=13
;Get here on COMND% error. Let user try again.
CMDERR: HRROI A,ERMES ;Point to message area
MOVE B,[.FHSLF,,-1] ;Ourself, most recent error
MOVSI C,-MESLN*5 ;Maximum string we've room for
ERSTR% ;Get error string
JFCL
JFCL ;Unexpected errors
HRROI A,ERMES ;Point at string
ESOUT% ;Print it in standard manner
;Come here to let user fix his error (by typing ^h) or issue another command
;Print error message before transferring here
CMDER1: SOS REPARA ;Modify reparse address so reprompt happens
JRST REPARS
;Place to transfer if user edits previously parsed fields
REPARS: MOVE P,CMDACS+P ;First restore P, how much stack to restore
HRLI A,CMDPDL ;Restore stack from saved stack
HRR A,CMDFRM ;Copy to bottom of stack
BLT A,(P) ;Restore the stack
MOVSI 16,CMDACS ;Make BLT pointer
BLT 16,16 ;Restore rest of AC's
JRSTF @REPARA ;Transfer back to just after .CMINI call
;Come here to prompt for new command or new prompt line of command.
;Call this routine with pointer to prompt in A, or 0 if no prompt.
DPROMP: CAIN A,0 ;Any prompt?
HRROI A,[0] ;No, point to a null string
MOVEM A,SBK+.CMRTY ;Save pointer to prompt
POP P,REPARA ;Remember reparse address
DMOVEM 0,CMDACS+0 ;Save AC's
MOVE 1,[2,,CMDACS+2]
BLT 1,CMDACS+17
HRL A,CMDFRM ;Save from bottom of stack
HRRI A,CMDPDL ;Move data to COMND% pdl area
HRRZ B,P ;See where top of stack is now
SUB B,CMDFRM ;Calculate number of words
BLT A,CMDPDL(B) ;Save the stack
PUSH P,REPARA ;Make stack like it was
PUSH P,SBK+.CMIOJ ;Save I/O JFNS
MOVE B,[.NULIO,,.NULIO] ;Get null JFNS
MOVEI A,.RSCNT ;Find count of rscan characters
RSCAN%
JSHLT ;Invalid function code?
SKIPE A ;If we're reading from rescan buffer
MOVEM B,SBK+.CMIOJ ;Do I/O to null this time
MOVEI A,[FLDDB. .CMINI] ;Type prompt
CALL RFIELD
POP P,SBK+.CMIOJ ;Restore I/O JFNS
RET ;Return to caller
;Read a field routine. Give it address of function block in A.
;JRSTs to CMDERR if error. A and B will have result of COMND% in them.
RFIELD: CALL RFLDE ;Read field, skip if success
JRST CMDERR ;Failed, go process error
RET ;Success
;Routine to read a field and skip iff successful. A, B, and C will have
;result of COMND% in them upon return.
RFLDE: MOVE B,A ;Put function block pointer in B
MOVEI A,SBK ;Pointer to state block in A
COMND% ;Read field of COMND%
ERJMP R ;Error in COMND%
TXNE A,CM%NOP ;Did command parse correctly?
RET ;No, single return
RETSKP ;Yes, skip return
;Read a field and require carriage return after it for confirmation
CFIELD: CALL RFIELD ;Read the field
DMOVEM A,VALUES ;Save data from field
CONFRM ;Get confirmation
DMOVE A,VALUES ;Get values of field
RET ;Return to caller
;COMND% initialization routine. Call only once at beginning of program
;Always call this routine at a less-than-or-equally nested location
; within the program in comparison with any subsequent call to the COMND%
; execution routines
CMDINI: MOVEI A,REPARS ;Reparse address
MOVEM A,SBK+.CMFLG
HRLI A,100 ;Set up I/O JFNS
HRRI A,101
MOVEM A,SBK+.CMIOJ
HRROI A,CMDBUF ;Pointer to command buffer
MOVEM A,SBK+.CMBFP
MOVEM A,SBK+.CMPTR ;Pointer to next field
MOVEI A,CMDBLN*5 ;Room for typin
MOVEM A,SBK+.CMCNT
SETZM SBK+.CMINC ;No unparsed characters yet
HRROI A,ATMBUF ;Pointer to atom buffer
MOVEM A,SBK+.CMABP
MOVEI A,ATMBLN*5
MOVEM A,SBK+.CMABC ;Room in atom buffer
MOVEI A,CJFNBK ;Pointer to JFN block
MOVEM A,SBK+.CMGJB
MOVEM P,CMDFRM ;Remember beginning of stack
RET
BLKLIN: SAVEAC <A,B,C> ;Save the AC's.
MOVEI A,.PRIOU ;To the terminal.
RFMOD% ;Get the tty info word.
PUSH P,B
TXZ B,TT%DAM ;Binary mode.
SFMOD%
MOVEI A,.CHCRT ;Get a carriage return.
PBOUT%
SKIPL TTYPE
IFSKP.
MOVEI A,.PRIOU ;Get terminal type index
GTTYP%
MOVEM B,TTYPE ;Save the terminal type
ELSE.
MOVE B,TTYPE ;Get terminal type
ENDIF.
CAIGE B,NCLRLN ;If more than table
SKIPE A,CLRLIN(B) ;Don't clear if wrong terminal type
IFSKP.
TMSG <
> ;Just print a crlf and finish.
JRST BLKLN2
ENDIF.
TLOE A,-1 ;More than 5 chars?
HRROI A,CLRLIN(B) ;No, use immediate.
PSOUT%
MOVEI A,.PRIOU
RFPOS% ;Get page position.
HLLZS B
SFPOS%
BLKLN2: MOVEI A,.PRIOU
POP P,B
SFMOD% ;Restore things.
RET
;MORCHK - Checks to see if there is AC1 lines left in the screen
MORCHK: TXNN F,F%MORE ;If using more processing
RET
PUSH P,A ;Save line count
MOVEI A,.PRIOU ;If previous message was long
RFPOS% ;Allow the user to be prompted
POP P,A ;Restore this counter
HLRZ C,B ;If we are past the number
ADD C,A ; of lines on the screen after
CAMG C,NLINES ; the four display lines
IFSKP.
CALL MORPMT ;Get the more prompt
JFCL ;Accept any answer
CALL BLKLIN ;Clear the line
ENDIF.
RET
;SUBBP - Subtracts the second byte pointer from the first resulting in
; the number of characters between them
;Takes A/ Byte pointer 1
; B/ Byte pointer 2
;Returns A/ 1-2 characters
SUBBP: SAVEAC <B,C,D>
MAKPTR A ;Make sure this is a byte pointer
MAKPTR B ;And this one too
MOVEI C,1
ADJBP C,B ;Put second pointer incremented in C
IBP A ;Now neither pointer is "44xx00,,"
MULI A,5 ;Multiply pointer by bytes per word
SUBI B,-4(A) ;B holds character address
MULI C,5 ;Do same to other pointer
SUBI D,-4(C)
SUB B,D ;Calculate difference
HRRE A,B ;Return answer in A.
RET