SOUP: the SOUrce Printer
This is a port from the soup project which I made under Linux with the Mocka compiler. Since this is a neat program that saves a lot of time when preparing hardcopy of sources, I decided to port it to DOS.
That was easier said than done. End-of-File handling under DOS is rather different from Unix. For one: it's less reliable, so I needed to practically completely rewrite the source to get something that barely comes close to the original, running under Linux.
Anyway, I succeeded and I now have a satisfactory port of soup to DOS.
The source of soup.
MODULE soup07;
(* This software is released under the rules of the GNU GPL.
Please feel free to improve this software, if needed. You may even port this source to a
lesser language like C. This software comes without any warranty of any kind. *)
(* 02 : Get a working program, no frills. Apr 20, 2003 *)
(* 03 : Added MakeDate, CardToString Apr 25, 2003 *)
(* We have an odd bug, causing one extra formfeed after the last page.
Solved: In 'WrapUp', the linefeeds were one too far, which
triggered an extra call to 'Skip' Apr 29, 2003 *)
(* 04 : Add /usr/local/soup/soup.rc configuration file,
Add ErrorMessage function May 9, 2003 *)
(* 05 : Add possibility to force formfeeds in sourcefile, change setup
file to '/usr/local/prut/soup.rc', make Soup determine if input
is from file or stdin May 26, 2003 *)
(* 06 : Make soup emit an ASCII.CR between last ASCII.FF and Printer
Reset string, If FileIO=TRUE then the projectname is derived from
the name of the inputfile. Jun 2, 2003 *)
(* 07 : Port SOUP to DOS with FST Modula-2 compiler
Wow, this was tough.. I had to rewrite just about everything. Oct 9, 2003 *)
IMPORT ASCII;
FROM InOut IMPORT CloseInput, Done, Read, ReadCard, ReadLine, ReadString, RedirectInput,
termCH, Write, WriteString, WriteLn, WriteCard;
FROM NumberConversion IMPORT CardToString;
FROM Strings IMPORT Append, Assign, CompareStr, Length, Pos;
FROM SYSTEM IMPORT ASSEMBLER;
FROM System IMPORT GetArg, Terminate;
FROM TimeDate IMPORT GetTime, Time;
TYPE ControlString = ARRAY [0..63] OF CHAR;
CONST maxLINE = 82; maxPOS = 120;
topMARGIN = 1; leftMARGIN = 8;
StdErr = 2;
VAR line, page, Xpos, LeftMargin, MaxLine,
MaxPos, TopMargin, FillUp : CARDINAL;
SomethingPrinted, FirstLineDone : BOOLEAN;
FirstLine : ARRAY [0..39] OF CHAR;
DateString : ARRAY [0..19] OF CHAR;
buffer : ARRAY [0..255] OF CHAR;
string : ARRAY [0..15] OF CHAR;
PrReset, PrSetup, option : ControlString;
This is all rather straightforward. It's just Modula-2 like with other compilers.
After ErrorMessage, the configuration file readers are defined. GetCtrlStr is used to read the printer setup
string from the C:\etc\soup.rc file.
Since the weirdest control sequences exist for many printers, I could not use simple ways to construct ESCape
sequences. Therefore I had to invent the tricks with Escape, Space and Control. See below and in the file
soup.rc.
PROCEDURE Eject; (* Have the printer eject one sheet of paper *)
BEGIN
ASM
MOV DL, 0CH
MOV AH, 2
INT 021H
END
END Eject;
PROCEDURE GetCtrlStr (VAR str : ARRAY OF CHAR);
VAR ch : CHAR;
BEGIN
str [0] := 0C; (* Clear string *)
LOOP
ReadString (option);
IF CompareStr (option, 'END') = 0 THEN EXIT END;
IF CompareStr (option, '<ESC>') = 0 THEN Append (str, 33C)
ELSIF CompareStr (option, '<_>') = 0 THEN Append (str, ' ')
ELSIF CompareStr (option, '<Ctrl>') = 0 THEN
REPEAT
Read (ch)
UNTIL ch > ' ';
option [0] := CHR (ORD (ch) - ORD ('@'));
option [1] := 0C;
Append (str, option)
ELSE
Append (str, option)
END
END
END GetCtrlStr;
PROCEDURE ConfigureSoup () : BOOLEAN;
BEGIN
RedirectInput ('c:\etc\soup.rc');
IF Done THEN
REPEAT
ReadString (option)
UNTIL CompareStr (option, 'BEGIN') = 0;
LOOP
ReadString (option);
IF CompareStr (option, 'END') = 0 THEN EXIT
ELSIF CompareStr (option, 'PrReset') = 0 THEN GetCtrlStr (PrReset)
ELSIF CompareStr (option, 'PrSetup') = 0 THEN GetCtrlStr (PrSetup)
ELSIF CompareStr (option, 'LeftMargin') = 0 THEN ReadCard (LeftMargin)
ELSIF CompareStr (option, 'TopMargin') = 0 THEN ReadCard (TopMargin)
ELSIF CompareStr (option, 'MaxPos') = 0 THEN ReadCard (MaxPos)
ELSIF CompareStr (option, 'MaxLine') = 0 THEN ReadCard (MaxLine)
ELSE
WriteString ('Invalid parameter in "c:\etc\soup.rc".');
WriteLn;
WriteString ('Reading aborted, assuming HP Laserjet IIP.');
WriteLn;
RETURN FALSE
END
END;
CloseInput
ELSE
WriteString ("No file 'c:\etc\soup.rc'. Assuming HP Laserjet IIP.");
WriteLn;
RETURN FALSE
END;
RETURN TRUE
END ConfigureSoup;
As you see, the file references have been adjusted for DOS style and the errormessages are now inside the
functions.
ConfigureSoup tries to open the rc file and if that fails, it sends an errormessage and falls back to HP LJ
mode.
If the soup.rc file exists, it is opened and read. The active part of soup.rc is enclosed between a 'BEGIN'
and an 'END'. Everything else (and there's a lot) is comment and explanation.
PROCEDURE Spaces (n : CARDINAL); (* Print n spaces *)
VAR i : CARDINAL;
BEGIN
FOR i := 1 TO n DO
Write (' ');
INC (Xpos)
END
END Spaces;
PROCEDURE Skip;
BEGIN
WriteLn; Xpos := 0;
Spaces (LeftMargin); WriteString (FirstLine);
Spaces (FillUp); WriteString ('page :'); WriteCard (page, 5);
Spaces (FillUp); WriteString (DateString); Eject;
SomethingPrinted := FALSE; INC (page); line := 0;
LineFeed (TopMargin)
END Skip;
PROCEDURE LineFeed (n : CARDINAL);
BEGIN
Xpos := 0;
WHILE n > 0 DO
WriteLn;
INC (line);
IF line > MaxLine THEN Skip END;
DEC (n)
END
END LineFeed;
PROCEDURE WriteLine (str : ARRAY OF CHAR);
VAR i : CARDINAL;
ch : CHAR;
BEGIN
Spaces (LeftMargin);
i := 0;
LOOP
ch := str [i];
IF ch = 0C THEN EXIT END;
IF ch # ASCII.EOF THEN Write (ch) END;
INC (Xpos);
IF Xpos > MaxPos THEN
LineFeed (1);
Spaces (LeftMargin)
END;
INC (i);
IF i > HIGH (str) THEN EXIT END
END;
SomethingPrinted := TRUE;
LineFeed (1)
END WriteLine;
I could omit the CardToString function, since that is part of the FST compiler.
The 'WriteLine' function is a nearly complete rewrite. The lines look the same, or at least mixed up, but the heart of this function needed to be changed to get things working under DOS.
What follows is a stripped version of 'Soup for Linux' MakeDate. DOS MakeDate will produce a simple date string in one format only.
PROCEDURE MakeDate (VAR str : ARRAY OF CHAR);
VAR t : Time;
jaar, maand, dag, uur, minuut : CARDINAL;
substr : ARRAY [0..1] OF CHAR;
BEGIN
str [0] := 0C;
GetTime (t);
uur := t.minute DIV 60; minuut := t.minute MOD 60;
dag := t.day MOD 32; maand := (t.day DIV 32) MOD 16;
jaar := (t.day DIV 512) + 1900 - 2000;
CASE maand OF
1: Append (str, 'Jan'); |
2: Append (str, 'Feb'); |
3: Append (str, 'Mar'); |
4: Append (str, 'Apr'); |
5: Append (str, 'May'); |
6: Append (str, 'Jun'); |
7: Append (str, 'Jul'); |
8: Append (str, 'Aug'); |
9: Append (str, 'Sep'); |
10: Append (str, 'Oct'); |
11: Append (str, 'Nov'); |
12: Append (str, 'Dec');
END;
Append (str, ' ');
CardToString (dag, substr, 2);
Append (str, substr); Append (str, ', 20');
CardToString (jaar, substr, 2);
IF jaar < 10 THEN substr [0] := '0' END;
Append (str, substr); Append (str, ' ');
CardToString (uur, substr, 2); Append (str, substr); Append (str, ':');
CardToString (minuut, substr, 2);
IF minuut < 10 THEN substr [0] := '0' END; Append (str, substr);
END MakeDate;
PROCEDURE Init;
VAR count : CARDINAL;
Option : ARRAY [0..31] OF CHAR;
BEGIN
FirstLineDone := FALSE;
IF ConfigureSoup () = FALSE THEN
MaxLine := maxLINE; TopMargin := topMARGIN;
MaxPos := maxPOS; LeftMargin := leftMARGIN;
PrReset [0] := ASCII.ESC; Append (PrReset, 'E'); (* Printer reset *)
PrSetup [0] := ASCII.ESC; Append (PrSetup, '(10U');
Append (PrSetup, ASCII.ESC); Append (PrSetup, '(s0p16.67h8.5v0s0b0T');
Append (PrSetup, ASCII.ESC); Append (PrSetup, '&l8D')
END;
GetArg (Option, count);
IF count > 0 THEN
RedirectInput (Option);
IF Done THEN
IF Pos ('mod', Option) > HIGH (Option) THEN
Assign (Option, FirstLine);
FirstLineDone := TRUE
END
ELSE
WriteString ('Input file does not exist. Aborting.');
WriteLn;
Terminate (1)
END
ELSE
WriteString ('No input file specified.');
WriteLn;
Terminate (2)
END;
WriteString (PrReset); WriteString (PrSetup);
line := 0;
page := 1;
Xpos := 0;
LineFeed (TopMargin);
SomethingPrinted := FALSE;
MakeDate (DateString)
END Init;
PROCEDURE WrapUp;
BEGIN
IF SomethingPrinted = TRUE THEN
LineFeed (MaxLine - line);
Skip
END;
Write (ASCII.ESC); Write ('E'); (* Printer reset *)
Write (ASCII.EOF)
END WrapUp;
In the DOS port, the two PROCEDURES which were commented out have been left out. So we start with the main
loops.
BEGIN (* Mocka/Linux version *)
Init;
IF NOT FileIO THEN
ReadLine (FirstLine); WriteLine (FirstLine)
END;
FillUp := (MaxPos - 12 - Length (FirstLine) - Length (DateString)) DIV 2;
REPEAT
ReadLine (buffer);
WriteLine (buffer);
IF pos ('<FormFeed>', buffer) < HIGH (buffer) THEN
LineFeed (MaxLine - line);
Skip
END
UNTIL exhausted;
WrapUp;
END soup06.
-----------------------------------------------------------------------------
BEGIN (* FST/DOS version *)
Init;
IF FirstLineDone = FALSE THEN ReadLine (FirstLine) END;
WriteLine (FirstLine);
FillUp := (MaxPos - 12 - Length (FirstLine) - Length (DateString)) DIV 2;
REPEAT
ReadLine (buffer);
WriteLine (buffer);
IF Pos ('<FormFeed>', buffer) < HIGH (buffer) THEN
LineFeed (MaxLine - line);
Skip
END
UNTIL termCH = ASCII.EOF;
WrapUp;
CloseInput
END soup07.
The soup.rc configuration file
Below you see the file soup.rc listed. It is full with comments, so I won't explain anything. It should be obvious after some thoughts.
Soup.rc
This is the configuration file for the program 'soup'.
All parameters in this file are entered between the 'BEGIN' and 'END'
statements below. All text before 'BEGIN' and after 'END' will be ignored.
Please retain the syntax of the parameter settings. The strings can be of
arbitrary length for different printers. Therefore the parameters PrReset
and PrSetup are considered as 'compound statements'. The keyword starts the
interpretation of the string and it is terminated by the first encountered
'END' statement.
Do not place comments in the section between BEGIN/END. If you need to
supply comments, clarifications or (even worse): names, please do so
outside the 'payload' of this configuration file.
BEGIN
TopMargin 1
LeftMargin 8
MaxLine 82
MaxPos 120
PrReset <ESC> E
END
PrSetup <ESC> (10U
<ESC> (s0p16.67h8.5v0s0b0T
<ESC> &l8D
END
END
END
TopMargin nr of lines to skip at top of page
LeftMargin nr of spaces to insert at start of each line
MaxLine when this line is reached, soup initiates a SKIP procedure
MaxPos if a very long line is encountered, and this horizontal
position is reached, then a line wrap is forced, but WITH
the specified indentation
PrReset Freeform string to initiate a printer RESET by software
This string must be ended by an END statement.
PrSetup Free form string to setup the printer for the desired font
and lettertype.
This string can be any length and hence must be ended by
an END statement.
If you need special tokens, make a choice from the following list:
<ESC> = ASCII Escape character (27)
<_> = ASCII space (32)
<Ctrl> X = Control character where X = '@..Z' and '[\]^_'
The <Ctrl> must be entered as such in this file.
The 'X' is just an example.
<Ctrl> @ = 0 <Ctrl> H = 8 <Ctrl> P = 16 <Ctrl> X = 24
<Ctrl> A = 1 <Ctrl> I = 9 <Ctrl> Q = 17 <Ctrl> Y = 25
<Ctrl> B = 2 <Ctrl> J = 10 <Ctrl> R = 18 <Ctrl> Z = 26
<Ctrl> C = 3 <Ctrl> K = 11 <Ctrl> S = 19 <Ctrl> [ = 27
<Ctrl> D = 4 <Ctrl> L = 12 <Ctrl> T = 20 <Ctrl> \ = 28
<Ctrl> E = 5 <Ctrl> M = 13 <Ctrl> U = 21 <Ctrl> ] = 29
<Ctrl> F = 6 <Ctrl> N = 14 <Ctrl> V = 22 <Ctrl> ^ = 30
<Ctrl> G = 7 <Ctrl> O = 15 <Ctrl> W = 23 <Ctrl> _ = 31
DO make sure there is a SPACE between the '<Ctrl>' prefix and the control
code. The space is ESSENTIAL. Also around the <ESC> and <_> special tokens.
I know this is a lot of fuzz, but I have to take into account that the makers
of printer control codes used every conceivable character for some silly
printer command. So I have to revert to this kind of measure.
If you enter a string like:
<ESC> E
the printer command string will consist of just TWO characters: ESC and E.
If you enter a string like
<ESC> ( <Ctrl> P <_> <Ctrl> @ <Ctrl> @
the setup string will contain 6 tokens: 27, '(', 16, ' ', 0, 0
This is the end of the configuration file for soup, which should be located as
/usr/local/prut/soup.rc
In case of problems, contact the maintainer of this executable.
Page created Mar 2004,
Page equipped with GoogleBuster technology