|
39 | 39 | */ |
40 | 40 | volatileboolcancel_pressed= false; |
41 | 41 |
|
| 42 | +/* info for locale-aware numeric formatting; set up by setDecimalLocale() */ |
42 | 43 | staticchar*decimal_point; |
43 | | -staticchar*grouping; |
| 44 | +staticintgroupdigits; |
44 | 45 | staticchar*thousands_sep; |
45 | 46 |
|
46 | 47 | staticchardefault_footer[100]; |
@@ -196,98 +197,87 @@ static void IsPagerNeeded(const printTableContent *cont, const int extra_lines, |
196 | 197 | staticvoidprint_aligned_vertical(constprintTableContent*cont,FILE*fout); |
197 | 198 |
|
198 | 199 |
|
| 200 | +/* Count number of digits in integral part of number */ |
199 | 201 | staticint |
200 | 202 | integer_digits(constchar*my_str) |
201 | 203 | { |
202 | | -intfrac_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 | | -returnstrlen(my_str)-frac_len; |
| 207 | +/* ... count initial integral digits */ |
| 208 | +returnstrspn(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 | staticint |
214 | 213 | additional_numeric_locale_len(constchar*my_str) |
215 | 214 | { |
216 | 215 | intint_len=integer_digits(my_str), |
217 | 216 | len=0; |
218 | | -intgroupdigits=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 | returnlen; |
229 | 227 | } |
230 | 228 |
|
231 | | -staticint |
232 | | -strlen_with_numeric_locale(constchar*my_str) |
233 | | -{ |
234 | | -returnstrlen(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 | staticchar* |
242 | 234 | format_numeric_locale(constchar*my_str) |
243 | 235 | { |
| 236 | +intnew_len=strlen(my_str)+additional_numeric_locale_len(my_str); |
| 237 | +char*new_str=pg_malloc(new_len+1); |
| 238 | +intint_len=integer_digits(my_str); |
244 | 239 | inti, |
245 | | -j, |
246 | | -int_len=integer_digits(my_str), |
247 | 240 | leading_digits; |
248 | | -intgroupdigits=atoi(grouping); |
249 | | -intnew_str_start=0; |
250 | | -char*new_str=pg_malloc(strlen_with_numeric_locale(my_str)+1); |
| 241 | +intnew_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 oversign, affects grouping |
256 | | - * calculations */ |
| 248 | +/*processsign */ |
| 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 | returnnew_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) |
|