/*$Header: /a/rathlin/disk/src/master/edml/EDML4/fam.src/UPTODATE/RCS/system.c,v 5.1 91/09/05 18:25:04 edml Exp $*/

#include <stdio.h>
#ifdef __STDC__
#  include <time.h>    /* for CLK_TCK, clock(), time() etc */
#endif

#ifdef CLOCKS_PER_SEC
#  ifndef CLK_TCK
#    define CLK_TCK CLOCKS_PER_SEC
#  endif
#endif

/* This order is useful for some unix's - eg system 5 - but tidy one day */
/* even more in need of tidying due to code move from system.c */
/*
 * Need `u3b' defined for generality on 3B range.  Already fixed in system.h
 * (qv) but system.h isn't in here, so do it again...
 *
 * Sam Nelson, Stirling Univ., 20.1.88.
 */
#if defined(u3b2) || defined(u3b5) || defined(u3b15)
#  define u3b
#endif
#ifdef unix  /* isn't unix standard??? */
#   ifdef orion
#   else	/* not orion */
#      include <ctype.h>
#      ifdef u3b
#        include <sys/types.h>
#      endif
#   endif	/* orion */
#   ifdef u3b
      /* sys/types already included */
#   else
#     include <sys/types.h>
#   endif	/* u3b */
#   include <sys/stat.h>
#   include <ctype.h>
#   ifdef u3b
#     include <sys/param.h>
#     include <sys/times.h>
#   else	/* not u3b */
#   ifdef hpux
#     include <time.h>
#     include <sys/times.h>
#     include <sys/resource.h>
#   else	/* not hpux */
#     include <sys/time.h>
#     ifdef orion
#     else
#        include <sys/resource.h>
#     endif	/* orion */
#   endif	/* hpux */
#   endif	/* u3b */
#endif		/* unix */
#include "globdefs.h"
#include "structs.h"
#include "globvars.h"
/* native code include file */
#include "ncglobs.h"

#ifdef acorn32016  /* no signal.h the last time AM looked */
#   define time(x) 0
#else
#   ifdef transputer
#      define time(x) 0
#   else
#      include <signal.h>
#   endif
#endif

#ifdef BSD
#   ifdef selport
#      define NO_VADVISE
#   else
#      ifdef apollo
#         define NO_VADVISE
#      endif
#   endif
#else
#   define NO_VADVISE
#endif

#ifndef NO_VADVISE
#  include <sys/vadvise.h>
#  include <fcntl.h>
#  include <errno.h>
#else
#  define vadvise(x)   /* do nothing, not even expand argument */
#endif

pagerand() { vadvise(VA_ANOM); }
pageseql() { vadvise(VA_SEQL); }
pagenorm() { vadvise(VA_NORM); }

#ifdef hpux
   int ffs(i)
   int i;
   {
      int bit;
      bit = 1;
      if (i == 0) {
	 return -1;
      } else  {
	 while (i&1 == 0) {
	    i = i>>1;
	    bit++;
	 };
         return bit;
      };
   }
#endif

getstamp(hid,pid,tim)
int *hid, *pid, *tim;
{
/*
 * The following needs some tidying up---gettimeofday() and gethostid() are
 * BSD-specific (or thereabouts) calls...
 *
 * Sam Nelson, Stirling Univ., 20.1.88.
 */
#ifdef unix
 
#  ifdef BSD
     struct timeval tv, tz;
#  endif
#  ifdef hpux
     struct timeval tv, tz;
#  endif

     *tim=0; *hid=0;    /* Catch-all for the others... */

#  ifdef BSD
     gettimeofday(&tv,&tz);
     *tim=tv.tv_sec; *hid=gethostid();
#  endif BSD
#  ifdef hpux
     gettimeofday(&tv,&tz);
     *tim=tv.tv_sec; *hid=0;
#  endif
#  ifdef u3b
     *tim=(int)time((long *)0); *hid=0;
#  endif

   *pid = getpid();     /* Can't be anyone who hasn't got getpid()...? */
#else
   *tim = 0; *hid = 0; *pid = 0;
#endif
}

int getfilestamp(hid,pid,tim,file)
int *hid, *pid, *tim; char *file;
{
#ifdef unix
   struct stat buf; int res;
   res = stat(file,&buf);
   if (res == 0) {
      *tim = buf.st_mtime;
      *pid = getpid();
/*
 * gethostid() is a BSD-specific system call or close to it.
 * Some messing about here to generalise from `hpux' stuff...
 *
 * Sam Nelson, Stirling Univ., 20.1.88.
 */
#  ifdef BSD
      *hid=gethostid();
#  else
      *hid=0;
#  endif
      }
   return(res);
#else
   return(-1);
#endif
}


/* note that the following function is only used on argv[0].
   even so, on unix (e.g. bsd4.2) finding out the file containing
   the image from argv[0] is a difficult task (set path=(...)).
   If the ANSI C predefined variable __DATE__ is defined then use
   the date of compilation of this file.
*/
char *filecreationdate(imagename)
char *imagename;
{   static char date[80];
#ifdef __DATE__
    return __DATE__;
#else		/* not __DATE__ */
#ifdef vms_not_ready
    struct FAB  fblock;
    struct XABDAT   xblock;
    short   datelen;
    struct dsc $descriptor_s creationdate;

    fblock = cc $ rms_fab;
    fblock.fab $ l_fna = imagename;
    fblock.fab $ b_fns = strlen (imagename);
    fblock.fab $ l_xab = &xblock;
    xblock = cc $ rms_xabdat;
    sys $ open (&fblock);
    creationdate.dsc $ w_length = strlen (date);
    creationdate.dsc $ a_pointer = date;
    creationdate.dsc $ b_class = DSC $ K_CLASS_S;
    creationdate.dsc $ b_dtype = DSC $ K_DTYPE_T;
    sys $ asctim (&datelen, &creationdate, &xblock.xab $ q_cdt, 0);
    date[datelen - 6] = 0;	/* not interested in seconds and tenths */
    sys $ close (&fblock);
    return (date);

#else		/* not vms_not_ready */
#ifdef unix
    struct stat statbuf;
    if (stat(imagename, &statbuf) != 0) return "<unknown>";
    strcpy(date, (char *) ctime (&(statbuf.st_mtime)));
    date[24] = '\0';
    return(date);

#else		/* not unix */
    strcpy(date, "<unknown>");
    return(date);
#endif		/* unix */
#endif		/* vms_not_ready */
#endif		/* __DATE__ */
}

int TodInS()
{
#ifdef DOS386
  time_t SecsSince1970 = 0;
#else
  int SecsSince1970 = 0;
#endif
  return time(&SecsSince1970); }

int TimeInMS()
{
#ifdef __STDC__
  return (CLK_TCK >= 1000 && CLK_TCK % 1000 == 0) ?
         clock() / (CLK_TCK/1000) : (clock() * 1000) / CLK_TCK;
#else
#ifdef os370
   return(clock()/1000);
#else
#ifdef unix
#  ifdef orion
     struct tms the_time;
     times(&the_time);
     return (the_time.tms_utime * 1000 / 60);
#  else
#  ifdef u3b
     struct tms the_time;
     times(&the_time);
     /*
      * Stuff based on sys/times.h around here was only counting parent
      * process' user time, which is going to be way off, especially with
      * this `execute' facility in these days.  Plus, with the clock tick
      * defined in sys/param.h on every machine ever seen, it seems a pity
      * not to use it...
      *
      * Sam Nelson, Stirling Univ., 20.1.88.
      */
     return ((the_time.tms_stime
	      +the_time.tms_utime
	      +the_time.tms_cstime
	      +the_time.tms_cutime) * (1000/HZ));
#  else
#  ifdef hpux
     struct tms the_time;
     times(&the_time);
     return (the_time.tms_utime * 10);
#  else
     struct rusage usage;
     getrusage(RUSAGE_SELF,&usage);
     return((usage.ru_utime.tv_sec * 1000) + (usage.ru_utime.tv_usec / 1000) +
            (usage.ru_stime.tv_sec * 1000) + (usage.ru_stime.tv_usec / 1000));
#  endif
#  endif
#  endif
#else
   return 0;
#endif
#endif
#endif
}

int DateInUnix()
{
#ifdef DOS386
  time_t SecsSince1970 = 0;
#else
  int SecsSince1970 = 0;
#endif
  time(&SecsSince1970);
  return SecsSince1970;
}


char *datestring(date)
int date;
{ static char ans[] = "01-Jan-1970 00:00:00";
  static char monnam[] =
    "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
  static char monlen[] = {31,28,31,30,31,30,31,31,30,31,30,31};
  int mon, year, hh, mm, ss, t;
  if (date==0) { strcpy(ans,"<unknown>"); return ans; }
  ss = date % 60; date = date/60;
  mm = date % 60; date = date/60;
  hh = date % 24; date = date/24;
  for (year=1970; date >= (t = ((year&3)==0 ? 366:365)); year++, date -= t);
  for (mon=0; date >= (t = ((year&3)==0 && mon==1) ? 29 : monlen[mon]);
              mon++, date -= t);
  sprintf(ans, "%d%d-%s-%d %d%d:%d%d:%d%d",
               (date+1)/10, (date+1)%10, monnam+mon*4, year,
               hh/10,hh%10,mm/10,mm%10,ss/10,ss%10
         );
  return ans;
}

char *verstring(ver)
int ver;
{ static char ans[] = "xx0.0xx";
  sprintf(ans, "%d.%d", (ver&0xff00)>>8, (ver&255));
  return ans;
}


/*
 * SIGVTALRM stuff has to be BSD-specific, surely?
 * Changed `unix' to `BSD'...
 *
 * Sam Nelson, Stirling Univ., 20.1.88.
 */
#ifdef BSD
#   ifndef NOALARMS
#      define TIMEOUTS
#   endif
#endif

#ifdef TIMEOUTS
static ProcTimeOut()
{ IntFlag = -1; TimeOut = 1; }

SetupTimeOut()
{ struct itimerval val, oval;
  if (TimeSlice != 0) {
     val.it_interval.tv_sec = 0; val.it_interval.tv_usec = 0;
     val.it_value.tv_sec = 0; val.it_value.tv_usec = 1000000/TimeSlice;
     setitimer(ITIMER_VIRTUAL,&val,&oval);
     signal(SIGVTALRM, ProcTimeOut); }
}
#else
SetupTimeOut() {}
#endif


#ifdef acorn32016  /* no signal.h the last time AM looked */
SetupIntHandler() {}
SetupFloatHandler() {}

#else
#ifdef transputer
SetupIntHandler() {}
SetupFloatHandler() {}
#else

int IntTime = 0;
int NumQuickInts = 0;

#ifdef RISC_OS
static void FloatHandler( int sig )
{
  signal( SIGFPE, FloatHandler );
  IntFlag = -1;
  FloatExc = 1;

}

#else
static void FloatHandler(int dummy)
{
  signal(SIGFPE, SIG_IGN);
  IntFlag = -1; FloatExc = 1;
}
#endif

#ifdef RISC_OS
static void IntHandler( int sig )
{ int CurrTime;
   IntFlag = -7;
   signal(SIGINT, IntHandler);
}

#else
static void IntHandler(int dummy)
{ int CurrTime;
  CurrTime = TodInS();
  if ((CurrTime - IntTime) > 5) {
     IntTime = CurrTime; NumQuickInts = 0; IntFlag = -1;
     signal(SIGINT, IntHandler); }
  else {
     if (NumQuickInts > 2) {
        fprintf(stderr, "Cancelling interrupt handler\n");
        signal(SIGINT, SIG_DFL); }
     else {
        NumQuickInts++; IntFlag = -1; signal(SIGINT, IntHandler); }
     }
}
#endif

SetupFloatHandler()
{
#ifdef RISC_OS 
    /* Ignore FP exceptions in RISC_OS as the moronic C library only
       allows you to longjmp out of them. :-( */
    signal( SIGFPE, SIG_IGN );
#else
    signal( SIGFPE, FloatHandler);
#endif
}

SetupIntHandler()
{ 
  IntTime = 0; NumQuickInts = 0;
  signal(SIGINT, IntHandler);
#ifdef selport
  { int i = 1; traper_(&i); }
#endif
  SetupFloatHandler();
#ifdef RISC_OS
  FloatExc = 0;
  IntFlag = -1;
#endif
  SetupTimeOut();
}
#endif   /* transputer */
#endif   /* acorn32016 */

#ifdef apm
#define CAN_TEST_INPUT 1
InputPossibleOn(fp)
FILE *fp;
{ return (((CurrentProcess->Next == CurrentProcess) &&
           (CurrentState->SuspendedProcesses == 0)) ||
          ipend() > 0);
}
SomeInputPossible(n)
int *n;
{ *n = -1;
  return (((CurrentProcess->Next == CurrentProcess) &&
           (CurrentState->SuspendedProcesses == 0)) ||
          ipend() > 0);
}
#endif

#ifdef vms
#define CAN_TEST_INPUT 1
#define ipend() 1   /* YUKKO */
InputPossibleOn(fp)
FILE *fp;
{ return (((CurrentProcess->Next == CurrentProcess) &&
           (CurrentState->SuspendedProcesses == 0)) ||
          ipend() > 0);
}
SomeInputPossible(n)
int *n;
{ *n = -1;
  return (((CurrentProcess->Next == CurrentProcess) &&
           (CurrentState->SuspendedProcesses == 0)) ||
          ipend() > 0);
}

#endif

/*
 * select() is a BSD-specific system call---isn't it?
 * Changed `unix' to `BSD'...
 *
 * Sam Nelson, Stirling Univ., 20.1.88.
 */
#ifdef BSD
#define CAN_TEST_INPUT 1
InputPossibleOn(fp)
FILE *fp;
{ int devtty = (1 << fileno(fp));
  struct timeval zerotim;   zerotim.tv_sec=0; zerotim.tv_usec=0;
  return (((CurrentProcess->Next == CurrentProcess) &&
           (CurrentState->SuspendedProcesses == 0)) ||
          select(32,&devtty,0,0,&zerotim) == 1);
}

SomeInputPossible(which)
int *which;
{ int devtty=0; int nfds; Process *p;
  struct timeval zerotim;   zerotim.tv_sec=0; zerotim.tv_usec=0;

  if ((p = CurrentState->SuspendedProcesses) != 0) {
     while (p != 0) { devtty = devtty | (1 << p->Flags);
                      p = p->Next; }
     nfds = select(32,&devtty,0,0,&zerotim);
     if (nfds > 0) { *which = ffs(nfds)-1; }
     else { *which = -1;} }
  else { nfds = 0; *which = -1; }

  return (nfds > 0);
}
#endif

#ifndef CAN_TEST_INPUT
InputPossibleOn(fp) FILE *fp; {return 1;}
SomeInputPossible(n) int *n; {*n = -1; return 1;}
#endif


#ifdef DEBUGinterp
extern int SnapShot();

SetUpSampler()
{
#ifdef unix
#  ifdef vax
   struct itimerval tim;
   signal(SIGVTALRM,SnapShot);
   tim.it_interval.tv_sec = 0; tim.it_interval.tv_usec = 1000;
   tim.it_value.tv_sec = 0; tim.it_value.tv_usec = 1000;
   setitimer(ITIMER_VIRTUAL,&tim,0);
#  endif
#endif
}
#endif /* DEBUGinterp */

#ifdef BSD
#ifdef PROFILER
extern int DynSnapShot();

SetUpDynSampler()
{
   struct itimerval tim;
   signal(SIGPROF,DynSnapShot);
   tim.it_interval.tv_sec = 0; tim.it_interval.tv_usec = 200;
   tim.it_value.tv_sec = 0; tim.it_value.tv_usec = 200;
   setitimer(ITIMER_PROF,&tim,0);

}
#endif
#endif

#ifdef ebcdic
int foutstring(str,len)
char *str; int len;
{ register int i;
  for (i=0; i<len; i++) putchar(hostchar(str[i]));
  return len;
}
#else
int foutstring(char *str, int len)
{ /* On an ascii machine foutstring is notionally fwrite to stdout. */
  int i;
  for (i = 0; i < len; i++) ml_putchar(str[i]);
  return len;
}
#endif

#ifdef ARM
#  ifdef RISC_OS
#    define memcmp_supplied 1
#  endif
#endif


#ifdef vax
#  ifdef unix
#     define memcmp_supplied 1
      int memcmp(a,b,n)
      register char a[], b[];
      register int n;
      {
         asm("cmpc3	r9,(r10),(r11)");
         asm("jneq	L10");
         asm("ret");
         asm("L10:subl3	(r1),(r3),r0");
         asm("cvtbl	r0, r0");
         asm("ret");
      }
#   endif
#endif

#ifndef memcmp_supplied
   int memcmp(a,b,n)
   register char a[], b[];
   register int n;
   {  register int i;
      for (i=0; i<n; i++) if (a[i] != b[i]) return (a[i]-b[i]);
      return 0;
   }
#endif

#ifdef os370
char *getenv(s)
char *s;
{ return 0;  /* not available */
}
#endif

#ifdef transputer
extern int errno;

perror(s)
   char *s;
{
   printf("%s: errno is %d\n", s, errno);
}
#endif
