Difference between revisions of "Console"
Josef templ (talk | contribs) (Created page with "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) ca...") |
Josef templ (talk | contribs) |
||
(2 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
Note: Starting with BlackBox 1.7.1 (issue-#158) a low-level Console logging feature has been added to the standard BlackBox distribution. | |||
Use The module ''HostConsole'' for low-level logging to the system's console. | |||
For BlackBox versions before 1.7.1 the following module can still be used. | |||
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. | 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. | ||
Line 4: | Line 8: | ||
It attaches a Windows Console window to the BlackBox process and outputs the log to the Console window. | 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 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. | |||
<pre>MODULE Console; | <pre>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; | IMPORT WinApi, SYSTEM; | ||
VAR res, stdOut, lineNr: INTEGER; | 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; | |||
PROCEDURE String*(IN | (* output null terminated string; converted to Windows default code page *) | ||
VAR | PROCEDURE String*(IN str: ARRAY OF CHAR); | ||
VAR buf: ARRAY 10000 OF SHORTCHAR; | |||
BEGIN | 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; | 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 34: | Line 112: | ||
END IntToString; | END IntToString; | ||
(* 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 | ||
IntToString(x, str); | IF on THEN | ||
LinePrefix; | |||
IntToString(x, str); | |||
res := WinApi.WriteFile(stdOut, SYSTEM.ADR(str), LEN(str$), NIL, NIL) | |||
END | |||
END Int; | END Int; | ||
PROCEDURE | (* outputs the line number and indentation level followed by repeated indentStr according to indentation level *) | ||
BEGIN | PROCEDURE LinePrefix; | ||
END | 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; | |||
PROCEDURE Ln* | (* output line end and increment line number *) | ||
PROCEDURE Ln*; | |||
VAR crlf: ARRAY 2 OF SHORTCHAR; | VAR crlf: ARRAY 2 OF SHORTCHAR; | ||
BEGIN | BEGIN | ||
crlf[0] := 0DX; crlf[1] := 0AX; | 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; | 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> |
Latest revision as of 08:35, 13 June 2017
Note: Starting with BlackBox 1.7.1 (issue-#158) a low-level Console logging feature has been added to the standard BlackBox distribution. Use The module HostConsole for low-level logging to the system's console. For BlackBox versions before 1.7.1 the following module can still be used.
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.