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  | lpr
   
The 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 second line is equivalent. It is the logical companion of line 1. Between lines 1 and 2, you take the stack of paper from your printer's output tray, flip it over, and put it in the manual feed tray. Now, the other side of the paper is printed. So, emit saves 50% on paper while programming.
Line 3 is an example of the smartness of soup (it 'sees' that input must come from a file, instead of a pipe) in combination with the elegance of emit. Here, you order Unix to print only pages 5, 7 and 9 from the source files.

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.
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.

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,