/*****************************************************************/ /** Microsoft Windows for Workgroups **/ /** Copyright (C) Microsoft Corp., 1991-1992 **/ /*****************************************************************/ /* npstring.h String classes: definition This file contains the basic string classes for the Thor UI. Its requirements are: - provide a modestly object-oriented interface to string manipulation, the better to work with the rest of our code; - encapsulate NLS and DBCS support as much as possible; - ensure that apps get correct form of library support, particularly with possible interference from intrinsics; The current solution consists of two classes: NLS_STR, and ISTR. class NLS_STR: use wherever NLS/DBCS support is needed. Most strings (in the UI, anyway) should be of this class. class ISTR: Indexes an NLS_STR in a DBCS safe manner. All positioning within an NLS_STR is done with ISTRs The class hierarchy looks like: BASE NLS_STR RESOURCE_STR ISTR This file also contains the STACK_NLS_STR macro and the strcpy( CHAR *, const NLS_STR& ) prototype. FILE HISTORY: beng 10/21/90 Created from email memo of last week johnl 11/13/90 Removed references to EB_STRING johnl 11/28/90 Release fully functional version johnl 12/07/90 Numerous revisions after code review (Removed SZ_STR, ISTR must associate w/a string upon decl etc.) beng 02/05/91 Replaced PCH with CHAR * for const-placement beng 04/26/91 Expunged of CB, IB types; relocated inline functions to string/strmisc.cxx beng 07/23/91 Added more *_STR types gregj 03/22/93 Ported to Chicago environment. gregj 03/25/93 Added Party(), DonePartying() gregj 03/30/93 Allow assigning NLS_STR to ISTR gregj 04/02/93 Added NLS_STR::IsDBCSLeadByte() gregj 04/02/93 Added ISTR::operator int gregj 04/08/93 Added NLS_STR::strncpy() gregj 04/08/93 Added NLS_STR::GetPrivateProfileString() */ #define WIN31 /* for certain string and NETLIB stuff */ #ifndef _BASE_HXX_ #include "base.h" #endif #ifndef _STRING_HXX_ #define _STRING_HXX_ extern HINSTANCE hInstance; // for NLS_STR::LoadString // String class doesn't allocate or deallocate memory // for STR_OWNERALLOC strings // #define STR_OWNERALLOC 0x8000 // Same as owner alloc only the string is initialized with the null string. // #define STR_OWNERALLOC_CLEAR 0x8001 // Maximum resource string size, owner alloced strings must be at least // MAX_RES_STR_LEN, otherwise an error will occur. // #define MAX_RES_STR_LEN 255 // The maximum number of insert parameters the InsertParams method can // handle // #define MAX_INSERT_PARAMS 9 /************************************************************************* NAME: ISTR SYNOPSIS: String index object, used in conjunction with NLS_STR INTERFACE: ISTR() - this ISTR gets associated with the passed string and can only be used on this string (NOTE: on non-debug versions this turns into a nop, can still be useful for decl. clarity however). ISTR() - Initialize to passed ISTR; this ISTR will be associated with the same string that the passed ISTR is associated with. operator=() - Copy passed ISTR (see prev.) operator=() - Associate ISTR with a new NLS_STR. operator-() - Returns CB diff. between *this & Param. (must both belong to the same string) operator++() - Advance the ISTR to the next logical character (use only where absolutely necessary). Stops at end of string operator+=() - Advance the ISTR to the ith logical character (call operator++ i times) Stops at end of string. operator==() - Returns TRUE if the two ISTRs point to the same position in the string (causes an assertion failure if the two ISTRs don't point to the same string). operator>() - Returns true of *this is greater then the passed ISTR (i.e., further along in the string). operator<() - Same as operator>, only less then. Reset() - Resets ISTR to beginning of string and updates the ISTR version number with the string's current version number private: QueryIB() - Returns the index in bytes QueryPNLS() - Returns the pointer to the NLS_STR this ISTR references SetPNLS() - Sets the pointer to point to the NLS_STR this ISTR references DEBUG ONLY: QueryVersion() - Gets the version number of the string this ISTR is associated with SetVersion() - Sets the version number of this ISTR. USES: CAVEATS: Each NLS_STR has a version number associated with it. When an operation is performed that modifies the string, the version number is updated. It is invalid to use an ISTR after its associated NLS_STR has been modified (can use Reset to resync it with the NLS_STR, the index gets reset to zero). You must associate an NLS_STR with an ISTR at the declaration of the ISTR. NOTES: The version checking and string association checking goes away in the non-debug version. HISTORY: johnl 11/16/90 Created johnl 12/07/90 Modified after code review gregj 03/30/93 Allow assigning NLS_STR to ISTR **************************************************************************/ class ISTR { friend class NLS_STR; public: ISTR( const ISTR& istr ); ISTR( const NLS_STR& nls ); ISTR& operator=( const ISTR& istr ); ISTR& operator=( const NLS_STR& nls ); INT operator-( const ISTR& istr ) const; ISTR& operator++(); VOID operator+=( INT iChars ); BOOL operator==( const ISTR& istr ) const; BOOL operator>( const ISTR& istr ) const; BOOL operator<( const ISTR& istr ) const; operator INT() const { return QueryIB(); } VOID Reset(); private: INT _ibString; // Index (in bytes) into an NLS_STR NLS_STR *_pnls; // Pointer to "owner" NLS INT QueryIB() const { return _ibString; } VOID SetIB( INT ib ) { _ibString = ib; } const NLS_STR* QueryPNLS() const { return _pnls; } VOID SetPNLS( const NLS_STR * pnls ) { _pnls = (NLS_STR*)pnls; } #ifdef DEBUG // Version number of NLS_STR this ISTR is associated with // USHORT _usVersion; USHORT QueryVersion() const { return _usVersion; } VOID SetVersion( USHORT usVers ) { _usVersion = usVers; } #endif }; /************************************************************************* NAME: NLS_STR (nls) SYNOPSIS: Provide a better string abstraction than the standard ASCIIZ representation offered by C (and C++). The abstraction is better mainly because it handles double-byte characters (DBCS) in the string and makes intelligent use of operator overloading. INTERFACE: NLS_STR() Construct a NLS_STR (initialized to a CHAR *, NLS_STR or NULL). Reports errors via BASE. ~NLS_STR() Destructor operator=() Assign one NLS_STR (or CHAR *) value to another (old string is deleted, new string is allocated and copies source) operator+=() Concatenate with assignment (equivalent to strcat - see strcat). operator==() Compare two NLS_STRs for equality operator!=() Compare two NLS_STRs for inequality QueryPch() Access operator, returning a "char *" aliased to the string. DO NOT MODIFY THE STRING USING THIS METHOD (or pass it to procedures that might modify it). Synonym: operator const CHAR *(). operator[]() Same as QueryPch, except the string is offset by ISTR characters IsDBCSLeadByte() Returns whether a byte is a lead byte, according to the ANSI- or OEM-ness of the string. C-runtime-style methods. strlen() Return the length of the string in bytes, less terminator. Provided only for crt compatibility; please use a Query method if possible. strcat() Append an NLS_STR. Will cause *this to be reallocated if the appended string is larger then this->QueryCb() and this is not an STR_OWNERALLOC string strncpy() Copy a non-null-terminated string into an NLS_STR. DBCS-safe. For similar functionality with an NLS_STR as the source, use the sub- string members. strcmp() Compare two NLS_STRs stricmp() " strncmp() Compare a portion of two NLS_STRs strnicmp() " strcspn() Find first char in *this that is a char in arg strspn() Find first char in *this that is not a char in arg strtok() Returns a token from the string strstr() Search for a NLS_STR. strchr() Search for a CHAR from beginning. Returns offset. strrchr() Search for a CHAR from end. strupr() Convert NLS_STR to upper case. atoi() Returns integer numeric value atol() Returns long value realloc() Resize string preserving its contents Other methods. QueryAllocSize() Returns total # of bytes allocated (i.e., number new was called with, or size of memory block if STR_OWNERALLOC IsOwnerAlloc() Returns TRUE if this string is an owner allocated string QuerySubStr() Return a substring InsertStr() Insert a NLS_STR at given index. DelSubStr() Delete a substring ReplSubStr() Replace a substring (given start and NLS_STR) InsertParams() Replace %1-%9 params in *this with the corresponding NLS_STRs contained in the array of pointers LoadString() Load the string associated with the passed resource into *this (OWNER_ALLOC strings must be at least MAX_RES_STR_LEN). Optionally calls InsertParams with a passed array of nls pointers. GetPrivateProfileString() Loads a string from an INI file. Reset() After an operation fails (due to memory failure), it is invalid to use the string until Reset has been called. If the object wasn't successfully constructed, Reset will fail. QueryTextLength() Returns the number of CHARS, less terminator. QueryTextSize() Returns the number of bytes, including terminator. Denotes amount of storage needed to dup string into a bytevector. QueryNumChar() Returns total number of logical characters within the string. Rarely needed. Append() Appends a string to the current string, like strcat. AppendChar() Appends a single character. Compare() As strcmp(). CopyFrom() As operator=(), but returns an APIERR. ToOEM() Convert string to OEM character set. ToAnsi() Convert string to ANSI character set. Party() Obtain read-write access to the buffer. DonePartying() Release read-write access. PARENT: BASE USES: ISTR CAVEATS: A NLS_STR object can enter an error state for various reasons - typically a memory allocation failure. Using an object in such a state is theoretically an error. Since string operations frequently occur in groups, we define operations on an erroneous string as no-op, so that the check for an error may be postponed until the end of the complex operation. Most member functions which calculate a value will treat an erroneous string as having zero length; however, clients should not depend on this. Attempting to use an ISTR that is registered with another string will cause an assertion failure. Each NLS_STR has a version/modification flag that is also stored in the the ISTR. An attempt to use an ISTR on an NLS_STR that has been modified (by calling one of the methods listed below) will cause an assertion failure. To use the ISTR after a modifying method, you must first call ISTR::Reset which will update the version in the ISTR. See the method definition for more detail. List of modifying methods: All NLS::operator= methods NLS::DelSubStr NLS::ReplSubStr NLS::InsertSubStr NOTE: The ISTR used as a starting index on the Substring methods remains valid after the call. Party() and DonePartying() can be used when you need to do something that the standard NLS_STR methods don't cover. For example, you might want to tweak the first couple of characters in a pathname; if you know they're definitely not double-byte characters, this is safe to do with ordinary character assignments. Calling Party() returns a pointer to the string buffer, and places the NLS_STR in an error state to prevent the standard methods from operating on it (and thereby getting confused by the possibly incorrect cached length). You can still call QueryAllocSize() to find out the maximum size of the buffer. When you've finished, call DonePartying() to switch the NLS_STR back to "normal" mode. There are two overloaded forms of DonePartying(). If you know what the length of the string is, you can pass it in, and NLS_STR will just use that. Otherwise, it will use strlenf() to find out what the new length is. If you don't plan to change the length, call strlen() on the string before you Party(), save that length, and pass it to DonePartying(). The initial strlen() is fast because it's cached. If you find yourself constantly Party()ing in order to accomplish a particular function, that function should be formally added to the NLS_STR definition. NOTES: The lack of a strlwr() method comes from a shortcoming in the casemap tables. Sorry. STR_OWNERALLOC strings are a special type of NLS_STR You mark a string as STR_OWNERALLOC on construction by passing the flag STR_OWNERALLOC and a pointer to your memory space where you want the string to reside plus the size of the memory block. THE POINTER MUST POINT TO A VALID NULL TERMINATED STRING. You are guaranteed this pointer will never be resized or deleted. Note that no checking is performed for writing beyond the end of the string. Valid uses include static strings, severe optimization, owner controlled memory allocation or stack controlled memory allocation. CODEWORK: Owner-alloc strings should be a distinct class from these normal strings. CODEWORK: Should add a fReadOnly flag. CODEWORK: Should clean up this mess, and make the owner-alloc constructor protected. I wish I could clean up this mess... HISTORY: johnl 11/28/90 First fully functioning version johnl 12/07/90 Incorporated code review changes terryk 04/05/91 add QueryNumChar method beng 07/22/91 Added more methods; separated fOwnerAlloc from cbData gregj 05/22/92 Added ToOEM, ToAnsi methods gregj 03/22/93 Ported to Chicago environment gregj 04/02/93 Added IsDBCSLeadByte() gregj 04/08/93 Added strncpy() **************************************************************************/ class NLS_STR : public BASE { friend class ISTR; // Allow access to CheckIstr public: // Default constructor, creating an empty string. // NLS_STR(); // Initialize to "cchInitLen" characters, each "chInit", // plus trailing NUL. // NLS_STR( INT cchInitLen ); // Initialize from a NUL-terminated character vector. // NLS_STR( const CHAR *pchInit ); // Initialize an NLS_STR to memory position passed in achInit // No memory allocation of any type will be performed on this string // cbSize should be the total memory size of the buffer, if cbSize == -1 // then the size of the buffer will assumed to be strlen(achInit)+1 // NLS_STR( unsigned stralloc, CHAR *pchInit, INT cbSize = -1 ); // Initialize from an existing x_STRING. // NLS_STR( const NLS_STR& nlsInit ); ~NLS_STR(); // Number of bytes the string uses (not including terminator) // Cf. QueryTextLength and QueryTextSize. // inline INT strlen() const; // Return a read-only CHAR vector, for the APIs. // const CHAR *QueryPch() const #ifdef DEBUG ; #else { return _pchData; } #endif const CHAR *QueryPch( const ISTR& istr ) const #ifdef DEBUG ; #else { return _pchData + istr.QueryIB(); } #endif WCHAR QueryChar( const ISTR& istr ) const #ifdef DEBUG ; #else { return *(_pchData+istr.QueryIB()); } #endif operator const CHAR *() const { return QueryPch(); } const CHAR *operator[]( const ISTR& istr ) const { return QueryPch(istr); } BOOL IsDBCSLeadByte( CHAR ch ) const; // Total allocated storage // inline INT QueryAllocSize() const; inline BOOL IsOwnerAlloc() const; // Increase the size of a string preserving its contents. // Returns TRUE if successful, false otherwise (illegal to // call for an owner alloced string). If you ask for a string smaller // then the currently allocated one, the request will be ignored and TRUE // will be returned. // BOOL realloc( INT cbNew ); // Returns TRUE if error was successfully cleared (string is now in valid // state), FALSE otherwise. // BOOL Reset(); NLS_STR& operator=( const NLS_STR& nlsSource ); NLS_STR& operator=( const CHAR *achSource ); NLS_STR& operator+=( WCHAR wch ); // NEW, replaces AppendChar NLS_STR& operator+=( const NLS_STR& nls ) { return strcat(nls); } NLS_STR& operator+=( LPCSTR psz ) { return strcat(psz); } NLS_STR& strncpy( const CHAR *pchSource, UINT cbSource ); NLS_STR& strcat( const NLS_STR& nls ); NLS_STR& strcat( LPCSTR psz ); BOOL operator== ( const NLS_STR& nls ) const; BOOL operator!= ( const NLS_STR& nls ) const; INT strcmp( const NLS_STR& nls ) const; INT strcmp( const NLS_STR& nls, const ISTR& istrThis ) const; INT strcmp( const NLS_STR& nls, const ISTR& istrThis, const ISTR& istrStart2 ) const; INT stricmp( const NLS_STR& nls ) const; INT stricmp( const NLS_STR& nls, const ISTR& istrThis ) const; INT stricmp( const NLS_STR& nls, const ISTR& istrThis, const ISTR& istrStart2 ) const; INT strncmp( const NLS_STR& nls, const ISTR& istrLen ) const; INT strncmp( const NLS_STR& nls, const ISTR& istrLen, const ISTR& istrThis ) const; INT strncmp( const NLS_STR& nls, const ISTR& istrLen, const ISTR& istrThis, const ISTR& istrStart2 ) const; INT strnicmp( const NLS_STR& nls, const ISTR& istrLen ) const; INT strnicmp( const NLS_STR& nls, const ISTR& istrLen, const ISTR& istrThis ) const; INT strnicmp( const NLS_STR& nls, const ISTR& istrLen, const ISTR& istrThis, const ISTR& istrStart2 ) const; // The following str* functions return TRUE if successful (istrPos has // meaningful data), false otherwise. // BOOL strcspn( ISTR *istrPos, const NLS_STR& nls ) const; BOOL strcspn( ISTR *istrPos, const NLS_STR& nls, const ISTR& istrStart ) const; BOOL strspn( ISTR *istrPos, const NLS_STR& nls ) const; BOOL strspn( ISTR *istrPos, const NLS_STR& nls, const ISTR& istrStart ) const; BOOL strstr( ISTR *istrPos, const NLS_STR& nls ) const; BOOL strstr( ISTR *istrPos, const NLS_STR& nls, const ISTR& istrStart ) const; BOOL stristr( ISTR *istrPos, const NLS_STR& nls ) const; BOOL stristr( ISTR *istrPos, const NLS_STR& nls, const ISTR& istrStart ) const; BOOL strchr( ISTR *istrPos, const CHAR ch ) const; BOOL strchr( ISTR *istrPos, const CHAR ch, const ISTR& istrStart ) const; BOOL strrchr( ISTR *istrPos, const CHAR ch ) const; BOOL strrchr( ISTR *istrPos, const CHAR ch, const ISTR& istrStart ) const; BOOL strtok( ISTR *istrPos, const NLS_STR& nlsBreak, BOOL fFirst = FALSE ); LONG atol() const; LONG atol( const ISTR& istrStart ) const; INT atoi() const; INT atoi( const ISTR& istrStart ) const; NLS_STR& strupr(); // Return a pointer to a new NLS_STR that contains the contents // of *this from istrStart to: // End of string if no istrEnd is passed // istrStart + istrEnd // NLS_STR *QuerySubStr( const ISTR& istrStart ) const; NLS_STR *QuerySubStr( const ISTR& istrStart, const ISTR& istrEnd ) const; // Collapse the string by removing the characters from istrStart to: // End of string // istrStart + istrEnd // The string is not reallocated // VOID DelSubStr( ISTR& istrStart ); VOID DelSubStr( ISTR& istrStart, const ISTR& istrEnd ); BOOL InsertStr( const NLS_STR& nlsIns, ISTR& istrStart ); // Replace till End of string of either *this or replacement string // (or istrEnd in the 2nd form) starting at istrStart // VOID ReplSubStr( const NLS_STR& nlsRepl, ISTR& istrStart ); VOID ReplSubStr( const NLS_STR& nlsRepl, ISTR& istrStart, const ISTR& istrEnd ); // Replace %1-%9 in *this with corresponding index from apnlsParamStrings // Ex. if *this="Error %1" and apnlsParamStrings[0]="Foo" the resultant // string would be "Error Foo" // USHORT InsertParams( const NLS_STR *apnlsParamStrings[] ); // Load a message from a resource file into *this (if string is an // OWNER_ALLOC string, then must be at least MAX_RES_STR_LEN. Heap // NLS_STRs will be reallocated if necessary // USHORT LoadString( USHORT usMsgID ); // Combines functionality of InsertParams & LoadString. *this gets loaded // with the string from the resource file corresponding to usMsgID. // USHORT LoadString( USHORT usMsgID, const NLS_STR *apnlsParamStrings[] ); VOID GetPrivateProfileString( const CHAR *pszFile, const CHAR *pszSection, const CHAR *pszKey, const CHAR *pszDefault = NULL ); VOID ToOEM(); // convert ANSI to OEM VOID ToAnsi(); // convert OEM to ANSI VOID SetOEM(); // declare that string was constructed as OEM VOID SetAnsi(); // declare that string was constructed as ANSI inline BOOL IsOEM() const; CHAR *Party(); // get read-write access VOID DonePartying( VOID ); // if you don't have the length handy VOID DonePartying( INT cchNew ); // if you do #ifdef EXTENDED_STRINGS // Initialize from a NUL-terminated character vector // and allocate a minimum of: cbTotalLen+1 bytes or strlen(achInit)+1 // NLS_STR( const CHAR *pchInit, INT iTotalLen ); // Similar to prev. except the string pointed at by pchInit is copied // to pchBuff. The address of pchBuff is used as the string storage. // cbSize is required. stralloc can only be STR_OWNERALLOC (it makes // no sense to use STR_OWNERALLOC_CLEAR). // NLS_STR( unsigned stralloc, CHAR *pchBuff, INT cbSize, const CHAR *pchInit ); // return the number of logical characters within the string // INT QueryNumChar() const; // Return the number of printing CHARs in the string. // This number does not include the termination character. // // Cf. QueryNumChar, which returns a count of glyphs. // INT QueryTextLength() const; // Return the number of BYTES occupied by the string's representation. // Cf. QueryAllocSize, which returns the total amount alloc'd. // INT QueryTextSize() const; APIERR Append( const NLS_STR& nls ); APIERR AppendChar( WCHAR wch ); APIERR CopyFrom( const NLS_STR& nlsSource ); APIERR CopyFrom( const CHAR *achSource ); INT Compare( const NLS_STR *nls ) const { return strcmp(*nls); } #endif private: UINT _fsFlags; // owner-alloc, character set flags #define SF_OWNERALLOC 0x1 #define SF_OEM 0x2 INT _cchLen; // Number of bytes string uses (strlen) INT _cbData; // Total storage allocated CHAR *_pchData; // Pointer to Storage #ifdef DEBUG USHORT _usVersion; // Version count (inc. after each change) #endif // The following substring functions are used internally (can't be // exposed since they take an INT cbLen parameter for an index). // VOID DelSubStr( ISTR&istrStart, INT cbLen ); NLS_STR *QuerySubStr( const ISTR& istrStart, INT cbLen ) const; VOID ReplSubStr( const NLS_STR& nlsRepl, ISTR& istrStart, INT cbLen ); BOOL Alloc( INT cchLen ); // Allocate memory for a string #ifdef DEBUG // DEBUG is new for these // CheckIstr checks whether istr is associated with this, asserts out // if it is not. Also checks version numbers in debug version. // VOID CheckIstr( const ISTR& istr ) const; // UpdateIstr syncs the version number between *this and the passed // ISTR. This is for operations that cause an update to the string // but the ISTR that was passed in is still valid (see InsertSubSt). // VOID UpdateIstr( ISTR *pistr ) const; // IncVers adds one to this strings version number because the previous // operation caused the contents to change thus possibly rendering // ISTRs on this string as invalid. // VOID IncVers(); // InitializeVers sets the version number to 0 // VOID InitializeVers(); // QueryVersion returns the current version number of this string // USHORT QueryVersion() const; #else // DEBUG VOID CheckIstr( const ISTR& istr ) const { } VOID UpdateIstr( ISTR *pistr ) const { } VOID IncVers() { } VOID InitializeVers() { } USHORT QueryVersion() const { return 0; } #endif }; /***********************************************************************/ /*********************************************************************** * * Macro STACK_NLS_STR(name, len ) * * Define an NLS string on the stack with the name of "name" and the * length of "len". The strlen will be 0 and the first character will * be '\0'. One byte is added for the NULL terminator. Usage: * STACK_NLS_STR( UncPath, UNCLEN ); * * Macro ISTACK_NLS_STR(name, len, pchInitString ) * * Same as STACK_NLS_STR except ISTACK_NLS_STR takes an initializer. **********************************************************************/ #define STACK_NLS_STR( name, len ) \ CHAR _tmp##name[ len+1 ] ; \ *_tmp##name = '\0' ; \ NLS_STR name( STR_OWNERALLOC, _tmp##name, len+1 ); #define ISTACK_NLS_STR( name, len, pchInitString ) \ STACK_NLS_STR( name, len ) ; \ name = pchInitString; /***********************************************************************/ BOOL NLS_STR::IsOwnerAlloc() const { return _fsFlags & SF_OWNERALLOC; } BOOL NLS_STR::IsOEM() const { return _fsFlags & SF_OEM; } INT NLS_STR::strlen() const { return _cchLen; } INT NLS_STR::QueryAllocSize() const { return _cbData; } #endif // _STRING_HXX_