root/usr/src/cmd/sendmail/libsm/smstdio.c
/*
 * Copyright (c) 2000-2002, 2004 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/gen.h>
SM_IDSTR(id, "@(#)$Id: smstdio.c,v 1.34 2004/08/03 20:46:34 ca Exp $")
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sm/assert.h>
#include <sm/io.h>
#include <sm/string.h>
#include "local.h"

static void     setup __P((SM_FILE_T *));

/*
** 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
**              rpool -- ignored
**
**      Returns:
**              Failure: -1
**              Success: 0 (zero)
*/

/* ARGSUSED3 */
int
sm_stdioopen(fp, info, flags, rpool)
        SM_FILE_T *fp;
        const void *info;
        int flags;
        const void *rpool;
{
        register FILE *s;
        char *stdiomode;

        switch (flags)
        {
          case SM_IO_RDONLY:
                stdiomode = "r";
                break;
          case SM_IO_WRONLY:
                stdiomode = "w";
                break;
          case SM_IO_APPEND:
                stdiomode = "a";
                break;
          case SM_IO_APPENDRW:
                stdiomode = "a+";
                break;
#if SM_IO_BINARY != 0
          case SM_IO_RDONLY_B:
                stdiomode = "rb";
                break;
          case SM_IO_WRONLY_B:
                stdiomode = "wb";
                break;
          case SM_IO_APPEND_B:
                stdiomode = "ab";
                break;
          case SM_IO_APPENDRW_B:
                stdiomode = "a+b";
                break;
          case SM_IO_RDWR_B:
                stdiomode = "r+b";
                break;
#endif /* SM_IO_BINARY != 0 */
          case SM_IO_RDWR:
          default:
                stdiomode = "r+";
                break;
        }

        if ((s = fopen((char *)info, stdiomode)) == NULL)
                return -1;
        fp->f_cookie = s;
        return 0;
}

/*
**  SETUP -- assign file type cookie when not already assigned
**
**      Parameters:
**              fp - the file pointer to get the cookie assigned
**
**      Return:
**              none.
*/

static void
setup(fp)
        SM_FILE_T *fp;
{
        if (fp->f_cookie == NULL)
        {
                switch (fp->f_ival)
                {
                  case 0:
                        fp->f_cookie = stdin;
                        break;
                  case 1:
                        fp->f_cookie = stdout;
                        break;
                  case 2:
                        fp->f_cookie = stderr;
                        break;
                  default:
                        sm_abort("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
**
**      Returns:
**              result from fread().
*/

ssize_t
sm_stdioread(fp, buf, n)
        SM_FILE_T *fp;
        char *buf;
        size_t n;
{
        register FILE *s;

        if (fp->f_cookie == NULL)
                setup(fp);
        s = fp->f_cookie;
        return fread(buf, 1, n, s);
}

/*
**  SM_STDIOWRITE -- write to the file
**
**      Parameters:
**              fp -- the file pointer
**              buf -- location of data to write
**              n - number of bytes to write
**
**      Returns:
**              result from fwrite().
*/

ssize_t
sm_stdiowrite(fp, buf, n)
        SM_FILE_T *fp;
        char const *buf;
        size_t n;
{
        register FILE *s;

        if (fp->f_cookie == NULL)
                setup(fp);
        s = fp->f_cookie;
        return fwrite(buf, 1, n, s);
}

/*
**  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().
*/

off_t
sm_stdioseek(fp, offset, whence)
        SM_FILE_T *fp;
        off_t offset;
        int whence;
{
        register FILE *s;

        if (fp->f_cookie == NULL)
                setup(fp);
        s = fp->f_cookie;
        return fseek(s, offset, whence);
}

/*
**  SM_STDIOCLOSE -- close the file
**
**      Parameters:
**              fp -- close file pointer
**
**      Return:
**              status from fclose()
*/

int
sm_stdioclose(fp)
        SM_FILE_T *fp;
{
        register FILE *s;

        if (fp->f_cookie == NULL)
                setup(fp);
        s = fp->f_cookie;
        return fclose(s);
}

/*
**  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).
*/

/* ARGSUSED2 */
int
sm_stdiosetinfo(fp, what, valp)
        SM_FILE_T *fp;
        int what;
        void *valp;
{
        switch (what)
        {
          case SM_IO_WHAT_MODE:
          default:
                errno = EINVAL;
                return -1;
        }
}

/*
**  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(fp, what, valp)
        SM_FILE_T *fp;
        int what;
        void *valp;
{
        switch (what)
        {
          case SM_IO_WHAT_SIZE:
          {
                  int fd;
                  struct stat st;

                  if (fp->f_cookie == NULL)
                          setup(fp);
                  fd = fileno((FILE *) fp->f_cookie);
                  if (fd < 0)
                          return -1;
                  if (fstat(fd, &st) == 0)
                          return st.st_size;
                  else
                          return -1;
          }

          case SM_IO_WHAT_MODE:
          default:
                errno = EINVAL;
                return -1;
        }
}

/*
**  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_FILE_T *
sm_io_stdioopen(stream, mode)
        FILE *stream;
        char *mode;
{
        int fd;
        bool r, w;
        int ioflags;
        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;

        fp = sm_fp(SmFtRealStdio, ioflags, NULL);
        fp->f_file = fd;
        fp->f_cookie = stream;
        return fp;
}