update from main archive
[kopensolaris-gnu/glibc.git] / stdio / getdelim.c
1 /* Copyright (C) 1991, 1992, 1995, 1996 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 <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 ((PTR) p, stream->__bufp, terminator, i);
139           if (found != NULL)
140             {
141               stream->__bufp += found - p;
142               p = found;
143               goto win;
144             }
145
146           stream->__bufp += i;
147           p += i;
148           copy -= i;
149           if (copy == 0)
150             {
151               /* Need to enlarge the line buffer.  */
152               size_t len = p - line;
153               size *= 2;
154               line = realloc (line, size);
155               if (line == NULL)
156                 goto lose;
157               *lineptr = line;
158               *n = size;
159               p = line + len;
160               copy = size - len;
161               /* Leave space for the terminating null.  */
162               --copy;
163             }
164         }
165     }
166
167  lose:
168   if (p == *lineptr)
169     return -1;
170   /* Return a partial line since we got an error in the middle.  */
171  win:
172   *p = '\0';
173   return p - *lineptr;
174 }
175
176 weak_alias (__getdelim, getdelim)