/* 2006-02-08 SMS.
   VMS-specific code for MTOOLS.
*/

#include <descrip.h>
#include <dmtdef.h>
#include <dvidef.h>
#include <iodef.h>
#include <mntdef.h>
#include <psldef.h>
#include <stdio.h>
#include <string.h>
#include <stsdef.h>
#include <ssdef.h>

/* Work around /NAMES = AS_IS problem in <lib$routines.h> on VAX. */

#ifdef __VAX
# define lib$getdvi LIB$GETDVI
#endif /* def __VAX */

#include <lib$routines.h>

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

#if !defined( __VAX) && (__CRTL_VER >= 70000000)
#include <iosbdef.h>
#else /* !defined( __VAX) && __CRTL_VER >= 70000000 */
#pragma __member_alignment __save
typedef struct _iosb {
#pragma __nomember_alignment
        unsigned short int iosb$w_status;       /* Final I/O status   */
        unsigned int iosb$l_bcnt;               /* 32-bit byte count    */
        unsigned int iosb$w_dev_depend_high;    /* 16-bit dev dependent */
    } IOSB;
#pragma __member_alignment __restore
#endif /* !defined( __VAX) && (__CRTL_VER >= 70000000) */

/* Work around /NAMES = AS_IS problems on VAX. */

#ifdef __VAX
# ifndef sys$alloc
#  define sys$alloc SYS$ALLOC
# endif /* ndef sys$alloc */
# ifndef sys$assign
#  define sys$assign SYS$ASSIGN
# endif /* ndef sys$assign */
# ifndef sys$dalloc
#  define sys$dalloc SYS$DALLOC
# endif /* ndef sys$dalloc */
# ifndef sys$dassgn
#  define sys$dassgn SYS$DASSGN
# endif /* ndef sys$dassgn */
# ifndef sys$dismou
#  define sys$dismou SYS$DISMOU
# endif /* ndef sys$dismou */
# ifndef sys$getdviw
#  define sys$getdviw SYS$GETDVIW
# endif /* ndef sys$getdviw */
# ifndef sys$getjpiw
#  define sys$getjpiw SYS$GETJPIW
# endif /* ndef sys$getjpiw */
# ifndef sys$mount
#  define sys$mount SYS$MOUNT
# endif /* ndef sys$mount */
# ifndef sys$parse
#  define sys$parse SYS$PARSE
# endif /* ndef sys$parse */
# ifndef sys$qiow
#  define sys$qiow SYS$QIOW
# endif /* ndef sys$qiow */
#endif /* def VAX */

#include <errno.h>
#include <starlet.h>
#include <jpidef.h>
#include <namdef.h>
#include <fabdef.h>
#include <rabdef.h>
#include <fab.h>                /* Needed only in old environments. */
#include <nam.h>                /* Needed only in old environments. */
#include <rmsdef.h>
#include <stsdef.h>
#include <xabdef.h>
#include <xabitmdef.h>

#include "vms.h"


/* Define macros for use with either NAM or NAML. */

#ifdef NAML$C_MAXRSS            /* NAML is available.  Use it. */

#  define NAM_STRUCT NAML

#  define FAB_OR_NAM( fab, nam) nam
#  define FAB_OR_NAM_DNA naml$l_long_defname
#  define FAB_OR_NAM_DNS naml$l_long_defname_size
#  define FAB_OR_NAM_FNA naml$l_long_filename
#  define FAB_OR_NAM_FNS naml$l_long_filename_size

#  define CC_RMS_NAM cc$rms_naml
#  define FAB_NAM fab$l_naml
#  define NAME_DNA naml$l_long_defname
#  define NAME_DNS naml$l_long_defname_size
#  define NAME_FNA naml$l_long_filename
#  define NAME_FNS naml$l_long_filename_size
#  define NAM_DID naml$w_did
#  define NAM_DVI naml$t_dvi
#  define NAM_ESA naml$l_long_expand
#  define NAM_ESL naml$l_long_expand_size
#  define NAM_ESS naml$l_long_expand_alloc
#  define NAM_FID naml$w_fid
#  define NAM_FNB naml$l_fnb
#  define NAM_RSA naml$l_long_result
#  define NAM_RSL naml$l_long_result_size
#  define NAM_RSS naml$l_long_result_alloc
#  define NAM_MAXRSS NAML$C_MAXRSS
#  define NAM_NOP naml$b_nop
#  define NAM_M_SYNCHK NAML$M_SYNCHK
#  define NAM_B_DEV naml$l_long_dev_size
#  define NAM_L_DEV naml$l_long_dev
#  define NAM_B_DIR naml$l_long_dir_size
#  define NAM_L_DIR naml$l_long_dir
#  define NAM_B_NAME naml$l_long_name_size
#  define NAM_L_NAME naml$l_long_name
#  define NAM_B_TYPE naml$l_long_type_size
#  define NAM_L_TYPE naml$l_long_type
#  define NAM_B_VER naml$l_long_ver_size
#  define NAM_L_VER naml$l_long_ver

#else /* def NAML$C_MAXRSS */   /* NAML is not available.  Use NAM. */

#  define NAM_STRUCT NAM

#  define FAB_OR_NAM( fab, nam) fab
#  define FAB_OR_NAM_DNA fab$l_dna
#  define FAB_OR_NAM_DNS fab$b_dns
#  define FAB_OR_NAM_FNA fab$l_fna
#  define FAB_OR_NAM_FNS fab$b_fns

#  define CC_RMS_NAM cc$rms_nam
#  define FAB_NAM fab$l_nam
#  define NAME_DNA fab$l_dna
#  define NAME_DNS fab$b_dns
#  define NAME_FNA fab$l_fna
#  define NAME_FNS fab$b_fns
#  define NAM_DID nam$w_did
#  define NAM_DVI nam$t_dvi
#  define NAM_ESA nam$l_esa
#  define NAM_ESL nam$b_esl
#  define NAM_ESS nam$b_ess
#  define NAM_FID nam$w_fid
#  define NAM_FNB nam$l_fnb
#  define NAM_RSA nam$l_rsa
#  define NAM_RSL nam$b_rsl
#  define NAM_RSS nam$b_rss
#  define NAM_MAXRSS NAM$C_MAXRSS
#  define NAM_NOP nam$b_nop
#  define NAM_M_SYNCHK NAM$M_SYNCHK
#  define NAM_B_DEV nam$b_dev
#  define NAM_L_DEV nam$l_dev
#  define NAM_B_DIR nam$b_dir
#  define NAM_L_DIR nam$l_dir
#  define NAM_B_NAME nam$b_name
#  define NAM_L_NAME nam$l_name
#  define NAM_B_TYPE nam$b_type
#  define NAM_L_TYPE nam$l_type
#  define NAM_B_VER nam$b_ver
#  define NAM_L_VER nam$l_ver

#endif /* def NAML$C_MAXRSS */


/* Character property table for (re-)escaping ODS5 extended file names.
   Note that this table ignore Unicode, and does not identify invalid
   characters.

   ODS2 valid characters: 0-9 A-Z a-z $ - _

   ODS5 Invalid characters:
      C0 control codes (0x00 to 0x1F inclusive)
      Asterisk (*)
      Question mark (?)

   ODS5 Invalid characters only in VMS V7.2 (which no one runs, right?):
      Double quotation marks (")
      Backslash (\)
      Colon (:)
      Left angle bracket (<)
      Right angle bracket (>)
      Slash (/)
      Vertical bar (|)

   Characters escaped by "^":
      SP  !  #  %  &  '  (  )  +  ,  .  ;  =  @  [  ]  ^  `  {  }  ~

   Either "^_" or "^ " is accepted as a space.  Period (.) is a special
   case.  Note that un-escaped < and > can also confuse a directory
   spec.

   Characters put out as ^xx:
      7F (DEL)
      80-9F (C1 control characters)
      A0 (nonbreaking space)
      FF (Latin small letter y diaeresis)

   Other cases:
      Unicode: "^Uxxxx", where "xxxx" is four hex digits.

    Property table values:
      Normal escape:    1
      Space:            2
      Dot:              4
      Hex-hex escape:   8
      -------------------
      Hex digit:       64
*/

unsigned char char_prop[ 256] = {

/* NUL SOH STX ETX EOT ENQ ACK BEL   BS  HT  LF  VT  FF  CR  SO  SI */
    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,

/* DLE DC1 DC2 DC3 DC4 NAK SYN ETB  CAN  EM SUB ESC  FS  GS  RS  US */
    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,

/*  SP  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /  */
    2,  1,  0,  1,  0,  1,  1,  1,   1,  1,  0,  1,  1,  0,  4,  0,

/*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?  */
   64, 64, 64, 64, 64, 64, 64, 64,  64, 64,  0,  1,  1,  1,  1,  1,

/*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O  */
    1, 64, 64, 64, 64, 64, 64,  0,   0,  0,  0,  0,  0,  0,  0,  0,

/*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _  */
    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  1,  0,  1,  1,  0,

/*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o  */
    1, 64, 64, 64, 64, 64, 64,  0,   0,  0,  0,  0,  0,  0,  0,  0,

/*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~  DEL */
    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  1,  0,  1,  1,  8,

    8,  8,  8,  8,  8,  8,  8,  8,   8,  8,  8,  8,  8,  8,  8,  8,
    8,  8,  8,  8,  8,  8,  8,  8,   8,  8,  8,  8,  8,  8,  8,  8,
    8,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  8
};


/* 2004-09-27 SMS.
   eat_carets().

   Delete ODS5 extended file name escape characters ("^") in the
   original buffer.
   Note that the current scheme does not handle all EFN cases, but it
   could be made more complicated.
*/

void eat_carets( char *str)
/* char *str;      Source pointer. */
{
  char *strd;   /* Destination pointer. */
  char hdgt;
  unsigned char uchr;
  unsigned char prop;

  /* Skip ahead to the first "^", if any. */
  while ((*str != '\0') && (*str != '^'))
     str++;

  /* If no caret was found, quit early. */
  if (*str != '\0')
  {
    /* Shift characters leftward as carets are found. */
    strd = str;
    while (*str != '\0')
    {
      uchr = *str;
      if (uchr == '^')
      {
        /* Found a caret.  Skip it, and check the next character. */
        uchr = *(++str);
        prop = char_prop[ uchr];
        if (prop& 64)
        {
          /* Hex digit.  Get char code from this and next hex digit. */
          if (uchr <= '9')
          {
            hdgt = uchr- '0';           /* '0' - '9' -> 0 - 9. */
          }
          else
          {
            hdgt = ((uchr- 'A')& 7)+ 10;    /* [Aa] - [Ff] -> 10 - 15. */
          }
          hdgt <<= 4;                   /* X16. */
          uchr = *(++str);              /* Next char must be hex digit. */
          if (uchr <= '9')
          {
            uchr = hdgt+ uchr- '0';
          }
          else
          {
            uchr = hdgt+ ((uchr- 'A')& 15)+ 10;
          }
        }
        else if (uchr == '_')
        {
          /* Convert escaped "_" to " ". */
          uchr = ' ';
        }
        else if (uchr == '/')
        {
          /* Convert escaped "/" (invalid Zip) to "?" (invalid VMS). */
          uchr = '?';
        }
        /* Else, not a hex digit.  Must be a simple escaped character
           (or Unicode, which is not yet handled here).
        */
      }
      /* Else, not a caret.  Use as-is. */
      *strd = uchr;

      /* Advance destination and source pointers. */
      strd++;
      str++;
    }
    /* Terminate the destination string. */
    *strd = '\0';
  }
}


/* 2005-09-29 SMS.
 *
 * vms_basename()
 *
 *    Extract the basename from a VMS file spec.
 */

const char *vms_basename( const char *file_spec)
{
    /* Static storage for NAM[L], and so on. */

    static struct NAM_STRUCT nam;
    static char exp_name[ NAM_MAXRSS+ 1];
    static char res_name[ NAM_MAXRSS+ 1];

    struct FAB fab;
    int status;

    /* Set up the FAB and NAM[L] blocks. */

    fab = cc$rms_fab;                   /* Initialize FAB. */
    nam = CC_RMS_NAM;                   /* Initialize NAM[L]. */

    fab.FAB_NAM = &nam;                 /* FAB -> NAM[L] */

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

    /* Arg name and length. */
    FAB_OR_NAM( fab, nam).FAB_OR_NAM_FNA = (char *) file_spec;
    FAB_OR_NAM( fab, nam).FAB_OR_NAM_FNS = strlen( file_spec);

    nam.NAM_ESA = exp_name;         /* Expanded name. */
    nam.NAM_ESS = NAM_MAXRSS;       /* Max length. */
    nam.NAM_RSA = res_name;         /* Resulting name. */
    nam.NAM_RSS = NAM_MAXRSS;       /* Max length. */

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

    /* Parse the file name. */
    status = sys$parse( &fab);      /* What could go wrong? */

    nam.NAM_L_NAME[ nam.NAM_B_NAME] = '\0';

    return nam.NAM_L_NAME;
}


/* 2006-02-13 SMS.
 *
 * vms_basenamex()
 *
 *    Extract the basename and filetype from a VMS file spec.
 *    Drop the filetype, if ".DIR;1".
 */

const char *vms_basenamex( const char *file_spec)
{
    /* Static storage for NAM[L], and so on. */

    static struct NAM_STRUCT nam;
    static char exp_name[ NAM_MAXRSS+ 1];
    static char res_name[ NAM_MAXRSS+ 1];

    struct FAB fab;
    int status;

    /* Set up the FAB and NAM[L] blocks. */

    fab = cc$rms_fab;                   /* Initialize FAB. */
    nam = CC_RMS_NAM;                   /* Initialize NAM[L]. */

    fab.FAB_NAM = &nam;                 /* FAB -> NAM[L] */

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

    /* Arg name and length. */
    FAB_OR_NAM( fab, nam).FAB_OR_NAM_FNA = (char *) file_spec;
    FAB_OR_NAM( fab, nam).FAB_OR_NAM_FNS = strlen( file_spec);

    nam.NAM_ESA = exp_name;         /* Expanded name. */
    nam.NAM_ESS = NAM_MAXRSS;       /* Max length. */
    nam.NAM_RSA = res_name;         /* Resulting name. */
    nam.NAM_RSS = NAM_MAXRSS;       /* Max length. */

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

    /* Parse the file name. */
    status = sys$parse( &fab);      /* What could go wrong? */

    if (status != RMS$_NORMAL)
    {
        /* Parse failed.
           Return original file spec and let someone else complain.
        */
        return file_spec;
    }
    else
    {
        /* Discard "." from a name with no type (NAM_B_TYPE == 1), and
           discard ".DIR;1" from a directory name.
        */
        if ((nam.NAM_B_TYPE == 1) ||
         (strcasecmp( nam.NAM_L_TYPE, DOT_DIRV) == 0))
        {
            nam.NAM_L_NAME[ nam.NAM_B_NAME] = '\0';
        }
        else
        {
            /* Discard ";version" from a non-directory name
               (with a non-null type).
            */
            nam.NAM_L_TYPE[ nam.NAM_B_TYPE] = '\0';
        }
        return nam.NAM_L_NAME;
    }
}



/* 2006-02-13 SMS.
 *
 * vms_basenamev()
 *
 *    Extract the basename and filetype from a VMS-ish file spec.
 */

const char *vms_basenamev( const char *file_spec)
{
    /* Static storage for NAM[L], and so on. */

    static struct NAM_STRUCT nam;
    static char exp_name[ NAM_MAXRSS+ 1];
    static char res_name[ NAM_MAXRSS+ 1];

    struct FAB fab;
    int status;

    /* Set up the FAB and NAM[L] blocks. */

    fab = cc$rms_fab;                   /* Initialize FAB. */
    nam = CC_RMS_NAM;                   /* Initialize NAM[L]. */

    fab.FAB_NAM = &nam;                 /* FAB -> NAM[L] */

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

    /* Arg name and length. */
    FAB_OR_NAM( fab, nam).FAB_OR_NAM_FNA = (char *) file_spec;
    FAB_OR_NAM( fab, nam).FAB_OR_NAM_FNS = strlen( file_spec);

    nam.NAM_ESA = exp_name;         /* Expanded name. */
    nam.NAM_ESS = NAM_MAXRSS;       /* Max length. */
    nam.NAM_RSA = res_name;         /* Resulting name. */
    nam.NAM_RSS = NAM_MAXRSS;       /* Max length. */

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

    /* Parse the file name. */
    status = sys$parse( &fab);      /* What could go wrong? */

    if (status != RMS$_NORMAL)
    {
        /* Parse failed.
           Return original file spec and let someone else complain.
        */
        return file_spec;
    }
    else
    {
        /* Discard "." from a name with no type (NAM_B_TYPE == 1).
        */
        if (nam.NAM_B_TYPE == 1)
        {
            nam.NAM_L_NAME[ nam.NAM_B_NAME] = '\0';
        }
        else
        {
            /* Discard ";version" from a name (with a non-null type).
            */
            nam.NAM_L_TYPE[ nam.NAM_B_TYPE] = '\0';
        }
        return nam.NAM_L_NAME;
    }
}


/* 2006-03-07 SMS.
 *
 * vms_basename2()
 *
 *    Extract the basename and filetype from a VMS-ish file spec.
 *    Drop the filetype, if ".DIR;1" or ".DIR".
 */

const char *vms_basename2( const char *file_spec)
{
    int len;

    /* Static storage for name. */

    static char name2[ NAM_MAXRSS+ 1];

    /* Copy the string (safely) into static storage. */
    strncpy( name2, file_spec, NAM_MAXRSS);
    name2[ NAM_MAXRSS] = '\0';

    /* Remove any trailing ".dir". */
    len = strlen( name2);
    if ((len >= sizeof( DOT_DIRV)) &&
     (strcasecmp( (name2+ len- sizeof( DOT_DIRV)+ 1), DOT_DIRV) == 0))
    {
        name2[ len- sizeof( DOT_DIRV)+ 1] = '\0';
    }
    else if ((len >= sizeof( DOT_DIR)) &&
     (strcasecmp( (name2+ len- sizeof( DOT_DIR)+ 1), DOT_DIR) == 0))
    {
        name2[ len- sizeof( DOT_DIR)+ 1] = '\0';
    }

    return name2;
}


#define PATH_DEFAULT "SYS$DISK:[]"

/* dest_struct_level()

      Returns file system structure level for argument, negative on
      error.
*/

int dest_struct_level(char *path)
{
    int acp_code;

#ifdef DVI$C_ACP_F11V5

    /* Should know about ODS5 file system.  Do actual check.
       (This should be non-VAX with __CRTL_VER >= 70200000.)
    */

    int sts;

    struct FAB fab;
    struct NAM_STRUCT nam;
    char e_name[NAM_MAXRSS + 1];

    struct dsc$descriptor_s dev_descr =
     { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0 };

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

#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 /* NAML$C_MAXRSS */

    FAB_OR_NAM(fab, nam).FAB_OR_NAM_DNA = PATH_DEFAULT;
    FAB_OR_NAM(fab, nam).FAB_OR_NAM_DNS = strlen(PATH_DEFAULT);

    FAB_OR_NAM(fab, nam).FAB_OR_NAM_FNA = path;
    FAB_OR_NAM(fab, nam).FAB_OR_NAM_FNS = strlen(path);

    nam.NAM_ESA = e_name;
    nam.NAM_ESS = sizeof(e_name)- 1;

    nam.NAM_NOP = NAM_M_SYNCHK;         /* Syntax-only analysis. */
    sts = sys$parse(&fab);

    if ((sts & STS$M_SUCCESS) == STS$K_SUCCESS)
    {
        /* Load resultant device name into device descriptor. */
        dev_descr.dsc$a_pointer = nam.NAM_L_DEV;
        dev_descr.dsc$w_length = nam.NAM_B_DEV;

        /* Get filesystem type code.
           (Text results for this item code have been unreliable.)
        */
        sts = lib$getdvi(&((int) DVI$_ACPTYPE),
                         0,
                         &dev_descr,
                         &acp_code,
                         0,
                         0);

        if ((sts & STS$M_SUCCESS) != STS$K_SUCCESS)
        {
            acp_code = -2;
        }
    }
    else
    {
        acp_code = -1;
    }

#else /* !DVI$C_ACP_F11V5 */

/* Too old for ODS5 file system.  Return level 2. */

    acp_code = DVI$C_ACP_F11V2;

#endif /* ?DVI$C_ACP_F11V5 */

    return acp_code;
}


int vms_ods2_names( char *file_spec)
{
    int ods_only;

    ods_only = (dest_struct_level( file_spec) <= DVI$C_ACP_F11V2);
    return ods_only;
}


/* 2006-03-14 SMS.  Mtools lacks a generally available verbose flag. */
#define DIAG_FLAG 0


/* 2004-11-23 SMS.
 *
 *       get_rms_defaults().
 *
 *    Get user-specified values from (DCL) SET RMS_DEFAULT.  FAB/RAB
 *    items of particular interest are:
 *
 *       fab$w_deq         default extension quantity (blocks) (write).
 *       rab$b_mbc         multi-block count.
 *       rab$b_mbf         multi-buffer count (used with rah and wbh).
 */

/* Default RMS parameter values. */

#define RMS_DEQ_DEFAULT 16384   /* About 1/4 the max (65535 blocks). */
#define RMS_MBC_DEFAULT 127     /* The max, */
#define RMS_MBF_DEFAULT 2       /* Enough to enable rah and wbh. */

/* Durable storage */

static int rms_defaults_known = 0;

/* JPI item buffers. */
static unsigned short rms_ext;
static char rms_mbc;
static unsigned char rms_mbf;

/* Active RMS item values. */
unsigned short rms_ext_active;
char rms_mbc_active;
unsigned char rms_mbf_active;

/* GETJPI item lengths. */
static short rms_ext_len;       /* Should come back 2. */
static short rms_mbc_len;       /* Should come back 1. */
static short rms_mbf_len;       /* Should come back 1. */

/* GETJPI item descriptor set. */

struct
    {
    xxi_item_t rms_ext_itm;
    xxi_item_t rms_mbc_itm;
    xxi_item_t rms_mbf_itm;
    int term;
    } jpi_itm_lst =
     { { 2, JPI$_RMS_EXTEND_SIZE, &rms_ext, &rms_ext_len },
       { 1, JPI$_RMS_DFMBC, &rms_mbc, &rms_mbc_len },
       { 1, JPI$_RMS_DFMBFSDK, &rms_mbf, &rms_mbf_len },
       0
     };

int get_rms_defaults()
{
int sts;

/* Get process RMS_DEFAULT values. */

sts = sys$getjpiw( 0, 0, 0, &jpi_itm_lst, 0, 0, 0);
if ((sts& STS$M_SEVERITY) != STS$M_SUCCESS)
    {
    /* Failed.  Don't try again. */
    rms_defaults_known = -1;
    }
else
    {
    /* Fine, but don't come back. */
    rms_defaults_known = 1;
    }

/* Limit the active values according to the RMS_DEFAULT values. */

if (rms_defaults_known > 0)
    {
    /* Set the default values. */

    rms_ext_active = RMS_DEQ_DEFAULT;
    rms_mbc_active = RMS_MBC_DEFAULT;
    rms_mbf_active = RMS_MBF_DEFAULT;

    /* Default extend quantity.  Use the user value, if set. */
    if (rms_ext > 0)
        {
        rms_ext_active = rms_ext;
        }

    /* Default multi-block count.  Use the user value, if set. */
    if (rms_mbc > 0)
        {
        rms_mbc_active = rms_mbc;
        }

    /* Default multi-buffer count.  Use the user value, if set. */
    if (rms_mbf > 0)
        {
        rms_mbf_active = rms_mbf;
        }
    }

if (DIAG_FLAG)
    {
    fprintf( stderr,
     "Get RMS defaults.  getjpi sts = %%x%08x.\n",
     sts);

    if (rms_defaults_known > 0)
        {
        fprintf( stderr,
         "               Default: deq = %6d, mbc = %3d, mbf = %3d.\n",
         rms_ext, rms_mbc, rms_mbf);
        }
    }
return sts;
}


/* 2004-11-23 SMS.
 *
 *       acc_cb(), access callback function for DEC C fopen().
 *
 *    Set some RMS FAB/RAB items, with consideration of user-specified
 * values from (DCL) SET RMS_DEFAULT.  Items of particular interest are:
 *
 *       fab$w_deq         default extension quantity (blocks).
 *       rab$b_mbc         multi-block count.
 *       rab$b_mbf         multi-buffer count (used with rah and wbh).
 *
 *    See also the FOP* macros in VMS.H.  Currently, no notice is
 * taken of the caller-ID value, but options could be set differently
 * for read versus write access.  (I assume that specifying fab$w_deq,
 * for example, for a read-only file has no ill effects.)
 */

/* Global storage. */

int fopr_id = FOPR_ID;          /* Callback id storage, read. */
int fopw_id = FOPW_ID;          /* Callback id storage, write. */

/* acc_cb() */

int acc_cb( int *id_arg, struct FAB *fab, struct RAB *rab)
{
int sts;

/* Get process RMS_DEFAULT values, if not already done. */
if (rms_defaults_known == 0)
    {
    get_rms_defaults();
    }

/* If RMS_DEFAULT (and adjusted active) values are available, then set
 * the FAB/RAB parameters.  If RMS_DEFAULT values are not available,
 * suffer with the default parameters.
 */
if (rms_defaults_known > 0)
    {
    /* Set the FAB/RAB parameters accordingly. */
    fab-> fab$w_deq = rms_ext_active;
    rab-> rab$b_mbc = rms_mbc_active;
    rab-> rab$b_mbf = rms_mbf_active;

    /* Truncate at EOF on close, as we'll probably over-extend. */
    fab-> fab$v_tef = 1;

    /* If using multiple buffers, enable read-ahead and write-behind. */
    if (rms_mbf_active > 1)
        {
        rab-> rab$v_rah = 1;
        rab-> rab$v_wbh = 1;
        }

    /* Set the "sequential access only" flag to avoid excessive lock
       time when writing on a file system with highwater marking
       enabled.
    */
    fab-> fab$v_sqo = 1;

    if (DIAG_FLAG)
        {
        fprintf( stderr,
         "Open callback.  ID = %d, deq = %6d, mbc = %3d, mbf = %3d.\n",
         *id_arg, fab-> fab$w_deq, rab-> rab$b_mbc, rab-> rab$b_mbf);
        }
    }

/* Declare success. */
return 0;
}



/* 2006-02-13 SMS.
 *
 *       VMS block device I/O functions.
 *
 *    Note that the high 16 bits in the (32-bit) int "file descriptor"
 *    are used for various purposes.  See "vms.h".
 */

#define CHAN_ACT( ch) ((unsigned short) (ch& VMS_FD_CMASK))

int vms_block_open( const char *name)
{
    unsigned short chan;
    int sts;
    struct dsc$descriptor_s name_dsc;

    static char name_phy[ 65];          /* Physical device name*/
    $DESCRIPTOR( name_phy_dsc,          /* name and descriptor. */
     name_phy);

    /* Item list structures for GETDVI. */

    static unsigned int dvi_mounted;
    static short dvi_mounted_len;

    static unsigned int dvi_foreign;
    static short dvi_foreign_len;

    struct
    {
        xxi_item_t dvi_itmlst_mnt;     /* Mounted? */
        xxi_item_t dvi_itmlst_for;     /* Mounted foreign? */
        int term;
    } dvi_itmlst =
       {
        { sizeof( dvi_mounted), DVI$_MNT, &dvi_mounted, &dvi_mounted_len },
        { sizeof( dvi_foreign), DVI$_FOR, &dvi_foreign, &dvi_foreign_len },
        0
       };


    /* Item list structures for MOUNT. */

    int mnt_flags[2] =
     { MNT$M_FOREIGN| MNT$M_NOASSIST, 0};

    struct
    {
        xxi_item_t name;
        xxi_item_t flags;
        int term;
    } mnt_itmlst =
       {
         { 0, MNT$_DEVNAM, NULL, NULL },
         { sizeof( mnt_flags), MNT$_FLAGS, &mnt_flags, NULL },
         0
       };


    int ret = 0;


    name_dsc.dsc$a_pointer = (char *) name;
    name_dsc.dsc$w_length = strlen( name);


    /* Allocate the device. */

    sts = sys$alloc( &name_dsc,
                     &name_phy_dsc.dsc$w_length,
                     &name_phy_dsc,
                     0,
                     0);

    if ((sts & STS$M_SEVERITY) != STS$M_SUCCESS)
    {
        printf( " Error in alloc of %s.  sts = %%x%08x.\n",
         name, sts);

        printf( " %s\n", strerror( EVMSERR, sts));

        ret = -1;
    }
    else
    {
        /* NUL-terminate the physical device name. */

        name_phy_dsc.dsc$a_pointer[ (int)name_phy_dsc.dsc$w_length] = '\0';

        /* Open (assign) device. */

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

        if ((sts & STS$M_SEVERITY) != STS$M_SUCCESS)
        {
            printf( " Error in assign of %s.  sts = %%x%08x.\n",
             name, sts);

            printf( " %s\n", strerror( EVMSERR, sts));
            ret = -1;
        }
    }

    if (ret >= 0)
    {

        /* Check status of device.  If mounted, must be mounted foreign. */

        sts = sys$getdviw( 0,                   /* Event flag nr. */
                           chan,                /* Channel. */
                           0,                   /* Device name. */
                           &dvi_itmlst,         /* Item list. */
                           0,                   /* IOSB. */
                           0,                   /* AST address. */
                           0,                   /* AST parameter. */
                           0);                  /* Null argument. */

        if ((sts & STS$M_SEVERITY) != STS$M_SUCCESS)
        {
            printf( " Error in getdvi of %s (%s).  sts = %%x%08x.\n",
             name, name_phy, sts);

            printf( " %s\n", strerror( EVMSERR, sts));
            ret = -1;
        }
        else if ((dvi_mounted != 0) && (dvi_foreign == 0))
        {
            printf( " Device %s (%s) is mounted, but not mounted foreign.\n",
             name, name_phy);

            ret = -1;
        }
        else if (dvi_mounted == 0)
        {
            /* Release the channel to allow mounting. */

            sts = sys$dassgn( chan);

            /* Fill in MOUNT item list data. */

            mnt_itmlst.name.buf_len = name_phy_dsc.dsc$w_length;
            mnt_itmlst.name.buf = name_phy_dsc.dsc$a_pointer;

            /* Mount the device. */

            sts = sys$mount( &mnt_itmlst);

            if ((sts & STS$M_SEVERITY) != STS$M_SUCCESS)
            {
                printf( " Error in mount of %s (%s).  sts = %%x%08x.\n",
                 name, name_phy, sts);

                printf( " %s\n", strerror( EVMSERR, sts));

                ret = -1;
            }
            else
            {
                printf( " %s (%s) mounted.\n", name, name_phy);

                /* Re-assign the device. */

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

                if ((sts & STS$M_SEVERITY) != STS$M_SUCCESS)
                {
                    printf( " Error in assign of %s (%s).  sts = %%x%08x.\n",
                     name, name_phy, sts);

                    printf( " %s\n", strerror( EVMSERR, sts));
                    ret = -1;
                }
            }
        }
    }

    if (ret >= 0)
    {
        /* Return channel, with channel flag. */
        ret = chan| VMS_FD_CHAN;

        /* If it wasn't originally mounted, we must have mounted it. */
        if (dvi_mounted == 0)
        {
            ret |= VMS_FD_MOUNTED;
        }
    }
    return ret;
}

int vms_block_close( int chan_i)
{
    int sts;
    int flags = 0;
    int ret = 0;

    static char name_phy[ 65];          /* Physical device name*/
    $DESCRIPTOR( name_phy_dsc,          /* name and descriptor. */
     name_phy);

    /* Item list structures for GETDVI. */

    struct
    {
        xxi_item_t dvi_itmlst_anm;     /* Allocatable device name */
        int term;
    } dvi_itmlst =
       {
        { sizeof( name_phy),
          DVI$_ALLDEVNAM,
          name_phy_dsc.dsc$a_pointer,
          (short *) &name_phy_dsc.dsc$w_length
        },
        0
       };

    if (chan_i& VMS_FD_MOUNTED)
    {
        /* We mounted it.  Dismount it now. */
        sts = sys$getdviw( 0,                   /* Event flag nr. */
                           CHAN_ACT( chan_i),   /* Channel. */
                           0,                   /* Device name. */
                           &dvi_itmlst,         /* Item list. */
                           0,                   /* IOSB. */
                           0,                   /* AST address. */
                           0,                   /* AST parameter. */
                           0);                  /* Null argument. */

        if ((sts & STS$M_SEVERITY) != STS$M_SUCCESS)
        {
            printf( " Error in getdvi of %s.  sts = %%x%08x.\n",
             name_phy, sts);

            printf( " %s\n", strerror( EVMSERR, sts));
            ret = -1;
        }
        else
        {

            /* NUL-terminate the name. */
            name_phy_dsc.dsc$a_pointer[ name_phy_dsc.dsc$w_length] = '\0';

            sts = sys$dassgn( CHAN_ACT( chan_i));
            if ((sts & STS$M_SEVERITY) != STS$M_SUCCESS)
            {
                printf( " Error in dassgn of %s, sts = %%x%08x.\n",
                 name_phy, sts);

                printf( " %s\n", strerror( EVMSERR, sts));
                ret = -1;
            }

            if (chan_i& VMS_FD_NOUNLOAD)
            {
                flags |= DMT$M_NOUNLOAD;
            }

            sts = sys$dismou( &name_phy_dsc, flags);

            if ((sts & STS$M_SEVERITY) != STS$M_SUCCESS)
            {
                printf( " Error in dismou of %s.  sts = %%x%08x.\n",
                 name_phy, sts);

                printf( " %s.\n", strerror( EVMSERR, sts));
                ret = -1;
            }

            printf( " %s dismounted.\n", name_phy);

            sts = sys$dalloc( &name_phy_dsc, PSL$C_USER);

            if ((sts & STS$M_SEVERITY) != STS$M_SUCCESS)
            {
                printf( " Error in dalloc of %s.  sts = %%x%08x.\n",
                 name_phy_dsc.dsc$a_pointer, sts);

                printf( " %s.\n", strerror( EVMSERR, sts));
                ret = -1;
            }
        }
    }
    else
    {
        sts = sys$dassgn( CHAN_ACT( chan_i));
        if ((sts & STS$M_SEVERITY) != STS$M_SUCCESS)
        {
            printf( " Error in dassgn of %s.  sts = %%x%08x.\n",
             name_phy, sts);

            printf( " %s\n", strerror( EVMSERR, sts));
            ret = -1;
        }
    }

    return ret;
}

int vms_block_read( int chan_i, char *buf, off_t where, size_t len)
{
    unsigned int lbn;                   /* Logical block number. */
    int ret;
    int sts;
    struct _iosb iosb_rd;               /* I/O status block. */

    lbn = where/ 512;

    sts = sys$qiow( 0,                  /* Event flag nr. */
                    CHAN_ACT( chan_i),  /* Channel. */
                    IO$_READLBLK,       /* Function code. */
                    &iosb_rd,           /* IOSB. */
                    0,                  /* AST address. */
                    0,                  /* AST parameter. */
                    buf,                /* P1 = buffer address. */
                    len,                /* P2 = byte count. */
                    lbn,                /* P3 = logical block nr. */
                    0,                  /* P4. */
                    0,                  /* P5. */
                    0);                 /* P6. */

    /* If initial status is success, use final status. */

    if ((sts & STS$M_SEVERITY) == STS$M_SUCCESS)
    {
        sts = iosb_rd.iosb$w_status;
    }

    if ((sts & STS$M_SEVERITY) != STS$M_SUCCESS)
    {
        printf( " Error in qiow (read) of %08x.  sts = %%x%08x.\n",
         chan_i, sts);

        printf( " %s.\n", strerror( EVMSERR, sts));
        ret = -1;
    }
    else
    {
        ret = iosb_rd.iosb$l_bcnt;
    }

    return ret;
}


int vms_block_write( int chan_i, char *buf, off_t where, size_t len)
{
    unsigned int lbn;                   /* Logical block number. */
    int ret;
    int sts;
    struct _iosb iosb_wr;               /* I/O status block. */

    lbn = where/ 512;

    sts = sys$qiow( 0,                  /* Event flag nr. */
                    CHAN_ACT( chan_i),  /* Channel. */
                    IO$_WRITELBLK,      /* Function code. */
                    &iosb_wr,           /* IOSB. */
                    0,                  /* AST address. */
                    0,                  /* AST parameter. */
                    buf,                /* P1 = buffer address. */
                    len,                /* P2 = byte count. */
                    lbn,                /* P3 = logical block nr. */
                    0,                  /* P4. */
                    0,                  /* P5. */
                    0);                 /* P6. */

    /* If initial status is success, use final status. */

    if ((sts & STS$M_SEVERITY) == STS$M_SUCCESS)
    {
        sts = iosb_wr.iosb$w_status;
    }

    if ((sts & STS$M_SEVERITY) != STS$M_SUCCESS)
    {
        printf( " Error in qiow (read) of %08x.  sts = %%x%08x.\n",
         chan_i, sts);

        printf( " %s.\n", strerror( EVMSERR, sts));
        ret = -1;
    }
    else
    {
        ret = iosb_wr.iosb$l_bcnt;
    }

    return ret;
}


pid_t fork( void)
{
    printf( "*** Dummy fork() ***\n");
    return -1;
}



/*
 * 2004-09-19 SMS.
 *
 *----------------------------------------------------------------------
 *
 *       decc_init()
 *
 *    On non-VAX systems, uses LIB$INITIALIZE to set a collection of C
 *    RTL features without using the DECC$* logical name method.
 *
 *----------------------------------------------------------------------
 */

#ifdef __CRTL_VER

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

#include <unixlib.h>

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

/* Global storage. */

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

int decc_init_done = -1;

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

/* decc_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 decc_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. */

decc_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. */
         fprintf( stderr,
          " 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. */
      fprintf( stderr,
       " UNKNOWN DECC FEATURE: %s.\n", decc_feat_array[i].name);
      }
   }
}

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

#pragma nostandard

/* Establish the LIB$INITIALIZE PSECT, with proper alignment and
   attributes.
*/
globaldef { "LIB$INITIALIZ" } readonly _align (LONGWORD)
   int spare[8] = { 0 };
globaldef { "LIB$INITIALIZE" } readonly _align (LONGWORD)
   void (*x_decc_init)() = decc_init;

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

#endif /* def __CRTL_VER */

