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