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