#pragma module sysmaniogen "x-13"
/*
**	
******************************************************************************
**                                                                           *
**  Copyright (c) 1992, 1994, 1996                                           *
**  By DIGITAL Equipment Corporation, Maynard, Mass.                         *
**                                                                           *
**  This software is furnished under a license and may be used and  copied   *
**  only  in  accordance  with  the  terms  of  such  license and with the   *
**  inclusion of the above copyright notice.  This software or  any  other   *
**  copies  thereof may not be provided or otherwise made available to any   *
**  other person.  No title to and ownership of  the  software  is  hereby   *
**  transferred.                                                             *
**                                                                           *
**  The information in this software is subject to change  without  notice   *
**  and  should  not  be  construed  as  a commitment by DIGITAL Equipment   *
**  Corporation.                                                             *
**                                                                           *
**  DIGITAL assumes no responsibility for the use or  reliability  of  its   *
**  software on equipment which is not supplied by DIGITAL.                  *
**                                                                           *
******************************************************************************
**++
**
** Facility:
**
**     SYSMAN
**
** Abstract:
**
**	This module implements the "command smarts" for the IO commands in the
**	SYSMAN utility.  It is cloned directly from the stopgap IOGEN.C written
**	by Walter Blaschuk.
**
** Authors:
**
**	Richard W. Critz, Jr.
**      Walter D. Blaschuk, Jr.
**	28-May-1992
**
** Modified by:
**
**      X-14	GCE		Glenn C. Everhart		13-May-1997
**		Add processing to handle HSZ alloc classes on sysman io connect
**		commands. This processing assumes that SCSI devices will
**		be handled by cKdriver (c=M, G, or D) and that the port
**		driver name can be computed by changing DKB310: (for
**		example) to PKB0:. This processing will be put in its own
**		subroutine to make it easier to extend for general naming.
**      X-13    WDA             W.D. Arbo                       07-Oct-1996
**              Add code to process /VERIFY qualifier on REBUILD command.
**
**      X-12    WDA             W.D. Arbo                       13-Aug-1996
**              Fix bugs in X-11.  Make REBUILD work across nodes and clusters.
**
**      X-11    WDA             W.D. Arbo                       19-Apr-1996
**              Add routine sysman$io_rebuild to process
**              $SYSMAN IO REBUILD command.  It rebuilds configuration
**              tables hanging off the ADPs.  This is part of file based
**              autoconfiguration.
**
**	X-10	MAS		Mary A. Sullvian		19-Jan-1996
**		Change name of variable 'index' to 'ile_index' to avoid a
**		conflict with a new function in string.h.
**		
**	X-9	RWC148		Richard W. Critz, Jr.		 7-Oct-1994
**		Add support for /NOVECTOR on the CONNECT command.
**		
**	X-8	RWC141	       	Richard W. Critz, Jr.		26-Jan-1994
**		Add IO SET/SHOW EXCLUDE commands.
**		
**	X-7	RWC131		Richard W. Critz, Jr.		 9-Jul-1993
**		Remove inappropriate checking of /NUM_UNITS against /MAX_UNITS,
**		defaulted or otherwise.  That's the job of $LOAD_DRIVER and
**		should not be duplicated here.  Also, remove references to
**		private copies of IOGENDEF.H and NDTDEF.H now that they are
**		available in SYS$LIB_C.
**		
**	X-6	RWC113		Richard W. Critz, Jr.		29-Jan-1993
**		Add the ShowNode routine and insert appropriate calls to same.
**		
**	X-5	RWC099		Richard W. Critz, Jr.		 9-Sep-1992
**		The second argument (TEXT_ONLY) to PRINT_ERR is not optional.
**		Unfortunately, I didn't realize that when I created this module
**		and this results in some strange behavior.  Fill in the blanks,
**		as it were.  Also, add some noise to the SHOW PREFIX command if
**		the prefix list is empty so that it actually tells the user
**		that.
**		
**	X-4	RWC094		Richard W. Critz, Jr.		16-Jul-1992
**		Fix uninitialized index in IO CONNECT command.
**		
**	X-3	RWC092		Richard W. Critz, Jr.		 9-Jul-1992
**		Add support for the /NODE qualifier on the IO CONNECT command.
**		Make everything static that can be.
**		
**	X-2	RWC087		Richard W. Critz, Jr.		 9-Jun-1992
**		Remove reference to obsolete VAXCSHR.H.
**		
** 	X-1	RWC082		Richard W. Critz, Jr.		28-May-1992
**		Initial version.
**		
**--
*/

#include <climsgdef.h>
#include <config_table.h>
#include <descrip.h>
#include <iogendef.h>
#include <ndtdef.h>
#include <ssdef.h>
#include <string.h> 
#include <ucbdef.h>
#include        exe_routines            /* exe$ and exe_std$ routines */
#include        ioc_routines            /* ioc$ and ioc_std$ routines */
#include        ldr_routines            /* ldr$ and ldr_std$ routines */
#include        sch_routines
#include        com_routines
#include devdef
#include        dcdef                   /* Device codes */
#include        ddbdef                  /* Device data block */
#include        ddtdef                  /* Driver dispatch table */
#include        devdef                  /* Device characteristics */
#include        dptdef                  /* Driver prologue table */
#include        dyndef                  /* Dynamic type definitions */
#include        iodef                   /* I/O function codes */
#include        vms_macros              /* Define bug_check and such */
#include        vms_drivers             /* Device driver support macros */


#include "lib$:smidef.h"
#include "lib$:smiiogendef.h"

void call_test_switch(UCB* ucb);


#define DYN_DESC		{0, DSC$K_DTYPE_T, DSC$K_CLASS_D, 0}
#define STAT_DESC(a)		{0, DSC$K_DTYPE_T, DSC$K_CLASS_S, a}

#define max_itmlst_entries	20	/* Largest possible item list        */
#define max_buslist_entries	16	/* max num of entries in AUTOCONFIG  */
					/* bus list			    */
#define max_number_of_vectors	64	/* max num of vector offsets per drvr*/
#define max_list_desc_length    508	/* max len for select & exclude list */
#define MAXSHOWLEN		132	/* buffer length for SHOW data from */
					/* SMI object			    */

#define one_byte		1	/* one byte length for buffer size   */
#define two_bytes		2	/* word length for buffer size       */
#define four_bytes		4	/* longword length for buffer size   */
#define eight_bytes		8	/* quadword length for buffer size   */

#define return_len		0	/* Unused field in an item list entry*/

#define LOAD_LOG		1       /* Output the LOAD created structures*/
#define CONN_LOG		0	/* Output the CONN created structures*/

#define FALSE			0
#define TRUE			1

typedef struct quadword_result		/* Set up bit oriented quadword      */
   {					/* For conversions.		     */
	unsigned n0  : 4;
	unsigned n1  : 4;
	unsigned n2  : 4;
	unsigned n3  : 4;
	unsigned n4  : 4;
	unsigned n5  : 4;
	unsigned n6  : 4;
	unsigned n7  : 4;
	unsigned n8  : 4;
	unsigned n9  : 4;
	unsigned n10 : 4;
	unsigned n11 : 4;
	unsigned n12 : 4;
	unsigned n13 : 4;
	unsigned n14 : 4;
	unsigned n15 : 4;

   } quadword_result;

extern int smictx;		/* SYSMAN context block */
extern int action_addr;		/* SYSMAN's "do it" routine pointer */
extern int CTL$GL_PCB;

extern int print_err();		/* SYSMAN error output routine */
extern int print_line();	/* SYSMAN normal output routine */
extern int smi$iogen();		/* the IOGEN SMI object dispatcher */
extern int smi$get_nodename();	/* returns name of execution node */

/* Data fields used in doing autoconfiguration */ 
static int		auto_flags;
static int		bus_list[max_buslist_entries+1];
static char		select_list_array[max_list_desc_length];
static char		exclude_list_array[max_list_desc_length];
static int              function_code;
static struct dsc$descriptor	select_list = STAT_DESC(select_list_array);
static struct dsc$descriptor	exclude_list = STAT_DESC(exclude_list_array);
static struct dsc$descriptor	bus_list_desc = STAT_DESC(bus_list);

/* Data fields used in doing SET PREFIX.  Note that the select and exclude  */
/* list arrays are available during SET PREFIX.  Reuse the former to save   */
/* space.								    */
static struct dsc$descriptor	prefix_list = STAT_DESC(select_list_array);

/* Need a comma str descriptor in order to separate the select & exclude list 
   elements. This is for desc_cat.  Also, need an "empty" message for display in
   SHOW PREFIX when the prefix list is empty. */
static $DESCRIPTOR(comma_desc,","); 
static $DESCRIPTOR(sys_desc,"SYS$");
static $DESCRIPTOR(empty_prefix_list,"(empty)");

/* These are the qualifiers for the IO AUTOCONFIGURE command */
static $DESCRIPTOR(buslist,"BUS");
static $DESCRIPTOR(exclude,"EXCLUDE");
static $DESCRIPTOR(prefix,"PREFIX");
static $DESCRIPTOR(select,"SELECT");
static $DESCRIPTOR(full,"FULL");
static $DESCRIPTOR(verify, "VERIFY");

/* These are the qualifiers for the CONNECT/LOAD/RELOAD commands */
static $DESCRIPTOR(adapter,"ADAPTER");
static $DESCRIPTOR(csr,"CSR");
static $DESCRIPTOR(device,"DEVICE");
static $DESCRIPTOR(driver,"DRIVER");
static $DESCRIPTOR(driver_name,"DRIVER_NAME");
static $DESCRIPTOR(file_spec,"FILE_SPEC");
static $DESCRIPTOR(log,"LOG");
static $DESCRIPTOR(max_units,"MAX_UNITS");
static $DESCRIPTOR(node,"NODE");
static $DESCRIPTOR(num_units,"NUM_UNITS");
static $DESCRIPTOR(num_vec,"NUM_VEC");
static $DESCRIPTOR(vector_spacing,"VECTOR_SPACING");
static $DESCRIPTOR(sys_id,"SYS_ID");
static $DESCRIPTOR(vector,"VECTOR");

/* String descriptors for the /LOG qualifier in LOAD and CONNECT */
static $DESCRIPTOR(ALL,"ALL");
static $DESCRIPTOR(DCRB,"CRB");
static $DESCRIPTOR(DDDB,"DDB");
static $DESCRIPTOR(DDPT,"DPT");
static $DESCRIPTOR(DIDB,"IDB");
static $DESCRIPTOR(DSB,"SB");
static $DESCRIPTOR(DUCB,"UCB");

/* String descriptors for the input values for each qualifier */
static struct dsc$descriptor  adapt_desc = DYN_DESC;
static int	  adapt_number; 		

static struct dsc$descriptor csr_desc = DYN_DESC;
static quadword_result	  csr_addr;	                                   

static long  crb_addr	     = NULL;    /* Holds SYSLOA created CRB address */

static struct dsc$descriptor device_desc = DYN_DESC;

static struct dsc$descriptor driver_desc = DYN_DESC;

static struct dsc$descriptor file_spec_desc = DYN_DESC;

static struct dsc$descriptor log_desc = DYN_DESC;

static struct dsc$descriptor maxunits_desc = DYN_DESC;
static int		  maximum_units;

/* Port driver descriptor template */
 $DESCRIPTOR(PKNAM,"PKA0:");
 $DESCRIPTOR(DEVUNIT,"000000");

static struct dsc$descriptor node_desc = DYN_DESC;
static int  node_number;

static struct dsc$descriptor numunits_desc = DYN_DESC;
static int	  number_units;

static struct dsc$descriptor numvec_desc = DYN_DESC;
static int	  	  num_vector;       
                                          
static struct dsc$descriptor sysid_desc = DYN_DESC;
static quadword_result	  system_id;	

static struct dsc$descriptor vector_spacing_desc = DYN_DESC;
static int		  vector_spacing_val;

static struct dsc$descriptor vector_desc = DYN_DESC;
static long  vector_off[max_number_of_vectors]; /* array of 64 bytes now */


typedef struct item_entry {
	     unsigned short int buffer_size; /* Status of the operation */
             unsigned short int item_code;   /* Indicates this entry's purpose*/
	     void		*buffer;     /* Pointer to buffer */
	     long int		ret_len;     /* Unused will contain zero */
	    } item_entry;		     /* item list entry */

static int		  ile_index = -1;	/* index into itemlist inited to -1*/
static struct item_entry  itemlist[max_itmlst_entries];  /* Array of item entries */
 

/* Fields for the addresses of the newly created control blocks */
/* for LOAD and CONNECT commands			 	*/
static long	new_crb;
static long	new_ddb;
static long	new_idb;
static long	new_sb;
static long	new_ucb;
static long	new_dpt;

/* Fields to indicate which control blocks were requested for /LOGing  */
static unsigned log_crb;
static unsigned log_ddb;
static unsigned log_dpt;
static unsigned log_idb;
static unsigned log_sb;
static unsigned log_ucb;

static unsigned short int number_of_vectors;	/* number of vectors specified in
					   vector list.			*/

/* When adding to this list, update the #define MAX_AT_INDEX */
static char *AdapterTable[] = {
			"MBA",			    /*  0 */
			"UBA",			    /*  1 */
			"DR32",			    /*  2 */
			"MPM",			    /*  3 */
			"CI",			    /*  4 */
			"Null",			    /*  5 */
			"KDB50",		    /*  6 */
			"DMB32",		    /*  7 */
			"DRB32",		    /*  8 */
			"BVP",			    /*  9 */
			"BVP SSP",		    /* 10 */
			"BVP NIP",		    /* 11 */
			"KA410/KA420",		    /* 12 */
			"GENBI",		    /* 13 */
			"NBIB",			    /* 14 */
			"DISK 009",		    /* 15 */
			"XBIB",			    /* 16 */
			"TERM9",		    /* 17 */
			"TAPE9",		    /* 18 */
			"Printer 009",		    /* 19 */
			"SFUN9",		    /* 20 */
			"USER9",		    /* 21 */
			"MBUS IO",		    /* 22 */
			"MBUS GFX",		    /* 23 */
			"KA640",		    /* 24 */
			"XWATCH",		    /* 25 */
			"XBI+XMI",		    /* 26 */
			"XBI+BI",		    /* 27 */
			"XJA",			    /* 28 */
			"KDM70",		    /* 29 */
			"NI",			    /* 30 */
			"KA43",			    /* 31 */
			"SJA",			    /* 32 */
			"GENXMI",		    /* 33 */
			"KA440/KA46",		    /* 34 */
			"KA520",		    /* 35 */
			"XSA XMI-SCSI",		    /* 36 */
			"XZA XMI-SCSI",		    /* 37 */
		      	"XMI-VME",		    /* 38 */
			"IOP",			    /* 39 */
			"LAMB",			    /* 40 */
			"KA49",			    /* 41 */
			"TURBOCHAN",		    /* 42 */
			"X1303",		    /* 43 */
			"XMI",			    /* 44 */
			"FBUS",			    /* 45 */
			"COREIO",		    /* 46 */
			"CSB",			    /* 47 */
			"LBUS",			    /* 48 */
			"LSB",			    /* 49 */
			"FSB",			    /* 50 */
			"TURBO_SCSI",		    /* 51 */
			"CIMNA",		    /* 52 */
			"XZA_DSSI",		    /* 53 */
			"DEMNA",		    /* 54 */
			"FFA",			    /* 55 */
			"Unknown"		    /* Always leave this at */
						    /* the end of the	    */
						    /* table, indexed as    */
						    /* MAX_AT_INDEX + 1	    */
			}; 
/* When adding to this list up the MAX_AT_INDEX */
#define MAX_AT_INDEX	55

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

static void add_item_entry(int buffer_size,int item_code,long *buffer,long int r_len)
/* add_item_entry:
   FUNCTION:
	Fills in the item list entry and increments global index into the
	itemlist array. 
   IMPLEMENTATION:
	Straight line fill in the item list with inputs.
   ENVIRONMENT:
	User Mode. 
   INPUTS:
	Size of buffer
	Item code
	Address of buffer
	returns nothing
   OUTPUTS:
	Upon completion of this routine an entry has bee added to the itemlist.

*/
{
	ile_index++;		/* increment global index into the array */
				/* to point to the new array slot	 */

	itemlist[ile_index].buffer_size = buffer_size;
	itemlist[ile_index].item_code   = item_code;
	itemlist[ile_index].buffer 	= buffer;
	itemlist[ile_index].ret_len     = r_len;

} /*  end of add_item_entry() */

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

/* terminate_itemlist:
   FUNCTION:
	Terminates the entry in the itemlist with all zeros.
   IMPLEMENTATION:
	Straight line code using add_item_entry().
   ENVIRONMENT:
	User Mode. 
   INPUTS:
	No explicit inputs.
   OUTPUTS:
   	In the last entry there is a terminating long word of zeros.
*/
static void terminate_itemlist()
{
   add_item_entry(0,0,NULL,0);

} /*  end of terminate_itemlist() */

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

static void ShowNode(void)
/*	  
**  Get the nodename for the current execution node and display it in a
**  "SYSMAN-consistent" style
*/	  
{
    struct dsc$descriptor nodeName = DYN_DESC;

    print_line(0, 1, &nodeName);    /* print a blank line to improve	    */
				    /* display readability		    */
				    
    smi$get_nodename(&smictx, &nodeName);
    print_line(SYSMAN$_OUTPUT, 0, &nodeName);
}

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

static int show_log(void)
/*
   If the address is zero then the control block requested was not
   created during this invocation.  Print out an appropriate message.
*/
{
    if (log_crb || log_ddb || log_dpt || log_idb || log_sb || log_ucb)
	ShowNode();

if (log_crb) 
   if (new_crb != 0) 
 	print_line(SYSMAN$_IOADDRESS, 0, &DCRB, new_crb);
   else 
 	print_line(SYSMAN$_IONOTEXIST, 0, &DCRB);

if (log_ddb) 
   if (new_ddb != 0) 
 	print_line(SYSMAN$_IOADDRESS, 0, &DDDB, new_ddb);
   else 
 	print_line(SYSMAN$_IONOTEXIST, 0, &DDDB);

if (log_dpt) 
   if (new_dpt != 0)
 	print_line(SYSMAN$_IOADDRESS, 0, &DDPT, new_dpt);
   else 
 	print_line(SYSMAN$_IONOTEXIST, 0, &DDPT);

if (log_idb) 
   if (new_idb != 0) 
 	print_line(SYSMAN$_IOADDRESS, 0, &DIDB, new_idb);
   else 
 	print_line(SYSMAN$_IONOTEXIST, 0, &DIDB);

if (log_sb) 
   if (new_sb != 0) 
 	print_line(SYSMAN$_IOADDRESS, 0, &DSB, new_sb);
   else 
 	print_line(SYSMAN$_IONOTEXIST, 0, &DSB);

if (log_ucb) 
   if (new_ucb != 0) 
 	print_line(SYSMAN$_IOADDRESS, 0, &DUCB, new_ucb);
   else 
 	print_line(SYSMAN$_IONOTEXIST, 0, &DUCB);

       
return SS$_NORMAL;

} /*  end of show_log() */

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

static int convert_quadword(char *s,quadword_result *quadword_addr)
/*
   FUNCTION:
   IMPLEMENTATION:
   ENVIRONMENT:
	User Mode. 
   INPUTS:
   OUTPUTS:
*/
{
int i	    = 0;	/* index into nibble array */
int eos_cnt = 18;	/* The string will not be anymore than 18 chars 
			   including the % and X */
quadword_result qwr;


        qwr.n0  = 0;
        qwr.n1  = 0;
        qwr.n2  = 0;
        qwr.n3  = 0;
        qwr.n4  = 0;
        qwr.n5  = 0;
        qwr.n6  = 0;
        qwr.n7  = 0;
        qwr.n8  = 0;
        qwr.n9  = 0;
        qwr.n10 = 0;
        qwr.n11 = 0;
        qwr.n12 = 0;
        qwr.n13 = 0;
        qwr.n14 = 0;
        qwr.n15 = 0;

	for (;;s++) {

		if (eos_cnt-- == 0) break;  /* dont look too far */

		switch (*s) {
		case '%':
			continue;

		case 'X':
			continue;
			
		case '0':
		case '1':	
		case '2':	
		case '3':	
		case '4':	
		case '5':	
		case '6':	
		case '7':	
		case '8':	
		case '9':	
		case 'A':	
		case 'B':	
		case 'C':	
		case 'D':	
		case 'E':	
		case 'F':
			/* keep scanning until the end of the string is 
			   located. */
			continue;

		default:
			break;	

		}

	break;  /* break out of for loop when at end of string */

	} /* end of for */

	s--;	/* string now points to least significan digit */


	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n0 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n0 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n1 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n1 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n2 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n2 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n3 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n3 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n4 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n4 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n5 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n5 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n6 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n6 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n7 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n7 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n8 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n8 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n9 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n9 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n10 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n10 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n11 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n11 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n12 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n12 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n13 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n13 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n14 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n14 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 
	if (!(*s == 'X')) {
		if (*s >= '0' && *s <= '9') {
			qwr.n15 = *s-- - '0';
		} else {
		    if (*s >= 'A' && *s <= 'F') {
			qwr.n15 = *s-- - '7';
		    } /* end if */
		} /* end if/else */
	} /* end if */ 


  *quadword_addr = qwr;

return SS$_NORMAL;

}  /* end of convert_quadword()*/

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

static int convert(struct dsc$descriptor *d)
/*
  convert():
  	Converts ASCII decimal string to a given sized binary value
 	and returns the address of the result.  Note that DCL handles
	radix specifiers for values of type $NUMBER and converts them
	all to decimal with no radix specifier
 
  input:
  	s		pointer to a character string descriptor

  output: 
 	binary value of input
 
*/
{
int  r = 0;
int  i;
char *s = d->dsc$a_pointer;

  for (i = 0; i < d->dsc$w_length; i++)
	r = r * 10 - '0' + *s++;

  return r;

} /* end of convert */

/*****************************************************************************/
       
/* init_log:
   FUNCTION:
   	/LOG: Qualifier code for both LOAD and CONNECT commands.
   	Calls add_item_entry to request the addresses of all the created 
	control blocks. Increments the index as needed.
   IMPLEMENTATION:
	Straight line code.
   ENVIRONMENT:
	User Mode. 
   INPUTS:
	Whether it is a LOAD or a CONNect.  LOADs only get DPT.
   OUTPUTS:
*/
static int init_log(int log_type)
{
int status;

 new_crb = 0;	/* Set each cell which will contain the     */
 new_ddb = 0;	/* the address of the newly created control */ 
 new_idb = 0;	/* control blocks were not requested by the */
 new_sb  = 0;	/* user when it become time to output the   */
 new_ucb = 0;	/* results.				    */
 new_dpt = 0;
 
 log_crb = FALSE;   /* Used as flags to indicate which control  */
 log_ddb = FALSE;   /* blocks are requested for display         */
 log_dpt = FALSE;
 log_idb = FALSE;
 log_sb  = FALSE;
 log_ucb = FALSE;

if (CLI$_PRESENT == cli$present(&log)) {

 if (log_type == LOAD_LOG) {
   log_dpt = TRUE;
   return SS$_NORMAL;
 }

 /* Get the first element in the DCL command line list */
 status = cli$get_value(&log, &log_desc);

 /* CLI$_ABSENT can not be returned because "ALL" is the default */
 if (desc_comp(&log_desc,&ALL)) { 
   add_item_entry(four_bytes,IOGEN$_CRB,&new_crb,return_len);
   log_crb = TRUE;
   add_item_entry(four_bytes,IOGEN$_DDB,&new_ddb,return_len);
   log_ddb = TRUE;
   /* The DPT is gotten from the returned IOSB not from itemlist pointer */
   log_dpt = TRUE;
   add_item_entry(four_bytes,IOGEN$_IDB,&new_idb,return_len);
   log_idb = TRUE;
   add_item_entry(four_bytes,IOGEN$_SB,&new_sb,return_len);
   log_sb  = TRUE;
   add_item_entry(four_bytes,IOGEN$_UCB,&new_ucb,return_len);
   log_ucb = TRUE;
 } else {

   while (status == CLI$_COMMA || status == SS$_NORMAL) {
	if (desc_comp(&log_desc,&DCRB)) {
   	   add_item_entry(four_bytes,IOGEN$_CRB,&new_crb,return_len);
	   log_crb = TRUE;
	}
	if (desc_comp(&log_desc,&DDDB)) {
   	   add_item_entry(four_bytes,IOGEN$_DDB,&new_ddb,return_len);
	   log_ddb = TRUE;
	}
	if (desc_comp(&log_desc,&DDPT)) {
   	   /* The DPT is gotten from the returned IOSB,      */ 
	   /* not from itemlist pointer just set the log_bit */
	   log_dpt = TRUE;
	}
	if (desc_comp(&log_desc,&DIDB)) {
	   add_item_entry(four_bytes,IOGEN$_IDB,&new_idb,return_len);
	   log_idb = TRUE;
	}
	if (desc_comp(&log_desc,&DSB)) {
	   add_item_entry(four_bytes,IOGEN$_SB,&new_sb,return_len);
	   log_sb = TRUE;
	}
	if (desc_comp(&log_desc,&DUCB)) {
	   add_item_entry(four_bytes,IOGEN$_UCB,&new_ucb,return_len);
	   log_ucb = TRUE;
	}
	status = cli$get_value(&log, &log_desc);    /* Get next elem in list */
  } /* end while == COMMA */

 } /* End if/else */

} /* end if log qualifier is present */

return SS$_NORMAL;

} /*  end of init_log() */

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

static int get_driver(void)
/*
   FUNCTION:
   IMPLEMENTATION:
	Straight line code.
   ENVIRONMENT:
	User Mode. 
   INPUTS:
   OUTPUTS:
*/
{
int status;

  if (CLI$_PRESENT == cli$present(&driver_name))
        status = cli$get_value(&driver_name, &driver_desc);
  else {
	driver_desc.dsc$w_length = 0;
	status = FALSE;
  } /* end if cli$_present */


 return status;

} /* end of get_driver*/

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

static int get_units(void)
/*
   FUNCTION:
	Get both max_number of units and number of units.
   IMPLEMENTATION:
	Straight line code.
   ENVIRONMENT:
	User Mode. 
   INPUTS:
   OUTPUTS:
*/
{
int status;

  if (CLI$_PRESENT == cli$present(&num_units)) {
   status = cli$get_value(&num_units, &numunits_desc);
   if (!(status & SS$_NORMAL))
	return FALSE;
   
   number_units = convert(&numunits_desc);
   
   add_item_entry(one_byte,
                  IOGEN$_NUMUNITS,
                  &number_units,
                  return_len);
  } /* end if cli$_present */

  if (CLI$_PRESENT == cli$present(&max_units)) {
   status = cli$get_value(&max_units, &maxunits_desc);

   if (!(status & SS$_NORMAL))
	return FALSE;
	
   maximum_units = convert(&maxunits_desc);

   add_item_entry(one_byte,
                  IOGEN$_MAXUNITS,
                  &maximum_units,
                  return_len);
  } /* end if cli$_present */

 return SS$_NORMAL;

} /* end of get_units()*/

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

static int get_sys_id(void)
/*
   FUNCTION:
   IMPLEMENTATION:
   ENVIRONMENT:
   INPUTS:
	No explicit inputs.
   OUTPUTS:
*/
{
int status;

  if (CLI$_PRESENT == cli$present(&sys_id)) {
   status = cli$get_value(&sys_id, &sysid_desc);
                      
   if (status & SS$_NORMAL) {
	status = convert_quadword(sysid_desc.dsc$a_pointer,&system_id);
	if (!(status & SS$_NORMAL)) {
		print_err(SYSMAN$_INVPARAM, 0, &sysid_desc);
		return FALSE;
	}
   }
   add_item_entry(eight_bytes,
                  IOGEN$_SYSID,
                  &system_id,
                  return_len);
  } /* end if cli$_present */

 return SS$_NORMAL;

} /* end of get_sys_id()*/

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

static int get_adapter(void)
/*
   FUNCTION:
   IMPLEMENTATION:
   ENVIRONMENT:
   INPUTS:                               
	No explicit inputs.
   OUTPUTS:
*/
{
int status;

	if (CLI$_PRESENT == cli$present(&adapter)) {
                status = cli$get_value(&adapter, &adapt_desc);

		if (!(status & SS$_NORMAL))
		    return status;
		    
		adapt_number = convert(&adapt_desc);
		
                add_item_entry(two_bytes,
                               IOGEN$_ADAPTER,
                               &adapt_number,
                               return_len);
	} /* end of if adapter present */

 return SS$_NORMAL;

}/* end of get_adapter() */

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

static int get_csr(void)
/*
   FUNCTION:
   IMPLEMENTATION:
   ENVIRONMENT:
   INPUTS:
	No explicit inputs.
   OUTPUTS:
*/
{
int status;

	if (CLI$_PRESENT == cli$present(&csr)) {
                status = cli$get_value(&csr, &csr_desc);
 		if (status & SS$_NORMAL) {
		   status =  convert_quadword(csr_desc.dsc$a_pointer,&csr_addr);
	 	   if (!(status & SS$_NORMAL)) {
			print_err(SYSMAN$_INVPARAM, 0, &csr_desc);
 			return FALSE;
	 	   }
		}
                add_item_entry(eight_bytes,
                               IOGEN$_CSR,
                               &csr_addr,
                               return_len);
	} /* csr present */

 return SS$_NORMAL;

}/* end of get_csr() */

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

static int get_vectors(void)
/*
   FUNCTION:
	Gets the  I/O vectors required  OR  gets number of  vectors and vector 
	spacing and the base I/O vector; then builds the vector list. 
	Or just gets the number of vectors and  allocates them  the builds the
	vector list.  
	
   IMPLEMENTATION:
	If  no vectors have been specified and  SYSLOA has  previously created 
	the CRB, IDB and allocated vectors then  SYSLOA_CRB must be set in the
	SYS$LOAD_DRIVER call.  This is the case for the XMI.
	If  no vectors have been supplied  and only the number of vectors have
	been supplied then interrupt vectors must been allocated.  This is the
	case for the Futurebus+.  Set_up_vectors will be called to use
	ioc$reserve_scb.
	For  Turbochannel  the vectors are fixed  and  are associated with and 
	determined by their position within the backplane slots.  Turbochannel
	uses one interrupt vector which is determined by  the alogorithm which
	the user  will use  to specify  the vectors in  the qualifier /VECTOR=
	(vector = slot_number * 4).

   INPUTS:
	No explicit inputs.
   OUTPUTS:
*/
{
int i;				/* Used in for loop */
int status;
int vector_buff_size = 0;

number_of_vectors    = 0;
num_vector = 1;	/* Default is for the num_vector input is 1 */

/* Handle /NOVECTOR first before checking anything else */
if (CLI$_NEGATED == cli$present(&vector)) {
	vector_off[0] = -1;

	add_item_entry(four_bytes,
		       IOGEN$_VECTOR,
		       vector_off,
		       return_len);

	return SS$_NORMAL;
}

if (CLI$_PRESENT == cli$present(&num_vec)) { /* Is there a NUM_VEC qualifier? */
					     /* Yes - Get it... 
					     /*      if not the default is one*/
     cli$get_value(&num_vec, &numvec_desc);

     num_vector = convert(&numvec_desc); 

} 	  /* Num_vector is present and it's value has been received */

if (CLI$_PRESENT  != cli$present(&vector))  /* /VECTORS is not present.     */
		if (cli$present(&num_vec)) {
			print_err(SYSMAN$_BADVECTOR, 0);
			return FALSE;     /* Just return and report it */
		} /* if else */

if (num_vector == 1) {  /* Have a list or 1, no vector spacing*/

       status = cli$get_value(&vector, &vector_desc);
       if (status == SS$_NORMAL) {
       	   vector_off[0] = convert(&vector_desc);

       	   add_item_entry(four_bytes,
                          IOGEN$_VECTOR,
                          vector_off,
                          return_len);

       }  		/* process only one vector */
	else 
	    {		/* process more than one vector, no vector_spacing*/
	   while (status != CLI$_ABSENT) {
	   	vector_off[number_of_vectors] = convert(&vector_desc);

		number_of_vectors++;
		
	   	vector_buff_size += four_bytes;

	        status = cli$get_value(&vector, &vector_desc);
           } /* end of while cli$get_value for vector */

     	   add_item_entry(vector_buff_size,
		    	  IOGEN$_VECTOR,
                    	  vector_off,
                    	  return_len);

     } /* status == SS$_NORMAL 1 vector or a list? */


} /* End if/else : There was one vector or a list no vector spacing */


if (num_vector > 1) {  /* Have a list of vecs with vector spacing*/
	status = cli$get_value(&vector, &vector_desc);
                   
	if (status == SS$_NORMAL) {  /* Got the one and only vector - good */
	   vector_off[0] = convert(&vector_desc);
	   vector_buff_size = four_bytes;

	   if (CLI$_PRESENT == cli$present(&vector_spacing)) {
	     status = cli$get_value(&vector_spacing, &vector_spacing_desc);
	     vector_spacing_val = convert(&vector_spacing_desc);

	   }  	/* end of if vector_spacings present */
	     else 
		  vector_spacing_val = 16; /* set the default to 16 */

	   for (i=1; i < num_vector; i++) {

	   	/* Build the vector list vector (lw) at a time */
	   	vector_off[i] = vector_off[i-1] + vector_spacing_val;

	   	/* the number_of_vectors (started at 0 as index) */
	   	vector_buff_size += four_bytes;

	   } /* End of for loop building the vector list */  

     	   add_item_entry(vector_buff_size,
		     	    IOGEN$_VECTOR,
                            &vector_off[0],
                            return_len);

	}  	/* End of Have 1 vector and vector spacing is present */
	 else 
	     {	/* This is the case of the XMI - Vectors had to be allocated. */
		status = SS$_NORMAL;
 	} /* End of if/else cli$_get_val vector gets more then 1 vector */	

} /* num_vector > 1 */

return status;

}/* end of get_vectors() */

/*****************************************************************************/
static void get_node_number()
{
    if (CLI$_PRESENT == cli$present(&node)) {
	cli$get_value(&node, &node_desc);
	node_number = convert(&node_desc);

	add_item_entry(sizeof(node_number),
		       IOGEN$_NODE,
		       &node_number,
		       return_len);
    }
} /*end of get_node_number() */
/*****************************************************************************/

static int find_bus_str(int *bus_lst[],struct dsc$descriptor *s)
/*
	Called form to find the bus string when attempting an Autoconfig
	with the /BUS=() qualifier.
*/
{
int AT_index	= 0;
int found     	= FALSE;

        while (!found && AT_index <= MAX_AT_INDEX ) {
	    found =	/* compstrg returns TRUE if strings are equal */
	      compstrg(s,AdapterTable[AT_index++]);
	} /* end of AdapterTable search */

	if (found) { /* returns SS$_NORMAL if strings are = */
	      bus_lst[ile_index++] = --AT_index;
	      return SS$_NORMAL;
	} else 
	      return FALSE;

} /* end of find_bus_str */

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

static int DoSmiIogen()
/*	  
**  Called by SYSMAN to actually invoke the SMI object.
*/	  
{
    int			    status;
    int			    firstTime;
    char		    ShowBuf[MAXSHOWLEN];
    struct dsc$descriptor   buffer = STAT_DESC(ShowBuf);

    /*	  
    **  Do the call to cause the function to occur
    */	  
    
    status = smi$iogen(&smictx,
		       function_code,
		       auto_flags,	/* IOGEN$AUTOCONFIGURE flags */
		       &select_list,	/* select list               */
		       &exclude_list,	/* exclude list		     */
		       0,		/* no output buffer	     */
		       0,		/* no item list		     */
		       &bus_list_desc); /* bus selection list	     */

    if (!(status & SS$_NORMAL)) {
	print_err(status, 0);
	return status;
    }

    /*	  
    **  Now drain any logged messages and display them to the user.
    */	  

    firstTime = TRUE;
    do {
	buffer.dsc$w_length = MAXSHOWLEN;
	status = smi$iogen(&smictx,
			   SMI$K_FUNC_SHOW_NEXT,
			   SMIIOGEN$K_SHOW_AUTOLOG,	/* show what?	*/
			   0,				/* no string1	*/
			   0,				/* no string 2	*/
			   &buffer);			/* data buffer	*/

	if ((status & SS$_NORMAL) && (status != SMI$_NOMORE)) {
	    if (firstTime) {
		ShowNode();
		firstTime = FALSE;
	    }
	    print_line(0, 1, &buffer);
	}
    } while ((status & SS$_NORMAL) && (status != SMI$_NOMORE));
       
    if (status != SMI$_NOMORE)
	print_err(status, 0);
    else
	status = SS$_NORMAL;
                                      
    return status;
    
}   /* end of DoSmiIogen */

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

int sysman$io_auto(void)
/* sysman$io_auto:
   FUNCTION:
	Builds the autoconfigure parameter list then calls IOGEN$AUTOCON.
   IMPLEMENTATION:
	Gets the /LOG qualifier if it exists, then the /SELECT list if it exists
	then the /EXCLUDE list if it exists, the the /BUS list if it exists and
	finally calls the IOGEN SMI object dispatcher with the appropriate
	parameters.
   INPUTS:
	No explicit inputs.      
   OUTPUTS:
	None.
*/
{ 
int			i;
int			status;
struct dsc$descriptor	select_elem_desc = DYN_DESC;
struct dsc$descriptor	exclude_elem_desc = DYN_DESC;
struct dsc$descriptor	buslist_desc = DYN_DESC;

    /*	  
    **  Initialize the static data fields used by AUTOCONFIGURE.
    */	  
    
    auto_flags = 0;
    select_list.dsc$w_length = 0;
    exclude_list.dsc$w_length = 0;
    ile_index = 0;

    /* Note that there are actually max_buslist_entries + 1 entries in the  */
    /* bus_list array so that there can be a 0 terminator as required by    */
    /* autoconfigure							    */
    for (i = 0; i <= max_buslist_entries; i++)
	bus_list[i] = 0;
   
  /* 
      /LOG: Autoconfigure qualifier to indicate to the ICBMs, that each device 
	    as it is configured, will be logged to SYS$OUTPUT by calling 
	    IOGEN$LOG.
  */
  if (CLI$_PRESENT == cli$present(&log)) {
	auto_flags = auto_flags | IOGEN$M_AC_LOG;	
  } /* end if log */

  /*
      /FULL: Autoconfigure qualifier to indicate to the ICBMs that "noisy"
             messages should be logged.
  */

  if (CLI$_PRESENT == cli$present(&full)) {
        auto_flags = auto_flags | IOGEN$M_AC_LOG_ALL;
  }  /* end if full */
 
  /*
      /SELECT: Autoconfigure qualifier to indicate which device names are to be 
	    configured in the system.
  */
  if (cli$present(&select)) {

  /* Get the first element in the DCL command line SELECT=([list], */
  status = cli$get_value(&select, &select_elem_desc);
  desc_cat(&select_list,&select_elem_desc);	/* Concat elem to list*/

  while (status == CLI$_COMMA) {
	desc_cat(&select_list,&comma_desc);		/* Concat the comma   */
	status = cli$get_value(&select,	&select_elem_desc);	/* Get the next elem  */
	desc_cat(&select_list,&select_elem_desc);   	/* Concat the next ele*/
  } /* end while */
  } /* end if select */

   /*
      /EXCLUDE: Autoconfigure qualifier to indicate which device names are to
	    be excluded from the configured system.
   */
   if (cli$present(&exclude)) {

   /* Get the first element in the DCL command line EXCLUDE=([list],) */
   status = cli$get_value(&exclude, &exclude_elem_desc);
   desc_cat(&exclude_list,&exclude_elem_desc);	/* Concat elem to list*/

   while (status == CLI$_COMMA) {
	desc_cat(&exclude_list,&comma_desc);		/* Concat the comma   */
	status = cli$get_value(&exclude, &exclude_elem_desc);	/* Get the next elem  */
	desc_cat(&exclude_list,&exclude_elem_desc); 	/* Concat the next ele*/
   } /* end while */
  } /* end if exclude */


  /*
      /BUS: Autoconfigure qualifier to indicate on which buses to restrict the
	  actions of Autoconfigure. 
  */

  status = cli$present(&buslist);

  if (status == CLI$_PRESENT) {
     
     status = cli$get_value(&buslist, &buslist_desc); /* Get first element */
     find_bus_str(&bus_list,&buslist_desc);	/* find AT number for this string */

     while (status == CLI$_COMMA ) {  /* If more get next elem */
	 status = cli$get_value(&buslist, &buslist_desc);
         find_bus_str(&bus_list, &buslist_desc);
     } /* end of while */
  } /* end if buslist */

   bus_list_desc.dsc$w_length = ile_index * 4;

   function_code = SMI$K_FUNC_AUTO;

   action_addr = DoSmiIogen;
    
   return SS$_NORMAL;	

} /* end of sysman$io_auto */


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

int sysman$io_rebuild(void)
/* sysman$io_rebuild:
   FUNCTION:
        This routine implements the SYSMAN IO REBUILD command.
        It puts DoSmiIogen into action_addr, which causes 
        smi$iogen to be called (with the REBUILD function code).
        SMI$IOGEN will call [sys]READ_CONFIG.C to read the
        SYS$SYSTEM:SYS$USER_CONFIG.DAT and SYS$SYSTEM:CONFIG.DAT
        files, and rebuild the config tables hanging off the
        ADPs.

   INPUTS:
	No explicit inputs.      
   OUTPUTS:
	Global data cells used by smi$iogen:
	    auto_flags
	    select_list
	    exclude_list
            bus_list
	    function_code
*/
{ 
    int status,i;

    auto_flags = 0;
    select_list.dsc$w_length = 0;
    exclude_list.dsc$w_length = 0;

    /* Note that there are actually max_buslist_entries + 1 entries in the  */
    /* bus_list array so that there can be a 0 terminator as required by    */
    /* autoconfigure							    */

    for (i = 0; i <= max_buslist_entries; i++)
	bus_list[i] = 0;

    /* Attempt to find /VERIFY */

    if (CLI$_PRESENT == cli$present(&verify)) {
        auto_flags = auto_flags | IOGEN$M_AC_VERIFY;
    }
 
    /*	  
    **  Do the call to cause a rebuild to occur
    */	  
    
    function_code = SMI$K_FUNC_CONFIG_REBUILD;

    action_addr = DoSmiIogen;

    return SS$_NORMAL;

} /* end of sysman$io_rebuild */

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

static int DoSetPrefix(void)
/*	  
**  Invoke the SMI object to set the prefix list.
*/	  
{
    int	    status;

    status = smi$iogen(&smictx,
		       SMI$K_FUNC_MODIFY,	/* do SET           */
		       SMIIOGEN$K_SET_PREFIX,	/* SET PREFIX	    */
		       &prefix_list);		/* the list	    */

    return status;

}   /* end of DoSetPrefix */

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

int sysman$io_set_prefix(void)
/* Set_prefix:
   FUNCTION:
	Sets the current prefix list. 
   IMPLEMENTATION:
	Builds a prefix list string descriptor from what is read in from the
	screen inserting commas between the elements. Call SET_PREFIX.  
   INPUTS:
	No explicit inputs.
   OUTPUTS:
	None.
*/
{                      
  struct dsc$descriptor	    prefix_element = DYN_DESC;
  int			    status;
    
  prefix_list.dsc$w_length = 0;  /* Initialize the list to be empty */


  /* Get the first element in the DCL command line list */
  status = cli$get_value(&prefix, &prefix_element);
  if (status != CLI$_ABSENT) 
    	desc_cat(&prefix_list,&prefix_element);	/* Concat elem to list*/

  while (status == CLI$_COMMA) {
       	desc_cat(&prefix_list,&comma_desc);	/* Concat the comma   */
	status = cli$get_value(&prefix, &prefix_element); /* Get the next elem  */
	desc_cat(&prefix_list,&prefix_element);   /* Concat the next ele*/
  } /* end while */

  if (status & SS$_NORMAL)
    action_addr = DoSetPrefix;

  return status;

} /* End of sysman$io_set_prefix */

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

static int DoShowPrefix(void)
/*	                         
**  Invoke the SMI object to show the current prefix list.  Note that the static
**  select_list_array is unused at this point so it can be reused here.  Same
**  goes for the prefix_list descriptor.
*/	  
{
    int	    status;

    prefix_list.dsc$w_length = max_list_desc_length;

    status = smi$iogen(&smictx,
		       SMI$K_FUNC_SHOW,
		       SMIIOGEN$K_SHOW_PREFIX,	    /* show what    */
		       0,			    /* no string1   */
		       0,			    /* no string2   */
		       &prefix_list);		    /* data buffer  */

    if (status & SS$_NORMAL) {
	ShowNode();
	if (prefix_list.dsc$w_length != 0)
	    print_line(SYSMAN$_IOPREFIX, 0, &prefix_list);
	else
	    print_line(SYSMAN$_IOPREFIX, 0, &empty_prefix_list);
    }
    else
	print_err(status, 0);

    return status;

}   /* end of DoShowPrefix */

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

int sysman$io_show_prefix(void)
/* Show_prefix:
   FUNCTION:
	Sets things up for DoShowPrefix, which actually does the work.
   INPUTS:
	None
   OUTPUTS:
	None
*/
{
    action_addr = DoShowPrefix;

    return SS$_NORMAL;   

} /* End of sysman$io_show_prefix */

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

static int DoSetExclude(void)
/*	  
**  Invoke the SMI object to set the prefix list.
*/	  
{
    int	    status;

    status = smi$iogen(&smictx,
		       SMI$K_FUNC_MODIFY,	/* do SET	    */
		       SMIIOGEN$K_SET_EXCLUSION,/* SET EXCLUDE	    */
		       &prefix_list);		/* the list	    */

    return status;

}   /* end of DoSetExclude */

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

int sysman$io_set_exclude(void)
/* Set_exclude:
   FUNCTION:
	Sets the current permanent exclusion list. 
   IMPLEMENTATION:
	Builds an exclusion list string descriptor from what is read in from the
	screen inserting commas between the elements. Note that the prefix list
	buffer can be reused since we cannot be using it at the moment.
   INPUTS:
	No explicit inputs.
   OUTPUTS:
	None.
*/
{
  struct dsc$descriptor	    exclude_element = DYN_DESC;
  int			    status;
    
  prefix_list.dsc$w_length = 0;  /* Initialize the list to be empty */


  /* Get the first element in the DCL command line list */
  status = cli$get_value(&exclude, &exclude_element);
  if (status != CLI$_ABSENT) 
    	desc_cat(&prefix_list,&exclude_element);/* Concat elem to list*/

  while (status == CLI$_COMMA) {
       	desc_cat(&prefix_list,&comma_desc);	/* Concat the comma   */
	status = cli$get_value(&exclude, &exclude_element); /* Get the next elem  */
	desc_cat(&prefix_list,&exclude_element);   /* Concat the next ele*/
  } /* end while */

  if (status & SS$_NORMAL)
    action_addr = DoSetExclude;

  return status;

} /* End of sysman$io_set_exclude */

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

static int DoShowExclude(void)
/*	                         
**  Invoke the SMI object to show the current permanent exclusion list.  Note
**  that the static select_list_array is unused at this point so it can be
**  reused here.  Same goes for the prefix_list descriptor.
*/	  
{
    int	    status;

    prefix_list.dsc$w_length = max_list_desc_length;

    status = smi$iogen(&smictx,
		       SMI$K_FUNC_SHOW,
		       SMIIOGEN$K_SHOW_EXCLUSION,   /* show what    */
		       0,			    /* no string1   */
		       0,			    /* no string2   */
		       &prefix_list);		    /* data buffer  */

    if (status & SS$_NORMAL) {
	ShowNode();
	if (prefix_list.dsc$w_length != 0)
	    print_line(SYSMAN$_IOEXCLUDE, 0, &prefix_list);
	else
	    print_line(SYSMAN$_IOEXCLUDE, 0, &empty_prefix_list);
    }
    else
	print_err(status, 0);

    return status;

}   /* end of DoShowExclude */

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

int sysman$io_show_exclude(void)
/* Show_prefix:
   FUNCTION:
	Sets things up for DoShowExclude, which actually does the work.
   INPUTS:
	None
   OUTPUTS:
	None
*/
{
    action_addr = DoShowExclude;

    return SS$_NORMAL;   

} /* End of sysman$io_show_exclude */
                                
/*****************************************************************************/

static int DoConnect(void)           
/*	  
**  Invoke the SMI object to perform the connect.  Then call show_log to do any
**  necessary logged output.
*/	  
{
    int	    status;
    struct dsc$descriptor *d = 0;

    if (driver_desc.dsc$w_length != 0)
	d = &driver_desc;

    status = smi$iogen(&smictx,
		       SMI$K_FUNC_CONNECT,
		       0,		    /* no flags		*/
		       &device_desc,	    /* device		*/
		       d,		    /* driver		*/
		       0,		    /* no output buffer	*/
		       itemlist,	    /* itemlist		*/
		       0,		    /* no bus list	*/
		       &new_dpt);	    /* DPT addr return	*/

    if (!(status & SS$_NORMAL)) {
	print_err(status, 0);
	return status;
    }

    show_log();

    return SS$_NORMAL;

}   /* end of DoConnect */

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

int sysman$io_connect(void)
/* iogen_connect_cmd:
   FUNCTION:
   IMPLEMENTATION:
   ENVIRONMENT:
	User Mode.  Called from main.
   INPUTS:
	None
   OUTPUTS:
	Upon completion of this routine a device is connected.
*/
{
 int	status;
 UCB*   lcl_ucb;
 unsigned int arglist[2];
 long iosb[2];
 char inquirybuf[255];
 int cdbescsi;
 int chan;
 int alcls;
 int id;
 char * kp;
 char * kkp;
 char * wkp;
 char * wkp2;
 char ck;

 cdbescsi = 0;
 ile_index = -1;	/* initialize index into itemlist */
 
 status = cli$get_value(&device, &device_desc);

 status = get_driver();


/* Now fill in the required itemlist entries if there are any. */

 status = init_log(CONN_LOG);	

 if (CLI$_NEGATED == cli$present(&adapter)) {
   /* Handle the /NOADAPTER case of CONNECT */

   add_item_entry(0,IOGEN$_NOADAPTER,NULL,return_len);

   cdbescsi = 1;
   status = get_sys_id();

/*   terminate_itemlist(); */

 } else {	/* Do the ADAPTER CONNECT */

   status = get_adapter();

   status = get_csr();

   status = get_vectors();

   status = get_units();

   get_node_number();
   
/*   terminate_itemlist();     */

 } /* end if/else cli$present(&noadapter) */

 if (!(status & SS$_NORMAL)) {
    print_err(status, 0);
    return status;
 }
/* Before we allow the connect, we must send an INQUIRY to the
   port driver if this is a SCSI device. Since the UCB isn't there
   yet, just check to see if this is xKdriver. Once we get INQUIRY
   data we can tell whether a controller allocation class (for HSZ50/70)
   is being used and fill that in, so the driver will be loaded right,
   and so it will get a new DDB if necessary. This is the major reason
   for not putting this material into the drivers or load_driver
   itself. The port driver start_io isn't good for much besides INQUIRY
   but it will work for that. */
/* Since these HSZ allocation classes will be for entire IDs, we will
   only check LUN 0. */

   kp = device_desc.dsc$a_pointer;
   wkp = kp + 1;
   if ((cdbescsi != 0) && (toupper(*wkp)) == 'K'){
     kkp = PKNAM.dsc$a_pointer;
/* form PKc0: descriptor */
/* Names are DKc or PKc so copy the "c" part */
     wkp = kp + 2;
     wkp2 = kkp + 2;
     *wkp2 = *wkp;
     status = sys$assign(&PKNAM,&chan,0,0,0);
     if ((status & 1) != 0){
/* Must find the ID to use. Get from device name/100 for now. */
       id = 0;
       wkp = kkp + 4;
       id = atoi(wkp);
       id = id / 100;
       if (id < 0) id = 0;
       if (id > 65535) id = 0;
       status = sys$qiow(1,chan,(IO$_READLBLK+IO$M_INHERLOG),&iosb,0,0,
         &inquirybuf[0],255,id,0,0,0);
       if ((status & 1) != 0){
         /* If this is an HSZ50 or 70 then get the allocation class if
            one is present, and if so add it to the parameters for the
            connection */
/* Add also a test that the inquiry buffer in fact is long enough to have
   the device allocation class. If it is not, there can be none. */
         if (inquirybuf[16] == 'H' &&
             inquirybuf[17] == 'S' &&
             inquirybuf[18] == 'Z' &&
             inquirybuf[4] > 97 &&
/* HSZ50, HSZ70, HSZ80 */
             (inquirybuf[19] == '5' ||
              inquirybuf[19] == '8' ||
              inquirybuf[19] == '7')){
/* Looks like an HSZ. The allocation class is at 102 so grab that */
             alcls = inquirybuf[102];
/* Bias the load by 20000000 hex to get load_driver to know it is not a
   Port Alloc Class */
             if (alcls > 0){
               alcls = alcls + 1073741824;
/* Now add the itemlist entry to load the driver with */
               add_item_entry(four_bytes,IOGEN$_ALLOCLS,&alcls,return_len);         
             }
           }
       }
     }
   }

/* Return the UCB address to allow switching to hook in */
 add_item_entry(four_bytes,IOGEN$_UCB,&lcl_ucb,return_len);
 terminate_itemlist();    


 action_addr = DoConnect;
 
/* Check if a disk was connected and ensure, if so, that a duplicate
   device has not been added, or if one has, hook it to the switching
   driver. */
 if (((long)lcl_ucb < 0) && (lcl_ucb->ucb$b_devclass == DC$_DISK)){
/* Note MUST have I/O mutex and forklock to call test_switch */
   arglist[0] = 1;
   arglist[1] = (unsigned int)&lcl_ucb;
   sys$cmkrnl(&call_test_switch, arglist);
 }

 return SS$_NORMAL;

} /*  end of sysman$io_connect() */

/* call_test_switch -
   Get the fork lock and i/o db write mutex, call test_switch, then undo
   locking and return. Called from kernel mode at IPL 2 initially. */
/*
   Environment: Kernel mode
   Processing:
   Calls swdriver routine to find if the device at UCB is a second
   device of the same name and if so hooks them up as aliases via
   SWdriver.
   Input:
   UCB of device to check for dual pathing on.
   Output: 
   none
   */


void call_test_switch(UCB* ucb){
   PCB* mypcb;
   int savipl;

   mypcb = CTL$GL_PCB;
/* Acquire the I/O mutex and the forklock, then set up the switching
   structures if necessary, undo the locking, and return. */
   sch_std$iolockw(mypcb);
   fork_lock(ucb->ucb$b_flck,(int*)&savipl);

/* Here look inside the driver if it's scsi and see if there's a device
   port id... or maybe dkdriver can set that up by itself. */

   ioc_std$test_switch(ucb);

   fork_unlock(ucb->ucb$b_flck,savipl,SMP_RESTORE);
   sch_std$iounlock(mypcb);
   return;
}

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

static int DoLoad(void)
/*	  
**  Invoke the SMI object to do the actual load.  Then call show_log to display
**  any information the user requested to be logged.
*/	  
{
    int	    status;

    status = smi$iogen(&smictx,
		       SMI$K_FUNC_LOAD,
		       0,		    /* no flags		*/
		       0,		    /* no device	*/
		       &driver_desc,	    /* driver		*/
		       0,		    /* no output buffer	*/
		       0,		    /* no itemlist	*/
		       0,		    /* no bus list	*/
		       &new_dpt);	    /* DPT addr return	*/

    if (!(status & SS$_NORMAL)) {
	print_err(status, 0);
	return status;
    }

    show_log();

    return SS$_NORMAL;

}   /* end of DoLoad */

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

int sysman$io_load(void)
/* iogen_load_cmd:
   FUNCTION:
	Load the driver and print out the control blocks that were created if
	requested.
	
   IMPLEMENTATION:
	Pick up the driver filespec and have at it.
	
   ENVIRONMENT:
	User Mode.  Called from main.

   INPUTS:
	None
   OUTPUTS:
	None
*/
{
    int	    status;

    cli$get_value(&file_spec, &driver_desc); /* Get the file-spec		     */

    status = init_log(LOAD_LOG);	

    action_addr = DoLoad;
    
    return SS$_NORMAL;

} /*  end of sysman$io_load() */

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

static int DoReload(void)
/*	  
**  Invoke the SMI object to do the actual reload.  Then call show_log to display
**  any information the user requested to be logged.
*/	  
{
    int	    status;

    status = smi$iogen(&smictx,
		       SMI$K_FUNC_RELOAD,
		       0,		    /* no flags		*/
		       0,		    /* no device	*/
		       &driver_desc,	    /* driver		*/
		       0,		    /* no output buffer	*/
		       0,		    /* no itemlist	*/
		       0,		    /* no bus list	*/
		       &new_dpt);	    /* DPT addr return	*/

    if (!(status & SS$_NORMAL)) {
	print_err(status, 0);
	return status;
    }          

    show_log();

    return SS$_NORMAL;

}   /* end of DoReload */

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

int sysman$io_reload(void)
/* iogen_load_cmd:
   FUNCTION:
	Load the driver and print out the control blocks that were created if
	requested.
	
   IMPLEMENTATION:
	Pick up the driver filespec and have at it.
	
   ENVIRONMENT:
	User Mode.  Called from main.

   INPUTS:
	None
   OUTPUTS:
	None
*/
{
    int	    status;

    cli$get_value(&file_spec, &driver_desc); /* Get the file-spec		     */

    status = init_log(LOAD_LOG);	

    action_addr = DoReload;
    
    return SS$_NORMAL;

} /*  end of sysman$io_reload() */

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

static int compstrg(struct dsc$descriptor *s,char *t)
/* Compares two strings char string within a descriptor and char string t. 
*/
{
    int i = 0;

   while (s->dsc$w_length >= i && t[i] != '\0' && s->dsc$a_pointer[i] == t[i]) {
	i++;
    } /* end while */
    /* If they are equal i will be index + 1 & i will equal the dsc$w_length */
    if (s->dsc$w_length == i && t[i] == '\0')
	return TRUE;
    else
	return FALSE; 
	

} /* end of compstrg */

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

static int desc_cat(struct dsc$descriptor *s, 
	    struct dsc$descriptor *t)
/* Concat two strings: descriptor string s and char string t. 
	Similar to strncat.
	Where s is the destination 
	and   t is the source.
*/
{
    int j;
    int i = 0;	/* indexes of character strings */

    j =	s->dsc$w_length; /* get to the end of destination string */

    while (i < t->dsc$w_length) { /* concat until length */
	s->dsc$a_pointer[j++] = t->dsc$a_pointer[i++];
    } /* end while */

    s->dsc$w_length = j;  /* set the lentgh */

    return SS$_NORMAL; 

} /* end of desc_cat */

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

static int desc_comp(struct dsc$descriptor *s, 
	      struct dsc$descriptor *t)
/*
Compares two strings desc: char string s and char string t. 
*/
{
    int i = 0;	/* indexes of character strings */

  if (s->dsc$w_length != t->dsc$w_length) /* Quick test to see if they are */
	return FALSE;			  /* possibly equal */

  /* We know the strings are the same length from previous test  */ 
  while (s->dsc$w_length > i) {
 	if (s->dsc$a_pointer[i] != t->dsc$a_pointer[i]) 
		return FALSE;
	i++;
  } /* end while */

  return TRUE;

} /* end of desc_cat */

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

static int DoShowBus(void)
/*	  
**  Invoke the SMI object to do a SHOW BUS.
*/	  
{                         
    char			ShowBuf[MAXSHOWLEN];
    struct dsc$descriptor_s	buffer = STAT_DESC(ShowBuf);
    int				status;

    buffer.dsc$w_length = MAXSHOWLEN;
    status = smi$iogen(&smictx,
		       SMI$K_FUNC_SHOW,
		       SMIIOGEN$K_SHOW_BUS,	/* show what	*/
		       0,			/* no string1	*/
		       0,			/* no string2	*/
		       &buffer);		/* data buffer	*/

    if (!(status & SS$_NORMAL)) {
	print_err(status, 0);
	return status;
    }

    ShowNode();
    print_line(0, 1, &buffer);	/* SHOW returned a header; display it */

    /*	  
    **  Now do repetitive SHOW_NEXTs to get and display the remaining data
    */	  
    
    do {
    	buffer.dsc$w_length = MAXSHOWLEN;
	status = smi$iogen(&smictx,
			   SMI$K_FUNC_SHOW_NEXT,
			   SMIIOGEN$K_SHOW_BUS,	/* show what	*/
			   0,			/* no string1	*/
			   0,			/* no string2	*/
			   &buffer);		/* data buffer	*/

	if ((status & SS$_NORMAL) && (status != SMI$_NOMORE))
	    print_line(0, 1, &buffer);
    } while ((status & SS$_NORMAL) && (status != SMI$_NOMORE));

    if (status != SMI$_NOMORE) {
	print_err(status, 0);
	return status;
    }        

    return SS$_NORMAL;
    
}   /* end of DoShowBus */

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

int sysman$io_show_bus()
/*	  
**  Set things up for the real workhorse to do a SHOW BUS
*/	  
{
    action_addr = DoShowBus;
    
    return SS$_NORMAL;

}   /* end of sysman$io_show_bus */

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

static int DoShowDev()
/*	  
**  Invoke the SMI object to perform a SHOW DEVICE
*/	  
{
    char		     	ShowBuf[MAXSHOWLEN];
    struct dsc$descriptor_s	buffer = STAT_DESC(ShowBuf);
    int				status;

    buffer.dsc$w_length = MAXSHOWLEN;
    status = smi$iogen(&smictx,
		       SMI$K_FUNC_SHOW,
		       SMIIOGEN$K_SHOW_DEVICE,	/* show what?	*/
		       0,			/* no string1	*/
		       0,  			/* no string2	*/
		       &buffer);		/* data buffer	*/

    if (!(status & SS$_NORMAL)) {
	print_err(status, 0);
	return status;
    }
       
    ShowNode();
    print_line(0, 1, &buffer);	/* SHOW returned a header; display it */

    /*	  
    **  Now do repetitive SHOW_NEXTs to get and display the remaining data
    */	  
    
    do {
    	buffer.dsc$w_length = MAXSHOWLEN;
	status = smi$iogen(&smictx,
			   SMI$K_FUNC_SHOW_NEXT,
			   SMIIOGEN$K_SHOW_DEVICE,  /* show what?   */
			   0,			    /* no string1   */
			   0,			    /* no string2   */
			   &buffer);		    /* data buffer  */

	if ((status & SS$_NORMAL) && (status != SMI$_NOMORE))
	    print_line(0, 1, &buffer);       
    } while ((status & SS$_NORMAL) && (status != SMI$_NOMORE));

    if (status != SMI$_NOMORE) {
	print_err(status, 0);
	return status;
    }

    return SS$_NORMAL;

}   /* end of DoShowDevice */

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

int sysman$io_show_device()
/*	  
**  Set up for the real action routine to do a SHOW DEVICE
*/	  
{                                
    action_addr = DoShowDev;
    
    return SS$_NORMAL;
    
}   /* end of sysman$io_show_device */
