TCCopyImpl.mesa
Copyright Ó 1991, 1992 by Xerox Corporation. All rights reserved.
taken from PFSCommandsImpl
Willie-s, February 19, 1991 11:53 am PST
DIRECTORY
Basics,
Commander USING [CommandProc, Register],
IO,
IOClasses,
PFS,
PFSNames,
Process USING [CheckForAbort],
Rope,
SharedErrors;
TCCopyImpl: CEDAR MONITOR
IMPORTS Commander, IO, IOClasses, PFS, PFSNames, Process, Rope, SharedErrors
= BEGIN
ROPE: TYPE = Rope.ROPE;
PATH: TYPE = PFSNames.PATH;
STREAM: TYPE = IO.STREAM;
QuotedStringError: ERROR = CODE;
CmdTokenBreak: PROC [char: CHAR] RETURNS [IO.CharClass] = {
IF char = '" THEN RETURN [break];
IF char = ' OR char = '\t OR char = ', OR char = '\l OR char = '\r THEN RETURN [sepr];
RETURN [other];
};
Token: TYPE = RECORD [value, literal: ROPE];
GetCmdToken: PROC [stream: IO.STREAM] RETURNS [token: Token ¬ [NIL, NIL]] = {
token.value ¬ token.literal ¬ IO.GetTokenRope[stream, CmdTokenBreak ! IO.EndOfStream => CONTINUE].token;
IF Rope.Equal[token.literal, "\""] THEN {
ref: REF;
IO.Backup[self: stream, char: '"];
ref ¬ IO.GetRefAny[stream ! IO.Error, IO.EndOfStream => ERROR QuotedStringError];
WITH ref SELECT FROM
rope: ROPE => token.value ¬ rope;
ENDCASE => ERROR QuotedStringError;
};
};
Copying using Retrieve and Store
DoTCCopy: Commander.CommandProc = {
destinationDirectory: PATH ¬ NIL;
leftArrowExists: BOOL ¬ FALSE;
compsBeforeStar: INT ¬ 0;
forceCopy: BOOL ¬ TRUE;
retainStructure: BOOL ¬ FALSE;
updateOnly: BOOL ¬ FALSE;
exactLevelMatch: BOOL ¬ FALSE;
componentsRequired: INT ¬ 0;
HandleAFile: PROC [to, from: PATH] = {
sourceID: PFS.UniqueID ¬ PFS.FileInfo[from].uniqueID;
toName: ROPE ~ PFS.RopeFromPath[to];
fromName: ROPE ~ PFS.RopeFromPath[from];
Process.CheckForAbort[];
cmd.out.PutF[" %g ← %g", [rope[toName]], [rope[fromName]]];
{ENABLE
PFS.Error =>
IF error.group # bug THEN {msg ¬ PFSErrorMsg1[error]; GO TO skipIt};
IF updateOnly THEN {
destID: PFS.UniqueID ¬ PFS.nullUniqueID;
destID ¬ PFS.FileInfo[to ! PFS.Error => IF error.group # bug THEN CONTINUE].uniqueID;
IF sourceID = destID AND sourceID # PFS.nullUniqueID THEN {
This file does not need a copy, since it has the same ID as the destination file. We have been instructed to trust the ID.
cmd.out.PutRope["\n -- not copied, create dates match"];
GO TO skipIt;
};
};
BEGIN
outputStream: IO.STREAM ~ PFS.StreamOpen[fileName: to, accessOptions: create, wantedUniqueID: sourceID
! PFS.Error => IF error.group = user THEN {
msg ¬ IO.PutFR["Cannot create %s: %s\n", [rope[toName]], [rope[error.explanation]]];
GO TO sigh;
}];
pipeBufferLength: INT ~ 2048;
toPipe, fromPipe: IO.STREAM;
Fetcher: PROC ~ {
pfsRemoteStreamProc: PFS.RetrieveConfirmProc ~ {
RETURN [toPipe];
};
PFS.Retrieve[name: from, proc: pfsRemoteStreamProc];
IO.Close[toPipe];
};
Storer: PROC ~ TRUSTED {
buffer: PACKED ARRAY [0..pipeBufferLength) OF CHAR;
block: Basics.UnsafeBlock ¬ [base: LOOPHOLE[LONG[@buffer]], startIndex: 0, count: pipeBufferLength];
count: INT;
DO
count ¬ IO.UnsafeGetBlock[fromPipe, block];
IF count < pipeBufferLength THEN {
block.count ¬ count;
IO.UnsafePutBlock[outputStream, block];
IO.Close[outputStream];
RETURN;
}
ELSE
IO.UnsafePutBlock[outputStream, block];
ENDLOOP;
};
[toPipe, fromPipe] ¬ IOClasses.CreatePipe[pipeBufferLength];
TRUSTED {
SharedErrors.Fork[LIST[Storer, Fetcher]
! PFS.Error => IF error.group = user THEN {
msg ¬ IO.PutFR["Cannot read %s: %s\n", [rope[fromName]], [rope[error.explanation]]];
GO TO sigh;
}];
};
EXITS
sigh => {};
END;
EXITS
skipIt => {};
};
cmd.out.PutRope["\n"];
};
argStream: IO.STREAM ~ IO.RIS[cmd.commandLine];
head: LIST OF ROPE ~ LIST[NIL];
last: LIST OF ROPE ¬ head;
nArgs: NAT ¬ 0;
args: LIST OF ROPE ¬ NIL;
sources: LIST OF ROPE ¬ NIL;
DO
arg: Token ~ GetCmdToken[argStream];
length: INT ~ Rope.Length[arg.value];
SELECT TRUE FROM
(arg.value = NIL) => EXIT;
(length = 0) => NULL;
(Rope.Fetch[arg.literal, 0] = '-) => {
sense: BOOL ¬ TRUE;
FOR j: INT IN [1..length) DO
SELECT arg.literal.Fetch[j] FROM
'~ => {sense ¬ NOT sense; LOOP };
'r, 'R => retainStructure ¬ sense;
'u, 'U => updateOnly ¬ sense;
'x, 'X => exactLevelMatch ¬ sense;
ENDCASE => {
msg ¬ Rope.Cat["Unknown switch: ", arg.literal.Substr[0, j], " ", arg.literal.Substr[j]];
GOTO Die;
};
sense ¬ TRUE;
ENDLOOP;
};
(nArgs = 1 AND (Rope.Equal[arg.literal, "←"] OR Rope.Equal[arg.literal, "¬"])) => {
leftArrowExists ¬ TRUE;
};
ENDCASE => { nArgs ¬ nArgs + 1; last ¬ last.rest ¬ LIST[arg.value] };
ENDLOOP;
args ¬ head.rest;
IF leftArrowExists
THEN {
target: PATH ¬ PFS.PathFromRope[args.first];
sources ¬ args.rest;
IF target.IsADirectory[]
THEN destinationDirectory ¬ target
ELSE {
IF nArgs # 2 OR Rope.Find[args.first, "*"] >= 0 OR Rope.Find[args.rest.first, "*"] >= 0
THEN RETURN[$Failure, "Bad syntax for copying a file"];
HandleAFile[from: PFS.PathFromRope[args.rest.first], to: target];
RETURN[IF msg # NIL THEN $Failure ELSE NIL, msg];
};
}
ELSE {
destinationDirectory ¬ PFS.GetWDir[];
sources ¬ args;
};
If we get here, then for each of the filenames and patterns, copy the file to the destination directory.
FOR tail: LIST OF ROPE ¬ sources, tail.rest UNTIL tail = NIL DO
source: PATH ¬ PFS.PathFromRope[tail.first];
IF source.IsADirectory[] THEN {
msg ¬ Rope.Concat["Cannot copy a directory: ", tail.first];
GO TO Die;
};
IF Rope.Find[tail.first, "*"] >= 0
THEN {
pattern: PATH ¬ source;
handleIt: PFS.NameProc = {
[name: PATH] RETURNS [continue: BOOL]
to: PATH;
short: ROPE ¬ NIL;
continue ¬ TRUE;
IF exactLevelMatch AND componentsRequired # name.ComponentCount[] THEN RETURN [TRUE];
IF retainStructure
THEN to ¬ name.SubName[compsBeforeStar]
ELSE to ¬ name.SubName[start~name.ComponentCount[]-1, count~1];
to ¬ to.StripVersionNumber[];
to ¬ PFSNames.Cat[destinationDirectory, to];
HandleAFile[from: name, to: to];
RETURN[msg = NIL];
};
IF pattern.ShortName[].version = [none] THEN pattern ¬ pattern.SetVersionNumber[[highest]];
pattern ¬ PFS.AbsoluteName[pattern];
IF exactLevelMatch THEN componentsRequired ¬ PFSNames.ComponentCount[pattern];
IF retainStructure THEN {
goOn: BOOL ¬ TRUE;
findStar: PFSNames.ComponentProc ~ {
IF goOn AND Rope.Find[comp.ComponentRope[], "*"] >= 0 THEN goOn ¬ FALSE ELSE compsBeforeStar ¬ compsBeforeStar+1;
};
dummySeparatorProc: PFSNames.SeparatorProc ~ {};
PFSNames.Map[pattern, findStar, dummySeparatorProc, NIL];
};
PFS.EnumerateForNames[pattern, handleIt
! PFS.Error => IF error.group # $bug THEN {msg ¬ PFSErrorMsg[error]; GO TO Die}];
}
ELSE {
from: PATH ¬ source;
to: PATH ¬ PFSNames.StripVersionNumber[from.SubName[from.ComponentCount[]-1]];
to ¬ PFSNames.Cat[destinationDirectory, to];
HandleAFile[from: from, to: to];
IF msg # NIL THEN GO TO Die;
};
Process.CheckForAbort[];
ENDLOOP;
EXITS
Die => result ¬ $Failure;
};
TCFileInfo: PROC[name: PATH] RETURNS[uid: PFS.UniqueID ¬ PFS.nullUniqueID] ~ {
don't have FileInfo in TC interface
Enum: PFS.InfoProc ~ {
uid ¬ uniqueID;
RETURN[FALSE];
};
comp: PFSNames.Component ¬ PFSNames.ShortName[name];
IF comp.version.versionKind = none THEN
name ¬ PFSNames.SetVersionNumber[name, [highest, 0 ]];
PFS.EnumerateForInfo[name, Enum];
};
PFSErrorMsg: PROC [error: PFS.ErrorDesc] RETURNS [ROPE] = {
IF error.code = $unknownFile
THEN RETURN [" -- not found!\n"]
ELSE RETURN[Rope.Cat[" -- PFS.Error: ", error.explanation, "\n"]];
};
PFSErrorMsg1: PROC [error: PFS.ErrorDesc] RETURNS [ROPE] = {
IF error.code = $unknownFile
THEN RETURN [" -- not found!"]
ELSE RETURN[Rope.Concat["\n -- PFS.Error: ", error.explanation]];
};
Initialization
copyDoc: ROPE = "newFile ← oldFile | directory ← {pattern}*
Copies a file or files.
-c force copy
-r retain structure
-u update only
-x exact level match";
Init: PROC = {
Commander.Register["TCCopy", DoTCCopy, copyDoc, $Copy];
};
Init[];
END.