Emit is a typical Unix filter. It filters out specific data from specific files. In this specific case: emit filters out complete pages of paper from printer specific streams of data. Emit is best used in conjunction with soup. Some commandline examples:
1: cat emit01.mi | soup | emit odd | lpr 2: cat emit01.mi | soup | emit even | lpr 3: soup emit01.mi | emit [5..9] odd | lprThe first line sends the contents of file 'emit01.mi' into a pipe to soup where it is formatted for your specific printer. soup pipes the data through to emit, which only passes the odd pages to the next step, the lpr program.
The options 'odd', 'even' and the range specifiers can be mixed at will. This can produce silly looking command lines such as 'emit odd even [1..99]' for a 3 page document, but emit does not care. Remember, we're programming in Modula-2. Not in one of the curly-braces-languages.
Emit will filter out the printer-setup and -reset sequences from the source streams. It does not need a
configuration file. It only expects a stream of data whereby each page is terminated by a Formfeed token
(ASCII 12 or ^L) and the last formfeed is followed by a Carriage Return (ASCII 13 or ^M) before the printer
reset sequence is issued.
The soup filter is ideally suited to be used with emit since it complies to these two rules.
Emit source.
MODULE emit01;
(* 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. *)
(* Emit is meant to filter out data from piped streams with the options
odd, even or [range]. This program will be most efficent when it is
combined with soup (qv). *)
(* 01 : get it working for 'odd', 'even' and 'range' May 28, 2003 *)
IMPORT ASCII;
FROM InOut IMPORT EOF, Read, Write, WriteBf, WriteLn, WriteString, WriteCard;
FROM Arguments IMPORT ArgTable, GetArgs;
FROM Strings IMPORT Assign, CAPS, StrEq;
TYPE pageMODE = (Odd, Even, Range);
VAR ch : CHAR;
page, low, high : CARDINAL;
command : ArgTable;
PageMode : SET OF pageMODE;
PageOK : BOOLEAN;
PROCEDURE WriteLine (str : ARRAY OF CHAR);
BEGIN
WriteString (str); WriteLn
END WriteLine;
PROCEDURE UserMessage (nr : CARDINAL);
BEGIN
WriteLn;
CASE nr OF
1 : WriteString ('Illegal character in range digits: ') |
2 : WriteLine ('Program aborted. Check syntax with emit -h.'); |
3 : WriteString ('Range identifier syntax error.') |
4 : WriteString ('Illegal commandline parameter: ') |
5 : WriteLine ('This is version 0.1 of emit.') |
6 : WriteLine ('Syntax : emit {odd} {even} {[low..high]}');
WriteLn;
WriteString ('emit will let through all the specified pages ');
WriteLine ("as defined by 'odd', 'even'");
WriteLine ('and/or the range specifier.');
WriteLn;
WriteString ('The range MUST comply to the format [first..last] ');
WriteLine ('INCLUDING the TWO dots');
WriteLine ('and both square brackets.');
WriteLn;
WriteLine ('emit odd will relay all odd pages.');
WriteLine ('emit odd [4..6] will relay only page 5.');
WriteLine ('emit even [2..7] will relay pages 2, 4 and 6.');
WriteLine ('emit odd even [1..20] will output pages 1 thru 20.')
ELSE
WriteString ('Strange error encountered. Inform fruttenboel@tomaatnet.nl.');
WriteLn
END
END UserMessage;
PROCEDURE GetRange (str : ARRAY OF CHAR; VAR lo, hi : CARDINAL);
VAR i, digit : CARDINAL;
BEGIN
INCL (PageMode, Range);
i := 1; lo := 0; hi := 0;
REPEAT
IF (i > HIGH (str)) OR (str [i] < '0') OR (str [i] > '9') THEN
UserMessage (1); WriteString (str); UserMessage (2);
HALT
END;
lo := lo * 10;
lo := lo + ORD (str [i]) - ORD ('0');
INC (i);
UNTIL str [i] = '.';
INC (i);
IF str [i] # '.' THEN
UserMessage (3);
HALT
END;
INC (i);
REPEAT
IF (i > HIGH (str)) OR (str [i] < '0') OR (str [i] > '9') THEN
UserMessage (1); WriteString (str); UserMessage (2);
HALT
END;
hi := hi * 10;
hi := hi + ORD (str [i]) - ORD ('0');
INC (i)
UNTIL str [i] = ']';
IF lo > hi THEN
i := lo;
lo := hi;
hi := i
END;
IF NOT (Even IN PageMode) AND NOT (Odd IN PageMode) THEN
INCL (PageMode, Odd);
INCL (PageMode, Even)
END
END GetRange;
PROCEDURE Init;
VAR count, i : SHORTCARD;
option : ARRAY [0..15] OF CHAR;
BEGIN
GetArgs (count, command);
IF count > 1 THEN
FOR i := 1 TO count - 1 DO
Assign (option, command^ [i]^);
CAPS (option);
IF StrEq (option, 'ODD') THEN INCL (PageMode, Odd)
ELSIF StrEq (option, 'EVEN') THEN INCL (PageMode, Even)
ELSIF option [0] = '[' THEN GetRange (option, low, high)
ELSIF option [0] = '-' THEN
CASE option [1] OF
'h', 'H', '?' : UserMessage (6) |
'v', 'V' : UserMessage (5)
END;
HALT
ELSE
UserMessage (4); WriteString (option); UserMessage (2);
HALT
END
END
ELSE
INCL (PageMode, Odd);
INCL (PageMode, Even)
END;
page := 1
END Init;
PROCEDURE CheckPage;
BEGIN
IF ((Odd IN PageMode) AND (ODD (page))) OR ((Even IN PageMode) AND (NOT ODD (page))) THEN
IF Range IN PageMode THEN
IF (page >= low) AND (page <= high) THEN
PageOK := TRUE
ELSE
PageOK := FALSE
END
ELSE
PageOK := TRUE
END
ELSE
PageOK := FALSE
END
END CheckPage;
BEGIN
Init;
(* WriteLn;
IF Odd IN PageMode THEN WriteString ('Odd'); WriteLn END;
IF Even IN PageMode THEN WriteString ('Even'); WriteLn END;
IF Range IN PageMode THEN WriteString ('Range');
WriteCard (low, 8);
WriteCard (high, 8); WriteLn END;
WriteLn; *)
REPEAT (* Make sure Printer Setup string is sent *)
Read (ch);
Write (ch)
UNTIL ch = ASCII.LF;
CheckPage;
REPEAT
Read (ch);
IF PageOK THEN Write (ch) END;
IF ch = ASCII.FF THEN
INC (page);
CheckPage
END
UNTIL ch = ASCII.CR;
REPEAT
Read (ch);
Write (ch)
UNTIL EOF ();
WriteBf
END emit01.
About the SET constructs.
In this source code I used Modula-2 SET concepts to make the program interact with the user. All options can
be used simultaneously, so I either had to fall back on many discrete BOOLEAN variables or one SET construct.
I chose for the latter.
Remember math in secondary school? With the sets, Venn diagrams and conjunction? Well, that's all implemented
in Modula-2. SET's are an integral part of the language and so there are specific SET operators. Look at the
following snippet:
IF StrEq (option, 'ODD') THEN INCL (PageMode, Odd) ELSIF StrEq (option, 'EVEN') THEN INCL (PageMode, Even)in which the SET variable 'PageMode' is loaded with SET elements. If the command line dictates so, the elements 'Odd', 'Even' or 'Range' are INCLuded in the SET PageMode.
IF Odd IN PageMode THEN WriteString ('Odd'); WriteLn END;
IF Even IN PageMode THEN WriteString ('Even'); WriteLn END;
IF Range IN PageMode THEN WriteString ('Range');
Can one do it more elegant and easier? No, it cannot. If you like mathematics, you *LOVE* Modula-2 (or one of
it's successors). It's all IN the language itself.
A very special PROCEDURE.
Have you noticed the 'CheckPage' function? No? Here you can do it again and in more detail:
PROCEDURE CheckPage;
BEGIN
IF ((Odd IN PageMode) AND (ODD (page))) OR ((Even IN PageMode) AND (NOT ODD (page))) THEN
IF Range IN PageMode THEN
IF (page >= low) AND (page <= high) THEN
PageOK := TRUE
ELSE
PageOK := FALSE
END
ELSE
PageOK := TRUE
END
ELSE
PageOK := FALSE
END
END CheckPage;
This PROCEDURE does not contain any semicolons (apart from the ones at the end of the procedure names).
Page created 2003,
Page equipped with FroogleBuster technology