|
39 | 39 | */
|
40 | 40 | volatile bool cancel_pressed = false;
|
41 | 41 |
|
| 42 | +/* info for locale-aware numeric formatting; set up by setDecimalLocale() */ |
42 | 43 | static char *decimal_point;
|
43 |
| -static char *grouping; |
| 44 | +static int groupdigits; |
44 | 45 | static char *thousands_sep;
|
45 | 46 |
|
46 | 47 | static char default_footer[100];
|
@@ -196,98 +197,87 @@ static void IsPagerNeeded(const printTableContent *cont, const int extra_lines,
|
196 | 197 | static void print_aligned_vertical(const printTableContent *cont, FILE *fout);
|
197 | 198 |
|
198 | 199 |
|
| 200 | +/* Count number of digits in integral part of number */ |
199 | 201 | static int
|
200 | 202 | integer_digits(const char *my_str)
|
201 | 203 | {
|
202 |
| - int frac_len; |
203 |
| - |
204 |
| - if (my_str[0] == '-') |
| 204 | + /* ignoring any sign ... */ |
| 205 | + if (my_str[0] == '-' || my_str[0] == '+') |
205 | 206 | my_str++;
|
206 |
| - |
207 |
| - frac_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0; |
208 |
| - |
209 |
| - return strlen(my_str) - frac_len; |
| 207 | + /* ... count initial integral digits */ |
| 208 | + return strspn(my_str, "0123456789"); |
210 | 209 | }
|
211 | 210 |
|
212 |
| -/* Return additional length required for locale-aware numeric output */ |
| 211 | +/* Compute additional length required for locale-aware numeric output */ |
213 | 212 | static int
|
214 | 213 | additional_numeric_locale_len(const char *my_str)
|
215 | 214 | {
|
216 | 215 | int int_len = integer_digits(my_str),
|
217 | 216 | len = 0;
|
218 |
| - int groupdigits = atoi(grouping); |
219 | 217 |
|
220 |
| - if (int_len > 0) |
221 |
| - /* Don't count a leading separator */ |
222 |
| - len = (int_len / groupdigits - (int_len % groupdigits == 0)) * |
223 |
| - strlen(thousands_sep); |
| 218 | + /* Account for added thousands_sep instances */ |
| 219 | + if (int_len > groupdigits) |
| 220 | + len += ((int_len - 1) / groupdigits) * strlen(thousands_sep); |
224 | 221 |
|
| 222 | + /* Account for possible additional length of decimal_point */ |
225 | 223 | if (strchr(my_str, '.') != NULL)
|
226 |
| - len += strlen(decimal_point) - strlen("."); |
| 224 | + len += strlen(decimal_point) - 1; |
227 | 225 |
|
228 | 226 | return len;
|
229 | 227 | }
|
230 | 228 |
|
231 |
| -static int |
232 |
| -strlen_with_numeric_locale(const char *my_str) |
233 |
| -{ |
234 |
| - return strlen(my_str) + additional_numeric_locale_len(my_str); |
235 |
| -} |
236 |
| - |
237 | 229 | /*
|
238 | 230 | * Returns the appropriately formatted string in a new allocated block,
|
239 | 231 | * caller must free
|
240 | 232 | */
|
241 | 233 | static char *
|
242 | 234 | format_numeric_locale(const char *my_str)
|
243 | 235 | {
|
| 236 | + int new_len = strlen(my_str) + additional_numeric_locale_len(my_str); |
| 237 | + char *new_str = pg_malloc(new_len + 1); |
| 238 | + int int_len = integer_digits(my_str); |
244 | 239 | int i,
|
245 |
| - j, |
246 |
| - int_len = integer_digits(my_str), |
247 | 240 | leading_digits;
|
248 |
| - int groupdigits = atoi(grouping); |
249 |
| - int new_str_start = 0; |
250 |
| - char *new_str = pg_malloc(strlen_with_numeric_locale(my_str) + 1); |
| 241 | + int new_str_pos = 0; |
251 | 242 |
|
252 |
| - leading_digits = (int_len % groupdigits != 0) ? |
253 |
| - int_len % groupdigits : groupdigits; |
| 243 | + /* number of digits in first thousands group */ |
| 244 | + leading_digits = int_len % groupdigits; |
| 245 | + if (leading_digits == 0) |
| 246 | + leading_digits = groupdigits; |
254 | 247 |
|
255 |
| - if (my_str[0] == '-') /* skip over sign, affects grouping |
256 |
| - * calculations */ |
| 248 | + /* process sign */ |
| 249 | + if (my_str[0] == '-' || my_str[0] == '+') |
257 | 250 | {
|
258 |
| - new_str[0] = my_str[0]; |
| 251 | + new_str[new_str_pos++] = my_str[0]; |
259 | 252 | my_str++;
|
260 |
| - new_str_start = 1; |
261 | 253 | }
|
262 | 254 |
|
263 |
| - for (i = 0, j = new_str_start;; i++, j++) |
| 255 | + /* process integer part of number */ |
| 256 | + for (i = 0; i < int_len; i++) |
264 | 257 | {
|
265 |
| - /* Hit decimal point? */ |
266 |
| - if (my_str[i] == '.') |
| 258 | + /* Time to insert separator? */ |
| 259 | + if (i > 0 && --leading_digits == 0) |
267 | 260 | {
|
268 |
| - strcpy(&new_str[j], decimal_point); |
269 |
| - j += strlen(decimal_point); |
270 |
| - /* add fractional part */ |
271 |
| - strcpy(&new_str[j], &my_str[i] + 1); |
272 |
| - break; |
| 261 | + strcpy(&new_str[new_str_pos], thousands_sep); |
| 262 | + new_str_pos += strlen(thousands_sep); |
| 263 | + leading_digits = groupdigits; |
273 | 264 | }
|
| 265 | + new_str[new_str_pos++] = my_str[i]; |
| 266 | + } |
274 | 267 |
|
275 |
| - /* End of string? */ |
276 |
| - if (my_str[i] == '\0') |
277 |
| - { |
278 |
| - new_str[j] = '\0'; |
279 |
| - break; |
280 |
| - } |
| 268 | + /* handle decimal point if any */ |
| 269 | + if (my_str[i] == '.') |
| 270 | + { |
| 271 | + strcpy(&new_str[new_str_pos], decimal_point); |
| 272 | + new_str_pos += strlen(decimal_point); |
| 273 | + i++; |
| 274 | + } |
281 | 275 |
|
282 |
| - /* Add separator? */ |
283 |
| - if (i != 0 && (i - leading_digits) % groupdigits == 0) |
284 |
| - { |
285 |
| - strcpy(&new_str[j], thousands_sep); |
286 |
| - j += strlen(thousands_sep); |
287 |
| - } |
| 276 | + /* copy the rest (fractional digits and/or exponent, and \0 terminator) */ |
| 277 | + strcpy(&new_str[new_str_pos], &my_str[i]); |
288 | 278 |
|
289 |
| - new_str[j] = my_str[i]; |
290 |
| - } |
| 279 | + /* assert we didn't underestimate new_len (an overestimate is OK) */ |
| 280 | + Assert(strlen(new_str) <= new_len); |
291 | 281 |
|
292 | 282 | return new_str;
|
293 | 283 | }
|
@@ -3241,10 +3231,11 @@ setDecimalLocale(void)
|
3241 | 3231 | decimal_point = pg_strdup(extlconv->decimal_point);
|
3242 | 3232 | else
|
3243 | 3233 | decimal_point = "."; /* SQL output standard */
|
| 3234 | + |
3244 | 3235 | if (*extlconv->grouping && atoi(extlconv->grouping) > 0)
|
3245 |
| - grouping = pg_strdup(extlconv->grouping); |
| 3236 | + groupdigits = atoi(extlconv->grouping); |
3246 | 3237 | else
|
3247 |
| - grouping = "3"; /* most common */ |
| 3238 | + groupdigits = 3; /* most common */ |
3248 | 3239 |
|
3249 | 3240 | /* similar code exists in formatting.c */
|
3250 | 3241 | if (*extlconv->thousands_sep)
|
|
0 commit comments