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; }; }; 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 { 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; }; 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 = { 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] ~ { 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]]; }; 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. ώ TCCopyImpl.mesa Copyright Σ 1991, 1992 by Xerox Corporation. All rights reserved. taken from PFSCommandsImpl Willie-s, February 19, 1991 11:53 am PST Copying using Retrieve and Store 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. If we get here, then for each of the filenames and patterns, copy the file to the destination directory. [name: PATH] RETURNS [continue: BOOL] don't have FileInfo in TC interface Initialization Κ /–(cedarcode) style•NewlineDelimiter ™codešœ™Kšœ Οeœ7™BK™K™(K™šΟk ˜ K˜Kšœ žœ˜)Kšžœ˜K˜ Kšžœ˜Kšœ ˜ Kšœžœ˜Kšœ˜Kšœ ˜ ——K˜šΠln œžœž˜Kšžœ žœ žœ'˜LKšœž˜K˜Kšžœžœžœ˜Kšžœžœ žœ˜šžœžœžœžœ˜K˜—šΟnœžœžœ˜ K˜—š   œžœžœžœžœ˜;Kšžœ žœžœ ˜!Kšžœ žœ žœ žœ žœ žœžœ˜WKšžœ ˜K˜K˜—Kšœžœžœžœ˜,š  œžœ žœžœžœžœžœ˜MKšœžœ&žœžœ˜h•StartOfExpansion6[pattern: ROPE, object: ROPE, case: BOOL _ TRUE]šžœ!žœ˜)Kšœžœ˜ K–[self: STREAM, char: CHAR]šžœ ˜"Kš œžœžœžœžœ˜Qšžœžœž˜Kšœžœ˜!Kšžœžœ˜#—Kšœ˜—K˜——head™ š œ˜#Kšœžœžœ˜!Kšœžœžœ˜Kšœžœ˜Kšœ žœžœ˜Kšœžœžœ˜Kšœ žœžœ˜Kšœžœžœ˜Kšœžœ˜K˜š  œžœ žœ˜&Kšœ žœ žœ˜5Kšœžœžœ˜$Kšœ žœžœ˜(Kšœ˜Kšœ<˜<šœž˜šžœ ˜ Kšžœžœžœžœ ˜D—šžœ žœ˜Kšœžœ žœ˜(Kš œ žœžœ žœžœžœ ˜Ušžœžœ žœžœ˜;Kšœ{™{Kšœ9˜9Kšžœžœ˜ K˜—K˜—šž˜–Κ[fileName: ROPE, accessOptions: FS.AccessOptions _ read, streamOptions: FS.StreamOptions _ (3)[TRUE, TRUE, TRUE], keep: CARDINAL _ 1B (1), createByteCount: FS.ByteCount _ 2560, streamBufferParms: FS.StreamBufferParms _ [vmPagesPerBuffer: 8, nBuffers: 2], extendFileProc: FS.ExtendFileProc, wantedCreatedTime: GMT _ nullGMT, remoteCheck: BOOL _ TRUE, wDir: ROPE _ NIL, checkFileType: BOOL _ FALSE, fileType: FS.FileType _ [0B (0)]]šœžœžœžœI˜fšœžœ žœžœ˜+KšœžœL˜TKšžœžœ˜ K˜——K˜Kšœžœ˜Kšœžœžœ˜š œžœ˜šΟbœžœ˜0Kšžœ ˜K˜—K˜Kšžœ1˜4Kšžœ˜K˜—š œžœžœ˜Kš œžœžœžœžœ˜3Kšœ#žœžœ4˜dKšœžœ˜ šž˜Kšœžœ!˜+šžœžœ˜"K˜Kšžœ%˜'Kšžœ˜Kšžœ˜Kšœ˜—šž˜Kšžœ%˜'—Kšžœ˜K˜——K˜<šžœ˜ šœžœ˜'šœžœ žœžœ˜+KšœžœL˜TKšžœžœ˜ 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˜"šžœ˜ K˜YKšžœ˜ K˜——Kšœžœ˜ Kšžœ˜—Kšœ˜—šœ žœžœ$˜SKšœžœ˜Kšœ˜—Kšžœ,žœ˜E—Kšžœ˜—K˜šžœ˜šžœ˜Kšœžœžœ˜,K˜šžœ˜Kšžœ˜"šžœ˜šžœ žœ!žœ%˜WKšžœžœ,˜7—Kšœžœ,˜AKš žœžœžœžœ žœžœ˜1K˜——K˜—šžœ˜Kšœžœ ˜%K˜Kšœ˜——K˜K™hš žœžœžœžœžœžœž˜?Kšœžœžœ˜,šžœžœ˜K˜;Kšžœžœ˜ K˜—šžœ ˜"šžœ˜Kšœ žœ ˜šœ žœ ˜Kšœžœžœ žœ™%Kšœžœ˜ Kšœžœžœ˜Kšœ žœ˜Kš žœžœ,žœžœžœ˜Ušžœ˜Kšžœ#˜'Kšžœ;˜?—K˜K˜,Kšœ ˜ Kšžœžœ˜K˜—Kšžœ&žœ/˜[K˜$Kšžœžœ7˜Nšžœžœ˜Kšœžœžœ˜šœ$˜$Kš žœžœ+žœžœžœ&˜rKšœ˜—Kšœ0˜0Kšœ4žœ˜9K˜—šžœ$˜'Kš œžœ žœžœžœžœ˜Q—Kšœ˜—šžœ˜Kšœžœ ˜KšœžœF˜NK˜,Kšœ ˜ Kš žœžœžœžœžœ˜K˜——Kšœ˜Kšžœ˜—šž˜K˜—K˜K˜—š   œžœžœžœžœ žœ˜NK™#š œžœ ˜K˜Kšžœžœ˜K˜—K˜4Kšžœ!žœ7˜^Kšžœ˜!K˜K˜—š   œžœ žœ žœžœ˜;šžœ˜Kšžœžœ˜!Kšžœžœ7˜B—Kšœ˜K˜—š   œžœ žœ žœžœ˜<šžœ˜Kšžœžœ˜Kšžœžœ7˜B—Kšœ˜——™˜šœ žœ˜˜₯K˜—K˜š œžœ˜Kšœ7˜7K˜K˜—K˜—K˜—Kšžœ˜—…—$+Q