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