Home | History | Annotate | Line # | Download | only in lib
getndelim2.c revision 1.1
      1 /* getndelim2 - Read a line from a stream, stopping at one of 2 delimiters,
      2    with bounded memory allocation.
      3 
      4    Copyright (C) 1993, 1996, 1997, 1998, 2000, 2003, 2004 Free Software
      5    Foundation, Inc.
      6 
      7    This program is free software; you can redistribute it and/or modify
      8    it under the terms of the GNU General Public License as published by
      9    the Free Software Foundation; either version 2, or (at your option)
     10    any later version.
     11 
     12    This program is distributed in the hope that it will be useful,
     13    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15    GNU General Public License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with this program; if not, write to the Free Software Foundation,
     19    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
     20 
     21 /* Originally written by Jan Brittenson, bson (at) gnu.ai.mit.edu.  */
     22 
     23 #ifdef HAVE_CONFIG_H
     24 # include <config.h>
     25 #endif
     26 
     27 #include "getndelim2.h"
     28 
     29 #include <stdlib.h>
     30 #include <stddef.h>
     31 
     32 #if USE_UNLOCKED_IO
     33 # include "unlocked-io.h"
     34 #endif
     35 
     36 #include <limits.h>
     37 #if HAVE_INTTYPES_H
     38 # include <inttypes.h>
     39 #endif
     40 #if HAVE_STDINT_H
     41 # include <stdint.h>
     42 #endif
     43 #ifndef PTRDIFF_MAX
     44 # define PTRDIFF_MAX ((ptrdiff_t) (SIZE_MAX / 2))
     45 #endif
     46 #ifndef SIZE_MAX
     47 # define SIZE_MAX ((size_t) -1)
     48 #endif
     49 #ifndef SSIZE_MAX
     50 # define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
     51 #endif
     52 
     53 /* The maximum value that getndelim2 can return without suffering from
     54    overflow problems, either internally (because of pointer
     55    subtraction overflow) or due to the API (because of ssize_t).  */
     56 #define GETNDELIM2_MAXIMUM (PTRDIFF_MAX < SSIZE_MAX ? PTRDIFF_MAX : SSIZE_MAX)
     57 
     58 /* Try to add at least this many bytes when extending the buffer.
     59    MIN_CHUNK must be no greater than GETNDELIM2_MAXIMUM.  */
     60 #define MIN_CHUNK 64
     61 
     62 ssize_t
     63 getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t nmax,
     64             int delim1, int delim2, FILE *stream)
     65 {
     66   size_t nbytes_avail;		/* Allocated but unused bytes in *LINEPTR.  */
     67   char *read_pos;		/* Where we're reading into *LINEPTR. */
     68   ssize_t bytes_stored = -1;
     69   char *ptr = *lineptr;
     70   size_t size = *linesize;
     71 
     72   if (!ptr)
     73     {
     74       size = nmax < MIN_CHUNK ? nmax : MIN_CHUNK;
     75       ptr = malloc (size);
     76       if (!ptr)
     77 	return -1;
     78     }
     79 
     80   if (size < offset)
     81     goto done;
     82 
     83   nbytes_avail = size - offset;
     84   read_pos = ptr + offset;
     85 
     86   if (nbytes_avail == 0 && nmax <= size)
     87     goto done;
     88 
     89   for (;;)
     90     {
     91       /* Here always ptr + size == read_pos + nbytes_avail.  */
     92 
     93       int c;
     94 
     95       /* We always want at least one byte left in the buffer, since we
     96 	 always (unless we get an error while reading the first byte)
     97 	 NUL-terminate the line buffer.  */
     98 
     99       if (nbytes_avail < 2 && size < nmax)
    100 	{
    101 	  size_t newsize = size < MIN_CHUNK ? size + MIN_CHUNK : 2 * size;
    102 	  char *newptr;
    103 
    104 	  if (! (size < newsize && newsize <= nmax))
    105 	    newsize = nmax;
    106 
    107 	  if (GETNDELIM2_MAXIMUM < newsize - offset)
    108 	    {
    109 	      size_t newsizemax = offset + GETNDELIM2_MAXIMUM + 1;
    110 	      if (size == newsizemax)
    111 		goto done;
    112 	      newsize = newsizemax;
    113 	    }
    114 
    115 	  nbytes_avail = newsize - (read_pos - ptr);
    116 	  newptr = realloc (ptr, newsize);
    117 	  if (!newptr)
    118 	    goto done;
    119 	  ptr = newptr;
    120 	  size = newsize;
    121 	  read_pos = size - nbytes_avail + ptr;
    122 	}
    123 
    124       c = getc (stream);
    125       if (c == EOF)
    126 	{
    127 	  /* Return partial line, if any.  */
    128 	  if (read_pos == ptr)
    129 	    goto done;
    130 	  else
    131 	    break;
    132 	}
    133 
    134       if (nbytes_avail >= 2)
    135 	{
    136 	  *read_pos++ = c;
    137 	  nbytes_avail--;
    138 	}
    139 
    140       if (c == delim1 || c == delim2)
    141 	/* Return the line.  */
    142 	break;
    143     }
    144 
    145   /* Done - NUL terminate and return the number of bytes read.
    146      At this point we know that nbytes_avail >= 1.  */
    147   *read_pos = '\0';
    148 
    149   bytes_stored = read_pos - (ptr + offset);
    150 
    151  done:
    152   *lineptr = ptr;
    153   *linesize = size;
    154   return bytes_stored;
    155 }
    156