Difference between revisions of "Console"
Jump to navigation
Jump to search
Josef templ (talk | contribs) (improved version) |
Josef templ (talk | contribs) (added ReadLn and Pause, line prefix improved) |
||
Line 5: | Line 5: | ||
In order to make it easy to detect new output it adds a line number at the left. | In order to make it easy to detect new output it adds a line number at the left. | ||
In order to increase readability it supports indentation of the output. | In order to increase readability it supports indentation of the output. | ||
Non-ASCII characters are converted to the underlying Windows default code page. | Reading of console input and pausing is also supported. | ||
Non-ASCII characters are converted to/from the underlying Windows default code page. | |||
<pre>MODULE Console; | <pre>MODULE Console; | ||
(* low level logging to console window; supports switching on and off and indentation | (* low level logging to console window; | ||
extended subset of Log/StdLog; | |||
supports switching logging on and off and indentation; | |||
supports reading from console; | |||
if used in Kernel (or other linked module) don't forget to link a new .exe file *) | if used in Kernel (or other linked module) don't forget to link a new .exe file *) | ||
Line 16: | Line 20: | ||
VAR | VAR | ||
res, stdOut, lineNr, indent, offAtLineNr: INTEGER; | res, stdIn, stdOut, lineNr, indent, offAtLineNr: INTEGER; | ||
on: BOOLEAN; | on, prefixed: BOOLEAN; | ||
indentStr: ARRAY 10 OF CHAR; | indentStr: ARRAY 10 OF CHAR; | ||
PROCEDURE^ LinePrefix; | |||
(* sets a non-default indent string, default is " " *) | (* sets a non-default indent string, default is " " *) | ||
Line 59: | Line 65: | ||
(* output null terminated string; converted to Windows default code page *) | (* output null terminated string; converted to Windows default code page *) | ||
PROCEDURE String*(IN | PROCEDURE String*(IN str: ARRAY OF CHAR); | ||
VAR buf: ARRAY 10000 OF SHORTCHAR; | VAR buf: ARRAY 10000 OF SHORTCHAR; | ||
BEGIN | BEGIN | ||
IF on & ( | IF on & (str # "") THEN | ||
res := WinApi.WideCharToMultiByte(WinApi.CP_OEMCP, {}, | LinePrefix; | ||
res := WinApi.WideCharToMultiByte(WinApi.CP_OEMCP, {}, str, LEN(str$), buf, LEN(buf), NIL, NIL); | |||
IF ~((res > 0) & (res <= LEN(buf))) THEN | IF ~((res > 0) & (res <= LEN(buf))) THEN | ||
buf := "*** error in Console.String ***"; res := LEN(buf$); | buf := "*** error in Console.String ***"; res := LEN(buf$); | ||
END; | END; | ||
res := WinApi.WriteFile(stdOut, SYSTEM.ADR(buf), res, NIL, NIL) | res := WinApi.WriteFile(stdOut, SYSTEM.ADR(buf), res, NIL, NIL); | ||
res := WinApi.FlushFileBuffers(stdOut) | |||
END | END | ||
END String; | END String; | ||
PROCEDURE IntToString(x: LONGINT; OUT s: ARRAY OF | (* output single character; converted to Windows default code page *) | ||
PROCEDURE Char*(ch: CHAR); | |||
VAR s: ARRAY 2 OF CHAR; | |||
BEGIN | |||
s[0] := ch; s[1] := 0X; | |||
String(s) | |||
END Char; | |||
(* output boolean as TRUE or FALSE *) | |||
PROCEDURE Bool*(x: BOOLEAN); | |||
BEGIN | |||
IF x THEN String("TRUE") ELSE String("FALSE") END | |||
END Bool; | |||
PROCEDURE IntToString(x: LONGINT; OUT s: ARRAY OF SHORTCHAR); (* inspired from Strings *) | |||
CONST minLongIntRev = "8085774586302733229"; | CONST minLongIntRev = "8085774586302733229"; | ||
VAR j, k: INTEGER; ch: | VAR j, k: INTEGER; ch: SHORTCHAR; a: ARRAY 32 OF SHORTCHAR; | ||
BEGIN | BEGIN | ||
IF x # MIN(LONGINT) THEN | IF x # MIN(LONGINT) THEN | ||
IF x < 0 THEN s[0] := "-"; k := 1; x := -x ELSE k := 0 END; | IF x < 0 THEN s[0] := "-"; k := 1; x := -x ELSE k := 0 END; | ||
j := 0; REPEAT a[j] := CHR(x MOD 10 + ORD("0")); x := x DIV 10; INC(j) UNTIL x = 0 | j := 0; REPEAT a[j] := SHORT(CHR(x MOD 10 + ORD("0"))); x := x DIV 10; INC(j) UNTIL x = 0 | ||
ELSE | ELSE | ||
a := minLongIntRev; s[0] := "-"; k := 1; | a := minLongIntRev; s[0] := "-"; k := 1; j := LEN(minLongIntRev); | ||
END; | END; | ||
ASSERT(k + j < LEN(s), 23); | ASSERT(k + j < LEN(s), 23); | ||
Line 89: | Line 110: | ||
(* output integer as decimal number without any padding *) | (* output integer as decimal number without any padding *) | ||
PROCEDURE Int*(x: INTEGER); | PROCEDURE Int*(x: INTEGER); | ||
VAR str | VAR str: ARRAY 20 OF SHORTCHAR; | ||
BEGIN | BEGIN | ||
IF on THEN | IF on THEN | ||
LinePrefix; | |||
IntToString(x, str); | IntToString(x, str); | ||
res := WinApi.WriteFile(stdOut, SYSTEM.ADR(str), LEN(str$), NIL, NIL) | |||
res := WinApi.WriteFile(stdOut, SYSTEM.ADR( | |||
END | END | ||
END Int; | END Int; | ||
(* outputs the line number and indentation level followed by repeated indentStr according to | (* outputs the line number and indentation level followed by repeated indentStr according to indentation level *) | ||
PROCEDURE LinePrefix; | PROCEDURE LinePrefix; | ||
VAR i: INTEGER; | VAR i: INTEGER; lStr, iStr: ARRAY 12 OF SHORTCHAR; | ||
BEGIN | BEGIN | ||
IF ~prefixed THEN | |||
prefixed := TRUE; | |||
IntToString(lineNr, lStr); IntToString(indent, iStr); | |||
String(lStr + ":" + iStr + " "); | |||
FOR i := 1 TO indent DO String(indentStr) END; | |||
END | |||
END LinePrefix; | END LinePrefix; | ||
Line 111: | Line 136: | ||
BEGIN | BEGIN | ||
IF on THEN | IF on THEN | ||
LinePrefix; | |||
crlf[0] := 0DX; crlf[1] := 0AX; | crlf[0] := 0DX; crlf[1] := 0AX; | ||
res := WinApi.WriteFile(stdOut, SYSTEM.ADR(crlf), 2, NIL, NIL); | res := WinApi.WriteFile(stdOut, SYSTEM.ADR(crlf), 2, NIL, NIL); | ||
INC(lineNr); | INC(lineNr); prefixed := FALSE; | ||
IF (offAtLineNr # -1) & (lineNr = offAtLineNr) THEN on := FALSE END | IF (offAtLineNr # -1) & (lineNr = offAtLineNr) THEN on := FALSE END | ||
END | END | ||
END Ln; | END Ln; | ||
(* read text line from console; terminate line by pressing RETURN; str does not contain end-of-line characters *) | |||
PROCEDURE ReadLn*(VAR str: ARRAY OF CHAR); | |||
VAR buf: ARRAY 256 OF SHORTCHAR; read: INTEGER; | |||
BEGIN str := ""; | |||
res := WinApi.ReadFile(stdIn, SYSTEM.ADR(buf), LEN(buf), read, NIL); | |||
IF (res # 0) & (read >= 2) THEN buf[read - 2] := 0X; (* removes CRLF *) | |||
IF buf # "" THEN | |||
res := WinApi.MultiByteToWideChar(WinApi.CP_OEMCP, {}, buf, LEN(buf$), str, LEN(str) - 1); | |||
IF (res > 0) & (res < LEN(str)) THEN str[res] := 0X ELSE str := "" END | |||
END | |||
END | |||
END ReadLn; | |||
(* press RETURN on the console to continue *) | |||
PROCEDURE Pause*; | |||
VAR dummy: ARRAY 256 OF CHAR; | |||
BEGIN | |||
ReadLn(dummy) | |||
END Pause; | |||
(* reset line number and indentation level, turns logging on if it was off *) | |||
PROCEDURE Reset*; | |||
BEGIN | |||
On; | |||
IF prefixed THEN Ln END; | |||
lineNr := 1; indent := 0 | |||
END Reset; | |||
BEGIN | BEGIN | ||
res := WinApi.AllocConsole(); | res := WinApi.AllocConsole(); | ||
stdIn := WinApi.GetStdHandle(WinApi.STD_INPUT_HANDLE); | |||
stdOut := WinApi.GetStdHandle(WinApi.STD_OUTPUT_HANDLE); | stdOut := WinApi.GetStdHandle(WinApi.STD_OUTPUT_HANDLE); | ||
indentStr := " "; Reset | |||
END Console.</pre> | END Console.</pre> |
Revision as of 09:47, 22 February 2017
For logging within the BlackBox framework, in particular the text subsystem, or for logging in very low-level modules the standard BlackBox logging facilities (Log, StdLog) cannot be used.
The following module 'Console' can be used for that purpose. It attaches a Windows Console window to the BlackBox process and outputs the log to the Console window. In order to make it easy to detect new output it adds a line number at the left. In order to increase readability it supports indentation of the output. Reading of console input and pausing is also supported. Non-ASCII characters are converted to/from the underlying Windows default code page.
MODULE Console; (* low level logging to console window; extended subset of Log/StdLog; supports switching logging on and off and indentation; supports reading from console; if used in Kernel (or other linked module) don't forget to link a new .exe file *) IMPORT WinApi, SYSTEM; VAR res, stdIn, stdOut, lineNr, indent, offAtLineNr: INTEGER; on, prefixed: BOOLEAN; indentStr: ARRAY 10 OF CHAR; PROCEDURE^ LinePrefix; (* sets a non-default indent string, default is " " *) PROCEDURE SetIndentString*(IN s: ARRAY OF CHAR); BEGIN indentStr := s$ END SetIndentString; (* turn logging on; default *) PROCEDURE On*; BEGIN offAtLineNr := -1; on := TRUE END On; (* turn logging on for the specified number of lines *) PROCEDURE OnFor*(nofLines: INTEGER); BEGIN offAtLineNr := lineNr + nofLines; on := TRUE END OnFor; (* turn logging off *) PROCEDURE Off*; BEGIN on := FALSE END Off; (* increment indentation level *) PROCEDURE Indent*; BEGIN INC(indent) END Indent; (* decrement indentation level *) PROCEDURE Undent*; BEGIN DEC(indent) END Undent; (* output null terminated string; converted to Windows default code page *) PROCEDURE String*(IN str: ARRAY OF CHAR); VAR buf: ARRAY 10000 OF SHORTCHAR; BEGIN IF on & (str # "") THEN LinePrefix; res := WinApi.WideCharToMultiByte(WinApi.CP_OEMCP, {}, str, LEN(str$), buf, LEN(buf), NIL, NIL); IF ~((res > 0) & (res <= LEN(buf))) THEN buf := "*** error in Console.String ***"; res := LEN(buf$); END; res := WinApi.WriteFile(stdOut, SYSTEM.ADR(buf), res, NIL, NIL); res := WinApi.FlushFileBuffers(stdOut) END END String; (* output single character; converted to Windows default code page *) PROCEDURE Char*(ch: CHAR); VAR s: ARRAY 2 OF CHAR; BEGIN s[0] := ch; s[1] := 0X; String(s) END Char; (* output boolean as TRUE or FALSE *) PROCEDURE Bool*(x: BOOLEAN); BEGIN IF x THEN String("TRUE") ELSE String("FALSE") END END Bool; PROCEDURE IntToString(x: LONGINT; OUT s: ARRAY OF SHORTCHAR); (* inspired from Strings *) CONST minLongIntRev = "8085774586302733229"; VAR j, k: INTEGER; ch: SHORTCHAR; a: ARRAY 32 OF SHORTCHAR; BEGIN IF x # MIN(LONGINT) THEN IF x < 0 THEN s[0] := "-"; k := 1; x := -x ELSE k := 0 END; j := 0; REPEAT a[j] := SHORT(CHR(x MOD 10 + ORD("0"))); x := x DIV 10; INC(j) UNTIL x = 0 ELSE a := minLongIntRev; s[0] := "-"; k := 1; j := LEN(minLongIntRev); END; ASSERT(k + j < LEN(s), 23); REPEAT DEC(j); ch := a[j]; s[k] := ch; INC(k) UNTIL j = 0; s[k] := 0X END IntToString; (* output integer as decimal number without any padding *) PROCEDURE Int*(x: INTEGER); VAR str: ARRAY 20 OF SHORTCHAR; BEGIN IF on THEN LinePrefix; IntToString(x, str); res := WinApi.WriteFile(stdOut, SYSTEM.ADR(str), LEN(str$), NIL, NIL) END END Int; (* outputs the line number and indentation level followed by repeated indentStr according to indentation level *) PROCEDURE LinePrefix; VAR i: INTEGER; lStr, iStr: ARRAY 12 OF SHORTCHAR; BEGIN IF ~prefixed THEN prefixed := TRUE; IntToString(lineNr, lStr); IntToString(indent, iStr); String(lStr + ":" + iStr + " "); FOR i := 1 TO indent DO String(indentStr) END; END END LinePrefix; (* output line end and increment line number *) PROCEDURE Ln*; VAR crlf: ARRAY 2 OF SHORTCHAR; BEGIN IF on THEN LinePrefix; crlf[0] := 0DX; crlf[1] := 0AX; res := WinApi.WriteFile(stdOut, SYSTEM.ADR(crlf), 2, NIL, NIL); INC(lineNr); prefixed := FALSE; IF (offAtLineNr # -1) & (lineNr = offAtLineNr) THEN on := FALSE END END END Ln; (* read text line from console; terminate line by pressing RETURN; str does not contain end-of-line characters *) PROCEDURE ReadLn*(VAR str: ARRAY OF CHAR); VAR buf: ARRAY 256 OF SHORTCHAR; read: INTEGER; BEGIN str := ""; res := WinApi.ReadFile(stdIn, SYSTEM.ADR(buf), LEN(buf), read, NIL); IF (res # 0) & (read >= 2) THEN buf[read - 2] := 0X; (* removes CRLF *) IF buf # "" THEN res := WinApi.MultiByteToWideChar(WinApi.CP_OEMCP, {}, buf, LEN(buf$), str, LEN(str) - 1); IF (res > 0) & (res < LEN(str)) THEN str[res] := 0X ELSE str := "" END END END END ReadLn; (* press RETURN on the console to continue *) PROCEDURE Pause*; VAR dummy: ARRAY 256 OF CHAR; BEGIN ReadLn(dummy) END Pause; (* reset line number and indentation level, turns logging on if it was off *) PROCEDURE Reset*; BEGIN On; IF prefixed THEN Ln END; lineNr := 1; indent := 0 END Reset; BEGIN res := WinApi.AllocConsole(); stdIn := WinApi.GetStdHandle(WinApi.STD_INPUT_HANDLE); stdOut := WinApi.GetStdHandle(WinApi.STD_OUTPUT_HANDLE); indentStr := " "; Reset END Console.