Upstream tarball 20080721
[amule.git] / src / libs / common / Format.cpp
blob79966552383fe5b70414ef34191be88390031c15
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"
28 //! Known type-modifiers.
29 enum Modifiers
31 //! No modifier field.
32 modNone,
33 //! Argument is interpreted as short int (integer types).
34 modShort,
35 //! Argument is interpreted as long int (interger types).
36 modLong,
37 //! Two 'long' modifieres, arguments is interpreted as long long (integer types).
38 modLongLong,
39 //! Argument is interpreted as long double (floating point types). Not supported.
40 modLongDouble
44 /**
45 * Extracts modifiers from the argument.
47 * Note that this function will possibly return wrong results
48 * for malformed format strings.
50 Modifiers getModifier(const wxString& str)
52 switch ( (wxChar)str[str.Len() - 2]) {
53 case wxT('h'): // short int (integer types).
54 return modShort;
55 case wxT('l'): // long int (interger types) or double (floating point types).
56 if ( str.Len() > 3 && str.GetChar( str.Len() - 3 ) == wxT('l') ) {
57 return modLongLong;
58 } else {
59 return modLong;
61 case wxT('L'): // long double (floating point types).
62 return modLongDouble;
63 default:
64 return modNone;
69 /** Returns true if the char is a format-type. */
70 bool isTypeChar(wxChar c)
72 switch (c) {
73 case wxT('s'): // String of characters
74 case wxT('u'): // Unsigned decimal integer
75 case wxT('i'): // Signed decimal integer
76 case wxT('d'): // Signed decimal integer
77 case wxT('c'): // Character
78 case wxT('f'): // Decimal floating point
79 case wxT('F'): // Decimal floating point
80 case wxT('x'): // Unsigned hexadecimal integer
81 case wxT('X'): // Unsigned hexadecimal integer (capital letters)
82 case wxT('o'): // Unsigned octal
83 case wxT('e'): // Scientific notation (mantise/exponent) using e character
84 case wxT('E'): // Scientific notation (mantise/exponent) using E character
85 case wxT('g'): // Use shorter %e or %f
86 case wxT('G'): // Use shorter %E or %f
87 case wxT('p'): // Not supported, still needs to be caught though
88 case wxT('n'): // Not supported, still needs to be caught though
89 return true;
92 return false;
95 /** Returns true if the char is a valid flag. */
96 bool isFlagChar(wxChar c)
98 switch (c) {
99 case wxT('+'): // Include sign for integers
100 case wxT('-'): // Left-align output
101 case wxT('#'): // Alternate form, varies
102 case wxT(' '): // Pad with spaces
103 case wxT('0'): // Pad with zeros
104 return true;
107 return false;
110 /** Returns true if the char is an integer (for width + precision). */
111 bool isIntChar(wxChar c)
113 return ((c >= wxT('0')) && (c <= wxT('9')));
117 /** Returns true if the cahr is a valid length modifier. */
118 bool isLengthChar(wxChar c)
120 switch (c) {
121 case wxT('h'): // Short ('hh') and char ('h')
122 case wxT('l'): // Long ('l') and long long ('ll')
123 case wxT('L'): // Double long
124 case wxT('z'): // size_t
125 case wxT('j'): // intmax_t
126 case wxT('t'): // ptrdiff_t
127 return true;
130 // Catches widths, precisons and zero-padding
131 return (c >= wxT('0')) && (c <= wxT('9'));
135 CFormat::CFormat(const wxChar* str)
137 m_fieldStart = 0;
138 m_fieldLength = 0;
139 m_skipCount = 0;
140 m_format = str;
142 if (m_format.Length()) {
143 SetCurrentField(wxEmptyString);
148 bool CFormat::IsReady() const
150 return (m_fieldStart == m_format.Length());
154 wxString CFormat::GetString() const
156 if (IsReady()) {
157 return m_result;
158 } else {
159 wxFAIL_MSG(wxT("Called GetString() before all values were passed: ") + m_format);
161 // Return as much as possible ...
162 return m_result + m_format.Mid(m_fieldStart);
167 void CFormat::SetCurrentField(const wxString& value)
169 wxCHECK_RET(m_fieldStart < m_format.Length(),
170 wxT("Setting field in already completed string: ") + m_format);
172 if (value.Length()) {
173 m_result += value;
176 enum {
177 PosNone = 0,
178 PosStart,
179 PosFlags,
180 PosWidth,
181 PosPrecision,
182 PosLength,
183 PosEnd
184 } pos = PosNone;
186 // Format strings are expected to follow the folllowing structure:
187 // %[Flags][Width][.Precision][Length]<Type>
188 for (size_t i = m_fieldStart + m_fieldLength; i < m_format.Length(); ++i) {
189 const wxChar c = m_format[i];
191 if (pos >= PosStart) {
192 m_fieldLength++;
194 if ((pos <= PosFlags) && isFlagChar(c)) {
195 pos = PosFlags;
196 } else if ((pos <= PosWidth) && isIntChar(c)) {
197 pos = PosWidth;
198 } else if ((pos < PosPrecision) && (c == wxT('.'))) {
199 pos = PosPrecision;
200 } else if ((pos == PosPrecision) && isIntChar(c)) {
201 // Nothing to do ...
202 } else if ((pos < PosLength) && isLengthChar(c)) {
203 pos = PosLength;
204 } else if ((pos == PosLength) && isLengthChar(c) && (c == m_format[i - 1])) {
205 // Nothing to do ...
206 } else if ((pos <= PosLength) && isTypeChar(c)) {
207 pos = PosEnd;
208 break;
209 } else if ((pos <= PosLength) && (c == wxT('%'))) {
210 // Append the %*% to the result
211 m_result += wxT("%");
213 pos = PosNone;
214 } else {
215 // Field is broken ...
216 break;
218 } else if (c == wxT('%')) {
219 const size_t offset = m_fieldStart + m_fieldLength;
220 // If there was anything before this, then prepend it.
221 if (offset < i) {
222 m_result += m_format.Mid(offset, i - offset);
225 // Starting a new format string
226 pos = PosStart;
227 m_fieldStart = i;
228 m_fieldLength = 1;
229 } else {
230 // Normal text, nothing to do ...
234 if (pos == PosNone) {
235 // No fields left
236 m_result += m_format.Mid(m_fieldStart + m_fieldLength);
238 m_fieldStart = m_fieldLength = m_format.Length();
239 } else if (pos != PosEnd) {
240 // A partial field was found ...
241 wxFAIL_MSG(wxT("Invalid field in format string: ") + m_format);
242 wxASSERT_MSG(m_fieldStart + m_fieldLength <= m_format.Length(),
243 wxT("Invalid field-start/length in format string: ") + m_format);
245 // Prepend the parsed part of the format-string
246 m_result += m_format.Mid(m_fieldStart, m_fieldLength);
248 // Return an empty string the next time GetCurrentField is called
249 m_skipCount++;
251 // Anything left to do?
252 if (!IsReady()) {
253 // Find the next format string
254 SetCurrentField(wxEmptyString);
260 wxString CFormat::GetCurrentField()
262 wxCHECK_MSG(m_fieldStart < m_format.Length(), wxEmptyString,
263 wxT("Passing argument to already completed string: ") + m_format);
264 wxASSERT_MSG(m_fieldStart + m_fieldLength <= m_format.Length(),
265 wxT("Invalid field-start/length in format string: ") + m_format);
267 if (m_skipCount) {
268 // The current field was invalid, so we skip it.
269 m_skipCount--;
271 return wxEmptyString;
274 return m_format.Mid(m_fieldStart, m_fieldLength);
278 wxString CFormat::GetIntegerField(const wxChar* fieldType)
280 const wxString field = GetCurrentField();
281 if (field.IsEmpty()) {
282 // Invalid or missing field ...
283 return field;
286 // Drop type and length
287 wxString newField = field;
288 while (isalpha(newField.Last())) {
289 newField.RemoveLast();
292 // Set the correct integer type
293 newField += fieldType;
295 switch ((wxChar)field.Last()) {
296 case wxT('o'): // Unsigned octal
297 case wxT('x'): // Unsigned hexadecimal integer
298 case wxT('X'): // Unsigned hexadecimal integer (capital letters)
299 // Override the default type
300 newField.Last() = field.Last();
302 case wxT('d'): // Signed decimal integer
303 case wxT('i'): // Signed decimal integer
304 case wxT('u'): // Unsigned decimal integer
305 return newField;
307 default:
308 wxFAIL_MSG(wxT("Integer value passed to non-integer format string: ") + m_format);
309 SetCurrentField(field);
310 return wxEmptyString;
315 CFormat& CFormat::operator%(double value)
317 wxString field = GetCurrentField();
318 if (field.IsEmpty()) {
319 return *this;
322 switch ( (wxChar)field.Last() ) {
323 case wxT('e'): // Scientific notation (mantise/exponent) using e character
324 case wxT('E'): // Scientific notation (mantise/exponent) using E character
325 case wxT('f'): // Decimal floating point
326 case wxT('F'): // Decimal floating point
327 case wxT('g'): // Use shorter %e or %f
328 case wxT('G'): // Use shorter %E or %f
329 wxASSERT_MSG(getModifier(field) == modNone, wxT("Invalid modifier specified for floating-point format: ") + m_format);
331 SetCurrentField(wxString::Format(field, value));
332 break;
334 default:
335 wxFAIL_MSG(wxT("Floating-point value passed to non-float format string: ") + m_format);
336 SetCurrentField(field);
339 return *this;
343 CFormat& CFormat::operator%(wxChar value)
345 wxString field = GetCurrentField();
347 if (field.IsEmpty()) {
348 // We've already asserted in GetCurrentField.
349 } else if (field.Last() != wxT('c')) {
350 wxFAIL_MSG(wxT("Char value passed to non-char format string: ") + m_format);
351 SetCurrentField(field);
352 } else {
353 SetCurrentField(wxString::Format(field, value));
356 return *this;
360 CFormat& CFormat::operator%(signed long long value)
362 wxString field = GetIntegerField(wxLongLongFmtSpec wxT("i"));
363 if (!field.IsEmpty()) {
364 SetCurrentField(wxString::Format(field, value));
367 return *this;
371 CFormat& CFormat::operator%(unsigned long long value)
373 wxString field = GetIntegerField(wxLongLongFmtSpec wxT("u"));
374 if (!field.IsEmpty()) {
375 SetCurrentField(wxString::Format(field, value));
378 return *this;
382 CFormat& CFormat::operator%(const wxString& val)
384 wxString field = GetCurrentField();
386 if (field.IsEmpty()) {
387 // We've already asserted in GetCurrentField
388 } else if (field.Last() != wxT('s')) {
389 wxFAIL_MSG(wxT("String value passed to non-string format string:") + m_format);
390 SetCurrentField(field);
391 } else if (field.GetChar(1) == wxT('.')) {
392 // A max-length is specified
393 wxString size = field.Mid( 2, field.Len() - 3 );
394 long lSize = 0;
396 // Try to convert the length-field.
397 if ((size.IsEmpty() || size.ToLong(&lSize)) && (lSize >= 0)) {
398 SetCurrentField(val.Left(lSize));
399 } else {
400 wxFAIL_MSG(wxT("Invalid value found in 'precision' field: ") + m_format);
401 SetCurrentField(field);
403 } else if (field.GetChar(1) == wxT('s')) {
404 // No limit on size, just set the string
405 SetCurrentField(val);
406 } else {
407 SetCurrentField(field);
408 wxFAIL_MSG(wxT("Malformed string format field: ") + m_format);
411 return *this;
414 // File_checked_for_headers