/*-
******************************************************************************
******************************************************************************
**
**  ARCHIVE HEADER INFORMATION
**
**  @C-file{
**      FILENAME    = "vmcms.c",
**      VERSION     = "1.00",
**      DATE        = "",
**      TIME        = "",
**
**      AUTHOR      = "Niel Kempson and Rainer Schoepf",
**      ADDRESS     = "25 Whitethorn Drive, Cheltenham, GL52 5LL, England",
**      TELEPHONE   = "+44-242 579105",
**      EMAIL       = "kempson@tex.ac.uk (Internet),
**                     schoepf@sc.ZIB-Berlin.de (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
**
**      apply_defaults      -   Apply default file name and extension to a
**                              full file specification if either of these 
**                              components is missing.
**      confirm_yesno       -   Display a message to the user and wait for a
**                              yes/no answer.
**      examine_file        -   Examine a file and determine its key features, 
**                              including mode, format, record length and 
**                              timestamp.
**      explain_error       -   Explain the reason for an error.
**      f_open_in           -   Open a file for input using the appropriate 
**                              mode, format etc for the file.
**      f_open_out          -   Open a file for output using the appropriate
**                              mode, format etc for the file.
**      file_exists         -   Determine whether a file exists.
**      force_file_ext      -   Force the file extension of a full file
**                              specification to a specified value.
**      is_a_file           -   Determine whether a file stream is connected
**                              to a real file rather than a character
**                              device, pipe etc.
**      legal_filespec      -   Takes an arbitrary string which may a file 
**                              specification from another operating system
**                              and manipulates it to produce a legal file 
**                              specification for the current operating
**                              system.
**      make_filespec       -   Construct a full file specification from
**                              component parts.
**      prompt_for_string   -   Present a prompt string and accept a string 
**                              from the user.
**      read_bytes          -   Read bytes from a currently open file.
**      read_line           -   Read a line from a currently open (text) file.
**      read_record         -   Read a record from a currently open file.
**      scan_cmd_line       -   [tbs]
**      set_ftime           -   Set the timestamp of a file to a specified 
**                              value.
**      set_pipe_mode       -   Set the mode of a file stream connected to a
**                              pipe, character device, redirected
**                              stdin/stdout/stderr, in fact any non-file.
**      split_file_spec     -   Split a full file specification into its
**                              component parts.
**      tz_offset           -   Determine the offset of local time from 
**                              Greenwich Mean Time (Coordinated Universal
**                              Time) at a specified date and time.  
**      user_message        -   Present a message to the user.
**      vv_exit             -   Exit the program, returning the appropriate
**                              status to the operating system.
**      write_bytes         -   Write bytes to a currently open file.
**      write_record        -   Write a record to a currently open file.
**
**  COPYRIGHT
**
**      Copyright (c) 1991-1993 by Niel Kempson <kempson@tex.ac.uk>
**      and Rainer Schoepf <schoepf@sc.ZIB-Berlin.de>
**
**      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>
#define MAX_CMD 128

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



/*-
**============================================================================
**
** FUNCTION
**
**      apply_defaults
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    apply_defaults (CONST char *default_name,
                                            CONST char *default_ext,
                                            char *full_spec)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    apply_defaults (default_name, default_ext, 
                                            full_spec)
        CONST char             *default_name;
        CONST char             *default_ext;
        char                   *full_spec;
#endif                          /* (ANSI_SYNTAX) */
{
    char                    name[MAX_NAME + 1];
    char                    extension[MAX_EXT + 1];
    Int16                   path_parts;
    char                    postamble[MAX_POSTAMBLE + 1];

    path_parts = split_file_spec (full_spec, NULL, name, extension,
                                  postamble);


    if (((path_parts & FS_NAME) == 0) && (default_name != NULL))
    {
        strncpy (name, default_name, MAX_NAME);
    }

    if (((path_parts & FS_EXTENSION) == 0) && (default_ext != NULL))
    {
        strncpy (extension, default_ext, MAX_EXT);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if ((path_parts & FS_POSTAMBLE) == 0)
    {
        strcpy (postamble, ".A");
    }

    make_file_spec (NULL, name, extension, postamble, full_spec);
}                               /* apply_defaults */



/*-
**============================================================================
**
** FUNCTION
**
**      confirm_yesno
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Boolean                 confirm_yesno (CONST char *confirm_str)
#else                           /* NOT (ANSI_SYNTAX) */
    Boolean                 confirm_yesno (confirm_str)
        CONST char             *confirm_str;
#endif                          /* (ANSI_SYNTAX) */
{
    int                      answer;
 
    FPRINTF (stderr, "\n%s [Y/N]:\n", confirm_str);
 
    do
    {
        answer = getchar ();

        if (answer == EOF)
        {
            ERRORMSG ("EOF detected on standard input");

            answer = 'N';
        }
        else
        {
            answer = TOUPPER (answer);
        }
    } while (!((answer == 'N') || (answer == 'Y')));
 
    return (answer == 'Y' ? TRUE : FALSE);
}                               /* confirm_yesno */
 
 
 
/*-
**============================================================================
**
** FUNCTION
**
**      examine_file
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#define EX_SAMPLE_SIZE         512

#if (ANSI_SYNTAX)
    void                    examine_file (File_Info *ex_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    examine_file (ex_file)
        File_Info              *ex_file;
#endif                          /* (ANSI_SYNTAX) */
{
    char                    tmp_buffer[EX_SAMPLE_SIZE];
    FILE                   *file_ptr;
    int                     bytes_read;
    int                     i, j;
    char                    cmd_string[MAX_CMD];
    char                    dummy[9];
    char                    c;
    int                     status;
    struct tm               tms;

    /*-
    **------------------------------------------------------------------------
    ** Initialize the File_Info structure before starting work.  If the input
    ** file is not a real file (i.e. a device or pipe), these are the values
    ** that will be returned.
    **------------------------------------------------------------------------
    */
    ex_file->mode = INV_MODE;
    ex_file->format = INV_FORMAT;
    ex_file->max_rec_len = INV_RECORD_LEN;
    ex_file->lng_rec_len = INV_RECORD_LEN;
    ex_file->mod_time = INV_TIMESTAMP;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    sprintf (cmd_string, "LISTFILE %s (NOHEADER DATE LIFO",
             ex_file->file_spec);

    for (i = 0; cmd_string[i] != '\0'; i++)
      {
        if (cmd_string[i] == '.') {
          cmd_string[i] = ' ';
        }
        else if (islower (cmd_string[i])) {
          cmd_string[i] = toupper (cmd_string[i]);
        }
      }

    DEBUG_1 ("examine_file: passing command `%s' to VM", cmd_string);

    status = system (cmd_string);

    if (status != 0) {
        ERRORMSG_1 ("error reading info for file `%s'",
                    ex_file->file_spec);
	explain_error ();
        return;
    }

    for (i = 1; i <=3; i++) {
        status = scanf ("%s", dummy);

        DEBUG_1 ("examine_file: item skipped: `%s'", dummy);

        if (status != 1) {
            ERRORMSG_1 ("error parsing info for file `%s'",
                        ex_file->file_spec);
	    explain_error ();
            return;
        }
    }
    
    status = scanf (" %c %d %d %d", &c, &(ex_file->lng_rec_len), &i, &j);
    
    DEBUG_2 ("examine_file: record format: %c, record_length: %d",
             c, ex_file->lng_rec_len);

    DEBUG_2 ("examine_file: number of records: %d, number of blocks: %d",
             i, j);

    if (status != 4) {
        ERRORMSG_2 ("error parsing record info for file `%s', only %d items parsed",
                    ex_file->file_spec, status);
        explain_error ();
        return;
    }

    ex_file->max_rec_len = ex_file->lng_rec_len;

    switch (c) {

        case 'V': 
            ex_file->format = FMT_VARIABLE;
            break;

        case 'F': 
            ex_file->format = FMT_FIXED;
            break;

        default: 
            ERRORMSG_2 ("file `%s' has an unsupported record format (%c)",
                        ex_file->file_spec, c);
	    explain_error ();
            return;
    }

    status = scanf ("%d/%d/%d %d:%d:%d",
                    &tms.tm_mon, &tms.tm_mday, &tms.tm_year,
                    &tms.tm_hour, &tms.tm_min, &tms.tm_sec);

    DEBUG_3 ("examine_file: date as parsed: %02d/%02d/%02d",
             tms.tm_mon, tms.tm_mday, tms.tm_year);

    DEBUG_3 ("examine_file: time as parsed: %02d:%02d:%02d",
             tms.tm_hour, tms.tm_min, tms.tm_sec);


    if (status == 5) {
        tms.tm_sec = 0;
    }
    else if (status != 6) {
        ERRORMSG_1 ("error parsing time info for file `%s'",
                    ex_file->file_spec);
        explain_error ();
        return;
    }
   
    tms.tm_mon--;

    ex_file->mod_time = make_time (&tms);

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    ex_file->file_ptr = fopen (ex_file->file_spec, "rb");
 
    if (file_ptr == (FILE *) NULL) {
        ERRORMSG_1 ("couldn't examine (open) file `%s'", ex_file->file_spec);
        explain_error ();
        return;
    }
 
    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */

    bytes_read = fread (tmp_buffer, sizeof (char),  EX_SAMPLE_SIZE,
			ex_file->file_ptr);

    ex_file->mode = MODE_TEXT; /* default */

    for (i = 0; i < bytes_read - 1; i++) {
        if (!isprint(tmp_buffer[i])) {
            ex_file->mode = MODE_BINARY;
	    break;
        }
    }
    f_close (ex_file);
    ex_file->file_ptr = (FILE *) NULL;
 
}                               /* examine_file */
 
 
 
/*-
**============================================================================
**
** FUNCTION
**
**      explain_error
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    explain_error (void)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    explain_error ()
#endif                          /* (ANSI_SYNTAX) */
{
    INFOMSG_1 ("Error number:          %ld", (long) errno);
}                               /* explain_error */



/*-
**============================================================================
**
** FUNCTION
**
**      f_open_in
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    FILE                   *f_open_in (File_Info *ip_file)
#else                           /* NOT (ANSI_SYNTAX) */
    FILE                   *f_open_in (ip_file)
        File_Info              *ip_file;
#endif                          /* (ANSI_SYNTAX) */
{
    FILE               *file_ptr;
 
    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */

/*    if ((ip_file->rec_length < MIN_RECORD_LEN) || (ip_file->rec_length > MAX_RECORD_LEN)) {
        FATALMSG_1 ("invalid record length specified `%d'", ip_file->rec_length);
        vv_exit (G_status);
    }*/

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    switch (ip_file->mode) {
        case MODE_BINARY:
            file_ptr = fopen (ip_file->file_spec, "rb");
            break;
        case MODE_TEXT:
            file_ptr = fopen (ip_file->file_spec, "r");
            break;
        default:
            ERRORMSG_1 ("invalid file mode specified (%d)", ip_file->mode);
            return ((FILE *) NULL);
    }
 
    if (file_ptr == (FILE *) NULL) {
        ERRORMSG_1 ("error opening file `%s' for input", ip_file->file_spec);
        explain_error ();
        return ((FILE *) NULL);
    }
    return (file_ptr);
}                               /* f_open_in */
 
 
 
/*-
**============================================================================
**
** FUNCTION
**
**      f_open_out
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    FILE                   *f_open_out (CONST Int16 overwrite_files,
                                        File_Info *op_file)
#else                           /* NOT (ANSI_SYNTAX) */
    FILE                   *f_open_out (overwrite_files, op_file)
        CONST Int16             overwrite_files;
        File_Info              *op_file;
#endif                          /* (ANSI_SYNTAX) */
{
    FILE               *file_ptr;
    char                tmp_buffer[512];
 
    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if ((overwrite_files != OVERWRITE_YES)
	&& (file_exists (op_file->file_spec)))
    {
        if (overwrite_files == OVERWRITE_ASK) {
            SPRINTF (G_tmp_msg, "File `%s' exists.  Overwrite ?",
                     op_file->file_spec);

            if (!confirm_yesno (G_tmp_msg))
            {
                ERRORMSG ("operation cancelled");
                return ((FILE *) NULL);
            }
        }
	else
        {
            ERRORMSG_1 ("file `%s' exists", op_file->file_spec);
            return ((FILE *) NULL);
        }
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */


    if (op_file->format == FMT_VARIABLE ) {
        if (op_file->max_rec_len == INV_RECORD_LEN) {
            op_file->max_rec_len = op_file->lng_rec_len = DEF_VARIABLE_RECORD_LEN;
	} else if  ((op_file->max_rec_len < MIN_VARIABLE_RECORD_LEN) ||
		    (op_file->max_rec_len > MAX_VARIABLE_RECORD_LEN)) {
	    WARNMSG_1 ("invalid record length specified `%d'", op_file->max_rec_len);
	    WARNMSG_1 ("using default (%d) instead", DEF_VARIABLE_RECORD_LEN);
	    op_file->max_rec_len = op_file->lng_rec_len = DEF_VARIABLE_RECORD_LEN;
	}

	sprintf (tmp_buffer, "%s, RECFM=V",
		 op_file->mode == MODE_BINARY ? "wb" : "w");

    } else if (op_file->format == FMT_FIXED ) {
        if (op_file->max_rec_len == INV_RECORD_LEN) {
            op_file->max_rec_len = op_file->lng_rec_len = DEF_FIXED_RECORD_LEN;
	} else if  ((op_file->max_rec_len < MIN_FIXED_RECORD_LEN) ||
		    (op_file->max_rec_len > MAX_FIXED_RECORD_LEN)) {
	    WARNMSG_1 ("invalid record length specified `%d'", op_file->max_rec_len);
	    WARNMSG_1 ("using default (%d) instead", DEF_FIXED_RECORD_LEN);
	    op_file->max_rec_len = op_file->lng_rec_len = DEF_FIXED_RECORD_LEN;
	}

	sprintf (tmp_buffer, "%s, RECFM=F, LRECL=%d",
		 op_file->mode == MODE_BINARY ? "wb" : "w",
		 op_file->lng_rec_len);
    }

    DEBUG_2 ("f_open_out: going to open file `%s' with specification `%s'",
             op_file->file_spec, tmp_buffer);

    file_ptr = fopen (op_file->file_spec, tmp_buffer);
 
    if (file_ptr == (FILE *) NULL)
    {
        ERRORMSG_1 ("error opening file `%s' for output", op_file->file_spec);
        explain_error ();
        return ((FILE *) NULL);
    }
    return (file_ptr);
}                               /* f_open_out */

 
 
/*-
**============================================================================
**
** FUNCTION
**
**      file_exists
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Boolean                 file_exists (CONST char *file_spec)
#else                           /* NOT (ANSI_SYNTAX) */
    Boolean                 file_exists (file_spec)
        CONST char             *file_spec;
#endif                          /* (ANSI_SYNTAX) */
{
    FILE               *fp;
    int                result;

    fp = fopen (file_spec, "r");

    DEBUG_2 ("file_exists: trial open of file `%s' in \"r\" mode returns file pointer `%lx'",
             file_spec, (long) fp);

    result = (fp != (FILE *) NULL);

    DEBUG_1 ("file_exists: result = %d", result);

    fclose (fp);

    return (result);
}                              /* file_exists */
 
 
 
/*-
**============================================================================
**
** FUNCTION
**
**      force_file_ext
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    force_file_ext (CONST char *forced_ext,
                                            char *full_spec)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    force_file_ext (forced_ext, full_spec)
        CONST char             *forced_ext;
        char                   *full_spec;
#endif                          /* (ANSI_SYNTAX) */
{
    char                    name[MAX_NAME + 1];
    char                    postamble[MAX_POSTAMBLE + 1];

    if (forced_ext == NULL)
    {
        return;
    }
    (void) split_file_spec (full_spec, NULL, name, NULL, postamble);
    make_file_spec (NULL, name, forced_ext, postamble, full_spec);
}                               /* force_file_ext */



/*-
**============================================================================
**
** FUNCTION
**
**      is_a_file
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Boolean                 is_a_file (CONST File_Info *ex_file)
#else                           /* NOT (ANSI_SYNTAX) */
    Boolean                 is_a_file (ex_file)
        CONST File_Info        *ex_file;
#endif                          /* (ANSI_SYNTAX) */
{
    return (TRUE);
}                               /* is_a_file */



/*-
**============================================================================
**
** FUNCTION
**
**      legal_filespec
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    char                   *legal_filespec (CONST char *hdrf_spec)
#else                           /* NOT (ANSI_SYNTAX) */
    char                   *legal_filespec (hdrf_spec)
        CONST char             *hdrf_spec;
#endif                          /* (ANSI_SYNTAX) */
{
    char                     tmpf_name[MAX_NAME + 1];
    char                     tmpf_ext[MAX_EXT + 1];
    char                    *tmpf_spec;
    char                    *tmp_ptr;
    static char              opf_spec[MAX_PATH + 1];
 
    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    tmpf_spec = STRDUP (hdrf_spec);
    tmp_ptr = strchr (tmpf_spec, '.');
 
    if (tmp_ptr != NULL) {
        strncpy (tmpf_ext, tmp_ptr, MAX_EXT);
        tmpf_ext[MAX_EXT] = '\0';
        *tmp_ptr = '\0';
    }
    else {
        strcpy (tmpf_ext, "");
    }
    strncpy (tmpf_name, tmpf_spec, MAX_NAME);
    tmpf_name[MAX_NAME] = '\0';
 
    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    for (tmp_ptr = tmpf_name; *tmp_ptr != '\0'; tmp_ptr++)
    {
        if (strchr (LEGAL_FILESPEC_CHARS, *tmp_ptr) == NULL)
	{
            *tmp_ptr = REPLACEMENT_FILESPEC_CHAR;
        }
    }
 
    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    for (tmp_ptr = tmpf_ext + 1; *tmp_ptr != '\0'; tmp_ptr++)
    {
        if (strchr (LEGAL_FILESPEC_CHARS, *tmp_ptr) == NULL)
	{
            *tmp_ptr = REPLACEMENT_FILESPEC_CHAR;
        }
    }
 
    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    strcpy (opf_spec, tmpf_name);
    strcat (opf_spec, tmpf_ext);
 
    if (strcmp (hdrf_spec, opf_spec) != 0) {
        WARNMSG_2 ("suspicious header file spec `%s' changed to `%s'",
                   hdrf_spec, opf_spec);
    }
    return (opf_spec);
}                               /* legal_filespec */
 
 
 
/*-
**============================================================================
**
** FUNCTION
**
**      make_file_spec
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    make_file_spec (CONST char *preamble,
                                            CONST char *name,
                                            CONST char *extension,
                                            CONST char *postamble,
                                            char *full_spec)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    make_file_spec (preamble, name, extension,
                                            postamble, full_spec)
        CONST char             *preamble;
        CONST char             *name;
        CONST char             *extension;
        CONST char             *postamble;
        char                   *full_spec;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     len;
    char                   *tmp_ptr;

    /*-
    **------------------------------------------------------------------------
    ** Start with an empty string and append components of the file
    ** specification only if they exist.
    **------------------------------------------------------------------------
    */
    strcpy (full_spec, "");

    /*-
    **------------------------------------------------------------------------
    ** VM/CMS has no preamble component.
    **------------------------------------------------------------------------
    */


    /*-
    **------------------------------------------------------------------------
    ** Simply append the name if it is present.
    **------------------------------------------------------------------------
    */
    if (name != NULL)
    {
        strncat (full_spec, name, MAX_NAME);
    }

    /*-
    **------------------------------------------------------------------------
    ** If the extension component does not begin with '.', prepend '.' before
    ** appending the extension to the file specification.    
    **------------------------------------------------------------------------
    */
    if ((extension != NULL) && (*extension != '\0'))
    {
        if (*extension != '.')
        {
            strcat (full_spec, ".");
            strncat (full_spec, extension, MAX_EXT - 1);
        }
        else
        {
            strncat (full_spec, extension, MAX_EXT);
        }
    }

    /*-
    **------------------------------------------------------------------------
    ** If the postamble component does not begin with '.', prepend '.' before
    ** appending the extension to the file specification.    
    **------------------------------------------------------------------------
    */
    if ((postamble != NULL) && (*postamble != '\0'))
    {
        if (*postamble != '.')
        {
            strcat (full_spec, ".");
            strncat (full_spec, postamble, MAX_POSTAMBLE - 1);
        }
        else
        {
            strncat (full_spec, postamble, MAX_POSTAMBLE);
        }
    }
}                               /* make_file_spec */



/*-
**============================================================================
**
** FUNCTION
**
**      prompt_for_string
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    prompt_for_string (CONST char *confirm_str,
                                               CONST Int16 buf_size,
                                               char *return_buffer)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    prompt_for_string (confirm_str, buf_size,
                                               return_buffer)
        CONST char             *confirm_str;
        CONST Int16             buf_size;
        char                   *return_buffer;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *status;

    FPRINTF (stderr, "\n%s", confirm_str);
    status = fgets (return_buffer, (int) buf_size, stdin);

    if (status == NULL)
    {
        FATALMSG ("error reading string from stdin");
        explain_error ();
        vv_exit ();
    }

    status = STRCHR (return_buffer, '\n');
    if (status != NULL)
    {
        *status = '\0';
    }
}                               /* prompt_for_string */



/*-
**============================================================================
**
** FUNCTION
**
**      read_bytes
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Int32                   read_bytes (CONST Int32 max_bytes,
                                        char *buffer,
                                        File_Info *ip_file)
#else                           /* NOT (ANSI_SYNTAX) */
    Int32                   read_bytes (max_bytes, buffer, ip_file)
        CONST Int32             max_bytes;
        char                   *buffer;
        File_Info              *ip_file;
#endif                          /* (ANSI_SYNTAX) */
{
    Int32                   idx;
    int                     temp;

    for (idx = 0L; idx < max_bytes; idx++)
    {
        temp = fgetc (ip_file->file_ptr);
        if (temp == EOF)
        {
            return (idx);
        }
        buffer[(SIZE_T) idx] = (char) temp;
    }
    return (idx);
}                               /* read_bytes */



/*-
**============================================================================
**
** FUNCTION
**
**      read_line
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Int32                   read_line (CONST Int32 max_bytes,
                                       char *buffer,
                                       File_Info *ip_file)
#else                           /* NOT (ANSI_SYNTAX) */
    Int32                   read_line (max_bytes, buffer, ip_file)
        CONST Int32             max_bytes;
        char                   *buffer;
        File_Info              *ip_file;
#endif                          /* (ANSI_SYNTAX) */
{
    char                   *tmp_ptr;

    tmp_ptr = fgets (buffer, (int) max_bytes, ip_file->file_ptr);
    buffer[(SIZE_T) (max_bytes - 1)] = '\0';

    if (tmp_ptr == NULL)
    {
        return (-1);
    }
    else
    {
        return ((Int32) strlen (buffer));
    }
}                               /* read_line */



/*-
**============================================================================
**
** FUNCTION
**
**      read_record
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Int32                   read_record (CONST Int32 max_bytes,
                                         char *buffer,
                                         File_Info *ip_file)
#else                           /* NOT (ANSI_SYNTAX) */
    Int32                   read_record (max_bytes, buffer, ip_file)
        CONST Int32             max_bytes;
        char                   *buffer;
        File_Info              *ip_file;
#endif                          /* (ANSI_SYNTAX) */
{
    int                 i;
    int                 c;
    char               *s;

    DEBUG_1 ("read_record: want to read record of with maximum size %d",
             max_bytes);

    if (ip_file->mode == MODE_TEXT) {
        s = fgets (buffer, max_bytes, ip_file->file_ptr);

        if (s == (char *) NULL) {
            return (-1L);
        }

        i = strlen (s);
    }
    else {

        if (feof (ip_file->file_ptr)) {
	    return (-1);
	}
        for (i = 0; i < max_bytes; i++) {
            c = getc (ip_file->file_ptr);
            if (c == EOF) {
                break;
	    }
            else {
                buffer[i] = c;
	    }
	}
    }

    DEBUG_1 ("read_record: record with %d bytes read", i);

    return (i);
}                               /* read_record */



/*-
**============================================================================
**
** FUNCTION
**
**      scan_cmd_line
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    scan_cmd_line (CONST int qargc,
                                           CONST char *qargv[],
                                           CONST char *q_intro_str,
                                           CONST char *q_sep_str,
                                           Qualifier_Struct *qual_array,
                                           Qualifier_Struct *qual_ipfile,
                                           Qualifier_Struct *qual_opfile)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    scan_cmd_line (qargc, qargv, q_intro_str,
                                           q_sep_str, qual_array, qual_ipfile,
                                           qual_opfile)
        CONST int               qargc;
        CONST char             *qargv[];
        CONST char             *q_intro_str;
        CONST char             *q_sep_str;
        Qualifier_Struct       *qual_array;
        Qualifier_Struct       *qual_ipfile;
        Qualifier_Struct       *qual_opfile;
#endif                          /* (ANSI_SYNTAX) */
{
    char                    *arg;
    int                      arg_no;
    int                      len;
    int                      qual_found;
    int                      q_no;
    char                    *qsep_ptr;
    char                    *qvalue_ptr;
    char                    *tmp_qstr_ptr;
    int                      paren_seen = 0;
    char                    *file_specs[6];
    int                      i;
    char                     tmp_buffer[512];

     
    /*-
    **------------------------------------------------------------------------
    ** Initialize file spec array
    **------------------------------------------------------------------------
    */

    for (i = 0; i < 6; i++) {
        file_specs[i] = NULL;
    }

    /*-
    **------------------------------------------------------------------------
    ** Loop through each of the user supplied command line arguments.
    **------------------------------------------------------------------------
    */
    for (arg_no = 1; arg_no < qargc; arg_no++) {
        arg = (char *) qargv[arg_no];
 
        if (arg[0] == '(') {
            paren_seen = 1;
            break;
        }
        else if (arg_no > 6) {
            USAGE ("too many arguments");
        }
        else {
            file_specs[arg_no - 1] = STRDUP (arg);
        }
    }

    if (arg_no == 1) {
        USAGE ("too few arguments");
    }

    /* arg_no is now the first option argument */

    strcpy (tmp_buffer, file_specs[0]);
    if (arg_no > 2) {
        strcat (tmp_buffer, ".");
        strcat (tmp_buffer, file_specs[1]);

        if (arg_no > 3) {
            strcat (tmp_buffer, ".");
            strcat (tmp_buffer, file_specs[2]);
        }
    }

    for (i = 0; tmp_buffer[i] != '\0'; i++) {
        if (islower (tmp_buffer[i])) {
            tmp_buffer[i] = toupper (tmp_buffer[i]);
        }
    }

    qual_ipfile->present = TRUE;
    qual_ipfile->value = STRDUP (tmp_buffer);

    if (arg_no > 4) {
        if (strcmp (file_specs[3], "=") == 0) {
            file_specs[3] = file_specs[0];
        }
        strcpy (tmp_buffer, file_specs[3]);
        if (arg_no > 5) {
            if (strcmp (file_specs[4], "=") == 0) {
                file_specs[4] = file_specs[1];
            }
            strcat (tmp_buffer, ".");
            strcat (tmp_buffer, file_specs[4]);

            if (arg_no > 6) {
                if (strcmp (file_specs[5], "=") == 0) {
                    file_specs[5] = file_specs[2];
                }
                strcat (tmp_buffer, ".");
                strcat (tmp_buffer, file_specs[5]);
            }
        }

	for (i = 0; tmp_buffer[i] != '\0'; i++) {
	    if (islower (tmp_buffer[i])) {
	        tmp_buffer[i] = toupper (tmp_buffer[i]);
	    }
	}

        qual_opfile->present = TRUE;
        qual_opfile->value = STRDUP (tmp_buffer);
    }      

    for (; arg_no < qargc; arg_no++) {
        arg = (char *) qargv[arg_no];

        /*-
        **--------------------------------------------------------------------
        **
        **--------------------------------------------------------------------
        */

        DEBUG_1 ("scan_cmd_line: command line argument: `%s'", arg);

        if (*arg == '(') {
            if (arg[1] == '\0') {
                continue;
            }

            ++arg;

        }

        /*-
        **----------------------------------------------------------------
        ** Extract the component of the qualifier string before the
        ** 'q_sep_str' character if it is present.
        **----------------------------------------------------------------
        */
        tmp_qstr_ptr = STRDUP (arg);
        qsep_ptr = STRSTR (tmp_qstr_ptr, q_sep_str);
        qvalue_ptr = NULL;
 
        if (qsep_ptr != NULL) {
            *qsep_ptr = '\0';
            qvalue_ptr = qsep_ptr + 1;
        }
 
        /*-
        **----------------------------------------------------------------
        ** Search the command qualifier structure array for this
        ** qualifier string.
        **----------------------------------------------------------------
        */
        len = strlen (tmp_qstr_ptr);
        qual_found = -1;
 
        for (q_no = 0; qual_array[q_no].q_string != NULL; q_no++) {

            if (STRNCMPI (qual_array[q_no].q_string, tmp_qstr_ptr, len) == 0) {
 
                /*-
                **--------------------------------------------------------
                **
                **--------------------------------------------------------
                */
                if (qual_found < 0) {
                    qual_found = q_no;
                    DEBUG_1 ("scan_cmd_line: matches option:      `%s'",
                             qual_array[q_no].q_string);
                }
                else {
                    USAGE_1 ("ambiguous option `%s'", qargv[arg_no]);
                }
            }
        }
 
        /*-
        **----------------------------------------------------------------
        ** If the option string is unknown, return that status.
        **----------------------------------------------------------------
        */
        if (qual_found < 0) {
            USAGE_1 ("unknown option `%s'", qargv[arg_no]);
        }

        /*-
        **----------------------------------------------------------------
        **
        **----------------------------------------------------------------
        */
        if (qsep_ptr == NULL) {
            if (qual_array[qual_found].val_needed == VALUE_NONE) {
                qual_array[qual_found].present = TRUE;
                qual_array[qual_found].value = NULL;

                if (qual_found == CMDQ_DEBUG) {
                    G_debugging = TRUE;
                }

                continue;
            }
            else {

                /*-
                **--------------------------------------------------------
                **
                **--------------------------------------------------------
                */
                if (((arg_no + 1) < qargc) &&
                     (STRCHR (QUAL_INTRO, *qargv[arg_no + 1]) == NULL)) {

                    /*-
                    **----------------------------------------------------
                    **
                    **----------------------------------------------------
                    */
                    qual_array[qual_found].present = TRUE;
                    qual_array[qual_found].value = STRDUP (qargv[++arg_no]);
                    DEBUG_1 ("scan_cmd_line: option value:        `%s'", qargv[arg_no]);
                }
                else {
                    /*-
                    **----------------------------------------------------
                    **
                    **----------------------------------------------------
                    */
                    if (qual_array[qual_found].val_needed == VALUE_NEEDED) {
                        USAGE_2 ("option `%.1s%s' needs a value",
                                 QUAL_INTRO,
                                 qual_array[qual_found].q_string);
                    }
                    else {
                        qual_array[qual_found].present = TRUE;
                        qual_array[qual_found].value = NULL;
                    }
                }
                continue;
            }
        }
        else {
            /*-
            **------------------------------------------------------------
            **
            **------------------------------------------------------------
            */
            if (*qvalue_ptr == '\0') {
                USAGE_1 ("value missing for option `%.1s%s'",
                         qargv[arg_no]);
            }
            else if (qual_array[qual_found].val_needed == VALUE_NONE) {
                USAGE_2 ("option `%.1s%s' does not take a value",
                         QUAL_INTRO, qual_array[qual_found].q_string);
            }
            else {
                qual_array[qual_found].present = TRUE;
                qual_array[qual_found].value = STRDUP (qvalue_ptr);
                DEBUG_1 ("scan_cmd_line: option value:        `%s'",
                         qual_array[qual_found].value);
                continue;
            }
        }
    }    
}                               /* scan_cmd_line */



/*-
**============================================================================
**
** FUNCTION
**
**      set_ftime
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    set_ftime (File_Info *target_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    set_ftime (target_file)
        File_Info              *target_file;
#endif                          /* (ANSI_SYNTAX) */
{
    int                      status;
    int                      i;
    struct tm               *tms;
    char                     cmd_string[MAX_CMD];

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    DEBUG_2 ("set_ftime: setting file `%s' time to %s",
             target_file->file_spec, ctime (&(target_file->mod_time)));

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    tms = localtime (&(target_file->mod_time));
    sprintf (cmd_string, "EXEC TOUCH %s (%02d/%02d/%02d %02d:%02d:%02d)",
             target_file->file_spec,
             tms->tm_mon + 1, tms->tm_mday, tms->tm_year,
             tms->tm_hour, tms->tm_min, tms->tm_sec);

    for (i = 0; cmd_string[i] != '\0'; i++)
      {
        if (cmd_string[i] == '.') {
          cmd_string[i] = ' ';
        }
        else if (islower (cmd_string[i])) {
          cmd_string[i] = toupper (cmd_string[i]);
        }
      }

    DEBUG_1 ("set_ftime: passing command `%s' to VM", cmd_string);

    status = system (cmd_string);

    if (status != 0) {
        ERRORMSG_2 ("error setting file `%s' time to %s",
                    target_file->file_spec, ctime (&(target_file->mod_time)));
        explain_error ();
    }

    return;
}                              /* set_ftime */



/*-
**============================================================================
**
** FUNCTION
**
**      set_pipe_mode
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    set_pipe_mode (File_Info *target_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    set_pipe_mode (target_file)
        File_Info              *target_file;
#endif                          /* (ANSI_SYNTAX) */
{
    return;
}                               /* set_pipe_mode */



/*-
**============================================================================
**
** FUNCTION
**
**      split_file_spec
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    Unsigned16              split_file_spec (CONST char *full_spec,
                                             char *preamble,
                                             char *name,
                                             char *extension,
                                             char *postamble)
#else                           /* NOT (ANSI_SYNTAX) */
    Unsigned16              split_file_spec (full_spec, preamble, name,
                                             extension, postamble)
        CONST char             *full_spec;
        char                   *preamble;
        char                   *name;
        char                   *extension;
        char                   *postamble;
#endif                          /* (ANSI_SYNTAX) */
{
    char                    tmp_full_spec[MAX_PATH + 1];
    char                   *start_ptr;
    char                   *tmp_ptr;
    char                   *tmp_ptr2;
    int                     idx;
    Int16                   path_components;


    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (preamble != NULL)
    {
        *preamble = '\0';
    }
    if (name != NULL)
    {
        *name = '\0';
    }
    if (extension != NULL)
    {
        *extension = '\0';
    }
    if (postamble != NULL)
    {
        *postamble = '\0';
    }
    path_components = FS_NOTHING;

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

    strcpy (tmp_full_spec, full_spec);
    start_ptr = (char *) tmp_full_spec;

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    tmp_ptr = STRCHR (start_ptr, '.');

    if (tmp_ptr != NULL)
    {
        path_components |= FS_EXTENSION;

        tmp_ptr2 = tmp_ptr;
        tmp_ptr2++;
        while (*tmp_ptr2 != '\0') {
            if (*tmp_ptr2 == '.') {
                *tmp_ptr2 = '\0';

                tmp_ptr2++;
                if ((tmp_ptr2 != '\0') && isalpha (*tmp_ptr2)) {
                    path_components |= FS_POSTAMBLE;
                    if (postamble != NULL) {
                        postamble[0] = '.';
                        postamble[1] = *tmp_ptr2;
                        tmp_ptr2++;
                        if (isdigit (*tmp_ptr2)) {
                            postamble[2] = *tmp_ptr2;
                            postamble[3] = '\0';
                        }
                        else {
                            postamble[2] = '\0';
                        }
                    }
                }

                break;
            }
            tmp_ptr2++;
        }

        if (extension != NULL)
        {
            strncpy (extension, tmp_ptr, MAX_EXT);
        }
        *tmp_ptr = '\0';
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (*start_ptr != '\0')
    {
        path_components |= FS_NAME;

        if (name != NULL)
        {
            strncpy (name, start_ptr, MAX_NAME);
        }
    }

    return (path_components);
}                               /* split_file_spec */



/*-
**============================================================================
**
** FUNCTION
**
**      tz_offset
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    TIME_T                  tz_offset (TIME_T the_time)
#else                           /* NOT (ANSI_SYNTAX) */
    TIME_T                  tz_offset (the_time)
        TIME_T                  the_time;
#endif                          /* (ANSI_SYNTAX) */

{
 /* struct tm               *tm;
    TIME_T                   timezone;
 
    tm = localtime (&the_time);
 
    if (tm->tm_isdst > 0) {
        return (timezone - (TIME_T) 3600);
    }
    else {
        return (timezone);
    }            */
    return ((TIME_T) 0);
}                               /* tz_offset */



/*-
**============================================================================
**
** FUNCTION
**
**      user_message
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    user_message (CONST Int16 status,
                                          CONST char *msg_str,
                                          File_Info *log_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    user_message (status, msg_str, log_file)
        CONST Int16             status;
        CONST char             *msg_str;
        File_Info              *log_file;
#endif                          /* (ANSI_SYNTAX) */
{
    int                     to_stderr;
    int                     to_logfile;
    char                   *status_str;


    /*-
    **------------------------------------------------------------------------
    ** Check the value supplied for the message status and set the output
    ** destinations accordingly.
    **------------------------------------------------------------------------
    */
    to_logfile = TRUE;
    to_stderr = FALSE;

    switch (status)
    {
        case NORMAL_STATUS:
        case LOG_STATUS:
            status_str = "";
            break;
        case INFO_STATUS:
            status_str = " -- ";
            to_stderr = TRUE;
            break;
        case WARNING_STATUS:
            status_str = "\nWARNING: ";
            to_stderr = TRUE;
            break;
        case ERROR_STATUS:
            status_str = "\nERROR: ";
            to_stderr = TRUE;
            break;
        case FATAL_STATUS:
            status_str = "\nFATAL: ";
            to_stderr = TRUE;
            break;
        default:
            INTERNAL_ERROR ("user_message");
            vv_exit ();
    }

    /*-
    **------------------------------------------------------------------------
    ** If the message is NULL, just update the status and/or the message
    ** show status.
    **------------------------------------------------------------------------
    */
    if ((msg_str == NULL) || (*msg_str == '\0'))
    {
        if (status > G_status)
        {
            G_status = status;
        }
        return;
    }

    /*-
    **------------------------------------------------------------------------
    ** Make sure that we don't try to write to a NULL log file pointer.
    **------------------------------------------------------------------------
    */
    if (log_file->file_ptr == (FILE *) NULL)
    {
        to_logfile = FALSE;
    }

    /*-
    **------------------------------------------------------------------------
    ** If the message is to be sent to the log file and stderr, check whether
    ** the log file output is also to stderr.  If it is, disable the 'stderr'
    ** output.
    **------------------------------------------------------------------------
    */
    if (to_logfile && to_stderr)
    {
        if (log_file->file_ptr == stderr)
        {
            to_stderr = FALSE;
        }
    }

    /*-
    **------------------------------------------------------------------------
    ** If the message is to be sent to stderr, output it here.
    **------------------------------------------------------------------------
    */
    if (to_stderr)
    {
        FPRINTF (stderr, "%s%s\n", status_str, msg_str);
    }

    /*-
    **------------------------------------------------------------------------
    ** If the message is to be sent to the log file, output it here.  If
    ** debugging is enabled, report also the name of the calling function.
    **------------------------------------------------------------------------
    */
    if (to_logfile)
    {
        FPRINTF (log_file->file_ptr, "%s%s\n", status_str, msg_str);
    }

    /*-
    **------------------------------------------------------------------------
    **
    **------------------------------------------------------------------------
    */
    if (status > G_status)
    {
        G_status = status;
    }

}                               /* user_message */



/*-
**============================================================================
**
** FUNCTION
**
**      vv_exit
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    vv_exit (void)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    vv_exit ()
#endif                          /* (ANSI_SYNTAX) */
{
    switch (G_status)
    {
        case LOG_STATUS:
        case NORMAL_STATUS:
            exit (0);
            break;
        case WARNING_STATUS:
            exit (1);
            break;
        case ERROR_STATUS:
            exit (2);
            break;
        case FATAL_STATUS:
        default:
            exit (3);
            break;
    }
}                               /* vv_exit */



/*-
**============================================================================
**
** FUNCTION
**
**      write_bytes
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    write_bytes (CONST Int32 max_bytes,
                                         CONST char *buffer,
                                         File_Info *op_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    write_bytes (max_bytes, buffer, op_file)
        CONST Int32             max_bytes;
        CONST char             *buffer;
        File_Info              *op_file;
#endif                          /* (ANSI_SYNTAX) */
{
    Int32                   idx;

    for (idx = 0L; idx < max_bytes; idx++)
    {
        fputc (buffer[(SIZE_T) idx], op_file->file_ptr);
    }
}                               /* write_bytes */



/*-
**============================================================================
**
** FUNCTION
**
**      write_record
**
** NOTE: see related file 'specific.h' for functional description.
**
**============================================================================
*/
#if (ANSI_SYNTAX)
    void                    write_record (CONST Int32 max_bytes,
                                          CONST char *buffer,
                                          File_Info *op_file)
#else                           /* NOT (ANSI_SYNTAX) */
    void                    write_record (max_bytes, buffer, op_file)
        CONST Int32             max_bytes;
        CONST char             *buffer;
        File_Info              *op_file;
#endif                          /* (ANSI_SYNTAX) */
{
    Int32               no_bytes;
 
    no_bytes = max_bytes * sizeof (char);
    fwrite (buffer, (Int16) no_bytes, 1, op_file->file_ptr);
}                               /* write_record */