|
40 | 40 | */
|
41 | 41 | volatileboolcancel_pressed= false;
|
42 | 42 |
|
| 43 | +/* info for locale-aware numeric formatting; set up by setDecimalLocale() */ |
43 | 44 | staticchar*decimal_point;
|
44 |
| -staticchar*grouping; |
| 45 | +staticintgroupdigits; |
45 | 46 | staticchar*thousands_sep;
|
46 | 47 |
|
47 | 48 | /* Line style control structures */
|
@@ -154,100 +155,85 @@ pg_local_calloc(int count, size_t size)
|
154 | 155 | returntmp;
|
155 | 156 | }
|
156 | 157 |
|
| 158 | +/* Count number of digits in integral part of number */ |
157 | 159 | staticint
|
158 | 160 | integer_digits(constchar*my_str)
|
159 | 161 | {
|
160 |
| -intfrac_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 |
| -returnstrlen(my_str)-frac_len; |
| 165 | +/* ... count initial integral digits */ |
| 166 | +returnstrspn(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 | staticint
|
172 | 171 | additional_numeric_locale_len(constchar*my_str)
|
173 | 172 | {
|
174 | 173 | intint_len=integer_digits(my_str),
|
175 | 174 | len=0;
|
176 |
| -intgroupdigits=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 | returnlen;
|
187 | 185 | }
|
188 | 186 |
|
189 |
| -staticint |
190 |
| -strlen_with_numeric_locale(constchar*my_str) |
191 |
| -{ |
192 |
| -returnstrlen(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 | staticchar*
|
200 | 192 | format_numeric_locale(constchar*my_str)
|
201 | 193 | {
|
| 194 | +intnew_len=strlen(my_str)+additional_numeric_locale_len(my_str); |
| 195 | +char*new_str=pg_local_malloc(new_len+1); |
| 196 | +intint_len=integer_digits(my_str); |
202 | 197 | inti,
|
203 |
| -j, |
204 |
| -int_len=integer_digits(my_str), |
205 | 198 | leading_digits;
|
206 |
| -intgroupdigits=atoi(grouping); |
207 |
| -intnew_str_start=0; |
208 |
| -char*new_str=new_str=pg_local_malloc( |
209 |
| -strlen_with_numeric_locale(my_str)+1); |
| 199 | +intnew_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 oversign, affects grouping |
215 |
| - * calculations */ |
| 206 | +/*processsign */ |
| 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 | returnnew_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)
|
|