home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / jsage / znode3 / uploads / dio21.lbr / DIO.CZ / DIO.C
Encoding:
Text File  |  1992-09-12  |  11.5 KB  |  407 lines

  1. /* dio.c
  2.  *  Directed I/O package for BDS C vZ2.0
  3.  *
  4.  * Version 2.1 -- September 12, 1992 -- Gene Pizzetta
  5.  *    Corrected bug in redirected output to NUL:.  Output was being echoed
  6.  *    to the screen because of a misplaced label.
  7.  *
  8.  * Version 2.0 -- August 15, 1992 -- Gene Pizzetta
  9.  *    I couldn't get piping to work properly under ZCPR3, so modification
  10.  *    was necessary.  Then I missed ZCPR's path search and ARUNZ for the
  11.  *    piped-to utilities, so more modification was necessary.  The remaining
  12.  *    command line after the pipe character ("|") is now loaded into the
  13.  *    multiple command line buffer, so things work as you would expect.  Now
  14.  *    sets the program error flag if an error is encountered.  Output can
  15.  *    now be appended to an existing file, sent to a printer (LST:), or
  16.  *    dumped to the bit bucket (NUL:).  Piping is only available under
  17.  *    ZCPR3.
  18.  *
  19.  * June 1986 -- Leor Zolman
  20.  *    For BDS C v1.6.
  21.  *
  22.  * The following functions make up the directed I/O library:
  23.  *
  24.  *  1. dioinit(&argc,argv)    Make this the first thing you do in your
  25.  *                "main" function, to process redirection
  26.  *                commands on the CP/M command line.
  27.  *
  28.  *  2. getchar()        Gets a character from the keyboard, or from
  29.  *                a directed input file if one was specified
  30.  *                on the command line.
  31.  *
  32.  * 3. putchar(c)        Puts a character out to the console, or to
  33.  *                a directed output file if one was specified
  34.  *                on the command line.
  35.  *
  36.  * 4. dioflush()        Flushes directed output file, if open, and
  37.  *                closes all directed I/O files (if any).
  38.  *                This function must be called before your
  39.  *                program exits or returns to CP/M.
  40.  *
  41.  * To activate redirection, several special arguments may be given on the
  42.  * command line to programs using this module:
  43.  *
  44.  *    >filename    "putchar" places characters into the given file
  45.  *            instead of to the console.  An existing file with
  46.  *            same name will be overwritten.
  47.  *
  48.  *    +filename    like ">filename" (above) except that the characters
  49.  *            are also sent to the console.
  50.  *
  51.  *    >>filename    characters to be appended to the given existing file
  52.  *            instead of being sent to the console.  If no such
  53.  *            file exists, it will be created.
  54.  *
  55.  *    +>filename    like ">>filename" (above) except that the characters
  56.  *            are also echoed at the console.
  57.  *
  58.  *    >LST:        characters are sent to the printer instead of to
  59.  *            the console.  ">>LST:" works the same.
  60.  *
  61.  *    +LST:        like ">LST:" (above) except that the characters are
  62.  *            also echoed at the console.  "+>LST:" works the same.
  63.  *
  64.  *    >NUL:        characters are sent to the "bit bucket".  ">>NUL:"
  65.  *            works the same.
  66.  *
  67.  *    <filename    "getchar" returns characters from the given file
  68.  *            instead of from the keyboard.
  69.  *
  70.  *    cmd1 |cmd2    characters from the standard output of "cmd1" are
  71.  *            sent to the standard input of "cmd2".  Both "cmd1"
  72.  *            and "cmd2" must be compiled with this DIO package.
  73.  *
  74.  * There must never be any spaces between >, +, <, or | and the following
  75.  * filename.
  76.  *
  77.  * When no "<" or "|" operator is used, standard input comes from the
  78.  * console and all standard line editing characters are recognized (a 
  79.  * new feature of v1.45).  To indicate end-of-file, you must type
  80.  *    ^Z <CR>
  81.  * (control-Z followed by a carriage-return.)
  82.  *
  83.  * When no ">" or "|" operator is used, standard output goes to the
  84.  * console.
  85.  *
  86.  * A program allowing redirection must have the following form:
  87.  *
  88.  *    #include <stdio.h>        /* standard header file       */
  89.  *    #include "dio.h"        /* directed I/O header       */
  90.  *
  91.  *    ...                /* other externals, if any */
  92.  *
  93.  *    main(argc,argv)
  94.  *    char **argv;
  95.  *    {
  96.  *        ...            /* declarations           */
  97.  *        dioinit(&argc,argv);    /* initialize redirection  */
  98.  *        ...
  99.  *        ...            /* body of program using   */
  100.  *        ...            /*  getchar and putchar       */
  101.  *        ...            /*  or any routines that   */
  102.  *        ...            /*  use stdin and stdout   */
  103.  *        ...
  104.  *        dioflush();        /* clean up redirection       */
  105.  *    }
  106.  *
  107.  * NOTES:
  108.  *
  109.  *   1. Console input may be raw (unbuffered, one character at a time) or
  110.  *    buffered (entire line must be typed before characters are returned,
  111.  *    allowing standard editing features, and characters come back one
  112.  *    at a time AFTER the entire line is typed).  The default is raw.
  113.  *    For buffered console input, change BUF_CONS definition in DIO.H from
  114.  *    0 to 1, then recompile this file and all files in your program.
  115.  *
  116.  *   2. Redirection and pipes work only for TEXT.  This mechanism should
  117.  *    not be used for binary data.  Piping works only under ZCPR3; other-
  118.  *    wise, DIO exits with a message is piping is attempted.
  119.  *
  120.  *   3. Do not define your own "getchar" or "putchar" when using the DIO
  121.  *    package, or things will get very confused.
  122.  *
  123.  *   4. Multiple pipes may be chained on one command line.  For example, the
  124.  *    following command feeds the output of program "foo" into the input
  125.  *    of program "bar", the output of "bar" into the input of program
  126.  *    "zot", and the output of "zot" into a file called "output":
  127.  *
  128.  *        A>foo arg1 |bar |zot arg2 arg3 >output <cr>
  129.  *
  130.  *    "arg1" is an actual argument to "foo", and "arg2" and "arg3" are
  131.  *    actual arguments to "zot".  This illustrates how actual arguments
  132.  *    may be interspersed with redirection commands.  The programs see
  133.  *    the actual arguments, but command line preprocessing handled by the
  134.  *    "dioinit" function cause the programs to never need to know about
  135.  *    the redirection commands.  All three programs ("foo", "bar" and
  136.  *    "zot") must have been compiled and linked with the DIO package for
  137.  *    everything to work properly.  In addition, "bar" and "zot" must be
  138.  *    located along the ZCPR3 path or must be available via an extended
  139.  *    processor like ARUNZ or LX.
  140.  */
  141.  
  142. #include <stdio.h>
  143. #include "dio.h"
  144.  
  145. #define CON_INPUT    1        /* BDOS call to read console       */
  146. #define CON_OUTPUT    2        /* BDOS call to write to console   */
  147. #define CON_STATUS    11        /* BDOS call to interrogate status */
  148.  
  149. #define CONTROL_C    3        /* Quit character           */
  150. #define INPIPE        2        /* bit setting to indicate directed
  151.                        input from a temp. pipe file    */
  152. #define VERBOSE        2        /* bit setting to indicate output is to
  153.                        go to console AND directed output */
  154.  
  155. /* 
  156.  * The "dioinit" function must be called at the beginning of the
  157.  * "main" function:
  158.  */
  159.  
  160. #define argc *argcp
  161.  
  162. dioinit(argcp,argv)
  163. int *argcp;
  164. char **argv;
  165. {
  166.     int i,j, argcount;
  167.  
  168.     /* No directed I/O by default */
  169.     _diflag = _doflag = _pipef = _apflag = _prflag = _nulflag = FALSE;
  170.     _nullpos = &argv[argc];
  171.     cmd = cmdstr;            /* pointer to string for MCL    */
  172.  
  173.     /* Assume no append */
  174.     fnptr = 1;            /* pointer to output filename    */
  175.     fmode = "w";            /* pointer to file open mode    */
  176.  
  177. #if BUF_CONS
  178.     _conbuf[0] = 0;            /* no characters in buffer yet    */
  179.     _conbufp = _conbuf;        /* point to null buffer     */
  180. #endif
  181.  
  182.     argcount = 1;
  183.  
  184.     for (i = 1; i < argc; i++)    /* Scan the command line for > and < */
  185.     {
  186.         if (_pipef) break;
  187.         switch(*argv[i])
  188.         {
  189.  
  190.            case '<':        /* Check for directed input:    */
  191.             if (!argv[i][1]) goto barf;
  192.             if ((_dibuf = fopen(&argv[i][1], "r")) == NULL)
  193.             {
  194.                 fputs("DIO: Can't open " ,stderr);
  195.                 fputs(&argv[i][1],stderr);
  196.                 fputs("\n",stderr);
  197.                 exit(10);    /* file not found error */
  198.             }
  199.             _diflag = TRUE;
  200.             if (strcmp(argv[i],"<TEMPIN.$$$") == 0)
  201.                  _diflag |= INPIPE;
  202.             goto movargv;
  203.  
  204.            case '|':    /* Check for pipe: */
  205.             if (zenv() == NULL)
  206.             {
  207.                 fputs("DIO: Pipes require ZCPR3",stderr);
  208.                 exit(4);
  209.             }
  210.             _pipef++;
  211.             _pipedest = &argv[i][fnptr]; /* save prog name for MCL */
  212.             if (argv[i][fnptr]) 
  213.             {
  214.                 argv[i] = ".TEMPOUT.$$$";  /* temp. output */
  215.                 _savei = &argv[i];
  216.             }
  217.             goto foo;
  218.  
  219.            case '+': 
  220.             _doflag |= VERBOSE;
  221.             
  222. foo:           case '>':    /* Check for directed output    */
  223.             if (argv[i][1] == '>')    /* if we have a second ">" */
  224.             {
  225.                 _apflag++;    /* then we're appending */
  226.                 fmode = "a";    /* open file in append mode */
  227.                 fnptr++;    /* that is, argv[i][2] */
  228.             }
  229.             if (!argv[i][fnptr])    /* any file or device name? */
  230.             {
  231. barf:                fputs("DIO: Bad redirection/pipe specifier",stderr);
  232.                 exit(4);    /* miscellaneous error */
  233.             }
  234.             if (!strcmp(&argv[i][fnptr],"NUL:"))
  235.             {
  236.                 _nulflag++;
  237.                 goto setdo;
  238.             }
  239.             if (!strcmp(&argv[i][fnptr],"LST:"))
  240.             {
  241.                 _prflag++;
  242.                 goto setdo;
  243.             }
  244.             if (!_apflag) unlink(&argv[i][fnptr]);
  245.             if ((_dobuf = fopen(&argv[i][fnptr], fmode)) == NULL)
  246.             {
  247.                 fputs("DIO: Can't open " ,stderr);
  248.                 fputs(&argv[i][1],stderr);
  249.                 fputs("\n",stderr);
  250.                 exit(11);    /* directory full error */
  251.             }
  252. setdo:            _doflag++;
  253.  
  254. movargv:        if (!_pipef)
  255.             {
  256.                 for (j = i; j < argc; j++) argv[j] = argv[j+1];
  257.                 (argc)--;
  258.                 i--;
  259.                 _nullpos--;
  260.             }
  261.             else
  262.             {
  263.                 argc = argcount;
  264.                 argv[argc] = 0;
  265.             }
  266.             break;
  267.  
  268.             default:    /* handle normal arguments: */
  269.             argcount++;
  270.         }
  271.     }
  272. }
  273.  
  274. #undef argc
  275.  
  276. /*
  277.  * The "dioflush" function must be called before exiting the program.
  278.  */
  279.  
  280. dioflush()
  281. {
  282.     if (_diflag)
  283.     {
  284.         fclose(_dibuf);
  285.         if (_diflag & INPIPE) unlink("tempin.$$$");
  286.     }
  287.     
  288.     if (_prflag || _nulflag) return;
  289.     
  290.     if (_doflag)
  291.     {
  292.         putc(CPMEOF,_dobuf);
  293.         fclose(_dobuf);
  294.         unlink("tempin.$$$");    /* in case previous pipe was aborted */
  295.         rename("tempout.$$$","tempin.$$$");
  296.         if (_pipef) 
  297.         {
  298.             *_savei = "<TEMPIN.$$$";
  299.             *_nullpos = NULL;
  300.             strcpy(cmd,_pipedest);
  301.             while (!(_savei == _nullpos))
  302.             {
  303.                 strcat(cmd," ");
  304.                 strcat(cmd,*_savei);
  305.                 _savei++;
  306.             }
  307.             if (putcl(cmd) == FALSE)    /* load ZCPR MCL */
  308.             {
  309.                 fputs("\007DIO: Command line overflow\n",
  310.                   stderr);
  311.                 exit(15);
  312.             }
  313.         }
  314.     }
  315. }
  316.  
  317.  
  318. /*
  319.  * This version of "getchar" replaces the regular version when using
  320.  * directed I/O. Note that the "BUF_CONS" defined symbol (in DIO.H)
  321.  * controls whether the console input is to be raw or buffered (see
  322.  * item 0. in NOTES above)
  323.  */
  324.  
  325. getchar()
  326. {
  327.     int c;
  328.  
  329.     if (_diflag)
  330.     {
  331.         if ((c = getc(_dibuf)) == '\r') c = getc(_dibuf);
  332.     }
  333.     else
  334.  
  335. #if BUF_CONS    /* For buffered console input, get a line of text   */
  336.     {    /* from the BDOS (using "gets"), & insert newline:  */
  337.         if (!*_conbufp)
  338.         {   
  339.             gets(_conbufp = _conbuf);
  340.             _conbuf[strlen(_conbuf) + 1] = '\0';
  341.             _conbuf[strlen(_conbuf)] = '\n';
  342.         }
  343.         c = *_conbufp++;
  344.     }
  345. #else        /* for raw console input, simulate normal "getchar": */
  346.         if ((c = bdos(CON_INPUT) & 0x7f) == CONTROL_C) exit(4);
  347. #endif
  348.  
  349.     if (c == CPMEOF) return EOF;         /* Control-Z is EOF key     */
  350.  
  351.     if (c == '\r') 
  352.     {
  353.         c = '\n';
  354. #if !BUF_CONS
  355.         if (!_diflag) bdos(2,'\n');  /* echo LF after CR to console */
  356. #endif
  357.     }
  358.     return c;
  359. }
  360.  
  361.  
  362. /*
  363.  * This version of "putchar" replaces the regular version when using
  364.  * directed I/O:
  365.  */
  366.  
  367. putchar(c)
  368. char c;
  369. {
  370.     char *static;
  371.     static = "";    /* remembers last character sent; start out null */
  372.  
  373.     if (_doflag)
  374.     {
  375.         if (_nulflag) goto nulskip;
  376.         if (_prflag)
  377.         {
  378.             if (c == '\n' && *static != '\r') putc('\r',stdlst);
  379.             *static = c;
  380.             if(putc(c,stdlst) == ERROR)
  381.             {
  382.                 fputs("DIO: Printer output error.",stderr);
  383.                 exit(4);
  384.             }
  385.         }
  386.         else
  387.         {
  388.             if (c == '\n' && *static != '\r') putc('\r',_dobuf);
  389.             *static = c;
  390.             if(putc(c,_dobuf) == ERROR)
  391.             {
  392.                 fputs("DIO: Output error--disk full?\n",stderr);
  393.                 exit(11);    /* disk full error */
  394.             }
  395.         }
  396. nulskip:    if (!(_doflag & VERBOSE)) return;
  397.     }
  398.     if (bdos(CON_STATUS) && bdos(CON_INPUT) == CONTROL_C)
  399.     {
  400.         fputs("\nDIO: Interrupt detected.",stderr);
  401.         exit(4);
  402.     }
  403.     if (c == '\n' && *static != '\r') bdos(CON_OUTPUT,'\r');
  404.     bdos(CON_OUTPUT,c);
  405.     *static = c;
  406. }
  407.