1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2002-2003 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 *
26 * Time routines, snagged from libc.
27 */
28
29#pragma ident	"%Z%%M%	%I%	%E% SMI"
30
31#include <errno.h>
32#include <sys/types.h>
33#include <sys/bootvfs.h>
34#include <sys/salib.h>
35#include <sys/promif.h>
36#include <stdio.h>
37#include <time.h>
38
39#define	CBUFSIZ 26
40
41static time_t	start_time, secs_since_boot;
42
43const int	__year_lengths[2] = {
44	DAYS_PER_NYEAR, DAYS_PER_LYEAR
45};
46const int	__mon_lengths[2][MONS_PER_YEAR] = {
47	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
48	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
49};
50
51/*
52 * Initializes our "clock" to the creation date of /timestamp, which is
53 * made on the fly for us by the web server. Thereafter, time() will keep
54 * time sort of up to date.
55 */
56void
57init_boot_time(void)
58{
59	struct stat sb;
60
61	if (start_time == 0) {
62		if (stat("/timestamp", &sb) < 0)
63			prom_panic("init_boot_time: cannot stat /timestamp");
64
65		start_time = sb.st_ctim.tv_sec;
66		secs_since_boot = prom_gettime() / 1000;
67	}
68}
69
70/*
71 * Time is crudely incremented.
72 */
73time_t
74time(time_t *tloc)
75{
76	time_t	time_now;
77
78	time_now = start_time + ((prom_gettime() / 1000) - secs_since_boot);
79
80	if (tloc != NULL)
81		*tloc = time_now;
82
83	if (start_time == 0)
84		return (0);
85	else
86		return (time_now);
87}
88
89struct tm *
90gmtime(const time_t *clock)
91{
92	static struct tm	result;
93	struct tm	*tmp;
94	long		days;
95	int		rem;
96	long		y;
97	long		newy;
98	const int	*ip;
99
100	tmp = &result;
101	days = *clock / SECS_PER_DAY;
102	rem = *clock % SECS_PER_DAY;
103	while (rem < 0) {
104		rem += SECS_PER_DAY;
105		--days;
106	}
107	while (rem >= SECS_PER_DAY) {
108		rem -= SECS_PER_DAY;
109		++days;
110	}
111	tmp->tm_hour = (int)(rem / SECS_PER_HOUR);
112	rem = rem % SECS_PER_HOUR;
113	tmp->tm_min = (int)(rem / SECS_PER_MIN);
114	tmp->tm_sec = (int)(rem % SECS_PER_MIN);
115	tmp->tm_wday = (int)((EPOCH_WDAY + days) % DAYS_PER_WEEK);
116	if (tmp->tm_wday < 0)
117		tmp->tm_wday += DAYS_PER_WEEK;
118	y = EPOCH_YEAR;
119
120#define	LEAPS_THRU_END_OF(y)    ((y) / 4 - (y) / 100 + (y) / 400)
121
122	while (days < 0 || days >= (long)__year_lengths[isleap(y)]) {
123		newy = y + days / DAYS_PER_NYEAR;
124		if (days < 0)
125			--newy;
126		days -= ((long)newy - (long)y) * DAYS_PER_NYEAR +
127			LEAPS_THRU_END_OF(newy > 0 ? newy - 1L : newy) -
128			LEAPS_THRU_END_OF(y > 0 ? y - 1L : y);
129		y = newy;
130	}
131
132	tmp->tm_year = y - TM_YEAR_BASE;
133	tmp->tm_yday = days;
134	ip = __mon_lengths[isleap(y)];
135	for (tmp->tm_mon = 0; days >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
136		days = days - ip[tmp->tm_mon];
137	tmp->tm_mday = (days + 1);
138	tmp->tm_isdst = 0;
139	return (tmp);
140}
141
142/*
143 * The standalone booter runs in GMT.
144 */
145struct tm *
146localtime(const time_t *clock)
147{
148	return (gmtime(clock));
149}
150
151static char *
152ct_numb(char *cp, int n)
153{
154	cp++;
155	if (n >= 10)
156		*cp++ = (n / 10) % 10 + '0';
157	else
158		*cp++ = ' ';		/* Pad with blanks */
159	*cp++ = n % 10 + '0';
160	return (cp);
161}
162
163char *
164asctime(const struct tm *t)
165{
166	char *cp;
167	const char *ncp;
168	const int *tp;
169	const char *Date = "Day Mon 00 00:00:00 1900\n";
170	const char *Day  = "SunMonTueWedThuFriSat";
171	const char *Month = "JanFebMarAprMayJunJulAugSepOctNovDec";
172	static char cbuf[CBUFSIZ];
173
174	cp = cbuf;
175	for (ncp = Date; *cp++ = *ncp++; /* */);
176	ncp = Day + (3 * t->tm_wday);
177	cp = cbuf;
178	*cp++ = *ncp++;
179	*cp++ = *ncp++;
180	*cp++ = *ncp++;
181	cp++;
182	tp = &t->tm_mon;
183	ncp = Month + ((*tp) * 3);
184	*cp++ = *ncp++;
185	*cp++ = *ncp++;
186	*cp++ = *ncp++;
187	cp = ct_numb(cp, *--tp);
188	cp = ct_numb(cp, *--tp + 100);
189	cp = ct_numb(cp, *--tp + 100);
190	--tp;
191	cp = ct_numb(cp, *tp + 100);
192	if (t->tm_year < 100) {
193		/* Common case: "19" already in buffer */
194		cp += 2;
195	} else if (t->tm_year < 8100) {
196		cp = ct_numb(cp, (1900 + t->tm_year) / 100);
197		cp--;
198	} else {
199		/* Only 4-digit years are supported */
200		errno = EOVERFLOW;
201		return (NULL);
202	}
203	(void) ct_numb(cp, t->tm_year + 100);
204	return (cbuf);
205}
206
207char *
208ctime(const time_t *t)
209{
210	return (asctime(localtime(t)));
211}
212