diff --git a/src/exch.c b/src/exch.c index aff35a7..0247d7d 100644 --- a/src/exch.c +++ b/src/exch.c @@ -96,9 +96,8 @@ void exchange_stock (void) } // Handle the locale's currency symbol - struct lconv *lc = localeconv(); - assert(lc != NULL); - snprintf(buf, BUFSIZE, "share (%s)", lc->currency_symbol); + snprintf(buf, BUFSIZE, "share (%s)", + localeconv_info.currency_symbol); wattrset(curwin, ATTR_WINDOW_SUBTITLE); mvwprintw(curwin, 4, 2, " %-22s %12s %10s %10s %10s ", @@ -111,7 +110,7 @@ void exchange_stock (void) if (company[i].on_map) { mvwaddch(curwin, line, 2, PRINTABLE_MAP_VAL(COMPANY_TO_MAP(i)) | ATTR_MAP_CHOICE); - strfmon(buf, BUFSIZE, "%!12n", company[i].share_price); + l_strfmon(buf, BUFSIZE, "%!12n", company[i].share_price); mvwprintw(curwin, line, 4, "%-22s %12s %10.2f %'10ld %'10ld ", company[i].name, buf, company[i].share_return * 100.0, company[i].stock_issued, @@ -241,9 +240,6 @@ void visit_bank (void) double val, max; char *buf; - struct lconv *lc = localeconv(); - assert(lc != NULL); - buf = malloc(BUFSIZE); if (buf == NULL) { @@ -263,18 +259,18 @@ void visit_bank (void) center(curwin, 1, ATTR_WINDOW_TITLE, " Interstellar Trading Bank "); - strfmon(buf, BUFSIZE, "%18n", player[current_player].cash); + l_strfmon(buf, BUFSIZE, "%18n", player[current_player].cash); center2(curwin, 3, ATTR_NORMAL_WINDOW, ATTR_HIGHLIGHT_STR, "Current cash: ", " %s ", buf); - strfmon(buf, BUFSIZE, "%18n", player[current_player].debt); + l_strfmon(buf, BUFSIZE, "%18n", player[current_player].debt); center2(curwin, 4, ATTR_NORMAL_WINDOW, ATTR_HIGHLIGHT_STR, "Current debt: ", " %s ", buf); center2(curwin, 5, ATTR_NORMAL_WINDOW, ATTR_HIGHLIGHT_STR, "Interest rate: ", " %17.2f%% ", interest_rate * 100.0); - strfmon(buf, BUFSIZE, "%18n", credit_limit); + l_strfmon(buf, BUFSIZE, "%18n", credit_limit); center2(curwin, 7, ATTR_HIGHLIGHT_STR, ATTR_WINDOW_TITLE, "Credit limit: ", " %s ", buf); @@ -354,16 +350,17 @@ void visit_bank (void) mvwprintw(curwin, 3, 10, "How much do you wish to borrow? "); wattron(curwin, A_BOLD); - if (lc->p_cs_precedes == 1) { - wprintw(curwin, "%s%s", lc->currency_symbol, - (lc->p_sep_by_space == 1) ? " " : ""); + if (localeconv_info.p_cs_precedes == 1) { + wprintw(curwin, "%s%s", localeconv_info.currency_symbol, + (localeconv_info.p_sep_by_space == 1) ? " " : ""); n = 10; } else { getyx(curwin, y, x); - n = strlen(lc->currency_symbol) + 10 + (lc->p_sep_by_space == 1); + n = strlen(localeconv_info.currency_symbol) + 10 + + (localeconv_info.p_sep_by_space == 1); mvwprintw(curwin, y, getmaxx(curwin) - n, "%s%s", - (lc->p_sep_by_space == 1) ? " " : "", - lc->currency_symbol); + (localeconv_info.p_sep_by_space == 1) ? " " : "", + localeconv_info.currency_symbol); wmove(curwin, y, x); } wattroff(curwin, A_BOLD); @@ -414,16 +411,17 @@ void visit_bank (void) mvwprintw(curwin, 3, 10, "How much do you wish to repay? "); wattron(curwin, A_BOLD); - if (lc->p_cs_precedes == 1) { - wprintw(curwin, "%s%s", lc->currency_symbol, - (lc->p_sep_by_space == 1) ? " " : ""); + if (localeconv_info.p_cs_precedes == 1) { + wprintw(curwin, "%s%s", localeconv_info.currency_symbol, + (localeconv_info.p_sep_by_space == 1) ? " " : ""); n = 10; } else { getyx(curwin, y, x); - n = strlen(lc->currency_symbol) + 10 + (lc->p_sep_by_space == 1); + n = strlen(localeconv_info.currency_symbol) + 10 + + (localeconv_info.p_sep_by_space == 1); mvwprintw(curwin, y, getmaxx(curwin) - n, "%s%s", - (lc->p_sep_by_space == 1) ? " " : "", - lc->currency_symbol); + (localeconv_info.p_sep_by_space == 1) ? " " : "", + localeconv_info.currency_symbol); wmove(curwin, y, x); } wattroff(curwin, A_BOLD); @@ -508,7 +506,7 @@ void trade_shares (int num, bool *bid_used) company[num].max_stock - company[num].stock_issued); mvwaddstr(curwin, 5, 2, "Price per share: "); - strfmon(buf, BUFSIZE, "%12n", company[num].share_price); + l_strfmon(buf, BUFSIZE, "%12n", company[num].share_price); attrpr(curwin, ATTR_HIGHLIGHT_STR, "%12s", buf); mvwaddstr(curwin, 6, 2, "Return: "); @@ -524,7 +522,7 @@ void trade_shares (int num, bool *bid_used) wmove(curwin, 6, 38); attrpr(curwin, ATTR_HIGHLIGHT_STR, "Current cash: "); - strfmon(buf, BUFSIZE, "%16n", player[current_player].cash); + l_strfmon(buf, BUFSIZE, "%16n", player[current_player].cash); attrpr(curwin, ATTR_WINDOW_TITLE, " %16s ", buf); wrefresh(curwin); diff --git a/src/game.c b/src/game.c index 48c9f3b..6a2ae93 100644 --- a/src/game.c +++ b/src/game.c @@ -455,7 +455,7 @@ void end_game (void) } if (number_players == 1) { - strfmon(buf, BUFSIZE, "%1n", total_value(0)); + l_strfmon(buf, BUFSIZE, "%1n", total_value(0)); newtxwin(9, 60, LINE_OFFSET + 8, COL_CENTER(60)); wbkgd(curwin, ATTR_NORMAL_WINDOW); @@ -468,10 +468,6 @@ void end_game (void) wait_for_key(curwin, 7, ATTR_WAITNORMAL_STR); deltxwin(); } else { - // Handle the locale's currency symbol - struct lconv *lc = localeconv(); - assert(lc != NULL); - // Sort players on the basis of total value for (i = 0; i < number_players; i++) { player[i].sort_value = total_value(i); @@ -489,12 +485,13 @@ void end_game (void) center2(curwin, 4, ATTR_NORMAL_WINDOW, ATTR_STANDOUT_STR, "who is ", "%s", "*** BANKRUPT ***"); } else { - strfmon(buf, BUFSIZE, "%1n", player[0].sort_value); + l_strfmon(buf, BUFSIZE, "%1n", player[0].sort_value); center2(curwin, 4, ATTR_NORMAL_WINDOW, ATTR_HIGHLIGHT_STR, "with a value of ", "%s", buf); } - snprintf(buf, BUFSIZE, "Total Value (%s)", lc->currency_symbol); + snprintf(buf, BUFSIZE, "Total Value (%s)", + localeconv_info.currency_symbol); int w = getmaxx(curwin) - 33; wattrset(curwin, ATTR_WINDOW_SUBTITLE); @@ -502,7 +499,7 @@ void end_game (void) wattrset(curwin, ATTR_NORMAL_WINDOW); for (i = 0; i < number_players; i++) { - strfmon(buf, BUFSIZE, "%!18n", player[i].sort_value); + l_strfmon(buf, BUFSIZE, "%!18n", player[i].sort_value); mvwprintw(curwin, i + 7, 2, "%5s %-*.*s %18s ", ordinal[i + 1], w, w, player[i].name, buf); } @@ -677,9 +674,8 @@ void show_status (int num) center(curwin, 8, ATTR_NORMAL_WINDOW, "No companies on the map"); } else { // Handle the locale's currency symbol - struct lconv *lc = localeconv(); - assert(lc != NULL); - snprintf(buf, BUFSIZE, "share (%s)", lc->currency_symbol); + snprintf(buf, BUFSIZE, "share (%s)", + localeconv_info.currency_symbol); wattrset(curwin, ATTR_WINDOW_SUBTITLE); mvwprintw(curwin, 4, 2, " %-22s %12s %10s %10s %10s ", @@ -690,7 +686,7 @@ void show_status (int num) for (line = 6, i = 0; i < MAX_COMPANIES; i++) { if (company[i].on_map) { - strfmon(buf, BUFSIZE, "%!12n", company[i].share_price); + l_strfmon(buf, BUFSIZE, "%!12n", company[i].share_price); mvwprintw(curwin, line, 2, " %-22s %10s %10.2f %'10ld %10.2f ", company[i].name, buf, @@ -705,18 +701,18 @@ void show_status (int num) } line = 15; - strfmon(buf, BUFSIZE, "%18n", player[num].cash); + l_strfmon(buf, BUFSIZE, "%18n", player[num].cash); center2(curwin, line++, ATTR_NORMAL_WINDOW, ATTR_HIGHLIGHT_STR, "Current cash: ", " %s ", buf); if (player[num].debt != 0.0) { - strfmon(buf, BUFSIZE, "%18n", player[num].debt); + l_strfmon(buf, BUFSIZE, "%18n", player[num].debt); center2(curwin, line++, ATTR_NORMAL_WINDOW, ATTR_HIGHLIGHT_STR, "Current debt: ", " %s ", buf); center2(curwin, line++, ATTR_NORMAL_WINDOW, ATTR_HIGHLIGHT_STR, "Interest rate: ", " %17.2f%% ", interest_rate * 100.0); } - strfmon(buf, BUFSIZE, "%18n", val); + l_strfmon(buf, BUFSIZE, "%18n", val); center2(curwin, line + 1, ATTR_HIGHLIGHT_STR, ATTR_WINDOW_TITLE, "Total value: ", " %s ", buf); diff --git a/src/intf.c b/src/intf.c index 7ffce6b..e2c3dd4 100644 --- a/src/intf.c +++ b/src/intf.c @@ -1287,14 +1287,12 @@ int gettxdouble (WINDOW *win, double *result, double min, double max, { char *buf, *buf_copy; char *allowed, *emptystr, *defaultstr; - struct lconv *lc = localeconv(); double val; bool done; int ret; assert(result != NULL); - assert(lc != NULL); if (max < min) { double n = max; @@ -1330,13 +1328,19 @@ int gettxdouble (WINDOW *win, double *result, double min, double max, *buf = '\0'; strcpy(allowed, "0123456789+-Ee"); - strncat(allowed, lc->decimal_point, BUFSIZE - strlen(allowed) - 1); - strncat(allowed, lc->thousands_sep, BUFSIZE - strlen(allowed) - 1); - strncat(allowed, lc->mon_decimal_point, BUFSIZE - strlen(allowed) - 1); - strncat(allowed, lc->mon_thousands_sep, BUFSIZE - strlen(allowed) - 1); + strncat(allowed, localeconv_info.decimal_point, + BUFSIZE - strlen(allowed) - 1); + strncat(allowed, localeconv_info.thousands_sep, + BUFSIZE - strlen(allowed) - 1); + strncat(allowed, localeconv_info.mon_decimal_point, + BUFSIZE - strlen(allowed) - 1); + strncat(allowed, localeconv_info.mon_thousands_sep, + BUFSIZE - strlen(allowed) - 1); - snprintf(emptystr, BUFSIZE, "%'1.*f", lc->frac_digits, emptyval); - snprintf(defaultstr, BUFSIZE, "%'1.*f", lc->frac_digits, defaultval); + snprintf(emptystr, BUFSIZE, "%'1.*f", localeconv_info.frac_digits, + emptyval); + snprintf(defaultstr, BUFSIZE, "%'1.*f", localeconv_info.frac_digits, + defaultval); done = false; while (! done) { @@ -1350,32 +1354,35 @@ int gettxdouble (WINDOW *win, double *result, double min, double max, buf_copy[BUFSIZE - 1] = '\0'; // Replace mon_decimal_point with decimal_point - if (strcmp(lc->mon_decimal_point, lc->decimal_point) != 0) { - while ((p = strstr(buf_copy, lc->mon_decimal_point)) != NULL) { + if (strcmp(localeconv_info.mon_decimal_point, "") != 0 + && strcmp(localeconv_info.decimal_point, "") != 0 + && strcmp(localeconv_info.mon_decimal_point, + localeconv_info.decimal_point) != 0) { + while ((p = strstr(buf_copy, localeconv_info.mon_decimal_point)) != NULL) { char *pn; - int len1 = strlen(lc->mon_decimal_point); - int len2 = strlen(lc->decimal_point); + int len1 = strlen(localeconv_info.mon_decimal_point); + int len2 = strlen(localeconv_info.decimal_point); - // Make space for lc->decimal_point, if needed + // Make space for localeconv_info.decimal_point, if needed memmove(p + len2, p + len1, strlen(p) - (len2 - len1) + 1); - // Copy lc->decimal_point over p WITHOUT copying ending NUL - for (pn = lc->decimal_point; *pn != '\0'; pn++, p++) { + // Copy localeconv_info.decimal_point over p WITHOUT copying ending NUL + for (pn = localeconv_info.decimal_point; *pn != '\0'; pn++, p++) { *p = *pn; } } } // Remove thousands separators if required - if (strcmp(lc->thousands_sep, "") != 0) { - while ((p = strstr(buf_copy, lc->thousands_sep)) != NULL) { - int len = strlen(lc->thousands_sep); + if (strcmp(localeconv_info.thousands_sep, "") != 0) { + while ((p = strstr(buf_copy, localeconv_info.thousands_sep)) != NULL) { + int len = strlen(localeconv_info.thousands_sep); memmove(p, p + len, strlen(p) - len + 1); } } - if (strcmp(lc->mon_thousands_sep, "") != 0) { - while ((p = strstr(buf_copy, lc->mon_thousands_sep)) != NULL) { - int len = strlen(lc->thousands_sep); + if (strcmp(localeconv_info.mon_thousands_sep, "") != 0) { + while ((p = strstr(buf_copy, localeconv_info.mon_thousands_sep)) != NULL) { + int len = strlen(localeconv_info.thousands_sep); memmove(p, p + len, strlen(p) - len + 1); } } @@ -1427,14 +1434,12 @@ int gettxlong (WINDOW *win, long *result, long min, long max, long emptyval, { char *buf, *buf_copy; char *allowed, *emptystr, *defaultstr; - struct lconv *lc = localeconv(); long val; bool done; int ret; assert(result != NULL); - assert(lc != NULL); if (max < min) { long n = max; @@ -1470,8 +1475,10 @@ int gettxlong (WINDOW *win, long *result, long min, long max, long emptyval, *buf = '\0'; strcpy(allowed, "0123456789+-"); - strncat(allowed, lc->thousands_sep, BUFSIZE - strlen(allowed) - 1); - strncat(allowed, lc->mon_thousands_sep, BUFSIZE - strlen(allowed) - 1); + strncat(allowed, localeconv_info.thousands_sep, + BUFSIZE - strlen(allowed) - 1); + strncat(allowed, localeconv_info.mon_thousands_sep, + BUFSIZE - strlen(allowed) - 1); snprintf(emptystr, BUFSIZE, "%'1ld", emptyval); snprintf(defaultstr, BUFSIZE, "%'1ld", defaultval); @@ -1488,15 +1495,15 @@ int gettxlong (WINDOW *win, long *result, long min, long max, long emptyval, buf_copy[BUFSIZE - 1] = '\0'; // Remove thousands separators if required - if (strcmp(lc->thousands_sep, "") != 0) { - while ((p = strstr(buf_copy, lc->thousands_sep)) != NULL) { - int len = strlen(lc->thousands_sep); + if (strcmp(localeconv_info.thousands_sep, "") != 0) { + while ((p = strstr(buf_copy, localeconv_info.thousands_sep)) != NULL) { + int len = strlen(localeconv_info.thousands_sep); memmove(p, p + len, strlen(p) - len + 1); } } - if (strcmp(lc->mon_thousands_sep, "") != 0) { - while ((p = strstr(buf_copy, lc->mon_thousands_sep)) != NULL) { - int len = strlen(lc->thousands_sep); + if (strcmp(localeconv_info.mon_thousands_sep, "") != 0) { + while ((p = strstr(buf_copy, localeconv_info.mon_thousands_sep)) != NULL) { + int len = strlen(localeconv_info.thousands_sep); memmove(p, p + len, strlen(p) - len + 1); } } diff --git a/src/move.c b/src/move.c index ca5a39d..f2b5855 100644 --- a/src/move.c +++ b/src/move.c @@ -789,10 +789,7 @@ void merge_companies (map_val_t a, map_val_t b) "%-20s", company[aa].name); // Handle the locale's currency symbol - struct lconv *lc = localeconv(); - assert(lc != NULL); - - snprintf(buf, BUFSIZE, "Bonus (%s)", lc->currency_symbol); + snprintf(buf, BUFSIZE, "Bonus (%s)", localeconv_info.currency_symbol); int w = getmaxx(curwin) - 52; wattrset(curwin, ATTR_WINDOW_SUBTITLE); @@ -816,7 +813,7 @@ void merge_companies (map_val_t a, map_val_t b) player[i].stock_owned[bb] = 0; player[i].cash += bonus; - strfmon(buf, BUFSIZE, "%!12n", bonus); + l_strfmon(buf, BUFSIZE, "%!12n", bonus); mvwprintw(curwin, line, 2, " %-*.*s %'8ld %'8ld %'8ld %12s ", w, w, player[i].name, old_stock, new_stock, player[i].stock_owned[aa], buf); @@ -992,11 +989,11 @@ void adjust_values (void) center(curwin, 7, ATTR_ERROR_WINDOW, "of the share value on each share owned."); - strfmon(buf, BUFSIZE, "%12n", company[which].share_price); + l_strfmon(buf, BUFSIZE, "%12n", company[which].share_price); center2(curwin, 9, ATTR_ERROR_WINDOW, ATTR_ERROR_STR, "Old share value: ", "%s", buf); - strfmon(buf, BUFSIZE, "%12n", company[which].share_price * rate); + l_strfmon(buf, BUFSIZE, "%12n", company[which].share_price * rate); center2(curwin, 10, ATTR_ERROR_WINDOW, ATTR_ERROR_STR, "Amount paid per share: ", "%s", buf); @@ -1095,10 +1092,10 @@ void adjust_values (void) center(curwin, 1, ATTR_ERROR_TITLE, " Interstellar Trading Bank "); - strfmon(buf, BUFSIZE, "%1n", player[current_player].debt); + l_strfmon(buf, BUFSIZE, "%1n", player[current_player].debt); center(curwin, 3, ATTR_ERROR_STR, "Your debt has amounted to %s", buf); - strfmon(buf, BUFSIZE, "%1n", impounded); + l_strfmon(buf, BUFSIZE, "%1n", impounded); center3(curwin, 4, ATTR_ERROR_WINDOW, ATTR_ERROR_WINDOW, ATTR_ERROR_STR, "The Bank has impounded ", " from your cash", "%s", buf); diff --git a/src/trader.c b/src/trader.c index de7816c..0b98d02 100644 --- a/src/trader.c +++ b/src/trader.c @@ -343,6 +343,9 @@ void init_program (void) // Initialise the random number generator init_rand(); + // Initialise locale-specific variables + init_locale(); + // Initialise signal-handling functions // @@@ To be completed diff --git a/src/utils.c b/src/utils.c index 2c64795..291df8f 100644 --- a/src/utils.c +++ b/src/utils.c @@ -31,6 +31,14 @@ #include "trader.h" +/************************************************************************ +* Global variable definitions * +************************************************************************/ + +// Global copy, suitably modified, of localeconv() information +struct lconv localeconv_info; + + /************************************************************************ * Module-specific constants and variable definitions * ************************************************************************/ @@ -38,6 +46,12 @@ #define GAME_FILENAME_PROTO "game%d" #define GAME_FILENAME_BUFSIZE 16 +// Default values used to override POSIX locale +#define MOD_POSIX_CURRENCY_SYMBOL "$" +#define MOD_POSIX_FRAC_DIGITS 2 +#define MOD_POSIX_P_CS_PRECEDES 1 +#define MOD_POSIX_P_SEP_BY_SPACE 0 + /************************************************************************ * Module-specific variables * @@ -47,6 +61,9 @@ static char *program_name_str = NULL; // Canonical program name static char *home_directory_str = NULL; // Full pathname to home static char *data_directory_str = NULL; // Writable data dir pathname +static char *current_mon_locale; // As returned by setlocale() +static bool add_currency_symbol = false; // Do we need to add "$"? + /************************************************************************ * Initialisation and environment function definitions * @@ -56,6 +73,8 @@ static char *data_directory_str = NULL; // Writable data dir pathname /***********************************************************************/ +// init_program_name: Make the program name "canonical" + void init_program_name (char *argv[]) { if (argv == NULL || argv[0] == NULL || *argv[0] == '\0') { @@ -73,6 +92,8 @@ void init_program_name (char *argv[]) /***********************************************************************/ +// program_name: Return the canonical program name + const char *program_name (void) { if (program_name_str == NULL) { @@ -84,6 +105,8 @@ const char *program_name (void) /***********************************************************************/ +// home_directory: Return home directory pathname + const char *home_directory (void) { if (home_directory_str == NULL) { @@ -100,6 +123,8 @@ const char *home_directory (void) /***********************************************************************/ +// data_directory: Return writable data directory pathname + const char *data_directory (void) { /* This implementation assumes a POSIX environment by using "/" as @@ -126,6 +151,8 @@ const char *data_directory (void) /***********************************************************************/ +// game_filename: Convert an integer to a game filename + char *game_filename (int gamenum) { /* This implementation assumes a POSIX environment by using "/" as @@ -170,6 +197,8 @@ char *game_filename (int gamenum) /***********************************************************************/ +// err_exit: Print an error and exit + void err_exit (const char *format, ...) { va_list args; @@ -188,6 +217,8 @@ void err_exit (const char *format, ...) /***********************************************************************/ +// errno_exit: Print an error message (using errno) and exit + void errno_exit (const char *format, ...) { va_list args; @@ -210,6 +241,8 @@ void errno_exit (const char *format, ...) /***********************************************************************/ +// err_exit_nomem: Print an "out of memory" error and exit + void err_exit_nomem (void) { err_exit("out of memory"); @@ -224,6 +257,8 @@ void err_exit_nomem (void) /***********************************************************************/ +// init_rand: Initialise the random number generator + void init_rand (void) { /* Ideally, initialisation of the random number generator should be @@ -238,6 +273,8 @@ void init_rand (void) /***********************************************************************/ +// randf: Return a random number between 0.0 and 1.0 + extern double randf (void) { return drand48(); @@ -245,13 +282,119 @@ extern double randf (void) /***********************************************************************/ +// randi: Return a random number between 0 and limit + extern int randi (int limit) { return drand48() * (double) limit; } +/************************************************************************ +* Locale-aware function definitions * +************************************************************************/ + +// These functions are documented in the file "utils.h" + + /***********************************************************************/ +// init_locale: Initialise locale-specific variables + +void init_locale (void) +{ + struct lconv *lc; + + + current_mon_locale = setlocale(LC_MONETARY, NULL); + lc = localeconv(); + + assert(current_mon_locale != NULL); + assert(lc != NULL); + + localeconv_info = *lc; + + /* Are we in the POSIX locale? This test may not be portable as the + string returned by setlocale() is supposed to be opaque. */ + add_currency_symbol = false; + if (strcmp(current_mon_locale, "POSIX") == 0 + || strcmp(current_mon_locale, "C") == 0) { + + add_currency_symbol = true; + localeconv_info.currency_symbol = MOD_POSIX_CURRENCY_SYMBOL; + localeconv_info.frac_digits = MOD_POSIX_FRAC_DIGITS; + localeconv_info.p_cs_precedes = MOD_POSIX_P_CS_PRECEDES; + localeconv_info.p_sep_by_space = MOD_POSIX_P_SEP_BY_SPACE; + } +} + + +/***********************************************************************/ +// l_strfmon: Convert monetary value to a string + +ssize_t l_strfmon (char *restrict s, size_t maxsize, + const char *restrict format, double val) +{ + /* The current implementation assumes MOD_POSIX_P_CS_PRECEDES is 1 + (currency symbol precedes value) and that MOD_POSIX_P_SEP_BY_SPACE + is 0 (no space separates currency symbol and value). It does, + however, handle currency symbols of length > 1 */ + assert(MOD_POSIX_P_CS_PRECEDES == 1); + assert(MOD_POSIX_P_SEP_BY_SPACE == 0); + + ssize_t ret = strfmon(s, maxsize, format, val); + + if (ret > 0 && add_currency_symbol) { + if (strstr(format, "!") == NULL) { + /* Insert localeconv_info.currency_symbol to s. + + NB: add_currecy_symbol == true assumes POSIX locale: + single-byte strings are in effect, so strlen(), etc, work + correctly. */ + const char *sym = localeconv_info.currency_symbol; + int symlen = strlen(sym); + char *p; + int spc; + + // Count number of leading spaces + for (p = s, spc = 0; *p == ' '; p++, spc++) + ; + + if (symlen <= spc) { + /* Enough space for currency symbol: copy it WITHOUT + copying terminating NUL character */ + for (p -= symlen; *sym != '\0'; p++, sym++) { + *p = *sym; + } + } else { + // Make space for currency symbol, then copy it + + memmove(s + symlen - spc, s, maxsize - (symlen - spc) + 1); + s[maxsize - 1] = '\0'; + + for ( ; *sym != '\0'; sym++, s++) { + // Make sure terminating NUL character is NOT copied! + *s = *sym; + } + + ret = MIN(ret + symlen - spc, maxsize - 1); + } + } + } + + return ret; +} + + +/************************************************************************ +* Encryption function definitions * +************************************************************************/ + +// These functions are documented in the file "utils.h" + + +/***********************************************************************/ +// scramble: Scramble (encrypt) the buffer + char *scramble (int key, char *buf, int bufsize) { /* The algorithm used here is reversable: scramble(scramble(...)) @@ -281,6 +424,8 @@ char *scramble (int key, char *buf, int bufsize) /***********************************************************************/ +// unscramble: Unscramble (decrypt) the buffer + char *unscramble (int key, char *buf, int bufsize) { return scramble(key, buf, bufsize); diff --git a/src/utils.h b/src/utils.h index 91f9f03..1c0eb9e 100644 --- a/src/utils.h +++ b/src/utils.h @@ -42,6 +42,14 @@ #define MAX(a, b) ((a) > (b) ? (a) : (b)) +/************************************************************************ +* Global variable declarations * +************************************************************************/ + +// Global copy, suitably modified, of localeconv() information +extern struct lconv localeconv_info; + + /************************************************************************ * Initialisation and environment function prototypes * ************************************************************************/ @@ -209,6 +217,42 @@ extern double randf (void); extern int randi (int limit); +/************************************************************************ +* Locale-aware function prototypes * +************************************************************************/ + +/* + Function: init_locale - Initialise locale-specific variables + Parameters: (none) + Returns: (nothing) + + This function initialises the global variable localeconv_info with + values suitable for this program. In particular, if the POSIX or C + locale is in effect, the currency_symbol and frac_digits members are + updated to be something reasonable. This function must be called + before using localeconf_info. +*/ +extern void init_locale (void); + + +/* + Function: l_strfmon - Convert monetary value to a string + Parameters: s - Buffer to receive result + maxsize - Maximum size of buffer + format - strfmon() format to use + val - Monetary value to convert + Returns: ssize_t - Size of returned string + + This function calls strfmon() to convert val to a suitable monetary + value string. If the POSIX or C locale is in effect, and "!" does NOT + appear in the format, "$" is inserted into the resulting string. This + function overcomes the limitation that the POSIX locale does not define + anything for localeconv()->currency_symbol. +*/ +extern ssize_t l_strfmon (char *restrict s, size_t maxsize, + const char *restrict format, double val); + + /************************************************************************ * Encryption function prototypes * ************************************************************************/