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