1 // =============================================================================
2 //  FILE:  StdString.h
3 //  AUTHOR:	Joe O'Leary (with outside help noted in comments)
4 //  REMARKS:
5 //		This header file declares the CStdStr template.  This template derives
6 //		the Standard C++ Library basic_string<> template and add to it the
7 //		the following conveniences:
8 //			- The full MFC CString set of functions (including implicit cast)
9 //			- writing to/reading from COM IStream interfaces
10 //			- Functional objects for use in STL algorithms
11 //
12 //		From this template, we intstantiate two classes:  CStdStringA and
13 //		CStdStringW.  The name "CStdString" is just a #define of one of these,
14 //		based upone the _UNICODE macro setting
15 //
16 //		This header also declares our own version of the MFC/ATL UNICODE-MBCS
17 //		conversion macros.  Our version looks exactly like the Microsoft's to
18 //		facilitate portability.
19 //
20 //	NOTE:
21 //		If you you use this in an MFC or ATL build, you should include either
22 //		afx.h or atlbase.h first, as appropriate.
23 //
24 //	PEOPLE WHO HAVE CONTRIBUTED TO THIS CLASS:
25 //
26 //		Several people have helped me iron out problems and othewise improve
27 //		this class.  OK, this is a long list but in my own defense, this code
28 //		has undergone two major rewrites.  Many of the improvements became
29 //		necessary after I rewrote the code as a template.  Others helped me
30 //		improve the CString facade.
31 //
32 //		Anyway, these people are (in chronological order):
33 //
34 //			- Pete the Plumber (???)
35 //			- Julian Selman
36 //			- Chris (of Melbsys)
37 //			- Dave Plummer
38 //			- John C Sipos
39 //			- Chris Sells
40 //			- Nigel Nunn
41 //			- Fan Xia
42 //			- Matthew Williams
43 //			- Carl Engman
44 //			- Mark Zeren
45 //			- Craig Watson
46 //			- Rich Zuris
47 //			- Karim Ratib
48 //			- Chris Conti
49 //			- Baptiste Lepilleur
50 //			- Greg Pickles
51 //			- Jim Cline
52 //			- Jeff Kohn
53 //			- Todd Heckel
54 //			- Ullrich Poll�hne
55 //			- Joe Vitaterna
56 //			- Joe Woodbury
57 //			- Aaron (no last name)
58 //			- Joldakowski (???)
59 //			- Scott Hathaway
60 //			- Eric Nitzche
61 //			- Pablo Presedo
62 //
63 //	REVISION HISTORY
64 //	  2001-APR-27 - StreamLoad was calculating the number of BYTES in one
65 //					case, not characters.  Thanks to Pablo Presedo for this.
66 //
67 //    2001-FEB-23 - Replace() had a bug which caused infinite loops if the
68 //					source string was empty.  Fixed thanks to Eric Nitzsche.
69 //
70 //    2001-FEB-23 - Scott Hathaway was a huge help in providing me with the
71 //					ability to build CStdString on Sun Unix systems.  He
72 //					sent me detailed build reports about what works and what
73 //					does not.  If CStdString compiles on your Unix box, you
74 //					can thank Scott for it.
75 //
76 //	  2000-DEC-29 - Joldakowski noticed one overload of Insert failed to do
77 //					range check as CString's does.  Now fixed -- thanks!
78 //
79 //	  2000-NOV-07 - Aaron pointed out that I was calling static member
80 //					functions of char_traits via a temporary.  This was not
81 //					technically wrong, but it was unnecessary and caused
82 //					problems for poor old buggy VC5.  Thanks Aaron!
83 //
84 //	  2000-JUL-11 - Joe Woodbury noted that the CString::Find docs don't match
85 //					what the CString::Find code really ends up doing.   I was
86 //					trying to match the docs.  Now I match the CString code
87 //				  - Joe also caught me truncating strings for GetBuffer() calls
88 //					when the supplied length was less than the current length.
89 //
90 //	  2000-MAY-25 - Better support for STLPORT's Standard library distribution
91 //				  - Got rid of the NSP macro - it interfered with Koenig lookup
92 //				  - Thanks to Joe Woodbury for catching a TrimLeft() bug that
93 //					I introduced in January.  Empty strings were not getting
94 //					trimmed
95 //
96 //	  2000-APR-17 - Thanks to Joe Vitaterna for pointing out that ReverseFind
97 //					is supposed to be a const function.
98 //
99 //	  2000-MAR-07 - Thanks to Ullrich Poll�hne for catching a range bug in one
100 //					of the overloads of assign.
101 //
102 //    2000-FEB-01 - You can now use CStdString on the Mac with CodeWarrior!
103 //					Thanks to Todd Heckel for helping out with this.
104 //
105 //	  2000-JAN-23 - Thanks to Jim Cline for pointing out how I could make the
106 //					Trim() function more efficient.
107 //				  - Thanks to Jeff Kohn for prompting me to find and fix a typo
108 //					in one of the addition operators that takes _bstr_t.
109 //				  - Got rid of the .CPP file -  you only need StdString.h now!
110 //
111 //	  1999-DEC-22 - Thanks to Greg Pickles for helping me identify a problem
112 //					with my implementation of CStdString::FormatV in which
113 //					resulting string might not be properly NULL terminated.
114 //
115 //	  1999-DEC-06 - Chris Conti pointed yet another basic_string<> assignment
116 //					bug that MS has not fixed.  CStdString did nothing to fix
117 //					it either but it does now!  The bug was: create a string
118 //					longer than 31 characters, get a pointer to it (via c_str())
119 //					and then assign that pointer to the original string object.
120 //					The resulting string would be empty.  Not with CStdString!
121 //
122 //	  1999-OCT-06 - BufferSet was erasing the string even when it was merely
123 //					supposed to shrink it.  Fixed.  Thanks to Chris Conti.
124 //				  - Some of the Q172398 fixes were not checking for assignment-
125 //					to-self.  Fixed.  Thanks to Baptiste Lepilleur.
126 //
127 //	  1999-AUG-20 - Improved Load() function to be more efficient by using
128 //					SizeOfResource().  Thanks to Rich Zuris for this.
129 //				  - Corrected resource ID constructor, again thanks to Rich.
130 //				  - Fixed a bug that occurred with UNICODE characters above
131 //					the first 255 ANSI ones.  Thanks to Craig Watson.
132 //				  - Added missing overloads of TrimLeft() and TrimRight().
133 //					Thanks to Karim Ratib for pointing them out
134 //
135 //	  1999-JUL-21 - Made all calls to GetBuf() with no args check length first.
136 //
137 //	  1999-JUL-10 - Improved MFC/ATL independence of conversion macros
138 //				  - Added SS_NO_REFCOUNT macro to allow you to disable any
139 //					reference-counting your basic_string<> impl. may do.
140 //				  - Improved ReleaseBuffer() to be as forgiving as CString.
141 //					Thanks for Fan Xia for helping me find this and to
142 //					Matthew Williams for pointing it out directly.
143 //
144 //	  1999-JUL-06 - Thanks to Nigel Nunn for catching a very sneaky bug in
145 //					ToLower/ToUpper.  They should call GetBuf() instead of
146 //					data() in order to ensure the changed string buffer is not
147 //					reference-counted (in those implementations that refcount).
148 //
149 //	  1999-JUL-01 - Added a true CString facade.  Now you can use CStdString as
150 //					a drop-in replacement for CString.  If you find this useful,
151 //					you can thank Chris Sells for finally convincing me to give
152 //					in and implement it.
153 //				  - Changed operators << and >> (for MFC CArchive) to serialize
154 //					EXACTLY as CString's do.  So now you can send a CString out
155 //					to a CArchive and later read it in as a CStdString.   I have
156 //					no idea why you would want to do this but you can.
157 //
158 //	  1999-JUN-21 - Changed the CStdString class into the CStdStr template.
159 //				  - Fixed FormatV() to correctly decrement the loop counter.
160 //					This was harmless bug but a bug nevertheless.  Thanks to
161 //					Chris (of Melbsys) for pointing it out
162 //				  - Changed Format() to try a normal stack-based array before
163 //					using to _alloca().
164 //				  - Updated the text conversion macros to properly use code
165 //					pages and to fit in better in MFC/ATL builds.  In other
166 //					words, I copied Microsoft's conversion stuff again.
167 //				  - Added equivalents of CString::GetBuffer, GetBufferSetLength
168 //				  - new sscpy() replacement of CStdString::CopyString()
169 //				  - a Trim() function that combines TrimRight() and TrimLeft().
170 //
171 //	  1999-MAR-13 - Corrected the "NotSpace" functional object to use _istpace()
172 //					instead of _isspace()   Thanks to Dave Plummer for this.
173 //
174 //	  1999-FEB-26 - Removed errant line (left over from testing) that #defined
175 //					_MFC_VER.  Thanks to John C Sipos for noticing this.
176 //
177 //	  1999-FEB-03 - Fixed a bug in a rarely-used overload of operator+() that
178 //					caused infinite recursion and stack overflow
179 //				  - Added member functions to simplify the process of
180 //					persisting CStdStrings to/from DCOM IStream interfaces
181 //				  - Added functional objects (e.g. StdStringLessNoCase) that
182 //					allow CStdStrings to be used as keys STL map objects with
183 //					case-insensitive comparison
184 //				  - Added array indexing operators (i.e. operator[]).  I
185 //					originally assumed that these were unnecessary and would be
186 //					inherited from basic_string.  However, without them, Visual
187 //					C++ complains about ambiguous overloads when you try to use
188 //					them.  Thanks to Julian Selman to pointing this out.
189 //
190 //	  1998-FEB-?? - Added overloads of assign() function to completely account
191 //					for Q172398 bug.  Thanks to "Pete the Plumber" for this
192 //
193 //	  1998-FEB-?? - Initial submission
194 //
195 // COPYRIGHT:
196 //		1999 Joseph M. O'Leary.  This code is free.  Use it anywhere you want.
197 //		Rewrite it, restructure it, whatever.  Please don't blame me if it makes
198 //		your $30 billion dollar satellite explode in orbit.  If you redistribute
199 //		it in any form, I'd appreciate it if you would leave this notice here.
200 //
201 //		If you find any bugs, please let me know:
202 //
203 //				jmoleary@earthlink.net
204 //				http://home.earthlink.net/~jmoleary
205 // =============================================================================
206 
207 // Avoid multiple inclusion the VC++ way,
208 // Turn off browser references
209 // Turn off unavoidable compiler warnings
210 
211 #if defined(_MSC_VER) && (_MSC_VER > 1100)
212 	#pragma once
213 	#pragma component(browser, off, references, "CStdString")
214 	#pragma warning (disable : 4290) // C++ Exception Specification ignored
215 	#pragma warning (disable : 4127) // Conditional expression is constant
216 	#pragma warning (disable : 4097) // typedef name used as synonym for class name
217 #endif
218 
219 #ifndef STDSTRING_H
220 #define STDSTRING_H
221 
222 // MACRO: SS_NO_REFCOUNT:
223 //		turns off reference counting at the assignment level
224 //		I define this by default.  comment it out if you don't want it.
225 
226 #define SS_NO_REFCOUNT
227 
228 // In non-Visual C++ and/or non-Win32 builds, we can't use some cool stuff.
229 
230 #if !defined(_MSC_VER) || !defined(_WIN32)
231 	#define SS_ANSI
232 #endif
233 
234 // Avoid legacy code screw up: if _UNICODE is defined, UNICODE must be as well
235 
236 #if defined (_UNICODE) && !defined (UNICODE)
237 	#define UNICODE
238 #endif
239 #if defined (UNICODE) && !defined (_UNICODE)
240 	#define _UNICODE
241 #endif
242 
243 // -----------------------------------------------------------------------------
244 // MIN and MAX.  The Standard C++ template versions go by so many names (at
245 // at least in the MS implementation) that you never know what's available
246 // -----------------------------------------------------------------------------
247 template<class Type>
SSMIN(const Type & arg1,const Type & arg2)248 inline const Type& SSMIN(const Type& arg1, const Type& arg2)
249 {
250 	return arg2 < arg1 ? arg2 : arg1;
251 }
252 template<class Type>
SSMAX(const Type & arg1,const Type & arg2)253 inline const Type& SSMAX(const Type& arg1, const Type& arg2)
254 {
255 	return arg2 > arg1 ? arg2 : arg1;
256 }
257 
258 // If they have not #included W32Base.h (part of my W32 utility library) then
259 // we need to define some stuff.  Otherwise, this is all defined there.
260 
261 #if !defined(W32BASE_H)
262 
263 	// If they want us to use only standard C++ stuff (no Win32 stuff)
264 
265 	#ifdef SS_ANSI
266 
267 		// On non-Win32 platforms, there is no TCHAR.H so define what we need
268 
269 		#ifndef _WIN32
270 
271 			typedef const char*		PCSTR;
272 			typedef char*			PSTR;
273 			typedef const wchar_t*	PCWSTR;
274 			typedef wchar_t*		PWSTR;
275 			#ifdef UNICODE
276 				typedef wchar_t		TCHAR;
277 			#else
278 				typedef char		TCHAR;
279 			#endif
280 			typedef wchar_t			OLECHAR;
281 
282 		#else
283 
284 			#include <TCHAR.H>
285 			#include <WTYPES.H>
286 			#ifndef STRICT
287 				#define STRICT
288 			#endif
289 
290 		#endif	// #ifndef _WIN32
291 
292 
293 		// Make sure ASSERT and verify are defined in an ANSI fashion
294 
295 		#ifndef ASSERT
296 			#include <assert.h>
297 			#define ASSERT(f) assert((f))
298 		#endif
299 		#ifndef VERIFY
300 			#ifdef _DEBUG
301 				#define VERIFY(x) ASSERT((x))
302 			#else
303 				#define VERIFY(x) x
304 			#endif
305 		#endif
306 
307 	#else // #ifdef SS_ANSI
308 
309 		#include <TCHAR.H>
310 		#include <WTYPES.H>
311 		#ifndef STRICT
312 			#define STRICT
313 		#endif
314 
315 		// Make sure ASSERT and verify are defined
316 
317 		#ifndef ASSERT
318 			#include <crtdbg.h>
319 			#define ASSERT(f) _ASSERTE((f))
320 		#endif
321 		#ifndef VERIFY
322 			#ifdef _DEBUG
323 				#define VERIFY(x) ASSERT((x))
324 			#else
325 				#define VERIFY(x) x
326 			#endif
327 		#endif
328 
329 	#endif // #ifdef SS_ANSI
330 
331 	#ifndef UNUSED
332 		#define UNUSED(x) x
333 	#endif
334 
335 #endif // #ifndef W32BASE_H
336 
337 // Standard headers needed
338 
339 #include <string>			// basic_string
340 #include <algorithm>		// for_each, etc.
341 #include <functional>		// for StdStringLessNoCase, et al
342 #include <locale>			// for various facets
343 
344 // If this is a recent enough version of VC include comdef.h, so we can write
345 // member functions to deal with COM types & compiler support classes e.g. _bstr_t
346 
347 #if defined (_MSC_VER) && (_MSC_VER >= 1100)
348 	#include <comdef.h>
349 	#define SS_INC_COMDEF		// signal that we #included MS comdef.h file
350 	#define STDSTRING_INC_COMDEF
351 	#define SS_NOTHROW __declspec(nothrow)
352 #else
353 	#define SS_NOTHROW
354 #endif
355 
356 #ifndef TRACE
357 	#define TRACE_DEFINED_HERE
358 	#define TRACE
359 #endif
360 
361 // Microsoft defines PCSTR, PCWSTR, etc, but no PCTSTR.  I hate to use the
362 // versions with the "L" in front of them because that's a leftover from Win 16
363 // days, even though it evaluates to the same thing.  Therefore, Define a PCSTR
364 // as an LPCTSTR.
365 
366 #if !defined(PCTSTR) && !defined(PCTSTR_DEFINED)
367 	typedef const TCHAR*			PCTSTR;
368 	#define PCTSTR_DEFINED
369 #endif
370 
371 #if !defined(PCOLESTR) && !defined(PCOLESTR_DEFINED)
372 	typedef const OLECHAR*			PCOLESTR;
373 	#define PCOLESTR_DEFINED
374 #endif
375 
376 #if !defined(POLESTR) && !defined(POLESTR_DEFINED)
377 	typedef OLECHAR*				POLESTR;
378 	#define POLESTR_DEFINED
379 #endif
380 
381 #if !defined(PCUSTR) && !defined(PCUSTR_DEFINED)
382 	typedef const unsigned char*	PCUSTR;
383 	typedef unsigned char*			PUSTR;
384 	#define PCUSTR_DEFINED
385 #endif
386 
387 // SS_USE_FACET macro and why we need it:
388 //
389 // Since I'm a good little Standard C++ programmer, I use locales.  Thus, I
390 // need to make use of the use_facet<> template function here.   Unfortunately,
391 // this need is complicated by the fact the MS' implementation of the Standard
392 // C++ Library has a non-standard version of use_facet that takes more
393 // arguments than the standard dictates.  Since I'm trying to write CStdString
394 // to work with any version of the Standard library, this presents a problem.
395 //
396 // The upshot of this is that I can't do 'use_facet' directly.  The MS' docs
397 // tell me that I have to use a macro, _USE() instead.  Since _USE obviously
398 // won't be available in other implementations, this means that I have to write
399 // my OWN macro -- SS_USE_FACET -- that evaluates either to _USE or to the
400 // standard, use_facet.
401 //
402 // If you are having trouble with the SS_USE_FACET macro, in your implementation
403 // of the Standard C++ Library, you can define your own version of SS_USE_FACET.
404 #ifndef schMSG
405 	#define schSTR(x)	   #x
406 	#define schSTR2(x)	schSTR(x)
407 	#define schMSG(desc) message(__FILE__ "(" schSTR2(__LINE__) "):" #desc)
408 #endif
409 
410 #ifndef SS_USE_FACET
411 	// STLPort #defines a macro (__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) for
412 	// all MSVC builds, erroneously in my opinion.  It causes problems for
413 	// my SS_ANSI builds.  In my code, I always comment out that line.  You'll
414 	// find it in   \stlport\config\stl_msvc.h
415 	#if defined(__SGI_STL_PORT) && (__SGI_STL_PORT >= 0x400 )
416 		#if defined(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) && defined(_MSC_VER)
417 			#ifdef SS_ANSI
418 				#pragma schMSG(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS defined!!)
419 			#endif
420 		#endif
421 		#define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)
422 	#elif defined(_MSC_VER )
423 		#define SS_USE_FACET(loc, fac) _USE(loc, fac)
424 
425 	// ...and
426 	#elif defined(_RWSTD_NO_TEMPLATE_ON_RETURN_TYPE)
427         #define SS_USE_FACET(loc, fac) std::use_facet(loc, (fac*)0)
428 	#else
429 		#define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)
430 	#endif
431 #endif
432 
433 // =============================================================================
434 // UNICODE/MBCS conversion macros.  Made to work just like the MFC/ATL ones.
435 // =============================================================================
436 
437 // First define the conversion helper functions.  We define these regardless of
438 // any preprocessor macro settings since their names won't collide.
439 
440 #ifdef SS_ANSI // Are we doing things the standard, non-Win32 way?...
441 
442 	typedef std::codecvt<wchar_t, char, mbstate_t> SSCodeCvt;
443 
444 	// Not sure if we need all these headers.   I believe ANSI says we do.
445 
446 	#include <stdio.h>
447 	#include <stdarg.h>
448 	#include <wchar.h>
449 	#ifndef va_start
450 		#include <varargs.h>
451 	#endif
452 
453 	// StdCodeCvt - made to look like Win32 functions WideCharToMultiByte annd
454 	//              MultiByteToWideChar but uses locales in SS_ANSI builds
455 	inline PWSTR StdCodeCvt(PWSTR pW, PCSTR pA, int nChars,
456 		const std::locale& loc=std::locale())
457 	{
458 		ASSERT(0 != pA);
459 		ASSERT(0 != pW);
460 		pW[0] = '\0';
461 		PCSTR pBadA				= 0;
462 		PWSTR pBadW				= 0;
463 		SSCodeCvt::result res	= SSCodeCvt::ok;
464 		const SSCodeCvt& conv	= SS_USE_FACET(loc, SSCodeCvt);
465         SSCodeCvt::state_type st= { 0 };
466 		res						= conv.in(st,
467 										  pA, pA + nChars, pBadA,
468 										  pW, pW + nChars, pBadW);
469 		ASSERT(SSCodeCvt::ok == res);
470 		return pW;
471 	}
472 	inline PWSTR StdCodeCvt(PWSTR pW, PCUSTR pA, int nChars,
473 		const std::locale& loc=std::locale())
474 	{
475 		return StdCodeCvt(pW, (PCSTR)pA, nChars, loc);
476 	}
477 
478 	inline PSTR StdCodeCvt(PSTR pA, PCWSTR pW, int nChars,
479 		const std::locale& loc=std::locale())
480 	{
481 		ASSERT(0 != pA);
482 		ASSERT(0 != pW);
483 		pA[0] = '\0';
484 		PSTR pBadA				= 0;
485 		PCWSTR pBadW			= 0;
486 		SSCodeCvt::result res	= SSCodeCvt::ok;
487 		const SSCodeCvt& conv	= SS_USE_FACET(loc, SSCodeCvt);
488         SSCodeCvt::state_type st= { 0 };
489 		res						= conv.out(st,
490 										   pW, pW + nChars, pBadW,
491 										   pA, pA + nChars, pBadA);
492 		ASSERT(SSCodeCvt::ok == res);
493 		return pA;
494 	}
495 	inline PUSTR StdCodeCvt(PUSTR pA, PCWSTR pW, int nChars,
496 		const std::locale& loc=std::locale())
497 	{
498 		return (PUSTR)StdCodeCvt((PSTR)pA, pW, nChars, loc);
499 	}
500 
501 #else   // ...or are we doing things assuming win32 and Visual C++?
502 
503 	#include <malloc.h>	// needed for _alloca
504 
505 	inline PWSTR StdCodeCvt(PWSTR pW, PCSTR pA, int nChars, UINT acp=CP_ACP)
506 	{
507 		ASSERT(0 != pA);
508 		ASSERT(0 != pW);
509 		pW[0] = '\0';
510 		MultiByteToWideChar(acp, 0, pA, -1, pW, nChars);
511 		return pW;
512 	}
513 	inline PWSTR StdCodeCvt(PWSTR pW, PCUSTR pA, int nChars, UINT acp=CP_ACP)
514 	{
515 		return StdCodeCvt(pW, (PCSTR)pA, nChars, acp);
516 	}
517 
518 	inline PSTR StdCodeCvt(PSTR pA, PCWSTR pW, int nChars, UINT acp=CP_ACP)
519 	{
520 		ASSERT(0 != pA);
521 		ASSERT(0 != pW);
522 		pA[0] = '\0';
523 		WideCharToMultiByte(acp, 0, pW, -1, pA, nChars, 0, 0);
524 		return pA;
525 	}
526 	inline PUSTR StdCodeCvt(PUSTR pA, PCWSTR pW, int nChars, UINT acp=CP_ACP)
527 	{
528 		return (PUSTR)StdCodeCvt((PSTR)pA, pW, nChars, acp);
529 	}
530 
531 	// Define our conversion macros to look exactly like Microsoft's to
532 	// facilitate using this stuff both with and without MFC/ATL
533 
534 	#ifdef _CONVERSION_USES_THREAD_LOCALE
535 		#ifndef _DEBUG
536 			#define SSCVT int _cvt; _cvt; UINT _acp=GetACP(); \
537 				_acp; PCWSTR _pw; _pw; PCSTR _pa; _pa
538 		#else
539 			#define SSCVT int _cvt = 0; _cvt; UINT _acp=GetACP();\
540 				 _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa
541 		#endif
542 	#else
543 		#ifndef _DEBUG
544 			#define SSCVT int _cvt; _cvt; UINT _acp=CP_ACP; _acp;\
545 				 PCWSTR _pw; _pw; PCSTR _pa; _pa
546 		#else
547 			#define SSCVT int _cvt = 0; _cvt; UINT _acp=CP_ACP; \
548 				_acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa
549 		#endif
550 	#endif
551 
552 	#ifdef _CONVERSION_USES_THREAD_LOCALE
553 		#define SSA2W(pa) (\
554 			((_pa = pa) == 0) ? 0 : (\
555 				_cvt = (strlen(_pa)+1),\
556 				StdCodeCvt((PWSTR) _alloca(_cvt*2), _pa, _cvt, _acp)))
557 		#define SSW2A(pw) (\
558 			((_pw = pw) == 0) ? 0 : (\
559 				_cvt = (wcslen(_pw)+1)*2,\
560 				StdW2AHelper((LPSTR) _alloca(_cvt), _pw, _cvt, _acp)))
561 	#else
562 		#define SSA2W(pa) (\
563 			((_pa = pa) == 0) ? 0 : (\
564 				_cvt = (strlen(_pa)+1),\
565 				StdCodeCvt((PWSTR) _alloca(_cvt*2), _pa, _cvt)))
566 		#define SSW2A(pw) (\
567 			((_pw = pw) == 0) ? 0 : (\
568 				_cvt = (wcslen(_pw)+1)*2,\
569 				StdCodeCvt((LPSTR) _alloca(_cvt), _pw, _cvt)))
570 	#endif
571 
572 	#define SSA2CW(pa) ((PCWSTR)SSA2W((pa)))
573 	#define SSW2CA(pw) ((PCSTR)SSW2A((pw)))
574 
575 	#ifdef UNICODE
576 		#define SST2A	SSW2A
577 		#define SSA2T	SSA2W
578 		#define SST2CA	SSW2CA
579 		#define SSA2CT	SSA2CW
SST2W(PTSTR p)580 		inline PWSTR	SST2W(PTSTR p)			{ return p; }
SSW2T(PWSTR p)581 		inline PTSTR	SSW2T(PWSTR p)			{ return p; }
SST2CW(PCTSTR p)582 		inline PCWSTR	SST2CW(PCTSTR p)		{ return p; }
SSW2CT(PCWSTR p)583 		inline PCTSTR	SSW2CT(PCWSTR p)		{ return p; }
584 	#else
585 		#define SST2W	SSA2W
586 		#define SSW2T	SSW2A
587 		#define SST2CW	SSA2CW
588 		#define SSW2CT	SSW2CA
SST2A(PTSTR p)589 		inline PSTR		SST2A(PTSTR p)			{ return p; }
SSA2T(PSTR p)590 		inline PTSTR	SSA2T(PSTR p)			{ return p; }
SST2CA(PCTSTR p)591 		inline PCSTR	SST2CA(PCTSTR p)		{ return p; }
SSA2CT(PCSTR p)592 		inline PCTSTR	SSA2CT(PCSTR p)			{ return p; }
593 	#endif // #ifdef UNICODE
594 
595 	#if defined(UNICODE)
596 	// in these cases the default (TCHAR) is the same as OLECHAR
SST2COLE(PCTSTR p)597 		inline PCOLESTR	SST2COLE(PCTSTR p)		{ return p; }
SSOLE2CT(PCOLESTR p)598 		inline PCTSTR	SSOLE2CT(PCOLESTR p)	{ return p; }
SST2OLE(PTSTR p)599 		inline POLESTR	SST2OLE(PTSTR p)		{ return p; }
SSOLE2T(POLESTR p)600 		inline PTSTR	SSOLE2T(POLESTR p)		{ return p; }
601 	#elif defined(OLE2ANSI)
602 	// in these cases the default (TCHAR) is the same as OLECHAR
SST2COLE(PCTSTR p)603 		inline PCOLESTR	SST2COLE(PCTSTR p)		{ return p; }
SSOLE2CT(PCOLESTR p)604 		inline PCTSTR	SSOLE2CT(PCOLESTR p)	{ return p; }
SST2OLE(PTSTR p)605 		inline POLESTR	SST2OLE(PTSTR p)		{ return p; }
SSOLE2T(POLESTR p)606 		inline PTSTR	SSOLE2T(POLESTR p)		{ return p; }
607 	#else
608 		//CharNextW doesn't work on Win95 so we use this
609 		#define SST2COLE(pa)	SSA2CW((pa))
610 		#define SST2OLE(pa)		SSA2W((pa))
611 		#define SSOLE2CT(po)	SSW2CA((po))
612 		#define SSOLE2T(po)		SSW2A((po))
613 	#endif
614 
615 	#ifdef OLE2ANSI
616 		#define SSW2OLE		SSW2A
617 		#define SSOLE2W		SSA2W
618 		#define SSW2COLE	SSW2CA
619 		#define SSOLE2CW	SSA2CW
SSA2OLE(PSTR p)620 		inline POLESTR		SSA2OLE(PSTR p)		{ return p; }
SSOLE2A(POLESTR p)621 		inline PSTR			SSOLE2A(POLESTR p)	{ return p; }
SSA2COLE(PCSTR p)622 		inline PCOLESTR		SSA2COLE(PCSTR p)	{ return p; }
SSOLE2CA(PCOLESTR p)623 		inline PCSTR		SSOLE2CA(PCOLESTR p){ return p; }
624 	#else
625 		#define SSA2OLE		SSA2W
626 		#define SSOLE2A		SSW2A
627 		#define SSA2COLE	SSA2CW
628 		#define SSOLE2CA	SSW2CA
SSW2OLE(PWSTR p)629 		inline POLESTR		SSW2OLE(PWSTR p)	{ return p; }
SSOLE2W(POLESTR p)630 		inline PWSTR		SSOLE2W(POLESTR p)	{ return p; }
SSW2COLE(PCWSTR p)631 		inline PCOLESTR		SSW2COLE(PCWSTR p)	{ return p; }
SSOLE2CW(PCOLESTR p)632 		inline PCWSTR		SSOLE2CW(PCOLESTR p){ return p; }
633 	#endif
634 
635 	// Above we've defined macros that look like MS' but all have
636 	// an 'SS' prefix.  Now we need the real macros.  We'll either
637 	// get them from the macros above or from MFC/ATL.  If
638 	// SS_NO_CONVERSION is #defined, we'll forgo them
639 
640 	#ifndef SS_NO_CONVERSION
641 
642 		#if defined (USES_CONVERSION)
643 
644 			#define _NO_STDCONVERSION	// just to be consistent
645 
646 		#else
647 
648 			#ifdef _MFC_VER
649 
650 				#include <afxconv.h>
651 				#define _NO_STDCONVERSION // just to be consistent
652 
653 			#else
654 
655 				#define USES_CONVERSION SSCVT
656 				#define A2CW			SSA2CW
657 				#define W2CA			SSW2CA
658 				#define T2A				SST2A
659 				#define A2T				SSA2T
660 				#define T2W				SST2W
661 				#define W2T				SSW2T
662 				#define T2CA			SST2CA
663 				#define A2CT			SSA2CT
664 				#define T2CW			SST2CW
665 				#define W2CT			SSW2CT
666 				#define ocslen			sslen
667 				#define ocscpy			sscpy
668 				#define T2COLE			SST2COLE
669 				#define OLE2CT			SSOLE2CT
670 				#define T2OLE			SST2COLE
671 				#define OLE2T			SSOLE2CT
672 				#define A2OLE			SSA2OLE
673 				#define OLE2A			SSOLE2A
674 				#define W2OLE			SSW2OLE
675 				#define OLE2W			SSOLE2W
676 				#define A2COLE			SSA2COLE
677 				#define OLE2CA			SSOLE2CA
678 				#define W2COLE			SSW2COLE
679 				#define OLE2CW			SSOLE2CW
680 
681 			#endif // #ifdef _MFC_VER
682 		#endif // #ifndef USES_CONVERSION
683 	#endif // #ifndef SS_NO_CONVERSION
684 
685 	// Define ostring - generic name for std::basic_string<OLECHAR>
686 
687 	#if !defined(ostring) && !defined(OSTRING_DEFINED)
688 		typedef std::basic_string<OLECHAR> ostring;
689 		#define OSTRING_DEFINED
690 	#endif
691 
692 #endif // #ifndef SS_ANSI
693 
694 // StdCodeCvt when there's no conversion to be done
StdCodeCvt(PSTR pDst,PCSTR pSrc,int nChars)695 inline PSTR StdCodeCvt(PSTR pDst, PCSTR pSrc, int nChars)
696 {
697 	pDst[0]				= '\0';
698 	std::char_traits<char>::copy(pDst, pSrc, nChars);
699 	if ( nChars > 0 )
700 		pDst[nChars]	= '\0';
701 
702 	return pDst;
703 }
StdCodeCvt(PSTR pDst,PCUSTR pSrc,int nChars)704 inline PSTR StdCodeCvt(PSTR pDst, PCUSTR pSrc, int nChars)
705 {
706 	return StdCodeCvt(pDst, (PCSTR)pSrc, nChars);
707 }
StdCodeCvt(PUSTR pDst,PCSTR pSrc,int nChars)708 inline PUSTR StdCodeCvt(PUSTR pDst, PCSTR pSrc, int nChars)
709 {
710 	return (PUSTR)StdCodeCvt((PSTR)pDst, pSrc, nChars);
711 }
712 
StdCodeCvt(PWSTR pDst,PCWSTR pSrc,int nChars)713 inline PWSTR StdCodeCvt(PWSTR pDst, PCWSTR pSrc, int nChars)
714 {
715 	pDst[0]				= '\0';
716 	std::char_traits<wchar_t>::copy(pDst, pSrc, nChars);
717 	if ( nChars > 0 )
718 		pDst[nChars]	= '\0';
719 
720 	return pDst;
721 }
722 
723 
724 // Define tstring -- generic name for std::basic_string<TCHAR>
725 
726 #if !defined(tstring) && !defined(TSTRING_DEFINED)
727 	typedef std::basic_string<TCHAR> tstring;
728 	#define TSTRING_DEFINED
729 #endif
730 
731 // a very shorthand way of applying the fix for KB problem Q172398
732 // (basic_string assignment bug)
733 
734 #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
735 	#define Q172398(x) (x).erase()
736 #else
737 	#define Q172398(x)
738 #endif
739 
740 // =============================================================================
741 // INLINE FUNCTIONS ON WHICH CSTDSTRING RELIES
742 //
743 // Usually for generic text mapping, we rely on preprocessor macro definitions
744 // to map to string functions.  However the CStdStr<> template cannot use
745 // macro-based generic text mappings because its character types do not get
746 // resolved until template processing which comes AFTER macro processing.  In
747 // other words, UNICODE is of little help to us in the CStdStr template
748 //
749 // Therefore, to keep the CStdStr declaration simple, we have these inline
750 // functions.  The template calls them often.  Since they are inline (and NOT
751 // exported when this is built as a DLL), they will probably be resolved away
752 // to nothing.
753 //
754 // Without these functions, the CStdStr<> template would probably have to broken
755 // out into two, almost identical classes.  Either that or it would be a huge,
756 // convoluted mess, with tons of "if" statements all over the place checking the
757 // size of template parameter CT.
758 //
759 // In several cases, you will see two versions of each function.  One version is
760 // the more portable, standard way of doing things, while the other is the
761 // non-standard, but often significantly faster Visual C++ way.
762 // =============================================================================
763 
764 // If they defined SS_NO_REFCOUNT, then we must convert all assignments
765 
766 #ifdef SS_NO_REFCOUNT
767 	#define SSREF(x) (x).c_str()
768 #else
769 	#define SSREF(x) (x)
770 #endif
771 
772 // -----------------------------------------------------------------------------
773 // sslen: strlen/wcslen wrappers
774 // -----------------------------------------------------------------------------
sslen(const CT * pT)775 template<typename CT> inline int sslen(const CT* pT)
776 {
777 	return 0 == pT ? 0 : std::char_traits<CT>::length(pT);
778 }
sslen(const std::string & s)779 inline SS_NOTHROW int sslen(const std::string& s)
780 {
781 	return s.length();
782 }
sslen(const std::wstring & s)783 inline SS_NOTHROW int sslen(const std::wstring& s)
784 {
785 	return s.length();
786 }
787 
788 
789 // -----------------------------------------------------------------------------
790 // ssasn: assignment functions -- assign "sSrc" to "sDst"
791 // -----------------------------------------------------------------------------
792 typedef std::string::size_type		SS_SIZETYPE; // just for shorthand, really
793 typedef std::string::pointer		SS_PTRTYPE;
794 typedef std::wstring::size_type		SW_SIZETYPE;
795 typedef std::wstring::pointer		SW_PTRTYPE;
796 
ssasn(std::string & sDst,const std::string & sSrc)797 inline void	ssasn(std::string& sDst, const std::string& sSrc)
798 {
799 	if ( sDst.c_str() != sSrc.c_str() )
800 	{
801 		sDst.erase();
802 		sDst.assign(SSREF(sSrc));
803 	}
804 }
ssasn(std::string & sDst,PCSTR pA)805 inline void	ssasn(std::string& sDst, PCSTR pA)
806 {
807 	// Watch out for NULLs, as always.
808 
809 	if ( 0 == pA )
810 	{
811 		sDst.erase();
812 	}
813 
814 	// If pA actually points to part of sDst, we must NOT erase(), but
815 	// rather take a substring
816 
817 	else if ( pA >= sDst.c_str() && pA <= sDst.c_str() + sDst.size() )
818 	{
819 		sDst =sDst.substr(static_cast<SS_SIZETYPE>(pA-sDst.c_str()));
820 	}
821 
822 	// Otherwise (most cases) apply the assignment bug fix, if applicable
823 	// and do the assignment
824 
825 	else
826 	{
827 		Q172398(sDst);
828 		sDst.assign(pA);
829 	}
830 }
ssasn(std::string & sDst,const std::wstring & sSrc)831 inline void	ssasn(std::string& sDst, const std::wstring& sSrc)
832 {
833 #ifdef SS_ANSI
834 	int nLen	= sSrc.size();
835 	sDst.resize(0);
836 	sDst.resize(nLen);
837 	StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), sSrc.c_str(), nLen);
838 #else
839 	SSCVT;
840 	sDst.assign(SSW2CA(sSrc.c_str()));
841 #endif
842 }
ssasn(std::string & sDst,PCWSTR pW)843 inline void	ssasn(std::string& sDst, PCWSTR pW)
844 {
845 #ifdef SS_ANSI
846 	int nLen	= sslen(pW);
847 	sDst.resize(0);
848 	sDst.resize(nLen);
849 	StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), pW, nLen);
850 #else
851 	SSCVT;
852 	sDst.assign(pW ? SSW2CA(pW) : "");
853 #endif
854 }
ssasn(std::string & sDst,const int nNull)855 inline void ssasn(std::string& sDst, const int nNull)
856 {
857 	UNUSED(nNull);
858 	ASSERT(nNull==0);
859 	sDst.assign("");
860 }
ssasn(std::wstring & sDst,const std::wstring & sSrc)861 inline void	ssasn(std::wstring& sDst, const std::wstring& sSrc)
862 {
863 	if ( sDst.c_str() != sSrc.c_str() )
864 	{
865 		sDst.erase();
866 		sDst.assign(SSREF(sSrc));
867 	}
868 }
ssasn(std::wstring & sDst,PCWSTR pW)869 inline void	ssasn(std::wstring& sDst, PCWSTR pW)
870 {
871 	// Watch out for NULLs, as always.
872 
873 	if ( 0 == pW )
874 	{
875 		sDst.erase();
876 	}
877 
878 	// If pW actually points to part of sDst, we must NOT erase(), but
879 	// rather take a substring
880 
881 	else if ( pW >= sDst.c_str() && pW <= sDst.c_str() + sDst.size() )
882 	{
883 		sDst = sDst.substr(static_cast<SW_SIZETYPE>(pW-sDst.c_str()));
884 	}
885 
886 	// Otherwise (most cases) apply the assignment bug fix, if applicable
887 	// and do the assignment
888 
889 	else
890 	{
891 		Q172398(sDst);
892 		sDst.assign(pW);
893 	}
894 }
895 #undef StrSizeType
ssasn(std::wstring & sDst,const std::string & sSrc)896 inline void	ssasn(std::wstring& sDst, const std::string& sSrc)
897 {
898 #ifdef SS_ANSI
899 	int nLen	= sSrc.size();
900 	sDst.resize(0);
901 	sDst.resize(nLen);
902 	StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), sSrc.c_str(), nLen);
903 #else
904 	SSCVT;
905 	sDst.assign(SSA2CW(sSrc.c_str()));
906 #endif
907 }
ssasn(std::wstring & sDst,PCSTR pA)908 inline void	ssasn(std::wstring& sDst, PCSTR pA)
909 {
910 #ifdef SS_ANSI
911 	int nLen	= sslen(pA);
912 	sDst.resize(0);
913 	sDst.resize(nLen);
914 	StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), pA, nLen);
915 #else
916 	SSCVT;
917 	sDst.assign(pA ? SSA2CW(pA) : L"");
918 #endif
919 }
ssasn(std::wstring & sDst,const int nNull)920 inline void ssasn(std::wstring& sDst, const int nNull)
921 {
922 	UNUSED(nNull);
923 	ASSERT(nNull==0);
924 	sDst.assign(L"");
925 }
926 
927 
928 // -----------------------------------------------------------------------------
929 // ssadd: string object concatenation -- add second argument to first
930 // -----------------------------------------------------------------------------
ssadd(std::string & sDst,const std::wstring & sSrc)931 inline void	ssadd(std::string& sDst, const std::wstring& sSrc)
932 {
933 #ifdef SS_ANSI
934 	int nLen	= sSrc.size();
935 	sDst.resize(sDst.size() + nLen);
936 	StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nLen), sSrc.c_str(), nLen);
937 #else
938 	SSCVT;
939 	sDst.append(SSW2CA(sSrc.c_str()));
940 #endif
941 }
ssadd(std::string & sDst,const std::string & sSrc)942 inline void	ssadd(std::string& sDst, const std::string& sSrc)
943 {
944 	sDst.append(sSrc.c_str());
945 }
ssadd(std::string & sDst,PCWSTR pW)946 inline void	ssadd(std::string& sDst, PCWSTR pW)
947 {
948 #ifdef SS_ANSI
949 	int nLen	= sslen(pW);
950 	sDst.resize(sDst.size() + nLen);
951 	StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nLen), pW, nLen);
952 #else
953 	SSCVT;
954 	if ( 0 != pW )
955 		sDst.append(SSW2CA(pW));
956 #endif
957 }
ssadd(std::string & sDst,PCSTR pA)958 inline void	ssadd(std::string& sDst, PCSTR pA)
959 {
960 	if ( pA )
961 		sDst.append(pA);
962 }
ssadd(std::wstring & sDst,const std::wstring & sSrc)963 inline void	ssadd(std::wstring& sDst, const std::wstring& sSrc)
964 {
965 	sDst.append(sSrc.c_str());
966 }
ssadd(std::wstring & sDst,const std::string & sSrc)967 inline void	ssadd(std::wstring& sDst, const std::string& sSrc)
968 {
969 #ifdef SS_ANSI
970 	int nLen	= sSrc.size();
971 	sDst.resize(sDst.size() + nLen);
972 	StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nLen), sSrc.c_str(), nLen);
973 #else
974 	SSCVT;
975 	sDst.append(SSA2CW(sSrc.c_str()));
976 #endif
977 }
ssadd(std::wstring & sDst,PCSTR pA)978 inline void	ssadd(std::wstring& sDst, PCSTR pA)
979 {
980 #ifdef SS_ANSI
981 	int nLen	= sslen(pA);
982 	sDst.resize(sDst.size() + nLen);
983 	StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nLen), pA, nLen);
984 #else
985 	SSCVT;
986 	if ( 0 != pA )
987 		sDst.append(SSA2CW(pA));
988 #endif
989 }
ssadd(std::wstring & sDst,PCWSTR pW)990 inline void	ssadd(std::wstring& sDst, PCWSTR pW)
991 {
992 	if ( pW )
993 		sDst.append(pW);
994 }
995 
996 
997 // -----------------------------------------------------------------------------
998 // ssicmp: comparison (case insensitive )
999 // -----------------------------------------------------------------------------
1000 #ifdef SS_ANSI
1001 	template<typename CT>
ssicmp(const CT * pA1,const CT * pA2)1002 	inline int ssicmp(const CT* pA1, const CT* pA2)
1003 	{
1004 		std::locale loc;
1005 		const std::ctype<CT>& ct = SS_USE_FACET(loc, std::ctype<CT>);
1006 		CT f;
1007 		CT l;
1008 
1009             do
1010 			{
1011 				f = ct.tolower(*(pA1++));
1012 				l = ct.tolower(*(pA2++));
1013             } while ( (f) && (f == l) );
1014 
1015         return (int)(f - l);
1016 	}
1017 #else
1018 	#ifdef _MBCS
sscmp(PCSTR pA1,PCSTR pA2)1019 		inline long sscmp(PCSTR pA1, PCSTR pA2)
1020 		{
1021 			return _mbscmp((PCUSTR)pA1, (PCUSTR)pA2);
1022 		}
ssicmp(PCSTR pA1,PCSTR pA2)1023 		inline long ssicmp(PCSTR pA1, PCSTR pA2)
1024 		{
1025 			return _mbsicmp((PCUSTR)pA1, (PCUSTR)pA2);
1026 		}
1027 	#else
sscmp(PCSTR pA1,PCSTR pA2)1028 		inline long sscmp(PCSTR pA1, PCSTR pA2)
1029 		{
1030 			return strcmp(pA1, pA2);
1031 		}
ssicmp(PCSTR pA1,PCSTR pA2)1032 		inline long ssicmp(PCSTR pA1, PCSTR pA2)
1033 		{
1034 			return _stricmp(pA1, pA2);
1035 		}
1036 	#endif
sscmp(PCWSTR pW1,PCWSTR pW2)1037 	inline long sscmp(PCWSTR pW1, PCWSTR pW2)
1038 	{
1039 		return wcscmp(pW1, pW2);
1040 	}
ssicmp(PCWSTR pW1,PCWSTR pW2)1041 	inline long ssicmp(PCWSTR pW1, PCWSTR pW2)
1042 	{
1043 		return _wcsicmp(pW1, pW2);
1044 	}
1045 #endif
1046 
1047 // -----------------------------------------------------------------------------
1048 // ssupr/sslwr: Uppercase/Lowercase conversion functions
1049 // -----------------------------------------------------------------------------
1050 #ifdef SS_ANSI
1051 	template<typename CT>
sslwr(CT * pT,size_t nLen)1052 	inline void sslwr(CT* pT, size_t nLen)
1053 	{
1054 		SS_USE_FACET(std::locale(), std::ctype<CT>).tolower(pT, pT+nLen);
1055 	}
1056 	template<typename CT>
ssupr(CT * pT,size_t nLen)1057 	inline void ssupr(CT* pT, size_t nLen)
1058 	{
1059 		SS_USE_FACET(std::locale(), std::ctype<CT>).toupper(pT, pT+nLen);
1060 	}
1061 #else  // #else we must be on Win32
1062 	#ifdef _MBCS
ssupr(PSTR pA,size_t)1063 		inline void	ssupr(PSTR pA, size_t /*nLen*/)
1064 		{
1065 			_mbsupr((PUSTR)pA);
1066 		}
sslwr(PSTR pA,size_t)1067 		inline void	sslwr(PSTR pA, size_t /*nLen*/)
1068 		{
1069 			_mbslwr((PUSTR)pA);
1070 		}
1071 	#else
ssupr(PSTR pA,size_t)1072 		inline void	ssupr(PSTR pA, size_t /*nLen*/)
1073 		{
1074 			_strupr(pA);
1075 		}
sslwr(PSTR pA,size_t)1076 		inline void	sslwr(PSTR pA, size_t /*nLen*/)
1077 		{
1078 			_strlwr(pA);
1079 		}
1080 	#endif
ssupr(PWSTR pW,size_t)1081 	inline void	ssupr(PWSTR pW, size_t /*nLen*/)
1082 	{
1083 		_wcsupr(pW);
1084 	}
sslwr(PWSTR pW,size_t)1085 	inline void	sslwr(PWSTR pW, size_t /*nLen*/)
1086 	{
1087 		_wcslwr(pW);
1088 	}
1089 #endif // #ifdef SS_ANSI
1090 
1091 // -----------------------------------------------------------------------------
1092 //  vsprintf/vswprintf or _vsnprintf/_vsnwprintf equivalents.  In standard
1093 //  builds we can't use _vsnprintf/_vsnwsprintf because they're MS extensions.
1094 // -----------------------------------------------------------------------------
1095 #ifdef SS_ANSI
ssvsprintf(PSTR pA,size_t,PCSTR pFmtA,va_list vl)1096 	inline int ssvsprintf(PSTR pA, size_t /*nCount*/, PCSTR pFmtA, va_list vl)
1097 	{
1098 		return vsprintf(pA, pFmtA, vl);
1099 	}
ssvsprintf(PWSTR pW,size_t nCount,PCWSTR pFmtW,va_list vl)1100 	inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
1101 	{
1102 		// JMO: It is beginning to seem like Microsoft Visual C++ is the only
1103 		// CRT distribution whose version of vswprintf takes THREE arguments.
1104 		// All others seem to take FOUR arguments.  Therefore instead of
1105 		// checking for every possible distro here, I'll assume that unless
1106 		// I am running with Microsoft's CRT, then vswprintf takes four
1107 		// arguments.  If you get a compilation error here, then you can just
1108 		// change this code to call the three-argument version.
1109 //	#if !defined(__MWERKS__) && !defined(__SUNPRO_CC_COMPAT) && !defined(__SUNPRO_CC)
1110 	#ifndef _MSC_VER
1111 		return vswprintf(pW, nCount, pFmtW, vl);
1112 	#else
1113 		nCount;
1114 		return vswprintf(pW, pFmtW, vl);
1115 	#endif
1116 	}
1117 #else
ssnprintf(PSTR pA,size_t nCount,PCSTR pFmtA,va_list vl)1118 	inline int	ssnprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
1119 	{
1120 		return _vsnprintf(pA, nCount, pFmtA, vl);
1121 	}
ssnprintf(PWSTR pW,size_t nCount,PCWSTR pFmtW,va_list vl)1122 	inline int	ssnprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
1123 	{
1124 		return _vsnwprintf(pW, nCount, pFmtW, vl);
1125 	}
1126 #endif
1127 
1128 
1129 // -----------------------------------------------------------------------------
1130 // ssload: Type safe, overloaded ::LoadString wrappers
1131 // There is no equivalent of these in non-Win32-specific builds.  However, I'm
1132 // thinking that with the message facet, there might eventually be one
1133 // -----------------------------------------------------------------------------
1134 #ifdef SS_ANSI
1135 #else
ssload(HMODULE hInst,UINT uId,PSTR pBuf,int nMax)1136 	inline int ssload(HMODULE hInst, UINT uId, PSTR pBuf, int nMax)
1137 	{
1138 		return ::LoadStringA(hInst, uId, pBuf, nMax);
1139 	}
ssload(HMODULE hInst,UINT uId,PWSTR pBuf,int nMax)1140 	inline int ssload(HMODULE hInst, UINT uId, PWSTR pBuf, int nMax)
1141 	{
1142 		return ::LoadStringW(hInst, uId, pBuf, nMax);
1143 	}
1144 #endif
1145 
1146 
1147 // -----------------------------------------------------------------------------
1148 // sscoll/ssicoll: Collation wrappers
1149 // -----------------------------------------------------------------------------
1150 #ifdef SS_ANSI
1151 	template <typename CT>
sscoll(const CT * sz1,int nLen1,const CT * sz2,int nLen2)1152 	inline int sscoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)
1153 	{
1154 		const std::collate<CT>& coll =
1155 			SS_USE_FACET(std::locale(), std::collate<CT>);
1156 		return coll.compare(sz1, sz1+nLen1, sz2, sz2+nLen2);
1157 	}
1158 	template <typename CT>
ssicoll(const CT * sz1,int nLen1,const CT * sz2,int nLen2)1159 	inline int ssicoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)
1160 	{
1161 		const std::locale loc;
1162 		const std::collate<CT>& coll = SS_USE_FACET(loc, std::collate<CT>);
1163 
1164 		// Some implementations seem to have trouble using the collate<>
1165 		// facet typedefs so we'll just default to basic_string and hope
1166 		// that's what the collate facet uses (which it generally should)
1167 
1168 //		std::collate<CT>::string_type s1(sz1);
1169 //		std::collate<CT>::string_type s2(sz2);
1170 		std::basic_string<CT> s1(sz1);
1171 		std::basic_string<CT> s2(sz2);
1172 
1173 		sslwr(const_cast<CT*>(s1.c_str()), nLen1);
1174 		sslwr(const_cast<CT*>(s2.c_str()), nLen2);
1175 		return coll.compare(s1.c_str(), s1.c_str()+nLen1,
1176 							s2.c_str(), s2.c_str()+nLen2);
1177 	}
1178 #else
1179 	#ifdef _MBCS
sscoll(PCSTR sz1,int,PCSTR sz2,int)1180 		inline int sscoll(PCSTR sz1, int /*nLen1*/, PCSTR sz2, int /*nLen2*/)
1181 		{
1182 			return _mbscoll((PCUSTR)sz1, (PCUSTR)sz2);
1183 		}
ssicoll(PCSTR sz1,int,PCSTR sz2,int)1184 		inline int ssicoll(PCSTR sz1, int /*nLen1*/, PCSTR sz2, int /*nLen2*/)
1185 		{
1186 			return _mbsicoll((PCUSTR)sz1, (PCUSTR)sz2);
1187 		}
1188 	#else
sscoll(PCSTR sz1,int,PCSTR sz2,int)1189 		inline int sscoll(PCSTR sz1, int /*nLen1*/, PCSTR sz2, int /*nLen2*/)
1190 		{
1191 			return strcoll(sz1, sz2);
1192 		}
ssicoll(PCSTR sz1,int,PCSTR sz2,int)1193 		inline int ssicoll(PCSTR sz1, int /*nLen1*/, PCSTR sz2, int /*nLen2*/)
1194 		{
1195 			return _stricoll(sz1, sz2);
1196 		}
1197 	#endif
sscoll(PCWSTR sz1,int,PCWSTR sz2,int)1198 	inline int sscoll(PCWSTR sz1, int /*nLen1*/, PCWSTR sz2, int /*nLen2*/)
1199 	{
1200 		return wcscoll(sz1, sz2);
1201 	}
ssicoll(PCWSTR sz1,int,PCWSTR sz2,int)1202 	inline int ssicoll(PCWSTR sz1, int /*nLen1*/, PCWSTR sz2, int /*nLen2*/)
1203 	{
1204 		return _wcsicoll(sz1, sz2);
1205 	}
1206 #endif
1207 
1208 
1209 // -----------------------------------------------------------------------------
1210 // ssfmtmsg: FormatMessage equivalents.  Needed because I added a CString facade
1211 // Again -- no equivalent of these on non-Win32 builds but their might one day
1212 // be one if the message facet gets implemented
1213 // -----------------------------------------------------------------------------
1214 #ifdef SS_ANSI
1215 #else
ssfmtmsg(DWORD dwFlags,LPCVOID pSrc,DWORD dwMsgId,DWORD dwLangId,PSTR pBuf,DWORD nSize,va_list * vlArgs)1216 	inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
1217 						  DWORD dwLangId, PSTR pBuf, DWORD nSize,
1218 						  va_list* vlArgs)
1219 	{
1220 		return FormatMessageA(dwFlags, pSrc, dwMsgId, dwLangId,
1221 							  pBuf, nSize,vlArgs);
1222 	}
ssfmtmsg(DWORD dwFlags,LPCVOID pSrc,DWORD dwMsgId,DWORD dwLangId,PWSTR pBuf,DWORD nSize,va_list * vlArgs)1223 	inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
1224 						  DWORD dwLangId, PWSTR pBuf, DWORD nSize,
1225 						  va_list* vlArgs)
1226 	{
1227 		return FormatMessageW(dwFlags, pSrc, dwMsgId, dwLangId,
1228 							  pBuf, nSize,vlArgs);
1229 	}
1230 #endif
1231 
1232 
1233 
1234 // FUNCTION: sscpy.  Copies up to 'nMax' characters from pSrc to pDst.
1235 // -----------------------------------------------------------------------------
1236 // FUNCTION:  sscpy
1237 //		inline int sscpy(PSTR pDst, PCSTR pSrc, int nMax=-1);
1238 //		inline int sscpy(PUSTR pDst,  PCSTR pSrc, int nMax=-1)
1239 //		inline int sscpy(PSTR pDst, PCWSTR pSrc, int nMax=-1);
1240 //		inline int sscpy(PWSTR pDst, PCWSTR pSrc, int nMax=-1);
1241 //		inline int sscpy(PWSTR pDst, PCSTR pSrc, int nMax=-1);
1242 //
1243 // DESCRIPTION:
1244 //		This function is very much (but not exactly) like strcpy.  These
1245 //		overloads simplify copying one C-style string into another by allowing
1246 //		the caller to specify two different types of strings if necessary.
1247 //
1248 //		The strings must NOT overlap
1249 //
1250 //		"Character" is expressed in terms of the destination string, not
1251 //		the source.  If no 'nMax' argument is supplied, then the number of
1252 //		characters copied will be sslen(pSrc).  A NULL terminator will
1253 //		also be added so pDst must actually be big enough to hold nMax+1
1254 //		characters.  The return value is the number of characters copied,
1255 //		not including the NULL terminator.
1256 //
1257 // PARAMETERS:
1258 //		pSrc - the string to be copied FROM.  May be a char based string, an
1259 //			   MBCS string (in Win32 builds) or a wide string (wchar_t).
1260 //		pSrc - the string to be copied TO.  Also may be either MBCS or wide
1261 //		nMax - the maximum number of characters to be copied into szDest.  Note
1262 //			   that this is expressed in whatever a "character" means to pDst.
1263 //			   If pDst is a wchar_t type string than this will be the maximum
1264 //			   number of wchar_ts that my be copied.  The pDst string must be
1265 //			   large enough to hold least nMaxChars+1 characters.
1266 //			   If the caller supplies no argument for nMax this is a signal to
1267 //			   the routine to copy all the characters in pSrc, regardless of
1268 //			   how long it is.
1269 //
1270 // RETURN VALUE: none
1271 // -----------------------------------------------------------------------------
1272 template<typename CT1, typename CT2>
sscpycvt(CT1 * pDst,const CT2 * pSrc,int nChars)1273 inline int sscpycvt(CT1* pDst, const CT2* pSrc, int nChars)
1274 {
1275 	StdCodeCvt(pDst, pSrc, nChars);
1276 	pDst[SSMAX(nChars, 0)]	= '\0';
1277 	return nChars;
1278 }
1279 
1280 template<typename CT1, typename CT2>
sscpy(CT1 * pDst,const CT2 * pSrc,int nMax,int nLen)1281 inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax, int nLen)
1282 {
1283 	return sscpycvt(pDst, pSrc, SSMIN(nMax, nLen));
1284 }
1285 template<typename CT1, typename CT2>
sscpy(CT1 * pDst,const CT2 * pSrc,int nMax)1286 inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax)
1287 {
1288 	return sscpycvt(pDst, pSrc, SSMIN(nMax, sslen(pSrc)));
1289 }
1290 template<typename CT1, typename CT2>
sscpy(CT1 * pDst,const CT2 * pSrc)1291 inline int sscpy(CT1* pDst, const CT2* pSrc)
1292 {
1293 	return sscpycvt(pDst, pSrc, sslen(pSrc));
1294 }
1295 template<typename CT1, typename CT2>
sscpy(CT1 * pDst,const std::basic_string<CT2> & sSrc,int nMax)1296 inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc, int nMax)
1297 {
1298 	return sscpycvt(pDst, sSrc.c_str(), SSMIN(nMax, (int)sSrc.length()));
1299 }
1300 template<typename CT1, typename CT2>
sscpy(CT1 * pDst,const std::basic_string<CT2> & sSrc)1301 inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc)
1302 {
1303 	return sscpycvt(pDst, sSrc.c_str(), (int)sSrc.length());
1304 }
1305 
1306 #ifdef SS_INC_COMDEF
1307 	template<typename CT1>
sscpy(CT1 * pDst,const _bstr_t & bs,int nMax)1308 	inline int sscpy(CT1* pDst, const _bstr_t& bs, int nMax)
1309 	{
1310 		return sscpycvt(pDst, static_cast<PCOLESTR>(bs), SSMIN(nMax, (int)bs.length()));
1311 	}
1312 	template<typename CT1>
sscpy(CT1 * pDst,const _bstr_t & bs)1313 	inline int sscpy(CT1* pDst, const _bstr_t& bs)
1314 	{
1315 		return sscpy(pDst, bs, bs.length());
1316 	}
1317 #endif
1318 
1319 
1320 // -----------------------------------------------------------------------------
1321 // Functional objects for changing case.  They also let you pass locales
1322 // -----------------------------------------------------------------------------
1323 
1324 #ifdef SS_ANSI
1325 	template<typename CT>
1326 	struct SSToUpper : public std::binary_function<CT, std::locale, CT>
1327 	{
operatorSSToUpper1328 		inline CT operator()(const CT& t, const std::locale& loc) const
1329 		{
1330 			return std::toupper<CT>(t, loc);
1331 		}
1332 	};
1333 	template<typename CT>
1334 	struct SSToLower : public std::binary_function<CT, std::locale, CT>
1335 	{
operatorSSToLower1336 		inline CT operator()(const CT& t, const std::locale& loc) const
1337 		{
1338 			return std::tolower<CT>(t, loc);
1339 		}
1340 	};
1341 #endif
1342 
1343 // This struct is used for TrimRight() and TrimLeft() function implementations.
1344 //template<typename CT>
1345 //struct NotSpace : public std::unary_function<CT, bool>
1346 //{
1347 //	const std::locale& loc;
1348 //	inline NotSpace(const std::locale& locArg) : loc(locArg) {}
1349 //	inline bool operator() (CT t) { return !std::isspace(t, loc); }
1350 //};
1351 template<typename CT>
1352 struct NotSpace : public std::unary_function<CT, bool>
1353 {
1354 	const std::locale& loc;
NotSpaceNotSpace1355 	NotSpace(const std::locale& locArg) : loc(locArg) {}
1356 
1357 	// DINKUMWARE BUG:
1358 	// Note -- using std::isspace in a COM DLL gives us access violations
1359 	// because it causes the dynamic addition of a function to be called
1360 	// when the library shuts down.  Unfortunately the list is maintained
1361 	// in DLL memory but the function is in static memory.  So the COM DLL
1362 	// goes away along with the function that was supposed to be called,
1363 	// and then later when the DLL CRT shuts down it unloads the list and
1364 	// tries to call the long-gone function.
1365 	// This is DinkumWare's implementation problem.  Until then, we will
1366 	// use good old isspace and iswspace from the CRT unless they
1367 	// specify SS_ANSI
1368 #ifdef SS_ANSI
operatorNotSpace1369 	bool operator() (CT t) const { return !std::isspace(t, loc); }
1370 #else
ssispNotSpace1371 	bool ssisp(char c) const { return FALSE != ::isspace((int) c); }
ssispNotSpace1372 	bool ssisp(wchar_t c) const { return FALSE != ::iswspace((wint_t) c); }
operatorNotSpace1373 	bool operator()(CT t) const  { return !ssisp(t); }
1374 #endif
1375 };
1376 
1377 
1378 
1379 
1380 //			Now we can define the template (finally!)
1381 // =============================================================================
1382 // TEMPLATE: CStdStr
1383 //		template<typename CT> class CStdStr : public std::basic_string<CT>
1384 //
1385 // REMARKS:
1386 //		This template derives from basic_string<CT> and adds some MFC CString-
1387 //		like functionality
1388 //
1389 //		Basically, this is my attempt to make Standard C++ library strings as
1390 //		easy to use as the MFC CString class.
1391 //
1392 //		Note that although this is a template, it makes the assumption that the
1393 //		template argument (CT, the character type) is either char or wchar_t.
1394 // =============================================================================
1395 
1396 //#define CStdStr _SS	// avoid compiler warning 4786
1397 
1398 
1399 template<typename CT>
1400 class CStdStr : public std::basic_string<CT>
1401 {
1402 	// Typedefs for shorter names.  Using these names also appears to help
1403 	// us avoid some ambiguities that otherwise arise on some platforms
1404 
1405 	typedef typename std::basic_string<CT>		MYBASE;	 // my base class
1406 	typedef CStdStr<CT>							MYTYPE;	 // myself
1407 	typedef typename MYBASE::const_pointer		PCMYSTR; // PCSTR or PCWSTR
1408 	typedef typename MYBASE::pointer			PMYSTR;	 // PSTR or PWSTR
1409 	typedef typename MYBASE::iterator			MYITER;  // my iterator type
1410 	typedef typename MYBASE::const_iterator		MYCITER; // you get the idea...
1411 	typedef typename MYBASE::reverse_iterator	MYRITER;
1412 	typedef typename MYBASE::size_type			MYSIZE;
1413 	typedef typename MYBASE::value_type			MYVAL;
1414 	typedef typename MYBASE::allocator_type		MYALLOC;
1415 
1416 public:
1417 
1418 	// shorthand conversion from PCTSTR to string resource ID
1419 	#define _TRES(pctstr) (LOWORD((DWORD)(pctstr)))
1420 
1421 	// CStdStr inline constructors
CStdStr()1422 	CStdStr()
1423 	{
1424 	}
1425 
CStdStr(const MYTYPE & str)1426 	CStdStr(const MYTYPE& str) : MYBASE(SSREF(str))
1427 	{
1428 	}
1429 
CStdStr(const std::string & str)1430 	CStdStr(const std::string& str)
1431 	{
1432 		ssasn(*this, SSREF(str));
1433 	}
1434 
CStdStr(const std::wstring & str)1435 	CStdStr(const std::wstring& str)
1436 	{
1437 		ssasn(*this, SSREF(str));
1438 	}
1439 
CStdStr(PCMYSTR pT,MYSIZE n)1440 	CStdStr(PCMYSTR pT, MYSIZE n) : MYBASE(pT, n)
1441 	{
1442 	}
1443 
CStdStr(PCSTR pA)1444 	CStdStr(PCSTR pA)
1445 	{
1446 	#ifdef SS_ANSI
1447 		*this = pA;
1448 	#else
1449 		if ( 0 != HIWORD(pA) )
1450 			*this = pA;
1451 		else if ( 0 != pA && !Load(_TRES(pA)) )
1452 			TRACE(_T("Can't load string %u\n"), _TRES(pA));
1453 	#endif
1454 	}
1455 
CStdStr(PCWSTR pW)1456 	CStdStr(PCWSTR pW)
1457 	{
1458 	#ifdef SS_ANSI
1459 		*this = pW;
1460 	#else
1461 		if ( 0 != HIWORD(pW) )
1462 			*this = pW;
1463 		else if ( 0 != pW && !Load(_TRES(pW)) )
1464 			TRACE(_T("Can't load string %u\n"), _TRES(pW));
1465 	#endif
1466 	}
1467 
CStdStr(MYCITER first,MYCITER last)1468 	CStdStr(MYCITER first, MYCITER last)
1469 		: MYBASE(first, last)
1470 	{
1471 	}
1472 
1473 	CStdStr(MYSIZE nSize, MYVAL ch, const MYALLOC& al=MYALLOC())
MYBASE(nSize,ch,al)1474 		: MYBASE(nSize, ch, al)
1475 	{
1476 	}
1477 
1478 	#ifdef SS_INC_COMDEF
CStdStr(const _bstr_t & bstr)1479 		CStdStr(const _bstr_t& bstr)
1480 		{
1481 			if ( bstr.length() > 0 )
1482 				append(static_cast<PCMYSTR>(bstr), bstr.length());
1483 		}
1484 	#endif
1485 
1486 	// CStdStr inline assignment operators -- the ssasn function now takes care
1487 	// of fixing  the MSVC assignment bug (see knowledge base article Q172398).
1488 	MYTYPE& operator=(const MYTYPE& str)
1489 	{
1490 		ssasn(*this, str);
1491 		return *this;
1492 	}
1493 
1494 	MYTYPE& operator=(const std::string& str)
1495 	{
1496 		ssasn(*this, str);
1497 		return *this;
1498 	}
1499 
1500 	MYTYPE& operator=(const std::wstring& str)
1501 	{
1502 		ssasn(*this, str);
1503 		return *this;
1504 	}
1505 
1506 	MYTYPE& operator=(PCSTR pA)
1507 	{
1508 		ssasn(*this, pA);
1509 		return *this;
1510 	}
1511 
1512 	MYTYPE& operator=(PCWSTR pW)
1513 	{
1514 		ssasn(*this, pW);
1515 		return *this;
1516 	}
1517 
1518 	MYTYPE& operator=(CT t)
1519 	{
1520 		Q172398(*this);
1521 		MYBASE::assign(1, t);
1522 		return *this;
1523 	}
1524 
1525 	#ifdef SS_INC_COMDEF
1526 		MYTYPE& operator=(const _bstr_t& bstr)
1527 		{
1528 			if ( bstr.length() > 0 )
1529 				return assign(static_cast<PCMYSTR>(bstr), bstr.length());
1530 			else
1531 			{
1532 				erase();
1533 				return *this;
1534 			}
1535 		}
1536 	#endif
1537 
1538 
1539 	// Overloads  also needed to fix the MSVC assignment bug (KB: Q172398)
1540 	//  *** Thanks to Pete The Plumber for catching this one ***
1541 	// They also are compiled if you have explicitly turned off refcounting
1542 	#if ( defined(_MSC_VER) && ( _MSC_VER < 1200 ) ) || defined(SS_NO_REFCOUNT)
1543 
assign(const MYTYPE & str)1544 		MYTYPE& assign(const MYTYPE& str)
1545 		{
1546 			ssasn(*this, str);
1547 			return *this;
1548 		}
1549 
assign(const MYTYPE & str,MYSIZE nStart,MYSIZE nChars)1550 		MYTYPE& assign(const MYTYPE& str, MYSIZE nStart, MYSIZE nChars)
1551 		{
1552 			// This overload of basic_string::assign is supposed to assign up to
1553 			// <nChars> or the NULL terminator, whichever comes first.  Since we
1554 			// are about to call a less forgiving overload (in which <nChars>
1555 			// must be a valid length), we must adjust the length here to a safe
1556 			// value.  Thanks to Ullrich Poll�hne for catching this bug
1557 
1558 			nChars		= SSMIN(nChars, str.length() - nStart);
1559 
1560 			// Watch out for assignment to self
1561 
1562 			if ( this == &str )
1563 			{
1564 				MYTYPE strTemp(str.c_str()+nStart, nChars);
1565 				assign(strTemp);
1566 			}
1567 			else
1568 			{
1569 				Q172398(*this);
1570 				MYBASE::assign(str.c_str()+nStart, nChars);
1571 			}
1572 			return *this;
1573 		}
1574 
assign(const MYBASE & str)1575 		MYTYPE& assign(const MYBASE& str)
1576 		{
1577 			ssasn(*this, str);
1578 			return *this;
1579 		}
1580 
assign(const MYBASE & str,MYSIZE nStart,MYSIZE nChars)1581 		MYTYPE& assign(const MYBASE& str, MYSIZE nStart, MYSIZE nChars)
1582 		{
1583 			// This overload of basic_string::assign is supposed to assign up to
1584 			// <nChars> or the NULL terminator, whichever comes first.  Since we
1585 			// are about to call a less forgiving overload (in which <nChars>
1586 			// must be a valid length), we must adjust the length here to a safe
1587 			// value. Thanks to Ullrich Poll�hne for catching this bug
1588 
1589 			nChars		= SSMIN(nChars, str.length() - nStart);
1590 
1591 			// Watch out for assignment to self
1592 
1593 			if ( this == &str )	// watch out for assignment to self
1594 			{
1595 				MYTYPE strTemp(str.c_str() + nStart, nChars);
1596 				assign(strTemp);
1597 			}
1598 			else
1599 			{
1600 				Q172398(*this);
1601 				MYBASE::assign(str.c_str()+nStart, nChars);
1602 			}
1603 			return *this;
1604 		}
1605 
assign(const CT * pC,MYSIZE nChars)1606 		MYTYPE& assign(const CT* pC, MYSIZE nChars)
1607 		{
1608 			// Q172398 only fix -- erase before assigning, but not if we're
1609 			// assigning from our own buffer
1610 
1611 	#if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
1612 			if ( !empty() && ( pC < data() || pC > data() + capacity() ) )
1613 				erase();
1614 	#endif
1615 			Q172398(*this);
1616 			MYBASE::assign(pC, nChars);
1617 			return *this;
1618 		}
1619 
assign(MYSIZE nChars,MYVAL val)1620 		MYTYPE& assign(MYSIZE nChars, MYVAL val)
1621 		{
1622 			Q172398(*this);
1623 			MYBASE::assign(nChars, val);
1624 			return *this;
1625 		}
1626 
assign(const CT * pT)1627 		MYTYPE& assign(const CT* pT)
1628 		{
1629 			return assign(pT, CStdStr::traits_type::length(pT));
1630 		}
1631 
assign(MYCITER iterFirst,MYCITER iterLast)1632 		MYTYPE& assign(MYCITER iterFirst, MYCITER iterLast)
1633 		{
1634 	#if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
1635 			// Q172398 fix.  don't call erase() if we're assigning from ourself
1636 			if ( iterFirst < begin() || iterFirst > begin() + size() )
1637 				erase()
1638 	#endif
1639 			replace(begin(), end(), iterFirst, iterLast);
1640 			return *this;
1641 		}
1642 	#endif
1643 
1644 
1645 	// -------------------------------------------------------------------------
1646 	// CStdStr inline concatenation.
1647 	// -------------------------------------------------------------------------
1648 	MYTYPE& operator+=(const MYTYPE& str)
1649 	{
1650 		ssadd(*this, str);
1651 		return *this;
1652 	}
1653 
1654 	MYTYPE& operator+=(const std::string& str)
1655 	{
1656 		ssadd(*this, str);
1657 		return *this;
1658 	}
1659 
1660 	MYTYPE& operator+=(const std::wstring& str)
1661 	{
1662 		ssadd(*this, str);
1663 		return *this;
1664 	}
1665 
1666 	MYTYPE& operator+=(PCSTR pA)
1667 	{
1668 		ssadd(*this, pA);
1669 		return *this;
1670 	}
1671 
1672 	MYTYPE& operator+=(PCWSTR pW)
1673 	{
1674 		ssadd(*this, pW);
1675 		return *this;
1676 	}
1677 
1678 	MYTYPE& operator+=(CT t)
1679 	{
1680 		append(1, t);
1681 		return *this;
1682 	}
1683 	#ifdef SS_INC_COMDEF	// if we have _bstr_t, define a += for it too.
1684 		MYTYPE& operator+=(const _bstr_t& bstr)
1685 		{
1686 			return operator+=(static_cast<PCMYSTR>(bstr));
1687 		}
1688 	#endif
1689 
1690 
1691 	// addition operators -- global friend functions.
1692 
1693 	friend	MYTYPE	operator+(const MYTYPE& str1,	const MYTYPE& str2);
1694 	friend	MYTYPE	operator+(const MYTYPE& str,	CT t);
1695 	friend	MYTYPE	operator+(const MYTYPE& str,	PCSTR sz);
1696 	friend	MYTYPE	operator+(const MYTYPE& str,	PCWSTR sz);
1697 	friend	MYTYPE	operator+(PCSTR pA,				const MYTYPE& str);
1698 	friend	MYTYPE	operator+(PCWSTR pW,			const MYTYPE& str);
1699 #ifdef SS_INC_COMDEF
1700 	friend	MYTYPE	operator+(const _bstr_t& bstr,	const MYTYPE& str);
1701 	friend	MYTYPE	operator+(const MYTYPE& str,	const _bstr_t& bstr);
1702 #endif
1703 
1704 	// -------------------------------------------------------------------------
1705 	// Case changing functions
1706 	// -------------------------------------------------------------------------
1707 	// -------------------------------------------------------------------------
ToUpper()1708 	MYTYPE& ToUpper()
1709 	{
1710 		//  Strictly speaking, this would be about the most portable way
1711 
1712 		//	std::transform(begin(),
1713 		//				   end(),
1714 		//				   begin(),
1715 		//				   std::bind2nd(SSToUpper<CT>(), std::locale()));
1716 
1717 		// But practically speaking, this works faster
1718 
1719 		if ( !empty() )
1720 			ssupr(GetBuf(), size());
1721 
1722 		return *this;
1723 	}
1724 
1725 
1726 
ToLower()1727 	MYTYPE& ToLower()
1728 	{
1729 		//  Strictly speaking, this would be about the most portable way
1730 
1731 		//	std::transform(begin(),
1732 		//				   end(),
1733 		//				   begin(),
1734 		//				   std::bind2nd(SSToLower<CT>(), std::locale()));
1735 
1736 		// But practically speaking, this works faster
1737 
1738 		if ( !empty() )
1739 			sslwr(GetBuf(), size());
1740 
1741 		return *this;
1742 	}
1743 
1744 
1745 
Normalize()1746 	MYTYPE& Normalize()
1747 	{
1748 		return Trim().ToLower();
1749 	}
1750 
1751 
1752 	// -------------------------------------------------------------------------
1753 	// CStdStr -- Direct access to character buffer.  In the MS' implementation,
1754 	// the at() function that we use here also calls _Freeze() providing us some
1755 	// protection from multithreading problems associated with ref-counting.
1756 	// -------------------------------------------------------------------------
1757 	CT* GetBuf(int nMinLen=-1)
1758 	{
1759 		if ( static_cast<int>(size()) < nMinLen )
1760 			resize(static_cast<MYSIZE>(nMinLen));
1761 
1762 		return empty() ? const_cast<CT*>(data()) : &(at(0));
1763 	}
1764 
SetBuf(int nLen)1765 	CT* SetBuf(int nLen)
1766 	{
1767 		nLen = ( nLen > 0 ? nLen : 0 );
1768 		if ( capacity() < 1 && nLen == 0 )
1769 			resize(1);
1770 
1771 		resize(static_cast<MYSIZE>(nLen));
1772 		return const_cast<CT*>(data());
1773 	}
1774 	void RelBuf(int nNewLen=-1)
1775 	{
1776 		resize(static_cast<MYSIZE>(nNewLen > -1 ? nNewLen : sslen(c_str())));
1777 	}
1778 
BufferRel()1779 	void BufferRel()		 { RelBuf(); }			// backwards compatability
Buffer()1780 	CT*  Buffer()			 { return GetBuf(); }	// backwards compatability
BufferSet(int nLen)1781 	CT*  BufferSet(int nLen) { return SetBuf(nLen);}// backwards compatability
1782 
1783 	bool Equals(const CT* pT, bool bUseCase=false) const
1784 	{	// get copy, THEN compare (thread safe)
1785 		return  bUseCase ? compare(pT) == 0 : ssicmp(MYTYPE(*this), pT) == 0;
1786 	}
1787 
1788 	// -------------------------------------------------------------------------
1789 	// FUNCTION:  CStdStr::Load
1790 	// REMARKS:
1791 	//		Loads string from resource specified by nID
1792 	//
1793 	// PARAMETERS:
1794 	//		nID - resource Identifier.  Purely a Win32 thing in this case
1795 	//
1796 	// RETURN VALUE:
1797 	//		true if successful, false otherwise
1798 	// -------------------------------------------------------------------------
1799 #ifndef SS_ANSI
1800 	bool Load(UINT nId, HMODULE hModule=NULL)
1801 	{
1802 		bool bLoaded		= false;	// set to true of we succeed.
1803 
1804 	#ifdef _MFC_VER		// When in Rome...
1805 
1806 		CString strRes;
1807 		bLoaded				= FALSE != strRes.LoadString(nId);
1808 		if ( bLoaded )
1809 			*this			= strRes;
1810 
1811 	#else
1812 
1813 		// Get the resource name and module handle
1814 
1815 		if ( NULL == hModule )
1816 			hModule			= GetResourceHandle();
1817 
1818 		PCTSTR szName		= MAKEINTRESOURCE((nId>>4)+1); // lifted
1819 		DWORD dwSize		= 0;
1820 
1821 		// No sense continuing if we can't find the resource
1822 
1823 		HRSRC hrsrc			= ::FindResource(hModule, szName, RT_STRING);
1824 
1825 		if ( NULL == hrsrc )
1826 			TRACE(_T("Cannot find resource %d: 0x%X"), nId, ::GetLastError());
1827 		else if ( 0 == (dwSize = ::SizeofResource(hModule, hrsrc) / sizeof(CT)))
1828 			TRACE(_T("Cant get size of resource %d 0x%X\n"),nId,GetLastError());
1829 		else
1830 		{
1831 			bLoaded			= 0 != ssload(hModule, nId, GetBuf(dwSize), dwSize);
1832 			ReleaseBuffer();
1833 		}
1834 
1835 	#endif
1836 
1837 		if ( !bLoaded )
1838 			TRACE(_T("String not loaded 0x%X\n"), ::GetLastError());
1839 
1840 		return bLoaded;
1841 	}
1842 #endif
1843 
1844 	// -------------------------------------------------------------------------
1845 	// FUNCTION:  CStdStr::Format
1846 	//		void _cdecl Formst(CStdStringA& PCSTR szFormat, ...)
1847 	//		void _cdecl Format(PCSTR szFormat);
1848 	//
1849 	// DESCRIPTION:
1850 	//		This function does sprintf/wsprintf style formatting on CStdStringA
1851 	//		objects.  It looks a lot like MFC's CString::Format.  Some people
1852 	//		might even call this identical.  Fortunately, these people are now
1853 	//		dead.
1854 	//
1855 	// PARAMETERS:
1856 	//		nId - ID of string resource holding the format string
1857 	//		szFormat - a PCSTR holding the format specifiers
1858 	//		argList - a va_list holding the arguments for the format specifiers.
1859 	//
1860 	// RETURN VALUE:  None.
1861 	// -------------------------------------------------------------------------
1862 	// formatting (using wsprintf style formatting)
1863 	#ifndef SS_ANSI
Format(UINT nId,...)1864 	void Format(UINT nId, ...)
1865 	{
1866 		va_list argList;
1867 		va_start(argList, nId);
1868 		va_start(argList, nId);
1869 
1870 		MYTYPE strFmt;
1871 		if ( strFmt.Load(nId) )
1872 			FormatV(strFmt, argList);
1873 
1874 		va_end(argList);
1875 	}
1876 	#endif
Format(const CT * szFmt,...)1877 	void Format(const CT* szFmt, ...)
1878 	{
1879 		va_list argList;
1880 		va_start(argList, szFmt);
1881 		FormatV(szFmt, argList);
1882 		va_end(argList);
1883 	}
AppendFormat(const CT * szFmt,...)1884 	void AppendFormat(const CT* szFmt, ...)
1885 	{
1886 		va_list argList;
1887 		va_start(argList, szFmt);
1888 		AppendFormatV(szFmt, argList);
1889 		va_end(argList);
1890 	}
1891 
1892 	#define MAX_FMT_TRIES		5	 // #of times we try
1893 	#define FMT_BLOCK_SIZE		2048 // # of bytes to increment per try
1894 	#define BUFSIZE_1ST	256
1895 	#define BUFSIZE_2ND 512
1896 	#define STD_BUF_SIZE		1024
1897 
1898 	// an efficient way to add formatted characters to the string.  You may only
1899 	// add up to STD_BUF_SIZE characters at a time, though
AppendFormatV(const CT * szFmt,va_list argList)1900 	void AppendFormatV(const CT* szFmt, va_list argList)
1901 	{
1902 		CT szBuf[STD_BUF_SIZE];
1903 	#ifdef SS_ANSI
1904 		int nLen = ssvsprintf(szBuf, STD_BUF_SIZE-1, szFmt, argList);
1905 	#else
1906 		int nLen = ssnprintf(szBuf, STD_BUF_SIZE-1, szFmt, argList);
1907 	#endif
1908 		if ( 0 < nLen )
1909 			append(szBuf, nLen);
1910 	}
1911 
1912 	// -------------------------------------------------------------------------
1913 	// FUNCTION:  FormatV
1914 	//		void FormatV(PCSTR szFormat, va_list, argList);
1915 	//
1916 	// DESCRIPTION:
1917 	//		This function formats the string with sprintf style format-specs.
1918 	//		It makes a general guess at required buffer size and then tries
1919 	//		successively larger buffers until it finds one big enough or a
1920 	//		threshold (MAX_FMT_TRIES) is exceeded.
1921 	//
1922 	// PARAMETERS:
1923 	//		szFormat - a PCSTR holding the format of the output
1924 	//		argList - a Microsoft specific va_list for variable argument lists
1925 	//
1926 	// RETURN VALUE:
1927 	// -------------------------------------------------------------------------
1928 
FormatV(const CT * szFormat,va_list argList)1929 	void FormatV(const CT* szFormat, va_list argList)
1930 	{
1931 	#ifdef SS_ANSI
1932 
1933 		int nLen	= sslen(szFormat) + STD_BUF_SIZE;
1934 		ssvsprintf(GetBuffer(nLen), nLen-1, szFormat, argList);
1935 		ReleaseBuffer();
1936 
1937 	#else
1938 
1939 		CT* pBuf			= NULL;
1940 		int nChars			= 1;
1941 		int nUsed			= 0;
1942 		size_type nActual	= 0;
1943 		int nTry			= 0;
1944 
1945 		do
1946 		{
1947 			// Grow more than linearly (e.g. 512, 1536, 3072, etc)
1948 
1949 			nChars			+= ((nTry+1) * FMT_BLOCK_SIZE);
1950 			pBuf			= reinterpret_cast<CT*>(_alloca(sizeof(CT)*nChars));
1951 			nUsed			= ssnprintf(pBuf, nChars-1, szFormat, argList);
1952 
1953 			// Ensure proper NULL termination.
1954 
1955 			nActual			= nUsed == -1 ? nChars-1 : SSMIN(nUsed, nChars-1);
1956 			pBuf[nActual+1]= '\0';
1957 
1958 
1959 		} while ( nUsed < 0 && nTry++ < MAX_FMT_TRIES );
1960 
1961 		// assign whatever we managed to format
1962 
1963 		assign(pBuf, nActual);
1964 
1965 	#endif
1966 	}
1967 
1968 
1969 	// -------------------------------------------------------------------------
1970 	// CString Facade Functions:
1971 	//
1972 	// The following methods are intended to allow you to use this class as a
1973 	// drop-in replacement for CString.
1974 	// -------------------------------------------------------------------------
1975 	#ifndef SS_ANSI
AllocSysString()1976 		BSTR AllocSysString() const
1977 		{
1978 			ostring os;
1979 			ssasn(os, *this);
1980 			return ::SysAllocString(os.c_str());
1981 		}
1982 	#endif
1983 
Collate(PCMYSTR szThat)1984 	int Collate(PCMYSTR szThat) const
1985 	{
1986 		return sscoll(c_str(), length(), szThat, sslen(szThat));
1987 	}
1988 
CollateNoCase(PCMYSTR szThat)1989 	int CollateNoCase(PCMYSTR szThat) const
1990 	{
1991 		return ssicoll(c_str(), length(), szThat, sslen(szThat));
1992 	}
1993 
Compare(PCMYSTR szThat)1994 	int Compare(PCMYSTR szThat) const
1995 	{
1996 		return MYBASE::compare(szThat);
1997 	}
1998 
CompareNoCase(PCMYSTR szThat)1999 	int CompareNoCase(PCMYSTR szThat)	const
2000 	{
2001 		return ssicmp(c_str(), szThat);
2002 	}
2003 
2004 	int Delete(int nIdx, int nCount=1)
2005 	{
2006 		if ( nIdx < GetLength() )
2007 			erase(static_cast<MYSIZE>(nIdx), static_cast<MYSIZE>(nCount));
2008 
2009 		return GetLength();
2010 	}
2011 
Empty()2012 	void Empty()
2013 	{
2014 		erase();
2015 	}
2016 
Find(CT ch)2017 	int Find(CT ch) const
2018 	{
2019 		MYSIZE nIdx	= find_first_of(ch);
2020 		return static_cast<int>(MYBASE::npos == nIdx  ? -1 : nIdx);
2021 	}
2022 
Find(PCMYSTR szSub)2023 	int Find(PCMYSTR szSub) const
2024 	{
2025 		MYSIZE nIdx	= find(szSub);
2026 		return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
2027 	}
2028 
Find(CT ch,int nStart)2029 	int Find(CT ch, int nStart) const
2030 	{
2031 		// CString::Find docs say add 1 to nStart when it's not zero
2032 		// CString::Find code doesn't do that however.  We'll stick
2033 		// with what the code does
2034 
2035 		MYSIZE nIdx	= find_first_of(ch, static_cast<MYSIZE>(nStart));
2036 		return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
2037 	}
2038 
Find(PCMYSTR szSub,int nStart)2039 	int Find(PCMYSTR szSub, int nStart) const
2040 	{
2041 		// CString::Find docs say add 1 to nStart when it's not zero
2042 		// CString::Find code doesn't do that however.  We'll stick
2043 		// with what the code does
2044 
2045 		MYSIZE nIdx	= find(szSub, static_cast<MYSIZE>(nStart));
2046 		return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
2047 	}
2048 
FindOneOf(PCMYSTR szCharSet)2049 	int FindOneOf(PCMYSTR szCharSet) const
2050 	{
2051 		MYSIZE nIdx = find_first_of(szCharSet);
2052 		return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
2053 	}
2054 
2055 #ifndef SS_ANSI
FormatMessage(PCMYSTR szFormat,...)2056 	void FormatMessage(PCMYSTR szFormat, ...) throw(std::exception)
2057 	{
2058 		va_list argList;
2059 		va_start(argList, szFormat);
2060 		PMYSTR szTemp;
2061 		if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
2062 					   szFormat, 0, 0,
2063 					   reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||
2064 			 szTemp == 0 )
2065 		{
2066 			throw std::runtime_error("out of memory");
2067 		}
2068 		*this = szTemp;
2069 		LocalFree(szTemp);
2070 		va_end(argList);
2071 	}
2072 
FormatMessage(UINT nFormatId,...)2073 	void FormatMessage(UINT nFormatId, ...) throw(std::exception)
2074 	{
2075 		MYTYPE sFormat;
2076 		VERIFY(sFormat.LoadString(nFormatId) != 0);
2077 		va_list argList;
2078 		va_start(argList, nFormatId);
2079 		PMYSTR szTemp;
2080 		if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
2081 					   sFormat, 0, 0,
2082 					   reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||
2083 			szTemp == 0)
2084 		{
2085 			throw std::runtime_error("out of memory");
2086 		}
2087 		*this = szTemp;
2088 		LocalFree(szTemp);
2089 		va_end(argList);
2090 	}
2091 #endif
2092 
2093 
2094 	// -------------------------------------------------------------------------
2095 	// GetXXXX -- Direct access to character buffer
2096 	// -------------------------------------------------------------------------
GetAt(int nIdx)2097 	CT GetAt(int nIdx) const
2098 	{
2099 		return at(static_cast<MYSIZE>(nIdx));
2100 	}
2101 
2102 	CT* GetBuffer(int nMinLen=-1)
2103 	{
2104 		return GetBuf(nMinLen);
2105 	}
2106 
GetBufferSetLength(int nLen)2107 	CT* GetBufferSetLength(int nLen)
2108 	{
2109 		return BufferSet(nLen);
2110 	}
2111 
2112 	// GetLength() -- MFC docs say this is the # of BYTES but
2113 	// in truth it is the number of CHARACTERs (chars or wchar_ts)
GetLength()2114 	int GetLength() const
2115 	{
2116 		return static_cast<int>(length());
2117 	}
2118 
2119 
Insert(int nIdx,CT ch)2120 	int Insert(int nIdx, CT ch)
2121 	{
2122 		if ( static_cast<MYSIZE>(nIdx) > size() -1 )
2123 			append(1, ch);
2124 		else
2125 			insert(static_cast<MYSIZE>(nIdx), 1, ch);
2126 
2127 		return GetLength();
2128 	}
Insert(int nIdx,PCMYSTR sz)2129 	int Insert(int nIdx, PCMYSTR sz)
2130 	{
2131 		if ( nIdx >= size() )
2132 			append(sz, sslen(sz));
2133 		else
2134 			insert(static_cast<MYSIZE>(nIdx), sz);
2135 
2136 		return GetLength();
2137 	}
2138 
IsEmpty()2139 	bool IsEmpty() const
2140 	{
2141 		return empty();
2142 	}
2143 
Left(int nCount)2144 	MYTYPE Left(int nCount) const
2145 	{
2146 		return substr(0, static_cast<MYSIZE>(nCount));
2147 	}
2148 
2149 	#ifndef SS_ANSI
LoadString(UINT nId)2150 	bool LoadString(UINT nId)
2151 	{
2152 		return this->Load(nId);
2153 	}
2154 	#endif
2155 
MakeLower()2156 	void MakeLower()
2157 	{
2158 		ToLower();
2159 	}
2160 
MakeReverse()2161 	void MakeReverse()
2162 	{
2163 		std::reverse(begin(), end());
2164 	}
2165 
MakeUpper()2166 	void MakeUpper()
2167 	{
2168 		ToUpper();
2169 	}
2170 
Mid(int nFirst)2171 	MYTYPE Mid(int nFirst ) const
2172 	{
2173 		return substr(static_cast<MYSIZE>(nFirst));
2174 	}
2175 
Mid(int nFirst,int nCount)2176 	MYTYPE Mid(int nFirst, int nCount) const
2177 	{
2178 		return substr(static_cast<MYSIZE>(nFirst), static_cast<MYSIZE>(nCount));
2179 	}
2180 
2181 	void ReleaseBuffer(int nNewLen=-1)
2182 	{
2183 		RelBuf(nNewLen);
2184 	}
2185 
Remove(CT ch)2186 	int Remove(CT ch)
2187 	{
2188 		MYSIZE nIdx		= 0;
2189 		int nRemoved	= 0;
2190 		while ( (nIdx=find_first_of(ch)) != MYBASE::npos )
2191 		{
2192 			erase(nIdx, 1);
2193 			nRemoved++;
2194 		}
2195 		return nRemoved;
2196 	}
2197 
Replace(CT chOld,CT chNew)2198 	int Replace(CT chOld, CT chNew)
2199 	{
2200 		int nReplaced	= 0;
2201 		for ( MYITER iter=begin(); iter != end(); iter++ )
2202 		{
2203 			if ( *iter == chOld )
2204 			{
2205 				*iter = chNew;
2206 				nReplaced++;
2207 			}
2208 		}
2209 		return nReplaced;
2210 	}
2211 
Replace(PCMYSTR szOld,PCMYSTR szNew)2212 	int Replace(PCMYSTR szOld, PCMYSTR szNew)
2213 	{
2214 		int nReplaced		= 0;
2215 		MYSIZE nIdx			= 0;
2216 		MYSIZE nOldLen		= sslen(szOld);
2217 		if ( 0 == nOldLen )
2218 			return 0;
2219 
2220 		static const CT ch	= CT(0);
2221 		MYSIZE nNewLen		= sslen(szNew);
2222 		PCMYSTR szRealNew	= szNew == 0 ? &ch : szNew;
2223 
2224 		while ( (nIdx=find(szOld, nIdx)) != MYBASE::npos )
2225 		{
2226 			replace(begin()+nIdx, begin()+nIdx+nOldLen, szRealNew);
2227 			nReplaced++;
2228 			nIdx += nNewLen;
2229 		}
2230 		return nReplaced;
2231 	}
2232 
ReverseFind(CT ch)2233 	int ReverseFind(CT ch) const
2234 	{
2235 		MYSIZE nIdx	= find_last_of(ch);
2236 		return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
2237 	}
2238 
2239 	// ReverseFind overload that's not in CString but might be useful
2240 	int ReverseFind(PCMYSTR szFind, MYSIZE pos=MYBASE::npos) const
2241 	{
2242 		MYSIZE nIdx	= rfind(0 == szFind ? MYTYPE() : szFind, pos);
2243 		return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
2244 	}
2245 
Right(int nCount)2246 	MYTYPE Right(int nCount) const
2247 	{
2248 		nCount = SSMIN(nCount, static_cast<int>(size()));
2249 		return substr(size()-static_cast<MYSIZE>(nCount));
2250 	}
2251 
SetAt(int nIndex,CT ch)2252 	void SetAt(int nIndex, CT ch)
2253 	{
2254 		ASSERT(size() > static_cast<MYSIZE>(nIndex));
2255 		at(static_cast<MYSIZE>(nIndex))		= ch;
2256 	}
2257 
2258 	#ifndef SS_ANSI
SetSysString(BSTR * pbstr)2259 		BSTR SetSysString(BSTR* pbstr) const
2260 		{
2261 			ostring os;
2262 			ssasn(os, *this);
2263 			if ( !::SysReAllocStringLen(pbstr, os.c_str(), os.length()) )
2264 				throw std::runtime_error("out of memory");
2265 
2266 			ASSERT(*pbstr != 0);
2267 			return *pbstr;
2268 		}
2269 	#endif
2270 
SpanExcluding(PCMYSTR szCharSet)2271 	MYTYPE SpanExcluding(PCMYSTR szCharSet) const
2272 	{
2273 		return Left(find_first_of(szCharSet));
2274 	}
2275 
SpanIncluding(PCMYSTR szCharSet)2276 	MYTYPE SpanIncluding(PCMYSTR szCharSet) const
2277 	{
2278 		return Left(find_first_not_of(szCharSet));
2279 	}
2280 
2281 	#if !defined(UNICODE) && !defined(SS_ANSI)
2282 
2283 		// CString's OemToAnsi and AnsiToOem functions are available only in
2284 		// Unicode builds.  However since we're a template we also need a
2285 		// runtime check of CT and a reinterpret_cast to account for the fact
2286 		// that CStdStringW gets instantiated even in non-Unicode builds.
2287 
AnsiToOem()2288 		void AnsiToOem()
2289 		{
2290 			if ( sizeof(CT) == sizeof(char) && !empty() )
2291 			{
2292 				::CharToOem(reinterpret_cast<PCSTR>(c_str()),
2293 							reinterpret_cast<PSTR>(GetBuf()));
2294 			}
2295 			else
2296 			{
2297 				ASSERT(false);
2298 			}
2299 		}
2300 
OemToAnsi()2301 		void OemToAnsi()
2302 		{
2303 			if ( sizeof(CT) == sizeof(char) && !empty() )
2304 			{
2305 				::OemToChar(reinterpret_cast<PCSTR>(c_str()),
2306 							reinterpret_cast<PSTR>(GetBuf()));
2307 			}
2308 			else
2309 			{
2310 				ASSERT(false);
2311 			}
2312 		}
2313 
2314 	#endif
2315 
2316 
2317 	// -------------------------------------------------------------------------
2318 	// Trim and its variants
2319 	// -------------------------------------------------------------------------
Trim()2320 	MYTYPE& Trim()
2321 	{
2322 		return TrimLeft().TrimRight();
2323 	}
2324 
TrimLeft()2325 	MYTYPE& TrimLeft()
2326 	{
2327 		erase(begin(), std::find_if(begin(),end(),NotSpace<CT>(std::locale())));
2328 		return *this;
2329 	}
2330 
TrimLeft(CT tTrim)2331 	MYTYPE&  TrimLeft(CT tTrim)
2332 	{
2333 		erase(0, find_first_not_of(tTrim));
2334 		return *this;
2335 	}
2336 
TrimLeft(PCMYSTR szTrimChars)2337 	MYTYPE&  TrimLeft(PCMYSTR szTrimChars)
2338 	{
2339 		erase(0, find_first_not_of(szTrimChars));
2340 		return *this;
2341 	}
2342 
TrimRight()2343 	MYTYPE& TrimRight()
2344 	{
2345 		std::locale loc;
2346 		MYRITER it = std::find_if(rbegin(), rend(), NotSpace<CT>(loc));
2347 		if ( rend() != it )
2348 			erase(rend() - it);
2349 
2350 		erase(it != rend() ? find_last_of(*it) + 1 : 0);
2351 		return *this;
2352 	}
2353 
TrimRight(CT tTrim)2354 	MYTYPE&  TrimRight(CT tTrim)
2355 	{
2356 		MYSIZE nIdx	= find_last_not_of(tTrim);
2357 		erase(MYBASE::npos == nIdx ? 0 : ++nIdx);
2358 		return *this;
2359 	}
2360 
TrimRight(PCMYSTR szTrimChars)2361 	MYTYPE&  TrimRight(PCMYSTR szTrimChars)
2362 	{
2363 		MYSIZE nIdx	= find_last_not_of(szTrimChars);
2364 		erase(MYBASE::npos == nIdx ? 0 : ++nIdx);
2365 		return *this;
2366 	}
2367 
FreeExtra()2368 	void			FreeExtra()
2369 	{
2370 		MYTYPE mt;
2371 		swap(mt);
2372 		if ( !mt.empty() )
2373 			assign(mt.c_str(), mt.size());
2374 	}
2375 
2376 	// I have intentionally not implemented the following CString
2377 	// functions.   You cannot make them work without taking advantage
2378 	// of implementation specific behavior.  However if you absolutely
2379 	// MUST have them, uncomment out these lines for "sort-of-like"
2380 	// their behavior.  You're on your own.
2381 
2382 //	CT*				LockBuffer()	{ return GetBuf(); }// won't really lock
2383 //	void			UnlockBuffer(); { }	// why have UnlockBuffer w/o LockBuffer?
2384 
2385 	// Array-indexing operators.  Required because we defined an implicit cast
2386 	// to operator const CT* (Thanks to Julian Selman for pointing this out)
2387 	CT& operator[](int nIdx)
2388 	{
2389 		return MYBASE::operator[](static_cast<MYSIZE>(nIdx));
2390 	}
2391 
2392 	const CT& operator[](int nIdx) const
2393 	{
2394 		return MYBASE::operator[](static_cast<MYSIZE>(nIdx));
2395 	}
2396 
2397 	CT& operator[](unsigned int nIdx)
2398 	{
2399 		return MYBASE::operator[](static_cast<MYSIZE>(nIdx));
2400 	}
2401 
2402 	const CT& operator[](unsigned int nIdx) const
2403 	{
2404 		return MYBASE::operator[](static_cast<MYSIZE>(nIdx));
2405 	}
2406 
2407 	operator const CT*() const
2408 	{
2409 		return c_str();
2410 	}
2411 
2412 	// IStream related functions.  Useful in IPersistStream implementations
2413 
2414 #ifdef SS_INC_COMDEF
2415 
2416 	// struct SSSHDR - useful for non Std C++ persistence schemes.
2417 	typedef struct SSSHDR
2418 	{
2419 		BYTE	byCtrl;
2420 		ULONG	nChars;
2421 	} SSSHDR;	// as in "Standard String Stream Header"
2422 
2423 	#define SSSO_UNICODE	0x01	// the string is a wide string
2424 	#define SSSO_COMPRESS	0x02	// the string is compressed
2425 
2426 	// -------------------------------------------------------------------------
2427 	// FUNCTION: StreamSize
2428 	// REMARKS:
2429 	//		Returns how many bytes it will take to StreamSave() this CStdString
2430 	//		object to an IStream.
2431 	// -------------------------------------------------------------------------
StreamSize()2432 	ULONG StreamSize() const
2433 	{
2434 		// Control header plus string
2435 		ASSERT(size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));
2436 		return (size() * sizeof(CT)) + sizeof(SSSHDR);
2437 	}
2438 
2439 	// -------------------------------------------------------------------------
2440 	// FUNCTION: StreamSave
2441 	// REMARKS:
2442 	//		Saves this CStdString object to a COM IStream.
2443 	// -------------------------------------------------------------------------
StreamSave(IStream * pStream)2444 	HRESULT StreamSave(IStream* pStream) const
2445 	{
2446 		ASSERT(size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));
2447 		HRESULT hr		= E_FAIL;
2448 		ASSERT(pStream != 0);
2449 		SSSHDR hdr;
2450 		hdr.byCtrl		= sizeof(CT) == 2 ? SSSO_UNICODE : 0;
2451 		hdr.nChars		= size();
2452 
2453 
2454 		if ( FAILED(hr=pStream->Write(&hdr, sizeof(SSSHDR), 0)) )
2455 			TRACE(_T("StreamSave: Cannot write control header, ERR=0x%X\n"),hr);
2456 		else if ( empty() )
2457 			;		// nothing to write
2458 		else if ( FAILED(hr=pStream->Write(c_str(), size()*sizeof(CT), 0)) )
2459 			TRACE(_T("StreamSave: Cannot write string to stream 0x%X\n"), hr);
2460 
2461 		return hr;
2462 	}
2463 
2464 
2465 	// -------------------------------------------------------------------------
2466 	// FUNCTION: StreamLoad
2467 	// REMARKS:
2468 	//		This method loads the object from an IStream.
2469 	// -------------------------------------------------------------------------
StreamLoad(IStream * pStream)2470 	HRESULT StreamLoad(IStream* pStream)
2471 	{
2472 		ASSERT(pStream != 0);
2473 		SSSHDR hdr;
2474 		HRESULT hr			= E_FAIL;
2475 
2476 		if ( FAILED(hr=pStream->Read(&hdr, sizeof(SSSHDR), 0)) )
2477 		{
2478 			TRACE(_T("StreamLoad: Cant read control header, ERR=0x%X\n"), hr);
2479 		}
2480 		else if ( hdr.nChars > 0 )
2481 		{
2482 			ULONG nRead		= 0;
2483 			PMYSTR pMyBuf	= BufferSet(hdr.nChars);
2484 
2485 			// If our character size matches the character size of the string
2486 			// we're trying to read, then we can read it directly into our
2487 			// buffer. Otherwise, we have to read into an intermediate buffer
2488 			// and convert.
2489 
2490 			if ( (hdr.byCtrl & SSSO_UNICODE) != 0 )
2491 			{
2492 				ULONG nBytes	= hdr.nChars * sizeof(wchar_t);
2493 				if ( sizeof(CT) == sizeof(wchar_t) )
2494 				{
2495 					if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )
2496 						TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
2497 				}
2498 				else
2499 				{
2500 					PWSTR pBufW = reinterpret_cast<PWSTR>(_alloca((nBytes)+1));
2501 					if ( FAILED(hr=pStream->Read(pBufW, nBytes, &nRead)) )
2502 						TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
2503 					else
2504 						sscpy(pMyBuf, pBufW, hdr.nChars);
2505 				}
2506 			}
2507 			else
2508 			{
2509 				ULONG nBytes	= hdr.nChars * sizeof(char);
2510 				if ( sizeof(CT) == sizeof(char) )
2511 				{
2512 					if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )
2513 						TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
2514 				}
2515 				else
2516 				{
2517 					PSTR pBufA = reinterpret_cast<PSTR>(_alloca(nBytes));
2518 					if ( FAILED(hr=pStream->Read(pBufA, hdr.nChars, &nRead)) )
2519 						TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
2520 					else
2521 						sscpy(pMyBuf, pBufA, hdr.nChars);
2522 				}
2523 			}
2524 		}
2525 		else
2526 		{
2527 			this->erase();
2528 		}
2529 		return hr;
2530 	}
2531 #endif // #ifdef SS_INC_COMDEF
2532 
2533 #ifndef SS_ANSI
2534 
2535 	// SetResourceHandle/GetResourceHandle.  In MFC builds, these map directly
2536 	// to AfxSetResourceHandle and AfxGetResourceHandle.  In non-MFC builds they
2537 	// point to a single static HINST so that those who call the member
2538 	// functions that take resource IDs can provide an alternate HINST of a DLL
2539 	// to search.  This is not exactly the list of HMODULES that MFC provides
2540 	// but it's better than nothing.
2541 
2542 	#ifdef _MFC_VER
SetResourceHandle(HMODULE hNew)2543 		static void SetResourceHandle(HMODULE hNew)
2544 		{
2545 			AfxSetResourceHandle(hNew);
2546 		}
GetResourceHandle()2547 		static HMODULE GetResourceHandle()
2548 		{
2549 			return AfxGetResourceHandle();
2550 		}
2551 	#else
SetResourceHandle(HMODULE hNew)2552 		static void SetResourceHandle(HMODULE hNew)
2553 		{
2554 			SSResourceHandle() = hNew;
2555 		}
GetResourceHandle()2556 		static HMODULE GetResourceHandle()
2557 		{
2558 			return SSResourceHandle();
2559 		}
2560 	#endif
2561 
2562 #endif
2563 };
2564 
2565 
2566 
2567 // -----------------------------------------------------------------------------
2568 // CStdStr friend addition functions defined as inline
2569 // -----------------------------------------------------------------------------
2570 template<typename CT>
2571 inline
2572 CStdStr<CT> operator+(const  CStdStr<CT>& str1, const  CStdStr<CT>& str2)
2573 {
2574 	CStdStr<CT> strRet(SSREF(str1));
2575 	strRet.append(str2);
2576 	return strRet;
2577 }
2578 
2579 template<typename CT>
2580 inline
2581 CStdStr<CT> operator+(const  CStdStr<CT>& str, CT t)
2582 {
2583 	// this particular overload is needed for disabling reference counting
2584 	// though it's only an issue from line 1 to line 2
2585 
2586 	CStdStr<CT> strRet(SSREF(str));	// 1
2587 	strRet.append(1, t);				// 2
2588 	return strRet;
2589 }
2590 
2591 template<typename CT>
2592 inline
2593 CStdStr<CT> operator+(const  CStdStr<CT>& str, PCSTR pA)
2594 {
2595 	return CStdStr<CT>(str) + CStdStr<CT>(pA);
2596 }
2597 
2598 template<typename CT>
2599 inline
2600 CStdStr<CT> operator+(PCSTR pA, const  CStdStr<CT>& str)
2601 {
2602 	CStdStr<CT> strRet(pA);
2603 	strRet.append(str);
2604 	return strRet;
2605 }
2606 
2607 template<typename CT>
2608 inline
2609 CStdStr<CT> operator+(const CStdStr<CT>& str, PCWSTR pW)
2610 {
2611 	return CStdStr<CT>(SSREF(str)) + CStdStr<CT>(pW);
2612 }
2613 
2614 template<typename CT>
2615 inline
2616 CStdStr<CT> operator+(PCWSTR pW, const CStdStr<CT>& str)
2617 {
2618 	CStdStr<CT> strRet(pW);
2619 	strRet.append(str);
2620 	return strRet;
2621 }
2622 
2623 #ifdef SS_INC_COMDEF
2624 	template<typename CT>
2625 	inline
2626 	CStdStr<CT> operator+(const _bstr_t& bstr, const CStdStr<CT>& str)
2627 	{
2628 		return static_cast<const CT*>(bstr) + str;
2629 	}
2630 
2631 	template<typename CT>
2632 	inline
2633 	CStdStr<CT> operator+(const CStdStr<CT>& str, const _bstr_t& bstr)
2634 	{
2635 		return str + static_cast<const CT*>(bstr);
2636 	}
2637 #endif
2638 
2639 
2640 
2641 
2642 // -----------------------------------------------------------------------------
2643 // These versions of operator+ provided by Scott Hathaway in order to allow
2644 // CStdString to build on Sun Unix systems.
2645 // -----------------------------------------------------------------------------
2646 
2647 #if defined(__SUNPRO_CC_COMPAT) || defined(__SUNPRO_CC)
2648 
2649 // Made non-template versions due to "undefined" errors on Sun Forte compiler
2650 // when linking with friend template functions
2651 inline
2652 CStdStr<wchar_t> operator+(const CStdStr<wchar_t>& str1,
2653 						   const CStdStr<wchar_t>& str2)
2654 {
2655 	CStdStr<wchar_t> strRet(SSREF(str1));
2656 	strRet.append(str2);
2657 	return strRet;
2658 }
2659 
2660 inline
2661 CStdStr<wchar_t> operator+(const CStdStr<wchar_t>& str, wchar_t t)
2662 {
2663 	// this particular overload is needed for disabling reference counting
2664 	// though it's only an issue from line 1 to line 2
2665 
2666 	CStdStr<wchar_t> strRet(SSREF(str));	// 1
2667 	strRet.append(1, t);					// 2
2668 	return strRet;
2669 }
2670 
2671 inline
2672 CStdStr<wchar_t> operator+(const CStdStr<wchar_t>& str, PCWSTR pW)
2673 {
2674 	return CStdStr<wchar_t>(str) + CStdStr<wchar_t>(pW);
2675 }
2676 
2677 inline
2678 CStdStr<wchar_t> operator+(PCWSTR pA, const  CStdStr<wchar_t>& str)
2679 {
2680 	CStdStr<wchar_t> strRet(pA);
2681 	strRet.append(str);
2682 	return strRet;
2683 }
2684 
2685 inline
2686 CStdStr<wchar_t> operator+(const CStdStr<wchar_t>& str, PCSTR pW)
2687 {
2688 	return CStdStr<wchar_t>(SSREF(str)) + CStdStr<wchar_t>(pW);
2689 }
2690 
2691 inline
2692 CStdStr<wchar_t> operator+(PCSTR pW, const CStdStr<wchar_t>& str)
2693 {
2694 	CStdStr<wchar_t> strRet(pW);
2695 	strRet.append(str);
2696 	return strRet;
2697 }
2698 
2699 inline
2700 CStdStr<char> operator+(const  CStdStr<char>& str1, const  CStdStr<char>& str2)
2701 {
2702 	CStdStr<char> strRet(SSREF(str1));
2703 	strRet.append(str2);
2704 	return strRet;
2705 }
2706 
2707 inline
2708 CStdStr<char> operator+(const  CStdStr<char>& str, char t)
2709 {
2710 	// this particular overload is needed for disabling reference counting
2711 	// though it's only an issue from line 1 to line 2
2712 
2713 	CStdStr<char> strRet(SSREF(str));	// 1
2714 	strRet.append(1, t);				// 2
2715 	return strRet;
2716 }
2717 
2718 inline
2719 CStdStr<char> operator+(const  CStdStr<char>& str, PCSTR pA)
2720 {
2721 	return CStdStr<char>(str) + CStdStr<char>(pA);
2722 }
2723 
2724 inline
2725 CStdStr<char> operator+(PCSTR pA, const  CStdStr<char>& str)
2726 {
2727 	CStdStr<char> strRet(pA);
2728 	strRet.append(str);
2729 	return strRet;
2730 }
2731 
2732 inline
2733 CStdStr<char> operator+(const CStdStr<char>& str, PCWSTR pW)
2734 {
2735 	return CStdStr<char>(SSREF(str)) + CStdStr<char>(pW);
2736 }
2737 
2738 inline
2739 CStdStr<char> operator+(PCWSTR pW, const CStdStr<char>& str)
2740 {
2741 	CStdStr<char> strRet(pW);
2742 	strRet.append(str);
2743 	return strRet;
2744 }
2745 
2746 
2747 #endif // defined(__SUNPRO_CC_COMPAT) || defined(__SUNPRO_CC)
2748 
2749 
2750 // =============================================================================
2751 //						END OF CStdStr INLINE FUNCTION DEFINITIONS
2752 // =============================================================================
2753 
2754 //	Now typedef our class names based upon this humongous template
2755 
2756 typedef CStdStr<char>		CStdStringA;	// a better std::string
2757 typedef CStdStr<wchar_t>	CStdStringW;	// a better std::wstring
2758 typedef CStdStr<OLECHAR>	CStdStringO;	// almost always CStdStringW
2759 
2760 #ifndef SS_ANSI
2761 	// SSResourceHandle: our MFC-like resource handle
SSResourceHandle()2762 	inline HMODULE& SSResourceHandle()
2763 	{
2764 		static HMODULE hModuleSS	= GetModuleHandle(0);
2765 		return hModuleSS;
2766 	}
2767 #endif
2768 
2769 
2770 // In MFC builds, define some global serialization operators
2771 // Special operators that allow us to serialize CStdStrings to CArchives.
2772 // Note that we use an intermediate CString object in order to ensure that
2773 // we use the exact same format.
2774 
2775 #ifdef _MFC_VER
2776 	inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringA& strA)
2777 	{
2778 		CString strTemp	= strA;
2779 		return ar << strTemp;
2780 	}
2781 	inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringW& strW)
2782 	{
2783 		CString strTemp	= strW;
2784 		return ar << strTemp;
2785 	}
2786 
2787 	inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringA& strA)
2788 	{
2789 		CString strTemp;
2790 		ar >> strTemp;
2791 		strA = strTemp;
2792 		return ar;
2793 	}
2794 	inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringW& strW)
2795 	{
2796 		CString strTemp;
2797 		ar >> strTemp;
2798 		strW = strTemp;
2799 		return ar;
2800 	}
2801 #endif	// #ifdef _MFC_VER -- (i.e. is this MFC?)
2802 
2803 
2804 
2805 // -----------------------------------------------------------------------------
2806 // HOW TO EXPORT CSTDSTRING FROM A DLL
2807 //
2808 // If you want to export CStdStringA and CStdStringW from a DLL, then all you
2809 // need to
2810 //		1.	make sure that all components link to the same DLL version
2811 //			of the CRT (not the static one).
2812 //		2.	Uncomment the 3 lines of code below
2813 //		3.	#define 2 macros per the instructions in MS KnowledgeBase
2814 //			article Q168958.  The macros are:
2815 //
2816 //		MACRO		DEFINTION WHEN EXPORTING		DEFINITION WHEN IMPORTING
2817 //		-----		------------------------		-------------------------
2818 //		SSDLLEXP	(nothing, just #define it)		extern
2819 //		SSDLLSPEC	__declspec(dllexport)			__declspec(dllimport)
2820 //
2821 //		Note that these macros must be available to ALL clients who want to
2822 //		link to the DLL and use the class.  If they
2823 // -----------------------------------------------------------------------------
2824 //#pragma warning(disable:4231) // non-standard extension ("extern template")
2825 //	SSDLLEXP template class SSDLLSPEC CStdStr<char>;
2826 //	SSDLLEXP template class SSDLLSPEC CStdStr<wchar_t>;
2827 
2828 
2829 // -----------------------------------------------------------------------------
2830 // GLOBAL FUNCTION:  WUFormat
2831 //		CStdStringA WUFormat(UINT nId, ...);
2832 //		CStdStringA WUFormat(PCSTR szFormat, ...);
2833 //
2834 // REMARKS:
2835 //		This function allows the caller for format and return a CStdStringA
2836 //		object with a single line of code.
2837 // -----------------------------------------------------------------------------
2838 #ifdef SS_ANSI
2839 #else
WUFormatA(UINT nId,...)2840 	inline CStdStringA WUFormatA(UINT nId, ...)
2841 	{
2842 		va_list argList;
2843 		va_start(argList, nId);
2844 
2845 		CStdStringA strFmt;
2846 		CStdStringA strOut;
2847 		if ( strFmt.Load(nId) )
2848 			strOut.FormatV(strFmt, argList);
2849 
2850 		va_end(argList);
2851 		return strOut;
2852 	}
WUFormatA(PCSTR szFormat,...)2853 	inline CStdStringA WUFormatA(PCSTR szFormat, ...)
2854 	{
2855 		va_list argList;
2856 		va_start(argList, szFormat);
2857 		CStdStringA strOut;
2858 		strOut.FormatV(szFormat, argList);
2859 		va_end(argList);
2860 		return strOut;
2861 	}
2862 
WUFormatW(UINT nId,...)2863 	inline CStdStringW WUFormatW(UINT nId, ...)
2864 	{
2865 		va_list argList;
2866 		va_start(argList, nId);
2867 
2868 		CStdStringW strFmt;
2869 		CStdStringW strOut;
2870 		if ( strFmt.Load(nId) )
2871 			strOut.FormatV(strFmt, argList);
2872 
2873 		va_end(argList);
2874 		return strOut;
2875 	}
WUFormatW(PCWSTR szwFormat,...)2876 	inline CStdStringW WUFormatW(PCWSTR szwFormat, ...)
2877 	{
2878 		va_list argList;
2879 		va_start(argList, szwFormat);
2880 		CStdStringW strOut;
2881 		strOut.FormatV(szwFormat, argList);
2882 		va_end(argList);
2883 		return strOut;
2884 	}
2885 #endif // #ifdef SS_ANSI
2886 
2887 #ifdef SS_ANSI
2888 #else
2889 	// -------------------------------------------------------------------------
2890 	// FUNCTION: WUSysMessage
2891 	//	 CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);
2892 	//	 CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);
2893 	//
2894 	// DESCRIPTION:
2895 	//	 This function simplifies the process of obtaining a string equivalent
2896 	//	 of a system error code returned from GetLastError().  You simply
2897 	//	 supply the value returned by GetLastError() to this function and the
2898 	//	 corresponding system string is returned in the form of a CStdStringA.
2899 	//
2900 	// PARAMETERS:
2901 	//	 dwError - a DWORD value representing the error code to be translated
2902 	//	 dwLangId - the language id to use.  defaults to english.
2903 	//
2904 	// RETURN VALUE:
2905 	//	 a CStdStringA equivalent of the error code.  Currently, this function
2906 	//	 only returns either English of the system default language strings.
2907 	// -------------------------------------------------------------------------
2908 	#define SS_DEFLANGID MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT)
2909 	inline CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)
2910 	{
2911 		CHAR szBuf[512];
2912 
2913 		if ( 0 != ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
2914 								   dwLangId, szBuf, 511, NULL) )
2915 			return WUFormatA("%s (0x%X)", szBuf, dwError);
2916 		else
2917  			return WUFormatA("Unknown error (0x%X)", dwError);
2918 	}
2919 	inline CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)
2920 	{
2921 		WCHAR szBuf[512];
2922 
2923 		if ( 0 != ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
2924 								   dwLangId, szBuf, 511, NULL) )
2925 			return WUFormatW(L"%s (0x%X)", szBuf, dwError);
2926 		else
2927  			return WUFormatW(L"Unknown error (0x%X)", dwError);
2928 	}
2929 #endif
2930 
2931 // Define TCHAR based friendly names for some of these functions
2932 
2933 #ifdef UNICODE
2934 	#define CStdString				CStdStringW
2935 	#define WUSysMessage			WUSysMessageW
2936 	#define WUFormat				WUFormatW
2937 #else
2938 	#define CStdString				CStdStringA
2939 	#define WUSysMessage			WUSysMessageA
2940 	#define WUFormat				WUFormatA
2941 #endif
2942 
2943 // ...and some shorter names for the space-efficient
2944 
2945 #define WUSysMsg					WUSysMessage
2946 #define WUSysMsgA					WUSysMessageA
2947 #define WUSysMsgW					WUSysMessageW
2948 #define WUFmtA						WUFormatA
2949 #define	WUFmtW						WUFormatW
2950 #define WUFmt						WUFormat
2951 #define WULastErrMsg()				WUSysMessage(::GetLastError())
2952 #define WULastErrMsgA()				WUSysMessageA(::GetLastError())
2953 #define WULastErrMsgW()				WUSysMessageW(::GetLastError())
2954 
2955 
2956 // -----------------------------------------------------------------------------
2957 // FUNCTIONAL COMPARATORS:
2958 // REMARKS:
2959 //		These structs are derived from the std::binary_function template.  They
2960 //		give us functional classes (which may be used in Standard C++ Library
2961 //		collections and algorithms) that perform case-insensitive comparisons of
2962 //		CStdString objects.  This is useful for maps in which the key may be the
2963 //		 proper string but in the wrong case.
2964 // -----------------------------------------------------------------------------
2965 #define StdStringLessNoCaseW		SSLNCW	// avoid VC compiler warning 4786
2966 #define StdStringEqualsNoCaseW		SSENCW
2967 #define StdStringLessNoCaseA		SSLNCA
2968 #define StdStringEqualsNoCaseA		SSENCA
2969 
2970 #ifdef UNICODE
2971 	#define StdStringLessNoCase		SSLNCW
2972 	#define StdStringEqualsNoCase	SSENCW
2973 #else
2974 	#define StdStringLessNoCase		SSLNCA
2975 	#define StdStringEqualsNoCase	SSENCA
2976 #endif
2977 
2978 struct StdStringLessNoCaseW
2979 	: std::binary_function<CStdStringW, CStdStringW, bool>
2980 {
2981 	inline
operatorStdStringLessNoCaseW2982 	bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const
2983 	{ return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }
2984 };
2985 struct StdStringEqualsNoCaseW
2986 	: std::binary_function<CStdStringW, CStdStringW, bool>
2987 {
2988 	inline
operatorStdStringEqualsNoCaseW2989 	bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const
2990 	{ return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }
2991 };
2992 struct StdStringLessNoCaseA
2993 	: std::binary_function<CStdStringA, CStdStringA, bool>
2994 {
2995 	inline
operatorStdStringLessNoCaseA2996 	bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const
2997 	{ return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }
2998 };
2999 struct StdStringEqualsNoCaseA
3000 	: std::binary_function<CStdStringA, CStdStringA, bool>
3001 {
3002 	inline
operatorStdStringEqualsNoCaseA3003 	bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const
3004 	{ return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }
3005 };
3006 
3007 // If we had to define our own version of TRACE above, get rid of it now
3008 
3009 #ifdef TRACE_DEFINED_HERE
3010 	#undef TRACE
3011 	#undef TRACE_DEFINED_HERE
3012 #endif
3013 
3014 
3015 #endif	// #ifndef STDSTRING_H