* hashtab.h: Bugfix: use SCM_API (WAS: extern).
[bpt/guile.git] / libguile / stime.c
1 /* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2003, 2004, 2005 Free Software Foundation, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18
19 \f
20
21 /* _POSIX_C_SOURCE is not defined always, because it causes problems on some
22 systems, notably
23
24 - FreeBSD loses all BSD and XOPEN defines.
25 - glibc loses some things like CLK_TCK.
26 - On MINGW it conflicts with the pthread headers.
27
28 But on HP-UX _POSIX_C_SOURCE is needed, as noted, for gmtime_r.
29
30 Perhaps a configure test could figure out what _POSIX_C_SOURCE gives and
31 what it takes away, and decide from that whether to use it, instead of
32 hard coding __hpux. */
33
34 #define _GNU_SOURCE /* ask glibc for everything, in particular strptime */
35 #ifdef __hpux
36 #define _POSIX_C_SOURCE 199506L /* for gmtime_r prototype */
37 #endif
38
39 #if HAVE_CONFIG_H
40 # include <config.h>
41 #endif
42
43 #include <stdio.h>
44 #include <errno.h>
45
46 #include "libguile/_scm.h"
47 #include "libguile/async.h"
48 #include "libguile/feature.h"
49 #include "libguile/strings.h"
50 #include "libguile/vectors.h"
51 #include "libguile/dynwind.h"
52
53 #include "libguile/validate.h"
54 #include "libguile/stime.h"
55
56 #ifdef HAVE_UNISTD_H
57 #include <unistd.h>
58 #endif
59
60 \f
61 # ifdef HAVE_SYS_TYPES_H
62 # include <sys/types.h>
63 # endif
64
65 #ifdef HAVE_STRING_H
66 #include <string.h>
67 #endif
68
69 #ifdef HAVE_SYS_TIMES_H
70 # include <sys/times.h>
71 #endif
72
73 #ifdef HAVE_SYS_TIMEB_H
74 # include <sys/timeb.h>
75 #endif
76
77 #if HAVE_CRT_EXTERNS_H
78 #include <crt_externs.h> /* for Darwin _NSGetEnviron */
79 #endif
80
81 #ifndef tzname /* For SGI. */
82 extern char *tzname[]; /* RS6000 and others reject char **tzname. */
83 #endif
84 #if defined (__MINGW32__)
85 # define tzname _tzname
86 #endif
87
88 #if ! HAVE_DECL_STRPTIME
89 extern char *strptime ();
90 #endif
91
92 #ifdef __STDC__
93 # define timet time_t
94 #else
95 # define timet long
96 #endif
97
98 extern char ** environ;
99
100 /* On Apple Darwin in a shared library there's no "environ" to access
101 directly, instead the address of that variable must be obtained with
102 _NSGetEnviron(). */
103 #if HAVE__NSGETENVIRON && defined (PIC)
104 #define environ (*_NSGetEnviron())
105 #endif
106
107
108 #ifdef HAVE_TIMES
109 static
110 timet mytime()
111 {
112 struct tms time_buffer;
113 times(&time_buffer);
114 return time_buffer.tms_utime + time_buffer.tms_stime;
115 }
116 #else
117 # ifdef LACK_CLOCK
118 # define mytime() ((time((timet*)0) - scm_your_base) * SCM_TIME_UNITS_PER_SECOND)
119 # else
120 # define mytime clock
121 # endif
122 #endif
123
124 #ifdef HAVE_FTIME
125 struct timeb scm_your_base = {0};
126 #else
127 timet scm_your_base = 0;
128 #endif
129
130 SCM_DEFINE (scm_get_internal_real_time, "get-internal-real-time", 0, 0, 0,
131 (),
132 "Return the number of time units since the interpreter was\n"
133 "started.")
134 #define FUNC_NAME s_scm_get_internal_real_time
135 {
136 #ifdef HAVE_FTIME
137 struct timeb time_buffer;
138
139 SCM tmp;
140 ftime (&time_buffer);
141 time_buffer.time -= scm_your_base.time;
142 tmp = scm_from_long (time_buffer.millitm - scm_your_base.millitm);
143 tmp = scm_sum (tmp,
144 scm_product (scm_from_int (1000),
145 scm_from_int (time_buffer.time)));
146 return scm_quotient (scm_product (tmp,
147 scm_from_int (SCM_TIME_UNITS_PER_SECOND)),
148 scm_from_int (1000));
149 #else
150 return scm_from_long ((time((timet*)0) - scm_your_base)
151 * (int)SCM_TIME_UNITS_PER_SECOND);
152 #endif /* HAVE_FTIME */
153 }
154 #undef FUNC_NAME
155
156
157 #ifdef HAVE_TIMES
158 SCM_DEFINE (scm_times, "times", 0, 0, 0,
159 (void),
160 "Return an object with information about real and processor\n"
161 "time. The following procedures accept such an object as an\n"
162 "argument and return a selected component:\n"
163 "\n"
164 "@table @code\n"
165 "@item tms:clock\n"
166 "The current real time, expressed as time units relative to an\n"
167 "arbitrary base.\n"
168 "@item tms:utime\n"
169 "The CPU time units used by the calling process.\n"
170 "@item tms:stime\n"
171 "The CPU time units used by the system on behalf of the calling\n"
172 "process.\n"
173 "@item tms:cutime\n"
174 "The CPU time units used by terminated child processes of the\n"
175 "calling process, whose status has been collected (e.g., using\n"
176 "@code{waitpid}).\n"
177 "@item tms:cstime\n"
178 "Similarly, the CPU times units used by the system on behalf of\n"
179 "terminated child processes.\n"
180 "@end table")
181 #define FUNC_NAME s_scm_times
182 {
183 struct tms t;
184 clock_t rv;
185
186 SCM result = scm_c_make_vector (5, SCM_UNDEFINED);
187 rv = times (&t);
188 if (rv == -1)
189 SCM_SYSERROR;
190 SCM_SIMPLE_VECTOR_SET (result, 0, scm_from_long (rv));
191 SCM_SIMPLE_VECTOR_SET (result, 1, scm_from_long (t.tms_utime));
192 SCM_SIMPLE_VECTOR_SET (result, 2, scm_from_long (t.tms_stime));
193 SCM_SIMPLE_VECTOR_SET (result ,3, scm_from_long (t.tms_cutime));
194 SCM_SIMPLE_VECTOR_SET (result, 4, scm_from_long (t.tms_cstime));
195 return result;
196 }
197 #undef FUNC_NAME
198 #endif /* HAVE_TIMES */
199
200 static long scm_my_base = 0;
201
202 long
203 scm_c_get_internal_run_time ()
204 {
205 return mytime () - scm_my_base;
206 }
207
208 SCM_DEFINE (scm_get_internal_run_time, "get-internal-run-time", 0, 0, 0,
209 (void),
210 "Return the number of time units of processor time used by the\n"
211 "interpreter. Both @emph{system} and @emph{user} time are\n"
212 "included but subprocesses are not.")
213 #define FUNC_NAME s_scm_get_internal_run_time
214 {
215 return scm_from_long (scm_c_get_internal_run_time ());
216 }
217 #undef FUNC_NAME
218
219 /* For reference, note that current-time and gettimeofday both should be
220 protected against setzone/restorezone changes in another thread, since on
221 DOS the system time is normally kept as local time, which means TZ
222 affects the return from current-time and gettimeofday. Not sure if DJGPP
223 etc actually has concurrent multi-threading, but it seems prudent not to
224 make assumptions about this. */
225
226 SCM_DEFINE (scm_current_time, "current-time", 0, 0, 0,
227 (void),
228 "Return the number of seconds since 1970-01-01 00:00:00 UTC,\n"
229 "excluding leap seconds.")
230 #define FUNC_NAME s_scm_current_time
231 {
232 timet timv;
233
234 SCM_CRITICAL_SECTION_START;
235 timv = time (NULL);
236 SCM_CRITICAL_SECTION_END;
237 if (timv == -1)
238 SCM_MISC_ERROR ("current time not available", SCM_EOL);
239 return scm_from_long (timv);
240 }
241 #undef FUNC_NAME
242
243 SCM_DEFINE (scm_gettimeofday, "gettimeofday", 0, 0, 0,
244 (void),
245 "Return a pair containing the number of seconds and microseconds\n"
246 "since 1970-01-01 00:00:00 UTC, excluding leap seconds. Note:\n"
247 "whether true microsecond resolution is available depends on the\n"
248 "operating system.")
249 #define FUNC_NAME s_scm_gettimeofday
250 {
251 #ifdef HAVE_GETTIMEOFDAY
252 struct timeval time;
253 int ret, err;
254
255 SCM_CRITICAL_SECTION_START;
256 ret = gettimeofday (&time, NULL);
257 err = errno;
258 SCM_CRITICAL_SECTION_END;
259 if (ret == -1)
260 {
261 errno = err;
262 SCM_SYSERROR;
263 }
264 return scm_cons (scm_from_long (time.tv_sec),
265 scm_from_long (time.tv_usec));
266 #else
267 # ifdef HAVE_FTIME
268 struct timeb time;
269
270 ftime(&time);
271 return scm_cons (scm_from_long (time.time),
272 scm_from_int (time.millitm * 1000));
273 # else
274 timet timv;
275 int err;
276
277 SCM_CRITICAL_SECTION_START;
278 timv = time (NULL);
279 err = errno;
280 SCM_CRITICAL_SECTION_END;
281 if (timv == -1)
282 {
283 errno = err;
284 SCM_SYSERROR;
285 }
286 return scm_cons (scm_from_long (timv), scm_from_int (0));
287 # endif
288 #endif
289 }
290 #undef FUNC_NAME
291
292 static SCM
293 filltime (struct tm *bd_time, int zoff, const char *zname)
294 {
295 SCM result = scm_c_make_vector (11, SCM_UNDEFINED);
296
297 SCM_SIMPLE_VECTOR_SET (result,0, scm_from_int (bd_time->tm_sec));
298 SCM_SIMPLE_VECTOR_SET (result,1, scm_from_int (bd_time->tm_min));
299 SCM_SIMPLE_VECTOR_SET (result,2, scm_from_int (bd_time->tm_hour));
300 SCM_SIMPLE_VECTOR_SET (result,3, scm_from_int (bd_time->tm_mday));
301 SCM_SIMPLE_VECTOR_SET (result,4, scm_from_int (bd_time->tm_mon));
302 SCM_SIMPLE_VECTOR_SET (result,5, scm_from_int (bd_time->tm_year));
303 SCM_SIMPLE_VECTOR_SET (result,6, scm_from_int (bd_time->tm_wday));
304 SCM_SIMPLE_VECTOR_SET (result,7, scm_from_int (bd_time->tm_yday));
305 SCM_SIMPLE_VECTOR_SET (result,8, scm_from_int (bd_time->tm_isdst));
306 SCM_SIMPLE_VECTOR_SET (result,9, scm_from_int (zoff));
307 SCM_SIMPLE_VECTOR_SET (result,10, (zname
308 ? scm_from_locale_string (zname)
309 : SCM_BOOL_F));
310 return result;
311 }
312
313 static char tzvar[3] = "TZ";
314
315 /* if zone is set, create a temporary environment with only a TZ
316 string. other threads or interrupt handlers shouldn't be allowed
317 to run until the corresponding restorezone is called. hence the use
318 of a static variable for tmpenv is no big deal. */
319 static char **
320 setzone (SCM zone, int pos, const char *subr)
321 {
322 char **oldenv = 0;
323
324 if (!SCM_UNBNDP (zone))
325 {
326 static char *tmpenv[2];
327 char *buf;
328 size_t zone_len;
329
330 zone_len = scm_to_locale_stringbuf (zone, NULL, 0);
331 buf = scm_malloc (zone_len + sizeof (tzvar) + 1);
332 strcpy (buf, tzvar);
333 buf[sizeof(tzvar)-1] = '=';
334 scm_to_locale_stringbuf (zone, buf+sizeof(tzvar), zone_len);
335 buf[sizeof(tzvar)+zone_len] = '\0';
336 oldenv = environ;
337 tmpenv[0] = buf;
338 tmpenv[1] = 0;
339 environ = tmpenv;
340 }
341 return oldenv;
342 }
343
344 static void
345 restorezone (SCM zone, char **oldenv, const char *subr SCM_UNUSED)
346 {
347 if (!SCM_UNBNDP (zone))
348 {
349 free (environ[0]);
350 environ = oldenv;
351 #ifdef HAVE_TZSET
352 /* for the possible benefit of user code linked with libguile. */
353 tzset();
354 #endif
355 }
356 }
357
358 SCM_DEFINE (scm_localtime, "localtime", 1, 1, 0,
359 (SCM time, SCM zone),
360 "Return an object representing the broken down components of\n"
361 "@var{time}, an integer like the one returned by\n"
362 "@code{current-time}. The time zone for the calculation is\n"
363 "optionally specified by @var{zone} (a string), otherwise the\n"
364 "@code{TZ} environment variable or the system default is used.")
365 #define FUNC_NAME s_scm_localtime
366 {
367 timet itime;
368 struct tm *ltptr, lt, *utc;
369 SCM result;
370 int zoff;
371 char *zname = 0;
372 char **oldenv;
373 int err;
374
375 itime = SCM_NUM2LONG (1, time);
376
377 /* deferring interupts is essential since a) setzone may install a temporary
378 environment b) localtime uses a static buffer. */
379 SCM_CRITICAL_SECTION_START;
380 oldenv = setzone (zone, SCM_ARG2, FUNC_NAME);
381 #ifdef LOCALTIME_CACHE
382 tzset ();
383 #endif
384 /* POSIX says localtime sets errno, but C99 doesn't say that.
385 Give a sensible default value in case localtime doesn't set it. */
386 errno = EINVAL;
387 ltptr = localtime (&itime);
388 err = errno;
389 if (ltptr)
390 {
391 const char *ptr;
392
393 /* copy zone name before calling gmtime or restoring zone. */
394 #if defined (HAVE_TM_ZONE)
395 ptr = ltptr->tm_zone;
396 #elif defined (HAVE_TZNAME)
397 ptr = tzname[ (ltptr->tm_isdst == 1) ? 1 : 0 ];
398 #else
399 ptr = "";
400 #endif
401 zname = scm_malloc (strlen (ptr) + 1);
402 strcpy (zname, ptr);
403 }
404 /* the struct is copied in case localtime and gmtime share a buffer. */
405 if (ltptr)
406 lt = *ltptr;
407 /* POSIX says gmtime sets errno, but C99 doesn't say that.
408 Give a sensible default value in case gmtime doesn't set it. */
409 errno = EINVAL;
410 utc = gmtime (&itime);
411 if (utc == NULL)
412 err = errno;
413 restorezone (zone, oldenv, FUNC_NAME);
414 /* delayed until zone has been restored. */
415 errno = err;
416 if (utc == NULL || ltptr == NULL)
417 SCM_SYSERROR;
418
419 /* calculate timezone offset in seconds west of UTC. */
420 zoff = (utc->tm_hour - lt.tm_hour) * 3600 + (utc->tm_min - lt.tm_min) * 60
421 + utc->tm_sec - lt.tm_sec;
422 if (utc->tm_year < lt.tm_year)
423 zoff -= 24 * 60 * 60;
424 else if (utc->tm_year > lt.tm_year)
425 zoff += 24 * 60 * 60;
426 else if (utc->tm_yday < lt.tm_yday)
427 zoff -= 24 * 60 * 60;
428 else if (utc->tm_yday > lt.tm_yday)
429 zoff += 24 * 60 * 60;
430
431 result = filltime (&lt, zoff, zname);
432 SCM_CRITICAL_SECTION_END;
433 if (zname)
434 free (zname);
435 return result;
436 }
437 #undef FUNC_NAME
438
439 /* tm_zone is normally a pointer, not an array within struct tm, so we might
440 have to worry about the lifespan of what it points to. The posix specs
441 don't seem to say anything about this, let's assume here that tm_zone
442 will be a constant and therefore no protection or anything is needed
443 until we copy it in filltime(). */
444
445 SCM_DEFINE (scm_gmtime, "gmtime", 1, 0, 0,
446 (SCM time),
447 "Return an object representing the broken down components of\n"
448 "@var{time}, an integer like the one returned by\n"
449 "@code{current-time}. The values are calculated for UTC.")
450 #define FUNC_NAME s_scm_gmtime
451 {
452 timet itime;
453 struct tm bd_buf, *bd_time;
454 const char *zname;
455
456 itime = SCM_NUM2LONG (1, time);
457
458 /* POSIX says gmtime sets errno, but C99 doesn't say that.
459 Give a sensible default value in case gmtime doesn't set it. */
460 errno = EINVAL;
461
462 #if HAVE_GMTIME_R
463 bd_time = gmtime_r (&itime, &bd_buf);
464 #else
465 SCM_CRITICAL_SECTION_START;
466 bd_time = gmtime (&itime);
467 if (bd_time != NULL)
468 bd_buf = *bd_time;
469 SCM_CRITICAL_SECTION_END;
470 #endif
471 if (bd_time == NULL)
472 SCM_SYSERROR;
473
474 #if HAVE_STRUCT_TM_TM_ZONE
475 zname = bd_buf.tm_zone;
476 #else
477 zname = "GMT";
478 #endif
479 return filltime (&bd_buf, 0, zname);
480 }
481 #undef FUNC_NAME
482
483 /* copy time components from a Scheme object to a struct tm. */
484 static void
485 bdtime2c (SCM sbd_time, struct tm *lt, int pos, const char *subr)
486 {
487 SCM_ASSERT (scm_is_simple_vector (sbd_time)
488 && SCM_SIMPLE_VECTOR_LENGTH (sbd_time) == 11,
489 sbd_time, pos, subr);
490
491 lt->tm_sec = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 0));
492 lt->tm_min = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 1));
493 lt->tm_hour = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 2));
494 lt->tm_mday = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 3));
495 lt->tm_mon = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 4));
496 lt->tm_year = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 5));
497 lt->tm_wday = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 6));
498 lt->tm_yday = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 7));
499 lt->tm_isdst = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 8));
500 #ifdef HAVE_TM_ZONE
501 lt->tm_gmtoff = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 9));
502 if (scm_is_false (SCM_SIMPLE_VECTOR_REF (sbd_time, 10)))
503 lt->tm_zone = NULL;
504 else
505 lt->tm_zone = scm_to_locale_string (SCM_SIMPLE_VECTOR_REF (sbd_time, 10));
506 #endif
507 }
508
509 SCM_DEFINE (scm_mktime, "mktime", 1, 1, 0,
510 (SCM sbd_time, SCM zone),
511 "@var{bd-time} is an object representing broken down time and @code{zone}\n"
512 "is an optional time zone specifier (otherwise the TZ environment variable\n"
513 "or the system default is used).\n\n"
514 "Returns a pair: the car is a corresponding\n"
515 "integer time value like that returned\n"
516 "by @code{current-time}; the cdr is a broken down time object, similar to\n"
517 "as @var{bd-time} but with normalized values.")
518 #define FUNC_NAME s_scm_mktime
519 {
520 timet itime;
521 struct tm lt, *utc;
522 SCM result;
523 int zoff;
524 char *zname = 0;
525 char **oldenv;
526 int err;
527
528 scm_frame_begin (0);
529
530 bdtime2c (sbd_time, &lt, SCM_ARG1, FUNC_NAME);
531 #if HAVE_STRUCT_TM_TM_ZONE
532 scm_frame_free ((char *)lt.tm_zone);
533 #endif
534
535 SCM_CRITICAL_SECTION_START;
536 oldenv = setzone (zone, SCM_ARG2, FUNC_NAME);
537 #ifdef LOCALTIME_CACHE
538 tzset ();
539 #endif
540 itime = mktime (&lt);
541 /* POSIX doesn't say mktime sets errno, and on glibc 2.3.2 for instance it
542 doesn't. Force a sensible value for our error message. */
543 err = EINVAL;
544
545 if (itime != -1)
546 {
547 const char *ptr;
548
549 /* copy zone name before calling gmtime or restoring the zone. */
550 #if defined (HAVE_TM_ZONE)
551 ptr = lt.tm_zone;
552 #elif defined (HAVE_TZNAME)
553 ptr = tzname[ (lt.tm_isdst == 1) ? 1 : 0 ];
554 #else
555 ptr = "";
556 #endif
557 zname = scm_malloc (strlen (ptr) + 1);
558 strcpy (zname, ptr);
559 }
560
561 /* get timezone offset in seconds west of UTC. */
562 /* POSIX says gmtime sets errno, but C99 doesn't say that.
563 Give a sensible default value in case gmtime doesn't set it. */
564 errno = EINVAL;
565 utc = gmtime (&itime);
566 if (utc == NULL)
567 err = errno;
568
569 restorezone (zone, oldenv, FUNC_NAME);
570 /* delayed until zone has been restored. */
571 errno = err;
572 if (utc == NULL || itime == -1)
573 SCM_SYSERROR;
574
575 zoff = (utc->tm_hour - lt.tm_hour) * 3600 + (utc->tm_min - lt.tm_min) * 60
576 + utc->tm_sec - lt.tm_sec;
577 if (utc->tm_year < lt.tm_year)
578 zoff -= 24 * 60 * 60;
579 else if (utc->tm_year > lt.tm_year)
580 zoff += 24 * 60 * 60;
581 else if (utc->tm_yday < lt.tm_yday)
582 zoff -= 24 * 60 * 60;
583 else if (utc->tm_yday > lt.tm_yday)
584 zoff += 24 * 60 * 60;
585
586 result = scm_cons (scm_from_long (itime),
587 filltime (&lt, zoff, zname));
588 SCM_CRITICAL_SECTION_END;
589 if (zname)
590 free (zname);
591
592 scm_frame_end ();
593 return result;
594 }
595 #undef FUNC_NAME
596
597 #ifdef HAVE_TZSET
598 SCM_DEFINE (scm_tzset, "tzset", 0, 0, 0,
599 (void),
600 "Initialize the timezone from the TZ environment variable\n"
601 "or the system default. It's not usually necessary to call this procedure\n"
602 "since it's done automatically by other procedures that depend on the\n"
603 "timezone.")
604 #define FUNC_NAME s_scm_tzset
605 {
606 tzset();
607 return SCM_UNSPECIFIED;
608 }
609 #undef FUNC_NAME
610 #endif /* HAVE_TZSET */
611
612 SCM_DEFINE (scm_strftime, "strftime", 2, 0, 0,
613 (SCM format, SCM stime),
614 "Formats a time specification @var{time} using @var{template}. @var{time}\n"
615 "is an object with time components in the form returned by @code{localtime}\n"
616 "or @code{gmtime}. @var{template} is a string which can include formatting\n"
617 "specifications introduced by a @code{%} character. The formatting of\n"
618 "month and day names is dependent on the current locale. The value returned\n"
619 "is the formatted string.\n"
620 "@xref{Formatting Date and Time, , , libc, The GNU C Library Reference Manual}.)")
621 #define FUNC_NAME s_scm_strftime
622 {
623 struct tm t;
624
625 char *tbuf;
626 int size = 50;
627 const char *fmt;
628 char *myfmt;
629 int len;
630 SCM result;
631
632 SCM_VALIDATE_STRING (1, format);
633 bdtime2c (stime, &t, SCM_ARG2, FUNC_NAME);
634
635 fmt = scm_i_string_chars (format);
636 len = scm_i_string_length (format);
637
638 /* Ugly hack: strftime can return 0 if its buffer is too small,
639 but some valid time strings (e.g. "%p") can sometimes produce
640 a zero-byte output string! Workaround is to prepend a junk
641 character to the format string, so that valid returns are always
642 nonzero. */
643 myfmt = scm_malloc (len+2);
644 *myfmt = 'x';
645 strncpy(myfmt+1, fmt, len);
646 myfmt[len+1] = 0;
647
648 tbuf = scm_malloc (size);
649 {
650 #if !defined (HAVE_TM_ZONE)
651 /* it seems the only way to tell non-GNU versions of strftime what
652 zone to use (for the %Z format) is to set TZ in the
653 environment. interrupts and thread switching must be deferred
654 until TZ is restored. */
655 char **oldenv = NULL;
656 SCM zone_spec = SCM_SIMPLE_VECTOR_REF (stime, 10);
657 int have_zone = 0;
658
659 if (scm_is_true (zone_spec) && scm_c_string_length (zone_spec) > 0)
660 {
661 /* it's not required that the TZ setting be correct, just that
662 it has the right name. so try something like TZ=EST0.
663 using only TZ=EST would be simpler but it doesn't work on
664 some OSs, e.g., Solaris. */
665 SCM zone =
666 scm_string_append (scm_list_2 (zone_spec,
667 scm_from_locale_string ("0")));
668
669 have_zone = 1;
670 SCM_CRITICAL_SECTION_START;
671 oldenv = setzone (zone, SCM_ARG2, FUNC_NAME);
672 }
673 #endif
674
675 #ifdef LOCALTIME_CACHE
676 tzset ();
677 #endif
678
679 /* POSIX says strftime returns 0 on buffer overrun, but old
680 systems (i.e. libc 4 on GNU/Linux) might return `size' in that
681 case. */
682 while ((len = strftime (tbuf, size, myfmt, &t)) == 0 || len == size)
683 {
684 free (tbuf);
685 size *= 2;
686 tbuf = scm_malloc (size);
687 }
688
689 #if !defined (HAVE_TM_ZONE)
690 if (have_zone)
691 {
692 restorezone (zone_spec, oldenv, FUNC_NAME);
693 SCM_CRITICAL_SECTION_END;
694 }
695 #endif
696 }
697
698 result = scm_from_locale_stringn (tbuf + 1, len - 1);
699 free (tbuf);
700 free (myfmt);
701 #if HAVE_STRUCT_TM_TM_ZONE
702 free ((char *) t.tm_zone);
703 #endif
704 return result;
705 }
706 #undef FUNC_NAME
707
708 #ifdef HAVE_STRPTIME
709 SCM_DEFINE (scm_strptime, "strptime", 2, 0, 0,
710 (SCM format, SCM string),
711 "Performs the reverse action to @code{strftime}, parsing\n"
712 "@var{string} according to the specification supplied in\n"
713 "@var{template}. The interpretation of month and day names is\n"
714 "dependent on the current locale. The value returned is a pair.\n"
715 "The car has an object with time components\n"
716 "in the form returned by @code{localtime} or @code{gmtime},\n"
717 "but the time zone components\n"
718 "are not usefully set.\n"
719 "The cdr reports the number of characters from @var{string}\n"
720 "which were used for the conversion.")
721 #define FUNC_NAME s_scm_strptime
722 {
723 struct tm t;
724 const char *fmt, *str, *rest;
725
726 SCM_VALIDATE_STRING (1, format);
727 SCM_VALIDATE_STRING (2, string);
728
729 fmt = scm_i_string_chars (format);
730 str = scm_i_string_chars (string);
731
732 /* initialize the struct tm */
733 #define tm_init(field) t.field = 0
734 tm_init (tm_sec);
735 tm_init (tm_min);
736 tm_init (tm_hour);
737 tm_init (tm_mday);
738 tm_init (tm_mon);
739 tm_init (tm_year);
740 tm_init (tm_wday);
741 tm_init (tm_yday);
742 #undef tm_init
743
744 /* GNU glibc strptime() "%s" is affected by the current timezone, since it
745 reads a UTC time_t value and converts with localtime_r() to set the tm
746 fields, hence the use of SCM_CRITICAL_SECTION_START. */
747 t.tm_isdst = -1;
748 SCM_CRITICAL_SECTION_START;
749 rest = strptime (str, fmt, &t);
750 SCM_CRITICAL_SECTION_END;
751 if (rest == NULL)
752 {
753 /* POSIX doesn't say strptime sets errno, and on glibc 2.3.2 for
754 instance it doesn't. Force a sensible value for our error
755 message. */
756 errno = EINVAL;
757 SCM_SYSERROR;
758 }
759
760 return scm_cons (filltime (&t, 0, NULL),
761 scm_from_signed_integer (rest - str));
762 }
763 #undef FUNC_NAME
764 #endif /* HAVE_STRPTIME */
765
766 void
767 scm_init_stime()
768 {
769 scm_c_define ("internal-time-units-per-second",
770 scm_from_long (SCM_TIME_UNITS_PER_SECOND));
771
772 #ifdef HAVE_FTIME
773 if (!scm_your_base.time) ftime(&scm_your_base);
774 #else
775 if (!scm_your_base) time(&scm_your_base);
776 #endif
777
778 if (!scm_my_base) scm_my_base = mytime();
779
780 scm_add_feature ("current-time");
781 #include "libguile/stime.x"
782 }
783
784
785 /*
786 Local Variables:
787 c-file-style: "gnu"
788 End:
789 */