(exception:string-contains-nul): New exception pattern.
[bpt/guile.git] / libguile / stime.c
1 /* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2003, 2004, 2005, 2006 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 #if HAVE_STRUCT_TM_TM_GMTOFF
501 lt->tm_gmtoff = - scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 9));
502 #endif
503 #ifdef HAVE_TM_ZONE
504 if (scm_is_false (SCM_SIMPLE_VECTOR_REF (sbd_time, 10)))
505 lt->tm_zone = NULL;
506 else
507 lt->tm_zone = scm_to_locale_string (SCM_SIMPLE_VECTOR_REF (sbd_time, 10));
508 #endif
509 }
510
511 SCM_DEFINE (scm_mktime, "mktime", 1, 1, 0,
512 (SCM sbd_time, SCM zone),
513 "@var{bd-time} is an object representing broken down time and @code{zone}\n"
514 "is an optional time zone specifier (otherwise the TZ environment variable\n"
515 "or the system default is used).\n\n"
516 "Returns a pair: the car is a corresponding\n"
517 "integer time value like that returned\n"
518 "by @code{current-time}; the cdr is a broken down time object, similar to\n"
519 "as @var{bd-time} but with normalized values.")
520 #define FUNC_NAME s_scm_mktime
521 {
522 timet itime;
523 struct tm lt, *utc;
524 SCM result;
525 int zoff;
526 char *zname = 0;
527 char **oldenv;
528 int err;
529
530 scm_dynwind_begin (0);
531
532 bdtime2c (sbd_time, &lt, SCM_ARG1, FUNC_NAME);
533 #if HAVE_STRUCT_TM_TM_ZONE
534 scm_dynwind_free ((char *)lt.tm_zone);
535 #endif
536
537 scm_dynwind_critical_section (SCM_BOOL_F);
538
539 oldenv = setzone (zone, SCM_ARG2, FUNC_NAME);
540 #ifdef LOCALTIME_CACHE
541 tzset ();
542 #endif
543 itime = mktime (&lt);
544 /* POSIX doesn't say mktime sets errno, and on glibc 2.3.2 for instance it
545 doesn't. Force a sensible value for our error message. */
546 err = EINVAL;
547
548 if (itime != -1)
549 {
550 const char *ptr;
551
552 /* copy zone name before calling gmtime or restoring the zone. */
553 #if defined (HAVE_TM_ZONE)
554 ptr = lt.tm_zone;
555 #elif defined (HAVE_TZNAME)
556 ptr = tzname[ (lt.tm_isdst == 1) ? 1 : 0 ];
557 #else
558 ptr = "";
559 #endif
560 zname = scm_malloc (strlen (ptr) + 1);
561 strcpy (zname, ptr);
562 }
563
564 /* get timezone offset in seconds west of UTC. */
565 /* POSIX says gmtime sets errno, but C99 doesn't say that.
566 Give a sensible default value in case gmtime doesn't set it. */
567 errno = EINVAL;
568 utc = gmtime (&itime);
569 if (utc == NULL)
570 err = errno;
571
572 restorezone (zone, oldenv, FUNC_NAME);
573 /* delayed until zone has been restored. */
574 errno = err;
575 if (utc == NULL || itime == -1)
576 SCM_SYSERROR;
577
578 zoff = (utc->tm_hour - lt.tm_hour) * 3600 + (utc->tm_min - lt.tm_min) * 60
579 + utc->tm_sec - lt.tm_sec;
580 if (utc->tm_year < lt.tm_year)
581 zoff -= 24 * 60 * 60;
582 else if (utc->tm_year > lt.tm_year)
583 zoff += 24 * 60 * 60;
584 else if (utc->tm_yday < lt.tm_yday)
585 zoff -= 24 * 60 * 60;
586 else if (utc->tm_yday > lt.tm_yday)
587 zoff += 24 * 60 * 60;
588
589 result = scm_cons (scm_from_long (itime),
590 filltime (&lt, zoff, zname));
591 if (zname)
592 free (zname);
593
594 scm_dynwind_end ();
595 return result;
596 }
597 #undef FUNC_NAME
598
599 #ifdef HAVE_TZSET
600 SCM_DEFINE (scm_tzset, "tzset", 0, 0, 0,
601 (void),
602 "Initialize the timezone from the TZ environment variable\n"
603 "or the system default. It's not usually necessary to call this procedure\n"
604 "since it's done automatically by other procedures that depend on the\n"
605 "timezone.")
606 #define FUNC_NAME s_scm_tzset
607 {
608 tzset();
609 return SCM_UNSPECIFIED;
610 }
611 #undef FUNC_NAME
612 #endif /* HAVE_TZSET */
613
614 SCM_DEFINE (scm_strftime, "strftime", 2, 0, 0,
615 (SCM format, SCM stime),
616 "Return a string which is broken-down time structure @var{stime}\n"
617 "formatted according to the given @var{format} string.\n"
618 "\n"
619 "@var{format} contains field specifications introduced by a\n"
620 "@samp{%} character. See @ref{Formatting Calendar Time,,, libc,\n"
621 "The GNU C Library Reference Manual}, or @samp{man 3 strftime},\n"
622 "for the available formatting.\n"
623 "\n"
624 "@lisp\n"
625 "(strftime \"%c\" (localtime (current-time)))\n"
626 "@result{} \"Mon Mar 11 20:17:43 2002\"\n"
627 "@end lisp\n"
628 "\n"
629 "If @code{setlocale} has been called (@pxref{Locales}), month\n"
630 "and day names are from the current locale and in the locale\n"
631 "character set.")
632 #define FUNC_NAME s_scm_strftime
633 {
634 struct tm t;
635
636 char *tbuf;
637 int size = 50;
638 const char *fmt;
639 char *myfmt;
640 int len;
641 SCM result;
642
643 SCM_VALIDATE_STRING (1, format);
644 bdtime2c (stime, &t, SCM_ARG2, FUNC_NAME);
645
646 fmt = scm_i_string_chars (format);
647 len = scm_i_string_length (format);
648
649 /* Ugly hack: strftime can return 0 if its buffer is too small,
650 but some valid time strings (e.g. "%p") can sometimes produce
651 a zero-byte output string! Workaround is to prepend a junk
652 character to the format string, so that valid returns are always
653 nonzero. */
654 myfmt = scm_malloc (len+2);
655 *myfmt = 'x';
656 strncpy(myfmt+1, fmt, len);
657 myfmt[len+1] = 0;
658
659 tbuf = scm_malloc (size);
660 {
661 #if !defined (HAVE_TM_ZONE)
662 /* it seems the only way to tell non-GNU versions of strftime what
663 zone to use (for the %Z format) is to set TZ in the
664 environment. interrupts and thread switching must be deferred
665 until TZ is restored. */
666 char **oldenv = NULL;
667 SCM zone_spec = SCM_SIMPLE_VECTOR_REF (stime, 10);
668 int have_zone = 0;
669
670 if (scm_is_true (zone_spec) && scm_c_string_length (zone_spec) > 0)
671 {
672 /* it's not required that the TZ setting be correct, just that
673 it has the right name. so try something like TZ=EST0.
674 using only TZ=EST would be simpler but it doesn't work on
675 some OSs, e.g., Solaris. */
676 SCM zone =
677 scm_string_append (scm_list_2 (zone_spec,
678 scm_from_locale_string ("0")));
679
680 have_zone = 1;
681 SCM_CRITICAL_SECTION_START;
682 oldenv = setzone (zone, SCM_ARG2, FUNC_NAME);
683 }
684 #endif
685
686 #ifdef LOCALTIME_CACHE
687 tzset ();
688 #endif
689
690 /* POSIX says strftime returns 0 on buffer overrun, but old
691 systems (i.e. libc 4 on GNU/Linux) might return `size' in that
692 case. */
693 while ((len = strftime (tbuf, size, myfmt, &t)) == 0 || len == size)
694 {
695 free (tbuf);
696 size *= 2;
697 tbuf = scm_malloc (size);
698 }
699
700 #if !defined (HAVE_TM_ZONE)
701 if (have_zone)
702 {
703 restorezone (zone_spec, oldenv, FUNC_NAME);
704 SCM_CRITICAL_SECTION_END;
705 }
706 #endif
707 }
708
709 result = scm_from_locale_stringn (tbuf + 1, len - 1);
710 free (tbuf);
711 free (myfmt);
712 #if HAVE_STRUCT_TM_TM_ZONE
713 free ((char *) t.tm_zone);
714 #endif
715 return result;
716 }
717 #undef FUNC_NAME
718
719 #ifdef HAVE_STRPTIME
720 SCM_DEFINE (scm_strptime, "strptime", 2, 0, 0,
721 (SCM format, SCM string),
722 "Performs the reverse action to @code{strftime}, parsing\n"
723 "@var{string} according to the specification supplied in\n"
724 "@var{template}. The interpretation of month and day names is\n"
725 "dependent on the current locale. The value returned is a pair.\n"
726 "The car has an object with time components\n"
727 "in the form returned by @code{localtime} or @code{gmtime},\n"
728 "but the time zone components\n"
729 "are not usefully set.\n"
730 "The cdr reports the number of characters from @var{string}\n"
731 "which were used for the conversion.")
732 #define FUNC_NAME s_scm_strptime
733 {
734 struct tm t;
735 const char *fmt, *str, *rest;
736 long zoff;
737
738 SCM_VALIDATE_STRING (1, format);
739 SCM_VALIDATE_STRING (2, string);
740
741 fmt = scm_i_string_chars (format);
742 str = scm_i_string_chars (string);
743
744 /* initialize the struct tm */
745 #define tm_init(field) t.field = 0
746 tm_init (tm_sec);
747 tm_init (tm_min);
748 tm_init (tm_hour);
749 tm_init (tm_mday);
750 tm_init (tm_mon);
751 tm_init (tm_year);
752 tm_init (tm_wday);
753 tm_init (tm_yday);
754 #if HAVE_STRUCT_TM_TM_GMTOFF
755 tm_init (tm_gmtoff);
756 #endif
757 #undef tm_init
758
759 /* GNU glibc strptime() "%s" is affected by the current timezone, since it
760 reads a UTC time_t value and converts with localtime_r() to set the tm
761 fields, hence the use of SCM_CRITICAL_SECTION_START. */
762 t.tm_isdst = -1;
763 SCM_CRITICAL_SECTION_START;
764 rest = strptime (str, fmt, &t);
765 SCM_CRITICAL_SECTION_END;
766 if (rest == NULL)
767 {
768 /* POSIX doesn't say strptime sets errno, and on glibc 2.3.2 for
769 instance it doesn't. Force a sensible value for our error
770 message. */
771 errno = EINVAL;
772 SCM_SYSERROR;
773 }
774
775 /* tm_gmtoff is set by GNU glibc strptime "%s", so capture it when
776 available */
777 #if HAVE_STRUCT_TM_TM_GMTOFF
778 zoff = - t.tm_gmtoff; /* seconds west, not east */
779 #else
780 zoff = 0;
781 #endif
782
783 return scm_cons (filltime (&t, zoff, NULL),
784 scm_from_signed_integer (rest - str));
785 }
786 #undef FUNC_NAME
787 #endif /* HAVE_STRPTIME */
788
789 void
790 scm_init_stime()
791 {
792 scm_c_define ("internal-time-units-per-second",
793 scm_from_long (SCM_TIME_UNITS_PER_SECOND));
794
795 #ifdef HAVE_FTIME
796 if (!scm_your_base.time) ftime(&scm_your_base);
797 #else
798 if (!scm_your_base) time(&scm_your_base);
799 #endif
800
801 if (!scm_my_base) scm_my_base = mytime();
802
803 scm_add_feature ("current-time");
804 #include "libguile/stime.x"
805 }
806
807
808 /*
809 Local Variables:
810 c-file-style: "gnu"
811 End:
812 */