/*-
******************************************************************************
******************************************************************************
**
**  ARCHIVE HEADER INFORMATION
**
**  @C-file{
**      FILENAME    = "vveparse.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
**
**      chk_skipfrom            [tbs]
**      getv_characterset       -   
**      getv_decodeversion      -   
**      getv_format             -   
**      getv_mode               -   
**      getv_operatingsystem    -
**      getv_reclen             -   
**      getv_timestamp          -   
**      parse_begin_header      -   
**      rd_next_line            -   
**
**  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$";


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


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


/*-
**============================================================================
**
** FUNCTION
**
**      chk_skipfrom
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Boolean                 chk_skipfrom (CONST char *line_ptr,
                                          CONST Int16 part_needed,
                                          CONST char *hdr_file_spec,
                                          CONST Header_Struct *skipfrom_hdr,
                                          File_Info *ip_file)
#else                           /* NOT (ANSI_SYNTAX) */
    Boolean                 chk_skipfrom (line_ptr, part_needed, hdr_file_spec,
                                          skipfrom_hdr, ip_file)
        CONST char             *line_ptr;
        CONST Int16             part_needed;
        CONST char             *hdr_file_spec;
        CONST Header_Struct    *skipfrom_hdr;
        File_Info              *ip_file;
#endif                          /* (ANSI_SYNTAX) */
{
    char                    pt_file_spec[MAX_IP_LINE_LEN + 1];
    Int16                   pt_no;
    int                     fields_read;

    strcpy (pt_file_spec, "");

    /*-
    **------------------------------------------------------------------------
    ** Header syntax:  skipfrom <WS> part-no <WS> file-spec <WS> <EOL>
    **
    ** The file-spec parameter may include whitespace, but leading or trailing
    ** whitespace will be ignored.  The Unix file "one two three" would be
    ** supported with a skipfrom header like:
    **
    **  skipfrom 1 one two three <EOL>
    **------------------------------------------------------------------------
    */
    fields_read = sscanf (line_ptr, "%*s %hd %[^\n]", &pt_no, pt_file_spec);

    if (fields_read < 2)
    {
        FATALMSG_1 ("error parsing `%s' header value", skipfrom_hdr->h_string);
        show_file_context (ip_file, line_ptr);
        vv_exit ();
    }
    vstrtrws (pt_file_spec);

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if ((pt_no == part_needed) && (STRCMP (hdr_file_spec, pt_file_spec) == 0))
    {
        return (TRUE);
    }
    return (FALSE);
}                               /* chk_skipfrom */



/*-
**============================================================================
**
** FUNCTION
**
**      getv_characterset
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    getv_characterset (
                                        CONST Header_Struct *characterset_hdr,
                                        char **character_set)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    getv_characterset (characterset_hdr, character_set)
        CONST Header_Struct    *characterset_hdr;
        char                  **character_set;
#endif                          /* (ANSI_SYNTAX) */
{

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (characterset_hdr->present != TRUE)
    {
	*character_set = STRDUP (VV_CHARACTERSET);
	LOGMSG ("VVE character set:                 not recorded");
	WARNMSG ("VVE file character set was not recorded");
        INFOMSG_1 ("local character set `%s' assumed", VV_CHARACTERSET);

    }
    else
    {
	*character_set = STRDUP (characterset_hdr->value);
	LOGMSG_1 ("VVE character set:                 %s", *character_set);
    }
}                               /* getv_characterset*/



/*-
**============================================================================
**
** FUNCTION
**
**      getv_decodeversion
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    getv_decodeversion (
                                    CONST Header_Struct *decodeversion_hdr,
                                    Int16 *decode_version)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    getv_decodeversion (decodeversion_hdr, 
                                                decode_version)
        CONST Header_Struct    *decodeversion_hdr;
        Int16                  *decode_version;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     fields_read;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    *decode_version = 0;
    if (decodeversion_hdr->present != TRUE)
    {
	LOGMSG ("VVE minimum decode version:        not recorded");
	return;
    }

    fields_read = sscanf (decodeversion_hdr->value, "%hd", decode_version);

    if (fields_read != 1)
    {
	ERRORMSG_2 ("Error parsing `%s' header value: `%s'",
		    decodeversion_hdr->h_string, decodeversion_hdr->value);
    }
    else
    {
	LOGMSG_1 ("VVE minimum decode version:        %d.0", *decode_version);
    }
}                               /* getv_decodeversion */



/*-
**============================================================================
**
** FUNCTION
**
**      getv_format
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    getv_format (CONST Header_Struct *format_hdr,
                                         CONST char *format_str_array[],
                                         File_Info *vve_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    getv_format (format_hdr, format_str_array, vve_file)
        CONST Header_Struct    *format_hdr;
        CONST char             *format_str_array[];
        File_Info              *vve_file;
#endif                          /* (ANSI_SYNTAX) */
{
    Int16                   format_no;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    vve_file->format = INV_FORMAT;

    if (format_hdr->present != TRUE)
    {
	LOGMSG ("VVE format:                        not recorded");
	return;
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    format_no = lookup_key (format_str_array, format_hdr->value,
			    NO_ABBREVIATIONS, CASE_INSENSITIVE);

    switch (format_no)
    {
	case FMT_FIXED:
	case FMT_STREAM:
	case FMT_VARIABLE:
	    LOGMSG_1 ("VVE format:                        %s",
                      format_str_array[format_no]);
            vve_file->format = format_no;
            break;
        default:
            ERRORMSG_2 ("invalid `%s' header value: `%s'",
                        format_hdr->h_string, format_hdr->value);
            break;
    }
    return;
}                               /* getv_format */



/*-
**============================================================================
**
** FUNCTION
**
**      getv_mode
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    getv_mode (CONST Header_Struct *mode_hdr,
                                       CONST char *mode_str_array[],
                                       File_Info *vve_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    getv_mode (mode_hdr, mode_str_array, vve_file)
        CONST Header_Struct    *mode_hdr;
        CONST char             *mode_str_array[];
        File_Info              *vve_file;
#endif                          /* (ANSI_SYNTAX) */
{
    Int16                   mode_no;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    vve_file->mode = INV_MODE;

    if (mode_hdr->present != TRUE)
    {
	LOGMSG ("\nVVE mode:                          not recorded");
	return;
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    mode_no = lookup_key (mode_str_array, mode_hdr->value,
			  NO_ABBREVIATIONS, CASE_INSENSITIVE);

    switch (mode_no)
    {
	case MODE_BINARY:
	case MODE_TEXT:
	    LOGMSG_1 ("\nVVE mode:                          %s",
		      mode_str_array[mode_no]);
            vve_file->mode = mode_no;
            break;
        default:
            ERRORMSG_2 ("invalid `%s' header value: `%s'",
                        mode_hdr->h_string, mode_hdr->value);
            break;
    }
    return;
}                               /* getv_mode */



/*-
**============================================================================
**
** FUNCTION
**
**      getv_operatingsystem
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    getv_operatingsystem (
                                    CONST Header_Struct *operatingsystem_hdr,
				    char **operating_system)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    getv_operatingsystem (operatingsystem_hdr, 
                                                  operating_system)
        CONST Header_Struct    *operatingsystem_hdr;
        char                  **operating_system;
#endif                          /* (ANSI_SYNTAX) */
{

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (operatingsystem_hdr->present != TRUE)
    {
	*operating_system = STRDUP (VV_OPERATING_SYSTEM);
	LOGMSG ("VVE operating system:              not recorded");
	WARNMSG ("VVE file operating system was not recorded");
        INFOMSG_1 ("local operating system `%s' assumed", VV_OPERATING_SYSTEM);
    }
    else
    {
	*operating_system = STRDUP (operatingsystem_hdr->value);
	LOGMSG_1 ("VVE operating system:              %s", *operating_system);
    }
}                               /* getv_operatingsystem */



/*-
**============================================================================
**
** FUNCTION
**
**      getv_reclen
**
** DESCRIPTION
**
**      [tbs]
**
**      NOTE:   define record length meanings
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    getv_reclen (CONST Header_Struct *reclen_hdr,
					 File_Info *vve_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    getv_reclen (reclen_hdr, vve_file)
        CONST Header_Struct    *reclen_hdr;
        File_Info              *vve_file;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     no_values;
    char                    dummy1[MAX_IP_LINE_LEN + 1];
    char                    dummy2[MAX_IP_LINE_LEN + 1];
    int                     status;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    vve_file->max_rec_len = INV_RECORD_LEN;
    vve_file->lng_rec_len = INV_RECORD_LEN;

    if (reclen_hdr->present != TRUE)
    {
	LOGMSG ("VVE longest record length:         not recorded");
	LOGMSG ("VVE maximum record length:         not recorded");
	return;
    }

    /*-
    **------------------------------------------------------------------------
    ** Check how many values are with the recordlength header.  There should
    ** be one or two.
    **------------------------------------------------------------------------
    */
    no_values = sscanf (reclen_hdr->value, "%s %s", dummy1, dummy2);

    switch (no_values)
    {
        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
	case 2:
	    status = sscanf (dummy2, "%ld", &vve_file->lng_rec_len);
	    if ((status != 1)
		|| (vve_file->lng_rec_len < 0)
		|| (vve_file->lng_rec_len > MAX_VV_RECORD_LEN))
	    {
		vve_file->lng_rec_len = INV_RECORD_LEN;
		ERRORMSG_2 ("Invalid second value of `%s' header: `%s'",
			    reclen_hdr->h_string, reclen_hdr->value);
	    }
	    else
	    {
		LOGMSG_1 ("VVE longest record length:         %ld",
			  vve_file->lng_rec_len);
	    }
            break;

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
	case 1:
	    status = sscanf (dummy1, "%ld", &vve_file->max_rec_len);
	    if ((status != 1)
		|| (vve_file->max_rec_len < 0)
                || (vve_file->max_rec_len > MAX_VV_RECORD_LEN))
            {
                vve_file->max_rec_len = INV_RECORD_LEN;
                ERRORMSG_2 ("Invalid first value of `%s' header: `%s'",
			    reclen_hdr->h_string, reclen_hdr->value);
            }
            else
            {
                LOGMSG_1 ("VVE maximum record length:         %ld",
                          vve_file->max_rec_len);
            }
            break;

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        default:
            ERRORMSG_2 ("Error parsing `%s' header value: `%s'",
                        reclen_hdr->h_string, reclen_hdr->value);
        break;
    }
}                               /* getv_reclen */



/*-
**============================================================================
**
** FUNCTION
**
**      getv_timestamp
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    getv_timestamp (CONST Header_Struct *timestamp_hdr,
					    File_Info *vve_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    getv_timestamp (timestamp_hdr, vve_file)
        CONST Header_Struct    *timestamp_hdr;
        File_Info              *vve_file;
#endif                          /* (ANSI_SYNTAX) */
{
    TIME_T                  timestamp;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    vve_file->mod_time = INV_TIMESTAMP;

    if (timestamp_hdr->present != TRUE)
    {
	LOGMSG ("VVE timestamp:                     not recorded");
	return;
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    timestamp = pars_time (timestamp_hdr->value);

    if (timestamp != INV_TIMESTAMP)
    {
        vve_file->mod_time = timestamp;
        LOGMSG_1 ("VVE timestamp:                     %s",
                  vv_timestamp (vve_file->mod_time));
    }
    else
    {
        ERRORMSG_2 ("invalid `%s' header value: `%s'",
                    timestamp_hdr->h_string, timestamp_hdr->value);
    }
}                               /* getv_timestamp */



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

    *hdr_file_spec = NULL;
    begin_value_buffer = allocate_buffer ((MAX_IP_LINE_LEN + 1), 
                                          "begin_value_buffer");

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (decoding_type == VV_DECODING)
    {
        fields_read = sscanf (line_ptr, "%*s %[^\n]", begin_value_buffer);

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        if (fields_read == 1)
        {
            *hdr_file_spec = STRDUP (begin_value_buffer);
        }
	else
        {
            FATALMSG_1 ("error parsing value of `%s' header",
                        hdr_struct->h_string);
            show_file_context (ip_file, line_ptr);
            vv_exit ();
        }

    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    else
    {
        fields_read = sscanf (line_ptr, "%*s %*s %[^\n]", begin_value_buffer);

        if (fields_read == 1)
        {
            *hdr_file_spec = STRDUP (begin_value_buffer);
        }
        else
        {
            FATALMSG_1 ("error parsing value of UU/XXcode `%s' header",
                        hdr_struct->h_string);
            show_file_context (ip_file, line_ptr);
            vv_exit ();
        }
    }
    vstrtrws (*hdr_file_spec);
    hdr_struct->present = TRUE;
    free (begin_value_buffer);
}                               /* parse_begin_header */



/*-
**============================================================================
**
** FUNCTION
**
**      rd_next_line
**
** DESCRIPTION
**
**      [tbs]
**
** INPUT PARAMETERS
**
**      [tbs]
**
** OUTPUT PARAMETERS
**
**      [tbs]
**
** RETURN VALUE
**
**      [tbs]
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    char                   *rd_next_line (CONST Int32 max_chars,
                                          CONST char *vve_line_prefix,
                                          char *buffer,
                                          File_Info *ip_file,
                                          Int32 *byte_count)
#else                           /* NOT (ANSI_SYNTAX) */
    char                   *rd_next_line (max_chars, vve_line_prefix, 
                                          buffer, ip_file, byte_count)
        CONST Int32             max_chars;
        CONST char             *vve_line_prefix;
        char                   *buffer;
        File_Info              *ip_file;
        Int32                  *byte_count;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *eol_ptr;
    Int32                   status;
    int                     pfx_len;
    
    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    for (;;)
    {
        status = read_line (max_chars, buffer, ip_file);
        buffer[(SIZE_T) max_chars - 1] = '\0';

        if (status < 0)
        {
            return (NULL);
        }

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */
        *byte_count += strlen (buffer);
        eol_ptr = STRRCHR (buffer, '\n');

        if (eol_ptr != NULL)
        {
            *eol_ptr = '\0';
        }
        else
        {
            eol_ptr = &buffer[(SIZE_T) (max_chars - 1)];
        }
        ip_file->line_no++;

        /*-
        **--------------------------------------------------------------------
        ** If there's a line prefix, check that it's here.
        **--------------------------------------------------------------------
        */
        if ((vve_line_prefix == NULL) || (*vve_line_prefix == '\0'))
        {
            break;
        }
        else 
        {
            pfx_len = strlen (vve_line_prefix);
            if (STRNCMPI (buffer, vve_line_prefix, (Int16) pfx_len) == 0)
            {
                buffer += pfx_len;
                break;
            }
        }
    }
    return (buffer);
}                               /* rd_next_line */



