EMIT: filter pages to LPT.

The main difference between 'emit' for DOS and Linux is that the DOS port only accepts the printer as output port. You simply cannot emit pages to a file.
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). If, right after an ASCII 12 (FF, ^L) comes an ASCII.ESC token, the printer reset string is expected.
In the following source code, the Mocka/Linux and the FST/DOS versions are printed right above eachother for easy comparison.

Emit source.

MODULE emit03;          (*  FST/DOS  *)

(*     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 *)
(* 02 : add -h, -v, -a and -e options                           May 29, 2003 *)
(* 03 : port it to FST Modula-2 for DOS                         Oct  9, 2003 *)

IMPORT  ASCII;      (*  FST/DOS  *)

FROM  InOut         IMPORT  Read, Write, WriteLn, WriteLine, WriteString, 
                            RedirectOutput, CloseOutput;
FROM  Strings       IMPORT  CompareStr;
FROM  System        IMPORT  GetArg, Terminate;
FROM  Xchar         IMPORT  UpperString;


TYPE  pageMODE          = (Odd, Even, Range);

VAR   ch                : CHAR;
      page, low, high   : CARDINAL;
      PageMode          : SET OF pageMODE;
      PageOK            : BOOLEAN;

PROCEDURE UserMessage (nr   : CARDINAL);                (*  FST/DOS  *)

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.3 (DOS) of emit.')                    |
     6 :  WriteLine   ('Syntax : emit {odd} {even} {[low..high]} -{E|A|V|H|?}');
          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              relay all odd pages');
          WriteLine   ('emit odd  [4..6]      relay only page 5');
          WriteLine   ('emit even [2..7]      relay pages 2, 4 and 6');
          WriteLine   ('emit odd even [1..20] output pages 1 thru 20');
          WriteLine   ('emit [1..20]          the same');
          WriteLn;
          WriteLine   ('emit accepts options prefixed by a hyphen "-". They are:');
          Write (ASCII.HT);
          WriteLine   ('emit -Email -Author -Help -Version');
          WriteLine   ('although only one option may be present at a time.')    |
     7 :  WriteLine ('E-mail address of owner: fruttenboel@tomaatnet.nl');      |
     8 :  WriteLine ('This software was made by a member of the WISclub.');
          WriteLine ('Check http://wisclub.fol.nl.')
   ELSE
     WriteString ('Strange error encountered. Inform fruttenboel@tomaatnet.nl.');
     WriteLn
   END
END UserMessage;

PROCEDURE GetRange (str  : ARRAY OF CHAR; VAR  lo, hi    : CARDINAL);     (*  FST/DOS  *)

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);
            Terminate (1)
        END;
        lo := lo * 10;
        lo := lo + ORD (str [i]) - ORD ('0');
        INC (i)
    UNTIL str [i] = '.';
    INC (i);
    IF str [i] # '.' THEN
        UserMessage (3);
        Terminate (3)
    END;
    INC (i);
    REPEAT
        IF (i > HIGH (str)) OR (str [i] < '0') OR (str [i] > '9') THEN
            UserMessage (1);          WriteString (str);       UserMessage (2);
            Terminate (1)
        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;                                (*  FST/DOS  *)

VAR     count, i        : CARDINAL;
        option          : ARRAY [0..15] OF CHAR;

BEGIN
    LOOP
        GetArg (option, count);
        IF count > 0 THEN
            UpperString (option);
            IF    CompareStr (option,  'ODD') = 0  THEN  INCL (PageMode, Odd)
            ELSIF CompareStr (option, 'EVEN') = 0  THEN  INCL (PageMode, Even)
            ELSIF option [0] = '['                 THEN  GetRange (option, low, high)
            ELSIF option [0] = '-'                 THEN
                CASE option [1] OF
                 'H', '?'   : UserMessage (6)    |
                 'V'        : UserMessage (5)    |
                 'E'        : UserMessage (7)    |
                 'A'        : UserMessage (8)
                END;
                Terminate (0)
            ELSE
                UserMessage (4);  WriteString (option);   UserMessage (2);
                Terminate (4)
            END
        ELSE
            EXIT
        END
    END;
    IF NOT (Odd IN PageMode) AND NOT (Even IN PageMode) THEN
        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;
    RedirectOutput ("PRN");

    REPEAT                  (*  Make sure Printer Setup string is sent  *)
        Read (ch);
        Write (ch)
    UNTIL ch = ASCII.EOL;

    LOOP                    (*  The main printing loop  *)
        CheckPage;
        IF PageOK THEN
            REPEAT
                Read (ch);
                IF  ch = ASCII.ESC THEN  EXIT        END;
                IF  ch # ASCII.LF  THEN  Write (ch)  END;
            UNTIL ch = ASCII.FF;
        ELSE
            REPEAT
                Read (ch);
                IF  ch = ASCII.ESC  THEN  EXIT  END
            UNTIL ch = ASCII.FF
        END;
        INC (page)
    END;

    LOOP
        Write (ch);
        Read (ch);
        IF  ch = ASCII.EOF  THEN  EXIT  END
    END;
    CloseOutput;
END emit03.
   

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.
The following snippet shows how we can check which elements are inside a set:
   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.

Page created March 2005 and