-- DFDiskImpl.Mesa
-- last edit March 8, 1983 12:41 pm
-- last edit May 22, 1983 4:54 pm, Russ Atkinson
-- last edit August 1, 1983 2:02 pm, Doug Wyatt
-- Pilot 6.0/ Mesa 7.0

    
DIRECTORY
Commander: TYPE USING [CommandProc, Register],
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],
IOMisc: TYPE USING[AskUser],
LongString: TYPE USING [EquivalentString],
Resource: TYPE USING[Acquire, Release],
Rope: TYPE USING[ROPE, Text],
Stream: TYPE USING [Delete, Handle, PutChar],
Subr: TYPE USING [AbortMyself, CheckForModify, CopyString, EndsIn, EnumerateDirectory,
 errorflg, GetCreateDate, GetRemoteFilenameProp, MakeTTYProcs,
 NewStream, PrintGreeting, SubrStop, TTYProcs, Write],
Time: TYPE USING [Current],
ViewerClasses: TYPE USING[Viewer],
ViewerOps: TYPE USING[CreateViewer, FindViewer, RestoreViewer],
WindowManager: TYPE USING[WaitCursor, UnWaitCursor];

DFDiskImpl: PROGRAM
IMPORTS Commander, CWF, DFSubr, File, FileStream, IO, IOMisc, LongString, Resource, Stream,
 Subr, Time, ViewerOps, WindowManager = {

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

MAXFILES: CARDINAL = 1500;

Main: Commander.CommandProc = TRUSTED {
ENABLE UNWIND => [] ← Resource.Release[$DFDisk];
 h: Subr.TTYProcs;
 [] ← Resource.Acquire[resource: $DFDisk, waitForIt: TRUE, owner: "DFDisk"];
 h ← Subr.MakeTTYProcs[cmd.in, cmd.out, NIL, MyConfirm];
 DFDiskUsingProcs[h];
 [] ← Resource.Release[$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: LONG 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: LONG STRING] RETURNS[bool: BOOL] ={
transferFileList: ARRAY [0.. 4) OF STRING ← [
 "disk.df"L,
 "line.cm"L,
 "SimpleExec"L,
 "Swatee"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,".logD"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 LongString.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 ← IOMisc.AskUser[msg: msg, in: in, out: out,
  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
Commander.Register["DFDisk", Main, "Make a DF file describing files on the local disk"];
}.