/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: strftime.c,v 1.2.2.1 92/06/24 14:01:10 marker Exp $"

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

/*	This module is created for MNLS on Mar.09.87		*/


#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 %e %T %Z %Y",
	"AM", "PM", NULL
};

#define MAXTZNAME	3
static char	*dflt_tz[] = {"GMT", "   "};


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)
		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++ = ' ';		/* Pad with blanks */
  *tz = '\0';
  return(p);	
}
