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: ROPE ← NIL;
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 };
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];
};
}.