/*
 *    VMS supplement for GNU diffutils.
 *
 *======================================================================
 *
 *       vms_init()
 *
 *    On non-VAX systems, uses LIB$INITIALIZE to set a collection of C
 *    RTL features without using the DECC$* logical name method.
 *
 *----------------------------------------------------------------------
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unixio.h>
#include <unixlib.h>

#include <descrip.h>
#include <errno.h>
#include <jpidef.h>
#include <lnmdef.h>
#include <psldef.h>
#include <ssdef.h>
#include <starlet.h>
#include <stsdef.h>

#include "vms_rms.h"

#include "config.h"


typedef struct
{
    short buf_len_avail;
    short itm_cod;
    void *buf;
    unsigned short *buf_len_used;
} xxx_itmlst_t;


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

char *dir_name( char *file_spec)
{

    struct FAB fab;                             /* FAB structure. */
    struct NAM_STRUCT nam;                      /* NAM[L] structure. */

    static char exp_str[ NAM_MAXRSS+ 1];        /* Expanded name storage. */

    int sts;
    char *dev_dir = NULL;                       /* Prepare for failure. */

    /* Extract device and directory from file_spec. */

    /* Initialize the FAB and NAM[L], and link the NAM[L] to the FAB. */
    fab = cc$rms_fab;
    nam = CC_RMS_NAM;
    fab.FAB_NAM = &nam;

    /* Point the FAB/NAM[L] fields to the actual name. */

#ifdef NAML$C_MAXRSS

    fab.fab$l_dna = (char *) -1;    /* Using NAML for default name. */
    fab.fab$l_fna = (char *) -1;    /* Using NAML for file name. */

#endif /* def NAML$C_MAXRSS */

    /* File name. */
    FAB_OR_NAML( fab, nam).FAB_OR_NAML_FNA = file_spec;
    FAB_OR_NAML( fab, nam).FAB_OR_NAML_FNS = strlen( file_spec);

    nam.NAM_ESA = exp_str;      /* Expanded name (result) storage. */
    nam.NAM_ESS = NAM_MAXRSS;   /* Size of expanded name storage. */

    nam.NAM_NOP = NAM_M_SYNCHK; /* Syntax-only analysis. */

    sts = sys$parse( &fab, 0, 0);       /* Parse the name(s). */

    if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)
    {
        dev_dir = nam.NAM_L_DEV;
        dev_dir[ nam.NAM_B_DEV+ nam.NAM_B_DIR] = '\0';
    }
    return dev_dir;
}


/* DIFF-specific main program initialization. */

#define GDIFF_EXE "gdiff_exe"

int initialize_main_vms( int *argcp, char ***argvp)
{
    int sts;

    pid_t pid;
    char *dev_dir_0;

    $DESCRIPTOR( lnm_tbl, "LNM$PROCESS_TABLE");
    $DESCRIPTOR( lnm_name, GDIFF_EXE);
    char lnm_mode = PSL$C_USER;

    struct
    {
        xxx_itmlst_t lnm_itm;
        int term;
    } lnm_item_list =
     {
         { 0, LNM$_STRING, NULL, NULL },
         0
     };

    /* Define GDIFF_EXE if needed. */
    sts = 0;                            /* Prepare for success. */
    dev_dir_0 = getenv( GDIFF_EXE);
    if (dev_dir_0 == NULL)
    {
        dev_dir_0 = dir_name( *argvp[ 0]);
        lnm_item_list.lnm_itm.buf = dev_dir_0;
        lnm_item_list.lnm_itm.buf_len_avail =
         strlen( lnm_item_list.lnm_itm.buf);

        sts = sys$crelnm( NULL,                 /* Flags. */
                          &lnm_tbl,             /* Table. */
                          &lnm_name,            /* Logical name. */
                          &lnm_mode,            /* Access mode. */
                          &lnm_item_list);      /* Item list. */

        if ((sts& STS$M_SEVERITY) != STS$K_SUCCESS)
        {
            fprintf( stderr, "initialize_main: error defining GDIFF_EXE.\n");
            fprintf( stderr, "%s\n", strerror( EVMSERR, sts));
            sts = -1;
        }
    }
    return sts;
}


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

/* vms_case_blind()
 *
 *    Uses SYS$GETJPIW to sense the setting of SET PROCESS /CASE_LOOKUP.
 */

typedef struct
{
    short buf_len;
    short itm_cod;
    void *buf;
    short *ret_len;
} xxx_item_t;


int vms_case_blind( void)
{
    int case_blind = 1;

/* Do the work, if we know how. */
#ifdef JPI$_CASE_LOOKUP_IMAGE

    int sts;
    char case_lu_imag;
    short case_lu_imag_len;

    struct                      /* Item list for GETJPIW. */
    {
        xxx_item_t case_lu_imag_itm;
        int term;
    } jpi_itm_lst =
       { { sizeof( case_lu_imag), JPI$_CASE_LOOKUP_IMAGE, &case_lu_imag,
          &case_lu_imag_len },
         0
       };

    sts = sys$getjpiw( 0, 0, 0, &jpi_itm_lst, 0, 0, 0);
    if ((sts& STS$M_SEVERITY) == STS$M_SUCCESS)
    {
        /* If process case-lookup sense worked, then set the return
         * value accordingly.  (Otherwise, the default case-blind value
         * will be returned.
         */
        case_blind = (case_lu_imag == 0);
    }

#endif /* def JPI$_CASE_LOOKUP_IMAGE */

    return case_blind;
}


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

/* Global storage. */

/*    Flag to sense if vms_init() was called. */

int vms_init_done = -1;

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

#if !defined( __VAX) && (__CRTL_VER >= 70301000)

/* vms_init()

      Uses LIB$INITIALIZE to set a collection of C RTL features without
      requiring the user to define the corresponding logical names.
*/

/* Structure to hold a DECC$* feature name and its desired value. */

typedef struct
   {
   char *name;
   int value;
   } decc_feat_t;

/* Array of DECC$* feature names and their desired values. */

decc_feat_t decc_feat_array[] = {
   /* Preserve command-line case with SET PROCESS/PARSE_STYLE=EXTENDED */
 { "DECC$ARGV_PARSE_STYLE", 1 },
   /* Preserve case for file names on ODS5 disks. */
 { "DECC$EFS_CASE_PRESERVE", 1 },
   /* Enable multiple dots (and most characters) in ODS5 file names,
      while preserving VMS-ness of ";version". */
 { "DECC$EFS_CHARSET", 1 },
   /* List terminator. */
 { (char *)NULL, 0 } };

/* LIB$INITIALIZE initialization function. */

static void vms_init( void)
{
int feat_index;
int feat_value;
int feat_value_max;
int feat_value_min;
int i;
int sts;

/* Set the global flag to indicate that LIB$INITIALIZE worked. */

vms_init_done = 1;

/* Loop through all items in the decc_feat_array[]. */

for (i = 0; decc_feat_array[ i].name != NULL; i++)
   {
   /* Get the feature index. */
   feat_index = decc$feature_get_index( decc_feat_array[ i].name);
   if (feat_index >= 0)
      {
      /* Valid item.  Collect its properties. */
      feat_value = decc$feature_get_value( feat_index, 1);
      feat_value_min = decc$feature_get_value( feat_index, 2);
      feat_value_max = decc$feature_get_value( feat_index, 3);

      if ((decc_feat_array[ i].value >= feat_value_min) &&
       (decc_feat_array[ i].value <= feat_value_max))
         {
         /* Valid value.  Set it if necessary. */
         if (feat_value != decc_feat_array[ i].value)
            {
            sts = decc$feature_set_value( feat_index,
             1,
             decc_feat_array[ i].value);
            }
         }
      else
         {
         /* Invalid DECC feature value. */
         printf( " INVALID DECC FEATURE VALUE, %d: %d <= %s <= %d.\n",
          feat_value,
          feat_value_min, decc_feat_array[ i].name, feat_value_max);
         }
      }
   else
      {
      /* Invalid DECC feature name. */
      printf( " UNKNOWN DECC FEATURE: %s.\n", decc_feat_array[ i].name);
      }
   }
}

/* Get "vms_init()" into a valid, loaded LIB$INITIALIZE PSECT. */

#pragma nostandard

/* Establish the LIB$INITIALIZE PSECTs, with proper alignment and
   other attributes.  Note that "nopic" is significant only on VAX.
*/
#pragma extern_model save

#pragma extern_model strict_refdef "LIB$INITIALIZE" 2, nopic, nowrt
void (*const x_vms_init)() = vms_init;

#pragma extern_model strict_refdef "LIB$INITIALIZ" 2, nopic, nowrt
const int spare[ 8] = { 0 };

#pragma extern_model restore

/* Fake reference to ensure loading the LIB$INITIALIZE PSECT. */

#pragma extern_model save
int lib$initialize(void);
#pragma extern_model strict_refdef
int dmy_lib$initialize = (int) lib$initialize;
#pragma extern_model restore

#pragma standard

#endif /* !defined( __VAX) && (__CRTL_VER >= 70301000) */

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

/* vms_fwrite()
 *
 *    fwrite(), modified to use (ptr, N, 1, file) argument order,
 * instead of RMS-unfriendly (ptr, 1, N, file) order.
 *
 *    This module must be compiled normally (DECC$ prefix on fwrite(),
 * fwrite not #define'd as vms_fwrite()).
 */

size_t vms_fwrite( const void *ptr,
                   size_t size,
                   size_t nmemb,
                   FILE *stream)
{
    if (size == 1)
        return fwrite( ptr, nmemb, size, stream)* nmemb;
    else
        return fwrite( ptr, size, nmemb, stream);
}

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

/* vms_unlink()
 *
 *    VMS-specific replacement for CRTL unlink().  Deletes all versions
 * of a file.
 */

int vms_unlink( const char *name)
{
    char *nm;
    int sts;

#define VERSION_STAR ";*"

    nm = malloc( strlen( name)+ sizeof( VERSION_STAR));
    if (nm == NULL)
        return -1;

    /* Compose "<name>;*", and delete that. */
    strncpy( nm, name, strlen( name));
    strcpy( nm+ strlen( name), VERSION_STAR);
    sts = delete( nm);

    /* Free allocated name storage.  Return delete() status. */
    free( nm);
    return sts;
}

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