Home | History | Annotate | Line # | Download | only in lib
getndelim2.c revision 1.1.1.1.26.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 #include <sys/cdefs.h>
     21 __RCSID("$NetBSD: getndelim2.c,v 1.1.1.1.26.1 2017/05/13 06:23:23 snj Exp $");
     22 
     23 
     24 /* Originally written by Jan Brittenson, bson (at) gnu.ai.mit.edu.  */
     25 
     26 #ifdef HAVE_CONFIG_H
     27 # include <config.h>
     28 #endif
     29 
     30 #include "getndelim2.h"
     31 
     32 #include <stdlib.h>
     33 #include <stddef.h>
     34 
     35 #if USE_UNLOCKED_IO
     36 # include "unlocked-io.h"
     37 #endif
     38 
     39 #include <limits.h>
     40 #if HAVE_INTTYPES_H
     41 # include <inttypes.h>
     42 #endif
     43 #if HAVE_STDINT_H
     44 # include <stdint.h>
     45 #endif
     46 #ifndef PTRDIFF_MAX
     47 # define PTRDIFF_MAX ((ptrdiff_t) (SIZE_MAX / 2))
     48 #endif
     49 #ifndef SIZE_MAX
     50 # define SIZE_MAX ((size_t) -1)
     51 #endif
     52 #ifndef SSIZE_MAX
     53 # define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
     54 #endif
     55 
     56 /* The maximum value that getndelim2 can return without suffering from
     57    overflow problems, either internally (because of pointer
     58    subtraction overflow) or due to the API (because of ssize_t).  */
     59 #define GETNDELIM2_MAXIMUM (PTRDIFF_MAX < SSIZE_MAX ? PTRDIFF_MAX : SSIZE_MAX)
     60 
     61 /* Try to add at least this many bytes when extending the buffer.
     62    MIN_CHUNK must be no greater than GETNDELIM2_MAXIMUM.  */
     63 #define MIN_CHUNK 64
     64 
     65 ssize_t
     66 getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t nmax,
     67             int delim1, int delim2, FILE *stream)
     68 {
     69   size_t nbytes_avail;		/* Allocated but unused bytes in *LINEPTR.  */
     70   char *read_pos;		/* Where we're reading into *LINEPTR. */
     71   ssize_t bytes_stored = -1;
     72   char *ptr = *lineptr;
     73   size_t size = *linesize;
     74 
     75   if (!ptr)
     76     {
     77       size = nmax < MIN_CHUNK ? nmax : MIN_CHUNK;
     78       ptr = malloc (size);
     79       if (!ptr)
     80 	return -1;
     81     }
     82 
     83   if (size < offset)
     84     goto done;
     85 
     86   nbytes_avail = size - offset;
     87   read_pos = ptr + offset;
     88 
     89   if (nbytes_avail == 0 && nmax <= size)
     90     goto done;
     91 
     92   for (;;)
     93     {
     94       /* Here always ptr + size == read_pos + nbytes_avail.  */
     95 
     96       int c;
     97 
     98       /* We always want at least one byte left in the buffer, since we
     99 	 always (unless we get an error while reading the first byte)
    100 	 NUL-terminate the line buffer.  */
    101 
    102       if (nbytes_avail < 2 && size < nmax)
    103 	{
    104 	  size_t newsize = size < MIN_CHUNK ? size + MIN_CHUNK : 2 * size;
    105 	  char *newptr;
    106 
    107 	  if (! (size < newsize && newsize <= nmax))
    108 	    newsize = nmax;
    109 
    110 	  if (GETNDELIM2_MAXIMUM < newsize - offset)
    111 	    {
    112 	      size_t newsizemax = offset + GETNDELIM2_MAXIMUM + 1;
    113 	      if (size == newsizemax)
    114 		goto done;
    115 	      newsize = newsizemax;
    116 	    }
    117 
    118 	  nbytes_avail = newsize - (read_pos - ptr);
    119 	  newptr = realloc (ptr, newsize);
    120 	  if (!newptr)
    121 	    goto done;
    122 	  ptr = newptr;
    123 	  size = newsize;
    124 	  read_pos = size - nbytes_avail + ptr;
    125 	}
    126 
    127       c = getc (stream);
    128       if (c == EOF)
    129 	{
    130 	  /* Return partial line, if any.  */
    131 	  if (read_pos == ptr)
    132 	    goto done;
    133 	  else
    134 	    break;
    135 	}
    136 
    137       if (nbytes_avail >= 2)
    138 	{
    139 	  *read_pos++ = c;
    140 	  nbytes_avail--;
    141 	}
    142 
    143       if (c == delim1 || c == delim2)
    144 	/* Return the line.  */
    145 	break;
    146     }
    147 
    148   /* Done - NUL terminate and return the number of bytes read.
    149      At this point we know that nbytes_avail >= 1.  */
    150   *read_pos = '\0';
    151 
    152   bytes_stored = read_pos - (ptr + offset);
    153 
    154  done:
    155   *lineptr = ptr;
    156   *linesize = size;
    157   return bytes_stored;
    158 }
    159