/* * Copyright (c) 2000-2002, 2004, 2005 Sendmail, Inc. and its suppliers. * All rights reserved. * * 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_IDSTR(id, "@(#)$Id: smstdio.c,v 1.20 2006/07/16 02:07:40 ca Exp $") #include "sm/stdio.h" #include "sm/fcntl.h" #include "sm/stat.h" #include "sm/error.h" #include "sm/assert.h" #include "sm/io.h" #include "sm/string.h" #include "io-int.h" /* ** Overall: ** This is a file type which implements a layer on top of the system ** stdio. fp->f_cookie is the FILE* of stdio. The cookie may be ** "bound late" because of the manner which Linux implements stdio. ** When binding late (when fp->f_cookie==NULL) then the value of ** fp->f_ival is used (0, 1 or 2) to map to stdio's stdin, stdout or ** stderr. */ /* ** SM_STDIOOPEN -- open a file to system stdio implementation ** ** Parameters: ** fp -- file pointer assign for this open ** info -- info about file to open ** flags -- indicating method of opening ** ** Returns: ** Failure: -1 ** Success: 0 (zero) */ sm_ret_T sm_stdioopen(sm_file_T *fp, const void *info, int flags, va_list ap) { FILE *s; const char *stdiomode; switch (flags) { case SM_IO_RDONLY: stdiomode = "r"; break; case SM_IO_WREXCL: /* XXX Wrong! Maybe return error instead? */ case SM_IO_WRONLY: stdiomode = "w"; break; case SM_IO_APPEND: stdiomode = "a"; break; case SM_IO_APPENDRW: stdiomode = "a+"; break; case SM_IO_RDWR: default: stdiomode = "r+"; break; } errno = 0; if ((s = fopen((const char *)info, stdiomode)) == NULL) return sm_error_perm(SM_EM_IO, errno); fp->f_cookie = s; return SM_SUCCESS; } /* ** SETUP -- assign file type cookie when not already assigned ** ** Parameters: ** fp -- the file pointer to get the cookie assigned ** ** Return: ** none. */ static void setup(sm_file_T *fp) { if (fp->f_cookie == NULL) { switch (fp->f_ival) { case 0: f_cookie(*fp) = stdin; break; case 1: f_cookie(*fp) = stdout; break; case 2: f_cookie(*fp) = stderr; break; default: SM_PANIC(("fp->f_ival=%d: out of range (0...2)", fp->f_ival)); break; } } } /* ** SM_STDIOREAD -- read from the file ** ** Parameters: ** fp -- the file pointer ** buf -- location to place the read data ** n -- number of bytes to read ** bytesread -- number of bytes read (output) ** ** Returns: ** failure: errno as sm_error code ** otherwise: SM_SUCCESS */ sm_ret_T sm_stdioread(sm_file_T *fp, uchar *buf, size_t n, ssize_t *bytesread) { FILE *s; size_t r; SM_REQUIRE(bytesread != NULL); if (f_cookie(*fp) == NULL) setup(fp); s = f_cookie(*fp); /* XXX no timeout! */ errno = 0; r = fread(buf, 1, n, s); *bytesread = r; if (r != n) return sm_error_perm(SM_EM_IO, errno); return SM_SUCCESS; } /* ** SM_STDIOWRITE -- write to the file ** ** Parameters: ** fp -- the file pointer ** buf -- location of data to write ** n -- number of bytes to write ** byteswritten -- number of bytes written (output) ** ** Returns: ** failure: errno as sm_error code ** otherwise: SM_SUCCESS */ sm_ret_T sm_stdiowrite(sm_file_T *fp, const uchar *buf, size_t n, ssize_t *byteswritten) { FILE *s; size_t r; SM_REQUIRE(byteswritten != NULL); if (f_cookie(*fp) == NULL) setup(fp); s = f_cookie(*fp); /* XXX no timeout! */ errno = 0; r = fwrite(buf, 1, n, s); *byteswritten = r; if (r != n) return sm_error_perm(SM_EM_IO, errno); return SM_SUCCESS; } /* ** SM_STDIOSEEK -- set position within file ** ** Parameters: ** fp -- the file pointer ** offset -- new location based on 'whence' ** whence -- indicates "base" for 'offset' ** ** Returns: ** result from fseek(). */ sm_ret_T sm_stdioseek(sm_file_T *fp, off_t offset, int whence) { FILE *s; int r; if (f_cookie(*fp) == NULL) setup(fp); s = f_cookie(*fp); errno = 0; r = fseek(s, offset, whence); if (r != 0) return sm_error_perm(SM_EM_IO, errno); return SM_SUCCESS; } /* ** SM_STDIOCLOSE -- close the file ** ** Parameters: ** fp -- close file pointer ** flags -- ignored ** ** Return: ** status from fclose() */ sm_ret_T sm_stdioclose(sm_file_T *fp, int flags) { FILE *s; int r; if (f_cookie(*fp) == NULL) setup(fp); s = f_cookie(*fp); errno = 0; r = fclose(s); if (r != 0) return sm_error_gen(0, SM_ERR_PERM, errno); return SM_SUCCESS; } /* ** SM_STDIOSETINFO -- set info for this open file ** ** Parameters: ** fp -- the file pointer ** what -- type of information setting ** valp -- memory location of info to set ** ** Return: ** Failure: -1 and sets errno ** Success: none (currently). */ /* ARGSUSED0 */ sm_ret_T sm_stdiosetinfo(sm_file_T *fp, int what, void *valp) { #if 0 switch (what) { case SM_IO_WHAT_MODE: default: } #endif return sm_error_perm(SM_EM_IO, EINVAL); } /* ** SM_STDIOGETINFO -- get info for this open file ** ** Parameters: ** fp -- the file pointer ** what -- type of information request ** valp -- memory location to place info ** ** Return: ** Failure: -1 and sets errno ** Success: none (currently). */ /* ARGSUSED2 */ int sm_stdiogetinfo(sm_file_T *fp, int what, void *valp) { switch (what) { case SM_IO_WHAT_SIZE: { int fd; struct stat st; if (f_cookie(*fp) == NULL) setup(fp); fd = fileno((FILE *) f_cookie(*fp)); if (fd < 0) return sm_error_perm(SM_EM_IO, EINVAL); if (fstat(fd, &st) < 0) return sm_error_perm(SM_EM_IO, errno); return st.st_size; } case SM_IO_WHAT_MODE: default: return sm_error_perm(SM_EM_IO, EINVAL); } } #if 0 /* ** SM_IO_STDIOOPEN -- create an SM_FILE which interfaces to a stdio FILE ** ** Parameters: ** stream -- an open stdio stream, as returned by fopen() ** mode -- the mode argument to fopen() which describes stream ** ** Return: ** On success, return a pointer to an SM_FILE object which ** can be used for reading and writing 'stream'. ** Abort if mode is gibberish or stream is bad. ** Raise an exception if we can't allocate memory. */ sm_ret_T sm_io_stdioopen(FILE *stream, char *mode) { int fd; bool r, w; int ioflags; sm_ret_T res; sm_file_T *fp; fd = fileno(stream); SM_REQUIRE(fd >= 0); r = w = false; switch (mode[0]) { case 'r': r = true; break; case 'w': case 'a': w = true; break; default: sm_abort("sm_io_stdioopen: mode '%s' is bad", mode); } if (strchr(&mode[1], '+') != NULL) r = w = true; if (r && w) ioflags = SMRW; else if (r) ioflags = SMRD; else ioflags = SMWR; res = sm_fp(&(SmFtRealStdio->f_stream), ioflags, NULL, &fp); if (sm_is_err(res)) return res; f_fd(*fp) = fd; f_cookie(*fp) = stream; return SM_SUCCESS; } #endif /* 0 */