1 |
|
|
/* $OpenBSD: fvwrite.c,v 1.20 2017/03/17 16:06:33 millert Exp $ */ |
2 |
|
|
/*- |
3 |
|
|
* Copyright (c) 1990, 1993 |
4 |
|
|
* The Regents of the University of California. All rights reserved. |
5 |
|
|
* |
6 |
|
|
* This code is derived from software contributed to Berkeley by |
7 |
|
|
* Chris Torek. |
8 |
|
|
* |
9 |
|
|
* Redistribution and use in source and binary forms, with or without |
10 |
|
|
* modification, are permitted provided that the following conditions |
11 |
|
|
* are met: |
12 |
|
|
* 1. Redistributions of source code must retain the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer. |
14 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
|
|
* notice, this list of conditions and the following disclaimer in the |
16 |
|
|
* documentation and/or other materials provided with the distribution. |
17 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
18 |
|
|
* may be used to endorse or promote products derived from this software |
19 |
|
|
* without specific prior written permission. |
20 |
|
|
* |
21 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
22 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
23 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
24 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
25 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
26 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
27 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
28 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
29 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
30 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
31 |
|
|
* SUCH DAMAGE. |
32 |
|
|
*/ |
33 |
|
|
|
34 |
|
|
#include <stdio.h> |
35 |
|
|
#include <stdlib.h> |
36 |
|
|
#include <string.h> |
37 |
|
|
#include <errno.h> |
38 |
|
|
#include <unistd.h> |
39 |
|
|
#include "local.h" |
40 |
|
|
#include "fvwrite.h" |
41 |
|
|
|
42 |
|
|
/* |
43 |
|
|
* Write some memory regions. Return zero on success, EOF on error. |
44 |
|
|
* |
45 |
|
|
* This routine is large and unsightly, but most of the ugliness due |
46 |
|
|
* to the three different kinds of output buffering is handled here. |
47 |
|
|
*/ |
48 |
|
|
int |
49 |
|
|
__sfvwrite(FILE *fp, struct __suio *uio) |
50 |
|
|
{ |
51 |
|
|
size_t len; |
52 |
|
|
char *p; |
53 |
|
|
struct __siov *iov; |
54 |
|
|
int w, s; |
55 |
|
|
char *nl; |
56 |
|
|
int nlknown, nldist; |
57 |
|
|
|
58 |
✗✓ |
3390 |
if ((len = uio->uio_resid) == 0) |
59 |
|
|
return (0); |
60 |
|
|
/* make sure we can write */ |
61 |
✓✓✓✓ ✗✓ |
3399 |
if (cantwrite(fp)) { |
62 |
|
|
errno = EBADF; |
63 |
|
|
return (EOF); |
64 |
|
|
} |
65 |
|
|
|
66 |
|
|
#define MIN(a, b) ((a) < (b) ? (a) : (b)) |
67 |
|
|
#define COPY(n) (void)memcpy(fp->_p, p, n) |
68 |
|
|
|
69 |
|
1695 |
iov = uio->uio_iov; |
70 |
|
1695 |
p = iov->iov_base; |
71 |
|
1695 |
len = iov->iov_len; |
72 |
|
1695 |
iov++; |
73 |
|
|
#define GETIOV(extra_work) \ |
74 |
|
|
while (len == 0) { \ |
75 |
|
|
extra_work; \ |
76 |
|
|
p = iov->iov_base; \ |
77 |
|
|
len = iov->iov_len; \ |
78 |
|
|
iov++; \ |
79 |
|
|
} |
80 |
✓✓ |
1695 |
if (fp->_flags & __SNBF) { |
81 |
|
|
/* |
82 |
|
|
* Unbuffered: write up to BUFSIZ bytes at a time. |
83 |
|
|
*/ |
84 |
|
12 |
do { |
85 |
✗✓ |
24 |
GETIOV(;); |
86 |
|
12 |
w = (*fp->_write)(fp->_cookie, p, MIN(len, BUFSIZ)); |
87 |
✓✗ |
12 |
if (w <= 0) |
88 |
|
|
goto err; |
89 |
|
12 |
p += w; |
90 |
|
12 |
len -= w; |
91 |
✗✓ |
12 |
} while ((uio->uio_resid -= w) != 0); |
92 |
✓✓ |
1683 |
} else if ((fp->_flags & __SLBF) == 0) { |
93 |
|
|
/* |
94 |
|
|
* Fully buffered: fill partially full buffer, if any, |
95 |
|
|
* and then flush. If there is no partial buffer, write |
96 |
|
|
* one _bf._size byte chunk directly (without copying). |
97 |
|
|
* |
98 |
|
|
* String output is a special case: write as many bytes |
99 |
|
|
* as fit, but pretend we wrote everything. This makes |
100 |
|
|
* snprintf() return the number of bytes needed, rather |
101 |
|
|
* than the number used, and avoids its write function |
102 |
|
|
* (so that the write function can be invalid). |
103 |
|
|
*/ |
104 |
|
1655 |
do { |
105 |
✓✓ |
6478 |
GETIOV(;); |
106 |
✗✗ |
2447 |
if ((fp->_flags & (__SALC | __SSTR)) == |
107 |
✗✓ |
2447 |
(__SALC | __SSTR) && fp->_w < len) { |
108 |
|
|
size_t blen = fp->_p - fp->_bf._base; |
109 |
|
|
int pgmsk = getpagesize() - 1; |
110 |
|
|
unsigned char *_base; |
111 |
|
|
int _size; |
112 |
|
|
|
113 |
|
|
/* Round up to nearest page. */ |
114 |
|
|
_size = ((blen + len + 1 + pgmsk) & ~pgmsk) - 1; |
115 |
|
|
_base = recallocarray(fp->_bf._base, |
116 |
|
|
fp->_bf._size + 1, _size + 1, 1); |
117 |
|
|
if (_base == NULL) |
118 |
|
|
goto err; |
119 |
|
|
fp->_w += _size - fp->_bf._size; |
120 |
|
|
fp->_bf._base = _base; |
121 |
|
|
fp->_bf._size = _size; |
122 |
|
|
fp->_p = _base + blen; |
123 |
|
|
} |
124 |
|
2447 |
w = fp->_w; |
125 |
✓✓ |
2447 |
if (fp->_flags & __SSTR) { |
126 |
✓✗ |
2304 |
if (len < w) |
127 |
|
2304 |
w = len; |
128 |
|
2304 |
COPY(w); /* copy MIN(fp->_w,len), */ |
129 |
|
2304 |
fp->_w -= w; |
130 |
|
2304 |
fp->_p += w; |
131 |
|
2304 |
w = len; /* but pretend copied all */ |
132 |
✓✓✗✓
|
2501 |
} else if (fp->_p > fp->_bf._base && len > w) { |
133 |
|
|
/* fill and flush */ |
134 |
|
|
COPY(w); |
135 |
|
|
/* fp->_w -= w; */ /* unneeded */ |
136 |
|
|
fp->_p += w; |
137 |
|
|
if (__sflush(fp)) |
138 |
|
|
goto err; |
139 |
✗✓ |
143 |
} else if (len >= (w = fp->_bf._size)) { |
140 |
|
|
/* write directly */ |
141 |
|
|
w = (*fp->_write)(fp->_cookie, p, w); |
142 |
|
|
if (w <= 0) |
143 |
|
|
goto err; |
144 |
|
|
} else { |
145 |
|
|
/* fill and done */ |
146 |
|
143 |
w = len; |
147 |
|
143 |
COPY(w); |
148 |
|
143 |
fp->_w -= w; |
149 |
|
143 |
fp->_p += w; |
150 |
|
|
} |
151 |
|
2447 |
p += w; |
152 |
|
2447 |
len -= w; |
153 |
✓✓ |
2447 |
} while ((uio->uio_resid -= w) != 0); |
154 |
|
|
} else { |
155 |
|
|
/* |
156 |
|
|
* Line buffered: like fully buffered, but we |
157 |
|
|
* must check for newlines. Compute the distance |
158 |
|
|
* to the first newline (including the newline), |
159 |
|
|
* or `infinity' if there is none, then pretend |
160 |
|
|
* that the amount to write is MIN(len,nldist). |
161 |
|
|
*/ |
162 |
|
|
nlknown = 0; |
163 |
|
|
nldist = 0; /* XXX just to keep gcc happy */ |
164 |
|
28 |
do { |
165 |
✓✓ |
104 |
GETIOV(nlknown = 0); |
166 |
✓✗ |
40 |
if (!nlknown) { |
167 |
|
40 |
nl = memchr(p, '\n', len); |
168 |
✓✓ |
120 |
nldist = nl ? nl + 1 - p : len + 1; |
169 |
|
|
nlknown = 1; |
170 |
|
40 |
} |
171 |
|
40 |
s = MIN(len, nldist); |
172 |
|
40 |
w = fp->_w + fp->_bf._size; |
173 |
✓✓✗✓
|
72 |
if (fp->_p > fp->_bf._base && s > w) { |
174 |
|
|
COPY(w); |
175 |
|
|
/* fp->_w -= w; */ |
176 |
|
|
fp->_p += w; |
177 |
|
|
if (__sflush(fp)) |
178 |
|
|
goto err; |
179 |
✗✓ |
40 |
} else if (s >= (w = fp->_bf._size)) { |
180 |
|
|
w = (*fp->_write)(fp->_cookie, p, w); |
181 |
|
|
if (w <= 0) |
182 |
|
|
goto err; |
183 |
|
|
} else { |
184 |
|
|
w = s; |
185 |
|
40 |
COPY(w); |
186 |
|
40 |
fp->_w -= w; |
187 |
|
40 |
fp->_p += w; |
188 |
|
|
} |
189 |
✓✓ |
40 |
if ((nldist -= w) == 0) { |
190 |
|
|
/* copied the newline: flush and forget */ |
191 |
✓✗ |
8 |
if (__sflush(fp)) |
192 |
|
|
goto err; |
193 |
|
|
nlknown = 0; |
194 |
|
8 |
} |
195 |
|
40 |
p += w; |
196 |
|
40 |
len -= w; |
197 |
✓✓ |
40 |
} while ((uio->uio_resid -= w) != 0); |
198 |
|
|
} |
199 |
|
1695 |
return (0); |
200 |
|
|
|
201 |
|
|
err: |
202 |
|
|
fp->_flags |= __SERR; |
203 |
|
|
return (EOF); |
204 |
|
1695 |
} |