entered into RCS
[kopensolaris-gnu/glibc.git] / stdio / getdelim.c
1 /* Copyright (C) 1991, 1992 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
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, USA.  */
18
19 #include <ansidecl.h>
20 #include <stddef.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <limits.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 DEFUN(__getdelim, (lineptr, n, terminator, stream),
34       char **lineptr AND size_t *n AND int terminator AND FILE *stream)
35 {
36   char *line, *p;
37   size_t size, copy;
38
39   if (!__validfp (stream) || lineptr == NULL || n == NULL)
40     {
41       errno = EINVAL;
42       return -1;
43     }
44
45   if (ferror (stream))
46     return -1;
47
48   /* Make sure we have a line buffer to start with.  */
49   if (*lineptr == NULL || *n < 2) /* !seen and no buf yet need 2 chars.  */
50     {
51 #ifndef MAX_CANON
52 #define MAX_CANON       256
53 #endif
54       line = realloc (*lineptr, MAX_CANON);
55       if (line == NULL)
56         return -1;
57       *lineptr = line;
58       *n = MAX_CANON;
59     }
60
61   line = *lineptr;
62   size = *n;
63
64   copy = size;
65   p = line;
66
67   if (stream->__buffer == NULL && stream->__userbuf)
68     {
69       /* Unbuffered stream.  Not much optimization to do.  */
70
71       while (1)
72         {
73           size_t len;
74
75           while (--copy > 0)
76             {
77               register int c = getc (stream);
78               if (c == EOF)
79                 goto lose;
80               else if ((*p++ = c) == terminator)
81                 goto win;
82             }
83
84           /* Need to enlarge the line buffer.  */
85           len = p - line;
86           size *= 2;
87           line = realloc (line, size);
88           if (line == NULL)
89             goto lose;
90           *lineptr = line;
91           *n = size;
92           p = line + len;
93           copy = size - len;
94         }
95     }
96   else
97     {
98       /* Leave space for the terminating null.  */
99       --copy;
100
101       if (!stream->__seen || stream->__buffer == NULL || stream->__pushed_back)
102         {
103           /* Do one with getc to allocate a buffer.  */
104           int c = getc (stream);
105           if (c == EOF)
106             goto lose;
107           *p++ = c;
108           if (c == terminator)
109             goto win;
110           --copy;
111         }
112
113       while (1)
114         {
115           size_t i;
116           char *found;
117
118           i = stream->__get_limit - stream->__bufp;     
119           if (i == 0)
120             {
121               /* Refill the buffer.  */
122               int c = __fillbf (stream);
123               if (c == EOF)
124                 goto lose;
125               *p++ = c;
126               if (c == terminator)
127                 goto win;
128               i = stream->__get_limit - stream->__bufp; 
129             }
130
131           if (i > copy)
132             i = copy;
133
134           found = (char *) __memccpy ((PTR) p, stream->__bufp, terminator, i);
135           if (found != NULL)
136             {
137               stream->__bufp += found - p;
138               p = found;
139               goto win;
140             }
141
142           stream->__bufp += i;
143           p += i;
144           copy -= i;
145           if (copy == 0)
146             {
147               /* Need to enlarge the line buffer.  */
148               size_t len = p - line;
149               size *= 2;
150               line = realloc (line, size);
151               if (line == NULL)
152                 goto lose;
153               *lineptr = line;
154               *n = size;
155               p = line + len;
156               copy = size - len;
157               /* Leave space for the terminating null.  */
158               --copy;
159             }
160         }
161     }
162
163  lose:
164   if (p == *lineptr)
165     return -1;
166   /* Return a partial line since we got an error in the middle.  */
167  win:
168   *p = '\0';
169   return p - *lineptr;
170 }