/*-
******************************************************************************
******************************************************************************
**
**  ARCHIVE HEADER INFORMATION
**
**  @C-file{
**      FILENAME    = "vvdecode.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
**
**
**  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 VVDECODE                1
#define VVCODE_MAIN             1

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


/*-
**----------------------------------------------------------------------------
** Include the machine specific definitions
**----------------------------------------------------------------------------
*/
#include "checkos.h"
#include "machine.h"
#include "local.h"
#include "globals.h"
#include "specific.h"
#include "vvutils.h"
#include "vveparse.h"


/*-
**----------------------------------------------------------------------------
** Forward declarations of functions in this file
**----------------------------------------------------------------------------
*/
int main                        ARGS ((CONST int argc,
				    CONST char *argv[]));
Boolean choose_xlt_table        ARGS ((CONST Qualifier_Struct *xlt_qualifier,
                                    CONST Qualifier_Struct *idx_qualifier,
                                    CONST char *from_charset,
                                    CONST char *to_charset,
                                    File_Info *xlt_file));
void decode                     ARGS ((void));
Int16 decode_bytes              ARGS ((CONST Int16 bytes_left,
				    CONST char *ip_buf_ptr,
				    char *op_buf_ptr));
void find_next_part             ARGS ((CONST Int16 part_needed,
				    File_Info *ip_file_ptr));
void initialize                 ARGS ((void));
void open_ip_file               ARGS ((CONST Qualifier_Struct *ip_qualifier,
				    File_Info *ip_file));
void open_next_part             ARGS ((CONST Int16 part_needed,
				    File_Info *ip_file));
void open_op_file               ARGS ((CONST Qualifier_Struct *op_qualifier,
				    CONST Int16 overwrite_flag,
				    CONST char *vve_hdr_spec,
				    File_Info *op_file));
char *parse_enc_tbl             ARGS ((CONST char *line_ptr,
				    Header_Struct *hdr_struct,
				    File_Info *ip_file));
Boolean prs_idx_file            ARGS ((CONST char *from_charset,
				    CONST char *to_charset,
				    File_Info *idx_file,
				    File_Info *xlt_file));
void rdfi_headers               ARGS ((CONST Int16 decoding_type,
				    File_Info *ip_file,
				    Header_Struct *vve_hdr_array));
void rdfi_trailers              ARGS ((CONST Int16 decoding_type,
				    File_Info *ip_file,
				    File_Info *vve_file,
				    Header_Struct *vve_hdr_array));
void set_decoding_type          ARGS ((CONST Qualifier_Struct *uu_qualifier,
				    CONST Qualifier_Struct *xx_qualifier,
				    Int16 *decoding_type,
				    char **vve_line_prefix,
				    char **vve_eol_marker));
void set_enc_tbl                ARGS ((CONST Qualifier_Struct *enc_qualifier,
				    CONST Int16 enc_tbl_len,
				    char *encode_table));
void set_format                 ARGS ((CONST Qualifier_Struct *fmt_qualifier,
				    CONST File_Info *vve_file,
				    CONST char *vve_os_str,
				    CONST char *fmt_str_array[],
				    File_Info *op_file));
Boolean set_idx_file            ARGS ((CONST Qualifier_Struct *idx_qualifier,
				    File_Info *idx_file));
void set_mode                   ARGS ((CONST Qualifier_Struct *mode_qualifier,
				    CONST File_Info *vve_file,
				    CONST char *mode_str_array[],
				    File_Info *op_file));
void set_pad_char               ARGS ((CONST Qualifier_Struct *pad_qualifier,
				    CONST File_Info *op_file,
				    char *pad_char));
void set_record_len             ARGS ((CONST Qualifier_Struct *rec_qualifier,
				    CONST File_Info *vve_file,
				    File_Info *op_file));
void set_timestamp              ARGS ((CONST Qualifier_Struct *time_qualifier,
				    CONST File_Info *vve_file,
				    File_Info *op_file));
void terminate                  ARGS ((void));
void usage_error                ARGS ((CONST char *reason));



/*-
**============================================================================
**
** FUNCTION
**
**      choose_xlt_file
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      xlt_file
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      TRUE                -   a candidate translation file was found
**      FALSE               -   a candidate translation file could not be
**                              identified
**
**============================================================================
*/
#if (ANSI_SYNTAX)
Boolean choose_xlt_table (CONST Qualifier_Struct *xlt_qualifier,
                          CONST Qualifier_Struct *idx_qualifier,
                          CONST char *from_charset,
                          CONST char *to_charset,
			  File_Info *xlt_file)
#else                           /* NOT (ANSI_SYNTAX) */
Boolean choose_xlt_table (xlt_qualifier, idx_qualifier, xlt_file,
			  from_charset, to_charset)
    CONST Qualifier_Struct     *xlt_qualifier;
    CONST Qualifier_Struct     *idx_qualifier;
    CONST char                 *from_charset;
    CONST char                 *to_charset;
    File_Info                  *xlt_file;
#endif                          /* (ANSI_SYNTAX) */
{
    File_Info               idx_file;
    Boolean                 status;

    /*-
    **------------------------------------------------------------------------
    ** If the /TRANSLATION_FILE qualifier was specified, we use its value as
    ** the file specification for the translation table file.
    **------------------------------------------------------------------------
    */
    if (xlt_qualifier->present == TRUE)
    {
	strcpy (xlt_file->file_spec, xlt_qualifier->value);

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

	return (TRUE);
    }

    /*-
    **------------------------------------------------------------------------
    ** The /TRANSLATION_FILE qualifier was not specified, so we look for an
    ** index file that might specify which translation file to use.  The
    ** function set_idx_file() will determine which index file to use or
    ** return FALSE if it couldn't.
    **------------------------------------------------------------------------
    */
    status = set_idx_file (idx_qualifier, &idx_file);

    if (status != TRUE)
    {
	LOGMSG ("\nTranslation index file:            none");
	return (FALSE);
    }
    LOGMSG_1 ("\nTranslation index file:            %s", idx_file.file_spec);

    /*-
    **------------------------------------------------------------------------
    ** Now parse the index file to see if it contains an entry for the
    ** desired character set translation.
    **------------------------------------------------------------------------
    */
    status = prs_idx_file (from_charset, to_charset, &idx_file, xlt_file);
    return (status);
}                               /* choose_xlt_table */



/*-
**============================================================================
**
** FUNCTION
**
**      DEC_BYTE
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#define DEC_BYTE(_from,_to)       \
    if (G_decode_table[(int) (_from)] == INV_TABLE_ENTRY)\
    {\
        FATALMSG_4 ("invalid character %d (0x%02X) on line %ld of file `%s'",\
                    (int) (_from), (int) (_from), G_ip_file.line_no,\
                    G_ip_file.file_spec);\
        vv_exit ();\
    }\
    else\
    {\
        _to = G_decode_table[(int) (_from)];\
    }



/*-
**============================================================================
**
** FUNCTION
**
**      decode
**
** DESCRIPTION
**
**      [tbs]
**
**      VVE     O/P     O/P             Conversion
**      Fmt     Fmt     Func            Type
**      --------------------------------------------------------------------
**      FIX     FIX     write_bytes()   byte copy, filling records and
**                                      wrapping round record boundaries.
**                                      If necessary, the last O/P record 
**                                      will be padded.
**      FIX     STM     write_bytes()   byte copy.
**      FIX     VAR     write_bytes()   byte copy, filling records to 
**                                      maximum length and wrapping data
**                                      round record boundaries.
**
**      STM     FIX     write_bytes()   byte, filling records and
**                                      wrapping round record boundaries.
**                                      If necessary, the last O/P record 
**                                      will be padded.
**      STM     STM     write_bytes()   byte copy
**      STM     VAR     write_bytes()   byte copy, filling records to 
**                                      maximum length and wrapping data
**                                      round record boundaries.
**
**      VAR     FIX     write_bytes()   record byte copy, filling records
**                                      and wrapping round record
**                                      boundaries.  If necessary, the last
**                                      O/P record will be padded.
**      VAR     STM     write_bytes()   record byte copy
**      VAR     VAR     write_record()  record copy, truncating any
**                                      overlong records
**      --------------------------------------------------------------------
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    decode (void)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    decode ()
#endif                          /* (ANSI_SYNTAX) */
{
    Int16                   bytes_left;
    SIZE_T                  idx;
    Int32                   buffer_size;
    Int16                   bytes_decoded;
    SIZE_T                  bytes_expected;
    Int16                   decode_state;
    Int16                   hdr_no;
    char                   *ip_buf_ptr;
    Int16                   no_bytes_encoded;
    char                   *op_buf_ptr;
    Int16                   part_ending;
    Int32                   rec_bytes_left;
    char                   *record_buffer;
    Int32                   record_length;
    char                    var_rec_size[32];

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    LOGMSG_2 ("\nPart %3d I/P file:                 %s",
              G_ippt_no, G_ip_file.file_spec);
    LOGMSG_2 ("Part %3d starts on line:           %ld",
              G_ippt_no, G_ip_file.line_no);

    /*-
    **------------------------------------------------------------------------
    ** Allocate space to decode data.  If the VVE file contains variable
    ** length records, the decoded data bytes are accumulated in a buffer
    ** before being written.
    **
    ** The size of IO_BUFFER_SIZE should be chosen so that most of the
    ** records received can be accommodated.  If an unusually large record is
    ** encountered, a larger buffer will be allocated on demand.
    **------------------------------------------------------------------------
    */
    buffer_size = IO_BUFFER_SIZE;
    record_buffer = allocate_buffer ((SIZE_T) buffer_size, "record_buffer");
    decode_state = DECODE_REC_LEN;
    op_buf_ptr = record_buffer;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    for (;;)
    {
        ip_buf_ptr = rd_next_line ((Int32) IP_BUFFER_SIZE, G_vv_prefix,
                                   G_ip_buffer, &G_ip_file, &G_ippart_bytes);

        if (ip_buf_ptr == NULL)
        {
            if (feof (G_ip_file.file_ptr))
            {
                FATALMSG ("premature EOF encountered while decoding");
            }
            else
            {
                FATALMSG ("error reading encoded data");
            }
            show_file_context (&G_ip_file, NULL);
            explain_error ();
            vv_exit ();
        }

        /*-
        **--------------------------------------------------------------------
        ** Decode the byte count character.  If it's less than zero, something
        ** has gone wrong.
        **--------------------------------------------------------------------
        */
        DEC_BYTE (ip_buf_ptr[0], no_bytes_encoded);
        ip_buf_ptr++;

        if (no_bytes_encoded < 0)
        {
            FATALMSG_1 ("invalid encoded byte count (%d)", no_bytes_encoded);
            show_file_context (&G_ip_file, ip_buf_ptr);
            vv_exit ();
        }

        /*-
        **--------------------------------------------------------------------
        ** If the byte count is zero, we're at the end of this segment.  The
        ** next line must be an 'end' or 'skipfrom' header line.
        **--------------------------------------------------------------------
        */
        if (no_bytes_encoded == 0)
        {
            ip_buf_ptr = rd_next_line ((Int32) IP_BUFFER_SIZE, G_vv_prefix, 
                                       G_ip_buffer, &G_ip_file,
                                       &G_ippart_bytes);

            if (ip_buf_ptr == NULL)
            {
                FATALMSG ("error reading encoded data");
                show_file_context (&G_ip_file, NULL);
                explain_error ();
                vv_exit ();
            }


            /*-
            **----------------------------------------------------------------
            **
            **----------------------------------------------------------------
            */
            hdr_no = header_type (&G_ip_file, ip_buf_ptr, G_vvehdr);

            switch (hdr_no)
            {
                /*-
                **------------------------------------------------------------
                **
                **------------------------------------------------------------
                */
		case HDR_END:
		    LOGMSG_2 ("Part %3d ends on line:             %ld",
			      G_ippt_no, G_ip_file.line_no);
                    return;

                /*-
                **------------------------------------------------------------
                **
                **------------------------------------------------------------
                */
                case HDR_SKIPTO:
                    if (G_dec_type != VV_DECODING)
                    {
			FATALMSG_1 ("`%s' header only allowed in VVE files",
				    G_vvehdr[HDR_SKIPTO].h_string);
			show_file_context (&G_ip_file, ip_buf_ptr);
                        vv_exit ();
                    }
                    parse_header (&G_ip_file, ip_buf_ptr, &G_vvehdr[hdr_no]);
                    sscanf (G_vvehdr[hdr_no].value, "%hd", &part_ending);

                    G_ippt_no++;
                    if (part_ending != G_ippt_no)
                    {
			FATALMSG_2 ("Unexpected `%s %d'",
                                    G_vvehdr[HDR_SKIPTO].h_string, part_ending);
			show_file_context (&G_ip_file, ip_buf_ptr);
                        vv_exit ();
                    }

		    LOGMSG_2 ("Part %3d ends on line:             %ld",
			      part_ending - 1, G_ip_file.line_no);
		    find_next_part (G_ippt_no, &G_ip_file);
                    continue;

                /*-
                **------------------------------------------------------------
                **
                **------------------------------------------------------------
                */
                default:
		    FATALMSG ("invalid header found");
		    show_file_context (&G_ip_file, ip_buf_ptr);
		    vv_exit ();
	    }
	}

	/*-
	**--------------------------------------------------------------------
	** Check that there are enough characters in the line. The encoded
	** characters are always written in fours.
	**--------------------------------------------------------------------
	*/
	bytes_expected = (SIZE_T) (4 * ((no_bytes_encoded + 2) / 3));

	if (G_eol_marker != NULL)
	{
	    bytes_expected += strlen (G_eol_marker);
	}

	if (strlen (ip_buf_ptr) < bytes_expected)
	{
	    ERRORMSG_2 ("%d characters expected - %d found",
		        bytes_expected, strlen (ip_buf_ptr));
	    show_file_context (&G_ip_file, ip_buf_ptr);
	    break;
	}

	/*-
        **--------------------------------------------------------------------
        ** 
        **--------------------------------------------------------------------
        */
        if (G_vv_file.format != FMT_VARIABLE)
        {

            /*-
            **----------------------------------------------------------------
            ** The VVE file was encoded in stream or fixed mode, so decode
            ** this line of bytes and then write them, updating the CRC as we
            ** go.
            **----------------------------------------------------------------
            */
            op_buf_ptr = record_buffer;
            bytes_left = no_bytes_encoded;

            while (bytes_left > 0)
            {
                bytes_decoded = decode_bytes (bytes_left, ip_buf_ptr,
                                              op_buf_ptr);
                ip_buf_ptr += 4;
                op_buf_ptr += bytes_decoded;
                bytes_left -= bytes_decoded;
            }

            for (idx = 0; idx < (SIZE_T) no_bytes_encoded; idx++)
            {
                UPDATE_CRC32 (G_op_file.crc32, record_buffer[idx]);
            }

            /*-
            **----------------------------------------------------------------
            ** If the /TRANSLATE command qualifier was specified, translate
            ** the bytes read to the destination character set.
            **----------------------------------------------------------------
            */
            if (Q_TRANSLATE)
            {
                for (idx = 0; idx < (SIZE_T) no_bytes_encoded; idx++)
                {
                    record_buffer[idx] =
                        (char) G_xlt_table.xlt_tbl[(Byte) record_buffer[idx]];
                }
            }


            write_bytes ((Int32) no_bytes_encoded, record_buffer, &G_op_file);
            G_op_file.bytecount += (Int32) no_bytes_encoded;
        }
        else
        {

            /*-
            **----------------------------------------------------------------
            ** The VVE file contains variable length records which are much
            ** more difficult to process.
            **
            ** If at the beginning of a record, treat the first two decoded
            ** bytes as the record length.
            **----------------------------------------------------------------
            */
            if (decode_state == DECODE_REC_LEN)
            {
                bytes_decoded = decode_bytes (2, ip_buf_ptr, var_rec_size);
                record_length = 256L * (((Int32) var_rec_size[0]) & 0xff);
                record_length += (((Int32) var_rec_size[1]) & 0xff);

                /*-
                **------------------------------------------------------------
                **
                **------------------------------------------------------------
                */
                if ((record_length < 0L) || (record_length > MAX_VV_RECORD_LEN))
                {
		    FATALMSG_1 ("invalid record length recorded (%ld)",
                                (Int32) record_length);
		    show_file_context (&G_ip_file, ip_buf_ptr);
                    vv_exit ();
                }

                /*-
                **------------------------------------------------------------
                ** If this record is longer than the current buffer size, we
                ** need to reallocate a larger buffer that most of the
                ** records received can be accommodated.  If an unusually
                ** large record is encountered, a larger buffer will be
                ** allocated on demand.
                **------------------------------------------------------------
                */
                if (record_length > buffer_size)
                {
                    record_buffer = (char *) realloc (record_buffer,
                                                      (SIZE_T) record_length);
                    if (record_buffer == NULL)
                    {
			FATALMSG_1 ("couldn't reallocate %ld bytes \
for the record buffer", record_length);
                        vv_exit ();
                    }
                    buffer_size = record_length;
                    DEBUG_1 ("decode: reallocated %ld bytes for the \
record buffer", record_length);
                }

                /*-
                **------------------------------------------------------------
                ** The buffer is definitely big enough to hold the record, so
                ** start decoding the encoded bytes into the buffer.
                **
                ** NOTE
                **
                ** We need to discard the first two decoded bytes which
                ** contain the record length to avoid a full length record
                ** overflowing the buffer.
                **------------------------------------------------------------
                */
                bytes_left = no_bytes_encoded;
                op_buf_ptr = record_buffer;
                bytes_decoded = decode_bytes (bytes_left, ip_buf_ptr,
                                              op_buf_ptr);
                ip_buf_ptr += 4;
                op_buf_ptr[0] = op_buf_ptr[2];
                ++op_buf_ptr;
                bytes_left -= bytes_decoded;

                /*-
                **------------------------------------------------------------
                **
                **------------------------------------------------------------
                */
                while (bytes_left > 0)
                {
                    bytes_decoded = decode_bytes (bytes_left, ip_buf_ptr,
                                                  op_buf_ptr);
                    ip_buf_ptr += 4;
                    op_buf_ptr += bytes_decoded;
                    bytes_left -= bytes_decoded;
                }

                decode_state = DECODE_DATA;
                rec_bytes_left = record_length;
                no_bytes_encoded -= 2;
                rec_bytes_left -= (Int32) no_bytes_encoded;
            }
            else
            {

                /*-
                **------------------------------------------------------------
                ** We're part of the way through a record, so just decode the
                ** bytes and append the result to the record buffer.
                **------------------------------------------------------------
                */
                bytes_left = no_bytes_encoded;

                while (bytes_left > 0)
                {
                    bytes_decoded = decode_bytes (bytes_left, ip_buf_ptr,
                                                  op_buf_ptr);
                    ip_buf_ptr += 4;
                    op_buf_ptr += bytes_decoded;
                    bytes_left -= bytes_decoded;
                }
                rec_bytes_left -= (Int32) no_bytes_encoded;
            }


            /*-
            **----------------------------------------------------------------
            ** If all of this record has now been read and decoded into 
            ** 'record_buffer', write it to the output file.
            **----------------------------------------------------------------
            */
            if (rec_bytes_left <= 0L)
            {
                for (idx = 0; idx < (SIZE_T) record_length; idx++)
                {
                    UPDATE_CRC32 (G_op_file.crc32, record_buffer[idx]);
                }

                /*-
                **------------------------------------------------------------
                ** If the /TRANSLATE command qualifier was specified,
                ** translate the bytes read to the destination character set.
                **------------------------------------------------------------
                */
                if (Q_TRANSLATE)
                {
                    for (idx = 0; idx < (SIZE_T) record_length; idx++)
                    {
                        record_buffer[idx] =
                         (char) G_xlt_table.xlt_tbl[(Byte) record_buffer[idx]];
                    }
                }

                /*-
                **------------------------------------------------------------
                ** 
                **------------------------------------------------------------
                */
                if (G_op_file.format == FMT_VARIABLE)
                {
                    write_record (record_length, record_buffer, &G_op_file);
                }
                else
                {
                    write_bytes (record_length, record_buffer, &G_op_file);
                }
                decode_state = DECODE_REC_LEN;
                G_op_file.bytecount += (Int32) record_length;
            }
        }
    }                           /* end for (;;) */
    free (record_buffer);
}                               /* decode */



/*-
**============================================================================
**
** FUNCTION
**
**      decode_bytes
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Int16                   decode_bytes (CONST Int16 bytes_left,
                                          CONST char *ip_buf_ptr,
                                          char *op_buf_ptr)
#else                           /* NOT (ANSI_SYNTAX) */
    Int16                   decode_bytes (bytes_left, ip_buf_ptr, op_buf_ptr)
        CONST Int16             bytes_left;
        CONST char             *ip_buf_ptr;
        char                   *op_buf_ptr;
#endif                          /* (ANSI_SYNTAX) */
{
    register Int16          tmp0;
    register Int16          tmp1;
    register Int16          tmp2;
    register Int16          tmp3;
    register Int16          c1;
    register Int16          c2;
    register Int16          c3;
    register Int16          bytes_decoded;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    bytes_decoded = 0;

    if (bytes_left >= 1)
    {
        DEC_BYTE (ip_buf_ptr[0], tmp0);
        DEC_BYTE (ip_buf_ptr[1], tmp1);
        c1 = (Int16) (tmp0 << 2) | (tmp1 >> 4);
        op_buf_ptr[0] = (char) c1;
        ++bytes_decoded;
    }

    if (bytes_left >= 2)
    {
        DEC_BYTE (ip_buf_ptr[2], tmp2);
        c2 = (Int16) (tmp1 << 4) | (tmp2 >> 2);
        op_buf_ptr[1] = (char) c2;
        ++bytes_decoded;
    }

    if (bytes_left >= 3)
    {
        DEC_BYTE (ip_buf_ptr[3], tmp3);
        c3 = (Int16) (tmp2 << 6) | (tmp3);
        op_buf_ptr[2] = (char) c3;
        ++bytes_decoded;
    }
    return (bytes_decoded);
}                               /* decode_bytes */



/*-
**============================================================================
**
** FUNCTION
**
**      find_next_part
**
** DESCRIPTION
**
**      [tbs]
**
**      NOTE:   if every part is in a file of its own there will be an
**              overhead of reading each file TWICE before trying to open
**              the new file for the next part.
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    find_next_part (CONST Int16 part_needed,
                                            File_Info *ip_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    find_next_part (part_needed, ip_file)
        CONST Int16             part_needed;
        File_Info              *ip_file;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *buf_ptr;
    Int16                   hdr_no;
    Int32                   start_line_no;
    Int16                   search_pass;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (part_needed > MAX_VV_PARTS)
    {
	FATALMSG_1 ("maximum number of parts reached (%d)", MAX_VV_PARTS);
        vv_exit ();
    }
    search_pass = 1;
    start_line_no = ip_file->line_no;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    buf_ptr = rd_next_line ((Int32) IP_BUFFER_SIZE, G_vv_prefix, G_ip_buffer, 
                            ip_file, &G_ippart_bytes);
    for (;;)
    {
        if (buf_ptr == NULL)
        {

            /*-
            **----------------------------------------------------------------
            **
            **----------------------------------------------------------------
            */
            if (feof (ip_file->file_ptr))
            {
                /*-
                **------------------------------------------------------------
                **
                **------------------------------------------------------------
                */
                if (ip_file->pipe_flag == TRUE)
                {
		    FATALMSG_1 ("EOF encountered on search pass %d", 
                                search_pass);
		    show_file_context (&G_ip_file, NULL);
                    vv_exit ();
                }
                else
                {
                    DEBUG_1 ("find_next_part: EOF encountered on search \
pass %d", search_pass);
                    DEBUG_2 ("find_next_part: Context: line %ld of file `%s'",
                             ip_file->line_no, ip_file->file_spec);
                }

                /*-
                **------------------------------------------------------------
                **
                **------------------------------------------------------------
                */
                switch (search_pass)
                {
                    case 1:
                        DEBUG_1 ("find_next_part: Rewinding file `%s'",
                                 ip_file->file_spec);
                        rewind (ip_file->file_ptr);
                        ip_file->line_no = 0;
                        search_pass = 2;
                        buf_ptr = rd_next_line ((Int32) IP_BUFFER_SIZE, 
                                                G_vv_prefix, G_ip_buffer, 
                                                ip_file, &G_ippart_bytes);
                        continue;
                    case 3:
			FATALMSG_2 ("couldn't find part %d in file `%s'",
                                    part_needed, ip_file->file_spec);
                        vv_exit ();
                    case 2:
                    default:
			FATALMSG_1 ("EOF encountered on search pass %d",
                                    search_pass);
                        show_file_context (ip_file, NULL);
                        vv_exit ();
                }
            }

            /*-
            **----------------------------------------------------------------
            **
            **----------------------------------------------------------------
            */
            else
            {
		FATALMSG ("error reading file");
		show_file_context (ip_file, NULL);
                explain_error ();
                vv_exit ();
            }
        }

        /*-
        **--------------------------------------------------------------------
        ** If on pass two, avoid redundant re-reading of the lines read on
        ** pass one.
        **--------------------------------------------------------------------
        */
        if ((search_pass == 2) && (ip_file->line_no >= start_line_no))
        {
            DEBUG_2 ("find_next_part: Couldn't find part %d in file `%s'",
                     part_needed, ip_file->file_spec);
            f_close (ip_file);
            open_next_part (part_needed, ip_file);
            search_pass = 3;
            ip_file->line_no = 0;
        }

        /*-
        **--------------------------------------------------------------------
        ** Check whether this line is a header line.  The only header lines
        ** processed are the SKIPFROM and TABLE headers.  
        **--------------------------------------------------------------------
        */
        hdr_no = header_type (ip_file, buf_ptr, G_vvehdr);

        /*-
        **--------------------------------------------------------------------
        ** If a SKIPFROM header is encountered and an encoding table was not
        ** specified on the command line, it is checked to see if it 
        ** corresponds to the start of wanted part.
        **--------------------------------------------------------------------
        */
        if (hdr_no == HDR_SKIPFROM)
        {
            if (chk_skipfrom (buf_ptr, part_needed, G_hdrf_spec,
                              &G_vvehdr[HDR_SKIPFROM], ip_file) == TRUE)
            {
		LOGMSG_2 ("Part %3d starts on line:           %ld",
                          part_needed, ip_file->line_no);
                return;
            }
        }

        /*-
        **--------------------------------------------------------------------
        ** If a TABLE header is encountered and the table contents were not
        ** specified on the command line, the encoding table is updated.
        **--------------------------------------------------------------------
        */
        else
        if ((!Q_ENCODE) && (hdr_no == HDR_TABLE))
        {
            buf_ptr = parse_enc_tbl (buf_ptr, &G_vvehdr[HDR_TABLE], ip_file);

            if (buf_ptr == NULL)
            {
		FATALMSG ("error reading encoding table");
		show_file_context (ip_file, NULL);
                vv_exit ();
            }
            strcpy ((char *) G_encode_table, G_vvehdr[HDR_TABLE].value);
            continue;
        }
        buf_ptr = rd_next_line ((Int32) IP_BUFFER_SIZE, G_vv_prefix,
                                G_ip_buffer, ip_file, &G_ippart_bytes);
    }
}                               /* find_next_part */



/*-
**============================================================================
**
** FUNCTION
**
**      initialize
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    initialize (void)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    initialize ()
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *def_file_name;
    Boolean                 status;
    File_Info		    xlt_file;
        
    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    G_ip_file.line_no = 0;
    G_ippt_no = 1;

    /*-
    **------------------------------------------------------------------------
    ** 
    **------------------------------------------------------------------------
    */
    open_ip_file (&G_cmd_ipfile, &G_ip_file);
    def_file_name = allocate_buffer ((MAX_PATH + 1), "def_file_name");
    (void) split_file_spec (G_ip_file.file_spec, NULL, def_file_name,
                            NULL, NULL);

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (Q_OVERWRITE)
    {
        G_overwrite_flag = OVERWRITE_YES;
    }
    else
    if (G_ip_file.pipe_flag == TRUE)
    {
        G_overwrite_flag = OVERWRITE_NO;
    }
    else
    {
        G_overwrite_flag = OVERWRITE_ASK;
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    open_log_file (&G_cmdqual[CMDQ_LOG], def_file_name, G_overwrite_flag,
                   &G_log_file);

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    LOGMSG_3 ("VVdecode %s, %s release (%s)\n",
	      VV_VERSION_STR, VV_IMPLEMENTATION, VV_DATE);
    LOGMSG_1 ("I/P file:                          %s", G_ip_file.file_spec);
    LOGMSG_1 ("Overwrite existing files:          %s",
              (Q_OVERWRITE ? "yes" : "no"));
    LOGMSG_1 ("Log file:                          %s", G_log_file.file_spec);

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    (void) tz_offset (time ((TIME_T) NULL));
    set_decoding_type (&G_cmdqual[CMDQ_UUDECODE], &G_cmdqual[CMDQ_XXDECODE],
		       &G_dec_type, &G_vv_prefix, &G_eol_marker);

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    rdfi_headers (G_dec_type, &G_ip_file, G_vvehdr);

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (G_dec_type == VV_DECODING)
    {
	/*-
        **--------------------------------------------------------------------
	**
        **--------------------------------------------------------------------
        */
        getv_decodeversion (&G_vvehdr[HDR_DECODEVERSION],
                            &G_vv_decode_version);

        if (G_vv_decode_version > (Int16) VV_MAJOR_VERSION_NO)
        {
	    FATALMSG_2 ("VVdecode version %hd.0 or later is\
required to decode file `%s'\n",
		        G_vv_decode_version, G_ip_file.file_spec);
            vv_exit ();
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        G_vv_file.file_spec[0] = '\0';
        getv_mode (&G_vvehdr[HDR_MODE], G_mode_str, &G_vv_file);
        getv_format (&G_vvehdr[HDR_FORMAT], G_fmt_str, &G_vv_file);
        getv_reclen (&G_vvehdr[HDR_RECORDLEN], &G_vv_file);
        getv_timestamp (&G_vvehdr[HDR_TIMESTAMP], &G_vv_file);
    }
    else
    {
        G_vv_decode_version = 0;
        G_vv_file.file_spec[0] = '\0';
	G_vv_file.format = FMT_STREAM;
        G_vv_file.mode = MODE_BINARY;
	G_vv_file.mod_time = INV_TIMESTAMP;
        G_vv_file.max_rec_len = INV_RECORD_LEN;
        G_vv_file.lng_rec_len = INV_RECORD_LEN;
    }
    getv_operatingsystem (&G_vvehdr[HDR_OPERATINGSYSTEM],
                            &G_vv_operating_system);
    getv_characterset (&G_vvehdr[HDR_CHARACTERSET], &G_vv_character_set);

    LOGMSG_1 ("VVE header file spec:              %s", G_hdrf_spec);

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    set_enc_tbl (&G_cmdqual[CMDQ_ENCODE], ENCODE_TABLE_LEN, G_encode_table);
    set_dec_tbl (ENCODE_TABLE_LEN, G_encode_table, DECODE_TABLE_LEN,
		 G_decode_table);

    /*-
    **------------------------------------------------------------------------
    ** ***CHANGES***
    **------------------------------------------------------------------------
    */
    G_xlt_table.xlt_from = NULL;
    G_xlt_table.xlt_to = NULL;

    if ((Q_TRANSLATE) || (STRCMPI (G_vv_character_set, VV_CHARACTERSET) != 0))
    {
	status = choose_xlt_table (&G_cmdqual[CMDQ_TRANSLATE],
				   &G_cmdqual[CMDQ_INDEXFILE],
				   G_vv_character_set, VV_CHARACTERSET,
				   &xlt_file);

	if (status == TRUE)
	{
	    set_xlt_tbl (&xlt_file, &G_xlt_table, G_xlthdr);
	}
	else
	{
	    LOGMSG ("\nTranslation performed:             none");
	}
    }


    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if ((G_xlt_table.xlt_from != NULL) && (G_xlt_table.xlt_to != NULL))
    {
	if (STRCMPI (G_vv_character_set, G_xlt_table.xlt_from) != 0)
	{
	    WARNMSG_2 ("translation file translates from `%s' to `%s'",
		       G_xlt_table.xlt_from, G_xlt_table.xlt_to);
            INFOMSG_1 ("VVE file character set is `%s'", G_vv_character_set);
	}

	if (STRCMPI (VV_CHARACTERSET, G_xlt_table.xlt_to) != 0)
	{
	    WARNMSG_2 ("translation file translates from `%s' to `%s'",
		       G_xlt_table.xlt_from, G_xlt_table.xlt_to);
            INFOMSG_1 ("local character set is `%s'", VV_CHARACTERSET);
	}
    }                           /* if (xlt_file != NULL) */
    else
    {
	if (STRCMPI (G_vv_character_set, VV_CHARACTERSET) != 0)
	{
	    WARNMSG_1 ("VVE file character set is `%s'", G_vv_character_set);
            INFOMSG_1 ("local character set is `%s'", VV_CHARACTERSET);
	}
    }

    /*-
    **------------------------------------------------------------------------
    ** Set the key File_Info parameters, taking into account the file
    ** parameters and the command line qualifier values.
    **------------------------------------------------------------------------
    */
    set_mode (&G_cmdqual[CMDQ_MODE], &G_vv_file, G_mode_str, &G_op_file);
    set_format (&G_cmdqual[CMDQ_FORMAT], &G_vv_file, G_vv_operating_system,
		G_fmt_str, &G_op_file);
    set_record_len (&G_cmdqual[CMDQ_RECORDLEN], &G_vv_file, &G_op_file);
    set_timestamp (&G_cmdqual[CMDQ_TIMESTAMP], &G_vv_file, &G_op_file);
    set_pad_char (&G_cmdqual[CMDQ_PADCHAR], &G_op_file, &G_op_padchar);

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    open_op_file (&G_cmd_opfile, G_overwrite_flag, G_hdrf_spec, &G_op_file);

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    init_crc32_table (CRC32_TABLE_SIZE, CRC32_POLYNOMIAL, G_crc32_table);
    G_op_file.crc32 = CRC32_MASK;
    G_op_file.bytecount = 0L;
    G_ip_file.bytecount = 0L;
    G_ippart_bytes = 0L;
    free (def_file_name);
}                               /* initialize */



/*-
**============================================================================
**
** FUNCTION
**
**      open_ip_file
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    open_ip_file (CONST Qualifier_Struct *ip_qualifier,
                                          File_Info *ip_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    open_ip_file (ip_qualifier, ip_file)
        CONST Qualifier_Struct *ip_qualifier;
        File_Info              *ip_file;
#endif                          /* (ANSI_SYNTAX) */
{

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (ip_qualifier->present != TRUE)
    {
        INTERNAL_ERROR ("open_ip_file");
    }

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

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (STRCMP (ip_qualifier->value, STDIO_SYMBOL) == 0)
    {
        strcpy (ip_file->file_spec, "stdin");
        ip_file->file_ptr = stdin;
        ip_file->pipe_flag = TRUE;
    }
    else
    {
        strcpy (ip_file->file_spec, ip_qualifier->value);

        /*-
        **--------------------------------------------------------------------
        ** Apply the default file extension for VVencoded files.
        **--------------------------------------------------------------------
        */
#if (STICKY_DEFAULTS)
        apply_defaults (NULL, DEF_VV_EXT, ip_file->file_spec);
#endif                          /* (STICKY_DEFAULTS) */


        DEBUG_1 ("open_ip_file: opening input file `%s'", ip_file->file_spec);
        ip_file->file_ptr = f_open_in (ip_file);
    }

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

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



/*-
**============================================================================
**
** FUNCTION
**
**      open_next_part
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    open_next_part (CONST Int16 part_needed,
                                            File_Info *ip_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    open_next_part (part_needed, ip_file)
        CONST Int16             part_needed;
        File_Info              *ip_file;
#endif                          /* (ANSI_SYNTAX) */
{
    char                    file_ext[MAX_EXT + 1];
    char                    prompt_str[65];
    
    SPRINTF (file_ext, VV_EXT_FMT, part_needed);
    force_file_ext (file_ext, ip_file->file_spec);

    if (!file_exists (ip_file->file_spec))
    {
	ERRORMSG_2 ("couldn't find part %d file `%s'",
                    part_needed, ip_file->file_spec);
        SPRINTF (prompt_str, "Enter file specification for part %d: ",
                 part_needed);
        prompt_for_string (prompt_str, MAX_PATH, ip_file->file_spec);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    DEBUG_1 ("open_ip_file: opening input file `%s'", ip_file->file_spec);
    ip_file->file_ptr = f_open_in (ip_file);

    if (ip_file->file_ptr == (FILE *) NULL)
    {
	FATALMSG_1 ("couldn't open part file `%s'", ip_file->file_spec);
        vv_exit ();
    }
    LOGMSG_2 ("\nPart %3d I/P file:                 %s", 
              part_needed, ip_file->file_spec);
}                               /* open_next_part */



/*-
**============================================================================
**
** FUNCTION
**
**      open_op_file
**
** DESCRIPTION
**
**      [tbs]
**
**          Cmd      VVE    STICKY  Result
**          -------------------------------
**           Y        N       *      Cmd
**           Y        Y       N      Cmd
**           Y        Y       Y      Cmd + VVE
**           N        Y       *      VVE
**           N        N       *      Error
**          -------------------------------
**
**      Notes:  1.  The VVE file specification is checked by legal_filespec()
**                  and transformed if appropriate.
**              2.  'Cmd + VVE' means that the command line value is used,
**                  with name/extension components taken from the VVE value if
**                  STICKY_DEFAULTS has been defined for the implementation.
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    open_op_file (CONST Qualifier_Struct *op_qualifier,
                                          CONST Int16 overwrite_flag,
                                          CONST char *vve_hdr_spec,
                                          File_Info *op_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    open_op_file (op_qualifier, overwrite_flag,
                                          vve_hdr_spec, op_file)
        CONST Qualifier_Struct *op_qualifier;
        CONST Int16             overwrite_flag;
        CONST char             *vve_hdr_spec;
        File_Info              *op_file;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *vv_file_name;
    char                   *vv_file_ext;
    char                   *legal_hdrf_spec;

    op_file->file_ptr = (FILE *) NULL;
    legal_hdrf_spec = allocate_buffer ((MAX_PATH + 1), "legal_hdrf_spec");
    vv_file_name = allocate_buffer ((MAX_PATH + 1), "vv_file_name");
    vv_file_ext = allocate_buffer ((MAX_PATH + 1), "vv_file_ext");

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (op_qualifier->present == TRUE)
    {
        if (STRCMP (op_qualifier->value, STDIO_SYMBOL) == 0)
        {
            op_file->pipe_flag = TRUE;
            strcpy (op_file->file_spec, "stdout");
            op_file->file_ptr = stdout;
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        else
        {
            strcpy (op_file->file_spec, op_qualifier->value);

#if (STICKY_DEFAULTS)
            if (VVEH_BEGIN)
            {
                strcpy (legal_hdrf_spec, legal_filespec (vve_hdr_spec));
                (void) split_file_spec (legal_hdrf_spec, NULL,
                                        vv_file_name, vv_file_ext, NULL);
                apply_defaults (vv_file_name, vv_file_ext, op_file->file_spec);
            }
#endif                          /* (STICKY_DEFAULTS) */

            DEBUG_1 ("open_op_file: opening output file `%s'",
                     op_file->file_spec);
            op_file->file_ptr = f_open_out (overwrite_flag, op_file);
        }
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    else
    if (VVEH_BEGIN)
    {
        strcpy (op_file->file_spec, legal_filespec (vve_hdr_spec));
        DEBUG_1 ("open_op_file: opening output file `%s'", op_file->file_spec);
        op_file->file_ptr = f_open_out (overwrite_flag, op_file);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    else
    {
        INTERNAL_ERROR ("open_op_file");
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (op_file->file_ptr == (FILE *) NULL)
    {
	FATALMSG_1 ("error opening output file `%s'", op_file->file_spec);
        vv_exit ();
    }
    LOGMSG_1 ("O/P file:                          %s", op_file->file_spec);

    /*-
    **------------------------------------------------------------------------
    ** Try to detect whether the output file is actually a character device
    ** like a comms port.  If it is, treat it as a pipe.
    **------------------------------------------------------------------------
    */
    if ((op_file->pipe_flag == TRUE) || (is_a_file (op_file) == FALSE))
    {
        DEBUG_1 ("open_op_file: `%s' is not a regular file",
                 op_file->file_spec);
        op_file->pipe_flag = TRUE;
        set_pipe_mode (op_file);
    }

    free (vv_file_name);
    free (vv_file_ext);
    free (legal_hdrf_spec);
}                               /* open_op_file */



/*-
**============================================================================
**
** FUNCTION
**
**      parse_enc_tbl
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    char                   *parse_enc_tbl (CONST char *line_ptr,
                                           Header_Struct *hdr_struct,
                                           File_Info *ip_file)
#else                           /* NOT (ANSI_SYNTAX) */
    char                   *parse_enc_tbl (line_ptr, hdr_struct, ip_file)
        CONST char             *line_ptr;
        Header_Struct          *hdr_struct;
        File_Info              *ip_file;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *buf_ptr;
    char                    dummy[MAX_IP_LINE_LEN + 1];
    char                   *tmp_ptr;
    int                     chars_read;
    int                     fields_read;

    /*-
    **------------------------------------------------------------------------
    ** Allocate enough memory to hold an encoding table
    **------------------------------------------------------------------------
    */
    hdr_struct->present = FALSE;
    tmp_ptr = allocate_buffer ((ENCODE_TABLE_LEN + 1), "tmp_ptr");
    fields_read = sscanf (line_ptr, "%*s %[^\n]", dummy);

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (fields_read == 1)
    {
        vstrtrws (dummy);
        chars_read = strlen (dummy);

        if (chars_read <= ENCODE_TABLE_LEN)
        {
            strcpy (tmp_ptr, dummy);
        }
        else
        {
	    ERRORMSG_1 ("too many characters (%d) in encoding table",
                        chars_read);
	    show_file_context (ip_file, NULL);
            return (NULL);
        }
    }
    else
    {
        *tmp_ptr = '\0';
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    for (;;)
    {
        chars_read = strlen ((char *) tmp_ptr);
        buf_ptr = rd_next_line ((Int32) IP_BUFFER_SIZE, G_vv_prefix,
                                G_ip_buffer, ip_file, &G_ippart_bytes);

        if (buf_ptr == NULL)
        {
	    ERRORMSG ("error reading encoding table");
	    show_file_context (ip_file, NULL);
            explain_error ();
            return (NULL);
        }

        /*-
        **--------------------------------------------------------------------
        ** The table is terminated by the next header line encountered.
        **--------------------------------------------------------------------
        */
        if (header_type (ip_file, buf_ptr, G_vvehdr) != HDR_UNKNOWN)
        {
            break;
        }
        chars_read += strlen (buf_ptr);

        if (chars_read <= ENCODE_TABLE_LEN)
        {
            strcat (tmp_ptr, buf_ptr);
        }
        else
        {
	    ERRORMSG_1 ("too many characters (%d) in encoding table",
                        chars_read);
	    show_file_context (ip_file, NULL);
            return (NULL);
        }
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (strlen (tmp_ptr) != ENCODE_TABLE_LEN)
    {
	ERRORMSG_1 ("wrong number of characters (%d) in encoding table",
                    chars_read);
	show_file_context (ip_file, NULL);
        return (NULL);
    }
    hdr_struct->present = TRUE;
    hdr_struct->value = tmp_ptr;
    return (buf_ptr);
}                               /* parse_enc_tbl */



/*-
**============================================================================
**
** FUNCTION
**
**      prs_idx_file
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      xlt_file
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      TRUE                -   a candidate translation file was found
**      FALSE               -   a candidate translation file could not be
**                              identified
**
**============================================================================
*/
#if (ANSI_SYNTAX)
Boolean prs_idx_file (CONST char *from_charset, CONST char *to_charset,
		      File_Info *idx_file, File_Info *xlt_file)
#else                           /* NOT (ANSI_SYNTAX) */
Boolean prs_idx_file (from_charset, to_charset, idx_file, xlt_file)
    CONST char 		       *from_charset;
    CONST char		       *to_charset;
    File_Info                  *idx_file;
    File_Info                  *xlt_file;
#endif                          /* (ANSI_SYNTAX) */
{
    char                    buffer[MAX_IP_LINE_LEN + 1];
    Int32                   bytes_read;
    char                   *comment;
    int			    fields_read;
    char                    idx_from_charset[MAX_IP_LINE_LEN + 1];
    char                    idx_to_charset[MAX_IP_LINE_LEN + 1];
    char                    idx_xlt_filespec[MAX_IP_LINE_LEN + 1];
    Boolean                 xlt_found;

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

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

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

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    xlt_found = FALSE;

    while (xlt_found != TRUE)
    {
	idx_file->line_no++;
	bytes_read = read_line ((Int32) MAX_IP_LINE_LEN, buffer, idx_file);

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

	/*-
	**--------------------------------------------------------------------
	**
	**--------------------------------------------------------------------
	*/
	buffer[MAX_IP_LINE_LEN] = (char) '\0';
	comment = STRCHR (buffer, IDX_COMMENT_CHAR);

	if (comment != NULL)
	{
	    *comment = '\0';
	}
	(void) vstrtrws (buffer);

	if (strlen (buffer) == 0)
	{
	    continue;
	}

	/*-
	**--------------------------------------------------------------------
	**
	**--------------------------------------------------------------------
	*/
	fields_read = sscanf (buffer, "%s %s %s", idx_from_charset,
			      idx_to_charset, idx_xlt_filespec);

	if (fields_read != 3)
	{
	    ERRORMSG ("error parsing 3 entries in translation index file");
	    show_file_context (idx_file, buffer);
	    continue;
	}

	DEBUG_4 ("prs_idx_file: %04ld: From: `%s', To: `%s', XLT-file: `%s'",
		 idx_file->line_no, idx_from_charset, idx_to_charset,
		 idx_xlt_filespec);

	/*-
	**--------------------------------------------------------------------
	**
	**--------------------------------------------------------------------
	*/
	if ((STRCMPI (idx_from_charset, from_charset) == 0)
	    && (STRCMPI (idx_to_charset, to_charset) == 0))
	{
	    strcpy (xlt_file->file_spec, idx_xlt_filespec);
	    xlt_found = TRUE;
	}
    }                           /* while (ret_status == TRUE) */
    f_close (idx_file);
    return (xlt_found);
}				/* prs_idx_file */



/*-
**============================================================================
**
** FUNCTION
**
**      rdfi_headers
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    rdfi_headers (CONST Int16 decoding_type,
                                             File_Info *ip_file,
                                             Header_Struct *vve_hdr_array)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    rdfi_headers (decoding_type, ip_file,
                                             vve_hdr_array)
        CONST Int16             decoding_type;
        File_Info              *ip_file;
        Header_Struct          *vve_hdr_array;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *buf_ptr;
    Int16                   hdr_no;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    buf_ptr = rd_next_line ((Int32) IP_BUFFER_SIZE, G_vv_prefix, G_ip_buffer,
                            ip_file, &G_ippart_bytes);
    for (;;)
    {

        if (buf_ptr == NULL)
        {
            if (feof (ip_file->file_ptr))
	    {
		FATALMSG_1 ("EOF encountered before `%s' header",
                            G_vvehdr[HDR_BEGIN].h_string);
		show_file_context (ip_file, NULL);
                explain_error ();
                vv_exit ();
            }
            else
            {
		FATALMSG ("error reading file headers");
		show_file_context (ip_file, NULL);
                explain_error ();
                vv_exit ();
            }
	}

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        hdr_no = header_type (ip_file, buf_ptr, vve_hdr_array);

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        if (decoding_type != VV_DECODING)
        {
            switch (hdr_no)
            {
                case HDR_ATTRBEGIN:
                case HDR_ATTREND:
                case HDR_BYTECOUNT:
                case HDR_CHARACTERSET:
		case HDR_COMMENT:
                case HDR_CRC32:
                case HDR_DECODEVERSION:
                case HDR_FORMAT:
                case HDR_MODE:
                case HDR_OPERATINGSYSTEM:
                case HDR_RECORDLEN:
                case HDR_SKIPFROM:
                case HDR_SKIPTO:
                case HDR_TIMESTAMP:
		    FATALMSG_1 ("VVcode header `%s' found when UU/XXdecoding",
                                G_vvehdr[hdr_no].h_string);
		    show_file_context (ip_file, buf_ptr);
                    vv_exit ();
                    break;
                default:
                    break;
            }
	}

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        switch (hdr_no)
        {
            case HDR_UNKNOWN:
                break;
            case HDR_BEGIN:
                if (vve_hdr_array[hdr_no].present == TRUE)
                {
		    FATALMSG_1 ("header `%s' repeated",
                                vve_hdr_array[hdr_no].h_string);
		    show_file_context (ip_file, buf_ptr);
		    vv_exit ();
                }
                parse_begin_header (buf_ptr, ip_file, G_dec_type,
                                    &vve_hdr_array[hdr_no], &G_hdrf_spec);
                return;
            case HDR_COMMENT:
                display_comment (ip_file, buf_ptr, &vve_hdr_array[hdr_no]);
                break;
            case HDR_CHARACTERSET:
            case HDR_DECODEVERSION:
            case HDR_FORMAT:
            case HDR_MODE:
            case HDR_OPERATINGSYSTEM:
            case HDR_RECORDLEN:
            case HDR_TIMESTAMP:
                if (vve_hdr_array[hdr_no].present == TRUE)
                {
		    FATALMSG_1 ("header `%s' repeated",
                                vve_hdr_array[hdr_no].h_string);
		    show_file_context (ip_file, buf_ptr);
                    vv_exit ();
                }
                parse_header (ip_file, buf_ptr, &vve_hdr_array[hdr_no]);
                break;

                /*-
                **------------------------------------------------------------
                ** After parsing the encoding table, jump to the top of the
                ** enclosing 'for' loop.
                **------------------------------------------------------------
                */
            case HDR_TABLE:
                buf_ptr = parse_enc_tbl (buf_ptr, &vve_hdr_array[hdr_no], 
                                         ip_file);

                if (buf_ptr == NULL)
                {
		    FATALMSG ("error reading encoding table");
		    show_file_context (ip_file, NULL);
                    vv_exit ();
                }
                continue;
            case HDR_ATTRBEGIN:
            case HDR_ATTREND:
                WARNMSG_1 ("VVE header `%s' not yet supported",
                            vve_hdr_array[hdr_no].h_string);
                show_file_context (ip_file, buf_ptr);
                break;
            case HDR_SKIPFROM:
            case HDR_SKIPTO:
            case HDR_END:
            case HDR_BYTECOUNT:
            case HDR_CRC32:
                break;
            default:
                INTERNAL_ERROR ("rdfi_headers");
                break;
        }
        buf_ptr = rd_next_line ((Int32) IP_BUFFER_SIZE, G_vv_prefix,
                                G_ip_buffer, ip_file, &G_ippart_bytes);
    }
}                               /* rdfi_headers */



/*-
**============================================================================
**
** FUNCTION
**
**      rdfi_trailers
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    rdfi_trailers (CONST Int16 decoding_type,
                                              File_Info *ip_file,
                                              File_Info *vve_file,
                                              Header_Struct *vve_hdr_array)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    rdfi_trailers (decoding_type, ip_file,
                                              vve_file, vve_hdr_array)
        CONST Int16             decoding_type;
        File_Info              *ip_file;
	File_Info              *vve_file;
        Header_Struct          *vve_hdr_array;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *buf_ptr;
    int                     hdr_no;
    int                     i;
    int                     status;

    for (i = 0; i < 2; i++)
    {
        buf_ptr = rd_next_line ((Int32) IP_BUFFER_SIZE, G_vv_prefix,
                                G_ip_buffer, ip_file, &G_ippart_bytes);

        if (buf_ptr == NULL)
        {
            break;
        }
        hdr_no = header_type (ip_file, buf_ptr, vve_hdr_array);

	/*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        if (hdr_no == HDR_BYTECOUNT)
        {
            if (decoding_type != VV_DECODING)
            {
		FATALMSG_1 ("header `%s' not supported when UU/XXdecoding",
                            vve_hdr_array[hdr_no].h_string);
                vv_exit ();
            }

            parse_header (ip_file, buf_ptr, &vve_hdr_array[hdr_no]);
            status = sscanf (vve_hdr_array[hdr_no].value, "%ld",
                             &(vve_file->bytecount));

            if ((status != 1) || (vve_file->bytecount < 0))
	    {
		ERRORMSG_1 ("error parsing value of `%s' header",
                            vve_hdr_array[hdr_no].h_string);
		show_file_context (ip_file, buf_ptr);
		vve_hdr_array[hdr_no].present = FALSE;
	    }
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        else
        if (hdr_no == HDR_CRC32)
        {

	    if (decoding_type != VV_DECODING)
            {
		FATALMSG_1 ("header `%s' not supported when UU/XXdecoding",
                            vve_hdr_array[hdr_no].h_string);
                vv_exit ();
            }

            parse_header (ip_file, buf_ptr, &vve_hdr_array[hdr_no]);
            status = sscanf (vve_hdr_array[hdr_no].value, "%lx",
                             &(vve_file->crc32));

            if (status != 1)
            {
		ERRORMSG_1 ("error parsing value of `%s' header",
                            vve_hdr_array[hdr_no].h_string);
		show_file_context (ip_file, buf_ptr);
		vve_hdr_array[hdr_no].present = FALSE;
            }
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        else
        {
            break;
        }
    }
}                               /* rdfi_trailers */



/*-
**============================================================================
**
** FUNCTION
**
**      set_decoding_type
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    set_decoding_type (
					CONST Qualifier_Struct *uu_qualifier,
					CONST Qualifier_Struct *xx_qualifier,
					Int16 *decoding_type,
					char **vve_line_prefix,
					char **vve_eol_marker)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    set_decoding_type (uu_qualifier, xx_qualifier,
					       decoding_type, vve_line_prefix,
					       vve_eol_marker)
	CONST Qualifier_Struct *uu_qualifier;
	CONST Qualifier_Struct *xx_qualifier;
	Int16                  *decoding_type;
	char                  **vve_line_prefix;
	char		      **vve_eol_marker;
#endif                          /* (ANSI_SYNTAX) */
{

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (uu_qualifier->present == TRUE)
    {
	*decoding_type = UU_DECODING;
	*vve_line_prefix = NULL;
	*vve_eol_marker = NULL;
	LOGMSG ("Decoding method:                   UUcode\n");
    }
    else if (xx_qualifier->present == TRUE)
    {
	*decoding_type = XX_DECODING;
	*vve_line_prefix = NULL;
	*vve_eol_marker = NULL;
	LOGMSG ("Decoding method:                   XXcode\n");
    }
    else
    {
	*decoding_type = VV_DECODING;
	*vve_line_prefix = STRDUP (VV_PREFIX);
	*vve_eol_marker = STRDUP (VV_EOL_MARKER);
	LOGMSG ("Decoding method:                   VVcode\n");
    }
}                               /* set_decoding_type */



/*-
**============================================================================
**
** FUNCTION
**
**      set_enc_tbl
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    set_enc_tbl (CONST Qualifier_Struct *enc_qualifier,
                                         CONST Int16 enc_tbl_len,
                                         char *encode_table)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    set_enc_tbl (enc_qualifier, enc_tbl_len,
					 encode_table)
        CONST Qualifier_Struct *enc_qualifier;
        CONST Int16             enc_tbl_len;
        char                   *encode_table;
#endif                          /* (ANSI_SYNTAX) */
{
    File_Info               enc_file;
    int                     status;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (enc_qualifier->present == TRUE)
    {
        strcpy (enc_file.file_spec, enc_qualifier->value);

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

        status = rd_enc_tbl (&enc_file, enc_tbl_len, encode_table);

        if (!status)
        {
	    FATALMSG_1 ("error reading encoding table file `%s'",
                        enc_file.file_spec);
            vv_exit ();
        }
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    else
    if (VVEH_TABLE)
    {
        strcpy (enc_file.file_spec, G_ip_file.file_spec);
        strcpy ((char *) encode_table, G_vvehdr[HDR_TABLE].value);
    }

    /*-
    **------------------------------------------------------------------------
    ** There's no table in the file, nor one specified on the command
    ** line, so use the default table for the particular decoding type.
    **------------------------------------------------------------------------
    */
    else
    {
        switch (G_dec_type)
	{
            case UU_DECODING:
                strcpy (enc_file.file_spec, "default UUcode");
                strcpy ((char *) encode_table, UU_ENCODE_TABLE);
                break;
            case XX_DECODING:
                strcpy (enc_file.file_spec, "default XXcode");
                strcpy ((char *) encode_table, XX_ENCODE_TABLE);
                break;
            case VV_DECODING:
            default:
                strcpy (enc_file.file_spec, "default VVcode");
                strcpy ((char *) encode_table, VV_ENCODE_TABLE);
                break;
        }
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    LOGMSG_1 ("\nEncoding table source:             %s", enc_file.file_spec);
    LOGMSG_1 ("Encoding table contents:           %.32s", encode_table);
    LOGMSG_1 ("                                   %.32s", encode_table + 32);

}                               /* set_enc_tbl */



/*-
**============================================================================
**
** FUNCTION
**
**      set_format
**
** DESCRIPTION
**
**      Set the format (fixed/stream/variable) to use when writing the output 
**      file.  The format may be set from the command line, VVE file or the 
**      default specified in machine.h for the file mode.  The logic used is:
**
**          Cmd    OS diff    VVE    Result
**          -------------------------------
**           Y        *        *      Cmd
**           N        Y        *      Def
**           N        N        Y      VVE
**           N        N        N      Def
**          -------------------------------
**
** INPUT PARAMETERS
**
**      vve_file   -   a File_Info structure containing the results of
**                          parsing the VVE file headers.
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    set_format (CONST Qualifier_Struct *fmt_qualifier,
                                        CONST File_Info *vve_file,
                                        CONST char *vve_os_str,
                                        CONST char *fmt_str_array[],
                                        File_Info *op_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    set_format (fmt_qualifier, vve_file, vve_os_str,
                                        fmt_str_array, op_file)
        CONST Qualifier_Struct *fmt_qualifier;
        CONST File_Info        *vve_file;
        CONST char             *vve_os_str;
	CONST char             *fmt_str_array[];
	File_Info              *op_file;
#endif                          /* (ANSI_SYNTAX) */
{
    Int16                   cmd_format;

    /*-
    **------------------------------------------------------------------------
    ** Get the format specified on the command line (if any).
    **------------------------------------------------------------------------
    */
    cmd_format = getc_format (fmt_qualifier, fmt_str_array);


    /*-
    **------------------------------------------------------------------------
    ** IF a valid format was specified on the command line
    ** THEN use the command line format value.
    **------------------------------------------------------------------------
    */
    if (cmd_format != INV_FORMAT)
    {
	op_file->format = cmd_format;
    }

    /*-
    **------------------------------------------------------------------------
    ** IF a valid format was not specified on the command line
    ** AND the VVE file was created on a different operating system
    ** THEN use the default format and issue a warning.
    **------------------------------------------------------------------------
    */
    else if (STRCMPI (vve_os_str, VV_OPERATING_SYSTEM) != 0)
    {
	WARNMSG_1 ("VVE file created on a different operating system: `%s'",
                   vve_os_str);
        INFOMSG ("the output file will be created using the default format");
	op_file->format = (op_file->mode == MODE_BINARY)
			   ? DEF_BINARY_FMT : DEF_TEXT_FMT;
    }

    /*-
    **------------------------------------------------------------------------
    ** IF a valid format was not specified on the command line
    ** AND the VVE file was created on the same operating system
    ** AND the VVE file contained a valid format value
    ** THEN use the VVE format value.
    **------------------------------------------------------------------------
    */
    else if ((STRCMPI (vve_os_str, VV_OPERATING_SYSTEM) == 0)
	     && (vve_file->format != INV_FORMAT))
    {
	op_file->format = vve_file->format;
    }

    /*-
    **------------------------------------------------------------------------
    ** ELSE use the default format value.
    **------------------------------------------------------------------------
    */
    else
    {
	op_file->format = (op_file->mode == MODE_BINARY)
			   ? DEF_BINARY_FMT : DEF_TEXT_FMT;
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    switch (op_file->format)
    {

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

#if (SUPPORT_FIXED_FMT)
	    break;
#else                           /* NOT (SUPPORT_FIXED_FMT) */
	    WARNMSG_1 ("format `%s' is not supported by this implementation",
                       fmt_str_array[op_file->format]);
            INFOMSG ("output file will be created using the default format");
	    op_file->format = (op_file->mode == MODE_BINARY)
			       ? DEF_BINARY_FMT : DEF_TEXT_FMT;
            break;
#endif                          /* (SUPPORT_FIXED_FMT) */

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

#if (SUPPORT_STREAM_FMT)
	    break;
#else                           /* NOT (SUPPORT_STREAM_FMT) */
	    WARNMSG_1 ("format `%s' is not supported by this implementation",
		       fmt_str_array[op_file->format]);
            INFOMSG ("output file will be created using the default format");
	    op_file->format = (op_file->mode == MODE_BINARY)
			       ? DEF_BINARY_FMT : DEF_TEXT_FMT;
            break;
#endif                          /* (SUPPORT_STREAM_FMT) */

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

#if (SUPPORT_VARIABLE_FMT)
	    break;
#else                           /* NOT (SUPPORT_VARIABLE_FMT) */
	    WARNMSG_1 ("format `%s' is not supported by this implementation", 
		       fmt_str_array[op_file->format]);
            INFOMSG ("output file will be created using the default format");
	    op_file->format = (op_file->mode == MODE_BINARY)
			       ? DEF_BINARY_FMT : DEF_TEXT_FMT;
            break;
#endif                          /* (SUPPORT_VARIABLE_FMT) */

	default:
	    op_file->format = (op_file->mode == MODE_BINARY)
			       ? DEF_BINARY_FMT : DEF_TEXT_FMT;
            break;
    }



    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (op_file->format != INV_FORMAT)
    {
	LOGMSG_1 ("O/P file format:                   %s",
		  fmt_str_array[op_file->format]);
    }
}                               /* set_format */



/*-
**============================================================================
**
** FUNCTION
**
**      set_idx_file
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Boolean                 set_idx_file (CONST Qualifier_Struct *idx_qualifier,
					  File_Info *idx_file)
#else                           /* NOT (ANSI_SYNTAX) */
    Boolean                 set_idx_file (idx_qualifier, idx_file)
	CONST Qualifier_Struct *idx_qualifier;
	File_Info              *idx_file;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *tmp_ptr;


    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (idx_qualifier->present == TRUE)
    {
	strcpy (idx_file->file_spec, idx_qualifier->value);

	/*-
	**--------------------------------------------------------------------
	** If an extension has not been provided, use the default extension
	** for the translation index file.
	**--------------------------------------------------------------------
	*/
#if (STICKY_DEFAULTS)
		apply_defaults (NULL, DEF_IDX_EXT, idx_file->file_spec);
#endif                          /* (STICKY_DEFAULTS) */

	return (TRUE);
    }

    tmp_ptr = NULL;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if ((VV_INDEX_ENVVAR != NULL) && (strlen (VV_INDEX_ENVVAR) > 0))
    {
	tmp_ptr = getenv (VV_INDEX_ENVVAR);

	if ((tmp_ptr != NULL) && (strlen (tmp_ptr) > 0))
	{
	    strcpy (idx_file->file_spec, tmp_ptr);
	    return (TRUE);
	}
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if ((tmp_ptr == NULL) && (VV_INDEX_FILE != NULL)
         && (strlen (VV_INDEX_FILE) > 0) && file_exists (VV_INDEX_FILE))
    {
	strcpy (idx_file->file_spec, VV_INDEX_FILE);
	return (TRUE);
    }
    return (FALSE);
}                               /* set_idx_file */



/*-
**============================================================================
**
** FUNCTION
**
**      set_mode
**
** DESCRIPTION
**
**      Set the mode (text/binary) to use when reading and encoding the input
**      file.  The mode may be set from the command line, VVE file or the 
**      default specified in machine.h.  The logic used is:
**
**          Cmd      VVE    Result
**          ----------------------
**           Y        *      Cmd
**           N        N      Def
**           N        Y      VVE
**          ----------------------
**
** INPUT PARAMETERS
**
**      vve_file   -   a  structure containing the results of
**                          parsing the VVE file headers.
**
** OUTPUT PARAMETERS
**
**      none
**
** RETURN VALUE
**
**      none
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    set_mode (CONST Qualifier_Struct *mode_qualifier,
                                      CONST File_Info *vve_file,
                                      CONST char *mode_str_array[],
                                      File_Info *op_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    set_mode (mode_qualifier, vve_file,
                                      mode_str_array, op_file)
        CONST Qualifier_Struct *mode_qualifier;
        CONST File_Info        *vve_file;
        CONST char             *mode_str_array[];
        File_Info              *op_file;
#endif                          /* (ANSI_SYNTAX) */
{
    Int16                   cmd_mode;

    /*-
    **------------------------------------------------------------------------
    ** Get the mode specified on the command line (if any).
    **------------------------------------------------------------------------
    */
    cmd_mode = getc_mode (mode_qualifier, mode_str_array);

    if (cmd_mode != INV_MODE)
    {
        op_file->mode = cmd_mode;
    }
    else
    if (vve_file->mode != INV_MODE)
    {
        op_file->mode = vve_file->mode;
    }
    else
    {
        op_file->mode = DEF_MODE;
    }

    /*-
    **------------------------------------------------------------------------
    ** One final check to make sure that the mode is valid.  The mode can only
    ** come from a validated command line or VVE file value, so it should not
    ** be wrong.
    **------------------------------------------------------------------------
    */
    switch (op_file->mode)
    {
        case MODE_BINARY:
        case MODE_TEXT:
            break;
        default:
	    FATALMSG_1 ("invalid file encoding mode set (%d)", op_file->mode);
            vv_exit ();
    }
    LOGMSG_1 ("\nO/P file mode:                     %s",
              mode_str_array[op_file->mode]);
}                               /* set_mode */



/*-
**============================================================================
**
** FUNCTION
**
**      set_pad_char
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    set_pad_char (CONST Qualifier_Struct *pad_qualifier,
                                          CONST File_Info *op_file,
                                          char *pad_char)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    set_pad_char (pad_qualifier, op_file, pad_char)
        CONST Qualifier_Struct *pad_qualifier;
        CONST File_Info        *op_file;
        char                   *pad_char;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     tmp_pad_char;
    int                     status;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (pad_qualifier->present != TRUE)
    {
        DEBUG ("pad character not specified on the command line");

        if (op_file->mode == MODE_BINARY)
        {
            tmp_pad_char = DEF_BINARY_PADCHAR;
        }
        else
        {
            tmp_pad_char = DEF_TEXT_PADCHAR;
        }
    }
    else
    {
        tmp_pad_char = -1;
        status = sscanf (pad_qualifier->value, "%d", &tmp_pad_char);

        if ((status != 1) || (tmp_pad_char < 0) || (tmp_pad_char > 255))
        {
            USAGE ("pad character value must be in the range 0-255");
            vv_exit ();
        }
    }

    *pad_char = (char) (tmp_pad_char & 0xff);

    if (op_file->format == FMT_FIXED)
    {
	LOGMSG_2 ("O/P file padding character:        %d (0x%02x)",
                  *pad_char, *pad_char);
    }

}                               /* set_pad_char */



/*-
**============================================================================
**
** FUNCTION
**
**      set_record_len
**
** DESCRIPTION
**
**      Set the maximum record length to use when writing the output file.
**      The record length may be set from the command line, VVE file or the 
**      the default specified in machine.h.  The logic used is:
**
**          Cmd      VVE    Result
**          ----------------------
**           Y        *      Cmd
**           N        Y      VVE
**           N        N      Def
**          ----------------------
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    set_record_len (
                                        CONST Qualifier_Struct *rec_qualifier,
                                        CONST File_Info *vve_file,
                                        File_Info *op_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    set_record_len (rec_qualifier, vve_file, op_file)
        CONST Qualifier_Struct *rec_qualifier;
        CONST File_Info        *vve_file;
	File_Info              *op_file;
#endif                          /* (ANSI_SYNTAX) */
{
    Int32                   cmd_reclen;

    /*-
    **------------------------------------------------------------------------
    ** Get the record length specified on the command line (if any).
    **------------------------------------------------------------------------
    */
    cmd_reclen = getc_reclen (rec_qualifier, op_file);

    if (cmd_reclen != INV_RECORD_LEN)
    {
	op_file->max_rec_len = cmd_reclen;
    }
    else
    if (vve_file->max_rec_len != INV_RECORD_LEN)
    {
	op_file->max_rec_len = vve_file->max_rec_len;
    }
    else
    {
	op_file->max_rec_len = INV_RECORD_LEN;
    }

    /*-
    **------------------------------------------------------------------------
    ** One final check to make sure that the maximum record length is valid.
    ** The record length can only come from a validated command line or VVE
    ** file value or the default setting, so it should not be wrong.
    ** There is an exception: the default may be set to INV_RECORD_LEN so that
    ** f_open_out() will use a default value.
    **------------------------------------------------------------------------
    */
    if (op_file->max_rec_len == INV_RECORD_LEN)
    {
	return;
    }

    if (op_file->format == FMT_VARIABLE)
    {
	if ((op_file->max_rec_len < MIN_VARIABLE_RECORD_LEN)
	    || (op_file->max_rec_len > MAX_VARIABLE_RECORD_LEN))
	{
	    FATALMSG_1 ("invalid maximum fixed record length set (%ld)",
		        op_file->max_rec_len);
	    vv_exit ();
	}
    }
    else
    {
	if (op_file->format == FMT_FIXED)
	{
	    if ((op_file->max_rec_len < MIN_FIXED_RECORD_LEN)
		|| (op_file->max_rec_len > MAX_FIXED_RECORD_LEN))
	    {
		FATALMSG_1 ("invalid maximum variable record length set (%ld)",
			    op_file->max_rec_len);
		vv_exit ();
	    }
	}
    }

    /*-
    **------------------------------------------------------------------------
    ** Only report the maximum record length if it is meaningful for the file
    ** format.
    **------------------------------------------------------------------------
    */
    if ((op_file->format == FMT_FIXED) || (op_file->format == FMT_VARIABLE))
    {
	LOGMSG_1 ("O/P file maximum record length:    %ld",
                  op_file->max_rec_len);
    }
}                               /* set_record_len */



/*-
**============================================================================
**
** FUNCTION
**
**      set_timestamp
**
** DESCRIPTION
**
**      Set the timestamp to record in the VVE file.  The timestamp may be
**      set from the command line or VVE file.  The logic used is:
**
**          Cmd      VVE    Result
**          ------------------------------
**           Y        *      Cmd
**           N        Y      ExF
**           N        N      Inv
**          ------------------------------
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    set_timestamp (
                                        CONST Qualifier_Struct *time_qualifier,
                                        CONST File_Info *vve_file,
                                        File_Info *op_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    set_timestamp (time_qualifier, vve_file, op_file)
        CONST Qualifier_Struct *time_qualifier;
        CONST File_Info        *vve_file;
        File_Info              *op_file;
#endif                          /* (ANSI_SYNTAX) */
{
    TIME_T                  cmd_timestamp;

    /*-
    **------------------------------------------------------------------------
    ** Get the timestamp specified on the command line (if any).
    **------------------------------------------------------------------------
    */
    cmd_timestamp = getc_timestamp (time_qualifier);

    if (cmd_timestamp != INV_TIMESTAMP)
    {
        op_file->mod_time = cmd_timestamp;
    }
    else
    if (vve_file->mod_time != INV_TIMESTAMP)
    {
        op_file->mod_time = vve_file->mod_time;
    }
    else
    {
        op_file->mod_time = INV_TIMESTAMP;
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (op_file->mod_time != INV_TIMESTAMP)
    {
	LOGMSG_1 ("O/P file timestamp:                %s",
                  vv_timestamp (op_file->mod_time));
    }
}                               /* set_timestamp */



/*-
**============================================================================
**
** FUNCTION
**
**      terminate
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    terminate (void)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    terminate ()
#endif                          /* (ANSI_SYNTAX) */
{
    Boolean                 file_was_padded;
    Int32                   bytes_to_write;
    Int32                   total_records;
    Int32                   count;

    G_op_file.crc32 = G_op_file.crc32 ^ CRC32_MASK;
    G_ip_file.bytecount += G_ippart_bytes;
    rdfi_trailers (G_dec_type, &G_ip_file, &G_vv_file, G_vvehdr);


    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (Q_INPUT_FILE && (G_ip_file.file_ptr != stdin))
    {
        f_close (&G_ip_file);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if ((Q_OUTPUT_FILE || VVEH_BEGIN) && (G_op_file.file_ptr != stdout))
    {

        /*-
        **--------------------------------------------------------------------
        ** If we're writing a fixed format file, pad an incomplete last 
        ** record with the file padding character.
        **--------------------------------------------------------------------
        */
        file_was_padded = FALSE;
        if ((G_op_file.format == FMT_FIXED)
            && ((G_op_file.bytecount % G_op_file.max_rec_len) != 0L))
        {
            total_records = (G_op_file.bytecount + G_op_file.max_rec_len - 1);
            total_records /= G_op_file.max_rec_len;

            bytes_to_write = (total_records * G_op_file.max_rec_len);
            bytes_to_write -= G_op_file.bytecount;

            for (count = 0; count < bytes_to_write; count++)
            {
                write_bytes (1L, &G_op_padchar, &G_op_file);
                ++G_op_file.bytecount;
            }
            file_was_padded = TRUE;
        }
        f_close (&G_op_file);
    }

    LOGMSG_1 ("\nTotal bytes read:                  %ld", G_ip_file.bytecount);
    LOGMSG_1 ("Total I/P parts read:              %d", G_ippt_no);

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (VVEH_BYTECOUNT)
    {
        if (G_vv_file.bytecount == G_op_file.bytecount)
        {
	    LOGMSG_1 ("Total bytes written:               %ld [OK]",
                      G_op_file.bytecount);
        }
        else
        {
	    LOGMSG_1 ("Total bytes written:               %ld",
                      G_op_file.bytecount);

            if (file_was_padded == TRUE)
            {
		WARNMSG_2 ("last record was padded with value %d (0x%02x)",
                           G_op_padchar, G_op_padchar);
            }
            else
            {
		WARNMSG_1 ("file size does not match that recorded [%ld]",
                           G_vv_file.bytecount);
            }
        }
    }
    else
    {
	LOGMSG_1 ("Total bytes written:               %ld [not recorded]",
                  G_op_file.bytecount);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (VVEH_CRC32)
    {
        if (G_vv_file.crc32 == G_op_file.crc32)
        {
	    LOGMSG_1 ("CRC-32:                            %08lx [OK]",
                      G_op_file.crc32);
        }
        else
        {
	    LOGMSG_1 ("CRC-32:                            %08lx",
                      G_op_file.crc32);
	    WARNMSG_1 ("CRC-32 does not match that recorded [%08lx]",
                       G_vv_file.crc32);
        }
    }
    else
    {
	LOGMSG_1 ("CRC-32:                            %08lx [not recorded]",
                  G_op_file.crc32);
    }


    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if ((G_op_file.pipe_flag != TRUE) && (G_op_file.mod_time != INV_TIMESTAMP))
    {
        set_ftime (&G_op_file);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (Q_LOG && (G_log_file.file_ptr != stderr))
    {
        f_close (&G_log_file);
    }
}                               /* terminate */



/*-
**============================================================================
**
** FUNCTION
**
**      usage_error
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    usage_error (CONST char *reason)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    usage_error (reason)
        CONST char             *reason;
#endif                          /* (ANSI_SYNTAX) */
{

    /*-
    **------------------------------------------------------------------------
    ** Initial error message, followed by Unix style usage message.
    **------------------------------------------------------------------------
    */
    FPRINTF (stderr, "\nVVdecode %s, %s release (%s)\n\n",
             VV_VERSION_STR, VV_IMPLEMENTATION, VV_DATE);
    FPRINTF (stderr, "VVdecode: %s\n\n", reason);
    FPRINTF (stderr,
             "Usage: VVdecode  [qualifiers]  input_file  [output_file]\n\n");
    FPRINTF (stderr, "  Valid qualifiers are:\n\n");

    /*-
    **------------------------------------------------------------------------
    ** qualifier: 'debug' (if support compiled in)
    **------------------------------------------------------------------------
    */
#if (VV_DEBUG_SUPPORT)
    FPRINTF (stderr, "  %.1s%s\n", QUAL_INTRO,
             G_cmdqual[CMDQ_DEBUG].q_string);
#endif                          /* (VV_DEBUG_SUPPORT) */

    /*-
    **------------------------------------------------------------------------
    ** qualifier: 'encoding_table'
    **------------------------------------------------------------------------
    */
    FPRINTF (stderr, "  %.1s%s%.1sfile-spec\n",
             QUAL_INTRO, G_cmdqual[CMDQ_ENCODE].q_string, QUAL_SEP);

    /*-
    **------------------------------------------------------------------------
    ** qualifier: 'format'
    **------------------------------------------------------------------------
    */
    FPRINTF (stderr,
             "  %.1s%s%.1sfile-format  (formats supported: %s%s%s%s%s%s%s)\n",
             QUAL_INTRO, G_cmdqual[CMDQ_FORMAT].q_string, QUAL_SEP,
             G_fmt_str[FMT_DEFAULT],

#if (SUPPORT_FIXED_FMT)
             " ", G_fmt_str[FMT_FIXED],
#else                           /* NOT (SUPPORT_FIXED_FMT) */
             "", "",
#endif                          /* (SUPPORT_FIXED_FMT) */

#if (SUPPORT_STREAM_FMT)
             " ", G_fmt_str[FMT_STREAM],
#else                           /* NOT (SUPPORT_STREAM_FMT) */
             "", "",
#endif                          /* (SUPPORT_STREAM_FMT) */

#if (SUPPORT_VARIABLE_FMT)
             " ", G_fmt_str[FMT_VARIABLE]);
#else                           /* NOT (SUPPORT_VARIABLE_FMT) */
             "", "");
#endif                          /* (SUPPORT_VARIABLE_FMT) */


    /*-
    **------------------------------------------------------------------------
    ** qualifiers: 'help', 'index', 'log', 'mode', 'overwrite'
    **------------------------------------------------------------------------
    */
    FPRINTF (stderr, "  %.1s%s\n",
             QUAL_INTRO, G_cmdqual[CMDQ_HELP].q_string);
    FPRINTF (stderr, "  %.1s%s%.1sfile-spec\n",
             QUAL_INTRO, G_cmdqual[CMDQ_INDEXFILE].q_string, QUAL_SEP);
    FPRINTF (stderr, "  %.1s%s[%.1sfile-spec]\n",
             QUAL_INTRO, G_cmdqual[CMDQ_LOG].q_string, QUAL_SEP);
    FPRINTF (stderr,
          "  %.1s%s%.1sfile-encoding-mode  (modes supported: binary text)\n",
             QUAL_INTRO, G_cmdqual[CMDQ_MODE].q_string, QUAL_SEP);
    FPRINTF (stderr, "  %.1s%s\n",
             QUAL_INTRO, G_cmdqual[CMDQ_OVERWRITE].q_string);

    /*-
    **------------------------------------------------------------------------
    ** qualifiers: 'pad_character', 'record_length', 'timestamp'
    **------------------------------------------------------------------------
    */
    FPRINTF (stderr, "  %.1s%s%.1sdecimal-char-value\n",
             QUAL_INTRO, G_cmdqual[CMDQ_PADCHAR].q_string, QUAL_SEP);
    FPRINTF (stderr, "  %.1s%s%.1sbytes\n",
             QUAL_INTRO, G_cmdqual[CMDQ_RECORDLEN].q_string, QUAL_SEP);
    FPRINTF (stderr, "  %.1s%s[%.1stime]\n",
             QUAL_INTRO, G_cmdqual[CMDQ_TIMESTAMP].q_string, QUAL_SEP);

    /*-
    **------------------------------------------------------------------------
    ** qualifiers: 'translation_file', 'uudecode', 'xxdecode'
    **------------------------------------------------------------------------
    */
    FPRINTF (stderr, "  %.1s%s%.1sfile-spec\n",
             QUAL_INTRO, G_cmdqual[CMDQ_TRANSLATE].q_string, QUAL_SEP);
    FPRINTF (stderr, "  %.1s%s\n",
             QUAL_INTRO, G_cmdqual[CMDQ_UUDECODE].q_string);
    FPRINTF (stderr, "  %.1s%s\n\n",
             QUAL_INTRO, G_cmdqual[CMDQ_XXDECODE].q_string);

    G_status = FATAL_STATUS;
    vv_exit ();
}                               /* usage_error */



/*-
**============================================================================
**
** FUNCTION
**
**      main
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    int                     main (CONST int argc, CONST char *argv[])
#else                           /* NOT (ANSI_SYNTAX) */
    int                     main (argc, argv)
        CONST int               argc;
        CONST char             *argv[];
#endif                          /* (ANSI_SYNTAX) */
{
    SIZE_T                  bytes_reqd;

    /*-
    **------------------------------------------------------------------------
    ** Allocate the global message buffer manually since it is used by the
    ** various MESSAGE_x and DEBUG_x macros.
    **------------------------------------------------------------------------
    */
    G_status = LOG_STATUS;
    bytes_reqd = sizeof (char) * (SIZE_T) MAX_MSG_LEN;
    G_tmp_msg = (char *) malloc (bytes_reqd);

    if (G_tmp_msg == NULL)
    {
        FPRINTF (stderr, "unable to allocate %ld bytes for buffer `G_tmp_msg'",
                 bytes_reqd);
        G_status = FATAL_STATUS;
        vv_exit ();
    }
    DEBUG_1 ("main: allocated %ld bytes for buffer `G_tmp_msg'",
             (Int32) bytes_reqd);

    /*-
    **------------------------------------------------------------------------
    ** Allocate the other global buffers.
    **------------------------------------------------------------------------
    */
    G_ip_buffer = allocate_buffer ((SIZE_T) IP_BUFFER_SIZE, "G_ip_buffer");
    G_op_buffer = allocate_buffer ((SIZE_T) OP_BUFFER_SIZE, "G_op_buffer");

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    scan_cmd_line (argc, argv, QUAL_INTRO, QUAL_SEP, G_cmdqual,
                   &G_cmd_ipfile, &G_cmd_opfile);

#if (VV_DEBUG_SUPPORT)
#else                           /* NOT (VV_DEBUG_SUPPORT) */
    if (Q_DEBUG)
    {
	WARNMSG ("Debugging support was not compiled into VVdecode");
    }
#endif                          /* (VV_DEBUG_SUPPORT) */

    /*-
    **------------------------------------------------------------------------
    ** If the 'help' option was selected, call usage_error() and stop.
    **------------------------------------------------------------------------
    */
    if (Q_HELP)
    {
        USAGE ("help requested");
    }

    /*-
    **------------------------------------------------------------------------
    ** If the 'debug' option was selected, ensure that the 'log' option is 
    ** selected.
    **------------------------------------------------------------------------
    */
    if (Q_DEBUG)
    {
        if (!Q_LOG)
        {
            G_cmdqual[CMDQ_LOG].present = TRUE;
            G_cmdqual[CMDQ_LOG].value = NULL;
        }
        G_debugging = TRUE;
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (!Q_INPUT_FILE)
    {
        USAGE ("input file must be specified");
    }

    initialize ();
    decode ();

    terminate ();
    vv_exit ();
    return (0);

}                               /* main */
