<> <> <> <<>> DIRECTORY Commander USING [CommandProc, Register], ConvertUnsafe USING [AppendRope], Directory USING [DeleteFile, Error, ignore, GetProperty, GetProps, Lookup, PutProperty, Rename], File USING [Capability, GetSize], FileIO USING [CapabilityFromStream, Open, OpenFailed], FileStream USING [SetLeaderPropertiesForCapability], IO USING [card, Close, GetBlock, GetLength, GreenwichMeanTime, int, PutBlock, PutRope, PutF, rope, SetLength, STREAM, text, time, UserAbort], PropertyTypes USING [tCreateDate], Rope USING [Concat, Equal, ROPE, ToRefText], Time USING [Current], UECP USING [Argv, Parse] ; FileCommandsImpl: CEDAR PROGRAM IMPORTS Commander, ConvertUnsafe, Directory, File, FileIO, FileStream, IO, Rope, Time, UECP = BEGIN CopyFiles: Commander.CommandProc = { argv: UECP.Argv _ UECP.Parse[cmd.commandLine]; buffer: REF TEXT; bufferSize: CARDINAL = 2048; bytes, totalBytes, numberOfFiles: INT _ 0; newF: IO.STREAM; newN, oldN: Rope.ROPE; createDate: IO.GreenwichMeanTime; capability: File.Capability; CopyOneFile: PROC = { oldF: IO.STREAM; oldLen: INT; oldF _ FileIO.Open[fileName: oldN, accessOptions: read, createOptions: oldOnly, raw: TRUE ! FileIO.OpenFailed => { cmd.out.PutF["\tCannot open %g\n", IO.rope[fileName]]; GOTO Error; }]; createDate _ GetCreateDate[FileIO.CapabilityFromStream[oldF]]; oldLen _ oldF.GetLength[]; IF newF = NIL THEN -- first time -- newF _ FileIO.Open[fileName: newN, accessOptions: overwrite, createLength: oldLen] ELSE newF.SetLength[totalBytes + oldLen]; numberOfFiles _ numberOfFiles + 1; DO bytes _ oldF.GetBlock[buffer]; IF bytes=0 THEN EXIT; totalBytes _ totalBytes + bytes; newF.PutBlock[buffer]; IF cmd.in.UserAbort[] THEN { cmd.out.PutRope["...aborted, Copy incomplete\n"]; GOTO Error; }; ENDLOOP; oldF.Close[]; EXITS Error => RETURN }; IF argv.argc < 4 OR NOT Rope.Equal[argv[2], "_"] THEN { cmd.out.PutRope["Copy: Usage is Copy newFile _ oldFile {more old files}\n"]; RETURN; }; newN _ argv[1]; buffer _ NEW[TEXT[bufferSize]]; FOR i: NAT IN [3..argv.argc) DO oldN _ argv[i]; IF cmd.in.UserAbort[] THEN EXIT; CopyOneFile[]; ENDLOOP; IF newF = NIL THEN RETURN; capability _ FileIO.CapabilityFromStream[newF]; newF.Close[]; cmd.out.PutF["\n Copied %d bytes", IO.card[totalBytes]]; IF numberOfFiles = 1 THEN SetCreateDate[capability, createDate] ELSE cmd.out.PutF[" from %g files", IO.card[numberOfFiles]]; cmd.out.PutF[" to %g\n", IO.rope[newN]]; }; ListFiles: Commander.CommandProc = { argv: UECP.Argv _ UECP.Parse[cmd.commandLine]; numPages: INT _ 0; file: Rope.ROPE; n: INT _ 0; Print: PROC [file: Rope.ROPE] = TRUSTED -- Directory -- { txt: REF TEXT; name: LONG STRING; cap: File.Capability; failed: BOOL _ FALSE; createDate: IO.GreenwichMeanTime; byteLength: LONG CARDINAL; txt _ Rope.ToRefText[file]; name _ LOOPHOLE[txt];-- Directory.GetProps may write into name, e.g. to change lower case to capitals. cap _ Directory.Lookup[fileName: name, permissions: Directory.ignore ! Directory.Error => GOTO NotFound]; [, , createDate,byteLength,] _ Directory.GetProps[file: cap, name: name ! Directory.Error => IF type = invalidProperty THEN { cmd.out.PutF["\t%g has bad leader page\n", IO.rope[file]]; GOTO Error; }]; cmd.out.PutF["%-30g %6d %g\n", IO.text[txt], IO.card[byteLength], IO.time[createDate]]; numPages _ File.GetSize[cap] + numPages; n _ n + 1; EXITS Error => {}; NotFound => cmd.out.PutF["\t%g not found\n", IO.rope[file]]; }; -- of Print FOR i: NAT IN [1..argv.argc) DO file _ argv[i]; IF cmd.in.UserAbort[] THEN EXIT; Print[file]; ENDLOOP; cmd.out.PutF["Total of %d files, %d pages\n", IO.int[n], IO.int[numPages]]; }; RenameFile: PROC [oldName, newName: Rope.ROPE, out: IO.STREAM] = { msg, r: Rope.ROPE; { Rename1: PROC [old, new: Rope.ROPE] = TRUSTED { olds: LONG STRING _ [60]; news: LONG STRING _ [60]; ConvertUnsafe.AppendRope[to: olds, from: old]; ConvertUnsafe.AppendRope[to: news, from: new]; Directory.Rename[olds, news]; FileStream.SetLeaderPropertiesForCapability[cap: Directory.Lookup[news], write: Time.Current[]]; }; Delete1: PROC [file: Rope.ROPE] = TRUSTED { Directory.DeleteFile[LOOPHOLE[Rope.ToRefText[file]] ! Directory.Error => CONTINUE]; }; Rename1[oldName, newName ! Directory.Error => { SELECT type FROM fileAlreadyExists => {IF Rope.Equal[oldName, newName, FALSE] THEN TRUSTED {Delete1["$$scratch" ! Directory.Error => CONTINUE]; Rename1[oldName, "$$scratch"]; Rename1["$$scratch", newName]; CONTINUE; }; r _ Rope.Concat[newName, "$$"]; IF Rope.Equal[r, oldName, FALSE] THEN -- e.g. user is in fact performing rename foo.mumble _ foo.mumble$$ {Delete1[newName]; RETRY; }; Delete1[r ! Directory.Error => CONTINUE]; Rename1[newName, r]; out.PutF["%g already existed, now renamed to %g\n", IO.rope[newName], IO.rope[r]]; RETRY; }; fileNotFound => msg _ "File Not Found"; invalidFileName => msg _ "Invalid File Name"; ENDCASE => msg _ "Directory Error"; out.PutF["%g, ", IO.rope[msg]]; GOTO fail; }; ]; out.PutF["%g renamed to %g\n", IO.rope[oldName], IO.rope[newName]]; EXITS fail => { out.PutRope["Unable to complete rename.\n"]; }; }; }; Rename: Commander.CommandProc = { argv: UECP.Argv _ UECP.Parse[cmd.commandLine]; oldName, newName: Rope.ROPE; IF argv.argc NOT IN [3..4] THEN { cmd.out.PutRope["Rename usage:\n\tRename newFileName _ oldFileName\nOR\n\tRename oldFileName newFileName\n"]; RETURN; }; oldName _ argv[1]; newName _ argv[2]; IF Rope.Equal[newName, "_"] THEN { IF argv.argc # 4 THEN { cmd.out.PutRope["Rename: Usage Rename newFileName _ oldFileName\n"]; RETURN; }; newName _ oldName; oldName _ argv[3]; }; RenameFile[oldName: oldName, newName: newName, out: cmd.out]; }; DeleteFiles: Commander.CommandProc = { argv: UECP.Argv _ UECP.Parse[cmd.commandLine]; file: Rope.ROPE; someMissing: BOOL _ FALSE; n: INT _ 0; numPages: INT _ 0; Delete: PROC [file: Rope.ROPE] RETURNS [deleted: BOOL _ TRUE] = TRUSTED { found: BOOL _ TRUE; ls: LONG STRING = LOOPHOLE[Rope.ToRefText[file]]; cap: File.Capability; cap _ Directory.Lookup[fileName: ls, permissions: Directory.ignore ! Directory.Error => { found _ FALSE; CONTINUE }]; IF found THEN { numPages _ File.GetSize[cap] + numPages; Directory.DeleteFile[ls ! Directory.Error => { deleted _ FALSE; CONTINUE } ]; } ELSE deleted _ FALSE; IF deleted THEN cmd.out.PutF["Deleted: %g\n", IO.rope[file]] ELSE cmd.out.PutF["\t%g not found.\n", IO.rope[file]]; }; FOR i: NAT IN [1..argv.argc) DO IF cmd.in.UserAbort[] THEN EXIT; file _ argv[i]; IF Delete[file] THEN n _ n + 1 ELSE someMissing _ TRUE; ENDLOOP; IF someMissing OR n > 1 THEN cmd.out.PutF["Deleted %d files, %d pages.\n", IO.int[n], IO.int[numPages]]; }; GetCreateDate: PROC [file: File.Capability] RETURNS [IO.GreenwichMeanTime] = TRUSTED { createDate: IO.GreenwichMeanTime; Directory.GetProperty[file: file, property: PropertyTypes.tCreateDate, propertyValue: DESCRIPTOR[@createDate, SIZE[IO.GreenwichMeanTime]]]; RETURN [createDate]; }; SetCreateDate: PROC [file: File.Capability, createDate: IO.GreenwichMeanTime] = TRUSTED { Directory.PutProperty[file: file, property: PropertyTypes.tCreateDate, propertyValue: DESCRIPTOR[@createDate, SIZE[IO.GreenwichMeanTime]]]; }; Init: PROCEDURE = { Commander.Register[key: "Copy", proc: CopyFiles, doc: "Copy newFile _ oldFile(s)"]; Commander.Register[key: "List", proc: ListFiles, doc: "List fileName(s)"]; Commander.Register[key: "Rename", proc: Rename, doc: "Rename newFile _ oldFile"]; Commander.Register[key: "Delete", proc: DeleteFiles, doc: "Delete fileName(s)"]; }; <<>> <> Init[]; END.