/*
 * Copyright (c) 2000-2001 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.1.1.1 2002/03/12 18:00:20 zarzycki Exp $")
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sm/assert.h>
#include <sm/io.h>
#include <sm/string.h>
#include "local.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
**		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;
	  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_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;
}
