<> <> <> <> <> <> <> <> 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] = { << test for alpine files>> 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 { << alpine files to rename>> 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 or IFS files to rename>> 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. <<>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<>> <> <> <> <> <<>>