Difference between revisions of "Console"

From BlackBox Framework Wiki
Jump to navigation Jump to search
(improved version)
 
(One intermediate revision 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 5: Line 9:
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 24:


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 69:


(* output null terminated string; converted to Windows default code page *)
(* output null terminated string; converted to Windows default code page *)
PROCEDURE String*(IN s: ARRAY OF CHAR);
PROCEDURE String*(IN str: ARRAY OF CHAR);
VAR buf: ARRAY 10000 OF SHORTCHAR;
VAR buf: ARRAY 10000 OF SHORTCHAR;
BEGIN
BEGIN
IF on & (s # "") THEN
IF on & (str # "") THEN
res := WinApi.WideCharToMultiByte(WinApi.CP_OEMCP, {}, s, LEN(s$), buf, LEN(buf), NIL, NIL);
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 CHAR); (* copied from Strings *)
(* 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: CHAR; a: ARRAY 32 OF CHAR;
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);
j := 0; WHILE a[j] # 0X DO INC(j) END
END;
END;
ASSERT(k + j < LEN(s), 23);
ASSERT(k + j < LEN(s), 23);
Line 89: Line 114:
(* 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: ARRAY 20 OF CHAR; sstr: ARRAY 20 OF SHORTCHAR;
VAR str: ARRAY 20 OF SHORTCHAR;
BEGIN
BEGIN
IF on THEN
IF on THEN
LinePrefix;
IntToString(x, str);
IntToString(x, str);
sstr := SHORT(str);
res := WinApi.WriteFile(stdOut, SYSTEM.ADR(str), LEN(str$), NIL, NIL)
res := WinApi.WriteFile(stdOut, SYSTEM.ADR(sstr), LEN(sstr$), NIL, NIL)
END
END
END Int;
END Int;


(* outputs the line number and indentation level followed by repeated indentStr according to the indentation level *)
(* 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
Int(lineNr); String(":"); Int(indent);
IF ~prefixed THEN
FOR i := 1 TO indent DO String(indentStr) END
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 140:
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); LinePrefix;
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);
lineNr := 1; indent := 1; indentStr := "  "; On;
indentStr := "  "; Reset
LinePrefix
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.