String class refactored; background log writer option implemented; fixed incorrect...
[public/netxms.git] / src / libnetxms / string.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** NetXMS Foundation Library
4 ** Copyright (C) 2003-2013 Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU Lesser General Public License as published
8 ** by the Free Software Foundation; either version 3 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU Lesser General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** File: string.cpp
21 **
22 **/
23
24 #include "libnetxms.h"
25
26 /**
27 * Static members
28 */
29 const int String::npos = -1;
30
31 /**
32 * Create empty string
33 */
34 String::String()
35 {
36 m_buffer = NULL;
37 m_length = 0;
38 m_allocated = 0;
39 m_allocationStep = 256;
40 }
41
42 /**
43 * Copy constructor
44 */
45 String::String(const String &src)
46 {
47 m_length = src.m_length;
48 m_allocated = src.m_length + 1;
49 m_allocationStep = src.m_allocationStep;
50 m_buffer = ((src.m_buffer != NULL) && (src.m_length > 0)) ? (TCHAR *)nx_memdup(src.m_buffer, m_allocated * sizeof(TCHAR)) : NULL;
51 }
52
53 /**
54 * Create string with given initial content
55 */
56 String::String(const TCHAR *init)
57 {
58 m_buffer = _tcsdup(init);
59 m_length = _tcslen(init);
60 m_allocated = m_length + 1;
61 m_allocationStep = 256;
62 }
63
64 /**
65 * Destructor
66 */
67 String::~String()
68 {
69 safe_free(m_buffer);
70 }
71
72 /**
73 * Operator =
74 */
75 const String& String::operator =(const TCHAR *str)
76 {
77 safe_free(m_buffer);
78 m_buffer = _tcsdup(CHECK_NULL_EX(str));
79 m_length = _tcslen(CHECK_NULL_EX(str));
80 m_allocated = m_length + 1;
81 return *this;
82 }
83
84 /**
85 * Operator =
86 */
87 const String& String::operator =(const String &src)
88 {
89 if (&src == this)
90 return *this;
91 safe_free(m_buffer);
92 m_length = src.m_length;
93 m_allocated = src.m_length + 1;
94 m_allocationStep = src.m_allocationStep;
95 m_buffer = ((src.m_buffer != NULL) && (src.m_length > 0)) ? (TCHAR *)nx_memdup(src.m_buffer, m_allocated * sizeof(TCHAR)) : NULL;
96 return *this;
97 }
98
99 /**
100 * Append given string to the end
101 */
102 const String& String::operator +=(const TCHAR *str)
103 {
104 if (str != NULL)
105 {
106 size_t len = _tcslen(str);
107 if (m_length + len >= m_allocated)
108 {
109 m_allocated += max(m_allocationStep, len);
110 m_buffer = (TCHAR *)realloc(m_buffer, m_allocated * sizeof(TCHAR));
111 }
112 _tcscpy(&m_buffer[m_length], str);
113 m_length += len;
114 }
115 return *this;
116 }
117
118 /**
119 * Append given string to the end
120 */
121 const String& String::operator +=(const String &str)
122 {
123 if (str.m_length > 0)
124 {
125 if (m_length + str.m_length >= m_allocated)
126 {
127 m_allocated += max(m_allocationStep, str.m_length);
128 m_buffer = (TCHAR *)realloc(m_buffer, m_allocated * sizeof(TCHAR));
129 }
130 memcpy(&m_buffer[m_length], str.m_buffer, (str.m_length + 1) * sizeof(TCHAR));
131 m_length += str.m_length;
132 }
133 return *this;
134 }
135
136 /**
137 * Add formatted string to the end of buffer
138 */
139 void String::appendFormattedString(const TCHAR *format, ...)
140 {
141 va_list args;
142
143 va_start(args, format);
144 appendFormattedStringV(format, args);
145 va_end(args);
146 }
147
148 void String::appendFormattedStringV(const TCHAR *format, va_list args)
149 {
150 int len;
151 TCHAR *buffer;
152
153 #ifdef UNICODE
154
155 #if HAVE_VASWPRINTF
156 vaswprintf(&buffer, format, args);
157 #elif HAVE_VSCWPRINTF && HAVE_DECL_VA_COPY
158 va_list argsCopy;
159 va_copy(argsCopy, args);
160
161 len = (int)vscwprintf(format, args) + 1;
162 buffer = (WCHAR *)malloc(len * sizeof(WCHAR));
163
164 vsnwprintf(buffer, len, format, argsCopy);
165 va_end(argsCopy);
166 #else
167 // No way to determine required buffer size, guess
168 len = wcslen(format) + NumCharsW(format, L'%') * 1000 + 1;
169 buffer = (WCHAR *)malloc(len * sizeof(WCHAR));
170
171 nx_vswprintf(buffer, len, format, args);
172 #endif
173
174 #else /* UNICODE */
175
176 #if HAVE_VASPRINTF && !defined(_IPSO)
177 if (vasprintf(&buffer, format, args) == -1)
178 {
179 buffer = (char *)malloc(1);
180 *buffer = 0;
181 }
182 #elif HAVE_VSCPRINTF && HAVE_DECL_VA_COPY
183 va_list argsCopy;
184 va_copy(argsCopy, args);
185
186 len = (int)vscprintf(format, args) + 1;
187 buffer = (char *)malloc(len);
188
189 vsnprintf(buffer, len, format, argsCopy);
190 va_end(argsCopy);
191 #elif SNPRINTF_RETURNS_REQUIRED_SIZE && HAVE_DECL_VA_COPY
192 va_list argsCopy;
193 va_copy(argsCopy, args);
194
195 len = (int)vsnprintf(NULL, 0, format, args) + 1;
196 buffer = (char *)malloc(len);
197
198 vsnprintf(buffer, len, format, argsCopy);
199 va_end(argsCopy);
200 #else
201 // No way to determine required buffer size, guess
202 len = strlen(format) + NumChars(format, '%') * 1000 + 1;
203 buffer = (char *)malloc(len);
204
205 vsnprintf(buffer, len, format, args);
206 #endif
207
208 #endif /* UNICODE */
209
210 append(buffer, _tcslen(buffer));
211 free(buffer);
212 }
213
214 /**
215 * Append string to the end of buffer
216 */
217 void String::append(const TCHAR *str, size_t len)
218 {
219 if (len <= 0)
220 return;
221
222 if (m_length + len >= m_allocated)
223 {
224 m_allocated += max(m_allocationStep, len);
225 m_buffer = (TCHAR *)realloc(m_buffer, m_allocated * sizeof(TCHAR));
226 }
227 memcpy(&m_buffer[m_length], str, len * sizeof(TCHAR));
228 m_length += len;
229 m_buffer[m_length] = 0;
230 }
231
232 /**
233 * Append multibyte string to the end of buffer
234 */
235 void String::appendMBString(const char *str, size_t len, int nCodePage)
236 {
237 #ifdef UNICODE
238 if (m_length + len >= m_allocated)
239 {
240 m_allocated += max(m_allocationStep, len);
241 m_buffer = (TCHAR *)realloc(m_buffer, m_allocated * sizeof(TCHAR));
242 }
243 m_length += MultiByteToWideChar(nCodePage, (nCodePage == CP_UTF8) ? 0 : MB_PRECOMPOSED, str, (int)len, &m_buffer[m_length], (int)len);
244 m_buffer[m_length] = 0;
245 #else
246 append(str, len);
247 #endif
248 }
249
250 /**
251 * Append widechar string to the end of buffer
252 */
253 void String::appendWideString(const WCHAR *str, size_t len)
254 {
255 #ifdef UNICODE
256 append(str, len);
257 #else
258 if (m_length + len >= m_allocated)
259 {
260 m_allocated += max(m_allocationStep, len);
261 m_buffer = (TCHAR *)realloc(m_buffer, m_allocated * sizeof(TCHAR));
262 }
263 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, str, len, &m_buffer[m_length], len, NULL, NULL);
264 m_length += len;
265 m_buffer[m_length] = 0;
266 #endif
267 }
268
269 /**
270 * Escape given character
271 */
272 void String::escapeCharacter(int ch, int esc)
273 {
274 if (m_buffer == NULL)
275 return;
276
277 int nCount = NumChars(m_buffer, ch);
278 if (nCount == 0)
279 return;
280
281 if (m_length + nCount >= m_allocated)
282 {
283 m_allocated += max(m_allocationStep, nCount);
284 m_buffer = (TCHAR *)realloc(m_buffer, m_allocated * sizeof(TCHAR));
285 }
286
287 m_length += nCount;
288 for(int i = 0; m_buffer[i] != 0; i++)
289 {
290 if (m_buffer[i] == ch)
291 {
292 memmove(&m_buffer[i + 1], &m_buffer[i], (m_length - i) * sizeof(TCHAR));
293 m_buffer[i] = esc;
294 i++;
295 }
296 }
297 m_buffer[m_length] = 0;
298 }
299
300 /**
301 * Set dynamically allocated string as a new buffer
302 */
303 void String::setBuffer(TCHAR *buffer)
304 {
305 safe_free(m_buffer);
306 m_buffer = buffer;
307 if (m_buffer != NULL)
308 {
309 m_length = _tcslen(m_buffer);
310 m_allocated = m_length + 1;
311 }
312 else
313 {
314 m_length = 0;
315 m_allocated = 0;
316 }
317 }
318
319 /**
320 * Replace all occurences of source substring with destination substring
321 */
322 void String::replace(const TCHAR *pszSrc, const TCHAR *pszDst)
323 {
324 if (m_buffer == NULL)
325 return;
326
327 size_t lenSrc = _tcslen(pszSrc);
328 size_t lenDst = _tcslen(pszDst);
329
330 for(size_t i = 0; (m_length > lenSrc) && (i <= m_length - lenSrc); i++)
331 {
332 if (!memcmp(pszSrc, &m_buffer[i], lenSrc * sizeof(TCHAR)))
333 {
334 if (lenSrc == lenDst)
335 {
336 memcpy(&m_buffer[i], pszDst, lenDst * sizeof(TCHAR));
337 i += lenDst - 1;
338 }
339 else if (lenSrc > lenDst)
340 {
341 memcpy(&m_buffer[i], pszDst, lenDst * sizeof(TCHAR));
342 i += lenDst;
343 size_t delta = lenSrc - lenDst;
344 m_length -= delta;
345 memmove(&m_buffer[i], &m_buffer[i + delta], (m_length - i + 1) * sizeof(TCHAR));
346 i--;
347 }
348 else
349 {
350 size_t delta = lenDst - lenSrc;
351 if (m_length + delta >= m_allocated)
352 {
353 m_allocated += max(m_allocationStep, delta);
354 m_buffer = (TCHAR *)realloc(m_buffer, m_allocated * sizeof(TCHAR));
355 }
356 memmove(&m_buffer[i + lenDst], &m_buffer[i + lenSrc], (m_length - i - lenSrc + 1) * sizeof(TCHAR));
357 m_length += delta;
358 memcpy(&m_buffer[i], pszDst, lenDst * sizeof(TCHAR));
359 i += lenDst - 1;
360 }
361 }
362 }
363 }
364
365 /**
366 * Extract substring into buffer
367 */
368 TCHAR *String::substring(int nStart, int nLen, TCHAR *pszBuffer)
369 {
370 int nCount;
371 TCHAR *pszOut;
372
373 if ((nStart < (int)m_length) && (nStart >= 0))
374 {
375 if (nLen == -1)
376 {
377 nCount = (int)m_length - nStart;
378 }
379 else
380 {
381 nCount = min(nLen, (int)m_length - nStart);
382 }
383 pszOut = (pszBuffer != NULL) ? pszBuffer : (TCHAR *)malloc((nCount + 1) * sizeof(TCHAR));
384 memcpy(pszOut, &m_buffer[nStart], nCount * sizeof(TCHAR));
385 pszOut[nCount] = 0;
386 }
387 else
388 {
389 pszOut = (pszBuffer != NULL) ? pszBuffer : (TCHAR *)malloc(sizeof(TCHAR));
390 *pszOut = 0;
391 }
392 return pszOut;
393 }
394
395 /**
396 * Find substring in a string
397 */
398 int String::find(const TCHAR *str, int nStart)
399 {
400 TCHAR *p;
401
402 if ((nStart >= (int)m_length) || (nStart < 0))
403 return npos;
404
405 p = _tcsstr(&m_buffer[nStart], str);
406 return (p != NULL) ? (int)(((char *)p - (char *)m_buffer) / sizeof(TCHAR)) : npos;
407 }
408
409 /**
410 * Strip leading and trailing spaces, tabs, newlines
411 */
412 void String::trim()
413 {
414 if (m_buffer != NULL)
415 {
416 Trim(m_buffer);
417 m_length = _tcslen(m_buffer);
418 }
419 }
420
421 /**
422 * Shring string by removing trailing characters
423 */
424 void String::shrink(int chars)
425 {
426 if (m_length > 0)
427 {
428 m_length -= min(m_length, chars);
429 if (m_buffer != NULL)
430 m_buffer[m_length] = 0;
431 }
432 }
433
434 /**
435 * Clear string
436 */
437 void String::clear()
438 {
439 m_length = 0;
440 if (m_buffer != NULL)
441 m_buffer[m_length] = 0;
442 }
443
444 /**
445 * Get content as dynamically allocated UTF-8 string
446 */
447 char *String::getUTF8String()
448 {
449 #ifdef UNICODE
450 return UTF8StringFromWideString(m_buffer);
451 #else
452 return strdup(m_buffer);
453 #endif
454 }