XMessageImpl.mesa
Copyright Ó 1984, 1985, 1986, 1987 by Xerox Corporation. All rights reserved.
Tim Diebert: January 6, 1987 5:21:16 pm PST
DIRECTORY
Convert USING [CardFromRope, Error],
CountedVM USING [SimpleAllocate, Free, Handle],
FS USING [Open, OpenFile, Close, Error, Read],
IO,
MessageFileFormat USING [Domain, FileHeader, Segment, TextEntry],
NSString USING [nullString, String],
PrincOps USING [wordsPerPage],
PrincOpsUtils USING [LongCopy],
Rope USING [FromRefText, Length, ROPE, ToRefText],
VM USING [PageCount, WordsForPages],
XMessage USING [ClientData, DestroyMsgsProc, ErrorType, Messages, MsgDomains, MsgKey, MsgKeyList, StringArray],
XMessagePrivate USING [FileInfo, FileInfoObject, Object];
XMessageImpl: PROGRAM
IMPORTS Convert, CountedVM, FS, IO, PrincOpsUtils, Rope, VM
EXPORTS XMessage
=
BEGIN OPEN XMessage, MFF: MessageFileFormat;
ROPE: TYPE ~ Rope.ROPE;
STREAM: TYPE ~ IO.STREAM;
initialHeapPages: CARDINAL = 4;
heapIncrement: CARDINAL = 4;
swapUnitSize: CARDINAL = 4;
leftAngleChar: XChar.Character = XCharSet0.Make[lessThan];
rightAngleChar: XChar.Character = XCharSet0.Make[greaterThan];
leftAngleChar: REF TEXT = "<";
rightAngleChar: REF TEXT = ">";
Handle: TYPE = REF Object;
Object: PUBLIC TYPE = XMessagePrivate.Object;
Error: PUBLIC ERROR [type: ErrorType] = CODE;
AllocateMessages: PUBLIC SAFE PROCEDURE [applicationName: NSString.String,
maxMessages: CARDINAL, clientData: ClientData, proc: DestroyMsgsProc]
RETURNS [Handle] = TRUSTED BEGIN
h: REF rawData Object;
h ← NEW[Object.rawData[maxMessages]];
h.proc ← proc; h.clientData ← clientData;
FOR i: CARDINAL IN [0..maxMessages) DO
h.array[i] ← NSString.nullString; ENDLOOP;
RETURN[h];
END;
DestroyMessages: PUBLIC SAFE PROCEDURE [h: Handle] = TRUSTED
{IF h.proc # NIL THEN h.proc[h.clientData];
WITH hh: h SELECT FROM
rawData => {FOR i: CARDINAL IN [0..hh.maxMsgIndex) DO
hh.array[i] ← NIL
ENDLOOP};
file => {};
ENDCASE;
};
Decompose: PUBLIC SAFE PROCEDURE [source: NSString.String] RETURNS [args: REF StringArray] = TRUSTED {RETURN[NIL]}; -- of Decompose
Compose: PUBLIC SAFE PROCEDURE [source: NSString.String, args: REF StringArray]
RETURNS [destination: NSString.String] = TRUSTED {
destination ← ComposeToFormatHandle[source, args]};
ComposeOne: PROCEDURE [source: NSString.String, arg: NSString.String]
RETURNS [destination: NSString.String] = TRUSTED {
args: REF StringArray ← NEW[StringArray[1]];
args.data[0] ← arg;
destination ← ComposeToFormatHandle[source, args]};
ComposeToFormatHandle: PROCEDURE [source: NSString.String, args: REF StringArray] RETURNS [destination: NSString.String] = {
TokenProc: IO.BreakProc = TRUSTED BEGIN
PROC [char: CHAR] RETURNS [CharClass];
IF char = tokenChar THEN RETURN [sepr] ELSE RETURN [other];
END;
tokenChar, c: CHAR; front: ROPE;
argIndex, index, length: CARDINAL ← 0;
sourceRope: ROPE ← Rope.FromRefText[source];
noNumber: BOOLEAN;
inS: STREAMIO.RIS[sourceRope];
outS: STREAMIO.ROS[];
IF Rope.Length[sourceRope] = 0 THEN Error[invalidString];
IF args = NIL THEN Error[notEnoughArguments];
UNTIL IO.EndOf[inS] DO
tokenChar ← '<;
front ← IO.GetTokenRope[inS, TokenProc].token;
[] ← IO.GetChar[inS]; -- Get rid of the <
IO.PutRope[outS, front];
IF ~IO.EndOf[inS] THEN {
tokenChar ← '>;
front ← IO.GetTokenRope[inS, TokenProc].token;
c ← IO.GetChar[inS];
IF c = '>
THEN {
noNumber ← FALSE;
argIndex ← Convert.CardFromRope[front
! Convert.Error => {noNumber ← TRUE; CONTINUE}];
IF noNumber
THEN {IO.PutChar[outS, '<]; IO.PutRope[outS, front]; IO.PutChar[outS, '>]}
ELSE IO.PutText[outS, args.data[argIndex - 1]];
}
ELSE IO.PutRope[outS, front];
};
ENDLOOP;
RETURN[Rope.ToRefText[IO.RopeFromROS[outS]]];
};
Get: PUBLIC SAFE PROCEDURE [h: Handle, msgKey: MsgKey]
RETURNS [msg: NSString.String] = TRUSTED {RETURN GetInternal[h, msgKey]};
GetList: PUBLIC SAFE PROCEDURE [h: Handle, msgKeys: REF MsgKeyList, msgs: REF StringArray] = TRUSTED BEGIN
IF msgKeys = NIL THEN Error[invalidMsgKeyList];
IF msgs = NIL THEN Error[invalidStringArray];
IF msgKeys.length # msgs.length THEN Error[arrayMismatch];
FOR i: CARDINAL IN [0..msgKeys.length) DO
msgs[i] ← GetInternal[h, msgKeys[i]] ENDLOOP
END; -- of GetList
RegisterMessages: PUBLIC SAFE PROCEDURE [h: Handle, messages: REF Messages, stringBodiesAreReal: BOOLEAN] = TRUSTED BEGIN
IF messages = NIL THEN Error[notEnoughArguments];
WITH hh: h SELECT FROM
rawData => {
FOR i: CARDINAL IN [0..messages.length) DO
hh.array[messages[i].msgKey] ← messages[i].msg;
ENDLOOP};
ENDCASE => ERROR Error[invalidMsgKeyList];
END; -- of RegisterMessages
<< This proc is commented out because it depends on the BWS file structure which is not available in services.
MessagesFromFile: PUBLIC SAFE PROCEDURE [
fileName: LONG STRING, clientData: ClientData, proc: DestroyMsgsProc]
RETURNS [msgDomains: XMessage.MsgDomains] = TRUSTED {
rb: XString.ReaderBody ← XString.FromSTRING[fileName];
fileHandle: NSFile.Handle = Catalog.GetFile[name: @rb, readonly: TRUE];
RETURN MessagesFromFileHandle[fileHandle, clientData, proc]};>>
MessagesFromReference: PUBLIC SAFE PROCEDURE [file: ROPE, clientData: ClientData,
proc: DestroyMsgsProc] RETURNS [msgDomains: REF MsgDomains] = TRUSTED {
fileHandle: FS.OpenFile = FS.Open[file ! FS.Error => GOTO Out];
RETURN [MessagesFromFileHandle[fileHandle, clientData, proc]];
EXITS Out => RETURN[NIL]};
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
PRIVATE OPERATIONS :
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
GetInternal: PROCEDURE [h: Handle, msgKey: MsgKey] RETURNS [NSString.String] = BEGIN
WITH hh: h SELECT FROM
rawData => RETURN[hh.array[msgKey]];
file => {
text: LONG POINTER TO MessageFileFormat.TextEntry = @hh.textBase[
hh.domain[hh.messageTable][msgKey]];
rt: REF TEXTNEW[TEXT[text.maxLength]];
PrincOpsUtils.LongCopy[
from: LOOPHOLE[LOOPHOLE[text, LONG POINTER] + SIZE[MessageFileFormat.TextEntry]],
nwords: text.length/2,
to: LOOPHOLE[LOOPHOLE[rt, LONG POINTER] + SIZE[TEXT[0]]]];
RETURN[rt]};
pilotFile => {
text: LONG POINTER TO MessageFileFormat.TextEntry = @hh.textBase[
hh.domain[hh.messageTable][msgKey]];
rt: REF TEXTNEW[TEXT[text.maxLength]];
PrincOpsUtils.LongCopy[
from: LOOPHOLE[LOOPHOLE[text, LONG POINTER] + SIZE[MessageFileFormat.TextEntry]],
nwords: text.length/2,
to: LOOPHOLE[LOOPHOLE[rt, LONG POINTER] + SIZE[TEXT[0]]]];
RETURN[rt]};
ENDCASE => ERROR Error[invalidMsgKeyList];
END;
DomainSeq: TYPE = REF DomainSeqRecord;
DomainSeqRecord: TYPE = RECORD [data: SEQUENCE length: CARDINAL OF XMessage.MsgDomain];
MsgDomain: TYPE = RECORD [applicationName: NSString.String, handle: Handle];
FreeMsgDomainsStorage: PUBLIC SAFE PROCEDURE [msgDomains: REF XMessage.MsgDomains] = TRUSTED {};
MessagesFromFileHandle: PROCEDURE [file: FS.OpenFile, clientData: ClientData,
proc: DestroyMsgsProc] RETURNS [msgDomains: REF MsgDomains] = TRUSTED {
header: MFF.FileHeader;
cvmH: CountedVM.Handle ← CountedVM.SimpleAllocate[VM.WordsForPages[1]];
nSegments, nPages: CARDINAL;
fileInfo: XMessagePrivate.FileInfo;
domainSeq: REF MsgDomains;
domainSeq: DomainSeq;
header ← LOOPHOLE[cvmH.pointer];
FS.Read[file: file, from: 0, nPages: 1, to: header];
header ← NSSegment.Map[
origin: [count: 1, base: 0, file: file], access: NSFile.readAccess].pointer;
nSegments ← header.nSegments;
nPages ← PagesForHeader[nSegments];
IF nPages # 1 THEN {
CountedVM.Free[cvmH];
cvmH ← CountedVM.SimpleAllocate[VM.WordsForPages[nPages]];
[] ← Space.Unmap[header];
header ← LOOPHOLE[cvmH.pointer];
FS.Read[file: file, from: 0, nPages: nPages, to: header];
header ← NSSegment.Map[
origin: [count: nPages, base: 0, file: file],
access: NSFile.readAccess].pointer
};
fileInfo ← NEW[XMessagePrivate.FileInfoObject ← [stuff: cvmH, nDomains: nSegments]];
domainSeq ← NEW[MsgDomains[nSegments]];
FOR i: CARDINAL IN [0..nSegments) DO
pageCnt: VM.PageCount ← header.segments[i].count;
cvmHandel: CountedVM.Handle ← CountedVM.SimpleAllocate[VM.WordsForPages[pageCnt]];
domain: MessageFileFormat.Domain ← -- LONG BASE POINTER TO DomainHeader --
LOOPHOLE[cvmHandel.pointer];
domain: MessageFileFormat.Domain ← NSSegment.Map[
origin: [count: header.segments[i].count, base: header.segments[i].base, file: file],
access: NSFile.readAccess].pointer;
h: REF Object.file ← NEW[Object.file ← [
proc: proc, clientData: clientData,
body: file[fileInfo: fileInfo, cvmH: cvmHandel,
domain: domain, textBase: @domain[domain.textTable],
messageTable: domain.messageTable]]];
text: LONG POINTER TO MessageFileFormat.TextEntry = @h.textBase[domain.domainName];
nsString: REF TEXTNEW[TEXT[text.length]];
FS.Read[file: file, from: header.segments[i].base, nPages: header.segments[i].count,
to: domain];
FOR j: CARDINAL IN [0 .. text.length) DO
nsString[i] ← LOOPHOLE[text.bytes[i]];
ENDLOOP;
nsString.length ← text.length;
domainSeq.data[i] ← [handle: h, applicationName: nsString];
ENDLOOP;
header ← Space.Unmap[header];
FS.Close[file];
RETURN[DESCRIPTOR[domainSeq, nSegments]]};
RETURN[domainSeq]};
PagesForHeader: PROCEDURE [nSegments: CARDINAL] RETURNS [CARDINAL] = INLINE {
wpp: CARDINAL = PrincOps.wordsPerPage;
RETURN[(MFF.FileHeader.SIZE + nSegments * MFF.Segment.SIZE + wpp - 1) / wpp]};
END. -- of MessageImpl
30-May-84 16:24:48 - Saund - Reduced initial Heap size from 64 to 4 pages.
7-Jun-84 15:15:52 - Sandman - Remove unused stuff.
23-Jul-84 11:42:42 - Sandman - Make Compose not alter source arg and use 1 relative indexing on args.
25-Jan-85 15:04:08 - Sandman - Implement message file stuff.
30-May-85 16:23:01 - McManis - Comment out MessageFromFile because services cannot use it and there is a conflict of Catalog with NSFile.
12-Sep-85 14:46:09 - McManis - Added check for NIL proc in DestroyMessages.
14-Oct-85 15:30:41 - McManis - Added pilotFile to SELECT in Get to allow messages to be retrieved from pilotFiles.