getndelim2.c revision 1.1.1.1.26.1 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.1.1.1.26.1 snj #include <sys/cdefs.h>
21 1.1.1.1.26.1 snj __RCSID("$NetBSD: getndelim2.c,v 1.1.1.1.26.1 2017/05/13 06:23:23 snj Exp $");
22 1.1.1.1.26.1 snj
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