DIRECTORY AlpFile USING[ GetSize, SetSize, Handle, LockOption, ReadProperties, ReadPages, WritePages, WriteProperties], AlpineEnvironment USING[ AccessList, ByteCount, bytesPerPage, nullUniversalFile, nullVolumeGroupID, Outcome, OwnerName, OwnerPropertySet, OwnerPropertyValuePair, PageCount, Principal, Property, PropertyValuePair, UniversalFile, wordsPerPage ], AlpineFile USING [ PropertySet], AlpDirectory, AlpineDirectory, AlpineInterimDirectory, AlpInstance, AlpTransaction, BasicTime USING[ GMT], Buttons USING[ ButtonProc ], CedarProcess USING[ SetPriority ], FS USING[ Close, ComponentPositions, Copy, Create, Delete, EnumerateForNames, Error, ExpandName, GetInfo, GetName, Open, OpenFile, NameProc, Read, Rename, SetByteCountAndCreatedTime, Write], IO, List, Menus, RefText, Rope, RPC, ViewerClasses USING[ Viewer ], ViewerTools USING[ GetContents], VM USING[ AddressForPageNumber, Allocate, Free, Interval], YodelData; YodelUserImpl2: CEDAR PROGRAM IMPORTS AlpDirectory, AlpFile, AlpInstance, AlpTransaction, AlpineInterimDirectory, CedarProcess, FS, IO, List, Rope, RPC, ViewerTools, VM, YodelData EXPORTS YodelData = BEGIN OPEN AE: AlpineEnvironment, YodelData; ROPE: TYPE = Rope.ROPE; ByteCount: TYPE = AE.ByteCount; bytesPerPage: INT = AE.bytesPerPage; PageCount: TYPE = AE.PageCount; PropertyValuePair: TYPE = AE.PropertyValuePair; UniversalFile: TYPE = AE.UniversalFile; wordsPerPage: INT = AE.wordsPerPage; PagesForBytes: PROC [byteLength: ByteCount] RETURNS [pageCount: PageCount] = { RETURN [(byteLength+bytesPerPage-1)/bytesPerPage]; }; BytesForPages: PROC [pageCount: PageCount] RETURNS [byteLength: ByteCount] = { RETURN [pageCount * bytesPerPage]; }; hasPattern: PUBLIC SAFE PROC [pattern: ROPE] RETURNS [BOOL] = { RETURN [-1 # Rope.Find[s1: pattern, s2: "*"]]; }; ParseDArgs: PROC [user: ROPE, d: MyData] RETURNS [destServer, destDir, destFile: ROPE _ NIL, parseError: BOOL _ FALSE, errorExplanation: ROPE _ NIL] = { destFileTemp: ROPE ; fullFName: ROPE; cp: FS.ComponentPositions; dirOmitted: BOOL; trimStar: BOOL _ FALSE; destFileTemp _ ViewerTools.GetContents[d.dest]; [fullFName: fullFName, cp: cp, dirOmitted: dirOmitted] _ FS.ExpandName[name: destFileTemp, wDir: "[]<>" ! FS.Error => { parseError _ TRUE; errorExplanation _ error.explanation; CONTINUE ; }; ]; IF parseError THEN { [fullFName: fullFName, cp: cp, dirOmitted: dirOmitted] _ FS.ExpandName[name: (destFileTemp _ destFileTemp.Concat["*"]), wDir: "[]<>" ! FS.Error => { GOTO FSError ; }; ]; trimStar _ TRUE; }; parseError _ FALSE; destServer _ fullFName.Substr[cp.server.start, cp.server.length]; IF dirOmitted THEN destFile _ "" ELSE { destFile _ fullFName.Substr[cp.dir.start - 1]; IF trimStar THEN destFile _ destFile.Substr[0, destFile.Length[]-1]; }; [destDir] _ DecomposePattern[server: destServer, pattern: destFile, user: user]; EXITS FSError => {}; }; PrintResult: PUBLIC PROC [out: IO.STREAM, resultList: LIST OF REF ANY] RETURNS [printedSomething: BOOL _ FALSE] = { DO nowRope: ROPE _ NARROW[IF resultList = NIL THEN NIL ELSE resultList.first]; IF resultList = NIL THEN EXIT; resultList _ resultList.rest; out.PutF[" %g\n", IO.rope[nowRope]]; printedSomething _ TRUE ; ENDLOOP; }; isDirectory: PROC [fileName: ROPE] RETURNS [dir: BOOL] = { len: INT ; IF fileName.IsEmpty[] THEN RETURN[TRUE]; len _ fileName.Length[]; IF fileName.InlineFetch[len-1] = '> OR fileName.InlineFetch[len-1] = '/ THEN RETURN [TRUE] ELSE RETURN[FALSE]; }; fileCopy: PROC [trans: AlpTransaction.Handle, to: ROPE, from: ROPE, fullCopy: BOOL, user: ROPE _ NIL, password: ROPE _ NIL, expunging: BOOLEAN _ FALSE, excessBytes: AlpineEnvironment.ByteCount _ 0, caller: AlpineEnvironment.Principal, key: RPC.EncryptionKey, serverForTrans: ROPE, d: MyData] RETURNS [copyResult: LIST OF REF ANY _ NIL] = { FileStateObject: TYPE = RECORD [ SELECT type: {alpine, fs} FROM alpine => [fileHandle: AlpFile.Handle], fs => [openFile: FS.OpenFile], ENDCASE]; AlpineFileStateObject: TYPE = FileStateObject.alpine; FSFileStateObject: TYPE = FileStateObject.fs; FileState: TYPE = REF FileStateObject; AlpineFileState: TYPE = REF AlpineFileStateObject; FSFileState: TYPE = REF FSFileStateObject; CreateFileState: PROC [file: ROPE, createOptions: AlpineInterimDirectory.CreateOptions, createLength: INT _ 0, trans: AlpTransaction.Handle _ transHandle] RETURNS [FileState] = { IF Rope.Match[pattern: "[*.alpine]*", object: file, case: FALSE] THEN { alpineFileState: AlpineFileState; rightSquareBracket: INT _ file.Find["]", 1]; IF rightSquareBracket IN [-1..1] THEN ERROR; IF createOptions = oldOnly THEN { alpineFileState _ NEW[AlpineFileStateObject _ [alpine[fileHandle: AlpDirectory.OpenFile[transHandle: trans, name: file, access: readOnly, lock: [read, wait], recoveryOption: log, referencePattern: sequential].openFileID]]]; } ELSE { alpineFileState _ NEW[AlpineFileStateObject _ [alpine[fileHandle: AlpDirectory.OpenFile[transHandle: trans, name: file, access: readWrite, lock: [read, wait], recoveryOption: log, referencePattern: sequential].openFileID]]]; AlpFile.SetSize[handle: alpineFileState.fileHandle, size: PagesForBytes[createLength]]; }; RETURN [alpineFileState]; } ELSE { openFile: FS.OpenFile; fsFileState: FSFileState; IF createOptions = oldOnly THEN openFile _ FS.Open[file] ELSE { openFile _ FS.Create[ name: file, setPages: TRUE, pages: PagesForBytes[createLength]]; }; fsFileState _ NEW[FSFileStateObject _ [fs[openFile: openFile]]]; RETURN [fsFileState]; }; }; CleanupFileState: PROC [fileState: FileState] = { WITH fileState SELECT FROM alpineFileState: AlpineFileState => { }; fsFileState: FSFileState => { fsFileState.openFile.Close[]; }; ENDCASE => ERROR; }; GetFileProperties: PROC [fileState: FileState] RETURNS [ byteLength: AlpineEnvironment.ByteCount, createTime: BasicTime.GMT, mutableAlpineProperites: LIST OF AlpineEnvironment.PropertyValuePair _ NIL, highWaterMark: AlpineEnvironment.PageCount _ 0, alpineSize: INT _ 0] = { WITH fileState SELECT FROM alpineFileState: AlpineFileState => { properties: LIST OF PropertyValuePair ; alpineSize _ alpineFileState.fileHandle.GetSize[]; properties _ alpineFileState.fileHandle.ReadProperties[]; WHILE properties # NIL DO property: AlpineEnvironment.PropertyValuePair _ properties.first ; properties _ properties.rest; SELECT property.property FROM byteLength => { byteLength _ NARROW[property, AlpineEnvironment.PropertyValuePair.byteLength].byteLength; mutableAlpineProperites _ CONS[[byteLength[byteLength]], mutableAlpineProperites]; }; createTime => { createTime _ NARROW[property, AlpineEnvironment.PropertyValuePair.createTime].createTime; mutableAlpineProperites _ CONS[[createTime[createTime]], mutableAlpineProperites]; }; highWaterMark => { highWaterMark _ NARROW[property, AlpineEnvironment.PropertyValuePair.highWaterMark].highWaterMark; mutableAlpineProperites _ CONS[[highWaterMark[highWaterMark]], mutableAlpineProperites]; }; modifyAccess => { modifyAccess: AlpineEnvironment.AccessList _ NARROW[property, AlpineEnvironment.PropertyValuePair.modifyAccess].modifyAccess; mutableAlpineProperites _ CONS[[modifyAccess[modifyAccess]], mutableAlpineProperites]; }; readAccess => { readAccess: AlpineEnvironment.AccessList _ NARROW[property, AlpineEnvironment.PropertyValuePair.readAccess].readAccess; mutableAlpineProperites _ CONS[[readAccess[readAccess]], mutableAlpineProperites]; }; ENDCASE; ENDLOOP; }; fsFileState: FSFileState => { [bytes: byteLength, created: createTime] _ fsFileState.openFile.GetInfo[]; }; ENDCASE => ERROR; }; SetFileProperties: PROC [fileState: FileState, byteLength: AlpineEnvironment.ByteCount, createTime: BasicTime.GMT, alpineProperties: LIST OF AlpineEnvironment.PropertyValuePair _ NIL] = { WITH fileState SELECT FROM alpineFileState: AlpineFileState => { IF fullCopy AND alpineProperties # NIL THEN alpineFileState.fileHandle.WriteProperties[alpineProperties] ELSE alpineFileState.fileHandle.WriteProperties[LIST[[byteLength[byteLength]], [createTime[createTime]]]]; }; fsFileState: FSFileState => { fsFileState.openFile.SetByteCountAndCreatedTime[bytes: byteLength, created: createTime]; }; ENDCASE => ERROR; }; FillBuffer: PROC [fromFile: FileState, pagesToMove: CARDINAL] = { WITH fromFile SELECT FROM alpineFileState: AlpineFileState => TRUSTED { alpineFileState.fileHandle.ReadPages[ pageRun: [firstPage: pagesCopied, count: pagesToMove], pageBuffer: DESCRIPTOR [ bufferPtr, pagesToMove*wordsPerPage]]; }; fsFileState: FSFileState => { TRUSTED BEGIN fsFileState.openFile.Read[from: pagesCopied, nPages: pagesToMove, to: bufferPtr]; END; }; ENDCASE => ERROR; }; EmptyBuffer: PROC [toFile: FileState, pagesToMove: CARDINAL] = { WITH toFile SELECT FROM alpineFileState: AlpineFileState => { TRUSTED BEGIN alpineFileState.fileHandle.WritePages[ pageRun: [firstPage: pagesCopied, count: pagesToMove], pageBuffer: DESCRIPTOR [ bufferPtr, pagesToMove*wordsPerPage]]; END; }; fsFileState: FSFileState => { TRUSTED BEGIN fsFileState.openFile.Write[to: pagesCopied, nPages: pagesToMove, from: bufferPtr]; END; }; ENDCASE => ERROR; }; moveTheBits: PROC [] RETURNS [aborted: BOOL _ FALSE]= { pagesCopied _ 0; UNTIL pagesCopied = pageCount DO pagesLeft: CARDINAL _ pageCount-pagesCopied; pagesToMove: CARDINAL _ MIN [bufferLen, pagesLeft]; IF d.stopFlag THEN { WITH toFile SELECT FROM alpineFileState: AlpineFileState => { [] _ transHandle.Finish[requestedOutcome: abort, continue: FALSE] ; IF secondTransHandle # NIL THEN [] _ secondTransHandle.Finish[requestedOutcome: abort, continue: FALSE] ; }; fsFileState: FSFileState => { fullF: ROPE; created: BasicTime.GMT ; [fullFName: fullF] _ fsFileState.openFile.GetName[]; [created: created] _ fsFileState.openFile.GetInfo[]; fsFileState.openFile.Close[]; FS.Delete[name: fullF, wantedCreatedTime: created]; }; ENDCASE => ERROR; RETURN[TRUE]; }; FillBuffer[fromFile, pagesToMove]; EmptyBuffer[toFile, pagesToMove]; pagesCopied _ pagesCopied + pagesToMove; ENDLOOP; }; processCopy: PROC [sourceName: ROPE] RETURNS [result: ROPE _ NIL] = { destFileName: ROPE _ fullNDest; sourceIsASegment: BOOL _ FALSE; fullSourceFName: ROPE; sourceFcp: FS.ComponentPositions; parseError: BOOL _ FALSE; errorExplanation: ROPE; IF transHandle # NIL AND d.assertWheel THEN transHandle.AssertAlpineWheel[TRUE]; [fullFName: fullSourceFName, cp: sourceFcp] _ FS.ExpandName[name: sourceName, wDir: "[]<>"! FS.Error => { parseError _ TRUE; errorExplanation _ error.explanation; CONTINUE ; }; ]; IF parseError THEN { result _ IO.PutFR["Bad file name %g could NOT be copied because %g", IO.rope[to], IO.rope[errorExplanation]]; RETURN; }; IF toDir THEN { destFileName _ Rope.Concat[fullNDest, Rope.Substr[fullSourceFName, sourceFcp.base.start, sourceFcp.base.length]]; IF sourceFcp.ext.length # 0 THEN destFileName _ destFileName.Cat[".", Rope.Substr[fullSourceFName, sourceFcp.ext.start, sourceFcp.ext.length]]; }; [byteLength, createTime, alpineProperties, highWaterMark, alpineSize] _ GetFileProperties[fromFile]; IF byteLength < 0 AND (~alpineSourceAndResult OR ~fullCopy) THEN RETURN["The source file has negative length. Copy aborted."]; sourceIsASegment _ Rope.Equal["segment", Rope.Substr[fullSourceFName, sourceFcp.ext.start, sourceFcp.ext.length], FALSE]; SELECT TRUE FROM alpineSourceAndResult AND fullCopy => createLength _ MAX[byteLength, BytesForPages[highWaterMark], BytesForPages[alpineSize]]; alpineSource AND ~alpineSourceAndResult AND sourceIsASegment => { IF byteLength = 0 THEN byteLength _ BytesForPages[alpineSize]; createLength _ byteLength; }; ENDCASE => createLength _ byteLength; IF Rope.IsEmpty[toServer] OR alpineResult THEN { IFSResult _ FALSE; IF alpineSourceAndResult AND ~Rope.Equal[ s1: fromServer, s2: toServer, case: FALSE] THEN { twoDifferentAlpine _ TRUE; secondInst _ AlpInstance.Create[fileStore: toServer, caller: caller, key: key]; secondTransHandle _ AlpTransaction.Create[secondInst]; IF d.assertWheel THEN secondTransHandle.AssertAlpineWheel[TRUE]; toFile _ CreateFileState[file: destFileName, createOptions: none, createLength: createLength, trans: secondTransHandle]; } ELSE toFile _ CreateFileState[file: destFileName, createOptions: none, createLength: createLength]; } ELSE { IFSResult _ TRUE; toFile _ CreateFileState[file: "///temp/qqqscratch.yodel", createOptions: none, createLength: createLength]; }; pageCount _ PagesForBytes[createLength]; TRUSTED { bufferInterval _ VM.Allocate[count: bufferLen]; bufferAllocated _ TRUE ; bufferPtr _ VM.AddressForPageNumber[bufferInterval.page]; }; IF moveTheBits[] THEN RETURN["Copy aborted "]; SetFileProperties[toFile, byteLength, createTime, alpineProperties]; CleanupFileState[fromFile]; CleanupFileState[toFile]; IF twoDifferentAlpine THEN { IF (secondTransHandle.Finish[requestedOutcome: commit, continue: FALSE] # commit) THEN RETURN["Transaction on destination server failed to commit - validity of copy is in question"]; }; TRUSTED { VM.Free[bufferInterval]; bufferAllocated _ FALSE ; }; IF IFSResult THEN { [] _ FS.Copy[from: "///temp/qqqscratch.yodel", to: destFileName ]; FS.Delete[name: "///temp/qqqscratch.yodel"]; }; IF transHandle # NIL AND (transHandle.Finish[requestedOutcome: commit, continue: FALSE] # commit) THEN RETURN["Transaction on source server failed to commit - validity of copy is in question"]; result _ IO.PutFR["%g written", IO.rope[destFileName]]; }; transHandle: AlpTransaction.Handle _ trans; secondInst: AlpInstance.Handle _ NIL; secondTransHandle: AlpTransaction.Handle _ NIL; toFile, fromFile: FileState; byteLength: AlpineEnvironment.ByteCount; createTime: BasicTime.GMT; bufferLen: CARDINAL = 12; pageCount: PageCount ; bufferInterval: VM.Interval _ TRASH; bufferAllocated: BOOL _ FALSE ; bufferPtr: LONG POINTER; pagesCopied: INT; cp: FS.ComponentPositions; toServer, fromServer: ROPE ; fullNSource, fullNDest: ROPE ; IFSResult: BOOL ; alpineSource: BOOL ; alpineResult: BOOL ; alpineSourceAndResult: BOOL; twoDifferentAlpine: BOOL _ FALSE; alpineProperties: LIST OF AlpineEnvironment.PropertyValuePair _ NIL; highWaterMark: AlpineEnvironment.PageCount _ 0; createLength: AlpineEnvironment.ByteCount; alpineSize: INT; toDir: BOOL; fromWithVersion: ROPE _ from; parseOfToError: BOOL _ FALSE; { ENABLE UNWIND => { IF transHandle # NIL THEN [] _ AlpTransaction.Finish[transHandle, abort ! RPC.CallFailed => CONTINUE]; IF secondTransHandle # NIL THEN [] _ AlpTransaction.Finish[secondTransHandle, abort ! RPC.CallFailed => CONTINUE]; IF bufferAllocated THEN TRUSTED {VM.Free[bufferInterval]}; bufferAllocated _ FALSE; IF fromFile # NIL THEN CleanupFileState[fromFile ! FS.Error => CONTINUE]; IF toFile # NIL THEN CleanupFileState[toFile ! FS.Error => CONTINUE]; }; IF Rope.Find[from,"!"] < 0 THEN fromWithVersion _ from.Concat["!H"]; [fullFName: fullNSource, cp: cp] _ FS.ExpandName[name: fromWithVersion]; fromServer _ Rope.Substr[fullNSource, cp.server.start, cp.server.length]; alpineSource _ Rope.Match[pattern: "*.alpine", object: fromServer, case: FALSE] ; [fullFName: fullNDest, cp: cp] _ FS.ExpandName[name: to ! FS.Error => { parseOfToError _ TRUE; CONTINUE ; }; ]; IF parseOfToError THEN { [fullFName: fullNDest, cp: cp] _ FS.ExpandName[name: to.Concat["*"]]; fullNDest _ fullNDest.Substr[0, fullNDest.Length[]-1]; }; toServer _ Rope.Substr[fullNDest, cp.server.start, cp.server.length]; alpineResult _ Rope.Match[pattern: "*.alpine", object: toServer, case: FALSE] ; alpineSourceAndResult _ alpineResult AND alpineSource ; toDir _ isDirectory[fullNDest]; IF alpineSource THEN { previousFile: ROPE _ NIL; DO file: AE.UniversalFile; fullPathName, link: ROPE; oneResult: ROPE; IF d.stopFlag THEN { oneResult _ "Copy Aborted"; copyResult _ CONS[first: oneResult, rest: copyResult]; EXIT; }; IF transHandle = NIL THEN { inst: AlpInstance.Handle _ NIL; inst _ AlpInstance.Create[fileStore: serverForTrans, caller: caller, key: key]; transHandle _ AlpTransaction.Create[inst]; }; IF d.assertWheel THEN transHandle.AssertAlpineWheel[TRUE]; [file, fullPathName, link] _ AlpDirectory.Enumerate[ transHandle: transHandle, pattern: fullNSource, previousFile: previousFile]; IF fullPathName.IsEmpty[] THEN { message: ROPE = "No files match the source pattern - No files copied"; IF copyResult = NIL THEN copyResult _ CONS[first: message, rest: NIL]; [] _ transHandle.Finish[requestedOutcome: abort, continue: FALSE ! RPC.CallFailed => CONTINUE; ]; EXIT; }; fromFile _ CreateFileState[file: fullPathName, createOptions: oldOnly]; oneResult _ processCopy[fullPathName]; copyResult _ CONS[first: oneResult, rest: copyResult]; transHandle _ NIL; previousFile _ fullPathName; ENDLOOP; } ELSE { copyEnum: FS.NameProc = { oneResult: ROPE; continue _ TRUE; IF alpineResult AND transHandle = NIL THEN { inst: AlpInstance.Handle _ NIL; inst _ AlpInstance.Create[fileStore: serverForTrans, caller: caller, key: key]; transHandle _ AlpTransaction.Create[inst]; }; IF alpineResult AND d.assertWheel THEN transHandle.AssertAlpineWheel[TRUE]; fromFile _ CreateFileState[file: fullFName, createOptions: oldOnly]; oneResult _ processCopy[fullFName]; copyResult _ CONS[first: oneResult, rest: copyResult]; transHandle _ NIL; IF d.stopFlag THEN { oneResult _ "Copy Aborted"; copyResult _ CONS[first: oneResult, rest: copyResult]; continue _ FALSE; }; }; FS.EnumerateForNames[pattern: fullNSource, proc: copyEnum]; }; IF copyResult # NIL THEN copyResult _ List.Reverse[copyResult]; }; }; CopyFilesProc: PUBLIC Buttons.ButtonProc = { InnerCopyFilesProc[FALSE, parent, clientData, mouseButton, shift, control]; }; FullCopyFilesProc: PUBLIC Buttons.ButtonProc = { InnerCopyFilesProc[TRUE, parent, clientData, mouseButton, shift, control]; }; InnerCopyFilesProc: PROC [fullCopy: BOOL, parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift, control: BOOL _ FALSE] = { d: MyData = NARROW[clientData]; resultList: LIST OF REF ANY _ NIL; server, srcServer, srcFile, srcDir, destServer, destFile, destDir: ROPE; user, password: ROPE; directory, sRestOfPattern, dRestOfPattern: ROPE; toFile, fromFile: ROPE; parseError: BOOL; errorExplanation: ROPE; callCopy: YodelData.PerformProc = { RETURN[fileCopy[trans: trans, to: toFile, from: fromFile, fullCopy: fullCopy, user: user, password: password, caller: caller, key: key, serverForTrans: serverForTrans, d: d ]]; }; typeParseError: PROC [sOrD: ROPE] = { d.out.PutF["\nBad pattern in %g name for Copy because %g\n", IO.rope[sOrD], IO.rope[errorExplanation]]; }; d.stopFlag _ FALSE; IF d.background THEN CedarProcess.SetPriority[background]; [user, password, srcServer, srcDir, srcFile, parseError, errorExplanation] _ ParseSArgs[d]; IF parseError THEN { typeParseError["source"]; GOTO badParse;}; [destServer, destDir, destFile, parseError, errorExplanation] _ ParseDArgs[user, d]; IF parseError THEN { typeParseError["destination"]; GOTO badParse;}; [directory, sRestOfPattern] _ DecomposePattern[server: srcServer, pattern: srcFile, user: user]; IF (Rope.Equal[srcServer,""] OR Rope.Equal[srcServer,NIL]) THEN { IF srcFile.InlineLength[]>0 AND srcFile.InlineFetch[0] = '< THEN fromFile _ Rope.Concat["[]", srcFile] ELSE fromFile _ srcFile; } ELSE { fromFile _ Rope.Concat["[",Rope.Concat[srcServer,Rope.Concat["]", Rope.Concat[directory,sRestOfPattern]]]]; }; [directory, dRestOfPattern] _ DecomposePattern[server: destServer, pattern: destFile, user: user, addStar: FALSE]; IF (Rope.Equal[destServer,""] OR Rope.Equal[destServer,NIL]) THEN { IF destFile.InlineLength[]>0 AND destFile.InlineFetch[0] = '< THEN toFile _ Rope.Concat["[]", destFile] ELSE toFile _ destFile; } ELSE { toFile _ Rope.Concat["[",Rope.Concat[destServer,Rope.Concat["]", Rope.Concat[directory,dRestOfPattern]]]]; }; IF hasPattern[destFile] THEN { d.out.PutF["\nCopy cannot have a pattern in the destination - but a directory is OK\n"]; } ELSE { IF hasPattern[sRestOfPattern] AND ~isDirectory[destFile] THEN { d.out.PutF["\nCopy of a file name pattern only works to a directory\n"]; } ELSE { d.out.PutF["\nCopy of %g to %g\n", IO.rope[fromFile], IO.rope[toFile]]; IF Rope.Match[pattern: "*.alpine", object: srcServer, case: FALSE] THEN server _ srcServer ELSE server _ destServer ; resultList _ PerformOp[performProc: callCopy, server: server, user: user, password: password]; IF ~PrintResult[d.out, resultList] THEN d.out.PutF[" Copy seems OK.\n"]; }; }; EXITS badParse => {}; }; fileRename: PROC [trans: AlpTransaction.Handle, to: ROPE, from: ROPE, user: ROPE _ NIL, password: ROPE _ NIL, caller: AlpineEnvironment.Principal, key: RPC.EncryptionKey, d: MyData] RETURNS [resultList: LIST OF REF ANY _ NIL] = { resultRope: ROPE _ NIL; outcome: AlpTransaction.Outcome; IF trans # NIL THEN { oldName, newName: Rope.ROPE; IF d.assertWheel THEN trans.AssertAlpineWheel[TRUE]; [oldName, newName] _ AlpDirectory.Rename[old: from, new: to, transHandle: trans]; outcome _ AlpTransaction.Finish[trans, commit]; IF outcome # commit THEN { resultList _ CONS[ first: Rope.Concat["Alpine transaction aborted -- rename NOT done", IF resultList = NIL THEN NIL ELSE "; log before abort is:"], rest: resultList]; }; } ELSE { FS.Rename[from: from, to: to]; }; }; RenameProc: PUBLIC Buttons.ButtonProc = { d: MyData = NARROW[clientData]; resultList: LIST OF REF ANY _ NIL; result: ROPE; srcServer, srcFile, srcDir, destServer, destFile, destDir: ROPE; user, password: ROPE; directory, sRestOfPattern, dRestOfPattern: ROPE; toFile, fromFile: ROPE; parseError: BOOL; errorExplanation: ROPE; callRename: YodelData.PerformProc = { RETURN[fileRename[trans: trans, to: toFile, from: fromFile, user: user, password: password, caller: caller, key: key, d: d ]]; }; typeParseError: PROC [sOrD: ROPE] = { d.out.PutF["\nBad pattern in %g name for Rename because %g\n", IO.rope[sOrD], IO.rope[errorExplanation]]; }; d.stopFlag _ FALSE; [user, password, srcServer, srcDir, srcFile, parseError, errorExplanation] _ ParseSArgs[d]; IF parseError THEN { typeParseError["source"]; GOTO badParse;}; [destServer, destDir, destFile, parseError, errorExplanation] _ ParseDArgs[user, d]; IF parseError THEN { typeParseError["destination"]; GOTO badParse;}; IF ~Rope.Equal[destServer,srcServer, FALSE] THEN { d.out.PutF["\nFor Rename, source (%g) and destination (%g) servers must be the same\n", IO.rope[srcServer], IO.rope[destServer]]; GOTO badParse; }; [directory, sRestOfPattern] _ DecomposePattern[server: srcServer, pattern: srcFile, user: user]; IF (Rope.Equal[srcServer,""] OR Rope.Equal[srcServer,NIL]) THEN { IF srcFile.InlineLength[]>0 AND srcFile.InlineFetch[0] = '< THEN fromFile _ Rope.Concat["[]", srcFile] ELSE fromFile _ srcFile; } ELSE { fromFile _ Rope.Concat["[",Rope.Concat[srcServer,Rope.Concat["]", Rope.Concat[directory,sRestOfPattern]]]]; }; [directory, dRestOfPattern] _ DecomposePattern[server: destServer, pattern: destFile, user: user]; IF (Rope.Equal[destServer,""] OR Rope.Equal[destServer,NIL]) THEN { IF destFile.InlineLength[]>0 AND destFile.InlineFetch[0] = '< THEN toFile _ Rope.Concat["[]", destFile] ELSE toFile _ destFile; } ELSE { toFile _ Rope.Concat["[",Rope.Concat[destServer,Rope.Concat["]", Rope.Concat[directory,dRestOfPattern]]]]; }; IF hasPattern[sRestOfPattern] OR hasPattern[dRestOfPattern] THEN { d.out.PutF["\nRename only works on a single file to a single file\n"]; } ELSE { d.out.PutF["\nRename of %g to %g\n", IO.rope[fromFile], IO.rope[toFile]]; resultList _ PerformOp[performProc: callRename, server: srcServer, user: user, password: password]; IF resultList = NIL THEN { d.out.PutF[" Rename seems OK.\n"]; } ELSE { result _ NARROW[IF resultList = NIL THEN NIL ELSE resultList.first]; d.out.PutF[" %g\n", IO.rope[result]]; }; }; EXITS badParse => {}; }; ConvertDirectoryProc: PUBLIC Buttons.ButtonProc = { d: MyData = NARROW[clientData]; resultList: LIST OF REF ANY _ NIL; srcServer, srcFile, srcDir : ROPE; user, password: ROPE; directory, sRestOfPattern: ROPE; parseError: BOOL; errorExplanation: ROPE; callConvert: YodelData.PerformProc = { RETURN[Convert[trans: trans, user: user, password: password, directoryName: directory, key: key, serverForTrans: serverForTrans, d: d ]]; }; typeParseError: PROC = { d.out.PutF["\nBad pattern in source name for Convert because %g\n", IO.rope[errorExplanation]]; }; [user, password, srcServer, srcDir, srcFile, parseError, errorExplanation] _ ParseSArgs[d]; IF parseError THEN { typeParseError[]; GOTO badParse;}; [directory, sRestOfPattern] _ DecomposePattern[server: srcServer, pattern: srcFile, user: user]; IF Rope.Compare[sRestOfPattern, "*"] # equal THEN { d.out.PutF["\nCan only convert a directory"]; } ELSE { IF NOT Rope.Match[pattern: "*.alpine", object: srcServer, case: FALSE] THEN { d.out.PutF["\nCan only convert a directory on an Alpine Server"]; } ELSE { resultList _ PerformOp[performProc: callConvert, server: srcServer, user: user, password: password]; IF ~PrintResult[d.out, resultList] THEN d.out.PutF[" Convert seems OK.\n"]; }; }; EXITS badParse => {}; }; Convert: PROC [trans: AlpTransaction.Handle, user: ROPE _ NIL, password: ROPE _ NIL, directoryName: ROPE _ NIL, key: RPC.EncryptionKey, serverForTrans: ROPE, d: MyData] RETURNS [convertResult: LIST OF REF ANY _ NIL] ~ { basicpath: Rope.ROPE _ Rope.Cat["[", serverForTrans, "]", directoryName]; administratorPath: Rope.ROPE _ Rope.Cat["[", serverForTrans, "]"]; rootFileProperty: AlpineEnvironment.OwnerPropertySet; owner: ROPE; FileItem : TYPE = RECORD [ name: ROPE, file: AlpineEnvironment.UniversalFile ]; itemList : LIST OF FileItem _ NIL; InterimEnumProc: PROC [ fileName: REF TEXT, universalFile: AlpineEnvironment.UniversalFile] RETURNS [quit: BOOL _ FALSE] = { name : ROPE _ Rope.FromRefText[fileName]; itemList _ CONS[ first: [name: name, file: universalFile], rest: itemList ]; }; interimDirectoryFile, oldDirectoryFile: AlpineEnvironment.UniversalFile; writeRootFileProperty: AlpineEnvironment.OwnerPropertyValuePair.rootFile; rootFileProperty[rootFile] _ TRUE; IF d.assertWheel THEN trans.AssertAlpineWheel[TRUE] ELSE { msg: ROPE _ "You must be an Alpine Wheel to use Convert"; convertResult _ CONS[ msg, convertResult]; RETURN; }; AlpineInterimDirectory.EnumerateDirectoryUnderTrans[ transHandle: trans, directoryName: basicpath, enumProc: InterimEnumProc]; IF itemList = NIL THEN { convertResult _ CONS[ Rope.Cat[ basicpath, " -- no files to move"], convertResult]; RETURN; }; { continue : BOOL _ FALSE; [] _ AlpDirectory.Lookup[ transHandle: trans, fileName: Rope.Cat[basicpath, "$$$.btree"] ! AlpDirectory.Error => IF type = damagedDirectory THEN {continue _ TRUE; CONTINUE} ]; IF NOT continue THEN { convertResult _ CONS[ Rope.Cat[ basicpath, " -- directory already exists"], convertResult]; RETURN; }; }; owner _ Rope.Substr[directoryName, 1, Rope.Length[directoryName]-2]; interimDirectoryFile _ NARROW[trans.ReadOwnerProperties[ volumeGroupID: trans.GetNextVolumeGroup[previousGroup: AlpineEnvironment.nullVolumeGroupID, lock: [none, wait]], owner: owner, desiredProperties: rootFileProperty ].first, AlpineEnvironment.OwnerPropertyValuePair.rootFile].rootFile; writeRootFileProperty.rootFile _ AlpineEnvironment.nullUniversalFile; trans.WriteOwnerProperties[ volumeGroupID: trans.GetNextVolumeGroup[previousGroup: AlpineEnvironment.nullVolumeGroupID, lock: [none, wait]], owner: owner, properties: LIST[writeRootFileProperty] ]; FOR localList: LIST OF FileItem _ itemList, localList.rest UNTIL localList = NIL DO item: FileItem _ localList.first; [] _ AlpDirectory.Insert[ transHandle: trans, fileName: Rope.Cat[basicpath, item.name], file: item.file ]; ENDLOOP; [oldDirectoryFile, ] _ AlpDirectory.Insert[ transHandle: trans, fileName: Rope.Cat[administratorPath, owner, ".InterimDirectoryFile"], file: interimDirectoryFile ]; IF oldDirectoryFile # AlpineEnvironment.nullUniversalFile THEN { -- if there was already a file by that name something's wrong [] _ AlpTransaction.Finish[ trans, abort, FALSE ]; convertResult _ LIST[Rope.Cat[basicpath, " -- Naming problems detected, convert NOT done" ]]; RETURN; }; convertResult _ LIST[Rope.Cat[basicpath, " converted" ]]; }; END. ΆYodelUserImpl2.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Yodel: User commands Last Edited by: Jack Kent, September 4, 1986 7:30:59 pm PDT Carl Hauser, February 13, 1986 1:34:22 pm PST Hauser, April 19, 1985 4:45:25 pm PST Bob Hagmann October 11, 1985 11:34:52 am PDT excessBytes is only used if expunging = TRUE. - this is a holdover from earlier code test for alpine files open the file and contract or extend it as neccessary body of procedure fileCopy alpine files to rename FS or IFS files to rename Copy the interimDirectory to a local data structure; See if there's already a valid AlpineDirectory file. Remember the file containing the interim directory. Nullify the rootFile property so AlpDirectory will create a new file for the directory. Create the Alpine Directory file and copy info from itemList into it. Make an entry for the old directory file in the new directory Bob Hagmann June 11, 1985 9:11:52 am PDT reformatted Bob Hagmann June 12, 1985 8:10:17 am PDT added Rename, split from YodelUserImpl Bob Hagmann July 8, 1985 11:09:18 am PDT changes to: changes for copy multiple files Bob Hagmann October 10, 1985 5:26:16 pm PDT changes to: processCopy Bob Hagmann October 11, 1985 7:58:30 am PDT changes to: ParseDArgs Bob Hagmann October 11, 1985 11:07:15 am PDT changes to: processCopy Jack Kent, August 18, 1986 10:53:22 am PDT changes to: CreateFileState (local of fileCopy) Jack Kent, August 18, 1986 4:42:42 pm PDT changes to: CreateFileState (local of fileCopy), DIRECTORY Jack Kent, August 19, 1986 12:01:09 pm PDT changes to: CreateFileState (local of fileCopy) Jack Kent, September 4, 1986 7:22:57 pm PDT changes to: CreateFileState (local of fileCopy) Κ˜šœ™Icodešœ Οmœ1™<—Jšœ™šœ™K™+K™-Jšœ%™%K™,—J˜J˜šΟk ˜ Jšœžœ`˜mJšœžœά˜σJšœ žœ˜ J˜ J˜J˜Jšœ ˜ Jšœ˜Jšœ žœžœ˜Jšœžœ˜Jšœ žœ˜#JšžœžœΆ˜ΎJšžœ˜J˜J˜J˜J˜Jšžœ˜Jšœžœ ˜Jšœ žœ˜ Jšžœžœ2˜:J˜ —J˜Jšœžœž˜J˜Jš žœ[žœžœžœžœ ˜•J˜šžœ ˜J˜—J˜J˜šžœžœžœ˜,J˜—Jšžœžœžœ˜J˜Jšœ žœžœ ˜Jšœžœžœ˜$Jšœ žœžœ ˜Jšœžœžœ˜/Jšœžœžœ˜'šœžœžœ˜$J˜—šΟn œžœžœ˜NJšžœ,˜2J˜J˜—šŸ œžœžœ˜NJšžœ˜"J˜—J˜šΟb œžœžœžœ žœžœžœ˜?Jšžœ(˜.J˜—J˜J˜J˜šŸ œžœžœ žœ!žœžœžœžœžœžœ˜™Jšœžœ˜Jšœ žœ˜Jšœžœ˜Jšœ žœ˜Jšœ žœžœ˜Jšœ/˜/šœ9žœ/žœ ˜wJšœ žœ˜Jšœ%˜%Jšžœ˜ Jšœ˜—Jšœ˜šžœ žœ˜šœ9žœLžœ ˜”Jšžœ ˜Jšœ˜—J˜Jšœ žœ˜J˜—Jšœ žœ˜JšœA˜AJšžœ žœ˜ šœžœ˜Kšœ.˜.Kšžœ žœ4˜DK˜—J˜Pšž˜J˜—J˜J˜—šŸ œžœžœžœžœžœžœžœžœžœžœžœ˜sšž˜Jšœ žœžœžœžœžœžœžœ˜KJšžœžœžœžœ˜J˜Jšœžœ˜%Jšœžœ˜Jšžœ˜—J˜—J˜š   œžœ žœžœžœ˜:Jšœžœ˜ Jšžœžœžœžœ˜(Jšœ˜Jšžœ"žœ"žœžœžœžœžœžœ˜nJ˜—J˜˜J˜JšœT™T—š& œžœ$žœžœ žœžœžœ žœžœ žœžœZžœ žœ žœžœžœžœžœžœ˜Σšœžœžœ˜ šžœž˜J˜'Jšœžœ ˜Jšžœ˜ ——Jšœžœ˜5Jšœžœ˜-Jšœ žœžœ˜&Jšœžœžœ˜2Jšœ žœžœ˜*J˜š ŸœžœžœEžœ2žœ˜²Jšœ™šžœ8žœžœ˜GJ˜!Jšœžœ˜,Jšžœžœ žœžœ˜-šžœžœ˜!šœžœ˜-J˜IJ˜J˜J˜A—J˜—šœžœ˜J™5šœžœ˜-J˜IJšœ˜Jšœ˜J˜B—J•StartOfExpansione[handle: AlpFile.Handle, size: AlpineEnvironment.PageCount, lock: AlpineEnvironment.LockOption]šœW˜WJ˜—Jšžœ˜J˜—šœžœ˜Jšœ žœ ˜J˜Jšžœžœ žœ ˜8šœžœ˜Jšœ žœžœ'˜WJ˜—Jšœžœ/˜@Jšžœ˜J˜—J˜——˜J˜šŸœžœ˜1šžœ žœž˜˜%J˜—˜J˜J˜—Jšžœžœ˜—J˜—J˜—˜šŸœžœžœBžœžœžœ'žœ>žœ ˜‘J˜šžœ žœž˜˜%Jšœ žœžœ˜'J˜2J˜9šžœžœž˜J˜BJ˜šžœž˜˜Jšœ žœF˜YJšœžœ4˜RJ˜—˜Jšœ žœ ˜J˜;Jšœžœ4˜RJ˜—˜JšœžœL˜bJšœžœ:˜XJ˜—˜Jšœ-žœJ˜}Jšœžœ8˜VJ˜—˜Jšœ+žœF˜wJšœžœ4˜RJ˜—Jšžœ˜—Jšžœ˜—J˜—˜J˜JJ˜—Jšžœžœ˜—˜J˜——š ŸœžœWžœžœžœ'žœ˜»šžœ žœž˜˜%šžœ žœžœžœ=˜hšžœ,žœ˜NJ˜——J˜—˜˜BJ˜—J˜—Jšžœžœ˜—J˜—J˜šŸ œžœ$žœ˜Ašžœ žœž˜šœ$žœ˜-šœ%˜%J˜6šœ ž œ˜Jšœ&˜&——J˜—˜šžœž˜ J˜QJšžœ˜—J˜—Jšžœžœ˜—J˜——˜šŸ œžœ"žœ˜@šžœžœž˜˜%šžœžœ'˜4J˜6šœ ž œ˜Jšœ'žœ˜+——J˜—˜šžœž˜ J˜RJšžœ˜—J˜—Jšžœžœ˜—J˜—J˜š   œžœžœ žœžœ˜7J˜šžœž˜ Jšœ žœ˜,Jšœ žœžœ˜3šžœ žœ˜šžœžœž˜˜%Jšœ;žœ˜Cšžœžœž˜JšœAžœ˜I—J˜—˜Jšœžœ˜ Jšœžœ˜J˜4J˜4J˜Jšžœ1˜3J˜—Jšžœžœ˜—Jšžœžœ˜ J˜—J˜"J˜!J˜(Jšžœ˜—Jšœ˜J˜—š   œžœžœžœ žœžœ˜EJšœžœ ˜Jšœžœžœ˜Jšœžœ˜Jšœ žœ˜!Jšœ žœžœ˜Jšœžœ˜Jš žœžœžœžœžœ˜Pšœ.žœ,žœ ˜iJšœ žœ˜Jšœ%˜%Jšžœ˜ Jšœ˜—Jšœ˜šžœ žœ˜Jšœ žœ:žœ žœ˜mJšžœ˜J˜—šžœžœ˜Jšœq˜qJšžœžœo˜J˜—J˜J˜dJ˜Jš žœžœžœ žœžœ8˜J–>[s1: ROPE, s2: ROPE, pos1: INT _ 0, case: BOOL _ TRUE]šœržœ˜yJ™šžœžœž˜KšœžœžœF˜~šœ žœžœ˜AKšžœžœ(˜>Kšœ˜K˜—Kšžœ˜%J˜—šžœžœžœ˜0Jšœ žœ˜šžœžœ2žœžœ˜[Jšœžœ˜J˜OJ˜6Jšžœžœ%žœ˜@šœ]˜]J˜—J˜—Jšœžœ_˜dJ˜—šœžœ˜Jšœ žœ˜˜OJ˜—J˜—J˜(J˜šžœ˜ Jšœžœ˜/Jšœžœ˜Jšœ žœ+˜9Jšœ˜—Jšžœžœžœ˜.J˜DJ˜J˜šžœžœ˜šžœ?žœ ˜QJšžœžœY˜d—Jšœ˜—šžœ˜ Jšžœ˜Jšœžœ˜Jšœ˜—šžœ žœ˜Jšœžœ;˜BJšžœ*˜,J˜—šžœžœž˜šœ8žœ ˜HJšžœžœT˜_——Jšœ žœžœ˜7J˜——J˜˜J˜,Jšœ!žœ˜%Jšœ+žœ˜/J˜J˜(Jšœžœ˜Jšœ žœ˜J˜Jšœžœ žœ˜$Jšœžœžœ˜Jšœ žœžœ˜Jšœ žœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœ žœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœžœ˜!Jšœžœžœ'žœ˜DJ˜/J˜*Jšœ žœ˜Jšœžœ˜ J˜Jšœžœžœ˜˜JšΟl™—˜šžœžœ˜šžœžœžœ.˜GJšœžœžœ˜—šžœžœžœ4˜SJšœžœžœ˜—Jšžœžœžœžœ˜:Jšœžœ˜Jš žœ žœžœžœ žœ˜IJš žœ žœžœžœ žœ˜FJ˜—J˜JšœD˜DJšœ#žœ#˜HJšœI˜IJšœIžœ˜QJ˜šœ!žœžœ ˜GJšœžœ˜Jšžœ˜ Jšœ˜—Jšœ˜šžœžœ˜Jšœ!žœ"˜EJšœ6˜6J˜—JšœE˜EJšœGžœ˜OJ˜Jšœ%žœ˜7J˜Jšœ˜šžœžœ˜Jšœžœžœ˜šž˜Jšœžœ˜Jšœžœ˜Jšœ žœ˜šžœ žœ˜Jšœ˜Jšœ žœ%˜6Jšžœ˜J˜—šžœžœžœ˜Jšœžœ˜JšœO˜OJšœ*˜*J˜—Jšžœžœžœ˜:Jšœ˜šžœžœ˜ Jšœ žœ9˜FJš žœžœžœžœžœ˜Fšœ;žœ˜CJšžœžœ˜Jšœ˜—Jšžœ˜J˜—JšœG˜GJšœ&˜&Jšœ žœ%˜6Jšœžœ˜Jšœ˜Jšžœ˜—J˜—šœžœ˜šœ žœ ˜Jšœ žœ˜Jšœ žœ˜šžœžœžœžœ˜,Jšœžœ˜JšœO˜OJšœ*˜*J˜—Jšžœžœžœžœ˜KJšœD˜DJšœ#˜#Jšœ žœ%˜6Jšœžœ˜šœžœ˜Jšœ˜Jšœ žœ%˜6Jšœžœ˜J˜—J˜—Jšžœ9˜;J˜—Jšžœžœžœ'˜?J˜—J˜—J˜J˜š  œžœ˜,Jšœžœ3˜KJ˜J˜—š œžœ˜0Jšœžœ3˜JJ˜—J˜šŸœžœ žœ žœžœžœžœžœ8žœžœ˜Jšœ žœ ˜Jš œ žœžœžœžœžœ˜"JšœCžœ˜HJšœžœ˜Jšœ+žœ˜0Jšœžœ˜Jšœ žœ˜Jšœžœ˜J˜š œ˜#šžœ3˜9Jšœv˜v—J˜J˜—š œžœžœ˜%Jšœ=žœ žœ˜gJ˜—Jšœ žœ˜Jšžœžœ&˜:J˜Jšœ[˜[Jšžœ žœžœ ˜?JšœT˜TJšžœ žœ"žœ ˜DJ˜`šžœžœžœžœ˜AJšžœžœ˜