/*-
******************************************************************************
******************************************************************************
**
**  ARCHIVE HEADER INFORMATION
**
**  @C-file{
**      FILENAME    = "vvutils.c",
**      VERSION     = "1.00",
**      DATE        = "",
**      TIME        = "",
**
**      AUTHOR      = "Niel Kempson",
**      ADDRESS     = "25 Whitethorn Drive, Cheltenham, GL52 5LL, England",
**      TELEPHONE   = "+44-242 579105",
**      EMAIL       = "kempson@tex.ac.uk (Internet)",
**
**      SUPPORTED   = "yes",
**      ARCHIVED    = "tex.ac.uk, ftp.tex.ac.uk",
**      KEYWORDS    = "VVcode",
**
**      CODETABLE   = "ISO/ASCII",
**      CHECKSUM    = "51492 1481 5732 57976",
**
**      DOCSTRING   = { This file is part of VVcode.
**                  }
**  }
**
**  MODULE CONTENTS
**
**      allocate_buffer         Allocate memory for a buffer.
**      display_comment         Display a VVE file comment.
**      f_close                 Close a file.
**      getc_format             Get the format specified on the command line.
**      getc_mode               Get the mode specified on the command line.
**      getc_reclen             Get the maximum record length specified on
**                              the command line.
**      getc_timestamp          Get the timestamp specified on the command 
**                              line.
**      header_type             Look for a header string in a list of headers.
**      init_crc32_table        Initialize the 32 bit CRC table.
**      lookup_key              Look for a string in a list of keywords.
**      make_time               Convert a 'tm' structure into a Unix style
**                              time_t value.
**      open_log_file           Open the log file for writing.
**      parse_header            Extract the value of a VVE/XLT header line.
**      pars_time               Parse a string in VVcode timestamp format and
**                              convert it to a Unix style time_t value.
**      pars_xlt_tbl            Parse a character set translation table.
**      rd_enc_tbl              Read and check an encoding table from a
**                              specified file.
**      rd_xlt_tbl              Read and check a character set translation 
**                              table from a specified file.
**      set_dec_tbl             Invert an encoding table to form a decoding 
**                              table.
**      set_xlt_tbl             Setup the character set translation table.
**      show_file_context       Show the context in which a file processing
**                              error occurred.
**      vstrchr                 Search a string for the first occurrence of a 
**                              specified character.
**      vstricmp                Compare two strings ignoring case differences.
**      vstrnicmp               Compare at most 'n' characters of two strings
**                              ignoring case differences.
**      vstrrchr                Search a string for the last occurrence of a 
**                              specified character.
**      vstrrstr                Search a string for the last occurrence of
**                              any of a list of specified characters.
**      vstrstr                 Search a string for the first occurrence of
**                              any of a list of specified characters.
**      vv_timestamp            Convert a Unix style time_t value into a 
**                              VVcode format timestamp string.
**      vstrdup                 Duplicate a string, allocating memory for the
**                              copy.
**      vstrtrws                Strip trailing whitespace from a string.
**
**  COPYRIGHT
**
**      Copyright (c) 1991-1993 by Niel Kempson <kempson@tex.ac.uk>
**
**      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 1, or
**      (at your option) any later version.
**
**      This program is distributed in the hope that it will be useful,
**      but WITHOUT ANY WARRANTY; without even the implied warranty of
**      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
**      General Public License for more details.
**
**      You should have received a copy of the GNU General Public License
**      along with this program; if not, write to the Free Software
**      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**      In other words, you are welcome to use, share and improve this
**      program.  You are forbidden to forbid anyone else to use, share
**      and improve what you give them.   Help stamp out software-hoarding!  
**
**  CHANGE LOG
**
******************************************************************************
******************************************************************************
*/
static char rcsid[] = "$Id$";


#define VVUTILS                 1


/*-
**----------------------------------------------------------------------------
** Standard include files
**----------------------------------------------------------------------------
*/
#include <stdio.h>


/*-
**----------------------------------------------------------------------------
** Include files
**----------------------------------------------------------------------------
*/
#include "checkos.h"
#include "machine.h"
#include "local.h"
#include "globals.h"
#include "specific.h"
#include "vvutils.h"


/*-
**----------------------------------------------------------------------------
** 
**----------------------------------------------------------------------------
*/
static int              month_days[12] =
    {
     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    };


/*-
**============================================================================
**
** FUNCTION
**
**      allocate_buffer
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    char                   *allocate_buffer (CONST SIZE_T buf_len,
                                             CONST char *buf_name)
#else                           /* NOT (ANSI_SYNTAX) */
    char                   *allocate_buffer (buf_len, buf_name)
        CONST SIZE_T        buf_len;
        CONST char         *buf_name;
#endif                          /* (ANSI_SYNTAX) */
{
    SIZE_T                  bytes_required;

    char                   *buf_ptr;

    bytes_required = buf_len * sizeof (char);
    buf_ptr = (char *) malloc (bytes_required);

    if (buf_ptr == NULL)
    {
        FATALMSG_2 ("error allocating %ld bytes for buffer `%s'",
                   (Int32) bytes_required, buf_name);
        vv_exit ();
    }
    else
    {
        DEBUG_2 ("allocate_buffer: allocated %ld bytes for buffer `%s'",
                 (Int32) bytes_required, buf_name);
    } 
    return (buf_ptr);
}                               /* allocate_buffer */



/*-
**============================================================================
**
** FUNCTION
**
**      display_comment
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    display_comment (CONST File_Info *ip_file,
                                             CONST char *line_ptr,
                                             Header_Struct *hdr_struct)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    display_comment (ip_file, line_ptr, hdr_struct)
        CONST File_Info    *ip_file;
        CONST char         *line_ptr;
        Header_Struct      *hdr_struct;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *comment_str_buffer;
    int                     fields_read;

    hdr_struct->present = FALSE;
    hdr_struct->value = NULL;
    comment_str_buffer = allocate_buffer ((MAX_IP_LINE_LEN + 1), 
                                          "comment_str_buffer");

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    fields_read = sscanf (line_ptr, "%*s %[^\n]", comment_str_buffer);
    if (fields_read == 1)
    {
	LOGMSG_1 ("VVE file comment:              %s", comment_str_buffer);
    }
    else
    {
	ERRORMSG_1 ("error parsing value of `%s' header", hdr_struct->h_string);
	show_file_context (ip_file, line_ptr);
    }
    free (comment_str_buffer);
}                               /* display_comment */



/*-
**============================================================================
**
** FUNCTION
**
**      f_close
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    f_close (File_Info *target_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    f_close (target_file)
        File_Info          *target_file;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     status;

    if (target_file->file_ptr != (FILE *) NULL)
    {
        status = fclose (target_file->file_ptr);

        if (status != 0)
        {
            ERRORMSG_1 ("error closing file `%s'", target_file->file_spec);
            explain_error ();
        }
    }
}                               /* f_close */



/*-
**============================================================================
**
** FUNCTION
**
**      getc_format
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Int16                   getc_format (CONST Qualifier_Struct *qual_format,
                                         CONST char *fmt_str_array[])
#else                           /* NOT (ANSI_SYNTAX) */
    Int16                   getc_format (qual_format, fmt_str_array)
        CONST Qualifier_Struct *qual_format;
        CONST char             *fmt_str_array[];
#endif                          /* (ANSI_SYNTAX) */
{
    Int16                   format_no;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (qual_format->present != TRUE)
    {
        DEBUG ("getc_format: format not specified on the command line");
        return (INV_FORMAT);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    format_no = lookup_key (fmt_str_array, qual_format->value,
                            ALLOW_ABBREVIATIONS, CASE_INSENSITIVE);

    switch (format_no)
    {

            /*-
            **----------------------------------------------------------------
            **
            **----------------------------------------------------------------
            */
        case FMT_FIXED:

#if (SUPPORT_FIXED_FMT)
            break;
#else                           /* NOT (SUPPORT_FIXED_FMT) */
            USAGE_1 ("format `%s' is not supported by this implementation",
                     fmt_str_array[format_no]);
            break;
#endif                          /* (SUPPORT_FIXED_FMT) */

            /*-
            **----------------------------------------------------------------
            **
            **----------------------------------------------------------------
            */
        case FMT_STREAM:

#if (SUPPORT_STREAM_FMT)
            break;
#else                           /* NOT (SUPPORT_STREAM_FMT) */
            USAGE_1 ("format `%s' is not supported by this implementation",
                     fmt_str_array[format_no]);
            break;
#endif                          /* (SUPPORT_STREAM_FMT) */

            /*-
            **----------------------------------------------------------------
            **
            **----------------------------------------------------------------
            */
        case FMT_VARIABLE:

#if SUPPORT_VARIABLE_FMT
            break;
#else                           /* NOT (SUPPORT_VARIABLE_FMT) */
            USAGE_1 ("format `%s' is not supported by this implementation",
                     fmt_str_array[format_no]);
            break;
#endif                          /* (SUPPORT_VARIABLE_FMT) */

            /*-
            **----------------------------------------------------------------
            **
            **----------------------------------------------------------------
            */
        case FMT_DEFAULT:
            break;

            /*-
            **----------------------------------------------------------------
            **
            **----------------------------------------------------------------
            */
        default:
            USAGE_1 ("invalid format `%s'", qual_format->value);
    }

    DEBUG_1 ("getc_format: command line specifies format `%s'",
             fmt_str_array[format_no]);
    return (format_no);
}                               /* getc_format */



/*-
**============================================================================
**
** FUNCTION
**
**      getc_mode
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Int16                   getc_mode (CONST Qualifier_Struct *qual_mode,
                                       CONST char *mode_str_array[])
#else                           /* NOT (ANSI_SYNTAX) */
    Int16                   getc_mode (qual_mode, mode_str_array)
        CONST Qualifier_Struct *qual_mode;
        CONST char             *mode_str_array[];
#endif                          /* (ANSI_SYNTAX) */
{
    Int16                   mode_no;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (qual_mode->present != TRUE)
    {
        DEBUG ("getc_mode: mode not specified on the command line");
        return (INV_MODE);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    mode_no = lookup_key (mode_str_array, qual_mode->value,
                          ALLOW_ABBREVIATIONS, CASE_INSENSITIVE);

    switch (mode_no)
    {
        case MODE_BINARY:
        case MODE_TEXT:
            break;
        default:
            USAGE_1 ("invalid mode `%s'", qual_mode->value);
    }

    DEBUG_1 ("getc_mode: command line specifies mode `%s'",
             mode_str_array[mode_no]);
    return (mode_no);
}                               /* getc_mode */



/*-
**============================================================================
**
** FUNCTION
**
**      getc_reclen
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Int32                   getc_reclen (CONST Qualifier_Struct *qual_reclen,
                                         CONST File_Info *ip_file)
#else                           /* NOT (ANSI_SYNTAX) */
    Int32                   getc_reclen (qual_reclen, ip_file)
        CONST Qualifier_Struct *qual_reclen;
        CONST File_Info        *ip_file;
#endif                          /* (ANSI_SYNTAX) */
{
    Int32                   record_len;
    int                     status;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (qual_reclen->present != TRUE)
    {
        DEBUG ("getc_reclen: record length not specified on the command line");
        return (INV_RECORD_LEN);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    record_len = INV_RECORD_LEN;
    status = sscanf (qual_reclen->value, "%ld", &record_len);

    if (status != 1)
    {
        USAGE_1 ("invalid record length specified `%s'", qual_reclen->value);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (ip_file->format == FMT_VARIABLE)
    {
        if ((record_len < (Int32) MIN_VARIABLE_RECORD_LEN)
            || (record_len > (Int32) MAX_VARIABLE_RECORD_LEN))
        {
            USAGE_3 ("record length must be in the range %ld-%ld (not %ld)",
                     (Int32) MIN_VARIABLE_RECORD_LEN,
                     (Int32) MAX_VARIABLE_RECORD_LEN, record_len);
        }
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    else
    if (ip_file->format == FMT_FIXED)
    {
        if ((record_len < (Int32) MIN_FIXED_RECORD_LEN)
            || (record_len > (Int32) MAX_FIXED_RECORD_LEN))
        {
            USAGE_3 ("record length must be in the range %ld-%ld (not %ld)",
                     (Int32) MIN_FIXED_RECORD_LEN,
                     (Int32) MAX_FIXED_RECORD_LEN, record_len);
        }
    }

    DEBUG_1 ("getc_reclen: command line specifies maximum record length `%ld'",
             record_len);
    return (record_len);
}                               /* getc_reclen */



/*-
**============================================================================
**
** FUNCTION
**
**      getc_timestamp
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    TIME_T                  getc_timestamp (CONST Qualifier_Struct *qual_timestamp)
#else                           /* NOT (ANSI_SYNTAX) */
    TIME_T                  getc_timestamp (qual_timestamp)
        CONST Qualifier_Struct *qual_timestamp;
#endif                          /* (ANSI_SYNTAX) */
{
    TIME_T                  cmd_timestamp;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (qual_timestamp->present != TRUE)
    {
        DEBUG ("getc_timestamp: timestamp not specified on the command line");
        return (INV_TIMESTAMP);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if ((qual_timestamp->value == NULL) || (*qual_timestamp->value == '\0'))
    {
        (void) time (&cmd_timestamp);
    }
    else
    {
        cmd_timestamp = pars_time (qual_timestamp->value);

        if (cmd_timestamp == INV_TIMESTAMP)
        {
            USAGE_1 ("invalid timestamp string specified `%s'",
                     qual_timestamp->value);
        }
    }
    return (cmd_timestamp);
}                               /* getc_timestamp */



/*-
**============================================================================
**
** FUNCTION
**
**      header_type
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Int16                   header_type (CONST File_Info *ip_file,
                                         CONST char *line_ptr,
                                         CONST Header_Struct *hdr_array)
#else                           /* NOT (ANSI_SYNTAX) */
    Int16                   header_type (ip_file, line_ptr, hdr_array)
        CONST File_Info        *ip_file;
        CONST char             *line_ptr;
        CONST Header_Struct    *hdr_array;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     cmp_len;
    Int16                   hdr_found;
    Int16                   hdr_no;


    /*-
    **------------------------------------------------------------------------
    ** If the string to be tested is NULL treat it as ambiguous and return.
    **------------------------------------------------------------------------
    */
    if ((line_ptr == NULL) || (line_ptr[0] == '\0'))
    {
        return (HDR_UNKNOWN);
    }

    /*-
    **------------------------------------------------------------------------
    ** Search the VVE header structure array for this string.
    **------------------------------------------------------------------------
    */
    hdr_found = -1;

    for (hdr_no = 0; hdr_array[hdr_no].h_string != NULL; hdr_no++)
    {
        cmp_len = strlen (hdr_array[hdr_no].h_string);

        if (STRNCMPI (hdr_array[hdr_no].h_string, line_ptr, cmp_len) == 0)
        {
            DEBUG_1 ("header_type: VVE line matches header: `%s'", 
                     hdr_array[hdr_no].h_string);

            if (hdr_found < 0)
            {
                hdr_found = hdr_no;
            }
            else
            {
                ERRORMSG ("ambiguous header ignored");
                show_file_context (ip_file, line_ptr);
                return (HDR_UNKNOWN);
            }
        }
    }

    /*-
    **------------------------------------------------------------------------
    ** If the header string is unknown, return that status.
    **------------------------------------------------------------------------
    */
    if (hdr_found < 0)
    {
        DEBUG_1 ("header_type: ignored unknown VVE header line: `%s'", 
                  line_ptr);
        return (HDR_UNKNOWN);
    }
    else
    {
        return (hdr_found);
    }
}                               /* header_type */



/*-
**============================================================================
**
** FUNCTION
**
**      init_crc32_table
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    init_crc32_table (CONST Int16 crc_table_size,
                                              CONST Unsigned32 crc_polynomial,
                                              Unsigned32 *crc_table)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    init_crc32_table (crc_table_size, crc_polynomial, 
                                              crc_table)
        CONST Int16             crc_table_size;
        CONST Unsigned32        crc_polynomial;
        Unsigned32             *crc_table;
#endif                          /* (ANSI_SYNTAX) */
{
    Unsigned16              idx;
    Unsigned16              j;
    Unsigned32              r;

    for (idx = 0; idx < (Unsigned16) crc_table_size; idx++)
    {
        r = idx;
        for (j = 0; j < 8; j++)
        {
            if (r & 1)
            {
                r = (r >> 1) ^ crc_polynomial;
            }
            else
            {
                r >>= 1;
            }
        }
        crc_table[idx] = r;
    }
}                               /* init_crc32_table */



/*-
**============================================================================
**
** FUNCTION
**
**      lookup_key
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Int16                   lookup_key (CONST char *key_list[],
                                        CONST char *string,
                                        CONST Int16 allow_abbrevs,
                                        CONST Int16 case_sensitivity)
#else                           /* NOT (ANSI_SYNTAX) */
    Int16                   lookup_key (key_list, string, allow_abbrevs,
                                        case_sensitivity)
        CONST char             *key_list[];
        CONST char             *string;
        CONST Int16             allow_abbrevs;
        CONST Int16             case_sensitivity;
#endif                          /* (ANSI_SYNTAX) */
{
    Int16                   key_no;
    int                     cmp_len;
    Int16                   look_status;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if ((string == NULL) || (*string == '\0'))
    {
        return (LOOKUP_AMBIGUOUS);
    }
    cmp_len = strlen (string);
    look_status = LOOKUP_UNKNOWN;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    for (key_no = 0; key_list[key_no] != NULL; key_no++)
    {

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        if (allow_abbrevs == NO_ABBREVIATIONS)
        {
            cmp_len = strlen (key_list[key_no]);
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        if (case_sensitivity == CASE_INSENSITIVE)
        {
            if (STRNCMPI (key_list[key_no], string, cmp_len) == 0)
            {
                if (look_status == LOOKUP_UNKNOWN)
                {
                    look_status = key_no;
                }
                else
                {
                    look_status = LOOKUP_AMBIGUOUS;
                }
            }
        }
        else
        {
            if (STRNCMP (key_list[key_no], string, cmp_len) == 0)
            {
                if (look_status == LOOKUP_UNKNOWN)
                {
                    look_status = key_no;
                }
                else
                {
                    look_status = LOOKUP_AMBIGUOUS;
                }
            }
        }
    }

    return (look_status);
}                               /* lookup_key */



/*-
**============================================================================
**
** FUNCTION
**
**      make_time
**
** DESCRIPTION
**
**      Convert the 'tm' structure (in GMT) to a standard 'TIME_T' value of
**      the number of seconds since 1-Jan-1970 00:00:00 GMT.
** 
**      NOTE:    the structure time is assumed to be GMT and only the fields
**               tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec are used.
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    TIME_T                  make_time (CONST struct tm *t_struct)
#else                           /* NOT (ANSI_SYNTAX) */
    TIME_T                  make_time (t_struct)
        CONST struct tm        *t_struct;
#endif                          /* (ANSI_SYNTAX) */
{
    TIME_T                  total_days;
    TIME_T                  total_secs;
    int                     i;

    DEBUG_3 ("make_time: date provided = %04d/%02d/%02d", 
             t_struct->tm_year, t_struct->tm_mon, t_struct->tm_mday);
    DEBUG_3 ("make_time: time provided = %02d:%02d:%02d",
             t_struct->tm_hour, t_struct->tm_min, t_struct->tm_sec);

    /*-
    **------------------------------------------------------------------------
    ** Calculate the number of days since 1 January 1970, but do not return a
    ** negative number.
    **------------------------------------------------------------------------
    */
    if (t_struct->tm_year < 70)
    {
        return (0L);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    total_days = (TIME_T) 0;
    for (i = 1970; i < (1900 + t_struct->tm_year); i++)
    {
        total_days += (TIME_T) 365;
        if (LEAPYEAR (i) == TRUE)
        {
            ++total_days;
        }
    }

    for (i = 0; i < t_struct->tm_mon; i++)
    {
        total_days += month_days[i];
        if ((i == 1) && (LEAPYEAR (1900 + t_struct->tm_year) == TRUE))
        {
            ++total_days;
        }
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    total_days += (TIME_T) (t_struct->tm_mday - 1);

    total_secs = ((TIME_T) total_days * 86400L)
        + ((TIME_T) t_struct->tm_hour * 3600L)
        + ((TIME_T) t_struct->tm_min * 60L)
        + (TIME_T) t_struct->tm_sec;

    DEBUG_1 ("make_time: time_t value returned = %ld", total_secs);
    return (total_secs);
}                               /* make_time */



/*-
**============================================================================
**
** FUNCTION
**
**      open_log_file
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    open_log_file (CONST Qualifier_Struct *log_qualifier,
                                           CONST char *def_file_name,
                                           CONST Int16 overwrite_flag,
                                           File_Info *log_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    open_log_file (log_qualifier, def_file_name, 
                                           overwrite_flag, log_file)
        CONST Qualifier_Struct *log_qualifier;
        CONST char             *def_file_name;
        CONST Int16             overwrite_flag;
        File_Info              *log_file;
#endif                          /* (ANSI_SYNTAX) */
{

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    log_file->file_ptr = (FILE *) NULL;
    log_file->mode = MODE_TEXT;
    log_file->format = DEF_TEXT_FMT;
    log_file->max_rec_len = INV_RECORD_LEN;
    log_file->lng_rec_len = INV_RECORD_LEN;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (log_qualifier->present != TRUE)
    {
        DEBUG ("open_log_file: log file not specified on the command line");
        return;
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (log_qualifier->value != NULL)
    {
        strcpy (log_file->file_spec, log_qualifier->value);

        /*-
        **--------------------------------------------------------------------
        ** If a file spec was given for the log file, use the default log
        ** file extension if one was not provided.
        **--------------------------------------------------------------------
        */
#if (STICKY_DEFAULTS)
        apply_defaults (def_file_name, DEF_LOG_EXT, log_file->file_spec);
#endif                          /* (STICKY_DEFAULTS) */

        DEBUG_1 ("open_log_file: opening log file `%s'", log_file->file_spec);
        log_file->file_ptr = f_open_out (overwrite_flag, log_file);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    else
    {
        strcpy (log_file->file_spec, "stderr");
        log_file->file_ptr = stderr;
        log_file->pipe_flag = TRUE;
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (log_file->file_ptr == (FILE *) NULL)
    {
        FATALMSG_1 ("error opening log file `%s'", log_file->file_spec);
        vv_exit ();
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if ((log_file->pipe_flag == TRUE) || (is_a_file (log_file) == FALSE))
    {
        DEBUG_1 ("open_log_file: `%s' is not a regular file",
                 log_file->file_spec);
        log_file->pipe_flag = TRUE;
        set_pipe_mode (log_file);
    }
}                               /* open_log_file */



/*-
**============================================================================
**
** FUNCTION
**
**      parse_header
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    parse_header (CONST File_Info *ip_file,
                                          CONST char *line_ptr,
                                          Header_Struct *hdr_struct)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    parse_header (ip_file, line_ptr, hdr_struct)
        CONST File_Info        *ip_file;
        CONST char             *line_ptr;
        Header_Struct          *hdr_struct;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *dummy;
    int                     fields_read;

    hdr_struct->present = FALSE;
    hdr_struct->value = NULL;
    dummy = allocate_buffer ((MAX_IP_LINE_LEN + 1), "dummy");
    fields_read = sscanf (line_ptr, "%*s %s", dummy);

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (fields_read == 1)
    {
        hdr_struct->value = STRDUP (dummy);
        hdr_struct->present = TRUE;
    }
    else
    {
        ERRORMSG_1 ("error parsing value of `%s' header", hdr_struct->h_string);
        show_file_context (ip_file, line_ptr);
    }
    free (dummy);
}                               /* parse_header */



/*-
**============================================================================
**
** FUNCTION
**
**      pars_time
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    TIME_T                  pars_time (CONST char *time_str)
#else                           /* NOT (ANSI_SYNTAX) */
    TIME_T                  pars_time (time_str)
        CONST char             *time_str;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     args;
    TIME_T                  time_val;
    struct tm               tm_struct;

    /*-
    **------------------------------------------------------------------------
    ** If the time string is empty or null, return an error.
    **------------------------------------------------------------------------
    */
    time_val = INV_TIMESTAMP;

    if ((time_str == NULL) || (*time_str == '\0'))
    {
        return (INV_TIMESTAMP);
    }

    DEBUG_1 ("pars_time: VVE timestamp string provided = `%s'", time_str);

    /*-
    **------------------------------------------------------------------------
    ** Initialize the time structure to 1970.01.01-00:00:00 GMT
    **------------------------------------------------------------------------
    */
    tm_struct.tm_year = 1970;
    tm_struct.tm_mon = 1;
    tm_struct.tm_mday = 1;
    tm_struct.tm_hour = 0;
    tm_struct.tm_min = 0;
    tm_struct.tm_sec = 0;

    args = sscanf (time_str, VV_TIMESTAMP_FORMAT,
                   &tm_struct.tm_year, &tm_struct.tm_mon, &tm_struct.tm_mday,
                   &tm_struct.tm_hour, &tm_struct.tm_min, &tm_struct.tm_sec);

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if ((args < 1) || (args > 6))
    {
        ERRORMSG_1 ("error parsing timestamp string `%s'", time_str);
        return (INV_TIMESTAMP);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if ((tm_struct.tm_year < 1970) || (tm_struct.tm_year > 2037))
    {
        ERRORMSG_1 ("year (YYYY) must be in the range 1970-2037 (not %d)\n",
                    tm_struct.tm_year);
        return (INV_TIMESTAMP);
    }
    (tm_struct.tm_year) -= 1900;

    if ((tm_struct.tm_mon < 1) || (tm_struct.tm_mon > 12))
    {
        ERRORMSG_1 ("month (MM) must be in the range 1-12 (not %d)\n",
                    tm_struct.tm_mon);
        return (INV_TIMESTAMP);
    }
    --(tm_struct.tm_mon);

    if ((tm_struct.tm_mday < 1) || (tm_struct.tm_mday > 31))
    {
        ERRORMSG_1 ("day (DD) must be in the range 1-31 (not %d)\n",
                    tm_struct.tm_mday);
        return (INV_TIMESTAMP);
    }

    if ((tm_struct.tm_hour < 0) || (tm_struct.tm_hour > 23))
    {
        ERRORMSG_1 ("hours (HH) must be in the range 0-23 (not %d)\n",
                    tm_struct.tm_hour);
        return (INV_TIMESTAMP);
    }

    if ((tm_struct.tm_min < 0) || (tm_struct.tm_min > 59))
    {
        ERRORMSG_1 ("minutes (MM) must be in the range 0-59 (not %d)\n",
                    tm_struct.tm_min);
        return (INV_TIMESTAMP);
    }

    if ((tm_struct.tm_sec < 0) || (tm_struct.tm_sec > 59))
    {
        ERRORMSG_1 ("seconds (SS) must be in the range 0-59 (not %d)\n",
                    tm_struct.tm_sec);
        return (INV_TIMESTAMP);
    }

    tm_struct.tm_wday = 0;
    tm_struct.tm_yday = 0;
    tm_struct.tm_isdst = 0;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    time_val = make_time (&tm_struct);
    DEBUG_1 ("pars_time: time_t value returned = %ld", time_val);

    return (time_val);
}                               /* pars_time */



/*-
**============================================================================
**
** FUNCTION
**
**      pars_xlt_tbl
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Boolean                 pars_xlt_tbl (CONST char *xlt_buffer,
                                          Xlt_Struct *xlt_tbl_struct,
                                          Int16 subst_char)
#else                           /* NOT (ANSI_SYNTAX) */
    Boolean                 pars_xlt_tbl (xlt_buffer, xlt_tbl_struct,
                                          subst_char)
        CONST char             *xlt_buffer;
        Xlt_Struct             *xlt_tbl_struct;
        Int16                   subst_char;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *curr_ptr;
    int                     fields_read;
    char                   *item_end;
    int                     item_num;
    SIZE_T                  item_len;
    char                   *item_start;
    char                   *item_str;
    Boolean                 status;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    status = TRUE;
    item_str = allocate_buffer (XLT_BUFFER_SIZE, "item_str");
    curr_ptr = (char *) xlt_buffer;

    for (item_num = 0;; item_num++)
    {
        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        while ((*curr_ptr != '\0') && isspace (*curr_ptr))
        {
            ++curr_ptr;
        }
        if (*curr_ptr == '\0')
        {
            break;
        }
        else
        {
            item_start = curr_ptr;
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        while ((*curr_ptr != '\0') && (!isspace (*curr_ptr)))
        {
            ++curr_ptr;
        }
        item_end = curr_ptr;

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        item_len = (SIZE_T) (item_end - item_start) + 1;
        strncpy (item_str, item_start, item_len);
        item_str[item_len] = '\0';

        fields_read = sscanf (item_str, "%hd", 
                              &(xlt_tbl_struct->xlt_tbl[item_num]));
        if (fields_read != 1)
        {
            ERRORMSG_2 ("error parsing item %d (`%s') in translation file",
                        (item_num + 1), item_str);
            status = FALSE;
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        if ((xlt_tbl_struct->xlt_tbl[item_num] < -1)
            || (xlt_tbl_struct->xlt_tbl[item_num] > 255))
        {
            ERRORMSG_2 ("invalid value (%d) of item %d in translation file",
                        xlt_tbl_struct->xlt_tbl[item_num], (item_num + 1));
            status = FALSE;
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        if (xlt_tbl_struct->xlt_tbl[item_num] == -1)
        {
            xlt_tbl_struct->xlt_tbl[item_num] = subst_char;
        }
    }                           /* end-for */

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if ((status == TRUE) && (item_num != XLT_TABLE_LEN))
    {
        ERRORMSG_1 ("wrong number of values in translation file: %d",
                    item_num);
        status = FALSE;
    }
    free (item_str);
    return (status);
}                               /* pars_xlt_tbl */



/*-
**============================================================================
**
** FUNCTION
**
**      rd_enc_tbl
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Boolean                 rd_enc_tbl (File_Info *enc_file,
                                        CONST Int16 enc_tbl_len,
                                        char *encode_table)
#else                           /* NOT (ANSI_SYNTAX) */
    Boolean                 rd_enc_tbl (enc_file, enc_tbl_len, encode_table)
        File_Info              *enc_file;
        CONST Int16             enc_tbl_len;
        char                   *encode_table;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *buffer;
    char                   *end_of_line;
    int                     chars_read;
    Boolean                 ret_status;
    Int32                   read_status;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    buffer = allocate_buffer ((MAX_IP_LINE_LEN + 1), "buffer");
    enc_file->mode = MODE_TEXT;
    enc_file->format = DEF_TEXT_FMT;
    enc_file->max_rec_len = INV_RECORD_LEN;
    enc_file->lng_rec_len = INV_RECORD_LEN;
    enc_file->line_no = 0;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    enc_file->file_ptr = f_open_in (enc_file);

    if (enc_file->file_ptr == (FILE *) NULL)
    {
        ERRORMSG_1 ("error opening encoding table file `%s'",
                    enc_file->file_spec);
        vv_exit ();
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    encode_table[0] = '\0';
    ret_status = TRUE;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    for (;;)
    {
        chars_read = strlen ((char *) encode_table);
        enc_file->line_no++;

        read_status = read_line ((Int32) MAX_IP_LINE_LEN, buffer, enc_file);

        if (read_status < 0L)
        {
            if (feof (enc_file->file_ptr) != 0)
            {
                break;
            }

            ERRORMSG ("error reading encoding table file");
            show_file_context (enc_file, NULL);
            explain_error ();
            ret_status = FALSE;
            break;
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        buffer[MAX_IP_LINE_LEN - 1] = (char) '\0';
        end_of_line = STRCHR (buffer, '\n');

        if (end_of_line != NULL)
        {
            *end_of_line = '\0';
        }
        chars_read += strlen (buffer);

        if (chars_read <= enc_tbl_len)
        {
            strcat ((char *) encode_table, buffer);
        }
        else
        {
            ERRORMSG_1 ("too many characters in encoding table file: %d",
                        chars_read);
            show_file_context (enc_file, buffer);
            ret_status = FALSE;
            break;
        }
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (chars_read < enc_tbl_len)
    {
        ERRORMSG_1 ("not enough characters in encoding table file: %d",
                    chars_read);
        ret_status = FALSE;
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    f_close (enc_file);
    free (buffer);
    return (ret_status);
}                               /* rd_enc_tbl */



/*-
**============================================================================
**
** FUNCTION
**
**      rd_xlt_tbl
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Int16                   rd_xlt_tbl (Header_Struct *xlt_hdr_array,
                                        File_Info *xlt_file,
                                        Xlt_Struct *xlt_tbl_struct)
#else                           /* NOT (ANSI_SYNTAX) */
    Int16                   rd_xlt_tbl (xlt_hdr_array, xlt_file, xlt_tbl_struct)
        Header_Struct          *xlt_hdr_array;
        File_Info              *xlt_file;
        Xlt_Struct             *xlt_tbl_struct;
#endif                          /* (ANSI_SYNTAX) */
{
    char                    buffer[MAX_IP_LINE_LEN + 1];
    Int32                   bytes_read;
    int                     chars_read;
    char                   *comment;
    char                   *end_of_line;
    int                     fields_read;
    Int16                   hdr_no;
    Boolean                 ret_status;
    Int16                   subst_char;
    char                   *xlt_buffer;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    xlt_file->mode = MODE_TEXT;
    xlt_file->format = DEF_TEXT_FMT;
    xlt_file->max_rec_len = INV_RECORD_LEN;
    xlt_file->lng_rec_len = INV_RECORD_LEN;
    xlt_file->line_no = 0;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    xlt_file->file_ptr = f_open_in (xlt_file);

    if (xlt_file->file_ptr == (FILE *) NULL)
    {
        FATALMSG_1 ("error opening translation file `%s'",
                    xlt_file->file_spec);
        vv_exit ();
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    xlt_buffer = allocate_buffer (XLT_BUFFER_SIZE, "xlt_buffer");
    xlt_buffer[0] = 0;
    ret_status = TRUE;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    while (ret_status == TRUE)
    {
        chars_read = strlen (xlt_buffer);
        xlt_file->line_no++;
        bytes_read = read_line ((Int32) MAX_IP_LINE_LEN, buffer, xlt_file);

        if (bytes_read < 0)
        {
            if (feof (xlt_file->file_ptr) != 0)
            {
                break;
            }
            ERRORMSG ("error reading translation file");
            show_file_context (xlt_file, NULL);
            explain_error ();
            ret_status = FALSE;
            continue;
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        buffer[MAX_IP_LINE_LEN - 2] = (char) '\n';
        buffer[MAX_IP_LINE_LEN - 1] = (char) '\0';
        comment = STRCHR (buffer, XLT_COMMENT_CHAR);

        if (comment != NULL)
        {
            *comment = '\n';
            *(++comment) = '\0';
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        end_of_line = STRCHR (buffer, '\n');

        if (end_of_line != NULL)
        {
            *end_of_line = ' ';
        }
        chars_read += strlen (buffer);

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        if ((xlt_hdr_array[HDR_XLT_FROM].present != TRUE)
            || (xlt_hdr_array[HDR_XLT_SUBSTCHAR].present != TRUE)
            || (xlt_hdr_array[HDR_XLT_TO].present != TRUE))
        {
            hdr_no = header_type (xlt_file, buffer, xlt_hdr_array);

            switch (hdr_no)
            {
                case HDR_UNKNOWN:
                    break;
                case HDR_XLT_FROM:
                case HDR_XLT_SUBSTCHAR:
                case HDR_XLT_TO:
                    if (xlt_hdr_array[hdr_no].present == TRUE)
                    {
                        ERRORMSG_1 ("header `%s' repeated in translation file",
                                    xlt_hdr_array[hdr_no].h_string);
                        show_file_context (xlt_file, buffer);
                        ret_status = FALSE;
                    }
                    else
                    {
                        parse_header (xlt_file, buffer, &xlt_hdr_array[hdr_no]);
                    }
                    break;
                default:
                    INTERNAL_ERROR ("rd_xlt_file");
                    break;
            }
            continue;
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        fields_read = sscanf (xlt_hdr_array[HDR_XLT_SUBSTCHAR].value, "%hd",
                              &subst_char);
        if ((fields_read != 1) || (subst_char < 0) || (subst_char > 255))
        {
            ERRORMSG_2 ("invalid `%s' header value in translation file: `%s'",
                        xlt_hdr_array[HDR_XLT_SUBSTCHAR].h_string,
                        xlt_hdr_array[HDR_XLT_SUBSTCHAR].value);
            ret_status = FALSE;
            break;
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        if (chars_read <= XLT_BUFFER_SIZE)
        {
            strcat ((char *) xlt_buffer, buffer);
        }
        else
        {
            ERRORMSG_1 ("too many characters in translation file: %d",
                        chars_read);
            show_file_context (xlt_file, buffer);
            ret_status = FALSE;
            break;
        }
    }
    f_close (xlt_file);

    /*-
    **------------------------------------------------------------------------
    ** The contents of the translation file have been read, now parse them to
    ** create the translation table.
    **------------------------------------------------------------------------
    */
    if (xlt_hdr_array[HDR_XLT_FROM].present != TRUE)
    {
        ERRORMSG_1 ("header `%s' not found in translation file",
                    xlt_hdr_array[HDR_XLT_FROM].h_string);
        ret_status = FALSE;
    }
    else if (xlt_hdr_array[HDR_XLT_SUBSTCHAR].present != TRUE)
    {
        ERRORMSG_1 ("header `%s' not found in translation file",
                    xlt_hdr_array[HDR_XLT_SUBSTCHAR].h_string);
        ret_status = FALSE;
    }
    else if (xlt_hdr_array[HDR_XLT_TO].present != TRUE)
    {
        ERRORMSG_1 ("header `%s' not found in translation file",
                    xlt_hdr_array[HDR_XLT_TO].h_string);
        ret_status = FALSE;
    }
    else
    {
        xlt_tbl_struct->xlt_from = STRDUP (xlt_hdr_array[HDR_XLT_FROM].value);
        xlt_tbl_struct->xlt_to = STRDUP (xlt_hdr_array[HDR_XLT_TO].value);
    }

    /*-
    **------------------------------------------------------------------------
    ** The contents of the translation file have been read, now parse them to
    ** create the translation table.
    **------------------------------------------------------------------------
    */
    if (ret_status == TRUE)
    {
        ret_status = pars_xlt_tbl (xlt_buffer, xlt_tbl_struct, subst_char);
    }

    free (xlt_buffer);
    return (ret_status);
}                               /* rd_xlt_tbl */



/*-
**============================================================================
**
** FUNCTION
**
**      set_dec_tbl
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    set_dec_tbl (CONST Int16 enc_tbl_len,
                                         CONST char *encode_table,
                                         CONST Int16 dec_tbl_len,
                                         Int16 *decode_table)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    set_dec_tbl (enc_tbl_len, encode_table,
                                         dec_tbl_len, decode_table)
        CONST Int16             enc_tbl_len;
        CONST char             *encode_table;
        CONST Int16             dec_tbl_len;
        Int16                  *decode_table;
#endif                          /* (ANSI_SYNTAX) */
{
    Int16                   i;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    for (i = 0; i < dec_tbl_len; i++)
    {
        decode_table[i] = INV_TABLE_ENTRY;
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    for (i = 0; i < enc_tbl_len; i++)
    {
        if (decode_table[encode_table[i]] == INV_TABLE_ENTRY)
        {
            decode_table[encode_table[i]] = i;
        }
        else
        {
            ERRORMSG_1 ("invalid encoding table - character 0x%02X duplicated",
                        encode_table[i]);
            vv_exit ();
        }
    }
}                               /* set_dec_tbl */



/*-
**============================================================================
**
** FUNCTION
**
**      set_xlt_tbl
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    set_xlt_tbl (File_Info *xlt_file,
					 Xlt_Struct *xlt_table,
					 Header_Struct *xlt_hdr_array)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    set_xlt_tbl (xlt_file, xlt_table, xlt_hdr_array)
	File_Info              *xlt_file;
	Xlt_Struct             *xlt_table;
	Header_Struct          *xlt_hdr_array;
#endif                          /* (ANSI_SYNTAX) */
{
    Int16                   status;

    /*-
    **------------------------------------------------------------------------
    ** If an extension has not been provided, use the default extension
    ** for translation table files.
    **------------------------------------------------------------------------
    */
#if (STICKY_DEFAULTS)
	apply_defaults (NULL, DEF_XLT_EXT, xlt_file->file_spec);
#endif                          /* (STICKY_DEFAULTS) */

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    xlt_table->xlt_from = NULL;
    xlt_table->xlt_to = NULL;
    status = rd_xlt_tbl (xlt_hdr_array, xlt_file, xlt_table);

    if (status < 0)
    {
	FATALMSG_1 ("error reading translation table file `%s'",
		    xlt_file->file_spec);
	vv_exit ();
    }

    LOGMSG_1 ("\nTranslation table source:          %s", xlt_file->file_spec);
    LOGMSG_2 ("Translation performed:             `%s' to `%s'",
	      xlt_table->xlt_from, xlt_table->xlt_to);

}                               /* set_xlt_tbl */



/*-
**============================================================================
**
** FUNCTION
**
**      show_file_context
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    show_file_context (CONST File_Info *file,
                                               CONST char *file_text)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    show_file_context (file, file_text)
        CONST File_Info        *file;
        CONST char             *file_text;
#endif                          /* (ANSI_SYNTAX) */
{
    if (file_text != NULL)
    {
        INFOMSG_2 ("context: line %ld of file `%s'", 
                   file->line_no, file->file_spec);
        INFOMSG_1 ("text: `%s'", file_text);
    }
    else
    {
        INFOMSG_2 ("context: line %ld of file `%s'",
                   file->line_no, file->file_spec);
    }
}                               /* show_file_context */



/*-
**============================================================================
**
** FUNCTION
**
**      vstrchr
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    char                   *vstrchr (CONST char *str, CONST int ch)
#else                           /* NOT (ANSI_SYNTAX) */
    char                   *vstrchr (str, ch)
        CONST char             *str;
        CONST int               ch;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     i;

    for (i = 0; str[i] != '\0'; i++)
    {
        if (str[i] == (char) ch)
        {
            return ((char *) &str[i]);
        }
    }
    return (NULL);
}                               /* vstrchr */



/*-
**============================================================================
**
** FUNCTION
**
**      vstricmp
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    int                     vstricmp (CONST char *str1, CONST char *str2)
#else                           /* NOT (ANSI_SYNTAX) */
    int                     vstricmp (str1, str2)
        CONST char             *str1;
        CONST char             *str2;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     i;
    int                     u1;
    int                     u2;

    i = 0;
    while (((str1[i] != '\0') || (str2[i] != '\0')))
    {
        u1 = (int) TOUPPER (str1[i]);
        u2 = (int) TOUPPER (str2[i]);
        if (u1 != u2)
        {
            return (u1 - u2);
        }
        i++;
    }
    return (0);
}                               /* vstricmp */



/*-
**============================================================================
**
** FUNCTION
**
**      vstrncmp
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    int                     vstrncmp (CONST char *str1, CONST char *str2,
                                       CONST int max_chars)
#else                           /* NOT (ANSI_SYNTAX) */
    int                     vstrncmp (str1, str2, max_chars)
        CONST char             *str1;
        CONST char             *str2;
        CONST int               max_chars;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     i;

    i = 0;
    while ((i < max_chars) && ((str1[i] != '\0') || (str2[i] != '\0')))
    {
        if (str1[i] != str2[i])
        {
            return ((int) (str1[i] != str2[i]));
        }
        i++;
    }
    return (0);
}                               /* vstrncmp */



/*-
**============================================================================
**
** FUNCTION
**
**      vstrnicmp
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    int                     vstrnicmp (CONST char *str1, CONST char *str2,
                                       CONST int max_chars)
#else                           /* NOT (ANSI_SYNTAX) */
    int                     vstrnicmp (str1, str2, max_chars)
        CONST char             *str1;
        CONST char             *str2;
        CONST int               max_chars;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     i;
    int                     u1;
    int                     u2;

    i = 0;
    while ((i < max_chars) && ((str1[i] != '\0') || (str2[i] != '\0')))
    {
        u1 = (int) TOUPPER (str1[i]);
        u2 = (int) TOUPPER (str2[i]);
        if (u1 != u2)
        {
            return (u1 - u2);
        }
        i++;
    }
    return (0);
}                               /* vstrnicmp */



/*-
**============================================================================
**
** FUNCTION
**
**      vstrrchr
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    char                   *vstrrchr (CONST char *str, CONST int ch)
#else                           /* NOT (ANSI_SYNTAX) */
    char                   *vstrrchr (str, ch)
        CONST char             *str;
        CONST int               ch;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     i;
    int                     length;

    length = strlen (str);

    for (i = (length - 1); i >= 0; i--)
    {
        if (str[i] == (char) ch)
        {
            return ((char *) &str[i]);
        }
    }
    return (NULL);
}                               /* vstrrchr */



/*-
**============================================================================
**
** FUNCTION
**
**      vstrrstr
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    char                   *vstrrstr (CONST char *str, CONST char *search_str)
#else                           /* NOT (ANSI_SYNTAX) */
    char                   *vstrrstr (str, search_str)
        CONST char             *str;
        CONST char             *search_str;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     i;
    int                     length;

    length = strlen (str);

    for (i = (length - 1); i >= 0; i--)
    {
        if (vstrchr (search_str, str[i]) != NULL)
        {
            return ((char *) &str[i]);
        }
    }
    return (NULL);
}                               /* vstrrstr */



/*-
**============================================================================
**
** FUNCTION
**
**      vstrstr
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    char                   *vstrstr (CONST char *str, CONST char *search_str)
#else                           /* NOT (ANSI_SYNTAX) */
    char                   *vstrstr (str, search_str)
        CONST char             *str;
        CONST char             *search_str;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     idx;

    for (idx = 0; str[idx] != '\0'; idx++)
    {
        if (vstrchr (search_str, str[idx]) != NULL)
        {
            return ((char *) &str[idx]);
        }
    }
    return (NULL);
}                               /* vstrstr */



/*-
**============================================================================
**
** FUNCTION
**
**      vv_timestamp
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    char                   *vv_timestamp (TIME_T time_val)
#else                           /* NOT (ANSI_SYNTAX) */
    char                   *vv_timestamp (time_val)
        TIME_T                  time_val;
#endif                          /* (ANSI_SYNTAX) */
{
    static char             time_string[32];    /* YYYY-MM-DD-HH:MM:SS GMT */
    int                     year;
    int                     month;
    int                     mday;
    int                     hours;
    int                     mins;
    int                     secs;
    TIME_T                  day_secs;
    TIME_T                  days_this_month;
    TIME_T                  year_secs;
    TIME_T                  leap_year_secs;
    TIME_T                  this_year_secs;

    /*-
    **------------------------------------------------------------------------
    ** We can't cope with times before 1-Jan-1970 00:00:00.
    **------------------------------------------------------------------------
    */
    DEBUG_1 ("vv_timestamp: time_t value provided = %ld", time_val);

    if (time_val < 0)
    {
        WARNMSG_1 ("time_t value < 0 (%ld) - converted to 0\n", time_val);
        time_val = 0;
    }

    day_secs = 86400L;
    year_secs = 365L * 86400L;
    leap_year_secs = year_secs + 86400L;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    for (year = 1970;; year++)
    {

        if (LEAPYEAR (year))
        {
            this_year_secs = leap_year_secs;
        }
        else
        {
            this_year_secs = year_secs;
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        if (time_val >= this_year_secs)
        {
            time_val -= this_year_secs;
        }
        else
        {
            break;
        }
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    for (month = 0;; month++)
    {
    	days_this_month = month_days[month];
	
    	if ((month == 1) && (LEAPYEAR (year)))
	{
	    ++days_this_month;
	}
	
        if (time_val >= (day_secs * days_this_month))
        {
            time_val -= day_secs * days_this_month;
        }
        else
        {
            break;
        }
    }
    ++month;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    mday = (int) (time_val / day_secs);
    ++mday;
    time_val = time_val % day_secs;

    hours = (int) (time_val / 3600L);
    time_val = time_val % 3600L;

    mins = (int) (time_val / 60L);
    time_val = time_val % 60L;

    secs = (int) time_val;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    sprintf (time_string, VV_TIMESTAMP_FORMAT,
             year, month, mday, hours, mins, secs);
    strcat (time_string, " GMT");
    DEBUG_1 ("vv_timestamp: VVE timestamp string return value = `%s'\n",
             time_string);
    return (time_string);
}                               /* vv_timestamp */



/*-
**============================================================================
**
** FUNCTION
**
**      vstrdup
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    char                   *vstrdup (CONST char *str)
#else                           /* NOT (ANSI_SYNTAX) */
    char                   *vstrdup (str)
        CONST char             *str;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *tmp_ptr;
    SIZE_T                  no_bytes;

    no_bytes = (SIZE_T) (strlen (str) + 1) * sizeof (char);
    tmp_ptr = (char *) malloc (no_bytes);

    if (tmp_ptr == NULL)
    {
        FATALMSG_2 ("couldn't allocate %d bytes to copy string `%s'",
                    no_bytes, str);
        vv_exit ();
    }
    strcpy (tmp_ptr, str);
    return (tmp_ptr);
}                               /* vstrdup */



/*-
**============================================================================
**
** FUNCTION
**
**      vstrtrws
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    char                   *vstrtrws (char *str)
#else                           /* NOT (ANSI_SYNTAX) */
    char                   *vstrtrws (str)
        char                   *str;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *end_ptr;
    int                     length;

    if ((str == NULL) || (*str == '\0'))
    {
        return (str);
    }

    length = strlen (str);
    end_ptr = str + length - 1;

    while ((end_ptr >= str) && (isspace (*end_ptr)))
    {
        *end_ptr = '\0';
        --end_ptr;
    }
    return (str);
}                               /* vstrtrws */
