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


syntax highlighted by Code2HTML, v. 0.9.1