Deleted Added
full compact
strfmon.c (104943) strfmon.c (104944)
1/*-
2 * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
1/*-
2 * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/lib/libc/stdlib/strfmon.c 104943 2002-10-11 23:04:59Z tjr $");
29__FBSDID("$FreeBSD: head/lib/libc/stdlib/strfmon.c 104944 2002-10-11 23:08:03Z tjr $");
30
31#include <sys/types.h>
32#include <ctype.h>
33#include <errno.h>
34#include <limits.h>
35#include <locale.h>
36#include <stdarg.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40
41/* internal flags */
42#define NEED_GROUPING 0x01 /* print digits grouped (default) */
43#define SIGN_POSN_USED 0x02 /* '+' or '(' usage flag */
44#define LOCALE_POSN 0x04 /* use locale defined +/- (default) */
45#define PARENTH_POSN 0x08 /* enclose negative amount in () */
46#define SUPRESS_CURR_SYMBOL 0x10 /* supress the currency from output */
47#define LEFT_JUSTIFY 0x20 /* left justify */
48#define USE_INTL_CURRENCY 0x40 /* use international currency symbol */
49#define IS_NEGATIVE 0x80 /* is argument value negative ? */
50
51/* internal macros */
52#define PRINT(CH) do { \
53 if (dst >= s + maxsize) \
54 goto e2big_error; \
55 *dst++ = CH; \
56} while (0)
57
58#define PRINTS(STR) do { \
59 char *tmps = STR; \
60 while (*tmps != '\0') \
61 PRINT(*tmps++); \
62} while (0)
63
64#define GET_NUMBER(VAR) do { \
65 VAR = 0; \
66 while (isdigit((unsigned char)*fmt)) { \
67 VAR *= 10; \
68 VAR += *fmt - '0'; \
69 fmt++; \
70 } \
71} while (0)
72
73#define GRPCPY(howmany) do { \
74 int i = howmany; \
75 while (i-- > 0) { \
76 avalue_size--; \
77 *--bufend = *(avalue+avalue_size+padded); \
78 } \
79} while (0)
80
81#define GRPSEP do { \
82 *--bufend = thousands_sep; \
83 groups++; \
84} while (0)
85
86static void __setup_vars(int, char *, char *, char *, char **);
87static int __calc_left_pad(int, char *);
88static char *__format_grouped_double(double, int *, int, int, int);
89
90ssize_t
91strfmon(char * __restrict s, size_t maxsize, const char * __restrict format,
92 ...)
93{
94 va_list ap;
95 char *dst; /* output destination pointer */
96 const char *fmt; /* current format poistion pointer */
97 struct lconv *lc; /* pointer to lconv structure */
98 char *asciivalue; /* formatted double pointer */
99
100 int flags; /* formatting options */
101 int pad_char; /* padding character */
102 int pad_size; /* pad size */
103 int width; /* field width */
104 int left_prec; /* left precision */
105 int right_prec; /* right precision */
106 double value; /* just value */
107 char space_char = ' '; /* space after currency */
108
109 char cs_precedes, /* values gathered from struct lconv */
110 sep_by_space,
111 sign_posn,
112 *signstr,
113 *currency_symbol;
114
115 char *tmpptr; /* temporary vars */
116 int *ntmp;
117
118 va_start(ap, format);
119
120 lc = localeconv();
121 dst = s;
122 fmt = format;
123 asciivalue = NULL;
124 currency_symbol = NULL;
125 pad_size = 0;
126
127 while (*fmt) {
128 /* pass nonformating characters AS IS */
129 if (*fmt != '%')
130 goto literal;
131
132 /* '%' found ! */
133
134 /* "%%" mean just '%' */
135 if (*(fmt+1) == '%') {
136 fmt++;
137 literal:
138 PRINT(*fmt++);
139 continue;
140 }
141
142 /* set up initial values */
143 flags = (NEED_GROUPING|LOCALE_POSN);
144 pad_char = ' '; /* padding character is "space" */
145 left_prec = -1; /* no left precision specified */
146 right_prec = -1; /* no right precision specified */
147 width = -1; /* no width specified */
148 value = 0; /* we have no value to print now */
149
150 /* Flags */
151 while (1) {
152 switch (*++fmt) {
153 case '=': /* fill character */
154 pad_char = *++fmt;
155 if (pad_char == '\0')
156 goto format_error;
157 continue;
158 case '^': /* not group currency */
159 flags &= ~(NEED_GROUPING);
160 continue;
161 case '+': /* use locale defined signs */
162 if (flags & SIGN_POSN_USED)
163 goto format_error;
164 flags |= (SIGN_POSN_USED|LOCALE_POSN);
165 continue;
166 case '(': /* enclose negatives with () */
167 if (flags & SIGN_POSN_USED)
168 goto format_error;
169 flags |= (SIGN_POSN_USED|PARENTH_POSN);
170 continue;
171 case '!': /* suppress currency symbol */
172 flags |= SUPRESS_CURR_SYMBOL;
173 continue;
174 case '-': /* alignment (left) */
175 flags |= LEFT_JUSTIFY;
176 continue;
177 default:
178 break;
179 }
180 break;
181 }
182
183 /* field Width */
184 if (isdigit((unsigned char)*fmt)) {
185 GET_NUMBER(width);
186 /* Do we have enough space to put number with
187 * required width ?
188 */
189 if (dst + width >= s + maxsize)
190 goto e2big_error;
191 }
192
193 /* Left precision */
194 if (*fmt == '#') {
195 if (!isdigit((unsigned char)*++fmt))
196 goto format_error;
197 GET_NUMBER(left_prec);
198 }
199
200 /* Right precision */
201 if (*fmt == '.') {
202 if (!isdigit((unsigned char)*++fmt))
203 goto format_error;
204 GET_NUMBER(right_prec);
205 }
206
207 /* Conversion Characters */
208 switch (*fmt++) {
209 case 'i': /* use internaltion currency format */
210 flags |= USE_INTL_CURRENCY;
211 break;
212 case 'n': /* use national currency format */
213 flags &= ~(USE_INTL_CURRENCY);
214 break;
215 default: /* required character is missing or
216 premature EOS */
217 goto format_error;
218 }
219
220 if (flags & USE_INTL_CURRENCY) {
221 currency_symbol = strdup(lc->int_curr_symbol);
222 if (currency_symbol != NULL)
223 space_char = *(currency_symbol+3);
224 } else
225 currency_symbol = strdup(lc->currency_symbol);
226
227 if (currency_symbol == NULL)
228 goto end_error; /* ENOMEM. */
229
230 /* value itself */
231 value = va_arg(ap, double);
232
233 /* detect sign */
234 if (value < 0) {
235 flags |= IS_NEGATIVE;
236 value = -value;
237 }
238
239 /* fill left_prec with amount of padding chars */
240 if (left_prec >= 0) {
241 pad_size = __calc_left_pad((flags ^ IS_NEGATIVE),
242 currency_symbol) -
243 __calc_left_pad(flags, currency_symbol);
244 if (pad_size < 0)
245 pad_size = 0;
246 }
247
248 asciivalue = __format_grouped_double(value, &flags,
249 left_prec, right_prec, pad_char);
250 if (asciivalue == NULL)
251 goto end_error; /* errno already set */
252 /* to ENOMEM by malloc() */
253
254 /* set some variables for later use */
255 __setup_vars(flags, &cs_precedes, &sep_by_space,
256 &sign_posn, &signstr);
257
258 /*
259 * Description of some LC_MONETARY's values:
260 *
261 * p_cs_precedes & n_cs_precedes
262 *
263 * = 1 - $currency_symbol precedes the value
264 * for a monetary quantity with a non-negative value
265 * = 0 - symbol succeeds the value
266 *
267 * p_sep_by_space & n_sep_by_space
268 *
269 * = 0 - no space separates $currency_symbol
270 * from the value for a monetary quantity with a
271 * non-negative value
272 * = 1 - space separates the symbol from the value
273 * = 2 - space separates the symbol and the sign string,
274 * if adjacent.
275 *
276 * p_sign_posn & n_sign_posn
277 *
278 * = 0 - parentheses enclose the quantity and the
279 * $currency_symbol
280 * = 1 - the sign string precedes the quantity and the
281 * $currency_symbol
282 * = 2 - the sign string succeeds the quantity and the
283 * $currency_symbol
284 * = 3 - the sign string precedes the $currency_symbol
285 * = 4 - the sign string succeeds the $currency_symbol
286 *
287 */
288
289 tmpptr = dst;
290
291 while (pad_size-- > 0)
292 PRINT(' ');
293
294 if (sign_posn == 0 && (flags & IS_NEGATIVE))
295 PRINT('(');
296
297 if (cs_precedes == 1) {
298 if (sign_posn == 1 || sign_posn == 3) {
299 PRINTS(signstr);
300 if (sep_by_space == 2) /* XXX: ? */
301 PRINT(' ');
302 }
303
304 if (!(flags & SUPRESS_CURR_SYMBOL)) {
305 PRINTS(currency_symbol);
306
307 if (sign_posn == 4) {
308 if (sep_by_space == 2)
309 PRINT(space_char);
310 PRINTS(signstr);
311 if (sep_by_space == 1)
312 PRINT(' ');
313 } else if (sep_by_space == 1)
314 PRINT(space_char);
315 }
316 } else if (sign_posn == 1)
317 PRINTS(signstr);
318
319 PRINTS(asciivalue);
320
321 if (cs_precedes == 0) {
322 if (sign_posn == 3) {
323 if (sep_by_space == 1)
324 PRINT(' ');
325 PRINTS(signstr);
326 }
327
328 if (!(flags & SUPRESS_CURR_SYMBOL)) {
329 if ((sign_posn == 3 && sep_by_space == 2)
330 || (sep_by_space == 1
331 && (sign_posn = 0
332 || sign_posn == 1
333 || sign_posn == 2
334 || sign_posn == 4)))
335 PRINT(space_char);
336 PRINTS(currency_symbol); /* XXX: len */
337 if (sign_posn == 4) {
338 if (sep_by_space == 2)
339 PRINT(' ');
340 PRINTS(signstr);
341 }
342 }
343 }
344
345 if (sign_posn == 2) {
346 if (sep_by_space == 2)
347 PRINT(' ');
348 PRINTS(signstr);
349 }
350
351 if (sign_posn == 0 && (flags & IS_NEGATIVE))
352 PRINT(')');
353
354 if (dst - tmpptr < width) {
355 if (flags & LEFT_JUSTIFY) {
356 while (dst - tmpptr <= width)
357 PRINT(' ');
358 } else {
359 pad_size = dst-tmpptr;
360 memmove(tmpptr + width-pad_size, tmpptr,
361 pad_size);
362 memset(tmpptr, ' ', width-pad_size);
363 dst += width-pad_size;
364 }
365 }
366 }
367
368 PRINT('\0');
369 va_end(ap);
370 return (dst - s - 1); /* return size of put data except trailing '\0' */
371
372e2big_error:
373 errno = E2BIG;
374 goto end_error;
375
376format_error:
377 errno = EINVAL;
378
379end_error:
380 if (asciivalue != NULL)
381 free(asciivalue);
382 if (currency_symbol != NULL)
383 free(currency_symbol);
384 va_end(ap);
385 return (-1);
386}
387
388static void
389__setup_vars(int flags, char *cs_precedes, char *sep_by_space,
390 char *sign_posn, char **signstr) {
391
392 struct lconv *lc = localeconv();
393
30
31#include <sys/types.h>
32#include <ctype.h>
33#include <errno.h>
34#include <limits.h>
35#include <locale.h>
36#include <stdarg.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40
41/* internal flags */
42#define NEED_GROUPING 0x01 /* print digits grouped (default) */
43#define SIGN_POSN_USED 0x02 /* '+' or '(' usage flag */
44#define LOCALE_POSN 0x04 /* use locale defined +/- (default) */
45#define PARENTH_POSN 0x08 /* enclose negative amount in () */
46#define SUPRESS_CURR_SYMBOL 0x10 /* supress the currency from output */
47#define LEFT_JUSTIFY 0x20 /* left justify */
48#define USE_INTL_CURRENCY 0x40 /* use international currency symbol */
49#define IS_NEGATIVE 0x80 /* is argument value negative ? */
50
51/* internal macros */
52#define PRINT(CH) do { \
53 if (dst >= s + maxsize) \
54 goto e2big_error; \
55 *dst++ = CH; \
56} while (0)
57
58#define PRINTS(STR) do { \
59 char *tmps = STR; \
60 while (*tmps != '\0') \
61 PRINT(*tmps++); \
62} while (0)
63
64#define GET_NUMBER(VAR) do { \
65 VAR = 0; \
66 while (isdigit((unsigned char)*fmt)) { \
67 VAR *= 10; \
68 VAR += *fmt - '0'; \
69 fmt++; \
70 } \
71} while (0)
72
73#define GRPCPY(howmany) do { \
74 int i = howmany; \
75 while (i-- > 0) { \
76 avalue_size--; \
77 *--bufend = *(avalue+avalue_size+padded); \
78 } \
79} while (0)
80
81#define GRPSEP do { \
82 *--bufend = thousands_sep; \
83 groups++; \
84} while (0)
85
86static void __setup_vars(int, char *, char *, char *, char **);
87static int __calc_left_pad(int, char *);
88static char *__format_grouped_double(double, int *, int, int, int);
89
90ssize_t
91strfmon(char * __restrict s, size_t maxsize, const char * __restrict format,
92 ...)
93{
94 va_list ap;
95 char *dst; /* output destination pointer */
96 const char *fmt; /* current format poistion pointer */
97 struct lconv *lc; /* pointer to lconv structure */
98 char *asciivalue; /* formatted double pointer */
99
100 int flags; /* formatting options */
101 int pad_char; /* padding character */
102 int pad_size; /* pad size */
103 int width; /* field width */
104 int left_prec; /* left precision */
105 int right_prec; /* right precision */
106 double value; /* just value */
107 char space_char = ' '; /* space after currency */
108
109 char cs_precedes, /* values gathered from struct lconv */
110 sep_by_space,
111 sign_posn,
112 *signstr,
113 *currency_symbol;
114
115 char *tmpptr; /* temporary vars */
116 int *ntmp;
117
118 va_start(ap, format);
119
120 lc = localeconv();
121 dst = s;
122 fmt = format;
123 asciivalue = NULL;
124 currency_symbol = NULL;
125 pad_size = 0;
126
127 while (*fmt) {
128 /* pass nonformating characters AS IS */
129 if (*fmt != '%')
130 goto literal;
131
132 /* '%' found ! */
133
134 /* "%%" mean just '%' */
135 if (*(fmt+1) == '%') {
136 fmt++;
137 literal:
138 PRINT(*fmt++);
139 continue;
140 }
141
142 /* set up initial values */
143 flags = (NEED_GROUPING|LOCALE_POSN);
144 pad_char = ' '; /* padding character is "space" */
145 left_prec = -1; /* no left precision specified */
146 right_prec = -1; /* no right precision specified */
147 width = -1; /* no width specified */
148 value = 0; /* we have no value to print now */
149
150 /* Flags */
151 while (1) {
152 switch (*++fmt) {
153 case '=': /* fill character */
154 pad_char = *++fmt;
155 if (pad_char == '\0')
156 goto format_error;
157 continue;
158 case '^': /* not group currency */
159 flags &= ~(NEED_GROUPING);
160 continue;
161 case '+': /* use locale defined signs */
162 if (flags & SIGN_POSN_USED)
163 goto format_error;
164 flags |= (SIGN_POSN_USED|LOCALE_POSN);
165 continue;
166 case '(': /* enclose negatives with () */
167 if (flags & SIGN_POSN_USED)
168 goto format_error;
169 flags |= (SIGN_POSN_USED|PARENTH_POSN);
170 continue;
171 case '!': /* suppress currency symbol */
172 flags |= SUPRESS_CURR_SYMBOL;
173 continue;
174 case '-': /* alignment (left) */
175 flags |= LEFT_JUSTIFY;
176 continue;
177 default:
178 break;
179 }
180 break;
181 }
182
183 /* field Width */
184 if (isdigit((unsigned char)*fmt)) {
185 GET_NUMBER(width);
186 /* Do we have enough space to put number with
187 * required width ?
188 */
189 if (dst + width >= s + maxsize)
190 goto e2big_error;
191 }
192
193 /* Left precision */
194 if (*fmt == '#') {
195 if (!isdigit((unsigned char)*++fmt))
196 goto format_error;
197 GET_NUMBER(left_prec);
198 }
199
200 /* Right precision */
201 if (*fmt == '.') {
202 if (!isdigit((unsigned char)*++fmt))
203 goto format_error;
204 GET_NUMBER(right_prec);
205 }
206
207 /* Conversion Characters */
208 switch (*fmt++) {
209 case 'i': /* use internaltion currency format */
210 flags |= USE_INTL_CURRENCY;
211 break;
212 case 'n': /* use national currency format */
213 flags &= ~(USE_INTL_CURRENCY);
214 break;
215 default: /* required character is missing or
216 premature EOS */
217 goto format_error;
218 }
219
220 if (flags & USE_INTL_CURRENCY) {
221 currency_symbol = strdup(lc->int_curr_symbol);
222 if (currency_symbol != NULL)
223 space_char = *(currency_symbol+3);
224 } else
225 currency_symbol = strdup(lc->currency_symbol);
226
227 if (currency_symbol == NULL)
228 goto end_error; /* ENOMEM. */
229
230 /* value itself */
231 value = va_arg(ap, double);
232
233 /* detect sign */
234 if (value < 0) {
235 flags |= IS_NEGATIVE;
236 value = -value;
237 }
238
239 /* fill left_prec with amount of padding chars */
240 if (left_prec >= 0) {
241 pad_size = __calc_left_pad((flags ^ IS_NEGATIVE),
242 currency_symbol) -
243 __calc_left_pad(flags, currency_symbol);
244 if (pad_size < 0)
245 pad_size = 0;
246 }
247
248 asciivalue = __format_grouped_double(value, &flags,
249 left_prec, right_prec, pad_char);
250 if (asciivalue == NULL)
251 goto end_error; /* errno already set */
252 /* to ENOMEM by malloc() */
253
254 /* set some variables for later use */
255 __setup_vars(flags, &cs_precedes, &sep_by_space,
256 &sign_posn, &signstr);
257
258 /*
259 * Description of some LC_MONETARY's values:
260 *
261 * p_cs_precedes & n_cs_precedes
262 *
263 * = 1 - $currency_symbol precedes the value
264 * for a monetary quantity with a non-negative value
265 * = 0 - symbol succeeds the value
266 *
267 * p_sep_by_space & n_sep_by_space
268 *
269 * = 0 - no space separates $currency_symbol
270 * from the value for a monetary quantity with a
271 * non-negative value
272 * = 1 - space separates the symbol from the value
273 * = 2 - space separates the symbol and the sign string,
274 * if adjacent.
275 *
276 * p_sign_posn & n_sign_posn
277 *
278 * = 0 - parentheses enclose the quantity and the
279 * $currency_symbol
280 * = 1 - the sign string precedes the quantity and the
281 * $currency_symbol
282 * = 2 - the sign string succeeds the quantity and the
283 * $currency_symbol
284 * = 3 - the sign string precedes the $currency_symbol
285 * = 4 - the sign string succeeds the $currency_symbol
286 *
287 */
288
289 tmpptr = dst;
290
291 while (pad_size-- > 0)
292 PRINT(' ');
293
294 if (sign_posn == 0 && (flags & IS_NEGATIVE))
295 PRINT('(');
296
297 if (cs_precedes == 1) {
298 if (sign_posn == 1 || sign_posn == 3) {
299 PRINTS(signstr);
300 if (sep_by_space == 2) /* XXX: ? */
301 PRINT(' ');
302 }
303
304 if (!(flags & SUPRESS_CURR_SYMBOL)) {
305 PRINTS(currency_symbol);
306
307 if (sign_posn == 4) {
308 if (sep_by_space == 2)
309 PRINT(space_char);
310 PRINTS(signstr);
311 if (sep_by_space == 1)
312 PRINT(' ');
313 } else if (sep_by_space == 1)
314 PRINT(space_char);
315 }
316 } else if (sign_posn == 1)
317 PRINTS(signstr);
318
319 PRINTS(asciivalue);
320
321 if (cs_precedes == 0) {
322 if (sign_posn == 3) {
323 if (sep_by_space == 1)
324 PRINT(' ');
325 PRINTS(signstr);
326 }
327
328 if (!(flags & SUPRESS_CURR_SYMBOL)) {
329 if ((sign_posn == 3 && sep_by_space == 2)
330 || (sep_by_space == 1
331 && (sign_posn = 0
332 || sign_posn == 1
333 || sign_posn == 2
334 || sign_posn == 4)))
335 PRINT(space_char);
336 PRINTS(currency_symbol); /* XXX: len */
337 if (sign_posn == 4) {
338 if (sep_by_space == 2)
339 PRINT(' ');
340 PRINTS(signstr);
341 }
342 }
343 }
344
345 if (sign_posn == 2) {
346 if (sep_by_space == 2)
347 PRINT(' ');
348 PRINTS(signstr);
349 }
350
351 if (sign_posn == 0 && (flags & IS_NEGATIVE))
352 PRINT(')');
353
354 if (dst - tmpptr < width) {
355 if (flags & LEFT_JUSTIFY) {
356 while (dst - tmpptr <= width)
357 PRINT(' ');
358 } else {
359 pad_size = dst-tmpptr;
360 memmove(tmpptr + width-pad_size, tmpptr,
361 pad_size);
362 memset(tmpptr, ' ', width-pad_size);
363 dst += width-pad_size;
364 }
365 }
366 }
367
368 PRINT('\0');
369 va_end(ap);
370 return (dst - s - 1); /* return size of put data except trailing '\0' */
371
372e2big_error:
373 errno = E2BIG;
374 goto end_error;
375
376format_error:
377 errno = EINVAL;
378
379end_error:
380 if (asciivalue != NULL)
381 free(asciivalue);
382 if (currency_symbol != NULL)
383 free(currency_symbol);
384 va_end(ap);
385 return (-1);
386}
387
388static void
389__setup_vars(int flags, char *cs_precedes, char *sep_by_space,
390 char *sign_posn, char **signstr) {
391
392 struct lconv *lc = localeconv();
393
394 if (flags & IS_NEGATIVE) {
394 if ((flags & IS_NEGATIVE) && (flags & USE_INTL_CURRENCY)) {
395 *cs_precedes = lc->int_n_cs_precedes;
396 *sep_by_space = lc->int_n_sep_by_space;
397 *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_n_sign_posn;
398 *signstr = (lc->negative_sign == '\0') ? "-"
399 : lc->negative_sign;
400 } else if (flags & USE_INTL_CURRENCY) {
401 *cs_precedes = lc->int_p_cs_precedes;
402 *sep_by_space = lc->int_p_sep_by_space;
403 *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_p_sign_posn;
404 *signstr = lc->positive_sign;
405 } else if (flags & IS_NEGATIVE) {
395 *cs_precedes = lc->n_cs_precedes;
396 *sep_by_space = lc->n_sep_by_space;
397 *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->n_sign_posn;
398 *signstr = (lc->negative_sign == '\0') ? "-"
399 : lc->negative_sign;
400 } else {
401 *cs_precedes = lc->p_cs_precedes;
402 *sep_by_space = lc->p_sep_by_space;
403 *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->p_sign_posn;
404 *signstr = lc->positive_sign;
405 }
406
407 /* Set defult values for unspecified information. */
408 if (*cs_precedes != 0)
409 *cs_precedes = 1;
410 if (*sep_by_space == CHAR_MAX)
411 *sep_by_space = 0;
412 if (*sign_posn == CHAR_MAX)
413 *sign_posn = 0;
414}
415
416static int
417__calc_left_pad(int flags, char *cur_symb) {
418
419 char cs_precedes, sep_by_space, sign_posn, *signstr;
420 int left_chars = 0;
421
422 __setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr);
423
424 if (cs_precedes != 0) {
425 left_chars += strlen(cur_symb);
426 if (sep_by_space != 0)
427 left_chars++;
428 }
429
430 switch (sign_posn) {
431 case 1:
432 left_chars += strlen(signstr);
433 break;
434 case 3:
435 case 4:
436 if (cs_precedes != 0)
437 left_chars += strlen(signstr);
438 }
439 return (left_chars);
440}
441
442static int
443get_groups(int size, char *grouping) {
444
445 int chars = 0;
446
447 if (*grouping == CHAR_MAX || *grouping <= 0) /* no grouping ? */
448 return (0);
449
450 while (size > (int)*grouping) {
451 chars++;
452 size -= (int)*grouping++;
453 /* no more grouping ? */
454 if (*grouping == CHAR_MAX)
455 break;
456 /* rest grouping with same value ? */
457 if (*grouping == 0) {
458 chars += (size - 1) / *(grouping - 1);
459 break;
460 }
461 }
462 return (chars);
463}
464
465/* convert double to ASCII */
466static char *
467__format_grouped_double(double value, int *flags,
468 int left_prec, int right_prec, int pad_char) {
469
470 char *rslt;
471 char *avalue;
472 int avalue_size;
473 char fmt[32];
474
475 size_t bufsize;
476 char *bufend;
477
478 int padded;
479
480 struct lconv *lc = localeconv();
481 char *grouping;
482 char decimal_point;
483 char thousands_sep;
484
485 int groups = 0;
486
487 grouping = lc->mon_grouping;
488 decimal_point = *lc->mon_decimal_point;
489 if (decimal_point == '\0') {
490 decimal_point = *lc->decimal_point;
491 if (decimal_point == '\0')
492 decimal_point = '.';
493 }
494 thousands_sep = *lc->mon_thousands_sep;
495 if (thousands_sep == '\0')
496 thousands_sep = *lc->thousands_sep;
497
498 /* fill left_prec with default value */
499 if (left_prec == -1)
500 left_prec = 0;
501
502 /* fill right_prec with default value */
503 if (right_prec == -1) {
504 if (*flags & USE_INTL_CURRENCY)
505 right_prec = lc->int_frac_digits;
506 else
507 right_prec = lc->frac_digits;
508
509 if (right_prec == CHAR_MAX) /* POSIX locale ? */
510 right_prec = 2;
511 }
512
513 if (*flags & NEED_GROUPING)
514 left_prec += get_groups(left_prec, grouping);
515
516 /* convert to string */
517 snprintf(fmt, sizeof(fmt), "%%%d.%df", left_prec + right_prec + 1,
518 right_prec);
519 avalue_size = asprintf(&avalue, fmt, value);
520 if (avalue_size < 0)
521 return (NULL);
522
523 /* make sure that we've enough space for result string */
524 bufsize = strlen(avalue)*2+1;
525 rslt = malloc(bufsize);
526 if (rslt == NULL) {
527 free(avalue);
528 return (NULL);
529 }
530 memset(rslt, 0, bufsize);
531 bufend = rslt + bufsize - 1; /* reserve space for trailing '\0' */
532
533 /* skip spaces at beggining */
534 padded = 0;
535 while (avalue[padded] == ' ') {
536 padded++;
537 avalue_size--;
538 }
539
540 if (right_prec > 0) {
541 bufend -= right_prec;
542 memcpy(bufend, avalue + avalue_size+padded-right_prec,
543 right_prec);
544 *--bufend = decimal_point;
545 avalue_size -= (right_prec + 1);
546 }
547
548 if ((*flags & NEED_GROUPING) &&
549 thousands_sep != '\0' && /* XXX: need investigation */
550 *grouping != CHAR_MAX &&
551 *grouping > 0) {
552 while (avalue_size > (int)*grouping) {
553 GRPCPY(*grouping);
554 GRPSEP;
555 grouping++;
556
557 /* no more grouping ? */
558 if (*grouping == CHAR_MAX)
559 break;
560
561 /* rest grouping with same value ? */
562 if (*grouping == 0) {
563 grouping--;
564 while (avalue_size > *grouping) {
565 GRPCPY(*grouping);
566 GRPSEP;
567 }
568 }
569 }
570 if (avalue_size != 0)
571 GRPCPY(avalue_size);
572 padded -= groups;
573
574 } else {
575 bufend -= avalue_size;
576 memcpy(bufend, avalue+padded, avalue_size);
577 if (right_prec == 0)
578 padded--; /* decrease assumed $decimal_point */
579 }
580
581 /* do padding with pad_char */
582 if (padded > 0) {
583 bufend -= padded;
584 memset(bufend, pad_char, padded);
585 }
586
587 bufsize = bufsize - (rslt - bufend);
588 memmove(rslt, bufend, bufsize);
589 free(avalue);
590 return (rslt);
591}
406 *cs_precedes = lc->n_cs_precedes;
407 *sep_by_space = lc->n_sep_by_space;
408 *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->n_sign_posn;
409 *signstr = (lc->negative_sign == '\0') ? "-"
410 : lc->negative_sign;
411 } else {
412 *cs_precedes = lc->p_cs_precedes;
413 *sep_by_space = lc->p_sep_by_space;
414 *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->p_sign_posn;
415 *signstr = lc->positive_sign;
416 }
417
418 /* Set defult values for unspecified information. */
419 if (*cs_precedes != 0)
420 *cs_precedes = 1;
421 if (*sep_by_space == CHAR_MAX)
422 *sep_by_space = 0;
423 if (*sign_posn == CHAR_MAX)
424 *sign_posn = 0;
425}
426
427static int
428__calc_left_pad(int flags, char *cur_symb) {
429
430 char cs_precedes, sep_by_space, sign_posn, *signstr;
431 int left_chars = 0;
432
433 __setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr);
434
435 if (cs_precedes != 0) {
436 left_chars += strlen(cur_symb);
437 if (sep_by_space != 0)
438 left_chars++;
439 }
440
441 switch (sign_posn) {
442 case 1:
443 left_chars += strlen(signstr);
444 break;
445 case 3:
446 case 4:
447 if (cs_precedes != 0)
448 left_chars += strlen(signstr);
449 }
450 return (left_chars);
451}
452
453static int
454get_groups(int size, char *grouping) {
455
456 int chars = 0;
457
458 if (*grouping == CHAR_MAX || *grouping <= 0) /* no grouping ? */
459 return (0);
460
461 while (size > (int)*grouping) {
462 chars++;
463 size -= (int)*grouping++;
464 /* no more grouping ? */
465 if (*grouping == CHAR_MAX)
466 break;
467 /* rest grouping with same value ? */
468 if (*grouping == 0) {
469 chars += (size - 1) / *(grouping - 1);
470 break;
471 }
472 }
473 return (chars);
474}
475
476/* convert double to ASCII */
477static char *
478__format_grouped_double(double value, int *flags,
479 int left_prec, int right_prec, int pad_char) {
480
481 char *rslt;
482 char *avalue;
483 int avalue_size;
484 char fmt[32];
485
486 size_t bufsize;
487 char *bufend;
488
489 int padded;
490
491 struct lconv *lc = localeconv();
492 char *grouping;
493 char decimal_point;
494 char thousands_sep;
495
496 int groups = 0;
497
498 grouping = lc->mon_grouping;
499 decimal_point = *lc->mon_decimal_point;
500 if (decimal_point == '\0') {
501 decimal_point = *lc->decimal_point;
502 if (decimal_point == '\0')
503 decimal_point = '.';
504 }
505 thousands_sep = *lc->mon_thousands_sep;
506 if (thousands_sep == '\0')
507 thousands_sep = *lc->thousands_sep;
508
509 /* fill left_prec with default value */
510 if (left_prec == -1)
511 left_prec = 0;
512
513 /* fill right_prec with default value */
514 if (right_prec == -1) {
515 if (*flags & USE_INTL_CURRENCY)
516 right_prec = lc->int_frac_digits;
517 else
518 right_prec = lc->frac_digits;
519
520 if (right_prec == CHAR_MAX) /* POSIX locale ? */
521 right_prec = 2;
522 }
523
524 if (*flags & NEED_GROUPING)
525 left_prec += get_groups(left_prec, grouping);
526
527 /* convert to string */
528 snprintf(fmt, sizeof(fmt), "%%%d.%df", left_prec + right_prec + 1,
529 right_prec);
530 avalue_size = asprintf(&avalue, fmt, value);
531 if (avalue_size < 0)
532 return (NULL);
533
534 /* make sure that we've enough space for result string */
535 bufsize = strlen(avalue)*2+1;
536 rslt = malloc(bufsize);
537 if (rslt == NULL) {
538 free(avalue);
539 return (NULL);
540 }
541 memset(rslt, 0, bufsize);
542 bufend = rslt + bufsize - 1; /* reserve space for trailing '\0' */
543
544 /* skip spaces at beggining */
545 padded = 0;
546 while (avalue[padded] == ' ') {
547 padded++;
548 avalue_size--;
549 }
550
551 if (right_prec > 0) {
552 bufend -= right_prec;
553 memcpy(bufend, avalue + avalue_size+padded-right_prec,
554 right_prec);
555 *--bufend = decimal_point;
556 avalue_size -= (right_prec + 1);
557 }
558
559 if ((*flags & NEED_GROUPING) &&
560 thousands_sep != '\0' && /* XXX: need investigation */
561 *grouping != CHAR_MAX &&
562 *grouping > 0) {
563 while (avalue_size > (int)*grouping) {
564 GRPCPY(*grouping);
565 GRPSEP;
566 grouping++;
567
568 /* no more grouping ? */
569 if (*grouping == CHAR_MAX)
570 break;
571
572 /* rest grouping with same value ? */
573 if (*grouping == 0) {
574 grouping--;
575 while (avalue_size > *grouping) {
576 GRPCPY(*grouping);
577 GRPSEP;
578 }
579 }
580 }
581 if (avalue_size != 0)
582 GRPCPY(avalue_size);
583 padded -= groups;
584
585 } else {
586 bufend -= avalue_size;
587 memcpy(bufend, avalue+padded, avalue_size);
588 if (right_prec == 0)
589 padded--; /* decrease assumed $decimal_point */
590 }
591
592 /* do padding with pad_char */
593 if (padded > 0) {
594 bufend -= padded;
595 memset(bufend, pad_char, padded);
596 }
597
598 bufsize = bufsize - (rslt - bufend);
599 memmove(rslt, bufend, bufsize);
600 free(avalue);
601 return (rslt);
602}