/************************************************************************/
/*                                                                      */
/*  FILE:       parameters.c                                            */
/*                                                                      */
/*  PURPOSE:    get parameters needed by the "Mk 4 Server Deamon".      */
/*              Uses these sources:                                     */
/*              1) Built-in defaults                                    */
/*              2) Command line arguments                               */
/*              3) A file called "mk4d.conf"                            */
/*                                                                      */
/*  AUTHOR:     Chris Albertson, Agust 1998                             */
/*                                                                      */
/*  HISTORY:    Agust 1998 -- Started work                              */
/*                                                                      */
/************************************************************************/
/*                                                                      */
/*                  Copyright (C) 1998 Chris Albertson.                 */
/*                                                                      */
/*   This program is free software; you can redistribute it and/or      */
/*   modify it under the terms of the GNU General Public License as     */ 
/*   published by the Free Software Foundation; either version 2, or    */ 
/*   (at your option) any later version.                                */
/*                                                                      */
/*   This program is distributed in the hope that it will be useful,    */
/*   but WITHOUT ANY WARRANTY; without even the implied warranty of     */
/*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the      */
/*   GNU General Public License for more details.                       */
/*                                                                      */
/*   You should have received a copy of the GNU General Public License  */
/*   along with this program; see the file COPYING.  If not, write to   */
/*   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,   */ 
/*   USA.                                                               */
/*                                                                      */
/************************************************************************/

/* Used by RCS and ident */
char parameters_rcsid[] = 
    "$Id: parameters.c,v 1.6 1998/09/29 06:44:33 chris Exp $";
    
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <values.h>
#include <math.h>

#include "parameters.h"
#include "MLog.h"
#include "shhopt.h"

/* This is the cycle period.  Actually the IO timeout for socket data   */
/* Set it to a large value 1.0 for low CPU utilization or to something  */
/* small 0.1 for better time resolution in task scheduling.  A  good    */
/* starting place is 0.5                                                */
#define MK4D_PERIOD 0.5


/* Prototypes for local functions */
int ProcArgs(int argc, char *argv[]);
int Keyword_Assignment(char token1[], char token2[], char FailureStr[]);
FILE* Open_config_file(char conf_pathname[]);
void PrintParm(void);

/* Not local but not written functions */
int process_message(char msg_buffer[], int sd);
void ServerUsage(void);
void ServerVersion(void);


/************************************************************************/
/************************************************************************/
/*                                                                      */
/*            HOW TO ADD A NEW GLOBAL PARAMETER TO MK4D                 */
/*                                                                      */
/* To add a new global parameter do the following:                      */
/*  1) Add a line to the GLOBAL PARAMETERS TABLE below                  */
/*  2) Add a line just like it but with "extern" prepended to the       */
/*     GLOBAL PARAMETERS TABLE in parameters.h                          */
/*  3) Add a new entry to the KEYWORD TABLE                             */
/*                                                                      */
/* That's it. you should NOT need to modify any of the fuctions in      */
/* this file.  After you modify these two tables and recomplite users   */
/* can then set the parameter by putting commands in the mk4d.conf      */
/* file or by clients sending SET commands                              */
/*                                                                      */
/************************************************************************/


/************************************************************************/
/************************************************************************/
/*                                                                      */
/*                     GLOBAL PARAMETERS TABLE                          */
/*                                                                      */
/************************************************************************/
/*                                                                      */
/************************************************************************/
/* The following my be set via the command line argument list           */
/************************************************************************/
int     gbl_port        = -1;       /* -1 = unspectified                */
int     gbl_syslog      =  0;       /*  0 = don't use syslog            */
int     gbl_msg_level   =  0;       /*  0 = minimal log messages        */
char    gbl_ram_device[256] = "/dev/mkIV0";     /* first ram card       */

/************************************************************************/
/* The following may be set from the config file or from a client "set" */
/* command at any time.                                                 */
/************************************************************************/
char    gbl_sitename[STRSZ]     =   "X";    /* Site X for eXperimental  */
char    gbl_observer[STRSZ]     =   "I. B. Anonymous";
char    gbl_observatory[STRSZ]  =   "No Name Observatory";

double  gbl_latitude    =    0.00000;    /* Latitude in decimal deg     */
double  gbl_longitude   =    0.00000;    /* Longitude in decimal deg    */
double  gbl_altitude    =    0.0;        /* Altitude in meters MSL      */

int     gbl_number_ccds =    1;          /* Number of CCDS, 1 or 2      */
int     gbl_CCD_Xsize   = 2000;          /* Sixe of CCD in X            */
int     gbl_CCD_Ysize   = 2000;          /* Sixe of CCD in Y            */

/* Address of a IRAF display server.See the CDL reference guide for     */
/* syntax                                                               */
char    gbl_display_server[STRSZ] = "fifo:/dev/imt1i:/dev/imt1o";

/* Name of fits file read as a test image by the "read fits" command    */
char    gbl_fits_testimage[STRSZ] = "testimage.fits";
/************************************************************************/
/*                     End of global parameters                         */
/************************************************************************/
/************************************************************************/




/************************************************************************/
/************************************************************************/
/*                         KEYWORD TABLE                                */
/************************************************************************/
#define KW_INT 1
#define KW_DBL 2
#define KW_STR 3

typedef struct{
    char    keyword[STRSZ];
    char    helpstr[80];
    int     type;
    void   *value;
    double  min_limit;      /* Limits are always doubles.  Even for int */
    double  max_limit;      /* parameters.                              */
    } keywordstru_typ;
    
static keywordstru_typ   KeyWordTable[] = {
    {"sitename",
     "Lettercode for site",
     KW_STR,
     gbl_sitename,
     0.0,
     0.0},
     
    {"observer",
     "Full name of observer",
     KW_STR,
     gbl_observer,
     0.0,
     0.0},
     
    {"observatory",
     "Name of observatory",
     KW_STR,
     gbl_observatory,
     0.0,
     0.0},
     
    {"latitude",
     "Latitude in decimal degrees",
     KW_DBL,
     &gbl_latitude,
     -80.0,
     80.0},
     
    {"longitude",
     "Longitude in decimal degrees",
     KW_DBL,
     &gbl_longitude,
     -180.0,
     180.0},
     
    {"altitude",
     "Altitude in meters MSL",
     KW_DBL,
     &gbl_altitude,
     0.0,
     10000.0},
     
    {"number_ccds",
     "Number of CCDS, 1 or 2",
     KW_INT,
     &gbl_number_ccds,
     1.0,
     2.0},
     
    {"CCD_Xsize",
     "Size of CCD in X direction",
     KW_INT,
     &gbl_CCD_Xsize,
     100.0,
     2200.0},
     
    {"CCD_Ysize",
     "Size of CCD in Y direction",
     KW_INT,
     &gbl_CCD_Ysize,
     100.0,
     2200.0},
     
    {"display_server",
     "Address of IRAF display server",
     KW_STR,
     gbl_display_server,
     0.0,
     0.0},
     
    {"fits_testimage",
     "Filename of FITS test image",
     KW_STR,
     gbl_fits_testimage,
     0.0,
     0.0},
    
    /* last entry */
    {"", "", 0, NULL} };
/************************************************************************/
/*                       End Keyword table                              */
/************************************************************************/
/************************************************************************/
 
    

/************************************************************************/
/*									*/
/* Main program used for testing                                        */
/*                                                                      */
/************************************************************************/
#if 0
void main(int argc, char *argv[])
{
    printf("Entered main\n");
    ProcArgs(argc, argv);
    
    PrintParm();
    ProcConf();
    PrintParm();
    
    printf("Existing main\n");
    exit(0);
}
#endif

/************************************************************************/
/*									*/
/*  RETURNS:                                                            */
/*      0    it no Error                                                */
/*      < 0  if there is an error                                       */
/*									*/
/************************************************************************/
int ProcArgs(int argc, char *argv[])
{  
    int     arg_port;
    int     arg_verbose;
    char   *arg_ram_dev_ptr;
    int     arg_debug;
    int     arg_syslog;
    int     msg_level;
    
    /********************************************************************/
    /* optStruct descibes the optional arguments accepted by this       */
    /* program.  "Long" parameter names are accepted.  For example      */
    /* either -h or --help will cause the usage mesage to be printed    */
    /********************************************************************/
    optStruct opt[] = {
      /* short long           type        var/func          special     */
        {'H', "help",        OPT_FLAG,   ServerUsage,      OPT_CALLFUNC},
        {'V', "version",     OPT_FLAG,   ServerVersion,    OPT_CALLFUNC},
        {'r', "ramcard",     OPT_STRING, &arg_ram_dev_ptr, 0           },
        {'l', "syslog",      OPT_FLAG,   &arg_syslog,      0           },  
        {'p', "port",        OPT_INT,    &arg_port,        0           },       
        {'v', "verbose",     OPT_FLAG,   &arg_verbose,     0           },        
        {'d', "debug",       OPT_FLAG,   &arg_debug,       0           },
        {0, 0, OPT_END, 0, 0 }  /* no more options */
    };
          
    /********************************************************************/
    /* Set default values then let arg list override.                   */
    /*                                                                  */
    /* If optParseOptions finds errors in the command line argument     */
    /* It will print an imformative message and abort the program       */
    /*									*/
    /* Next, set the "verboseity" level, print a sign-on message and	*/
    /* Set up an empty client table.					*/
    /*									*/
    /* Finaly, we create a socket, listen on it and connect up with any	*/
    /* clients that have been waiting for us to start up.  After this	*/
    /* we can start the "main loop."					*/
    /********************************************************************/
    arg_port	= -1; /* -1 = unspectified */
    arg_verbose =  0;
    arg_debug	=  0;
    arg_syslog	=  0;
    
    optParseOptions(&argc, argv, opt, 0);
    
    msg_level = 0;
    if (arg_verbose) msg_level = 1;
    if (arg_debug)   msg_level = 2;
    
    ml_setup(msg_level, NULL, NULL);
    
    if (arg_syslog)
    {	
    	ml_use_syslog("Mk4d");
    }
    
    if(arg_ram_dev_ptr)
    {
        strncpy(gbl_ram_device, arg_ram_dev_ptr, sizeof(gbl_ram_device));
    }
    
    
    gbl_port = arg_port;
    return (RTN_OK);
}


int ProcConf(void)
{
    FILE   *ConfigFile; 
    char    conf_line[200];
    char    buffer[200];
    char   *tok;
    char    token1[STRSZ];
    char    token2[STRSZ];
    int     res;
    
        
    /********************************************************************/
    /* Open the parameter file, or try to.  Not finding a file to       */
    /* read is OK.  We will just print out a mandatory warning and      */
    /* continue on using the defaults.                                  */ 
    /********************************************************************/
    
    if(!(ConfigFile = Open_config_file(NULL)))
    {
        /* No config file found */
        ml_print (ML_MANDATORY, ML_ERROR,
            "\n         WARNING -*- WARNING -*- WARNING\n");
        ml_print (ML_MANDATORY, ML_ERROR,
            "WARNING: Configuration file not opened.  Using defaults\n");
        ml_print (ML_MANDATORY, ML_ERROR,
            "         This is likely NOT what you wanted.\n");
        ml_printf(ML_MANDATORY, ML_ERROR,
            "         File:%s Line:%d\n\n", __FILE__, __LINE__);
    }
    else
    {
        /* Config file is open now read it. */
        while(fgets(conf_line, sizeof(conf_line), ConfigFile))
        {
            /* Got a line.  Check to see if it is zero lenght or comment    */
            if ( !((0 == strlen(conf_line))   ||
                   ('#' == conf_line[0])      ||
                   (';' == conf_line[0])      ||
                   ('!' == conf_line[0])
                  )
               )
            {     
                /* The string is >0 long and not a comment, it still could      */
                /* white space.  Pull off first token.  If no first token       */
                /* then the string must be whitespace. so break.                */
                strncpy(buffer, conf_line, sizeof(buffer));
                tok = strtok(buffer, " \t=:(");
                if (tok)
                {
                    strncpy(token1, tok, sizeof(token1));

                    /* Extract second token from string.  Note that spaces ARE  */
                    /* an allowed character.  Names must may contain spaces.    */
                    /* The field is terminated by either a comment character or */
                    /* the end of the line                                      */
                    tok = strtok(NULL, "#;\t\n");
                    if (!tok)
                    {
                        /* No second token found.  Syntax error */
                        ml_print (ML_MANDATORY, ML_ERROR,
                            "ERROR: invalid line in config file.  Missing argument\n");
                        ml_printf(ML_MANDATORY, ML_ERROR,
                            "       %s\n", conf_line);
                    } 
                    else
                    {
                        /* there is a non-null token */
                        strncpy(token2, tok, sizeof(token2));
                        /* At this point token1 is the keyword                  */
                        /*               token2 is the value to be assigned     */ 
                        /* We need to lookup the keyword in the keyword table   */
                        /* and perform the asignment.                           */
                        if (RTN_ERR == Keyword_Assignment(token1, token2, NULL))
                        {
                            /* ERROR.  Couldnot perform asignment */
                            ml_print(ML_MANDATORY, ML_ERROR,
                                "ERROR: could not perform asignment\n");
                        }
                    }
                } /*if (tok)*/
            }
        }
        
        /* end of file reached.  Close the fiie. */
        res = fclose(ConfigFile);
        if (res != 0)
        {
            /* Error closing file, no big deal */
            ml_print(ML_MANDATORY, ML_ERROR,
                "WARNING: error while closing configuration file\n");
            ml_printf(ML_MANDATORY, ML_ERROR,
                "         %s\n", strerror(errno));
            ml_printf(ML_MANDATORY, ML_ERROR,
                "         File:%s Line:%d\n", __FILE__, __LINE__);
        }
        
    }
    return RTN_OK;
}

FILE* Open_config_file(char conf_pathname[])
{
    FILE   *Conf_file;
    char   *env_ptr;
    char    config_filename[] = "mk4d.conf";
    char    pathname[FILENAME_MAX];
    
    if (conf_pathname)
    {
        /****************************************************************/
        /* We were passed a specific filename to open.  Attempt to      */
        /* open it.  if it fails give up and return the null pointer    */
        /****************************************************************/
        ml_printf(ML_DEBUG, ML_INFORMATION,
            "looking for named config file: <%s>\n", conf_pathname);
            
        Conf_file = fopen(conf_pathname, "r");
        if (!Conf_file)
        {
            /* Error opening the file */
            ml_printf(ML_MANDATORY, ML_ERROR,
                "ERROR: Could not open configuration file <%s>\n",
                conf_pathname);
            ml_printf(ML_MANDATORY, ML_ERROR,
                "       %s\n", strerror(errno));
            ml_printf(ML_MANDATORY, ML_ERROR,
                "       File:%s Line:%d\n", __FILE__, __LINE__);
            return NULL;
        }
        
        ml_printf(ML_VERBOSE, ML_INFORMATION,
                "Opened config file <%s>\n", conf_pathname);
        return Conf_file;
    }
    else
    {
        /****************************************************************/
        /* we were not given a specific file to open so now we look     */
        /* down our search path.  Look in the following locations in    */
        /* order stopping when we find something.                       */
        /*  1) The current directory                                    */
        /*  2) The user's home diectory                                 */
        /*  3) /etc                                                     */
        /****************************************************************/
        
        /****************************************************************/
        /* Check current dir.                                           */
        /****************************************************************/
        
            
        env_ptr = getenv("PWD");
        if (!env_ptr)
        {
            /* Error obtaining current directory */
            ml_print (ML_MANDATORY, ML_ERROR,
                "ERROR: Could not determine the current directory\n");
            ml_printf(ML_MANDATORY, ML_ERROR,
                "       File:%s Line:%d\n", __FILE__, __LINE__);
            return NULL;
        }
        strncpy(pathname, env_ptr,         sizeof(pathname));
        strncat(pathname, "/", sizeof(pathname));
        strncat(pathname, config_filename, sizeof(pathname));
        
        /* Attempt to open.  Return on success */
        ml_printf(ML_DEBUG, ML_INFORMATION,
            "looking for config file in current directory: <%s>\n", pathname); 
             
        Conf_file = fopen(pathname, "r");
        if (Conf_file)
        {
            ml_printf(ML_VERBOSE, ML_INFORMATION,
                "Opened config file <%s>\n", pathname);
            return  Conf_file;
        }
        
        /* The open failed. "file does not exist" is the olnly allowed  */
        /* error.  Print and error if the failure is for anything but   */
        /* that                                                         */
        if  (ENOENT != errno)
        {
            /* Error opening the file */
            ml_printf(ML_MANDATORY, ML_ERROR,
                "ERROR: Could not open configuration file <%s>\n",
                pathname);
            ml_printf(ML_MANDATORY, ML_ERROR,
                "       %s\n", strerror(errno));
            ml_printf(ML_MANDATORY, ML_ERROR,
                "       File:%s Line:%d\n", __FILE__, __LINE__);
            return NULL;
        }
        
        /****************************************************************/
        /* Check home dir.                                              */
        /****************************************************************/
        env_ptr = getenv("HOME");
        if (!env_ptr)
        {
            /* Error obtaining home directory */
            ml_print (ML_MANDATORY, ML_ERROR,
                "ERROR: Could not determine home directory\n");
            ml_printf(ML_MANDATORY, ML_ERROR,
                "       File:%s Line:%d\n", __FILE__, __LINE__);
            return NULL;
        }
        strncpy(pathname, env_ptr,         sizeof(pathname));
        strncat(pathname, "/", sizeof(pathname));
        strncat(pathname, config_filename, sizeof(pathname));
        
        /* Attempt to open.  Return on success */
        ml_printf(ML_DEBUG, ML_INFORMATION,
            "looking for config file in home directory: <%s>\n", pathname); 
            
        Conf_file = fopen(pathname, "r");
        if (Conf_file)
        {
            ml_printf(ML_VERBOSE, ML_INFORMATION,
                "Opened config file <%s>\n", pathname);
            return  Conf_file;
        }
        
        /* The open failed. "file does not exist" is the olnly allowed  */
        /* error.  Print and error if the failure is for anything but   */
        /* that                                                         */
        if  (ENOENT != errno)
        {
            /* Error opening the file */
            ml_printf(ML_MANDATORY, ML_ERROR,
                "ERROR: Could not open configuration file <%s>\n",
                pathname);
            ml_printf(ML_MANDATORY, ML_ERROR,
                "       %s\n", strerror(errno));
            ml_printf(ML_MANDATORY, ML_ERROR,
                "       File:%s Line:%d\n", __FILE__, __LINE__);
            return NULL;
        }
        
        /****************************************************************/
        /* Check /etc                                                   */
        /****************************************************************/
        strncpy(pathname, "/etc/",          sizeof(pathname));         
        strncat(pathname, config_filename, sizeof(pathname));
        
        ml_printf(ML_DEBUG, ML_INFORMATION,
            "looking for config file in /etc directory: <%s>\n", pathname);
            
        Conf_file = fopen(pathname, "r");
        if (!Conf_file)
        {
            /* Error opening the file */
            ml_printf(ML_MANDATORY, ML_ERROR,
                "ERROR: Could not open configuration file <%s>\n", pathname);
            ml_printf(ML_MANDATORY, ML_ERROR,
                "       %s\n", strerror(errno));
            ml_printf(ML_MANDATORY, ML_ERROR,
                "       File:%s Line:%d\n", __FILE__, __LINE__);
            return NULL;
        }
        
        ml_printf(ML_VERBOSE, ML_INFORMATION,
            "Opened config file <%s>\n", pathname);
        return Conf_file;
    }
}

/********************************************************************************/
/*                                                                              */
/*  Keyword_Assignment                                                          */
/*                                                                              */
/*          if FailureStr is non-NULL and this function fails then  the reason  */
/*          for the failure is place into FailureStr[]                          */
/*                                                                              */
/********************************************************************************/
int Keyword_Assignment(char token1[], char token2[], char FailureStr[]) 
{
    int     i;
    int     num;
    void   *pointer;
    double  dbl_val;
    int     int_val;
    
    i = 0;
    while (KeyWordTable[i].value)
    {
        if (0 == strncasecmp(KeyWordTable[i].keyword, token1,
                 sizeof(KeyWordTable[i].keyword)))
        {
            /* Found a match. now edcode it. */
            switch(KeyWordTable[i].type)
            {
                case KW_STR: 
                    strncpy(KeyWordTable[i].value, token2, STRSZ);
                    break;
                    
                case KW_DBL:
                    pointer = KeyWordTable[i].value;
                    num = sscanf(token2, "%lf", &dbl_val);
                    if (num != 1)
                    {
                        /* ERROR: Conversion failed */
                        ml_print (ML_MANDATORY, ML_ERROR,
                            "ERROR: In config file -- Invalid floating point argument\n");
                        ml_printf(ML_MANDATORY, ML_ERROR,
                            "       Keyword: %s\n", token1);
                        ml_printf(ML_MANDATORY, ML_ERROR,
                            "       Argument: <%s>\n", token2);
                        if(FailureStr)
                        {
                            strcpy(FailureStr, "Invalid floating point argument");
                        }
                        return RTN_ERR;
                    }
                    
                    /* the conversion worked now let's do a limit check */
                    if ( (dbl_val > KeyWordTable[i].max_limit) || 
                         (dbl_val < KeyWordTable[i].min_limit) )
                    {    
                        /* ERROR: Out of range */
                        ml_print (ML_MANDATORY, ML_ERROR,
                            "ERROR: In config file -- Argument out of range\n");
                        ml_printf(ML_MANDATORY, ML_ERROR,
                            "       Keyword:     %s, value: <%s>\n", token1);    
                        ml_printf(ML_MANDATORY, ML_ERROR,
                            "       Argument:    %f\n", dbl_val);
                        ml_printf(ML_MANDATORY, ML_ERROR,
                            "       Valid Range: %f -> %f\n", 
                            KeyWordTable[i].min_limit, KeyWordTable[i].max_limit);
                        
                        if(FailureStr)
                        {
                            strcpy(FailureStr, "Argument out of range");
                        }
                        return RTN_ERR;
                    }
                    
                    /* Looks good.  Do the assignment. */
                    *(double*)pointer = dbl_val; 
                    break;
                    
                case KW_INT:
                    pointer = KeyWordTable[i].value;
                    num = sscanf(token2, "%d", &int_val);
                    if (num != 1)
                    {
                        /* ERROR: Conversion failed */
                        ml_print (ML_MANDATORY, ML_ERROR,
                            "ERROR: In config file -- Invalid integer argument\n");
                        ml_printf(ML_MANDATORY, ML_ERROR,
                            "       keyword: %s, value: <%s>\n", token1, token2);
                        
                        if(FailureStr)
                        {
                            strcpy(FailureStr, "Invalid integer argument");
                        }
                        return RTN_ERR;
                    }
                    
                    /* the conversion worked now let's do a limit check */
                    if ( (int_val > rint(KeyWordTable[i].max_limit)) || 
                         (int_val < rint(KeyWordTable[i].min_limit)) )
                    {    
                        /* ERROR: Out of range */
                        ml_print (ML_MANDATORY, ML_ERROR,
                            "ERROR: In config file -- Argument out of range\n");
                        ml_printf(ML_MANDATORY, ML_ERROR,
                            "       Keyword:     %s, value: <%s>\n", token1);    
                        ml_printf(ML_MANDATORY, ML_ERROR,
                            "       Argument:    %d\n", int_val);
                        ml_printf(ML_MANDATORY, ML_ERROR,
                            "       Valid Range: %d -> %d\n", 
                            (int)(rint(KeyWordTable[i].min_limit)), 
                            (int)(rint(KeyWordTable[i].max_limit)));
                        
                        if(FailureStr)
                        {
                            strcpy(FailureStr, "Argument out of range");
                        }
                        return RTN_ERR;
                    
                    }
                    
                    /* Looks good.  Do the assignment. */
                    *(int*)pointer = int_val; 
                    break;
                    
                default:
                    ml_print (ML_MANDATORY, ML_ERROR,
                        "ERROR: Internal error while processing config file\n"); 
                    ml_printf(ML_MANDATORY, ML_ERROR,
                        "       File:%s Line:%d\n", __FILE__, __LINE__); 
                        
                    if(FailureStr)
                    {
                        strcpy(FailureStr, "Internal error");
                    }
                    return RTN_ERR;
            }
            
            /* Print out the values if debug is enabled */
            ml_printf(ML_DEBUG, ML_INFORMATION,
                "assigned parameter:   %s <- %s\n", token1, token2);
            return RTN_OK;
        }
        i++;    
    }
    
    /* If we get here then we failed to find the keyword in the table */
    ml_print (ML_MANDATORY, ML_ERROR,
        "ERROR: While processing config file -- Invalid keyword\n");
    ml_printf(ML_MANDATORY, ML_ERROR,
        "       keyword: <%s>\n", token1); 
                        
    if(FailureStr)
    {
        strcpy(FailureStr, "Invalid keyword");
    }
    return RTN_ERR;
}

void PrintParm(void)
{
    int     i;
    char    value_str[100];
    double  dbl_val;
    int     int_val;
    
    ml_print(ML_DEBUG, ML_INFORMATION,
            "Parameter Table Content:\n");
    i = 0;
    while (KeyWordTable[i].value)
    {
            switch(KeyWordTable[i].type)
            {
                case KW_STR: 
                    strncpy(value_str, KeyWordTable[i].value, sizeof(value_str));
                    break;
                    
                case KW_DBL:
                    dbl_val = *((double*)KeyWordTable[i].value);
                    sprintf(value_str, "%f", dbl_val);
                    break;
                    
                case KW_INT:
                    int_val = *((int*)KeyWordTable[i].value);
                    sprintf(value_str, "%d", int_val);
                    break;
                    
                default:
                    ml_print (ML_MANDATORY, ML_ERROR,
                        "ERROR: Internal error while printing argument table\n"); 
                    ml_printf(ML_MANDATORY, ML_ERROR,
                        "       File:%s Line:%d\n", __FILE__, __LINE__); 
                    return;
            }
        ml_printf(ML_DEBUG, ML_INFORMATION,
            "  %s\t%s\t# %s\n", KeyWordTable[i].keyword, value_str, KeyWordTable[i].helpstr);
        i++;
    }
    ml_print(ML_DEBUG, ML_INFORMATION,
            "--End of Parameter Table\n");
}
   
void ServerUsage(void)
{
    printf("Mk4d usage:  Mk4d <options>...\n\n");
    printf("options my be the traditional signle dash, single letter style\n");
    printf("or the GNU style double dash, word style.  Both styles can be\n");
    printf("used interchangably.  The valid options are:\n\n");
    printf(" -V --version               Print version number, exit\n");
    printf(" -H --help                  Print this message, exit\n");
    printf(" -l --syslog                route messages to syslog\n");
    printf(" -p --port <portnumber>     server's port number\n");
    printf(" -v --verbose               print progress messages\n");
    printf(" -d --debug                 print debug messages\n");
    
    exit(0);
}

void ServerVersion(void)
{
    printf("Mk4d Version %s Compiled  %s %s\n",
            VERS, __DATE__, __TIME__);
     
    exit(0);
}
