; VTREE -- A Visual Tree-structured subdirectory list ; ===== ; ; (c) copyright Charles Petzold, 1985 ; ; Assembler file: COM format ; Requirements: DOS 2.0 or higher ; Parameters: Optional Drive Specification CSEG Segment Assume CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG Org 005Ch FCB Label Byte ; Will contain drive parameter Org 0080h DefaultDTA Label Byte ; Used for look-ahead searches Org 0100h Entry: Jmp Begin ; Program entry point ; Most Data ; --------- SearchAsciiz db ?,":\*.*",0 ; ? gets drive spec db '(C) Copyright Charles Petzold, 1985' DriveError: db 'Invalid disk drive$' ; Error message DosVersError db 'Requires DOS 2.0 +$' ; Another error message FirstOrNext db 0 ; Flag for searches LevelsIn dw 0 ; Directory level (0 = root) SearchString db "\*.*",0 ; Asciiz for "all files" SearchPointer dw 3 + Offset SearchAsciiz ; End of SearchAsciiz string DtaPointer dw Offset EndProg ; Place for current DTA DashCount dw ? ; For horizontal line count ; Check drive validity and DOS Version ; ------------------------------------ Begin: Cmp AL,0FFh ; Check for Valid Drive Specification Jnz DriveSpecOK ; If OK, proceed Lea DX,DriveError ; Otherwise print an error message! ErrorExit: Mov AH,9 ; Print string Int 21h ; by calling DOS Int 20h ; And exit DriveSpecOK: Mov AH,30h ; Use DOS to get the Int 21h ; Version Number Cmp AL,2 ; See if it's 2.0 or above Jae DosVersOK ; If so, proceed Lea DX,DosVersError ; Otherwise print an error message! Jmp ErrorExit ; Print message & exit ; Get disk drive for tree ; ----------------------- DosVersOK: Mov AL,[FCB] ; Get drive parameter from FCB Or AL,AL ; See if it's zero (default) Jnz NotDefault ; If not, skip a few instructions Mov AH,19h ; Get default drive Int 21h ; by calling DOS Inc AL ; Turns drive A: = 0 to drive A: = 1 NotDefault: Mov DL,AL ; Set DL to drive number (A: = 1) Add AL,'@' ; Convert to character "A", etc Mov [SearchAsciiz],AL ; Put it in our search string Cld ; All string directions forward ; Do *.* search for sub-directories ; --------------------------------- MainLoop: Mov DX,[DTAPointer] ; Get current DTA address Mov AH,1Ah ; Set the DTA Int 21h ; by calling DOS Mov BX,[LevelsIn] ; BX represents level Add BX,BX ; Double it for addressing Cmp [FirstOrNext],0 ; See if this is first search Jnz FindNextFile ; If not, skip some code Mov Word Ptr [SubDirCounter + BX],0 ; Count starts at 0 Mov DX,Offset SearchAsciiZ ; Directory to search for Mov CX,10h ; Directory attribute search Mov AH,4Eh ; Find first file Int 21h ; by calling DOS Jmp Short TestMatch ; See if we've got match FindNextFile: Mov AH,4Fh ; Otherwise find next file Int 21h ; by calling DOS TestMatch: Jnc TestAttr ; If CY flag not set, continue Jmp NoMoreFiles ; Otherwise, no more files TestAttr: Mov SI,[DTAPointer] ; SI now points to DTA Cmp Byte Ptr [SI + 21],10h ; Test if a directory Jnz FindNextFile ; If not, loop up for next FoundDirEntry: Add SI,30 ; SI points to directory name Cmp Byte Ptr [SI],'.' ; See if it's . or .. entry Jz FindNextFile ; If so, loop up for next ; Have found a valid directory entry ; ---------------------------------- Inc Word Ptr [SubDirCounter + BX] ; Got another one Mov CX,[LevelsIn] ; Number of levels deep Jcxz LookAheadSearch ; If root, skip indentation Cmp Word Ptr [SubDirCounter + BX],1 ; See if first found Jz NoSpaceIn ; If so, no indentations Sub BX,BX ; Start index at zero IndentLoop: Mov AL,179 ; Vertical line character Test Word Ptr [SubDirCounter + BX],8000h Jz GotContinueChar ; Use if still more dirs left Mov AL,' ' ; Otherwise use a blank GotContinueChar:Call PrintChar ; And print it Push CX ; Save the levels count Mov CX,16 ; Need 16 blanks indentation BlankLoop: Mov AL,' ' ; This is the blank Call PrintChar ; We print it Loop BlankLoop ; And loop for the next Pop CX ; Retrieve the levels count Inc BX ; Kick up the levels index Inc BX ; Twice because word access Loop IndentLoop ; Loop for all the levels NoSpaceIn: Cmp Word Ptr [SubDirCounter + BX],1 ; See if first one Jnz LookAheadSearch ; If not skip a little Mov CX,[DashCount] ; Number of lines to print DashPrint: Mov AL,196 ; Horizontal line character Call PrintChar ; Print them Loop DashPrint ; And loop for whole count ; Check for more directories to determine proper line characters ; -------------------------------------------------------------- LookAheadSearch:Push SI ; Save ptr to directory name Mov SI,[DtaPointer] ; Set source to DTA Mov DI,Offset DefaultDTA ; Set destination to 80h Mov DX,DI ; Also DX (used later) Mov CX,43 ; 43 characters to transfer Rep Movsb ; Move them in Pop SI ; Get back directory name ptr Mov AH,1Ah ; Set a new DTA Int 21h ; by calling DOS CheckIfAnyMore: Mov AH,4Fh ; Find the next file Int 21h ; by calling DOS Jc CantFindAnother ; CY set if can't find one Cmp Byte Ptr [DefaultDTA + 21],10h ; Test if a directory Jnz CheckIfAnyMore ; If not, gotta try again Mov AL,194 ; Horizontal w/ vertical below Cmp Word Ptr [SubDirCounter + BX],1 Jz GotGoodChar ; This is good if it's first Mov AL,195 ; Vertical w/ horizontal right Jmp Short GotGoodChar ; Other than first found CantFindAnother:Mov AL,196 ; Horizontal line character Cmp Word Ptr [SubDirCounter + BX],1 Jz GotGoodChar ; This is good if first one Mov AL,192 ; Lower left corner Or Word Ptr [SubDirCounter + BX],8000h ; Flag no more GotGoodChar: Call PrintChar ; Print that character also Mov AL,196 ; Horizontal line character Call PrintChar ; Another print Mov AL,' ' ; Space before file name Call PrintChar ; Print the space ; Now print name of directory and append to SearchPointer string ; -------------------------------------------------------------- Mov CX,13 ; Number of characters in name Mov DI,[SearchPointer] ; End of current search asciiz PrintNameLoop: Lodsb ; Get the directory name character Or AL,AL ; Check if it's zero terminator Jz EndOfName ; If so, we're at the end Stosb ; Save on end of search asciiz Call PrintChar ; And print it also Loop PrintNameLoop ; Loop for maximum number of chars EndOfName: Mov AL,' ' ; Stick a blank at the end Call PrintChar ; Print the blank Mov [DashCount],CX ; Save for later dashes at end FixUpSearch: Mov [SearchPointer],DI ; New end of asciiz string Inc [SearchPointer] ; Point to next character Mov SI,Offset SearchString ; Will move in \*.*,0 string Mov CX,5 ; It's only five characters Rep Movsb ; Move it ine Inc [LevelsIn] ; We're one level deeper now Mov [FirstOrNext],0 ; Prepare for search first Add [DtaPointer],43 ; New DTA will be needed Jmp MainLoop ; Back up to beginning ; When no more files are found, time to back up to previous subdirectory ; ---------------------------------------------------------------------- NoMoreFiles: Cmp [LevelsIn],0 ; See if we're back in root Jz Terminate ; If so, we're all done! Test Word Ptr [SubDirCounter + BX],7FFFh Jnz BackUpOneDir ; If at least one file found Mov AL,13 ; A carriage return Call PrintChar ; is printed Mov AL,10 ; A line feed Call PrintChar ; makes a new line BackUpOneDir: Mov DI,Offset SearchAsciiz ; Let's look at search string Mov CX,70 ; It could have 70 characters Mov AL,0 ; We'll search for a zero Repnz Scasb ; Let's do it Dec DI ; So points to zero Mov CX,64 ; Now we'll search backwards Mov AL,'\' ; For last slash Std ; Backwards search Repnz Scasb ; Do it once Repnz Scasb ; Again for dir we're leaving Inc DI ; So points to slash Mov [SearchPointer],DI ; New end of asciiz string Inc [SearchPointer] ; Actually one character more Mov SI,Offset SearchString ; Now append \*.*,0 to it Mov CX,5 ; Five characters Cld ; In forward direction Rep Movsb ; Move them int Dec [LevelsIn] ; We go back one level Mov [FirstOrNext],1 ; Have to search next, not 1st Sub [DtaPointer],43 ; Previous DTA will be used Jmp MainLoop ; Back up to beginning Terminate: Int 20h ; Terminate program ; PrintChar subroutine -- prints a character on the screen ; -------------------------------------------------------- PrintChar: Push DX Mov DL,AL ; DL gets the character Mov AH,2 ; Print character on display Int 21h ; by calling DOS Pop DX Ret ; Other data areas here at end so COM file short as possible ; ---------------------------------------------------------- SubDirCounter Label Word ; Counts dir entries EndProg equ 64 + Offset SubDirCounter ; DTAs go here CSEG EndS End Entry