home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Media Share 9
/
MEDIASHARE_09.ISO
/
bbs
/
mfix430.zip
/
MAILFIX.BAS
< prev
next >
Wrap
BASIC Source File
|
1994-01-24
|
58KB
|
1,463 lines
' MAILFIX.BAS
' ***************************************************
' * MESSAGE REPAIR/PURGE UTILITY FOR MAIL MANAGER *
' * Copyright (C) 1991-94 Makai Software *
' ***************************************************
'
' -----------------------------------------------------------------------
' LICENSE AGREEMENT:
'
' You are free to modify, recompile, and run this code for your own use.
' If you use portions of it for a utility of your own design, you may do
' so, provided that your program is distributed free of charge, and that
' credit is given in your documentation for the portions of our code you
' have used.
'
' To use portions of this code "for profit" requires that you contact
' us for further arrangements. We can be reached at:
'
' Makai Software
' 870 Golden Drive
' Newark, OH 43055
'
' Submissions to re-release MailFix with your modifications installed
' are MORE THAN WELCOME. We will continue to release updates to MailFix
' as a free program, and will be more than happy to give you credit for
' your enhancements if your changes make it into a future MailFix release.
' -----------------------------------------------------------------------
'
' Major rewrite to accomodate fixed-length message bases - 101a 9/24/91
' Version # only - 101b 9/29/91
' Accomodate OverMail'ed message bases - 101c 10/02/91
' Version # only - 102a 10/05/91
' Version # only - 104 10/15/91
' Version # only - 110 11/19/91
' Version # only - 200 8/04/92
' Version # only - 300 1/02/93
' Slight modifications for distributed source - 301 2/14/93
' Block read/write of message bodies - 301a 2/28/93
' Major rewrite: uses i/o buffer blocks, renumbers msgs,
' sets user message pointers - 301b 3/04/93
' Misc bug fixes - 301c thru h 4/20/93
' Cleaned up for release - 400 4/28/93
' Extended from 999 to up to 2000 message capacity - 400a 6/21/93
' Added error checking to prevent lockup at msg # 2001 - 400b 6/22/93
' Released as - 401 7/14/93
' Increased msg capacity to 5000 - 401a 8/04/93
' Minor bug fix - 401b 8/16/93
' Released as - 402 8/18/93
' Added /P command line to purge received private msgs - 410 12/04/93
' Released as - 430 1/24/94
'-------------------------------------------------------------------------
' NOTE:
'
' This program is written to use Crescent Software's PDQ library, and
' Microsoft's QuickBASIC v4.50 compiler. Extensive recoding would be
' necessary to use this code in stock QuickBASIC.
'
' PDQDECL.BAS is PDQ's function and subprogram declaration file. PDQ on
' the link command line is PDQ.LIB. The "_*.obj" files on the link command
' line are the PDQ stub files used when creating the executable.
'
' Command lines used to compile/link:
'
' bc mailfix /o;
' link mailfix+_noval+_noread+_noerror+_nofield+_str$/nod/noe,,nul,pdq
'
'-------------------------------------------------------------------------
' $INCLUDE: 'PDQDECL.BAS'
'
' OutFile$ = "*.FIX" (fixed *M.DEF after the mailfix run)
' Z$ = Original "*.DEF" (the original *M.DEF file being read)
DECLARE FUNCTION PadOut$ (In1$, In2$)
DECLARE SUB Rotate ()
DECLARE SUB EndFix ()
DECLARE SUB GETT (filenum%, a$, endfile%) ' input buffer routine
DECLARE SUB PUTT (filenum%, a$) ' output buffer routine
DECLARE SUB PRINTT (a$) ' use pdq direct screen print
DECLARE SUB PRINTLF (a$) ' pdq direct screen print + crlf
DECLARE SUB Scroll () ' scroll screen
DECLARE SUB skip () ' skip blank screen line
DECLARE SUB Finish ()
TYPE CheckPoint ' Messages file checkpoint record.
LastMess AS STRING * 8 ' Highest message in this file.
AutoAdd AS INTEGER ' Security to auto-add conf user.
CallerNum AS STRING * 10 ' Caller number.
Reserved1 AS STRING * 36 ' 36 bytes of wasteland.
UsersUsed AS STRING * 5 ' User records taken in user file.
Reserved2 AS STRING * 6 ' 6 bytes of wasteland.
RecStart AS STRING * 7 ' Record # of beginning of msgs.
NextAvail AS STRING * 7 ' Next available message record #.
LastRec AS STRING * 7 ' Last record # (physically).
MaxMess AS STRING * 7 ' Max number of messages.
Reserved3 AS STRING * 31 ' 31 more bytes of wasteland.
MaxCopies AS STRING * 2 ' Total number of RBBS Nodes.
END TYPE
TYPE NodeRec ' Messages file node record.
LastUser AS STRING * 31 ' Last user on this copy of RBBS.
SysAvail AS INTEGER ' Sysop availability toggle.
SysAnnoy AS INTEGER ' Sysop annoy toggle.
SysNext AS INTEGER ' Sysop wants system next toggle.
LinePrint AS INTEGER ' Activity is being printed toggle.
DoorAvail AS INTEGER ' Are doors available?
EightBit AS INTEGER ' Possibly a flag for N,8,1?
Baud AS STRING * 2 ' User's baudrate (packed)
Upper AS INTEGER ' Does user want all upper case?
NumBytes AS LONG ' Number of bytes downloaded.
BatchXfer AS STRING * 1 ' Was last file Xfer a batch?
Graphics AS INTEGER ' User's graphics preference.
Sysop AS INTEGER ' Is user the sysop? (I think)
Active AS STRING * 1 ' Is this node active or waiting?
Snoop AS INTEGER ' Sysop Snoop toggle.
BaudLock AS STRING * 5 ' Is the baud rate locked?
TimeIn AS STRING * 3 ' I don't know...
Reserved1 AS STRING * 4 ' 4 bytes of wasteland.
PrivateDoor AS INTEGER ' Toggle for 'private door'
External AS STRING * 1 ' Was last Xfer via external proto?
XferLetter AS STRING * 1 ' Last Xfer protocal letter.
Reserved2 AS STRING * 1 ' a single byte of nothing.
PackDate AS STRING * 2 ' Packed date of logon.
Reserved3 AS STRING * 7 ' 7 bytes of space.
LastDOS AS STRING * 5 ' Last time dropped to dos.
Reliable AS INTEGER ' MNP flag.
City AS STRING * 24 ' City/State of user.
SubIndex AS STRING * 2 ' Dunno...
ProtoDate AS STRING * 6 ' Dunno...
ProtoTime AS STRING * 4 ' Dunno...
END TYPE
TYPE MessHeader ' Individual message header.
Private AS STRING * 1 ' * if private message.
MessNum AS STRING * 4 ' Message number. (Key field)
MessFrom AS STRING * 31 ' Who's it from?
MessTo AS STRING * 22 ' Who's it to?
TimeSent AS STRING * 8 ' What time was it sent (Key field)
NumHeaders AS STRING * 1 ' Number of msg headers
DateSent AS STRING * 8 ' What date was it sent (Key field)
Subject AS STRING * 25 ' Subject of message.
Password AS STRING * 15 ' Message password (if any)
Killed AS STRING * 1 ' Killed or active (226 or 225).
NumRecs AS STRING * 4 ' Number of msg recs (incl Header).
SecLev AS INTEGER ' Security level of message itself.
LastDate AS STRING * 3 ' Date msg last received (packed)
LastTime AS STRING * 3 ' Time msg last received (packed)
END TYPE
TYPE MessBody ' Main body of an RBBS message.
Text AS STRING * 128 ' Pretty self-explanitory.
END TYPE
TYPE RBBSUser ' User record in RBBS user file
Dummy1 AS STRING * 50
LastRead AS INTEGER
Dummy2 AS STRING * 76
END TYPE
' ********************
' * SHARED TYPE VARS *
' ********************
COMMON SHARED CheckPoint AS CheckPoint, _
Node AS NodeRec, _
Header AS MessHeader, _
Body AS MessBody, _
User AS RBBSUser ' User file record
COMMON SHARED ExitErr, _ ' Dos Errorlevel on exit
Version$, _
Copyright$
DIM SHARED Registers AS RegType ' Type for PDQ interrupt handling
DEFINT A-Z
TimeStart& = PDQTimer ' Save clock ticks to compute run
' time
Version$ = "v4.30" ' Current version number.
Copyright$ = "Copyright (C) 1991-94 Makai Software. All rights reserved."
MsgLim = 1000 ' Default value for size of message
' base to handle (no. of msgs.)
' 401b
IF RTRIM$(COMMAND$) = "" THEN EndFix ' If nothing on command line, show usage.
Z$ = UCASE$(COMMAND$) ' PDQ doesn't capitalize COMMAND$
'*********************************
'* PARSE COMMANDLINE FOR OPTIONS *
'*********************************
'Check for Dos screen writes first so
'any messages will be sent via
'correct method
DosPrint = INSTR(Z$, "/D") ' Check for /D
IF DosPrint THEN ' If found,
' adjust cmd line
Z$ = LTRIM$(LEFT$(Z$, DosPrint - 1) + MID$(Z$, DosPrint + 2))
END IF
CLS ' Show status info
PRINTLF "MailFIX " + Version$ + " - " + Copyright$
PRINTLF STRING$(79, 205)
PRINTLF "Run date " + DATE$ + " Run time " + TIME$
PRINTLF "Command line options: " + COMMAND$
skip
ViewFlag = INSTR(Z$, "/V") ' Check for /V
IF ViewFlag THEN
Z$ = LTRIM$(LEFT$(Z$, ViewFlag - 1) + MID$(Z$, ViewFlag + 2))
ViewFlag = -1
END IF
RBBSFlag = INSTR(Z$, "/R") ' Check for /R
IF RBBSFlag THEN
Z$ = LTRIM$(LEFT$(Z$, RBBSFlag - 1) + MID$(Z$, RBBSFlag + 2))
RBBSFlag = -1
END IF
OverMail = INSTR(Z$, "/O") ' Check for /O
IF OverMail THEN
IF RBBSFlag THEN
PRINTLF "Command line switches /R and /O cannot be used together."
ExitErr = 1
Finish
END IF
RBBSFlag = -1
Z$ = LTRIM$(LEFT$(Z$, OverMail - 1) + MID$(Z$, OverMail + 2))
END IF
FixedLen = INSTR(Z$, "/F") ' Check for /F
IF FixedLen THEN
Z$ = LTRIM$(LEFT$(Z$, FixedLen - 1) + MID$(Z$, FixedLen + 2))
FixedLen = -1
END IF
' ------- /P command line added v4.10 ------
PurgePriv = INSTR(Z$, "/P") ' Check for /P
IF PurgePriv THEN
Z$ = LTRIM$(LEFT$(Z$, PurgePriv - 1) + MID$(Z$, PurgePriv + 2))
PurgePriv = -1
END IF
' ------- End v4.10 addition -------
SlashK = INSTR(Z$, "/K") ' Is "/k" there?
IF SlashK THEN
IF ViewFlag THEN
PRINTLF "Command line switches /V and /Knnn cannot be used together."
skip
ExitErr = 1: Finish
END IF
ZZ$ = MID$(Z$, SlashK + 2) ' split cmd line after /K
Z$ = LEFT$(Z$, SlashK - 1) ' split cmd line before /K
Blank = INSTR(ZZ$, " ")
IF Blank THEN
keep = PDQValI(LEFT$(ZZ$, Blank - 1))
ZZ$ = MID$(ZZ$, Blank)
ELSE
keep = PDQValI(ZZ$)
ZZ$ = ""
END IF
IF keep < 1 THEN
PRINTLF "Invalid number specified with /K option."
ExitErr = 1: Finish
END IF
SlashK = -1
Z$ = LTRIM$(Z$ + ZZ$)
ZZ$ = ""
END IF
Renum = INSTR(Z$, "/N") ' Check for /N
IF Renum THEN
IF ViewFlag THEN
PRINTLF "Command line switches /V and /N cannot be used together."
skip
ExitErr = 1: Finish
END IF
ZZ$ = MID$(Z$, Renum + 2) ' split cmd line following /N
Z$ = LEFT$(Z$, Renum - 1) ' split cmd line before /N
IF MidChar(ZZ$, 1) <> 32 THEN ' if first char after /N
' isn't a space, then a
' user file was specified
Blank = INSTR(ZZ$, " ") ' find first blank
IF Blank THEN ' if there IS a blank
UserFile$ = LEFT$(ZZ$, Blank - 1) ' split out user file name
ZZ$ = MID$(ZZ$, Blank) ' remove filename from ZZ$
ELSE ' if no blank, end of cmd line
UserFile$ = ZZ$ ' save as filename
ZZ$ = ""
END IF
IF LEN(UserFile$) THEN UpdtU = -1 ' if a userfile, set flag
END IF
Z$ = Z$ + ZZ$: ZZ$ = "" ' recombine adjusted cmd line
Renum = -1
END IF
IF UpdtU THEN ' If asked to update user file
IF NOT PDQExist(UserFile$) THEN ' make sure we can find it
PRINTLF "Cannot find user file " + UserFile$ + "."
skip
ExitErr = 1: Finish
END IF
END IF
Siz = INSTR(Z$, "/S") ' Check for /S
IF Siz THEN
ZZ$ = MID$(Z$, Siz + 2) ' split cmd line following /S
Z$ = LEFT$(Z$, Siz - 1) ' split cmd line before /S
Blank = INSTR(ZZ$, " ")
IF Blank THEN
Siz = PDQValI(LEFT$(ZZ$, Blank - 1))
ZZ$ = MID$(ZZ$, Blank)
ELSE
Siz = PDQValI(ZZ$)
ZZ$ = ""
END IF
END IF
Z$ = Z$ + ZZ$
IF Siz > 0 then MsgLim = Siz ' MsgLim preset to default value
' at beginning of program. Reset
' if have new value. 401b
REDIM SeekIndex&(MsgLim) ' Array for storing msg location
' in .FIX file 400b
'-------------- End of command line parsing ------------------------------
Z$ = LTRIM$(RTRIM$(Z$)) ' At this point Z$ should be
' just the message filename.
IF Z$ = "" THEN EndFix ' If no file name, show usage.
IF PDQExist(Z$) THEN ' If file exists
OPEN Z$ FOR BINARY SHARED AS #1 ' Open it
ELSE ' If couldn't open, exit.
PRINTLF "Couldn't find " + Z$
ExitErr = 1: Finish
END IF
Z = INSTR(Z$, ".") ' Find period in filename.
IF Z > 0 THEN ' Set output file name.
OutFile$ = LEFT$(Z$, Z) + "FIX" ' (Always *.FIX).
ELSE
OutFile$ = Z$ + ".FIX"
END IF
Colon$ = ":" ' 2nd separator in time fld
IF RBBSFlag THEN Colon$ = "." ' period for RBBSMail
IF OverMail THEN Colon$ = ";" ' semicolon for OverMail
ColonFix$ = Colon$ + "00" ' For repair work only.
IF NOT ViewFlag THEN ' If we're not viewing,
IF PDQExist(OutFile$) THEN KILL OutFile$
OPEN OutFile$ FOR BINARY AS #2 ' Open the output file.
IF ERR THEN PRINTLF "Error opening " + OutFile$: ExitErr = 1: Finish
END IF
maxmem = 128 * 128 ' Desired memory for input buffer
' (64 recs @ 128 bytes each)
IF maxmem > FRE(a$) - 10240 THEN _
maxmem = (FRE(a$) - 10240) \ 128 * 128 ' reduce to leave 10k memory
IF LOF(1) < maxmem THEN maxmem = LOF(1) ' If input file shorter than buffer
' then adjust buffer size
bufrecs = maxmem \ 128 ' buffer length in 128-byte records
GettBlock$ = SPACE$(maxmem) ' Define multi-record buffer block
GET #1, , GettBlock$ ' Input initial block
GLoc = 1 ' Start at beginning of block
Block$ = SPACE$(128) ' Define block for checkpoint
GETT 1, Block$, FileErr ' Get checkpoint record from buffer
RecsRead& = 1& ' Update read counter
LastSave$ = RTRIM$(MID$(Block$, 1, 8)) ' Save initial info for
RecStart$ = RTRIM$(MID$(Block$, 68, 7)) ' later display.
NextAvail$ = RTRIM$(MID$(Block$, 75, 7))
LastRec$ = RTRIM$(MID$(Block$, 82, 7))
MaxMess$ = RTRIM$(MID$(Block$, 89, 7))
MaxCopies$ = RTRIM$(MID$(Block$, 127, 2))
MaxCopies = PDQValI(MaxCopies$)
IF FixedLen THEN ' If fixed-length,
TopMessage = PDQValI(LastSave$) ' Save last message #,
MaxRecs& = PDQValL(LastRec$) ' and total # of records.
END IF
IF NOT ViewFlag THEN ' If not "just lookin'"
PUTT 2, Block$ ' write it to output buffer
RecsWrote& = 1& ' update counter of recs written
END IF
PRINTLF "Press [Esc] to abort..."
skip
Block$ = ""
Block$ = SPACE$(128 * MaxCopies) ' Redefine block for node records
GETT 1, Block$, FileErr ' Read in as single block
RecsRead& = RecsRead& + MaxCopies
IF NOT ViewFlag THEN
PUTT 2, Block$
RecsWrote& = RecsWrote& + MaxCopies
END IF
' ***********************************
' * INDIVIDUAL MESSAGE PROCESSING *
' ***********************************
' ----------------------------------------------------------------------------
MsgCount = 0
MessNum$ = SPACE$(4)
NextMess: ' Branch here to keep
' stepping through msg
' file.
Headr$ = ""
Headr$ = SPACE$(128) 'Define for header record
GETT 1, Headr$, FileErr ' get msg header
IF INKEY$ = CHR$(27) THEN ' Allow [Esc] to break
PRINTLF "Aborted.": ExitErr = 1: Finish ' out of loop.
END IF
IF FileErr THEN ' If error reading msg
skip ' header, we're at EOF.
PRINTLF "End of messages at record #" + STR$(RecsRead&) + "."
IF NOT ViewFlag THEN ' If we're not viewing,
PRINTT "Updating checkpoint record..." ' update Checkpoint
IF LEN(PuttBlock$) THEN PUT #2, , PuttBlock$ ' flush output buffer
PuttBlock$ = ""
GET #2, 1, CheckPoint ' read in checkpoint
CheckPoint.LastRec = STR$(RecsWrote&) ' update variables
CheckPoint.NextAvail = STR$(RecsWrote& + 1&)
CheckPoint.LastMess = LastMess$
PUT #2, 1, CheckPoint ' write updated
' checkpoint to file
ELSE
GET #1, 1, CheckPoint
END IF
CLOSE
IF NOT ViewFlag THEN PRINTLF "done." ' Updated Checkpoint.
skip
Line1$ = PadOut$(LastSave$, RTRIM$(CheckPoint.LastMess)) ' Prepare
Line2$ = PadOut$(RecStart$, RTRIM$(CheckPoint.RecStart)) ' for clean
Line3$ = PadOut$(NextAvail$, RTRIM$(CheckPoint.NextAvail)) ' display
Line4$ = PadOut$(LastRec$, RTRIM$(CheckPoint.LastRec)) ' below...
Line5$ = PadOut$(MaxCopies$, RTRIM$(CheckPoint.MaxCopies)) ' ...
Line6$ = PadOut$(MaxMess$, RTRIM$(CheckPoint.MaxMess)) ' ...
Line7$ = PadOut$(STR$(MsgsWritten), STR$(MsgsWritten)) ' ...
PRINTLF " Original MailFix"
PRINTLF " -------- -------"
PRINTLF " Last message number : " + Line1$
PRINTLF "Msg Record starts at : " + Line2$
PRINTLF " Next available : " + Line3$
PRINTLF " Last Record : " + Line4$
PRINTLF " Node records : " + Line5$
PRINTLF " Maximum messages : " + Line6$
PRINTLF " Total active msgs : " + Line7$
skip
PRINTT STR$(RecsRead&) + " records read, " ' Update display
PRINTLF STR$(RecsWrote&) + " records written."
IF SlashK OR Renum THEN GOTO KeepFixed
Finish ' and we're done.
END IF
'*************************************************************
'* Above IF-END IF block executed only if errror encountered *
'* when reading in message header. Normally this will occur *
'* when end of file is reached. *
'* *
'*If message header reads in ok, continue below... *
'*************************************************************
RecsRead& = RecsRead& + 1& ' Update # of recs read
' Check for what constitutes an invalid message header. Current checks are:
'
' Message number = 0
' Killed flag not set
' Number of message records < 1
'
' Other useful variables:
'
' MaxRecs& = Total number of message records (fixed length only)
' TopMessage = Highest message number in this base (fixed length only)
'
MessNum = PDQValI(MID$(Headr$, 2, 4)) ' Determine msg #
IF FixedLen AND MessNum = 0 AND PDQValI(MessNum$) >= TopMessage THEN ' and last msg was hi,
' time to pre-format.
skip
PRINTLF "End of messages."
IF NOT ViewFlag THEN ' If not just lookin'..
PRINTT "Preformatting " + STR$(MaxRecs& - (RecsWrote& + 1)) + " records : "
StartFormat = RecsWrote& + 1& ' Begin after last msg
Block$ = SPACE$(128) ' Define empty record
FOR i = StartFormat TO MaxRecs& ' For all remaining recs
PUTT 2, Block$ ' Write blank rec
RecsWrote& = RecsWrote& + 1&
Rotate
NEXT
LOCATE CSRLIN, POS(0) - 1
PRINTLF "Done."
END IF
skip
IF NOT ViewFlag THEN ' If we're not viewing,
PRINTT "Updating checkpoint record..." ' update Checkpoint
IF LEN(PuttBlock$) THEN PUT 2, , PuttBlock$ ' flush output buffer
PuttBlock$ = "" ' if necessary
IF MaxRecs& <> RecsWrote& THEN
PRINTLF STRING$(80, "-")
PRINTLF "** ERROR! ** Total records : " + STR$(RecsWrote&)
PRINTLF " Last record SHOULD have been : " + STR$(MaxRecs&)
skip
PRINTLF "Do *NOT* use " + OutFile$ + "!"
ExitErr = 1
Finish
END IF
GET #2, 1, CheckPoint ' Recall checkpoint.
CheckPoint.LastRec = STR$(RecsWrote&) ' Update info
CheckPoint.NextAvail = STR$(StartFormat) '
CheckPoint.LastMess = MessNum$ '
PUT #2, 1, CheckPoint ' Put back in file.
PRINTLF "done."
END IF
CLOSE
skip
Line1$ = PadOut$(LastSave$, RTRIM$(CheckPoint.LastMess)) ' Prepare
Line2$ = PadOut$(RecStart$, RTRIM$(CheckPoint.RecStart)) ' for clean
Line3$ = PadOut$(NextAvail$, RTRIM$(CheckPoint.NextAvail)) ' display
Line4$ = PadOut$(LastRec$, RTRIM$(CheckPoint.LastRec)) ' below...
Line5$ = PadOut$(MaxCopies$, RTRIM$(CheckPoint.MaxCopies)) ' ...
Line6$ = PadOut$(MaxMess$, RTRIM$(CheckPoint.MaxMess)) ' ...
Line7$ = PadOut$(STR$(MsgsWritten), STR$(MsgsWritten)) ' ...
PRINTLF " Original MailFix"
PRINTLF " -------- -------"
PRINTLF " Last message number : " + Line1$
PRINTLF "Msg Record starts at : " + Line2$
PRINTLF " Next available : " + Line3$
PRINTLF " Last Record : " + Line4$
PRINTLF " Node records : " + Line5$
PRINTLF " Maximum messages : " + Line6$
PRINTLF " Total active msgs : " + Line7$
skip
PRINTT STR$(RecsRead&) + " records read, " ' Update display
PRINTLF STR$(RecsWrote&) + " records written." ' ...
IF SlashK OR Renum THEN GOTO KeepFixed
Finish ' and we're done.
END IF ' End fixed-len check
MessNum = PDQValI(MID$(Headr$, 2, 4))
Killed$ = MID$(Headr$, 116, 1)
NumRecs = PDQValI(MID$(Headr$, 117, 4))
PrivRec = 0 ' 410
IF MID$(Headr$, 1, 1) = "*" THEN ' 410
MessR = ASC(MID$(Headr$, 123, 1)) ' 410
IF (MessR <> 0) AND (MessR <> 32) THEN ' 410
PrivRec = -1 ' 410 (Private, and received)
END IF ' 410
END IF ' 410
IF MessNum = 0 OR INSTR("ßΓ", Killed$) < 1 OR NumRecs < 1 THEN ' or no message records,
PRINTT "Skipping record #" + STR$(RecsRead&) ' this isn't a message
PRINTLF " - invalid Msg Header." ' header. Skip it by
GOTO NextMess ' branching back.
END IF
LSET MessNum$ = STR$(MessNum) ' At this point, we
' must have a valid
' header, so save the
' msg number for
' possible use later
' in updating CheckPoint
' record in output file.
PRINTT LEFT$(Headr$, 1) + MessNum$ + " " ' Print progress to
PRINTT MID$(Headr$, 6, 15) + " " ' screen.
PRINTT MID$(Headr$, 76, 25) + " "
PRINTT MID$(Headr$, 101, 15)
FixedIt = 0 ' We haven't fixed this
' message yet.
' ***************************************************
' * UPDATE KEY FIELDS IN DATE & TIME STAMP TO ALLOW *
' * PROPER MESSAGE PROCESSING. *
' ***************************************************
TimeSent$ = MID$(Headr$, 59, 8)
DateSent$ = MID$(Headr$, 68, 8)
' If date/time separators
' aren't "stock" ..
IF (MID$(TimeSent$, 3, 1) <> ":") _
OR (MID$(TimeSent$, 6, 1) <> Colon$) _
OR (MID$(DateSent$, 3, 1) <> "-") _
OR (MID$(DateSent$, 6, 1) <> "-") THEN
IF RBBSFlag THEN ' Maybe it's RBBSMail?
IF (INSTR("ßΓ", Killed$) > 0) _
AND (MID$(TimeSent$, 3, 1) = ":") _
AND (MID$(TimeSent$, 6, 1) = ":") _
AND MID$(DateSent$, 3, 1) = "-" _
AND MID$(TimeSent$, 6, 1) = "-" THEN ' all is fine, so...
FixedIt = 0 ' we didn't fix it.
GOTO FinishFix ' Branch off to keep
END IF ' processing.
ELSE ' It's not RBBSMail, so..
FixedIt = -1 ' We're gonna fix 'er.
END IF ' End of "/r" test.
IF NOT ViewFlag THEN ' If we're not just
' viewing (/v), and
IF FixedIt THEN ' If we need to fix it
MidChars Headr$, 61, 58 ' : for first time delimiter
MID$(Headr$, 64, 3) = ColonFix$ ' :00 for second time delim.
MidChars Headr$, 70, 45 ' - first date delimiter
MidChars Headr$, 73, 45 ' - second date delimiter
END IF
ELSE ' We're just viewing
Disp$ = " <" + Killed$ + MID$(TimeSent$, 3, 1) + _
MID$(TimeSent$, 6, 1) + MID$(DateSent$, 3, 1) + _
MID$(DateSent$, 6, 1) + ">"
END IF
END IF ' END OF MAIN "IF" TEST.
' *********************************************************************
' * PREPARE TO WRITE THE MESSAGE (AND/OR FINISH UPDATING DISPLAY) *
' * DoWhat variable is 1, 2, or 3, depending on action to be taken. *
' * 1 = Purge *
' * 2 = Fix *
' * 3 = Copy *
' *********************************************************************
' ----------------------------------------------------------------------------
FinishFix: ' Branch here for RBBSMail/OverMail if msg not been processed yet.
IF PurgePriv THEN ' 410 If /P command line,
IF PrivRec THEN ' 410 and message is both private and received,
Killed$ = "Γ" ' 410 set killed flag for purge.
END IF ' 410
END IF ' 410
IF Killed$ = "Γ" THEN ' If message is killed,
IF ViewFlag THEN ' Maybe we're just viewing?
IF NOT FixedIt THEN ' If we haven't fixed it,
Report$ = " [purged]" ' inform that it would
' be a purge.
ELSE ' Else, it was a fix, so..
Report$ = Disp$ ' inform of 5 key fields.
END IF
DoWhat = 1 ' Set marker
ELSE ' We're not just viewing.
Report$ = " [purged]" ' Inform that it's a purge,
DoWhat = 1 ' and set flag accordingly.
END IF ' END OF VIEW TEST
ELSE ' Message wasn't killed.
IF FixedIt THEN ' Did we fix it?
IF NOT ViewFlag THEN ' If we're not viewing,
Report$ = " <fixed>" ' say that we fixed it,
ELSE ' Otherwise...
Report$ = Disp$ ' Prepare to display the
' 5 key fields.
END IF ' END OF VIEW TEST
DoWhat = 2 ' Set marker
ELSE ' We didn't fix it, so
Report$ = " ..copied.." ' we're just copying the
DoWhat = 3 ' message to the output
' file.
END IF ' END OF FIX TEST
LastMess$ = MessNum$
END IF ' END OF KILLED TEST
' *******************************************************************
' * PRELIMINARY WORK ALL DONE, TIME TO ACTUALLY WRITE THE MESSAGE *
' *******************************************************************
' ----------------------------------------------------------------------------
IF INKEY$ = CHR$(27) THEN PRINTLF "Aborted.": ExitErr = 1: Finish ' Allow [Esc] to abort.
SELECT CASE DoWhat ' What are we doing?
' -----------------------
CASE 1 ' PURGE <<<<<<<<<<<<<<<<<
BodyRecs = NumRecs - 1 ' how many more recs?
DO
Recs = BodyRecs
IF Recs > 32 THEN Recs = 32
Block$ = ""
Block$ = SPACE$(128 * Recs) ' define input block
GETT 1, Block$, FileErr ' input it
RecsRead& = RecsRead& + Recs ' update recs read
BodyRecs = BodyRecs - Recs
LOOP WHILE BodyRecs
' -----------------------
CASE 2, 3 ' FIX, COPY <<<<<<<<<<<<<
IF NOT ViewFlag THEN
MsgCount = MsgCount + 1
IF MsgCount > MsgLim then
skip
PRINTLF "Aborted! - More than " + STR$(MsgLim) + " messages!"
ExitErr = 1
Finish
END IF
SeekIndex&(MsgCount) = _ ' Remember location in .FIX
SEEK(2) + LEN(PuttBlock$) ' file of start of msg
PUTT 2, Headr$ ' Write header to output.
RecsWrote& = RecsWrote& + 1& ' Update # of recs written.
END IF
BodyRecs = NumRecs - 1 ' How long msg body?
DO
Block$ = "" ' Read in msg body, up to
Recs = BodyRecs ' 32 records at a time.
IF Recs > 32 THEN Recs = 32
Block$ = SPACE$(Recs * 128)
GETT 1, Block$, FileErr ' Read in whole body
RecsRead& = RecsRead& + Recs ' Update # recs read
IF NOT ViewFlag THEN ' If not "just lookin'"
PUTT 2, Block$ ' Write to output buffer
RecsWrote& = RecsWrote& + Recs ' Update # recs written.
END IF
BodyRecs = BodyRecs - Recs ' Update count remaining
' records in msg body
LOOP WHILE BodyRecs ' Loop until whole body
' processed.
MsgsWritten = MsgsWritten + 1 ' We just wrote a message.
CASE ELSE ' Shouldn't be possible.
END SELECT ' END OF MESSAGE.
PRINTLF Report$ ' Report what we did,
GOTO NextMess ' ... and get next msg.
'============================================================================
'**************************************************************
'* REWRITE FILE, CUTTING BACK TO SPECIFIED NUMBER OF MESSAGES *
'**************************************************************
'
' ----------------------------------
' BRANCH HERE IF SlashK and/or Renum
' ----------------------------------
KeepFixed:
'Housekeeping:
IF LEN(PuttBlock$) THEN PUT 2, , PuttBlock$ ' save output buffer
PuttBlock$ = "" ' kill buffers
GettBlock$ = ""
TotalMsgs$ = STR$(MsgsWritten)
NewMsgs$ = TotalMsgs$
CLOSE
OPEN OutFile$ FOR INPUT SHARED AS #1
IF LOF(1) < 129 THEN
PRINTLF "Message file is invalid!"
CLOSE
ExitErr = 1
Finish
END IF
skip
'Reminder: Z$ is message file name
'(command line after option switches
'removed).
IF (MsgsWritten <= keep) AND (Renum = 0) THEN
PRINTT "You want to keep " + STR$(keep) + " messages, but only "
PRINTLF STR$(MsgsWritten) + " were found."
PRINTT "Moving " + OutFile$ + " to " + Z$ + " as is ..."
CLOSE
IF PDQExist(Z$) AND PDQExist(OutFile$) THEN
KILL Z$
NAME OutFile$ AS Z$
PRINTLF "done."
ELSE
PRINTLF "unable to do it!"
PRINTLF "Original message base unchanged."
END IF
ExitErr = 1
Finish
END IF
IF MsgsWritten <= keep THEN keep = MsgsWritten
IF keep = 0 THEN keep = MsgsWritten
PRINTT "Found " + STR$(MsgsWritten) + " msgs. "
IF SlashK THEN PRINTT "Keeping"
IF Renum AND SlashK THEN PRINTT "/"
IF Renum THEN PRINTT "Renumbering"
PRINTT " the last " + STR$(keep) + ": "
CLOSE
OPEN OutFile$ FOR BINARY SHARED AS #1 ' Input from *.FIX file
IF PDQExist(Z$) THEN KILL Z$ ' Delete orig msg file
OPEN Z$ FOR BINARY AS #2 ' Output to msg file
MsgsToSkip = MsgsWritten - keep ' How many msgs do we dump?
RecsRead& = 0&
RecsWrote& = 0&
Block$ = ""
Block$ = SPACE$((MaxCopies + 1) * 128) ' Block = Chkpoint + node recs
GET #1, , Block$ ' Read block directly
PUTT 2, Block$ ' Write block via buffer
RecsWrote& = MaxCopies + 1& ' Update # recs written.
LastSave$ = RTRIM$(MID$(Block$, 1, 8)) ' Save checkpoint data for
RecStart$ = RTRIM$(MID$(Block$, 68, 7)) ' later display
NextAvail$ = RTRIM$(MID$(Block$, 75, 7))
LastRec$ = RTRIM$(MID$(Block$, 82, 7))
MaxMess$ = RTRIM$(MID$(Block$, 89, 7))
REDIM OldNum(keep) ' Array of orig msg #s
Headr$ = ""
Headr$ = SPACE$(128)
MessNum$ = SPACE$(4)
SEEK #1, SeekIndex&(MsgsToSkip + 1) ' Move input file
' to beginning of
' first msg to keep
maxmem = 128 * 128 ' initialize input
IF maxmem + 10240 > FRE(a$) THEN maxmem = (FRE(a$) - 10240) \ 128 * 128
L& = LOF(1) - SEEK(1) + 1& ' How long is rest of file?
IF L& < maxmem THEN maxmem = L& ' adjust buffer length if necessary
L& = 0
GettBlock$ = SPACE$(maxmem)
RecsRead& = SEEK(1) \ 128& ' account for skipped
' records
GET #1, , GettBlock$ ' Grab initial input block.
GLoc = 1 ' Set buffer pointer to beginning
' of buffer block.
ERASE SeekIndex& ' Done with array. Reclaim memory
FOR Z = 1 TO keep 'Now save "Keep" msgs
Rotate
Headr$ = ""
Headr$ = SPACE$(128)
GETT 1, Headr$, FileErr 'input header
IF FileErr THEN EXIT FOR
MessNum = PDQValI(MID$(Headr$, 2, 4)) 'determine msg #
OldNum(Z) = MessNum 'save orig msg # in array
NewNum = MessNum
NumRecs = PDQValI(MID$(Headr$, 117, 4)) 'how many records in msg?
IF Renum THEN 'if renumbering
NewNum = Z
LSET MessNum$ = STR$(Z) ' put new number
MID$(Headr$, 2, 4) = MessNum$ ' into header
END IF
PUTT 2, Headr$ 'save header to file
RecsRead& = RecsRead& + 1& 'update counter
RecsWrote& = RecsWrote& + 1& 'update counter
NumHeaders = MidChar(Headr$, 67) 'any cc headers?
IF Renum THEN 'if renumbering ..
'check first records to see
'if they are cc multi-headers
'whose msg numbers must be
'updated.
' Note:
' RBBS before 17.4 would put a space in
' byte 67 of message headers. This
' results in MailFIX interpreting as 32
' message headers when there really
' would only be 1. So we must make sure
' multiple headers really exist before
' changing the date in them.
Block$ = ""
Block$ = SPACE$(128)
HeadersChecked = 1
FOR i = 2 TO NumHeaders 'loop thru any cc headers
GETT 1, Block$, FileErr
GoodHeader = 0 'initialize flag
HeadersChecked = HeadersChecked + 1
IF INSTR("ßΓ", MID$(Block$, 116, 1)) > 0 THEN 'Pass 1st test
TimeSent$ = MID$(Block$, 59, 8)
IF MID$(TimeSent$, 3, 1) = ":" THEN 'Pass 2nd test
IF (MID$(TimeSent$, 6, 1) = ":") _
OR (MID$(TimeSent$, 6, 1) = Colon$) THEN 'Pass 3rd test
DateSent$ = MID$(Block$, 68, 8)
IF MID$(DateSent$, 3, 1) = "-" THEN 'Pass 4th test
IF MID$(DateSent$, 6, 1) = "-" THEN 'Pass 5th test
MID$(Block$, 2, 4) = MessNum$ 'put new msg # into header
GoodHeader = -1 'Set flag
END IF
END IF
END IF
END IF
END IF
PUTT 2, Block$ ' Write to output whether
' a header or not
RecsWrote& = RecsWrote& + 1&
IF NOT GoodHeader THEN EXIT FOR ' If not header, quit
' checking headers
NEXT
BodyRecs = NumRecs - HeadersChecked ' adjust count of recs in
' rest of message
ELSE
BodyRecs = NumRecs - 1
END IF
DO ' loop thru rest of msg
Recs = BodyRecs
IF Recs > 32 THEN Recs = 32 ' take records 32 at a time
Block$ = ""
Block$ = SPACE$(Recs * 128)
GETT 1, Block$, FileErr ' read from input buffer
PUTT 2, Block$ ' write to output buffer
Block$ = ""
BodyRecs = BodyRecs - Recs ' how many msg records left?
RecsWrote& = RecsWrote& + Recs ' update count recs written
RecsRead& = RecsRead& + Recs ' update count recs read
LOOP WHILE BodyRecs ' loop til no more recs
NEXT ' go back for next msg
LOCATE CSRLIN, POS(0) - 1
PRINTLF "Done."
IF NOT FixedLen THEN
PRINTLF "End of messages at record #" + STR$(RecsRead&) + "."
ELSE
StartFormat& = RecsWrote& + 1& ' pre-format balance of
PRINTT "Preformatting " ' file for fixed base.
PRINTT STR$(MaxRecs& - StartFormat&)
PRINTT " empty records for fixed length base: "
Block$ = ""
Block$ = SPACE$(128)
FOR i = StartFormat& TO MaxRecs&
Rotate
PUTT 2, Block$
RecsWrote& = RecsWrote& + 1&
NEXT
LOCATE CSRLIN, POS(0) - 1
PRINTLF "Done."
END IF
IF LEN(PuttBlock$) THEN _ ' clear output buffer
PUT #2, , PuttBlock$: PuttBlock$ = ""
PRINTT "Updating checkpoint record..." ' Update Checkpoint
GET #2, 1, CheckPoint ' record in output with
IF FixedLen THEN ' info based on what
CheckPoint.NextAvail = STR$(StartFormat) ' we've read from the
ELSE ' message file.
CheckPoint.LastRec = STR$(RecsWrote&)
CheckPoint.NextAvail = STR$(RecsWrote& + 1&)
END IF
IF Renum THEN LSET CheckPoint.LastMess = STR$(NewNum) ' set to new high msg #
PUT #2, 1, CheckPoint ' Write updated Checkpoint
' directly to file.
CLOSE
PRINTLF "done."
Block$ = "" ' reclaim string memory
Headr$ = ""
GettBlock$ = ""
skip
Line1$ = PadOut$(LastSave$, RTRIM$(CheckPoint.LastMess)) ' Prepare
Line2$ = PadOut$(RecStart$, RTRIM$(CheckPoint.RecStart)) ' for clean
Line3$ = PadOut$(NextAvail$, RTRIM$(CheckPoint.NextAvail)) ' display
Line4$ = PadOut$(LastRec$, RTRIM$(CheckPoint.LastRec)) ' below...
Line5$ = PadOut$(MaxCopies$, RTRIM$(CheckPoint.MaxCopies)) ' ...
Line6$ = PadOut$(MaxMess$, RTRIM$(CheckPoint.MaxMess)) ' ...
Line7$ = PadOut$(TotalMsgs$, STR$(keep)) ' ...
PRINTLF " Mailfix /K" + STR$(keep)
PRINTLF " -------- -------"
PRINTLF " Last message number : " + Line1$
PRINTLF "Msg record starts at : " + Line2$
PRINTLF " Next available : " + Line3$
PRINTLF " Last record : " + Line4$
PRINTLF " Node records : " + Line5$
PRINTLF " Maximum messages : " + Line6$
PRINTLF " Active messages : " + Line7$
skip
PRINTT STR$(RecsRead& - 1&) + " records read, " ' Update display
PRINTLF STR$(RecsWrote&) + " records written." ' ...
IF PDQExist(OutFile$) THEN KILL OutFile$
'===========================================================================
'******************************************
'* Update last read pointers in user file *
'******************************************
IF UpdtU THEN 'If we're to update user
'pointers
PRINTT "Updating user message pointers ... "
OPEN UserFile$ FOR BINARY AS #1 'Open file
Block$ = SPACE$(128 * 128) 'block size 128 recs
'Note: User file is manipulated in place,
' so we will not use the GETT and PUTT
' buffered read and write functions,
' but will buffer with code here.
users = 0
totusers = 0
DO
IF SEEK(1) > LOF(1) THEN EXIT DO 'If EOF, done
Rotate
IF (LOF(1) - SEEK(1) + 1) < LEN(Block$) THEN 'If < full block left
Block$ = ""
Block$ = SPACE$(LOF(1) - SEEK(1) + 1) ' resize block
END IF
GET #1, , Block$ 'Read block
NPos = 1 'Pointer to name
PPos = 51 'Pointer to last msg #
DO
UName$ = RTRIM$(MID$(Block$, NPos, 31)) 'Read user name
IF NOT (UName$ = "" OR UName$ = "NEWUSER" OR _ 'Make sure valid name
UName$ = " deleted user") THEN
Pointer = CVI(MID$(Block$, PPos, 2)) 'determine val pointer
totusers = totusers + 1 'Increment user count
IF Pointer THEN 'if ptr 0, leave alone
FOR i = keep TO 0 STEP -1 'loop thru old msg #s
IF Pointer >= OldNum(i) THEN ' when find old pointer
MID$(Block$, PPos, 2) = MKI$(i) ' change to new pointer val
users = users + 1 ' add to update count
EXIT FOR ' exit msg # loop
END IF
NEXT
END IF
END IF
NPos = NPos + 128 'reset pointers for next rec
PPos = PPos + 128
LOOP UNTIL PPos > LEN(Block$) 'If still in block, loop again
PUT #1, SEEK(1) - LEN(Block$), Block$ 'Done with block, save it
LOOP 'Go back for next block
CLOSE
LOCATE CSRLIN, POS(0) - 1
PRINTLF "Done - " + STR$(users) + " of " _ 'Report user stats
+ STR$(totusers) + " active users updated"
END IF
Finish ' and we're done.
END
'---------------------------------------------------------------------------
' SUBS AND FUNCTIONS
'---------------------------------------------------------------------------
SUB EndFix 'Help/syntax screen display
CLS
PRINTLF "MailFIX " + Version$ + " - " + Copyright$
PRINTLF STRING$(79, 205)
PRINTLF "Usage: MAILFIX [options] D:\PATH\MESSAGES.DEF"
skip
PRINTLF "Available options:"
skip ' 410
PRINTLF " /D = Use dos screen writes (slower but redirectable) instead of direct."
PRINTLF " /F = Tell MAILFIX this is a fixed-length message base."
PRINTLF " /Kn = Keep only the last 'n' messages in the conference."
PRINTLF " *** This option *WILL* overwrite your old message base! ***"
PRINTLF " /N = Renumber the message base, starting at message #1."
PRINTLF " Enter path\filename of conference user file directly after the /N"
PRINTLF " (no intervening space) to tell MAILFIX to update the user file"
PRINTLF " message pointers for this base after renumbering base."
PRINTLF " *** This option *WILL* overwrite your old message base! ***"
PRINTLF " /O = Tell MAILFIX this is an OverMail'ed message base."
PRINTLF " /P = Purge private messages that have been received." ' 410
PRINTLF " /R = Tell MAILFIX this is an RBBSMail/MsgToss message base." ' 402
PRINTLF " /Sn = Set max size of input RBBS msg file to n msgs (default is 1000)."
PRINTLF " /V = Only View the message base - make no changes."
skip
PRINTLF " Unless the /Knnn, /N, or /V options are used, MAILFIX will create"
PRINTLF " a new message file with the extension '.FIX'." ' 410
CLOSE
ExitErr = 1
END
END SUB 'ENDFIX
'---------------------------------------------------------------------------
SUB Finish ' Display run time
SHARED TimeStart&, ExitErr
CLOSE
PRINTT "MAILFIX run time: "
Elapsed& = (PDQTimer + 1573085 - TimeStart&) MOD 1573085
PRINTLF Dollar$(100000 * (Elapsed&) \ 18207) + " seconds."
skip
EndLevel ExitErr
END
END SUB 'FINISH
'---------------------------------------------------------------------------
SUB GETT (filenum, strvar$, endfile) 'Retrieve a string variable
'from a input buffer block
SHARED GettBlock$ ' Pre-defined input buffer block
SHARED GLoc ' Current position in buffer block
varlen = LEN(strvar$) ' How long is the variable being
' requested?
IF varlen MOD 128 THEN
PRINTLF "IN GETT: requested var len not multiple of 128! " + STR$(varlen)
ExitErr = 1
Finish
END IF
endfile = 0
'How far does strvar$
'request go beyond
'end of buffer block?
shortfall = CLNG(GLoc - 1) + LEN(strvar$) - LEN(GettBlock$)
IF shortfall > 0 THEN 'If beyond end of
' block ..
Part1$ = ""
strvar$ = ""
Part1$ = MID$(GettBlock$, GLoc) ' grab what we can
' as first part of
' string block
' If another full
' block would go
' past end of file
IF (SEEK(filenum) - 1& + LEN(GettBlock$)) > LOF(filenum) THEN _
GettBlock$ = "": _ 'adjust size
GettBlock$ = SPACE$(LOF(filenum) - SEEK(filenum) + 1&) 'of block
IF LEN(GettBlock$) < 1 THEN endfile = -1: EXIT SUB ' block len should
' be zero at eof
GET #filenum, , GettBlock$ ' read in next block
strvar$ = Part1$ + LEFT$(GettBlock$, shortfall) ' get rest of string
Part1$ = ""
GLoc = shortfall + 1 ' save position of
' next char in block
ELSE 'Else, strvar$ all
'contained in current
'block ..
strvar$ = ""
strvar$ = MID$(GettBlock$, GLoc, varlen) ' grab strvar$
GLoc = GLoc + varlen ' update block pointer
END IF
END SUB 'GETT
'---------------------------------------------------------------------------
FUNCTION PadOut$ (In1$, In2$) STATIC 'String functions must be STATIC
'under PDQ (ver 3.10)
PadOut$ = ""
Test1$ = SPACE$(8)
Test2$ = SPACE$(10)
RSET Test1$ = In1$
RSET Test2$ = In2$
PadOut$ = Test1$ + Test2$
END FUNCTION 'PadOut$
'---------------------------------------------------------------------------
SUB PRINTLF (a$) 'Equivalent to QB PRINT a$
SHARED DosPrint
IF DosPrint THEN
PRINT a$
ELSE
PDQPrint a$, CSRLIN, POS(0), 7
LOCATE CSRLIN + 1, 1
Scroll
END IF
END SUB 'PRINTLF
'---------------------------------------------------------------------------
SUB PRINTT (a$) 'Equivalent to QB PRINT a$;
SHARED DosPrint
IF DosPrint THEN
PRINT a$;
ELSE
PDQPrint a$, CSRLIN, POS(0), 7
LOCATE CSRLIN, POS(0) + LEN(a$)
END IF
END SUB 'PRINTT
'---------------------------------------------------------------------------
SUB PUTT (filenum, strvar$) ' Print to output buffer
SHARED PuttBlock$ ' Predefined output buffer
' If adding strvar$ to buffer
' would leave < 500 bytes in
' in string space,
IF (LEN(strvar$) + LEN(PuttBlock$)) > (FRE(a$) - 500) THEN
PUT filenum, , PuttBlock$ ' write buffer to disk, and
PuttBlock$ = ""
PuttBlock$ = strvar$ ' start new buffer w strvar$
ELSE
PuttBlock$ = PuttBlock$ + strvar$ ' ..if not just add to buffer
END IF
END SUB 'PUTT
'---------------------------------------------------------------------------
SUB Rotate STATIC ' Print a twiddle to show program is
' still working
RotChar = RotChar + 1
IF RotChar > 4 THEN RotChar = 1
SELECT CASE RotChar
CASE 1:
a$ = "-"
CASE 2, 4:
a$ = "+"
CASE 3:
a$ = "*"
CASE ELSE:
END SELECT
LOCATE CSRLIN, POS(0) - 1
PRINTT a$
END SUB 'ROTATE
'---------------------------------------------------------------------------
SUB Scroll 'Scroll screen vertically
SHARED DosPrint
IF CSRLIN < 25 THEN EXIT SUB
IF DosPrint THEN
PRINT
ELSE
Registers.AX = &H601
Registers.BX = (7 * 256) + 0
Registers.CX = 0
Registers.DX = (256 * 25) + 79
CALL INTERRUPT(&H10, Registers)
LOCATE 24, POS(0)
END IF
END SUB 'SCROLL
'---------------------------------------------------------------------------
SUB skip 'Equivalent to QB PRINT ""
SHARED DosPrint
IF DosPrint THEN
PRINT
ELSE
LOCATE CSRLIN + 1, 1
Scroll
END IF
END SUB 'SKIP