<> <> <> <> DIRECTORY AlpFile USING[ FileID, GetSize, Handle, LockOption, Open, ReadProperties, ReadPages, SetSize, WritePages, WriteProperties], AlpineEnvironment USING[ AccessList, ByteCount, bytesPerPage, NeededAccess, Outcome, OwnerName, PageCount, Principal, Property, PropertyValuePair, RName, UniversalFile, wordsPerPage], AlpineFile USING [ allProperties, PropertySet], AlpineInterimDirectory USING[ CreateOptions, DeleteUnderTrans, EnumerateDirectoryUnderTrans, OpenUnderTrans], AlpInstance USING[ AccessFailed, Create, Handle, LockFailed ], AlpTransaction USING[ AssertAlpineWheel, Create, Finish, Handle, Outcome], BasicTime USING[ GMT], Basics USING[ Comparison], Buttons USING[ Button, ButtonProc, SetDisplayStyle ], FS USING[ Close, ComponentPositions, Copy, Create, Delete, EnumerateForInfo, Error, ExpandName, GetInfo, GetName, InfoProc, Open, OpenFile, Read, SetByteCountAndCreatedTime, Write], IO USING[ int, PutF, PutFR, PutRope, rope, STREAM, text, time ], List USING[ Sort, Car, Cdr], Menus USING[ MouseButton], RefText USING[ TrustTextAsRope], Rope, RPC, UserCredentials USING[ Get ], ViewerClasses USING[ Viewer ], ViewerTools USING[ GetContents], VM USING[ AddressForPageNumber, Allocate, Free, Interval], YodelData; YodelUser: CEDAR PROGRAM IMPORTS AlpineInterimDirectory, AlpFile, AlpInstance, AlpTransaction, Buttons, FS, IO, List, RefText, Rope, RPC, UserCredentials, ViewerTools, VM, YodelData EXPORTS YodelData = BEGIN OPEN AE: AlpineEnvironment, YodelData; 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]; }; ROPE: TYPE = Rope.ROPE; hasPattern: PUBLIC SAFE PROC [pattern: ROPE] RETURNS [BOOL] = { RETURN [-1 # Rope.Find[s1: pattern, s2: "*"]]; }; CompareProc: SAFE PROC [ref1, ref2: REF ANY] RETURNS [Basics.Comparison] = CHECKED { RETURN [Rope.Compare[NARROW[ref1], NARROW[ref2], FALSE]]; }; makeSlashAWedge: Rope.TranslatorType = { IF old = '/ THEN RETURN['>] ELSE RETURN[old]; }; ParseSArgs: PUBLIC PROC [ d: MyData] RETURNS [user, password, srcServer, srcDir, srcFile: ROPE] = { srcFileTemp: ROPE ; srcServer _ ViewerTools.GetContents[d.srcServer]; -- srcFile _ ViewerTools.GetContents[d.srcFile]; srcFileTemp _ ViewerTools.GetContents[d.srcFile] ; IF (NOT srcFileTemp.IsEmpty[]) AND srcFileTemp.Fetch[0] = '/ THEN srcFileTemp _ Rope.Concat["<" , srcFileTemp.Substr[start: 1, len: srcFileTemp.InlineLength[]-1]]; srcFile _ Rope.Translate[base: srcFileTemp, translator: makeSlashAWedge ]; user _ ViewerTools.GetContents[d.user]; IF (Rope.Equal[user,""] OR Rope.Equal[user,NIL]) THEN [user] _ UserCredentials.Get[]; password _ ViewerTools.GetContents[d.password]; [srcDir] _ DecomposePattern[server: srcServer, pattern: srcFile, user: user]; }; ParseDArgs: PROC [user: ROPE, d: MyData] RETURNS [destServer, destDir, destFile: ROPE] = { destFileTemp: ROPE ; destServer _ ViewerTools.GetContents[d.destServer]; destFileTemp _ ViewerTools.GetContents[d.destFile]; IF (NOT destFileTemp.IsEmpty[]) AND destFileTemp.Fetch[0] = '/ THEN destFileTemp _ Rope.Concat["<" , destFileTemp.Substr[start: 1, len: destFileTemp.InlineLength[]-1]]; destFile _ Rope.Translate[base: destFileTemp, translator: makeSlashAWedge ]; [destDir] _ DecomposePattern[server: destServer, pattern: destFile, user: user]; }; listFiles: PROC [trans: AlpTransaction.Handle, server: ROPE, pattern: ROPE, user: ROPE _ NIL, password: ROPE _ NIL , displayProperties: AlpineFile.PropertySet _ AlpineFile.allProperties, d: MyData] RETURNS [LIST OF REF ANY] = { directory, restOfPattern: ROPE; resultList: LIST OF REF ANY _ NIL; AlpineEnumProc: PROC [fileName: REF TEXT, universalFile: AlpineEnvironment.UniversalFile] RETURNS [quit: BOOL] = { intendReadLockOption: AlpFile.LockOption = [intendRead , IF d.breakLocks THEN wait ELSE fail]; readLockOption: AlpFile.LockOption = [read, IF d.breakLocks THEN wait ELSE fail]; IF d.stopFlag THEN { resultList _ CONS[first: NARROW[" ** List aborted **", ROPE], rest: NIL]; RETURN [quit: TRUE]; }; IF Rope.Match[pattern: restOfPattern, object: RefText.TrustTextAsRope[fileName], case: FALSE] THEN { file: AlpFile.Handle; fileID: AlpFile.FileID; properties: LIST OF AlpineEnvironment.PropertyValuePair; byteLength: INT; resultRope: ROPE ; [file, fileID] _ AlpFile.Open[transHandle: trans, universalFile: universalFile, lock: intendReadLockOption ! AlpInstance.LockFailed => GOTO lockError; AlpInstance.AccessFailed => IF missingAccess = AlpineEnvironment.NeededAccess.fileRead THEN GOTO skipFile ELSE REJECT; ]; properties _ AlpFile.ReadProperties[handle: file, lock: readLockOption ! AlpInstance.LockFailed => GOTO lockError; AlpInstance.AccessFailed => IF missingAccess = AlpineEnvironment.NeededAccess.fileRead THEN GOTO skipFile ELSE REJECT; ]; byteLength _ NARROW[properties.first, AlpineEnvironment.PropertyValuePair.byteLength].byteLength; resultRope _ IO.PutFR["%g\t", IO.text[fileName]]; <> UNTIL properties = NIL DO property: AlpineEnvironment.PropertyValuePair _ properties.first ; properties _ properties.rest; IF displayProperties[property.property] THEN BEGIN SELECT property.property FROM <> byteLength => { byteLength: INT _ NARROW[property, AlpineEnvironment.PropertyValuePair.byteLength].byteLength; resultRope _ Rope.Concat[resultRope, IO.PutFR["%g bytes ", IO.int[byteLength]]]; }; <> createTime => { createTime: BasicTime.GMT _ NARROW[property, AlpineEnvironment.PropertyValuePair.createTime].createTime; resultRope _ Rope.Concat[resultRope, IO.PutFR["%g ", IO.time[createTime]]]; }; <> highWaterMark => { highWaterMark: AlpineEnvironment.PageCount _ NARROW[property, AlpineEnvironment.PropertyValuePair.highWaterMark].highWaterMark; resultRope _ Rope.Concat[resultRope, IO.PutFR["HWM: %g ", IO.int[highWaterMark]]]; }; <> modifyAccess => { modifyAccess: AlpineEnvironment.AccessList _ NARROW[property, AlpineEnvironment.PropertyValuePair.modifyAccess].modifyAccess; resultRope _ Rope.Concat[resultRope,"modify access: ("]; IF modifyAccess = NIL THEN resultRope _ Rope.Concat[resultRope,"*none*"] ELSE BEGIN UNTIL modifyAccess = NIL DO accessItem: AlpineEnvironment.RName _ modifyAccess.first; resultRope _ Rope.Concat[resultRope,accessItem]; modifyAccess _ modifyAccess.rest; IF modifyAccess # NIL THEN resultRope _ Rope.Concat[resultRope,", "]; ENDLOOP; END ; resultRope _ Rope.Concat[resultRope,") "]; }; <> owner => { owner: AlpineEnvironment.OwnerName _ NARROW[property, AlpineEnvironment.PropertyValuePair.owner].owner; resultRope _ Rope.Concat[resultRope, IO.PutFR["owner: %g ", IO.rope[owner]]]; }; <> readAccess => { readAccess: AlpineEnvironment.AccessList _ NARROW[property, AlpineEnvironment.PropertyValuePair.readAccess].readAccess; resultRope _ Rope.Concat[resultRope,"read access: ("]; IF readAccess = NIL THEN resultRope _ Rope.Concat[resultRope,"*none*"] ELSE BEGIN UNTIL readAccess = NIL DO accessItem: AlpineEnvironment.RName _ readAccess.first; resultRope _ Rope.Concat[resultRope,accessItem]; readAccess _ readAccess.rest; IF readAccess # NIL THEN resultRope _ Rope.Concat[resultRope,", "]; ENDLOOP; END ; resultRope _ Rope.Concat[resultRope,") "]; }; <> stringName => { stringName: ROPE _ NARROW[property, AlpineEnvironment.PropertyValuePair.stringName].stringName; resultRope _ Rope.Concat[resultRope, IO.PutFR["stringName: %g ", IO.rope[stringName]]]; }; <> version => { version: LONG INTEGER _ NARROW[property, AlpineEnvironment.PropertyValuePair.version].version; resultRope _ Rope.Concat[resultRope, IO.PutFR["version: %g ", IO.int[version]]]; }; ENDCASE; END; ENDLOOP; resultList _ CONS[first: resultRope, rest: resultList]; EXITS lockError => { resultRope _ IO.PutFR["%g has lock set", IO.text[fileName]]; resultList _ CONS[first: resultRope, rest: resultList]; }; skipFile => {}; }; RETURN [quit: FALSE]; }; FSEnumProc: FS.InfoProc = { -- PROC [fullFName, attachedTo: ROPE, created: BasicTime.GMT, bytes: INT, -- keep: CARDINAL] RETURNS [continue: BOOLEAN]; resultRope: ROPE ; fName: ROPE ; componentPositions: FS.ComponentPositions ; IF d.stopFlag THEN { resultList _ CONS[first: NARROW[" ** List aborted **", ROPE], rest: NIL]; RETURN [continue: FALSE]; }; [fullFName: fName, cp: componentPositions] _ FS.ExpandName[name: fullFName]; resultRope _ IO.PutFR["%g\t", IO.rope[fName]]; resultRope _ Rope.Concat[resultRope, IO.PutFR["%g bytes ", IO.int[bytes]]]; resultRope _ Rope.Concat[resultRope, IO.PutFR["\t%g ", IO.time[created]]]; resultList _ CONS[first: resultRope, rest: resultList]; RETURN [continue: TRUE]; }; resultRope: ROPE _ NIL; outcome: AlpTransaction.Outcome; failureName: ROPE _ "" ; { [directory, restOfPattern] _ DecomposePattern[server, pattern, user]; IF trans # NIL THEN { <> bangPos: INT ; IF d.assertWheel THEN trans.AssertAlpineWheel[TRUE]; IF (bangPos _ Rope.Find[restOfPattern,"!"]) >= 0 THEN { IF bangPos = 0 THEN restOfPattern _ "*" ELSE restOfPattern _ Rope.Substr[base: restOfPattern, len: bangPos]; }; AlpineInterimDirectory.EnumerateDirectoryUnderTrans[ transHandle: trans, directoryName: Rope.Concat["[",Rope.Concat[server,Rope.Concat["]",directory]]], enumProc: AlpineEnumProc]; outcome _ AlpTransaction.Finish[trans, commit]; } ELSE { <> pattern: ROPE; pattern _ Rope.Concat[Rope.Concat[Rope.Concat[Rope.Concat ["[", server], "]"], directory], restOfPattern]; FS.EnumerateForInfo[pattern: pattern, proc: FSEnumProc ]; }; }; resultList _ List.Sort[resultList, CompareProc]; RETURN [resultList]; }; DecomposePattern: PUBLIC PROC [server: ROPE, pattern: ROPE, user: ROPE] RETURNS [directory, restOfPattern: ROPE] = { rightAngleBracket: INT; dir: ROPE ; isAlpine: BOOL ; IF Rope.Match[pattern: "*.alpine", object: server, case: FALSE] THEN { isAlpine _ TRUE; dir _ user; } ELSE { isAlpine _ FALSE; IF server.IsEmpty[] THEN dir _ NIL ELSE { IF Rope.Match[pattern: "*.*", object: user, case: FALSE] THEN dir _ user.Substr[start: 0, len: user.SkipTo[1, "."]] ELSE dir _ user; }; }; IF NOT Rope.Match[pattern: "<*>*", object: pattern, case: FALSE] THEN BEGIN directory _ Rope.Concat["<", dir] ; directory _ Rope.Concat[directory,">"]; restOfPattern _ pattern ; IF restOfPattern.IsEmpty[] THEN restOfPattern _ "*"; END ELSE BEGIN rightAngleBracket _ pattern.SkipTo[1, ">"]; IF rightAngleBracket > 1 THEN directory _ pattern.Substr[start: 0, len: rightAngleBracket+1] ELSE directory _ Rope.Concat["<>", dir] ; restOfPattern _ pattern.Substr[start: rightAngleBracket+1]; IF restOfPattern.IsEmpty[] THEN restOfPattern _ "*"; END }; ListFilesProc: PUBLIC Buttons.ButtonProc= BEGIN resultList: LIST OF REF ANY _ NIL; d: MyData = NARROW[clientData]; server, user, file, password: ROPE; directory, restOfPattern: ROPE; printedSomething: BOOL _ FALSE; callList: YodelData.PerformProc = { RETURN[listFiles[trans, server, file, user, password, d.displayProperties, d]]; }; d.stopFlag _ FALSE; [user, password, server, directory, file] _ ParseSArgs[d]; [directory, restOfPattern] _ DecomposePattern[server: server, pattern: file, user: user]; d.out.PutF["\nList of [%g]%g%g\n", IO.rope[server], IO.rope[directory], IO.rope[restOfPattern]]; resultList _ PerformOp[performProc: callList, server: server, user: user, password: password]; DO nowRope: ROPE _ NARROW[List.Car[resultList]]; IF resultList = NIL THEN EXIT; resultList _ List.Cdr[resultList]; d.out.PutF[" %g\n", IO.rope[nowRope]]; printedSomething _ TRUE ; ENDLOOP; IF NOT printedSomething THEN d.out.PutRope[" ** no files match the pattern **\n"]; END; fileDelete: PROC [trans: AlpTransaction.Handle, server: ROPE, file: ROPE, user: ROPE _ NIL, password: ROPE _ NIL, d: MyData] RETURNS[resultList:LIST OF REF ANY _ NIL] = { directory, restOfPattern, directoryName: ROPE; AlpineDeleteEnumProc: PROC [fileName: REF TEXT, universalFile: AlpineEnvironment.UniversalFile] RETURNS [quit: BOOL] = { IF d.stopFlag THEN { resultList _ CONS[first: NARROW[" ** Delete aborted **", ROPE], rest: NIL]; RETURN [quit: TRUE]; }; IF Rope.Match[pattern: restOfPattern, object: RefText.TrustTextAsRope[fileName], case: FALSE] THEN { fileN: ROPE; fileN _ Rope.Concat[directoryName,Rope.FromRefText[fileName]]; AlpineInterimDirectory.DeleteUnderTrans[transHandle: trans, fileName: fileN]; resultRope _ IO.PutFR["%g deleted", IO.text[fileName]]; resultList _ CONS[first: resultRope, rest: resultList]; }; RETURN [quit: FALSE]; }; FSDeleteEnumProc: FS.InfoProc = { -- PROC [fullFName, attachedTo: ROPE, created: BasicTime.GMT, bytes: INT, -- keep: CARDINAL] RETURNS [continue: BOOLEAN]; IF d.stopFlag THEN { resultList _ CONS[first: NARROW[">> ** Delete aborted **", ROPE], rest: NIL]; RETURN [continue: FALSE]; }; FS.Delete[ name: fullFName, wantedCreatedTime: created]; resultRope _ IO.PutFR["%g deleted", IO.rope[fullFName]]; resultList _ CONS[first: resultRope, rest: resultList]; RETURN [continue: TRUE]; }; resultRope: ROPE _ NIL; outcome: AlpTransaction.Outcome; failureName: ROPE _ "" ; { [directory, restOfPattern] _ DecomposePattern[server, file, user]; IF trans # NIL THEN { -- alpine files to delete bangPos: INT ; IF d.assertWheel THEN trans.AssertAlpineWheel[TRUE]; IF (bangPos _ Rope.Find[restOfPattern,"!"]) >= 0 THEN { IF bangPos = 0 THEN restOfPattern _ "*" ELSE restOfPattern _ Rope.Substr[base: restOfPattern, len: bangPos]; }; directoryName _ Rope.Concat["[",Rope.Concat[server,Rope.Concat["]",directory]]]; AlpineInterimDirectory.EnumerateDirectoryUnderTrans[ transHandle: trans, directoryName: directoryName, enumProc: AlpineDeleteEnumProc]; outcome _ AlpTransaction.Finish[trans, commit]; IF outcome # commit THEN { resultList _ CONS[ first: Rope.Concat["Alpine transaction aborted -- deletes NOT done", IF resultList = NIL THEN NIL ELSE "; log before abort is:"], rest: resultList]; }; } ELSE { -- FS or IFS files to delete pattern: ROPE; pattern _ Rope.Concat[Rope.Concat[Rope.Concat[Rope.Concat ["[", server], "]"], directory], restOfPattern]; IF pattern.Find["!"] < 0 THEN pattern _ Rope.Concat[pattern, "!l"]; FS.EnumerateForInfo[pattern: pattern, proc: FSDeleteEnumProc ]; }; }; resultList _ List.Sort[resultList, CompareProc]; RETURN [resultList]; }; DeleteFilesProc: PUBLIC Buttons.ButtonProc= BEGIN d: MyData = NARROW[clientData]; resultList: LIST OF REF ANY _ NIL; srcServer, srcFile, srcDir: ROPE; user, password: ROPE; directory, restOfPattern: ROPE; printedSomething: BOOL _ FALSE; callDelete: YodelData.PerformProc = { RETURN[fileDelete[trans: trans, server: srcServer, file: srcFile, user: user, password: password, d: d]]; }; d.stopFlag _ FALSE; [user, password, srcServer, srcDir, srcFile] _ ParseSArgs[d]; [directory, restOfPattern] _ DecomposePattern[server: srcServer, pattern: srcFile, user: user]; IF Rope.IsEmpty[srcFile] THEN { d.out.PutF["\nDelete of [%g]%g%g NOT done -- MUST have explicit pattern\n", IO.rope[srcServer], IO.rope[srcDir], IO.rope[restOfPattern]]; RETURN; }; d.out.PutF["\nDelete of [%g]%g%g\n", IO.rope[srcServer], IO.rope[srcDir], IO.rope[restOfPattern]]; resultList _ PerformOp[performProc: callDelete, server: srcServer, user: user, password: password]; DO nowRope: ROPE _ NARROW[List.Car[resultList]]; IF resultList = NIL THEN EXIT; resultList _ List.Cdr[resultList]; d.out.PutF[" %g\n", IO.rope[nowRope]]; printedSomething _ TRUE ; ENDLOOP; IF NOT printedSomething THEN d.out.PutRope[" ** no files match the pattern **\n"]; END; -- excessBytes is only used if expunging = TRUE. 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, 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 { universalFile: UniversalFile; createdFile: BOOLEAN; alpineFileState: AlpineFileState; rightSquareBracket: INT _ file.Find["]", 1]; IF rightSquareBracket IN [-1..1] THEN BEGIN IF expunging THEN ERROR ELSE ERROR ; END; [universalFile, createdFile] _ AlpineInterimDirectory.OpenUnderTrans[ trans, file, createOptions, createLength]; alpineFileState _ NEW[AlpineFileStateObject _ [alpine[fileHandle: AlpFile.Open[trans, universalFile, IF createOptions = oldOnly THEN readOnly ELSE readWrite, [IF createOptions = oldOnly THEN read ELSE write, wait], log, sequential].handle]]]; IF createOptions # oldOnly -- this is the "to" file. THEN BEGIN IF ((expunging) AND (NOT createdFile)) THEN BEGIN alpineFileState.fileHandle.WriteProperties[LIST[[highWaterMark[0]]]]; IF (trans.Finish[requestedOutcome: commit, continue: TRUE] # commit) THEN ERROR; END; alpineFileState.fileHandle.SetSize[PagesForBytes[createLength + (IF expunging THEN excessBytes ELSE 0)]]; END; 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 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 BEGIN alpineFileState.fileHandle.ReadPages[ pageRun: [firstPage: pagesCopied, count: pagesToMove], pageBuffer: DESCRIPTOR [ bufferPtr, pagesToMove*wordsPerPage]]; END; }; 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; }; 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 ; fullN: 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; { 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 transHandle # NIL AND d.assertWheel THEN transHandle.AssertAlpineWheel[TRUE]; fromFile _ CreateFileState[file: from, createOptions: oldOnly]; [byteLength, createTime, alpineProperties, highWaterMark, alpineSize] _ GetFileProperties[fromFile]; [fullFName: fullN, cp: cp] _ FS.ExpandName[name: to]; toServer _ Rope.Substr[fullN,cp.server.start, cp.server.length]; alpineResult _ Rope.Match[pattern: "*.alpine", object: toServer, case: FALSE] ; [fullFName: fullN, cp: cp] _ FS.ExpandName[name: from]; fromServer _ Rope.Substr[fullN,cp.server.start, cp.server.length]; alpineSource _ Rope.Match[pattern: "*.alpine", object: fromServer, case: FALSE] ; alpineSourceAndResult _ alpineResult AND alpineSource ; IF byteLength < 0 AND (~alpineSourceAndResult OR ~fullCopy) THEN RETURN[CONS[ NARROW["The source file has negative length. Copy aborted.",ROPE] ,NIL]]; IF alpineSourceAndResult AND fullCopy THEN { createLength _ MAX[byteLength, BytesForPages[highWaterMark], alpineSize]; } ELSE 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: to, createOptions: none, createLength: createLength, trans: secondTransHandle]; } ELSE toFile _ CreateFileState[file: to, createOptions: none, createLength: createLength]; } ELSE { IFSResult _ TRUE; toFile _ CreateFileState[file: "///temp/qqqscratch.yodel", createOptions: none, createLength: createLength]; }; -- toFile _ CreateFileState[file: to, createOptions: none, createLength: byteLength]; pageCount _ PagesForBytes[createLength]; TRUSTED BEGIN bufferInterval _ VM.Allocate[count: bufferLen]; bufferAllocated _ TRUE ; bufferPtr _ VM.AddressForPageNumber[bufferInterval.page]; END; IF moveTheBits[] THEN RETURN[CONS[ NARROW["Copy aborted ",ROPE] ,NIL]]; SetFileProperties[toFile, byteLength, createTime, alpineProperties]; CleanupFileState[fromFile]; CleanupFileState[toFile]; IF twoDifferentAlpine THEN { IF (secondTransHandle.Finish[requestedOutcome: commit, continue: FALSE] # commit) THEN RETURN[CONS[ NARROW[ "Transaction on destination server failed to commit - validity of copy is in question", ROPE] ,NIL]]; }; TRUSTED BEGIN VM.Free[bufferInterval]; bufferAllocated _ FALSE ; END; IF IFSResult THEN { FS.Copy[from: "///temp/qqqscratch.yodel", to: to ]; FS.Delete[name: "///temp/qqqscratch.yodel"]; }; IF transHandle # NIL AND (transHandle.Finish[requestedOutcome: commit, continue: FALSE] # commit) THEN RETURN[CONS[ NARROW["Transaction on source server failed to commit - validity of copy is in question",ROPE] ,NIL]]; }; }; 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]= BEGIN d: MyData = NARROW[clientData]; resultList: LIST OF REF ANY _ NIL; result: ROPE; server, srcServer, srcFile, srcDir, destServer, destFile, destDir: ROPE; user, password: ROPE; directory, sRestOfPattern, dRestOfPattern: ROPE; toFile, fromFile: ROPE; callCopy: YodelData.PerformProc = { RETURN[fileCopy[trans: trans, to: toFile, from: fromFile, fullCopy: fullCopy, user: user, password: password, caller: caller, key: key, d: d ]]; }; d.stopFlag _ FALSE; [user, password, srcServer, srcDir, srcFile] _ ParseSArgs[d]; [destServer, destDir, destFile] _ ParseDArgs[user, d]; [directory, sRestOfPattern] _ DecomposePattern[server: srcServer, pattern: srcFile, user: user]; IF (Rope.Equal[srcServer,""] OR Rope.Equal[srcServer,NIL]) THEN BEGIN IF srcFile.InlineLength[]>0 AND srcFile.InlineFetch[0] = '< THEN fromFile _ Rope.Concat["[]", srcFile] ELSE fromFile _ srcFile; END ELSE BEGIN fromFile _ Rope.Concat["[",Rope.Concat[srcServer,Rope.Concat["]", Rope.Concat[directory,sRestOfPattern]]]]; END; [directory, dRestOfPattern] _ DecomposePattern[server: destServer, pattern: destFile, user: user]; IF (Rope.Equal[destServer,""] OR Rope.Equal[destServer,NIL]) THEN BEGIN IF destFile.InlineLength[]>0 AND destFile.InlineFetch[0] = '< THEN toFile _ Rope.Concat["[]", destFile] ELSE toFile _ destFile; END ELSE BEGIN toFile _ Rope.Concat["[",Rope.Concat[destServer,Rope.Concat["]", Rope.Concat[directory,dRestOfPattern]]]]; END; IF hasPattern[sRestOfPattern] OR hasPattern[dRestOfPattern] THEN BEGIN d.out.PutF["\nCopy only works on a single file to a single file\n"]; END ELSE BEGIN 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 resultList = NIL THEN BEGIN d.out.PutF[" Copy seems OK.\n"]; END ELSE BEGIN result _ NARROW[resultList.first]; d.out.PutF[" %g\n", IO.rope[result]]; END; END; END; OptionsProc: PUBLIC Buttons.ButtonProc= BEGIN d: MyData = NARROW[clientData]; p: ViewerClasses.Viewer = NARROW[parent]; d.displayOptions _ NOT d.displayOptions ; CreateButtons[d, p.parent.parent]; END; ChangeOptionsProc: PUBLIC Buttons.ButtonProc = BEGIN d: MyData = NARROW[clientData]; p: ViewerClasses.Viewer = NARROW[parent]; count: INT ; option: AlpineEnvironment.Property; <> <> FOR count IN [0..NumberOfAlpineProperties) DO propertyName: ROPE ; [option, propertyName] _ PropertySetToRopeArray[count]; IF Rope.Equal[propertyName, p.name] THEN BEGIN d.displayProperties[option] _ NOT d.displayProperties[option] ; IF d.displayProperties[option] THEN Buttons.SetDisplayStyle[p, $WhiteOnBlack] ELSE Buttons.SetDisplayStyle[p, $BlackOnWhite]; EXIT; END; ENDLOOP; END; END.