/*

  C-Kermit C RTL replacement functions for VMS systems.

*/



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

 *

 *       utime()

 *

 *    VMS C RTL before V7.3 lacks utime().  In V7.3, utime() sets only

 *    the modified (revised) date, not the created date of a file.

 *

 *    UNIX "ls -l" reports the modified time.  VMS "DIRECTORY /DATE"

 *    reports the creation time.  Reconciling these in FTP DIR reports

 *    is non-trivial.

 *

 *    UNIX utime() sets revision and access times.  VMS does not always

 *    maintain access times, so this utime() replacement sets the

 *    creation and revision times to the specified revision (or

 *    creation?) time.  Any access time is ignored.

 *

 *----------------------------------------------------------------------

 */



#if __CRTL_VER >= 70300000



/* Avoid "%CC-W-EMPTYFILE, Source file does not contain any declarations." */



int dummy_function(void);



#else /* __CRTL_VER < 70300000 */



#include <errno.h>

#include <string.h>

#include <time.h>

#include <unixlib.h>



#include <atrdef.h>

#include <descrip.h>

#include <fibdef.h>

#include <iodef.h>

#include <lib$routines.h>

#include <rms.h>

#include <starlet.h>

#include <stsdef.h>



#include "ckvrtl.h"

#include "ckvrms.h"



/* Use <iosbdef.h> if available.  Otherwise declare IOSB here. */



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

#include <iosbdef.h>

#else /* __CRTL_VER >= 70000000 */

typedef struct _iosb {

        unsigned short int iosb$w_status; /* Final I/O status   */

        unsigned short int iosb$w_bcnt; /* 16-bit byte count    */

        unsigned int iosb$l_dev_depend; /* 32-bit dev dependent */

    } IOSB;

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





/* Ugly work-around for bad type in VAX <atrdef.h>. */



#ifdef __VAX

#define UWA (unsigned int)

#else /* def __VAX */

#define UWA

#endif /* def __VAX */



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



#ifdef __DECC



/* Private utime() code. */



/* Action routine for decc$to_vms(), in utime(). */



char vms_path[ NAMX_C_MAXRSS+ 1];



int set_vms_name( char *name, int type)

{

   strncpy( vms_path, name, NAMX_C_MAXRSS);

   vms_path[ NAMX_C_MAXRSS] = '\0';

   return 1;

}



#endif /* def __DECC */



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



/* utime() replacement. */



int vms_utime( const char *path, const struct utimbuf *times)

{

time_t utc_unsigned;



int chan, i;

int sts, sts2;



unsigned short int vms_num_vec_time[ 7];

/* Static to avoid %CC-W-ADDRCONSTEXT from old (V4.0-000) DEC C. */

static unsigned int vms_abs_time[ 2];

struct tm *tms;

struct _iosb iosb_q;



/* QIOW item list used to set creation and revision dates. */



struct atrdef ut_atr[ 3] = {

 {sizeof( vms_abs_time), ATR$C_CREDATE, UWA vms_abs_time},

 {sizeof( vms_abs_time), ATR$C_REVDATE, UWA vms_abs_time},

 {0,0,0}};



/* Various RMS structures used for file access. */



struct FAB ut_fab = cc$rms_fab;

struct RAB ut_rab = cc$rms_rab;

struct NAMX ut_namx = CC_RMS_NAMX;

static struct fibdef ut_fib;



/* Device and file name buffers and their descriptors. */



static char dev_namx[ NAMX_C_MAXRSS+ 1];

char esa_namx[ NAMX_C_MAXRSS+ 1];

char rsa_namx[ NAMX_C_MAXRSS+ 1];



struct dsc$descriptor dev_dsc =

 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, dev_namx};



struct dsc$descriptor fib_dsc =

 {sizeof( ut_fib), DSC$K_DTYPE_T, DSC$K_CLASS_S, (char *) &ut_fib};



#ifdef __DECC



/* We will accept either a UNIX-like path name or a VMS-like path name. 

   If a slash is found in the name, assume that it's UNIX-like, and

   convert it to VMS form.  Otherwise, use it as-is.

*/



if (strchr( path, '/') != NULL)

   {

   sts = decc$to_vms( path, set_vms_name, 0, 0);

   path = vms_path;

   }



#endif /* def __DECC */



/* Install the VMS file specification into the FAB. */



ut_fab.fab$l_fna = (char *) path;

ut_fab.fab$b_fns = (unsigned char) strlen( path);



ut_fab.fab$l_dna = "";

ut_fab.fab$b_dns = 0;



/* Point the FAB to the NAMX. */



ut_fab.FAB_L_NAMX = &ut_namx;



/* Install the name buffers into the NAM. */



ut_namx.NAMX_L_ESA = esa_namx;

ut_namx.NAMX_B_ESL = 0;

ut_namx.NAMX_B_ESS = sizeof( esa_namx)- 1;



ut_namx.NAMX_L_RSA = rsa_namx;

ut_namx.NAMX_B_RSL = 0;

ut_namx.NAMX_B_RSS = sizeof( rsa_namx)- 1;



/* Convert the modification time (UTC time_t) to local "tm" time. */



tms = localtime( &(times-> modtime));



/* Move (translate) "tm" structure local time to VMS vector time. */



if (tms != NULL)

   {

   vms_num_vec_time[ 0] = tms-> tm_year+ 1900;

   vms_num_vec_time[ 1] = tms-> tm_mon+ 1;

   vms_num_vec_time[ 2] = tms-> tm_mday;

   vms_num_vec_time[ 3] = tms-> tm_hour;

   vms_num_vec_time[ 4] = tms-> tm_min;

   vms_num_vec_time[ 5] = tms-> tm_sec;

   vms_num_vec_time[ 6] = 0;  /* centiseconds */



/* Convert VMS vector time to VMS absolute time (quadword). */



   sts = lib$cvt_vectim( vms_num_vec_time, vms_abs_time);



   if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)

      {

/* Parse the file specification. */



      sts = sys$parse( &ut_fab, 0, 0);



      if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)

         {

/* Locate the file. (Gets the FID.) */



         sts = sys$search( &ut_fab, 0, 0);



         if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)

            {

/* Form the device name descriptor. */



            dev_dsc.dsc$w_length = ut_namx.NAMX_B_DEV;

            dev_dsc.dsc$a_pointer = (char *) ut_namx.NAMX_L_DEV;



/* Assign a channel to the disk device. */



            sts = sys$assign( &dev_dsc, &chan, 0, 0);



            if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)

               {

/* Move the FID (and not the DID) into the FIB. */



               memset( (void *) &ut_fib, 0, sizeof( ut_fib));



               for (i = 0; i < 3; i++)

                  {

                  ut_fib.FIB_W_FID[ i] = ut_namx.NAMX_W_FID[ i];

                  ut_fib.FIB_W_DID[ i] = 0;

                  }



/* Prevent this QIOW from setting the revision time to now. */



               ut_fib.FIB_L_ACCTL = FIB$M_NORECORD;



/* Set the file dates. */



               sts = sys$qiow( 0,

                               chan,

                               IO$_MODIFY,

                               &iosb_q,

                               0,

                               0,

                               &fib_dsc,

                               0,

                               0,

                               0,

                               ut_atr,

                               0);



               if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)

                  {

                   sts = iosb_q.iosb$w_status;

                  }

               sts2 = sys$dassgn( chan);



               if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)

                  {

                  sts = sts2;

                  }

               }

            }

         }

      }

   }



/* Convert successful VMS status to zero = success status.

   If failure, set errno and vaxc$errno, and return -1 = failure status.

*/



if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)

   {

   sts = 0;

   }

else

   {

   errno = EVMSERR;

   vaxc$errno = sts;

   sts = -1;

   }



return sts;

}



#endif /* __CRTL_VER >= 70300000 [else] */



/**********************************************************************/



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



#include <stdio.h>

#include <unixlib.h>



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



int vms_init_done = -1;





/* 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) */



