1587b00764e3d0bdda1d77c921234417e4af7163
[public/netxms.git] / src / libnetxms / string.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** NetXMS Foundation Library
4 ** Copyright (C) 2003-2016 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 String& String::operator =(const TCHAR *str)
76 {
77 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 String& String::operator =(const String &src)
88 {
89 if (&src == this)
90 return *this;
91 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 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 + 1);
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 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 + 1);
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 * Concatenate two strings
138 */
139 String String::operator +(const String &right) const
140 {
141 String result(*this);
142 result.append(right);
143 return result;
144 }
145
146 /**
147 * Concatenate two strings
148 */
149 String String::operator +(const TCHAR *right) const
150 {
151 String result(*this);
152 result.append(right);
153 return result;
154 }
155
156 /**
157 * Add formatted string to the end of buffer
158 */
159 void String::appendFormattedString(const TCHAR *format, ...)
160 {
161 va_list args;
162
163 va_start(args, format);
164 appendFormattedStringV(format, args);
165 va_end(args);
166 }
167
168 void String::appendFormattedStringV(const TCHAR *format, va_list args)
169 {
170 int len;
171 TCHAR *buffer;
172
173 #ifdef UNICODE
174
175 #if HAVE_VASWPRINTF
176 vaswprintf(&buffer, format, args);
177 #elif HAVE_VSCWPRINTF && HAVE_DECL_VA_COPY
178 va_list argsCopy;
179 va_copy(argsCopy, args);
180
181 len = (int)vscwprintf(format, args) + 1;
182 buffer = (WCHAR *)malloc(len * sizeof(WCHAR));
183
184 vsnwprintf(buffer, len, format, argsCopy);
185 va_end(argsCopy);
186 #else
187 // No way to determine required buffer size, guess
188 len = wcslen(format) + NumCharsW(format, L'%') * 1000 + 1;
189 buffer = (WCHAR *)malloc(len * sizeof(WCHAR));
190
191 nx_vswprintf(buffer, len, format, args);
192 #endif
193
194 #else /* UNICODE */
195
196 #if HAVE_VASPRINTF && !defined(_IPSO)
197 if (vasprintf(&buffer, format, args) == -1)
198 {
199 buffer = (char *)malloc(1);
200 *buffer = 0;
201 }
202 #elif HAVE_VSCPRINTF && HAVE_DECL_VA_COPY
203 va_list argsCopy;
204 va_copy(argsCopy, args);
205
206 len = (int)vscprintf(format, args) + 1;
207 buffer = (char *)malloc(len);
208
209 vsnprintf(buffer, len, format, argsCopy);
210 va_end(argsCopy);
211 #elif SNPRINTF_RETURNS_REQUIRED_SIZE && HAVE_DECL_VA_COPY
212 va_list argsCopy;
213 va_copy(argsCopy, args);
214
215 len = (int)vsnprintf(NULL, 0, format, args) + 1;
216 buffer = (char *)malloc(len);
217
218 vsnprintf(buffer, len, format, argsCopy);
219 va_end(argsCopy);
220 #else
221 // No way to determine required buffer size, guess
222 len = strlen(format) + NumChars(format, '%') * 1000 + 1;
223 buffer = (char *)malloc(len);
224
225 vsnprintf(buffer, len, format, args);
226 #endif
227
228 #endif /* UNICODE */
229
230 append(buffer, _tcslen(buffer));
231 free(buffer);
232 }
233
234 /**
235 * Append string to the end of buffer
236 */
237 void String::append(const TCHAR *str, size_t len)
238 {
239 if (len <= 0)
240 return;
241
242 if (m_length + len >= m_allocated)
243 {
244 m_allocated += max(m_allocationStep, len + 1);
245 m_buffer = (TCHAR *)realloc(m_buffer, m_allocated * sizeof(TCHAR));
246 }
247 memcpy(&m_buffer[m_length], str, len * sizeof(TCHAR));
248 m_length += len;
249 m_buffer[m_length] = 0;
250 }
251
252 /**
253 * Append integer
254 */
255 void String::append(INT32 n)
256 {
257 TCHAR buffer[64];
258 append(_itot(n, buffer, 10));
259 }
260
261 /**
262 * Append integer
263 */
264 void String::append(UINT32 n)
265 {
266 TCHAR buffer[64];
267 _sntprintf(buffer, 64, _T("%u"), n);
268 append(buffer);
269 }
270
271 /**
272 * Append integer
273 */
274 void String::append(INT64 n)
275 {
276 TCHAR buffer[64];
277 _sntprintf(buffer, 64, INT64_FMT, n);
278 append(buffer);
279 }
280
281 /**
282 * Append integer
283 */
284 void String::append(UINT64 n)
285 {
286 TCHAR buffer[64];
287 _sntprintf(buffer, 64, UINT64_FMT, n);
288 append(buffer);
289 }
290
291
292 /**
293 * Append multibyte string to the end of buffer
294 */
295 void String::appendMBString(const char *str, size_t len, int nCodePage)
296 {
297 #ifdef UNICODE
298 if (m_length + len >= m_allocated)
299 {
300 m_allocated += max(m_allocationStep, len + 1);
301 m_buffer = (TCHAR *)realloc(m_buffer, m_allocated * sizeof(TCHAR));
302 }
303 m_length += MultiByteToWideChar(nCodePage, (nCodePage == CP_UTF8) ? 0 : MB_PRECOMPOSED, str, (int)len, &m_buffer[m_length], (int)len);
304 m_buffer[m_length] = 0;
305 #else
306 append(str, len);
307 #endif
308 }
309
310 /**
311 * Append widechar string to the end of buffer
312 */
313 void String::appendWideString(const WCHAR *str, size_t len)
314 {
315 #ifdef UNICODE
316 append(str, len);
317 #else
318 if (m_length + len >= m_allocated)
319 {
320 m_allocated += max(m_allocationStep, len + 1);
321 m_buffer = (TCHAR *)realloc(m_buffer, m_allocated * sizeof(TCHAR));
322 }
323 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, str, len, &m_buffer[m_length], len, NULL, NULL);
324 m_length += len;
325 m_buffer[m_length] = 0;
326 #endif
327 }
328
329 /**
330 * Escape given character
331 */
332 void String::escapeCharacter(int ch, int esc)
333 {
334 if (m_buffer == NULL)
335 return;
336
337 int nCount = NumChars(m_buffer, ch);
338 if (nCount == 0)
339 return;
340
341 if (m_length + nCount >= m_allocated)
342 {
343 m_allocated += max(m_allocationStep, (size_t)nCount);
344 m_buffer = (TCHAR *)realloc(m_buffer, m_allocated * sizeof(TCHAR));
345 }
346
347 m_length += nCount;
348 for(int i = 0; m_buffer[i] != 0; i++)
349 {
350 if (m_buffer[i] == ch)
351 {
352 memmove(&m_buffer[i + 1], &m_buffer[i], (m_length - i) * sizeof(TCHAR));
353 m_buffer[i] = esc;
354 i++;
355 }
356 }
357 m_buffer[m_length] = 0;
358 }
359
360 /**
361 * Set dynamically allocated string as a new buffer
362 */
363 void String::setBuffer(TCHAR *buffer)
364 {
365 safe_free(m_buffer);
366 m_buffer = buffer;
367 if (m_buffer != NULL)
368 {
369 m_length = _tcslen(m_buffer);
370 m_allocated = m_length + 1;
371 }
372 else
373 {
374 m_length = 0;
375 m_allocated = 0;
376 }
377 }
378
379 /**
380 * Replace all occurences of source substring with destination substring
381 */
382 void String::replace(const TCHAR *pszSrc, const TCHAR *pszDst)
383 {
384 if (m_buffer == NULL)
385 return;
386
387 size_t lenSrc = _tcslen(pszSrc);
388 size_t lenDst = _tcslen(pszDst);
389
390 for(size_t i = 0; (m_length >= lenSrc) && (i <= m_length - lenSrc); i++)
391 {
392 if (!memcmp(pszSrc, &m_buffer[i], lenSrc * sizeof(TCHAR)))
393 {
394 if (lenSrc == lenDst)
395 {
396 memcpy(&m_buffer[i], pszDst, lenDst * sizeof(TCHAR));
397 i += lenDst - 1;
398 }
399 else if (lenSrc > lenDst)
400 {
401 memcpy(&m_buffer[i], pszDst, lenDst * sizeof(TCHAR));
402 i += lenDst;
403 size_t delta = lenSrc - lenDst;
404 m_length -= delta;
405 memmove(&m_buffer[i], &m_buffer[i + delta], (m_length - i + 1) * sizeof(TCHAR));
406 i--;
407 }
408 else
409 {
410 size_t delta = lenDst - lenSrc;
411 if (m_length + delta >= m_allocated)
412 {
413 m_allocated += max(m_allocationStep, delta);
414 m_buffer = (TCHAR *)realloc(m_buffer, m_allocated * sizeof(TCHAR));
415 }
416 memmove(&m_buffer[i + lenDst], &m_buffer[i + lenSrc], (m_length - i - lenSrc + 1) * sizeof(TCHAR));
417 m_length += delta;
418 memcpy(&m_buffer[i], pszDst, lenDst * sizeof(TCHAR));
419 i += lenDst - 1;
420 }
421 }
422 }
423 }
424
425 /**
426 * Extract substring into buffer
427 */
428 String String::substring(size_t start, int len) const
429 {
430 String s;
431 if ((start < m_length) && (start >= 0))
432 {
433 int count;
434 if (len == -1)
435 {
436 count = (int)(m_length - start);
437 }
438 else
439 {
440 count = min(len, (int)(m_length - start));
441 }
442 s.append(&m_buffer[start], count);
443 }
444 return s;
445 }
446
447 /**
448 * Extract substring into buffer
449 */
450 TCHAR *String::substring(size_t start, int len, TCHAR *buffer) const
451 {
452 TCHAR *s;
453 if ((start < m_length) && (start >= 0))
454 {
455 int count;
456 if (len == -1)
457 {
458 count = (int)(m_length - start);
459 }
460 else
461 {
462 count = min(len, (int)(m_length - start));
463 }
464 s = (buffer != NULL) ? buffer : (TCHAR *)malloc((count + 1) * sizeof(TCHAR));
465 memcpy(s, &m_buffer[start], count * sizeof(TCHAR));
466 s[count] = 0;
467 }
468 else
469 {
470 s = (buffer != NULL) ? buffer : (TCHAR *)malloc(sizeof(TCHAR));
471 *s = 0;
472 }
473 return s;
474 }
475
476 /**
477 * Find substring in a string
478 */
479 int String::find(const TCHAR *str, size_t start) const
480 {
481 if ((start >= m_length) || (start < 0))
482 return npos;
483
484 TCHAR *p = _tcsstr(&m_buffer[start], str);
485 return (p != NULL) ? (int)(((char *)p - (char *)m_buffer) / sizeof(TCHAR)) : npos;
486 }
487
488 /**
489 * Strip leading and trailing spaces, tabs, newlines
490 */
491 void String::trim()
492 {
493 if (m_buffer != NULL)
494 {
495 Trim(m_buffer);
496 m_length = _tcslen(m_buffer);
497 }
498 }
499
500 /**
501 * Shring string by removing trailing characters
502 */
503 void String::shrink(int chars)
504 {
505 if (m_length > 0)
506 {
507 m_length -= min(m_length, (size_t)chars);
508 if (m_buffer != NULL)
509 m_buffer[m_length] = 0;
510 }
511 }
512
513 /**
514 * Clear string
515 */
516 void String::clear()
517 {
518 m_length = 0;
519 if (m_buffer != NULL)
520 m_buffer[m_length] = 0;
521 }
522
523 /**
524 * Get content as dynamically allocated UTF-8 string
525 */
526 char *String::getUTF8String()
527 {
528 #ifdef UNICODE
529 return UTF8StringFromWideString(m_buffer);
530 #else
531 return UTF8StringFromMBString(m_buffer);
532 #endif
533 }
534
535 /**
536 * Check that two strings are equal
537 */
538 bool String::equals(const String& s) const
539 {
540 if (m_length != s.m_length)
541 return false;
542 return !memcmp(m_buffer, s.m_buffer, m_length * sizeof(TCHAR));
543 }
544
545 /**
546 * Check that two strings are equal
547 */
548 bool String::equals(const TCHAR *s) const
549 {
550 if (s == NULL)
551 return false;
552 return !_tcscmp(m_buffer, s);
553 }
554
555 /**
556 * Check that this string starts with given sub-string
557 */
558 bool String::startsWith(const String& s) const
559 {
560 if (s.m_length > m_length)
561 return false;
562 return !memcmp(m_buffer, s.m_buffer, s.m_length * sizeof(TCHAR));
563 }
564
565 /**
566 * Check that this string starts with given sub-string
567 */
568 bool String::startsWith(const TCHAR *s) const
569 {
570 if (s == NULL)
571 return false;
572 size_t l = _tcslen(s);
573 if (l > m_length)
574 return false;
575 return !memcmp(m_buffer, s, l * sizeof(TCHAR));
576 }
577
578 /**
579 * Check that this string ends with given sub-string
580 */
581 bool String::endsWith(const String& s) const
582 {
583 if (s.m_length > m_length)
584 return false;
585 return !memcmp(&m_buffer[m_length - s.m_length], s.m_buffer, s.m_length * sizeof(TCHAR));
586 }
587
588 /**
589 * Check that this string ends with given sub-string
590 */
591 bool String::endsWith(const TCHAR *s) const
592 {
593 if (s == NULL)
594 return false;
595 size_t l = _tcslen(s);
596 if (l > m_length)
597 return false;
598 return !memcmp(&m_buffer[m_length - l], s, l * sizeof(TCHAR));
599 }
600
601 /**
602 * Split string
603 */
604 StringList *String::split(const TCHAR *separator) const
605 {
606 StringList *result = new StringList();
607
608 size_t slen = _tcslen(separator);
609 if (slen == 0)
610 {
611 result->add(CHECK_NULL(m_buffer));
612 return result;
613 }
614 if (m_length < slen)
615 {
616 result->add(_T(""));
617 return result;
618 }
619
620 TCHAR *curr = m_buffer;
621 while(true)
622 {
623 TCHAR *next = _tcsstr(curr, separator);
624 if (next == NULL)
625 {
626 result->add(curr);
627 break;
628 }
629
630 *next = 0;
631 result->add(curr);
632 *next = *separator;
633 curr = next + slen;
634 }
635
636 return result;
637 }