xref: /csrg-svn/lib/libc/gen/ctime.c (revision 30684)
1 /*
2  * Copyright (c) 1987 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.8 (Berkeley) 03/28/87";
9 #endif LIBC_SCCS and not lint
10 
11 #include "sys/param.h"
12 #include "sys/time.h"
13 #include "tzfile.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 tzsetkernel()
223 {
224 	struct timeval	tv;
225 	struct timezone	tz;
226 	char	*tztab();
227 
228 	if (gettimeofday(&tv, &tz))
229 		return -1;
230 	s.timecnt = 0;		/* UNIX counts *west* of Greenwich */
231 	s.ttis[0].tt_gmtoff = tz.tz_minuteswest * -SECS_PER_MIN;
232 	s.ttis[0].tt_abbrind = 0;
233 	(void)strcpy(s.chars, tztab(tz.tz_minuteswest, 0));
234 	tzname[0] = tzname[1] = s.chars;
235 #ifdef USG_COMPAT
236 	timezone = tz.tz_minuteswest * 60;
237 	daylight = tz.tz_dsttime;
238 #endif /* USG_COMPAT */
239 	return 0;
240 }
241 
242 static
243 tzsetgmt()
244 {
245 	s.timecnt = 0;
246 	s.ttis[0].tt_gmtoff = 0;
247 	s.ttis[0].tt_abbrind = 0;
248 	(void) strcpy(s.chars, "GMT");
249 	tzname[0] = tzname[1] = s.chars;
250 #ifdef USG_COMPAT
251 	timezone = 0;
252 	daylight = 0;
253 #endif /* USG_COMPAT */
254 }
255 
256 void
257 tzset()
258 {
259 	register char *	name;
260 
261 	tz_is_set = TRUE;
262 	name = getenv("TZ");
263 	if (!name || *name) {			/* did not request GMT */
264 		if (name && !tzload(name))	/* requested name worked */
265 			return;
266 		if (!tzload((char *)0))		/* default name worked */
267 			return;
268 		if (!tzsetkernel())		/* kernel guess worked */
269 			return;
270 	}
271 	tzsetgmt();				/* GMT is default */
272 }
273 
274 struct tm *
275 localtime(timep)
276 time_t *	timep;
277 {
278 	register struct ttinfo *	ttisp;
279 	register struct tm *		tmp;
280 	register int			i;
281 	time_t				t;
282 
283 	if (!tz_is_set)
284 		(void) tzset();
285 	t = *timep;
286 	if (s.timecnt == 0 || t < s.ats[0]) {
287 		i = 0;
288 		while (s.ttis[i].tt_isdst)
289 			if (++i >= s.timecnt) {
290 				i = 0;
291 				break;
292 			}
293 	} else {
294 		for (i = 1; i < s.timecnt; ++i)
295 			if (t < s.ats[i])
296 				break;
297 		i = s.types[i - 1];
298 	}
299 	ttisp = &s.ttis[i];
300 	/*
301 	** To get (wrong) behavior that's compatible with System V Release 2.0
302 	** you'd replace the statement below with
303 	**	tmp = offtime((time_t) (t + ttisp->tt_gmtoff), 0L);
304 	*/
305 	tmp = offtime(&t, ttisp->tt_gmtoff);
306 	tmp->tm_isdst = ttisp->tt_isdst;
307 	tzname[tmp->tm_isdst] = &s.chars[ttisp->tt_abbrind];
308 	tmp->tm_zone = &s.chars[ttisp->tt_abbrind];
309 	return tmp;
310 }
311 
312 struct tm *
313 gmtime(clock)
314 time_t *	clock;
315 {
316 	register struct tm *	tmp;
317 
318 	tmp = offtime(clock, 0L);
319 	tzname[0] = "GMT";
320 	tmp->tm_zone = "GMT";		/* UCT ? */
321 	return tmp;
322 }
323 
324 static int	mon_lengths[2][MONS_PER_YEAR] = {
325 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
326 	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
327 };
328 
329 static int	year_lengths[2] = {
330 	DAYS_PER_NYEAR, DAYS_PER_LYEAR
331 };
332 
333 struct tm *
334 offtime(clock, offset)
335 time_t *	clock;
336 long		offset;
337 {
338 	register struct tm *	tmp;
339 	register long		days;
340 	register long		rem;
341 	register int		y;
342 	register int		yleap;
343 	register int *		ip;
344 	static struct tm	tm;
345 
346 	tmp = &tm;
347 	days = *clock / SECS_PER_DAY;
348 	rem = *clock % SECS_PER_DAY;
349 	rem += offset;
350 	while (rem < 0) {
351 		rem += SECS_PER_DAY;
352 		--days;
353 	}
354 	while (rem >= SECS_PER_DAY) {
355 		rem -= SECS_PER_DAY;
356 		++days;
357 	}
358 	tmp->tm_hour = (int) (rem / SECS_PER_HOUR);
359 	rem = rem % SECS_PER_HOUR;
360 	tmp->tm_min = (int) (rem / SECS_PER_MIN);
361 	tmp->tm_sec = (int) (rem % SECS_PER_MIN);
362 	tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYS_PER_WEEK);
363 	if (tmp->tm_wday < 0)
364 		tmp->tm_wday += DAYS_PER_WEEK;
365 	y = EPOCH_YEAR;
366 	if (days >= 0)
367 		for ( ; ; ) {
368 			yleap = isleap(y);
369 			if (days < (long) year_lengths[yleap])
370 				break;
371 			++y;
372 			days = days - (long) year_lengths[yleap];
373 		}
374 	else do {
375 		--y;
376 		yleap = isleap(y);
377 		days = days + (long) year_lengths[yleap];
378 	} while (days < 0);
379 	tmp->tm_year = y - TM_YEAR_BASE;
380 	tmp->tm_yday = (int) days;
381 	ip = mon_lengths[yleap];
382 	for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
383 		days = days - (long) ip[tmp->tm_mon];
384 	tmp->tm_mday = (int) (days + 1);
385 	tmp->tm_isdst = 0;
386 	tmp->tm_zone = "";
387 	tmp->tm_gmtoff = offset;
388 	return tmp;
389 }
390