|
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];
|
@@ -130,98 +131,87 @@ static void IsPagerNeeded(const printTableContent *cont, const int extra_lines,
|
130 | 131 | static void print_aligned_vertical(const printTableContent *cont, FILE *fout);
|
131 | 132 |
|
132 | 133 |
|
| 134 | +/* Count number of digits in integral part of number */ |
133 | 135 | static int
|
134 | 136 | integer_digits(const char *my_str)
|
135 | 137 | {
|
136 |
| - int frac_len; |
137 |
| - |
138 |
| - if (my_str[0] == '-') |
| 138 | + /* ignoring any sign ... */ |
| 139 | + if (my_str[0] == '-' || my_str[0] == '+') |
139 | 140 | my_str++;
|
140 |
| - |
141 |
| - frac_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0; |
142 |
| - |
143 |
| - return strlen(my_str) - frac_len; |
| 141 | + /* ... count initial integral digits */ |
| 142 | + return strspn(my_str, "0123456789"); |
144 | 143 | }
|
145 | 144 |
|
146 |
| -/* Return additional length required for locale-aware numeric output */ |
| 145 | +/* Compute additional length required for locale-aware numeric output */ |
147 | 146 | static int
|
148 | 147 | additional_numeric_locale_len(const char *my_str)
|
149 | 148 | {
|
150 | 149 | int int_len = integer_digits(my_str),
|
151 | 150 | len = 0;
|
152 |
| - int groupdigits = atoi(grouping); |
153 | 151 |
|
154 |
| - if (int_len > 0) |
155 |
| - /* Don't count a leading separator */ |
156 |
| - len = (int_len / groupdigits - (int_len % groupdigits == 0)) * |
157 |
| - strlen(thousands_sep); |
| 152 | + /* Account for added thousands_sep instances */ |
| 153 | + if (int_len > groupdigits) |
| 154 | + len += ((int_len - 1) / groupdigits) * strlen(thousands_sep); |
158 | 155 |
|
| 156 | + /* Account for possible additional length of decimal_point */ |
159 | 157 | if (strchr(my_str, '.') != NULL)
|
160 |
| - len += strlen(decimal_point) - strlen("."); |
| 158 | + len += strlen(decimal_point) - 1; |
161 | 159 |
|
162 | 160 | return len;
|
163 | 161 | }
|
164 | 162 |
|
165 |
| -static int |
166 |
| -strlen_with_numeric_locale(const char *my_str) |
167 |
| -{ |
168 |
| - return strlen(my_str) + additional_numeric_locale_len(my_str); |
169 |
| -} |
170 |
| - |
171 | 163 | /*
|
172 | 164 | * Returns the appropriately formatted string in a new allocated block,
|
173 | 165 | * caller must free
|
174 | 166 | */
|
175 | 167 | static char *
|
176 | 168 | format_numeric_locale(const char *my_str)
|
177 | 169 | {
|
| 170 | + int new_len = strlen(my_str) + additional_numeric_locale_len(my_str); |
| 171 | + char *new_str = pg_malloc(new_len + 1); |
| 172 | + int int_len = integer_digits(my_str); |
178 | 173 | int i,
|
179 |
| - j, |
180 |
| - int_len = integer_digits(my_str), |
181 | 174 | leading_digits;
|
182 |
| - int groupdigits = atoi(grouping); |
183 |
| - int new_str_start = 0; |
184 |
| - char *new_str = pg_malloc(strlen_with_numeric_locale(my_str) + 1); |
| 175 | + int new_str_pos = 0; |
185 | 176 |
|
186 |
| - leading_digits = (int_len % groupdigits != 0) ? |
187 |
| - int_len % groupdigits : groupdigits; |
| 177 | + /* number of digits in first thousands group */ |
| 178 | + leading_digits = int_len % groupdigits; |
| 179 | + if (leading_digits == 0) |
| 180 | + leading_digits = groupdigits; |
188 | 181 |
|
189 |
| - if (my_str[0] == '-') /* skip over sign, affects grouping |
190 |
| - * calculations */ |
| 182 | + /* process sign */ |
| 183 | + if (my_str[0] == '-' || my_str[0] == '+') |
191 | 184 | {
|
192 |
| - new_str[0] = my_str[0]; |
| 185 | + new_str[new_str_pos++] = my_str[0]; |
193 | 186 | my_str++;
|
194 |
| - new_str_start = 1; |
195 | 187 | }
|
196 | 188 |
|
197 |
| - for (i = 0, j = new_str_start;; i++, j++) |
| 189 | + /* process integer part of number */ |
| 190 | + for (i = 0; i < int_len; i++) |
198 | 191 | {
|
199 |
| - /* Hit decimal point? */ |
200 |
| - if (my_str[i] == '.') |
| 192 | + /* Time to insert separator? */ |
| 193 | + if (i > 0 && --leading_digits == 0) |
201 | 194 | {
|
202 |
| - strcpy(&new_str[j], decimal_point); |
203 |
| - j += strlen(decimal_point); |
204 |
| - /* add fractional part */ |
205 |
| - strcpy(&new_str[j], &my_str[i] + 1); |
206 |
| - break; |
| 195 | + strcpy(&new_str[new_str_pos], thousands_sep); |
| 196 | + new_str_pos += strlen(thousands_sep); |
| 197 | + leading_digits = groupdigits; |
207 | 198 | }
|
| 199 | + new_str[new_str_pos++] = my_str[i]; |
| 200 | + } |
208 | 201 |
|
209 |
| - /* End of string? */ |
210 |
| - if (my_str[i] == '\0') |
211 |
| - { |
212 |
| - new_str[j] = '\0'; |
213 |
| - break; |
214 |
| - } |
| 202 | + /* handle decimal point if any */ |
| 203 | + if (my_str[i] == '.') |
| 204 | + { |
| 205 | + strcpy(&new_str[new_str_pos], decimal_point); |
| 206 | + new_str_pos += strlen(decimal_point); |
| 207 | + i++; |
| 208 | + } |
215 | 209 |
|
216 |
| - /* Add separator? */ |
217 |
| - if (i != 0 && (i - leading_digits) % groupdigits == 0) |
218 |
| - { |
219 |
| - strcpy(&new_str[j], thousands_sep); |
220 |
| - j += strlen(thousands_sep); |
221 |
| - } |
| 210 | + /* copy the rest (fractional digits and/or exponent, and \0 terminator) */ |
| 211 | + strcpy(&new_str[new_str_pos], &my_str[i]); |
222 | 212 |
|
223 |
| - new_str[j] = my_str[i]; |
224 |
| - } |
| 213 | + /* assert we didn't underestimate new_len (an overestimate is OK) */ |
| 214 | + Assert(strlen(new_str) <= new_len); |
225 | 215 |
|
226 | 216 | return new_str;
|
227 | 217 | }
|
@@ -2678,10 +2668,11 @@ setDecimalLocale(void)
|
2678 | 2668 | decimal_point = pg_strdup(extlconv->decimal_point);
|
2679 | 2669 | else
|
2680 | 2670 | decimal_point = "."; /* SQL output standard */
|
| 2671 | + |
2681 | 2672 | if (*extlconv->grouping && atoi(extlconv->grouping) > 0)
|
2682 |
| - grouping = pg_strdup(extlconv->grouping); |
| 2673 | + groupdigits = atoi(extlconv->grouping); |
2683 | 2674 | else
|
2684 |
| - grouping = "3"; /* most common */ |
| 2675 | + groupdigits = 3; /* most common */ |
2685 | 2676 |
|
2686 | 2677 | /* similar code exists in formatting.c */
|
2687 | 2678 | if (*extlconv->thousands_sep)
|
|
0 commit comments