/* * Copyright (c) 2000-2002, 2004, 2005 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: fvwrite.c,v 1.22 2005/03/15 19:56:07 ca Exp $") #include "sm/error.h" #include "sm/time.h" #include "sm/fcntl.h" #include "sm/io.h" #include "sm/setjmp.h" #include "io-int.h" #include "fvwrite.h" /* ** SM_FVWRITE -- write memory regions and buffer for file pointer ** ** Parameters: ** fp -- the file pointer to write to ** uio -- the memory regions to write ** ** Returns: ** usual sm_error and usually modified uio (resid) ** ** This routine is large and unsightly, but most of the ugliness due ** to the different kinds of output buffering handled here. */ /* ** XXX what about error handling? what kind of errors are returned ** by f_write(), esp.: timeout, EAGAIN, EINTR, etc? */ #define COPYF_P(n) (void) sm_memcpy((void *)f_p(*fp), (void *)p, (size_t)(n)) #define GETIOV(extra_work) \ while (len == 0) \ { \ extra_work; \ p = iov->iov_base; \ len = iov->iov_len; \ iov++; \ } sm_ret_T sm_fvwrite(sm_file_T *fp, sm_uio_T *uio) { size_t len; uchar *p; sm_iov_T *iov; int w; ssize_t n; sm_ret_T res; if (uio->uio_resid == 0) return SM_SUCCESS; /* make sure we can write */ if (cantwrite(fp)) return sm_error_perm(SM_EM_IO, EBADF); iov = uio->uio_iov; p = iov->iov_base; len = iov->iov_len; iov++; res = SM_SUCCESS; if (f_flags(*fp) & SMNBF) { /* Unbuffered: write up to BUFSIZ bytes at a time. */ do { GETIOV(;); res = (*f_write(*fp))(fp, p, SM_MIN(len, SM_IO_BUFSIZ), &n); if (sm_is_err(res)) goto err; w = n; p += w; len -= w; } while ((uio->uio_resid -= w) != 0); } else { /* ** SMFBF buffered: fill partially full buffer, if any, ** and then flush. If there is no partial buffer, write ** one f_bf.smb_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). */ do { GETIOV(;); if (((f_flags(*fp) & (SMALC|SMSTR)) == (SMALC|SMSTR)) && (size_t) f_w(*fp) < len) { size_t blen; int tsize; uchar *tbase; /* Allocate space exponentially. */ tsize = f_bfsize(*fp); blen = f_p(*fp) - f_bfbase(*fp); do { tsize = (tsize << 1) + 1; } while ((size_t) tsize < blen + len); tbase = (uchar *) sm_realloc(f_bfbase(*fp), tsize + 1); if (tbase == NULL) { res = sm_error_temp(SM_EM_IO, ENOMEM); goto err; } f_w(*fp) += tsize - f_bfsize(*fp); f_bfbase(*fp) = tbase; f_bfsize(*fp) = tsize; f_p(*fp) = tbase + blen; } w = f_w(*fp); if (f_flags(*fp) & SMSTR) { if (len < (size_t) w) w = len; if (w > 0) { COPYF_P(w); /* copy MIN(w,len) */ f_w(*fp) -= w; f_p(*fp) += w; } w = len; /* but pretend copied all */ } #ifdef SMSTRSTR /* str? */ else if (f_flags(*fp) & SMSTRSTR) { if (len < (size_t) w) w = len; if (w > 0) { res = sm_str_scatn(f_cookie(*fp), (char *) p, w); if (sm_is_err(res)) goto err; f_w(*fp) -= w; f_p(*fp) += w; } w = len; /* but pretend copied all */ } #endif /* SMSTRSTR */ else if (f_p(*fp) > f_bfbase(*fp) && len > (size_t) w) { /* fill and flush */ COPYF_P(w); f_p(*fp) += w; res = sm_flush(fp); if (sm_is_err(res)) goto err; } else if (len >= (size_t) (w = f_bfsize(*fp))) { /* write directly; it doesn't fit in buffer */ res = (*f_write(*fp))(fp, p, w, &n); if (sm_is_err(res)) goto err; w = n; } else { /* fill and done */ w = len; COPYF_P(w); f_w(*fp) -= w; f_p(*fp) += w; } p += w; len -= w; } while ((uio->uio_resid -= w) != 0); } return SM_SUCCESS; err: /* res set before goto places us here */ f_flags(*fp) |= SMERR; #if SM_IO_ERR_VAL f_error(*fp) = res; #endif return res; }