/* Release 3.00 */

/*-------------------------------------------------------------------------*
*                                                                          *
*  clib2.c - common miscellaneous library functions.    		   *
*                                                                          *
*  COPYRIGHT (C) 1989 Jensen and Partners. All Rights Reserved             *
*  Written by Simon Knight.                                                *
*                                                                          *
*  COPYRIGHT (C) 1991 Psion PLC. All Rights Reserved                       *
*  Modified by NSM.                                                        *
*                                                                          *
*--------------------------------------------------------------------------*/

#define _USE_TIME
#include <clib.h>
#include <float.h>

_STATIC void _qsort(char *buf, size_t num, size_t width,
	            int (* compare)(const void *e1, const void *e2),
	            char *_partition);

void _cvt(LONGDOUBLE x, int n, int *dec, int *sign, char * buf) {
  unsigned char bcd[10];
  LONGDOUBLE zero = 0;
  LONGDOUBLE norm;
  int i, k, kp, xtra;

  if ( x < zero ) {
    x = -x;
    *sign = 1;
  } else {
    *sign = 0;
  }

  if ( x == zero )
    k = -10000;
  else {
    k = 1 + (int) FLOORL( LOG10L(x) + 0.001 );
  }

  xtra=0;
  while (1) {
    norm = x;
    if ( k != -10000 ) {
      kp = 18 - k;
      if(kp > LDBL_MAX_10_EXP-1) {
        xtra = kp - (LDBL_MAX_10_EXP-1);
        kp = LDBL_MAX_10_EXP-1;
      }
      norm *= _pow_int(10,kp);
      if(xtra)
        norm*=_pow_int(10,xtra);;
    }

    _bcd( norm, (char  *)bcd );
    if ( k == -10000 ) {
      k = 1;
      break;
    }
    if ( ( bcd[8] >> 4 ) != 0 )
      break;
    k -= 1;
  }

  *dec = k;
  for (i=8; n>1; n-=2){
    unsigned tmp = bcd[i];
    i -= 1;
    *((int*)buf) = ( '0' + '0'*256 ) + ((tmp & 15)<<8) + (tmp >> 4);
    buf+=2;
  }
  if ( n ) {
    unsigned tmp = bcd[i];
    *buf = '0' + (tmp >> 4);
    ++buf;
  }
  *buf = '\0';
}


_STATIC void _insert_str(char *buf, char c) {

  movmem(buf, &buf[1], strlen(buf)+1);
  buf[0] = c;
}

void _cvt_round(int n, int *dec, int fc) {
  int prec, l;

  if(n < 0)
    n=-n;
  if(fc)
    n+=*dec;
  else if(n == 0) {
    _cvt_buf[0]='\0';
    return;
  }
  if(n > 63)
    n=63;
  l = strlen(_cvt_buf);
  if(l > n) {
    prec = n;
    if(_cvt_buf[prec] >= '5') {
      do {
        _cvt_buf[prec--] = '0';
        if(prec < 0) {
          _insert_str(_cvt_buf, '1');
          ++(*dec);
          ++n;
          break;
        }
        ++_cvt_buf[prec];
      } while(_cvt_buf[prec] > '9');
    }
    _cvt_buf[n] = '\0';
  }
  else {
    while(l < n) {
      _cvt_buf[l++]='0';
    }
    _cvt_buf[l] = '\0';
  }
}

double  pow10(int p)
{
    return(pow(10.0, (double) p));
}

double modf(double x, double *y){
  double tmp = _round((LONGDOUBLE) x);

  *y = tmp;
  return x - tmp;
}

long double modfl(long double x, long double *y){

  long double tmp = _round(x);
  *y = tmp;
  return x - tmp;
}

char * ecvt(double x, int n, int *dec, int *sign ) {

  _cvt((LONGDOUBLE)x,18,dec,sign,_cvt_buf);
  _cvt_round(n, dec, 0);
  return _cvt_buf;
}

char * fcvt(double x, int n, int *dec, int *sign ) {

  _cvt((LONGDOUBLE) x,18,dec,sign,_cvt_buf);
  _cvt_round(n, dec, 1);
  return _cvt_buf;
}

/*-------------------------------------------------------------------------*
*  toupper and tolower - case conversion functions.			   *
*--------------------------------------------------------------------------*/

int toupper(int  ch)
{
    if(_ctype[(ch)+1] & _IS_LOW)
	ch-= 0x20;
    return(ch);
}

int tolower(int  ch)
{
    if(_ctype[(ch)+1] & _IS_UPP)
	ch+=0x20;
    return(ch);
}

int isalnum(int c)
{
    return(_ctype[(c)+1] & (_IS_DIG | _IS_UPP | _IS_LOW));
}

int  isalpha(int c)
{
    return(_ctype[(c)+1] & (_IS_UPP | _IS_LOW));
}

int  isascii(int c)	
{
    return((unsigned)((c)) < 0x81);
}

int  iscntrl(int c)
{
    return(_ctype[(c)+1] & _IS_CTL);
}

int  isdigit(int c)
{
    return(_ctype[(c)+1] & _IS_DIG);
}

int  isgraph(int c)
{
    return((c) >= 0x21 && (c) <= 0x7e);
}

int  islower(int c)
{
    return(_ctype[(c)+1] & _IS_LOW);
}

int  isprint(int c)
{
    return((c) >= 0x20 && (c) <= 0x7e);
}

int  ispunct(int c)
{
    return(_ctype[(c)+1] & _IS_PUN);
}

int  isspace(int c)
{
    return(_ctype[(c)+1] & _IS_SP);
}

int  isupper(int c)
{
    return(_ctype[(c)+1] & _IS_UPP);
}

int  isxdigit(int c)
{
    return(_ctype[(c)+1] & (_IS_DIG | _IS_HEX));
}

int  _toupper(int c)
{
    return(c + 'A' - 'a');
}

int  _tolower(int c)
{
    return(c + 'a' - 'A');
}

int  toascii(int c)	
{
    return(c & 0x7f);
}

/*-------------------------------------------------------------------------*
*  misc math functions							   *
*--------------------------------------------------------------------------*/

extern double HUGE;		   /* overflow return value */
double _ten16 = 10.0e16;

/*-------------------------------------------------------------------------*
*    abs and labs - return absolute value of integer.			   *
*--------------------------------------------------------------------------*/

int  abs(int num)
{
    if(num < 0 )
	return(-num);
    return(num);
}

long labs(long num)
{
    if(num < 0 )
	return(-num);
    return(num);
}

double   poly(double x, int degree, double coeffs[])
{
    double result;

    result=coeffs[degree];
    while(--degree >= 0)
    {
        result=x*result+coeffs[degree];
    }
    return(result);
}

long double   polyl(long double x, int degree, long double coeffs[])
{
    long double result;

    result=coeffs[degree];
    while(--degree >= 0)
    {
        result=x*result+coeffs[degree];
    }
    return(result);
}

/*-------------------------------------------------------------------------*
*    atoul -  converts string to unsigned long integer			   *
*--------------------------------------------------------------------------*/

unsigned long atoul(const char *s)
{
    unsigned long num, c;
    int n;

    n=0;
    num=0;
    while(isspace(s[n++]));		       /* skip leading white-space */
    --n;
    while(isdigit((unsigned) (c=s[n++])))      /* total digits	*/
    {
	num*=10;
	num+=c-'0';
    }
    return(num);
}

/*-------------------------------------------------------------------------*
*    atol - converts string to signed long integer.			   *
*--------------------------------------------------------------------------*/

#define MINUS 1

long atol(const char *s)
{
    long num;
    int n, c, sign;

    n=0;
    num=0;
    sign=0;
    while(isspace(s[n++]));		      /* skip leading white-space */
    --n;
    switch(s[n])			      /* get sign */
    {
	case '-':
	    sign=MINUS;
	case '+':
	    ++n;
	    break;
	default:
	    break;
    }
    while(isdigit(c=s[n++]))		      /* total digits */
    {
	num*=10;
	num+=c-'0';
    }
    if(sign == MINUS)			      /* set sign */
	num*=-1;
    return(num);
}

/*-------------------------------------------------------------------------*
*    atoi - converts string to signed integer.				   *
*--------------------------------------------------------------------------*/

int atoi(const	char *s)
{
    int num;
    int n, c, sign;

    n=0;
    num=0;
    sign=0;
    while(isspace(s[n++]));		      /* skip white-space  */
    --n;
    switch(s[n])			      /* get sign */
    {
	case '-':
	    sign=MINUS;
	case '+':
	    ++n;
	    break;
	default:
	    break;
    }
    while(isdigit(c=s[n++]))		      /* total digits */
    {
	num*=10;
	num+=c-'0';
    }
    if(sign == MINUS)			      /* set sign */
	num*=-1;
    return(num);
}

/*-------------------------------------------------------------------------*
*    atof - convert string to double precision real.			   *
*--------------------------------------------------------------------------*/

double atof(const char *s)
{
    int discard;

    return(_atof(s, 0, &discard));	  /*_atof does the work */
}

long double atofl(const char *s)
{
    int discard;

    return(_atof(s, 0, &discard));	  /*_atof does the work */
}

/*-------------------------------------------------------------------------*
*    strtod - convert string to double and store last character scanned.   *
*--------------------------------------------------------------------------*/

double strtod(const char *s, char **eptr)
{
    int pos;
    double ret;

    ret=(double) _atof(s, _STD, &pos);	       /* _atof does the work */
    if(pos <= 0)
    {
        if(pos == -1)
            errno=ERANGE;
        if(eptr)
            *eptr=(char *) s;
    }
    else if(eptr)
        *eptr= (char *) &s[pos];      /* store address of last char scanned */
    return(ret);
}

long double strtodl(const char *s, char **eptr)
{
    int pos;
    long double ret;

    ret= _atof(s, _STD, &pos);	       /* _atof does the work */
    if(pos <= 0)
    {
        if(pos == -1)
            errno=ERANGE;
        if(eptr)
            *eptr=(char *) s;
    }
    else if(eptr)
        *eptr= (char *) &s[pos];      /* store address of last char scanned */
    return(ret);
}

double _atof(const char *s, int flag, int *pos)
{
    char mbuf[18];			  /* converts string to unpacked BCD */
    int  n, p, point;			  /* then calls _acvt to convert to  */
    int  c, count, places;		  /* double			     */
    int  exp;
    int  last_exp;
    double ret;

    n=0;
    p=0;
    exp=0;
    count=18;
    point=0;
    places=0;
    last_exp=0;
    while(isspace(s[n]))		   /* skip leading white-space */
    {
	++n;
    }
    if((c=s[n]) == '+') 		   /* get sign of mantissa */
	++n;
    else if(c == '-')
    {
	++n;
	flag|=_MNEG;
    }
    while(s[n] == '0')			   /* skip leading zeros */
    {
	++n;
    }
    if(s[n] == DEC_POINT)
    {
	++n;				   /* number less than 10e0 */
	while(s[n] == '0')
	{
	    ++n;
	    --point;
	}
	while(isdigit(c=s[n]))		   /* store digits to precsion */
	{
	    if(count-- == 0)
		break;
	    mbuf[p++]= (char) c;
	    ++n;
	    ++places;
	}
	point-=places;			   /* adjust for BCD */
    }
    else if(!isdigit(s[n]))
    {
        *pos=0;
        return(0);
    }
    else
    {
	while(isdigit(c=s[n]))		   /* number greater than 10e0 */
	{
	    if(count-- > 0)
		mbuf[p++]= (char) c;	   /* store digits to precision */
	    ++n;
	    ++places;
	}
	if(c == '.')
	{
	    point=p;
	    ++n;
	    while(isdigit(c=s[n]))
	    {
		if(count-- > 0)
		    mbuf[p++]= (char) c;
		++n;
	    }
	    point-=p;			   /* adjust exponent for decimal point */
	}
	else if(places > 18)
	    point=places-18;		   /* adjust exponent for BCD */
    }
    if(((c=tolower(c)) == 'd') || (c == 'e'))
    {
	++n;
	if((c=s[n]) == '+')		   /* get sign of exponent */
	    ++n;
	else if(c == '-')
	{
	    ++n;
	    flag|=_ENEG;
	}
	while(isdigit(c=s[n++]))
	{
	    exp*=10;
	    exp+=c - '0';
	    if(exp < last_exp)
	    {
	        exp=1000;
	        break;
	    }
	    last_exp=exp;
	}
	if(flag&_ENEG)
	    exp*= -1;
	--n;
    }
      exp+=point; 			  /* calculate total exponent */
    if(exp > 308)
    {
        ret=HUGE_VAL;
        if(flag&_MNEG)
            ret*= -1;
        *pos=-1;
        return(ret);
    }
    else if(exp < -308)
    {
        *pos=-1;
        return(0);
    }
    *pos=n;
    ret=_acvt(flag, mbuf, exp, p);	  /* convert from BCD to double */
    return ret;
}

/*-------------------------------------------------------------------------*
*    strtol - converts string to signed long and stores address of	   *
*    last character scanned.						   *
*--------------------------------------------------------------------------*/

long strtol(const char *s, char **eptr, int base)
{
    long num, last_val;
    int n, c, flag, scanned;

    n=0;
    num=0;
    last_val=0;
    flag=0;
    scanned=0;
    while(isspace(s[n]))	      /* skip white-space */
    {
	++n;
    }
    switch(s[n])		      /* get sign */
    {
	case '-':
	    flag|=_NEG;
	case '+':
	    ++n;
	    break;
    }
    if(s[n] == '0')			/* check for hex or octal prefixes */
    {
	++n;
	if(tolower(s[n]) ==  'x')
	{
	    if((base == 16) || (base == 0))
	    {
	        flag|=_HEX;
                ++n;
            }
            else
                return(0);
	}
	else if((base == 0) || (base == 8))
	    flag|=_OCT;
	else
            scanned=1;
    }
    if((base > 36) || (base < 2))
       base=0;
    if(base == 0)		       /* if base is 0 use prefix */
    {
	if(flag&_HEX)
	    base=16;
	else if(flag&_OCT)
	    base=8;
	else
	    base=10;
    }
    while(1)
    {
	c=s[n];
	if(!isdigit(c))
	    c=tolower(c)-'a'+10;	/* convert to digit and total */
	else
	    c-='0';
	if((c >= base) || (c < 0))     /* exit if not in radix */
	    break;
        scanned=1;
	num*= (long) base;
	num+=c;
	if(num < last_val)
	{
	    errno=ERANGE;
	    if(flag&_NEG)
	        return(LONG_MIN);
	    return(LONG_MAX);
	}
	last_val=num;
	++n;

    }
    if(!scanned)
    {
        *eptr= (char *) s;
        return(0);
    }
    if(flag&_NEG)			/* set sign */
	num*=-1;
    if(eptr != NULL)	        /* store address of last character scanned */
	*eptr=(char *) &s[n];
    return(num);
}

/*-------------------------------------------------------------------------*
*    strtoul - converts string to unsigned long and stores address of	   *
*    last character scanned.						   *
*--------------------------------------------------------------------------*/

unsigned long strtoul(const char *s, char **eptr, int base)
{
    unsigned long num;
    unsigned long last_val;
    int  n, c, flag, scanned;

    n=0;
    num=0;
    last_val=0;
    flag=0;
    scanned=0;
    while(isspace(s[n]))
    {
	++n;
    }
    if(s[n] == '+')
        ++n;
    if(s[n] == '0')
    {
	++n;
	if(tolower(s[n]) ==  'x')
	{
	    if((base == 16) || (base == 0))
	    {
	        flag|=_HEX;
                ++n;
            }
            else
                return(0);
	}
	else if((base == 8) || (base == 0))
	    flag|=_OCT;
    }	
    if((base > 36) || (base < 2))
       base=0;
    if(base == 0)
    {
	if(flag&_HEX)
	    base=16;
	else if(flag&_OCT)
	    base=8;
	else
	    base=10;
    }
    while(1)
    {
	c=s[n];
	if(!isdigit(c))
	    c=tolower(c)-'a'+10;
	else
	    c-='0';
	if((c >= base) || (c < 0))
	    break;
        scanned=1;
	num*=base;
	num+= (long) (unsigned) c;
	if(num < last_val)
	{
	    errno=ERANGE;
	    return(ULONG_MAX);
	}
	last_val=num;
	++n;

    }
    if(!scanned)
    {
        if(eptr != NULL)
            *eptr= (char *) s;
        return(0);
    }
    if(eptr != NULL)
	*eptr= (char *) &s[n];
    return(num);
}

/*-------------------------------------------------------------------------*
*    gcvt - convert double precision real to ascii string.		   *
*--------------------------------------------------------------------------*/

char *gcvt(double num, int digits, char *buf)
{
    if((digits > 18) || ( digits < 0))	/* max precision is 18 */
	digits=18;
    return(_f_fmt(buf, (LONGDOUBLE) num, 0x400, digits));
}

char *gcvtl(LONGDOUBLE num, int digits, char *buf)
{
    if((digits > 18) || ( digits < 0))	/* max precision is 18 */
	digits=18;
    return(_f_fmt(buf, num, 0x400, digits));
}

/*-------------------------------------------------------------------------*
*  div and ldiv - return quotient and remainder of division in structure   *
*--------------------------------------------------------------------------*/

div_t div(int num, int den)
{
    div_t ret;

    ret.quot=num/den;
    ret.rem= num%den;
    return(ret);
}

ldiv_t ldiv(long num, long den)
{
    ldiv_t ret;

    ret.quot=num/den;
    ret.rem= num%den;
    return(ret);
}

/*-------------------------------------------------------------------------*
*    strerror - return address of system message string.		   *
*--------------------------------------------------------------------------*/

char *strerror(int errnum)
{
#ifdef OLDERRORS
    if(errno > sys_nerr)
	errnum=EFAULT;
    return(sys_errlist[errnum+1]);	/* message is system string only */
#else
    return(_getErrorText(errnum,_sys_errbuf));
#endif
}

char *_strerror(const char *str)
{
#ifdef OLDERRORS
    int  ernum;
					/* concatenates user message and */
    if(errno > sys_nerr)		/* system message for last error */
	ernum=EFAULT;
    else
	ernum=errno;
    if(str == NULL)
	return(sys_errlist[ernum+1]);
    sprintf(_sys_errbuf, "%.94s: %s\n", str, sys_errlist[ernum+1]);
#else
    char err[0x128];

    _getErrorText(errno,&err[0]);
    if (str == NULL)
	strcpy(_sys_errbuf,&err[0]);
    sprintf(_sys_errbuf, "%.94s: %s\n", str, &err[0]);
#endif
    return(_sys_errbuf);
}

/*-------------------------------------------------------------------------*
*    perror - prints user mesage and system message for last error	   *
*--------------------------------------------------------------------------*/

void perror(const char *str)
{
#ifdef OLDERRORS
    int ernum;

    if(errno > sys_nerr)
	ernum=EFAULT;
    else
	ernum=errno;
    if(str != NULL)
        fprintf(stderr, "%.94s: ", str);
    fprintf(stderr, "%s\n", sys_errlist[ernum+1]);
#else
    fprintf(stderr, _strerror(str));
#endif
    return;
}

/*-------------------------------------------------------------------------*
*    rand, srand and randomize - random number generation.		   *
*--------------------------------------------------------------------------*/

_STATIC int  _r_hptr=0;
_STATIC int  _r_lptr=0;
_STATIC int  _r_hr[_HMAX+1];

int rand()
{
    int result;

    if((_r_lptr == 0) && (_r_hptr == 0))
            srand(1);
    if (_r_hptr == 0)
    {
        _r_hptr=_HMAX;
	_r_lptr-=1;
    }
    else
    {
        _r_hptr-=1;
        if(_r_lptr == 0)
            _r_lptr=_HMAX;
        else
            _r_lptr-=1;
    }
    result=_r_hr[_r_hptr]+_r_hr[_r_lptr];
    _r_hr[_r_hptr]=result;
    return(result&0x7FFF);
}

void randomize(void)
{
   struct time t;

   gettime(&t);
   srand(t.ti_sec+t.ti_hund);
   return;
}

void srand(unsigned seed)
{
    unsigned long x;
    int i;

    _r_hptr=_HMAX;
    _r_lptr=23;
    x=(long)(unsigned) seed;
    i=0;
    do
    {
	x= x*3141592621L+17;
	_r_hr[i++]= (int) (x/0x10000L);
    } while (i <= _HMAX);
    return;
}

/*-------------------------------------------------------------------------*
*    time module - system anf file time functions.			   *
*--------------------------------------------------------------------------*/

/*
Leap years - centennial years are only leap years if they are divisible by
400. Therefore the year 2000 is a leap year. MSDOS clock functions won't
work in 2100 anyway, so execption will not occur during the life of this code.
*/

_STATIC int _mtable[]=  {   0,  31,  59,  90, 120, 151,
	         	  181, 212, 243, 273, 304, 334, 365 };

_STATIC int _lmtable[]= {   0,	31,  60,  91, 121, 152,
	        	  182, 213, 244, 274, 305, 335, 366 };

_STATIC char *_wdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
_STATIC char *_months[]= { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
	        	   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

_STATIC char *_fwdays[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
_STATIC char *_fmonths[]= { "January", "February", "March", "April", "May", "June",
	                    "July", "August", "September", "October", "November", "December" };

_STATIC  char _asct_buf[28];
_STATIC  struct tm _tm_buf;

/*-------------------------------------------------------------------------*
*    ctime - convert time in seconds to string format.			   *
*--------------------------------------------------------------------------*/

char *ctime(const time_t *clock)
{
    struct date dte;			       /* uses static buffer */
    struct time tme;
    int weekday;
    char *buffer;

    buffer=_asct_buf;
    weekday=_t_to_struct(*clock, &dte, &tme);
    sprintf(buffer,"%s %s %.2d %.2d:%.2d:%.2d %.4d\n",_wdays[weekday],
	   _months[dte.da_mon-1],dte.da_day,tme.ti_hour,tme.ti_min,
	   tme.ti_sec,dte.da_year);
    return(buffer);
}

/*-------------------------------------------------------------------------*
*    asctime - convert time structure to string format. 		   *
*--------------------------------------------------------------------------*/

char *asctime(const struct tm *tme)
{
    char *buffer;
    buffer=_asct_buf;
    sprintf(buffer,"%s %s %.2d %.2d:%.2d:%.2d %.4d\n",_wdays[tme->tm_wday],
	   _months[tme->tm_mon],tme->tm_mday,tme->tm_hour,tme->tm_min,
	   tme->tm_sec,tme->tm_year+1900);
    return(buffer);
}

/*-------------------------------------------------------------------------*
*    ftime - fills in timeb structure.                    		   *
*--------------------------------------------------------------------------*/

void ftime(struct timeb *tp)
{
    struct time st;

    gettime(&st);
    tp->millitm=st.ti_hund*10;
    time(&tp->time);
    tp->dstflag=daylight;
    if(timezone >= 0)
        tp->timezone=1440-(((int) timezone)/60);
    else
        tp->timezone=(int) -(timezone/60);
    return;
}

/*-------------------------------------------------------------------------*
*    low- level time functions. 					   *
*--------------------------------------------------------------------------*/

struct tm *_t_to_tm(unsigned long clock)
{
    struct date dte;			 /* converts time in seconds to */
    struct time tme;			 /* struct tm format */
    struct tm *tmbuf;

    tmbuf=&_tm_buf;
    tmbuf->tm_wday=_t_to_struct(clock, &dte, &tme);
    tmbuf->tm_sec=tme.ti_sec;
    tmbuf->tm_min=tme.ti_min;
    tmbuf->tm_hour=tme.ti_hour;
    tmbuf->tm_mday=dte.da_day;
    tmbuf->tm_mon=dte.da_mon-1;
    tmbuf->tm_year=dte.da_year-1900;
    if(dte.da_year%4 == 0)			 /* 2000 is a leap year */
	tmbuf->tm_yday=_lmtable[dte.da_mon-1];
    else
	tmbuf->tm_yday=_mtable[dte.da_mon-1];
    tmbuf->tm_yday+=dte.da_day-1;
    tmbuf->tm_isdst=daylight;
    return(tmbuf);
 }
/*-------------------------------------------------------------------------*
*    time - returns number of seconds since 01 01 1970 GMT		   *
*--------------------------------------------------------------------------*/

time_t time(time_t *tptr)
{
    struct date dte;
    struct time tme;
    time_t ret;

    getdate(&dte);		   /* get time and date from system */
    gettime(&tme);
    ret=(time_t)_t_calc(dte, tme);         /* convert to seconds GMT */
    if(tptr)
        *tptr=ret;
    return(ret);
}

/*-------------------------------------------------------------------------*
*    stime - set system clock from number of seconds after 01 01 1970 GMT  *
*--------------------------------------------------------------------------*/

int stime(time_t *tptr)
{
    struct date dte;
    struct time tme;
    time_t val;

    val=*tptr+timezone;
    if(daylight)
	val+=3600;
    _t_to_struct(val, &dte, &tme);	/* convert seconds to years etc. */
    setdate(&dte);			/* set system time */
    settime(&tme);
    return(0);
}

/*-------------------------------------------------------------------------*
*    difftime - returns double time difference				   *
*--------------------------------------------------------------------------*/

double difftime(time_t t1, time_t t2)

{
    return((double) (t1-t2));
}

/*-------------------------------------------------------------------------*
*    tzset - does nothing in this implementation			   *
*--------------------------------------------------------------------------*/

void tzset(void)
{
    return;
}

/*-------------------------------------------------------------------------*
*    gmtime  - converts time in seconds to structure GMT.		   *
*--------------------------------------------------------------------------*/

struct tm *gmtime(const time_t *clock)
{
    struct tm *ret;

    ret=_t_to_tm(*clock);
    ret->tm_isdst=0;
    return(ret);
}

/*-------------------------------------------------------------------------*
*    localtime - converts time in seconds to structure loca time.	   *
*--------------------------------------------------------------------------*/

struct tm *localtime(const time_t *clock)
{
    time_t tt;
    struct tm *ret;
    int dst;

    tt=*clock+timezone;
    if(daylight)
    {
        tt+=3600;
        dst=1;
    }
    else
        dst=0;
    ret=_t_to_tm(tt);
    ret->tm_isdst=dst;
    return(ret);
}

/*-------------------------------------------------------------------------*
*    clock - returns number of seconds since process started.		   *
*--------------------------------------------------------------------------*/

clock_t clock()
{
    clock_t tt;

    tt=_clock_time();
    if(tt < _proc_time)
      tt+= _DAYSECS;
    return(tt -_proc_time);
}

clock_t _clock_time()
{
    struct time tme;
    clock_t ret;

    gettime(&tme);
    ret=tme.ti_hund;
    ret+=((clock_t)tme.ti_sec)*100;
    ret+=((clock_t)tme.ti_min)*6000;
    ret+=((clock_t)tme.ti_hour)*360000L;
    return(ret);
}

time_t mktime(struct tm *timeptr)
{
    unsigned long ret;
    int isdt;

    if(timeptr->tm_year < 0)
        return((time_t) -1);
    if((ret=_t_norm(timeptr)) == ULONG_MAX)
        return((time_t) -1);
    isdt=timeptr->tm_isdst;
    memcpy(timeptr, _t_to_tm(ret), sizeof(struct tm));
    if(isdt >= 0)
        timeptr->tm_isdst=isdt;
    if(timeptr->tm_isdst)
        ret-=3600;
    ret-=timezone;
    return(ret);
}

size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
{
    size_t fcount;
    size_t scount;
    int c, thour;
    char temp[32];
    char *source;
    int yday;
    int yweek;

    fcount=0;
    scount=0;
    if(timeptr->tm_year%4 == 0)			 /* 2000 is a leap year */
        yday=_lmtable[timeptr->tm_mon];
    else
        yday=_mtable[timeptr->tm_mon];
    yday+=timeptr->tm_mday;
    while((c=format[fcount++]) != 0)
    {
        if(scount == maxsize)
            return(0);
        if(c == '%')
        {
            c=format[fcount++];
            if(c == 0)
                break;
            switch(c)
            {
                case '%':
                    if(scount == maxsize)
                        return(0);
                    s[scount++]=c;
                    continue;
                case 'a':
                    source= _wdays[timeptr->tm_wday];
                    break;
                case 'A':
                    source= _fwdays[timeptr->tm_wday];
                    break;
                case 'b':
                    source=_months[timeptr->tm_mon];
                    break;
                case 'B':
                    source= _fmonths[timeptr->tm_mon];
                    break;
                case 'c':
                    source= asctime(timeptr);
                    break;
                case 'd':
                    sprintf(temp, "%.2d", timeptr->tm_mday);
                    source=temp;
                    break;
                case 'H':
                    sprintf(temp, "%.2d", timeptr->tm_hour);
                    source=temp;
                    break;
                case 'I':
                    thour=timeptr->tm_hour;
                    if(thour > 12)
                        thour-=12;
                    sprintf(temp, "%.2d", thour);
                    source=temp;
                    break;
                case 'j':
                    sprintf(temp, "%.2d", yday);
                    source=temp;
                    break;
                case 'm':
                    sprintf(temp, "%.2d", timeptr->tm_mon+1);
                    source=temp;
                    break;
                case 'M':
                    sprintf(temp, "%.2d", timeptr->tm_min);
                    source=temp;
                    break;
                case 'p':
                    thour=timeptr->tm_hour;
                    if(thour > 12)
                        source="PM";
                    else
                        source="AM";
                    break;
                case 'S':
                    sprintf(temp, "%.2d", timeptr->tm_sec);
                    source=temp;
                    break;
                case 'U':
                    yweek=yday/7;
                    if(timeptr->tm_wday == 0)
                        ++yweek;
                    sprintf(temp, "%.2d", yweek);
                    source=temp;
                    break;
                case 'w':
                    sprintf(temp, "%d", timeptr->tm_wday);
                    source=temp;
                    break;
                case 'W':
                    yweek=yday/7;
                    if(timeptr->tm_wday == 1)
                        ++yweek;
                    sprintf(temp, "%.2d", yweek);
                    source=temp;
                    break;
                case 'x':
                    sprintf(temp, "%s %s %d, %d", _wdays[timeptr->tm_wday],
                                                    _months[timeptr->tm_mon],
                                                    timeptr->tm_mday,
                                                    timeptr->tm_year+1900);
                    source=temp;
                    break;
                case 'X':
                    sprintf(temp, "%.2d:%.2d:%.2d", timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);
                    source=temp;
                    break;
                case 'y':
                    sprintf(temp, "%.2d", timeptr->tm_year);
                    source=temp;
                    break;
                case 'Y':
                    source=itoa((timeptr->tm_year+1900), temp, 10);
                    break;
                case 'Z':
                    continue;
                default:
                    continue;
            }
            scount=_set_date(s, scount, maxsize, source);
            continue;
        }
        if(scount == maxsize)
            return(0);
        s[scount++]=c;
    }
    if(scount == maxsize)
        return(0);
    s[scount]=0;
    return(scount);
}

char *_strtime(char *timestr)
{
    time_t tt;
    struct tm *timeptr;

    time(&tt);
    timeptr=localtime(&tt);
    sprintf(timestr, "%.2d:%.2d:%.2d", timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);
    return(timestr);
}

char *_strdate(char *datestr)
{
    time_t tt;
    struct tm *timeptr;

    time(&tt);
    timeptr=localtime(&tt);
    sprintf(datestr, "%.2d/%.2d/%.2d", timeptr->tm_mon+1, timeptr->tm_mday, timeptr->tm_year);
    return(datestr);
}

unsigned long _ytable(int year)
{

  return( ((long)year * 365L) + (long)((year+1)/4) );
}


size_t _set_date(char *s, size_t scount, size_t lim, char *st)
{
    int n=0;
    int c;

    while(scount < lim)
    {
        c=st[n++];
        if(c == 0)
            break;
        s[scount]=c;
        scount++;
    }
    return(scount);
}

unsigned long _t_norm(struct tm *tp)
{
  unsigned long xdays;
  long st, pt;

  st  = (long)tp->tm_sec;
  st += ((long)tp->tm_min*60);
  st += ((long)tp->tm_hour*3600);
  if(abs(tp->tm_mday) > 24800)
      return(ULONG_MAX);
  pt = ((long)(tp->tm_mday-1)*3600L*24L);
  if(labs(st/2 + pt/2) > LONG_MAX/2)
      return(ULONG_MAX);
  st+=pt;

  tp->tm_year+=tp->tm_mon/12;
  if(tp->tm_year < 0)
    return(-1);
  tp->tm_mon=tp->tm_mon%12;
  if((tp->tm_year+1900)%4)
    xdays=_mtable[tp->tm_mon];
  else
    xdays=_lmtable[tp->tm_mon];
  if(tp->tm_year > 186)
      return(ULONG_MAX);
  xdays+=_ytable(tp->tm_year-70);
  xdays*=(3600L*24L);
  if((labs(st/2 + xdays/2) > LONG_MAX) || ((st < 0) && (labs(st) > xdays)))
      return(ULONG_MAX);
  return(st+xdays);
}

unsigned long _t_calc(struct date dte, struct time tme)
{
    time_t ret;
    int leap_year;
		
    leap_year=(dte.da_year%4 == 0);			   /* converts DOS format to seconds */
    dte.da_year-=1970;
    ret= _ytable(dte.da_year);/* total days in years */
    if(leap_year)
	ret+=_lmtable[dte.da_mon-1];	   /* add days in months */
    else
	ret+=_mtable[dte.da_mon-1];
    ret+=dte.da_day-1;			   /* add days in month */
    ret*=_DAYSECS;			   /* convert to seconds */
    ret+= ((long) tme.ti_hour)*3600;	   /* add hours in day */
    ret+=tme.ti_min*60;			   /* add hours in day */
    ret+=tme.ti_sec;			   /* add seconds */
    ret-=timezone;			   /* timezone and daylight saving */
    if(daylight)
	ret-=3600;
    return(ret);
}

int _t_to_struct(unsigned long val, struct date *dte, struct time *tme)
{
    int n, wday;			      /* convert time in seconds to */
    long day;				      /* DOS format		    */

    n=0;
    day=val%_DAYSECS;
    tme->ti_hour= (unsigned char) (day/3600);
    day%=3600;
    tme->ti_min=  (unsigned char) (day/60);
    tme->ti_sec=  (unsigned char) (day%60);
    tme->ti_hund=0;
    val/=_DAYSECS;			      /* convert to days */
    wday= (int) (val%7);
    wday = (wday+4)%7;
    while(_ytable(n) <= val)
      ++n;
    dte->da_year=1969+n;
    val-=_ytable(n-1);
    n=0;
    if(dte->da_year%4 == 0)   /* 2000 is leap year so no exception */
    {
	while(_lmtable[n] <= val)
	    ++n;
	val-=_lmtable[n-1];
    }
    else
    {
	while(_mtable[n] <= val)
	    ++n;
	val-=_mtable[n-1];
    }
    dte->da_mon= (unsigned char) n;
    dte->da_day= (unsigned char) val+1;
    return(wday);
}

time_t get_utime(int handle)		      /* gets file time and converts */
{
    struct utm ftime;
    struct date dte;
    struct time tme;

    _filetime(handle, &ftime);
    tme.ti_sec = (unsigned char) ftime.u_sec;
    tme.ti_min = (unsigned char) ftime.u_min;
    tme.ti_hour= (unsigned char) ftime.u_hour;
    dte.da_day= (unsigned char) ftime.u_day;
    dte.da_mon= (unsigned char) ftime.u_mon;
    dte.da_year=ftime.u_year+1980;
    return(_t_calc(dte, tme));
}

/*-------------------------------------------------------------------------*
*  qsort - sort array member using quicksort algorithm			   *
*--------------------------------------------------------------------------*/

_STATIC void _qsort(char *buf, size_t num, size_t width,
	            int (* compare)(const void *e1, const void *e2),
	            char *_partition)\
{
    int i, j;
    int  new_num, right_num;

    while(num > 1) {
      memcpy(_partition, &buf[(num/2)*width], width);  /* get partition element */
      i= -width;
      j=num*width;
      while(1) {					     /* sort partition */
  	do {
  	    i+=width;
  	} while( (i < j) && (compare(&buf[i], _partition) < 0 ) );
  	do {
  	    j-=width;
  	} while( (i < j) && (compare(&buf[j], _partition) > 0) );
  	if(i >= j)
  	    break;
  	_swap(&buf[i], &buf[j], width);
      }
      new_num=i/width;
      right_num= num-new_num;
      if( new_num < right_num)	{		     /* sort new partitions */
  	_qsort(buf, new_num, width, compare, _partition);
  	num=right_num;
  	buf=&buf[i];
      }
      else {
  	_qsort(&buf[i], right_num, width, compare, _partition);
  	num=new_num;
      }
    }
    return;
}

void qsort(void *base, size_t num, size_t width,
	   int (* compare)(const void *e1, const void *e2))
{
    char *_partition;

    _partition=malloc(width);	/* allocate buffer for partition member */
    if(_partition != NULL)
    {
       _qsort((char *)base, num, width, compare, _partition);	 /* sort array */
       free(_partition);
    }
    return;
}

/*-------------------------------------------------------------------------*
*  lfind - find key element in array.					   *
*--------------------------------------------------------------------------*/

char *lfind(const char *key, const char *base, unsigned *num,
	    unsigned width, int (* compare)(const void *e1, const void *e2))
{
    const char *ptr;
    unsigned count;

    count=*num;
    ptr=base;
    while(count--)
    {
	if(compare(key, ptr) == 0)
	    return(ptr);		      /* found key */
	ptr+=width;
    }
    return(NULL);			      /* key not found */
}

/*-------------------------------------------------------------------------*
*  lfind - find key element in array and append if not present. 	   *
*--------------------------------------------------------------------------*/

char *lsearch(const char *key, char *base, unsigned *num,
	      unsigned width, int (* compare)(const void *e1, const void *e2))
{
    char *ptr;
    unsigned count;

    count=*num;
    ptr=base;
    while(count--)
    {
	if(compare(key, ptr) == 0)
	    return(ptr);	       /* found key */
	ptr+=width;
    }
    memcpy(ptr, key, width);	       /* not found - append */
    return(ptr);
}

/*-------------------------------------------------------------------------*
*  bsearch - search for key member in sorted array.			   *
*--------------------------------------------------------------------------*/

void *bsearch(const void *key, const void *base, unsigned num,
	      unsigned width, int (* compare)(const void *e1, const void *e2))
{
  long l, r, x;
  int ret;
  char *ptr;

  l = 0;
  r = num;
  do
    {
    x = (l+r)/2;
    ptr = &((char *)base)[x*width];
    ret=compare(key, ptr);
    if(ret < 0)
      r = x - 1;
    else if(ret > 0)
      l = x + 1;
    else
      return(ptr);
    } while (l < r);
  ptr = &((char *)base)[l*width];
  return(compare(key,ptr) ? NULL : ptr);
}

/*-------------------------------------------------------------------------*
*  sbrk - returns -1 in this implementation.				   *
*--------------------------------------------------------------------------*/

#pragma warn(wpnu => off)

char *sbrk(int incr)

{
    return( (char *) 0xFFFF);
}


/*-------------------------------------------------------------------------*
*  locale functions                                                        *
*--------------------------------------------------------------------------*/

_STATIC char _last_localeALL[4]="C";
_STATIC char _last_localeCOLL[4]="C";
_STATIC char _last_localeCTYP[4]="C";
_STATIC char _last_localeNUM[4]="C";
_STATIC char _last_localeTIME[4]="C";
_STATIC char _last_localeMON[4]="C";

struct lconv *localeconv()

{
    return(&_lconv);
}

char * setlocale(int category, const char *locale)
{
    char *ret;

    if(locale)
    {
        if((strcmp(locale, "")) && (strcmp(locale, "C")))
            return(NULL);
    }
    switch(category)
    {
        case LC_ALL:
            if(locale == NULL)
                return(_last_localeALL);
            ret=_last_localeALL;
            strcpy(_last_localeALL, locale);
            strcpy(_last_localeCOLL, locale);
            strcpy(_last_localeCTYP, locale);
            strcpy(_last_localeNUM, locale);
            strcpy(_last_localeTIME, locale);
            strcpy(_last_localeMON, locale);
            return(ret);
            break;
        case LC_COLLATE:
            if(locale == NULL)
                return(_last_localeCOLL);
            ret=_last_localeCOLL;
            strcpy(_last_localeCOLL, locale);
            return(ret);
            break;
        case LC_CTYPE:
            if(locale == NULL)
                return(_last_localeCTYP);
            ret=_last_localeCTYP;
            strcpy(_last_localeCTYP, locale);
            return(ret);
            break;
        case LC_NUMERIC:
            if(locale == NULL)
                return(_last_localeNUM);
            ret=_last_localeNUM;
            strcpy(_last_localeNUM, locale);
            return(ret);
            break;
        case LC_TIME:
            if(locale == NULL)
                return(_last_localeTIME);
            ret=_last_localeTIME;
            strcpy(_last_localeTIME, locale);
            return(ret);
            break;
        case LC_MONETARY:
            if(locale == NULL)
                return(_last_localeMON);
            ret=_last_localeMON;
            strcpy(_last_localeMON, locale);
            return(ret);
            break;
        default:
            return(NULL);
    }
}

/*-------------------------------------------------------------------------*
*  multi-byte functions                                                    *
*--------------------------------------------------------------------------*/

int mblen(const char *s, size_t n)
{
    if((s) && (s[0]))
        return(1);
    return(0);
}

int mbtowc(wchar_t *pwc, const char *s, size_t n)
{
    if((s) && (pwc))
    {
        *pwc=*s;
        if(s[0])
            return(1);
    }
    return(0);
}

int wctomb(char *s, wchar_t wchar)
{
    if(s)
    {
        *s=wchar;
        return(1);
    }
    return(0);
}

/*-------------------------------------------------------------------------*
*    matherr - dummy math error handler.				   *
*--------------------------------------------------------------------------*/

int  _matherr(struct exception *x)
{
    if(x != NULL);
    return(0);
}

/*-------------------------------------------------------------------------*
*  misc compatibilty functions                                             *
*--------------------------------------------------------------------------*/

#pragma warn(wpnu => on)

void fnmerge(char *path,const char *drive,const char *dir,const char *name,const char *ext)
  {
  if (drive && *drive)
    {
    *path++ = *drive++; // may not need ++ on drive
    *path++ = ':';
    }
  if (dir && *dir)
    {
    path = stpcpy(path,dir);
      if (*(path-1) != '\\' && *(path-1) != '/')
        *path++ = '\\';
    }
  if (name)
    path=stpcpy(path,name);
  if (ext && *ext)
    {
    if(*ext != '.')
      *path++ = '.';
    path=stpcpy(path,ext);
    }
  *path = 0;
  }

int fnsplit(const char *path, char *drive, char *dir, char *name, char *ext) {

    int n, p, c;
    int ret;
    int dir_start, name_start, ext_start, path_end;

    n=0;
    ret=0;
    if((path[0]) && ((path[1] == ':') || (path[2] == ':'))) {
      do {
        c=path[n];
        drive[n]=c;
        ++n;
      } while (c != ':');
      ret |= DRIVE;
    }
    drive[n]='\0';
    dir_start=n;
    name_start=n;
    ext_start=0;
    while((c=path[n]) != '\0') {
      switch(c) {
        case '.' :
          if(!((path[n+1] == '.') || (path[n+1] == '\\') || (path[n+1] == '/')))
            ext_start=n;
          ++n;
          break;
        case '/' :
        case '\\':
          name_start=++n;
          ext_start=0;
          break;
        case '*' :
        case '?' :
          ret |= WILDCARDS;
        default:
          ++n;
          break;
      }
    }
    path_end=n;
    if(ext_start == 0)
      ext_start=n;

    n=dir_start;
    p=0;
    if(n < name_start) {
      ret|=DIRECTORY;
      while((n < name_start) && (p < MAXDIR-1)) {
        dir[p++]=path[n++];
      }
    }
    dir[p]='\0';

    n=name_start;
    p=0;
    if(n < ext_start) {
      ret |= FILENAME;
      while((n < ext_start) && (p < MAXFILE-1)) {
        name[p++]=path[n++];
      }
    }
    name[p]='\0';

    p=0;
    if((ext_start >= name_start) && (path[ext_start])) {
      n=ext_start;
      ret |= EXTENSION;
      while((n < path_end) && (p < MAXEXT-1)) {
        ext[p++]=path[n++];
      }
    }
    ext[p]='\0';

    return ret;
}

void _makepath(char *path,  const char *drive,  const char *dir,
               const char *name,  const char *ext)
{
    int pos;

    if(drive)
        strcpy(path, drive);
    if(dir)
    {
        if((dir[0] != '\\') && (dir[0] != '/'))
        strcat(path, "\\");
        strcat(path, dir);
        pos=strlen(path)-1;
        if(!((path[pos] == '\\') || (path[pos] == '/') && (!dir[0])))
        {
            path[pos+1]='\\';
            path[pos+2]=0;
        }
    }
    strcat(path, name);
    if((ext) && (ext[0]))
    {
        if(ext[0] != '.')
        {
            pos=strlen(path);
            path[pos]='.';
            path[pos+1]='\0';
        }
        strcat(path, ext);
    }
    return;
}

void _splitpath(const char *path, char *drive, char *dir, char *name, char *ext) {
    int n, p, c;
    int dir_start, name_start, ext_start, path_end;

    n=0;
    if((path[0]) && ((path[1] == ':') || (path[2] == ':'))) {
      do {
        c=path[n];
        drive[n]=c;
        ++n;
      } while (c != ':');
    }
    drive[n]='\0';
    dir_start=n;
    name_start=n;
    ext_start=0;
    while((c=path[n]) != '\0') {
      switch(c) {
        case '.' :
          if(!((path[n+1] == '.') || (path[n+1] == '\\') || (path[n+1] == '/')))
            ext_start=n;
          ++n;
          break;
        case '/' :
        case '\\':
          name_start=++n;
          ext_start=0;
          break;
        default:
          ++n;
          break;
      }
    }
    path_end=n;
    if(ext_start == 0)
      ext_start=n;

    n=dir_start;
    p=0;
    while((n < name_start) && (p < _MAX_DIR-1)) {
      dir[p++]=path[n++];
    }
    dir[p]='\0';

    n=name_start;
    p=0;
    while((n < ext_start) && (p < _MAX_FNAME-1)) {
      name[p++]=path[n++];
    }
    name[p]='\0';

    p=0;
    if(ext_start >= name_start) {
      n=ext_start;
      while((n < path_end) && (p < _MAX_EXT-1)) {
        ext[p++]=path[n++];
      }
    }
    ext[p]='\0';
}

_STATIC char _spath[80];

char *searchpath(const char *filename)
{

    _searchenv(filename, "PATH", _spath);
    if(_spath[0])
        return(_spath);
    return(NULL);
}

/*-------------------------------------------------------------------------*
*    strtok -  separates tokens from string.				   *
*--------------------------------------------------------------------------*/

_STATIC char *_token;

char *strtok(char *s1, const char  *s2)
{
    int n, retn;

    if(s1 == NULL)
        s1=_token;
    if(s1 == NULL)
	return(NULL);
	                        /* use end of last token as starting point */
    n=strspn(s1, s2);		/* skip leading deliminators */
    if(s1[n] == '\0')
	return(NULL);
    retn=n;			/* save beginning of token */
    n+=strcspn(&s1[n], s2);	/* find end of token */
    if(n == retn)
	return(NULL);
    if(s1[n])	
    {	
        _token=&s1[n+1];
        s1[n]=0;			/* terminate token */
    }
    else
    {	
        _token=NULL;
    }
    return(&s1[retn]);		/* return address of token */
}

char *StrToPas(char *passtr, const char *s) {

  size_t l;

  l=strlen(s);
  if(l > 255)
    return NULL;
  passtr[0]=(char) l;
  return strcpy(&passtr[1], s);
}

char *_ld_format(char *fmt_buf, LONGDOUBLE num, int flag, int prec)
{
  return _f_fmt(fmt_buf, num, flag, prec);
}

char *_v_format(unsigned long l, char *s)
{
  return _fptoa(l, s);
}
