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"]; FSErrorMsg: PROC [error: FS.ErrorDesc] RETURNS [ROPE] = { SELECT error.group FROM lock => RETURN[" -- locked!\n"]; ENDCASE => IF error.code = $unknownFile THEN RETURN [" -- not found!\n"] ELSE RETURN[Rope.Cat[" -- FS.Error: ", error.explanation, "\n"]]; }; SkipFunny: PROC [fullFName: ROPE] RETURNS [BOOL] = { lastPoint, bangIndex: INT _ 0; FOR i: INT DECREASING IN [0 .. fullFName.Length[]) DO SELECT fullFName.Fetch[i] FROM '! => bangIndex _ i; '> => {lastPoint _ i; EXIT}; ENDCASE; ENDLOOP; RETURN[ bangIndex = lastPoint + 1 ]; -- skip [..]<..>!1 }; CountAngles: PROC [pattern: ROPE] RETURNS [count: INT _ 0] = { len: INT = Rope.Length[pattern]; pos: INT _ 0; WHILE pos < len DO pos _ Rope.SkipTo[pattern, pos+1, ">/"]; count _ count + 1; ENDLOOP; IF Rope.Match["/*", pattern] THEN count _ count - 1; }; createKeep: CARDINAL _ 2; CopyAndRename: Commander.CommandProc = { 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; 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"]; EXITS skipIt => {}; }; cmd.out.PutRope["\n"]; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; nArgs: NAT _ argv.argc; { 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 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[]]; 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 = { 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[]; 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; }; Init: PROCEDURE = { doc: ROPE = "NSCopy -- Copy file between PUP & NS Lands.\n NSCopy newFile _ oldFile, or \n NSCopy directory _ {pattern}*\n -c: force copy, -r: retain structure, -u: update only, -x: eXact level match"; Commander.Register[ key: "///Commands/NSCopy", proc: CopyAndRename, doc: doc, clientData: $Copy, interpreted: TRUE ]; }; Init[]; END... |NSCopyImpl.mesa Copyright c 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" This routine counts either angle brackets or slashes (not counting a slash in the first position, which does not correspond to a slash). The idea is that this gives a measure of the number of levels in a path name. The count is too high by 1 (we skipped the first char anyway), since the first two slashes indicate server delimiters rather than directory element delimiters. [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] 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; }; ELSE NSFiler.Rename[from: from, to: to]; Process switches First find out whether there is a _ anywhere. If there is, it must be the second arg. If we get here, then for each of the filenames and patterns, copy the file to the destination directory. [fullFName: ROPE] RETURNS [continue: BOOL] Count the number of slashes after the first *. key is the name of the command. Registering the command under the "///Commands/" directory indicates that this command should be globally known and useful. If no directory is given, the command is registered under the current working directory, which is the proper place for more specific commands. proc is called to execute the command. doc gives the brief documentation for the command. This should be as brief as possible to remind the user of the function and options. clientData is passed to the command through cmd.procData.clientData. This is useful when various commands are registered with the same procedure, since the clientData can be used to tell the invocations apart. interpreted indicates whether or not the arguments should be scanned for special characters. The default is TRUE, which implies that I/O redicrection characters should be interpreted by the CommandTool. Initialization Ê ˜˜šœ™Jšœ Ïmœ=™HIcode™+K™+K˜Jšœ~™~J™Jšœ‚™‚—K™šÏk ˜ Kšœ˜Kšœ˜Kšœ žœžœ ˜Kšœ žœ!˜0Kšœ žœ!˜2Kšœžœ˜)Kšœ žœ—˜¦KšžœžœQ˜YKšœžœ'˜Kšœ×™×Kšœžœ˜ Kšœžœ˜ šžœ ž˜Kšœ(˜(Kšœ˜Kšžœ˜—šžœž˜!KšœŸ™ŸKšœ˜—K˜K˜—K˜Kšœ žœ˜K˜š  œ˜(Kš œžœžœ žœžœžœžœ™Ešœ™Kšœžœžœ™1Kšœ6™6—Kšœ žœžœ˜Kšœžœžœ˜!Kšœžœ˜Kšœ žœ#˜0Kšœ žœ$˜1Kšœžœ˜Kšœžœ˜Kšœžœžœ˜Kšœ žœžœ˜Kšœžœžœ˜Kšœžœ˜K˜š¡ œžœžœžœ žœžœ žœ˜RKšœžœ˜ Kšœžœ˜(Kšœžœ+˜?Kšœžœ˜ Kšœžœ˜K˜Kšœžœ˜3Kšœ$˜$Kšœ5˜5šžœžœ˜$KšœA˜AKšœ"˜"Kšœ˜—šžœ˜Kšœ˜šžœ ž˜Kšœ_˜_—šž˜Kšœ[˜[—Kšœ˜Kšœ:˜:—K˜K˜—š¡œžœ žœ žœžœžœ$žœ$žœžœ žœžœ žœžœžœ žœ˜àK˜Kšœ žœžœžœ˜Kšœ žœžœžœ˜Kšœžœ˜ Kšœžœ ˜Kšœžœžœ˜Kšœžœžœžœ˜Kšœ žœžœžœ˜Kšœžœžœžœ˜Kšœ žœžœžœ˜Kšœžœ˜Kšœžœ˜Kšœ8˜8K˜Kšœ'žœ˜.Kšœ žœžœžœ˜3Kšœ)žœ˜/Kšœ žœžœžœ˜5K˜Kšœ=˜=K˜Kšœž œ žœžœ˜>Kšœž œ"žœžœ˜BK˜Kšœž œEžœžœ˜cKšœž œ"žœžœ˜BK˜šœ#žœ ˜1šžœ˜K˜Kšœ žœ˜Kšžœ;˜=Kšžœ˜Kšžœ˜šžœž˜Kšœžœ ˜4Kšœžœ˜.Kšœžœ˜,Kšœžœ˜0Kšœ žœ˜&Kšœžœ˜0Kšžœžœ˜!—Kšžœ˜Kšžœ˜ K˜——K˜Kšžœžœžœ˜K˜Kšžœ˜Kšžœw˜~Kšžœ˜Kšžœ(˜/Kšžœ˜K˜K˜K˜—š¡ œžœ žœ˜&Kšœ˜Kšœ4˜4šœž˜šœ˜Kšžœžœžœžœ ˜>—šžœ žœ˜Kšœžœ˜.Kšœžœ˜,Kšœ žœ™,Kš œ žœžœ žœžœžœ ™^šžœžœ žœ™BKšœ™Kšœ9™9Kšžœžœ™ Kšœ™—Kšœ2˜2Kšœ˜—šžœ˜ Kšžœ;žœ ˜MKšžœ/˜3Kšžœžœ™(—šž˜K˜ —K˜—Kšœ˜K˜—K˜šœ8˜8Kšœ)žœžœ˜5—Kšœžœ ˜K˜˜K™Kšœžœ˜ Kšœžœ˜ š¡œžœ žœ˜šžœžœžœž˜!K˜Kšžœ˜—K˜K˜—K™šžœ ž˜K˜šžœžœž˜Kšœ˜šœ˜šžœžœžœ ž˜šžœž˜Kšœžœ˜Kšœžœ˜!Kšœžœ˜Kšœžœ˜!Kšžœ˜—Kšžœ˜—K˜K˜—Kšžœ˜—Kšžœ˜—Kšœ¢˜—K™K™VK˜Kšœžœ˜:šžœžœžœ ž˜KšœQ˜QKšžœ˜—K˜šžœ˜šžœ˜šžœ ˜"Kšžœ˜#šžœ˜šžœ žœžœ˜NKšžœžœ,˜7—Kšœ(˜(Kš žœžœžœžœ žœžœ˜1K˜——K˜—šžœ˜KšœD˜D——K˜K™hš žœžœžœžœžœžœž˜>šžœ!žœ˜)Kšœ8˜8Kšžœžœ˜ K˜—šžœ˜ šžœ˜Kšœ žœ ˜šœ žœ ˜Kšœ žœžœ žœ™*Kšœžœ˜ Kšœžœžœ˜Kšœ žœ˜Kšžœžœžœ˜$Kšžœžœ)žœžœ˜KKšœ6˜6šžœ˜KšžœI˜MKšžœ(˜,—Kšœ+˜+Kšœ%˜%Kšžœžœ˜K˜—Kšžœžœ&˜Dšžœž˜KšœD˜D—Kšžœžœ'˜>šœ.˜.Kšœ6˜6—šœ-˜-K™.—šžœ$˜&Kš œžœ žœžœžœžœ˜O—Kšœ˜—šžœ˜KšœžœF˜NKšœ#˜#Kš žœžœžœžœžœ˜K˜——Kšœ˜Kšžœ˜—šž˜Kšœ˜—K˜K˜—š¡œž œ˜šœžœÃ˜ÌK˜šœ˜šœ˜KšÏoœ©™¬—šœ˜Kš£œ"™&—šœ ˜ Kš£œ„™‡—šœ˜Kš£ œÈ™Ò—šœ ž˜Kš£ œbžœžœžœB™Ë—Kšœ˜—K˜K˜—K•StartOfExpansionx[key: ROPE, proc: Commander.CommandProc, doc: ROPE _ NIL, clientData: REF ANY _ NIL, interpreted: BOOL _ TRUE]˜™K˜—˜K˜——šžœ˜K˜——…—"Ú:î