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

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

 ' declare keyboard functions.
 Declare Function InkeyX$()
 DECLARE FUNCTION KeyIS()
 DECLARE FUNCTION KeyboardChar$()

 ' define global error routine
 On Error Goto Error.Routine

 ' reseed random number generator
 RANDOMIZE TIMER

 ' initialize some variables
 Foreground = 15
 Background = 0
 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

 ' reset alarm arrays
 ReDim Alarm.Flags(1 to Max.Alarms) As Integer
 ReDim Alarm.Start(1 to Max.Alarms) As Single
 ReDim Alarm.Time(1 to Max.Alarms) As Single
 ReDim Alarm.Type(1 to Max.Alarms) As Integer
 ReDim Alarm.Beep(1 to Max.Alarms) As Integer
 ReDim Alarm.Color(1 to Max.Alarms) As Integer
 ReDim Alarm.Music(1 to Max.Alarms) As Integer
 ReDim Alarm.Mess(1 to Max.Alarms) As String
 For VarZ=1 To Max.Alarms
    Alarm.Flags(VarZ)=0
    Alarm.Start(VarZ)=0!
    Alarm.Time(VarZ)=0!
    Alarm.Type(VarZ)=0
    Alarm.Beep(VarZ)=0
    Alarm.Color(VarZ)=0
    Alarm.Music(VarZ)=0
    Alarm.Mess(VarZ)=""
 Next
 MakeBeep = -1
 Var$ = Environ$("SICMAKEBEEP")
 If Len(Var$) Then
    If UCase$(Var$) = "OFF" Then
       MakeBeep = 0
    End If
 End If

 ' 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, _
 File.Records(1 To MaxRecords) As Long, _
 Field.Sizes(1 TO MaxFiles) AS INTEGER, _
 File.Trap(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

 ' redirected input
 Call Read.Stdin(Var)
 Color 7, 0

 ' 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
 Redim EndOfHistoryFlag(0 To 3) As Integer
 For VarQ1=1 To 3
    EndOfHistoryFlag(VarQ1)=0
    History.Count2(VarQ1)=0
    For VarQ2=1 To MaxHistory
       History2(VarQ1,VarQ2)=Nul
    Next
 Next

 ' logon banner
 If StdinEnabled = 0 Then
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
    Strng3 = "DNDSIC v"+Version$+" r"+Release$+" t"+Tweak$+": Symbolic Instruction Code programming shell."
    CALL IO.O
 Endif
 Call Modem.ANSI(4,33,14)
 Call ScreenANSI(4,33,14)
 X=CSRLIN
 Y=POS(0)
 If Debug Then
    Call Sic.Status.Line2
 Else
    Call Sic.Status.Line
 Endif
 Locate X,Y,1

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

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

Error.Resume:
 If StdinEnabled Then
    Local.Mode=True
    If Logon.Error Then
       Call Clear.Status.Line
       System
    Endif
    CALL Prepare.Program(0)
    CALL Count.Lines(Last.Line)
    If Last.Line=0 Then
       Print "No program found."
    Else
       Run.Type = True
       CALL Run.Program
    Endif
    Call Remove.TempFile
    Call Clear.Status.Line
    System
 Endif
 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

' gets standard input .sic file
SUB Read.Stdin(Var)
 On Local Error Goto StdinErr
 DIM LINUX AS INTEGER
 DIM Pipe.Buffer AS STRING * 1
 Color 7, 0
 Var=False
 ' store linux variable
 IF ENVIRON$("LINUX") <> "" THEN
    LINUX = True
 END IF
 ' check stdin length
 InregsX.AX = &H4202 ' eof
 InregsX.BX = 0 ' stdin
 InregsX.CX = 0
 InregsX.DX = 0
 Call InterruptX(&H21, InregsX, OutregsX)
 IF OutregsX.AX = 0 THEN ' no input found
    EXIT SUB
 END IF
 Var=True
 If Account.Override = Nul Then
    Color 14, 0
    Print "Unknown account specified."
    Color 7, 0
    End
 Endif

 ' reset standard input variable
 StdinEnabled = -1

 ' reset stdin start
 InregsX.AX = &H4200
 InregsX.BX = 0 ' stdin
 InregsX.CX = 0
 InregsX.DX = 0
 Call InterruptX(&H21, InregsX, OutregsX)

 ' loop while reading input
 DO

    ' read character from standard input
    InregsX.AX = &H3F00
    InregsX.BX = 0 ' stdin
    InregsX.CX = 1 ' char
    InregsX.DS = VARSEG(Pipe.Buffer)
    InregsX.DX = VARPTR(Pipe.Buffer)
    Call InterruptX(&H21, InregsX, OutregsX)
    If (OutregsX.Flags AND &H1) = &H1 Then
       Exit Do
    Endif
    If (OutregsX.Flags AND &H1) = &H0 Then
       If OutregsX.AX = 0 Then
          Exit Do
       Endif
       Char$ = Pipe.Buffer

       ' determine character type
       IF (ASC(Char$)=10 AND LINUX) OR (ASC(Char$)=13 AND LINUX=0) THEN
          Out2 = STRIM$(Out2)
          FOR Blanks = 1 TO LEN(White.Space)
             Imbedded = INSTR(Out2, MID$(White.Space, Blanks, 1))
             IF Imbedded THEN
                Line.Number = CINT(VAL(LEFT$(Out2, Imbedded - 1)))
                IF Line.Number > False AND Line.Number <= Max.Lines THEN
                   Out2 = MID$(Out2, Imbedded)
                   Out2 = STRIM$(Out2)
                   Program(Line.Number) = Out2
                   EXIT FOR
                END IF
             END IF
          NEXT
          Out2 = Nul
       ELSE
          IF (ASC(Char$)=10 AND LINUX=0) THEN
             Eat$=""
          ELSE
             Out2 = Out2 + Char$
          END IF
       END IF
    END IF
 LOOP
' Print "Standard input enabled."
 Exit Sub
StdinErr:
 Cls
 Print Err
 End
END SUB

' exits dndsic
Sub Terminate.Program
 On Local Error Goto ErrorRoutine1
 Close
 Call Remove.TempFile
 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

 ERASE Alarm.Flags, Alarm.Start, Alarm.Time, Alarm.Type, _
 Alarm.Beep, Alarm.Color, Alarm.Music, Alarm.Mess

 ' check program chained from
 IF Chained2 THEN
    CHAIN DND.Path+"EDIT.EXE"
 END IF
 If Chained Then
    Debug.Mode=0
    CHAIN DND.Path+"DNDBBS.EXE"
 END IF

 ' end program
 If StdinEnabled = 0 Then
    Color 15,0
    Call ScreenANSI(1,37,7)
    Cls
    Call ScreenANSI(3,0,0)
    Call Save.Screen(-1)
    If TempX=0 Then TempX=1
    Locate TempX,1,1
 Endif

 ' release file handles
 CALL Decrease.PSP

 Allow.Break=False
 Break=False
 If StdinEnabled Then
    IF Local.Mode THEN
       If Logon.Error=-2 Then
          Color 15,0
          Print
          Print "Error entering username/password."
       Endif
    Endif
    Color 7,0
    System
 Endif
 IF Local.Mode THEN
    Color 15,0
    If Logon.Error=-2 Then
       Print
       Print "Error entering username/password."
    Else
       Var$ = Account.Override
       If Var$<>"" Then
          Print
          If Logon.Error=-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
ErrorResume1:
 Locate ,,,7,7
 END
 Exit Sub
ErrorRoutine1:
 Resume ErrorResume1
END SUB

Sub Remove.TempFile
 On Local Error Goto ErrorRoutine1zz
 Close #TempFile1, #TempFile2
 If Prepare.Filename<>"" Then
    If Dir$(Prepare.Filename)<>"" Then
       KILL Prepare.Filename
    Endif
 Endif
ErrorResume1zz:
 Exit Sub
ErrorRoutine1zz:
 Resume ErrorResume1zz
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
 Gosub Restore.Color
 If StdinEnabled Then
    Var$ = Environ$("STDINCHAR")
    If Var$<>"" Then
       VarX$=Chr$(Val(Var$))
       Strng3 = VarX$
       Out9 = VarX$
       Buffer = Nul
       Last.Token = False
    Else
       X$ = Inkey$
       Strng3 = NUL
       Out9 = Nul
       Buffer = Nul
       Last.Token = False
    Endif
    Exit Sub
 Endif

' If Temp0 Then
'    Call Command.History(-1) ' reset
' Endif
 Carriage.Input=True
 CALL IO.O
 Out9=Nul
 Do
    Char$=Nul
    Var!=Timeit!
    Do While Char$=Nul
       If Time.Elapsed(Var!,1!) Then
          Var!=Timeit!
          Call Get.Cursor.Row(X)
          Call Get.Cursor(Y)
          If Debug Then
             Call Sic.Status.Line2
          Else
             Call Sic.Status.Line
          Endif
          Locate X,Y,1
          Gosub Restore.Color
       Endif
       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
       Out9=Nul
       Exit Do
    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
 Foreground=15
 Background=0
 Gosub Restore.Color
 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
 Exit Sub
Restore.Color:
 Call Get.ANSI.Color(VarX,VarY,VarQ)
 VarZ1=VarX
 VarZ2=VarY
 Call Get.ANSI.Color2(VarX,VarY)
 ' background
 Call Modem.ANSI(5,VarX,VarY)
 Call ScreenANSI(5,VarX,VarY)
 ' foreground
 IF VarQ THEN ' hi-intensity
    Call Modem.ANSI(4,VarZ1,VarZ2)
    Call ScreenANSI(4,VarZ1,VarZ2)
 ELSE
    Call Modem.ANSI(1,VarZ1,VarZ2)
    Call ScreenANSI(1,VarZ1,VarZ2)
 END IF
 Return
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
    If StdinEnabled=0 Then
       While InkeyX$<>""
          X$=InkeyX$
       Wend
    Endif
 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 CheckAlarms(1)
 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

' special inkey$ function
Function InkeyX$
  X$ = KeyboardChar$
  If X$ = Chr$(3) Then
     X$ = Nul
  Endif
  If Len(X$) Then
     InkeyX$ = X$
     Exit Function
  Endif
End Function

' checks keyboard buffer
FUNCTION KeyIS
 InregsX.AX = &H0100
 CALL InterruptX(&H16, InregsX, OutregsX)
 IF (OutregsX.Flags AND &H40) = &H40 THEN
    KeyIS = False
 ELSE
    KeyIS = True
 END IF
END FUNCTION

' reads character directly from keyboard including extended keys.
FUNCTION KeyboardChar$
 IF KeyIS = 0 THEN
    EXIT FUNCTION
 END IF
 InregsX.AX = &H0000
 CALL InterruptX(&H16, InregsX, OutregsX)
 Key1 = (OutregsX.AX AND &HFF)
 IF Key1 = False THEN ' extended key
    Key2 = (OutregsX.AX AND &HFF00) / 256
    KeyboardChar$ = CHR$(0) + CHR$(Key2)
 ELSE
    If Key1=3 Then
       Call Set.Break
    Else
       KeyboardChar$ = CHR$(Key1)
    Endif
 END IF
END FUNCTION

Sub Keyboard
 On Local Error Goto ErrorRoutine12
 If StdinEnabled Then
    Buffer = Chr$(27)
    Exit Sub
 Endif
 VarX$ = InkeyX$
 Select Case Len(VarX$)
 Case 1
    Var=Asc(VarX$)
    Select Case Var
    Case 27 ' escape
       If Chat Then
          Break=True
          Chat=False
       Else
          Buffer=Buffer+VarX$
       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$
             Prompt = Strng3
             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 Temp0=0 Then
          Buffer=Buffer+VarX$
       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
    Do While Lost.Carrier=False
       Call Modem.ANSI(4,33,14)
       Call ScreenANSI(4,33,14)
       Out9 = Nul
       X=CSRLIN
       Y=POS(0)
       If Debug Then
          Call Sic.Status.Line2
       Else
          Call Sic.Status.Line
       Endif
       Locate X,Y,1
       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
       Prompt = Strng3
       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)
          Strng3 = NUL
          Call IO.O
          Select Case VarX
             Case -3
                Strng3 = RecordBreakMessage + " (file"+Str$(File.Num)+") (record"+Str$(Record.Number)+")"
             Case -2
                Strng3 = FileBreakMessage + " (file"+Str$(File.Num)+")"
             Case Else
                Strng3 = BreakMessage
          End Select
          If Local.Mode Then
             If MakeBeep Then
                SOUND Freq, Duration
                SLEEP 1
             Endif
          Endif
          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 "ALARMS"
             Var2$ = MID$(Out9, 7)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "ALARMS"
          Case "ALA"
             Var2$ = MID$(Out9, 4)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "ALARMS"
          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 "EDIT"
             Var2$ = MID$(Out9, 5)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "EDIT"
          CASE "FILES"
             Var2$ = MID$(Out9, 6)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "FILES"
          CASE "DIR"
             Var2$ = MID$(Out9, 4)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "DIRS"
          CASE "DIRS"
             Var2$ = MID$(Out9, 5)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "DIRS"
          CASE "LIB"
             Var2$ = MID$(Out9, 4)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "LIBRARY"
          CASE "LIBRARY"
             Var2$ = MID$(Out9, 8)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "LIBRARY"
          CASE "GRO"
             Var2$ = MID$(Out9, 4)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "GROUP"
          CASE "GROUP"
             Var2$ = MID$(Out9, 6)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "GROUP"
          CASE "CAT"
             Var2$ = MID$(Out9, 4)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "CATALOG"
          CASE "CATALOG"
             Var2$ = MID$(Out9, 8)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "CATALOG"
          CASE "INT"
             Var2$ = MID$(Out9, 4)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "INTERPRETER"
          CASE "INTER"
             Var2$ = MID$(Out9, 6)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "INTERPRETER"
          CASE "INTERPRETER"
             Var2$ = MID$(Out9, 12)
             Var2$ = RTRIM$(LTRIM$(Var2$))
             Out9 = "INTERPRETER"
          Case Else
             Out9 = Nul
          END SELECT
       END IF
       SELECT CASE UCASE$(Out9)
       CASE "ALARMS"
          If Var2$ <> "" Then
             Call AlarmMenu(Var2$)
          Else
             Call AlarmMenu("HELP")
          End If
       CASE "DRIVES"
          CALL ListDrives
       CASE "EDIT"
          If Local.Mode Then
             Var$=Editor$
             Print "Calling: ";Editor$
             If Dir$(Var$)="" Then
                Print Var$;" not found."
             Endif
             If Dir$(Var$)<>"" Then
                ' store current drive/directory
                Directory$ = CURDIR$
                ' clear display
                Call Clear.Status.Line
                Print "If error here use shroom.."
                Print "For example: shroom -q dndsic ********"
                If Var2$<>"" Then
                   SHELL Editor$ + " " + Var2$
                Else
                   SHELL Editor$
                Endif
                ' restore current drive/directory
                CHDRIVE Directory$
                CHDIR Directory$
                ' restore display
                If Debug Then
                   Call Sic.Status.Line2
                Else
                   Call Sic.Status.Line
                Endif
             END IF
          ELSE
             Strng3 = "Only works in local mode."
             Call IO.O
          END IF
       CASE "INTERPRETER", "INTER", "INT"
          If Local.Mode Then
             Var$=Interpreter$
             Print "Calling: ";Interpreter$
             If Dir$(Var$)="" Then
                Print Var$;" not found."
             Endif
             If Dir$(Var$)<>"" Then
                ' store current drive/directory
                Directory$ = CURDIR$
                ' clear display
                Call Clear.Status.Line
                Print "If error here use shroom.."
                Print "For example: shroom -q dndsic ********"
                If Var2$<>"" Then
                   If Instr(Var2$," ") Then
                      Print "Illegal command line."
                   Else
                      SHELL Interpreter$+" /RUN "+Var2$
                   Endif
                Else
                   SHELL Interpreter$
                Endif
                ' restore current drive/directory
                CHDRIVE Directory$
                CHDIR Directory$
                ' restore display
                If Debug Then
                   Call Sic.Status.Line2
                Else
                   Call Sic.Status.Line
                Endif
             END IF
          ELSE
             Strng3 = "Only works in local mode."
             Call IO.O
          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,Var#)
             Program.Name = Running.Program
             Close
             If Dir$(Prepare.Filename)<>"" Then
                KILL Prepare.Filename
             Endif
          Endif
       CASE "CATALOG", "CAT"
          CALL List.Programs(Var2$)
       CASE "FILES"
          CALL List.Programs(Var2$)
       CASE "DIRS", "DIR"
          CALL List.Dirs(Var2$)
       CASE "EXECUTE", "EXE"
          Run.Type=False
          CALL Run.Library
       CASE "GROUP"
          Run.Type=False
          CALL Group.Files(Var2$)
       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(Var2$)
       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
          Call Remove.TempFile
       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
          Filename = Prepare.Filename
          CALL Read.Program(Var,Var#)
          Program.Name = Running.Program
       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(0)
       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
                   Filename = Prepare.Filename
                   CALL Read.Program(Var,Var#)
                   Program.Name = Running.Program
                END IF
             Endif
          Endif
       CASE "DEBUG", "DEB", "D"
          CALL Debug.Command(0, -1)
       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 "CLS"
          Cls
          Locate Max.Row-1,1,1
          Print
       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
 Logon.Error=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
 Logon.Error=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 RecordNumber! = 1! TO LOF(SicFile)/UserProfileRecLen
          Call Read.User.Record(RecordNumber!)
          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 = RecordNumber!
                Close #SicFile

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

                ' check carry flag
                If StdinEnabled = 0 Then
                   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
                Endif

                ' login user time
                Logon.Time = Timeit!
                If StdinEnabled=0 Then
                   Call System.Accounting
                Endif

                ' 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
       Logon.Error=True
       Strng3 = "Error entering account: "+Account.Override
       Call IO.O
       Exit Do
    Endif
    Tries = Tries + 1
    IF Tries >= LogonTries THEN
       Logon.Error=-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) : VarX$ = Out2
 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 = CINT(VAL(LEFT$(Out2, Imbedded - 1)))
       IF Line.Number > 0 AND Line.Number <= Max.Lines THEN
          Out3 = MID$(Out2, Imbedded)
          IF STRIM$(Out3) = Nul THEN
             EXIT FOR
          END IF
          Assign = True
          Strng3 = "Line" + Str$(Line.Number) + " added."
          Call IO.O
          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 = CINT(VAL(Out2))
 IF Line.Number > 0 AND Line.Number <= Max.Lines THEN
    Assign = True
    Strng3 = "Line" + Str$(Line.Number) + " removed."
    Call IO.O
    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(VarX$)
 On Local Error Goto ErrorRoutine36
 If VarX$="" Then
    Program.File$="*"
    Goto Next.Prog3
 Endif
 Program.File$ = VarX$
 If Left$(Program.File$, 1) = Chr$(34) Then
    If Right$(Program.File$, 1) = Chr$(34) Then
       Program.File$ = Left$(Program.File$, Len(Program.File$) - 1)
       Program.File$ = Mid$(Program.File$, 2)
    Endif
 Endif      
 If Lcase$(Right$(Program.File$, 4)) = ".sic" Then
    Program.File$ = Left$(Program.File$, Len(Program.File$) - 4)
 Endif
 IF UCASE$(Program.File$) = "ACCOUNTS" THEN
    Strng3 = "Program unavailable."
    Call IO.O
    Exit Sub
 END IF
Next.Prog3:
 Strng3 = "Group Account Files:"
 CALL IO.O
 Program.File$=Ucase$(Program.File$)
 Group.Directory$ = Current.Directory + Group.Account + ".SIC\"+Program.File$+".SIC"
 CALL List.FilesX (Group.Directory$, -1)
ErrorExit36:
 Exit Sub
ErrorRoutine36:
 Resume ErrorExit36
END SUB

' lists account library files
SUB Library.Files(VarX$)
 On Local Error Goto ErrorRoutine37
 If VarX$="" Then
    Program.File$="*"
    Goto Next.Prog4
 Endif
 Program.File$ = VarX$
 If Left$(Program.File$, 1) = Chr$(34) Then
    If Right$(Program.File$, 1) = Chr$(34) Then
       Program.File$ = Left$(Program.File$, Len(Program.File$) - 1)
       Program.File$ = Mid$(Program.File$, 2)
    Endif
 Endif      
 If Lcase$(Right$(Program.File$, 4)) = ".sic" Then
    Program.File$ = Left$(Program.File$, Len(Program.File$) - 4)
 Endif
 IF UCASE$(Program.File$) = "ACCOUNTS" THEN
    Strng3 = "Program unavailable."
    Call IO.O
    Exit Sub
 END IF
Next.Prog4:
 Strng3 = "Library Account Files."
 CALL IO.O
 Program.File$=Ucase$(Program.File$)
 Library.Directory$ = Current.Directory + Library.Account + ".SIC\"+Program.File$+".SIC"
 CALL List.FilesX (Library.Directory$, -1)
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
    Strng3 = "Program unavailable."
    Call IO.O
    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"
       Program.File$ = Current.Directory + "siclist.doc"
       CALL List.Help.File(Program.File$)
       IF Account.Login = Library.Login THEN
          Strng3 = " LOCK      --  lock library program."
          CALL IO.O
          Strng3 = " UNLOCK    --  unlock library program."
          CALL IO.O
          CALL Key.Prompt
       END IF
    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
       Carriage.Return=0
       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$)+.5)
 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 RecordNumber! = 1! TO LOF(SicFile)/UserProfileRecLen
    Call Read.User.Record(RecordNumber!)
    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(VarX$)
 On Local Error Goto ErrorRoutine43
 If VarX$="" Then
    Program.File$="*"
    Goto Next.Prog
 Endif
 Program.File$ = VarX$
 If Left$(Program.File$, 1) = Chr$(34) Then
    If Right$(Program.File$, 1) = Chr$(34) Then
       Program.File$ = Left$(Program.File$, Len(Program.File$) - 1)
       Program.File$ = Mid$(Program.File$, 2)
    Endif
 Endif      
 If Lcase$(Right$(Program.File$, 4)) = ".sic" Then
    Program.File$ = Left$(Program.File$, Len(Program.File$) - 4)
 Endif
 IF UCASE$(Program.File$) = "ACCOUNTS" THEN
    Strng3 = "Program unavailable."
    Call IO.O
    Exit Sub
 END IF
Next.Prog:
 Strng3 = "Account: " + Quote$ + Account.Login + Quote$
 Strng3 = Strng3 + ": " + Form$(Cdbl(Max.Records)) + " KB. records max."
 CALL IO.O
 Catalog.Directory$ = Current.Directory + Account + ".SIC\"+Program.File$+".SIC"
 Catalog.Directory$ = Ucase$(Catalog.Directory$)
 Strng3="Searching: " + Catalog.Directory$
 Call IO.O
 CALL List.FilesX (Catalog.Directory$, -1)
ErrorExit43:
 Exit Sub
ErrorRoutine43:
 Resume ErrorExit43
END SUB

' lists .sic directories in current subdirectory.
SUB List.Dirs(VarX$)
 On Local Error Goto ErrorRoutine43z
 If VarX$="" Then
    Program.File$="*"
    Goto Next.Prog2
 Endif
 Program.File$ = VarX$
 If Left$(Program.File$, 1) = Chr$(34) Then
    If Right$(Program.File$, 1) = Chr$(34) Then
       Program.File$ = Left$(Program.File$, Len(Program.File$) - 1)
       Program.File$ = Mid$(Program.File$, 2)
    Endif
 Endif      
 If Lcase$(Right$(Program.File$, 4)) = ".sic" Then
    Program.File$ = Left$(Program.File$, Len(Program.File$) - 4)
 Endif
 IF UCASE$(Program.File$) = "ACCOUNTS" THEN
    Strng3 = "Program unavailable."
    Call IO.O
    Exit Sub
 END IF
Next.Prog2:
 Strng3 = "Account: " + Quote$ + Account.Login + Quote$
 Strng3 = Strng3 + ": " + Form$(Cdbl(Max.Records)) + " KB. records max."
 CALL IO.O
 Catalog.Directory$ = Current.Directory + Account + ".SIC\"+Program.File$+".*"
 Catalog.Directory$ = Ucase$(Catalog.Directory$)
 Strng3="Searching: " + Catalog.Directory$
 Call IO.O
 CALL List.FilesX (Catalog.Directory$, 0)
ErrorExit43z:
 Exit Sub
ErrorRoutine43z:
 Resume ErrorExit43z
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 file"
          Call IO.O
          Strng3="[5]Other directory"
          Call IO.O
       Endif
       If Local.Mode Or Account.Login = Library.Login Then
          Strng2 = "Enter file search option(1,2,3,4,5,Q to quit)"
          CALL More.Prompt(Strng2,"12345q",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 Or Account.Login = Library.Login 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.FilesX (Catalog.Directory$, -1)
             IF Break THEN EXIT DO
             CALL Key.Prompt
             EXIT DO
          Endif
       Case "5"
          If Local.Mode Or Account.Login = Library.Login Then
             Strng3 = "Enter dir 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.FilesX (Catalog.Directory$, 0)
             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 = BreakMessage
    If Local.Mode Then
       If MakeBeep Then
          SOUND Freq, Duration
          SLEEP 1
       Endif
    Endif
    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.FilesX (Catalog.Directory$, -1)
    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
 If Left$(Program.File$, 1) = Chr$(34) Then
    If Right$(Program.File$, 1) = Chr$(34) Then
       Program.File$ = Left$(Program.File$, Len(Program.File$) - 1)
       Program.File$ = Mid$(Program.File$, 2)
    Endif
 Endif      
 If Lcase$(Right$(Program.File$, 4)) = ".sic" Then
    Program.File$ = Left$(Program.File$, Len(Program.File$) - 4)
 Endif
 Call Strip.Program(Program.File$,Var2)
 If Var2 Then
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
    If Var2 = -1 Then
       Strng3="Cannot load program. Illegal characters in filename."
    Else
       Strng3="Cannot load program. Filename is not 8.3 format."
    Endif
    Call IO.O
    Exit Sub
 Endif
 IF UCASE$(Program.File$) = "ACCOUNTS" THEN
    Strng3 = "Program unavailable."
    Call IO.O
    Exit Sub
 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,Var#)
 If Var Then
    Program.Name = LCASE$(Program.File$)
    Strng3="Program loaded: "+Form$(Var#)+" bytes."
    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
 Strip$ = "\:/*?>|<."+Chr$(34)
 Var2=0
 For VarX = 1 To Len(Strip$)
    If Instr(Var$,Mid$(Strip$,VarX,1)) Then
       Var2=-1
       Exit Sub
    Endif
 Next
 Var$ = RTRIM$(Var$)
 If Len(Var$) <= 8 Then
    Exit Sub
 Endif
 Var2=-2
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$)
 Call ProgramBytes(Program.Bytes#)

 ' check account size limit exceeded
 IF Total.Bytes# + Program.Bytes# > CDBL(Max.Records) * 1024# THEN
    Strng3 = "Account maximum record size overflow."
    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(QuitPrompt,"yn",Outpt$,"y")
 If Outpt$="n" Then Exit Sub
 If Program.Name=None$ Then
    Eat$=Nul
 Else
    CALL Save.Current.Program
 Endif
 Call Remove.TempFile
 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,Var#)
 Program.Name = Running.Program
 Close TempFile1, TempFile2
 Carriage.Return=0
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. Use Run instead."
    CALL IO.O
    EXIT SUB
 END IF
 CALL Save.Current.Program
Start.Group:
 Strng3 = "Group program name? "
 CALL IO.I
 Group.Program$ = Out9
 If Left$(Group.Program$, 1) = Chr$(34) Then
    If Right$(Group.Program$, 1) = Chr$(34) Then
       Group.Program$ = Left$(Group.Program$, Len(Group.Program$) - 1)
       Group.Program$ = Mid$(Group.Program$, 2)
    Endif
 Endif      
 If Lcase$(Right$(Group.Program$, 4)) = ".sic" Then
    Group.Program$ = Left$(Group.Program$, Len(Group.Program$) - 4)
 Endif
 Call Strip.Program(Group.Program$,Var2)
 If Var2 Then
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
    If Var2 = -1 Then
       Strng3="Cannot load program. Illegal characters in filename."
    Else
       Strng3="Cannot load program. Filename is not 8.3 format."
    Endif
    Call IO.O
    Exit Sub
 Endif
 Group.Name$ = Group.Program$
 IF UCASE$(Group.Program$) = "ACCOUNTS" THEN
    Strng3 = "Program unavailable."
    Call IO.O
    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,Var#)
 If Var Then
    Strng3="Group Program loaded: "+Form$(Var#)+" bytes."
    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. Use Run instead."
    CALL IO.O
    EXIT SUB
 END IF
 CALL Save.Current.Program
Start.Library:
 Strng3 = "Library program name? "
 CALL IO.I
 Library.Program$ = Out9
 If Left$(Library.Program$, 1) = Chr$(34) Then
    If Right$(Library.Program$, 1) = Chr$(34) Then
       Library.Program$ = Left$(Library.Program$, Len(Library.Program$) - 1)
       Library.Program$ = Mid$(Library.Program$, 2)
    Endif
 Endif      
 If Lcase$(Right$(Library.Program$, 4)) = ".sic" Then
    Library.Program$ = Left$(Library.Program$, Len(Library.Program$) - 4)
 Endif
 Call Strip.Program(Library.Program$,Var2)
 If Var2 Then
    Call Modem.ANSI(4,33,14)
    Call ScreenANSI(4,33,14)
    If Var2 = -1 Then
       Strng3="Cannot load program. Illegal characters in filename."
    Else
       Strng3="Cannot load program. Filename is not 8.3 format."
    Endif
    Call IO.O
    Exit Sub
 Endif
 Library.Name$ = Library.Program$
 IF UCASE$(Library.Program$) = "ACCOUNTS" THEN
    Strng3 = "Program unavailable."
    Call IO.O
    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,Var#)
 If Var Then
    Strng3="Library Program loaded: "+Form$(Var#)+" bytes."
    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 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
' IF Account.Login <> Library.Login THEN
    IF Local.Mode = False THEN
       Strng3 = "Account is not available."
       CALL IO.O
       EXIT SUB
    END IF
' 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 + ".sic" + 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 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
' IF Account.Login <> Library.Login THEN
    IF Local.Mode = False THEN
       Strng3 = "Account is not available."
       CALL IO.O
       EXIT SUB
    END IF
' 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 + ".sic" + 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:
 If StdinEnabled=0 Then
    Strng3 = CtrlBreak1$
    If Local.Mode Then
       Strng3 = CtrlBreak2$
    Endif
    CALL IO.O
 Endif
 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
 Recurse2=0
 VarX=False
 DO
    ' check break/carrier
    CALL Check.Carrier
    If Lost.Carrier Then
       Exit Sub
    Endif
    CALL Check.Program
    IF Break THEN
       VarX=Break
       Break=False
       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=Break
          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

 ' restore screen mode
 SCREEN 0
 Locate Max.Row-1,1,1

 ' 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)
    Strng3 = NUL
    Call IO.O
    Call Modem.ANSI(4,31,12)
    Call ScreenANSI(4,31,12)
    Select Case VarX
       Case -3
          Strng3 = RecordBreakMessage + " (file"+Str$(File.Num)+") (record"+Str$(Record.Number)+")"
       Case -2
          Strng3 = FileBreakMessage + " (file"+Str$(File.Num)+")"
       Case Else
          Strng3 = BreakMessage
    End Select
    If Local.Mode Then
       If MakeBeep Then
          SOUND Freq, Duration
          SLEEP 1
       Endif
    Endif
    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
 If StdinEnabled=0 Then
    CALL IO.O
 Endif
 If Debug Then
    Call Sic.Status.Line2
 Else
    Call Sic.Status.Line
 Endif
 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

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

 ' 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 + ".sic)"
       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
          If Left$(Name.Store$, 1) = Chr$(34) Then
             If Right$(Name.Store$, 1) = Chr$(34) Then
                Name.Store$ = Left$(Name.Store$, Len(Name.Store$) - 1)
                Name.Store$ = Mid$(Name.Store$, 2)
             Endif
          Endif      
          If Lcase$(Right$(Name.Store$, 4)) = ".sic" Then
             Name.Store$ = Left$(Name.Store$, Len(Name.Store$) - 4)
          Endif
          Call Strip.Program(Name.Store$, Var2)
          If Var2=0 Then
             EXIT DO
          Endif
          Call Modem.ANSI(4,37,15)
          Call ScreenANSI(4,37,15)
          If Var2 = -1 Then
             Strng3="Cannot store program. Illegal characters in filename."
          Else
             Strng3="Cannot store program. Filename is not 8.3 format."
          Endif
          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
       Strng3 = "Program unavailable."
       Call IO.O
       GOTO Start.Save
    END IF
    Program.Name = Name.Store$
    Strng2 = "Store program as " + Quote$ + LCASE$(RTRIM$(Program.Name)) + ".sic" + 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))+".sic" + 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(VarX)
 On Error Goto Error.Routine
 Static RecurseX As Integer
 If VarX=0 Then RecurseX=0
 If VarX=-1 Then
    RecurseX=RecurseX+1
    Strng3="Whatis recurse"+Str$(RecurseX)+" level."
    Call IO.O
 Endif
 Strng3 = "Enter 'quit' to return to DNDSIC."
 CALL IO.O
 Strng3 = "  Enter 'debug' to start DEBUG command."
 CALL IO.O
 Temp5 = 0
 For VarZZ = 1 To Max.Alarms
    If Alarm.Flags(VarZZ) Then
       Temp5 = Temp5 + 1
    End If
 Next
 If Temp5 Then
    Strng3 = "  " + Str$(Temp5) + " alarms set." : Call IO.O
 End If
Start.Whatis:
 DO While Lost.Carrier=False
    Allow.Break=True
    Break = False
    DO While Lost.Carrier=False
       Call Modem.ANSI(4,33,14)
       Call ScreenANSI(4,33,14)
       Out9 = Nul
       X=CSRLIN
       Y=POS(0)
       If Debug Then
          Call Sic.Status.Line2
       Else
          Call Sic.Status.Line
       Endif
       Locate X,Y,1
       Call Modem.ANSI(4,37,15)
       Call ScreenANSI(4,37,15)
       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
       Prompt = Strng3
       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
          Select Case VarX
             Case -3
                Strng3 = RecordBreakMessage + " (file"+Str$(File.Num)+") (record"+Str$(Record.Number)+")"
             Case -2
                Strng3 = FileBreakMessage + " (file"+Str$(File.Num)+")"
             Case Else
                Strng3 = BreakMessage
          End Select
          If Local.Mode Then
             If MakeBeep Then
                SOUND Freq, Duration
                SLEEP 1
             Endif
          Endif
          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
    VarX = Instr(Out9, " ")
    If VarX Then
       Var1$ = Left$(Out9, VarX - 1)
       Var2$ = Mid$(Out9, VarX + 1)
       Var1$ = Ucase$(Rtrim$(Ltrim$(Var1$)))
       Var2$ = Ucase$(Rtrim$(Ltrim$(Var2$)))
       If Var1$ = "ALARMS" Then
          Call AlarmMenu(Var2$)
          Goto Start.Whatis
       Endif
    Endif
    If Ucase$(Out9) = "ALARMS" Then
       Call AlarmMenu("HELP")
       Goto Start.Whatis
    End If
    IF UCASE$(Out9) = "DEBUG" THEN
       CALL Debug.Command(0, -1)
       Goto Start.Whatis
    END IF
    IF UCASE$(Out9) = "WHATIS" THEN
       CALL Whatis.Command(-1)
       Goto Start.Whatis
    END IF
    Out2 = Out9
    Error.Type = False
    Program.Line = False
    Out2 = TTRIM$(Out2, True)
    Recurse = False
    Recurse2 = 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 = NUL
       Call IO.O
       Select Case VarX
          Case -3
             Strng3 = RecordBreakMessage + " (file"+Str$(File.Num)+") (record"+Str$(Record.Number)+")"
          Case -2
             Strng3 = FileBreakMessage + " (file"+Str$(File.Num)+")"
          Case Else
             Strng3 = BreakMessage
       End Select
       If Local.Mode Then
          If MakeBeep Then
             SOUND Freq, Duration
             SLEEP 1
          Endif
       Endif
       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
 If Debug Then
    Call Sic.Status.Line2
 Else
    Call Sic.Status.Line
 Endif
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,Var#)
 On Local Error Goto ErrorRoutine59
 Var = -1
 Var2 = 0
 Var3 = 0
 Var# = 0#
 Tlines = 0
 CLOSE #TempFile2
 CALL New.Program
 OPEN Filename FOR INPUT AS #TempFile2
 DO WHILE NOT EOF(TempFile2)
    LINE INPUT #TempFile2, Out2
    Out2 = STRIM$(Out2)
    If Len(Out2) Then
       IF LEFT$(Out2, 1) = ";" OR LEFT$(Out2, 1) = "'" THEN ' commented line
          Eat$ = Nul
       ELSE
          For Blanks = 1 To Len(Out2)
             Imbedded$ = Mid$(Out2, Blanks, 1)
             If Imbedded$ >= "0" And Imbedded$ <= "9" Then
                Eat$ = Nul
             Else
                Line.Number = CInt(Val(Left$(Out2, Blanks)))
                If Line.Number > False And Line.Number <= Max.Lines Then
                   Program$(Line.Number) = Mid$(Out2, Blanks)
                   Var# = Var# + Int(Val(Out2)+.5)
                   Var3 = -1
                   Tlines=Tlines+1
                Else
                   Var2 = Var2 + 1
                Endif
                Exit For
             End If
          Next
       END IF
    Endif
 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
 'If Var3 Then
 '   Strng3="Program loaded:"+Str$(Tlines)+" lines "+Form$(Var#)+" bytes."
 '   Call IO.O
 'Endif
 If Var3=0 Then
    Strng3="No program loaded."
    Call IO.O
    Var=0
 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 RecordNumber! = 1! TO LOF(SicFile)/UserProfileRecLen
    Call Read.User.Record(RecordNumber!)
    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 ' is case-sensitive
   S2$ = Var1$
   S3$ = Var2$
Else ' is not case-sensitive
   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.Array1(VarX3) As Integer
    Redim Temp.Array2(VarX3) As Integer
    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.Array1(VarX2)=TempZ1
          Temp.Array2(VarX2)=TempZ2
       Next
    Next
 Else
    Locate ,,,7,7
    Cls
    For VarX=Max.Row To 1 Step -1
       For VarY=1 To 80
          VarX2=(VarX-1)*80+VarY
          If Temp.Array1(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.Array2(VarX2)/16)
          VarF=Temp.Array2(VarX2) Mod 16
          TempZ1=Temp.Array1(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
