home *** CD-ROM | disk | FTP | other *** search
/ The Developer Connection…ice Driver Kit for OS/2 3 / DEV3-D1.ISO / source / util2src / lbuf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-17  |  14.7 KB  |  327 lines

  1. /*============================================================================*
  2.  * module: lbuf.c - Buffered line input.
  3.  *
  4.  * (C)Copyright IBM Corporation, 1991.                    Brian E. Yoder
  5.  *
  6.  * This module contains subroutines for buffered ASCII text line input.
  7.  * The lgets() subroutine works like fgets() except that it doesn't choke on
  8.  * non-ASCII, null, or end-of-text characters.
  9.  *
  10.  * This module also contains subroutines to manage buffered input streams:
  11.  *      newlbuf() - Create a new LBUF buffered stream.
  12.  *      setlbuf() - Bind an open file descriptor to an LBUF buffered stream.
  13.  *      lgets()   - Get the next line from an LBUF buffered stream.
  14.  *
  15.  * Note:  Never directly allocate or declare storage for an LBUF structure.
  16.  * Always use newlbuf() to create LBUF buffered streams and setlbuf() to
  17.  * set them up.  That way, any necessary initialization is taken care of.
  18.  *
  19.  * 10/17/91 - Created for DOS and OS/2.
  20.  * 10/26/91 - Initial version.
  21.  * 09/30/93 - Treat non-ASCII characters, nulls, and end-of-text characters
  22.  *            as newlines. See lgets() for why.
  23.  * 11/17/93 - Add formfeed to list of allowable characters in lgets().
  24.  *============================================================================*/
  25.  
  26. #include <stdio.h>
  27. #include <errno.h>
  28. #include <fcntl.h>
  29. #include <string.h>
  30. #include <malloc.h>
  31. #include <memory.h>
  32. #include <sys/types.h>
  33. #include <sys/stat.h>
  34. #include <io.h>
  35. #include <ctype.h>
  36.  
  37. #include "util.h"
  38.  
  39. /*============================================================================*
  40.  * newlbuf() - Create a new LBUF buffered stream.
  41.  *
  42.  * REMARKS:
  43.  *   This subroutine allocates space for the LBUF structure type and its I/O
  44.  *   buffer.  The structure and buffer are contained within the same malloc'd
  45.  *   block of memory.
  46.  *
  47.  * RETURNS:
  48.  *   A pointer to the new LBUF structure.  A NULL pointer is returned if
  49.  *   there is not enough memory for the structure and its I/O buffer.
  50.  *
  51.  * NOTES:
  52.  *   Freeing an LBUF buffered stream involves closing its file descriptor
  53.  *   (if != -1) and freeing the memory block (LBUF *).
  54.  *============================================================================*/
  55. LBUF *newlbuf(
  56.  
  57. uint      bufflen )                 /* Size of I/O buffer, in bytes */
  58.  
  59. {
  60.   int     rc;                       /* Return code storage */
  61.   char   *newmem;                   /* Pointer to newly allocated memory */
  62.   uint    memlen;                   /* Length of newly allocation memory */
  63.   uint    memmin;                   /* Minimum amount of memory required */
  64.   LBUF   *stream;                   /* Pointer to newly allocated LBUF */
  65.  
  66.  /*---------------------------------------------------------------------------*
  67.   * Allocate a block of memory to hold the LBUF structure + the I/O buffer
  68.   *---------------------------------------------------------------------------*/
  69.  
  70.   if (bufflen == 0) return(NULL);   /* If buffer len is 0: just return */
  71.  
  72.   memlen = sizeof(LBUF) + bufflen;  /* Calculate amount of memory needed */
  73.  
  74.   memmin = sizeof(LBUF) + BUFSIZ;   /* Determine minimum amount we should have */
  75.   if (memlen < memmin)              /* If memlen is too small (overflow when */
  76.   {                                 /* adding, or because bufflen < BUFSIZ): */
  77.      memlen = memmin;                    /* Set memlen = mimimum required.  Our */
  78.      bufflen = BUFSIZ;                   /* I/O buffer should at least be BUFSIZ */
  79.   }                                      /* or we have no advantage over fgets */
  80.  
  81.   newmem = malloc(memlen);          /* Allocate the block of memory */
  82.  
  83.   if (newmem == NULL) return(NULL); /* If we couldn't get the memory: return */
  84.  
  85.   stream = (LBUF *)newmem;          /* LBUF resides at the start of the block */
  86.  
  87.  /*---------------------------------------------------------------------------*
  88.   * Initialize the LBUF structure:
  89.   *---------------------------------------------------------------------------*/
  90.  
  91.   memset(newmem, 0, memlen);        /* Fill LBUF + I/O buffer with nulls */
  92.  
  93.   stream->fd = -1;                  /* Set file descriptor to -1 (not open) */
  94.   stream->bufflen = bufflen;        /* Store length of I/I buffer */
  95.  
  96.   stream->buff = newmem +           /* Store pointer to I/O buffer: just past */
  97.                  sizeof(LBUF);      /* the LBUF structure */
  98.  
  99.   stream->next = newmem + memlen;   /* Set 'next' pointer just past end of I/O buffer */
  100.   stream->end = stream->next;       /* Set end pointer = next pointer */
  101.  
  102.   return(stream);                   /* Done: Return pointer to LBUF structure */
  103. }
  104.  
  105. /*============================================================================*
  106.  * setlbuf() - Bind an open file descriptor to an LBUF buffered stream.
  107.  *
  108.  * REMARKS:
  109.  *   This subroutine stores the specified file descriptor in the LBUF
  110.  *   stream.  It is the caller's responsibility to provide a file descriptor
  111.  *   that is open for reading.
  112.  *
  113.  *   If the LBUF stream already contains an open file descriptor, then it
  114.  *   is closed before storing the new file descriptor.
  115.  *
  116.  * RETURNS:
  117.  *   Nothing.
  118.  *
  119.  * NOTES:
  120.  *   Don't directly allocate or declare storage for an LBUF structure:
  121.  *   always use newlbuf() to create a new LBUF buffered stream.
  122.  *
  123.  *   For DOS and OS/2, the file descriptor may be open for reading in either
  124.  *   binary or text modes -- it won't matter to the lgets() subroutine.
  125.  *   However, for performance reasons you should open the file in binary mode.
  126.  *============================================================================*/
  127. void setlbuf(
  128.  
  129. LBUF     *stream,                   /* Pointer to LBUF stream */
  130. int       fd )                      /* Open file descriptor */
  131.  
  132. {
  133.   int     rc;                       /* Return code storage */
  134.  
  135.   if (stream->fd != -1)             /* If stream is open: */
  136.   {
  137.      close(stream->fd);                  /* Close it */
  138.      stream->fd = -1;                    /* Set file descriptor to -1 */
  139.   }
  140.  
  141.   stream->fd = fd;                  /* Store file descriptor */
  142.  
  143.   stream->end = stream->buff +      /* Initialize 'end' to point one character */
  144.                 stream->bufflen;    /* past end of stream's I/O buffer */
  145.  
  146.   stream->next = stream->end;       /* Point 'next' past end of buffer, too. */
  147.                                     /* Therefore, the next call to lgets() */
  148.                                     /* will see there's no data in the I/O */
  149.                                     /* buffer and will start reading */
  150.  
  151.   stream->ferrno = 0;               /* Reset stream's error condition */
  152.   stream->seof = FALSE;             /* And reset its end-of-stream flag */
  153.  
  154.   return;                           /* Done: Return */
  155. }
  156.  
  157. /*============================================================================*
  158.  * lgets()   - Get the next line from an LBUF buffered stream.
  159.  *
  160.  * REMARKS:
  161.  *   This subroutine reads a line from the LBUF stream and stores it in
  162.  *   the specified string.  It reads characters from the stream up to
  163.  *   and including the first new-line character (\n), up to the end of
  164.  *   the stream, or until n-1 characters have been read, which ever comes
  165.  *   first.   The subroutine then stores a zero character at the end of
  166.  *   the string.  The string includes the new-line character, if it was
  167.  *   read.
  168.  *
  169.  *   Non-ASCII non-control characters, nulls, and DOS end-of-text characters
  170.  *   are treated as newline characters. This facilitates reading binary files
  171.  *   and keeping  a string from being split across two calls to lgets().
  172.  *   Carriage returns are completely ignored: lines are delimited by newline
  173.  *   characters as both unix and text-mode DOS programs do.
  174.  *
  175.  *   Before calling lgets, first call newlbuf to create an LBUF stream.
  176.  *   Then, open a file and call setlbuf to store its file descriptor
  177.  *   in the LBUF structure.  The stream is now ready to be read by
  178.  *   lgets.
  179.  *
  180.  *   It is the caller's responsibility to ensure that the string can
  181.  *   hold up to n characters, including the null string terminating
  182.  *   byte.
  183.  *
  184.  * RETURNS:
  185.  *   The lgets subroutine returns a pointer to the string.  A NULL pointer
  186.  *   is returned if the end of the stream was encountered and no characters
  187.  *   were read, if an error condition is encountered, or if the stream
  188.  *   doesn't contain an open file descriptor.
  189.  *
  190.  *   If n is 1, then str will be empty (*str contains '\0').
  191.  *
  192.  * NOTES:
  193.  *   This subroutine uses the stream's 'next' field as a pointer to the next
  194.  *   character position within the I/O buffer from which to obtain a character.
  195.  *============================================================================*/
  196. char *lgets(
  197.  
  198. char     *str,                      /* Pointer to location to store string */
  199. uint      n,                        /* Max string length, including null at end */
  200. LBUF     *stream )                  /* Pointer to LBUF buffered stream */
  201.  
  202. {
  203.   int     rc;                       /* Return code storage */
  204.   int     rcnt;                     /* Count returned from reading */
  205.   char   *ostr;                     /* Pointer to output position within str */
  206.   int     c;                        /* Character from the stream */
  207.   char   *snext;                    /* Placeholder to next char from I/O buffer */
  208.   char   *send;                     /* Pointer past end of I/O buffer */
  209.  
  210.   if (n == 0)                       /* If n is zero: We can't even hold the */
  211.      return(NULL);                  /* terminating null character */
  212.  
  213.   if ( (stream->seof == TRUE) ||    /* If already at the end of the stream, or */
  214.        (stream->ferrno != 0) )      /* If we got an error on a previous I/O: */
  215.   {
  216.      *str = '\0';                        /* Make output string zero length */
  217.      return(NULL);                       /* Don't do anything */
  218.   }
  219.  
  220.   n--;                              /* Set n = no. of characters, NOT counting */
  221.                                     /* the terminating null character */
  222.  
  223.   ostr = str;                       /* Point to first position within str */
  224.   snext = stream->next;             /* Get stream's next I/O buffer position */
  225.   send  = stream->end;              /* Get stream's end-of-data position */
  226.  
  227.  /*---------------------------------------------------------------------------*
  228.   * Loop to put characters from stream into the caller's string
  229.   *---------------------------------------------------------------------------*/
  230.  
  231.   for (;;)                          /* Loop to store characters in str: */
  232.   {
  233.      if (n == 0) break;                  /* If we filled 'str': quit loop */
  234.  
  235.     /*-------------------------------------------------------------------*
  236.      * If stream's I/O buffer needs refilling, then fill it and reset
  237.      * 'send' to point just past end of last byte read, and 'snext' to
  238.      * point to the start of the buffer.
  239.      *-------------------------------------------------------------------*/
  240.  
  241.      if (snext >= send)                  /* If we've run out of data in buffer: */
  242.      {
  243.         rcnt = read(stream->fd,               /* Read the next segment from file */
  244.                     stream->buff,
  245.                     stream->bufflen);
  246.  
  247.         if (rcnt == -1)                       /* If I/O error: */
  248.         {
  249.            stream->ferrno = errno;                 /* Store errno, and */
  250.            break;                                  /* Stop storing chars */
  251.         }
  252.  
  253.         if (rcnt == 0)                        /* If we're at the end: */
  254.         {
  255.            stream->seof = TRUE;                    /* Set end-of-stream */
  256.            break;                                  /* Stop storing chars */
  257.         }
  258.  
  259.         snext = stream->buff;                 /* Point 'next' to start of data */
  260.  
  261.         send =  snext + rcnt;                 /* Point 'end' one character past */
  262.                                               /* end of data in I/O buffer */
  263.      } /* end of if stmt to check for data in buffer */
  264.  
  265.     /*-------------------------------------------------------------------*
  266.      * Store next character from stream's buffer into the output string
  267.      *-------------------------------------------------------------------*/
  268.  
  269.      c = *snext;                         /* Store character from buffer in c */
  270.  
  271.      if (isascii(c))                    /* If c is an ASCII control char: */
  272.      {                                  /* only allow certain ones: Turn the */
  273.         if (iscntrl(c))                 /* rest into newlines */
  274.         {
  275.            switch (c)
  276.            {                            /* Allowable ctrl chars: */
  277.               case '\n':                     /* newline */
  278.               case '\r':                     /* carriage return */
  279.               case '\t':                     /* tab */
  280.               case '\b':                     /* backspace */
  281.               case '\f':                     /* formfeed */
  282.               case 27:                       /* escape */
  283.                  break;
  284.  
  285.               default:                  /* Turn the rest into newlines */
  286.                  c = '\n';
  287.            }
  288.         }
  289.      }
  290.  
  291.      switch (c)                          /* Check the character again: */
  292.      {
  293.         case '\r':                            /* For carriage returns: */
  294.            snext++;                                /* Skip over them */
  295.            n--;
  296.            break;
  297.  
  298.         default:                              /* All others: */
  299.            *ostr = c;                              /* Store it in output string */
  300.            ostr++;                                 /* Bump output pointer */
  301.            snext++;                                /* Point to next char in buffer */
  302.            n--;                                    /* Adjust n to no. of chars */
  303.            break;
  304.      }
  305.  
  306.      if (c == '\n')                      /* If c is also a new-line: */
  307.         break;                                /* Break out: We have a line */
  308.  
  309.   } /* end of for(;;) loop to store characters in str */
  310.  
  311.  /*---------------------------------------------------------------------------*
  312.   * Done storing characters.
  313.   *
  314.   * Note: If ostr still points to the beginning of str, then we must have
  315.   * gotten an error or to the end of the file without storing anything.
  316.   *---------------------------------------------------------------------------*/
  317.  
  318.   *ostr = '\0';                     /* Terminate string with null char */
  319.   stream->next = snext;             /* Store stream's I/O buffer placeholder */
  320.   stream->end  = send;              /* Store stream's end-of-data pointer */
  321.  
  322.   if (ostr == str)                  /* If we haven't stored anything at all: */
  323.      return(NULL);                  /* Then return NULL */
  324.  
  325.   return(str);                      /* Return pointer to caller's string */
  326. }
  327.