/**
 * @file trace_message_print.cc
 *
 * TraceMessagePrint - Class to convert CAPI trace message to a textual
 * representation.
 *
 * Copyright: 2002-2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: trace_message_print.cc,v 1.1.2.1 2006/02/28 21:35:43 thomas Exp $
 * Project  CAPI for BSD
 * Target   capitrace - Tracing CAPI calls and messages
 * @date    03.12.2002
 * @author  "Thomas Wintergerst" <twinterg@gmx.de>
 * <p>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * </p>
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

// System includes
#include <iomanip>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <assert.h>

// Import includes

#define __TRACE_MESSAGE_PRINT__

// Local includes
#ifndef __CONFIG_H
#  include "config.h"
#endif
#ifndef __TRACE_MESSAGE_PRINT_H
#  include "trace_message_print.h"
#endif





// === Private definitions ===============================================





#define ARRAY_COUNT( a )        (sizeof (a) / sizeof (a [0]))



/**
 * Class to represent a pointer stored in two dwords.
 *
 * This class is only used to print out a pointer either as a 32bit or a 64bit
 * pointer. This is done by a related stream output operator.
 */
class CTwoDwordPointer
{
   public:
      CTwoDwordPointer (void)
         { m_dwLow = m_dwHigh = 0; }

      CTwoDwordPointer
         (u_int32_t dwLow,
          u_int32_t dwHigh):
         m_dwLow (dwLow),
         m_dwHigh (dwHigh)
         { }

      u_int32_t m_dwLow;
      u_int32_t m_dwHigh;
      
}; // CTwoDwordPointer



/**
 * Stream output function for a struct timeval.
 */
static std::ostream & operator<<
   (std::ostream         &os,
    const struct timeval &tv);

/**
 * Stream output function for a pointer stored in two dwords.
 */
static std::ostream & operator<<
   (std::ostream           &os,
    const CTwoDwordPointer &tdwp);





// === Definitions for public declarations ===============================





// === Prototypes of private functions ===================================





// === Implementation of class members ===================================

/// The indentation of trace messages in the output.
const size_t CTraceMessagePrint::m_nNormalIndent = 3;

/// The global dispatch table to print a message of a specific type.
CTraceMessagePrint::MsgTypeDispatchTableEntry_t
   CTraceMessagePrint::m_aMsgTypeDispTbl [] =
   {
      { C4BTRC_MSGTYPE_APPL_REGISTER,             &CTraceMessagePrint::PrintApplRegister          },
      { C4BTRC_MSGTYPE_APPL_RELEASE,              &CTraceMessagePrint::PrintApplRelease           },
      { C4BTRC_MSGTYPE_APPL_PUT_MESSAGE,          &CTraceMessagePrint::PrintApplPutMessage        },
      { C4BTRC_MSGTYPE_APPL_GET_MESSAGE,          &CTraceMessagePrint::PrintApplGetMessage        },
      { C4BTRC_MSGTYPE_APPL_SET_SIGNAL,           &CTraceMessagePrint::PrintApplSetSignal         },
      { C4BTRC_MSGTYPE_APPL_GET_MANUFACTURER,     &CTraceMessagePrint::PrintApplGetManufacturer   },
      { C4BTRC_MSGTYPE_APPL_GET_VERSION,          &CTraceMessagePrint::PrintApplGetVersion        },
      { C4BTRC_MSGTYPE_APPL_GET_SERIAL_NUMBER,    &CTraceMessagePrint::PrintApplGetSerialNumber   },
      { C4BTRC_MSGTYPE_APPL_GET_PROFILE,          &CTraceMessagePrint::PrintApplGetProfile        },
      { C4BTRC_MSGTYPE_APPL_GET_CTLR_DRIVER_INFO, &CTraceMessagePrint::PrintApplGetCtlrDriverInfo },
      { C4BTRC_MSGTYPE_APPL_RESET_CTLR,           &CTraceMessagePrint::PrintApplResetCtlr         },
      { C4BTRC_MSGTYPE_APPL_MSG,                  &CTraceMessagePrint::PrintApplMsg               },
      { C4BTRC_MSGTYPE_DRVR_REGISTER,             &CTraceMessagePrint::PrintDrvrRegister          },
      { C4BTRC_MSGTYPE_DRVR_RELEASE,              &CTraceMessagePrint::PrintDrvrRelease           },
      { C4BTRC_MSGTYPE_DRVR_PUT_MESSAGE,          &CTraceMessagePrint::PrintDrvrPutMessage        },
      { C4BTRC_MSGTYPE_DRVR_GET_MESSAGE,          &CTraceMessagePrint::PrintDrvrGetMessage        },
      { C4BTRC_MSGTYPE_DRVR_RESET_CTLR,           &CTraceMessagePrint::PrintDrvrResetCtlr         },
      { C4BTRC_MSGTYPE_DRVR_MSG,                  &CTraceMessagePrint::PrintDrvrMsg               }
   };

/// The list of fields for a Connect-Request.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aConnectReqList [] =
   {
      { "CIP Value",                &CTraceMessagePrint::PrintCipValue           },
      { "Called Party Number",      &CTraceMessagePrint::PrintCalledPartyNumber  },
      { "Calling Party Number",     &CTraceMessagePrint::PrintCallingPartyNumber },
      { "Called Party Subaddress",  &CTraceMessagePrint::PrintCapiStruct         },
      { "Calling Party Subaddress", &CTraceMessagePrint::PrintCapiStruct         },
      { "B-Protocol",               &CTraceMessagePrint::PrintBProtocol          },
      { "Bearer Capability",        &CTraceMessagePrint::PrintCapiStruct         },
      { "Low Layer Compatibility",  &CTraceMessagePrint::PrintCapiStruct         },
      { "High Layer Compatibility", &CTraceMessagePrint::PrintCapiStruct         },
      { "Additional Info",          &CTraceMessagePrint::PrintAdditionalInfo     }
   };

/// The list of fields for a Connect-Indication.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aConnectIndList [] =
   {
      { "CIP Value",                   &CTraceMessagePrint::PrintCipValue           },
      { "Called Party Number",         &CTraceMessagePrint::PrintCalledPartyNumber  },
      { "Calling Party Number",        &CTraceMessagePrint::PrintCallingPartyNumber },
      { "Called Party Subaddress",     &CTraceMessagePrint::PrintCapiStruct         },
      { "Calling Party Subaddress",    &CTraceMessagePrint::PrintCapiStruct         },
      { "Bearer Capability",           &CTraceMessagePrint::PrintCapiStruct         },
      { "Low Layer Compatibility",     &CTraceMessagePrint::PrintCapiStruct         },
      { "High Layer Compatibility",    &CTraceMessagePrint::PrintCapiStruct         },
      { "Additional Info",             &CTraceMessagePrint::PrintAdditionalInfo     },
      { "Second Calling Party Number", &CTraceMessagePrint::PrintCallingPartyNumber }
   };

/// The list of fields for a Connect-Response.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aConnectRespList [] =
   {
      { "Reject",                     &CTraceMessagePrint::PrintRejectValue        },
      { "B-Protocol",                 &CTraceMessagePrint::PrintBProtocol          },
      { "Connected Party Number",     &CTraceMessagePrint::PrintCallingPartyNumber },
      { "Connected Party Subaddress", &CTraceMessagePrint::PrintCapiStruct         },
      { "Low Layer Compatibility",    &CTraceMessagePrint::PrintCapiStruct         },
      { "Additional Info",            &CTraceMessagePrint::PrintAdditionalInfo     }
   };

/// The list of fields for a Connect-Active-Indication.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aConnActIndList [] =
   {
      { "Connected Party Number",     &CTraceMessagePrint::PrintCallingPartyNumber },
      { "Connected Party Subaddress", &CTraceMessagePrint::PrintCapiStruct         },
      { "Low Layer Compatibility",    &CTraceMessagePrint::PrintCapiStruct         }
   };

/// The list of fields for a Disconnect-Indication.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aDisconnectIndList [] =
   {
      { "Reason", &CTraceMessagePrint::PrintHexWord }
   };

/// The list of fields for a Listen-Request.
/// XXX: The bit fields should be decoded.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aListenReqList [] =
   {
      { "Info Mask",                &CTraceMessagePrint::PrintHexDWord           },
      { "CIP Mask",                 &CTraceMessagePrint::PrintHexDWord           },
      { "CIP Mask 2",               &CTraceMessagePrint::PrintHexDWord           },
      { "Calling Party Number",     &CTraceMessagePrint::PrintCallingPartyNumber },
      { "Calling Party Subaddress", &CTraceMessagePrint::PrintCapiStruct         }
   };

/// The list of fields for a Info-Request.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aInfoReqList [] =
   {
      { "Called Party Number", &CTraceMessagePrint::PrintCallingPartyNumber },
      { "Additional Info",     &CTraceMessagePrint::PrintAdditionalInfo     }
   };

/// The list of fields for a Info-Indication.
/// XXX: The Info Element should be decoded in the context of the Info Number.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aInfoIndList [] =
   {
      { "Info Number",  &CTraceMessagePrint::PrintInfoNumber   },
      { "Info Element", &CTraceMessagePrint::PrintCapiStruct   }
   };

/// The list of fields for a Select-B-Protocol-Request.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aSelBProtReqList [] =
   {
      { "B-Protocol", &CTraceMessagePrint::PrintBProtocol }
   };

/// The list of fields for a Facility-Request.
/// XXX: The Facility-Request-Parameter should be decoded according to the
///      selector.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aFacilityReqList [] =
   {
      { "Selector",                   &CTraceMessagePrint::PrintFacilitySelector },
      { "Facility Request Parameter", &CTraceMessagePrint::PrintCapiStruct       }
   };

/// The list of fields for a Facility-Confirm.
/// XXX: The Facility-Confirm-Parameter should be decoded according to the
///      selector.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aFacilityConfList [] =
   {
      { "Info",                       &CTraceMessagePrint::PrintResultValue      },
      { "Selector",                   &CTraceMessagePrint::PrintFacilitySelector },
      { "Facility Confirm Parameter", &CTraceMessagePrint::PrintCapiStruct       }
   };

/// The list of fields for a Facility-Indication.
/// XXX: The Facility-Indication-Parameter should be decoded according to the
///      selector.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aFacilityIndList [] =
   {
      { "Selector",                      &CTraceMessagePrint::PrintFacilitySelector },
      { "Facility Indication Parameter", &CTraceMessagePrint::PrintCapiStruct       }
   };

/// The list of fields for a Facility-Response.
/// XXX: The Facility-Response-Parameter should be decoded according to the
///      selector.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aFacilityRespList [] =
   {
      { "Selector",                    &CTraceMessagePrint::PrintFacilitySelector },
      { "Facility Response Parameter", &CTraceMessagePrint::PrintCapiStruct       }
   };

/// The list of fields for a Connect-B3-Response.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aConnectB3RespList [] =
   {
      { "Reject", &CTraceMessagePrint::PrintRejectValue  },
      { "NCPI",   &CTraceMessagePrint::PrintCapiStruct   }
   };

/// The list of fields for a Disconnect-B3-Indication.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aDisconnectB3IndList [] =
   {
      { "Reason", &CTraceMessagePrint::PrintResultValue  },
      { "NCPI",   &CTraceMessagePrint::PrintCapiStruct   }
   };

/// The list of fields for a Data-B3-Request.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aDataB3ReqList [] =
   {
      { "Data",   &CTraceMessagePrint::PrintHexDWord    },
      { "Length", &CTraceMessagePrint::PrintDecWord     },
      { "Handle", &CTraceMessagePrint::PrintDecWord     },
      { "Flags",  &CTraceMessagePrint::PrintDataB3Flags },
      { "Data64", &CTraceMessagePrint::PrintHexQWord    }
   };

/// The list of fields for a Data-B3-Confirm.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aDataB3ConfList [] =
   {
      { "Handle", &CTraceMessagePrint::PrintDecWord     },
      { "Info",   &CTraceMessagePrint::PrintResultValue }
   };

/// The list of fields for a Data-B3-Indication.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aDataB3IndList [] =
   {
      { "Data",   &CTraceMessagePrint::PrintHexDWord    },
      { "Length", &CTraceMessagePrint::PrintDecWord     },
      { "Handle", &CTraceMessagePrint::PrintDecWord     },
      { "Flags",  &CTraceMessagePrint::PrintDataB3Flags },
      { "Data64", &CTraceMessagePrint::PrintHexQWord    }
   };

/// The list of fields for a Data-B3-Response.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aDataB3RespList [] =
   {
      { "Handle", &CTraceMessagePrint::PrintDecWord }
   };

/// The list of fields for a message with only Additional Info.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aAddInfoList [] =
   {
      { "Additional Info", &CTraceMessagePrint::PrintAdditionalInfo }
   };

/// The list of fields for a message with only an Info field.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aInfoList [] =
   {
      { "Info", &CTraceMessagePrint::PrintResultValue }
   };

/// The list of fields for a message with only a NCPI.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aNcpiList [] =
   {
      { "NCPI", &CTraceMessagePrint::PrintCapiStruct   }
   };

/// The list of fields for Manufacturer messages.
CTraceMessagePrint::CapiMsgFieldDescriptionEntry_t
   CTraceMessagePrint::m_aManuMsgList [] =
   {
      { "Manufacturer id",            &CTraceMessagePrint::PrintHexDWord           },
      { "Manufacturer specific data", &CTraceMessagePrint::PrintRestAsByteSequence }
   };

/// The global table to assign a field description list to every CAPI message.
CTraceMessagePrint::CapiMsgDispatchTableEntry_t
   CTraceMessagePrint::m_aCapiMsgDispTbl [] =
   {
      { CAPI_REQUEST (C_ALERT),                 CTraceMessagePrint::m_aAddInfoList,         ARRAY_COUNT (CTraceMessagePrint::m_aAddInfoList)         },
      { CAPI_CONFIRM (C_ALERT),                 CTraceMessagePrint::m_aInfoList,            ARRAY_COUNT (CTraceMessagePrint::m_aInfoList)            },
      { CAPI_REQUEST (C_CONNECT),               CTraceMessagePrint::m_aConnectReqList,      ARRAY_COUNT (CTraceMessagePrint::m_aConnectReqList)      },
      { CAPI_CONFIRM (C_CONNECT),               CTraceMessagePrint::m_aInfoList,            ARRAY_COUNT (CTraceMessagePrint::m_aInfoList)            },
      { CAPI_INDICAT (C_CONNECT),               CTraceMessagePrint::m_aConnectIndList,      ARRAY_COUNT (CTraceMessagePrint::m_aConnectIndList)      },
      { CAPI_RESPONSE (C_CONNECT),              CTraceMessagePrint::m_aConnectRespList,     ARRAY_COUNT (CTraceMessagePrint::m_aConnectRespList)     },
      { CAPI_INDICAT (C_CONNECT_ACTIVE),        CTraceMessagePrint::m_aConnActIndList,      ARRAY_COUNT (CTraceMessagePrint::m_aConnActIndList)      },
      { CAPI_REQUEST (C_DISCONNECT),            CTraceMessagePrint::m_aAddInfoList,         ARRAY_COUNT (CTraceMessagePrint::m_aAddInfoList)         },
      { CAPI_CONFIRM (C_DISCONNECT),            CTraceMessagePrint::m_aInfoList,            ARRAY_COUNT (CTraceMessagePrint::m_aInfoList)            },
      { CAPI_INDICAT (C_DISCONNECT),            CTraceMessagePrint::m_aDisconnectIndList,   ARRAY_COUNT (CTraceMessagePrint::m_aDisconnectIndList)   },
      { CAPI_REQUEST (C_LISTEN),                CTraceMessagePrint::m_aListenReqList,       ARRAY_COUNT (CTraceMessagePrint::m_aListenReqList)       },
      { CAPI_CONFIRM (C_LISTEN),                CTraceMessagePrint::m_aInfoList,            ARRAY_COUNT (CTraceMessagePrint::m_aInfoList)            },
      { CAPI_REQUEST (C_INFO_20),               CTraceMessagePrint::m_aInfoReqList,         ARRAY_COUNT (CTraceMessagePrint::m_aInfoReqList)         },
      { CAPI_CONFIRM (C_INFO_20),               CTraceMessagePrint::m_aInfoList,            ARRAY_COUNT (CTraceMessagePrint::m_aInfoList)            },
      { CAPI_INDICAT (C_INFO_20),               CTraceMessagePrint::m_aInfoIndList,         ARRAY_COUNT (CTraceMessagePrint::m_aInfoIndList)         },
      { CAPI_REQUEST (C_SELECT_B_PROTOCOL),     CTraceMessagePrint::m_aSelBProtReqList,     ARRAY_COUNT (CTraceMessagePrint::m_aSelBProtReqList)     },
      { CAPI_CONFIRM (C_SELECT_B_PROTOCOL),     CTraceMessagePrint::m_aInfoList,            ARRAY_COUNT (CTraceMessagePrint::m_aInfoList)            },
      { CAPI_REQUEST (C_FACILITY),              CTraceMessagePrint::m_aFacilityReqList,     ARRAY_COUNT (CTraceMessagePrint::m_aFacilityReqList)     },
      { CAPI_CONFIRM (C_FACILITY),              CTraceMessagePrint::m_aFacilityConfList,    ARRAY_COUNT (CTraceMessagePrint::m_aFacilityConfList)    },
      { CAPI_INDICAT (C_FACILITY),              CTraceMessagePrint::m_aFacilityIndList,     ARRAY_COUNT (CTraceMessagePrint::m_aFacilityIndList)     },
      { CAPI_RESPONSE (C_FACILITY),             CTraceMessagePrint::m_aFacilityRespList,    ARRAY_COUNT (CTraceMessagePrint::m_aFacilityRespList)    },
      { CAPI_REQUEST (C_CONNECT_B3),            CTraceMessagePrint::m_aNcpiList,            ARRAY_COUNT (CTraceMessagePrint::m_aNcpiList)            },
      { CAPI_CONFIRM (C_CONNECT_B3),            CTraceMessagePrint::m_aInfoList,            ARRAY_COUNT (CTraceMessagePrint::m_aInfoList)            },
      { CAPI_INDICAT (C_CONNECT_B3),            CTraceMessagePrint::m_aNcpiList,            ARRAY_COUNT (CTraceMessagePrint::m_aNcpiList)            },
      { CAPI_RESPONSE (C_CONNECT_B3),           CTraceMessagePrint::m_aConnectB3RespList,   ARRAY_COUNT (CTraceMessagePrint::m_aConnectB3RespList)   },
      { CAPI_INDICAT (C_CONNECT_B3_ACTIVE),     CTraceMessagePrint::m_aNcpiList,            ARRAY_COUNT (CTraceMessagePrint::m_aNcpiList)            },
      { CAPI_REQUEST (C_DISCONNECT_B3),         CTraceMessagePrint::m_aNcpiList,            ARRAY_COUNT (CTraceMessagePrint::m_aNcpiList)            },
      { CAPI_CONFIRM (C_DISCONNECT_B3),         CTraceMessagePrint::m_aInfoList,            ARRAY_COUNT (CTraceMessagePrint::m_aInfoList)            },
      { CAPI_INDICAT (C_DISCONNECT_B3),         CTraceMessagePrint::m_aDisconnectB3IndList, ARRAY_COUNT (CTraceMessagePrint::m_aDisconnectB3IndList) },
      { CAPI_REQUEST (C_DATA_B3),               CTraceMessagePrint::m_aDataB3ReqList,       ARRAY_COUNT (CTraceMessagePrint::m_aDataB3ReqList)       },
      { CAPI_CONFIRM (C_DATA_B3),               CTraceMessagePrint::m_aDataB3ConfList,      ARRAY_COUNT (CTraceMessagePrint::m_aDataB3ConfList)      },
      { CAPI_INDICAT (C_DATA_B3),               CTraceMessagePrint::m_aDataB3IndList,       ARRAY_COUNT (CTraceMessagePrint::m_aDataB3IndList)       },
      { CAPI_RESPONSE (C_DATA_B3),              CTraceMessagePrint::m_aDataB3RespList,      ARRAY_COUNT (CTraceMessagePrint::m_aDataB3RespList)      },
      { CAPI_REQUEST (C_RESET_B3_20),           CTraceMessagePrint::m_aNcpiList,            ARRAY_COUNT (CTraceMessagePrint::m_aNcpiList)            },
      { CAPI_CONFIRM (C_RESET_B3_20),           CTraceMessagePrint::m_aInfoList,            ARRAY_COUNT (CTraceMessagePrint::m_aInfoList)            },
      { CAPI_INDICAT (C_RESET_B3_20),           CTraceMessagePrint::m_aNcpiList,            ARRAY_COUNT (CTraceMessagePrint::m_aNcpiList)            },
      { CAPI_INDICAT (C_CONNECT_B3_T90_ACTIVE), CTraceMessagePrint::m_aNcpiList,            ARRAY_COUNT (CTraceMessagePrint::m_aNcpiList)            },
      { CAPI_REQUEST (C_MANUFACTURER),          CTraceMessagePrint::m_aManuMsgList,         ARRAY_COUNT (CTraceMessagePrint::m_aManuMsgList)         },
      { CAPI_CONFIRM (C_MANUFACTURER),          CTraceMessagePrint::m_aManuMsgList,         ARRAY_COUNT (CTraceMessagePrint::m_aManuMsgList)         },
      { CAPI_INDICAT (C_MANUFACTURER),          CTraceMessagePrint::m_aManuMsgList,         ARRAY_COUNT (CTraceMessagePrint::m_aManuMsgList)         },
      { CAPI_RESPONSE (C_MANUFACTURER),         CTraceMessagePrint::m_aManuMsgList,         ARRAY_COUNT (CTraceMessagePrint::m_aManuMsgList)         }
   };





/**
 * Constructor.
 */

CTraceMessagePrint::CTraceMessagePrint
   (CFormOutStream &ros):
   m_ros (ros)
{
} // CTraceMessagePrint::CTraceMessagePrint





/**
 * Destructor.
 */

CTraceMessagePrint::~CTraceMessagePrint (void)
{
   // nothing to do
   
} // CTraceMessagePrint::~CTraceMessagePrint





/**
 * Print a CAPI trace message to the output stream member.
 *
 * Assumption: The buffer specified is large enough to hold at least the
 *             trace message header.
 *
 * @param pTraceMsg       I: The address of the message to interpret.
 * @param nLenTraceMsg    I: The length of the message in bytes for error
 *                           checking.
 *
 * @return Nothing.
 */

void CTraceMessagePrint::Print
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   unsigned uMsgType;
   int      i;
   
   assert (pTraceMsg);
   assert (nLenTraceMsg >= sizeof (pTraceMsg->head));
   
   // find the message type in the top level dispatch table
   uMsgType = (unsigned) C_GET_WORD (pTraceMsg->head.wMsgType);
   for (i = 0; (size_t) i < ARRAY_COUNT (m_aMsgTypeDispTbl); i++)
   {
      if (m_aMsgTypeDispTbl [i].uMsgType == uMsgType)
      {
         (this->*(m_aMsgTypeDispTbl [i].pfnPrintMsg)) (pTraceMsg, nLenTraceMsg);
         return;
      }
   }
   
   // if we reach this point, the message type is unknown
   PrintUnknownMessage (pTraceMsg, nLenTraceMsg);
    
} // CTraceMessagePrint::Print





/**
 * Print an application register message.
 */

void CTraceMessagePrint::PrintApplRegister
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard guard (m_ros);
   struct timeval     tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Application function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "kcapi_register" << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_register))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_register)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   m_ros << "   (uMaxLogicalConnections = "
         << C_GET_DWORD (pTraceMsg->info.appl_register.dwMaxLogicalConnections)
         << newl;
   m_ros << "    uMaxBDataBlocks        = "
         << C_GET_DWORD (pTraceMsg->info.appl_register.dwMaxBDataBlocks)
         << newl;
   m_ros << "    uMaxBDataLen           = "
         << C_GET_DWORD (pTraceMsg->info.appl_register.dwMaxBDataLen)
         << newl;
   m_ros << "    puApplID               = "
         << CTwoDwordPointer
               (C_GET_DWORD (pTraceMsg->info.appl_register.dwApplIdPointerLow),
                C_GET_DWORD (pTraceMsg->info.appl_register.dwApplIdPointerHigh))
         << ")" << newl;
   m_ros << "Result: 0x" << std::hex << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.appl_register.wResult) << std::dec
	 << newl;
   m_ros << "*puApplID = "
         << C_GET_DWORD (pTraceMsg->info.appl_register.dwApplID) << newl;

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintApplRegister





/**
 * Print an application release message.
 */

void CTraceMessagePrint::PrintApplRelease
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard guard (m_ros);
   struct timeval     tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Application function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "kcapi_release" << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_release))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_release)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   m_ros << "   (uApplID = "
         << C_GET_DWORD (pTraceMsg->info.appl_release.dwApplID)
         << ")" << newl;
   m_ros << "Result: 0x" << std::hex << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.appl_release.wResult)
	 << std::dec << newl;

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintApplRelease





/**
 * Print an application put message call.
 */

void CTraceMessagePrint::PrintApplPutMessage
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard guard (m_ros);
   struct timeval     tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Application function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "kcapi_put_message" << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_put_message))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_put_message)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   m_ros << "   (uApplID = "
         << C_GET_DWORD (pTraceMsg->info.appl_put_message.dwApplID)
         << newl;
   m_ros << "    pmbMsg  = "
         << CTwoDwordPointer
               (C_GET_DWORD (pTraceMsg->info.appl_put_message.dwMsgPointerLow),
                C_GET_DWORD (pTraceMsg->info.appl_put_message.dwMsgPointerHigh))
         << ")" << newl;
   m_ros << "Result: 0x" << std::hex << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.appl_put_message.wResult)
	 << std::dec << newl;

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintApplPutMessage





/**
 * Print an application get message call.
 */

void CTraceMessagePrint::PrintApplGetMessage
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard guard (m_ros);
   struct timeval     tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Application function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "kcapi_get_message" << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_get_message))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_get_message)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   m_ros << "   (uApplID = "
         << C_GET_DWORD (pTraceMsg->info.appl_get_message.dwApplID)
         << newl;
   m_ros << "    ppmbMsg = "
         << CTwoDwordPointer
               (C_GET_DWORD (pTraceMsg->info.appl_get_message.dwPointerArgLow),
                C_GET_DWORD (pTraceMsg->info.appl_get_message.dwPointerArgHigh))
         << ")" << newl;
   m_ros << "Result:   0x" << std::hex << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.appl_get_message.wResult)
	 << std::dec << newl;
   m_ros << "*ppmbMsg: "
         << CTwoDwordPointer
               (C_GET_DWORD (pTraceMsg->info.appl_get_message.dwMsgPointerLow),
                C_GET_DWORD (pTraceMsg->info.appl_get_message.dwMsgPointerHigh))
         << newl;

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintApplGetMessage





/**
 * Print an application set signal call.
 */

void CTraceMessagePrint::PrintApplSetSignal
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard guard (m_ros);
   struct timeval     tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Application function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "kcapi_set_signal" << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) +
             sizeof (pTraceMsg->info.appl_set_signal) -
             sizeof (pTraceMsg->info.appl_set_signal.qwParam64))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_set_signal)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   m_ros << "   (uApplID     = "
         << C_GET_DWORD (pTraceMsg->info.appl_set_signal.dwApplID)
         << newl;
   m_ros << "    pfnCallback = "
         << CTwoDwordPointer
               (C_GET_DWORD (pTraceMsg->info.appl_set_signal.dwCallbackLow),
                C_GET_DWORD (pTraceMsg->info.appl_set_signal.dwCallbackHigh))
         << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_set_signal))
   {
      /* the last qword is missing --> call from a 32bit application */
      m_ros << "    dwParam     = 0x"
	    << std::hex << std::setw (8) << std::setfill ('0')
            << C_GET_DWORD (pTraceMsg->info.appl_set_signal.dwParam)
            << std::dec << ")" << newl;
   }
   else
   {
      /* the last qword is in the message --> call from a 64bit application */
      m_ros << "    qwParam64   = 0x"
	    << std::hex << std::setw (16) << std::setfill ('0')
            << C_GET_QWORD (pTraceMsg->info.appl_set_signal.qwParam64)
            << std::dec << ")" << newl;
   }
   m_ros << "Result: 0x" << std::hex << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.appl_set_signal.wResult)
	 << std::dec << newl;

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintApplSetSignal





/**
 * Print an application get manufacturer call.
 */

void CTraceMessagePrint::PrintApplGetManufacturer
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard guard (m_ros);
   struct timeval     tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Application function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "kcapi_get_manufacturer" << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_get_manufacturer))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_get_manufacturer)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   m_ros << "   (uCtlr     = "
         << C_GET_DWORD (pTraceMsg->info.appl_get_manufacturer.dwCtlrNum)
         << newl;
   m_ros << "    pszBuffer = "
         << CTwoDwordPointer
               (C_GET_DWORD (pTraceMsg->info.appl_get_manufacturer.dwBufferPointerLow),
                C_GET_DWORD (pTraceMsg->info.appl_get_manufacturer.dwBufferPointerHigh))
         << ")" << newl;
   m_ros << "Result: 0x" << std::hex << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.appl_get_manufacturer.wResult)
	 << std::dec << newl;
   m_ros << "Buffer: "
         << (char *) &(pTraceMsg->info.appl_get_manufacturer.abBuffer [0])
         << newl;

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintApplGetManufacturer





/**
 * Print an application get version call.
 */

void CTraceMessagePrint::PrintApplGetVersion
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard guard (m_ros);
   struct timeval     tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Application function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "kcapi_get_version" << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_get_version))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_get_version)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   m_ros << "   (uCtlr = "
         << C_GET_DWORD (pTraceMsg->info.appl_get_version.dwCtlrNum)
         << ")" << newl;
   m_ros << "Result:             0x"
         << std::hex << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.appl_get_version.wResult)
         << std::dec << newl;
   m_ros << "CAPI major:         "
         << C_GET_DWORD (pTraceMsg->info.appl_get_version.dwCAPIMajor)
         << newl;
   m_ros << "CAPI minor:         "
         << C_GET_DWORD (pTraceMsg->info.appl_get_version.dwCAPIMinor)
         << newl;
   m_ros << "Manufacturer major: "
         << C_GET_DWORD (pTraceMsg->info.appl_get_version.dwManufacturerMajor)
         << newl;
   m_ros << "Manufacturer minor: "
         << C_GET_DWORD (pTraceMsg->info.appl_get_version.dwManufacturerMinor)
         << newl;
   m_ros << "BSD major:          "
         << C_GET_DWORD (pTraceMsg->info.appl_get_version.dwBSDMajor)
         << newl;
   m_ros << "BSD minor:          "
         << C_GET_DWORD (pTraceMsg->info.appl_get_version.dwBSDMinor)
         << newl;

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintApplGetVersion





/**
 * Print an application get serial number call.
 */

void CTraceMessagePrint::PrintApplGetSerialNumber
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard guard (m_ros);
   struct timeval     tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Application function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "kcapi_get_serial_number" << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_get_serial_number))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_get_serial_number)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   m_ros << "   (uCtlr     = "
         << C_GET_DWORD (pTraceMsg->info.appl_get_serial_number.dwCtlrNum)
         << newl;
   m_ros << "    pszBuffer = "
         << CTwoDwordPointer
               (C_GET_DWORD (pTraceMsg->info.appl_get_serial_number.dwBufferPointerLow),
                C_GET_DWORD (pTraceMsg->info.appl_get_serial_number.dwBufferPointerHigh))
         << ")" << newl;
   m_ros << "Result: 0x" << std::hex << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.appl_get_serial_number.wResult)
	 << std::dec << newl;
   m_ros << "Buffer: "
         << (char *) &(pTraceMsg->info.appl_get_serial_number.abBuffer [0])
         << newl;

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintApplGetSerialNumber





/**
 * Print an application get profile call.
 *
 * XXX: The profile data should be decoded. At least the fields themselves and
 *      their respective values should be printed out. The final version would
 *      also decode the bit fields, so for every field there would be a list of
 *      symbols according to the bits set.
 */

void CTraceMessagePrint::PrintApplGetProfile
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard guard (m_ros);
   struct timeval     tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Application function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "kcapi_get_profile" << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_get_profile))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_get_profile)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   m_ros << "   (uCtlr    = "
         << C_GET_DWORD (pTraceMsg->info.appl_get_profile.dwCtlrNum)
         << newl;
   m_ros << "    pProfile = "
         << CTwoDwordPointer
               (C_GET_DWORD (pTraceMsg->info.appl_get_profile.dwBufferPointerLow),
                C_GET_DWORD (pTraceMsg->info.appl_get_profile.dwBufferPointerHigh))
         << ")" << newl;
   m_ros << "Result: 0x" << std::hex << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.appl_get_profile.wResult)
	 << std::dec << newl;
   m_ros << std::setfill (' ');
   m_ros << "Buffer: ";
   m_ros.leftmargin (m_nNormalIndent + 8, true);
   m_ros.hexdump (&(pTraceMsg->info.appl_get_profile.profile),
                  sizeof (pTraceMsg->info.appl_get_profile.profile));

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintApplGetProfile





/**
 * Print an application get controller driver info call.
 *
 * XXX: The driver data should be decoded.
 */

void CTraceMessagePrint::PrintApplGetCtlrDriverInfo
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard guard (m_ros);
   struct timeval     tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Application function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "kcapi_get_ctlr_driver_info" << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_get_ctlr_driver_info))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_get_ctlr_driver_info)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   m_ros << "   (uCtlr    = "
         << C_GET_DWORD (pTraceMsg->info.appl_get_ctlr_driver_info.dwCtlrNum)
         << newl;
   m_ros << "    pDrvInfo = "
         << CTwoDwordPointer
               (C_GET_DWORD (pTraceMsg->info.appl_get_ctlr_driver_info.dwBufferPointerLow),
                C_GET_DWORD (pTraceMsg->info.appl_get_ctlr_driver_info.dwBufferPointerHigh))
         << ")" << newl;
   m_ros << "Result: 0x" << std::dec << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.appl_get_ctlr_driver_info.wResult) << newl;
   m_ros << std::dec << std::setfill (' ');
   m_ros << "Buffer: ";
   m_ros.leftmargin (m_nNormalIndent + 8, true);
   m_ros.hexdump (&(pTraceMsg->info.appl_get_ctlr_driver_info.driverInfo),
                  sizeof (pTraceMsg->info.appl_get_ctlr_driver_info.driverInfo));

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintApplGetCtlrDriverInfo





/**
 * Print an application reset controller call.
 */

void CTraceMessagePrint::PrintApplResetCtlr
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard        guard (m_ros);
   struct timeval            tv;
   size_t                    nNumDataBlocks;
   size_t                    n;
   CAPITraceCtlrDataBlock_t *paBlockInfo;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Application function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "kcapi_reset_ctlr" << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_reset_ctlr))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_reset_ctlr)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   nNumDataBlocks = C_GET_DWORD (pTraceMsg->info.appl_reset_ctlr.dwNumDataBlocks);
   m_ros << "   (uCtlr          = "
         << C_GET_DWORD (pTraceMsg->info.appl_reset_ctlr.dwCtlrNum)
         << newl;
   m_ros << "    nNumDataBlocks = " << nNumDataBlocks;
   paBlockInfo = (CAPITraceCtlrDataBlock_t *)
                    ((u_int8_t *) &(pTraceMsg->info.appl_reset_ctlr) +
                     sizeof (pTraceMsg->info.appl_reset_ctlr));
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) +
             sizeof (pTraceMsg->info.appl_reset_ctlr) +
             nNumDataBlocks * sizeof (*paBlockInfo))
   {
      m_ros << newl;
      m_ros << "Trace message too short for " << nNumDataBlocks
            << ", length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) +
               sizeof (pTraceMsg->info.appl_reset_ctlr) +
               nNumDataBlocks * sizeof (*paBlockInfo)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   for (n = 0; n < nNumDataBlocks; n++)
   {
      m_ros << newl;
      m_ros << "       nLenDataBlock [" << n << "] = "
            << C_GET_DWORD (paBlockInfo [n].dwLenDataBlock)
            << newl;
      m_ros << "       paucDataBlock [" << n << "] = "
            << CTwoDwordPointer
                  (C_GET_DWORD (paBlockInfo [n].dwDataBlockPointerLow),
                   C_GET_DWORD (paBlockInfo [n].dwDataBlockPointerHigh));
   }
   m_ros << ")" << newl;
   m_ros << "Result: 0x" << std::hex << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.appl_reset_ctlr.wResult)
	 << std::dec << newl;

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintApplResetCtlr





/**
 * Print an application CAPI message.
 */

void CTraceMessagePrint::PrintApplMsg
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard  guard (m_ros);
   struct timeval      tv;
   CAPIMsg_t          *pCapiMsg;
   size_t              nLenCapiMsg;
   unsigned            uCmd;
   u_int8_t           *pData;
   
   // check for at least an existing trace message header
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_msg))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be at least "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.appl_msg)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   // address the CAPI message block
   pCapiMsg = (CAPIMsg_t *) ((u_int8_t *) &(pTraceMsg->info.appl_msg) +
                             sizeof (pTraceMsg->info.appl_msg));
   nLenCapiMsg = nLenTraceMsg -
                 (size_t) ((u_int8_t *) pCapiMsg - (u_int8_t *) pTraceMsg);
                 
   // check for at least an existing CAPI message header
   if (nLenCapiMsg < sizeof (pCapiMsg->head))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be at least "
            << sizeof (pCapiMsg->head)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   // check for CAPI message to filter
   uCmd = CAPI_GET_CMD (pCapiMsg);
   if ((uCmd & CAPI_CMDMASK_COMMAND) == C_DATA_B3 &&
       ! e_oConfig.GetInterpretDataB3Msgs ())
   {
      return;
   }
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Application CAPI message" << newl;

   // determine the address of the data block for 32bit and 64bit machines
#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
   pData = (u_int8_t *)
              C_MAKE_QWORD
                 (C_GET_DWORD (pTraceMsg->info.appl_msg.dwDataPointerLow),
                  C_GET_DWORD (pTraceMsg->info.appl_msg.dwDataPointerHigh));
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
   pData = (u_int8_t *) C_GET_DWORD (pTraceMsg->info.appl_msg.dwDataPointerLow);
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */

   PrintCapiMsg (pCapiMsg, nLenCapiMsg, pData,
                 (size_t) C_GET_DWORD (pTraceMsg->info.appl_msg.dwDataLength));

} // CTraceMessagePrint::PrintApplMsg





/**
 * Print a driver level application register message.
 */

void CTraceMessagePrint::PrintDrvrRegister
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard guard (m_ros);
   struct timeval     tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Driver function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "CAPIDrv_Register at unique controller no. "
         << C_GET_DWORD (pTraceMsg->info.drvr_register.dwUniqueCtlrNum)
         << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.drvr_register))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.drvr_register)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   m_ros << "   (uDrvCtlrNum            = "
         << C_GET_DWORD (pTraceMsg->info.drvr_register.dwDrvCtlrNum)
         << newl;
   m_ros << "    uMaxLogicalConnections = "
         << C_GET_DWORD (pTraceMsg->info.drvr_register.dwMaxLogicalConnections)
         << newl;
   m_ros << "    uMaxBDataBlocks        = "
         << C_GET_DWORD (pTraceMsg->info.drvr_register.dwMaxBDataBlocks)
         << newl;
   m_ros << "    uMaxBDataLen           = "
         << C_GET_DWORD (pTraceMsg->info.drvr_register.dwMaxBDataLen)
         << newl;
   m_ros << "    *puApplID (input)      = "
         << C_GET_DWORD (pTraceMsg->info.drvr_register.dwUniqueApplID)
         << ")" << newl;
   m_ros << "Result: 0x" << std::hex << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.drvr_register.wResult)
	 << std::dec << newl;
   m_ros << "*puApplID (output) = "
         << C_GET_DWORD (pTraceMsg->info.drvr_register.dwCtlrApplID) << newl;

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintDrvrRegister





/**
 * Print a driver level application release message.
 */

void CTraceMessagePrint::PrintDrvrRelease
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard guard (m_ros);
   struct timeval     tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Driver function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "CAPIDrv_Release at unique controller no. "
         << C_GET_DWORD (pTraceMsg->info.drvr_release.dwUniqueCtlrNum)
         << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.drvr_release))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.drvr_release)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   m_ros << "   (uDrvCtlrNum = "
         << C_GET_DWORD (pTraceMsg->info.drvr_release.dwDrvCtlrNum)
         << newl;
   m_ros << "    uCtlrApplID = "
         << C_GET_DWORD (pTraceMsg->info.drvr_release.dwCtlrApplID)
         << ")" << newl;
   m_ros << "Result:          0x"
         << std::hex << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.drvr_release.wResult)
         << std::dec << std::setfill (' ')
         << newl;
   m_ros << "Unique appl. id: "
         << C_GET_DWORD (pTraceMsg->info.drvr_release.dwUniqueApplID)
         << newl;

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintDrvrRelease





/**
 * Print a driver level put message call.
 */

void CTraceMessagePrint::PrintDrvrPutMessage
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard guard (m_ros);
   struct timeval     tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Driver function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "CAPIDrv_PutMessage at unique controller no. "
         << C_GET_DWORD (pTraceMsg->info.drvr_put_message.dwUniqueCtlrNum)
         << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.drvr_put_message))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.drvr_put_message)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   m_ros << "   (uDrvCtlrNum = "
         << C_GET_DWORD (pTraceMsg->info.drvr_put_message.dwDrvCtlrNum)
         << newl;
   m_ros << "    uCtlrApplID = "
         << C_GET_DWORD (pTraceMsg->info.drvr_put_message.dwCtlrApplID)
         << newl;
   m_ros << "    pmbMsg      = "
         << CTwoDwordPointer
               (C_GET_DWORD (pTraceMsg->info.drvr_put_message.dwMsgPointerLow),
                C_GET_DWORD (pTraceMsg->info.drvr_put_message.dwMsgPointerHigh))
         << ")" << newl;
   m_ros << "Result:          0x"
	 << std::hex << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.drvr_put_message.wResult)
         << std::dec << std::setfill (' ')
         << newl;
   m_ros << "Unique appl. id: "
         << C_GET_DWORD (pTraceMsg->info.drvr_put_message.dwUniqueApplID)
         << newl;

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintDrvrPutMessage





/**
 * Print a driver level get message call.
 */

void CTraceMessagePrint::PrintDrvrGetMessage
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard guard (m_ros);
   struct timeval     tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Driver function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "kcapi_ctlr_receive_capi_message from unique controller no. "
         << C_GET_DWORD (pTraceMsg->info.drvr_get_message.dwUniqueCtlrNum)
         << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.drvr_get_message))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.drvr_get_message)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   m_ros << "   (uUniqueCtlrNum = "
         << C_GET_DWORD (pTraceMsg->info.drvr_get_message.dwUniqueCtlrNum)
         << newl;
   m_ros << "    uCtlrApplID    = "
         << C_GET_DWORD (pTraceMsg->info.drvr_get_message.dwCtlrApplID)
         << newl;
   m_ros << "    pmbMsg         = "
         << CTwoDwordPointer
               (C_GET_DWORD (pTraceMsg->info.drvr_get_message.dwMsgPointerLow),
                C_GET_DWORD (pTraceMsg->info.drvr_get_message.dwMsgPointerHigh))
         << ")" << newl;
   m_ros << "Unique appl. id:  "
         << C_GET_DWORD (pTraceMsg->info.drvr_put_message.dwUniqueApplID)
         << newl;
   m_ros << "Driver ctlr. no.: "
         << C_GET_DWORD (pTraceMsg->info.drvr_put_message.dwDrvCtlrNum)
         << newl;

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintDrvrGetMessage





/**
 * Print a driver level reset controller call.
 */

void CTraceMessagePrint::PrintDrvrResetCtlr
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard        guard (m_ros);
   struct timeval            tv;
   size_t                    nNumDataBlocks;
   size_t                    n;
   CAPITraceCtlrDataBlock_t *paBlockInfo;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Driver function call" << newl;

   m_ros.leftmargin (m_nNormalIndent);
   
   m_ros << "CAPIDrv_ResetCtlr at unique controller no. "
         << C_GET_DWORD (pTraceMsg->info.drvr_reset_ctlr.dwUniqueCtlrNum)
         << newl;
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.drvr_reset_ctlr))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.drvr_reset_ctlr)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   nNumDataBlocks = C_GET_DWORD (pTraceMsg->info.drvr_reset_ctlr.dwNumDataBlocks);
   m_ros << "   (uUniqueCtlrNum = "
         << C_GET_DWORD (pTraceMsg->info.drvr_reset_ctlr.dwUniqueCtlrNum)
         << newl;
   m_ros << "    nNumDataBlocks = " << nNumDataBlocks;
   paBlockInfo = (CAPITraceCtlrDataBlock_t *)
                    ((u_int8_t *) &(pTraceMsg->info.drvr_reset_ctlr) +
                     sizeof (pTraceMsg->info.drvr_reset_ctlr));
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) +
             sizeof (pTraceMsg->info.drvr_reset_ctlr) +
             nNumDataBlocks * sizeof (*paBlockInfo))
   {
      m_ros << newl;
      m_ros << "Trace message too short for " << nNumDataBlocks
            << ", length is " << nLenTraceMsg
            << ", must be "
            << sizeof (pTraceMsg->head) +
               sizeof (pTraceMsg->info.drvr_reset_ctlr) +
               nNumDataBlocks * sizeof (*paBlockInfo)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   for (n = 0; n < nNumDataBlocks; n++)
   {
      m_ros << "       nLenDataBlock [" << n << "] = "
            << C_GET_DWORD (paBlockInfo [n].dwLenDataBlock)
            << newl;
      m_ros << "       paucDataBlock [" << n << "] = "
            << CTwoDwordPointer
                  (C_GET_DWORD (paBlockInfo [n].dwDataBlockPointerLow),
                   C_GET_DWORD (paBlockInfo [n].dwDataBlockPointerHigh));
   }
   m_ros << ")" << newl;
   m_ros << "Result: 0x" << std::hex << std::setw (4) << std::setfill ('0')
         << C_GET_WORD (pTraceMsg->info.drvr_reset_ctlr.wResult)
	 << std::dec << newl;

   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintDrvrResetCtlr





/**
 * Print a driver level CAPI message.
 */

void CTraceMessagePrint::PrintDrvrMsg
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   CStreamConfigGuard  guard (m_ros);
   struct timeval      tv;
   CAPIMsg_t          *pCapiMsg;
   size_t              nLenCapiMsg;
   unsigned            uCmd;
   u_int8_t           *pData;
   
   // check for at least an existing trace message header
   if (nLenTraceMsg <
          sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.drvr_msg))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be at least "
            << sizeof (pTraceMsg->head) + sizeof (pTraceMsg->info.drvr_msg)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   // address the CAPI message block
   pCapiMsg = (CAPIMsg_t *) ((u_int8_t *) &(pTraceMsg->info.drvr_msg) +
                             sizeof (pTraceMsg->info.drvr_msg));
   nLenCapiMsg = nLenTraceMsg -
                 (size_t) ((u_int8_t *) pCapiMsg - (u_int8_t *) pTraceMsg);
                 
   // check for at least an existing CAPI message header
   if (nLenCapiMsg < sizeof (pCapiMsg->head))
   {
      m_ros << "Trace message too short, length is " << nLenTraceMsg
            << ", must be at least "
            << sizeof (pCapiMsg->head)
            << newl;
      
      m_ros.leftmargin (0);
      m_ros << newl;
      
      return;
   }
   
   // check for CAPI message to filter
   uCmd = CAPI_GET_CMD (pCapiMsg);
   if ((uCmd & CAPI_CMDMASK_COMMAND) == C_DATA_B3 &&
       ! e_oConfig.GetInterpretDataB3Msgs ())
   {
      return;
   }
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Driver level CAPI message" << newl;

   // determine the address of the data block for 32bit and 64bit machines
#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
   pData = (u_int8_t *)
              C_MAKE_QWORD
                 (C_GET_DWORD (pTraceMsg->info.drvr_msg.dwDataPointerLow),
                  C_GET_DWORD (pTraceMsg->info.drvr_msg.dwDataPointerHigh));
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
   pData = (u_int8_t *) C_GET_DWORD (pTraceMsg->info.drvr_msg.dwDataPointerLow);
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */

   PrintCapiMsg (pCapiMsg, nLenCapiMsg, pData,
                 (size_t) C_GET_DWORD (pTraceMsg->info.drvr_msg.dwDataLength));

} // CTraceMessagePrint::PrintDrvrMsg





/**
 * Print an unknown message
 */

void CTraceMessagePrint::PrintUnknownMessage
   (const CAPITraceMsg_t *pTraceMsg,
    size_t                nLenTraceMsg)
{
   struct timeval tv;
   
   tv.tv_sec = C_GET_DWORD (pTraceMsg->head.dwTimeSeconds);
   tv.tv_usec = C_GET_DWORD (pTraceMsg->head.dwTimeMicroSec);
   
   m_ros << tv << " Got unknown trace message type "
         << C_GET_WORD (pTraceMsg->head.wMsgType) << newl;
   m_ros << newl;
   
   m_ros.leftmargin (m_nNormalIndent);
   m_ros.hexdump (&(pTraceMsg->info), nLenTraceMsg - sizeof (pTraceMsg->head));
   
   m_ros.leftmargin (0);
   m_ros << newl;

} // CTraceMessagePrint::PrintUnknownMessage





/**
 * Print a general CAPI message.
 */

void CTraceMessagePrint::PrintCapiMsg
   (const CAPIMsg_t *pCapiMsg,
    size_t           nLenCapiMsg,
    u_int8_t        *pData,
    size_t           nLenData)
{
   unsigned uCmd;
   size_t   n;

   // check for valid parameters
   assert (pCapiMsg);
   assert (nLenCapiMsg >= sizeof (pCapiMsg->head));
   
   m_ros.leftmargin (m_nNormalIndent);
   
   // print out the CAPI message command
   uCmd = CAPI_GET_CMD (pCapiMsg);
   m_ros << GetCapiMsgName (uCmd) << " (0x"
         << std::hex << std::setw (4) << std::setfill ('0')
         << uCmd
         << std::dec << std::setfill (' ') << ")"
         << newl;
   m_ros.newl ();
   m_ros.leftmargin (m_nNormalIndent + 3);
   
   // check if hex output is desired
   if (e_oConfig.GetInterpretHexOutput ())
   {
      m_ros.hexdump (pCapiMsg, nLenCapiMsg);
      m_ros.newl ();
   }
   
   // and now print the rest of the header
   m_ros << "Length:   " << CAPI_GET_LEN (pCapiMsg) << newl;
   m_ros << "Appl. id: " << CAPI_GET_APPL (pCapiMsg) << newl;
   m_ros << "Msg. no.: 0x"
         << std::hex << std::setw (4) << std::setfill ('0')
         << CAPI_GET_MSGNUM (pCapiMsg)
         << std::dec << std::setfill (' ') << newl;
   m_ros << "CID:      0x"
         << std::hex << std::setw (8) << std::setfill ('0')
         << CAPI_GET_CID (pCapiMsg)
         << std::dec << std::setfill (' ') << newl;
   m_ros << newl;

   // decode the info fields of the CAPI message
   
   // initialize the bound pointer over the remaining CAPI message
   CBoundPointer addr ((u_int8_t *) &(pCapiMsg->info),
                       nLenCapiMsg - sizeof (pCapiMsg->head));
                       
   // find the current CAPI message in the dispatch table
   for (n = 0; n < ARRAY_COUNT (m_aCapiMsgDispTbl); ++n)
   {
      if (uCmd == m_aCapiMsgDispTbl [n].wMsgCmd)
      {
         break;
      }
   }
   if (n < ARRAY_COUNT (m_aCapiMsgDispTbl))
   {
      // message command found in table --> print out the fields
      PrintCapiMsgInfoFields (addr,
                              m_aCapiMsgDispTbl [n].paMsgFields,
                              m_aCapiMsgDispTbl [n].nNumMsgFields);

      m_ros << newl;
   }

   // Now all fields of the CAPI message should be print. If the message was not
   // found in the table, it is assumed to have no additional fields to print
   // out. E.g. a Connect-Active-Response has no content after the CAPI header.
   // So if there are still bytes left to print at this point, the message is
   // inconsistent.
   if (addr.bytes_remaining () != 0)
   {
      m_ros << "WARNING: Still " << addr.bytes_remaining ()
            << " bytes left after decoding, message inconsistent"
            << newl;
      m_ros << newl;
   }
   
   // finally do a hexdump of an attached data block if desired
   if (nLenData > 0 && pData != NULL &&
       e_oConfig.GetInterpretDataB3Blocks () &&
       e_oConfig.GetInterpretDataB3BlockLen () > 0)
   {
      m_ros.setf (std::ios::left, std::ios::adjustfield);
      m_ros << std::setw (10) << "Data-B3:";
      m_ros.setf (std::ios::right, std::ios::adjustfield);
      m_ros.leftmargin (m_ros.leftmargin () + 10, true);
      m_ros.hexdump (pData, nLenData);
      m_ros << newl;
   }
   
} // CTraceMessagePrint::PrintCapiMsg





/**
 * Print the info fields of a CAPI message.
 */

void CTraceMessagePrint::PrintCapiMsgInfoFields
   (CBoundPointer                  &addr,
    CapiMsgFieldDescriptionEntry_t *paMsgFields,
    size_t                          nNumMsgFields)
{
   unsigned uValueIndent;
   size_t   n;
   size_t   nLen;
   
   try
   {
      // determine the value indentation according to the prompt strings
      for (uValueIndent = 0, n = 0; n < nNumMsgFields; ++n)
      {
         nLen = strlen (paMsgFields [n].pszPrompt);
         if (nLen > uValueIndent)
         {
            uValueIndent = nLen;
         }
      }
      // adjust the indent for ": "
      uValueIndent += 2;
      
      // now print out every field value
      for (n = 0; n < nNumMsgFields && addr.bytes_remaining () > 0; ++n)
      {
         (this->*(paMsgFields [n].pfnPrintField))
            (addr, paMsgFields [n].pszPrompt, uValueIndent);
      }
   }
   catch (CBoundPointerDomainError &)
   {
      m_ros << newl;
      m_ros << "ERROR: Field " << paMsgFields [n].pszPrompt
            << " not available or incomplete in CAPI message"
            << newl;
   }
   
} // CTraceMessagePrint::PrintCapiMsgInfoFields





/**
 * Print a string representation of a CIP value.
 */

void CTraceMessagePrint::PrintCipValue
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   struct CipValueInfo_t
   {
      cWORD wCipValue;
   } *pInfo;
   CStreamConfigGuard  guard (m_ros);
   const char         *psz;
   unsigned            uCipValue;

   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the CIP value and advance the address pointer
   pInfo = (CipValueInfo_t *) addr.get_pointer ();
   addr += sizeof (pInfo->wCipValue);
   uCipValue = C_GET_WORD (pInfo->wCipValue);
   
   // get a string representation of the CIP value
   switch (uCipValue)
   {
      case CAPI_CIP_NO:                        psz = "No CIP value"; break;
      case CAPI_CIP_SPEECH:                    psz = "Speech"; break;
      case CAPI_CIP_UNRESTRICTED_DATA:         psz = "Unrestricted data"; break;
      case CAPI_CIP_RESTRICTED_DATA:           psz = "Restricted data"; break;
      case CAPI_CIP_3100Hz_AUDIO:              psz = "3.1 kHz Audio"; break;
      case CAPI_CIP_7kHz_AUDIO:                psz = "7 kHz Audio"; break;
      case CAPI_CIP_VIDEO:                     psz = "Video"; break;
      case CAPI_CIP_PACKET_MODE:               psz = "Packet mode"; break;
      case CAPI_CIP_DATA_56:                   psz = "56 kbit/s data"; break;
      case CAPI_CIP_UNRESTRICTED_DATA_TONES:   psz = "Unrestricted data with tones"; break;
      case CAPI_CIP_TELEPHONY:                 psz = "Telephony"; break;
      case CAPI_CIP_FAX_G2_G3:                 psz = "Fax G2/G3"; break;
      case CAPI_CIP_FAX_G4_CLASS1:             psz = "Fax G4 class 1"; break;
      case CAPI_CIP_FAX_G4_CLASS2_CLASS3:      psz = "Fax G4 class 2 / class 3"; break;
      case CAPI_CIP_TELETEX_BASIC_PROCESSABLE: psz = "Teletex basic, processable"; break;
      case CAPI_CIP_TELETEX_BASIC:             psz = "Teletex basic"; break;
      case CAPI_CIP_VIDEOTEX:                  psz = "Videotex"; break;
      case CAPI_CIP_TELEX:                     psz = "Telex"; break;
      case CAPI_CIP_X400:                      psz = "X.400"; break;
      case CAPI_CIP_X200:                      psz = "X.200"; break;
      case CAPI_CIP_7kHz_TELEPHONY:            psz = "Telephony, 7 kHz"; break;
      case CAPI_CIP_VIDEO_TELEPHONY_1ST:       psz = "Video telephony, 1st"; break;
      case CAPI_CIP_VIDEO_TELEPHONY_2ND:       psz = "Video telephony, 2nd"; break;
      default:                                 psz = "Unknown CIP value"; break;
   }
   
   // now finally do the output
   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ": ";
   m_ros << psz << " (" << uCipValue << ")" << newl;
   
} // CTraceMessagePrint::PrintCipValue





/**
 * Print a Called Party Number.
 */

void CTraceMessagePrint::PrintCalledPartyNumber
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard  guard (m_ros);
   u_int8_t           *p;
   size_t              nLen;
   unsigned            uTon;
   std::string         str;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the information element and determine its length
   p = addr.get_pointer ();
   nLen = (size_t) *p;
   addr += nLen + 1;
   ++p;
   
   // check for empty information element
   if (nLen == 0)
   {
      m_ros.setf (std::ios::left, std::ios::adjustfield);
      m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt))
            << ": <Empty>" << newl;
      return;
   }
   
   // determine the type of number (and numbering plan)
   uTon = *(p++);

   // create a string with the digits
   str = std::string ((char *) p, nLen - 1);
   
   // now finally do the output
   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ": ";
   m_ros.setf (std::ios::right, std::ios::adjustfield);
   m_ros << std::hex << "0x" << std::setw (2) << std::setfill ('0') << uTon
         << std::dec << std::setfill (' ')
         << " '" << str << "'" << newl;
   
} // CTraceMessagePrint::PrintCalledPartyNumber





/**
 * Print a Calling Party Number.
 */

void CTraceMessagePrint::PrintCallingPartyNumber
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard  guard (m_ros);
   u_int8_t           *p;
   size_t              nLen;
   unsigned            uTon;
   bool                fPresAndScreenIndAvail;
   unsigned            uPresAndScreenInd;
   std::string         str;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the information element and determine its length
   p = addr.get_pointer ();
   nLen = (size_t) *p;
   addr += nLen + 1;
   ++p;
   m_ros.setf (std::ios::left, std::ios::adjustfield);
   
   // check for empty information element
   if (nLen == 0)
   {
      m_ros << pszPrompt
            << std::setw (uValueIndent - strlen (pszPrompt)) << ": "
            << "<Empty>" << newl;
      return;
   }
   
   // determine the type of number (and numbering plan)
   uTon = *(p++);

   // determine the presentation and screening indicator
   if ((uTon & 0x80) == 0 && nLen >= 2)
   {
      uPresAndScreenInd = *(p++);
      fPresAndScreenIndAvail = true;
   }
   else
   {
      uPresAndScreenInd = 0;
      fPresAndScreenIndAvail = false;
   }
   
   // create a string with the digits
   str = std::string ((char *) p, nLen - 2);
   
   // now finally do the output
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ": ";
   m_ros.setf (std::ios::right, std::ios::adjustfield);
   m_ros << std::hex << "0x" << std::setfill ('0') << std::setw (2) << uTon;
   if (fPresAndScreenIndAvail)
   {
      m_ros << " 0x" << std::setw (2) << uPresAndScreenInd;
   }
   m_ros << " '" << str << "'" << newl;
   
} // CTraceMessagePrint::PrintCallingPartyNumber





/**
 * Print B-Protocol information.
 */

void CTraceMessagePrint::PrintBProtocol
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard  guard (m_ros);
   CAPIBProtocol_t    *pInfo;
   size_t              nLenTotal;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the information element and determine its length
   pInfo = (CAPIBProtocol_t *) addr.get_pointer ();
   nLenTotal = (size_t) C_GET_BYTE (pInfo->bLength);
   addr += nLenTotal + 1;

   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ":";
   
   // check for empty information element
   if (nLenTotal == 0)
   {
      m_ros << "<Default>" << newl;
      return;
   }
   m_ros << newl;
   
   // check for minimum length (fixed part of structure, 3 empty sub structures,
   // minus the length byte of the overall structure)
   if (nLenTotal < sizeof (*pInfo) + 3 - sizeof (pInfo->bLength))
   {
      m_ros << "ERROR: B-Protocol structure is too short" << newl;
      return;
   }
   
   // adjust the output margins according to the new indentation
   m_ros.leftmargin (m_ros.leftmargin () + 1);
   if (uValueIndent <= sizeof ("B1 Configuration") + 1)
   {
      uValueIndent = sizeof ("B1 Configuration") + 1;
   }
   else
   {
      --uValueIndent;
   }
   
   // print the B-Protocol values
   m_ros << std::setw (uValueIndent) << "B1 Protocol:"
         << GetB1ProtocolName (C_GET_WORD (pInfo->wB1protocol))
         << " (" << C_GET_WORD (pInfo->wB1protocol) << ")"
         << newl;
   m_ros << std::setw (uValueIndent) << "B2 Protocol:"
         << GetB2ProtocolName (C_GET_WORD (pInfo->wB2protocol))
         << " (" << C_GET_WORD (pInfo->wB2protocol) << ")"
         << newl;
   m_ros << std::setw (uValueIndent) << "B3 Protocol:"
         << GetB3ProtocolName (C_GET_WORD (pInfo->wB3protocol))
         << " (" << C_GET_WORD (pInfo->wB3protocol) << ")"
         << newl;
   m_ros.setf (std::ios::right, std::ios::adjustfield);
   
   CBoundPointer  tmpAddr ((u_int8_t *) pInfo, nLenTotal + 1);
   const char    *pszContext = "";
   tmpAddr += sizeof (*pInfo);
   
   try
   {
      pszContext = "B1 Configuration";
      PrintB1Configuration
         (tmpAddr, pszContext, C_GET_WORD (pInfo->wB1protocol), uValueIndent);
      pszContext = "B2 Configuration";
      PrintB2Configuration
         (tmpAddr, pszContext, C_GET_WORD (pInfo->wB2protocol), uValueIndent);
      pszContext = "B3 Configuration";
      PrintB3Configuration
         (tmpAddr, pszContext, C_GET_WORD (pInfo->wB3protocol), uValueIndent);
      if (tmpAddr.bytes_remaining () > 0)
      {
         pszContext = "Global Configuration";
         PrintBProtGlobalConfig (tmpAddr, pszContext, uValueIndent);
      }
   }
   catch (CBoundPointerDomainError &)
   {
      m_ros << newl;
      m_ros << "ERROR: Length of embedded structure \"" << pszContext
            << "\" exceeds length of B-Protocol structure" << newl;
   }
   
} // CTraceMessagePrint::PrintBProtocol





/**
 * Print B1-Configuration.
 */

void CTraceMessagePrint::PrintB1Configuration
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uB1Protocol,
    unsigned       uValueIndent)
{
   CStreamConfigGuard  guard (m_ros);
   u_int8_t           *pBase;
   size_t              nLenTotal;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the information element and determine its length
   pBase = addr.get_pointer ();
   nLenTotal = (size_t) *pBase;
   addr += nLenTotal + 1;

   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ":";
   
   // check for empty information element
   if (nLenTotal == 0)
   {
      m_ros << "<Default>" << newl;
      return;
   }
   m_ros << newl;
   
   // adjust the output margins according to the new indentation
   m_ros.leftmargin (m_ros.leftmargin () + 1);
   if (uValueIndent <= 24)
   {
      uValueIndent = 24;
   }
   else
   {
      --uValueIndent;
   }
   
   // distinguish between the various B1 protocols
   if (uB1Protocol == CAPI_B1_V110_ASYNCH ||
       uB1Protocol == CAPI_B1_V110_SYNCH_HDLC)
   {
      CAPIB1ConfigV110_t *pInfo = (CAPIB1ConfigV110_t *) pBase;
      const char         *pszParity;
      const char         *pszStopbits;
      
      // check for valid length
      if (nLenTotal < sizeof (*pInfo) - sizeof (pInfo->bLength))
      {
         m_ros << "ERROR: B1-Configuration structure is too short" << newl;
         return;
      }
      if (nLenTotal >= sizeof (*pInfo))
      {
         m_ros << "WARNING: B1-Configuration structure is too long" << newl;
      }

      m_ros << std::setw (uValueIndent) << "Maximum bit rate:"
            << C_GET_WORD (pInfo->wRate)
            << newl;
      m_ros << std::setw (uValueIndent) << "Bits per character:"
            << C_GET_WORD (pInfo->wBitsPerChar)
            << newl;
      switch (C_GET_WORD (pInfo->wParity))
      {
         case 0:        pszParity = "None"; break;
         case 1:        pszParity = "Odd"; break;
         case 2:        pszParity = "Even"; break;
         default:       pszParity = "Unknown"; break;
      }
      m_ros << std::setw (uValueIndent) << "Parity:" << pszParity
            << newl;
      if (uB1Protocol == CAPI_B1_V110_ASYNCH)
      {
         switch (C_GET_WORD (pInfo->wStopbits))
         {
            case 0:     pszStopbits = "1"; break;
            case 1:     pszStopbits = "2"; break;
            default:    pszStopbits = "Unknown"; break;
         }
      }
      else
      {
         pszStopbits = "None";
      }
      m_ros << std::setw (uValueIndent) << "Stop bits:" << pszStopbits
            << newl;
   }
   else if (uB1Protocol == CAPI_B1_T30_MODEM)
   {
      CAPIB1ConfigFaxG3 *pInfo = (CAPIB1ConfigFaxG3 *) pBase;
      const char        *pszParity;
      const char        *pszStopbits;
      
      // check for valid length
      if (nLenTotal < sizeof (*pInfo) - sizeof (pInfo->bLength))
      {
         m_ros << "ERROR: B1-Configuration structure is too short" << newl;
         return;
      }
      if (nLenTotal >= sizeof (*pInfo))
      {
         m_ros << "WARNING: B1-Configuration structure is too long" << newl;
      }

      m_ros << std::setw (uValueIndent) << "Maximum bit rate:"
            << C_GET_WORD (pInfo->wRate)
            << newl;
      m_ros << std::setw (uValueIndent) << "Transmit level in dB:"
            << C_GET_WORD (pInfo->wTransmitLevel)
            << newl;
      switch (C_GET_WORD (pInfo->wParity))
      {
         case 0:        pszParity = "None"; break;
         case 1:        pszParity = "Odd"; break;
         case 2:        pszParity = "Even"; break;
         default:       pszParity = "Unknown"; break;
      }
      m_ros << std::setw (uValueIndent) << "Parity:" << pszParity
            << newl;
      switch (C_GET_WORD (pInfo->wStopbits))
      {
         case 0:     pszStopbits = "1"; break;
         case 1:     pszStopbits = "2"; break;
         default:    pszStopbits = "Unknown"; break;
      }
      m_ros << std::setw (uValueIndent) << "Stop bits:" << pszStopbits
            << newl;
   }
   else if (uB1Protocol == CAPI_B1_MODEM_FULL ||
            uB1Protocol == CAPI_B1_MODEM_ASYNCH ||
            uB1Protocol == CAPI_B1_MODEM_SYNCH_HDLC)
   {
      CAPIB1ConfigModem *pInfo = (CAPIB1ConfigModem *) pBase;
      const char        *pszParity;
      const char        *pszStopbits;
      
      // check for valid length
      if (nLenTotal < sizeof (*pInfo) - sizeof (pInfo->bLength))
      {
         m_ros << "ERROR: B1-Configuration structure is too short" << newl;
         return;
      }
      if (nLenTotal >= sizeof (*pInfo))
      {
         m_ros << "WARNING: B1-Configuration structure is too long" << newl;
      }

      m_ros << std::setw (uValueIndent) << "Maximum bit rate:"
            << C_GET_WORD (pInfo->wRate)
            << newl;
      m_ros << std::setw (uValueIndent) << "Bits per character:"
            << C_GET_WORD (pInfo->wBitsPerChar)
            << newl;
      switch (C_GET_WORD (pInfo->wParity))
      {
         case 0:        pszParity = "None"; break;
         case 1:        pszParity = "Odd"; break;
         case 2:        pszParity = "Even"; break;
         default:       pszParity = "Unknown"; break;
      }
      m_ros << std::setw (uValueIndent) << "Parity:" << pszParity
            << newl;
      if (uB1Protocol != CAPI_B1_MODEM_SYNCH_HDLC)
      {
         switch (C_GET_WORD (pInfo->wStopbits))
         {
            case 0:     pszStopbits = "1"; break;
            case 1:     pszStopbits = "2"; break;
            default:    pszStopbits = "Unknown"; break;
         }
      }
      else
      {
         pszStopbits = "None";
      }
      m_ros << std::setw (uValueIndent) << "Stop bits:" << pszStopbits
            << newl;
      m_ros << std::setw (uValueIndent) << "Options:";
      m_ros.setf (std::ios::right, std::ios::adjustfield);
      m_ros << std::hex << "0x" << std::setw (4)
            << C_GET_WORD (pInfo->wOptions) << std::dec << newl;
      m_ros.setf (std::ios::left, std::ios::adjustfield);
      m_ros << std::setw (uValueIndent) << "Speed negotiation:";
      m_ros.setf (std::ios::right, std::ios::adjustfield);
      m_ros << std::hex << "0x" << std::setw (4)
            << C_GET_WORD (pInfo->wSpeedNegotiation) << std::dec << newl;
   }
   else
   {
      m_ros << "WARNING: Unknown B1-Configuration for this B1-Protocol" << newl;
   }

} // CTraceMessagePrint::PrintB1Configuration





/**
 * Print B2-Configuration.
 */

void CTraceMessagePrint::PrintB2Configuration
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uB2Protocol,
    unsigned       uValueIndent)
{
   CStreamConfigGuard  guard (m_ros);
   u_int8_t           *pBase;
   size_t              nLenTotal;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the information element and determine its length
   pBase = addr.get_pointer ();
   nLenTotal = (size_t) *pBase;
   addr += nLenTotal + 1;

   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ":";
   
   // check for empty information element
   if (nLenTotal == 0)
   {
      m_ros << "<Default>" << newl;
      return;
   }
   m_ros << newl;
   
   // adjust the output margins according to the new indentation
   m_ros.leftmargin (m_ros.leftmargin () + 1);
   if (uValueIndent <= 24)
   {
      uValueIndent = 24;
   }
   else
   {
      --uValueIndent;
   }
   
   // distinguish between the various B2 protocols
   if (uB2Protocol == CAPI_B2_ISO7776_X75_SLP ||
       uB2Protocol == CAPI_B2_SDLC ||
       uB2Protocol == CAPI_B2_LAPD_X25 ||
       uB2Protocol == CAPI_B2_ISO7776_X75_SLP_COMP ||
       uB2Protocol == CAPI_B2_V120_ASYNCH ||
       uB2Protocol == CAPI_B2_V120_ASYNCH_COMP ||
       uB2Protocol == CAPI_B2_V120_TRANSPARENT ||
       uB2Protocol == CAPI_B2_LAPD_FREE_SAPI)
   {
      // Note: Assume all structures are of the same layout.
      CAPIB2ConfigX75Slp_t *pInfo = (CAPIB2ConfigX75Slp_t *) pBase;
      
      // check for valid length
      if (uB2Protocol == CAPI_B2_ISO7776_X75_SLP_COMP ||
          uB2Protocol == CAPI_B2_V120_ASYNCH_COMP)
      {
         if (nLenTotal <
                sizeof (CAPIB2ConfigV42bis_t) - sizeof (pInfo->bLength))
         {
            m_ros << "ERROR: B2-Configuration structure is too short" << newl;
            return;
         }
         if (nLenTotal >= sizeof (CAPIB2ConfigV42bis_t))
         {
            m_ros << "WARNING: B2-Configuration structure is too long" << newl;
         }
      }
      else
      {
         if (nLenTotal < sizeof (*pInfo) - sizeof (pInfo->bLength))
         {
            m_ros << "ERROR: B2-Configuration structure is too short" << newl;
            return;
         }
      }

      m_ros << std::setw (uValueIndent) << "Address A:";
      m_ros.setf (std::ios::right, std::ios::adjustfield);
      m_ros << "0x" << std::hex << std::setw (2)
            << C_GET_BYTE (pInfo->bAddressA) << std::dec
            << newl;
      m_ros.setf (std::ios::left, std::ios::adjustfield);
      m_ros << std::setw (uValueIndent) << "Address B:";
      m_ros.setf (std::ios::right, std::ios::adjustfield);
      m_ros << "0x" << std::hex << std::setw (2)
            << C_GET_BYTE (pInfo->bAddressB) << std::dec
            << newl;
      m_ros.setf (std::ios::left, std::ios::adjustfield);
      m_ros << std::setw (uValueIndent) << "Modulo mode:"
            << C_GET_BYTE (pInfo->bModuloMode)
            << newl;
      m_ros << std::setw (uValueIndent) << "Window size:"
            << C_GET_BYTE (pInfo->bWindowSize)
            << newl;
      
      if (uB2Protocol == CAPI_B2_ISO7776_X75_SLP_COMP ||
          uB2Protocol == CAPI_B2_V120_ASYNCH_COMP)
      {
         CAPIB2ConfigV42bis_t *pInfo2 = (CAPIB2ConfigV42bis_t *) pBase;
         const char           *pszDirection;
         
         switch (C_GET_WORD (pInfo2->wDirection))
         {
            case CAPI_B2CFG_V42BIS_DIR_ALL:
               pszDirection = "All";
               break;
               
            case CAPI_B2CFG_V42BIS_DIR_INCOMING:
               pszDirection = "Incoming";
               break;
               
            case CAPI_B2CFG_V42BIS_DIR_OUTGOING:
               pszDirection = "Outgoing";
               break;
               
            default:
               pszDirection = "Unknown";
               break;
         }
         m_ros << std::setw (uValueIndent) << "Direction:" << pszDirection
               << newl;
         m_ros << std::setw (uValueIndent) << "Number of code words:"
               << C_GET_WORD (pInfo2->wNumCodeWords)
               << newl;
         m_ros << std::setw (uValueIndent) << "Maximum string length:"
               << C_GET_WORD (pInfo2->wMaxStringLength)
               << newl;
      }
      else
      {
         CBoundPointer tmpAddr (pBase, nLenTotal + 1);
         tmpAddr += sizeof (*pInfo);
         if (tmpAddr.bytes_remaining () > 0)
         {
            PrintCapiStruct (tmpAddr, "XID", uValueIndent);
         }
      }
   }
   else if (uB2Protocol == CAPI_B2_MODEM_FULL)
   {
      CAPIB2ConfigModemFull_t *pInfo = (CAPIB2ConfigModemFull_t *) pBase;
      
      // check for valid length
      if (nLenTotal < sizeof (*pInfo) - sizeof (pInfo->bLength))
      {
         m_ros << "ERROR: B2-Configuration structure is too short" << newl;
         return;
      }
      if (nLenTotal >= sizeof (*pInfo))
      {
         m_ros << "WARNING: B2-Configuration structure is too long" << newl;
      }

      m_ros << std::setw (uValueIndent) << "Options:";
      m_ros.setf (std::ios::right, std::ios::adjustfield);
      m_ros << "0x" << std::hex << std::setw (4)
            << C_GET_WORD (pInfo->wOptions) << std::dec
            << newl;
   }
   else
   {
      m_ros << "WARNING: Unknown B2-Configuration for this B2-Protocol" << newl;
   }

} // CTraceMessagePrint::PrintB2Configuration





/**
 * Print B3-Configuration.
 */

void CTraceMessagePrint::PrintB3Configuration
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uB3Protocol,
    unsigned       uValueIndent)
{
   CStreamConfigGuard  guard (m_ros);
   u_int8_t           *pBase;
   size_t              nLenTotal;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the information element and determine its length
   pBase = addr.get_pointer ();
   nLenTotal = (size_t) *pBase;
   addr += nLenTotal + 1;

   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ":";
   
   // check for empty information element
   if (nLenTotal == 0)
   {
      m_ros << "<Default>" << newl;
      return;
   }
   m_ros << newl;
   
   // adjust the output margins according to the new indentation
   m_ros.leftmargin (m_ros.leftmargin () + 1);
   if (uValueIndent <= 24)
   {
      uValueIndent = 24;
   }
   else
   {
      --uValueIndent;
   }
   
   // distinguish between the various B3 protocols
   if (uB3Protocol == CAPI_B3_T90NL ||
       uB3Protocol == CAPI_B3_ISO8208 ||
       uB3Protocol == CAPI_B3_X25_DCE)
   {
      CAPIB3ConfigT90NL_t *pInfo = (CAPIB3ConfigT90NL_t *) pBase;
      
      // check for valid length
      if (nLenTotal < sizeof (*pInfo) - sizeof (pInfo->bLength))
      {
         m_ros << "ERROR: B3-Configuration structure is too short" << newl;
         return;
      }
      if (nLenTotal >= sizeof (*pInfo))
      {
         m_ros << "WARNING: B3-Configuration structure is too long" << newl;
      }

      m_ros << std::setw (uValueIndent) << "LIC:" << C_GET_WORD (pInfo->wLIC)
            << newl;
      m_ros << std::setw (uValueIndent) << "HIC:" << C_GET_WORD (pInfo->wHIC)
            << newl;
      m_ros << std::setw (uValueIndent) << "LTC:" << C_GET_WORD (pInfo->wLTC)
            << newl;
      m_ros << std::setw (uValueIndent) << "HTC:" << C_GET_WORD (pInfo->wHTC)
            << newl;
      m_ros << std::setw (uValueIndent) << "LOC:" << C_GET_WORD (pInfo->wLOC)
            << newl;
      m_ros << std::setw (uValueIndent) << "HOC:" << C_GET_WORD (pInfo->wHOC)
            << newl;
      m_ros << std::setw (uValueIndent) << "Modulo mode:"
            << C_GET_WORD (pInfo->wModuloMode)
            << newl;
      m_ros << std::setw (uValueIndent) << "Window size:"
            << C_GET_WORD (pInfo->wWindowSize)
            << newl;
   }
   else if (uB3Protocol == CAPI_B3_T30_FAX_G3 ||
            uB3Protocol == CAPI_B3_T30_FAX_G3_EXT)
   {
      // Note: Assume both structure are of the same layout, only the meaning of
      //       the first field differs.
      CAPIB3ConfigFaxG3Ext_t *pInfo = (CAPIB3ConfigFaxG3Ext_t *) pBase;
      const char             *pszResolution;
      const char             *pszFormat;
      
      // check for valid length
      if (nLenTotal < sizeof (*pInfo) - sizeof (pInfo->bLength))
      {
         m_ros << "ERROR: B3-Configuration structure is too short" << newl;
         return;
      }

      if (uB3Protocol == CAPI_B3_T30_FAX_G3)
      {
         switch (C_GET_WORD (pInfo->wOptions))
         {
            case CAPI_FAXG3_RESOLUTION_STANDARD:
               pszResolution = "Normal";
               break;
               
            case CAPI_FAXG3_RESOLUTION_FINE:
               pszResolution = "Fine";
               break;
               
            default:
               pszResolution = "Unknown";
               break;
         }
         m_ros << std::setw (uValueIndent) << "Resolution:" << pszResolution
               << newl;
      }
      else
      {
         m_ros << std::setw (uValueIndent) << "Options:";
         m_ros.setf (std::ios::right, std::ios::adjustfield);
         m_ros << std::hex << std::setfill ('0') << "0x" << std::setw (4)
               << C_GET_WORD (pInfo->wOptions)
               << std::dec << std::setfill (' ')
               << newl;
         m_ros.setf (std::ios::left, std::ios::adjustfield);
      }
      switch (C_GET_WORD (pInfo->wFormat))
      {
         case CAPI_FAXG3_FORMAT_SFF: pszFormat = "SFF"; break;
         case CAPI_FAXG3_FORMAT_PLAIN_FAX:   pszFormat = "Plain fax"; break;
         case CAPI_FAXG3_FORMAT_PCX:         pszFormat = "PCX"; break;
         case CAPI_FAXG3_FORMAT_DCX:         pszFormat = "DCX"; break;
         case CAPI_FAXG3_FORMAT_TIFF:        pszFormat = "TIFF"; break;
         case CAPI_FAXG3_FORMAT_ASCII:       pszFormat = "ASCII"; break;
         case CAPI_FAXG3_FORMAT_EXT_ANSI:    pszFormat = "Extended ANSI"; break;
         case CAPI_FAXG3_FORMAT_BINARY_FILE: pszFormat = "Binary file"; break;
         default:                            pszFormat = "Unknown"; break;
      }
      m_ros << std::setw (uValueIndent) << "Format:" << pszFormat
            << newl;

      CBoundPointer tmpAddr ((u_int8_t *) pInfo, nLenTotal + 1);
      const char    *pszContext = "";
      tmpAddr += sizeof (*pInfo);

      try
      {
         if (tmpAddr.bytes_remaining () > 0)
         {
            pszContext = "Station ID";
            PrintCapiStruct (tmpAddr, pszContext, uValueIndent);
         }
         if (tmpAddr.bytes_remaining () > 0)
         {
            pszContext = "Headline";
            PrintCapiStruct (tmpAddr, pszContext, uValueIndent);
         }
      }
      catch (CBoundPointerDomainError &)
      {
         m_ros << newl;
         m_ros << "ERROR: Length of embedded structure \"" << pszContext
               << "\" exceeds length of B3-Configuration structure" << newl;
      }
   }

} // CTraceMessagePrint::PrintB3Configuration





/**
 * Print B-Protocol Global Configuration structure.
 */

void CTraceMessagePrint::PrintBProtGlobalConfig
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard          guard (m_ros);
   CAPIBProtocolGlobalOptions *pInfo;
   size_t                      nLenTotal;
   const char                 *pszOp;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the information element and determine its length
   pInfo = (CAPIBProtocolGlobalOptions_t *) addr.get_pointer ();
   nLenTotal = (size_t) C_GET_BYTE (pInfo->bLength);
   addr += nLenTotal + 1;

   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ":";
   
   // check for empty information element
   if (nLenTotal == 0)
   {
      m_ros << "<Default>" << newl;
      return;
   }
   m_ros << newl;
   
   // adjust the output margins according to the new indentation
   m_ros.leftmargin (m_ros.leftmargin () + 1);
   if (uValueIndent <= 24)
   {
      uValueIndent = 24;
   }
   else
   {
      --uValueIndent;
   }
   
   // check for valid length
   if (nLenTotal < sizeof (*pInfo) - sizeof (pInfo->bLength))
   {
      m_ros << "ERROR: Global Configuration structure is too short" << newl;
      return;
   }
   if (nLenTotal >= sizeof (*pInfo))
   {
      m_ros << "WARNING: Global Configuration structure is too long" << newl;
   }

   switch (C_GET_WORD (pInfo->wBChannelOperation))
   {
      case CAPI_BPROT_GLOBOPT_BCHNOP_DEFAULT: pszOp = "Default"; break;
      case CAPI_BPROT_GLOBOPT_BCHNOP_DTE:     pszOp = "DTE"; break;
      case CAPI_BPROT_GLOBOPT_BCHNOP_DCE:     pszOp = "DCE"; break;
      default:                                pszOp = "Unknown"; break;
   }
   m_ros << std::setw (uValueIndent) << "B-channel operation:" << pszOp
         << newl;

} // CTraceMessagePrint::PrintBProtGlobalConfig





/**
 * Print an Additional Info structure.
 */

void CTraceMessagePrint::PrintAdditionalInfo
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard  guard (m_ros);
   u_int8_t           *p;
   size_t              nLenTotal;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the information element and determine its length
   p = addr.get_pointer ();
   nLenTotal = (size_t) *(p++);
   addr += nLenTotal + 1;

   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ":";
   m_ros.setf (std::ios::right, std::ios::adjustfield);
   
   // check for empty information element
   if (nLenTotal == 0)
   {
      m_ros << "<Empty>" << newl;
      return;
   }
   m_ros << newl;
   
   // adjust the output margins according to the new indentation
   m_ros.leftmargin (m_ros.leftmargin () + 1);
   if (uValueIndent <= sizeof ("B-channel information") + 1)
   {
      uValueIndent = sizeof ("B-channel information") + 1;
   }
   else
   {
      --uValueIndent;
   }
   
   CBoundPointer  tmpAddr (p, nLenTotal);
   const char    *pszContext = "";
   
   try
   {
      if (tmpAddr.bytes_remaining () > 0)
      {
         pszContext = "B-channel information";
         PrintBChannelInfo (tmpAddr, pszContext, uValueIndent);
      }
      if (tmpAddr.bytes_remaining () > 0)
      {
         pszContext = "Keypad facility";
         PrintCapiStruct (tmpAddr, pszContext, uValueIndent);
      }
      if (tmpAddr.bytes_remaining () > 0)
      {
         pszContext = "User-user data";
         PrintCapiStruct (tmpAddr, pszContext, uValueIndent);
      }
      if (tmpAddr.bytes_remaining () > 0)
      {
         pszContext = "Facility data array";
         PrintCapiStruct (tmpAddr, pszContext, uValueIndent);
      }
      if (tmpAddr.bytes_remaining () > 0)
      {
         pszContext = "Sending complete";
         PrintSendingComplete (tmpAddr, pszContext, uValueIndent);
      }
   }
   catch (CBoundPointerDomainError &)
   {
      m_ros << newl;
      m_ros << "ERROR: Length of embedded structure \"" << pszContext
            << "\" exceeds length of Additional Info structure" << newl;
   }
   
} // CTraceMessagePrint::PrintAdditionalInfo





/**
 * Print a B-channel Information structure.
 */

void CTraceMessagePrint::PrintBChannelInfo
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard  guard (m_ros);
   CAPIBChannelInfo_t *pInfo;
   size_t              nLenTotal;
   unsigned            uChannelMode;
   const char         *pszMode;
   const char         *pszOp;
   size_t              n;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the information element and determine its length
   pInfo = (CAPIBChannelInfo_t *) addr.get_pointer ();
   nLenTotal = (size_t) C_GET_BYTE (pInfo->bLength);
   addr += nLenTotal + 1;

   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ":";
   
   // check for empty information element
   if (nLenTotal == 0)
   {
      m_ros << "<Empty>" << newl;
      return;
   }
   m_ros << newl;
   
   // check for minimum length
   if (nLenTotal < sizeof (pInfo->wChannel))
   {
      m_ros << "ERROR: B-Channel Information structure too short" << newl;
      return;
   }
   
   // adjust the output margins according to the new indentation
   m_ros.leftmargin (m_ros.leftmargin () + 1);
   if (uValueIndent <= sizeof ("Channel Identification") + 1)
   {
      uValueIndent = sizeof ("Channel Identification") + 1;
   }
   else
   {
      --uValueIndent;
   }
   
   uChannelMode = C_GET_WORD (pInfo->wChannel);
   switch (uChannelMode)
   {
      case CAPI_BCHAN_MODE_B:            pszMode = "B-channel"; break;
      case CAPI_BCHAN_MODE_D:            pszMode = "D-channel"; break;
      case CAPI_BCHAN_MODE_NONE:         pszMode = "No channel"; break;
      case CAPI_BCHAN_MODE_LEASED_ALLOC: pszMode = "Leased line channel allocation"; break;
      case CAPI_BCHAN_MODE_CHANNEL_ID:   pszMode = "Channel id"; break;
      default:                           pszMode = "Unknown"; break;
   }
   m_ros << std::setw (uValueIndent) << "Channel mode:" << pszMode << newl;

   if (uChannelMode == CAPI_BCHAN_MODE_LEASED_ALLOC)
   {
      // check for valid length
      if (nLenTotal + 1 < sizeof (*pInfo))
      {
         m_ros << "ERROR: B-Channel Information structure too short" << newl;
         return;
      }
      
      switch (C_GET_WORD (pInfo->info.alloc.wOperation))
      {
         case 0:  pszOp = "DTE"; break;
         case 1:  pszOp = "DCE"; break;
         default: pszOp = "Unknown"; break;
      }
      m_ros << std::setw (uValueIndent) << "Operation:" << pszOp << newl;
      
      m_ros << std::setw (uValueIndent) << "Channel mask array:";
      m_ros.setf (std::ios::right | std::ios::hex,
                  std::ios::adjustfield | std::ios::basefield);
      m_ros.fill ('0');
      m_ros.rightmargin (78);
      m_ros.leftmargin (m_ros.leftmargin () + uValueIndent, true);
      m_ros << "<";
      for (n = 0; n < ARRAY_COUNT (pInfo->info.alloc.abChannelMaskArray); ++n)
      {
         if (n > 0)
         {
            m_ros << " ";
         }
         m_ros << std::setw (2)
               << C_GET_BYTE (pInfo->info.alloc.abChannelMaskArray [n]);
      }
      m_ros << newl;
   }
   else if (uChannelMode == CAPI_BCHAN_MODE_CHANNEL_ID)
   {
      CBoundPointer tmpAddr ((u_int8_t *) pInfo, nLenTotal + 1);
      tmpAddr += sizeof (pInfo->bLength) + sizeof (pInfo->wChannel);
      PrintCapiStruct (tmpAddr, "Channel id", uValueIndent);
   }
   else
   {
      if (nLenTotal > sizeof (pInfo->wChannel))
      {
         m_ros << "WARNING: B-Channel Information structure is longer than defined"
               << newl;
      }
   }
   
} // CTraceMessagePrint::PrintBChannelInfo





/**
 * Print a Sending Complete structure.
 */

void CTraceMessagePrint::PrintSendingComplete
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard     guard (m_ros);
   CAPISendingComplete_t *pInfo;
   size_t                 nLenTotal;
   const char            *psz;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the information element and determine its length
   pInfo = (CAPISendingComplete_t *) addr.get_pointer ();
   nLenTotal = (size_t) C_GET_BYTE (pInfo->bLength);
   addr += nLenTotal + 1;

   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ":";
   
   // check for empty information element
   if (nLenTotal == 0)
   {
      m_ros << "<Empty>" << newl;
      return;
   }
   
   switch (C_GET_WORD (pInfo->wMode))
   {
      case CAPI_SENDING_COMPLETE_NO:  psz = "No"; break;
      case CAPI_SENDING_COMPLETE_YES: psz = "Yes"; break;
      default:                        psz = "Unknown"; break;
   }
   m_ros << psz << newl;
   
} // CTraceMessagePrint::PrintSendingComplete





/**
 * Print a string representation of the flags in a Data-B3 message.
 */

void CTraceMessagePrint::PrintDataB3Flags
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard guard (m_ros);
   struct WordInfo
   {
      cWORD w;
   } *pInfo;
   unsigned u;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the value and read over it
   pInfo = (WordInfo *) addr.get_pointer ();
   addr += sizeof (pInfo->w);
   
   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ": ";
   m_ros.leftmargin (m_ros.leftmargin () + uValueIndent, true);
   m_ros.rightmargin (78);
   
   u = C_GET_WORD (pInfo->w);
   m_ros << "( ";
   if ((u & CAPI_DATA_B3_FLAG_QUALIFIER) != 0)
   {
      m_ros << "Qualifier ";
   }
   if ((u & CAPI_DATA_B3_FLAG_MORE_DATA) != 0)
   {
      m_ros << "More-Data ";
   }
   if ((u & CAPI_DATA_B3_FLAG_DELIVERY_CONF) != 0)
   {
      m_ros << "Delivery-Confirm ";
   }
   if ((u & CAPI_DATA_B3_FLAG_EXPEDITED) != 0)
   {
      m_ros << "Expedited-Data ";
   }
   if ((u & CAPI_DATA_B3_FLAG_FRAME_ERROR) != 0)
   {
      m_ros << "Framing-Error ";
   }
   m_ros << ")" << newl;
   if ((u & ~(CAPI_DATA_B3_FLAG_QUALIFIER |
              CAPI_DATA_B3_FLAG_MORE_DATA |
              CAPI_DATA_B3_FLAG_DELIVERY_CONF |
              CAPI_DATA_B3_FLAG_EXPEDITED |
              CAPI_DATA_B3_FLAG_FRAME_ERROR)) != 0)
   {
      m_ros << "WARNING: Unknown bits set in flags value" << newl;
   }
   
} // CTraceMessagePrint::PrintDataB3Flags





/**
 * Print a string representation of a Facility Selector.
 */

void CTraceMessagePrint::PrintFacilitySelector
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard guard (m_ros);
   struct WordInfo
   {
      cWORD w;
   } *pInfo;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the value and read over it
   pInfo = (WordInfo *) addr.get_pointer ();
   addr += sizeof (pInfo->w);
   
   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ": ";
   
   switch (C_GET_WORD (pInfo->w))
   {
      case CAPI_FACILITY_SELECTOR_HANDSET:             m_ros << "Handset"; break;
      case CAPI_FACILITY_SELECTOR_DTMF:                m_ros << "DTMF"; break;
      case CAPI_FACILITY_SELECTOR_V42BIS:              m_ros << "V.42bis"; break;
      case CAPI_FACILITY_SELECTOR_SUPPL_SERVICES:      m_ros << "Supplementary Services"; break;
      case CAPI_FACILITY_SELECTOR_POWER_MNGMNT_WAKEUP: m_ros << "Power Management Wakeup"; break;
      case CAPI_FACILITY_SELECTOR_LINE_INTERCONNECT:   m_ros << "Line Interconnect"; break;
      case CAPI_FACILITY_SELECTOR_ECHO_CANCELLATION:   m_ros << "Echo Cancellation"; break;
      default:                                         m_ros << "Unknown"; break;
   }
   m_ros << newl;

} // CTraceMessagePrint::PrintFacilitySelector





/**
 * Print the meaning of an Info Number in an Info Indication.
 *
 * XXX: The network message and information element types should be decoded
 *      according to ETS 300 102.
 */

void CTraceMessagePrint::PrintInfoNumber
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard guard (m_ros);
   struct WordInfo
   {
      cWORD w;
   } *pInfo;
   unsigned           u;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the value and read over it
   pInfo = (WordInfo *) addr.get_pointer ();
   addr += sizeof (pInfo->w);
   
   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ": ";
   m_ros.setf (std::ios::right, std::ios::adjustfield);
   
   u = C_GET_WORD (pInfo->w);
   if ((u & 0xC000) == 0x0000)
   {
      m_ros << "Information element, type 0x"
            << std::hex << std::setw (2) << std::setfill ('0') << (u & 0xFF);
   }
   else if ((u & 0xC000) == 0x4000)
   {
      m_ros << "Supplementary information, ";
      if ((u & 0xFF) == 0)
      {
         m_ros << "total charges in charge units";
      }
      else if ((u & 0xFF) == 1)
      {
         m_ros << "total charges in national currency";
      }
      else
      {
         m_ros << "unknown";
      }
   }
   else if ((u & 0xC000) == 0x8000)
   {
      m_ros << "Network message, type 0x"
            << std::hex << std::setw (2) << std::setfill ('0') << (u & 0xFF);
   }
   else if ((u & 0xC000) == 0xC000)
   {
      m_ros << "Undefined information value";
   }
   m_ros << newl;

} // CTraceMessagePrint::PrintInfoNumber





/**
 * Print a reject value.
 */

void CTraceMessagePrint::PrintRejectValue
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard guard (m_ros);
   struct WordInfo
   {
      cWORD w;
   } *pInfo;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the value and read over it
   pInfo = (WordInfo *) addr.get_pointer ();
   addr += sizeof (pInfo->w);
   
   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ": ";
   m_ros.setf (std::ios::right, std::ios::adjustfield);
   
   switch (C_GET_WORD (pInfo->w))
   {
      case CAPI_CALL_ACCEPT:                      m_ros << "Accept call"; break;
      case CAPI_CALL_IGNORE:                      m_ros << "Ignore call"; break;
      case CAPI_CALL_REJECT_NORMAL_CLEARING:      m_ros << "Normal call clearing"; break;
      case CAPI_CALL_REJECT_USER_BUSY:            m_ros << "User busy"; break;
      case CAPI_CALL_REJECT_NO_CHANNEL:           m_ros << "Requested circuit/channel not available"; break;
      case CAPI_CALL_REJECT_FACILITY:             m_ros << "Facility rejected"; break;
      case CAPI_CALL_REJECT_CHANNEL_UNACCEPTABLE: m_ros << "Channel unacceptable"; break;
      case CAPI_CALL_REJECT_INCOMPAT_DESTINATION: m_ros << "Incompatible destination"; break;
      case CAPI_CALL_REJECT_OUT_OF_ORDER:         m_ros << "Destination out of order"; break;
      default:
         m_ros << "0x" << std::hex << std::setfill ('0') << std::setw (4)
               << C_GET_WORD (pInfo->w);
         break;
   }
   m_ros << newl;

} // CTraceMessagePrint::PrintRejectValue





/**
 * Print a CAPI result / info / reason value.
 */

void CTraceMessagePrint::PrintResultValue
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   // XXX: For now we simply print out the value as a hexadecimal word. It
   //      should be decoded into a readable string.

   PrintHexWord (addr, pszPrompt, uValueIndent);
   
} // CTraceMessagePrint::PrintResultValue





/**
 * Print a QWord value in hexadecimal.
 */

void CTraceMessagePrint::PrintHexQWord
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard guard (m_ros);
   struct QWordInfo
   {
      cQWORD qw;
   } *pInfo;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the value and read over it
   pInfo = (QWordInfo *) addr.get_pointer ();
   addr += sizeof (pInfo->qw);
   
   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ": ";
   m_ros.setf (std::ios::right, std::ios::adjustfield);
   m_ros << "0x" << std::hex << std::setfill ('0') << std::setw (16)
         << C_GET_QWORD (pInfo->qw);
   m_ros << newl;

} // CTraceMessagePrint::PrintHexQWord





/**
 * Print a DWord value in hexadecimal.
 */

void CTraceMessagePrint::PrintHexDWord
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard guard (m_ros);
   struct DWordInfo
   {
      cDWORD dw;
   } *pInfo;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the value and read over it
   pInfo = (DWordInfo *) addr.get_pointer ();
   addr += sizeof (pInfo->dw);
   
   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ": ";
   m_ros.setf (std::ios::right, std::ios::adjustfield);
   m_ros << "0x" << std::hex << std::setfill ('0') << std::setw (8)
         << C_GET_DWORD (pInfo->dw);
   m_ros << newl;

} // CTraceMessagePrint::PrintHexDWord





/**
 * Print a Word value in hexadecimal.
 */

void CTraceMessagePrint::PrintHexWord
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard guard (m_ros);
   struct WordInfo
   {
      cWORD w;
   } *pInfo;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the value and read over it
   pInfo = (WordInfo *) addr.get_pointer ();
   addr += sizeof (pInfo->w);
   
   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ": ";
   m_ros.setf (std::ios::right, std::ios::adjustfield);
   m_ros << "0x" << std::hex << std::setfill ('0') << std::setw (4)
         << C_GET_DWORD (pInfo->w);
   m_ros << newl;

} // CTraceMessagePrint::PrintHexWord





/**
 * Print a Word value in decimal.
 */

void CTraceMessagePrint::PrintDecWord
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard guard (m_ros);
   struct WordInfo
   {
      cWORD w;
   } *pInfo;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the value and read over it
   pInfo = (WordInfo *) addr.get_pointer ();
   addr += sizeof (pInfo->w);
   
   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ": "
         << C_GET_DWORD (pInfo->w)
         << newl;

} // CTraceMessagePrint::PrintDecWord





/**
 * Print a CAPI struct as a string.
 */

void CTraceMessagePrint::PrintStringStruct
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard  guard (m_ros);
   u_int8_t           *p;
   size_t              nLen;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the string and determine its length
   p = addr.get_pointer ();
   nLen = (size_t) *p;
   addr += nLen + 1;
   ++p;
   
   // check for empty string
   if (nLen == 0)
   {
      m_ros.setf (std::ios::left, std::ios::adjustfield);
      m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt))
            << ": <Empty>" << newl;
      return;
   }
   
   // create a string variable with the digits
   const std::string str ((char *) p, nLen - 1);
   
   // now finally do the output
   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ": ";
   m_ros << "'" << str << "'" << newl;
   
} // CTraceMessagePrint::PrintStringStruct





/**
 * Print a CAPI struct as a byte sequence.
 */

void CTraceMessagePrint::PrintCapiStruct
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard   guard (m_ros);
   const unsigned char *p;
   size_t               nLen;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the CAPI struct and determine its length
   p = (const unsigned char *) addr.get_pointer ();
   nLen = (size_t) *p;
   addr += nLen + 1;
   ++p;
   
   // print out the prompt
   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ": ";
   m_ros.setf (std::ios::right, std::ios::adjustfield);
   m_ros.leftmargin (m_ros.leftmargin () + uValueIndent, true);
   m_ros.rightmargin (78);

   // print the CAPI structure as a byte sequence
   m_ros.byte_sequence (p, nLen);

} // CTraceMessagePrint::PrintCapiStruct





/**
 * Print the rest of a message as a byte sequence.
 */

void CTraceMessagePrint::PrintRestAsByteSequence
   (CBoundPointer &addr,
    const char    *pszPrompt,
    unsigned       uValueIndent)
{
   CStreamConfigGuard   guard (m_ros);
   const unsigned char *p;
   size_t               nLen;
   
   // for security assure pszPrompt points to a valid string
   if (! pszPrompt)
   {
      pszPrompt = "";
   }
   
   // address the remaining bytes and determine its length
   p = (const unsigned char *) addr.get_pointer ();
   nLen = addr.bytes_remaining ();
   addr += nLen;
   
   // print out the prompt
   m_ros.setf (std::ios::left, std::ios::adjustfield);
   m_ros << pszPrompt << std::setw (uValueIndent - strlen (pszPrompt)) << ": ";
   m_ros.setf (std::ios::right, std::ios::adjustfield);
   m_ros.leftmargin (m_ros.leftmargin () + uValueIndent, true);
   m_ros.rightmargin (78);

   // print the CAPI structure as a byte sequence
   m_ros.byte_sequence (p, nLen);

} // CTraceMessagePrint::PrintRestAsByteSequence





/**
 * Translate a CAPI message command into a string.
 */

const char *CTraceMessagePrint::GetCapiMsgName
   (unsigned uCmd)
{
   static std::string  str;
   const char         *pszCmd;
   const char         *pszSubCmd;
   
   switch (uCmd & CAPI_CMDMASK_COMMAND)
   {
      case C_ALERT:                     pszCmd = "ALERT"; break;
      case C_CONNECT:                   pszCmd = "CONNECT"; break;
      case C_CONNECT_ACTIVE:            pszCmd = "CONNECT ACTIVE"; break;
      case C_DISCONNECT:                pszCmd = "DISCONNECT"; break;
      case C_LISTEN:                    pszCmd = "LISTEN"; break;
      case C_INFO_20:                   pszCmd = "INFO"; break;
      case C_SELECT_B_PROTOCOL:         pszCmd = "SELECT B PROTOCOL"; break;
      case C_FACILITY:                  pszCmd = "FACILITY"; break;
      case C_CONNECT_B3:                pszCmd = "CONNECT B3"; break;
      case C_CONNECT_B3_ACTIVE:         pszCmd = "CONNECT B3 ACTIVE"; break;
      case C_DISCONNECT_B3:             pszCmd = "DISCONNECT B3"; break;
      case C_DATA_B3:                   pszCmd = "DATA B3"; break;
      case C_RESET_B3_20:               pszCmd = "RESET B3"; break;
      case C_CONNECT_B3_T90_ACTIVE:     pszCmd = "CONNECT B3 T90 ACTIVE"; break;
      case C_MANUFACTURER:              pszCmd = "MANUFACTURER"; break;

      default:
         pszCmd = "Unknown command";
         break;
   }
   
   switch (uCmd & CAPI_CMDMASK_SUBCMD)
   {
      case C_REQ:                       pszSubCmd = " REQUEST"; break;
      case C_CONF:                      pszSubCmd = " CONFIRMATION"; break;
      case C_IND:                       pszSubCmd = " INDICATION"; break;
      case C_RESP:                      pszSubCmd = " RESPONSE"; break;
      
      default:
         pszSubCmd = " unknown sub command";
         break;
   }
   
   str = std::string (pszCmd) + pszSubCmd;
   
   return (str.c_str ());
} // CTraceMessagePrint::GetCapiMsgName





/**
 * Translate a B1-Protocol value into a string.
 */

const char *CTraceMessagePrint::GetB1ProtocolName
   (unsigned uB1Protocol)
{
   switch (uB1Protocol)
   {
      case CAPI_B1_HDLC_64:
         return ("64 kbit/s with HDLC framing");
         
      case CAPI_B1_TRANSPARENT_64:
         return ("64 kbit/s bit-transparent operation");
         
      case CAPI_B1_V110_ASYNCH:
         return ("V.110 asynchronous operation with start/stop byte framing");
         
      case CAPI_B1_V110_SYNCH_HDLC:
         return ("V.110 synchronous operation with HDLC framing");
         
      case CAPI_B1_T30_MODEM:
         return ("T.30 modem for Grou 3 fax");
         
      case CAPI_B1_HDLC_64_INVERTED:
         return ("64 kbit/s inverted with HDLC framing");
         
      case CAPI_B1_TRANSPARENT_56:
         return ("56 kbit/s bit-transparent operation");
         
      case CAPI_B1_MODEM_FULL:
         return ("Modem with full negotiation");
         
      case CAPI_B1_MODEM_ASYNCH:
         return ("Modem asynchronous operation with start/stop byte framing");
         
      case CAPI_B1_MODEM_SYNCH_HDLC:
         return ("Modem synchronous operation with HDLC framing");
         
      default:
         return ("Unknown protocol");
   }
   
} // CTraceMessagePrint::GetB1ProtocolName





/**
 * Translate a B2-Protocol value into a string.
 */

const char *CTraceMessagePrint::GetB2ProtocolName
   (unsigned uB2Protocol)
{
   switch (uB2Protocol)
   {
      case CAPI_B2_ISO7776_X75_SLP:
         return ("ISO 7776 (X.75 SLP)");
         
      case CAPI_B2_TRANSPARENT:
         return ("Transparent");
         
      case CAPI_B2_SDLC:
         return ("SDLC");
         
      case CAPI_B2_LAPD_X25:
         return ("LAPD in accordance with Q.921 for D-channel X.25 (SAPI 16)");
         
      case CAPI_B2_T30_FAX_G3:
         return ("T.30 for Group 3 fax");
         
      case CAPI_B2_PPP:
         return ("Point-to-Point Protocol (PPP)");
         
      case CAPI_B2_TRANSPARENT_NO_B1_ERR:
         return ("Transparent (ignoring framing errors of B1 protocol)");
         
      case CAPI_B2_MODEM_FULL:
         return ("Modem with full negotiation");
         
      case CAPI_B2_ISO7776_X75_SLP_COMP:
         return ("ISO 7776 (X.75 SLP) modified to support V.42bis compression");
         
      case CAPI_B2_V120_ASYNCH:
         return ("V.120 asynchronous mode");
         
      case CAPI_B2_V120_ASYNCH_COMP:
         return ("V.120 asynchronous mode with V.42bis compression");
         
      case CAPI_B2_V120_TRANSPARENT:
         return ("V.120 bit-transparent mode");

      case CAPI_B2_LAPD_FREE_SAPI:
         return ("LAPD in accordance with Q.921 including free SAPI selection");
         
      default:
         return ("Unknown protocol");
   }
   
} // CTraceMessagePrint::GetB2ProtocolName





/**
 * Translate a B3-Protocol value into a string.
 */

const char *CTraceMessagePrint::GetB3ProtocolName
   (unsigned uB3Protocol)
{
   switch (uB3Protocol)
   {
      case CAPI_B3_TRANSPARENT:
         return ("Transparent");
         
      case CAPI_B3_T90NL:
         return ("T.90NL with compatibility to T.70NL");
         
      case CAPI_B3_ISO8208:
         return ("ISO 8208 (X.25 DTE-DTE)");
         
      case CAPI_B3_X25_DCE:
         return ("X.25 DCE");
         
      case CAPI_B3_T30_FAX_G3:
         return ("T.30 for Group 3 fax");
         
      case CAPI_B3_T30_FAX_G3_EXT:
         return ("T.30 for Group 3 fax extended");
         
      case CAPI_B3_MODEM:
         return ("Modem");
         
      default:
         return ("Unknown protocol");
   }
   
} // CTraceMessagePrint::GetB3ProtocolName





// === Implementation of public functions ================================





// === Implementation of private functions ===============================





/**
 * Stream output function for a struct timeval.
 */

static std::ostream & operator<<
   (std::ostream         &os,
    const struct timeval &tv)
{
   time_t    t;
   struct tm tm;
   char      szBuf [32];
   size_t    nLen;
   
   t = (time_t) tv.tv_sec;
   memset (&tm, 0, sizeof (tm));
   (void) localtime_r (&t, &tm);

   strftime (szBuf, sizeof (szBuf), "%H:%M:%S", &tm);
   nLen = strlen (szBuf);
   snprintf (szBuf + nLen, sizeof (szBuf) - nLen, ".%03u",
             (unsigned) (tv.tv_usec / 1000));

   os << szBuf;

   return (os);
} // operator<<





/**
 * Stream output function for a pointer stored in two dwords.
 */

static std::ostream & operator<<
   (std::ostream           &os,
    const CTwoDwordPointer &tdwp)
{
   // in all cases the output is switched to hexadezimal
   os << "0x" << std::hex << std::setfill ('0');
   
   // if the high dword is null, the pointer is assumed to be a 32bit pointer
   if (tdwp.m_dwHigh == 0)
   {
      os << std::setw (8) << tdwp.m_dwLow;
   }
   else
   {
      os << std::setw (16)
         << (((u_int64_t) tdwp.m_dwHigh << 32) | (u_int64_t) tdwp.m_dwLow);
   }
   
   os << std::dec << std::setfill (' ');
   
} // operator<<
