Upstream tarball 9324
[amule.git] / src / libs / common / Format.cpp
blob298ddc7915b3390fbf6c8f5dbe2a1fc1df0c5d76
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2005-2008 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.
19 //
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 #ifndef _MSC_VER
28 # include <stdint.h>
29 #endif
32 //! Known type-modifiers.
33 enum Modifiers
35 //! No modifier field.
36 modNone,
37 //! Argument is interpreted as short int (integer types).
38 modShort,
39 //! Argument is interpreted as long int (integer types).
40 modLong,
41 //! Two 'long' modifieres, arguments is interpreted as long long (integer types).
42 modLongLong,
43 //! Argument is interpreted as long double (floating point types). Not supported.
44 modLongDouble
48 /**
49 * Extracts modifiers from the argument.
51 * Note that this function will possibly return wrong results
52 * for malformed format strings.
54 Modifiers getModifier(const wxString& str)
56 switch ( (wxChar)str[str.Len() - 2]) {
57 case wxT('h'): // short int (integer types).
58 return modShort;
59 case wxT('l'): // long int (interger types) or double (floating point types).
60 if ( str.Len() > 3 && str.GetChar( str.Len() - 3 ) == wxT('l') ) {
61 return modLongLong;
62 } else {
63 return modLong;
65 case wxT('L'): // long double (floating point types).
66 return modLongDouble;
67 default:
68 return modNone;
73 /** Returns true if the char is a format-type. */
74 bool isTypeChar(wxChar c)
76 switch (c) {
77 case wxT('s'): // String of characters
78 case wxT('u'): // Unsigned decimal integer
79 case wxT('i'): // Signed decimal integer
80 case wxT('d'): // Signed decimal integer
81 case wxT('c'): // Character
82 case wxT('f'): // Decimal floating point
83 case wxT('F'): // Decimal floating point
84 case wxT('x'): // Unsigned hexadecimal integer
85 case wxT('X'): // Unsigned hexadecimal integer (capital letters)
86 case wxT('o'): // Unsigned octal
87 case wxT('e'): // Scientific notation (mantise/exponent) using e character
88 case wxT('E'): // Scientific notation (mantise/exponent) using E character
89 case wxT('g'): // Use shorter %e or %f
90 case wxT('G'): // Use shorter %E or %f
91 case wxT('p'): // Not supported, still needs to be caught though
92 case wxT('n'): // Not supported, still needs to be caught though
93 return true;
96 return false;
99 /** Returns true if the char is a valid flag. */
100 bool isFlagChar(wxChar c)
102 switch (c) {
103 case wxT('+'): // Include sign for integers
104 case wxT('-'): // Left-align output
105 case wxT('#'): // Alternate form, varies
106 case wxT(' '): // Pad with spaces
107 case wxT('0'): // Pad with zeros
108 return true;
111 return false;
114 /** Returns true if the char is an integer (for width + precision). */
115 bool isIntChar(wxChar c)
117 return ((c >= wxT('0')) && (c <= wxT('9')));
121 /** Returns true if the char is a valid length modifier. */
122 bool isLengthChar(wxChar c)
124 switch (c) {
125 case wxT('h'): // Short ('hh') and char ('h')
126 case wxT('l'): // Long ('l') and long long ('ll')
127 case wxT('L'): // Double long
128 case wxT('z'): // size_t
129 case wxT('j'): // intmax_t
130 case wxT('t'): // ptrdiff_t
131 return true;
134 // Catches widths, precisons and zero-padding
135 return (c >= wxT('0')) && (c <= wxT('9'));
139 CFormat::CFormat(const wxChar* str)
141 m_fieldStart = 0;
142 m_fieldLength = 0;
143 m_skipCount = 0;
144 m_format = str;
146 if (m_format.Length()) {
147 SetCurrentField(wxEmptyString);
152 bool CFormat::IsReady() const
154 return (m_fieldStart == m_format.Length());
158 wxString CFormat::GetString() const
160 if (IsReady()) {
161 return m_result;
162 } else {
163 wxFAIL_MSG(wxT("Called GetString() before all values were passed: ") + m_format);
165 // Return as much as possible ...
166 return m_result + m_format.Mid(m_fieldStart);
171 void CFormat::SetCurrentField(const wxString& value)
173 wxCHECK_RET(m_fieldStart < m_format.Length(),
174 wxT("Setting field in already completed string: ") + m_format);
176 if (value.Length()) {
177 m_result += value;
180 enum {
181 PosNone = 0,
182 PosStart,
183 PosFlags,
184 PosWidth,
185 PosPrecision,
186 PosLength,
187 PosEnd
188 } pos = PosNone;
190 // Format strings are expected to follow the folllowing structure:
191 // %[Flags][Width][.Precision][Length]<Type>
192 for (size_t i = m_fieldStart + m_fieldLength; i < m_format.Length(); ++i) {
193 const wxChar c = m_format[i];
195 if (pos >= PosStart) {
196 m_fieldLength++;
198 if ((pos <= PosFlags) && isFlagChar(c)) {
199 pos = PosFlags;
200 } else if ((pos <= PosWidth) && isIntChar(c)) {
201 pos = PosWidth;
202 } else if ((pos < PosPrecision) && (c == wxT('.'))) {
203 pos = PosPrecision;
204 } else if ((pos == PosPrecision) && isIntChar(c)) {
205 // Nothing to do ...
206 } else if ((pos < PosLength) && isLengthChar(c)) {
207 pos = PosLength;
208 } else if ((pos == PosLength) && isLengthChar(c) && (c == m_format[i - 1])) {
209 // Nothing to do ...
210 } else if ((pos <= PosLength) && isTypeChar(c)) {
211 pos = PosEnd;
212 break;
213 } else if ((pos <= PosLength) && (c == wxT('%'))) {
214 // Append the %*% to the result
215 m_result += wxT("%");
217 pos = PosNone;
218 } else {
219 // Field is broken ...
220 break;
222 } else if (c == wxT('%')) {
223 const size_t offset = m_fieldStart + m_fieldLength;
224 // If there was anything before this, then prepend it.
225 if (offset < i) {
226 m_result += m_format.Mid(offset, i - offset);
229 // Starting a new format string
230 pos = PosStart;
231 m_fieldStart = i;
232 m_fieldLength = 1;
233 } else {
234 // Normal text, nothing to do ...
238 if (pos == PosNone) {
239 // No fields left
240 m_result += m_format.Mid(m_fieldStart + m_fieldLength);
242 m_fieldStart = m_fieldLength = m_format.Length();
243 } else if (pos != PosEnd) {
244 // A partial field was found ...
245 wxFAIL_MSG(wxT("Invalid field in format string: ") + m_format);
246 wxASSERT_MSG(m_fieldStart + m_fieldLength <= m_format.Length(),
247 wxT("Invalid field-start/length in format string: ") + m_format);
249 // Prepend the parsed part of the format-string
250 m_result += m_format.Mid(m_fieldStart, m_fieldLength);
252 // Return an empty string the next time GetCurrentField is called
253 m_skipCount++;
255 // Anything left to do?
256 if (!IsReady()) {
257 // Find the next format string
258 SetCurrentField(wxEmptyString);
264 wxString CFormat::GetCurrentField()
266 wxCHECK_MSG(m_fieldStart < m_format.Length(), wxEmptyString,
267 wxT("Passing argument to already completed string: ") + m_format);
268 wxASSERT_MSG(m_fieldStart + m_fieldLength <= m_format.Length(),
269 wxT("Invalid field-start/length in format string: ") + m_format);
271 if (m_skipCount) {
272 // The current field was invalid, so we skip it.
273 m_skipCount--;
275 return wxEmptyString;
278 return m_format.Mid(m_fieldStart, m_fieldLength);
282 wxString CFormat::GetIntegerField(const wxChar* fieldType)
284 const wxString field = GetCurrentField();
285 if (field.IsEmpty()) {
286 // Invalid or missing field ...
287 return field;
290 // Drop type and length
291 wxString newField = field;
292 while (isalpha(newField.Last())) {
293 newField.RemoveLast();
296 // Set the correct integer type
297 newField += fieldType;
299 switch ((wxChar)field.Last()) {
300 case wxT('o'): // Unsigned octal
301 case wxT('x'): // Unsigned hexadecimal integer
302 case wxT('X'): // Unsigned hexadecimal integer (capital letters)
303 // Override the default type
304 newField.Last() = field.Last();
306 case wxT('d'): // Signed decimal integer
307 case wxT('i'): // Signed decimal integer
308 case wxT('u'): // Unsigned decimal integer
309 return newField;
311 default:
312 wxFAIL_MSG(wxT("Integer value passed to non-integer format string: ") + m_format);
313 SetCurrentField(field);
314 return wxEmptyString;
319 CFormat& CFormat::operator%(double value)
321 wxString field = GetCurrentField();
322 if (field.IsEmpty()) {
323 return *this;
326 switch ( (wxChar)field.Last() ) {
327 case wxT('e'): // Scientific notation (mantise/exponent) using e character
328 case wxT('E'): // Scientific notation (mantise/exponent) using E character
329 case wxT('f'): // Decimal floating point
330 case wxT('F'): // Decimal floating point
331 case wxT('g'): // Use shorter %e or %f
332 case wxT('G'): // Use shorter %E or %f
333 wxASSERT_MSG(getModifier(field) == modNone, wxT("Invalid modifier specified for floating-point format: ") + m_format);
335 SetCurrentField(wxString::Format(field, value));
336 break;
338 default:
339 wxFAIL_MSG(wxT("Floating-point value passed to non-float format string: ") + m_format);
340 SetCurrentField(field);
343 return *this;
347 CFormat& CFormat::operator%(wxChar value)
349 wxString field = GetCurrentField();
351 if (field.IsEmpty()) {
352 // We've already asserted in GetCurrentField.
353 } else if (field.Last() != wxT('c')) {
354 wxFAIL_MSG(wxT("Char value passed to non-char format string: ") + m_format);
355 SetCurrentField(field);
356 } else {
357 SetCurrentField(wxString::Format(field, value));
360 return *this;
364 CFormat& CFormat::operator%(signed long long value)
366 wxString field = GetIntegerField(wxLongLongFmtSpec wxT("i"));
367 if (!field.IsEmpty()) {
368 SetCurrentField(wxString::Format(field, value));
371 return *this;
375 CFormat& CFormat::operator%(unsigned long long value)
377 wxString field = GetIntegerField(wxLongLongFmtSpec wxT("u"));
378 if (!field.IsEmpty()) {
379 SetCurrentField(wxString::Format(field, value));
382 return *this;
386 CFormat& CFormat::operator%(const wxString& val)
388 wxString field = GetCurrentField();
390 if (field.IsEmpty()) {
391 // We've already asserted in GetCurrentField
392 } else if (field.Last() != wxT('s')) {
393 wxFAIL_MSG(wxT("String value passed to non-string format string:") + m_format);
394 SetCurrentField(field);
395 } else if (field.GetChar(1) == wxT('.')) {
396 // A max-length is specified
397 wxString size = field.Mid( 2, field.Len() - 3 );
398 long lSize = 0;
400 // Try to convert the length-field.
401 if ((size.IsEmpty() || size.ToLong(&lSize)) && (lSize >= 0)) {
402 SetCurrentField(val.Left(lSize));
403 } else {
404 wxFAIL_MSG(wxT("Invalid value found in 'precision' field: ") + m_format);
405 SetCurrentField(field);
407 } else if (field.GetChar(1) == wxT('s')) {
408 // No limit on size, just set the string
409 SetCurrentField(val);
410 } else {
411 SetCurrentField(field);
412 wxFAIL_MSG(wxT("Malformed string format field: ") + m_format);
415 return *this;
418 CFormat& CFormat::operator%(void * value)
420 wxString field = GetCurrentField();
422 if (field.IsEmpty()) {
423 // We've already asserted in GetCurrentField.
424 } else if (field.Last() != wxT('p')) {
425 wxFAIL_MSG(wxT("Pointer value passed to non-pointer format string: ") + m_format);
426 SetCurrentField(field);
427 } else if (field != wxT("%p")) {
428 wxFAIL_MSG(wxT("Modifiers are not allowed for pointer format string: ") + m_format);
429 SetCurrentField(field);
430 } else {
431 // built-in Format for pointer is not optimal:
432 // - Windows: uppercase, no leading 0x
433 // - Linux: leading zeros missing
434 // -> format it as hex
435 if (sizeof (void *) == 8) { // 64 bit
436 SetCurrentField(wxString::Format(wxT("0x%016x"), (uintptr_t) value));
437 } else { // 32 bit
438 SetCurrentField(wxString::Format(wxT("0x%08x"), (uintptr_t) value));
442 return *this;
445 // File_checked_for_headers