home *** CD-ROM | disk | FTP | other *** search
/ For Beginners & Professional Hackers / cd.iso / docum / advdos.doc / s1c15 < prev    next >
Encoding:
Text File  |  1992-04-21  |  26.0 KB  |  673 lines

  1. ────────────────────────────────────────────────────────────────────────────
  2. Chapter 15  Filters
  3.  
  4.   A filter is, essentially, a program that operates on a stream of
  5.   characters. The source and destination of the character stream can be
  6.   files, another program, or almost any character device. The transformation
  7.   applied by the filter to the character stream can range from an operation
  8.   as simple as character substitution to one as elaborate as generating
  9.   splines from sets of coordinates.
  10.  
  11.   The standard MS-DOS package includes three simple filters: SORT, which
  12.   alphabetically sorts text on a line-by-line basis; FIND, which searches a
  13.   text stream to match a specified string; and MORE, which displays text one
  14.   screenful at a time.
  15.  
  16.  
  17. System Support for Filters
  18.  
  19.   The operation of a filter program relies on two MS-DOS features that first
  20.   appeared in version 2.0: standard devices and redirectable I/O.
  21.  
  22.   The standard devices are represented by five handles that are originally
  23.   established by COMMAND.COM. Each process inherits these handles from its
  24.   immediate parent. Thus, the standard device handles are already open when
  25.   a process acquires control of the system, and it can use them with
  26.   Interrupt 21H Functions 3FH and 40H for read and write operations
  27.   without further preliminaries. The default assignments of the standard
  28.   device handles are as follows:
  29.  
  30.   Handle             Name                                 Default device
  31.   ──────────────────────────────────────────────────────────────────────────
  32.   0                  stdin (standard input)               CON
  33.   1                  stdout (standard output)             CON
  34.   2                  stderr (standard error)              CON
  35.   3                  stdaux (standard auxiliary)          AUX
  36.   4                  stdprn (standard printer)            PRN
  37.   ──────────────────────────────────────────────────────────────────────────
  38.  
  39.   The CON device is assigned by default to the system's keyboard and video
  40.   display. AUX and PRN are respectively associated by default with COM1 (the
  41.   first physical serial port) and LPT1 (the first parallel printer port).
  42.   You can use the MODE command to redirect LPT1 to one of the serial ports;
  43.   the MODE command will also redirect PRN.
  44.  
  45.   When executing a program by entering its name at the COMMAND.COM prompt,
  46.   you can redirect the standard input, the standard output, or both from
  47.   their default device (CON) to another file, a character device, or a
  48.   process. You do this by including one of the special characters <, >, >>,
  49.   and | in the command line, in the form shown on the following page.
  50.  
  51.   Symbol             Effect
  52.   ──────────────────────────────────────────────────────────────────────────
  53.   < file             Takes standard input from the specified file instead of
  54.                      the keyboard.
  55.  
  56.   < device           Takes standard input from the named device instead of
  57.                      the keyboard.
  58.  
  59.   > file             Sends standard output to the specified file instead of
  60.                      the display.
  61.  
  62.   >> file            Appends standard output to the current contents of the
  63.                      specified file instead of sending it to the display.
  64.  
  65.   > device           Sends standard output to the named device instead of
  66.                      the display.
  67.  
  68.   p1 | p2            Routes standard output of program p1 to become the
  69.                      standard input of program p2. (Output of p1 is said to
  70.                      be piped to p2.)
  71.   ──────────────────────────────────────────────────────────────────────────
  72.  
  73.   For example, the command
  74.  
  75.   C>SORT <MYFILE.TXT >PRN <Enter>
  76.  
  77.   causes the SORT filter to read its input from the file MYFILE.TXT, sort
  78.   the lines alphabetically, and write the resulting text to the character
  79.   device PRN (the logical name for the system's list device).
  80.  
  81.   The redirection requested by the <, >, >>, and | characters takes place at
  82.   the level of COMMAND.COM and is invisible to the program it affects. Any
  83.   other process can achieve a similar effect by redirecting the standard
  84.   input and standard output with Int 21H Function 46H before calling the
  85.   EXEC function (Int 21H Function 4BH) to run a child process.
  86.  
  87.   Note that if a program circumvents MS-DOS to perform its input and output,
  88.   either by calling ROM BIOS functions or by manipulating the keyboard or
  89.   video controller directly, redirection commands placed in the program's
  90.   command line do not have the expected effect.
  91.  
  92.  
  93. How Filters Work
  94.  
  95.   By convention, a filter program reads its text from the standard input
  96.   device and writes the results of its operations to the standard output
  97.   device. When it reaches the end of the input stream, the filter simply
  98.   terminates. As a result, filters are both flexible and simple.
  99.  
  100.   Filter programs are flexible because they do not know, and do not care
  101.   about, the source of the data they process or the destination of their
  102.   output. Thus, any character device that has a logical name within the
  103.   system (CON, AUX, COM1, COM2, PRN, LPT1, LPT2, LPT3, and so on), any file
  104.   on any block device (local or network) known to the system, or any other
  105.   program can supply a filter's input or accept its output. If necessary,
  106.   you can concatenate several functionally simple filters with pipes to
  107.   perform very complex operations.
  108.  
  109.   Although flexible, filters are also simple because they rely on their
  110.   parent processes to supply standard input and standard output handles that
  111.   have already been appropriately redirected. The parent must open or create
  112.   any necessary files, check the validity of logical character-device names,
  113.   and load and execute the preceding or following process in a pipe. The
  114.   filter concerns itself only with the transformation it applies to the
  115.   data.
  116.  
  117.  
  118. Building a Filter
  119.  
  120.   Creating a new filter for MS-DOS is a straightforward process. In its
  121.   simplest form, a filter need only use the handle-oriented read (Interrupt
  122.   21H Function 3FH) and write (Interrupt 21H Function 40H) functions to
  123.   get characters or lines from standard input and send them to standard
  124.   output, performing any desired alterations on the text stream on a
  125.   character-by-character or line-by-line basis.
  126.  
  127.   Figures 15-1 and 15-2 contain prototype character-oriented filters in
  128.   both assembly language and C. In these examples, the translate routine,
  129.   which is called for each character transferred from the standard input to
  130.   the standard output, does nothing at all. As a result, both filters
  131.   function rather like a very slow COPY command. You can quickly turn these
  132.   primitive filters into useful programs by substituting your own translate
  133.   routine.
  134.  
  135.   If you try out these programs, you'll notice that the C prototype filter
  136.   runs much faster than its MASM equivalent. This is because the C runtime
  137.   library is performing hidden blocking and deblocking of the input and
  138.   output stream, whereas the MASM filter is doing exactly what it appears to
  139.   be doing: making two calls to MS-DOS for each character processed. You can
  140.   easily restore the MASM filter's expected speed advantage by adapting it
  141.   to read and write lines instead of single characters.
  142.  
  143.   ──────────────────────────────────────────────────────────────────────────
  144.            name      proto
  145.            page      55,132
  146.            title     PROTO.ASM--prototype filter
  147.   ;
  148.   ; PROTO.ASM:  prototype character-oriented filter
  149.   ;
  150.   ; Copyright 1988 Ray Duncan
  151.   ;
  152.  
  153.   stdin   equ     0               ; standard input handle
  154.   stdout  equ     1               ; standard output handle
  155.   stderr  equ     2               ; standard error handle
  156.  
  157.   cr      equ     0dh             ; ASCII carriage return
  158.   lf      equ     0ah             ; ASCII linefeed
  159.  
  160.   _TEXT   segment word public 'CODE'
  161.  
  162.           assume  cs:_TEXT,ds:_DATA,ss:STACK
  163.  
  164.   main    proc    far             ; entry point from MS-DOS
  165.  
  166.           mov     ax,_DATA        ; set DS = our data segment
  167.           mov     ds,ax
  168.  
  169.   main1:                          ; read char from stdin...
  170.           mov     dx,offset char  ; DS:DX = buffer address
  171.           mov     cx,1            ; CX = length to read
  172.           mov     bx,stdin        ; BX = standard input handle
  173.           mov     ah,3fh          ; function 3fh = read
  174.           int     21h             ; transfer to MS-DOS
  175.           jc      main3           ; if error, terminate
  176.  
  177.           cmp     ax,1            ; any character read?
  178.           jne     main2           ; if end of file, terminate
  179.  
  180.           call    translate       ; translate character
  181.  
  182.                                   ; write char to stdout...
  183.           mov     dx,offset char  ; DS:DX = buffer address
  184.           mov     cx,1            ; CX = length to write
  185.           mov     bx,stdout       ; BX = standard output handle
  186.           mov     ah,40h          ; function 40h = write
  187.           int     21h             ; transfer to MS-DOS
  188.           jc      main3           ; if error, terminate
  189.           cmp     ax,1            ; was character written?
  190.           jne     main3           ; if disk full, terminate
  191.  
  192.           jmp     main1           ; get another character
  193.  
  194.   main2:                          ; end of file reached
  195.           mov     ax,4c00h        ; function 4ch = terminate
  196.                                   ; return code = 0
  197.           int     21h             ; transfer to MS-DOS
  198.  
  199.   main3:                          ; error or disk full
  200.           mov     ax,4c01h        ; function 4ch = terminate
  201.                                   ; return code = 1
  202.           int     21h             ; transfer to MS-DOS
  203.  
  204.   main    endp
  205.  
  206.   ;
  207.   ; Perform any necessary translation on character
  208.   ; from standard input stored in variable 'char'.
  209.   ; This example simply leaves character unchanged.
  210.   ;
  211.   translate proc  near
  212.  
  213.           ret                     ; does nothing
  214.  
  215.   translate endp
  216.  
  217.   _TEXT   ends
  218.  
  219.  
  220.   _DATA   segment word public 'DATA'
  221.  
  222.   char    db      0               ; storage for input character
  223.  
  224.   _DATA   ends
  225.  
  226.  
  227.   STACK   segment para stack 'STACK'
  228.  
  229.           dw      64 dup (?)
  230.  
  231.   STACK   ends
  232.  
  233.           end     main            ; defines program entry point
  234.   ──────────────────────────────────────────────────────────────────────────
  235.  
  236.   Figure 15-1.  PROTO.ASM, the source code for a prototype
  237.   character-oriented MASM filter.
  238.  
  239.   ──────────────────────────────────────────────────────────────────────────
  240.   /*
  241.       PROTO.C:  prototype character-oriented filter
  242.  
  243.       Copyright 1988 Ray Duncan
  244.   */
  245.  
  246.   #include <stdio.h>
  247.  
  248.   main(int argc, char *argv[])
  249.   {
  250.       char ch;
  251.  
  252.       while((ch=getchar()) != EOF)    /* read a character            */
  253.       {
  254.           ch = translate(ch);         /* translate it if necessary   */
  255.  
  256.           putchar(ch);                /* write the character         */
  257.       }
  258.       exit(0);                        /* terminate at end of file    */
  259.   }
  260.  
  261.  
  262.   /*
  263.       Perform any necessary translation on character
  264.       from input file. This example simply returns
  265.       the same character.
  266.   */
  267.  
  268.   int translate(char ch)
  269.   {
  270.       return (ch);
  271.   }
  272.   ──────────────────────────────────────────────────────────────────────────
  273.  
  274.   Figure 15-2.  PROTO.C, the source code for a prototype character-oriented
  275.   C filter.
  276.  
  277.  
  278. The CLEAN Filter
  279.  
  280.   As a more practical example of MS-DOS filters, let's look at a simple but
  281.   very useful filter called CLEAN. Figures 15-3 and 15-4 show the
  282.   assembly-language and C source code for this filter. CLEAN processes a
  283.   text stream by stripping the high bit from all characters, expanding tabs
  284.   to spaces, and throwing away all control codes except carriage returns,
  285.   linefeeds, and formfeeds. Consequently, CLEAN can transform almost any
  286.   kind of word-processed document file into a plain ASCII text file.
  287.  
  288.   ──────────────────────────────────────────────────────────────────────────
  289.           name    clean
  290.           page    55,132
  291.           title   CLEAN--Text-file filter
  292.   ;
  293.   ; CLEAN.ASM     Filter to turn document files into
  294.   ;               normal text files.
  295.   ;
  296.   ; Copyright 1988 Ray Duncan
  297.   ;
  298.   ; Build:        C>MASM CLEAN;
  299.   ;               C>LINK CLEAN;
  300.   ;
  301.   ; Usage:        C>CLEAN  <infile  >outfile
  302.   ;
  303.   ; All text characters are passed through with high
  304.   ; bit stripped off. Formfeeds, carriage returns,
  305.   ; and linefeeds are passed through. Tabs are expanded
  306.   ; to spaces. All other control codes are discarded.
  307.   ;
  308.  
  309.   tab     equ     09h             ; ASCII tab code
  310.   lf      equ     0ah             ; ASCII linefeed
  311.   ff      equ     0ch             ; ASCII formfeed
  312.   cr      equ     0dh             ; ASCII carriage return
  313.   blank   equ     020h            ; ASCII space code
  314.   eof     equ     01ah            ; Ctrl-Z end-of-file
  315.  
  316.   tabsiz  equ     8               ; width of tab stop
  317.  
  318.   bufsiz  equ     128             ; size of input and
  319.                                   ; output buffers
  320.  
  321.   stdin   equ     0000            ; standard input handle
  322.   stdout  equ     0001            ; standard output handle
  323.   stderr  equ     0002            ; standard error handle
  324.  
  325.  
  326.   _TEXT   segment word public 'CODE'
  327.  
  328.           assume  cs:_TEXT,ds:_DATA,es:_DATA,ss:STACK
  329.  
  330.   clean   proc    far             ; entry point from MS-DOS
  331.  
  332.           push    ds              ; save DS:0000 for final
  333.           xor     ax,ax           ; return to MS-DOS, in case
  334.           push    ax              ; function 4ch can't be used
  335.           mov     ax,_DATA        ; make data segment addressable
  336.           mov     ds,ax
  337.           mov     es,ax
  338.  
  339.           mov     ah,30h          ; check version of MS-DOS
  340.           int     21h
  341.           cmp     al,2            ; MS-DOS 2.0 or later?
  342.           jae     clean1          ; jump if version OK
  343.  
  344.                                   ; MS-DOS 1, display error
  345.                                   ; message and exit...
  346.           mov     dx,offset msg1  ; DS:DX = message address
  347.           mov     ah,9            ; function 9 = display string
  348.           int     21h             ; transfer to MS-DOS
  349.           ret                     ; then exit the old way
  350.  
  351.   clean1: call    init            ; initialize input buffer
  352.  
  353.   clean2: call    getc            ; get character from input
  354.           jc      clean9          ; exit if end of stream
  355.  
  356.           and     al,07fh         ; strip off high bit
  357.  
  358.           cmp     al,blank        ; is it a control char?
  359.           jae     clean4          ; no, write it
  360.  
  361.           cmp     al,eof          ; is it end of file?
  362.           je      clean8          ; yes, write EOF and exit
  363.  
  364.           cmp     al,tab          ; is it a tab?
  365.           je      clean6          ; yes, expand it to spaces
  366.  
  367.           cmp     al,cr           ; is it a carriage return?
  368.           je      clean3          ; yes, go process it
  369.  
  370.           cmp     al,lf           ; is it a linefeed?
  371.           je      clean3          ; yes, go process it
  372.  
  373.           cmp     al,ff           ; is it a formfeed?
  374.           jne     clean2          ; no, discard it
  375.  
  376.   clean3: mov     column,0        ; if CR, LF, or FF,
  377.           jmp     clean5          ; reset column to zero
  378.  
  379.   clean4: inc     column          ; if non-control character,
  380.                                   ; increment column counter
  381.   clean5: call    putc            ; write char to stdout
  382.           jnc     clean2          ; if disk not full,
  383.                                   ; get another character
  384.  
  385.                                   ; write failed...
  386.           mov     dx,offset msg2  ; DS:DX = error message
  387.           mov     cx,msg2_len     ; CX = message length
  388.           mov     bx,stderr       ; BX = standard error handle
  389.           mov     ah,40h          ; function 40h = write
  390.           int     21h             ; transfer to MS-DOS
  391.  
  392.           mov     ax,4c01h        ; function 4ch = terminate
  393.                                   ; return code = 1
  394.           int     21h             ; transfer to MS-DOS
  395.  
  396.   clean6: mov     ax,column       ; tab code detected
  397.           cwd                     ; tabsiz - (column MOD tabsiz)
  398.           mov     cx,tabsiz       ; is number of spaces needed
  399.           idiv    cx              ; to move to next tab stop
  400.           sub     cx,dx
  401.  
  402.           add     column,cx       ; also update column counter
  403.  
  404.   clean7: push    cx              ; save spaces counter
  405.  
  406.           mov     al,blank        ; write an ASCII space
  407.           call    putc
  408.  
  409.           pop     cx              ; restore spaces counter
  410.           loop    clean7          ; loop until tab stop
  411.  
  412.           jmp     clean2          ; get another character
  413.  
  414.   clean8: call    putc            ; write EOF mark
  415.  
  416.   clean9: call    flush           ; write last output buffer
  417.           mov     ax,4c00h        ; function 4ch = terminate
  418.                                   ; return code = 0
  419.           int     21h             ; transfer to MS-DOS
  420.  
  421.   clean   endp
  422.  
  423.  
  424.   getc    proc    near            ; get character from stdin
  425.                                   ; returns carry = 1 if
  426.                                   ; end of input, else
  427.                                   ; AL = char, carry = 0
  428.           mov     bx,iptr         ; get input buffer pointer
  429.           cmp     bx,ilen         ; end of buffer reached?
  430.           jne     getc1           ; not yet, jump
  431.  
  432.                                   ; more data is needed...
  433.           mov     bx,stdin        ; BX = standard input handle
  434.           mov     cx,bufsiz       ; CX = length to read
  435.           mov     dx,offset ibuff ; DS:DX = buffer address
  436.           mov     ah,3fh          ; function 3fh = read
  437.           int     21h             ; transfer to MS-DOS
  438.           jc      getc2           ; jump if read failed
  439.  
  440.           or      ax,ax           ; was anything read?
  441.           jz      getc2           ; jump if end of input
  442.  
  443.           mov     ilen,ax         ; save length of data
  444.           xor     bx,bx           ; reset buffer pointer
  445.  
  446.   getc1:  mov     al,[ibuff+bx]   ; get character from buffer
  447.           inc     bx              ; bump buffer pointer
  448.  
  449.           mov     iptr,bx         ; save updated pointer
  450.           clc                     ; return character in AL
  451.           ret                     ; and carry = 0 (clear)
  452.  
  453.   getc2:  stc                     ; end of input stream
  454.           ret                     ; return carry = 1 (set)
  455.  
  456.   getc    endp
  457.  
  458.  
  459.   putc    proc    near            ; send character to stdout,
  460.                                   ; returns carry = 1 if
  461.                                   ; error, else carry = 0
  462.  
  463.           mov     bx,optr         ; store character into
  464.           mov     [obuff+bx],al   ; output buffer
  465.  
  466.           inc     bx              ; bump buffer pointer
  467.           cmp     bx,bufsiz       ; buffer full?
  468.           jne     putc1           ; no, jump
  469.  
  470.  
  471.           mov     bx,stdout       ; BX = standard output handle
  472.           mov     cx,bufsiz       ; CX = length to write
  473.           mov     dx,offset obuff ; DS:DX = buffer address
  474.           mov     ah,40h          ; function 40h = write
  475.           int     21h             ; transfer to MS-DOS
  476.           jc      putc2           ; jump if write failed
  477.  
  478.           cmp     ax,cx           ; was write complete?
  479.           jne     putc2           ; jump if disk full
  480.  
  481.           xor     bx,bx           ; reset buffer pointer
  482.  
  483.   putc1:  mov     optr,bx         ; save buffer pointer
  484.           clc                     ; write successful,
  485.           ret                     ; return carry = 0 (clear)
  486.  
  487.   putc2:  stc                     ; write failed or disk full,
  488.           ret                     ; return carry = 1 (set)
  489.  
  490.   putc    endp
  491.  
  492.  
  493.   init    proc    near            ; initialize input buffer
  494.  
  495.           mov     bx,stdin        ; BX = standard input handle
  496.           mov     cx,bufsiz       ; CX = length to read
  497.           mov     dx,offset ibuff ; DS:DX = buffer address
  498.           mov     ah,3fh          ; function 3fh = read
  499.           int     21h             ; transfer to MS-DOS
  500.           jc      init1           ; jump if read failed
  501.           mov     ilen,ax         ; save actual bytes read
  502.   init1:  ret
  503.  
  504.   init    endp
  505.  
  506.  
  507.   flush   proc    near            ; flush output buffer
  508.  
  509.           mov     cx,optr         ; CX = bytes to write
  510.           jcxz    flush1          ; exit if buffer empty
  511.           mov     dx,offset obuff ; DS:DX = buffer address
  512.           mov     bx,stdout       ; BX = standard output handle
  513.           mov     ah,40h          ; function 40h = write
  514.           int     21h             ; transfer to MS-DOS
  515.   flush1: ret
  516.  
  517.   flush   endp
  518.  
  519.   _TEXT   ends
  520.   _DATA   segment word public 'DATA'
  521.  
  522.   ibuff   db      bufsiz dup (0)  ; input buffer
  523.   obuff   db      bufsiz dup (0)  ; output buffer
  524.  
  525.   iptr    dw      0               ; ibuff pointer
  526.   ilen    dw      0               ; bytes in ibuff
  527.   optr    dw      0               ; obuff pointer
  528.  
  529.   column  dw      0               ; current column counter
  530.  
  531.   msg1    db      cr,lf
  532.           db      'clean: need MS-DOS version 2 or greater.'
  533.           db      cr,lf,'$'
  534.  
  535.   msg2    db      cr,lf
  536.           db      'clean: disk is full.'
  537.           db      cr,lf
  538.   msg2_len equ    $-msg2
  539.  
  540.   _DATA   ends
  541.  
  542.  
  543.   STACK   segment para stack 'STACK'
  544.  
  545.           dw      64 dup (?)
  546.  
  547.   STACK   ends
  548.  
  549.           end     clean
  550.   ──────────────────────────────────────────────────────────────────────────
  551.  
  552.   Figure 15-3.  CLEAN.ASM, the source code for the MASM version of the CLEAN
  553.   filter.
  554.  
  555.   ──────────────────────────────────────────────────────────────────────────
  556.   /*
  557.       CLEAN.C     Filter to turn document files into
  558.                   normal text files.
  559.  
  560.       Copyright 1988 Ray Duncan
  561.  
  562.       Compile:    C>CL CLEAN.C
  563.  
  564.       Usage:      C>CLEAN  <infile >outfile
  565.  
  566.       All text characters are passed through with high bit stripped
  567.       off. Formfeeds, carriage returns, and linefeeds are passed
  568.       through. Tabs are expanded to spaces. All other control codes
  569.       are discarded.
  570.   */
  571.  
  572.   #include <stdio.h>
  573.  
  574.   #define TAB_WIDTH   8              /* width of a tab stop     */
  575.   #define TAB     '\x09'             /* ASCII tab character     */
  576.   #define LF      '\x0A'             /* ASCII linefeed          */
  577.   #define FF      '\x0C'             /* ASCII formfeed          */
  578.   #define CR      '\x0D'             /* ASCII carriage return   */
  579.   #define BLANK   '\x20'             /* ASCII space code        */
  580.   #define EOFMK   '\x1A'             /* Ctrl-Z end of file      */
  581.  
  582.  
  583.   main(int argc, char *argv[])
  584.   {
  585.       char c;                        /* character from stdin    */
  586.       int col = 0;                   /* column counter          */
  587.  
  588.       while((c = getchar()) != EOF)  /* read input character    */
  589.       {
  590.           c &= 0x07F;                /* strip high bit          */
  591.  
  592.           switch(c)                  /* decode character        */
  593.           {
  594.               case LF:               /* if linefeed or          */
  595.               case CR:               /* carriage return,        */
  596.                   col=0;             /* reset column count      */
  597.  
  598.               case FF:               /* if formfeed, carriage   */
  599.                   wchar(c);          /* return, or linefeed,    */
  600.                   break;             /* pass character through  */
  601.  
  602.               case TAB:              /* if tab, expand to spaces*/
  603.                   do wchar(BLANK);
  604.                   while((++col % TAB_WIDTH) != 0);
  605.                   break;
  606.  
  607.               default:               /* discard other control   */
  608.                   if(c >= BLANK)     /* characters, pass text   */
  609.                   {                  /* characters through      */
  610.                       wchar(c);
  611.                       col++;         /* bump column counter     */
  612.                   }
  613.                   break;
  614.           }
  615.       }
  616.       wchar(EOFMK);                  /* write end-of-file mark  */
  617.       exit(0);
  618.   }
  619.  
  620.  
  621.   /*
  622.       Write a character to the standard output. If
  623.       write fails, display error message and terminate.
  624.   */
  625.  
  626.   wchar(char c)
  627.   {
  628.       if((putchar(c) == EOF) && (c != EOFMK))
  629.       {
  630.           fputs("clean: disk full",stderr);
  631.           exit(1);
  632.       }
  633.   }
  634.   ──────────────────────────────────────────────────────────────────────────
  635.  
  636.   Figure 15-4.  CLEAN.C, the source code for the C version of the CLEAN
  637.   filter.
  638.  
  639.   When using the CLEAN filter, you must specify the source and destination
  640.   files with redirection parameters in the command line; otherwise, CLEAN
  641.   will simply read the keyboard and write to the display. For example, to
  642.   filter the document file MYFILE.DOC and leave the result in the file
  643.   MYFILE.TXT, you would enter the following command:
  644.  
  645.   C>CLEAN <MYFILE.DOC >MYFILE.TXT  <Enter>
  646.  
  647.   (Note that the original file, MYFILE.DOC, is unchanged.)
  648.  
  649.   One valuable application of this filter is to rescue assembly-language
  650.   source files. If you accidentally edit such a source file in document
  651.   mode, the resulting file may cause the assembler to generate spurious or
  652.   confusing error messages. CLEAN lets you turn the source file back into
  653.   something the assembler can cope with, without losing the time you spent
  654.   to edit it.
  655.  
  656.   Another handy application for CLEAN is to list a word-processed document
  657.   in raw form on the printer, using a command such as
  658.  
  659.   C>CLEAN <MYFILE.DOC >PRN  <Enter>
  660.  
  661.   Contrasting the C and assembly-language versions of this filter provides
  662.   some interesting statistics. The C version contains 79 lines and compiles
  663.   to a 5889-byte .EXE file, whereas the assembly-language version contains
  664.   265 lines and builds an 1107-byte .EXE file. The size and execution-speed
  665.   advantages of implementing such tools in assembly language is obvious,
  666.   even compared with such an excellent compiler as the Microsoft C
  667.   Optimizing Compiler. However, you must balance performance considerations
  668.   against the time and expense required for programming, particularly when a
  669.   program will not be used very often.
  670.  
  671.  
  672.  
  673.