Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
printf.cpp
Go to the documentation of this file.
1
29// expand _TEST_EXPORT macros
30#define _RWSTD_TEST_SRC
31#include "fmt_defs.h"
32
33#include <rw_printf.h>
34#include <rw_environ.h>
35
36#include <ctype.h> // for isalpha(), ...
37#include <errno.h> // for errno, errno constants
38#include <float.h> // for floating point macros
39#include <locale.h>
40#include <signal.h> // for signal constant
41#include <stdarg.h> // for va_list, va_start, ...
42#include <stddef.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <time.h> // for struct tm
47
48#ifndef _RWSTD_NO_WCHAR_H
49# include <wchar.h>
50#endif // _RWSTD_NO_WCHAR_H
51
52#ifndef _RWSTD_NO_WCTYPE_H
53# include <wctype.h> // for iswalpha(), ...
54#endif // _RWSTD_NO_WCTYPE_H
55
56#if defined (_WIN32)
57# include <windows.h> // for GetLastError(), OutputDebugString()
58#else
59# include <dlfcn.h>
60#endif // _WIN32
61
62#include <ios>
63#include <iostream>
64#include <locale>
65#include <string>
66
67#ifndef WEOF
68# define WEOF -1
69#endif // WEOF
70
71
73
75 __rw_memattr (const void*, size_t, int);
76
77}
78
79
81static const union {
82 int ival;
83 UChar bytes [sizeof (int)];
84} _rw_one = { 1 };
85
86static const int
87_rw_big_endian = '\0' == _rw_one.bytes [0];
88
89
90
93_rw_fmtlong (const FmtSpec&, Buffer&, long);
94
95#ifdef _RWSTD_LONG_LONG
96
98_rw_fmtllong (const FmtSpec&, Buffer&, _RWSTD_LONG_LONG);
99
100#endif // _RWSTD_LONG_LONG
101
102struct VarArgs {
103 union {
104 va_list *pva_;
105 void *pargs_;
107
108 enum {
111};
112
113#define VA_ARG(va, T) \
114 (VarArgs::VA_list == (va).kind_ ? va_arg (*(va).arg_.pva_, T) \
115 : *((T*&)(va).arg_.pargs_)++)
116
117
118static int
119_rw_fmtinteger (FmtSpec*, size_t, Buffer&, VarArgs*);
120
121static int
122_rw_fmtfloating (const FmtSpec&, Buffer&, const void*);
123
125_rw_fmtptr (const FmtSpec&, Buffer&, const void*);
126
127// format an invalid address
129_rw_fmtbadaddr (const FmtSpec&, Buffer&, const void*, size_t = 0);
130
131static int
132_rw_fmtfunptr (const FmtSpec&, Buffer&, funptr_t);
133
134static int
135_rw_fmtmemptr (const FmtSpec&, Buffer&, memptr_t);
136
137static int
138_rw_fmtchr (const FmtSpec&, Buffer&, int, int);
139
140static int
141_rw_fmtwchr (const FmtSpec&, Buffer&, wint_t, int);
142
143static int
144_rw_fmtwstr (const FmtSpec&, Buffer&, const wchar_t*, size_t);
145
146// format an argv-style (null-terminated) array of character strings
147static int
148_rw_fmtstrarray (FmtSpec*, size_t, Buffer&, const char* const*);
149
150// format struct tm
151static int
152_rw_fmttm (const FmtSpec&, Buffer&, const tm*);
153
154// format an expression (parameter/environment variable)
155static int
156_rw_fmtexpr (FmtSpec&, Buffer&, VarArgs*);
157
158// format a nested parametrized directive (%{@}, %{*.*@}, etc.)
159static int
160_rw_fmtnested (const FmtSpec&, Buffer&, const char*, VarArgs*);
161
162// format an extension
163static int
164_rw_vasnprintf_ext (FmtSpec*, size_t, Buffer&, const char*, VarArgs*);
165
166
168typedef int _rw_usr_cb_t (char**, size_t*, const char*, ...);
169
170static _rw_usr_cb_t* _rw_usr_fun [32];
171static size_t _rw_usr_inx;
172
173
175/*
176
177 C99 format specifier:
178
179 % flags width [ . prec ] mod cvtspec
180
181
182 GNU glibc format specifier:
183
184 % [ paramno $] flags width [ . prec ] mod cvtspec
185 or
186 % [ paramno $] flags width . * [ paramno $] mod cvtspec
187
188
189 Extended format specifier:
190
191 % { [ paramno $] [ flags ] [ width [. prec ]] mod cvtspec }
192 or
193 % { [ paramno $] [ flags ] [@ base [. width [. prec ]]] mod cvtspec }
194 or
195 % { $ envvar }
196
197
198*/
199
200static int
201_rw_fmtspec (FmtSpec *pspec, bool ext, const char *fmt, VarArgs *pva)
202{
203 RW_ASSERT (0 != pspec);
204 RW_ASSERT (0 != fmt);
205 RW_ASSERT (0 != pva);
206
207 memset (pspec, 0, sizeof *pspec);
208
209 pspec->iwidth = -1; // integer width not specified
210 pspec->width = -1; // width not specified
211 pspec->prec = -1; // precision not specified
212 pspec->base = -1; // base not specified
213 pspec->paramno = -1; // paramno not specified
214
215 const char* const fmtbeg = fmt;
216
217 if (ext) {
218 if ('$' == *fmt && '}' != fmt [1]) {
219
220 // %{$<string>}: introduces the name of an environment
221 // variable (or parameter)
222
223 ++fmt;
224
225 const char *str = 0;
226 size_t len = 0;
227
228 const char* const end = strchr (fmt, '}');
229
230 if (end) {
231 len = end - fmt;
232 str = fmt;
233 fmt = end;
234 }
235
236 if (str) {
237 char* const tmp = (char*)malloc (len + 1);
238 if (tmp) {
239 memcpy (tmp, str, len);
240 pspec->strarg = tmp;
241 pspec->strarg [len] = '\0';
242
243 return int (fmt - fmtbeg);
244 }
245
246 return -1;
247 }
248 }
249 }
250
251 // extension: extract the parameter number (if followed by '$')
252 if ('1' <= *fmt && *fmt <= '9') {
253 char *end;
254 const long tmp = strtol (fmt, &end, 10);
255
256 fmt = end;
257
258 if ('$' == *fmt) {
259 pspec->paramno = tmp;
260 ++fmt;
261 }
262 else {
263 // when not followed by '$' interpret the number
264 // as the width (i.e., that follows the empty set
265 // of flags)
266 pspec->width = int (tmp);
267 }
268 }
269
270 if (-1 == pspec->width) {
271 // extract any number of optional flags
272 for ( ; ; ++fmt) {
273 switch (*fmt) {
274 case '-': pspec->fl_minus = true; continue;
275 case '+': pspec->fl_plus = true; continue;
276 case '#': pspec->fl_pound = true; continue;
277 case ' ': pspec->fl_space = true; continue;
278 case '0': pspec->fl_zero = true; continue;
279 }
280 break;
281 }
282 }
283
284 if (ext && '@' == *fmt && '}' != fmt [1]) {
285 // @<decimal-number>
286
287 ++fmt;
288
289 // extract the numerical base
290 if ('0' <= fmt [1] && fmt [1] <= '9') {
291 char *end;
292 pspec->base = int (strtol (fmt, &end, 10));
293 fmt = end;
294 }
295 else if ('*' == *fmt) {
296 pspec->base = VA_ARG (*pva, int);
297 ++fmt;
298 }
299
300 if ('.' == *fmt)
301 ++fmt;
302 }
303
304 if (-1 == pspec->width) {
305 // extract the optional width
306 if ('0' <= *fmt && *fmt <= '9') {
307 char *end;
308 pspec->width = int (strtol (fmt, &end, 10));
309 fmt = end;
310 }
311 else if ('*' == *fmt) {
312 pspec->width = VA_ARG (*pva, int);
313
314 if (pspec->width < 0) {
315 // 7.19.6.1, p5 of ISO/IEC 9899:1999:
316 // A negative field width argument is taken
317 // as a - flag followed by a positive field width.
318 pspec->width = -pspec->width;
319 pspec->fl_minus = true;
320 }
321
322 ++fmt;
323 }
324 }
325
326 // extract the optional precision
327 if ('.' == *fmt) {
328
329 ++fmt;
330
331 if ('0' <= *fmt && *fmt <= '9') {
332 char *end;
333 pspec->prec = int (strtol (fmt, &end, 10));
334 fmt = end;
335 }
336 else if ('*' == *fmt) {
337 pspec->prec = VA_ARG (*pva, int);
338 ++fmt;
339 }
340 }
341
342 // extract an optional modifier but avoid misinterpreting
343 // a formatting specifier as a modifier when extensions
344 // are enabled; e.g., treat the 't' in %{t} as a specifier
345 // and not a modifier for a non-existent specifier
346 const char mod = !ext || '}' != fmt [1] ? *fmt : '\0';
347
348 switch (mod) {
349 case 'A':
350 if (ext) {
351 ++fmt;
352 pspec->mod = pspec->mod_ext_A;
353 break;
354 }
355 // fall thru
356
357 case 'h':
358 if ('h' == fmt [1] && '}' != fmt [2]) {
359 pspec->mod = pspec->mod_hh;
360 fmt += 2;
361 }
362 else if ('}' != fmt [1]) {
363 pspec->mod = pspec->mod_h;
364 ++fmt;
365 }
366 break;
367
368 case 'l':
369 if ('l' == fmt [1] && '}' != fmt [2]) {
370 pspec->mod = pspec->mod_ll;
371 fmt += 2;
372 }
373 else if ('}' != fmt [1]) {
374 pspec->mod = pspec->mod_l;
375 ++fmt;
376 }
377 break;
378
379 case 'j':
380 pspec->mod = pspec->mod_j;
381 ++fmt;
382 break;
383
384 case 'z':
385 pspec->mod = pspec->mod_z;
386 ++fmt;
387 break;
388
389 case 't':
390 pspec->mod = pspec->mod_t;
391 ++fmt;
392 break;
393
394 case 'L':
395 pspec->mod = pspec->mod_L;
396 ++fmt;
397 break;
398
399 case 'I':
400 if (ext) {
401
402 ++fmt;
403
404 if ('8' == *fmt) {
405 pspec->iwidth = 1;
406 ++fmt;
407 }
408 else if ('1' == fmt [0] && '6' == fmt [1]) {
409 pspec->iwidth = 2;
410 fmt += 2;
411 }
412 else if ('3' == fmt [0] && '2' == fmt [1]) {
413 pspec->iwidth = 3;
414 fmt += 2;
415 }
416 else if ('6' == fmt [0] && '4' == fmt [1]) {
417 pspec->iwidth = 4;
418 fmt += 2;
419 }
420 else {
421 pspec->mod = pspec->mod_ext_I;
422 }
423 break;
424 }
425 }
426
427 pspec->cvtspec = *fmt;
428
429 if (pspec->cvtspec)
430 ++fmt;
431
432 return int (fmt - fmtbeg);
433}
434
435
437_RWSTD_INTERNAL char*
438_rw_bufcat (Buffer &buf, const char *str, size_t len)
439{
440 RW_ASSERT (0 != buf.pbuf);
441 RW_ASSERT (0 != buf.pbufsize);
442
443 size_t buflen = buf.endoff;
444 const size_t bufree = *buf.pbuf ? *buf.pbufsize - buflen : 0;
445
446 if (bufree < len || !*buf.pbuf) {
447
448 // for guard block
449 static const char guard[] = "\xde\xad\xbe\xef";
450
451 const size_t guardsize = sizeof guard - 1;
452
453 size_t newbufsize = *buf.pbufsize * 2;
454 if (newbufsize < 16)
455 newbufsize = 16;
456
457 const size_t requiredsize = buflen + len + 1;
458 if (newbufsize < requiredsize)
459 newbufsize = requiredsize * 2;
460
461 // prevent buffer size from exceeding the maximum
462 if (buf.maxsize < newbufsize)
463 newbufsize = buf.maxsize;
464
465 if (newbufsize < requiredsize) {
466#ifdef ENOMEM
467 errno = ENOMEM;
468#endif // ENOMEM
469 return 0;
470 }
471
472 char* const newbuf = (char*)malloc (newbufsize + guardsize);
473
474 // return 0 on failure to allocate, let caller deal with it
475 if (0 == newbuf)
476 return 0;
477
478 memcpy (newbuf, *buf.pbuf, buflen);
479
480 // append a guard block to the end of the buffer
481 memcpy (newbuf + newbufsize, guard, guardsize);
482
483 if (*buf.pbuf) {
484 // verify that we didn't write past the end of the buffer and
485 // that the user didn't pass a local or static buffer without
486 // a maxsize format specifier.
487 RW_ASSERT (0 == memcmp (*buf.pbuf + *buf.pbufsize,
488 guard, guardsize));
489 free (*buf.pbuf);
490 }
491
492 *buf.pbuf = newbuf;
493 *buf.pbufsize = newbufsize;
494 }
495
496 if (0 != str) {
497 memcpy (*buf.pbuf + buflen, str, len);
498 buflen += len;
499 }
500
501 buf.endoff = buflen;
502
503 return *buf.pbuf + buflen;
504}
505
506
508static int
509_rw_vasnprintf_c99 (FmtSpec *pspec, // array of processed parameters
510 size_t paramno, // index of current parameter
511 Buffer &buf,
512 VarArgs *pva) // parameter list
513{
514 _RWSTD_UNUSED (paramno);
515
516 RW_ASSERT (0 != pspec);
517
518 int len = -1;
519
520 FmtSpec &spec = pspec [paramno];
521
522// retreve paramater either from the argument list (or array),
523// or from the array of previously saved parameters in the case
524// of a positional parameter
525#define PARAM(name, pva) \
526 (0 < spec.paramno \
527 ? pspec [spec.paramno - 1].param.name \
528 : VA_ARG (*pva, FmtSpec::name ## t))
529
530 switch (spec.cvtspec) {
531
532 case 'b': // extension: bool
533 case 'd':
534 case 'i':
535 case 'o':
536 case 'x':
537 case 'X':
538 case 'u':
539 len = _rw_fmtinteger (pspec, paramno, buf, pva);
540 break;
541
542 case 'e':
543 case 'E':
544 case 'f':
545 case 'F':
546 case 'g':
547 case 'G':
548 if (spec.mod == spec.mod_L) {
549 spec.param.ldbl_ = PARAM (ldbl_, pva);
550 len = _rw_fmtfloating (spec, buf, &spec.param.ldbl_);
551 }
552 else {
553 spec.param.dbl_ = PARAM (dbl_, pva);
554 len = _rw_fmtfloating (spec, buf, &spec.param.dbl_);
555 }
556 break;
557
558 case 'a':
559 RW_ASSERT (!"%a not implemented");
560 break;
561
562 case 'A':
563 RW_ASSERT (!"%A not implemented");
564 break;
565
566 case 'c':
567 // If no l length modifier is present, the int argument is converted
568 // to an unsigned char, and the resulting character is written. If
569 // an l length modifier is present, the wint_t argument is converted
570 // as if by an ls conversion specification with no precision and an
571 // argument that points to the initial element of a two-element array
572 // of wchar_t, the first element containing the wint_t argument to
573 // the lc conversion specification and the second a null wide
574 // character.
575 if (spec.mod == spec.mod_l) {
576 // wint_t argument formatted as wchar_t (no escapes)
577 spec.param.wint_ = PARAM (wint_, pva);
578 len = _rw_fmtwchr (spec, buf, spec.param.wint_, 1);
579 }
580 else {
581 // int argument formatted as char (no escapes)
582 spec.param.int_ = PARAM (int_, pva);
583 len = _rw_fmtchr (spec, buf, spec.param.int_, 1);
584 }
585 break;
586
587 case 's':
588 if (spec.mod == spec.mod_l) {
589 spec.param.ptr_ = PARAM (ptr_, pva);
590 const wchar_t* const str = (wchar_t*)spec.param.ptr_;
591 len = _rw_fmtwstr (spec, buf, str, _RWSTD_SIZE_MAX);
592 }
593 else {
594 spec.param.ptr_ = PARAM (ptr_, pva);
595 const char* const str = (char*)spec.param.ptr_;
596 len = _rw_fmtstr (spec, buf, str, _RWSTD_SIZE_MAX);
597 }
598 break;
599
600 case 'p': {
601 // The argument shall be a pointer to void. The value of the pointer
602 // is converted to a sequence of printing characters, in an
603 // implementation-defined manner.
604 spec.param.ptr_ = PARAM (ptr_, pva);
605 len = _rw_fmtptr (spec, buf, spec.param.ptr_);
606 break;
607 }
608
609 case 'm': { // %m (popular extension)
610 spec.param.int_ = errno;
611 len = _rw_fmterrno (spec, buf, spec.param.int_);
612 break;
613 }
614
615 case 'n': {
616 // The argument shall be a pointer to signed integer into which
617 // is written the number of characters written to the output
618 // stream so far by this call to fprintf. No argument is converted,
619 // but one is consumed. If the conversion specification includes
620 // any flags, a field width, or a precision, the behavior is
621 // undefined.
622
623 RW_ASSERT (0 != buf.pbuf);
624 RW_ASSERT (0 != *buf.pbuf);
625
626 // len = int (strlen (*buf.pbuf));
627 len = int (buf.endoff);
628
629 spec.param.ptr_ = PARAM (ptr_, pva);
630
631 if (spec.mod == spec.mod_hh) {
632 UChar* const ptr = (UChar*)spec.param.ptr_;
633
634 RW_ASSERT (0 != ptr);
635
636 *ptr = len;
637 }
638 else if (spec.mod == spec.mod_h) {
639 short* const ptr = (short*)spec.param.ptr_;
640
641 RW_ASSERT (0 != ptr);
642
643 *ptr = len;
644 }
645 else if (spec.mod == spec.mod_L) {
646#ifdef _RWSTD_LONG_LONG
648
649 RW_ASSERT (0 != ptr);
650
651 *ptr = short (len);
652#else // if !defined (_RWSTD_LONG_LONG)
653 RW_ASSERT (!"%Ln not implemented");
654#endif // _RWSTD_LONG_LONG
655 }
656 else if (spec.mod == spec.mod_l) {
657 long* const ptr = (long*)spec.param.ptr_;
658
659 RW_ASSERT (0 != ptr);
660
661 *ptr = long (unsigned (len));
662 }
663 else if (spec.mod == spec.mod_t) {
664 ptrdiff_t* const ptr = (ptrdiff_t*)spec.param.ptr_;
665
666 RW_ASSERT (0 != ptr);
667
668 *ptr = ptrdiff_t (unsigned (len));
669 }
670 else {
671 int* const ptr = (int*)spec.param.ptr_;
672
673 RW_ASSERT (0 != ptr);
674
675 *ptr = len;
676 }
677 break;
678 }
679
680 case '%':
681 break;
682 }
683
684 return len;
685}
686
687
689// implements rw_vasnprintf, but may be called recursively
690static int
691_rw_pvasnprintf (Buffer &buf, const char *fmt, VarArgs *pva)
692{
693 // save the length of the initial subsequence already
694 // in the buffer
695 const size_t begoff = buf.endoff;
696
697 // local buffer for a small number of conversion specifiers
698 // will grow dynamically if their number exceeds its capacity
699 FmtSpec specbuf [32];
700 FmtSpec *pspec = specbuf;
701
702 // local buffer for backtrack offsets implementing conditionals
703 int backtrack [64];
704 int nextoff = 0;
705
706 char fmtspec [64];
707
708 size_t spec_bufsize = sizeof specbuf / sizeof *specbuf;
709 size_t paramno = 0;
710
711 if (0 == fmt || 0 > _RW::__rw_memattr (fmt, _RWSTD_SIZE_MAX, -1))
712 return _rw_fmtbadaddr (pspec [0], buf, fmt);
713
714 for (const char *fc = fmt; *fc; ) {
715
716 const char* const pcnt = strchr (fc, '%');
717
718 size_t nchars = pcnt ? pcnt - fmt : strlen (fc);
719
720 if (0 == _rw_bufcat (buf, fmt, nchars))
721 goto fail;
722
723 RW_ASSERT (0 != *buf.pbuf);
724 RW_ASSERT (0 != buf.pbufsize);
725
726 if (0 == pcnt)
727 break;
728
729 fc = pcnt + 1;
730
731 if ('%' == *fc) {
732 // handle "%%"
733 if (0 == _rw_bufcat (buf, "%", 1))
734 goto fail;
735
736 fmt = ++fc;
737 continue;
738 }
739
740 if (spec_bufsize == paramno) {
741 // grow the buffer of conversion specifiers
742 const size_t bytesize = spec_bufsize * sizeof (FmtSpec);
743
744 FmtSpec* const tmp = (FmtSpec*)malloc (bytesize * 2);
745 if (tmp) {
746 memcpy (tmp, pspec, bytesize);
747 if (pspec != specbuf)
748 free (pspec);
749 pspec = tmp;
750 }
751 else
752 goto fail;
753
754 spec_bufsize *= 2;
755 }
756
757 if ('{' == *fc) {
758 const char* const endbrace = strchr (++fc, '}');
759
760 if (0 == endbrace || fc == endbrace) {
761 const size_t flen = strlen (fc -= 2);
762 if (0 == _rw_bufcat (buf, fc, flen))
763 goto fail;
764
765 fc += flen;
766 continue;
767 }
768
769 const size_t fmtlen = endbrace - fc;
770
771 // FIXME: handle specifier strings that are longer
772 RW_ASSERT (fmtlen < sizeof fmtspec);
773
774 memcpy (fmtspec, fc, fmtlen);
775 fmtspec [fmtlen] = '\0';
776
777 // compute the length of the buffer so far
778 const size_t buflen = _rw_bufcat (buf, "", 0) - *buf.pbuf;
779
780 RW_ASSERT (paramno < spec_bufsize);
781
782 // initiaze the current format specification, setting
783 // all unused bits to 0
784 const int speclen = _rw_fmtspec (pspec + paramno, true, fc, pva);
785
786 // copy the current backtrack offset if one exists
787 // and set the condition and condition true bits
788 if (nextoff) {
789
790 // set the condition valid bit
791 pspec [paramno].cond = 1;
792
793 if (backtrack [nextoff - 1] < 0) {
794 // negative offset indicates an active clause
795 pspec [paramno].cond_true = 1;
796 }
797 else {
798 // non-negative offset indicates an inactive clause
799 // no-op
800 }
801 }
802
803 // append formatted string to the end of the buffer
804 // reallocating it if necessary; callee may change
805 // the specification as necessary (e.g., based on
806 // the if/else clause)
807
808 int len =
809 _rw_vasnprintf_ext (pspec, paramno, buf, fmtspec, pva);
810
811 // the callback returns a negative value on error
812 if (len < 0)
813 goto fail;
814
815 RW_ASSERT (size_t (len) <= *buf.pbufsize);
816 RW_ASSERT (buf.endoff <= *buf.pbufsize);
817
818 const size_t offinx = size_t (nextoff) - 1;
819
820 if (pspec [paramno].cond_end && pspec [paramno].cond_begin) {
821 // change from an if to an else clause
822
823 RW_ASSERT (0 < nextoff);
824 RW_ASSERT (0 == len);
825 RW_ASSERT (offinx < sizeof backtrack / sizeof *backtrack);
826
827 if (pspec [paramno].cond_true) {
828 // change from an inactive if to an active else
829 // (same as the end of an inactive clause)
830
831 RW_ASSERT (0 <= backtrack [offinx]);
832
833 // set the length so as to backtrack to the position
834 // saved on the top of the backtrack stack
835 len = -int (buflen) + backtrack [offinx];
836 buf.endoff += len;
837
838 // invert the offset to indicate an active clause
839 backtrack [offinx] = ~backtrack [offinx];
840 }
841 else {
842 // change from an active if to an inactive else
843 RW_ASSERT (backtrack [offinx] < 0);
844
845 // save the current length of the buffer
846 // as the new backtrack offset
847 backtrack [offinx] = int (buflen);
848 }
849 }
850 else if (pspec [paramno].cond_begin) {
851 // start of a new if clause
852
853 // push it on the stack of backtracking offsets using
854 // negative values to indicate active clauses and
855 // non-negative values inactive ones
856 if (pspec [paramno].cond_true)
857 backtrack [nextoff++] = ~int (buflen);
858 else
859 backtrack [nextoff++] = int (buflen);
860
861 // store the value of the conditional expression
862 // extracted from the argument list
863 ++paramno;
864 }
865 else if (pspec [paramno].cond_end) {
866 // the end of an if/else clause
867
868 if (!pspec [paramno].cond_true) {
869 // the end of an inactive clause
870
871 RW_ASSERT (offinx < sizeof backtrack / sizeof *backtrack);
872 RW_ASSERT (backtrack [offinx] <= int (buflen));
873
874 // set the length so as to backtrack to the position
875 // saved on the top of the backtrack stack
876 len = -int (buflen) + backtrack [offinx];
877 buf.endoff += len;
878 }
879
880 // pop it off the top of the stack
881 --nextoff;
882 }
883 else {
884 // store the value of the argument extracted from the list
885 ++paramno;
886 }
887
888 RW_ASSERT (len + buflen <= *buf.pbufsize);
889
890 fc += speclen + 1;
891 if (fc < endbrace)
892 fc = endbrace + 1;
893 }
894 else {
895 const int speclen =
896 _rw_fmtspec (pspec + paramno, false, fc, pva);
897
898 if (speclen) {
899
900 const int len = _rw_vasnprintf_c99 (pspec, paramno, buf, pva);
901
902 if (-1 == len)
903 goto fail;
904
905 // store the argument extracted from the argument list
906 // but discard positional specifiers
907 if (-1 == pspec [paramno].paramno)
908 ++paramno;
909
910 fc += speclen;
911 }
912 else if (0 == _rw_bufcat (buf, "%", 1)) {
913 goto fail;
914 }
915 }
916
917 fmt = fc;
918 }
919
920 for (size_t i = 0; i != paramno; ++i)
921 free (pspec [i].strarg);
922
923 // deallocate if dynamically allocated
924 if (pspec != specbuf)
925 free (pspec);
926
927 RW_ASSERT (begoff <= buf.endoff);
928
929 // return the number of characters appended to the buffer
930 // buffer isn't necessarily NUL terminated at this point
931 return int (buf.endoff - begoff);
932
933fail: // function failed
934
935 for (size_t i = 0; i != paramno; ++i)
936 free (pspec [i].strarg);
937
938 if (pspec != specbuf)
939 free (pspec);
940
941 return -1;
942}
943
944
945_TEST_EXPORT int
946rw_vasnprintf (char **pbuf, size_t *pbufsize, const char *fmt, va_list varg)
947{
948 va_list *pva;
949
950 va_list vacpy;
952
953 pva = &vacpy;
954
955// do not use varg or vacpy below this point -- use *pva instead
956#define varg DONT_TOUCH_ME
957#define vacpy DONT_TOUCH_ME
958
959 size_t default_bufsize = 1024;
960 if (0 == pbufsize)
961 pbufsize = &default_bufsize;
962
963 Buffer buf = { pbuf, pbufsize, _RWSTD_SIZE_MAX, 0 };
964
965 // save the initial value of `pbuf'
966 char* const pbuf_save = *buf.pbuf;
967
968 RW_ASSERT (0 != buf.pbuf);
969 RW_ASSERT (0 != buf.pbufsize);
970
971 if ( fmt
972 && '%' == fmt [0] && '{' == fmt [1]
973 && '+' == fmt [2] && '}' == fmt [3]) {
974 // when the format string begins with the special %{+}
975 // directive append to the buffer instead of writing
976 // over it
977 fmt += 4;
978 buf.endoff = *buf.pbuf ? strlen (*buf.pbuf) : 0;
979 RW_ASSERT (buf.endoff < *buf.pbufsize || !*buf.pbuf);
980 }
981 else if (*buf.pbuf)
982 **buf.pbuf = '\0';
983
984 if (fmt && '%' == fmt [0] && '{' == fmt [1]) {
985 if ('*' == fmt [2] && '}' == fmt [3]) {
986 const int n = va_arg (*pva, int);
987 if (n < 0) {
988#ifdef EINVAL
989 errno = EINVAL;
990#endif // EINVAL
991 return -1;
992 }
993
994 buf.maxsize = size_t (n);
995 fmt += 4;
996 }
997 else if (isdigit (fmt [2])) {
998
999 char* end = 0;
1000 const ULong n = strtoul (fmt + 2, &end, 0);
1001 if ('}' == *end) {
1002 buf.maxsize = n;
1003 fmt = end + 1;
1004 }
1005 }
1006 }
1007
1008 // format buffer w/o appending terminating NUL
1009 VarArgs args;
1010 args.kind_ = VarArgs::VA_list;
1011 args.arg_.pva_ = pva;
1012
1013 const int len = _rw_pvasnprintf (buf, fmt, &args);
1014
1015 // append terminating NUL
1016 if (len < 0 || !_rw_bufcat (buf, "", 1)) {
1017
1018 const int error = errno;
1019
1020 fprintf (stderr, "%s:%d: rw_vasnprintf(%p, %p, \"%s\", va_list) "
1021 "error: errno = %d: %s\n",
1022 __FILE__, __LINE__, (void*)buf.pbuf, (void*)buf.pbufsize,
1023 fmt, error, strerror (error));
1024
1025 if (*buf.pbuf != pbuf_save) {
1026 // free any allocated memory
1027 free (*buf.pbuf);
1028 *buf.pbuf = 0;
1029 }
1030
1031 if (errno != error) {
1032 // reset errno if it's been modified since it was saved
1033 errno = error;
1034 }
1035
1036 return len < 0 ? len : -1;
1037 }
1038
1039 return len;
1040
1041#undef varg
1042#undef vacpy
1043
1044}
1045
1046
1048static const char _rw_digits[] = {
1049 "0123456789abcdefghijklmnopqrstuvwxyz"
1050 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1051};
1052
1054_rw_fmtlong (const FmtSpec &spec, Buffer &buf, long val)
1055{
1056 char buffer [130]; // big enough for a 128-bit long in base 2
1057 char *end = buffer;
1058
1059 // 7.19.6.1 of ISO/IEC 9899:1999:
1060 // The result of converting a zero value with a precision
1061 // of zero is no characters.
1062 if (spec.prec || val) {
1063 const long upoff = 'X' == spec.cvtspec ? 36 : 0;
1064 const int base = 1 < spec.base && spec.base < 37 ? spec.base : 10;
1065 const bool is_signed = 'd' == spec.cvtspec || 'i' == spec.cvtspec;
1066 const bool neg = 0 > val && is_signed;
1067
1068 ULong uval = ULong (neg ? -val : val);
1069
1070 do {
1071 *end++ = _rw_digits [upoff + uval % base];
1072 } while (uval /= base);
1073
1074 // insert as many zeros as specified by precision
1075 // FIXME: prevent buffer overrun
1076 for (int i = int (end - buffer); i < spec.prec; ++i)
1077 *end++ = '0';
1078
1079 // insert octal or hex prefix for non-zero values
1080 if (spec.fl_pound && val) {
1081 switch (spec.base) {
1082 case 16:
1083 *end++ = upoff ? 'X' : 'x';
1084 // fall through
1085 case 8:
1086 *end++ = '0';
1087 break;
1088 }
1089 }
1090
1091 if (neg)
1092 *end++ = '-';
1093 else if (spec.fl_plus && is_signed)
1094 *end++ = '+';
1095 }
1096
1097 // not NUL-terminated
1098 RW_ASSERT (buffer <= end);
1099 const size_t size = size_t (end - buffer);
1100
1101 for (char *pc = buffer; pc < --end; ++pc) {
1102 const char tmp = *pc;
1103 *pc = *end;
1104 *end = tmp;
1105 }
1106
1107 // reset precision to -1 (already handled above)
1108 FmtSpec newspec (spec);
1109 newspec.fl_pound = 0;
1110 newspec.prec = -1;
1111
1112 // handle justification by formatting the resulting string
1113 return _rw_fmtstr (newspec, buf, buffer, size);
1114}
1115
1116
1118#ifdef _RWSTD_LONG_LONG
1119
1121_rw_fmtllong (const FmtSpec &spec, Buffer &buf, _RWSTD_LONG_LONG val)
1122{
1123 char buffer [130]; // big enough for a 128-bit long long in base 2
1124 char *end = buffer;
1125
1126 // 7.19.6.1 of ISO/IEC 9899:1999:
1127 // The result of converting a zero value with a precision
1128 // of zero is no characters.
1129 if (spec.prec || val) {
1130 const long upoff = 'X' == spec.cvtspec ? 36 : 0;
1131 const int base = 1 < spec.base && spec.base < 37 ? spec.base : 10;
1132 const bool is_signed = 'd' == spec.cvtspec || 'i' == spec.cvtspec;
1133 const bool neg = 0 > val && is_signed;
1134
1135 typedef unsigned _RWSTD_LONG_LONG ULLong;
1136 ULLong uval = ULLong (neg ? -val : val);
1137
1138 do {
1139 *end++ = _rw_digits [upoff + uval % base];
1140 } while (uval /= base);
1141
1142 // insert as many zeros as specified by precision
1143 // FIXME: prevent buffer overrun
1144 for (int i = int (end - buffer); i < spec.prec; ++i)
1145 *end++ = '0';
1146
1147 // insert octal or hex prefix for non-zero values
1148 if (spec.fl_pound && val) {
1149 switch (spec.base) {
1150 case 16:
1151 *end++ = upoff ? 'X' : 'x';
1152 // fall through
1153 case 8:
1154 *end++ = '0';
1155 break;
1156 }
1157 }
1158
1159 if (neg)
1160 *end++ = '-';
1161 else if (spec.fl_plus && is_signed)
1162 *end++ = '+';
1163 }
1164
1165 // not NUL-terminated
1166 RW_ASSERT (buffer <= end);
1167 const size_t size = size_t (end - buffer);
1168
1169 for (char *pc = buffer; pc < --end; ++pc) {
1170 const char tmp = *pc;
1171 *pc = *end;
1172 *end = tmp;
1173 }
1174
1175 // reset precision to -1 (already handled above)
1176 FmtSpec newspec (spec);
1177 newspec.fl_pound = 0;
1178 newspec.prec = -1;
1179
1180 // handle justification by formatting the resulting string
1181 return _rw_fmtstr (newspec, buf, buffer, size);
1182}
1183
1184#endif // _RWSTD_LONG_LONG
1185
1186
1188template <class IntT>
1189inline int
1190rw_fmtinteger (const FmtSpec &spec, Buffer &buf, IntT val)
1191{
1192#ifdef _RWSTD_LONG_LONG
1193
1194 typedef _RWSTD_LONG_LONG LLong;
1195
1196 const int len = sizeof (val) <= sizeof (long) ?
1197 ::_rw_fmtlong (spec, buf, long (val))
1198 : ::_rw_fmtllong (spec, buf, LLong (val));
1199
1200#else // if !defined (_RWSTD_LONG_LONG)
1201
1202 const int len = ::_rw_fmtlong (spec, buf, long (val));
1203
1204#endif // _RWSTD_LONG_LONG
1205
1206 return len;
1207}
1208
1209
1211static int
1212_rw_fmtinteger (FmtSpec *pspec, size_t paramno, Buffer &buf, VarArgs *pva)
1213{
1214 int len = -1;
1215
1216 FmtSpec &spec = pspec [paramno];
1217
1218 switch (spec.cvtspec) {
1219 case 'b': // extension: int argument formatted as bool
1220 spec.param.int_ = PARAM (int_, pva);
1221 len = _rw_fmtstr (spec, buf,
1222 spec.param.int_ ? "true" : "false",
1223 _RWSTD_SIZE_MAX);
1224 break;
1225
1226 case 'd':
1227 case 'i':
1228 if (spec.mod == spec.mod_hh) {
1229 // promoted signed char argument
1230 spec.param.int_ = PARAM (int_, pva);
1231 const signed char val = spec.param.int_;
1232 len = rw_fmtinteger (spec, buf, val);
1233 }
1234 else if (spec.mod == spec.mod_h) {
1235 // promoted signed short argument
1236 spec.param.int_ = PARAM (int_, pva);
1237 const short val = spec.param.int_;
1238 len = rw_fmtinteger (spec, buf, val);
1239 }
1240 else if (spec.mod == spec.mod_l) { // %li
1241 spec.param.long_ = PARAM (long_, pva);
1242 len = rw_fmtinteger (spec, buf, spec.param.long_);
1243 }
1244 else if (spec.mod == spec.mod_ll) { // %lli
1245
1246#ifdef _RWSTD_LONG_LONG
1247 spec.param.llong_ = PARAM (llong_, pva);
1248 len = rw_fmtinteger (spec, buf, spec.param.llong_);
1249#elif 8 == _RWSTD_LONG_SIZE
1250 spec.param.long_ = PARAM (long_, pva);
1251 len = rw_fmtinteger (spec, buf, spec.param.long_);
1252#else
1253 RW_ASSERT (!"%lld, %lli: long long not supported");
1254
1255#endif // _RWSTD_LONG_LONG
1256 }
1257 else if (spec.mod == spec.mod_t) {
1258 spec.param.diff_ = PARAM (diff_, pva);
1259 len = rw_fmtinteger (spec, buf, spec.param.diff_);
1260 }
1261 else if (spec.mod == spec.mod_z) {
1262 spec.param.diff_ = PARAM (diff_, pva);
1263 len = rw_fmtinteger (spec, buf, spec.param.diff_);
1264 }
1265 else if (1 == spec.iwidth) {
1266 spec.param.int_ = PARAM (int_, pva);
1267 const _RWSTD_INT8_T val = spec.param.int_;
1268 len = rw_fmtinteger (spec, buf, val);
1269 }
1270 else if (2 == spec.iwidth) {
1271 spec.param.int_ = PARAM (int_, pva);
1272 const _RWSTD_INT16_T val = spec.param.int_;
1273 len = rw_fmtinteger (spec, buf, val);
1274 }
1275 else if (3 == spec.iwidth) {
1276 spec.param.i32_ = PARAM (i32_, pva);
1277 const long val = long (spec.param.i32_);
1278 len = rw_fmtinteger (spec, buf, val);
1279 }
1280 else if (4 == spec.iwidth) {
1281
1282#ifdef _RWSTD_INT64_T
1283 spec.param.i64_ = PARAM (i64_, pva);
1284#else // if !defined (_RWSTD_INT64_T)
1285 RW_ASSERT (!"%I64d, %I64i: 64-bit types not supported");
1286#endif // _RWSTD_INT64_T
1287
1288#if 8 == _RWSTD_LONG_SIZE
1289 const long val = spec.param.i64_;
1290 len = rw_fmtinteger (spec, buf, val);
1291#elif defined (_RWSTD_LONG_LONG)
1292 const _RWSTD_LONG_LONG val = spec.param.i64_;
1293 len = rw_fmtinteger (spec, buf, val);
1294#else
1295 RW_ASSERT (!"%I64d, %I64i: 64-bit types not supported");
1296#endif
1297 }
1298 else { // %i
1299 spec.param.int_ = PARAM (int_, pva);
1300 len = rw_fmtinteger (spec, buf, spec.param.int_);
1301 }
1302 break;
1303
1304 case 'o':
1305 RW_ASSERT (-1 == spec.base);
1306 spec.base = 8;
1307 // fall thru
1308
1309 case 'x':
1310 if (-1 == spec.base)
1311 spec.base = 16;
1312 // fall thru
1313
1314 case 'X':
1315 if (-1 == spec.base)
1316 spec.base = 16;
1317
1318 case 'u':
1319 if (spec.mod == spec.mod_hh) {
1320 // promoted unsigned char argument
1321 spec.param.int_ = PARAM (int_, pva);
1322 const UChar val = spec.param.int_;
1323 len = rw_fmtinteger (spec, buf, val);
1324 }
1325 else if (spec.mod == spec.mod_h) {
1326 // promoted unsigned short argument
1327 spec.param.int_ = PARAM (int_, pva);
1328 const UShrt val = spec.param.int_;
1329 len = rw_fmtinteger (spec, buf, val);
1330 }
1331 else if (spec.mod == spec.mod_ll) {
1332#ifdef _RWSTD_LONG_LONG
1333 spec.param.llong_ = PARAM (llong_, pva);
1334 const unsigned _RWSTD_LONG_LONG val = spec.param.llong_;
1335 len = rw_fmtinteger (spec, buf, val);
1336#elif 8 == _RWSTD_LONG_SIZE
1337 spec.param.long_ = PARAM (long_, pva);
1338 const ULong val = spec.param.long_;
1339 len = rw_fmtinteger (spec, buf, val);
1340#else
1341 RW_ASSERT (!"long long not supported");
1342#endif // _RWSTD_LONG_LONG
1343
1344 }
1345 else if (spec.mod == spec.mod_l) {
1346 spec.param.long_ = PARAM (long_, pva);
1347 const ULong val = spec.param.long_;
1348 len = rw_fmtinteger (spec, buf, val);
1349 }
1350 else if (spec.mod == spec.mod_t) {
1351 spec.param.size_ = PARAM (size_, pva);
1352 len = rw_fmtinteger (spec, buf, spec.param.size_);
1353 }
1354 else if (spec.mod == spec.mod_z) {
1355 spec.param.size_ = PARAM (size_, pva);
1356 len = rw_fmtinteger (spec, buf, spec.param.size_);
1357 }
1358 else if (1 == spec.iwidth) {
1359 spec.param.int_ = PARAM (int_, pva);
1360 const _RWSTD_UINT8_T val = spec.param.int_;
1361 len = rw_fmtinteger (spec, buf, val);
1362 }
1363 else if (2 == spec.iwidth) {
1364 spec.param.int_ = PARAM (int_, pva);
1365 const long val = UShrt (spec.param.int_);
1366 len = rw_fmtinteger (spec, buf, val);
1367 }
1368 else if (3 == spec.iwidth) {
1369 spec.param.i32_ = PARAM (i32_, pva);
1370 const long val = long (unsigned (spec.param.int_));
1371 len = rw_fmtinteger (spec, buf, val);
1372 }
1373 else if (4 == spec.iwidth) {
1374#ifdef _RWSTD_INT64_T
1375 spec.param.i64_ = PARAM (i64_, pva);
1376#else // if defined 9_RWSTD_INT64_T)
1377 RW_ASSERT (!"%I64o, %I64u, %I64x: 64-bit types not supported");
1378#endif // _RWSTD_INT64_T
1379
1380#if 8 == _RWSTD_LONG_SIZE
1381 const ULong val = spec.param.i64_;
1382 len = rw_fmtinteger (spec, buf, val);
1383#elif defined (_RWSTD_LONG_LONG)
1384 const unsigned _RWSTD_LONG_LONG val = spec.param.i64_;
1385 len = rw_fmtinteger (spec, buf, val);
1386#else
1387 RW_ASSERT (!"%I64o, %I64u, %I64x: 64-bit types not supported");
1388#endif
1389 }
1390 else {
1391 spec.param.int_ = PARAM (int_, pva);
1392 const unsigned val = spec.param.int_;
1393 len = rw_fmtinteger (spec, buf, val);
1394 }
1395
1396 break;
1397 }
1398
1399 return len;
1400}
1401
1402
1404static int
1405_rw_fmtfloating (const FmtSpec &spec, Buffer &buf, const void *pval)
1406{
1407 char fmt [128];
1408 char *pf = fmt;
1409
1410 *pf++ = '%';
1411
1412 if (spec.fl_minus)
1413 *pf++ = '-';
1414
1415 if (spec.fl_plus)
1416 *pf++ = '+';
1417
1418 if (spec.fl_pound)
1419 *pf++ = '#';
1420
1421 if (spec.fl_space)
1422 *pf++ = ' ';
1423
1424 if (spec.fl_zero)
1425 *pf++ = '0';
1426
1427 if (spec.mod == spec.mod_h)
1428 *pf++ = 'h';
1429 else if (spec.mod == spec.mod_hh) {
1430 *pf++ = 'h';
1431 *pf++ = 'h';
1432 }
1433 else if (spec.mod == spec.mod_l)
1434 *pf++ = 'l';
1435 else if (spec.mod == spec.mod_ll) {
1436 *pf++ = 'l';
1437 *pf++ = 'l';
1438 }
1439 else if (spec.mod == spec.mod_j)
1440 *pf++ = 'j';
1441 else if (spec.mod == spec.mod_z)
1442 *pf++ = 'z';
1443 else if (spec.mod == spec.mod_t)
1444 *pf++ = 't';
1445 else if (spec.mod == spec.mod_L) {
1446 strcpy (pf, _RWSTD_LDBL_PRINTF_PREFIX);
1447 pf += strlen (pf);
1448 }
1449 else if (spec.mod == spec.mod_ext_A && _RWSTD_LDBL_SIZE == spec.width) {
1450 strcpy (pf, _RWSTD_LDBL_PRINTF_PREFIX);
1451 pf += strlen (pf);
1452 }
1453
1454 if (spec.mod != spec.mod_ext_A && 0 <= spec.width) {
1455 pf += sprintf (pf, "%i", spec.width);
1456 }
1457
1458 if (0 <= spec.prec)
1459 pf += sprintf (pf, ".%i", spec.prec);
1460
1461 *pf++ = char (spec.cvtspec);
1462 *pf = '\0';
1463
1464 // verify that the format buffer hasn't overflowed
1465 RW_ASSERT (size_t (pf - fmt) + 1 < sizeof fmt);
1466
1467 // this might make the buffer almost 5KB
1468 char buffer [_RWSTD_LDBL_MAX_10_EXP + _RWSTD_LDBL_DIG + 3];
1469 int len = -1;
1470
1471 if (spec.mod == spec.mod_ext_A) {
1472
1473 if (_RWSTD_FLT_SIZE == spec.width) {
1474 len = sprintf (buffer, fmt, *(const float*)pval);
1475 }
1476 else if (_RWSTD_DBL_SIZE == spec.width) {
1477 len = sprintf (buffer, fmt, *(const double*)pval);
1478 }
1479 else if (_RWSTD_LDBL_SIZE == spec.width) {
1480 len = sprintf (buffer, fmt, *(const long double*)pval);
1481 }
1482 else {
1483 RW_ASSERT (!"unknown floating point size");
1484 }
1485 }
1486 else if (spec.mod == spec.mod_L)
1487 len = sprintf (buffer, fmt, *(const long double*)pval);
1488 else
1489 len = sprintf (buffer, fmt, *(const double*)pval);
1490
1491 RW_ASSERT (size_t (len) < sizeof buffer);
1492
1493#if defined (_MSC_VER) || defined (__MINGW32__)
1494
1495 if (5 < len) {
1496 // remove redundant zeros from the exponent (if present)
1497 if ( ('e' == buffer [len - 5] || 'E' == buffer [len - 5])
1498 && '0' == buffer [len - 3]) {
1499 buffer [len - 3] = buffer [len - 2];
1500 buffer [len - 2] = buffer [len - 1];
1501 buffer [len - 1] = buffer [len];
1502 }
1503 }
1504
1505#endif // _MSC_VER || __MINGW32__
1506
1507
1508 if (-1 < len && 0 == _rw_bufcat (buf, buffer, size_t (len)))
1509 return -1;
1510
1511 return len;
1512}
1513
1514
1516// formats a data, function, or class member pointer as follows
1517// 0x<hex-long-0>[:<hex-long-1>[:...[:<hex-long-N>]]]
1518// where N is the size of the pointer in long ints
1519
1520
1521static int
1522_rw_fmtpointer (const FmtSpec &spec, Buffer &buf,
1523 const void *pptr, size_t nelems)
1524{
1525 FmtSpec newspec (spec);
1526
1527 // always format data pointers in hexadecimal
1528 newspec.base = 16;
1529
1530 // set the number of digits
1531 newspec.prec = _RWSTD_LONG_SIZE * 2;
1532
1533 int len = 0;
1534
1535 if (newspec.fl_pound) {
1536 // prepend the 0x prefix even to null pointers
1537 if (0 == _rw_bufcat (buf, "0x", size_t (len = 2))) {
1538 return -1;
1539 }
1540
1541 // reset the pound flag to prevent the integer formatter
1542 // from inserting it again
1543 newspec.fl_pound = 0;
1544 }
1545
1546 const union {
1547 const void *ptr;
1548 const ULong *lptr;
1549 } uptr = { pptr };
1550
1551 for (size_t i = 0; i != nelems; ++i) {
1552 const size_t inx = _rw_big_endian ? i : nelems - i - 1;
1553
1554 int n = _rw_fmtlong (newspec, buf, uptr.lptr [inx]);
1555 if (n < 0)
1556 return -1;
1557
1558 len += n;
1559 }
1560
1561 return len;
1562}
1563
1564
1567_rw_fmtptr (const FmtSpec &spec, Buffer &buf, const void *val)
1568{
1569 return _rw_fmtpointer (spec, buf, &val,
1570 sizeof val / sizeof (long));
1571}
1572
1573
1577 const void *addr, size_t objsize /* = 0 */)
1578{
1579 if (0 == addr)
1580 return _rw_bufcat (buf, "(null)", 6) ? 6 : -1;
1581
1582 const size_t off = buf.endoff;
1583
1584 const size_t num_addr = (size_t)addr;
1585
1586 if (objsize && (num_addr & (objsize - 1))) {
1587 if (0 == _rw_bufcat (buf, "(misaligned address ", 20))
1588 return -1;
1589 }
1590 else if (0 == _rw_bufcat (buf, "(invalid address ", 17))
1591 return -1;
1592
1593 FmtSpec newspec (spec);
1594 newspec.fl_pound = 1;
1595
1596 if (-1 == _rw_fmtptr (newspec, buf, addr))
1597 return -1;
1598
1599 if (0 == _rw_bufcat (buf, ")", 1))
1600 return -1;
1601
1602 return int (buf.endoff - off);
1603}
1604
1605
1607static int
1608_rw_fmtfunptr (const FmtSpec &spec, Buffer &buf, funptr_t val)
1609{
1610 if (spec.mod == spec.mod_l) {
1611
1612#if 0 // disabled until this is implemented on other platforms
1613#ifdef _RWSTD_OS_SUNOS
1614
1615 char buffer [256];
1616
1617 Dl_info dli;
1618
1619 // find the symbol corresponding to the address
1620 if (dladdr ((void*)val, &dli)) {
1621 if (!dli.dli_sname)
1622 dli.dli_fname = "unknown";
1623
1624 const size_t sym_off = size_t (dli.dli_saddr);
1625
1626 // compute the offset of the address from the address
1627 // of the symbol dladdr() found in the address space
1628 // of the calling process
1629 const size_t addr_off = size_t (val) < sym_off ?
1630 sym_off - size_t (val) : size_t (val) - sym_off;
1631
1632 // format the address folowed by the name of the symbol
1633 // followed by the offset
1634 const int len = sprintf (buffer, "%#x=%s%c%lu",
1635 size_t (val), dli.dli_sname,
1636 size_t (val) < sym_off ? '-' : '+',
1637 addr_off);
1638
1639 FmtSpec newspec (spec);
1640 newspec.mod_l = newspec.mod_none;
1641
1642 return _rw_fmtstr (newspec, buf, buffer, size_t (len));
1643 }
1644
1645#endif // _RWSTD_OS_SUNOS
1646#endif // 0/1
1647 }
1648
1649 return _rw_fmtpointer (spec, buf, &val,
1650 sizeof val / sizeof (long));
1651}
1652
1653
1655static int
1656_rw_fmtmemptr (const FmtSpec &spec, Buffer &buf, memptr_t val)
1657{
1658 return _rw_fmtpointer (spec, buf, &val,
1659 sizeof val / sizeof (long));
1660}
1661
1662
1664static int
1665_rw_fmttm (const FmtSpec &spec, Buffer &buf, const tm *tmb)
1666{
1667 if ( 0 == tmb || 0 > _RW::__rw_memattr (tmb, sizeof *tmb, -1)
1668 || (size_t)tmb & (sizeof (int) - 1)) {
1669 return _rw_fmtbadaddr (spec, buf, tmb, sizeof (int));
1670 }
1671
1672 static const char* const months[] = {
1673 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1674 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1675 };
1676
1677 static const char* const wdays[] = {
1678 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1679 };
1680
1681 char vbuf [1024]; // verbose format
1682
1683 int verbose = spec.fl_pound;
1684 int invalid = 0;
1685
1686 if ( tmb->tm_sec < 0 || 60 < tmb->tm_sec
1687 || tmb->tm_min < 0 || 59 < tmb->tm_min
1688 || tmb->tm_hour < 0 || 23 < tmb->tm_hour
1689 || tmb->tm_wday < 0 || 6 < tmb->tm_wday
1690 || tmb->tm_mday < 1 || 31 < tmb->tm_mday
1691 || _RWSTD_INT_MAX - 1900 < tmb->tm_year
1692 || tmb->tm_mon < 0 || 11 < tmb->tm_mon
1693 || tmb->tm_yday < 0 || 365 < tmb->tm_yday) {
1694 invalid = 1;
1695 verbose = 1;
1696 }
1697
1698 if (!invalid) {
1699 strcpy (vbuf, wdays [tmb->tm_wday]);
1700 strcat (vbuf, " ");
1701 strcat (vbuf, months [tmb->tm_mon]);
1702 strcat (vbuf, " ");
1703
1704 sprintf (vbuf + strlen (vbuf), "%2d %02d:%02d:%02d ",
1705 tmb->tm_mday, tmb->tm_hour, tmb->tm_min, tmb->tm_sec);
1706
1707 if (tmb->tm_isdst)
1708 strcat (vbuf, "DST ");
1709
1710 if (tmb->tm_year < -1899) {
1711 sprintf (vbuf + strlen (vbuf), "%d BC",
1712 -(tmb->tm_year + 1899));
1713 }
1714 else {
1715 sprintf (vbuf + strlen (vbuf), "%d",
1716 tmb->tm_year + 1900);
1717 }
1718 }
1719
1720 if (verbose) {
1721 static const char comma[] = ", ";
1722 const char *sep = "";
1723
1724 strcpy (vbuf, "{ ");
1725
1726 if (tmb->tm_sec) {
1727 sprintf (vbuf + strlen (vbuf), "%stm_sec=%d", sep, tmb->tm_sec);
1728 if (tmb->tm_sec < 0 || 60 < tmb->tm_sec)
1729 strcat (vbuf, " [0,60]");
1730 sep = comma;
1731 }
1732
1733 if (tmb->tm_min) {
1734 sprintf (vbuf + strlen (vbuf), "%stm_min=%d", sep, tmb->tm_min);
1735 if (tmb->tm_min < 0 || 59 < tmb->tm_min)
1736 strcat (vbuf, " [0,59]");
1737 sep = comma;
1738 }
1739
1740 if (tmb->tm_hour) {
1741 sprintf (vbuf + strlen (vbuf), "%stm_hour=%d", sep, tmb->tm_hour);
1742 if (tmb->tm_hour < 0 || 23 < tmb->tm_hour)
1743 strcat (vbuf, " [0,23]");
1744 sep = comma;
1745 }
1746
1747 sprintf (vbuf + strlen (vbuf), "%stm_mday=%d", sep, tmb->tm_mday);
1748 if (tmb->tm_mday < 1 || 31 < tmb->tm_mday)
1749 strcat (vbuf, " [1,31]");
1750 sep = comma;
1751
1752 if (tmb->tm_mon) {
1753 sprintf (vbuf + strlen (vbuf), "%stm_mon=%d", sep, tmb->tm_mon);
1754 if (tmb->tm_mon < 0 || 11 < tmb->tm_mon)
1755 strcat (vbuf, " [0,11]");
1756 else {
1757 sprintf (vbuf + strlen (vbuf), " %s",
1758 months [unsigned (tmb->tm_mon) % 7]);
1759 }
1760 }
1761
1762 if (tmb->tm_year) {
1763 sprintf (vbuf + strlen (vbuf), "%stm_year=%d", sep, tmb->tm_year);
1764 if (_RWSTD_INT_MAX - 1900 < tmb->tm_year)
1765 sprintf (vbuf + strlen (vbuf), " [%d,%d]",
1766 _RWSTD_INT_MIN, _RWSTD_INT_MAX - 1900);
1767 }
1768
1769 if (tmb->tm_wday) {
1770 sprintf (vbuf + strlen (vbuf), "%stm_wday=%d", sep, tmb->tm_wday);
1771 if (tmb->tm_wday < 0 || 6 < tmb->tm_wday)
1772 strcat (vbuf, " [0,6]");
1773 else {
1774 sprintf (vbuf + strlen (vbuf), " %s",
1775 wdays [unsigned (tmb->tm_wday) % 7]);
1776 }
1777 }
1778
1779 if (tmb->tm_yday) {
1780 sprintf (vbuf + strlen (vbuf), "%stm_yday=%d", sep, tmb->tm_yday);
1781 if (tmb->tm_yday < 0 || 365 < tmb->tm_yday)
1782 strcat (vbuf, " [0,365]");
1783 }
1784
1785 if (tmb->tm_isdst)
1786 sprintf (vbuf + strlen (vbuf), "%stm_isdst=%d", sep, tmb->tm_isdst);
1787
1788 strcat (vbuf, " }");
1789 }
1790
1791 const size_t len = strlen (vbuf);
1792
1793 if (0 == _rw_bufcat (buf, vbuf, len))
1794 return -1;
1795
1796 return int (len);
1797}
1798
1799
1801template <class charT>
1802int _rw_quotechar (char *buf, charT wc, int noesc)
1803{
1804 // wchar_t is signed, convert its value to unsigned long
1805 // without widening (i.e., treat it as an unsigned type)
1806 const _RWSTD_UWCHAR_INT_T wi =
1808
1809 if ((1 == sizeof wc || wi < 0x100U) && noesc) {
1810 buf [0] = char (wc);
1811 buf [1] = '\0';
1812 return 1;
1813 }
1814
1815 int len = 3;
1816
1817 buf [0] = '\\';
1818 buf [2] = '\0';
1819
1820 switch (wc) {
1821 case '\a': buf [1] = 'a'; buf [len = 2] = '\0'; break;
1822 case '\b': buf [1] = 'b'; buf [len = 2] = '\0'; break;
1823 case '\f': buf [1] = 'f'; buf [len = 2] = '\0'; break;
1824 case '\n': buf [1] = 'n'; buf [len = 2] = '\0'; break;
1825 case '\r': buf [1] = 'r'; buf [len = 2] = '\0'; break;
1826 case '\t': buf [1] = 't'; buf [len = 2] = '\0'; break;
1827 case '\v': buf [1] = 'v'; buf [len = 2] = '\0'; break;
1828 case '\\': buf [1] = '\\'; buf [len = 2] = '\0'; break;
1829 case '"' : buf [1] = '"'; buf [len = 2] = '\0'; break;
1830 default:
1831 if (wc < ' ' || wc > '~') {
1832
1833 if (0 == wc) {
1834 buf [1] = '0';
1835 buf [2] = '\0';
1836 len = 2;
1837 }
1838 else if (1 == sizeof wc) {
1839 len = 1 + sprintf (buf + 1, "x%02x", UChar (wc));
1840 }
1841 else {
1842
1843 const int width =
1844 wi > 0xfffffffUL ? 8 : wi > 0xffffffUL ? 7
1845 : wi > 0xfffffUL ? 6 : wi > 0xffffUL ? 5
1846 : wi > 0xfffUL ? 4 : wi > 0xffUL ? 3
1847 : wi > 0xfUL ? 2 : 2;
1848
1849 len = 1 + sprintf (buf + 1, "x%0*lx", width, ULong (wi));
1850 }
1851 }
1852 else {
1853 buf [0] = char (wc);
1854 buf [1] = '\0';
1855 len = 1;
1856 }
1857 }
1858
1859 return len;
1860}
1861
1862
1863enum {
1864 A_ESC = 1, // use escape sequences
1865 A_CHAR = 1 << 1, // format each element as a char
1866 A_WCHAR = 1 << 2, // format each element as a wchar_t
1867 A_ARRAY = 1 << 3 // generic array formatting
1869
1870template <class elemT>
1871int _rw_fmtarray (const FmtSpec &spec,
1872 Buffer &buf,
1873 const elemT *array, // pointer to first element
1874 size_t nelems, // number of elements
1875 int flags)
1876{
1877 RW_ASSERT (0 != buf.pbuf);
1878
1879 const size_t memlen = _RWSTD_SIZE_MAX == nelems
1880 ? _RWSTD_SIZE_MAX : nelems * sizeof *array;
1881 if ( 0 == array
1882 || 0 > _RW::__rw_memattr (array, memlen, -1)
1883 || ((size_t)array & (sizeof *array - 1))) {
1884 // qualify the name of the static function in order to
1885 // allow it to be found when referenced from a template
1886 return ::_rw_fmtbadaddr (spec, buf, array, sizeof *array);
1887 }
1888
1889 if (_RWSTD_SIZE_MAX == nelems) {
1890 // compute the length of the NUL-terminate string
1891 nelems = 0;
1892 for (const elemT *pc = array; *pc; ++pc, ++nelems);
1893 }
1894
1895 // separator string to insert between numerical elements
1896 const char* const sep = flags & (A_CHAR | A_WCHAR) ?
1897 "" : spec.strarg ? spec.strarg : ",";
1898
1899 // length of the separator string
1900 const size_t seplen = strlen (sep);
1901
1902 // compute the size of the buffer to preallocate
1903 const size_t bufsize = (nelems + 1) * (65 + seplen) + 3;
1904
1905 // preallocate a buffer large enough for the formatted array
1906 char *next = _rw_bufcat (buf, 0, bufsize);
1907 const char* const bufend = next;
1908
1909 if (0 == next)
1910 return -1;
1911
1912 if (0 == nelems) {
1913 if (!(flags & A_ESC)) {
1914
1915#if 0 // width handling disabled (width used for array formatting)
1916 for (int w = 0; w < spec.width; ++w)
1917 *next++ = ' ';
1918#endif // 0/1
1919
1920 }
1921 else {
1922 // append 'L' for a generic array (i.e., one whose type
1923 // is not known to be wchar_t) that is the same size as
1924 // wchar_t (bot not for an array of wchar_t's being
1925 // formatted using the {%ls} directive -- the caller
1926 // can easily stick the 'L' there themselves)
1927 if (flags & A_ARRAY && _RWSTD_WCHAR_SIZE == sizeof (elemT))
1928 *next++ = 'L';
1929
1930 *next++ = '"';
1931#if 0 // width handling disabled (width used for array formatting)
1932 for (int w = 0; w < spec.width; ++w)
1933 *next++ = ' ';
1934#endif // 0/1
1935 *next++ = '"';
1936 }
1937 buf.endoff += size_t (next - bufend);
1938 return int (next - bufend);
1939 }
1940
1941 char *s = next;
1942
1943 const elemT *last = array;
1944
1945 bool any_repeats = false;
1946 long last_repeat = flags & A_ESC ? -1L : 0L;
1947
1948 // large enough for a 64-bit integer in binary notation
1949 char elemstr [65];
1950
1951 const ptrdiff_t N = ptrdiff_t (flags & A_ESC ? 20 : 0);
1952
1953 for (const elemT *pelem = last + 1; ; ++pelem) {
1954
1955 if (size_t (pelem - array) < nelems && *pelem == *last) {
1956 // if the last processed element repeats, continue
1957 // until a different element is encountered
1958 continue;
1959 }
1960
1961 if (N > 1 && pelem - last > N) {
1962
1963 // if the last processed element repeats N or more
1964 // times, format the repeat count instead of all the
1965 // repeated occurrences of the element to conserve
1966 // space and make the string more readable
1967
1968 const long repeat = long (pelem - last);
1969
1970 if (flags & (A_CHAR | A_WCHAR)) {
1971 // format element into elemstr as a character
1972 _rw_quotechar (elemstr, *last, !(flags & A_ESC));
1973
1974 s += sprintf (s, "%s'%s' <repeats %ld times>",
1975 -1 == last_repeat ? ""
1976 : 0 == last_repeat ? "\", " : ", ",
1977 elemstr, repeat);
1978 }
1979 else {
1980 // format element into elemstr as an integer
1981 char *localbuf = elemstr;
1982 size_t localbufsize = sizeof elemstr;
1983
1984 Buffer bufspec = {
1985 &localbuf, &localbufsize, sizeof elemstr, 0
1986 };
1987
1988 *localbuf = '\0';
1989 const int intlen = rw_fmtinteger (spec, bufspec, *last);
1990
1991 // no error should happen
1992 RW_ASSERT (0 < intlen);
1993
1994 localbuf [intlen] = '\0';
1995
1996 // verify that rw_fmtinteger didn't try to reallocate
1997 RW_ASSERT (localbuf == elemstr);
1998
1999 s += sprintf (s, "%s%s <repeats %ld times>%s",
2000 last_repeat < 1 ? "" : sep, elemstr, repeat, sep);
2001 }
2002
2003 last = pelem;
2004
2005 any_repeats = true;
2006 last_repeat = repeat;
2007 }
2008 else {
2009 // otherwise (if the last processed element repeats
2010 // fewer than N times) format the element that many
2011 // times
2012
2013 if (last_repeat < 0) {
2014 if (flags & (A_CHAR | A_WCHAR)) {
2015 if ( (flags & A_ARRAY)
2016 && _RWSTD_WCHAR_SIZE == sizeof (elemT))
2017 *s++ = 'L';
2018
2019 // insert an opening quote
2020 *s++ = '\"';
2021 }
2022 }
2023 else if (last_repeat > 0) {
2024 if (flags & (A_CHAR | A_WCHAR)) {
2025 // append a comma after a preceding "<repeats N times>"
2026 *s++ = ',';
2027 *s++ = ' ';
2028 // append an opening quote for character arrays
2029 *s++ = '\"';
2030 }
2031 }
2032
2033 if (flags & (A_CHAR | A_WCHAR)) {
2034 // format element into elemstr as a character
2035 _rw_quotechar (elemstr, *last, !(flags & A_ESC));
2036 }
2037 else {
2038 // format element into elemstr as an integer
2039 char *localbuf = elemstr;
2040 size_t localbufsize = sizeof elemstr;
2041
2042 Buffer bufspec = {
2043 &localbuf, &localbufsize, sizeof elemstr, 0
2044 };
2045
2046 *localbuf = '\0';
2047 const int intlen = rw_fmtinteger (spec, bufspec, *last);
2048
2049 // no error should happen
2050 RW_ASSERT (0 < intlen);
2051
2052 localbuf [intlen] = '\0';
2053
2054 // verify that rw_fmtinteger didn't try to reallocate
2055 RW_ASSERT (localbuf == elemstr);
2056 }
2057
2058 const size_t elemlen = strlen (elemstr);
2059
2060 for ( ; last != pelem; ++last) {
2061 memcpy (s, elemstr, elemlen);
2062 s += elemlen;
2063 memcpy (s, sep, seplen + 1);
2064 s += seplen;
2065 }
2066
2067 last_repeat = 0;
2068
2069 if (size_t (pelem - array) == nelems) {
2070 if (flags & (A_CHAR | A_WCHAR) && flags & A_ESC)
2071 *s++ = '\"';
2072
2073 // eliminate the last separator
2074 s -= seplen;
2075 *s = '\0';
2076
2077 break;
2078 }
2079 }
2080
2081 if (size_t (pelem - array) == nelems) {
2082 // eliminate the last separator
2083 s -= seplen;
2084 *s = '\0';
2085
2086 break;
2087 }
2088 }
2089
2090 if (any_repeats) {
2091 // enclose the whole thing in curly braces if any repeated elements
2092 // have been formatted using the "... <repeats N times>" syntax
2093 const size_t len = strlen (next);
2094 memmove (next + 2, next, len);
2095 next [0] = '{';
2096 next [1] = ' ';
2097 next [len + 2] = ' ';
2098 next [len + 3] = '}';
2099 next [len + 4] = '\0';
2100 s = next + len + 4;
2101 }
2102
2103 buf.endoff += size_t (s - bufend);
2104
2105 return int (s - bufend);
2106}
2107
2108#ifndef _RWSTD_NO_EXPLICIT_INSTANTIATION
2109
2110template int _rw_quotechar (char*, UChar, int);
2111template int _rw_quotechar (char*, char, int);
2112template int _rw_quotechar (char*, UShrt, int);
2113template int _rw_quotechar (char*, UInt, int);
2114template int _rw_quotechar (char*, ULong, int);
2115
2116template int _rw_fmtarray (const FmtSpec&, Buffer&, const UChar*, size_t, int);
2117template int _rw_fmtarray (const FmtSpec&, Buffer&, const char*, size_t, int);
2118template int _rw_fmtarray (const FmtSpec&, Buffer&, const UShrt*, size_t, int);
2119template int _rw_fmtarray (const FmtSpec&, Buffer&, const UInt*, size_t, int);
2120template int _rw_fmtarray (const FmtSpec&, Buffer&, const ULong*, size_t, int);
2121
2122
2123# ifndef _RWSTD_NO_NATIVE_WCHAR_T
2124
2125// avoid instantiating when wchar_t is not a fundamental type
2126// (since it's most likely not distinct from one of the types
2127// the templates are instantiated on above)
2128template int _rw_quotechar (char*, wchar_t, int);
2129template int _rw_fmtarray(const FmtSpec&, Buffer&, const wchar_t*, size_t, int);
2130
2131# endif // _RWSTD_NO_NATIVE_WCHAR_T
2132
2133#endif // _RWSTD_NO_EXPLICIT_INSTANTIATION
2134
2135
2137static int
2138_rw_fmtarray (FmtSpec *pspec, size_t paramno, Buffer &buf, VarArgs *pva)
2139{
2140 RW_ASSERT (0 != pspec);
2141
2142 FmtSpec &spec = pspec [paramno];
2143
2144 // save width and set to unspecified (i.e., 1)
2145 const int width = spec.width;
2146 spec.width = -1;
2147
2148 const int flags = A_ARRAY
2149 | ('c' == spec.cvtspec || 'S' == spec.cvtspec ?
2150 A_CHAR | A_ESC : spec.fl_pound ? A_ESC : 0);
2151
2152 // to format an array of integers using the 0 or 0x/0X prefix
2153 // both the pound and the zero flags must be set; clear the pound
2154 // flag unless zero is also set
2155 const unsigned pound = spec.fl_pound;
2156 if (0 == spec.fl_zero)
2157 spec.fl_pound = 0;
2158
2159 const size_t nelems = size_t (spec.prec);
2160 spec.prec = -1;
2161
2162 int len = -1;
2163
2164 // array formatting: width determines the width of each array element,
2165 // precision the number of elements (when negative the array is taken
2166 // to extend up to but not including the first 0 element)
2167 if (-1 == width || 1 == width) {
2168 spec.param.ptr_ = PARAM (ptr_, pva);
2169 const _RWSTD_UINT8_T* const array = (_RWSTD_UINT8_T*)spec.param.ptr_;
2170 // note that when no precision is specified in the format string
2171 // (e.g., "%{Ac}") its value will be -1 and the function will format
2172 // all elements up to but excluding the terminating 0
2173 len = _rw_fmtarray (spec, buf, array, nelems, flags);
2174 }
2175 else if (2 == width) {
2176 spec.param.ptr_ = PARAM (ptr_, pva);
2177 const _RWSTD_UINT16_T* const array = (_RWSTD_UINT16_T*)spec.param.ptr_;
2178 len = _rw_fmtarray (spec, buf, array, nelems, flags);
2179 }
2180 else if (4 == width) {
2181 spec.param.ptr_ = PARAM (ptr_, pva);
2182 const _RWSTD_UINT32_T* const array = (_RWSTD_UINT32_T*)spec.param.ptr_;
2183 len = _rw_fmtarray (spec, buf, array, nelems, flags);
2184 }
2185
2186#ifdef _RWSTD_UINT64_T
2187
2188 else if (8 == width) {
2189 spec.param.ptr_ = PARAM (ptr_, pva);
2190 const _RWSTD_UINT64_T* const array = (_RWSTD_UINT64_T*)spec.param.ptr_;
2191 len = _rw_fmtarray (spec, buf, array, nelems, flags);
2192 }
2193
2194#endif // _RWSTD_UINT64_T
2195
2196 else {
2197 RW_ASSERT (!"%{Ac} not implemented for this character size");
2198 }
2199
2200 // restore modified members
2201 spec.fl_pound = pound;
2202 spec.prec = int (nelems);
2203 spec.width = width;
2204
2205 return len;
2206}
2207
2208
2210// formats an argv-style array of character strings
2211static int
2212_rw_fmtstrarray (FmtSpec *pspec, size_t paramno, Buffer &buf,
2213 const char* const* argv)
2214{
2215 FmtSpec &spec = pspec [paramno];
2216
2217 int len = 0;
2218
2219 if (0 == argv || 0 > _RW::__rw_memattr (argv, sizeof *argv, -1))
2220 return _rw_fmtbadaddr (spec, buf, argv);
2221
2222 size_t argc = spec.prec;
2223
2224 for (size_t i = 0; i != argc; ++i) {
2225 if (_RWSTD_SIZE_MAX == argc && 0 == argv [i])
2226 break;
2227
2228 int n = _rw_fmtstr (spec, buf, argv [i], _RWSTD_SIZE_MAX);
2229
2230 if (n < 0)
2231 return n;
2232
2233 len += n;
2234
2235 if (i + 1 == argc || (_RWSTD_SIZE_MAX == argc && 0 == argv [i + 1]))
2236 break;
2237
2238 const unsigned pound = spec.fl_pound;
2239 spec.fl_pound = 0;
2240 n = _rw_fmtstr (spec, buf, spec.fl_space ? " " : ",", 1);
2241 spec.fl_pound = pound;
2242
2243 if (n < 0)
2244 return n;
2245
2246 len += n;
2247 }
2248
2249 return len;
2250}
2251
2252
2254// implements %c and %{c} (narrow character formatting, both without
2255// and with escape sequences, respectively)
2256static int
2257_rw_fmtchr (const FmtSpec &spec, Buffer &buf, int val, int noesc)
2258{
2259 const UChar uc = UChar (val);
2260
2261 char buffer [8];
2262
2263 int len = _rw_quotechar (buffer + spec.fl_pound, uc, noesc);
2264 if (spec.fl_pound) {
2265 buffer [0] = buffer [len + 1] = '\'';
2266 buffer [len + 2] = '\0';
2267 len += 2;
2268 }
2269
2270 FmtSpec newspec (spec);
2271 newspec.fl_pound = 0;
2272 return _rw_fmtstr (newspec, buf, buffer, size_t (len));
2273}
2274
2275
2277// implements %lc and %{lc} (wide character formatting, both without
2278// and with escape sequences, respectively)
2279static int
2280_rw_fmtwchr (const FmtSpec &spec, Buffer &buf, wint_t val, int noesc)
2281{
2282 char buffer [16];
2283 int len;
2284
2285 if (0 == noesc && WEOF == val) {
2286
2287 // format EOF
2288 buffer [0] = 'E';
2289 buffer [1] = 'O';
2290 buffer [2] = 'F';
2291 buffer [3] = '\0';
2292 len = 3;
2293 }
2294 else {
2295 const wchar_t wc = wchar_t (val);
2296
2297 len = _rw_quotechar (buffer + spec.fl_pound, wc, noesc);
2298 if (spec.fl_pound) {
2299 buffer [0] = buffer [len + 1] = '\'';
2300 buffer [len + 2] = '\0';
2301 len += 2;
2302 }
2303 }
2304
2305 FmtSpec newspec (spec);
2306 newspec.fl_pound = 0;
2307 return _rw_fmtstr (newspec, buf, buffer, size_t (len));
2308}
2309
2310
2311// extern so it can be used in other translation units of the driver
2312// although it's not mneant to be called directly except by the
2313// implementation of rw_printf() and friends
2314
2315/* extern */ int
2316_rw_fmtstr (const FmtSpec &spec, Buffer &buf, const char *str, size_t len)
2317{
2318 if (spec.fl_pound)
2319 return _rw_fmtarray (spec, buf, str, len, A_CHAR | A_ESC);
2320
2321 if (0 == str || 0 > _RW::__rw_memattr (str, len, -1))
2322 return _rw_fmtbadaddr (spec, buf, str);
2323
2324 if (_RWSTD_SIZE_MAX == len)
2325 len = strlen (str);
2326
2327 // compute the minimum number of characters to be generated
2328 if (-1 < spec.prec && size_t (spec.prec) < len)
2329 len = size_t (spec.prec);
2330
2331 // the number of generated characters depends on three variables:
2332 // -- the optional field width,
2333 // -- the optional precision, and
2334 // -- the length of the argument
2335
2336 // compute the field width
2337 const size_t width =
2338 -1 < spec.width && len < size_t (spec.width) ?
2339 size_t (spec.width) : len;
2340
2341 // compute the size of padding
2342 const size_t pad = len < width ? width - len : 0;
2343
2344 // [re]allocate enough space in the buffer
2345 if (0 == _rw_bufcat (buf, 0, pad + len))
2346 return -1;
2347
2348 RW_ASSERT (0 != *buf.pbuf);
2349 char *next = *buf.pbuf + buf.endoff;
2350
2351 if (!spec.fl_minus) {
2352 for (size_t i = 0; i != pad; ++i)
2353 *next++ = ' ';
2354 }
2355
2356 memcpy (next, str, len);
2357 next += len;
2358
2359 if (spec.fl_minus) {
2360 for (size_t i = 0; i != pad; ++i)
2361 *next++ = ' ';
2362 }
2363
2364 len += pad;
2365
2366 buf.endoff += len;
2367
2368 return int (len);
2369}
2370
2371
2373#ifndef _RWSTD_NO_WCHAR_T
2374
2375static int
2376_rw_fmtwstr (const FmtSpec &spec, Buffer &buf,
2377 const wchar_t *wstr, size_t len)
2378{
2379 if (spec.fl_pound) {
2380
2381 // set up flags so that %{#*S} for generic basic_string quoted
2382 // formatting (i.e., parametrized on the size of the character
2383 // type) prepends a 'L' to the quoted string when the character
2384 // type is (likely to be) wchar_t
2385 const int flags =
2386 ('S' == spec.cvtspec && -1 != spec.width ? A_ARRAY : 0)
2387 | A_WCHAR | A_ESC;
2388
2389 return _rw_fmtarray (spec, buf, wstr, len, flags);
2390 }
2391
2392 const size_t memlen = _RWSTD_SIZE_MAX == len
2393 ? _RWSTD_SIZE_MAX : len * sizeof *wstr;
2394 if ( 0 == wstr
2395 || 0 > _RW::__rw_memattr (wstr, memlen, -1)
2396 || ((size_t)wstr & (sizeof *wstr - 1)))
2397 return _rw_fmtbadaddr (spec, buf, wstr, sizeof *wstr);
2398
2399 if (_RWSTD_SIZE_MAX == len) {
2400
2401#ifndef _RWSTD_NO_WCSLEN
2402 len = wcslen (wstr);
2403#else // if defined (_RWSTD_NO_WCSLEN)
2404 len = 0;
2405 for (const wchar_t *pwc = wstr; *pwc; ++pwc, ++len);
2406#endif // _RWSTD_NO_WCSLEN
2407
2408 }
2409
2410 // compute the minimum number of characters to be generated
2411 if (-1 < spec.prec && size_t (spec.prec) < len)
2412 len = size_t (spec.prec);
2413
2414 // the number of generated characters depends on three variables:
2415 // -- the optional field width,
2416 // -- the optional precision, and
2417 // -- the length of the argument
2418
2419 // compute the field width
2420 const size_t width =
2421 -1 < spec.width && len < size_t (spec.width) ?
2422 size_t (spec.width) : len;
2423
2424 // compute the size of padding
2425 const size_t pad = len < width ? width - len : 0;
2426
2427 // [re]allocate enough space in the buffer
2428 if (0 == _rw_bufcat (buf, 0, pad + len * 10))
2429 return -1;
2430
2431 RW_ASSERT (0 != *buf.pbuf);
2432 char *next = *buf.pbuf + buf.endoff;
2433
2434 if (!spec.fl_minus) {
2435 for (size_t i = 0; i != pad; ++i)
2436 *next++ = ' ';
2437 }
2438
2439 // wmemcpy (next, wstr, len);
2440 for (const wchar_t *pwc = wstr; pwc != wstr + len; ++pwc) {
2441 const int n = _rw_quotechar (next, *pwc, 0);
2442
2443 if (n < 0)
2444 return n;
2445
2446 next += n;
2447 }
2448
2449 if (spec.fl_minus) {
2450 for (size_t i = 0; i != pad; ++i)
2451 *next++ = ' ';
2452 }
2453
2454 len += pad;
2455
2456 buf.endoff += len;
2457
2458 return int (len);
2459}
2460
2461#endif // _RWSTD_NO_WCHAR_T
2462
2463
2465static int
2466_rw_vasnprintf_ext (FmtSpec *pspec,
2467 size_t paramno,
2468 Buffer &buf,
2469 const char *fmt,
2470 VarArgs *pva)
2471{
2472 RW_ASSERT (0 != pva);
2473 RW_ASSERT (0 != pspec);
2474
2475 // for convenience
2476 FmtSpec &spec = pspec [paramno];
2477
2478 // the length of the sequence appended to the buffer to return
2479 // to the caller, or a negative value (such as -1) on error
2480 int len = -1;
2481
2482 for (size_t i = _rw_usr_inx; i-- && _rw_usr_fun [i]; ) {
2483
2484 // process the stack of user-defined formatting functions
2485 // top to bottom
2486
2487 if (_rw_usr_fun [i]) {
2488 if (0 == _rw_bufcat (buf, 0, 1))
2489 return -1;
2490
2491 (*buf.pbuf)[buf.endoff] = '\0';
2492
2493 // user-defined extension?
2494 if (0 < spec.paramno) {
2495 // pass the address of the paramno argument
2496 len = (_rw_usr_fun [i])(buf.pbuf, buf.pbufsize, fmt,
2497 &pspec [spec.paramno - 1].param);
2498 }
2499 else {
2500 // pass the address of the variable argument list
2501 // followed by the address of the current parameter
2502 len = (_rw_usr_fun [i])(buf.pbuf, buf.pbufsize, fmt,
2503 pva->arg_.pva_, &spec.param);
2504 }
2505
2506 // special value indicating that the user-defined function
2507 // decided not to process the format string and is passing
2508 // control back to us
2509 if (_RWSTD_INT_MIN != len) {
2510 if (-1 != len)
2511 buf.endoff += len;
2512 return len;
2513 }
2514 }
2515 }
2516
2517 len = -1;
2518
2519 switch (spec.cvtspec) {
2520
2521 case '!': // %{!}, %{+!}, %{-!}
2522 // set a user-defined formatting function
2523 if (spec.fl_plus) {
2524 // push a new user-defined handler on top of the stack
2525 spec.param.funptr_ = PARAM (funptr_, pva);
2526 _rw_usr_fun [_rw_usr_inx++] = (_rw_usr_cb_t*)spec.param.funptr_;
2527 }
2528 if (spec.fl_minus) {
2529 // pop the stack of user-defined handlers
2530 if (_rw_usr_inx)
2531 --_rw_usr_inx;
2532 }
2533
2534 if (!spec.fl_plus && !spec.fl_minus) {
2535 // replace the top of the stack of user-defined handlers
2536 if (0 == _rw_usr_inx)
2537 ++_rw_usr_inx;
2538
2539 spec.param.funptr_ = PARAM (funptr_, pva);
2540 _rw_usr_fun [_rw_usr_inx - 1] = (_rw_usr_cb_t*)spec.param.funptr_;
2541 }
2542
2543 len = 0;
2544 break;
2545
2546 case '@': { // %{@}
2547 // parametrized (nested) formatting directive
2548 spec.param.ptr_ = PARAM (ptr_, pva);
2549 const char* const tmp_fmt = (const char*)spec.param.ptr_;
2550 len = _rw_fmtnested (spec, buf, tmp_fmt, pva);
2551 break;
2552 }
2553
2554 case '?': // %{?}
2555 // beginning of an if clause
2556 spec.param.int_ = PARAM (int_, pva);
2557 spec.cond = 1;
2558 spec.cond_begin = 1;
2559 spec.cond_true = 0 != spec.param.int_;
2560 len = 0;
2561 break;
2562
2563 case ':': // %{:}
2564 if (spec.cond) {
2565 // end of an active if clause and the beginning
2566 // of an inactive else clause
2567
2568 spec.cond_begin = 1; // beginning of an else clause
2569 spec.cond_end = 1; // end of an if clause
2570 spec.cond_true = !spec.cond_true;
2571 len = 0;
2572 }
2573 else {
2574 // misplaced "%{:}"?
2575 static const char str[] = "%{:}";
2576 len = _rw_fmtstr (spec, buf, str, _RWSTD_SIZE_MAX);
2577 }
2578 break;
2579
2580 case ';': // %{;}
2581 if (spec.cond) {
2582 spec.cond_end = 1; // end of an if or else clause
2583 len = 0;
2584 }
2585 else {
2586 // misplaced "%{;}"?
2587 static const char str[] = "%{;}";
2588 len = _rw_fmtstr (spec, buf, str, _RWSTD_SIZE_MAX);
2589 }
2590 break;
2591
2592 case 'B': // %{B}
2593 // std::bitset
2594 spec.param.ptr_ = PARAM (ptr_, pva);
2595 if ( 0 == spec.param.ptr_
2596 || 0 > _RW::__rw_memattr (spec.param.ptr_, spec.prec, -1))
2597 len = _rw_fmtbadaddr (spec, buf, spec.param.ptr_, sizeof (long));
2598 else
2599 len = _rw_fmtbits (spec, buf, spec.param.ptr_, sizeof (long));
2600 break;
2601
2602 case 'b': // %{b}
2603 spec.param.ptr_ = PARAM (ptr_, pva);
2604 if ( 0 == spec.param.ptr_
2605 || 0 > _RW::__rw_memattr (spec.param.ptr_, spec.prec, -1))
2606 len = _rw_fmtbadaddr (spec, buf, spec.param.ptr_);
2607 else
2608 len = _rw_fmtbits (spec, buf, spec.param.ptr_, 1);
2609 break;
2610
2611 case 'c': // %{c}, %{Ac}, %{Lc}, %{lc}
2612 if (spec.mod == spec.mod_ext_A) { // array of characters
2613 len = _rw_fmtarray (pspec, paramno, buf, pva);
2614 }
2615 else if (spec.mod == spec.mod_L) {
2616 // locale category or LC_XXX constant
2617 spec.param.int_ = PARAM (int_, pva);
2618 len = _rw_fmtlc (spec, buf, spec.param.int_);
2619 }
2620 else if (spec.mod == spec.mod_l) {
2621 // wint_t argument formatted as wchar_t with non-printable
2622 // characters represented using traditional C escape
2623 // sequences
2624 spec.param.wint_ = PARAM (wint_, pva);
2625 return _rw_fmtwchr (spec, buf, spec.param.wint_, 0);
2626 }
2627 else {
2628 // int argument formatted as char with non-printable
2629 // characters represented using traditional C escape
2630 // sequences
2631 spec.param.int_ = PARAM (int_, pva);
2632 return _rw_fmtchr (spec, buf, spec.param.int_, 0);
2633 }
2634 break;
2635
2636 case 'C': // %{C}, %{LC}
2637 if (spec.mod == spec.mod_L) {
2638 // ctype_base::mask value
2639 spec.param.int_ = PARAM (int_, pva);
2640 len = _rw_fmtmask (spec, buf, spec.param.int_);
2641 }
2642 else {
2643 // ctype_base::mask of a character
2644 spec.param.int_ = PARAM (int_, pva);
2645 len = _rw_fmtcharmask (spec, buf, spec.param.int_);
2646 }
2647 break;
2648
2649 case 'E': // %{E} -- Windows GetLastError(), errno elsewhere
2650#ifdef _WIN32
2651 spec.param.int_ = -1 == spec.width ? GetLastError () : spec.width;
2652#else // if !defined (_WIN32)
2653 spec.param.int_ = -1 == spec.width ? errno : spec.width;
2654#endif // _WIN32
2655 len = _rw_fmtlasterror (spec, buf, spec.param.int_);
2656 break;
2657
2658 case 'e': // %{e}, %{Ae}
2659 if (spec.mod == spec.mod_ext_A) { // array of floating point values
2660 spec.param.ptr_ = PARAM (ptr_, pva);
2661 len = _rw_fmtfloating (spec, buf, spec.param.ptr_);
2662 }
2663 else if (spec.mod == spec.mod_ext_I) { // ios::copyfmt_event
2664 spec.param.int_ = PARAM (int_, pva);
2665 len = _rw_fmtevent (spec, buf, spec.param.int_);
2666 }
2667 break;
2668
2669 case 'f': // %{f}, %{Af}, %{If}
2670 if (spec.mod == spec.mod_ext_A) { // array of floating point values
2671 spec.param.ptr_ = PARAM (ptr_, pva);
2672 len = _rw_fmtfloating (spec, buf, spec.param.ptr_);
2673 }
2674 else if (spec.mod == spec.mod_ext_I) { // ios::fmtflags
2675 spec.param.int_ = PARAM (int_, pva);
2676 len = _rw_fmtflags (spec, buf, spec.param.int_);
2677 }
2678 else { // function pointer
2679 spec.param.funptr_ = PARAM (funptr_, pva);
2680 len = _rw_fmtfunptr (spec, buf, spec.param.funptr_);
2681 }
2682 break;
2683
2684 case 'g': // %{g}, %{Ag}
2685 if (spec.mod == spec.mod_ext_A) { // array of floating point values
2686 spec.param.ptr_ = PARAM (ptr_, pva);
2687 len = _rw_fmtfloating (spec, buf, spec.param.ptr_);
2688 }
2689 else {
2690 RW_ASSERT (!"%{g} not implemented");
2691 }
2692 break;
2693
2694 case 'd': // %{Id}
2695 case 'i': // %{Ii}
2696 if (-1 == spec.base)
2697 spec.base = 10;
2698 // fall through
2699
2700 case 'o': // %{Io}
2701 if (spec.mod == spec.mod_ext_I) { // ios::openmode
2702 spec.param.int_ = PARAM (int_, pva);
2703 len = _rw_fmtopenmode (spec, buf, spec.param.int_);
2704 break;
2705 }
2706 if (-1 == spec.base)
2707 spec.base = 8;
2708 // fall through
2709
2710 case 'x': // %{x}, %{Ax}
2711 case 'X': // %{X}, %{AX}
2712 if (-1 == spec.base)
2713 spec.base = 16;
2714 // fall through
2715
2716 case 'u': // %{u}, %{Au}
2717 if (spec.mod == spec.mod_ext_A) { // array
2718 if (-1 == spec.base)
2719 spec.base = 10;
2720 len = _rw_fmtarray (pspec, paramno, buf, pva);
2721 }
2722 else {
2723 // reset base set above
2724 spec.base = -1;
2725 len = _rw_fmtinteger (pspec, paramno, buf, pva);
2726 }
2727 break;
2728
2729 case 'K': // %{K} -- signal
2730 spec.param.int_ = PARAM (int_, pva);
2731 len = _rw_fmtsignal (spec, buf, spec.param.int_);
2732 break;
2733
2734 case 'm': // %{m} -- errno
2735 spec.param.int_ = -1 == spec.width ? errno : spec.width;
2736 len = _rw_fmterrno (spec, buf, spec.param.int_);
2737 break;
2738
2739 case 'M': // %{M}, %{LM}
2740 if (spec.mod == spec.mod_L) { // money_base::pattern
2741 spec.param.ptr_ = PARAM (ptr_, pva);
2742 len = _rw_fmtmonpat (spec, buf, (char*)spec.param.ptr_);
2743 }
2744 else { // member pointer
2745 spec.param.memptr_ = PARAM (memptr_, pva);
2746 len = _rw_fmtmemptr (spec, buf, spec.param.memptr_);
2747 }
2748 break;
2749
2750 case 'n': { // %{n}
2751 // The argument shall be a pointer to signed integer into which
2752 // is written the size of the buffer allocated by this call to
2753 // fprintf. No argument is converted, but one is consumed. If
2754 // the conversion specification includes any flags, a field
2755 // width, or a precision, the behavior is undefined.
2756
2757 RW_ASSERT (0 != buf.pbuf);
2758 RW_ASSERT (0 != *buf.pbuf);
2759 RW_ASSERT (0 != buf.pbufsize);
2760
2761 const size_t nbytes = *buf.pbufsize;
2762
2763 spec.param.ptr_ = PARAM (ptr_, pva);
2764
2765 if (spec.mod == spec.mod_hh) {
2766 UChar* const ptr = (UChar*)spec.param.ptr_;
2767
2768 RW_ASSERT (0 != ptr);
2769
2770 *ptr = UChar (nbytes);
2771 }
2772 else if (spec.mod == spec.mod_h) {
2773 short* const ptr = (short*)spec.param.ptr_;
2774
2775 RW_ASSERT (0 != ptr);
2776
2777 *ptr = short (nbytes);
2778 }
2779 else if (spec.mod == spec.mod_L) {
2780#ifdef _RWSTD_LONG_LONG
2782
2783 RW_ASSERT (0 != ptr);
2784
2785 *ptr = (_RWSTD_LONG_LONG)(nbytes);
2786#else // if !defined (_RWSTD_LONG_LONG)
2787 RW_ASSERT (!"%{Ln} not implemented");
2788#endif // _RWSTD_LONG_LONG
2789 }
2790 else if (spec.mod == spec.mod_l) {
2791 long* const ptr = (long*)spec.param.ptr_;
2792
2793 RW_ASSERT (0 != ptr);
2794
2795 *ptr = long (nbytes);
2796 }
2797 else if (spec.mod == spec.mod_t) {
2798 ptrdiff_t* const ptr = (ptrdiff_t*)spec.param.ptr_;
2799
2800 RW_ASSERT (0 != ptr);
2801
2802 *ptr = ptrdiff_t (nbytes);
2803 }
2804 else {
2805 int* const ptr = (int*)spec.param.ptr_;
2806
2807 RW_ASSERT (0 != ptr);
2808
2809 *ptr = int (nbytes);
2810 }
2811 len = 0;
2812 break;
2813 }
2814
2815 case 'P': // %{P}
2816 // rw_pid_t is _RWSTD_SSIZE_T which is equal to _RWSTD_PTRDIFF_T
2817 spec.param.diff_ = PARAM (diff_, pva);
2818 len = rw_fmtinteger (spec, buf, spec.param.diff_);
2819 break;
2820
2821 case 's': // %{s}, %{As}, %{Is}, %{ls}
2822 if (spec.mod == spec.mod_ext_A) { // array of strings
2823 spec.param.ptr_ = PARAM (ptr_, pva);
2824 const char* const* const argv = (char**)spec.param.ptr_;
2825 len = _rw_fmtstrarray (pspec, paramno, buf, argv);
2826 }
2827 else if (spec.mod == spec.mod_ext_I) { // ios::iostate
2828 spec.param.int_ = PARAM (int_, pva);
2829 len = _rw_fmtiostate (spec, buf, spec.param.int_);
2830 }
2831 else if (spec.mod == spec.mod_l) { // wchar_t*
2832 spec.param.ptr_ = PARAM (ptr_, pva);
2833 const wchar_t* const wstr = (wchar_t*)spec.param.ptr_;
2834 const size_t wstr_len =
2835 spec.width < 0 ? _RWSTD_SIZE_MAX : size_t (spec.width);
2836 len = _rw_fmtarray (spec, buf, wstr, wstr_len, A_WCHAR | A_ESC);
2837 }
2838 else { // char*
2839 spec.param.ptr_ = PARAM (ptr_, pva);
2840 const char* const str = (char*)spec.param.ptr_;
2841 const size_t str_len =
2842 spec.width < 0 ? _RWSTD_SIZE_MAX : size_t (spec.width);
2843 len = _rw_fmtarray (spec, buf, str, str_len, A_CHAR | A_ESC);
2844 }
2845 break;
2846
2847 case 'S': // %{S}, %{lS}, %{#*S}
2848 if ( spec.mod == spec.mod_l
2849 || ( spec.mod == FmtSpec::mod_none
2850 && spec.fl_pound && sizeof (wchar_t) == spec.width)) {
2851 // std::wstring
2852 spec.param.ptr_ = PARAM (ptr_, pva);
2853
2854 const std::wstring* const pstr = (std::wstring*)spec.param.ptr_;
2855
2856 if ( 0 == pstr || 0 > _RW::__rw_memattr (pstr, sizeof *pstr, -1)
2857 || (size_t)pstr & (sizeof (pstr) - 1)) {
2858 len = _rw_fmtbadaddr (spec, buf, pstr, sizeof pstr);
2859 }
2860 else {
2861
2862 const wchar_t* const wstr = pstr->data ();
2863 const std::wstring::size_type size = pstr->size ();
2864
2865 len = _rw_fmtwstr (spec, buf, wstr, size);
2866 }
2867 }
2868 else { // std::string
2869 spec.param.ptr_ = PARAM (ptr_, pva);
2870
2871 const std::string* const pstr = (std::string*)spec.param.ptr_;
2872
2873 if ( 0 == pstr || 0 > _RW::__rw_memattr (pstr, sizeof *pstr, -1)
2874 || (size_t)pstr & (sizeof (pstr) - 1)) {
2875 len = _rw_fmtbadaddr (spec, buf, pstr, sizeof pstr);
2876 }
2877 else {
2878 const char* const str = pstr->data ();
2879 const std::string::size_type size = pstr->size ();
2880
2881 len = _rw_fmtstr (spec, buf, str, size);
2882 }
2883 }
2884 break;
2885
2886 case 't': { // %{t}
2887 spec.param.ptr_ = PARAM (ptr_, pva);
2888
2889 const tm* const tmb = (tm*)spec.param.ptr_;
2890
2891 len = _rw_fmttm (spec, buf, tmb);
2892
2893 break;
2894 }
2895
2896 case 'w': { // %{Iw}
2897 if (spec.mod == spec.mod_ext_I) { // ios::seekdir
2898 spec.param.int_ = PARAM (int_, pva);
2899 len = _rw_fmtseekdir (spec, buf, spec.param.int_);
2900 break;
2901 }
2902 }
2903
2904 default:
2905 if (spec.strarg) {
2906 return _rw_fmtexpr (spec, buf, pva);
2907 }
2908 else {
2909 char text [80];
2910 len = sprintf (text, "%%{%.*s}", int (sizeof text - 3), fmt);
2911 if (0 == _rw_bufcat (buf, text, size_t (len)))
2912 return -1;
2913 }
2914 }
2915
2916 return len;
2917}
2918
2919
2921static int
2922_rw_fmtexpr (FmtSpec &spec, Buffer &buf, VarArgs *pva)
2923{
2924 RW_ASSERT (0 != spec.strarg);
2925
2926 char oper [2] = "";
2927 char *param = spec.strarg;
2928
2929 // look for the first operator character (if any)
2930 char* word = strpbrk (param, ":+-=?!");
2931 if (word) {
2932 if (':' == *word) {
2933 if ( '+' == word [1] || '-' == word [1]
2934 || '=' == word [1] || '?' == word [1]) {
2935 oper [0] = word [0];
2936 oper [1] = word [1];
2937 *word = '\0';
2938 word += 2;
2939 }
2940 else {
2941 // ':' without an immediately following '+', '-',
2942 // '=', or '?' is not special
2943 }
2944 }
2945 else if ('!' == word [0] && ':' == word [1]) {
2946 // extension
2947 oper [0] = word [0];
2948 oper [1] = word [1];
2949 *word = '\0';
2950 word += 2;
2951 }
2952 else {
2953 oper [0] = *word;
2954 *word++ = '\0';
2955 }
2956 }
2957 else
2958 word = oper;
2959
2960 if ('*' == *param) {
2961 // extract the name of the parameter from the argument list
2962 param = VA_ARG (*pva, char*);
2963 }
2964
2965 char* fmtword = 0;
2966
2967 if ('*' == *word) {
2968 // extract "word" from the argument list
2969 word = VA_ARG (*pva, char*);
2970 }
2971 else if ('@' == *word) {
2972 // extract formatting directive from the argument list
2973 // and set word to the result of processing it
2974 const char* const fmt = VA_ARG (*pva, char*);
2975
2976 size_t dummy_size = 0; // unused
2977 Buffer tmpbuf = { &fmtword, &dummy_size, _RWSTD_SIZE_MAX, 0 };
2978 const int len = _rw_pvasnprintf (tmpbuf, fmt, pva);
2979 if (len < 0)
2980 return -1;
2981
2982 // add terminating NUL
2983 if (0 == _rw_bufcat (tmpbuf, "", 1)) {
2984 free (fmtword);
2985 return -1;
2986 }
2987
2988 // set word to the formatted string
2989 word = *tmpbuf.pbuf;
2990 }
2991
2992 // retrieve the value of the parameter from the environments
2993 const char* val = getenv (param);
2994
2996 // From IEEE Std 1003.1, 2004 Edition:
2997 // http://www.opengroup.org/onlinepubs/009695399/utilities/
2998 // xcu_chap02.html#tag_02_06_02
2999
3000 // +--------------------+-------------+-------------+-------------+
3001 // | | parameter | parameter | parameter |
3002 // | +-------------+-------------+-------------+
3003 // | |Set, Not Null| Set, Null | Unset |
3004 // +--------------------+-------------+-------------+-------------+
3005 // | ${parameter:-word} | parameter | word | word |
3006 // | ${parameter-word} | parameter | null | word |
3007 // | ${parameter:=word} | parameter | assign word | assign word |
3008 // | ${parameter=word} | parameter | null | assign word |
3009 // | ${parameter:?word} | parameter | error | error |
3010 // | ${parameter?word} | parameter | null | error |
3011 // | ${parameter:+word} | word | null | null |
3012 // | ${parameter+word} | word | word | null |
3013 // +--------------------+-------------+-------------+-------------+
3014
3015 // Extension:
3016 // +--------------------+-------------+-------------+-------------+
3017 // | ${parameter!:word} | assign word | assign word | assign word |
3018 // +--------------------+-------------+-------------+-------------+
3019
3020 int assign = 0;
3021 int error = 0;
3022
3023 switch (oper [0]) {
3024 case ':': // process two-character operators
3025 if ('-' == oper [1]) {
3026 if (0 == val || '\0' == *val)
3027 val = word;
3028 }
3029 else if ('=' == oper [1]) {
3030 if (0 == val || '\0' == *val) {
3031 val = word;
3032 assign = 1;
3033 }
3034 }
3035 else if ('?' == oper [1]) {
3036 if (0 == val || '\0' == *val) {
3037 val = "";
3038 error = 1;
3039 }
3040 }
3041 else if ('+' == oper [1]) {
3042 if (0 == val || '\0' == *val)
3043 val = "";
3044 else
3045 val = word;
3046 }
3047 else {
3048 // should never happen
3049 RW_ASSERT (!"logic error");
3050 }
3051 break;
3052
3053 case '+':
3054 val = val ? word : "";
3055 break;
3056
3057 case '-':
3058 if (0 == val)
3059 val = word;
3060 break;
3061
3062 case '?':
3063 if (0 == val) {
3064 val = "";
3065 error = 1;
3066 }
3067 break;
3068
3069 case '=':
3070 if (0 == val) {
3071 val = word;
3072 assign = 1;
3073 }
3074 break;
3075
3076 case '!':
3077 val = word ? word : "";
3078 assign = 1;
3079 break;
3080
3081 default:
3082 if (0 == val) {
3083 // undefined variable
3084 val = "";
3085 }
3086
3087 break;
3088 }
3089
3090 RW_ASSERT (0 != val);
3091 int len = int (strlen (val));
3092
3093 if (assign) {
3094 // set/assign the specified value to the variable
3095 char varbuf [256];
3096 char *pbuf = varbuf;
3097
3098 const size_t varlen = strlen (param) + strlen (val) + 2;
3099 if (sizeof varbuf < varlen) {
3100 pbuf = (char*)malloc (varlen);
3101 if (0 == pbuf)
3102 return -1;
3103 }
3104
3105 strcpy (pbuf, param);
3106 strcat (pbuf, "=");
3107 strcat (pbuf, val);
3108
3109 rw_putenv (pbuf);
3110 if (pbuf != varbuf)
3111 free (pbuf);
3112 }
3113
3114 if (error) {
3115 // on error simply print the directive
3116 if (oper [0])
3117 spec.strarg [strlen (spec.strarg)] = oper [0];
3118
3119 char text [256];
3120 len = sprintf (text, "%%{$%.*s}", int (sizeof text - 3), spec.strarg);
3121 if (0 == _rw_bufcat (buf, text, size_t (len))) {
3122 free (fmtword);
3123 return -1;
3124 }
3125 }
3126 else {
3127 // format the value of the variable (after assignment
3128 // if it takes place)
3129 if (0 == _rw_bufcat (buf, val, size_t (len))) {
3130 free (fmtword);
3131 return -1;
3132 }
3133 }
3134
3135 // free the formatted word (if any)
3136 free (fmtword);
3137
3138 // free the string allocated in _rw_fmtspec()
3139 free (spec.strarg);
3140 spec.strarg = 0;
3141 spec.param.ptr_ = _RWSTD_CONST_CAST (char*, val);
3142
3143 return len;
3144}
3145
3146
3148static int
3149_rw_fmtnested (const FmtSpec &spec,
3150 Buffer &buf,
3151 const char *fmt,
3152 VarArgs *pva)
3153{
3154 RW_ASSERT ('@' == spec.cvtspec);
3155
3156 // optional precision specifies the number of times
3157 // for the directive to be applied
3158 const int nelems = spec.prec < 0 ? 1 : spec.prec;
3159
3160 // separator between array elements
3161 const char *sepstr = "";
3162 size_t seplen = 0;
3163
3164 if (spec.fl_space) {
3165 sepstr = " ";
3166 seplen = 1;
3167 }
3168
3169 VarArgs tmparg (*pva);
3170
3171 if (spec.mod_ext_A == spec.mod) {
3172 tmparg.kind_ = VarArgs::VA_array;
3173 tmparg.arg_.pargs_ = VA_ARG (*pva, void*);
3174 }
3175
3176 int len = 0;
3177
3178 for (int i = 0; i != nelems; ++i) {
3179 len += _rw_pvasnprintf (buf, fmt, &tmparg);
3180
3181 if (seplen && i + 1 < nelems && 0 == _rw_bufcat (buf, sepstr, seplen))
3182 return -1;
3183 }
3184
3185 return len;
3186}
3187
3188
3190_TEST_EXPORT int
3191rw_asnprintf (char **pbuf, size_t *pbufsize, const char *fmt, ...)
3192{
3193 RW_ASSERT (0 == pbuf || 0 == *pbuf || pbufsize);
3194
3195 va_list va;
3196 va_start (va, fmt);
3197
3198 char* bufptr = 0;
3199 size_t bufsize = 0;
3200
3201 if (0 == pbuf) {
3202 // if pbyf is 0 (i.e., this is a request for the size of the
3203 // buffer necessary to format all the arguments), set pbuf to
3204 // point to a local buf
3205 pbuf = &bufptr;
3206 }
3207
3208 if (0 == pbufsize) {
3209 // pbuf may be 0 regardless of the value of pbuf
3210 pbufsize = &bufsize;
3211 }
3212
3213 const int nchars = rw_vasnprintf (pbuf, pbufsize, fmt, va);
3214
3215 va_end (va);
3216
3217 // verify that the length of the fomatted buffer is less than
3218 // its size (this test is unreliable if there are any embedded
3219 // NULs in the output)
3220 RW_ASSERT (nchars < 0 || strlen (*pbuf) < *pbufsize);
3221
3222 if (pbuf == &bufptr) {
3223 // free the character buffer if pbuf was initially 0
3224 free (pbuf);
3225 }
3226
3227 return nchars;
3228}
3229
3230
3232_TEST_EXPORT char*
3233rw_snprintfa (char *buf, size_t bufsize, const char* fmt, ...)
3234{
3235 if (buf)
3236 *buf = '\0';
3237
3238 va_list va;
3239 va_start (va, fmt);
3240
3241 const int nchars = rw_vasnprintf (&buf, &bufsize, fmt, va);
3242
3243 va_end (va);
3244
3245 // verify that the length of the fomatted buffer is less than
3246 // its size (this test is unreliable if there are any embedded
3247 // NULs in the output)
3248 RW_ASSERT (nchars < 0 || strlen (buf) < bufsize);
3249
3251
3252 return nchars < 0 ? 0 : buf;
3253}
3254
3255
3257_TEST_EXPORT char*
3258rw_sprintfa (const char *fmt, ...)
3259{
3260 char* buf = 0;
3261 size_t bufsize = 0;
3262
3263 va_list va;
3264 va_start (va, fmt);
3265
3266 const int nchars = rw_vasnprintf (&buf, &bufsize, fmt, va);
3267
3268 va_end (va);
3269
3270 // verify that the length of the fomatted buffer is less than
3271 // its size (this test is unreliable if there are any embedded
3272 // NULs in the output)
3273 RW_ASSERT (nchars < 0 || (buf && strlen (buf) < bufsize));
3274
3276
3277 return buf;
3278}
3279
3280
3281
3283_TEST_EXPORT int
3284rw_sprintf (char *buf, const char *fmt, ...)
3285{
3286 char *tmpbuf = 0;
3287 size_t tmpsize = 0;
3288
3289 va_list va;
3290 va_start (va, fmt);
3291
3292 // FIXME: format directly into buf to avoid dynamic allocation
3293 const int nchars = rw_vasnprintf (&tmpbuf, &tmpsize, fmt, va);
3294
3295 va_end (va);
3296
3297 if (-1 < nchars)
3298 memcpy (buf, tmpbuf, size_t (nchars) + 1 /* NUL */);
3299
3300 free (tmpbuf);
3301
3302 return nchars;
3303}
3304
3305
3307_TEST_EXPORT int
3308rw_snprintf (char *buf, size_t bufsize, const char *fmt, ...)
3309{
3310 char *tmpbuf = 0;
3311 size_t tmpsize = 0;
3312
3313 va_list va;
3314 va_start (va, fmt);
3315
3316 // FIXME: format directly into buf to avoid dynamic allocation
3317 const int nchars = rw_vasnprintf (&tmpbuf, &tmpsize, fmt, va);
3318
3319 va_end (va);
3320
3321 if (size_t (nchars) < bufsize) {
3322 memcpy (buf, tmpbuf, size_t (nchars) + 1 /* NUL */);
3323 }
3324 else {
3325 // buffer isn't big enough
3326 memcpy (buf, tmpbuf, bufsize);
3327 if (bufsize)
3328 buf [bufsize - 1] = '\0';
3329 }
3330
3331 free (tmpbuf);
3332
3333 return nchars;
3334}
3335
3336
3338// avoid re-declaring these as exported here and instead rely on the
3339// declaration in the header #included above in order to prevent the
3340// bogus MSVC error:
3341// error C2201: 'rw_stdout' : must have external linkage in order to
3342// be exported/imported
3343
3344/* _TEST_EXPORT */ rw_file* const
3346
3347/* _TEST_EXPORT */ rw_file* const
3349
3350
3352static int
3353_rw_vfprintf (rw_file *file, const char *fmt, va_list va)
3354{
3355 char* buf = 0;
3356 size_t bufsize = 0;
3357
3358 const int nchars = rw_vasnprintf (&buf, &bufsize, fmt, va);
3359
3360 int nwrote = nchars;
3361
3362 if (0 < nchars) {
3363
3364 // avoid formatting when there's nothing to output
3365
3366 if (file) {
3367 // allow null file argument (to determine
3368 // the length of the formatted string)
3369
3370 // FIXME: implement this in terms of POSIX write()
3371 // for async-signal safety
3372 FILE* const stdio_file = _RWSTD_REINTERPRET_CAST (FILE*, file);
3373
3374 nwrote = int (fwrite (buf, 1, size_t (nchars), stdio_file));
3375
3376 // flush in case stderr isn't line-buffered (e.g., when
3377 // it's determined not to refer to a terminal device,
3378 // for example after it has been redirected to a file)
3379 fflush (stdio_file);
3380
3381#ifdef _WIN32
3382 // write string to the attached debugger (if any)
3383 OutputDebugString (buf);
3384#endif // _WIN32
3385
3386 }
3387 }
3388
3389 free (buf);
3390
3391 return nwrote;
3392}
3393
3394
3396_TEST_EXPORT int
3397rw_fprintf (rw_file *file, const char *fmt, ...)
3398{
3399 va_list va;
3400 va_start (va, fmt);
3401
3402 const int nchars = _rw_vfprintf (file, fmt, va);
3403
3404 va_end (va);
3405
3406 return nchars;
3407}
3408
3409
3411_TEST_EXPORT int
3412rw_printf (const char *fmt, ...)
3413{
3414 va_list va;
3415 va_start (va, fmt);
3416
3417 const int nchars = _rw_vfprintf (rw_stdout, fmt, va);
3418
3419 va_end (va);
3420
3421 return nchars;
3422}
int fail
#define N
Definition 17.names.cpp:157
#define i
Definition 17.names.cpp:126
#define nchars
Definition 17.names.cpp:502
#define ptr
Definition 17.names.cpp:525
#define last
Definition 17.names.cpp:487
#define pf
Definition 17.names.cpp:516
#define w
Definition 17.names.cpp:139
#define neg
Definition 17.names.cpp:503
#define array
Definition 17.names.cpp:447
#define buf
Definition 17.names.cpp:452
#define L
Definition 17.names.cpp:155
#define off
Definition 17.names.cpp:507
#define buffer
Definition 17.names.cpp:453
#define addr
Definition 17.names.cpp:446
#define charT
Definition 17.names.cpp:456
#define tmp
Definition 17.names.cpp:553
#define s
#define len
Definition 17.names.cpp:488
#define n
Definition 17.names.cpp:130
#define inx
Definition 17.names.cpp:484
#define val
Definition 17.names.cpp:570
#define flags
@ ULLong
const int end
#define LLong
#define _RWSTD_LONG_LONG
unsigned short wchar_t
#define _RWSTD_LDBL_PRINTF_PREFIX
Definition _config.h:381
#define va_arg(va, T)
#define va_end(ignore)
#define va_start(va, name)
#define stdout
#define FILE
#define stderr
#define _RWSTD_VA_COPY(va_dst, va_src)
Definition _defs.h:1597
#define _RWSTD_EXPORT
Definition _defs.h:485
#define _RWSTD_SSIZE_T
Definition _defs.h:1280
#define _RWSTD_REINTERPRET_CAST(T, x)
Definition _defs.h:535
#define _RWSTD_UNUSED(arg)
Definition _defs.h:972
#define _RWSTD_INTERNAL
Definition _defs.h:1005
#define _RWSTD_STATIC_CAST(T, x)
Definition _defs.h:534
#define _RWSTD_NAMESPACE(name)
Definition _defs.h:567
#define _RWSTD_UWCHAR_INT_T
Definition _defs.h:1287
#define _RWSTD_CONST_CAST(T, x)
Definition _defs.h:536
A TOML array.
Definition array.hpp:258
const char * str
Definition exec.cpp:170
int _rw_fmtcharmask(const FmtSpec &spec, Buffer &buf, int c)
int _rw_fmtmonpat(const FmtSpec &spec, Buffer &buf, const char pat[4])
Definition fmt_bits.cpp:406
int _rw_fmtlasterror(const FmtSpec &spec, Buffer &buf, int val)
int _rw_fmtbits(const FmtSpec &spec, Buffer &buf, const void *pelems, size_t elemsize)
int _rw_fmtmask(const FmtSpec &spec, Buffer &buf, int bits)
int _rw_fmtiostate(const FmtSpec &spec, Buffer &buf, int bits)
Definition fmt_bits.cpp:236
int _rw_fmtevent(const FmtSpec &spec, Buffer &buf, int event)
Definition fmt_bits.cpp:316
int _rw_fmtflags(const FmtSpec &spec, Buffer &buf, int bits)
Definition fmt_bits.cpp:154
int _rw_fmtlc(const FmtSpec &spec, Buffer &buf, int val)
Definition fmt_bits.cpp:349
int _rw_fmterrno(const FmtSpec &spec, Buffer &buf, int val)
Definition fmt_bits.cpp:657
int _rw_fmtseekdir(const FmtSpec &spec, Buffer &buf, int bits)
Definition fmt_bits.cpp:299
int _rw_fmtopenmode(const FmtSpec &spec, Buffer &buf, int bits)
Definition fmt_bits.cpp:253
int _rw_fmtsignal(const FmtSpec &spec, Buffer &buf, int val)
Definition fmt_bits.cpp:461
void(DummyStruct::* memptr_t)() const
Definition fmt_defs.h:54
unsigned long ULong
Definition fmt_defs.h:43
unsigned int UInt
Definition fmt_defs.h:42
unsigned short UShrt
Definition fmt_defs.h:41
void(* funptr_t)()
#define ENOMEM
Definition memattr.cpp:78
@ error
throw a parse_error exception in case of a tag
#define WEOF
Definition printf.cpp:68
int ival
Definition printf.cpp:82
UChar bytes[sizeof(int)]
Definition printf.cpp:83
_RWSTD_INTERNAL char * _rw_bufcat(Buffer &buf, const char *str, size_t len)
Definition printf.cpp:438
rw_file *const rw_stdout
Definition printf.cpp:3345
_TEST_EXPORT int rw_fprintf(rw_file *file, const char *fmt,...)
Definition printf.cpp:3397
_TEST_EXPORT int rw_asnprintf(char **pbuf, size_t *pbufsize, const char *fmt,...)
Definition printf.cpp:3191
_RWSTD_INTERNAL int _rw_fmtlong(const FmtSpec &, Buffer &, long)
Definition printf.cpp:1054
_TEST_EXPORT int rw_sprintf(char *buf, const char *fmt,...)
Definition printf.cpp:3284
int _rw_fmtstr(const FmtSpec &spec, Buffer &buf, const char *str, size_t len)
Definition printf.cpp:2316
_TEST_EXPORT char * rw_snprintfa(char *buf, size_t bufsize, const char *fmt,...)
Definition printf.cpp:3233
_TEST_EXPORT char * rw_sprintfa(const char *fmt,...)
Definition printf.cpp:3258
int _rw_fmtarray(const FmtSpec &spec, Buffer &buf, const elemT *array, size_t nelems, int flags)
Definition printf.cpp:1871
rw_file *const rw_stderr
Definition printf.cpp:3348
#define VA_ARG(va, T)
Definition printf.cpp:113
_TEST_EXPORT int rw_snprintf(char *buf, size_t bufsize, const char *fmt,...)
Definition printf.cpp:3308
#define PARAM(name, pva)
_TEST_EXPORT int rw_printf(const char *fmt,...)
Definition printf.cpp:3412
#define vacpy
int _rw_usr_cb_t(char **, size_t *, const char *,...)
Definition printf.cpp:168
_RWSTD_INTERNAL int _rw_fmtbadaddr(const FmtSpec &, Buffer &, const void *, size_t=0)
Definition printf.cpp:1576
_TEST_EXPORT int rw_vasnprintf(char **pbuf, size_t *pbufsize, const char *fmt, va_list varg)
Definition printf.cpp:946
@ A_ESC
Definition printf.cpp:1864
@ A_ARRAY
Definition printf.cpp:1867
@ A_WCHAR
Definition printf.cpp:1866
@ A_CHAR
Definition printf.cpp:1865
_RWSTD_INTERNAL int _rw_fmtptr(const FmtSpec &, Buffer &, const void *)
Definition printf.cpp:1567
int _rw_quotechar(char *buf, charT wc, int noesc)
Definition printf.cpp:1802
int rw_fmtinteger(const FmtSpec &spec, Buffer &buf, IntT val)
Definition printf.cpp:1190
#define varg
_TEST_EXPORT int rw_putenv(const char *, int=-1)
Definition environ.cpp:69
#define _TEST_EXPORT
Definition rw_testdefs.h:55
#define RW_ASSERT(expr)
unsigned char UChar
Definition collate.cpp:122
char ** pbuf
Definition fmt_defs.h:59
size_t size_
Definition fmt_defs.h:172
unsigned fl_pound
Definition fmt_defs.h:72
signed int base
Definition fmt_defs.h:105
int_t int_
Definition fmt_defs.h:170
unsigned cond
Definition fmt_defs.h:93
unsigned fl_minus
Definition fmt_defs.h:70
i32_t i32_
Definition fmt_defs.h:169
Modifier mod
Definition fmt_defs.h:91
unsigned fl_space
Definition fmt_defs.h:73
ldbl_t ldbl_
Definition fmt_defs.h:164
i64_t i64_
Definition fmt_defs.h:166
unsigned fl_zero
Definition fmt_defs.h:74
int prec
Definition fmt_defs.h:112
int width
Definition fmt_defs.h:111
memptr_t memptr_
Definition fmt_defs.h:176
dbl_t dbl_
Definition fmt_defs.h:175
char * strarg
Definition fmt_defs.h:115
unsigned cond_begin
Definition fmt_defs.h:95
ptr_t ptr_
Definition fmt_defs.h:167
signed int iwidth
Definition fmt_defs.h:102
wint_t wint_
Definition fmt_defs.h:173
unsigned fl_plus
Definition fmt_defs.h:71
int cvtspec
Definition fmt_defs.h:118
diff_t diff_
Definition fmt_defs.h:171
long_t long_
Definition fmt_defs.h:168
unsigned cond_true
Definition fmt_defs.h:94
funptr_t funptr_
Definition fmt_defs.h:177
llong_t llong_
Definition fmt_defs.h:165
union FmtSpec::@64 param
long paramno
Definition fmt_defs.h:108
@ mod_t
Definition fmt_defs.h:85
@ mod_L
Definition fmt_defs.h:86
@ mod_z
Definition fmt_defs.h:84
@ mod_hh
Definition fmt_defs.h:80
@ mod_ext_A
Definition fmt_defs.h:87
@ mod_h
Definition fmt_defs.h:79
@ mod_l
Definition fmt_defs.h:81
@ mod_ll
Definition fmt_defs.h:82
@ mod_ext_I
Definition fmt_defs.h:88
@ mod_j
Definition fmt_defs.h:83
@ mod_none
Definition fmt_defs.h:78
unsigned cond_end
Definition fmt_defs.h:96
union VarArgs::@69 arg_
@ VA_list
Definition printf.cpp:109
@ VA_array
Definition printf.cpp:109
void * pargs_
Definition printf.cpp:105
va_list * pva_
Definition printf.cpp:104
enum VarArgs::@70 kind_
#define EINVAL
Definition cmdopt.cpp:40