-- DFDeleteImpl.Mesa, last edit February 9, 1983 11:57 am
-- Pilot 6.0/ Mesa 7.0

DIRECTORY
  CWF: TYPE USING [FWF0, FWF1, SetWriteProcedure, WF0, WF1, WFCR],
  DFSubr: TYPE USING [AllocateDFSeq, DF, DFSeq, FlattenDF, FreeDFSeq, ReadInDir,
  	SortByCap, SortByFileName],
  LongString: TYPE USING [AppendString],
  IO: TYPE USING[Handle, PutChar, PutF, rope, UserAbort],
  Rope: TYPE USING[Fetch, Length, ROPE, Text],
  RopeInline: TYPE USING[InlineFlatten],
  STPSubr: TYPE USING [StopSTP],
  Stream: TYPE USING [Delete, Handle, PutChar],
  Subr: TYPE USING [AbortMyself, Any, CheckForModify, debugflg, errorflg,
  	GetCreateDate, MakeTTYProcs, NewStream, PackedTime, PrintGreeting,
  	strcpy, SubrStop, TTYProcs, Write],
  Time: TYPE USING [Current],
  UECP: TYPE USING[Argv, Parse],
  UserExec: TYPE USING[AcquireResource, AskUser, CommandProc, GetStreams, 
  	RegisterCommand, ReleaseResource];

DFDeleteImpl: PROGRAM
IMPORTS CWF, DFSubr, IO, LongString, Rope, RopeInline, 
	STPSubr, Stream, Subr, Time, UECP, UserExec = {

-- MDS usage!!
stdout: IO.Handle;
-- end of mds


-- max number of files in all DF file we want to ignore
MAXDELETEFILE: CARDINAL = 800;


Main: UserExec.CommandProc = TRUSTED {
	ENABLE UNWIND => [] ← UserExec.ReleaseResource[$DFDelete];
	h: Subr.TTYProcs;
	in, out: IO.Handle;
	[in, out] ← UserExec.GetStreams[exec];
	[] ← UserExec.AcquireResource[$DFDelete, "DFDelete", exec];
	h ← Subr.MakeTTYProcs[in, out, exec, MyConfirm];
	DFDeleteUsingProcs[h, event.commandLine];
	[] ← UserExec.ReleaseResource[$DFDelete];
	};

-- this is the procedure called by the Simple Executive

DFDeleteUsingProcs: PROC[h: Subr.TTYProcs, commandLine: Rope.ROPE] = {
dfseq: DFSubr.DFSeq ← NIL;
tok: Rope.ROPE;
starttime: Subr.PackedTime;
alsoDeleteReadOnly: BOOL ← FALSE;
argv: UECP.Argv ← UECP.Parse[commandLine];

	Cleanup: PROC = {
	DFSubr.FreeDFSeq[@dfseq];
	STPSubr.StopSTP[];
	Subr.SubrStop[];
	};

stdout ← h.out;
[] ← CWF.SetWriteProcedure[MyPutChar];
starttime ← Time.Current[];
Subr.errorflg ← Subr.debugflg ← FALSE;
Subr.PrintGreeting["DFDelete"L];
dfseq ← DFSubr.AllocateDFSeq[maxEntries: MAXDELETEFILE, zoneType: shared];
{
	ENABLE {
	Subr.AbortMyself => {
		CWF.WF0["DFDelete Aborted.\n"L];
		GOTO leave;
		};
	UNWIND => Cleanup[];
	};

IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
FOR p: CARDINAL IN [1 .. argv.argc) DO
	tok ← argv[p];
	IF tok.Fetch[0] = '/ THEN {
		IF tok.Fetch[1] = 'r THEN 
			alsoDeleteReadOnly ← TRUE
		ELSE h.out.PutF["Unknown switch '%s'.\n", IO.rope[tok]];
		}
	ELSE {
		str: STRING ← [100];
		flat: Rope.Text ← RopeInline.InlineFlatten[tok];
		Subr.strcpy[str, LOOPHOLE[flat]];
		IF NOT Subr.Any[str, '.] THEN 
			LongString.AppendString[str, ".DF"L];
		DFSubr.FlattenDF[dfseq: dfseq, dffilename: str, h: h];
		};
	ENDLOOP;
IF tok.Length[] = 0 THEN GOTO usage;
STPSubr.StopSTP[];
IF dfseq.size = 0 THEN GOTO leave;
DFSubr.ReadInDir[dfseq];
DFSubr.SortByCap[dfseq, 0, dfseq.size-1];
FOR i: CARDINAL IN [0 .. dfseq.size) DO
	dfseq[i].need ← NOT dfseq[i].readonly OR alsoDeleteReadOnly;
	ENDLOOP;
-- only ReadWrite files are marked need = TRUE
CWF.WF0["Reading create dates on local disk ... "L];
CheckCreateDates[dfseq];
CWF.WF0["Done.\n"L];
DFSubr.SortByFileName[dfseq, 0, dfseq.size-1, TRUE];
GenerateDelete[dfseq, alsoDeleteReadOnly, h];
EXITS
usage => CWF.WF0["Usage: DFDelete dffile(s).\n"L];
leave => NULL;
};
starttime ← Time.Current[] - starttime;
CWF.WF1["\nTotal elapsed time for DFDelete %lr."L,@starttime];
IF Subr.errorflg THEN CWF.WF0["\tErrors logged.\n"L];
CWF.WFCR[];
Cleanup[];
};

CheckCreateDates: PROC[dfseq: DFSubr.DFSeq] = {
df: DFSubr.DF;
FOR i: CARDINAL IN [0 .. dfseq.size) DO
	df ← @dfseq[i];
	IF df.need AND df.presentonlocaldisk 
	AND df.createtime ~= 0 THEN {
		IF Subr.GetCreateDate[df.cap] ~= df.createtime THEN
			df.need ← FALSE;
		};
	ENDLOOP;
};

GenerateDelete: PROC[dfseq: DFSubr.DFSeq, alsoDeleteReadOnly: BOOL, h: Subr.TTYProcs] = {
linesh: Stream.Handle ← NIL;
df: DFSubr.DF;

	WP: PROC[ch: CHAR] = {
	h.out.PutChar[ch];
	Stream.PutChar[linesh, ch];
	};

FOR i: CARDINAL IN [0 .. dfseq.size) DO
	df ← @dfseq[i];
	IF df.need AND df.presentonlocaldisk 
	AND df.createtime ~= 0 THEN {
		IF linesh = NIL THEN {
			CWF.WF1["(You should delete these files since %s\n"L,
				IF alsoDeleteReadOnly THEN ""L ELSE "they appeared non-ReadOnly and "L];
			CWF.WF0["their createdates are equal to those in the DF file(s).)\n"L];
			IF NOT Subr.CheckForModify["Line.CM"L, h] THEN SIGNAL Subr.AbortMyself;
			CWF.WF0["Execute: "L];
			linesh ← Subr.NewStream["Line.CM"L, Subr.Write];
			CWF.FWF0[WP, "Delete "L];
			};
		CWF.FWF1[WP, "%s "L, dfseq[i].shortname];
		};
	ENDLOOP;
IF linesh ~= NIL THEN {
	Stream.Delete[linesh];
	CWF.WF0["\n\n(Delete command also written on 'Line.CM'.)\n"L];
	};
};

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["DFDelete.~", Main];
}.