;====================================================================== ; DIRTREE 1.00 * Copyright (c) 1992, Robert L. Hummel ; PC Magazine Assembly Language Lab Notes ; ; Display a representation of the directory structure of a DOS drive. ;---------------------------------------------------------------------- CSEG SEGMENT PARA PUBLIC 'CODE' ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:CSEG ORG 100H ;COM file format ENTPT: JMP MAIN ;Jump over data ;====================================================================== ; Program data area. ;---------------------------------------------------------------------- LF EQU 10 ;Line feed CR EQU 13 ;Carriage return BLANK EQU 32 ;Blank or space char DTA EQU 80H ;Offset of default DTA ;---------------------------------------------------------------------- ; Messages. ;---------------------------------------------------------------------- COPYRIGHT$ DB CR,LF,"DIRTREE 1.00 ",254," Copyright (c) 1992" DB ", Robert L. Hummel",CR,LF DB "PC Magazine Assembly Language Lab Notes",LF CRLF$ DB CR,LF,"$" SUMMARY$ DB "Maximum Nested Depth:" DEPTH$ DB "..$",CR,LF DRIVESPEC$ DB "A:\",CR,LF,"$" ;Display search drive INVDRIVE$ DB "Drive Letter Is Invalid$" DRIVEERR$ DB "Error Accessing Target Drive$" TOODEEP$ DB "Nested Too Deep -- Insufficient Stack$" ;---------------------------------------------------------------------- ; Program variables. ;---------------------------------------------------------------------- OLDDRIVE DB 0 ;Current disk drive OLDDIR DB "\",64 DUP(0) ;Holds current path ROOTSPEC DB "\",0 ;Pathname of root dir SEARCHSPEC DB "*.*",0 ;All files PARENT DB "..",0 ;Parent directory LEVELS LABEL WORD NESTLEVEL DB 0 ;Dir nesting level MAXNEST DB 0 ;Maximum depth DEPTH DW 32 DUP (0) ;Maintains dir count MIDDIR$ DB 195,196,196,196,"$" ;Print prior to name LASTDIR$ DB 192,196,196,196,"$" ;Print prior to name HORZTAB$ DB 179, 32, 32, 32,"$" ;Continue prev level HORZBLANK$ DB " $" ;Don't continue ;====================================================================== ; MAIN procedure. ;---------------------------------------------------------------------- MAIN PROC NEAR ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:CSEG ;---------------------------------------------------------------------- ; Initialize the machine and display the program title. ;---------------------------------------------------------------------- CLD ;String moves forward MOV CX,AX ;Save drive status MOV AH,9 ;Display string MOV DX,OFFSET COPYRIGHT$ ; located here INT 21H ; thru DOS ;---------------------------------------------------------------------- ; If the first entry on the command line contains an invalid drive ; spec, DOS will have set AL (now in CL) to FFh. If so, tell the user. ;---------------------------------------------------------------------- MOV DX,OFFSET INVDRIVE$ ;Assume invalid drive CMP CL,0FFH ;Was drive invalid? JE M_ERR ;---------------------------------------------------------------------- ; Get the current drive. If we change drives, we'll need to restore the ; original drive when we're done. ;---------------------------------------------------------------------- MOV AH,19H ;Get current drive # INT 21H ; thru DOS MOV [OLDDRIVE],AL ;Save current drive MOV CL,AL ;Save drive number ;---------------------------------------------------------------------- ; If a drive letter was specified on the command line, DOS places the ; corresponding drive number in the first FCB (0=default, A=1, etc). ; Change to A=0. If no drive specified, use the default. ;---------------------------------------------------------------------- MOV DL,DS:[5CH] ;Get drive from FCB DEC DL ;If 0,turns sign bit on JS M_1 MOV CL,DL ;(Save drive number) MOV AH,0EH ;Select current drive INT 21H ; thru DOS M_1: ;---------------------------------------------------------------------- ; Past this point, any exit must restore the original drive. ; Display the root spec. ;---------------------------------------------------------------------- ADD CL,"A" ;Convert to ASCII MOV [DRIVESPEC$],CL ;Put in string MOV AH,9 ;Display string MOV DX,OFFSET DRIVESPEC$ ; of drive spec INT 21H ; thru DOS ;---------------------------------------------------------------------- ; Save the current directory on this drive so we can restore it later. ;---------------------------------------------------------------------- MOV AH,47H ;Get current directory SUB DL,DL ; on current drive MOV SI,OFFSET OLDDIR+1 ;Store after backslash INT 21H ; thru DOS JC M_3A ;---------------------------------------------------------------------- ; Change to the root directory of the target drive to begin displaying ; the directory structure. ;---------------------------------------------------------------------- MOV AH,3BH ;Set directory MOV DX,OFFSET ROOTSPEC ; to the root INT 21H ; thru DOS JC M_3A ;---------------------------------------------------------------------- ; Past this point, any exit must restore the original subdir. ; Call the recursive search routine to display the tree. ;---------------------------------------------------------------------- CALL SEARCH_BRANCH ;Display this branch ;---------------------------------------------------------------------- ; Print summary message. ;---------------------------------------------------------------------- MOV AL,[MAXNEST] ;Maximum nested level DEC AL ;Make 0-based AAM ;Split digits OR AX,3030H ;Make ASCII digits CMP AH,30H ;Top digit 0? JNE M_2 MOV AH,BLANK ;Change to blank M_2: XCHG AH,AL ;Put in order MOV WORD PTR [DEPTH$],AX ;Put chars in message MOV AH,9 ;Display string MOV DX,OFFSET SUMMARY$ ; showing summary INT 21H ; thru DOS ;---------------------------------------------------------------------- ; Restore subdir on this drive. ;---------------------------------------------------------------------- MOV AH,3BH ;Set directory MOV DX,OFFSET OLDDIR ; to original subdir INT 21H ; thru DOS JNC M_3B ;---------------------------------------------------------------------- ; An error accessing the target drive occurred. ;---------------------------------------------------------------------- M_3A: MOV DX,OFFSET DRIVEERR$ ; say couldn't read it ;---------------------------------------------------------------------- ; Display an error message. ;---------------------------------------------------------------------- MOV AH,9 ;Display string INT 21H ; thru DOS ;---------------------------------------------------------------------- ; Restore the original drive. ;---------------------------------------------------------------------- M_3B: MOV AH,0EH ;Set current drive MOV DL,[OLDDRIVE] ; to original drive INT 21H ; thru DOS JMP M_EXIT ;---------------------------------------------------------------------- ; Display the message at DS:DX, then exit. ;---------------------------------------------------------------------- M_ERR: MOV AH,9 ;Display string INT 21H ; thru DOS ;---------------------------------------------------------------------- ; Skip to the next line and terminate the program. ;---------------------------------------------------------------------- M_EXIT: MOV AH,9 ;Display string MOV DX,OFFSET CRLF$ ; to goto next line INT 21H ; thru DOS MOV AH,4CH ;Terminate program INT 21H ; thru DOS MAIN ENDP ;====================================================================== ; SEARCH_BRANCH (Near) - Recursive ; ; Search the current directory on the current drive and identify each ; subdirectory. Then recursively calls itself to trace each of those ; subdirectories. ;---------------------------------------------------------------------- ; CALL SEARCH_BRANCH ; Entry: None ; Exit: ; CF=NC - success ; =CY - failure (error accessing the disk) ;---------------------------------------------------------------------- ; Changes: AX BX CX DX SI DI ;---------------------------------------------------------------------- SEARCH_BRANCH PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG PUSH BP ;Create stack frame MOV BP,SP ;---------------------------------------------------------------------- ; Increment the nesting level counter. Update maximum nesting depth. ;---------------------------------------------------------------------- MOV BX,[LEVELS] ;Get deepest/current INC BL ;Move 1 deeper CMP BH,BL ;Deepest yet? JAE SB_0 MOV BH,BL ;Yes, remember it SB_0: MOV [LEVELS],BX ;Save them back DEC BL ;BL = level SUB BH,BH ;Make into a word ADD BX,BX ;Double for indexing MOV WORD PTR [DEPTH][BX],0 ;Init to 0 ;---------------------------------------------------------------------- ; Begin to count the subdirs at this level using find first/next. ; When no file is found (CY), we're finished searching this subdir. ;---------------------------------------------------------------------- MOV AH,4EH ;Find first match MOV CX,10H ;Attribute for subdir MOV DX,OFFSET SEARCHSPEC ;String to search for SB_1A: INT 21H ; thru DOS JC SB_1C ;---------------------------------------------------------------------- ; The search returns both normal files and subdirs. If attribute ; indicates a subdir, increment the count for this level and continue ; the search. ;---------------------------------------------------------------------- CMP BYTE PTR DS:[DTA][21],10H ;Subdir attribute? JNE SB_1B ;---------------------------------------------------------------------- ; Ignore the "." and ".." housekeeping subdir entries. ;---------------------------------------------------------------------- CMP BYTE PTR DS:[DTA+30],"." ;Dot or double-dot? JE SB_1B INC WORD PTR [DEPTH][BX] ;Count as traceable dir SB_1B: MOV AH,4FH ;Find next match JMP SB_1A ;Go continue search SB_1C: ;---------------------------------------------------------------------- ; If our search found no subdirs in this dir, we can exit now. ;---------------------------------------------------------------------- CMP WORD PTR [DEPTH][BX],0 ;Find any? JNE SB_1E SB_1D: JMP SB_EXIT ;Leave SB_1E: ;---------------------------------------------------------------------- ; This section of code repeats the find first/next procedure, but ; displays the tree as it goes. We only search for as many subdirs as ; we found previously. ;---------------------------------------------------------------------- MOV AH,4EH ;Find first match SB_2A: MOV CX,10H ;Attribute for subdir MOV DX,OFFSET SEARCHSPEC ;String to search for INT 21H ; thru DOS JC SB_1D ;Should never happen ;---------------------------------------------------------------------- ; A file was found. The search returns both normal files and subdirs. ; If it's not a subdir, just skip to the next file. ;---------------------------------------------------------------------- CMP BYTE PTR DS:[DTA][21],10H ;Subdir attribute? JE SB_2C SB_2B: JMP SB_6B ;Go find next SB_2C: ;---------------------------------------------------------------------- ; Ignore the "." and ".." housekeeping subdir entries. ;---------------------------------------------------------------------- CMP BYTE PTR DS:[DTA+30],"." ;Current or parent? JE SB_2B ;---------------------------------------------------------------------- ; Subdir found. Is it the last one at this level? ;---------------------------------------------------------------------- MOV DI,OFFSET MIDDIR$ ;Assume it's not MOV AX,WORD PTR [DEPTH][BX] ;Get subdir count CMP AX,1 ;Last one? JNE SB_2D MOV DI,OFFSET LASTDIR$ ;Use last dir string SB_2D: ;---------------------------------------------------------------------- ; Display the characters necessary to continue any higher-level ; directories. ;---------------------------------------------------------------------- MOV CL,[NESTLEVEL] ;Get current next level DEC CL ; and make zero based JZ SB_3C SUB CH,CH ;Make into word MOV SI,OFFSET DEPTH ;Start of depth array SB_3A: MOV DX,OFFSET HORZTAB$ ;Assume inside a branch LODSW ;Get dirs remaining OR AX,AX ;Any left this level? JNZ SB_3B MOV DX,OFFSET HORZBLANK$ ;Nope, print blanks SB_3B: MOV AH,9 ;Display string INT 21H ; thru DOS LOOP SB_3A ;Repeat for all levels SB_3C: MOV AH,9 ;Display string MOV DX,DI ;Precedes name INT 21H ; thru DOS ;---------------------------------------------------------------------- ; Display this subdirectory's name after its branch. ;---------------------------------------------------------------------- MOV SI,DTA+30 ;Point to name SB_4A: LODSB ;Get a char OR AL,AL ;Test for ending zero JZ SB_4B MOV DL,AL ;Put in DL MOV AH,2 ;Display char INT 21H ; thru DOS JMP SB_4A ;Continue for all chars SB_4B: MOV AH,9 ;Display string MOV DX,OFFSET CRLF$ ; new line INT 21H ; thru DOS ;---------------------------------------------------------------------- ; Remove this subdir from the count for this level. ;---------------------------------------------------------------------- DEC WORD PTR [DEPTH][BX] ;Reduce count ;---------------------------------------------------------------------- ; Make sure we have enough space on the stack. Maximum required is ; (32 levels) * (44 bytes per level) = 1408 bytes. ;---------------------------------------------------------------------- CMP SP,(OFFSET LASTBYTE + 44) - CSEG JAE SB_5 MOV AH,9 ;Display string MOV DX,OFFSET TOODEEP$ ;Not enough stack INT 21H ; thru DOS JMP SHORT SB_6A SB_5: ;---------------------------------------------------------------------- ; Make this subdir the default and call ourselves to search it. ;---------------------------------------------------------------------- MOV AH,3BH ;Make dir current MOV DX,DTA+30 ;Point to name INT 21H ; thru DOS JC SB_6A ;---------------------------------------------------------------------- ; Save the dir search info for this level on the stack. If an error ; occurs, just skip it and continue. ;---------------------------------------------------------------------- SUB SP,44 ;Create storage area MOV SI,DTA ;Source MOV DI,SP ;Destination MOV CX,22 ;Words to move REP MOVSW ;Move 'em ;---------------------------------------------------------------------- ; Call ourselves to trace this sub-subdir branch. ;---------------------------------------------------------------------- PUSH BX ;Save DEPTH index CALL SEARCH_BRANCH ;Recurse POP BX ;Restore DEPTH index ;---------------------------------------------------------------------- ; Restore the DIR search info for this subdir. ;---------------------------------------------------------------------- MOV SI,SP ;Source MOV DI,DTA ;Destination MOV CX,22 ;Words to move REP MOVSW ;Move 'em ADD SP,44 ;Destroy storage area ;---------------------------------------------------------------------- ; Change back to this directory. ;---------------------------------------------------------------------- MOV AH,3BH ;Change default dir MOV DX,OFFSET PARENT ; to this level INT 21H ; thru DOS JC SB_EXIT ;---------------------------------------------------------------------- ; Check the count to see if any more dirs left at this level. ;---------------------------------------------------------------------- SB_6A: CMP WORD PTR [DEPTH][BX],0 ;Reduce count JZ SB_EXIT SB_6B: MOV AH,4FH ;Find next JMP SB_2A ;---------------------------------------------------------------------- ; Return to the previous level. ;---------------------------------------------------------------------- SB_EXIT: DEC [NESTLEVEL] ;Up one level, please POP BP ;Destroy stack frame RET SEARCH_BRANCH ENDP ;---------------------------------------------------------------------- ; LASTBYTE points past the code and minimum free stack in this segment. ;---------------------------------------------------------------------- LASTBYTE EQU $+256 ;Allow small stack CSEG ENDS END ENTPT