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