/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

/*=~=~=~=~ McGill-Computer-Vision-and-Robotics-Laboratory--C-Module ~=~=~=~=
 *		
 * MODULE NAME:	input
 *
 * DESCRIPTION:	Routines for parsing numeric input. The actual character by
 *		character input is done by the input routine in calc.lex.
 * 
 * AUTHOR/DATE:	John Lloyd, Summer 1987
 *
 *~=~=~=~=~=~= McGill-Computer-Vision-and-Robotics-Laboratory ~=~=~=~=~=~=*/

#include <ctype.h>
#include "parameters.h"
#include "cdefs.h"
#include "types.h"

static int inputBase = 10;

char *strcpy();
char *talloc();

/* Set the default input base for integers */

setInputBase (x)
int x;
{
	if (x < 2 || x > 16)
	 { x = 10;
	 }
	else
	 { inputBase = x;
	 }
}

setInput (r, n, a)
GTYPE *r;
int n;
GTYPE *a;
{
	int i;

	switch (a[0].type)
	 { case INTEGER:
	       { if ((i = acceptInputBaseInteger(a[0].val.i)) == -1)
		  { return (-1);
		  }
		 else
		  { inputBase = i;
		  }
		 break;
	       }
	   case STRING:
	       { if ((i = acceptInputBaseString(a[0].val.s)) == -1)
		  { return (-1);
		  }
		 else
		  { inputBase = i;
		  }
		 break;
	       }
	   default:
	       { 
error (0, "`input()' accepts only strings and scalars\n");
		 return (-1);
		 break;
	       }
	 }
 	return (1);
}

/* parse the string specifying the input format */

acceptInputBaseString (s)
char *s;
{
	if (strcmp (s, "x") == 0 || strcmp (s, "X") == 0)
	 { return (16);
	 }
	else if (strcmp (s, "d") == 0 || strcmp (s, "D") == 0)
	 { return (10);
	 }
	else if (strcmp (s, "o") == 0 || strcmp (s, "O") == 0)
	 { return (8);
	 }
 	else if (strcmp (s, "b") == 0 || strcmp (s, "B") == 0)
	 { return (2);
	 }
	else if ( (strlen (s) == 1 && isdigit(s[0])) ||
		  (strlen (s) == 2 && isdigit(s[0]) && isdigit(s[1])) )
  	 { int b = atoi(s);

	   if (b < 2 || b > 16)
	    { error (0, "legal input values range from 2 to 16\n");
	      return (-1);
	    }
	   return (b);
	 }
 	else
 	 { 
error (0, "valid input base codes are digits and the letters <x,d,o,b>\n");
	   return (-1);
	 }
}

/* check an integer specifying a new input base  */

acceptInputBaseInteger (i)
int i;
{
	if (i < 2 || i > 16)
	 { error (0, "legal input base values range from 2 to 16\n");
	   return (-1);
	 }
 	else
	 { return (i);
	 }
}

defaultInputBase()
{
	return (inputBase);
}

#define SYNTAX	 -1
#define OVERFLOW -2

/* parse the contents of 'string' as a integer in the indicated 'base',
   and return the result through 'number' */

readInt (string, number, base, signedp)
char *string;
int *number;
int base;
int signedp;
{	
	int status;

	status = convertInteger (string, number, base, signedp);
	if (status == SYNTAX)
	 { 
	   error (0, "number %s incompatible with input base of %d\n", 
		  strcpy(talloc(strlen(string)+1), string), base);
	   return (-1);
	 }
	else if (status == OVERFLOW)
	 { 
	   error (0, "integer overflow ocurred during read of %s\n", 
		  strcpy(talloc(strlen(string)+1), string));
	   return (-1);
	 }
	return (1);
}

static unsigned int maxUnsignedInt;
static unsigned int maxSignedInt;
static init = NO;

convertInteger (string, number, base, signedp)
char *string;
int *number, base, signedp;
{
	int increment, temp, i, start;
	int overflow;
	int negative;

	if (!init)
	 { maxUnsignedInt = -1;
	   maxSignedInt = maxUnsignedInt >> 1;
	   init = YES;
	 }

	overflow = NO;
	negative = NO;

	if (base > 16 || base < 2)
	 { return (SYNTAX);
	 }
	i = 0;
	while (isspace (string[i]))
	 { i++;
	 }
	if (string[i] == '-')
	 { negative = YES;
	   i++;
	 }
	if (negative && !signedp)      /* cannot have negative unsigned int */
	 { return (SYNTAX);
	 }

	start = i;
	temp = 0;
	increment = evalchar (string [i]);
	while (increment >= 0 && increment < base)
	 { 
	   /* test for overflow at this point */

	   if ( (signedp && temp > (maxSignedInt - increment) / base ) ||
	        (!signedp && temp > (maxUnsignedInt - increment) / base ) )
	    { overflow = YES;
	    }
	    
	   temp = base * temp + increment;
	   increment = evalchar (string[++i]);
	 }

	if (i ==  start)
	 { return (SYNTAX);
	 }
	if (!isspace (string[i]) && string[i] != 0)
	 { return (SYNTAX);
	 }

	*number = negative ? -temp : temp;
	return ( overflow ? OVERFLOW : 0 );
}

evalchar (c)
char c;
{
	if ('0' <= c && c <= '9')
	 { return (c - '0');
	 }
	else if ('a' <= c <= 'f')
	 { return (c - 'a' + 10);
	 }
	else if ('A' <= c <= 'F')
	 { return (c - 'A' + 10);
	 }
	else
 	 { return (-1);
	 }
}
