/*
 * Copyright (c) 2002-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_RCSID("@(#)$Id: rcbrcv.c,v 1.21 2005/06/02 19:00:36 ca Exp $")

#include "sm/assert.h"
#include "sm/magic.h"
#include "sm/memops.h"
#include "sm/rpool.h"
#include "sm/limits.h"
#include "sm/io.h"
#include "sm/rcb.h"
#include "sm/str2rcb.h"
#include "sm/str-int.h"
#include "sm/reccom.h"

/*
**  SM_RCB_RCV -- Read data from fd into rcb
**
**	Parameters:
**		fd -- file descriptor to receive data from
**		rcb -- sm_rcb_P object to fill
**		rs -- minimum record size
**		timeout -- timeout
**
**	Returns:
**		= 0: done receiving.
**		> 0: need to receive more data.
**		< 0: usual sm_error code
**
**	Side Effects: fill in rcb (maybe partially on error)
**
**	Last code review: 2005-03-29 05:18:28
**	Last code change:
*/

sm_ret_T
sm_rcb_rcv(rcb_fd_T fd, sm_rcb_P rcb, uint rs
#if SM_RCB_ST
	, st_utime_t timeout
#endif
	)
{
	size_t l;
	ssize_t r;
	uint32_t val, h;

	SM_IS_RCB(rcb);
#if SM_RCB_CHECK
	SM_REQUIRE(rcb->sm_rcb_state == SM_RCB_RCV);
#endif

	/* number of bytes to read: initially rs, otherwise rcb_rw */
#if SM_RCB_ST
  again:
#endif
	l = (rcb->sm_rcb_rw == -1) ? rs : (uint) rcb->sm_rcb_rw;

	/* XXX ?? */
#if SM_RCB_ST
	r = st_read_resid(fd, rcb->sm_rcb_base + rcb->sm_rcb_len, &l, timeout);
#else
	r = read(fd, rcb->sm_rcb_base + rcb->sm_rcb_len, l);
#endif
	if (r == -1)
	{
		if (errno == EINTR)
		{
#if SM_RCB_ST
			rcb->sm_rcb_rw -= l;
			rcb->sm_rcb_len += l;
#endif
			return rcb->sm_rcb_rw;
		}
		return sm_error_perm(SM_EM_RECCOM, errno == 0 ? EIO : errno);
	}
#if SM_RCB_ST
	r = ((rcb->sm_rcb_rw == -1) ? rs : (uint) rcb->sm_rcb_rw) - l;
#endif
	if (r == 0)
	{
		if (errno == 0)
		{
#if SM_RCB_ST
			rcb->sm_rcb_rw -= l;
			rcb->sm_rcb_len += l;
#endif
			return SM_IO_EOF;
		}

		/* got no data? since fd was readable, it's an error */
		return SM_IO_EOF;
	}

	h = rcb->sm_rcb_len;
	rcb->sm_rcb_len += r;

	/* did we already get the initial length? */
	if (h < sizeof(int))
	{
		/* first call: first element contains length */
		if (rcb->sm_rcb_len < sizeof(int))
		{
			/* still not enough */
#if SM_RCB_ST
			goto again;
#else
			return 1;
#endif
		}

		/* extract value */
		sm_buf2uint32(rcb->sm_rcb_base, &val);
		h = val;
		if (val > rcb->sm_rcb_size)
		{
			/* this changes val! sm_rcb_len MUST be correct! */
			SM_STR_INCREASE_R(rcb, val);
		}
		rcb->sm_rcb_rw = h;
		if (h < sizeof(int))
			return sm_error_perm(SM_EM_RECCOM, EINVAL);
	}

	/*
	**  What happens if we got more than we need?
	**  Shouldn't happen due to the rs parameter.
	*/

	if (rcb->sm_rcb_rw < r)
		return sm_error_perm(SM_EM_RECCOM, EINVAL);
	rcb->sm_rcb_rw -= r;

#if SM_RCB_ST
	if (rcb->sm_rcb_rw != 0)
		goto again;
#endif
	return rcb->sm_rcb_rw;
}


syntax highlighted by Code2HTML, v. 0.9.1