1 : #define VSTR_ADD_FMT_C
2 : /*
3 : * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 James Antill
4 : *
5 : * This library is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU Lesser General Public
7 : * License as published by the Free Software Foundation; either
8 : * version 2 of the License, or (at your option) any later version.
9 : *
10 : * This library is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : * Lesser General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU Lesser General Public
16 : * License along with this library; if not, write to the Free Software
17 : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 : *
19 : * email: james@and.org
20 : */
21 : /* this code is hacked from:
22 : * vsprintf in linux/lib -- by Lars Wirzenius <liw@iki.fi>
23 : * He gave his permission for it to be released as LGPL.
24 : * ... it's changed a lot since then. */
25 : /* functions for portable printf functionality in a vstr -- does C99 std. and
26 : * custom format specifiers (registration via. vstr_fmt.c).
27 : * Has a "portable" implementation of double printing */
28 : #include "main.h"
29 :
30 : #if !USE_WIDE_CHAR_T
31 : # undef wchar_t
32 : # define wchar_t int /* doesn't matter ... fails out anyway */
33 : # undef wint_t
34 : # define wint_t int /* doesn't matter ... fails out anyway */
35 : #endif
36 :
37 :
38 :
39 : #define VSTR__ADD_FMT_ISDIGIT(x) (((x) >= '0') && ((x) <= '9'))
40 :
41 : #define VSTR__ADD_FMT_CHAR2INT_1(x) ((x) - '0')
42 : #define VSTR__ADD_FMT_CHAR2INT_2(x, y) ((((x) - '0') * 10) + \
43 : ((y) - '0'))
44 :
45 : /* a very small number of digits is _by far_ the norm */
46 : #define VSTR__ADD_FMT_STRTOL(x) \
47 : (VSTR__ADD_FMT_ISDIGIT((x)[1]) ? \
48 : VSTR__ADD_FMT_ISDIGIT((x)[2]) ? strtol(x, (char **) &(x), 10) : \
49 : ((x) += 2, VSTR__ADD_FMT_CHAR2INT_2((x)[-2], (x)[-1])) : \
50 : ((x) += 1, VSTR__ADD_FMT_CHAR2INT_1((x)[-1])))
51 :
52 : /* 1 byte for the base of the number, can go upto base 255 */
53 : #define BASE_MASK ((1 << 8) - 1)
54 :
55 : #define ZEROPAD (1 << 8) /* pad with zero */
56 : #define SIGN (1 << 9) /* unsigned/signed */
57 : #define PLUS (1 << 10) /* show plus sign '+' */
58 : #define SPACE (1 << 11) /* space if plus */
59 : #define LEFT (1 << 12) /* left justified */
60 : #define SPECIAL (1 << 13) /* 0x */
61 : #define LARGE (1 << 14) /* use 'ABCDEF' instead of 'abcdef' */
62 : #define THOUSAND_SEP (1 << 15) /* split at grouping marks according to locale */
63 : #define ALT_DIGITS (1 << 16) /* does nothing in core code, can be used in
64 : * custom format specifiers */
65 :
66 : #define PREC_USR (1 << 28) /* user specified precision */
67 : #define NUM_IS_ZERO (1 << 29) /* is the number zero */
68 : #define NUM_IS_NEGATIVE (1 << 30) /* is the number negative */
69 :
70 : #define VSTR__FMT_ADD(x, y, z) vstr_add_buf((x), ((x)->len - pos_diff), y, z)
71 : #define VSTR__FMT_ADD_REP_CHR(x, y, z) \
72 : vstr_add_rep_chr((x), ((x)->len - pos_diff), y, z)
73 : #define VSTR__FMT_ADD_GRPBASENUM(x, NB, y, z) \
74 : vstr_sc_add_grpbasenum_buf((x), ((x)->len - pos_diff), NB, y, z)
75 :
76 : /* deals well with INT_MIN */
77 : #define VSTR__FMT_S2U_NUM(unum, snum) do { \
78 : ++snum; unum = -snum; ++unum; \
79 : } while (FALSE)
80 : #define VSTR__FMT_ABS_NUM(unum, snum) do { \
81 : if (snum < 0) { \
82 : spec->flags |= NUM_IS_NEGATIVE; \
83 : VSTR__FMT_S2U_NUM(unum, snum); \
84 : } else \
85 : unum = snum; \
86 : } while (FALSE)
87 :
88 :
89 : /* functions for outputing thousands grouping ... */
90 : unsigned int vstr__add_fmt_grouping_mod(const char *grouping, unsigned int num)
91 74755 : {
92 74755 : unsigned int tmp = 0;
93 :
94 74755 : if (!*grouping)
95 47020 : return (num);
96 :
97 10895727 : while (((unsigned char)*grouping < SCHAR_MAX) &&
98 : ((tmp + *grouping) < num))
99 : {
100 10867992 : tmp += *grouping;
101 10867992 : if (grouping[1])
102 15882 : ++grouping;
103 : }
104 :
105 27735 : return (num - tmp);
106 : }
107 :
108 : size_t vstr__add_fmt_grouping_num_sz(Vstr_base *base,
109 : unsigned int num_base, size_t len)
110 4485 : {
111 4485 : size_t ret = 0;
112 4485 : int done = FALSE;
113 4485 : Vstr_locale *loc = base->conf->loc;
114 4485 : const char *grouping = vstr__loc_num_grouping(loc, num_base);
115 4485 : size_t sep_len = vstr__loc_num_sep_len(loc, num_base);
116 :
117 11514 : while (len)
118 : {
119 7029 : unsigned int num = vstr__add_fmt_grouping_mod(grouping, len);
120 :
121 7029 : if (done)
122 2726 : ret += sep_len;
123 :
124 7029 : ret += num;
125 7029 : assert(num <= len);
126 7029 : len -= num;
127 :
128 7029 : done = TRUE;
129 : }
130 :
131 4485 : return (ret);
132 : }
133 :
134 :
135 : struct Vstr__fmt_spec
136 : {
137 : union Vstr__fmt_sp_un
138 : {
139 : unsigned char data_c;
140 : unsigned short data_s;
141 : unsigned int data_i;
142 : wint_t data_wint;
143 : unsigned long data_l;
144 : Vstr__unsigned_long_long data_L;
145 : size_t data_sz;
146 : uintmax_t data_m;
147 :
148 : double data_d;
149 : long double data_Ld;
150 :
151 : void *data_ptr;
152 : } u;
153 :
154 : ptrdiff_t data_t; /* NOTE: no uptrdiff_t, but need real ptrdiff_t for
155 : * custom formatters. So normal uintmax_t */
156 :
157 : unsigned char fmt_code;
158 : int num_base;
159 : unsigned int int_type;
160 : unsigned int flags;
161 : unsigned int field_width;
162 : unsigned int precision;
163 :
164 : unsigned int main_param;
165 : unsigned int field_width_param;
166 : unsigned int precision_param;
167 :
168 : Vstr__fmt_usr_name_node *usr_spec;
169 :
170 : /* these two needed for double, not used elsewhere */
171 : /* unsigned int precision_usr : 1; done with the PREC_USR flag */
172 : unsigned int field_width_usr : 1; /* did the usr specify a field width */
173 : unsigned int escape_usr : 1; /* did the usr specify this escape */
174 :
175 : struct Vstr__fmt_spec *next;
176 : };
177 :
178 : static int vstr__add_fmt_number(Vstr_base *base, size_t pos_diff,
179 : struct Vstr__fmt_spec *spec)
180 167655 : {
181 167655 : char sign = 0;
182 : /* used to hold the actual number */
183 167655 : char buf_beg[BUF_NUM_TYPE_SZ(uintmax_t)];
184 167655 : char *buf = buf_beg + sizeof(buf_beg);
185 167655 : size_t i = 0;
186 167655 : size_t real_i = 0;
187 : /* can't make array */
188 167655 : const char *chrs_base = "0123456789abcdefghijklmnopqrstuvwxyz";
189 167655 : const char *grouping = NULL;
190 167655 : unsigned char grp_num = 0;
191 167655 : const char *thou = NULL;
192 167655 : size_t thou_len = 0;
193 167655 : size_t max_p_i = 0;
194 167655 : unsigned int field_width = 0;
195 167655 : unsigned int precision = 1;
196 167655 : unsigned int wr_hex_0x = FALSE;
197 :
198 167655 : if (spec->flags & PREC_USR)
199 30912 : precision = spec->precision;
200 :
201 167655 : if (spec->field_width_usr)
202 67031 : field_width = spec->field_width;
203 :
204 : /* if the usr specified a precision the '0' flag is ignored */
205 167655 : if (spec->flags & PREC_USR)
206 30912 : spec->flags &= ~ZEROPAD;
207 :
208 167655 : if (spec->flags & LARGE)
209 12932 : chrs_base = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
210 :
211 167655 : if (spec->flags & LEFT)
212 12013 : spec->flags &= ~ZEROPAD;
213 :
214 167655 : switch (spec->num_base)
215 : {
216 : case 10:
217 : case 16:
218 : case 8:
219 :
220 123387 : ASSERT_NO_SWITCH_DEF();
221 : }
222 :
223 167655 : if (spec->flags & SIGN)
224 : {
225 25334 : if (spec->flags & NUM_IS_NEGATIVE)
226 6093 : sign = '-';
227 : else
228 : {
229 19241 : if (spec->flags & PLUS)
230 1870 : sign = '+';
231 : else
232 17371 : if (spec->flags & SPACE)
233 2090 : sign = ' ';
234 : else
235 15281 : ++field_width;
236 : }
237 :
238 25334 : if (field_width) --field_width;
239 : }
240 :
241 167655 : grouping = vstr__loc_num_grouping(base->conf->loc, spec->num_base);
242 167655 : thou_len = vstr__loc_num_sep_len(base->conf->loc, spec->num_base);
243 167655 : thou = vstr__loc_num_sep_ptr(base->conf->loc, spec->num_base);
244 :
245 167655 : grp_num = *grouping;
246 167655 : if (!thou_len || (grp_num >= SCHAR_MAX) || (grp_num <= 0))
247 153304 : spec->flags &= ~THOUSAND_SEP; /* don't do it */
248 :
249 167655 : switch (spec->int_type)
250 : {
251 : case VSTR_TYPE_FMT_UCHAR:
252 170 : VSTR__ADD_FMT_NUM(unsigned char, spec->u.data_c, spec->num_base); break;
253 : case VSTR_TYPE_FMT_USHORT:
254 159 : VSTR__ADD_FMT_NUM(unsigned short, spec->u.data_s, spec->num_base); break;
255 : case VSTR_TYPE_FMT_UINT:
256 92985 : VSTR__ADD_FMT_NUM(unsigned int, spec->u.data_i, spec->num_base); break;
257 : case VSTR_TYPE_FMT_ULONG:
258 5124 : VSTR__ADD_FMT_NUM(unsigned long, spec->u.data_l, spec->num_base); break;
259 : case VSTR_TYPE_FMT_ULONG_LONG:
260 66 : VSTR__ADD_FMT_NUM(Vstr__unsigned_long_long, spec->u.data_L,
261 : spec->num_base); break;
262 : case VSTR_TYPE_FMT_SIZE_T:
263 141 : VSTR__ADD_FMT_NUM(size_t, spec->u.data_sz, spec->num_base); break;
264 : /* ptrdiff_t is actually promoted to intmax_t so that unsigned works */
265 : case VSTR_TYPE_FMT_UINTMAX_T:
266 69005 : VSTR__ADD_FMT_NUM(uintmax_t, spec->u.data_m, spec->num_base); break;
267 : case VSTR_TYPE_FMT_PTR_VOID:
268 5 : VSTR__ADD_FMT_NUM(uintptr_t, (uintptr_t)spec->u.data_ptr, spec->num_base);
269 123387 : ASSERT_NO_SWITCH_DEF(); /* only valid types above */
270 : }
271 :
272 167655 : i = sizeof(buf_beg) - (buf - buf_beg);
273 :
274 167655 : real_i = i;
275 167655 : if (spec->flags & THOUSAND_SEP)
276 1601 : real_i = vstr__add_fmt_grouping_num_sz(base, spec->num_base, i);
277 :
278 167655 : if (spec->flags & SPECIAL)
279 : {
280 20415 : if ((spec->num_base == 16) && !(spec->flags & NUM_IS_ZERO))
281 : { /* only append 0x if it is a non-zero value, but not if precision == 0 */
282 6495 : wr_hex_0x = TRUE;
283 6495 : if (field_width) --field_width;
284 6495 : if (field_width) --field_width;
285 : }
286 : else
287 : { /* hacky spec, if octal then we up the precision so that a 0 is printed as
288 : * the first character, if there isn't a 0 there to start with
289 : * -- even if num == 0 and precision == 0 */
290 13920 : if ((spec->num_base == 8) &&
291 : (((spec->flags & NUM_IS_ZERO) && !precision) ||
292 : (precision <= real_i)))
293 5053 : precision = real_i + 1;
294 : }
295 : }
296 :
297 167655 : if (real_i > precision)
298 130743 : max_p_i = real_i;
299 : else
300 36912 : max_p_i = precision;
301 :
302 167655 : if (field_width < max_p_i)
303 111246 : field_width = 0;
304 : else
305 56409 : field_width -= max_p_i;
306 :
307 167655 : if (!(spec->flags & (ZEROPAD | LEFT)))
308 112921 : if (field_width > 0)
309 : { /* right justify number, with spaces -- zeros done after sign/spacials */
310 8673 : if (!VSTR__FMT_ADD_REP_CHR(base, ' ', field_width))
311 44 : goto failed_alloc;
312 8629 : field_width = 0;
313 : }
314 :
315 167611 : if (sign)
316 10032 : if (!VSTR__FMT_ADD_REP_CHR(base, sign, 1))
317 3 : goto failed_alloc;
318 :
319 167608 : if (spec->flags & SPECIAL)
320 : {
321 20382 : if (wr_hex_0x)
322 : {
323 6489 : if (!VSTR__FMT_ADD_REP_CHR(base, '0', 1))
324 2 : goto failed_alloc;
325 6487 : if (!VSTR__FMT_ADD_REP_CHR(base, chrs_base[33], 1))
326 2 : goto failed_alloc;
327 : }
328 : }
329 :
330 167604 : if (!(spec->flags & LEFT))
331 155591 : if (field_width > 0)
332 : { /* right justify number, with zeros */
333 6368 : assert(spec->flags & ZEROPAD);
334 6368 : if (!VSTR__FMT_ADD_REP_CHR(base, '0', field_width))
335 16 : goto failed_alloc;
336 6352 : field_width = 0;
337 : }
338 :
339 167588 : if (precision > real_i) /* make number the desired length */
340 : {
341 20314 : if (!VSTR__FMT_ADD_REP_CHR(base, '0', precision - real_i))
342 94 : goto failed_alloc;
343 : }
344 :
345 167494 : if (i)
346 : {
347 159459 : int ret = FALSE;
348 :
349 : /* output number */
350 159459 : if (spec->flags & THOUSAND_SEP)
351 1419 : ret = VSTR__FMT_ADD_GRPBASENUM(base, spec->num_base, buf, i);
352 : else
353 158040 : ret = VSTR__FMT_ADD(base, buf, i);
354 :
355 159459 : if (!ret)
356 279 : goto failed_alloc;
357 : }
358 :
359 167215 : if (field_width > 0)
360 : {
361 5131 : assert(spec->flags & LEFT);
362 5131 : if (!VSTR__FMT_ADD_REP_CHR(base, ' ', field_width))
363 55 : goto failed_alloc;
364 : }
365 :
366 167160 : return (TRUE);
367 :
368 : failed_alloc:
369 495 : return (FALSE);
370 : }
371 :
372 : void vstr__add_fmt_free_conf(Vstr_conf *conf)
373 6057 : {
374 6057 : struct Vstr__fmt_spec *scan = conf->vstr__fmt_spec_make;
375 :
376 10019 : assert(!conf->vstr__fmt_spec_list_beg && !conf->vstr__fmt_spec_list_beg);
377 :
378 11218 : while (scan)
379 : {
380 5161 : struct Vstr__fmt_spec *scan_next = scan->next;
381 5161 : VSTR__F(scan);
382 5161 : scan = scan_next;
383 : }
384 :
385 6057 : conf->vstr__fmt_spec_make = NULL;
386 :
387 61893 : while (conf->fmt_usr_names)
388 : {
389 55836 : Vstr__fmt_usr_name_node *tmp = conf->fmt_usr_names;
390 :
391 55836 : vstr_fmt_del(conf, tmp->name_str);
392 : }
393 : }
394 :
395 : static int vstr__fmt_add_spec(Vstr_conf *conf)
396 5763 : {
397 5763 : struct Vstr__fmt_spec *spec = NULL;
398 :
399 5763 : if (!(spec = VSTR__MK(sizeof(struct Vstr__fmt_spec))))
400 42 : return (FALSE);
401 :
402 5721 : assert(vstr_wrap_memset(spec, 0xeF, sizeof(struct Vstr__fmt_spec)));
403 :
404 5721 : spec->next = conf->vstr__fmt_spec_make;
405 5721 : conf->vstr__fmt_spec_make = spec;
406 :
407 5721 : return (TRUE);
408 : }
409 :
410 : #define VSTR__FMT_MV_SPEC(conf, x) vstr__fmt_mv_spec(conf, spec, x, ¶ms)
411 : static void vstr__fmt_mv_spec(Vstr_conf *conf, struct Vstr__fmt_spec *spec,
412 : int main_param, unsigned int *params)
413 1513017 : {
414 1513017 : conf->vstr__fmt_spec_make = spec->next;
415 :
416 1513017 : if (!conf->vstr__fmt_spec_list_beg)
417 : {
418 253504 : conf->vstr__fmt_spec_list_end = spec;
419 253504 : conf->vstr__fmt_spec_list_beg = spec;
420 : }
421 : else
422 : {
423 1259513 : conf->vstr__fmt_spec_list_end->next = spec;
424 1259513 : conf->vstr__fmt_spec_list_end = spec;
425 : }
426 :
427 1513017 : spec->next = NULL;
428 :
429 : /* increment if this counts towards users $x numbers */
430 1513017 : if (main_param && !spec->main_param)
431 895695 : spec->main_param = ++*params;
432 : }
433 :
434 : # define VSTR__FMT_ANAL_ZERO 1 /* anally zero stuff that shouldn't really need
435 : * it but seems to *sigh*/
436 : static void vstr__fmt_init_spec(struct Vstr__fmt_spec *spec)
437 1513029 : {
438 1513029 : assert(spec);
439 :
440 1513029 : if (VSTR__FMT_ANAL_ZERO)
441 : {
442 1513029 : spec->u.data_ptr = NULL;
443 : }
444 :
445 1513029 : spec->fmt_code = 0;
446 1513029 : spec->num_base = 10;
447 1513029 : spec->int_type = VSTR_TYPE_FMT_UINT;
448 1513029 : spec->flags = 0;
449 :
450 1513029 : if (VSTR__FMT_ANAL_ZERO)
451 : {
452 1513029 : spec->precision = 0;
453 1513029 : spec->field_width = 0;
454 : }
455 :
456 1513029 : spec->main_param = 0;
457 1513029 : spec->field_width_param = 0;
458 1513029 : spec->precision_param = 0;
459 :
460 1513029 : spec->usr_spec = NULL;
461 :
462 1513029 : spec->field_width_usr = FALSE;
463 1513029 : spec->escape_usr = FALSE;
464 : }
465 :
466 : /* must match with code in vstr_version.c */
467 : #if defined(VSTR_AUTOCONF_FMT_DBL_glibc)
468 : # include "vstr_dbl/vstr_add_fmt_dbl_glibc.c"
469 : #elif defined(VSTR_AUTOCONF_FMT_DBL_none)
470 : # include "vstr_dbl/vstr_add_fmt_dbl_none.c"
471 : #elif defined(VSTR_AUTOCONF_FMT_DBL_host)
472 : # include "vstr_dbl/vstr_add_fmt_dbl_host.c"
473 : #else
474 : # error "Please configure properly..."
475 : #endif
476 :
477 : static int vstr__add_fmt_char(Vstr_base *base, size_t pos_diff,
478 : struct Vstr__fmt_spec *spec)
479 5407 : {
480 5407 : if (spec->field_width > 0)
481 214 : --spec->field_width; /* account for char */
482 :
483 5407 : if (spec->field_width_usr && !(spec->flags & LEFT))
484 112 : if (spec->field_width > 0)
485 : {
486 112 : if (!VSTR__FMT_ADD_REP_CHR(base, ' ', spec->field_width))
487 9 : goto failed_alloc;
488 103 : spec->field_width_usr = FALSE;
489 : }
490 :
491 5398 : if (!VSTR__FMT_ADD_REP_CHR(base, spec->u.data_c, 1))
492 10 : goto failed_alloc;
493 :
494 5388 : if (spec->field_width_usr && (spec->field_width > 0))
495 : {
496 101 : if (!VSTR__FMT_ADD_REP_CHR(base, ' ', spec->field_width))
497 9 : goto failed_alloc;
498 : }
499 :
500 5379 : return (TRUE);
501 :
502 : failed_alloc:
503 28 : return (FALSE);
504 : }
505 :
506 : #if USE_WIDE_CHAR_T
507 : static int vstr__add_fmt_wide_char(Vstr_base *base, size_t pos_diff,
508 : struct Vstr__fmt_spec *spec)
509 282 : {
510 282 : mbstate_t state;
511 282 : size_t len_mbs = 0;
512 282 : char *buf_mbs = NULL;
513 :
514 282 : vstr_wrap_memset(&state, 0, sizeof(mbstate_t));
515 282 : len_mbs = wcrtomb(NULL, spec->u.data_wint, &state);
516 282 : if (len_mbs != (size_t)-1)
517 : { /* stupid glibc only returns -1 when you pass a buf */
518 282 : len_mbs += wcrtomb(NULL, L'\0', &state);
519 :
520 282 : if (!(buf_mbs = VSTR__MK(len_mbs)))
521 : {
522 31 : base->conf->malloc_bad = TRUE;
523 31 : goto failed_alloc;
524 : }
525 251 : vstr_wrap_memset(&state, 0, sizeof(mbstate_t));
526 251 : len_mbs = wcrtomb(buf_mbs, spec->u.data_wint, &state);
527 : }
528 251 : if (len_mbs == (size_t)-1)
529 4 : goto failed_EILSEQ;
530 247 : len_mbs += wcrtomb(buf_mbs + len_mbs, L'\0', &state);
531 247 : --len_mbs; /* remove terminator */
532 :
533 247 : if (spec->field_width > 0)
534 128 : --spec->field_width; /* account for char */
535 :
536 247 : if (spec->field_width_usr && !(spec->flags & LEFT))
537 73 : if (spec->field_width > 0)
538 : {
539 73 : if (!VSTR__FMT_ADD_REP_CHR(base, ' ', spec->field_width))
540 10 : goto C_failed_alloc_free_buf_mbs;
541 63 : spec->field_width_usr = FALSE;
542 : }
543 :
544 237 : if (!VSTR__FMT_ADD(base, buf_mbs, len_mbs))
545 4 : goto C_failed_alloc_free_buf_mbs;
546 :
547 233 : if (spec->field_width_usr && (spec->field_width > 0))
548 : {
549 54 : if (!VSTR__FMT_ADD_REP_CHR(base, ' ', spec->field_width))
550 10 : goto C_failed_alloc_free_buf_mbs;
551 : }
552 :
553 223 : VSTR__F(buf_mbs);
554 223 : return (TRUE);
555 :
556 : C_failed_alloc_free_buf_mbs:
557 : failed_EILSEQ: /* FIXME: need a good way to see this */
558 28 : VSTR__F(buf_mbs);
559 : failed_alloc:
560 59 : return (FALSE);
561 : }
562 : #else
563 : static int vstr__add_fmt_wide_char(Vstr_base *VSTR__ATTR_UNUSED(base),
564 : size_t VSTR__ATTR_UNUSED(pos_diff),
565 : struct Vstr__fmt_spec *
566 : VSTR__ATTR_UNUSED(spec))
567 1 : {
568 1 : assert(FALSE);
569 1 : return (FALSE);
570 : }
571 : #endif
572 :
573 : static int vstr__add_fmt_cstr(Vstr_base *base, size_t pos_diff,
574 : struct Vstr__fmt_spec *spec)
575 697628 : {
576 697628 : size_t len = 0;
577 697628 : const char *str = spec->u.data_ptr;
578 :
579 697628 : if (!str)
580 : {
581 30 : str = base->conf->loc->null_ref->ptr;
582 30 : len = base->conf->loc->null_len;
583 30 : if ((spec->flags & PREC_USR) && (len > spec->precision))
584 10 : len = spec->precision;
585 : }
586 697598 : else if (spec->flags & PREC_USR)
587 443956 : len = strnlen(str, spec->precision);
588 : else
589 253642 : len = strlen(str);
590 :
591 697628 : if ((spec->flags & PREC_USR) && spec->field_width_usr &&
592 : (spec->field_width > spec->precision))
593 10 : spec->field_width = spec->precision;
594 :
595 697628 : if (spec->field_width_usr && !(spec->flags & LEFT))
596 14281 : if (spec->field_width > len)
597 : {
598 13568 : if (!VSTR__FMT_ADD_REP_CHR(base, ' ', spec->field_width - len))
599 2969 : goto failed_alloc;
600 10599 : spec->field_width_usr = FALSE;
601 : }
602 :
603 694659 : if (!VSTR__FMT_ADD(base, str, len))
604 1250 : goto failed_alloc;
605 :
606 693409 : if (spec->field_width_usr && (spec->field_width > len))
607 101 : if (!VSTR__FMT_ADD_REP_CHR(base, ' ', spec->field_width - len))
608 9 : goto failed_alloc;
609 :
610 693400 : return (TRUE);
611 :
612 : failed_alloc:
613 4228 : return (FALSE);
614 : }
615 :
616 : #if USE_WIDE_CHAR_T
617 : static int vstr__add_fmt_wide_cstr(Vstr_base *base, size_t pos_diff,
618 : struct Vstr__fmt_spec *spec)
619 357 : { /* note precision is number of wchar_t's allowed through, not bytes
620 : * as in sprintf() */
621 357 : mbstate_t state;
622 357 : size_t len_mbs = 0;
623 357 : char *buf_mbs = NULL;
624 357 : const wchar_t *wstr = spec->u.data_ptr;
625 357 : const wchar_t *tmp = NULL;
626 :
627 357 : if (!wstr)
628 12 : return (vstr__add_fmt_cstr(base, pos_diff, spec));
629 :
630 345 : tmp = wstr;
631 :
632 345 : vstr_wrap_memset(&state, 0, sizeof(mbstate_t));
633 :
634 345 : if (!(spec->flags & PREC_USR))
635 325 : spec->precision = UINT_MAX;
636 345 : len_mbs = wcsnrtombs(NULL, &tmp, spec->precision, 0, &state);
637 345 : if (len_mbs == (size_t)-1)
638 4 : goto failed_EILSEQ;
639 : /* NULL when found end of input -- glibc doesn't do this when arg1 NULL
640 : * if (!tmp)
641 : * ++len_mbs;
642 : * else
643 : */
644 : { /* wcslen() > spec->precision */
645 341 : size_t tmp_mbs = wcrtomb(NULL, L'\0', &state);
646 :
647 341 : if (tmp_mbs != (size_t)-1)
648 341 : len_mbs += tmp_mbs; /* include '\0' */
649 : }
650 :
651 341 : if (!(buf_mbs = VSTR__MK(len_mbs)))
652 : {
653 32 : base->conf->malloc_bad = TRUE;
654 32 : goto failed_alloc;
655 : }
656 309 : tmp = wstr;
657 309 : vstr_wrap_memset(&state, 0, sizeof(mbstate_t));
658 309 : len_mbs = wcsnrtombs(buf_mbs, &tmp, spec->precision, len_mbs, &state);
659 309 : if (tmp) /* wcslen() > spec->precision */
660 : {
661 16 : size_t tmp_mbs = wcrtomb(buf_mbs + len_mbs, L'\0', &state);
662 16 : len_mbs += tmp_mbs - 1; /* ignore '\0' */
663 : }
664 :
665 309 : if ((spec->flags & PREC_USR) && spec->field_width_usr &&
666 : (spec->field_width > spec->precision))
667 8 : spec->field_width = spec->precision;
668 :
669 309 : if (spec->field_width_usr && !(spec->flags & LEFT))
670 97 : if (spec->field_width > len_mbs)
671 : {
672 93 : if (!VSTR__FMT_ADD_REP_CHR(base, ' ', spec->field_width - len_mbs))
673 10 : goto S_failed_alloc_free_buf_mbs;
674 83 : spec->field_width_usr = FALSE;
675 : }
676 :
677 299 : if (!VSTR__FMT_ADD(base, buf_mbs, len_mbs))
678 6 : goto S_failed_alloc_free_buf_mbs;
679 :
680 293 : if (spec->field_width_usr && (spec->field_width > len_mbs))
681 54 : if (!VSTR__FMT_ADD_REP_CHR(base, ' ', spec->field_width - len_mbs))
682 10 : goto S_failed_alloc_free_buf_mbs;
683 :
684 283 : VSTR__F(buf_mbs);
685 283 : return (TRUE);
686 :
687 : S_failed_alloc_free_buf_mbs:
688 26 : VSTR__F(buf_mbs);
689 : failed_alloc:
690 : failed_EILSEQ: /* FIXME: */
691 62 : return (FALSE);
692 : }
693 : #else
694 : static int vstr__add_fmt_wide_cstr(Vstr_base *base,
695 : size_t pos_diff,
696 : struct Vstr__fmt_spec *spec)
697 4 : {
698 4 : if (!spec->u.data_ptr)
699 3 : return (vstr__add_fmt_cstr(base, pos_diff, spec));
700 :
701 1 : assert(FALSE);
702 1 : return (FALSE);
703 : }
704 : #endif
705 :
706 : static
707 : struct Vstr__fmt_spec *
708 : vstr__add_fmt_usr_write_spec(Vstr_base *base, size_t orig_len, size_t pos_diff,
709 : struct Vstr__fmt_spec *spec, int sve_errno)
710 210182 : {
711 210182 : struct Vstr__fmt_spec *last = NULL;
712 : struct
713 : {
714 : Vstr_fmt_spec usr_spec;
715 : void *params[VSTR__FMT_USR_SZ];
716 210182 : } dummy;
717 210182 : Vstr_fmt_spec *usr_spec = NULL;
718 210182 : unsigned int scan = 0;
719 210182 : int (*func)(Vstr_base *, size_t, struct Vstr_fmt_spec *) =
720 210182 : spec->usr_spec->func;
721 210182 : unsigned int *types = spec->usr_spec->types;
722 210182 : unsigned int sz = spec->usr_spec->sz;
723 :
724 210182 : assert(spec->escape_usr);
725 210182 : assert(spec->usr_spec);
726 :
727 210182 : if (sz <= VSTR__FMT_USR_SZ)
728 190237 : usr_spec = &dummy.usr_spec;
729 : else
730 : {
731 19945 : if (!(usr_spec = VSTR__MK(sizeof(Vstr_fmt_spec) +
732 : (spec->usr_spec->sz * sizeof(void *)))))
733 49 : return (NULL);
734 : }
735 :
736 210133 : usr_spec->vstr_orig_len = orig_len;
737 210133 : usr_spec->name = spec->usr_spec->name_str;
738 :
739 210133 : usr_spec->obj_precision = 0;
740 210133 : usr_spec->obj_field_width = 0;
741 :
742 210133 : if ((usr_spec->fmt_precision = !!(spec->flags & PREC_USR)))
743 45667 : usr_spec->obj_precision = spec->precision;
744 210133 : if ((usr_spec->fmt_field_width = spec->field_width_usr))
745 60781 : usr_spec->obj_field_width = spec->field_width;
746 :
747 210133 : usr_spec->fmt_minus = !!(spec->flags & LEFT);
748 210133 : usr_spec->fmt_plus = !!(spec->flags & PLUS);
749 210133 : usr_spec->fmt_space = !!(spec->flags & SPACE);
750 210133 : usr_spec->fmt_hash = !!(spec->flags & SPECIAL);
751 210133 : usr_spec->fmt_zero = !!(spec->flags & ZEROPAD);
752 210133 : usr_spec->fmt_quote = !!(spec->flags & THOUSAND_SEP);
753 210133 : usr_spec->fmt_I = !!(spec->flags & ALT_DIGITS);
754 :
755 210133 : if (!types[scan])
756 : {
757 35 : assert(spec->escape_usr && !!spec->usr_spec);
758 :
759 35 : last = spec;
760 35 : spec = spec->next;
761 : }
762 :
763 611253 : while (types[scan])
764 : {
765 401120 : switch (types[scan])
766 : {
767 : case VSTR_TYPE_FMT_INT:
768 : case VSTR_TYPE_FMT_UINT:
769 : case VSTR_TYPE_FMT_LONG:
770 : case VSTR_TYPE_FMT_ULONG:
771 : case VSTR_TYPE_FMT_LONG_LONG:
772 : case VSTR_TYPE_FMT_ULONG_LONG:
773 : case VSTR_TYPE_FMT_SSIZE_T:
774 : case VSTR_TYPE_FMT_SIZE_T:
775 : case VSTR_TYPE_FMT_INTMAX_T:
776 : case VSTR_TYPE_FMT_UINTMAX_T:
777 : case VSTR_TYPE_FMT_DOUBLE:
778 : case VSTR_TYPE_FMT_DOUBLE_LONG:
779 239033 : usr_spec->data_ptr[scan] = &spec->u;
780 239033 : break;
781 : case VSTR_TYPE_FMT_PTRDIFF_T:
782 48 : ASSERT(spec->data_t == (ptrdiff_t)spec->u.data_m);
783 48 : usr_spec->data_ptr[scan] = &spec->data_t;
784 48 : break;
785 :
786 : case VSTR_TYPE_FMT_PTR_VOID:
787 : case VSTR_TYPE_FMT_PTR_CHAR:
788 : case VSTR_TYPE_FMT_PTR_WCHAR_T:
789 : case VSTR_TYPE_FMT_PTR_SIGNED_CHAR:
790 : case VSTR_TYPE_FMT_PTR_SHORT:
791 : case VSTR_TYPE_FMT_PTR_INT:
792 : case VSTR_TYPE_FMT_PTR_LONG:
793 : case VSTR_TYPE_FMT_PTR_LONG_LONG:
794 : case VSTR_TYPE_FMT_PTR_SSIZE_T:
795 : case VSTR_TYPE_FMT_PTR_PTRDIFF_T:
796 : case VSTR_TYPE_FMT_PTR_INTMAX_T:
797 162029 : usr_spec->data_ptr[scan] = spec->u.data_ptr;
798 162029 : break;
799 : case VSTR_TYPE_FMT_ERRNO:
800 10 : errno = sve_errno;
801 2 : ASSERT_NO_SWITCH_DEF();
802 : }
803 401120 : assert(spec->escape_usr && (scan ? !spec->usr_spec : !!spec->usr_spec));
804 :
805 401120 : ++scan;
806 401120 : last = spec;
807 401120 : spec = spec->next;
808 : }
809 210133 : assert(!spec || !spec->escape_usr || spec->usr_spec);
810 210133 : usr_spec->data_ptr[scan] = NULL;
811 :
812 210133 : if (!(*func)(base, base->len - pos_diff, usr_spec))
813 23531 : last = NULL;
814 :
815 210133 : if (sz > VSTR__FMT_USR_SZ)
816 19896 : VSTR__F(usr_spec);
817 :
818 210133 : return (last);
819 : }
820 :
821 : #define VSTR__FMT_N_PTR(x, y) \
822 : case x: do \
823 : { \
824 : y *len_curr = spec->u.data_ptr; \
825 : *len_curr = len; \
826 : } while (FALSE)
827 :
828 : static int vstr__fmt_write_spec(Vstr_base *base, size_t pos_diff,
829 : size_t orig_len, int sve_errno)
830 253483 : {
831 253483 : struct Vstr__fmt_spec *const beg = base->conf->vstr__fmt_spec_list_beg;
832 253483 : struct Vstr__fmt_spec *const end = base->conf->vstr__fmt_spec_list_end;
833 253483 : struct Vstr__fmt_spec * spec = base->conf->vstr__fmt_spec_list_beg;
834 :
835 253483 : if (!end) /* invalid format... */
836 22 : return (TRUE);
837 :
838 253461 : ASSERT(beg && end);
839 :
840 : /* allow vstr_add_vfmt() to be called form a usr cb */
841 253461 : base->conf->vstr__fmt_spec_list_beg = NULL;
842 253461 : base->conf->vstr__fmt_spec_list_end = NULL;
843 :
844 1475249 : while (spec)
845 : {
846 1253947 : if (spec->escape_usr)
847 : {
848 210182 : if (!(spec = vstr__add_fmt_usr_write_spec(base, orig_len,
849 : pos_diff, spec, sve_errno)))
850 23580 : goto failed_user;
851 : }
852 1043765 : else switch (spec->fmt_code)
853 : {
854 : case 'c':
855 5407 : if (!vstr__add_fmt_char(base, pos_diff, spec))
856 28 : goto failed_alloc;
857 283 : break;
858 :
859 : case 'C':
860 283 : if (!vstr__add_fmt_wide_char(base, pos_diff, spec))
861 60 : goto failed_alloc;
862 19 : break;
863 :
864 : case 'm':
865 19 : spec->u.data_ptr = strerror(sve_errno);
866 : case 's':
867 697613 : if (!vstr__add_fmt_cstr(base, pos_diff, spec))
868 4228 : goto failed_alloc;
869 361 : break;
870 :
871 : case 'S':
872 361 : if (!vstr__add_fmt_wide_cstr(base, pos_diff, spec))
873 63 : goto failed_alloc;
874 123388 : break;
875 :
876 : case 'p': /* convert ptr to unsigned long and print */
877 : {
878 123388 : assert(spec->int_type == VSTR_TYPE_FMT_PTR_VOID);
879 123388 : assert(!(spec->flags & SIGN));
880 : }
881 : /* FALLTHROUGH */
882 : case 'd':
883 : case 'i':
884 : case 'u':
885 : case 'X':
886 : case 'x':
887 : case 'o':
888 167655 : if (!vstr__add_fmt_number(base, pos_diff, spec))
889 495 : goto failed_alloc;
890 27360 : break;
891 :
892 : case 'n':
893 : {
894 27360 : size_t len = (base->len - orig_len);
895 :
896 27360 : switch (spec->int_type)
897 : {
898 10 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_UCHAR, signed char); break;
899 10 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_USHORT, short); break;
900 27290 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_UINT, int); break;
901 10 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_ULONG, long); break;
902 10 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_ULONG_LONG, Vstr__long_long); break;
903 10 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_SIZE_T, ssize_t); break;
904 10 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_UINTMAX_T, intmax_t); break;
905 10 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_PTRDIFF_T, ptrdiff_t);
906 10 : ASSERT_NO_SWITCH_DEF();
907 : }
908 : }
909 145086 : break;
910 :
911 : case 'a':
912 : case 'A':
913 : case 'e':
914 : case 'E':
915 : case 'f':
916 : case 'F':
917 : case 'g':
918 : case 'G':
919 145086 : assert(VSTR__FMT_ANAL_ZERO &&
920 : (spec->field_width_usr || !spec->field_width));
921 145086 : assert(VSTR__FMT_ANAL_ZERO &&
922 : ((spec->flags & PREC_USR) || !spec->precision));
923 145086 : if (!vstr__add_fmt_dbl(base, pos_diff, spec))
924 3705 : goto failed_alloc;
925 911552 : ASSERT_NO_SWITCH_DEF();
926 : }
927 :
928 1221788 : spec = spec->next;
929 : }
930 :
931 221302 : end->next = base->conf->vstr__fmt_spec_make;
932 221302 : base->conf->vstr__fmt_spec_make = beg;
933 :
934 221302 : return (TRUE);
935 :
936 : failed_alloc:
937 : failed_user:
938 32159 : end->next = base->conf->vstr__fmt_spec_make;
939 32159 : base->conf->vstr__fmt_spec_make = beg;
940 :
941 32159 : return (FALSE);
942 : }
943 :
944 : #undef VSTR__FMT_N_PTR
945 : #define VSTR__FMT_N_PTR(x, y) \
946 : case x: \
947 : u.data_ptr = va_arg(ap, y *)
948 :
949 : #define VSTR__FMT_ARG_NUM(sT, asT, auT, memb) \
950 : if (spec->flags & SIGN) \
951 : { \
952 : sT tmp = va_arg(ap, asT); \
953 : VSTR__FMT_ABS_NUM(u.memb, tmp); \
954 : } \
955 : else \
956 : u.memb = va_arg(ap, auT); \
957 : if (!u.memb) spec->flags |= NUM_IS_ZERO; \
958 : break
959 :
960 : static int vstr__fmt_fillin_spec(Vstr_conf *conf, va_list ap, int have_dollars)
961 253487 : {
962 253487 : struct Vstr__fmt_spec *beg = conf->vstr__fmt_spec_list_beg;
963 253487 : unsigned int count = 0;
964 253487 : unsigned int need_to_fin_now = FALSE;
965 :
966 1422676 : while (beg)
967 : {
968 1169193 : struct Vstr__fmt_spec *spec = NULL;
969 1169193 : int done = FALSE;
970 1169193 : union Vstr__fmt_sp_un u;
971 :
972 1169193 : ++count;
973 2228538 : ASSERT(beg);
974 :
975 2681799 : while (beg &&
976 : !beg->field_width_param &&
977 : !beg->precision_param &&
978 : !beg->main_param)
979 1512606 : beg = beg->next;
980 :
981 1169193 : ASSERT_RET(!(beg && need_to_fin_now), FALSE); /* incomplete spec */
982 :
983 1169189 : spec = beg;
984 1186158 : while (spec)
985 : {
986 927836 : if (count == spec->field_width_param)
987 : {
988 18152 : int tmp_fw_p = 0;
989 18152 : if (!done) u.data_i = va_arg(ap, int);
990 18152 : done = TRUE;
991 :
992 18152 : assert(spec->field_width_usr);
993 :
994 18152 : tmp_fw_p = u.data_i;
995 18152 : if (tmp_fw_p < 0) /* negative field width == flag '-' */
996 : {
997 812 : spec->flags |= LEFT;
998 812 : VSTR__FMT_S2U_NUM(spec->field_width, tmp_fw_p);
999 : }
1000 : else
1001 17340 : spec->field_width = tmp_fw_p;
1002 :
1003 18152 : spec->field_width_param = 0;
1004 18152 : if (!have_dollars)
1005 15064 : break;
1006 : }
1007 :
1008 912772 : if (count == spec->precision_param)
1009 : {
1010 472 : int tmp_fw_p = 0;
1011 472 : if (!done) u.data_i = va_arg(ap, int);
1012 472 : done = TRUE;
1013 :
1014 472 : assert(spec->flags & PREC_USR);
1015 :
1016 472 : tmp_fw_p = u.data_i;
1017 472 : if (tmp_fw_p < 0) /* negative precision == pretend one wasn't given */
1018 20 : spec->flags &= ~PREC_USR;
1019 : else
1020 452 : spec->precision = tmp_fw_p;
1021 472 : spec->precision_param = 0;
1022 472 : if (!have_dollars)
1023 442 : break;
1024 : }
1025 :
1026 912330 : if (count == spec->main_param)
1027 : {
1028 898909 : if (!done)
1029 898634 : switch (spec->fmt_code)
1030 : {
1031 : case 'c':
1032 5432 : u.data_c = va_arg(ap, int);
1033 5432 : break;
1034 :
1035 : case 'C':
1036 477 : u.data_wint = va_arg(ap, wint_t);
1037 477 : break;
1038 :
1039 : case 'm':
1040 125227 : break;
1041 :
1042 : case 's':
1043 125227 : u.data_ptr = va_arg(ap, char *);
1044 125227 : break;
1045 :
1046 : case 'S':
1047 610 : u.data_ptr = va_arg(ap, wchar_t *);
1048 610 : break;
1049 :
1050 : case 'd':
1051 : case 'i':
1052 : case 'u':
1053 : case 'X':
1054 : case 'x':
1055 : case 'o':
1056 411641 : switch (spec->int_type)
1057 : {
1058 : case VSTR_TYPE_FMT_UCHAR:
1059 192 : VSTR__FMT_ARG_NUM(signed char, int, unsigned int, data_c);
1060 : case VSTR_TYPE_FMT_USHORT:
1061 182 : VSTR__FMT_ARG_NUM(signed short, int, unsigned int, data_s);
1062 : case VSTR_TYPE_FMT_UINT:
1063 138227 : VSTR__FMT_ARG_NUM(int, int, unsigned int, data_i);
1064 : case VSTR_TYPE_FMT_ULONG:
1065 18267 : VSTR__FMT_ARG_NUM(long, long, unsigned long, data_l);
1066 : case VSTR_TYPE_FMT_ULONG_LONG:
1067 209 : VSTR__FMT_ARG_NUM(Vstr__long_long, Vstr__long_long,
1068 : Vstr__unsigned_long_long, data_L);
1069 : case VSTR_TYPE_FMT_SIZE_T:
1070 148423 : VSTR__FMT_ARG_NUM(ssize_t, ssize_t, size_t, data_sz);
1071 : case VSTR_TYPE_FMT_UINTMAX_T:
1072 105995 : VSTR__FMT_ARG_NUM(intmax_t, intmax_t, uintmax_t, data_m);
1073 :
1074 : case VSTR_TYPE_FMT_PTRDIFF_T:
1075 146 : if (1)
1076 : { /* no unsigned type ... */
1077 : /* FIXME: volatile for bug in gcc-2.95.x */
1078 146 : volatile ptrdiff_t ttmp = va_arg(ap, ptrdiff_t);
1079 146 : volatile intmax_t jtmp = ttmp;
1080 :
1081 146 : spec->data_t = ttmp;
1082 146 : VSTR__FMT_ABS_NUM(u.data_m, jtmp);
1083 : }
1084 146 : if (!u.data_m) spec->flags |= NUM_IS_ZERO;
1085 146 : spec->int_type = VSTR_TYPE_FMT_UINTMAX_T;
1086 :
1087 146 : ASSERT_NO_SWITCH_DEF();
1088 : }
1089 152064 : break;
1090 :
1091 : case 'p':
1092 152064 : u.data_ptr = va_arg(ap, void *);
1093 152064 : break;
1094 :
1095 : case 'n':
1096 41632 : switch (spec->int_type)
1097 : {
1098 59 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_UCHAR, signed char); break;
1099 59 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_USHORT, short); break;
1100 41219 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_UINT, int); break;
1101 59 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_ULONG, long); break;
1102 59 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_ULONG_LONG, Vstr__long_long);
1103 59 : break;
1104 59 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_SIZE_T, ssize_t); break;
1105 59 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_UINTMAX_T, intmax_t); break;
1106 59 : VSTR__FMT_N_PTR(VSTR_TYPE_FMT_PTRDIFF_T, ptrdiff_t);
1107 59 : ASSERT_NO_SWITCH_DEF();
1108 : }
1109 161532 : break;
1110 :
1111 : case 'a': /* print like [-]x.xxxpxx -- in hex using abcdef */
1112 : case 'A': /* print like [-]x.xxxPxx -- in hex using ABCDEF */
1113 : case 'e': /* print like [-]x.xxxexx */
1114 : case 'E': /* use big E instead */
1115 : case 'f': /* print like an int */
1116 : case 'F': /* print like an int - upper case infinity/nan */
1117 : case 'g': /* use the smallest of e and f */
1118 : case 'G': /* use the smallest of E and F */
1119 161532 : if (spec->int_type == VSTR_TYPE_FMT_ULONG_LONG)
1120 65846 : u.data_Ld = va_arg(ap, long double);
1121 : else
1122 95686 : u.data_d = va_arg(ap, double);
1123 :
1124 46384 : ASSERT_NO_SWITCH_DEF();
1125 : }
1126 898909 : done = TRUE;
1127 898909 : spec->u = u;
1128 898909 : spec->main_param = 0;
1129 :
1130 898909 : if (!have_dollars)
1131 895361 : break;
1132 : }
1133 :
1134 16969 : spec = spec->next;
1135 : }
1136 :
1137 1169189 : if (!done)
1138 253465 : need_to_fin_now = 1;
1139 : }
1140 :
1141 253483 : return (TRUE);
1142 : }
1143 : #undef VSTR__FMT_N_PTR
1144 : #undef VSTR__FMT_ARG_NUM
1145 :
1146 : static const char *vstr__add_fmt_usr_esc(Vstr_conf *conf,
1147 : const char *fmt,
1148 : struct Vstr__fmt_spec *spec,
1149 : unsigned int *passed_params)
1150 220465 : {
1151 220465 : unsigned int params = *passed_params;
1152 220465 : Vstr__fmt_usr_name_node *node = NULL;
1153 220465 : unsigned int have_i18n_args = spec->main_param;
1154 :
1155 220465 : if ((node = vstr__fmt_usr_match(conf, fmt)))
1156 : {
1157 215772 : unsigned int scan = 0;
1158 :
1159 215772 : spec->usr_spec = node;
1160 :
1161 410753 : while (TRUE)
1162 : {
1163 410753 : int have_arg = TRUE;
1164 :
1165 410753 : spec->escape_usr = TRUE;
1166 :
1167 410753 : switch (node->types[scan])
1168 : {
1169 : case VSTR_TYPE_FMT_END:
1170 35 : spec->fmt_code = 0;
1171 35 : have_arg = FALSE;
1172 : /* it's possible, but stupid, with i18n */
1173 35 : ASSERT(!scan && !spec->main_param);
1174 35 : spec->main_param = 0;
1175 35 : break;
1176 : case VSTR_TYPE_FMT_INT:
1177 538 : spec->flags |= SIGN;
1178 538 : spec->fmt_code = 'd';
1179 538 : break;
1180 : case VSTR_TYPE_FMT_UINT:
1181 44024 : spec->fmt_code = 'u';
1182 44024 : break;
1183 : case VSTR_TYPE_FMT_LONG:
1184 58 : spec->int_type = VSTR_TYPE_FMT_ULONG;
1185 58 : spec->flags |= SIGN;
1186 58 : spec->fmt_code = 'd';
1187 58 : break;
1188 : case VSTR_TYPE_FMT_ULONG:
1189 13065 : spec->int_type = VSTR_TYPE_FMT_ULONG;
1190 13065 : spec->fmt_code = 'u';
1191 13065 : break;
1192 : case VSTR_TYPE_FMT_LONG_LONG:
1193 57 : spec->int_type = VSTR_TYPE_FMT_ULONG_LONG;
1194 57 : spec->flags |= SIGN;
1195 57 : spec->fmt_code = 'd';
1196 57 : break;
1197 : case VSTR_TYPE_FMT_ULONG_LONG:
1198 53 : spec->int_type = VSTR_TYPE_FMT_ULONG_LONG;
1199 53 : spec->fmt_code = 'u';
1200 53 : break;
1201 : case VSTR_TYPE_FMT_SSIZE_T:
1202 56 : spec->int_type = VSTR_TYPE_FMT_SIZE_T;
1203 56 : spec->flags |= SIGN;
1204 56 : spec->fmt_code = 'd';
1205 56 : break;
1206 : case VSTR_TYPE_FMT_SIZE_T:
1207 148194 : spec->int_type = VSTR_TYPE_FMT_SIZE_T;
1208 148194 : spec->fmt_code = 'u';
1209 148194 : break;
1210 : case VSTR_TYPE_FMT_PTRDIFF_T:
1211 57 : spec->int_type = VSTR_TYPE_FMT_PTRDIFF_T;
1212 57 : spec->flags |= SIGN;
1213 57 : spec->fmt_code = 'd';
1214 57 : break;
1215 : case VSTR_TYPE_FMT_INTMAX_T:
1216 58 : spec->int_type = VSTR_TYPE_FMT_UINTMAX_T;
1217 58 : spec->flags |= SIGN;
1218 58 : spec->fmt_code = 'd';
1219 58 : break;
1220 : case VSTR_TYPE_FMT_UINTMAX_T:
1221 36972 : spec->int_type = VSTR_TYPE_FMT_UINTMAX_T;
1222 36972 : spec->fmt_code = 'u';
1223 36972 : break;
1224 : case VSTR_TYPE_FMT_DOUBLE:
1225 60 : spec->fmt_code = 'f';
1226 60 : break;
1227 : case VSTR_TYPE_FMT_DOUBLE_LONG:
1228 60 : spec->int_type = VSTR_TYPE_FMT_ULONG_LONG;
1229 60 : spec->fmt_code = 'f';
1230 60 : break;
1231 : case VSTR_TYPE_FMT_PTR_VOID:
1232 152149 : spec->fmt_code = 'p';
1233 152149 : break;
1234 : case VSTR_TYPE_FMT_PTR_CHAR:
1235 952 : spec->fmt_code = 's';
1236 952 : break;
1237 : case VSTR_TYPE_FMT_PTR_WCHAR_T:
1238 57 : spec->fmt_code = 'S';
1239 57 : break;
1240 : case VSTR_TYPE_FMT_ERRNO:
1241 : /* it's possible, but stupid, with i18n */
1242 10 : ASSERT(!spec->main_param);
1243 10 : spec->main_param = 0;
1244 10 : spec->fmt_code = 0;
1245 10 : have_arg = FALSE;
1246 10 : break;
1247 :
1248 : case VSTR_TYPE_FMT_PTR_SIGNED_CHAR:
1249 52 : spec->fmt_code = 'n'; spec->int_type = VSTR_TYPE_FMT_UCHAR;
1250 52 : break;
1251 : case VSTR_TYPE_FMT_PTR_SHORT:
1252 52 : spec->fmt_code = 'n'; spec->int_type = VSTR_TYPE_FMT_USHORT;
1253 52 : break;
1254 : case VSTR_TYPE_FMT_PTR_INT:
1255 13946 : spec->fmt_code = 'n'; spec->int_type = VSTR_TYPE_FMT_UINT;
1256 13946 : break;
1257 : case VSTR_TYPE_FMT_PTR_LONG:
1258 50 : spec->fmt_code = 'n'; spec->int_type = VSTR_TYPE_FMT_ULONG;
1259 50 : break;
1260 : case VSTR_TYPE_FMT_PTR_LONG_LONG:
1261 50 : spec->fmt_code = 'n'; spec->int_type = VSTR_TYPE_FMT_ULONG_LONG;
1262 50 : break;
1263 : case VSTR_TYPE_FMT_PTR_SSIZE_T:
1264 50 : spec->fmt_code = 'n'; spec->int_type = VSTR_TYPE_FMT_SIZE_T;
1265 50 : break;
1266 : case VSTR_TYPE_FMT_PTR_PTRDIFF_T:
1267 49 : spec->fmt_code = 'n'; spec->int_type = VSTR_TYPE_FMT_PTRDIFF_T;
1268 49 : break;
1269 : case VSTR_TYPE_FMT_PTR_INTMAX_T:
1270 49 : spec->fmt_code = 'n'; spec->int_type = VSTR_TYPE_FMT_UINTMAX_T;
1271 :
1272 43 : ASSERT_NO_SWITCH_DEF();
1273 : }
1274 :
1275 : /* custom formatters have multiple args, they _must_ follow each other
1276 : * so if the first is set, set the rest based off that */
1277 410753 : if (have_i18n_args && have_arg)
1278 1788 : spec->main_param = have_i18n_args++;
1279 410753 : VSTR__FMT_MV_SPEC(conf, have_arg);
1280 :
1281 : /* allow _END to be the only thing passed */
1282 410753 : if (!node->types[scan] || !node->types[++scan])
1283 215726 : break;
1284 :
1285 194992 : if (!conf->vstr__fmt_spec_make && !vstr__fmt_add_spec(conf))
1286 11 : goto failed_alloc;
1287 194981 : spec = conf->vstr__fmt_spec_make;
1288 194981 : vstr__fmt_init_spec(spec);
1289 : }
1290 :
1291 215761 : *passed_params = params;
1292 :
1293 215761 : return (fmt + node->name_len);
1294 : }
1295 :
1296 4693 : return (fmt);
1297 :
1298 : failed_alloc:
1299 11 : return (NULL);
1300 : }
1301 :
1302 : static const char *vstr__add_fmt_spec(const char *fmt,
1303 : struct Vstr__fmt_spec *spec,
1304 : unsigned int *params,
1305 : unsigned int *have_dollars)
1306 704268 : {
1307 704268 : int tmp_num = 0;
1308 :
1309 : /* get i18n param number */
1310 704268 : if (VSTR__ADD_FMT_ISDIGIT(*fmt) && (*fmt != '0'))
1311 : {
1312 37133 : tmp_num = VSTR__ADD_FMT_STRTOL(fmt);
1313 :
1314 37133 : if (*fmt != '$')
1315 33651 : goto use_field_width;
1316 :
1317 3482 : ++fmt;
1318 3482 : *have_dollars = TRUE;
1319 3482 : spec->main_param = tmp_num;
1320 : }
1321 :
1322 : /* process flags */
1323 862475 : while (TRUE)
1324 : {
1325 862475 : switch (*fmt)
1326 : {
1327 44242 : case '-': spec->flags |= LEFT; break;
1328 11339 : case '+': spec->flags |= PLUS; break;
1329 9423 : case ' ': spec->flags |= SPACE; break;
1330 38990 : case '#': spec->flags |= SPECIAL; break;
1331 69462 : case '0': spec->flags |= ZEROPAD; break;
1332 18337 : case '\'': spec->flags |= THOUSAND_SEP; break;
1333 65 : case 'I': spec->flags |= ALT_DIGITS; break;
1334 :
1335 : default:
1336 191858 : goto got_flags;
1337 : }
1338 191858 : ++fmt;
1339 : }
1340 : got_flags:
1341 :
1342 : /* get field width */
1343 670617 : if (VSTR__ADD_FMT_ISDIGIT(*fmt))
1344 : {
1345 108654 : tmp_num = VSTR__ADD_FMT_STRTOL(fmt);
1346 :
1347 : use_field_width:
1348 142305 : spec->field_width_usr = TRUE;
1349 142305 : spec->field_width = tmp_num;
1350 142305 : ASSERT(tmp_num >= 0);
1351 : }
1352 561963 : else if (*fmt == '*')
1353 : {
1354 18162 : const char *dollar_start = fmt;
1355 :
1356 18162 : spec->field_width_usr = TRUE;
1357 :
1358 18162 : ++fmt;
1359 18162 : tmp_num = 0;
1360 18162 : if (VSTR__ADD_FMT_ISDIGIT(*fmt))
1361 3088 : tmp_num = VSTR__ADD_FMT_STRTOL(fmt);
1362 :
1363 18162 : if (*fmt != '$')
1364 : {
1365 15074 : fmt = dollar_start + 1;
1366 15074 : spec->field_width_param = ++*params;
1367 : }
1368 : else
1369 : {
1370 3088 : ++fmt;
1371 3088 : spec->field_width_param = tmp_num;
1372 : }
1373 : }
1374 :
1375 : /* get the precision */
1376 704268 : if (*fmt == '.')
1377 : {
1378 79360 : spec->flags |= PREC_USR;
1379 :
1380 79360 : ++fmt;
1381 79360 : if (VSTR__ADD_FMT_ISDIGIT(*fmt))
1382 : {
1383 35750 : tmp_num = VSTR__ADD_FMT_STRTOL(fmt);
1384 :
1385 35750 : spec->precision = tmp_num;
1386 35750 : ASSERT(tmp_num >= 0);
1387 : }
1388 43610 : else if (*fmt == '*')
1389 : {
1390 472 : const char *dollar_start = fmt;
1391 472 : ++fmt;
1392 :
1393 472 : tmp_num = 0;
1394 472 : if (VSTR__ADD_FMT_ISDIGIT(*fmt))
1395 30 : tmp_num = VSTR__ADD_FMT_STRTOL(fmt);
1396 :
1397 472 : if (*fmt != '$')
1398 : {
1399 442 : fmt = dollar_start + 1;
1400 442 : spec->precision_param = ++*params;
1401 : }
1402 : else
1403 : {
1404 30 : ++fmt;
1405 30 : spec->precision_param = tmp_num;
1406 : }
1407 : }
1408 : }
1409 :
1410 704268 : return (fmt);
1411 : }
1412 :
1413 : static size_t vstr__add_vfmt(Vstr_base *base, size_t pos, unsigned int userfmt,
1414 : const char *fmt, va_list ap)
1415 253537 : {
1416 253537 : int sve_errno = errno;
1417 253537 : size_t start_pos = pos + 1;
1418 253537 : size_t orig_len = 0;
1419 253537 : size_t pos_diff = 0;
1420 253537 : unsigned int params = 0;
1421 253537 : unsigned int have_dollars = FALSE; /* have posix %2$d etc. stuff */
1422 253537 : unsigned char fmt_usr_escape = 0;
1423 253537 : unsigned int orig_malloc_bad = FALSE;
1424 :
1425 253537 : ASSERT_RET(!(!base || !fmt || (pos > base->len)), 0);
1426 :
1427 253529 : orig_len = base->len;
1428 :
1429 : /* so things can use this as a flag */
1430 253529 : orig_malloc_bad = base->conf->malloc_bad;
1431 253529 : base->conf->malloc_bad = FALSE;
1432 :
1433 : /* this will be correct as you add chars */
1434 253529 : pos_diff = base->len - pos;
1435 :
1436 253529 : if (userfmt)
1437 219209 : fmt_usr_escape = base->conf->fmt_usr_escape;
1438 :
1439 1571566 : while (*fmt)
1440 : {
1441 1318079 : const char *fmt_orig = fmt;
1442 1318079 : struct Vstr__fmt_spec *spec = NULL;
1443 :
1444 1318079 : if (!base->conf->vstr__fmt_spec_make && !vstr__fmt_add_spec(base->conf))
1445 31 : goto failed_alloc;
1446 :
1447 1318048 : spec = base->conf->vstr__fmt_spec_make;
1448 1318048 : vstr__fmt_init_spec(spec);
1449 :
1450 1318048 : assert(VSTR__FMT_ANAL_ZERO &&
1451 : (spec->field_width_usr || !spec->field_width));
1452 1318048 : assert(VSTR__FMT_ANAL_ZERO &&
1453 : ((spec->flags & PREC_USR) || !spec->precision));
1454 :
1455 1318048 : if ((*fmt != '%') && (*fmt != fmt_usr_escape))
1456 : {
1457 613720 : char *next_escape = strchr(fmt, '%');
1458 :
1459 613720 : spec->fmt_code = 's';
1460 613720 : spec->u.data_ptr = (char *)fmt;
1461 :
1462 613720 : if (fmt_usr_escape)
1463 : { /* find first of the two escapes */
1464 424416 : char *next_usr_escape = strchr(fmt, fmt_usr_escape);
1465 424416 : if (next_usr_escape &&
1466 : (!next_escape || (next_usr_escape < next_escape)))
1467 169903 : next_escape = next_usr_escape;
1468 : }
1469 :
1470 613720 : if (next_escape)
1471 : {
1472 465990 : size_t len = next_escape - fmt;
1473 465990 : spec->precision = len;
1474 465990 : spec->flags |= PREC_USR;
1475 465990 : fmt = fmt + len;
1476 : }
1477 : else
1478 147730 : fmt = "";
1479 :
1480 613720 : VSTR__FMT_MV_SPEC(base->conf, FALSE);
1481 :
1482 613720 : continue;
1483 : }
1484 :
1485 704328 : if (fmt[0] == fmt[1])
1486 : {
1487 60 : spec->u.data_c = fmt[0];
1488 60 : spec->fmt_code = 'c';
1489 60 : VSTR__FMT_MV_SPEC(base->conf, FALSE);
1490 60 : fmt += 2; /* skip escs */
1491 60 : continue;
1492 : }
1493 704268 : assert(fmt_orig == fmt);
1494 704268 : ++fmt; /* skip esc */
1495 :
1496 704268 : fmt = vstr__add_fmt_spec(fmt, spec, ¶ms, &have_dollars);
1497 :
1498 704268 : assert(VSTR__FMT_ANAL_ZERO &&
1499 : (spec->field_width_usr || !spec->field_width));
1500 704268 : assert(VSTR__FMT_ANAL_ZERO &&
1501 : ((spec->flags & PREC_USR) || !spec->precision));
1502 :
1503 704268 : if (fmt_usr_escape && (*fmt_orig == fmt_usr_escape))
1504 : {
1505 220465 : const char *fmt_end = vstr__add_fmt_usr_esc(base->conf, fmt,
1506 220465 : spec, ¶ms);
1507 :
1508 220465 : if (!fmt_end)
1509 11 : goto failed_alloc;
1510 220454 : else if (fmt_end == fmt)
1511 : {
1512 4693 : if (fmt_usr_escape == '%')
1513 4685 : goto vstr__fmt_sys_escapes;
1514 :
1515 8 : assert(FALSE); /* $$ etc. is already done before here */
1516 69554 : fmt = ""; /* should end because we don't know if the types are
1517 : * screwed up */
1518 : }
1519 : else
1520 215761 : fmt = fmt_end;
1521 215761 : continue;
1522 : }
1523 :
1524 : vstr__fmt_sys_escapes:
1525 : /* get width of type */
1526 488488 : switch (*fmt)
1527 : {
1528 : case 'h':
1529 399 : ++fmt;
1530 399 : if (*fmt == 'h')
1531 : {
1532 205 : ++fmt;
1533 205 : spec->int_type = VSTR_TYPE_FMT_UCHAR;
1534 : }
1535 : else
1536 194 : spec->int_type = VSTR_TYPE_FMT_USHORT;
1537 194 : break;
1538 : case 'l':
1539 6140 : ++fmt;
1540 6140 : if (*fmt == 'l')
1541 : {
1542 135 : ++fmt;
1543 135 : spec->int_type = VSTR_TYPE_FMT_ULONG_LONG;
1544 : }
1545 : else
1546 6005 : spec->int_type = VSTR_TYPE_FMT_ULONG;
1547 6005 : break;
1548 65807 : case 'L': ++fmt; spec->int_type = VSTR_TYPE_FMT_ULONG_LONG; break;
1549 210 : case 'z': ++fmt; spec->int_type = VSTR_TYPE_FMT_SIZE_T; break;
1550 115 : case 't': ++fmt; spec->int_type = VSTR_TYPE_FMT_PTRDIFF_T; break;
1551 69005 : case 'j': ++fmt; spec->int_type = VSTR_TYPE_FMT_UINTMAX_T; break;
1552 :
1553 : default:
1554 488488 : break;
1555 : }
1556 :
1557 488488 : spec->fmt_code = *fmt;
1558 488488 : switch (*fmt)
1559 : {
1560 : case 'c':
1561 5801 : if (spec->int_type != VSTR_TYPE_FMT_ULONG) /* %lc == %C */
1562 5440 : break;
1563 : /* FALL THROUGH */
1564 :
1565 : case 'C':
1566 479 : spec->fmt_code = 'C';
1567 479 : break;
1568 :
1569 : case 'm':
1570 19 : ASSERT(!spec->main_param); /* it's possible, but stupid, with i18n */
1571 19 : spec->main_param = 0;
1572 19 : break;
1573 :
1574 : case 's':
1575 124746 : if (spec->int_type != VSTR_TYPE_FMT_ULONG) /* %ls == %S */
1576 124291 : break;
1577 : /* FALL THROUGH */
1578 :
1579 : case 'S':
1580 569 : spec->fmt_code = 'S';
1581 569 : break;
1582 :
1583 : case 'd':
1584 : case 'i':
1585 25687 : spec->flags |= SIGN;
1586 : case 'u':
1587 25687 : break;
1588 :
1589 : case 'X':
1590 13223 : spec->flags |= LARGE;
1591 : case 'x':
1592 26562 : spec->num_base = 16;
1593 26562 : break;
1594 :
1595 : case 'o':
1596 13157 : spec->num_base = 8;
1597 13157 : break;
1598 :
1599 : case 'p':
1600 5 : spec->num_base = 16;
1601 5 : spec->int_type = VSTR_TYPE_FMT_PTR_VOID;
1602 5 : spec->flags |= SPECIAL;
1603 5 : break;
1604 :
1605 : case 'n':
1606 80659 : break;
1607 :
1608 : case 'A': /* print like [-]x.xxxPxx -- in hex using ABCDEF */
1609 : case 'E': /* use big E instead */
1610 : case 'F': /* print like an int - upper case infinity/nan */
1611 : case 'G': /* use the smallest of E and F */
1612 80659 : spec->flags |= LARGE;
1613 : case 'a': /* print like [-]x.xxxpxx -- in hex using abcdef */
1614 : case 'e': /* print like [-]x.xxxexx */
1615 : case 'f': /* print like an int */
1616 : case 'g': /* use the smallest of e and f */
1617 80659 : break;
1618 :
1619 : default:
1620 4 : assert(FALSE);
1621 151457 : fmt = "";
1622 151457 : continue;
1623 : }
1624 :
1625 488484 : VSTR__FMT_MV_SPEC(base->conf, TRUE);
1626 488484 : ++fmt;
1627 : }
1628 :
1629 253487 : if (!vstr__fmt_fillin_spec(base->conf, ap, have_dollars))
1630 4 : goto failed_alloc;
1631 :
1632 253483 : errno = sve_errno;
1633 253483 : if (!vstr__fmt_write_spec(base, pos_diff, orig_len, sve_errno))
1634 32159 : goto failed_write_spec;
1635 :
1636 : /* restore the original state of the bad malloc flag... */
1637 221324 : base->conf->malloc_bad = orig_malloc_bad;
1638 :
1639 221324 : return (base->len - orig_len);
1640 :
1641 : failed_write_spec:
1642 :
1643 32159 : if (base->len - orig_len)
1644 20107 : vstr_del(base, start_pos, base->len - orig_len);
1645 :
1646 20107 : if (0) /* already done in write_spec */
1647 : {
1648 : failed_alloc:
1649 46 : if (base->conf->vstr__fmt_spec_list_end)
1650 43 : base->conf->vstr__fmt_spec_list_end->next = base->conf->vstr__fmt_spec_make;
1651 46 : base->conf->vstr__fmt_spec_make = base->conf->vstr__fmt_spec_list_beg;
1652 46 : base->conf->vstr__fmt_spec_list_beg = NULL;
1653 46 : base->conf->vstr__fmt_spec_list_end = NULL;
1654 : }
1655 32205 : base->conf->malloc_bad |= orig_malloc_bad;
1656 :
1657 32205 : return (0);
1658 : }
1659 :
1660 : size_t vstr_add_vfmt(Vstr_base *base, size_t pos,
1661 : const char *fmt, va_list ap)
1662 80713 : {
1663 80713 : return (vstr__add_vfmt(base, pos, TRUE, fmt, ap));
1664 : }
1665 :
1666 : size_t vstr_add_vsysfmt(Vstr_base *base, size_t pos,
1667 : const char *fmt, va_list ap)
1668 5765 : {
1669 5765 : return (vstr__add_vfmt(base, pos, FALSE, fmt, ap));
1670 : }
1671 :
1672 : size_t vstr_add_fmt(Vstr_base *base, size_t pos, const char *fmt, ...)
1673 138504 : {
1674 138504 : size_t len = 0;
1675 138504 : va_list ap;
1676 :
1677 138504 : va_start(ap, fmt);
1678 :
1679 138504 : len = vstr__add_vfmt(base, pos, TRUE, fmt, ap);
1680 :
1681 138504 : va_end(ap);
1682 :
1683 138504 : return (len);
1684 : }
1685 :
1686 : size_t vstr_add_sysfmt(Vstr_base *base, size_t pos, const char *fmt, ...)
1687 28555 : {
1688 28555 : size_t len = 0;
1689 28555 : va_list ap;
1690 :
1691 28555 : va_start(ap, fmt);
1692 :
1693 28555 : len = vstr__add_vfmt(base, pos, FALSE, fmt, ap);
1694 :
1695 28555 : va_end(ap);
1696 :
1697 28555 : return (len);
1698 : }
1699 :
|