(Symbol Primitives): In gensym, cross reference
[bpt/guile.git] / libguile / stime.c
CommitLineData
756414cf 1/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2003 Free Software Foundation, Inc.
1c299a6b 2 *
73be1d9e
MV
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.
1c299a6b 7 *
73be1d9e 8 * This library is distributed in the hope that it will be useful,
0f2d19dd 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
73be1d9e
MV
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
1c299a6b 12 *
73be1d9e
MV
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 */
1bbd0b84 17
1bbd0b84 18
0f2d19dd
JB
19\f
20
3cf2f52d
RB
21#if HAVE_CONFIG_H
22# include <config.h>
23#endif
24
0f2d19dd 25#include <stdio.h>
e6e2e95a
MD
26#include <errno.h>
27
a0599745
MD
28#include "libguile/_scm.h"
29#include "libguile/feature.h"
30#include "libguile/strings.h"
31#include "libguile/vectors.h"
20e6290e 32
a0599745
MD
33#include "libguile/validate.h"
34#include "libguile/stime.h"
20e6290e 35
0f2d19dd
JB
36#ifdef HAVE_UNISTD_H
37#include <unistd.h>
38#endif
39
40\f
41# ifdef HAVE_SYS_TYPES_H
42# include <sys/types.h>
43# endif
44
a15e6dcc
MV
45#ifdef HAVE_STRING_H
46#include <string.h>
47#endif
48
b1978258
GH
49#ifdef HAVE_SYS_TIMES_H
50# include <sys/times.h>
51#endif
52
53#ifdef HAVE_SYS_TIMEB_H
54# include <sys/timeb.h>
55#endif
0f2d19dd 56
b9525b92
GH
57#ifndef tzname /* For SGI. */
58extern char *tzname[]; /* RS6000 and others reject char **tzname. */
59#endif
79dcdf51
MV
60#if defined (__MINGW32__)
61# define tzname _tzname
62#endif
b9525b92 63
4d3bacdd
JB
64#ifdef MISSING_STRPTIME_DECL
65extern char *strptime ();
66#endif
67
0f2d19dd
JB
68#ifdef __STDC__
69# define timet time_t
70#else
71# define timet long
72#endif
73
74#ifdef HAVE_TIMES
0f2d19dd 75static
1c299a6b 76timet mytime()
0f2d19dd
JB
77{
78 struct tms time_buffer;
79 times(&time_buffer);
80 return time_buffer.tms_utime + time_buffer.tms_stime;
81}
82#else
83# ifdef LACK_CLOCK
756414cf 84# define mytime() ((time((timet*)0) - scm_your_base) * SCM_TIME_UNITS_PER_SECOND)
0f2d19dd
JB
85# else
86# define mytime clock
87# endif
88#endif
89
0f2d19dd 90#ifdef HAVE_FTIME
0f2d19dd 91struct timeb scm_your_base = {0};
b450f070
GB
92#else
93timet scm_your_base = 0;
94#endif
1bbd0b84 95
1c299a6b 96SCM_DEFINE (scm_get_internal_real_time, "get-internal-real-time", 0, 0, 0,
1bbd0b84 97 (),
1e6808ea
MG
98 "Return the number of time units since the interpreter was\n"
99 "started.")
1bbd0b84 100#define FUNC_NAME s_scm_get_internal_real_time
0f2d19dd 101{
b450f070 102#ifdef HAVE_FTIME
0f2d19dd 103 struct timeb time_buffer;
55c4d089
JB
104
105 SCM tmp;
106 ftime (&time_buffer);
0f2d19dd 107 time_buffer.time -= scm_your_base.time;
55c4d089
JB
108 tmp = scm_long2num (time_buffer.millitm - scm_your_base.millitm);
109 tmp = scm_sum (tmp,
110 scm_product (SCM_MAKINUM (1000),
111 SCM_MAKINUM (time_buffer.time)));
756414cf 112 return scm_quotient (scm_product (tmp, SCM_MAKINUM (SCM_TIME_UNITS_PER_SECOND)),
55c4d089 113 SCM_MAKINUM (1000));
0f2d19dd 114#else
756414cf 115 return scm_long2num((time((timet*)0) - scm_your_base) * (int)SCM_TIME_UNITS_PER_SECOND);
b450f070 116#endif /* HAVE_FTIME */
0f2d19dd 117}
1bbd0b84
GB
118#undef FUNC_NAME
119
0f2d19dd 120
f25f761d 121#ifdef HAVE_TIMES
1c299a6b 122SCM_DEFINE (scm_times, "times", 0, 0, 0,
1bbd0b84 123 (void),
1e6808ea
MG
124 "Return an object with information about real and processor\n"
125 "time. The following procedures accept such an object as an\n"
126 "argument and return a selected component:\n"
127 "\n"
b380b885
MD
128 "@table @code\n"
129 "@item tms:clock\n"
130 "The current real time, expressed as time units relative to an\n"
131 "arbitrary base.\n"
132 "@item tms:utime\n"
133 "The CPU time units used by the calling process.\n"
134 "@item tms:stime\n"
1e6808ea
MG
135 "The CPU time units used by the system on behalf of the calling\n"
136 "process.\n"
b380b885 137 "@item tms:cutime\n"
1e6808ea
MG
138 "The CPU time units used by terminated child processes of the\n"
139 "calling process, whose status has been collected (e.g., using\n"
140 "@code{waitpid}).\n"
b380b885 141 "@item tms:cstime\n"
1e6808ea 142 "Similarly, the CPU times units used by the system on behalf of\n"
b380b885
MD
143 "terminated child processes.\n"
144 "@end table")
1bbd0b84 145#define FUNC_NAME s_scm_times
6afcd3b2 146{
6afcd3b2
GH
147 struct tms t;
148 clock_t rv;
149
00ffa0e7 150 SCM result = scm_c_make_vector (5, SCM_UNDEFINED);
6afcd3b2
GH
151 rv = times (&t);
152 if (rv == -1)
1bbd0b84 153 SCM_SYSERROR;
34d19ef6
HWN
154 SCM_VECTOR_SET (result, 0, scm_long2num (rv));
155 SCM_VECTOR_SET (result, 1, scm_long2num (t.tms_utime));
156 SCM_VECTOR_SET (result, 2, scm_long2num (t.tms_stime));
157 SCM_VECTOR_SET (result ,3, scm_long2num (t.tms_cutime));
158 SCM_VECTOR_SET (result, 4, scm_long2num (t.tms_cstime));
6afcd3b2 159 return result;
6afcd3b2 160}
1bbd0b84 161#undef FUNC_NAME
f25f761d 162#endif /* HAVE_TIMES */
6afcd3b2 163
0f2d19dd
JB
164static long scm_my_base = 0;
165
1c299a6b
ML
166long
167scm_c_get_internal_run_time ()
168{
169 return mytime () - scm_my_base;
170}
171
172SCM_DEFINE (scm_get_internal_run_time, "get-internal-run-time", 0, 0, 0,
1bbd0b84 173 (void),
1e6808ea
MG
174 "Return the number of time units of processor time used by the\n"
175 "interpreter. Both @emph{system} and @emph{user} time are\n"
176 "included but subprocesses are not.")
1bbd0b84 177#define FUNC_NAME s_scm_get_internal_run_time
0f2d19dd 178{
1c299a6b 179 return scm_long2num (scm_c_get_internal_run_time ());
0f2d19dd 180}
1bbd0b84 181#undef FUNC_NAME
0f2d19dd 182
1c299a6b 183SCM_DEFINE (scm_current_time, "current-time", 0, 0, 0,
1bbd0b84 184 (void),
1e6808ea
MG
185 "Return the number of seconds since 1970-01-01 00:00:00 UTC,\n"
186 "excluding leap seconds.")
1bbd0b84 187#define FUNC_NAME s_scm_current_time
0f2d19dd 188{
19468eff
GH
189 timet timv;
190
191 SCM_DEFER_INTS;
192 if ((timv = time (0)) == -1)
1bbd0b84 193 SCM_SYSERROR;
19468eff
GH
194 SCM_ALLOW_INTS;
195 return scm_long2num((long) timv);
196}
1bbd0b84 197#undef FUNC_NAME
19468eff 198
1c299a6b 199SCM_DEFINE (scm_gettimeofday, "gettimeofday", 0, 0, 0,
1bbd0b84 200 (void),
1e6808ea
MG
201 "Return a pair containing the number of seconds and microseconds\n"
202 "since 1970-01-01 00:00:00 UTC, excluding leap seconds. Note:\n"
203 "whether true microsecond resolution is available depends on the\n"
204 "operating system.")
1bbd0b84 205#define FUNC_NAME s_scm_gettimeofday
19468eff
GH
206{
207#ifdef HAVE_GETTIMEOFDAY
208 struct timeval time;
209
210 SCM_DEFER_INTS;
211 if (gettimeofday (&time, NULL) == -1)
1bbd0b84 212 SCM_SYSERROR;
19468eff
GH
213 SCM_ALLOW_INTS;
214 return scm_cons (scm_long2num ((long) time.tv_sec),
215 scm_long2num ((long) time.tv_usec));
216#else
217# ifdef HAVE_FTIME
218 struct timeb time;
219
220 ftime(&time);
1c299a6b 221 return scm_cons (scm_long2num ((long) time.time),
9a81afca 222 SCM_MAKINUM (time.millitm * 1000));
19468eff
GH
223# else
224 timet timv;
1c299a6b 225
19468eff
GH
226 SCM_DEFER_INTS;
227 if ((timv = time (0)) == -1)
1bbd0b84 228 SCM_SYSERROR;
19468eff
GH
229 SCM_ALLOW_INTS;
230 return scm_cons (scm_long2num (timv), SCM_MAKINUM (0));
231# endif
232#endif
233}
1bbd0b84 234#undef FUNC_NAME
19468eff
GH
235
236static SCM
237filltime (struct tm *bd_time, int zoff, char *zname)
238{
00ffa0e7 239 SCM result = scm_c_make_vector (11, SCM_UNDEFINED);
19468eff 240
34d19ef6
HWN
241 SCM_VECTOR_SET (result,0, SCM_MAKINUM (bd_time->tm_sec));
242 SCM_VECTOR_SET (result,1, SCM_MAKINUM (bd_time->tm_min));
243 SCM_VECTOR_SET (result,2, SCM_MAKINUM (bd_time->tm_hour));
244 SCM_VECTOR_SET (result,3, SCM_MAKINUM (bd_time->tm_mday));
245 SCM_VECTOR_SET (result,4, SCM_MAKINUM (bd_time->tm_mon));
246 SCM_VECTOR_SET (result,5, SCM_MAKINUM (bd_time->tm_year));
247 SCM_VECTOR_SET (result,6, SCM_MAKINUM (bd_time->tm_wday));
248 SCM_VECTOR_SET (result,7, SCM_MAKINUM (bd_time->tm_yday));
249 SCM_VECTOR_SET (result,8, SCM_MAKINUM (bd_time->tm_isdst));
250 SCM_VECTOR_SET (result,9, SCM_MAKINUM (zoff));
251 SCM_VECTOR_SET (result,10, zname ? scm_makfrom0str (zname) : SCM_BOOL_F);
19468eff
GH
252 return result;
253}
254
ef9ff3fd
GH
255static char tzvar[3] = "TZ";
256extern char ** environ;
257
38c1d3c4
GH
258/* if zone is set, create a temporary environment with only a TZ
259 string. other threads or interrupt handlers shouldn't be allowed
260 to run until the corresponding restorezone is called. hence the use
261 of a static variable for tmpenv is no big deal. */
ef9ff3fd 262static char **
3eeba8d4 263setzone (SCM zone, int pos, const char *subr)
b9525b92 264{
ef9ff3fd 265 char **oldenv = 0;
b9525b92
GH
266
267 if (!SCM_UNBNDP (zone))
268 {
ef9ff3fd 269 static char *tmpenv[2];
b9525b92
GH
270 char *buf;
271
9fd38a3d 272 SCM_ASSERT (SCM_STRINGP (zone), zone, pos, subr);
4c9419ac 273 buf = scm_malloc (SCM_STRING_LENGTH (zone) + sizeof (tzvar) + 1);
a6d9e5ab 274 sprintf (buf, "%s=%s", tzvar, SCM_STRING_CHARS (zone));
ef9ff3fd
GH
275 oldenv = environ;
276 tmpenv[0] = buf;
277 tmpenv[1] = 0;
278 environ = tmpenv;
b9525b92 279 }
ef9ff3fd 280 return oldenv;
b9525b92
GH
281}
282
283static void
e81d98ec 284restorezone (SCM zone, char **oldenv, const char *subr SCM_UNUSED)
b9525b92
GH
285{
286 if (!SCM_UNBNDP (zone))
287 {
4c9419ac 288 free (environ[0]);
ef9ff3fd 289 environ = oldenv;
38c1d3c4
GH
290#ifdef HAVE_TZSET
291 /* for the possible benefit of user code linked with libguile. */
b9525b92 292 tzset();
38c1d3c4 293#endif
b9525b92
GH
294 }
295}
296
1c299a6b 297SCM_DEFINE (scm_localtime, "localtime", 1, 1, 0,
1bbd0b84 298 (SCM time, SCM zone),
1e6808ea
MG
299 "Return an object representing the broken down components of\n"
300 "@var{time}, an integer like the one returned by\n"
301 "@code{current-time}. The time zone for the calculation is\n"
302 "optionally specified by @var{zone} (a string), otherwise the\n"
303 "@code{TZ} environment variable or the system default is used.")
1bbd0b84 304#define FUNC_NAME s_scm_localtime
19468eff
GH
305{
306 timet itime;
4edc089c 307 struct tm *ltptr, lt, *utc;
19468eff
GH
308 SCM result;
309 int zoff;
310 char *zname = 0;
ef9ff3fd 311 char **oldenv;
19468eff
GH
312 int err;
313
e4b265d8 314 itime = SCM_NUM2LONG (1, time);
38c1d3c4
GH
315
316 /* deferring interupts is essential since a) setzone may install a temporary
317 environment b) localtime uses a static buffer. */
19468eff 318 SCM_DEFER_INTS;
1bbd0b84 319 oldenv = setzone (zone, SCM_ARG2, FUNC_NAME);
38c1d3c4
GH
320#ifdef LOCALTIME_CACHE
321 tzset ();
322#endif
4edc089c 323 ltptr = localtime (&itime);
19468eff 324 err = errno;
4edc089c 325 if (ltptr)
19468eff 326 {
4d3bacdd 327 const char *ptr;
ef9ff3fd 328
43ff3170
GH
329 /* copy zone name before calling gmtime or restoring zone. */
330#if defined (HAVE_TM_ZONE)
ef9ff3fd 331 ptr = ltptr->tm_zone;
43ff3170 332#elif defined (HAVE_TZNAME)
ef9ff3fd 333 ptr = tzname[ (ltptr->tm_isdst == 1) ? 1 : 0 ];
43ff3170
GH
334#else
335 ptr = "";
b9525b92 336#endif
4c9419ac 337 zname = scm_malloc (strlen (ptr) + 1);
ef9ff3fd 338 strcpy (zname, ptr);
19468eff 339 }
ef9ff3fd
GH
340 /* the struct is copied in case localtime and gmtime share a buffer. */
341 if (ltptr)
342 lt = *ltptr;
343 utc = gmtime (&itime);
344 if (utc == NULL)
345 err = errno;
1bbd0b84 346 restorezone (zone, oldenv, FUNC_NAME);
b9525b92 347 /* delayed until zone has been restored. */
19468eff 348 errno = err;
4edc089c 349 if (utc == NULL || ltptr == NULL)
1bbd0b84 350 SCM_SYSERROR;
19468eff
GH
351
352 /* calculate timezone offset in seconds west of UTC. */
4edc089c
GH
353 zoff = (utc->tm_hour - lt.tm_hour) * 3600 + (utc->tm_min - lt.tm_min) * 60
354 + utc->tm_sec - lt.tm_sec;
355 if (utc->tm_year < lt.tm_year)
19468eff 356 zoff -= 24 * 60 * 60;
4edc089c 357 else if (utc->tm_year > lt.tm_year)
19468eff 358 zoff += 24 * 60 * 60;
4edc089c 359 else if (utc->tm_yday < lt.tm_yday)
19468eff 360 zoff -= 24 * 60 * 60;
4edc089c 361 else if (utc->tm_yday > lt.tm_yday)
19468eff 362 zoff += 24 * 60 * 60;
1c299a6b 363
4edc089c 364 result = filltime (&lt, zoff, zname);
19468eff 365 SCM_ALLOW_INTS;
4c9419ac
MV
366 if (zname)
367 free (zname);
19468eff
GH
368 return result;
369}
1bbd0b84 370#undef FUNC_NAME
19468eff 371
1c299a6b 372SCM_DEFINE (scm_gmtime, "gmtime", 1, 0, 0,
1bbd0b84 373 (SCM time),
1e6808ea
MG
374 "Return an object representing the broken down components of\n"
375 "@var{time}, an integer like the one returned by\n"
376 "@code{current-time}. The values are calculated for UTC.")
1bbd0b84 377#define FUNC_NAME s_scm_gmtime
19468eff
GH
378{
379 timet itime;
380 struct tm *bd_time;
381 SCM result;
382
e4b265d8 383 itime = SCM_NUM2LONG (1, time);
19468eff
GH
384 SCM_DEFER_INTS;
385 bd_time = gmtime (&itime);
386 if (bd_time == NULL)
1bbd0b84 387 SCM_SYSERROR;
19468eff
GH
388 result = filltime (bd_time, 0, "GMT");
389 SCM_ALLOW_INTS;
390 return result;
391}
1bbd0b84 392#undef FUNC_NAME
19468eff 393
b9525b92
GH
394/* copy time components from a Scheme object to a struct tm. */
395static void
3eeba8d4 396bdtime2c (SCM sbd_time, struct tm *lt, int pos, const char *subr)
19468eff 397{
34d19ef6 398 SCM const *velts;
55a7fc62
GH
399 int i;
400
0c95b57d 401 SCM_ASSERT (SCM_VECTORP (sbd_time)
9fd38a3d 402 && SCM_VECTOR_LENGTH (sbd_time) == 11,
b9525b92 403 sbd_time, pos, subr);
55a7fc62
GH
404 velts = SCM_VELTS (sbd_time);
405 for (i = 0; i < 10; i++)
406 {
407 SCM_ASSERT (SCM_INUMP (velts[i]), sbd_time, pos, subr);
408 }
0c95b57d 409 SCM_ASSERT (SCM_FALSEP (velts[10]) || SCM_STRINGP (velts[10]),
55a7fc62
GH
410 sbd_time, pos, subr);
411
412 lt->tm_sec = SCM_INUM (velts[0]);
413 lt->tm_min = SCM_INUM (velts[1]);
414 lt->tm_hour = SCM_INUM (velts[2]);
415 lt->tm_mday = SCM_INUM (velts[3]);
416 lt->tm_mon = SCM_INUM (velts[4]);
417 lt->tm_year = SCM_INUM (velts[5]);
418 lt->tm_wday = SCM_INUM (velts[6]);
419 lt->tm_yday = SCM_INUM (velts[7]);
420 lt->tm_isdst = SCM_INUM (velts[8]);
c7abe4f3 421#ifdef HAVE_TM_ZONE
55a7fc62
GH
422 lt->tm_gmtoff = SCM_INUM (velts[9]);
423 if (SCM_FALSEP (velts[10]))
424 lt->tm_zone = NULL;
425 else
a002f1a2 426 lt->tm_zone = SCM_STRING_CHARS (velts[10]);
c7abe4f3 427#endif
b9525b92
GH
428}
429
1c299a6b 430SCM_DEFINE (scm_mktime, "mktime", 1, 1, 0,
1bbd0b84 431 (SCM sbd_time, SCM zone),
a3c8b9fc
MD
432 "@var{bd-time} is an object representing broken down time and @code{zone}\n"
433 "is an optional time zone specifier (otherwise the TZ environment variable\n"
434 "or the system default is used).\n\n"
a8eac221 435 "Returns a pair: the car is a corresponding\n"
a3c8b9fc 436 "integer time value like that returned\n"
a8eac221 437 "by @code{current-time}; the cdr is a broken down time object, similar to\n"
a3c8b9fc 438 "as @var{bd-time} but with normalized values.")
1bbd0b84 439#define FUNC_NAME s_scm_mktime
b9525b92
GH
440{
441 timet itime;
442 struct tm lt, *utc;
443 SCM result;
444 int zoff;
445 char *zname = 0;
ef9ff3fd 446 char **oldenv;
b9525b92
GH
447 int err;
448
1bbd0b84 449 bdtime2c (sbd_time, &lt, SCM_ARG1, FUNC_NAME);
19468eff
GH
450
451 SCM_DEFER_INTS;
1bbd0b84 452 oldenv = setzone (zone, SCM_ARG2, FUNC_NAME);
38c1d3c4
GH
453#ifdef LOCALTIME_CACHE
454 tzset ();
455#endif
19468eff 456 itime = mktime (&lt);
b9525b92 457 err = errno;
19468eff 458
b9525b92
GH
459 if (itime != -1)
460 {
4d3bacdd 461 const char *ptr;
ef9ff3fd 462
43ff3170
GH
463 /* copy zone name before calling gmtime or restoring the zone. */
464#if defined (HAVE_TM_ZONE)
ef9ff3fd 465 ptr = lt.tm_zone;
43ff3170 466#elif defined (HAVE_TZNAME)
ef9ff3fd 467 ptr = tzname[ (lt.tm_isdst == 1) ? 1 : 0 ];
43ff3170
GH
468#else
469 ptr = "";
b9525b92 470#endif
4c9419ac 471 zname = scm_malloc (strlen (ptr) + 1);
ef9ff3fd 472 strcpy (zname, ptr);
b9525b92 473 }
ef9ff3fd
GH
474
475 /* get timezone offset in seconds west of UTC. */
476 utc = gmtime (&itime);
477 if (utc == NULL)
478 err = errno;
479
1bbd0b84 480 restorezone (zone, oldenv, FUNC_NAME);
b9525b92
GH
481 /* delayed until zone has been restored. */
482 errno = err;
483 if (utc == NULL || itime == -1)
1bbd0b84 484 SCM_SYSERROR;
b9525b92 485
19468eff
GH
486 zoff = (utc->tm_hour - lt.tm_hour) * 3600 + (utc->tm_min - lt.tm_min) * 60
487 + utc->tm_sec - lt.tm_sec;
488 if (utc->tm_year < lt.tm_year)
489 zoff -= 24 * 60 * 60;
490 else if (utc->tm_year > lt.tm_year)
491 zoff += 24 * 60 * 60;
492 else if (utc->tm_yday < lt.tm_yday)
493 zoff -= 24 * 60 * 60;
494 else if (utc->tm_yday > lt.tm_yday)
495 zoff += 24 * 60 * 60;
496
19468eff
GH
497 result = scm_cons (scm_long2num ((long) itime),
498 filltime (&lt, zoff, zname));
499 SCM_ALLOW_INTS;
4c9419ac
MV
500 if (zname)
501 free (zname);
19468eff 502 return result;
0f2d19dd 503}
1bbd0b84 504#undef FUNC_NAME
0f2d19dd 505
38c1d3c4 506#ifdef HAVE_TZSET
1c299a6b 507SCM_DEFINE (scm_tzset, "tzset", 0, 0, 0,
1bbd0b84 508 (void),
a3c8b9fc
MD
509 "Initialize the timezone from the TZ environment variable\n"
510 "or the system default. It's not usually necessary to call this procedure\n"
511 "since it's done automatically by other procedures that depend on the\n"
512 "timezone.")
1bbd0b84 513#define FUNC_NAME s_scm_tzset
0f2d19dd 514{
19468eff
GH
515 tzset();
516 return SCM_UNSPECIFIED;
0f2d19dd 517}
1bbd0b84 518#undef FUNC_NAME
38c1d3c4 519#endif /* HAVE_TZSET */
0f2d19dd 520
a1ec6916 521SCM_DEFINE (scm_strftime, "strftime", 2, 0, 0,
1bbd0b84 522 (SCM format, SCM stime),
a3c8b9fc
MD
523 "Formats a time specification @var{time} using @var{template}. @var{time}\n"
524 "is an object with time components in the form returned by @code{localtime}\n"
525 "or @code{gmtime}. @var{template} is a string which can include formatting\n"
526 "specifications introduced by a @code{%} character. The formatting of\n"
527 "month and day names is dependent on the current locale. The value returned\n"
528 "is the formatted string.\n"
529 "@xref{Formatting Date and Time, , , libc, The GNU C Library Reference Manual}.)")
1bbd0b84 530#define FUNC_NAME s_scm_strftime
b9525b92
GH
531{
532 struct tm t;
533
534 char *tbuf;
535 int size = 50;
a15e6dcc 536 char *fmt, *myfmt;
b9525b92 537 int len;
ef9ff3fd 538 SCM result;
b9525b92 539
a6d9e5ab 540 SCM_VALIDATE_STRING (1, format);
1bbd0b84 541 bdtime2c (stime, &t, SCM_ARG2, FUNC_NAME);
b9525b92 542
a6d9e5ab
DH
543 fmt = SCM_STRING_CHARS (format);
544 len = SCM_STRING_LENGTH (format);
b9525b92 545
a15e6dcc
MV
546 /* Ugly hack: strftime can return 0 if its buffer is too small,
547 but some valid time strings (e.g. "%p") can sometimes produce
548 a zero-byte output string! Workaround is to prepend a junk
549 character to the format string, so that valid returns are always
550 nonzero. */
4c9419ac 551 myfmt = scm_malloc (len+2);
a15e6dcc
MV
552 *myfmt = 'x';
553 strncpy(myfmt+1, fmt, len);
554 myfmt[len+1] = 0;
555
4c9419ac 556 tbuf = scm_malloc (size);
b8a1b29b
GH
557 {
558#if !defined (HAVE_TM_ZONE)
559 /* it seems the only way to tell non-GNU versions of strftime what
560 zone to use (for the %Z format) is to set TZ in the
561 environment. interrupts and thread switching must be deferred
562 until TZ is restored. */
563 char **oldenv = NULL;
2e945bcc 564 SCM *velts = (SCM *) SCM_VELTS (stime);
e652b54f 565 int have_zone = 0;
b8a1b29b 566
36284627 567 if (!SCM_FALSEP (velts[10]) && *SCM_STRING_CHARS (velts[10]) != 0)
b8a1b29b
GH
568 {
569 /* it's not required that the TZ setting be correct, just that
e652b54f
GH
570 it has the right name. so try something like TZ=EST0.
571 using only TZ=EST would be simpler but it doesn't work on
572 some OSs, e.g., Solaris. */
573 SCM zone =
574 scm_string_append (scm_cons (velts[10],
575 scm_cons (scm_makfrom0str ("0"),
576 SCM_EOL)));
1c299a6b 577
e652b54f 578 have_zone = 1;
b8a1b29b 579 SCM_DEFER_INTS;
e652b54f 580 oldenv = setzone (zone, SCM_ARG2, FUNC_NAME);
b8a1b29b
GH
581 }
582#endif
583
38c1d3c4 584#ifdef LOCALTIME_CACHE
b8a1b29b
GH
585 tzset ();
586#endif
587
a15e6dcc
MV
588 /* POSIX says strftime returns 0 on buffer overrun, but old
589 systems (i.e. libc 4 on GNU/Linux) might return `size' in that
590 case. */
591 while ((len = strftime (tbuf, size, myfmt, &t)) == 0 || len == size)
b8a1b29b 592 {
4c9419ac 593 free (tbuf);
b8a1b29b 594 size *= 2;
4c9419ac 595 tbuf = scm_malloc (size);
b8a1b29b
GH
596 }
597
598#if !defined (HAVE_TM_ZONE)
e652b54f 599 if (have_zone)
b8a1b29b
GH
600 {
601 restorezone (velts[10], oldenv, FUNC_NAME);
602 SCM_ALLOW_INTS;
603 }
38c1d3c4 604#endif
b9525b92 605 }
b8a1b29b 606
36284627 607 result = scm_mem2string (tbuf + 1, len - 1);
4c9419ac
MV
608 free (tbuf);
609 free (myfmt);
ef9ff3fd 610 return result;
b9525b92 611}
1bbd0b84 612#undef FUNC_NAME
b9525b92 613
f25f761d 614#ifdef HAVE_STRPTIME
a1ec6916 615SCM_DEFINE (scm_strptime, "strptime", 2, 0, 0,
1bbd0b84 616 (SCM format, SCM string),
a8eac221
MG
617 "Performs the reverse action to @code{strftime}, parsing\n"
618 "@var{string} according to the specification supplied in\n"
619 "@var{template}. The interpretation of month and day names is\n"
620 "dependent on the current locale. The value returned is a pair.\n"
621 "The car has an object with time components\n"
a3c8b9fc
MD
622 "in the form returned by @code{localtime} or @code{gmtime},\n"
623 "but the time zone components\n"
624 "are not usefully set.\n"
a8eac221
MG
625 "The cdr reports the number of characters from @var{string}\n"
626 "which were used for the conversion.")
1bbd0b84 627#define FUNC_NAME s_scm_strptime
b9525b92 628{
b9525b92
GH
629 struct tm t;
630 char *fmt, *str, *rest;
631
a6d9e5ab
DH
632 SCM_VALIDATE_STRING (1, format);
633 SCM_VALIDATE_STRING (2, string);
b9525b92 634
a6d9e5ab
DH
635 fmt = SCM_STRING_CHARS (format);
636 str = SCM_STRING_CHARS (string);
b9525b92
GH
637
638 /* initialize the struct tm */
639#define tm_init(field) t.field = 0
640 tm_init (tm_sec);
641 tm_init (tm_min);
642 tm_init (tm_hour);
643 tm_init (tm_mday);
644 tm_init (tm_mon);
645 tm_init (tm_year);
646 tm_init (tm_wday);
647 tm_init (tm_yday);
648#undef tm_init
649
650 t.tm_isdst = -1;
651 SCM_DEFER_INTS;
652 if ((rest = strptime (str, fmt, &t)) == NULL)
1bbd0b84 653 SCM_SYSERROR;
b9525b92
GH
654
655 SCM_ALLOW_INTS;
656 return scm_cons (filltime (&t, 0, NULL), SCM_MAKINUM (rest - str));
b9525b92 657}
1bbd0b84 658#undef FUNC_NAME
f25f761d 659#endif /* HAVE_STRPTIME */
b9525b92 660
0f2d19dd
JB
661void
662scm_init_stime()
0f2d19dd 663{
86d31dfe 664 scm_c_define ("internal-time-units-per-second",
756414cf 665 scm_long2num((long) SCM_TIME_UNITS_PER_SECOND));
0f2d19dd
JB
666
667#ifdef HAVE_FTIME
668 if (!scm_your_base.time) ftime(&scm_your_base);
669#else
670 if (!scm_your_base) time(&scm_your_base);
671#endif
672
673 if (!scm_my_base) scm_my_base = mytime();
674
876c87ce 675 scm_add_feature ("current-time");
a0599745 676#include "libguile/stime.x"
0f2d19dd
JB
677}
678
89e00824
ML
679
680/*
681 Local Variables:
682 c-file-style: "gnu"
683 End:
684*/