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