/*
** mg_int.c for  in 
** 
** Made by vianney rancurel
** Login   <vianney@epita.fr>
** 
** Started on  Wed Aug 25 12:14:51 1999 vianney rancurel
** Last update Thu Oct 28 20:15:56 1999 
*/
#include "mg.h"
#include <ctype.h>

t_base			bases[] =
{
  {2,			"01",			"0b"},
  {8,			"01234567",		"0"},
  {10,			"0123456789",		""},
  {16,			"0123456789abcdef",	"0x"},
  {BASE_HEX_CAP,	"0123456789ABCDEF",	"0X"},
  {64,"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz","0s"},
}; 

/* gets the t_base structures associated with num.
   Returns a base structure or NULL */
t_base			*base_get_from_num(num)
int			num;
{
  int			i;
  
  i = 0;
  while (i < ARRAY_COUNT(bases))
    {
      if (bases[i].num == num)
	return (bases + i);
      i++;
    }
  return (NULL);
}

/* converts-and-catenates an unsigned long to a bridled string.
   It is used internally by ulong_to_str(3). */
t_status		ulong_to_str_i(num,base,str,max_len)
unsigned long		num;
t_base			*base;
char			*str;
int			max_len;
{
  int			len;
  int			div;
  t_status		status;

  if ((status = str_cat_str(str,
			    max_len,
			    base->prefix)) != 0)
    return (status);
  if (num == 0)
    return (str_cat_char(str,
			 max_len,
			 base->str[0]));
  len = strlen(base->str);
  div = 1;
  while ((num / div) >= len)
    div *= len;
  while (div > 0)
    {
      unsigned long	result;

      result = num/div;
      if ((status = str_cat_char(str,
				 max_len,
				 base->str[result])) != 0)
	return (status);
      num -= result * div;
      div /= len;
    }
  return (0);
}

/* converts-and-catenates a long to a bridled string.
   It is used internally by long_to_str(3). */
t_status		long_to_str_i(num,base,str,max_len)
signed long		num;
t_base			*base;
char			*str;
int			max_len;
{
  t_status		status;

  if (num < 0)
    {
      if ((status = str_cat_char(str,
				 max_len,
				 '-')) != 0)
	return (status);
      num = -num;
    }
  return (ulong_to_str_i((unsigned long)num,
			 base,
			 str,
			 max_len));
}

/* converts-and-catenates an unsigned long to a bridled string.
   Basenum can be 2, 8, 10, 16 or BASE_HEX_CAP.
   Returns 0 if OK, might return ERR_MG_BO in case of buffer overflow. 
   ERR_MG_NO_SUCH_BASE if base doesn't exist. */
t_status		ulong_to_str(num,basenum,str,max_len)
unsigned long		num;
int			basenum;
char			*str;
int			max_len;
{
  t_base		*base;

  if (!(base = base_get_from_num(basenum)))
    return (ERR_MG_NO_SUCH_BASE);
  return (ulong_to_str_i(num,
			 base,
			 str,
			 max_len));
}

/* converts-and-catenates a long to a bridled string.
   Basenum can be 2, 8, 10, 16 or BASE_HEX_CAP.
   Returns 0 if OK, might return ERR_MG_BO in case of buffer overflow. 
   ERR_MG_NO_SUCH_BASE if base doesn't exist. */
t_status		long_to_str(num,basenum,str,max_len)
signed long		num;
int			basenum;
char			*str;
int			max_len;
{
  t_base		*base;

  if (!(base = base_get_from_num(basenum)))
    return (ERR_MG_NO_SUCH_BASE);
  return (long_to_str_i(num,
			base,
			str,
			max_len));
}

/* converts a string to a signed long.
   It tries to discover the base by the string prefix. E.g "0x" means 
   hexadecimal, ... Currently only "0nnnnn" (octal), "0[xX]nnnnn" (hexa),
   and "nnnnn" (decimal) are recognized. 
   Returns 0 if OK. Might return ERR_MG_UNRECOG_BASE. */
t_status		long_from_str(str,basenum,num)
char			*str;		/* Input string */
int			*basenum;	/* Base discovered */
signed long		*num;		/* Signed long returned */
{
  if (str[0] == '0')
    {
      if (str[1] == 'x' || str[1] == 'X')
	{
	  (*basenum) = (str[1] == 'x')?16:BASE_HEX_CAP;
	  (*num) = strtol(str,NULL,16);
	  return (0);
	}
      else
	if (isdigit(str[1]))
	  {
	    (*basenum) = 8;
	    (*num) = strtol(str,NULL,8);
	    return (0);
	  }
	else
	  return (ERR_MG_UNRECOG_BASE);
    }
  (*basenum) = 10;
  (*num) = strtol(str,NULL,10);
  return (0);
}

/* converts a string to an unsigned long.
   It tries to discover the base by the string prefix. E.g "0x" means 
   hexadecimal, ... Currently only "0nnnnn" (octal), "0[xX]nnnnn" (hexa),
   and "nnnnn" (decimal) are recognized.
   Returns 0 if OK. Might return ERR_MG_UNRECOG_BASE. */
t_status		ulong_from_str(str,basenum,num)
char			*str;		/* Input string */
int			*basenum;	/* Base discovered */
unsigned long		*num;		/* Unsigned long returned */
{
  if (str[0] == '0')
    {
      if (str[1] == 0)
	{
	  (*basenum) = 10;
	  (*num) = 0;
	  return (0);
	}
      if (str[1] == 'x' || str[1] == 'X')
	{
	  (*basenum) = (str[1] == 'x')?16:BASE_HEX_CAP;
	  (*num) = strtoul(str + 2,NULL,16);
	  return (0);
	}
      else
	if (isdigit(str[1]))
	  {
	    (*basenum) = 8;
	    (*num) = strtoul(str + 1,NULL,8);
	    return (0);
	  }
	else
	  return (ERR_MG_UNRECOG_BASE);
    }
  (*basenum) = 10;
  (*num) = strtoul(str,NULL,10);
  return (0);
}
