/* * Copyright (c) 2000-2002, 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: setvbuf.c,v 1.10 2006/07/14 14:42:25 ca Exp $") #include "sm/assert.h" #include "sm/error.h" #include "sm/io.h" #include "sm/fcntl.h" #include "sm/heap.h" #include "io-int.h" /* ** SM_IO_SETVBUF -- set the buffering type for a file ** ** Set one of the different kinds of buffering, optionally including ** a buffer. ** If 'size' is == 0 then an "optimal" size will be selected. ** If 'buf' is == NULL then space will be allocated at 'size'. ** ** Parameters: ** fp -- the file that buffering is to be changed for ** buf -- buffer to use ** mode -- buffering method to use ** size -- size of 'buf' ** ** Returns: ** usual sm_error */ sm_ret_T sm_io_setvbuf(sm_file_T *fp, uchar *buf, int mode, size_t size) { sm_f_flags_T flags; size_t iosize; sm_ret_T res; SM_IS_FP(fp); /* ** Verify arguments. The `int' limit on `size' is due to this ** particular implementation. Note, buf and size are ignored ** when setting SM_IO_NBF. */ if (mode != SM_IO_NBF && (mode != SM_IO_FBF || (int) size < 0)) return sm_error_perm(SM_EM_IO, EINVAL); /* ** Write current buffer, if any. Discard unread input (including ** ungetc data), cancel line buffering, and free old buffer if ** malloc()ed. We also clear any eof condition, as if this were ** a seek. */ res = 0; (void) sm_flush(fp); f_r(*fp) = 0; flags = f_flags(*fp); if (flags & SMMBF) { sm_free((void *) f_bfbase(*fp)); f_bfbase(*fp) = NULL; } flags &= ~(SMNBF | SMMBF | SMOPT | SMNPT | SMFEOF | SMFBF); /* If setting unbuffered mode, skip all the hard work. */ if (mode == SM_IO_NBF) goto nbf; flags |= sm_whatbuf(fp, &iosize); if (size == 0) { buf = NULL; /* force local allocation */ size = iosize; } /* Allocate buffer if needed. */ if (buf == NULL) { buf = (uchar *) sm_malloc(size); if (buf == NULL) { /* ** Unable to honor user's request. We will return ** failure, but try again with file system size. */ res = sm_error_temp(SM_EM_IO, ENOMEM); if (size != iosize) { size = iosize; buf = (uchar *) sm_malloc(size); } } if (buf == NULL) { /* No luck; switch to unbuffered I/O. */ nbf: f_flags(*fp) = flags | SMNBF; f_w(*fp) = 0; f_bfbase(*fp) = f_p(*fp) = fp->f_nbuf; f_bfsize(*fp) = 1; return res; } flags |= SMMBF; } /* ** Kill any seek optimization if the buffer is not the ** right size. ** ** SHOULD WE ALLOW MULTIPLES HERE (i.e., ok iff (size % iosize) == 0)? */ if (size != iosize) flags |= SMNPT; /* ** Fix up the sm_file_T fields, and set sm_cleanup for output flush on ** exit (since we are buffered in some way). */ if (mode == SM_IO_FBF) flags |= SMFBF; f_flags(*fp) = flags; f_bfbase(*fp) = f_p(*fp) = buf; f_bfsize(*fp) = size; if (flags & SMWR) { /* ** Begin or continue writing: see sm_wsetup(). Note ** that SMNBF is impossible (it was handled earlier). */ f_w(*fp) = size; } else { /* begin/continue reading, or stay in intermediate state */ f_w(*fp) = 0; } return res; }