XNSAddressParsingImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Demers, October 16, 1986 10:30:18 am PDT
DIRECTORY
CommDriver USING [GetNetworkChain, Network],
Process USING [Pause, SecondsToTicks, Ticks],
RefText USING [ObtainScratch, ReleaseScratch],
Rope USING [Cat, Concat, Equal, Fetch, FromRefText, Index, Length, ROPE, Substr],
XNS USING [Address, broadcastHost, broadcastNet, GetThisHost, Host, Net, unknownAddress, unknownNet, unknownSocket, Socket],
XNSAddressParsing USING[ErrorCode, Format];
XNSAddressParsingImpl: CEDAR PROGRAM
IMPORTS CommDriver, Process, RefText, Rope, XNS
EXPORTS XNSAddressParsing
~ {
ROPE: TYPE ~ Rope.ROPE;
Name: TYPE ~ Rope.ROPE;
Address: TYPE ~ XNS.Address;
Host: TYPE ~ XNS.Host;
Net: TYPE ~ XNS.Net;
Socket: TYPE ~ XNS.Socket;
ErrorCode: TYPE ~ XNSAddressParsing.ErrorCode;
Format: TYPE ~ XNSAddressParsing.Format;
BYTE: TYPE ~ [0..100H);
FieldType: TYPE = { default, broadcast, octal, decimal, hex, notANumber };
The order in which these possibilities are specified is important: receiving more information (i.e. looking at more characters of the field) may cause a computed FieldType to increase (e.g. octal->decimal->hex) but cannot cause it to decrease. See GetType[...].
Local Address
MyAddress: PUBLIC PROC RETURNS [Address] ~ {
Return best address for this machine.
Socket will be unknownSocket.
RETURN [ [net~DefaultNet[], host~XNS.GetThisHost[], socket~XNS.unknownSocket] ] };
myRopes: ARRAY Format OF ROPE;
MyRope: PUBLIC PROC [format: Format] RETURNS [ROPE] ~ {
IF myRopes[format] = NIL THEN myRopes[format] ← RopeFromAddress[MyAddress[], format];
RETURN [myRopes[format]] };
Name/Address translation
AddressFromRope: PUBLIC PROC [rope: ROPE, default: Socket] RETURNS [Address] ~ {
nameLen: INT ~ Rope.Length[rope];
answer: Address ← XNS.unknownAddress;
netPart, hostPart, socketPart: ROPENIL;
netType, hostType, socketType: FieldType ← default;
Split name into pieces.
BEGIN
firstDot, secondDot: INT;
firstDot ← Rope.Index[s1~rope, s2~"."];
IF firstDot = nameLen THEN
firstDot ← Rope.Index[s1~rope, s2~"#"];
IF firstDot = nameLen THEN {
One-part name.
hostPart ← rope; hostType ← GetType[hostPart];
GOTO DoneSplit };
secondDot ← Rope.Index[s1~rope, pos1~firstDot+1, s2~"."];
IF secondDot = nameLen THEN
secondDot ← Rope.Index[s1~rope, pos1~firstDot+1, s2~"#"];
IF secondDot = nameLen THEN {
Two-part name.
part1: ROPE ~ Rope.Substr[base~rope, len~firstDot];
part2: ROPE ~ Rope.Substr[base~rope, start~firstDot+1];
type1: FieldType ~ GetType[part1];
type2: FieldType ~ GetType[part2];
SELECT TRUE FROM
(type1 = notANumber) AND (type2 # notANumber) => {
hostPart ← part1; hostType ← type1;
socketPart ← part2; socketType ← type2 };
(type1 # notANumber) AND (type2 = notANumber) => {
netPart ← part1; netType ← type1;
hostPart ← part2; hostType ← type2 };
ENDCASE => ERROR Error[code~syntax, text~"ambiguous address"];
GOTO DoneSplit };
{
Three-part name.
netPart ← Rope.Substr[base~rope, len~firstDot];
netType ← GetType[netPart];
hostPart ← Rope.Substr[base~rope, start~firstDot+1, len~secondDot-firstDot-1];
hostType ← GetType[hostPart];
socketPart ← Rope.Substr[base~rope, start~secondDot+1];
socketType ← GetType[socketPart];
GOTO DoneSplit };
EXITS
DoneSplit => NULL;
END;
SELECT hostType FROM
default => ERROR Error[code~syntax, text~"cannot default host"];
broadcast => answer.host ← XNS.broadcastHost;
notANumber => {
IF Rope.Equal[hostPart, "ME"]
THEN { answer ← MyAddress[] }
ELSE { ERROR Error[code~syntax, text~"host syntax"] };
};
ENDCASE => answer.host ← CvtHost[hostPart, hostType];
SELECT netType FROM
default => IF answer.net = XNS.unknownNet THEN answer.net ← DefaultNet[];
broadcast => answer.net ← XNS.broadcastNet;
notANumber => ERROR Error[code~syntax, text~"net syntax"];
ENDCASE => answer.net ← CvtNet[netPart, netType];
SELECT socketType FROM
default => IF answer.socket = XNS.unknownSocket THEN answer.socket ← default;
broadcast => answer.socket ← XNS.unknownSocket;
notANumber => ERROR Error[code~syntax, text~"socket syntax"];
ENDCASE => answer.socket ← CvtSocket[socketPart, socketType];
RETURN [answer];
};
RopeFromAddress: PUBLIC PROC [address: Address, format: Format ← octal] RETURNS [rope: ROPE] ~ {
Returns a ROPE of the form "1.12.123".
An unknownSocket is expanded as the null string, e.g. "1.12." (no trailing 0).
rope ← Fmt[ [0, 0, address.net.a, address.net.b, address.net.c, address.net.d], format];
rope ← Rope.Cat[rope, ".",
Fmt[ [address.host.a, address.host.b, address.host.c, address.host.d, address.host.e, address.host.f], format],
"." ];
IF address.socket # XNS.unknownSocket THEN
rope ← Rope.Concat[rope, Fmt[ [0, 0, 0, 0, address.socket.a, address.socket.b], format] ];
};
NetFromRope: PUBLIC PROC [rope: ROPE] RETURNS [Net] ~ {
type: FieldType ~ GetType[rope];
SELECT type FROM
broadcast => RETURN [XNS.broadcastNet];
octal, decimal, hex => RETURN [CvtNet[rope, type]];
ENDCASE => ERROR Error[syntax, "net syntax"];
};
HostFromRope: PUBLIC PROC [rope: ROPE] RETURNS [Host] ~ {
type: FieldType ~ GetType[rope];
SELECT type FROM
broadcast => RETURN [XNS.broadcastHost];
octal, decimal, hex => RETURN [CvtHost[rope, type]];
notANumber => ERROR Error[syntax, "host syntax"];
ENDCASE => ERROR Error[syntax, "host syntax"];
};
SocketFromRope: PUBLIC PROC [rope: ROPE] RETURNS [Socket] ~ {
type: FieldType ~ GetType[rope];
SELECT type FROM
octal, decimal, hex => RETURN [CvtSocket[rope, type]];
ENDCASE => ERROR Error[syntax, "socket syntax"];
};
RopeFromNet: PUBLIC PROC [net: Net, format: Format ← octal] RETURNS [ROPE] ~ {
RETURN[ Fmt[ [0, 0, net.a, net.b, net.c, net.d], format ] ];
};
RopeFromHost: PUBLIC PROC [host: Host, format: Format ← octal] RETURNS [ROPE] ~ {
RETURN[ Fmt[ [host.a, host.b, host.c, host.d, host.e, host.f], format ] ];
};
RopeFromSocket: PUBLIC PROC [socket: Socket, format: Format ← octal] RETURNS [ROPE] ~ {
RETURN[ Fmt[ [0, 0, 0, 0, socket.a, socket.b], format ] ];
};
Private Procedures
DefaultNet: PROC RETURNS [default: Net ← XNS.unknownNet] ~ {
We define the default net to be the one at the head of the network chain. It may not be a good idea to block the caller of DefaultNet for so long, but it shouldn't happen (in theory).
netH: CommDriver.Network;
oneSecond: Process.Ticks ~ Process.SecondsToTicks[1];
FOR i: NAT IN [1..5] DO
IF ((netH ← CommDriver.GetNetworkChain[]) # NIL)
AND ((default ← netH.xns.net) # XNS.unknownNet)
THEN EXIT;
Process.Pause[oneSecond];
ENDLOOP;
};
bnSize: NAT ~ 6;
BigNum: TYPE ~ ARRAY[0..bnSize) OF CARDINAL;
A BigNum a represents sum over i ( a[i] * 256**(5-i) ); that is, it's a base 256 number with the most significant part in a[0] and the least significant in a[5]. WARNING: Call a BigNum "normalized" if all the elements are in the range [0..256). The BigNum multiplication routine, MulAdd, always produces normalized results, but the division routine, DivRem, while mathematically correct, would leave its result unnormalized except for the explicit renormalization code. For legal network numbers, it can be proved that BigNum elements won't exceed LAST[LONG CARDINAL], so to speed thing up you could change the declaration of BigNum and delete the renormalization code. Yecch.
CvtHost: PROC [rope: ROPE, type: FieldType] RETURNS [Host] ~ {
n: BigNum ~ Cvt[rope, type];
RETURN [ [a~n[0], b~n[1], c~n[2], d~n[3], e~n[4], f~n[5]] ] };
CvtNet: PROC [rope: ROPE, type: FieldType] RETURNS [Net] ~ {
n: BigNum ~ Cvt[rope, type];
RETURN [ [a~n[2], b~n[3], c~n[4], d~n[5]] ] };
CvtSocket: PROC [rope: ROPE, type: FieldType] RETURNS [Socket] ~ {
n: BigNum ~ Cvt[rope, type];
RETURN [ [a~n[4], b~n[5]] ] };
Cvt: PROC [rope: ROPE, type: FieldType] RETURNS [BigNum] ~ {
n: BigNum ← ALL [0];
base: CARDINAL;
len: CARDINAL;
c: CHAR;
MulAdd: PROC [increment: CARDINAL] ~ {
FOR i: CARDINAL DECREASING IN [0..bnSize) DO
temp: CARDINAL ← n[i] * base + increment;
n[i] ← temp MOD 100H;
increment ← temp / 100H;
ENDLOOP;
};
base ← SELECT type FROM octal => 8, decimal => 10, ENDCASE => 16;
IF (len ← Rope.Length[rope]) = 0 THEN ERROR;
c ← Rope.Fetch[rope, len-1];
SELECT TRUE FROM
(type = octal) AND ((c = 'B) OR (c = 'b)) => len ← len - 1;
(type = hex) AND ((c = 'H) OR (c = 'h)) => len ← len - 1;
ENDCASE => NULL;
FOR i: CARDINAL IN [0..len) DO
SELECT (c ← Rope.Fetch[rope,i]) FROM
IN ['0..'9] => MulAdd[c - '0];
IN ['A..'F] => MulAdd[(c - 'A) + 10];
IN ['a..'f] => MulAdd[(c - 'a) + 10];
ENDCASE => NULL;
ENDLOOP;
RETURN [n];
};
GetType: PROC [rope: ROPE] RETURNS [type: FieldType] ~ {
limit: CARDINAL;
IF (limit ← Rope.Length[rope]) = 0 THEN RETURN [default];
limit ← limit - 1;
IF Rope.Equal[rope, "*"] THEN RETURN [broadcast];
type ← (SELECT Rope.Fetch[rope, 0] FROM
IN ['0 .. '7] => octal,
IN ['8 .. '9] => decimal,
ENDCASE => notANumber);
FOR i: CARDINAL IN [1 .. limit) WHILE type < notANumber DO
SELECT Rope.Fetch[rope, i] FROM
IN ['0 .. '7] => type ← MAX[type, octal];
IN ['8 .. '9] => type ← MAX[type, decimal];
IN ['A .. 'F], IN ['a .. 'f] => type ← MAX[type, hex];
'- => type ← (IF type <= decimal THEN decimal ELSE notANumber);
ENDCASE => type ← notANumber;
ENDLOOP;
IF limit > 0 THEN SELECT Rope.Fetch[rope, limit] FROM
IN ['0 .. '9], 'D, 'd => type ← MAX[type, decimal];
'B, 'b => type ← MAX[type, octal];
IN ['A .. 'F], IN ['a .. 'f], 'H, 'h => type ← MAX[type, hex];
ENDCASE => type ← notANumber;
};
maxDigits: NAT ~ 24;
repChar: ARRAY [0 .. 16) OF CHAR ~ ['0, '1, '2, '3, '4, '5, '6, '7, '8, '9, 'A, 'B, 'C, 'D, 'E, 'F];
Fmt: PROC [n: BigNum, type: Format] RETURNS [rope: ROPE] ~ {
text: REF TEXT;
base, rem, i: CARDINAL;
isZero: BOOL;
DivRem: PROC ~ {
[n, rem, isZero] ← [n/base, n MOD base, (n/base = 0)]
temp, carry: CARDINAL;
rem ← 0; isZero ← TRUE;
FOR j: CARDINAL IN [0 .. bnSize) DO
temp n[j] + rem*0100H;
IF (n[j] ← temp / base) # 0 THEN isZero ← FALSE;
rem ← temp MOD base;
ENDLOOP;
n ← renormalize[n]:
carry ← 0;
FOR j: CARDINAL DECREASING IN [0 .. bnSize) DO
temp ← n[j] + carry;
n[j] ← temp MOD 0100H;
carry ← temp / 0100H;
ENDLOOP;
};
text ← RefText.ObtainScratch[maxDigits];
text.length ← text.maxLength;
i ← text.length;
SELECT type FROM
productSoftware => {
untilDash: NAT ← 3;
nDashes: NAT ← 0;
base ← 10;
isZero ← FALSE;
WHILE (NOT isZero) OR (nDashes = 0) DO
DivRem[]; -- [n, rem, isZero] ← [n/base, n MOD base, (n/base = 0)]
IF untilDash = 0 THEN {
text[i ← i - 1] ← '-;
untilDash ← 3;
nDashes ← nDashes + 1 };
text[i ← i - 1] ← repChar[rem];
untilDash ← untilDash - 1;
ENDLOOP;
};
octal => {
base ← 8;
text[i ← i - 1] ← 'B;
isZero ← FALSE;
WHILE NOT isZero DO
DivRem[]; -- [n, rem, isZero] ← [n/base, n MOD base, (n/base = 0)]
text[i ← i - 1] ← repChar[rem];
ENDLOOP;
};
hex => {
base ← 16;
text[i ← i - 1] ← 'H;
isZero ← FALSE;
WHILE (NOT isZero) OR (rem >= 10) DO
DivRem[]; -- [n, rem, isZero] ← [n/base, n MOD base, (n/base = 0)]
text[i ← i - 1] ← repChar[rem];
ENDLOOP;
};
ENDCASE => ERROR;
rope ← Rope.Substr[base~Rope.FromRefText[text], start~i];
RefText.ReleaseScratch[text];
};
Errors
Error: PUBLIC ERROR [code: ErrorCode, text: ROPE] ~ CODE;
}.