xref: /netbsd-src/lib/libc/stdio/getdelim.c (revision 555c77845c87d9da003293ebef8f491375415b21)
1*555c7784Suwe /* $NetBSD: getdelim.c,v 1.14 2017/06/08 15:59:45 uwe Exp $ */
2d4a3cf6aSroy 
3d4a3cf6aSroy /*
4d4a3cf6aSroy  * Copyright (c) 2009 The NetBSD Foundation, Inc.
5d4a3cf6aSroy  *
6d4a3cf6aSroy  * This code is derived from software contributed to The NetBSD Foundation
7d4a3cf6aSroy  * by Roy Marples.
8d4a3cf6aSroy  *
9d4a3cf6aSroy  * Redistribution and use in source and binary forms, with or without
10d4a3cf6aSroy  * modification, are permitted provided that the following conditions
11d4a3cf6aSroy  * are met:
12d4a3cf6aSroy  * 1. Redistributions of source code must retain the above copyright
13d4a3cf6aSroy  *    notice, this list of conditions and the following disclaimer.
14d4a3cf6aSroy  * 2. Redistributions in binary form must reproduce the above copyright
15d4a3cf6aSroy  *    notice, this list of conditions and the following disclaimer in the
16d4a3cf6aSroy  *    documentation and/or other materials provided with the distribution.
17d4a3cf6aSroy  *
18d4a3cf6aSroy  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19d4a3cf6aSroy  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20d4a3cf6aSroy  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21d4a3cf6aSroy  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22d4a3cf6aSroy  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23d4a3cf6aSroy  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24d4a3cf6aSroy  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25d4a3cf6aSroy  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26d4a3cf6aSroy  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27d4a3cf6aSroy  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28d4a3cf6aSroy  */
29d4a3cf6aSroy 
30d4a3cf6aSroy #include <sys/cdefs.h>
31*555c7784Suwe __RCSID("$NetBSD: getdelim.c,v 1.14 2017/06/08 15:59:45 uwe Exp $");
3286eafd3eSroy 
3386eafd3eSroy #include "namespace.h"
34d4a3cf6aSroy 
35d4a3cf6aSroy #include <sys/param.h>
36d4a3cf6aSroy 
37d4a3cf6aSroy #include <assert.h>
38d4a3cf6aSroy #include <errno.h>
39d4a3cf6aSroy #include <limits.h>
40d4a3cf6aSroy #include <stdio.h>
41d4a3cf6aSroy #include <stdlib.h>
42d4a3cf6aSroy #include <string.h>
43d4a3cf6aSroy 
44d4a3cf6aSroy #include "reentrant.h"
45d4a3cf6aSroy #include "local.h"
46d4a3cf6aSroy 
473490b83aSroy #ifdef __weak_alias
__weak_alias(getdelim,_getdelim)483490b83aSroy __weak_alias(getdelim, _getdelim)
493490b83aSroy #endif
503490b83aSroy 
51d4a3cf6aSroy /* Minimum buffer size we create.
52d4a3cf6aSroy  * This should allow config files to fit into our power of 2 buffer growth
53d4a3cf6aSroy  * without the need for a realloc. */
54d4a3cf6aSroy #define MINBUF	128
55d4a3cf6aSroy 
56d4a3cf6aSroy ssize_t
577cfa0468Sroy __getdelim(char **__restrict buf, size_t *__restrict buflen,
58d4a3cf6aSroy     int sep, FILE *__restrict fp)
59d4a3cf6aSroy {
60d4a3cf6aSroy 	unsigned char *p;
6186eafd3eSroy 	size_t len, newlen, off;
62d4a3cf6aSroy 	char *newb;
63d4a3cf6aSroy 
64d4a3cf6aSroy 	_DIAGASSERT(fp != NULL);
65d4a3cf6aSroy 
66d4a3cf6aSroy 	if (buf == NULL || buflen == NULL) {
67d4a3cf6aSroy 		errno = EINVAL;
689c6b248aSjoerg 		goto error;
69d4a3cf6aSroy 	}
70d4a3cf6aSroy 
71d4a3cf6aSroy 	/* If buf is NULL, we have to assume a size of zero */
72d4a3cf6aSroy 	if (*buf == NULL)
73d4a3cf6aSroy 		*buflen = 0;
74d4a3cf6aSroy 
75d4a3cf6aSroy 	_SET_ORIENTATION(fp, -1);
76d4a3cf6aSroy 	off = 0;
77755657beSroy 	do {
78d4a3cf6aSroy 		/* If the input buffer is empty, refill it */
79d4a3cf6aSroy 		if (fp->_r <= 0 && __srefill(fp)) {
8086eafd3eSroy 			if (__sferror(fp))
81d4a3cf6aSroy 				goto error;
82755657beSroy 			/* No error, so EOF. */
83d4a3cf6aSroy 			break;
84d4a3cf6aSroy 		}
85d4a3cf6aSroy 
86d4a3cf6aSroy 		/* Scan through looking for the separator */
87cfbb35edSchristos 		p = memchr(fp->_p, sep, (size_t)fp->_r);
88d4a3cf6aSroy 		if (p == NULL)
89d4a3cf6aSroy 			len = fp->_r;
90d4a3cf6aSroy 		else
91d4a3cf6aSroy 			len = (p - fp->_p) + 1;
92d4a3cf6aSroy 
935eba3548Sroy 		newlen = off + len;
9486eafd3eSroy 		/* Ensure we can handle it */
95c9c21f1eSroy 		if (newlen < off || newlen > SSIZE_MAX) {
96d4a3cf6aSroy 			errno = EOVERFLOW;
97d4a3cf6aSroy 			goto error;
98d4a3cf6aSroy 		}
99*555c7784Suwe 		newlen++; /* reserve space for the null terminator */
100d4a3cf6aSroy 		if (newlen > *buflen) {
101d4a3cf6aSroy 			if (newlen < MINBUF)
102d4a3cf6aSroy 				newlen = MINBUF;
103d4a3cf6aSroy 			if (!powerof2(newlen)) {
104d4a3cf6aSroy 				/* Grow the buffer to the next power of 2 */
105d4a3cf6aSroy 				newlen--;
106d4a3cf6aSroy 				newlen |= newlen >> 1;
107d4a3cf6aSroy 				newlen |= newlen >> 2;
108d4a3cf6aSroy 				newlen |= newlen >> 4;
109d4a3cf6aSroy 				newlen |= newlen >> 8;
110d4a3cf6aSroy 				newlen |= newlen >> 16;
111d4a3cf6aSroy #if SIZE_T_MAX > 0xffffffffU
112d4a3cf6aSroy 				newlen |= newlen >> 32;
113d4a3cf6aSroy #endif
114d4a3cf6aSroy 				newlen++;
115d4a3cf6aSroy 			}
116d4a3cf6aSroy 
117d4a3cf6aSroy 			newb = realloc(*buf, newlen);
118d4a3cf6aSroy 			if (newb == NULL)
119d4a3cf6aSroy 				goto error;
120d4a3cf6aSroy 			*buf = newb;
121d4a3cf6aSroy 			*buflen = newlen;
122d4a3cf6aSroy 		}
123d4a3cf6aSroy 
124cfbb35edSchristos 		(void)memcpy((*buf + off), fp->_p, len);
125cfef64a1Schristos 		/* Safe, len is never greater than what fp->_r can fit. */
126cfef64a1Schristos 		fp->_r -= (int)len;
127cfef64a1Schristos 		fp->_p += (int)len;
128d4a3cf6aSroy 		off += len;
129755657beSroy 	} while (p == NULL);
130755657beSroy 
131755657beSroy 	/* POSIX demands we return -1 on EOF. */
132755657beSroy 	if (off == 0)
133755657beSroy 		return -1;
134755657beSroy 
135d4a3cf6aSroy 	if (*buf != NULL)
136d4a3cf6aSroy 		*(*buf + off) = '\0';
137d4a3cf6aSroy 	return off;
138d4a3cf6aSroy 
139d4a3cf6aSroy error:
140755657beSroy 	fp->_flags |= __SERR;
141d4a3cf6aSroy 	return -1;
142d4a3cf6aSroy }
1437cfa0468Sroy 
1447cfa0468Sroy ssize_t
getdelim(char ** __restrict buf,size_t * __restrict buflen,int sep,FILE * __restrict fp)1457cfa0468Sroy getdelim(char **__restrict buf, size_t *__restrict buflen,
1467cfa0468Sroy     int sep, FILE *__restrict fp)
1477cfa0468Sroy {
1487cfa0468Sroy 	ssize_t n;
1497cfa0468Sroy 
1507cfa0468Sroy 	FLOCKFILE(fp);
1517cfa0468Sroy 	n = __getdelim(buf, buflen, sep, fp);
1527cfa0468Sroy 	FUNLOCKFILE(fp);
1537cfa0468Sroy 	return n;
1547cfa0468Sroy }
155