Difference between revisions of "Console"

From BlackBox Framework Wiki
Jump to navigation Jump to search
(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...")
 
(improved version)
Line 4: Line 4:
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.
Non-ASCII characters are converted to 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.
  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, stdOut, lineNr, indent, offAtLineNr: INTEGER;
on: BOOLEAN;
indentStr: ARRAY 10 OF CHAR;
 
(* 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 s: ARRAY OF CHAR);
PROCEDURE String*(IN s: ARRAY OF CHAR);
VAR line: ARRAY 10000 OF SHORTCHAR;
VAR buf: ARRAY 10000 OF SHORTCHAR;
BEGIN
BEGIN
line := SHORT(s);
IF on & (s # "") THEN
res := WinApi.WriteFile(stdOut, SYSTEM.ADR(line), LEN(line$), NIL, NIL);
res := WinApi.WideCharToMultiByte(WinApi.CP_OEMCP, {}, s, LEN(s$), 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)
END
END String;
END String;


Line 34: Line 87:
END IntToString;
END IntToString;


(* 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 CHAR; sstr: ARRAY 20 OF SHORTCHAR;
BEGIN
BEGIN
IntToString(x, str);
IF on THEN
sstr := SHORT(str);
IntToString(x, str);
res := WinApi.WriteFile(stdOut, SYSTEM.ADR(sstr), LEN(sstr$), NIL, NIL);
sstr := SHORT(str);
res := WinApi.WriteFile(stdOut, SYSTEM.ADR(sstr), LEN(sstr$), NIL, NIL)
END
END Int;
END Int;


PROCEDURE LogLineNr;
(* outputs the line number and indentation level followed by repeated indentStr according to the indentation level *)
BEGIN INC(lineNr); Int(lineNr); String(": ")
PROCEDURE LinePrefix;
END LogLineNr;
VAR i: INTEGER;
BEGIN
Int(lineNr); String(":"); Int(indent);
FOR i := 1 TO indent DO String(indentStr) 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
res := WinApi.WriteFile(stdOut, SYSTEM.ADR(crlf), 2, NIL, NIL);
crlf[0] := 0DX; crlf[1] := 0AX;
LogLineNr
res := WinApi.WriteFile(stdOut, SYSTEM.ADR(crlf), 2, NIL, NIL);
INC(lineNr); LinePrefix;
IF (offAtLineNr # -1) & (lineNr = offAtLineNr) THEN on := FALSE END
END
END Ln;
END Ln;


Line 57: Line 121:
res := WinApi.AllocConsole();
res := WinApi.AllocConsole();
stdOut := WinApi.GetStdHandle(WinApi.STD_OUTPUT_HANDLE);
stdOut := WinApi.GetStdHandle(WinApi.STD_OUTPUT_HANDLE);
LogLineNr
lineNr := 1; indent := 1; indentStr := "  "; On;
LinePrefix
END Console.</pre>
END Console.</pre>

Revision as of 14:58, 21 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. Non-ASCII characters are converted to the underlying Windows default code page.


MODULE Console;

(* low level logging to console window; supports switching on and off and indentation.
   if used in Kernel (or other linked module) don't forget to link a new .exe file *)

IMPORT WinApi, SYSTEM;

VAR
	res, stdOut, lineNr, indent, offAtLineNr: INTEGER;
	on: BOOLEAN;
	indentStr: ARRAY 10 OF CHAR;

(* 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 s: ARRAY OF CHAR);
	VAR buf: ARRAY 10000 OF SHORTCHAR;
BEGIN
	IF on & (s # "") THEN
		res := WinApi.WideCharToMultiByte(WinApi.CP_OEMCP, {}, s, LEN(s$), 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)
	END
END String;

PROCEDURE IntToString(x: LONGINT; OUT s: ARRAY OF CHAR); (* copied from Strings *)
	CONST minLongIntRev = "8085774586302733229";
	VAR j, k: INTEGER; ch: CHAR; a: ARRAY 32 OF CHAR;
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] := CHR(x MOD 10 + ORD("0")); x := x DIV 10; INC(j) UNTIL x = 0
	ELSE
		a := minLongIntRev; s[0] := "-"; k := 1;
		j := 0; WHILE a[j] # 0X DO INC(j) END
	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 CHAR; sstr: ARRAY 20 OF SHORTCHAR;
BEGIN
	IF on THEN
		IntToString(x, str);
		sstr := SHORT(str);
		res := WinApi.WriteFile(stdOut, SYSTEM.ADR(sstr), LEN(sstr$), NIL, NIL)
	END
END Int;

(* outputs the line number and indentation level followed by repeated indentStr according to the indentation level *)
PROCEDURE LinePrefix;
	VAR i: INTEGER;
BEGIN
	Int(lineNr); String(":"); Int(indent);
	FOR i := 1 TO indent DO String(indentStr) END
END LinePrefix;

(* output line end and increment line number *)
PROCEDURE Ln*;
	VAR crlf: ARRAY 2 OF SHORTCHAR;
BEGIN
	IF on THEN
		crlf[0] := 0DX; crlf[1] := 0AX;
		res := WinApi.WriteFile(stdOut, SYSTEM.ADR(crlf), 2, NIL, NIL);
		INC(lineNr); LinePrefix;
		IF (offAtLineNr # -1) & (lineNr = offAtLineNr) THEN on := FALSE END
	END
END Ln;

BEGIN
	res := WinApi.AllocConsole();
	stdOut := WinApi.GetStdHandle(WinApi.STD_OUTPUT_HANDLE);
	lineNr := 1; indent := 1; indentStr := "  "; On;
	LinePrefix
END Console.