REM Program: Hex Editor v7.7a, Module 4 of 6, PD 2007.
REM Author: Erik Jon Oredson AS. Csci
REM Release: 10/15/2007.
REM Status: Public Domain.
REM Email: eoredson@gmail.com
REM Urls: www.filegate.net www.simtel.net

REM Marker and Hilighting subroutines.
REM Hex/Dec calculator subroutine.
REM Formatting subroutine.
REM File menu box move subroutine.
REM File menu box file/dir override subroutine.
REM File exclusion subourtine.
REM Path override subroutines.
REM Specialized LINE INPUT subroutine.
REM LoadDirSpec menu subroutine.
REM LoadFileSpec menu subroutine.

' get include file.
REM $INCLUDE: 'hexedit.inc'

REM Marker routines:

SUB Marker(Var)
' declare error trap.
ON LOCAL ERROR GOTO Error.Routine1
IF Var = 1 THEN GOTO ListMarkerRange
IF Var = 2 THEN GOTO AddMarker
IF Var = 3 THEN GOTO AddSpecificMarker
IF Var = 4 THEN GOTO DeleteMarker
IF Var = 5 THEN GOTO DeleteSpecificMarker
IF Var = 6 THEN GOTO JumpPreviousMarker
IF Var = 7 THEN GOTO JumpCurrentMarker
IF Var = 8 THEN GOTO JumpNextMarker
IF Var = 9 THEN GOTO JumpSpecificMarker
IF Var = 10 THEN GOTO ListAllMarkers
IF Var = 11 THEN GOTO ListSpecificMarker
IF Var = 12 THEN GOTO ListMarkersOfValue
IF Var = 13 THEN GOTO DeleteMarkerRange
IF Var = 14 THEN GOTO DeleteAllMarkers
IF Var = 15 THEN GOTO DeleteMarkersOfValue
TopProgram:
EXIT SUB

AddMarker:
 TempPosition3 = FilePosition
 GOSUB AddMarkerValue
 EXIT SUB

AddMarkerValue:
 IF MarkerCount = 16777216! THEN
    StatusMessage = "Maximum markers are at 16,777,216."
    CALL DisplayStatus2
    RETURN
 END IF
 IF MarkerCount < 16777216! THEN
    MarkerCount = MarkerCount + 1!
    GET #7, MarkerCount, MarkerFile
    MarkerFile.Markers1(CurrentFile) = TempPosition3
    PUT #7, MarkerCount, MarkerFile
    CurrentMarker = MarkerCount
 END IF
 RETURN

AddSpecificMarker:
 Call ClearStatus
 PRINTf "Enter file position"
 ByteString$ = lineinput$(2, 24, 10)
 IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
    CALL CheckHexValue(ByteString$)
    IF ValidHexValue = False THEN
       EXIT SUB
    END IF
    TempPosition3 = HexValue
 ELSE
    CALL CheckASCIIValue(ByteString$)
    IF ValidASCIIValue = False THEN
       EXIT SUB
    END IF
    TempPosition3 = ASCIIValue3
 END IF
 IF TempPosition3 >= 1 AND TempPosition3 <= FileLength THEN
    GOSUB AddMarkerValue
    CALL LocateCursor2
    CALL DisplayFilename
    EXIT SUB
 END IF
 StatusMessage = "Invalid file position."
 CALL DisplayStatus2
 EXIT SUB

DeleteMarker:
 IF CurrentMarker >= 1! AND CurrentMarker <= MarkerCount THEN
    GET #7, CurrentMarker, MarkerFile
    MarkerFile.Markers1(CurrentFile) = False
    PUT #7, CurrentMarker, MarkerFile
 END IF
 EXIT SUB

DeleteSpecificMarker:
 Call ClearStatus
 PRINTf "Enter marker number"
 ByteString$ = lineinput$(2, 24, 10)
 IF LEN(ByteString$) THEN
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = "&" + ByteString$
    END IF
    MarkerPosition = INT(VAL(ByteString$))
    IF MarkerPosition >= 1! AND MarkerPosition <= MarkerCount THEN
       GET #7, MarkerPosition, MarkerFile
       MarkerFile.Markers1(CurrentFile) = False
       PUT #7, MarkerPosition, MarkerFile
       CALL LocateCursor2
       CALL DisplayFilename
       EXIT SUB
    END IF
 END IF
 StatusMessage = "Invalid marker number."
 CALL DisplayStatus2
 EXIT SUB

JumpPreviousMarker:
 NewMarker = CurrentMarker
 DO
    IF NewMarker <= 1! THEN
       EXIT SUB
    END IF
    NewMarker = NewMarker - 1!
    IF Markers#(NewMarker) > 0# THEN
       CurrentMarker = NewMarker
       EXIT DO
    END IF
 LOOP
 GOTO JumpCurrentMarker

JumpNextMarker:
 NewMarker = CurrentMarker
 DO
    IF NewMarker >= MarkerCount THEN
       EXIT SUB
    END IF
    NewMarker = NewMarker + 1!
    IF Markers#(NewMarker) > 0# THEN
       CurrentMarker = NewMarker
       EXIT DO
    END IF
 LOOP
 GOTO JumpCurrentMarker

JumpSpecificMarker:
 Call ClearStatus
 PRINTf "Enter marker number"
 ByteString$ = lineinput$(2, 24, 10)
 IF LEN(ByteString$) THEN
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = "&" + ByteString$
    END IF
    MarkerPosition = INT(VAL(ByteString$))
    IF MarkerPosition >= 1! AND MarkerPosition <= MarkerCount THEN
       IF Markers#(MarkerPosition) > 0# THEN
          CurrentMarker = MarkerPosition
          GOTO JumpCurrentMarker
       END IF
    END IF
 END IF
 StatusMessage = "Invalid marker number."
 CALL DisplayStatus2
 EXIT SUB

JumpCurrentMarker:
 IF CurrentMarker >= 1! AND CurrentMarker <= MarkerCount THEN
    TempPosition = Markers#(CurrentMarker)
    IF TempPosition >= 1 AND TempPosition <= FileLength THEN
       IF TempPosition <> FilePosition THEN
          CALL ClearPageByte
          LastPage = FilePage
          FilePosition = TempPosition
          CALL CalculatePosition1
          IF LastPage <> FilePage THEN
             CALL DisplayHexPage
          END IF
          CALL DisplayPageByte
       END IF
    END IF
 END IF
 CALL DisplayFilename
 EXIT SUB

ListAllMarkers:
 CALL DisplayScreen2
 COLORf White
 LOCATEf 2, 2, 0
 PRINTf "Marker list:"
 PageCount = False
 CountFlag = False
 Var2 = 2
 FOR MarkerPosition = 1! TO MarkerCount
    IF Markers#(MarkerPosition) > 0# THEN
       PageCount = PageCount + 1
       CountFlag = True
       COLORf Yellow
       Var2 = Var2 + 1
       LOCATEf Var2, 2, 0
       CALL FormatPosition2(2)
       IF PageCount >= 20 THEN
          PageCount = False
          LOCATEf 23, 2, 0
          CALL PressKey
          CALL DisplayScreen2
          COLORf White
          LOCATEf 2, 2, 0
          PRINTf "Marker list:"
          Var2 = 2
       END IF
    END IF
 NEXT
 IF CountFlag = False THEN
    COLORf Yellow
    LOCATEf 3, 2, 0
    PRINTf "No markers stored."
    LOCATEf 23, 2, 0
    CALL PressKey
 END IF
 IF PageCount > False THEN
    LOCATEf 23, 2, 0
    CALL PressKey
 END IF
 EXIT SUB

ListSpecificMarker:
 Call ClearStatus
 PRINTf "Enter marker number"
 ByteString$ = lineinput$(2, 24, 10)
 IF LEN(ByteString$) THEN
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = "&" + ByteString$
    END IF
    MarkerPosition = INT(VAL(ByteString$))
    IF MarkerPosition >= 1! AND MarkerPosition <= MarkerCount THEN
       Call ClearStatus
       CALL FormatPosition2(1)
       CALL LocateCursor2
       CALL PromptKey
       CALL DisplayFilename
       EXIT SUB
    END IF
 END IF
 StatusMessage = "Invalid marker number."
 CALL DisplayStatus2
 EXIT SUB

ListMarkersOfValue:
 Call ClearStatus
 PRINTf "Enter marker value"
 ByteString$ = lineinput$(2, 23, 10)
 IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
    CALL CheckHexValue(ByteString$)
    IF ValidHexValue = False THEN
       EXIT SUB
    END IF
    TempPosition1 = HexValue
 ELSE
    CALL CheckASCIIValue(ByteString$)
    IF ValidASCIIValue = False THEN
       EXIT SUB
    END IF
    TempPosition1 = ASCIIValue3
 END IF
 CALL DisplayScreen2
 COLORf White
 LOCATEf 2, 2, 0
 PRINTf "Marker list:"
 PageCount = False
 CountFlag = False
 Var2 = 2
 FOR MarkerPosition = 1! TO MarkerCount
    IF Markers#(MarkerPosition) = TempPosition1 THEN
       PageCount = PageCount + 1
       CountFlag = True
       COLORf Yellow
       Var2 = Var2 + 1
       LOCATEf Var2, 2, 0
       CALL FormatPosition2(2)
       IF PageCount >= 20 THEN
          PageCount = False
          LOCATEf 23, 2, 0
          CALL PressKey
          CALL DisplayScreen2
          COLORf White
          LOCATEf 2, 2, 0
          PRINTf "Marker list:"
          Var2 = 2
       END IF
    END IF
 NEXT
 IF CountFlag = False THEN
    COLORf Yellow
    LOCATEf 3, 2, 0
    PRINTf "No markers of value stored."
    LOCATEf 23, 2, 0
    CALL PressKey
 END IF
 IF PageCount > False THEN
    LOCATEf 23, 2, 0
    CALL PressKey
 END IF
 EXIT SUB

ListMarkerRange:
 Call ClearStatus
 PRINTf "Enter beginning marker number"
 ByteString$ = lineinput$(2, 34, 10)
 IF LEN(ByteString$) THEN
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = "&" + ByteString$
    END IF
    MarkerPosition1 = INT(VAL(ByteString$))
    IF MarkerPosition1 >= 1! AND MarkerPosition1 <= MarkerCount THEN
       Call ClearStatus
       PRINTf "Enter ending marker number"
       ByteString$ = lineinput$(2, 31, 10)
       IF LEN(ByteString$) THEN
          IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
             ByteString$ = "&" + ByteString$
          END IF
          MarkerPosition2 = INT(VAL(ByteString$))
          IF MarkerPosition2 >= 1! AND MarkerPosition2 <= MarkerCount THEN
             CALL DisplayScreen2
             COLORf White
             LOCATEf 2, 2, 0
             PRINTf "Marker list:"
             PageCount = False
             CountFlag = False
             Var2 = 2
             FOR MarkerPosition = MarkerPosition1 TO MarkerPosition2
                IF Markers#(MarkerPosition) > 0# THEN
                   PageCount = PageCount + 1
                   CountFlag = True
                   COLORf Yellow
                   Var2 = Var2 + 1
                   LOCATEf Var2, 2, 0
                   CALL FormatPosition2(2)
                   IF PageCount >= 20 THEN
                      PageCount = False
                      LOCATEf 23, 2, 0
                      CALL PressKey
                      CALL DisplayScreen2
                      COLORf White
                      LOCATEf 2, 2, 0
                      PRINTf "Marker list:"
                      Var2 = 2
                   END IF
                END IF
             NEXT
             IF CountFlag = False THEN
                COLORf Yellow
                LOCATEf 3, 2, 0
                PRINTf "No markers stored."
                LOCATEf 23, 2, 0
                CALL PressKey
             END IF
             IF PageCount > False THEN
                LOCATEf 23, 2, 0
                CALL PressKey
             END IF
             ValidFunction = True
             EXIT SUB
          END IF
       END IF
    END IF
 END IF
 ValidFunction = False
 StatusMessage = "Invalid marker range."
 CALL DisplayStatus2
 EXIT SUB

DeleteMarkerRange:
 Call ClearStatus
 PRINTf "Enter beginning marker number"
 ByteString$ = lineinput$(2, 34, 10)
 IF LEN(ByteString$) THEN
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       ByteString$ = "&" + ByteString$
    END IF
    MarkerPosition1 = INT(VAL(ByteString$))
    IF MarkerPosition1 >= 1! AND MarkerPosition1 <= MarkerCount THEN
       Call ClearStatus
       PRINTf "Enter ending marker number"
       ByteString$ = lineinput$(2, 31, 10)
       IF LEN(ByteString$) THEN
          IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
             ByteString$ = "&" + ByteString$
          END IF
          MarkerPosition2 = INT(VAL(ByteString$))
          IF MarkerPosition2 >= 1! AND MarkerPosition2 <= MarkerCount THEN
             FOR MarkerPosition = MarkerPosition1 TO MarkerPosition2
                GET #7, MarkerPosition, MarkerFile
                MarkerFile.Markers1(CurrentFile) = False
                PUT #7, MarkerPosition, MarkerFile
             NEXT
             StatusMessage = "Range of markers deleted."
             CALL DisplayStatus2
             EXIT SUB
          END IF
       END IF
    END IF
 END IF
 StatusMessage = "Invalid marker range."
 CALL DisplayStatus2
 EXIT SUB

DeleteAllMarkers:
 CurrentMarker = False
 MarkerCount = False
 StatusMessage = "All markers deleted."
 CALL DisplayStatus2
 EXIT SUB

DeleteMarkersOfValue:
 Call ClearStatus
 PRINTf "Enter marker value"
 ByteString$ = lineinput$(2, 23, 10)
 IF LEN(ByteString$) THEN
    IF UCASE$(LEFT$(ByteString$, 1)) = "H" THEN
       CALL CheckHexValue(ByteString$)
       IF ValidHexValue = False THEN
          EXIT SUB
       END IF
       TempPosition1 = HexValue
    ELSE
       CALL CheckASCIIValue(ByteString$)
       IF ValidASCIIValue = False THEN
          EXIT SUB
       END IF
       TempPosition1 = ASCIIValue3
    END IF
    FOR MarkerPosition = 1! TO MarkerCount
       IF Markers#(MarkerPosition) = TempPosition1 THEN
          GET #7, MarkerPosition, MarkerFile
          MarkerFile.Markers1(CurrentFile) = False
          PUT #7, MarkerPosition, MarkerFile
       END IF
    NEXT
 END IF
 StatusMessage = "All markers of value deleted."
 CALL DisplayStatus2
 EXIT SUB

' critical error trap.
Error.Routine1:
 ErrorTrap = ERR
 IF ScreenDrawn THEN
    CLS
 END IF
 ScreenDrawn = False
 COLORf White
 LOCATEf Csrlin, 1, 0
 PRINTf "Hex Editor Marker " + Version + " " + Release + " critical error trap:"
 COLORf Yellow
 LOCATEf Csrlin+1, 1, 0
 CALL DisplayCriticalError(ErrorTrap)
 ' display error prompt.
 COLORf Green
 LOCATEf Csrlin+1, 1, 0
 PRINTf "Press R(etry), C(ontinue), Q(uit):"
 LOCATEf Csrlin, 35, 1
 ' get keypress.
 DO
    ErrorRespond$ = Nul
    DO
       ErrorRespond$ = INKEY$
       IF LEN(ErrorRespond$) THEN
          EXIT DO
       END IF
       R = ReleaseTime
    LOOP
    ' parse key.
    SELECT CASE LCASE$(ErrorRespond$)
    CASE "r"
       PRINTf "r"
       LOCATEf Csrlin+1, 1, 0
       RESUME TopProgram
    CASE "c"
       PRINTf "c"
       LOCATEf Csrlin+1, 1, 0
       RESUME TopProgram
    CASE "q"
       PRINTf "q"
       LOCATEf Csrlin+1, 1, 0
       RESUME TopProgram
    END SELECT
 LOOP
END SUB

' process left mouse button drag. also includes shift-<key> hilighting.
SUB MouseButton1Drag(Var)
 ON LOCAL ERROR GOTO Error.Routine2
 IF Var = 0 THEN GOTO DragMouse
 IF Var = 1 THEN GOTO ShiftLeftKey
 IF Var = 2 THEN GOTO ShiftRightKey
 IF Var = 3 THEN GOTO ShiftUpKey
 IF Var = 4 THEN GOTO ShiftDownKey
 IF Var = 5 THEN GOTO ShiftPageUpKey
 IF Var = 6 THEN GOTO ShiftPageDownKey
 IF Var = 7 THEN GOTO ShiftEndKey
 IF Var = 8 THEN GOTO ShiftHomeKey
 IF Var = 9 THEN GOTO DragMousePageUp
 IF Var = 10 THEN GOTO DragMousePageDown
 EXIT SUB

REM Hilighting key routines:

' move hilight area left.
ShiftLeftKey:
 IF FilePosition = 1 THEN
    GOSUB StoreHilight
 ELSE
    IF PageColumn - 1 >= 1 THEN
       GOSUB StoreHilight
       IF CopyPositionEnd - 1 >= CopyPositionPivot THEN
          CALL ClearHilightByte
          PageColumn = PageColumn - 1
          FilePosition = FilePosition - 1
          CopyPositionEnd = CopyPositionEnd - 1
       ELSE
          IF CopyPositionStart - 1 <= CopyPositionPivot THEN
             PageColumn = PageColumn - 1
             FilePosition = FilePosition - 1
             CopyPositionStart = CopyPositionStart - 1
          ELSE
             EXIT SUB
          END IF
       END IF
    END IF
 END IF
 CALL DisplayHilightByte
 CALL LocateHilightCursor
 EXIT SUB

' move hilight area right.
ShiftRightKey:
 IF FilePosition = FileLength THEN
    GOSUB StoreHilight
 ELSE
    IF PageColumn = 20 THEN
       GOSUB StoreHilight
    ELSE
       IF PageColumn + 1 <= 20 THEN
          IF FilePosition + 1 <= FileLength THEN
             IF CopyPositionStart = DFalse THEN
                GOSUB StoreHilight
             ELSE
                IF CopyPositionStart + 1 <= CopyPositionPivot THEN
                   CALL ClearHilightByte
                   PageColumn = PageColumn + 1
                   FilePosition = FilePosition + 1
                   CopyPositionStart = CopyPositionStart + 1
                ELSE
                   IF CopyPositionEnd + 1 >= CopyPositionPivot THEN
                      PageColumn = PageColumn + 1
                      FilePosition = FilePosition + 1
                      CopyPositionEnd = CopyPositionEnd + 1
                   ELSE
                      EXIT SUB
                   END IF
                END IF
             END IF
          END IF
       END IF
    END IF
 END IF
 CALL DisplayHilightByte
 CALL LocateHilightCursor
 EXIT SUB

' move hilight area up.
ShiftUpKey:
 IF PageRow - 1 >= 1 THEN
    GOSUB StoreHilight
    IF CopyPositionEnd > CopyPositionPivot THEN
       IF CopyPositionEnd - 20 >= CopyPositionPivot THEN
          Temp# = FilePosition
          Temp2# = PageRow
          FOR FilePosition = CopyPositionEnd TO CopyPositionEnd - 20 STEP -1
             CALL CalculatePosition1
             CALL ClearHilightByte
          NEXT
          PageRow = Temp2# - 1
          FilePosition = Temp# - 20
          CopyPositionEnd = CopyPositionEnd - 20
          CALL CalculatePosition1
          CALL DisplayHilightByte
          CALL LocateHilightCursor
       ELSE
          IF CopyPositionEnd - 20 < CopyPositionPivot THEN
             Temp# = FilePosition
             Temp2# = PageRow
             FOR FilePosition = CopyPositionPivot TO CopyPositionEnd
                CALL CalculatePosition1
                CALL ClearHilightByte
             NEXT
             CopyPositionStart = CopyPositionEnd - 20
             CopyPositionEnd = CopyPositionPivot
             FOR FilePosition = CopyPositionStart TO CopyPositionPivot 
                CALL CalculatePosition1
                CALL DisplayHilightByte
             NEXT
             PageRow = Temp2# - 1
             FilePosition = Temp# - 20
             CALL CalculatePosition1
             CALL DisplayHilightByte
             CALL LocateHilightCursor
          END IF
       END IF
    ELSE
       Temp# = FilePosition
       Temp2# = PageRow
       FOR FilePosition = CopyPositionStart - 20 TO CopyPositionStart
          CALL CalculatePosition1
          CALL DisplayHilightByte
       NEXT
       CopyPositionStart = CopyPositionStart - 20
       PageRow = Temp2# - 1
       FilePosition = Temp# - 20
       CALL CalculatePosition1
       CALL DisplayHilightByte
       CALL LocateHilightCursor
    END IF
 END IF
 EXIT SUB

' move hilight area down.
ShiftDownKey:
 IF PageRow + 1 <= 16 THEN
    IF FilePosition + 20 <= FileLength THEN
       GOSUB StoreHilight
       IF CopyPositionStart < CopyPositionPivot THEN
          Temp# = FilePosition
          Temp2# = PageRow
          IF CopyPositionStart + 20 < CopyPositionPivot THEN
             FOR FilePosition = CopyPositionStart TO CopyPositionStart + 20
                CALL CalculatePosition1
                CALL ClearHilightByte
             NEXT
             PageRow = Temp2# + 1
             FilePosition = Temp# + 20
             CopyPositionStart = CopyPositionStart + 20
             CALL DisplayHilightByte
             CALL LocateHilightCursor
          ELSE
             FOR FilePosition = CopyPositionStart TO CopyPositionPivot
                CALL CalculatePosition1
                CALL ClearHilightByte
             NEXT
             CopyPositionStart = CopyPositionPivot
             CopyPositionEnd = Temp# + 20
             FOR FilePosition = CopyPositionPivot TO CopyPositionEnd
                CALL CalculatePosition1
                CALL DisplayHilightByte
             NEXT
             PageRow = Temp2# + 1
             FilePosition = CopyPositionEnd
             CALL DisplayHilightByte
             CALL LocateHilightCursor
          END IF
       ELSE
          IF CopyPositionEnd >= CopyPositionPivot THEN
             Temp# = FilePosition
             Temp2# = PageRow
             FOR FilePosition = CopyPositionEnd TO CopyPositionEnd + 20
                CALL CalculatePosition1
                CALL DisplayHilightByte
             NEXT
             PageRow = Temp2# + 1
             FilePosition = Temp# + 20
             CopyPositionEnd = CopyPositionEnd + 20
             CALL DisplayHilightByte
             CALL LocateHilightCursor
          END IF
       END IF
    END IF
 END IF
 EXIT SUB

' move hilight area up one page.
ShiftPageUpKey:
 ' check there is a page to move up.
 IF FilePosition - 320 >= 1 THEN
    GOSUB StoreHilight
    ' move copypositionstart up one page.
    IF CopyPositionEnd = CopyPositionPivot THEN
       FilePage = FilePage - 1
       FilePosition = FilePosition - 320
       CopyPositionStart = CopyPositionStart - 320
    ELSE
       IF CopyPositionEnd > CopyPositionPivot THEN
          ' move copypositionend up one page before pivot.
          IF CopyPositionEnd - 320 < CopyPositionPivot THEN
             FilePage = FilePage - 1
             FilePosition = FilePosition - 320
             CopyPositionStart = FilePosition
             CopyPositionEnd = CopyPositionPivot
          ELSE
             ' move copypositionend up one page.
             IF CopyPositionEnd - 320 >= CopyPositionPivot THEN
                FilePage = FilePage - 1
                FilePosition = FilePosition - 320
                CopyPositionEnd = CopyPositionEnd - 320
             END IF
          END IF
       END IF
    END IF
 ELSE
    ' check there is a first position on the first page to move up to.
    IF FilePosition > 1 THEN
       GOSUB StoreHilight
       PageRow = 1
       PageColumn = 1
       FilePosition = 1
       CopyPositionStart = 1
       CopyPositionEnd = CopyPositionPivot
    ELSE
       EXIT SUB
    END IF
 END IF
 CALL CalculatePosition1
 CALL DisplayHexPage
 Reset1 = False
 CALL ResetByte
 CALL DisplayHilightByte
 CALL LocateHilightCursor
 EXIT SUB

' move hilight area down one page.
ShiftPageDownKey:
 ' check there is a page to move down.
 IF FilePosition + 320 <= FileLength THEN
    GOSUB StoreHilight
    ' move copypositionend down one page.
    IF CopyPositionStart = CopyPositionPivot THEN
       FilePage = FilePage + 1
       FilePosition = FilePosition + 320
       CopyPositionEnd = CopyPositionEnd + 320
    ELSE
       IF CopyPositionStart < CopyPositionPivot THEN
          ' move copypositionstart down one page after pivot.
          IF CopyPositionStart + 320 <= CopyPositionPivot THEN
             FilePage = FilePage + 1
             FilePosition = FilePosition + 320
             CopyPositionStart = FilePosition
             CopyPositionEnd = CopyPositionPivot
          ELSE
             ' move copypositionstart down one page.
             IF CopyPositionStart + 320 > CopyPositionPivot THEN
                FilePage = FilePage + 1
                FilePosition = FilePosition + 320
                CopyPositionStart = CopyPositionPivot
                CopyPositionEnd = FilePosition
             END IF
          END IF
       END IF
    END IF
 ELSE
    ' check there is a last position on the last page to move down to.
    IF FilePosition < FileLength THEN
       GOSUB StoreHilight
       FilePosition = FileLength
       CopyPositionStart = CopyPositionPivot
       CopyPositionEnd = FileLength
    ELSE
       EXIT SUB
    END IF
 END IF
 CALL CalculatePosition1
 CALL DisplayHexPage
 Reset1 = False
 CALL ResetByte
 CALL DisplayHilightByte
 CALL LocateHilightCursor
 EXIT SUB

' move hilight area to end of line.
ShiftEndKey:
 ' set to column 20
 IF PageColumn < 20 THEN
    ' check column 20 is positioned before end of file
    GOSUB StoreHilight
    IF FilePosition - PageColumn + 20 <= FileLength THEN
       Temp# = FilePosition
       Temp2# = PageColumn
       TempPosition3 = CopyPositionPivot
       CALL CalculatePosition2
       IF PageRow = PageRow2 THEN
          IF Temp# - Temp2# + 20 >= CopyPositionPivot THEN
             FOR FilePosition = Temp# - Temp2# + 1 TO CopyPositionPivot
                CALL CalculatePosition1
                CALL ClearHilightByte
             NEXT
             FOR FilePosition = CopyPositionPivot TO Temp# - Temp2# + 20
                CALL CalculatePosition1
                CALL DisplayHilightByte
             NEXT
          END IF
          CopyPositionEnd = Temp# - Temp2# + 20
          CopyPositionStart = CopyPositionPivot
       ELSE
          IF Temp# - Temp2# + 20 >= CopyPositionPivot THEN
             FOR FilePosition = Temp# TO Temp# - Temp2# + 20
                CALL CalculatePosition1
                CALL DisplayHilightByte
             NEXT
             CopyPositionEnd = Temp# - Temp2# + 20
             CopyPositionStart = CopyPositionPivot
          ELSE
             FOR FilePosition = Temp# TO Temp# - Temp2# + 20
                CALL CalculatePosition1
                CALL ClearHilightByte
             NEXT
             CopyPositionStart = Temp# - Temp2# + 1
             CopyPositionEnd = CopyPositionPivot
          END IF
       END IF
       FilePosition = Temp# - Temp2# + 20
       PageColumn = 20
       CALL DisplayHilightByte
       CALL LocateHilightCursor
    ELSE
       ' last position is end of file.
       Temp# = FilePosition
       Temp2# = PageColumn
       TempPosition3 = CopyPositionPivot
       CALL CalculatePosition2
       IF PageRow = PageRow2 THEN
          IF FileLength >= CopyPositionPivot THEN
             FOR FilePosition = Temp# - Temp2# + 1 TO CopyPositionPivot
                CALL CalculatePosition1
                CALL ClearHilightByte
             NEXT
             FOR FilePosition = CopyPositionPivot TO FileLength
                CALL CalculatePosition1
                CALL DisplayHilightByte
             NEXT
          END IF
       END IF
       FilePosition = FileLength
       CopyPositionEnd = FileLength
       CopyPositionStart = CopyPositionPivot
       CALL CalculatePosition1
       CALL DisplayHilightByte
       CALL LocateHilightCursor
    END IF
 END IF
 EXIT SUB

' move hilight area to start of line.
ShiftHomeKey:
 ' set to column 1
 IF PageColumn > 1 THEN
    GOSUB StoreHilight
    Temp# = FilePosition
    Temp2# = PageColumn
    TempPosition3 = CopyPositionPivot
    CALL CalculatePosition2
    IF PageRow = PageRow2 THEN
       IF Temp# - Temp2# + 1 <= CopyPositionPivot THEN
          FOR FilePosition = CopyPositionPivot TO Temp#
             CALL CalculatePosition1
             CALL ClearHilightByte
          NEXT
          FOR FilePosition = Temp# - Temp2# + 1 TO CopyPositionPivot
             CALL CalculatePosition1
             CALL DisplayHilightByte
          NEXT
       END IF
       CopyPositionStart = Temp# - Temp2# + 1
       CopyPositionEnd = CopyPositionPivot
    ELSE
       IF Temp# - Temp2# + 1 <= CopyPositionPivot THEN
          FOR FilePosition = Temp# - Temp2# + 1 TO Temp#
             CALL CalculatePosition1
             CALL DisplayHilightByte
          NEXT
          CopyPositionStart = Temp# - Temp2# + 1
          CopyPositionEnd = CopyPositionPivot
       ELSE
          FOR FilePosition = Temp# - Temp2# + 1 TO Temp#
             CALL CalculatePosition1
             CALL ClearHilightByte
          NEXT
          CopyPositionEnd = Temp# - Temp2# + 1
          CopyPositionStart = CopyPositionPivot
       END IF
    END IF
    FilePosition = Temp# - Temp2# + 1
    PageColumn = 1
    CALL DisplayHilightByte
    CALL LocateHilightCursor
 END IF
 EXIT SUB

' store inital hilight start
StoreHilight:
 IF CopyPositionStart = DFalse THEN
    CopyPositionStart = FilePosition
    CopyPositionPivot = FilePosition
    CopyPositionEnd = FilePosition
 END IF
 RETURN

REM Mouse hilighting routines:

DragMouse:
 ' check mouse position in screen editing area.
 IF Mouse.Row > 3 AND Mouse.Row < 20 THEN
    IF Mouse.Column >= 6 AND Mouse.Column <= 49 THEN
       ' check initial click is inside editing area.
       IF CopyStart = DFalse THEN
          EXIT SUB
       END IF
       ' check screen character.
       VarX = SCREEN(Mouse.Row, Mouse.Column)
       ' check space.
       IF VarX = 32 THEN
          EXIT SUB
       END IF
       ' check new position.
       IF VarX <> 32 THEN
          ' calculate new position.
          NewPosition = CalculatePosition3
          ' initialize copy area.
          IF CopyPositionStart = DFalse THEN
             CopyPositionStart = CopyStart
             CopyPositionPivot = CopyStart
             CopyPositionEnd = CopyStart
          END IF
          CALL HMouse
          ' check initial click area.
          IF NewPosition = CopyPositionPivot THEN
             FOR TempPosition3 = CopyPositionStart TO CopyPositionEnd
                CALL CalculatePosition2
                IF FilePage2 = FilePage THEN
                   FilePosition = TempPosition3
                   CALL CalculatePosition1
                   COLORf White
                   CALL BytePrint
                END IF
             NEXT
             CopyPositionStart = NewPosition
             CopyPositionEnd = NewPosition
             FilePosition = CopyPositionPivot
             CALL CalculatePosition1
             COLORf2 0, 7
             CALL BytePrint
             COLORf2 7, 0
          END IF
          ' check backwards wrap.
          IF NewPosition < CopyPositionPivot THEN
             IF CopyPositionEnd > CopyPositionPivot THEN
                FOR TempPosition3 = CopyPositionPivot TO CopyPositionEnd
                   CALL CalculatePosition2
                   IF FilePage2 = FilePage THEN
                      FilePosition = TempPosition3
                      CALL CalculatePosition1
                      COLORf White
                      CALL BytePrint
                   END IF
                NEXT
             END IF
             IF NewPosition > CopyPositionStart THEN
                FOR TempPosition3 = CopyPositionStart TO NewPosition
                   CALL CalculatePosition2
                   IF FilePage2 = FilePage THEN
                      FilePosition = TempPosition3
                      CALL CalculatePosition1
                      COLORf White
                      CALL BytePrint
                   END IF
                NEXT
             END IF
             CopyPositionStart = NewPosition
             CopyPositionEnd = CopyPositionPivot
             FOR TempPosition3 = CopyPositionStart TO CopyPositionPivot
                CALL CalculatePosition2
                IF FilePage2 = FilePage THEN
                   FilePosition = TempPosition3
                   CALL CalculatePosition1
                   COLORf2 0, 7
                   CALL BytePrint
                END IF
             NEXT
             COLORf2 7, 0
          END IF
          ' check forwards wrap.
          IF NewPosition > CopyPositionPivot THEN
             IF CopyPositionStart < CopyPositionPivot THEN
                FOR TempPosition3 = CopyPositionStart TO CopyPositionPivot
                   CALL CalculatePosition2
                   IF FilePage2 = FilePage THEN
                      FilePosition = TempPosition3
                      CALL CalculatePosition1
                      COLORf White
                      CALL BytePrint
                   END IF
                NEXT
             END IF
             IF NewPosition < CopyPositionEnd THEN
                FOR TempPosition3 = NewPosition TO CopyPositionEnd
                   CALL CalculatePosition2
                   IF FilePage2 = FilePage THEN
                      FilePosition = TempPosition3
                      CALL CalculatePosition1
                      COLORf White
                      CALL BytePrint
                   END IF
                NEXT
             END IF
             CopyPositionStart = CopyPositionPivot
             CopyPositionEnd = NewPosition
             FOR TempPosition3 = CopyPositionPivot TO CopyPositionEnd
                CALL CalculatePosition2
                IF FilePage2 = FilePage THEN
                   FilePosition = TempPosition3
                   CALL CalculatePosition1
                   COLORf2 0, 7
                   CALL BytePrint
                END IF
             NEXT
             COLORf2 7, 0
          END IF
          ' get new position and locate there.
          FilePosition = NewPosition
          CALL CalculatePosition1
          COLORf2 0, 7
          CALL BytePrint
          COLORf2 7, 0
          CALL DisplayPosition
          CALL LocateCursor
          CALL SMouse
       END IF
    END IF
 END IF
TopProgram1:
 EXIT SUB

REM this variable must be set before a call to dragmouse functions:
'   store mouse timing.
' MouseTime = 1!

' move hilight area up one page.
DragMousePageUp:
 ' check initial click is inside editing area.
 IF CopyStart = DFalse THEN
    EXIT SUB
 END IF
 ' check scrolling interval.
 IF Time1 = SFalse THEN
    Time1 = Timer
 END IF
 Gosub TimeElapsed
 If VarT = SFalse Then
    EXIT SUB
 END IF
 Time1 = Timer
 ' check drag variables.
 IF CopyPositionEnd = DFalse THEN
    CopyPositionEnd = CopyStart
 END IF
 IF CopyPositionPivot = DFalse THEN
    CopyPositionPivot = CopyPositionEnd
 END IF
 IF CopyPositionStart = DFalse THEN
    CopyPositionStart = (Filepage - 1) * 320 + 1
 END IF
 ' check there is a page to move up.
 IF FilePage - 1 >= 1 THEN
    ' move copypositionstart up one page.
    FilePage = FilePage - 1
    FilePosition = (FilePage - 1) * 320 + 1
    CopyPositionStart = FilePosition
    CALL CalculatePosition1
    CALL DisplayHexPage
    Reset1 = False
    CALL ResetByte
    CALL DisplayHilightByte
    CALL LocateHilightCursor
 END IF
 EXIT SUB

' move hilight area down one page.
DragMousePageDown:
 ' check initial click is inside editing area.
 IF CopyStart = DFalse THEN
    EXIT SUB
 END IF
 ' check scrolling interval.
 IF Time1 = SFalse THEN
    Time1 = Timer
 END IF
 Gosub TimeElapsed
 If VarT = SFalse Then
    EXIT SUB
 END IF
 Time1 = Timer
 ' check drag variables.
 IF CopyPositionStart = DFalse THEN
    CopyPositionStart = CopyStart
 END IF
 IF CopyPositionPivot = DFalse THEN
    CopyPositionPivot = CopyPositionStart
 END IF
 IF CopyPositionEnd = DFalse THEN
    CopyPositionEnd = (Filepage - 1) * 320 + 320
 END IF
 ' check there is a page to move down.
 FilePage2 = INT((FileLength - 1) / 320) + 1
 IF FilePage + 1 <= FilePage2 THEN
    ' move copypositionend down one page.
    FilePage = FilePage + 1
    FilePosition = (FilePage - 1) * 320 + 320
    IF FilePosition > FileLength THEN
       FilePosition = FileLength
    END IF
    CopyPositionEnd = FilePosition
    CALL CalculatePosition1
    CALL DisplayHexPage
    Reset1 = False
    CALL ResetByte
    CALL DisplayHilightByte
    CALL LocateHilightCursor
 END IF
 EXIT SUB

' calculate elapsed time.
TimeElapsed:
 Elapsed.Time!=Timer-Time1
 If Elapsed.Time!<SFalse Then
    Elapsed.Time!=Elapsed.Time!+86400!
 Endif
 If Elapsed.Time!>MouseTime Then
    VarT=True
 Else
    VarT=False
 Endif
 Return

' critical error trap.
Error.Routine2:
 ErrorTrap = ERR
 IF ScreenDrawn THEN
    CLS
 END IF
 ScreenDrawn = False
 COLORf White
 LOCATEf Csrlin, 1, 0
 PRINTf "Hex Editor MouseButton1Drag " + Version + " " + Release + " critical error trap:"
 COLORf Yellow
 LOCATEf Csrlin+1, 1, 0
 CALL DisplayCriticalError(ErrorTrap)
 ' display error prompt.
 COLORf Green
 LOCATEf Csrlin+1, 1, 0
 PRINTf "Press R(etry), C(ontinue), Q(uit):"
 LOCATEf Csrlin, 35, 1
 ' get keypress.
 DO
    ErrorRespond$ = Nul
    DO
       ErrorRespond$ = INKEY$
       IF LEN(ErrorRespond$) THEN
          EXIT DO
       END IF
       R = ReleaseTime
    LOOP
    ' parse key.
    SELECT CASE LCASE$(ErrorRespond$)
    CASE "r"
       PRINTf "r"
       LOCATEf Csrlin+1, 1, 0
       RESUME TopProgram1
    CASE "c"
       PRINTf "c"
       LOCATEf Csrlin+1, 1, 0
       RESUME TopProgram1
    CASE "q"
       PRINTf "q"
       LOCATEf Csrlin+1, 1, 0
       RESUME TopProgram1
    END SELECT
 LOOP
END SUB

' realtime hex/dec calculator.
SUB HexCalc
 ON LOCAL ERROR GOTO Error.Routine3
 GOSUB MakeBox ' create edit box.
 Ins = -1 ' reset insert flag.
 CalcBox = 1 ' reset to left box.
 Box$ = "0" ' current box
 Box1$ = "0" ' left box string
 Box2$ = "0" ' right box string
 GOSUB SelectBox2 ' display box 2.
 GOSUB SelectBox1 ' display box 1.
 DO ' calculator loop
    DO ' keystroke loop
       Var$ = INKEY$ ' get keystroke
       IF LEN(Var$) THEN ' check keystroke
          EXIT DO
       END IF
       R = ReleaseTime ' release mouse timing
    LOOP
    SELECT CASE LEN(Var$)
    CASE 1 ' single ascii key
       SELECT CASE ASC(Var$)
       CASE 8 ' backspace
          IF Ycoor1 - Column3 > False THEN
             IF Ins THEN
                Box$ = LEFT$(Box$, Ycoor1 - Column3 - 1) + MID$(Box$, Ycoor1 - Column3 + 1)
                Ycoor1 = Ycoor1 - 1
                LOCATEf Xcoor1, Ycoor1, 1
                Var$ = MID$(Box$, Ycoor1 - Column3 + 1) + " "
                COLORf2 15, 0
                PRINTf Var$
                LOCATEf Xcoor1, Ycoor1, 1
                GOSUB PrintBoxes
             ELSE ' move left
                Ycoor1 = Ycoor1 - 1
                LOCATE Xcoor1, Ycoor1, 1
             END IF
          END IF
       CASE 9 ' tab
          IF CalcBox = 1 THEN
             CalcBox = 2
             GOSUB SelectBox2
             GOSUB PrintBoxes
          END IF
       CASE 27 ' escape
          COLORf2 7, 0
          EXIT SUB
       CASE ELSE ' keystroke
          VarX = False
          TempX$ = UCASE$(Var$)
          SELECT CASE TempX$
          CASE "0" TO "9"
             VarX = True
          CASE "A" TO "F"
             IF CalcBox = 1 THEN
                VarX = True
             END IF
          END SELECT
          IF VarX THEN
             IF Ycoor1 - Column3 + 1 <= LineLength THEN
                ' insert digit
                IF Ins THEN
                   Box$ = LEFT$(Box$, Ycoor1 - Column3) + TempX$ + MID$(Box$, Ycoor1 - Column3 + 1)
                   Box$ = LEFT$(Box$, LineLength)
                   LOCATEf Xcoor1, Ycoor1, 1
                   Var$ = MID$(Box$, Ycoor1 - Column3 + 1)
                   COLORf2 15, 0
                   PRINTf Var$
                   Ycoor1 = Ycoor1 + 1
                   LOCATEf Xcoor1, Ycoor1, 1
                   GOSUB PrintBoxes
                ELSE ' overstrike char
                   IF Ycoor1 - Column3 + 1 > LEN(Box$) THEN
                      Box$ = Box$ + TempX$ ' append box
                   ELSE
                      MID$(Box$, Ycoor1 - Column3 + 1, 1) = TempX$ ' replace
                   END IF
                   LOCATEf Xcoor1, Ycoor1, 1
                   COLORf2 15, 0
                   PRINTf TempX$
                   Ycoor1 = Ycoor1 + 1
                   LOCATEf Xcoor1, Ycoor1, 1
                   GOSUB PrintBoxes
                END IF
             END IF
          END IF
       END SELECT
    CASE 2 ' extended key
       SELECT CASE ASC(RIGHT$(Var$,1))
       CASE 15 ' shift-tab
          IF CalcBox = 2 THEN
             CalcBox = 1
             GOSUB SelectBox1
             GOSUB PrintBoxes
          END IF
       CASE 71 ' home
          Ycoor1 = Column3
       CASE 79 ' end
          Ycoor1 = LEN(Box$) + Column3
       CASE 77 ' right
          IF Ycoor1 - Column3 + 1 <= LEN(Box$) THEN
             Ycoor1 = Ycoor1 + 1
          END IF
       CASE 75 ' left
          IF Ycoor1 - Column3 > False THEN
             Ycoor1 = Ycoor1 - 1
          END IF
       CASE 82 ' insert
          Ins = NOT Ins
          COLORf2 14, 1
          IF Ins THEN
             LOCATEf 4, 30, 1
             PRINTf "<ins>"
          ELSE
             LOCATEf 4, 30, 1
             PRINTf STRING$(5, 205)
          END IF
          COLORf2 15, 0
       CASE 83 ' delete
          IF Ycoor1 - Column3 + 1 <= LEN(Box$) THEN
             Box$ = LEFT$(Box$, Ycoor1 - Column3) + MID$(Box$, Ycoor1 - Column3 + 2)
             LOCATEf Xcoor1, Ycoor1, 1
             Var$ = MID$(Box$, Ycoor1 - Column3 + 1) + " "
             COLORf2 15, 0
             PRINTf Var$
             LOCATEf Xcoor1, Ycoor1, 1
             GOSUB PrintBoxes
          END IF
       CASE 119 ' control-home
          Ycoor1 = Column3
       CASE 117 ' control-end
          Ycoor1 = LEN(Box$) + Column3
       END SELECT
       LOCATEf Xcoor1, Ycoor1, 1
    END SELECT
 LOOP
 EXIT SUB

' create edit box.
MakeBox:
 COLORf2 14, 1
 LOCATEf 4, 6, 1
 Var$ = " " + CHR$(201) + STRING$(1, 205) + "<esc>=Quit" + STRING$(11, 205) + "<ins>" + CHR$(187) + " "
 PRINTf Var$
 LOCATEf 5, 6, 1
 Var$ = " " + CHR$(186) + SPACE$(9) + "Hex" + SPACE$(11) + "Dec " + CHR$(186) + " "
 PRINTf Var$
 LOCATEf 6, 6, 1
 Var$ = " " + CHR$(200) + STRING$(1, 205) + "<tab>/<shift-tab>=switch" + STRING$(2, 205) + CHR$(188) + " "
 PRINTf Var$
 RETURN

' move to box 1 (left box).
SelectBox1:
 Box2$ = Box$ ' store right box.
 Box$ = Box1$ ' reset to left box.

 ' reset editing parameters.
 Xcoor1 = 5
 Ycoor1 = 9
 Column3 = 9
 LineLength = 8

 ' display left box.
 COLORf2 15, 0
 LOCATEf Xcoor1, Ycoor1, 1
 PRINTf SPACE$(8)
 LOCATEf Xcoor1, Ycoor1, 1
 PRINTf Box$
 Ycoor1 = Ycoor1 + LEN(Box$)
 LOCATEf Xcoor1, Ycoor1, 1
 RETURN

' move to box 2 (right box).
SelectBox2:
 Box1$ = Box$ ' store left box.
 Box$ = Box2$ ' reset to right box.

 ' reset editing parameters.
 Xcoor1 = 5
 Ycoor1 = 21
 Column3 = 21
 LineLength = 10

 ' display right box.
 COLORf2 15, 0
 LOCATEf Xcoor1, Ycoor1, 1
 PRINTf SPACE$(10)
 LOCATEf Xcoor1, Ycoor1, 1
 PRINTf Box$
 Ycoor1 = Ycoor1 + LEN(Box$)
 LOCATEf Xcoor1, Ycoor1, 1
 RETURN

' calculates value of current box,
' displays result in calculated form
' in opposite box.
PrintBoxes:
 IF CalcBox = 1 THEN ' is in hex box.
    ' strip leading zeroes for signed bit conversion.
    TempBox$ = Box$
    DO
       IF LEFT$(TempBox$, 1) = "0" THEN
          TempBox$ = MID$(TempBox$, 2)
       ELSE
          EXIT DO
       END IF
    LOOP
    Value# = VAL("&H" + TempBox$) ' retreive decimal value.
    IF Value# < 0# THEN ' check twos-complement from signed value.
       IF LEN(TempBox$) = 8 THEN ' 8000 0000 to FFFF FFFF
          Value# = Value# + 4294967296#
       ELSE
          IF LEN(TempBox$) = 4 THEN ' 8000 to FFFF
             Value# = Value# + 65536
          END IF
       END IF
    END IF
    ' display decimal value.
    Box2$ = MID$(STR$(Value#), 2)
    COLORf2 15, 0
    LOCATEf 5, 21, 1
    PRINTf SPACE$(10)
    LOCATEf 5, 21, 1
    PRINTf Box2$
 END IF
 IF CalcBox = 2 THEN ' is in decimal box.
    Value# = VAL(Box$) ' retreive hex value.
    IF Value# >= 4294967296# THEN ' check overflow.
       COLORf2 15, 0
       LOCATEf 5, 9, 1
       Var$ = "overflow"
       PRINTf Var$
       Box1$ = "0"
    ELSE
       IF Value# > 2147483647# THEN ' force to twos-complement.
          Value# = Value# - 4294967296# ' 8000 0000 to FFFF FFFF
       END IF
       ' display hex value.
       Box1$ = HEX$(Value#)
       COLORf2 15, 0
       LOCATEf 5, 9, 1
       PRINTf SPACE$(8)
       LOCATEf 5, 9, 1
       PRINTf Box1$
    END IF
 END IF
 LOCATEf Xcoor1, Ycoor1, 1
 RETURN
Error.Resume3:
 EXIT SUB
Error.Routine3:
 RESUME Error.Resume3
END SUB

' formatting function.
'   Value$  output string.
'   Var#  input value.
'   Var3  true for filesize.
SUB FormatX(Value$, Var#, Var3)
 ON LOCAL ERROR GOTO Error.Routine4
 Var2# = Var#
 IF Var3 THEN
    SELECT CASE FileSizeType
    CASE 1 ' KB.
       Var# = INT(Var# / 1024#)
    CASE 2 ' MB.
       Var# = INT(Var# / 1048576#)
    END SELECT
 END IF
 Value$ = Nul
 Var4$ = MID$(STR$(Var#), 2)
 FOR Var4 = LEN(Var4$) TO 3 STEP -3
    Value$ = MID$(Var4$, Var4 - 2, 3) + "," + Value$
 NEXT
 IF Var4 > False THEN
    Value$ = MID$(Var4$, 1, Var4) + "," + Value$
 END IF
 IF LEN(Var4$) < 3 THEN
    Value$ = Var4$
 END IF
 IF RIGHT$(Value$, 1) = "," THEN
    Value$ = LEFT$(Value$, LEN(Value$) - 1)
 END IF
 IF Var3 THEN
    SELECT CASE FileSizeType
    CASE 1 ' KB.
       Var2# = Var2# / 1024#
       GOSUB AddPoint
    CASE 2 ' MB.
       Var2# = Var2# / 1048576#
       GOSUB AddPoint
    END SELECT
 END IF
 EXIT SUB
AddPoint:
 V$ = STR$(Var2#)
 V = INSTR(V$, ".")
 IF V THEN
    Value$ = Value$ + MID$(V$, V, 2)
 ELSE
    Value$ = Value$ + ".0"
 END IF
 RETURN
Error.Resume4:
 EXIT SUB
Error.Routine4:
 RESUME Error.Resume4
END SUB

' file menu box move subroutine.
SUB MoveMenu
 ON LOCAL ERROR GOTO Error.Routine5
 GOSUB StoreArea
 COLORf2 White, 1
 CLS
 GOSUB RestoreArea
 I$ = Nul
 DO
    I$ = INKEY$
    IF LEN(I$) THEN
       IF I$ = CHR$(27) THEN
          I$ = Nul
          EXIT DO
       END IF
       IF LEN(I$) = 2 THEN
          SELECT CASE ASC(RIGHT$(I$, 1))
          CASE 72 ' up
             IF Xcoor - 1 >= 2 THEN
                Xcoor = Xcoor - 1
                COLORf2 White, 1
                CLS
                GOSUB RestoreArea
             END IF
          CASE 80 ' down
             IF Xcoor + 1 <= 8 THEN
                Xcoor = Xcoor + 1
                COLORf2 White, 1
                CLS
                GOSUB RestoreArea
             END IF
          CASE 75 ' left
             IF Ycoor - 1 >= 1 THEN
                Ycoor = Ycoor - 1
                COLORf2 White, 1
                CLS
                GOSUB RestoreArea
             END IF
          CASE 77 ' right
             IF Ycoor + 1 <= 39 THEN
                Ycoor = Ycoor + 1
                COLORf2 White, 1
                CLS
                GOSUB RestoreArea
             END IF
          END SELECT
       END IF
    END IF
    R = ReleaseTime
 LOOP
 EXIT SUB

' store area.
StoreArea:
 RowX1 = False
 ColumnY1 = False
 FOR RowX2 = Xcoor - 1 TO Xcoor + 16
    RowX1 = RowX1 + 1
    ColumnY1 = False
    FOR ColumnY2 = Ycoor TO Ycoor + 41
       ColumnY1 = ColumnY1 + 1
       ' store ascii character.
       Area1(RowX1, ColumnY1) = SCREEN(RowX2, ColumnY2)
       ' store color. (undocumented: also stores background color).
       Area2(RowX1, ColumnY1) = SCREEN(RowX2, ColumnY2, 1)
    NEXT
 NEXT
 RETURN

' restore area.
RestoreArea:
 RowX1 = False
 ColumnY1 = False
 FOR RowX2 = Xcoor - 1 TO Xcoor + 16
    RowX1 = RowX1 + 1
    ColumnY1 = False
    FOR ColumnY2 = Ycoor TO Ycoor + 41
       ColumnY1 = ColumnY1 + 1
       LOCATEf RowX2, ColumnY2, 1
       ' restore color.
       TempZ = Area2(RowX1, ColumnY1)
       ' undocumented: also stores background color.
       VarB = INT(TempZ / 16)
       VarF = TempZ MOD 16
       COLORf2 VarF, VarB
       ' restore ascii character.
       PRINTf CHR$(Area1(RowX1, ColumnY1))
    NEXT
 NEXT
 RETURN
Error.Resume5:
 EXIT SUB
Error.Routine5:
 RESUME Error.Resume5
END SUB

SUB OverridePrompt
 ON LOCAL ERROR GOTO Error.Routine6
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 PRINTf SPACE$(38)
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 COLORf2 White, 0
 PRINTf "Require readonly bit. Press <y/n>:"
 DO
    M$ = Nul
    WHILE M$ = Nul
       M$ = INKEY$
    WEND
    M$ = UCASE$(M$)
    IF M$ = "Y" THEN
       RequireReadOnly = True
       EXIT DO
    END IF
    IF M$ = "N" THEN
       RequireReadOnly = False
       EXIT DO
    END IF
    R = ReleaseTime
 LOOP
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 PRINTf SPACE$(38)
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 COLORf2 White, 0
 PRINTf "Require hidden bit. Press <y/n>:"
 DO
    M$ = Nul
    WHILE M$ = Nul
       M$ = INKEY$
    WEND
    M$ = UCASE$(M$)
    IF M$ = "Y" THEN
       RequireHidden = True
       EXIT DO
    END IF
    IF M$ = "N" THEN
       RequireHidden = False
       EXIT DO
    END IF
    R = ReleaseTime
 LOOP
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 PRINTf SPACE$(38)
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 COLORf2 White, 0
 PRINTf "Require system bit. Press <y/n>:"
 DO
    M$ = Nul
    WHILE M$ = Nul
       M$ = INKEY$
    WEND
    M$ = UCASE$(M$)
    IF M$ = "Y" THEN
       RequireSystem = True
       EXIT DO
    END IF
    IF M$ = "N" THEN
       RequireSystem = False
       EXIT DO
    END IF
    R = ReleaseTime
 LOOP
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 PRINTf SPACE$(38)
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 COLORf2 White, 0
 PRINTf "Require archive bit. Press <y/n>:"
 DO
    M$ = Nul
    WHILE M$ = Nul
       M$ = INKEY$
    WEND
    M$ = UCASE$(M$)
    IF M$ = "Y" THEN
       RequireArchive = True
       EXIT DO
    END IF
    IF M$ = "N" THEN
       RequireArchive = False
       EXIT DO
    END IF
    R = ReleaseTime
 LOOP
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 PRINTf SPACE$(38)
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 COLORf2 White, 0
 PRINTf "Require dir readonly bit. Press <y/n>:"
 DO
    M$ = Nul
    WHILE M$ = Nul
       M$ = INKEY$
    WEND
    M$ = UCASE$(M$)
    IF M$ = "Y" THEN
       RequireDirReadOnly = True
       EXIT DO
    END IF
    IF M$ = "N" THEN
       RequireDirReadOnly = False
       EXIT DO
    END IF
    R = ReleaseTime
 LOOP
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 PRINTf SPACE$(38)
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 COLORf2 White, 0
 PRINTf "Require dir hidden bit. Press <y/n>:"
 DO
    M$ = Nul
    WHILE M$ = Nul
       M$ = INKEY$
    WEND
    M$ = UCASE$(M$)
    IF M$ = "Y" THEN
       RequireDirHidden = True
       EXIT DO
    END IF
    IF M$ = "N" THEN
       RequireDirHidden = False
       EXIT DO
    END IF
    R = ReleaseTime
 LOOP
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 PRINTf SPACE$(38)
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 COLORf2 White, 0
 PRINTf "Require dir system bit. Press <y/n>:"
 DO
    M$ = Nul
    WHILE M$ = Nul
       M$ = INKEY$
    WEND
    M$ = UCASE$(M$)
    IF M$ = "Y" THEN
       RequireDirSystem = True
       EXIT DO
    END IF
    IF M$ = "N" THEN
       RequireDirSystem = False
       EXIT DO
    END IF
    R = ReleaseTime
 LOOP
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 PRINTf SPACE$(38)
 LOCATEf Xcoor + 1, Ycoor + 2, 1
 COLORf2 White, 0
 PRINTf "Require dir archive bit. Press <y/n>:"
 DO
    M$ = Nul
    WHILE M$ = Nul
       M$ = INKEY$
    WEND
    M$ = UCASE$(M$)
    IF M$ = "Y" THEN
       RequireDirArchive = True
       EXIT DO
    END IF
    IF M$ = "N" THEN
       RequireDirArchive = False
       EXIT DO
    END IF
    R = ReleaseTime
 LOOP
Error.Resume6:
 EXIT SUB
Error.Routine6:
 RESUME Error.Resume6
END SUB

' routine compares occurrence of filename1$ in filename2$.
'  allows imbedded ? and * characters,
'  also compares ^ character exclusion.
SUB CheckExcluded(Filename1$, Filename2$, Match)
 On Local Error Goto Error.Routine7
 Match = True ' assume mask matches filename2
 Length1 = 1
 Length2 = 1
 DO
    ' global replacement
    IF MID$(Filename1$, Length1, 1) = "*" THEN
       DO
          Length1 = Length1 + 1
          IF Length1 > LEN(Filename1$) THEN
             EXIT SUB
          END IF
          ' global replacement followed by exclusion character
          ' searches remaining string until exclusion character found or not.
          IF MID$(Filename1$, Length1, 1) = "^" THEN
             Length1 = Length1 + 1
             Not.Include$ = MID$(Filename1$, Length1, 1)
             DO
                IF Not.Include$ <> MID$(Filename2$, Length2, 1) THEN
                   Length2 = Length2 + 1
                ELSE
                   Match = False
                   EXIT SUB
                END IF
                IF Length2 > LEN(Filename2$) THEN
                   EXIT SUB
                END IF
             LOOP
          END IF
          ' global replacement followed by ? or another *
          ' skips to next character.
          IF MID$(Filename1$, Length1, 1) <> "*" THEN
             IF MID$(Filename1$, Length1, 1) <> "?" THEN
                EXIT DO
             END IF
          END IF
       LOOP
       ' global replacement
       ' searches for next matching character
       DO
          IF MID$(Filename1$, Length1, 1) = MID$(Filename2$, Length2, 1) THEN
             EXIT DO
          ELSE
             Length2 = Length2 + 1
          END IF
          IF Length2 > LEN(Filename2$) THEN
             EXIT DO
          END IF
       LOOP
    ELSE
       ' character replacement
       ' matches any next character
       IF MID$(Filename1$, Length1, 1) = "?" THEN
          Length1 = Length1 + 1
          Length2 = Length2 + 1
       ELSE
          ' exclusion character
          ' checks next character unmatched
          IF MID$(Filename1$, Length1, 1) = "^" THEN
             Length1 = Length1 + 1
             Not.Include$ = MID$(Filename1$, Length1, 1)
             IF Not.Include$ <> MID$(Filename2$, Length2, 1) THEN
                Length1 = Length1 + 1
                Length2 = Length2 + 1
             ELSE
                Match = False
                EXIT DO
             END IF
          ELSE
             ' matches next character
             IF MID$(Filename1$, Length1, 1) = MID$(Filename2$, Length2, 1) THEN
                Length1 = Length1 + 1
                Length2 = Length2 + 1
             ELSE
                Match = False
                EXIT DO
             END IF
             ' check string lengths
             IF Length1 > LEN(Filename1$) THEN
                IF Length2 <= LEN(Filename2$) THEN
                   Match = False
                END IF
                EXIT DO
             END IF
          END IF
       END IF
    END IF
 LOOP
 EXIT SUB
Error.Resume7:
 Exit Sub
Error.Routine7:
 Resume Error.Resume7
END SUB

SUB Parse.Path(D$, X$)
 On Local Error Goto Error.Routine8
 ' parse out imbedded \.\ path.
 GOSUB PathOverride1
 ' parse out trailing \.. path with multiple dots.
 GOSUB PathOverride2
 ' parse out leading ..\ path with multiple dots.
 GOSUB PathOverride3
 ' parse out leading \..\ path with multiple dots.
 GOSUB PathOverride4
 ' parse out imbedded \..\ path with multiple dots.
 GOSUB PathOverride5
 ' parse out imbedded \\ from path.
 GOSUB PathOverride6
 EXIT SUB

' parse out imbedded \.\ path.
PathOverride1:
 IF LEN(X$) THEN
    DO
       V = INSTR(X$, "\.\")
       IF V THEN
          X$ = LEFT$(X$, V) + MID$(X$, V + 3)
       ELSE
          EXIT DO
       END IF
    LOOP
 END IF
 RETURN

' parse out trailing \.. path with multiple dots.
PathOverride2:
 Z$ = Nul
 IF LEN(X$) THEN
    ' search for trailing path.
    DO
       Z = False
       L = False
       IF RIGHT$(X$, 1) = "." THEN
          FOR V = LEN(X$) TO 1 STEP -1
             IF MID$(X$, V, 1) = "." THEN
                L = L + 1
             ELSE
                IF MID$(X$, V, 1) = "\" THEN
                   Z = True
                   EXIT FOR
                ELSE
                   EXIT FOR
                END IF
             END IF
          NEXT
       END IF
       IF Z = False THEN
          EXIT DO
       END IF
       Z$ = MID$(X$, V) + Z$
       X$ = LEFT$(X$, V - 1)
    LOOP
    IF Z$ = Nul THEN
       ' strip trailing dots.
       IF INSTR(X$, "\") THEN
          DO
             IF RIGHT$(X$, 1) = "." THEN
                X$ = LEFT$(X$, LEN(X$) - 1)
             ELSE
                EXIT DO
             END IF
          LOOP
       END IF
       RETURN
    END IF
    DO
       ' search leading dots.
       L = False
       FOR V = 2 TO LEN(Z$)
          IF MID$(Z$, V, 1) = "." THEN
             L = L + 1
             IF V = LEN(Z$) THEN
                Z$ = Nul
                EXIT FOR
             END IF
          ELSE
             IF MID$(Z$, V, 1) = "\" THEN
                Z$ = MID$(Z$, V)
                EXIT FOR
             END IF
          END IF
       NEXT
       ' strip trailing path.
       DO
          L2 = 1
          Z = False
          FOR V = LEN(X$) TO 1 STEP -1
             IF MID$(X$, V, 1) = "\" THEN
                L2 = L2 + 1
                IF L = L2 THEN
                   Z = True
                   EXIT DO
                END IF
             END IF
          NEXT
          IF Z = False THEN
             EXIT DO
          END IF
       LOOP
       IF Z = False THEN
          X$ = Nul
          EXIT DO
       END IF
       IF V = 1 THEN
          IF LEFT$(X$, 1) = "\" THEN
             X$ = "\"
             EXIT DO
          END IF
       END IF
       X$ = LEFT$(X$, V - 1)
       IF Z$ = Nul THEN
          EXIT DO
       END IF
    LOOP
 END IF
 ' strip trailing dots.
 IF INSTR(X$, "\") THEN
    DO
       IF RIGHT$(X$, 1) = "." THEN
          X$ = LEFT$(X$, LEN(X$) - 1)
       ELSE
          EXIT DO
       END IF
    LOOP
 END IF
 RETURN

' parse out leading ..\ path with multiple dots.
PathOverride3:
 Z$ = D$
 DO
    ' search for leading dots.
    Z = False
    L = False
    IF LEFT$(X$, 1) = "." THEN
       FOR V = 1 TO LEN(X$)
          IF MID$(X$, V, 1) = "." THEN
             L = L + 1
          ELSE
             IF MID$(X$, V, 1) = "\" THEN
                Z = True
                EXIT FOR
             ELSE
                EXIT FOR
             END IF
          END IF
       NEXT
    END IF
    IF Z = False THEN
       EXIT DO
    END IF
    X$ = MID$(X$, V + 1)
    ' parse off trailing path.
    IF L > 1 THEN
    L2 = 1
       DO
          Z = False
          FOR V = LEN(Z$) TO 1 STEP -1
             IF MID$(Z$, V, 1) = "\" THEN
                Z$ = LEFT$(Z$, V - 1)
                L2 = L2 + 1
                Z = True
                EXIT FOR
             END IF
          NEXT
          IF Z = False THEN
             Z$ = Nul
             EXIT DO
          END IF
          IF L2 = L THEN
             EXIT DO
          END IF
       LOOP
    END IF
 LOOP
 D$ = Z$
 RETURN

' parse out leading \..\ path with multiple dots.
PathOverride4:
 DO
    IF LEFT$(X$, 1) = "\" THEN
       IF LEN(X$) = 1 THEN
          EXIT DO
       END IF
       L = False
       FOR V = 2 TO LEN(X$)
          IF MID$(X$, V, 1) = "." THEN
             L = L + 1
          ELSE
             IF MID$(X$, V, 1) = "\" THEN
                IF L > 0 THEN
                   X$ = MID$(X$, V)
                   EXIT FOR
                END IF
             END IF
             EXIT DO
          END IF
       NEXT
    ELSE
       EXIT DO
    END IF
 LOOP
 RETURN

' parse out imbedded \..\ path with multiple dots.
PathOverride5:
 IF LEN(X$) THEN
    DO
       ' search for \.\
       Z = False
       FOR V = 1 TO LEN(X$)
          IF MID$(X$, V, 1) = "\" THEN
             L = False
             FOR V2 = V + 1 TO LEN(X$)
                IF MID$(X$, V2, 1) = "." THEN
                   L = L + 1
                ELSE
                   IF MID$(X$, V2, 1) = "\" THEN
                      IF L > 0 THEN
                         Z = True
                      END IF
                      EXIT FOR
                   ELSE
                      EXIT FOR
                   END IF
                END IF
             NEXT
          END IF
          IF Z = True THEN
             EXIT FOR
          END IF
       NEXT
       IF Z = False THEN
          EXIT DO
       END IF
       Z$ = "\" + STRING$(L, ".") + "\"
       V4 = INSTR(X$, Z$)
       A$ = MID$(X$, V4 + L + 2)
       L2 = 1
       DO
          V4 = V4 - 1
          IF V4 = False THEN
             EXIT DO
          END IF
          IF MID$(X$, V4, 1) = "\" THEN
             L2 = L2 + 1
             IF L2 = L THEN
                EXIT DO
             END IF
          END IF
       LOOP
       IF V4 = False THEN
          X$ = A$
       ELSE
          X$ = LEFT$(X$, V4) + A$
       END IF
    LOOP
 END IF
 ' search for imbedded ..\
 DO
    V = INSTR(X$, ".\")
    IF V THEN
       L = False
       FOR V2 = V TO 1 STEP -1
          IF MID$(X$, V2, 1) = "." THEN
             L = L + 1
          ELSE
             X$ = LEFT$(X$, V2) + MID$(X$, V + 1)
             EXIT FOR
          END IF
       NEXT
    ELSE
       EXIT DO
    END IF
 LOOP
 RETURN

' strip imbedded \\ from path.
PathOverride6:
 DO
    V = INSTR(X$, "\\")
    IF V THEN
       X$ = LEFT$(X$, V - 1) + MID$(X$, V + 1)
    ELSE
       EXIT DO
    END IF
 LOOP
 RETURN
Error.Resume8:
 Exit Sub
Error.Routine8:
 Resume Error.Resume8
END SUB

' strip multiple dots from path.
SUB Parse.Path2(D$, X$)
 On Local Error Goto Error.Routine9
 DO
    IF LEFT$(X$, 2) = ".." THEN
       Z = False
       FOR Var = LEN(D$) TO 1 STEP -1
          IF MID$(D$, Var, 1) = "\" THEN
             D$ = LEFT$(D$, Var - 1)
             X$ = MID$(X$, 2)
             Z = True
             EXIT FOR
          END IF
       NEXT
       IF Z = False THEN
          D$ = Nul
          X$ = Nul
          EXIT DO
       END IF
    ELSE
       X$ = Nul
       EXIT DO
    END IF
 LOOP
Error.Resume9:
 Exit Sub
Error.Routine9:
 Resume Error.Resume9
END SUB

' specialized keyboard input function.
Function LineInput$(XC,YC,LC) ' screen xcoor/ycoor/length
 On Local Error Resume Next

 ' init entry area position.
 Xposition1 = 1
 Xposition2 = 0

 ' display file entry area.
 GOSUB DisplayFileLine

 ' main input loop.
 DO
    ' get keypress.
    I$ = INKEY$

    ' check key.
    IF LEN(I$) = 1 THEN

       ' enter key.
       IF I$ = CHR$(13) THEN
          lineinput$ = X$
          EXIT FUNCTION
       END IF

       ' escape key.
       IF I$ = CHR$(27) THEN
          lineinput$ = ""
          EXIT FUNCTION
       END IF

       ' backspace.
       IF I$ = CHR$(8) THEN
          IF LEN(X$) THEN
             IF XPosition2 > 0 THEN
                X$ = LEFT$(X$, Xposition2 - 1) + MID$(X$, Xposition2 + 1)
                Xposition2 = Xposition2 - 1
                IF Xposition2 - LC <= Xposition1 THEN
                   Xposition1 = Xposition1 - 1
                   IF Xposition1 < 1 THEN
                      Xposition1 = 1
                   END IF
                END IF
                GOSUB DisplayFileLine
             END IF
          END IF
       ELSE
          ' append/insert character into filename area.
          IF LEN(I$) THEN
             X$ = LEFT$(X$, Xposition2) + I$ + MID$(X$, Xposition2 + 1)
             Xposition2 = Xposition2 + 1
             IF Xposition2 > LC THEN
                Xposition1 = Xposition1 + 1
             END IF
             GOSUB DisplayFileLine
          END IF
       END IF
    END IF

    ' check extended key
    IF LEN(I$) = 2 THEN
       SELECT CASE ASC(RIGHT$(I$, 1))
       CASE 71 ' Home
          IF Xposition2 > 0 THEN
             Xposition1 = 1
             Xposition2 = 0
             GOSUB DisplayFileLine
          END IF
       CASE 79 ' End
          IF Xposition2 < LEN(X$) THEN
             Xposition2 = LEN(X$)
             Xposition1 = Xposition2 - LC + 1
             IF Xposition1 < 1 THEN
                Xposition1 = 1
             END IF
             GOSUB DisplayFileLine
          END IF
       CASE 83 ' Delete
          IF LEN(X$) > 0 THEN
             X$ = LEFT$(X$, Xposition2) + MID$(X$, Xposition2 + 2)
             GOSUB DisplayFileLine
          END IF
       CASE 75 ' Left
          IF LEN(X$) THEN
             IF Xposition2 > 0 THEN
                Xposition2 = Xposition2 - 1
                IF Xposition2 - LC <= Xposition1 THEN
                   Xposition1 = Xposition1 - 1
                   IF Xposition1 < 1 THEN
                      Xposition1 = 1
                   END IF
                   GOSUB DisplayFileLine
                END IF
             END IF
          END IF
       CASE 77 ' Right
          IF LEN(X$) THEN
             IF Xposition2 < LEN(X$) THEN
                Xposition2 = Xposition2 + 1
                IF Xposition2 > Xposition1 + LC - 1 THEN
                   Xposition1 = Xposition1 + 1
                END IF
                GOSUB DisplayFileLine
             END IF
          END IF
       END SELECT
    END IF
    R = ReleaseTime
 LOOP
 EXIT FUNCTION

' display current entry area.
DisplayFileLine:
 CALL HMouse
 LOCATEf XC, YC, 1
 PRINTf SPACE$(LC)
 ' display edit line and position cursor.
 LOCATEf XC, YC, 1
 IF LEN(X$) THEN
    COLORf White
    PRINTf MID$(X$, Xposition1, LC)
 END IF
 LOCATEf XC, YC + Xposition2 - Xposition1 + 1, 1
 COLORf2 Plain, 0
 CALL SMouse
 RETURN
END FUNCTION

' load current subdirectories.
SUB LoadDirSpec(DirY$, N$, C$, D$)
 IF RequireDirReadOnly OR RequireDirHidden OR RequireDirSystem OR RequireDirArchive THEN
    Required = True
 ELSE
    Required = False
 END IF
 CALL HMouse
 COLORf White
 LOCATEf Xcoor + 1, Ycoor + 12, 1
 PRINTf SPACE$(26)
 LOCATEf Xcoor + 1, Ycoor + 12, 1
 PRINTf Z3$
 COLORf Plain
 CALL SMouse
 ' verify valid path.
 GOSUB CheckPath
 IF ValidPath = False THEN
    D$ = Nul
 END IF
 ' reset directory counter.
 Num.Dirs = False
 ' set dta.
 IF Windows.Detected = False THEN
    GOSUB SetDTA1
 END IF

 ' find first directory.
 ASCIIZ3 = MakeFilename3$(N$, C$, D$) + CHR$(0)

 ' start findfirst.
 IF Windows.Detected THEN
    InregsX.AX = &H714E
    InregsX.CX = &H37
    InregsX.SI = &H1
    InregsX.DS = VARSEG(ASCIIZ3)
    InregsX.DX = VARPTR(ASCIIZ3)
    InregsX.ES = VARSEG(WDTAfile)
    InregsX.DI = VARPTR(WDTAfile)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Wfile.Handle = OutregsX.AX
 ELSE
    InregsX.AX = &H4E00
    InregsX.CX = &H37
    InregsX.DS = VARSEG(ASCIIZ3)
    InregsX.DX = VARPTR(ASCIIZ3)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' store directories.
 DO
    ' check findnext error.
    IF (OutregsX.Flags AND &H1) = &H1 THEN
       EXIT DO
    END IF
    ' check for directory.
    IF Windows.Detected THEN
       Attribute = ASC(WDTAfile.FileBits)
    ELSE
       Attribute = ASC(DTAfile.FileBits)
    END IF
    ' check directory attribute.
    IF (Attribute AND &H10) = &H10 THEN
       ' reset file flag.
       Flag = True
       IF Required THEN
          GOSUB Check.Required1
       END IF
       ' store directory name
       GOSUB StoreName1
       Flag2 = False
       IF TempF$ = "." OR TempF$ = ".." THEN
          Flag = True
          Flag2 = True
       END IF
       ' check flag.
       IF Flag THEN
          IF Num.Dirs = 16777216! THEN
             EXIT DO
          END IF
          ' check excluded list.
          IF Flag2 = False THEN
             IF LEN(DirY$) THEN
                GOSUB Check.List1
             END IF
          END IF
          IF Flag THEN
             ' store filename.
             Num.Dirs = Num.Dirs + 1
             If Windows.Detected Then
                WinFileStruc.Name = TempF$
                PUT 3, Num.Dirs, WinFileStruc
             Else
                DosFileStruc.Name = TempF$
                PUT 3, Num.Dirs, DosFileStruc
             Endif
          END IF
       END IF
    END IF
    ' find next directory.
    IF Windows.Detected THEN
       ' find next long filename.
       InregsX.AX = &H714F
       InregsX.BX = Wfile.Handle
       InregsX.SI = &H1
       InregsX.ES = VARSEG(WDTAfile)
       InregsX.DI = VARPTR(WDTAfile)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' find next directory.
       InregsX.AX = &H4F00
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF
 LOOP

 ' check windows dos.
 IF Windows.Detected THEN
    ' close long filename search.
    InregsX.AX = &H71A1
    InregsX.BX = Wfile.Handle
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' restore dta.
 IF Windows.Detected = False THEN
    GOSUB ResetDTA1
 END IF

 ' reset number of directories.
 IF Num.Dirs = False THEN
    Num.Dirs = 1
    If Windows.Detected Then
       WinFileStruc.Name = "."
       PUT 3, 1, WinFileStruc
    Else
       DosFileStruc.Name = "."
       PUT 3, 1, DosFileStruc
    Endif
 END IF

 ' display number of directories.
 CALL HMouse
 LOCATEf Xcoor + 3, Ycoor + 24, 0
 PRINTf SPACE$(8)
 LOCATEf Xcoor + 3, Ycoor + 24, 0
 COLORf White
 Var# = Num.Dirs
 CALL FormatX(Value$, Var#, 0)
 PRINTf Value$
 COLORf Plain
 CALL SMouse

 ' sort directories.
 IF HeapSortOff THEN
    EXIT SUB
 END IF
 IF Num.Dirs < 2 THEN
    EXIT SUB
 END IF
 CALL HMouse
 COLORf White
 LOCATEf Xcoor + 1, Ycoor + 12, 1
 PRINTf SPACE$(26)
 LOCATEf Xcoor + 1, Ycoor + 12, 1
 PRINTf "Sorting dirs.."
 COLORf Plain
 CALL SMouse
 CALL SortFiles(2)
 EXIT SUB

' check dir exclusion list.
Check.List1:
 Y2$ = UCASE$(DirY$)
 L1$ = UCASE$(TempF$)
 DO
    ' parse list.
    Y1 = INSTR(Y2$, " ")
    IF Y1 THEN
       Y1$ = LEFT$(Y2$, Y1 - 1)
       Y2$ = MID$(Y2$, Y1 + 1)
    ELSE
       Y1$ = Y2$
       Y2$ = Nul
    END IF
    Y1$ = LTRIM$(Y1$)
    Y1$ = RTRIM$(Y1$)
    Y2$ = LTRIM$(Y2$)
    Y2$ = RTRIM$(Y2$)
    ' check excluded file in filename.
    IF LEN(Y1$) THEN
       CALL CheckExcluded(Y1$, L1$, Exclude.File%)
       IF Exclude.File% THEN
          Flag = False
          EXIT DO
       END IF
    END IF
    IF Y2$ = Nul THEN
       EXIT DO
    END IF
 LOOP
 RETURN

' check requirements.
Check.Required1:
 IF RequireDirReadOnly THEN
    IF (Attribute AND 1) = 0 THEN
       Flag = False
    END IF
 END IF
 IF RequireDirHidden THEN
    IF (Attribute AND 2) = 0 THEN
       Flag = False
    END IF
 END IF
 IF RequireDirSystem THEN
    IF (Attribute AND 4) = 0 THEN
       Flag = False
    END IF
 END IF
 IF RequireDirArchive THEN
    IF (Attribute AND 32) = 0 THEN
       Flag = False
    END IF
 END IF
 RETURN

' check path.
CheckPath:
 ' reset flag.
 ValidPath = True
 ' read path attributes.
 GOSUB ReadPath
 ' check path.
 IF D$ = Nul THEN
    RETURN
 END IF
 IF D$ = "\" THEN
    RETURN
 END IF
 ' check carry flag error.
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    ValidPath = False
    RETURN
 END IF
 ' check filename is not path.
 IF (OutregsX.CX AND &H10) <> &H10 THEN
    ValidPath = False
 END IF
 RETURN

' read path/file attribute.
ReadPath:
 ' store directory.
 ASCIIZ = MakeFilename2$(N$, C$, D$) + CHR$(0)

 ' conanicalize filename.
 V4 = True
 GOSUB ShortFilenameX
 IF D$ = Nul THEN
    RETURN
 END IF
 IF D$ = "\" THEN
    RETURN
 END IF

 ' store filename.
 ASCIIZ = RTRIM$(Short.Filename$) + CHR$(0)

 ' check windows.
 IF Windows.Detected THEN
    ' get file attribute.
    InregsX.AX = &H7143
    InregsX.BX = &H0
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)
 ELSE
    ' get file attribute.
    InregsX.AX = &H4300
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF
 RETURN

' format filename for display and search purposes.
ShortFilenameX:
 IF D$ = "\" THEN
    Short.Filename$ = "\"
    RETURN
 END IF
 IF D$ = Nul THEN
    Short.Filename$ = "\"
    RETURN
 END IF

 ' store filename.
 If Windows.Detected Then
    ' read ambiguated 8.3 filename.
    CALL GetShortFilename(Short.Filename$)
 Else
    Short.Filename$ = ASCIIZ
    Imbedded = Instr(Short.Filename$, Chr$(0))
    If Imbedded Then
       Short.Filename$ = Left$(Short.Filename$, Imbedded - 1)
    Endif
 Endif
 IF Short.Filename$ = Nul THEN
    RETURN
 END IF

 ' check 8.3 flag.
 IF V4 THEN
    RETURN
 END IF

 ' store 8.3 filename.
 FOR V = LEN(Short.Filename$) TO 1 STEP -1
    IF MID$(Short.Filename$, V, 1) = "\" THEN
       Short.Filename$ = MID$(Short.Filename$, V + 1)
       EXIT FOR
    END IF
 NEXT
 RETURN

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

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

' store asciiz name.
StoreName1:
 IF Windows.Detected THEN
    TempF$ = WDTAfile.ASCIIZfull

    ' check ambiguate override.
    IF Ambiguate THEN
       TempF$ = WDTAfile.ASCIIZshort

       ' check for . and .. or nul short paths.
       VarF$ = TempF$
       VX = INSTR(VarF$, CHR$(0))
       IF VX THEN
          VarF$ = LEFT$(VarF$, VX - 1)
       END IF
       VarF$ = RTRIM$(VarF$)
       IF VarF$ = Nul THEN
          ' reset to long path.
          TempF$ = WDTAfile.ASCIIZfull
       END IF
    END IF
 ELSE
    TempF$ = DTAfile.ASCIIZfilename
 END IF
 VX = INSTR(TempF$, CHR$(0))
 IF VX THEN
    TempF$ = LEFT$(TempF$, VX - 1)
 END IF
 RETURN
END SUB

' load specified files.
SUB LoadFileSpec(FileY$,F$)
 IF RequireReadOnly OR RequireHidden OR RequireSystem OR RequireArchive THEN
    Required = True
 ELSE
    Required = False
 END IF
 CALL HMouse
 COLORf White
 LOCATEf Xcoor + 1, Ycoor + 12, 1
 PRINTf SPACE$(26)
 LOCATEf Xcoor + 1, Ycoor + 12, 1
 PRINTf Z4$
 COLORf Plain
 CALL SMouse
 Num.Files = False

 ' set dta.
 IF Windows.Detected = False THEN
    GOSUB SetDTA2
 END IF

 ' find first file.
 IF RIGHT$(F$, 1) = "\" THEN
    F$ = F$ + "*.*"
 END IF
 ASCIIZ3 = F$ + CHR$(0)

 ' start findfirst.
 IF Windows.Detected THEN
    InregsX.AX = &H714E
    InregsX.CX = &H27
    InregsX.SI = &H1
    InregsX.DS = VARSEG(ASCIIZ3)
    InregsX.DX = VARPTR(ASCIIZ3)
    InregsX.ES = VARSEG(WDTAfile)
    InregsX.DI = VARPTR(WDTAfile)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Wfile.Handle = OutregsX.AX
 ELSE
    InregsX.AX = &H4E00
    InregsX.CX = &H27
    InregsX.DS = VARSEG(ASCIIZ3)
    InregsX.DX = VARPTR(ASCIIZ3)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' store files.
 DO
    ' check findnext error.
    IF (OutregsX.Flags AND &H1) = &H1 THEN
       EXIT DO
    END IF

    ' check for file.
    IF Windows.Detected THEN
       Attribute = ASC(WDTAfile.FileBits)
    ELSE
       Attribute = ASC(DTAfile.FileBits)
    END IF

    ' reset file flag.
    Flag = True
    IF Required THEN
       GOSUB Check.Required2
    END IF

    ' check flag.
    IF Flag THEN
       ' store filename.
       GOSUB StoreName2
       IF Num.Files = 16777216! THEN
          EXIT DO
       END IF
       ' check excluded list.
       IF LEN(FileY$) THEN
          GOSUB Check.List2
       END IF
       ' check flag.
       IF Flag THEN
          ' store filename.
          Num.Files = Num.Files + 1
          If Windows.Detected Then
             WinFileStruc.Name = TempF$
             PUT 2, Num.Files, WinFileStruc
          Else
             DosFileStruc.Name = TempF$
             PUT 2, Num.Files, DosFileStruc
          Endif
       END IF
    END IF

    ' find next file.
    IF Windows.Detected THEN
       ' find next long filename.
       InregsX.AX = &H714F
       InregsX.BX = Wfile.Handle
       InregsX.SI = &H1
       InregsX.ES = VARSEG(WDTAfile)
       InregsX.DI = VARPTR(WDTAfile)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' find next filename.
       InregsX.AX = &H4F00
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF
 LOOP

 ' check windows dos.
 IF Windows.Detected THEN
    ' close long filename search.
    InregsX.AX = &H71A1
    InregsX.BX = Wfile.Handle
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' restore dta.
 IF Windows.Detected = False THEN
    GOSUB ResetDTA2
 END IF

 ' display number of files.
 CALL HMouse
 LOCATEf Xcoor + 3, Ycoor + 9, 0
 PRINTf SPACE$(9)
 LOCATEf Xcoor + 3, Ycoor + 9, 0
 COLORf White
 Var# = Num.Files
 CALL FormatX(Value$, Var#, 0)
 PRINTf Value$
 COLORf Plain
 CALL SMouse

 ' sort files.
 IF HeapSortOff THEN
    EXIT SUB
 END IF
 IF Num.Files < 2 THEN
    EXIT SUB
 END IF
 CALL HMouse
 COLORf White
 LOCATEf Xcoor + 1, Ycoor + 12, 1
 PRINTf SPACE$(26)
 LOCATEf Xcoor + 1, Ycoor + 12, 1
 PRINTf "Sorting files.."
 COLORf Plain
 CALL SMouse
 CALL SortFiles(1)
 EXIT SUB

' check file exclusion list.
Check.List2:
 Y2$ = UCASE$(FileY$)
 L1$ = UCASE$(TempF$)
 DO
    ' parse list.
    Y1 = INSTR(Y2$, " ")
    IF Y1 THEN
       Y1$ = LEFT$(Y2$, Y1 - 1)
       Y2$ = MID$(Y2$, Y1 + 1)
    ELSE
       Y1$ = Y2$
       Y2$ = Nul
    END IF
    Y1$ = LTRIM$(Y1$)
    Y1$ = RTRIM$(Y1$)
    Y2$ = LTRIM$(Y2$)
    Y2$ = RTRIM$(Y2$)
    ' check excluded file in filename.
    IF LEN(Y1$) THEN
       CALL CheckExcluded(Y1$, L1$, Exclude.File%)
       IF Exclude.File% THEN
          Flag = False
          EXIT DO
       END IF
    END IF
    IF Y2$ = Nul THEN
       EXIT DO
    END IF
 LOOP
 RETURN

' check requirements.
Check.Required2:
 IF RequireReadOnly THEN
    IF (Attribute AND 1) = 0 THEN
       Flag = False
    END IF
 END IF
 IF RequireHidden THEN
    IF (Attribute AND 2) = 0 THEN
       Flag = False
    END IF
 END IF
 IF RequireSystem THEN
    IF (Attribute AND 4) = 0 THEN
       Flag = False
    END IF
 END IF
 IF RequireArchive THEN
    IF (Attribute AND 32) = 0 THEN
       Flag = False
    END IF
 END IF
 RETURN

' store asciiz name.
StoreName2:
 IF Windows.Detected THEN
    TempF$ = WDTAfile.ASCIIZfull

    ' check ambiguate override.
    IF Ambiguate THEN
       TempF$ = WDTAfile.ASCIIZshort

       ' check for . and .. or nul short paths.
       VarF$ = TempF$
       VX = INSTR(VarF$, CHR$(0))
       IF VX THEN
          VarF$ = LEFT$(VarF$, VX - 1)
       END IF
       VarF$ = RTRIM$(VarF$)
       IF VarF$ = Nul THEN
          ' reset to long path.
          TempF$ = WDTAfile.ASCIIZfull
       END IF
    END IF
 ELSE
    TempF$ = DTAfile.ASCIIZfilename
 END IF
 VX = INSTR(TempF$, CHR$(0))
 IF VX THEN
    TempF$ = LEFT$(TempF$, VX - 1)
 END IF
 RETURN

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

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

REM End-of-subprogram. All I did was sit down and type it in.
