Salmon Spawning A Process Via Exec Functions

Next: Introduction [Contents][Index]

# Exec Library Functions - Tutorial (salmon)

Copyright © 2012-2026 Mahlon R. Smith, The Software Samurai This manual describes version 0.0.05 of ’salmon’. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".

## Table of Contents

## Introduction

The `'Salmon'` application is a GNU/Linux console (command-line) utility which demonstrates some common examples of spawning an external process from within an application.

Because this is a demonstration program only, the logic is straightforward but not comprehensive. A production-worthy application will give much more attention to possible error conditions. Also, the documentation (this document) is quite simple, but we hope you will find it useful.

Also, we apologize for the application name, but we couldn’t resist. This project was written strictly for fun. “Salmon” swim upstream to spawn fish eggs, not process `IDs`, but we hope you get the idea.

Note to novice programmers:

This application is written in C++, but it discusses various C-language primitive functions. When we use the word `"function,"` we mean a C-language routine. When we use the word "method," we mean a C++ routine. This difference in terminology may seem to be a small one, but wars have been fought over it. (We are geeks, after all.) Since Software Sam is a teacher, prepare yourself for a boring explanation:
A C-language "function" does something (moves the program flow from point A to point B), while a C++ “method” accomplishes something (solves a problem) within a class or object. This is tied to the concept of object-oriented programming as contrasted with the so-called “structured” programming that was common in the days when dinosaurs roamed the earth. (And that doesn’t begin to describe the spaghetti code written by the Neanderthals in the UNIX world!)

The "Exec" Family Of Functions

1. This application is intended as a reference for students and others who want to understand the use of the `"exec"` group of primitive functions and other functions related to spawing a child process from within a parent application.

   The Salmon package includes source code for two executable binary files, `'Salmon'` and `'childApp'.` Salmon contains the interesting code which invokes `childApp` with various arguments. `childApp` simply reports the arguments it has received from the controlling application, then termnates.

2. The `"exec"` function group invokes an external program.
   1. If the `exec` call is successful, then the process `ID` used to invoke the external program will terminate when the called application or script exits.
   2. If the calling `PID` is the only process `ID` running in the calling application, then the calling application terminates immediately when the `'exec'` function is called.
   3. If multiple process `IDs` are active when the external application is called, then only the process which executes the `'exec'` function is detached from the calling application and will eventually terminate while the remaining processes will continue to execute within the calling application. See the description of the `"fork"` function, below for more information.
   4. An external application could be a web browser, email client, image viewer, etc. It may or may not write to the terminal window from which it was launched. If it does, it will overwrite the display data of whatever application is already running in that window, so be aware, and capture the output if necessary.
3. Technical Note: Do not confuse the creation of multiple, independent processes with the concept of multithreading. Processes and threads have significant differences in terms of resource allocation and scope of access. To perform multiple tasks within a single application, we strongly recommend multithreading rather than creating multiple processes.

   Multithreading is an operation in which multiple execution threads (paths of execution) are created, and each is launched into its own private method to perform a specific task. The creation, use of and termination of programatic threads is not covered in this example application.
   Please see our `"Sew"` (as in “Sew, a needle pulling thread”) application for a demonstration of multithreading.

4. The `"fork"` function creates a split in a program’s flow of execution.
   1. The `"fork"` group of functions is used to create a child process within the calling application. For the "fork" function, the parent and child processes initially have identical copies of the application environment (the `"process image"`). See the `"fork"` function documentation (`info fork`) for exactly what is included in this process image.

      Note that for this application, we want the forked process to immediately execute an external process. For this reason, we use the `"vfork"` function rather than `"fork"` because `"vfork"` does not create an unnecessary copy of the process image which will be immediately discarded anyway. Instead, the `"vfork"` function allows the parent and child to share the single process image for the short time they are running concurrently in the parent application.

   2. The parent process continues to execute the calling application.
      1. This process may immediately continue, OR
      2. the process may sleep using the `"waitpid"` function until the child process terminates.
5. The child process also continues to execute within the calling application; however, its actions must be strictly limited to avoid destruction of the calling application’s data and environment.
   1. When an application spawns a child process using `"vfork"`, that process needs something to do. That “something” must not interfere with the operation of the parent process.
      1. It may not modify the environment (with certain exceptions)
      2. It may not change variable values (with certain exceptions)
      3. It may not gather input nor produce (asynchronous) output which conflicts with the parent process.
      4. Perhaps most importantly, the child may not allocate or release stack space. In practical terms, this means that the child process must NEVER return from the method in which it was spawned.
   2. Resource conflicts for the input/output streams, `'stdin', 'stdout'` and `'stderr'` may occur between the parent and child processes. To avoid resource conflicts with console applications or scripts launched by the child process:
      1. The child process will avoid capture of input and will produce no output to the standard streams, `'stdout'` and `'stderr'`. Note that this is not always practical, and must be handled carefully. Either the called executable was written by you, and is therefore guaranteed to generate no `I/O` to the shared streams, or it must have a `"silent mode"`, similar to our `'cTrash'` utility in which all I/O is redirected to either temporary files or to a block of static memory. (see `"dup2"` below)
      2. The parent process sleeps until the child process terminates.
         See the `'-f'` option (Invoking) for an example.
      3. Input (stdin) and output (stdout and stderr) for the child process may be redirected, which allows the parent process to retain control of stdin, stdout and stderr.
         See the `'-s'` option (Invoking) for an example.
         - Typically, any startup messages of an external (non-console) application can be discarded by redirecting `'stdout'` and `'stderr'` to temporary files.
           This redirection can be accomplished by using the `"dup2"` function.
         - The `"dup2"` redirection method may also be used to capture any output produced by the child process which may be needed later.
         - For simple external calls when the output is not needed, the output can be redirected to the “bit bucket”.
           The bit bucket consists of a device, `'/dev/null'` which simply discards whatever is sent to it. Note that this is a shared resource.

           `Command-line Example:`
           `echo $PWD 1>/dev/null 2>/dev/null`
           `Example using 'system' function call:`
           `system ( "echo $PWD 1>/dev/null 2>/dev/null" ) ;`

      4. The child process could be launched in a new terminal window.
         Note that this is an advanced operation which may not be transparently supported by all terminal emulators and/or shell programs.
         See the `'-t'` option (Invoking) for further discussion and examples.
6. When specifying the location of the application called through one of the `'exec'` functions, the first argument is one of the following:

         1) A relative filespec:
            execv ( "./childApp", ... ) ;
         2) An absolute filespec:
            execv ( "/home/sam/SoftwareDevelopment/Salmon/childApp", ... ) ;
         3) The name of a file which can be found on the system $PATH:
            execv ( "grep", ... ) ;

   In the `Salmon` test application, both the calling app, `'salmon'` and the called app, `'childApp'` are located in the same directory, so a relative filespec would work as expected so long as `'salmon'` is invoked within the currrent working directory (CWD).
   `[Salmon]$` `./salmon -v`
   In the real world, however, things are not always so simple. For this reason, an absolute filespec is used, so that even if `'salmon'` is not in the CWD, `'childApp'` will still be found. For example:
   `[Documents]$` `../SoftwareDesign/Salmon/salmon -v`
   To understand how this is done, please examine the `getBasePath()` method in `Salmon.cpp`.

## Invoking

`./salmon -[v | l | p | f | s | t | D] [ARG1[ ARG2[ ARG3[ ARG4]]]]`
`| [--version | --help]`

### Required Options

To invoke the application, specify one of the required options. plus zero or more optional arguments.

Examples:
`./salmon -v`
`./salmon -t 80 25`
`./salmon --version`

If no options are specified, the `"--help"` option is the default.

### Optional Arguments ARG1 through ARG4

These optional comand-line arguments are recognized by the `'-v', '-l', '-p', '-f'` and `'-t'` options.

For tests `'-v', '-l'` and `'-f'`, the specified arguments will simply be passed to the child application (`childApp`) which will then display them to verify that they were passed successfully:

    ./salmon -l 'catsup is red' 'mustard is yellow' 'pickles are green' 'onions stink!'

        will yield:

    Salmon Swim Upstream To Spawn!
     Invocation: "./salmon -l 'catsup is red' 'mustard is yellow' 'pickles are green' 'onions stink!'"
     Parent PID: 6201
     calling execl( childApp ... )

     childApp: PID: 6201
     ====================
     argv[0] childApp
     argv[1] -l
     argv[2] catsup is red
     argv[3] mustard is yellow
     argv[4] pickles are green
     argv[5] onions stink!

Test `'-p'` calls the `'grep'` utility to scan for a specific text string within a specific source file. Therefore, any optional arguments provided will be interpreted as arguments for invoking `'grep'`. See the example below.

Test `'-t'` interprets any optional arguments as parameters for configuring the launch of a child window. See the example below.

### Invocation Options

- `-v` execv() function
  Pass an argv[] array to childApp (calling application terminates).

  The `'v'` in `"execv"` indicates the `argv[]` vector array, and as a software designer, you may be aware that this is an array of char pointers:
  `char const* argv[]`
  with the last pointer in the array being the NULL pointer.

  Example: -------- const char* fileName = "./childApp" ; char const* argV[] = { "This is argv[0]", // this should be the bare filename of // the target (no path component) "This is argv[1]", // additional arguments . . . "This is argv[2]", "This is argv[3]", "This is argv[4]", NULL // null pointer terminates the array } ; execv ( fileName, argV ) ;
- `-l` execl() function
  Pass a list of individual arguments to childApp (calling application terminates).

  The `'l'` in `"execl"` indicates a list (sequence) of individual arguments, with the last item in the list being the NULL pointer.

  Example: -------- const char* fileName = "./childApp" ; execl ( fileName, // path/filename of target application &filename[2], // should be bare filename of the target "This is argv[1]", // additional arguments . . . "This is argv[2]", "This is argv[3]", "This is argv[4]", NULL // null pointer terminates the list ) ;
- `-p` execlp() function
  Pass a list of individual arguments which invokes an application on the system PATH. (calling application terminates)

  The ’l’ in "execlp" indicates a list (sequence) of individual arguments, and the ’p’ indicates that the directories specified by the $PATH environment variable will be searched for the application to be invoked.

  Note that if the forward-slash character ( ’/’ ) is found in the application name, then the string will be interpreted as an absolute or relative path, and the $PATH variable _will not_ be searched.

  For this example, the external application called is the ’grep’ utility. ’grep’ is called with arguments describing what to search for and which file(s) to search.

  `grep -n 'COMPILE' Makefile`

  Example: -------- execlp ( "grep", "grep", "-n", "COMPILE", "Makefile", NULL ) ; Note that each argument to execlp() is a separate string.

  This test can accept optional arguments which are interpreted as arguments for invoking the `'grep'` utility, replacing the default arguments shown above.
  The first optional argument will be interpreted as the search string, while the remaining optional arguments will be interpreted as the names of the files to be searched.

        Examples:
        ---------

        ./salmon -p fish Salmon.cpp
           yields:
           execlp ( "grep", "grep", "-n", "fish", "Salmon.cpp", NULL ) ;

        ./salmon -p '[#]define' Salmon.hpp GlobalDef.hpp
           yields:
           execlp ( "grep", "grep", "-n", "[#]define", "Salmon.hpp", "GlobalDef.hpp", NULL ) ;

        ./salmon -p Copyright Salmon.[hc]pp GlobalDef.hpp
           yields:
           execlp ( "grep", "grep", "-n", "Copyright", "Salmon.[hc]pp", "GlobalDef.hpp", NULL ) ;

- `-f` execl() function executed by "forked" process.
  The primary process calls the `"fork"` (actually `"vfork"`) function to create a child process, then the child process invokes an external program (`childApp`) which produces some output, then exits.

  After calling `"vfork"`, the parent process waits silently for the child process to terminate, at which time it reclaims control of the I/O streams.

  Example: -------- pid_t fpid = vfork () ; // create the child process if ( fpid == ZERO) // the child process (if created) executes here { execl ( "./childApp", // target application path/filename "childApp", // argv[0] bare target app filename "-f", // argv[1] additional arguments . . . "some arg", // argv[2] "some arg", // argv[3] "some arg", // argv[4] "some arg", // argv[5] NULL ) ; // In case the exec call fails: child process MUST NOT return, // so we force it to exit immediately. _exit ( 0 ) ; } else // the parent process continues execution here { if ( fpid > ZERO ) // child successfully launched { int childStatus = ZERO ; // receives child's exit status int statusOptions = ZERO ; // status bitmask (none defined at this time) // sleep until the child process terminates waitpid ( fpid, &childStatus, statusOptions ) ; } else { // creation of child process failed } }
- `-s` execvp() function executed by “forked” process.
  The primary process calls the `"fork"` (actually `"vfork"`) function to create a child process, then the child process invokes an external program (`childApp`) which produces some output, then exits.

  After calling `"vfork"`, the parent process continues execution immediately.

  For this example, the output streams (`'stdout'` and `'stderr'`) of the child process are redirected to temporary files `'stdout.txt'` and `'stderr.txt'`, respectively. This allows the parent process to retain control of the output streams, even though both processes are simultaneously executing programs in the same terminal window.

  After the child process has terminated, the parent process will copy the contents of the two temporary files to the display so the user can verify that the redirection was functioning properly.

  Example: -------- const char* childApp = "./childApp" ;// name of app that child invokes const char* childArgs[] = // child's argv[] array { "childApp", // argv[0] bare target filename "-s", // argv[1] option switch from invocation of parent "Hello stdout!\n", // argv[2] written to stdout (which is redirected) "Hello stderr!\n", // argv[3] written to stderr (which is redirected) NULL // terminate the list } ; const char* soFName = "./sout.txt" ; // stdout target file const char* seFName = "./serr.txt" ; // stderr target file int soDesc = ZERO, // file descriptor for stdout target seDesc = ZERO ; // file descriptor for stderr target pid_t fpid = vfork () ; // create the child process if ( fpid == ZERO) // the child process (if created) executes here { // Create the temporary files. soDesc = open ( soFName, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) ; seDesc = open ( seFName, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) ; // Redirect 'stdout' and 'stderr' streams to the files. dup2 ( soDesc, STDOUT_FILENO) ; dup2 ( seDesc, STDERR_FILENO) ; // Close the temporary files. close ( soDesc ) ; close ( seDesc ) ; // If execvp() call is successful, the child process will not // return to the parent application, else the return value // will be written to stderr (which has been redirected to serr.txt). wcerr << execvp ( childApp, (char* const* )childArgs ) ; wcerr << " Error! Child process call to execvp() failed." << endl ; // In case the exec call fails: child process MUST NOT return, // so we force it to exit immediately. _exit ( 0 ) ; } else // the parent process continues execution here { if ( fpid > ZERO ) // child successfully launched { wcout << L"Fork of child process was successful!" << endl ; } else { wcout << L"Creation of child process failed!" << endl ; } }
- `-t` execlp() function executed by "forked" process. The primary process calls the `"fork"` (actually `"vfork"`) function to create a child process, then the child process opens a new terminal window and invokes an external program (in this case, `grep`) within the new window.

  The layout of the parameters for the call to `'execlp'` are described below.

  After calling `"vfork"`, the parent process continues execution immediately.

  In the Salmon source code, we perform some magic to:

  1. determine the name of the terminal emulator program which is currently running,
  2. determine the name of the shell program running in the terminal window,
  3. calculate the size and position for the new terminal window.

  Please see Are You My Mother? for a discussion of this practical magic.

  Terminal Emulators and Shell Programs

  Terminal emulator applications share the common function of providing a command-line interface for exploring and configuring the more esoteric corners of the host system.

  In the beginning, the UNIX gods created the heavens and the `XTerm`. Until `GUI` interfaces were invented `XTerm` was the user interface. While `XTerm` was, and is a marvellous creation, the command-line concept has, over time, evolved in several different directions. In the GNU/Linux world, the two most popular offshoots of `XTerm` are GNOME terminal (`gnome-terminal`) and `KDE Konsole` (`konsole`).

  This test supports identification, configuration and invocation of these three terminal emulator programs:
  `a) gnome-terminal`
  `b) konsole`
  `c) xterm`

  The default configuration for this test is to launch a small terminal window, of the same type as the existing window, but centered in the screen. Optional argument, if provided, may be used to adjust some parameters.
  See Terminal Configuration below for details.

  Constructing the Commands for Launch

  Each terminal emulator program has its own invocation syntax and although they share the same basic functionality, there are some differences which need to be considered.

  The most obvious is that `'gnome-terminal'` and `'konsole'` support tabs (multiple sub-windows), while `'xterm'` does not.

  Another difference is that neither `'gnome-terminal'` nor `'konsole'` support window positioning under Wayland. XTerm continues to recognize horizontal and vertical offset parameters.

  The configuration parameters for launching a new instance of the terminal window vary slightly depending on the emulator invoked; however, the following parameters are included:

  1. geometry:
     The “geometry” for a window includes the window width in columns, and window height in rows. plus the horizontal and vertical offset (in pixels). As mentioned above, each emulator has its own way of handling geometry parameters; whether to accept, reject or ignore them.
  2. working directory:
     This is the directory which will be the Current-Working-Directory (CWD) when the window is opened.

     Technical Notes:
     By default, the `'gnome-terminal'` working directory is taken from the parent’s $PWD environment variable. The CWD may be specified explicitly, either as an absolute path or via the environment variable.
     `--working-directory='/home/sam/SoftwareDesign'`
     `--working-directory='$PWD'`
     `--working-directory='$HOME/temp'`
     The `'konsole'` working directory is specified in its profile (generally the `$HOME` directory, so we must explicitly redirect it to the PWD:
     `--workdir='$PWD'`
     By default, the `'xterm'` working directory is the `$HOME` directory, but a child window will probably reference the `'$PWD'` environment variable. XTerm has no command to directly specify the target working directory, so if a reliable CWD is needed, then the change-directory (`'cd'`) command must be integrated into the command sequence. Example:
     `-e bash -c "cd $PWD ; ls -l ; exec bash"`

     Note that the tilde character (`'~'`), used by the shell as a shortcut for the “Home” directory IS NOT recognized in these contexts.

  3. command sequence:
     This is the command or sequence of commands to be executed within the new window. The syntax for this sequence varies according to the emulator and shell program used. Study the source code for the `Test_Fork_execlp` method to compare each emulator’s invocation syntax.

  The following example describes the parameters for the `'execlp'` call. This example uses the `gnome-terminal`, but other emulators have a similar syntax.

      execlp (
         "gnome-terminal",           // name of terminal program (on system $PATH)
         "gnome-terminal",           // argv[0] (bare program name)
         "--window",                 // argv[1] (target is new window, not tab))
         "-q",                       // argv[2] (no output to parent window)
         "--hide-menubar",           // argv[3] (disable the  window's menu bar)
         "--geometry=65x9+400+300",  // argv[4] (size and position for new window)
         "--working-directory=trgdir"// argv[5] (target working directory)
         "-e",                       // argv[6] (the execlp 'execute' command)
         "bash -c \"grep -n 'COMPILE' Makefile ; exec bash\"", // argv[7]
         NULL                        // argv[8] (signals end of pointer array)
         ) ;

  The main command to the ’execlp’ function, labelled as ’argv[7]’ in the above example needs some further clarification.

  - If all we wanted was to invoke the ’grep’ command in the new terminal window and immediately close the window, then `argv[7]` would be the same as in the `'-p'` option described above:
    `grep -n 'COMPILE' Makefile`
    However, what we want to do here is to open the new window, invoke the `'grep'` command and then leave the window open so the user can view the results. The elements of `argv[7]` are as follows:
    - `bash`
      This is the name of the shell program. It generates a fresh copy of the shell in which to run the ’grep’ command.
    - `-c`
      This is the bash `"command"` command which indicates that the next token is to be executed by the shell program.
    - `\"`
      This is an escaped double-quotation character. This character will be interpreted by the shell program as a quotation mark beginning the command to be executed. If the character were not `'escaped'`, then the shell program would discard it, potentially causing the `'grep'` utility to mis-interpret any whitespace and “regular expression” characters in the search string.
    - `grep -n 'COMPILE' Makefile`
      This is the ’grep’ command sequence.
    - `;`
      The semicolon character is used to separate a list of commands to be executed sequentially by the shell. In our example, there are two commands, the ’grep’ invocation described in the previous line item, and the shell command, ’exec’ described in the next item.
    - `exec bash`
      This command replaces the currently running copy of `'bash'` with a new copy. This may seem odd, but without the ’exec’ two copies of the shell would be left running in the window. The effect of this command is to keep the new window open and to return to the command prompt after all the specified commands have finished.
    - `\"`
      This is another escaped double-quotation character, which the shell program interprets as the end of the command sequence to be executed.
    - `NULL`
      This is a null pointer. It signals the end of the `argv[]` list.

    Pipes and redirection of output also work within the command sequence:
    `-e "grep -n 'COMPILE' Makefile | grep 'childApp' >./dtmp.txt"`
    In this case, the window would close immediately, but our answer is in the temp file.

  Terminal Configuration - Optional Arguments

  This test accepts optional arguments which are interpreted as configuration parameters for launching the child window (or tab within the current window).

  1. The name of the emulator in which the Salmon application is currently running and the name of the shell program have been determined by the getEmulator() method. This is referred to as the “parent” window.
  2. The configuration of the child window is constructed by the DefineChildEmulator() method. This includes the "geometry" for the window, consisting of the width, height and position within the screen. This configuration can be modified using one or more of the optional command-line parameters:
     1. If no command line arguments provided, the child window is defined as a small window centered in the screen.
     2. The dimensions of the child window may be modified via the first two optional arguments.
        Example: `./salmon -t 128 25`
        `'128'` is the width of the window in columns, and
        `'25'` is the height of the window in rows.
        Note that these parameters are not range checked against the size of the computer screen. The author believes that doing such a range check would have added an unnecessary layer of confusion to this simple demonstration app. If these values are not chosen wisely, the invocation of the emulator program may produce unexpected results.
     3. If the first optional parameter is the letter `'f'`, the child window will be opened in full-screen mode, and the geometry parameters will be ignored.
        Example: `./salmon -t f`
     4. If the first optional parameter is the letter `'t'`, then instead of opening a new child window, a new Tab will be opened in the current window.
        Example: `./salmon -t t`
        Note: This option does not apply to XTerm, which does not support tabs.
     5. If the first optional parameter is the letter `'h'` (hint), then display information on using the optional arguments and return without executing the test.
        Example: `./salmon -t h`
     6. The third and fourth optional arguments may be used to specify the position of the window within the computer screen. Note, however, that the Wayland compositor will ignore these values for the gnome-terminal. Note also that KDE Konsole has simply given up and no longer supports the `'--geometry'` option, and there appears to be no workaround for specification of window offsets.
        XTerm still honors the horizontal and vertical offsets specified by the `'-geometry'` option. These offsets are specified in pixels.
        Example: `./salmon -t 128 25 400 200`
        `'128'` is the width of the window in columns
        `'25'` is the height of the window in rows
        `'400'` is the horizontal offset from the left edge of the screen
        `'200'` is the vertical offset from the top of the screen
- `-D` Sandbox for use during development

  The “Sandbox” is the play area traditionally reserved for exploring new ideas while within a safe, mommy-approved environment.

  The `Sandbox` method of the `Salmon` application is designed to be that safe space for those of us whose mommy is exhausted by our constant demands, and who is no longer watching over us every second.

  During development, your humble author used this Sandbox extensively to fill the knowledge gaps in the standard library documentation for the `'exec'` group of functions which is somewhere between spartan and downright lazy.

  > Software Sam must admit that this is among his pet peeves —
  > primarily because he spent about half his time in Silicon Valley
  > writing (or rewriting) the documentation for software and hardware
  > projects, both his own and everybody else’s. — Rant completed!

  For curious experimenters, several of these ad-hoc tests are retained as comments in the method header, to provide hints for further development.

  Special Tests:

  Certain tests have been embedded in the Sandbox code like scarab beetles in amber, and may be invoked using the first optional argument.
  Example: `./salmon -D dump`

  If optional argument ARG1 == `"dump"`, report emulator/shell information.
  If optional argument ARG1 == `"screen"`, report dimensions of computer screen.

## Are You My Mother?

> > > This chapter is named in honor of the classic children’s book of the same name, written and illustrated by P.D. Eastman, Random House, 1980.

For any application, knowing the configuration of the host environment is critical. Under the X-Windows (X11) windowing system
(MIT -> X.org Foundation) there were several tools available for query of the system configuration:
`ps`
`xwininfo`
`loginctl`
`xprop`
`xdpyinfo`
`xdriinfo`
`xvinfo`
Under Wayland, these utilities have either disappeared entirely or their functionality has been severely limited in the name of “security”.

Much of this system information is still available, but only if you are willing to get your hands dirty, like a surgeon playing with the intestines of the patient. But like the surgeon in the operating room, caution must be used to avoid massive internal bleeding and messages such as: “Permission denied”, “No such file or directory”, “Command not found”, or our historical favorite: “lp0 on fire” which meant that your Teletype machine was overheating. Ahh, those were the days!

In the `Salmon` application, we take a few baby steps into this dangerous environment by traversing the `"process ID tree"` to determine the name and version number of the terminal emulator program, as well as the name and version number of the currently active shell program.

Terminal Emulators

The three most popular terminal emulators in the GNU/Linux world are:
`GNOME Terminal - 'gnome-terminal'`
`KDE Konsole - 'konsole'`
`XWindows XTerm - 'xterm'`

Several other branches of the terminal emulator evolution have appeared, some as enhancements to the existing functionality, and some as specialized tools for specific environments. Some of these are:

    tilda          Alacritty   WezTerm        Sakura      xfce4-terminal
    terminator     Kitty       Foot           st          urxvt
    yakuake        Tilix       LXterminal     PuTTY       rxvt

While some of these emulators have undeniably interesting capabilities, for our current demonstration, we stay with the three most common emulators: `'gnome-terminal', 'konsole'` and `'xterm'`.

Shell Programs

The “shell program” is one of most powerful, yet under-appreciated applications in the GNU/Linux world. The “shell” runs within any terminal window environment; interpreting user input, launching the specified shell utilities and external applications.

As with terminal emulator programs, the choice of a shell program is strongly influenced by the host system, the type of work being done on the system and personal preferences.

Certain shell programs are installed by default depending on platform and configuration. The file “/etc/shells” lists the available shell programs. The most common shells seem to be `‘sh'` and `‘bash’`.

Technical Note: The `'tmux'` program is also listed; however, `'tmux'` is not an actual shell program. It is a manager program for running multiple screens simultaneously. Note also that `'sh'` is seldom, if ever actually installed on modern systems; instead, `'sh'` is generally just a symbolic link to `'bash'`.

Other shell programs have small, but wildly enthusiastic user groups.

    Z Shell                    - 'zsh'
    Friendly Interactive Shell - 'fish'
    Korn Shell                 - 'ksh'
    Debian Almquist Shell      - 'dash'
    C Shell                    - 'csh'
    TENEX Shell                - 'tcsh'

“Salmon” currently recognizes five shell programs: `'bash', 'zsh', 'fish', 'ksh'` and `'sh'`.

> Note: It is possible that a valid, but unrecognized terminal emulator
> or shell program may be encountered. If your favorite emulator/shell
> are not supported by this application, the code can easily be updated
> to recognize them. In `getEmulator()`, update the two arrays:
> `shNames[], and shVers[]`. Then add a new switch case in
> `DefineChildEmulator()`.

The emulator/shell information is contained in series of temporary files located in the system’s `"/proc"` (process) directory. The target files are identified by the `"process ID" (PID)` of the process being investigated.

C-library functions are available to identify the `process ID` for any currently active process.

`getpid - get PID of current process`
`getppid - get PID of parent of current process`

Once the `PID` of the parent process is known, it can be used to identify the file containing statistics about that process. For example, if the parent `PID` is 4734, then the target file will be:
`/proc/4734/stat`

This file is a plain text file, so opening as READ ONLY and extracting (approximately) the first 128 characters, we get:

`4734 (bash) S 4663 4734 4734 34818 20813 4194304 6335`
`90409 0 16 7 9 189 47 20 0 1 0 37270 240005120 1569 18`
`446744073709551615`

The first four(4) tokens contain the information we need:
`4734 (bash) S 4663`
These tokens represent, respectively:
The PID of the application being run by this process
`4734`
The name of the application; in this case, the name of the shell program.
`(bash)`
A system identification code (ignored)
`S`
This is the gold mine: the `PID` for the parent of the application’s parent process — the “grandparent” process, if you will.
`4663`

Repeating the process using our newly acquired `PID`, we get:
`4663 (gnome-terminal-) R 1387`

We now have the name of the terminal emulator program, and incidentally, we also have the name of the shell program which is currently running inside the emulator.

The version number of each program can now be requested, and the data will be captured to a temporary file:
`gnome-terminal --version 1>"tempfile_name" 2>/dev/null"`
`bash --version 1>"tempfile_name" 2>/dev/null"`

The captured data are then stored in our customized emuData-class object, and are then available to the application.

── ── ── ── ── ── ──

Note on climbing the PID Tree: If a PID of one (1) is encountered, STOP! This means that the top of the process tree has been reached.

## Building from Source

1. Open a terminal window of a convenient size.
   The application should _build_ in any terminal emulator window; however, application functionality testing has been done only in `'gnome-terminal'`, `'konsole'` and `'xterm'`.
2. Verify that your compiler is at least version 8.1:
   `g++ --version`

   `g++(GCC) 15.2.1 20260123 (Red Hat 15.2.1-7)`
   `Copyright (C) 2025 Free Software Foundation, Inc.`

   If you are not using GCC, then verify that your compiler fully supports the C++17 standard.

3. Download the Salmon archive and copy it to the directory where your software projects live: `cp --preserve=all salmon-x.x.xx.tar.bz2 ~/SoftwareDesign`
   `cd ~/SoftwareDesign`
4. Unpack the archive.
   The Salmon directory will be created and all source files will be expanded into that directory.
   `tar -xjvf salmon-x.x.xx.tar.bz2`
5. Navigate to the source directory:
   `cd Salmon`
6. Build both the main application and the target application:
   `gmake all`

   The Makefile includes additional options if needed:
   `gmake` build only the `'salmon'` application
   `gmake child` build only the `'childApp'` application
   `gmake clean` remove binaries and object files for clean build

   When both applications build without errors and without warnings,
   `` CONGRATULATIONS!

7. Test the primary application:
   `./salmon --version`

   The application version number and copyright information should be displayed.

   If the application does not run, then verify that all necessary libraries are installed on your system. Run the ’ldd’ command to list the necessary libraries. The needed libraries should look something like the following. These are all standard libraries which should be installed on your system by default. `ldd epts` `linux-vdso.so.1 (0x00007f6e7b85a000)` `libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f6e7b400000)` `libm.so.6 => /lib64/libm.so.6 (0x00007f6e7b74a000)` `libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f6e7b71e000)` `libc.so.6 => /lib64/libc.so.6 (0x00007f6e7b20f000)` `/lib64/ld-linux-x86-64.so.2 (0x00007f6e7b85c000)`
8. Test communication between parent and child applications:
   `./salmon -v`

   You should see something like the following, indicating that both applications are functioning properly:

       Salmon Swim Upstream To Spawn!
        Invocation: './salmon -v'
        Parent PID: 8592
        calling execv( childApp argv[] )

       childApp: PID: 8592
       ====================
       argv[0] childApp
       argv[1] -v
       argv[2] Types of Salmon:
       argv[3] Trout
       argv[4] Char
       argv[5] Grayling
       argv[6] Whitefish

       [Salmon]$

Rebuilding the Documentation:

1. Rebuilding the Texinfo documentation requires ’makeinfo’ (texi2any) v:6.8 or greater (v:7.2 recommended).
2. Rebuilding the HTML documentation requires the ’idpp’ post-processor utility, which is part of the ’Infodoc’ package (v:0.0.16) available for download on the author’s website.
   If `'idpp'` is not found on the path, CSS formatting will not be applied to the HTML document; however, both the `'.info'` and `'.html'` documents will have been built correctly. (The HTML document will just look ugly.)
3. Downloand and expand the archive as described above.
4. Navigate to the documentation directory:
   `cd Salmon/Texinfo`
5. Build both the `'.info'` and `.html` versions of the document, and apply CSS formatting to the HTML document:
   `gmake`
6. To verify the build:
   1. Invoke the `'info'` reader:
      `info -f salmon.info`
   2. Then, open `'salmon.html'` in your favorite browser.

## gString Text Tool

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

### Introduction to gString

#### 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: 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.

#### 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.

1. 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!
2. NEVER assume that one character can be represented with one byte.
3. NEVER assume that one character is one display column in width.
4. The idea that text flows from left-to-right, may only be PROVISIONALLY assumed, because again: "!איר זענט נאָר דילודינג זיך" you’re just deluding yourself.
5. 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.

See also a discussion of multiple-language support in the NcDialog API.

### gString Public Methods

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

gString Method Name | Chapter Reference
--- | ---
gString [constructor] | see gString Instantiation
~gString [destructor] |
operator= | see Assignment Operators
compose | see Formatted Assignments
formatInt | see Integer Formatting
gstr | see Data Access
ustr |
copy | see Copying Data
operator<< |
substr |
append | see Modifying Existing Data
insert |
limitChars |
limitCols |
shiftChars |
shiftCols |
padCols |
strip |
erase |
replace |
loadChars |
textReverse |
formatParagraph |
compare | see Comparisons
compcoll |
operator== |
operator!= |
find |
findlast |
after |
findr |
findx |
scan |
gscanf | see Extract Formatted Data
gschars | see Statistical Info
gscols |
utfbytes |
isASCII |
clear | see gString Miscellaneous
wAlloc |
uAlloc |
freeSpace |
reAlloc |
Get_gString_Version |
dumpGstring |
dbMsg |

### 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.

- gString ( void ) ;

        Input  :
           none
        Returns:
           nothing

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

- gString ( int32_t charAlloc );

        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

  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, see Dynamic Memory Allocation.

- gString ( const char* usrc, int32_t charLimit = -1 ) ;

        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

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

- gString ( const wchar_t* wsrc, int32_t charLimit = -1 ) ;

        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

  Constructor: Convert specified wchar_t (’wide’) source to gString.

- gString ( short iVal, short fWidth, bool lJust = false,
  `` bool sign = false, bool kibi = false, fiUnits units = fiK ) ;
- gString ( unsigned short iVal, short fWidth, bool lJust = false,
  `` bool sign = false, bool kibi = false, fiUnits units = fiK ) ;
- gString ( int iVal, short fWidth, bool lJust = false,
  `` bool sign = false, bool kibi = false, fiUnits units = fiK ) ;
- gString ( unsigned int iVal, short fWidth, bool lJust = false,
  `` bool sign = false, bool kibi = false, fiUnits units = fiK ) ;
- gString ( long int iVal, short fWidth, bool lJust = false,
  `` bool sign = false, bool kibi = false, fiUnits units = fiK ) ;
- gString ( unsigned long int iVal, short fWidth, bool lJust = false,
  `` bool sign = false, bool kibi = false, fiUnits units = fiK ) ;
- gString ( long long int iVal, short fWidth, bool lJust = false,
  `` bool sign = false, bool kibi = false, fiUnits units = fiK ) ;
- gString ( unsigned long long int iVal, short fWidth, bool lJust = false,
  `` bool sign = false, bool kibi = false, fiUnits units = fiK ) ;

        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.

  Constructor: Convert specified integer value to gString.
  Please see Integer Formatting `'formatInt'` method group for formatting details.

- gString ( const char* fmt, const void* arg1, ... )
  `` __attribute__ ((format (gnu_printf, 2, 0))) ;

        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

  Constructor: Convert formatting specification and its arguments to gString. Please refer to the compose() method:
  (see Formatted Assignments) for more information.

  Technical Note: There is no constructor using a “`const wchar_t* fmt`” format specification because it would conflict with the constructor which limits the number of characters used to initialize the instance.

- ~gString ( void ) ;

        Input  :
           none
        Returns:
           nothing

  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.

  #### Examples

      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

#### Dynamic Memory Allocation

- During instantiation of a gString object, a dynamic memory allocation occurs; that is, memory is allocated from the “heap” which is RAM memory not directly controlled by the application.

  This is in contrast to the “stack” memory which is assigned to the application on startup.

- 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.

- 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:
  `gString gs( gsALLOCMED ); gString gs( 2500 );`
  This constructor is described above.
- The DEFAULT storage capacity is the minimum capacity:
  `(gsALLOCDFLT == gsALLOCMIN)`
  Total dynamic allocation for the default storage capacity will be:
  `1K UTF-32 characters + 4K UTF-8 bytes + 1K 16-bit integers,`
  `or 4Kb + 4Kb + 2Kb == 10Kb (approximately).`
- The “medium” 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:
  `4K UTF-32 characters + 16K UTF-8 bytes + 4Kb 16-bit integers,`
  `or 16Kb + 16Kb + 8Kb == 40Kb (approximately).`
- 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:
  `1000K UTF-32 characters + 4000K UTF-8 bytes + 1000K 16-bit integers,`
  `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.
- The “reAlloc” method described in the chapter, gString Miscellaneous, may be used to manually increase or decrease the size of the storage buffers in increments of gsALLOCMIN.
- All methods which set or modify the stored text data implement automatic re-sizing of the data buffers.

  The ‘gsmeter’ 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 see gsmeter Test App for additional information; specifically the `R[a|b]` (reallocate) tests which exercise all methods that perform modifications to the size of the storage buffers.

### 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.

- void operator = ( const char* usrc ) ;
- void operator = ( const uint8_t* usrc ) ;

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

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

- void operator = ( const wchar_t* wsrc ) ;

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

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

- void operator = ( const gString& gssrc ) ;

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

  Assignment operator. Copies one gString object to another.

#### Examples

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

    gs1 = utf8Data ;
    gs2 = gs1 ;
    wcout << gs2 << endl ;
     - - -> Youth is wasted on the young.

### Formatted Assignments

- const wchar_t* compose ( const wchar_t* fmt, ... )
  `` __attribute__ ((format (gnu_wprintf, 2, 0))) ;
- const wchar_t* compose ( const char* fmt, ... )
  `` __attribute__ ((format (gnu_printf, 2, 0))) ;

        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

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

      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.

#### Examples

    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

See also formatted instantiation: gString Instantiation.

#### 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.

The following constructs will produce errors:

    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 ) ;

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.

IMPORTANT NOTE:
Depending on your compiler version, you may get a warning when using the `'%b'` binary format specification (described below):
`"warning: unknown conversion type character ‘b’ in format [-Wformat=]"`
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.

    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 );

#### 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.

- Base formatting specifier:
  %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.
- Format modifiers for data size are the same as for swprintf:
  hh , h , l , ll , L , q Examples: %hhb %hB %llB %qb
- Format modifier for prepending of a data-type indicator.
  '#' (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
- Format modifier for appending of a data-type indicator.
  '-#' (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
- Format modifier for specifying the group-seperator character.
  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)
- Format modifier for specifying bit grouping.
  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

#### 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:
(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.

#### Unsupported format specifications

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

### Integer Formatting

- bool formatInt ( short iVal, short fWidth,
  `` bool lJust = false, bool sign = false,
  `` bool kibi = false, fiUnits units = fiK ) ;
- bool formatInt ( unsigned short iVal, short fWidth,
  `` bool lJust = false, bool sign = false,
  `` bool kibi = false, fiUnits units = fiK ) ;
- bool formatInt ( int iVal, short fWidth,
  `` bool lJust = false, bool sign = false,
  `` bool kibi = false, fiUnits units = fiK ) ;
- bool formatInt ( unsigned int iVal, short fWidth,
  `` bool lJust = false, bool sign = false,
  `` bool kibi = false, fiUnits units = fiK ) ;
- bool formatInt ( long iVal, short fWidth,
  `` bool lJust = false, bool sign = false,
  `` bool kibi = false, fiUnits units = fiK ) ;
- bool formatInt ( unsigned long iVal, short fWidth,
  `` bool lJust = false, bool sign = false,
  `` bool kibi = false, fiUnits units = fiK ) ;
- bool formatInt ( long long iVal, short fWidth,
  `` bool lJust = false, bool sign = false,
  `` bool kibi = false, fiUnits units = fiK ) ;
- bool formatInt ( unsigned long long iVal, short fWidth,
  `` bool lJust = false, bool sign = false,
  `` bool kibi = false, fiUnits units = fiK ) ;

      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.

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: `'+9,876,543,210,777'`

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

#### Examples

    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:

Please see (NcDialog test application, ’Dialogw’ for more examples.)

#### Notes on the `formatInt` group

Optional specification of the units suffix for the ’formatInt’ methods. Units are specified using the optional `'units'` parameter, which is a member of the `'fiUnits'` enumerated type.. `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 `'kibi'` parameter. `DECIMAL BINARY` `kilobytes (x/1000) kibibytes (x/1024)` `megabytes (x/1000000) mibibytes (x/1024000)` `gigabytes (x/1000000000) gibibytes (x/1024000000)` `terabytes (x/1000000000000) tebibytes (x/1024000000000)`

#### 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.
If you blindly follow style standards against your own better judgement, then be forever labelled as a weenie.

#### `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 `'#'`.

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

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

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

#### How To Set the Application Locale In C++

The `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 “locale” 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 `"std::locale"` structure referencing the empty string (`""`).
`locale* locptr = new locale("");`
The captured locale is then made “global”, that is it replaces the so-called `"classic"` (C/C++ language) locale with the specified locale definition.
`locptr->global( *locptr );`
Please see the C++ documentation for `"std::locale"` for details.

### Data Access

- const wchar_t* gstr ( void ) const ;

        Input  :
           none
        Returns:
           const pointer to array of wchar_t characters

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

- const wchar_t* gstr ( int32_t& charCount ) const ;

        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

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

- const char* ustr ( void ) const ;

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

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

- const char* ustr ( int32_t& charCount, int32_t& byteCount ) const;

        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

  Return a const pointer to the char (UTF-8) character array, along with the number of characters and the number of bytes in the array (including the null terminator).

#### Examples

    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 ) ;

### Copying Data

- int32_t copy ( char* uTarget, int32_t maxBytes, int32_t maxCols = -1 ) const ;

        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)

  Copy gString text to specified target buffer.

  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()];`

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

- int32_t copy ( wchar_t* wTarget, int32_t maxChars, int32_t maxCols = -1 ) const ;

        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)

  Copy gString text to specified target buffer.

  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()];`

- std::wostream& operator<< ( std:wostream& os, const gString& gs2 );

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

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

  Note that due to the way the output stream is defined, you cannot mix ’cout’ (narrow) and ’wcout’ (wide) output streams indiscriminately. −− If ’wcout’ is called first, then ’cout’ is disabled. −− If ’cout’ is called first, then both narrow and wide channels are active. −− ’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.
- std::ostream& operator<< ( std:ostream& os, const gString& gs2 );

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

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

  IMPORTANT NOTE: 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.
- int32_t substr ( char* uTarget, int32_t offset, int32_t charCnt ) const;
- int32_t substr ( wchar_t* wTarget, int32_t offset, int32_t charCnt ) const;
- int32_t substr ( gString& wTarget, int32_t offset, int32_t charCnt ) const;

        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.

  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.

  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()];`

  > Please Note: The number of bytes can NEVER be assumed to be the same as the number of characters.
  > Please refer to the ’Multi-language Support’ chapter of the `'NcDialog API'` documentation.

#### Examples

    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

### Modifying Existing Data

- int32_t append ( const wchar_t* wPtr ) ;
- int32_t append ( const char* uPtr ) ;
- int32_t append ( const wchar_t wChar ) ;

        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.

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

  #### Example

      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.

- int32_t append ( const wchar_t* fmt, const void* arg1, ... ) __attribute__ ((format (gnu_wprintf, 2, 0)));
- int32_t append ( const char* fmt, const void* arg1, ... ) __attribute__ ((format (gnu_printf, 2, 0)));

        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.

  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 (see Formatted Assignments) for more information on converting data using a format specification string.

  #### Example

      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.

- int32_t insert ( const wchar_t* wPtr, int32_t offset = 0 ) ;
- int32_t insert ( const char* uPtr, int32_t offset = 0 ) ;
- int32_t insert ( wchar_t wChar, int32_t offset = 0 ) ;

        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.

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

  #### Example

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

- int32_t insert( int32_t offset, const char* fmt, const void* arg1, ... );
- int32_t insert( int32_t offset, const wchar_t* fmt, const void* arg1, ... );

        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.

  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.

  #### Example

      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.

- int32_t limitChars ( int32_t charCount ) ;

        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)

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

  #### Example

      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.

- int32_t limitCols ( int32_t colCount ) ;

        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.

  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.

  #### Example

      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.

- int32_t shiftChars ( int32_t shiftCount, wchar_t padChar = L’ ’ ) ;

        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

  Shift text data by the specified number of characters.

      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.

  #### Example

      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' ').

- int32_t shiftCols ( int32_t shiftCount, wchar_t padChar = L’ ’ ) ;

        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

  Shift text data by the specified number of display columns.

      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.

  #### Example

      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.

- int32_t padCols ( int32_t fieldWidth, wchar_t padChar = L’ ’, bool centered = false );

        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

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

  If the `'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 ‘padCols’ 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, 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.

  #### Example

      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."

- int32_t strip ( bool leading = true, bool trailing = true );

        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)

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

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

  “Leading” whitespace is space characters from the beginning of the data to the first non-space character. “Trailing” whitespace is from after the last non-space character through the end of the data.

  By default, both leading and trailing whitespace will be removed; however, this action may be modified by resetting the appropriate parameter to ‘false’.

- int32_t erase ( const gString& src, int32_t offset = 0, bool casesen = false, bool all = false );
- int32_t erase ( const wchar_t* src, int32_t offset = 0, bool casesen = false, bool all = false );
- int32_t erase ( const char* src, int32_t offset = 0, bool casesen = false, bool all = false );
- int32_t erase ( wchar_t src, int32_t offset = 0, bool casesen = false, bool all = false );

        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

  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 `'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.

- int32_t erase ( int32_t offset = 0, int32_t length = gsALLOCMAX );

        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)

  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.

  #### Example

  - If the ’length’ parameter specifies deletion of more characters than remain, then ’erase’ has the same effect as calling the `'limitChars'` method (i.e. truncates the string at `'offset'`).
  - If the defaults for both `'offset'` and `length'` are used, then the `'erase'` method has the same effect as calling the `'clear'` method (gString data are reset to an empty string).
  - Note that the NULL terminator will never be deleted.

  #### Examples for `'erase'`

      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."

- int32_t replace ( const wchar_t* src, const wchar_t* newtxt, int32_t offset = 0, bool casesen = false, bool all = false );
- int32_t replace ( const wchar_t* src, const char* newtxt, int32_t offset = 0, bool casesen = false, bool all = false );
- int32_t replace ( const wchar_t* src, const wchar_t newtxt, int32_t offset = 0, bool casesen = false, bool all = false );
- int32_t replace ( const char* src, const wchar_t* newtxt, int32_t offset = 0, bool casesen = false, bool all = false );
- int32_t replace ( const char* src, const char* newtxt, int32_t offset = 0, bool casesen = false, bool all = false );
- int32_t replace ( const char* src, const wchar_t newtxt, int32_t offset = 0, bool casesen = false, bool all = false );
- int32_t replace ( const wchar_t src, const wchar_t* newtxt, int32_t offset = 0, bool casesen = false, bool all = false );
- int32_t replace ( const wchar_t src, const char* newtxt, int32_t offset = 0, bool casesen = false, bool all = false );
- int32_t replace ( const wchar_t src, const wchar_t newtxt, int32_t offset = 0, bool casesen = false, bool all = false );

        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

  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.

  #### Examples for `'replace'`

      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."

- int32_t loadChars ( const wchar_t* wsrc, int32_t charLimit, bool append = false );
- int32_t loadChars ( const char* usrc, int32_t charLimit, 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.
  `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.
  See gString Instantiation.

        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)

  #### Examples for `'loadChars'`

      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,"

- int32_t textReverse ( bool punct = false, bool para = false, bool rjust = false );

  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.) 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.

        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)

  #### Examples for `'textReverse'`

  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).

      // 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 :השעה הנוכחית היא

  Technical Note On Punctuation

  Determining which characters are and are not punctuation is locale specific (see the `'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.

- int32_t formatParagraph ( int32_t maxRows, int32_t maxCols, bool trunc = true, bool hypenbrk = false,
  `` int32_t *truncIndex = NULL );

        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

  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.

  Technical Description of the Line Break Algorithm

  Formatting is done in three steps:

  1. Remove all newlines from the text.
  2. Perform word wrap to limit the width of each row of the paragraph.
  3. If necessary, truncate the text to limit the height of the paragraph. (but see the `'trunc'` and `'truncIndex'` parameters)

  Token: A ‘token’ 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.

  1. Line breaks occur 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 after the identified space character.
     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:
     `'、' 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 `'hyphenbrk'` parameter.) The characters included within this group are:

     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
  2. 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 would have truncated the text so that caller can manually truncate the text if desired. (see the `'trunc'` and `'truncIndex'` parameters)
  3. 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 “long” tokens as those which are more than half the specified area width (`'maxCols'`).

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

  4. Notes on automatic hyphenation:
     Technically, hyphens should be placed between syllables, but that would require a full dictionary of the target language.
     “Can open... worms everywhere.” (Thank you, Chandler Bing.)
     - 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.
     - 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.
     - 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 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, “...worms everywhere.”

### Comparisons

- int32_t compare ( const char* uStr, bool casesen = true, int32_t length = -1, int32_t offset = 0 ) const;
- int32_t compare ( const uint8_t* uStr, bool casesen = true, int32_t length = -1, int32_t offset = 0 ) const;
- int32_t compare ( const wchar_t* wStr, bool casesen = true, int32_t length = -1, int32_t offset = 0 ) const;
- int32_t compare ( const gString& gs, bool casesen = true ) const;

        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.

  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 numerical, not locale-based. This includes the relationship between upper-case and lower-case characters.
  For locale-specific comparisons, please see compcoll method below.

- int32_t compare ( const gString& gs, bool casesen = true ) const ;

        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.

  Compares the text content of two gString objects.

  The comparison is performed against the gString objects’ wchar_t character arrays. The character comparisons are numerical, not locale-based. This includes the relationship between upper-case and lower-case characters.
  For locale-specific comparisons, please see the `compcoll` method below.

- int32_t compcoll ( const wchar_t* wStr ) const;
- int32_t compcoll ( const char* uStr ) const;
- int32_t compcoll ( const gString& bs ) const;

        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.

  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 “collation order” or “dictionary order” comparison where lowercase letters are considered as less than their uppercase equivalents. Characters with diacritical markers and unitary character sequence (e.g. Spanish `'ll'`) will also be sorted according to the rules of the active locale.

  1. 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.
  2. If the application has not specified the locale, then the so-called POSIX C/C++ pseudo-locale will be used.
  3. 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 `'compcoll'` method is not effective for these languages.
  4. Note that if the source data contain mixed languages, the ’wcscoll’ function will be ineffective and misleading.
  5. Technical Note: The ’wcscoll’ librarary function is also considerably slower than the `'wcscmp'` (numeric comparison) function because it scans the data multiple times.
  Please see how to set locale for additional information. Note: Regardless of source data format, this method uses the C-library `'wcscoll'` function exclusively to perform the comparison.

  > Technical Note:

  > In the ASCII world, the letter `'A'` is numerically less than the letter `'a'`; however, in the world of locale-specific comparison, English lowercase is “less than” uppercase, so `'a'` is less than `'A'`.

  > Examples of string comparison using the collation algorithm:

  > '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.

- bool operator == ( const gString& gs2 ) const ;

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

  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 is is case-sensitive.

- bool operator != ( const gString& gs2 ) const ;

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

  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 is case-sensitive.

- int32_t find ( const char* src, int32_t offset=0, bool casesen=false, int32_t maxcmp= -1 ) const;
- int32_t find ( const wchar_t* src, int32_t offset=0, bool casesen=false, int32_t maxcmp= -1 ) const ;
- int32_t find ( const gString& src, int32_t offset=0, bool casesen=false, int32_t maxcmp= -1 ) const;
- int32_t find ( const wchar_t src, int32_t offset=0, bool casesen=false ) const;

        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

  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.

- int32_t findlast ( const char* src, bool casesen=false ) const;
- int32_t findlast ( const wchar_t* src, bool casesen=false ) const;
- int32_t findlast ( const gString& src, bool casesen=false ) const;
- int32_t findlast ( const wchar_t src, bool casesen=false ) const;

        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

  Scan the data for the 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.

- int32_t after ( const char* src, int32_t offset=0, bool casesen=false ) const;
- int32_t after ( const wchar_t* src, int32_t offset=0, bool casesen=false ) const;
- int32_t after ( const gString& src, int32_t offset=0, bool casesen=false ) const;

        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

  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 FOLLOWS the substring.

- int32_t findr ( const gString& src, int32_t offset = -1, bool casesen = false ) const;
- int32_t findr ( const wchar_t *src, int32_t offset = -1, bool casesen = false ) const;
- int32_t findr ( const char *src, int32_t offset = -1, bool casesen = false ) const;
- int32_t findr ( const wchar_t& src, int32_t offset = -1, bool casesen = false ) const;

        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

  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.

- int32_t findx ( wchar_t srcChar = L`' '`, int32_t offset=0 ) const;

        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

  Scan the text and locate the first character which DOES NOT match the specified character.

  This method is used primarily to scan past the end of a sequence of space `(' ')` characters (0x0020), but may be used to skip over any sequence of a single, repeated character. Note that the character specified must be a ‘wide’ (wchar_t) character.

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

- int32_t scan ( int32_t offset=0 ) const ;

        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.

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

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

#### Examples

    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.

For more examples of using gString-class methods, please refer to Test #6 of the ’Dialogw’ test application.

### Extract Formatted Data

- int32_t gscanf ( const wchar_t* fmt, ... ) const ;
- int32_t gscanf ( const char* fmt, ... ) const ;

        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

  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 `'const wchar_t*'` as in a call to `'swscanf'`, or a `'const char*'` as in a call to `'sscanf'`.

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

  The optional arguments are pointers to the target variables which will receive the formatted data.

  As with `'swscanf'`, the number of optional arguments must be equal to or greater than the number of format specifiers:
  `(%d, %63s, %X %lld, %24lls, %[, etc.).`
  Excess arguments will be ignored; however, unlike the `'swscanf'` function, `'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. (You’re welcome :-)

- int32_t gscanf ( int32_t offset, const wchar_t* fmt, ... ) const ;
- int32_t gscanf ( int32_t offset, const char* fmt, ... ) const ;

        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

  This is the same method as described above except that the scan of the data begins at the specified character offset.

  #### Examples

      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 ) ;

  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. `THIS: %64s %128lls %256S %32[a-z]` `NOT THIS: %s %lls %S %[a-z]`

### Statistical Info

- int32_t gString::gschars ( void ) const ;

        Input  :
           none
        Returns:
           number of characters in the string

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

- int32_t utfbytes ( void ) const ;

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

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

- int32_t gscols ( void ) const ;

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

  Returns the number of columns required to display the string.

- const short* gscols ( short& charCount ) const ;

        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

  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).

  #### Example

      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() *

- bool isASCII ( void ) ;

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

  Scan the data to determine whether it is pure, 7-bit ASCII.

### gString Miscellaneous

- void gString::clear ( void );

        Input  : none
        Returns: nothing

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

- int32_t wAlloc ( void ) const;
- int32_t uAlloc ( void ) const;

        Input  : none

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

  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.
  `wAlloc :` returns the storage allocation for
  `` UTF-32 (wchar_t) characters
  `uAlloc :` returns the storage allocation for UTF-8 bytes

- int32_t freeSpace ( void ) const;

        Input  : none

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

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

- int32_t reAlloc ( int32_t steps, bool clear = false );

        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

  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:
  `gsALLOCMIN <= size <= gsALLOCMAX`
  `1024 chars 1,024,000 chars`

- const char* Get_gString_Version ( void ) const ;

        Input  :
           none
        Returns:
           pointer to version string

  Return a pointer to gString class version number string.

- void gString::dbMsg ( gString& gsmsg ) const;

        Input  :
           gsmsg : (caller's object, by reference)
                   receives most recent debug message
        Returns:
           nothing

  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.

- `void dumpGstring (` const wchar_t*& gstr, const char*& ustr,
  `` const short*& cwid, int32_t& gsw,
  `` int32_t& gsu, int32_t& bsch, int32_t& bsco,
  `` int32_t& utfb, bool& isas ) const;

        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

  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.

### gString Examples

The NcDialog API test application ’Dialogw’ contains extensive examples of gString usage, including working copies of the examples used in this chapter.
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.

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

       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 ) ;

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

       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 ) ;

3. Concatenate strings.

       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?

4. Create formatted string data.

       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!

   > 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.

5. 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.

       //*  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() *

   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).

   For a more sophisticated text-parsing algorithm, see formatParagraph which parses text data by token rather than simply counting columns.

### gsmeter Test App

> GSMETER
> gsmeter Introduction
> gsmeter Options
> Building gsmeter

#### 'gsmeter' Introduction

The original design of the gString class was based upon a fixed buffer size of `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, `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 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 `'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!

#### 'gsmeter' Command-line Options

While this application lacks professional polish, it does provide a simple menu structure and the usual `--help` and `--version` options.

    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

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 “Sandbox” for creating ad-hoc tests.

#### Test Groups

1. Constructors
   A: Constructors A, default, specify allocation, char*, wchar_t*, format specification B: Constructors B: integer-formatting constructors
2. Initializers
   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)
3. Modifiers
   A: append(), limitChars(), limitCols(), insert(), erase() B: replace(), shiftChars(), shiftCols() C: rightJustify(), padCols(), strip(), textReverse() D: formatParagraph(), clear()
4. Analyzers
   A: operator==, operator!=, compare(gString&), compare() B: compcoll(), find() C: findlast(), findx(), scan() D: after() E: findr() F: gscanf()
5. Statistics
   Info Dump, Public Stats, Character Widths, Class Version Number
6. Output group
   "operator<<" for both UTF-32 (wide) and UTF-8 (narrow) streams
7. Reallocation tests
   A: Reallocation tests, Group A B: Reallocation tests, Group B
8. Buffer Size/Resize (attempted overflow) tests
   A: Constructors and Assignments Overflow tests B: Insert and Append Overflow tests

Reporting of test results is split between the terminal window and a file `(gsmdbg.txt)` created in the current working directory.

Note on Application Locale

`gsmeter` automatically enables the “locale” specified in the terminal environment. While the gString class is written to be locale-independent, some of the `gsmeter` tests rely upon the locale to function properly.
Specifically, the gString `compcoll` (compare-collated) method invokes the locale-aware C-language function,`'wcscoll'`. This function uses a dictonary-style comparison which is dependent on the language and locale involved. Please see compcoll method for details.

#### Building 'gsmeter'

`gsmeter` is a very simple application, so building it is also simple.

There are four(4) ways to invoke the `Makefile`:

gmake # compile the application module and link # to the existing gString library gmake buildlib # delete the existing gString.lib link library # and rebuild it gmake clean # Remove the object files, the gString library # and the executable gmake all # Execute the 'clean', 'buildlib' and # 'gmake' options

> > 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)

## Technical Support

Please Note: All trademarks and service marks mentioned in this document are the entirely-too-proprietary property of their respective owners, and this author makes no representation of affiliation with or ownership of any of the damned things.

### Contact

Salmon (salmon) binary, source code and associated Texinfo documentation were written and are maintained by: Mahlon R. Smith, The Software Samurai Beijing University of Technology on the web at: www.SoftwareSam.us For bugs, suggestions, periodic updates, or possible praise, please post a message to the author via website. The author wishes to thank everyone for their intelligent, kind and thoughtful responses. (ranters I can live without)

### By the same author

- NcDialog Application Programming Interface (API) library.
  The NcDialog API forms the basis for most of our other software projects. It provides the application developer with the tools to create dialog-based console applications, without the need to learn anything about the complexities of the underlying ‘ncurses’ C-language library.

  Console applications have always been the most efficient and easily-implemented of computer programs. What they lacked was a friendly and visually-pleasing user interface.

  With the NcDialog API, console applications can now be used and understood by experts and novice users alike.

- FileMangler (fmg), is a compact, fast and flexible tool for handling all your file management needs.

  FileMangler performs all basic file management tasks, as well as performing scheduled and ad-hoc file backup and synchronization activities.

  FileMangler runs in a console window, and thus provides access to many system tools not available to a GUI application. FileMangler also provides full support for accessing the local Trashcan.

  FileMangler is based on the NcDialog API, and thus will run in almost any GNU/Linux or UNIX terminal environment.

- Console Trashcan (ctrash) is a simple, console-based (command-line) utility for managing the contents of your local Trashcan. Send items to the trash, restore items from the trash, report the trashcan contents, and empty the trash. ’ctrash’ is designed as a safer, and more flexible alternative to the ’rm’ and ’rmdir’ commands.
- Infodoc, post-processing and CSS style for HTML documents created by the Texinfo (makeinfo) utility. If you write documentation using the Texinfo/makeinfo documentation engine, AND if you care at all about what your documentation looks like, then you really do need the ‘infodoc-styles.css‘ definition file and the ‘idpp’ HTML post-processor.

  The HTML version of this document was formatted using ‘idpp’ and is displayed using the ‘infodoc-styles.css‘ definition file. Many more examples are available on the author’s website.

- gString text internationalization tool.
  gString implements a C++ class that may be integraged into any application for smooth and painless text formatting, and for converting between UTF-8 and ‘wchar_t’ (wide) character encoding.

  The gString class is lightweight, consisting of one C++ source module and one header file. The gString class may be directly integrated into an application, or may be built as a link library.
  The gString class is also embedded within the NcDialog API library (see above).

- Taggit (taggit) is a demonstration program written for students, or for anyone who wishes to create a console application with a multilingual user interface.

  Conceptually, Taggit is an audio-file tag editor (metadata editor), and is album oriented rather than file oriented so that all audio files in an album may be edited simultaneously.

  Taggit is not intended as a full-featured tag editor; for instance, Taggit does not access online databases of audio tag information. Taggit fully supports tag editing for audio formats: MP3, M4A, OGG/Vorbis and WMA.

  The OGG/Vorbis I specification is supported for all text tags in `'.ogg'` and `'.oga'` audio files.

  For MP3 audio files, all tag frames of the ID3v2.3 standard are supported, along with some features of ID3v2.4, such as support for UTF-8 text encoding which enables writing text tags in any language.

  Taggit is implemented in four(4) user interface languages: Español (Spanish), Zhōngwén (中文) (Chinese, simplified), Tiếng Việt (Vietnamese) and English (U.S.). Additional user interface languages (both LTR and RTL) may be added with minimum effort.

- AnsiCmd Library and ACTest test application.
  The AnsiCmd link library defines a simple and comprehensive way to configure a console program’s text attributes, as well as cursor positioning, area erasure and other operations defined by `ANSI` escape sequences in the ANSI X3.64 standard.

  `ANSI` escape sequences are available for setting foreground and background color, including 3-bit and 4-bit color, 8-bit color, 24-bit (RGB) color, greyscale color, and the `aixterm` (IBM) extensions.

  Text modifiers include bold/faint, italic, underline, double-underline, overline, strikethrough, blinking text (fast/slow), reversed fgnd/bkgnd, and invisible text; as well as commands to disable each of these modifiers.

  A suite of cursor-positioning commands and text erasure commands are also supported.

  The standard also specifies escape sequences for little-used and seldom-supported options such as Fractur, Ideogram, Framed, Encircled and Alternate-font. The AnsiCmd library implements these escape sequence commands; however, most modern terminal emulators do not support them.

  AnsiCmd also provides some common terminal configuration operations such as flexible input buffering options and capture of the Panic Button (break signal).

  Also included in the AnsiCmd library are application-level functions such as line-drawing, creation of windowed objects, direct control of the cursor-movent keys and reasonably sophisticated user input methods.

  A stand-alone, temporary-file-management class (TFMan) is included as part of the package.

  Because this is primarily an experimental project, comprehensive testing of all functionality is integrated into the AnsiCmd library.

- TFMan is a temporary-file management class for Linux applications.
  `TFMan` is written in C++ and may be easily integrated into any application. The system directory for creating temporary files is identified and application access is verified. During the session, creation and deletion of temporary files is tracked so that all temporary files created during the session are deleted on application exit.
- Source Profiler (srcprof) is a source code analysis tool for determining the ‘maintainability’ of source code modules.

  ‘srcprof’ can be used to profile source code for high-level languages such as C, C++ and Java, as well as various assembly languages and scripting languages such as Python, Perl and Ruby. For a complete list of currently-supported source languages, please see the Source Profiler documentation.

  ’srcprof’ can be used both as a productivity-measurement tool and as a tool for testing source code quality based on an evaluation of its ‘maintainability’.

  Source Profiler is a console-based utility, which runs as either a pure, command-line utility, OR as a dialog application based on the NcDialog API.

- WaylandCB, clipboard access for console applications.
  Access to the system clipboard under the Wayland communications protocol, leveraging Sergey Bugaev’s "wl-clipboard" application suite.

  WaylandCB is a simple C++ class definition which provides console applications with seemless access to the system clipboard.

- Exercalc (ecalc) is a utility for tracking daily exercise activity.
  Specify the type of exercise, walk/bike/run/cardio/free weights, etc. and either the elapsed time, distance travelled, or energy expended (KCal or METs). Exercalc then calculates the efficiency and progress over time. Archive the data, retrieve and analyze the data in various ways including tables, comparisons and bar charts.

  The application is specifically designed to be a student exercise in creating applications which incorporate a multi-language user interface. Exercalc is implemented in four(4) user interface languages: Español (Spanish), Zhōngwén (中文) (Chinese, simplified), Tiếng Việt (Vietnamese) and English (U.S.). Additional user interface languages (both LTR and RTL) may be added with minimum effort.

- DVD Repair (dvdrep) is a utility for exploring the contents of a damaged DVD video disc and rescuing as much of the data as possible.

  ‘dvdrep’ can be used to rescue data from any non-encrypted DVD video source disc that is formatted using the Universal Disc Format (UDF) filesystem (as all commercially produced DVD movies are).

  ‘dvdrep’ takes a layered approach to the analysis of the source disc. A detailed log file is maintained for each step of the process in case manual intervention is needed at a later step.

  DVD Repair is based on the NcDialog API, and thus will run in almost any GNU/Linux or UNIX terminal environment.

- crcPlus (crcplus) is a reference model and demonstration program for implementating CRC (Cyclic Redundancy Check) data error detection. crcPlus provides a reliable reference algorithm for verifying the function of other CRC generators and decoders as well as providing the tools to build a fully customizable hash table (lookup table) for creating your own high-speed CRC generator.
- Various other Linux utilities designed as academic exercises are also available for student use. For example: interprocess communication pipes, FIFOs, sockets, shared memory blocks, multi-threading, the ’Sleeping Barber’ problem and so on.
  These cannot be considered as production-worthy, but they demonstrate the concepts in simple terms.

## Copyright Notice

The Salmon binary and source code are released under the GNU General Public License (GPL 3+), and the user documentation (this document) is released under the GNU Free Documentation License (FDL 1.3+):
Copyright © 2012-2026 Mahlon R. Smith, The Software Samurai This manual describes version 0.0.05 of ’salmon’. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".

### GNU General Public License

Version 3, 29 June 2007
Copyright © 2007 Free Software Foundation, Inc. https://fsf.org/ Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

#### Preamble

The GNU General Public License is a free, copyleft license for software and other kinds of works.

The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program—to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.

For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.

For the developers’ and authors’ protection, the GPL clearly explains that there is no warranty for this free software. For both users’ and authors’ sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.

Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users’ freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.

Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.

The precise terms and conditions for copying, distribution and modification follow.

#### TERMS AND CONDITIONS

0. Definitions.

   “This License” refers to version 3 of the GNU General Public License.

   “Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

   “The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations.

   To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.

   A “covered work” means either the unmodified Program or a work based on the Program.

   To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

   To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

   An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.

1. Source Code.

   The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work.

   A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

   The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

   The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work’s System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

   The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

   The Corresponding Source for a work in source code form is that same work.

2. Basic Permissions.

   All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

   You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

   Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.

3. Protecting Users’ Legal Rights From Anti-Circumvention Law.

   No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

   When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work’s users, your or third parties’ legal rights to forbid circumvention of technological measures.

4. Conveying Verbatim Copies.

   You may convey verbatim copies of the Program’s source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

   You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

5. Conveying Modified Source Versions.

   You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

   1. The work must carry prominent notices stating that you modified it, and giving a relevant date.
   2. The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
   3. You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
   4. If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.

   A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation’s users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

6. Conveying Non-Source Forms.

   You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

   1. Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
   2. Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
   3. Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
   4. Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
   5. Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.

   A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

   A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

   “Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

   If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

   The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

   Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

7. Additional Terms.

   “Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

   When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

   Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

   1. Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
   2. Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
   3. Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
   4. Limiting the use for publicity purposes of names of licensors or authors of the material; or
   5. Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
   6. Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.

   All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

   If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

   Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

8. Termination.

   You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

   However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

   Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

   Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

9. Acceptance Not Required for Having Copies.

   You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

10. Automatic Licensing of Downstream Recipients.

    Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.

    An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party’s predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

    You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

11. Patents.

    A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor’s “contributor version”.

    A contributor’s “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

    Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor’s essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

    In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

    If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient’s use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

    If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

    A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

    Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

12. No Surrender of Others’ Freedom.

    If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

13. Use with the GNU Affero General Public License.

    Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.

14. Revised Versions of this License.

    The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

    Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.

    If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy’s public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

    Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

15. Disclaimer of Warranty.

    THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

16. Limitation of Liability.

    IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

17. Interpretation of Sections 15 and 16.

    If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

#### END OF TERMS AND CONDITIONS

#### How to Apply These Terms to Your New Programs

If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.

one line to give the program's name and a brief idea of what it does. Copyright (C) year name of author This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.

Also add information on how to contact you by electronic and paper mail.

If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:

program Copyright (C) year name of author This program comes with ABSOLUTELY NO WARRANTY; for details type ‘`show w`’. This is free software, and you are welcome to redistribute it under certain conditions; type ‘`show c`’ for details.

The hypothetical commands ‘`show w`’ and ‘`show c`’ should show the appropriate parts of the General Public License. Of course, your program’s commands might be different; for a GUI interface, you would use an “about box”.

You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see https://www.gnu.org/licenses/.

The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read https://www.gnu.org/licenses/why-not-lgpl.html.

### GNU Free Documentation License

Version 1.3, 3 November 2008
Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. https://fsf.org/ Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

0. PREAMBLE

   The purpose of this License is to make a manual, textbook, or other functional and useful document free in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.

   This License is a kind of “copyleft”, which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.

   We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.

1. APPLICABILITY AND DEFINITIONS

   This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The “Document”, below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as “you”. You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.

   A “Modified Version” of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.

   A “Secondary Section” is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document’s overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.

   The “Invariant Sections” are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.

   The “Cover Texts” are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.

   A “Transparent” copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not “Transparent” is called “Opaque”.

   Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.

   The “Title Page” means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, “Title Page” means the text near the most prominent appearance of the work’s title, preceding the beginning of the body of the text.

   The “publisher” means any person or entity that distributes copies of the Document to the public.

   A section “Entitled XYZ” means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as “Acknowledgements”, “Dedications”, “Endorsements”, or “History”.) To “Preserve the Title” of such a section when you modify the Document means that it remains a section “Entitled XYZ” according to this definition.

   The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.

2. VERBATIM COPYING

   You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.

   You may also lend copies, under the same conditions stated above, and you may publicly display copies.

3. COPYING IN QUANTITY

   If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document’s license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.

   If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.

   If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.

   It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.

4. MODIFICATIONS

   You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:

   1. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
   2. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
   3. State on the Title page the name of the publisher of the Modified Version, as the publisher.
   4. Preserve all the copyright notices of the Document.
   5. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
   6. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
   7. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document’s license notice.
   8. Include an unaltered copy of this License.
   9. Preserve the section Entitled “History”, Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled “History” in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
   10. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the “History” section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
   11. For any section Entitled “Acknowledgements” or “Dedications”, Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
   12. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
   13. Delete any section Entitled “Endorsements”. Such a section may not be included in the Modified Version.
   14. Do not retitle any existing section to be Entitled “Endorsements” or to conflict in title with any Invariant Section.
   15. Preserve any Warranty Disclaimers.

   If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version’s license notice. These titles must be distinct from any other section titles.

   You may add a section Entitled “Endorsements”, provided it contains nothing but endorsements of your Modified Version by various parties—for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.

   You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.

   The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.

5. COMBINING DOCUMENTS

   You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.

   The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.

   In the combination, you must combine any sections Entitled “History” in the various original documents, forming one section Entitled “History”; likewise combine any sections Entitled “Acknowledgements”, and any sections Entitled “Dedications”. You must delete all sections Entitled “Endorsements.”

6. COLLECTIONS OF DOCUMENTS

   You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.

   You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.

7. AGGREGATION WITH INDEPENDENT WORKS

   A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an “aggregate” if the copyright resulting from the compilation is not used to limit the legal rights of the compilation’s users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.

   If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document’s Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.

8. TRANSLATION

   Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.

   If a section in the Document is Entitled “Acknowledgements”, “Dedications”, or “History”, the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.

9. TERMINATION

   You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License.

   However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

   Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

   Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it.

10. FUTURE REVISIONS OF THIS LICENSE

    The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See https://www.gnu.org/licenses/.

    Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License “or any later version” applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy’s public statement of acceptance of a version permanently authorizes you to choose that version for the Document.

11. RELICENSING

    “Massive Multiauthor Collaboration Site” (or “MMC Site”) means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A “Massive Multiauthor Collaboration” (or “MMC”) contained in the site means any set of copyrightable works thus published on the MMC site.

    “CC-BY-SA” means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization.

    “Incorporate” means to publish or republish a Document, in whole or in part, as part of another Document.

    An MMC is “eligible for relicensing” if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008.

    The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing.

#### ADDENDUM: How to use this License for your documents

To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:

Copyright (C) year your name. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''.

If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the “with…Texts.” line with this:

with the Invariant Sections being list their titles, with the Front-Cover Texts being list, and with the Back-Cover Texts being list.

If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.

If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.

## Index

Jump to: | 0 B C D E F G I L M P R S T

| Index Entry | Section
0
| 01.0 Introduction | Introduction
| 02.0 Invoking | Invoking
| 03.0 Are You My Mother? | Are You My Mother?
| 04.0 Building from Source | Building from Source
| 05.00 gString Text Tool | gString Text Tool
| 05.01 Introduction to gString | Introduction to gString
| 05.02 gString Public Methods | gString Public Methods
| 05.03 gString Instantiation | gString Instantiation
| 05.04 Assignment Operators | Assignment Operators
| 05.05 Formatted Assignments | Formatted Assignments
| 05.06 Integer Formatting | Integer Formatting
| 05.07 Data Access | Data Access
| 05.08 Copying Data | Copying Data
| 05.09 Modifying Existing Data | Modifying Existing Data
| 05.10 Comparisons | Comparisons
| 05.11 Extract Formatted Data | Extract Formatted Data
| 05.12 Statistical Info | Statistical Info
| 05.13 gString Miscellaneous | gString Miscellaneous
| 05.14 gString Examples | gString Examples
| 05.15 gsmeter Test App | gsmeter Test App
| 06.0 Technical Support | Technical Support
| 07.0 Copyright Notice | Copyright Notice
| 07.1 GNU General Public License | GNU General Public License
| 07.2 GNU Free Documentation License | GNU Free Documentation License
B
| BiDi text | Modifying Existing Data
| by the same author | Technical Support
C
| contact info | Technical Support
D
| development, test -D | Invoking
| dynamic alloc. gString | gString Instantiation
E
| emulator, terminal | Are You My Mother?
| execl, test -f | Invoking
| execl, test -l | Invoking
| execlp, test -p | Invoking
| execlp, test -t | Invoking
| execv, test -v | Invoking
| execvp, test -s | Invoking
F
| fiUnits enumerated type | Integer Formatting
| formatInt field overflow | Integer Formatting
G
| gString memory alloc. | gString Instantiation
| gString methods | gString Public Methods
| gString text conversion | gString Text Tool
I
| invoking | Invoking
L
| locale, set | Integer Formatting
M
| memory, alloc. gString | gString Instantiation
| methods, gString | gString Public Methods
P
| process tree | Are You My Mother?
R
| ram memory, gString | gString Instantiation
| RTL text | Modifying Existing Data
S
| set locale | Integer Formatting
| source code | Building from Source
| standbox | Invoking
| support, technical | Technical Support
| swscanf emulation | Extract Formatted Data
T
| technical support | Technical Support
| terminal emulator | Are You My Mother?
| test -D, development | Invoking
| test -f, execl | Invoking
| test -l, execl | Invoking
| test -p, execlp | Invoking
| test -s, execvp | Invoking
| test -t, execlp | Invoking
| test -v, execv | Invoking
| text conversion, gString | gString Text Tool
| tree, process | Are You My Mother?