Upgrade awk(1). 1/2
[dragonfly.git] / lib / libc / stdio / getdelim.c
1 /*-
2  * Copyright (c) 2009 David Schultz <das@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/lib/libc/stdio/getdelim.c,v 1.2 2009/04/06 13:50:04 das Exp $
27  */
28
29 #include "namespace.h"
30 #include <sys/param.h>
31 #include <errno.h>
32 #include <limits.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include "un-namespace.h"
37
38 #include "libc_private.h"
39 #include "local.h"
40
41 static inline size_t
42 p2roundup(size_t n)
43 {
44
45         if (!powerof2(n)) {
46                 n--;
47                 n |= n >> 1;
48                 n |= n >> 2;
49                 n |= n >> 4;
50                 n |= n >> 8;
51                 n |= n >> 16;
52 #if SIZE_T_MAX > 0xffffffffU
53                 n |= n >> 32;
54 #endif
55                 n++;
56         }
57         return (n);
58 }
59
60 /*
61  * Expand *linep to hold len bytes (up to SSIZE_MAX + 1).
62  */
63 static inline int
64 expandtofit(char ** __restrict linep, size_t len, size_t * __restrict capp)
65 {
66         char *newline;
67         size_t newcap;
68
69         if (len > (size_t)SSIZE_MAX + 1) {
70                 errno = EOVERFLOW;
71                 return (-1);
72         }
73         if (len > *capp) {
74                 if (len == (size_t)SSIZE_MAX + 1)       /* avoid overflow */
75                         newcap = (size_t)SSIZE_MAX + 1;
76                 else
77                         newcap = p2roundup(len);
78                 newline = realloc(*linep, newcap);
79                 if (newline == NULL)
80                         return (-1);
81                 *capp = newcap;
82                 *linep = newline;
83         }
84         return (0);
85 }
86
87 /*
88  * Append the src buffer to the *dstp buffer. The buffers are of
89  * length srclen and *dstlenp, respectively, and dst has space for
90  * *dstlenp bytes. After the call, *dstlenp and *dstcapp are updated
91  * appropriately, and *dstp is reallocated if needed. Returns 0 on
92  * success, -1 on allocation failure.
93  */
94 static int
95 sappend(char ** __restrict dstp, size_t * __restrict dstlenp,
96         size_t * __restrict dstcapp, char * __restrict src, size_t srclen)
97 {
98
99         /* ensure room for srclen + dstlen + terminating NUL */
100         if (expandtofit(dstp, srclen + *dstlenp + 1, dstcapp))
101                 return (-1);
102         memcpy(*dstp + *dstlenp, src, srclen);
103         *dstlenp += srclen;
104         return (0);
105 }
106
107 ssize_t
108 getdelim(char ** __restrict linep, size_t * __restrict linecapp, int delim,
109          FILE * __restrict fp)
110 {
111         u_char *endp;
112         size_t linelen;
113
114         FLOCKFILE(fp);
115         ORIENT(fp, -1);
116
117         if (linep == NULL || linecapp == NULL) {
118                 errno = EINVAL;
119                 goto error;
120         }
121
122         if (*linep == NULL)
123                 *linecapp = 0;
124
125         if (fp->pub._r <= 0 && __srefill(fp)) {
126                 /* If fp is at EOF already, we just need space for the NUL. */
127                 if (__sferror(fp) || expandtofit(linep, 1, linecapp))
128                         goto error;
129                 FUNLOCKFILE(fp);
130                 (*linep)[0] = '\0';
131                 return (-1);
132         }
133
134         linelen = 0;
135         while ((endp = memchr(fp->pub._p, delim, fp->pub._r)) == NULL) {
136                 if (sappend(linep, &linelen, linecapp, fp->pub._p, fp->pub._r))
137                         goto error;
138                 if (__srefill(fp)) {
139                         if (__sferror(fp))
140                                 goto error;
141                         goto done;      /* hit EOF */
142                 }
143         }
144         endp++; /* snarf the delimiter, too */
145         if (sappend(linep, &linelen, linecapp, fp->pub._p, endp - fp->pub._p))
146                 goto error;
147         fp->pub._r -= endp - fp->pub._p;
148         fp->pub._p = endp;
149 done:
150         /* Invariant: *linep has space for at least linelen+1 bytes. */
151         (*linep)[linelen] = '\0';
152         FUNLOCKFILE(fp);
153         return (linelen);
154
155 error:
156         fp->pub._flags |= __SERR;
157         FUNLOCKFILE(fp);
158         return (-1);
159 }