~peter-pearse/ubuntu/natty/guile-1.8/prop001

« back to all changes in this revision

Viewing changes to libguile/stime.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Schepler
  • Date: 2006-11-09 03:11:16 UTC
  • Revision ID: james.westby@ubuntu.com-20061109031116-hu0q1jxqg12y6yeg
Tags: upstream-1.8.1+1
ImportĀ upstreamĀ versionĀ 1.8.1+1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
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
 
 
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
*/