/*---------------------------------------------------------------------------
 *		
 * MODULE NAME:	strtoX
 *
 * DESCRIPTION:	Routines for converting between strings and integers,
 *		doubles, vectors, and binary strings.
 *
 * CAVEATS:	Some of these routines are provided as standard on some UNIX
 *		systems. For instance, 'strtol()' and 'strtod()' as provided
 *		as part of SunOS.
 *
 *		strtod() does not create the actual floating point number;
 *		it merely checks the syntax of the strinf and then calls
 *		atof().
 *
 * AUTHOR/DATE:	John Lloyd, fall 1990
 *
 * MODIFICATION HISTORY:
 *
 *--------------------------------------------------------------------------*/

#include <ctype.h>
#include <strings.h>

#include "../h/cdefs.h"

#if defined(vax) || defined(VXWORKS_CFG)

static validDigit();

long strtol(str, ptr, base)
char *str;
char **ptr;
int base;
{
	/* Convert a string into a long integer, and return an optional
	   pointer to the place in the string where the conversion ended. For
	   the complete specification of how this routine works, check the
	   manual page of strtol() on SunOS. */

	register char *cp;
	long result;
	int negate;
	int leadingZero;
	int leading0x;
	int k;

	result = 0;
	negate = 0;
	leadingZero = 0;
	leading0x = 0;

	/* skip leading white space */
	for (cp=str; isspace(*cp); cp++)
	   ;
	if (*cp == '-')
	 { negate = 1;
	   cp++;
	 }
	if (*cp == '0')
	 { leadingZero = 1;
	   cp++;
	 }
	if (base == 16 && (*cp == 'x' || *cp == 'X') && leadingZero)
	 { leading0x = 1;
	   cp++;
	 }
	else if (base <= 0)
	 { /* we don't know the base. Have to figure it out */
	   if (leadingZero && (*cp == 'x' || *cp == 'X'))
	    { base = 16;
	      leading0x = 1;
	      cp++;
	    }
	   else if (leadingZero)
	    { base = 8;
	    }
	   else
	    { base = 10;
	    }
	 }
	if ((k = validDigit(*cp, base)) >= 0)
	 { result = k;
	   while ((k = validDigit(*++cp, base)) >= 0)
	    { result = result*base + k;
	    }
	 }
	else if (!leadingZero || leading0x)
	 { cp = str;	/* mark invalid conversion */
	 }
	if (ptr)
	 { *ptr = cp;
	 }
	return (negate ? -result : result);
}

static validDigit(c, base)
register char c;
register int base;
{
	register int k;

	if (c >= '0' && c <= '9')
	 { if ((k = c - '0') < base)
	    { return (k);
	    }
	 }
	else
	 { if ((k = (c - 'a' + 10)) < base && k >= 10)
	    { return (k);
	    }
	   else if ((k = (c - 'A' + 10)) < base && k >= 10)
	    { return (k);
	    }
	 }
	return (-1);
}

#if !defined(VXWORKS_CFG)
extern double atof();
#else		/* GRRR! */
double atof(str)
char *str;
{
	double r;
	sscanf (str, "%lf", &r);
	return (r);
}
#endif

double strtod(str, ptr)
char *str;
char **ptr;
{
	/* Convert a string into a double, and return an optional pointer 
	   to the place in the string where the conversion ended. For the
	   complete specification of how this routine works, check the manual
	   page of strtod() on SunOS. */

	register char *cp, *ep;
	int leftOfDecimal;
	int rightOfDecimal;

	leftOfDecimal = 0;
	rightOfDecimal = 0;

	/* skip leading white space */
	for (cp=str; *cp != '\0' & isspace(*cp); cp++)
	   ;
	if (*cp == '-')
	 { cp++;
	 }
	if (isdigit(*cp))
	 { leftOfDecimal = 1;
	   while (isdigit(*++cp))
	      ;
	 }
	if (*cp == '.')
	 { cp++;
	 }
	if (isdigit(*cp))
	 { rightOfDecimal = 1;
	   while (isdigit(*++cp))
	      ;
	 }
	if (!leftOfDecimal && !rightOfDecimal)
	 { cp = str;
	   goto finish;
	 }
	if (*cp != 'e' && *cp != 'E')
	 { goto finish;
	 }
	ep = cp+1;	/* swallow exponent */
	if (*ep == '-')
	 { ep++;
	 }
	if (isdigit(*ep))
	 { while (isdigit(*++ep))
	      ;
	   cp = ep;
	 }
finish:
	if (ptr)
	 { *ptr = cp;
	 }
	return (atof(str));
}

#else
extern double strtod();
#endif

#ifdef VXWORKS_CFG
#undef max
#undef min
#endif

int strtoVd(str, v, max, mask, ptr)
char *str;
double *v;
int max;
long *mask;
char **ptr;
{
	/* Convert a string into a vector of doubles, and return an optional
	   pointer to the place in the string where the conversion ended.
	
	   str -	the string to convert.

	   v -		the vector to place the floating point values into.

	   max -	the maximum size of the vector.

	   mask -	an optional mask describing which values in the vector
			actually had defined values.

	   ptr -	an optional pointer used to return the place in the
			string where the conversion ended.

	   The routine returns the size of the vector. The routine returns -1
	   if a valid converison can't be performed, or the vector is larger
	   than the maximum allowed size.

	   A vector is defined by a set of real numbers, separated by commas,
	   and located between angle brackets ('<' '>'). The string

			< 1.0, 2.3, 4.0 >

	   defines a vector of length 3, with values 1, 2.3, and 4. Individual
	   values within the vector may be omitted, so that 

			< 1.0, , 4.0 >
	   
	   defines a vector of length 3 with the 2nd value omitted. If a 
	   value is omitted, the corresponding location in the return vector
	   is left unchanged, and the corresponding bit in the 'mask' 
	   argument (if provided) is set to 0. 

	   White space is permitted at the beginning of the string, and
	   between the brackets, commas, and numbers, but not within the
	   numbers.
	 */

	long set;
	int cnt;
	int state;
#define SVD_START		0
#define SVD_INSIDE_BRACKET	1
#define SVD_AFTER_NUMBER	2
#define SVD_AFTER_COMMA		3

	register char *cp;
	char *pp;

	if (mask)
	 { *mask = 0;
	 }
	if (ptr)
	 { *ptr = str;
	 }
	if (max > 8*sizeof(long))
	 { return (-1);
	 }

	state = SVD_START;
	set = 0;
	cnt = 0;
	cp = str;

	while (1)
	 { while (isspace(*cp))
	    { cp++;
	    }
	   switch (state)
	    { case SVD_START:
	       { if (*cp == '<')
		  { state = SVD_INSIDE_BRACKET;
		    cp++;
		  }
		 else
		  { return(-1);
		  }	
		 break;
	       }
	      case SVD_INSIDE_BRACKET:
	       { if (*cp == '>')
		  { goto svf_end;
		  }
		 else if (*cp == ',')
		  { cp++;
		    cnt++;
		    state = SVD_AFTER_COMMA;
		  }
		 else
		  { v[cnt] = strtod(cp,&pp);
		    if (pp != cp)
		     { cp = pp;
		       set |= (1<<cnt);
		       cnt++;
		       state = SVD_AFTER_NUMBER;
		     }
		    else
		     { return (-1);
		     }
		  }
		 break;
	       }
	      case SVD_AFTER_NUMBER:
	       { if (*cp == '>')
		  { goto svf_end;
		  }
		 else if (*cp == ',')
		  { cp++;
		    state = SVD_AFTER_COMMA;
		  }
		 else
		  { return (-1);
		  }
		 break;
	       }
	      case SVD_AFTER_COMMA:
	       { if (*cp == '>')
		  { if (cnt++ >= max)
		     { return (-1);
		     }
		    goto svf_end;
		  }
		 else if (*cp == ',')
		  { if (cnt++ >= max)
		     { return (-1);
		     }
		    cp++;
		  }
		 else
		  { v[cnt] = strtod(cp,&pp);
		    if (pp != cp)
		     { cp = pp;
		       set |= (1<<cnt);
		       if (cnt++ >= max)
			{ return (-1);
			}
		       state = SVD_AFTER_NUMBER;
		     }
		    else
		     { return (-1);
		     }
		  }
		 break;
	       }
	    }
	 }
svf_end:
	if (mask)
	 { *mask = set;
	 }
	if (ptr)
	 { *ptr = cp+1;
	 }
	return (cnt);
}

int strtoVf(str, v, max, mask, ptr)
char *str;
float *v;
int max;
long *mask;
char **ptr;
{
	/* This routine is analagous to strtoVd(), only it returns a
	   vector of floats instead of doubles. */

     	double buf[8*sizeof(long)];
	char *myPtr;
	long myMask;
	int cnt;
	int k;

	cnt = strtoVd(str, buf, max, &myMask, &myPtr);

	for (k=0; k<cnt; k++)
	 { if (myMask & (1<<k))
	    { v[k] = buf[k];
	    }
	 }
	if (mask)
	 { *mask = myMask;
	 }
	if (ptr)
	 { *ptr = myPtr;
	 }
	return (cnt);
}

strtos (str, buf, max, ptr)
char *str;
register char *buf;
int max;
char **ptr;
{
	/* Converts a string delimited by quotes ("") into an internal 
	   binary string, applying the conversion rules used by C,
	   and return an optional pointer to the place in the string where the
	   conversion ended. The string is terminated by a null character
	   ('\0'). 
	
	   str -	the string to convert.

	   buf -	the buffer to place the binary string in.

	   max -	the maximum size of the buffer, including space
			for the terminating null character.

	   ptr -	an optional pointer used to return the place in the
			string where the conversion ended.

	   The routine returns the size of the string. It returns -1
	   if a valid converison can't be performed, or the string is larger
	   than the buffer size.
       
	   The string is delimited by quotes (""). Quotes within the string
	   are escaped using a backslash (\"). Other escape sequences are
	   used to delimit escape itself (\\), newline (\n), tab (\t),
	   formfeed (\f), carriage return (\r), and backspace (\b).
	   The C convention for converted \<octalNumber> into the 
	   corresponding character is also observed. 

	   Strings may not continue across new lines unless the new line
	   itself is escaped.
	 */

	int cnt;
	register char *cp;

	if (ptr)
	 { *ptr = str;
	 }
	cp = str;
	cnt = -1;

	while(isspace(*cp))
	 { cp++;
	 }
	if (*cp++ != '"')
	 { return (-1);
	 }
	while (*cp != '"' && *cp != '\n' && *cp != '\0')
	 { if (cp[0] == '\\' && cp[1] == '\n')
	    { cp += 2;
	      continue;
	    }
	   if (++cnt >= max-1)
	    { return (-1);
	    }
	   if (cp[0] == '\\')
	    { if (cp[1] == '"')
	       { buf[cnt] = '"';
		 cp += 2;
	       }
	      else if (cp[1] == '\\')
	       { buf[cnt] = '\\';
		 cp += 2;
	       }
	      else if (cp[1] == 'n')
	       { buf[cnt] = '\n';
		 cp += 2;
	       }
	      else if (cp[1] == 't')
	       { buf[cnt] = '\t';
		 cp += 2;
	       }
	      else if (cp[1] == 'f')
	       { buf[cnt] = '\f';
		 cp += 2;
	       }
	      else if (cp[1] == 'r')
	       { buf[cnt] = '\r';
		 cp += 2;
	       }
	      else if (cp[1] == 'b')
	       { buf[cnt] = '\b';
		 cp += 2;
	       }
	      else if (isdigit(cp[1]) && cp[1] < 56)
	       { int v, i;
		 v = cp[1] - '0';
		 cp += 2;
		 for (i=0; isdigit(*cp) && *cp < 56 && i<2; i++)
		  { v = v*8 + *cp++ - '0';
		  }
		 buf[cnt] = MIN(v,255);
	       }
	      else
	       { buf[cnt] = cp[1];
		 cp += 2;
	       }
	    }
	   else
	    { buf[cnt] = *cp++;
	    }
	 }	   

	if (*cp != '"')
	 { return (-1);
	 }
	if (ptr)
	 { *ptr = cp+1;
	 }
	buf[cnt+1] = '\0';
	return (cnt+1);
}

#if TEST

#include <stdio.h>

main(argc, argv)
int argc;
char **argv;
{
	testStrtoVd();
	testStrtos();
	testStrtol();

	printf ("\nROUTINES OK\n\n");

	exit(0);
}

struct svdTest {
  char *input;
  int max;
  int error;
  int cnt;
  int mask;
  double output[4];
  int idx;
  };

struct svdTest svdTestVals[] = 
 { "<>", 		4,  NO,  0,  0x0, {0.0}, 			0,
   "<123> ", 		4,  NO,  1,  0x1, {123.0},			1,
   "<1,2><1", 		4,  NO,  2,  0x3, {1.0,2.0},			2,
   "<1,2,3,4>>  ",	4,  NO,  4,  0xf, {1.0,2.0,3.0,4.0},		3,
   "<1.0,2.0,3.0,4.0>",	4,  NO,  4,  0xf, {1.0,2.0,3.0,4.0},		0,
   "<,,,4.0>",		4,  NO,  4,  0x8, {1.0,2.0,3.0,4.0},		0,
   "<,4.0>,",		4,  NO,  2,  0x2, {1.0,4.0},			1,
   "<1.0,,,4.0>00",	4,  NO,  4,  0x9, {1.0,2.0,3.0,4.0},		2,
   "<1.0,,3,4.0>\n11",	4,  NO,  4,  0xd, {1.0,2.0,3.0,4.0},		3,
   "<-1.0,,-3,-4.0>",	4,  NO,  4,  0xd, {-1.0,2.0,-3.0,-4.0},		0,

   " <, ,, 4.0>",	4,  NO,  4,  0x8, {1.0,2.0,3.0,4.0},		0,
   "< , 4.0 >.",	4,  NO,  2,  0x2, {1.0,4.0},			1,
   "<1.0 ,, , 4.0>--",	4,  NO,  4,  0x9, {1.0,2.0,3.0,4.0},		2,
   "< 1.0 , , 3,  4.0>",4,  NO,  4,  0xd, {1.0,2.0,3.0,4.0},		0,
   "<,>",		4,  NO,  2,  0x0, {0.0},			0,
   "<1,><123>",		4,  NO,  2,  0x1, {1.0},			5,
   "ggg",		4, YES,  0,  0x0, {0.0},			0,
   "<",			4, YES,  0,  0x0, {0.0},			0,
   "<,a>",		4, YES,  0,  0x0, {0.0},			0,
   "<1,2a>",		4, YES,  0,  0x0, {0.0},			0,

   "<a>",		4, YES,  0,  0x0, {0.0},			0,
   "<1,2]",		4, YES,  0,  0x0, {0.0},			0,
   "<1,- 2>",		4, YES,  0,  0x0, {0.0},			0,
   "<1,2,3,4,5>",	4, YES,  0,  0x0, {0.0},			0,
   "<,,,,>",		4, YES,  0,  0x0, {0.0},			0,
 };

#define EPSILON (1e-7)

testStrtoVd()
{
	double buf[256];
	struct svdTest *tv;
	int mask;
	char *ptr;
	int cnt;
	int k, j;
	int ol;

	for (k=0; k<ARRAY_SIZE(svdTestVals); k++)
	 { tv = &svdTestVals[k];
	   for (j=0; j<tv->max; j++)
	    { buf[j] = -123.0;
	    }
	   cnt = strtoVd(tv->input,buf,tv->max,&mask,&ptr);
	   if (tv->error)
	    { if (mask != 0 || ptr != tv->input || cnt != -1)
	       { fprintf (stderr, "Bad return values on error, k=%d\n", k);
		 exit(-1);
	       }
	    }
	   else
	    { if (cnt != tv->cnt)
	       { fprintf (stderr, "Count=%d vs. %d, k=%d\n", cnt, tv->cnt, k);
		 exit(-1);
	       }
	      if (mask != tv->mask)
	       { fprintf (stderr, "Mask=0x%x vs. 0x%x, k=%d\n", 
				   mask,tv->mask, k);
		 exit(-1);
	       }
	      ol = strlen(tv->input) - tv->idx;
	      if (ptr != &tv->input[ol])
	       { fprintf (stderr, "bad return pointer, 0x%x vs. 0x%x, k=%d\n",
				   ptr, &tv->input[ol], k);
		 exit (-1);
	       }
	      for (j=0; j<cnt; j++)
	       { if (mask & (1<<j) && FABS(tv->output[j] - buf[j]) > EPSILON)
		  { fprintf (stderr, "Output[%d]=%g vs. %g, k=%d\n", 
				     j, buf[j], tv->output[j],k);
		    exit(-1);
		  }
		 else if (!(mask & (1<<j)) && buf[j] != -123.0)
		  { fprintf (stderr, "output[%d] set to %g, k=%d\n", 
				     j, buf[j], k);
		    exit(-1);
		  }
	       }
	    }
	 }
}

struct svsTest {
  char *input;
  int max;
  int error;
  int cnt;
  char *output;
  int idx;
  };

struct svsTest svsTestVals[] = 
 { "\"abc\"",			10,  NO,  3, "abc",			0,
   "\"123456789\"\"",		10,  NO,  9, "123456789",		1,
   "  \"abc\" \"",		10,  NO,  3, "abc",			2,
   "\"\"\\\n\"",		10,  NO,  0, "",			3,
   "\"\\\"\\\\\\n\\t\\f\\r\\b\"", 10, NO, 7, "\"\\\n\t\f\r\b",		0,
   "\"\\111\\8\\91\\123123\" ", 10,  NO,  8, "\111\8\91\123123",	1,
   "\"\\118\\0077\"12",		10,  NO,  4, "\118\0077",		2,
   "\"abc\\\ndef\"\"\"\"",	10,  NO,  6, "abcdef",			3,
   "\"1234567890\"",		10, YES, -1, "",			0,
   "a\"abc\"",			10, YES, -1, "",			0,
   "\"abc",			10, YES, -1, "",			0,
   "\"abc\n\"",			10, YES, -1, "",			0,
 };

testStrtos()
{
	char buf[256];
	struct svsTest *tv;
	char *ptr;
	int cnt;
	int k;
	int ol;

	for (k=0; k<ARRAY_SIZE(svsTestVals); k++)
	 { tv = &svsTestVals[k];
	   cnt = strtos(tv->input,buf,tv->max,&ptr);
	   if (tv->error)
	    { if (ptr != tv->input || cnt != -1)
	       { fprintf (stderr, "Bad return values on error, k=%d\n", k);
		 exit(-1);
	       }
	    }
	   else
	    { if (cnt != tv->cnt)
	       { fprintf (stderr, "Count=%d vs. %d, k=%d\n", cnt, tv->cnt, k);
		 exit(-1);
	       }
	      ol = strlen(tv->input) - tv->idx;
	      if (ptr != &tv->input[ol])
	       { fprintf (stderr, "bad return pointer, 0x%x vs. 0x%x, k=%d\n",
				   ptr, &tv->input[ol], k);
		 exit (-1);
	       }
	      if (strcmp (buf, tv->output) != 0)
	       { fprintf (stderr, "Output=%s vs. %s, k=%d\n", 
				   buf, tv->output, k);
		 exit(-1);
	       }
	    }
	 }
}

struct svlTest {
  char *input;
  int base;
  int error;
  int output;
  int idx;
  };

struct svlTest svlTestVals[] = 
 { /* decimal */

   "123", 			10,  NO,  123, 		0,
   " 123ab",			10,  NO,  123,          2,
   "0   ",			10,  NO,  0,            3,
   "  -456 ",			 0,  NO,  -456,         1,
   "7",				 0,  NO,  7,            0,
   "00x",			 0,  NO,  0,            1,
   "-0000",			 0,  NO,  0,		0,

   "",				10, YES,  0,		0,
   "a0",			10, YES,  0,		0,

   /* hex */

   "0x123e  ",			16,  NO,  0x123e,	2,
   " -0x123eg",			 0,  NO,  -0x123e,	1,
   "0XFFEFK",			 0,  NO,  0xffef,	1,
   "0xffe2",			 0,  NO,  0xffe2,	0,
   "ffe2",			16,  NO,  0xffe2,	0,
   "0x0G",			16,  NO,  0,		1,

   "ge123",			16, YES,  0,		0,
   "ffe2",			 0, YES,  0,            0,
   "x123",			16, YES,  0,		0,
   " X123",			16, YES,  0,		0,
   "x123",			 0, YES,  0,		0,

   /* octal */

   "01238  ",			 8,  NO,  0123,		3,
   " -0123eg",			 0,  NO,  -0123,	2,
   "8123",			 8, YES,  0,		0,
   " 0077",			 8,  NO,  077,		0,
   " 0077",			 0,  NO,  077,		0,

   /* binary */

   "111011",			 2,  NO,  0x3b,		0,
   " -11101122",		 2,  NO,  -0x3b,	2,
   "211",			 2, YES,  0,		0,

   /* ternary */
 
   "211",			 3,  NO,  22,		0,
   " -21144",			 3,  NO,  -22,		2,
   "3211",			 3, YES,  0,		0,

   /* base 20 */

   "123",			20,  NO,  443,		0,
   " -123j ",			20,  NO,  -8879,	1,
   " -123k ",			20,  NO,  -443,		2,
   "jJjk",			20,  NO,  7999,		1,
   "ABC-",			20,  NO,  4232,		1,
   "abcK-",			20,  NO,  4232,		2,
   "kJjk",			20, YES,  0,		0,

 };

testStrtol()
{
	int output;
	struct svlTest *tv;
	char *ptr;
	int k;

	for (k=0; k<ARRAY_SIZE(svlTestVals); k++)
	 { tv = &svlTestVals[k];
	   output = strtol(tv->input,&ptr,tv->base);
	   if (tv->error)
	    { if (ptr != tv->input || output != 0)
	       { fprintf (stderr, 
"Bad return values on error, k=%d, input=%s output=%d\n", k,tv->input,output);
		 exit(-1);
	       }
	    }
	   else
	    { int ol;
	      if (output != tv->output)
	       { fprintf (stderr, "Output=%d vs. %d, k=%d\n", 
				   output, tv->output, k);
		 exit(-1);
	       }
	      ol = strlen(tv->input) - tv->idx;
	      if (ptr != &tv->input[ol])
	       { fprintf (stderr, "bad return pointer, 0x%x vs. 0x%x, k=%d\n",
				   ptr, &tv->input[ol], k);
		 exit (-1);
	       }
	    }
	 }
}

#endif
