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