2cdb95c2a63536b18b1a9bc1acb44b55c01380f7
[kopensolaris-gnu/glibc.git] / stdio / getdelim.c
1 /* Copyright (C) 1991, 1992, 1995 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 #include <errno.h>
26
27 /* Read up to (and including) a TERMINATOR from STREAM into *LINEPTR
28    (and null-terminate it). *LINEPTR is a pointer returned from malloc (or
29    NULL), pointing to *N characters of space.  It is realloc'd as
30    necessary.  Returns the number of characters read (not including the
31    null terminator), or -1 on error or EOF.  */
32
33 ssize_t
34 DEFUN(__getdelim, (lineptr, n, terminator, stream),
35       char **lineptr AND size_t *n AND int terminator AND FILE *stream)
36 {
37   char *line, *p;
38   size_t size, copy;
39
40   if (!__validfp (stream) || lineptr == NULL || n == NULL)
41     {
42       errno = EINVAL;
43       return -1;
44     }
45
46   if (ferror (stream))
47     return -1;
48
49   /* Make sure we have a line buffer to start with.  */
50   if (*lineptr == NULL || *n < 2) /* !seen and no buf yet need 2 chars.  */
51     {
52 #ifndef MAX_CANON
53 #define MAX_CANON       256
54 #endif
55       line = realloc (*lineptr, MAX_CANON);
56       if (line == NULL)
57         return -1;
58       *lineptr = line;
59       *n = MAX_CANON;
60     }
61
62   line = *lineptr;
63   size = *n;
64
65   copy = size;
66   p = line;
67
68   if (stream->__buffer == NULL && stream->__userbuf)
69     {
70       /* Unbuffered stream.  Not much optimization to do.  */
71
72       while (1)
73         {
74           size_t len;
75
76           while (--copy > 0)
77             {
78               register int c = getc (stream);
79               if (c == EOF)
80                 goto lose;
81               else if ((*p++ = c) == terminator)
82                 goto win;
83             }
84
85           /* Need to enlarge the line buffer.  */
86           len = p - line;
87           size *= 2;
88           line = realloc (line, size);
89           if (line == NULL)
90             goto lose;
91           *lineptr = line;
92           *n = size;
93           p = line + len;
94           copy = size - len;
95         }
96     }
97   else
98     {
99       /* Leave space for the terminating null.  */
100       --copy;
101
102       if (!stream->__seen || stream->__buffer == NULL || stream->__pushed_back)
103         {
104           /* Do one with getc to allocate a buffer.  */
105           int c = getc (stream);
106           if (c == EOF)
107             goto lose;
108           *p++ = c;
109           if (c == terminator)
110             goto win;
111           --copy;
112         }
113
114       while (1)
115         {
116           size_t i;
117           char *found;
118
119           i = stream->__get_limit - stream->__bufp;     
120           if (i == 0)
121             {
122               /* Refill the buffer.  */
123               int c = __fillbf (stream);
124               if (c == EOF)
125                 goto lose;
126               *p++ = c;
127               if (c == terminator)
128                 goto win;
129               --copy;
130               i = stream->__get_limit - stream->__bufp; 
131             }
132
133           if (i > copy)
134             i = copy;
135
136           found = (char *) __memccpy ((PTR) p, stream->__bufp, terminator, i);
137           if (found != NULL)
138             {
139               stream->__bufp += found - p;
140               p = found;
141               goto win;
142             }
143
144           stream->__bufp += i;
145           p += i;
146           copy -= i;
147           if (copy == 0)
148             {
149               /* Need to enlarge the line buffer.  */
150               size_t len = p - line;
151               size *= 2;
152               line = realloc (line, size);
153               if (line == NULL)
154                 goto lose;
155               *lineptr = line;
156               *n = size;
157               p = line + len;
158               copy = size - len;
159               /* Leave space for the terminating null.  */
160               --copy;
161             }
162         }
163     }
164
165  lose:
166   if (p == *lineptr)
167     return -1;
168   /* Return a partial line since we got an error in the middle.  */
169  win:
170   *p = '\0';
171   return p - *lineptr;
172 }
173
174 weak_alias (__getdelim, getdelim)