-- DFDiskImpl.Mesa, last edit  March 8, 1983 12:41 pm
-- Pilot 6.0/ Mesa 7.0

				
DIRECTORY
  CWF: TYPE USING [FWF1, SetWriteProcedure, WF0, WF1, WF2, WFCR],
  DFSubr: TYPE USING [AllocateDFSeq, DF, DFSeq, FreeDFSeq, InsertCRs, NextDF, SortByCap, 
  	SortByFileName, StripLongName, WriteOut],
  File: TYPE USING [Capability, Unknown],
  FileStream: TYPE USING [NoLeaderPage],
  IO: TYPE USING[Handle, PutChar, PutF, ResetUserAbort, rope, string,
  	UserAbort, UserAborted],
  LongString: TYPE USING [EquivalentString],
  Rope: TYPE USING[ROPE, Text],
  Stream: TYPE USING [Delete, Handle, PutChar],
  String: TYPE USING [EquivalentString],
  Subr: TYPE USING [AbortMyself, CheckForModify, CopyString, EndsIn, EnumerateDirectory, 
  	errorflg, GetCreateDate, GetRemoteFilenameProp, MakeTTYProcs,
  	NewStream, PrintGreeting, SubrStop, TTYProcs, Write],
  Time: TYPE USING [Current],
  UserExec: TYPE USING[AcquireResource, AskUser, CommandProc, ExecHandle, GetStreams, 
  	RegisterCommand, ReleaseResource],
  ViewerClasses: TYPE USING[Viewer],
  ViewerOps: TYPE USING[CreateViewer, FindViewer, RestoreViewer],
  WindowManager: TYPE USING[WaitCursor, UnWaitCursor];

DFDiskImpl: PROGRAM
IMPORTS CWF, DFSubr, File, FileStream, IO, LongString, Stream, 
	String, Subr, Time, UserExec, ViewerOps, WindowManager = {

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

MAXFILES: CARDINAL = 1500;

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

DFDiskUsingProcs: PROC[h: Subr.TTYProcs] = {
dfseq: DFSubr.DFSeq ← NIL;
starttime: LONG CARDINAL;
sh: Stream.Handle;
viewer: ViewerClasses.Viewer;

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

	WFSH: PROC[ch: CHAR] = {
	Stream.PutChar[sh, ch];
	};
	
{
ENABLE {
	UNWIND => Cleanup[];
	Subr.AbortMyself, IO.UserAborted => {
		h.out.ResetUserAbort[];
		CWF.WF0["\nDFDisk Aborted.\n"L];
		GOTO leave;
		};
	};
	
starttime ← Time.Current[];
stdout ← h.out;
[] ← CWF.SetWriteProcedure[MyPutChar];
Subr.PrintGreeting["DFDisk"L];
dfseq ← DFSubr.AllocateDFSeq[maxEntries: MAXFILES, zoneType: shared];
ConstructDF[dfseq];
IF NOT Subr.CheckForModify["Disk.DF"L, h] THEN SIGNAL Subr.AbortMyself;
FillInCreateDates[dfseq, h];
sh ← Subr.NewStream["Disk.DF"L, Subr.Write];
CWF.FWF1[WFSH, "// Contents of entire volume on %lt\n"L, @starttime];
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
DFSubr.SortByFileName[dfseq, 0, dfseq.size-1];
DFSubr.InsertCRs[dfseq];
DFSubr.WriteOut[dfseq, NIL, sh, FALSE];
Stream.Delete[sh];
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
CWF.WF0["Listing created on file 'Disk.DF'\n"L];
viewer ← ViewerOps.FindViewer["Disk.DF"];
IF viewer ~= NIL THEN {
	IF viewer.newVersion THEN 
		CWF.WF0["Warning - you are already editing Disk.DF.\n"L]
	ELSE
		ViewerOps.RestoreViewer[viewer];
	}
ELSE CreateANewViewer["Disk.DF", h.out];
starttime ← Time.Current[] - starttime;
CWF.WF1["\nTotal elapsed time for DFDisk %lr."L,@starttime];
IF Subr.errorflg THEN CWF.WF0["\tErrors logged.\n"L];
CWF.WFCR[];
GOTO leave;
EXITS
leave => Cleanup[];
}};

CreateANewViewer: PROC [name: Rope.Text, out: IO.Handle] = {
	viewer: ViewerClasses.Viewer;
	WindowManager.WaitCursor[];
	viewer ← ViewerOps.CreateViewer[flavor: $Text, info: [name: name,
			file: LOOPHOLE[name], iconic: FALSE, column: left]];
	out.PutF["Created Viewer: %s\n", IO.rope[name]];
	WindowManager.UnWaitCursor[];
	};
	
ConstructDF: PROC[dfseq: DFSubr.DFSeq] = {
	
	AddDir: PROC[cap: File.Capability, name: STRING] RETURNS[BOOL] = {
	df: DFSubr.DF;
	IF ThrowAwayThisFile[name] THEN RETURN[FALSE];
	df ← DFSubr.NextDF[dfseq];
	IF df = NIL THEN RETURN[TRUE]; 	-- too big
	df.shortname ← Subr.CopyString[name, dfseq.dfzone];
	df.presentonlocaldisk ← TRUE;
	df.cap ← cap;
	RETURN[FALSE];
	};

Subr.EnumerateDirectory[AddDir];
};

FillInCreateDates: PROC[dfseq: DFSubr.DFSeq, h: Subr.TTYProcs] = {
df: DFSubr.DF;
host: STRING ← [30];
directory: STRING ← [100];
fullname: STRING ← [125];
short: STRING ← [100];
time: LONG CARDINAL;

	FillDefs: PROC = {
	IF df.host = NIL THEN df.host ← Subr.CopyString["Unknown"L, dfseq.dfzone];
	IF df.directory = NIL THEN df.directory ← Subr.CopyString["Unknown"L, dfseq.dfzone];
	};

DFSubr.SortByCap[dfseq, 0, dfseq.size-1];
time ← Time.Current[];
FOR i: CARDINAL IN [0..dfseq.size) DO
	df ← @dfseq[i];
	IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
	IF NOT df.presentonlocaldisk THEN LOOP;
	[create: df.createtime] ← Subr.GetCreateDate[df.cap
		! FileStream.NoLeaderPage => { 
			FillDefs[]; 
			LOOP
			};
		File.Unknown => {
			h.out.PutF["ERROR File.Unknown for %s\n", IO.string[df.shortname]];
			FillDefs[]; 
			LOOP
			}];
	Subr.GetRemoteFilenameProp[df.cap, fullname];
	IF fullname.length = 0 THEN {
		FillDefs[]; 
		LOOP
		};
	[] ← DFSubr.StripLongName[fullname, host, directory, short];
	IF NOT LongString.EquivalentString[short, df.shortname] THEN { 
		FillDefs[]; 
		LOOP
		};
	IF host.length > 0 AND directory.length > 0 THEN {
		df.host ← Subr.CopyString[host, dfseq.dfzone];
		df.directory ← Subr.CopyString[directory, dfseq.dfzone];
		}
	ELSE FillDefs[];
	ENDLOOP;
time ← Time.Current[] - time;
CWF.WF2["Took %lu seconds to read %u leader pages.\n"L, @time, @dfseq.size];
};

-- don't record these files in the df file
ThrowAwayThisFile: PROC[p: STRING] RETURNS[bool: BOOL] ={
transferFileList: ARRAY [0.. 5) OF STRING ← [
	"disk.df"L,
	"Executive.DontDeleteMe"L,
	"FileTool.logD"L,
	"line.cm"L,
	"SimpleExec"L
	];
bool ← Subr.EndsIn[p,".outload"L]
	OR Subr.EndsIn[p,"$"L]
	OR Subr.EndsIn[p,".errlog"L]
	OR Subr.EndsIn[p,".log"L]
	OR Subr.EndsIn[p,".pgslog"L]
	OR Subr.EndsIn[p,".echo"L]
	OR Subr.EndsIn[p,".tipc"L]
	OR Subr.EndsIn[p,".dontdeleteme"L]
	OR Subr.EndsIn[p,".tmp"L];
IF NOT bool THEN {
	FOR i: CARDINAL IN [0.. LENGTH[transferFileList]) DO
		IF String.EquivalentString[p, transferFileList[i]] THEN
			RETURN[TRUE];
		ENDLOOP;
	RETURN[FALSE];
	}
};

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]];  -- order is important
	SELECT value FROM
	$No => RETURN['n];
	$Yes => RETURN['y];
	ENDCASE => ERROR;
	};

MyPutChar: PROC[ch: CHAR] = {
	stdout.PutChar[ch];
	};
	

-- start code
UserExec.RegisterCommand["DFDisk", Main];
}.