/*
* contain.cxx
*
* Container Classes
*
* Portable Windows Library
*
* Copyright (c) 1993-1998 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* $Log: contain.cxx,v $
* Revision 1.173 2005/11/30 12:47:41 csoutheren
* Removed tabs, reformatted some code, and changed tags for Doxygen
*
* Revision 1.172 2005/09/18 11:05:36 dominance
* include/ptlib/channel.h, include/ptlib/pstring.h, src/ptlib/common/contain.cxx,
* src/ptlib/common/pchannel.cxx:
* correct the STL defined checking to use proper syntax.
*
* include/ptlib/object.h:
* re-add typedef to compile on mingw
*
* make/ptlib-config.in:
* import a long-standing fix from the Debian packs which allows usage of
* ptlib-config without manually adding -lpt for each of the subsequent
* projects
*
* Revision 1.171 2005/08/08 07:01:58 rjongbloed
* Minor changes to remove possible ambiguity where virtual and non-virtual
* functions are overloaded.
* Removed commented out code.
*
* Revision 1.170 2005/05/02 13:10:24 csoutheren
* Fixed problem with PString::SetMinSize not always uniquing the string
*
* Revision 1.169 2005/05/02 09:02:35 csoutheren
* Fixed previous fix to contain.cxx which broke PString::MakeUnique
*
* Revision 1.168 2005/04/28 04:48:41 csoutheren
* Changed PContainer::SetSize to not unique a container when the size is unchanged
*
* Revision 1.167 2005/01/09 06:35:05 rjongbloed
* Fixed ability to make Clone() or MakeUnique() of a sorted list.
*
* Revision 1.166 2004/10/21 13:04:21 rjongbloed
* Fixed possibility of const operator[] on PStringArray returning a NULL reference. This
* function should return a non-lvalue PString anyway as it is const!
*
* Revision 1.165 2004/08/04 00:56:16 csoutheren
* Added protection against signed chars triggering asserts in VS.net in debug mode
* Thanks to Michal Zygmuntowicz
*
* Revision 1.164 2004/07/19 13:55:00 csoutheren
* Fixed typo flagged as warning by gcc 3.5-20040704
*
* Revision 1.163 2004/07/11 07:56:36 csoutheren
* Applied jumbo VxWorks patch, thanks to Eize Slange
*
* Revision 1.162 2004/06/08 01:31:07 csoutheren
* Make the test sense correct for the init(NULL)
*
* Revision 1.161 2004/06/08 01:29:00 csoutheren
* Removed memory leak on VS.net caused by unobvious iostream allocation
*
* Revision 1.160 2004/05/28 23:59:23 csoutheren
* Added guards for negative offsets and lengths in various PString functions
*
* Revision 1.159 2004/05/13 14:52:32 csoutheren
* Allow PString::IsEmpty to return TRUE when theArray is NULL
*
* Revision 1.158 2004/05/04 11:10:37 rjongbloed
* Fixed usage of MakeEmpty() with PStringStream.
*
* Revision 1.157 2004/04/24 06:27:56 rjongbloed
* Fixed GCC 3.4.0 warnings about PAssertNULL and improved recoverability on
* NULL pointer usage in various bits of code.
*
* Revision 1.156 2004/04/18 04:33:37 rjongbloed
* Changed all operators that return BOOL to return standard type bool. This is primarily
* for improved compatibility with std STL usage removing many warnings.
*
* Revision 1.155 2004/04/15 03:50:35 csoutheren
* Fixed problem with MakeUnique
*
* Revision 1.154 2004/04/14 23:34:52 csoutheren
* Added plugin for data access
*
* Revision 1.153 2004/04/12 00:36:05 csoutheren
* Added new class PAtomicInteger and added Windows implementation
*
* Revision 1.152 2004/04/11 06:15:36 csoutheren
* Modified to use Atomic_word if available
*
* Revision 1.151 2004/04/11 02:55:18 csoutheren
* Added PCriticalSection for Windows
* Added compile time option for PContainer to use critical sections to provide thread safety under some circumstances
*
* Revision 1.150 2004/04/09 06:38:11 rjongbloed
* Fixed compatibility with STL based streams, eg as used by VC++2003
*
* Revision 1.149 2004/04/03 23:53:09 csoutheren
* Added various changes to improce compatibility with the Sun Forte compiler
* Thanks to Brian Cameron
* Added detection of readdir_r version
*
* Revision 1.148 2004/04/03 08:22:21 csoutheren
* Remove pseudo-RTTI and replaced with real RTTI
*
* Revision 1.147 2004/04/03 06:54:25 rjongbloed
* Many and various changes to support new Visual C++ 2003
*
* Revision 1.146 2004/03/20 04:20:34 rjongbloed
* Fixed some VxWorks port issues especially underrrun memory access in
* the PString::FindLast function,, thanks Eize Slange
*
* Revision 1.145 2004/02/23 00:44:38 csoutheren
* A completely different, other regex include hack to avoid requiring
* the sources when using a header-file only environment
*
* Revision 1.144 2004/02/23 00:26:05 csoutheren
* Finally, a generic and elegant fix for the regex include hacks. Thanks to Roger Hardiman
*
* Revision 1.143 2004/02/15 03:04:52 rjongbloed
* Fixed problem with PSortedList nil variable and assignment between instances,
* pointed out by Ben Lear.
*
* Revision 1.142 2004/02/11 05:09:14 csoutheren
* Fixed problems with regex libraries on Solaris, and with host OS numbering
* being a quoted string rather than a number. Thanks to Chad Attermann
* Fixed problems SSL detection problems thanks to Michal Zygmuntowicz
*
* Revision 1.141 2004/02/08 11:13:20 rjongbloed
* Fixed crash in heavily loaded multi-threaded systems using simultaneous sorted
* lists, Thanks Federico Pinna, Fabrizio Ammollo and the gang at Reitek S.p.A.
*
* Revision 1.140 2004/01/17 17:28:25 csoutheren
* Changed PString::Empty to be inline in header file
*
* Revision 1.139 2004/01/16 13:24:38 csoutheren
* Changed PString::Empty to be thread-safe
* Fixed PContainer::SetMinSize and PAbstractArray::SetSize, thanks to 123@call2ua.com
* Fixed PString::FindLast, thanks to Andreas Sikkema
*
* Revision 1.138 2003/12/14 01:12:00 csoutheren
* Added return value to PRegularExpression::operator = again (Doh!)
*
* Revision 1.137 2003/12/13 23:08:46 csoutheren
* Changed PRegularExpression to allow a copy constructor and operator =
*
* Revision 1.136 2003/12/04 13:12:41 csoutheren
* Fixed error in PRegularExpression that caused double delete when incorrect regular expression used
*
* Revision 1.135 2003/09/17 09:02:13 csoutheren
* Removed memory leak detection code
*
* Revision 1.134 2003/07/28 18:44:01 dsandras
* Make use of the libc regex on Linux.
*
* Revision 1.133 2003/05/14 00:48:33 rjongbloed
* Added constructor to string lists/arrays etc that takes a single PString.
* Fixed bug in doing a MakeUnique on a container, it would lose the
* DisallowDeleteObjects flag.
*
* Revision 1.132 2003/04/28 09:14:14 robertj
* Fixed bad sign extension problem in PBYTEArray output
*
* Revision 1.131 2003/04/15 07:08:37 robertj
* Changed read and write from streams for base array classes so operates in
* the same way for both PIntArray and PArray<int> etc
*
* Revision 1.130 2003/03/31 01:24:23 robertj
* Added ReadFrom functions for standard container classes such as
* PIntArray and PStringList etc
*
* Revision 1.129 2003/03/05 08:48:32 robertj
* Added PStringArray::ToCharAray() function at suggestion of Ravelli Rossano
*
* Revision 1.128 2003/02/02 23:29:34 robertj
* Fixed bug in RightTrim() (lost off last non blank char), tnanks Joerg Schoemer
*
* Revision 1.127 2002/12/16 08:10:35 robertj
* Fixed infinite loop when converting an illegal (incomplete) UTF-8 string
* to UCS-2, thanks Chih-Wei Huang
*
* Revision 1.126 2002/11/26 01:08:33 robertj
* Fixed problem when using pre-initialised PStringStream greater than 255
* bytes, would truncate and lose trailing null. Reported by Thien Nguyen
*
* Revision 1.125 2002/11/12 09:18:03 robertj
* Added PString::NumCompare() as functional equivalent of strncmp().
* Added PSortedStringList::GetNextStringsIndex() to do searches of binary
* tree on partal strings.
*
* Revision 1.124 2002/11/01 05:10:01 robertj
* Fixed bug in UTF-8 to UCS-2 conversion, not compiler portable!
*
* Revision 1.123 2002/10/31 07:33:59 robertj
* Changed UTF-8 to UCS-2 conversion function to not include trailing null.
*
* Revision 1.122 2002/10/31 05:55:55 robertj
* Now comprehensively stated that a PString is ALWAYS an 8 bit string as
* there are far too many inheerent assumptions every to make it 16 bit.
* Added UTF-8/UCS-2 conversion functions to PString.
*
* Revision 1.121 2002/10/10 04:43:44 robertj
* VxWorks port, thanks Martijn Roest
*
* Revision 1.120 2002/08/14 00:43:40 robertj
* Added ability to have fixed maximum length PStringStream's so does not do
* unwanted malloc()'s while outputing data.
*
* Revision 1.119 2002/08/06 08:51:36 robertj
* Added missing va_end, thanks Klaus Kaempf
*
* Revision 1.118 2002/06/27 06:59:34 robertj
* Removed memory leak display of static that is not really a leak.
*
* Revision 1.117 2002/06/25 02:24:24 robertj
* Improved assertion system to allow C++ class name to be displayed if
* desired, especially relevant to container classes.
*
* Revision 1.116 2002/06/24 06:18:36 robertj
* Fixed bug when getting extra space at start of outputing PBaseArray.
* Added ability to not include ASCII in PbaseArray output using ios::fixed.
*
* Revision 1.115 2002/06/19 04:04:30 robertj
* Fixed bug in setting/getting bits from PBitArray, could exceed array bounds.
*
* Revision 1.114 2002/06/17 09:16:18 robertj
* Fixed strange deadlock woth gcc 3.0.2, thanks Artis Kugevics
*
* Revision 1.113 2002/06/14 13:22:52 robertj
* Added PBitArray class.
*
* Revision 1.112 2002/06/05 12:29:15 craigs
* Changes for gcc 3.1
*
* Revision 1.111 2002/04/09 02:30:18 robertj
* Removed GCC3 variable as __GNUC__ can be used instead, thanks jason Spence
*
* Revision 1.110 2002/02/15 04:30:39 robertj
* Added PString::Empty() to return the primordial empty string. Saves on a
* couple of memory allocations for every empty string ever used.
* Changed every place where a const char * is used with PString so that a
* NULL pointer is treated like an empty string instead of asserting.
*
* Revision 1.109 2002/01/26 23:57:45 craigs
* Changed for GCC 3.0 compatibility, thanks to manty@manty.net
*
* Revision 1.108 2002/01/22 01:03:57 craigs
* Added operator += and operator + functions to PStringArray and PStringList
* Added AppendString operator to PStringArray
*
* Revision 1.107 2001/10/30 00:20:12 robertj
* Fixed broken signed number conversion from previous change.
*
* Revision 1.106 2001/10/18 00:35:08 robertj
* Fixed problem with Tokenise() includeing empty string at beginning when
* not in onePerSeparator mode and a separator starts the string.
*
* Revision 1.105 2001/10/17 05:09:22 robertj
* Added contructors and assigmnent operators so integer types can be
* automatically converted to strings.
*
* Revision 1.104 2001/08/11 15:01:44 rogerh
* Add Mac OS Carbon changes from John Woods <jfw@jfwhome.funhouse.com>
*
* Revision 1.103 2001/05/08 23:27:12 robertj
* Fixed, yet again, case significance in hash function.
*
* Revision 1.102 2001/04/26 03:45:19 robertj
* Changed PString has function again, use a prime number for modulus.
*
* Revision 1.101 2001/04/24 02:39:18 robertj
* Fixed problem with new string hash function giving negative indexes.
*
* Revision 1.100 2001/04/23 03:13:16 robertj
* Changed PString hash function to better one, thanks Patrick Koorevaar
*
* Revision 1.99 2001/04/18 04:10:15 robertj
* Removed hash function for caseless strings as confuses mixed dictionaries.
*
* Revision 1.98 2001/04/18 01:20:59 robertj
* Fixed problem with hash function for short strings, thanks Patrick Koorevaar.
* Also fixed hash function for caseless strings.
*
* Revision 1.97 2001/03/14 01:51:01 craigs
* Changed to handle CRLF at end of PString::ReadFrom as well as LF
*
* Revision 1.96 2001/02/26 07:50:18 robertj
* Updated regular expression parser to latest version from Henry Spencer.
*
* Revision 1.95 2001/02/21 03:38:37 robertj
* Added ability to copy between various string lists/arrays etc during construction.
*
* Revision 1.94 2001/02/14 22:21:08 robertj
* Fixed compiler error on some versions of GCC, thanks Klaus Kaempf.
*
* Revision 1.93 2001/02/14 06:50:01 robertj
* Fixed bug in doing ::flush on a PStringStream, did not set pointers correctly.
*
* Revision 1.92 2001/02/13 04:39:08 robertj
* Fixed problem with operator= in container classes. Some containers will
* break unless the copy is virtual (eg PStringStream's buffer pointers) so
* needed to add a new AssignContents() function to all containers.
*
* Revision 1.91 2000/12/29 07:36:57 craigs
* Fixed problem with Tokenise function returning NULL entries in array
*
* Revision 1.90 2000/10/12 05:14:41 robertj
* Fixed crash caused by previous change, didn;t work if in constructor.
*
* Revision 1.89 2000/10/09 23:43:58 robertj
* Fixed GNU C++ compatibility on last change.
*
* Revision 1.88 2000/10/09 23:37:17 robertj
* Improved PString sprintf functions so no longer limited to 1000 characters, thanks Yuriy Ershov.
*
* Revision 1.87 2000/06/26 11:17:20 robertj
* Nucleus++ port (incomplete).
*
* Revision 1.86 2000/04/07 06:29:46 rogerh
* Add a short term workaround for an Internal Compiler Error on MAC OS X when
* returning certain types of PString. Submitted by Kevin Packard.
*
* Revision 1.85 2000/02/05 22:36:09 craigs
* Fixed problem caused by last modification
*
* Revision 1.84 2000/02/04 19:34:26 craigs
* Fixed problem with changing size of referenced objects
*
* Revision 1.83 2000/01/25 14:05:35 robertj
* Added optimisation to array comparisons if referencing same array.
*
* Revision 1.82 1999/08/18 01:45:13 robertj
* Added concatenation function to "base type" arrays.
*
* Revision 1.81 1999/08/17 03:46:40 robertj
* Fixed usage of inlines in optimised version.
*
* Revision 1.80 1999/08/12 12:12:47 robertj
* GCC 2.95 compatibility.
*
* Revision 1.79 1999/05/28 14:01:53 robertj
* Added initialisers to string containers (list, sorted list and set).
*
* Revision 1.78 1999/04/18 09:36:31 robertj
* Get date grammar build.
*
* Revision 1.77 1999/04/16 14:38:37 craigs
* Changes to make getdate.y compile under Linux
*
* Revision 1.76 1998/10/28 00:58:40 robertj
* Changed PStringStream so flush or endl does not clear the string output, this now
* just sets the string to minimum size.
*
* Revision 1.75 1998/10/13 14:06:18 robertj
* Complete rewrite of memory leak detection code.
*
* Revision 1.74 1998/09/23 06:21:54 robertj
* Added open source copyright license.
*
* Revision 1.73 1998/09/22 02:42:39 robertj
* Fixed problem treating unsigned integer as signed in PString contructor.
*
* Revision 1.72 1998/09/15 08:26:42 robertj
* Fixed a number of warnings at maximum optimisation.
*
* Revision 1.71 1998/09/14 12:36:29 robertj
* Fixed bug causing memory leak due to uninitialised member variable for dynamic allocation of arrays.
*
* Revision 1.70 1998/08/21 05:24:07 robertj
* Added hex dump capability to base array types.
* Added ability to have base arrays of static memory blocks.
*
* Revision 1.69 1998/03/17 10:13:23 robertj
* Fixed bug in Trim() should do all white space not just the space character.
*
* Revision 1.68 1998/01/26 00:37:48 robertj
* Fixed PString & operator putting space in if right hand side is empty string, it shouldn't..
* Added Execute() functions to PRegularExpression that take PINDEX references instead of PIntArrays.
* Added FindRegEx function to PString that returns position and length.
*
* Revision 1.67 1997/12/11 13:32:49 robertj
* Added AsUnsigned() function to convert string to DWORD.
*
* Revision 1.66 1997/07/08 13:14:41 robertj
* Fixed bug where freeing null pointer.
*
* Revision 1.65 1997/06/08 04:48:04 robertj
* Added regular expressions.
*
* Revision 1.64 1997/03/02 03:41:42 robertj
* Fixed bug in not being able to construct a zero length PStringArray.
*
* Revision 1.63 1996/10/08 13:13:25 robertj
* Added operator += and &= for char so no implicit PString construction.
*
* Revision 1.62 1996/09/14 12:45:57 robertj
* Fixed bug in PString::Splice() function, no end of string put in.
*
* Revision 1.61 1996/08/22 13:21:55 robertj
* Fixed major bug in FindLast(), could scan all of memory in negative direction.
*
* Revision 1.60 1996/08/08 10:08:45 robertj
* Directory structure changes for common files.
*
* Revision 1.59 1996/05/26 03:46:27 robertj
* Compatibility to GNU 2.7.x
*
* Revision 1.58 1996/05/15 10:17:02 robertj
* Fixed idiotic bug in string compare, caseless version always matched.
*
* Revision 1.57 1996/05/09 12:17:10 robertj
* Fixed incorrect use of memcmp/strcmp return value.
* Added assertion when finding empty string.
*
* Revision 1.56 1996/04/14 02:52:39 robertj
* Fixed bug in PString::FindLast(), never found sub-strings.
*
* Revision 1.55 1996/03/31 08:58:49 robertj
* Fixed hash function for strings to work for caseless strings.
*
* Revision 1.54 1996/03/16 04:56:59 robertj
* Fixed bug in PStringStream assignment oeprator getting pointers wrong.
*
* Revision 1.53 1996/03/02 03:20:11 robertj
* Fixed bug in PString::Find() not finding substring if exactly same as string.
*
* Revision 1.52 1996/02/22 10:23:54 robertj
* Fixed buf in *= operator only comparing up to shortest string.
* Fixed bug in & operator for if left string is empty.
*
* Revision 1.51 1996/02/19 13:34:53 robertj
* Removed PCaselessString hash function to fix dictionary match failure.
* Fixed *= operator yet again.
*
* Revision 1.50 1996/02/08 12:20:44 robertj
* Added new operators to PString for case insensitive compare and spaced concatenate.
* Fixed bug in Find() not finding case insensitive substrings.
*
* Revision 1.49 1996/02/03 11:08:51 robertj
* Changed memcpy to memove to guarentee string operations will work correctly
* when moving overlapping strings around eg in PString::Splice().
*
* Revision 1.48 1996/01/28 14:12:22 robertj
* Fixed bug in Tokenise() for first token empty and PINDEX unsigned.
*
* Revision 1.47 1996/01/28 02:53:40 robertj
* Added assert into all Compare functions to assure comparison between compatible objects.
* Fixed bug in Find() function, subset sum calculation added one to many bytes.
*
* Revision 1.46 1996/01/24 14:43:19 robertj
* Added initialisers to string dictionaries.
*
* Revision 1.45 1996/01/23 13:17:38 robertj
* Added Replace() function to strings.
* String searching algorithm rewrite.
*
* Revision 1.44 1996/01/02 12:51:05 robertj
* Mac OS compatibility changes.
* Removed requirement that PArray elements have parameterless constructor..
*
* Revision 1.43 1995/10/14 15:07:42 robertj
* Changed arrays to not break references, but strings still need to.
*
* Revision 1.42 1995/06/17 00:46:20 robertj
* Added flag for PStringArray constructor to create caseless strings.
* Fixed bug in arrays when size set to zero.
*
* Revision 1.41 1995/06/04 12:39:59 robertj
* Made char * array all const in PStringArray constructor.
*
* Revision 1.40 1995/04/25 11:29:38 robertj
* Fixed Borland compiler warnings.
*
* Revision 1.39 1995/04/02 09:27:27 robertj
* Added "balloon" help.
*
* Revision 1.38 1995/03/12 04:46:02 robertj
* Fixed use of PCaselessString as dictionary key.
*
* Revision 1.37 1995/01/15 04:56:28 robertj
* Fixed PStringStream for correct pointer calculations in output.
*
* Revision 1.36 1995/01/10 11:44:13 robertj
* Removed PString parameter in stdarg function for GNU C++ compatibility.
*
* Revision 1.35 1995/01/09 12:32:56 robertj
* Removed unnecesary return value from I/O functions.
* Changed function names due to Mac port.
*
* Revision 1.34 1995/01/04 10:57:08 robertj
* Changed for HPUX and GNU2.6.x
*
* Revision 1.33 1995/01/03 09:39:08 robertj
* Put standard malloc style memory allocation etc into memory check system.
*
* Revision 1.32 1994/12/13 11:50:56 robertj
* Added MakeUnique() function to all container classes.
*
* Revision 1.31 1994/12/12 13:13:17 robertj
* Fixed bugs in PString mods just made.
*
* Revision 1.30 1994/12/12 10:16:27 robertj
* Restructuring and documentation of container classes.
* Renaming of some macros for declaring container classes.
* Added some extra functionality to PString.
* Added start to 2 byte characters in PString.
* Fixed incorrect overrides in PCaselessString.
*
* Revision 1.29 1994/12/05 11:19:36 robertj
* Moved SetMinSize from PAbstractArray to PContainer.
*
* Revision 1.28 1994/11/28 12:37:29 robertj
* Added dummy parameter to container classes.
*
* Revision 1.27 1994/10/30 11:50:44 robertj
* Split into Object classes and Container classes.
* Changed mechanism for doing notification callback functions.
*
* Revision 1.26 1994/10/23 03:43:07 robertj
* Changed PBaseArray so can have zero elements in it.
* Added Printf style constructor to PString.
*
* Revision 1.25 1994/09/25 10:49:44 robertj
* Added empty functions for serialisation.
*
* Revision 1.24 1994/08/21 23:43:02 robertj
* Added object serialisation classes.
* Changed parameter before variable argument list to NOT be a reference.
*
* Revision 1.23 1994/08/04 12:57:10 robertj
* Rewrite of memory check code.
*
* Revision 1.22 1994/08/01 03:40:28 robertj
* Fixed PString() constructor from integer
*
* Revision 1.21 1994/07/27 05:58:07 robertj
* Synchronisation.
*
* Revision 1.20 1994/07/25 03:38:38 robertj
* Added more memory tests.
*
* Revision 1.19 1994/07/17 10:46:06 robertj
* Added number conversions to PString.
*
* Revision 1.18 1994/06/25 11:55:15 robertj
* Unix version synchronisation.
*
* Revision 1.17 1994/04/20 12:17:44 robertj
* assert changes
*
* Revision 1.16 1994/04/11 12:08:37 robertj
* Fixed bug in memory leak hash table hash function, cant have negative numbers.
*
* Revision 1.15 1994/04/03 08:34:18 robertj
* Added help and focus functionality.
*
* Revision 1.14 1994/04/01 14:01:11 robertj
* Streams and stuff.
*
* Revision 1.13 1994/03/07 07:47:00 robertj
* Major upgrade
*
* Revision 1.12 1994/01/15 03:14:22 robertj
* Mac portability problems.
*
* Revision 1.11 1994/01/03 04:42:23 robertj
* Mass changes to common container classes and interactors etc etc etc.
*
* Revision 1.10 1993/12/31 06:53:02 robertj
* Made inlines optional for debugging purposes.
*
* Revision 1.9 1993/12/24 04:20:52 robertj
* Mac CFront port.
*
* Revision 1.8 1993/12/16 00:51:46 robertj
* Made some container functions const.
*
* Revision 1.7 1993/12/15 21:10:10 robertj
* Fixed reference system used by container classes.
* Plugged memory leaks in PList and PSortedList.
*
* Revision 1.6 1993/12/14 18:44:56 robertj
* Added RemoveAll() function to collections.
* Fixed bug in list processing when being destroyed (removes the item being
* deleted from the list before deleting it).
* Changed GetIndex() so does not assert if entry not in collection.
*
* Revision 1.5 1993/12/04 05:22:38 robertj
* Added more string functions.
*
* Revision 1.4 1993/09/27 16:35:25 robertj
* Fixed bugs in sorted list.
* Fixed compatibility problem with sprintf return value (SVR4).
* Change function for making string array to a constructor.
*
* Revision 1.3 1993/08/27 18:17:47 robertj
* Fixed bugs in PAbstractSortedList (including some formatting).
*
* Revision 1.2 1993/08/21 01:50:33 robertj
* Made Clone() function optional, default will assert if called.
*
* Revision 1.8 1993/08/01 14:05:27 robertj
* Added const to ToLower() and ToUpper() in the PString class.
*
* Revision 1.7 1993/07/16 14:40:55 robertj
* Added PString constructor for individual characters.
* Added string to C style literal format.
*
* Revision 1.6 1993/07/15 05:02:57 robertj
* Removed redundant word in PString enum for string types.
*
* Revision 1.5 1993/07/15 04:29:39 robertj
* Added new constructor to convert from other string formats.
* Fixed sprintf variable parameter list bug.
*
* Revision 1.4 1993/07/14 12:41:52 robertj
* Fixed comment leader.
*
* Revision 1.3 1993/07/14 02:06:34 robertj
* Fixed header comment for RCS.
*/
#include <ptlib.h>
#include <ctype.h>
#ifdef __NUCLEUS_PLUS__
extern "C" int vsprintf(char *, const char *, va_list);
#endif
#if P_REGEX
#include <regex.h>
#else
#include "regex/regex.h"
#endif
#define regexpression ((regex_t *)expression)
#if !P_USE_INLINES
#include "ptlib/contain.inl"
#endif
#define new PNEW
#undef __CLASS__
#define __CLASS__ GetClass()
///////////////////////////////////////////////////////////////////////////////
PContainer::PContainer(PINDEX initialSize)
{
reference = new Reference(initialSize);
PAssert(reference != NULL, POutOfMemory);
}
PContainer::PContainer(int, const PContainer * cont)
{
PAssert(cont != NULL, PInvalidParameter);
PAssert2(cont->reference != NULL, cont->GetClass(), "Clone of deleted container");
#if PCONTAINER_USES_CRITSEC
PEnterAndLeave m(cont->reference->critSec);
#endif
reference = new Reference(*cont->reference); // create a new reference
PAssert(reference != NULL, POutOfMemory);
}
PContainer::PContainer(const PContainer & cont)
{
PAssert2(cont.reference != NULL, cont.GetClass(), "Copy of deleted container");
#if PCONTAINER_USES_CRITSEC
PEnterAndLeave m(cont.reference->critSec);
#endif
++cont.reference->count;
reference = cont.reference; // copy the reference pointer
}
void PContainer::AssignContents(const PContainer & cont)
{
#if PCONTAINER_USES_CRITSEC
// make sure the critsecs are entered and left in the right order to avoid deadlock
cont.reference->critSec.Enter();
reference->critSec.Enter();
#endif
PAssert2(cont.reference != NULL, cont.GetClass(), "Assign of deleted container");
if (reference == cont.reference) {
#if PCONTAINER_USES_CRITSEC
reference->critSec.Leave();
cont.reference->critSec.Leave();
#endif
return;
}
if (!IsUnique()) {
--reference->count;
#if PCONTAINER_USES_CRITSEC
reference->critSec.Leave();
#endif
} else {
#if PCONTAINER_USES_CRITSEC
reference->critSec.Leave();
#endif
DestroyContents();
delete reference;
reference = NULL;
}
++cont.reference->count;
reference = cont.reference;
#if PCONTAINER_USES_CRITSEC
cont.reference->critSec.Leave();
#endif
}
void PContainer::Destruct()
{
if (reference != NULL) {
#if PCONTAINER_USES_CRITSEC
Reference * ref = reference;
ref->critSec.Enter();
#endif
--reference->count;
if (reference->count > 0) {
reference = NULL;
#if PCONTAINER_USES_CRITSEC
ref->critSec.Leave();
#endif
}
else {
#if PCONTAINER_USES_CRITSEC
ref->critSec.Leave();
#endif
DestroyContents();
delete reference;
reference = NULL;
}
}
}
BOOL PContainer::SetMinSize(PINDEX minSize)
{
PASSERTINDEX(minSize);
if (minSize < 0)
minSize = 0;
if (minSize < GetSize())
minSize = GetSize();
return SetSize(minSize);
}
BOOL PContainer::MakeUnique()
{
#if PCONTAINER_USES_CRITSEC
PEnterAndLeave m(reference->critSec);
#endif
if (IsUnique())
return TRUE;
Reference * oldReference = reference;
reference = new Reference(*reference);
--oldReference->count;
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
PAbstractArray::PAbstractArray(PINDEX elementSizeInBytes, PINDEX initialSize)
: PContainer(initialSize)
{
elementSize = elementSizeInBytes;
PAssert(elementSize != 0, PInvalidParameter);
if (GetSize() == 0)
theArray = NULL;
else {
theArray = (char *)calloc(GetSize(), elementSize);
PAssert(theArray != NULL, POutOfMemory);
}
allocatedDynamically = TRUE;
}
PAbstractArray::PAbstractArray(PINDEX elementSizeInBytes,
const void *buffer,
PINDEX bufferSizeInElements,
BOOL dynamicAllocation)
: PContainer(bufferSizeInElements)
{
elementSize = elementSizeInBytes;
PAssert(elementSize != 0, PInvalidParameter);
allocatedDynamically = dynamicAllocation;
if (GetSize() == 0)
theArray = NULL;
else if (dynamicAllocation) {
PINDEX sizebytes = elementSize*GetSize();
theArray = (char *)malloc(sizebytes);
PAssert(theArray != NULL, POutOfMemory);
memcpy(theArray, PAssertNULL(buffer), sizebytes);
}
else
theArray = (char *)buffer;
}
void PAbstractArray::DestroyContents()
{
if (theArray != NULL) {
if (allocatedDynamically)
free(theArray);
theArray = NULL;
}
}
void PAbstractArray::CopyContents(const PAbstractArray & array)
{
elementSize = array.elementSize;
theArray = array.theArray;
allocatedDynamically = array.allocatedDynamically;
}
void PAbstractArray::CloneContents(const PAbstractArray * array)
{
elementSize = array->elementSize;
PINDEX sizebytes = elementSize*GetSize();
char * newArray = (char *)malloc(sizebytes);
if (newArray == NULL)
reference->size = 0;
else
memcpy(newArray, array->theArray, sizebytes);
theArray = newArray;
allocatedDynamically = TRUE;
}
void PAbstractArray::PrintOn(ostream & strm) const
{
char separator = strm.fill();
int width = strm.width();
for (PINDEX i = 0; i < GetSize(); i++) {
if (i > 0 && separator != '\0')
strm << separator;
strm.width(width);
PrintElementOn(strm, i);
}
if (separator == '\n')
strm << '\n';
}
void PAbstractArray::ReadFrom(istream & strm)
{
PINDEX i = 0;
while (strm.good()) {
ReadElementFrom(strm, i);
if (!strm.fail())
i++;
}
SetSize(i);
}
PObject::Comparison PAbstractArray::Compare(const PObject & obj) const
{
PAssert(PIsDescendant(&obj, PAbstractArray), PInvalidCast);
const PAbstractArray & other = (const PAbstractArray &)obj;
char * otherArray = other.theArray;
if (theArray == otherArray)
return EqualTo;
if (elementSize < other.elementSize)
return LessThan;
if (elementSize > other.elementSize)
return GreaterThan;
PINDEX thisSize = GetSize();
PINDEX otherSize = other.GetSize();
if (thisSize < otherSize)
return LessThan;
if (thisSize > otherSize)
return GreaterThan;
if (thisSize == 0)
return EqualTo;
int retval = memcmp(theArray, otherArray, elementSize*thisSize);
if (retval < 0)
return LessThan;
if (retval > 0)
return GreaterThan;
return EqualTo;
}
BOOL PAbstractArray::SetSize(PINDEX newSize)
{
return InternalSetSize(newSize, FALSE);
}
BOOL PAbstractArray::InternalSetSize(PINDEX newSize, BOOL force)
{
if (newSize < 0)
newSize = 0;
PINDEX newsizebytes = elementSize*newSize;
PINDEX oldsizebytes = elementSize*GetSize();
if (!force && (newsizebytes == oldsizebytes))
return TRUE;
char * newArray;
#if PCONTAINER_USES_CRITSEC
PEnterAndLeave m(reference->critSec);
#endif
if (!IsUnique()) {
if (newsizebytes == 0)
newArray = NULL;
else {
if ((newArray = (char *)malloc(newsizebytes)) == NULL)
return FALSE;
if (theArray != NULL)
memcpy(newArray, theArray, PMIN(oldsizebytes, newsizebytes));
}
--reference->count;
reference = new Reference(newSize);
} else {
if (theArray != NULL) {
if (newsizebytes == 0) {
if (allocatedDynamically)
free(theArray);
newArray = NULL;
}
else if (allocatedDynamically) {
if ((newArray = (char *)realloc(theArray, newsizebytes)) == NULL)
return FALSE;
}
else {
if ((newArray = (char *)malloc(newsizebytes)) == NULL)
return FALSE;
memcpy(newArray, theArray, PMIN(newsizebytes, oldsizebytes));
allocatedDynamically = TRUE;
}
}
else if (newsizebytes != 0) {
if ((newArray = (char *)malloc(newsizebytes)) == NULL)
return FALSE;
}
else
newArray = NULL;
reference->size = newSize;
}
if (newsizebytes > oldsizebytes)
memset(newArray+oldsizebytes, 0, newsizebytes-oldsizebytes);
theArray = newArray;
return TRUE;
}
void PAbstractArray::Attach(const void *buffer, PINDEX bufferSize)
{
if (allocatedDynamically && theArray != NULL)
free(theArray);
#if PCONTAINER_USES_CRITSEC
PEnterAndLeave m(reference->critSec);
#endif
theArray = (char *)buffer;
reference->size = bufferSize;
allocatedDynamically = FALSE;
}
void * PAbstractArray::GetPointer(PINDEX minSize)
{
PAssert(SetMinSize(minSize), POutOfMemory);
return theArray;
}
BOOL PAbstractArray::Concatenate(const PAbstractArray & array)
{
if (!allocatedDynamically || array.elementSize != elementSize)
return FALSE;
PINDEX oldLen = GetSize();
PINDEX addLen = array.GetSize();
if (!SetSize(oldLen + addLen))
return FALSE;
memcpy(theArray+oldLen*elementSize, array.theArray, addLen*elementSize);
return TRUE;
}
void PAbstractArray::PrintElementOn(ostream & /*stream*/, PINDEX /*index*/) const
{
}
void PAbstractArray::ReadElementFrom(istream & /*stream*/, PINDEX /*index*/)
{
}
///////////////////////////////////////////////////////////////////////////////
void PCharArray::PrintOn(ostream & strm) const
{
PINDEX width = strm.width();
if (width > GetSize())
width -= GetSize();
else
width = 0;
BOOL left = (strm.flags()&ios::adjustfield) == ios::left;
if (left)
strm.write(theArray, GetSize());
while (width-- > 0)
strm << (char)strm.fill();
if (!left)
strm.write(theArray, GetSize());
}
void PCharArray::ReadFrom(istream &strm)
{
PINDEX size = 0;
SetSize(size+100);
while (strm.good()) {
strm >> theArray[size++];
if (size >= GetSize())
SetSize(size+100);
}
SetSize(size);
}
void PBYTEArray::PrintOn(ostream & strm) const
{
PINDEX line_width = strm.width();
if (line_width == 0)
line_width = 16;
strm.width(0);
PINDEX indent = strm.precision();
PINDEX val_width = ((strm.flags()&ios::basefield) == ios::hex) ? 2 : 3;
PINDEX i = 0;
while (i < GetSize()) {
if (i > 0)
strm << '\n';
PINDEX j;
for (j = 0; j < indent; j++)
strm << ' ';
for (j = 0; j < line_width; j++) {
if (j == line_width/2)
strm << ' ';
if (i+j < GetSize())
strm << setw(val_width) << (theArray[i+j]&0xff);
else {
PINDEX k;
for (k = 0; k < val_width; k++)
strm << ' ';
}
strm << ' ';
}
if ((strm.flags()&ios::floatfield) != ios::fixed) {
strm << " ";
for (j = 0; j < line_width; j++) {
if (i+j < GetSize()) {
unsigned val = theArray[i+j]&0xff;
if (isprint(val))
strm << (char)val;
else
strm << '.';
}
}
}
i += line_width;
}
}
void PBYTEArray::ReadFrom(istream &strm)
{
PINDEX size = 0;
SetSize(size+100);
while (strm.good()) {
unsigned v;
strm >> v;
theArray[size] = (BYTE)v;
if (!strm.fail()) {
size++;
if (size >= GetSize())
SetSize(size+100);
}
}
SetSize(size);
}
///////////////////////////////////////////////////////////////////////////////
PBitArray::PBitArray(PINDEX initialSize)
: PBYTEArray((initialSize+7)>>3)
{
}
PBitArray::PBitArray(const void * buffer,
PINDEX length,
BOOL dynamic)
: PBYTEArray((const BYTE *)buffer, (length+7)>>3, dynamic)
{
}
PObject * PBitArray::Clone() const
{
return new PBitArray(*this);
}
PINDEX PBitArray::GetSize() const
{
return PBYTEArray::GetSize()<<3;
}
BOOL PBitArray::SetSize(PINDEX newSize)
{
return PBYTEArray::SetSize((newSize+7)>>3);
}
BOOL PBitArray::SetAt(PINDEX index, BOOL val)
{
if (!SetMinSize(index+1))
return FALSE;
if (val)
theArray[index>>3] |= (1 << (index&7));
else
theArray[index>>3] &= ~(1 << (index&7));
return TRUE;
}
BOOL PBitArray::GetAt(PINDEX index) const
{
PASSERTINDEX(index);
if (index >= GetSize())
return FALSE;
return (theArray[index>>3]&(1 << (index&7))) != 0;
}
void PBitArray::Attach(const void * buffer, PINDEX bufferSize)
{
PBYTEArray::Attach((const BYTE *)buffer, (bufferSize+7)>>3);
}
BYTE * PBitArray::GetPointer(PINDEX minSize)
{
return PBYTEArray::GetPointer((minSize+7)>>3);
}
BOOL PBitArray::Concatenate(const PBitArray & array)
{
return PAbstractArray::Concatenate(array);
}
///////////////////////////////////////////////////////////////////////////////
PString::PString(const char * cstr)
: PCharArray(cstr != NULL ? strlen(cstr)+1 : 1)
{
if (cstr != NULL)
memcpy(theArray, cstr, GetSize());
}
PString::PString(const WORD * ustr)
{
if (ustr == NULL)
SetSize(1);
else {
PINDEX len = 0;
while (ustr[len] != 0)
len++;
InternalFromUCS2(ustr, len);
}
}
PString::PString(const char * cstr, PINDEX len)
: PCharArray(len+1)
{
if (len > 0)
memcpy(theArray, PAssertNULL(cstr), len);
}
PString::PString(const WORD * ustr, PINDEX len)
: PCharArray(len+1)
{
InternalFromUCS2(ustr, len);
}
PString::PString(const PWORDArray & ustr)
{
InternalFromUCS2(ustr, ustr.GetSize());
}
static int TranslateHex(char x)
{
if (x >= 'a')
return x - 'a' + 10;
if (x >= 'A')
return x - 'A' + '\x0a';
return x - '0';
}
static const unsigned char PStringEscapeCode[] = { 'a', 'b', 'f', 'n', 'r', 't', 'v' };
static const unsigned char PStringEscapeValue[] = { '\a', '\b', '\f', '\n', '\r', '\t', '\v' };
static void TranslateEscapes(const char * src, char * dst)
{
if (*src == '"')
src++;
while (*src != '\0') {
int c = *src++ & 0xff;
if (c == '"' && *src == '\0')
c = '\0'; // Trailing '"' is ignored
else if (c == '\\') {
c = *src++ & 0xff;
for (PINDEX i = 0; i < PARRAYSIZE(PStringEscapeCode); i++) {
if (c == PStringEscapeCode[i])
c = PStringEscapeValue[i];
}
if (c == 'x' && isxdigit(*src & 0xff)) {
c = TranslateHex(*src++);
if (isxdigit(*src & 0xff))
c = (c << 4) + TranslateHex(*src++);
}
else if (c >= '0' && c <= '7') {
int count = c <= '3' ? 3 : 2;
src--;
c = 0;
do {
c = (c << 3) + *src++ - '0';
} while (--count > 0 && *src >= '0' && *src <= '7');
}
}
*dst++ = (char)c;
}
}
PString::PString(ConversionType type, const char * str, ...)
{
switch (type) {
case Pascal :
if (*str != '\0') {
PINDEX len = *str & 0xff;
PAssert(SetSize(len+1), POutOfMemory);
memcpy(theArray, str+1, len);
}
break;
case Basic :
if (str[0] != '\0' && str[1] != '\0') {
PINDEX len = (str[0] & 0xff) | ((str[1] & 0xff) << 8);
PAssert(SetSize(len+1), POutOfMemory);
memcpy(theArray, str+2, len);
}
break;
case Literal :
PAssert(SetSize(strlen(str)+1), POutOfMemory);
TranslateEscapes(str, theArray);
PAssert(MakeMinimumSize(), POutOfMemory);
break;
case Printf : {
va_list args;
va_start(args, str);
vsprintf(str, args);
va_end(args);
break;
}
default :
PAssertAlways(PInvalidParameter);
}
}
template <class T> char * p_unsigned2string(T value, T base, char * str)
{
if (value >= base)
str = p_unsigned2string<T>(value/base, base, str);
value %= base;
if (value < 10)
*str = (char)(value + '0');
else
*str = (char)(value + 'A'-10);
return str+1;
}
template <class T> char * p_signed2string(T value, T base, char * str)
{
if (value >= 0)
return p_unsigned2string<T>(value, base, str);
*str = '-';
return p_unsigned2string<T>(-value, base, str+1);
}
PString::PString(short n)
: PCharArray(sizeof(short)*3+1)
{
p_signed2string<int>(n, 10, theArray);
MakeMinimumSize();
}
PString::PString(unsigned short n)
: PCharArray(sizeof(unsigned short)*3+1)
{
p_unsigned2string<unsigned int>(n, 10, theArray);
MakeMinimumSize();
}
PString::PString(int n)
: PCharArray(sizeof(int)*3+1)
{
p_signed2string<int>(n, 10, theArray);
MakeMinimumSize();
}
PString::PString(unsigned int n)
: PCharArray(sizeof(unsigned int)*3+1)
{
p_unsigned2string<unsigned int>(n, 10, theArray);
MakeMinimumSize();
}
PString::PString(long n)
: PCharArray(sizeof(long)*3+1)
{
p_signed2string<long>(n, 10, theArray);
MakeMinimumSize();
}
PString::PString(unsigned long n)
: PCharArray(sizeof(unsigned long)*3+1)
{
p_unsigned2string<unsigned long>(n, 10, theArray);
MakeMinimumSize();
}
PString::PString(PInt64 n)
: PCharArray(sizeof(PInt64)*3+1)
{
p_signed2string<PInt64>(n, 10, theArray);
MakeMinimumSize();
}
PString::PString(PUInt64 n)
: PCharArray(sizeof(PUInt64)*3+1)
{
p_unsigned2string<PUInt64>(n, 10, theArray);
MakeMinimumSize();
}
PString::PString(ConversionType type, long value, unsigned base)
: PCharArray(sizeof(long)*3+1)
{
PAssert(base >= 2 && base <= 36, PInvalidParameter);
switch (type) {
case Signed :
p_signed2string<long>(value, base, theArray);
break;
case Unsigned :
p_unsigned2string<unsigned long>(value, base, theArray);
break;
default :
PAssertAlways(PInvalidParameter);
}
MakeMinimumSize();
}
PString::PString(ConversionType type, double value, unsigned places)
{
switch (type) {
case Decimal :
sprintf("%0.*f", (int)places, value);
break;
case Exponent :
sprintf("%0.*e", (int)places, value);
break;
default :
PAssertAlways(PInvalidParameter);
}
}
PString & PString::operator=(short n)
{
SetMinSize(sizeof(short)*3+1);
p_signed2string<int>(n, 10, theArray);
MakeMinimumSize();
return *this;
}
PString & PString::operator=(unsigned short n)
{
SetMinSize(sizeof(unsigned short)*3+1);
p_unsigned2string<unsigned int>(n, 10, theArray);
MakeMinimumSize();
return *this;
}
PString & PString::operator=(int n)
{
SetMinSize(sizeof(int)*3+1);
p_signed2string<int>(n, 10, theArray);
MakeMinimumSize();
return *this;
}
PString & PString::operator=(unsigned int n)
{
SetMinSize(sizeof(unsigned int)*3+1);
p_unsigned2string<unsigned int>(n, 10, theArray);
MakeMinimumSize();
return *this;
}
PString & PString::operator=(long n)
{
SetMinSize(sizeof(long)*3+1);
p_signed2string<long>(n, 10, theArray);
MakeMinimumSize();
return *this;
}
PString & PString::operator=(unsigned long n)
{
SetMinSize(sizeof(unsigned long)*3+1);
p_unsigned2string<unsigned long>(n, 10, theArray);
MakeMinimumSize();
return *this;
}
PString & PString::operator=(PInt64 n)
{
SetMinSize(sizeof(PInt64)*3+1);
p_signed2string<PInt64>(n, 10, theArray);
MakeMinimumSize();
return *this;
}
PString & PString::operator=(PUInt64 n)
{
SetMinSize(sizeof(PUInt64)*3+1);
p_unsigned2string<PUInt64>(n, 10, theArray);
MakeMinimumSize();
return *this;
}
PString & PString::MakeEmpty()
{
SetSize(1);
*theArray = '\0';
return *this;
}
PObject * PString::Clone() const
{
return new PString(*this);
}
void PString::PrintOn(ostream &strm) const
{
strm << theArray;
}
void PString::ReadFrom(istream &strm)
{
SetMinSize(100);
char * ptr = theArray;
PINDEX len = 0;
int c;
while ((c = strm.get()) != EOF && c != '\n') {
*ptr++ = (char)c;
len++;
if (len >= GetSize()) {
SetSize(len + 100);
ptr = theArray + len;
}
}
*ptr = '\0';
if ((len > 0) && (ptr[-1] == '\r'))
ptr[-1] = '\0';
PAssert(MakeMinimumSize(), POutOfMemory);
}
PObject::Comparison PString::Compare(const PObject & obj) const
{
PAssert(PIsDescendant(&obj, PString), PInvalidCast);
return InternalCompare(0, P_MAX_INDEX, ((const PString &)obj).theArray);
}
PINDEX PString::HashFunction() const
{
// Hash function from "Data Structures and Algorithm Analysis in C++" by
// Mark Allen Weiss, with limit of only executing over first 8 characters to
// increase speed when dealing with large strings.
PINDEX hash = 0;
for (PINDEX i = 0; i < 8 && theArray[i] != 0; i++)
hash = (hash << 5) ^ tolower(theArray[i] & 0xff) ^ hash;
return PABSINDEX(hash)%127;
}
BOOL PString::IsEmpty() const
{
return (theArray == NULL) || (*theArray == '\0');
}
BOOL PString::SetSize(PINDEX newSize)
{
return InternalSetSize(newSize, TRUE);
}
BOOL PString::MakeUnique()
{
#if PCONTAINER_USES_CRITSEC
PEnterAndLeave m(reference->critSec);
#endif
if (IsUnique())
return TRUE;
InternalSetSize(GetSize(), TRUE);
return FALSE;
}
PString PString::operator+(const char * cstr) const
{
if (cstr == NULL)
return *this;
PINDEX olen = GetLength();
PINDEX alen = strlen(cstr)+1;
PString str;
str.SetSize(olen+alen);
memmove(str.theArray, theArray, olen);
memcpy(str.theArray+olen, cstr, alen);
return str;
}
PString PString::operator+(char c) const
{
PINDEX olen = GetLength();
PString str;
str.SetSize(olen+2);
memmove(str.theArray, theArray, olen);
str.theArray[olen] = c;
return str;
}
PString & PString::operator+=(const char * cstr)
{
if (cstr == NULL)
return *this;
PINDEX olen = GetLength();
PINDEX alen = strlen(cstr)+1;
SetSize(olen+alen);
memcpy(theArray+olen, cstr, alen);
return *this;
}
PString & PString::operator+=(char ch)
{
PINDEX olen = GetLength();
SetSize(olen+2);
theArray[olen] = ch;
return *this;
}
PString PString::operator&(const char * cstr) const
{
if (cstr == NULL)
return *this;
PINDEX alen = strlen(cstr)+1;
if (alen == 1)
return *this;
PINDEX olen = GetLength();
PString str;
PINDEX space = olen > 0 && theArray[olen-1]!=' ' && *cstr!=' ' ? 1 : 0;
str.SetSize(olen+alen+space);
memmove(str.theArray, theArray, olen);
if (space != 0)
str.theArray[olen] = ' ';
memcpy(str.theArray+olen+space, cstr, alen);
return str;
}
PString PString::operator&(char c) const
{
PINDEX olen = GetLength();
PString str;
PINDEX space = olen > 0 && theArray[olen-1] != ' ' && c != ' ' ? 1 : 0;
str.SetSize(olen+2+space);
memmove(str.theArray, theArray, olen);
if (space != 0)
str.theArray[olen] = ' ';
str.theArray[olen+space] = c;
return str;
}
PString & PString::operator&=(const char * cstr)
{
if (cstr == NULL)
return *this;
PINDEX alen = strlen(cstr)+1;
if (alen == 1)
return *this;
PINDEX olen = GetLength();
PINDEX space = olen > 0 && theArray[olen-1]!=' ' && *cstr!=' ' ? 1 : 0;
SetSize(olen+alen+space);
if (space != 0)
theArray[olen] = ' ';
memcpy(theArray+olen+space, cstr, alen);
return *this;
}
PString & PString::operator&=(char ch)
{
PINDEX olen = GetLength();
PINDEX space = olen > 0 && theArray[olen-1] != ' ' && ch != ' ' ? 1 : 0;
SetSize(olen+2+space);
if (space != 0)
theArray[olen] = ' ';
theArray[olen+space] = ch;
return *this;
}
void PString::Delete(PINDEX start, PINDEX len)
{
if (start < 0 || len < 0)
return;
MakeUnique();
register PINDEX slen = GetLength();
if (start > slen)
return;
if (len > slen - start)
SetAt(start, '\0');
else
memmove(theArray+start, theArray+start+len, slen-start-len+1);
MakeMinimumSize();
}
PString PString::operator()(PINDEX start, PINDEX end) const
{
if (end < 0 || start < 0 || end < start)
return Empty();
register PINDEX len = GetLength();
if (start > len)
return Empty();
if (end >= len) {
if (start == 0)
return *this;
end = len-1;
}
len = end - start + 1;
return PString(theArray+start, len);
}
PString PString::Left(PINDEX len) const
{
if (len <= 0)
return Empty();
if (len >= GetLength())
return *this;
return PString(theArray, len);
}
PString PString::Right(PINDEX len) const
{
if (len <= 0)
return Empty();
PINDEX srclen = GetLength();
if (len >= srclen)
return *this;
return PString(theArray+srclen-len, len);
}
PString PString::Mid(PINDEX start, PINDEX len) const
{
if (len <= 0 || start < 0)
return Empty();
if (start+len < start) // Beware of wraparound
return operator()(start, P_MAX_INDEX);
else
return operator()(start, start+len-1);
}
bool PString::operator*=(const char * cstr) const
{
if (cstr == NULL)
return IsEmpty() != FALSE;
const char * pstr = theArray;
while (*pstr != '\0' && *cstr != '\0') {
if (toupper(*pstr & 0xff) != toupper(*cstr & 0xff))
return FALSE;
pstr++;
cstr++;
}
return *pstr == *cstr;
}
PObject::Comparison PString::NumCompare(const PString & str, PINDEX count, PINDEX offset) const
{
if (offset < 0 || count < 0)
return LessThan;
PINDEX len = str.GetLength();
if (count > len)
count = len;
return InternalCompare(offset, count, str);
}
PObject::Comparison PString::NumCompare(const char * cstr, PINDEX count, PINDEX offset) const
{
if (offset < 0 || count < 0)
return LessThan;
PINDEX len = ::strlen(cstr);
if (count > len)
count = len;
return InternalCompare(offset, count, cstr);
}
PObject::Comparison PString::InternalCompare(PINDEX offset, char c) const
{
if (offset < 0)
return LessThan;
const int ch = theArray[offset] & 0xff;
if (ch < (c & 0xff))
return LessThan;
if (ch > (c & 0xff))
return GreaterThan;
return EqualTo;
}
PObject::Comparison PString::InternalCompare(
PINDEX offset, PINDEX length, const char * cstr) const
{
if (offset < 0 || length < 0)
return LessThan;
if (offset == 0 && theArray == cstr)
return EqualTo;
if (offset < 0 || cstr == NULL)
return IsEmpty() ? EqualTo : LessThan;
int retval;
if (length == P_MAX_INDEX)
retval = strcmp(theArray+offset, cstr);
else
retval = strncmp(theArray+offset, cstr, length);
if (retval < 0)
return LessThan;
if (retval > 0)
return GreaterThan;
return EqualTo;
}
PINDEX PString::Find(char ch, PINDEX offset) const
{
if (offset < 0)
return P_MAX_INDEX;
register PINDEX len = GetLength();
while (offset < len) {
if (InternalCompare(offset, ch) == EqualTo)
return offset;
offset++;
}
return P_MAX_INDEX;
}
PINDEX PString::Find(const char * cstr, PINDEX offset) const
{
if (cstr == NULL || *cstr == '\0' || offset < 0)
return P_MAX_INDEX;
PINDEX len = GetLength();
PINDEX clen = strlen(cstr);
if (clen > len)
return P_MAX_INDEX;
if (offset > len - clen)
return P_MAX_INDEX;
if (len - clen < 10) {
while (offset+clen <= len) {
if (InternalCompare(offset, clen, cstr) == EqualTo)
return offset;
offset++;
}
return P_MAX_INDEX;
}
int strSum = 0;
int cstrSum = 0;
for (PINDEX i = 0; i < clen; i++) {
strSum += toupper(theArray[offset+i] & 0xff);
cstrSum += toupper(cstr[i] & 0xff);
}
// search for a matching substring
while (offset+clen <= len) {
if (strSum == cstrSum && InternalCompare(offset, clen, cstr) == EqualTo)
return offset;
strSum += toupper(theArray[offset+clen] & 0xff);
strSum -= toupper(theArray[offset] & 0xff);
offset++;
}
return P_MAX_INDEX;
}
PINDEX PString::FindLast(char ch, PINDEX offset) const
{
PINDEX len = GetLength();
if (len == 0 || offset < 0)
return P_MAX_INDEX;
if (offset >= len)
offset = len-1;
while (InternalCompare(offset, ch) != EqualTo) {
if (offset == 0)
return P_MAX_INDEX;
offset--;
}
return offset;
}
PINDEX PString::FindLast(const char * cstr, PINDEX offset) const
{
if (cstr == NULL || *cstr == '\0' || offset < 0)
return P_MAX_INDEX;
PINDEX len = GetLength();
PINDEX clen = strlen(cstr);
if (clen > len)
return P_MAX_INDEX;
if (offset > len - clen)
offset = len - clen;
int strSum = 0;
int cstrSum = 0;
for (PINDEX i = 0; i < clen; i++) {
strSum += toupper(theArray[offset+i] & 0xff);
cstrSum += toupper(cstr[i] & 0xff);
}
// search for a matching substring
while (strSum != cstrSum || InternalCompare(offset, clen, cstr) != EqualTo) {
if (offset == 0)
return P_MAX_INDEX;
--offset;
strSum += toupper(theArray[offset] & 0xff);
strSum -= toupper(theArray[offset+clen] & 0xff);
}
return offset;
}
PINDEX PString::FindOneOf(const char * cset, PINDEX offset) const
{
if (cset == NULL || *cset == '\0' || offset < 0)
return P_MAX_INDEX;
PINDEX len = GetLength();
while (offset < len) {
const char * p = cset;
while (*p != '\0') {
if (InternalCompare(offset, *p) == EqualTo)
return offset;
p++;
}
offset++;
}
return P_MAX_INDEX;
}
PINDEX PString::FindRegEx(const PRegularExpression & regex, PINDEX offset) const
{
if (offset < 0)
return P_MAX_INDEX;
PINDEX pos = 0;
PINDEX len = 0;
if (FindRegEx(regex, pos, len, offset))
return pos;
return P_MAX_INDEX;
}
BOOL PString::FindRegEx(const PRegularExpression & regex,
PINDEX & pos,
PINDEX & len,
PINDEX offset,
PINDEX maxPos) const
{
if (offset < 0 || maxPos < 0 || offset >= GetLength())
return FALSE;
if (!regex.Execute(&theArray[offset], pos, len, 0))
return FALSE;
pos += offset;
if (pos+len > maxPos)
return FALSE;
return TRUE;
}
void PString::Replace(const PString & target,
const PString & subs,
BOOL all, PINDEX offset)
{
if (offset < 0)
return;
MakeUnique();
PINDEX tlen = target.GetLength();
PINDEX slen = subs.GetLength();
do {
PINDEX pos = Find(target, offset);
if (pos == P_MAX_INDEX)
return;
Splice(subs, pos, tlen);
offset = pos + slen;
} while (all);
}
void PString::Splice(const char * cstr, PINDEX pos, PINDEX len)
{
if (len < 0 || pos < 0)
return;
register PINDEX slen = GetLength();
if (pos >= slen)
operator+=(cstr);
else {
MakeUnique();
PINDEX clen = cstr != NULL ? strlen(cstr) : 0;
PINDEX newlen = slen-len+clen;
if (clen > len)
SetSize(newlen+1);
if (pos+len < slen)
memmove(theArray+pos+clen, theArray+pos+len, slen-pos-len+1);
if (clen > 0)
memcpy(theArray+pos, cstr, clen);
theArray[newlen] = '\0';
}
}
PStringArray
PString::Tokenise(const char * separators, BOOL onePerSeparator) const
{
PStringArray tokens;
if (separators == NULL || IsEmpty()) // No tokens
return tokens;
PINDEX token = 0;
PINDEX p1 = 0;
PINDEX p2 = FindOneOf(separators);
if (p2 == 0) {
if (onePerSeparator) { // first character is a token separator
tokens[token] = Empty();
token++; // make first string in array empty
p1 = 1;
p2 = FindOneOf(separators, 1);
}
else {
do {
p1 = p2 + 1;
} while ((p2 = FindOneOf(separators, p1)) == p1);
}
}
while (p2 != P_MAX_INDEX) {
if (p2 > p1)
tokens[token] = operator()(p1, p2-1);
else
tokens[token] = Empty();
token++;
// Get next separator. If not one token per separator then continue
// around loop to skip over all the consecutive separators.
do {
p1 = p2 + 1;
} while ((p2 = FindOneOf(separators, p1)) == p1 && !onePerSeparator);
}
tokens[token] = operator()(p1, P_MAX_INDEX);
return tokens;
}
PStringArray PString::Lines() const
{
PStringArray lines;
if (IsEmpty())
return lines;
PINDEX line = 0;
PINDEX p1 = 0;
PINDEX p2;
while ((p2 = FindOneOf("\r\n", p1)) != P_MAX_INDEX) {
lines[line++] = operator()(p1, p2-1);
p1 = p2 + 1;
if (theArray[p2] == '\r' && theArray[p1] == '\n') // CR LF pair
p1++;
}
if (p1 < GetLength())
lines[line] = operator()(p1, P_MAX_INDEX);
return lines;
}
PStringArray & PStringArray::operator += (const PStringArray & v)
{
PINDEX i;
for (i = 0; i < v.GetSize(); i++)
AppendString(v[i]);
return *this;
}
PString PString::LeftTrim() const
{
const char * lpos = theArray;
while (isspace(*lpos & 0xff))
lpos++;
return PString(lpos);
}
PString PString::RightTrim() const
{
char * rpos = theArray+GetLength()-1;
if (isspace(*rpos & 0xff))
return *this;
while (isspace(*rpos & 0xff)) {
if (rpos == theArray)
return Empty();
rpos--;
}
// make Apple & Tornado gnu compiler happy
PString retval(theArray, rpos - theArray + 1);
return retval;
}
PString PString::Trim() const
{
const char * lpos = theArray;
while (isspace(*lpos & 0xff))
lpos++;
if (*lpos == '\0')
return Empty();
const char * rpos = theArray+GetLength()-1;
if (!isspace(*rpos & 0xff))
return PString(lpos);
while (isspace(*rpos & 0xff))
rpos--;
return PString(lpos, rpos - lpos + 1);
}
PString PString::ToLower() const
{
PString newStr(theArray);
for (char *cpos = newStr.theArray; *cpos != '\0'; cpos++) {
if (isupper(*cpos & 0xff))
*cpos = (char)tolower(*cpos & 0xff);
}
return newStr;
}
PString PString::ToUpper() const
{
PString newStr(theArray);
for (char *cpos = newStr.theArray; *cpos != '\0'; cpos++) {
if (islower(*cpos & 0xff))
*cpos = (char)toupper(*cpos & 0xff);
}
return newStr;
}
long PString::AsInteger(unsigned base) const
{
PAssert(base >= 2 && base <= 36, PInvalidParameter);
char * dummy;
return strtol(theArray, &dummy, base);
}
DWORD PString::AsUnsigned(unsigned base) const
{
PAssert(base >= 2 && base <= 36, PInvalidParameter);
char * dummy;
return strtoul(theArray, &dummy, base);
}
double PString::AsReal() const
{
#ifndef __HAS_NO_FLOAT
char * dummy;
return strtod(theArray, &dummy);
#else
return 0.0;
#endif
}
PWORDArray PString::AsUCS2() const
{
#ifdef P_HAS_G_CONVERT
gsize g_len = 0;
gchar * g_ucs2 = g_convert(theArray, GetSize()-1, "UCS-2", "UTF-8", 0, &g_len, 0);
if (g_ucs2 == NULL)
return PWORDArray();
PWORDArray ucs2((const WORD *)g_ucs2, (PINDEX)g_len);
g_free(g_ucs2)
return ucs2;
#else
PWORDArray ucs2(GetSize()); // Always bigger than required
PINDEX count = 0;
PINDEX i = 0;
PINDEX length = GetSize()-1;
while (i < length) {
int c = theArray[i];
if ((c&0x80) == 0)
ucs2[count++] = (BYTE)theArray[i++];
else if ((c&0xe0) == 0xc0) {
if (i < length-1)
ucs2[count++] = (WORD)(((theArray[i ]&0x1f)<<6)|
(theArray[i+1]&0x3f));
i += 2;
}
else if ((c&0xf0) == 0xe0) {
if (i < length-2)
ucs2[count++] = (WORD)(((theArray[i ]&0x0f)<<12)|
((theArray[i+1]&0x3f)<< 6)|
(theArray[i+2]&0x3f));
i += 3;
}
else {
if ((c&0xf8) == 0xf0)
i += 4;
else if ((c&0xfc) == 0xf8)
i += 5;
else
i += 6;
if (i <= length)
ucs2[count++] = 0xffff;
}
}
ucs2.SetSize(count);
return ucs2;
#endif
}
void PString::InternalFromUCS2(const WORD * ptr, PINDEX len)
{
if (ptr == NULL || len <= 0) {
*this = Empty();
return;
}
#ifdef P_HAS_G_CONVERT
gsize g_len = 0;
gchar * g_utf8 = g_convert(ptr, len, "UTF-8", "UCS-2", 0, &g_len, 0);
if (g_utf8 == NULL) {
*this = Empty();
return;
}
SetSize(&g_len);
memcpy(theArray, g_char, g_len);
g_free(g_utf8);
#else
PINDEX i;
PINDEX count = 1;
for (i = 0; i < len; i++) {
if (ptr[i] < 0x80)
count++;
else if (ptr[i] < 0x800)
count += 2;
else
count += 3;
}
SetSize(count);
count = 0;
for (i = 0; i < len; i++) {
unsigned v = *ptr++;
if (v < 0x80)
theArray[count++] = (char)v;
else if (v < 0x800) {
theArray[count++] = (char)(0xc0+(v>>6));
theArray[count++] = (char)(0x80+(v&0x3f));
}
else {
theArray[count++] = (char)(0xd0+(v>>12));
theArray[count++] = (char)(0x80+((v>>6)&0x3f));
theArray[count++] = (char)(0x80+(v&0x3f));
}
}
#endif
}
PBYTEArray PString::ToPascal() const
{
PINDEX len = GetLength();
PAssert(len < 256, "Cannot convert to PASCAL string");
BYTE buf[256];
buf[0] = (BYTE)len;
memcpy(&buf[1], theArray, len);
return PBYTEArray(buf, len+1);
}
PString PString::ToLiteral() const
{
PString str('"');
for (char * p = theArray; *p != '\0'; p++) {
if (*p == '"')
str += "\\\"";
else if (isprint(*p & 0xff))
str += *p;
else {
PINDEX i;
for (i = 0; i < PARRAYSIZE(PStringEscapeValue); i++) {
if (*p == PStringEscapeValue[i]) {
str += PString('\\') + (char)PStringEscapeCode[i];
break;
}
}
if (i >= PARRAYSIZE(PStringEscapeValue))
str.sprintf("\\%03o", *p & 0xff);
}
}
return str + '"';
}
PString & PString::sprintf(const char * fmt, ...)
{
va_list args;
va_start(args, fmt);
return vsprintf(fmt, args);
}
#if defined(__GNUC__) || defined(__SUNPRO_CC)
#define _vsnprintf vsnprintf
#endif
PString & PString::vsprintf(const char * fmt, va_list arg)
{
PINDEX len = theArray != NULL ? GetLength() : 0;
#ifdef P_VXWORKS
// The library provided with tornado 2.0 does not have the implementation
// for vsnprintf
// as workaround, just use a array size of 2000
PAssert(SetSize(2000), POutOfMemory);
::vsprintf(theArray+len, fmt, arg);
#else
PINDEX size = 0;
do {
size += 1000;
PAssert(SetSize(size), POutOfMemory);
} while (_vsnprintf(theArray+len, size-len, fmt, arg) == -1);
#endif // P_VXWORKS
PAssert(MakeMinimumSize(), POutOfMemory);
return *this;
}
PString psprintf(const char * fmt, ...)
{
PString str;
va_list args;
va_start(args, fmt);
return str.vsprintf(fmt, args);
}
PString pvsprintf(const char * fmt, va_list arg)
{
PString str;
return str.vsprintf(fmt, arg);
}
///////////////////////////////////////////////////////////////////////////////
PObject * PCaselessString::Clone() const
{
return new PCaselessString(*this);
}
PObject::Comparison PCaselessString::InternalCompare(PINDEX offset, char c) const
{
if (offset < 0)
return LessThan;
int c1 = toupper(theArray[offset] & 0xff);
int c2 = toupper(c & 0xff);
if (c1 < c2)
return LessThan;
if (c1 > c2)
return GreaterThan;
return EqualTo;
}
PObject::Comparison PCaselessString::InternalCompare(
PINDEX offset, PINDEX length, const char * cstr) const
{
if (offset < 0 || length < 0)
return LessThan;
if (cstr == NULL)
return IsEmpty() ? EqualTo : LessThan;
while (length-- > 0 && (theArray[offset] != '\0' || *cstr != '\0')) {
Comparison c = PCaselessString::InternalCompare(offset++, *cstr++);
if (c != EqualTo)
return c;
}
return EqualTo;
}
///////////////////////////////////////////////////////////////////////////////
PStringStream::Buffer::Buffer(PStringStream & str, PINDEX size)
: string(str),
fixedBufferSize(size != 0)
{
string.SetMinSize(size > 0 ? size : 256);
sync();
}
int PStringStream::Buffer::overflow(int c)
{
if (pptr() >= epptr()) {
if (fixedBufferSize)
return EOF;
int gpos = gptr() - eback();
int ppos = pptr() - pbase();
char * newptr = string.GetPointer(string.GetSize() + 32);
setp(newptr, newptr + string.GetSize() - 1);
pbump(ppos);
setg(newptr, newptr + gpos, newptr + ppos);
}
if (c != EOF) {
*pptr() = (char)c;
pbump(1);
}
return 0;
}
int PStringStream::Buffer::underflow()
{
return gptr() >= egptr() ? EOF : *gptr();
}
int PStringStream::Buffer::sync()
{
char * base = string.GetPointer();
PINDEX len = string.GetLength();
setg(base, base, base + len);
setp(base, base + string.GetSize() - 1);
pbump(len);
return 0;
}
#ifdef __USE_STL__
streambuf::pos_type PStringStream::Buffer::seekoff(off_type off, ios_base::seekdir dir, ios_base::openmode mode)
#else
streampos PStringStream::Buffer::seekoff(streamoff off, ios::seek_dir dir, int mode)
#endif
{
int len = string.GetLength();
int gpos = gptr() - eback();
int ppos = pptr() - pbase();
char * newgptr;
char * newpptr;
switch (dir) {
case ios::beg :
if (off < 0)
newpptr = newgptr = eback();
else if (off >= len)
newpptr = newgptr = egptr();
else
newpptr = newgptr = eback()+off;
break;
case ios::cur :
if (off < -ppos)
newpptr = eback();
else if (off >= len-ppos)
newpptr = epptr();
else
newpptr = pptr()+off;
if (off < -gpos)
newgptr = eback();
else if (off >= len-gpos)
newgptr = egptr();
else
newgptr = gptr()+off;
break;
case ios::end :
if (off < -len)
newpptr = newgptr = eback();
else if (off >= 0)
newpptr = newgptr = egptr();
else
newpptr = newgptr = egptr()+off;
break;
default:
PAssertAlways2(string.GetClass(), PInvalidParameter);
newgptr = gptr();
newpptr = pptr();
}
if ((mode&ios::in) != 0)
setg(eback(), newgptr, egptr());
if ((mode&ios::out) != 0)
setp(newpptr, epptr());
return 0;
}
#ifdef __USE_STL__
streampos PStringStream::Buffer::seekpos(pos_type pos, ios_base::openmode mode)
{
return seekoff(pos, ios_base::beg, mode);
}
#endif
#ifdef _MSC_VER
#pragma warning(disable:4355)
#endif
PStringStream::PStringStream()
: iostream(new PStringStream::Buffer(*this, 0))
{
}
PStringStream::PStringStream(PINDEX fixedBufferSize)
: iostream(new PStringStream::Buffer(*this, fixedBufferSize))
{
}
PStringStream::PStringStream(const PString & str)
: PString(str),
iostream(new PStringStream::Buffer(*this, 0))
{
}
PStringStream::PStringStream(const char * cstr)
: PString(cstr),
iostream(new PStringStream::Buffer(*this, 0))
{
}
#ifdef _MSC_VER
#pragma warning(default:4355)
#endif
PStringStream::~PStringStream()
{
delete (PStringStream::Buffer *)rdbuf();
#ifndef _WIN32
init(NULL);
#endif
}
PString & PStringStream::MakeEmpty()
{
*theArray = '\0';
flush();
return *this;
}
void PStringStream::AssignContents(const PContainer & cont)
{
PString::AssignContents(cont);
flush();
}
///////////////////////////////////////////////////////////////////////////////
PStringArray::PStringArray(PINDEX count, char const * const * strarr, BOOL caseless)
{
if (count == 0)
return;
if (PAssertNULL(strarr) == NULL)
return;
if (count == P_MAX_INDEX) {
count = 0;
while (strarr[count] != NULL)
count++;
}
SetSize(count);
for (PINDEX i = 0; i < count; i++) {
PString * newString;
if (caseless)
newString = new PCaselessString(strarr[i]);
else
newString = new PString(strarr[i]);
SetAt(i, newString);
}
}
PStringArray::PStringArray(const PString & str)
{
SetSize(1);
(*theArray)[0] = new PString(str);
}
PStringArray::PStringArray(const PStringList & list)
{
SetSize(list.GetSize());
for (PINDEX i = 0; i < list.GetSize(); i++)
(*theArray)[i] = new PString(list[i]);
}
PStringArray::PStringArray(const PSortedStringList & list)
{
SetSize(list.GetSize());
for (PINDEX i = 0; i < list.GetSize(); i++)
(*theArray)[i] = new PString(list[i]);
}
void PStringArray::ReadFrom(istream & strm)
{
while (strm.good()) {
PString str;
strm >> str;
AppendString(str);
}
}
PString PStringArray::operator[](PINDEX index) const
{
PASSERTINDEX(index);
if (index < GetSize() && (*theArray)[index] != NULL)
return *(PString *)(*theArray)[index];
return PString::Empty();
}
PString & PStringArray::operator[](PINDEX index)
{
PASSERTINDEX(index);
PAssert(SetMinSize(index+1), POutOfMemory);
if ((*theArray)[index] == NULL)
(*theArray)[index] = new PString;
return *(PString *)(*theArray)[index];
}
char ** PStringArray::ToCharArray(PCharArray * storage) const
{
PINDEX i;
PINDEX mySize = GetSize();
PINDEX storageSize = (mySize+1)*sizeof(char *);
for (i = 0; i < mySize; i++)
storageSize += (*this)[i].GetLength()+1;
char ** storagePtr;
if (storage != NULL)
storagePtr = (char **)storage->GetPointer(storageSize);
else
storagePtr = (char **)malloc(storageSize);
if (storagePtr == NULL)
return NULL;
char * strPtr = (char *)&storagePtr[GetSize()+1];
for (i = 0; i < mySize; i++) {
storagePtr[i] = strPtr;
const PString & str = (*this)[i];
PINDEX len = str.GetLength()+1;
memcpy(strPtr, (const char *)str, len);
strPtr += len;
}
storagePtr[i] = NULL;
return storagePtr;
}
///////////////////////////////////////////////////////////////////////////////
PStringList::PStringList(PINDEX count, char const * const * strarr, BOOL caseless)
{
if (count == 0)
return;
if (PAssertNULL(strarr) == NULL)
return;
for (PINDEX i = 0; i < count; i++) {
PString * newString;
if (caseless)
newString = new PCaselessString(strarr[i]);
else
newString = new PString(strarr[i]);
Append(newString);
}
}
PStringList::PStringList(const PString & str)
{
AppendString(str);
}
PStringList::PStringList(const PStringArray & array)
{
for (PINDEX i = 0; i < array.GetSize(); i++)
AppendString(array[i]);
}
PStringList::PStringList(const PSortedStringList & list)
{
for (PINDEX i = 0; i < list.GetSize(); i++)
AppendString(list[i]);
}
PStringList & PStringList::operator += (const PStringList & v)
{
PINDEX i;
for (i = 0; i < v.GetSize(); i++)
AppendString(v[i]);
return *this;
}
void PStringList::ReadFrom(istream & strm)
{
while (strm.good()) {
PString str;
strm >> str;
AppendString(str);
}
}
///////////////////////////////////////////////////////////////////////////////
PSortedStringList::PSortedStringList(PINDEX count,
char const * const * strarr,
BOOL caseless)
{
if (count == 0)
return;
if (PAssertNULL(strarr) == NULL)
return;
for (PINDEX i = 0; i < count; i++) {
PString * newString;
if (caseless)
newString = new PCaselessString(strarr[i]);
else
newString = new PString(strarr[i]);
Append(newString);
}
}
PSortedStringList::PSortedStringList(const PString & str)
{
AppendString(str);
}
PSortedStringList::PSortedStringList(const PStringArray & array)
{
for (PINDEX i = 0; i < array.GetSize(); i++)
AppendString(array[i]);
}
PSortedStringList::PSortedStringList(const PStringList & list)
{
for (PINDEX i = 0; i < list.GetSize(); i++)
AppendString(list[i]);
}
void PSortedStringList::ReadFrom(istream & strm)
{
while (strm.good()) {
PString str;
strm >> str;
AppendString(str);
}
}
PINDEX PSortedStringList::GetNextStringsIndex(const PString & str) const
{
PINDEX len = str.GetLength();
info->lastIndex = InternalStringSelect(str, len, info->root);
if (info->lastIndex != 0) {
Element * prev;
while ((prev = info->Predecessor(info->lastElement)) != &info->nil &&
((PString *)prev->data)->NumCompare(str, len) >= EqualTo) {
info->lastElement = prev;
info->lastIndex--;
}
}
return info->lastIndex;
}
PINDEX PSortedStringList::InternalStringSelect(const char * str,
PINDEX len,
Element * thisElement) const
{
if (thisElement == &info->nil)
return 0;
switch (((PString *)thisElement->data)->NumCompare(str, len)) {
case PObject::LessThan :
{
PINDEX index = InternalStringSelect(str, len, thisElement->right);
return thisElement->left->subTreeSize + index + 1;
}
case PObject::GreaterThan :
return InternalStringSelect(str, len, thisElement->left);
default :
info->lastElement = thisElement;
return thisElement->left->subTreeSize;
}
}
///////////////////////////////////////////////////////////////////////////////
PStringSet::PStringSet(PINDEX count, char const * const * strarr, BOOL caseless)
{
if (count == 0)
return;
if (PAssertNULL(strarr) == NULL)
return;
for (PINDEX i = 0; i < count; i++) {
if (caseless)
Include(PCaselessString(strarr[i]));
else
Include(PString(strarr[i]));
}
}
PStringSet::PStringSet(const PString & str)
{
Include(str);
}
void PStringSet::ReadFrom(istream & strm)
{
while (strm.good()) {
PString str;
strm >> str;
Include(str);
}
}
///////////////////////////////////////////////////////////////////////////////
POrdinalToString::POrdinalToString(PINDEX count, const Initialiser * init)
{
while (count-- > 0) {
SetAt(init->key, init->value);
init++;
}
}
void POrdinalToString::ReadFrom(istream & strm)
{
while (strm.good()) {
POrdinalKey key;
char equal;
PString str;
strm >> key >> ws >> equal >> str;
if (equal != '=')
SetAt(key, PString::Empty());
else
SetAt(key, str.Mid(equal+1));
}
}
///////////////////////////////////////////////////////////////////////////////
PStringToOrdinal::PStringToOrdinal(PINDEX count,
const Initialiser * init,
BOOL caseless)
{
while (count-- > 0) {
if (caseless)
SetAt(PCaselessString(init->key), init->value);
else
SetAt(init->key, init->value);
init++;
}
}
void PStringToOrdinal::ReadFrom(istream & strm)
{
while (strm.good()) {
PString str;
strm >> str;
PINDEX equal = str.FindLast('=');
if (equal == P_MAX_INDEX)
SetAt(str, 0);
else
SetAt(str.Left(equal), str.Mid(equal+1).AsInteger());
}
}
///////////////////////////////////////////////////////////////////////////////
PStringToString::PStringToString(PINDEX count,
const Initialiser * init,
BOOL caselessKeys,
BOOL caselessValues)
{
while (count-- > 0) {
if (caselessValues)
if (caselessKeys)
SetAt(PCaselessString(init->key), PCaselessString(init->value));
else
SetAt(init->key, PCaselessString(init->value));
else
if (caselessKeys)
SetAt(PCaselessString(init->key), init->value);
else
SetAt(init->key, init->value);
init++;
}
}
void PStringToString::ReadFrom(istream & strm)
{
while (strm.good()) {
PString str;
strm >> str;
PINDEX equal = str.Find('=');
if (equal == P_MAX_INDEX)
SetAt(str, PString::Empty());
else
SetAt(str.Left(equal), str.Mid(equal+1));
}
}
///////////////////////////////////////////////////////////////////////////////
PRegularExpression::PRegularExpression()
{
lastError = NotCompiled;
expression = NULL;
flagsSaved = IgnoreCase;
}
PRegularExpression::PRegularExpression(const PString & pattern, int flags)
{
expression = NULL;
Compile(pattern, flags);
}
PRegularExpression::PRegularExpression(const char * pattern, int flags)
{
expression = NULL;
Compile(pattern, flags);
}
PRegularExpression::PRegularExpression(const PRegularExpression & from)
{
expression = NULL;
patternSaved = from.patternSaved;
flagsSaved = from.flagsSaved;
Compile(patternSaved, flagsSaved);
}
PRegularExpression & PRegularExpression::operator =(const PRegularExpression & from)
{
expression = NULL;
patternSaved = from.patternSaved;
flagsSaved = from.flagsSaved;
Compile(patternSaved, flagsSaved);
return *this;
}
PRegularExpression::~PRegularExpression()
{
if (expression != NULL) {
regfree(regexpression);
delete regexpression;
}
}
PRegularExpression::ErrorCodes PRegularExpression::GetErrorCode() const
{
return (ErrorCodes)lastError;
}
PString PRegularExpression::GetErrorText() const
{
PString str;
regerror(lastError, regexpression, str.GetPointer(256), 256);
return str;
}
BOOL PRegularExpression::Compile(const PString & pattern, int flags)
{
return Compile((const char *)pattern, flags);
}
BOOL PRegularExpression::Compile(const char * pattern, int flags)
{
patternSaved = pattern;
flagsSaved = flags;
if (expression != NULL) {
regfree(regexpression);
delete regexpression;
expression = NULL;
}
if (pattern == NULL || *pattern == '\0')
lastError = BadPattern;
else {
expression = new regex_t;
lastError = regcomp(regexpression, pattern, flags);
}
return lastError == NoError;
}
BOOL PRegularExpression::Execute(const PString & str, PINDEX & start, int flags) const
{
PINDEX dummy;
return Execute((const char *)str, start, dummy, flags);
}
BOOL PRegularExpression::Execute(const PString & str, PINDEX & start, PINDEX & len, int flags) const
{
return Execute((const char *)str, start, len, flags);
}
BOOL PRegularExpression::Execute(const char * cstr, PINDEX & start, int flags) const
{
PINDEX dummy;
return Execute(cstr, start, dummy, flags);
}
BOOL PRegularExpression::Execute(const char * cstr, PINDEX & start, PINDEX & len, int flags) const
{
if (expression == NULL) {
((PRegularExpression*)this)->lastError = NotCompiled;
return FALSE;
}
regmatch_t match;
((PRegularExpression*)this)->lastError = regexec(regexpression, cstr, 1, &match, flags);
if (lastError != NoError)
return FALSE;
start = match.rm_so;
len = match.rm_eo - start;
return TRUE;
}
BOOL PRegularExpression::Execute(const PString & str, PIntArray & starts, int flags) const
{
PIntArray dummy;
return Execute((const char *)str, starts, dummy, flags);
}
BOOL PRegularExpression::Execute(const PString & str,
PIntArray & starts,
PIntArray & ends,
int flags) const
{
return Execute((const char *)str, starts, ends, flags);
}
BOOL PRegularExpression::Execute(const char * cstr, PIntArray & starts, int flags) const
{
PIntArray dummy;
return Execute(cstr, starts, dummy, flags);
}
BOOL PRegularExpression::Execute(const char * cstr,
PIntArray & starts,
PIntArray & ends,
int flags) const
{
if (expression == NULL) {
((PRegularExpression*)this)->lastError = NotCompiled;
return FALSE;
}
regmatch_t single_match;
regmatch_t * matches = &single_match;
PINDEX count = starts.GetSize();
if (count > 1)
matches = new regmatch_t[count];
else
count = 1;
((PRegularExpression*)this)->lastError = regexec(regexpression, cstr, count, matches, flags);
if (lastError == NoError) {
starts.SetMinSize(count);
ends.SetMinSize(count);
for (PINDEX i = 0; i < count; i++) {
starts[i] = matches[i].rm_so;
ends[i] = matches[i].rm_eo;
}
}
if (matches != &single_match)
delete [] matches;
return lastError == NoError;
}
PString PRegularExpression::EscapeString(const PString & str)
{
PString translated;
PINDEX lastPos = 0;
PINDEX nextPos;
while ((nextPos = str.FindOneOf("\\^$+?*.[]()|{}", lastPos+1)) != P_MAX_INDEX) {
translated += str(lastPos, nextPos-1) + "\\";
lastPos = nextPos;
}
if (lastPos == 0)
return str;
return translated + str.Mid(lastPos);
}
// End Of File ///////////////////////////////////////////////////////////////
syntax highlighted by Code2HTML, v. 0.9.1