/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_ |