1/*	$OpenBSD: fgetln.c,v 1.7 2005/08/08 08:05:36 espie Exp $ */
2/*-
3 * Copyright (c) 1990, 1993
4 *	The Regents of the University of California.  All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * Chris Torek.
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 * 3. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include "local.h"
38
39/*
40 * Expand the line buffer.  Return -1 on error.
41#ifdef notdef
42 * The `new size' does not account for a terminating '\0',
43 * so we add 1 here.
44#endif
45 */
46int
47__slbexpand(FILE *fp, size_t newsize)
48{
49	void *p;
50
51#ifdef notdef
52	++newsize;
53#endif
54	if ((size_t)fp->_lb._size >= newsize)
55		return (0);
56	if ((p = realloc(fp->_lb._base, newsize)) == NULL)
57		return (-1);
58	fp->_lb._base = p;
59	fp->_lb._size = newsize;
60	return (0);
61}
62
63/*
64 * Get an input line.  The returned pointer often (but not always)
65 * points into a stdio buffer.  Fgetline does not alter the text of
66 * the returned line (which is thus not a C string because it will
67 * not necessarily end with '\0'), but does allow callers to modify
68 * it if they wish.  Thus, we set __SMOD in case the caller does.
69 */
70char *
71fgetln(FILE *fp, size_t *lenp)
72{
73	unsigned char *p;
74	size_t len;
75	size_t off;
76
77	/* make sure there is input */
78	if (fp->_r <= 0 && __srefill(fp)) {
79		*lenp = 0;
80		return (NULL);
81	}
82
83	/* look for a newline in the input */
84	if ((p = memchr((void *)fp->_p, '\n', fp->_r)) != NULL) {
85		char *ret;
86
87		/*
88		 * Found one.  Flag buffer as modified to keep fseek from
89		 * `optimising' a backward seek, in case the user stomps on
90		 * the text.
91		 */
92		p++;		/* advance over it */
93		ret = (char *)fp->_p;
94		*lenp = len = p - fp->_p;
95		fp->_flags |= __SMOD;
96		fp->_r -= len;
97		fp->_p = p;
98		return (ret);
99	}
100
101	/*
102	 * We have to copy the current buffered data to the line buffer.
103	 * As a bonus, though, we can leave off the __SMOD.
104	 *
105	 * OPTIMISTIC is length that we (optimistically) expect will
106	 * accommodate the `rest' of the string, on each trip through the
107	 * loop below.
108	 */
109#define OPTIMISTIC 80
110
111	for (len = fp->_r, off = 0;; len += fp->_r) {
112		size_t diff;
113
114		/*
115		 * Make sure there is room for more bytes.  Copy data from
116		 * file buffer to line buffer, refill file and look for
117		 * newline.  The loop stops only when we find a newline.
118		 */
119		if (__slbexpand(fp, len + OPTIMISTIC))
120			goto error;
121		(void)memcpy((void *)(fp->_lb._base + off), (void *)fp->_p,
122		    len - off);
123		off = len;
124		if (__srefill(fp))
125			break;	/* EOF or error: return partial line */
126		if ((p = memchr((void *)fp->_p, '\n', fp->_r)) == NULL)
127			continue;
128
129		/* got it: finish up the line (like code above) */
130		p++;
131		diff = p - fp->_p;
132		len += diff;
133		if (__slbexpand(fp, len))
134			goto error;
135		(void)memcpy((void *)(fp->_lb._base + off), (void *)fp->_p,
136		    diff);
137		fp->_r -= diff;
138		fp->_p = p;
139		break;
140	}
141	*lenp = len;
142#ifdef notdef
143	fp->_lb._base[len] = '\0';
144#endif
145	return ((char *)fp->_lb._base);
146
147error:
148	*lenp = 0;		/* ??? */
149	return (NULL);		/* ??? */
150}
151