#include <stdio.h>
#include <stdlib.h>

/**
 * Exercise 2-3.  Write the function htoi(s), which converts a 
 * string of hexadecimal digits (including an optional 0x or 0X) 
 * into its equivalent integer value.
 */

int   htoi(char*);
char* format(char*);
int   decode(char);

int main(void)
{
   char* in;
   char* out;
   int   val;

   printf("Enter hex number: ");
   scanf("%s",in);

   /* Call htoi() */
   val = htoi(in);

   /* If htoi returns 0, but original hex string not zero, then ERROR */
   if(val == 0 && !hzero(in))
   {
      printf("format error (%s not valid hexadecimal)\n",in);
      exit(-1);
   }

   /* If htoi() does not return the same value as strtol, then ERROR */
   if(val != (int)strtol((out = format(in)), (char**)NULL, 16) )
   {
      printf("numerical error (%s not equal to %d)\n",in,val);
      free(out);
      exit(-1);
   }   

   printf("%s = %d\n",in,val);
   exit(0);
}

char* format(char* s)
{
   char* hex_s = (char*)strdup(s);
   bool  good  = TRUE;
   int   i;

   /* Check for and remove prefix 0x or 0X */
   if(hex_s[1] == 'x' || hex_s[1] == 'X') 
   {
      if(hex_s[0] != '0') good = FALSE;
      for(i=0;i<=strlen(hex_s)-2;i++)
         hex_s[i] = hex_s[i+2];
   }

   /* Check all characters are valid */
   for(i=0; good && hex_s[i]!='\0'; i++)
   {
      hex_s[i]  = tolower(hex_s[i]);
      good     &= (hex_s[i] >= 'a' && hex_s[i] <= 'f') || (hex_s[i] >= '0' && hex_s[i] <= '9'); 
   }

   if(!good) 
   {
      free(hex_s);
      return NULL;
   }

   return hex_s;
}

int decode(char c)
{
   if(isdigit(c)) 
      return c - '0';

   switch(c)
   {
      case 'a': case 'A': return 10;
      case 'b': case 'B': return 11;
      case 'c': case 'C': return 12;
      case 'd': case 'D': return 13;
      case 'e': case 'E': return 14;
      case 'f': case 'F': return 15;
      default:
         printf("format error (%c not valid hexadecimal digit)\n",c);
   }
   
   return -1;
}

int hzero(char* s)
{
   int   i;
   bool  zero = TRUE;

   /* Check for and skip prefix 0x or 0X */
   if(s[1] == 'x' || s[1] == 'X') 
      i = 2;
   else
      i = 0;

   /* Check all characters are zero */
   for(; zero && s[i]!='\0'; i++)
      zero &= (s[i] == '0');

   return zero;
}

int htoi(char* s)
{
   int i       = 0;
   int result  = 0;
   int power   = 1;

   char* hex_s = format(s);
   if(hex_s == NULL) return 0;

   for(i=strlen(hex_s)-1; i>=0; i--)
   {
       result += (decode(hex_s[i]) * power);
       power  *= 16;
   } 

   free(hex_s);
   return result;
}
