debian: fix build-deps for focal
[amule.git] / src / libs / common / Format.cpp
blobb7736aed3327a0a90f31d513bce4e02494c396e5
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2005-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 //
6 // Any parts of this program derived from the xMule, lMule or eMule project,
7 // or contributed by third-party developers are copyrighted by their
8 // respective authors.
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "Format.h"
27 #include "config.h"
29 #if defined HAVE_STDINT_H
30 # include <stdint.h>
31 #elif defined HAVE_INTTYPES_H
32 # include <inttypes.h>
33 #endif
35 #include <wx/intl.h> // Needed for _()
37 #include <errno.h> // Needed for errno and EINVAL
38 #include "strerror_r.h" // Needed for mule_strerror_r()
40 /** Returns true if the char is a format-type. */
41 inline bool isTypeChar(wxChar c)
43 switch (c) {
44 case wxT('s'): // String of characters
45 case wxT('u'): // Unsigned decimal integer
46 case wxT('i'): // Signed decimal integer
47 case wxT('d'): // Signed decimal integer
48 case wxT('c'): // Character
49 case wxT('f'): // Decimal floating point
50 case wxT('F'): // Decimal floating point
51 case wxT('x'): // Unsigned hexadecimal integer
52 case wxT('X'): // Unsigned hexadecimal integer (capital letters)
53 case wxT('o'): // Unsigned octal
54 case wxT('e'): // Scientific notation (mantise/exponent) using e character
55 case wxT('E'): // Scientific notation (mantise/exponent) using E character
56 case wxT('g'): // Use shorter %e or %f
57 case wxT('G'): // Use shorter %E or %F
58 case wxT('p'): // Pointer
59 case wxT('n'): // Not supported, still needs to be caught though
60 case wxT('a'): // (C99; not in SUSv2) Double in hexadecimal notation.
61 case wxT('A'): // (C99; not in SUSv2) Double in hexadecimal notation (capital letters).
62 case wxT('C'): // (Not in C99, but in SUSv2.) Synonym for lc. Don't use. Not supported.
63 case wxT('S'): // (Not in C99, but in SUSv2.) Synonym for ls. Don't use. Not supported.
64 case wxT('m'): // (Glibc extension.) Print output of strerror(errno). No argument is required.
65 // case wxT('%'): // A `%' is written. No argument is converted. The complete conversion specification is `%%'.
66 return true;
69 return false;
72 /** Returns true if the char is a valid flag. */
73 inline bool isFlagChar(wxChar c)
75 switch (c) {
76 // C standard flags
77 case wxT('+'): // Include sign for integers
78 case wxT('-'): // Left-align output
79 case wxT('#'): // Alternate form, varies
80 case wxT(' '): // Pad with spaces
81 case wxT('0'): // Pad with zeros
82 // SUSv2
83 case wxT('\''): // For decimal conversion (i, d, u, f, F, g, G) the output is to be grouped with thousands' grouping characters if the locale information indicates any.
84 // glibc 2.2
85 case wxT('I'): // For decimal integer conversion (i, d, u) the output uses the locale's alternative output digits, if any.
86 return true;
89 return false;
92 /** Returns true if the char is a valid length modifier. */
93 inline bool isLengthChar(wxChar c)
95 switch (c) {
96 case wxT('h'): // Short ('hh') and char ('h')
97 case wxT('l'): // Long ('l') and long long ('ll')
98 case wxT('L'): // Double long
99 case wxT('z'): // size_t
100 case wxT('j'): // intmax_t
101 case wxT('t'): // ptrdiff_t
102 case wxT('q'): // quad. Synonym for 'll'. Don't use.
103 return true;
106 return false;
109 /** Returns true if the argument is a valid length modifier */
110 inline bool isValidLength(const wxString& str)
112 return ((str == wxT("hh")) || (str == wxT("h")) ||
113 (str == wxT("l")) || (str == wxT("ll")) ||
114 (str == wxT("L")) || (str == wxT("z")) ||
115 (str == wxT("j")) || (str == wxT("t")) ||
116 (str == wxT("q")));
120 enum eStringParserStates {
121 esNonFormat = 0, // Not in a format string
122 esFormatStart, // Start of a format string
123 esFormat, // Inside a format string
124 esFormatEnd, // Finished reading a format string
125 esInvalidFormat // Invalid (incomplete) format specifier
129 * State machine to extract format specifiers from the string
131 * All format strings will be extracted, regardless whether they are valid or
132 * not. Also '%%' is considered to be special format string which requires no
133 * arguments, thus it will be extracted too (that is because it has to be
134 * converted to '%').
136 static eStringParserStates stringParser[][3] = {
137 /* %-sign, type-char, other */
138 /* esNonFormat */ { esFormatStart, esNonFormat, esNonFormat },
139 /* esFormatStart */ { esFormatEnd, esFormatEnd, esFormat },
140 /* esFormat */ { esInvalidFormat, esFormatEnd, esFormat },
141 /* esFormatEnd */ { esFormatStart, esNonFormat, esNonFormat },
142 /* esInvalidFormat */ { esFormatEnd, esFormatEnd, esFormat }
145 enum eFormatParserStates {
146 efStart = 0, // Format string start
147 efArgIndex, // Argument index
148 efArgIndexEnd, // End of the argument index ('$' sign)
149 efFlagChar, // Flag character
150 efWidth, // Width field
151 efPrecStart, // Precision field start ('.' character)
152 efPrecision, // Precision field
153 efLength, // Length field
154 // The following two are terminal states, they terminate processing
155 efType, // Type character
156 efError // Invalid format specifier
160 * State machine to parse format specifiers
162 * Format specifiers are expected to follow the following structure:
163 * %[argIndex$][Flags][Width][.Precision][Length]<Type>
165 static eFormatParserStates formatParser[][7] = {
166 /* [1-9], '0', flagChar, '.', lengthChar, typeChar, '$' */
167 /* efStart */ { efArgIndex, efFlagChar, efFlagChar, efPrecStart, efLength, efType, efError, },
168 /* efArgIndex */ { efArgIndex, efArgIndex, efError, efPrecStart, efLength, efType, efArgIndexEnd, },
169 /* efArgIndexEnd */ { efWidth, efFlagChar, efFlagChar, efPrecStart, efLength, efType, efError, },
170 /* efFlagChar */ { efWidth, efError, efError, efPrecStart, efLength, efType, efError, },
171 /* efWidth */ { efWidth, efWidth, efError, efPrecStart, efLength, efType, efError, },
172 /* efPrecStart */ { efPrecision, efPrecision, efError, efError, efLength, efType, efError, },
173 /* efPrecision */ { efPrecision, efPrecision, efError, efError, efLength, efType, efError, },
174 /* efLength */ { efError, efError, efError, efError, efLength, efType, efError, }
177 // Forward-declare the specialization for const wxString&, needed by the parser
178 template<> void CFormat::ProcessArgument(FormatList::iterator, const wxString&);
180 void CFormat::Init(const wxString& str)
182 m_formatString = str;
183 m_argIndex = 0;
185 // Extract format-string-like substrings from the input
187 size_t formatStart = 0;
188 eStringParserStates state = esNonFormat;
189 for (size_t pos = 0; pos < str.length(); ++pos) {
190 if (str[pos] == wxT('%')) {
191 state = stringParser[state][0];
192 } else if (isTypeChar(str[pos])) {
193 state = stringParser[state][1];
194 } else {
195 state = stringParser[state][2];
197 switch (state) {
198 case esInvalidFormat:
199 wxFAIL_MSG(wxT("Invalid format specifier: ") + str.Mid(formatStart, pos - formatStart + 1));
200 /* fall through */
201 case esFormatStart:
202 formatStart = pos;
203 break;
204 case esFormatEnd:
206 FormatSpecifier fs;
207 fs.startPos = formatStart;
208 fs.endPos = pos;
209 fs.result = str.Mid(formatStart, pos - formatStart + 1);
210 m_formats.push_back(fs);
212 default:
213 break;
216 wxASSERT_MSG((state == esFormatEnd) || (state == esNonFormat), wxT("Incomplete format specifier: ") + str.Mid(formatStart));
219 // Parse the extracted format specifiers, removing invalid ones
220 unsigned formatCount = 0;
221 for (FormatList::iterator it = m_formats.begin(); it != m_formats.end();) {
222 if (it->result == wxT("%%")) {
223 it->argIndex = 0;
224 it->result = wxT("%");
225 ++it;
226 } else {
227 it->argIndex = ++formatCount;
228 it->flag = '\0';
229 it->width = 0;
230 it->precision = -1;
231 it->type = '\0';
232 unsigned num = 0;
233 wxString lengthModifier;
234 bool isPrecision = false;
235 eFormatParserStates state = efStart;
236 for (size_t pos = 1; pos < it->result.length(); ++pos) {
237 wxChar c = it->result[pos];
238 if ((c >= wxT('1')) && (c <= wxT('9'))) {
239 state = formatParser[state][0];
240 } else if (c == wxT('0')) {
241 state = formatParser[state][1];
242 } else if (isFlagChar(c)) {
243 state = formatParser[state][2];
244 } else if (c == wxT('.')) {
245 state = formatParser[state][3];
246 } else if (isLengthChar(c)) {
247 state = formatParser[state][4];
248 } else if (isTypeChar(c)) {
249 state = formatParser[state][5];
250 } else if (c == wxT('$')) {
251 state = formatParser[state][6];
252 } else {
253 state = efError;
255 if ((c >= wxT('0')) && (c <= wxT('9'))) {
256 num *= 10;
257 num += (c - wxT('0'));
259 switch (state) {
260 case efArgIndexEnd:
261 it->argIndex = num;
262 num = 0;
263 break;
264 case efFlagChar:
265 it->flag = c;
266 break;
267 case efPrecStart:
268 it->width = num;
269 num = 0;
270 isPrecision = true;
271 break;
272 case efLength:
273 if (isPrecision) {
274 it->precision = num;
275 } else if (num > 0) {
276 it->width = num;
278 num = 0;
279 lengthModifier += c;
280 if (!isValidLength(lengthModifier)) {
281 state = efError;
283 break;
284 case efType:
285 if (isPrecision) {
286 it->precision = num;
287 } else if (num > 0) {
288 it->width = num;
290 if (c == wxT('m')) {
291 it->argIndex = 0;
292 it->type = wxT('s');
293 int errnum = errno;
294 #if defined(HAVE_STRERROR) || defined(HAVE_STRERROR_R)
295 unsigned buflen = 256;
296 bool done = false;
297 do {
298 errno = 0;
299 char* buf = new char[buflen];
300 *buf = '\0';
301 int result = mule_strerror_r(errnum, buf, buflen);
302 if ((result == 0) || (buflen > 1024)) {
303 ProcessArgument<const wxString&>(it, wxString(buf, wxConvLocal));
304 } else if (errno == EINVAL) {
305 if (*buf == '\0') {
306 ProcessArgument<const wxString&>(it, wxString::Format(_("Unknown error %d"), errnum));
307 } else {
308 ProcessArgument<const wxString&>(it, wxString(buf, wxConvLocal));
310 } else if (errno != ERANGE) {
311 ProcessArgument<const wxString&>(it, wxString::Format(_("Unable to get error description for error %d"), errnum));
312 } else {
313 buflen <<= 1;
315 delete [] buf;
316 done = ((result == 0) || (errno != ERANGE));
317 } while (!done);
318 #else
319 wxFAIL_MSG(wxString::Format(wxT("Unable to get error description for error %d."), errnum));
320 ProcessArgument<const wxString&>(it, wxString::Format(_("Unable to get error description for error %d"), errnum));
321 #endif
322 } else {
323 it->type = c;
325 default:
326 break;
328 wxCHECK2_MSG(state != efError, break, wxT("Invalid format specifier: ") + it->result);
329 if (state == efType) {
330 // Needed by the '%m' conversion, which takes place immediately,
331 // overwriting it->result
332 break;
335 if (state == efError) {
336 it = m_formats.erase(it);
337 --formatCount;
338 } else {
339 ++it;
345 wxString CFormat::GetString() const
347 wxString result;
348 FormatList::const_iterator it = m_formats.begin();
349 if (it == m_formats.end()) {
350 result = m_formatString;
351 } else {
352 unsigned lastEnd = 0;
353 for (; it != m_formats.end(); ++it) {
354 result += m_formatString.Mid(lastEnd, it->startPos - lastEnd);
355 result += it->result;
356 lastEnd = it->endPos + 1;
358 result += m_formatString.Mid(lastEnd);
360 return result;
363 wxString CFormat::GetModifiers(FormatList::const_iterator it) const
365 wxString result = wxT("%");
366 if (it->flag != wxT('\0')) {
367 result += it->flag;
369 if (it->width > 0) {
370 result += wxString::Format(wxT("%u"), it->width);
372 if (it->precision >= 0) {
373 result += wxString::Format(wxT(".%u"), it->precision);
375 return result;
378 // Forward-declare the specialization for unsigned long long
379 template<> void CFormat::ProcessArgument(FormatList::iterator, unsigned long long);
381 // Processing a double-precision floating-point argument
382 template<>
383 void CFormat::ProcessArgument(FormatList::iterator it, double value)
385 switch (it->type) {
386 case wxT('a'):
387 case wxT('A'):
388 case wxT('e'):
389 case wxT('E'):
390 case wxT('f'):
391 case wxT('F'):
392 case wxT('g'):
393 case wxT('G'):
394 break;
395 case wxT('s'):
396 it->type = wxT('g');
397 break;
398 default:
399 wxFAIL_MSG(wxT("Floating-point value passed for non-floating-point format field: ") + it->result);
400 return;
402 it->result = wxString::Format(GetModifiers(it) + it->type, value);
405 // Processing a wxChar argument
406 template<>
407 void CFormat::ProcessArgument(FormatList::iterator it, wxChar value)
409 switch (it->type) {
410 case wxT('c'):
411 break;
412 case wxT('s'):
413 it->type = wxT('c');
414 break;
415 case wxT('u'):
416 case wxT('d'):
417 case wxT('i'):
418 case wxT('o'):
419 case wxT('x'):
420 case wxT('X'):
421 ProcessArgument(it, (unsigned long long)value);
422 return;
423 case wxT('a'):
424 case wxT('A'):
425 case wxT('e'):
426 case wxT('E'):
427 case wxT('f'):
428 case wxT('F'):
429 case wxT('g'):
430 case wxT('G'):
431 ProcessArgument(it, (double)value);
432 return;
433 default:
434 wxFAIL_MSG(wxT("Character value passed to non-character format field: ") + it->result);
435 return;
437 it->result = wxString::Format(GetModifiers(it) + it->type, value);
440 // Processing a signed long long argument
441 template<>
442 void CFormat::ProcessArgument(FormatList::iterator it, signed long long value)
444 switch (it->type) {
445 case wxT('c'):
446 ProcessArgument(it, (wxChar)value);
447 return;
448 case wxT('i'):
449 break;
450 case wxT('o'):
451 case wxT('x'):
452 case wxT('X'):
453 ProcessArgument(it, (unsigned long long)value);
454 return;
455 case wxT('u'):
456 case wxT('d'):
457 case wxT('s'):
458 it->type = wxT('i');
459 break;
460 case wxT('a'):
461 case wxT('A'):
462 case wxT('e'):
463 case wxT('E'):
464 case wxT('f'):
465 case wxT('F'):
466 case wxT('g'):
467 case wxT('G'):
468 ProcessArgument(it, (double)value);
469 return;
470 default:
471 wxFAIL_MSG(wxT("Integer value passed for non-integer format field: ") + it->result);
472 return;
474 it->result = wxString::Format(GetModifiers(it) + WXLONGLONGFMTSPEC + it->type, value);
477 // Processing an unsigned long long argument
478 template<>
479 void CFormat::ProcessArgument(FormatList::iterator it, unsigned long long value)
481 switch (it->type) {
482 case wxT('c'):
483 ProcessArgument(it, (wxChar)value);
484 return;
485 case wxT('u'):
486 case wxT('o'):
487 case wxT('x'):
488 case wxT('X'):
489 break;
490 case wxT('i'):
491 case wxT('d'):
492 case wxT('s'):
493 it->type = wxT('u');
494 break;
495 case wxT('a'):
496 case wxT('A'):
497 case wxT('e'):
498 case wxT('E'):
499 case wxT('f'):
500 case wxT('F'):
501 case wxT('g'):
502 case wxT('G'):
503 ProcessArgument(it, (double)value);
504 return;
505 default:
506 wxFAIL_MSG(wxT("Integer value passed for non-integer format field: ") + it->result);
507 return;
509 it->result = wxString::Format(GetModifiers(it) + WXLONGLONGFMTSPEC + it->type, value);
512 // Processing a wxString argument
513 template<>
514 void CFormat::ProcessArgument(FormatList::iterator it, const wxString& value)
516 if (it->type != wxT('s')) {
517 wxFAIL_MSG(wxT("String value passed for non-string format field: ") + it->result);
518 } else {
519 if (it->precision >= 0) {
520 it->result = value.Left(it->precision);
521 } else {
522 it->result = value;
524 if ((it->width > 0) && (it->result.length() < it->width)) {
525 if (it->flag == wxT('-')) {
526 it->result += wxString(it->width - it->result.length(), wxT(' '));
527 } else {
528 it->result = wxString(it->width -it->result.length(), wxT(' ')) + it->result;
534 // Processing pointer arguments
535 template<>
536 void CFormat::ProcessArgument(FormatList::iterator it, void * value)
538 if ((it->type == wxT('p')) || (it->type == wxT('s'))) {
539 // Modifiers (if any) are ignored for pointer conversions
540 // built-in Format for pointer is not consistent:
541 // - Windows: uppercase, no leading 0x
542 // - Linux: leading zeros missing
543 // -> format it as hex
544 if (sizeof(void*) == 8) {
545 // 64 bit
546 it->result = wxString::Format(wxString(wxT("0x%016")) + WXLONGLONGFMTSPEC + wxT("x"), (uintptr_t)value);
547 } else {
548 // 32 bit
549 it->result = wxString::Format(wxT("0x%08x"), (uintptr_t)value);
551 } else {
552 wxFAIL_MSG(wxT("Pointer value passed for non-pointer format field: ") + it->result);
557 // Generic argument processor template
558 template<typename _Tp>
559 CFormat& CFormat::operator%(_Tp value)
561 m_argIndex++;
562 for (FormatList::iterator it = m_formats.begin(); it != m_formats.end(); ++it) {
563 if (it->argIndex == m_argIndex) {
564 if ((it->type != wxT('n')) && (it->type != wxT('C')) && (it->type != wxT('S'))) {
565 ProcessArgument<_Tp>(it, value);
566 } else {
567 wxFAIL_MSG(wxT("Not supported conversion type in format field: ") + it->result);
571 return *this;
574 // explicit instatiation for the types we handle
575 template CFormat& CFormat::operator%<double>(double);
576 template CFormat& CFormat::operator%<wxChar>(wxChar);
577 template CFormat& CFormat::operator%<signed long long>(signed long long);
578 template CFormat& CFormat::operator%<unsigned long long>(unsigned long long);
579 template CFormat& CFormat::operator%<const wxString&>(const wxString&);
580 template CFormat& CFormat::operator%<void *>(void *);
582 // File_checked_for_headers