;;
 ; @(#) erasedir.asm - Dir and file complete (unrecoverable) erase utility.
 ; (c) 1995 Ivan Maidanski <ivmai@chat.ru> http://ivmai.chat.ru
 ; Freeware program source. All rights reserved.
 ;;
 ; Language: ASM8086 (for IBM PC/AT, MS-DOS v3.0+, COM file)
 ; Tested with: Turbo Assembler v3.1, Turbo Link v5.1
 ; Last modified: 1995-05-26 13:40:00 GMT+04:00
 ;;
NAME EraseDir

 ; tasm /m2 erasedir.asm
 ; tlink /t erasedir.obj

IDEAL
MODEL TINY
LOCALS

; Definitions
 MaxFileNameLen EQU 100h

CODESEG

; PSP variables
  ORG 80h
  LABEL DosCmdLine BYTE
  LABEL Dta BYTE

; Program initialization
  ORG 100h
  LABEL Start NEAR
  mov sp,OFFSET StackEnd
  mov bx,(ProgramLen+0Fh)/10h
  mov ah,4Ah
  int 21h

; Main program
  call AnalyzeDos
  call AnalyzeCmdLine
  call GetClearedBuffer
  call TestIsDir
  jnc @@Skip
  call ClearAllDirAndReturn
 @@Skip:
  call WriteToFile
  call DeleteOneFile
  call FreeBuffer
  call ClearFileName

; Return to DOS
  mov ax,4C00h
  int 21h

; Procedures
PROC AnalyzeDos
 ; Expects: -
 ; Returns: -
 ; Uses: ax,bx,cx
  mov ah,30h
  int 21h
  cmp al,2h
  jb @@InvalidVersion
  ret
 @@InvalidVersion:
  lea si,[@@MsgInvalidVer]
  call WriteStr
  mov ax,4C02h
  int 21h
 @@MsgInvalidVer DB 'MS-DOS Version too low (must be 2.00 or higher).'
                 DB 0Dh,0Ah,0
ENDP
PROC AnalyzeCmdLine
 ; Expects: [ds:DosCmdLine]
 ; Returns: [ds:FileName]
 ; Uses: ax,cx,si,di,es
  mov cl,[DosCmdLine]
  lea si,[DosCmdLine]
  inc cl
 @@Loop:
  dec cl
  jz @@ShowInfo
  inc si
  cmp [byte ptr si],' '
  jbe @@Loop
  lea di,[FileName]
  mov [FileNameStart],cl
 @@CopyName:
  mov al,[si]
  mov [di],al
  inc si
  inc di
  dec cl
  jnz @@CopyName
 @@Back:
  dec di
  cmp [byte ptr di],' '
  jbe @@Back
  mov [byte ptr di+1],0
  ret
 @@ShowInfo:
  lea si,[@@MsgInfo]
  call WriteStr
  mov ax,4C01h
  int 21h
 @@MsgInfo DB 'Directory (and Files) Complete Erase Utility v1.0',0Dh,0Ah
           DB ' Usage: ERASEDIR path',0Dh,0Ah,0
ENDP
PROC GetClearedBuffer
 ; Expects: -
 ; Returns: [ds:BufSeg], [ds:BufLen]
 ; Uses: ax,bx,cx,di,es
  mov ah,48h ; determine available memory
  mov bx,0FFFFh
  int 21h
  jnc @@Error
  and bx,bx
  jz @@Error
  cmp bh,0
  je @@LowMem
  mov bl,0
  cmp bh,0Fh
  jbe @@LowMem
  mov bh,0Fh
 @@LowMem:
  mov ax,bx
  REPT 4h
   shl ax,1
  ENDM
  mov [BufLen],ax
  shr ax,1
  mov cx,ax
  mov ah,48h ; get memory block
  int 21h
  jc @@Error
  mov [BufSeg],ax
  mov es,ax
  mov di,0
  mov ax,0
  rep stosw
  ret
 @@Error:
  call FatalError
ENDP
PROC FreeBuffer
 ; Expects: [ds:BufSeg]
 ; Returns: -
 ; Uses: ax,es
  mov ah,49h
  mov es,[BufSeg]
  int 21h
  jc @@Error
  ret
 @@Error:
  call FatalError
ENDP
PROC FatalError
 ; Expects: -
 ; Returns: -
 ; Uses: -
  lea si,[@@MsgFatalError]
  call WriteStr
  mov ax,4CFFh
  int 21h
 @@MsgFatalError DB 'Fatal Error!',0Dh,0Ah,0
ENDP
PROC ClearFileName
 ; Expects: -
 ; Returns: [ds:FileName]
 ; Uses: al,cx,di,es
  cld
  mov cx,ds
  mov es,cx
  lea di,[FileName]
  mov al,0
  mov cx,MaxFileNameLen
  rep stosb
  ret
ENDP
PROC TestIsDir
 ; Expects: [ds:FileName]
 ; Returns: cf=(0-File,1-Dir)
 ; Uses: ax,bx,dx
  mov ax,3D00h ; open file for reading
  lea dx,[FileName]
  int 21h
  jc @@TestDir
  mov bx,ax
  mov ah,3Eh ; close file
  int 21h
  jc @@Error
  clc
  ret
 @@TestDir:
  cmp ax,5h
  jne @@Error
  stc
  ret
 @@Error:
  call TestError
ENDP
PROC ClearAllDirAndReturn
 ; Expects: [ds:FileName]
 ; Returns: [ds:FileName]
 ; Uses: ax,bx,cx,dx,si,di,es [ds:Dta]
  inc [FileNameStart]
  lea si,[@@BackSlash]
  call AppendToFileName
 @@EnterDir:
  call FindDir
  jnc @@EnterDir
  call CountEntries
  push cx
  call ClearAllFiles
  pop cx
  call DeleteOneDirectory
  jnc @@EnterDir
  ret
 @@BackSlash DB '\',0
ENDP
PROC ClearAllFiles
 ; Expects: [ds:FileName]
 ; Returns: [ds:FileName]
 ; Uses: ax,bx,cx,dx,si,di,es, [ds:Dta]
  clc
 @@Next:
  mov bl,0
  call FindFile
  jc @@Exit
  call WriteToFile
  call RenameOneFile
  call DeleteOneFile
  stc
  jmp @@Next
 @@Exit:
  ret
ENDP
PROC WriteToFile
 ; Expects: [ds:FileName]
 ; Returns: -
 ; Uses: ax,bx,cx,dx,si,di
  mov ax,3D00h ; open file for reading
  lea dx,[FileName]
  int 21h
  jc @@Error
  mov bx,ax
  mov ax,4202h ; set file pointer to the end
  mov cx,0
  mov dx,0
  int 21h
  jc @@Error
  mov si,dx
  mov di,ax
  mov ah,3Eh ; close file
  int 21h
  jc @@Error
  mov ax,3D01h ; open file for writing
  lea dx,[FileName]
  int 21h
  jc @@Error
  mov bx,ax
 @@Loop:
  mov ah,40h ; write to file
  mov cx,[BufLen]
  mov dx,0
  push ds
  mov ds,[BufSeg]
  int 21h
  pop ds
  jc @@Error
  cmp ax,[BufLen]
  jne @@Error
  sub di,[BufLen]
  jnc @@Loop
  sub si,1
  jnc @@Loop
  mov ax,4200h ; set file pointer
  mov cx,0
  mov dx,0
  int 21h
  jc @@Error
  mov ah,40h ; truncate file
  mov cx,0
  int 21h
  jc @@Error
  mov ah,3Eh ; close file
  int 21h
  jc @@Error
  ret
 @@Error:
  call TestError
ENDP
PROC CountEntries
 ; Expects: [ds:FileName]
 ; Returns: cx=AmountOfEntries
 ; Uses: ax,bl,cx,dx,si,di, [ds:Dta]
  mov cx,0
  push cx
  mov bl,0
  clc
 @@Loop:
  call FindFile
  jc @@NotFound
  call GetLastFileName
  pop cx
  inc cx
  push cx
  stc
  jmp @@Loop
 @@NotFound:
  pop cx
  inc cx
  inc cx
  ret
ENDP
PROC DeleteAllFiles
 ; Expects: [ds:FileName]
 ; Returns: -
 ; Uses: ax,bl,cx,dx,si,di, [ds:Dta]
  mov bl,0
  clc
 @@Loop:
  call FindFile
  jc @@NotFound
  call DeleteOneFile
  stc
  jmp @@Loop
 @@NotFound:
  ret
ENDP
PROC RenameOneFile
 ; Expects: [ds:FileName]
 ; Returns: [ds:FileName]
 ; Uses: ax,dx,si,di,es
  mov ax,ds
  mov es,ax
  lea si,[FileName]
  lea di,[OldFileName]
 @@Copy:
  mov al,[si]
  mov [di],al
  inc si
  inc di
  cmp al,0
  jne @@Copy
  call GetLastFileName
  lea si,[@@NewName]
  call AppendToFileName
  mov ah,56h ; rename file
  lea di,[FileName]
  lea dx,[OldFileName]
  int 21h
  jc @@Error
  ret
 @@Error:
  call TestError
 @@NewName DB '$$$$$$$$.$$$',0
ENDP
PROC DeleteOneFile
 ; Expects: [ds:FileName]
 ; Returns: [ds:FileName]
 ; Uses: ax,dx,di
  mov ah,41h
  lea dx,[FileName]
  int 21h
  jc @@Error
  call GetLastFileName
  ret
 @@Error:
  call TestError
ENDP
PROC DeleteOneDirectory
 ; Expects: cx=AmountOfEntries, [ds:FileName]
 ; Returns: cf=(1-ParentStartDir), [ds:FileName]
 ; Uses: ax,bx,cx,dx,di
  lea di,[FileName]
  dec di
 @@FindEnd:
  inc di
  cmp [byte ptr di],0
  jne @@FindEnd
  mov [byte ptr di-1],0
  mov ah,3Ah ; delete directory
  lea dx,[FileName]
  int 21h
  jc @@Error
  push cx
  mov ah,3Ch ; create file
  mov cx,00100000b ; attr - ARCfile
  int 21h
  jc @@Error
  pop dx
  mov bx,ax
  mov ax,4200h ; set file pointer
  mov cx,0
  REPT 5h
   shl dx,1
   rcl cx,1
  ENDM
  int 21h
  jc @@Error
  mov ah,40h ; truncate file
  mov cx,0
  int 21h
  jc @@Error
  mov ah,3Eh ; close file
  int 21h
  jc @@Error
  call GetLastFileName
  ret
 @@Error:
  call TestError
ENDP
PROC FindDir
 ; Expects: [ds:FileName]
 ; Returns: [ds:FileName], cf=(1-No (more) files)
 ; Uses: ax,bl,cx,dx,si,di, [ds:Dta]
  mov bl,1
  clc
  call FindFile
  jc @@Exit
  lea si,[@@BackSlash]
  call AppendToFileName
 @@Exit:
  ret
 @@BackSlash DB '\',0
ENDP
PROC FindFile
 ; Expects: [ds:FileName], bl=(0-File,1-Dir), cf=(0-First,1-Next)
 ; Returns: [ds:FileName], [ds:Dta], cf=(1-No (more) files)
 ; Uses: ax,cx,dx,si,di, [ds:Dta]
  mov ah,4Eh
  adc ah,0
  lea si,[@@WildCard]
  call AppendToFileName
  lea dx,[FileName]
  mov cx,0FFh
 @@TryAgain:
  int 21h
  jnc @@Found
  cmp ax,12h
  jne @@Error
  call GetLastFileName
  stc
  ret
 @@Found:
  cmp bl,0
  je @@File
  mov ah,4Fh
  test [byte ptr Dta+15h],00010000b
  jz @@TryAgain
  cmp [byte ptr Dta+1Eh],'.' ; current or last dir
  je @@TryAgain
  jmp @@AddName
 @@File:
  test [byte ptr Dta+15h],11011110b
  mov ah,4Fh
  jnz @@TryAgain
 @@AddName:
  call GetLastFileName
  lea si,[Dta+1Eh] ; File name
  call AppendToFileName
  clc
  ret
 @@Error:
  call GetLastFileName
  call TestError
 @@WildCard DB '*.*',0
ENDP
PROC TestError
 ; Expects: ax=ErrorCode, [ds:FileName]
 ; Returns: (none)
 ; Uses: -
  lea si,[@@MsgUnknown]
  cmp ah,0
  jne @@Print
  lea si,[@@MsgFileNotFound]
  cmp al,2h
  je @@Print
  lea si,[@@MsgPathNotFound]
  cmp al,3h
  je @@Print
  lea si,[@@MsgTooManyFiles]
  cmp al,4h
  je @@Print
  lea si,[@@MsgAccessDenied]
  cmp al,5h
  je @@Print
  lea si,[@@MsgInvalidFormat]
  cmp al,0Bh
  je @@Print
  lea si,[@@MsgInvalidDrive]
  cmp al,0Fh
  je @@Print
  lea si,[@@MsgCannotRmDir]
  cmp al,10h
  je @@Print
  lea si,[@@MsgUnknown]
 @@Print:
  call WriteStr
  lea si,[FileName]
  call WriteStr
  lea si,[@@CrLf]
  call WriteStr
  call FreeBuffer
  call ClearFileName
  mov ax,4C04h
  int 21h
 @@MsgUnknown DB 'Unknown Error: ',0
 @@MsgFileNotFound DB 'File not found: ',0
 @@MsgPathNotFound DB 'Path not found: ',0
 @@MsgTooManyFiles DB 'Too many open files: ',0
 @@MsgAccessDenied DB 'Access denied: ',0
 @@MsgInvalidFormat DB 'Invalid format: ',0
 @@MsgInvalidDrive DB 'Invalid drive specified: ',0
 @@MsgCannotRmDir DB 'Cannot remove directory: ',0
 @@CrLf DB 0Dh,0Ah,0
ENDP
PROC AppendToFileName
 ; Expects: [ds:si] - ASCIIZ String
 ; Returns: [ds:FileName] - ASCIIZ String
 ; Uses: al,si,di
  lea di,[FileName]
  dec di
 @@FindEnd:
  inc di
  cmp [byte ptr di],0
  jne @@FindEnd
 @@AddChar:
  mov al,[si]
  mov [di],al
  cmp al,0
  je @@Exit
  inc si
  inc di
  cmp di,OFFSET FileName+MaxFileNameLen
  jb @@AddChar
  lea si,[@@MsgStrOverflow]
  call FreeBuffer
  call ClearFileName
  call WriteStr
  mov ax,4C03h
  int 21h
 @@Exit:
  ret
 @@MsgStrOverflow DB 'Error: File name too long.',0Dh,0Ah,0
ENDP
PROC GetLastFileName
 ; Expects: [ds:FileName] - ASCIIZ String
 ; Returns: [ds:FileName] - ASCIIZ String, cf=(1-StrUnderflow)
 ; Uses: ax,di
  lea di,[FileName]
  dec di
 @@FindEnd:
  inc di
  cmp [byte ptr di],0
  jne @@FindEnd
  mov ax,di
  dec di
  sub ax,OFFSET FileName
  cmp al,[FileNameStart]
  jc @@Exit
 @@FindSlash:
  dec di
  cmp [byte ptr di],'\'
  jne @@FindSlash
  mov [byte ptr di+1],0
  clc
 @@Exit:
  ret
ENDP
PROC WriteStr
 ; Expects: [ds:si] - ASCIIZ String
 ; Returns: -
 ; Uses: ax,dl,si
  mov ah,2h
 @@Loop:
  mov dl,[si]
  cmp dl,0
  je @@Exit
  int 21h
  inc si
  jmp @@Loop
 @@Exit:
  ret
ENDP

; Undefined data
 FileNameStart DB ?
 BufSeg DW ?
 BufLen DW ? ; in bytes
 FileName DB MaxFileNameLen DUP(?)
 OldFileName DB MaxFileNameLen DUP(?)

; Program Stack
  EVENDATA
  DB 100h DUP(?)
  LABEL StackEnd NEAR

; End of program
  ProgramLen= $-Start+100h
END Start
