/* $Id: radixof.c,v 1.3 2009/01/31 20:51:48 kiesling Exp $ */

/*
  This file is part of Ctalk.
  Copyright © 2005-2009  Robert Kiesling, ctalk@ctalklang.org.
  Permission is granted to copy this software provided that this copyright
  notice is included in all source code modules.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library 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
  Lesser 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., 51 Franklin St., Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "object.h"
#include "message.h"
#include "ctalk.h"
#include "lex.h"

#define IS_SIGN(c,d) ((c == '-' || c == '+') && \
		      (isdigit (d) || d == '.'))

RADIX radix_of (char *buf) {

  RADIX radix = decimal;
  int i, firstdigit;
  
  if (buf[0] && buf[1]) {
#if defined(__GNUC__) && defined(__sparc__) && defined(__svr4__)
    firstdigit = (IS_SIGN (buf[0],(int)buf[1]) ? 1 : 0);
#else
    firstdigit = (IS_SIGN (buf[0],(int)buf[1]) ? 1 : 0);
#endif
  } else {
    if (isdigit ((int)buf[0])) {
      firstdigit = 0;
    } else {
      return -1;
    }
  }

  for (i = firstdigit; i < strlen (buf); i++)
    if (!isxdigit ((int)buf[i]) && 
	(buf[i] != (char)'l') && (buf[i] != (char)'L') &&
	(buf[i] != (char)'u') && (buf[i] != (char)'U') &&
	(buf[i] != (char)'x') && (buf[i] != (char)'X') &&
	(buf[i] != (char)'b') && (buf[i] != (char)'B'))
      return -1;

#if defined(__GNUC__) && defined(__sparc__) && defined(__svr4__)
  if (buf[0] && buf[1] && !IS_SIGN (buf[0],(int)buf[1])) {
#else
  if (buf[0] && buf[1] && !IS_SIGN (buf[0],buf[1])) {
#endif
    if ((buf[0] == (char)'0') && (buf[1] != (char)'.')) {
      if (buf[1] == (char) 'x' || buf[1] == 'X') {
	radix = hexadecimal;
      } else {
	if (buf[1] && (buf[1] >= '0' && buf[1] <= '7'))
	  radix = octal;
      }
    } else {
      if ((buf[strlen(buf)-1] == (char)'B') || 
	  (buf[strlen(buf)-1] == (char)'b'))
	radix = binary;
    }
  } else {
    if (buf[1] && buf[2]) {
      if ((buf[1] == (char)'0') && (buf[2] != (char)'.')) {
	if (buf[2] == (char) 'x' || buf[2] == 'X') {
	  radix = hexadecimal;
	} else {
	  if (buf[2] && (buf[2] >= '0' && buf[2] <= '7'))
	    radix = octal;
	}
      } else {
	if ((buf[strlen(buf)-1] == (char)'B') || 
	    (buf[strlen(buf)-1] == (char)'b'))
	  radix = binary;
      }
    }
  }
  return radix;
}

char *__ctalkLongLongRadixToDecimal (char *longlong) {
  static char buf[MAXMSG];
  unsigned long long int value;
  RADIX radix;

  radix = radix_of (longlong);
  switch (radix) 
    {
    case hexadecimal:
      sscanf (longlong, "%Lx", &value);
      sprintf (buf, "%lld", value);
      break;
    case octal:
      sscanf (longlong, "%Lo", &value);
      sprintf (buf, "%lld", value);
      break;
    case decimal:
    case binary:
      strcpy (buf, longlong);
      break;
    }

  if (!strstr (buf, "ll") && !strstr (buf, "L"))
    strcat (buf, "ll");

  return buf;
}

char *__ctalkIntRadixToDecimal (char *intbuf) {
  static char buf[MAXMSG];
  unsigned int value;
  int i, bit_exp, sum;
  RADIX radix;

  if ((radix = radix_of (intbuf)) == (RADIX)-1) {
    strcpy (buf, intbuf);
    return buf;
  }

  switch (radix) 
    {
    case hexadecimal:
      sscanf (intbuf, "%x", &value);
      sprintf (buf, "%d", value);
      break;
    case octal:
      sscanf (intbuf, "%o", &value);
      sprintf (buf, "%d", value);
      break;
    case decimal:
      strcpy (buf, intbuf);
      break;
    case binary:
      for (i = strlen (intbuf) - 2, bit_exp = 1, sum = 0; i >= 0; 
	   i--, bit_exp *= 2) {
	if ((intbuf[i] == (char)'b') || (intbuf[i] == (char)'B')) 
	  break;
	switch (intbuf[i])
	  {
	  case '1':
	    sum += bit_exp;
	    break;
	  case '0':
	    break;
	  default:
	    _warning ("Bad binary constant.\n");
	    break;
	  }
      }
      sprintf (buf, "%d", sum);
      break;
    }

  return buf;
}

/*
 *  If the formatted character is more than one digit, then
 *  convert from a decimal integer.
 */
char *__ctalkCharRadixToChar (char *charbuf) {
  static char buf[MAXMSG];
  char tmpbuf[MAXMSG];
  unsigned int value;
  int char_is_quoted = 0;
  int i, bit_exp, sum;
  RADIX radix;

  if ((radix = radix_of (charbuf)) == (RADIX)-1) {
    strcpy (buf, charbuf);
    return buf;
  }

  strcpy (tmpbuf, charbuf);
  while (*tmpbuf == '\'') {
    TRIM_CHAR(tmpbuf);
    ++char_is_quoted;
  }
  
  switch (radix) 
    {
    case hexadecimal:
      sscanf (tmpbuf, "%x", (unsigned int *)&value);
      sprintf (buf, "%c", value);
      break;
    case octal:
      sscanf (tmpbuf, "%o", (unsigned int *)&value);
      sprintf (buf, "%c", value);
      break;
    case decimal:
      if (isdigit ((int)*tmpbuf) && (strlen (tmpbuf) > 1)) {
	sscanf (tmpbuf, "%d", &value);
	sprintf (buf, "%c", value);
      } else {
	strcpy (buf, tmpbuf);
      }
      break;
    case binary:
      for (i = strlen (tmpbuf) - 2, bit_exp = 1, sum = 0; i >= 0; 
	   i--, bit_exp *= 2) {
	if ((tmpbuf[i] == (char)'b') || (tmpbuf[i] == (char)'B')) 
	  break;
	switch (tmpbuf[i])
	  {
	  case '1':
	    sum += bit_exp;
	    break;
	  case '0':
	    break;
	  default:
	    _warning ("Bad binary constant.\n");
	    break;
	  }
      }
      sprintf (buf, "%c", sum);
      break;
    default:
      strcpy (buf, tmpbuf);
      break;
    }

  while (char_is_quoted--) {
    sprintf (tmpbuf, "\'%s\'", buf);
    strcpy (buf, tmpbuf);
  }

  return buf;
}

