/*
   Convert an unsigned number to a positive int and vice versa.
   These functions are intended for handling numbers that represent
   addresses or unsigned bytes. 
   
   <kbd>
   <b>environ - C programming support functions</b><br>
   Copyright (C) 2005  Martin C Gregorie
   
   This program is free software; you can redistribute it and/or
   modify it under the terms of the Lesser GNU General Public License.
   
   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 Lesser GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   </kbd>
  
   <p>Contact Martin Gregorie at <i>martin@gregorie.org</i></p>
*/

#include "environ.h"

#ifdef OS9
#include <strings.h>
#endif

#ifdef MSDOS
#include <string.h>
#endif

#ifdef LINUX
#include <stdlib.h>
#include <string.h>
#endif

static char buff[MAXLINE];

/*
   <p>Accepts a string containing an unsigned integer value representing an
   address or a character in a number of formats. It returns an integer value.
   Acceptable input formats are:</p>

   <ul> 
    <li><b>decimal</b>: [0-9]+ e.g. 7, 42</li>

    <li><b>kilobytes</b>: [0-9]+{Kk][Bb] e.g. 4kb, 1024KB</li>

    <li><b>megabytes</b>: [0-9]+{Mm][Bb] e.g. 4mb, 1024MB</li>
   
    <li><b>hexadecimal</b>: $[0-9a-fA-F]+ e.g. $fffe, $1B</li>

    <li><b>hexadecimal</b>: 0[xX][0-9a-fA-F]+ e.g. 0xfffe, 0X1B</li>
   </ul>
   
   <p>An empty string returns zero.</p>
*/
int numconv(char *s)
{
   int   base;
   int   i;
   int   n;
   char  *ns;
   char  *nsend;
   long  result;

   if (s[0] == '$')
   {
      ns = get_space(strlen(s)+2);
      sprintf(ns, "0x%s", s+1);
   }
   else
      ns = strdup(s);

   strlwr(ns);
   n = strlen(ns);
   if (n == 0)
      result = 0;
   else
   {
      base = (strncmp(ns, "0x", 2) == 0 ? 16 : 10);
      result = strtoul(ns, &nsend, base);
      if (nsend[0])
      {
         if (nsend[0] == 'k')
            result *= 1024;
         else
         if (nsend[0] == 'm')
            result *= 1048576;
      }
   }
  
   n = result;
   return n; 
}

/*
   <p>Return a string containing an positive integer value as a decimal,
   hex, kilobyte or megabyte representation. Note that if kilobytes or
   megabytes are specified, the value will be rounded down to the
   nearest whole unit.</p>

   <p>Valid units are:</p>
   <ul>
    <li><b>"dec"</b>: decimal number</li>
    <li><b>"$"</b>: hexadecimal number preceeded by '$'.</li>
    <li><b>"0x"</b>: hexadecimal number preceeded by '0x'</li>
    <li><b>"k"</b> or <b>"kb"</b>:  kilobytes</li>
    <li><b>"m"</b> or <b>"mb"</b>: megabytes</li>
    <li><b>"x"</b>: hexadecimal number</li>
   </ul>

   <p>Hexadecimal values are always shown
   in upper case. If the hexadecimal units are followed by a number,
   it gives the minimum number of digits to be output. Shorter
   strings are zero padded on the left, so a value of 1 with a unit
   code of "$2" will be output as $01 and a value of 255 with a code of
   "0x4" will be output as 0X00FE.</p>
  
   <p>Invalid units are treated as requests for decimal output.</p>

   <p><i>numdisp()</i> output is accepted as valid input by
   <i>numconv()</i>.</p>
*/
char *numdisp(int n, char* units)
{
   int   i;
   long  l;
   char  *uend;
   char  *u = strdup(units);
   char  fs[10];

   strlwr(u);
   if (strncmp(u, "$", 1) == 0)
   {
      if (strlen(u) == 1)
         sprintf(buff, "$%X", n);
      else
      {
         l = strtoul(u + 1, &uend, 10);
         sprintf(fs, "$%%0%dX", l);
         sprintf(buff, fs, n);
      }
   }
   else
   if (strncmp(u, "0x", 2) == 0)
   {
      if (strlen(u) == 2)
         sprintf(buff, "0X%X", n);
      else
      {
         l = strtoul(u + 2, &uend, 10);
         sprintf(fs, "0X%%0%dX", l);
         sprintf(buff, fs, n);
      }
   }
   else
   if (u[0] == 'k')
      sprintf(buff, "%dKb", n/1024);
   else
   if (u[0] == 'm')
      sprintf(buff, "%dMb", n/1048576);
   else
   if (strcmp(u, "x") == 0)
      sprintf(buff, "%X", n);
   else
      sprintf(buff, "%d", n);

   return strdup(buff);
}
