-- FilesMain.mesa -- edited by Barth, October 14, 1980 2:26 PM -- edited by Brotz, March 25, 1981 4:41 PM -- edited by Schroeder, Wednesday Oct. 29, 1980 12:08 pm PST DIRECTORY AltoFileDefs, crD: FROM "CoreDefs", csD: FROM "CoreStreamDefs", DirectoryDefs, ImageDefs, intCommon: FROM "IntCommon", IODefs, LaurelExecDefs, MatchDefs, ovD: FROM "OverviewDefs", SegmentDefs, Storage, StreamDefs, StringDefs, TimeDefs; FilesMain: PROGRAM IMPORTS crD, csD, DirectoryDefs, ImageDefs, intC: intCommon, IODefs, LaurelExecDefs, MatchDefs, SegmentDefs, Storage, StreamDefs, StringDefs, TimeDefs = BEGIN OPEN StringDefs, IODefs; -- Constants numBufferPages: CARDINAL = 2; promptChar: CHARACTER = '>; -- Types commandIndex: TYPE = {copy, delete, filestat, list, quit, rename, type}; -- Variables capFileFilter: STRING _ [50]; argList: STRING _ Storage.String[50]; fileStatString: STRING; cc: CARDINAL; sawFile: BOOLEAN; ci: commandIndex; -- Routines WriteError: PROCEDURE [error: STRING]= BEGIN WriteLine[""L]; WriteString["? "L]; WriteLine[error]; END; -- of WriteError IsSpace: PROCEDURE[c: CHARACTER] RETURNS [BOOLEAN] = {RETURN[c = TAB OR c = SP]}; GetCommand: PROCEDURE RETURNS [ci: commandIndex] = -- Entry Invariants: argList should contain the default command line, if any, if -- argList.length is zero then the default will be set to "list *.mail", the display -- should be at the beginning of the line the command is to be typed in on. -- Exit Invariants: ci will contain a valid commandIndex, argList will contain the -- remainder of the command line after parsing off the command name by -- looking for [SP]*[~SP]*[SP]*, the display will be at the beginning of the -- line following the line the command was typed on. BEGIN maxCommandLength: CARDINAL = 8; -- update this if maxlength(ac) changes ac: ARRAY commandIndex OF STRING = [copy: "COPY"L, delete: "DELETE"L, filestat: "FILESTAT"L, list: "LIST"L, quit: "QUIT"L, rename: "RENAME"L, type: "TYPE"L]; TestEndChar: PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] = {RETURN[c = CR OR c = '?]}; RubOutCommand: PROCEDURE = BEGIN argList.length _ 0; WriteLine[" XXX"L]; WriteChar[promptChar]; END; -- of RubOutCommand -- ParseCommand: PROCEDURE RETURNS [BOOLEAN] = BEGIN i, i2: CARDINAL _ 0; tci: commandIndex; matchCount: CARDINAL _ 0; cm: STRING _ [maxCommandLength]; SkipSpaces: PROCEDURE = {WHILE IsSpace[argList[i]] AND i < argList.length DO i _ i + 1 ENDLOOP}; IF argList.length = 0 THEN -- ignore blank lines -- {WriteLine[""L]; RETURN[FALSE]}; SkipSpaces[]; -- skip leading spaces UNTIL argList.text[i] = SP OR i >= argList.length DO -- pick out the command IF cm.length < cm.maxlength THEN {cm[cm.length] _ argList[i]; cm.length _ cm.length+1} ELSE {WriteError["Unknown Command"L]; RETURN[FALSE]}; i _ i+1; ENDLOOP; SkipSpaces[]; -- skip trailing spaces FOR i2 IN [i .. argList.length) DO argList[i2 - i] _ argList[i2] ENDLOOP; argList.length _ argList.length - i; -- reduce command line size by command length MatchDefs.Capitalize[cm, cm]; -- convert to canonical form for matching FOR tci IN commandIndex DO BEGIN IF cm.length > ac[tci].length THEN LOOP; FOR i2 IN [0 .. cm.length) DO IF cm[i2] # ac[tci].text[i2] THEN GOTO notfound; ENDLOOP; ci _ tci; -- this command matches at least partially, remember it IF cm.length = ac[tci].length THEN RETURN[TRUE]; -- exact match matchCount _ matchCount + 1; EXITS notfound => NULL; END; ENDLOOP; SELECT matchCount FROM = 1 => RETURN[TRUE]; -- only one partial match, use it > 1 => WriteError["Ambiguous Command"L]; ENDCASE => WriteError["Unknown Command"L]; RETURN[FALSE]; END; -- of ParseCommand -- IF argList.length = 0 THEN AppendString[to: argList, from: "list *.mail"L]; DO WriteChar[promptChar]; SELECT ReadEditedString[argList, TestEndChar, TRUE ! Rubout => {RubOutCommand; RESUME}; LineOverflow => {IncreaseStringSize[]; RESUME[argList]}] FROM CR => IF ParseCommand[] THEN {WriteLine[""L]; RETURN}; '? => BEGIN WriteLine["?"L]; WriteString["Commands are:"L]; FOR ci IN commandIndex DO WriteString[" "L]; WriteString[ac[ci]]; ENDLOOP; WriteLine[""L]; WriteLine["Control DEL cancels commands in progress."L]; END; ENDCASE; ENDLOOP; END; -- of GetCommand -- GetAtom: PROCEDURE [a: STRING] RETURNS [BOOLEAN]= BEGIN IF cc >= argList.length THEN RETURN[FALSE]; a.length _ 0; UNTIL IsSpace[argList[cc]] OR cc >= argList.length DO a[a.length] _ argList[cc]; a.length _ a.length + 1; cc _ cc + 1; ENDLOOP; WHILE IsSpace[argList[cc]] AND cc < argList.length DO cc _ cc + 1; ENDLOOP; RETURN[TRUE]; END; -- of GetAtom -- IncreaseStringSize: PROCEDURE = BEGIN temp: STRING _ Storage.String[argList.length + 50]; AppendString[temp, argList]; Storage.FreeString[argList]; argList _ temp; END; -- of IncreaseStringSize -- ScanDirectory: PROCEDURE [processFile: PROC [POINTER TO AltoFileDefs.FP, STRING] RETURNS [BOOLEAN]] = BEGIN fileFilter: STRING _ [50]; cc: CARDINAL _ 0; sawFile _ FALSE; WHILE GetAtom[fileFilter] DO IF StreamDefs.ControlDELtyped[] THEN EXIT; IF ci = type AND fileFilter[0] = '[ THEN [] _ TypeFile[NIL, fileFilter] ELSE BEGIN MatchDefs.Capitalize[s: fileFilter, capS: capFileFilter]; DirectoryDefs.EnumerateDirectory[processFile]; END; ENDLOOP; IF ~sawFile AND ~StreamDefs.ControlDELtyped[] THEN WriteLine["No such files"L] ELSE IF ci = list THEN WriteLine[""L]; END; -- of ScanDirectory -- FileMessage: PROCEDURE[msg: STRING, fileName: STRING] = BEGIN WriteString[msg]; WriteChar[SP]; WriteLine[fileName]; END; -- of FileMessage -- DeleteAFile: PROCEDURE [p: POINTER TO AltoFileDefs.FP, fileName: STRING] RETURNS [BOOLEAN] = BEGIN fileHandle: crD.UFileHandle; errorCode: ovD.ErrorCode; capFileName: STRING _ [50]; IF StreamDefs.ControlDELtyped[] THEN RETURN[TRUE]; MatchDefs.Capitalize[s: fileName, capS: capFileName]; IF ~MatchDefs.IsMatch[name: capFileName, pattern: capFileFilter] THEN RETURN[FALSE]; sawFile _ TRUE; [errorCode, fileHandle] _ crD.OpenFile[intC.user, fileName, read]; IF errorCode # ovD.ok THEN FileMessage["Can't open file"L, fileName] ELSE BEGIN WriteString["Delete File "L]; WriteString[fileName]; WriteString[" [Confirm]"L]; SELECT ReadChar[] FROM SP, CR, 'y, 'Y, ESC => BEGIN errorCode _ crD.DeleteFile[fileHandle]; IF errorCode # ovD.ok THEN WriteLine[" Can't delete"L] ELSE WriteLine[" Deleted"L]; END; ENDCASE => BEGIN errorCode _ crD.CloseFile[fileHandle]; IF errorCode # ovD.ok THEN WriteLine[" Can't close file"L]; WriteLine[" Not deleted"L]; END; END; RETURN[FALSE]; END; -- of DeleteAFile -- TypeFile: PROCEDURE [p: POINTER TO AltoFileDefs.FP, fileName: STRING] RETURNS [BOOLEAN]= BEGIN capFileName: STRING _ [50]; bsh: csD.StreamHandle; char: CHARACTER; inBravoStuff: BOOLEAN _ FALSE; lengthString: STRING _ [20]; MatchDefs.Capitalize[s: fileName, capS: capFileName]; IF ~MatchDefs.IsMatch[name: capFileName, pattern: capFileFilter] THEN RETURN[FALSE]; sawFile _ TRUE; bsh _ csD.OpenFromName[fileName, intC.user, byte, read, 1 ! csD.Error => {FileMessage["Can't open file"L, fileName]; GO TO Return}]; WriteLine[""L]; WriteString["Contents of file "L]; WriteString[fileName]; WriteString[": (length = "L]; StringDefs.AppendLongNumber[lengthString, csD.GetLength[bsh], 10]; WriteString[lengthString]; WriteLine[" (decimal) bytes)"L]; WriteLine[""L]; DO IF StreamDefs.ControlDELtyped[] THEN EXIT; char _ csD.Read[bsh ! csD.Error => BEGIN IF reason # ovD.endOfStream THEN FileMessage["Error Reading File"L, fileName]; EXIT; END]; IF inBravoStuff THEN inBravoStuff _ char # CR ELSE inBravoStuff _ char = ControlZ; IF ~inBravoStuff THEN WriteChar[char]; ENDLOOP; WriteLine[""L]; csD.Destroy[bsh]; GO TO Return; EXITS Return => RETURN[StreamDefs.ControlDELtyped[]]; END; -- of TypeFile -- ListFile: PROCEDURE[p: POINTER TO AltoFileDefs.FP, fileName: STRING] RETURNS [BOOLEAN]= BEGIN capFileName: STRING _ [50]; IF StreamDefs.ControlDELtyped[] THEN RETURN[TRUE]; MatchDefs.Capitalize[s: fileName, capS: capFileName]; IF MatchDefs.IsMatch[name: capFileName, pattern: capFileFilter] THEN BEGIN IF fileName[fileName.length - 1] = '. THEN fileName.length _ fileName.length - 1; -- remove trailing period WriteString[fileName]; WriteString[" "L]; sawFile _ TRUE; END; RETURN[FALSE]; END; -- of ListFile -- oFileName: STRING _ [50]; obsh: csD.StreamHandle; InnerCopyLoop: PROCEDURE[p:POINTER TO AltoFileDefs.FP, iFileName:STRING] RETURNS [BOOLEAN]= BEGIN ibsh: csD.StreamHandle _ NIL; capFileName: STRING _ [50]; flagQuit: BOOLEAN _ FALSE; MatchDefs.Capitalize[s: iFileName, capS: capFileName]; IF ~MatchDefs.IsMatch[name: capFileName, pattern: capFileFilter] THEN RETURN[FALSE]; sawFile _ TRUE; BEGIN ENABLE csD.Error => BEGIN FileMessage[SELECT reason FROM ovD.illegalFilename, ovD.fileInUse => "Can't open file"L ENDCASE => "Disk error while copying"L, iFileName]; flagQuit _ TRUE; CONTINUE END; ibsh _ csD.OpenFromName[iFileName, intC.user, byte, read, numBufferPages]; csD.StreamCopy[from: ibsh, to: obsh, fromItems: csD.GetLength[ibsh]]; END; IF ibsh # NIL THEN csD.Destroy[ibsh]; flagQuit _ flagQuit OR StreamDefs.ControlDELtyped[]; RETURN[flagQuit]; END; -- of InnerCopyLoop -- CopyFile: PROCEDURE = BEGIN errorCode: ovD.ErrorCode; ofh: crD.UFileHandle; oFileName:STRING _ [50]; IF ~GetAtom[oFileName] OR ~(argList[cc] = '_ AND IsSpace[argList[cc + 1]]) THEN {WriteLine["Form is: destination _ source source ..."L]; RETURN}; cc _ cc + 2; -- throw away seperator WHILE IsSpace[argList[cc]] DO cc _ cc+ 1; ENDLOOP; [errorCode, ofh] _ crD.OpenFile[intC.user, oFileName, read]; IF crD.CloseFile[ofh] # ovD.ok THEN FileMessage["Couldn't close file"L, oFileName]; IF errorCode = ovD.ok THEN {FileMessage["File already exists:"L, oFileName]; RETURN}; [errorCode, ofh] _ crD.OpenFile[intC.user, oFileName, update]; IF errorCode # ovD.ok THEN {FileMessage["Can't open file"L, oFileName]; RETURN}; obsh _ csD.Open[ofh, byte, overwrite, numBufferPages]; ScanDirectory[InnerCopyLoop]; csD.Close[obsh ! csD.Error => {FileMessage["Couldn't close byte stream for file"L, oFileName]; CONTINUE}]; IF crD.CloseFile[ofh] # ovD.ok THEN FileMessage["Couldn't close file"L, oFileName]; END; -- of CopyFile -- StatFile: PROCEDURE [p: POINTER TO AltoFileDefs.FP, fileName: STRING] RETURNS [BOOLEAN]= BEGIN capFileName: STRING _ [50]; afh: SegmentDefs.FileHandle; page, byte: CARDINAL; bytes: LONG CARDINAL; read, write, create: TimeDefs.PackedTime; WriteTime: PROCEDURE[s: STRING, t: TimeDefs.PackedTime]= BEGIN AppendString[fileStatString, s]; AppendChar[fileStatString, ':]; TimeDefs.AppendDayTime[fileStatString, TimeDefs.UnpackDT[t]]; AppendString[fileStatString, " "L]; END; -- of WriteTime -- IF StreamDefs.ControlDELtyped[] THEN RETURN[TRUE]; MatchDefs.Capitalize[s: fileName, capS: capFileName]; IF ~MatchDefs.IsMatch[name: capFileName, pattern: capFileFilter] THEN RETURN[FALSE]; sawFile _ TRUE; afh _ SegmentDefs.NewFile[fileName, SegmentDefs.Read ! SegmentDefs.FileNameError => {afh _ NIL; CONTINUE}]; IF afh = NIL THEN {FileMessage["Can't open file"L, fileName]; RETURN[FALSE]}; [read, write, create] _ SegmentDefs.GetFileTimes[afh]; [page, byte] _ SegmentDefs.GetEndOfFile[afh]; fileStatString.length _ 0; AppendString[fileStatString, fileName]; AppendString[fileStatString, " "L]; IF byte = 512 THEN {byte _ 0; page _ page + 1}; bytes _ byte + LONG[(page - 1)] * 512; AppendLongNumber[fileStatString, bytes, 10]; AppendString[fileStatString, " bytes "L]; AppendNumber[fileStatString, page + 1, 10]; AppendString[fileStatString, " pages "L]; WriteTime[" Create"L, create]; WriteTime["Write"L, write]; WriteTime["Read"L, read]; WriteLine[fileStatString]; SegmentDefs.SetFileTimes[afh, read, write, create]; SegmentDefs.ReleaseFile[afh ! SegmentDefs.FileError => CONTINUE]; RETURN[FALSE]; END; -- of StatFile -- RenameFile: PROCEDURE= BEGIN oldFileName: STRING _ [50]; newFileName: STRING _ [50]; fh: crD.UFileHandle; errorCode: ovD.ErrorCode; fp: AltoFileDefs.FP; dir: StreamDefs.DiskHandle; cfh: SegmentDefs.FileHandle; ComplainSyntax: PROCEDURE = {WriteLine[ "Form is: oldfilename newfilename OR newfilename _ oldfilename"L]}; IF ~GetAtom[oldFileName] OR ~GetAtom[newFileName] THEN {ComplainSyntax; RETURN}; IF newFileName.length = 1 AND newFileName[0] = '_ THEN BEGIN newFileName.length _ 0; AppendString[newFileName, oldFileName]; IF ~GetAtom[oldFileName] THEN {ComplainSyntax; RETURN} END; IF ~EquivalentStrings[oldFileName, newFileName] THEN BEGIN [errorCode, fh] _ crD.OpenFile[intC.user, newFileName, read]; IF crD.CloseFile[fh] # ovD.ok THEN FileMessage["Couldn't close file"L, newFileName]; IF errorCode = ovD.ok THEN {WriteString[newFileName]; WriteLine[" already exists"]; RETURN}; END; IF ~DirectoryDefs.DirectoryLookup[@fp, oldFileName, FALSE] THEN {FileMessage["Cannot find"L, oldFileName]; RETURN}; IF ~DirectoryDefs.DirectoryPurgeFP[@fp] THEN {FileMessage["Couldn't remove from directory:"L, oldFileName]; RETURN}; dir _ StreamDefs.CreateWordStream [SegmentDefs.NewFile["SysDir", SegmentDefs.ReadWriteAppend], StreamDefs.ReadWriteAppend]; IF newFileName[newFileName.length - 1] # '. THEN BEGIN newFileName[newFileName.length] _ '.; newFileName.length _ newFileName.length + 1; END; IF DirectoryDefs.Insert[dir, @fp, newFileName] THEN FileMessage["Couldn't insert new file:", newFileName]; dir.destroy[dir]; IF (cfh _ crD.LookupInFileCache[oldFileName]) # NIL THEN crD.FreeCacheEntry[cfh]; IF (cfh _ crD.LookupInFileCache[newFileName]) # NIL THEN crD.FreeCacheEntry[cfh]; END; -- of RenameFile -- WriteHerald: PROCEDURE = BEGIN time: STRING _ [25]; TimeDefs.AppendDayTime [time, TimeDefs.UnpackDT[ImageDefs.BcdVersion[].time]]; WriteLine[""L]; WriteString["Laurel File Utility of "L]; WriteLine[time]; WriteLine["Type ? for help."L]; END; -- of WriteHerald -- -- Main Program LaurelExecDefs.MakeMenuCommandCallable[newMail]; LaurelExecDefs.MakeMenuCommandCallable[mailFile]; LaurelExecDefs.MakeMenuCommandCallable[display]; LaurelExecDefs.MakeMenuCommandCallable[delete]; LaurelExecDefs.MakeMenuCommandCallable[undelete]; LaurelExecDefs.MakeMenuCommandCallable[moveTo]; LaurelExecDefs.MakeMenuCommandCallable[copy]; WriteHerald[]; DO ci _ GetCommand[]; cc _ 0; StreamDefs.ResetControlDEL; SELECT ci FROM rename => RenameFile; filestat => BEGIN fileStatString _ Storage.String[200]; ScanDirectory[StatFile]; Storage.FreeString[fileStatString]; END; delete => ScanDirectory[DeleteAFile]; type => ScanDirectory[TypeFile]; list => ScanDirectory[ListFile]; copy => CopyFile; quit => EXIT; ENDCASE; ENDLOOP; Storage.FreeString[argList]; END. -- of FilesMain -- (635)\f1