 REM File: Dndsic for Dndbbs Version v5.0a Release r2.0

 ' get include files
 REM $INCLUDE: 'DNDSIC.INC'

 ' define global error routine
 On Error Goto Error.Routine

 ' reseed random number generator
 RANDOMIZE TIMER

 ' initialize some variables
 Max.Gosubs = 10
 Max.Lines = 4096
 Program.Name = None$
 Quote$ = Chr$(34)
 Token.List = " -+*/\^()[]{}<>=|&!%~?:#@`;,'" + Quote$
 White.Space = CHR$(9) + CHR$(32)
 Timing.Toggle=0 : Brief.Prompt=0

 ' initialize account variables
 Account = Nul ' no account
 Account.Active = False ' account has been entered
 Account.Login = Nul ' no account
 Allow.Select = False ' allow remote line printing
 Group.Account = Nul ' account is group
 Last.Month = False ' last month logged on
 Library.Account = Nul ' account is library
 Logon.Time = SFalse ' time user logged on
 Max.Account.Time = 3600! ' monthly time in minutes
 Max.Records = 1024! ' 1 record = 1,024 bytes
 Max.Time = SFalse ' maximum time allowed
 Minutes.Used = SFalse ' total minutes used this session
 Two.Minutes.Left = False ' time remaining warning
 PrinterSelected = False ' remote line printer is selected
 Program.Name = None$ ' reset name of currently open program
 UserProfileRecLen=LEN(UserProfile) ' user profile structure length

 ' store command line
 Account.Override = Nul
 Var$ = Command$
 Var$ = Ltrim$(Rtrim$(Var$))
 VarX$=Nul
 ' get node
 If Left$(Var$, 1) = "/" Then
    Var$=Mid$(Var$,2)
    If Left$(Var$,1)>="A" And Left$(Var$,1)<="Z" Then
       VarX$=Left$(Var$,1)
       Var$=Mid$(Var$,2)
    Else
       Do
          If Left$(Var$,1)>="0" And Left$(Var$,1)<="9" Then
             VarX$=VarX$+Left$(Var$,1)
             Var$=Mid$(Var$,2)
          Else
             Exit Do
          Endif
       Loop
    Endif
 Endif
 Var$ = Ltrim$(Rtrim$(Var$))
 If Shelled=0 Then
    Account.Override = Var$
 Endif
 If Len(TempX$) Then
    Account.Override = TempX$
 Endif

 ' check dndsic entered from dndbbs
 IF Chained = False THEN
    IF Chained2 = False THEN
       Call Increase.Psp
       NodeX=Nul
       If Rtrim$(VarX$)<>"" Then
          NodeX=Rtrim$(VarX$)
       Endif
       Call Read.Config
       User.PageLength=Max.Row
       Call Save.Screen(0)
    END IF
 END IF

 ' store initial directory
 IF LEN(DND.Path) THEN
    Current.Directory = DND.Path
 ELSE
    Current.Directory = CURDIR$
    IF Right$(Current.Directory, 1) <> "\" THEN
       Current.Directory = Current.Directory + "\"
    END IF
 END IF

 ' program variables
 REDIM Arrays(0 TO 26, 0 TO MaxArrays) AS DOUBLE, _
 Definitions(0 to MaxFNs) AS STRING, _
 Statements(1 TO MaxProfiles) AS STRING, _
 Strngs(0 TO 26) AS STRING, _
 Variables(0 TO 26) AS DOUBLE

 ' stores line number gosub started from
 REDIM GosubReturn(1 TO 10) AS INTEGER

 ' program code
 REDIM Program(0 TO Max.Lines) AS STRING, _
 LineBreak(1 TO Max.Lines) AS INTEGER

 ' variable break arrays
 REDIM VariableBreak(1 TO 26) AS INTEGER, _
 VariableBreak2(1 TO 26) AS INTEGER, _
 VariableBreak3(0 TO 26, 0 TO MaxArrays) AS INTEGER, _
 VariableValue(1 TO 26) AS DOUBLE, _
 VariableValue2(1 TO 26) AS STRING, _
 VariableValue3(0 TO 26, 0 TO MaxArrays) AS DOUBLE

 ' file areas
 REDIM Field.Array(1 TO MaxFiles) AS STRING, _
 File.Fields(1 TO MaxFiles) AS STRING, _
 Field.Sizes(1 TO MaxFiles) AS INTEGER

 ' increase stack for recursion
 STACK 16384 ' increased 11-10-2019

 ' read in program statements
 VarX=0
 Close #TempFile2
 Filename=DND.Path+"profile.dat"
 IF Dir$(Filename)=Nul THEN
    VarX=-1
 ELSE
    Open Filename For Input As #TempFile2
    For Temp=1 To MaxProfiles
       If Eof(TempFile2) Then
          VarX=-1
          Exit For
       Endif
       Input #TempFile2,TempC$,Temp2
       Statements(Temp)=TempC$
    Next
 END IF
 IF VarX THEN
    Call ScreenANSI(4,37,15)
    Strng3="DNDSIC error loading:"
    Call IO.O
    Call ScreenANSI(4,33,14)
    Strng3="Filename "+Quote$+Filename+Quote$+" not found or invalid."
    Call IO.O
    Strng3="Run 'Dndutil /W1' to create default file."
    Call IO.O
    CALL More.Prompt("More(y/n/c)","ync",Outpt$,"y")
    Call Terminate.Program
    END
 END IF

 ' store basic dta
 Call StdinInt(47,0)
 BASIC.DTA.SEG=OutregsX.ES
 BASIC.DTA.OFF=OutregsX.BX

 ' close file handles
 CLOSE

 ' start dndsic interpreter
 CALL End.Program

 ' logon banner
 Call Modem.ANSI(4,37,15)
 Call ScreenANSI(4,37,15)
 Strng3 = "DNDSIC v"+Version$+" r"+Release$+": Symbolic Instruction Code programming shell."
 CALL IO.O
 Call Modem.ANSI(4,33,14)
 Call ScreenANSI(4,33,14)
 Call Sic.Status.Line

 ' login user account profile
 CALL Enter.Account(Var)
 Account.Active = Var

 ' reset multi-dimensional command history arrays
 Redim History2(1 To 3, 1 To MaxHistory) As String*256
 Redim History.Count2(1 To 3) As Integer
 For VarQ1=1 To 3
    History.Count2(VarQ1)=0
    For VarQ2=1 To MaxHistory
       History2(VarQ1,VarQ2)=Nul
    Next
 Next

 ' history variable:
 '   1=command prompt
 '   2=whatis prompt
 '   3=debug prompt
 Temp0=0

Error.Resume:
 IF Account.Active THEN
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
    Strng3 = "Enter 'quit' to return to "
    Strng2="System"
    If Chained2 Then
       Strng2="Editor"
    Else
       If Chained Then
          Strng2="Dndbbs"
       Endif
    Endif
    Strng3=Strng3+Strng2+"."
    CALL IO.O
    Call SIC.Program
 END IF
 Call Terminate.Program
 END

' exits dndsic
Sub Terminate.Program
 Close
 If Prepare.Filename<>"" Then
    If Dir$(Prepare.Filename)<>"" Then
       KILL Prepare.Filename
    Endif
 Endif
 Call Clear.Status.Line

 ' remove dimensioned arrays from memory
 ERASE Arrays, Definitions, Statements, Strngs, Variables, GosubReturn, _
 Program, Field.Array, File.Fields, Field.Sizes

 ERASE LineBreak, VariableBreak, VariableBreak2, VariableBreak3, _
 VariableValue, VariableValue2, VariableValue3

 ' check program chained from
 IF Chained2 THEN
    CHAIN DND.Path+"EDIT.EXE"
 END IF
 If Chained Then
    CHAIN DND.Path+"DNDBBS.EXE"
 END IF
 ' end program
 Color 15,0
 Call ScreenANSI(1,37,7)
 Cls
 Call ScreenANSI(3,0,0)
 Call Save.Screen(-1)
' Locate Max.Row,1,1
 Locate TempX,1,1
 IF Local.Mode THEN
    Color 15,0
    If TempC=-2 Then
       Print
       Print "Error entering username/password."
    Else
       Var$ = Account.Override
       If Var$<>"" Then
          Print
          If TempC=-1 Then
             Print "Error accessing account: "+Var$
          Else
             Print "Closing account: "+Var$
          Endif
       Endif
    Endif
    Print
    Print "Now exiting the Dndsic interpreter."
    Color 7,0
 END IF
 ' release file handles
 CALL Decrease.PSP
 END
END SUB

Sub IO.O
 On Local Error Goto ErrorRoutine2
 VarX=False
 Do
    Call Check.Break
    Call Check.Time
    Call Keyboard
    If Break Then
       Exit Do
    Endif
    VarX=VarX+1
    If VarX>Len(Strng3) Then
       Exit Do
    Endif
    VarX$=Mid$(Strng3,VarX,1)
    Call Put.Modem(VarX$)
    Call Scrn(VarX$)
 Loop
 If Carriage.Input=False Then
    If Len(Strng3)=False Then
       Call Scroll.Return
    Else
       Call Line.Return
    Endif
 Endif
 Strng3=Nul
 Carriage.Input=False
ErrorResume2:
 Exit Sub
ErrorRoutine2:
 Resume ErrorResume2
End Sub

Sub IO.I
 On Local Error Goto ErrorRoutine3
 If Temp0 Then
    Call Command.History(-1) ' reset
 Endif
 Carriage.Input=True
 CALL IO.O
 Out9=Nul
 Do
    Char$=Nul
    Do While Char$=Nul
       Call Check.Break
       Call Check.Time
       Call Keyboard
       If Break Then
          Exit Do
       Endif
       Call Get.Modem
       If Len(Buffer) Then
	  Char$=Left$(Buffer,1)
	  Buffer=Mid$(Buffer,2)
       Endif
       If Local.Mode Then
          Call Release.Time(1)
       Endif
    Loop
    Char=Asc(Char$)
    Select Case Char
    Case 27 ' escape
       If Local.Mode=False Then
          Esc=-1
          Esc$=Chr$(27)
       Endif
    Case 3, 11
       Type.Error=False
       Call Set.Break
    Case 8
       Type.Error=False
       If Len(Out9) Then
          Out9=Left$(Out9,Len(Out9)-1)
          Call Back.Space
       Endif
    Case 13
       Type.Error=False
       If Out9=Nul Then
	  If Len(No.Input.Out) Then
             Out9=No.Input.Out
	     OutY$=Lcase$(No.Input.Out)
	     If User.Echo=False Then
		Call Put.Modem(OutY$)
	     Endif
	     Call Scrn(OutY$)
	  Endif
       Endif
       If Temp0 Then
          Call Command.History(0)
       Endif
       If Carriage.Return=False Then
          If Linefeed.Off=False Then
             Call Line.Return
             Call Get.Cursor.Row(VarX)
             If VarX<(Max.Row-1) Then
                Call Scrn(Chr$(10))
             Endif
          Endif
       Endif
       Exit Do
    Case 9, 32 To 126
       ' escape enabled
       If Esc Then
          Esc$=Esc$+Ucase$(Char$)
          Select Case Len(Esc$)
          Case 1 ' escape
             Eat$ = Nul
          Case 2 ' escape[
             If Mid$(Esc$,2,1)="[" Then
                Eat$ = Nul
             Else ' reset
                Esc$=Nul
                Esc=0
             Endif
          Case 3 ' escape[X
             Esc2$=Mid$(Esc$,3,1)
             Select Case Esc2$
             Case "A" To "Z"
                If Temp0 Then
                   Call Command.History(Asc(Esc2$)-64)
                Endif
             End Select
             Esc$=Nul
             Esc=0
          Case Else
             Esc$=Nul
             Esc=0
          End Select
       Else
          Type.Error=False
          VarX$=Char$
          If No.Echo=False Then
             If Hidden Then
                VarX$=Mask$
             Endif
             If User.Echo=False Then
                Call Put.Modem(VarX$)
             Endif
          Endif
          Out9=Out9+Char$
          If No.Echo=False Then
             Call Scrn(VarX$)
          Endif
          If Line.Length>False Then
             If Len(Out9)>=Line.Length Then
                If No.Echo=False Then
                   If Carriage.Return=False Then
                      Call Line.Return
                   Endif
                Endif
                Exit Do
             Endif
          Endif
       Endif
    Case Else ' garbage
       Type.Error=Type.Error+1
       If Type.Error>=MaxErrors Then
	  Type.Error=False
          Lost.Carrier=True
       Endif
    End Select
 Loop
 Carriage.Return=False
 No.Input=(Out9=Nul)
 Var2$=Ucase$(Left$(Out9,1))
 Continuous=(Var2$="C")
 Quit=(Var2$="Q")
 Yes=(Var2$="Y")
 No=(Var2$="N")
 Linefeed.Off=False
 Line.Length=False
 No.Echo=False
 No.Input.Out=Nul
 Strng3=Nul
ErrorResume3:
 Exit Sub
ErrorRoutine3:
 Resume ErrorResume3
End Sub

Sub Put.Modem(Var$)
 On Local Error Goto ErrorRoutine4
 If Local.Mode Then
    Exit Sub
 Endif
 Call Check.Carrier
 If Lost.Carrier Then
    Exit Sub
 Endif
 For VarX=1 To Len(Var$)
    Var=Asc(Mid$(Var$,VarX,1))
    Do
       Call Driver(3)
       If (OutregsX.AX And &H4000)=&H4000 Then
          InregsX.AX=Var
          Call Driver(1)
          Exit Do
       Endif
    Loop
 Next
ErrorResume4:
 Exit Sub
ErrorRoutine4:
 Resume ErrorResume4
End Sub

Sub Get.Modem
 On Local Error Goto ErrorRoutine5
 If Local.Mode Then
    Exit Sub
 Endif
 Call Check.Carrier
 If Lost.Carrier Then
    Exit Sub
 Endif
 If Fossil.Active Then
    If Fossil.Version=1 Then
       InregsX.AX=&H2000
       InregsX.DX=Port
       Call InterruptX(&H14,InregsX,OutregsX)
       If (OutregsX.AX And &HFF00)=&H0 Then
          Goto End.Input
       Endif
       Exit Sub
    Endif
 Endif
 Call Driver(3)
 If (OutregsX.AX And &H0100)=False Then
    Exit Sub
 Endif
 Call Driver(2)
End.Input:
 VarX=(OutregsX.AX And &HFF)
 If VarX=3 Or VarX=11 Then
    Call Set.Break
 Else
    Buffer=Buffer+Chr$(VarX)
 Endif
ErrorResume5:
 Exit Sub
ErrorRoutine5:
 Resume ErrorResume5
End Sub

Sub Check.Break
 On Local Error Goto ErrorRoutine7
 If Local.Mode Then
    Exit Sub
 Endif
 If Lost.Carrier Then
    Exit Sub
 Endif
 If Fossil.Active Then
    Call Extended.Driver(16)
    If (OutregsX.AX And 1)=1 Then
       Call Set.Break
    Endif
 Endif
ErrorResume7:
 Exit Sub
ErrorRoutine7:
 Resume ErrorResume7
End Sub

Sub Set.Break
 On Local Error Goto ErrorRoutine8
 If Allow.Break Then
    Break=True
 Endif
ErrorResume8:
 Exit Sub
ErrorRoutine8:
 Resume ErrorResume8
End Sub

SUB Check.Time
 On Local Error Goto ErrorRoutine9
 If Logon.Time = SFalse Then
    Exit Sub
 Endif
 CALL Total.Time(Elapsed!, Time.Used!, Remaining!)
 IF Remaining! = -1! THEN
    EXIT SUB
 END IF
 IF Remaining! = SFalse THEN
    CALL Write.Account
    Logon.Time = SFalse
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
    Strng3 = Nul
    Call IO.O
    Strng3 = Chr$(7) + "Monthly account time limit exceeded."
    Call IO.O
    Strng3 = " Try the account next month."
    Call IO.O
    CALL More.Prompt("More(y/n/c)","ync",Outpt$,"y")
    Account.Active = False
    Call Terminate.Program
 END IF
 IF Remaining! <= 1! THEN
    IF Two.Minutes.Left = 1 THEN
       Two.Minutes.Left = True
       Call Modem.ANSI(4,33,14)
       Call ScreenANSI(4,33,14)
       Strng3 = Nul
       Call IO.O
       Strng3 = Chr$(7) + "Less than one minute left!"
       Call IO.O
    END IF
 END IF
 IF Remaining! <= 2! THEN
    IF Two.Minutes.Left = False THEN
       Two.Minutes.Left = 1
       Call Modem.ANSI(4,33,14)
       Call ScreenANSI(4,33,14)
       Strng3 = Nul
       Call IO.O
       Strng3 = Chr$(7) + "Less than two minutes left!"
       Call IO.O
    END IF
 END IF
ErrorResume9:
 Exit Sub
ErrorRoutine9:
 Resume ErrorResume9
End Sub

Sub Check.Carrier
 On Local Error Goto ErrorRoutine10
 CALL Check.Time
 If Local.Mode Then
    Exit Sub
 Endif
 Call Driver(3)
 If (OutregsX.AX And &H80)=False Then
    CALL Write.Account
    Account.Active = False
    Lost.Carrier = True
    Call Terminate.Program
 Endif
ErrorResume10:
 Exit Sub
ErrorRoutine10:
 Resume ErrorResume10
END SUB

SUB Check.Program
 On Local Error Goto ErrorRoutine11
 Program.Time! = Timeit! - Program.Start!
 IF Program.Time! < SFalse THEN
    Program.Time! = Program.Time! + 86400!
 END IF
 ' break in seconds
 IF LineBreakTime > SFalse THEN
    IF Program.Time! >= LineBreakTime THEN
       IF LineFeed THEN
          Strng3 = Nul
          CALL IO.O
       END IF
       Call Modem.ANSI(4,33,14)
       Call ScreenANSI(4,33,14)
       Strng3 = "Debug - Program runtime exceeded limit of"+Str$(LineBreakTime)+" seconds."
       Call IO.O
       'Program.Start! = Timeit!
       Break = True
       Exit Sub
    END IF
 END IF
 ' break in minutes
 Program.Time! = Program.Time! / 60!
 IF Max.Time > SFalse THEN
    IF Program.Time! > Max.Time THEN
       IF LineFeed THEN
          Strng3 = Nul
          CALL IO.O
       END IF
       Call Modem.ANSI(4,33,14)
       Call ScreenANSI(4,33,14)
       Strng3 = "Program runtime exceeded limit of"+Str$(Max.Time)+" minutes."
       Call IO.O
       'Program.Start! = Timeit!
       Break = True
       Exit Sub
    END IF
 END IF
ErrorResume11:
 Exit Sub
ErrorRoutine11:
 Resume ErrorResume11
END SUB

Sub Keyboard
 On Local Error Goto ErrorRoutine12
 VarX$ = Inkey$
 Select Case Len(VarX$)
 Case 1
    Var=Asc(VarX$)
    Select Case Var
    Case 27 ' escape
       If Chat Then
          Break=True
          Chat=False
       Endif
    Case 8, 13, 32 To 127
       Buffer=Buffer+VarX$
    Case 3, 11
       Call Set.Break
    Case Else
       Buffer=Buffer+VarX$
    End Select
 Case 2
    Select Case Asc(Right$(VarX$,1))
    Case 76, 82 ' keypad-5, insert
       Call Insert.Char(VarZ$)
       If Len(VarZ$) Then
          Buffer=Buffer+VarZ$
       Endif
    Case 83 ' delete
       Call Toggle.Display.Line
    Case 71 ' home
       If Temp0 Then
          If Chat=False Then
             Chat=True
             Call Enter.Chat
             Chat=False
             ' redisplay prompt
             Strng3 = "#"
             If Len(NodeX) Then
                Strng3 = NodeX
             Endif
             Select Case Temp0
             Case 1
                Var3$=">"
             Case 2
                Var3$=":"
             Case 3
                Var3$="?"
             End Select
             If Brief.Prompt Then
                Select Case Temp0
                Case 1
                   Strng3="Dndsic "+Strng3
                Case 2
                   Strng3="Whatis "+Strng3
                Case 3
                   Strng3="Debug "+Strng3
                End Select
             Endif
             Strng3=Strng3+Var3$
             Carriage.Input=True
             Call IO.O
          Endif
       Endif
    Case 79 ' end
       Strng3=Nul
       Call IO.O
       CALL Write.Account
       Account.Active = False
       Lost.Carrier = -2
       Call Terminate.Program
    Case 75 ' left
       If Temp0 Then
          Call Command.History(4)
       Endif
    Case 77 ' right
       If Temp0 Then
          Call Command.History(3)
       Endif
    Case 119 ' ctrl-home
       If Temp0 Then
          Call Command.History(8) ' home
       Endif
    Case 117 ' ctrl-end
       If Temp0 Then
          Call Command.History(11) ' end
       Endif
    Case Else
       If VarX$=Chr$(0)+Chr$(0) Then
          Call Set.Break
       Else
          If Temp0=0 Then
             Buffer=Buffer+VarX$
          Endif
       Endif
    End Select
 End Select
ErrorResume12:
 Exit Sub
ErrorRoutine12:
 Resume ErrorResume12
End Sub

Sub Enter.Chat
 On Local Error Goto ErrorTrp35
 Graphics.Off=True
 Logged.Node=False
 Var1!=Logon.Time
 Var2!=Timeit!
 Var3!=Max.Time
 Logon.Time=Timeit!
 Time.Used=60
 Allow.Break=False
 Break=False
 Strng3=Nul
 Call IO.O
 Strng3="Chat Mode.."
 Call IO.O
 Strng3="Sysop press <esc> to exit.."+Chr$(13)+Chr$(10)
 Call Scrn(Strng3)
 Strng3=Nul
 Call IO.O
 Call Modem.ANSI(4,37,15)
 Call ScreenANSI(4,37,15)
 Do While Chat
    Logon.Time=Timeit!
    Max.Time=600!
    Call IO.I
    If Break Or Lost.Carrier Then
       Chat=False
    Endif
 Loop
 Chat=False
 Elapsed!=Fix(Timeit!-Var2!)
 If Elapsed!<0! Then
    Elapsed!=Elapsed!+86400!
 Endif
 Logon.Time=Var1!+Elapsed!
 Max.Time=Var3!
 Allow.Break=False
 Break=False
 Strng3=Nul
 Out9=Nul
 Graphics.Off=False
 Call IO.O
 Strng3="Exit chat.."
 Call IO.O
ErrorRsm35:
 Exit Sub
ErrorTrp35:
 Resume ErrorRsm35
End Sub

Sub Line.Return
 On Local Error Goto ErrorRoutine13
 VarX=Break
 Break=False
 If No.Echo=False Then
    Call Put.Modem(Chr$(13))
    If User.Linefeeds=False Then
       Call Put.Modem(Chr$(10))
    Endif
 Endif
 Call Scrn(Chr$(13))
 Break=VarX
ErrorResume13:
 Exit Sub
ErrorRoutine13:
 Resume ErrorResume13
End Sub

Sub Scroll.Return
 On Local Error Goto ErrorRoutine14
 VarX=Break
 Break=False
 If No.Echo=False Then
    Call Put.Modem(Chr$(13))
    If User.Linefeeds=False Then
       Call Put.Modem(Chr$(10))
    Endif
 Endif
 Call Scroll.Screen
 Break=VarX
ErrorResume14:
 Exit Sub
ErrorRoutine14:
 Resume ErrorResume14
End Sub

Sub Back.Space
 On Local Error Goto ErrorRoutine15
 If Local.Mode=False Then
    If User.Echo=False Then
       Call Get.Cursor(VarX) ' column
       ' backspace at column 2 remote only if wrapped.
       If VarX=2 And Len(Out9)>=3 Then
          Call Get.Cursor.Row(VarQ) ' current row remote
          Call Modem.ANSI(2,VarQ,User.LineLength)
          Call Put.Modem(" ")
          Call Modem.ANSI(2,VarQ,User.LineLength)
       Else   
          Var$=Chr$(8) ' sometimes won't unwrap remote..
          Call Put.Modem(Var$)
          Call Put.Modem(" ")
          Call Put.Modem(Var$)
       Endif
    Endif
 Endif
 If Local.ANSI.Color Or Local.Avatar.Color Then
    Call Get.Cursor(VarX) ' column
    If VarX=1 Then ' backspace at column 1 local
       Call Get.Cursor.Row(VarQ) ' current row local
       Call ScreenANSI(2,VarQ-1,User.LineLength-1)
       Call Put.Screen(" ")
       Call ScreenANSI(2,VarQ-1,User.LineLength-1)
    Else
       Var$=Chr$(27)+"[D"
       Call Put.Screen(Var$)
       Call Put.Screen(" ")
       Call Put.Screen(Var$)
    Endif
 Else
    If Pos(0)=1 Then
       VarQ=Csrlin
       Locate VarQ-1,User.LineLength-1,0
       Print " ";
       Locate VarQ-1,User.LineLength-1,1
    Else
       Locate Csrlin,Pos(0)-1,0
       Print " ";
       Locate Csrlin,Pos(0)-1,1
    Endif
 Endif
ErrorResume15:
 Exit Sub
ErrorRoutine15:
 Resume ErrorResume15
End Sub

Sub Reset.Cursor
 On Local Error Goto ErrorRoutine16
 VarQ=Max.Row-1
 Call ScreenANSI(2,VarQ,1)
 Call ScreenANSI(1,37,7)
 Line.Length=False
ErrorResume16:
 Exit Sub
ErrorRoutine16:
 Resume ErrorResume16
End Sub

Sub Get.Cursor(Var)
 On Local Error Goto ErrorRoutine17
 If Local.ANSI.Color Or Local.Avatar.Color Then
    Call VideoInt(3,0)
    Var=(OutregsX.DX And &HFF)+1
    Exit Sub
 Endif
 Var=Pos(0)
ErrorResume17:
 Exit Sub
ErrorRoutine17:
 Resume ErrorResume17
End Sub

Sub Get.Cursor.Row(Var)
 On Local Error Goto ErrorRoutine18
 If Local.ANSI.Color Or Local.Avatar.Color Then
    Call VideoInt(3,0)
    Var=(OutregsX.DX And &HFF00)/256+1
    Exit Sub
 Endif
 Var=Csrlin
ErrorResume18:
 Exit Sub
ErrorRoutine18:
 Resume ErrorResume18
End Sub

Sub Scrn(Var$)
 On Local Error Goto ErrorRoutine19
 If Local.ANSI.Color Or Local.Avatar.Color Then
    Call VideoInt(3,0)
    VarR=Int(OutregsX.DX/&HFF)
    VarC=(OutregsX.DX And &HFF)+1
 Else
    VarR=Csrlin-1
    VarC=Pos(0)
 Endif
 Select Case VarR
 Case Is>Max.Row-2
    Call Scroll.Screen
 Case Max.Row-2
    If VarC>User.LineLength Then
       Call Put.Screen(Chr$(13))
       Call Scroll.Screen
       If Var$=Chr$(13) Then
	  Exit Sub
       Endif
    Endif
    If Var$=Chr$(13) Then
       Call Scroll.Screen
       Exit Sub
    Endif
 End Select
 Call Put.Screen(Var$)
ErrorResume19:
 Exit Sub
ErrorRoutine19:
 Resume ErrorResume19
End Sub

Sub Scroll.Screen
 On Local Error Goto ErrorRoutine20
 Scroll.Row=Max.Row-2
 Var=Scroll.Row*256+&H4F
 Call VideoInt(6,Var)
 Var=Scroll.Row*256
 Call VideoInt(2,Var)
 Locate Max.Row-1,1,1
ErrorResume20:
 Exit Sub
ErrorRoutine20:
 Resume ErrorResume20
End Sub

Sub Put.Screen(Var1$)
 On Local Error Goto ErrorRoutine21
 If Local.ANSI.Color Or Local.Avatar.Color Then
    For VarX=1 To Len(Var1$)
       VarY=Asc(Mid$(Var1$,VarX,1))
       Call Put.Screen.Char(VarY)
    Next
 Else
    For VarX=1 To Len(Var1$)
       Var2=Asc(Mid$(Var1$,VarX,1))
       Select Case Var2
       Case 10
	  Print
       Case 13
	  Print;
       Case Else
	  Print Chr$(Var2);
       End Select
    Next
 Endif
ErrorResume21:
 Exit Sub
ErrorRoutine21:
 Resume ErrorResume21
End Sub

Sub Put.Screen.Char(Var)
 On Local Error Goto ErrorRoutine22
 Call StdinInt(6,Var)
ErrorResume22:
 Exit Sub
ErrorRoutine22:
 Resume ErrorResume22
End Sub

Sub Local.ANSI(Var1,Var2,Var3)
 On Local Error Goto ErrorRoutine23
 If Local.ANSI.Color Then
    Call ANSI(Var1,Var2,Var3)
    Exit Sub
 Endif
 If Local.Avatar.Color Then
    Call Avatar(Var1,Var2,Var3)
    Exit Sub
 Endif
 TempD$=Nul
 Select Case Var1
 Case 1
    Call Convert.Color(Var2,True)
 Case 2
    Locate Var2,Var3,1
 Case 3
    Cls
 Case 4
    Call Convert.Color(Var2,False)
 Case 5
    Call Convert.Color(Var2,False)
 End Select
ErrorResume23:
 Exit Sub
ErrorRoutine23:
 Resume ErrorResume23
End Sub

Sub ScreenANSI(Var1,Var2,Var3)
 On Local Error Goto ErrorRoutine24
 Call Local.ANSI(Var1,Var2,Var3)
 If TempD$<>Nul Then
    Call Scrn(TempD$)
 Endif
ErrorResume24:
 Exit Sub
ErrorRoutine24:
 Resume ErrorResume24
End Sub

Sub Modem.ANSI(Var1,Var2,Var3)
 On Local Error Goto ErrorRoutine25
 If Remote.ANSI.Color Then
    Call ANSI(Var1,Var2,Var3)
    Call Put.Modem(TempD$)
 Else
    If Remote.Avatar.Color Then
       Call Avatar(Var1,Var2,Var3)
       Call Put.Modem(TempD$)
    Endif
 Endif
ErrorResume25:
 Exit Sub
ErrorRoutine25:
 Resume ErrorResume25
End Sub

Sub ANSI(VarX,VarY,VarZ)
 On Local Error Goto ErrorRoutine26
 TempD$=Nul
 Select Case VarX
 Case 1 ' clear ansi, reset color
    TempD$=Chr$(27)+"[0;"+Mid$(Str$(VarY),2)+"m"
 Case 2 ' screen coordinate
    TempD$=Chr$(27)+"["+Mid$(Str$(VarY),2)+";"+Mid$(Str$(VarZ),2)+"H"
 Case 3 ' clear screen
    TempD$=Chr$(27)+"[2J"
 Case 4 ' hilight ansi color
    TempD$=Chr$(27)+"[1;"+Mid$(Str$(VarY),2)+"m"
 Case 5 ' background ansi color/no intensity override
    TempD$=Chr$(27)+"["+Mid$(Str$(VarY),2)+"m"
 End Select
ErrorResume26:
 Exit Sub
ErrorRoutine26:
 Resume ErrorResume26
End Sub

Sub Avatar(VarX,VarY,VarZ)
 On Local Error Goto ErrorRoutine27
 TempD$=Nul
 Select Case VarX
 Case 1, 4, 5 ' ansi color
    TempD$=Chr$(22)+Chr$(1)+Chr$(VarZ)
 Case 2 ' screen coordinate
    TempD$=Chr$(22)+Chr$(8)+Chr$(VarY)+Chr$(VarZ)
 Case 3 ' clear screen
    TempD$=Chr$(12)
 End Select
ErrorResume27:
 Exit Sub
ErrorRoutine27:
 Resume ErrorResume27
End Sub

Sub Convert.Color(VarX,VarY)
 On Local Error Goto ErrorRoutine28
 VarF=False
 VarB=False
 Select Case VarX
 Case 30
    VarF=8
 Case 31
    VarF=12
 Case 32
    VarF=10
 Case 33
    VarF=14
 Case 34
    VarF=9
 Case 35
    VarF=13
 Case 36
    VarF=11
 Case 37
    VarF=15
 Case 40
    VarB=8
 Case 41
    VarB=12
 Case 42
    VarB=10
 Case 43
    VarB=14
 Case 44
    VarB=9
 Case 45
    VarB=13
 Case 46
    VarB=11
 Case 47
    VarB=15
 End Select
 If VarF Then
    If VarY Then
       VarF=VarF-8
    Endif
    Color VarF, 0
 Else
    If VarB Then
       VarB=VarB-8
       Color ,VarB
    Endif
 Endif
ErrorResume28:
 Exit Sub
ErrorRoutine28:
 Resume ErrorResume28
End Sub

' standard dndsic program loop
SUB SIC.Program
 On Error Goto Error.Routine
 Account.Active = True

 ' loop until quit entered
 DO While Lost.Carrier=False
    CALL Check.Carrier
    IF Current.Program THEN
       CALL New.Program
       Current.Program = False
    END IF
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
    Out9 = Nul
    Call Sic.Status.Line
    Do While Lost.Carrier=False
       Call Modem.ANSI(4,37,15)
       Call ScreenANSI(4,37,15)
       Allow.Break=True
       Break=False
       If Brief.Prompt=0 Then
          If Len(NodeX) Then
             Strng3 = NodeX+">"
          Else
             Strng3 = "#>"
          Endif
       Else
          If Len(NodeX) Then
             Strng3 = "Dndsic "+NodeX+">"
          Else
             Strng3 = "Dndsic #>"
          Endif
       Endif
       Temp0=1
       Line.Length=255
       CALL IO.I
       Temp0=False
       VarX=Break
       Allow.Break=False
       Break=False
       If VarX Then
          Call Modem.ANSI(4,31,12)
          Call ScreenANSI(4,31,12)
          Call Get.Cursor(VarX)
          IF VarX > 1 THEN
             Strng3 = NUL
             Call IO.O
          END IF
          Strng3 = "*break*"
          CALL IO.O
          Out9=Nul
       Endif
       Call Modem.ANSI(4,33,14)
       Call ScreenANSI(4,33,14)
       If Len(Out9) Then
          Exit Do
       Endif
    Loop
    Assign = False
    CALL Enter.Program.Line
    IF Assign = False THEN
       Var2$ = Nul
       ' parse commands with parameters
       IF INSTR(Out9, " ") THEN
          VarX$ = Left$(Out9, Instr(Out9, " ") - 1)
          Select Case UCASE$(VarX$)
          Case "KILL"
             Var2$ = MID$(Out9, 5)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "KILL"
          Case "KIL"
             Var2$ = MID$(Out9, 4)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "KILL"
          Case "K"
             Var2$ = MID$(Out9, 2)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "KILL"
          Case "LOAD"
             Var2$ = MID$(Out9, 5)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "LOAD"
          Case "LOA"
             Var2$ = MID$(Out9, 4)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "LOAD"
          Case "L"
             Var2$ = MID$(Out9, 2)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "LOAD"
          Case "SEARCHLIMITS"
             Var2$ = MID$(Out9, 13)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "SEARCHLIMITS"
          Case "SEAL"
             Var2$ = MID$(Out9, 5)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "SEARCHLIMITS"
          Case "S"
             Var2$ = MID$(Out9, 2)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "SEARCHLIMITS"
          Case Else
             Out9 = Nul
          END SELECT
       END IF
       SELECT CASE UCASE$(Out9)
       CASE "DRIVES"
           'IF Local.Mode THEN
               CALL ListDrives
           'END IF
       CASE "SHELL"
           IF UserProfile.Commands(102) Then
               CALL Execute.Command("")
           ELSE
               Strng3="Function not allowed."
               Call IO.O
           END IF
       CASE "ACCOUNT", "ACC"
          CALL System.Accounting
          Call Time.Online
       CASE "ANALYZE", "ANA", "A"
          If Program.Name=None$ Then
             Strng3="Program not loaded."
             Call IO.O
          Else
             Strng3 = "Preparing program.."
             Call IO.O
             Running.Program = Program.Name
             CALL Prepare.Program(True)
             CALL Analyze.Program
             CALL New.Program
             Filename = Prepare.Filename
             CALL Read.Program(Var)
             Program.Name = Running.Program
             Close
             If Dir$(Prepare.Filename)<>"" Then
                KILL Prepare.Filename
             Endif
          Endif
       CASE "CATALOG", "CAT"
          CALL List.Programs
       CASE "EXECUTE", "EXE"
          Run.Type=False
          CALL Run.Library
       CASE "GROUP", "GRO"
          Run.Type=False
          CALL Group.Files
       CASE "HELP", "HEL", "H"
          CALL List.Help
       CASE "INDENT", "IND", "I"
          If Program.Name=None$ Then
             Strng3="Program not loaded."
             Call IO.O
          Else
             CALL Indent.Program
          Endif
       CASE "KILL", "KIL", "K"
          CALL Kill.Program(Var2$)
       CASE "LIBRARY", "LIB"
          CALL Library.Files
       CASE "LIMITS", "LIM"
          CALL Display.Limits
       CASE "SEARCHLIMITS", "SEAL", "S"
          CALL Search.Limits(Var2$)
       CASE "LIST", "LIS"
          CALL List.Program
       CASE "LLIST", "LLI", "LL"
          CALL LList.Program
       CASE "LOAD", "LOA", "L"
          Last.Search.Line = False
          CALL Load.Program(Var2$)
       CASE "LOCK", "LOC"
          CALL Lock.Program
       CASE "LOGOFF", "LOG"
          CALL Quit.Program
       CASE "LPRINT", "LPR"
          IF Allow.Select = False THEN
             Strng3 = "Line printer unavailable."
             CALL IO.O
          ELSE
             PrinterSelected = NOT PrinterSelected
             If PrinterSelected Then
                Strng3 = "Line printer selected."
             ELSE
                Strng3 = "Line printer deselected."
             END IF
             CALL IO.O
          END IF
       CASE "NEW", "ERASE", "N"
          Last.Search.Line = False
          CALL Erase.Program
       CASE "PROFILES", "PRO", "P"
          CALL List.Accounts
       CASE "QUIT", "Q", "EXIT", "LOGOFF"
          CALL Quit.Program
       CASE "RENUMBER", "REN", "R"
          If Program.Name=None$ Then
             Strng3="Program not loaded."
             Call IO.O
          Else
             CALL Renumber.Program
          Endif
       CASE "RUN"
          Run.Line = False
          CALL Run.Catalog
       CASE "SAVE", "SAV"
          CALL Save.Program
       CASE "SEARCH", "SEA"
          CALL Search.Files
       CASE "START", "STA"
          CALL Run.Group
       CASE "UNLOCK", "UNL"
          CALL Unlock.Program
       CASE "WHATIS", "WHAT", "WHA", "W"
          Program.Running = False
          Call Read.Account.Commands(Account)
          CALL Count.Lines(Last.Line)
          CALL Whatis.Command
       CASE "C", "CONT", "CONTINUE"
          If Program.Name=None$ Then
             Strng3="Program not loaded."
             Call IO.O
          Else
             If Run.Line=False Then
                Strng3="Program not loaded."
                Call IO.O
             Else
                CALL Count.Lines(V%)
                IF Run.Line > V% THEN
                   Strng3="Can't continue."
                   Call IO.O
                ELSE
                   Strng3="Resuming at line: "+Form$(Cdbl(Run.Line))
                   Call IO.O
                   Call Run.Catalog
                END IF
             Endif
          Endif
       CASE "DEBUG", "DEB", "D"
          'If Program.Name=None$ Then
          '   Strng3="Program not loaded."
          '   Call IO.O
          'Else
             CALL Debug.Command
          'Endif
       CASE "CLOCK"
          Strng3 = "Clock: " + Fclock$
          Call IO.O
          Call Time.Online
       CASE "DATE"
          Strng3 = "Date: " + DATES$
          Call IO.O
          Call Time.Online
       CASE "TIME"
          Strng3 = "Time: " + Format$(Now,"hh:mm:ss")
          Call IO.O
          Call Time.Online
       Case "BRIEF"
          Brief.Prompt = Not Brief.Prompt
          If Brief.Prompt Then
             Strng3 = "Brief display off."
          Else
             Strng3 = "Brief display on."
          Endif
          Call IO.O
       Case "TIMING"
          Timing.Toggle = Not Timing.Toggle
          If Timing.Toggle Then
             Strng3 = "Timing display off."
          Else
             Strng3 = "Timing display on."
          Endif
          Call IO.O
       Case "LOGON"
          Call Time.Online
       Case "FIND", "F"
          Call Search.Program
       CASE ELSE
          Strng3="Type 'help' for information."
          Call IO.O
       END SELECT
    END IF
 LOOP
 END SUB

' standard error trap for all DndSic functions.
Error.Routine:
 PrinterLF = False
 Printing = False
 ErrorLine = Program.Line
 If ErrorLine > Max.Lines Then
    ErrorLine = False
 Endif
 ErrorValue = ERR
 Call Get.Cursor(VarX)
 IF VarX > 1 THEN
    Strng3 = NUL
    Call IO.O
 END IF
 Call Modem.ANSI(4,31,12)
 Call ScreenANSI(4,31,12)
 CALL Display.Error
 Call IO.O
 CALL Key.Prompt
 LineFeed = 0
 RESUME Error.Resume

' finds last line in current .sic program
SUB Count.Lines (Temp1%)
 On Local Error Goto ErrorRoutine30
 Temp1% = False
 FOR Temp2% = Max.Lines TO 1 STEP -1
    Temp1$ = Program(Temp2%)
    Temp1$ = STRIM$(Temp1$)
    IF LEN(Temp1$) THEN
       EXIT FOR
    END IF
 NEXT
 Temp1% = Temp2%
ErrorResume30:
 Exit Sub
ErrorRoutine30:
 Resume ErrorResume30
END SUB

' opens/reads account information
SUB Enter.Account (Var)
 On Local Error Goto ErrorRoutine32
 TempC=False
 Var = False
 Account.File$ = Current.Directory + "ACCOUNTS.SIC"
 IF DIR$(Account.File$) = Nul THEN
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
    Strng3 = "No accounts are active."
    CALL IO.O
    Strng3 = "Edit the account file and restart."
    Call IO.O
    CALL More.Prompt("More(y/n/c)","ync",Outpt$,"y")
    EXIT SUB
 END IF
 CLOSE #SicFile
 OPEN Account.File$ FOR RANDOM SHARED AS #SicFile LEN=UserProfileRecLen
 IF LOF(SicFile) = SFalse THEN
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
    Strng3 = "No file accounts are active."
    CALL IO.O
    Strng3 = "Edit the account file and restart."
    Call IO.O
    CALL More.Prompt("More(y/n/c)","ync",Outpt$,"y")
    EXIT SUB
 END IF
 Call Modem.ANSI(4,33,14)
 Call ScreenANSI(4,33,14)
 If Account.Override = Nul Then
    Strng3="Enter account/password(?=list):"
    Call IO.O
 Endif
 Tries = False
 TempC=False
Start.Account:
 If Account.Override = Nul Then
    Strng3 = "Enter account. Try"+Str$(Tries+1)+" of"+Str$(LogonTries)+"."
    Call IO.O
 Endif
 SICRecord = SFalse
 DO While Lost.Carrier=False
    Account = Nul
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
    If Account.Override = Nul Then
       Strng3 = "Account? "
       CALL IO.I
       If Out9="?" Then
          CALL List.Accounts
          Goto Start.Account
       Endif
    Else
       Out9 = Account.Override
    Endif
    Out3 = Ucase$(Out9)
    IF Out3 = "ACCOUNTS" THEN
       Eat$ = Nul
    ELSE
       If Account.Override = Nul Then
          Strng3 = "Password? "
          Hidden = True
          CALL IO.I
          Hidden = False
          Out9 = Ucase$(Out9)
          Call Cryptograph(Out9)
       Endif
       FOR Record.Number! = 1! TO LOF(SicFile)/UserProfileRecLen
          Call Read.User.Record(Record.Number!)
          IF RTRIM$(UserProfile.AccountLogin) = Out3 THEN
             If Account.Override <> Nul Then
                Out9 = UserProfile.Password
             Endif
             IF UserProfile.Password = Out9 THEN
                Max.Records = UserProfile.MaxRecords
                Max.Account.Time = UserProfile.TimeLimit
                Allow.Select = UserProfile.PrinterSelect
                Last.Month = UserProfile.LastMonth
                Minutes.Used = UserProfile.TimeUsed
                Max.Time = UserProfile.MaxRunTime
                IF Last.Month <> VAL(LEFT$(DATES$, 2)) THEN
                   Last.Month = VAL(LEFT$(DATES$, 2))
                   Minutes.Used = SFalse
                ELSE
                   IF Max.Account.Time>0! THEN
                      IF Minutes.Used >= Max.Account.Time THEN
                         Call Modem.ANSI(4,33,14)
                         Call ScreenANSI(4,33,14)
                         Strng3 = "Monthly account time limit exceeded."
                         CALL IO.O
                         CALL More.Prompt("More(y/n/c)","ync",Outpt$,"y")
                         EXIT SUB
                      END IF
                   END IF
                END IF
                Account = Rtrim$(UserProfile.Account)
                Account.Directory$ = Current.Directory + Account + ".SIC"
                Account.Directory$ = UCASE$(Account.Directory$)
                Account.Subdirectory = Account.Directory$ + CHR$(0)
                Account.Login = Lcase$(Rtrim$(UserProfile.AccountLogin))
                Group.Account = Rtrim$(UserProfile.Group)
                Group.Login = Lcase$(Rtrim$(UserProfile.GroupLogin))
                Library.Account = Rtrim$(UserProfile.Library)
                Library.Login = Lcase$(Rtrim$(UserProfile.LibraryLogin))
                SICRecord = Record.Number!
                Close #SicFile

                ' make directory
                InregsX.DS=VARSEG(Account.Subdirectory)
                InregsX.DX=VARPTR(Account.Subdirectory)
                Call StdinInt(63,0)

                ' check carry flag
                IF (OutregsX.Flags AND &H1) = &H0 THEN
                   Call Modem.ANSI(4,33,14)
                   Call ScreenANSI(4,33,14)
                   Strng3 = "Creating account directory."
                   CALL IO.O
                ELSE
                   ' error code path not found
                   IF OutregsX.AX = &H3 THEN
                      Call Modem.ANSI(4,33,14)
                      Call ScreenANSI(4,33,14)
                      Strng3 = "Unable to create account directory."
                      CALL IO.O
                      EXIT SUB
                   ELSE
                      ' error code access denied
                      IF OutregsX.AX = &H5 THEN
                         Call Modem.ANSI(4,33,14)
                         Call ScreenANSI(4,33,14)
                         Strng3 = "Opening account directory."
                         CALL IO.O
                      END IF
                   END IF
                END IF

                ' login user time
                Logon.Time = Timeit!
                Call System.Accounting

                ' login user
                Var = True
                EXIT SUB
             END IF
          END IF
       NEXT
    END IF
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
    If Account.Override <> Nul Then
       TempC=True
       Strng3 = "Error entering account."
       Call IO.O
       Exit Do
    Endif
    Tries = Tries + 1
    IF Tries >= LogonTries THEN
       TempC=-2
       Strng3 = "Error entering account."
       CALL IO.O
       Strng3 = "Check the password and restart."
       Call IO.O
       CALL More.Prompt("More(y/n/c)","ync",Outpt$,"y")
       EXIT DO
    END IF
    Strng3 = "Error entering password. Try"+Str$(Tries+1)+" of"+Str$(LogonTries)+"."
    Call IO.O
 LOOP
ErrorExit32:
 Exit Sub
ErrorRoutine32:
 Resume ErrorExit32
END SUB

' stores account information
SUB Write.Account
 On Local Error Goto ErrorRoutine33
 IF SICRecord = SFalse THEN
    EXIT SUB
 END IF
 IF Account = "ACCOUNTS" THEN
    EXIT SUB
 END IF
 CLOSE #SicFile
 Account.File$ = Current.Directory + "ACCOUNTS.SIC"
 OPEN Account.File$ FOR RANDOM SHARED AS #SicFile LEN=UserProfileRecLen
 IF LOF(SicFile) = False THEN
    EXIT SUB
 END IF
 IF SICRecord > LOF(SicFile)/UserProfileRecLen THEN
    EXIT SUB
 END IF
 CALL Total.Time(Elapsed!, Time.Used!, Remaining!)
 Call Read.User.Record(SICRecord)
 If Time.Used!=0! Then
    Time.Used!=1!
 Endif
 UserProfile.TimeUsed = Time.Used!
 UserProfile.LastMonth = Last.Month
 Call Write.User.Profile(SICRecord)
ErrorExit33:
 Exit Sub
ErrorRoutine33:
 Resume ErrorExit32
END SUB

' end program routine
SUB End.Program
 On Local Error Goto ErrorRoutine34
 IF New.Screen THEN
    New.Screen = False
    SCREEN 0
 END IF
 IF New.Width THEN
    New.Width = False
    WIDTH 80, 25
 END IF
 COLOR 7,0
 Call Modem.ANSI(4,37,0)
 Call ScreenANSI(4,37,0)
 LOCATE , , , 8, 8
 IF LineFeed THEN
    IF POS(0) > 1 THEN
       Call IO.O
    END IF
 END IF
 CALL Reset.Cursor
ErrorExit34:
 Exit Sub
ErrorRoutine34:
 Resume ErrorExit34
END SUB

' parses and stores a program line number
SUB Enter.Program.Line
 On Local Error Goto ErrorRoutine35
 Run.Type = True
 Assign = False
 Out2 = STRIM$(Out9)
 IF Left$(Out2, 1) = ";" THEN ' comment is ignored
    Assign = True
    EXIT SUB
 END IF
 IF Left$(Out2, 1) = "'" THEN ' comment is ignored
    Assign = True
    EXIT SUB
 END IF
 IF Left$(Ucase$(Out2), 3) = "REM" THEN ' comment is ignored
    Assign = True
    EXIT SUB
 END IF
 FOR Blanks = 1 TO LEN(Out2)
    Imbedded = False
    FOR Parse = 1 TO LEN(White.Space)
       IF MID$(Out2, Blanks, 1) = MID$(White.Space, Parse, 1) THEN
          Imbedded = Blanks
          EXIT FOR
       END IF
    NEXT
    IF Imbedded THEN
       Line.Number = INT(VAL(LEFT$(Out2, Imbedded - 1)))
       IF Line.Number > False AND Line.Number <= Max.Lines THEN
          Out3 = MID$(Out2, Imbedded)
          IF STRIM$(Out3) = Nul THEN
             EXIT FOR
          END IF
          Assign = True
          Program(Line.Number) = Out3
          IF Program.Name = None$ THEN
             Program.Name = UnTitled$
	  END IF
       ELSE
          Out3 = STRIM$(Out2)
          IF INSTR("0123456789", LEFT$(Out3, 1)) THEN
             Assign = True
             Strng3 = "Line number out of range."
             Call IO.O
          END IF
       END IF
       EXIT SUB
    END IF
 NEXT
 Line.Number = INT(VAL(Out2))
 IF Line.Number > False AND Line.Number <= Max.Lines THEN
    Assign = True
    Program(Line.Number) = Nul
    CALL Count.Lines(Last.Line)
    IF Last.Line = False THEN
       Program.Name = None$
    ELSE
       IF Program.Name = None$ THEN
          Program.Name = UnTitled$
       END IF
    END IF
 ELSE
    Out3 = STRIM$(Out2)
    IF INSTR("0123456789", LEFT$(Out3, 1)) THEN
       Assign = True
       Strng3 = "Line number out of range."
       Call IO.O
    END IF
 END IF
ErrorExit35:
 Exit Sub
ErrorRoutine35:
 Resume ErrorExit35
END SUB

' lists account group files
SUB Group.Files
 On Local Error Goto ErrorRoutine36
 IF Account.Login = Group.Login THEN
    Strng3 = "Account is group."
    CALL IO.O
    EXIT SUB
 END IF
 Strng3 = "Group Account Files:"
 CALL IO.O
 Group.Directory$ = Current.Directory + Group.Account + ".SIC\*.SIC"
 CALL List.Files (Group.Directory$)
ErrorExit36:
 Exit Sub
ErrorRoutine36:
 Resume ErrorExit36
END SUB

' lists account library files
SUB Library.Files
 On Local Error Goto ErrorRoutine37
 IF Account.Login = Library.Login THEN
    Strng3 = "Account is library."
    CALL IO.O
    EXIT SUB
 END IF
 Strng3 = "Library Account Files:"
 CALL IO.O
 Library.Directory$ = Current.Directory + Library.Account + ".SIC\*.SIC"
 CALL List.Files (Library.Directory$)
ErrorExit37:
 Exit Sub
ErrorRoutine37:
 Resume ErrorExit37
END SUB

' prompts to delete a .sic program
SUB Kill.Program(Var2$)
 On Local Error Goto ErrorRoutine38
 If Len(Var2$)=0 Then
    Strng3 = "Program name? "
    CALL IO.I
 Else
    Out9=Var2$
 Endif
 Program.File$ = Out9
 Program.File$ = RTRIM$(LEFT$(Program.File$, 8))
 IF UCASE$(Program.File$) = "ACCOUNTS" THEN
    Exit Sub
 END IF
 CLOSE #TempFile2
 Filename = Current.Directory + Account + ".SIC\" + Program.File$ + ".SIC"
 IF DIR$(Filename) = Nul THEN
    Strng3 = "Cannot find " + Quote$ + LCASE$(RTRIM$(Program.File$+".sic")) + Quote$
    CALL IO.O
    EXIT SUB
 END IF
 DO While Lost.Carrier=False
    Strng2 = "Kill " + Program.File$ + ". Are you sure(y/n)"
    CALL More.Prompt(Strng2,"yn",Output.Char$,"n")
    SELECT CASE Output.Char$
    CASE "y"
       EXIT DO
    CASE "n"
       EXIT SUB
    END SELECT
 LOOP
 Close
 KILL Filename
 Strng3 = "Program killed."
 CALL IO.O
ErrorExit38:
 Exit Sub
ErrorRoutine38:
 Resume ErrorExit38
END SUB

' prompts for help file
SUB List.Help
 On Local Error Goto ErrorRoutine39
 DO While Lost.Carrier=False
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
    Strng3 = "DNDSIC Help utility list:"
    CALL IO.O
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
    Strng3 = "[1]command list"
    CALL IO.O
    Strng3 = "[2]general documentation"
    CALL IO.O
    Strng3 = "[3]formatting information"
    CALL IO.O
    Strng3 = "[4]account editing list"
    CALL IO.O
    Strng3 = "[5]screen formats list"
    CALL IO.O
    Strng3 = "[6]command syntax list"
    CALL IO.O
    Strng2 = "Enter(1-6,Q to quit)"
    CALL More.Prompt(Strng2,"123456q",Output.Char$,"q")
    SELECT CASE Output.Char$
    CASE "1"
       Strng3 = "ACCOUNT   --  display user profile account."
       CALL IO.O
       Strng3 = "LIMITS    --  display accounts capabilities."
       CALL IO.O
       Strng3 = "PROFILES  --  lists active accounts."
       CALL IO.O
       Strng3 = "  CATALOG   --  list catalog program files."
       CALL IO.O
       Strng3 = "  GROUP     --  list group program files."
       CALL IO.O
       Strng3 = "  LIBRARY   --  list library program files."
       CALL IO.O
       Strng3 = "RUN       --  start the current program."
       CALL IO.O
       Strng3 = "  START     --  run group program."
       CALL IO.O
       Strng3 = "  EXECUTE   --  run library program."
       CALL IO.O
       Strng3 = "ANALYZE   --  analyzes current program."
       CALL IO.O
       Strng3 = "INDENT    --  indents program"
       CALL IO.O
       Strng3 = "RENUMBER  --  change current program line numbering."
       CALL IO.O
       Strng3 = "  KILL      --  delete a .sic program."
       CALL IO.O
       Strng3 = "  LIST      --  display current program."
       CALL IO.O
       Strng3 = "  LOAD      --  read a program from disk."
       CALL IO.O
       Strng3 = "  NEW       --  erase the current program."
       CALL IO.O
       Strng3 = "  QUIT      --  exit the DNDSIC interpreter."
       CALL IO.O
       Strng3 = "  SAVE      --  store current program to disk."
       CALL IO.O
       Strng3 = "  SEARCH    --  locate .sic programs."
       CALL IO.O
       Strng3 = "  WHATIS    --  enter immediate mode."
       CALL IO.O
       CALL Key.Prompt
       Strng3 = "DEBUG     --  starts debug prompt."
       Call IO.O
       Strng3 = "CONTINUE  --  continues program after break."
       Call IO.O
       Strng3 = "FIND      --  search program for string."
       Call IO.O

       'IF Account.Login = Library.Login THEN
       Strng3 = "DRIVES    --  list current drive information."
       Call IO.O
       'END IF

       Strng3 = "LLIST     --  send program to line printer."
       CALL IO.O
       Strng3 = "LPRINT    --  select line printer."
       CALL IO.O
       Strng3 = "SEARCHLIMITS  --  displays limits for statements."
       CALL IO.O
       IF Account.Login = Library.Login THEN
          Strng3 = "LOCK      --  lock library program."
          CALL IO.O
          Strng3 = "UNLOCK    --  unlock library program."
          CALL IO.O
       END IF
       Strng3 = "BRIEF     --  toggle brief mode."
       Call IO.O
       Strng3 = "TIMING    --  toggle timing display."
       Call IO.O
       CALL Key.Prompt
    CASE "2"
       Program.File$ = Current.Directory + "dndsic.doc"
       CALL List.Help.File(Program.File$)
    CASE "3"
       Program.File$ = Current.Directory + "sicform.doc"
       CALL List.Help.File(Program.File$)
    CASE "4"
       Program.File$ = Current.Directory + "sicedit.doc"
       CALL List.Help.File(Program.File$)
    CASE "5"
       Program.File$ = Current.Directory + "sicscrn.doc"
       CALL List.Help.File(Program.File$)
    CASE "6"
       Program.File$ = Current.Directory + "syntax.doc"
       CALL List.Help.File(Program.File$)
    CASE "q"
       EXIT DO
    END SELECT
 LOOP
ErrorExit39:
 Exit Sub
ErrorRoutine39:
 Resume ErrorExit39
END SUB

' lists current .sic program
SUB List.Program
 On Local Error Goto ErrorRoutine40
 IF Program.Name = None$ THEN
    Strng3 = "Program not loaded."
    Call IO.O
    Exit Sub
 END IF
 Strng3 = "Current program: "
 IF Program.Name = UnTitled$ THEN
    Strng3 = Strng3 + UnTitled$
 ELSE
    Strng3 = Strng3 + Quote$ + LCASE$(Program.Name + ".sic") + Quote$
 END IF
 CALL IO.O
 Line.Count = 1
 Continuous = False
 CALL Count.Lines(Last.Line)
 FOR Program.Line = 1 TO Last.Line
    Out2 = Program(Program.Line)
    IF LEN(Out2) THEN
       IF INSTR(White.Space, LEFT$(Out2, 1)) THEN
          Strng3 = ">" + MID$(STR$(Program.Line), 2) + Out2
       ELSE
          Strng3 = ">" + MID$(STR$(Program.Line), 2) + " " + Out2
       END IF
       Line.Count = Line.Count + INT((LEN(Strng3) - 1) / 80) + 1
       CALL IO.O
       IF Line.Count >= User.Pagelength - 2 THEN
          Line.Count = False
          IF Continuous = False THEN
             CALL More.Prompt("More(y/n/c)","ync",Outpt$,"y")
             IF Outpt$ = "n" THEN
                EXIT FOR
             END IF
             IF Outpt$ = "c" THEN
                Continuous = True
             END IF
          END IF
       END IF
    END IF
 NEXT
 IF Line.Count > False THEN
    CALL More.Prompt("More(y/n/c)","ync",Outpt$,"y")
 END IF
 Strng3 = "Program list ended."
 CALL IO.O
ErrorExit40:
 Exit Sub
ErrorRoutine40:
 Resume ErrorExit40
END SUB

' prints current .sic program
SUB LList.Program
 On Local Error Goto ErrorRoutine41
 IF PrinterSelected = False THEN
    Strng3 = "Line printer not selected."
    Call IO.O
    Exit Sub
 END IF
 IF Program.Name = None$ THEN
    Strng3 = "Program not loaded."
    Call IO.O
    Exit Sub
 END IF
 Strng2 = "Printer port(1-3,Q to quit)"
 CALL More.Prompt(Strng2,"123q",Output.Char$,"q")
 PrinterPort = Int(Val(Output.Char$))
 IF PrinterPort >= 1 And PrinterPort <= 3 THEN
    Close #TempFile2
    Filename="lpt"+Mid$(Str$(PrinterPort),2)+":"
    Open Filename For Output As #TempFile2
    Print #TempFile2, "Program: " + LCASE$(Program.Name)
    Call Count.Lines(Last.Line)
    FOR Program.Line = 1 TO Last.Line
       Out2 = Program(Program.Line)
       IF LEN(Out2) THEN
          IF INSTR(White.Space, LEFT$(Out2, 1)) THEN
             Print #TempFile2, ">" + MID$(STR$(Program.Line), 2) + Out2
          ELSE
             Print #TempFile2, ">" + MID$(STR$(Program.Line), 2) + " " + Out2
          END IF
       END IF
    NEXT
    Print #TempFile2, Chr$(12);
    Strng3 = "Program printed."
    CALL IO.O
 END IF
ErrorExit41:
 Exit Sub
ErrorRoutine41:
 Resume ErrorExit41
END SUB

' lists active accounts.
SUB List.Accounts
 On Local Error Goto ErrorRoutine42
 Strng3 = "List of currently active accounts."
 Call IO.O
 Gosub Account.Header
 Account.File$ = Current.Directory + "ACCOUNTS.SIC"
 CLOSE #SicFile
 OPEN Account.File$ FOR RANDOM SHARED AS #SicFile LEN=UserProfileRecLen
 FOR Record.Number! = 1! TO LOF(SicFile)/UserProfileRecLen
    Call Read.User.Record(Record.Number!)
    Strng3=LCASE$(RTRIM$(UserProfile.AccountLogin))
    If Len(Strng3) Then
       Strng3=Strng3+Space$(8-Len(Strng3))

       Var#=Cdbl(UserProfile.MaxRecords)
       Var$=Form$(Var#)+" KB."
       Var$=Left$(Var$,12)
       Strng3=Strng3+Space$(12-Len(Var$))+Var$

       Strng3=Left$(Strng3,21)
       Strng3=Strng3+Space$(21-Len(Strng3))
       IF UserProfile.AccountLogin = UserProfile.LibraryLogin THEN
          Strng3 = Strng3 + " Library"
       ELSE
          IF UserProfile.AccountLogin = UserProfile.GroupLogin THEN
             Strng3 = Strng3 + " Group"
          ELSE
             Strng3 = Strng3 + " Catalog"
          END IF
       END IF
       Strng3=Left$(Strng3,32)
       Strng3=Strng3+Space$(32-Len(Strng3))
       If UserProfile.PrinterSelect Then
          Strng3=Strng3+"Yes"
       Else
          Strng3=Strng3+"No"
       Endif
       Strng3=Left$(Strng3,42)
       Strng3=Strng3+Space$(42-Len(Strng3))
       Temp.Time! = UserProfile.TimeLimit
       IF Temp.Time! = 0! THEN
          Strng3 = Strng3 + UnLimited$
       ELSE
          Strng3 = Strng3 + Form$(Cdbl(Temp.Time!)) + " minute"
          If Temp.Time!>1! Then
             Strng3=Strng3+"s"
          Endif
       END IF
       Call IO.O
       Line.Count = Line.Count + 1
       IF Line.Count >= User.PageLength - 2 THEN
          Line.Count = False
          CALL More.Prompt("More(y/n)","yn",Output.Char$,"y")
          IF Output.Char$ = "n" THEN
             EXIT FOR
          END IF
          Gosub Account.Header
       END IF
    END IF
 NEXT
 IF Line.Count > False  THEN
    CALL More.Prompt("More(y/n)","yn",Output.Char$,"y")
 END IF
 Exit Sub
Account.Header:
 Line.Count = 2
 Strng3="Account  Max Records  Type      Printer   AccountTime"
 Call IO.O
 Strng3="-----------------------------------------------------"
 Call IO.O
 Return
ErrorExit42:
 Exit Sub
ErrorRoutine42:
 Resume ErrorExit42
END SUB

' lists .sic programs in current subdirectory.
SUB List.Programs
 On Local Error Goto ErrorRoutine43
 Strng3 = "Account: " + Quote$ + Account.Login + Quote$
 Strng3 = Strng3 + ": " + Form$(Cdbl(Max.Records)) + " KB. records max."
 CALL IO.O
 Catalog.Directory$ = Current.Directory + Account + ".SIC\*.SIC"
 CALL List.Files (Catalog.Directory$)
ErrorExit43:
 Exit Sub
ErrorRoutine43:
 Resume ErrorExit43
END SUB

' search for .sic programs in current subdirectory.
SUB Search.Files
 On Local Error Goto ErrorRoutine44
 DO While Lost.Carrier=False
    DO While Lost.Carrier=False
       Continuous.Display = False
       Strng3 = "[1]Library"
       CALL IO.O
       Strng3 = "[2]Group"
       CALL IO.O
       Strng3 = "[3]Catalog"
       CALL IO.O
       If Local.Mode Then
          Strng3="[4]Other"
          Call IO.O
       Endif
       If Local.Mode Then
          Strng2 = "Enter file search option(1,2,3,4,Q to quit)"
          CALL More.Prompt(Strng2,"1234q",Output.Char$,"q")
       Else
          Strng2 = "Enter file search option(1,2,3,Q to quit)"
          CALL More.Prompt(Strng2,"123q",Output.Char$,"q")
       Endif
       SELECT CASE Output.Char$
       Case "1"
          Account.Directory$=Library.Account
          Var$=Library.Login
          Gosub Display3
          Exit Do
       Case "2"
          Account.Directory$=Group.Account
          Var$=Group.Login
          Gosub Display3
          Exit Do
       Case "3"
          Account.Directory$=Account
          Var$=Account.Login
          Gosub Display3
          Exit Do
       Case "4"
          If Local.Mode Then
             Strng3 = "Enter file specification? "
             CALL IO.I
             Out9 = UCASE$(Out9)
             If Out9="" Then
                Out9="*.*"
             Endif
             Strng3 = "Searching for: " + Quote$ + Out9 + Quote$
             Call IO.O
             Catalog.Directory$ = Out9
             CALL List.Files (Catalog.Directory$)
             IF Break THEN EXIT DO
             CALL Key.Prompt
             EXIT DO
          Endif
       Case "q"
          Exit Sub
       End Select
    LOOP
    IF Break THEN EXIT DO
 LOOP
 IF Break THEN
    Break = 0
    Out9 = Nul
    Call Modem.ANSI(4,31,12)
    Call ScreenANSI(4,31,12)
    Strng3 = "*break*"
    CALL IO.O
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
 END IF
 Exit Sub
Display3:
 Strng3 = "Enter file specification? "
 CALL IO.I
 Out9 = UCASE$(Out9)
 IF RIGHT$(Out9, 4) = ".SIC" THEN
    Out9 = LEFT$(Out9, LEN(Out9) - 4)
 END IF
 IF INSTR(Out9, ".") OR INSTR(Out9, "\") OR INSTR(Out9, ":") THEN
    Strng3 = "Bad file specification."
    CALL IO.O
 ELSE
    Strng3 = "Searching account: " + Quote$ + LCASE$(Var$) + Quote$
    Strng3 = Strng3 + " for " + Quote$ + LCASE$(RTRIM$(Out9+".sic")) + Quote$
    Call IO.O
    Catalog.Directory$ = Current.Directory + Account.Directory$ + ".SIC\" + Out9 + ".SIC"
    CALL List.Files (Catalog.Directory$)
    IF Break THEN RETURN
    CALL Key.Prompt
 END IF
 Return
ErrorExit44:
 Exit Sub
ErrorRoutine44:
 Resume ErrorExit44
END SUB

' prompts for program in current subdirectory
SUB Load.Program(Var$)
 On Local Error Goto ErrorRoutine45
 CALL Save.Current.Program
Start.Load:
 If Len(Var$) Then
    Out9 = Var$
 Else
    Strng3 = "Program name? "
    CALL IO.I
 Endif
 Program.File$ = Out9
 Call Strip.Program(Program.File$,Var2)
 If Var2 Then
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
    Strng3="Cannot load program."
    Call IO.O
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
    Exit Sub
 Endif
 IF UCASE$(Program.File$) = "ACCOUNTS" THEN
    GOTO Start.Load
 END IF
 Filename = Current.Directory + Account + ".SIC\" + Program.File$ + ".SIC"
 IF DIR$(Filename) = Nul THEN
    Strng3 = "Cannot find " + Quote$ + LCASE$(RTRIM$(Program.File$+".sic")) + Quote$
    CALL IO.O
    EXIT SUB
 END IF
 CALL Read.Program(Var)
 If Var Then
    Program.Name = LCASE$(Program.File$)
    Strng3 = "Program loaded."
    CALL IO.O
 Endif
ErrorExit45:
 Exit Sub
ErrorRoutine45:
 Resume ErrorExit45
END SUB

' detects illegal filename characters
SUB Strip.Program(Var$,Var2)
 On Local Error Goto ErrorRoutine45a
 Var2=0
 If Instr(Var$,"/") Then
    Var2=-1
    Exit Sub
 Endif
 If Instr(Var$,"\") Then
    Var2=-1
    Exit Sub
 Endif
 If Instr(Var$,":") Then
    Var2=-1
    Exit Sub
 Endif
 If Instr(Var$,".") Then
    Var2=-1
    Exit Sub
 Endif
 Var$ = RTRIM$(LEFT$(Var$, 8))
ErrorExit45a:
 Exit Sub
ErrorRoutine45a:
 Resume ErrorExit45a
END SUB

' stores/erases current .sic program,
SUB Erase.Program
 On Local Error Goto ErrorRoutine46
 CALL Save.Current.Program
 CALL New.Program
 Strng3 = "Program cleared."
 CALL IO.O
ErrorExit46:
 Exit Sub
ErrorRoutine46:
 Resume ErrorExit46
END SUB

' prompts to store current .sic program
SUB Save.Current.Program
 On Local Error Goto ErrorRoutine47

 ' check program loaded
 IF Program.Name = None$ THEN
    Exit Sub
 END IF

 ' get total bytes in account
 Program.Data$ = Current.Directory + Account + ".SIC\*.*"
 Total.Bytes# = AsciizCount# (Program.Data$)

 ' check account size limit exceeded
 IF Total.Bytes# + Program.Bytes# > CDBL(Max.Records) * 1024# THEN
    Strng3 = "Account maximum record size overflow."
    Call IO.O
'    Strng3 = "Filesize= "+Form$(Program.Bytes#)+" bytes"
'    Strng3 = Strng3 + " and max. records= "+Form$(Cdbl(Max.Records)*1024#)+" bytes."
'    Call IO.O
    EXIT SUB
 END IF

 ' save program prompt
 Strng2 = "Store current program(y/n)"
 CALL More.Prompt(Strng2,"yn",Output.Char$,"y")
 If Output.Char$ = "y" Then
    CALL Save.Program
 END IF
ErrorExit47:
 Exit Sub
ErrorRoutine47:
 Resume ErrorExit47
END SUB

' remove current .sic program from memory
SUB New.Program
 On Local Error Goto ErrorRoutine48
 ' reset program name
 Program.Name = None$
 ' erase/redimension program code array
 ERASE Program
 REDIM Program(1 TO Max.Lines) AS STRING
ErrorExit48:
 Exit Sub
ErrorRoutine48:
 Resume ErrorExit48
END SUB

' exits dndsic
SUB Quit.Program
 On Local Error Goto ErrorRoutine49
 CALL More.Prompt("Quit(y/n)","yn",Outpt$,"y")
 If Outpt$="n" Then Exit Sub
 If Program.Name=None$ Then
    Eat$=Nul
 Else
    CALL Save.Current.Program
 Endif
 Call Time.Online
 Call Modem.ANSI(4,37,15)
 Call ScreenANSI(4,37,15)
 Strng3 = "Press any key to return to "
 Strng2="System"
 If Chained2 Then
    Strng2="Editor"
 Else
    If Chained Then
       Strng2="Dndbbs"
    Endif
 Endif
 Strng3=Strng3+Strng2+":"
 No.Echo = True
 Line.Length = 1
 CALL IO.I
 No.Echo = False
 Strng3 = Nul
 If Local.Mode = False Then
    Call IO.O
 Endif
 CALL Write.Account
 Account.Active = False
 Call Terminate.Program
ErrorExit49:
 END
ErrorRoutine49:
 Resume ErrorExit49
END SUB

Sub Time.Online
 On Local Error Goto ErrorRoutine49a
 Call Modem.ANSI(4,33,14)
 Call ScreenANSI(4,33,14)
 CALL Total.Time(Elapsed!, Time.Used!, Remaining!)
 Strng3 = "Time elapsed since logon: " + Form$(Cdbl(Elapsed!)) + " minute"
 If Elapsed!<>1! Then
    Strng3=Strng3+"s"
 Endif
 Strng3=Strng3+"."
 CALL IO.O
 Strng3 = "Time used this month: " + Form$(Cdbl(Time.Used!)) + " minute"
 If Time.Used!<>1! Then
    Strng3=Strng3+"s"
 Endif
 Strng3=Strng3+"."
 CALL IO.O
 Strng3 = "Time remaining: "
 IF Remaining! = -1! THEN
    Strng3 = Strng3 + UnLimited$
 ELSE
    Strng3 = Strng3 + Form$(Cdbl(Remaining!)) + " minute"
    If Remaining! <> 1! Then
       Strng3=Strng3+"s"
    Endif
    Strng3=Strng3+"."
 END IF
 CALL IO.O
ErrorExit49a:
 Exit Sub
ErrorRoutine49a:
 Resume ErrorExit49a
END SUB

' calculates account time.
' returns:
'  Var1! = Elapsed!
'  Var2! = Time.Used!
'  Var3! = Remaining!
SUB Total.Time(Var1!, Var2!, Var3!)
 On Local Error Goto ErrorRoutine50
 Elapsed! = Timeit! - Logon.Time
 IF Elapsed! < SFalse THEN
    Elapsed! = Elapsed! + 86400!
 END IF
 Elapsed! = INT(Elapsed! / 60!)
 If Elapsed! = SFalse Then
    Elapsed! = 1!
 Endif
 Var1! = Elapsed!
 Var2! = Minutes.Used + Elapsed!
 IF Max.Account.Time = 0! THEN
    Var3! = -1!
 ELSE
    Var3! = Max.Account.Time - Var2!
    IF Var3! <= SFalse THEN
       Var3! = SFalse
    END IF
 END IF
ErrorExit50:
 Exit Sub
ErrorRoutine50:
 Resume ErrorExit50
END SUB

' start a catalog program
SUB Run.Catalog
 On Local Error Goto ErrorRoutine51
 IF Program.Name = None$ THEN
    Strng3 = "Program not loaded."
    CALL IO.O
    EXIT SUB
 END IF
 Select Case Account.Login
 Case Library.Login
    Program.Running = LibraryProgram
 Case Group.Login
    Program.Running = GroupProgram
 Case Else
    Program.Running = CatalogProgram
 End Select
 Running.Program = Program.Name
 Call Read.Account.Commands(Account)
 Call Prepare.Program(True)
 Run.Type = True
 CALL Run.Program
 CALL New.Program
 Filename = Prepare.Filename
 CALL Read.Program(Var)
 Program.Name = Running.Program
 Close TempFile1, TempFile2
 If Dir$(Prepare.Filename)<>"" Then
    KILL Prepare.Filename
 Endif
ErrorExit51:
 Exit Sub
ErrorRoutine51:
 Resume ErrorExit51
END SUB

' start a group program
SUB Run.Group
 On Local Error Goto ErrorRoutine52
 IF Account.Login = Group.Login THEN
    Strng3 = "Account is group."
    CALL IO.O
    EXIT SUB
 END IF
 CALL Save.Current.Program
Start.Group:
 Strng3 = "Group program name? "
 CALL IO.I
 Group.Program$ = Out9
 Call Strip.Program(Group.Program$,Var2)
 If Var2 Then
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
    Strng3="Cannot load program."
    Call IO.O
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
    Exit Sub
 Endif
 Group.Name$ = Group.Program$
 IF UCASE$(Group.Program$) = "ACCOUNTS" THEN
    GOTO Start.Group
 END IF
 Filename = Current.Directory + Group.Account + ".SIC\" + Group.Program$ + ".SIC"
 IF DIR$(Filename) = Nul THEN
    Strng3 = "Cannot find " + Quote$ + LCASE$(RTRIM$(Group.Program$+".sic")) + Quote$
    CALL IO.O
    EXIT SUB
 END IF
 CALL Read.Program(Var)
 If Var Then
    Strng3 = "Group program loaded."
    CALL IO.O
    Current.Program = True
    Program.Running = GroupProgram
    Call Inherit(1)
    Call Prepare.Program(False)
    Run.Line = False
    Run.Type = False
    REDIM LineBreak(1 TO Max.Lines) AS INTEGER
    Call Clear.All.Arrays
    CALL Run.Program
    Current.Program = False
    CALL New.Program
 Endif
ErrorExit52:
 Exit Sub
ErrorRoutine52:
 Resume ErrorExit52
END SUB

' start a library program
SUB Run.Library
 On Local Error Goto ErrorRoutine53
 IF Account.Login = Library.Login THEN
    Strng3 = "Account is library."
    CALL IO.O
    EXIT SUB
 END IF
 CALL Save.Current.Program
Start.Library:
 Strng3 = "Library program name? "
 CALL IO.I
 Library.Program$ = Out9
 Call Strip.Program(Library.Program$,Var2)
 If Var2 Then
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
    Strng3="Cannot load program."
    Call IO.O
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
    Exit Sub
 Endif
 Library.Name$ = Library.Program$
 IF UCASE$(Library.Program$) = "ACCOUNTS" THEN
    GOTO Start.Library
 END IF
 Filename = Current.Directory + Library.Account + ".SIC\" + Library.Program$ + ".SIC"
 IF DIR$(Filename) = Nul THEN
    Strng3 = "Cannot find " + Quote$ + LCASE$(RTRIM$(Library.Program$+".sic")) + Quote$
    CALL IO.O
    EXIT SUB
 END IF
 CALL Read.Program(Var)
 If Var Then
    Strng3 = "Library program loaded."
    CALL IO.O
    Current.Program = True
    Program.Running = LibraryProgram
    Call Inherit(2)
    Call Prepare.Program(False)
    Run.Line = False
    Run.Type = False
    REDIM LineBreak(1 TO Max.Lines) AS INTEGER
    Call Clear.All.Arrays
    CALL Run.Program
    Current.Program = False
    CALL New.Program
 Endif
ErrorExit53:
 Exit Sub
ErrorRoutine53:
 Resume ErrorExit53
END SUB

' inherit group/library commands
SUB Inherit(Var)
 On Local Error Goto ErrorRoutine53a
 CALL Count.Lines(Last.Line)
 FOR Temp2% = 1 TO Last.Line
    Temp1$ = Program(Temp2%)
    Temp1$ = STRIM$(Temp1$)
    IF LEN(Temp1$) THEN
       Temp1$ = Ucase$(Temp1$)
       If Left$(Temp1$,3) = "REM" Then
          If Strim$(Mid$(Temp1$,4)) = "$INHERIT" Then
             Select Case Var
             Case 1 ' group
                Call Read.Account.Commands(Group.Account)
                Strng3 = "Group permissions inherited."
                Call IO.O
             Case 2 ' library
                Call Read.Account.Commands(Library.Account)
                Strng3 = "Library permissions inherited."
                Call IO.O
             End Select
          Endif
       Endif
       EXIT FOR
    END IF
 NEXT
ErrorExit53a:
 Exit Sub
ErrorRoutine53a:
 Resume ErrorExit53a
END SUB

' lock a program
SUB Lock.Program
 On Local Error Goto ErrorRoutine54
 IF UCASE$(Program.Name) = "ACCOUNTS" THEN
    Strng3 = "Program unavailable."
    Call IO.O
    EXIT SUB
 END IF
 IF Account.Login <> Library.Login THEN
    Strng3 = "Account is not library."
    CALL IO.O
    EXIT SUB
 END IF
 IF Program.Name = None$ THEN
    Strng3 = "Program not loaded."
    CALL IO.O
    EXIT SUB
 END IF
 IF Program.Name = UnTitled$ THEN
    Strng3 = "Program not loaded."
    CALL IO.O
    EXIT SUB
 END IF

 ' store filename
 Filename = Current.Directory + Account + ".SIC\" + Program.Name + ".SIC"
 IF DIR$(Filename) = Nul THEN
    Strng3 = "Cannot find " + Quote$ + LCASE$(RTRIM$(Program.Name+".sic")) + Quote$
    CALL IO.O
    EXIT SUB
 END IF

 ' get file attribute
 ASCIIZ=Filename+CHR$(0)
 InregsX.BX=&H0
 InregsX.DS=VARSEG(ASCIIZ)
 InregsX.DX=VARPTR(ASCIIZ)
' Call StdinInt(113,&H43) ' extended getattr
 Call StdinInt(68,&H43)

 ' check attribute
 Attribute = OutregsX.CX
 If (Attribute And &H1) = &H1 Then
    Strng3="Program already locked."
    Call IO.O
    Exit Sub
 Endif

 ' prompt to lock
 DO While Lost.Carrier=False
    Strng2 = "Lock program " + Quote$ + Program.Name + Quote$ + ". Are you sure(y/n)"
    CALL More.Prompt(Strng2,"yn",Output.Char$,"n")
    SELECT CASE Output.Char$
    CASE "y"
       EXIT DO
    CASE "n"
       Strng3 = "Program not locked."
       Call IO.O
       EXIT SUB
    END SELECT
 LOOP

 ' store attribute
 Attribute = Attribute OR &H1

 ' change file attribute
 InregsX.CX=Attribute
 InregsX.DS=VARSEG(ASCIIZ)
 InregsX.DX=VARPTR(ASCIIZ)
 Call StdinInt(67,0)

 ' check flags
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    Strng3 = "Error locking file."
 ELSE
    Strng3 = "Program locked."
 END IF
 CALL IO.O
ErrorExit54:
 Exit Sub
ErrorRoutine54:
 Resume ErrorExit54
END SUB

' unlock a program
SUB Unlock.Program
 On Local Error Goto ErrorRoutine55
 IF UCASE$(Program.Name) = "ACCOUNTS" THEN
    Strng3 = "Program unavailable."
    Call IO.O
    EXIT SUB
 END IF
 IF Account.Login <> Library.Login THEN
    Strng3 = "Account is not library."
    CALL IO.O
    EXIT SUB
 END IF
 IF Program.Name = None$ THEN
    Strng3 = "Program not loaded."
    CALL IO.O
    EXIT SUB
 END IF
 IF Program.Name = UnTitled$ THEN
    Strng3 = "Program not loaded."
    CALL IO.O
    EXIT SUB
 END IF

 ' store program name
 Filename = Current.Directory + Account + ".SIC\" + Program.Name + ".SIC"
 IF DIR$(Filename) = Nul THEN
    Strng3 = "Cannot find " + Quote$ + LCASE$(RTRIM$(Program.Name+".sic")) + Quote$
    CALL IO.O
    EXIT SUB
 END IF

 ' get file attribute
 ASCIIZ=Filename+CHR$(0)
 InregsX.BX=&H0
 InregsX.DS=VARSEG(ASCIIZ)
 InregsX.DX=VARPTR(ASCIIZ)
' Call StdinInt(113,&H43) ' extended getattr
 Call StdinInt(68,&H43)

 ' check attribute
 Attribute = OutregsX.CX
 If (Attribute And &H1) = &H0 Then
    Strng3="Program already unlocked."
    Call IO.O
    Exit Sub
 Endif

 ' prompt to unlock
 DO While Lost.Carrier=False
    Strng2 = "Unlock program " + Quote$ + Program.Name + Quote$ + ". Are you sure(y/n)"
    CALL More.Prompt(Strng2,"yn",Output.Char$,"n")
    SELECT CASE Output.Char$
    CASE "y"
       EXIT DO
    CASE "n"
       Strng3 = "Program not unlocked."
       Call IO.O
       EXIT SUB
    END SELECT
 LOOP

 ' store attribute
 Attribute = Attribute AND NOT &H1

 ' change file attribute
 InregsX.CX=Attribute
 InregsX.DS=VARSEG(ASCIIZ)
 InregsX.DX=VARPTR(ASCIIZ)
 Call StdinInt(67,0)

 ' check flags
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    Strng3 = "Error unlocking file."
 ELSE
    Strng3 = "Program unlocked."
 END IF
 CALL IO.O
ErrorExit55:
 Exit Sub
ErrorRoutine55:
 Resume ErrorExit55
END SUB

' starts current .sic program
SUB Run.Program
 On Error Goto Error.Routine
 ' store current drive/directory
 Program.Directory = Curdir$

 ' check to continue
 If Run.Line Then
    Program.Line = Run.Line - 1
    Goto Continue1
 Endif

 ' restore run variables
 Close
 Debug.Active = -1
 DataLine = 1
 DataNumber = False
 ErrorLine = False
 ErrorType = False
 ErrorValue = False
 Nested.Gosub = False
 Max.Gosubs = 10
 Program.Line = False
 ScreenWidth = 80
 ScreenHeight = 25
 ScreenMode = False
 Visible = 1

 REDIM GosubReturn(1 TO 10) AS INTEGER, _
 Variables(1 to 26) As DOUBLE, _
 Arrays(1 To 26, 1 To MaxArrays) AS DOUBLE

 FOR Count1 = 1 TO 26
    Strngs(Count1) = Nul
 NEXT
 FOR Count1 = 1 TO MaxFNs
    Definitions(Count1) = Nul
 NEXT

 ' prepare to run program
Continue1:
 Strng3 = CtrlBreak1$
 If Local.Mode Then
    Strng3 = CtrlBreak2$
 Endif
 CALL IO.O
 Out3=Out4

 Allow.Break = True
 Break = False
 Program.Start! = Timeit!
 COLOR 7,0
 CALL ScreenANSI(1,37,7)
 CALL Count.Lines(Last.Line)

 ' start program flow
 Break.Flag1 = False
 Break.Flag2 = False
 Break.Flag3 = False
 Break.Flag4 = False
 Break.Flag5 = False
 Break.Flag6 = False
 Break.Flag7 = False

 ' check for break variables
 FOR VarBreak = 1 TO 26
    IF VariableBreak(VarBreak) THEN
       ' store first break variable
       Break.Flag3 = VarBreak
       EXIT FOR
    END IF
 NEXT

 ' check for break variables
 FOR VarBreak = 1 TO 26
    IF LEN(VariableBreak2(VarBreak))<>False THEN
       ' store first break variable
       Break.Flag5 = VarBreak
       EXIT FOR
    END IF
 NEXT

 ' check for break variables
 FOR VarBreak = 1 TO 26
    FOR VarBreak2 = 1 TO MaxArrays
       IF VariableBreak3(VarBreak, VarBreak2) THEN
          ' store first break variable
          Break.Flag7 = VarBreak
          EXIT FOR
       END IF
    NEXT
    IF Break.Flag7 THEN
       EXIT FOR
    END IF
 NEXT

 ' start program loop
 VarX=False
 DO
    ' check break/carrier
    CALL Check.Carrier
    If Lost.Carrier Then
       Exit Sub
    Endif
    CALL Check.Program
    IF Break THEN
       Break=False
       VarX=True
       EXIT DO
    END IF

    ' increment next program line number
    Program.Line = Program.Line + 1
    IF Program.Line > Last.Line THEN
       EXIT DO
    END IF
    IF Program.Line > Max.Lines THEN
       EXIT DO
    END IF

    ' test line list/line break
    If Debug.Active Then
       IF LEN(Program(Program.Line))<>False THEN
          ' display debug list line
          IF ListLines THEN
             IF POS(0) > 1 THEN
                CALL IO.O
             END IF
             Strng3="Debug line: "+Form$(Cdbl(Program.Line)) + "."
             Call IO.O
          END IF
          ' test debug break line
          IF LineBreak(Program.Line) THEN
             Break.Flag1 = True
             EXIT DO
          END IF
       END IF
    Endif

    ' process program line
    Out2 = STRIM$(Program(Program.Line))
    IF LEN(Out2) THEN
       ' check break
       IF Local.Mode Then
          CALL Keyboard
       ELSE
          CALL Get.Modem
       END IF
       IF Break THEN
          Break=False
          VarX=True
          EXIT DO
       END IF

       ' start parser
       Recurse = False
       CALL Enter.Equate
       Out4=Out3

       ' test break variables
       If Debug.Active Then
          IF Break.Flag3 THEN
             GOSUB Test.Break1
             IF Break.Flag2 THEN
                EXIT DO
             END IF
          END IF
          IF Break.Flag5 THEN
             GOSUB Test.Break2
             IF Break.Flag4 THEN
                EXIT DO
             END IF
          END IF
          IF Break.Flag7 THEN
             GOSUB Test.Break3
             IF Break.Flag6 THEN
                EXIT DO
             END IF
          END IF
       Endif
    END IF
 LOOP

 ' restore BASIC segment
 DEF SEG

 ' reset current program line
 Run.Line = Program.Line

 ' reset color
 COLOR 7,0
 Call Modem.ANSI(4,37,0)
 Call ScreenANSI(4,37,0)

 ' test control-break
 IF VarX THEN
    Call Modem.ANSI(4,31,12)
    Call ScreenANSI(4,31,12)
    Call Get.Cursor(VarX)
    IF VarX > 1 THEN
       Strng3 = NUL
       Call IO.O
    END IF
    Call Modem.ANSI(4,31,12)
    Call ScreenANSI(4,31,12)
    Strng3 = "*break*"
    CALL IO.O
    Strng3 = "Line: " + Form$(Cdbl(Run.Line)) + "."
    Call IO.O
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
    Break = False
    Out9 = Nul
    Strng3 = "Program run halted. "
    Goto Stop.Loop
    EXIT SUB
 END IF

 ' display break line
 Allow.Break = False
 Break = False
 Buffer = Nul
 Out9 = Nul
 IF LineFeed THEN
    Strng3 = Nul
    CALL IO.O
 END IF

 ' display any break messages
 IF Break.Flag1 THEN
    Call Modem.ANSI(4,31,12)
    Call ScreenANSI(4,31,12)
    Strng3 = "*line break*"
    Call IO.O
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
    Strng3 = "Line: " + Form$(Cdbl(Run.Line)) + "."
    Call IO.O
    Strng3 = "Program run halted. "
    Goto Stop.Loop
    EXIT SUB
 END IF
 IF Break.Flag2 THEN
    Call Modem.ANSI(4,31,12)
    Call ScreenANSI(4,31,12)
    Strng3 = "*variable break*"
    Call IO.O
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
    Strng3 = "Line: " + Form$(Cdbl(Run.Line)) + "."
    Call IO.O
    Strng3 = "Variable: " + CHR$(VarBreak + 64)
    Call IO.O
    Strng3 = "Test: " + Symbol$ + " " + LTRIM$(STR$(VariableValue(VarBreak)))
    Call IO.O
    Strng3 = "Value: " + LTRIM$(STR$(Variables(VarBreak)))
    Call IO.O
    Strng3 = "Program run halted. "
    Goto Stop.Loop
    EXIT SUB
 END IF
 IF Break.Flag4 THEN
    Call Modem.ANSI(4,31,12)
    Call ScreenANSI(4,31,12)
    Strng3 = "*variable break*"
    Call IO.O
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
    Strng3 = "Line: " + Form$(Cdbl(Run.Line)) + "."
    Call IO.O
    Strng3 = "Variable: " + CHR$(VarBreak + 64) + "$"
    Call IO.O
    IF LEN(VariableValue2(VarBreak)) > 40 THEN
       Strng3 = "Test: " + Symbol$ + " " + Quote$ + LEFT$(VariableValue2(VarBreak), 40) + "..."
       Call IO.O
    ELSE
       Strng3 = "Test: " + Symbol$ + " " + Quote$ + VariableValue2(VarBreak) + Quote$
       Call IO.O
    END IF
    IF LEN(Strngs(VarBreak)) > 40 THEN
       Strng3 = "Value: " + Quote$ + LEFT$(Strngs(VarBreak), 40) + "..."
       Call IO.O
    ELSE
       Strng3 = "Value: " + Quote$ + Strngs(VarBreak) + Quote$
       Call IO.O
    END IF
    Strng3 = "Program run halted. "
    Goto Stop.Loop
    EXIT SUB
 END IF
 IF Break.Flag6 THEN
    Call Modem.ANSI(4,31,12)
    Call ScreenANSI(4,31,12)
    Strng3 = "*variable break*"
    Call IO.O
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
    Strng3 = "Line: " + Form$(Cdbl(Run.Line)) + "."
    Call IO.O
    Strng3 = "Variable: " + CHR$(VarBreak + 64) + "(" + MID$(STR$(VarBreak2), 2) + ")"
    Call IO.O
    Strng3 = "Test: " + Symbol$ + " " + LTRIM$(STR$(VariableValue3(VarBreak, VarBreak2)))
    Call IO.O
    Strng3 = "Value: " + LTRIM$(STR$(Arrays(VarBreak, VarBreak2)))
    Call IO.O
    Strng3 = "Program run halted. "
    Goto Stop.Loop
    EXIT SUB
 END IF

 ' parse remaining token
 IF PrinterLF THEN
    CALL IO.O
 END IF

 ' reset current program line
 Program.Line = Max.Lines

 ' restore current drive/directory
 CHDRIVE Program.Directory
 CHDIR Program.Directory

 ' end program
 CALL End.Program
 Strng3 = "Program run ended. "
Stop.Loop:
 ' display end program run
 Call Modem.ANSI(4,37,15)
 Call ScreenANSI(4,37,15)
 Program.Time! = Timeit! - Program.Start!
 IF Program.Time! < SFalse THEN
    Program.Time! = Program.Time! + 86400!
 END IF
 If Timing.Toggle=0 Then
    Strng3 = Strng3 + "Program runtime:"+Str$(Program.Time!)+" seconds."
 Endif
 CALL IO.O
 Call Sic.Status.Line
 Exit Sub

Test.Break1:
 FOR VarBreak = Break.Flag3 TO 26
    IF VariableBreak(VarBreak) THEN
       SELECT CASE VariableBreak(VarBreak)
       CASE -1
          IF Variables(VarBreak) = VariableValue(VarBreak) THEN
             Break.Flag2 = True
             Symbol$ = "="
             RETURN
          END IF
       CASE 1
          IF Variables(VarBreak) <> VariableValue(VarBreak) THEN
             Break.Flag2 = True
             Symbol$ = "<>"
             RETURN
          END IF
       CASE 2
          IF Variables(VarBreak) > VariableValue(VarBreak) THEN
             Break.Flag2 = True
             Symbol$ = ">"
             RETURN
          END IF
       CASE 3
          IF Variables(VarBreak) >= VariableValue(VarBreak) THEN
             Break.Flag2 = True
             Symbol$ = ">="
             RETURN
          END IF
       CASE 4
          IF Variables(VarBreak) < VariableValue(VarBreak) THEN
             Break.Flag2 = True
             Symbol$ = "<"
             RETURN
          END IF
       CASE 5
          IF Variables(VarBreak) <= VariableValue(VarBreak) THEN
             Break.Flag2 = True
             Symbol$ = "<="
             RETURN
          END IF
       END SELECT
    END IF
 NEXT
 RETURN

Test.Break2:
 FOR VarBreak = Break.Flag5 TO 26
    IF VariableBreak2(VarBreak) THEN
       SELECT CASE VariableBreak2(VarBreak)
       CASE -1
          IF Strngs(VarBreak) = VariableValue2(VarBreak) THEN
             Break.Flag4 = True
             Symbol$ = "="
             RETURN
          END IF
       CASE 1
          IF Strngs(VarBreak) <> VariableValue2(VarBreak) THEN
             Break.Flag4 = True
             Symbol$ = "<>"
             RETURN
          END IF
       CASE 2
          IF Strngs(VarBreak) > VariableValue2(VarBreak) THEN
             Break.Flag4 = True
             Symbol$ = ">"
             RETURN
          END IF
       CASE 3
          IF Strngs(VarBreak) >= VariableValue2(VarBreak) THEN
             Break.Flag4 = True
             Symbol$ = ">="
             RETURN
          END IF
       CASE 4
          IF Strngs(VarBreak) < VariableValue2(VarBreak) THEN
             Break.Flag4 = True
             Symbol$ = "<"
             RETURN
          END IF
       CASE 5
          IF Strngs(VarBreak) <= VariableValue2(VarBreak) THEN
             Break.Flag4 = True
             Symbol$ = "<="
             RETURN
          END IF
       END SELECT
    END IF
 NEXT
 RETURN

Test.Break3:
 FOR VarBreak = Break.Flag7 TO 26
    FOR VarBreak2 = 1 TO MaxArrays
       IF VariableBreak3(VarBreak, VarBreak2) THEN
          SELECT CASE VariableBreak3(VarBreak, VarBreak2)
          CASE -1
             IF Arrays(VarBreak, VarBreak2) = VariableValue3(VarBreak, VarBreak2) THEN
                Break.Flag6 = True
                Symbol$ = "="
                RETURN
             END IF
          CASE 1
             IF Arrays(VarBreak, VarBreak2) <> VariableValue3(VarBreak, VarBreak2) THEN
                Break.Flag6 = True
                Symbol$ = "<>"
                RETURN
             END IF
          CASE 2
             IF Arrays(VarBreak, VarBreak2) > VariableValue3(VarBreak, VarBreak2) THEN
                Break.Flag6 = True
                Symbol$ = ">"
                RETURN
             END IF
          CASE 3
             IF Arrays(VarBreak, VarBreak2) >= VariableValue3(VarBreak, VarBreak2) THEN
                Break.Flag6 = True
                Symbol$ = ">="
                RETURN
             END IF
          CASE 4
             IF Arrays(VarBreak, VarBreak2) < VariableValue3(VarBreak, VarBreak2) THEN
                Break.Flag6 = True
                Symbol$ = "<"
                RETURN
             END IF
          CASE 5
             IF Arrays(VarBreak, VarBreak2) <= VariableValue3(VarBreak, VarBreak2) THEN
                Break.Flag6 = True
                Symbol$ = "<="
                RETURN
             END IF
          END SELECT
       END IF
    NEXT
 NEXT
 RETURN
END SUB

' stores current .sic program
SUB Save.Program
 On Local Error Goto ErrorRoutine56

 ' check program loaded
 IF Program.Name = None$ THEN
    Strng3="Program not loaded."
    Call IO.O
    EXIT SUB
 END IF

 ' get total bytes in account
 Program.Data$ = Current.Directory + Account + ".SIC\*.*"
 Total.Bytes# = AsciizCount# (Program.Data$)

 ' calculate total bytes in program
 Program.Bytes# = Dfalse
 Call Count.Lines(Last.Line)
 FOR Program.Line = 1 TO Last.Line
    Line1$ = Program(Program.Line)
    IF RTRIM$(Line1$) <> NUL THEN
       Program.Bytes# = Program.Bytes# + CDBL(LEN(MID$(STR$(Program.Line), 2) + Line1$)) + 2#
    END IF
 NEXT

 ' check account size limit exceeded
 IF Total.Bytes# + Program.Bytes# > CDBL(Max.Records) * 1024# THEN
    Strng3 = "Account maximum record size overflow."
    Call IO.O
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
    Strng3 = " Filesize: "+Form$(Program.Bytes#)+" bytes"
    Strng3 = Strng3 + " and total bytes: "+Form$(Total.Bytes#)+" bytes."
    Call IO.O
    Strng3 = " Exceed max. records: "+Form$(Cdbl(Max.Records)*1024#)+" bytes."
    Call IO.O
    EXIT SUB
 END IF
Start.Save:
 DO While Lost.Carrier=False
    DO While Lost.Carrier=False
       Strng3 = "Program name to save"
       IF Program.Name <> UnTitled$ THEN
          Strng3 = Strng3 + "(" + Program.Name + ")"
       END IF
       Strng3 = Strng3 + "? "
       CALL IO.I
       Name.Store$ = Out9
       IF Name.Store$ = Nul THEN
          IF Program.Name <> UnTitled$ THEN
             Name.Store$=Program.Name
	     EXIT DO
	  END IF
          Strng2 = "Don't store program(y/n)"
          CALL More.Prompt(Strng2,"yn",Output.Char$,"y")
          SELECT CASE Output.Char$
          CASE "y"
             EXIT SUB
          END SELECT
       ELSE
          Call Strip.Program(Name.Store$, Var2)
          If Var2=0 Then
             EXIT DO
          Endif
          Call Modem.ANSI(4,37,15)
          Call ScreenANSI(4,37,15)
          Strng3 = "Illegal characters in program name."
          Call IO.O
          Call Modem.ANSI(4,33,14)
          Call ScreenANSI(4,33,14)
       END IF
    LOOP
    Name.Store$ = RTRIM$(LEFT$(Name.Store$, 8))
    IF UCASE$(Name.Store$) = "ACCOUNTS" THEN
       GOTO Start.Save
    END IF
    Program.Name=Name.Store$
    Strng2 = "Store program as " + Quote$ + LCASE$(RTRIM$(Program.Name)) + Quote$ + "(y/n/q)"
    CALL More.Prompt(Strng2,"ynq",Output.Char$,"y")
    SELECT CASE Output.Char$
    CASE "y"
       Output.Char$ = "y"
       Filename = Current.Directory + Account + ".SIC\" + Program.Name + ".SIC"
       IF DIR$(Filename) <> Nul THEN
          Strng2 = "Program already exists. Overwrite(y/n)"
          CALL More.Prompt(Strng2,"yn",Output.Char$,"y")
       END IF
       IF Output.Char$ = "y" THEN
          Strng3 = "Writing " + Form$(Program.Bytes#) + " bytes."
          Call IO.O
          CALL Store.Program
          Strng3 = "Program " + Quote$ + LCASE$(RTRIM$(Program.Name)) + Quote$ + " stored to disk."
          Call IO.O
	  EXIT DO
       END IF
    CASE "n", "q"
       Strng3 = "Program not stored to disk."
       Call IO.O
       EXIT DO
    END SELECT
 LOOP
ErrorExit56:
 Exit Sub
ErrorRoutine56:
 Resume ErrorExit56
END SUB

' displays account information
SUB System.Accounting
 On Local Error Goto ErrorRoutine57
 Select Case Account.Login
 Case Library.Login 
    Strng3 = "Library"
 Case Group.Login
    Strng3 = "Group"
 Case Else
    Strng3 = "Catalog"
 End Select
 Strng3 = Strng3 + " account: " + Quote$ + Account.Login + Quote$ + "."
 CALL IO.O
 Program.Data$ = Current.Directory + Account + ".SIC\*.*"
 Total.Bytes# = AsciizCount# (Program.Data$)
 Strng2 = ParseSize$ (Total.Bytes#)
 Strng3 = "Account size: " + Strng2
 CALL IO.O
 Strng3 = "Maximum records: " + Form$(Cdbl(Max.Records)) + " KB."
 CALL IO.O
 Strng3 = "Maximum account time: "
 IF Max.Account.Time = 0! THEN
    Strng3 = Strng3 + UnLimited$
 ELSE
    Strng3 = Strng3 + Form$(Cdbl(Max.Account.Time)) + " minute"
    If Max.Account.Time>1! Then
       Strng3=Strng3+"s"
    Endif
    Strng3=Strng3+"."
 END IF
 CALL IO.O
 Strng3 = "Maximum program runtime: "
 IF Max.Time = 0! THEN
    Strng3 = Strng3 + UnLimited$
 ELSE
    Strng3 = Strng3 + Form$(Cdbl(Max.Time)) + " minute"
    If Max.Time>1! Then
       Strng3=Strng3+"s"
    Endif
    Strng3=Strng3+"."
 END IF
 CALL IO.O
 If Allow.Select Then
    Strng3 = "Line printer allowed: Yes."
    CALL IO.O
 Endif
ErrorExit57:
 Exit Sub
ErrorRoutine57:
 Resume ErrorExit57
END SUB

' immediate parsing prompt.
SUB Whatis.Command
 On Error Goto Error.Routine
 Strng3 = "Enter 'quit' to return to DNDSIC."
 CALL IO.O
 Strng3 = "  Enter 'debug' to start DEBUG command."
 CALL IO.O
Start.Whatis:
 DO While Lost.Carrier=False
    Allow.Break=True
    Break = False
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
    DO While Lost.Carrier=False
       If Brief.Prompt=0 Then
          If Len(NodeX) Then
             Strng3 = NodeX+":"
          Else
             Strng3 = "#:"
          Endif
       Else
          If Len(NodeX) Then
             Strng3 = "Whatis "+NodeX+":"
          Else
             Strng3 = "Whatis #:"
          Endif
       Endif
       Temp0=2
       Line.Length=255
       CALL IO.I
       If Len(Out9) Then
          Exit Do
       Endif
       Temp0=0
       Allow.Break=False
       VarX = Break
       Break = False
       IF VarX THEN
          Call Modem.ANSI(4,31,12)
          Call ScreenANSI(4,31,12)
          Call IO.O
          Strng3 = "*break*"
          CALL IO.O
          Call Modem.ANSI(4,33,14)
          Call ScreenANSI(4,33,14)
          Goto ExitWhatis
       END IF
    Loop
    IF UCASE$(Out9) = "QUIT" THEN
       EXIT DO
    END IF
    IF UCASE$(Out9) = "DEBUG" THEN
       CALL Debug.Command
       Goto Start.Whatis
    END IF
    Out2 = Out9
    Error.Type = False
    Program.Line = False
    Out2 = TTRIM$(Out2, True)
    Recurse = False
    Allow.Break = True
    Break = False
    Program.Start! = Timeit!
    Program.Start2! = Timeit!
    ScreenWidth = 80
    ScreenHeight = 25
    ScreenMode = False
    Visible = 1
    CALL Enter.Equate
    VarX = Break
    Break = False
    IF VarX THEN
       Call Modem.ANSI(4,31,12)
       Call ScreenANSI(4,31,12)
       Strng3 = "*break*"
       CALL IO.O
       Call Modem.ANSI(4,33,14)
       Call ScreenANSI(4,33,14)
    END IF
    Allow.Break = False
    Locate ,,1
    VarX = Break
    Break = False
    Buffer = Nul
    Out9 = Nul
    IF LineFeed THEN
       Strng3 = Nul
       CALL IO.O
    END IF
    Program.Time! = Timeit! - Program.Start2!
    IF Program.Time! < SFalse THEN
       Program.Time! = Program.Time! + 86400!
    END IF
    If Timing.Toggle=0 Then
       Strng3 = "Whatis runtime:"+Str$(Program.Time!)+" seconds."
       CALL IO.O
    Endif
 LOOP
ExitWhatis:
 CALL End.Program
 Call Modem.ANSI(4,33,14)
 Call ScreenANSI(4,33,14)
 Strng3 = "Whatis run ended."
 CALL IO.O
 Call Sic.Status.Line
END SUB

' writes out the current .sic program to file
SUB Store.Program
 On Local Error Goto ErrorRoutine58
 CLOSE #TempFile2
 OPEN Filename FOR OUTPUT AS #TempFile2
 CALL Count.Lines(Last.Line)
 FOR Line.Number = 1 TO Last.Line
    ProgramLine$ = Program(Line.Number)
    IF STRIM$(ProgramLine$) <> Nul THEN
       IF INSTR(White.Space, LEFT$(ProgramLine$, 1)) THEN
          PRINT #TempFile2, MID$(STR$(Line.Number), 2) + ProgramLine$
       ELSE
          PRINT #TempFile2, MID$(STR$(Line.Number), 2) + " " + ProgramLine$
       END IF
    END IF
 NEXT
ErrorExit58:
 Exit Sub
ErrorRoutine58:
 Resume ErrorExit58
END SUB

' loads a program from disk
SUB Read.Program(Var)
 On Local Error Goto ErrorRoutine59
 Var = -1
 Var2 = 0
 CLOSE #TempFile2
 CALL New.Program
 OPEN Filename FOR INPUT AS #TempFile2
 DO WHILE NOT EOF(TempFile2)
    LINE INPUT #TempFile2, Out2
    IF LEFT$(Out2, 1) = ";" OR LEFT$(Out2, 1) = "'" THEN ' commented line
       Eat$ = Nul
    ELSE
       FOR Blanks = 1 TO LEN(White.Space)
          Imbedded = INSTR(Out2, MID$(White.Space, Blanks, 1))
          IF Imbedded THEN
             If Imbedded>5 Then
                Var2=Var2+1
             Else
                Line.Number = INT(VAL(LEFT$(Out2, Imbedded - 1)))
                Select Case Line.Number
                Case Is <= 0 , Is > Max.Lines
                   Var2 = Var2 + 1
                Case Else
                   Program(Line.Number) = MID$(Out2, Imbedded)
                   EXIT FOR
                END SELECT
             Endif
          END IF
       NEXT
    END IF
 LOOP
 If Var2 Then
    Strng3="Error:"
    If Var2>1 Then
       Strng3=Strng3+" "+Form$(Cdbl(Var2))
    Endif
    Strng3=Strng3+" line number"
    If Var2>1 Then Strng3=Strng3+"s"
    Strng3=Strng3+" out of range ignored."
    Call IO.O
 Endif
ErrorExit59:
 Exit Sub
ErrorRoutine59:
 Var = 0
 Error.Num=Err
 Var3$="Error:"+Str$(Error.Num)
 Select Case Error.Num
 Case 7
    Var3$="Out of memory"
 Case 9
    Var3$="Subscript out of range"
 Case 14
    Var3$="Out of string space"
 End Select
 Strng3=Var3$+" loading program."
 Call Modem.ANSI(4,33,14)
 Call ScreenANSI(4,33,14)
 Call IO.O
 Program.Name = None$
 Resume ErrorExit59
END SUB

' displays a help file
SUB List.Help.File(Filename.Help$)
 On Local Error Goto ErrorRoutine60
 CLOSE #TempFile2
 Filename = Filename.Help$
 IF DIR$(Filename) = Nul THEN
    Strng3 = "Help file " + Quote$ + Filename + Quote$ + " not found."
    CALL IO.O
    EXIT SUB
 END IF
 OPEN Filename FOR INPUT AS #TempFile2
 Allow.Break=True
 Break=False
 Continuous = False
 Line.Count = False
 DO WHILE NOT EOF(TempFile2)
    LINE INPUT #TempFile2, Strng3
    CALL IO.O
    If Break Or Lost.Carrier Then
       Exit Do
    Endif
    If Continuous=0 Then
       Line.Count = Line.Count + 1
       If Line.Count >= User.PageLength - 2 Then
          Line.Count = False
          CALL More.Prompt("More(y/n/c)","ync",Outpt$,"y")
          IF Outpt$ = "n" THEN
             EXIT DO
          END IF
          IF Outpt$ = "c" THEN
             Continuous=-1
          END IF
       END IF
    Endif
 LOOP
 Allow.Break=False
 Break=False
 CALL Key.Prompt
ErrorExit60:
 Exit Sub
ErrorRoutine60:
 Resume ErrorExit60
END SUB

' reads the command permissions from the profile data file
SUB Read.Account.Commands(Var$)
 On Local Error Goto ErrorRoutine61
 ' make filename
 Account.File$ = Current.Directory + "ACCOUNTS.SIC"
 ' read file
 CLOSE #SicFile
 OPEN Account.File$ FOR RANDOM SHARED AS #SicFile LEN=UserProfileRecLen
 ' search for account
 FOR Record.Number! = 1! TO LOF(SicFile)/UserProfileRecLen
    Call Read.User.Record(Record.Number!)
    IF RTRIM$(UserProfile.Account) = RTRIM$(Var$) THEN
       ' store maximum limit of account size
       Max.Limit = UserProfile.MaxRecords
       EXIT SUB
    END IF
 NEXT
 Strng3 = "Unable to read user profile."
 Call IO.O
ErrorExit61:
 Exit Sub
ErrorRoutine61:
 Resume ErrorExit61
END SUB

' searchs current .sic program
SUB Search.Program
 On Local Error Goto SearchErr1
 CALL Count.Lines(Last.Line)
 Call Modem.ANSI(4,37,15)
 Call ScreenANSI(4,37,15)
 IF Last.Search.Line > False THEN
    Strng3 = "Continue search(y/n)?"
    Call IO.I
    IF Yes THEN
       Var1 = Last.Search.Case
       Var1$ = Last.Search.Keyword
       Start.Line = Last.Search.Line + 1
       IF Start.Line > Max.Lines THEN
          Call Modem.ANSI(4,37,15)
          Call ScreenANSI(4,37,15)
          Strng3 = "Program search ended."
          Call IO.O
          EXIT SUB
       END IF
       Strng3 = "Continuing search from line: " + Form$(Cdbl(Start.Line)) + "."
       Call IO.O
       GOTO Start.Search
    END IF
 END IF
 DO While Lost.Carrier=False
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
    Strng3 = "Search string?"
    Call IO.I
    IF Out9 <> Nul THEN
       EXIT DO
    END IF
 LOOP
 Var1$ = Out9
 Strng3 = "Case-sensitive(y/n)?"
 Call IO.I
 Var1 = Yes
 Start.Line = 1
Start.Search:
 Strng3 = "Searching Program: "+Quote$+Program.Name+Quote$
 Call IO.O
 FOR Program.Line = Start.Line TO Last.Line
    Out2 = Rtrim$(Program(Program.Line))
    IF LEN(Out2) THEN
       CALL InstrSUB1(Flag,Var1$,Out2,Var1) ' substring ?, * match
       IF Flag THEN
          Call Modem.ANSI(4,33,14)
          Call ScreenANSI(4,33,14)
          IF INSTR(White.Space, LEFT$(Out2, 1)) THEN
             Strng3 = ">" + MID$(STR$(Program.Line), 2) + Out2
             Call IO.O
          ELSE
             Strng3 = ">" + MID$(STR$(Program.Line), 2) + " " + Out2
             Call IO.O
          END IF
          CALL More.Prompt("More(y/n)","yn",Output.Char$,"y")
          IF Output.Char$="n" Then
             EXIT FOR
          END IF
       END IF
    END IF
 NEXT
 Last.Search.Case = Var1
 Last.Search.Line = Program.Line
 Last.Search.Keyword = Var1$
 Call Modem.ANSI(4,37,15)
 Call ScreenANSI(4,37,15)
 Strng3="Program search ended."
 Call IO.O
SearchExit1:
 Exit Sub
SearchErr1:
 Resume SearchExit1
END SUB

' function to match case-sensitive substring with ?, * characters in substring
SUB InstrSUB1(Var,Var1$,Var2$,VarQ)

' see if Var1$ matches in Var2$
On Local Error Goto ErrorTrap24x5

' store case-sensitive string match variables
If VarQ Then
   S2$ = Var1$
   S3$ = Var2$
Else
   S2$ = Lcase$(Var1$)
   S3$ = Lcase$(Var2$)
Endif

' check default instr
IF INSTR(S2$, "*") = 0 THEN
   IF INSTR(S2$, "?") = 0 THEN
      Var = INSTR(S3$, S2$)
      EXIT SUB
   END IF
END IF

Var = -1 ' assume match

' see if S2$ matches in S3$ with substrings
FOR S3 = 1 TO LEN(S3$)
   S1$ = MID$(S3$, S3)
   P1 = 1 ' pointer to S1$
   P2 = 1 ' pointer to S2$
   DO
      ' check match
      IF P2 > LEN(S2$) THEN
         EXIT SUB
      END IF

      ' check character in S2$ at P2
      V$ = MID$(S2$, P2, 1)
      SELECT CASE V$
      CASE "*" ' global character
         ' scan to next char
         IF P2 > LEN(S2$) THEN
            EXIT DO
         END IF
         S4$ = MID$(S2$, P2 + 1, 1)
         SELECT CASE S4$
         CASE "*", "?"
            P2 = P2 + 1
         CASE ELSE
            DO
               IF MID$(S1$, P1, 1) = S4$ THEN
                  EXIT DO
               END IF
               IF P1 >= LEN(S1$) THEN
                  EXIT DO
               END IF
               P1 = P1 + 1
            LOOP
            P3 = P1
            P2 = P2 + 1
         END SELECT
      CASE "?" ' wildcard character
         P1 = P1 + 1
         P2 = P2 + 1
      CASE ELSE ' ascii character
         IF MID$(S1$, P1, 1) <> V$ THEN ' no match
            EXIT DO
         END IF
         P1 = P1 + 1
         P2 = P2 + 1
      END SELECT
   LOOP
NEXT
Var = 0 ' no match
ErrorResume24x5:
 Exit Sub
ErrorTrap24x5:
 Resume ErrorResume24x5
End Sub

Sub Save.Screen(Var)
 On Local Error Goto Error.Trap62
 If Var=False Then
    VarX3=(Max.Row-1)*80+80
    Redim Temp.ArrayY(VarX3) As Long
    Redim Temp.ArrayZ(VarX3) As Double
    For Var1=1 To Max.Row
       For Var2=1 To 80
          TempZ1=Screen(Var1,Var2)
          TempZ2=Screen(Var1,Var2,1)
          VarX2=(Var1-1)*80+Var2
          Temp.ArrayY(VarX2)=TempZ1
          Temp.ArrayZ(VarX2)=TempZ2
       Next
    Next
 Else
    Cls
    For VarX=Max.Row To 1 Step -1
       For VarY=1 To 80
          VarX2=(VarX-1)*80+VarY
          If Temp.ArrayY(VarX2)<>32 Then
             Goto Next.Line
          Endif
       Next
    Next
Next.Line:
    TempX=VarX
    For Var1=1 To VarX ' Max.Row
       For Var2=1 To 80
          VarX2=(Var1-1)*80+Var2
          VarB=Int(Temp.ArrayZ(VarX2)/16)
          VarF=Temp.ArrayZ(VarX2) Mod 16
          TempZ1=Temp.ArrayY(VarX2)
          Locate Var1,Var2,0
          Color VarF,VarB
          Print Chr$(TempZ1);
       Next
    Next
 Endif
Error.Resume62:
 Exit Sub
Error.Trap62:
 Resume Error.Resume62
End Sub

