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