1999-01-22 Roland McGrath <roland@baalperazim.frob.com>
[kopensolaris-gnu/glibc.git] / stdio / getdelim.c
1 /* Copyright (C) 1991, 1992, 1995, 1996, 1997 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public
15    License along with the GNU C Library; see the file COPYING.LIB.  If not,
16    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17    Boston, MA 02111-1307, USA.  */
18
19 #include <stddef.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <limits.h>
24 #include <errno.h>
25
26 /* Read up to (and including) a TERMINATOR from STREAM into *LINEPTR
27    (and null-terminate it). *LINEPTR is a pointer returned from malloc (or
28    NULL), pointing to *N characters of space.  It is realloc'd as
29    necessary.  Returns the number of characters read (not including the
30    null terminator), or -1 on error or EOF.  */
31
32 ssize_t
33 __getdelim (lineptr, n, terminator, stream)
34      char **lineptr;
35      size_t *n;
36      int terminator;
37      FILE *stream;
38 {
39   char *line, *p;
40   size_t size, copy;
41
42   if (!__validfp (stream) || lineptr == NULL || n == NULL)
43     {
44       __set_errno (EINVAL);
45       return -1;
46     }
47
48   if (ferror (stream))
49     return -1;
50
51   /* Make sure we have a line buffer to start with.  */
52   if (*lineptr == NULL || *n < 2) /* !seen and no buf yet need 2 chars.  */
53     {
54 #ifndef MAX_CANON
55 #define MAX_CANON       256
56 #endif
57       line = realloc (*lineptr, MAX_CANON);
58       if (line == NULL)
59         return -1;
60       *lineptr = line;
61       *n = MAX_CANON;
62     }
63
64   line = *lineptr;
65   size = *n;
66
67   copy = size;
68   p = line;
69
70   if (stream->__buffer == NULL && stream->__userbuf)
71     {
72       /* Unbuffered stream.  Not much optimization to do.  */
73
74       while (1)
75         {
76           size_t len;
77
78           while (--copy > 0)
79             {
80               register int c = getc (stream);
81               if (c == EOF)
82                 goto lose;
83               else if ((*p++ = c) == terminator)
84                 goto win;
85             }
86
87           /* Need to enlarge the line buffer.  */
88           len = p - line;
89           size *= 2;
90           line = realloc (line, size);
91           if (line == NULL)
92             goto lose;
93           *lineptr = line;
94           *n = size;
95           p = line + len;
96           copy = size - len;
97         }
98     }
99   else
100     {
101       /* Leave space for the terminating null.  */
102       --copy;
103
104       if (!stream->__seen || stream->__buffer == NULL || stream->__pushed_back)
105         {
106           /* Do one with getc to allocate a buffer.  */
107           int c = getc (stream);
108           if (c == EOF)
109             goto lose;
110           *p++ = c;
111           if (c == terminator)
112             goto win;
113           --copy;
114         }
115
116       while (1)
117         {
118           size_t i;
119           char *found;
120
121           i = stream->__get_limit - stream->__bufp;
122           if (i == 0)
123             {
124               /* Refill the buffer.  */
125               int c = __fillbf (stream);
126               if (c == EOF)
127                 goto lose;
128               *p++ = c;
129               if (c == terminator)
130                 goto win;
131               --copy;
132               i = stream->__get_limit - stream->__bufp;
133             }
134
135           if (i > copy)
136             i = copy;
137
138           found = (char *) __memccpy ((void *) p, stream->__bufp, 
139                                       terminator, i);
140           if (found != NULL)
141             {
142               stream->__bufp += found - p;
143               p = found;
144               goto win;
145             }
146
147           stream->__bufp += i;
148           p += i;
149           copy -= i;
150           if (copy == 0)
151             {
152               /* Need to enlarge the line buffer.  */
153               size_t len = p - line;
154               size *= 2;
155               line = realloc (line, size);
156               if (line == NULL)
157                 goto lose;
158               *lineptr = line;
159               *n = size;
160               p = line + len;
161               copy = size - len;
162               /* Leave space for the terminating null.  */
163               --copy;
164             }
165         }
166     }
167
168  lose:
169   if (p == *lineptr)
170     return -1;
171   /* Return a partial line since we got an error in the middle.  */
172  win:
173   *p = '\0';
174   return p - *lineptr;
175 }
176
177 weak_alias (__getdelim, getdelim)