/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
*/

/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

/* $Log:	strftime.c,v $

/* ____________________________________________________________________*
 * This file forms part of the POSIX requirement described in sections *
 * 8.2 of 1003.1-1988 which should be refered to before making any     *
 * changes.                                                	       *
 *_____________________________________________________________________*/

#ident	"$Header: strftime.c 1.6 90/05/22 $@(#)locale/strftime.c	1.9+"

#include	<fcntl.h>
#include	<time.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<locale.h>
#include	<string.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<ctype.h>
#include	"_locale.h"

static int  settime();
static char *itoa();
static char *getzname();
enum {
	   aJan, aFeb, aMar, aApr, aMay, aJun, aJul, aAug, aSep, aOct, aNov, aDec,
	   Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec,
	   aSun, aMon, aTue, aWed, aThu, aFri, aSat,
	   Sun, Mon, Tue, Wed, Thu, Fri, Sat,
	   Local_time, Local_date, DFL_FMT,
	   AM, PM,
	   LAST
};
char * _time[] = {
	"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",
	"January", "February", "March","April",
	"May","June", "July", "August", "September",
	"October", "November", "December",
	"Sun","Mon", "Tue", "Wed","Thu", "Fri","Sat",
	"Sunday","Monday","Tuesday","Wednesday", "Thursday","Friday","Saturday",
	"%H:%M:%S","%m/%d/%y", "%a %b %d %H:%M:%S %Y",
	"AM", "PM", NULL
};

static char * _C_time[] = {
	"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",
	"January", "February", "March","April",
	"May","June", "July", "August", "September",
	"October", "November", "December",
	"Sun","Mon", "Tue", "Wed","Thu", "Fri","Sat",
	"Sunday","Monday","Tuesday","Wednesday", "Thursday","Friday","Saturday",
	"%H:%M:%S","%m/%d/%y", "%a %b %d %H:%M:%S %Y",
	"AM", "PM", NULL
};

#define MAXTZNAME	50
static char	tz0[MAXTZNAME+1] = "GMT";
static char	tz1[MAXTZNAME+1] = "   ";
static char	*dflt_tz[] = { tz0, tz1 };


size_t
strftime(s, maxsize, format, tm)
char *s;
size_t maxsize;
char *format;
struct tm *tm;
{
	register char	*p,  c;
	register int	r;
	int		i, temp;
	size_t		size = 0;
	char		*ptr;
	char		nstr[5];
	size_t		n;
	char		tznm[MAXTZNAME + 1];

	settime();

	/* Set format string, if not already set */
	if (format == NULL)
		format = _time[DFL_FMT];

	/* Build date string by parsing format string */
	while ((c = *format++) != '\0') {
		if (c == '%') {
			switch (*format++) {
			case '%':	/* Percent sign */
				p = "%";
				break;
			case 'a':	/* Abbreviated weekday name */
				p = _time[aSun + tm->tm_wday];
				break;
			case 'A':	/* Weekday name */
				p = _time[Sun + tm->tm_wday];
				break;
			case 'b':	/* Abbreviated month name */
			case 'h':	/* X/Open conformance */
				p = _time[aJan + tm->tm_mon];
				break;
			case 'B':	/* Month name */
				p = _time[Jan + tm->tm_mon];
				break;
			case 'c':	/* Localized date & time format */
				p = _time[DFL_FMT];
				goto recur;
			case 'd':	/* Day number */
				p = itoa(tm->tm_mday, nstr, 2);
				break;
			case 'D':
				p = "%m/%d/%y";
				goto recur;
			case 'e':
				p = itoa(tm->tm_mday, nstr, 2);
				if (tm->tm_mday < 10)
					p[0] = ' ';
				break;
			case 'H':	/* Hour (24 hour version) */
				p = itoa(tm->tm_hour, nstr, 2);
				break;
			case 'I':	/* Hour (12 hour version) */
				temp = tm->tm_hour % 12;
				p = itoa(temp == 0 ? 12 : temp, nstr, 2);
				break;
			case 'j':	/* Julian date */
				p = itoa(tm->tm_yday + 1, nstr, 3);
				break;
			case 'm':	/* Month number */
				p = itoa(tm->tm_mon + 1, nstr, 2);
				break;
			case 'M':	/* Minute */
				p = itoa(tm->tm_min, nstr, 2);
				break;
			case 'n':	/* Newline */
				p = "\n";
				break;
			case 'p':	/* AM or PM */
				if (tm->tm_hour >= 12)
					p = _time[PM];
				else
					p = _time[AM];
				break;
			case 'r':
				p = "%I:%M:%S %p";
				goto recur;
			case 'R':
				p = "%H:%M";
				goto recur;
			case 'S':	/* Seconds */
				p = itoa(tm->tm_sec, nstr, 2);
				break;
			case 't':	/* Tab */
				p = "	";
				break;
			case 'T':
				p = "%H:%M:%S";
				goto recur;
			case 'U':	/* Weekday number, taking Sunday as
					 * the first day of the week */
				p = itoa((tm->tm_yday - tm->tm_wday)/7 + 1, nstr, 2);
				break;
			case 'w':	/* Weekday number */
				p = itoa(tm->tm_wday, nstr, 1);
				break;
			case 'W':	/* Week number of year, taking Monday as
					 * first day of week */
				if ((size += 2) >= maxsize)
					break;
				if (tm->tm_wday == 0)
					temp = tm->tm_yday - 6;
				else
					temp = tm->tm_yday - tm->tm_wday + 1;
				p = itoa(temp/7 + 1, nstr, 2);
				break;
			case 'x':	/* Localized date format */
				p = _time[Local_date];
				goto recur;
			case 'X':	/* Localized time format */
				p = _time[Local_time];
				goto recur;
			case 'y':	/* Year in the form yy */
				p = itoa(tm->tm_year, nstr, 2);
				break;
			case 'Y':	/* Year in the form ccyy */
				p = itoa(1900 + tm->tm_year, nstr, 4);
				break;
			case 'Z':	/* Timezone */
				if ((ptr = (char *)getenv("TZ")) == 0 || *ptr == '\0'
					|| (ptr = getzname(ptr, tznm)) == 0) {
					if (tm->tm_isdst)
						p = dflt_tz[1];
					else
						p = dflt_tz[0];
					break;
				} else if (tm->tm_isdst) {
					while (*ptr != '\0' && !isalpha(*ptr))
						ptr++;
					if ((ptr = getzname(ptr, tznm)) == 0) {
						p = dflt_tz[1];
						break;
					}
				}
				p = tznm;
				break;
			default:
				nstr[0] = c;
				nstr[1] = *(format - 1);
				nstr[2] = '\0';
				p = nstr;
				break;
			recur:;
				if ((n = strftime(s, maxsize-size, p, tm)) == 0)
					return(0);
				s += n;
				size += n;
				continue;
			}
			n = strlen(p);
			if ((size += n) >= maxsize)
				return(0);
			(void)strcpy(s, p);
			s += n;
		} else {
			if (++size >= maxsize)
				return(0);
	 		*s++ = c;
		}
	}
	*s = '\0';
	return(size);
}

static char *
itoa(i, ptr, dig)
register int i;
register char *ptr;
register int dig;
{
	ptr += dig;
	*ptr = '\0';
	while (--dig >= 0) {
		*(--ptr) = i % 10 + '0';
		i /= 10;
	}
	return(ptr);
}

static char saved_locale[LC_NAMELEN] = "C";

static int settime()
{
	register char *p;
	register int  j;
	char *locale;
	char *my_time[LAST];
	static char *ostr = (char *)0;
	char *str;
	int  fd;
	struct stat buf;

	locale = _cur_locale[LC_TIME];

	if (strcmp(locale, saved_locale) == 0)
		return;

	if ( (fd = open(_fullocale(locale, "LC_TIME"), O_RDONLY)) == -1) 
		if (!strcmp(locale, "C")) {	/* Reload default strings. */
			memcpy(_time, _C_time, sizeof(_C_time)); 
			strcpy(saved_locale, locale);
			if (ostr != 0)	 /* free the previous array*/
				free(ostr);
			ostr = 0;
			return;
		}
		else
			goto err1;

	if( (fstat(fd, &buf)) != 0)
		goto err2;

	if ( (str = (char *)malloc(buf.st_size + 2)) == NULL
		|| (read(fd, str, buf.st_size)) != buf.st_size)
		goto err3;

	/* Set last character of str to '\0' */
	p = &str[buf.st_size];
	p[0] = '\n';
	p[1] = '\0';

	/* p will "walk thru" str */
	p = str;  	

	j = -1;
	while (*p != '\0')
	{ 
		/* "Look for a newline, i.e. end of sub-string
		 * and  change it to a '\0'. If LAST pointers
		 * have been set in my_time, but the newline hasn't been seen
		 * yet, keep going thru the string leaving my_time alone.
		 */
		if (++j < LAST) 
			my_time[j] = p;
		p = strchr(p,'\n');
		*p++ = '\0';
	}
	if (j == LAST)
	{
		memcpy(_time, my_time, sizeof(my_time)); 
		strcpy(saved_locale, locale);
		if (ostr != 0)	 /* free the previoulsy allocated local array*/
			free(ostr);
		ostr = str;
		return;
	}

err3:	free(str);
err2:	(void)close(fd);
err1:	strcpy(_cur_locale[LC_TIME], saved_locale);
	return;
}


static char *
getzname(p,tz)
char *p, *tz;
{
  int n = MAXTZNAME;

  if (!isalpha(*p))
	return(0);
  do
  {
	*tz++ = *p ;
  } while (--n > 0 && isalpha(*++p) );
  while(isalpha(*p))
	p++;
  while(--n >= 0)
	*tz++ = '\0';		/* Pad with nulls */
  *tz = '\0';
  return(p);	
}
