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 ], 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[IF resultList = NIL THEN NIL ELSE resultList.first]; IF resultList = NIL THEN EXIT; resultList _ resultList.rest; 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[IF resultList = NIL THEN NIL ELSE resultList.first]; IF resultList = NIL THEN EXIT; resultList _ resultList.rest; 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 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 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[IF resultList = NIL THEN NIL ELSE 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. <Yodel: User commands YodelUser.mesa Last Edited by: Hagmann, January 23, 1985 5:22:33 pm PST Bob Hagmann June 22, 1985 1:07:13 pm PDT Hauser, March 19, 1985 4:25:25 pm PST Rhagmann, January 24, 1985 9:39:30 am PST loop through properties and print those enabled byteLength createTime highWaterMark modifyAccess owner readAccess stringName version alpine files to list FS or IFS files to list loop through all the properties and find the one selected comparison is by Rope equivalent to option! Bob Hagmann June 22, 1985 1:07:13 pm PDT changes to: SetFileProperties Êä˜Jšœ™Jšœ™šœ8™8Icode™(K™%J™)—Jšœ˜JšÏk ˜ Jšœ œG˜VJšœ,˜,Jšœ˜Jšœ œ'˜7Jšœ0˜0JšœH˜HJšœ+˜+Jšœ œ˜0Jšœ˜Jšœœ#˜-Jšœ4˜4Jšœ œ-˜?Jšœœ6˜JJšœ œœ˜Jšœ œ˜Jšœ œ(˜7Jšœœ2˜>JšœF˜FJšœ?˜?Jšœœ$œ˜EJšœœ ˜Jšœœ˜Jšœ œ˜"Jšœ˜Jšœ˜Jšœœ˜Jšœœ ˜Jšœ œ˜!Jšœœ2˜=Jšœ ˜ J˜JšÏb œœ˜J˜Jš œHœœœ œ ˜œJ˜Jšœ ˜Jšœ˜Jšœ˜J˜Jšœœœ˜,Jšœ˜Jšœ œœ ˜Jšœœœ˜$Jšœ œœ ˜Jšœœœ˜/Jšœœœ˜'Jšœœœ˜$J˜J˜šÏn œœœ˜NJšœ,˜2Jšœ˜—J˜šŸ œœœ˜NJšœ˜"Jšœ˜—J˜J˜Jšœ˜Jšœœœ˜J˜šž œœœœ œœœ˜?Jšœ(˜.Jšœ˜—JšUœ Ÿ œœœœœœœ œœœœžœœ œœœœŸ œœœœ.œœAÏc2œ?œœœœúœœœœ¶Ÿ œœœœ!œœ{œœœœ¬˜Ò š&ž œœ(œ œœœ œœiœœœœœ$œœœœœœ˜Ðš Ÿœœ œœ2œœ˜rJšœ9œœœ˜^Jšœ,œœœ˜Qšœ œ˜Jš œ œœœ œ˜JJšœœ˜Jšœ˜—JšœUœœ˜dJšœ˜Jšœ˜Jšœ œœ%˜8Jšœ œ˜Jšœ œ˜Jš œ‡œ(œ:œœ œœ˜Jšœ˜J˜JšœF˜FJšœ%œ ˜4Jšœ&˜&Jšœ œ9œœ ˜ZJšœ œœ˜Jšœ ˜ Jšœœ˜-JšœE˜EJšœœœ˜9Jšœ/™/Jšœœœ˜!JšœL˜LJšœ'˜'Jšœ œ&œ˜œ3œœFœœFœœ(œ8œ œ˜è—J˜Jšœ˜Jšœœœ˜Jšœ$˜$Jšœœ˜šœ ˜ JšœE˜Ešœ œœ˜Jšœ™Jšœ œ˜Jšœœœ˜4šœ/œ˜7Jšœ œœ@˜lJšœ˜—Jšœ³˜³Jšœ/˜/Jšœ˜—Jšœœ˜šœ™Jšœ œ˜Jšœj˜jJšœ7˜9Jšœ˜—Jšœ˜J˜Jšœ0˜0Jšœ˜Jšœ˜—Jš œŸœœœ œ œœ˜HJš—œœœœ œœœ7œœœœœœœœœ œ0œœFœ"œœ4œœœƒœœœœœ=œ œIœnœœœž œœœœœœœœœ2œ"œœœ=œwœäœœœ‡œœœœœœœœœœœœAœ*œœœœœ:œž œœ(œœ œœ œœœ œœœœœ1œŸœœ œœ7œœ œ œœœœ œ œœœ_œœœÁœœ(œ:œœžœœ Kœ 1œœ œœœœ œ œ œ œFœœ'œ.œ œ"œœ2œRœ œœ  œœ œœœ œ/œ œ œœ‹œœœZœœœœœTœ  œœƒœœ.œ™œžœœœœœœœœœ"œœ œœœ;œ—œ¸œœ\œœœœ<œœœ…œœœœœœœœœœœœAœ*œœœœœ=œ˜Ë5JšÇœ +Ðck Ÿžœœ$œœœœœ œœœœ\œœœœœœœ#œœ œœJœœ%œ2œ&œœ'œœ)œœŸœœœJœ8œ œœ8œœ>œHœœœœœœ'œœœœœŸœdœœ œœœœ>œ œ œœœœœœœIœ0œ3œ(œœœPœœ œœ œ$œœ5œœœ"œœ8œYœBœ.Ÿœœ!œ œœžœœ˜ÈšœŸœœ˜/š œBœœœ'œ>œ ˜âšœ œ˜šœ%˜%Jšœ œœ˜'Jšœ2˜2Jšœ9˜9šœœ˜JšœB˜BJšœ˜šœ˜šœ˜šœ œ ˜Jšœ;˜;—Jšœœ4˜RJšœ˜—šœ˜Jšœ œ ˜Jšœ;˜;Jšœœ4˜RJšœ˜—šœ˜šœœ ˜ JšœA˜A—Jšœœ:˜XJ˜—šœ˜šœ-œ ˜=Jšœ?˜?—Jšœœ8˜VJ˜—šœ˜šœ+œ ˜;Jšœ;˜;—Jšœœ4˜RJ˜—J˜Jšœ˜—Jšœ˜—Jšœ˜—šœ˜JšœJ˜JJšœ˜—Jšœœ˜—šœ˜J˜———Jš‡œŸœœ]œœœ'œ œ œœ9œ œœœLœ,œøœœŸ œœ$œ œ œœ9œœ‚ œ8œ?œœiœœœŸ œœ"œ œœœ9œœƒ œ8œ?œœjœœœž œœœ œœ"œœœ/œœ!œ œ œœœxœœœœPœMœ œ·œJœœ œœœcœ1œjœœ6œ œœœœœœ œ/œœœœœœœœœœ'œuœœœ œœœ:œÐksœ œœœ@œ¢œœœœœ1œ œ œœœ œ œ œœœ œœœœœœÂ˜»%JšEœ)œ§œ'œ±œ/œœœœ œœœ œ7œ œœœ œœHœ*œœœœ œœ<œœ!œ¤œœ%œœiœœ‘˜ô šWœ XœCœœœ6œœ2œœœœœœœœ—œœ œ?œœœœœtœœœœœ,œœœ œœ9œ;œœœ@œœœœ œSœœ˜ šž œœ˜,Jšœœ3˜KJšœ˜J˜—šžœœ˜0Jšœœ3˜JJ˜—J˜šÐbnœœ œ œœœœœ˜UJšŸœ6œœœœœœœœœœIœœ1œœ9œ¾œìœœœœœœœ)œ2œœœœ~œrœœœœœœœ+œ1œœœœ}œ œœœœXœœœ3œœœ:œ œœ‰œœœœ1œœœœœœœœœ1œœœ œž œœœœ,œ!œ=œ˜°——šžœœ˜4Jšœ œ ˜Jšœœ ˜)Jšœœ˜ šœ#˜#Jšœ9™9Jšœ+™+—šœœ˜-Jšœœ˜Jšœ7˜7šœ"œ˜.Jšœœ˜?Jšœœ+œ+˜}Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜—Jšœ˜Jšœ˜Jšœœ˜™(Kšœ Ïr™—K™—…—Š£6