/* Moscow SML primitives */

#include <math.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/resource.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h> 
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <utime.h>
#include <ctype.h>
#include "fail.h"
#include "memory.h"
#include "str.h"
#include "runtime.h"

#if defined(sun) && !defined(__svr4__)
#define tm2cal(tptr)	timelocal(tptr)
#else
#define tm2cal(tptr)	mktime(tptr)
#endif

#define Raise_float_if(cond) \
   if( cond ) \
      { mlraise(Atom(float_exn)); }

#define Check_float(dval) \
   Raise_float_if( (dval > maxdouble) || (dval < -maxdouble) )

/* Structural equality on trees. */
/* Note how reference cells are treated! */

static int sml_equal_aux(v1, v2)
     value v1,v2;
{
  mlsize_t i;
  value * p1, * p2;

 again:
  if (v1 == v2) return 1;
  if (Is_long(v1) || Is_long(v2)) return 0;
  if (!Is_in_heap(v1) && !Is_young(v1)) return 0;
  if (!Is_in_heap(v2) && !Is_young(v2)) return 0;
  if (Tag_val(v1) != Tag_val(v2)) return 0;
  switch(Tag_val(v1)) {
  case String_tag:
    return (compare_strings(v1, v2) == Val_long(0));
  case Double_tag:
    return (Double_val(v1) == Double_val(v2));
  case Reference_tag:  /* Different reference cells are not equal! */
  case Abstract_tag:
  case Final_tag:
    return 0;
  case Closure_tag:
    invalid_argument("sml_equal: functional value");
  default:
    i = Wosize_val(v1);
    if (i != Wosize_val(v2)) return 0;
    for(p1 = Op_val(v1), p2 = Op_val(v2);
        i > 1;
        i--, p1++, p2++)
      if (!sml_equal_aux(*p1, *p2)) return 0;
    v1 = *p1;
    v2 = *p2;                   /* Tail-call */
    goto again;
  }
}

value sml_equal(v1, v2) /* ML */
     value v1, v2;
{
  return Atom(sml_equal_aux(v1,v2));
}

value sml_not_equal(v1, v2) /* ML */
     value v1, v2;
{
  return Atom(!sml_equal_aux(v1,v2));
}

value sml_system(cmd)        /* ML */
     value cmd;
{
  return Val_int(system(String_val(cmd)));
}

value sml_abs_int(x)          /* ML */
    value x;
{ value tmp, v;
  tmp = Long_val(x);
  if( tmp < 0 ) tmp = -tmp;
  v = Val_long(tmp);
  if( Long_val(v) != tmp )
    mlraise(Atom(SMLEXN_OVF));
  return v;
}

value sml_floor(f)              /* ML */
     value f;
{ double r;
  long i;
  value v;
  r = Double_val(f);
  if( r >= 0.0 )
    { if( r >= ((double) (Max_long+1)) ) goto raise_floor;
      i = (long) r;
    }
  else
    { if( r < ((double) Min_long) ) goto raise_floor;
      i = (long) r;
      if( r < ((double) i) ) i -= 1;
    }
  v = Val_long(i);
  if( Long_val(v) != i )  goto raise_floor;
  return v;

raise_floor:
    mlraise(Atom(SMLEXN_OVF));
}

value sml_ceil(f)              /* ML */
     value f;
{ double r;
  long i;
  value v;
  r = Double_val(f);
  if( r >= 0.0 )
    { if( r > ((double) (Max_long)) ) goto raise_ceil;
      i = (long) r;
      if( r > ((double) i) ) i += 1;
    }
  else
    { if( r <= ((double) (Min_long-1)) ) goto raise_ceil;
      i = (long) r;
    }
  v = Val_long(i);
  if( Long_val(v) != i )  goto raise_ceil;
  return v;

raise_ceil:
    mlraise(Atom(SMLEXN_OVF));
}

#ifdef __MWERKS__
#include <Types.h>
double_t nearbyint ( double_t x );
#define rint nearbyint
#endif

value sml_round(f)              /* ML */
     value f;
{ double r;
  long i;
  value v;
  /* Apparently no rint() in djgpp's libm: */
#if defined(MSDOS) || defined(hpux)
  r = Double_val(f);
  if( r >= 0.0 )
    { if( (r+0.5) >= ((double) (Max_long+1)) ) goto raise_round;
      i = (long) (r+0.5);
    }
  else
    { if( (r-0.5) < ((double) Min_long) ) goto raise_round;
      i = (long) (r-0.5);
    }
  v = Val_long(i);
  if( Long_val(v) != i )  goto raise_round;
#else
  r = rint(Double_val(f));
  if ((r > (double) (Max_long)) || (r < (double)(Min_long))) goto raise_round;
  i = (long) r;
  v = Val_long(i);
#endif

  return v;

raise_round:
    mlraise(Atom(SMLEXN_OVF));
}

value sml_trunc(f)              /* ML */
     value f;
{ double r;
  long i;
  value v;
  r = Double_val(f);
  if ((r >= (double) (Max_long+1)) || (r <= (double)(Min_long-1))) 
    goto raise_trunc;
  i = (long) r;
  v = Val_long(i);
  return v;

raise_trunc:
    mlraise(Atom(SMLEXN_OVF));
}

value sml_abs_real(f)              /* ML */
     value f;
{ double r;
  float_exn = SMLEXN_OVF;
  r = Double_val(f);
  if( r >= 0.0 )
    return f;
  else
    r = -r;
    Check_float(r);
    return copy_double(r);
}

value sml_sqrt(f)         /* ML */
     value f;
{ double r;
  float_exn = SMLEXN_DOMAIN;
  r = Double_val(f);
  Raise_float_if( r < 0.0 );
  r = sqrt(r);
  Check_float(r);
  return copy_double(r);
}

value sml_sin(f)         /* ML */
     value f;
{ double r;
  r = Double_val(f);
  r = sin(r);
  if( r != r || r > 1.0 || r < -1.0 )
    failwith("sin: argument too large");
  return copy_double(r);
}

value sml_cos(f)         /* ML */
     value f;
{ double r;
  r = Double_val(f);
  r = cos(r);
  if( r != r || r > 1.0 || r < -1.0 )
    failwith("cos: argument too large");
  return copy_double(r);
}

value sml_exp(f)           /* ML */
     value f;
{ double r;
  float_exn = SMLEXN_OVF;
  r = exp(Double_val(f));
  Check_float(r);
  return copy_double(r);
}

value sml_ln(f)           /* ML */
     value f;
{ double r;
  float_exn = SMLEXN_DOMAIN;
  r = Double_val(f);
  Raise_float_if( r <= 0.0 );
  r = log(r);
  Check_float(r);
  return copy_double(r);
}

value scandec(p, max)
     char *p;
     unsigned long max;
{ unsigned long res;
  int c, d;
  res = 0;
  while (1) {
    c = *p;
    if (c >= '0' && c <= '9')
      d = c - '0';
    else
      break;
    if( (res > (max/10)) ||
        ((res == (max/10) && ((max % 10) <= d))) )
      goto raise_failure;
    res = 10 * res + d;
    p++;
  }
  if (*p != 0)
    goto raise_failure;
  return res;

  raise_failure:
    failwith("scandec");
}

value scanhex(p, max)
     char *p;
     unsigned long max;
{ unsigned long res;
  int c, d;
  res = 0;
  while (1) {
    c = toupper(*p);
    if (c >= '0' && c <= '9')
      d = c - '0';
    else if (c >= 'A' && c <= 'F')
      d = c + (10 - 'A');
    else
      break;
    if( (res > (max/16)) ||
        ((res == (max/16) && ((max % 16) <= d))) )
      goto raise_failure;
    res = 16 * res + d;
    p++;
  }
  if (*p != 0)
    goto raise_failure;
  return res;

  raise_failure:
    failwith("scandec");
}

value sml_int_of_string(s)          /* ML */
     value s;
{ value v;
  long res;
  int sign;
  char * p;

  p = String_val(s);
  sign = 1;
  if (*p == '~') {
    sign = -1;
    p++;
  }
  res = sign * scandec(p, (unsigned long)Min_long);
  v = Val_long(res);
  if( Long_val(v) != res )
    goto raise_failure;
  return v;

  raise_failure:
    failwith("sml_int_of_string");
}

value sml_concat(s1, s2)        /* ML */
     value s1, s2;
{
  mlsize_t len1, len2, len;
  value s;
  Push_roots(r, 2);
  r[0] = s1;
  r[1] = s2;
  len1 = string_length(s1);
  len2 = string_length(s2);
  len = len1 + len2;
  if( (len + sizeof (value)) / sizeof (value) > Max_wosize )
    mlraise(Atom(END_OF_FILE_EXN)); /* This translates to exn Size! 
				       See src/compiler/Smlexc.sml */
  s = alloc_string(len);
  bcopy(&Byte(r[0],0), &Byte(s,0), len1);
  bcopy(&Byte(r[1],0), &Byte(s,len1), len2);
  Pop_roots();
  return s;
}

value sml_chr(v)          /* ML */
     value v;
{
  long i;
  value s;
  i = Long_val(v);
  if( i < 0 || i > 255 )
    mlraise(Atom(SMLEXN_CHR));
  s = alloc_string(1);
  *(&Byte(s,0)) = (unsigned char) i;
  return s;
}

value sml_ord(s)          /* ML */
     value s;
{
  long i;
  if( string_length(s) == 0 )
    mlraise(Atom(SMLEXN_ORD));
  i = (unsigned char) *(&Byte(s,0));
  return Val_long(i);
}

value sml_float_of_string(s)        /* ML */
     value s;
{

  char buff[64];
  mlsize_t len;
  int i, e_len;
  char c;
  char *p;
  double r;

  len = string_length(s);
  if(len > sizeof(buff) - 1)
    failwith("sml_float_of_string: argument too large");
  p = String_val(s);
  e_len = -1;
  for (i = 0; i<len; i++) {
    c = *p++;
    switch( c ) {
        case '~':
          buff[i] = '-'; break;
        case 'E':
          buff[i] = 'e'; e_len = 0; break;
        default:
          buff[i] = c;
          if( e_len >= 0 ) e_len++;
          Raise_float_if( e_len > 5 )
          break;
    }
  }
  buff[len] = 0;
  r = atof(buff);
  if( (r > maxdouble) || (r < -maxdouble) )
    failwith("sml_float_of_string: result too large");
  return copy_double(r);
}


static int countChar(c, s)
   int c; char* s;
{
  char *p; int count;

  count = 0;
  for( p=s; *p != '\0'; p++ ) {
    if( *p == c ) count++;
  }
  return count;
}

/* Here we remove all '+', and replace '-' and 'e' */
/* with '~' and 'E', respectively. */

static void mkSMLMinus(s)
  char *s;
{
  char *p, *q;

  for( p=s, q=s; *p != '\0'; p++ ) {
    switch( *p ) {
        case '+': break;
        case '-': *q++ = '~'; break;
        case 'e': *q++ = 'E'; break;
        default: *q++ = *p;
    }
  }
  *q = '\0';
  return;
}

value sml_string_of_int(arg)      /* ML */
     value arg;
{
  char format_buffer[32];

  sprintf(format_buffer, "%ld", Long_val(arg));
  mkSMLMinus(format_buffer);
  return copy_string(format_buffer);
}

value sml_string_of_float(arg)    /* ML */
     value arg;
{
  char format_buffer[64];

  sprintf(format_buffer, "%.12g", Double_val(arg));
  mkSMLMinus(format_buffer);
  if( countChar('.', format_buffer) == 0 &&
      countChar('E', format_buffer) == 0 )
    strcat(format_buffer, ".0");
  return copy_string(format_buffer);
}

#ifdef __MWERKS__
#pragma mpwc_newline on
#endif

value sml_makestring_of_char(arg)      /* ML */
     value arg;
{
  unsigned char c;
  char buff[8];

  c = Int_val(arg);
  switch (c)
    {
    case '"':   return copy_string("#\"\\\"\"");
    case '\\':  return copy_string("#\"\\\\\"");
    case '\a':  return copy_string("#\"\\a\"");
    case '\b':  return copy_string("#\"\\b\"");
    case '\t':  return copy_string("#\"\\t\"");
    case '\n':  return copy_string("#\"\\n\"");
    case '\v':  return copy_string("#\"\\v\"");
    case '\f':  return copy_string("#\"\\f\"");
    case '\r':  return copy_string("#\"\\r\"");
    default:
      buff[0] = '#'; buff[1] = '"';
      if( c <= 31 ) {
        buff[2] = '\\'; buff[3] = '^'; buff[4] = c + 64;
        buff[5] = '"'; buff[6] = 0;
        return copy_string(buff);
        }
      else if( (32 <= c && c <= 126) || (128 <= c && c <= 254) ) {
        buff[2] = c; buff[3] = '"'; buff[4] = 0;
        return copy_string(buff);
        }
      else {
        buff[2] = '\\';
        buff[3] = 48 + c / 100;
        buff[4] = 48 + (c / 10) % 10;
        buff[5] = 48 + c % 10;
        buff[6] = '"';
        buff[7] = 0;
        return copy_string(buff);
        }
    }
}

value sml_makestring_of_string(arg)      /* ML */
     value arg;
{
  mlsize_t arg_len, len, i;
  value res;
  char *a; char *b;
  unsigned char c;
  Push_roots(r, 1);

  r[0] = arg;
  arg_len = string_length(arg);

  a = String_val(r[0]);
  len = 0;
  for( i = 0; i < arg_len; i++ ) {
    c = a[i];
    switch (c)
      {
      case '"': case '\\': 
      case '\a': case '\b': case '\t': case '\n': case '\v': 
      case '\f': case '\r':
        len += 2; break;
      default:
        if( c <= 31)
          len += 3;
        else if( (32 <= c && c <= 126) || (128 <= c && c <= 254) )
          len += 1;
        else
          len += 4;
        break;
      }
    }

  if( (len + 2 + sizeof (value)) / sizeof (value) > Max_wosize )
    failwith("sml_string_for_read: result too large");
  res = alloc_string(len + 2);

  a = String_val(r[0]);
  b = String_val(res);
  *b++ = '"';
  for( i = 0; i < arg_len; i++) {
    c = a[i];
    switch (c)
      {
      case '"':   *b++ = '\\'; *b++ = '"';  break;
      case '\\':  *b++ = '\\'; *b++ = '\\'; break;
      case '\a':  *b++ = '\\'; *b++ = 'a';  break;
      case '\b':  *b++ = '\\'; *b++ = 'b';  break;
      case '\t':  *b++ = '\\'; *b++ = 't';  break;
      case '\n':  *b++ = '\\'; *b++ = 'n';  break;
      case '\v':  *b++ = '\\'; *b++ = 'v';  break;
      case '\f':  *b++ = '\\'; *b++ = 'f';  break;
      case '\r':  *b++ = '\\'; *b++ = 'r';  break;
      default:
        if( c <= 31 )
          { *b++ = '\\'; *b++ = '^'; *b++ = c + 64; break; }
        else if( (32 <= c && c <= 126) || (128 <= c && c <= 254) )
          { *b++ = c; break; }
        else
          { *b++ = '\\';
            *b++ = 48 + c / 100;
            *b++ = 48 + (c / 10) % 10;
            *b++ = 48 + c % 10;
            break; }
      }
    }
  *b++ = '"';
  Pop_roots();
  return res;
}

#ifdef __MWERKS__
#pragma mpwc_newline off
#endif

/* The following must agree with timebase in mosmllib/Time.sml: */

#define TIMEBASE (-1073741824)

/* There is another problem on the Mac: with a time base of 1904,
   most times are simply out of range of mosml integers. So, I added
   the macros below to compensate. 07Sep95 e
*/

#ifdef macintosh

#if ( __MWERKS__ >= 0x1100 )
// 15May97 e for MSL 2.1.1
#define TMacbaseyr      1900L
/* number of leap days between the two years -- MSL base was not a leap year! */
#define TLpD    ((TUNIXbaseyr-TMacbaseyr)/4)
#else
/* 28Jan93   Kjeld & Soren */
#define TMacbaseyr      1904L
/* number of leap days between the two years -- Mac base was a leap year! */
#define TLpD    ((TUNIXbaseyr-TMacbaseyr-1)/4) + 1
#endif

#define TUNIXbaseyr     1970L

/* TimeBaseDif is the number of seconds between Mac and UNIX time (GMT) */
#define TimeBaseDif     ((((TUNIXbaseyr-TMacbaseyr)*365)+TLpD)*24*60*60)

#define SYStoSMLtime(m) ((m) - TimeBaseDif)
#define SMLtoSYStime(s) ((s) + TimeBaseDif)

#else

#define SYStoSMLtime
#define SMLtoSYStime

#endif

value sml_getrealtime (v) /* ML */
    value v;
{
  value res;
  struct timeval tp;

  gettimeofday(&tp, NULL);
  res = alloc (2, 0);
  Field (res, 0) = Val_long (SYStoSMLtime(tp.tv_sec)+TIMEBASE);
  Field (res, 1) = Val_long (tp.tv_usec);
  return res;
}

value sml_getrutime (v) /* ML */
    value v;
{
  value res;

#if defined(__MWERKS__)
  res = e_getrusage();
#else
#if defined(hpux) || defined(__svr4__)
  struct tms buffer;

  long persec = sysconf(_SC_CLK_TCK);
  times(&buffer);
  res = alloc (6, 0);
  Field (res, 2) = Val_long (buffer.tms_stime / persec);
  Field (res, 3) = Val_long ((buffer.tms_stime % persec) * (1000000 / persec));
  Field (res, 4) = Val_long (buffer.tms_utime / persec);
  Field (res, 5) = Val_long ((buffer.tms_utime % persec) * (1000000 / persec));
#else
  struct rusage rusages;
  getrusage(RUSAGE_SELF, &rusages);
  res = alloc (6, 0);
  Field (res, 2) = Val_long (rusages.ru_stime.tv_sec);
  Field (res, 3) = Val_long (rusages.ru_stime.tv_usec);
  Field (res, 4) = Val_long (rusages.ru_utime.tv_sec); 
  Field (res, 5) = Val_long (rusages.ru_utime.tv_usec);
#endif

  Field (res, 0) = Val_long (gc_time.tv_sec);
  Field (res, 1) = Val_long (gc_time.tv_usec); 
#endif

  return res;
}


value sml_errno(arg)          /* ML */
     value arg;
{
  return Val_long(errno);
}

value sml_getdir(arg)		/* ML */
     value arg;
{
 char directory[MAXPATHLEN];
 char *res;

 errno = 0;
 res = getcwd(directory, MAXPATHLEN);
 if (res == NULL)
    failwith("getcwd");
 return copy_string(directory);
}

value sml_mkdir(path)          /* ML */
     value path;
{
  if (mkdir(String_val(path), 0777) == -1) 
      failwith("mkdir");
  return Val_unit;
}

value sml_rmdir(path)          /* ML */
     value path;
{
  if (rmdir(String_val(path)) == -1) 
      failwith("rmdir");
  return Val_unit;
}

value sml_opendir(path)          /* ML */
     value path;
{ DIR * dstr;

  dstr = opendir(String_val(path));
  if (dstr == NULL)
      failwith("opendir");
#ifdef MSDOS
  if (readdir(dstr) == NULL) 
      failwith("opendir");
  else
      rewinddir(dstr);
#endif
  return (value) dstr;
}

value sml_rewinddir(v)          /* ML */
     value v;
{ 
  rewinddir((DIR *) v);
  return Val_unit;
}

value sml_readdir(v)          /* ML */
     value v;
{ struct dirent *direntry;

  direntry = readdir((DIR *) v);
  if (direntry == NULL) 
      return copy_string("");
  return copy_string((*direntry).d_name);
}

value sml_closedir(v)          /* ML */
     value v;
{ 
  if (closedir((DIR *) v) == -1)
      failwith("closedir");
  return Val_unit;
}

value sml_isdir(path)          /* ML */
     value path;
{ struct stat buf;

  if (stat(String_val(path), &buf) == -1)
      failwith("stat");
  return (Val_bool(S_ISDIR(buf.st_mode)));
}

value sml_modtime(path)          /* ML */
     value path;
{ struct stat buf;

  if (stat(String_val(path), &buf) == -1)
      failwith("stat");
  return (copy_double ((double) (SYStoSMLtime(buf.st_mtime))));
}

value sml_settime(path, time)          /* ML */
     value path, time;
{ struct utimbuf tbuf;

  tbuf.actime = tbuf.modtime = SMLtoSYStime((long) (Double_val(time)));
  if (utime(String_val(path), &tbuf) == -1)
      failwith("utime");
  return Val_unit;
}

value sml_access(path, permarg)          /* ML */
     value path, permarg;
{ long perms;
  long perm = Long_val(permarg);

  perms  = ((0x1 & perm) ? R_OK : 0);
  perms |= ((0x2 & perm) ? W_OK : 0);
  perms |= ((0x4 & perm) ? X_OK : 0);
  if (perms == 0) perms = F_OK;

  if (access(String_val(path), perms) == 0)
    return Val_bool(1);
  return Val_bool(0);
}

#ifndef HAS_STRERROR
  extern int sys_nerr;
  extern char * sys_errlist [];
  extern char *realpath();
  char *mktemp();
#endif

value sml_tmpnam(v)          /* ML */
     value v;
{ char *res;

  res = tmpnam(NULL);
  if (res == NULL) 
    failwith("tmpnam");  
  return copy_string(res);
}

value sml_errormsg(err)   /* ML */
     value err;
{
  int errnum;
  errnum = Long_val(err);
#ifdef HAS_STRERROR
  return copy_string(strerror(errnum));
#else
  if (errnum < 0 || errnum >= sys_nerr) 
      return copy_string("(Unknown error)");
  else 
    return copy_string(sys_errlist[errnum]);
#endif
}

value sml_asin(f)           /* ML */
     value f;
{ double r = Double_val(f);
  float_exn = SMLEXN_DOMAIN;
  Raise_float_if( r < -1.0 || r > 1.0 );  
  r = asin(r);
  Raise_float_if( r != r );
  return copy_double(r);
}

value sml_acos(f)           /* ML */
     value f;
{ double r = Double_val(f);
  float_exn = SMLEXN_DOMAIN;
  Raise_float_if( r < -1.0 || r > 1.0 );  
  r = acos(r);
  Raise_float_if( r != r );
  return copy_double(r);
}

value sml_atan2(f1, f2)           /* ML */
     value f1, f2;
{ double r, r1, r2;
  float_exn = SMLEXN_DOMAIN;
  r1 = Double_val(f1);
  r2 = Double_val(f2);
  if (r1 == 0.0 && r2 == 0.0) 
    return copy_double(0.0);
  r = atan2(r1, r2);
  Check_float(r);
  Raise_float_if( r != r );
  return copy_double(r);
}

value sml_pow(f1, f2)           /* ML */
     value f1, f2;
{ double r, r1, r2;
  float_exn = SMLEXN_DOMAIN;
  r1 = Double_val(f1);
  r2 = Double_val(f2);
  if (r1 == 0.0 && r2 == 0.0) 
    return copy_double(1.0);
  if (   (r1 == 0.0 && r2 < 0.0) 
      || (r1 < 0.0 && (   fabs(r2) > (double) (Max_long) 
		       || r2 != (double)(long)r2)))
    mlraise(Atom(float_exn));
  r = pow(r1, r2);
  float_exn = SMLEXN_OVF;
  Check_float(r);
  float_exn = SMLEXN_DOMAIN;
  Raise_float_if( r != r );
  return copy_double(r);
}

value sml_localtime (v) /* ML */
    value v;
{
  value res;
  struct tm *tmr;
  time_t clock = SMLtoSYStime((long) (Double_val(v)));
  tmr = localtime(&clock);
  res = alloc (9, 0);
  Field (res, 0) = Val_long ((*tmr).tm_hour);
  Field (res, 1) = Val_long ((*tmr).tm_isdst);
  Field (res, 2) = Val_long ((*tmr).tm_mday);
  Field (res, 3) = Val_long ((*tmr).tm_min); 
  Field (res, 4) = Val_long ((*tmr).tm_mon);
  Field (res, 5) = Val_long ((*tmr).tm_sec);
  Field (res, 6) = Val_long ((*tmr).tm_wday);
  Field (res, 7) = Val_long ((*tmr).tm_yday);
  Field (res, 8) = Val_long ((*tmr).tm_year);

  return res;
}

value sml_gmtime (v) /* ML */
    value v;
{
  value res;
  struct tm *tmr;
  time_t clock = SMLtoSYStime((long) (Double_val(v)));
  tmr = gmtime(&clock);
  res = alloc (9, 0);
  Field (res, 0) = Val_long ((*tmr).tm_hour);
  Field (res, 1) = Val_long ((*tmr).tm_isdst);
  Field (res, 2) = Val_long ((*tmr).tm_mday);
  Field (res, 3) = Val_long ((*tmr).tm_min); 
  Field (res, 4) = Val_long ((*tmr).tm_mon);
  Field (res, 5) = Val_long ((*tmr).tm_sec);
  Field (res, 6) = Val_long ((*tmr).tm_wday);
  Field (res, 7) = Val_long ((*tmr).tm_yday);
  Field (res, 8) = Val_long ((*tmr).tm_year);
  return res;
}

value sml_mktime (v) /* ML */
    value v;
{
  struct tm tmr;

  tmr.tm_hour  = Long_val(Field (v, 0));
  tmr.tm_isdst = Long_val(Field (v, 1));
  tmr.tm_mday  = Long_val(Field (v, 2));
  tmr.tm_min   = Long_val(Field (v, 3));
  tmr.tm_mon   = Long_val(Field (v, 4));
  tmr.tm_sec   = Long_val(Field (v, 5));
  tmr.tm_wday  = Long_val(Field (v, 6));
  tmr.tm_yday  = Long_val(Field (v, 7));
  tmr.tm_year  = Long_val(Field (v, 8));

  return copy_double((double)SYStoSMLtime(tm2cal(&tmr)));

}

value sml_asctime (v) /* ML */
    value v;
{
  struct tm tmr;
  char *res;

  tmr.tm_hour  = Long_val(Field (v, 0));
  tmr.tm_isdst = Long_val(Field (v, 1));
  tmr.tm_mday  = Long_val(Field (v, 2));
  tmr.tm_min   = Long_val(Field (v, 3));
  tmr.tm_mon   = Long_val(Field (v, 4));
  tmr.tm_sec   = Long_val(Field (v, 5));
  tmr.tm_wday  = Long_val(Field (v, 6));
  tmr.tm_yday  = Long_val(Field (v, 7));
  tmr.tm_year  = Long_val(Field (v, 8));

  tm2cal(&tmr);

  res = asctime(&tmr);
  if (res == NULL) 
      failwith("asctime");
  return copy_string(res);
}

value sml_strftime (fmt, v) /* ML */
    value fmt, v;
{
  struct tm tmr;
#define BUFSIZE 256      
  char buf[BUFSIZE];
  long ressize;

  tmr.tm_hour  = Long_val(Field (v, 0));
  tmr.tm_isdst = Long_val(Field (v, 1));
  tmr.tm_mday  = Long_val(Field (v, 2));
  tmr.tm_min   = Long_val(Field (v, 3));
  tmr.tm_mon   = Long_val(Field (v, 4));
  tmr.tm_sec   = Long_val(Field (v, 5));
  tmr.tm_wday  = Long_val(Field (v, 6));
  tmr.tm_yday  = Long_val(Field (v, 7));
  tmr.tm_year  = Long_val(Field (v, 8));

  tm2cal(&tmr);

  ressize = strftime(buf, BUFSIZE, String_val(fmt), &tmr);
  if (ressize == 0 || ressize == BUFSIZE) 
      failwith("strftime");
  return copy_string(buf);
#undef BUFSIZE
}

value sml_general_string_of_float(fmt, arg)    /* ML */
     value fmt, arg;
{
#define BUFSIZE 512
  char format_buffer[BUFSIZE];
  int i;

  /* Unfortunately there seems to be no way to ensure that this does not
   * crash by overflowing the format_buffer (e.g. when specifying a huge 
   * number of decimal digits in the fixed-point format): 
   */

  sprintf(format_buffer, String_val(fmt), Double_val(arg));

  mkSMLMinus(format_buffer);
  return copy_string(format_buffer);
#undef BUFSIZE
}

value sml_filesize(path)          /* ML */
     value path;
{ struct stat buf;

  if (stat(String_val(path), &buf) == -1)
      failwith("stat");
  return (Val_long (buf.st_size));
}

value sml_int_of_hex(s)          /* ML */
     value s;
{ value v;
  long res;
  int sign;
  char * p;

  /* The argument s has form [~]?0x[0-9a-fA-F]+ */

  p = String_val(s);
  sign = 1;
  if (*p == '~') {
    sign = -1;
    p++;
  }
  /* skip 0x in s */
  p += 2; 

  res = sign * scanhex(p, (unsigned long)Min_long);
  v = Val_long(res);
  if( Long_val(v) != res )
    goto raise_failure;
  return v;

  raise_failure:
    failwith("sml_int_of_hex");
}

value sml_word_of_hex(s)          /* ML */
     value s;
{ value v;
  long res;
  char * p;

  /* The argument s has form 0wx[0-9a-fA-F]+ */

  p = String_val(s);
  /* skip 0wx in s */
  p += 3; 

  res = scanhex(p, 2 * (unsigned long)Min_long);
  v = Val_long((long)res);
  return v;
}

value sml_word_of_dec(s)          /* ML */
     value s;
{ value v;
  long res;
  char * p;

  /* The argument s has form 0w[0-9]+ */
  p = String_val(s);
  /* skip 0w in s */
  p += 2; 

  res = (long)scandec(p, 2 * (unsigned long)Min_long);
  v = Val_long((long)res);
  return v;
}

value sml_hexstring_of_word(arg)      /* ML */
     value arg;
{
  char format_buffer[32];

  sprintf(format_buffer, "0wx%lX", Long_val((unsigned long)arg));
  return copy_string(format_buffer);
}

value sml_sinh(f)         /* ML */
     value f;
{ double r;
  float_exn = SMLEXN_OVF;  
  r = Double_val(f);
  r = sinh(r);
  Check_float(r);
  return copy_double(r);
}

value sml_cosh(f)         /* ML */
     value f;
{ double r;
  float_exn = SMLEXN_OVF;
  r = Double_val(f);
  r = cosh(r);
  Check_float(r);
  return copy_double(r);
}

value sml_tanh(f)         /* ML */
     value f;
{ double r;
  float_exn = SMLEXN_DOMAIN;
  r = Double_val(f);
  r = tanh(r);
  Check_float(r);
  return copy_double(r);
}

