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