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
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]];
loop through properties and print those enabled
UNTIL properties = NIL DO
property: AlpineEnvironment.PropertyValuePair ← properties.first ;
properties ← properties.rest;
IF displayProperties[property.property] THEN BEGIN
SELECT property.property FROM
byteLength
byteLength => {
byteLength: INT ← NARROW[property,
AlpineEnvironment.PropertyValuePair.byteLength].byteLength;
resultRope ← Rope.Concat[resultRope,
IO.PutFR["%g bytes ", IO.int[byteLength]]];
};
createTime
createTime => {
createTime: BasicTime.GMT ← NARROW[property,
AlpineEnvironment.PropertyValuePair.createTime].createTime;
resultRope ← Rope.Concat[resultRope,
IO.PutFR["%g ", IO.time[createTime]]];
};
highWaterMark
highWaterMark => {
highWaterMark: AlpineEnvironment.PageCount ← NARROW[property,
AlpineEnvironment.PropertyValuePair.highWaterMark].highWaterMark;
resultRope ← Rope.Concat[resultRope,
IO.PutFR["HWM: %g ", IO.int[highWaterMark]]];
};
modifyAccess
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 => {
owner: AlpineEnvironment.OwnerName ← NARROW[property,
AlpineEnvironment.PropertyValuePair.owner].owner;
resultRope ← Rope.Concat[resultRope,
IO.PutFR["owner: %g ", IO.rope[owner]]];
};
readAccess
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 => {
stringName: ROPE ← NARROW[property,
AlpineEnvironment.PropertyValuePair.stringName].stringName;
resultRope ← Rope.Concat[resultRope,
IO.PutFR["stringName: %g ", IO.rope[stringName]]];
};
version
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 {
alpine files to list
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 {
FS or IFS files to list
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;
loop through all the properties and find the one selected
comparison is by Rope equivalent to option!
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.
Bob Hagmann June 22, 1985 1:07:13 pm PDT
changes to: SetFileProperties