newlib-cygwin/newlib/libc/stdio/fvwrite.c

234 lines
5.5 KiB
C
Raw Normal View History

2000-02-18 03:39:52 +08:00
/* No user fns here. Pesch 15apr92. */
/*
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
2000-02-18 03:39:52 +08:00
#include "local.h"
#include "fvwrite.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define COPY(n) (void) memmove((void *) fp->_p, (void *) p, (size_t) (n))
#define GETIOV(extra_work) \
while (len == 0) \
{ \
extra_work; \
p = iov->iov_base; \
len = iov->iov_len; \
iov++; \
}
/*
* Write some memory regions. Return zero on success, EOF on error.
*
* This routine is large and unsightly, but most of the ugliness due
* to the three different kinds of output buffering is handled here.
*/
int
__sfvwrite (fp, uio)
register FILE *fp;
register struct __suio *uio;
{
register size_t len;
register _CONST char *p;
register struct __siov *iov;
register int w, s;
char *nl;
int nlknown, nldist;
if ((len = uio->uio_resid) == 0)
return 0;
/* make sure we can write */
if (cantwrite (fp))
return EOF;
iov = uio->uio_iov;
len = 0;
#ifdef __SCLE
if (fp->_flags & __SCLE) /* text mode */
{
do
{
GETIOV (;);
while (len > 0)
{
if (putc(*p, fp) == EOF)
return EOF;
p++;
len--;
uio->uio_resid--;
}
}
while (uio->uio_resid > 0);
return 0;
}
#endif
2000-02-18 03:39:52 +08:00
if (fp->_flags & __SNBF)
{
/*
* Unbuffered: write up to BUFSIZ bytes at a time.
*/
do
{
GETIOV (;);
w = (*fp->_write) (fp->_cookie, p, MIN (len, BUFSIZ));
if (w <= 0)
goto err;
p += w;
len -= w;
}
while ((uio->uio_resid -= w) != 0);
}
else if ((fp->_flags & __SLBF) == 0)
{
/*
* Fully buffered: fill partially full buffer, if any,
* and then flush. If there is no partial buffer, write
* one _bf._size byte chunk directly (without copying).
*
* String output is a special case: write as many bytes
* as fit, but pretend we wrote everything. This makes
* snprintf() return the number of bytes needed, rather
* than the number used, and avoids its write function
* (so that the write function can be invalid). If
* we are dealing with the asprintf routines, we will
* dynamically increase the buffer size as needed.
2000-02-18 03:39:52 +08:00
*/
do
{
GETIOV (;);
w = fp->_w;
if (fp->_flags & __SSTR)
{
if (len > w && fp->_flags & __SMBF)
{ /* must be asprintf family */
unsigned char *ptr;
int curpos = (fp->_p - fp->_bf._base);
2003-08-22 Jeff Johnston <jjohnstn@redhat.com> * libc/include/sys/reent.h: Add _GLOBAL_REENT macro. * libc/stdio: Globally remove/replace all references to fp->_data. Replace with _REENT or _GLOBAL_REENT where appropriate. * libc/stdio/asprintf.c: Ditto. * libc/stdio/fclose.c: Ditto. * libc/stdio/fvwrite.c: Ditto. * libc/stdio/makebuf.c: Ditto. * libc/stdio/refill.c: Ditto. * libc/stdio/local.h: Ditto. * libc/stdio/setvbuf.c: Ditto. * libc/stdio/sscanf.c: Ditto. * libc/stdio/stdio.c: Ditto. * libc/stdio/ungetc.c: Ditto. * libc/stdio/vfscanf.c: Ditto. * libc/stdio/vsscanf.c: Ditto. * libc/stdio/fopen.c: Ditto. Also use _fseek_r in _fopen_r. * libc/stdio/vasprintf.c: Ditto. Also call _vfprintf_r directly. * libc/stdio/vsnprintf.c: Ditto. * libc/stdio/vsprintf.c: Ditto. * libc/stdio/fcloseall.c(fcloseall): Use _GLOBAL_REENT macro instead of _REENT to walk file list. * libc/stdio/fflush.c: Ditto. * libc/stdio/fgetpos.c: Add reentrant version and have regular version call reentrant version with _REENT argument. * libc/stdio/fsetpos.c: Ditto. * libc/stdio/fseek.c: Ditto. * libc/stdio/fseeko.c: Ditto. * libc/stdio/ftell.c: Ditto. * libc/stdio/ftello.c: Ditto. * libc/stdio/freopen.c: Ditto. * libc/stdio/findfp.c: Use _GLOBAL_REENT pointer when adding new files to chain. Also use _GLOBAL_REENT pointer for cleaning up. * libc/stdio/fiprintf.c: Reformatted to minimize duplicate code. * libc/stdio/siprintf.c: Ditto. * libc/stdio/iprintf.c: Ditto. * libc/stdio/fprintf.c: Ditto. * libc/stdio/printf.c: Ditto. * libc/stdio/snprintf.c: Call _vfprintf_r directly. * libc/stdio/sprintf.c: Ditto. * libc/stdio/vprintf.c: Ditto. Also add _REENT_ONLY check. * libc/stdio/rewind.c: Call _fseek_r directly. * libc/stdio/tmpfile.c: Call _fopen_r and _remove_r directly. * libc/stdio/vfprintf.c (_VFPRINTF_R): Change _r routines to use data pointer. (get_arg): Add extra struct _reent pointer argument. * libc/stdio64/fgetpos64.c: Add _r versions, remove any reference to fp->_data. * libc/stdio64/fopen64.c: Ditto. * libc/stdio64/freopen64.c: Ditto. * libc/stdio64/fsetpos64.c: Ditto. * libc/stdio64/ftello64.c: Ditto. * libc/stdio64/local64.h: Ditto. * libc/stdio64/stdio64.c: Ditto. * libc/stdio64/fseeko64.c: Ditto plus use _fstat_r instead of _fstat64_r for the meantime.
2003-08-23 02:52:25 +08:00
ptr = (unsigned char *)_realloc_r (_REENT,
fp->_bf._base,
curpos + len);
if (!ptr)
goto err;
fp->_bf._base = ptr;
fp->_p = ptr + curpos;
fp->_bf._size = curpos + len;
w = fp->_w = len;
}
2000-02-18 03:39:52 +08:00
if (len < w)
w = len;
COPY (w); /* copy MIN(fp->_w,len), */
fp->_w -= w;
fp->_p += w;
w = len; /* but pretend copied all */
}
else if (fp->_p > fp->_bf._base && len > w)
{
/* fill and flush */
COPY (w);
/* fp->_w -= w; *//* unneeded */
fp->_p += w;
if (fflush (fp))
goto err;
}
else if (len >= (w = fp->_bf._size))
{
/* write directly */
w = (*fp->_write) (fp->_cookie, p, w);
if (w <= 0)
goto err;
}
else
{
/* fill and done */
w = len;
COPY (w);
fp->_w -= w;
fp->_p += w;
}
p += w;
len -= w;
}
while ((uio->uio_resid -= w) != 0);
}
else
{
/*
* Line buffered: like fully buffered, but we
* must check for newlines. Compute the distance
* to the first newline (including the newline),
* or `infinity' if there is none, then pretend
* that the amount to write is MIN(len,nldist).
*/
nlknown = 0;
do
{
GETIOV (nlknown = 0);
if (!nlknown)
{
nl = memchr ((void *) p, '\n', len);
nldist = nl ? nl + 1 - p : len + 1;
nlknown = 1;
}
s = MIN (len, nldist);
w = fp->_w + fp->_bf._size;
if (fp->_p > fp->_bf._base && s > w)
{
COPY (w);
/* fp->_w -= w; */
fp->_p += w;
if (fflush (fp))
goto err;
}
else if (s >= (w = fp->_bf._size))
{
w = (*fp->_write) (fp->_cookie, p, w);
if (w <= 0)
goto err;
}
else
{
w = s;
COPY (w);
fp->_w -= w;
fp->_p += w;
}
if ((nldist -= w) == 0)
{
/* copied the newline: flush and forget */
if (fflush (fp))
goto err;
nlknown = 0;
}
p += w;
len -= w;
}
while ((uio->uio_resid -= w) != 0);
}
return 0;
err:
fp->_flags |= __SERR;
return EOF;
}