|
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];
|
@@ -130,98 +131,87 @@ static void IsPagerNeeded(const printTableContent *cont, const int extra_lines,
|
130 | 131 | staticvoidprint_aligned_vertical(constprintTableContent*cont,FILE*fout);
|
131 | 132 |
|
132 | 133 |
|
| 134 | +/* Count number of digits in integral part of number */ |
133 | 135 | staticint
|
134 | 136 | integer_digits(constchar*my_str)
|
135 | 137 | {
|
136 |
| -intfrac_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 |
| -returnstrlen(my_str)-frac_len; |
| 141 | +/* ... count initial integral digits */ |
| 142 | +returnstrspn(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 | staticint
|
148 | 147 | additional_numeric_locale_len(constchar*my_str)
|
149 | 148 | {
|
150 | 149 | intint_len=integer_digits(my_str),
|
151 | 150 | len=0;
|
152 |
| -intgroupdigits=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 | returnlen;
|
163 | 161 | }
|
164 | 162 |
|
165 |
| -staticint |
166 |
| -strlen_with_numeric_locale(constchar*my_str) |
167 |
| -{ |
168 |
| -returnstrlen(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 | staticchar*
|
176 | 168 | format_numeric_locale(constchar*my_str)
|
177 | 169 | {
|
| 170 | +intnew_len=strlen(my_str)+additional_numeric_locale_len(my_str); |
| 171 | +char*new_str=pg_malloc(new_len+1); |
| 172 | +intint_len=integer_digits(my_str); |
178 | 173 | inti,
|
179 |
| -j, |
180 |
| -int_len=integer_digits(my_str), |
181 | 174 | leading_digits;
|
182 |
| -intgroupdigits=atoi(grouping); |
183 |
| -intnew_str_start=0; |
184 |
| -char*new_str=pg_malloc(strlen_with_numeric_locale(my_str)+1); |
| 175 | +intnew_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 oversign, affects grouping |
190 |
| - * calculations */ |
| 182 | +/*processsign */ |
| 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 | returnnew_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)
|
|