/** * atoi.c * An improved atoi library. */ // +---------+-------------------------------------------------------- // | Headers | // +---------+ #include // For INT_MIN // +---------+-------------------------------------------------------- // | Globals | // +---------+ char *atoi_error = ""; // +---------+-------------------------------------------------------- // | Helpers | // +---------+ /** * Determine whether a character is a base-10 digit. */ static int is_digit_10 (char ch) { switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return 1; default: return 0; } // switch } // is_digit_10 /** * Determine whether a character is a base 8 digit. */ static int is_digit_8 (char ch) { switch (ch) { case '8': case '9': return 0; default: return is_digit_10 (ch); } } // is_digit_8 /** * Determine whether a character is a base-16 digit. */ static int is_digit_16 (char ch) { switch (ch) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': return 1; default: return is_digit_10 (ch); } } // is_digit_16 /** * Determine the value of a base-16 digit, without the evil K&R hacks. */ static int digit_value_16 (char ch) { switch (ch) { case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; case 'a': return 10; case 'A': return 10; case 'b': return 11; case 'B': return 11; case 'c': return 12; case 'C': return 12; case 'd': return 13; case 'D': return 13; case 'e': return 14; case 'E': return 14; case 'f': return 15; case 'F': return 15; default: return -1; } // switch } // digit_value_16 /** * Base 10 atoi. Based on code from K&R. */ static int atoi_10 (char *a) { int result = 0; // The computed result int sign = 1; // The sign of the result int i=0; // Counter variable for position int digit; // The value of the current digit // Special case: Signed if (a[0] == '+') i = 1; else if (a[0] == '-') { i = 1; sign = -1; } // Special case: No digits if (a[i] == '\0') { atoi_error = "missing digits in base-10 number"; return INT_MIN; } // Process remaining digits while (a[i] != '\0') { if (! is_digit_10 (a[i])) { atoi_error = "invalid digit in base-10 number"; return INT_MIN; } digit = digit_value_16 (a[i]); if (result > (INT_MAX - digit) / 10) { atoi_error = "number too large/small"; return INT_MIN; } result = result * 10 + digit; i++; } // while // Incorporate the sign and we're done. return result * sign; } // atoi_10 /** * Base 8 atoi. * Pre: a[0] = '0' */ static int atoi_8 (char *a) { int i = 1; // Position in the string int result = 0; // The computed result int digit; // The value of the current digit // Process remaining digits while (a[i] != '\0') { if (! is_digit_8 (a[i])) { atoi_error = "invalid digit in base-8 number"; return INT_MIN; } digit = digit_value_16 (a[i]); if (result > (INT_MAX - digit) / 8) { atoi_error = "number too large/small"; return INT_MIN; } result = result * 8 + digit; i++; } // while // We're done return result; } // atoi_8 /** * Base 16 atoi. * Pre: a[0] = '0'; * a[1] = 'x' or a[1] = 'X' */ static int atoi_16 (char *a) { int i = 2; // Position in the string int result = 0; // The computed result int digit; // The value of the current digit // Special case: No digits if (a[i] == '\0') { atoi_error = "missing digits in base-16 number"; return INT_MIN; } // Process remaining digits while (a[i] != '\0') { if (! is_digit_16 (a[i])) { atoi_error = "invalid digit in base-16 number"; return INT_MIN; } digit = digit_value_16 (a[i]); if (result > (INT_MAX - digit) / 16) { atoi_error = "number too large/small"; return INT_MIN; } result = result * 16 + digit; i++; } // while // We're done return result; } // atoi_16 // +--------------------+--------------------------------------------- // | Exported Functions | // +--------------------+ int atoi (char *a) { // Special case: Empty string if (a[0] == '\0') { atoi_error = "empty string"; return INT_MIN; } // Special cases: Begins with 0 if (a[0] == '0') { // If it's just "0" ... if (a[1] == '\0') return 0; // If it's hex else if ((a[1] == 'x') || (a[1] == 'X')) return atoi_16 (a); // Otherwise, it should be octal else return atoi_8 (a); } // begins with 0 // Normal case return atoi_10 (a); } // atoi