123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624 |
- ////////////////////////////////////////////////////////////////////////////////
- //
- // Copyright 2016 RWS Inc, All Rights Reserved
- //
- // This program is free software; you can redistribute it and/or modify
- // it under the terms of version 2 of the GNU General Public License as published by
- // the Free Software Foundation
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License along
- // with this program; if not, write to the Free Software Foundation, Inc.,
- // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- //
- ////////////////////////////////////////////////////////////////////////////////
- //
- // string.cpp
- // Project: Nostril (aka Postal)
- //
- // History:
- // 01/17/97 MJR Started.
- // 01/18/97 MJR First (tentative) release.
- // 01/20/97 MJR Revised lots of stuff, ready for testing/prerelease.
- // 01/21/97 MJR Did a bunch of testing, fixed one minor typo-bug.
- // 01/28/97 MJR Fixed ASSERT(1) to be ASSERT(0), as it should have been.
- // Added char* operator.
- // Added Update() function.
- // 02/10/97 MJR Minor tweak to Save() -- got rid of (strange) unecessary
- // cast. Also got rid of one constructor varation which had
- // allowed you to create an RString based on the value of a
- // char. The problem was that this caused an "abiguous"
- // overload when you were trying to create an RString of
- // a specified length, as in RString str(5). Hopefully the
- // removed constructor will not be missed.
- //
- // 06/28/97 MJR Fixed typo in mac-specific code that had never been
- // compiled before (naturally).
- //
- // 07/12/97 MJR RString::Load() didn't initialize sResult, so it would
- // return random error codes instead of 0 for success.
- //
- ////////////////////////////////////////////////////////////////////////////////
- //
- // The primary purpose of RString is to make it easier to work with text strings
- // by hiding the memory and storage aspects from the user. It lets the user
- // focus on the real task without getting caught up in the nasty details.
- //
- //
- // Most RString functions and operators are overloaded to accept any of the
- // following types. These are often referred to as the "supported types".
- //
- // * Other RString's
- //
- // * C-style strings (char*)
- //
- // * Characters (char), which are treated as ASCII codes
- //
- // * Integers (short, unsigned short, long, and unsigned long) converted
- // into string representations of their numerical values. The exception
- // is that there are no constructors to take these types, only because
- // it caused a conflict with other, more usefull constructors.
- //
- //
- // This is an overview of the available functions. Check the comments on
- // the individual functions for details.
- //
- // RString() Various constructors that allow the creation of a string,
- // optionally based on any supported type, and optionally
- // with a specific buffer size
- //
- // ~RString() Destructor
- //
- // GetSize() Get size of buffer (always larger than string length)
- //
- // Grow() Grow buffer to at least the specified size
- //
- // Shrink() Shrink buffer to at most the specified size
- //
- // Compact() Compact buffer to minimum size required by current string
- //
- // Clear() Clear string (length will be 0)
- //
- // GetLen() Get string length
- //
- // GetAt() Get character at specified position in string
- //
- // operator[] Get character at specified position in string
- //
- // SetAt() Set character at specified position in string
- //
- // operator char* Casts RString to C-style string
- //
- // Update() Updates string if it was modified via the char* pointer
- //
- // Format() Create string using sprintf-like method
- //
- // Left() Returns new RString based on a portion of this string
- //
- // Right() Returns new RString based on a portion of this string
- //
- // Mid() Returns new RString based on a portion of this string
- //
- // Range() Returns new RString based on a portion of this string
- //
- // Insert() Insert any supported type into string
- //
- // Delete() Delete portion of string
- //
- // ToUpper() Convert string to upper case
- //
- // ToLower() Convert string to lower case
- //
- // Load() Load previously saved string
- //
- // Save() Save string
- //
- // operator= Assigns any supported type into string
- //
- // operator+= Appends supported types onto current string
- //
- // operator+ Concatinates string and any supported type
- //
- // operator== Full gamut of comparison operators
- // operator!=
- // operator<=
- // operator>=
- // operator<
- // operator>
- //
- //
- // Functions to be added some time soon...
- //
- // I'm thinking Insert() could be easily extended to make it much more
- // usefull -- see the Insert() comment block (in the header file!) for
- // details.
- //
- // Along the same lines as Insert(), I think a Copy() would be usefull.
- // It would be just like Insert() except it would overwrite the data in
- // the destination string.
- //
- // Together, just about everything else could be implimented based on
- // Insert() and Copy()! I'm not sure it's worth switching over to that,
- // though. Too bad I didn't create them first.
- //
- //
- // More Information
- //
- // An RString object will automatically increase the size of its memory buffer
- // as necessary to accomodate the growth of the text string it holds. It will
- // never reduce the buffer's size automatically, although the user can trigger
- // such a reduction at any time by calling Compact().
- //
- // The user can also explicitly set the size of the buffer, although it will
- // still be grown automatically if necessary. The idea is to allow the user to
- // make the buffer large enough so that additional growth isn't necessary,
- // thereby improving performance and reducing memory fragmentation.
- //
- // When working with RStrings, knowing the length of a string becomes much less
- // important than it is with C-style strings. In light of this, all RString
- // functions that take a length as a parameter will automatically clip that
- // length to the actual size of the string. For instance, if the first 8
- // characters of a string are requested but the string is only 5 characters
- // long, then the requested length is clipped, and only 5 characters are
- // returned.
- //
- // In constract, NO clipping is performed at the beginning of a string. The
- // first character of a string is always at position 0. If a negative position
- // is passed to any RString function, it will do nothing or return an empty
- // RString, whichever is appropriate for that function. Negative positions
- // are basically considered meaningless, but are handled without any complaints
- // (no TRACE's or ASSERT's), the idea being to leave it up to the user to
- // decide whether to validate positions beforehand or to check results
- // afterwards.
- //
- // Put another way, functions will generally clip lengths to fit
- // When an RString function takes a length as a parameter, that length will
- // be truncated if necessary to avoid going past the end of the string. When
- // a function takes a position as a parameter, the function won't do anything
- // if that position is negative.
- //
- // As a general rule, clipping is performed on the right of strings but not
- // on the left end. The reason for these two different approaches is actually
- // pretty straightforward.
- //
- ////////////////////////////////////////////////////////////////////////////////
- #define RSTRING_CPP
- #include <ctype.h>
- #include "rstring.h"
- // Static pointer that is initialized to point at an empty string. Used
- // in cases where string is being cast as char* but doesn't have a buffer
- // of it's own.
- char* RString::ms_pszEmpty = "";
- ////////////////////////////////////////////////////////////////////////////////
- // Grow the buffer to the specified size. If the buffer is already greater than
- // or equal to the specified size, then this will have no effect. Otherwise,
- // the buffer is grown to the specified size. The string is unaffected.
- ////////////////////////////////////////////////////////////////////////////////
- void RString::Grow(long lMinimumSize)
- {
- // New size must be greater than current size (which might be 0)
- // (This comparison also filters out negative sizes, which are stupid)
- if (lMinimumSize > m_lBufSize)
- {
- if (m_lBufSize > 0)
- {
- // Change buffer size
- char* pOld = m_pBuf;
- m_pBuf = (char*)malloc(lMinimumSize);
- ASSERT(m_pBuf != 0); // should be caught by new_handler, but just in case...
- memcpy(m_pBuf, pOld, m_lBufSize);
- free(pOld);
- }
- else
- {
- // Create new buffer
- m_pBuf = (char*)malloc(lMinimumSize);
- ASSERT(m_pBuf != 0); // should be caught by new_handler, but just in case...
- *m_pBuf = 0; // write terminating null (string length must be 0 at this point)
- }
- m_lBufSize = lMinimumSize;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Shrink the buffer to the specified size. If the buffer is already less than
- // or equal to the specified size, then this will have no effect. Otherwise,
- // the buffer is reduced to the specified size. If the specified size is 0,
- // the buffer, if any, will be freed. If the specified size is less than is
- // required to hold the current string (including the terminating null), then
- // the string is truncated to a length one less than the specified size, and a
- // new terminating null is written. Note that a size of 1 will result in an
- // empty string since there will only be room for the terminating null.
- ////////////////////////////////////////////////////////////////////////////////
- void RString::Shrink(long lMaximumSize)
- {
- // Guard against stupid values
- if (lMaximumSize >= 0)
- {
- // New size must be less than current size
- // (This comparison also ensures that m_lBufSize > 0, meaning there is a buffer)
- if (lMaximumSize < m_lBufSize)
- {
- if (lMaximumSize == 0)
- {
- FreeBuf();
- }
- else
- {
- // Change buffer size
- char* pOld = m_pBuf;
- m_pBuf = (char*)malloc(lMaximumSize);
- ASSERT(m_pBuf != 0); // should be caught by new_handler, but just in case...
- memcpy(m_pBuf, pOld, m_lBufSize);
- free(pOld);
- m_lBufSize = lMaximumSize;
- // See if we truncated the string
- if (m_lBufSize <= m_lStrLen)
- {
- // Write new terminating null at end of buffer
- m_pBuf[m_lBufSize - 1] = 0;
- // Set new string length
- m_lStrLen = m_lBufSize - 1;
- }
- }
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Compact the buffer to the minimum size required to hold the current string.
- // If the string is empty, the buffer is freed.
- ////////////////////////////////////////////////////////////////////////////////
- void RString::Compact(void)
- {
- // If there's a string, shrink buffer to fit it, otherwise get rid of it
- if (m_lStrLen > 0)
- Shrink(m_lStrLen + 1); // shrink buffer (if necessary) to 1 larger than string
- else
- FreeBuf();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Format string using sprintf-like method. The specified size is used to make
- // sure the buffer is large enough to hold the generated string (the specified
- // size is passed to the Grow() function -- see it for more details).
- //
- // WARNING: The ANSI standard vsprintf function is used to format the string,
- // which is good because the results will be consistant with the rest of the
- // printf family, but is also very bad because there is no way to limit the
- // number of characters that are actually written to the buffer! Therefore,
- // this function is fully reliant on the user to ensure the buffer is large
- // enough to hold the resulting string. This represents a gaping hole in
- // RString's functionality.
- //
- // Note #1: At some point, I would like to modify the source to vsprintf so it
- // works directly with RString and takes advantage of its dynamic sizing. If
- // that were done, the size parameter could be removed from this function, and
- // other internal RString code that uses sprintf could be simplified as well.
- //
- // Note #2: Microsoft DOES supply a non-ANSI variation called _vsnprintf() that
- // takes a "maximum output size" parameter. For now, we use this variation
- // whenever we're compiled under Microsoft.
- //
- // Returns number of characters written, or -1 if an error occurred (this is
- // basically the value returned by vsprintf.)
- ////////////////////////////////////////////////////////////////////////////////
- long RString::Format(long lMinimumSize, char* format, ...)
- {
- long lWritten = 0;
- if (lMinimumSize >= 0)
- {
- // Grow buffer
- Grow(lMinimumSize);
- // Since minimum size might be 0, we have to make sure buffer exists
- if (m_lBufSize > 0)
- {
- va_list varp;
- va_start(varp, format);
- #ifdef _MSC_VER
- lWritten = _vsnprintf(m_pBuf, m_lBufSize - 1, format, varp);
- #else
- lWritten = vsprintf(m_pBuf, format, varp);
- #ifdef _DEBUG
- if ((lWritten >= 0) && ((lWritten + 1) > m_lBufSize))
- {
- TRACE("RString::Format(): String buffer was overwritten! String will be cleared!\n");
- Clear();
- ASSERT(0); // memory has been corrupted!
- }
- #endif
- #endif
- va_end(varp);
- if (lWritten >= 0)
- {
- m_lStrLen = lWritten;
- }
- else
- {
- TRACE("RString::Format(): Error returned by vsprintf()! String will be cleared!\n");
- Clear();
- }
- }
- }
- return lWritten;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Create a new RString based on this string's first 'lLen' characters. If
- // lLen is negative, the returned string will be empty. Otherwise, the returned
- // string's length will be lLen or this string's length, whichever is less.
- ////////////////////////////////////////////////////////////////////////////////
- RString RString::Left(long lLen) const
- {
- RString str;
- if ((lLen > 0) && (m_lStrLen > 0))
- {
- if (lLen > m_lStrLen)
- lLen = m_lStrLen;
- str.Grow(lLen + 1); // size is always > 0, so this will always return with a valid buffer
- memcpy(str.m_pBuf, m_pBuf, lLen);
- str.m_pBuf[lLen] = 0;
- str.m_lStrLen = lLen;
- }
- return str;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Create a new RString based on this string's last 'lLen' characters. If lLen
- // is negative, the returned string will be empty. Otherwise, the returned
- // string's length will be lLen or this string's length, whichever is less.
- ////////////////////////////////////////////////////////////////////////////////
- RString RString::Right(long lLen) const
- {
- RString str;
- if ((lLen > 0) && (m_lStrLen > 0))
- {
- if (lLen > m_lStrLen)
- lLen = m_lStrLen;
- str.Grow(lLen + 1); // size is always > 0, so this will always return with a valid buffer
- memcpy(str.m_pBuf, m_pBuf + (m_lStrLen - lLen), lLen);
- str.m_pBuf[lLen] = 0;
- str.m_lStrLen = lLen;
- }
- return str;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Create a new RString based on a portion of this string, starting at lPos and
- // proceeding for lLen characters. If lPos is negative or beyond the end of
- // this string, or if lLen is negative, the returned string will be empty. The
- // specified lLen will be clipped if necessary to avoid going past the end
- // of this string.
- ////////////////////////////////////////////////////////////////////////////////
- RString RString::Mid(long lPos, long lLen) const
- {
- RString str;
- if ((lPos >= 0) && (lPos < m_lStrLen) && (lLen > 0)) // implies m_lStrLen > 0
- {
- if ((lPos + lLen) > m_lStrLen)
- lLen = m_lStrLen - lPos;
- str.Grow(lLen + 1); // size is always > 0, so this will always return with a valid buffer
- memcpy(str.m_pBuf, m_pBuf + lPos, lLen);
- str.m_pBuf[lLen] = 0;
- str.m_lStrLen = lLen;
- }
- return str;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Create a new RString based on the specified range of characters from this
- // string. If either position is negative the returned string will be empty.
- // The positions are clipped if they are past the end of this string. lPos1
- // must be less than or equal to lPos2.
- ////////////////////////////////////////////////////////////////////////////////
- RString RString::Range(long lPos1, long lPos2) const
- {
- RString str;
- if ((lPos1 >= 0) && (lPos1 <= lPos2) && (lPos1 < m_lStrLen)) // implies m_lStrLen > 0
- {
- if (lPos2 >= m_lStrLen)
- lPos2 = m_lStrLen - 1;
- long lLen = (lPos2 - lPos1) + 1;
- str.Grow(lLen + 1); // size is always > 0, so this will always return with a valid buffer
- memcpy(str.m_pBuf, m_pBuf + lPos1, lLen);
- str.m_pBuf[lLen] = 0;
- str.m_lStrLen = lLen;
- }
- return str;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Insert any of the supported types into the string at the specified position.
- // The current string contents, from the character at the specified position up
- // to the last character, inclusive, are moved forward to make room for the new
- // characters. The position is handled slightly differently by this function
- // in that it is valid for it to equal the string length, in which case the new
- // characters are basically appended onto the end of the string (in most other
- // functions, the position must be less than the string length). If the
- // position is negative or greater than the string length, then this function
- // will do nothing.
- ////////////////////////////////////////////////////////////////////////////////
- void RString::Insert(long lPos, const RString& str)
- {
- if ((lPos >= 0) && (lPos <= m_lStrLen) && (str.m_lStrLen > 0))
- {
- Grow(m_lStrLen + str.m_lStrLen + 1);
- // Shift existing characters to the right (including terminating null!)
- memmove(m_pBuf + lPos + str.m_lStrLen, m_pBuf + lPos, (m_lStrLen + 1) - lPos);
- // Copy new characters
- memcpy(m_pBuf + lPos, str.m_pBuf, str.m_lStrLen);
- m_lStrLen += str.m_lStrLen;
- }
- }
- void RString::Insert(long lPos, const char* psz)
- {
- long lLen = strlen(psz);
- if ((lPos >= 0) && (lPos <= m_lStrLen) && (lLen > 0))
- {
- Grow(m_lStrLen + lLen + 1);
- // Shift existing characters to the right (including terminating null!)
- memmove(m_pBuf + lPos + lLen, m_pBuf + lPos, (m_lStrLen + 1) - lPos);
- // Copy new characters
- memcpy(m_pBuf + lPos, psz, lLen);
- m_lStrLen += lLen;
- }
- }
- void RString::Insert(long lPos, char c)
- {
- if ((lPos >= 0) && (lPos <= m_lStrLen))
- {
- Grow(m_lStrLen + 1 + 1);
- // Shift existing characters to the right (including terminating null!)
- memmove(m_pBuf + lPos + 1, m_pBuf + lPos, (m_lStrLen + 1) - lPos);
- // Copy new character
- m_pBuf[lPos] = c;
- m_lStrLen++;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Delete the specified number of characters starting at the specified position.
- // If the position or length are negative, the string is left unmodified.
- ////////////////////////////////////////////////////////////////////////////////
- void RString::Delete(long lPos, long lLen)
- {
- if ((lPos >= 0) && (lPos < m_lStrLen) && (lLen > 0)) // implies m_lStrLen > 0
- {
- if ((lPos + lLen) > m_lStrLen)
- lLen = m_lStrLen - lPos;
- // Shift existing characters to the left (including terminating null!)
- memmove(m_pBuf + lPos, m_pBuf + lPos + lLen, (m_lStrLen + 1) - (lPos + lLen));
- m_lStrLen -= lLen;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Convert string to upper case
- ////////////////////////////////////////////////////////////////////////////////
- void RString::ToUpper(void)
- {
- long lLen = m_lStrLen;
- char* p = m_pBuf;
- if (lLen > 0)
- {
- do {
- *p = toupper(*p);
- p++;
- } while (--lLen);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Convert string to lower case
- ////////////////////////////////////////////////////////////////////////////////
- void RString::ToLower(void)
- {
- long lLen = m_lStrLen;
- char* p = m_pBuf;
- if (lLen > 0)
- {
- do {
- *p = tolower(*p);
- p++;
- } while (--lLen);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Load a previously saved string from the specified RFile. Calls Clear() if
- // an error occurs while loading. Returns 0 if successfull, non-zero otherwise.
- ////////////////////////////////////////////////////////////////////////////////
- short RString::Load(RFile* pFile)
- {
- short sResult = 0;
- // Read length to separate var to avoid corrupting real one in case of read error
- long lLen;
- if (pFile->Read(&lLen) == 1)
- {
- // Check if there's more to read
- if (lLen > 0)
- {
- // Grow buffer
- Grow(lLen + 1);
- if (pFile->Read(m_pBuf, lLen) == lLen)
- {
- // Add null and set length
- m_pBuf[lLen] = 0;
- m_lStrLen = lLen;
- }
- else
- {
- Clear();
- TRACE("RString::Load(): Error reading string!\n");
- sResult = -1;
- }
- }
- else
- {
- Clear();
- }
- }
- else
- {
- Clear();
- TRACE("RString::Load(): Error reading string length!\n");
- sResult = -1;
- }
- return sResult;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Save this string to the specified RFile. This RString is not modified by the
- // save, even if an error occurs. Returns 0 if successfull, non-zero otherwise.
- ////////////////////////////////////////////////////////////////////////////////
- short RString::Save(RFile* pFile) const
- {
- // Save string length
- pFile->Write(m_lStrLen);
- // See if there's more to save
- if (m_lStrLen > 0)
- pFile->Write(m_pBuf, m_lStrLen);
- // Check for errors
- if (pFile->Error())
- {
- TRACE("RString::Save(): Error writing string!\n");
- return -1; // error
- }
- return 0; // success
- }
- ////////////////////////////////////////////////////////////////////////////////
- // EOF
- ////////////////////////////////////////////////////////////////////////////////
|