1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of OGRE
4     (Object-oriented Graphics Rendering Engine)
5 For the latest info, see http://www.ogre3d.org/
6 
7 Copyright (c) 2000-2013 Torus Knot Software Ltd
8 
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15 
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 -----------------------------------------------------------------------------
27 */
28 #include "OgreStableHeaders.h"
29 #include "OgreString.h"
30 #include "OgreStringVector.h"
31 
32 namespace Ogre {
33 
34 	//-----------------------------------------------------------------------
35 	const String StringUtil::BLANK;
36 	//-----------------------------------------------------------------------
trim(String & str,bool left,bool right)37     void StringUtil::trim(String& str, bool left, bool right)
38     {
39         /*
40         size_t lspaces, rspaces, len = length(), i;
41 
42         lspaces = rspaces = 0;
43 
44         if( left )
45         {
46             // Find spaces / tabs on the left
47             for( i = 0;
48                 i < len && ( at(i) == ' ' || at(i) == '\t' || at(i) == '\r');
49                 ++lspaces, ++i );
50         }
51 
52         if( right && lspaces < len )
53         {
54             // Find spaces / tabs on the right
55             for( i = len - 1;
56                 i >= 0 && ( at(i) == ' ' || at(i) == '\t' || at(i) == '\r');
57                 rspaces++, i-- );
58         }
59 
60         *this = substr(lspaces, len-lspaces-rspaces);
61         */
62         static const String delims = " \t\r";
63         if(right)
64             str.erase(str.find_last_not_of(delims)+1); // trim right
65         if(left)
66             str.erase(0, str.find_first_not_of(delims)); // trim left
67     }
68 
69     //-----------------------------------------------------------------------
split(const String & str,const String & delims,unsigned int maxSplits,bool preserveDelims)70     StringVector StringUtil::split( const String& str, const String& delims, unsigned int maxSplits, bool preserveDelims)
71     {
72         StringVector ret;
73         // Pre-allocate some space for performance
74         ret.reserve(maxSplits ? maxSplits+1 : 10);    // 10 is guessed capacity for most case
75 
76         unsigned int numSplits = 0;
77 
78         // Use STL methods
79         size_t start, pos;
80         start = 0;
81         do
82         {
83             pos = str.find_first_of(delims, start);
84             if (pos == start)
85             {
86                 // Do nothing
87                 start = pos + 1;
88             }
89             else if (pos == String::npos || (maxSplits && numSplits == maxSplits))
90             {
91                 // Copy the rest of the string
92                 ret.push_back( str.substr(start) );
93                 break;
94             }
95             else
96             {
97                 // Copy up to delimiter
98                 ret.push_back( str.substr(start, pos - start) );
99 
100                 if(preserveDelims)
101                 {
102                     // Sometimes there could be more than one delimiter in a row.
103                     // Loop until we don't find any more delims
104                     size_t delimStart = pos, delimPos;
105                     delimPos = str.find_first_not_of(delims, delimStart);
106                     if (delimPos == String::npos)
107                     {
108                         // Copy the rest of the string
109                         ret.push_back( str.substr(delimStart) );
110                     }
111                     else
112                     {
113                         ret.push_back( str.substr(delimStart, delimPos - delimStart) );
114                     }
115                 }
116 
117                 start = pos + 1;
118             }
119             // parse up to next real data
120             start = str.find_first_not_of(delims, start);
121             ++numSplits;
122 
123         } while (pos != String::npos);
124 
125 
126 
127         return ret;
128     }
129 	//-----------------------------------------------------------------------
tokenise(const String & str,const String & singleDelims,const String & doubleDelims,unsigned int maxSplits)130 	StringVector StringUtil::tokenise( const String& str, const String& singleDelims, const String& doubleDelims, unsigned int maxSplits)
131 	{
132         StringVector ret;
133         // Pre-allocate some space for performance
134         ret.reserve(maxSplits ? maxSplits+1 : 10);    // 10 is guessed capacity for most case
135 
136         unsigned int numSplits = 0;
137 		String delims = singleDelims + doubleDelims;
138 
139 		// Use STL methods
140         size_t start, pos;
141 		char curDoubleDelim = 0;
142         start = 0;
143         do
144         {
145 			if (curDoubleDelim != 0)
146 			{
147 				pos = str.find(curDoubleDelim, start);
148 			}
149 			else
150 			{
151 				pos = str.find_first_of(delims, start);
152 			}
153 
154             if (pos == start)
155             {
156 				char curDelim = str.at(pos);
157 				if (doubleDelims.find_first_of(curDelim) != String::npos)
158 				{
159 					curDoubleDelim = curDelim;
160 				}
161                 // Do nothing
162                 start = pos + 1;
163             }
164             else if (pos == String::npos || (maxSplits && numSplits == maxSplits))
165             {
166 				if (curDoubleDelim != 0)
167 				{
168 					//Missing closer. Warn or throw exception?
169 				}
170                 // Copy the rest of the string
171                 ret.push_back( str.substr(start) );
172                 break;
173             }
174             else
175             {
176 				if (curDoubleDelim != 0)
177 				{
178 					curDoubleDelim = 0;
179 				}
180 
181 				// Copy up to delimiter
182 				ret.push_back( str.substr(start, pos - start) );
183 				start = pos + 1;
184             }
185 			if (curDoubleDelim == 0)
186 			{
187 				// parse up to next real data
188 				start = str.find_first_not_of(singleDelims, start);
189 			}
190 
191             ++numSplits;
192 
193         } while (start != String::npos);
194 
195         return ret;
196     }
197     //-----------------------------------------------------------------------
toLowerCase(String & str)198     void StringUtil::toLowerCase(String& str)
199     {
200         std::transform(
201             str.begin(),
202             str.end(),
203             str.begin(),
204 			tolower);
205     }
206 
207     //-----------------------------------------------------------------------
toUpperCase(String & str)208     void StringUtil::toUpperCase(String& str)
209     {
210         std::transform(
211             str.begin(),
212             str.end(),
213             str.begin(),
214 			toupper);
215     }
216     //-----------------------------------------------------------------------
startsWith(const String & str,const String & pattern,bool lowerCase)217     bool StringUtil::startsWith(const String& str, const String& pattern, bool lowerCase)
218     {
219         size_t thisLen = str.length();
220         size_t patternLen = pattern.length();
221         if (thisLen < patternLen || patternLen == 0)
222             return false;
223 
224         String startOfThis = str.substr(0, patternLen);
225         if (lowerCase)
226 		{
227 			String lowerCasePattern = pattern;
228             StringUtil::toLowerCase(lowerCasePattern);
229             StringUtil::toLowerCase(startOfThis);
230 			return (startOfThis == lowerCasePattern);
231 		}
232 
233         return (startOfThis == pattern);
234     }
235     //-----------------------------------------------------------------------
endsWith(const String & str,const String & pattern,bool lowerCase)236     bool StringUtil::endsWith(const String& str, const String& pattern, bool lowerCase)
237     {
238         size_t thisLen = str.length();
239         size_t patternLen = pattern.length();
240         if (thisLen < patternLen || patternLen == 0)
241             return false;
242 
243         String endOfThis = str.substr(thisLen - patternLen, patternLen);
244         if (lowerCase)
245 		{
246 			String lowerCasePattern = pattern;
247             StringUtil::toLowerCase(lowerCasePattern);
248             StringUtil::toLowerCase(endOfThis);
249 			return (endOfThis == lowerCasePattern);
250 		}
251 
252         return (endOfThis == pattern);
253     }
254     //-----------------------------------------------------------------------
standardisePath(const String & init)255     String StringUtil::standardisePath(const String& init)
256     {
257         String path = init;
258 
259         std::replace( path.begin(), path.end(), '\\', '/' );
260         if( path[path.length() - 1] != '/' )
261             path += '/';
262 
263         return path;
264     }
265 	//-----------------------------------------------------------------------
normalizeFilePath(const String & init,bool makeLowerCase)266 	String StringUtil::normalizeFilePath(const String& init, bool makeLowerCase)
267 	{
268 		const char* bufferSrc = init.c_str();
269 		int pathLen = (int)init.size();
270 		int indexSrc = 0;
271 		int indexDst = 0;
272 		int metaPathArea = 0;
273 
274 		char reservedBuf[1024];
275 		char* bufferDst = reservedBuf;
276 		bool isDestAllocated = false;
277 		if (pathLen > 1023)
278 		{
279 			//if source path is to long ensure we don't do a buffer overrun by allocating some
280 			//new memory
281 			isDestAllocated = true;
282 			bufferDst = new char[pathLen + 1];
283 		}
284 
285 		//The outer loop loops over directories
286 		while (indexSrc < pathLen)
287 		{
288 			if ((bufferSrc[indexSrc] == '\\') || (bufferSrc[indexSrc] == '/'))
289 			{
290 				//check if we have a directory delimiter if so skip it (we should already
291 				//have written such a delimiter by this point
292 				++indexSrc;
293 				continue;
294 			}
295 			else
296 			{
297 				//check if there is a directory to skip of type ".\"
298 				if ((bufferSrc[indexSrc] == '.') &&
299 					((bufferSrc[indexSrc + 1] == '\\') || (bufferSrc[indexSrc + 1] == '/')))
300 				{
301 					indexSrc += 2;
302 					continue;
303 				}
304 
305 				//check if there is a directory to skip of type "..\"
306 				else if ((bufferSrc[indexSrc] == '.') && (bufferSrc[indexSrc + 1] == '.') &&
307 					((bufferSrc[indexSrc + 2] == '\\') || (bufferSrc[indexSrc + 2] == '/')))
308 				{
309 					if (indexDst > metaPathArea)
310 					{
311 						//skip a directory backward in the destination path
312 						do {
313 							--indexDst;
314 						}
315 						while ((indexDst > metaPathArea) && (bufferDst[indexDst - 1] != '/'));
316 						indexSrc += 3;
317 						continue;
318 					}
319 					else
320 					{
321 						//we are about to write "..\" to the destination buffer
322 						//ensure we will not remove this in future "skip directories"
323 						metaPathArea += 3;
324 					}
325 				}
326 			}
327 
328 			//transfer the current directory name from the source to the destination
329 			while (indexSrc < pathLen)
330 			{
331 				char curChar = bufferSrc[indexSrc];
332 				if (makeLowerCase) curChar = tolower(curChar);
333 				if ((curChar == '\\') || (curChar == '/')) curChar = '/';
334 				bufferDst[indexDst] = curChar;
335 				++indexDst;
336 				++indexSrc;
337 				if (curChar == '/') break;
338 			}
339 		}
340 		bufferDst[indexDst] = 0;
341 
342 		String normalized(bufferDst);
343 		if (isDestAllocated)
344 		{
345 			delete[] bufferDst;
346 		}
347 
348 		return normalized;
349 	}
350     //-----------------------------------------------------------------------
splitFilename(const String & qualifiedName,String & outBasename,String & outPath)351     void StringUtil::splitFilename(const String& qualifiedName,
352         String& outBasename, String& outPath)
353     {
354         String path = qualifiedName;
355         // Replace \ with / first
356         std::replace( path.begin(), path.end(), '\\', '/' );
357         // split based on final /
358         size_t i = path.find_last_of('/');
359 
360         if (i == String::npos)
361         {
362             outPath.clear();
363 			outBasename = qualifiedName;
364         }
365         else
366         {
367             outBasename = path.substr(i+1, path.size() - i - 1);
368             outPath = path.substr(0, i+1);
369         }
370 
371     }
372 	//-----------------------------------------------------------------------
splitBaseFilename(const Ogre::String & fullName,Ogre::String & outBasename,Ogre::String & outExtention)373 	void StringUtil::splitBaseFilename(const Ogre::String& fullName,
374 		Ogre::String& outBasename, Ogre::String& outExtention)
375 	{
376 		size_t i = fullName.find_last_of(".");
377 		if (i == Ogre::String::npos)
378 		{
379 			outExtention.clear();
380 			outBasename = fullName;
381 		}
382 		else
383 		{
384 			outExtention = fullName.substr(i+1);
385 			outBasename = fullName.substr(0, i);
386 		}
387 	}
388 	// ----------------------------------------------------------------------------------------------------------------------------------------------
splitFullFilename(const Ogre::String & qualifiedName,Ogre::String & outBasename,Ogre::String & outExtention,Ogre::String & outPath)389 	void StringUtil::splitFullFilename(	const Ogre::String& qualifiedName,
390 		Ogre::String& outBasename, Ogre::String& outExtention, Ogre::String& outPath )
391 	{
392 		Ogre::String fullName;
393 		splitFilename( qualifiedName, fullName, outPath );
394 		splitBaseFilename( fullName, outBasename, outExtention );
395 	}
396     //-----------------------------------------------------------------------
match(const String & str,const String & pattern,bool caseSensitive)397     bool StringUtil::match(const String& str, const String& pattern, bool caseSensitive)
398     {
399         String tmpStr = str;
400 		String tmpPattern = pattern;
401         if (!caseSensitive)
402         {
403             StringUtil::toLowerCase(tmpStr);
404             StringUtil::toLowerCase(tmpPattern);
405         }
406 
407         String::const_iterator strIt = tmpStr.begin();
408         String::const_iterator patIt = tmpPattern.begin();
409 		String::const_iterator lastWildCardIt = tmpPattern.end();
410         while (strIt != tmpStr.end() && patIt != tmpPattern.end())
411         {
412             if (*patIt == '*')
413             {
414 				lastWildCardIt = patIt;
415                 // Skip over looking for next character
416                 ++patIt;
417                 if (patIt == tmpPattern.end())
418 				{
419 					// Skip right to the end since * matches the entire rest of the string
420 					strIt = tmpStr.end();
421 				}
422 				else
423                 {
424 					// scan until we find next pattern character
425                     while(strIt != tmpStr.end() && *strIt != *patIt)
426                         ++strIt;
427                 }
428             }
429             else
430             {
431                 if (*patIt != *strIt)
432                 {
433 					if (lastWildCardIt != tmpPattern.end())
434 					{
435 						// The last wildcard can match this incorrect sequence
436 						// rewind pattern to wildcard and keep searching
437 						patIt = lastWildCardIt;
438 						lastWildCardIt = tmpPattern.end();
439 					}
440 					else
441 					{
442 						// no wildwards left
443 						return false;
444 					}
445                 }
446                 else
447                 {
448                     ++patIt;
449                     ++strIt;
450                 }
451             }
452 
453         }
454 		// If we reached the end of both the pattern and the string, we succeeded
455 		if (patIt == tmpPattern.end() && strIt == tmpStr.end())
456 		{
457         	return true;
458 		}
459 		else
460 		{
461 			return false;
462 		}
463 
464     }
465 	//-----------------------------------------------------------------------
replaceAll(const String & source,const String & replaceWhat,const String & replaceWithWhat)466 	const String StringUtil::replaceAll(const String& source, const String& replaceWhat, const String& replaceWithWhat)
467 	{
468 		String result = source;
469         String::size_type pos = 0;
470 		while(1)
471 		{
472 			pos = result.find(replaceWhat,pos);
473 			if (pos == String::npos) break;
474 			result.replace(pos,replaceWhat.size(),replaceWithWhat);
475             pos += replaceWithWhat.size();
476 		}
477 		return result;
478 	}
479 
480 }
481