; nasm -f bin -o subdir.com subdir.asm org 100h ; maximum nesting level for subdirectories %define MAXDEPTH 34 ; bit masks for file attributes %define att_ro 1 %define att_sys 2 %define att_hid 4 %define att_vol 8 %define att_sub 16 %define att_arc 32 ; directory entry as returned by findfirst/findnext struc dirent de_res resb 15h de_att resb 1 de_time resw 1 de_date resw 1 de_size resd 1 de_name resb 13 endstruc section .data ; misc. strings volmsg db 'Volume Label: ',0 newline db 0Dh,0Ah,0 any db '*.*',0 bslash db '\',0 indentspace db ' ',0 ; current nesting level of subdirectories sublevel dw 0 section .bss ; buffer for our current pattern for findfirst/next curpath resb 80h ; pointer to the data transfer area for this level curdta resw 1 ; big buffer for dta's dtabuf resb MAXDEPTH * dirent_size section .text mov byte[curpath],0 ; make sure we start with a null string! mov di,curpath ; build a pattern for findfirst/next mov si,bslash ; start from root call kitty ; naive little strcat mov si,any ; add the "*.*" call kitty call setdta ; set transfer area for "root" level newloop: ; here we begin searching directories mov dx,curpath ; our pattern mov cx,0FFh ; we'll take *any* attribute mov ah,4Eh ; findfirst matching name int 21h jc nofirst ; carry set if none found dirloop: ; process the directory entry mov si,[curdta] cmp byte[si+de_name],'.' je noshow ; if parent or this dir, skip it call showname ; print name to stdout mov si,[curdta] ; see if it's a subdir test byte[si+de_att],att_sub jz nosubdir ; skip this part, if not inc word[sublevel] ; update level cmp word[sublevel],MAXDEPTH ja nofirst ; gaak! bail out! call downpath ; add new name to path call showsdsuffix ; do "\" after name, and show full path call setdta ; set new dta for this level call newloop ; recurse ; return when subdir (including it's subdirs) dec word[sublevel] ; is done - back up a level call uppath ; remove last name from path call setdta ; get dta back to this level's jmp noshow nosubdir: mov si,newline ; CR after name if it wasn't a subdir call putz noshow: mov dx,[curdta] ; dx wants dta mov ah,4Fh ; findnext matching file int 21h jnc dirloop ; carry if notfound, else do more nofirst: ; if we get here, nothing left in this dir cmp word[sublevel],0 ; if it wasn't a subdir, we're done jz exit ret ; else return (un-recurse) and go back up a level exit: mov ah,4Ch int 21h ;--------------------------------------------------------- ;-------------------------------------------- setdta: push ax push dx mov dx,dtabuf mov ah,dirent_size ; calculate where dta for this level is mov al,[sublevel] mul ah ; ax=level*size add dx,ax ; add to base of our buffer mov ah,1Ah ; setdta per dx int 21h mov word[curdta],dx ; update our pointer to it pop dx pop ax ret ;----------------------------------------- ;------------------------------------------ downpath: ; add a name to our search pattern mov di,curpath call cutpath ; remove the "*.*" mov si,de_name ; point to filename add si,[curdta] ; in our dta call kitty ; and add it mov si,bslash ; and a backslash call kitty mov si,any ; put the "*.*" back call kitty ret ;------------------------------------------ ;---------------------------------------- uppath: ; remove a name from our search pattern mov di,curpath call cutpath ; remove the "*.*" call cutpath ; remove the last name (to "\") mov si,any ; put back "*.*" call kitty ret ;--------------------------------------- ;--------------------------------------- cutpath: ; chop pattern back to last "\" push ax push cx push di mov cx,-1 ; find the terminating zero sub al,al repne scasb dec di ; point to the zero dec di ; back one more to miss "\" if it's last .l1: dec di cmp byte[di],'\' ; look for next "\" jz .done mov byte[di],0 ; if not, truncate string jmp .l1 .done pop di pop cx pop ax ret ;---------------------------------------------- ;--------------------------------- showname: ; print file name from dta test byte[si+de_att],att_vol jz notvl mov si,volmsg ; if it's the volume label, say so call putz notvl: mov cx,[sublevel] inc cx ; indent according to subdir level mov si,indentspace indent: call putz loop indent mov si,[curdta] ; dta for this level add si,de_name ; offset of filename into it call putz ; print it ret ;-------------------------------------- ;------------------------------ showsdsuffix: ; do "\" after subdir name mov si,bslash call putz mov si,newline call putz mov si,curpath ; show full path, too call putz mov si,newline call putz ret ;----------------------------------------- ;------------------------------------------ kitty: ; a naive little strcat - adds si to end of di push ax push cx push si push di mov cx,-1 ; find the end sub al,al repne scasb dec di ; back up to overwrite zero .l1: lodsb ; copy si to end stosb or al,al ; zero copied? we're done jnz .l1 ; else do more pop di pop si pop cx pop ax ret ;---------------------------------------- ;----------------------------------------- putz: ; print zero-terminated string at si to stdout push ax push dx push si mov ah,2 .l1: lodsb or al,al jz .done mov dl,al int 21h jmp .l1 .done: pop si pop dx pop ax ret ;-------------------------------