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