From 4c4e5f9cb4faf71c304060b0622a7b43b6280fb2 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 7 Oct 2023 21:03:07 +0200 Subject: parser for INI configuration of DD51 currency specifications --- src/include/taler_util.h | 77 ++++++++++++++ src/util/config.c | 257 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 334 insertions(+) diff --git a/src/include/taler_util.h b/src/include/taler_util.h index 77cb58591..66951e124 100644 --- a/src/include/taler_util.h +++ b/src/include/taler_util.h @@ -209,6 +209,83 @@ TALER_config_get_currency (const struct GNUNET_CONFIGURATION_Handle *cfg, char **currency); +/** + * Details about how to render a currency. + */ +struct TALER_CurrencySpecification +{ + /** + * Name of the currency. + */ + char currency[TALER_CURRENCY_LEN]; + + /** + * Character used to separate decimals. String as + * multi-byte sequences may be required (UTF-8!). + */ + char *decimal_separator; + + /** + * how many digits the user may enter at most after the @e decimal_separator + */ + unsigned int num_fractional_input_digits; + + /** + * how many digits we render in normal scale after the @e decimal_separator + */ + unsigned int num_fractional_normal_digits; + + /** + * how many digits we render in after the @e decimal_separator even if all + * remaining digits are zero. + */ + unsigned int num_fractional_trailing_zero_digits; + + /** + * True to put the currency symbol before the number, + * false to put the currency symbol after the number. + */ + bool is_currency_name_leading; + + /** + * Mapping of powers of 10 to alternative currency names or symbols. + * Keys are the decimal powers, values the currency symbol to use. + * Map MUST contain an entry for "0" to the default currency symbol. + */ + json_t *map_alt_unit_names; + +}; + + +/** + * Parse information about supported currencies from + * our configuration. + * + * @param cfg configuration to parse + * @param[out] num_currencies set to number of enabled currencies, length of @e cspecs + * @param[out] cspecs set to currency specification array + * @return #GNUNET_OK on success, #GNUNET_NO if zero + * currency specifications were enabled, + * #GNUNET_SYSERR if the configuration was malformed + */ +enum GNUNET_GenericReturnValue +TALER_CONFIG_parse_currencies (const struct GNUNET_CONFIGURATION_Handle *cfg, + unsigned int *num_currencies, + struct TALER_CurrencySpecification **cspecs); + + +/** + * Free @a cspecs array. + * + * @param num_currencies length of @a cspecs array + * @param[in] cspecs array to free + */ +void +TALER_CONFIG_free_currencies ( + unsigned int num_currencies, + struct TALER_CurrencySpecification cspecs[static num_currencies]); + + /** * Allow user to specify an amount on the command line. * diff --git a/src/util/config.c b/src/util/config.c index c00792469..125ea7b1b 100644 --- a/src/util/config.c +++ b/src/util/config.c @@ -166,3 +166,260 @@ TALER_config_get_currency (const struct GNUNET_CONFIGURATION_Handle *cfg, } return GNUNET_OK; } + + +/** + * Closure for #parse_currencies_cb(). + */ +struct CurrencyParserContext +{ + /** + * Current offset in @e cspecs. + */ + unsigned int num_currencies; + + /** + * Length of the @e cspecs array. + */ + unsigned int len_cspecs; + + /** + * Array of currency specifications (see DD 51). + */ + struct TALER_CurrencySpecification *cspecs; + + /** + * Configuration we are parsing. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Set to true if the configuration was malformed. + */ + bool failure; +}; + + +/** + * Function to iterate over section. + * + * @param cls closure with a `struct CurrencyParserContext *` + * @param section name of the section + */ +static void +parse_currencies_cb (void *cls, + const char *section) +{ + struct CurrencyParserContext *cpc = cls; + struct TALER_CurrencySpecification *cspec; + unsigned long long num; + char *str; + + if (cpc->failure) + return; + if (0 != strncasecmp (section, + "currency-", + strlen ("currency-"))) + return; /* not interesting */ + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_yesno (cpc->cfg, + section, + "ENABLED")) + return; /* disabled */ + if (cpc->len_cspecs == cpc->num_currencies) + { + GNUNET_array_grow (cpc->cspecs, + cpc->len_cspecs, + cpc->len_cspecs * 2 + 4); + } + cspec = &cpc->cspecs[cpc->num_currencies++]; + cspec->is_currency_name_leading + = GNUNET_CONFIGURATION_get_value_yesno (cpc->cfg, + section, + "NAME_LEADING"); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cpc->cfg, + section, + "NAME", + &str)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "NAME"); + cpc->failure = true; + return; + } + if (strlen (str) >= TALER_CURRENCY_LEN) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "NAME", + "Name given is too long"); + cpc->failure = true; + GNUNET_free (str); + return; + } + strcpy (cspec->currency, + str); + GNUNET_free (str); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cpc->cfg, + section, + "DECIMAL_SEPARATOR", + &str)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "DECIMAL_SEPARATOR"); + cpc->failure = true; + return; + } + cspec->decimal_separator = str; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cpc->cfg, + section, + "FRACTIONAL_INPUT_DIGITS", + &num)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "FRACTIONAL_INPUT_DIGITS"); + cpc->failure = true; + return; + } + if (num > TALER_AMOUNT_FRAC_LEN) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "FRACTIONAL_INPUT_DIGITS", + "Number given is too big"); + cpc->failure = true; + return; + } + cspec->num_fractional_input_digits = num; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cpc->cfg, + section, + "FRACTIONAL_NORMAL_DIGITS", + &num)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "FRACTIONAL_NORMAL_DIGITS"); + cpc->failure = true; + return; + } + if (num > TALER_AMOUNT_FRAC_LEN) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "FRACTIONAL_NORMAL_DIGITS", + "Number given is too big"); + cpc->failure = true; + return; + } + cspec->num_fractional_normal_digits = num; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cpc->cfg, + section, + "FRACTIONAL_TRAILING_ZERO_DIGITS", + &num)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "FRACTIONAL_TRAILING_ZERO_DIGITS"); + cpc->failure = true; + return; + } + if (num > TALER_AMOUNT_FRAC_LEN) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "FRACTIONAL_TRAILING_ZERO_DIGITS", + "Number given is too big"); + cpc->failure = true; + return; + } + cspec->num_fractional_trailing_zero_digits = num; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cpc->cfg, + section, + "ALT_UNIT_NAMES", + &str)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "ALT_UNIT_NAMES"); + cpc->failure = true; + return; + } + { + json_error_t err; + + cspec->map_alt_unit_names = json_loads (str, + JSON_REJECT_DUPLICATES, + &err); + GNUNET_free (str); + if (NULL == cspec->map_alt_unit_names) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "ALT_UNIT_NAMES", + err.text); + cpc->failure = true; + return; + } + } +} + + +enum GNUNET_GenericReturnValue +TALER_CONFIG_parse_currencies (const struct GNUNET_CONFIGURATION_Handle *cfg, + unsigned int *num_currencies, + struct TALER_CurrencySpecification **cspecs) +{ + struct CurrencyParserContext cpc = { + .cfg = cfg + }; + + GNUNET_CONFIGURATION_iterate_sections (cfg, + &parse_currencies_cb, + &cpc); + if (cpc.failure) + { + GNUNET_array_grow (cpc.cspecs, + cpc.len_cspecs, + 0); + return GNUNET_SYSERR; + } + GNUNET_array_grow (cpc.cspecs, + cpc.len_cspecs, + cpc.num_currencies); + *num_currencies = cpc.num_currencies; + *cspecs = cpc.cspecs; + if (0 == *num_currencies) + return GNUNET_NO; + return GNUNET_OK; +} + + +void +TALER_CONFIG_free_currencies ( + unsigned int num_currencies, + struct TALER_CurrencySpecification cspecs[static num_currencies]) +{ + for (unsigned int i = 0; idecimal_separator); + json_decref (cspec->map_alt_unit_names); + } + GNUNET_array_grow (cspecs, + num_currencies, + 0); +} -- cgit v1.2.3