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 }; MyAddress: PUBLIC PROC RETURNS [Address] ~ { 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]] }; 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; BEGIN firstDot, secondDot: INT; firstDot _ Rope.Index[s1~rope, s2~"."]; IF firstDot = nameLen THEN firstDot _ Rope.Index[s1~rope, s2~"#"]; IF firstDot = nameLen THEN { 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 { 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 }; { 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] ~ { 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 ] ]; }; DefaultNet: PROC RETURNS [default: Net _ XNS.unknownNet] ~ { 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; 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 ~ { 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; 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]; }; Error: PUBLIC ERROR [code: ErrorCode, text: ROPE] ~ CODE; }. €XNSAddressParsingImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Demers, October 16, 1986 10:30:18 am PDT 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 Return best address for this machine. Socket will be unknownSocket. Name/Address translation Split name into pieces. One-part name. Two-part name. Three-part name. Returns a ROPE of the form "1.12.123". An unknownSocket is expanded as the null string, e.g. "1.12." (no trailing 0). Private Procedures 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). 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. [n, rem, isZero] _ [n/base, n MOD base, (n/base = 0)] n _ renormalize[n]: Errors Κ O˜codešœ™Kšœ Οmœ1™—Kšžœ ˜—K˜˜K™Kšœ/˜/K˜KšœN˜NK˜Kšœ7˜7K˜!Kšžœ ˜—K˜šž˜Kšœ žœ˜—Kšžœ˜—K˜šžœ ž˜Kšœ žœ0˜@Kšœžœ˜-šœ˜šžœ˜Kšžœ˜Kšžœžœ*˜6—K˜—Kšžœ.˜5—K˜šžœ ž˜Kšœ žœžœ žœ˜IKšœžœ˜+Kšœžœ'˜:Kšžœ*˜1—K˜šžœ ž˜Kšœ žœžœžœ˜MKšœžœ˜/Kšœžœ*˜=Kšžœ6˜=—K˜Kšžœ ˜K˜—K˜š Ÿœžœžœ,žœžœ˜`Kšœ žœ™&K™NKšœX˜Xšœ˜Kšœo˜oK˜—šžœžœž˜*K˜Z—Kšœ˜K™—šŸ œž œžœžœ ˜7K˜ šžœž˜Kšœ žœžœ˜'Kšœžœ˜3Kšžœžœ˜-—K˜K˜—šŸ œž œžœžœ ˜9K˜ šžœž˜Kšœ žœžœ˜(Kšœžœ˜4Kšœžœ˜1Kšžœžœ˜.—K˜K˜—šŸœž œžœžœ ˜=K˜ šžœž˜Kšœžœ˜6Kšžœžœ ˜0—K˜K˜—š Ÿ œžœžœ$žœžœ˜NKšžœ6˜K˜Kšžœ8˜>—K˜šŸœžœžœžœ ˜Kšžœ˜—K˜K˜—K˜Kšœ žœ žœžœD˜dK˜šŸœžœžœžœ˜