-- DeleteAllImpl.Mesa, last edit February 6, 1983 9:17 pm -- Pilot 6.0/ Mesa 7.0 -- Switch Meaning -- /a do NOT confirm the deletes (and CheckForOverwrite is FALSE) -- /n do NOT use BasicCoPilotVolumeFiles.DF (or BasicClientVolumeFiles.DF) DIRECTORY CWF: TYPE USING [SetWriteProcedure, SWF1, WF0, WF1, WF3, WFCR], DFSubr: TYPE USING [AllocateDFSeq, DF, DFSeq, FlattenDF, FreeDFSeq, LookupDF, NextDF, SortByFileName], Directory: TYPE USING [DeleteFile], File: TYPE USING [Capability, Unknown], IO: TYPE USING[Handle, PutChar, PutFR, PutRope, string, UserAbort], LongString: TYPE USING [EquivalentString], Rope: TYPE USING[ROPE, Text], RopeInline: TYPE USING[InlineFlatten], UnsafeSTP: TYPE USING [Error, Handle], STPSubr: TYPE USING [GeneralOpen, StopSTP], Stream: TYPE USING [Delete, EndOfStream, Handle], Subr: TYPE USING [AbortMyself, CheckForModify, CopyString, debugflg, EndsIn, EnumerateDirectory, errorflg, FileError, GetID, MakeTTYProcs, PackedTime, PrintGreeting, strcpy, SubrStop, SubStrCopy, TTYProcs], Time: TYPE USING [Current], UECP: TYPE USING[Argv, Parse], UserExec: TYPE USING[AcquireResource, AskUser, CommandProc, ExecHandle, GetStreams, RegisterCommand, ReleaseResource], Volume: TYPE USING [GetType, systemID, Type]; DeleteAllImpl: PROGRAM IMPORTS CWF, DFSubr, Directory, File, IO, LongString, Rope, RopeInline, STP: UnsafeSTP, STPSubr, Stream, Subr, Time, UECP, UserExec, Volume = { -- MDS USAGE !!! useremotedeletelist: BOOL; dontconfirm: BOOL; argv: UECP.Argv; parm: CARDINAL ← 1; stdout: IO.Handle; -- endof MDS usage -- max number of files on local disk maxdirsize: CARDINAL = 1100; -- total # entries from Basic...VolumeFiles.DF MAXSTANDARDDELETEFILE: CARDINAL = 1200; -- max number of files in each DF file we want to ignore (listed on command file) MAXDELETEFILE: CARDINAL = 1200; Main: UserExec.CommandProc = TRUSTED { ENABLE UNWIND => [] ← UserExec.ReleaseResource[$DeleteAll]; h: Subr.TTYProcs; in, out: IO.Handle; [in, out] ← UserExec.GetStreams[exec]; [] ← UserExec.AcquireResource[$DeleteAll, "DeleteAll", exec]; h ← Subr.MakeTTYProcs[in, out, exec, MyConfirm]; DeleteAllUsingProcs[h, event.commandLine]; [] ← UserExec.ReleaseResource[$DeleteAll]; }; DeleteAllUsingProcs: PROC[h: Subr.TTYProcs, commandLine: Rope.ROPE] = { dfseq: DFSubr.DFSeq; tok: STRING ← [100]; starttime: Subr.PackedTime; elapt: LONG CARDINAL; Cleanup: PROC = { DFSubr.FreeDFSeq[@dfseq]; }; useremotedeletelist ← TRUE; stdout ← h.out; [] ← CWF.SetWriteProcedure[MyPutChar]; dontconfirm ← FALSE; starttime ← Time.Current[]; Subr.errorflg ← Subr.debugflg ← FALSE; Subr.PrintGreeting["DeleteAll"L]; dfseq ← DFSubr.AllocateDFSeq[maxEntries: maxdirsize, zoneType: shared]; { ENABLE { STP.Error => { CWF.WF0["FTP Error. "L]; IF error ~= NIL THEN CWF.WF1["message: %s\n"L, error]; Subr.errorflg ← TRUE; GOTO leave; }; Subr.AbortMyself => { CWF.WF0["DeleteAll Aborted.\n"L]; GOTO leave; }; UNWIND => Cleanup[]; }; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; argv ← UECP.Parse[commandLine]; DeleteAll[dfseq, h]; EXITS leave => NULL; }; STPSubr.StopSTP[]; elapt ← starttime; elapt ← Time.Current[] - elapt; CWF.WF1["\nTotal elapsed time for DeleteAll %lr."L,@elapt]; IF Subr.errorflg THEN CWF.WF0["\tErrors logged.\n"L]; CWF.WFCR[]; Cleanup[]; Subr.SubrStop[]; }; DeleteAll: PROC[dfseq: DFSubr.DFSeq, h: Subr.TTYProcs] = { i, ndel: CARDINAL; header: BOOL ← FALSE; short: STRING ← [100]; -- if look.presentonlocaldisk is true, don't delete it FillInDFSeq[dfseq]; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; FOR i IN [0.. dfseq.size) DO dfseq[i].presentonlocaldisk ← FALSE;-- assume we will delete file ENDLOOP; DFSubr.SortByFileName[dfseq, 0, dfseq.size - 1]; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; MatchAgainstFile[dfseq, h]; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; IF useremotedeletelist THEN HandleRemoteDeleteList[dfseq, h]; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; -- determine which are to be deleted FOR i IN [0..dfseq.size) DO -- check against built in list just in case IF NOT dfseq[i].presentonlocaldisk THEN dfseq[i].presentonlocaldisk ← MatchesDontDeleteList[dfseq[i].shortname]; ENDLOOP; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; -- print out list of those to be left on disk CWF.WF0["These files will be left on the disk:\n"L]; FOR i IN [0..dfseq.size) DO IF dfseq[i].presentonlocaldisk THEN CWF.WF1["%s "L, dfseq[i].shortname]; ENDLOOP; -- print out list of those to be deleted CWF.WF0["\n\nThese files will be deleted:\n\n"L]; FOR i IN [0..dfseq.size) DO IF NOT dfseq[i].presentonlocaldisk THEN { IF NOT header THEN { CWF.WF0["Delete.~"L]; header ← TRUE; }; -- these will be deleted CWF.WF1[" %s"L, dfseq[i].shortname]; }; ENDLOOP; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; IF NOT dontconfirm THEN CWF.WF0["\n\nType 'y' or CR to delete, 'n' to skip, and 'q' to quit.\n"L]; ndel ← 0; FOR i IN [0..dfseq.size) DO IF NOT dfseq[i].presentonlocaldisk THEN { r: Rope.ROPE; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; r ← IO.PutFR["Delete %s ", IO.string[dfseq[i].shortname]]; IF NOT dontconfirm THEN { ch: CHAR; ch ← h.Confirm[h.in, h.out, h.data, r, 'y]; IF ch = 'q THEN { EXIT; }; IF ch ~= 'y THEN { LOOP; }; } ELSE h.out.PutRope[r]; CWF.WF0["Yes ... "L]; -- deletes the file Subr.strcpy[short, dfseq[i].shortname]; IF NOT Subr.CheckForModify[short, h] THEN { CWF.WF0[" ... can't be modified.\n"L]; LOOP; }; Directory.DeleteFile[dfseq[i].shortname ! File.Unknown => { -- bugs in directory package CWF.WF0["File/Directory Error.\n"L]; CONTINUE; } ]; CWF.WF0["Done.\n"L]; ndel ← ndel + 1; }; ENDLOOP; CWF.WF1["%u files deleted.\n"L, @ndel]; }; -- returns TRUE if the file should not be deleted -- these files are NOT deleted by DeleteAll MatchesDontDeleteList: PROC[p: LONG STRING] RETURNS[bool: BOOL] = { OPEN LongString; bool ← FALSE; IF EquivalentString[p, "Binder.Log"L] OR EquivalentString[p, "Compiler.Log"L] OR EquivalentString[p, "Debug.Log"L] OR EquivalentString[p, "Debuggee.outload"L] OR EquivalentString[p, "Debugger.outload"L] OR EquivalentString[p, "DeleteAll.Bcd"L] OR EquivalentString[p, "Executive.Bcd"L] OR EquivalentString[p, "Executive.DontDeleteMe"L] OR EquivalentString[p, "FileTool.logD"L] OR EquivalentString[p, "SimpleExec"L] OR EquivalentString[p, "SimpleExec.Log"L] OR EquivalentString[p, "user.cm"L] THEN bool ← TRUE; }; HandleRemoteDeleteList: PROC[dfdirseq: DFSubr.DFSeq, h: Subr.TTYProcs] ={ sfn: STRING ← [100]; type: Volume.Type; dfseq: DFSubr.DFSeq ← NIL; { ENABLE UNWIND => DFSubr.FreeDFSeq[@dfseq]; dfseq ← DFSubr.AllocateDFSeq[maxEntries: MAXSTANDARDDELETEFILE, zoneType: shared]; type ← Volume.GetType[Volume.systemID]; CWF.SWF1[sfn, "[Indigo]<CedarLib>DFFiles>Basic%sVolumeFiles.DF"L, IF type = normal THEN "Client"L ELSE "CoPilot"L]; CWF.WF1["Using delete list from '%s'\n"L, sfn]; DFSubr.FlattenDF[dfseq: dfseq, dffilename: sfn, checkForOverwrite: NOT dontconfirm, h: h]; DontDeleteTheseDFFiles[dfdirseq, dfseq, h]; DFSubr.FreeDFSeq[@dfseq]; CWF.WFCR[]; }}; MatchAgainstFile: PROC[dfdirseq: DFSubr.DFSeq, h: Subr.TTYProcs] = { tok: STRING ← [100]; flat: Rope.Text; WHILE parm < argv.argc DO flat ← RopeInline.InlineFlatten[argv[parm]]; Subr.strcpy[tok, LOOPHOLE[flat]]; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; IF tok[0] = '@ THEN { -- is an escaped at-sign sh: Stream.Handle; Subr.SubStrCopy[tok, tok, 1]; [sh] ← STPSubr.GeneralOpen[filename: tok, h: h ! Subr.FileError => { CWF.WF1["Error - Can't open '%s'\n"L, tok]; GOTO out } ]; IF Subr.EndsIn[tok, ".df"L] THEN { dfseq: DFSubr.DFSeq ← NIL; { ENABLE UNWIND => DFSubr.FreeDFSeq[@dfseq]; dfseq ← DFSubr.AllocateDFSeq[maxEntries: MAXDELETEFILE, zoneType: shared]; DFSubr.FlattenDF[dfseq: dfseq, dffilename: tok, checkForOverwrite: NOT dontconfirm, h: h]; DontDeleteTheseDFFiles[dfdirseq, dfseq, h]; DFSubr.FreeDFSeq[@dfseq]; }} ELSE DontDeleteTheseFiles[sh, dfdirseq]; Stream.Delete[sh]; EXITS out => NULL; } ELSE IF tok[0] = '- OR tok[0] = '/ THEN { -- is an option SELECT tok[1] FROM 'a, 'A => dontconfirm ← TRUE; 'n, 'N => useremotedeletelist ← FALSE; ENDCASE =>CWF.WF1["Error - malformed argument '%s'.\n"L, tok]; } ELSE { df: DFSubr.DF; df ← DFSubr.LookupDF[dfdirseq, tok]; IF df = NIL THEN CWF.WF1["File %s is not on local disk.\n"L, tok] ELSE -- dont delete it df.presentonlocaldisk ← TRUE; }; ENDLOOP; }; DontDeleteTheseFiles: PROC[sh: Stream.Handle, dfseq: DFSubr.DFSeq] = { df: DFSubr.DF; str: STRING ← [100]; DO str.length ← 0; Subr.GetID[sh, str ! Stream.EndOfStream => { str.length ← 0; EXIT; } ]; IF str.length = 0 THEN LOOP; df ← DFSubr.LookupDF[dfseq, str]; IF df = NIL THEN CWF.WF1["File %s is not on local disk.\n"L, str] ELSE -- dont delete it df.presentonlocaldisk ← TRUE; ENDLOOP; }; -- dfdirseq is for the local directory -- dfseq is the exception list -- dfseq must already be filled DontDeleteTheseDFFiles: PROC[dfdirseq, dfseq: DFSubr.DFSeq, h: Subr.TTYProcs] = { df, dfdir: DFSubr.DF; host: LONG STRING; FOR i: CARDINAL IN [0.. dfseq.size) DO IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; df ← @dfseq[i]; IF df.host ~= NIL THEN host ← df.host; dfdir ← DFSubr.LookupDF[dfdirseq, df.shortname]; IF dfdir = NIL THEN { IF df.directory ~= NIL THEN CWF.WF3["File [%s]<%s>%s not on local disk.\n"L, host, df.directory, df.shortname] ELSE CWF.WF1["File %s not on local disk.\n"L, df.shortname]; } ELSE -- dont delete it dfdir.presentonlocaldisk ← TRUE; ENDLOOP; }; FillInDFSeq: PROC[dfseq: DFSubr.DFSeq] = { AddDir: PROC[cap: File.Capability, name: STRING] RETURNS[BOOL]={ df: DFSubr.DF; df ← DFSubr.NextDF[dfseq]; IF df = NIL THEN { CWF.WF0["Too many files on local disk.\n"L]; RETURN[TRUE]; }; df.shortname ← Subr.CopyString[name, dfseq.dfzone]; df.presentonlocaldisk ← TRUE; df.cap ← cap; RETURN[FALSE]; }; Subr.EnumerateDirectory[AddDir]; CWF.WF1["%u files on the local disk.\n"L, @dfseq.size]; }; MyConfirm: PROC[in, out: IO.Handle, data: REF ANY, msg: Rope.ROPE, dch: CHAR] RETURNS[CHAR] = { value: ATOM; value ← UserExec.AskUser[msg: msg, exec: NARROW[data], keyList: LIST[$Yes, $No, $All, $Local, $Quit]]; -- order is important SELECT value FROM $All => RETURN['a]; $Local => RETURN['l]; $No => RETURN['n]; $Quit => RETURN['q]; $Yes => RETURN['y]; ENDCASE => ERROR; }; MyPutChar: PROC[ch: CHAR] = { stdout.PutChar[ch]; }; UserExec.RegisterCommand["DeleteAll.~", Main]; }.