XFormatImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last Edit :26-Jun-84 18:39:36, KNguyen
Tim Diebert: December 9, 1986 2:08:29 pm PST
DIRECTORY
Basics USING [BITOR, BITSHIFT, LowHalf, LongDivMod, bitsPerWord, UnsafeBlock],
BasicTime USING [GMT, Now, nullGMT],
Convert USING [RopeFromTime],
IO USING [STREAM, PutBlock],
NSString USING [Character, String],
RefText USING [AppendChar, AppendRope, ObtainScratch, ReleaseScratch, TrustTextAsRope],
Rope USING [Substr, Text, ToRefText],
TTY USING [Handle, Narrow, PutText],
XFormat USING [DateFormat, ErrorCode, FormatProc, Handle, NetFormat, NumberFormat, Object, OctalFormat],
XNS;
XFormatImpl: CEDAR MONITOR
IMPORTS Basics, BasicTime, Convert, IO, RefText, Rope, TTY
EXPORTS XFormat =
BEGIN OPEN XFormat, nss: NSString;
defaultObject: Object ← [proc: NopProc, stream: NIL, data: NIL];
SetDefaultOutputSink: PUBLIC ENTRY PROC [new: Object] RETURNS [old: Object] = {
old ← defaultObject;
defaultObject ← new};
GetDefaults: ENTRY PROCEDURE RETURNS [Handle] =
{RETURN[NEW[Object ← defaultObject]]};
NopProc: FormatProc = {};
Public procedures
Blanks: PUBLIC PROCEDURE [h: Handle, n: CARDINAL ← 1] = {
blank: REF TEXT ← " "; -- 16 blanks
IF h = NIL THEN h ← GetDefaults[];
THROUGH [0..n/blank.length) DO h.proc[blank, h] ENDLOOP;
blank ← Rope.ToRefText[Rope.Substr[RefText.TrustTextAsRope[blank], 0, n MOD blank.length]];
h.proc[blank, h]};
Block: PUBLIC PROCEDURE [h: Handle, block: Basics.UnsafeBlock] = {
rt: REF TEXTNEW[TEXT[block.count]];
FOR i: INT IN [block.startIndex .. block.startIndex + block.count) DO
bp: Basics.BytePair ← IF
rt[i - block.startIndex] ←
rb: XS.ReaderBody ← XS.FromBlock[block];
IF h = NIL THEN h ← GetDefaults[];
h.proc[@rb, h];
};
Char: PUBLIC PROCEDURE [h: Handle, char: CHAR] = {
rt: REF TEXT ← RefText.ObtainScratch[1];
rt[0] ← char; rt.length ← 1;
IF h = NIL THEN h ← GetDefaults[];
h.proc[rt, h ! UNWIND => RefText.ReleaseScratch[rt]];
RefText.ReleaseScratch[rt];
};
CR: PUBLIC PROCEDURE [h: Handle, n: CARDINAL] = {
rt: REF TEXT ← RefText.ObtainScratch[1];
rt[0] ← '\n; rt.length ← 1;
IF h = NIL THEN h ← GetDefaults[];
THROUGH [0..n) DO h.proc[rt, h ! UNWIND => RefText.ReleaseScratch[rt]] ENDLOOP;
RefText.ReleaseScratch[rt];
};
Date: PUBLIC PROCEDURE [h: Handle, time: BasicTime.GMT ← BasicTime.nullGMT,
format: DateFormat ← dateAndTime] = {
rope: Rope.Text;
IF time = BasicTime.nullGMT THEN time ← BasicTime.Now[];
SELECT format FROM
dateOnly => {rope ← Convert.RopeFromTime[from: time, start: years, end: days, includeDayOfWeek: FALSE]};
timeOnly => {rope ← Convert.RopeFromTime[from: time, start: hours, end: seconds, useAMPM: FALSE, includeZone: FALSE]};
dateAndTime => {rope ← Convert.RopeFromTime[from: time, start: years, end: seconds, useAMPM: FALSE, includeZone: FALSE]};
ENDCASE => ERROR;
IF h = NIL THEN h ← GetDefaults[];
h.proc[Rope.ToRefText[rope], h];
};
Line: PUBLIC PROCEDURE [h: Handle, r: REF TEXT, n: CARDINAL] = {
IF h = NIL THEN h ← GetDefaults[];
h.proc[r, h]; CR[h, n]};
maxDigitLength: CARDINAL = 34;
NSString: PUBLIC PROCEDURE [h: Handle, s: nss.String] = {
IF h = NIL THEN h ← GetDefaults[];
h.proc[s, h]};
NSChar: PUBLIC PROCEDURE [h: Handle, char: nss.Character] =
{Char[h, LOOPHOLE[char.code]]};
NSLine: PUBLIC PROCEDURE [h: Handle, s: nss.String, n: CARDINAL ← 1] = {
NSString[h, s]; CR[h, n]};
Number: PUBLIC PROCEDURE [h: Handle, n: LONG UNSPECIFIED, format: NumberFormat] =
BEGIN
s: REF TEXT ← RefText.ObtainScratch[maxDigitLength];
neg: BOOLEAN;
NextDigit: PROCEDURE [n: LONG CARDINAL] = {
lr: LONG CARDINAL;
r: CARDINAL;
[n, lr] ← Basics.LongDivMod[n, format.base];
IF n # 0 THEN NextDigit[n];
IF (r ← Basics.LowHalf[lr]) > 9 THEN r ← r + 'A - '0 - 10;
s ← RefText.AppendChar[s, LOOPHOLE[(r + '0).ORD]];
};
IF h = NIL THEN h ← GetDefaults[];
IF LOOPHOLE[n, LONG INTEGER] < 0 AND format.signed THEN {
n ← -LOOPHOLE[n, LONG INTEGER]; neg ← TRUE}
ELSE neg ← FALSE;
NextDigit[n];
FormatNumber[w: s, format: format, neg: neg, h: h];
RefText.ReleaseScratch[s];
END;
Octal: PUBLIC PROCEDURE [h: Handle, n: LONG CARDINAL] = {
Number[h, n, XFormat.OctalFormat];
IF n > 7 THEN Char[h, 'B]};
Reader: PUBLIC PROC [h: Handle, r: REF TEXT] = {
IF h = NIL THEN h ← GetDefaults[];
h.proc[r, h]};
ReaderBody: PUBLIC PROC [h: Handle, rb: REF TEXT] = {
IF h = NIL THEN h ← GetDefaults[];
h.proc[rb, h]};
String: PUBLIC PROC [h: Handle, s: REF TEXT] = {
IF h = NIL THEN h ← GetDefaults[];
h.proc[s, h]};
StreamProc: PUBLIC FormatProc = {
FormatProc: TYPE = PROCEDURE [r: NSString.String, h: Handle];
stream: IO.STREAM;
IF (stream ← NARROW[h.stream]) = NIL THEN ERROR Error[nilData];
IO.PutBlock[stream, r];
};
TTYProc: PUBLIC FormatProc = {
FormatProc: TYPE = PROCEDURE [r: NSString.String, h: Handle];
tty: TTY.Handle;
IF (tty ← TTY.Narrow[h.stream]) = NIL THEN ERROR Error[nilData];
TTY.PutText[tty, r];
};
WriterProc: PUBLIC FormatProc = {
IF h.data = NIL THEN ERROR Error[nilData];
XS.AppendReader[to: h.data, from: r];
h.context ← XString.WriterInfo[h.data].endContext;
};
StreamObject: PUBLIC PROC [sH: IO.STREAM] RETURNS [Handle] = {
h: Handle ← NEW [Object];
IF sH = NIL THEN ERROR Error[nilData];
h.proc ← StreamProc; h.stream ← sH;
RETURN[h]};
TTYObject: PUBLIC PROC [h: TTY.Handle] RETURNS [Handle] = {
handle: Handle ← NEW [Object];
IF h = NIL THEN ERROR Error[nilData];
handle.proc ← TTYProc; handle.stream ← h;
RETURN[handle]};
WriterObject: PUBLIC PROC [w: XString.Writer] RETURNS [Handle] = {
IF w = NIL THEN ERROR Error[nilData];
RETURN[[WriterProc, XString.vanillaContext, w]];
RETURN[NIL];
};
Private procedures
FormatNumber: PROCEDURE [w: REF TEXT, format: NumberFormat, neg: BOOLEAN, h: Handle] = BEGIN
l: CARDINAL;
fill: REF TEXT ← (IF format.zerofill THEN "00000000" ELSE " ");
l ← w.length + neg.ORD;
IF l < format.columns
THEN BEGIN
fillChars: CARDINAL ← format.columns - l;
IF neg AND format.zerofill THEN Char[h, '-];
THROUGH [0..fillChars/8) DO h.proc[fill, h]; ENDLOOP;
THROUGH [0..fillChars MOD 8) DO Char[h, fill[0]]; ENDLOOP;
IF neg AND ~format.zerofill THEN Char[h, '-];
END
ELSE IF neg THEN Char[h, '-];
h.proc[w, h];
END;
Network related things
Error: PUBLIC ERROR [code: ErrorCode] = CODE;
bitsPerPSCharacter: CARDINAL = 2; -- I don't want to think about the dashes
bitsPerOctalCharacter: CARDINAL = 3;
bitsPerHexCharacter: CARDINAL = 4;
minBitsPerCharacter: CARDINAL = MIN[
bitsPerPSCharacter, bitsPerOctalCharacter, bitsPerHexCharacter];
maxCharsInHostNumber: CARDINAL =
SIZE[XNS.Host]*Basics.bitsPerWord/minBitsPerCharacter + 1;
maxCharsInNetworkNumber: CARDINAL =
SIZE[XNS.Net]*Basics.bitsPerWord/minBitsPerCharacter + 1;
maxCharsInSocketNumber: CARDINAL =
SIZE[XNS.Socket]*Basics.bitsPerWord/minBitsPerCharacter + 1;
maxCharsInNetworkAddress: CARDINAL =
maxCharsInNetworkNumber + 1 + maxCharsInHostNumber + 1 + maxCharsInSocketNumber;
maxDigits: CARDINAL = MAX[
maxCharsInHostNumber, maxCharsInNetworkNumber, maxCharsInSocketNumber];
Digits: TYPE = ARRAY [0..maxDigits) OF CARDINAL;
Words: TYPE = POINTER TO ARRAY [0..3) OF WORD;
HostNumber: PUBLIC PROCEDURE [h: Handle, hostNumber: XNS.Host, format: NetFormat] = BEGIN
temp: REF TEXTNEW[TEXT[maxCharsInHostNumber]];
host: XNS.Host ← hostNumber;
words: Words;
IF h = NIL THEN h ← GetDefaults[];
TRUSTED { words ← LOOPHOLE[@host]; };
IF hostNumber = XNS.broadcastHost
THEN temp ← RefText.AppendChar[temp, '*]
ELSE temp ← AppendField[temp, words, SIZE[XNS.Host], format];
h.proc[temp, h];
END;
NetworkNumber: PUBLIC PROCEDURE [h: Handle, networkNumber: XNS.Net,
format: NetFormat] = BEGIN
temp: REF TEXTNEW[TEXT[maxCharsInHostNumber]];
net: XNS.Net ← networkNumber;
words: Words;
IF h = NIL THEN h ← GetDefaults[];
TRUSTED { words ← LOOPHOLE[@net]; };
temp ← AppendField[temp, words, SIZE[XNS.Net], format];
h.proc[temp, h];
END;
SocketNumber: PUBLIC PROCEDURE [h: Handle, socketNumber: XNS.Socket, format: NetFormat] = TRUSTED BEGIN
temp: REF TEXTNEW[TEXT[maxCharsInHostNumber]];
socket: XNS.Socket ← socketNumber;
words: Words;
IF h = NIL THEN h ← GetDefaults[];
TRUSTED { words ← LOOPHOLE[@socket]; };
temp ← AppendField[temp, words, SIZE[XNS.Socket], format];
h.proc[temp, h];
END;
NetworkAddress: PUBLIC PROCEDURE [h: Handle, networkAddress: XNS.Address, format: NetFormat] = TRUSTED BEGIN
temp: REF TEXTNEW[TEXT[maxCharsInHostNumber]];
words: Words;
IF h = NIL THEN h ← GetDefaults[];
TRUSTED { words ← LOOPHOLE[@networkAddress.net]; };
temp ← AppendField[temp, words, SIZE[XNS.Address], format];
temp ← RefText.AppendChar[temp, '.];
TRUSTED { words ← LOOPHOLE[@networkAddress.host]; };
IF networkAddress.host = XNS.broadcastHost
THEN temp ← RefText.AppendChar[temp, '*]
ELSE temp ← AppendField[temp, words, SIZE[XNS.Host], format];
temp ← RefText.AppendChar[temp, '.];
TRUSTED { words ← LOOPHOLE[@networkAddress.socket]; };
temp ← AppendField[temp, words, SIZE[XNS.Socket], format];
h.proc[temp, h];
END;
This will break on a 32 bit machine.
AppendField: PROC [text: REF TEXT, words: Words, count: NAT, format: NetFormat] RETURNS [REF TEXT] = {
digits: Digits;
base: NAT;
SELECT format FROM
octal => base ← 8;
productSoftware => base ← 10;
hex => base ← 16;
ENDCASE => ERROR Error[invalidFormat];
TRUSTED {
ConvertToDigits[words, count, base, @digits];
text ← AppendDigits[text, @digits, base = 10]; };
IF base = 16 THEN text ← RefText.AppendChar[text, 'H];
RETURN[text];
};
ConvertToDigits: PROC [words: Words, size, base: NAT, digits: POINTER TO Digits] = TRUSTED {
digits^ ← ALL[0];
FOR i: NAT IN [0..size*Basics.bitsPerWord) DO
bit: CARDINAL ← ShiftFieldLeft[words, size, 1];
FOR j: NAT DECREASING IN [0..maxDigits) DO
digits[j] ← digits[j]*2 + bit;
IF digits[j] >= base THEN { digits[j] ← digits[j] - base; bit ← 1; }
ELSE bit ← 0;
ENDLOOP;
ENDLOOP;
};
ShiftFieldLeft: PROC [words: Words, count: NAT, shift: INTEGER]
RETURNS [left: NAT] = TRUSTED {
right: WORD ← 0;
FOR i: NAT DECREASING IN [0..count) DO
left ← Basics.BITSHIFT[words[i], shift - 16];
words[i] ← Basics.BITOR[Basics.BITSHIFT[words[i], shift], right];
right ← left;
ENDLOOP;
};
AppendDigits: PROC [text: REF TEXT, digits: POINTER TO Digits, dashes: BOOL] RETURNS [REF TEXT] = TRUSTED {
something: BOOLFALSE;
FOR i: NAT IN [0..maxDigits) DO
v: NAT ← digits[i];
IF dashes AND something AND (maxDigits - i) MOD 3 = 0 THEN
text ← RefText.AppendChar[text, '-];
IF v # 0 AND ~something THEN {
IF dashes THEN {
SELECT maxDigits - i FROM
1 => text ← RefText.AppendRope[text, "0-00"];
2 => text ← RefText.AppendRope[text, "0-0"];
3 => text ← RefText.AppendRope[text, "0-"];
ENDCASE => NULL; };
IF v > 9 THEN text ← RefText.AppendChar[text, '0]; -- Leading digit for Hex case
something ← TRUE; };
IF something THEN {
c: CHARIF v > 9 THEN v - 10 + 'A ELSE v + '0;
text ← RefText.AppendChar[text, c]; };
ENDLOOP;
IF ~something THEN text ← RefText.AppendChar[text, '0];
RETURN[text];
};
Mainline Code
END.