/*===========================================================================
  Copyright (C) 1988-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 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Corresponding 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
===========================================================================*/

/*+++++++++ 
.MODULE    trpic.c
.AUTHOR    Francois Ochsenbein [ESO]
.LANGUAGE  C
.CATEGORY  Conversion from character to binary using a picture.

.COMMENTS
	Definition of pictures are in edpic.c


.VERSION   1.0	27-May-1988: Creation
.VERSION   1.1	08-Jul-1988: Modified for separator = '.':
		following is left-adjusted...
.VERSION   1.2	21-Oct-1988: Cosmetic Modifications.
.VERSION   1.3	13-Jun-1989: Allow dashes (-) in middle of a number
.VERSION   1.4	22-Mar-1990: Added stkpic (tokenisation)
.VERSION   1.5	01-Aug-1990: Modified tr_pic, to allow e.g. +995959 pictures
			(no blanks embedded)

 090831		last modif
-----------------------------------------*/
 
#include <midas_def.h>
#include <macrogen.h>
#include <tra.h>
#include <atype.h>
#include <string.h>

extern int  tr_error();

MID_EXTERN struct trerr_s	*trerror;

#define LEN	132

#define FINISH			goto FIN
#define GobbleSpaces(s,l)	s += oscspan((unsigned char *)s, l, _SPACE_, main_ascii)

MID_STATIC	long 	accu;
MID_RSTATIC	long	p10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
			100000000, 1000000000};
MID_STATIC  int	base = 10;
MID_STATIC	char	sign;
MID_STATIC	char	stk = 0;	/* Indicator for stkpic	*/

/*===========================================================================*/
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	*/
{
	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);
}	

/*===========================================================================*/
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	*/
{
	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);
}	

/*===========================================================================*/
int tr_pic(str, len, pic, value)
/*++++++
.PURPOSE Converts a string to a long number according to a picture
	(see module edpic.c)
.RETURNS Length of matched characters in picture / -1 for error or no number
.REMARKS returned value is e.g. 101 for 1.01 under picture 9.99
--------*/
	char	*str;	/* IN: String to scan	*/
	int	len;	/* IN: Length of str	*/
	char	*pic;	/* IN: The picture	*/
	long	*value;	/* OUT: result		*/
{
	char 	*p, *pe, *a, *astop, b, separator, sign_found;
	int	i, nd, nda;
	long 	limit;

  b = ' ' ;
  p = str, pe = p + len;
  GobbleSpaces(p, len);
  *value 	= 0;
  sign 		= 0;
  separator 	= 0;
  sign_found 	= 0;
  trerror->errno= 0;
  
  for (a = pic, astop = a; *a; a += i, p += nd) {
  	/* if ((p < pe) && (*p == '&'))	pe = p; */
  	i = 0, nd = 0, limit = 0, base = 0;
  	switch(*a)
  	{ case '+': a++;	/* No digit allowed */
		if (sign_found)	goto case_other;
		i = oscskip(a, LEN, '+');
		goto case_sign;
  	  case '-':		/* Look for sign */
		if (sign_found)	goto case_other;
		if (*p == '-')	a++;
		i = oscskip(a, LEN, '-');
	  case_sign:
		sign_found = 1;
  		p += Sign(p, pe-p);
		b = '9';
		if (a[i] == '0') 	goto case_0;
		goto case_other;
#if 0
  	  case '&':		/* It's a stop	*/
		GobbleSpaces(p, pe-p);	/* Skip trailing spaces */
		pe = p;
		FINISH;
#endif
  	  case ' ':
		GobbleSpaces(p, pe-p);	
		break;

	  case '\\':	i = 2;		/* Escape */
	  	if ((p < pe) && (*p != a[1]))	goto BAD;
		nd = 1;
	  	break;

	  case_hexa:
	  case 'X':
		sign_found = 1;
		i += oscskip(a, LEN, 'X');
		if (i < 8)	limit = 1 << (4*i);
	  	base = 16;
	  	break;

	  case_0:
	  case '0':	
		sign_found = 1;
		i += oscskip(a, LEN, '0');
		b = '9';	/* Default base	     */

	  case_other:
	  default:
		if ((i > 0) && (a[i] == 'X'))
			goto case_hexa;
		if (isdigit(a[i])) 
		{ 	sign_found = 1; 
			b = a[i++];
			while (b == a[i]) 	i++;
		}
		if (i > 0)		/* Must get a number ... */
		{	base = b - '0' + 1;
			if ((isdigit(*a)) && (*a != '0'))
				Number(a, i), limit = accu + 1;
			else if ((base == 10) && (i < ITEMS(p10)))
				limit = p10[i];
			break;
	  	}
	  	if (p >= pe) 	break;
		i = 1;
		if (isspace(*p))		/* Skip trailing spaces */
		{	GobbleSpaces(p, pe-p);	
			/* if (*p == '&')	pe = p; */
		}
		else	nd = 1;
	  	if (p >= pe) 	break;
		if (*a == '.') 	sign_found = 1; 
		if (*p != *a)	goto BAD;
		separator = *p;
		nd = 1;
  	}
	if (i == 0)	i = oscskip(a, LEN, *a);
	nda = i;	/* Number of bytes taken into account in picture */
	if (base)	/* Must get a number...	*/
	{	nd = Number(p, MIN(nda, pe-p));
		if (nd > i)	/* Too many digits */
			FINISH;
		if (limit)
		{	if (accu >= limit)	FINISH;	/* Bad... */
			*value = *value * limit;
		}
		if ((separator == '.') && (base == 10))
					/* Decimal part left adjusted... */
			accu *= p10[i-nd], nda = nd;
		*value += accu;
	}
	if (p < pe)	astop = a+nda;
  }
  FIN:
  GobbleSpaces(p, pe-p);		/* Skip trailing spaces */

  BAD:	
	/* We've finished... When stkpic was called, we've to return
	   the length of the matched string --- otherwise the
	   length of the matched pattern (picture)
	*/

  if (stk)	return(p - str);	/* Called from stkpic	*/

  if (sign)	*value = -(*value);

  if (p < pe)	/* Bad number */
  {	trerror->errno = TRERR_UNDEF;
  	trerror->msg = pic;
  	trerror->str = str;
	trerror->len = len;
	trerror->offset = (p-str);
	tr_error();
	i = -1;
  }
  else	i = astop - pic;

  return(i);
}

/*===========================================================================*/
int stkpic(str, pic)
/*++++++
.PURPOSE Compute the length of the tokenized string
.RETURNS Length of str matched for specified picture
.REMARKS 
--------*/
	char	*str;	/* IN: String to scan	*/
	char	*pic;	/* IN: The picture	*/
{
	int	status;
	long	value;
	
    stk = 1;	/* Tells tr_pic that we just tokenize */
    status = tr_pic (str, strlen(str), pic, &value);
    stk = 0;
    
    return(status);
}
