-- LTFloppyImpl.mesa
-- Created by
--   JFung.pasa	  	14-Dec-83 12:37:04
-- last edited by
--   JFung.pasa	   	24-Sep-84 10:27:36


DIRECTORY
     AccessFloppy,
     AccessFloppyUtil,
     Ascii,
     Environment,
     Exec,
     File,
     FileName,
     FileTypes USING [tUntypedFile],
     Floppy,
     Format,
     FormSW,
     Heap USING [systemZone],
     Inline,
     LispToolOps,
     MFile,
     MStream,
     NSString,
     OthelloDefs,
     OthelloOps,
     Process USING [Pause, SecondsToTicks],
     Put,
     Runtime,
     Space,
     SpecialMFile USING [GetCapaWithAccess, LeaderPages],
     Storage,
     Stream,
     String,
     StringLookUp,
     Time,
     Tool USING [
          Create, Destroy, MakeFileSW, MakeFormSW, MakeMsgSW, MakeSWsProc,
          UnusedLogName],
     ToolDriver,
     ToolWindow,
     Token,
     UserInput,
     Version,
     Volume,
     Window;


LTFloppyImpl: PROGRAM
     IMPORTS
          AccessFloppy, File, FileName, Floppy, FormSW, Heap, Inline, LispToolOps,
          MFile, NSString, OthelloOps, Process, Put, SpecialMFile, Storage, String,
          Volume, Time, Tool, ToolDriver, Token, UserInput


     EXPORTS LispToolOps =

     BEGIN OPEN ILT: LispToolOps;

     -- constant

     nullNextFile: NextFile = [];

     -- TYPEs
     Indicator: TYPE = {off, left, right};
     DataHandle: TYPE = LONG POINTER TO Data;
     Data: TYPE = MACHINE DEPENDENT RECORD [
          -- Message subwindow stuff
          msgSW(0): Window.Handle ← NIL,
          -- File subwindow stuff
          fileSW(2): Window.Handle ← NIL,
          -- Form subwindow stuff
          -- Note: enumerateds and booleans must be word boundary
          -- aligned as addresses for them must be generated
          --formSW: Window.Handle ← NIL,
          paramSW(4): Window.Handle ← NIL,
          commandSW(6): Window.Handle ← NIL,
          busy(8): BOOLEAN ← FALSE,  -- command is running
          volName(9): LONG STRING ← NIL,
          pattern(11): LONG STRING ← NIL,
          indicator(13): Indicator ← left,
          floppyName(14): LONG STRING ← NIL,
          stream(16): MStream.Handle ← NIL,
          from(18): LONG STRING ← NIL,
          to(20): LONG STRING ← NIL,
          numberOfFiles(22): INTEGER ← 0,
          current(23): LptNextFile ← NIL,
          fileList(25): LptNextFile ← NIL];

     EnumProc: TYPE = PROCEDURE [
          attributes: AccessFloppy.Attributes, fH: Floppy.FileHandle,
          name: LONG STRING] RETURNS [stop: BOOLEAN ← FALSE];


     LptNextFile: TYPE = LONG POINTER TO NextFile;
     NextFile: TYPE = RECORD [
          file: LONG STRING ← NIL,
          size: LONG CARDINAL ← 0,  -- file size including leader page if it has one.
          type: File.Type ← AccessFloppy.tFloppyLeaderPage,  -- specify whether it has leader page.
          next: LptNextFile ← NIL];


     VolHints: TYPE = RECORD [names: SEQUENCE length: CARDINAL OF LONG STRING];
     SizeHints: TYPE = RECORD [names: SEQUENCE length: CARDINAL OF CARDINAL];

     -- global variables

     Abort: ERROR [s: STRING] = CODE;
     active: BOOLEAN ← FALSE;
     atSource: PUBLIC CARDINAL ← 0;
     attributes: AccessFloppy.Attributes ← NIL;
     bufferSize: Space.PageCount ← 30;
     dataVersion: CARDINAL = 02222;  -- version of clientData
     debug: BOOLEAN ← FALSE;

     floppyWH: Window.Handle ← NIL;
     formDisplay: ToolWindow.DisplayProcType ← NIL;
     heraldName: STRING ← [50];
     indicatorBox: Window.Box = [[10, 10], [16, 16]];
     nullFile: Floppy.FileHandle;
     opened: BOOLEAN ← FALSE;
     --spaceHandle: Space.Handle ← Space.nullHandle;
     toolData: DataHandle ← NIL;
     vH: Floppy.VolumeHandle ← Floppy.nullVolumeHandle;
     volumeID: Volume.ID ← Volume.nullID;
     volumeOpen: BOOLEAN ← FALSE;
     z: UNCOUNTED ZONE = Heap.systemZone;

     SetOutcome: SIGNAL [value: Exec.Outcome] = CODE;


     CleanUp: PROC =
          BEGIN
          current: LptNextFile ← toolData.fileList;
          releaseData: MFile.ReleaseData ← [NIL, NIL];

          WHILE current↑ # nullNextFile DO
               file: MFile.Handle ← NIL;
               scratchName: LONG STRING ← [120];

               String.AppendString[scratchName, current.file];
               String.AppendString[scratchName, ".Scratch$"L];
               toolData.current ← current.next;
               file ← MFile.Acquire[
                    scratchName, MFile.Access[delete], releaseData];
               MFile.Delete[file];
               --current.file ← Storage.FreeStringNil[current.file];
               Storage.Free[current];
               current ← toolData.current;

               ENDLOOP;
          toolData.fileList ← toolData.current ← Storage.FreeNodeNil[
               toolData.current];
          toolData.from ← Storage.FreeStringNil[toolData.from];
          toolData.to ← Storage.FreeStringNil[toolData.to];
          END;  -- CleanUp.


     <<
     CopyCmd: PROCEDURE =
          BEGIN
          END;  


     -- from AccessFloppyUtil
     CreateBuffer: PUBLIC PROC [size: Space.PageCount]
          RETURNS [spH: Space.Handle, spSize: Space.PageCount] =
          BEGIN
          END;  -- CreateBuffer.

     DeleteBuffer: PUBLIC PROC [spH: Space.Handle] =
          BEGIN 
	  END;  -- DeleteBuffer.

     DiskToFloppy: PROC =
          BEGIN
          END;  -- DiskToFloppy.
>>

     DisplayFloppyName: PROCEDURE [] =
          BEGIN

          IF debug THEN {
               Put.Line[ILT.toolData.fileSW, "DisplayFile...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          FormSW.DisplayItem[toolData.paramSW, 3];
          END;  --DisplayFloppyName


     EnumerateFloppyFiles: PROCEDURE [
          v: Floppy.VolumeHandle, proc: EnumProc, pattern: LONG STRING] =
          BEGIN
          nullFile: Floppy.FileHandle = [volume: v, file: Floppy.nullFileID];
          attributes: AccessFloppy.Attributes ← Heap.systemZone.NEW[
               AccessFloppy .AttributesRecord[AccessFloppy.maxDataSize]];
          name: LONG STRING = LOOPHOLE[@attributes.length];

          IF debug THEN {
               Put.Line[ILT.toolData.fileSW, "EnumerateFloppyFiles...."L];
               --Process.Pause[Process.SecondsToTicks[5]];
               };

          FOR current: Floppy.FileHandle ← Floppy.GetNextFile[nullFile].nextFile,
               Floppy.GetNextFile[current].nextFile WHILE current # nullFile DO
               ENABLE UNWIND => Heap.systemZone.FREE[@attributes];
               IF Floppy.GetFileAttributes[current].type #
                    AccessFloppy.tFloppyLeaderPage THEN LOOP;
               IF debug THEN {
                    Put.Line[ILT.toolData.fileSW, "Floppy.GetNextFile"L];
                    --Process.Pause[Process.SecondsToTicks[5]];
                    Put.Text[ILT.toolData.fileSW, "Pattern = "L];
                    Put.Line[ILT.toolData.fileSW, pattern];
                    };

               AccessFloppy.GetAttributes[current, attributes];
               IF
                    --(pattern = NIL
                    --OR Exec.MatchPattern[string: name, pattern: pattern]) OR 
                    proc[attributes, current, name] THEN {
                    Put.Line[ILT.toolData.fileSW, "Exited loop."L]; EXIT; };
               ENDLOOP;
          Heap.systemZone.FREE[@attributes];
          END;  --EnumerateFloppyFiles


     FileLookUp: PROC [nsName: NSString.String]
          RETURNS [fFile: Floppy.FileHandle, continue: BOOLEAN] =
          BEGIN
          continue ← TRUE;
          IF debug THEN {
               Put.Line[ILT.toolData.fileSW, "FileLookUp"L];
               --Put.Text[ILT.toolData.msgSW, nsName];
               --Process.Pause[Process.SecondsToTicks[5]];
               };

          fFile ← AccessFloppy.LookUp[
               nsName, attributes !
               AccessFloppy.Error => {
                    continue ← FALSE;
                    Put.Line[ILT.toolData.fileSW, "Floppy Error"L];
                    SELECT type FROM
                         attributesNotAllowed => {
                              Put.Line[
                                   ILT.toolData.msgSW, "attributesNotAllowed"L];
                              };
                         fileNotFound => {
                              --Put.Text[ILT.toolData.msgSW, name];
                              Put.Line[ILT.toolData.msgSW, "File name not found"L];
                              };
                         invalidParameter => {
                              Put.Line[ILT.toolData.msgSW, "invalidParameter"L]; };
                         nameInUse => {
                              Put.Line[ILT.toolData.msgSW, "nameInUse"L]; };
                         volumeNotOpen => {
                              Put.Line[ILT.toolData.msgSW, "volumeNotOpen"L]; };
                         ENDCASE => Put.Line[ILT.toolData.msgSW, "others"L];
                    IF debug THEN Process.Pause[Process.SecondsToTicks[10]];
                    }];
          RETURN[fFile, continue];
          END;  --FileLookUp


     FixDirectory: PUBLIC PROC [dir: LONG POINTER TO LONG STRING] =
          BEGIN
          IF dir↑ = NIL THEN RETURN;
          SELECT dir[dir.length - 1] FROM '>, '# => RETURN; ENDCASE;
          String.AppendCharAndGrow[dir, '>, z];
          END;  --FixDirectory


     FloppyDelete: PUBLIC PROCEDURE [pattern: LONG STRING]
          RETURNS [deleted: BOOLEAN] =
          BEGIN

          name: LONG STRING ← NIL;
          vH: Floppy.VolumeHandle;

          DeleteOne: EnumProc =
               BEGIN
               deleted ← TRUE;
               Put.Text[ILT.toolData.msgSW, name];
               Put.Text[ILT.toolData.msgSW, "... "L];
               AccessFloppy.DeleteFile[
                    NSString.StringFromMesaString[name] !
                    AccessFloppy.Error =>
                         SELECT type FROM
                              fileNotFound => {
                                   Put.Line[ILT.toolData.msgSW, "not found"L];
                                   IF debug THEN
                                        Process.Pause[Process.SecondsToTicks[5]];
                                   GOTO error;
                                   };
                              ENDCASE];
               Put.Line[ILT.toolData.msgSW, "deleted"L];
               IF debug THEN Process.Pause[Process.SecondsToTicks[10]];
               RETURN[];
               EXITS error => {deleted ← FALSE; RETURN[]; };
               END;  --DeleteOne

          [vH] ← OpenFloppy[];
          IF ~opened THEN RETURN;
          BEGIN
          ENABLE UNWIND => {AccessFloppy.Close[ ! Floppy.Error => CONTINUE]};
          --name ← toolData.pattern;
          --IF toolData.pattern = NIL THEN EXIT;
          IF WildCards[pattern] THEN EnumerateFloppyFiles[vH, DeleteOne, pattern]
          ELSE [] ← DeleteOne[NIL, [vH, Floppy.nullFileID], pattern];
          END;
          IF debug THEN {
               Put.Text[ILT.toolData.fileSW, "AccessFloppy.Close"L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          AccessFloppy.Close[];
          END;  --FloppyDelete


     FloppyDuplicate: PUBLIC PROCEDURE = BEGIN END;


     FloppyFormat: PROCEDURE [
          nFiles: CARDINAL, label: LONG STRING, sides: Floppy.Sides,
          density: Floppy.Density] RETURNS [gotError: BOOLEAN] =
          BEGIN

          gotError: BOOLEAN ← FALSE;

          BEGIN
          v: Floppy.VolumeHandle;
          v ← AccessFloppy.Open[
               !
               Floppy.Error =>
                    SELECT error FROM
                         invalidFormat => GOTO virgin;
                         needsScavenging => {gotError ← TRUE; CONTINUE};
                         ENDCASE => gotError ← TRUE; ];
          IF ~gotError THEN {
               [] ← Floppy.GetAttributes[v, label];
               Floppy.Close[v];
               Put.Text[ILT.toolData.fileSW, "Floppy volume """L];
               Put.Text[ILT.toolData.fileSW, label];
               Put.Line[ILT.toolData.fileSW, """ already formatted"L];
               }
          ELSE
               Put.Line[
                    toolData.fileSW,
                    "Floppy probably contains valid information"L];
          EXITS virgin => NULL;
          END;

          Put.Line[
               toolData.fileSW,
               "Formatting will DESTROY all contents, confirm to continue"L];

          IF ~ILT.Confirm[] THEN RETURN;
          --label.length ← 0;

          IF label = NIL THEN {
               Put.Line[toolData.fileSW, "Please supply floppy name"];
               Process.Pause[Process.SecondsToTicks[5]];
               RETURN;
               };

          <<	  IF label # NIL THEN  
	  	toolData.floppyName ← String.CopyToNewString[
                    s: label, z: Heap.systemZone]
          ELSE toolData.floppyName ← String.CopyToNewString[
                    s: "UnnamedFloppy"L, z: Heap.systemZone];
>>

          IF label # NIL OR toolData.floppyName # NIL THEN {
               Put.Text[toolData.fileSW, "label # Nil:"L];
               Put.Text[toolData.fileSW, label];
               Put.Line[toolData.fileSW, "<"L];
               Process.Pause[Process.SecondsToTicks[5]];

               toolData.floppyName ← label;
               Put.Text[toolData.fileSW, "floppyName:"L];
               Put.Text[toolData.fileSW, toolData.floppyName];
               Put.Line[toolData.fileSW, "<"L];
               Process.Pause[Process.SecondsToTicks[5]];

               toolData.floppyName ← String.CopyToNewString[
                    s: label, z: Heap.systemZone];
               Put.Text[toolData.fileSW, "floppyName:"L];
               Put.Text[toolData.fileSW, toolData.floppyName];
               Put.Line[toolData.fileSW, "<"L];
               };


          IF debug THEN {
               Put.Line[ILT.toolData.fileSW, toolData.floppyName];
               Process.Pause[Process.SecondsToTicks[5]];
               };

          IF ~ILT.Confirm[] THEN RETURN;  --temp

          Put.Text[toolData.fileSW, "Formatting... "L];
          Floppy.Format[
               0, nFiles, toolData.floppyName, density, sides !
               Floppy.Error =>
                    SELECT error FROM
                         onlyOneSide => {sides ← one; RETRY};
                         onlySingleDensity => {density ← single; RETRY};
                         badDisk => {
                              Put.Line[
                                   toolData.fileSW,
                                   "Can't format this disk; may be write protecte"L];
                              gotError ← TRUE;
                              Process.Pause[Process.SecondsToTicks[5]];
                              GOTO noGood;
                              };
                         ENDCASE;

               -- ERROR Abort["Can't format this disk; may be write protected"L];
               Floppy.AlreadyFormatted => RESUME ];

          Put.Line[toolData.fileSW, "...done"L];
          EXITS noGood => RETURN;
          END;  -- FloppyFormat


     FloppyInfo: PROCEDURE [] =
          BEGIN
          density: Floppy.Density;
          sides: Floppy.Sides;
          label: STRING ← [Floppy.maxCharactersInLabel];
          freeSpace, largestBlock: LONG CARDINAL;
          v: Floppy.VolumeHandle;

          IF debug THEN {
               Put.Line[ILT.toolData.fileSW, "InfoDisk...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };

          [v] ← OpenFloppy[];

          IF ~opened THEN RETURN;

          IF debug THEN {
               Put.Line[ILT.toolData.fileSW, "GetAttributes...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };

          [freeSpace, largestBlock, , , density, sides] ← Floppy.GetAttributes[
               v, label];

          toolData.floppyName ← String.CopyToNewString[
               s: label, z: Heap.systemZone];

          Put.CR[toolData.fileSW];
          Put.Text[toolData.fileSW, "Floppy """L];
          Put.Text[toolData.fileSW, label];
          Put.Text[toolData.fileSW, """; "L];
          Put.Text[toolData.fileSW, IF sides = one THEN "single"L ELSE "double"L];
          Put.Text[toolData.fileSW, " sided; "L];
          Put.Text[
               toolData.fileSW, IF density = single THEN "single"L ELSE "double"L];
          Put.Line[toolData.fileSW, " density"L];
          Put.LongDecimal[toolData.fileSW, freeSpace];
          Put.Text[toolData.fileSW, " free pages; largest free block = "L];
          Put.LongDecimal[toolData.fileSW, largestBlock];
          Put.Line[toolData.fileSW, " pages"L];
          AccessFloppy.Close[];
          FormSW.DisplayItem[toolData.paramSW, 2];
          END;  --FloppyInfo



     FloppyList: PROCEDURE [] =
          BEGIN
          pattern: LONG STRING ← NIL;
          v: Floppy.VolumeHandle;

          ListOne: EnumProc =
               BEGIN
               Put.CR[toolData.fileSW];
               Put.Text[toolData.fileSW, name];
               FOR i: CARDINAL IN [name.length + WritePartial[attributes]..24) DO
                    Put.Text[toolData.fileSW, " "]; ENDLOOP;
               Put.Number[toolData.fileSW, attributes.type, [10, FALSE, TRUE, 5]];
               Put.Text[toolData.fileSW, "  "L];
               Put.LongNumber[
                    toolData.fileSW, attributes.totalSizeInBytes, [
                    10, FALSE, TRUE, 9]];
               Put.Text[toolData.fileSW, "  "L];
               Put.Date[toolData.fileSW, attributes.createDate, noSeconds];
               Put.Text[toolData.fileSW, "  "L];
               Put.Date[toolData.fileSW, attributes.lastWrittenDate, noSeconds];
               END;  --ListOne

          IF debug THEN {
               Put.Line[ILT.toolData.fileSW, "ListFiles: AccessFloppy.Open...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          [v] ← OpenFloppy[];
          IF ~opened THEN RETURN;

          Put.Text[toolData.fileSW, " NAME                   "L];  --23 
          Put.Text[toolData.fileSW, " TYPE  "L];  -- 5+2
          Put.Text[toolData.fileSW, "   LENGTH  "L];  -- 9+2
          Put.Text[toolData.fileSW, "      CREATE          "L];  -- 20+2
          Put.Line[toolData.fileSW, "  WRITE"L];
          EnumerateFloppyFiles[
               v, ListOne, "*"L !
               UNWIND => {AccessFloppy.Close[ ! Floppy.Error => CONTINUE]}];
          AccessFloppy.Close[];
          END;  --FloppyList



     FloppyReadSysout: PUBLIC PROCEDURE [pattern: LONG STRING] RETURNS [BOOLEAN] =
          BEGIN

          altName: LONG STRING ← NIL;
          pagesRemaining: INTEGER ← 1;
          created: BOOLEAN ← FALSE;
          continue: BOOLEAN ← TRUE;
          file: File.File;
          firstTime: BOOLEAN ← TRUE;
          fFile: Floppy.FileHandle;
          firstPage: File.PageNumber;
          freePages: Volume.PageCount;
          loopCnt: CARDINAL ← 1;
          nextFile: LONG STRING ← ILT.toolData.filePathName;
          nsName: NSString.String;
          readName: LONG STRING ← NIL;
          switches: LONG STRING ← NIL;
          v: Floppy.VolumeHandle;
          volSize: Volume.PageCount;

          --ReadOne: EnumProc =
          ReadOne: PROCEDURE [
               attributes: AccessFloppy.Attributes, fH: Floppy.FileHandle,
               name: LONG STRING] RETURNS [stop: BOOLEAN ← FALSE] =

               BEGIN
               --mFile: MFile.Handle ← NIL;
               type: MFile.Type ← unknown;
               destName: LONG STRING ← NIL;
               srcVFN, destVFN: FileName.VirtualFilename ← NIL;

               IF debug THEN {
                    Put.Line[ILT.toolData.fileSW, "ReadOne"L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };

               --InvertIndicator[];
               IF name # NIL THEN srcVFN ← FileName.AllocVFN[name];
               IF String.Length[altName] # 0 THEN
                    destVFN ← FileName.AllocVFN[altName]
               ELSE destVFN ← FileName.AllocVFN["[dummy]"L];
               IF String.Length[destVFN.name] = 0 THEN {
                    destName ← FileName.PackFilename[
                         srcVFN, FALSE, FALSE, TRUE, FALSE];
                    FileName.UnpackFilename[destName, destVFN];
                    FileName.FreeFilename[destName];
                    };
               destName ← FileName.PackFilename[destVFN, FALSE, TRUE, TRUE, FALSE];

               Put.Text[ILT.toolData.fileSW, name];
               Put.Text[ILT.toolData.fileSW, "... "L];
               IF attributes.clientDataLength = 2
                    AND attributes.clientData[0] = dataVersion THEN
                    type ← attributes.clientData[1];
               Put.Text[ILT.toolData.fileSW, "copying to "L];
               Put.Text[ILT.toolData.fileSW, destName];
               [] ← WritePartial[attributes];
               Put.Text[ILT.toolData.fileSW, "... "L];
               IF debug THEN {
                    Put.LongDecimal[
                         ILT.toolData.fileSW, attributes.totalSizeInBytes];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };
               IF attributes.size # 0 THEN
                    Floppy.CopyToPilotFile[
                         floppyFile: fH, pilotFile: file,
                         firstFloppyPage: AccessFloppy.leaderLength,
                         firstPilotPage:
                         SpecialMFile.LeaderPages[] + attributes.offset,
                         count: attributes.size !
                         Floppy.Error => {
                              Put.Line[
                                   ILT.toolData.msgSW,
                                   "incompatibleSizes/Insufficient space for floppy file"L];
                              continue ← FALSE;
                              Process.Pause[Process.SecondsToTicks[5]];
                              }];
               Put.Line[ILT.toolData.fileSW, "copied"L];

               --MFile.Release[mFile];
               IF destVFN # NIL THEN FileName.FreeVFN[destVFN];
               IF srcVFN # NIL THEN FileName.FreeVFN[srcVFN];
               IF destName # NIL THEN FileName.FreeFilename[destName];

               --EXITS return => NULL;
               END;  --ReadOne;


          [volumeID, volumeOpen] ← ILT.GetVolumeID[ILT.toolData.volName];
          [volSize, freePages] ← Volume.GetAttributes[volumeID];
          [file, firstPage] ← OthelloOps.GetVolumeBootFile[
               volumeID, hardMicrocode];

          Put.Text[ILT.toolData.fileSW, "Creating file."L];
          IF (created ← file = File.nullFile) THEN {
               file ← File.Create[
                    volumeID, freePages, FileTypes.tUntypedFile !
                    Volume.InsufficientSpace => {
                         Put.Text[ILT.toolData.fileSW, "."L];
                         freePages ← freePages - 100;
                         RETRY;
                         }];
               Put.CR[ILT.toolData.fileSW];
               }
          ELSE {
               IF debug THEN {
                    Put.Line[ILT.toolData.fileSW, "MakeUnbootable.."L];
                    --Process.Pause[Process.SecondsToTicks[10]];
                    };
               OthelloOps.MakeUnbootable[
                    file, hardMicrocode, OthelloDefs.leaderPages];
               };

          WHILE pagesRemaining > 0 DO
               SELECT loopCnt FROM
                    1 =>
                         Put.Line[
                              ILT.toolData.fileSW, "Insert first (#1) floppy "L];
                    2 =>
                         Put.Line[
                              ILT.toolData.fileSW, "Insert second (#2) floppy "L];
                    3 =>
                         Put.Line[
                              ILT.toolData.fileSW, "Insert third (#3) floppy "L];
                    4 =>
                         Put.Line[
                              ILT.toolData.fileSW, "Insert fourth (#4) floppy "L];
                    5 =>
                         Put.Line[
                              ILT.toolData.fileSW, "Insert fifth (#5) floppy "L];
                    6 =>
                         Put.Line[
                              ILT.toolData.fileSW, "Insert sixth (#6) floppy "L];
                    ENDCASE =>
                         Put.Line[ILT.toolData.fileSW, "Insert next floppy "L];
               IF ~ILT.Confirm[] THEN {continue ← FALSE; EXIT; };

               [v] ← OpenFloppy[];
               IF ~opened THEN RETURN[FALSE];

               IF String.Length[ILT.toolData.filePathName] # 0 THEN {
                    Put.Text[ILT.toolData.fileSW, "Retrieving... "L];
                    Put.Line[ILT.toolData.fileSW, ILT.toolData.filePathName];
                    }
               ELSE {
                    AccessFloppy.Close[ ! Floppy.Error => CONTINUE];
                    --ERROR Abort["Please enter source file name(s)."L];
                    Put.Line[ILT.toolData.msgSW, "Please enter source file name"L];
                    };

               atSource ← 0;
               IF attributes = NIL THEN
                    attributes ← z.NEW[
                         AccessFloppy .AttributesRecord[AccessFloppy.maxDataSize]];

               IF String.Length[altName] # 0 THEN FixDirectory[@altName];

               readName ← String.MakeString[z, 80];
               nextFile ← GetNextFilename[];
               IF nextFile = NIL THEN EXIT;
               String.AppendStringAndGrow[@readName, nextFile, z, 80];
               [] ← Token.FreeTokenString[nextFile];

               IF readName = NIL THEN EXIT;
               [nsName] ← NSString.StringFromMesaString[readName];

               IF WildCards[readName] THEN {
                    Put.Line[ILT.toolData.msgSW, "Wild Cards not allowed here"L];
                    continue ← FALSE;
                    EXIT;
                    };

               [fFile, continue] ← FileLookUp[nsName];
               IF ~continue THEN EXIT;

               IF firstTime THEN {
                    pagesRemaining ← attributes.totalSize; firstTime ← FALSE; };

               IF debug THEN {
                    Put.Line[ILT.toolData.fileSW, "Floppy.CopyToPilotFile"L];
                    Put.Decimal[ILT.toolData.fileSW, AccessFloppy.leaderLength];
                    Put.CR[ILT.toolData.fileSW];
                    Put.LongDecimal[
                         ILT.toolData.fileSW, SpecialMFile.LeaderPages[]];
                    Put.CR[ILT.toolData.fileSW];
                    Put.Text[ILT.toolData.fileSW, " attributes.offset"L];
                    Put.LongDecimal[ILT.toolData.fileSW, attributes.offset];
                    Put.CR[ILT.toolData.fileSW];
                    Put.Text[ILT.toolData.fileSW, " attributes.size"L];
                    Put.LongDecimal[ILT.toolData.fileSW, attributes.size];
                    Put.CR[ILT.toolData.fileSW];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };

               [] ← ReadOne[attributes, fFile, NameFromAttributes[attributes]];
               pagesRemaining ← pagesRemaining - attributes.size;
               Put.LongDecimal[ILT.toolData.fileSW, pagesRemaining];
               Put.Line[ILT.toolData.fileSW, " pages more"L];

               IF debug THEN {
                    Put.Line[ILT.toolData.fileSW, "UserInput.UserAbort"L];
                    --Process.Pause[Process.SecondsToTicks[5]];
                    };
               IF UserInput.UserAbort[ILT.windowHandle] THEN {
                    Put.Text[ILT.toolData.fileSW, "User Abort! "L];
                    Put.Line[ILT.toolData.fileSW, "Aborted"L];
                    UserInput.ResetUserAbort[ILT.windowHandle];
                    continue ← FALSE;
                    };

               IF debug THEN {
                    Put.Line[ILT.toolData.fileSW, "z.FREE"L];
                    --Process.Pause[Process.SecondsToTicks[5]];
                    };
               z.FREE[@attributes];

               IF debug THEN {
                    Put.Line[ILT.toolData.fileSW, "String.FreeString"L];
                    --Process.Pause[Process.SecondsToTicks[5]];
                    };

               IF readName # NIL THEN String.FreeString[z, readName];
               IF altName # NIL THEN String.FreeString[z, altName];

               IF debug THEN {
                    Put.Line[ILT.toolData.fileSW, "AccessFloppy.Close"L];
                    --Process.Pause[Process.SecondsToTicks[5]];
                    };
               AccessFloppy.Close[];
               IF ~continue THEN EXIT;
               loopCnt ← loopCnt + 1;
               ENDLOOP;
          IF continue THEN {
               ILT.ExpandVMemSize[file, volumeID];
               ILT.SetupBootFile[file, volumeID];
               };

          RETURN[continue];
          END;  --FloppyReadSysout


     FloppyStuff: PUBLIC PROCEDURE =
          BEGIN
          aborted: BOOLEAN ← FALSE;

          IF toolData = NIL THEN toolData ← Heap.systemZone.NEW[Data ← []];
          IF attributes = NIL THEN
               attributes ← z.NEW[
                    AccessFloppy .AttributesRecord[AccessFloppy.maxDataSize]];

          IF debug THEN {
               Put.Line[ILT.toolData.fileSW, "FloppyStuff...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          floppyWH ← MakeTool[];

          BEGIN
          ENABLE {
               Abort => {Put.Line[toolData.fileSW, s]; aborted ← TRUE; };
               AccessFloppy.Error => {
                    Put.Text[toolData.fileSW, "unexpected "L];
                    WriteAccessFloppyError[type];
                    Put.CR[toolData.fileSW];
                    aborted ← TRUE;
                    };
               AccessFloppy.InconsistentFile => {
                    Put.Line[toolData.fileSW, "AccessFloppy.InconsistentFile"L];
                    aborted ← TRUE;
                    };
               AccessFloppy.InvalidVersion => {
                    Put.Line[toolData.fileSW, "AccessFloppy.InvalidVersion"L];
                    aborted ← TRUE;
                    };
               AccessFloppy.NoRoomForClientData => {
                    Put.Line[toolData.fileSW, "AccessFloppy.NoRoomForClientData"L];
                    aborted ← TRUE;
                    };
               Floppy.Error => {
                    IF error = writeInhibited THEN
                         Put.Line[toolData.fileSW, "Floppy is write protected"L]
                    ELSE {
                         Put.Text[toolData.fileSW, "unexpected "L];
                         WriteFloppyError[error];
                         Put.CR[toolData.fileSW];
                         };
                    aborted ← TRUE;
                    };
               };

          END;
          END;  --FloppyStuff


     <<

     FloppyToDisk: PROC =
          BEGIN
          handle: MStream.Handle ← NIL;
          releaseData: MStream.ReleaseData ← [];

          FOR current: Floppy.FileHandle ← Floppy.GetNextFile[nullFile].nextFile,
               Floppy.GetNextFile[current].nextFile WHILE current # nullFile DO
               scratchName: LONG STRING ← [120];

               IF debug THEN {
                    Put.Line[toolData.fileSW, ".Floppy.GetFileAttributes...."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };
               [toolData.current.size, toolData.current.type] ←
                    Floppy.GetFileAttributes[current];

               IF debug THEN {
                    Put.Line[
                         toolData.fileSW, ".AccessFloppy.GetFileAttributes...."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };
               AccessFloppy.GetAttributes[current, attributes];

               IF debug THEN {
                    Put.Line[toolData.fileSW, ".String.MakeString...."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };
               toolData.current.file ← String.MakeString[
                    z, AccessFloppy.maxNameLength];

               IF debug THEN {
                    Put.Line[
                         toolData.fileSW,
                         "AccessFloppyUtil.MesaStringFromAttributes...."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };
               MesaStringFromAttributes[attributes, toolData.current.file];

               IF debug THEN {
                    Put.Line[toolData.fileSW, ".String.AppendString...."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };
               String.AppendString[scratchName, toolData.current.file];
               String.AppendString[scratchName, ".Scratch$"L];

               IF debug THEN {
                    Put.Line[toolData.fileSW, ".String.AppendString...."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };
               handle ← MStream.ReadWrite[
                    name: scratchName, release: releaseData,
                    type: MFile.Type[unknown]];
               attributes.size ← toolData.current.size;  -- modify size to actual file size.

               IF debug THEN {
                    Put.Line[toolData.fileSW, "RetrieveFilePiece..."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };
               RetrieveFilePiece[current, handle, attributes];
               toolData.numberOfFiles ← toolData.numberOfFiles + 1;
               toolData.current ← toolData.current.next ← z.NEW[NextFile ← []];

               IF debug THEN {
                    Put.Line[toolData.fileSW, "handle.delete..."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };
               handle.delete[handle];
               ENDLOOP;
          -- will have current points to a NextFile[NIL, NIL].   
          END;  -- FloppyToDisk.

>>



     FloppyWrite: PUBLIC PROCEDURE [name: LONG STRING] =
          BEGIN
          attributes: AccessFloppy.Attributes ← NIL;
          altName, switches: LONG STRING ← NIL;

          fileType: File.Type ← FileTypes.tUntypedFile;
          mFile: MFile.Handle;

          --[] ← AccessFloppy.Open[! AccessFloppy.Error => ERROR Abort["Can't open floppy"L]];
          [vH] ← OpenFloppy[];
          IF ~opened THEN RETURN;
          --  DO
          BEGIN
          ENABLE
               UNWIND => {
                    IF mFile # NIL THEN MFile.Release[mFile];
                    Heap.systemZone.FREE[@attributes];
                    AccessFloppy.Close[ ! Floppy.Error => CONTINUE]};

          fFile: Floppy.FileHandle;
          failed: BOOLEAN ← FALSE;
          mFileError: MFile.ErrorCode;
          offset, length: CARDINAL;
          IF attributes = NIL THEN
               attributes ← Heap.systemZone.NEW[
                    AccessFloppy .AttributesRecord[AccessFloppy.maxDataSize]];
          mFile ← NIL;
          IF name = NIL AND switches = NIL THEN GOTO done;
          [offset, length] ← ParsePartial[
               name !
               Token.SyntaxError => {
                    Put.Line[ILT.toolData.fileSW, name];
                    Put.Line[ILT.toolData.fileSW, " - bad name syntax"L];
                    --SetOutcome[error];
                    --LOOP;
                    }];

          IF switches = NIL THEN altName ← name;

          mFile ← MFile.ReadOnly[
               name, [] ! MFile.Error => {mFileError ← code; CONTINUE}];
          IF mFile = NIL THEN
               SELECT mFileError FROM
                    noSuchFile => {
                         Put.Line[ILT.toolData.msgSW, " not found"L];
                         --SetOutcome[error];
                         --LOOP;
                         };
                    conflictingAccess => {
                         Put.Line[ILT.toolData.msgSW, " conflicting access"L];
                         --SetOutcome[error];
                         --LOOP;
                         };
                    ENDCASE => ERROR Abort[" unexpected MFile.Error"L];
          MakeAttributes[mFile, altName, attributes, fileType];
          attributes.offset ← offset;
          attributes.size ← MIN[length, attributes.totalSize - offset];
          --Put.Text[ILT.toolData.msgSW, name];
          [] ← WritePartial[attributes];
          Put.Text[ILT.toolData.msgSW, "... "L];
          Put.Text[ILT.toolData.msgSW, "copying to "L];
          Put.Text[ILT.toolData.msgSW, altName];
          Put.Text[ILT.toolData.msgSW, "... "L];
          IF debug THEN Process.Pause[Process.SecondsToTicks[10]];
          fFile ← AccessFloppy.CreateFile[
               attributes !
               AccessFloppy.Error =>
                    IF type = nameInUse THEN {failed ← TRUE; CONTINUE};
               Floppy.Error =>
                    SELECT error FROM
                         fileListFull => ERROR Abort["too many files"L];

                         insufficientSpace => ERROR Abort["floppy is full"L];
                         ENDCASE];
          IF failed THEN {
               Put.Line[
                    ILT.toolData.msgSW, "floppy file already exists - skipped"L];
               Process.Pause[Process.SecondsToTicks[5]];
               --SetOutcome[error];
               }
          ELSE {

               IF attributes.size # 0 THEN
                    Floppy.CopyFromPilotFile[
                         floppyFile: fFile,
                         pilotFile: SpecialMFile.GetCapaWithAccess[mFile],
                         firstFloppyPage: AccessFloppy.leaderLength,
                         firstPilotPage: SpecialMFile.LeaderPages[] + offset,
                         count: attributes.size];
               Put.Line[ILT.toolData.msgSW, "copied"L]};
          Process.Pause[Process.SecondsToTicks[5]];
          MFile.Release[mFile];
          --ENDLOOP;
          EXITS done => NULL;
          END;
          Heap.systemZone.FREE[@attributes];
          AccessFloppy.Close[];
          END;  -- FloppyWrite



     FormatAndOpen: PROC [] RETURNS [skip: BOOLEAN] =
          BEGIN
          density: Floppy.Density ← double;
          sides: Floppy.Sides ← two;
          nFile: CARDINAL ← 2 * toolData.numberOfFiles + 1;
          ok: BOOLEAN;

          skip ← TRUE;
          Put.Line[toolData.fileSW, "Please Remove Floppy Disk, are you ready? "L];
          IF ~ILT.Confirm[] THEN RETURN;
          Put.Line[toolData.fileSW, "format..."L];
          [ok] ← FloppyFormat[nFile, toolData.floppyName, sides, density];
          IF ~ok THEN {CleanUp; RETURN};

          Put.Line[toolData.fileSW, "...ted"L];
          vH ← AccessFloppy.Open[];
          nullFile ← [vH, Floppy.nullFileID];
          skip ← FALSE;
          END;  -- FormatAndOpen.



     FormSWDeleteProc: FormSW.ProcType =
          BEGIN
          IF debug THEN {
               Put.Line[ILT.toolData.fileSW, "FormSWDeleteProc...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          [] ← FloppyDelete[toolData.pattern];
          END;  --FormSWDeleteProc



     FormSWDuplicateProc: FormSW.ProcType =
          BEGIN
          IF debug THEN {
               Put.Line[toolData.fileSW, "FormSWDuplicateProc...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          Put.Line[toolData.fileSW, "to be implemented"L];
          FloppyDuplicate;
          END;  --FormSWDuplicateProc




     FormSWFormatProc: FormSW.ProcType =
          BEGIN
          ok: BOOLEAN;
          IF debug THEN {
               Put.Line[toolData.fileSW, "FormSWFormatProc...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          [ok] ← FloppyFormat[256, toolData.floppyName, default, default];
          END;  --FormSWFormatProc




     FormSWInfoProc: FormSW.ProcType =
          BEGIN
          IF debug THEN {
               Put.Line[toolData.fileSW, "FormSWInfoProc...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          FloppyInfo;
          END;  --FormSWInfoProc



     FormSWListProc: FormSW.ProcType =
          BEGIN
          IF debug THEN {
               Put.Line[toolData.fileSW, "FormSWListProc...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          FloppyList;
          END;  --FormSWListProc



     FormSWReadProc: FormSW.ProcType =
          BEGIN
          IF debug THEN {
               Put.Line[toolData.fileSW, "FormSWReadProc...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          Put.Line[toolData.fileSW, "to be implemented"L];
          END;  --FormSWReadProc



     FormSWWriteProc: FormSW.ProcType =
          BEGIN

          IF debug THEN {
               Put.Line[toolData.fileSW, "FormSWWriteProc...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          Put.Line[toolData.fileSW, "to be implemented"L];
          FloppyWrite[toolData.pattern];
          END;  --FormSWWriteProc



     FormSWQuitProc: FormSW.ProcType =
          BEGIN

          IF toolData # NIL THEN
               BEGIN Tool.Destroy[floppyWH]; Heap.systemZone.FREE[@toolData]; END;
          END;  --FormSWQuitProc



     FormSWVolHintsProc: FormSW.MenuProcType =
          BEGIN
          RETURN[
               hints: DESCRIPTOR[
               @ILT.toolData.volHints[0], ILT.toolData.volHints.length],
                    freeHintsProc: HintsNoLongerBusy, replace: TRUE];
          END;


     GetNextFilename: PUBLIC PROC RETURNS [fn: LONG STRING ← NIL] =
          BEGIN
          MyGet: PROC [Token.Handle] RETURNS [c: CHARACTER] =
               BEGIN
               IF atSource >= ILT.toolData.filePathName.length THEN c ← Ascii.NUL
               ELSE
                    BEGIN
                    c ← ILT.toolData.filePathName[atSource];
                    atSource ← atSource + 1;
                    END;
               END;
          getObject: Token.Object ← [getChar: MyGet, break: Ascii.NUL];

          IF atSource >= ILT.toolData.filePathName.length THEN RETURN[NIL];
          fn ← Token.Item[h: @getObject, temporary: FALSE];
          END;  --GetNextFilename

     HintsNoLongerBusy: FormSW.FreeHintsProcType = BEGIN END;


     MesaStringFromAttributes: PUBLIC PROC [
          attris: AccessFloppy.Attributes, name: LONG STRING] =
          BEGIN  -- should protect against a bogus attris (i.e., one from a non AccessFloppy file).
          name.length ← 0;

          IF attris = NIL THEN
               RETURN WITH ERROR AccessFloppy.Error[invalidParameter];
          IF attris.length > attris.maxlength THEN  -- invalidAttributes?
               RETURN WITH ERROR AccessFloppy.Error[invalidParameter];
          IF name.maxlength < attris.length THEN
               RETURN WITH ERROR AccessFloppy.Error[invalidParameter];
          IF debug THEN {
               Put.Line[toolData.fileSW, "Inline.LongCOPY...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          [] ← Inline.LongCOPY[
               to: @name.text, nwords: attris.length / 2, from: @attris.name];
          name.length ← attris.length;
          IF name.length # 0 THEN  -- pick up odd character.
               name[name.length - 1] ← LOOPHOLE[attris.name[name.length - 1]];
          END;  -- MesaStringFromAttributes.



     MakeAttributes: PROCEDURE [
          file: MFile.Handle, name: LONG STRING,
          attributes: AccessFloppy.Attributes, type: File.Type] =
          BEGIN
          attributes.version ← AccessFloppy.currentVersion;
          attributes.type ← type;
          attributes.lastWrittenDate ← Time.Current[];
          attributes.name ← ALL[0];
          attributes.clientDataLength ← 2;
          attributes.clientData[0] ← dataVersion;
          [length: attributes.totalSizeInBytes, create: attributes.createDate,
               type: attributes.clientData[1]] ← MFile.GetProperties[file];
          attributes.totalSize ← PagesFromBytes[attributes.totalSizeInBytes];
          attributes.size ← attributes.totalSize;
          attributes.length ← MIN[name.length, AccessFloppy.maxNameLength];
          FOR i: CARDINAL IN [0..attributes.length) DO
               attributes.name[i] ← LOOPHOLE[name[i]]; ENDLOOP
          END;  --MakeAttributes



     MakeCommands: FormSW.ClientItemsProcType =
          BEGIN OPEN FormSW;

          tabs: ARRAY [0..5) OF CARDINAL ← [0, 20, 40, 60, 70];
          nItems: CARDINAL = 8;
          items ← AllocateItemDescriptor[nItems];

          items[0] ← CommandItem[
               tag: "Info"L, place: newLine, proc: FormSWInfoProc];
          items[1] ← CommandItem[tag: "List"L, proc: FormSWListProc];
          items[2] ← CommandItem[tag: "Read"L, proc: FormSWReadProc];
          items[3] ← CommandItem[tag: "Write"L, proc: FormSWWriteProc];
          items[4] ← CommandItem[
               tag: "Format"L, place: newLine, proc: FormSWFormatProc];
          items[5] ← CommandItem[tag: "Delete"L, proc: FormSWDeleteProc];
          items[6] ← CommandItem[tag: "Duplicate"L, proc: FormSWDuplicateProc];
          items[7] ← CommandItem[tag: "Quit"L, proc: FormSWQuitProc];
          SetTagPlaces[items, DESCRIPTOR[tabs], FALSE];
          RETURN[items, TRUE];
          END;  --MakeCommands


     MakeParams: FormSW.ClientItemsProcType =
          BEGIN OPEN FormSW;
          i, nVols: CARDINAL;
          tabs: ARRAY [0..4) OF CARDINAL ← [0, 30, 60, 75];
          nItems: CARDINAL = 3;

          items ← AllocateItemDescriptor[nItems];
          nVols ← ILT.ListLogicalVolumes[];

          String.Copy[toolData.volName, ILT.toolData.volHints[0]];
          FOR i IN [0..nVols) DO
               IF String.Equivalent[ILT.toolData.volHints[i], "Lisp"L] THEN
                    BEGIN
                    String.Replace[
                         @toolData.volName, ILT.toolData.volHints[i],
                         Heap.systemZone];
                    EXIT;
                    END;
               ENDLOOP;
          items[0] ← StringItem[
               tag: "Source Volume"L, place: newLine, string: @toolData.volName,
               inHeap: TRUE, menuProc: FormSWVolHintsProc];
          items[1] ← StringItem[
               tag: "Pattern"L, string: @toolData.pattern, inHeap: TRUE];
          items[2] ← StringItem[
               tag: "Floppy Name"L, string: @toolData.floppyName, place: newLine,
               inHeap: TRUE];
          SetTagPlaces[items, DESCRIPTOR[tabs], FALSE];
          RETURN[items, TRUE]
          END;  --MakeParams


     MakeSWs: Tool.MakeSWsProc =
          BEGIN
          addresses: ARRAY [0..4) OF ToolDriver.Address;
          logName: STRING ← [20];

          IF debug THEN {
               Put.Line[ILT.toolData.fileSW, "MakeSWs...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };

          Tool.UnusedLogName[unused: logName, root: "FloppyOptions.log"L];
          toolData.msgSW ← Tool.MakeMsgSW[window: window, lines: 2];
          toolData.paramSW ← Tool.MakeFormSW[window: window, formProc: MakeParams];
          toolData.commandSW ← Tool.MakeFormSW[
               window: window, formProc: MakeCommands];
          --note: logName is compulsory, else it bombs     
          toolData.fileSW ← Tool.MakeFileSW[window: window, name: logName];
          --Supervisor.AddDependency[client: agent, implementor: Event.toolWindow];
          -- do the ToolDriver stuff
          addresses ← [
               [name: "msgSW"L, sw: toolData.msgSW],
               --[name: "formSW"L, sw: toolData.formSW],
               [name: "ParamSW"L, sw: toolData.paramSW], [
               name: "CmdSW"L, sw: toolData.commandSW], [
               name: "fileSW"L, sw: toolData.fileSW]];
          ToolDriver.NoteSWs[
               tool: "FloppyOptions"L, subwindows: DESCRIPTOR[addresses]];
          END;  --MakeSWs



     MakeTool: PROCEDURE RETURNS [wh: Window.Handle] =
          BEGIN
          heraldName: STRING ← [80];

          String.AppendString[heraldName, "XSIS:Xerox Floppy Option"L];
          --String.AppendString[heraldName, " of "L];
          --Time.Append[heraldName, Time.Unpack[Runtime.GetBcdTime[]]];
          --heraldName.length ← heraldName.length - 3;
          --String.AppendString[heraldName, " on Pilot Version "L];
          --Version.Append[heraldName];
          RETURN[
               Tool.Create[
                    makeSWsProc: MakeSWs, initialState: default,
                    --clientTransition: Transit, 
                    name: heraldName]];
          --initialBox: [
          --place: sw.BitmapPlace[[10, hisBox.dims.h]],
          --dims: [hisBox.dims.w - 20, 180]]];
          END;  --MakeTool



     NameFromAttributes: PROCEDURE [attributes: AccessFloppy.Attributes]
          RETURNS [LONG STRING] = INLINE {RETURN[LOOPHOLE[@attributes.length]]};


     OpenFloppy: PROCEDURE [] RETURNS [v: Floppy.VolumeHandle] =
          BEGIN

          IF debug THEN {
               Put.Line[ILT.toolData.fileSW, "OpenFloppy...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          opened ← TRUE;
          v ← AccessFloppy.Open[
               !
               Floppy.Error =>
                    SELECT error FROM
                         notReady => {
                              Put.Line[ILT.toolData.msgSW, "Floppy Not Closed"L];
                              IF debug THEN
                                   Process.Pause[Process.SecondsToTicks[10]];
                              GOTO noGood;
                              };
                         invalidFormat => {
                              Put.Line[
                                   ILT.toolData.msgSW, "Invalid Floppy Format"L];
                              GOTO noGood;
                              };
                         needsScavenging => {
                              Put.Line[
                                   ILT.toolData.msgSW, "Floppy Needs Scavenging"L];
                              GOTO noGood;
                              };
                         ENDCASE];
          EXITS noGood => opened ← FALSE;
          END;  -- OpenFloppy


     ParsePartial: PROCEDURE [name: LONG STRING]
          RETURNS [offset: CARDINAL ← 0, length: CARDINAL ← LAST[CARDINAL]] =
          BEGIN
          h: Token.Handle;
          i: CARDINAL;
          IF name = NIL THEN RETURN;
          FOR i IN [1..name.length) DO
               IF name[i] = '[ THEN EXIT; REPEAT FINISHED => RETURN ENDLOOP;
          h ← Token.StringToHandle[name, i + 1];
          BEGIN
          ENABLE UNWIND => [] ← Token.FreeStringHandle[h];
          offset ← Token.Decimal[h];
          SELECT h.break FROM
               '. =>
                    IF h.getChar[h] = '. THEN
                         length ← Token.Decimal[h] - offset + 1
                    ELSE ERROR Token.SyntaxError[NIL];
               '! => length ← Token.Decimal[h];
               '] => NULL;
               ENDCASE => ERROR Token.SyntaxError[NIL];
          END;
          name.length ← i;
          [] ← Token.FreeStringHandle[h];
          END;  --ParsePartial


     PagesFromBytes: PROCEDURE [bytes: LONG CARDINAL] RETURNS [LONG CARDINAL] =
          BEGIN
          RETURN[
               (bytes + Environment.bytesPerPage - 1) / Environment.bytesPerPage];
          END;  --PagesFromBytes


     PutVolumeName: PROCEDURE [] =
          BEGIN
          Put.Text[toolData.fileSW, "Volume = "L];
          Put.LongString[toolData.fileSW, toolData.volName];
          Put.CR[toolData.fileSW];
          END;  --PutVolumeName


     <<
     RetrieveFilePiece: PROC [
          floppyFile: Floppy.FileHandle, mStream: MStream.Handle,
          attributes: AccessFloppy.Attributes] =
          BEGIN
          mFile: MFile.Handle ← NIL;
          block: Environment.Block;
          bufferSizeInBytes, bytesRemaining, bytesToRead, bytesTransfered:
               AccessFloppy.LengthInBytes;
          start, pagesToRead: File.PageNumber;

          bytesTransfered ← 0;
          -- starting page number of the floppy file, page count of the floppy file, and byte count of the floppy file.
          start ← 0;
          bytesRemaining ← attributes.size*Environment.bytesPerPage;
          bufferSizeInBytes ← bufferSize*Environment.bytesPerPage;

          -- set attributes
          mFile ← MStream.GetFile[mStream];

          MFile.SetTimes[
               file: mFile, create: attributes.createDate,
               write: attributes.lastWrittenDate];
          MFile.SetType[mFile, MFile.Type[binary]];
          MStream.SetLength[mStream, bytesRemaining];

          -- set up block
          block.blockPointer ← Space.LongPointer[spaceHandle];
          block.startIndex ← 0;

          --copy as many chunks of the standard buffer size as possible.....
          UNTIL bytesRemaining = 0 DO
               bytesToRead ← MIN[bytesRemaining, bufferSizeInBytes];
               pagesToRead ← PagesFromBytes[bytesToRead];

               Floppy.Read[floppyFile, start, pagesToRead, block.blockPointer];
               block.stopIndexPlusOne ← Inline.LowHalf[bytesToRead];
               mStream.put[mStream, block, FALSE];
               start ← start + pagesToRead;
               bytesTransfered ← bytesTransfered + bytesToRead;
               bytesRemaining ← bytesRemaining - bytesToRead;
               ENDLOOP;
          END;  -- RetrieveFilePiece. 


     StoreFilePiece: PROC [
          mStream: MStream.Handle, floppyFile: Floppy.FileHandle,
          size: File.PageNumber] =
          BEGIN

          -- store data from disk to floppy
          block: Environment.Block;
          bufferSizeInBytes, bytesRemaining, bytesToWrite, bytesTransfered:
               AccessFloppy.LengthInBytes;
          start, pagesToWrite: File.PageNumber;
          why: Stream.CompletionCode ← normal;
          bytesTransfered ← 0;
          -- starting page number of the floppy file, page count of the floppy file, and byte count of the floppy file.
          start ← 0;
          bytesRemaining ← (size*Environment.bytesPerPage);
          bufferSizeInBytes ← bufferSize*Environment.bytesPerPage;
          -- set up block
          block.blockPointer ← Space.LongPointer[spaceHandle];
          block.startIndex ← 0;
          bytesToWrite ← bytesRemaining;

          --copy as many chunks of the standard buffer size as possible.....
          UNTIL bytesRemaining = 0 OR why = endOfStream DO
               bytesToWrite ← MIN[bytesRemaining, bufferSizeInBytes];
               block.stopIndexPlusOne ← Inline.LowHalf[bytesToWrite];
               [bytesToWrite, why, ] ← mStream.get[
                    mStream, block, Stream.defaultInputOptions];
               pagesToWrite ← PagesFromBytes[bytesToWrite];
               Floppy.Write[floppyFile, start, pagesToWrite, block.blockPointer];
               start ← start + pagesToWrite;
               bytesTransfered ← bytesTransfered + bytesToWrite;
               bytesRemaining ← bytesRemaining - bytesToWrite;
               ENDLOOP;
          END;  -- StoreFilePiece.
>>

     Transit: ToolWindow.TransitionProcType =
          BEGIN
          SELECT TRUE FROM

               old = inactive =>
                    BEGIN
                    IF toolData = NIL THEN
                         toolData ← Heap.systemZone.NEW[Data ← []];
                    IF attributes = NIL THEN
                         attributes ← z.NEW[
                              AccessFloppy
                              .AttributesRecord[AccessFloppy.maxDataSize]];
                    --UserInput.ResetUserAbort[window];
                    --data.logSW ← window;
                    -- data.window ← ToolWindow.WindowForSubwindow[window];
                    -- data.ttyWindow ← TTYSW.GetTTYHandle[window];
                    -- destroy ← FALSE;

                    active ← TRUE;
                    END;
               new = inactive =>
                    BEGIN
                    IF toolData # NIL THEN
                         BEGIN
                         FormSW.Destroy[toolData.paramSW];
                         FormSW.Destroy[toolData.commandSW];
                         Heap.systemZone.FREE[@toolData];
                         END;

                    IF attributes # NIL THEN {
                         z.FREE[@attributes]; attributes ← NIL};

                    --ToolDriver.RemoveSWs[tool: "LispTool"L];
                    active ← FALSE;
                    END;
               ENDCASE;
          END;  -- Transit



     WriteAccessFloppyError: PROCEDURE [type: AccessFloppy.ErrorType] =
          BEGIN
          Put.Text[toolData.fileSW, "AccessFloppy.Error["L];

          Put.Text[
               toolData.fileSW,
               SELECT type FROM
                    attributesNotAllowed => "attributesNotAllowed"L,
                    fileNotFound => "fileNotFound"L,
                    invalidParameter => "invalidParameter"L,
                    nameInUse => "nameInUse"L,
                    volumeNotOpen => "volumeNotOpen"L,
                    ENDCASE => "?"L];

          Put.Text[toolData.fileSW, "]"L];
          END;  --WriteAccessFloppyError



     WriteFloppyError: PROCEDURE [error: Floppy.ErrorType] =
          BEGIN
          Put.Text[toolData.fileSW, "Floppy.Error["L];
          Put.Text[
               toolData.fileSW,
               SELECT error FROM
                    badDisk => "badDisk"L,
                    badSectors => "badSectors"L,
                    endOfFile => "endOfFile"L,
                    fileListFull => "fileListFull"L,
                    fileNotFound => "fileNotFound"L,
                    hardwareError => "hardwareError"L,
                    incompatibleSizes => "incompatibleSizes"L,
                    invalidFormat => "invalidFormat"L,
                    invalidPageNumber => "invalidPageNumber"L,
                    invalidVolumeHandle => "invalidVolumeHandle"L,
                    insufficientSpace => "insufficientSpace"L,
                    needsScavenging => "needsScavenging"L,
                    noSuchDrive => "noSuchDrive"L,
                    notReady => "notReady"L,
                    onlyOneSide => "onlyOneSide"L,
                    onlySingleDensity => "onlySingleDensity"L,
                    initialMicrocodeSpaceNotAvailable =>
                         "initialMicrocodeSpaceNotAvailable"L,
                    stringTooShort => "stringTooShort"L,
                    volumeNotOpen => "volumeNotOpen"L,
                    writeInhibited => "writeInhibited"L,
                    zeroSizeFile => "zeroSizeFile"L,
                    ENDCASE => "?"L];
          Put.Line[toolData.fileSW, "]"L];
          END;  --WriteFloppyError


     -- used by both ILT and subwindows

     WritePartial: PROCEDURE [attributes: AccessFloppy.Attributes]
          RETURNS [chars: CARDINAL ← 0] =
          BEGIN
          CountedNumber: PROCEDURE [n: LONG CARDINAL] RETURNS [CARDINAL] = {
               s: STRING = [12];
               String.AppendLongDecimal[s, n];
               Put.Text[ILT.toolData.fileSW, s];
               RETURN[s.length]};
          IF attributes.offset # 0 OR attributes.size # attributes.totalSize THEN {
               chars ← 4;
               Put.Char[ILT.toolData.fileSW, '[];
               chars ← chars + CountedNumber[attributes.offset];
               -- Write[".."L];
               chars ←
                    chars + CountedNumber[attributes.offset + attributes.size - 1];
               Put.Char[ILT.toolData.fileSW, ']]};
          END;  --WritePartial

     << 
	WriteFiles: PROCEDURE [gSwitches: LONG STRING, h: Exec.Handle] =
          BEGIN
          attributes: AccessFloppy.Attributes ← NIL;
          name, altName, switches: LONG STRING ← NIL;
          fileType: File.Type ← FileTypes.tUntypedFile;
          mFile: MFile.Handle;
          Write: Format.StringProc = Exec.OutputProc[h];
          IF gSwitches # NIL THEN
               fileType ← [
                    String.StringToNumber[
                    gSwitches ! String.InvalidNumber => CONTINUE]];
          [] ← AccessFloppy.Open[
               ! AccessFloppy.Error => ERROR Abort["Can't open floppy"L]];
          DO
               ENABLE
                    UNWIND => {
                         IF altName # name THEN
                              altName ← Exec.FreeTokenString[altName];
                         name ← Exec.FreeTokenString[name];
                         switches ← Exec.FreeTokenString[switches];
                         IF mFile # NIL THEN MFile.Release[mFile];
                         Heap.systemZone.FREE[@attributes];
                         AccessFloppy.Close[ ! Floppy.Error => CONTINUE]};
               fFile: Floppy.FileHandle;
               failed: BOOLEAN ← FALSE;
               mFileError: MFile.ErrorCode;
               offset, length: CARDINAL;
               IF attributes = NIL THEN
                    attributes ← Heap.systemZone.NEW[
                         AccessFloppy .AttributesRecord[AccessFloppy.maxDataSize]];
               mFile ← NIL;
               IF altName # name THEN altName ← Exec.FreeTokenString[altName];
               name ← Exec.FreeTokenString[name];
               switches ← Exec.FreeTokenString[switches];
               [name, switches] ← Exec.GetToken[h];
               IF name = NIL AND switches = NIL THEN EXIT;
               [offset, length] ← ParsePartial[
                    name !
                    Token.SyntaxError => {
                         Write[name];
                         Format.Line[Write, " - bad name syntax"L];
                         SetOutcome[error];
                         LOOP}];
               IF switches # NIL AND switches.length # 0 THEN
                    SELECT switches[0] FROM
                         's => {
                              switches ← Exec.FreeTokenString[switches];
                              [altName, switches] ← Exec.GetToken[h]};
                         't => {
                              fileType ← [
                                   String.StringToNumber[
                                   name ! String.InvalidNumber => CONTINUE]];
                              switches ← Exec.FreeTokenString[switches];
                              LOOP};
                         ENDCASE
               ELSE altName ← name;
               mFile ← MFile.ReadOnly[
                    name, [] ! MFile.Error => {mFileError ← code; CONTINUE}];
               IF mFile = NIL THEN
                    SELECT mFileError FROM
                         noSuchFile => {
                              Format.Line[Write, " not found"L];
                              SetOutcome[error];
                              LOOP};
                         conflictingAccess => {
                              Format.Line[Write, " conflicting access"L];
                              SetOutcome[error];
                              LOOP};
                         ENDCASE => ERROR Abort[" unexpected MFile.Error"L];
               MakeAttributes[mFile, altName, attributes, fileType];
               attributes.offset ← offset;
               attributes.size ← MIN[length, attributes.totalSize - offset];
               Write[name];
               [] ← WritePartial[attributes];
               Write["... "L];
               Write["copying to "L];
               Write[altName];
               Write["... "L];
               fFile ← AccessFloppy.CreateFile[
                    attributes !
                    AccessFloppy.Error =>
                         IF type = nameInUse THEN {failed ← TRUE; CONTINUE};
                    Floppy.Error =>
                         SELECT error FROM
                              fileListFull => ERROR Abort["too many files"L];
                              insufficientSpace => ERROR Abort["floppy is full"L];
                              ENDCASE];
               IF failed THEN {
                    Format.Line[Write, "floppy file already exists - skipped"L];
                    SetOutcome[error]}
               ELSE {
                    IF attributes.size # 0 THEN
                         Floppy.CopyFromPilotFile[
                              floppyFile: fFile,
                              pilotFile: SpecialMFile.GetCapaWithAccess[mFile],
                              firstFloppyPage: AccessFloppy.leaderLength,
                              firstPilotPage: SpecialMFile.LeaderPages[] + offset,
                              count: attributes.size];
                    Format.Line[Write, "copied"L]};
               MFile.Release[mFile];
               ENDLOOP;
          Heap.systemZone.FREE[@attributes];
          AccessFloppy.Close[];
          END;  -- WriteFiles
>>


     WildCards: PROCEDURE [pattern: LONG STRING] RETURNS [BOOLEAN] =
          BEGIN
          IF pattern # NIL THEN
               FOR i: CARDINAL IN [0..pattern.length) DO
                    SELECT pattern[i] FROM '*, '# => RETURN[TRUE]; ENDCASE;
                    ENDLOOP;
          IF debug THEN {
               Put.Line[ILT.toolData.fileSW, " NOT WildCards"L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          RETURN[FALSE];
          END;

     END.