Commit | Line | Data |
---|---|---|
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 | 22 | static 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 |
29 | char * |
30 | ctime(t) | |
31 | time_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 |
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]; | |
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 | ||
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 */ | |
4e884c92 RE |
79 | }; |
80 | ||
e747b257 KB |
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]; | |
7f253f66 SL |
89 | }; |
90 | ||
e747b257 KB |
91 | static struct state s; |
92 | ||
93 | static int tz_is_set; | |
94 | ||
95 | char * tzname[2] = { | |
96 | "GMT", | |
97 | "GMT" | |
674afc9d BJ |
98 | }; |
99 | ||
e747b257 KB |
100 | #ifdef USG_COMPAT |
101 | time_t timezone = 0; | |
102 | int daylight = 0; | |
103 | #endif /* USG_COMPAT */ | |
674afc9d | 104 | |
e747b257 KB |
105 | static long |
106 | detzcode(codep) | |
107 | char * 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 | 118 | static |
e747b257 KB |
119 | tzload(name) |
120 | register 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 |
235 | static |
236 | tzsetkernel() | |
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 |
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 | } | |
674afc9d | 269 | |
e747b257 KB |
270 | void |
271 | tzset() | |
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 |
288 | struct tm * |
289 | localtime(timep) | |
290 | time_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 |
326 | struct tm * |
327 | gmtime(clock) | |
328 | time_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 |
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; | |
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 | } |