root/maint/gnulib/lib/getndelim2.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. getndelim2

   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-1998, 2000, 2003-2004, 2006, 2008-2021 Free
   5    Software Foundation, Inc.
   6 
   7    This file is free software: you can redistribute it and/or modify
   8    it under the terms of the GNU Lesser General Public License as
   9    published by the Free Software Foundation; either version 2.1 of the
  10    License, or (at your option) any later version.
  11 
  12    This file 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 Lesser General Public License for more details.
  16 
  17    You should have received a copy of the GNU Lesser General Public License
  18    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  19 
  20 /* Originally written by Jan Brittenson, bson@gnu.ai.mit.edu.  */
  21 
  22 #include <config.h>
  23 
  24 #include "getndelim2.h"
  25 
  26 #include <stdbool.h>
  27 #include <stddef.h>
  28 #include <stdlib.h>
  29 #include <string.h>
  30 
  31 #if USE_UNLOCKED_IO
  32 # include "unlocked-io.h"
  33 #endif
  34 #if !HAVE_FLOCKFILE
  35 # undef flockfile
  36 # define flockfile(x) ((void) 0)
  37 #endif
  38 #if !HAVE_FUNLOCKFILE
  39 # undef funlockfile
  40 # define funlockfile(x) ((void) 0)
  41 #endif
  42 
  43 #include <limits.h>
  44 #include <stdint.h>
  45 
  46 #include "freadptr.h"
  47 #include "freadseek.h"
  48 #include "memchr2.h"
  49 
  50 #ifndef SSIZE_MAX
  51 # define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
  52 #endif
  53 
  54 /* Use this to suppress gcc's "...may be used before initialized" warnings. */
  55 #if defined GCC_LINT || defined lint
  56 # define IF_LINT(Code) Code
  57 #else
  58 # define IF_LINT(Code) /* empty */
  59 #endif
  60 
  61 /* The maximum value that getndelim2 can return without suffering from
  62    overflow problems, either internally (because of pointer
  63    subtraction overflow) or due to the API (because of ssize_t).  */
  64 #define GETNDELIM2_MAXIMUM (PTRDIFF_MAX < SSIZE_MAX ? PTRDIFF_MAX : SSIZE_MAX)
  65 
  66 /* Try to add at least this many bytes when extending the buffer.
  67    MIN_CHUNK must be no greater than GETNDELIM2_MAXIMUM.  */
  68 #define MIN_CHUNK 64
  69 
  70 ssize_t
  71 getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t nmax,
     /* [previous][next][first][last][top][bottom][index][help] */
  72             int delim1, int delim2, FILE *stream)
  73 {
  74   size_t nbytes_avail;          /* Allocated but unused bytes in *LINEPTR.  */
  75   char *read_pos;               /* Where we're reading into *LINEPTR. */
  76   ssize_t bytes_stored = -1;
  77   char *ptr = *lineptr;
  78   size_t size = *linesize;
  79   bool found_delimiter;
  80 
  81   if (!ptr)
  82     {
  83       size = nmax < MIN_CHUNK ? nmax : MIN_CHUNK;
  84       ptr = malloc (size);
  85       if (!ptr)
  86         return -1;
  87     }
  88 
  89   if (size < offset)
  90     goto done;
  91 
  92   nbytes_avail = size - offset;
  93   read_pos = ptr + offset;
  94 
  95   if (nbytes_avail == 0 && nmax <= size)
  96     goto done;
  97 
  98   /* Normalize delimiters, since memchr2 doesn't handle EOF.  */
  99   if (delim1 == EOF)
 100     delim1 = delim2;
 101   else if (delim2 == EOF)
 102     delim2 = delim1;
 103 
 104   flockfile (stream);
 105 
 106   found_delimiter = false;
 107   do
 108     {
 109       /* Here always ptr + size == read_pos + nbytes_avail.
 110          Also nbytes_avail > 0 || size < nmax.  */
 111 
 112       int c IF_LINT (= 0);
 113       const char *buffer;
 114       size_t buffer_len;
 115 
 116       buffer = freadptr (stream, &buffer_len);
 117       if (buffer)
 118         {
 119           if (delim1 != EOF)
 120             {
 121               const char *end = memchr2 (buffer, delim1, delim2, buffer_len);
 122               if (end)
 123                 {
 124                   buffer_len = end - buffer + 1;
 125                   found_delimiter = true;
 126                 }
 127             }
 128         }
 129       else
 130         {
 131           c = getc (stream);
 132           if (c == EOF)
 133             {
 134               /* Return partial line, if any.  */
 135               if (read_pos == ptr)
 136                 goto unlock_done;
 137               else
 138                 break;
 139             }
 140           if (c == delim1 || c == delim2)
 141             found_delimiter = true;
 142           buffer_len = 1;
 143         }
 144 
 145       /* We always want at least one byte left in the buffer, since we
 146          always (unless we get an error while reading the first byte)
 147          NUL-terminate the line buffer.  */
 148 
 149       if (nbytes_avail < buffer_len + 1 && size < nmax)
 150         {
 151           /* Grow size proportionally, not linearly, to avoid O(n^2)
 152              running time.  */
 153           size_t newsize = size < MIN_CHUNK ? size + MIN_CHUNK : 2 * size;
 154           char *newptr;
 155 
 156           /* Increase newsize so that it becomes
 157              >= (read_pos - ptr) + buffer_len.  */
 158           if (newsize - (read_pos - ptr) < buffer_len + 1)
 159             newsize = (read_pos - ptr) + buffer_len + 1;
 160           /* Respect nmax.  This handles possible integer overflow.  */
 161           if (! (size < newsize && newsize <= nmax))
 162             newsize = nmax;
 163 
 164           if (GETNDELIM2_MAXIMUM < newsize - offset)
 165             {
 166               size_t newsizemax = offset + GETNDELIM2_MAXIMUM + 1;
 167               if (size == newsizemax)
 168                 goto unlock_done;
 169               newsize = newsizemax;
 170             }
 171 
 172           nbytes_avail = newsize - (read_pos - ptr);
 173           newptr = realloc (ptr, newsize);
 174           if (!newptr)
 175             goto unlock_done;
 176           ptr = newptr;
 177           size = newsize;
 178           read_pos = size - nbytes_avail + ptr;
 179         }
 180 
 181       /* Here, if size < nmax, nbytes_avail >= buffer_len + 1.
 182          If size == nmax, nbytes_avail > 0.  */
 183 
 184       if (1 < nbytes_avail)
 185         {
 186           size_t copy_len = nbytes_avail - 1;
 187           if (buffer_len < copy_len)
 188             copy_len = buffer_len;
 189           if (buffer)
 190             memcpy (read_pos, buffer, copy_len);
 191           else
 192             *read_pos = c;
 193           read_pos += copy_len;
 194           nbytes_avail -= copy_len;
 195         }
 196 
 197       /* Here still nbytes_avail > 0.  */
 198 
 199       if (buffer && freadseek (stream, buffer_len))
 200         goto unlock_done;
 201     }
 202   while (!found_delimiter);
 203 
 204   /* Done - NUL terminate and return the number of bytes read.
 205      At this point we know that nbytes_avail >= 1.  */
 206   *read_pos = '\0';
 207 
 208   bytes_stored = read_pos - (ptr + offset);
 209 
 210  unlock_done:
 211   funlockfile (stream);
 212 
 213  done:
 214   *lineptr = ptr;
 215   *linesize = size;
 216   return bytes_stored ? bytes_stored : -1;
 217 }

/* [previous][next][first][last][top][bottom][index][help] */