Skip to content

Commit a9bbc9e

Browse files
Update cstring.c
1 parent 3805104 commit a9bbc9e

File tree

1 file changed

+39
-21
lines changed

1 file changed

+39
-21
lines changed

code/logic/cstring.c

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
*/
1414
#include "fossil/io/cstring.h"
1515
#include "fossil/io/output.h"
16-
#include <string.h> // For strlen, strnlen, strncasecmp
17-
#include <strings.h> // For strncasecmp on POSIX
16+
#include <strings.h>
1817
#include <stdlib.h>
19-
#include <ctype.h> // For toupper, tolower
18+
#include <string.h>
19+
#include <locale.h>
20+
#include <ctype.h>
2021
#include <time.h>
2122
#include <math.h>
2223

@@ -58,42 +59,51 @@ void fossil_io_cstring_free(cstring str) {
5859
}
5960
}
6061

61-
// ----------------------
62-
// Money String Conversions
63-
// ----------------------
62+
// ---------------------------------------
63+
// Locale-Aware Money String Conversions
64+
// ---------------------------------------
6465

65-
int fossil_io_cstring_money_to_string(double amount, cstring output, size_t size) {
66+
int fossil_io_cstring_money_to_string(double amount, char *output, size_t size) {
6667
if (!output || size == 0) return -1;
6768

68-
// Round to 2 decimals to avoid floating-point quirks like 1.199999
69-
amount = round(amount * 100.0) / 100.0;
69+
// Set locale temporarily to the user's default locale
70+
char *old_locale = setlocale(LC_NUMERIC, NULL);
71+
setlocale(LC_NUMERIC, "");
72+
73+
amount = round(amount * 100.0) / 100.0; // Round to 2 decimals
7074

71-
// Temporary buffer for raw numeric formatting (no commas)
7275
char temp[64];
7376
int written = snprintf(temp, sizeof(temp), "%.2f", fabs(amount));
7477
if (written < 0 || written >= (int)sizeof(temp)) return -1;
7578

76-
// Insert commas into the integer part
79+
// Determine locale decimal and thousand separators
80+
struct lconv *lc = localeconv();
81+
char decimal_sep = lc && lc->decimal_point ? lc->decimal_point[0] : '.';
82+
char thousand_sep = lc && lc->thousands_sep ? lc->thousands_sep[0] : ',';
83+
84+
// Replace decimal point with locale decimal separator
7785
char *dot = strchr(temp, '.');
86+
if (dot) *dot = decimal_sep;
87+
7888
int int_len = dot ? (int)(dot - temp) : (int)strlen(temp);
7989
int commas = (int_len - 1) / 3;
8090
int total_len = int_len + commas + (dot ? strlen(dot) : 0);
8191

82-
if ((size_t)(total_len + 3) > size) return -1; // +3 for sign, '$', and '\0'
92+
if ((size_t)(total_len + 3) > size) return -1;
8393

8494
char formatted[128];
8595
int fpos = 0;
8696

8797
if (amount < 0) formatted[fpos++] = '-';
88-
formatted[fpos++] = '$';
98+
formatted[fpos++] = '$'; // Keep USD-style symbol
8999

90100
int leading = int_len % 3;
91101
if (leading == 0) leading = 3;
92102

93103
for (int i = 0; i < int_len; i++) {
94104
formatted[fpos++] = temp[i];
95105
if ((i + 1) % leading == 0 && (i + 1) < int_len) {
96-
formatted[fpos++] = ',';
106+
formatted[fpos++] = thousand_sep;
97107
leading = 3;
98108
}
99109
}
@@ -107,10 +117,13 @@ int fossil_io_cstring_money_to_string(double amount, cstring output, size_t size
107117
strncpy(output, formatted, size - 1);
108118
output[size - 1] = '\0';
109119

120+
// Restore previous locale
121+
setlocale(LC_NUMERIC, old_locale);
122+
110123
return 0;
111124
}
112125

113-
int fossil_io_cstring_string_to_money(ccstring input, double *amount) {
126+
int fossil_io_cstring_string_to_money(const char *input, double *amount) {
114127
if (!input || !amount) return -1;
115128

116129
char buffer[128];
@@ -120,27 +133,32 @@ int fossil_io_cstring_string_to_money(ccstring input, double *amount) {
120133
// Skip leading spaces
121134
while (isspace((unsigned char)*input)) input++;
122135

123-
// Detect negative in parentheses "(123.45)"
124136
if (*input == '(') {
125137
negative = 1;
126138
input++;
127139
}
128140

129-
// Extract only digits, dot, and minus sign
141+
struct lconv *lc = localeconv();
142+
char decimal_sep = lc && lc->decimal_point ? lc->decimal_point[0] : '.';
143+
144+
// Copy digits and decimal separator only
130145
for (size_t i = 0; input[i] && j < sizeof(buffer) - 1; i++) {
131-
if (isdigit((unsigned char)input[i]) || input[i] == '.') {
146+
if (isdigit((unsigned char)input[i]) || input[i] == decimal_sep) {
132147
buffer[j++] = input[i];
133148
}
134149
}
135150
buffer[j] = '\0';
136151

137152
if (j == 0) return -1;
138153

139-
*amount = atof(buffer);
140-
if (negative || strstr(input, "-")) {
141-
*amount = -*amount;
154+
// Replace locale decimal with '.' for atof
155+
for (size_t i = 0; i < j; i++) {
156+
if (buffer[i] == decimal_sep) buffer[i] = '.';
142157
}
143158

159+
*amount = atof(buffer);
160+
if (negative || strchr(input, '-')) *amount = -*amount;
161+
144162
return 0;
145163
}
146164

0 commit comments

Comments
 (0)