@c NcDialog API User's Guide - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.0 gString Text Tool  - - - - - - - - - - - - - - - - - - - - - - -
@node gString Text Tool
@unnumbered gString Text Tool
@GSChIndx{00} gString Text Tool
@c @cindex 07 gString Text Tool
@cindex gString text conversion
@cindex text conversion, gString

'gString' is a small, fast and flexible way to seamlessly convert, format and 
analyze both UTF-8 and wchar_t ('wide') text.

@menu
* Introduction to gString::   An overview of gString-class functionality
* gString Public Methods::    A list of all public methods
* gString Instantiation::     How to create a new gString-class object
* Assignment Operators::      Overloaded assignment operators
* Formatted Assignments::     Data formatting (like swprintf, but better)
* Integer Formatting::        Formatting and sizing integer display
* Data Access::               Referencing the 'const' data of gString
* Copying Data::              Exporting gString contents
* Modifying Existing Data::   Add to, subtract from, shift or replace data
* Comparisons::               Compare gString contents to other data
* Extract Formatted Data::    Scan data and extract formatted info (swscanf)
* Statistical Info::          Report statistics on gString contents
* gString Miscellaneous::     Get class version, reinitialize data
* gString Examples::          Examples of using gString methods
* gsmeter Test App::          Testing gString functionality
@end menu

@lowersections

@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.01 Introduction to gString - - - - - - - - - - - - - - - - - - - -
@c               An overview of gString-class functionality  - - - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node Introduction to gString
@unnumbered Introduction to gString
@GSChIndx{01} Introduction to gString
@c @cindex 07.01 Introduction to gString

@Subheading{Introduction to a Wider World,-----------------------------}

Modern applications must be designed for a worldwide audience, and for this 
reason, the application designer must plan for multi-language support.

Fortunately, the Universal Character Set standard ISO/IEC 10646 and UTF-8, the 
most popular and flexible method of character-encoding smoothly provide all the 
character sets, alphabets and special characters which are currently in general 
use.

Unfortunately, the C and C++ languages offer only minimal support for 
internationalization. std::string and std::wstring are nothing more than a 
cruel joke to a serious application designer. The GTK+ toolkit's Glib::ustring 
class is an excellent internationalization tool, but it requires installation 
of the GTK+ toolkit's 'glib' and 'glibmm' libraries.@*
For more information on Glib::ustring, see: @w{https://developer.gnome.org/}

'gString' falls halfway between the full feature set of Glib::ustring and the 
meaningless garbage that is std::string. 'gString' consists of one C++ header 
file and one C++ source code module. 'gString' is integrated into the NcDialog 
API library, but may also be compiled independently as a small (16Kb) link 
library or the source may be embedded directly into your application.

@Subheading{Preparing to Support Multiple Languages,---------------------------------------}

Here are the basic ideas you will need to understand in order for your 
application to smoothly support multiple languages.

@enumerate 1
@item
ASCII (American Standard Code for Information Interchange) is only the very 
first step in character encoding. It is an ancient and venerable encoding, 
but supports only the 95 printable characters of the basic Latin alphabet.@*
If you think you can say "你是在欺骗自己！" in ASCII, you're just deluding 
yourself!

@item
NEVER assume that one character can be represented with one byte.

@item
NEVER assume that one character is one display column in width.

@item
The idea that text flows from left-to-right, may only be PROVISIONALLY 
assumed, because again: 
@ifnothtml
"!איר זענט נאָר דילוזינג זיך"
@end ifnothtml
@ifhtml
@html
<span>"!איר זענט נאָר דילודינג זיך"</span>
@end html
@end ifhtml
 you're just deluding yourself.

@item
NEVER assume that "everyone reads English, so why bother?". Native 
speakers of Spanish, French, Chinese, the various flavors of 
Arabic and others (i.e. your potential customers) all have a 
significant impact on the daily events of our planet, so include 
them when planning for your next killer app.

@end enumerate

See also a discussion of multiple-language support in the NcDialog API.@*
@ifclear GSTRING_STANDALONE
@pxref{Multi-language Support}.
@end ifclear
@sp 2


@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.02 gString Public Methods  - - - - - - - - - - - - - - - - - - - -
@c               A list of all public methods  - - - - - - - - - - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node gString Public Methods
@unnumbered gString Public Methods
@GSChIndx{02} gString Public Methods
@c @cindex 07.02 gString Public Methods
@cindex methods, gString
@cindex gString methods

What follows is a list of all public methods of the gString class.@*
Methods are arranged in functional groups.

@c @w{123456789-123456789-123456789-123456789-123456789-123456789-123456789-12345678|}
@multitable {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} 
@headitem @ @ @ @ @ @ @ @ gString Method Name @ @ @ @ @ @ @ @ 
@tab @ @ @ Chapter Reference @ @ @

@item @ gString   [constructor]     @tab @ @pxref{gString Instantiation}
@item @ ~gString  [destructor]      @tab @ 
@item @ operator=                   @tab @ @pxref{Assignment Operators}
@item @                             @tab @ 
@item @ compose                     @tab @ @pxref{Formatted Assignments}
@item @                             @tab @ 
@item @ formatInt                   @tab @ @pxref{Integer Formatting}
@item @                             @tab @ 
@item @ gstr                        @tab @ @pxref{Data Access}
@item @ ustr                        @tab @ 
@item @                             @tab @ 
@item @ copy                        @tab @ @pxref{Copying Data}
@item @ operator<<                  @tab @ 
@item @ substr                      @tab @ 
@item @                             @tab @ 
@item @ append                      @tab @ @pxref{Modifying Existing Data}
@item @ insert                      @tab @ 
@item @ limitChars                  @tab @ 
@item @ limitCols                   @tab @ 
@item @ shiftChars                  @tab @ 
@item @ shiftCols                   @tab @ 
@item @ padCols                     @tab @ 
@item @ strip                       @tab @ 
@item @ erase                       @tab @ 
@item @ replace                     @tab @ 
@item @ loadChars                   @tab @ 
@item @ textReverse                 @tab @ 
@item @ formatParagraph             @tab @ 
@item @                             @tab @ 
@item @ compare                     @tab @ @pxref{Comparisons}
@item @ compcoll                    @tab @ 
@item @ operator==                  @tab @ 
@item @ operator!=                  @tab @ 
@item @ find                        @tab @ 
@item @ findlast                    @tab @ 
@item @ after                       @tab @ 
@item @ findr                       @tab @ 
@item @ findx                       @tab @ 
@item @ scan                        @tab @ 
@item @                             @tab @ 
@item @ gscanf                      @tab @ @pxref{Extract Formatted Data}
@item @                             @tab @ 
@item @ gschars                     @tab @ @pxref{Statistical Info}
@item @ gscols                      @tab @ 
@item @ utfbytes                    @tab @ 
@item @ isASCII                     @tab @ 
@item @                             @tab @ 
@item @ clear                       @tab @ @pxref{gString Miscellaneous}
@item @ wAlloc                      @tab @ 
@item @ uAlloc                      @tab @ 
@item @ freeSpace                   @tab @ 
@item @ reAlloc                     @tab @ 
@item @ Get_gString_Version         @tab @ 
@item @ dumpGstring                 @tab @ 
@item @ dbMsg                       @tab @ 
@end multitable
@sp 2


@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.03 gString Instantiation   - - - - - - - - - - - - - - - - - - - -
@c               How to create a new gString-slass object  - - - - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node gString Instantiation
@unnumbered gString Instantiation
@GSChIndx{03} gString Instantiation
@c @cindex 07.03 gString Instantiation

The following are the 'constructors' for the gString class. 

For those new to C++, a constructor creates an 'instance' of the class. 
An instance is a particular, named object, and can be thought of as a 
complex variable.

@itemize @MSQUARE
@item @b{gString ( void ) ;}

@verbatim
  Input  :
     none
  Returns:
     nothing
@end verbatim

Constructor: Initialize members to default values (NULL string).
@spi

@item @b{gString ( int32_t charAlloc );}

@verbatim
  Input  :
     charAlloc : storage capacity
        interpreted as the maximum number of wchar_t (32-bit) characters 
        for the storage buffer.
        Range: gsALLOCMIN <= charAlloc <= gsALLOCMAX
        -- The minimum allocation of gsALLOCDMIN will result in the
           same storage capacity at the default constructor.
        -- The maximum allocation of gsALLOCMAX is 1000 times the
           minimum allocation.
        The specified value is normalized by rounding upward to the
        nearest multiple of gsALLOCMIN.

  Returns: nothing
@end verbatim

Constructor: Initialize members to default values with empty string.

This constructor is used to specify the initial storage capacity for the 
gString object. This may be useful if it is known that the data the 
object will hold will be larger than the default storage capacity.@*
For more information, @pxref{Dynamic Memory Allocation}.
@spi

@item @b{gString ( const char* usrc, int32_t charLimit = -1 ) ;}

@verbatim
  Input  :
     usrc     : pointer to a UTF-8-encoded, null-terminated string
     charLimit: (optional, -1 by default) maximum number of source
                characters to be stored (not including nullchar).
                This is the number of characters (NOT the number of
                UTF-8 bytes).
                If not specified, then all source data will be stored
                up to the maximum storage limit.

  Returns: nothing
@end verbatim

Constructor: Convert specified UTF-8-encoded source to gString.
@spi

@item @b{gString ( const wchar_t* wsrc, int32_t charLimit = -1 ) ;}

@verbatim
  Input  :
     wsrc      : pointer to a wchar_t-encoded, null-terminated string
     charLimit : (optional, -1 by default)
                 maximum number of characters from source array to 
                 convert

  Returns: nothing
@end verbatim

Constructor: Convert specified wchar_t ('wide') source to gString.
@spi

@item @b{gString ( short iVal, short fWidth, bool lJust = false,@* 
@w{@t{       }   bool sign = false, bool kibi = false, fiUnits units = fiK ) ;}}@*
@item @b{gString ( unsigned short iVal, short fWidth, bool lJust = false,@* 
@w{@t{       }   bool sign = false, bool kibi = false, fiUnits units = fiK ) ;}}@*
@item @b{gString ( int iVal, short fWidth, bool lJust = false,@* 
@w{@t{       }   bool sign = false, bool kibi = false, fiUnits units = fiK ) ;}}@*
@item @b{gString ( unsigned int iVal, short fWidth, bool lJust = false,@* 
@w{@t{       }   bool sign = false, bool kibi = false, fiUnits units = fiK ) ;}}@*
@item @b{gString ( long int iVal, short fWidth, bool lJust = false,@* 
@w{@t{       }   bool sign = false, bool kibi = false, fiUnits units = fiK ) ;}}@*
@item @b{gString ( unsigned long int iVal, short fWidth, bool lJust = false,@* 
@w{@t{       }   bool sign = false, bool kibi = false, fiUnits units = fiK ) ;}}@*
@item @b{gString ( long long int iVal, short fWidth, bool lJust = false,@* 
@w{@t{       }   bool sign = false, bool kibi = false, fiUnits units = fiK ) ;}}@*
@item @b{gString ( unsigned long long int iVal, short fWidth, bool lJust = false,@* 
@w{@t{       }   bool sign = false, bool kibi = false, fiUnits units = fiK ) ;}}@*

@verbatim
  Input  :
     iVal  : value to be converted
             Supported value range: plus/minus 9.999999999999 terabytes
     fWidth: field width (number of display columns)
             range: 1 to FI_MAX_FIELDWIDTH
     lJust : (optional, false by default)
             if true, strip leading spaces to left-justify the value
             in the field. (resulting string may be less than fWidth)
     sign  : (optional, false by default)
             'false' : only negative values are signed
             'true'  : always prepend a '+' or '-' sign.
     kibi  : (optional, false by default)
             'false' : calculate as a decimal value (powers of 10)
                       kilobyte, megabyte, gigabyte, terabyte
             'true'  : calculate as a binary value (powers of 2)
                       kibibyte, mebibyte, gibibyte, tebibyte
     units  : (optional) member of enum fiUnits (fiK by default)
              specifies the format for the units suffix.
              Note that if the uncompressed value fits in the field,
              then this parameter is ignored.

  Returns: nothing
     Note: if field overflow, field will be filled with '#' characters.
@end verbatim

Constructor: Convert specified integer value to gString.@*
Please @pxref{Integer Formatting} @verb{|'formatInt'|} method group 
for formatting details.
@spi

@item @b{gString ( const char* fmt, const void* arg1, ... )@* 
@w{@t{       }   __attribute__ ((format (gnu_printf, 2, 0))) ;}}

@verbatim
  Input  :
     fmt       : a format specification string in the style of 
                 sprintf(), swprintf() and related formatting 
                 C/C++ functions.
     arg1      : pointer to first value to be converted by 'fmt'
     ...       : optional arguments (between ZERO and gsfMAXARGS - 1)
                 Each optional argument is a POINTER (address of) the 
                 value to be formatted.
  Returns:
     nothing
@end verbatim

Constructor: Convert formatting specification and its arguments to gString.
Please refer to the compose() method:@*
(@pxref{Formatted Assignments}) for more information.

@b{Technical Note:} There is no constructor using a @LDQ{}@t{const wchar_t* fmt}@RDQ{} 
format specification because it would conflict with the constructor which 
limits the number of characters used to initialize the instance.
@spi

@item @b{~gString ( void ) ;}

@verbatim
  Input  :
     none
  Returns:
     nothing
@end verbatim

Destructor: Release all resources associated with the gString object.

Object is destroyed either when it goes out of scope, or by explicitly 
deleting the object.

For those new to C++, please note that if you use the 'new' keyword to 
create objects, then those objects persist (take up space) until you 
explicitly delete them or until the application is closed, even if the 
pointer to the object has gone out-of-scope. See examples below.
@c @spi

@Subsubheading{Examples,........}
@verbatim
void calling_method ( void )
{
   gString *gsPtr = playful_kitten ( "Hello World!" ) ;

   // report contents of object created by called method
   wcout << gsPtr->gstr() << endl ;
   delete gsPtr ; // delete object created by called method
}

gString* playful_kitten ( const char* msg )
{
   gString gs_local( "I love tuna!" ) ;  // local object
   gString *gsPtr1 = new gString,        // global object
           *gsPtr2 = new gString(msg) ;  // global object (initialized)
   gString *gsArray = new gString[4] ;   // global array

   *gsPtr1 = gs_local ;   // be a kitten: play with the strings...
   gsArray[2] = *gsPtr2 ;
   gsArray[3] = "Scratch my belly!" ;
   gsArray[1] = gsArray[3] ;

   delete gsPtr1 ;      // delete object referenced by gsPtr1
   delete [] gsArray ;  // delete object array referenced by gsArray
   return gsPtr2 ;      // return pointer to object referenced by gsPtr2
                        // (caller is responsible for deleting object)
}           // 'gs_local' goes out of scope and is destroyed here
@end verbatim
@end itemize
@hr90

@cindex ram memory, gString
@cindex dynamic alloc. gString
@cindex memory, alloc. gString
@cindex gString memory alloc.
@anchor{Dynamic Memory Allocation}
@Subsubheading{Dynamic Memory Allocation,
               .........................}

@itemize @SDIAMOND
@item During instantiation of a gString object, a dynamic memory 
allocation occurs; that is, memory is allocated from the @LDQ{}heap@RDQ{} 
which is RAM memory not directly controlled by the application.

This is in contrast to the @LDQ{}stack@RDQ{} memory which is assigned 
to the application on startup.

@item The dynamic allocation is initially the default value: gsALLOCDFLT. 
This is the number of UTF-32 (wchar_t) characters the object can hold.

Storage capacity is automatically expanded as necessary to accomodate 
the size of the data assigned to the object.

@item The exception to the default allocation is the gString constructor 
which takes as it argument a specific storage capacity, expressed as the 
number of UTF-32 (wchar_t) characters. Examples:@*
@verb{|   gString gs( gsALLOCMED );   gString gs( 2500 );|}@*
This constructor is described above.

@item The DEFAULT storage capacity is the minimum capacity:@*
@verb{|      (gsALLOCDFLT == gsALLOCMIN)|}@*
Total dynamic allocation for the default storage capacity will be:@* 
@verb{|   1K UTF-32 characters + 4K UTF-8 bytes + 1K 16-bit integers,|}@* 
@verb{|   or 4Kb + 4Kb + 2Kb == 10Kb (approximately).|} 

@item The @LDQ{}medium@RDQ{} capacity, gsALLOCMED is four(4) times 
the minimum capacity, i.e. 4,096 wchar_t characters.@*
This should handle all but the most extreme circumstances. 
Total dynamic allocation will be:@*
@verb{|   4K UTF-32 characters + 16K UTF-8 bytes + 4Kb 16-bit integers,|}@* 
@verb{|   or 16Kb + 16Kb + 8Kb == 40Kb (approximately).|} 

@item The MAXIMUM storage capacity is one thousand (1,000) times the minimum 
allocation.@*
This is 1,024,000 wchar_t characters. (Note the mixture of Kilo-byte and 
Kibi-byte math.) Total dynamic allocation will be:@* 
@verb{|   1000K UTF-32 characters + 4000K UTF-8 bytes + 1000K 16-bit integers,|}@* 
@verb{|   or 1Mb + 1Mb + 500Kb == 2.5Mb (approximately).|}@*
This is enough memory to hold a short novel, and so the maximum allocation 
will be needed in only the most extreme cicumstances.

@item The @LDQ{}reAlloc@RDQ{} method described in the chapter, 
@ifnothtml
@*
@end ifnothtml
@ref{gString Miscellaneous}, may be used to manually increase or 
decrease the size of the storage buffers in increments of gsALLOCMIN. 

@item All methods which set or modify the stored text data implement 
automatic re-sizing of the data buffers.

@i{The @LSQ{}@b{gsmeter}@RSQ{} test application reports all these methods 
to be fully functional; however, be aware that the allocation/reallocation 
algorithm is still young, relatively speaking, (about eight months 
as of May 2025); so if a problem arises, please drop the author a note.}

Please @pxref{gsmeter Test App} for additional information; specifically 
the @t{R[a|b]} (reallocate) tests which exercise all methods that perform 
modifications to the size of the storage buffers.
@end itemize
@sp 2


@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.04 Assignment Operators  - - - - - - - - - - - - - - - - - - - - -
@c               Overloaded assigment operators  - - - - - - - - - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node Assignment Operators
@unnumbered Assignment Operators
@GSChIndx{04} Assignment Operators
@c @cindex 07.04 Assignment Operators

For those new to C++, an assignment operator assigns (initializes) 
the object to the left of the '=' using the data on the right of the '='. 
You may also hear the term 'overloaded operator'. This just means that 
the '=' assignment operator may be defined in more than one way, so it 
will perform different tasks according to the context or circumstance.

@itemize @MSQUARE
@item @b{void operator = ( const char* usrc ) ;}
@item @b{void operator = ( const uint8_t* usrc ) ;}

@verbatim
  Input  :
     usrc  : pointer to an array of UTF-8-encoded characters
  Returns:
     nothing
@end verbatim

Assignment operator: converts UTF-8-encoded source to gString.

@item @b{void operator = ( const wchar_t* wsrc ) ;}

@verbatim
  Input  :
     wsrc  : pointer to an array of wchar_t 'wide' characters
  Returns:
     nothing
@end verbatim

Assignment operator: converts wchar_t ('wide') source to gString.

@item @b{void operator = ( const gString& gssrc ) ;}

@verbatim
  Input  :
     gssrc : gString object to be copied (by reference)
  Returns:
     nothing
@end verbatim

Assignment operator. Copies one gString object to another.
@end itemize

@Subsubheading{Examples,........}

@verbatim
char utf8Data[] = { "Youth is wasted on the young." } ;
gString gs1, gs2 ;

gs1 = utf8Data ;
gs2 = gs1 ;
wcout << gs2 << endl ;
 - - -> Youth is wasted on the young.
@end verbatim
@sp 2


@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.05 Formatted Assignments   - - - - - - - - - - - - - - - - - - - -
@c               Data formatting (like swprintf, but better) - - - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node Formatted Assignments
@unnumbered Formatted Assignments
@GSChIndx{05} Formatted Assignments
@c @cindex 07.05 Formatted Assignments

@itemize @MSQUARE
@item @b{const wchar_t* compose ( const wchar_t* fmt, ... )@*
@w{@t{                        } __attribute__ ((format (gnu_wprintf, 2, 0))) ;}}@*
@item @b{const wchar_t* compose ( const char* fmt, ... )@* 
@w{@t{                        } __attribute__ ((format (gnu_printf, 2, 0))) ;}}

@verbatim
  Input  :
     fmt  : a format specification string in the style of sprintf(),
            swprintf() and related formatting C/C++ functions.
     ...  : optional arguments (between ZERO and gsfMAXARGS)
            Each optional argument is a POINTER (address of) the value
            to be formatted.
            - Important Note: There must be AT LEAST as many optional
              arguments as the number of format specifiers defined in
              the formatting string. Excess arguments will be ignored;
              HOWEVER, too few arguments will result in an application
              crash. You have been warned.
  Returns:
     const wchar_t* to formatted data
@end verbatim

Create formatted text data from a format specification string including 
between ZERO and gsfMAXARGS format specifications and their corresponding 
argument pointers.

@verbatim
Supported data types:
 %d, %i  integer (decimal)
 %o      integer (octal)
 %u      integer (unsigned)
 %x, %X  integer (hex lower or upper case)
 %f      floating point (fixed point)
 %e, %E  floating point (scientific notation, lower/uppercase)
 %g, %G  floating point (normal/exponential, lower/uppercase)
 %a, %A  floating point (hex fraction)
 %c      character
 %C      character (alias for %lc)
 %s      string
 %S      string (alias for %ls)
 %p      pointer
 %b, %B  (extension to swprintf - see description below)
 %m      capture 'errno' description string (see /usr/include/errno.h)
 %n      number of characters printed so far
         (value written to corresponding argument's location)
 %%      literal '%'

See man pages for the C/C++ function 'swprintf' or
'Table of Output Conversions' for additional details.
@end verbatim
@end itemize

@Subsubheading{Examples,........}

@verbatim
char      Greeting[] = { "Hello!" } ;
int       iValue = 27064 ;
long long int qValue = 7842561 ;
long int  lValue = 485772 ;
short int sValue1 = 28875, sValue2 = -261, sValue3 = 529 ;
bool      flagValue = true ;
float     fltValue = 278.5610234 ;
double    dblValue = 9982.5610234 ;
gString gs ;
gs.compose( "%s - %d %12hd, %-hi, %#hx %08lXh %lld %hhd",
            Greeting, &iValue, &sValue1, &sValue2, &sValue3,
            &lValue, &qValue, &flagValue ) ;
wcout << gs << endl ;
 - - -> Hello! - 27064        28875, -261, 0x211 0007698Ch 7842561 1
gs.compose( "floating downstream:%10.2f and doubling our pace:%.4lf",
            &fltValue, &dblValue ) ;
wcout << gs << endl ;
 - - -> floating downstream:    278.56 and doubling our pace:9982.5610
@end verbatim

See also formatted instantiation: @ref{gString Instantiation}.
@sp 1

@Subsubheading{Important Note on Formatting,............................}

Because THE PARAMETERS ARE POINTERS TO THEIR DATA, similar 
to the C/C++ library function 'sscanf' and friends, the compiler cannot perform 
automatic promotions from short int* to int* or from float* to double*, and 
so-on as it would for swprintf.

This implementation was selected because a) it eliminates data-width conflicts 
when moving among hardware platforms, and b) it reduces code size while 
increasing performance.

This implementation relies on you, the designer, to use care that the data 
type you specify in the formatting string matches the data type of the variable 
referenced by its parameter pointer AND that you use the 'address-of' ('&') 
operator to reference non-pointer variables. Note also that 'literal' values 
may not be used as parameters because literals have no address.

@b{@i{The following constructs will produce errors:}}
@verbatim
gString gs ;
char   grade = 'A' ;
short  age   = 21 ;
int    sat   = 1550 ;
double gpa   = 3.75 ;

   // These examples fail to use the 'address-of' operator for the 
   // referenced variables, and will cause a 'segmentation fault' 
   // i.e. an application crash.
   gs.compose( "My grade is an %c", grade ) ;
   gs.compose( "I got a %d on my SAT.", sat ) ;
   // The above should be:
   gs.compose( "My grade is an %c", &grade ) ;
   gs.compose( "I got a %d on my SAT.", &sat ) ;

   // These examples use mismatched format-specification/variable 
   // reference. This will result in either bad data out OR will 
   // cause a memory-access violation.
   gs.compose( "I can't wait to be %d.", &age ) ;
   gs.compose( "My GPA is %1.3f", &gpa ) ;
   gs.compose( "The hex value of %c is: %#x", &grade, &grade ) ;
   gs.compose( "My GPA is %1.3lf", 3.88 ) ; // (literal value)
   // The above should be:
   gs.compose( "I can't wait to be %hd.", &age ) ;
   gs.compose( "My GPA is %1.3lf", &gpa ) ;
   gs.compose( "The hex value of %c is: %#hhx", &grade, &grade ) ;
@end verbatim

Parameter Type Checking:@*
Unfortunately, type-checking of wchar_t formatting strings is not yet 
supported by the gnu (v:4.8.0) compiler, (but see wchar.h which is 
preparing for the future). Thus, use care when constructing your 
'wchar_t fmt' formatting string. The 'char fmt' string IS type-checked.

@b{IMPORTANT NOTE:}@*
@i{Depending on your compiler version, you may get a warning when using 
the @verb{|'%b'|} binary format specification (described below):}@*
@b{@verb{|   "warning: unknown conversion type character ‘b’ in format [-Wformat=]"|}}@*
@i{This is because the preprocessor does not recognize our custom format 
specifier. If this happens, use a 'wchar_t' (wide) formatting template 
to avoid the preprocessor type checking.}

@verbatim
Instead of:
      gs.compose( "bitmask: %b", &wk.mevent.eventType );
Use this (not type checked by the preprocessor):
      gs.compose( L"bitmask: %b", &wk.mevent.eventType );
@end verbatim
@sp 1
@ifnothtml
@sp 1
@end ifnothtml


@Subsubheading{Formatted binary output (extension to swprintf),...............................................}

We implement an extension to the swprintf output-conversion-specifiers 
for binary formatted output. We have found this formatting option useful 
when working with bit masks, for verifying bit-shifting operations 
during encryption/decryption and other uses.

@itemize @MCIRCLE
@item Base formatting specifier:@*
@example
%b , %B

Note that the lower-case / upper-case variants have identical 
function, and indicate only the case of the identifier character. 
See format modifiers for data size.
@end example

@item Format modifiers for @b{data size} are the same as for swprintf:@*
@example
hh , h , l , ll , L , q
Examples: %hhb  %hB  %llB  %qb
@end example

@item Format modifier for @b{prepending} of a data-type indicator.@*
@example
'#' (hash character)
This is the same principle as for prepending a '0x' indicator 
to hex output, and will place either a 'b' or 'B' character at 
the beginning of the output.
Examples:  %#hhb -> b0101.1010   %#hhB -> B0101.1010 
@end example

@item Format modifier for @b{appending} of a data-type indicator.@*
@example
'-#' (minus sign and hash character)
Rather than prepending the indicator, the indicator will be 
append to the end of the output.
Examples: %-#hhb -> 0101.1010b   %-#hhB -> 0101.1010B
@end example

@item Format modifier for specifying the @b{group-seperator} character.@*
@example
By default, the bit groups are seperated by a '.' (fullstop) 
character. To specify an alternate seperator character:
% hB   -> 0111 0101 1010 0001   (' ' (space) as seperator)
%_hB   -> 0111_0101_1010_0001   ('_' (underscore) as seperator)
%#/hB  -> B0111/0101/1010/0001  ('/' (slash) as seperator)
%-#-hB -> 0111-0101-1010-0001B  ('-' (dash) as seperator)

Valid seperator characters are any printable ASCII character that
IS NOT alphabetical, IS NOT a number, and IS NOT a '.'(fullstop)
@end example

@item Format modifier for specifying @b{bit grouping}.@*
@example
By default, bits are formatted in groups of four (4 nybble); however,
if desired, bits can be formatted in groups of eight (8 byte):
%.8hB    -> 01110101.10100001
%-.8hB   -> 01110101-10100001
%# .8hB  -> B01110101 10100001
%-#`.8hb -> 01110101`10100001b
@end example
@end itemize
@sp 1

@Subsubheading{Field-width specification (swprintf bug fix),............................................}

The standard library 'swprintf' function has a design flaw for format 
specifications that include a field-width specifier.

'swprintf' pads the string to the specified number of CHARACTERS, not 
the number of COLUMNS as it should do. For ASCII numeric source 
values this is not a problem because one character equals one display 
column. For string source data, however, if the source string contains 
characters that require more than one display column each, then the 
output may be too wide.

Therefore, for string-source-formatting specifications ONLY:@*
@w{          (examples: "%12s"  "%-6s"  "%16ls"  "%5S"  "%-24S")}@*
we compensate for this ethnocentric behavior by interpreting the field-width 
specifier as number-of-columns, NOT number-of-characters. For non-ASCII string 
data, this will result in output that appears different (and better) than 
output created directly by the 'swprintf' function.
@sp 1

@Subsubheading{Unsupported format specifications,.................................}

Conversion modifiers that are not fully supported at this time:@*
@w{                       'j', 'z', 't', '%['}@*
Also, the '*' field-width specification or precision specification which uses 
the following argument as the width/precision value IS NOT supported.

@sp 2


@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.06 Integer Formatting  - - - - - - - - - - - - - - - - - - - - - -
@c               Formatting and sizing integer display - - - - - - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node Integer Formatting
@unnumbered Integer Formatting
@GSChIndx{06} Integer Formatting
@c @cindex 07.06 Integer Formatting

@itemize @MSQUARE
@item @b{bool formatInt ( short iVal, short fWidth,@*
@w{@t{              }   bool lJust = false, bool sign = false,}@*
@w{@t{              }   bool kibi = false, fiUnits units = fiK ) ;}}@*

@item @b{bool formatInt ( unsigned short iVal, short fWidth,@* 
@w{@t{              }   bool lJust = false, bool sign = false,}@*
@w{@t{              }   bool kibi = false, fiUnits units = fiK ) ;}}@*

@item @b{bool formatInt ( int iVal, short fWidth,@* 
@w{@t{              }   bool lJust = false, bool sign = false,}@*
@w{@t{              }   bool kibi = false, fiUnits units = fiK ) ;}}@*

@item @b{bool formatInt ( unsigned int iVal, short fWidth,@* 
@w{@t{              }   bool lJust = false, bool sign = false,}@*
@w{@t{              }   bool kibi = false, fiUnits units = fiK ) ;}}@*

@item @b{bool formatInt ( long iVal, short fWidth,@* 
@w{@t{              }   bool lJust = false, bool sign = false,}@*
@w{@t{              }   bool kibi = false, fiUnits units = fiK ) ;}}@*

@item @b{bool formatInt ( unsigned long iVal, short fWidth,@* 
@w{@t{              }   bool lJust = false, bool sign = false,}@*
@w{@t{              }   bool kibi = false, fiUnits units = fiK ) ;}}@*

@item @b{bool formatInt ( long long iVal, short fWidth,@* 
@w{@t{              }   bool lJust = false, bool sign = false,}@*
@w{@t{              }   bool kibi = false, fiUnits units = fiK ) ;}}@*

@item @b{bool formatInt ( unsigned long long iVal, short fWidth,@* 
@w{@t{              }   bool lJust = false, bool sign = false,}@*
@w{@t{              }   bool kibi = false, fiUnits units = fiK ) ;}}@*
@end itemize

@verbatim
  Input  :
     iVal  : value to be converted
             Supported value range: plus/minus 9.999999999999 terabytes
     fWidth: field width (number of display columns)
             range: 1 to FI_MAX_FIELDWIDTH
     lJust : (optional, false by default)
             if true, strip leading spaces to left-justify the value
             in the field. (resulting string may be less than fWidth)
     sign  : (optional, false by default)
             'false' : only negative values are signed
             'true'  : always prepend a '+' or '-' sign.
     kibi  : (optional, false by default)
             'false' : calculate as a decimal value (powers of 10)
                       kilobyte, megabyte, gigabyte, terabyte
             'true'  : calculate as a binary value (powers of 2)
                       kibibyte, mebibyte, gibibyte, tebibyte
     units  : (optional) member of enum fiUnits (fiK by default)
              specifies the format for the units suffix.
              Note that if the uncompressed value fits in the field,
              then this parameter is ignored.

  Returns:
     'true' if successful
     'false' if field overflow (field will be filled with '#' chars)
             See notes below on field overflow.
@end verbatim

@sp 1

Convert an integer value into a formatted display string of the specified 
width. Value is right-justified in the field, with leading spaces added 
if necessary (but see 'lJust' parameter).

Maximum field width is FI_MAX_FIELDWIDTH. This is wide enough to display 
a 18-digit, signed and comma-formatted value: @t{'+9,876,543,210,777'}

@format
Actual formatting of the value depends on the combination of:
@t{  a) magnitude of the value}
@t{  b) whether it is a signed value}
@t{  c) the specified field-width}
@t{  d) the specified suffix format}
@t{  e) locale-specific grouping of digits according the LC_NUMERIC locale} 
@t{     environment variable}
@t{     @b{Important Note:} the 'C' (default) locale defines an empty string}
@t{     as the grouping separator character. Therefore, the locale should}
@t{     be explicitly set before calling this method. (This is done}
@t{     automatically when the NcDialog API is initialized.)}
@t{  f) See notes below on the possible reasons for field overflow:}
@t{     @pxref{field overflow}}
     
The following examples are based on the U.S. English locale: @LSQ{}en_US.utf8@RSQ{}.
@end format

@sp 1

@Subsubheading{Examples,........}

@verbatim
1) Simple comma formatted output if specified field-width is sufficient.
   345    654,345    782,654,345    4,294,967,295

2) Output with values compressed to fit a specified field width.
   12.3K    999K    12.345M    1.234G    4.3G

   
gString gs ;      // gString object

3) Convert a signed integer value:
   int iValue = 28954 ;

   // field width == 8, right justified (note: compression unnecessary)
   gs.formatInt( iValue, 8 ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :  28,954:

   // field width == 8, left justified (note: compression unnecessary)
   gs.formatInt( iValue, 8, true ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :28,954:

   // field width == 6
   gs.formatInt( iValue, 6 ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :28.95K:

   // field width == 6 with forced sign
   gs.formatInt( iValue, 6, false, true ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :+29.0K:

   // field width == 5
   gs.formatInt( iValue, 5 ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :29.0K:

   iValue = -28954 ;    // convert negative source value

   // field width == 8, right justified (note: compression unnecessary)
   gs.formatInt( iValue, 8 ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  : -28,954:

   // field width == 8, left justified (note: compression unnecessary)
   gs.formatInt( iValue, 8, true ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :-28,954:

   // field width == 6
   gs.formatInt( iValue, 6 ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :-29.0K:

   // field width == 5
   gs.formatInt( iValue, 5 ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  : -29K:

4) Convert an unsigned long long integer value (field width == 11):
   unsigned long long int qValue = 39000009995 ;

   // decimal compression (gigabytes) with "official" IEC suffix
   gs.formatInt( qValue, 11, false, false, false, fikB ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :39.000010gB:

   // binary compression (gibibytes) with "official" IEC suffix
   gs.formatInt( qValue, 11, false, false, true, fiKiB ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :38.08595GiB:
@end verbatim

Please see (NcDialog test application, 'Dialogw' for more examples.)@*
@ifclear GSTRING_STANDALONE
@xref{Test Applications, Dialogw App}.
@end ifclear
@ifnothtml
@sp 1
@end ifnothtml

@cindex fiUnits enumerated type
@Subsubheading{Notes on the @t{formatInt} group,............................}

@format
Optional specification of the units suffix for the 'formatInt' methods.
Units are specified using the optional @verb{|'units'|} parameter, which
is a member of the @verb{|'fiUnits'|} enumerated type..

@verb{|enum fiUnits : short
{
   fiK,     // 'K'   'M'   'G'   'T'   (default)
   fik,     // 'k'   'm'   'g'   't'
   fiKb,    // 'Kb'  'Mb'  'Gb'  'Tb'
   fikB,    // 'kB'  'mB'  'gB'  'tB'  ("official" metric 'kilo' designation)  
   fiKiB,   // 'KiB' 'MiB' 'GiB' 'TiB' ("official" binary 'kibi' designation)
} ;|}

The 'formatInt' methods use decimal (powers of 10) compression 
calculations by default. To use binary (powers of 2) compression, 
use the optional @verb{|'kibi'|} parameter.

@t{   DECIMAL                      BINARY}
@t{   kilobytes (x/1000)           kibibytes (x/1024)}
@t{   megabytes (x/1000000)        mibibytes (x/1024000)}
@t{   gigabytes (x/1000000000)     gibibytes (x/1024000000)}
@t{   terabytes (x/1000000000000)  tebibytes (x/1024000000000)}
@end format

@Subsubheading{The kilo/kibi controversy,.........................}

The IEC (International System of Quantities) recommends lower-case for 
metric (powers of 10) and upper-case for binary (powers of 2).
However, unless you must be accurate in presenting the data according to 
IEC standard, it is recommended that you choose the format according to: 
your space requirements, visual appeal, and clear communication with your 
users.@*
@i{If you blindly follow style standards against your own better 
judgement, then be forever labelled as a weenie.}
@ifnothtml
@sp 1
@end ifnothtml

@anchor{field overflow}
@cindex formatInt field overflow
@Subsubheading{@t{formatInt} field overflow,........................}

As described above, the actual formatting of a fixed-width integer field 
depends on a number of factors. Every effort is made to compress the 
data to fit within the field while retaining an accurate representation of 
the numeric value.

There are cases, however, where it is not possible to represent the data 
within the specified field width. When this occurs, the entire field 
will be filled with HASH characters @verb{|'#'|}.

The specified field must be wide enough to accomodate either the entire, 
uncompressed value, @b{@emph{or}} the combination of compressed value, 
units designator and sign (if any). The following situations may cause 
field overflow.

@format
@t{ a) Values <= -10.0 Tbytes or >= 10.0 Tbytes cannot be represented}
@t{    by @verb{|'formatInt'|} methods.}
@t{ b) One-column fields can display values between @verb{|0 and 9|}.}
@t{    Values outside this range will cause overflow.}
@t{ c) Two-column fields can display values between @verb{|-9 and 99|}.}
@t{    Values outside this range will cause overflow.}
@t{ d) Three-column fields can display @b{@emph{compressed}} data}
@t{    only if the combined width of value, sign and units require}
@t{    no more than three(3) columns.}
@t{ e) Four-column fields can display @b{@emph{compressed}} data}
@t{    only if the combined width of value, sign and units require}
@t{    no more than four(4) columns.}
@t{ f) Five-column fields can accurately display any value @b{@emph{IF}}}
@t{    the units designator requires only one(1) column.}
@t{ g) Six-column fields can accurately display any value @b{@emph{IF}}}
@t{    the units designator requires no more than two(2) columns.}
@end format

Fields of seven(7) or more columns can display any formatted value 
without danger of overflow.

@c @ifset GSTRING_STANDALONE
@hr20

@anchor{how to set locale}
@cindex set locale
@cindex locale, set

@Subsubheading{How To Set the Application Locale In C++,
               ........................................}

The @t{NcDialogAPI} library automatically sets the application locale 
according to the console window environment.@
Please see the NcDialog documentation, chapter: Multi-language Support 
for details.

In brief, the @LDQ{}locale@RDQ{} specifies the upper-/lower-case text rules, 
numeric formatting, language-specific punctuation, currency symbols and so on.

The application locale should be taken from the terminal environment if 
possible, This is done by creating an instance of the @t{"std::locale"} 
structure referencing the empty string (@t{""}).@*
@b{@t{@ @ @ locale* locptr = new locale("");}}@*
The captured locale is then made @LDQ{}global@RDQ{}, that is it replaces the 
so-called @t{"classic"} (C/C++ language) locale with the specified 
locale definition.@*
@b{@t{@ @ @ locptr->global( *locptr );}}@*
Please see the C++ documentation for @t{"std::locale"} for details.
@c @end ifset
@sp 2


@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.07 Data Access   - - - - - - - - - - - - - - - - - - - - - - - - -
@c               Referencing the 'const' data of gString - - - - - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node Data Access
@unnumbered Data Access
@GSChIndx{07} Data Access
@c @cindex 07.07 Data Access

@itemize @MSQUARE
@item @b{const wchar_t* gstr ( void ) const ;}

@verbatim
  Input  :
     none
  Returns:
     const pointer to array of wchar_t characters
@end verbatim

Return a const pointer to the wchar_t (wide) character array. 
@spi

@item @b{const wchar_t* gstr ( int32_t& charCount ) const ;}

@verbatim
  Input  :
     charCount : (by reference, initial value ignored)
                 receives number of characters in array, 
                 including null terminator
  Returns:
     const pointer to array of wchar_t characters
@end verbatim

Return a const pointer to the wchar_t (wide) character array, 
along with the number of characters in the array (including 
the null terminator).
@spi

@item @b{const char* ustr ( void ) const ;}

@verbatim
  Input  :
     none
  Returns:
     const pointer to array of UTF-8 characters
@end verbatim

Return a const pointer to the char (UTF-8) character array.
@spi

@item @b{const char* ustr ( int32_t& charCount, int32_t& byteCount ) const;}

@verbatim
  Input  :
     charCount : (by reference, initial value ignored)
                 receives number of characters in array, 
                 including null terminator
     byteCount : (by reference, initial value ignored)
                 receives number of bytes in array, 
                 including null terminator
  Returns:
     const pointer to array of UTF-8 characters
@end verbatim

Return a const pointer to the char (UTF-8) character array, 
along with the number of characters @emph{and} the number of 
bytes in the array (including the null terminator).
@end itemize
@spi

@Subsubheading{Examples,........}

@verbatim
int32_t charCount, byteCount ;
gString gs( "Wherever you go, there you are!" ) ;

const wchar_t* wPtr = gs.gstr() ;
const wchar_t* wPtr = gs.gstr( charCount ) ;
const char* utf8Ptr = gs.ustr() ;
const char* utf8Ptr = gs.ustr( charCount, byteCount ) ;
@end verbatim
@sp 2


@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.08 Copying Data  - - - - - - - - - - - - - - - - - - - - - - - - -
@c               Exporting gString contents  - - - - - - - - - - - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node Copying Data
@unnumbered Copying Data
@GSChIndx{08} Copying Data
@c @cindex 07.08 Copying Data

@itemize @MSQUARE
@item @b{int32_t copy ( char* uTarget, int32_t maxBytes, 
@ifnothtml
@*@verb{|              |}
@end ifnothtml
int32_t maxCols = -1 ) const ;}

@verbatim
  Input  :
     uTarget  : pointer to target array to receive UTF-8-encoded text
     maxBytes : maximum number of bytes to copy (incl. NULL terminator)
     maxCols  : (optional, default: -1, count bytes only)
                maximum number of display-columns to copy
  Returns:
     number of bytes copied (incl. NULL terminator)
@end verbatim

Copy gString text to specified target buffer.

@ifnothtml
@example
  ┌─────────────────────────────────────────────────────────┐
  │ Important Note: It is the caller's responsibility to    │
  │ specify a target buffer large enough to hold the data.  │
  │ Be safe: char ubuff[gs.utfbytes()];                     │
  └─────────────────────────────────────────────────────────┘
@end example
@end ifnothtml
@CartHtml{@b{Important Note:} It is the caller's responsibility to 
specify a target buffer large enough to hold the data.
Be safe: @t{ char ubuff[gs.utfbytes()];}}

@i{@b{Technical Note:} If source data bytes greater than specified @t{'maxBytes'},@*
then copy data up to the complete UTF-8 character <= byte limit.@*
This avoids writing an invalid character into the target array.}
@spi

@item @b{int32_t copy ( wchar_t* wTarget, int32_t maxChars, 
@ifnothtml
@*@verb{|              |}
@end ifnothtml
int32_t maxCols = -1 ) const ;}

@verbatim
  Input  :
     wTarget  : pointer to target array to receive wchar_t 'wide' text
     maxChars : maximum number of characters to copy (incl. NULL)
     maxCols  : (optional, default: -1, count characters only)
                maximum number of display-columns to copy
  Returns:
     number of characters copied (incl. NULL terminator)
@end verbatim

Copy gString text to specified target buffer.
@ifnothtml
@example
  ┌─────────────────────────────────────────────────────────┐
  │ Important Note: It is the caller's responsibility to    │
  │ specify a target buffer large enough to hold the data.  │
  │ Be safe:  wchar_t wbuff[gs.gschars()];                  │
  └─────────────────────────────────────────────────────────┘
@end example
@end ifnothtml
@CartHtml{@b{Important Note:} It is the caller's responsibility to 
specify a target buffer large enough to hold the data.
Be safe: @t{ wchar_t wbuff[gs.gschars()];}}
@spi

@item @b{std::wostream& operator<< ( std:wostream& os, const gString& gs2 );}

@verbatim
  Input  :
     IMPLIED reference to the output stream
     IMPLIED reference to the gString object
  Returns: reference to the specified output stream
@end verbatim

!! NON-MEMBER METHOD !!@*
Insertion operator: Copies the contents of the gString object into 
the 'wcout' (wide) standard output stream.

@format
Note that due to the way the output stream is defined, you cannot mix 
'cout' (narrow) and 'wcout' (wide) output streams indiscriminately.
  @minus{}@minus{} If 'wcout' is called first, then 'cout' is disabled.
  @minus{}@minus{} If 'cout' is called first, then both narrow and wide 
     channels are active.
  @minus{}@minus{} 'wcout' handles both narrow and wide data, however, 
     'cout' handles ONLY narrow data.
This is not related to gString, but is a characteristic of the default 
C++ output stream itself. We recommend that you always use the 'wcout' 
stream in console applications for both narrow and wide text data.
@end format
@sp 1

@item @b{std::ostream& operator<< ( std:ostream& os, const gString& gs2 );}

@verbatim
  Input  :
     IMPLIED reference to the output stream
     IMPLIED reference to the gString object
  Returns: reference to the specified output stream
@end verbatim

!! NON-MEMBER METHOD !!@*
Insertion operator: Copies the contents of the gString object into 
the 'cout' (narrow) standard output stream.

@example
@b{IMPORTANT NOTE:} @i{Access to the narrow output stream is 
provided for convenience only. It is recommended that the 
wide stream version (above), if available on your system, 
be used exclusively.}
@end example
@spi

@item @b{int32_t substr ( char* uTarget, int32_t offset, 
@ifnothtml
@*@verb{|                |}
@end ifnothtml
int32_t charCnt ) const;}
@item @b{int32_t substr ( wchar_t* wTarget, int32_t offset, 
@ifnothtml
@*@verb{|                |}
@end ifnothtml
int32_t charCnt ) const;}
@item @b{int32_t substr ( gString& wTarget, int32_t offset, 
@ifnothtml
@*@verb{|                |}
@end ifnothtml
int32_t charCnt ) const;}

@verbatim
  Input  :
     targ    : (by reference, initial contents ignored)
               receives null-terminated contents of specified
               character range
               -- If target buffer is a char*, then data returned is 
                  a UTF-8 text string.
               -- If target buffer is a wchar_t*, then data returned is 
                  a wchar_t (wide) text string.
               -- If target buffer is a gString object, then both 
                  UTF-8 and wchar_t data are returned 
     offset  : character index at which substring begins
               (this IS NOT a byte index)
     charCnt : number of characters to copy (not incl. NULL terminator)
  Returns:
     if target is a wchar_t* or gString object, then returns number of
       characters written to target (not including the NULL terminator)
     if target is a char*, then returns number of bytes written to
       target (not including the NULL terminator)

     Note: returns ZERO if either 'offset' or 'charCnt' out of range
     Note: If 'charCnt' extends beyond the end of the source data, 
           then returns the available data.
@end verbatim

Copy the specified character range to target buffer.@*
These methods copy the indicated substring (null terminated) to 
the target buffer, leaving the original data unchanged.

If you have a fixed-format field, then the offset and character count 
will be known in advance. Otherwise you can use the 'find()' method to 
locate the substring to be copied.

@ifnothtml
@example
  ┌─────────────────────────────────────────────────────────┐
  │ Important Note: It is the caller's responsibility to    │
  │ specify a target buffer large enough to hold the data.  │
  │ If size of substring is unknown, be safe:               │
  │    wchar_t wbuff[gs.gschars()];   or                    │
  │    char ubuff[gs.utfbytes()];                           │
  └─────────────────────────────────────────────────────────┘
@end example
@end ifnothtml
@CartHtml{@b{Important Note:} It is the caller's responsibility to 
specify a target buffer large enough to hold the data.
If size of substring is unknown, be safe:
@t{   wchar_t wbuff[gs.gschars()];} or 
@t{   char ubuff[gs.utfbytes()];}}

@quotation
Please Note: The number of bytes can @b{@emph{NEVER}} be assumed to be 
the same as the number of characters.@*
@ifclear GSTRING_STANDALONE
Please @pxref{Multi-language Support}.
@end ifclear
@ifset GSTRING_STANDALONE
Please refer to the 'Multi-language Support' chapter of the @t{'NcDialog API'} 
documentation.
@end ifset
@end quotation
@end itemize

@Subsubheading{Examples,........}

@verbatim
gString gs( "That's not flying, that's falling -- with style!\n"
            "Buzz Lightyear" ) ;
char    utf8Data[gs.gs.utfbytes()] ;
wchar_t wideData[gs.gschars()] ;

gs.copy( utf8Data, gs.utfbytes() ) ;
gs.copy( wideData, gs.gschars() ) ;

gString gstream( "You're a child's TOY! -- Woody" ) ;
wcout << gstream << endl ;

// get a copy of the first word starting with 'c'
gString AusAnimal( "Aardvark Kangaroo Cockatoo Dingo Wombat " ) ;
gString gsc ;
int b = AusAnimal.find( " c" ) ;
if ( b >= 0 )
{
   int e = AusAnimal.find( L' ', b + 1 ) ;
   if ( e > b )
   {
      AusAnimal.substr( gsc, (b + 1), (e - b - 1) ) ;
      wcout << gsc << endl ;
   }
}
 - - -> Cockatoo
@end verbatim
@sp 2


@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.09 Modifying Existing Data   - - - - - - - - - - - - - - - - - - -
@c               Add to, subtract from, shift or replace data  - - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node Modifying Existing Data
@unnumbered Modifying Existing Data
@GSChIndx{09} Modifying Existing Data
@c @cindex 07.09 Modifying Existing Data

@itemize @MSQUARE
@item @b{int32_t append ( const wchar_t* wPtr ) ;}@*
@item @b{int32_t append ( const char* uPtr ) ;}@*
@item @b{int32_t append ( const wchar_t wChar ) ;}

@verbatim
  Input  :
     wPtr  : pointer to array of wchar_t 'wide' text to be appended
             OR
     uPtr  : pointer to array of char UTF-8 text to be appended
             OR
     wChar : a single, 'wide' character
  Returns:
     number of characters in resulting string (incl. NULL terminator)
     Note: if value returned equals gsALLOCMAX, then 
           some data MAY HAVE BEEN discarded.
@end verbatim

Append text to existing gString text data up to a combined length of
gsALLOCMAX. Characters in excess of the maximum will not be appended.

@Subsubheading{Example,.......}

@verbatim
gString gs( L"Be kind to your manager." ) ;
gs.limitChars( gs.gschars() - 2 ) ;
gs.append( L", and other lower forms of life." ) ;
wcout << gs << endl ;
 - - -> Be kind to your manager, and other lower forms of life.
@end verbatim
@spi

@item @b{int32_t append ( const wchar_t* fmt, const void* arg1, ... ) 
@ifnothtml
@*@verb{|                |}
@end ifnothtml
__attribute__ ((format (gnu_wprintf, 2, 0)));}@*
@item @b{int32_t append ( const char* fmt, const void* arg1, ... ) 
@ifnothtml
@*@verb{|                |}
@end ifnothtml
__attribute__ ((format (gnu_printf, 2, 0)));}

@verbatim
  Input  :
     fmt  : a format specification string in the style of sprintf(),
            swprintf() and related formatting C/C++ functions.
     arg1 : pointer to first value to be converted by 'fmt'
     ...  : optional arguments (between ZERO and gsfMAXARGS - 1)
            Each optional argument is a POINTER (address of) the value
            to be formatted.

  Returns:
     number of characters in resulting string (incl. NULL terminator)
     Note: if return equals gsALLOCMAX, then
           some data MAY HAVE BEEN discarded.
@end verbatim

Append formatted text data to existing gString text data up to a combined 
length of gsALLOCMAX. Characters in excess of the maxmum will not be 
appended.

Please refer to the 'compose' method (@pxref{Formatted Assignments}) 
for more information on converting data using a format specification string.

@Subsubheading{Example,.......}

@verbatim
short gaddress = 2840 ;
wchar_t gdirection = L'E' ;
const wchar_t* gstreet = L"Colorado Blvd." ;
double gcost = 29.95 ;
gString gs( "Gorilla Men's Clothing" ) ; // existing text

gs.append( ", %hd %C %S\n  Dress shirts on sale, $%.2lf.", 
           &gaddress, &gdirection, gstreet, &gcost ) ;

wcout << gs << endl ;
 - - -> Gorilla Men's Clothing, 2840 E Colorado Blvd.
          Dress shirts on sale, $29.95.
@end verbatim
@spi

@item @b{int32_t insert ( const wchar_t* wPtr, int32_t offset = 0 ) ;}@*
@item @b{int32_t insert ( const char* uPtr, int32_t offset = 0 ) ;}@*
@item @b{int32_t insert ( wchar_t wChar, int32_t offset = 0 ) ;}

@verbatim
  Input  : 
     wPtr  : pointer to array of wchar_t 'wide' text to be inserted
             OR
     uPtr  : pointer to array of char UTF-8 text to be inserted
             OR
     wChar : a single wchar_t 'wide' character
     offset: (optional, ZERO by default)
             character offset at which to insert specified text into
             existing text.
             Note: if specified 'offset' > number of characters in
                   existing text, then acts like 'append' method.
  Returns:
     number of characters in resulting string (incl. NULL terminator)
     Note: if value returned equals gsALLOCMAX, then 
           some data MAY HAVE BEEN discarded.
@end verbatim

Insert text into existing gString text data up to a combined length of
gsALLOCMAX. Characters in excess of the maximum will be truncated.

@Subsubheading{Example,.......}

@verbatim
gString gs( L"Remember to hurt people!" ) ;
gs.insert( L"NOT ", 9 ) ;
wcout << gs << endl ;
 - - -> Remember NOT to hurt people!
@end verbatim
@spi

@item @b{int32_t insert( int32_t offset, const char* fmt, const void* arg1, ... );}@*
@item @b{int32_t insert( int32_t offset, const wchar_t* fmt, 
@ifnothtml
@* @verb{|               |}
@end ifnothtml
const void* arg1, ... );}

@verbatim
  Input  :
     offset: character offset at which to insert specified text into
             existing text.
             Note: if specified 'offset' > number of characters in
                   existing text, then data will be appended to existing text.
     fmt   : a format specification string in the style of sprintf(),
             swprintf() and related formatting C/C++ functions.
             Format spec may be either char data or wchar_t data.
     arg1  : pointer to first value to be converted by 'fmt'
     ...   : optional arguments (between ZERO and gsfMAXARGS - 1)
             Each optional argument is a POINTER (address of) the value
             to be formatted.

  Returns:
     number of characters in resulting string (incl. NULL terminator)
     Note: if value returned equals gsALLOCMAX, then 
           some data MAY HAVE BEEN discarded.
@end verbatim

Insert formatted text data into existing text data at the specified offset.

Please refer to compose() method for more information on converting data 
using a format-specification string.

@Subsubheading{Example,.......}

@verbatim
const char* Month = "April" ;
short       Date  = 28 ;
gString gs( L"We have a date on to see the Yankees." ) ;
int off = gs.find( "to see" ) ; // index insertion point
gs.insert( off, "%s %hd ", Month, &Date ) ;
wcout << gs << endl ;
 - - -> We have a date on April 28 to see the Yankees.

gString gs( "drive above 90 kph!" ) ;
gs.insert( ZERO, "%S", L"Don't " ) ; // insert at head of text
int off   = gs.gschars() ; // index end-of-text
int Speed = 55 ;
gs.insert( off, L" Stay under %02d kph in the city.", &Speed ) ;
 - - -> Don't drive above 90 kph! Stay under 55 kph in the city.
@end verbatim
@spi

@item @b{int32_t limitChars ( int32_t charCount ) ;}

@verbatim
  Input  :
     charCount : maximum number of characters allowed in formatted data 
                 (not including NULL) Range: 1 to storage limit.
  Returns:
     number of characters in the adjusted data (including NULL)
@end verbatim

Truncate the data to no more than charCount display characters.@*
Insert a null terminator after the specified number of characters.

@Subsubheading{Example,.......}

@verbatim
gString gs( "This shirt is available in yellow or red." ) ;
gs.limitChars( 34 ) ;
gs.append( "only." ) ;
wcout << gs << endl ;
 - - -> This shirt is available in yellow only.
@end verbatim
@spi

@item @b{int32_t limitCols ( int32_t colCount ) ;}

@verbatim
  Input  :
     colCount : maximum number of display columns allowed in formatted data
                Range: 1 to storage capacity.
  Returns:
     number of columns needed to display the adjusted data
     Note: If specified column count occurs in mid-character, then the 
           partial character will be removed from the string.
@end verbatim

Truncate the data to no more than colCount display columns.
Insert a null terminator after the number of characters required to fill
the specified number of display columns.

@Subsubheading{Example,.......}

@verbatim
gString gs( "The manual is located at:\n"
            "http://cdn.funcom.com/aoc/pdf/aoc_manual.pdf" ) ;
gs.limitCols( 55 ) ;
wcout << gs << endl ;
 - - -> The manual is located at:
        http://cdn.funcom.com/aoc/pdf/

Note that there are 25 display columns for the first line, (the newline 
character requires no column), and 30 columns remain on the second line.
@end verbatim
@spi

@item @b{int32_t shiftChars ( int32_t shiftCount, wchar_t padChar = L' ' ) ;}

@verbatim
  Input  :
     shiftCount: < ZERO: shift data to the left, discarding the
                         specified number of characters from the
                         beginning of the array
                 > ZERO: shift data to the right, padding the vacated
                         positions on the left with 'padChar'
                 ==ZERO: do nothing
     padChar   : (optional, SPACE character, 0x20 by default)
                 when shifting data to the right, use this character
                 to fill the vacated character positions
                 NOTE: Specify a one-column character ONLY as the
                 padding character. (multi-column characters ignored)
  Returns:
     number of characters in adjusted array
@end verbatim

Shift text data by the specified number of characters.

@verbatim
Note for writers of RTL (right-to-left) languages:
 In the above descriptions, the terms 'left' and 'right' are used for 
 convenience, but actually 'left' refers to the head of the data 
 and 'right' refers to the tail.
@end verbatim

@Subsubheading{Example,.......}

@verbatim
gString gs( "Your balance is: 2,521,697.56 USD" ) ;
gs.shiftChars( -17 ) ;
wcout << gs << endl ;
 - - -> 2,521,697.56 USD
gs.shiftChars( 5, L'#' ) ;
wcout << gs << endl ;
 - - -> #####2,521,697.56 USD

Note: For this example, the optional fill character used for 
right-shift is L'#'. The default fill character is space (L' ').
@end verbatim
@spi

@item @b{int32_t shiftCols ( int32_t shiftCount, wchar_t padChar = L' ' ) ;}

@verbatim
  Input  :
     shiftCount: < ZERO: shift data to the left, discarding the
                         number of characters equivalent to the
                         specified number of display columns
                         NOTE: May discard one extra column if count
                         falls within a multi-column character.
                 > ZERO: shift data to the right, padding the vacated
                         positions on the left with 'padChar'
                 ==ZERO: do nothing
     padChar   : (optional, SPACE character, U+0020 by default)
                 when shifting data to the right, use this character
                 to fill the vacated column positions
                 NOTE: Specify a one-column character ONLY as the
                 padding character. (multi-column characters ignored)
  Returns:
     number of display columns in adjusted array
@end verbatim

Shift text data by the specified number of display columns.

@verbatim
Note for writers of RTL (right-to-left) languages:
 In the above descriptions, the terms 'left' and 'right' are used for 
 convenience, but actually 'left' refers to the head of the data 
 and 'right' refers to the tail.
@end verbatim

@Subsubheading{Example,.......}

@verbatim
gString gs( "您的帐户余额是500元" ) ; // "Your account balance is 500 yuan"
gs.shiftCols( -14 ) ;
wcout << gs << endl ;
 - - -> 500元
gs.shiftCols( 5, L'.' ) ;
wcout << gs << endl ;
 - - -> .....500元

Note: Most Chinese characters are two display columns wide, 
therefore we shift 14 columns (7 characters) out on the left.
For this example, the optional fill character used for 
right-shift is L'.' (U+002E, ASCII full stop). 
The Chinese full-stop (U+3002) MAY NOT be used as a fill 
character because it is a two-column character.
@end verbatim
@spi

@item @b{int32_t padCols ( int32_t fieldWidth, wchar_t padChar = L' ', 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
bool centered = false );}

@verbatim
  Input  :
     fieldWidth: number of columns for the adjusted data array
     padChar   : (optional, ASCII space 0x20 by default)
                 character with which to pad the data
                 Note: Specify a one-column character ONLY.
                       Multi-column characters will be ignored.
     centered  : (optional, 'false' by default)
                 if 'false', all padding will be appended at the end 
                             of the existing data
                 if 'true',  padding will be equally divided between 
                             the beginning and end of the existing data

  Returns:
     number of display columns in the adjusted array

@end verbatim

Append padding to the existing string data to achieve the specified number 
of display columns.

If the @t{'@b{centered}'} flag is set, the padding will be divided equally 
between the beginning and end of the data to achieve the specified field 
width. Note that if an odd number of columns is to be added, then the odd 
column will be placed at the end of the data.

The @LSQ{}padCols@RSQ{} method calculates the number of padding 
characters needed to fill out the specified field width.

The default padding character is the ASCII space character (20 hex).@*
Any single-column character may be specified as an alternate padding 
character.

Padding will be added until either the specified number of columns is 
reached, @b{@emph{OR}} until the array contains the maximum number of characters 
(storage limit).

If the fieldWidth specified is <= the current width of the data, then 
the data will not be modified.

@Subsubheading{Example,.......}

@verbatim
gString gs( "This is a test." );

gs.padCols( 30 );              ==> "This is a test.               "
gs.padCols( 30, L'#' );        ==> "This is a test.###############"
gs.padCols( 30, L'#', true );  ==> "#######This is a test.########"

// To create a right-justified field, use 'shiftCols' (see above):
gs.shiftCols( (30 - gs.gscols()) );
                               ==> "               This is a test."
gs.shiftCols( (30 - gs.gscols()), L'#' );
                               ==> "###############This is a test."
@end verbatim
@spi

@item @b{int32_t strip ( bool leading = true, bool trailing = true );}

@verbatim
  Input  :
     leading  : (optional, 'true' by default)
                if 'true', strip leading whitespace
                if 'false', leading whitespace unchanged
     trailing : (optional, 'true' by default)
                if 'true', strip trailing whitespace
                if 'false', trailing whitespace unchanged

  Returns:
     number of characters in modified string (incl. NULL terminator)
@end verbatim

Strip (remove) leading and/or trailing whitespace from the string data.

@t{   } Whitespace characters are defined as:@*
@t{    0x0020 }single-column ASCII space@*
@t{    0x3000 }two-column CJK space@*
@t{    0x0A   }linefeed character@*
@t{    0x0D   }carriage-return character@*
@t{    0x09   }horizontal-tab character@*
@t{    0x0B   }vertical-tab character@*
@t{    0x0C   }formfeed character@*

@LDQ{}Leading@RDQ{} whitespace is space characters from the beginning 
of the data to the first non-space character. @LDQ{}Trailing@RDQ{} whitespace 
is from after the last non-space character through the end of the data.

By default, @b{@emph{both}} leading and trailing whitespace will be removed; 
however, this action may be modified by resetting the appropriate parameter 
to @LSQ{}false@RSQ{}.
@spi

@item @b{int32_t erase ( const gString& src, int32_t offset = 0, 
@ifnothtml
@*@verb{|               |}
@end ifnothtml
bool casesen = false, bool all = false );}
@item @b{int32_t erase ( const wchar_t* src, int32_t offset = 0, 
@ifnothtml
@*@verb{|               |}
@end ifnothtml
bool casesen = false, bool all = false );}
@item @b{int32_t erase ( const char* src, int32_t offset = 0, 
@ifnothtml
@*@verb{|               |}
@end ifnothtml
bool casesen = false, bool all = false );}
@item @b{int32_t erase ( wchar_t src, int32_t offset = 0, 
@ifnothtml
@*@verb{|               |}
@end ifnothtml
bool casesen = false, bool all = false );}

@verbatim
  Input  :
     src    : source data to be matched, one of the following:
              -- pointer to a UTF-8 string
              -- pointer to a wchar_t string
              -- a gString object containing the source (by reference)
              -- a single, wchar_t character
     offset : (optional, ZERO by default)
              character index at which to begin search
              NOTE: This is a character index, NOT a byte offset.
     casesen: (optional, 'false' by default)
              if 'false', then scan IS NOT case sensitive
              if 'true, then scan IS case sensitive
     all    : (optional, 'false' by default)
              if 'false', then only the first instance of the substring
                          will be deleted
              if 'true',  then all instances of the specified substring
                          from 'offset' forward will be deleted

  Returns:
     index of first character following the deleted sequence
     Note: This is the wchar_t character index, NOT a byte index
     Returns (-1) if:
       a) no matching substring found
       b) 'offset' out-of-range
       c) 'src' is an empty string or a NULL character

@end verbatim

Scan the data for a matching substring, and if found, erase (delete) the 
first occurance of the substring, or optionally all instances of the substring 
from @t{'offset'} onward.

Actual comparison is always performed against the 'wide' character array 
using wcsncasecmp (or wcsncmp). Null terminator character IS NOT included 
in the comparison.
@spi

@item @b{int32_t erase ( int32_t offset = 0, int32_t length = gsALLOCMAX );}

@verbatim
  Input  :
     offset : (optional, ZERO by default)
              index of first character of sequence to be erased
              NOTE: This is a character index, NOT a byte offset.
     length : (optional, gsALLOCMAX by default)
              if not specified, then erase all characters from
                 'offset' to end of data
              if specified, then erase the specified number of
                 characters beginning at 'offset'

  Returns:
     index of first character following the deleted sequence
     Note: This is the wchar_t character index, NOT a byte index
     Returns (-1) if:
       a) offset < ZERO
       b) offset >= number of characters in data
       c) length <= ZERO
     (data will not be modified)

@end verbatim

Erase (delete) the data sequence specified by 'offset' and 'length'.

'offset' is the index of the first character to be deleted, and 'length' 
specifies the number of characters to delete.

@Subsubheading{Example,.......}

@itemize @MDISC
@item If the 'length' parameter specifies deletion of more characters 
than remain, then 'erase' has the same effect as calling the @t{'limitChars'} 
method (i.e. truncates the string at @t{'offset'}).

@item If the defaults for both @t{'offset'} and @t{length'} are used, then the 
@t{'erase'} method has the same effect as calling the @t{'clear'} method 
(gString data are reset to an empty string).

@item Note that the NULL terminator will never be deleted.
@end itemize

@Subsubheading{Examples for @t{'erase'},....................}

@verbatim
For the gString object containing the following verbal 
exchange, erase all occurances of the word 'crapweasel'.

gString gs( "Ross : Are you familiar with the word crapweasel?\n"
            "Paolo: No, I don't know crapweasel.\n"
            "Ross : You're a huge crapweasel!" );
int index = ZERO ;
while ( (index = gs.erase( " crapweasel", index )) >= ZERO ) ;

Yields:
  "Ross : Are you familiar with the word?\n"
  "Paolo: No, I don't know.\n"
  "Ross : You're a huge!"

Find the substring, "the the" and erase the extra "the".
gString gs1( L"There are seven candidates in the the Primary Election." );
gString gs2( L"the " );
int index = gs1.find( "the the" );
gs1.erase( index, (gs2.gschars() - 1) );

Yields:
  "There are seven candidates in the Primary Election."
@end verbatim
@spi

@item @b{int32_t replace ( const wchar_t* src, const wchar_t* newtxt, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
int32_t offset = 0, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
bool casesen = false, bool all = false );}@*
@item @b{int32_t replace ( const wchar_t* src, const char* newtxt, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
int32_t offset = 0, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
bool casesen = false, bool all = false );}
@item @b{int32_t replace ( const wchar_t* src, const wchar_t newtxt, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
int32_t offset = 0, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
bool casesen = false, bool all = false );}
@item @b{int32_t replace ( const char* src, const wchar_t* newtxt, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
int32_t offset = 0, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
bool casesen = false, bool all = false );}
@item @b{int32_t replace ( const char* src, const char* newtxt, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
int32_t offset = 0, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
bool casesen = false, bool all = false );}
@item @b{int32_t replace ( const char* src, const wchar_t newtxt, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
int32_t offset = 0, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
bool casesen = false, bool all = false );}
@item @b{int32_t replace ( const wchar_t src, const wchar_t* newtxt, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
int32_t offset = 0, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
bool casesen = false, bool all = false );}
@item @b{int32_t replace ( const wchar_t src, const char* newtxt, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
int32_t offset = 0, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
bool casesen = false, bool all = false );}
@item @b{int32_t replace ( const wchar_t src, const wchar_t newtxt, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
int32_t offset = 0, 
@ifnothtml
@*@verb{|                 |}
@end ifnothtml
bool casesen = false, bool all = false );}

@verbatim
  Input  :
     src    : source data to be matched
              -- pointer to a UTF-8 string
              -- pointer to a wchar_t string
              -- a single, wchar_t character
     newtxt : data to overwrite existing text
              -- pointer to a UTF-8 string
              -- pointer to a wchar_t string
              -- a single, wchar_t character
     offset : (optional, ZERO by default)
              character index at which to begin search
              NOTE: This is a character index, NOT a byte offset.
     casesen: (optional, 'false' by default)
              if 'false', then scan IS NOT case sensitive
              if 'true, then scan IS case sensitive
     all    : (optional, 'false' by default)
              if 'false', then replace only the first occurance found
              if 'true',  then replace all occurances of the specified
                          substring

  Returns:
     'true' if successful
     returns 'false' if error (existing data not modified):
       a) no matching source substring found
       b) 'src' is a empty string or a null character
       c) offset < ZERO or offset is beyond existing data
       d) modifying the data would cause buffer overflow
@end verbatim

Replace the specified source substring, or optionally all matching substrings 
with the provided substring.

Actual comparison is always performed against the 'wide' character array 
using wcsncasecmp (or wcsncmp). Null terminator character IS NOT included 
in the comparison.

@Subsubheading{Examples for @t{'replace'},......................}

@verbatim
Correct the spelling errors in the following data:
gString gs( "The land near hare is full of Heres, hoping her and ther." ) ;

bool okiday = gs.replace( L"hare", L"here" ) ;
Yields:
  "The land near here is full of Heres, hoping her and ther."

okiday = gs.replace( "Here", "Hare", ZERO, true ) ;
Yields:
  "The land near here is full of Hares, hoping her and ther."

okiday = gs.replace( L'p', L"pp" ) ;
Yields:
  "The land near here is full of Hares, hopping her and ther."

int index = gs.find( "her " ) ;
okiday = gs.replace( "her", L"here", index, true, true ) ;
Yields:
  "The land near here is full of Hares, hopping here and there."

Then, replace all spaces ' ' with underscores '_'.
okiday = gs.replace( L' ', L'_', ZERO, false, true ) ;
Yields:
  "The_land_near_here_is_full_of_Hares,_hopping_here_and_there."
@end verbatim
@spi

@item @b{int32_t loadChars ( const wchar_t* wsrc, int32_t charLimit, 
@ifnothtml
@*@verb{|                   |}
@end ifnothtml
bool append = false );}@*
@item @b{int32_t loadChars ( const char* usrc, int32_t charLimit, 
@ifnothtml
@*@verb{|                   |}
@end ifnothtml
bool append = false );}

Load the specified number of characters from the source data. 
This is useful for extracting data from fixed-width fields when 
the contents of the field is unknown.@*
@verb{|   gs.loadChars( StreetAddress1, 48 );|}

By default, the new text REPLACES the existing text; however, the 'append' 
parameter allows the new text to be appended to the existing text.

The functionality is equivalent to the gString constructor which loads 
only the specified number of characters.@*
@xref{gString Instantiation}.
@spi

@verbatim
  Input  :
     usrc     : pointer to a UTF-8 encoded string
     wsrc     : pointer to a wchar_t encoded string
     charLimit: maximum number of characters (not bytes) from source
                array to load. Range: 1 through target storage capacity.
                The count should not include the NULL terminator
     append   : (optional, 'false' by default)
                if 'false', replace existing text with specified text
                if 'true', append new text to existing text

  Returns:
     number of characters in modified string (incl. NULL terminator)
@end verbatim

@Subsubheading{Examples for @t{'loadChars'},........................}

@verbatim
Replace the existing text with the specified number of characters 
from the source text.

const char* Piggy =  // (56 characters)
           "Spider-pig, Spider-pig does whatever a Spider-pig does." ;
gString gs( "Existing text." ) ;
gs.loadChars( Piggy, 36 ) ;
Yields:
  "Spider-pig, Spider-pig does whatever"

Append specified number of characters from the source text to the 
existing text.

gs.loadChars( " it's told, because it's stupid.", 11, true ) ;
Yields:
  "Spider-pig, Spider-pig does whatever it's told,"
@end verbatim
@spi

@anchor{textReverse method}
@item @b{int32_t textReverse ( bool punct = false, 
@ifnothtml
@*@verb{|                     |}
@end ifnothtml
 bool para = false, bool rjust = false );}
@cindex BiDi text
@cindex RTL text

Reverse the order of characters in the text string.

If RTL text data displayed by your application is not formatted as desired, 
then this method may be used to reverse the character order before writing 
to the display. This is useful for manipulating both RTL (Right-To-Left) 
language text and mixed RTL/LTR text.

The 'para' parameter is useful for outputting columns of numeric data 
or when the column labels are in an RTL language (see example below).

Although modern web browsers (Firefox, Opera, Chromium, etc.) @emph{usually} 
handle RTL text correctly, other applications often do not. This is especially 
true of terminal emulator software.

See also a discussion of multiple-language support in the NcDialog API.@*
@ifclear GSTRING_STANDALONE
@pxref{Multi-language Support}.
@end ifclear

@verbatim
  Input  :
     punct : (optional, 'false' by default)
             if 'false', invert entire string
             if 'true' AND if a punctuation mark is seen at either end
                of the string, invert everything except the punctuation
                mark(s). typically one of the following:
                '.' ',' '?' '!' ';' ':' but see note below.
     para  : (optional, 'false' by default)
             if 'false', invert data as a single character stream
             if 'true',  invert data separately for each logical line
                         (line data are separated by newlines ('\n')
     rjust : (optional, 'false' by default)
             if 'false', do not insert right-justification padding
             if 'true',  insert padding for each logical line to
                         right-justify the data
                         (used to right-justify LTR output)
                         Note that right-justification is performed
                         ONLY if the 'para' parameter is true.
                         Otherwise, 'rjust' will be ignored.

  Returns:
     number of wchar_t characters in gString object
     (if return value >= storage limit, data may have been truncated)
@end verbatim

@Subsubheading{Examples for @t{'textReverse'},..........................}

@c Programmer's Note: Because Texinfo (texi2any) is a console application, 
@c it allows the terminal to incorrectly re-format our Hebrew string. 
@c We must play a trick with the character order to display it correctly 
@c in this document.
@c "?םיירהצ תחוראל ןכומ התא םאה"   (displayed correctly in the text editor)
@c "האם אתה מוכן לארוחת צהריים?"   (inverted character sequence)
@c const wchar_t *timeLabel = L" :השעה הנוכחית היא" ;
Note that the const Hebrew strings are written canonically in 
the source, but are displayed incorrectly in the terminal window 
by the info reader and by the HTML browser (sorry about that).

@verbatim
// Hebrew: "Are you ready for lunch?"
// Sometimes the terminal will inappropriately move the punctuation 
// to the opposite end of the string.
// To prevent this, use the 'punct' option.
const wchar_t* const Lunch = L"?םיירהצ תחוראל ןכומ התא םאה" ;

gString gs( Lunch ) ;
gs.textReverse() ;
wcout << gs.gstr() << endl ;

OUTPUT (correct except punctuation) : האם אתה מוכן לארוחת צהריים?

gs = Lunch ;
gs.textReverse( true ) ;
wcout << gs.gstr() << endl ;

OUTPUT (correct): ?האם אתה מוכן לארוחת צהריים

// When the 'punct' flag is set, both leading and trailing punctuation 
// are identified.
// Questions in Spanish use a leading inverted question mark (U+00BF), 
// and a trailing ASCII question mark (U+003F).
// Reverse the internal text but do not reverse the terminal punctuation.
gs = "¿ozreumla le arap atsil sátsE?" ;
gs.textReverse( true ) ;
wcout << gs.gstr() << endl ;

OUTPUT : ¿Estás lista para el almuerzo?

 = = = = =
 
// Reverse multi-line text (paragraph formatting).
// Ordinary ASCII text is used for this example 
// to demonstrate the reversal.
// The example outputs to an NcDialog window referenced by NcDialog *dp
const char* Vaccine = "Have you received your covid-19 vaccination yet?\n
                      "Protect yourself and others,\n"
                      "get your vaccination today!" ;
gs = Vaccine ;
// Write unmodified text as LTR data:
dp->WriteParagraph ( 1, 1, gs, nc.grR, true, false ) ;

OUTPUT: Have you received your covid-19 vaccination yet?
        Protect yourself and others,
        get your vaccination today!

// Reverse the data, without punctuation processing or 
// right-justification. (Note: all parameters are optional, 
// but are shown here for clarity.)
gs.textReverse( false, true, false ) ;

// Write reversed text as LTR data:
dp->WriteParagraph ( 1, 1, gs, nc.grR, true, false ) ;

OUTPUT: ?tey noitaniccav 91-divoc ruoy deviecer uoy evaH
        ,srehto dna flesruoy tcetorP
        !yadot noitaniccav ruoy teg

// Write the same data as RTL (note the X origin of 48):
dp->WriteParagraph ( 1, 48, gs, nc.grR, true, true ) ;
OUTPUT: ?tey noitaniccav 91-divoc ruoy deviecer uoy evaH
                            ,srehto dna flesruoy tcetorP
                             !yadot noitaniccav ruoy teg

// Reload the source data and reverse it without punctuation processing,
// but WITH right-justification padding.
gs = Vaccine ;
gs.textReverse( false, true, true ) ;

// Write reversed text as LTR data.
// Note that the padding character is ASCII space ' ',
// however the '-' character is used here to show the padding position.
dp->WriteParagraph ( 1, 1, gs, nc.grR, true, false ) ;

OUTPUT: ?tey noitaniccav 91-divoc ruoy deviecer uoy evaH
        --------------------,srehto dna flesruoy tcetorP
        ---------------------!yadot noitaniccav ruoy teg

// Write the same data as RTL (note the X origin of 48):
dp->WriteParagraph ( 1, 48, gs, nc.grR, true, true ) ;

OUTPUT: ?tey noitaniccav 91-divoc ruoy deviecer uoy evaH
        --------------------,srehto dna flesruoy tcetorP
        ---------------------!yadot noitaniccav ruoy teg

// Combine RTL text with numeric (LTR) data.
// Create an ASCII numeric time string.
// Reverse the numeric string.
// Insert a Hebrew label: "The current time is: "
const wchar_t *timeLabel = L"השעה הנוכחית היא: " ; //(displayed incorrectly)
short hours = 12, minutes = 32, seconds = 45 ;
gs.compose( "%02hd:%02hd:%02hd", &hours, &minutes, &seconds ) ;
gs.textReverse( false, true, false ) ;
gs.insert( timeLabel ) ;

// Write the data as RTL (note the X origin of 26):
dp->WriteParagraph ( 1, 26, gs, nc.grR, true, true ) ;

OUTPUT:
   12:32:45 :השעה הנוכחית היא

@end verbatim

@H5heading{Technical Note On Punctuation,
           ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔}

Determining which characters are and are not punctuation is locale specific 
(see the @t{'ispunct()'} C-language function).@*
Rather than rely on the locale used by the application calling this method, 
we test against a list of the most common punctuation characters used in 
modern languages. If a punctuation character used by your application is 
not recognized as punctuation, please send us a note including the Unicode 
codepoint and we will add it to the list.
@spi

@anchor{formatParagraph}
@item @b{int32_t formatParagraph ( int32_t maxRows, int32_t maxCols, 
@ifnothtml
@*@verb{|                         |}
@end ifnothtml
bool trunc = true, bool hypenbrk = false, 
@*@verb{|                      |}
@ifnothtml
@verb{|  |}
@end ifnothtml
int32_t *truncIndex = NULL );}

@verbatim
  Input  : maxRows  : maximum rows for message (>= 1)
           maxCols  : maximum columns on any message row (>= 4)
           trunc    : (optional, 'true' by default)
                      if 'true',  truncate the data if necessary to ensure
                                  that the data do not extend beyond the
                                  specified target area
                      if 'false', format the entire source text array, even
                                  if doing so requires violating the specified
                                  height of the target area (maxRows).
                                  (see also 'truncIndex' parameter)
           hyphenbrk: (optional, 'false' by default)
                      if 'false', automatic line breaks occur at space ' '
                                  characters only (20h)
                                  Special Case: the following CJK ideographs
                                  are also recognized as whitespace for
                                  purposes of line formatting (see below)
                                   '、' comma U+3001 and
                                   '。' full stop U+3002
                      if 'true',  in addition to the space ' ' characters,
                                  enable line break at:
                                   ASCII hyphen    '-' (2dh)
                                   Unicode &mdash; '—' (2014h)
                                   Unicode &ndash; '–' (2013h)
                                   Unicode &minus; '−' (2212h)
                                   Unicode &shy;       (00ADh) (soft hyphen)
           truncIndex: (optional, null pointer by default)
                       (referenced _only_ when the 'trunc' flag is reset)
                       If specified, points to a variable to receive the
                       index at which the data _would_have_been_ truncted to
                       fit the specified height of the target area if the
                       'trunc' flag had been set.
                       a) A positive value indicates that the data have not
                          been truncated, AND that the data extend beyond
                          the specified target area.
                       b) A negative value indicates that it was not necessary
                          to truncate the data i.e. the data fit entirely
                          within the target area.

  Returns: number of text rows in the formatted output
@end verbatim
@spi

Reformat the text so that when written to the display, it will fit within 
the specified rectangular area.

The dimensions of the formatted text is specified as the number of text 
rows and columns, with a minimum of one(1) row by four(4) columns.
@sp 2

@H5heading{Technical Description of the Line Break Algorithm,
           ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔}

Formatting is done in three steps:
@enumerate 1
@item Remove all newlines from the text.
@item Perform word wrap to limit the width of each row of the paragraph.
@item If necessary, truncate the text to limit the height of the paragraph. 
(but see the @t{'trunc'} and @t{'truncIndex'} parameters)
@end enumerate

@b{Token:} A @LSQ{}token@RSQ{} as used here is a series of printing characters 
delimited by whitespace characters. If line breaks on hyphen-like characters 
is enabled, those characters are also interpreted as token delimiters.

@enumerate 1
@item Line breaks occur @b{@emph{after}} the space character at the end 
of a token, i.e. a word. This means that the line is broken by placing 
the newline @emph{after} the identified space character.@*
@b{Note:} Currently the ASCII space character (20h) and the two-column 
CJK space (U+3000) are the only whitespace characters recognized.

Special Case: CJK text seldom contains any spaces at all within a sentence, 
or even between sentences. Two-column punctuation is designed to provide a 
visual spacing effect. For this reason, we have made the design decision to 
process the following CJK ideographs as whitespace:@*
@b{@verb{|           '、' comma U+3001  and '。' full stop U+3002|}}

Caution: Tab characters ('\t') _are not_ recognized as whitespace 
characters because they are effectively variable width 
characters. Don't use them!@*
For safety, if Tab characters are present in the text, they are silently removed.

Optionally, the ASCII hyphen and the Unicode hyphen-like characters 
can be treated as if they were whitespace characters for purposes of the 
algorithm. (see the @t{'hyphenbrk'} parameter.) The characters included within 
this group are:@*
@example
@b{ NAME        HTML TAG  GLYPH  UNICODE CODEPOINT}
ASCII hyphen            '-'    U+002D
mDASH        &mdash;    '—'    U+2014
nDASH        &ndash;    '–'    U+2013
minus        &minus;    '−'    U+2212
soft hyphen  &shy;             U+00AD
@end example

@item When reformatting the data, it is possible that the text will be pushed 
beyond the specified number of rows. In this case, we have two options:@*
a) Truncate the text after the last specified row is filled.@*
b) Alert the caller about the number of rows actually required.@*
Optionally, we can indicate the index of where we @b{@emph{would have}} 
truncated the text so that caller can manually truncate the text if desired. 
(see the @t{'trunc'} and @t{'truncIndex'} parameters)

@item It is possible that a single token (word) will be longer than the width 
of the target area. Handling this (unlikely) scenario complicates the 
line-break algorithm, but could come into play; for instance: filespecs, 
URLs, or some German words. :-)

Filespecs and URLs should be parsed using specialized formatting methods.@*
Long words can be a challenge during parsing of the data. Our solution is 
to define @LDQ{}long@RDQ{} tokens as those which are more than half the 
specified area width (@t{'maxCols'}).

This method can optionally break after hyphens, but this may sometimes cause 
unintended breaks or confusing output. Use the @t{'hyphenbrk'} option wisely.

@item Notes on automatic hyphenation:@*
Technically, hyphens should be placed between syllables, but that would require 
a full dictionary of the target language.@*
@LDQ{}Can open... worms everywhere.@RDQ{} (Thank you, Chandler Bing.)
@itemize @minus
@item The hyphen used is the the Unicode &ndash; U+2013. This facilitates 
stripping them from the text if the text is copied and pasted elsewhere.@*
Note that ideally we would use the "soft hyphen," Unicode &shy; U+00AD, but 
unfortunately most non-word procesing applications interpret this as a 
zero-width character making it invisible under most circumstances.
@item Programmer's Note: If the current character is the same width as the 
hyphen, then the hyphen will be at the right edge of the target area. 
Otherwise there will be a one-column gap at the end of the line.
@item Special case: For multi-column characters, it is assumed that the 
characters belong to the CJK character groups. (This may not be true, 
but multi-column characters seldom appear in Romance languages. 
Therefore, for multi-column characters only, hyphens @b{@emph{are not}} 
inserted after mid-token line breaks because there is no way of knowing if 
we are breaking in mid-word or between words unless we have access to 
dictionaries for those languages. Again, @LDQ...worms everywhere.@RDQ{}
@end itemize
@end enumerate

@end itemize
@sp 2


@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.10 Comparisons   - - - - - - - - - - - - - - - - - - - - - - - - -
@c               Compare gString contents to other data  - - - - - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node Comparisons
@unnumbered Comparisons
@GSChIndx{10} Comparisons
@c @cindex 07.10 Comparisons

@itemize @MSQUARE
@item @b{int32_t compare ( const char* uStr, bool casesen = true, 
@ifnothtml
@*
@verb{|                 |}
@end ifnothtml
int32_t length = -1, int32_t offset = 0 ) const;}
@item @b{int32_t compare ( const uint8_t* uStr, bool casesen = true, 
@ifnothtml
@*
@verb{|                 |}
@end ifnothtml
int32_t length = -1, int32_t offset = 0 ) const;}
@item @b{int32_t compare ( const wchar_t* wStr, bool casesen = true, 
@ifnothtml
@*
@verb{|                 |}
@end ifnothtml
int32_t length = -1, int32_t offset = 0 ) const;}
@item @b{int32_t compare ( const gString& gs, bool casesen = true ) const;}

@verbatim
  Input  :
     uStr     : (UTF-8 string) to be compared
       OR
     wStr     : (wchar_t string) to be compared
       OR
     gs       : (by reference) gString object containing
                data to be compared
     casesen  : (optional, 'true' by default)
                if 'true' perform case-sensitive comparison
                if 'false' perform case-insensitive comparison
     length   : (optional, -1 by default. i.e. compare to end)
                maximum number of characters to compare
     offset   : (optional, ZERO by default)
                If specified, equals the character offset into the
                gString character array at which to begin comparison.

  Returns: 
     return value uses the rules of the 'wcsncmp' (or 'wcsncasecmp') 
     library function (see string.h):
       ZERO, text data are identical
     > ZERO, first differing char of gString object is numerically larger.
     < ZERO, first differing char of gString object is numerically smaller.
@end verbatim

Compares the text content of the gString object with the specified text. 

The comparison is performed against the gString objects' wchar_t character 
arrays. The character comparisons are @b{@emph{numerical}}, not locale-based. 
This includes the relationship between upper-case and lower-case characters.@*
For locale-specific comparisons, please @pxref{compcoll} method below.
@spi

@item @b{int32_t compare ( const gString& gs, bool casesen = true ) const ;}@*

@verbatim
  Input  :
     gs       : (by reference) object whose text is to be compared
     casesen  : (optional, 'true' by default)
                if 'true' perform case-sensitive comparison
                if 'false' perform case-insensitive comparison

  Returns:
     return value uses the rules of the 'wcsncmp' (or 'wcsncasecmp') 
     library function (see string.h):
       ZERO, text data are identical
     > ZERO, first differing char of gString object is numerically larger.
     < ZERO, first differing char of gString object is numerically smaller.
@end verbatim

Compares the text content of two gString objects.

The comparison is performed against the gString objects@RSQ{} wchar_t character 
arrays. The character comparisons are @b{@emph{numerical}}, not 
locale-based. This includes the relationship between upper-case and 
lower-case characters.@*
For locale-specific comparisons, please see the @t{compcoll} method below.
@spi

@anchor{compcoll}
@item @b{int32_t compcoll ( const wchar_t* wStr ) const;}@*
@item @b{int32_t compcoll ( const char* uStr ) const;}@*
@item @b{int32_t compcoll ( const gString& bs ) const;}

@verbatim
  Input  : wStr     : (UTF-32 string) to be compared
              OR
           uStr     : (UTF-8 string) to be compared
              OR
           gs       : (gString object by reference) data to be compared

  Returns: using the rules of the 'wcscoll' library function (see wchar.h):
      ZERO, text data are identical
    > ZERO, first differing char of stored data is larger.
    < ZERO, first differing char of stored data is smaller.
@end verbatim

Compares the text content of the gString object with the specified text.

The comparison is performed according to the active "locale" within the 
application. This comparison is known as the @LDQ{}collation order@RDQ{} or 
@LDQ{}dictionary order@RDQ{} comparison where lowercase letters are considered as 
less than their uppercase equivalents. Characters with diacritical markers 
and unitary character sequence (e.g. Spanish @t{'ll'}) will also be sorted 
according to the rules of the active locale.

@enumerate a
@item If the application has set the locale, the rules of that locale will be 
used for the comparison.@*
Note that the comparison is always case sensitive.
@item If the application has not specified the locale, then the so-called 
POSIX C/C++ pseudo-locale will be used.
@item Note that for some languages, Chinese and Japanese for instance, there 
is no "dictionary" order for the characters. Instead, dictionaries rely 
on sound markers, e.g. pinyin transliteration, stroke count, or other 
criteria. The @t{'compcoll'} method is not effective for these languages.
@item Note that if the source data contain mixed languages, the 'wcscoll' 
function will be ineffective and misleading.
@item Technical Note: The 'wcscoll' librarary function is also 
@b{@emph{considerably}} slower than the @t{'wcscmp'} (numeric comparison) 
function because it scans the data multiple times.
@end enumerate
@spi

@display
Please @pxref{how to set locale} for additional information.
@b{Note:} Regardless of source data format, this method uses the 
C-library @t{'wcscoll'} function exclusively to perform the comparison.
@end display
@spi

@indentedblock
@H5heading{Technical Note:,▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔}
In the ASCII world, the letter @t{'A'} is numerically less than the letter 
@t{'a'}; however, in the world of locale-specific comparison, English 
lowercase is @LDQ{}less than@RDQ{} uppercase, so @t{'a'} is less than @t{'A'}.

Examples of string comparison using the collation algorithm:
@example
 'abc'  ==  'abc'
 'abc'   <  'ABC'
 'ABC'   <  'abc'
 'abc'   <  'aBc'
 'abc'   <  'abC'
 'abc'   <  'abcd'
 'abcd'  >  'abC'
 'abcd'  <  'abCd'
 'abC'   <  'abcd'
This would suggest that the collation algoorithm is essentially 
useless unless your project is to create a dictionary.
@end example
@end indentedblock
@spi

@item @b{bool operator == ( const gString& gs2 ) const ;}

@verbatim
  Input  :
     gs2   : (by reference)
             gString object containing string to be compared
  Returns:
     'true' if the strings are identical, else 'false'
@end verbatim

Comparison operator: Compares the text content of two gString objects.

The comparison is performed against the wchar_t character arrays of the 
two objects. The comparison @b{@emph{is}} is case-sensitive.
@sp 2

@item @b{bool operator != ( const gString& gs2 ) const ;}

@verbatim
  Input  :
     gs2   : (by reference)
             gString object containing string to be compared
  Returns:
     'true' if the strings are different, else 'false'
@end verbatim

Comparison operator: Compares the text content of two gString objects.

The comparison is performed against the wchar_t character arrays of the 
two objects. The comparison @b{@emph{is}} case-sensitive.
@spi

@item @b{int32_t find ( const char* src, int32_t offset=0, 
@ifnothtml
@*@verb{|              |}
@end ifnothtml
bool casesen=false, int32_t maxcmp= -1 ) const;}
@item @b{int32_t find ( const wchar_t* src, int32_t offset=0, 
@ifnothtml
@*@verb{|              |}
@end ifnothtml
bool casesen=false, int32_t maxcmp= -1 ) const ;}
@item @b{int32_t find ( const gString& src, int32_t offset=0, 
@ifnothtml
@*@verb{|              |}
@end ifnothtml
bool casesen=false, int32_t maxcmp= -1 ) const;}
@item @b{int32_t find ( const wchar_t src, int32_t offset=0, 
@ifnothtml
@*@verb{|              |}
@end ifnothtml
bool casesen=false ) const;}

@verbatim
  Input  :
     src    : source data to be matched, one of the following:
              -- pointer to a UTF-8 string
              -- pointer to a wchar_t string
              -- a gString object containing the source (by reference)
              -- a single, wchar_t character
     offset : (optional, ZERO by default)
              character index at which to begin search
              -- if out-of-range, then same as if not specified
     casesen: (optional, 'false' by default)
              if 'false', then scan IS NOT case sensitive
              if 'true, then scan IS case sensitive
     maxcmp : (optional, (-1) by default)
              -- if not specified, then scan for a match of all
                 characters in 'src' (not including null terminator)
              -- if specified, then scan for a match of only the first
                 'maxcmp' characters of 'src'
              -- if out-of-range, then same as if not specified
  Returns:
     index of matching substring or (-1) if no match found
     Note: This is the wchar_t character index, NOT a byte index
@end verbatim

Scan the data for a matching substring and if found, return the index at 
which the first substring match begins.

Actual comparison is always performed against the 'wide' character array 
using wcsncasecmp (or wcsncmp). Null terminator character is not included 
in the comparison.
@sp 1

@item @b{int32_t findlast ( const char* src, bool casesen=false ) const;}
@item @b{int32_t findlast ( const wchar_t* src, bool casesen=false ) const;}
@item @b{int32_t findlast ( const gString& src, bool casesen=false ) const;}
@item @b{int32_t findlast ( const wchar_t src, bool casesen=false ) const;}

@verbatim
  Input  :
     src    : source data to be matched, one of the following:
              -- pointer to a UTF-8 string
              -- pointer to a wchar_t string
              -- a gString object containing the source (by reference)
              -- a single, wchar_t character
     casesen: (optional, 'false' by default)
              if 'false', then scan IS NOT case sensitive
              if 'true, then scan IS case sensitive

  Returns:
     index of last matching substring or (-1) if no match found
     Note: This is the wchar_t character index, NOT a byte index
@end verbatim

Scan the data for the @b{last} occurance of the matching substring and if 
found, return the index at which the substring match occurs.

Actual comparison is always performed against the 'wide' character array 
using wcsncasecmp (or wcsncmp). Null terminator character is not included 
in the comparison.
@sp 1

@item @b{int32_t after ( const char* src, int32_t offset=0, 
@ifnothtml
@*@verb{|               |} 
@end ifnothtml
bool casesen=false ) const;}
@item @b{int32_t after ( const wchar_t* src, int32_t offset=0, 
@ifnothtml
@*@verb{|               |} 
@end ifnothtml
bool casesen=false ) const;}
@item @b{int32_t after ( const gString& src, int32_t offset=0, 
@ifnothtml
@*@verb{|               |} 
@end ifnothtml
bool casesen=false ) const;}

@verbatim
  Input  :
     src    : source data to be matched, one of the following:
              -- pointer to a UTF-8 string
              -- pointer to a wchar_t string
              -- a gString object containing the source (by reference)
     offset : (optional, ZERO by default)
              character index at which to begin search
              -- if out-of-range, then same as if not specified
     casesen: (optional, 'false' by default)
              if 'false', then scan IS NOT case sensitive
              if 'true, then scan IS case sensitive
  Returns:
     index of the character which follows the matching substring
       or (-1) if no match found
     Note: This is the wchar_t character index, NOT a byte index
@end verbatim

This method is very similar to the 'find()' method above, but instead of 
returning the index to the beginning of the substring, returns the index 
of the character which @b{FOLLOWS} the substring.
@sp 1

@item @b{int32_t findr ( const gString& src, int32_t offset = -1, 
@ifnothtml
@*@verb{|               |}
@end ifnothtml
bool casesen = false ) const;}
@item @b{int32_t findr ( const wchar_t *src, int32_t offset = -1, 
@ifnothtml
@*@verb{|               |}
@end ifnothtml
bool casesen = false ) const;}
@item @b{int32_t findr ( const char *src, int32_t offset = -1, 
@ifnothtml
@*@verb{|               |}
@end ifnothtml
bool casesen = false ) const;}
@item @b{int32_t findr ( const wchar_t& src, int32_t offset = -1, 
@ifnothtml
@*@verb{|               |}
@end ifnothtml
bool casesen = false ) const;}

@verbatim
  Input  : src    : source data to be matched, one of the following:
                    -- pointer to a UTF-8 string
                    -- pointer to a wchar_t string
                    -- a gString object containing the source (by reference)
                    -- a single, wchar_t character (by reference)
           offset : (optional, by default: index of null terminator minus one)
                    character index at which to begin search
                    -- if out-of-range, then same as if not specified
           casesen: (optional, 'false' by default)
                    if 'false', then scan IS NOT case sensitive
                    if 'true, then scan IS case sensitive

  Returns: index of the first character (closest to head of data) of the 
           matching substring or (-1) if no match found
           Note: This is the wchar_t character index, NOT a byte index
@end verbatim

Scan the data beginning at the specified offset and moving toward the head 
of the string (toward offset zero). Locate the matching substring and if 
found, return the index of the first match.

Actual comparison is always performed against the 'wide' character array 
using wcsncasecmp (or wcsncmp). Null terminator character IS NOT included 
in the comparison.
@sp 1

@item @b{int32_t findx ( wchar_t srcChar = L@t{' '}, int32_t offset=0 ) const;}

@verbatim
  Input  :
     srcChar: (optional, L' ' by default) character to be skipped over
     offset : (optional, ZERO by default)
              if specified, equals the character offset into the 
              character array at which to begin the scan.
  Returns:
     a) If successful, returns the index of first character which 
        DOES NOT match specified character.
     b) If the scan finds no character which is different from the 
        specified character, OR if 'offset' is out of range, OR if the 
        specified character is the null character, the return value 
        indexes the null terminator of the array.
     Note: This is the wchar_t character index, NOT a byte index
@end verbatim

Scan the text and locate the first character which @b{@i{DOES NOT}} match the 
specified character.

This method is used primarily to scan past the end of a sequence of space 
@w{@t{(' ')}} characters (0x0020), but may be used to skip over any sequence 
of a single, repeated character. Note that the character specified must be 
a @LSQ{}wide@RSQ{} (wchar_t) character.

See also the 'scan' method below which scans to the first non-whitespace 
character.

@sp 1

@item @b{int32_t scan ( int32_t offset=0 ) const ;}

@verbatim
  Input  :
     offset : (optional, ZERO by default)
              if specified, equals the character offset into the 
              character array at which to begin the scan.
  Returns:
     a) If successful, returns the index of first non-whitespace 
        character
     b) If the scan finds no non-whitespace character OR if 'offset' 
        is out of range, the return value indexes the null terminator 
        of the array.
     Note: This is the wchar_t character index, NOT a byte index.
@end verbatim

Scans the text array and returns the index of the first non-whitespace 
character found.

@t{   } Whitespace characters are defined as:@*
@t{    0x0020 }single-column ASCII space@*
@t{    0x3000 }two-column CJK space@*
@t{    0x0A   }linefeed character@*
@t{    0x0D   }carriage-return character@*
@t{    0x09   }horizontal-tab character@*
@t{    0x0B   }vertical-tab character@*
@t{    0x0C   }formfeed character@*

@end itemize

@sp 1

@Subsubheading{Examples,........}

@verbatim
gString str1( "Toys for Tots" ) ;
gString str2( "The Tin Drum" ) ;

// compare with UTF-8 string
int result = str1.compare( "Toys for Tots" ) ;

// compare with wchar_t string
int result = str2.compare( L"The Tin Drum" ) ;

// compare gString objects
if ( str1 == str2 ) { /* do stuff */ }
if ( str1 != str2 ) { /* do stuff */ }


gString gs( L"Tiny Tim had a titillating tour of Times Square." ) ;

// find first instance of substring "tim" (not case sensitive)
int tIndex = gs.find( "tim" ) ;
wcout << &gs.gstr()[tIndex] << endl ;
 - - -> Tim had a titillating tour of Times Square.

// find the next instance of "tim"
gString gsSub( "tim" ) ;      // search string in a gString object
tIndex = gs.find( gsSub, tIndex + 1 ) ;
wcout << &gs.gstr()[tIndex] << endl ;
 - - -> Times Square.

// find first instance of substring "ti" (case sensitive)
tIndex = gs.find( L"ti", 0, true ) ;
wcout << &gs.gstr()[tIndex] << endl ;
 - - -> titillating tour of Times Square.

// match the first three characters of the search string
tIndex = gs.find( L"squirrel", 0, false, 3 ) ;
wcout << &gs.gstr()[tIndex] << endl ;
 - - -> Square.

// find first instance of L'R' (not case sensitive)
tIndex = gs.find( L'R' ) ;
wcout << &gs.gstr()[tIndex] << endl ;
 - - -> r of Times Square.

// extract the filename from path/filename string
gString gs( "/home/sam/SoftwareDesign/NcDialog/Dialog1/gString.hpp" ) ;
if ( (tIndex = gs.findlast( L'/' )) >= 0 )
   wcout << &gs.gstr()[tIndex + 1] << endl ;
 - - -> gString.hpp

// insert text after first instance of substring
gString gs( "I think that a parrot would be an ideal pet." ) ;
int pIndex = gs.after( L"would" ) ;
gs.insert( " NOT", pIndex ) ;
 - - -> I think that a parrot would NOT be an ideal pet.
@end verbatim

For more examples of using gString-class methods, please refer to 
Test #6 of the 'Dialogw' test application.@*
@ifclear GSTRING_STANDALONE
@xref{Dialogw App}.
@end ifclear

@sp 2


@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.11 Extract Formatted Data  - - - - - - - - - - - - - - - - - - - -
@c               Scan data and extract formatted info (swscanf)  - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node Extract Formatted Data
@unnumbered Extract Formatted Data
@GSChIndx{11} Extract Formatted Data
@c @cindex 07.11 Extract Formatted Data
@cindex swscanf emulation

@itemize @MSQUARE
@item @b{int32_t gscanf ( const wchar_t* fmt, ... ) const ;}@*
@item @b{int32_t gscanf ( const char* fmt, ... ) const ;}

@verbatim
  Input  :
     fmt  : a format specification template in the style of swscanf()
            or sscanf() and related C/C++ functions
            Template may be either a const char* OR a const wchar_t*

     ...  : optional arguments
            Each optional argument is a POINTER to (address of) the
            variable to receive the formatted data.
            - Important Note: There must be AT LEAST as many optional
              arguments as the number of format specifiers defined in
              the formatting template. Excess arguments will be
              ignored; however, too few arguments will return an
              error condition. (see below)

  Returns: 
     number of items captured and converted
     returns 0 if:
       a) number of format specifications in 'fmt' > gsFormMAXARGS
       b) number of format specifications in 'fmt' > number of
          optional arguments (pointers to target variables) provided
@end verbatim

Scan the text data contained in the gString object and extract data 
according to the specified formatting template.@*
(This is an implementation of the standard C-library 'swscanf' function.)

The formatting template may be either a @t{'const wchar_t*'} as 
in a call to @t{'swscanf'}, @b{@emph{or}} a @t{'const char*'} as in a call 
to @t{'sscanf'}.

The formatting template may contain between zero(0) and gsFormMAXARGS 
format specifiers.

The optional arguments are @b{@emph{pointers}} to the target variables 
which will receive the formatted data.

As with @t{'swscanf'}, the number of optional arguments must be equal 
to or greater than the number of format specifiers:@*
@verb{|      (%d, %63s, %X %lld, %24lls, %[, etc.).|}@*
Excess arguments will be ignored; however, unlike the @t{'swscanf'} function, 
@t{'gscanf'} counts the number of format specifiers and optional arguments, 
and if there are too few arguments, the scan will be aborted to avoid an 
application crash due to memory-access violation. @i{(You're welcome :-)}
@spi

@item @b{int32_t gscanf ( int32_t offset, const wchar_t* fmt, ... ) const ;}@*
@item @b{int32_t gscanf ( int32_t offset, const char* fmt, ... ) const ;}

@verbatim
  Input  :
     offset: wide-character offset at which to begin the scan
             Important Note: This IS NOT a byte offset.
             If offset < 0 || offset > length of data, offset will
             be silently set to 0.

     fmt   : a format specification template in the style of swscanf()
             or sscanf() and related C/C++ functions
             Template may be either a const char* OR a const wchar_t*

     ...   : optional arguments
             Each optional argument is a POINTER to (address of) the
             variable to receive the formatted data.
             - Important Note: There must be AT LEAST as many optional
               arguments as the number of format specifiers defined in
               the formatting template. Excess arguments will be
               ignored; however, too few arguments will return an
               error condition. (see below)

  Returns: 
     number of items captured and converted
     returns 0 if:
       a) number of format specifications in 'fmt' > gsFormMAXARGS
       b) number of format specifications in 'fmt' > number of
          optional arguments (pointers to target variables) provided
@end verbatim

This is the same method as described above @b{@emph{except}} that 
the scan of the data begins at the specified character offset.
@spi

@Subsubheading{Examples,........}

@verbatim
int  sval1, sval2, sval3, sval4 ;
int  ival ;
long long int llval ;
double dval ;
char   str1[64], str2[16], str3[16], ch1 ;
gString gs( "17 % 18 21 22 48A2B 720451 24.325 A country song "
            "is three chords and the truth. - Willie Nelson" ) ;
int32_t cnt = 
   gs.gscanf( L"%d %% %d %d %d %X %lld %lf %63[^-] %c %16s %16s", 
              &sval1, &sval2, &sval3, &sval4, &ival, &llval, 
              &dval, str1, &ch1, str2, str3 ) ;

gString gsOut( "items    : %d\n"
               "numeric  : %d  %d  %d  %d  0x%04X  %lld  %4.6lf\n"
               "text data: \"%s\" %c %s %s\n",
               &cnt, &sval1, &sval2, &sval3, &sval4, &ival, &llval, &dval, 
               str1, &ch1, str2, str3 ) ;
dp->WriteParagraph ( 1, 1, gsOut, dColor ) ;

This yields:
items    : 11
numeric  : 17  18  21  22  0x48A2B  720451  24.325000
text data: "A country song is three chords and the truth. " - Willie Nelson

A working example may be found in the NcDialog API package, 
(Dialogw test application, Test #6).

 - - - - -

To begin the scan from a particular offset into the text, the unneeded 
initial data may either be scanned into a throw-away buffer or the 
scan offset may be specified directly. For example, to discard the 
first sixteen(16) characters:

wchar_t junk[gsALLOCMIN] ;
gs = "Useless garbage: 17 21 34" ;

gs.gscanf( L"%16C %d %d %d", junk, &sval1, &sval2, &sval3 ) ;
  _or_
gs.gscanf ( 16, L"%d %d %d", &sval1, &sval2, &sval3 ) ;

@end verbatim

@format
@b{Programmer's Note:} It is good practice to specify a maximum length 
for all scanned strings in order to avoid overrun of the target buffer.
@verb{|    THIS: %64s   %128lls   %256S   %32[a-z]|}
@verb{|NOT THIS: %s     %lls      %S      %[a-z]|}
@end format

@end itemize
@sp 2


@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.12 Statistical Info  - - - - - - - - - - - - - - - - - - - - - - -
@c               Report statistics on gString contents   - - - - - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node Statistical Info
@unnumbered Statistical Info
@GSChIndx{12} Statistical Info
@c @cindex 07.12 Statistical Info

@itemize @MSQUARE
@item @b{int32_t gString::gschars ( void ) const ;}

@verbatim
  Input  :
     none
  Returns:
     number of characters in the string
@end verbatim

Returns the number of characters in the string including 
the null terminator.
@spi

@item @b{int32_t utfbytes ( void ) const ;}

@verbatim
  Input  :
     none
  Returns:
     number of bytes in UTF-8 string
@end verbatim

Returns the number of bytes in the UTF-8-encoded string including 
the null terminator.
@spi

@item @b{int32_t gscols ( void ) const ;}

@verbatim
 Input  :
    none
 Returns:
    number of columns needed to display the data
@end verbatim

Returns the number of columns required to display the string.
@spi

@item @b{const short* gscols ( short& charCount ) const ;}

@verbatim
  Input  :
     charCount : (by reference, initial value ignored)
                 on return, contains number of characters in 
                 the string including the null terminator.
  Returns:
     pointer to number of columns needed for display of each character
@end verbatim

Returns a pointer to an array of column counts, one for each character 
of the text data. Note that number of array elements equals number of 
characters (plus meta-characters, if any).

@Subsubheading{Example,.......}

@verbatim
This example is from the FileMangler utility. It trims the head of 
the provided path/filename string to fit within a dialog window.

void FmConfig::ecoPathFit ( gString& gsPath, short colsAvail )
{
   if ( (gsPath.gscols()) > colsAvail )
   {
      gString gst = gsPath ;
      int32_t width = gst.gscols(),
              offset = ZERO,
              charCount ;
      const short* colArray = gst.gscols( charCount ) ;

      while ( width > (colsAvail - 3) )
         width -= colArray[offset++] ;
      gsPath.compose( L"...%S", &gst.gstr()[offset] ) ;
   }
}  //* End ecoPathFit() *
@end verbatim
@spi

@item @b{bool isASCII ( void ) ;}

@verbatim
  Input  :
     none
  Returns:
     'true' if data are pure, 7-bit ASCII, else 'false'
@end verbatim

Scan the data to determine whether it is pure, 7-bit ASCII.
@end itemize
@sp 2


@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.13 gString Miscellaneous - - - - - - - - - - - - - - - - - - - - -
@c               Get class version, reinitialize data  - - - - - - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node gString Miscellaneous
@unnumbered gString Miscellaneous
@GSChIndx{13} gString Miscellaneous
@c @cindex 07.13 gString Miscellaneous

@itemize @MSQUARE
@item @b{void gString::clear ( void );}

@verbatim
  Input  : none
  Returns: nothing
@end verbatim

Reset contents to an empty string i.e. @t{""}. The data will consist of 
a single, NULLCHAR character @t{(L'\0')}.@*
The character and byte counts are set to 1 (one), and the column count is zero.
@spi

@item @b{int32_t wAlloc ( void ) const;}
@item @b{int32_t uAlloc ( void ) const;}
@verbatim
  Input  : none

  Returns: size of dynamic storage allocation in wchar_t words
           OR
           size of dynamic storage allocation in uint8_t (char) bytes
@end verbatim

Report data storage allocation for the gString object:@*
Note that this the maximum UTF-32 (wchar_t) characters,@*
(or UTF-8 byte) capacity, NOT the current number of@*
characters or bytes stored.@*
@verb{|  wAlloc :|} returns the storage allocation for@*
@verb{|          |} UTF-32 (wchar_t) characters@*
@verb{|  uAlloc :|} returns the storage allocation for UTF-8 bytes
@spi

@item @b{int32_t freeSpace ( void ) const;}
@verbatim
  Input  : none

  Returns: available buffer space
           Note: If zero(0) is returned, data may have been
                 truncated to fit.
@end verbatim

Reports the amount of free buffer space available, expressed as a count of 
UTF-32 (wchar_t) characters.
@spi

@item @b{int32_t reAlloc ( int32_t steps, bool clear = false );}
@verbatim
  Input  :
     steps : number of steps to increase or decrease in buffer size.
             Range: +/-999
             If value out of range, will be set to upper/lower limit.
     clear : (optional, 'false' by default)
             if 'false', if possible, retain the existing contents
                         of the buffer. (Note that if buffer size is
                         reduced, some data may be truncated.)
             if 'true',  erase any existing text in the buffer
                         (same as 'clear' method, above)

  Returns: new buffer capacity expressed as the number of wchar_t characters
@end verbatim

Increase or decrease the size of storage buffer by the specified number 
of gsALLOCMIN increments.

The buffer is sized in multiples of gsALLOCMIN in the range:@*
@verb{|         gsALLOCMIN <= size <= gsALLOCMAX|}@*
@verb{|         1024 chars            1,024,000 chars|}
@spi

@item @b{const char* Get_gString_Version ( void ) const ;}
@verbatim
  Input  :
     none
  Returns:
     pointer to version string
@end verbatim

Return a pointer to gString class version number string.
@spi

@item @b{void gString::dbMsg ( gString& gsmsg ) const;}
@verbatim
  Input  :
     gsmsg : (caller's object, by reference)
             receives most recent debug message
  Returns:
     nothing
@end verbatim

FOR DEBUGGING ONLY! Application can retrieve most recent debug message.@*
Note: This method is visible only if the DEBUG_GSTRING flag is set 
in gString.hpp.
@spi

@item @b{@verb{|void dumpGstring (|} const wchar_t*& gstr, const char*& ustr, @*
@verb{|                  |} const short*& cwid, int32_t& gsw,@*
@verb{|                  |} int32_t& gsu, int32_t& bsch, int32_t& bsco,@*
@verb{|                  |} int32_t& utfb, bool& isas ) const;}
@verbatim
  Input  :
     gstr   : array of wchar_t characters
     ustr   : array of UTF-8 bytes
     cwid   : array of column widths
     gsw    : size of gstr array
     gsu    : size of ustr array
     bsco   : number of display columns required for stored data
     bsch   : number of wchar_t characters stored (incl. nullchar)
     utfb   : number of UTF-8 bytes stored (incl.nullchar)
     isas   : set if stored data are pure ASCII data

  Returns: nothing
@end verbatim

FOR DEBUGGING ONLY! Get a snapshot of the private gString data members.@*
Note: This method is visible only if the DEBUG_GSTRING flag is set 
in gString.hpp.
@end itemize
@sp 2


@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.14 gString Examples  - - - - - - - - - - - - - - - - - - - - - - -
@c               Examples of using gString methods   - - - - - - - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node gString Examples
@unnumbered gString Examples
@GSChIndx{14} gString Examples
@c @cindex 07.14 gString Examples

The NcDialog API test application 'Dialogw' contains @emph{extensive} 
examples of gString usage, including working copies of the examples 
used in this chapter.@*
@ifclear GSTRING_STANDALONE
@xref{Test Applications, Dialogw App}.@*
@end ifclear
The other NcDialog API test applications also use gString in various ways.

Here, we show just a sample of some basic uses for the gString class.

@enumerate 1
@item
Convert a UTF-8 (8-bit) character string to a wchar_t (32-bit) 
character string.

@verbatim
const char* some_UTF-8_data = "I want to buy an hamburger." ;
wchar_t some_wide_data[gsALLOCDFLT] ;

gString gs( some_UTF-8_data ) ;
gs.copy( some_wide_data, gsALLOCDFLT ) ;
@end verbatim

@item
Convert a wchar_t (32-bit) character string to a UTF-8 (8-bit) 
character string.

@verbatim
const wchar_t* some_wide_data = L"I want to buy an hamburger." ;
char some_UTF-8_data[gsALLOCDFLT] ;

gString gs( some_wide_data ) ;
gs.copy( some_UTF-8_data, gsALLOCDFLT ) ;
@end verbatim

@item
Concatenate strings.

@verbatim
const char* Head = "Where" ;
const wchar_t* Tail = L"is Carmen Sandiego?" ;
gString gs( L" in the world " ) ;
gs.insert( Head, ZERO ) ;
gs.append( Tail ) ;
wcout << gs << endl ;
 - - ->  Where in the world is Carmen Sandiego?
@end verbatim

@item
Create formatted string data.

@verbatim
const char* utf8String = "We present" ;
const wchar_t* wideString = L"for your enjoyment:" ;
const char utf8Char = 'W' ;
const wchar_t wideChar = L'C' ;
short int ways = 100 ;
double dish = 17.57 ;
gString gs ;

gs.compose( "%s %S %hd %cays to %Cook %Chicken,\n"
            "and %.2lf side dishes and beverages!",
            utf8String,
            wideString,
            &ways,
            &utf8Char,
            &wideChar, &wideChar,
            &dish ) ;

wcout << gs << endl ;
 - - ->  We present for your enjoyment: 100 Ways to Cook Chicken,
         and 17.57 side dishes and beverages!
@end verbatim

@ifnothtml
@verbatim
  ┌────────────────────────────────────────────────────────────┐
  │ Important Note: All parameters are _pointers_ to the data: │
  │ For strings (and pointers), the _address_ is the name of   │
  │ the variable. For all other data types, including single   │
  │ characters, use the address-of ('&') operator.             │
  └────────────────────────────────────────────────────────────┘
@end verbatim
@end ifnothtml
@ifhtml
@indentedblock
@CartHtml{
Important Note: All parameters are @b{@emph{pointers}} to the data:
For strings (and pointers), the @b{@emph{address}} is the name of
the variable. For all other data types, including single 
characters, use the @emph{address-of} @b{@t{('&')}} operator.}
@end indentedblock
@end ifhtml

@item
Count display columns to make data fit the window.

This is a formatting method taken from the 'Dialogx' test 
application. It breaks a text stream into lines which fit within 
the dianostic window. It's not speedy (or particularly smart), but 
it demonstrates the use of 'gString' to calculate the space needed 
to display text data and then formatting the data to fit the space.

@verbatim
//*  FormatOutput   *
//* Input  : gsOut     : semi-formatted source data
//*          wpos      : start position for display
//*          lineCount : maximum display lines before truncation
//* Returns: nothing

void dXClip::FormatOutput ( const gString& gsOut, 
                            winPos& wpos, int32_t lineCount )
{
   int tWidth = (ddCOLS - 2),        // inside width of target window
       maxWidth = tWidth * lineCount,// max columns for message
       tLines = ZERO ;               // loop counter

   gString gsw( gsOut.gstr() ), gso ;
   if ( gsw.gscols() > maxWidth )
   { // truncate the string if necessary
      gsw.limitCols ( maxWidth - 3 ) ;
      gsw.append( L"..." ) ;
   }
   do
   {  // break the source into convenient widths
      gso = gsw ;
      gso.limitCols( tWidth ) ;
      gso.append( L'\n' ) ;
      gsw.shiftCols( -tWidth ) ;
      this->dpd->ClearLine ( wpos.ypos ) ; // clear target display line
      wpos = this->dpd->WriteParagraph ( wpos, gso, 
                                         dxPtr->dColor, true ) ;
   }
   while ( ++tLines <= lineCount && gsw.gschars() > 1 ) ;

}  //* End FormatOutput() *
@end verbatim

The example above is just a simple demonstration of using column widths. 
To see it in action, please refer to the 'Dialogw' NcDialog API Test 
Application, test seven (7).@*
@ifclear GSTRING_STANDALONE
@xref{Test Applications, Dialogw App}.@* 
@end ifclear

For a more sophisticated text-parsing algorithm, @pxref{formatParagraph} which 
parses text data by @b{@emph{token}} rather than simply counting columns.
@end enumerate
@sp 2


@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@c Chapter 07.15 gsmeter Test App  - - - - - - - - - - - - - - - - - - - - - - -
@c               Testing gString functionality - - - - - - - - - - - - - - - - -
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node gsmeter Test App
@unnumbered gsmeter Test App
@GSChIndx{15} gsmeter Test App
@c @cindex 07.15 gsmeter Test App

@indentedblock
@multitable {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} 
@headitem @ @ @ @ @ @ @ @ @ @ @ @ GSMETER @ @ @ @ @ @ @ @ @ @ @ @ 
@item @ @ @ @ @ @ref{gsmeter Introduction} @ @ @ @ @ 
@item @ @ @ @ @ @ref{gsmeter Options} @ @ @ @ @ 
@item @ @ @ @ @ @ref{Building gsmeter} @ @ @ @ @ 
@end multitable
@end indentedblock

@anchor{gsmeter Introduction}
@Subheading{'gsmeter' Introduction,--------------------}

The original design of the gString class was based upon a fixed buffer size 
of @t{PATH_MAX}, which is the maximum length of a file specification under 
the POSIX standard. This is is defined as s GNU compiler constant, (4096 bytes), 
which is greater-than-or-equal-to the number of bytes (or characters), 
depending on the character set used for filespecs.@* 
This buffer size was chosen specifically to balance storage capacity with 
performance. The original gString design has performed far beyond expectations 
for over fifteen years, and it is expected to meet or exceed most daily needs 
of applications into the future.

In October of 2024, a beta-test branch of the gString class, @b{@t{bString}} 
was created. The design critera for this class were to retain the original 
functionality of the gString class with as few modifications as possible, 
while converting the static character buffers to dynamically-resized character 
and byte buffers.

This compares favorably with std::string and std::wstring which are cute in 
their own way and semi-functional; but @b{@emph{seriously}} overweight. While 
it is not our intention to be fat-shaming the C++ language, std::string 
support for text data is functionally inconvenient, entirely too English-centric 
and practically-speaking, is often mis-used (or ignored completely) by 
developers due to its opaque implementation, which is based on a solid idea, 
but clearly, was designed by committee.

Because this new design touches every aspect of the original gString 
functionality, the @t{'@b{gsmeter}'} test utility was created to perform 
exhaustive regression testing, with a special focus on the algorithms 
for allocation and re-allocation of dynamic memory.

The beta branch has now been re-integrated into the mainline gString class, 
so the test application is now being included as part of the gString 
distribution. Have fun!
@spi
@hr40

@anchor{gsmeter Options}
@Subheading{'gsmeter' Command-line Options,
            ------------------------------}

While this application lacks professional polish, it does provide a simple 
menu structure and the usual @b{@t{--help}} and @b{@t{--version}} options.

@verbatim
gsmeter: version:0.0.05  gString v:0.0.37 - (c) 2024-2025 The Software Samurai
------------------------------------------------------------------------------
Test Suite for gString Class  Usage: gsmeter TEST [SUBTEST]
   C[a|b] --------- Constructors  :  S ------- Statistics   
   I[a|b] --------- Initializers  :  O ------- Output group 
   M[a|b|c|d]------ Modifiers     :  B[a|b] -- Buffer size 
   A[a|b|c|d|e|f]-- Analyzers     :  R[a|b] -- Reallocation
   --help  --version              :  P ------- Playground

      Example Invocations:
         gsmeter              # No arguments, invokes the options menu
         gsmeter Ca           # Constructor Group, section 'a'
         gsmeter Ac           # Analyzer Group, section 'c'
         gsmeter --version    # Report version numbers, current locale
                              # and copyright info
@end verbatim

The tests are grouped according to functionality, and are sub-divided into 
smaller groups so that each test run will fit within one terminal window of 
39 rows x 150 columns.

There are seven(7) test groups with eighteen(18) tests, 
plus a @LDQ{}@emph{Sandbox}@RDQ{} for creating ad-hoc tests.

@Subsubheading{Test Groups,...........}
@enumerate 1
@item Constructors
@example
A: Constructors A, default, specify allocation, char*, 
   wchar_t*, format specification
B: Constructors B: integer-formatting constructors
@end example
@item Initializers
@example
A: Initializers A, and basic reporting methods: 
   wAlloc(), uAlloc(), gstr(), ustr(), gschars(), uftbytes(), gscols(), 
   isASCII(), operator=, loadChars(), formatInt()
B: Initializers B: compose(format, ...), compose(binary extension)
@end example
@item Modifiers
@example
A: append(), limitChars(), limitCols(), insert(), erase()
B: replace(), shiftChars(), shiftCols()
C: rightJustify(), padCols(), strip(), textReverse()
D: formatParagraph(), clear()
@end example
@item Analyzers
@example
A: operator==, operator!=, compare(gString&), compare()
B: compcoll(), find()
C: findlast(), findx(), scan()
D: after()
E: findr()
F: gscanf()
@end example
@item Statistics
@example
Info Dump, Public Stats, Character Widths, Class Version Number
@end example
@item Output group
@example
"operator<<" for both UTF-32 (wide) and UTF-8 (narrow) streams
@end example
@item Reallocation tests
@example
A: Reallocation tests, Group A
B: Reallocation tests, Group B
@end example
@item Buffer Size/Resize (attempted overflow) tests
@example
A: Constructors and Assignments Overflow tests
B: Insert and Append Overflow tests
@end example
@end enumerate

Reporting of test results is split between the terminal window and a 
file @t{(@b{gsmdbg.txt})} created in the current working directory.
@spi

@H5heading{Note on Application Locale,
           ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔}
@b{@t{gsmeter}} automatically enables the @LDQ{}locale@RDQ{} specified in 
the terminal environment. While the gString class is written to be 
locale-independent, some of the @t{gsmeter} tests rely upon the locale to 
function properly.@*
Specifically, the gString @b{@t{compcoll}} (compare-collated) method invokes 
the locale-aware C-language function,@t{'wcscoll'}. This function uses a 
dictonary-style comparison which is dependent on the language and locale 
involved. Please @pxref{compcoll} method for details.
@spi

@hr40

@anchor{Building gsmeter}
@Subheading{Building 'gsmeter',------------------}

@t{gsmeter} is a very simple application, so building it is also simple.

There are four(4) ways to invoke the @b{@t{Makefile}}:
@example
@b{gmake}            # compile the application module and link
                     # to the existing gString library

@b{gmake buildlib}   # delete the existing gString.lib link library
                     # and rebuild it

@b{gmake clean}      # Remove the object files, the gString library
                     # and the executable

@b{gmake all}        # Execute the 'clean', 'buildlib' and
                     # 'gmake' options
@end example


@ifnothtml
@example
┌───────────────────────────────────────────────────────────────┐
│ Important Note: Some tests include a snapshot of the gString  │
│ object's internal configuration. This report requires that a  │
│ conditional-compile declaration be enabled:                   │
│       #define DEBUG_GSTRING (1)                               │
│ This declaration is located near the top of gString.hpp.      │
│ This declaration should be enabled only for use by gsmeter,   │
│ and should be disabled for all other applications.            │
│       #define DEBUG_GSTRING (0)                               │
└───────────────────────────────────────────────────────────────┘
@end example
@end ifnothtml
@ifhtml
@indentedblock
@indentedblock
@CartHtml{@embedTOKEN{ai}
@b{Important Note:} Some tests include a snapshot of the gString object's 
internal configuration. This report requires that a conditional-compile 
declaration be enabled:  @b{#define DEBUG_GSTRING (1)}
This declaration is located near the top of gString.hpp.
This declaration should be enabled only for use by @t{gsmeter}, and should 
be disabled for all other applications. @b{#define DEBUG_GSTRING (0)}
}
@end indentedblock
@end indentedblock
@end ifhtml
@raisesections
@sp 3


