add Berkeley specific copyright notices
[unix-history] / usr / src / lib / libc / gen / ctime.c
CommitLineData
b8f253e8 1/*
5d10bbb4 2 * Copyright (c) 1987 Regents of the University of California.
83b522e6
KB
3 * All rights reserved.
4 *
04540e0d
KB
5 * This code is derived from software contributed to Berkeley by
6 * Arthur Olson.
7 *
83b522e6 8 * Redistribution and use in source and binary forms are permitted
9e31603f
KB
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the University of California, Berkeley. The name of the
14 * University may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
b8f253e8
KM
19 */
20
f60731f0 21#if defined(LIBC_SCCS) && !defined(lint)
04540e0d 22static char sccsid[] = "@(#)ctime.c 5.14 (Berkeley) %G%";
83b522e6 23#endif /* LIBC_SCCS and not lint */
b8f253e8 24
e747b257 25#include "sys/param.h"
5d10bbb4 26#include "sys/time.h"
e747b257 27#include "tzfile.h"
674afc9d 28
e747b257
KB
29char *
30ctime(t)
31time_t *t;
674afc9d 32{
e747b257
KB
33 struct tm *localtime();
34 char *asctime();
35
36 return(asctime(localtime(t)));
37}
674afc9d
BJ
38
39/*
e747b257
KB
40** A la X3J11
41*/
bdea7503 42
e747b257
KB
43char *
44asctime(timeptr)
45register 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];
bdea7503 55
36ce8be7 56 (void) sprintf(result, "%.3s %.3s%3d %02d:%02d:%02d %d\n",
e747b257
KB
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}
7f253f66 64
e747b257
KB
65#ifndef TRUE
66#define TRUE 1
67#define FALSE 0
68#endif /* !TRUE */
69
70extern char * getenv();
71extern char * strcpy();
72extern char * strcat();
73struct tm * offtime();
74
75struct 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 */
4e884c92
RE
79};
80
e747b257
KB
81struct 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];
7f253f66
SL
89};
90
e747b257
KB
91static struct state s;
92
93static int tz_is_set;
94
95char * tzname[2] = {
96 "GMT",
97 "GMT"
674afc9d
BJ
98};
99
e747b257
KB
100#ifdef USG_COMPAT
101time_t timezone = 0;
102int daylight = 0;
103#endif /* USG_COMPAT */
674afc9d 104
e747b257
KB
105static long
106detzcode(codep)
107char * codep;
674afc9d 108{
e747b257
KB
109 register long result;
110 register int i;
674afc9d 111
e747b257
KB
112 result = 0;
113 for (i = 0; i < 4; ++i)
114 result = (result << 8) | (codep[i] & 0xff);
115 return result;
674afc9d
BJ
116}
117
674afc9d 118static
e747b257
KB
119tzload(name)
120register char * name;
674afc9d 121{
e747b257
KB
122 register int i;
123 register int fid;
674afc9d 124
e747b257
KB
125 if (name == 0 && (name = TZDEFAULT) == 0)
126 return -1;
127 {
128 register char * p;
129 register int doaccess;
130 char fullname[MAXPATHLEN];
674afc9d 131
e747b257
KB
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;
674afc9d 153 }
e747b257
KB
154 {
155 register char * p;
156 register struct tzhead * tzhp;
157 char buf[sizeof s];
674afc9d 158
e747b257
KB
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;
674afc9d 185
e747b257
KB
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;
674afc9d 202 /*
e747b257
KB
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
689df48b 213 timezone = -s.ttis[0].tt_gmtoff;
e747b257
KB
214 daylight = 0;
215#endif /* USG_COMPAT */
216 for (i = 1; i < s.typecnt; ++i) {
217 register struct ttinfo * ttisp;
674afc9d 218
e747b257
KB
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
689df48b 228 timezone = -ttisp->tt_gmtoff;
e747b257
KB
229#endif /* USG_COMPAT */
230 }
231 }
232 return 0;
233}
674afc9d 234
5d10bbb4
KB
235static
236tzsetkernel()
237{
238 struct timeval tv;
239 struct timezone tz;
2555d7f3 240 char *_tztab();
5d10bbb4
KB
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;
2555d7f3 247 (void)strcpy(s.chars, _tztab(tz.tz_minuteswest, 0));
5d10bbb4
KB
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
e747b257
KB
256static
257tzsetgmt()
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}
674afc9d 269
e747b257
KB
270void
271tzset()
272{
273 register char * name;
274
275 tz_is_set = TRUE;
276 name = getenv("TZ");
5d10bbb4
KB
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 */
674afc9d
BJ
286}
287
e747b257
KB
288struct tm *
289localtime(timep)
290time_t * timep;
674afc9d 291{
e747b257
KB
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];
674afc9d 312 }
e747b257
KB
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;
674afc9d
BJ
324}
325
e747b257
KB
326struct tm *
327gmtime(clock)
328time_t * clock;
674afc9d 329{
e747b257
KB
330 register struct tm * tmp;
331
332 tmp = offtime(clock, 0L);
333 tzname[0] = "GMT";
334 tmp->tm_zone = "GMT"; /* UCT ? */
335 return tmp;
674afc9d
BJ
336}
337
e747b257
KB
338static 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
343static int year_lengths[2] = {
344 DAYS_PER_NYEAR, DAYS_PER_LYEAR
345};
346
347struct tm *
348offtime(clock, offset)
349time_t * clock;
350long offset;
674afc9d 351{
e747b257
KB
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;
674afc9d 403}