xref: /csrg-svn/lib/libc/gen/ctime.c (revision 30608)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #if defined(LIBC_SCCS) && !defined(lint)
8 static char sccsid[] = "@(#)ctime.c	5.6 (Berkeley) 03/11/87";
9 #endif LIBC_SCCS and not lint
10 
11 #include "sys/param.h"
12 #include "tzfile.h"
13 #include "time.h"
14 
15 char *
16 ctime(t)
17 time_t *t;
18 {
19 	struct tm	*localtime();
20 	char	*asctime();
21 
22 	return(asctime(localtime(t)));
23 }
24 
25 /*
26 ** A la X3J11
27 */
28 
29 char *
30 asctime(timeptr)
31 register struct tm *	timeptr;
32 {
33 	static char	wday_name[DAYS_PER_WEEK][3] = {
34 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
35 	};
36 	static char	mon_name[MONS_PER_YEAR][3] = {
37 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
38 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
39 	};
40 	static char	result[26];
41 
42 	(void) sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
43 		wday_name[timeptr->tm_wday],
44 		mon_name[timeptr->tm_mon],
45 		timeptr->tm_mday, timeptr->tm_hour,
46 		timeptr->tm_min, timeptr->tm_sec,
47 		TM_YEAR_BASE + timeptr->tm_year);
48 	return result;
49 }
50 
51 #ifndef TRUE
52 #define TRUE		1
53 #define FALSE		0
54 #endif /* !TRUE */
55 
56 extern char *		getenv();
57 extern char *		strcpy();
58 extern char *		strcat();
59 struct tm *		offtime();
60 
61 struct ttinfo {				/* time type information */
62 	long		tt_gmtoff;	/* GMT offset in seconds */
63 	int		tt_isdst;	/* used to set tm_isdst */
64 	int		tt_abbrind;	/* abbreviation list index */
65 };
66 
67 struct state {
68 	int		timecnt;
69 	int		typecnt;
70 	int		charcnt;
71 	time_t		ats[TZ_MAX_TIMES];
72 	unsigned char	types[TZ_MAX_TIMES];
73 	struct ttinfo	ttis[TZ_MAX_TYPES];
74 	char		chars[TZ_MAX_CHARS + 1];
75 };
76 
77 static struct state	s;
78 
79 static int		tz_is_set;
80 
81 char *			tzname[2] = {
82 	"GMT",
83 	"GMT"
84 };
85 
86 #ifdef USG_COMPAT
87 time_t			timezone = 0;
88 int			daylight = 0;
89 #endif /* USG_COMPAT */
90 
91 static long
92 detzcode(codep)
93 char *	codep;
94 {
95 	register long	result;
96 	register int	i;
97 
98 	result = 0;
99 	for (i = 0; i < 4; ++i)
100 		result = (result << 8) | (codep[i] & 0xff);
101 	return result;
102 }
103 
104 static
105 tzload(name)
106 register char *	name;
107 {
108 	register int	i;
109 	register int	fid;
110 
111 	if (name == 0 && (name = TZDEFAULT) == 0)
112 		return -1;
113 	{
114 		register char *	p;
115 		register int	doaccess;
116 		char		fullname[MAXPATHLEN];
117 
118 		doaccess = name[0] == '/';
119 		if (!doaccess) {
120 			if ((p = TZDIR) == 0)
121 				return -1;
122 			if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
123 				return -1;
124 			(void) strcpy(fullname, p);
125 			(void) strcat(fullname, "/");
126 			(void) strcat(fullname, name);
127 			/*
128 			** Set doaccess if '.' (as in "../") shows up in name.
129 			*/
130 			while (*name != '\0')
131 				if (*name++ == '.')
132 					doaccess = TRUE;
133 			name = fullname;
134 		}
135 		if (doaccess && access(name, 4) != 0)
136 			return -1;
137 		if ((fid = open(name, 0)) == -1)
138 			return -1;
139 	}
140 	{
141 		register char *			p;
142 		register struct tzhead *	tzhp;
143 		char				buf[sizeof s];
144 
145 		i = read(fid, buf, sizeof buf);
146 		if (close(fid) != 0 || i < sizeof *tzhp)
147 			return -1;
148 		tzhp = (struct tzhead *) buf;
149 		s.timecnt = (int) detzcode(tzhp->tzh_timecnt);
150 		s.typecnt = (int) detzcode(tzhp->tzh_typecnt);
151 		s.charcnt = (int) detzcode(tzhp->tzh_charcnt);
152 		if (s.timecnt > TZ_MAX_TIMES ||
153 			s.typecnt == 0 ||
154 			s.typecnt > TZ_MAX_TYPES ||
155 			s.charcnt > TZ_MAX_CHARS)
156 				return -1;
157 		if (i < sizeof *tzhp +
158 			s.timecnt * (4 + sizeof (char)) +
159 			s.typecnt * (4 + 2 * sizeof (char)) +
160 			s.charcnt * sizeof (char))
161 				return -1;
162 		p = buf + sizeof *tzhp;
163 		for (i = 0; i < s.timecnt; ++i) {
164 			s.ats[i] = detzcode(p);
165 			p += 4;
166 		}
167 		for (i = 0; i < s.timecnt; ++i)
168 			s.types[i] = (unsigned char) *p++;
169 		for (i = 0; i < s.typecnt; ++i) {
170 			register struct ttinfo *	ttisp;
171 
172 			ttisp = &s.ttis[i];
173 			ttisp->tt_gmtoff = detzcode(p);
174 			p += 4;
175 			ttisp->tt_isdst = (unsigned char) *p++;
176 			ttisp->tt_abbrind = (unsigned char) *p++;
177 		}
178 		for (i = 0; i < s.charcnt; ++i)
179 			s.chars[i] = *p++;
180 		s.chars[i] = '\0';	/* ensure '\0' at end */
181 	}
182 	/*
183 	** Check that all the local time type indices are valid.
184 	*/
185 	for (i = 0; i < s.timecnt; ++i)
186 		if (s.types[i] >= s.typecnt)
187 			return -1;
188 	/*
189 	** Check that all abbreviation indices are valid.
190 	*/
191 	for (i = 0; i < s.typecnt; ++i)
192 		if (s.ttis[i].tt_abbrind >= s.charcnt)
193 			return -1;
194 	/*
195 	** Set tzname elements to initial values.
196 	*/
197 	tzname[0] = tzname[1] = &s.chars[0];
198 #ifdef USG_COMPAT
199 	timezone = s.ttis[0].tt_gmtoff;
200 	daylight = 0;
201 #endif /* USG_COMPAT */
202 	for (i = 1; i < s.typecnt; ++i) {
203 		register struct ttinfo *	ttisp;
204 
205 		ttisp = &s.ttis[i];
206 		if (ttisp->tt_isdst) {
207 			tzname[1] = &s.chars[ttisp->tt_abbrind];
208 #ifdef USG_COMPAT
209 			daylight = 1;
210 #endif /* USG_COMPAT */
211 		} else {
212 			tzname[0] = &s.chars[ttisp->tt_abbrind];
213 #ifdef USG_COMPAT
214 			timezone = ttisp->tt_gmtoff;
215 #endif /* USG_COMPAT */
216 		}
217 	}
218 	return 0;
219 }
220 
221 static
222 tzsetgmt()
223 {
224 	s.timecnt = 0;
225 	s.ttis[0].tt_gmtoff = 0;
226 	s.ttis[0].tt_abbrind = 0;
227 	(void) strcpy(s.chars, "GMT");
228 	tzname[0] = tzname[1] = s.chars;
229 #ifdef USG_COMPAT
230 	timezone = 0;
231 	daylight = 0;
232 #endif /* USG_COMPAT */
233 }
234 
235 void
236 tzset()
237 {
238 	register char *	name;
239 
240 	tz_is_set = TRUE;
241 	name = getenv("TZ");
242 	if (name != 0 && *name == '\0')
243 		tzsetgmt();		/* GMT by request */
244 	else if (tzload(name) != 0)
245 		tzsetgmt();
246 }
247 
248 struct tm *
249 localtime(timep)
250 time_t *	timep;
251 {
252 	register struct ttinfo *	ttisp;
253 	register struct tm *		tmp;
254 	register int			i;
255 	time_t				t;
256 
257 	if (!tz_is_set)
258 		(void) tzset();
259 	t = *timep;
260 	if (s.timecnt == 0 || t < s.ats[0]) {
261 		i = 0;
262 		while (s.ttis[i].tt_isdst)
263 			if (++i >= s.timecnt) {
264 				i = 0;
265 				break;
266 			}
267 	} else {
268 		for (i = 1; i < s.timecnt; ++i)
269 			if (t < s.ats[i])
270 				break;
271 		i = s.types[i - 1];
272 	}
273 	ttisp = &s.ttis[i];
274 	/*
275 	** To get (wrong) behavior that's compatible with System V Release 2.0
276 	** you'd replace the statement below with
277 	**	tmp = offtime((time_t) (t + ttisp->tt_gmtoff), 0L);
278 	*/
279 	tmp = offtime(&t, ttisp->tt_gmtoff);
280 	tmp->tm_isdst = ttisp->tt_isdst;
281 	tzname[tmp->tm_isdst] = &s.chars[ttisp->tt_abbrind];
282 	tmp->tm_zone = &s.chars[ttisp->tt_abbrind];
283 	return tmp;
284 }
285 
286 struct tm *
287 gmtime(clock)
288 time_t *	clock;
289 {
290 	register struct tm *	tmp;
291 
292 	tmp = offtime(clock, 0L);
293 	tzname[0] = "GMT";
294 	tmp->tm_zone = "GMT";		/* UCT ? */
295 	return tmp;
296 }
297 
298 static int	mon_lengths[2][MONS_PER_YEAR] = {
299 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
300 	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
301 };
302 
303 static int	year_lengths[2] = {
304 	DAYS_PER_NYEAR, DAYS_PER_LYEAR
305 };
306 
307 struct tm *
308 offtime(clock, offset)
309 time_t *	clock;
310 long		offset;
311 {
312 	register struct tm *	tmp;
313 	register long		days;
314 	register long		rem;
315 	register int		y;
316 	register int		yleap;
317 	register int *		ip;
318 	static struct tm	tm;
319 
320 	tmp = &tm;
321 	days = *clock / SECS_PER_DAY;
322 	rem = *clock % SECS_PER_DAY;
323 	rem += offset;
324 	while (rem < 0) {
325 		rem += SECS_PER_DAY;
326 		--days;
327 	}
328 	while (rem >= SECS_PER_DAY) {
329 		rem -= SECS_PER_DAY;
330 		++days;
331 	}
332 	tmp->tm_hour = (int) (rem / SECS_PER_HOUR);
333 	rem = rem % SECS_PER_HOUR;
334 	tmp->tm_min = (int) (rem / SECS_PER_MIN);
335 	tmp->tm_sec = (int) (rem % SECS_PER_MIN);
336 	tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYS_PER_WEEK);
337 	if (tmp->tm_wday < 0)
338 		tmp->tm_wday += DAYS_PER_WEEK;
339 	y = EPOCH_YEAR;
340 	if (days >= 0)
341 		for ( ; ; ) {
342 			yleap = isleap(y);
343 			if (days < (long) year_lengths[yleap])
344 				break;
345 			++y;
346 			days = days - (long) year_lengths[yleap];
347 		}
348 	else do {
349 		--y;
350 		yleap = isleap(y);
351 		days = days + (long) year_lengths[yleap];
352 	} while (days < 0);
353 	tmp->tm_year = y - TM_YEAR_BASE;
354 	tmp->tm_yday = (int) days;
355 	ip = mon_lengths[yleap];
356 	for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
357 		days = days - (long) ip[tmp->tm_mon];
358 	tmp->tm_mday = (int) (days + 1);
359 	tmp->tm_isdst = 0;
360 	tmp->tm_zone = "";
361 	tmp->tm_gmtoff = offset;
362 	return tmp;
363 }
364