Coverage Report

Created: 2026-04-01 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wxwidgets/include/wx/private/wxprintf.h
Line
Count
Source
1
/////////////////////////////////////////////////////////////////////////////
2
// Name:        wx/private/wxprintf.h
3
// Purpose:     wxWidgets wxPrintf() implementation
4
// Author:      Ove Kaven
5
// Modified by: Ron Lee, Francesco Montorsi
6
// Created:     09/04/99
7
// Copyright:   (c) wxWidgets copyright
8
// Licence:     wxWindows licence
9
/////////////////////////////////////////////////////////////////////////////
10
11
#ifndef _WX_PRIVATE_WXPRINTF_H_
12
#define _WX_PRIVATE_WXPRINTF_H_
13
14
// ---------------------------------------------------------------------------
15
// headers and macros
16
// ---------------------------------------------------------------------------
17
18
#include "wx/crt.h"
19
#include "wx/log.h"
20
#include "wx/utils.h"
21
22
#include <limits.h>
23
#include <string.h>
24
25
// prefer snprintf over sprintf
26
#if defined(__VISUALC__)
27
    #define system_sprintf(buff, max, flags, data)      \
28
        ::_snprintf(buff, max, flags, data)
29
#elif defined(HAVE_SNPRINTF)
30
    #define system_sprintf(buff, max, flags, data)      \
31
        ::snprintf(buff, max, flags, data)
32
#else       // NB: at least sprintf() should always be available
33
    // since 'max' is not used in this case, wxVsnprintf() should always
34
    // ensure that 'buff' is big enough for all common needs
35
    // (see wxMAX_SVNPRINTF_FLAGBUFFER_LEN and wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN)
36
    #define system_sprintf(buff, max, flags, data)      \
37
        ::sprintf(buff, flags, data)
38
39
    #define SYSTEM_SPRINTF_IS_UNSAFE
40
#endif
41
42
// ---------------------------------------------------------------------------
43
// printf format string parsing
44
// ---------------------------------------------------------------------------
45
46
// some limits of our implementation
47
0
#define wxMAX_SVNPRINTF_ARGUMENTS         64
48
0
#define wxMAX_SVNPRINTF_FLAGBUFFER_LEN    32
49
#define wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN   512
50
51
52
// the conversion specifiers accepted by wxCRT_VsnprintfW
53
enum wxPrintfArgType
54
{
55
    wxPAT_INVALID = -1,
56
57
    wxPAT_INT,          // %d, %i, %o, %u, %x, %X
58
    wxPAT_LONGINT,      // %ld, etc
59
    wxPAT_LONGLONGINT,  // %Ld, etc
60
    wxPAT_SIZET,        // %zd, etc
61
62
    wxPAT_DOUBLE,       // %e, %E, %f, %g, %G
63
    wxPAT_LONGDOUBLE,   // %le, etc
64
65
    wxPAT_POINTER,      // %p
66
67
    wxPAT_CHAR,         // %hc  (in ANSI mode: %c, too)
68
    wxPAT_WCHAR,        // %lc  (in Unicode mode: %c, too)
69
70
    wxPAT_PCHAR,        // %s   (related to a char *)
71
    wxPAT_PWCHAR,       // %s   (related to a wchar_t *)
72
73
    wxPAT_NINT,         // %n
74
    wxPAT_NSHORTINT,    // %hn
75
    wxPAT_NLONGINT,     // %ln
76
77
    wxPAT_STAR          // '*' used for width or precision
78
};
79
80
// an argument passed to wxCRT_VsnprintfW
81
union wxPrintfArg
82
{
83
    int pad_int;                        //  %d, %i, %o, %u, %x, %X
84
    long int pad_longint;               // %ld, etc
85
    wxLongLong_t pad_longlongint;       // %Ld, etc
86
    size_t pad_sizet;                   // %zd, etc
87
88
    double pad_double;                  // %e, %E, %f, %g, %G
89
    long double pad_longdouble;         // %le, etc
90
91
    void *pad_pointer;                  // %p
92
93
    char pad_char;                      // %hc  (in ANSI mode: %c, too)
94
    wchar_t pad_wchar;                  // %lc  (in Unicode mode: %c, too)
95
96
    void *pad_str;                      // %s
97
98
    int *pad_nint;                      // %n
99
    short int *pad_nshortint;           // %hn
100
    long int *pad_nlongint;             // %ln
101
};
102
103
// helper for converting string into either char* or wchar_t* depending
104
// on the type of wxPrintfConvSpec<T> instantiation:
105
template<typename CharType> struct wxPrintfStringHelper {};
106
107
template<> struct wxPrintfStringHelper<char>
108
{
109
    typedef const wxWX2MBbuf ConvertedType;
110
0
    static ConvertedType Convert(const wxString& s) { return s.mb_str(); }
111
};
112
113
template<> struct wxPrintfStringHelper<wchar_t>
114
{
115
    typedef const wxWX2WCbuf ConvertedType;
116
0
    static ConvertedType Convert(const wxString& s) { return s.wc_str(); }
117
};
118
119
120
// Contains parsed data relative to a conversion specifier given to
121
// wxCRT_VsnprintfW and parsed from the format string
122
// NOTE: in C++ there is almost no difference between struct & classes thus
123
//       there is no performance gain by using a struct here...
124
template<typename CharType>
125
class wxPrintfConvSpec
126
{
127
public:
128
129
    // the position of the argument relative to this conversion specifier
130
    size_t m_pos;
131
132
    // the type of this conversion specifier
133
    wxPrintfArgType m_type;
134
135
    // the minimum and maximum width
136
    // when one of this var is set to -1 it means: use the following argument
137
    // in the stack as minimum/maximum width for this conversion specifier
138
    int m_nMinWidth, m_nMaxWidth;
139
140
    // does the argument need to the be aligned to left ?
141
    bool m_bAlignLeft;
142
143
    // pointer to the '%' of this conversion specifier in the format string
144
    // NOTE: this points somewhere in the string given to the Parse() function -
145
    //       it's task of the caller ensure that memory is still valid !
146
    const CharType *m_pArgPos;
147
148
    // pointer to the last character of this conversion specifier in the
149
    // format string
150
    // NOTE: this points somewhere in the string given to the Parse() function -
151
    //       it's task of the caller ensure that memory is still valid !
152
    const CharType *m_pArgEnd;
153
154
    // a little buffer where formatting flags like #+\.hlqLz are stored by Parse()
155
    // for use in Process()
156
    char m_szFlags[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
157
158
159
public:
160
161
    // we don't declare this as a constructor otherwise it would be called
162
    // automatically and we don't want this: to be optimized, wxCRT_VsnprintfW
163
    // calls this function only on really-used instances of this class.
164
    void Init();
165
166
    // Parses the first conversion specifier in the given string, which must
167
    // begin with a '%'. Returns false if the first '%' does not introduce a
168
    // (valid) conversion specifier and thus should be ignored.
169
    bool Parse(const CharType *format);
170
171
    // Process this conversion specifier and puts the result in the given
172
    // buffer. Returns the number of characters written in 'buf' or -1 if
173
    // there's not enough space.
174
    int Process(CharType *buf, size_t lenMax, wxPrintfArg *p, size_t written);
175
176
    // Loads the argument of this conversion specifier from given va_list.
177
    bool LoadArg(wxPrintfArg *p, va_list &argptr);
178
179
private:
180
    // A helper function of LoadArg() which is used to handle the '*' flag
181
    void ReplaceAsteriskWith(int w);
182
};
183
184
template<typename CharType>
185
void wxPrintfConvSpec<CharType>::Init()
186
0
{
187
0
    m_nMinWidth = 0;
188
0
    m_nMaxWidth = INT_MAX;
189
0
    m_pos = 0;
190
0
    m_bAlignLeft = false;
191
0
    m_pArgPos = m_pArgEnd = nullptr;
192
0
    m_type = wxPAT_INVALID;
193
194
0
    memset(m_szFlags, 0, sizeof(m_szFlags));
195
    // this character will never be removed from m_szFlags array and
196
    // is important when calling sprintf() in wxPrintfConvSpec::Process() !
197
0
    m_szFlags[0] = '%';
198
0
}
Unexecuted instantiation: wxPrintfConvSpec<char>::Init()
Unexecuted instantiation: wxPrintfConvSpec<wchar_t>::Init()
199
200
template<typename CharType>
201
bool wxPrintfConvSpec<CharType>::Parse(const CharType *format)
202
0
{
203
0
    bool done = false;
204
205
    // temporary parse data
206
0
    size_t flagofs = 1;
207
0
    bool in_prec,       // true if we found the dot in some previous iteration
208
0
         prec_dot;      // true if the dot has been already added to m_szFlags
209
0
    int ilen = 0;
210
211
0
    m_bAlignLeft = in_prec = prec_dot = false;
212
0
    m_pArgPos = m_pArgEnd = format;
213
0
    do
214
0
    {
215
0
#define CHECK_PREC \
216
0
        if (in_prec && !prec_dot) \
217
0
        { \
218
0
            m_szFlags[flagofs++] = '.'; \
219
0
            prec_dot = true; \
220
0
        }
221
222
        // what follows '%'?
223
0
        const CharType ch = *(++m_pArgEnd);
224
0
        switch ( ch )
225
0
        {
226
0
            case wxT('\0'):
227
0
                return false;       // not really an argument
228
229
0
            case wxT('%'):
230
0
                return false;       // not really an argument
231
232
0
            case wxT('#'):
233
0
            case wxT('0'):
234
0
            case wxT(' '):
235
0
            case wxT('+'):
236
0
            case wxT('\''):
237
0
                CHECK_PREC
238
0
                m_szFlags[flagofs++] = char(ch);
239
0
                break;
240
241
0
            case wxT('-'):
242
0
                CHECK_PREC
243
0
                m_bAlignLeft = true;
244
0
                m_szFlags[flagofs++] = char(ch);
245
0
                break;
246
247
0
            case wxT('.'):
248
                // don't use CHECK_PREC here to avoid warning about the value
249
                // assigned to prec_dot inside it being never used (because
250
                // overwritten just below)
251
0
                if (in_prec && !prec_dot)
252
0
                    m_szFlags[flagofs++] = '.';
253
0
                in_prec = true;
254
0
                prec_dot = false;
255
0
                m_nMaxWidth = 0;
256
                // dot will be auto-added to m_szFlags if non-negative
257
                // number follows
258
0
                break;
259
260
0
            case wxT('h'):
261
0
                ilen = -1;
262
0
                CHECK_PREC
263
0
                m_szFlags[flagofs++] = char(ch);
264
0
                break;
265
266
0
            case wxT('l'):
267
                // NB: it's safe to use flagofs-1 as flagofs always start from 1
268
0
                if (m_szFlags[flagofs-1] == 'l')       // 'll' modifier is the same as 'L' or 'q'
269
0
                    ilen = 2;
270
0
                else
271
0
                ilen = 1;
272
0
                CHECK_PREC
273
0
                m_szFlags[flagofs++] = char(ch);
274
0
                break;
275
276
0
            case wxT('q'):
277
0
            case wxT('L'):
278
0
                ilen = 2;
279
0
                CHECK_PREC
280
0
                m_szFlags[flagofs++] = char(ch);
281
0
                break;
282
#ifdef __WINDOWS__
283
            // under Windows we support the special '%I64' notation as longlong
284
            // integer conversion specifier for MSVC compatibility
285
            // (it behaves exactly as '%lli' or '%Li' or '%qi')
286
            case wxT('I'):
287
                if (*(m_pArgEnd+1) == wxT('6') &&
288
                    *(m_pArgEnd+2) == wxT('4'))
289
                {
290
                    m_pArgEnd++;
291
                    m_pArgEnd++;
292
293
                    ilen = 2;
294
                    CHECK_PREC
295
                    m_szFlags[flagofs++] = char(ch);
296
                    m_szFlags[flagofs++] = '6';
297
                    m_szFlags[flagofs++] = '4';
298
                    break;
299
                }
300
                // else: fall-through, 'I' is MSVC equivalent of C99 'z'
301
                wxFALLTHROUGH;
302
#endif      // __WINDOWS__
303
304
0
            case wxT('z'):
305
0
            case wxT('Z'):
306
                // 'z' is C99 standard for size_t and ptrdiff_t, 'Z' was used
307
                // for this purpose in libc5 and by wx <= 2.8
308
0
                ilen = 3;
309
0
                CHECK_PREC
310
0
                m_szFlags[flagofs++] = char(ch);
311
0
                break;
312
313
0
            case wxT('*'):
314
0
                if (in_prec)
315
0
                {
316
0
                    CHECK_PREC
317
318
                    // tell Process() to use the next argument
319
                    // in the stack as maxwidth...
320
0
                    m_nMaxWidth = -1;
321
0
                }
322
0
                else
323
0
                {
324
                    // tell Process() to use the next argument
325
                    // in the stack as minwidth...
326
0
                    m_nMinWidth = -1;
327
0
                }
328
329
                // save the * in our formatting buffer...
330
                // will be replaced later by Process()
331
0
                m_szFlags[flagofs++] = char(ch);
332
0
                break;
333
334
0
            case wxT('1'): case wxT('2'): case wxT('3'):
335
0
            case wxT('4'): case wxT('5'): case wxT('6'):
336
0
            case wxT('7'): case wxT('8'): case wxT('9'):
337
0
                {
338
0
                    int len = 0;
339
0
                    CHECK_PREC
340
0
                    while ( (*m_pArgEnd >= CharType('0')) &&
341
0
                            (*m_pArgEnd <= CharType('9')) )
342
0
                    {
343
0
                        m_szFlags[flagofs++] = char(*m_pArgEnd);
344
0
                        len = len*10 + (*m_pArgEnd - wxT('0'));
345
0
                        m_pArgEnd++;
346
0
                    }
347
348
0
                    if (in_prec)
349
0
                        m_nMaxWidth = len;
350
0
                    else
351
0
                        m_nMinWidth = len;
352
353
0
                    m_pArgEnd--; // the main loop pre-increments n again
354
0
                }
355
0
                break;
356
357
0
            case wxT('$'):      // a positional parameter (e.g. %2$s) ?
358
0
                {
359
0
                    if (m_nMinWidth <= 0)
360
0
                        break;      // ignore this formatting flag as no
361
                                    // numbers are preceding it
362
363
                    // remove from m_szFlags all digits previously added
364
0
                    do {
365
0
                        flagofs--;
366
0
                    } while (m_szFlags[flagofs] >= '1' &&
367
0
                             m_szFlags[flagofs] <= '9');
368
369
                    // re-adjust the offset making it point to the
370
                    // next free char of m_szFlags
371
0
                    flagofs++;
372
373
0
                    m_pos = m_nMinWidth;
374
0
                    m_nMinWidth = 0;
375
0
                }
376
0
                break;
377
378
0
            case wxT('d'):
379
0
            case wxT('i'):
380
0
            case wxT('o'):
381
0
            case wxT('u'):
382
0
            case wxT('x'):
383
0
            case wxT('X'):
384
0
                CHECK_PREC
385
0
                m_szFlags[flagofs++] = char(ch);
386
0
                if (ilen == 0)
387
0
                    m_type = wxPAT_INT;
388
0
                else if (ilen == -1)
389
                    // NB: 'short int' value passed through '...'
390
                    //      is promoted to 'int', so we have to get
391
                    //      an int from stack even if we need a short
392
0
                    m_type = wxPAT_INT;
393
0
                else if (ilen == 1)
394
0
                    m_type = wxPAT_LONGINT;
395
0
                else if (ilen == 2)
396
0
                    m_type = wxPAT_LONGLONGINT;
397
0
                else if (ilen == 3)
398
0
                    m_type = wxPAT_SIZET;
399
0
                done = true;
400
0
                break;
401
402
0
            case wxT('e'):
403
0
            case wxT('E'):
404
0
            case wxT('f'):
405
0
            case wxT('g'):
406
0
            case wxT('G'):
407
0
                CHECK_PREC
408
0
                m_szFlags[flagofs++] = char(ch);
409
0
                if (ilen == 2)
410
0
                    m_type = wxPAT_LONGDOUBLE;
411
0
                else
412
0
                    m_type = wxPAT_DOUBLE;
413
0
                done = true;
414
0
                break;
415
416
0
            case wxT('p'):
417
0
                m_type = wxPAT_POINTER;
418
0
                m_szFlags[flagofs++] = char(ch);
419
0
                done = true;
420
0
                break;
421
422
0
            case wxT('c'):
423
0
                if (ilen == -1)
424
0
                {
425
                    // %hc == ANSI character
426
0
                    m_type = wxPAT_CHAR;
427
0
                }
428
0
                else
429
0
                {
430
                    // %lc == %c == Unicode character
431
0
                    m_type = wxPAT_WCHAR;
432
0
                }
433
0
                done = true;
434
0
                break;
435
436
0
            case wxT('s'):
437
0
                if (ilen == -1)
438
0
                {
439
                    // wx extension: we'll let %hs mean non-Unicode strings
440
0
                    m_type = wxPAT_PCHAR;
441
0
                }
442
0
                else
443
0
                {
444
                    // %ls == %s == Unicode string
445
0
                    m_type = wxPAT_PWCHAR;
446
0
                }
447
0
                done = true;
448
0
                break;
449
450
0
            case wxT('n'):
451
0
                if (ilen == 0)
452
0
                    m_type = wxPAT_NINT;
453
0
                else if (ilen == -1)
454
0
                    m_type = wxPAT_NSHORTINT;
455
0
                else if (ilen >= 1)
456
0
                    m_type = wxPAT_NLONGINT;
457
0
                done = true;
458
0
                break;
459
460
0
            default:
461
                // bad format, don't consider this an argument;
462
                // leave it unchanged
463
0
                return false;
464
0
        }
465
466
0
        if (flagofs == wxMAX_SVNPRINTF_FLAGBUFFER_LEN)
467
0
        {
468
0
            wxLogDebug(wxT("Too many flags specified for a single conversion specifier!"));
469
0
            return false;
470
0
        }
471
0
    }
472
0
    while (!done);
473
474
0
    return true;        // parsing was successful
475
0
}
Unexecuted instantiation: wxPrintfConvSpec<char>::Parse(char const*)
Unexecuted instantiation: wxPrintfConvSpec<wchar_t>::Parse(wchar_t const*)
476
477
template<typename CharType>
478
void wxPrintfConvSpec<CharType>::ReplaceAsteriskWith(int width)
479
{
480
    char temp[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
481
482
    // find the first * in our flag buffer
483
    char *pwidth = strchr(m_szFlags, '*');
484
    wxCHECK_RET(pwidth, wxT("field width must be specified"));
485
486
    // save what follows the * (the +1 is to skip the asterisk itself!)
487
    strcpy(temp, pwidth+1);
488
    if (width < 0)
489
    {
490
        pwidth[0] = wxT('-');
491
        pwidth++;
492
    }
493
494
    // replace * with the actual integer given as width
495
#ifndef SYSTEM_SPRINTF_IS_UNSAFE
496
    int maxlen = (m_szFlags + wxMAX_SVNPRINTF_FLAGBUFFER_LEN - pwidth) /
497
                        sizeof(*m_szFlags);
498
#endif
499
    int offset = system_sprintf(pwidth, maxlen, "%d", abs(width));
500
501
    // restore after the expanded * what was following it
502
    strcpy(pwidth+offset, temp);
503
}
504
505
template<typename CharType>
506
bool wxPrintfConvSpec<CharType>::LoadArg(wxPrintfArg *p, va_list &argptr)
507
{
508
    // did the '*' width/precision specifier was used ?
509
    if (m_nMaxWidth == -1)
510
    {
511
        // take the maxwidth specifier from the stack
512
        m_nMaxWidth = va_arg(argptr, int);
513
        if (m_nMaxWidth < 0)
514
            m_nMaxWidth = 0;
515
        else
516
            ReplaceAsteriskWith(m_nMaxWidth);
517
    }
518
519
    if (m_nMinWidth == -1)
520
    {
521
        // take the minwidth specifier from the stack
522
        m_nMinWidth = va_arg(argptr, int);
523
524
        ReplaceAsteriskWith(m_nMinWidth);
525
        if (m_nMinWidth < 0)
526
        {
527
            m_bAlignLeft = !m_bAlignLeft;
528
            m_nMinWidth = -m_nMinWidth;
529
        }
530
    }
531
532
    switch (m_type) {
533
        case wxPAT_INT:
534
            p->pad_int = va_arg(argptr, int);
535
            break;
536
        case wxPAT_LONGINT:
537
            p->pad_longint = va_arg(argptr, long int);
538
            break;
539
        case wxPAT_LONGLONGINT:
540
            p->pad_longlongint = va_arg(argptr, wxLongLong_t);
541
            break;
542
        case wxPAT_SIZET:
543
            p->pad_sizet = va_arg(argptr, size_t);
544
            break;
545
        case wxPAT_DOUBLE:
546
            p->pad_double = va_arg(argptr, double);
547
            break;
548
        case wxPAT_LONGDOUBLE:
549
            p->pad_longdouble = va_arg(argptr, long double);
550
            break;
551
        case wxPAT_POINTER:
552
            p->pad_pointer = va_arg(argptr, void *);
553
            break;
554
555
        case wxPAT_CHAR:
556
            p->pad_char = (char)va_arg(argptr, int);  // char is promoted to int when passed through '...'
557
            break;
558
        case wxPAT_WCHAR:
559
            p->pad_wchar = (wchar_t)va_arg(argptr, int);  // char is promoted to int when passed through '...'
560
            break;
561
562
        case wxPAT_PCHAR:
563
        case wxPAT_PWCHAR:
564
            p->pad_str = va_arg(argptr, void *);
565
            break;
566
567
        case wxPAT_NINT:
568
            p->pad_nint = va_arg(argptr, int *);
569
            break;
570
        case wxPAT_NSHORTINT:
571
            p->pad_nshortint = va_arg(argptr, short int *);
572
            break;
573
        case wxPAT_NLONGINT:
574
            p->pad_nlongint = va_arg(argptr, long int *);
575
            break;
576
577
        case wxPAT_STAR:
578
            // this will be handled as part of the next argument
579
            return true;
580
581
        case wxPAT_INVALID:
582
        default:
583
            return false;
584
    }
585
586
    return true;    // loading was successful
587
}
588
589
template<typename CharType>
590
int wxPrintfConvSpec<CharType>::Process(CharType *buf, size_t lenMax, wxPrintfArg *p, size_t written)
591
{
592
    // buffer to avoid dynamic memory allocation each time for small strings;
593
    // note that this buffer is used only to hold results of number formatting,
594
    // %s directly writes user's string in buf, without using szScratch
595
    char szScratch[wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN];
596
    size_t lenScratch = 0, lenCur = 0;
597
598
#define APPEND_CH(ch) \
599
                { \
600
                    if ( lenCur == lenMax ) \
601
                        return -1; \
602
                    \
603
                    buf[lenCur++] = ch; \
604
                }
605
606
    switch ( m_type )
607
    {
608
        case wxPAT_INT:
609
            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_int);
610
            break;
611
612
        case wxPAT_LONGINT:
613
            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longint);
614
            break;
615
616
        case wxPAT_LONGLONGINT:
617
            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longlongint);
618
            break;
619
620
        case wxPAT_SIZET:
621
            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_sizet);
622
            break;
623
624
        case wxPAT_LONGDOUBLE:
625
            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longdouble);
626
            break;
627
628
        case wxPAT_DOUBLE:
629
            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_double);
630
            break;
631
632
        case wxPAT_POINTER:
633
            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_pointer);
634
            break;
635
636
        case wxPAT_CHAR:
637
        case wxPAT_WCHAR:
638
            {
639
                wxUniChar ch;
640
                if (m_type == wxPAT_CHAR)
641
                    ch = p->pad_char;
642
                else // m_type == wxPAT_WCHAR
643
                    ch = p->pad_wchar;
644
645
                CharType val = ch;
646
647
                size_t i;
648
649
                if (!m_bAlignLeft)
650
                    for (i = 1; i < (size_t)m_nMinWidth; i++)
651
                        APPEND_CH(wxT(' '));
652
653
                APPEND_CH(val);
654
655
                if (m_bAlignLeft)
656
                    for (i = 1; i < (size_t)m_nMinWidth; i++)
657
                        APPEND_CH(wxT(' '));
658
            }
659
            break;
660
661
        case wxPAT_PCHAR:
662
        case wxPAT_PWCHAR:
663
            {
664
                wxString s;
665
                if ( !p->pad_str )
666
                {
667
                    if ( m_nMaxWidth >= 6 )
668
                        s = wxT("(null)");
669
                }
670
                else if (m_type == wxPAT_PCHAR)
671
                    s.assign(static_cast<const char *>(p->pad_str));
672
                else // m_type == wxPAT_PWCHAR
673
                    s.assign(static_cast<const wchar_t *>(p->pad_str));
674
675
                typename wxPrintfStringHelper<CharType>::ConvertedType strbuf(
676
                        wxPrintfStringHelper<CharType>::Convert(s));
677
678
                // at this point we are sure that m_nMaxWidth is positive or
679
                // null (see top of wxPrintfConvSpec::LoadArg)
680
                int len = wxMin((unsigned int)m_nMaxWidth, wxStrlen(strbuf));
681
682
                int i;
683
684
                if (!m_bAlignLeft)
685
                {
686
                    for (i = len; i < m_nMinWidth; i++)
687
                        APPEND_CH(wxT(' '));
688
                }
689
690
                len = wxMin((unsigned int)len, lenMax-lenCur);
691
                wxStrncpy(buf+lenCur, strbuf, len);
692
                lenCur += len;
693
694
                if (m_bAlignLeft)
695
                {
696
                    for (i = len; i < m_nMinWidth; i++)
697
                        APPEND_CH(wxT(' '));
698
                }
699
            }
700
            break;
701
702
        case wxPAT_NINT:
703
            *p->pad_nint = written;
704
            break;
705
706
        case wxPAT_NSHORTINT:
707
            *p->pad_nshortint = (short int)written;
708
            break;
709
710
        case wxPAT_NLONGINT:
711
            *p->pad_nlongint = written;
712
            break;
713
714
        case wxPAT_INVALID:
715
        default:
716
            return -1;
717
    }
718
719
    // if we used system's sprintf() then we now need to append the s_szScratch
720
    // buffer to the given one...
721
    switch (m_type)
722
    {
723
        case wxPAT_INT:
724
        case wxPAT_LONGINT:
725
        case wxPAT_LONGLONGINT:
726
        case wxPAT_SIZET:
727
        case wxPAT_LONGDOUBLE:
728
        case wxPAT_DOUBLE:
729
        case wxPAT_POINTER:
730
            wxASSERT(lenScratch < wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN);
731
            // NB: 1) we can compare lenMax (for CharType*, i.e. possibly
732
            //        wchar_t*) with lenScratch (char*) because this code is
733
            //        formatting integers and that will have the same length
734
            //        even in UTF-8 (the only case when char* length may be
735
            //        more than wchar_t* length of the same string)
736
            //     2) wxStrncpy converts the 2nd argument to 1st argument's
737
            //        type transparently if their types differ, so this code
738
            //        works for both instantiations
739
            if (lenMax < lenScratch)
740
            {
741
                // fill output buffer and then return -1
742
                wxStrncpy(buf, szScratch, lenMax);
743
                return -1;
744
            }
745
            wxStrncpy(buf, szScratch, lenScratch);
746
            lenCur += lenScratch;
747
            break;
748
749
        default:
750
            break;      // all other cases were completed previously
751
    }
752
753
    return lenCur;
754
}
755
756
757
// helper that parses format string
758
template<typename CharType>
759
struct wxPrintfConvSpecParser
760
{
761
    typedef wxPrintfConvSpec<CharType> ConvSpec;
762
763
    wxPrintfConvSpecParser(const CharType *fmt)
764
0
    {
765
0
        nspecs =
766
0
        nargs = 0;
767
0
        posarg_present =
768
0
        nonposarg_present = false;
769
770
0
        memset(pspec, 0, sizeof(pspec));
771
772
        // parse the format string
773
0
        for ( const CharType *toparse = fmt; *toparse != wxT('\0'); toparse++ )
774
0
        {
775
            // skip everything except format specifications
776
0
            if ( *toparse != '%' )
777
0
                continue;
778
779
            // also skip escaped percent signs
780
0
            if ( toparse[1] == '%' )
781
0
            {
782
0
                toparse++;
783
0
                continue;
784
0
            }
785
786
0
            ConvSpec *spec = &specs[nspecs];
787
0
            spec->Init();
788
789
            // attempt to parse this format specification
790
0
            if ( !spec->Parse(toparse) )
791
0
                continue;
792
793
            // advance to the end of this specifier
794
0
            toparse = spec->m_pArgEnd;
795
796
            // special handling for specifications including asterisks: we need
797
            // to reserve an extra slot (or two if asterisks were used for both
798
            // width and precision) in specs array in this case
799
0
            if ( const char *f = strchr(spec->m_szFlags, '*') )
800
0
            {
801
0
                unsigned numAsterisks = 1;
802
0
                if ( strchr(++f, '*') )
803
0
                    numAsterisks++;
804
805
0
                for ( unsigned n = 0; n < numAsterisks; n++ )
806
0
                {
807
0
                    if ( ++nspecs == wxMAX_SVNPRINTF_ARGUMENTS )
808
0
                        break;
809
810
                    // TODO: we need to support specifiers of the form "%2$*1$s"
811
                    // (this is the same as "%*s") as if any positional arguments
812
                    // are used all asterisks must be positional as well but this
813
                    // requires a lot of changes in this code (basically we'd need
814
                    // to rewrite Parse() to return "*" and conversion itself as
815
                    // separate entries)
816
0
                    if ( posarg_present )
817
0
                    {
818
0
                        wxFAIL_MSG
819
0
                        (
820
0
                            wxString::Format
821
0
                            (wxASCII_STR(
822
0
                                "Format string \"%s\" uses both positional "
823
0
                                "parameters and '*' but this is not currently "
824
0
                                "supported by this implementation, sorry."),
825
0
                                fmt
826
0
                            )
827
0
                        );
828
0
                    }
829
830
0
                    specs[nspecs] = *spec;
831
832
                    // make an entry for '*' and point to it from pspec
833
0
                    spec->Init();
834
0
                    spec->m_type = wxPAT_STAR;
835
0
                    pspec[nargs++] = spec;
836
837
0
                    spec = &specs[nspecs];
838
0
                }
839
840
                // If we hit the maximal number of arguments inside the inner
841
                // loop, break out of the outer one as well.
842
0
                if ( nspecs == wxMAX_SVNPRINTF_ARGUMENTS )
843
0
                    break;
844
0
            }
845
846
847
            // check if this is a positional or normal argument
848
0
            if ( spec->m_pos > 0 )
849
0
            {
850
                // the positional arguments start from number 1 so we need
851
                // to adjust the index
852
0
                spec->m_pos--;
853
854
                // We could be reusing an already existing argument, only
855
                // increment their number if it's really a new one.
856
0
                if ( spec->m_pos >= nargs )
857
0
                {
858
0
                    nargs = spec->m_pos + 1;
859
0
                }
860
0
                else if ( pspec[spec->m_pos] ) // Had we seen it before?
861
0
                {
862
                    // Check that the type specified this time is compatible
863
                    // with the previously-specified type.
864
0
                    wxASSERT_MSG
865
0
                    (
866
0
                        pspec[spec->m_pos]->m_type == spec->m_type,
867
0
                        "Positional parameter specified multiple times "
868
0
                        "with incompatible types."
869
0
                    );
870
0
                }
871
872
0
                posarg_present = true;
873
0
            }
874
0
            else // not a positional argument...
875
0
            {
876
0
                spec->m_pos = nargs++;
877
0
                nonposarg_present = true;
878
0
            }
879
880
            // this conversion specifier is tied to the pos-th argument...
881
0
            pspec[spec->m_pos] = spec;
882
883
0
            if ( ++nspecs == wxMAX_SVNPRINTF_ARGUMENTS )
884
0
                break;
885
0
        }
886
887
888
        // warn if we lost any arguments (the program probably will crash
889
        // anyhow because of stack corruption...)
890
0
        if ( nspecs == wxMAX_SVNPRINTF_ARGUMENTS )
891
0
        {
892
0
            wxFAIL_MSG
893
0
            (
894
0
                wxString::Format
895
0
                (wxASCII_STR(
896
0
                    "wxVsnprintf() currently supports only %d arguments, "
897
0
                    "but format string \"%s\" defines more of them.\n"
898
0
                    "You need to change wxMAX_SVNPRINTF_ARGUMENTS and "
899
0
                    "recompile if more are really needed."),
900
0
                    fmt, wxMAX_SVNPRINTF_ARGUMENTS
901
0
                )
902
0
            );
903
0
        }
904
0
    }
Unexecuted instantiation: wxPrintfConvSpecParser<char>::wxPrintfConvSpecParser(char const*)
Unexecuted instantiation: wxPrintfConvSpecParser<wchar_t>::wxPrintfConvSpecParser(wchar_t const*)
905
906
    // total number of valid elements in specs
907
    unsigned nspecs;
908
909
    // total number of arguments, also number of valid elements in pspec, and
910
    // always less than or (usually) equal to nspecs
911
    unsigned nargs;
912
913
    // all format specifications in this format string in order of their
914
    // appearance (which may be different from arguments order)
915
    ConvSpec specs[wxMAX_SVNPRINTF_ARGUMENTS];
916
917
    // pointer to specs array element for the N-th argument
918
    ConvSpec *pspec[wxMAX_SVNPRINTF_ARGUMENTS];
919
920
    // true if any positional/non-positional parameters are used
921
    bool posarg_present,
922
         nonposarg_present;
923
};
924
925
#undef APPEND_CH
926
#undef CHECK_PREC
927
928
#endif // _WX_PRIVATE_WXPRINTF_H_