|
40 | 40 | */ |
41 | 41 | volatile bool cancel_pressed = false; |
42 | 42 |
|
| 43 | +/* info for locale-aware numeric formatting; set up by setDecimalLocale() */ |
43 | 44 | static char *decimal_point; |
44 | | -static char *grouping; |
| 45 | +static int groupdigits; |
45 | 46 | static char *thousands_sep; |
46 | 47 |
|
47 | 48 | /* Line style control structures */ |
@@ -154,100 +155,85 @@ pg_local_calloc(int count, size_t size) |
154 | 155 | return tmp; |
155 | 156 | } |
156 | 157 |
|
| 158 | +/* Count number of digits in integral part of number */ |
157 | 159 | static int |
158 | 160 | integer_digits(const char *my_str) |
159 | 161 | { |
160 | | - int frac_len; |
161 | | - |
162 | | - if (my_str[0] == '-') |
| 162 | + /* ignoring any sign ... */ |
| 163 | + if (my_str[0] == '-' || my_str[0] == '+') |
163 | 164 | my_str++; |
164 | | - |
165 | | - frac_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0; |
166 | | - |
167 | | - return strlen(my_str) - frac_len; |
| 165 | + /* ... count initial integral digits */ |
| 166 | + return strspn(my_str, "0123456789"); |
168 | 167 | } |
169 | 168 |
|
170 | | -/* Return additional length required for locale-aware numeric output */ |
| 169 | +/* Compute additional length required for locale-aware numeric output */ |
171 | 170 | static int |
172 | 171 | additional_numeric_locale_len(const char *my_str) |
173 | 172 | { |
174 | 173 | int int_len = integer_digits(my_str), |
175 | 174 | len = 0; |
176 | | - int groupdigits = atoi(grouping); |
177 | 175 |
|
178 | | - if (int_len > 0) |
179 | | - /* Don't count a leading separator */ |
180 | | - len = (int_len / groupdigits - (int_len % groupdigits == 0)) * |
181 | | - strlen(thousands_sep); |
| 176 | + /* Account for added thousands_sep instances */ |
| 177 | + if (int_len > groupdigits) |
| 178 | + len += ((int_len - 1) / groupdigits) * strlen(thousands_sep); |
182 | 179 |
|
| 180 | + /* Account for possible additional length of decimal_point */ |
183 | 181 | if (strchr(my_str, '.') != NULL) |
184 | | - len += strlen(decimal_point) - strlen("."); |
| 182 | + len += strlen(decimal_point) - 1; |
185 | 183 |
|
186 | 184 | return len; |
187 | 185 | } |
188 | 186 |
|
189 | | -static int |
190 | | -strlen_with_numeric_locale(const char *my_str) |
191 | | -{ |
192 | | - return strlen(my_str) + additional_numeric_locale_len(my_str); |
193 | | -} |
194 | | - |
195 | 187 | /* |
196 | 188 | * Returns the appropriately formatted string in a new allocated block, |
197 | 189 | * caller must free |
198 | 190 | */ |
199 | 191 | static char * |
200 | 192 | format_numeric_locale(const char *my_str) |
201 | 193 | { |
| 194 | + int new_len = strlen(my_str) + additional_numeric_locale_len(my_str); |
| 195 | + char *new_str = pg_local_malloc(new_len + 1); |
| 196 | + int int_len = integer_digits(my_str); |
202 | 197 | int i, |
203 | | - j, |
204 | | - int_len = integer_digits(my_str), |
205 | 198 | leading_digits; |
206 | | - int groupdigits = atoi(grouping); |
207 | | - int new_str_start = 0; |
208 | | - char *new_str = new_str = pg_local_malloc( |
209 | | - strlen_with_numeric_locale(my_str) + 1); |
| 199 | + int new_str_pos = 0; |
210 | 200 |
|
211 | | - leading_digits = (int_len % groupdigits != 0) ? |
212 | | - int_len % groupdigits : groupdigits; |
| 201 | + /* number of digits in first thousands group */ |
| 202 | + leading_digits = int_len % groupdigits; |
| 203 | + if (leading_digits == 0) |
| 204 | + leading_digits = groupdigits; |
213 | 205 |
|
214 | | - if (my_str[0] == '-') /* skip over sign, affects grouping |
215 | | - * calculations */ |
| 206 | + /* process sign */ |
| 207 | + if (my_str[0] == '-' || my_str[0] == '+') |
216 | 208 | { |
217 | | - new_str[0] = my_str[0]; |
| 209 | + new_str[new_str_pos++] = my_str[0]; |
218 | 210 | my_str++; |
219 | | - new_str_start = 1; |
220 | 211 | } |
221 | 212 |
|
222 | | - for (i = 0, j = new_str_start;; i++, j++) |
| 213 | + /* process integer part of number */ |
| 214 | + for (i = 0; i < int_len; i++) |
223 | 215 | { |
224 | | - /* Hit decimal point? */ |
225 | | - if (my_str[i] == '.') |
| 216 | + /* Time to insert separator? */ |
| 217 | + if (i > 0 && --leading_digits == 0) |
226 | 218 | { |
227 | | - strcpy(&new_str[j], decimal_point); |
228 | | - j += strlen(decimal_point); |
229 | | - /* add fractional part */ |
230 | | - strcpy(&new_str[j], &my_str[i] + 1); |
231 | | - break; |
232 | | - } |
233 | | - |
234 | | - /* End of string? */ |
235 | | - if (my_str[i] == '\0') |
236 | | - { |
237 | | - new_str[j] = '\0'; |
238 | | - break; |
239 | | - } |
240 | | - |
241 | | - /* Add separator? */ |
242 | | - if (i != 0 && (i - leading_digits) % groupdigits == 0) |
243 | | - { |
244 | | - strcpy(&new_str[j], thousands_sep); |
245 | | - j += strlen(thousands_sep); |
| 219 | + strcpy(&new_str[new_str_pos], thousands_sep); |
| 220 | + new_str_pos += strlen(thousands_sep); |
| 221 | + leading_digits = groupdigits; |
246 | 222 | } |
| 223 | + new_str[new_str_pos++] = my_str[i]; |
| 224 | + } |
247 | 225 |
|
248 | | - new_str[j] = my_str[i]; |
| 226 | + /* handle decimal point if any */ |
| 227 | + if (my_str[i] == '.') |
| 228 | + { |
| 229 | + strcpy(&new_str[new_str_pos], decimal_point); |
| 230 | + new_str_pos += strlen(decimal_point); |
| 231 | + i++; |
249 | 232 | } |
250 | 233 |
|
| 234 | + /* copy the rest (fractional digits and/or exponent, and \0 terminator) */ |
| 235 | + strcpy(&new_str[new_str_pos], &my_str[i]); |
| 236 | + |
251 | 237 | return new_str; |
252 | 238 | } |
253 | 239 |
|
@@ -2473,10 +2459,11 @@ setDecimalLocale(void) |
2473 | 2459 | decimal_point = pg_strdup(extlconv->decimal_point); |
2474 | 2460 | else |
2475 | 2461 | decimal_point = "."; /* SQL output standard */ |
| 2462 | + |
2476 | 2463 | if (*extlconv->grouping && atoi(extlconv->grouping) > 0) |
2477 | | - grouping = pg_strdup(extlconv->grouping); |
| 2464 | + groupdigits = atoi(extlconv->grouping); |
2478 | 2465 | else |
2479 | | - grouping = "3"; /* most common */ |
| 2466 | + groupdigits = 3; /* most common */ |
2480 | 2467 |
|
2481 | 2468 | /* similar code exists in formatting.c */ |
2482 | 2469 | if (*extlconv->thousands_sep) |
|
0 commit comments