NSCopyImpl.mesa
Copyright © 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Bill Jackson August 29, 1985 8:52:51 pm PDT
Dave Rumph, February 3, 1986 6:12:09 pm PST
Note: The vast majority of this code is taken from FSFileCommandsImpl since I'm trying to emulate the "built-in" Copy command.
This guy know about NSAgent entries in your profile, e.g.:
NSAgent.Server: "Thunderbird"
NSAgent.CHName: "Bill Jackson:PARC:Xerox"
DIRECTORY
NSFiler,
NSFilerRpcControl,
BasicTime USING [GMT, nullGMT],
Commander USING [CommandProc, Handle, Register],
CommandTool USING [ArgumentVector, Failed, Parse],
File USING [GetVolumeName, SystemVolume],
FileNames USING [ConvertToSlashFormat, CurrentWorkingDirectory, DirectoryContaining, GetShortName, IsADirectory, IsAPattern, ResolveRelativePath, StripVersionNumber],
FS USING [ComponentPositions, EnumerateForNames, Error, ErrorDesc, ExpandName, NameProc],
FSPseudoServers USING [TranslateForRead, TranslateForWrite],
IO USING [PutF, PutRope, STREAM],
Process USING [CheckForAbort],
Rope USING [Cat, Concat, Equal, Fetch, Find, Flatten, Index, Length, Match, ROPE, SkipTo, Substr],
RPC USING [ImportFailed, ShortROPE],
ThisMachine USING [Name],
UserCredentials USING [Get],
UserProfile USING [Token];
NSCopyImpl:
CEDAR
PROGRAM
IMPORTS NSFiler, NSFilerRpcControl, Commander, CommandTool, File, FileNames, FS, FSPseudoServers, IO, Process, Rope, RPC, ThisMachine, UserCredentials, UserProfile
= BEGIN
ROPE: TYPE = Rope.ROPE;
ShortROPE: TYPE = RPC.ShortROPE;
STREAM: TYPE = IO.STREAM;
huhMsg: ARRAY NSFiler.Huh OF ROPE ← ["badCredentials", "fromNotFound", "cantWrite", "whoKnows", "noError"];
CopyAndRename: Commander.CommandProc = {
[cmd: REF CommandObject] RETURNS [result: REF ← NIL, msg: ROPE ← NIL]
CommandObject = [
in, out, err: STREAM, commandLine, command: ROPE,
propertyList: List.AList, procData: CommandProcHandle]
forceCopy: BOOL ← FALSE;
destinationDirectory: ROPE ← NIL;
leftArrowExists: BOOL;
doACopy: BOOL ← cmd.procData.clientData = $Copy;
doStore: BOOL ← cmd.procData.clientData = $Store;
dirBeforeStar: ROPE;
dirBeforeStarLength: INT;
retainStructure: BOOL ← FALSE;
updateOnly: BOOL ← FALSE;
exactLevelMatch: BOOL ← FALSE;
anglesRequired: INT ← 0;
FixUpFilename:
PROC [name:
ROPE ←
NIL, copyFrom:
BOOL]
RETURNS [newName:
ROPE] = {
body: ROPE;
platformHost: ROPE ← ThisMachine.Name[];
platformVolume: ROPE ← File.GetVolumeName[File.SystemVolume[]];
prefix: ROPE;
cp: FS.ComponentPositions;
[fullFName: newName, cp: cp] ← FS.ExpandName[name];
prefix ← Rope.Substr[newName, 0, 4];
body ← Rope.Substr[newName, 4, Rope.Length[newName]];
IF Rope.Equal[prefix, "[]<>"]
THEN {
newName ← Rope.Cat["[", platformHost, "]<", platformVolume, ">"];
newName ← Rope.Cat[newName, body];
}
ELSE newName ← Rope.Cat[
"[",
IF copyFrom
THEN
FSPseudoServers.TranslateForRead[Rope.Substr[newName, cp.server.start, cp.server.length]].first
ELSE
FSPseudoServers.TranslateForWrite[Rope.Substr[newName, cp.server.start, cp.server.length]],
"]<",
Rope.Substr[newName, cp.dir.start, Rope.Length[newName]]];
};
DoCopy:
PROC [from, to:
ROPE,
setKeep:
BOOL ←
FALSE, keep:
CARDINAL ← 1,
wantedCreatedTime: BasicTime.
GMT ← BasicTime.nullGMT,
remoteCheck:
BOOL ←
TRUE,
attach:
BOOL ←
FALSE,
wDir:
ROPE ←
NIL
]
RETURNS [toFName:
ROPE] =
{
fromString: LONG STRING ← NIL;
toString: LONG STRING ← NIL;
x1, x2: ROPE;
log: STREAM ← cmd.out;
noBind: BOOLEAN ← FALSE;
gvName: LONG STRING ← NIL;
gvPasswd: LONG STRING ← NIL;
chName: LONG STRING ← NIL;
chPasswd: LONG STRING ← NIL;
credentialsName: ROPE;
credentialsPasswd: ROPE;
server: ShortROPE ← UserProfile.Token["NSAgent.Server"];
x1 ← FixUpFilename[name: to, copyFrom: FALSE];
toString ← LOOPHOLE[Rope.Flatten[x1], LONG STRING];
x2 ← FixUpFilename[name: from, copyFrom: TRUE];
fromString ← LOOPHOLE[Rope.Flatten[x2], LONG STRING];
[credentialsName, credentialsPasswd] ← UserCredentials.Get[];
gvName ← LOOPHOLE[Rope.Flatten[credentialsName], LONG STRING];
gvPasswd ← LOOPHOLE[Rope.Flatten[credentialsPasswd], LONG STRING];
chName ← LOOPHOLE[Rope.Flatten[UserProfile.Token["NSAgent.CHName", credentialsName]], LONG STRING];
chPasswd ← LOOPHOLE[Rope.Flatten[credentialsPasswd], LONG STRING];
NSFilerRpcControl.ImportInterface[[
NIL, server] !
RPC.ImportFailed =>
{
noBind ← TRUE;
IO.PutRope[log, "\nNSFilerRpcControl.ImportInterface from "];
IO.PutRope[log, server];
IO.PutRope[log, " failed: "];
SELECT why
FROM
communications => IO.PutRope[log, "communications"];
badInstance => IO.PutRope[log, "badInstance"];
badVersion => IO.PutRope[log, "badVersion"];
wrongVersion => IO.PutRope[log, "wrongVersion"];
unbound => IO.PutRope[log, "unbound"];
stubProtocol => IO.PutRope[log, "stubProtocol"];
ENDCASE => IO.PutRope[log, "??"];
IO.PutRope[log, ".\n"];
CONTINUE;
}];
IF noBind THEN RETURN;
IO.PutRope[log, " Copying.."];
TRUSTED{NSFiler.Copy[from: fromString, to: toString, gvname: gvName, gvpasswd: gvPasswd, chname: chName, chpasswd: chPasswd]};
IO.PutRope[log, ".."];
TRUSTED{NSFilerRpcControl.UnimportInterface[]};
IO.PutRope[log, "done.\n"];
};
HandleAFile:
PROC [to, from:
ROPE] = {
Process.CheckForAbort[];
cmd.out.PutF[" %g ← %g", [rope[to]], [rope[from]]];
{
ENABLE
NSFiler.Error =>
IF reason # noError THEN {msg ← huhMsg[reason]; GO TO skipIt};
IF updateOnly
THEN {
sourceTime: BasicTime.GMT ← BasicTime.nullGMT;
destTime: BasicTime.GMT ← BasicTime.nullGMT;
sourceTime ← NSFiler.FileInfo[from].created;
destTime ← NSFiler.FileInfo[to ! NSFiler.Error => IF error.group # bug THEN CONTINUE].created;
IF sourceTime = destTime AND sourceTime # BasicTime.nullGMT THEN {
This file does not need a copy, since it has the same create date as the destination file. We have been instructed to trust the create date.
cmd.out.PutRope["\n -- not copied, create dates match"];
GO TO skipIt;
};
cmd.out.PutRope["\n updateOnly NOT IMPLEMENTED"];
};
IF doACopy
THEN [] ← DoCopy[from: from, to: to, keep: createKeep, attach: NOT forceCopy]
ELSE cmd.out.PutRope["\n Rename NOT IMPLEMENTED"];
ELSE NSFiler.Rename[from: from, to: to];
};
cmd.out.PutRope["\n"];
};
argv: CommandTool.ArgumentVector ← CommandTool.Parse[cmd
! CommandTool.Failed => {msg ← errorMsg; GO TO Die}];
nArgs: NAT ← argv.argc;
{
Process switches
i: NAT ← 1;
length: INT;
Bump:
PROC [scratch:
NAT] = {
FOR j:
NAT
IN (scratch..nArgs)
DO
argv[j - 1] ← argv[j];
ENDLOOP;
nArgs ← nArgs - 1;
};
WHILE i < nArgs
DO
length ← argv[i].Length[];
SELECT
TRUE
FROM
length = 0 => Bump[i];
argv[i].Fetch[0] = '- => {
FOR j:
INT
IN [1..length)
DO
SELECT argv[i].Fetch[j]
FROM
'c, 'C => forceCopy ← TRUE;
'r, 'R => retainStructure ← TRUE;
'u, 'U => updateOnly ← TRUE;
'x, 'X => exactLevelMatch ← TRUE;
ENDCASE;
ENDLOOP;
Bump[i];
};
ENDCASE => i ← i + 1;
ENDLOOP;
}; -- end of switch processing
First find out whether there is a ← anywhere. If there is, it must be the second arg.
leftArrowExists ← nArgs >= 3 AND Rope.Equal[argv[2], "←"];
FOR i:
NAT
IN [1..nArgs)
DO
argv[i] ← FileNames.ConvertToSlashFormat[FileNames.ResolveRelativePath[argv[i]]];
ENDLOOP;
IF leftArrowExists
THEN {
IF FileNames.IsADirectory[argv[1]]
THEN destinationDirectory ← argv[1]
ELSE {
IF nArgs # 4
OR FileNames.IsAPattern[argv[1]]
OR FileNames.IsAPattern[argv[3]]
THEN RETURN[$Failure, "Bad syntax for copying a file"];
HandleAFile[from: argv[3], to: argv[1]];
RETURN[IF msg # NIL THEN $Failure ELSE NIL, msg];
};
}
ELSE destinationDirectory ←
FileNames.ConvertToSlashFormat[FileNames.CurrentWorkingDirectory[]];
If we get here, then for each of the filenames and patterns, copy the file to the destination directory.
FOR i:
NAT
IN [(
IF leftArrowExists
THEN 3
ELSE 1) .. nArgs)
DO
IF FileNames.IsADirectory[argv[i]]
THEN {
msg ← Rope.Concat["Cannot copy a directory: ", argv[i]];
GO TO Die;
};
IF FileNames.IsAPattern[argv[i]]
THEN {
pattern: ROPE ← argv[i];
handleIt:
FS.NameProc = {
[fullFName: ROPE] RETURNS [continue: BOOL]
to: ROPE;
short: ROPE ← NIL;
continue ← TRUE;
IF SkipFunny[fullFName] THEN RETURN;
IF exactLevelMatch AND anglesRequired # CountAngles[fullFName] THEN RETURN;
fullFName ← FileNames.ConvertToSlashFormat[fullFName];
IF retainStructure
THEN to ← FileNames.StripVersionNumber[fullFName.Substr[dirBeforeStarLength]]
ELSE to ← FileNames.GetShortName[fullFName];
to ← Rope.Concat[destinationDirectory, to];
HandleAFile[from: fullFName, to: to];
RETURN[msg = NIL];
};
IF pattern.Find["!"] = -1 THEN pattern ← Rope.Concat[pattern, "!H"];
IF pattern.Fetch[0] # '/
THEN
pattern ← Rope.Concat[FileNames.CurrentWorkingDirectory[], pattern];
IF exactLevelMatch THEN anglesRequired ← CountAngles[pattern];
dirBeforeStar ← FileNames.DirectoryContaining[
path: pattern, pos: Rope.Index[s1: pattern, s2: "*"]];
dirBeforeStarLength ← dirBeforeStar.Length[];
Count the number of slashes after the first *.
FS.EnumerateForNames[pattern, handleIt
! FS.Error => IF error.group # $bug THEN {msg ← FSErrorMsg[error]; GO TO Die}];
}
ELSE {
to: ROPE ← Rope.Concat[destinationDirectory, FileNames.GetShortName[argv[i]]];
HandleAFile[from: argv[i], to: to];
IF msg # NIL THEN GO TO Die;
};
Process.CheckForAbort[];
ENDLOOP;
EXITS
Die => result ← $Failure;
};