Commit | Line | Data |
---|---|---|
b8f253e8 | 1 | /* |
5d10bbb4 | 2 | * Copyright (c) 1987 Regents of the University of California. |
b8f253e8 KM |
3 | * All rights reserved. The Berkeley software License Agreement |
4 | * specifies the terms and conditions for redistribution. | |
5 | */ | |
6 | ||
f60731f0 | 7 | #if defined(LIBC_SCCS) && !defined(lint) |
689df48b | 8 | static char sccsid[] = "@(#)ctime.c 5.11 (Berkeley) %G%"; |
f60731f0 | 9 | #endif LIBC_SCCS and not lint |
b8f253e8 | 10 | |
e747b257 | 11 | #include "sys/param.h" |
5d10bbb4 | 12 | #include "sys/time.h" |
e747b257 | 13 | #include "tzfile.h" |
674afc9d | 14 | |
e747b257 KB |
15 | char * |
16 | ctime(t) | |
17 | time_t *t; | |
674afc9d | 18 | { |
e747b257 KB |
19 | struct tm *localtime(); |
20 | char *asctime(); | |
21 | ||
22 | return(asctime(localtime(t))); | |
23 | } | |
674afc9d BJ |
24 | |
25 | /* | |
e747b257 KB |
26 | ** A la X3J11 |
27 | */ | |
bdea7503 | 28 | |
e747b257 KB |
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]; | |
bdea7503 | 41 | |
36ce8be7 | 42 | (void) sprintf(result, "%.3s %.3s%3d %02d:%02d:%02d %d\n", |
e747b257 KB |
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 | } | |
7f253f66 | 50 | |
e747b257 KB |
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 */ | |
4e884c92 RE |
65 | }; |
66 | ||
e747b257 KB |
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]; | |
7f253f66 SL |
75 | }; |
76 | ||
e747b257 KB |
77 | static struct state s; |
78 | ||
79 | static int tz_is_set; | |
80 | ||
81 | char * tzname[2] = { | |
82 | "GMT", | |
83 | "GMT" | |
674afc9d BJ |
84 | }; |
85 | ||
e747b257 KB |
86 | #ifdef USG_COMPAT |
87 | time_t timezone = 0; | |
88 | int daylight = 0; | |
89 | #endif /* USG_COMPAT */ | |
674afc9d | 90 | |
e747b257 KB |
91 | static long |
92 | detzcode(codep) | |
93 | char * codep; | |
674afc9d | 94 | { |
e747b257 KB |
95 | register long result; |
96 | register int i; | |
674afc9d | 97 | |
e747b257 KB |
98 | result = 0; |
99 | for (i = 0; i < 4; ++i) | |
100 | result = (result << 8) | (codep[i] & 0xff); | |
101 | return result; | |
674afc9d BJ |
102 | } |
103 | ||
674afc9d | 104 | static |
e747b257 KB |
105 | tzload(name) |
106 | register char * name; | |
674afc9d | 107 | { |
e747b257 KB |
108 | register int i; |
109 | register int fid; | |
674afc9d | 110 | |
e747b257 KB |
111 | if (name == 0 && (name = TZDEFAULT) == 0) |
112 | return -1; | |
113 | { | |
114 | register char * p; | |
115 | register int doaccess; | |
116 | char fullname[MAXPATHLEN]; | |
674afc9d | 117 | |
e747b257 KB |
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; | |
674afc9d | 139 | } |
e747b257 KB |
140 | { |
141 | register char * p; | |
142 | register struct tzhead * tzhp; | |
143 | char buf[sizeof s]; | |
674afc9d | 144 | |
e747b257 KB |
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; | |
674afc9d | 171 | |
e747b257 KB |
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; | |
674afc9d | 188 | /* |
e747b257 KB |
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 | |
689df48b | 199 | timezone = -s.ttis[0].tt_gmtoff; |
e747b257 KB |
200 | daylight = 0; |
201 | #endif /* USG_COMPAT */ | |
202 | for (i = 1; i < s.typecnt; ++i) { | |
203 | register struct ttinfo * ttisp; | |
674afc9d | 204 | |
e747b257 KB |
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 | |
689df48b | 214 | timezone = -ttisp->tt_gmtoff; |
e747b257 KB |
215 | #endif /* USG_COMPAT */ |
216 | } | |
217 | } | |
218 | return 0; | |
219 | } | |
674afc9d | 220 | |
5d10bbb4 KB |
221 | static |
222 | tzsetkernel() | |
223 | { | |
224 | struct timeval tv; | |
225 | struct timezone tz; | |
2555d7f3 | 226 | char *_tztab(); |
5d10bbb4 KB |
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; | |
2555d7f3 | 233 | (void)strcpy(s.chars, _tztab(tz.tz_minuteswest, 0)); |
5d10bbb4 KB |
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 | ||
e747b257 KB |
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 | } | |
674afc9d | 255 | |
e747b257 KB |
256 | void |
257 | tzset() | |
258 | { | |
259 | register char * name; | |
260 | ||
261 | tz_is_set = TRUE; | |
262 | name = getenv("TZ"); | |
5d10bbb4 KB |
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 */ | |
674afc9d BJ |
272 | } |
273 | ||
e747b257 KB |
274 | struct tm * |
275 | localtime(timep) | |
276 | time_t * timep; | |
674afc9d | 277 | { |
e747b257 KB |
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]; | |
674afc9d | 298 | } |
e747b257 KB |
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; | |
674afc9d BJ |
310 | } |
311 | ||
e747b257 KB |
312 | struct tm * |
313 | gmtime(clock) | |
314 | time_t * clock; | |
674afc9d | 315 | { |
e747b257 KB |
316 | register struct tm * tmp; |
317 | ||
318 | tmp = offtime(clock, 0L); | |
319 | tzname[0] = "GMT"; | |
320 | tmp->tm_zone = "GMT"; /* UCT ? */ | |
321 | return tmp; | |
674afc9d BJ |
322 | } |
323 | ||
e747b257 KB |
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; | |
674afc9d | 337 | { |
e747b257 KB |
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; | |
674afc9d | 389 | } |