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