// // This file is part of the aMule Project. // // Copyright (c) 2005-2011 aMule Team ( admin@amule.org / http://www.amule.org ) // // Any parts of this program derived from the xMule, lMule or eMule project, // or contributed by third-party developers are copyrighted by their // respective authors. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA // #include "Format.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif #if defined HAVE_STDINT_H # include #elif defined HAVE_INTTYPES_H # include #endif #include // Needed for _() #include // Needed for errno and EINVAL #include "strerror_r.h" // Needed for mule_strerror_r() /** Returns true if the char is a format-type. */ inline bool isTypeChar(wxChar c) { switch (c) { case wxT('s'): // String of characters case wxT('u'): // Unsigned decimal integer case wxT('i'): // Signed decimal integer case wxT('d'): // Signed decimal integer case wxT('c'): // Character case wxT('f'): // Decimal floating point case wxT('F'): // Decimal floating point case wxT('x'): // Unsigned hexadecimal integer case wxT('X'): // Unsigned hexadecimal integer (capital letters) case wxT('o'): // Unsigned octal case wxT('e'): // Scientific notation (mantise/exponent) using e character case wxT('E'): // Scientific notation (mantise/exponent) using E character case wxT('g'): // Use shorter %e or %f case wxT('G'): // Use shorter %E or %F case wxT('p'): // Pointer case wxT('n'): // Not supported, still needs to be caught though case wxT('a'): // (C99; not in SUSv2) Double in hexadecimal notation. case wxT('A'): // (C99; not in SUSv2) Double in hexadecimal notation (capital letters). case wxT('C'): // (Not in C99, but in SUSv2.) Synonym for lc. Don't use. Not supported. case wxT('S'): // (Not in C99, but in SUSv2.) Synonym for ls. Don't use. Not supported. case wxT('m'): // (Glibc extension.) Print output of strerror(errno). No argument is required. // case wxT('%'): // A `%' is written. No argument is converted. The complete conversion specification is `%%'. return true; } return false; } /** Returns true if the char is a valid flag. */ inline bool isFlagChar(wxChar c) { switch (c) { // C standard flags case wxT('+'): // Include sign for integers case wxT('-'): // Left-align output case wxT('#'): // Alternate form, varies case wxT(' '): // Pad with spaces case wxT('0'): // Pad with zeros // SUSv2 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. // glibc 2.2 case wxT('I'): // For decimal integer conversion (i, d, u) the output uses the locale's alternative output digits, if any. return true; } return false; } /** Returns true if the char is a valid length modifier. */ inline bool isLengthChar(wxChar c) { switch (c) { case wxT('h'): // Short ('hh') and char ('h') case wxT('l'): // Long ('l') and long long ('ll') case wxT('L'): // Double long case wxT('z'): // size_t case wxT('j'): // intmax_t case wxT('t'): // ptrdiff_t case wxT('q'): // quad. Synonym for 'll'. Don't use. return true; } return false; } /** Returns true if the argument is a valid length modifier */ inline bool isValidLength(const wxString& str) { return ((str == wxT("hh")) || (str == wxT("h")) || (str == wxT("l")) || (str == wxT("ll")) || (str == wxT("L")) || (str == wxT("z")) || (str == wxT("j")) || (str == wxT("t")) || (str == wxT("q"))); } enum eStringParserStates { esNonFormat = 0, // Not in a format string esFormatStart, // Start of a format string esFormat, // Inside a format string esFormatEnd, // Finished reading a format string esInvalidFormat // Invalid (incomplete) format specifier }; /** * State machine to extract format specifiers from the string * * All format strings will be extracted, regardless whether they are valid or * not. Also '%%' is considered to be special format string which requires no * arguments, thus it will be extracted too (that is because it has to be * converted to '%'). */ static eStringParserStates stringParser[][3] = { /* %-sign, type-char, other */ /* esNonFormat */ { esFormatStart, esNonFormat, esNonFormat }, /* esFormatStart */ { esFormatEnd, esFormatEnd, esFormat }, /* esFormat */ { esInvalidFormat, esFormatEnd, esFormat }, /* esFormatEnd */ { esFormatStart, esNonFormat, esNonFormat }, /* esInvalidFormat */ { esFormatEnd, esFormatEnd, esFormat } }; enum eFormatParserStates { efStart = 0, // Format string start efArgIndex, // Argument index efArgIndexEnd, // End of the argument index ('$' sign) efFlagChar, // Flag character efWidth, // Width field efPrecStart, // Precision field start ('.' character) efPrecision, // Precision field efLength, // Length field // The following two are terminal states, they terminate processing efType, // Type character efError // Invalid format specifier }; /** * State machine to parse format specifiers * * Format specifiers are expected to follow the following structure: * %[argIndex$][Flags][Width][.Precision][Length] */ static eFormatParserStates formatParser[][7] = { /* [1-9], '0', flagChar, '.', lengthChar, typeChar, '$' */ /* efStart */ { efArgIndex, efFlagChar, efFlagChar, efPrecStart, efLength, efType, efError, }, /* efArgIndex */ { efArgIndex, efArgIndex, efError, efPrecStart, efLength, efType, efArgIndexEnd, }, /* efArgIndexEnd */ { efWidth, efFlagChar, efFlagChar, efPrecStart, efLength, efType, efError, }, /* efFlagChar */ { efWidth, efError, efError, efPrecStart, efLength, efType, efError, }, /* efWidth */ { efWidth, efWidth, efError, efPrecStart, efLength, efType, efError, }, /* efPrecStart */ { efPrecision, efPrecision, efError, efError, efLength, efType, efError, }, /* efPrecision */ { efPrecision, efPrecision, efError, efError, efLength, efType, efError, }, /* efLength */ { efError, efError, efError, efError, efLength, efType, efError, } }; // Forward-declare the specialization for const wxString&, needed by the parser template<> void CFormat::ProcessArgument(FormatList::iterator, const wxString&); void CFormat::Init(const wxString& str) { m_formatString = str; m_argIndex = 0; // Extract format-string-like substrings from the input { size_t formatStart = 0; eStringParserStates state = esNonFormat; for (size_t pos = 0; pos < str.length(); ++pos) { if (str[pos] == wxT('%')) { state = stringParser[state][0]; } else if (isTypeChar(str[pos])) { state = stringParser[state][1]; } else { state = stringParser[state][2]; } switch (state) { case esInvalidFormat: wxFAIL_MSG(wxT("Invalid format specifier: ") + str.Mid(formatStart, pos - formatStart + 1)); case esFormatStart: formatStart = pos; break; case esFormatEnd: { FormatSpecifier fs; fs.startPos = formatStart; fs.endPos = pos; fs.result = str.Mid(formatStart, pos - formatStart + 1); m_formats.push_back(fs); } default: break; } } wxASSERT_MSG((state == esFormatEnd) || (state == esNonFormat), wxT("Incomplete format specifier: ") + str.Mid(formatStart)); } // Parse the extracted format specifiers, removing invalid ones unsigned formatCount = 0; for (FormatList::iterator it = m_formats.begin(); it != m_formats.end();) { if (it->result == wxT("%%")) { it->argIndex = 0; it->result = wxT("%"); ++it; } else { it->argIndex = ++formatCount; it->flag = '\0'; it->width = 0; it->precision = -1; it->type = '\0'; unsigned num = 0; wxString lengthModifier; bool isPrecision = false; eFormatParserStates state = efStart; for (size_t pos = 1; pos < it->result.length(); ++pos) { wxChar c = it->result[pos]; if ((c >= wxT('1')) && (c <= wxT('9'))) { state = formatParser[state][0]; } else if (c == wxT('0')) { state = formatParser[state][1]; } else if (isFlagChar(c)) { state = formatParser[state][2]; } else if (c == wxT('.')) { state = formatParser[state][3]; } else if (isLengthChar(c)) { state = formatParser[state][4]; } else if (isTypeChar(c)) { state = formatParser[state][5]; } else if (c == wxT('$')) { state = formatParser[state][6]; } else { state = efError; } if ((c >= wxT('0')) && (c <= wxT('9'))) { num *= 10; num += (c - wxT('0')); } switch (state) { case efArgIndexEnd: it->argIndex = num; num = 0; break; case efFlagChar: it->flag = c; break; case efPrecStart: it->width = num; num = 0; isPrecision = true; break; case efLength: if (isPrecision) { it->precision = num; } else if (num > 0) { it->width = num; } num = 0; lengthModifier += c; if (!isValidLength(lengthModifier)) { state = efError; } break; case efType: if (isPrecision) { it->precision = num; } else if (num > 0) { it->width = num; } if (c == wxT('m')) { it->argIndex = 0; it->type = wxT('s'); int errnum = errno; #if defined(HAVE_STRERROR) || defined(HAVE_STRERROR_R) unsigned buflen = 256; bool done = false; do { errno = 0; char* buf = new char[buflen]; *buf = '\0'; int result = mule_strerror_r(errnum, buf, buflen); if ((result == 0) || (buflen > 1024)) { ProcessArgument(it, wxString(buf, wxConvLocal)); } else if (errno == EINVAL) { if (*buf == '\0') { ProcessArgument(it, wxString::Format(_("Unknown error %d"), errnum)); } else { ProcessArgument(it, wxString(buf, wxConvLocal)); } } else if (errno != ERANGE) { ProcessArgument(it, wxString::Format(_("Unable to get error description for error %d"), errnum)); } else { buflen <<= 1; } delete [] buf; done = ((result == 0) || (errno != ERANGE)); } while (!done); #else wxFAIL_MSG(wxString::Format(wxT("Unable to get error description for error %d."), errnum)); ProcessArgument(it, wxString::Format(_("Unable to get error description for error %d"), errnum)); #endif } else { it->type = c; } default: break; } wxCHECK2_MSG(state != efError, break, wxT("Invalid format specifier: ") + it->result); if (state == efType) { // Needed by the '%m' conversion, which takes place immediately, // overwriting it->result break; } } if (state == efError) { it = m_formats.erase(it); --formatCount; } else { ++it; } } } } wxString CFormat::GetString() const { wxString result; FormatList::const_iterator it = m_formats.begin(); if (it == m_formats.end()) { result = m_formatString; } else { unsigned lastEnd = 0; for (; it != m_formats.end(); ++it) { result += m_formatString.Mid(lastEnd, it->startPos - lastEnd); result += it->result; lastEnd = it->endPos + 1; } result += m_formatString.Mid(lastEnd); } return result; } wxString CFormat::GetModifiers(FormatList::const_iterator it) const { wxString result = wxT("%"); if (it->flag != wxT('\0')) { result += it->flag; } if (it->width > 0) { result += wxString::Format(wxT("%u"), it->width); } if (it->precision >= 0) { result += wxString::Format(wxT(".%u"), it->precision); } return result; } // Forward-declare the specialization for unsigned long long template<> void CFormat::ProcessArgument(FormatList::iterator, unsigned long long); // Processing a double-precision floating-point argument template<> void CFormat::ProcessArgument(FormatList::iterator it, double value) { switch (it->type) { case wxT('a'): case wxT('A'): case wxT('e'): case wxT('E'): case wxT('f'): case wxT('F'): case wxT('g'): case wxT('G'): break; case wxT('s'): it->type = wxT('g'); break; default: wxFAIL_MSG(wxT("Floating-point value passed for non-floating-point format field: ") + it->result); return; } it->result = wxString::Format(GetModifiers(it) + it->type, value); } // Processing a wxChar argument template<> void CFormat::ProcessArgument(FormatList::iterator it, wxChar value) { switch (it->type) { case wxT('c'): break; case wxT('s'): it->type = wxT('c'); break; case wxT('u'): case wxT('d'): case wxT('i'): case wxT('o'): case wxT('x'): case wxT('X'): ProcessArgument(it, (unsigned long long)value); return; case wxT('a'): case wxT('A'): case wxT('e'): case wxT('E'): case wxT('f'): case wxT('F'): case wxT('g'): case wxT('G'): ProcessArgument(it, (double)value); return; default: wxFAIL_MSG(wxT("Character value passed to non-character format field: ") + it->result); return; } it->result = wxString::Format(GetModifiers(it) + it->type, value); } // Processing a signed long long argument template<> void CFormat::ProcessArgument(FormatList::iterator it, signed long long value) { switch (it->type) { case wxT('c'): ProcessArgument(it, (wxChar)value); return; case wxT('i'): break; case wxT('o'): case wxT('x'): case wxT('X'): ProcessArgument(it, (unsigned long long)value); return; case wxT('u'): case wxT('d'): case wxT('s'): it->type = wxT('i'); break; case wxT('a'): case wxT('A'): case wxT('e'): case wxT('E'): case wxT('f'): case wxT('F'): case wxT('g'): case wxT('G'): ProcessArgument(it, (double)value); return; default: wxFAIL_MSG(wxT("Integer value passed for non-integer format field: ") + it->result); return; } it->result = wxString::Format(GetModifiers(it) + WXLONGLONGFMTSPEC + it->type, value); } // Processing an unsigned long long argument template<> void CFormat::ProcessArgument(FormatList::iterator it, unsigned long long value) { switch (it->type) { case wxT('c'): ProcessArgument(it, (wxChar)value); return; case wxT('u'): case wxT('o'): case wxT('x'): case wxT('X'): break; case wxT('i'): case wxT('d'): case wxT('s'): it->type = wxT('u'); break; case wxT('a'): case wxT('A'): case wxT('e'): case wxT('E'): case wxT('f'): case wxT('F'): case wxT('g'): case wxT('G'): ProcessArgument(it, (double)value); return; default: wxFAIL_MSG(wxT("Integer value passed for non-integer format field: ") + it->result); return; } it->result = wxString::Format(GetModifiers(it) + WXLONGLONGFMTSPEC + it->type, value); } // Processing a wxString argument template<> void CFormat::ProcessArgument(FormatList::iterator it, const wxString& value) { if (it->type != wxT('s')) { wxFAIL_MSG(wxT("String value passed for non-string format field: ") + it->result); } else { if (it->precision >= 0) { it->result = value.Left(it->precision); } else { it->result = value; } if ((it->width > 0) && (it->result.length() < it->width)) { if (it->flag == wxT('-')) { it->result += wxString(it->width - it->result.length(), wxT(' ')); } else { it->result = wxString(it->width -it->result.length(), wxT(' ')) + it->result; } } } } // Processing pointer arguments template<> void CFormat::ProcessArgument(FormatList::iterator it, void * value) { if ((it->type == wxT('p')) || (it->type == wxT('s'))) { // Modifiers (if any) are ignored for pointer conversions // built-in Format for pointer is not consistent: // - Windows: uppercase, no leading 0x // - Linux: leading zeros missing // -> format it as hex if (sizeof(void*) == 8) { // 64 bit it->result = wxString::Format(wxT("0x%016x"), (uintptr_t)value); } else { // 32 bit it->result = wxString::Format(wxT("0x%08x"), (uintptr_t)value); } } else { wxFAIL_MSG(wxT("Pointer value passed for non-pointer format field: ") + it->result); } } // Generic argument processor template template CFormat& CFormat::operator%(_Tp value) { m_argIndex++; for (FormatList::iterator it = m_formats.begin(); it != m_formats.end(); ++it) { if (it->argIndex == m_argIndex) { if ((it->type != wxT('n')) && (it->type != wxT('C')) && (it->type != wxT('S'))) { ProcessArgument<_Tp>(it, value); } else { wxFAIL_MSG(wxT("Not supported conversion type in format field: ") + it->result); } } } return *this; } // explicit instatiation for the types we handle template CFormat& CFormat::operator%(double); template CFormat& CFormat::operator%(wxChar); template CFormat& CFormat::operator%(signed long long); template CFormat& CFormat::operator%(unsigned long long); template CFormat& CFormat::operator%(const wxString&); template CFormat& CFormat::operator%(void *); // File_checked_for_headers