FileCommandsImpl.mesa
L. Stewart, April 6, 1983 5:29 pm
Stolen from UserExecOpsImpl.mesa
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: BOOLFALSE;
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: BOOLFALSE;
n: INT ← 0;
numPages: INT ← 0;
Delete: PROC [file: Rope.ROPE] RETURNS [deleted: BOOLTRUE] = TRUSTED {
found: BOOLTRUE;
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)"];
};
Initialization
Init[];
END.