source: vendor/netbsd/5-20091104/src/lib/libc/stdio/fvwrite.c @ 160

Last change on this file since 160 was 1, checked in by bouyer, 8 years ago

Import of NetBSD netbsd-5 as or 4/11/2009

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