/*
 * Copyright (c) 1998-2001, 2003 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
 * Copyright (c) 1988, 1993
 *	The Regents of the University of California.  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: arpadate.c,v 1.7 2006/10/06 02:51:35 ca Exp $")
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/time.h"
#include "sm/str.h"

/*
**  ARPADATE -- Create date in RFC 2822 format
**
**	Parameters:
**		when -- time_t
**		udate -- (pointer to) str for RFC2822 date field (output)
**
**	Returns:
**		pointer to an RFC 2822 date field
*/

#define MIN_per_HOUR	60	/* minutes in an hour */
#define MIN_per_DAY	(24 * MIN_per_HOUR) /* minutes in a day */
#define SEC_per_MIN	60	/* seconds in a minute */

sm_ret_T
arpadate(time_t *when, sm_str_P udate)
{
	char *p;
	int off, i;
#if MTA_USE_PTHREADS
	struct tm tm;
#endif
	struct tm gmt;
	struct tm *lt;
	sm_ret_T ret;
	char *ud, ct[26];	/* ctime_r(3) */

#define APPCHAR(ch)							\
	do								\
	{								\
		if ((ret = sm_str_put(udate, (uchar) (ch))) != SM_SUCCESS) \
			return ret;					\
	} while (0)

#define APPCHARP		\
	do			\
	{			\
		APPCHAR(*p);	\
		p++;		\
	} while (0)

	/*
	**  Get current time.
	**	This will be used if a null argument is passed and
	**	to resolve the timezone.
	*/

	SM_REQUIRE(when != NULL);
#if HAVE_CTIME_R
# if SM_CTIME_R_API == 2
	(void) ctime_r(when, ct);
# elif SM_CTIME_R_API == 3
	(void) ctime_r(when, ct, sizeof(ct));
# else
	OOPS: unknown ctime_r() API
# endif
	ud = ct;
#else /* HAVE_CTIME_R */
	OOPS: missing ctime_r()
	write a replacement function in librepl/
#endif /* HAVE_CTIME_R */

	/*
	**  Crack the UNIX date line in a singularly unoriginal way.
	*/

	p = &ud[0];		/* weekday (%a) */
	APPCHARP;
	APPCHARP;
	APPCHARP;
	APPCHAR(',');
	APPCHAR(' ');

	p = &ud[8];		/* day of month (%e) */
	APPCHARP;		/* might be blank */
	APPCHARP;
	APPCHAR(' ');

	p = &ud[4];		/* month (%b) */
	APPCHARP;
	APPCHARP;
	APPCHARP;
	APPCHAR(' ');

	p = &ud[20];		/* year (%G) */
	APPCHARP;
	APPCHARP;
	APPCHARP;
	APPCHARP;
	APPCHAR(' ');

	p = &ud[11];		/* HH:MM:SS */
	for (i = 8; i > 0; i--)
		APPCHARP;

	/*
	**  Should really get the timezone from the time in "ud" (which
	**  is only different if a non-null arg was passed which is different
	**  from the current time), but for all practical purposes, returning
	**  the current local zone will do (it's all that is ever needed).
	*/

	gmt = *gmtime(when);
#if MTA_USE_PTHREADS
	lt = localtime_r(when, &tm);
#else /* MTA_USE_PTHREADS */
	lt = localtime(when);
#endif /* MTA_USE_PTHREADS */

	off = (lt->tm_hour - gmt.tm_hour) * MIN_per_HOUR + lt->tm_min - gmt.tm_min;

	/* assume that offset isn't more than a day ... */
	if (lt->tm_year < gmt.tm_year)
		off -= MIN_per_DAY;
	else if (lt->tm_year > gmt.tm_year)
		off += MIN_per_DAY;
	else if (lt->tm_yday < gmt.tm_yday)
		off -= MIN_per_DAY;
	else if (lt->tm_yday > gmt.tm_yday)
		off += MIN_per_DAY;

	/* seconds can be 0 - 60 now */
	if (lt->tm_sec <= gmt.tm_sec - SEC_per_MIN)
		off -= 1;
	else if (lt->tm_sec >= gmt.tm_sec + SEC_per_MIN)
		off += 1;

	APPCHAR(' ');
	if (off == 0)
	{
		APPCHAR('+');
		APPCHAR('0');
		APPCHAR('0');
		APPCHAR('0');
		APPCHAR('0');
	}
	else
	{
		if (off < 0)
		{
			off = -off;
			APPCHAR('-');
		}
		else
			APPCHAR('+');

		if (off >= 24*60)		/* should be impossible */
			off = 23*60+59;		/* if not, insert silly value */

		APPCHAR((off / 600) + '0');
		APPCHAR((off / 60) % 10 + '0');
		off %= 60;
		APPCHAR((off / 10) + '0');
		APPCHAR((off % 10) + '0');
	}
	/* APPCHAR('\0'); */

	return SM_SUCCESS;
}


syntax highlighted by Code2HTML, v. 0.9.1