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