XNSNameImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Demers, June 6, 1986 0:08:49 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],
XNSName USING[ErrorCode, Format];
XNSNameImpl: CEDAR PROGRAM
IMPORTS CommDriver, Process, RefText, Rope, XNS
EXPORTS XNSName
~ {
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 ~ XNSName.ErrorCode;
Format: TYPE ~ XNSName.Format;
BYTE: TYPE ~ [0..100H);
FieldType: TYPE = { default, broadcast, octal, decimal, hex, clearinghouse };
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 name
MyAddress: PUBLIC PROC RETURNS [Address] ~ {
Return best address for this machine.
Socket will be unknownSocket.
RETURN [ [net~DefaultNet[], host~XNS.GetThisHost[], socket~XNS.unknownSocket] ] };
MyName: PUBLIC PROC RETURNS [Name] ~ {
Tries to get a human-readable name for the local machine, e.g "Shark".
This is hard to do in the XNS world, so for now it just calls MyRope.
RETURN [RopeFromAddress[MyAddress[]]] };
MyRope: PUBLIC PROC [format: Format ← octal] RETURNS [ROPE] ~ {
Returns a ROPE of the form "3.313.".
There are no possible errors. NIL might happen if there are no drivers active.
THIS IS PROBABLY BOGUS!
RETURN [RopeFromAddress[MyAddress[], format]] };
Name/Address translation
The implementation might use a local cache, so all those sorts of problems can happen. The servers already have a cache, and the Dicentras sometimes don't notice updates as fast as they should, so local caching shouldn't make things much more complicated.
AddressFromRope: PUBLIC PROC [rope: ROPE, default: Socket ← XNS.unknownSocket] RETURNS [Address] ~ { RETURN [AddressFromName[name~rope, default~default]] };
AddressFromName: PUBLIC PROC [name: Name, default: Socket ← XNS.unknownSocket] RETURNS [Address] ~ {
nameLen: INT ~ Rope.Length[name];
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~name, s2~"."];
IF firstDot = nameLen THEN
firstDot ← Rope.Index[s1~name, s2~"#"];
IF firstDot = nameLen THEN {
One-part name.
hostPart ← name; hostType ← GetType[hostPart];
GOTO DoneSplit };
secondDot ← Rope.Index[s1~name, pos1~firstDot+1, s2~"."];
IF secondDot = nameLen THEN
secondDot ← Rope.Index[s1~name, pos1~firstDot+1, s2~"#"];
IF secondDot = nameLen THEN {
Two-part name.
part1: ROPE ~ Rope.Substr[base~name, len~firstDot];
part2: ROPE ~ Rope.Substr[base~name, start~firstDot+1];
type1: FieldType ~ GetType[part1];
type2: FieldType ~ GetType[part2];
SELECT TRUE FROM
(type1 = clearinghouse) AND (type2 # clearinghouse) => {
hostPart ← part1; hostType ← type1;
socketPart ← part2; socketType ← type2 };
(type1 # clearinghouse) AND (type2 = clearinghouse) => {
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~name, len~firstDot];
netType ← GetType[netPart];
hostPart ← Rope.Substr[base~name, start~firstDot+1, len~secondDot-firstDot-1];
hostType ← GetType[hostPart];
socketPart ← Rope.Substr[base~name, start~secondDot+1];
socketType ← GetType[socketPart];
GOTO DoneSplit };
EXITS
DoneSplit => NULL;
END;
IF netType = clearinghouse THEN
ERROR Error[code~syntax, text~"net syntax"];
IF socketType = clearinghouse THEN
ERROR Error[code~syntax, text~"socket syntax"];
SELECT hostType FROM
default => ERROR Error[code~syntax, text~"cannot default host"];
broadcast => answer.host ← XNS.broadcastHost;
clearinghouse => {
IF Rope.Equal[hostPart, "ME"]
THEN { answer ← MyAddress[] }
ELSE { ERROR Error[code~noResponse, text~"no Clearinghouse access (yet)"] };
};
ENDCASE => answer.host ← CvtHost[hostPart, hostType];
SELECT netType FROM
default => IF answer.net = XNS.unknownNet THEN answer.net ← DefaultNet[];
broadcast => answer.net ← XNS.broadcastNet;
clearinghouse => ERROR;
ENDCASE => answer.net ← CvtNet[netPart, netType];
SELECT socketType FROM
default => IF answer.socket = XNS.unknownSocket THEN answer.socket ← default;
broadcast => answer.socket ← XNS.unknownSocket;
clearinghouse => ERROR;
ENDCASE => answer.socket ← CvtSocket[socketPart, socketType];
RETURN [answer];
};
AddressesFromName: PUBLIC PROC [name: Name, default: Socket ← XNS.unknownSocket] RETURNS [LIST OF Address] ~ {
Like AddressFromName, except it returns all the addresses, closest first.
RETURN [ LIST [AddressFromName[name, default]] ] };
DOES THIS MAKE SENSE IN XNS???
RopeFromAddress: PUBLIC PROC [address: Address, format: Format ← octal] RETURNS [rope: ROPE] ~ {
Returns a ROPE of the form "1.12.123".
Hack: unknownSocket => "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] ];
};
NameFromAddress: PUBLIC PROC [address: Address, ignoreSocket: BOOLTRUE] RETURNS [Name] ~ {
This is hard to do in the XNS world. For now it just calls RopeFromAddress.
If ignoreSocket is TRUE, then unknownSocket is used in place of address.socket.
temp: Address ← address;
IF ignoreSocket THEN temp.socket ← XNS.unknownSocket;
RETURN [ RopeFromAddress[temp] ];
};
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]];
clearinghouse => ERROR Error[syntax, "no CH lookup (yet)"];
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 => clearinghouse);
FOR i: CARDINAL IN [1 .. limit) WHILE type < clearinghouse 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 clearinghouse);
ENDCASE => type ← clearinghouse;
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 ← clearinghouse;
};
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;
}.