 Rem File: Convert.bas - for Cmd*.bas modules, Dndbbs.
 Rem $Include: 'dndbbs.inc'
 Declare Function MakeTime$(V2!)

 ' DOS Data Transfer Area
 Type DTAtype
  Drive As String*1
  SearchTemplate As String*11
  SearchAttr As String*1
  EntryCount As String*2
  ClusterNumber As String*2
  Reserved As String*4
  FileAttr As String*1
  FileTime As String*2
  FileDate As String*2
  FileSize As String*4
  ASCIIZfilename As String*13
 End Type

Sub Convert.ANSI(Var,VarQ,VarR)
 On Local Error Goto ErrorTrp36x
 Static Current.Avatar.Background As Integer
 Static Current.Avatar.Foreground As Integer
 If Var Then ' reset before each line
    Current.Avatar.Background = 0
    Call Modem.ANSI(4,40,0)
    Call ScreenANSI(4,40,0)
    Current.Avatar.Foreground = 15
    Call Modem.ANSI(4,37,15)
    Call ScreenANSI(4,37,15)
 Endif
 VarX$=Strng
 Var2$=ANSIPrefixChar
 VarZ=Len(Var2$)
 If VarZ=False Then
    Exit Sub
 Endif
 Do
    If Len(Strng)=False Then
       Exit Do
    Endif
    Var1=Instr(Strng,Var2$)
    If Var1=False Then
       Exit Do
    Endif
    Select Case Lcase$(Mid$(Strng,Var1+VarZ,1))
    Case "z" ' pause second
       Var4$=Lcase$(Mid$(Strng,Var1+VarZ+1,1))
       Select Case Var4$
       Case "1" To "9"
          Strng=Left$(Strng,Var1-1)
          Carriage.Return=True
          Graphics.On=True
          Call IO.O
          Graphics.On=False
          VarQ1!=Csng(Int(Val(Var4$)))
          Call Sleep.Second(VarQ1!)
          Strng=Mid$(VarX$,Var1+VarZ+2)
          VarX$=Strng
       Case Else
          Goto Strip.Tail
       End Select
    Case "m" ' moreprompt
       Var4$=Lcase$(Mid$(Strng,Var1+VarZ+1,1))
       Select Case Var4$
       Case "1" To "5"
          Strng=Left$(Strng,Var1-1)
          If Continuous=False Then
             Gosub Get.More.Color
             If VarQ=0 And VarR=0 Then
                VarQ=37
                VarR=7
             Endif
             Call Modem.ANSI(4,VarQ,VarR)
             Call ScreenANSI(4,VarQ,VarR)
             Graphics.On=True
             Graphics.Off=False
             Call More.Prompt
             Graphics.On=False
          Endif
          Strng=Mid$(VarX$,Var1+VarZ+2)
          VarX$=Strng
       Case "o"
          Strng=Left$(Strng,Var1-1)
          If Continuous=False Then
             Graphics.On=False
             Graphics.Off=False
             Call More.Prompt
          Endif
          Strng=Mid$(VarX$,Var1+VarZ+2)
          VarX$=Strng
       Case Else
          Goto Strip.Tail
       End Select
    Case "f" ' foreground
       Var4$=Lcase$(Mid$(Strng,Var1+VarZ+1,1))
       Select Case Var4$
       Case "0" To "7" ' low-intensity
          Strng=Left$(Strng,Var1-1)
          Carriage.Return=True
          Graphics.On=True
          Call IO.O
          Graphics.On=False
          Gosub Get.ANSI.Color
          Current.Avatar.Foreground=VarY
          VarY=VarY Or Current.Avatar.Background
          Call Modem.ANSI(1,VarX,VarY)
          Call ScreenANSI(1,VarX,VarY)
          Strng=Mid$(VarX$,Var1+VarZ+2)
          VarX$=Strng
       Case "8", "9", "a" To "f" ' high-intensity
          Strng=Left$(Strng,Var1-1)
          Carriage.Return=True
          Graphics.On=True
          Call IO.O
          Graphics.On=False
          Gosub Get.ANSI.Color
          Current.Avatar.Foreground=VarY
          VarY=VarY Or Current.Avatar.Background
          Call Modem.ANSI(4,VarX,VarY)
          Call ScreenANSI(4,VarX,VarY)
          Strng=Mid$(VarX$,Var1+VarZ+2)
          VarX$=Strng
       Case Else
          Goto Strip.Tail
       End Select
    Case "b" ' background
       Var4$=Mid$(Strng,Var1+VarZ+1,1)
       Select Case Var4$
       Case "0" To "7"
          Strng=Left$(Strng,Var1-1)
          Carriage.Return=True
          Graphics.On=True
          Call IO.O
          Graphics.On=False
          Gosub Get.ANSI.Color2
          Current.Avatar.Background=VarY
          VarY=VarY Or Current.Avatar.Foreground
          Call Modem.ANSI(5,VarX,VarY)
          Call ScreenANSI(5,VarX,VarY)
          Strng=Mid$(VarX$,Var1+VarZ+2)
          VarX$=Strng
       Case Else
          Goto Strip.Tail
       End Select
    Case "c" ' clear screen
       Var4$=Lcase$(Mid$(Strng,Var1+VarZ+1,1))
       If Var4$="s" Then
          Strng=Left$(Strng,Var1-1)
          Call Modem.ANSI(3,0,0)
          Call ScreenANSI(3,0,0)
          Strng=Mid$(VarX$,Var1+VarZ+2)
          VarX$=Strng
       Else
          Goto Strip.Tail
       Endif
    Case Else
       Goto Strip.Tail
    End Select
 Loop
 Exit Sub
Strip.Tail:
 Strng=Left$(Strng,Var1)
 Carriage.Return=True
 If Var Then
    Graphics.Off=True
 Endif
 Call IO.O
 If Var Then
    Graphics.Off=False
 Endif
 Strng=Mid$(VarX$,Var1+VarZ)
 VarX$=Strng
 Exit Sub
Get.More.Color:
 Select Case Var4$
 Case "1"
    VarQ=34
    VarR=9
 Case "2"
    VarQ=32
    VarR=10
 Case "3"
    VarQ=31
    VarR=12
 Case "4"
    VarQ=35
    VarR=13
 Case "5"
    VarQ=33
    VarR=14
 End Select
 Return
Get.ANSI.Color:
 Select Case Var4$
 Case "0"
    VarX=30
    VarY=0
 Case "1"
    VarX=34
    VarY=1
 Case "2"
    VarX=32
    VarY=2
 Case "3"
    VarX=36
    VarY=3
 Case "4"
    VarX=31
    VarY=4
 Case "5"
    VarX=35
    VarY=5
 Case "6"
    VarX=33
    VarY=6
 Case "7"
    VarX=37
    VarY=7
 Case "8"
    VarX=30
    VarY=8
 Case "9"
    VarX=34
    VarY=9
 Case "a"
    VarX=32
    VarY=10
 Case "b"
    VarX=36
    VarY=11
 Case "c"
    VarX=31
    VarY=12
 Case "d"
    VarX=35
    VarY=13
 Case "e"
    VarX=33
    VarY=14
 Case "f"
    VarX=37
    VarY=15
 End Select
 Return
Get.ANSI.Color2:
 Select Case Var4$
 Case "0"
    VarX=40
    VarY=0
 Case "1"
    VarX=44
    VarY=16
 Case "2"
    VarX=42
    VarY=32
 Case "3"
    VarX=46
    VarY=48
 Case "4"
    VarX=41
    VarY=64
 Case "5"
    VarX=45
    VarY=80
 Case "6"
    VarX=43
    VarY=96
 Case "7"
    VarX=47
    VarY=112
 End Select
 Return
ErrorRsm36x:
 Exit Sub
ErrorTrp36x:
 Resume ErrorRsm36x
End Sub

Sub Convert.String
 On Local Error Goto ErrorTrap24x

 ' xon/xoff display variable
 Static Disable.String2 As Integer

 ' dta variables and structure declaration
 DIM BASIC.DTA.SEG As Integer
 DIM BASIC.DTA.OFF As Integer
 DIM DTAfile AS DTAtype

 ' filename variables
 DIM Drive.Search AS STRING * 1
 DIM ASCIIZ AS STRING * 260
 DIM Default.Net AS STRING * 16

 ' recurse count variable
 Dim MaxRecurse As Integer
 MaxRecurse=16

 ' init some variables
 Var2=False
 VarC=False
 VarZ=False

 ' parse string variables
 Select Case Lcase$(Strng)
 Case "[xon]"
    Disable.String2=False
    VarZ=True
 Case "[xoff]"
    Disable.String2=True
    VarZ=True
 Case "[bell]"
    Call Put.Modem(Chr$(7)) ' send bell
    VarZ=True
 End Select
 If VarZ Then
    Carriage.Return=-1
    Strng=Nul
    Exit Sub
 Endif
 If Disable.String Then
    Exit Sub
 Endif
 If Disable.String2 Then
    Exit Sub
 Endif
 Do
    If Len(Strng)=False Then
       Exit Do
    Endif
    Var2=Instr(Var2+1,Strng,"[")
    If Var2=False Then
       Exit Do
    Endif
    Var$=Mid$(Strng,Var2+2,1)
    If Var$="]" Then
       Var2$=Mid$(Strng,Var2+1,1)
       Var3$=Nul
       Select Case Lcase$(Var2$)
       Case "1"
          Var3$=UserRecord.CodeName
          Var3$=Rtrim$(Var3$)
          Var3$=Lcase$(Var3$)
          Mid$(Var3$,1,1)=Ucase$(Mid$(Var3$,1,1))
       Case "2"
          Var3$=UserRecord.ClassName
          Var3$=Rtrim$(Var3$)
          Var3$=Lcase$(Var3$)
          Mid$(Var3$,1,1)=Ucase$(Mid$(Var3$,1,1))
       Case "3"
          Var3$=DateS$+" "+Format$(Now,"hh:mm:ss")
       Case "4"
          Var3$=DateS$
       Case "5"
          Var3$=Format$(Now,"hh:mm:ss")
       Case "6"
          Var3$=Fclock$
       Case "7"
          If NodeX="" Then
             Var3$="-"
          Else
             Var3$=NodeX
          Endif
       Case "8"
          If Port=-1 Then
             Var3$="-"
          Else
             Var3$=Mid$(Str$(Port+1),2)
          Endif
       Case "9"
          If Port=-1 Then
             Var3$="-"
          Else
             Var3$=Mid$(Str$(Port),2)
          Endif
       Case "a"
          Var3$=Clock.On
       Case "b"
          Var3$=Version$
       Case "c"
          Var3$=Release$
       Case "d"
          Var3$=ProgramName$
       Case "e"
          Var3$=RunMode$
       Case "f"
          Var3$=Mid$(Str$(Run.Number),2)
       Case "g" to "j" ' Var2$ = 103 to 106 (1-4)
          Var3$=Rtrim$(GuestLines(Asc(Var2$)-102))
       Case "k"
          Var3$=Rtrim$(GuestRecord.SysopName)
       Case "l"
          Var3$=Rtrim$(GuestRecord.SysopEmail)
       Case "m"
          Var3$=Rtrim$(GuestRecord.BBSname)
       Case "n"
          Var3$=Rtrim$(GuestRecord.BBSdesc)
       Case "o" to "z" ' Var2$ = 111 - 122 (5-16)
          Var3$=Rtrim$(GuestLines(Asc(Var2$)-106))
       End Select
       If Len(Var3$) Then
          VarC=VarC+1
          If VarC>MaxRecurse Then
             Exit Sub
          Endif
       Endif
       Strng=Left$(Strng,Var2-1)+Var3$+Mid$(Strng,Var2+3)
    Endif
 Loop

' parse system guestlines 17 to 99.
 Var2=False
 Do
    If Len(Strng)=False Then
       Exit Do
    Endif
    Var2=Instr(Var2+1,Strng,"{")
    If Var2=False Then
       Exit Do
    Endif
    Var$=Mid$(Strng,Var2+3,1)
    If Var$="}" Then
       Var2$=Mid$(Strng,Var2+1,2)
       Var3$=Nul
       Select Case Val(Var2$)
       Case 17 to 99
          Var3$=Rtrim$(GuestLines(Val(Var2$)))
       End Select
       If Len(Var3$) Then
          VarC=VarC+1
          If VarC>MaxRecurse Then
             Exit Sub
          Endif
       Endif
       Strng=Left$(Strng,Var2-1)+Var3$+Mid$(Strng,Var2+4)
    Endif
 Loop

' parse system guestlines 100 to 127.
 Var2=False
 Do
    If Len(Strng)=False Then
       Exit Do
    Endif
    Var2=Instr(Var2+1,Strng,"{")
    If Var2=False Then
       Exit Do
    Endif
    Var$=Mid$(Strng,Var2+4,1)
    If Var$="}" Then
       Var2$=Mid$(Strng,Var2+1,3)
       Var3$=Nul
       Select Case Val(Var2$)
       Case 100 to 127
          Var3$=Rtrim$(GuestLines(Val(Var2$)))
       End Select
       If Len(Var3$) Then
          VarC=VarC+1
          If VarC>MaxRecurse Then
             Exit Sub
          Endif
       Endif
       Strng=Left$(Strng,Var2-1)+Var3$+Mid$(Strng,Var2+5)
    Endif
 Loop

 ' parse drive strings
 Var2=False
 Do
    If Len(Strng)=False Then
       Exit Do
    Endif
    Var2=Instr(Var2+1,Strng,"(")
    If Var2=False Then
       Exit Do
    Endif
    Var$=Mid$(Strng,Var2+2,1)
    If Var$=")" Then
       Var2$=Mid$(Strng,Var2+1,1)
       Var3$=Nul
       Select Case Lcase$(Var2$)
       Case "1" ' C:\
          Var3$=Left$(Curdir$,1)
       Case "2" ' C:\Dndbbs
          Var3$=Mid$(Curdir$,4)
       Case "3" ' volume label
          Gosub Label
       Case "4"
          Gosub Machine
       End Select
       If Len(Var3$) Then
          VarC=VarC+1
          If VarC>MaxRecurse Then
             Exit Sub
          Endif
       Endif
       Strng=Left$(Strng,Var2-1)+Var3$+Mid$(Strng,Var2+3)
    Endif
 Loop

 ' parse user strings
 Var2=False
 Do
    If Len(Strng)=False Then
       Exit Do
    Endif
    Var2=Instr(Var2+1,Strng,"<")
    If Var2=False Then
       Exit Do
    Endif
    Var$=Mid$(Strng,Var2+3,1)
    If Var$=">" Then
       Var2$=Mid$(Strng,Var2+1,2)
       Var3$=Nul
       Select Case Ucase$(Var2$)
       Case "W1", "W2", "W3", "W4", "W5", "W6", "W7", "W8", "W9"
          VarQ1 = Int(Val(Right$(Var2$,1)))
          Call PlayWav(VarQ1)
       Case "WA", "WB", "WC", "WD", "WE", "WF"
          VarQ1 = Int(Val("&H"+Right$(Var2$,1)))
          Call PlayWav(VarQ1)
       Case "GS" ' sysop page slots
          Var3$=Rtrim$(GuestRecord.PageTimes)
       Case "WS" ' wishes left
          Var3$=Mid$(Str$(UserRecord.WishsLeft),2)
       Case "GD" ' gold
          Var3$=Mid$(Str$(UserRecord.Gold),2)
       Case "EX" ' experience
          Var3$=Mid$(Str$(UserRecord.Experience),2)
       Case "LV" ' level
          Var3$=Mid$(Str$(UserRecord.Level),2)
       Case "ST" ' strength
          Var3$=Mid$(Str$(UserRecord.Stats(1)),2)
       Case "IN" ' intelligence
          Var3$=Mid$(Str$(UserRecord.Stats(2)),2)
       Case "WI" ' wisdom
          Var3$=Mid$(Str$(UserRecord.Stats(3)),2)
       Case "DE" ' dexterity
          Var3$=Mid$(Str$(UserRecord.Stats(4)),2)
       Case "CO" ' constitution
          Var3$=Mid$(Str$(UserRecord.Stats(5)),2)
       Case "PI" ' piety
          Var3$=Mid$(Str$(UserRecord.Stats(6)),2)
       Case "CH" ' charisma
          Var3$=Mid$(Str$(UserRecord.Stats(7)),2)
       Case "FA" ' fatigue
          Var3$=Mid$(Str$(UserRecord.Fatigue),2)
       Case "VI" ' vitality
          Var3$=Mid$(Str$(UserRecord.Vitality),2)
       Case "MA" ' magic
          Var3$=Mid$(Str$(UserRecord.Magic),2)
       Case "PS" ' psionic
          Var3$=Mid$(Str$(UserRecord.Psionic),2)
       Case "TA" ' login date\time
          Var3$=Date.On+" "+Time.On
       Case "TB" ' login date
          Var3$=Date.On
       Case "TC" ' login time
          Var3$=Time.On
       Case "TH" ' hexidecimal date\time
          d$ = Format$(Now,"mm-dd-yyyy")
          t$ = Format$(Now,"hh:mm:ss")
          ' year/month/day
          y = VAL(RIGHT$(d$, 4))
          m0 = VAL(MID$(d$, 1, 2))
          d = VAL(MID$(d$, 4, 2))
          ' hour/minute/second
          h = VAL(MID$(t$, 1, 2))
          m1 = VAL(MID$(t$, 4, 2))
          s = VAL(MID$(t$, 7, 2))
          ' construct string
          Var3$ = RIGHT$("000" + HEX$(y), 3) + HEX$(m0)
          Var3$ = Var3$ + RIGHT$("00" + HEX$(d), 2) + "-"
          Var3$ = Var3$ + RIGHT$("00" + HEX$(h), 2)
          Var3$ = Var3$ + RIGHT$("00" + HEX$(m1), 2)
          Var3$ = Var3$ + RIGHT$("00" + HEX$(s), 2)
       Case "TI"
          d$ = Format$(Now,"mm-dd-yyyy")
          y = VAL(RIGHT$(d$, 4))
          m0 = VAL(MID$(d$, 1, 2))
          d = VAL(MID$(d$, 4, 2))
          Var3$ = RIGHT$("000" + HEX$(y), 3)
          Var3$ = Var3$ + HEX$(m0)
          Var3$ = Var3$ + RIGHT$("00" + HEX$(d), 2)
       Case "TJ"
          t$ = Format$(Now,"hh:mm:ss")
          h = VAL(MID$(t$, 1, 2))
          m1 = VAL(MID$(t$, 4, 2))
          s = VAL(MID$(t$, 7, 2))
          Var3$ = RIGHT$("00" + HEX$(h), 2)
          Var3$ = Var3$ + RIGHT$("00" + HEX$(m1), 2)
          Var3$ = Var3$ + RIGHT$("00" + HEX$(s), 2)
       Case "TO" ' time online
          Call TimeSub2(Var3$)
       Case "TR" ' time remaining
          Call TimeSub3(Var3$)
       Case "P0" ' node prefix
          Var3$=Config3(78)
       Case "P1" ' dm prefix
          Var3$=Config3(9)
       Case "P2" ' sysop prefix
          Var3$=Config3(79)
       Case "DO" ' door name
          If BBS.Mode Then
             Var3$=Door.Name
          Else
             Var3$="<n/a>"
          Endif
       Case "DN" ' door node
          If BBS.Mode Then
             Var3$=Door.Node
          Else
             Var3$="<n/a>"
          Endif
       End Select
       If Len(Var3$) Then
          VarC=VarC+1
          If VarC>MaxRecurse Then
             Exit Sub
          Endif
       Endif
       Strng=Left$(Strng,Var2-1)+Var3$+Mid$(Strng,Var2+4)
    Endif
 Loop
 Exit Sub

Label:
 ' store basic dta.
 InregsX.AX = &H2F00
 CALL InterruptX(&H21, InregsX, OutregsX)
 BASIC.DTA.SEG = OutregsX.ES
 BASIC.DTA.OFF = OutregsX.BX

 ' assign program dta.
 InregsX.AX = &H1A00
 InregsX.DS = VARSEG(DTAfile)
 InregsX.DX = VARPTR(DTAfile)
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' get current drive
 InregsX.AX = &H1900
 CALL InterruptX(&H21, InregsX, OutregsX)
 Drive.Search$ = CHR$((OutregsX.AX AND &HFF) + 65)

 ' store long filename.
 W$ = Drive.Search$ + ":\*.*"

 ' store asciiz filename.
 ASCIIZ = W$ + CHR$(0)

 ' start findfirst.
 InregsX.AX = &H4E00
 InregsX.CX = &H8 ' volume label
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.DX = VARPTR(ASCIIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' restore basic dta.
 InregsX.AX = &H1A00
 InregsX.DS = BASIC.DTA.SEG
 InregsX.DX = BASIC.DTA.OFF
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' store drive volume label.
 Var3$ = ""
 Var3$ = DTAfile.ASCIIZfilename
 Var = INSTR(Var3$, CHR$(0))
 IF Var THEN
    Var3$ = LEFT$(Var3$, Var - 1)
 END IF
 Return

Machine:
 Default.Net = Nul
 InregsX.AX = &H5E00
 InregsX.DS = VARSEG(Default.Net)
 InregsX.DX = VARPTR(Default.Net)
 CALL InterruptX(&H21, InregsX, OutregsX)
 Var3$ = Default.Net
 Imbedded = INSTR(Var3$, CHR$(0))
 IF Imbedded THEN
    Var3$ = LEFT$(Var3$, Imbedded - 1)
 END IF
 Var3$ = UCASE$(RTRIM$(Var3$))
 ' check net path.
 If Var3$ = Nul Then
    Var$ = Environ$("COMPUTERNAME")
    If Len(Var$) Then
       Var3$ = Var$
    Endif
 Endif
 Return
ErrorResume24x:
 Exit Sub
ErrorTrap24x:
 Resume ErrorResume24x
End Sub

' time online
Sub TimeSub2(Var3$)
 On Local Error Goto ErrorTrap24x2
 OnDate#=DateValue(Date.On)
 d1$=Format$(Now,"mm-dd-yyyy")
 Var#=DateValue(d1$)-OnDate# ' serialized date
ErrorResume24x2:
 If Var#>=2# Then
    Var3$=Mid$(Str$(Var#),2)+":00:00:00" ' days
    Exit Sub
 Endif
 Var1!=Timeit-Timeon ' elapsed
 If Var1!<0! Then
    Var1!=Var1!+86400! ' midnight
 Endif
 Var3$=MakeTime$(Var1!)
 Exit Sub
ErrorTrap24x2:
 Var#=0#
 Resume ErrorResume24x2
End Sub

' time remaining
Sub TimeSub3(Var3$)
 On Local Error Goto ErrorTrap24x3
 If UserRecord.TimeLimit<SFalse Then
    Var3$="99:99:99" ' unlimited
    Exit Sub
 Endif
 Var2!=Timeit-Timeon ' elapsed
 If Var2!<0! Then
    Var2!=Var2!+86400! ' midnight
 Endif
 Var1!=Time.Left-Var2! ' remaining
 If Var1!<0! Then
    Var1!=0!
 Endif
 Var3$=MakeTime$(Var1!)
 Exit Sub
ErrorResume24x3:
 Var3$=""
 Exit Sub
ErrorTrap24x3:
 Resume ErrorResume24x3
End Sub

' construct time string
Function MakeTime$(Var1!)
 Hours=Int(Var1!/3600!)
 Var1!=Var1!-Hours*3600!
 Minutes=Int(Var1!/60!)
 Var1!=Var1!-Minutes*60!
 Seconds=Int(Var1!)
 Var3$=Right$("00"+Mid$(Str$(Hours),2),2)+":"+_
 Right$("00"+Mid$(Str$(Minutes),2),2)+":"+_
 Right$("00"+Mid$(Str$(Seconds),2),2)
 MakeTime$=Var3$
End Function

Sub Capitalize(Var3$)
 On Local Error Goto ErrorTrap24x4
 Var3$=Rtrim$(Ltrim$(Var3$))
 If Var3$=Nul Then
    Exit Sub
 Endif
 Mid$(Var3$,1,1)=Ucase$(Mid$(Var3$,1,1))
 Var=Instr(Var3$," ")
 If Var<Len(Var3$) Then
    Do While Var
       Mid$(Var3$,Var+1,1)=Ucase$(Mid$(Var3$,Var+1,1))
       Var=Instr(Var+1,Var3$," ")
    Loop
 Endif
ErrorResume24x4:
 Exit Sub
ErrorTrap24x4:
 Resume ErrorResume24x4
End Sub

' function to match substring with ?, * characters.
Sub InstrSUB2(VarX)
On Local Error Goto ErrorTrap24x6

' store string match variables
S2$ = Lcase$(Out2)
S3$ = Lcase$(Out3)

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

VarX = -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
            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
VarX = 0 ' no match
ErrorResume24x6:
 Exit Sub
ErrorTrap24x6:
 Resume ErrorResume24x6
End Sub
