Oberon/A2/Oberon.RFC3986.Mod

(* ETH Oberon, Copyright 1990-2006 Computer Systems Institute, ETH Zurich, CH-8092 Zurich. Desktops.OpenDoc http&#58;//www.oberon.ethz.ch/ and follow the "license agreement" link. *) MODULE RFC3986 IN Oberon ;	(** portable *) (* Desktops.OpenDoc  http&#58;//www.apps.ietf.org/rfc/rfc3986.html . https&#58;//en.wikipedia.org/wiki/Percent-encoding Based upon procedures in HyperDocs.Mod of ETH Oberon / PC Native 05.01.2003. The source and target parameters of exported procedures are strings.   A source string lacking a 0X string termination is rejected.  ple 2005-09-07*) IMPORT SYSTEM, Texts, Oberon, Strings; CONST &#9;hexdigits &#61; "0123456789ABCDEF"; VAR &#9;errors*&#58; SHORTINT; (* Error counter *) &#9;W&#58; Texts.Writer; &#9;hexdigitsarray&#58; ARRAY 17 OF CHAR; &#32;&#32; &#9;PROCEDURE Log(ch&#58; CHAR); &#9;BEGIN Texts.Write(W, ch) END Log; &#9;&#32;&#32;&#32;&#32; &#9;PROCEDURE LogLn; &#9;BEGIN Texts.WriteLn(W) END LogLn; &#9;&#32;&#32;&#32;&#32; &#9;(* Precondition &#61; string ends with 0X *) &#9;PROCEDURE LogString(s&#58; ARRAY OF CHAR); &#9;BEGIN Texts.WriteString(W, s) END LogString; &#9;&#32;&#32;&#32;&#32; &#9;(* Precondition&#58; s contains characters and possibly 0X. &#9;If 0X is present, stop the output there. *) &#9;PROCEDURE LogChars(s&#58; ARRAY OF CHAR); &#9;VAR i&#58; LONGINT; &#9;BEGIN &#9;&#9;i &#58;&#61; 0; &#32;&#32;&#32;&#32;&#9;WHILE (i &#60; LEN(s)) &#38; (s&#91;i&#93; # 0X) DO Texts.Write(W, s&#91;i&#93;); INC(i) END; &#9;&#32;END LogChars; &#9;&#32;&#32;&#32;&#32; &#9;(* PROCEDURE LogInt(x, n&#58; LONGINT); &#9;BEGIN Texts.WriteInt(W, x, n) END LogInt; *) &#9;&#32;&#32;&#32;&#32; &#9;PROCEDURE LogAppend; &#9;BEGIN Texts.Append(Oberon.Log, W.buf) END LogAppend; &#9;&#32;&#32;&#32;&#32; &#9;PROCEDURE HexVal(ch&#58; CHAR)&#58; INTEGER; &#9;BEGIN &#9;&#9;(* LogString("RFC3986.HexVal&#58; invoked on ch &#61; "); Log(22X); Log(ch); &#9;&#9;Log(22X); LogString("."); LogLn; *) &#9;&#9;(* We might hope that an application of CAP would remove the &#9;&#9;need for the case of ch in a..z but CAP does not check the parameter.   &#9;&#9;Thus CAP("A") and CAP("2") shift characters which should not be  &#9;&#9;shifted. *) &#9;&#9;(* ch &#58;&#61; CAP(ch); *) &#9;&#9;(* LogString("CAP(ch) &#61; "); Log(HexDigit(ORD(ch) DIV 16)); &#9;&#9; Log(HexDigit(ORD(ch) MOD 16)); LogString("X."); LogLn; *) &#9;&#9;IF (ch &#62;&#61; "0") &#38; (ch &#60;&#61; "9") THEN &#9;&#9;&#9;RETURN ORD(ch)-ORD("0") &#9;&#9;ELSIF (ch &#62;&#61; "A") &#38; (ch &#60;&#61; "F") THEN &#9;&#9;&#9;RETURN ORD(ch)-ORD("A")+10 &#9;&#9;ELSIF (ch &#62;&#61; "a") &#38; (ch &#60;&#61; "f") THEN &#9;&#9;&#9;RETURN ORD(ch)-ORD("a")+10 &#9;&#9;ELSE &#9;&#9;&#9;LogString("RFC3986.HexVal&#58; invoked on illegal character."); LogLn; &#9;&#9;&#9;RETURN 0 &#9;&#9;END &#9;END HexVal; &#9;PROCEDURE HexDigit(i&#58; INTEGER)&#58; CHAR; &#9;BEGIN &#9;&#9;RETURN hexdigitsarray&#91;i&#93;; &#9;(*	IF i &#60; 10 THEN &#9;&#9;&#9;RETURN CHR(i+ORD("0")) &#9;&#9;ELSE &#9;&#9;&#9;RETURN CHR(i-10+ORD("A")) &#9;&#9;END *) &#9;END HexDigit; (** Escape codes are literalized or contracted.  &#9;E.g. "Hello%20World" becomes "Hello World". *) PROCEDURE Decode*(VAR source, target&#58; ARRAY OF CHAR); VAR &#9;is, it, slen, tlen&#58; LONGINT; &#9;ch&#58; CHAR; BEGIN &#9;errors &#58;&#61; 0; &#9;is &#58;&#61; 0; it &#58;&#61; 0; slen &#58;&#61; LEN(source); tlen &#58;&#61; LEN(target); &#9;IF SYSTEM.ADR(source&#91;0&#93;) &#61; SYSTEM.ADR(target&#91;0&#93;) THEN &#9;(* Aiming to put the literal version where the source exists. The literal  &#9;version is never larger than the escaped version.  Array overflow is  &#9;impossible but check for presence of 0X. *) &#9;&#9;LOOP &#9;&#9;&#9;IF is &#61; slen THEN (* 0X absent. *) &#9;&#9;&#9;&#9;INC(errors); &#9;&#9;&#9;&#9;LogString("RFC3986.Decode&#58; 0X absent from "); LogLn; &#9;&#9;&#9;&#9;LogString("source character array. Not literalizing."); LogLn; &#9;&#9;&#9;&#9;RETURN &#9;&#9;&#9;ELSIF source&#91;is&#93; &#61; 0X THEN EXIT &#9;&#9;&#9;ELSE INC(is); &#9;&#9;&#9;END &#9;&#9;END; &#9;&#9;(* Now literalize. *) &#9;&#9;is &#58;&#61; 0; &#9;&#9;LOOP &#9;&#9;&#9;ch &#58;&#61; source&#91;is&#93;; &#9;&#9;&#9;IF ch &#61; 0X THEN target&#91;it&#93; &#58;&#61; 0X; EXIT &#9;&#9;&#9;ELSIF (ch &#61; "%") &#38; ((is+2) &#60; slen) THEN &#9;&#9;&#9;&#9;IF Strings.IsHexDigit(source&#91;is+1&#93;) &#38; Strings.IsHexDigit(source&#91;is+2&#93;) &#9;&#9;&#9;&#9;&#9;THEN (* have a valid encoding such as %3d *) &#9;&#9;&#9;&#9;&#9;target&#91;it&#93; &#58;&#61; CHR(16*HexVal(source&#91;is+1&#93;)+HexVal(source&#91;is+2&#93;)); &#9;&#9;&#9;&#9;&#9;INC(is); INC(is) &#9;&#9;&#9;&#9;ELSE target&#91;it&#93; &#58;&#61; ch; (* &#61; "%" *) &#9;&#9;&#9;&#9;END &#9;&#9;&#9;ELSE target&#91;it&#93; &#58;&#61; ch (* &#60;&#62; "%" *) &#9;&#9;&#9;END; &#9;&#9;&#9;INC(is); INC(it) &#9;&#9;END &#9;ELSE (* Source and target are distinct. Count characters to check  &#9;&#9;whether the literalized string will fit in the target array. *) &#9;&#9;LOOP &#9;&#9;&#9;IF is &#61; slen THEN (* 0X absent. *) &#9;&#9;&#9;&#9;INC(errors); &#9;&#9;&#9;&#9;LogString("RFC3986.Decode&#58; 0X absent from "); LogLn; &#9;&#9;&#9;&#9;LogString("source character array. Not literalizing."); LogLn; &#9;&#9;&#9;&#9;RETURN &#9;&#9;&#9;ELSIF source&#91;is&#93; &#61; 0X THEN EXIT &#9;&#9;&#9;ELSIF (source&#91;is&#93; &#61; "%") &#38; ((is+2) &#60; slen) THEN &#9;&#9;&#9;&#9;IF Strings.IsHexDigit(source&#91;is+1&#93;) &#38; Strings.IsHexDigit(source&#91;is+2&#93;) &#9;&#9;&#9;&#9;&#9;THEN (* have a valid encoding such as %3d *) INC(is, 2); &#9;&#9;&#9;&#9;(* ELSE in all other cases, source&#91;is..(is+2)&#93; &#61; "%GG" for example, &#9;&#9;&#9;&#9;&#9;&#9;source&#91;is&#93; &#61; "%" is left as literal. *) &#9;&#9;&#9;&#9;END &#9;&#9;&#9;END; &#9;&#9;&#9;INC(is); INC(it) &#9;&#9;END; &#9;&#9;(* Index it now locates the terminal character of the hypothetical &#9;&#9;literalized string. *) &#9;&#9;IF it &#62;&#61; tlen THEN (* literalized string is too long for the target array. *) &#9;&#9;&#9;INC(errors); &#9;&#9;&#9;LogString("RFC3986.Decode&#58; literalized string "); LogLn; &#9;&#9;&#9;LogString("will be too long for the target "); LogLn; &#9;&#9;&#9;LogString("array. Not literalizing."); LogLn; &#9;&#9;&#9;RETURN &#9;&#9;END; &#9;&#9;is &#58;&#61; 0; it &#58;&#61; 0; &#9;&#9;LOOP &#9;&#9;&#9;ch &#58;&#61; source&#91;is&#93;; &#9;&#9;&#9;IF (ch &#61; "%") &#38; ((is+2) &#60; slen) THEN &#9;&#9;&#9;&#9;IF Strings.IsHexDigit(source&#91;is+1&#93;) &#38; Strings.IsHexDigit(source&#91;is+2&#93;) &#9;&#9;&#9;&#9;&#9;THEN (* have a valid encoding such as %3d *) &#9;&#9;&#9;&#9;&#9;target&#91;it&#93; &#58;&#61; CHR(16*HexVal(source&#91;is+1&#93;)+HexVal(source&#91;is+2&#93;)); &#9;&#9;&#9;&#9;&#9;INC(is, 2) &#9;&#9;&#9;&#9;ELSE target&#91;it&#93; &#58;&#61; ch (* &#61; "%" *) &#9;&#9;&#9;&#9;END &#9;&#9;&#9;ELSE target&#91;it&#93; &#58;&#61; ch (* &#60;&#62; "%" *) &#9;&#9;&#9;END; &#9;&#9;&#9;IF target&#91;it&#93; &#61; 0X THEN EXIT END; &#9;&#9;&#9;INC(is); INC(it) &#9;&#9;END &#9;END END Decode; (** All special characters in source are escaped according to RFC 2396. E.g. "a+b" becomes "a%2Bb".  Special characters are&#58; 1X .. 20X, "+", "&#38;", "&#61;",  "?", "%", "$", ";". "#", "&#58;" &#38; special.  &#32;&#32; Escaping a character makes the string longer.  Overflow of the character  array is avoided. Two procedures Encode1 and Encode2 follow.  This Encode is a wrapper which can  invoke either of them according to choice of the user. *) PROCEDURE Encode*(VAR source, target&#58; ARRAY OF CHAR; special&#58; CHAR); PROCEDURE RequiresEscape(ch&#58;CHAR)&#58;BOOLEAN; BEGIN &#9;IF (ch &#61; 0X) &#9;THEN &#9;&#9;RETURN(FALSE) &#9;ELSIF (ch &#60;&#61; 020X) OR (ch &#61; "+") OR (ch &#61; "&#38;") OR (ch &#61; "&#61;") OR (ch &#61; "?") &#9;&#9;OR (ch &#61; "%") OR (ch &#61; "$") OR (ch &#61; ";") OR (ch &#61; "#") OR (ch &#61; "&#58;") &#9;&#9;OR (ch &#61; special) &#9;THEN &#9;&#9;RETURN(TRUE) &#9;ELSE &#9;&#9;RETURN(FALSE) &#9;END END RequiresEscape; (** The source and target parameters can refer to one or two actual parameters.  With one actual parameter, the string is escaped  "in place".  With two actual parameters, the source parameter is untouched. *) PROCEDURE Encode1(VAR source, target&#58; ARRAY OF CHAR; special&#58; CHAR); VAR &#9;is, it, slen, tlen&#58; LONGINT; &#9;ch&#58; CHAR; BEGIN &#9;errors &#58;&#61; 0; &#9;is &#58;&#61; 0; it &#58;&#61; 0; slen &#58;&#61; LEN(source); tlen &#58;&#61; LEN(target); &#9;LOOP &#9;&#9;IF is &#61; slen THEN (* 0X absent. *) &#9;&#9;&#9;DEC(is); DEC(it); INC(errors); &#9;&#9;&#9;LogString("RFC3986.Encode.Encode1&#58; 0X absent from "); LogLn; &#9;&#9;&#9;LogString("character array. Not escaping."); LogLn; &#9;&#9;&#9;RETURN &#9;&#9;ELSIF source&#91;is&#93; &#61; 0X THEN EXIT &#9;&#9;ELSIF RequiresEscape(source&#91;is&#93;) THEN INC(it, 3); INC(is) &#9;&#9;ELSE INC(it); INC(is) &#9;&#9;END &#9;END; &#9;(* is now contains the index of the last character of the string; &#9;"it" contains the index of the last character in the escaped version &#9;and may be &#62;&#61; tlen.  The source string ends with 0X. *) &#9;IF it &#62;&#61; tlen THEN (* The escaped version is too long for the target array. *) &#9;&#9;INC(errors); &#9;&#9;LogString("RFC3986.Encode.Encode1&#58; Escaped version of string "); LogLn; &#9;&#9;Log(22X); LogString(source); Log(22X); LogLn; &#9;&#9;LogString("will be too long for the array. Not escaping."); LogLn; &#9;&#9;RETURN &#9;END; &#9;IF SYSTEM.ADR(source&#91;0&#93;) &#61; SYSTEM.ADR(target&#91;0&#93;) THEN &#9;&#9;(* Aiming to put the escaped version where the source exists. &#9;&#9;Put the translated characters into the tail end of the array.  &#9;&#9;After translation is complete, shift the string up to the front of the array.   &#9;&#9;This avoids shifting the downstream characters for each character which  &#9;&#9;needs escaping. *) &#9;&#9;it &#58;&#61; tlen-1; &#9;&#9;IF source&#91;is&#93; &#61; 0X THEN target&#91;it&#93; &#58;&#61; 0X; DEC(is); DEC(it) &#9;&#9;ELSE &#9;&#9;&#9;INC(errors); &#9;&#9;&#9;LogString("RFC3986.Encode1&#58; index of terminus of source string"); LogLn; &#9;&#9;&#9;LogString("not located properly. Not escaping."); LogLn; &#9;&#9;&#9;RETURN &#9;&#9;END; &#9;&#9;(* Proceed to check characters from the terminus towards the origin of &#9;&#9;the array; escape as required. *) &#9;&#9;LOOP &#9;&#9;&#9;IF is &#61; -1 THEN (* finished source array *) EXIT END; &#9;&#9;&#9;ch &#58;&#61; source&#91;is&#93;; &#9;&#9;&#9;IF RequiresEscape(ch) THEN &#9;&#9;&#9;&#9;target&#91;it&#93; &#58;&#61; HexDigit(ORD(ch) MOD 16); DEC(it); &#9;&#9;&#9;&#9;target&#91;it&#93; &#58;&#61; HexDigit(ORD(ch) DIV 16); DEC(it); &#9;&#9;&#9;&#9;target&#91;it&#93; &#58;&#61; "%"; DEC(it) &#9;&#9;&#9;ELSE (* just copy the character *) &#9;&#9;&#9;&#9;target&#91;it&#93; &#58;&#61; ch; DEC(it) &#9;&#9;&#9;END; &#9;&#9;&#9;DEC(is) &#9;&#9;END; &#9;&#9;(* Encoding finished; shift the string to the origin of the array. *) &#9;&#9;is &#58;&#61; 0; INC(it); &#9;&#9;LOOP &#9;&#9;&#9;IF it &#61; tlen THEN EXIT END; &#9;&#9;&#9;target&#91;is&#93; &#58;&#61; target&#91;it&#93;; &#9;&#9;&#9;IF target&#91;it&#93; &#61; 0X THEN EXIT END; &#9;&#9;&#9;INC(is); INC(it); &#9;&#9;END &#9;ELSE (* source and target are in distinct arrays. In this case work  &#9;&#9;from the head toward the tail. *) &#9;&#9;is &#58;&#61; 0; it &#58;&#61; 0; &#9;&#9;LOOP &#9;&#9;&#9;IF is &#61; slen THEN (* end of source array *) RETURN END; &#9;&#9;&#9;(* check the character and translate if required; *) &#9;&#9;&#9;IF RequiresEscape(source&#91;is&#93;) THEN &#9;&#9;&#9;&#9;target&#91;it&#93; &#58;&#61; "%"; INC(it); &#9;&#9;&#9;&#9;target&#91;it&#93; &#58;&#61; HexDigit(ORD(source&#91;is&#93;) DIV 16); INC(it); &#9;&#9;&#9;&#9;target&#91;it&#93; &#58;&#61; HexDigit(ORD(source&#91;is&#93;) MOD 16);INC(it) &#9;&#9;&#9;ELSE (* just copy the character; *) &#9;&#9;&#9;&#9;target&#91;it&#93; &#58;&#61; source&#91;is&#93;; INC(it); &#9;&#9;&#9;END; &#9;&#9;&#9;IF source&#91;is&#93; &#61; 0X THEN (* End of string; finished. *) EXIT END; &#9;&#9;&#9;INC(is) &#9;&#9;END; (* Loop over source characters. *) &#9;END; (* Cases of single and distinct arrays for source and target. *) &#9;RETURN END Encode1; (** The source and target parameters can refer to one or two actual parameters.  In this procedure an intermediate array is created to put  the escaped array into.  After escaping, the intermediate array is copied to  the target. *) PROCEDURE Encode2(VAR source, target&#58; ARRAY OF CHAR; special&#58; CHAR); VAR &#9;is, it, slen, tlen&#58; LONGINT; &#9;(* ch&#58; CHAR; *) &#9;intermediate&#58; POINTER TO ARRAY OF CHAR; BEGIN &#9;errors &#58;&#61; 0; &#9;is &#58;&#61; 0; it &#58;&#61; 0; slen &#58;&#61; LEN(source); tlen &#58;&#61; LEN(target); &#9;LOOP &#9;&#9;IF is &#61; slen THEN (* 0X absent. *) &#9;&#9;&#9;DEC(is); DEC(it); INC(errors); &#9;&#9;&#9;LogString("RFC3986.Encode.Encode2&#58; 0X absent from character array."); LogLn; &#9;&#9;&#9;RETURN &#9;&#9;ELSIF source&#91;is&#93; &#61; 0X THEN EXIT &#9;&#9;ELSIF RequiresEscape(source&#91;is&#93;) THEN INC(it, 3); INC(is) &#9;&#9;ELSE INC(it); INC(is) &#9;&#9;END &#9;END; &#9;(* is now contains the index of the last character of the string; &#9;"it" contains the index of the last character in the escaped version  &#9;and may be &#62;&#61; tlen.  The source string ends with 0X. *) &#9;IF it &#62;&#61; tlen THEN (* The escaped version is too long for the target array. *) &#9;&#9;INC(errors); &#9;&#9;LogString("RFC3986.Encode.Encode2&#58; Escaped version of string "); LogLn; &#9;&#9;Log(22X); LogString(source); Log(22X); LogLn; &#9;&#9;LogString("will be too large for the array. Not escaping."); LogLn; &#9;&#9;RETURN &#9;END; &#9;NEW(intermediate, it+1); &#9;is &#58;&#61; 0; it &#58;&#61; 0; &#9;LOOP &#9;&#9;IF is &#61; slen THEN (* end of source array *) EXIT END; &#9;&#9;(* check the character and translate if required; *) &#9;&#9;IF RequiresEscape(source&#91;is&#93;) THEN &#9;&#9;&#9;intermediate&#91;it&#93; &#58;&#61; "%"; INC(it); &#9;&#9;&#9;intermediate&#91;it&#93; &#58;&#61; HexDigit(ORD(source&#91;is&#93;) DIV 16); INC(it); &#9;&#9;&#9;intermediate&#91;it&#93; &#58;&#61; HexDigit(ORD(source&#91;is&#93;) MOD 16);INC(it) &#9;&#9;ELSE &#9;&#9;&#9;intermediate&#91;it&#93; &#58;&#61; source&#91;is&#93;; INC(it); &#9;&#9;&#9;(* LogChars("intermediate &#61; "); LogChars(intermediate^); LogLn; *) &#9;&#9;END; &#9;&#9;IF source&#91;is&#93; &#61; 0X THEN (* end of string *) EXIT END; &#9;&#9;INC(is); &#9;END; (* Loop over characters. *) &#9;(* The escaped string is now in intermediate^. Copy intermediate to  &#9;target. *) &#9;(* (LogString("intermediate &#61; "); &#9;Log(22X); LogChars(intermediate^); Log(22X); LogLn; *) &#9;it &#58;&#61; 0; &#9;LOOP &#9;&#9;IF it &#61; tlen THEN  &#9;&#9;&#9;INC(errors); &#9;&#9;&#9;LogString("RFC3986.Encode.Encode2&#58; 0X not found at end of target string."); LogLn; &#9;&#9;EXIT  &#9;&#9;END; &#9;&#9;target&#91;it&#93; &#58;&#61; intermediate&#91;it&#93;; &#9;&#9;IF target&#91;it&#93; &#61; 0X THEN EXIT END; &#9;&#9;INC(it) &#9;END END Encode2; BEGIN &#9;Encode1(source, target, special) END Encode; PROCEDURE Test*; VAR  &#9;source5, intermediate5, target5&#58; ARRAY 5 OF CHAR; &#9;source6, intermediate6 (*, target6*) &#58; ARRAY 6 OF CHAR; &#9;source7, intermediate7, target7&#58; ARRAY 7 OF CHAR; &#9;source8, intermediate8 (*, target8*) &#58; ARRAY 8 OF CHAR; BEGIN &#9;LogString("RFC3986.Test&#58; begun."); LogLn; &#9;LogLn; &#9;LogString("Test with distinct source and target locations."); LogLn; &#9;intermediate7 &#58;&#61; "%GGlah"; (* Anomalous case suggested by Jan Verhoeven. *) &#9;target7 &#58;&#61; "--"; LogLn; &#9;LogString("intermediate7 &#61; "); Log(22X); LogChars(intermediate7); Log(22X); LogLn; &#9;LogString("target7 &#61; "); Log(22X); LogChars(target7); Log(22X); LogLn;  &#9;LogString("Invoke Decode(intermediate7, target7)."); LogLn; &#9;Decode(intermediate7, target7); &#9;LogString("target7 &#61; "); Log(22X); LogChars(target7); Log(22X); LogLn;  &#9;LogLn; &#9;LogAppend; &#9;LogLn; &#9;source5 &#58;&#61; "blah"; &#9;intermediate5 &#58;&#61; "";  &#9;target5 &#58;&#61; "";  &#9;LogString("source5 &#61; "); Log(22X);  LogChars(source5);  Log(22X); LogLn; &#9;LogString("intermediate5 &#61; "); Log(22X); LogChars(intermediate5);  Log(22X); LogLn; &#9;LogString("target5 &#61; "); Log(22X); LogChars(target5); Log(22X); LogLn;  &#9;LogString("Invoke Encode(source5, intermediate5, ..."); LogLn; &#9;Encode(source5, intermediate5, " "); &#9;LogString("intermediate5 &#61; "); Log(22X); LogChars(intermediate5); Log(22X); LogLn; &#9;LogString("Invoke Decode(intermediate5, target5)."); LogLn; &#9;Decode(intermediate5, target5); &#9;LogString("target5 &#61; "); Log(22X); LogChars(target5); Log(22X); LogLn; &#9;LogLn; &#9;LogAppend; &#9; &#9;source5 &#58;&#61; "blah"; source5&#91;4&#93; &#58;&#61; 73X; &#9;intermediate5 &#58;&#61; ""; intermediate5&#91;4&#93; &#58;&#61; 2DX; &#9;target5 &#58;&#61; ""; &#9;LogString("source5 &#61; "); Log(22X); LogChars(source5);  Log(22X); LogLn; &#9;LogString("intermediate5 &#61; "); Log(22X); LogChars(intermediate5); Log(22X); LogLn; &#9;LogString("target5 &#61; "); Log(22X); LogChars(target5); Log(22X); LogLn; &#9;LogString("Invoke Encode(source5, intermediate5, ..."); LogLn; &#9;Encode(source5, intermediate5, " "); &#9;LogString("intermediate5 &#61; "); Log(22X); LogChars(intermediate5); Log(22X); LogLn; &#9;LogString("Invoke Decode.(intermediate5, target5)"); LogLn; &#9;Decode(intermediate5, target5); &#9;LogString("target5 &#61; "); Log(22X); LogChars(target5); Log(22X); LogLn;  &#9;LogLn; &#9;LogAppend; &#9; &#9;source5 &#58;&#61; "+lah"; &#9;intermediate7 &#58;&#61; "--";  &#9;target5 &#58;&#61; "";  &#9;LogString("source5 &#61; "); Log(22X);  LogChars(source5);  Log(22X); LogLn; &#9;LogString("intermediate7 &#61; "); Log(22X); LogChars(intermediate7);  Log(22X); LogLn; &#9;LogString("target5 &#61; "); Log(22X); LogChars(target5); Log(22X); LogLn;  &#9;LogString("Invoke Encode(source5, intermediate7, ..."); LogLn; &#9;Encode(source5, intermediate7, " "); &#9;LogString("intermediate7 &#61; "); Log(22X); LogChars(intermediate7); Log(22X); LogLn; &#9;LogString("Invoke Decode(intermediate7, target5)."); LogLn; &#9;Decode(intermediate7, target5); &#9;LogString("target5 &#61; "); Log(22X); LogChars(target5); Log(22X); LogLn; &#9;LogLn; &#9;LogAppend; &#9;source5 &#58;&#61; "bla+"; &#9;intermediate6 &#58;&#61; "-"; &#9;LogString("source5 &#61; "); Log(22X); LogChars(source5);  Log(22X); LogLn; &#9;LogString("intermediate6 &#61; "); Log(22X); LogChars(intermediate6); Log(22X); LogLn; &#9;LogString("Invoke Encode(source5, intermediate6, ..."); LogLn; &#9;Encode(source5, intermediate6, " "); &#9;LogString("intermediate6 &#61; "); Log(22X); LogChars(intermediate6); Log(22X); LogLn; &#9;LogLn; &#9;LogAppend; &#9;source5 &#58;&#61; "bla+"; &#9;intermediate7 &#58;&#61; "--"; &#9;target5 &#58;&#61; "";  &#9;LogString("source5 &#61; "); Log(22X);  LogChars(source5);  Log(22X); LogLn; &#9;LogString("intermediate7 &#61; "); Log(22X); LogChars(intermediate7);  Log(22X); LogLn; &#9;LogString("target5 &#61; "); Log(22X); LogChars(target5); Log(22X); LogLn;  &#9;LogString("Invoke Encode(source5, intermediate7, ..."); LogLn; &#9;Encode(source5, intermediate7, " "); &#9;LogString("intermediate7 &#61; "); Log(22X); LogChars(intermediate7); Log(22X);LogLn; &#9;LogString("Invoke Decode(intermediate7, target5)."); LogLn; &#9;Decode(intermediate7, target5); &#9;LogString("target5 &#61; "); Log(22X); LogChars(target5); Log(22X); LogLn; &#9;LogLn; &#9;LogAppend; &#9;source8 &#58;&#61; "bla+"; &#9;intermediate8 &#58;&#61; "---"; &#9;target5 &#58;&#61; ""; &#9;LogString("source8 &#61; "); Log(22X); LogChars(source8);  Log(22X); LogLn; &#9;LogString("intermediate8 &#61; "); Log(22X); LogChars(intermediate8); Log(22X); LogLn; &#9;LogString("target5 &#61; "); Log(22X); LogChars(target5);  Log(22X); LogLn; &#9;LogString("Invoke Encode(source8, intermediate8, ..."); LogLn; &#9;Encode(source8, intermediate8, " "); &#9;LogString("intermediate8 &#61; "); Log(22X); LogChars(intermediate8); Log(22X); LogLn; &#9;LogString("Invoke Decode(intermediate8, target5)."); LogLn; &#9;Decode(intermediate8, target5); &#9;LogString("target5 &#61; "); Log(22X); LogChars(target5); Log(22X); LogLn; &#9;LogLn; &#9;LogAppend; &#9;LogString("Now test with coincident source and target locations."); LogLn; &#9;LogLn; &#9; &#9;intermediate7 &#58;&#61; "%GGlah"; (* Anomalous case suggested by Jan Verhoeven. *) &#9;LogString("intermediate7 &#61; "); Log(22X); LogChars(intermediate7); Log(22X); LogLn; &#9;LogString("Invoke Decode(intermediate7, intermediate7)."); LogLn; &#9;Decode(intermediate7, intermediate7); &#9;LogString("intermediate7 &#61; "); Log(22X); LogChars(intermediate7); Log(22X); LogLn;  &#9;LogLn; &#9;LogAppend; &#9;source5 &#58;&#61; "blah"; &#9;LogString("source5 &#61; "); Log(22X);  LogChars(source5);  Log(22X); LogLn; &#9;LogString("Invoke Encode(source5, source5, ..."); LogLn; &#9;Encode(source5, source5, " "); &#9;LogString("source5 &#61; "); Log(22X); LogChars(source5); Log(22X); LogLn; &#9;LogString("Invoke Decode(source5, source5)."); LogLn; &#9;Decode(source5, source5); &#9;LogString("source5 &#61; "); Log(22X); LogChars(source5); Log(22X); LogLn; &#9;LogLn; &#9;LogAppend; &#9; &#9;source5 &#58;&#61; "blah"; source5&#91;4&#93; &#58;&#61; 73X; &#9;LogString("source5 &#61; "); Log(22X); LogChars(source5);  Log(22X); LogLn; &#9;LogString("Invoke Encode(source5, source5, ..."); LogLn; &#9;Encode(source5, source5, " "); &#9;LogString("source5 &#61; "); Log(22X); LogChars(source5); Log(22X); LogLn; &#9;LogString("Invoke Decode(source5, source5)."); LogLn; &#9;Decode(source5, source5); &#9;LogString("source5 &#61; "); Log(22X); LogChars(source5); Log(22X); LogLn;  &#9;LogLn; &#9;LogAppend; &#9; &#9;source5 &#58;&#61; "+lah"; &#9;LogString("source5 &#61; "); Log(22X);  LogChars(source5);  Log(22X); LogLn; &#9;LogString("Invoke Encode(source5, source5, ..."); LogLn; &#9;Encode(source5, source5, " "); &#9;LogString("source5 &#61; "); Log(22X); LogChars(source5); Log(22X); LogLn; &#9;LogString("Invoke Decode(source5, source5)."); LogLn; &#9;Decode(source5, source5); &#9;LogString("source5 &#61; "); Log(22X); LogChars(source5); Log(22X); LogLn; &#9;LogLn; &#9;LogAppend; &#9; &#9;source6 &#58;&#61; "+lah"; &#9;LogString("source6 &#61; "); Log(22X); LogChars(source6);  Log(22X); LogLn; &#9;LogString("Invoke Encode(source6, source6, ..."); LogLn; &#9;Encode(source6, source6, " "); &#9;LogString("source6 &#61; "); Log(22X); LogChars(source6); Log(22X); LogLn; &#9;LogString("Invoke Decode(source6, source6)."); LogLn; &#9;Decode(source6, source6); &#9;LogString("source6 &#61; "); Log(22X); LogChars(source6); Log(22X); LogLn;  &#9;LogLn; &#9;LogAppend; &#9; &#9;source7 &#58;&#61; "+lah"; &#9;LogString("source7 &#61; "); Log(22X);  LogChars(source7);  Log(22X); LogLn; &#9;LogString("Invoke Encode(source7, source7, ..."); LogLn; &#9;Encode(source7, source7, " "); &#9;LogString("source7 &#61; "); Log(22X); LogChars(source7); Log(22X); LogLn; &#9;LogAppend; &#9;LogString("Invoke Decode(source7, source7)."); LogLn; &#9;LogAppend; &#9;Decode(source7, source7); &#9;LogString("source7 &#61; "); Log(22X); LogChars(source7); Log(22X); LogLn; &#9;LogLn; &#9;LogAppend; &#9; &#9;source5 &#58;&#61; "bla+"; &#9;LogString("source5 &#61; "); Log(22X); LogChars(source5);  Log(22X); LogLn; &#9;LogString("Invoke Encode(source5, source5, ..."); LogLn; &#9;Encode(source5, source5, " "); &#9;LogString("source5 &#61; "); Log(22X); LogChars(source5); Log(22X); LogLn; &#9;LogString("Invoke Decode(source5, source5)."); LogLn; &#9;Decode(source5, source5); &#9;LogString("source5 &#61; "); Log(22X); LogChars(source5); Log(22X); LogLn; &#9;LogLn; &#9;LogAppend; &#9; &#9;source6 &#58;&#61; "bla+"; &#9;LogString("source6 &#61; "); Log(22X);  LogChars(source6);  Log(22X); LogLn; &#9;LogString("Invoke Encode(source6, source6, ..."); LogLn; &#9;Encode(source6, source6, " "); &#9;LogString("source6 &#61; "); Log(22X); LogChars(source6); Log(22X); LogLn; &#9;LogString("Invoke Decode(source6, source6)."); LogLn; &#9;Decode(source6, source6); &#9;LogString("source6 &#61; "); Log(22X); LogChars(source6); Log(22X); LogLn; &#9;LogLn; &#9;LogAppend; &#9; &#9;source7 &#58;&#61; "bla+"; &#9;LogString("source7 &#61; "); Log(22X); LogChars(source7);  Log(22X); LogLn; &#9;LogString("Invoke Encode(source7, source7, ..."); LogLn; &#9;Encode(source7, source7, " "); &#9;LogString("source7 &#61; "); Log(22X); LogChars(source7); Log(22X); LogLn; &#9;LogString("Invoke Decode(source7, source7)."); LogLn; &#9;Decode(source7, source7); &#9;LogString("source7 &#61; "); Log(22X); LogChars(source7); Log(22X); LogLn; &#9;LogLn; &#9;LogAppend; &#9; &#9;source8 &#58;&#61; "bla+"; &#9;LogString("source8 &#61; "); Log(22X);  LogChars(source8);  Log(22X); LogLn; &#9;LogString("Invoke Encode(source8, source8, ..."); LogLn; &#9;Encode(source8, source8, " "); &#9;LogString("source8 &#61; "); Log(22X); LogChars(source8); Log(22X); LogLn; &#9;LogString("Invoke Decode(source8, source8)."); LogLn; &#9;Decode(source8, source8); &#9;LogString("source8 &#61; "); Log(22X); LogChars(source8); Log(22X); LogLn; &#9;LogLn; &#9;LogAppend; (**) &#9; &#9;LogString("RFC3986.Test&#58; completed."); LogLn; &#9;LogAppend END Test; BEGIN &#9;Texts.OpenWriter(W); &#9;hexdigitsarray &#58;&#61; hexdigits; END RFC3986. RFC3986.Test