 Rem File: IO6.bas - Dndbbs.bas, Dndsub*.bas
 Rem $Include: 'dndbbs.inc'

Function ValidHex(Var$)
 On Local Error Goto HexError
 For Var=1 To Len(Var$)
    Var2$=Mid$(Var$,Var,1)
    Select Case Var2$
    Case "0" To "9", "A" To "F"
       Eat$=Nul
    Case Else
       ValidHex=0
       Exit Function
    End Select
 Next
 ValidHex=-1
 Exit Function
HexResume:
 ValidHex=0
 Exit Function
HexError:
 Resume HexResume
End Function

Function ValidDate(Var$)
 On Local Error Goto DateError
 Var$=Rtrim$(Var$)
 If Len(Var$)=10 Then ' mm-dd-yyyy
    For Var=1 To 10
       V$=Mid$(Var$,Var,1)
       Select Case Var
       Case 1, 2, 4, 5, 7, 8, 9, 10
          If V$>="0" And V$<="9" Then
             Eat$ = Nul
          Else
             Goto DateResume
          Endif
       Case Else
          If V$ <> "-" Then
             Goto DateResume
          Endif
       End Select
    Next
 Endif
 Temp#=DateValue(Var$)
 If Temp# Then
    TempC$=Var$
    ValidDate=True
    Exit Function
 Endif
DateResume:
 ValidDate=False
 Exit Function
DateError:
 Resume DateResume
End Function

Function Timeit!
 t1$ = Format$(Now, "hh:mm:ss") ' seconds past midnight
 t1! = CSNG(INT(VAL(MID$(t1$, 1, 2)))) * 3600! + CSNG(INT(VAL(MID$(t1$, 4, 2)))) * 60! + CSNG(INT(VAL(MID$(t1$, 7, 2))))
 Timeit!=t1!
End Function

Function Time.Elapsed(Start.Time!,Wait.Time!)
 Elapsed.Time!=Timeit!-Start.Time!
 If Elapsed.Time!<SFalse Then
    Elapsed.Time!=Elapsed.Time!+86400! ' adjust for midnight
 Endif
 If Elapsed.Time!>=Wait.Time! Then
    Time.Elapsed=True
 Else
    Time.Elapsed=False
 Endif
End Function

Function Config1!(Var)
 On Local Error Goto Error.Trap10
 Call Read.Message.Record(MessWorkFile9,Var)
 Config1!=UserConfigRecord.UserConfig1
Error.Resume10:
 Exit Function
Error.Trap10:
 Resume Error.Resume10
End Function

Function Config2%(Var)
 On Local Error Goto Error.Trap11
 Call Read.Message.Record(MessWorkFile9,Var+MaxConfigArray1)
 Config2%=UserConfigRecord.UserConfig2
Error.Resume11:
 Exit Function
Error.Trap11:
 Resume Error.Resume11
End Function

Function Config3$(Var)
 On Local Error Goto Error.Trap12
 Call Read.Message.Record(MessWorkFile9,Var+MaxConfigArray1+MaxConfigArray2)
 Var$=Rtrim$(UserConfigRecord.UserConfig3)
 If Right$(Var$,1)=Chr$(1) Then
    Var$=Left$(Var$,Len(Var$)-1)
 Endif
 Config3$=Var$
Error.Resume12:
 Exit Function
Error.Trap12:
 Resume Error.Resume12
End Function

Function DateS$
 DateS$=Format$(Now,"mm-dd-yyyy")
End Function

Function FClock$
 FClock$=Format$(Now,"ddd dd mmm yyyy\, hh:mma/p")
End Function

Function FClock2$
 FClock2$=Format$(Now,"dd mmm yyyy\, hh:mm:ss")
End Function

Function Form$(Var#)
 Form$=Format$(Var#,"#,##0;;"+Chr$(34)+"zero"+Chr$(34))
End Function

Sub IO.O
 On Local Error Goto Error.Trap44
 If Environ$("UPPERCASE")<>"" Then
    IO.Upper.Case=-1
 Endif
 If Environ$("TELETYPE")<>"" Then
    IO.Upper.Case=-1
 Endif
 If Lost.Carrier Then
    Exit Sub
 Endif
 If Len(Strng) Then
    If Graphics.On=False Then
       Call Send.ANSI
    Endif
 Endif
 Count=False
 Do
    Call Check.Carrier
    If Lost.Carrier Then
       Exit Sub
    Endif
    Call Check.Break
    Call Keyboard(Var,0)
    If Break Then
       Exit Do
    Endif
    If Var Then
       Exit Sub
    Endif
    Gosub IO.Wrap
    If Count>Len(Strng) Then
       Exit Do
    Endif
    VarX$=Mid$(Strng,Count,1)
    If IO.Upper.Case Then VarX$=Ucase$(VarX$)
    Call Put.Modem(VarX$)
    Call Scrn(VarX$)
 Loop
 If Carriage.Return=False Then
    If Carriage.Input=False Then
       If Len(Strng)=False Then
          Call Scroll.Return
       Else
	  Call Line.Return
       Endif
    Endif
 Endif
 Strng=Nul
 Carriage.Input=False
 Carriage.Return=False
 Exit Sub
IO.Wrap:
 Count=Count+1
 If User.Linelength>False Then
    Call Get.Cursor(VarC)
    Select Case User.Wordwrap
    Case True
       If VarC>=User.Linelength Then
          If Mid$(Strng,Count,1)=" " Then
             Count=Count+1
          Endif
          If Count<=Len(Strng) Then
             Call Line.Return
             If Mid$(Strng,Count,1)=" " Then
                Count=Count+1
             Endif
          Endif
       Endif
    Case False
       VarW=Instr(Count+1,Strng," ")
       If VarW Then
          VarW$=Mid$(Strng,Count)
          VarW=Instr(2,VarW$," ")
          VarW$=Left$(VarW$,VarW-1)
       Else
          If Mid$(Strng,Count,1)=" " Then
             VarW$=Mid$(Strng,Count)
          Else
             VarW$=Nul
          Endif
       Endif
       If VarC+Len(VarW$)>User.Linelength Then
          If Mid$(Strng,Count,1)=" " Then
             Count=Count+1
          Endif
          Call Line.Return
       Endif
    End Select
 Endif
 Return
Error.Resume44:
 Exit Sub
Error.Trap44:
 Resume Error.Resume44
End Sub

Sub Send.ANSI
 On Local Error Goto Error.Trap99x
 Call Modem.ANSI(4,40,0)
 Call ScreenANSI(4,40,0)
 Select Case Graphics.Off
 Case True
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
 Case False
    Color.Code=Color.Code+1
    Avatar.Code=Avatar.Code+1
    Select Case Color.Code
    Case Is<31, Is>35
       Color.Code=31
    End Select
    Select Case Avatar.Code
    Case Is<10, Is>14
       Avatar.Code=10
    End Select
    Call Modem.ANSI(4,Color.Code,Avatar.Code)
    Call ScreenANSI(4,Color.Code,Avatar.Code)
 End Select
Error.Resume99x:
 Exit Sub
Error.Trap99x:
 Resume Error.Resume99x
End Sub

Sub IO.I
 On Local Error Goto Error.Trap45
 If Lost.Carrier Then
    Exit Sub
 Endif
 If Logged.Node Then
    Call Remote.Status.Line(1) ' store
    Call Command.History(-1) ' reset
 Endif
 Var$=Strng
 Prompt$=Strng
 Call IO.Timing
 Strng=Var$
 Out9=Var$
 Carriage.Input=True
 Strng=Strng+Type.Ahead
 VarZ=Len(Strng)
 Call IO.O
 Out2=Type.Ahead
 Type.Ahead=Nul
 Var!=Timeit!
 Call Read.Node.Mess(Prompt$)
 If Logged.Node Then
    Call Remote.Status.Line(0) ' display
 Endif
 Call Modem.ANSI(1,40,0) ' reset background
 Call ScreenANSI(1,40,0)
 Call Modem.ANSI(4,37,15) ' set foreground to white
 Call ScreenANSI(4,37,15)
 Do
    Char$=Nul
    Timeout=Timeit!
    Do Until Len(Char$)
       Call Check.Carrier
       If Lost.Carrier Then
	  Exit Sub
       Endif
       Call Check.Break
       Call Get.Modem(False)
       Call Keyboard(Var,-1)
       If Var Then
	  Strng=Nul
	  Call IO.O
	  Exit Sub
       Endif
       If Break Then
          If Logged.Node Then
             Call Remote.Status.Line(-1)
          Endif
	  Exit Do
       Endif
       If Len(Buffer) Then
	  Char$=Left$(Buffer,1)
	  Buffer=Mid$(Buffer,2)
       Endif
       If Time.Elapsed(Var!,1!) Then
          Var!=Timeit!
          Call IO.Timing
          Call Read.Node.Mess(Prompt$)
          If Config2(56)>False Then
             If Time.Elapsed(Timeout,Csng(Config2(56))) Then
                If Logged.Node Then
                   Call Remote.Status.Line(-1)
                Endif
                Call Hang.Up(1)
             Endif
          Endif
       Endif
       If Local.Mode Then
          Call Release.Time(1)
       Endif
    Loop
    Char=Asc(Char$)
    Select Case Char
    Case 27 ' escape
       Esc=-1
       Esc$=Chr$(27)
    Case 3, 11 ' break
       Type.Error=False
       Call Set.Break
    Case 8 ' backspace
       Type.Error=False
       If Len(Out2) Then
	  Out2=Left$(Out2,Len(Out2)-1)
	  Call Back.Space
       Endif
    Case 13 ' return
       Type.Error=False
       If Out2=Nul Then
	  If Len(No.Input.Out) Then
	     Out2=No.Input.Out
	     OutY$=Lcase$(No.Input.Out)
             If Len(OutY$) Then
                Mid$(OutY$,1,1)=Ucase$(Mid$(OutY$,1,1))
             Endif
	     If User.Echo=False Then
		Call Put.Modem(OutY$)
	     Endif
	     Call Scrn(OutY$)
	  Endif
       Endif
       If Logged.Node Then
          Call Command.History(0)
          Call Remote.Status.Line(-1)
       Endif
       Call Line.Return
       Call Get.Row(VarX)
       If VarX<(Max.Row-1) Then
          Call Scrn(Chr$(10))
       Endif
       Exit Do
    Case 32 To 126 ' ascii keystroke
       ' 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 Logged.Node 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
          If Out2=Nul Then
             If Upper.Case Then
                Char$=Ucase$(Char$)
             Endif
          Endif
          VarX$=Char$
          If No.Echo=False Then
             If Hidden Then
                VarX$=Mask$
             Endif
             If User.Echo=False Then
                Call Put.Modem(VarX$)
             Endif
          Endif
          ' store char
          Out2=Out2+Char$
          ' wordwrap
          If Word.Wrap=False Then
             Call Get.Cursor(VarC)
             Select Case VarC
             Case 1 To User.Linelength-1
                Call Scrn(VarX$)
             Case Else
                If Logged.Node Then
                   Call Remote.Status.Line(-1)
                Endif
                Call Line.Return
                Exit Do
             End Select
          Endif
          If Word.Wrap Then
             If No.Echo=False Then
                Call Get.Cursor(VarC)
                Select Case VarC
                Case 1 To User.Linelength-1
                   Call Scrn(VarX$)
                Case Else
                   VarL=False
                   VarW=False
                   For VarX=Len(Out2) To 1 Step -1
                      If Mid$(Out2,VarX,1)=" " Then
                         For VarB=1 To VarL
                            Call Back.Space
                         Next
                         Type.Ahead=Mid$(Out2,VarX+1)
                         Out2=Left$(Out2,VarX)
                         VarW=True
                         Exit For
                      Endif
                      VarL=VarL+1
                   Next
                   If VarW Then
                      If Logged.Node Then
                         Call Remote.Status.Line(-1)
                      Endif
                      Call Line.Return
                   Endif
                   Exit Do
                End Select
             Endif
          Endif
          If Line.Length>False Then
             If Len(Out2)>=Line.Length Then
                If Logged.Node Then
                   Call Remote.Status.Line(-1)
                Endif
                Call Line.Return
                Exit Do
             Endif
          Endif
       Endif
    Case Else ' garbage
       Type.Error=Type.Error+1
       If Type.Error>=MaxErrors Then
	  Type.Error=False
	  Call Hang.Up(14)
       Endif
    End Select
 Loop
 No.Input=(Out2=Nul)
 No=(Ucase$(Left$(Out2,1))="N")
 Yes=(Ucase$(Left$(Out2,1))="Y")
 Quit=(Ucase$(Left$(Out2,1))="Q")
 Continuous=(Ucase$(Left$(Out2,1))="C")
 Line.Length=False
 No.Echo=False
 No.Input.Out=Nul
 Strng=Nul
Error.Resume45:
 Exit Sub
Error.Trap45:
 Resume Error.Resume45
End Sub

' VarQ: 1=store/0=display/-1=restore
' VarL: Remote line number.
' VarM: Local line number.
Sub Remote.Status.Line(VarQ)
 On Local Error Goto Error.Trap45x
 ' store line 1 between calls.
 Static Remote.Line As String

 ' store line 1 colors.
 Static Line.Colors() As Integer
 Redim Preserve Line.Colors(1 To 79) As Integer

 ' check flags
 If Local.Mode Then
    Exit Sub
 Endif
 If Graphics.Status=False Then
    Exit Sub
 Endif
 Call Check.Carrier
 If Lost.Carrier Then
    Exit Sub
 Endif

 ' get user linelength
 Select Case User.LineLength
 Case 40
    VarQ3=39
 Case 80
    VarQ3=79
 Case Else
    VarQ3=79
 End Select

 ' check line variable
 If StatusLine.Line = 0 Then
    Statusline.Line = 2
 Endif
 VarL = StatusLine.Line ' remote
 VarM = VarL - 1 ' local

 ' store remote ansi
 VarZ2=Remote.ANSI.Color
 Remote.ANSI.Color=True

 ' select statusline mode
 Select Case VarQ
 Case 1 ' store
    ' store line 1 (local), line 2 (remote).
    Remote.Line$=Nul

    ' store local cursor
    Call Cursor(False)

    ' store top line of local screen
    For Var2=1 To VarQ3
       ' read screen character
       TempZ1=Screen(VarM,Var2)

       ' read character color
       TempZ2=Screen(VarM,Var2,1)

       ' store character and color
       Remote.Line$=Remote.Line$+Chr$(TempZ1)
       Line.Colors(Var2)=TempZ2
    Next

    ' restore local cursor
    Call Cursor(True)
 Case 0 ' display
    ' store status line
    Call Make.Status.Line(VarZ)

    ' check online user
    If VarZ Then
       ' store local cursor
       Call Cursor(False)

       ' store status line
       Var2$=Temp$
       If Rtrim$(Statusline.String)<>"" Then
          Strng=Rtrim$(Statusline.String)
          Call Convert.String
          Var2$=Strng
       Endif

       ' store remote cursor
       Var$=Chr$(27)+"[s"
       Call Put.Modem(Var$)

       ' reset to line 2 remote
       Call Modem.ANSI(2,VarL,1)

       ' display statusline color
       Var3$=Strng
       Strng=Rtrim$(UserRecord.Prefix3)
       If Strng=Nul Then
          Call Modem.ANSI(4,37,15)
       Else
          Call Convert.ANSI(True,VarQ,VarR)
       Endif
       Strng=Var3$

       ' reset statusline length
       Var2$=Left$(Var2$,VarQ3)
       Var2$=Var2$+Space$(VarQ3-Len(Var2$))

       ' display statusline
       Call Put.Modem(Var2$)

       ' restore colors
       Call Modem.ANSI(4,37,15)
       Call Modem.ANSI(4,40,0)
       Call ScreenANSI(4,37,15)
       Call ScreenANSI(4,40,0)

       ' restore remote cursor
       Var$=Chr$(27)+"[u"
       Call Put.Modem(Var$)

       ' restore local cursor
       Call Cursor(True)
    Endif
 Case -1 ' restore
    ' store remote cursor
    Var$=Chr$(27)+"[s"
    Call Put.Modem(Var$)

    ' restore colors
    Call Modem.ANSI(4,37,15)
    Call Modem.ANSI(4,40,0)

    ' current colors
    B1=0 ' background
    F1=15 ' foreground

    ' locate to row 2 remote
    Call Modem.ANSI(2,VarL,1)

    ' display stored line
    For Var2=1 To VarQ3
       ' get character color
       TempZ2=Line.Colors(Var2)
       VarB=Int(TempZ2/16) ' background
       VarF=TempZ2 Mod 16 ' foreground

       ' change foreground
       If VarF<>F1 Then
          F1=VarF
          Gosub GetForeGround
          If VarZ1 Then ' intensity
             Call Modem.ANSI(1,VarF1,VarF2) ' low
          Else
             Call Modem.ANSI(4,VarF1,VarF2) ' high
          Endif
       Endif

       ' change background
       If VarB<>B1 Then
          B1=VarB
          Gosub GetBackGround
          Call Modem.ANSI(5,VarB1,VarB2)
       Endif

       ' get character
       Var$=Mid$(Remote.Line$,Var2,1)
       Call Put.Modem(Var$)
    Next

    ' restore remote cursor
    Var$=Chr$(27)+"[u"
    Call Put.Modem(Var$)
 End Select

 ' restore remote ansi
 Remote.ANSI.Color=VarZ2
 Exit Sub

GetBackGround:
 Select Case VarB
 Case 0
    VarB1=40
    VarB2=0
 Case 1
    VarB1=44
    VarB2=16
 Case 2
    VarB1=42
    VarB2=32
 Case 3
    VarB1=46
    VarB2=48
 Case 4
    VarB1=41
    VarB2=64
 Case 5
    VarB1=45
    VarB2=80
 Case 6
    VarB1=43
    VarB2=96
 Case 7
    VarB1=47
    VarB2=112
 Case Else
    VarB1=40
    VarB2=0
 End Select
 Return
GetForeGround:
 Select Case VarF
 ' high-intensity
 Case 8
    VarF1=30
    VarF2=8
    VarZ1=0
 Case 12
    VarF1=31
    VarF2=12
    VarZ1=0
 Case 10
    VarF1=32
    VarF2=10
    VarZ1=0
 Case 14
    VarF1=33
    VarF2=14
    VarZ1=0
 Case 9
    VarF1=34
    VarF2=9
    VarZ1=0
 Case 13
    VarF1=35
    VarF2=13
    VarZ1=0
 Case 11
    VarF1=36
    VarF2=11
    VarZ1=0
 Case 15
    VarF1=37
    VarF2=15
    VarZ1=0
 ' low-intensity
 Case 0
    VarF1=30
    VarF2=0
    VarZ1=-1
 Case 4
    VarF1=31
    VarF2=4
    VarZ1=-1
 Case 2
    VarF1=32
    VarF2=2
    VarZ1=-1
 Case 6
    VarF1=33
    VarF2=6
    VarZ1=-1
 Case 1
    VarF1=34
    VarF2=1
    VarZ1=-1
 Case 5
    VarF1=35
    VarF2=5
    VarZ1=-1
 Case 3
    VarF1=36
    VarF2=3
    VarZ1=-1
 Case 7
    VarF1=37
    VarF2=7
    VarZ1=-1
 Case Else
    VarF1=37
    VarF2=15
    VarZ1=0
 End Select
 Return
Error.Resume45x:
 Exit Sub
Error.Trap45x:
 Resume Error.Resume45x
End Sub

' VarQ: -2=init/-1=reset/0=enter/1-26=A-Z
Sub Command.History(VarQ)
 On Local Error Goto Error.Trap45z
 Static EndOfHistory As Integer
 ' check flags
 Call Check.Carrier
 If Lost.Carrier Then
    Exit Sub
 Endif
 If Config3(10)<>"" And Config3(10)=Out2 Then
    Exit Sub
 Endif
 ' select command history
 Select Case VarQ
 Case -2 ' init
    For Var=1 To 10
       History(Var)=Nul
    Next
 Case -1 ' reset
    Exit Sub
    History.Count=0
 Case 0 ' enter
    If Len(Out2) Then
       Gosub Get.Count
       EndOfHistory = -1
       If Var1=0 Then
          History.Count=1
          History(1)=Out2
       Else
          If Var1<10 Then
             History.Count=Var1+1
             History(Var1+1)=Out2
          Else
             For Var=1 To 9
                History(Var)=History(Var+1)
             Next
             History.Count=10
             History(10)=Out2
          Endif
       Endif      
    Endif
 Case 1 ' up ^[[A
    ' move remote status line up
    Var = StatusLine.Line
    If Var > 2 Then
       Call Remote.Status.Line(-1) ' restore
       StatusLine.Line = Var - 1
       Call Remote.Status.Line(1) ' store
       Call Remote.Status.Line(0) ' display
    Endif
 Case 2 ' down ^[[B
    ' move remote status line down
    Var = StatusLine.Line
    If Var < User.PageLength-2  Then
       Call Remote.Status.Line(-1) ' restore
       StatusLine.Line = Var + 1
       Call Remote.Status.Line(1) ' store
       Call Remote.Status.Line(0) ' display
    Endif
 Case 3 ' right ^[[C
    Gosub Get.Count
    ' return blank line at end of history
    IF Var1 > 0 THEN
       If Var1 = History.Count Then
          EndOfHistory=-1
          Gosub End.History
          Var1 = 0
       Endif
    Endif
    ' move to next history
    If Var1>0 Then
       Select Case History.Count
       Case 0
          VarQ=Var1
          Gosub Goto.History
       Case 1 To 9
          If History.Count+1<=Var1 Then
             History.Count=History.Count+1
             VarQ=History.Count
             Gosub Goto.History
          Else
             If History.Count=Var1 Then
                VarQ=Var1
                Gosub Goto.History
             Endif
          Endif
       Case 10
          VarQ=10
          Gosub Goto.History
       End Select
    Endif
 Case 4 ' left ^[[D
    Gosub Get.Count
    If Var1>0 Then
       If EndOfHistory Then
          EndOfHistory = 0
          VarQ=Var1
          Gosub Goto.History
       Else
          Select Case History.Count
          Case 0
             VarQ=Var1
             Gosub Goto.History
          Case 1
             VarQ=1
             Gosub Goto.History
          Case Else
             If History.Count-1>0 Then
                History.Count=History.Count-1
                VarQ=History.Count
                Gosub Goto.History
             Endif
          End Select
       Endif
    Endif
 Case 8 ' home ^[[H
    Gosub Get.Count
    If Var1>0 Then
       VarQ=1
       Gosub Goto.History
    Endif
 Case 11 ' end ^[[K
    Gosub Get.Count
    If Var1>0 Then
       VarQ=Var1
       Gosub Goto.History
    Endif
 Case Else
    Var$=Rtrim$(ANSI.Keys(VarQ))
    If Len(Var$) Then
       Carriage.Input=True
       Graphics.Off=True
       Gosub Truncate.Line
       Strng=Var$
       Call IO.O
       Out2=Var$
    Endif
 End Select
 Exit Sub
End.History:
 If Len(Out2) Then
    For VarZ=1 To Len(Out2)
       Call Back.Space
    Next
 Endif
 Out2=Nul
 Return
Goto.History:
 If Len(Out2) Then
    For VarZ=1 To Len(Out2)
       Call Back.Space
    Next
 Endif
 Out2=Nul
 History.Count=VarQ
 Carriage.Input=True
 Graphics.Off=True
 Var$=Rtrim$(History(VarQ))
 Gosub Truncate.Line
 Strng=Var$
 Call IO.O
 Out2=Var$
 Return
Get.Count:
 Var1=0
 For Var2=10 To 1 Step -1
    If Rtrim$(History(Var2))<>Nul Then
       Var1=Var2
       Exit For
    Endif
 Next
 Return
Truncate.Line:
 VarX2$=Strng
 Carriage.Input=True
 Strng=Action.Prompt
 ' strip macros
 Call Convert.String
 ' strip ansi prefixes
 Var2$=Rtrim$(ANSIPrefixChar)
 If Len(Var2$) Then
    Do
       VarX=Instr(Strng,Var2$)
       If VarX Then
          VarX2=Len(Var2$)
          Strng=Left$(Strng,VarX-1)+Mid$(Strng,VarX+VarX2+2) ' {FE
       Else
          Exit Do
       Endif
    Loop
 Endif
 VarY=Len(Strng)
 Strng=VarX2$
 If VarY+Len(Var$)>=User.LineLength Then
    Var$=Left$(Var$,User.LineLength-VarY-1)
 Endif
 Return
Error.Resume45z:
 Exit Sub
Error.Trap45z:
 Resume Error.Resume45z
End Sub

Sub Release.Time(Var)
 On Local Error Goto Error.Trap6a
 If Supported.Call=False Then
    For Var1=1 To Var
       Call Slice.Function
       If (OutregsX.AX And &HFF)=&H80 Then
          Supported.Call=True
          Exit For
       Endif
    Next
 Endif
Error.Resume6a:
 Exit Sub
Error.Trap6a:
 Resume Error.Resume6a
End Sub

' put string to modem port
Sub Put.Modem(Var$)
 If Local.Mode Then
    Exit Sub
 Endif
 If Lost.Carrier Then
    Exit Sub
 Endif
 For VarX=1 To Len(Var$)
    Var=Asc(Mid$(Var$,VarX,1))
    Do
       Call Driver(3) ' get line/modem status
       If (OutregsX.AX And &H4000)=&H4000 Then ' empty buffer
          InregsX.AX=Var
          Call Driver(1) ' put char w/ wait
          Exit Do
       Endif
    Loop
 Next
 Do
    Call Get.Modem(False)
    Call Driver(3) ' get line/modem status
    If (OutregsX.AX And &H4000)=&H4000 Then ' empty buffer
       Exit Do
    Endif
 Loop
End Sub

' get char from modem port
Sub Get.Modem(Var)
 If Local.Mode Then
    Exit Sub
 Endif
 If Lost.Carrier Then
    Exit Sub
 Endif
 If Fossil.Active Then ' fossil
    If Fossil.Version=1 Then ' x00 superset >1bh
       InregsX.AX=&H2000 ' destructive read w/o wait
       InregsX.DX=Port
       Call InterruptX(&H14,InregsX,OutregsX)
       If (OutregsX.AX And &HFF00)=&H0 Then ' got char
          Goto End.Input
       Endif
       Exit Sub
    Endif
 Endif
 Call Driver(3) ' get line/modem status
 If (OutregsX.AX And &H0100)=0 Then ' character is available
    Exit Sub
 Endif
 Call Driver(2) ' read char w/ wait
' store char
End.Input:
 VarX=(OutregsX.AX And &HFF)
 If VarX=0 Then
    Exit Sub
 Endif
 If VarX=3 Or VarX=11 Then
    Call Set.Break
    If Var=False Then
       Buffer=Buffer+Chr$(VarX)
    Endif
 Else
    Buffer=Buffer+Chr$(VarX)
 Endif
End Sub

Sub Check.Break
 On Local Error Goto Error.Trap21
 If Local.Mode=False Then
    If Lost.Carrier Then
       Exit Sub
    Endif
    ' check modem break
    Call Extended.Driver(16)
    If (OutregsX.AX And 1)=1 Then
       Call Set.Break
    Endif
 Endif
 ' check DOS break flag
 If Disable.Break=False Then
    DEF SEG = &H40
    X = PEEK(&H71)
    DEF SEG
    IF X = 128 THEN
       Call Control.Break
    END IF
 Endif
Error.Resume21:
 Exit Sub
Error.Trap21:
 Resume Error.Resume21
End Sub

Sub Sleep.Second(Var!)
 On Local Error Goto Error.Trap5
 Timer.Temp!=Timeit!
 Do
    Call Read.Keyboard(VarX$)
    If Time.Elapsed(Timer.Temp!,Var!) Then
       Exit Do
    Endif
    Call Release.Time(1)
 Loop
Error.Resume5:
 Exit Sub
Error.Trap5:
 Resume Error.Resume5
End Sub

Sub Control.Break
 On Local Error Goto Error.Trap21a
 Call Sleep.Second(1!)
 DEF SEG = &H40
 POKE &H71, &H0
 DEF SEG
 If Logged.In=False Then
    Exit Sub
 Endif
 Graphics.Off=True
 Call Send.ANSI
 Call Scrn(Chr$(13)+Chr$(10))
 If Allow.Break Then
    Var$="Enter (B)reak, (C)ancel, (L)ogoff user, (A)bort node: "
 Else
    Var$="Enter (C)ancel, (L)ogoff user, (A)bort node: "
 Endif
 Call Scrn(Var$)
 V$=Inkey$
 Var!=Timeit!
 Do
    V$=Inkey$
    If Time.Elapsed(Var!,10!) Then
       V$="C"
    Endif
    If Len(V$)=2 Then
       If Asc(Right$(V$,1))=0 Then ' imbedded control-break
          DEF SEG = &H40
          POKE &H71, &H0
          DEF SEG
          If Allow.Break Then
             V$="B"
          Else
             V$="C"
          Endif
       Endif
    Endif
    If Len(V$)=1 Then
       Select Case Ucase$(V$)
       Case "B" ' break
          If Allow.Break Then
             Call Scrn("B")
             Call Scrn(Chr$(13)+Chr$(10))
             V$=Inkey$
             Call Set.Break
             Exit Do
          Endif
       Case "C" ' cancel
          Out2=Nul
          Call Scrn("C")
          Call Scrn(Chr$(13)+Chr$(10))
          V$=Inkey$
          Graphics.Off=False
          Strng=Nul
          Call IO.O
          Break=1
          If Allow.Break Then
             Call Redisplay.Prompt
          Endif
          Exit Do
       Case "L" ' logoff
          Call Scrn("L")
          V$=Inkey$
          Color 7, 0
          Locate Max.Row-1,1,1
          Print
          Call Hang.Up(4)
          Exit Do
       Case "A" ' abort
          Call Scrn("A")
          V$=Inkey$
          Color 7, 0
          Locate Max.Row-1,1,1
          Print
          Call Hang.Up(11)
          Exit Do
       End Select
    Endif
    Call Release.Time(1)
 Loop
Error.Resume21a:
 Exit Sub
Error.Trap21a:
 Resume Error.Resume21a
End Sub

Sub Set.Break
 On Local Error Goto Error.Trap22
 If Allow.Break Then
    If Logged.In Then
       Break=True
       No=True
       Quit=True
    Endif
 Endif
Error.Resume22:
 Exit Sub
Error.Trap22:
 Resume Error.Resume22
End Sub

Sub Check.Carrier
 On Local Error Goto Error.Trap23
 If Local.Mode Then
    Exit Sub
 Endif
 If Lost.Carrier Then
    Exit Sub
 Endif
 If Mouse.Present Then
    Call MouseDriver
    If Mouse.Button2 Then
       If Local.Mode=False Or Normal.User=False Then
          Var1$=Temp$:Var2$=TempA$
          Call Status.Line(-1) ' toggle statusline
          Temp$=Var1$:TempA$=Var2$
       Endif
    Endif
    If Mouse.Button3 Then
       Call Hang.Up(16)
    Endif
 Endif
 Call Driver(3)
 If (OutregsX.AX And &HFF)=&HFF Then
    Call Hang.Up(3)
 Endif
 If (OutregsX.AX And &H80)=False Then
    Call Hang.Up(3)
 Endif
Error.Resume23:
 Exit Sub
Error.Trap23:
 Resume Error.Resume23
End Sub

Sub Line.Return
 On Local Error Goto Error.Trap25
 VarX=Break
 Break=False
 If User.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
Error.Resume25:
 Exit Sub
Error.Trap25:
 Resume Error.Resume25
End Sub

Sub Scroll.Return
 On Local Error Goto Error.Trap26
 VarX=Break
 Break=False
 If User.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
Error.Resume26:
 Exit Sub
Error.Trap26:
 Resume Error.Resume26
End Sub

' destructive backspace
Sub Back.Space
 On Local Error Goto Error.Trap27
 If Local.Mode=False Then
    If User.Echo=False Then
       Call Get.Cursor(VarX)
       ' backspace at column 2 remote
       If VarX=2 And Len(Out2)>=3 Then
          Call Get.Row(VarQ) ' current row
          Call Modem.ANSI(2,VarQ,User.LineLength)
          Call Put.Modem(" ")
          Call Modem.ANSI(2,VarQ,User.LineLength)
       Else
          Var$=Chr$(8)
          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)
    If VarX=1 Then
       Call Get.Row(VarQ) ' current row
       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
Error.Resume27:
 Exit Sub
Error.Trap27:
 Resume Error.Resume27
End Sub

' non-destructive backspace
Sub Back.Space2(Var)
 On Local Error Goto Error.Trap27z
 If Local.Mode=False Then
    If Remote.ANSI.Color Or Remote.Avatar.Color Then
       For Var2=1 To Var
          Var$=Chr$(27)+"[D"
          Call Put.Modem(Var$)
       Next
    Else
       For Var2=1 To Var
          Var$=Chr$(8)
          Call Put.Modem(Var$)
       Next
    Endif
 Endif
 If Local.ANSI.Color Or Local.Avatar.Color Then
    For Var2=1 To Var
       Var$=Chr$(27)+"[D"
       Call Put.Screen(Var$)
    Next
 Else
    For Var2=1 To Var
       Locate Csrlin,Pos(0)-1,1
    Next
 Endif
Error.Resume27z:
 Exit Sub
Error.Trap27z:
 Resume Error.Resume27z
End Sub

Sub Get.Cursor(Var)
 On Local Error Goto Error.Trap28
 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)
Error.Resume28:
 Exit Sub
Error.Trap28:
 Resume Error.Resume28
End Sub

Sub Get.Row(Var)
 On Local Error Goto Error.Trap28a
 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
Error.Resume28a:
 Exit Sub
Error.Trap28a:
 Resume Error.Resume28a
End Sub

Sub Scrn(Var$)
 On Local Error Goto Error.Trap33
 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$)
Error.Resume33:
 Exit Sub
Error.Trap33:
 Resume Error.Resume33
End Sub

Sub Scroll.Screen
 On Local Error Goto Error.Trap34
 Var1$=Chr$(13)+Chr$(10)
 If Disk.Log Then
    Call Disk.Output(Var1$)
 Endif
 If Print.Log Then
    Call Print.Output(Var1$)
 Endif
 Call Scrollit(Max.Row-2)
 Locate Max.Row-1,1,1
Error.Resume34:
 Exit Sub
Error.Trap34:
 Resume Error.Resume34
End Sub

Sub Scrollit(Var)
 On Local Error Goto Error.Trap34a
 Call VideoInt(6,Var*256+&H4F)
 Call VideoInt(2,Var*256)
Error.Resume34a:
 Exit Sub
Error.Trap34a:
 Resume Error.Resume34a
End Sub

Sub Scroll.Row(Var)
 On Local Error Goto Error.Trap34b
 Call Put.Screen(Strng)
 Call Scrollit(Max.Row-1)
 Locate Max.Row,1,1
Error.Resume34b:
 Exit Sub
Error.Trap34b:
 Resume Error.Resume34b
End Sub

Sub Local.ANSI(Var1,Var2,Var3)
 On Local Error Goto Error.Trap35
 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
Error.Resume35:
 Exit Sub
Error.Trap35:
 Resume Error.Resume35
End Sub

Sub ScreenANSI(Var1,Var2,Var3)
 On Local Error Goto Error.Trap36
 Call Local.ANSI(Var1,Var2,Var3)
 If Len(TempD$) Then
    Call Put.Screen(TempD$)
 Endif
Error.Resume36:
 Exit Sub
Error.Trap36:
 Resume Error.Resume36
End Sub

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

Sub ANSI(VarX,VarY,VarZ)
 On Local Error Goto Error.Trap38
 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
Error.Resume38:
 Exit Sub
Error.Trap38:
 Resume Error.Resume38
End Sub

Sub Avatar(VarX,VarY,VarZ)
 On Local Error Goto Error.Trap39
 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
Error.Resume39:
 Exit Sub
Error.Trap39:
 Resume Error.Resume39
End Sub

Sub Put.Screen(Var1$)
 On Local Error Goto Error.Trap40
 If Local.ANSI.Color Or Local.Avatar.Color Then
    For VarX=1 To Len(Var1$)
       VarY=Asc(Mid$(Var1$,VarX,1))
       If VarY=255 Then
          VarY=32
       Endif
       Call StdinInt(6,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 255
          Print " ";
       Case Else
          Print Chr$(Var2);
       End Select
    Next
 Endif
 If Disk.Log Then
    Call Disk.Output(Var1$)
 Endif
 If Print.Log Then
    Call Print.Output(Var1$)
 Endif
Error.Resume40:
 Exit Sub
Error.Trap40:
 Resume Error.Resume40
End Sub

Sub Disk.Output(VarX$)
 On Local Error Goto Disk.Error
 Data.Error=False
 If Logged.In=False Then
    Exit Sub
 Endif
 For VarX=1 To Len(VarX$)
    Var2=Asc(Mid$(VarX$,VarX,1))
    Select Case Var2
    Case 10
       Print #DiskFile,""
    Case 13
       Print #DiskFile,"";
    Case Else
       Print #DiskFile,Chr$(Var2);
    End Select
 Next
 Exit Sub
Disk.Exit:
 If Data.Error Then
    Disk.Log=False
    Call Open.Session.Files
 Endif
 Exit Sub
Disk.Error:
 Data.Error=True
 Resume Disk.Exit
End Sub

Sub Print.Output(VarX$)
 On Local Error Goto Print.Error
 Data.Error=False
 If Logged.In=False Then
    Exit Sub
 Endif
 If Instr(VarX$,Chr$(27)) Then
    Exit Sub
 Endif
 For VarX=1 To Len(VarX$)
    Var2=Asc(Mid$(VarX$,VarX,1))
    Select Case Var2
    Case 10
       Print #PrintFile,""
    Case 13
       Print #PrintFile,"";
    Case Else
       If Var2>=32 And Var2<127 Then
          Print #PrintFile,Chr$(Var2);
       Endif
    End Select
 Next
 Exit Sub
Print.Exit:
 If Data.Error Then
    Print.Log=False
    Call Open.Session.Files
 Endif
 Exit Sub
Print.Error:
 Data.Error=True
 Resume Print.Exit
End Sub

Sub Convert.Color(VarX,VarY)
 On Local Error Goto Error.Trap41
 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
 Else
    If VarB Then
       VarB=VarB-8
       Color ,VarB
    Endif
 Endif
Error.Resume41:
 Exit Sub
Error.Trap41:
 Resume Error.Resume41
End Sub

Sub Cursor(Var)
 On Local Error Goto Error.Trap42
 If Local.ANSI.Color Or Local.Avatar.Color Then
    Select Case Var
    Case False
       Call VideoInt(3,0)
       Cursor.Position=OutregsX.DX
    Case True
       Call VideoInt(2,Cursor.Position)
    End Select
 Else
    Select Case Var
    Case False
       CursorX=Csrlin
       CursorY=Pos(0)
    Case True
       Locate CursorX,CursorY,1
    End Select
 Endif
 If Var Then
    Var1$=Chr$(13)+Chr$(10)
    If Disk.Log Then
       Call Disk.Output(Var1$)
    Endif
    If Print.Log Then
       Call Print.Output(Var1$)
    Endif
 Endif
Error.Resume42:
 Exit Sub
Error.Trap42:
 Resume Error.Resume42
End Sub

Sub Restore.Color
 On Local Error Goto Error.Trap43
 Call ScreenANSI(1,37,15)
 If Graphics.Off Then
    Call ScreenANSI(4,37,15)
 Else
    Call ScreenANSI(4,Color.Code,Avatar.Code)
 Endif
Error.Resume43:
 Exit Sub
Error.Trap43:
 Resume Error.Resume43
End Sub

Sub Driver(Var)
 On Local Error Goto Error.Trap49
 If Local.Mode Then
    Exit Sub
 Endif
 Select Case Var
 Case 1 ' send char
    InregsX.AX=InregsX.AX Or &H0100
 Case 2 ' get char
    InregsX.AX=&H0200
 Case 3 ' read line/modem status
    InregsX.AX=&H0300
 End Select
 InregsX.DX=Port
 Call InterruptX(&H14,InregsX,OutregsX)
Error.Resume49:
 Exit Sub
Error.Trap49:
 Resume Error.Resume49
End Sub

' fossil function 00h-1bh, x00 fossil 00h-21h
Sub Extended.Driver(Var)
 On Local Error Goto Error.Trap50
 If Local.Mode Then
    Exit Sub
 Endif
 If Fossil.Active=False Then
    OutregsX.AX=False
    Exit Sub
 Endif
 Select Case Var
 Case 9 ' purge output
    InregsX.AX=&H0900
 Case 10 ' purge input
    InregsX.AX=&H0A00
 Case 13 ' dcd watchdog on
    InregsX.AX=&H1401
 Case 14 ' dcd watchdog off
    InregsX.AX=&H1400
 Case 15 ' flow control
    InregsX.AX=&H0F00
    If Config2(79) Then ' xon/xoff
       InregsX.AX=InregsX.AX Or &H09
    Endif
    If Config2(80) Then ' rts/cts
       InregsX.AX=InregsX.AX Or &H02
    Endif
 Case 16 ' set/check control-c/control-k
    InregsX.AX=&H1001
 Case 17 ' warm reboot
    InregsX.AX=&H1701
 Case 18 ' cold reboot
    InregsX.AX=&H1700
 Case 28 ' activate
    If Fossil.Version=1 Then
       InregsX.AX=&H1C00 ' x00
    Else
       InregsX.AX=&H0400 ' fossil
    Endif
 Case 29 ' deactivate
    If Fossil.Version=1 Then
       InregsX.AX=&H1D00 ' x00
    Else
       InregsX.AX=&H0500 ' fossil
    Endif
 End Select
 InregsX.DX=Port
 Call InterruptX(&H14,InregsX,OutregsX)
Error.Resume50:
 Exit Sub
Error.Trap50:
 Resume Error.Resume50
End Sub

' lower/raise dtr, get x00 status
Sub Extended.Driver2(Var,Var2)
 On Local Error Goto Error.Trap50x
 Select Case Var
 Case 4 ' detect fossil 2
    InregsX.AX=&H0400
 Case 6 ' line dtr io fossil
    InregsX.AX=&H0600+Var2
 Case 28 ' detect fossil 1 (x00)
    InregsX.AX=&H1C00
 Case 31 ' line dtr io x00
    InregsX.AX=&H1F01
    InregsX.BX=Var2
 End Select
 InregsX.DX=Port
 Call InterruptX(&H14,InregsX,OutregsX)
Error.Resume50x:
 Exit Sub
Error.Trap50x:
 Resume Error.Resume50x
End Sub

' send string to modem
Sub Send.Modem(Var$)
 On Local Error Goto Error.Trap55
 If Local.Mode Then
    Exit Sub
 Endif
 Do
    Call Driver(3) ' get line/modem status
    If (OutregsX.AX And &H40)=0 Then ' check ring bit
       Exit Do
    Endif
 Loop
 For VarX=1 To Len(Var$)
    Var=Asc(Mid$(Var$,VarX,1))
    Do
       Call Driver(3) ' get line/modem status
       If (OutregsX.AX And &H4000)=&H4000 Then ' empty buffer
          InregsX.AX=Var
          Call Driver(1) ' send character
          Exit Do
       Endif
    Loop
 Next
 Call Read.Modem
Error.Resume55:
 Exit Sub
Error.Trap55:
 Resume Error.Resume55
End Sub

' read character(s) from modem
Sub Read.Modem
 On Local Error Goto Error.Trap56
 If Local.Mode Then
    Exit Sub
 Endif
 Do
    Call Driver(3) ' get line/modem status
    If (OutregsX.AX And &H0100)=0 Then ' character is available
       Exit Do
    Endif
    Call Driver(2) ' get character
    VarX=(OutregsX.AX And &HFF)
    If VarX>0 Then
       Out2=Out2+Chr$(VarX)
    Endif
 Loop
Error.Resume56:
 Exit Sub
Error.Trap56:
 Resume Error.Resume56
End Sub

' Return Var=-1 if modem offline.
Sub Check.Modem(Var)
 On Local Error Goto Error.Trap46a
 Var=False
 Call Driver(3) ' get line/modem status
 If (OutregsX.AX And &HFF)=&HFF Then ' modem is disconnected
    Var=True
    Exit Sub
 Endif
 If (OutregsX.AX AND &HFF)=&H08 Then ' fossil always sets bit 3
    Var=True
    Exit Sub
 Endif
 If (OutregsX.AX AND &HFF)=&H00 Then ' check non-x00 fossil
    Var=True
    Exit Sub
 Endif
Error.Resume46a:
 Exit Sub
Error.Trap46a:
 Resume Error.Resume46a
End Sub

Sub IO.Timing
 On Local Error Goto Error.Trap18
 If User.Index=SFalse Then
    Exit Sub
 Endif
 Var=False
 Select Case UserRecord.ClassType
 Case Is>=AsstDM
    If Config1(3)>SFalse Then
       Var=True
    Endif
 Case Else
    Select Case UserRecord.Level
    Case Is<=1
       If Config1(1)>SFalse Then
	  Var=True
       Endif
    Case Else
       If Config1(2)>SFalse Then
	  Var=True
       Endif
    End Select
 End Select
 If Time.Override>False Then
    Var=True
 Endif
 If UserRecord.TimeLimit>SFalse Then
    Var=True
 Endif
 If UserRecord.TimeLimit<SFalse Then
    Var=False
 Endif
 If Var Then
    If Time.Elapsed(Timeon,Time.Left) Then
       Call Hang.Up(2)
       Exit Sub
    Endif
    If Two.Minutes.Left>=False Then
       If Time.Left>0! Then
          If Time.Elapsed(Timeon,Time.Left-60!) Then
             Gosub Display.One.Minute
             Exit Sub
          Endif
          If Time.Elapsed(Timeon,Time.Left-120!) Then
             Gosub Display.Two.Minutes
             Exit Sub
          Endif
       Endif
    Endif
 Endif
 If Two.Minutes.Left Then
    Exit Sub
 Endif
 Var1!=Csng(UserRecord.FromHour*3600!+UserRecord.FromMin*60!)
 Var2!=Csng(UserRecord.ToHour*3600!+UserRecord.ToMin*60!)
 If Var1!>SFalse Or Var2!>SFalse Then
    If Timeit!<=Var2! Then
       VarZ!=Var2!
       Gosub Check.Warning2
    Endif
 Endif
 If Two.Minutes.Left Then
    Exit Sub
 Endif
 Var2=False
 For VarZ=1 To 6+Special.Names
    Select Case VarZ
    Case 1
       Var1$=Rtrim$(GuestRecord.TownMayorTime)
    Case 2
       Var1$=Rtrim$(GuestRecord.GovernorTime)
    Case 3
       Var1$=Rtrim$(GuestRecord.GuildMasterTime)
    Case 4
       Var1$=Rtrim$(GuestRecord.SysopTime)
    Case 5
       Var1$=Rtrim$(GuestRecord.DMTime)
    Case 6
       Var1$=Rtrim$(GuestRecord.AsstDMTime)
    Case Else
       Var1$=Rtrim$(SpecialUserTime(VarZ-6))
    End Select
    If Var1$<>Nul Then
       FromH=Val(Mid$(Var1$,1,2))
       FromM=Val(Mid$(Var1$,4,2))
       ToH=Val(Mid$(Var1$,7,2))
       ToM=Val(Mid$(Var1$,10,2))
       Var1!=Csng(FromH*3600!+FromM*60!)
       Var2!=Csng(ToH*3600!+ToM*60!)
       If Var1!>SFalse Or Var2!>SFalse Then
          If Timeit!<=Var1! Then
             Select Case VarZ
             Case 1
                If UserRecord.Flags And Special.Char1 Then
                   Exit Sub
                Endif
             Case 2
                If UserRecord.Flags And Special.Char2 Then
                   Exit Sub
                Endif
             Case 3
                If UserRecord.Flags And Special.Char3 Then
                   Exit Sub
                Endif
             Case 4
                If UserRecord.Flags And Special.Char4 Then
                   Exit Sub
                Endif
             Case 5
                If UserRecord.ClassType=DM Then
                   Exit Sub
                Endif
             Case 6
                If UserRecord.ClassType=AsstDM Then
                   Exit Sub
                Endif
             Case Else
                Var2$=Rtrim$(SpecialUserName(VarZ-6))
                If Ucase$(Rtrim$(UserRecord.CodeName))=Var2$ Then
                   Exit Sub
                Endif
             End Select
          Endif
       Endif
    Endif
 Next
 If Var2=False Then
    Exit Sub
 Endif
 For VarZ=1 To 6+Special.Names
    If Two.Minutes.Left Then
       Exit For
    Endif
    Select Case VarZ
    Case 1
       Var1$=Rtrim$(GuestRecord.TownMayorTime)
       Gosub Check.Warning
    Case 2
       Var1$=Rtrim$(GuestRecord.GovernorTime)
       Gosub Check.Warning
    Case 3
       Var1$=Rtrim$(GuestRecord.GuildMasterTime)
       Gosub Check.Warning
    Case 4
       Var1$=Rtrim$(GuestRecord.SysopTime)
       Gosub Check.Warning
    Case 5
       Var1$=Rtrim$(GuestRecord.DMTime)
       Gosub Check.Warning
    Case 6
       Var1$=Rtrim$(GuestRecord.AsstDMTime)
       Gosub Check.Warning
    Case Else
       Var1$=Rtrim$(SpecialUserTime(VarZ-6))
       Gosub Check.Warning
    End Select
 Next
 Exit Sub
Check.Warning:
 FromH=Val(Mid$(Var1$,1,2))
 FromM=Val(Mid$(Var1$,4,2))
 VarZ!=Csng(FromH*3600!+FromM*60!)
Check.Warning2:
 VarX!=VarZ!-60!
 If VarX!<0! Then
    VarX!=VarX!+86400!
 Endif
 If Timeit!>=VarX! Then
    Gosub Display.One.Minute
    Return
 Endif
 VarX!=VarZ!-120!
 If VarX!<0! Then
    VarX!=VarX!+86400!
 Endif
 If Timeit!>=VarX! Then
    Gosub Display.Two.Minutes
 Endif
 Return
Display.One.Minute:
 If Two.Minutes.Left=1 Then
    Two.Minutes.Left=True
    If Logged.In Then
       Strng=Nul
       Call IO.O
       Call Put.Modem(Chr$(7))
       Strng="Less than one minute left!"
       Call IO.O
    Endif
 Endif
 Return
Display.Two.Minutes:
 If Two.Minutes.Left=False Then
    Two.Minutes.Left=1
    If Logged.In Then
       Strng=Nul
       Call IO.O
       Call Put.Modem(Chr$(7))
       Strng="Less than two minutes left!"
       Call IO.O
    Endif
 Endif
 Return
Error.Resume18:
 Exit Sub
Error.Trap18:
 Resume Error.Resume18
End Sub

Sub Keyboard(Var1,Var2)
 On Local Error Goto Error.Trap24
 Call Read.Keyboard(VarX$)
 Var1=False
 Select Case Len(VarX$)
 Case 0
    Exit Sub
 Case 1
    Var=Asc(VarX$)
    Select Case Var
    Case 8, 13, 32 To 127
       Buffer=Buffer+VarX$
    Case 3, 11 ' control-c/control-k
       Call Set.Break
    Case 27 ' escape
       If Local.Mode=False Or Normal.User=False Then
          Var1$=Temp$:Var2$=TempA$
          Call Status.Line(-1) ' toggle statusline
          Temp$=Var1$:TempA$=Var2$
       Endif
    End Select
 Case 2
    Select Case Asc(Right$(VarX$,1))
    Case 0 ' Control-Break
       Call Control.Break
    Case 75, 115, 155 ' left/ctrl-left/alt-left
       If Var2 Then
          If Logged.Node Then
             Call Command.History(4) ' left
          Endif
       Endif
    Case 77, 116, 157 ' right/ctrl-right/alt-right
       If Var2 Then
          If Logged.Node Then
             Call Command.History(3) ' right
          Endif
       Endif
    Case 119 ' ctrl-home
       If Var2 Then
          If Logged.Node Then
             Call Command.History(8) ' home
          Endif
       Endif
    Case 117 ' ctrl-end
       If Var2 Then
          If Logged.Node Then
             Call Command.History(11) ' end
          Endif
       Endif
    Case 71 ' home
       If Logged.In Then
          Buffer=Nul
          Func.Buffer=Nul
          If Logged.Node Then
             Call Remote.Status.Line(-1)
          Endif
          TempX=1
          Chat=True
          Remote=-1
          Call Chain.Command(4) ' Call Enter.Chat
          Call Redisplay.Prompt
          Chat=False
       Endif
    Case 79 ' end
       Color 7, 0
       Locate Max.Row-1,1,1
       Print
       Call Hang.Up(4)
       Var1=True
    Case 147 ' control-delete
       If Logged.In Then ' lockout user
          If User.Index>SFalse Then
             Close #TempFile1
             Open DND.Path+"delete.log" For Append Shared As #TempFile1
             Print #TempFile1,Lcase$(Rtrim$(UserRecord.CodeName))
             Print #TempFile1,Fclock$+" ("+DateS$+")"
             Print #TempFile1,"Locked out."
             Close #TempFile1
             Strng=Nul
             Call IO.O
             Graphics.Off=True
             Strng="User locked out: "+Fclock$+"."
             Call IO.O
             Graphics.Off=False
             Call Hang.Up(4)
             Var1=True
          Endif
       Endif
    Case 82 ' insert
       Call Remote.Adjust(-1)
    Case 83 ' delete
       Call Remote.Adjust(-2)
    Case 146 ' ctrl-insert
       Call Remote.Adjust(-3)
    Case 76, 143 ' keypad-5/control-keypad-5
       Call Remote.Adjust(1)
    Case 72, 152 ' up/alt-up
       Call Remote.Adjust(2)
    Case 80, 160 ' down/alt-dn
       Call Remote.Adjust(3)
    Case 141 ' ctrl-up
       Call Remote.Adjust(4)
    Case 145 ' ctrl-down
       Call Remote.Adjust(5)
    Case 73, 132 ' pageup/ctrl-pageup
       If Logged.Node Then
          Call Remote.Status.Line(-1)
       Endif
       Call Remote.Edit(0)
    Case 81, 118 ' pagedown/ctrl-pagedn
       If Logged.Node Then
          Call Remote.Status.Line(-1)
       Endif
       Call Remote.Edit(1)
    Case Else
       If Local.Mode=False Then
	  Func.Buffer=Right$(VarX$,1)
	  Var1=True
       Endif
    End Select
 End Select
 Exit Sub
Error.Resume24:
 Exit Sub
Error.Trap24:
 Resume Error.Resume24
End Sub

Sub Remote.Adjust(VarX)
 On Local Error Goto Error.Trap24b
 If Logged.In=False Then
    Exit Sub
 Endif
 If Logged.Node Then
    Call Remote.Status.Line(-1)
 Endif
 If VarX>0 Then
    Call Adjust.Time(VarX)
 Else
    Call Adjust.Level(Abs(VarX))
 Endif
 Call Redisplay.Prompt
Error.Resume24b:
 Exit Sub
Error.Trap24b:
 Resume Error.Resume24b
End Sub

Sub Adjust.Level(VarX)
 On Local Error Goto Error.Trap24ax
 Call Modem.ANSI(1,37,15)
 Call ScreenANSI(1,37,15)
 Graphics.On=False
 Graphics.Off=True
 Strng=Nul
 Call IO.O
 Out2=Nul
 Select Case VarX
 Case 1 ' insert (level up)
    Var1=UserRecord.Level
    If Var1<32767 Then
       Var1=Var1+1
       UserRecord.Level=Var1
    Endif
    Strng="Level is now"+Str$(Var1)+"."
    Call IO.O
    Graphics.Off=False
    Call New.Stats
 Case 2 ' delete (level down)
    Var1=UserRecord.Level
    If Var1>0 Then
       Var1=Var1-1
       UserRecord.Level=Var1
       Strng="Level is now"+Str$(Var1)+"."
       If Var1=0 Then
          Strng=Strng+" You are dead."
       Endif
       Call IO.O
       Graphics.Off=False
       If Var1=0 Then
          Call Player.Died
       Else
          Call New.Stats
       Endif
    Else
       Strng="You are dead."
       Call IO.O
       Graphics.Off=False
    Endif
 Case 3 ' ctrl-insert (level up 2)
    Var1=UserRecord.Level
    If Var1<32766 Then
       Var1=Var1+2
    Else
       Var1=32767
    Endif
    UserRecord.Level=Var1
    Strng="Level is now"+Str$(Var1)+"."
    Call IO.O
    Graphics.Off=False
    Call New.Stats
 End Select
 Call Send.ANSI
Error.Resume24ax:
 Exit Sub
Error.Trap24ax:
 Resume Error.Resume24ax
End Sub

Sub Adjust.Time(VarX)
 On Local Error Goto Error.Trap24a
 Call Modem.ANSI(1,37,15)
 Call ScreenANSI(1,37,15)
 Graphics.On=False
 Graphics.Off=True
 Strng=Nul
 Call IO.O
 Out2=Nul
 If UserRecord.TimeLimit<SFalse Then
    Graphics.Off=True
    Strng="Time remaining is unlimited."
    Call IO.O
    Graphics.Off=False
    Call Send.ANSI
    Exit Sub
 Endif
 Select Case VarX
 Case 1 ' keypad-5/control-keypad-5
    Var1!=Timeit!-Timeon ' elapsed
    If Var1!<0! Then Var1!=Var1!+86400! ' midnight
    Var2!=Time.Left-Var1! ' remaining
    Strng="Time remaining is"+Str$(Int(Var2!/60!))+" minutes."
    Call IO.O
    Graphics.Off=False
 Case 2 ' up/alt-up
    Graphics.Off=False
    Time.Left=Time.Left+300!
    Var1!=Timeit!-Timeon
    If Var1!<0! Then Var1!=Var1!+86400! ' midnight
    Var2!=Time.Left-Var1!
    Two.Minutes.Left=False
    Strng="Time remaining increased by 5 minutes to"+Str$(Int(Var2!/60!))+" minutes."
    Call IO.O
    Graphics.Off=False
 Case 3 ' down/alt-dn
    Graphics.Off=False
    If Time.Left=0! Then
       Eat$=Nul
    Else
       Time.Left=Time.Left-300!
       If Time.Left<0! Then
          Time.Left=0!
       Endif
       Var1!=Timeit!-Timeon
       If Var1!<0! Then Var1!=Var1!+86400! ' midnight
       Var2!=Time.Left-Var1!
       If Var2!<0! Then Var2!=0!
       Strng="Time remaining decreased by 5 minutes to"+Str$(Int(Var2!/60!))+" minutes."
       Call IO.O
    Endif
    Graphics.Off=False
 Case 4 ' ctrl-up
    Graphics.Off=False
    Time.Left=Time.Left+900!
    Var1!=Timeit!-Timeon
    If Var1!<0! Then Var1!=Var1!+86400! ' midnight
    Var2!=Time.Left-Var1!
    Two.Minutes.Left=False
    Strng="Time remaining increased by 15 minutes to"+Str$(Int(Var2!/60!))+" minutes."
    Call IO.O
    Graphics.Off=False
 Case 5 ' ctrl-down
    Graphics.Off=False
    If Time.Left=0! Then
       Eat$=Nul
    Else
       Time.Left=Time.Left-900!
       If Time.Left<0! Then
          Time.Left=0!
       Endif
       Var1!=Timeit!-Timeon
       If Var1!<0! Then Var1!=Var1!+86400! ' midnight
       Var2!=Time.Left-Var1!
       If Var2!<0! Then Var2!=0!
       Strng="Time remaining decreased by 15 minutes to"+Str$(Int(Var2!/60!))+" minutes."
       Call IO.O
    Endif
    Graphics.Off=False
 End Select
 Call Send.ANSI
Error.Resume24a:
 Exit Sub
Error.Trap24a:
 Resume Error.Resume24a
End Sub

Sub Remote.Edit(VarX)
 On Local Error Goto Error.Trap24c
 If Logged.In Then
    Call Cursor(False)
    Call Save.Screen(False)
    TempX=200+VarX
    Remote=-1
    Call Chain.Command(TempX) ' Call QuickEdit/Call HexCalc
    Call Redisplay.Prompt
 Endif
Error.Resume24c:
 Exit Sub
Error.Trap24c:
 Resume Error.Resume24c
End Sub

Sub Read.Node.Mess(VarX$)
 On Local Error Goto ErrorTrap13x
 Graphics.Off=True
 For Var!=1 To Lof(MessWorkFile2)/MessWorkRecLen2
    Call Read.Record(MessWorkFile2,Var!)
    VarX=False
    If Mid$(MessWorkRecord2.Flags,Node+1,1)="0" Then
       Select Case MessWorkRecord2.MessageType
       Case 12, 14, 30, 31
          VarX=True
          Strng=" aborted program!"
       Case 32, 33
          VarX=True
          Strng=" shutdown program!"
       Case 35
          If Logged.In Then
             VarX=True
             Strng=MessWorkRecord2.Message
          Endif
       End Select
       If VarX Then
          Mid$(MessWorkRecord2.Flags,Node+1,1)="1"
          Call Write.Record(MessWorkFile2,Var!)
          Exit For
       Endif
    Endif
 Next
 If VarX=False Then
    Exit Sub
 Endif
 If MessWorkRecord2.Node=False Then
    Out3="P##"
 Else
    If Brief.Messages Then
       Select Case MessWorkRecord2.Node
       Case 1 To 10 ' 0-9
          Out3="P#"+Mid$(Str$(MessWorkRecord2.Node-1),2)
       Case 11 To 36 ' A-Z
          Out3="P#"+Chr$(MessWorkRecord2.Node+54)
       Case 37 To 126 ' 10-99
          Out3="P#"+Mid$(Str$(MessWorkRecord2.Node-27),2)
       End Select
    Else
       Out3=MessWorkRecord2.UserName
       Out3=Rtrim$(Out3)
       Out3=Lcase$(Out3)
       Mid$(Out3,1,1)=Ucase$(Mid$(Out3,1,1))
    Endif
 Endif
 Strng=Out3+Strng
 Mid$(Strng,1,1)=Ucase$(Mid$(Strng,1,1))
 Select Case MessWorkRecord2.MessageType
 Case 12, 14
    Call IO.O
    'If Node>False Then
       Call Hang.Up(11)
    'Endif
 Case 30 ' dndpack suspend mode on
    VarX2=Node+1
    Call Read.Message.Record(MessWorkFile1,VarX2)
    MessWorkRecord1.SuspendNode=True
    Call Write.Message.Record(MessWorkFile1,VarX2)
    Suspend.Node=True
    Call Hang.Up(3)
 Case 31 ' dndpack suspend mode off
    VarX2=Node+1
    Call Read.Message.Record(MessWorkFile1,VarX2)
    MessWorkRecord1.SuspendNode=False
    Call Write.Message.Record(MessWorkFile1,VarX2)
    Suspend.Node=False
 Case 32, 33 ' shut down node
    Call IO.O
    'If Node>False Then
       Color 7, 0
       Locate Max.Row-1,1,1
       Print
       Call Hang.Up(4)
    'Endif
 Case 35 ' shut down message
    Var$=Strng
    Strng=Nul
    Call IO.O
    Strng=Var$
    Call IO.O
    If Logged.In=-2 Then
       Call Redisplay.Prompt
       Exit Sub
    Endif
    Graphics.Off=False
    Carriage.Input=True
    Strng=VarX$
    Call IO.O
    Out2=Nul
 End Select
ErrorResume13x:
 Exit Sub
ErrorTrap13x:
 Resume ErrorResume13x
End Sub

Sub Valid.Name(Var$)
 On Local Error Goto Error.Trap58
 TempA=True
 Select Case Lcase$(Rtrim$(Var$))
 Case "login", "logoff", "relogin"
    TempA=False
 Case "deleted", "offline", "enter"
    TempA=False
 Case "dead", "null", "range error"
    TempA=False
 Case Deleted$, Offline$, Enter$
    TempA=False
 Case Dead$, None$, Range$
    TempA=False
 End Select
Error.Resume58:
 Exit Sub
Error.Trap58:
 Resume Error.Resume58
End Sub

Sub Valid(Var$,Var)
 On Local Error Goto Error.Trap59
 If Len(Var$)/2<>Len(Var$)\2 Then
    Var$=Var$+" "
 Endif
 If Var/2<>Var\2 Then
    Var=Var+1
 Endif
 Var$=Left$(Var$,Var)
 Var$=Var$+Space$(Var-Len(Var$))
 For Var1=1 To Var
    Var2=Asc(Mid$(Var$,Var1,1))
    If Var2<32 Or Var2>127 Then
       Var$=Nul
       Exit For
    Endif
 Next
Error.Resume59:
 Exit Sub
Error.Trap59:
 Resume Error.Resume59
End Sub

Sub More.Prompt
 On Local Error Goto Error.Trap51
 If Break Then
    Exit Sub
 Endif
 VarX=Allow.Break
 Allow.Break=False
 Auto.Break=False
 Break=False
 Buffer=Nul
 Line.Length=1
 No.Echo=True
 Var=Graphics.Off
 Graphics.Off=False
 Type.Ahead=Nul
 Out2=Nul
 If Environ$("MOREPROMPT")<>"" Then
    Strng=Environ$("MOREPROMPT")
    If Left$(Strng,1)=Chr$(34) Then
       If Right$(Strng,1)=Chr$(34) Then
          Strng=Mid$(Strng,2)
          Strng=Left$(Strng,Len(Strng)-1)
       Endif
    Endif
    Word.Wrap=-1
    Call IO.I
    Word.Wrap=0
 Else
    Strng=More$
    Call IO.I
 Endif
 If Out2=Nul Then
    Var$=Environ$("MOREPROMPTDEFAULT")
    If Len(Var$) Then
       Select Case Var$
       Case "Y", "YES"
          Yes=-1 : No=0 : Quit=0 : Continuous=0
       Case "N", "NO"
          Yes=0 : No=-1 : Quit=0 : Continuous=0
       Case "Q", "QUIT"
          Yes=0 : No=0 : Quit=-1 : Continuous=0
       Case "C", "CONTINUOUS"
          Yes=0 : No=0 : Quit=0 : Continuous=-1
       Case Else
          Yes=-1 : No=0 : Quit=0 : Continuous=0
       End Select
    Endif
 Endif
 Graphics.Off=Var
 Allow.Break=VarX
Error.Resume51:
 Exit Sub
Error.Trap51:
 Resume Error.Resume51
End Sub

Sub Read.Keyboard(Var$)
 On Local Error Goto Error.Trap32
 Var$=Inkey$
 If Len(Var$)<=1 Then
    Exit Sub
 Endif
 ' control-break
 If Var$=Chr$(0)+Chr$(0) Then
    Var$=Nul
    Exit Sub
 Endif
 If Logged.In=False Then
    Exit Sub
 Endif
 If Local.Mode=False Then
    Exit Sub
 Endif
 Select Case Asc(Right$(Var$,1))
 Case 115, 155 ' ctrl-left/alt-left
    Exit Sub
 Case 116, 157 ' ctrl-right/alt-right
    Exit Sub
 Case 152, 160 ' alt-up/alt-dn
    Exit Sub
 Case 132, 118 ' ctrl-pageup/ctrl-pagedn
    Exit Sub
 Case 71, 72 ' home/up
    Exit Sub
 Case 73, 81 ' pageup/pagedown
    Exit Sub
 Case 75, 77 ' left/right
    Exit Sub
 Case 79, 80 ' end/down
    Exit Sub
 Case 82, 83, 146 ' insert/delete
    Exit Sub
 Case 147 ' control-delete
    Exit Sub
 Case 119, 117 ' ctrl-home/ctrl-end
    Exit Sub
 Case 141, 145 ' ctrl-up/ctrl-down
    Exit Sub
 Case 76, 143 ' keypad-5/control-keypad-5
    Exit Sub
 Case 31 ' Alt-S
    Call Modem.ANSI(1,37,15)
    Call ScreenANSI(1,37,15)
    Graphics.On=False
    Graphics.Off=True
    Strng=Nul
    Call IO.O
    If Logged.Node Then
       ' call windows screen saver
       Filename=DND.Path+"scrnsave.exe"
       If Dir$(Filename)<>"" Then
          Call Shell.Program("SCRNSAVE.EXE","")
       Endif
    Endif
    Graphics.Off=False
    Call Send.ANSI
    Call Redisplay.Prompt
    Exit Sub
 Case 32 ' Alt-D
    If Logged.Node Then
       Call Remote.Status.Line(-1)
    Endif
    Call Flag.Toggles(7)
    Call Modem.ANSI(1,37,15)
    Call ScreenANSI(1,37,15)
    Graphics.On=False
    Graphics.Off=True
    Strng=Nul
    Call IO.O
    If Disk.Log Then
       Strng="Disk log open."
    Else
       Strng="Disk log closed."
    Endif
    Call IO.O
    Graphics.Off=False
    Call Send.ANSI
    Call Redisplay.Prompt
    Exit Sub
 Case 25 ' Alt-P
    If Logged.Node Then
       Call Remote.Status.Line(-1)
    Endif
    Call Flag.Toggles(8)
    Call Modem.ANSI(1,37,15)
    Call ScreenANSI(1,37,15)
    Graphics.On=False
    Graphics.Off=True
    Strng=Nul
    Call IO.O
    If Print.Log Then
       Strng="Print log open."
    Else
       Strng="Print log closed."
    Endif
    Call IO.O
    Graphics.Off=False
    Call Send.ANSI
    Call Redisplay.Prompt
    Exit Sub
 Case 17 ' Alt-W
    Call Modem.ANSI(1,37,15)
    Call ScreenANSI(1,37,15)
    Graphics.On=False
    Graphics.Off=True
    Strng=Nul
    Call IO.O
    If Print.Log=False Then
       Strng="Print log not open."
       Call IO.O
       Graphics.Off=False
       Call Send.ANSI
       Call Redisplay.Prompt
       Exit Sub
    Endif
    Call Print.Screen
    Strng="Screen print."
    Call IO.O
    Graphics.Off=False
    Call Send.ANSI
    Call Redisplay.Prompt
    Exit Sub
 Case 45 ' Alt-X
    Call Modem.ANSI(1,37,15)
    Call ScreenANSI(1,37,15)
    Graphics.On=False
    Graphics.Off=True
    Strng=Nul
    Call IO.O
    If Disk.Log=False Then
       Strng="Disk log not open."
       Call IO.O
       Graphics.Off=False
       Call Send.ANSI
       Call Redisplay.Prompt
       Exit Sub
    Endif
    Call Print.LogFile
    Strng="Screen dump."
    Call IO.O
    Graphics.Off=False
    Call Send.ANSI
    Call Redisplay.Prompt
    Exit Sub
 Case 44 ' Alt-Z
    If Logged.Node Then
       Call Remote.Status.Line(-1)
    Endif
    Debug.Mode=Not Debug.Mode
    Call Modem.ANSI(1,37,15)
    Call ScreenANSI(1,37,15)
    Graphics.On=False
    Graphics.Off=True
    Strng=Nul
    Call IO.O
    If Debug.Mode Then
       Strng="Debug mode on."
    Else
       Strng="Debug mode off."
    Endif
    Call IO.O
    Graphics.Off=False
    Call Send.ANSI
    Call Redisplay.Prompt
    Exit Sub
 Case 33 ' Alt-F
    If Logged.Node Then
       Call Remote.Status.Line(-1)
    Endif
    Local.Mode=Not Local.Mode
    Call Modem.ANSI(1,37,15)
    Call ScreenANSI(1,37,15)
    Graphics.On=False
    Graphics.Off=True
    Strng=Nul
    Call IO.O
    If Local.Mode Then
       Strng="Local mode on."
    Else
       Strng="Local mode off."
    Endif
    Call IO.O
    Graphics.Off=False
    Call Send.ANSI
    Call Redisplay.Prompt
    Exit Sub
 Case 131 ' Alt-=
    If Normal.User=False Then
       Call IO.O
       Filename=DND.Path+"funckeys.txt"
       Call Out.File(FileName)
       Graphics.Off=False
       Call Send.ANSI
       Call Redisplay.Prompt
    Endif
    Exit Sub
 End Select
 If Normal.User Then
    Exit Sub
 Endif
 Select Case Asc(Right$(Var$,1))
 Case 59 ' dm command 7
    TempA$="EDIT"
    VarX=7 : VarY=1
    Goto MakeDMCommand
 Case 60 ' dm command 17
    TempA$="STA"
    VarX=17 : VarY=1
    Goto MakeDMCommand
 Case 61 ' dm command 6
    TempA$="DIS"
    VarX=6 : VarY=2
    Goto MakeDMCommand
 Case 62 ' dm command 16
    TempA$="REDU"
    VarX=16 : VarY=2
    Goto MakeDMCommand
 Case 63 ' dm command 4
    TempA$="CALL"
    VarX=4 : VarY=3
    Goto MakeDMCommand
 Case 64 ' dm command 13
    TempA$="KILL"
    VarX=13 : VarY=2
    Goto MakeDMCommand
 Case 65 ' dm command 18
    TempA$="TELE"
    VarX=18 : VarY=2
    Goto MakeDMCommand
 Case 66 ' dm command 12
    TempA$="INV"
    VarX=12 : VarY=1
    Goto MakeDMCommand
 Case 67 ' dm command 9
    TempA$="GET"
    VarX=9 : VarY=2
    Goto MakeDMCommand
 Case 68 ' dm command 14
    TempA$="LINK"
    VarX=14 : VarY=1
    Goto MakeDMCommand
 Case 133 ' sysop command 2
    TempA$="CONFIG"
    VarX=2
    Goto MakeSysopCommand
 Case 134 ' sysop command 10
    TempA$="SYSED"
    VarX=10
    Goto MakeSysopCommand
 Case Else
    Strng=Nul
    Call IO.O
    Strng="Type Alt-= for Help."
    Call IO.O
    Graphics.Off=False
    Call Redisplay.Prompt
 End Select
 Var$=Nul
 Exit Sub
MakeDMCommand:
 If Len(Config3(9)) Then
    Call Format.Command(VarX)
    VarX$=Config3(9)+TempA$
    Select Case VarY
    Case 1
       Buffer=Buffer+VarX$+Chr$(13)
    Case 2
       Buffer=Buffer+VarX$+" "
    Case 3
       Buffer=Buffer+VarX$
    End Select
 Endif
 Var$=Nul
 Exit Sub
MakeSysopCommand:
 If Len(Config3(79)) Then
    Call Format.Command2(VarX)
    Buffer=Buffer+Config3(79)+TempA$+Chr$(13)
 Endif
 Var$=Nul
 Exit Sub
Error.Resume32:
 Exit Sub
Error.Trap32:
 Resume Error.Resume32
End Sub

Sub Print.Screen
 On Local Error Goto Error.Trap32x
 Print #PrintFile,Chr$(13)+Chr$(10);
 For Var1=1 To Max.Row-1
    For Var2=1 To User.LineLength-1
       TempZ1=Screen(Var1,Var2) ' char
       Print #PrintFile,Chr$(TempZ1);
    Next
    Print #PrintFile,Chr$(13)+Chr$(10);
 Next
 Exit Sub
Error.Resume32x:
 Exit Sub
Error.Trap32x:
 Print.Log=False
 Resume Error.Resume32x
End Sub

Sub Print.LogFile
 On Local Error Goto Error.Trap32z
 Print #DiskFile,Chr$(13)+Chr$(10);
 For Var1=1 To Max.Row-1
    For Var2=1 To User.LineLength-1
       TempZ1=Screen(Var1,Var2) ' char
       Print #DiskFile,Chr$(TempZ1);
    Next
    Print #DiskFile,Chr$(13)+Chr$(10);
 Next
 Exit Sub
Error.Resume32z:
 Exit Sub
Error.Trap32z:
 Disk.Log=False
 Resume Error.Resume32z
End Sub

Sub Redisplay.Prompt
 On Local Error Goto Error.Trap33z
 Carriage.Input=True
 Strng=Action.Prompt
 Call Convert.String ' convert macros first
 Call Convert.ANSI(False,VarQ,VarR) ' convert prefixes next
 Carriage.Input=True
 Graphics.On=True
 Call IO.O
 Graphics.On=False
Error.Resume33z:
 Exit Sub
Error.Trap33z:
 Resume Error.Resume33z
End Sub
