-- File: AlpineCmds.mesa
-- Last edited by:
-- MBrown on March 16, 1983 5:19 pm
-- Kolling on June 6, 1983 4:07 pm

-- Procs that might usefully be called from the interpreter:

-- Procs for use by any Alpine user:

-- Copy: PROC [to: ROPE, from: ROPE] ("to" and "from" may Alpine or local files).
-- Delete: PROC [file: ROPE] RETURNS[fileFound: BOOLEAN] ("file" must be an Alpine file)
-- List: PROC [pattern: ROPE] RETURNS [LIST OF REF ANY] (* allowed in "pattern")
-- ReadQuota: PROC [directory: ROPE] RETURNS [pageLimit, spaceInUse: PageCount]
-- GetReadAccess: PROC [file: ROPE] RETURNS [fileFound: BOOLEAN, accessList: LIST OF REF ANY]
-- GetModifyAccess: PROC [file: ROPE] RETURNS [fileFound: BOOLEAN, accessList: LIST OF REF ANY]
-- GetCreateTime: PROC [file: ROPE] RETURNS [fileFound: BOOLEAN, createTime: System.GreenwichMeanTime]
-- SetReadAccess: PROC [file: ROPE, list: LIST OF REF ANY] RETURNS [fileFound: BOOLEAN]
-- SetModifyAccess: PROC [file: ROPE, list: LIST OF REF ANY] RETURNS [fileFound: BOOLEAN]
-- GetOwnerCreateAccess: PROC [directory: ROPE] RETURNS [ownerFound: BOOLEAN, ownerAccessList: LIST OF REF ANY]
-- SetOwnerCreateAccess: PROC [directory: ROPE, list: LIST OF REF ANY] RETURNS [ownerFound: BOOLEAN]

-- Procs for use by administrators:

-- CreateOwner: PROC [directory: ROPE, pageLimit: PageCount]
-- WriteQuota: PROC [directory: ROPE, pageLimit: PageCount]
-- ListOwners: PROC [fileStore: ROPE] RETURNS [LIST OF REF ANY]
-- ReorganizeOwnerDB: PROC [fileStore: ROPE, nEntries: NAT]
-- GetOwnerModifyAccess: PROC [directory: ROPE] RETURNS [ownerFound: BOOLEAN, ownerAccessList: LIST OF REF ANY]
-- SetOwnerModifyAccess: PROC [directory: ROPE, list: LIST OF REF ANY] RETURNS [ownerFound: BOOLEAN]


DIRECTORY
AlpFile
USING[Handle, Open, ReadPages, ReadProperties, SetSize, WritePages, WriteProperties],
AlpineEnvironment
USING[AccessList, AccessRights, ByteCount, bytesPerPage, FileStore, LockOption,
nullVolumeGroupID, OwnerProperty, OwnerPropertyValuePair, Outcome, PageCount,
Property, PropertyValuePair, RName, UniversalFile, wordsPerPage],
AlpineInterimDirectory
USING[Delete, EnumerateDirectory, Error, OpenUnderTrans],
AlpInstance
USING[Create, Handle],
AlpTransaction
USING[AssertAlpineWheel, Create, CreateOwner, Finish,
GetNextVolumeGroup, Handle, ReadNextOwner, ReadOwnerProperties,
ReorganizeOwnerDB, Unknown, WriteOwnerProperties],
Directory
USING[GetProperty, PutProperty],
Environment
USING[Comparison],
File
USING[Capability, SetSize],
FileIO
USING[CapabilityFromStream, CreateOptions, minimumStreamBufferParms, Open],
IO
USING[Close, PutFR, STREAM, text],
List
USING[Cons, Sort],
PropertyTypes
USING[tByteLength, tCreateDate],
RefText
USING[TrustTextAsRope],
Rope
USING[Compare, Fetch, Find, IsEmpty, Match, ROPE, SkipTo, Substr],
Space
USING[CopyIn, CopyOut, Create, Delete, Handle, LongPointer, Map, Unmap,
virtualMemory],
System
USING[GreenwichMeanTime, gmtEpoch];

AlpineCmds: CEDAR PROGRAM
IMPORTS
AlpF: AlpFile, AlpI: AlpInstance, AlpT: AlpTransaction,
Directory, File, FileIO, AlpineInterimDirectory, IO, Lists: List, RefText, Rope, Space
= BEGIN OPEN AE: AlpineEnvironment;

ROPE: TYPE = Rope.ROPE;
CreateOptions: TYPE = FileIO.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: PROC [to: ROPE, from: ROPE] = {
transHandle: AlpT.Handle ← NIL;

FileStateObject: TYPE = RECORD [
SELECT type: {alpine, pilot} FROM
alpine => [fileHandle: AlpF.Handle],
pilot => [fc: File.Capability],
ENDCASE];

AlpineFileStateObject: TYPE = FileStateObject.alpine;
PilotFileStateObject: TYPE = FileStateObject.pilot;
FileState: TYPE = REF FileStateObject;
AlpineFileState: TYPE = REF AlpineFileStateObject;
PilotFileState: TYPE = REF PilotFileStateObject;

CreateFileState: PROC [file: ROPE, createOptions: CreateOptions, createLength: INT ← 0]
RETURNS [FileState] = {
IF file.Fetch[0] = '[ THEN {
universalFile: UniversalFile;
alpineFileState: AlpineFileState;
rightSquareBracket: INT ← file.Find["]", 1];
IF rightSquareBracket IN [-1..1] THEN ERROR Error[illegalFileName];
IF transHandle = NIL THEN
transHandle ← AlpT.Create[instHandle: AlpI.Create[fileStore:
file.Substr[start: 1, len: rightSquareBracket - 1]], createLocalWorker: TRUE];
[universalFile, ] ← AlpineInterimDirectory.OpenUnderTrans[
transHandle, file, createOptions, createLength];
alpineFileState ← NEW[AlpineFileStateObject ← [alpine[fileHandle:
AlpF.Open[transHandle, universalFile,
IF createOptions = oldOnly THEN readOnly ELSE readWrite,
[IF createOptions = oldOnly THEN read ELSE write, wait],
log, sequential].handle]]];
IF createOptions # oldOnly THEN
alpineFileState.fileHandle.SetSize[PagesForBytes[createLength]];
RETURN [alpineFileState];
}
ELSE {
stream: IO.STREAM;
pilotFileState: PilotFileState;
stream ← FileIO.Open[
fileName: file,
accessOptions: IF createOptions = oldOnly THEN read ELSE overwrite,
createOptions: createOptions,
createLength: createLength,
streamBufferParms: FileIO.minimumStreamBufferParms];
pilotFileState ← NEW[PilotFileStateObject ←
[pilot[fc: FileIO.CapabilityFromStream[stream]]]];
stream.Close[];
TRUSTED BEGIN IF createOptions # oldOnly THEN
File.SetSize[pilotFileState.fc, 1+PagesForBytes[createLength]]; END;
RETURN [pilotFileState];
};
};

GetFileProperties: PROC [fileState: FileState] RETURNS [
byteLength: ByteCount, createTime: System.GreenwichMeanTime] = {
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;
};
pilotFileState: PilotFileState => {
TRUSTED BEGIN
Directory.GetProperty[file: pilotFileState.fc, property: PropertyTypes.tByteLength,
propertyValue: DESCRIPTOR[
LOOPHOLE[LONG[@byteLength], LONG POINTER TO UNSPECIFIED],
SIZE[ByteCount]]];
Directory.GetProperty[file: pilotFileState.fc, property: PropertyTypes.tCreateDate,
propertyValue: DESCRIPTOR[
LOOPHOLE[LONG[@createTime], LONG POINTER TO UNSPECIFIED],
SIZE[System.GreenwichMeanTime]]];
END};
ENDCASE => ERROR;
};

SetFileProperties: PROC [fileState: FileState,
byteLength: ByteCount, createTime: System.GreenwichMeanTime] = {
WITH fileState SELECT FROM
alpineFileState: AlpineFileState => {
alpineFileState.fileHandle.WriteProperties[LIST[[byteLength[byteLength]],
[createTime[createTime]]]];
};
pilotFileState: PilotFileState => {
TRUSTED BEGIN
Directory.PutProperty[file: pilotFileState.fc, property: PropertyTypes.tByteLength,
propertyValue: DESCRIPTOR[
LOOPHOLE[LONG[@byteLength], LONG POINTER TO UNSPECIFIED],
SIZE[ByteCount]]];
Directory.PutProperty[file: pilotFileState.fc, property: PropertyTypes.tCreateDate,
propertyValue: DESCRIPTOR[
LOOPHOLE[LONG[@createTime], LONG POINTER TO UNSPECIFIED],
SIZE[System.GreenwichMeanTime]]];
END};
ENDCASE => ERROR;
};

buffer: Space.Handle;
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;
};
pilotFileState: PilotFileState => {
TRUSTED BEGIN Space.CopyIn[buffer, [pilotFileState.fc, pagesCopied+1]]; 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;
};
pilotFileState: PilotFileState => {
TRUSTED BEGIN Space.CopyOut[buffer, [pilotFileState.fc, pagesCopied+1]]; END;
};
ENDCASE => ERROR;
};


toFile, fromFile: FileState;
byteLength: ByteCount; createTime: System.GreenwichMeanTime;
fromFile ← CreateFileState[file: from, createOptions: oldOnly];
[byteLength, createTime] ← GetFileProperties[fromFile];
toFile ← CreateFileState[file: to, createOptions: none, createLength: byteLength];

{
pageCount: PageCount = PagesForBytes[byteLength];
bufferLen: CARDINAL = 12;
TRUSTED BEGIN
buffer ← Space.Create[bufferLen, Space.virtualMemory];
bufferPtr ← Space.LongPointer[buffer];
Space.Map[buffer];
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
Space.Unmap[buffer];
Space.Delete[buffer];
END;
SetFileProperties[toFile, byteLength, createTime];
IF (transHandle.Finish[requestedOutcome: commit, continue: FALSE] # commit)
THEN ERROR;
};
};

Delete: PROC [file: ROPE] RETURNS[fileFound: BOOLEAN] = {
fileFound ← TRUE;
AlpineInterimDirectory.Delete[file
! AlpineInterimDirectory.Error => IF why = fileNotFound THEN GOTO noFile];
EXITS noFile => fileFound ← FALSE;
};


GetCreateTime: PROC [file: ROPE] RETURNS [fileFound: BOOL, createTime:
System.GreenwichMeanTime] = {
transHandle: AlpT.Handle;
fileHandle: AlpF.Handle;
fileProperties: LIST OF PropertyValuePair;
[fileFound, transHandle, fileHandle] ← FromOldFileGetHandles[file, readOnly, [read, wait]];
IF NOT fileFound THEN RETURN [FALSE, System.gmtEpoch];
fileProperties ← fileHandle.ReadProperties[[createTime: TRUE]];
[] ← transHandle.Finish[commit, FALSE];
createTime ← NARROW[fileProperties.first, PropertyValuePair.createTime].createTime;
};


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


GetModifyAccess: 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]];
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 accessListLists.Sort[LOOPHOLE[resultAccessList, LIST OF REF ANY],
CompareProc]; END;
END;


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


SetModifyAccess: 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]];
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] RETURNS[fileFound: BOOLEAN, transHandle: AlpT.Handle, fileHandle:
AlpF.Handle] =
BEGIN
rightSquareBracket: INT;
IF
file.Fetch[0] # '[ THEN ERROR Error[illegalFileName];
rightSquareBracket ← file.Find["]", 1];
IF
rightSquareBracket IN [-1..1] THEN ERROR Error[illegalFileName];
fileFound ← TRUE;
transHandle ← AlpT.Create[instHandle: AlpI.Create[fileStore: file.Substr[start: 1, len:
rightSquareBracket - 1]], createLocalWorker: TRUE];
fileHandle ← AlpF.Open[transHandle, AlpineInterimDirectory.OpenUnderTrans[transHandle,
file, oldOnly
! AlpineInterimDirectory.Error => IF why = fileNotFound THEN BEGIN fileFound ←
FALSE; CONTINUE; END;].universalFile, access, lock, log, sequential].handle;
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 [Environment.Comparison] = CHECKED {
RETURN [Rope.Compare[NARROW[ref1], NARROW[ref2], FALSE]];
};


List
: PROC [pattern: ROPE] RETURNS [LIST OF REF ANY] = {
directory, restOfPattern: ROPE;
resultList: LIST OF REF ANYNIL;
EnumProc: PROC [fileName: REF TEXT, universalFile: UniversalFile]
RETURNS [quit: BOOL] = {
IF Rope.Match[pattern: restOfPattern,
object: RefText.TrustTextAsRope[fileName], case: FALSE] THEN {
resultRope: ROPEIO.PutFR["%g", IO.text[fileName]];
resultList ← CONS[first: resultRope, rest: resultList];
};
RETURN [quit: FALSE];
};
resultRope: ROPENIL;
[directory, restOfPattern] ← DecomposePattern[pattern];
AlpineInterimDirectory.EnumerateDirectory[
directoryName: directory, enumProc: EnumProc];
resultList ← Lists.Sort[resultList, CompareProc];
RETURN [resultList];
};

DecomposePattern: PROC [pattern: ROPE]
RETURNS [directory, restOfPattern: ROPE] = {
rightAngleBracket: INT;
IF NOT Rope.Match[pattern: "[*]<*>*", object: pattern, case: FALSE] THEN
ERROR Error[illegalFileName];
rightAngleBracket ← pattern.SkipTo[1, ">"];
directory ← pattern.Substr[start: 0, len: rightAngleBracket+1];
restOfPattern ← pattern.Substr[start: rightAngleBracket+1];
IF restOfPattern.IsEmpty[] THEN restOfPattern ← "*";
};

Error: ERROR [what: ErrorType] = CODE;
ErrorType: TYPE = {illegalFileName};

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

CreateOwner: 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;
};


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


GetOwnerModifyAccess
: 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: PROC [directory: ROPE, list: LIST OF REF ANY] RETURNS
[ownerFound: BOOLEAN] =
BEGIN RETURN[SetOwnerAccess[directory, list, createAccessList]];
END;


SetOwnerModifyAccess
: 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 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 Error[illegalFileName];
transHandle ← AlpT.Create[instHandle: AlpI.Create[fileStore: fileStore], createLocalWorker:
TRUE];
IF assertWheel THEN transHandle.AssertAlpineWheel[TRUE];
};

ReadQuota: 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: 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: 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: 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
.