Home | History | Annotate | Download | only in Stdio
      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  */
     62 int
     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 
    226 err:
    227   fp->_flags |= __SERR;
    228   return (EOF);
    229 }
    230