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