fgetln.c revision 1.8 1 /* $NetBSD: fgetln.c,v 1.8 1999/09/16 11:45:26 lukem Exp $ */
2
3 /*-
4 * Copyright (c) 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Chris Torek.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 #if defined(LIBC_SCCS) && !defined(lint)
41 #if 0
42 static char sccsid[] = "@(#)fgetline.c 8.1 (Berkeley) 6/4/93";
43 #else
44 __RCSID("$NetBSD: fgetln.c,v 1.8 1999/09/16 11:45:26 lukem Exp $");
45 #endif
46 #endif /* LIBC_SCCS and not lint */
47
48 #include "namespace.h"
49
50 #include <assert.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include "local.h"
55 #include "reentrant.h"
56
57 #ifdef __weak_alias
58 __weak_alias(fgetln,_fgetln);
59 #endif
60
61 int __slbexpand __P((FILE *, size_t));
62
63 /*
64 * Expand the line buffer. Return -1 on error.
65 #ifdef notdef
66 * The `new size' does not account for a terminating '\0',
67 * so we add 1 here.
68 #endif
69 */
70 int
71 __slbexpand(fp, newsize)
72 FILE *fp;
73 size_t newsize;
74 {
75 void *p;
76
77 #ifdef notdef
78 ++newsize;
79 #endif
80 _DIAGASSERT(fp != NULL);
81 #ifdef _DIAGNOSTIC
82 if (fp == NULL)
83 return (-1);
84 #endif
85
86 if (fp->_lb._size >= newsize)
87 return (0);
88 if ((p = realloc(fp->_lb._base, newsize)) == NULL)
89 return (-1);
90 fp->_lb._base = p;
91 fp->_lb._size = newsize;
92 return (0);
93 }
94
95 /*
96 * Get an input line. The returned pointer often (but not always)
97 * points into a stdio buffer. Fgetline does not alter the text of
98 * the returned line (which is thus not a C string because it will
99 * not necessarily end with '\0'), but does allow callers to modify
100 * it if they wish. Thus, we set __SMOD in case the caller does.
101 */
102 char *
103 fgetln(fp, lenp)
104 FILE *fp;
105 size_t *lenp;
106 {
107 unsigned char *p;
108 size_t len;
109 size_t off;
110
111 _DIAGASSERT(fp != NULL);
112 _DIAGASSERT(lenp != NULL);
113 #ifdef _DIAGNOSTIC
114 if (fp == NULL || lenp == NULL)
115 return (NULL);
116 #endif
117
118 FLOCKFILE(fp);
119
120 /* make sure there is input */
121 if (fp->_r <= 0 && __srefill(fp)) {
122 *lenp = 0;
123 FUNLOCKFILE(fp);
124 return (NULL);
125 }
126
127 /* look for a newline in the input */
128 if ((p = memchr((void *)fp->_p, '\n', (size_t)fp->_r)) != NULL) {
129 char *ret;
130
131 /*
132 * Found one. Flag buffer as modified to keep fseek from
133 * `optimising' a backward seek, in case the user stomps on
134 * the text.
135 */
136 p++; /* advance over it */
137 ret = (char *)fp->_p;
138 *lenp = len = p - fp->_p;
139 fp->_flags |= __SMOD;
140 fp->_r -= len;
141 fp->_p = p;
142 FUNLOCKFILE(fp);
143 return (ret);
144 }
145
146 /*
147 * We have to copy the current buffered data to the line buffer.
148 * As a bonus, though, we can leave off the __SMOD.
149 *
150 * OPTIMISTIC is length that we (optimistically) expect will
151 * accomodate the `rest' of the string, on each trip through the
152 * loop below.
153 */
154 #define OPTIMISTIC 80
155
156 for (len = fp->_r, off = 0;; len += fp->_r) {
157 size_t diff;
158
159 /*
160 * Make sure there is room for more bytes. Copy data from
161 * file buffer to line buffer, refill file and look for
162 * newline. The loop stops only when we find a newline.
163 */
164 if (__slbexpand(fp, len + OPTIMISTIC))
165 goto error;
166 (void)memcpy((void *)(fp->_lb._base + off), (void *)fp->_p,
167 len - off);
168 off = len;
169 if (__srefill(fp))
170 break; /* EOF or error: return partial line */
171 if ((p = memchr((void *)fp->_p, '\n', (size_t)fp->_r)) == NULL)
172 continue;
173
174 /* got it: finish up the line (like code above) */
175 p++;
176 diff = p - fp->_p;
177 len += diff;
178 if (__slbexpand(fp, len))
179 goto error;
180 (void)memcpy((void *)(fp->_lb._base + off), (void *)fp->_p,
181 diff);
182 fp->_r -= diff;
183 fp->_p = p;
184 break;
185 }
186 *lenp = len;
187 #ifdef notdef
188 fp->_lb._base[len] = 0;
189 #endif
190 FUNLOCKFILE(fp);
191 return ((char *)fp->_lb._base);
192
193 error:
194 *lenp = 0; /* ??? */
195 FUNLOCKFILE(fp);
196 return (NULL); /* ??? */
197 }
198