//********************************************************************************
//* File       : AnsiCmdApi.cpp                                                  *
//* Author     : Mahlon R. Smith                                                 *
//*              Copyright (c) 2022-2026 Mahlon R. Smith, The Software Samurai   *
//*                 GNU GPL copyright notice below                               *
//* Date       : 20-Dec-2025                                                     *
//* Version    : (see AnsiCmdVersion string in AnsiCmd.cpp)                      *
//*                                                                              *
//* Description: AnsiCmd-class miscellaneous application-level methods.          *
//* 1) Parsing of the "--term" command-line options for terminal setup.          *
//*    Non-member method: ansicmdSetup()                                         *
//* 2) Parsing of the "--test" command-line options for testing AnsiCmd-class    *
//*    functionality.                                                            *
//*    Non-member method: ansicmdSetup()                                         *
//* 3) Application-level 'clear-area' methods.                                   *
//* 4) Application-level line and box drawing methods.                           *
//* 5) Encoding and decoding of attribute tracking data.                         *
//* 6) Create/delete temporary files (TFMan class).                              *
//* 7) Text for inclusion in command-line help for the "--term" and "--test"     *
//*    options.                                                                  *
//* 8) Clipboard access, both local and system clipboards.                       *
//*                                                                              *
//*                                                                              *
//********************************************************************************


//*****************
//* Include Files *
//*****************
#include "AnsiCmd.hpp"     //* AnsiCmd-class definition

//*----------------------------------------------------------------------------*
#if INCLUDE_API != 0 //* Build the API methods as part of the AnsiCmd library. *
//*----------------------------------------------------------------------------*

//***************
//* Definitions *
//***************
#if DEBUG_ANSICMD != 0 && DEBUG_LOG != 0
extern ofstream ofsdbg ;
extern gString  gsdbg ;
#endif   // DEBUG_ANSICMD && DEBUG_LOG

//********************
//* Local prototypes *
//********************

//**************
//* Local data *
//**************
// Note: See "HelpTerm" and "HelpAnsi" at the bottom of this module.

   //******************************************************
   //** THIS SECTION IMPLEMENTS THE PUBLIC and PRIVATE   **
   //** METHODS FOR ACCESSING THE LOCAL CLIPBOARD,       **
   //** AND IF ACCESSIBLE, THE Wayland SYSTEM CLIPBOARD. **
   //******************************************************

   //** Note that both WaylandCB.hpp and WaylandCB,cpp are included. **
   #if INCLUDE_WAYLANDCB != 0
   #include "WaylandCB.cpp"
   #endif   // INCLUDE_WAYLANDCB

   //* Non-member pointer to WaylandCB class object for access to the         *
   //* GNOME/Wayland clipboard. This must be a non-member method so that all  *
   //* child windows and dialogs can inherit access to the system clipboard.  *
   #if INCLUDE_WAYLANDCB != 0
   static WaylandCB *wcbClip = NULL ;
   bool wcbConnected = false ;      // set if connected to system clipboard
   #define DEBUG_ACWCB (0)          // for debugging only
   #endif   // INCLUDE_WAYLANDCB

   //******************************************************
   //** Local Clipboard, definition and instance:        **
   //** -- The local clipboard is shared by all instances**
   //**    of the AnsiCmd, ACWin and ACDlg classes       **
   //** -- The local clipboard is used to copy/cut/paste **
   //**    data withing the application.                 **
   //** -- The most common usage is to share data among  **
   //**    Textbox controls (ftTB); however, other uses  **
   //**    are possible, such as writing clipboard data  **
   //**    to the display, or copying to/from a file.    **
   //** -- There is no direct application-level access   **
   //**    to the local clipboard.                       **
   //** -- The local clipboard knows nothing of the      **
   //**    system (Wayland) clipboard.                   **
   //**                                                  **
   //******************************************************
   class LocalClipboard
   {
      // NOTE: Even through member methods are declared 'public', they
      //       are accessed only by AnsiCmd/ACWin member methods.
      public:
      LocalClipboard ( void )             //* Constructor
      {
         this->lcbClear () ;
      }

      int lcbStore ( const gString& srcText )
      {  //* Store caller's data (if any)
         this->lcb = srcText ;
         return this->lcb.gschars() ;     // return character count
      }
      int lcbRetrieve ( gString& trgText )
      {
         trgText = this->lcb ;
         return trgText.gschars() ;       // return character count
      }

      short lcbClear ( void )
      {
         this->lcb.clear() ;              // empty string
         return ZERO ;
      }
      friend class AnsiCmd ;     //* Allow AnsiCmd class access to all members

      private:
      gString lcb ;                       // Local clipboard buffer
      //short tbsrc ;                          // Index of source Textbox control

   } static localClipboard ;     //* Instance of local clipboard
   //** ----------------------------------------------------------------- **
   //**                   End Local Clipboard Definition                  **
   //** ----------------------------------------------------------------- **


//*************************
//*         cbSet         *
//*************************
//********************************************************************************
//* Protected Method                                                             *
//* ----------------                                                             *
//* Copy the specified text to the local clipboard.                              *
//* Then, if specified, and if currently connected to the system clipboard,      *
//* copy the local clipboard data to the system clipboard.                       *
//*                                                                              *
//* Input  : gsTxt : (by reference) contains text to be saved on the clipboard   *
//*                  Note: If 'gsTxt' contains the empty string, the clipboard   *
//*                        is cleared.                                           *
//*          toSys : (optional, default: true )                                  *
//*                  if set, copy data to system clipboard (if accessable)       *
//*                  if reset, copy only to local clipboard                      *
//*                                                                              *
//* Returns: number of UTF-8 data bytes written to clipboard                     *
//*           0 if clipboard cleared (only null character written)               *
//********************************************************************************

int AnsiCmd::cbSet ( const gString& gsTxt, bool toSys )
{
   int cbBytes = ZERO ;                   // return value

   localClipboard.lcbStore ( gsTxt ) ;    // write data to local clipboard
   if ( (cbBytes = gsTxt.utfbytes()) == 1 )
      cbBytes = ZERO ;

   //* If connection to system clipboard established, *
   //* copy local clipboard to system clipboard.      *
   #if INCLUDE_WAYLANDCB != 0
   if ( toSys && wcbConnected )
   {
      cbBytes = wcbClip->wcbSet ( (const uint8_t*)(gsTxt.ustr()), cbBytes ) ;
   }
   #endif   // INCLUDE_WAYLANDCB

   return cbBytes ;

}  //* End cbSet() *

//*************************
//*         cbGet         *
//*************************
//********************************************************************************
//* Private Method                                                               *
//* --------------                                                               *
//* Get a copy of clipboard contents.                                            *
//* If currently connected to the system clipboard, copy the data from the       *
//* system clipboard to the local clipboard.                                     *
//* Then, return the contents of the local clipboard.                            *
//*                                                                              *
//*                                                                              *
//* Input  : gsTxt   : (by reference) receives clipboard data                    *
//*          fromSys : (optional, default: true )                                *
//*                    if set, retrieve data from system clipboard(if accessable)*
//*                            and store it in local clipboard                   *
//*                    if reset, retrieve data only from local clipboard         *
//*                                                                              *
//* Returns: number of UTF-8 data bytes read from clipboard                      *
//*          or  0 if clipboard is empty (gsTxt == empty string: "\0")           *
//********************************************************************************

int AnsiCmd::cbGet ( gString& gsTxt, bool fromSys )
{
   int cbBytes = ZERO ;                   // return value

   //* If connection to system clipboard established, *
   //* copy system clipboard to local clipboard.      *
   #if INCLUDE_WAYLANDCB != 0
   if ( fromSys && wcbConnected )
   {
      gString gs ;
      wcbClip->wcbGet ( gs ) ;
      localClipboard.lcbStore ( gs ) ; // note: the _character_ count is returned
   }     // (connected)
   #endif   // INCLUDE_WAYLANDCB

   localClipboard.lcbRetrieve ( gsTxt ) ;
   if ( (cbBytes = gsTxt.utfbytes()) == 1 )
      cbBytes = ZERO ;

   return cbBytes ;

}  //* End cbGet() *

//*************************
//*   acClipboardEnable   *
//*************************
//********************************************************************************
//* Establish temp file for communications with the external                     *
//* "wl-clipboard" utilities, "wl-copy" and "wl-paste".                          *
//*                                                                              *
//* If wl-clipboard utilities installed, establish the connection                *
//* with the system clipboard.                                                   *
//*                                                                              *
//* Technical Note: If connection was previously established,                    *
//*                 connection will be reset and re-initialized.                 *
//*                                                                              *
//* Input  : version : (optional, default: null pointer) if specified,           *
//*                    pointer to gString object which receives version          *
//*                    number of the WaylandCB class _and_ if available,         *
//*                    the version number of the wl-clipboard utility.           *
//*                                                                              *
//* Returns: 'true'  if system clipboard connection established                  *
//*          'false' if unable to connect: wl-clipboard utilities                *
//*                  not installed or inaccessable, or system error              *
//********************************************************************************
//* Note on version number report:                                               *
//* ------------------------------                                               *
//* The two version-number strings are returned in a gString object, and are     *
//* separated by a newline character ('\n') as shown:                            *
//*    "x.y.zz\na.b.c"                                                           *
//* where "x.y.zz" is the WaylandCB version, and "a.b.c" is the reported         *
//* wl-copy/wl-paste version. Example:  "0.0.04\n2.2.1"                          *
//********************************************************************************

bool AnsiCmd::acClipboardEnable ( gString* version )
{
   #if INCLUDE_WAYLANDCB != 0
   if ( wcbClip == NULL )                 // if interface not yet established
   {
      wcbClip = new WaylandCB() ;
      wcbConnected = wcbClip->wcbIsConnected () ;
   }
   else                                   // re-initialize the existing connection
   {
      wcbConnected = wcbClip->wcbReinit () ;
   }
   //* If requested, retrieve version numbers. *
   if ( version != NULL && wcbClip != NULL )
      wcbClip->wcbVersion ( *version ) ;

   #if DEBUG_ANSICMD != 0 && DEBUG_LOG != 0 && DEBUG_ACWCB != 0
   if ( ofsdbg.is_open() )
   {
      gString gs( "unknown" ) ;
      if ( wcbClip != NULL )
      {
         if ( version != NULL )  { gs = *version ; }
         else                    { wcbClip->wcbVersion ( gs ) ; }
         gs.replace( L'\n', L'_' ) ;
      }
      gsdbg.compose( "ClipboardEnable( wcbClip:%p wcbConnected:%hhd version:%S )",
                     wcbClip, &wcbConnected, gs.gstr() ) ;
      ofsdbg << gsdbg << endl ;
   }
   #endif   // DEBUG_ANSICMD && DEBUG_LOG && DEBUG_ACWCB
   
   return wcbConnected ;

   #else    // WaylandCB not compiled
   return false ;
   #endif   // INCLUDE_WAYLANDCB

}  //* End acClipboardEnable() *

//*************************
//*  acClipboardDisable   *
//*************************
//********************************************************************************
//* Terminate the connection to system clipboard.                                *
//* 1) Delete temp files.                                                        *
//* 2) Return all resources to the system                                        *
//*                                                                              *
//* Input  : none                                                                *
//*                                                                              *
//* Returns: nothing                                                             *
//********************************************************************************

void AnsiCmd::acClipboardDisable ( void )
{
   #if INCLUDE_WAYLANDCB != 0
   if ( wcbClip != NULL )
   {
      //* Delete the WaylandCB object.        *
      //* This releases all system resources. *
      delete wcbClip ;
      wcbClip = NULL ;
   }
   wcbConnected = false ;

   #if DEBUG_ANSICMD != 0 && DEBUG_LOG != 0 && DEBUG_ACWCB != 0
   if ( ofsdbg.is_open() )
   {
      gsdbg.compose( "ClipboardDisable( wcbClip:%p wcbConnected:%hhd )",
                     wcbClip, &wcbConnected ) ;
      ofsdbg << gsdbg << endl ;
   }
   #endif   // DEBUG_ANSICMD && DEBUG_LOG && DEBUG_ACWCB
   #endif   // INCLUDE_WAYLANDCB

}  //* End acClipboardDisable() *

//*************************
//*    acClipboardTest    *
//*************************
//********************************************************************************
//* Test the connection with the Wayland clipboard.                              *
//* Send a short message to the clipboard, then retrieve                         *
//* the data and compare the results.                                            *
//*                                                                              *
//* Note that the value returned by the WaylandCB test method is a member of     *
//* enum wcbStatus. If the value received is wcbsACTIVE, the connection has      *
//* been verified; else communication was not established.                       *
//*                                                                              *
//* Input  : none                                                                *
//*                                                                              *
//* Returns: 'true'  if connection verified, else 'false'                        *
//********************************************************************************

bool AnsiCmd::acClipboardTest ( void )
{
   #if INCLUDE_WAYLANDCB != 0
   const char* testData = "Test AnsiCmd-to-WaylandCB-to-SystemClipboard Connection." ;
   wcbStatus cbStatus = wcbsNOINIT ;
   if ( wcbClip != NULL )
   {
      cbStatus = wcbClip->wcbTest ( testData ) ;

      #if DEBUG_ACWCB != 0
      if ( (ofsdbg.is_open()) )
      {
         gString gs( testData ) ;
         int srcBytes = gs.utfbytes() ;
         gsdbg.compose( "acClipboardTest( Source: '%S' srcBytes:%d cbStatus:%s )",
                        gs.gstr(), &srcBytes,
                        (cbStatus == wcbsACTIVE    ? "wcbsACTIVE"    :
                         cbStatus == wcbsNOINIT    ? "wcbsNOINIT"    :
                         cbStatus == wcbsNOCONNECT ? "wcbsNOCONNECT" : 
                                                     "wcbsNOINSTALL") ) ;
         ofsdbg << gsdbg << endl ;
      }
      #endif   // DEBUG_ACWCB
   }
   return ( bool(cbStatus == wcbsACTIVE) ) ;

   #else    // WaylandCB not compiled
   return false ;
   #endif   // INCLUDE_WAYLANDCB

}  //* End acClipboardTest() *

//*************************
//*  acClipboardGetText   *
//*************************
//********************************************************************************
//* Get a copy of the text on the clipboard.                                     *
//*                                                                              *
//* Note: If communication with the system (Wayland) clipboard has               *
//*       been established, the data are taken from the system                   *
//*       clipboard; else, data are taken from the local clipboard               *
//*       buffer.                                                                *
//*       See documentation for details.                                         *
//*                                                                              *
//* Input  : gsTxt : (by reference) receives contents of clipboard               *
//*                                                                              *
//* Returns: number of characters returned                                       *
//********************************************************************************

int AnsiCmd::acClipboardGetText ( gString& gsTxt )
{

   //* Return the contents of the clipboard.*
   return ( (this->cbGet ( gsTxt )) ) ;

}  //* End acClipboardGetText() *

//*************************
//*  acClipboardSetText   *
//*************************
//********************************************************************************
//* Save the specified text to the clipboard.                                    *
//*                                                                              *
//* Note:                                                                        *
//* -----                                                                        *
//* The data are first written to the local clipboard buffer; then, if           *
//* communication with the system (Wayland) clipboard has been established,      *
//* the data are copied to the system clipboard.                                 *
//*       See documentation for details.                                         *
//*                                                                              *
//* Input  : gsTxt : (by reference) contains data to be written                  *
//*                                                                              *
//* Returns: number of characters written                                        *
//********************************************************************************

int AnsiCmd::acClipboardSetText ( const gString& gsTxt )
{

   return ( (this->cbSet ( gsTxt )) ) ;

}  //* End acClipboardSetText() *

//***************************
//*   acClipboardCharCnt    *
//***************************
//********************************************************************************
//* Returns the number of UTF-32 characters currently in the local clipboard     *
//* buffer.                                                                      *
//*                                                                              *
//* Note that if connected to the system (Wayland) clipboard, the local          *
//* clipboard contains the same data as the system clipboard.                    *
//*                                                                              *
//* Input  : none                                                                *
//*                                                                              *
//* Returns: number of (wchar_t) characters (including NULLCHAR)                 *
//********************************************************************************
//* Programmer's Note: If connected to the system clipboard, this method reports *
//* the count of characters on the system clipboard; however, be aware that this *
//* value is not necessarily the same as the local clipboard count.              *
//********************************************************************************

int AnsiCmd::acClipboardCharCnt ( void )
{
   int cbChars = ZERO ;                   // return value

   #if INCLUDE_WAYLANDCB != 0
   if ( wcbConnected )
      cbChars = wcbClip->wcbChars () ;
   else
   #endif   // INCLUDE_WAYLANDCB
      cbChars = localClipboard.lcb.utfbytes() ;

   return cbChars ;

}  //* End acClipboardCharCnt() *

//***************************
//*   acClipboardByteCnt    *
//***************************
//********************************************************************************
//* Returns the number of UTF-8 bytes of data on the clipboard.                  *
//*                                                                              *
//* 1) If connected to the system (Wayland) clipboard, return size of system     *
//*    clipboard data.                                                           *
//*    -- Note that system clipboard data is not necessarily text data.          *
//* 2) If NOT connected to the system clipboard, value returned is the number    *
//*    data bytes stored on the local clipboard.                                 *
//*                                                                              *
//* Input  : none                                                                *
//*                                                                              *
//* Returns: number of data bytes (including NULLCHAR)                           *
//*          Note that a value of one(1) indicates an empty string.              *
//********************************************************************************
//* Programmer's Note: If connected to the system clipboard, this method reports *
//* the count of bytes on the system clipboard; however, be aware that this      *
//* value is not necessarily the same as the local clipboard count.              *
//********************************************************************************

int AnsiCmd::acClipboardByteCnt ( void )
{
   int cbBytes = ZERO ;                   // return value

   #if INCLUDE_WAYLANDCB != 0
   if ( wcbConnected )
      cbBytes = wcbClip->wcbBytes () ;
   else
   #endif   // INCLUDE_WAYLANDCB
      cbBytes = localClipboard.lcb.utfbytes() ;

   return cbBytes ;

}  //* End acClipboardByteCnt() *


               //** ------------------------------ **
               //**     Color Attribute Group      **
               //** ------------------------------ **
//*************************
//*    acSetAttributes    *
//*************************
//********************************************************************************
//* Modify or replace the current color attributes and/or text modifiers.        *
//*                                                                              *
//* Examples:                                                                    *
//* ---------                                                                    *
//* To set the foreground color attribute to blue text:                          *
//*    acSetAttributes( acaFG_BLUE ) ;                                           *
//*                                                                              *
//* To set the background color attribute to green:                              *
//*    acSetAttributes( acaBG_GREEN ) ;                                          *
//*                                                                              *
//* To set both foreground and background color attributes:                      *
//*    acSetAttributes( acAttr(acaFG_GREEN | acaBG_DFLT) ) ;                     *
//*                                                                              *
//* To set Bold and Italic text modifiers:                                       *
//*    acSetAttributes( acAttr(acaBOLD | acaITALIC) ) ;                          *
//*                                                                              *
//* To reset the Bold modifier:                                                  *
//*    acSetAttributes( acAttr(acaCLEAR_MODS | acaBOLD) ) ;                      *
//*                                                                              *
//* To reset all text modifiers (return to plain text):                          *
//*    acSetAttributes( acaCLEAR_MODS ) ;                                        *
//*                                                                              *
//* To set Bold, Blue foreground text:                                           *
//*    acSetAttributes( acAttr(acaBOLD | acaFG_BLUE) ) ;  OR                     *
//*    acSetAttributes( acaFGb_BLUE ) ;                                          *
//*                                                                              *
//* To set Bold, Blue foreground text and bold Red background:                   *
//*    acSetAttributes( acAttr(acaBOLD | acaFG_BLUE | acaBGb_RED) ) ;            *
//*                                                                              *
//* To reset all attributes and modifiers to their default values:               *
//*    acSetAttributes( acaATTR_DFLT, true ) ;                                   *
//*                                                                              *
//* To reset all attributes and modifiers to their default values,               *
//* and then set new fgnd/bgnd attributes and text modifiers:                    *
//*    acSetAttributes( acAttr(acaFG_BLUE | acaBG_GREY | acaBOLD), true ) ;      *
//*                                                                              *
//* To set 8-bit color attributes or "web-safe" RGB (Red/Green/Blue) color       *
//* attributes, first call acComposeAttributes() to encode the desired indices,  *
//* then call acSetAttributes with the value returned by acComposeAttributes().  *
//*                                                                              *
//* 8-bit Example:                                                               *
//*  acAttr newAttr =                                                            *
//*     acComposeAttributes( 16, 32, false, false, acAttr(acaBOLD | acaBLINK) ); *
//*      where "16" and "32" are 8-bit color indices into the color lookup       *
//*      table for foreground and background, respectively.                      *
//*      Range: min8BIT <= value <= max8BIT  _OR_                                *
//*             minGSCALE <= value <= maxGSCALE   (greyscale attribute)          *
//*      (both rgb-foreground and rgb-background flags are reset)                *
//*      Text modifiers, acaBOLD and acaBLINK are specified as a logical OR'd    *
//*      value.                                                                  *
//*  acSetAttributes( newAttr ) ;                                                *
//*                                                                              *
//* RGB Example:                                                                 *
//*  acAttr newAttr =                                                            *
//*     acComposeAttributes( wsrgbBLUE + 3, wsrgbCYAN + 16, true, true ) ;       *
//*      where "wsrgbBLUE + 3" is three steps brighter than basic blue, and      *
//*            "wsrgbCYAN + 16" is 16 steps brighter than basic cyan.            *
//*      Range: minRGB <= value <= maxRGB                                        *
//*      (both rgb-foreground and rgb-background flags are set)                  *
//*      Text modifiers are not specified, so default modifiers(acaPLAINTXT)     *
//*      is assumed.                                                             *
//*  acSetAttributes( newAttr ) ;                                                *
//*  ----- ----- ----- ----- ----- ----- ----- ------ ----- ----- ----- -----    *
//*                                                                              *
//*                                                                              *
//* Input  : newAttr : bitfield containing the logical OR of the desired         *
//*                    foreground/background attributes and text modifiers.      *
//*          resetAll: (optional, 'false' by default)                            *
//*                    if 'false', only the the attributes specified by          *
//*                                'newAttr' will be modified.                   *
//*                    if 'true',  all attributes and modifiers will be reset    *
//*                                to their default values before the new        *
//*                                attributes are applied.                       *
//*                                                                              *
//* Returns: 'true' if setting updated,                                          *
//*          'false' (specified setting == current settings so nothing to do)    *
//********************************************************************************
//* Notes:                                                                       *
//* ------                                                                       *
//* 1) Interpretation of the acaCLEAR_MODS bit:                                  *
//*    a) If specified alone, without specifying any mod bits, this is           *
//*       interpreted as a request to reset all text-modifier bits.              *
//*       Examples: (acaCLEAR_MODS)  or  (acaCLEAR_MODS | acaPLAINTXT)           *
//*    b) If specified in combination with one or more mod bits, reset only      *
//*       those specified bits.                                                  *
//*                                                                              *
//* 2) The way the request for Bold text is implemented is platform dependent.   *
//*    Some terminal emulation software renders "bold" text as a lighter, more   *
//*    intense color. Others render "bold" text as thicker text strokes.         *
//*    Experiment with your system to determine how bold-text is rendered.       *
//*                                                                              *
//*  ----- ----- ----- ----- ----- ----- ----- ------ ----- ----- ----- -----    *
//* acaFG_BLACK and acaBG_BLACK                                                  *
//* ---------------------------                                                  *
//* Special Case: Because of the way the terminal defines the black color        *
//* attribute (that is, both are 0x00 values), if setting either foreground      *
//* or background to black (acaFG_BLACK or acaBG_BLACK, respectively), then      *
//* the foreground and background should be specified simultaneously to avoid    *
//* ambiguity.                                                                   *
//*    acSetAttributes( acAttr(acaFG_BLACK, acaBG_GREEN ) ;                      *
//*    acSetAttributes( acAttr(acaFG_GREEN, acaBG_BLACK ) ;                      *
//* Of course it would be foolish to set both foreground and background to       *
//* black at the same time, so if the 'newAttr' parameter were specified as      *
//* (acaFG_BLACK | acaBG_BLACK) or simply as (acaFG_BLACK) or (acaBG_BLACK),     *
//* the command will be ambiguous. In this case, the resulting fgnd/bgnd         *
//* combination may be unexpected.                                               *
//*                                                                              *
//********************************************************************************

bool AnsiCmd::acSetAttributes ( acAttr newAttr, bool resetAll )
{
   #define DEBUG_SA (0)             // for debugging only
   bool setFgnd = false,            // true if foreground to be modified
        setBgnd = false,            // true if background to be modified
        setMods = false,            // true if text modifiers to be modified
        allSet  = false ;           // return value

   #if DEBUG_ANSICMD != 0 && DEBUG_LOG != 0 && DEBUG_SA != 0
   if ( ofsdbg.is_open() )
   {
      gsdbg.compose( 
         "acSetAttributes( %hhd       ---r brfc --mods--- --bgnd--- --fgnd---\n"
         "                 acaVal:%#-b \n"
         "                newAttr:%#-b",
         &resetAll, &this->attrBits.acaVal, &newAttr ) ;
      ofsdbg << gsdbg.ustr() << endl ;
   }
   #endif    // DEBUG_ANSICMD && DEBUG_LOG && DEBUG_SA

   //* If specified, reset all color attributes and *
   //* text modifiers before setting new attributes.*
   if ( resetAll )
   {
      //* Reset all attributes and modifiers to defaults.*
      this->acReset () ;
      newAttr = acAttr(newAttr & ~acaCLEAR_MODS) ; // modifiers have been cleared

      //* If default values specified, updates complete.*
      if ( newAttr == acaATTR_DFLT )
         allSet = true ;

      #if DEBUG_ANSICMD != 0 && DEBUG_LOG != 0 && DEBUG_SA != 0
      setFgnd = setBgnd = setMods = true ; // all processed (used for debugging output)
      #endif    // DEBUG_ANSICMD && DEBUG_LOG && DEBUG_SA
   }  // reset all

   if ( ! allSet )
   {
      //* Decode the attributes into their component bitfields *
      acaExpand acaEx( newAttr ) ;

      //* If changes to text-modifiers specified i.e. either *
      //*  a) the clear bit is set, indicating that the      *
      //*     specified mods are to be cleared.              *
      //*      i)  remove the specified modifiers, then      *
      //*     ii) add back the remaining modifiers           *
      //*  b) the clear bit is reset, indicating that the    *
      //*     specified mod bits are to be _added_ to the    *
      //*     existing mods.                                 *
      if ( acaEx.clrMods || (acaEx.modFlags != this->attrBits.modFlags) )
      {
         if ( acaEx.clrMods )             // clear the specified mod flags
         {
            uint32_t newMods = acaPLAINTXT ;
            if ( acaEx.modFlags != acaPLAINTXT)
               newMods = this->attrBits.modFlags & ~acaEx.modFlags ;
            acaEx.acaVal = acAttr(acaEx.acaVal & ~(acaMOD_MASK | acaCLEAR_MODS)) ;
            acaEx.acaVal = acAttr(acaEx.acaVal | newMods) ;
         }
         else                             // add the specified mod flags
         {
            acaEx.acaVal = acAttr(acaEx.acaVal | this->attrBits.modFlags) ;
         }
         setMods = true ;
      }  // text modifiers

      //* If new background is _different_ from current background *
      if ( acaEx.bgBits != this->attrBits.bgBits )
      { setBgnd = true ; }

      //* If new foreground is _different_ from current foreground *
      if ( acaEx.fgBits != this->attrBits.fgBits )
      { setFgnd = true ; }

      //* Decode the new bitfields and set the new attributes.        *
      //* (This will update the 'attrBits' member of AnsiCmd object.) *
      if ( setFgnd || setBgnd || setMods )
      {
         acaEx.decode( acaEx.acaVal ) ;
         this->setAttributes ( acaEx, setFgnd, setBgnd, setMods ) ;
         allSet = true ;
      }
   }  // allSet

   #if DEBUG_ANSICMD != 0 && DEBUG_LOG != 0 && DEBUG_SA != 0
   if ( ofsdbg.is_open() )
   {
      gsdbg.compose( "   f:%hhd,b%hhd,m:%hhd    acaVal:%#-b \n", 
                     &setFgnd, &setBgnd, &setMods, &this->attrBits.acaVal ) ;
      ofsdbg << gsdbg.ustr() << endl ;
   }
   #endif    // DEBUG_ANSICMD && DEBUG_LOG && DEBUG_SA

   return allSet ;

}  //* End acSetAttributes() *

//*************************
//*  acComposeAttributes  *
//*************************
//********************************************************************************
//* Construct an acAttr attribute bitmap from component values:                  *
//* Call this method when composing attributes with 8-bit fgnd/bgnd indices or   *
//* web-safe RGB fgnd/bgnd indices. Optionally set the new attributes.           *
//*                                                                              *
//* Note on 4-bit values:                                                        *
//* The 4-bit color attributes are individually named, so the attributes may be  *
//* specified directly.                                                          *
//*    Examples: acSetAttributes( acAttr(acaFG_BLUE | acaBGb_BROWN) );           *
//*              acSetAttributes( acAttr(acaFG_DFLT | acaBGb_BLUE) );            *
//* Important Note: Although this method is not designed to configure 4-bit      *
//* color attributes, they will be handled correctly, EXCEPT THAT it is not      *
//* possible to specify terminal default foreground or background using this     *
//* method.                                                                      *
//*     Undocumented: We document that this method may not be used               *
//*                   to set terminal-default attributes; however,               *
//*                   it is _possible_ to do it by intentionally                 *
//*                   specifying an out-of-range RGB value which                 *
//*                   will cause the terminal default to be used.                *
//*                                                                              *
//* Input  : fgVal  : foreground index (8-bit index or web-safe RGB index)       *
//*                   -- if 'rgb' reset, then value interpreted as 8-bit index.  *
//*                      Range: 16-231 (or greyscale:232-255)                    *
//*                   -- if 'rgb' set, then value interpreted as web-safe RGB    *
//*                      index. Range: 0-252                                     *
//*                   -- if 'rgb' reset AND 'fgVal' 0-15, then assume 4-bit color*
//*          bgVal  : background index (8-bit index or web-safe RGB index)       *
//*                   -- if 'rgb' reset, then value interpreted as 8-bit index.  *
//*                      Range: 16-231 (or greyscale:232-255)                    *
//*                   -- if 'rgb' set, then value interpreted as web-safe RGB    *
//*                      index. Range: 0-252                                     *
//*                   -- if 'rgb' reset AND 'fgVal' 0-15, then assume 4-bit color*
//*          rgbfg  : if set, fgVal is interpreted as a (web-safe) RGB index     *
//*                   if reset, fgVal is interpreted as a 4/8-bit index          *
//*          rgbbg  : if set, bgVal is interpreted as a (web-safe) RGB index     *
//*                   if reset, bgVal is interpreted as a 4/8-bit index          *
//*          mods   : (optional, acaPLAINTXT by default)                         *
//*                   logical OR of acAttr text-modifier flags)                  *
//*                                                                              *
//* Returns: the constructed acAttr value                                        *
//*          (if a value is out-of-range, terminal default will be used)         *
//********************************************************************************

acAttr AnsiCmd::acComposeAttributes ( uint8_t fgVal, uint8_t bgVal, bool rgbfg, 
                                      bool rgbbg, acAttr mods )
{
   acaExpand acaEx ;                // construct bitfield value
   bool useDflts = false ;          // 'true' if parameter error

   acaEx.fgDflt = acaEx.bgDflt = false ; // reset terminal-default flags

   //* If foreground value refers to RGB index *
   if ( rgbfg )
   {
      //* Range check for web-safe index limits *
      if ( (fgVal >= wsrgbMIN) && (fgVal <= wsrgbMAX) )
      { acaEx.fgIndex = fgVal ;  acaEx.fgRgb = true ; }
      else     // out-of-range, use terminal default
      { acaEx.fgIndex = ZERO ;  acaEx.fgDflt = true ; }
   }
   //* If foreground value is an 8-bit (or 4-bit) index value *
   else
   { acaEx.fgIndex = fgVal ; }

   //* If background value refers to RGB index *
   if ( rgbbg )
   {
      //* Range check for web-safe index limits *
      if ( (bgVal >= wsrgbMIN) && (bgVal <= wsrgbMAX) )
      { acaEx.bgIndex = bgVal ;  acaEx.bgRgb = true ; }
      else     // out-of-range, use terminal default
      { acaEx.bgIndex = ZERO ;  acaEx.bgDflt = true ; }
   }
   //* If background value is an 8-bit (or 4-bit) index value *
   else
   { acaEx.bgIndex = bgVal ; }

   //* Set specified text-modifier flags *
   if ( !useDflts && (mods != acaPLAINTXT) )
      acaEx.modFlags = (mods & acaMOD_MASK) ;

   //* Encode the data *
   acaEx.compose() ;

   return acaEx.acaVal ;

}  //* End acComposeAttributes() *

//*************************
//*    acGetAttributes    *
//*************************
//********************************************************************************
//* Get a copy of the current color attributes and text modifier flags.          *
//* The bitfield containing the attributes is decoded into it component values   *
//* in human-readable form. Refer to the description of the acaExpand class      *
//* data members for more information.                                           *
//*                                                                              *
//* Input  : decodedAttr: (by reference) instance of an acaExpand object         *
//*                       to receive data.                                       *
//*                                                                              *
//* Returns: nothing                                                             *
//********************************************************************************

void AnsiCmd::acGetAttributes ( acaExpand& decodedAttr )
{

   decodedAttr = this->attrBits ;

}  //* End acGetAttributes() *


               //** ------------------------------ **
               //**      Clear Display Group       **
               //** ------------------------------ **
//*************************
//*     acClearScreen     *
//*************************
//********************************************************************************
//* Clear the entire terminal window using the specified fill character.         *
//* On return, the cursor will be at the HOME position (1,1).                    *
//*                                                                              *
//* Input  : fillchar: (optional, space character ' ' by default)                *
//*                    specify a (single-column) character to be written to      *
//*                    each character cell of the terminal window                *
//*          fillattr: (optional, default: terminal default attributes)          *
//*                    color attribute for clearing the terminal window.         *
//*                    Note: If a non-default attribute is specified, that will  *
//*                          be the current fg/bg attribute on return.           *
//*                                                                              *
//* Returns: nothing                                                             *
//********************************************************************************

void AnsiCmd::acClearScreen ( wchar_t fillchar, acAttr fillattr )
{

   if ( fillattr == acaUSEDFLT ) // If default, set to terminal default attributes
      fillattr = acaATTR_DFLT ;

   this->acClearArea ( 1, 1, this->termRows, this->termCols, fillchar, fillattr )  ;
   this->acSetCursor ( 1, 1 ) ;

}  //* End acClearScreen() *

//*************************
//*      acClearArea      *
//*************************
//********************************************************************************
//* Clear the specified rectangular area using spaces (default), or using the    *
//* specified fill character.                                                    *
//*                                                                              *
//* Position, height and width of the area are adjusted as necessary to remain   *
//* within the terminal window. See also the note in module header regarding     *
//* 1-based vs. 0-based cursor positioning.                                      *
//*                                                                              *
//* Input  : row     : top row of target area                                    *
//*          col     : left column of target area                                *
//*                 OR                                                           *
//*          wpOrig  : top row and left column of target area                    *
//*                                                                              *
//*          height  : number of rows in target area                             *
//*          width   : number of columns in target area                          *
//*          fillchar: (optional, ' ' by default)                                *
//*                    specify a (single-column) character to be written to      *
//*                    each character cell in the target area                    *
//*          fillattr: (optional, current attribute setting by default)          *
//*                    color attribute for clearing the target area.             *
//*                    Note: If a non-default attribute is specified, that will  *
//*                          be the current fg/bg attribute on return.           *
//*                                                                              *
//* Returns: 'true'  if successful                                               *
//*          'false' if parameter(s) out-of-range                                *
//*             Note: on return, the cursor position will be indeterminate.      *
//********************************************************************************

bool AnsiCmd::acClearArea ( short row, short col, short height, short width, 
                            wchar_t fillchar, acAttr fillattr )
{
   const wchar_t dfltFILLCHAR = L' ' ;
   gString gsOut( gsALLOCMED ) ;
   bool status = false ;

   if ( row == ZERO )   row = 1 ;   // accept zero-based values, but convert to 1
   if ( col == ZERO )   col = 1 ;
   if ( (row >= 1) && (col >= 1) && (height > ZERO) && (width > ZERO) )
   {
      status = true ;               // declare success

      //* Target area must lie wholly within the terminal window.*
      //* Adjust height and width as necessary and continue, but *
      //* reset the status flag.                                 *
      if ( (row + height) > this->termRows )
      { height = this->termRows - row + 1 ; status = false ; }
      if ( (col + width) > this->termCols )
      { width = this->termCols - col + 1 ; status = false ; }

      //* If non-default attributes specified *
      if ( fillattr != acaUSEDFLT )
         this->acSetAttributes ( fillattr ) ;

      //* If fill character is not the default space (L' '), *
      //* then verify that it is a single-width character.   *
      if ( fillchar != dfltFILLCHAR )
      {
         gsOut.compose( L"%C", &fillchar ) ;
         if ( gsOut.gscols() != 1 )
            fillchar = dfltFILLCHAR ;
         gsOut.clear() ;
      }

      //* Construct the output row text *
      gsOut.padCols( width, fillchar ) ;

      //* Construct a multi-line text string of the specified *
      //* dimensions, and using the specified fill character. *
      wchar_t wbuff[gsALLOCMIN] ;
      gsOut.append( L'\n' ) ;
      gsOut.copy( wbuff, gsALLOCMIN ) ;
      for ( short h = 1 ; h < height ; ++h )
         gsOut.append( wbuff ) ;
      gsOut.limitChars( gsOut.gschars() - 2 ) ; // delete the trailing newline
      this->acWrite ( row, col, gsOut ) ;
   }

   return status ;

}  //* End acClearArea() *

bool AnsiCmd::acClearArea ( WinPos wpOrig, short height, short width, 
                            wchar_t fillchar, acAttr fillattr )
{
   return ( (this->acClearArea ( wpOrig.row, wpOrig.col, height, width, 
                                 fillchar, fillattr )) ) ;
}  //* End acClearArea() *


               //** ------------------------------ **
               //**     Text Formatting Group      **
               //** ------------------------------ **
//*************************
//*   acFormatParagraph   *
//*************************
//********************************************************************************
//* Reformat the provided text to fit within the specified dimensions.           *
//*  1) Remove all newlines from the text.                                       *
//*  2) Perform word wrap to limit 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)                        *
//*                                                                              *
//* Input  : gsTxt    : on entry, contains raw message text                      *
//*                     on return, contains formatted message text               *
//*                     NOTE: Be sure there is enough freespace in the gString   *
//*                           object for the addition of newline characters.     *
//*          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. U+0020 and U+3000           *
//*                                 Special Case: the following CJK ideographs   *
//*                                 are also recognized as whitespace            *
//*                                 (see documentation for details)              *
//*                                  '、' 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 formatted paragraph                          *
//********************************************************************************
//* Notes on automatic word wrap:                                                *
//* -----------------------------                                                *
//* See gString documentation.                                                   *
//*                                                                              *
//********************************************************************************

short AnsiCmd::acFormatParagraph ( gString& gsTxt, short maxRows, short maxCols, 
                                   bool trunc, bool hyphenbrk, int* truncIndex )
{

   if ( maxRows < 1 ) { maxRows = 1 ; }   // range-check parameters
   if ( maxCols < 4 ) { maxCols = 4 ; }
   return ( (gsTxt.formatParagraph ( maxRows, maxCols, trunc, hyphenbrk, truncIndex )) ) ;

}  //* acFormatParagraph() *

//*************************
//*   acFormatParagraph   *
//*************************
//********************************************************************************
//* Reformat the provided text to fit within the specified dimensions.           *
//*  1) Remove all newlines from the text.                                       *
//*  2) Perform word wrap to limit 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)                        *
//*                                                                              *
//* Input  : wTxt     : on entry, contains raw source text                       *
//*                     on return, contains formatted text                       *
//*                     Note that the source data are limited only by the maximum*
//*                     character capacity of a gString object. (gsALLOCMAX)     *
//*                     NOTE: Be sure to leave room for the addition of          *
//*                           newline ('\n') characters during formatting.       *
//*          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. U+0020 and U+3000           *
//*                                 Special Case: the following CJK ideographs   *
//*                                 are also recognized as whitespace            *
//*                                 (see documentation for details)              *
//*                                  '、' 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 formatted paragraph                          *
//********************************************************************************
//* Notes on truncation of output:                                               *
//* ------------------------------                                               *
//* 1) If 'trunc' is reset AND 'truncIndex' == NULL, then all source data will   *
//*    be processed, limited only by the maximum conversion-buffer size.         *
//* 2) If 'trunc' is reset AND 'truncIndex' != NULL, then all source data will   *
//*    be processed, and '*truncIndex' will receive the index of the newline     *
//*    character at the end of the text row where the text WOULD-HAVE BEEN       *
//*    truncated to comply with 'maxRows' limit.                                 *
//* 3) If 'trunc' is set, then if the source data is too long, the output will   *
//*    be truncated to the number of text rows specified by 'maxRows'.           *
//* 4) In the (very rare) case when source data exceed buffer capacity           *
//*    (i.e. several thousand formatted text rows), then data will be silently   *
//*    truncated to fit.                                                         *
//*                                                                              *
//********************************************************************************

short AnsiCmd::acFormatParagraph ( wchar_t* wTxt, short maxRows, short maxCols, 
                                   bool trunc, bool hyphenbrk, int* truncIndex )
{
   int   maxChars = ZERO,                 // maximum buffer space available
         charCnt  = ZERO ;                // number of source characters
   short outRows  = ZERO ;                // return value

   if ( maxRows < 1 ) { maxRows = 1 ; }   // range-check parameters
   if ( maxCols < 4 ) { maxCols = 4 ; }
   maxChars = gsALLOCMAX - maxRows - 2 ;

   //* Count the source characters (codepoints), including null terminator. *
   while ( wTxt[charCnt++] != NULLCHAR ) ;

   //* If the source data fit within a gString object, then reformat *
   //* the full data stream. This should (nearly) always be true.    *
   //* In the rare case that caller's data exceeds the maximum buffer*
   //* size, then some data may be discarded.                        *
   if ( charCnt > maxChars )
      wTxt[maxChars] = NULLCHAR ;

   gString gsFmt( wTxt ) ;             // Create the work buffer
   outRows = this->acFormatParagraph ( gsFmt, maxRows, maxCols, trunc, hyphenbrk, truncIndex ) ;
   gsFmt.copy( wTxt, gsFmt.gschars() ) ;

   return outRows ;

}  //* acFormatParagraph() *

//*************************
//*      acShellOut       *
//*************************
//********************************************************************************
//* Temporarily return control to terminal shell program.                        *
//*                                                                              *
//* I/O buffering and input echo are returned to the terminal defaults.          *
//* Because the application will not be able to see the various signals          *
//* which have captured within the application, these signals will be seen       *
//* by the shell program or any utility executing within the shell.              *
//* See the acCaptureSignals() method or refer to the documentation for a        *
//* discussion of available signal handlers.                                     *
//*                                                                              *
//* This method calls the standard library 'system' function.                    *
//* Review standard library docs for details:                                    *
//*    info system                                                               *
//*    man 3 system                                                              *
//* The 'system' function uses members of the 'exec' function group to format    *
//* and execute the command and its arguments.                                   *
//*                                                                              *
//* Input  : cmd    : (optional, default: null pointer) command to be executed   *
//*                   -- any valid command, or sequence of commands may be       *
//*                      specified, using the syntax recognized by the shell     *
//*                      program.                                                *
//*                   -- if a command is not specified, the default shell        *
//*                      program is invoked with a message similar to:           *
//*                      "type 'exit' to return."                                *
//*          clrWin : (optional, default: false)                                 *
//*                   if 'false', the called application begins output at the    *
//*                               current cursor position                        *
//*                   if 'true',  clear the terminal window before invoking the  *
//*                               command. The command will appear at the top    *
//*                               of the terminal window                         *
//*          txtAttr: (optional, default: acaUSEDFLT)                            *
//*                   -- if the default value is seen, the the current color     *
//*                      attribute settings will be used for both the optional   *
//*                      clear of the window and for the text written by the     *
//*                      called terminal application.                            *
//*                   -- if a non-default value is specified, it is used for     *
//*                      both clearing the terminal window ('clrWin' parameter)  *
//*                      and as the color attributes used by the called program  *
//*                      for writing text to the window.                         *
//*                      Note: Many console utilities override the existing      *
//*                            terminal attributes, so the specified attribute   *
//*                            may be ignored.                                   *
//*                                                                              *
//* Returns: exit code of child process                                          *
//*           Ideally, this is the exit code of the last utility run in the      *
//*           child process, but this cannot be guaranteed.                      *
//********************************************************************************

int AnsiCmd::acShellOut ( const char* cmd, bool clrWin, acAttr txtAttr )
{
   gString shellCmd( cmd ) ;              // command transmitted to shell
   EchoOpt orig_inpEch = this->tset->inpEch ; // saved configuration
   bool    orig_inpBuf = this->tset->inpBuf,
           orig_inpBlk = this->tset->inpBlk ;
   aeSeq   orig_fgnd   = this->attrBits.aesFgnd,
           orig_bgnd   = this->attrBits.aesBgnd ;
   int status = ZERO ;                    // return value

   //* If specified, clear the terminal window *
   if ( clrWin )
      this->acClearScreen ( L' ', txtAttr ) ;

   //* If alternate color attributes specified */
   if ( txtAttr != acaUSEDFLT )
   {
      //* Convert acAttr to aes format, then set *
      //* attributes using the low-level method. *
      acaExpand acx( txtAttr ) ;
      this->acSetFgBg ( acx.aesFgnd, acx.aesBgnd ) ;
   }

   //* If no command was specified, invoke the default shell program.*
   //* 1) Query the environment for the shell filespec.              *
   //* 2) Isolate the shell filename.                                *
   //* 3) Construct the invocation string with message.              *
   // Programmer's Note: If the "SHELL" environment variable is not defined, 
   // "bash" will be invoked.
   if ( (cmd == NULL) || (*cmd == NULLCHAR) )
   {
      char shellProg[64] ;
      shellCmd = secure_getenv ( "SHELL" ) ;
      if ( (shellCmd.gschars()) == 1 ) { shellCmd = "bash" ; }
      int indx = ((shellCmd.findlast( L'/' )) + 1) ;
      if ( indx > ZERO )
         shellCmd.shiftChars( -(indx) ) ;
      shellCmd.copy( shellProg, 64 ) ;
      shellCmd.compose( 
         "echo \"****************************\n"
                "** Type 'exit' to return. **\n"
                "****************************\""
                ";%s", shellProg ) ;
   }

   //* Return I/O control to terminal window *
   this->acBufferedInput ( true, termEcho, true ) ;

   //* Invoke the external application or shell program *
   status = system ( shellCmd.ustr() ) ;

   //* Re-establish application control over the terminal I/O streams. *
   this->acBufferedInput ( orig_inpBuf, orig_inpEch, orig_inpBlk ) ;

   //* Restore previous attributes *
   if ( txtAttr != acaUSEDFLT )
      this->acSetFgBg ( orig_fgnd, orig_bgnd ) ;

   return status ;

}  //* End acShellOut


               //** ------------------------------ **
               //**   Line and Box Drawing Group   **
               //** ------------------------------ **
//*************************
//*      acDrawLine       *
//*************************
//********************************************************************************
//* Draw a horizontal or vertical line in the terminal window.                   *
//* Horizontal lines are drawn left-to-right.                                    *
//* Vertical lines are drawn top-to-bottom.                                      *
//*                                                                              *
//* Optionally, the endpoint character(s) may be specified for the beginning     *
//* and/or end of the line. If the endpoint(s) not specified, the line-drawing   *
//* character is used. The valid endpoint characters include any of the          *
//* line-drawing glyphs defined in AnsiCmdDef.hpp. Note however, that the        *
//* 'startChar' and 'endChar' parameter are not range checked, so whatever       *
//* character (if any) is specified, is written.                                 *
//* (Use single-column characters only.)                                         *
//*                                                                              *
//* Examples:                                                                    *
//*   startChar == wcsLTEE and endChar == wcsRTEE:                               *
//*   startChar == wcsLTEE and endChar == wcsINSECT:                             *
//*   startChar == wcsTTEE and        startChar == wcsUL and                     *
//*   endChar == wcsBTEE:             endChar === wcsLL:                         *
//*                                                                              *
//*                                                                              *
//*                                                                              *
//*                                                                              *
//* Input  : pos     : offset from upper-left corner of terminal window to       *
//*                    upper-left corner of box (1-based)                        *
//*          length  : length of the line: number of number of rows (vertical),  *
//*                    or number of columns (horizontal).                        *
//*          vertical: 'true'  : vertical line                                   *
//*                    'false' : horizontal line                                 *
//*          lType   : (optional, ltSINGLE by default) line style                *
//*                     member of enum LineType: either ltSINGLE or ltDUAL       *
//*          begChar : (optional, null character by default)                     *
//*                     specify the beginning endpoint character                 *
//*          endChar : (optional, null character by default)                     *
//*                     specify the final endpoint character                     *
//*                                                                              *
//* Returns: cursor position at the end of the line:                             *
//*          For horizontal lines, the column following the last character.      *
//*          For vertical lines, the column below the last character.            *
//********************************************************************************

WinPos AnsiCmd::acDrawLine ( const WinPos& wpOrig, short length, bool vertical, 
                             LineType lType, wchar_t begChar, wchar_t endChar )
{
   gString gs, gs2 ;
   WinPos  wp = wpOrig ;
   wchar_t lt ;

   if ( length < 2 )             // minimum length == 2 endpoints
      length = 2 ;

   if ( vertical )               // vertical line
   {
      if ( lType == ltDUAL )     // dual line
         lt = wcsVERTd ;
      else
         lt = wcsVERT ;

      wchar_t pair[3] ;          // vertical character + newline
      short   px = ((length * 2) - 4) ; // number of char+newline pairs (not incl. endpoints)
      gs.compose( "%C\n", &lt ) ;
      gs.copy( pair, 3 ) ;
      if ( px > 1 )              // build the line
      {
         while ( gs.gschars() < px )
            gs.append( pair ) ;
      }
      else if ( px == ZERO )
         gs.clear() ;

      if ( begChar == L'\0' )  begChar = lt ;   // if default endpoints
      if ( endChar == L'\0' )  endChar = lt ;
      gs2.compose( "%C\n", &begChar ) ;
      gs.insert( gs2.gstr() ) ;                 // attach the endpoints
      gs.append( "%C\n", &endChar ) ;
   }

   else                          // horizontal line
   {
      if ( lType == ltDUAL )     // dual line
         lt = wcsHORIZd ;
      else
         lt = wcsHORIZ ;
      if ( length > 2 )
         gs.padCols( (length - 2), lt ) ; // build the line

      if ( begChar == L'\0' )  begChar = lt ;   // if default endpoints
      if ( endChar == L'\0' )  endChar = lt ;
      gs.insert( begChar ) ;                    // attach the endpoints
      gs.append( endChar ) ;
   }

   wp = this->acWrite ( wp, gs ) ;

   return wp ;

}  //* End acDrawLine

//*************************
//*       acDrawBox       *
//*************************
//********************************************************************************
//* Draw a box in the terminal window and optionally write text in the box.      *
//* This is a greatly simplified form of drawing a window. It simply draws a     *
//* rectangle in the terminal window at the specified position and of the        *
//* specified dimensions.                                                        *
//* The box is drawn using the current foreground/background colors.             *
//*                                                                              *
//* If parameters are out-of-range, the position and dimensions will be          *
//* modified as necessary to fit within the terminal window.                     *
//*                                                                              *
//* Input  : pos   : offset from upper-left corner of terminal window to         *
//*                  upper-left corner of box (1-based)                          *
//*          height: number of rows for box                                      *
//*          width : number of columns for box                                   *
//*          lType : (optional, ltSINGLE by default) line style                  *
//*                  member of enum LineType: either ltSINGLE or ltDUAL          *
//*          text  : (optional, null pointer by default)                         *
//*                  If specified, text to be written to interior of box.        *
//*                                                                              *
//* Returns: current cursor position within the box                              *
//********************************************************************************

WinPos AnsiCmd::acDrawBox ( WinPos pos, short height, short width, 
                           LineType lType, const char *text )
{
   gString gsOut ;                        // text formatting
   wchar_t ul, ur, ll, lr, h, v ;         // characters for drawing border
   WinPos wp ;                            // return value

   //* Silently convert from zero-based to one-based mapping *
   if ( pos.row == ZERO )
      pos.row = 1 ;
   if ( pos.col == ZERO )
      pos.col = 1 ;
   if ( pos.row > (this->termRows - 2) )  // range check position
      pos.row = this->termRows - 2 ;
   if ( pos.col > (this->termCols - 2) )
      pos.col = this->termCols - 2 ;

   //* Target area must be fully inside the terminal window *
   if ( (pos.row + height - 1) > this->termRows )
   {
      if ( height > this->termRows )
         height = this->termRows ;
      pos.row = this->termRows - height + 1 ;
   }
   if ( (pos.col + width - 1) > this->termCols )
   {
      if ( width > this->termCols )
         width = this->termCols ;
      pos.col = this->termCols - width + 1 ;
   }

   //* This is the origin (upper-left corner) of target area.*
   wp = pos ;

   if ( (lType != ltSINGLE) && (lType != ltDUAL) ) // limit line type
      lType = ltSINGLE ;

   //* Clear the target area *
   this->acEraseBox ( pos, height, width ) ;

   //* Initialize the drawing characters according to the line type *
   switch ( lType )
   {
      case ltDUAL:                     // dual line
         ul = wcsULd ;  ur = wcsURd ;    ll = wcsLLd ;
         lr = wcsLRd ;  h = wcsHORIZd ;  v = wcsVERTd ;
         break ;
      case ltSINGLE:                   // single-line normal
      default:
         ul = wcsULs ;  ur = wcsURs ;   ll = wcsLLs ;
         lr = wcsLRs ;  h = wcsHORIZ ;  v = wcsVERT ;
         break ;
   } ;

   //* Draw top border *
   gsOut.compose( "%C", &ul ) ;
   gsOut.padCols( width - 1, h ) ;
   gsOut.append( "%C", &ur ) ;
   this->acSetCursor ( wp.row++, wp.col ) ;
   this->ttyWrite ( gsOut, false ) ;
   wp.col += width - 1 ;

   //* Create the vertical bar for left and right borders.*
   //* Draw the right border, then the left border.       *
   gsOut.clear() ;
   for ( short irows = 2 ; irows < height ; ++irows )
      gsOut.append( "%C\n", &v ) ;
   this->acWrite ( wp, gsOut, false ) ;
   wp.col = pos.col ;
   wp = this->acWrite ( wp, gsOut, false ) ;

   //* Draw bottom border *
   gsOut.compose( "%C", &ll ) ;
   gsOut.padCols( width - 1, h ) ;
   gsOut.append( "%C", &lr ) ;
   this->ttyWrite ( gsOut ) ;

   wp = { short(pos.row + 1), short(pos.col + 1) } ; // initial text position

   //* If text data provided, display it.*
   if ( text != NULL )
   {
      wp = this->acWrite ( wp, text ) ;

      //* If text has gone into or beyond the border, minimize *
      //* the damage by moving the cursor back inside the box. *
      bool reposition = false ;
      if ( wp.row >= (pos.row + height - 1) )
      { wp.row = pos.row + height - 2 ; reposition = true ; }
      if ( wp.col >= (pos.col + width - 1) )
      { wp.col = pos.col + width - 2 ; reposition = true ; }
      if ( reposition )
         this->acSetCursor ( wp ) ;
   }
   else
      this->acSetCursor ( wp ) ;

   return wp ;

}  //* End acDrawBox() *

//*************************
//*      acEraseBox       *
//*************************
//********************************************************************************
//* Erase the specified area. This is a companion method to the acDrawBox()      *
//* method above. acEraseBox() simply calls the low-level method to clear the    *
//* text from the specified area.                                                *
//*                                                                              *
//* Input  : pos   : offset from upper-left corner of terminal window to         *
//*                  upper-left corner of box (1-based)                          *
//*          height: number of rows for box                                      *
//*          width : number of columns for box                                   *
//*                                                                              *
//* Returns: nothing                                                             *
//********************************************************************************

void AnsiCmd::acEraseBox ( WinPos wpOrig, short height, short width )
{

   this->acClearArea ( wpOrig.row, wpOrig.col, height, width ) ;

}  //* End acEraseBox() *

//*************************
//*       acBoxDraw       *
//*************************
//********************************************************************************
//* Display the data of an AC_Box object in the terminal window.                 *
//*                                                                              *
//* 1) Clear the target area.                                                    *
//* 2) Draw the box.                                                             *
//* 3) Initialize the interior of the box to the specified background color.     *
//* 4) If specified, write the initial text to the interior of the box.          *
//*    a) Text will be written using the foreground and background attributes    *
//*       specified for the interior.                                            *
//*    b) Caller is responsible for ensuring that the text fits entirely         *
//*       within the interior area, AND that the final cursor position also      *
//*       lies within the interior area. Automatic text formatting is not        *
//*       performed.                                                             *
//* 5) On return, the visible cursor will be positioned at the origin            *
//*    (upper-left corner) of the text area UNLESS initial text data were        *
//*    specified, in which case the visible cursor will be positioned at the     *
//*    end-of-text.                                                              *
//* 6) On return, color attributes will have been set to terminal defaults.      *
//*                                                                              *
//*                                                                              *
//* Input  : box : (by reference) configuration parameters for the object        *
//*          text: (optional, null pointer by default)                           *
//*                If specified, text to be written to interior of box.          *
//*                                                                              *
//* Returns: coordinates of upper-left corner of text area within the box        *
//*          (These coordinates are the one-based offset from the )              *
//*          (upper-left corner of the terminal window.           )              *
//********************************************************************************

WinPos AnsiCmd::acBoxDraw ( AC_Box& box, const char* text )
{
   WinPos wpTxt ;                   // return value

   //* Box must lie fully inside the terminal window.     *
   //* Minimum dimensions are 3 rows by 3 columns.        *
   //* The box is resized if necessary to fit within the  *
   //* terminal window. (This is a 1-base calculation.)   *
   //* Note: The lower limits for the arguments have been *
   //*       validated during instantiation.              *
   if ( (box.orig.row + box.hgt - 1) > this->termRows )
   {
      if ( box.hgt > this->termRows )
         box.hgt = this->termRows ;
      box.orig.row = this->termRows - box.hgt + 1 ;
   }
   if ( (box.orig.col + box.wid - 1) > this->termCols )
   {
      if ( box.wid > this->termCols )
         box.wid = this->termCols ;
      box.orig.col = this->termCols - box.wid + 1 ;
   }

   //* Line type limited to either ltSINGLE (default) or ldDUAL.*
   if ( box.lnType != ltDUAL )
      box.lnType = ltSINGLE ;

   //* Set the color attributes *
   this->acSetFgBg ( box.bFgnd, box.bBgnd ) ;

   //* Draw the box *
   this->acDrawBox ( box.orig, box.hgt, box.wid, box.lnType ) ;

   //* If a title was specified, insert it into the top border.*
   if ( *box.title != NULLCHAR )
   {
      //* Calculate cursor position for center of top border *
      WinPos wpTit( box.orig.row, (box.orig.col + (box.wid / 2)) ) ;
      gString gsTit( box.title ) ;           // working copy of text
      gsTit.limitCols( box.wid - 4 ) ;       // truncate title text if necessary
      wchar_t rt = wcsRTEEs, lt = wcsLTEEs ; // right/left delimiters
      if ( box.lnType == ltDUAL )
      { rt = wcsRTEEd ; lt = wcsLTEEd ; }
      gsTit.insert( rt ) ;                   // add title delimeters
      gsTit.append( lt ) ;
      wpTit.col -= gsTit.gscols() / 2 ;      // left column of title text
      if ( wpTit.col <= box.orig.col )       // (may be off-by-one)
         wpTit.col = box.orig.col + 1 ;
      this->acSetCursor ( wpTit ) ;
      this->ttyWrite ( gsTit ) ;             // draw the title
    }

   //* Initialize the interior of the box.*
   //* Origin of text area is returned.   *
   wpTxt = this->acBoxClear ( box ) ;

   //* If initial text provided, display it.*
   if ( text != NULL )
      this->acWrite ( wpTxt, text ) ;

   //* Return attributes to terminal defaults.*
   this->acSetFgBg ( aesFG_DFLT, aesBG_DFLT ) ;

   return ( wpTxt ) ;         // return coordinates of text origin

}  //* End acBoxDraw() *

//*************************
//*      acBoxErase       *
//*************************
//********************************************************************************
//* Erase the rectangular area drawn by the acBoxDraw() method, above.           *
//*                                                                              *
//* Input  : box : (by reference) configuration parameters for the object        *
//*                                                                              *
//* Returns: nothing                                                             *
//********************************************************************************

void AnsiCmd::acBoxErase ( const AC_Box& box )
{

   this->acClearArea ( box.orig, box.hgt, box.wid ) ;
   this->acSetCursor ( box.orig ) ;

}  //* End acBoxErase() *

//*************************
//*      acBoxClear       *
//*************************
//********************************************************************************
//* Erase the text area of of an AC_Box object.                                  *
//*                                                                              *
//* 1) On return, the foreground/background color attributes are set to the      *
//*    colors specified for the interior of the object.                          *
//* 2) On return, the visible cursor is set to the upper-left corner of the      *
//*    text area.                                                                *
//*                                                                              *
//* Input  : box : (by reference) configuration parameters for the object        *
//*                                                                              *
//* Returns: coordinates of upper-left corner of text area within the box        *
//********************************************************************************

WinPos AnsiCmd::acBoxClear ( const AC_Box& box )
{
   //* Base position of text area within the box.*
   //* This is the return value.                 *
   WinPos wpTxt( (box.orig.row + 1), (box.orig.col + 1) ) ;

   //* Set color attributes *
   this->acSetFgBg ( box.tFgnd, box.tBgnd ) ;

   //* Clear the interior *
   this->acClearArea ( wpTxt, (box.hgt - 2), (box.wid - 2) ) ;

   //* Set the cursor to text origin *
   this->acSetCursor ( wpTxt ) ;

   return ( wpTxt ) ;

}  //* End acBoxClear() *

               //** ------------------------------ **
               //**      Temporary File Group      **
               //** ------------------------------ **
//*************************
//*   acCreateTemppath    *
//*************************
//********************************************************************************
//* Create a unique sub-directory in the system's temp-file directory to contain *
//* the application's temporary files. This is done by creating an instance of   *
//* the 'TFMan' class.                                                           *
//*                                                                              *
//* Input  : prefix : (optional, default: null pointer)                          *
//*                   specify a constant token which will prefix the name of     *
//*                   the temporary sub-directory in which temp files will be    *
//*                   created.                                                   *
//*                   Important Note: Use only valid filename characters         *
//*                                   (not "special" chars) for the prefix string*
//*                                                                              *
//* Returns: 'true'  if path/dirname created successfully                        *
//*          'false' if call to standard library function failed                 *
//********************************************************************************

bool AnsiCmd::acCreateTemppath ( const char* prefix )
{
   //* Default sub-directory name prefix *
   const char* const dfltPrefix = "ANSICMD_" ;

   if ( this->tfm == NULL )
   {
      this->tfm = new TFMan ( (prefix != NULL) ? prefix : dfltPrefix ) ;
   }
   return ( (this->tfm->isActive ()) ) ;

}  //* End acCreateTemppath() *

//*************************
//*   acDeleteTemppath    *
//*************************
//********************************************************************************
//* Delete the sub-directory containing the application's temporary files and    *
//* any files remaining in that subdirectory.                                    *
//*   1) May be called directly by the application layer, or else                *
//*   2) Will be called by the AnsiCmd-class destructor.                         *
//*                                                                              *
//* Input  : none                                                                *
//*                                                                              *
//* Returns: nothing                                                             *
//********************************************************************************

void AnsiCmd::acDeleteTemppath ( void )
{

   if ( this->tfm != NULL )
   { delete this->tfm ; this->tfm = NULL ; }

}  //* End acDeleteTemppath() *

//*************************
//*   acCreateTempfile    *
//*************************
//********************************************************************************
//* Create a unique path/filename for a temporary file, then create the file.    *
//* The file will have been closed and will be empty (will contain no data).     *
//*                                                                              *
//* Input  : tmpPath: (by reference) receives the path/filename of the new file  *
//*                                                                              *
//* Returns: 'true'  if path/filename created successfully                       *
//*          'false' if library call failed                                      *
//********************************************************************************

bool AnsiCmd::acCreateTempfile ( gString& tmpPath )
{
   bool status = false ;                  // return value
   tmpPath.clear() ;                      // initialize caller's buffer

   //* If temp-file manager not yet initialized, do it now. *
   if ( this->tfm == NULL )
      this->acCreateTemppath () ;
   if ( this->tfm != NULL )
      status = this->tfm->createTempFile ( tmpPath ) ;
   return status ;

}  //* End acCreateTempfile() *

//*************************
//*   acDeleteTempfile    *
//*************************
//********************************************************************************
//* Delete the specified temporary file.                                         *
//*                                                                              *
//* Input  : tmpPath : full path/filename specification of source                *
//*                                                                              *
//* Returns: 'true'  if successfully deleted                                     *
//*          'false' if library call failed                                      *
//********************************************************************************

bool AnsiCmd::acDeleteTempfile ( const gString& tmpPath )
{
   bool status = false ;                  // return value

   //* If temp-file manager not yet initialized, do it now. *
   //*  (If this happened, it would be incredibly dumb, )   *
   //*  (but we programmers tend to be simple creatures.)   *
   if ( this->tfm == NULL )
      this->acCreateTemppath () ;

   if ( this->tfm != NULL )
      status = this->tfm->deleteTempFile ( tmpPath ) ;

   return status ;

}  //* End acDeleteTempfile() *

//*-----------------------*
#endif   //* INCLUDE_API  *
//*-----------------------*


//** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -**
//** - - - - - - - - - - Command-Line Parsing Methods  - - - - - - - - - - - -**
//** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -**

//*************************
//*     ansicmdSetup      *
//*************************
//********************************************************************************
//* Non-member Method:                                                           *
//* ------------------                                                           *
//* Decode the terminal-setup arguments provided in the "--term" command-line    *
//* option: 'gsCmd'. The decoded parameters are stored in the 'tCfg' object.     *
//*                                                                              *
//* Note: The documentation states that parameters must be seperated by commas;  *
//*       however, parameters may also be seperated by spaces if the parameters  *
//*       are properly delimited with quotation marks. Equivalent:               *
//*       These are equivalent:  --term='B:ux,C:y,W:n,L:en_US.utf8'              *
//*                              --term='B:ux C:y W:n L:en_US.utf8'              *
//*       Note also that we rely on the fact that valid locale filenames         *
//*       never contain spaces.                                                  *
//*                                                                              *
//* Input  : gsCmd   : (by reference) "--term=SETUP" command string              *
//*          tCfg    : (by reference) receives decoded terminal configuration    *
//*                    options                                                   *
//*                                                                              *
//* Returns: 'true'  if valid argument(s) specified and decoded                  *
//*          'false' if invalid argument(s) or parsing error                     *
//********************************************************************************
//* Programmer's Note: The parsing of the terminal-setup parameter is rather     *
//* detailed, and may be difficult to follow. For this reason, we have written   *
//* detailed comments below to assist in describing what is actually happening.  *
//*                                                                              *
//* -- The --term parameter has multiple arguments, all optional.                *
//*    -- W: Width of streams stdin/stdout/stderr                                *
//*          Arguments: [w | n] (default: w)                                     *
//*                                                                              *
//*    -- A: Compatibility with the ANSI standard for escape sequence            *
//*          Arguments: [y | n] (default: y)                                     *
//*                                                                              *
//*    -- B: Buffering of the input stream with echo-type selection              *
//*          Arguments: [b | u] (default: b)                                     *
//*          Sub-arguments for 'u' argument only: [x | y | s] (default: x)       *
//*             Sub-argument is optional and 'x' is assumed if not specified.    *
//*          For softEcho options only, sub-sub-options specify the way          *
//*          special keycodes (cursor keys, bksp/del keys) are handled:          *
//*          See specialkeyDecode() for details.                                 *
//*          'a' Translate all supported special keycodes. (default)             *
//*          'c' Translate the cursor keycodes only (discard Bksp/Del keycodes)  *
//*          'b' Translate only the Bksp/Del keycodes (discard cursor keycodes)  *
//*          'd' Disable special-keycode translation i.e. discard non-printing   *
//*              characters.                                                     *
//*                                                                              *
//*    -- C: Color attributes for terminal foreground and background.            *
//*          Arguments: [fg[bg]]                                                 *
//*             foreground designation (required):                               *
//*             d (default) or color: k|r|g|n|b|m|c|y                            *
//*             background designation (optional):                               *
//*             d (default) or color: k|r|g|n|b|m|c|y                            *
//*          fg and bg are one of the following: d==Default, b==blacK, r==Red,   *
//*                   g==Green n==browN, b==Blue, m==Magenta, c==Cyan, g==greY   *
//*                                                                              *
//*    -- L: Locale filename                                                     *
//*          Arguments: ['e' | locale_name] (default: 'e')                       *
//*          'e' indicates that locale from environment s/b used.                *
//*          If no argument specified, 'e' is assumed.                           *
//*          If a filename is specified, we do not attempt to validate it as     *
//*          an actual filename or as a valid locale name. Note that if an       *
//*          invalid locale filename is passed to the system, it will throw      *
//*          an exception: 'std::runtime_error'                                  *
//*                                                                              *
//*    -- P  Capture Control+C (break key)                                       *
//*          Arguments: [[i|e|u]t]                                               *
//*          For 'i', 'e' and 'u' the application captures the break signal      *
//*          and handles it according to the specified handler option.           *
//*         - i : Ignore break-key signal.                                       *
//*         - e : Exit application on receipt of break-key signal.               *
//*         - u : User is asked whether to exit the application.                 *
//*         - t : Terminal handles break-key signal, killing application         *
//*                immediately. (default). [This is very rude :-]"               *
//*                                                                              *
//*    -- Z Capture Control+Z (suspend key)                                      *
//*          Arguments: [y | n] (default: y)                                     *
//*                                                                              *
//*    -- S  Cursor Style (shape)                                                *
//*          Arguments; [d | b[b|s] | u[b|s] | v[b|s]]                           *
//*         - d : Default shape specified in the terminal preferences.           *
//*         - b : Block shape: full character cell glyph                         *
//*         - u : Underline shape: bottom pixel row of character cell            *
//*         - v : Vertical Bar shape: left pixel column of character cell        *
//*          For 'b', 'u' and 'v' options the sub-option is either:              *
//*          'b'="Blinking" (default if sub-option not specified)                *
//*          's'="Steady" i.e. not blinking                                      *
//*          Note: In a future release, we may implement a color attribte as     *
//*                the fifth argument. Currently, the terminal programs ignore   *
//*                attempts to set the color attribute for the cursor.           *
//*                                                                              *
//* -- The design of this parameter is open so that we can add more terminal     *
//*    setup parametes in the future.                                            *
//*                                                                              *
//* -- Inside the Loop:                                                          *
//*    1) Copy the first four(4) characters into four temp variables:            *
//*       parm: Parameter (W | C | B | L | etc.)                                 *
//*       colo: s/b the colon separating the parameter from its arguement(s)     *
//*       parg: Parameter argument (see above)                                   *
//*       arg4: This is the sub-argument for options that require it.            *
//*       arg5: This is the sub-sub-argument for options that require it.        *
//*    2) Test for the colon ':' character to be present and in the correct      *
//*       position. If it is not, we return an error condition.                  *
//*    3) The switch validates the parameter character and parses its            *
//*       arguments.                                                             *
//*    4) The parameter which has just been processed is then shifted out        *
//*       of the buffer 'gs', so the next parameter will be at the beginning     *
//*       of the string.                                                         *
//*                                                                              *
//*    Note that we allow multiple instances of the same parameter, so if the    *
//*    user is dumb enough to provide multiple instances, the last one will      *
//*    be used.                                                                  *
//*                                                                              *
//*    The decoded setup parameters are then returned to caller in the           *
//*    TermConfig object.                                                        *
//*                                                                              *
//* -- To fascilitate future parameter additions, the debugging code used        *
//*    during development has been retained under a conditional-compile flag:    *
//*        DEBUG_TERMSETUP (and DEBUG_TS_PAUSE)                                  *
//********************************************************************************

bool ansicmdSetup ( const gString& gsCmd, TermConfig& tCfg )
{
   #define DEBUG_TERMSETUP (0)      // for debugging only
   #if DEBUG_TERMSETUP != 0
   #define DEBUG_TS_PAUSE (0)
   #endif   // DEBUG_TERMSETUP

   gString gsLocale ;               // for capture of alternate locale name
   #if DEBUG_TERMSETUP != 0
   gString gsDbg ;
   #endif   // DEBUG_TERMSETUP
   wchar_t parm, colo, parg, arg4, arg5 ; // for parsing of arguments
   short off ;                      // offset index
   bool status = false ;            // return value

   //* If command string properly formed and non-empty arguments *
   if ( ((off = gsCmd.after( L'=' )) > ZERO) && (gsCmd.gstr()[off] != NULLCHAR) )
   {
      //* Application may have set fg/bg color attributes *
      //* through another method, so save them here and   *
      //* restore them after reset.                       *
      aeSeq fgTemp = tCfg.foreground,
            bgTemp = tCfg.background ;
      tCfg.reset() ;                               // initialize caller's object
      tCfg.foreground = fgTemp ;
      tCfg.background = bgTemp ;

      status = true ;                              // command format is valid

      gString gs( &gsCmd.gstr()[off] ) ;           // local copy of parameters
      gs.strip() ;                                 // strip leading/trailing whitespace
      gs.replace( L',', L' ', ZERO, false, true) ; // replace all commas with spaces

      #if DEBUG_TERMSETUP != 0
      gsDbg.compose( "ansicmdSetup(%S)", gs.gstr() ) ;
      wcout << gsDbg.gstr() << endl ;
      #endif   // DEBUG_TERMSETUP
      do
      {
         //* Get the first five(5) characters of the buffer: *
         //* parm == parameter                               *
         //* colo == colon seperator (':')                   *
         //* parg == argument to the parameter               *
         //* arg4 == used for buffering, color and locale    *
         //* arg5 == currently soft-echo sub-sub options only*
         off = ZERO ;
         colo = parg = arg4 = arg5 = DASH ; // initialize argument variables
         if ( (parm = toupper ( gs.gstr()[off++] )) != NULLCHAR )
            if ( (colo = tolower ( gs.gstr()[off++] )) != NULLCHAR )
               if ( (parg = tolower ( gs.gstr()[off++] )) != NULLCHAR )
                  if ( (arg4 = tolower ( gs.gstr()[off++] )) != NULLCHAR )
                     arg5 = tolower ( gs.gstr()[off++] ) ;

         #if DEBUG_TERMSETUP != 0
         if ( parm != NULLCHAR )
         {
            if ( (colo != L':') || (parg == NULLCHAR) || (parg == SPACE) )
            { parg = arg4 = arg5 = DASH ; }
            else if ( (arg4 == NULLCHAR) || (arg4 == SPACE) )
            { arg4 = arg5 = DASH ; }
            else if ( (arg5 == NULLCHAR) || (arg5 == SPACE) )
            { arg5 = DASH ; }

            gsDbg.compose( "TOKEN: '%C' '%C' '%C'", &parm, &colo, &parg ) ;
            if ( (parm == L'B') || (parm == L'T') || (parm == L'S') || (parm == L'P') )
            {
               gsDbg.append( " '%C'", &arg4 ) ;
               if ( (parm == L'B') && (parg == 'u') && (arg4 == L's') )
               {
                  gsDbg.append( " '%C'", &arg5 ) ;
               }
            }
            wcout << gsDbg << endl ;
         }
         #endif   // DEBUG_TERMSETUP

         if ( parm == NULLCHAR )          // end-of-text
            break ;
         if ( colo != L':' )              // incorrect token format
         { status = false ; break ; }

         switch ( parm )
         {
            case L'W':                    // Width of I/O streams
               if ( parg == L'w' )        tCfg.wideStream = true ;
               else if ( parg == L'n' )   tCfg.wideStream = false ;
               else                       status = false ;
               gs.shiftChars( -3 ) ;
               gs.strip() ;

               #if DEBUG_TERMSETUP != 0
               gsDbg.compose( "->%C (%C) wideStream:%hhd", 
                              &parm, &parg, &tCfg.wideStream ) ;
               wcout << gsDbg << endl ;
               #endif   // DEBUG_TERMSETUP
               break ;

            case L'A':                    // ANSI compliance
               if ( parg == L'y' )        tCfg.nonStandard = false ;
               else if ( parg == L'n' )   tCfg.nonStandard = true ;
               else                       status = false ;
               gs.shiftChars( -3 ) ;
               gs.strip() ;

               #if DEBUG_TERMSETUP != 0
               gsDbg.compose( "->%C (%C) nonStandard:%hhd", 
                              &parm, &parg, &tCfg.nonStandard ) ;
               wcout << gsDbg << endl ;
               #endif   // DEBUG_TERMSETUP
               break ;

            case L'B':                    // Buffering
               {  // guard local variables
               //* Buffered vs. Non-buffered not specified? Set default. *
               if ( (parg == SPACE) || (parg == NULLCHAR) || (parg == DASH) )
                  parg = L'b' ;        // buffering enabled

               //* Echo-type sub-argument not specified? Set default.*
               if ( (arg4 == SPACE) || (arg4 == NULLCHAR) || (arg4 == DASH) )
               {
                  if ( parg == L'b' )  arg4 = L't' ;     // terminal echo
                  else                 arg4 = L'x' ;     // no echo
               }

               //* Validate the echo type *
               EchoOpt tmpEcho = noEcho ;
               switch ( arg4 )
               {
                  case L'x': tmpEcho = noEcho ;    break ;  // noEcho
                  case L't': tmpEcho = termEcho ;  break ;  // termEcho
                  case L's': tmpEcho = softEchoA ; break ;  // softEchoA (all)
                  default:   status = false ;      break ;  // invalid echo option
               } ;
               if ( status != false )
               {
                  //* Initialized the buffered-input flag *
                  if ( parg == L'b' )              // buffering enabled
                  {
                     tCfg.bufferInput = true ;
                     tmpEcho = termEcho ; // always echo when buffering enabled
                  }
                  else if ( parg == L'u' )         // buffering disabled
                  {
                     tCfg.bufferInput = false ;

                     //* For soft-echo sub-option only, sub-sub *
                     //* option for special keycode translation.*
                     if ( arg4 == L's' )
                     {
                        switch ( arg5 )
                        {
                           case 'c':            // translate cursor keys only
                              tmpEcho = softEchoC ;
                              break ;
                           case 'e':            // translate Edit keys only
                              tmpEcho = softEchoE ;
                              break ;
                           case 'd':            // disable translation
                              tmpEcho = softEchoD ;
                              break ;
                           case 'a':            // translate all
                           default:             // default translation
                              tmpEcho = softEchoA ;
                              break ;
                        } ;
                     }
                  }
                  else                             // invalid argument
                  {
                     tCfg.bufferInput = true ;
                     tmpEcho = termEcho ;
                     status = false ;
                  }
                  tCfg.echoOption = tmpEcho ;      // save the echo type
               }
               gs.shiftChars( (arg5 == DASH) ? -4 : -5 ) ;
               gs.strip() ;

               #if DEBUG_TERMSETUP != 0
               gsDbg.compose( "->%C (%C %C %C) bufferInput:%hhd echoOpt:%s", 
                              &parm, &parg, &arg4, &arg5,  
                              &tCfg.bufferInput, 
                              (tCfg.echoOption == termEcho ? "termEcho" : 
                               tCfg.echoOption == softEchoA ? "softEchoA" :
                               tCfg.echoOption == softEchoC ? "softEchoC" :
                               tCfg.echoOption == softEchoE ? "softEchoE" :
                               tCfg.echoOption == softEchoD ? "softEchoD" : "noEcho") ) ;
               wcout << gsDbg << endl ;
               #endif   // DEBUG_TERMSETUP
               }  // guard local variables
               break ;

            case L'C':                    // Attributes (fg/bg color attributes)
               switch ( parg )
               {
                  case L'k': tCfg.foreground = aesFG_BLACK ;      break ;  // black
                  case L'r': tCfg.foreground = aesFG_RED ;        break ;  // red
                  case L'g': tCfg.foreground = aesFG_GREEN ;      break ;  // green
                  case L'n': tCfg.foreground = aesFG_BROWN ;      break ;  // brown
                  case L'b': tCfg.foreground = aesFG_BLUE ;       break ;  // blue
                  case L'm': tCfg.foreground = aesFG_MAGENTA ;    break ;  // magenta
                  case L'c': tCfg.foreground = aesFG_CYAN ;       break ;  // cyan
                  case L'y': tCfg.foreground = aesFG_GREY ;       break ;  // grey
                  case L'd': tCfg.foreground = aesFG_DFLT ;       break ;  // default
                  default:    // invalid argument
                     tCfg.foreground = aesFG_DFLT ;
                     status = false ;
                     break ;
               }
               switch ( arg4 )
               {
                  case L'k': tCfg.background = aesBG_BLACK ;      break ;  // black
                  case L'r': tCfg.background = aesBG_RED ;        break ;  // red
                  case L'g': tCfg.background = aesBG_GREEN ;      break ;  // green
                  case L'n': tCfg.background = aesBG_BROWN ;      break ;  // brown
                  case L'b': tCfg.background = aesBG_BLUE ;       break ;  // blue
                  case L'm': tCfg.background = aesBG_MAGENTA ;    break ;  // magenta
                  case L'c': tCfg.background = aesBG_CYAN ;       break ;  // cyan
                  case L'y': tCfg.background = aesBG_GREY ;       break ;  // grey
                  case L'd': tCfg.background = aesBG_DFLT ;       break ;  // default
                  default:    // invalid argument or argument not specified
                     tCfg.background = aesBG_DFLT ;
                     // Programmer's Note: (see top of loop)
                     // If 'C' was the last term option specified,then arg4==DASH.
                     // If 'C' was not the last term option specified, then arg4==SPACE.
                     if ( (arg4 != SPACE) && (arg4 != DASH) )
                        status = false ;
                     break ;
               }
               if ( status != false )
               {
                  gs.shiftChars( -4 ) ;
                  gs.strip() ;
               }

               #if DEBUG_TERMSETUP != 0
               gsDbg.compose( "->%C (%C %C) foreground:%s background:%s", 
                              &parm, &parg, &arg4,
                              (tCfg.foreground == aesFG_BLACK   ? "aesFG_BLACK" :
                               tCfg.foreground == aesFG_RED     ? "aesFG_RED" :
                               tCfg.foreground == aesFG_GREEN   ? "aesFG_GREEN" :
                               tCfg.foreground == aesFG_BROWN   ? "aesFG_BROWN" :
                               tCfg.foreground == aesFG_BLUE    ? "aesFG_BLUE" :
                               tCfg.foreground == aesFG_MAGENTA ? "aesFG_MAGENTA" :
                               tCfg.foreground == aesFG_CYAN    ? "aesFG_CYAN" :
                               tCfg.foreground == aesFG_GREY    ? "aesFG_GREY" :
                               "aesFG_DFLT"),
                              (tCfg.background == aesBG_BLACK   ? "aesBG_BLACK" :
                               tCfg.background == aesBG_RED     ? "aesBG_RED" :
                               tCfg.background == aesBG_GREEN   ? "aesBG_GREEN" :
                               tCfg.background == aesBG_BROWN   ? "aesBG_BROWN" :
                               tCfg.background == aesBG_BLUE    ? "aesBG_BLUE" :
                               tCfg.background == aesBG_MAGENTA ? "aesBG_MAGENTA" :
                               tCfg.background == aesBG_CYAN    ? "aesBG_CYAN" :
                               tCfg.background == aesBG_GREY    ? "aesBG_GREY" :
                               "aesFG_DFLT")
                            ) ;
               wcout << gsDbg << endl ;
               #endif   // DEBUG_TERMSETUP
               break ;

            case L'L':                    // Locale
               //* Determine whether the default 'e' (environment) argument *
               //* specified, if no argument specified, or if a potential   *
               //* filename was specified. If a filename, extract it.       *
               if ( ((parg != L'e') && (parg != SPACE) && (parg != NULLCHAR)) ||
                    ((parg == L'e') && (arg4 != SPACE) && (arg4 != NULLCHAR)) )
               {
                  gs.shiftChars( -2 ) ;      // reference beginning of filename
                  gsLocale = gs ;            // copy filename (and any trailing data)
                  //* If additional parameters follow the filename, *
                  //* delete them. Save the filename string to the  *
                  //* target member, then shift the processed token *
                  //* out of the parameter object.                  *
                  if ( (off = gsLocale.find( wcsSPACE )) > ZERO )
                     gsLocale.limitChars( off ) ;
                  gsLocale.copy( tCfg.localeName, LOCALE_LEN ) ;
                  off = gsLocale.gschars() ;
                  gs.shiftChars( -(off) ) ; // shift out the filename + 1
               }
               //* This is only necessary if the user is an idiot *
               //* and specified multiple locale parameters.      *
               //* Clearing the tCfg.locale member indicates      *
               //* that locale s/b taken from the environment.    *
               else if ( parg == L'e' )
               {
                  tCfg.localeName[ZERO] = NULLCHAR ; // clear target member
                  //* Shift out the three characters of the       *
                  //* parameter AND the character that follows it.*
                  gs.shiftChars ( -4 ) ;
               }
               #if DEBUG_TERMSETUP != 0
               gsDbg.compose( "->%C (%C) gsLocale:'%S'\n"
                              "            tCfg:'%s'\n"
                              "              gs:'%S'", 
                              &parm, &parg, gsLocale.gstr(), tCfg.localeName, gs.gstr() ) ;
               wcout << gsDbg << endl ;
               #endif   // DEBUG_TERMSETUP
               break ;

            case L'P':                    // Panic button
               //* Save the specified handler type *
               switch ( parg )
               {
                  case L'e':     tCfg.bcType = cchtExit ;   break ;
                  case L'u':     tCfg.bcType = cchtUser ;   break ;
                  case L'c':     tCfg.bcType = cchtCopy ;   break ;
                  case L'i':
                     tCfg.bcType = cchtIgnore ;
                     tCfg.bcAlert = (arg4 == L'a') ? true : false ;
                     break ;
                  default:
                     tCfg.bcType = cchtTerm ;   // Terminal's signal handler
                     break ;
               } ;
               gs.shiftChars( (arg4 != DASH) ? -4 : -3 ) ;
               gs.strip() ;

               #if DEBUG_TERMSETUP != 0
               if ( tCfg.bcType == cchtTerm )  { parg = L't' ; }
               gsDbg.compose( "->%C (%C) bcType:%s", 
                              &parm, &parg, 
                              (tCfg.bcType==cchtExit ? "cchtExit" :
                               tCfg.bcType==cchtUser ? "cchtUser" :
                               tCfg.bcType==cchtIgnore ? "cchtIgnore" : "cchtTerm") ) ;
               if ( tCfg.bcType == cchtIgnore )
                  gsDbg.append( "(%s)", (tCfg.bcAlert ? "alert" : "silent") ) ;
               wcout << gsDbg << endl ;
               #endif   // DEBUG_TERMSETUP
               break ;

            case L'Q':                    // Capture the Ctrl+4 (kill) signal
               tCfg.quitSignal = bool(parg == L'n' ? false : true) ;

               gs.shiftChars( (parg != DASH) ? -3 : -2 ) ;
               gs.strip() ;

               #if DEBUG_TERMSETUP != 0
               gsDbg.compose( "->%C (%s)", 
                              &parm, (tCfg.quitSignal ? "true" : "false") ) ;
               wcout << gsDbg << endl ;
               #endif   // DEBUG_TERMSETUP
               break ;

            case L'Z':                    // Capture and convert the Ctrl+Z signal
               tCfg.suspendSignal = bool(parg == L'n' ? false : true) ;

               gs.shiftChars( (parg != DASH) ? -3 : -2 ) ;
               gs.strip() ;

               #if DEBUG_TERMSETUP != 0
               gsDbg.compose( "->%C (%s)", 
                              &parm, (tCfg.suspendSignal ? "true" : "false") ) ;
               wcout << gsDbg << endl ;
               #endif   // DEBUG_TERMSETUP
               break ;

            case L'S':                    // Cursor shape (and in future, color)
               //* Validate the cursor shape: block/underline/vertical/default.*
               switch ( parg )
               {
                  case L'b':              // Block
                     if ( arg4 == L's' )  tCfg.cursorStyle = csSblock ;
                     else if ( (arg4 == L'b') || (arg4 == L'-') )
                        tCfg.cursorStyle = csBblock ;
                     else status = false ;
                     break ;
                  case L'u':              // Underline
                     if ( arg4 == L's' )  tCfg.cursorStyle = csSuline ;
                     else if ( (arg4 == L'b') || (arg4 == L'-') ) tCfg.cursorStyle = csBuline ;
                     else status = false ;
                     break ;
                  case L'v':              // Vertical Bar
                     if ( arg4 == L's' )  tCfg.cursorStyle = csSvbar ;
                     else if ( (arg4 == L'b') || (arg4 == L'-') ) tCfg.cursorStyle = csBvbar ;
                     else status = false ;
                     break ;
                  case L'd':              // Default (terminal default)
                     tCfg.cursorStyle = csDflt ;
                     break ;
                  default:                // invalid argument
                     status = false ;
                     break ;
               } ;

               gs.shiftChars( (arg4 == DASH) ? -3 : -4 ) ;
               gs.strip() ;

               #if DEBUG_TERMSETUP != 0
               gsDbg.compose( "->%C (%C %C) curStyle:%s", 
                              &parm, &parg, &arg4, 
                              (tCfg.cursorStyle == csBblock ? "csBblock" :
                               tCfg.cursorStyle == csDflt   ? "csDflt" :
                               tCfg.cursorStyle == csSblock ? "csSblock" :
                               tCfg.cursorStyle == csBuline ? "csBuline" :
                               tCfg.cursorStyle == csSuline ? "csSuline" :
                               tCfg.cursorStyle == csBvbar  ? "csBvbar" : "csSvbar") ) ;
               wcout << gsDbg << endl ;
               #endif   // DEBUG_TERMSETUP
               break ;

            default:    // invalid argument or end-of-text
               if ( gs.gschars() > 1 )
               {
                  #if DEBUG_TERMSETUP != 0
                  gsDbg.compose( "BAD TOKEN: '%C'", &parm ) ;
                  wcout << gsDbg << endl ;
                  #endif   // DEBUG_TERMSETUP
                  status = false ;
               }
               break ;
         } ;
      }
      while ( (parm != NULLCHAR) && (status != false) ) ;

      #if DEBUG_TERMSETUP != 0 && DEBUG_TS_PAUSE != 0
      wcin.get();    // generally not needed for debugging
      #endif   // DEBUG_TERMSETUP && DEBUG_TS_PAUSE
   }
   return status ;

   #undef DEBUG_TERMSETUP     // for debugging only
   #undef DEBUG_TS_PAUSE
}  //* End ansicmdSetup() *

//*************************
//*     ansicmdSetup      *
//*************************
//********************************************************************************
//* Non-member Method:                                                           *
//* ------------------                                                           *
//* Parse command-line arguments for setup of AnsiCmd test options.              *
//* --test option: specifies an AnsiCmd-class functionality test.                *
//*                                                                              *
//*                                                                              *
//* Input  : gsCmd   : (by reference) command-line arguments                     *
//*          args    : (by reference) receives testing arguments                 *
//*                                                                              *
//* Returns: 'true'  if valid argument(s) specified and decoded                  *
//*          'false' if invalid argument(s) or parsing error                     *
//********************************************************************************

bool ansicmdSetup ( const gString& gsCmd, dbgArgs& args )
{
   #if DEBUG_ANSICMD != 0  // AnsiCmd Test Code Is Enabled
   short off ;                            // offset index
   bool status = false ;                  // return value

   //* Initialize caller's data (
   args.major = args.minor = args.supp = args.attr = L'?' ;

   //* One, two, three or four characters define the test to be performed.*
   if ( ((off = gsCmd.after( L'=' )) > ZERO) && (gsCmd.gstr()[off] != NULLCHAR) )
   {
      args.major = toupper ( gsCmd.gstr()[off++] ) ;
      if ( gsCmd.gstr()[off] != NULLCHAR )
         args.minor = tolower ( gsCmd.gstr()[off++] ) ;
      if ( gsCmd.gstr()[off] != NULLCHAR )
         args.supp = tolower ( gsCmd.gstr()[off++] ) ;
      if ( gsCmd.gstr()[off] != NULLCHAR )
         args.attr = tolower ( gsCmd.gstr()[off++] ) ;
   }
   return status ;

   #else                   // AnsiCmd Test Code Is Disabled
   args.major = args.minor = args.supp = args.attr = L'?' ;
   return true ;
   #endif   // DEBUG_ANSICMD

}  //* End ansicmdSetup() *


   //*******************************************************************
   //* Text for command-line help used by the application is defined   *
   //* in this module and declared extern elsewhere.                   *
   //* Text for application's command-line help.                       *
   //*                                                                 *
   //* HelpAnsi  : Describes available AnsiCmd tests.                  *
   //* HelpTerm  : Terminal setup options.                             *
   //*             Options include foreground/background colors,       *
   //*             buffering options, locale, stream width, etc.       *
   //*                                                                 *
   //*******************************************************************
   const char* HelpTerm = 
    // Eighty Columns Max:123456789-123456789-123456789-123456789-123456789-123456789-
   "\n==============================================================================="
   "\n --term=[W:[w | n]] C:fgnd[bgnd]"
   "\n        [A:[y | n]] [B:[b | u[x|t|s]]] [L:[e | locale_name]]"
   "\n        [P:[i[a]|e|u|t]] [Q:[n|y]] [Z:[n|y]] [S:[d | b[b|u|v] | s[b|u|v]"
   "\n   Specify terminal setup parameters. All parameters are optional, and may"
   "\n   be specified in any order. Parameters must be separated by commas."
   "\n   Available Setup Parameters:"
   "\n   ---------------------------"
   "\n   Stream Width: W:[w | n]  where:"
   "\n     - w : use the wide (wcin/wcout) input/output streams. This is the"
   "\n           default, and is strongly recommended."
   "\n     - n : use the legacy narrow (cin/cout) input/output streams."
   "\n           The \"narrow\" I/O streams are not recommended for modern"
   "\n           applications, but may be used for testing the interface."
   "\n   Color Attributes: C:[k|r|g|n|b|m|c|y|d[k|r|g|n|b|m|c|y|d]]"
   "\n     - foreground (required): "
   "\n     - background (optional): (default: d)"
   "\n     where k==blacK, r==Red, g==Green, n==brown, b==Blue, m==Magenta"
   "\n           c==Cyan, y==greY, d==Default"
   "\n   Ansi Compliant: A:[y | n]"
   "\n     - y : Yes, Host terminal is compliant with all common commands of"
   "\n           the ANSI standard (ANSI X3.64 (ISO/IEC6429))."
   "\n     - n : No, Host terminal ignores or incorrectly processes one or"
   "\n           more ANSI commands. The ANSI \"reset\" command will be issued after"
   "\n           disabling any previously-set text attribute to ensure that the"
   "\n           attribute was actually disabled. Note that this will reset all"
   "\n           text attributes, not just the specified attribute."
   "\n   Input Buffering: B:[b | u[x|t|s[s|a|c|b|d]]]"
   "\n     - b  : Buffered input from stdin is enabled (default, data echo enabled)"
   "\n     - u[x|t|s] : Unbuffered input from stdin. Data-echo sub-options:"
   "\n        x : echo of data from stdin is disabled. This is the default for"
   "\n            unbuffered input and is assumed if no sub-option is specified."
   "\n        t : terminal echoes input data to terminal window."
   "\n        s : \"soft\" echo. Terminal echo is disabled, but the acRead()"
   "\n            method echoes _printing_ characters received through stdin."
   "\n            Soft Echo sub-options: These sub-options specify how \"special\""
   "\n            keys (cursor keys and Bksp/Del/Insert keys) are translated."
   "\n            s : Standard (default): translate left/right arrows, Home, End,"
   "\n                Tab/Shift+Tab, Backspace, Delete and Insert keycodes only."
   "\n            a : All: translate all special keycodes."
   "\n            c : Cursor: translate cursor-control keycodes only."
   "\n            e : Edit: translate Bksp/Del/Ins keycodes plus Tab/Shift+Tab only."
   "\n            d : Disable: disable (discard) all special keycodes."
   "\n   Locale Name: L:[e | locale_name]"
   "\n     - e  : 'e' specifies that the locale should be taken from the"
   "\n            environment. This is the default and is almost always the"
   "\n            right choice. At the command line, type \"echo $LANG\""
   "\n            to see your default locale,"
   "\n     - locale_name : specify a valid filename for the locale to be used."
   "\n            Type: \"locale --all | grep -i 'utf8'\" for a complete list of"
   "\n            compatible locales."
   "\n   Panic Button: P:[i[a]|e|u|t] Capture Control+C (break key)"
   "\n     - i  : Ignore break-key signal."
   "\n            - a : optionally enable auditory Alert when signal discarded."
   "\n     - e  : Exit application on receipt of break-key signal."
   "\n     - u  : User is asked whether to exit the application."
   "\n     - t  : Terminal handles break-key signal, killing application (default)."
   "\n   Quit: Q:[n|y] Capture Control+4 (kill key)"
   "\n     - y : Yes (default), shut down application in an orderly manner."
   "\n     - n : No, quit signal immediately terminates application with core dump."
   "\n   Suspend: Z:[n|y] Capture Control+Z (suspend key)"
   "\n     - y : Yes (default), capture suspend signal and convert to keycode"
   "\n     - n : No, suspend signal puts active process into hibernation mode."
    // Eighty Columns Max:123456789-123456789-123456789-123456789-123456789-123456789-
   "\n   Cursor Style: S:[d | b[b|s] | u[b|s] | v[b|s] | c[?]] Set the cursor style."
   "\n     - d  : Default (set in terminal preferences, usually blinking block)"
   "\n     - b  : Block shape."
   "\n     - u  : Underline shape."
   "\n     - v  : Vertical bar shape."
   "\n            For the -b, -u, -v options, two states are available:"
   "\n            'b'=blinking (default) or 's'=steady (unblinking)"
//   "\n     - c  : Color, NOT IMPLEMENTED"
   "\n "
   "\n   Examples: --term=W:n     narrow I/O streams"
   "\n             --term=W:n,Cb  narrow I/O streams and blue text"
   "\n             --term=B:us    unbuffered input(soft echo)"
   "\n             --term=P:e     capture the break signal and exit application"
   "\n             --term=S:ub    cursor: blinking underline"
   "\n             --term=L:e     locale from environment"
   "\n             --term=L:fr_CA.utf8   locale for French(Canada)"
   "\n             --term=W:w,Ccb,B:ux,L:zh_CN.utf8"
   "\n                            wide I/O streams, cyan-on-blue text, unbuffered"
   "\n                            input(echo off), Chinese(Zhongwen, China) locale"
   "\n " ;
    // Eighty Columns Max:123456789-123456789-123456789-123456789-123456789-123456789-

   #if DEBUG_ANSICMD != 0
   const char* HelpAnsi = 
    // Eighty Columns Max:123456789-123456789-123456789-123456789-123456789-123456789-
   "\n==============================================================================="
   "\n --test=[C[f | b | t | g | a | r[r|g|b|y|w] | d[a|b]] | P | E | | A | F | I |"
   "\n         T[i|s|f] | W[w|f|d|b|l] | S[argA[argB]]]"
   "\n   (for debugging only)"
   "\n   Test the functionality of the AnsiCmd class which formats and writes"
   "\n   ANSI escape sequences to the terminal's stdout (wcout || cout)."
   "\n     C == test color attributes: f==four-bit color, b==background options,"
   "\n          t==table lookup (8-bit color), g==greyscale, a==aixterm extensions,"
   "\n          r==red/green/blue (24-bit color, with sub-option), s==Fg/Bg swap,"
   "\n          d==decoding test (with sub-option: api(dflt) or basic)"
   "\n     P == test cursor positioning commands."
   "\n     E == test text erasure commands."
   "\n     A == test ASCII control codes, Function keys and ALT-key combinations."
   "\n     F == test support for switching to \"alternate fonts\"."
   "\n     I == test support for the \"Ideogram\" group of commands."
   "\n     T == test a terminal setup option:"
   "\n          i==input stream capture testing"
   "\n          e==echo options tests, focus on the \"soft-echo\""
   "\n          t==terminal setup-options validation"
   "\n          b==blocking/non-blocking read of 'stdin'"
   "\n          r==raw text from input stream reported (incl.escape sequences)"
   "\n          o==output of multi-byte UTF-8 text (mixed English and Chinese)"
   "\n          m==modifiers: Bold, Italic, Underline, Reverse, Overline, etc."
   "\n          f==formatted-text (paragraph display) verification"
   "\n     W == test windows, forms, line-drawing, etc."
   "\n          w==ACWin (window) class"
   "\n          f==skForm and skField classes for user interaction"
   "\n          t==text auto-formatting to fit within the window"
   "\n          c==constructor, ACWin default constructor"
   "\n          s==save-and-restore window data (BlockFifo class)"
   "\n          m==menu controls, scrollbox controls"
   "\n          b==AC_Box class for drawing rectangular objects"
   "\n          l==line-drawing methods"
   "\n     D == Dialog: Sub-windows within the parent window."
   "\n          q==quick dialog group"
   "\n          d==dialog, full-initialization group"
   "\n          s==sidebar tests"
   "\n          0==display dialog test menu"
   "\n     S == Sandbox, used for creating your own tests. Optional arguments:"
   "\n          argA==user-defined argument A"
   "\n          argB==user-defined argument B"
   "\n     An optional character following the P/E/A/F/I/T options"
   "\n     indicates the text color attribute used for the test."
   "\n     Specify color as: [k|r|g|n|b|m|c|y] blacK, Red, Green, browN," 
   "\n     Blue, Magenta, Cyan, greY.    Example --test=Ac  --test=Tig"
   "\n     If not specified, the terminal default color is used."
   "\n "
   "\n   To perform tests using the \"narrow\" I/O streams, specify"
   "\n   stream width using the --term='W:n' command."
   "\n " ;
    // Eighty Columns Max:123456789-123456789-123456789-123456789-123456789-123456789-
   #endif   // DEBUG_ANSICMD

//*************************
//*                       *
//*************************
//********************************************************************************
//*                                                                              *
//*                                                                              *
//*                                                                              *
//* Input  :                                                                     *
//*                                                                              *
//* Returns:                                                                     *
//********************************************************************************

