
#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>

#include "internal.h"
#include "internalfile.h"
#include "imptypes.h"
#include "visible.h"
#include "psignal.h"

char *_imp_current_file;
int _imp_current_line;

// All imp output must go through this code so that OUTPOS can be kept up to date.
// It's an overhead but one I can live with.  Note that we probably don't care
// about OUTPOS when we know it's a binary file that is being written to.

// I'm not yet handling INPOS for input files and I don't think I will need to,
// it's not a particularly useful function.  Unless it's needed in order to handle
// prompts correctly?)  The wrapping of printf won't work as well for scanf.

// (btw old IMP programs used EM CHAR as the end of file marker!  Then again,
//  they were all also 7-bit ISO.)

void _imp_putchar(int ch /*, FILE *f*/) {
  // really only on text mode files, not binary output...
  fputc(ch, _imp_outfile[_imp_OutStream].f);
  _imp_outfile[_imp_OutStream].lastchar = ch;
  if (ch == '\n' || ch == '\r' || ch == '\f') {
    _imp_outfile[_imp_OutStream].outpos = 0;
  } else if (ch == '\b') {
    _imp_outfile[_imp_OutStream].outpos -= 1;
  } else if (ch == '\t') {
    _imp_outfile[_imp_OutStream].outpos = (_imp_outfile[_imp_OutStream].outpos + 8) & (~7)  ;// at least on linux
  } else {
    _imp_outfile[_imp_OutStream].outpos += 1;
  }
}


// TO DO !!!!!

// Best Practices for va_list:  When intercepting variadic functions like vprintf,
// remember that va_list is a pointer-like object. If you need to read the arguments
// (e.g., to log them) and pass them to the real vprintf, you must first duplicate
// the argument list using va_copy and clean up with va_end to prevent memory corruption:

// https://stackoverflow.com/questions/10807310/platform-inconsistencies-with-vsprintf-and-va-list
// https://onlinedocs.microchip.com/oxy/GUID-07E1F2AB-C1A0-411C-966F-8738802C42B9-en-US-4/GUID-24E22499-EAEA-4FB2-A99D-739C4A28D618.html
// https://bumbershootsoft.wordpress.com/2022/07/24/custom-printf-wrapping-variadic-functions-in-c/

//va_list ap_copy;
//va_copy(ap_copy, ap);
// Now you can safely use ap_copy with va_arg() or similar,
// while ap goes to the original vprintf.
//va_end(ap_copy);


int _imp_printf(char *s, ...)
{
  /* Size the string by vfprint'ing it to /dev/null... */
  /* then malloc an appropriate area                   */
  char *APPROPRIATE_STRING;
  va_list ap;
  static FILE *nullfile = NULL;
  int string_length;

  if (nullfile == NULL) nullfile = fopen("/dev/null", "w");
  if (nullfile == NULL) {
    fprintf(stderr, "Major error - cannot open /dev/null\n");
    fflush(stderr);
    exit(1);
  }
  va_start(ap, s);
  string_length = vfprintf(nullfile, s, ap);
  va_end(ap);
  /* fclose(nullfile); */
  APPROPRIATE_STRING = malloc(string_length+1);
  va_start(ap, s);
  vsprintf(APPROPRIATE_STRING, s, ap);
  va_end(ap);
  int len = 0;
  s = APPROPRIATE_STRING;
  for (;;) {
    if (*s == '\0') break;
    _imp_putchar(*s++ /*, _imp_OUTFILE*/);
  }
  free(APPROPRIATE_STRING);
  return s-APPROPRIATE_STRING; // *should* be equal to string_length
}



int raw_getchar(FILE *f) { // TO DO: fscanf etc
  int ch, next;
  _imp_infile[_imp_InStream].lastchar = ch = fgetc(f);
  _imp_infile[_imp_InStream].nextchar = next = fgetc(f); if (next != EOF) ungetc(next, f);
  if (ch == EOF) return EOF; 
  if (ch == '\n' || ch == '\r') {
    _imp_infile[_imp_InStream].inpos = 0;
  } else {
    _imp_infile[_imp_InStream].inpos += 1;
  }
  return ch;
}

unsigned char *_imp_length(_imp_string *str) {
  return &str->length;
}

unsigned char *_imp_charno(_imp_string *str, int pos) {
  return &str->charno[pos];
}

_imp_string *_imp_strcat(_imp_string *left, _imp_string right) {
  /*TO DO*/
  int llen = *_imp_LENGTH(left);
  int rlen = *_imp_LENGTH(&right);
  int moved = 0;
  for (;;) {
    if (moved == rlen) break;
    moved += 1;
    left->charno[llen+moved] = right.charno[moved];
    *_imp_LENGTH(left) += 1;
  }
  return left;
}

// external not static inline in perms.h
int _imp_resolve(_imp_string s, _imp_string *left, _imp_string match, _imp_string *right) {
  char *matches;
  int matchstart;
  matches = (char *)memmem(s.cstr.s, s.length, match.cstr.s, match.length);
  if (matches) {
    matchstart = matches-s.cstr.s;
    if (left) {
      strncpy(left->cstr.s, s.cstr.s, matchstart);
      //left->cstr.s[matchstart] = '\0';
      left->length = matchstart;
    }
    if (right) {
      strncpy(right->cstr.s,
              s.cstr.s+matchstart+strlen(match.cstr.s),
              strlen(s.cstr.s+matchstart+strlen(match.cstr.s))+1);//<--needs length fix
      right->length = strlen(right->cstr.s);//<--needs length fix
    }
    return 1/*True*/;
  } else {
    // Did not contain match string.  Do not update left or right.
    return 0/*False*/;
  }
}

// this one is implemented as a static inline in perms.h

//_imp_string _imp_join(_imp_string left, _imp_string right) {
//  fprintf(stderr, "* _imp_join not yet implemented.\n");
//  exit(1);
//}

int _imp_strcmp(_imp_string left, _imp_string right) {
  int comp, i = 1;
  for (;;) {
    comp = right.charno[i] - left.charno[i];
    if (comp != 0) return comp;
    if (i == left.length && i == right.length) return comp;
    if (i == left.length) return -1;
    if (i == right.length) return 1;
    i += 1;
    if (i == 256) return 1;//ERROR!
  }
}

void _imp_readsymbol(int *P) {
  int ch;
  check_instream(__PRETTY_FUNCTION__);
  _imp_issue_prompt();
  //fprintf(stderr, "READSYMBOL at %p\n", P);
  ch = raw_getchar(_imp_INFILE);
  if (ch == EOF) _imp_signal(9, 2, _imp_InStream, strerror(errno));
  *P = ch;
}

void _imp_readch(int *P) {
  int ch;
  check_instream(__PRETTY_FUNCTION__);
  _imp_issue_prompt();
  ch = raw_getchar(_imp_INFILE);
  if (ch == EOF) _imp_signal(9, 2, _imp_InStream, strerror(errno));
  *P = ch;
}

int _imp_nextsymbol(void) {
  check_instream(__PRETTY_FUNCTION__);
  _imp_issue_prompt();
  int ch = raw_getchar(_imp_INFILE);
  if (ch == EOF) _imp_signal(9, 2, _imp_InStream, strerror(errno));
  ungetc(ch, _imp_INFILE);
  return ch;
}

int _imp_nextch(void) {
  check_instream(__PRETTY_FUNCTION__);
  _imp_issue_prompt();
  int ch = raw_getchar(_imp_INFILE);
  if (ch == EOF) _imp_signal(9, 2, _imp_InStream, strerror(errno));
  ungetc(ch, _imp_INFILE);
  return ch;
}

void _imp_skipsymbol(void) {
  int ch;
  check_instream(__PRETTY_FUNCTION__);
  _imp_issue_prompt();
  ch = raw_getchar(_imp_INFILE);
  if (ch == EOF) _imp_signal(9, 2, _imp_InStream, strerror(errno));
}
      
void _imp_printsymbol(char SYM) {
  if (_imp_OUTFILE == NULL) {
    fprintf(stderr, "* OUTFILE (stream %d) is NULL\n", _imp_OutStream);
    exit(1);
  }
  _imp_putchar(SYM /*, _imp_OUTFILE*/);
  if (_imp_OutStream == 0) fflush(_imp_OUTFILE);
}

void _imp_printch(char SYM) {
  if (_imp_OUTFILE == NULL) {
    fprintf(stderr, "* OUTFILE (stream %d) is NULL\n", _imp_OutStream);
    exit(1);
  }
  _imp_putchar(SYM /*, _imp_OUTFILE*/);
  if (_imp_OutStream == 0) fflush(_imp_OUTFILE);
}

void _imp_printstring(_imp_string S) {
  // _imp_printf("%*s", S.length, S.cstr.s);
  if (_imp_OUTFILE == NULL) { fprintf(stderr, "* OUTFILE (stream %d) is NULL\n", _imp_OutStream); exit(1); }
  for (int i = 1; i <= S.length; i++) _imp_putchar(S.charno[i] /*, _imp_OUTFILE*/);
  if (_imp_OutStream == 0) fflush(_imp_OUTFILE);
}

void _imp_write(int V, int P) {
  if (_imp_OUTFILE == NULL) {
    fprintf(stderr, "* OUTFILE (stream %d) is NULL\n", _imp_OutStream);
    exit(1);
  }
  _imp_printf(P < 0 ? "%*d" : " %*d", P, V);
  if (_imp_OutStream == 0) fflush(_imp_OUTFILE);
} // or swap.
// IMPORTANT: See https://gtoal.com/imp77/reference-manual/IO-LIBRARY-EXPERIMENTS.{imp,txt}

void _imp_readstring(_imp_string *S) {
  // quoted string
  check_instream(__PRETTY_FUNCTION__);
  _imp_issue_prompt();
  fprintf(stderr, "* READSTRING not implemented\n");
  exit(1);
}

void _imp_readtext(_imp_string *S, int DELIM) {
  check_instream(__PRETTY_FUNCTION__);
  _imp_issue_prompt();
  fprintf(stderr, "* READTEXT not implemented\n");
  exit(1);
}

_imp_string _imp_nextitem(void) {
  check_instream(__PRETTY_FUNCTION__);
  _imp_issue_prompt();
  return _imp_TOSTRING(_imp_nextsymbol());
}

void _imp_readline(_imp_string *S) {
  check_instream(__PRETTY_FUNCTION__);
  _imp_issue_prompt();
  S->length = 0;
  for (;;) {
    int ch;
    ch = raw_getchar(_imp_INFILE);
    if (ch == EOF) _imp_signal(9, 1, 0, "EM CHAR IN STMNT - DISASTER");
    if (ch == '\n') break;
    S->length += 1;
    S->charno[S->length] = ch&255;
  }
  S->charno[S->length+1] = '\0'; // strictly not correct but helpful anyway.
}

int _imp_instream(void) {
  check_instream(__PRETTY_FUNCTION__);
  return _imp_InStream;
}

int _imp_outstream(void) {
  return _imp_OutStream;
}

int _imp_inputstream(void) {
  check_instream(__PRETTY_FUNCTION__);
  return _imp_InStream;
}

int _imp_outputstream(void) {
  return _imp_OutStream;
}

_imp_string _imp_inputname(void) {
  check_instream(__PRETTY_FUNCTION__);
  return _imp_infile[_imp_InStream].fname;
}

_imp_string _imp_outputname(void) {
  return _imp_outfile[_imp_OutStream].fname;
}

_imp_string _imp_infilename(void) {
  check_instream(__PRETTY_FUNCTION__);
  return _imp_infile[_imp_InStream].fname;
}

_imp_string _imp_outfilename(void) {
  return _imp_outfile[_imp_OutStream].fname;
}

void _imp_selectinput(int N) {
  _imp_INFILE  = _imp_infile[_imp_InStream = N].f;
  check_instream(__PRETTY_FUNCTION__);
}

void _imp_selectoutput(int N) {
  _imp_OUTFILE = _imp_outfile[_imp_OutStream = N].f;
  if (_imp_OUTFILE == NULL) {
    // Need to signal!  Will fall back to stream 0 for now
    fprintf(stderr, "SELECT OUTPUT(%d): Stream is not open.\n", N);
    _imp_OUTFILE = _imp_outfile[_imp_OutStream = 0].f;
  }
}

void _imp_openinput(int N, _imp_string FD) {
  FILE *f;
  // Should we warn if there is already a stream open on this channel? (probably)
  // should openinput also do a select input on it?
//fprintf(stderr, "perms: openinput(%d,%s)\n", N, _imp_i2cstr(&FD));
  f = fopen(_imp_i2cstr(&FD), "r");
  if (f == NULL) {
    _imp_signal(9,3,N,_imp_i2cstr(&FD)/*strerror(errno)*/); // if signals not enabled, drop through
  }
  _imp_infile[N].f = f;
  _imp_infile[N].fname = FD;
  _imp_infile[N].streamno = N;
  _imp_infile[N].lastchar = '\n';
  _imp_infile[N].nextchar = -1;
  _imp_infile[N].inpos = 0;
}

void _imp_openbinaryinput(int N, _imp_string FD) {
  FILE *f;
//fprintf(stderr, "perms: openbinaryinput(%d,%s)\n", N, _imp_i2cstr(&FD));
  f = fopen(_imp_i2cstr(&FD), "rb");
  if (f == NULL) {
    _imp_signal(9,3,N,strerror(errno)); // if signals not enabled, drop through
  }
  _imp_infile[N].f = f;
  _imp_infile[N].fname = FD;
  _imp_infile[N].streamno = N;
  _imp_infile[N].lastchar = '\n';
  _imp_infile[N].nextchar = -1;
  _imp_infile[N].inpos = 0;
}

void _imp_openoutput(int N, _imp_string FD) {
  FILE *f;
//fprintf(stderr, "perms: openoutput(%d,%s)\n", N, _imp_i2cstr(&FD));
  f = fopen(_imp_i2cstr(&FD), "w");
  if (f == NULL) {
    _imp_signal(9,3,N,strerror(errno)); // if signals not enabled, drop through
  }
  _imp_outfile[N].f = f;
  _imp_outfile[N].fname = FD;
  _imp_outfile[N].streamno = N;
  _imp_outfile[N].lastchar = '\n';
  _imp_outfile[N].outpos = 0;
}

void _imp_openbinaryoutput(int N, _imp_string FD) {
  FILE *f;
//fprintf(stderr, "perms: openbinaryoutput(%d,%s)\n", N, _imp_i2cstr(&FD));
  f = fopen(_imp_i2cstr(&FD), "wb");
  if (f == NULL) {
    _imp_signal(9,3,N,strerror(errno)); // if signals not enabled, drop through
  }
  _imp_outfile[N].f = f;
  _imp_outfile[N].fname = FD;
  _imp_outfile[N].streamno = N;
  _imp_outfile[N].lastchar = '\n';
  _imp_outfile[N].outpos = 0;
}

void _imp_defineinput(int I, _imp_string SPEC) {
  // not an openinput I think but setting up the filename/stream correspondence for a later openinput
  fprintf(stderr, "* DEFINEINPUT not implemented\n");
  exit(1);
}

void _imp_defineoutput(int I, _imp_string SPEC) {
  // not an openoutput I think but setting up the filename/stream correspondence for a later openoutput
  fprintf(stderr, "* DEFINEOUTPUT not implemented\n");
  exit(1);
}

void _imp_closestream(int Stream) { /* EMAS call.  Ambiguous since it could be input or output. */
  int rc;

  // this call is problematic if the same stream number is open for both input and output.
  // In that case we assume input and try closing that, but if it wasn't we try output.

  if (_imp_infile[Stream].f != NULL) {
    rc = fclose(_imp_infile[Stream].f); // aka INFILE
    if (rc == 0) {
      if (Stream == _imp_InStream) {
        if (Stream != 0) {
          _imp_InStream = -1;
          _imp_infile[Stream].f = NULL;
         _imp_selectinput(0);
        }
      }
      return;
    } else _imp_signal(9, 2, Stream, strerror(errno));
  } else if (_imp_outfile[Stream].f != NULL) {
    // Wasn't input so we'll try output.  This is 'an expedient hack'.
    // And example of this call is in the EMAS "gammon.imp" program
    rc = fclose(_imp_outfile[Stream].f); // aka OUTFILE
    if (rc == 0) {
      if (Stream == _imp_OutStream) {
        if (Stream != 0) {
          _imp_OutStream = -1;
          _imp_outfile[Stream].f = NULL;
          _imp_selectoutput(0);
        }
      }
    } else _imp_signal(9, 2, Stream, strerror(errno));
  } else {
    // signal that stream <Stream> is not open and therefore cannot be closed
  }
}

void _imp_closeinput(void) {
  int rc;
  // TO DO: check validity of _imp_InStream
  check_instream(__PRETTY_FUNCTION__);
  rc = fclose(_imp_infile[_imp_InStream].f); // aka INFILE
  _imp_infile[_imp_InStream].f = _imp_INFILE = NULL;
  if (rc != 0) _imp_signal(9, 2, _imp_InStream, strerror(errno));
  // if signals not enabled, drop through
  _imp_InStream = -1;
}

void _imp_closeoutput(void) {
  int rc;
  // TO DO: check validity of _imp_OutStream
  rc = fclose(_imp_outfile[_imp_OutStream].f); // aka OUTFILE
  _imp_outfile[_imp_OutStream].f = _imp_OUTFILE = NULL;
  if (rc != 0) _imp_signal(9, 2, _imp_OutStream, strerror(errno));
  // if signals not enabled, drop through...
  _imp_OutStream = -1;
  // or should we default to stream 0 on closing any other stream?
  // While disallowing closing of stream 0?
  // also do OUTFILE = NULL; or OUTFILE = stderr; ?
}

void _imp_abandoninput(void) {
  // throw away any unread typeahead if interactive.  Not sure what if an ordinary file. probably just close?
  fprintf(stderr, "* ABANDONINPUT not implemented\n");
  exit(1);
} // ABANDON INPUT and OUTPUT not yet added to perms.h

void _imp_abandonoutput(void) {
  // discard any as-yet unprinted output buffer
  fprintf(stderr, "* ABANDONOUTPUT not implemented\n");
  exit(1);
}

void _imp_resetinput(void) {
  check_instream(__PRETTY_FUNCTION__);
  fseek(_imp_infile[_imp_InStream].f, 0L, SEEK_SET);
}
// *Is* there a RESET OUTPUT?

void _imp_inputposition(void) {    // ftell
  fprintf(stderr, "*** ERROR: Empty version of %s called!\n", __PRETTY_FUNCTION__);
}

void _imp_outputposition(void) {
  fprintf(stderr, "*** ERROR: Empty version of %s called!\n", __PRETTY_FUNCTION__);
}

void _imp_setinput(int P) {
  check_instream(__PRETTY_FUNCTION__);
  fseek (_imp_INFILE, P, SEEK_SET);
}
void _imp_setoutput(int P) {
  fseek (_imp_OUTFILE, P, SEEK_SET);
}

void _imp_positioninput(int P) {     // fseek
  check_instream(__PRETTY_FUNCTION__);
  _imp_setinput(P);
}
void _imp_positionoutput(int P) {
  _imp_setoutput(P);
}

void _imp_space(void) {
  if (_imp_OUTFILE == NULL) {
    fprintf(stderr, "* OUTFILE (stream %d) is NULL\n", _imp_OutStream);
    exit(1);
  }
  _imp_printf(" ");
  if (_imp_OutStream == 0) fflush(_imp_OUTFILE);
}

void _imp_spaces(int N) {
  if (_imp_OUTFILE == NULL) {
    fprintf(stderr, "* OUTFILE (stream %d) is NULL\n", _imp_OutStream);
    exit(1);
  }
  while (N --> 0) _imp_printf(" ");
  if (_imp_OutStream == 0) fflush(_imp_OUTFILE);
}

void _imp_newpage(void) {
  if (_imp_OUTFILE == NULL) {
    fprintf(stderr, "* OUTFILE (stream %d) is NULL\n", _imp_OutStream);
    exit(1);
  }
  _imp_printf("\f");
  if (_imp_OutStream == 0) fflush(_imp_OUTFILE);
}

void _imp_newline(void) {
  if (_imp_OUTFILE == NULL) {
    fprintf(stderr, "* OUTFILE (stream %d) is NULL\n", _imp_OutStream);
    exit(1);
  }
  _imp_printf("\n");
  if (_imp_OutStream == 0) fflush(_imp_OUTFILE);
}

void _imp_newlines(int N) {
  if (_imp_OUTFILE == NULL) {
    fprintf(stderr, "* OUTFILE (stream %d) is NULL\n", _imp_OutStream);
    exit(1);
  }
  while (N --> 0) _imp_printf("\n");
  if (_imp_OutStream == 0) fflush(_imp_OUTFILE);
}

void _imp_print(double R, int BEFORE, int AFTER) {
  // IMPORTANT: See https://gtoal.com/imp77/reference-manual/IO-LIBRARY-EXPERIMENTS.{imp,txt}
  if (_imp_OUTFILE == NULL) {
    fprintf(stderr, "* OUTFILE (stream %d) is NULL\n", _imp_OutStream);
    exit(1);
  }
  _imp_printf("%*.*f", BEFORE+AFTER, AFTER, R);
  if (_imp_OutStream == 0) fflush(_imp_OUTFILE);
}

void _imp_printfloating(double R, int BEFORE, int AFTER) {
  // TO DO          // IMPORTANT: See https://gtoal.com/imp77/reference-manual/IO-LIBRARY-EXPERIMENTS.{imp,txt}
  if (_imp_OUTFILE == NULL) {
    fprintf(stderr, "* OUTFILE (stream %d) is NULL\n", _imp_OutStream);
    exit(1);
  }
  _imp_printf("%*.*f", BEFORE+AFTER, AFTER, R);
  if (_imp_OutStream == 0) fflush(_imp_OUTFILE);
}

void _imp_printfl(double R, int PLACES) {
  // IMPORTANT: See https://gtoal.com/imp77/reference-manual/IO-LIBRARY-EXPERIMENTS.{imp,txt}
  if (_imp_OUTFILE == NULL) {
    fprintf(stderr, "* OUTFILE (stream %d) is NULL\n", _imp_OutStream);
    exit(1);
  }
  _imp_printf("%*f", PLACES, R);
  if (_imp_OutStream == 0) fflush(_imp_OUTFILE);
}

void _imp_printfhex(double R) {
  if (_imp_OUTFILE == NULL) {
    fprintf(stderr, "* OUTFILE (stream %d) is NULL\n", _imp_OutStream);
    exit(1);
  }
  _imp_printf("%A", R);
  if (_imp_OutStream == 0) fflush(_imp_OUTFILE);
}

_imp_string _imp_substring(_imp_string S, int FROM, int TO) {
    int GET;
    int PUT;
    _imp_string TEMP;
    if (FROM < 0) goto L_0002;
    if (FROM <= *_imp_LENGTH(&S)) goto L_0003;
  L_0002:
    _imp_signal(6, 2, FROM, "substring: from is past end of string");
  L_0003:
    if (TO < 0) goto L_0004;
    if (TO <= *_imp_LENGTH(&S)) goto L_0005;
  L_0004:
    _imp_signal(6, 2, TO, "substring: to is past end of string");
  L_0005:
    if (FROM <= TO) goto L_0006;
    _imp_signal(5, 3, 0, "substring inside-out");
  L_0006:
    *_imp_LENGTH(&TEMP) = (TO - FROM) + 1;
    PUT = 1;
    GET = FROM;
  L_0007:
    if (GET > TO) goto L_0008;
    *_imp_CHARNO(&TEMP, PUT) = *_imp_CHARNO(&S, GET);
    PUT = PUT + 1;
    GET = GET + 1;
    goto L_0007;
  L_0008:
    return TEMP;
    ;
}
_imp_string _imp_fromstring(_imp_string S, int FROM, int TO) { return _imp_substring(S, FROM, TO); }
_imp_string _imp_trim(_imp_string S, int MAX) {
  if (MAX >= 0) goto L_0002;
  _imp_signal(6, 2, MAX, "");
  L_0002:
  if ( /*%map*/ * _imp_LENGTH( &S) <= MAX) goto L_0003;
  /*%map*/ *_imp_LENGTH( &S) = MAX;
L_0003:
  return S;
}

_imp_string _imp_time(void) {
  char temp[256];
  time_t rawtime;
  struct tm *ptm;
  
  if ((rawtime=time(NULL)) == -1)        { _imp_signal(10,0,0, "time() failed"); exit(EXIT_FAILURE); }
  if ((ptm=localtime(&rawtime)) == NULL) { _imp_signal(10,0,0, "localtime() failed"); exit(EXIT_FAILURE); }
  strftime(temp, 255, "%T", ptm);
  return _imp_c2istr(temp);
}

_imp_string _imp_date(void) {
  char temp[256];
  time_t rawtime;
  struct tm *ptm;

  if ((rawtime=time(NULL)) == -1)        { _imp_signal(10,0,0, "time() failed"); exit(EXIT_FAILURE); }
  if ((ptm=localtime(&rawtime)) == NULL) { _imp_signal(10,0,0, "localtime() failed"); exit(EXIT_FAILURE); }
  strftime(temp, 255, "%D", ptm);
  return _imp_c2istr(temp);
}

// Currently returning microseconds. I think the original was miliseconds.
// This is just temporary to get me through the Dhrystone benchmark...
int _imp_cputime(void) {
  return (int)(((long long int)clock() * 1000000LL)/(long long int)CLOCKS_PER_SEC);
}

_imp_string _imp_cliparam(void) {
  _imp_string tmp;
  *_imp_LENGTH(&tmp) = 0;
  if (_imp_ARGC() >= 2) {
    _imp_strcpy(&tmp, _imp_ARGV(1));
  }
  for (int arg = 2; arg < _imp_ARGC(); arg++) {
    _imp_string each;
    _imp_strcpy(&each, _imp_ARGV(arg));
    if (*_imp_LENGTH(&tmp)+1+*_imp_LENGTH(&each) < 255) {
      _imp_strcat(&tmp, _imp_str_literal(" "));
      _imp_strcat(&tmp, each);
    } else break;
  }
  return tmp;
}

_imp_string _imp_eventmessage(void) {
  //return _imp_c2istr(_imp_event.MESSAGE);
  return _imp_event.MESSAGE;
}

_imp_string _imp_itos(int V, int P) {
  _imp_string temp;
  if (P >= 0) {
    sprintf(temp.cstr.s, " %*d", P, V);    // adds a single space
  } else {
    sprintf(temp.cstr.s, "%*d", -P, V);
  }
  temp.length = strlen(temp.cstr.s);
  return temp;
}

