{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * NUKE.PAS by Shane Kerr * * Deletes a subdirectory and everything it contains. * * Nuke for DOS written Turbo Pascal 5.5 * * Nuke for Windows written using Turbo Pascal for Windows 1.0 * * Version 1.95 November 23, 1991 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} program Nuke; uses {$IFDEF MsDos} DOS; {$ENDIF} {$IFDEF Windows} WinCRT, WinDOS, Strings; {$ENDIF} const MajorVer = '1'; { Current major version number } MinorVer = '95'; { Current minor version number } Year = 1991; { Release year } {$IFDEF MsDos} fsDirectory = 64; { Set directory length } faReadOnly = ReadOnly; { Set directory flags } faHidden = Hidden; faSysFile = SysFile; faVolumeID = VolumeID; faDirectory = Directory; faArchive = Archive; faAnyFile = AnyFile; {$ENDIF} {$IFDEF MsDos} type TRegisters = Registers; { Used for DOS calls } TSearchRec = SearchRec; { Used for search record } {$ENDIF} {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * procedure FCBDeleteFile (FileSpec : string); * Deletes files using the MS-DOS FCB function (from Version 1.0). * parameters: filespec, file(s) to be deleted * notes: Can only delete files in the current directory. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure FCBDeleteFile (filespec : string); type TFCB = record drive : char; { 0 = default, 1 = A, 2 = B } name : array[0..7] of char; { File name } ext : array[0..2] of char; { File extension } curblk : word; { Current block number } recsize : word; { Logical record size in bytes } filsize : longint; { File size in bytes } date : word; { Date file was last written } resv : array[0..10] of byte; { Reserved for DOS } currec : byte; { Current record in block } random : longint; { Random record number } end; var FCB : TFCB; Regs : TRegisters; TempStr : string; NameSeg, NameOfs : word; FCBSeg, FCBOfs : word; begin { Get segment and offset of the filespec } TempStr := filespec + chr(0); NameSeg := seg(TempStr); NameOfs := ofs(TempStr) + 1; FCBSeg := seg(FCB); FCBOfs := ofs(FCB); { Do the actual DOS calls } Regs.AX := $2900; Regs.DS := NameSeg; Regs.SI := NameOfs; Regs.ES := FCBSeg; Regs.DI := FCBOfs; MsDos(Regs); { Parse file to FCB } Regs.DS := FCBSeg; Regs.DX := FCBOfs; Regs.AH := $13; MsDos(Regs); { Delete file (FCB) } end; { FCBDeleteFile } {$IFDEF MsDos} {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * procedure ClearKb * Clears the keyboard buffer * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure ClearKb; var Regs : TRegisters; begin Regs.AH := $01; Intr($16, Regs); while ((Regs.Flags and FZero) = 0) do begin Regs.AH := $00; Intr($16, Regs); Regs.AH := $01; Intr($16, Regs); end; end; { procedure ClearKb } {$ENDIF} {$IFDEF MsDos} {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * procedure WaitKey * Waits for a key press * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure WaitKey; var Regs : TRegisters; begin Regs.AH := $00; Intr($16, Regs); end; { procedure WaitKey } {$ENDIF} {$IFDEF MsDos} {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * function IsRedirected : boolean; * Determines whether a program's input or output is redirected * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function IsRedirected : boolean; var Regs : Registers; { Register values } StdIn : ^Byte; { Standard input } StdOut : ^Byte; { Standard output } begin Regs.AH := $62; { Get segment address of PSP } MsDos(Regs); StdIn := Ptr(Regs.BX, $18); { Point to StdIn value } StdOut := Ptr(Regs.BX, $19); { Point to StdOut value } { Return TRUE if StdIn is the same as StdOut } IsRedirected := (StdIn^ <> StdOut^); end; {$ENDIF} {$IFDEF MsDos} {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * function NumRows : byte; * Returns the number of rows on the screen * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function NumRows : byte; var ScreenWidth : word absolute $0040:$004A; ScreenSize : word absolute $0040:$004C; begin NumRows := (((ScreenSize div 1000) * 1000) div 2) div ScreenWidth; end; {$ENDIF} {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * function NukeDir (directory : string) : boolean; * * Destroys the specified directory and all it contains recursively * * parameters: directory, path of the directory to be destroyed * * remove, TRUE to remove directory * * display, TRUE to display files as they are deleted * * pause, TRUE to pause after each screen * * attr, file search attributes to delete * * lines, number of lines displayed so far * * returns: TRUE if directory is removed, FALSE otherwise * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function NukeDir (directory : string; remove, display, pause : boolean; attrib : word; var lines : word) : boolean; var OrgDir : string[fsDirectory]; { Saved original directory } SrchRec : TSearchRec; { For file searches } Dummy : boolean; Handle : file; { File handle (for attrib change) } begin GetDir(0, OrgDir); { Get original directory } ChDir(directory); { Change to target directory } { If display isn't on, just delete everything (grumble) } if (not display) then FCBDeleteFile('????????.???'); { Delete all files } { Find first file present } FindFirst('*.*', faDirectory or attrib, SrchRec); { Loop and nuke any subdirectories found } repeat if (((SrchRec.Attr and faDirectory) <> 0) and (DosError = 0) and {$IFDEF MsDos} (SrchRec.Name[1] <> '.')) then {$ENDIF} {$IFDEF Windows} (SrchRec.Name[0] <> '.')) then {$ENDIF} begin Assign(Handle, SrchRec.Name); SetFAttr(Handle, faDirectory); Dummy := NukeDir(SrchRec.Name, TRUE, Display, Pause, Attrib, Lines); end else if ((DosError = 0) and {$IFDEF MsDos} (SrchRec.Name[1] <> '.') and {$ENDIF} {$IFDEF Windows} (SrchRec.Name[0] <> '.') and {$ENDIF} (((SrchRec.Attr and Attrib) <> 0) or (Attrib = 0))) then begin Assign(Handle, SrchRec.Name); SetFAttr(Handle, 0); Erase(Handle); { If displaying, then show name and increase line count } if (Display) then begin WriteLn(' Deleting ', Directory, '\', SrchRec.Name); Inc(Lines); end; { If pausing, check line count } if (Pause and ((Lines mod (NumRows - 2)) = 0)) then begin Write('Press any key to continue...'); WaitKey; WriteLn; end; end; { if block } FindNext(SrchRec); until (DosError <> 0); { If original directory is current, change to parent } if (OrgDir = Directory) then ChDir('..') else if (pos(Directory, OrgDir) = 1) then begin ChDir(Directory); ChDir('..'); end else ChDir(OrgDir); { Restore directory } NukeDir := FALSE; if (Remove) then begin {$I-} RmDir(Directory); { Kill target directory } if (IOResult = 0) then NukeDir := TRUE; {$I+} end; end; { function NukeDir } {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * function ToUpper (Str : string) : string; * * Convert string to upper case * * parameters: Str, any string * * returns: uppercase value of the string * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function ToUpper (Str : string) : string; var i : integer; Temp : string; begin Temp := str; for i := 1 to length(Str) do Temp[i] := UpCase(Temp[i]); ToUpper := Temp; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * function ListFiles (directory : string) : integer * * Lists files and attributes in the specified directory below * * parameters: directory, directory to start listing at * * returns: number of files listed * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function ListFiles (directory : string) : integer; var OrgDir : string; { Original directory } CurDir : string; { Current directory } SearchRec : TSearchRec; { Used to find filespecs } NumListed : Integer; { Number of files listed } Attr: word; { Attributes to remove } begin NumListed := 0; { Number of files listed } GetDir(0, OrgDir); { Get original directory } ChDir(directory); { Change to target directory } GetDir(0, CurDir); { Get current directory } { Find first directory present } FindFirst('*.*', faDirectory or faReadOnly or faHidden or faSysFile, SearchRec); FindNext(SearchRec); FindNext(SearchRec); { Loop and list any files found } repeat if ((DosError = 0) and ((SearchRec.Attr and faDirectory) <> 0)) then begin NumListed := NumListed + ListFiles(SearchRec.Name); end; if (DosError = 0) then begin NumListed := NumListed + 1; Write(' ', CurDir, '\', SearchRec.Name); if ((SearchRec.Attr and faDirectory) <> 0) then Write(', directory'); if ((SearchRec.Attr and faReadOnly) <> 0) then Write(', read-only'); if ((SearchRec.Attr and faHidden) <> 0) then Write(', hidden'); if ((SearchRec.Attr and faSysFile) <> 0) then Write(', system'); WriteLn; end; { if } FindNext(SearchRec); until (DosError <> 0); ChDir(OrgDir); { Restore directory } ListFiles := NumListed; { Return number of files listed } end; { procedure ListFiles } {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * function HasSwitch (switch : string) : boolean * * Checks the command-line arguements for the specified switch * * parameters: switch, the switch to search for * * returns: TRUE if found, else FALSE * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function HasSwitch (switch : char) : boolean; var i : integer; { Index variable } begin HasSwitch := FALSE; for i := 1 to ParamCount-1 do begin if (Pos(UpCase(switch), ToUpper(ParamStr(i))) <> 0) then begin HasSwitch := TRUE; Exit; end; { if } end; { for } end; { function HasSwitch } var { main variables } UserInput : string[fsDirectory]; { user response } Answer : string; { user response } OrgDir : string[fsDirectory]; { Original directory } Target : string[fsDirectory]; { Directory to nuke } Remove : boolean; { If directory actually removed } Result : boolean; { Result of nuking } LinesShown : word; { Number of lines shown so far } Attrib : word; { File attributes to delete } begin { main program } { Print greeting } WriteLn('NUKE Directory ', MajorVer, '.', MinorVer); WriteLn(' (C)', Year, ' by Kerr'); WriteLn; { Check for DOS help command } if ((ParamCount < 1) or HasSwitch('?') or (Pos('?', ParamStr(1)) <> 0)) then begin Write('Removes a subdirectory, along with the files and '); WriteLn('subdirectories is contains'); WriteLn; WriteLn('NUKE [options] [directory]'); WriteLn; WriteLn('Options are as follows:'); WriteLn(' K Keeps the subdirectory after clearing out files.'); WriteLn(' H Deletes hidden files.'); WriteLn(' R Deletes read-only files.'); WriteLn(' S Deletes system files.'); WriteLn(' A Deletes files of all attributes.'); WriteLn(' Y No verification before NUKEing - dangerous!'); Write (' V Verbose, displays files and subdirectories they '); WriteLn('are removed - SLOW!'); WriteLn(' P Pause after each screen.'); WriteLn; WriteLn('You cannot nuke the root directory.'); WriteLn('Nuke will not Pause if you redirect the input or output.'); Exit; end; { Set number of lines displayed } LinesShown := 0; { Check for /K switch } Remove := not HasSwitch('K'); Attrib := 0; { Check for /H switch } if (HasSwitch('H')) then Attrib := Attrib or faHidden; { Check for /R switch } if (HasSwitch('R')) then Attrib := Attrib or faReadOnly; { Check for /S switch } if (HasSwitch('S')) then Attrib := Attrib or faSysFile; { Check for /A switch } if (HasSwitch('A')) then if (Attrib <> 0) then begin WriteLn('Cannot use the /A switch with other attribute switches.'); Exit; end else Attrib := faAnyFile; {$IFDEF MsDos} UserInput := ParamStr(ParamCount); {$ENDIF} {$IFDEF Windows} Write('Input directory to remove: '); ReadLn(UserInput); {$ENDIF} { Save directory and drive and try to change to new directory } GetDir(0, OrgDir); {$I-} ChDir(UserInput); if (IOResult <> 0) then begin WriteLn(' Specified directory not found!'); ChDir(OrgDir); Exit; end; {$I+} GetDir(0, Target); { Get new directory } { Display target directory and change back from it } WriteLn(' Target is ', Target); WriteLn; ChDir(OrgDir); { Restore directory } { Exit if root directory being nuked } if (length(Target) = 3) then begin WriteLn('You cannot NUKE the root directory!'); WriteLn(' (Try FORMAT...)'); Exit; end; { Double check before DECIMATING directory } if (not HasSwitch('Y')) then begin WriteLn(' Are you SURE you want to OBLITERATE this directory and'); Write(' everything in or under it?!?!? (Y/N) '); {$IFDEF MsDos} ClearKb; {$ENDIF} ReadLn(Answer); Answer := ToUpper(Answer); end; { If 'yes' or 'y' entered, or 'Y' SWITCH set, nuke that puppy } if ((answer = 'YES') or (answer = 'Y') or HasSwitch('Y')) then begin WriteLn(' Beginning now...'); Result := NukeDir(Target, Remove, HasSwitch('V'), HasSwitch('P') and (not IsRedirected), Attrib, LinesShown); WriteLn(' ...may the diety of your choice have mercy on your soul.'); end { if } else begin Result := FALSE; WriteLn(' Nothing done.'); Exit; end; { else } { List files not deleted } if (not Result) then begin WriteLn; { Display a message if the directory was SUPPOSED to be removed } if (Remove) then begin WriteLn(' NUKE failed to remove the directory.'); end else begin WriteLn(' NUKE has kept the directory.'); end; WriteLn(' The following files or directories remain in it:'); if (ListFiles(Target) = 0) then WriteLn(' None'); { Display helpful hint if the directory was SUPPOSED to be removed } if (Remove) then begin WriteLn; Write('If you wish to remove these files, try the '); WriteLn('/H, /R, and /S options,'); WriteLn(' or the /A option.'); end; end; { if } end. { main }