/** @file numericx.c * Source code of the number translator command-line interface. */ #include #include #include #include #include #define PROG_NAME "numericx" /* ||COMPILATION FLAGS|| */ /** * @name Compilation Flags * * These are the valid compilation flags for the definition of numerical systems. * Use them to make the program show more output, or * to set the proprieties of the FROM and the TO numerical system. * * @{ */ /** * @param bool * * Configuration of the resulting numeral system * to have the units place on the end of the writing direction (on the right). */ #ifndef TO_UNITS_ON_THE_END #define TO_UNITS_ON_THE_END false #endif /** * @param bool * * Configuration of the numeral system of the number input * to have the units place on the end of the writing direction (on the right). */ #ifndef FROM_UNITS_ON_THE_END #define FROM_UNITS_ON_THE_END false #endif /** * @param bool * * Configuration of the resulting numeral system * to start counting on the second number, being the * first number void. */ #ifndef TO_FIRST_NUMBER_VOID #define TO_FIRST_NUMBER_VOID false #endif /** * @param bool * * Configuration of the numeral system of the number input * to start counting on the second number, being the * first number void. */ #ifndef FROM_FIRST_NUMBER_VOID #define FROM_FIRST_NUMBER_VOID false #endif /** * @param bool * * Configuration of the resulting numeral system * to have the first number as an infinite number, * for example, if the first number is 'A', then * A == AA == AAA == AAAA ... */ #ifndef TO_INFINITE_BASE #define TO_INFINITE_BASE false #endif /** * @param bool * * Configuration of the numeral system of the number input * to have the first number as an infinite number, * for example, if the first number is 'A', then * A == AA == AAA == AAAA ... */ #ifndef FROM_INFINITE_BASE #define FROM_INFINITE_BASE false #endif /** @} */ /* ||DATA STRUCTURE|| */ /** * @brief Numeral structure * * This struct is in use to apply our operations on the number. * The number are converted to this struct, the operations are applied, * and on the end, the resulting number is converted from this struct. */ typedef struct NumeralPtr { char const* symbol; /**< a pointer to the glyph/numeral/symbol of this digit. */ struct NumeralPtr *next; /**< a pointer to the next digit */ struct NumeralPtr *previous; /**< a pointer to the previous digit */ } numeral_ptr; /* ||FUNCTIONS|| */ /** * @brief Create new digit for numeral_ptr. * * It needs the last_numeral ('last_numeral') into which will * add the new numeral, and also need to know the symbol of the * new numeral ('to_first'). * * @param last_numeral - last numeral_ptr* into which new numeral_ptr will be added. * @param to_first - symbol of the new digit. * * @return pointer to new numeral_ptr in case of success. * @return NULL in case of no memory. */ numeral_ptr* new_digit(numeral_ptr* last_numeral, char const* to_first) { numeral_ptr* new_numeral = malloc(sizeof(numeral_ptr)); if(new_numeral == NULL) { fprintf(stderr, "error: %s: %s!", PROG_NAME, strerror(ENOMEM)); return NULL; } new_numeral->symbol = to_first; new_numeral->previous = last_numeral; new_numeral->next = NULL; return new_numeral; } /** * @brief Initializer of numeral_ptr * * Creates a number of the form numeral_ptr that * begins with 'case' number of cases, and all cases * are set to 'to_first' char. * * @param to_first - the symbol of the cases for the new number. * @param cases - number of cases of the new number. * * @return pointer to numeral_ptr in case of success. * @return NULL in case of no memory ( because depends * on new_digit() ). */ numeral_ptr* numeral_infinity(char const* to_first, size_t cases) { numeral_ptr* starting_case = new_digit(NULL, to_first); numeral_ptr* other_case = starting_case; for(size_t i = 2; i <= cases; ++i) { other_case->next = new_digit(other_case, to_first); other_case = other_case->next; } return starting_case; } /** * @brief Increment numeral_ptr 'numeral' one unit up. * * It needs 'num_first' as the first numeral of the numerical * system. Also needs 'num_last' as the last numeral of the * numerical system. * When incrementing, if the function adds a new digit/numeral, it * will use the 'brand_new_digit' as its new numeral/digit. * * @param numeral - numeral to increment. * @param num_first - number's numeral system first numeral. * @param num_last - number's numeral system last numeral. * @param brand_new_digit - brand new created numeral, if needed by incrementation. */ void increment(numeral_ptr* numeral, char* num_first, char* num_last, char* brand_new_digit) { bool cycled = false; if( numeral->symbol == num_last ) { if( numeral->next == NULL ) numeral->next = new_digit(numeral, brand_new_digit); else increment(numeral->next, num_first, num_last, brand_new_digit); cycled = true; } if( cycled ) numeral->symbol = num_first; else ++(numeral->symbol); } /** * @brief Decrement a string 'number' by one unit. * * It needs to know the first numeral ('first_numeral') and also the * last numeral ('last_numeral'). It also needs the numeral system in * form of a string ('numeral_system') to know which is the numeral * before the actual numeral that needs decrementation. * * @param number - the string number to be decremented. * @param first_numeral - number's numeral system first numeral. * @param last_numeral - number's numeral system last numeral. * @param numeral_system - string of the corresponding number's numeral system numeral. */ void decrement_number_string(char* number, char* first_numeral, char* last_numeral, char* numeral_system) { while( *number == *first_numeral ) { *number = *last_numeral; ++number; } if( *number == '\0' ) { *(--number) = '\0'; } else { while( !(*numeral_system == *number) ) ++numeral_system; *number = *(--numeral_system); } } /** * @brief Check if numeral_ptr and string are of the same value * * Compares if a numeral_ptr ('numeral') matches the string ('number_arg'). * * @param numeral - numeral_ptr to check. * @param number_arg - string to check. * * @return true if matches. * @return false if doesn't match. */ bool is_the_same(numeral_ptr* numeral, char* number_arg) { while( !(numeral == NULL) ) { if( *(numeral->symbol) == *(number_arg) ) { numeral = numeral->next; ++(number_arg); continue; } return false; } return (*number_arg == '\0') ? true : false; } /** * @brief Print numeral * * Prints to standard output numeral_ptr ('numeral'). * * @param numeral - numeral_ptr to print. */ void print_numeral(numeral_ptr* numeral) { if( TO_UNITS_ON_THE_END ) { while( !(numeral->next == NULL) ) numeral = numeral->next; while( !(numeral == NULL) ) { printf("%c", *(numeral->symbol)); numeral = numeral->previous; } } else { while( !(numeral == NULL) ) { printf("%c", *(numeral->symbol)); numeral = numeral->next; } } } /** * @brief Reverse a string. * * @param string - string to be reversed. */ void reverse_string(char* string) { char tmp; char* string_end = string + strlen(string); while ( --string_end > string ) { tmp = *string; *string++ = *string_end; *string_end = tmp; } } /** * @brief Does string belongs to numeral system? * * Check if string 'number' belongs to the numerical * system ('numeral_system'), which is also a string. * Belonging mean all the symbols of 'number' are included * in 'numeral_system'. * * @param numeral_system - numeral system string to check against. * @param number - number to see if belongs. * * @return true if it belongs. * @return false if it doesn't belong. */ bool is_valid_number(char* numeral_system, char *number) { /* if( *number == '-' || *number == '+' ) ++number; */ while( !(*number == '\0') ) { if( strchr(numeral_system, *number) == NULL ) return false; ++number; } return true; } /** * @brief Free numeral memory. * * Free up numeral_ptr ('numeral') linked chain. * * @param numeral - numeral_ptr to free. */ void free_numeral(numeral_ptr* numeral) { numeral_ptr* tmp = NULL; while( !(numeral == NULL) ) { tmp = numeral->next; free(numeral); numeral = tmp; } } /** * @brief Free numericx result. * * Function frees up the result of numericx_translate(), * after we are done with it. * * @param string - char pointer to be free. */ void numericx_free(char* string) { free(string); } /** * @brief Convert numeral_ptr to string. * * Allocates space for string and converts numeral_ptr* to char*. * * @param numeral - numeral_ptr to be converted to string. * @param result_with_units_on_the_end - define if result has units on the end. * * @return char pointer to converted string in case of success. * @return NULL in case of no memory. */ char* numeral_to_string(numeral_ptr* numeral, bool result_with_units_on_the_end) { size_t length = 1; numeral_ptr* numeral_last = numeral; while( !(numeral_last->next == NULL) ) { ++length; numeral_last = numeral_last->next; } char* result = malloc((length + 1) * sizeof(char)); char* tmp = result; if( !result_with_units_on_the_end ) { for( numeral_ptr* digit = numeral; !(digit == NULL); digit = digit->next) { *tmp = *(digit->symbol); ++tmp; } } else { for( numeral_ptr* digit = numeral_last; !(digit == NULL); digit = digit->previous) { *tmp = *(digit->symbol); ++tmp; } } *(tmp) = '\0'; return result; } /** * @brief Translate string to a different numerical system. * * After definition of the 'from' numerical system proprieties and the * 'to' numerical system proprieties, will be translate 'number' from the * 'from' to the 'to' numerical system, resulting in a string. * * @param from - string with all the numerals of the number's numerical system. * @param to - string with all the numerals of the resulting number's numerical system. * @param from_units_on_the_end - does the translate from numerical system have units on the end (on the right)? * @param to_units_on_the_end - does the translate to numerical system have units on the end (on the right)? * @param from_first_number_void - does the translate from numerical system start counting on the second number? * @param to_first_number_void - does the translate to numerical system start counting on the second number? * @param from_infinite_base - is the translate from numerical system first numeral infinite? For example, if first numeral is 'A', then does 'A' == 'AA' == 'AAA' == 'AAAA' ... ? * @param to_infinite_base - is the translate to numerical system first numeral infinite? For example, if first numeral is 'A', then does 'A' == 'AA' == 'AAA' == 'AAAA' ... ? * @param number - number of the from numerical system, to be translated into the to numerical system. * @param result_string - string to where to store the result. * * @return EINVAL if argument of result_string is not NULL. * @return EDOM if number doesn't belong to the from numerical system. * @return ERANGE if the resulting number cannot be represented, because of a from void number and a lack of void number in to. * @return EXIT_SUCCESS in case of success. */ int numericx_translate(char* from, bool from_units_on_the_end, bool from_first_number_void, bool from_infinite_base, char* to, bool to_units_on_the_end, bool to_first_number_void, bool to_infinite_base, char* number, char** result_string) { /* result_string has to be NULL */ if( !(*result_string == NULL) ) return EINVAL; /* Check if number belongs to it's numerical system */ if( !is_valid_number(from, number) ) return EDOM; /* _first and _last variables */ char* from_first = from; char* from_last = from + (strlen(from) - 1); char* to_first = to; char* to_last = to + (strlen(to) - 1); if( from_units_on_the_end ) { reverse_string(number); } /* initializing counting and result */ numeral_ptr* counting = NULL; numeral_ptr* result = NULL; if( from_infinite_base ) counting = numeral_infinity(from_first, strlen(number)); else counting = numeral_infinity(from_first, 1); result = numeral_infinity(to_first, 1); /* first number void */ if( !(from_first_number_void && to_first_number_void) ) { if( from_first_number_void ) { if( strlen(number) == 1 && *number == *from_first ) { free_numeral(counting); free_numeral(result); return ERANGE; } decrement_number_string(number, from_first, from_last, from_first); } if( to_first_number_void ) increment(result, to_first, to_last, to_first); } /* minor optimization for the cycle below */ char* to_second = NULL; if( to_infinite_base ) to_second = to_first + 1; /* increments until it finishes */ while( !is_the_same(counting, number) ) { if( to_infinite_base ) increment(result, to_first, to_last, to_second); else increment(result, to_first, to_last, to_first); increment(counting, from_first, from_last, from_first); } /* result to string (result_string) */ *result_string = numeral_to_string(result, to_units_on_the_end); /* free memory */ free_numeral(counting); free_numeral(result); return EXIT_SUCCESS; } /* ||MAIN FUNCTION|| */ /** * @brief CLI program. * * This is a command-line interface program, that uses the 'numericx_*' * functions. It receives on number and returns the translated number. * * @param argv[] - one number as a string * * @return E2BIG in case of wrong number of arguments * @return EXIT_FAILURE in case of unsuccessful number translation * @return EXIT_SUCCESS in case of a successful number translation */ int main(int argc, char* argv[]) { /* argument processing */ if( !(argc == 2) ) { fprintf(stderr, "usage: %s \n", PROG_NAME); return E2BIG; } /* Number variables to be converted */ char* number = argv[1]; /* Arguments for translation */ char* from = malloc( (strlen(FROM_NUMERICALS) + 1) * sizeof(char) ); char* to = malloc( (strlen(TO_NUMERICALS) + 1) * sizeof(char) ); strcpy(from, FROM_NUMERICALS); strcpy(to, TO_NUMERICALS); bool to_units_on_the_end = TO_UNITS_ON_THE_END; bool from_units_on_the_end = FROM_UNITS_ON_THE_END; bool to_first_number_void = TO_FIRST_NUMBER_VOID; bool from_first_number_void = FROM_FIRST_NUMBER_VOID; bool to_infinite_base = TO_INFINITE_BASE; bool from_infinite_base = FROM_INFINITE_BASE; /* Translation */ char* result = NULL; int status = numericx_translate( from, from_units_on_the_end, from_first_number_void, from_infinite_base, to, to_units_on_the_end, to_first_number_void, to_infinite_base, number, &result); /* Test for translation failure */ switch( status ) { case EINVAL: fprintf(stderr, "error: %s: %s. Resulting string variable has to be NULL.\n", PROG_NAME, strerror(EINVAL)); break; case EDOM: fprintf(stderr, "error: %s: %s. Valid numerals are: \"%s\".\n", PROG_NAME, strerror(EDOM), from); break; case ERANGE: fprintf(stderr, "error: %s: Unrepresentable void number.\n", PROG_NAME); break; } if( !(status == EXIT_SUCCESS) ) { fprintf(stderr, "error: %s: Incapable of translating.\n", PROG_NAME); free(from); free(to); numericx_free(result); return EXIT_FAILURE; } /* Print */ printf("%s\n", result); /* Free */ free(from); free(to); numericx_free(result); return EXIT_SUCCESS; }