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