1 /* 2 * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 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 * By using this file, you agree to the terms and conditions set 11 * forth in the LICENSE file which can be found at the top level of 12 * the sendmail distribution. 13 */ 14 15 #pragma ident "%Z%%M% %I% %E% SMI" 16 17 #include <sm/gen.h> 18 SM_RCSID("@(#)$Id: fvwrite.c,v 1.47 2001/08/27 13:02:20 ca Exp $") 19 #include <stdlib.h> 20 #include <unistd.h> 21 #include <string.h> 22 #include <errno.h> 23 #include <signal.h> 24 #include <fcntl.h> 25 #include <sm/io.h> 26 #include <sm/setjmp.h> 27 #include <sm/conf.h> 28 #include "local.h" 29 #include "fvwrite.h" 30 31 /* 32 ** SM_FVWRITE -- write memory regions and buffer for file pointer 33 ** 34 ** Parameters: 35 ** fp -- the file pointer to write to 36 ** timeout -- time length for function to return by 37 ** uio -- the memory regions to write 38 ** 39 ** Returns: 40 ** Failure: returns SM_IO_EOF and sets errno 41 ** Success: returns 0 (zero) 42 ** 43 ** This routine is large and unsightly, but most of the ugliness due 44 ** to the different kinds of output buffering handled here. 45 */ 46 47 #define COPY(n) (void)memcpy((void *)fp->f_p, (void *)p, (size_t)(n)) 48 #define GETIOV(extra_work) \ 49 while (len == 0) \ 50 { \ 51 extra_work; \ 52 p = iov->iov_base; \ 53 len = iov->iov_len; \ 54 iov++; \ 55 } 56 57 int 58 sm_fvwrite(fp, timeout, uio) 59 register SM_FILE_T *fp; 60 int timeout; 61 register struct sm_uio *uio; 62 { 63 register size_t len; 64 register char *p; 65 register struct sm_iov *iov; 66 register int w, s; 67 char *nl; 68 int nlknown, nldist; 69 int fd; 70 struct timeval to; 71 72 if (uio->uio_resid == 0) 73 return 0; 74 75 /* make sure we can write */ 76 if (cantwrite(fp)) 77 { 78 errno = EBADF; 79 return SM_IO_EOF; 80 } 81 82 SM_CONVERT_TIME(fp, fd, timeout, &to); 83 84 iov = uio->uio_iov; 85 p = iov->iov_base; 86 len = iov->iov_len; 87 iov++; 88 if (fp->f_flags & SMNBF) 89 { 90 /* Unbuffered: write up to BUFSIZ bytes at a time. */ 91 do 92 { 93 GETIOV(;); 94 errno = 0; /* needed to ensure EOF correctly found */ 95 w = (*fp->f_write)(fp, p, SM_MIN(len, SM_IO_BUFSIZ)); 96 if (w <= 0) 97 { 98 if (w == 0 && errno == 0) 99 break; /* EOF found */ 100 if (IS_IO_ERROR(fd, w, timeout)) 101 goto err; /* errno set */ 102 103 /* write would block */ 104 SM_IO_WR_TIMEOUT(fp, fd, timeout); 105 w = 0; 106 } 107 else 108 { 109 p += w; 110 len -= w; 111 } 112 } while ((uio->uio_resid -= w) != 0); 113 } 114 else if ((fp->f_flags & SMLBF) == 0) 115 { 116 /* 117 ** Not SMLBF (line-buffered). Either SMFBF or SMNOW 118 ** buffered: fill partially full buffer, if any, 119 ** and then flush. If there is no partial buffer, write 120 ** one bf._size byte chunk directly (without copying). 121 ** 122 ** String output is a special case: write as many bytes 123 ** as fit, but pretend we wrote everything. This makes 124 ** snprintf() return the number of bytes needed, rather 125 ** than the number used, and avoids its write function 126 ** (so that the write function can be invalid). 127 */ 128 129 do 130 { 131 GETIOV(;); 132 if ((((fp->f_flags & (SMALC | SMSTR)) == (SMALC | SMSTR)) 133 || ((fp->f_flags & SMNOW) != 0)) 134 && (size_t) fp->f_w < len) 135 { 136 size_t blen = fp->f_p - fp->f_bf.smb_base; 137 unsigned char *tbase; 138 int tsize; 139 140 /* Allocate space exponentially. */ 141 tsize = fp->f_bf.smb_size; 142 do 143 { 144 tsize = (tsize << 1) + 1; 145 } while ((size_t) tsize < blen + len); 146 tbase = (unsigned char *) sm_realloc(fp->f_bf.smb_base, 147 tsize + 1); 148 if (tbase == NULL) 149 { 150 errno = ENOMEM; 151 goto err; /* errno set */ 152 } 153 fp->f_w += tsize - fp->f_bf.smb_size; 154 fp->f_bf.smb_base = tbase; 155 fp->f_bf.smb_size = tsize; 156 fp->f_p = tbase + blen; 157 } 158 w = fp->f_w; 159 errno = 0; /* needed to ensure EOF correctly found */ 160 if (fp->f_flags & SMSTR) 161 { 162 if (len < (size_t) w) 163 w = len; 164 COPY(w); /* copy SM_MIN(fp->f_w,len), */ 165 fp->f_w -= w; 166 fp->f_p += w; 167 w = len; /* but pretend copied all */ 168 } 169 else if (fp->f_p > fp->f_bf.smb_base 170 && len > (size_t) w) 171 { 172 /* fill and flush */ 173 COPY(w); 174 fp->f_p += w; 175 if (sm_flush(fp, &timeout)) 176 goto err; /* errno set */ 177 } 178 else if (len >= (size_t) (w = fp->f_bf.smb_size)) 179 { 180 /* write directly */ 181 w = (*fp->f_write)(fp, p, w); 182 if (w <= 0) 183 { 184 if (w == 0 && errno == 0) 185 break; /* EOF found */ 186 if (IS_IO_ERROR(fd, w, timeout)) 187 goto err; /* errno set */ 188 189 /* write would block */ 190 SM_IO_WR_TIMEOUT(fp, fd, timeout); 191 w = 0; 192 } 193 } 194 else 195 { 196 /* fill and done */ 197 w = len; 198 COPY(w); 199 fp->f_w -= w; 200 fp->f_p += w; 201 } 202 p += w; 203 len -= w; 204 } while ((uio->uio_resid -= w) != 0); 205 206 if ((fp->f_flags & SMNOW) != 0 && sm_flush(fp, &timeout)) 207 goto err; /* errno set */ 208 } 209 else 210 { 211 /* 212 ** Line buffered: like fully buffered, but we 213 ** must check for newlines. Compute the distance 214 ** to the first newline (including the newline), 215 ** or `infinity' if there is none, then pretend 216 ** that the amount to write is SM_MIN(len,nldist). 217 */ 218 219 nlknown = 0; 220 nldist = 0; /* XXX just to keep gcc happy */ 221 do 222 { 223 GETIOV(nlknown = 0); 224 if (!nlknown) 225 { 226 nl = memchr((void *)p, '\n', len); 227 nldist = nl != NULL ? nl + 1 - p : len + 1; 228 nlknown = 1; 229 } 230 s = SM_MIN(len, ((size_t) nldist)); 231 w = fp->f_w + fp->f_bf.smb_size; 232 errno = 0; /* needed to ensure EOF correctly found */ 233 if (fp->f_p > fp->f_bf.smb_base && s > w) 234 { 235 COPY(w); 236 /* fp->f_w -= w; */ 237 fp->f_p += w; 238 if (sm_flush(fp, &timeout)) 239 goto err; /* errno set */ 240 } 241 else if (s >= (w = fp->f_bf.smb_size)) 242 { 243 w = (*fp->f_write)(fp, p, w); 244 if (w <= 0) 245 { 246 if (w == 0 && errno == 0) 247 break; /* EOF found */ 248 if (IS_IO_ERROR(fd, w, timeout)) 249 goto err; /* errno set */ 250 251 /* write would block */ 252 SM_IO_WR_TIMEOUT(fp, fd, timeout); 253 w = 0; 254 } 255 } 256 else 257 { 258 w = s; 259 COPY(w); 260 fp->f_w -= w; 261 fp->f_p += w; 262 } 263 if ((nldist -= w) == 0) 264 { 265 /* copied the newline: flush and forget */ 266 if (sm_flush(fp, &timeout)) 267 goto err; /* errno set */ 268 nlknown = 0; 269 } 270 p += w; 271 len -= w; 272 } while ((uio->uio_resid -= w) != 0); 273 } 274 275 return 0; 276 277 err: 278 /* errno set before goto places us here */ 279 fp->f_flags |= SMERR; 280 return SM_IO_EOF; 281 } 282