1 // This code is in the public domain -- castanyo@yahoo.es
2 
3 #include <nvcore/StrLib.h>
4 
5 #include <math.h>	// log
6 #include <stdio.h>	// vsnprintf
7 
8 #if NV_CC_MSVC
9 #include <stdarg.h> // vsnprintf
10 #endif
11 
12 #if NV_OS_WIN32
13 #define NV_PATH_SEPARATOR '\\'
14 #else
15 #define NV_PATH_SEPARATOR '/'
16 #endif
17 
18 using namespace nv;
19 
20 namespace
21 {
strAlloc(uint size)22 	static char * strAlloc(uint size)
23 	{
24 		return static_cast<char *>(::malloc(size));
25 	}
26 
strReAlloc(char * str,uint size)27 	static char * strReAlloc(char * str, uint size)
28 	{
29 		return static_cast<char *>(::realloc(str, size));
30 	}
31 
strFree(const char * str)32 	static void strFree(const char * str)
33 	{
34 		return ::free(const_cast<char *>(str));
35 	}
36 
37 	/*static char * strDup( const char * str )
38 	{
39 		nvDebugCheck( str != NULL );
40 		uint len = uint(strlen( str ) + 1);
41 		char * dup = strAlloc( len );
42 		memcpy( dup, str, len );
43 		return dup;
44 	}*/
45 
46 	// helper function for integer to string conversion.
i2a(uint i,char * a,uint r)47 	static char * i2a( uint i, char *a, uint r )
48 	{
49 		if( i / r > 0 ) {
50 			a = i2a( i / r, a, r );
51 		}
52 		*a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % r];
53 		return a + 1;
54 	}
55 
56 	// Locale independent functions.
toUpper(char c)57 	static inline char toUpper( char c ) {
58 		return (c<'a' || c>'z') ? (c) : (c+'A'-'a');
59 	}
toLower(char c)60 	static inline char toLower( char c ) {
61 		return (c<'A' || c>'Z') ? (c) : (c+'a'-'A');
62 	}
isAlpha(char c)63 	static inline bool isAlpha( char c ) {
64 		return (c>='a' && c<='z') || (c>='A' && c<='Z');
65 	}
isDigit(char c)66 	static inline bool isDigit( char c ) {
67 		return c>='0' && c<='9';
68 	}
isAlnum(char c)69 	static inline bool isAlnum( char c ) {
70 		return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9');
71 	}
72 
73 }
74 
strCmp(const char * s1,const char * s2)75 int nv::strCmp(const char * s1, const char * s2)
76 {
77 	nvDebugCheck(s1 != NULL);
78 	nvDebugCheck(s2 != NULL);
79 	return strcmp(s1, s2);
80 }
81 
strCaseCmp(const char * s1,const char * s2)82 int nv::strCaseCmp(const char * s1, const char * s2)
83 {
84 	nvDebugCheck(s1 != NULL);
85 	nvDebugCheck(s1 != NULL);
86 #if NV_CC_MSVC
87 	return _stricmp(s1, s2);
88 #else
89 	return strcasecmp(s1, s2);
90 #endif
91 }
92 
strCpy(char * dst,int size,const char * src)93 void nv::strCpy(char * dst, int size, const char * src)
94 {
95 	nvDebugCheck(dst != NULL);
96 	nvDebugCheck(src != NULL);
97 #if NV_CC_MSVC && _MSC_VER >= 1400
98 	strcpy_s(dst, size, src);
99 #else
100 	NV_UNUSED(size);
101 	strcpy(dst, src);
102 #endif
103 }
104 
strCpy(char * dst,int size,const char * src,int len)105 void nv::strCpy(char * dst, int size, const char * src, int len)
106 {
107 	nvDebugCheck(dst != NULL);
108 	nvDebugCheck(src != NULL);
109 #if NV_CC_MSVC && _MSC_VER >= 1400
110 	strncpy_s(dst, size, src, len);
111 #else
112 	NV_UNUSED(size);
113 	strncpy(dst, src, len);
114 #endif
115 }
116 
strCat(char * dst,int size,const char * src)117 void nv::strCat(char * dst, int size, const char * src)
118 {
119 	nvDebugCheck(dst != NULL);
120 	nvDebugCheck(src != NULL);
121 #if NV_CC_MSVC && _MSC_VER >= 1400
122 	strcat_s(dst, size, src);
123 #else
124 	NV_UNUSED(size);
125 	strcat(dst, src);
126 #endif
127 }
128 
129 
130 /** Pattern matching routine. I don't remember where did I get this. */
strMatch(const char * str,const char * pat)131 bool nv::strMatch(const char * str, const char * pat)
132 {
133 	nvDebugCheck(str != NULL);
134 	nvDebugCheck(pat != NULL);
135 
136     char c2;
137 
138     while (true) {
139         if (*pat==0) {
140             if (*str==0) return true;
141             else         return false;
142         }
143         if ((*str==0) && (*pat!='*')) return false;
144         if (*pat=='*') {
145             pat++;
146             if (*pat==0) return true;
147             while (true) {
148                 if (strMatch(str, pat)) return true;
149                 if (*str==0) return false;
150                 str++;
151             }
152         }
153         if (*pat=='?') goto match;
154         if (*pat=='[') {
155             pat++;
156             while (true) {
157                 if ((*pat==']') || (*pat==0)) return false;
158                 if (*pat==*str) break;
159                 if (pat[1] == '-') {
160                     c2 = pat[2];
161                     if (c2==0) return false;
162                     if ((*pat<=*str) && (c2>=*str)) break;
163                     if ((*pat>=*str) && (c2<=*str)) break;
164                     pat+=2;
165                 }
166                 pat++;
167             }
168             while (*pat!=']') {
169                 if (*pat==0) {
170                     pat--;
171                     break;
172                 }
173                 pat++;
174             }
175             goto match;
176         }
177 
178         if (*pat == NV_PATH_SEPARATOR) {
179             pat++;
180             if (*pat==0) return false;
181         }
182         if (*pat!=*str) return false;
183 
184 match:
185         pat++;
186         str++;
187     }
188 }
189 
190 
191 
192 /** Empty string. */
StringBuilder()193 StringBuilder::StringBuilder() : m_size(0), m_str(NULL)
194 {
195 }
196 
197 /** Preallocate space. */
StringBuilder(int size_hint)198 StringBuilder::StringBuilder( int size_hint ) : m_size(size_hint)
199 {
200 	nvDebugCheck(m_size > 0);
201 	m_str = strAlloc(m_size);
202 	*m_str = '\0';
203 }
204 
205 /** Copy ctor. */
StringBuilder(const StringBuilder & s)206 StringBuilder::StringBuilder( const StringBuilder & s ) : m_size(0), m_str(NULL)
207 {
208 	copy(s);
209 }
210 
211 /** Copy string. */
StringBuilder(const char * s)212 StringBuilder::StringBuilder( const char * s ) : m_size(0), m_str(NULL)
213 {
214 	copy(s);
215 }
216 
217 /** Delete the string. */
~StringBuilder()218 StringBuilder::~StringBuilder()
219 {
220 	m_size = 0;
221 	strFree(m_str);
222 	m_str = NULL;
223 }
224 
225 
226 /** Format a string safely. */
format(const char * fmt,...)227 StringBuilder & StringBuilder::format( const char * fmt, ... )
228 {
229 	nvDebugCheck(fmt != NULL);
230 	va_list arg;
231 	va_start( arg, fmt );
232 
233 	format( fmt, arg );
234 
235 	va_end( arg );
236 
237 	return *this;
238 }
239 
240 
241 /** Format a string safely. */
format(const char * fmt,va_list arg)242 StringBuilder & StringBuilder::format( const char * fmt, va_list arg )
243 {
244 	nvDebugCheck(fmt != NULL);
245 
246 	if( m_size == 0 ) {
247 		m_size = 64;
248 		m_str = strAlloc( m_size );
249 	}
250 
251 	va_list tmp;
252 	va_copy(tmp, arg);
253 #if NV_CC_MSVC && _MSC_VER >= 1400
254 	int n = vsnprintf_s(m_str, m_size, _TRUNCATE, fmt, tmp);
255 #else
256 	int n = vsnprintf(m_str, m_size, fmt, tmp);
257 #endif
258 	va_end(tmp);
259 
260 	while( n < 0 || n >= int(m_size) ) {
261 		if( n > -1 ) {
262 			m_size = n + 1;
263 		}
264 		else {
265 			m_size *= 2;
266 		}
267 
268 		m_str = strReAlloc(m_str, m_size);
269 
270 		va_copy(tmp, arg);
271 #if NV_CC_MSVC && _MSC_VER >= 1400
272 		n = vsnprintf_s(m_str, m_size, _TRUNCATE, fmt, tmp);
273 #else
274 		n = vsnprintf(m_str, m_size, fmt, tmp);
275 #endif
276 		va_end(tmp);
277 	}
278 
279 	nvDebugCheck(n < int(m_size));
280 
281 	// Make sure it's null terminated.
282 	nvDebugCheck(m_str[n] == '\0');
283 	//str[n] = '\0';
284 
285 	return *this;
286 }
287 
288 
289 /** Append a string. */
append(const char * s)290 StringBuilder & StringBuilder::append( const char * s )
291 {
292 	nvDebugCheck(s != NULL);
293 
294 	const uint slen = uint(strlen( s ));
295 
296 	if( m_str == NULL ) {
297 		m_size = slen + 1;
298 		m_str = strAlloc(m_size);
299 		strCpy( m_str, m_size, s );
300 	}
301 	else {
302 
303 		const uint len = uint(strlen( m_str ));
304 
305 		if( m_size < len + slen + 1 ) {
306 			m_size = len + slen + 1;
307 			m_str = strReAlloc(m_str, m_size);
308 		}
309 
310 		strCat( m_str, m_size, s );
311 	}
312 
313 	return *this;
314 }
315 
316 
317 /** Append a formatted string. */
appendFormat(const char * format,...)318 StringBuilder & StringBuilder::appendFormat( const char * format, ... )
319 {
320 	nvDebugCheck( format != NULL );
321 
322 	va_list arg;
323 	va_start( arg, format );
324 
325 	appendFormat( format, arg );
326 
327 	va_end( arg );
328 
329 	return *this;
330 }
331 
332 
333 /** Append a formatted string. */
appendFormat(const char * format,va_list arg)334 StringBuilder & StringBuilder::appendFormat( const char * format, va_list arg )
335 {
336 	nvDebugCheck( format != NULL );
337 
338 	va_list tmp;
339 	va_copy(tmp, arg);
340 
341 	StringBuilder tmp_str;
342 	tmp_str.format( format, tmp );
343 	append( tmp_str );
344 
345 	va_end(tmp);
346 
347 	return *this;
348 }
349 
350 
351 /** Convert number to string in the given base. */
number(int i,int base)352 StringBuilder & StringBuilder::number( int i, int base )
353 {
354 	nvCheck( base >= 2 );
355 	nvCheck( base <= 36 );
356 
357 	// @@ This needs to be done correctly.
358 	// length = floor(log(i, base));
359 	uint len = uint(log(float(i)) / log(float(base)) + 2);	// one more if negative
360 	reserve(len);
361 
362 	if( i < 0 ) {
363 		*m_str = '-';
364 		*i2a(uint(-i), m_str+1, base) = 0;
365 	}
366 	else {
367 		*i2a(i, m_str, base) = 0;
368 	}
369 
370 	return *this;
371 }
372 
373 
374 /** Convert number to string in the given base. */
number(uint i,int base)375 StringBuilder & StringBuilder::number( uint i, int base )
376 {
377 	nvCheck( base >= 2 );
378 	nvCheck( base <= 36 );
379 
380 	// @@ This needs to be done correctly.
381 	// length = floor(log(i, base));
382 	uint len = uint(log(float(i)) / log(float(base)) - 0.5f + 1);
383 	reserve(len);
384 
385 	*i2a(i, m_str, base) = 0;
386 
387 	return *this;
388 }
389 
390 
391 /** Resize the string preserving the contents. */
reserve(uint size_hint)392 StringBuilder & StringBuilder::reserve( uint size_hint )
393 {
394 	nvCheck(size_hint != 0);
395 	if( size_hint > m_size ) {
396 		m_str = strReAlloc(m_str, size_hint);
397 		m_size = size_hint;
398 	}
399 	return *this;
400 }
401 
402 
403 /** Copy a string safely. */
copy(const char * s)404 StringBuilder & StringBuilder::copy( const char * s )
405 {
406 	nvCheck( s != NULL );
407 	uint str_size = uint(strlen( s )) + 1;
408 	reserve(str_size);
409 	strCpy( m_str, str_size, s );
410 	return *this;
411 }
412 
413 
414 /** Copy an StringBuilder. */
copy(const StringBuilder & s)415 StringBuilder & StringBuilder::copy( const StringBuilder & s )
416 {
417 	if( s.m_str == NULL ) {
418 		nvCheck( s.m_size == 0 );
419 		m_size = 0;
420 		strFree( m_str );
421 		m_str = NULL;
422 	}
423 	else {
424 		reserve( s.m_size );
425 		strCpy( m_str, s.m_size, s.m_str );
426 	}
427 	return *this;
428 }
429 
430 /** Reset the string. */
reset()431 void StringBuilder::reset()
432 {
433 	m_size = 0;
434 	strFree( m_str );
435 	m_str = NULL;
436 }
437 
438 
439 /// Get the file name from a path.
fileName() const440 const char * Path::fileName() const
441 {
442 	return fileName(m_str);
443 }
444 
445 
446 /// Get the extension from a file path.
extension() const447 const char * Path::extension() const
448 {
449 	return extension(m_str);
450 }
451 
452 
453 /// Toggles path separators (ie. \\ into /).
translatePath()454 void Path::translatePath()
455 {
456 	nvCheck( m_str != NULL );
457 
458 	for(int i = 0; ; i++) {
459 		if( m_str[i] == '\0' ) break;
460 #if NV_PATH_SEPARATOR == '/'
461 		if( m_str[i] == '\\' ) m_str[i] = NV_PATH_SEPARATOR;
462 #else
463 		if( m_str[i] == '/' ) m_str[i] = NV_PATH_SEPARATOR;
464 #endif
465 	}
466 }
467 
468 
469 /**
470  * Strip the file name from a path.
471  * @warning path cannot end with '/' o '\\', can't it?
472  */
stripFileName()473 void Path::stripFileName()
474 {
475 	nvCheck( m_str != NULL );
476 
477 	int length = (int)strlen(m_str) - 1;
478 	while (length > 0 && m_str[length] != '/' && m_str[length] != '\\'){
479 		length--;
480 	}
481 	if( length ) {
482 		m_str[length+1] = 0;
483 	}
484 	else {
485 		m_str[0] = 0;
486 	}
487 }
488 
489 
490 /// Strip the extension from a path name.
stripExtension()491 void Path::stripExtension()
492 {
493 	nvCheck( m_str != NULL );
494 
495 	int length = (int)strlen(m_str) - 1;
496 	while( length > 0 && m_str[length] != '.' ) {
497 		length--;
498 		if( m_str[length] == NV_PATH_SEPARATOR ) {
499 			return;		// no extension
500 		}
501 	}
502 	if( length ) {
503 		m_str[length] = 0;
504 	}
505 }
506 
507 
508 /// Get the path separator.
509 // static
separator()510 char Path::separator()
511 {
512 	return NV_PATH_SEPARATOR;
513 }
514 
515 // static
fileName(const char * str)516 const char * Path::fileName(const char * str)
517 {
518 	nvCheck( str != NULL );
519 
520 	int length = (int)strlen(str) - 1;
521 	while( length >= 0 && str[length] != separator() ) {
522 		length--;
523 	}
524 
525 	return &str[length+1];
526 }
527 
528 // static
extension(const char * str)529 const char * Path::extension(const char * str)
530 {
531 	nvCheck( str != NULL );
532 
533 	int length, l;
534 	l = length = (int)strlen( str );
535 	while( length > 0 && str[length] != '.' ) {
536 		length--;
537 		if( str[length] == separator() ) {
538 			return &str[l];		// no extension
539 		}
540 	}
541 	if( length == 0 ) {
542 		return &str[l];
543 	}
544 	return &str[length];
545 }
546 
547 
548 
549 /// Clone this string
clone() const550 String String::clone() const
551 {
552 	String str(data);
553 	return str;
554 }
555 
setString(const char * str)556 void String::setString(const char * str)
557 {
558 	if (str == NULL) {
559 		data = NULL;
560 	}
561 	else {
562 		allocString( str );
563 		addRef();
564 	}
565 }
566 
setString(const char * str,int length)567 void String::setString(const char * str, int length)
568 {
569 	nvDebugCheck(str != NULL);
570 
571 	allocString(str, length);
572 	addRef();
573 }
574 
setString(const StringBuilder & str)575 void String::setString(const StringBuilder & str)
576 {
577 	if (str.str() == NULL) {
578 		data =	NULL;
579 	}
580 	else {
581 		allocString(str);
582 		addRef();
583 	}
584 }
585