Upstream tarball 9890
[amule.git] / src / libs / common / Format.cpp
blob4e1c3e6875e007f0f08807e0752f2aff0895b716
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 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <ctype.h>
33 #if defined HAVE_STDINT_H
34 # include <stdint.h>
35 #elif defined HAVE_INTTYPES_H
36 # include <inttypes.h>
37 #endif
40 //! Known type-modifiers.
41 enum Modifiers
43 //! No modifier field.
44 modNone,
45 //! Argument is interpreted as short int (integer types).
46 modShort,
47 //! Argument is interpreted as long int (integer types).
48 modLong,
49 //! Two 'long' modifieres, arguments is interpreted as long long (integer types).
50 modLongLong,
51 //! Argument is interpreted as long double (floating point types). Not supported.
52 modLongDouble
56 /**
57 * Extracts modifiers from the argument.
59 * Note that this function will possibly return wrong results
60 * for malformed format strings.
62 Modifiers getModifier(const wxString& str)
64 switch ( (wxChar)str[str.Len() - 2]) {
65 case wxT('h'): // short int (integer types).
66 return modShort;
67 case wxT('l'): // long int (interger types) or double (floating point types).
68 if ( str.Len() > 3 && str.GetChar( str.Len() - 3 ) == wxT('l') ) {
69 return modLongLong;
70 } else {
71 return modLong;
73 case wxT('L'): // long double (floating point types).
74 return modLongDouble;
75 default:
76 return modNone;
81 /** Returns true if the char is a format-type. */
82 bool isTypeChar(wxChar c)
84 switch (c) {
85 case wxT('s'): // String of characters
86 case wxT('u'): // Unsigned decimal integer
87 case wxT('i'): // Signed decimal integer
88 case wxT('d'): // Signed decimal integer
89 case wxT('c'): // Character
90 case wxT('f'): // Decimal floating point
91 case wxT('F'): // Decimal floating point
92 case wxT('x'): // Unsigned hexadecimal integer
93 case wxT('X'): // Unsigned hexadecimal integer (capital letters)
94 case wxT('o'): // Unsigned octal
95 case wxT('e'): // Scientific notation (mantise/exponent) using e character
96 case wxT('E'): // Scientific notation (mantise/exponent) using E character
97 case wxT('g'): // Use shorter %e or %f
98 case wxT('G'): // Use shorter %E or %f
99 case wxT('p'): // Not supported, still needs to be caught though
100 case wxT('n'): // Not supported, still needs to be caught though
101 return true;
104 return false;
107 /** Returns true if the char is a valid flag. */
108 bool isFlagChar(wxChar c)
110 switch (c) {
111 case wxT('+'): // Include sign for integers
112 case wxT('-'): // Left-align output
113 case wxT('#'): // Alternate form, varies
114 case wxT(' '): // Pad with spaces
115 case wxT('0'): // Pad with zeros
116 return true;
119 return false;
122 /** Returns true if the char is an integer (for width + precision). */
123 bool isIntChar(wxChar c)
125 return ((c >= wxT('0')) && (c <= wxT('9')));
129 /** Returns true if the char is a valid length modifier. */
130 bool isLengthChar(wxChar c)
132 switch (c) {
133 case wxT('h'): // Short ('hh') and char ('h')
134 case wxT('l'): // Long ('l') and long long ('ll')
135 case wxT('L'): // Double long
136 case wxT('z'): // size_t
137 case wxT('j'): // intmax_t
138 case wxT('t'): // ptrdiff_t
139 return true;
142 // Catches widths, precisons and zero-padding
143 return (c >= wxT('0')) && (c <= wxT('9'));
147 CFormat::CFormat(const wxChar* str)
149 m_fieldStart = 0;
150 m_fieldLength = 0;
151 m_skipCount = 0;
152 m_format = str;
154 if (m_format.Length()) {
155 SetCurrentField(wxEmptyString);
160 CFormat::CFormat(const wxString& str)
162 m_fieldStart = 0;
163 m_fieldLength = 0;
164 m_skipCount = 0;
165 m_format = str;
167 if (m_format.Length()) {
168 SetCurrentField(wxEmptyString);
173 bool CFormat::IsReady() const
175 return (m_fieldStart == m_format.Length());
179 wxString CFormat::GetString() const
181 if (IsReady()) {
182 return m_result;
183 } else {
184 wxFAIL_MSG(wxT("Called GetString() before all values were passed: ") + m_format);
186 // Return as much as possible ...
187 return m_result + m_format.Mid(m_fieldStart);
192 void CFormat::SetCurrentField(const wxString& value)
194 wxCHECK_RET(m_fieldStart < m_format.Length(),
195 wxT("Setting field in already completed string: ") + m_format);
197 if (value.Length()) {
198 m_result += value;
201 enum {
202 PosNone = 0,
203 PosStart,
204 PosFlags,
205 PosWidth,
206 PosPrecision,
207 PosLength,
208 PosEnd
209 } pos = PosNone;
211 // Format strings are expected to follow the folllowing structure:
212 // %[Flags][Width][.Precision][Length]<Type>
213 for (size_t i = m_fieldStart + m_fieldLength; i < m_format.Length(); ++i) {
214 const wxChar c = m_format[i];
216 if (pos >= PosStart) {
217 m_fieldLength++;
219 if ((pos <= PosFlags) && isFlagChar(c)) {
220 pos = PosFlags;
221 } else if ((pos <= PosWidth) && isIntChar(c)) {
222 pos = PosWidth;
223 } else if ((pos < PosPrecision) && (c == wxT('.'))) {
224 pos = PosPrecision;
225 } else if ((pos == PosPrecision) && isIntChar(c)) {
226 // Nothing to do ...
227 } else if ((pos < PosLength) && isLengthChar(c)) {
228 pos = PosLength;
229 } else if ((pos == PosLength) && isLengthChar(c) && (c == m_format[i - 1])) {
230 // Nothing to do ...
231 } else if ((pos <= PosLength) && isTypeChar(c)) {
232 pos = PosEnd;
233 break;
234 } else if ((pos <= PosLength) && (c == wxT('%'))) {
235 // Append the %*% to the result
236 m_result += wxT("%");
238 pos = PosNone;
239 } else {
240 // Field is broken ...
241 break;
243 } else if (c == wxT('%')) {
244 const size_t offset = m_fieldStart + m_fieldLength;
245 // If there was anything before this, then prepend it.
246 if (offset < i) {
247 m_result += m_format.Mid(offset, i - offset);
250 // Starting a new format string
251 pos = PosStart;
252 m_fieldStart = i;
253 m_fieldLength = 1;
254 } else {
255 // Normal text, nothing to do ...
259 if (pos == PosNone) {
260 // No fields left
261 m_result += m_format.Mid(m_fieldStart + m_fieldLength);
263 m_fieldStart = m_fieldLength = m_format.Length();
264 } else if (pos != PosEnd) {
265 // A partial field was found ...
266 wxFAIL_MSG(wxT("Invalid field in format string: ") + m_format);
267 wxASSERT_MSG(m_fieldStart + m_fieldLength <= m_format.Length(),
268 wxT("Invalid field-start/length in format string: ") + m_format);
270 // Prepend the parsed part of the format-string
271 m_result += m_format.Mid(m_fieldStart, m_fieldLength);
273 // Return an empty string the next time GetCurrentField is called
274 m_skipCount++;
276 // Anything left to do?
277 if (!IsReady()) {
278 // Find the next format string
279 SetCurrentField(wxEmptyString);
285 wxString CFormat::GetCurrentField()
287 wxCHECK_MSG(m_fieldStart < m_format.Length(), wxEmptyString,
288 wxT("Passing argument to already completed string: ") + m_format);
289 wxASSERT_MSG(m_fieldStart + m_fieldLength <= m_format.Length(),
290 wxT("Invalid field-start/length in format string: ") + m_format);
292 if (m_skipCount) {
293 // The current field was invalid, so we skip it.
294 m_skipCount--;
296 return wxEmptyString;
299 return m_format.Mid(m_fieldStart, m_fieldLength);
303 wxString CFormat::GetIntegerField(const wxChar* fieldType)
305 const wxString field = GetCurrentField();
306 if (field.IsEmpty()) {
307 // Invalid or missing field ...
308 return field;
311 // Drop type and length
312 wxString newField = field;
313 while (isalpha(newField.Last())) {
314 newField.RemoveLast();
317 // Set the correct integer type
318 newField += fieldType;
320 switch ((wxChar)field.Last()) {
321 case wxT('o'): // Unsigned octal
322 case wxT('x'): // Unsigned hexadecimal integer
323 case wxT('X'): // Unsigned hexadecimal integer (capital letters)
324 // Override the default type
325 newField.Last() = field.Last();
327 case wxT('d'): // Signed decimal integer
328 case wxT('i'): // Signed decimal integer
329 case wxT('u'): // Unsigned decimal integer
330 return newField;
332 default:
333 wxFAIL_MSG(wxT("Integer value passed to non-integer format string: ") + m_format);
334 SetCurrentField(field);
335 return wxEmptyString;
340 CFormat& CFormat::operator%(double value)
342 wxString field = GetCurrentField();
343 if (field.IsEmpty()) {
344 return *this;
347 switch ( (wxChar)field.Last() ) {
348 case wxT('e'): // Scientific notation (mantise/exponent) using e character
349 case wxT('E'): // Scientific notation (mantise/exponent) using E character
350 case wxT('f'): // Decimal floating point
351 case wxT('F'): // Decimal floating point
352 case wxT('g'): // Use shorter %e or %f
353 case wxT('G'): // Use shorter %E or %f
354 wxASSERT_MSG(getModifier(field) == modNone, wxT("Invalid modifier specified for floating-point format: ") + m_format);
356 SetCurrentField(wxString::Format(field, value));
357 break;
359 default:
360 wxFAIL_MSG(wxT("Floating-point value passed to non-float format string: ") + m_format);
361 SetCurrentField(field);
364 return *this;
368 CFormat& CFormat::operator%(wxChar value)
370 wxString field = GetCurrentField();
372 if (field.IsEmpty()) {
373 // We've already asserted in GetCurrentField.
374 } else if (field.Last() != wxT('c')) {
375 wxFAIL_MSG(wxT("Char value passed to non-char format string: ") + m_format);
376 SetCurrentField(field);
377 } else {
378 SetCurrentField(wxString::Format(field, value));
381 return *this;
385 CFormat& CFormat::operator%(signed long long value)
387 wxString field = GetIntegerField(WXLONGLONGFMTSPEC wxT("i"));
388 if (!field.IsEmpty()) {
389 SetCurrentField(wxString::Format(field, value));
392 return *this;
396 CFormat& CFormat::operator%(unsigned long long value)
398 wxString field = GetIntegerField(WXLONGLONGFMTSPEC wxT("u"));
399 if (!field.IsEmpty()) {
400 SetCurrentField(wxString::Format(field, value));
403 return *this;
407 CFormat& CFormat::operator%(const wxString& val)
409 wxString field = GetCurrentField();
411 if (field.IsEmpty()) {
412 // We've already asserted in GetCurrentField
413 } else if (field.Last() != wxT('s')) {
414 wxFAIL_MSG(wxT("String value passed to non-string format string:") + m_format);
415 SetCurrentField(field);
416 } else if (field.GetChar(1) == wxT('.')) {
417 // A max-length is specified
418 wxString size = field.Mid( 2, field.Len() - 3 );
419 long lSize = 0;
421 // Try to convert the length-field.
422 if ((size.IsEmpty() || size.ToLong(&lSize)) && (lSize >= 0)) {
423 SetCurrentField(val.Left(lSize));
424 } else {
425 wxFAIL_MSG(wxT("Invalid value found in 'precision' field: ") + m_format);
426 SetCurrentField(field);
428 } else if (field.GetChar(1) == wxT('s')) {
429 // No limit on size, just set the string
430 SetCurrentField(val);
431 } else {
432 SetCurrentField(field);
433 wxFAIL_MSG(wxT("Malformed string format field: ") + m_format);
436 return *this;
439 CFormat& CFormat::operator%(void * value)
441 wxString field = GetCurrentField();
443 if (field.IsEmpty()) {
444 // We've already asserted in GetCurrentField.
445 } else if (field.Last() != wxT('p')) {
446 wxFAIL_MSG(wxT("Pointer value passed to non-pointer format string: ") + m_format);
447 SetCurrentField(field);
448 } else if (field != wxT("%p")) {
449 wxFAIL_MSG(wxT("Modifiers are not allowed for pointer format string: ") + m_format);
450 SetCurrentField(field);
451 } else {
452 // built-in Format for pointer is not optimal:
453 // - Windows: uppercase, no leading 0x
454 // - Linux: leading zeros missing
455 // -> format it as hex
456 if (sizeof (void *) == 8) { // 64 bit
457 SetCurrentField(wxString::Format(wxT("0x%016x"), (uintptr_t) value));
458 } else { // 32 bit
459 SetCurrentField(wxString::Format(wxT("0x%08x"), (uintptr_t) value));
463 return *this;
466 // File_checked_for_headers