From a0e818a9516e9f4bfe34c6533fa375ffe1bc4f53 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Sat, 9 Nov 2019 01:27:49 -0600 Subject: [PATCH] Progress: f_conversion and fl_console changes This includes adding support for duodecimal (base 12) --- level_0/f_conversion/c/conversion.c | 604 ++++++++++++++++++++++++++++++++++-- level_0/f_conversion/c/conversion.h | 295 +++++++++++++++++- level_1/fl_console/c/console.c | 113 +++---- level_1/fl_console/c/console.h | 144 +++++---- 4 files changed, 973 insertions(+), 183 deletions(-) diff --git a/level_0/f_conversion/c/conversion.c b/level_0/f_conversion/c/conversion.c index dca1b35..bd7990c 100644 --- a/level_0/f_conversion/c/conversion.c +++ b/level_0/f_conversion/c/conversion.c @@ -26,6 +26,25 @@ extern "C" { } #endif // _di_f_conversion_character_is_decimal_ +#ifndef _di_f_conversion_character_is_duodecimal_ + f_return_status f_conversion_character_is_duodecimal(const int8_t character) { + + if (character > 0x29 && character < 0x40) { + return f_true; + } + + if (character == 0x41 || character == 0x42) { + return f_true; + } + + if (character == 0x61 || character == 0x62) { + return f_true; + } + + return f_false; + } +#endif // _di_f_conversion_character_is_duodecimal_ + #ifndef _di_f_conversion_character_is_hexidecimal_ f_return_status f_conversion_character_is_hexidecimal(const int8_t character) { @@ -96,6 +115,35 @@ extern "C" { } #endif // _di_f_conversion_character_to_decimal_ +#ifndef _di_f_conversion_character_to_duodecimal_ + f_return_status f_conversion_character_to_duodecimal(const int8_t character, uint64_t *decimal) { + #ifndef _di_level_0_parameter_checking_ + if (decimal == 0) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ + + switch (character) { + case 0x30: *decimal = 0; break; + case 0x31: *decimal = 1; break; + case 0x32: *decimal = 2; break; + case 0x33: *decimal = 3; break; + case 0x34: *decimal = 4; break; + case 0x35: *decimal = 5; break; + case 0x36: *decimal = 6; break; + case 0x37: *decimal = 7; break; + case 0x38: *decimal = 8; break; + case 0x39: *decimal = 9; break; + case 0x41: *decimal = 10; break; + case 0x42: *decimal = 11; break; + case 0x61: *decimal = 10; break; + case 0x62: *decimal = 11; break; + default: + return f_status_set_error(f_invalid_number); + } + + return f_none; + } +#endif // _di_f_conversion_character_to_duodecimal_ + #ifndef _di_f_conversion_character_to_hexidecimal_ f_return_status f_conversion_character_to_hexidecimal(const int8_t character, uint64_t *decimal) { #ifndef _di_level_0_parameter_checking_ @@ -156,6 +204,66 @@ extern "C" { } #endif // _di_f_conversion_character_to_octal_ +#ifndef _di_f_conversion_string_to_binary_signed_ + f_return_status f_conversion_string_to_binary_signed(const f_string string, int64_t *number, const f_string_location location, const bool negative) { + #ifndef _di_level_0_parameter_checking_ + if (string == 0) return f_status_set_error(f_invalid_parameter); + if (number == 0) return f_status_set_error(f_invalid_parameter); + if (location.start < 0) return f_status_set_error(f_invalid_parameter); + if (location.stop < location.start) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ + + uint8_t scale = 0; + uint64_t digit = 0; + uint64_t converted = 0; + + for (f_string_length i = location.start; i <= location.stop; i++) { + if (f_conversion_character_to_binary(string[i], &digit) == f_none) { + if (scale) { + scale++; + + if (scale > 63) { + return f_status_set_error(f_overflow); + } + + if (negative) { + if (scale > 63) { + return f_status_set_error(f_underflow); + } + + converted <<= 1; + converted -= digit; + } + else { + if (scale > 63) { + return f_status_set_error(f_overflow); + } + + converted <<= 1; + converted += digit; + } + } + else if (digit != 0) { + scale = 1; + + if (negative) { + converted = 0 - digit; + } + else { + converted = digit; + } + } + } + else if (string[i] != '\0') { + return f_status_set_error(f_invalid_number); + } + } // for + + *number = converted; + return f_none; + } +#endif // _di_f_conversion_string_to_binary_signed_ + #ifndef _di_f_conversion_string_to_binary_unsigned_ f_return_status f_conversion_string_to_binary_unsigned(const f_string string, uint64_t *number, const f_string_location location) { #ifndef _di_level_0_parameter_checking_ @@ -165,14 +273,19 @@ extern "C" { if (location.stop < location.start) return f_status_set_error(f_invalid_parameter); #endif // _di_level_0_parameter_checking_ - bool scale = 0; + uint8_t scale = 0; uint64_t digit = 0; uint64_t converted = 0; - // @todo: needs overflow/underflow detection. for (f_string_length i = location.start; i <= location.stop; i++) { if (f_conversion_character_to_binary(string[i], &digit) == f_none) { if (scale) { + scale++; + + if (scale > 64) { + return f_status_set_error(f_overflow); + } + converted <<= 1; converted += digit; } @@ -181,7 +294,7 @@ extern "C" { converted = digit; } } - else { + else if (string[i] != '\0') { return f_status_set_error(f_invalid_number); } } // for @@ -191,6 +304,67 @@ extern "C" { } #endif // _di_f_conversion_string_to_binary_unsigned_ +#ifndef _di_f_conversion_string_to_decimal_signed_ + f_return_status f_conversion_string_to_decimal_signed(const f_string string, int64_t *number, const f_string_location location, const bool negative) { + #ifndef _di_level_0_parameter_checking_ + if (string == 0) return f_status_set_error(f_invalid_parameter); + if (number == 0) return f_status_set_error(f_invalid_parameter); + if (location.start < 0) return f_status_set_error(f_invalid_parameter); + if (location.stop < location.start) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ + + uint8_t scale = 0; + uint64_t digit = 0; + uint64_t converted = 0; + + for (f_string_length i = location.start; i <= location.stop; i++) { + if (f_conversion_character_to_decimal(string[i], &digit) == f_none) { + + if (scale) { + scale++; + + if (negative) { + if (scale > 18) { + if ((converted * 10) - digit < LLONG_MIN || (converted * 10) - digit > converted) { + return f_status_set_error(f_underflow); + } + } + + converted *= 10; + converted -= digit; + } + else { + if (scale > 18) { + if ((converted * 10) + digit > LLONG_MAX || (converted * 10) + digit < converted) { + return f_status_set_error(f_overflow); + } + } + + converted *= 10; + converted += digit; + } + } + else if (digit != 0) { + scale = 1; + + if (negative) { + converted = 0 - digit; + } + else { + converted = digit; + } + } + } + else if (string[i] != '\0') { + return f_status_set_error(f_invalid_number); + } + } // for + + *number = converted; + return f_none; + } +#endif // _di_f_conversion_string_to_decimal_signed_ + #ifndef _di_f_conversion_string_to_decimal_unsigned_ f_return_status f_conversion_string_to_decimal_unsigned(const f_string string, uint64_t *number, const f_string_location location) { #ifndef _di_level_0_parameter_checking_ @@ -200,15 +374,22 @@ extern "C" { if (location.stop < location.start) return f_status_set_error(f_invalid_parameter); #endif // _di_level_0_parameter_checking_ - bool scale = 0; + uint8_t scale = 0; uint64_t digit = 0; uint64_t converted = 0; - // @todo: needs overflow/underflow detection. for (f_string_length i = location.start; i <= location.stop; i++) { if (f_conversion_character_to_decimal(string[i], &digit) == f_none) { if (scale) { + scale++; + + if (scale > 18) { + if ((converted * 10) + digit > ULLONG_MAX || (converted * 10) + digit < converted) { + return f_status_set_error(f_overflow); + } + } + converted *= 10; converted += digit; } @@ -217,7 +398,7 @@ extern "C" { converted = digit; } } - else { + else if (string[i] != '\0') { return f_status_set_error(f_invalid_number); } } // for @@ -227,6 +408,171 @@ extern "C" { } #endif // _di_f_conversion_string_to_decimal_unsigned_ +#ifndef _di_f_conversion_string_to_duodecimal_signed_ + f_return_status f_conversion_string_to_duodecimal_signed(const f_string string, int64_t *number, const f_string_location location, const bool negative) { + #ifndef _di_level_0_parameter_checking_ + if (string == 0) return f_status_set_error(f_invalid_parameter); + if (number == 0) return f_status_set_error(f_invalid_parameter); + if (location.start < 0) return f_status_set_error(f_invalid_parameter); + if (location.stop < location.start) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ + + uint8_t scale = 0; + uint64_t digit = 0; + uint64_t converted = 0; + + for (f_string_length i = location.start; i <= location.stop; i++) { + if (f_conversion_character_to_duodecimal(string[i], &digit) == f_none) { + + if (scale) { + scale++; + + if (negative) { + if (scale > 15) { + if ((converted * 12) - digit < LLONG_MIN || (converted * 12) - digit > converted) { + return f_status_set_error(f_underflow); + } + } + + converted *= 12; + converted -= digit; + } + else { + if (scale > 15) { + if ((converted * 12) + digit > LLONG_MAX || (converted * 12) + digit < converted) { + return f_status_set_error(f_overflow); + } + } + + converted *= 12; + converted += digit; + } + } + else if (digit != 0) { + scale = 1; + + if (negative) { + converted = 0 - digit; + } + else { + converted = digit; + } + } + } + else if (string[i] != '\0') { + return f_status_set_error(f_invalid_number); + } + } // for + + *number = converted; + return f_none; + } +#endif // _di_f_conversion_string_to_duodecimal_signed_ + +#ifndef _di_f_conversion_string_to_duodecimal_unsigned_ + f_return_status f_conversion_string_to_duodecimal_unsigned(const f_string string, uint64_t *number, const f_string_location location) { + #ifndef _di_level_0_parameter_checking_ + if (string == 0) return f_status_set_error(f_invalid_parameter); + if (number == 0) return f_status_set_error(f_invalid_parameter); + if (location.start < 0) return f_status_set_error(f_invalid_parameter); + if (location.stop < location.start) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ + + uint8_t scale = 0; + uint64_t digit = 0; + uint64_t converted = 0; + + for (f_string_length i = location.start; i <= location.stop; i++) { + if (f_conversion_character_to_duodecimal(string[i], &digit) == f_none) { + + if (scale) { + scale++; + + if (scale > 16) { + if ((converted * 12) + digit > ULLONG_MAX || (converted * 12) + digit < converted) { + return f_status_set_error(f_overflow); + } + } + + converted *= 12; + converted += digit; + } + else if (digit != 0) { + scale = 1; + converted = digit; + } + } + else if (string[i] != '\0') { + return f_status_set_error(f_invalid_number); + } + } // for + + *number = converted; + return f_none; + } +#endif // _di_f_conversion_string_to_duodecimal_unsigned_ + +#ifndef _di_f_conversion_string_to_hexidecimal_signed_ + f_return_status f_conversion_string_to_hexidecimal_signed(const f_string string, int64_t *number, const f_string_location location, const bool negative) { + #ifndef _di_level_0_parameter_checking_ + if (string == 0) return f_status_set_error(f_invalid_parameter); + if (number == 0) return f_status_set_error(f_invalid_parameter); + if (location.start < 0) return f_status_set_error(f_invalid_parameter); + if (location.stop < location.start) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ + + uint8_t scale = 0; + uint64_t digit = 0; + uint64_t converted = 0; + + for (f_string_length i = location.start; i <= location.stop; i++) { + if (f_conversion_character_to_hexidecimal(string[i], &digit) == f_none) { + + if (scale) { + scale++; + + if (negative) { + if (scale > 15) { + if ((converted << 4) - digit < LLONG_MIN || (converted << 4) - digit > converted) { + return f_status_set_error(f_underflow); + } + } + + converted <<= 4; + converted -= digit; + } + else { + if (scale > 15) { + if ((converted << 4) + digit > LLONG_MAX || (converted << 4) + digit < converted) { + return f_status_set_error(f_overflow); + } + } + + converted <<= 4; + converted += digit; + } + } + else if (digit != 0) { + scale = 1; + + if (negative) { + converted = 0 - digit; + } + else { + converted = digit; + } + } + } + else if (string[i] != '\0') { + return f_status_set_error(f_invalid_number); + } + } // for + + *number = converted; + return f_none; + } +#endif // _di_f_conversion_string_to_hexidecimal_signed_ + #ifndef _di_f_conversion_string_to_hexidecimal_unsigned_ f_return_status f_conversion_string_to_hexidecimal_unsigned(const f_string string, uint64_t *number, const f_string_location location) { #ifndef _di_level_0_parameter_checking_ @@ -236,15 +582,20 @@ extern "C" { if (location.stop < location.start) return f_status_set_error(f_invalid_parameter); #endif // _di_level_0_parameter_checking_ - bool scale = 0; + uint8_t scale = 0; uint64_t digit = 0; uint64_t converted = 0; - // @todo: needs overflow/underflow detection. for (f_string_length i = location.start; i <= location.stop; i++) { if (f_conversion_character_to_hexidecimal(string[i], &digit) == f_none) { if (scale) { + scale++; + + if (scale > 16) { + return f_status_set_error(f_overflow); + } + converted <<= 4; converted += digit; } @@ -253,7 +604,7 @@ extern "C" { converted = digit; } } - else { + else if (string[i] != '\0') { return f_status_set_error(f_invalid_number); } } // for @@ -263,6 +614,67 @@ extern "C" { } #endif // _di_f_conversion_string_to_hexidecimal_unsigned_ +#ifndef _di_f_conversion_string_to_octal_signed_ + f_return_status f_conversion_string_to_octal_signed(const f_string string, int64_t *number, const f_string_location location, const bool negative) { + #ifndef _di_level_0_parameter_checking_ + if (string == 0) return f_status_set_error(f_invalid_parameter); + if (number == 0) return f_status_set_error(f_invalid_parameter); + if (location.start < 0) return f_status_set_error(f_invalid_parameter); + if (location.stop < location.start) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ + + uint8_t scale = 0; + uint64_t digit = 0; + uint64_t converted = 0; + + for (f_string_length i = location.start; i <= location.stop; i++) { + if (f_conversion_character_to_octal(string[i], &digit) == f_none) { + + if (scale) { + scale++; + + if (negative) { + if (scale > 19) { + if ((converted << 3) - digit < LLONG_MIN || (converted << 3) - digit > converted) { + return f_status_set_error(f_underflow); + } + } + + converted <<= 3; + converted -= digit; + } + else { + if (scale > 19) { + if ((converted << 3) + digit > LLONG_MAX || (converted << 3) + digit < converted) { + return f_status_set_error(f_overflow); + } + } + + converted <<= 3; + converted += digit; + } + } + else if (digit != 0) { + scale = 1; + + if (negative) { + converted = 0 - digit; + } + else { + converted = digit; + } + } + } + else if (string[i] != '\0') { + return f_status_set_error(f_invalid_number); + } + } // for + + *number = converted; + return f_none; + } +#endif // _di_f_conversion_string_to_octal_signed_ + #ifndef _di_f_conversion_string_to_octal_unsigned_ f_return_status f_conversion_string_to_octal_unsigned(const f_string string, uint64_t *number, const f_string_location location) { #ifndef _di_level_0_parameter_checking_ @@ -272,17 +684,23 @@ extern "C" { if (location.stop < location.start) return f_status_set_error(f_invalid_parameter); #endif // _di_level_0_parameter_checking_ - bool scale = 0; + uint8_t scale = 0; uint64_t digit = 0; uint64_t converted = 0; - // @todo: needs overflow/underflow detection. for (f_string_length i = location.start; i <= location.stop; i++) { if (f_conversion_character_to_octal(string[i], &digit) == f_none) { - // when the scale exists, then we need to make the number larger, for this function the scale is base 8. if (scale) { - converted *= 8; + scale++; + + if (scale > 20) { + if ((converted << 3) + digit > ULLONG_MAX || (converted << 3) + digit < converted) { + return f_status_set_error(f_overflow); + } + } + + converted <<= 3; converted += digit; } else if (digit != 0) { @@ -290,7 +708,7 @@ extern "C" { converted = digit; } } - else { + else if (string[i] != '\0') { return f_status_set_error(f_invalid_number); } } // for @@ -300,6 +718,151 @@ extern "C" { } #endif // _di_f_conversion_string_to_octal_unsigned_ +#ifndef _di_f_conversion_string_to_number_signed_ + f_return_status f_conversion_string_to_number_signed(const f_string string, int64_t *number, const f_string_location location) { + #ifndef _di_level_0_parameter_checking_ + if (string == 0) return f_status_set_error(f_invalid_parameter); + if (number == 0) return f_status_set_error(f_invalid_parameter); + if (location.start < 0) return f_status_set_error(f_invalid_parameter); + if (location.stop < location.start) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ + + uint8_t width = 0; + uint8_t width_max = 0; + uint8_t mode = 0; + int8_t vector = 0; // 0 for assumed positive, 1 for explicit positive, -1 for negative. + f_string_length j = 0; + f_string_length offset = 0; + f_status status = f_none; + + for (f_string_length i = location.start; i <= location.stop; i++) { + width = f_macro_utf_byte_width_is(string[i]); + + if (width == 0) { + if (isspace(string[i])) { + if (mode == 0 && vector == 0) { + offset++; + continue; + } + + return f_status_set_error(f_invalid_number); + } + } + else { + if (mode == 0 && vector == 0) { + width_max = (location.stop - i) + 1; + + status = f_utf_is_whitespace(string + i, width_max); + + if (status == f_true) { + offset = i + 1; + continue; + } + + if (f_status_is_error(status)) { + if (status == f_status_set_error(f_maybe)) { + status = f_status_set_error(f_incomplete_utf); + } + + return status; + } + } + + return f_status_set_error(f_invalid_number); + } + + if (string[i] == 0x30) { + j = i + 1; + + // immediate next value must be either '0', 'x', 'X', 'd', 'D', 'o', 'O', 'b', or 'B'. + if (j > location.stop) { + *number = 0; + return f_none; + } + else if (string[j] == 0x30) { + mode = 10; + } + else if (string[j] == 0x78 || string[j] == 0x58) { + mode = 16; + offset += 2; + } + else if (string[j] == 0x44 || string[j] == 0x64) { + mode = 12; + offset += 2; + } + else if (string[j] == 0x6f || string[j] == 0x4f) { + mode = 8; + offset += 2; + } + else if (string[j] == 0x62 || string[j] == 0x42) { + mode = 2; + offset += 2; + } + else { + return f_status_set_error(f_invalid_number); + } + + break; + } + + // plus sign is only allowed as the first non-whitespace character. + if (string[i] == 0x2b) { + if (mode == 0 && vector == 0) { + vector = 1; + offset++; + } + else { + return f_status_set_error(f_invalid_number); + } + } + + // negative sign is not allowed. + if (string[i] == 0x2d) { + if (mode == 0 && vector == 0) { + vector = -1; + offset++; + } + else { + return f_status_set_error(f_invalid_number); + } + } + + if (f_conversion_character_is_decimal(string[i]) == f_true) { + mode = 10; + break; + } + + return f_status_set_error(f_negative_number); + } // for + + if (mode == 0) { + return f_no_data; + } + + f_string_location location_offset = f_string_location_initialize; + location_offset.start = location.start + offset; + location_offset.stop = location.stop; + + if (mode == 10) { + return f_conversion_string_to_decimal_signed(string, number, location_offset, vector == -1); + } + + if (mode == 16) { + return f_conversion_string_to_hexidecimal_signed(string, number, location_offset, vector == -1); + } + + if (mode == 12) { + return f_conversion_string_to_duodecimal_signed(string, number, location_offset, vector == -1); + } + + if (mode == 8) { + return f_conversion_string_to_octal_signed(string, number, location_offset, vector == -1); + } + + return f_conversion_string_to_binary_signed(string, number, location_offset, vector == -1); + } +#endif // _di_f_conversion_string_to_number_signed_ + #ifndef _di_f_conversion_string_to_number_unsigned_ f_return_status f_conversion_string_to_number_unsigned(const f_string string, uint64_t *number, const f_string_location location) { #ifndef _di_level_0_parameter_checking_ @@ -309,9 +872,6 @@ extern "C" { if (location.stop < location.start) return f_status_set_error(f_invalid_parameter); #endif // _di_level_0_parameter_checking_ - bool scale = 0; - uint64_t digit = 0; - uint64_t converted = 0; uint8_t width = 0; uint8_t width_max = 0; uint8_t mode = 0; @@ -358,7 +918,7 @@ extern "C" { if (string[i] == 0x30) { j = i + 1; - // immediate next value must be either '0', 'x', 'X', 'o', 'O', 'b', or 'B'. + // immediate next value must be either '0', 'x', 'X', 'd', 'D', 'o', 'O', 'b', or 'B'. if (j > location.stop) { *number = 0; return f_none; @@ -370,6 +930,10 @@ extern "C" { mode = 16; offset += 2; } + else if (string[j] == 0x44 || string[j] == 0x64) { + mode = 12; + offset += 2; + } else if (string[j] == 0x6f || string[j] == 0x4f) { mode = 8; offset += 2; @@ -419,6 +983,10 @@ extern "C" { return f_conversion_string_to_hexidecimal_unsigned(string, number, location_offset); } + if (mode == 12) { + return f_conversion_string_to_duodecimal_unsigned(string, number, location_offset); + } + if (mode == 8) { return f_conversion_string_to_octal_unsigned(string, number, location_offset); } diff --git a/level_0/f_conversion/c/conversion.h b/level_0/f_conversion/c/conversion.h index cd94168..16fac6c 100644 --- a/level_0/f_conversion/c/conversion.h +++ b/level_0/f_conversion/c/conversion.h @@ -12,6 +12,7 @@ // libc includes #include +#include #include // fll-0 includes @@ -53,6 +54,20 @@ extern "C" { #endif // _di_f_conversion_character_is_decimal_ /** + * Convert a single character into the duodecimal digit that it represents. + * + * @param character + * The character to validate. + * + * @return + * f_true if character is a duodecimal. + * f_false if character is not a duodecimal. + */ +#ifndef _di_f_conversion_character_is_duodecimal_ + extern f_return_status f_conversion_character_is_duodecimal(const int8_t character); +#endif // _di_f_conversion_character_is_duodecimal_ + +/** * Convert a single character into the hexidecimal digit that it represents. * * @param character @@ -81,7 +96,25 @@ extern "C" { #endif // _di_f_conversion_character_is_octal_ /** - * Convert a single character into the digit that it represents. + * Convert a single character into the binary digit that it represents. + * + * @param character + * The character to convert. + * @param number + * This will store the value of the converted character. + * This value is only changed on success. + * + * @return + * f_none if character was converted to a binary. + * f_invalid_number (with error bit) if no conversion was made due to non-binary values being found. + * f_invalid_parameter (with error bit) if a parameter is invalid. + */ +#ifndef _di_f_conversion_character_to_binary_ + extern f_return_status f_conversion_character_to_binary(const int8_t character, uint64_t *number); +#endif // _di_f_conversion_character_to_binary_ + +/** + * Convert a single character into the decimal digit that it represents. * * @param character * The character to convert. @@ -99,6 +132,24 @@ extern "C" { #endif // _di_f_conversion_character_to_decimal_ /** + * Convert a single character into the duodecimal digit that it represents. + * + * @param character + * The character to convert. + * @param number + * This will store the value of the converted character. + * This value is only changed on success. + * + * @return + * f_none if character was converted to a duodecimal. + * f_invalid_number (with error bit) if no conversion was made due to non-duodecimal values being found. + * f_invalid_parameter (with error bit) if a parameter is invalid. + */ +#ifndef _di_f_conversion_character_to_duodecimal_ + extern f_return_status f_conversion_character_to_duodecimal(const int8_t character, uint64_t *number); +#endif // _di_f_conversion_character_to_duodecimal_ + +/** * Convert a single character into the hexidecimal digit that it represents. * * @param character @@ -135,10 +186,38 @@ extern "C" { #endif // _di_f_conversion_character_to_octal_ /** + * Convert a series of positive or negative binary number characters into a int64_t. + * + * This will stop at one of the following: location.stop or a non-digit. + * This will ignore NULL values. + * This will not process signed statuses (+/-). + * + * @param string + * The string to convert. + * @param number + * This will store the value of the converted string. + * This value is only changed on success. + * @param location + * The start/stop range to convert. + * @param negative + * Set to 0 to treat string as a positive number, 1 for as a negative number. + * + * @return + * f_none if the binary string was converted to an signed long. + * f_invalid_number (with error bit) if no conversion was made due to non-binary values being found. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_overflow (with error bit) on integer overflow. + * f_underflow (with error bit) on integer underflow. + */ +#ifndef _di_f_conversion_string_to_binary_signed_ + extern f_return_status f_conversion_string_to_binary_signed(const f_string string, int64_t *number, const f_string_location location, const bool negative); +#endif // _di_f_conversion_string_to_binary_signed_ + +/** * Convert a series of positive binary number characters into a uint64_t. * - * This will stop at one of the following: EOS, location.stop, or a non-digit. - * This works like atoi, except there is a start/stop range. + * This will stop at one of the following: location.stop or a non-digit. + * This will ignore NULL values. * This will not process signed statuses (+/-). * * @param string @@ -153,16 +232,45 @@ extern "C" { * f_none if the binary string was converted to an unsigned long. * f_invalid_number (with error bit) if no conversion was made due to non-binary values being found. * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_overflow (with error bit) on integer overflow. */ #ifndef _di_f_conversion_string_to_binary_unsigned_ extern f_return_status f_conversion_string_to_binary_unsigned(const f_string string, uint64_t *number, const f_string_location location); #endif // _di_f_conversion_string_to_binary_unsigned_ /** + * Convert a series of positive or negative decimal number characters into an int64_t. + * + * This will stop at one of the following: location.stop or a non-digit. + * This will ignore NULL values. + * This will not process signed statuses (+/-). + * + * @param string + * The string to convert. + * @param number + * This will store the value of the converted string. + * This value is only changed on success. + * @param location + * The start/stop range to convert. + * @param negative + * Set to 0 to treat string as a positive number, 1 for as a negative number. + * + * @return + * f_none if the decimal string was converted to an signed long. + * f_invalid_number (with error bit) if no conversion was made due to non-decimal values being found. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_overflow (with error bit) on integer overflow. + * f_underflow (with error bit) on integer underflow. + */ +#ifndef _di_f_conversion_string_to_decimal_signed_ + extern f_return_status f_conversion_string_to_decimal_signed(const f_string string, int64_t *number, const f_string_location location, const bool negative); +#endif // _di_f_conversion_string_to_decimal_signed_ + +/** * Convert a series of positive decimal number characters into an uint64_t. * - * This will stop at one of the following: EOS, location.stop, or a non-digit. - * This works like atoi, except there is a start/stop range. + * This will stop at one of the following: location.stop or a non-digit. + * This will ignore NULL values. * This will not process signed statuses (+/-). * * @param string @@ -177,16 +285,98 @@ extern "C" { * f_none if the decimal string was converted to an unsigned long. * f_invalid_number (with error bit) if no conversion was made due to non-decimal values being found. * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_overflow (with error bit) on integer overflow. */ #ifndef _di_f_conversion_string_to_decimal_unsigned_ extern f_return_status f_conversion_string_to_decimal_unsigned(const f_string string, uint64_t *number, const f_string_location location); #endif // _di_f_conversion_string_to_decimal_unsigned_ /** + * Convert a series of positive or negative duodecimal number characters into an int64_t. + * + * This will stop at one of the following: location.stop or a non-digit. + * This will ignore NULL values. + * This will not process signed statuses (+/-). + * + * @param string + * The string to convert. + * @param number + * This will store the value of the converted string. + * This value is only changed on success. + * @param location + * The start/stop range to convert. + * @param negative + * Set to 0 to treat string as a positive number, 1 for as a negative number. + * + * @return + * f_none if the duodecimal string was converted to an signed long. + * f_invalid_number (with error bit) if no conversion was made due to non-duodecimal values being found. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_overflow (with error bit) on integer overflow. + * f_underflow (with error bit) on integer underflow. + */ +#ifndef _di_f_conversion_string_to_duodecimal_signed_ + extern f_return_status f_conversion_string_to_duodecimal_signed(const f_string string, int64_t *number, const f_string_location location, const bool negative); +#endif // _di_f_conversion_string_to_duodecimal_signed_ + +/** + * Convert a series of positive duodecimal number characters into an uint64_t. + * + * This will stop at one of the following: location.stop or a non-digit. + * This will ignore NULL values. + * This will not process signed statuses (+/-). + * + * @param string + * The string to convert. + * @param number + * This will store the value of the converted string. + * This value is only changed on success. + * @param location + * The start/stop range to convert. + * + * @return + * f_none if the duodecimal string was converted to an unsigned long. + * f_invalid_number (with error bit) if no conversion was made due to non-duodecimal values being found. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_overflow (with error bit) on integer overflow. + */ +#ifndef _di_f_conversion_string_to_duodecimal_unsigned_ + extern f_return_status f_conversion_string_to_duodecimal_unsigned(const f_string string, uint64_t *number, const f_string_location location); +#endif // _di_f_conversion_string_to_duodecimal_unsigned_ + +/** + * Convert a series of positive or negative hexidecimal number characters into an int64_t. + * + * This will stop at one of the following: location.stop or a non-digit. + * This will ignore NULL values. + * This will not process signed statuses (+/-). + * + * @param string + * The string to convert. + * @param number + * This will store the value of the converted string. + * This value is only changed on success. + * @param location + * The start/stop range to convert. + * @param negative + * Set to 0 to treat string as a positive number, 1 for as a negative number. + * + * @return + * f_none if the hexidecimal string was converted to an signed long. + * f_invalid_number (with error bit) if no conversion was made due to non-hexidecimal values being found. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_overflow (with error bit) on integer overflow. + * f_underflow (with error bit) on integer underflow. + */ +#ifndef _di_f_conversion_string_to_hexidecimal_signed_ + extern f_return_status f_conversion_string_to_hexidecimal_signed(const f_string string, int64_t *number, const f_string_location location, const bool negative); +#endif // _di_f_conversion_string_to_hexidecimal_signed_ + +/** * Convert a series of positive hexidecimal number characters into an uint64_t. * - * This will stop at one of the following: EOS, location.stop, or a non-digit. - * This works like atoi, except there is a start/stop range. + * This will stop at one of the following: location.stop or a non-digit. + * This will ignore NULL values. * This will not process signed statuses (+/-). * * @param string @@ -201,16 +391,44 @@ extern "C" { * f_none if the hexidecimal string was converted to an unsigned long. * f_invalid_number (with error bit) if no conversion was made due to non-hexidecimal values being found. * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_overflow (with error bit) on integer overflow. */ #ifndef _di_f_conversion_string_to_hexidecimal_unsigned_ extern f_return_status f_conversion_string_to_hexidecimal_unsigned(const f_string string, uint64_t *number, const f_string_location location); #endif // _di_f_conversion_string_to_hexidecimal_unsigned_ /** + * Convert a series of positive or negative octal number characters into an int64_t. + * + * This will stop at one of the following: location.stop or a non-digit. + * This will ignore NULL values. + * This will not process signed statuses (+/-). + * + * @param string + * The string to convert. + * @param number + * This will store the value of the converted string. + * This value is only changed on success. + * @param location + * The start/stop range to convert. + * @param negative + * Set to 0 to treat string as a positive number, 1 for as a negative number. + * + * @return + * f_none if the octal string was converted to an signed long. + * f_invalid_number (with error bit) if no conversion was made due to non-octal values being found. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_overflow (with error bit) on integer overflow. + */ +#ifndef _di_f_conversion_string_to_octal_signed_ + extern f_return_status f_conversion_string_to_octal_signed(const f_string string, int64_t *number, const f_string_location location, const bool negative); +#endif // _di_f_conversion_string_to_octal_signed_ + +/** * Convert a series of positive octal number characters into an uint64_t. * - * This will stop at one of the following: EOS, location.stop, or a non-digit. - * This works like atoi, except there is a start/stop range. + * This will stop at one of the following: location.stop or a non-digit. + * This will ignore NULL values. * This will not process signed statuses (+/-). * * @param string @@ -225,19 +443,63 @@ extern "C" { * f_none if the octal string was converted to an unsigned long. * f_invalid_number (with error bit) if no conversion was made due to non-octal values being found. * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_overflow (with error bit) on integer overflow. */ #ifndef _di_f_conversion_string_to_octal_unsigned_ extern f_return_status f_conversion_string_to_octal_unsigned(const f_string string, uint64_t *number, const f_string_location location); #endif // _di_f_conversion_string_to_octal_unsigned_ /** - * Convert a series of positive number characters into an uint64_t. + * Convert a series of positive or negative number characters into an int64_t. * - * This will stop at one of the following: EOS, location.stop, or a non-digit. - * This works like atoi, except there is a start/stop range. + * This will stop at one of the following: location.stop or a non-digit. + * This will ignore NULL values. * This will process signed statuses (+/-). * This will detect based types as follows: * - hexidecimals begin with either '0x' or '0X'. + * - duodecimals begin with either '0d' or '0D'. + * - octals begin with either '0o' or '0O'. + * - binaries begin with either '0b' or '0B'. + * - decimal is used for all other cases. + * + * Leading 0's and whitespace are ignored. + * Whitespace after the first digit is considered invalid. + * + * This function is similar to strtoll(), but the behavior of error handling and special bases are different. + * In particular, octals are specified here with '0b' prefix or '0B' prefix instead of the ridiculous '0' prefix. + * + * @param string + * The string to convert. + * @param number + * This will store the value of the converted string. + * This value is only changed on success. + * @param location + * The start/stop range to convert. + * + * @return + * f_none on success. + * f_no_data is returned if string only contains valid whitespace. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_invalid_number (with error bit) if parameter is not a number. + * f_overflow (with error bit) on integer overflow. + * f_underflow (with error bit) on integer underflow. + * f_incomplete_utf (with error bit) if an incomplete UTF-8 fragment is found. + * + * @see strtoll() + */ +#ifndef _di_f_conversion_string_to_number_signed_ + extern f_return_status f_conversion_string_to_number_signed(const f_string string, int64_t *number, const f_string_location location); +#endif // _di_f_conversion_string_to_number_signed_ + +/** + * Convert a series of positive number characters into an uint64_t. + * + * This will stop at one of the following: location.stop or a non-digit. + * This will ignore NULL values. + * This will not process signed statuses (+/-). + * This will detect based types as follows: + * - hexidecimals begin with either '0x' or '0X'. + * - duodecimals begin with either '0d' or '0D'. * - octals begin with either '0o' or '0O'. * - binaries begin with either '0b' or '0B'. * - decimal is used for all other cases. @@ -245,6 +507,10 @@ extern "C" { * Leading 0's and whitespace are ignored. * Whitespace after the first digit is considered invalid. * + * This function is similar to strtoull(), but the behavior of error handling and special bases are different. + * In particular, octals are specified here with '0b' prefix or '0B' prefix instead of the ridiculous '0' prefix. + * Negative values are reported as such instead of being converted into the unsigned equivalent. + * * @param string * The string to convert. * @param number @@ -259,9 +525,10 @@ extern "C" { * f_invalid_parameter (with error bit) if a parameter is invalid. * f_invalid_number (with error bit) if parameter is not a number. * f_negative_number (with error bit) on negative value. - * f_underflow (with error bit) on underflow. - * f_overflow (with error bit) on overflow. + * f_overflow (with error bit) on integer overflow. * f_incomplete_utf (with error bit) if an incomplete UTF-8 fragment is found. + * + * @see strtoull() */ #ifndef _di_f_conversion_string_to_number_unsigned_ extern f_return_status f_conversion_string_to_number_unsigned(const f_string string, uint64_t *number, const f_string_location location); diff --git a/level_1/fl_console/c/console.c b/level_1/fl_console/c/console.c index 847af52..4ce0e4e 100644 --- a/level_1/fl_console/c/console.c +++ b/level_1/fl_console/c/console.c @@ -4,6 +4,42 @@ extern "C" { #endif +#ifndef _fl_console_parameter_to_number_signed_ + f_return_status fl_console_parameter_to_number_signed(const f_string argument, int64_t *number) { + #ifndef _di_level_0_parameter_checking_ + if (argument == 0) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_f + + if (argument[0] == '\0') { + return f_no_data; + } + + f_string_location location = f_string_location_initialize; + location.start = 0; + location.stop = strlen(argument) - 1; + + return f_conversion_string_to_number_signed(argument, number, location); + } +#endif // _fl_console_parameter_to_number_signed_ + +#ifndef _fl_console_parameter_to_number_unsigned_ + f_return_status fl_console_parameter_to_number_unsigned(const f_string argument, uint64_t *number) { + #ifndef _di_level_0_parameter_checking_ + if (argument == 0) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_f + + if (argument[0] == '\0') { + return f_no_data; + } + + f_string_location location = f_string_location_initialize; + location.start = 0; + location.stop = strlen(argument) - 1; + + return f_conversion_string_to_number_unsigned(argument, number, location); + } +#endif // _fl_console_parameter_to_number_unsigned_ + #ifndef _di_fl_console_parameter_process_ f_return_status fl_console_parameter_process(const f_console_arguments arguments, f_console_parameters parameters, f_string_lengths *remaining) { #ifndef _di_level_1_parameter_checking_ @@ -332,83 +368,6 @@ extern "C" { } #endif // _di_fl_console_parameter_prioritize_ -#ifndef _fl_console_parameter_to_number_unsigned_ - f_return_status fl_console_parameter_to_number_unsigned(const f_string argument, uint64_t *number) { - #ifndef _di_level_0_parameter_checking_ - if (argument == 0) return f_status_set_error(f_invalid_parameter); - #endif // _di_level_0_parameter_checking_f - - if (argument[0] == '\0') { - return f_no_data; - } - - f_string_location location = f_string_location_initialize; - location.start = 0; - location.stop = strlen(argument) - 1; - - f_status status = f_conversion_string_to_number_unsigned(argument, number, location); - - return status; -/* - errno = 0; - - f_string end = 0; - uint64_t converted = (uint64_t) strtoull(argument, &end, 0); - - if (errno == ERANGE) { - if (converted == LLONG_MAX) { - return f_status_set_error(f_overflow); - } - - return f_status_set_error(f_invalid_number); - } - - if (*end == '\0') { - *number = converted; - return f_none; - } - - return f_status_set_error(f_invalid_number); -*/ - } -#endif // _fl_console_parameter_to_number_unsigned_ - -#ifndef _fl_console_parameter_to_number_signed_ - f_return_status fl_console_parameter_to_number_signed(const f_string argument, int64_t *number) { - #ifndef _di_level_0_parameter_checking_ - if (argument == 0) return f_status_set_error(f_invalid_parameter); - #endif // _di_level_0_parameter_checking_f - - if (argument[0] == '\0') { - return f_no_data; - } - - errno = 0; - - f_string end = 0; - int64_t converted = (int64_t) strtoull(argument, &end, 0); - - if (errno == ERANGE) { - if (converted == LLONG_MAX) { - return f_status_set_error(f_overflow); - } - - if (converted == LLONG_MIN) { - return f_status_set_error(f_underflow); - } - - return f_status_set_error(f_invalid_number); - } - - if (*end == '\0') { - *number = converted; - return f_none; - } - - return f_status_set_error(f_invalid_number); - } -#endif // _fl_console_parameter_to_number_signed_ - #ifdef __cplusplus } // extern "C" #endif diff --git a/level_1/fl_console/c/console.h b/level_1/fl_console/c/console.h index d84e076..e1486fc 100644 --- a/level_1/fl_console/c/console.h +++ b/level_1/fl_console/c/console.h @@ -27,6 +27,76 @@ extern "C" { #endif /** + * Convert a console parameter additional argument to a signed 64-bit integer. + * + * This will detect based types as follows: + * - hexidecimals begin with either '0x' or '0X'. + * - duodecimals begin with either '0d' or '0D'. + * - octals begin with either '0o' or '0O'. + * - binaries begin with either '0b' or '0B'. + * - decimal is used for all other cases. + * + * Leading 0's and whitespace are ignored. + * Whitespace after the first digit is considered invalid. + * + * @param argv + * The argument string expected to be a number. + * This is generally passed from the argv[]. + * @param number + * The converted number is stored here. + * This only gets modified on success. + * + * @return + * f_none on success. + * f_no_data the argument is empty or only contains valid whitespace. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_invalid_number (with error bit) if parameter is not a number. + * f_overflow (with error bit) on integer overflow. + * f_underflow (with error bit) on integer underflow. + * f_incomplete_utf (with error bit) if an incomplete UTF-8 fragment is found. + * + * @see f_conversion_string_to_number_signed() + */ +#ifndef _fl_console_parameter_to_number_signed_ + f_return_status fl_console_parameter_to_number_signed(const f_string argument, int64_t *number); +#endif // _fl_console_parameter_to_number_signed_ + +/** + * Convert a console parameter additional argument to an unsigned 64-bit integer. + * + * This will detect based types as follows: + * - hexidecimals begin with either '0x' or '0X'. + * - duodecimals begin with either '0d' or '0D'. + * - octals begin with either '0o' or '0O'. + * - binaries begin with either '0b' or '0B'. + * - decimal is used for all other cases. + * + * Leading 0's and whitespace are ignored. + * Whitespace after the first digit is considered invalid. + * + * @param argv + * The argument string expected to be a number. + * This is generally passed from the argv[]. + * @param number + * The converted number is stored here. + * This only gets modified on success. + * + * @return + * f_none on success. + * f_no_data the argument is empty or only contains valid whitespace. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_invalid_number (with error bit) if parameter is not a number. + * f_negative_number (with error bit) on negative value. + * f_overflow (with error bit) on integer overflow. + * f_incomplete_utf (with error bit) if an incomplete UTF-8 fragment is found. + * + * @see f_conversion_string_to_number_unsigned() + */ +#ifndef _fl_console_parameter_to_number_unsigned_ + f_return_status fl_console_parameter_to_number_unsigned(const f_string argument, uint64_t *number); +#endif // _fl_console_parameter_to_number_unsigned_ + +/** * Process console parameters. * * Short parameters are processed as follows: @@ -99,80 +169,6 @@ extern "C" { extern f_return_status fl_console_parameter_prioritize(const f_console_parameters parameters, const f_console_parameter_ids choices, f_console_parameter_id *decision); #endif // _di_fl_console_parameter_prioritize__ -/** - * Convert a console parameter additional argument to an unsigned integer. - * - * Unlike strtoull(), this only accepts complete numbers. - * If the argument has anything else, such as "123abc", this will consider the number to be "123". - * - * This accepts base-16, base-10, and base-8. - * - Base-16 is prefixed with '0x' or '0X'. - * - Base-10 is not prefixed. - * - Base-8 is prefixed with '0'. - * - * Note: The idea of an octal (base-8) being prefixed with '0' is a horrible mistake. - * This is done by strtoull(). - * In the future, custom code may be used in place of strtoull() to use '0o' for octal. - * Furthermore, '0b' for binary should be supported as well. - * - * @param argv - * The argument string expected to be a number. - * This is generally passed from the argv[]. - * @param number - * The converted number is stored here. - * This only gets modified on success. - * - * @return - * f_none on success. - * f_no_data the argument is empty. - * f_invalid_parameter (with error bit) if a parameter is invalid. - * f_invalid_number (with error bit) if parameter is not a number. - * f_negative_number (with error bit) on negative value. - * f_overflow (with error bit) on overflow. - * - * @see strtoull() - */ -#ifndef _fl_console_parameter_to_number_unsigned_ - f_return_status fl_console_parameter_to_number_unsigned(const f_string argument, uint64_t *number); -#endif // _fl_console_parameter_to_number_unsigned_ - -/** - * Convert a console parameter additional argument to a signed integer. - * - * Unlike strtoll(), this only accepts complete numbers. - * If the argument has anything else, such as "123abc", this will consider the number to be "123". - * - * This accepts base-16, base-10, and base-8. - * - Base-16 is prefixed with '0x' or '0X'. - * - Base-10 is not prefixed. - * - Base-8 is prefixed with '0'. - * - * Note: The idea of an octal (base-8) being prefixed with '0' is a horrible mistake. - * This is done by strtoull(). - * In the future, custom code may be used in place of strtoull() to use '0o'/'0O' for octal. - * Furthermore, '0b'/'0B' for binary should be supported as well. - * - * @param argv - * The argument string expected to be a number. - * This is generally passed from the argv[]. - * @param number - * The converted number is stored here. - * This only gets modified on success. - * - * @return - * f_none on success. - * f_no_data the argument is empty. - * f_invalid_parameter (with error bit) if a parameter is invalid. - * f_invalid_number (with error bit) if parameter is not a number. - * f_underflow (with error bit) on underflow. - * f_overflow (with error bit) on overflow. - * - * @see strtoll() - */ -#ifndef _fl_console_parameter_to_number_signed_ - f_return_status fl_console_parameter_to_number_signed(const f_string argument, int64_t *number); -#endif // _fl_console_parameter_to_number_signed_ - #ifdef __cplusplus } // extern "C" #endif -- 1.8.3.1