/*===========================================================================
  Copyright (C) 1995-2009 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.TYPE        Module
.NAME        tz3.c
.LANGUAGE    C
.AUTHOR      J.D.Ponz, F. Ochsenbein  IPG-ESO Garching
.CATEGORY    table interface (Design 3.0). 
.KEYWORDS    tables, file name
.PURPOSE     Code / Decode Routines
.VERSION  3.0 	01-Jul-1990   Extracted from tz2
.VERSION  3.1 	19-Dec-1990: Added NULL setup
.VERSION  3.2   22-Sep-1992: treats properly NULL values (for the Convex)

 090406		last modif


.COMMENTS 	An extended set of formats for i/o was implemented.
\begin{TeX}
These are described below, with an example on the integer number 86401
or the Floating number 83.5
\begin{tabular}{| l | l | l r |} \hline 
		& Meaning & \multicolumn{2}{l |}{Example}\\ \hline
	\multicolumn{4}{| c | } { Integer Numbers} \\ \hline
	I$n$	& Decimal edition & I8 & {\tt 86401} \\
	i$n$    & Decimal with sign & i8 & {\tt +86401} \\
	X$n$    & Hexadecimal       & X8 & {\tt 15181} \\
	o$n$    & Octal	            & o8 & {\tt 250601} \\
	T$n.d$    & Time, numeric     & T19.6 & {\tt 1970-01-02:00:00:01} \\
	t$n.d$    & Time, litteral    & t20.6 & {\tt 02-Jan-1970:00:00:01} \\
	Z$n$  & Zero-filled number& Z12 &     {\tt 000000086401} \\
	z$n$  & zero-filled with sign & z12 & {\tt +00000086401} \\
	\hline
	\multicolumn{4}{| c | } { Floating-point Numbers} \\ \hline
	F$n.d$  & F format	& F12.2 & {\tt    83.50} \\
	f$n.d$  & F format with sign & f12.2 & {\tt    +83.50} \\
	E$n.d$  & E format	& E12.2 & {\tt 8.3e+01 } \\
	f$n.d$  & E format with sign & f12.2 & {\tt +8.3e+01} \\
	R$n.d$  & RA (angle of 24h)  & R11.6 & {\tt 05 34 00.00} \\
	r$n.d$  & RA (angle of 24h)  & R12.6 & {\tt +05 34 00.00} \\
	S$n.d$  & Sexagesimal 	& S9.4 & {\tt 83 00 00} \\
	s$n.d$  & Signed Sexagesimal 	& S9.4 & {\tt +83 00 00} \\
	T$n.d$  & Julian Date		&	&	\\
	t$n.d$  & Litteral Julian Date		&	&	\\
	Z$n.d$  & Zero-filled number& Z12.2 &     {\tt 000000083.50} \\
	Z$n.d$  & Zero-filled number& Z12.2 &     {\tt 000000083.50} \\
	z$n.d$  & zero-filled with sign & z12.2 & {\tt +00000083.50} \\
	\hline
	\end{tabular}

\end{TeX}
		For Integers
------------------------------------------------------------*/

#include <midas_def.h>		/* General MIDAS Symbols	*/
#include <computer.h>
#include <tblsys.h>		/* Table System parameters	*/
#include <tblerr.h>		/* List of Table Errors		*/
#include <macrogen.h>		/* Classical Macros		*/
#include <atype.h>		/* Character classification	*/

#include <stdlib.h>
#include <stdio.h>



double	modf();			/* Modulo function in DP (math)	*/

#define LEN	80

#define issign(c)		((c == '+') || (c == '-'))
#define GobbleSpaces(s,l)	s += oscspan((unsigned char *)s, l, _SPACE_, main_ascii)

static  char	*errmsg = (char *)0;
static  char 	*stopped;	/* To communicate the stopping char */

static	int 	accu;
static  int	base = 10;
static	char	sign;
static	char	has_decimals;

				/* Limits related to integer size	*/
static int	llim[] = { -127, -32767, -2147483647, -2147483647};
static int	ulim[] = {  255, 65535,   2147483647,  2147483647};

/*==========================================================================*
 *			NULL definitions for floating-point
 *==========================================================================*/

 
/*==========================================================================*
 *			Internal for Time Manipulation
 *==========================================================================*/

#include <time.h>	/* System	*/

#define JD1970		2440587.5e0	/* JD   of 00:00:00 Jan. 1, 1970   */
 
#if 0 /*===========================TAKEN FROM SYSTEM ===================*/
struct tm {			/* Gregorian!		*/
	int	tm_year,	/* Complete year	*/
		tm_mon,		/* 0-11		*/
		tm_mday,	/* 1-31		*/
		tm_hour,	/* 0-23		*/
		tm_min,		/* 0-59		*/
		tm_sec,		/* 0-59		*/
		tm_wday,	/* 0(Sunday)-6	*/
		tm_yday,	/* 0-365	*/
		tm_isdst;	/* unused	*/
	   };        	
#endif

static char month_list[] = "\0Jan\01Feb\02Mar\03Apr\04May\05Jun\06Jul\07Aug\
\010Sep\011Oct\012Nov\013Dec\01Fev\03Avr\04Mai\05Jui\07Aou\011Okt\013Dez";
static char wday_list[] = "SunMonTueWedThuFriSat";


static unsigned char month[12]={31,28,31,30,31,30,31,31,30,31,30,31};

/*========================================================================*/
#ifdef __STDC__
static int tr_tm(struct tm *T)
#else
static int tr_tm(T)
/*+++
.PURPOSE	Compute missing info in T structure
.RETURNS	0 (good date) / -1 (bad data: invalid month or day number ...)
.REMARKS	
---*/
	struct tm *T;	/* MOD:	Time as a structure		*/
#endif
{
	int y, j, t;

  month[1] = 29;
  
  if (T->tm_mday) {			/* Compute Day of 400 yr cycle 	*/
  	if ( (T->tm_mon > 11) || (T->tm_mon < 0) )
  	 	errmsg = "bad month";
  	if ( (T->tm_mday <= 0) || (T->tm_mday > (int)month[T->tm_mon]))
  		errmsg = "bad day_in_month";
	if (errmsg)	return (-1);
  }

	/* Convert time	*/

  j = 0;
  t = ((T->tm_hour*60) + (T->tm_min))*60 + T->tm_sec;
  while (t < 0)		{ j--; t += 86400; }
  while (t >= 86400)	{ j++; t -= 86400; }
  T->tm_sec = t%60;
  t /= 60;
  T->tm_min = t%60;
  T->tm_hour= t/60;
  t = j;		/* Days	*/

  y = T->tm_year % 400;
  T->tm_year /= 400;
  if (y < 0)	{ y += 400; T->tm_year -= 1;}
  T->tm_year *= 400;
  if (T->tm_mday){			/* Compute Day of 400 yr cycle 	*/
  	y += 400;			/* Positive interval	*/
  	j = y - (11 - T->tm_mon)/10;
	t += ((1461*j)/4 + (306 * ((T->tm_mon+10)%12) + 5)/10
	    - (3* ((j + 100)/100))/4 + T->tm_mday + 59) % 146097;
	y -= 400;
  }
  else	t += 365*y + (y+3)/4 - (y-1)/100 + T->tm_yday;

  while (t < 0)	{ T->tm_year -= 400; t += 146097;}

						/* Compute Week day	*/
  T->tm_wday = (t+6)%7;

						/* Compute Year	*/
  T->tm_year += 400*(t/146097);
  t %= 146097;
  y = 4 *(t + (t-60)/36524) / 1461;
  T->tm_year += y;
  T->tm_yday = t - y*365 - (y+3)/4 + (y-1)/100;
  
					/* Compute mday + mon from yday	*/
  if (y&3)	month[1] = 28;
  if ( ((y%100) == 0) && (y))	month[1] = 28;

  for (y=T->tm_yday, j=0; y>=(int)month[j]; y -= month[j], j++)	;
  T->tm_mon  = j;
  T->tm_mday = y+1;

  return(0);
}

/*========================================================================*/
#ifdef __STDC__
static int tr_itm(int t, struct tm *T)
#else
static int tr_itm(t, T)
/*+++
.PURPOSE Transform integer time to structure
.RETURNS 0
.REMARKS	
---*/
	int 	t;	/* IN:	Time in seconds since Jan 1, 1970	*/
	struct tm *T;	/* OUT: Time in the structure			*/
#endif
{
	int days, year;

  days	  = t/86400;
  if (t < 0)	days--;
  t -= (days*86400);
  T->tm_hour = t/3600;
  T->tm_min  = 0;
  T->tm_sec  = (t%3600);

  days   += (70*365 + 18);		/* Offset 1900	*/
  year    = (4*days)/1461;
  T->tm_year = year;
  T->tm_mon  = 0;
  T->tm_mday = 0;
  T->tm_yday = days - 365*year - (year+3)/4;
  T->tm_year+= 1900;

  tr_tm(T);  

  return(0);
}

/*========================================================================*/
#ifdef __STDC__
static int tr_tmi(struct tm *T, int *t)
#else
static int tr_tmi(T, t)
/*+++
.PURPOSE Transform structure time to time in seconds from Jan 1, 1970
.RETURNS 0 / -1 (time not in limits 1902-2037). Error message logged,
.REMARKS	
---*/
	struct tm *T;	/* IN:	Time as a structure		*/
	int   *t;	/* OUT:	Time in seconds			*/
#endif
{
	register int n, y;

  *t = 0x80000000;	/* Default returned time...		*/
  
  if (tr_tm(T))		/* Compute missing information...	*/
	return(-1);

  if ((T->tm_year < 1902) || (T->tm_year >=2038)) {
  	errmsg = "Year outside limits [1902, 2038]";
	return(-1);
  }

  n = T->tm_year - 1970;
  y = (n >= 0 ? n+1 : n-2);		/* Leap year counter */
  n = 365*n + y/4;

  *t = (n + T->tm_yday) * 86400 + T->tm_hour * 3600
	+ T->tm_min * 60L + T->tm_sec;

  return(0);
}

/*========================================================================*/
#ifdef __STDC__
static double tmtojd(struct tm *T)
#else
static double tmtojd(T)
/*+++
.PURPOSE Transform structure time to Julian Day
.RETURNS JD equivalence
.REMARKS	
---*/
	struct tm *T;	/* IN:	Time as a structure		*/
#endif
{
	int jm, j;
	double JD;

  tr_tm(T);		/* Can be wrong...	*/

  if (T->tm_year <= -4712)
  {	j = 1 + (T->tm_year + 4712)/400;   	/* Number of 400-yr cycles */
  	jm = -j * 146097;
	j  = T->tm_year + 400*j;
  }
  else	jm = 0, j = T->tm_year;

  j  -= (11 - T->tm_mon)/10;
  jm += (1461* ( j + 4712))/4 + (306 * ((T->tm_mon+10)%12) + 5)/10
	- (3* ((j + 4900)/100))/4 + T->tm_mday + 96;
  JD = jm;

  jm  = ((12+T->tm_hour))*3600L + T->tm_min*60 + T->tm_sec;
  JD += (jm/86400.e0);

  return(JD);
}

/*========================================================================*/
#ifdef __STDC__
static int tr_jdtm(double JD, struct tm *T)
#else
static int tr_jdtm(JD, T)
/*+++
.PURPOSE Transform Julian Date to time structure
.RETURNS 0 
.REMARKS 
---*/
	double	JD;	/* IN:	Julian date	*/
	struct tm *T;	/* OUT: Date in structure	*/
#endif
{
	register int j, n4, nd10;
	double x;

  x = JD + 0.5e0 + 0.5e0/86400.e0;	/* Round day	*/
  j = x;
  if (x < 0)	 --j; 
  x = x - j;				/* Fraction of day	*/

  T->tm_year = -4712;
  while (j <= 4480)	{ j += 146097; T->tm_year -= 400;}

  n4 = 4*(j+((2*((4*j-17918)/146097)*3)/4+1)/2-37);
  nd10=10*( ((n4-237)%1461)/4)+5;

  T->tm_year += n4/1461;
  T->tm_mon  = (nd10/306+2)%12;
  T->tm_mday = (nd10%306)/10+1;

  x *= 86400.e0;			/* Seconds	*/
  j = x;
  T->tm_hour = j/3600;
  T->tm_min  = 0;
  T->tm_sec  = j%3600;

  tr_tm(T);				/* Add missing info...	*/

  return(0);
}

/*==========================================================================*
 *			Internal Routines for Edition
 *==========================================================================*/
#ifdef __STDC__
static char *ed60 (char *buf,int n,char sep)
#else
static char *ed60 (buf, n, sep)
/*++++++++++++++++++++
.PURPOSE Edit a sexagesimal element, starting from the right
.RETURNS Position 
---------------------*/
	char	*buf;	/* IN: Rightmost+1 position	*/
	int	n;	/* IN: Number 0-50 to edit	*/
	char	sep;	/* IN: Separator		*/
#endif
{
	char	*a;

  a = buf;
  *--a = '0' + n%10; n /= 10; 
  *--a = '0' + n;
  *--a = sep;
  
  return(a);
}

#ifdef __STDC__
int ed_sexa(char *buf,char *pic,double value)
#else
int ed_sexa(buf, pic, value)
/*++++++++++++++++++++
.PURPOSE Edit a value in sexagesimal
.RETURNS Status
.REMARKS pic is L.d with d = 
	0 : No fraction
	1 : .1 degree
	2 : arcmin
	3 : .1 arcmin
	4 : arcsec
	5... fractions of arcseconds
---------------------*/
	char	*buf;	/* OUT: edited value 		*/
	char	*pic;	/* IN:  Edition elements	*/
	double	value;	/* IN: Value to edit		*/
#endif
{
	static double factor[10] = {1., 10., 60., 600., 3600., 3.6e4,
		3.6e5, 3.6e6, 3.6e7, 3.6e8 };
	double 	x, f;
	char   	*p ,*a;
	int	len, dec;
	int	n;

  x = ABSOLUTE(value);
  
  for (len = 0, p = pic; isdigit(*p); p++)	len = len*10 + (*p - '0');
  for (dec = 0, ++p;     isdigit(*p); p++)	dec = dec*10 + (*p - '0');
  if (dec > 9)	dec = 9;
  
  x += .5 / factor[dec];
  f = modf (x, &x) * factor[dec];
  n = f;
  
  a = buf + len;	*a = EOS;

  while (dec > 5)	{ *--a = '0' + n%10; n /= 10; dec--; }
  
  switch(dec) {
    case 5:
    	*--a = '0' + n%10; n /= 10; *--a = '.';
    case 4:
    	*--a = '0' + n%10; n /= 10; 
    	*--a = '0' + n%6;  n /= 6;  *--a = *p;
    	goto case_2;
    case 3:
    	*--a = '0' + n%10; n /= 10; *--a = '.';
    case 2: case_2:
    	*--a = '0' + n%10; n /= 10; 
    	*--a = '0' + n%6 ; n /= 6;  *--a = *p;
    	break;
    case 1:
    	*--a = '0' + n; *--a = '.';
    	break;
  }
  n = x;		/* Degrees */
  while (a > buf) {
    	*--a = '0' + n%10; n /= 10; 
  }  
  
  if (value < 0)	*buf = '-';
  
  return(0);
}

/*===============================================================
 * 	The following Conversion Routines are extracted from 
 *			tc3lib / tral.c
 *================================================================*/
 
/*===========================================================================*/
#ifdef __STDC__
static int Number(char *str, int len)
#else
static int Number(str, len)
/*++++++
.PURPOSE Just scan a string until non-valid characters are found.
	The number is stored in accu.
.RETURNS Number of valid characters
.REMARKS Not traced. 
--------*/
	char *str;	/* IN: The string to scan	*/
	int  len;	/* IN: Length of the string	*/
#endif
{
	char *p, *pe;
	int  i;
  
  accu = 0;
  if (len <= 0)		return(0);
  
  for (p = str, pe = p + len; (p < pe) && (isxdigit(*p)); p++)
  {	if (isdigit(*p))	i = '0';
  	else if (isupper(*p))	i = 'A' - 10;
	else			i = 'a' - 10;
	i = *p - i;
	if (i >= base)		break;
	accu = accu * base + i;
  }
  i = p - str;		/* Number of Digits */

  return(i);
}	

/*===========================================================================*/
#ifdef __STDC__
static int Sign(char *str, int len)
#else
static int Sign(str, len)
/*++++++
.PURPOSE Just scan the beginning of a string: skip leading blanks,
	and get the sign.
.RETURNS Number of valid characters
.REMARKS Not traced. 
--------*/
	char *str;	/* IN: The string to scan	*/
	int  len;	/* IN: Length of the string	*/
#endif
{
	char *p, *pe;
  
  sign = 0;
  if (len <= 0)		return(0);

  p = str, pe = p + len;
  GobbleSpaces(p, len);
  
  if (p < pe)
  {	switch(*p)
  	{ case '-':	sign = 1;
  	  case '+':	p++; 	break;
  	}
	GobbleSpaces(p, pe-p);		/* Skip blanks */
  }

  return(p-str);
}	

/*===========================================================================*/
#ifdef __STDC__
static int tral(char *str, int len, int *value)
#else
static int tral(str, len, value)
/*++++++
.PURPOSE Converts a string to a int number. 
	Input is assumed to be in decimal, unless the string starts
	with 'X' (hexadecimal) or 'O' (octal).
	The digits may be followed by K (implies a 1024 factor)
.RETURNS Number of digits (excluding spaces) scanned, -1 for 
	error (message logged)
.REMARKS Return 0 if string contains only spaces or a sign. 
	Value is then set to NULL4.
--------*/
	char	*str;	/* IN: String to scan	*/
	int	len;	/* IN: Length of str	*/
	int	*value;	/* OUT: result		*/
#endif
{
	char 	*p, *pe;
	int	nd;

  nd   = 1;
  accu = 0;
  errmsg = (char *)0;
    
  p = str + Sign(str, len);	/* Skip trailer + sign	*/
  pe = str + len;
  
  if (*p == '\'')		/* Get a Character */
  {	p++;
	if (*p != '\'')	accu = *(p++);
	if (*p == '\'')	p++;
	goto check_remaining_bytes;
  }
  if (*p == '^')		/* Get a Control Character */
  {	p++;
  	accu = tocntrl(*p), p++;
	goto check_remaining_bytes;
  }

  nd = Number(p, pe-p);		/* Get number		*/
  p += nd;			/* End of number	*/

	/* If the string is not completely understood, look if
	   it can be due to O(octal), X(hexadecimal), k (k-factor)
    	   Note that X and O can only be at the beginning, i.e.
    	   when accu == 0; k can only be a suffix.
	 */

  if (p < pe)	switch(toupper(*p)) { 
    case 'O':	base = 8;
  	if (accu == 0)	goto special_base;
  	break;
    case 'X':	base = 16;
  	if (accu == 0)	goto special_base;
	break;
    special_base:
	p++;
	nd = Number(p, pe-p);
	p += nd;
	break;
    case 'K':	
    	accu *= 1024;
    	p++;
	break;
  }

	/* Take Sign into account, and check remaining bytes are only
	   white spaces
	 */

  check_remaining_bytes:
  if (nd > 0) 	*value = (sign ? -accu : accu);
  else		toNULL4(value);

  stopped = p;

  return(nd);
}
  
/*===============================================================
 * 	The following Conversion Routines are extracted from 
 *			tc3lib / traf.c
 *================================================================*/
 

/*===========================================================================*/
#ifdef __STDC__
static int traf(char *str, int len, double *value)
#else
static int traf(str, len, value)
/*++++++
.PURPOSE Converts a string to a floating point.
	Input is assumed to be in decimal.
.RETURNS Number of significant digits / -1 for error 
.REMARKS Return 0 if string contains only spaces.
	Stopped index returned in static `stopped'.
	The static has_decimals is not-zero if decimals found.
--------*/
	char	*str;	/* IN: String to scan	*/
	int	len;	/* IN: Length of str	*/
	double	*value;	/* OUT: result		*/
#endif
{
	char 	*p, *pe, *ps, x, something;
	int	i, nd;
	double	atof();
  
  errmsg = (char *)0;
  has_decimals = 0;
  
  nd = 0, something = 0;
  p = str, pe = p + len;
  GobbleSpaces(p,len);

  if (p == pe)	{ errmsg = "Wrong Input"; goto error; }

  ps = p;			/* Starting point of string */
  if (issign(*p))	p++;
  if (p == pe)	goto error;		/* Zero	*/

  if (something == 0)	something = *p;
  if (*p != '0') p += oscskip(p, pe-p, '0');	/* Skip leading zeroes */
                                                /* MP 060391 */
  if (p == pe)	goto error;		/* Zero	*/
  
  nd = oscspan((unsigned char *)p, pe-p, _DIGIT_, main_ascii), p += nd;
  if (p == pe)	goto get_value;
  
  if (*p == '.')	has_decimals = 1, p++;
  if (nd == 0)	{		/* Skip leading zeroes	*/
	if (something == 0)	something = *p;
	p += oscskip(p, pe-p, '0');
  }
  i = oscspan((unsigned char *)p, pe-p, _DIGIT_, main_ascii);	/* Decimals */
  p += i, nd += i;

	/* Look for Exponent */

  if ((p < pe) && (isalpha(*p)))
  {	x = toupper(*p);
  	if ((x == 'E') || (x == 'D'))	/* Exponent */
  	{	if (p == ps)	errmsg = "Missing exponent";
  		p++;
  		if (issign(*p))	p++;
  		p += oscspan((unsigned char *)p, pe-p, _DIGIT_, main_ascii);
  	}
  }

 get_value:
  if (p == ps)	errmsg = "No data"; 
  x = *p, *p = '\0', *value = atof(str), *p = x;
  
 error:
  if ((nd == 0) && (has_decimals))	nd = 1;
  if (nd == 0)	toNULLD(value);
  stopped = p;
  return(nd);
}
  
/*===============================================================
 * 	The following Conversion Routines are extracted from 
 *			as3lib / traq.c  (Sexagesimal)
 *================================================================*/
 
/*===========================================================================*/
#ifdef __STDC__
static int trasex(char *str, int l, double *value)
#else
static int trasex(str, l, value)
/*++++++
.PURPOSE Converts a string to a double value; input may be
	in sexagesimal.
.RETURNS The precision, from 0 (degree) to 9 (.00001"), or -1 (error or no data)
.REMARKS No error logging.
--------*/
	char	*str;	/* IN: String to scan	*/
	int	l;	/* IN: Length of str	*/
	double  *value;	/* OUT: result		*/
#endif
{
	char 	*p, *pe;
	int	i, nd, ndec;
	int	hms[3], fraction;
	static  double factor[3] = {1., 60., 3600.};

  p = str, pe = p + l;
  errmsg = (char *)0;
  base = 10;
  nd = ndec = 0;
  hms[0] = hms[1] = hms[2] = fraction = 0;

				/* Extract the Sign, if any	*/
  p += Sign (p, pe-p);

				/* Get the 3 numbers		*/
  for (i=0; (p < pe) && isdigit(*p) && (i<3); i++) {
	p += Number (p, pe-p);
	hms[i] = accu;
	GobbleSpaces(p, pe-p);
	if (p >= pe)	continue;
	if (*p == ':')	p++, GobbleSpaces (p, pe-p);
  }
  if (i == 0)	{ errmsg = "No data"; goto error; }
  
  if ((p < pe) && (*p == '.')) {
	p++; 
  	ndec = Number (p, pe-p);
  	fraction = accu;
  	p += ndec;
  }
						/* Convert */
  nd = 2*i + ndec;
  for (*value = fraction; --ndec >= 0; *value /= 1.e1)	;
  *value /= factor[--i];
  while (i >= 0)	*value += hms[i] / factor[i], i--;

 error:
  if (sign)	*value = -(*value);
  if (nd == 0)	toNULLD(value);
  stopped = p;
  return(nd);
}  

/*===============================================================
 * 	The following Conversion Routines are extracted from 
 *			tc3lib / tratime.c
 *================================================================*/

/*===========================================================================*/
#ifdef __STDC__
static int tratm(char *str, int len, struct tm *T)
#else
static int tratm(str, len, T)
/*++++++
.PURPOSE Converts a string to a date/time; input can be 
	YYYY MM DD hh mm ss, or MMM DD YYYY hh mm ss,
	with month alphabetic or numeric.
.RETURNS -1 if error (error logged) / Number of Items present, from 1 (only 
	year) to 6 (year + month + day + hours + min + sec)
.REMARKS An empty string gives the current date/time
--------*/
	char	*str;	/* IN: String to scan	*/
	int	len;	/* IN: Length of string to scan	*/
	struct tm *T;	/* OUT: the date structure	*/
#endif
{
	char 	*p, *pe;
	int	k, i;
	int	l;

  p = str, pe = p + len;
  errmsg = (char *)0;
  base   = 10;

  p += oscspan((unsigned char *)p, pe-p, _SPACE_|_PUNCT_, main_ascii);	/* Skip blanks */
  
  if (p == pe)	/* Empty String: Get Current Date / Time */ {
  	tr_itm((int)oshtime(), T);
	k = 0;
  	goto error;
  }
  
				/* Initialize the structure to zero	*/
  oscfill((char *)T, sizeof(struct tm), 0);
  T->tm_mon = -1;		    /* ... but with a bad month		*/
  
  for (k=0;  p < pe; p += l + oscspan((unsigned char *)(p+l), pe-p, _SPACE_|_PUNCT_, main_ascii)){
  	if (k >= 6)		break;	/* We have all items!		*/
	if (isdigit(*p))	{	/* A number comes now ...	*/
		l = Number(p, pe-p);
		i = accu;		/* ... get it ...		*/
		switch(k) {
		  case 1:		/* Month number or day number	*/
			if (T->tm_mon < 0)	T->tm_mon  = i-1;
			else			T->tm_mday = i;
			break;
		  case 0: 		/* Year or Day			*/
		  	if (i <= 31)	T->tm_mday = i;
		  	else		T->tm_year = i;
			break;
		  case 2:		/* Year or Day			*/
		  	if (T->tm_mday == 0)	T->tm_mday = i;
		  	else			T->tm_year = i;
		  	break;
		  case 3:		/* Hours	*/
		  	T->tm_hour = i;	break;
		  case 4:		/* Hours	*/
		  	T->tm_min = i;	break;
		  case 5:		/* Hours	*/
		  	T->tm_sec = i;	break;
		}
		k++; continue;
	}
	if (isalpha(*p))	{	/* A text comes now ...		*/
		l = oscspan((unsigned char *)p, pe-p, _ALPHA_, main_ascii);
		if (k >= 2)		continue;	/* Useless...	*/
		for (i=1; i < sizeof(month_list); i += 4)
			if (osccomp(&month_list[i], p, 3) == 0)	break;
		if (i >= sizeof(month_list))	{	/* Not Found...	*/
			if (k > 0)	k++;
			continue;
		}
		T->tm_mon = month_list[i-1];
		k++; continue;
	}
	break;				/* Control  character ??	*/
  }
  p += oscspan((unsigned char *)p, pe-p, _SPACE_, main_ascii);	/* Strip trailing	*/
  tr_tm(T);					/* Add missing info	*/

 error:
  stopped = p;
  return(errmsg ? -1 : k);
}  

#ifdef __STDC__
static int trat(char *str, int len, int *t)
#else
static int trat(str, len, t)
/*++++++
.PURPOSE Converts a string to a date/time; input can be 
	YYYY MM DD hh mm ss, or MMM DD YYYY hh mm ss,
	with month alphabetic or numeric.
.RETURNS Length of the scanned text / -1 if error (error logged)
.REMARKS An empty string (value 0 returned) gives the current date/time
--------*/
	char	*str;	/* IN: String to scan	*/
	int	len;	/* IN: Length of string to scan	*/
	int	*t;	/* OUT: the date (seconds since 1970)	*/
#endif
{
	struct 	tm T;
	int	status;
	
  *t = 0x80000000;
  if (len > 0)	status = tratm(str, len, &T);
  else		status = 0;

  if(status == 0)	*t = oshtime();
  else	if (status > 0) 
  {
  	if (T.tm_year < 100)
	{	
	   /*T.tm_year += 1900; Two digits for year are not enough!*/
	   printf("Ambiguous entry for year! Please use always 4 digits.\n");
	   printf("However, for these two digits, 1900 will be added now.\n");
	   T.tm_year += 1900;
	}   
  	if (tr_tmi(&T, t) < 0)	status = -1;
  }
  
  return(status);
}

#ifdef __STDC__
static int fmtdec(char *str)
#else
static int fmtdec(str)
/*++++++++++++++++++++
.PURPOSE Extract the number of decimals in a Format
.RETURNS Number of decimals
---------------------*/
	char	*str;	/* IN: String to scan	*/
#endif
{
	char *p;
     
  for (p = str; *p && (*p != '.'); p++) ;
  return (*p ? atoi(p+1) : 0);
}
	
/*==========================================================================
 *		Conversion according to Format
 *==========================================================================*/

#ifdef __STDC__
static int toLong(char *str, int len, char *f, int *value)
#else
static int toLong(str, len, f, value)
/*++++++++++++++++++++
.PURPOSE Convert a string to a Long according to Format
.RETURNS Number of significant digits / 0 for Error
.REMARKS Sexagesimal values are given in marcsec.
---------------------*/
	char	*str;	/* IN: String to scan	*/
	int	len;	/* IN: Length of str	*/
	char	*f;	/* IN: Format		*/
	int	*value;	/* OUT: result		*/
#endif
{
	double	aDouble;
	int	status, round, E;
	
  base = 10;
  toNULL4(value);
  switch(*f) {
    case 'R': case 'r':	
    	status = trasex (str, len, &aDouble);
    	if (status > 0)	*value = aDouble * 15.e0 * 3.6e6;
	break;
    case 'S': case 's':	
    	status = trasex (str, len, &aDouble);
    	if (status > 0)	*value = aDouble * 3.6e6;
	break;
    case 'T': case 't':	
    	status = trat(str, len, value);
	if (status == 0) {		/* Adjust to e.g. integer days */
		switch(fmtdec(f)/2) {
		  case 0: round = 86400; break;
		  case 1: round =  3600; break;
		  case 2: round =    60; break;
		  default: round =    1; break;
		}
		E = *value/round; 	/* Adjust for Negative times */
		if (*value < round*E)	E--;
		*value = E*round;
	}
	break;
    case 'O': case 'o':
    	base = 8;	
    convert_int:
	status = tral (str, len, value);
	break;
    case 'X': case 'x':
    	base = 16;
	goto convert_int;
    default:
    	base = 10;
	goto convert_int;
  }
  return(status);
}

#ifdef __STDC__
static int toDouble(char *str, int len, char *f, double *value)
#else
static int toDouble(str, len, f, value)
/*++++++++++++++++++++
.PURPOSE Convert a string to a Long according to Format
.RETURNS Number of significant digits / 0 for Error
.REMARKS 
---------------------*/
	char	*str;	/* IN: String to scan	*/
	int	len;	/* IN: Length of str	*/
	char	*f;	/* IN: Format		*/
	double	*value;	/* OUT: result		*/
#endif
{
	int	aLong;
	struct  tm T;
	int	status;
	
  toNULLD(value);
  switch(*f) {
    case 'R': case 'r':	
    	status = trasex (str, len, value);
    	if (status > 0)	*value *= 15.e0;
    	break;
    case 'S': case 's':	
    	status = trasex(str, len, value);
    	break;
    case 'T': case 't':	
    	status = tratm (str, len, &T);
	if (status == 0)  {	/* Current value. Remove useless items */
		switch(fmtdec(f)) {
		  case 0: T.tm_hour = 0;
		  case 1: T.tm_min  = 0;
		  case 2: T.tm_sec  = 0;
		  break;
		}	
	}
    	if (status >= 0)	*value = tmtojd (&T);
	break;
    case 'O': case 'o':
    	base = 8;
    convert_int:
	status = tral (str, len, &aLong);
    	if (status > 0)	*value = aLong;
	break;
    case 'X': case 'x':
    	base = 16;	
	goto convert_int;
    default:	
	status = traf (str, len, value);
  }
  return(status);
  
}

/*==========================================================================
 *		Public Routines
 *==========================================================================*/

int TBL_cv(buf, form, dtype, addr)
/*++++++++++++++++++++
.PURPOSE Convert a string according to F77 format
.RETURNS Status
.REMARKS The array is NOT filled with NULL values, which means
	that ONLY the values which are present are converted.
	Elements in an array column may be separated by , or ;
---------------------*/
	char	*buf;	/* IN: Edited string		*/
	char	*form;	/* IN: F77 Format 		*/
	int	dtype;	/* IN: Data type in Table Format */
	char 	*addr;	/* OUT: Converted Value(s)	*/
{
	char	*p, *pe;
	int	len, items, status, etype, elen;
	double	aDouble;
	int	aLong;


  etype = TBL_ElementType(dtype);
  elen  = (1<< ( etype & 3));
  items = TBL_Items(dtype);
  status = 0;
  errmsg = (char *)0;
  len    = strlen(buf);

  if (etype == TBL_D_A1) {	/* Character String	*/
    	if (++len > items) len = items;	
    	oscopy(addr, buf, len); 
    	return (ERR_NORMAL);
  }
  
  for (p = buf, pe = p + len; 
      (!errmsg) && (p < pe) && (--items >= 0); addr += elen) {
      	if (etype & 020)
      		status = toDouble(p, pe-p, form, &aDouble);
      	else	{
	      	status = toLong  (p, pe-p, form, &aLong);
	      	if ( (aLong < llim[etype&3]) || (aLong > ulim[etype&3]) )
	      		errmsg = "integer outside range";
		if ((status == 0) && ((etype & 010) == 0))	/* A1 A2 A4 */
			aLong = 0;
      	}
      	/* if (status == 0) TBL_errf(-1, toupper(*form) == 'T' ?
      		"Missing date, assumed to be NOW"  :
      		"Missing number, assumed to be NULL." );
	*/

	switch (etype) {
	  case TBL_D_I1: 
		if (aLong == NULL4)	aLong = NULL1;
	  	*addr = aLong;		break;
	  case TBL_D_I2:
		if (aLong == NULL4)	aLong = NULL2;
	  case TBL_D_A2:
	  	*(short *)addr  = aLong;	break;
	  case TBL_D_A4:
	  case TBL_D_I4:
	  	*(int *)addr   = aLong;	break;
	  case TBL_D_R4:
	  	if (status > 0)	*(float *)addr  = aDouble;	
	        else toNULLF(addr);	        break;
	  case TBL_D_R8:
	  	*(double *)addr = aDouble;	break;
	}
      	p = stopped;
	GobbleSpaces(p, pe-p);		/* Skip blanks */
	if ( (*p == ',') || (*p == ';'))	p++;
      		
  }

					/* Fill remaining items with 0	*/
  if (items > 0)	
	oscfill (addr, items*elen, 0);

					/* Was something not understood?*/
  if ((!errmsg) && (p != pe))
  	errmsg = "non-numeric character ";

  status = (errmsg ? ERR_TBLFMT : ERR_NORMAL);
/*  if (status)	TBL_errf(status, "%s in '%s'", errmsg, buf); */
  
  return (status);
}

/*==========================================================================
 *			edT
 *==========================================================================*/
#ifdef __STDC__
static int edT(char *buf,char *pic, struct tm *T)
#else
static int edT(buf, pic, T)
/*+++
.PURPOSE Edit the structure describing time & date (localtime)
.RETURNS Length of edited date / time
---------*/ 
	char *buf;		/* OUT: Output buffer	*/
	char *pic;		/* IN: Used picture	*/
	struct tm *T;		/* IN: Decomposed time	*/
#endif
{
	char   	*p ,*a;
	int	len, dec, ny, n;

  
  len = atoi(pic);
  dec = fmtdec(pic);
  if (dec > 6)	dec = 6;
  dec &= ~1;	/* Even number of decimals	*/
  
  a = buf + len;	*a = EOS;

  if (dec >= 6)		a = ed60(a, T->tm_sec, ':');
  if (dec >= 4)		a = ed60(a, T->tm_min, ':');
  if (dec >= 2)		a = ed60(a, T->tm_hour, ':');

  ny = len - dec - dec/2 - 6;	/* Digits for Year edition	*/
			/* The last byte indices litteral month	*/
  for (p = pic; ischar(*p, _PUNCT_|_DIGIT_); p++)	;
  if (isalpha(*p))	ny--;
  if ((T->tm_year < 10000) && (ny > 4))	ny = 4;
  
  if (isalpha(*p))	{	/* Alphabetic Edition DD-MMM-YYYY */
	if (T->tm_year >= 0)
		for (n = T->tm_year; --ny >= 0; n /= 10) *--a = '0' + n%10;
	else	while (--ny >= 0) *--a = '*';
	if (a > buf)	*--a = '-';
	
	if ((a-buf) >= 6)  {
		a -= 3;
		oscopy (a, &month_list[4*T->tm_mon+1], 3);
		*--a = '-';
	}
	if ((a-buf) >= 2)  {
		*--a = '0' + T->tm_mday%10;
		*--a = '0' + T->tm_mday/10;
	}
	if ((a-buf) >= 4)  {	/* Add Weekday ... */
		*--a = ':';
		a -= 3;
		oscopy (a, &wday_list[3*T->tm_wday], 3);
	}
  }
  else  {				/* Numeric Edition	*/
	if ((a-buf) >= 3)	a = ed60 (a, T->tm_mday, '-');
	if ((a-buf) >= 3)	a = ed60 (a, T->tm_mon+1, '-');
	n = T->tm_year; 	ny = 0;
	if (n < 0)	n = -n, ny = 1;
	for ( ; (a > buf) && (n); n /= 10)
		*--a = '0' + n%10;
	if (ny)	{
		if (a > buf)	*--a = '-';
		else		*buf = '*';	/* Error */
	}
  }

					/* Fill leading blanks	*/
  while (a > buf)	*--a = ' ';

  return(len);
}
	
int TBL_ed(buf, form, dtype, addr)
/*++++++++++++++++++++
.PURPOSE Edit a value according to F77 format
.RETURNS Status
.REMARKS NULL values are edited as NOTHING (two consecutive commas). 
	A comma is inserted between items in an array.
---------------------*/
	char	*buf;	/* OUT: Edited string	*/
	char	*form;	/* IN: F77 Format 	*/
	int	dtype;	/* IN: Data type	*/
	char 	*addr;	/* IN: Value to edit	*/
{

	char	*Cform, isnull, sep_items, *p, *f;
	int	len, items, etype, elen;
	double	aDouble;
	int	aLong;
	struct tm T;

aLong = 0;
aDouble = 0.0;
isnull = '\0';

  items = TBL_Items(dtype);
  etype = TBL_ElementType(dtype);
  elen  = (1<< ( etype & 3));
  len   = atoi(form+1);

  if (etype == TBL_D_A1) {	/* Character String	*/
	if (len > items)	len = items;
  	oscopy(buf, addr, len); 
  	buf[len] = '\0';
    	return (ERR_NORMAL);
  }

  Cform = TBL_Cfmt(form, dtype);
  sep_items = ',';

  for (; --items >= 0; addr += elen) {
  	switch (etype) {
	    case TBL_D_I1:  
		aLong = *addr;
		isnull = (aLong == NULL1);	break;
	    case TBL_D_I2:  
		aLong = *(short *)addr;		
		isnull = (aLong == NULL2);	break;
	    case TBL_D_A2:  
		aLong = *(unsigned short *)addr;
		isnull = (aLong == 0);		break;
	    case TBL_D_A4:
		aLong = *(int *)addr;		
		isnull = (aLong == 0);		break;
	    case TBL_D_I4:  
		aLong = *(int *)addr;		
		isnull = (aLong == NULL4);	break;
	    case TBL_D_R4:  
                isnull = isNULLF(addr);
		if (!isnull)	aDouble = *(float *)addr;	
						break;
	    case TBL_D_R8:  
		aDouble = *(double *)addr;	
                isnull = isNULLD(addr);         break;
	}
/*	if (isnull) *buf = '\0'; */
        if (isnull) {
            oscfill(buf,len,' ');
            buf[len-1] = '*';
            }
	else for (f = Cform; *f; f++) {
	  	switch(*f) {
		  case 'H':	aDouble /= 15.;
		  	continue;
		  case '%':
			if (etype & 020)	sprintf(buf, f, aDouble);
		  	else			sprintf(buf, f, aLong);
		  	f += strlen(f);	
		  	continue;
		  case 'Z':	
		  	for (p = buf; *p == ' '; p++)	*p = '0';
		  	if (*p == '-')	*p = '0', *buf = '-';
		  	continue;
		  case '+':	
		  	for (p = buf; *p == ' '; p++)	;
		  	if (*p == '-')	continue;
		  	if (p > buf)  p--;
		  	if ((*p == '0') || (*p == ' '))	*p = '+';
		  	continue;
		  case 'S':	/* Sexagesimal	*/
			if (etype & 020)	;
			else	aDouble = aLong / 3.6e6;
		  	ed_sexa (buf, f+1, aDouble);
		  	f += strlen(f);	
		  	continue;
		  case 'T':	/* Time		*/
			if (etype & 020)	tr_jdtm (aDouble, &T);
		  	else			tr_itm (aLong, &T);
		  	edT (buf, f+1, &T);
		  	f += strlen(f);	
		  	continue;
	  	}
	}
	if (items > 0)	{
		buf += strlen(buf);
		*(buf++) = sep_items;
	}
  }

  return(ERR_NORMAL);
}

