-- File: AlpineCmdsImpl.mesa
-- Last edited by:
-- MBrown on March 16, 1983 5:19 pm
-- Kolling on October 21, 1983 5:28 pm
-- Last Edited by: Hauser, January 9, 1985 11:33:01 am PST


DIRECTORY
AlpFile
USING[GetSize, Handle, ReadPages, ReadProperties, SetSize, WritePages,
WriteProperties],
AlpineCmds
USING[Error, ErrorType],
AlpineEnvironment
USING[AccessList, AccessRights, ByteCount, bytesPerPage, FileStore, LockOption,
nullVolumeGroupID, OwnerProperty, OwnerPropertyValuePair, Outcome, PageCount,
Property, PropertyValuePair, RName, UniversalFile, wordsPerPage],
AlpineDirectory
USING[CreateOptions, Error, OpenFile],
AlpDirectory,
AlpineWalnutCmds,
AlpInstance
USING[Create, Handle],
AlpTransaction
USING[AssertAlpineWheel, Create, CreateOwner, DestroyOwner, Finish,
GetNextVolumeGroup, Handle, ReadNextOwner, ReadOwnerProperties,
ReorganizeOwnerDB, Unknown, WriteOwnerProperties],
Basics
USING[Comparison],
BasicTime
USING[GMT, nullGMT],
FS
USING[Close, Create, GetInfo, Open, OpenFile, PagesForBytes, Read,
SetByteCountAndCreatedTime, Write],
IO
USING[PutFR, rope],
List
USING[Cons, Sort],
Rope
USING[Compare, Fetch, Find, IsEmpty, Match, ROPE, SkipTo, Substr],
VM
USING[AddressForPageNumber, Allocate, Free, Interval];

AlpineCmdsImpl: CEDAR PROGRAM
IMPORTS AlpF: AlpFile, AlpineCmds, AlpineDirectory, AlpDirectory, AlpI: AlpInstance, AlpT:
AlpTransaction, FS, IO, Lists: List, Rope, VM
EXPORTS AlpineCmds, AlpineWalnutCmds

= BEGIN OPEN AE: AlpineEnvironment;

ROPE: TYPE = Rope.ROPE;
CreateOptions: TYPE = AlpineDirectory.CreateOptions;

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];
};


Copy: PUBLIC PROC [to: ROPE, from: ROPE] =
BEGIN
KernelCopy[to: to, from: from, enableWheel: FALSE, expunging: FALSE, excessBytes: 0]
END;

WheelCopy: PUBLIC PROC [to: ROPE, from: ROPE, enableWheel: BOOLEAN] =
BEGIN
KernelCopy[to: to, from: from, enableWheel: enableWheel, expunging: FALSE, excessBytes: 0]
END;

CopyForExpunge
: PUBLIC PROC [to: ROPE, from: ROPE, excessBytes: ByteCount] =
BEGIN
IF excessBytes < 0 THEN ERROR;
KernelCopy[to: to, from: from, enableWheel: FALSE, expunging: TRUE, excessBytes:
excessBytes]
END;

-- excessBytes is only used if expunging = TRUE.
KernelCopy
: PROC [to: ROPE, from: ROPE, enableWheel: BOOLEAN, expunging: BOOLEAN,
excessBytes: ByteCount] =
{
transHandle: AlpT.Handle ← NIL;

FileStateObject: TYPE = RECORD [
SELECT type: {alpine, nucleus} FROM
alpine => [fileHandle: AlpF.Handle],
nucleus => [openFile: FS.OpenFile],
ENDCASE];

AlpineFileStateObject: TYPE = FileStateObject.alpine;
NucleusFileStateObject: TYPE = FileStateObject.nucleus;
FileState: TYPE = REF FileStateObject;
AlpineFileState: TYPE = REF AlpineFileStateObject;
NucleusFileState: TYPE = REF NucleusFileStateObject;

CreateFileState: PROC [file: ROPE, createOptions: CreateOptions, createLength: INT ← 0]
RETURNS [FileState] = {
IF file.Fetch[0] = '[ THEN {
alpineFileState: AlpineFileState;
rightSquareBracket: INT ← file.Find["]", 1];
IF rightSquareBracket IN [-1..1]
THEN
BEGIN IF expunging
THEN ERROR IllegalFileName
ELSE ERROR AlpineCmds.Error[illegalFileName];
END;
IF transHandle = NIL THEN
BEGIN
transHandle ← AlpT.Create[instHandle: AlpI.Create[fileStore:
file.Substr[start: 1, len: rightSquareBracket - 1]], createLocalWorker: TRUE];
IF enableWheel THEN transHandle.AssertAlpineWheel[TRUE];
END;
alpineFileState ← NEW[AlpineFileStateObject ← [alpine[fileHandle:
AlpineDirectory.OpenFile[trans: transHandle, name: file,
access: IF createOptions = oldOnly THEN readOnly ELSE readWrite,
lock: [IF createOptions = oldOnly THEN read ELSE write, wait],
recoveryOption: log, referencePattern: sequential].openFileID]]];
IF createOptions # oldOnly -- this is the "to" file.
THEN BEGIN
IF (expunging)
THEN BEGIN
alpineFileState.fileHandle.WriteProperties[LIST[[highWaterMark[0]]]];
IF (transHandle.Finish[requestedOutcome: commit, continue: TRUE] #
commit) THEN ERROR;
END;
alpineFileState.fileHandle.SetSize[PagesForBytes[createLength + (IF expunging
THEN excessBytes ELSE 0)]];
END;
RETURN [alpineFileState];
}
ELSE {
nucleusFileState: NucleusFileState;
openFile: FS.OpenFile;
openFile ← IF createOptions = oldOnly
THEN FS.Open[name: file, lock: $read, wantedCreatedTime: BasicTime.nullGMT,
remoteCheck: TRUE, wDir: NIL]
ELSE FS.Create[name: file, setPages: TRUE, pages: FS.PagesForBytes[createLength],
wDir: NIL];
nucleusFileState ← NEW[NucleusFileStateObject ← [nucleus[openFile: openFile]]];
RETURN [nucleusFileState];
};
};

CloseFile: PROC[fileState: FileState] =
BEGIN
WITH fileState SELECT FROM
alpineFileState: AlpineFileState => NULL;
nucleusFileState: NucleusFileState => FS.Close[nucleusFileState.openFile];
ENDCASE => ERROR;
END;


AlpineFileType
: PROC[fileState: FileState] RETURNS[alpineFile: BOOLEAN]=
BEGIN
WITH fileState SELECT FROM
alpineFileState: AlpineFileState => RETURN[TRUE];
nucleusFileState: NucleusFileState => RETURN[FALSE];
ENDCASE => ERROR;
END;


GetFileProperties
: PROC [fileState: FileState] RETURNS [
byteLength: ByteCount, createTime: BasicTime.GMT] = {
WITH fileState SELECT FROM
alpineFileState: AlpineFileState => {
fileProperties: LIST OF PropertyValuePair =
alpineFileState.fileHandle.ReadProperties[[byteLength: TRUE, createTime: TRUE]];
byteLength ← NARROW[
fileProperties.first, PropertyValuePair.byteLength].byteLength;
createTime ← NARROW[
fileProperties.rest.first, PropertyValuePair.createTime].createTime;
};
nucleusFileState: NucleusFileState => {
BEGIN
[, , byteLength, createTime, ] ← FS.GetInfo[file: nucleusFileState.openFile];
END
};
ENDCASE => ERROR;
};

SetFileProperties: PROC [fileState: FileState,
byteLength: ByteCount, createTime: BasicTime.GMT] = {
WITH fileState SELECT FROM
alpineFileState: AlpineFileState => {
alpineFileState.fileHandle.WriteProperties[LIST[[byteLength[byteLength]],
[createTime[createTime]]]];
};
nucleusFileState: NucleusFileState => {
FS.SetByteCountAndCreatedTime[file: nucleusFileState.openFile, bytes: byteLength,
created: createTime];
};
ENDCASE => ERROR;
};

bufferPtr: LONG POINTER;
pagesCopied: INT;

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;
};
nucleusFileState: NucleusFileState => {
TRUSTED BEGIN FS.Read[file: nucleusFileState.openFile, 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;
};
nucleusFileState: NucleusFileState => {
TRUSTED BEGIN FS.Write[file: nucleusFileState.openFile, to: pagesCopied, nPages:
pagesToMove, from: bufferPtr]; END;
};
ENDCASE => ERROR;
};


toFile, fromFile: FileState;
byteLength: ByteCount;
createTime: BasicTime.GMT;
fromFile ← CreateFileState[file: from, createOptions: oldOnly];
[byteLength, createTime] ← GetFileProperties[fromFile];
toFile ← CreateFileState[file: to, createOptions: none, createLength: byteLength!
UNWIND => CloseFile[fromFile];];

BEGIN
BEGIN
ENABLE UNWIND => BEGIN CloseFile[toFile]; CloseFile[fromFile]; END;
pageCount: PageCount = PagesForBytes[byteLength];
bufferLen: CARDINAL = 12;
bufferInterval: VM.Interval ← VM.Allocate[count: bufferLen];
TRUSTED BEGIN bufferPtr ← VM.AddressForPageNumber[bufferInterval.page]; END;
pagesCopied ← 0;
UNTIL pagesCopied = pageCount
DO
pagesLeft: CARDINAL ← pageCount-pagesCopied;
pagesToMove: CARDINALMIN [bufferLen, pagesLeft];
FillBuffer[fromFile, pagesToMove];
EmptyBuffer[toFile, pagesToMove];
pagesCopied ← pagesCopied + pagesToMove;
ENDLOOP;
TRUSTED BEGIN VM.Free[bufferInterval]; END;
SetFileProperties[toFile, byteLength, createTime];
END;
CloseFile[toFile]; CloseFile[fromFile];
IF ((transHandle.Finish[requestedOutcome: commit, continue: FALSE] # commit) AND
(AlpineFileType[toFile])) THEN ERROR;
END;
};

Delete: PUBLIC PROC [file: ROPE] RETURNS[fileFound: BOOLEAN] = {
fileFound ← TRUE;
[] ← AlpDirectory.DeleteFile[ name: file
! AlpDirectory.Error => IF type = fileNotFound THEN GOTO noFile];
EXITS noFile => fileFound ← FALSE
};


GetCreateTime: PUBLIC PROC [file: ROPE] RETURNS [fileFound: BOOL, createTime:
BasicTime.GMT] = {
[fileFound, createTime] ← WheelGetCreateTime[file, FALSE];
};


WheelGetCreateTime: PUBLIC PROC [file: ROPE, enableWheel: BOOLEAN] RETURNS [fileFound:
BOOL, createTime: BasicTime.GMT] = {
transHandle: AlpT.Handle;
fileHandle: AlpF.Handle;
fileProperties: LIST OF PropertyValuePair;
[fileFound, transHandle, fileHandle] ← FromOldFileGetHandles[file, readOnly, [read, wait],
enableWheel];
IF NOT fileFound THEN RETURN [FALSE, BasicTime.nullGMT];
fileProperties ← fileHandle.ReadProperties[[createTime: TRUE]];
[] ← transHandle.Finish[commit, FALSE];
createTime ← NARROW[fileProperties.first, PropertyValuePair.createTime].createTime;
};


GetSize: PUBLIC PROC [file: ROPE] RETURNS [fileFound: BOOL, size: PageCount] = {
transHandle: AlpT.Handle;
fileHandle: AlpF.Handle;
[fileFound, transHandle, fileHandle] ← FromOldFileGetHandles[file, readOnly, [read, wait],
FALSE];
IF NOT fileFound THEN RETURN [FALSE, 0];
size ← fileHandle.GetSize[];
[] ← transHandle.Finish[commit, FALSE];
};


-- this does not truncate to before the byteLength, if asked to it will instead truncate to PagesForBytes[byteLength].

SetSize: PUBLIC PROC [file: ROPE, size: PageCount] RETURNS [fileFound: BOOL, newSize:
PageCount] = {
transHandle: AlpT.Handle;
fileHandle: AlpF.Handle;
fileProperties: LIST OF PropertyValuePair;
oldSize: PageCount;
[fileFound, transHandle, fileHandle] ← FromOldFileGetHandles[file, readWrite, [write, wait],
FALSE];
IF NOT fileFound THEN RETURN [FALSE, 0];
fileProperties ← fileHandle.ReadProperties[[byteLength: TRUE]];
oldSize ← PagesForBytes[NARROW[fileProperties.first,
PropertyValuePair.byteLength].byteLength];
newSize ← MAX[size, oldSize];
fileHandle.SetSize[newSize];
IF transHandle.Finish[commit, FALSE] # commit THEN ERROR;
};


GetReadAccess: PUBLIC PROCEDURE[file: ROPE] RETURNS[fileFound: BOOLEAN, accessList:
LIST OF REF ANY] =
BEGIN [fileFound, accessList] ← GetFileAccess[file, readAccess];
END;


GetModifyAccess: PUBLIC PROCEDURE[file: ROPE] RETURNS[fileFound: BOOLEAN,
accessList: LIST OF REF ANY] =
BEGIN [fileFound, accessList] ← GetFileAccess[file, modifyAccess];
END;


GetFileAccess: PROCEDURE[file: ROPE, accessProp: AE.Property] RETURNS[fileFound:
BOOLEAN, accessList: LIST OF REF ANY] =
BEGIN
transHandle: AlpT.Handle;
fileHandle: AlpF.Handle;
resultAccessList: AE.AccessList;
fileProperty: AE.PropertyValuePair;
[fileFound, transHandle, fileHandle] ← FromOldFileGetHandles[file: file, access: readOnly,
lock: [read, wait], enableWheel: FALSE];
IF NOT fileFound THEN RETURN[FALSE, NIL];
TRUSTED BEGIN fileProperty ← fileHandle.ReadProperties[[readAccess: (accessProp =
readAccess), modifyAccess: (accessProp = modifyAccess)]].first; END;
[] ← transHandle.Finish[commit, FALSE];
resultAccessList ←
(SELECT accessProp FROM
readAccess => NARROW[fileProperty, PropertyValuePair.readAccess].readAccess,
modifyAccess => NARROW[fileProperty, PropertyValuePair.modifyAccess].modifyAccess,
ENDCASE => ERROR);
TRUSTED BEGIN accessList ← Lists.Sort[LOOPHOLE[resultAccessList, LIST OF REF ANY],
CompareProc]; END;
END;


SetReadAccess: PUBLIC PROCEDURE[file: ROPE, list: LIST OF REF ANY] RETURNS[fileFound: BOOLEAN]
=
BEGIN fileFound ← SetFileAccess[file, list, readAccess];
END;


SetModifyAccess: PUBLIC PROCEDURE[file: ROPE, list: LIST OF REF ANY] RETURNS[fileFound:
BOOLEAN] =
BEGIN fileFound ← SetFileAccess[file, list, modifyAccess];
END;


SetFileAccess: PROCEDURE[file: ROPE, list: LIST OF REF ANY, accessProp:
AE.Property] RETURNS[fileFound: BOOLEAN] =
BEGIN
transHandle: AlpT.Handle;
fileHandle: AlpF.Handle;
accessList: AE.AccessList ← MakeListOfRefAnyAnAccessList[list];
fileProperties: LIST OF AE.PropertyValuePair ←
(SELECT accessProp FROM
readAccess => LIST[[readAccess[accessList]]],
modifyAccess => LIST[[modifyAccess[accessList]]],
ENDCASE => ERROR);
[fileFound, transHandle, fileHandle] ← FromOldFileGetHandles[file: file, access: readOnly,
lock: [read, wait], enableWheel: FALSE];
IF NOT fileFound THEN RETURN;
fileHandle.WriteProperties[properties: fileProperties, lock: [write, wait]];
IF (transHandle.Finish[requestedOutcome: commit, continue: FALSE] # commit) THEN ERROR;
END;


MakeListOfRefAnyAnAccessList: PROCEDURE[inList: LIST OF REF ANY] RETURNS[outList:
AE.AccessList] =
BEGIN
FOR myList: LIST OF REF ANY ← inList, myList.rest
UNTIL myList = NIL
DO outList ← CONS[NARROW[myList.first, AE.RName], outList];
ENDLOOP;
END;


FromOldFileGetHandles: PROCEDURE[file: ROPE, access: AE.AccessRights, lock:
AE.LockOption, enableWheel: BOOLEAN] RETURNS[fileFound: BOOLEAN, transHandle:
AlpT.Handle, fileHandle: AlpF.Handle] =
BEGIN
rightSquareBracket: INT;
IF file.Fetch[0] # '[ THEN ERROR AlpineCmds.Error[illegalFileName];
rightSquareBracket ← file.Find["]", 1];
IF rightSquareBracket IN [-1..1] THEN ERROR AlpineCmds.Error[illegalFileName];
fileFound ← TRUE;
transHandle ← AlpT.Create[instHandle: AlpI.Create[fileStore: file.Substr[start: 1, len:
rightSquareBracket - 1]], createLocalWorker: TRUE];
IF enableWheel THEN transHandle.AssertAlpineWheel[TRUE];
fileHandle ← AlpineDirectory.OpenFile[ trans: transHandle, name: file, access: access,
lock: lock ! AlpineDirectory.Error => IF type = fileNotFound THEN BEGIN fileFound ←
FALSE; CONTINUE; END;].openFileID;
IF NOT fileFound
THEN BEGIN [] ← transHandle.Finish[requestedOutcome: abort, continue: FALSE];
RETURN[FALSE, NIL, NIL];
END;
END;


CompareProc: SAFE PROC [ref1, ref2: REF ANY]
RETURNS [Basics.Comparison] = CHECKED {
RETURN [Rope.Compare[NARROW[ref1], NARROW[ref2], FALSE]];
};
EnumProc: TYPE = PROC [ fileName: ROPE, universalFile: AE.UniversalFile ] RETURNS [ quit: BOOL ];
EnumeratePattern: PROC [ pattern: ROPE, enumProc: EnumProc] = {
file: AE.UniversalFile;
fullPathName, link: ROPE;
[file, fullPathName, link] ← AlpDirectory.Enumerate[ pattern: pattern, previousFile: NIL];
WHILE fullPathName # NIL DO
[] ← enumProc[ fullPathName, file ];
[file, fullPathName, link] ← AlpDirectory.Enumerate[ pattern: pattern, previousFile: fullPathName];
ENDLOOP;
};


List
: PUBLIC PROC [pattern: ROPE] RETURNS [LIST OF REF ANY] = {
resultList: LIST OF REF ANYNIL;
EnumProc: PROC [fileName: ROPE, universalFile: UniversalFile]
RETURNS [quit: BOOL] = {
resultRope: ROPEIO.PutFR["%g", IO.rope[fileName]];
resultList ← CONS[first: resultRope, rest: resultList];
RETURN [quit: FALSE];
};
resultRope: ROPENIL;
EnumeratePattern[
pattern: pattern, enumProc: EnumProc];
resultList ← Lists.Sort[resultList, CompareProc];
RETURN [resultList];
};

IllegalFileName: PUBLIC ERROR = CODE; -- AlpineWalnutCmds error, for stability in interface, only errored from CopyForExpunge.

Error: PUBLIC ERROR [what: AlpineCmds.ErrorType] = CODE;

OwnerPropertyValuePair
: TYPE = AE.OwnerPropertyValuePair;
OwnerPropertyValuePairList: TYPE = LIST OF OwnerPropertyValuePair;

CreateOwner: PUBLIC PROC [directory: ROPE, pageLimit: PageCount] = {
p: REF OwnerPropertyValuePair = NEW[
OwnerPropertyValuePair ← [quota[quota: pageLimit]]]; -- crock to make it compile
properties: OwnerPropertyValuePairList = LIST[[quota[pageLimit]]];
transHandle: AlpT.Handle;
owner: ROPE;
[transHandle, owner] ← FromDirectoryGetTransHandleAndOwner[directory: directory,
assertWheel: TRUE];
[] ← transHandle.CreateOwner[
volumeGroupID: transHandle.GetNextVolumeGroup[previousGroup:
AE.nullVolumeGroupID, lock: [none, wait]],
owner: owner,
properties: properties];
IF (transHandle.Finish[requestedOutcome: commit, continue: FALSE] # commit) THEN ERROR;
};


DestroyOwner: PUBLIC PROC [directory: ROPE] =
BEGIN
transHandle: AlpT.Handle;
owner: ROPE;
[transHandle, owner] ← FromDirectoryGetTransHandleAndOwner[directory: directory,
assertWheel: TRUE];
[] ← transHandle.DestroyOwner[
volumeGroupID: transHandle.GetNextVolumeGroup[previousGroup:
AE.nullVolumeGroupID, lock: [none, wait]], owner: owner];
IF (transHandle.Finish[requestedOutcome: commit, continue: FALSE] # commit) THEN ERROR;
END;


GetOwnerCreateAccess: PUBLIC PROC [directory: ROPE] RETURNS [ownerFound: BOOLEAN,
ownerAccessList: LIST OF REF ANY] =
BEGIN [ownerFound, ownerAccessList]GetOwnerAccess[directory, createAccessList];
END;


GetOwnerModifyAccess
: PUBLIC PROC [directory: ROPE] RETURNS [ownerFound: BOOLEAN,
ownerAccessList: LIST OF REF ANY] =
BEGIN [ownerFound, ownerAccessList]GetOwnerAccess[directory, modifyAccessList];
END;


GetOwnerAccess
: PROC [directory: ROPE, ownerAccessProp: AE.OwnerProperty]
RETURNS[ownerFound: BOOLEAN, ownerAccessList: LIST OF REF ANY] =
BEGIN
transHandle: AlpT.Handle;
owner: ROPE;
resultOwnerAccessList: AE.AccessList;
ownerProperty: AE.OwnerPropertyValuePair;
[transHandle, owner] ← FromDirectoryGetTransHandleAndOwner[directory: directory,
assertWheel: FALSE];
ownerFound ← TRUE;
TRUSTED BEGIN ownerProperty ← transHandle.ReadOwnerProperties[volumeGroupID:
transHandle.GetNextVolumeGroup[previousGroup: AE.nullVolumeGroupID, lock: [none,
wait]], owner: owner, desiredProperties: [createAccessList: (ownerAccessProp =
createAccessList), modifyAccessList: (ownerAccessProp = modifyAccessList)]
! AlpT.Unknown => IF what = owner THEN BEGIN ownerFound ← FALSE; CONTINUE;
END;].first; END;
[] ← transHandle.Finish[requestedOutcome: (IF ownerFound THEN commit ELSE abort),
continue: FALSE];
IF NOT ownerFound THEN RETURN[FALSE, NIL];
resultOwnerAccessList ←
(SELECT ownerAccessProp FROM
createAccessList =>
NARROW
[ownerProperty, OwnerPropertyValuePair.createAccessList].createAccessList,
modifyAccessList =>
NARROW
[ownerProperty, OwnerPropertyValuePair.modifyAccessList].modifyAccessList,
ENDCASE => ERROR);
TRUSTED BEGIN ownerAccessListLists.Sort[LOOPHOLE[resultOwnerAccessList, LIST OF
REF ANY
], CompareProc]; END;
END;


SetOwnerCreateAccess: PUBLIC PROC [directory: ROPE, list: LIST OF REF ANY] RETURNS
[ownerFound: BOOLEAN] =
BEGIN RETURN[SetOwnerAccess[directory, list, createAccessList]];
END;


SetOwnerModifyAccess
: PUBLIC PROC [directory: ROPE, list: LIST OF REF ANY] RETURNS
[ownerFound: BOOLEAN] =
BEGIN RETURN[SetOwnerAccess[directory, list, modifyAccessList]];
END;


SetOwnerAccess
: PROC [directory: ROPE, list: LIST OF REF ANY, ownerAccessProp:
AE.OwnerProperty] RETURNS [ownerFound: BOOLEAN] =
BEGIN
transHandle: AlpT.Handle;
owner: ROPE;
ownerAccessList: AE.AccessList ← MakeListOfRefAnyAnAccessList[list];
ownerProperties: LIST OF AE.OwnerPropertyValuePair ←
(SELECT
ownerAccessProp FROM
createAccessList => LIST[[createAccessList[ownerAccessList]]],
modifyAccessList => LIST[[modifyAccessList[ownerAccessList]]],
ENDCASE => ERROR);
[transHandle, owner] ← FromDirectoryGetTransHandleAndOwner[directory: directory,
assertWheel: TRUE];
ownerFound ← TRUE;
transHandle.WriteOwnerProperties[volumeGroupID:
transHandle.GetNextVolumeGroup[previousGroup: AE.nullVolumeGroupID, lock: [none,
wait]], owner: owner, overCommitQuotasIfNeeded: TRUE, properties: ownerProperties
! AlpT.Unknown => IF what = owner THEN BEGIN ownerFound ← FALSE; CONTINUE;
END;];
IF ownerFound
THEN
BEGIN
IF
transHandle.Finish[requestedOutcome: commit, continue: FALSE] # commit
THEN ERROR;
END
ELSE [] ← transHandle.Finish[requestedOutcome: abort, continue: FALSE];
END
;


FromDirectoryGetTransHandleAndOwner: PROC [directory: ROPE, assertWheel: BOOLEAN]
RETURNS [transHandle: AlpT.Handle, owner: ROPE] = {
rightSquareBracket, rightAngleBracket: INT;
fileStore, restOfFileName: ROPE;
IF NOT Rope.Match[pattern: "[*]<*>*", object: directory, case: FALSE] THEN
ERROR AlpineCmds.Error[illegalFileName];
rightSquareBracket ← directory.SkipTo[1, "]"];
fileStore ← directory.Substr[start: 1, len: rightSquareBracket-1];
rightAngleBracket ← directory.SkipTo[1, ">"];
owner ← directory.Substr[
start: rightSquareBracket+2, len: rightAngleBracket-rightSquareBracket-2];
restOfFileName ← directory.Substr[start: rightAngleBracket+1];
IF fileStore.IsEmpty[] OR owner.IsEmpty[] OR NOT restOfFileName.IsEmpty[] THEN
ERROR AlpineCmds.Error[illegalFileName];
transHandle ← AlpT.Create[instHandle: AlpI.Create[fileStore: fileStore], createLocalWorker:
TRUE];
IF assertWheel THEN transHandle.AssertAlpineWheel[TRUE];
};

ReadQuota: PUBLIC PROC [directory: ROPE] RETURNS [pageLimit, spaceInUse: PageCount] = {
properties: OwnerPropertyValuePairList;
transHandle: AlpT.Handle;
owner: ROPE;
[transHandle, owner] ← FromDirectoryGetTransHandleAndOwner[directory: directory,
assertWheel: FALSE];
properties ← transHandle.ReadOwnerProperties[
volumeGroupID: transHandle.GetNextVolumeGroup[previousGroup:
AE.nullVolumeGroupID, lock: [none, wait]],
owner: owner,
desiredProperties: [quota: TRUE, spaceInUse: TRUE]];
[] ← transHandle.Finish[requestedOutcome: commit, continue: FALSE];
UNTIL properties = NIL DO -- because ReadOwnerProperties does not sort them yet ...
WITH properties.first SELECT FROM
q: OwnerPropertyValuePair.quota => pageLimit ← q.quota;
s: OwnerPropertyValuePair.spaceInUse => spaceInUse ← s.spaceInUse;
ENDCASE => ERROR;
properties ← properties.rest;
ENDLOOP;
};

WriteQuota: PUBLIC PROC [directory: ROPE, pageLimit: PageCount] = {
properties: OwnerPropertyValuePairList = LIST[[quota[pageLimit]]];
transHandle: AlpT.Handle;
owner: ROPE;
[transHandle, owner] ← FromDirectoryGetTransHandleAndOwner[directory: directory,
assertWheel: TRUE];
transHandle.WriteOwnerProperties[
volumeGroupID: transHandle.GetNextVolumeGroup[previousGroup:
AE.nullVolumeGroupID, lock: [none, wait]],
owner: owner,
properties: properties];
IF (transHandle.Finish[requestedOutcome: commit, continue: FALSE]) # commit THEN ERROR;
};

ListOwners: PUBLIC PROC [fileStore: ROPE] RETURNS [LIST OF REF ANY] = {
result: LIST OF REF ANYNIL;
owner: ROPE;
transHandle: AlpT.Handle ← AlpT.Create[instHandle: AlpI.Create[fileStore: fileStore],
createLocalWorker: TRUE];
transHandle.AssertAlpineWheel[TRUE];
owner ← NIL;
DO
[owner, ] ← transHandle.ReadNextOwner[
volumeGroupID: transHandle.GetNextVolumeGroup[previousGroup:
AE.nullVolumeGroupID, lock: [none, wait]],
previousOwner: owner,
desiredProperties: ALL [FALSE]];
IF owner = NIL THEN EXIT;
result ← Lists.Cons[owner, result];
ENDLOOP;
[] ← transHandle.Finish[requestedOutcome: commit, continue: FALSE];
RETURN [Lists.Sort[result, CompareProc]];
};

ReorganizeOwnerDB: PUBLIC PROC [fileStore: ROPE, nEntries: NAT] = {
transHandle: AlpT.Handle ← AlpT.Create[instHandle: AlpI.Create[fileStore: fileStore],
createLocalWorker: TRUE];
transHandle.AssertAlpineWheel[TRUE];
[] ← transHandle.ReorganizeOwnerDB[
volumeGroupID: transHandle.GetNextVolumeGroup[previousGroup:
AE.nullVolumeGroupID, lock: [none, wait]],
nEntries: nEntries];
IF (transHandle.Finish[requestedOutcome: commit, continue: FALSE] # commit) THEN ERROR;
};

SpecialReorganizeOwnerDB: PROC [fileStore: ROPE, nEntries: NAT] = {
result, resultList: LIST OF REF ANYNIL;
owner: ROPE;
transHandle: AlpT.Handle ← AlpT.Create[instHandle: AlpI.Create[fileStore: fileStore],
createLocalWorker: TRUE];
transHandle.AssertAlpineWheel[TRUE];
owner ← NIL;
DO
[owner, ] ← transHandle.ReadNextOwner[
volumeGroupID: transHandle.GetNextVolumeGroup[previousGroup:
AE.nullVolumeGroupID, lock: [none, wait]],
previousOwner: owner,
desiredProperties: ALL [FALSE]];
IF owner = NIL THEN EXIT;
result ← Lists.Cons[owner, result];
ENDLOOP;
resultList ← Lists.Sort[result, CompareProc];
[] ← transHandle.ReorganizeOwnerDB[
volumeGroupID: transHandle.GetNextVolumeGroup[previousGroup:
AE.nullVolumeGroupID, lock: [none, wait]],
nEntries: nEntries];
owner ← NIL;
result ← NIL;
DO

[owner, ] ← transHandle.ReadNextOwner[
volumeGroupID: transHandle.GetNextVolumeGroup[previousGroup:
AE.nullVolumeGroupID, lock: [none, wait]],
previousOwner: owner,
desiredProperties: ALL [FALSE]];
IF owner = NIL THEN EXIT;
result ← Lists.Cons[owner, result];
ENDLOOP;
resultList ← Lists.Sort[result, CompareProc];
IF
(transHandle.Finish[requestedOutcome: commit, continue: FALSE] # commit) THEN ERROR;
};


END
.