/**
 * @file config.cc
 *
 * Config - Handling of configuration data.
 *
 * Copyright: 2003-2005 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: config.cc,v 1.2.2.1 2006/02/28 21:35:46 thomas Exp $
 * $Project:    CAPI for BSD $
 * $Target:     Diehl legacy active ISDN boards control program $
 * @date        09.03.2003
 * @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$");

// Own interface
#include "config.h"

// System includes
#include <string>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <stdexcept>            // std::runtime_error

// Import includes

#define __CONFIG__

// Local includes





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





// --- Default configuration settings ---

/// The default program name if not (yet) set through the command line.
static const std::string DEFAULT_PROG_NAME = "daicctl";

/// The default file name suffix for backup files.
static const std::string DEFAULT_BACKUP_SUFFIX = "~";

/// The driver name for Diehl active ISDN boards.
static const std::string DAIC_DRIVER_NAME = "daic";

/// The default base directory for boot code and firmware files.
static const std::string DAIC_DEFAULT_FW_BASE_DIRECTORY =
   "/usr/share/capi/daic";

/// The default configuration file name.
static const std::string DAIC_DEFAULT_CONFIG_FILE_NAME =
   "/etc/daic.cfg";





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





// === Prototypes for private functions ==================================





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





// --- Class for a command line option with a scalar value without an argument ---

/**
 * Constructor.
 *
 * @param cmdLineEvaluator
 *                        I: The command line evaluator object this option
 *                           object will be a member of. It is forwarded
 *                           to the constructor of the base class.
 * @param pszName         I: The name of the option.
 * @param iFlags          I: A bitmask of flags controlling the behaviour
 *                           of this class and of the command line
 *                           evaluator. It is a combination of the values
 *                           of the enumeration Flags_t.
 * @param ptValueTarget   I: The address of a memory location to copy the
 *                           current option value to. If this feature is
 *                           not needed, this parameter may be NULL.
 * @param tNotFoundValue  I: The value for the option if it is found on
 *                           the command line. With this parameter You
 *                           can query the option value without prior
 *                           checking if the option is found.
 * @param tFoundValue     I: The value for the option if it is found on
 *                           the command line.
 */

template<class T>
CValNoArgCmdOpt<T>::CValNoArgCmdOpt
   (thwutil::CCmdLineEvaluator &cmdLineEvaluator,
    const char                 *pszName,
    int                         iFlags,
    T                          *ptValueTarget,
    T                           tNotFoundValue,
    T                           tFoundValue):
   thwutil::CCmdOptBase (cmdLineEvaluator, pszName, iFlags),
   m_tNotFoundValue (tNotFoundValue),
   m_tFoundValue (tFoundValue),
   m_ptValueTarget (ptValueTarget)
{
   // nothing more to do
   
} // CValNoArgCmdOpt<T>::CValNoArgCmdOpt





/**
 * Destructor.
 */

template<class T>
CValNoArgCmdOpt<T>::~CValNoArgCmdOpt (void)
{
   // nothing to do
   
} // CValNoArgCmdOpt<T>::~CValNoArgCmdOpt





/**
 * Copy the current value to the internally managed address.
 *
 * This function simply copies the current option value to the memory
 * location specified in the constructor call. If it was given as the NULL
 * pointer, no copy operation will take place.
 *
 * @param None.
 *
 * @retval None.
 */

template<class T>
void CValNoArgCmdOpt<T>::CopyValue (void)
{
   if (m_ptValueTarget != NULL)
   {
      *m_ptValueTarget = GetValue ();
   }
   
} // CValNoArgCmdOpt<T>::CopyValue





/**
 * Get a string representation of the current value.
 *
 * This function returns the current value of the member strings
 * m_strPosValue and m_strNegValue respectively, according to the current
 * option value. The two strings may be specified in the constructor or
 * set later by using SetPositiveString() or SetNegativeString().
 *
 * @param None.
 *
 * @result A string representing the current (parameter) value of the
 *         option.
 */

template<class T>
std::string CValNoArgCmdOpt<T>::GetValueString (void)
{
   std::stringstream oss;
   if ((m_iFlags & UINT_HEX_OUTPUT) != 0)
   {
      oss << std::hex << std::setfill ('0') << std::setw (sizeof (T) * 2);
   }
   oss << GetValue ();
   return (oss.str ());
} // CValNoArgCmdOpt<T>::GetValueString





/**
 * Get the current value of the option.
 */

template<class T>
T CValNoArgCmdOpt<T>::GetValue (void)
{
   return (WasFound () ? m_tFoundValue : m_tNotFoundValue);
} // CValNoArgCmdOpt<T>::GetValue





// --- Class for configuration data handling -----------------------------

/**
 * Constructor.
 */

CConfig::CConfig (void):
   m_strProgName (DEFAULT_PROG_NAME),
   m_cmdLineEvaluator (),
   m_cmdOptTaskList
      (m_cmdLineEvaluator,
       "l", 0,
       &m_task, CConfig::TASK_UNKNOWN, CConfig::TASK_LIST),
   m_cmdOptTaskInit
      (m_cmdLineEvaluator,
       "i", 0,
       &m_task, CConfig::TASK_UNKNOWN, CConfig::TASK_INIT),
   m_cmdOptTaskInitForce
      (m_cmdLineEvaluator,
       "I", 0,
       &m_task, CConfig::TASK_UNKNOWN, CConfig::TASK_INIT_FORCE),
   m_cmdOptTaskUpdate
      (m_cmdLineEvaluator,
       "u", 0,
       &m_task, CConfig::TASK_UNKNOWN, CConfig::TASK_UPDATE),
   m_cmdOptTaskUpdateRemove
      (m_cmdLineEvaluator,
       "U", 0,
       &m_task, CConfig::TASK_UNKNOWN, CConfig::TASK_UPDATE_REMOVE),
   m_cmdOptTaskReset
      (m_cmdLineEvaluator,
       "r", 0,
       &m_task, CConfig::TASK_UNKNOWN, CConfig::TASK_RESET),
   m_cmdOptTaskLoad
      (m_cmdLineEvaluator,
       "d", 0,
       &m_task, CConfig::TASK_UNKNOWN, CConfig::TASK_LOAD),
   m_cmdOptQuiet
      (m_cmdLineEvaluator,
       "q", 0,
       &m_fQuiet),
   m_cmdOptCtlrName
      (m_cmdLineEvaluator,
       "n",
       thwutil::CCmdOptBase::HAS_PARAMETER,
       &m_strCtlrName),
   m_cmdOptCtlrNum
      (m_cmdLineEvaluator,
       "c",
       thwutil::CCmdOptBase::HAS_PARAMETER,
       &m_uCtlrNum, 0, 1, 127),
   m_cmdOptConfigFileName
      (m_cmdLineEvaluator,
       "f",
       thwutil::CCmdOptBase::HAS_PARAMETER,
       &m_strConfigFileName),
   m_cmdOptBaseDir
      (m_cmdLineEvaluator,
       "b",
       thwutil::CCmdOptBase::HAS_PARAMETER,
       &m_strBaseDir),
   m_cmdOptHelp1
      (m_cmdLineEvaluator,
       "h",
       thwutil::CCmdOptBase::TERMINATE_EVALUATION,
       &m_task, CConfig::TASK_UNKNOWN, CConfig::TASK_USAGE),
   m_cmdOptHelp2
      (m_cmdLineEvaluator,
       "?",
       thwutil::CCmdOptBase::TERMINATE_EVALUATION,
       &m_task, CConfig::TASK_UNKNOWN, CConfig::TASK_USAGE),
   m_task (CConfig::TASK_LOAD),
   m_strCtlrName (),
   m_uCtlrNum (0),
   m_fQuiet (false),
   m_strBaseDir (DAIC_DEFAULT_FW_BASE_DIRECTORY),
   m_strConfigFileName (DAIC_DEFAULT_CONFIG_FILE_NAME)
{
   // nothing more to do
   
} // CConfig::CConfig





/**
 * Destructor.
 */

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





/**
 * Perform general initialization.
 *
 * As this class is designed to create a static object, the constructor
 * will not get enough information for initializing the object. This will
 * thus be done by calling this member function.
 *
 * The main task is to set all internal member variables to reasonable
 * defaults. In addition the short program name is determined from the
 * argument specified. It is expected to be a file name, possibly
 * including some path specification. The basename of this file name will
 * be set as the program name.
 *
 * @param pszProgFileName I: The file name of the current program, maybe
 *                           including a path specification.
 *
 * @retval None.
 *
 * @throws std::invalid_argument
 *                        The argument is the NULL pointer or the empty
 *                        string.
 */

void CConfig::Init
   (const char *pszProgFileName)
{
   const char *p;
   
   p = strrchr (pszProgFileName, '/');
   if (p)
   {
      m_strProgName = std::string (p + 1);
   }
   else
   {
      m_strProgName = std::string (pszProgFileName);
   }
   
} // CConfig::Init





/**
 * Get the program name.
 */

const char *CConfig::GetProgName (void)
{
   return (m_strProgName.c_str ());
} // CConfig::GetProgName





/**
 * Print usage information.
 */

void CConfig::Usage (void)
{
   std::cout << std::endl;
   std::cout << m_strProgName
             << " - Download firmware and configuration to Diehl active ISDN boards"
             << std::endl;
   std::cout << std::endl;
   std::cout << "Usage:" << std::endl << std::endl;
   std::cout << m_strProgName << " -l" << std::endl;
   std::cout << "\tList available controllers" << std::endl;
   std::cout << std::endl;
   std::cout << m_strProgName
             << " {-i|-I} [-q] [-b <firmware base directory>] [-f <config file name>]"
             << std::endl;
   std::cout << "\tCreate an initial configuration file for available boards."
             << std::endl;
   std::cout << "\t\"-I\" will overwrite an existing file."
             << std::endl << std::endl;
   std::cout << m_strProgName
             << " {-u|-U} [-q] [-b <firmware base directory] [-f <config file name>]"
             << std::endl;
   std::cout << "\tUpdate an existing configuration file. \"-U\" will remove board"
             << std::endl;
   std::cout << "\tentries that do not exist (any more)."
             << std::endl << std::endl;
   std::cout << m_strProgName
             << " -r [-q] [{-c#|-n<name>}] [-f <config file name>]"
             << std::endl;
   std::cout << "\tPerform a reset operation, i.e. disable all boards or only one board."
             << std::endl << std::endl;
   std::cout << m_strProgName
             << " [-d] [-q] [{-c#|-n<name>}] [-f <config file name>]"
             << std::endl;
   std::cout << "\tPerform a download operation for all boards or only one board"
             << std::endl;
   std::cout << "\t(default operation)."
             << std::endl << std::endl;
   std::cout << m_strProgName << " {-h | -?}" << std::endl;
   std::cout << "\tPrint this help text." << std::endl;
   std::cout << std::endl;
   
   std::cout << "Option meanings:" << std::endl << std::endl;
   std::cout << "   -b<base directory>" << std::endl;
   std::cout << "\tUse a different directory for the firmware files. The default"
             << std::endl;
   std::cout << "\tis \"/usr/share/capi/daic\"."
             << std::endl << std::endl;
   std::cout << "   -c#\tSpecify the controller number to download or reset."
             << std::endl;
   std::cout << "\tIf not specified, the reset or download will be performed"
             << std::endl;
   std::cout << "\tfor all Diehl boards."
             << std::endl << std::endl;
   std::cout << "   -f<config file name>" << std::endl;
   std::cout << "\tUse a different path name for the configuration file. The"
             << std::endl;
   std::cout << "\tdefault is \"/etc/daic.cfg\"."
             << std::endl << std::endl;
   std::cout << "   -n<name>" << std::endl;
   std::cout << "\tSpecify the controller name to download or reset. If not"
             << std::endl;
   std::cout << "\tspecified, the reset or download will be performed for all"
             << std::endl;
   std::cout << "\tboards."
             << std::endl << std::endl;
   std::cout << "   -q\tSuppress normal output. Only error message will be printed."
             << std::endl << std::endl;

} // CConfig::Usage





/**
 * Evaluate command line parameters.
 */

void CConfig::EvalCommandLine
   (int    iArgc,
    char **papszArgv)
{
   thwutil::CCmdLineEvaluator::CmdLineEvalResult_t resEval;
   int                                             iEndIdx;
   
   // evaluate the command line through the command line evaluator object
   resEval = m_cmdLineEvaluator.EvalCommandLine
                (iArgc, papszArgv, 1, &iEndIdx);
   if (resEval != thwutil::CCmdLineEvaluator::CLE_OK &&
       resEval != thwutil::CCmdLineEvaluator::CLE_EVALUATION_TERMINATED)
   {
      // all values except O.K. and termination are real errors
      m_task = TASK_ERROR;
      std::ostringstream oss;
      oss << "Error in command line: "
          << m_cmdLineEvaluator.ResultMsg (resEval);
      throw std::runtime_error (oss.str ().c_str ());
   }
   
   // if there is still a command line argument remaining, there is an error
   // (all parameters must be switches with arguments)
   if (resEval != thwutil::CCmdLineEvaluator::CLE_EVALUATION_TERMINATED &&
       iEndIdx < iArgc)
   {
      m_task = TASK_ERROR;
      std::ostringstream oss;
      oss << "Error in command line: Unsupported parameter \""
          << papszArgv [iEndIdx] << "\"";
      throw std::runtime_error (oss.str ().c_str ());
   }
   
   // now transfer the information of all options found to their propper place
   m_cmdLineEvaluator.TakeCommandLineOptions (NULL);
   
} // CConfig::EvalCommandLine





/**
 * Get the task selected through the command line parameters.
 */

CConfig::Task_t CConfig::GetTask (void)
{
   return (m_task);
} // CConfig::GetTask





/**
 * Get the name of the daic board driver.
 */

const std::string &CConfig::GetDriverName (void)
{
   return (DAIC_DRIVER_NAME);
} // CConfig::GetDriverName





/**
 * Get the base directory for download files.
 */

const std::string &CConfig::GetBaseDirectory (void)
{
   return (m_strBaseDir);
} // CConfig::GetBaseDirectory





/**
 * Get the file suffix for backup files.
 */

const std::string &CConfig::GetBackupSuffix (void)
{
   return (DEFAULT_BACKUP_SUFFIX);
} // CConfig::GetBackupSuffix





/**
 * Get the setting for quiet operation.
 */

bool CConfig::Quiet (void)
{
   return (m_fQuiet);
} // CConfig::Quiet





/**
 * Get the name for the configuration file.
 */

const std::string CConfig::GetConfigFileName (void)
{
   return (m_strConfigFileName);
} // CConfig::GetConfigFileName





/**
 * Get the number of a specifically set controller number to handle.
 */

unsigned CConfig::GetControllerNumber (void)
{
   return (m_uCtlrNum);
} // CConfig::GetControllerNumber





/**
 * Get the name of a specifically set controller name to handle.
 */

const std::string &CConfig::GetControllerName (void)
{
   return (m_strCtlrName);
} // CConfig::GetControllerName





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





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