[0001]
[0002]
[0003]
[0004]
[0005]
[0006]
[0007]
[0008]
[0009]
[0010]
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024]
[0025]
[0026]
[0027]
[0028]
[0029]
[0030]
[0031]
[0032]
[0033]
[0034]
[0035]
[0036]
[0037]
[0038]
[0039]
[0040]
[0041]
[0042]
[0043]
[0044]
[0045]
[0046]
[0047]
[0048]
[0049]
[0050]
[0051]
[0052]
[0053]
[0054]
[0055]
[0056]
[0057]
[0058]
[0059]
[0060]
[0061]
[0062]
[0063]
[0064]
[0065]
[0066]
[0067]
[0068]
[0069]
[0070]
[0071]
[0072]
[0073]
[0074]
[0075]
[0076]
[0077]
[0078]
[0079]
[0080]
[0081]
[0082]
[0083]
[0084]
[0085]
[0086]
[0087]
[0088]
[0089]
[0090]
[0091]
[0092]
[0093]
[0094]
[0095]
[0096]
[0097]
[0098]
[0099]
[0100]
[0101]
[0102]
[0103]
[0104]
[0105]
[0106]
[0107]
[0108]
[0109]
[0110]
[0111]
[0112]
[0113]
[0114]
[0115]
[0116]
[0117]
[0118]
[0119]
[0120]
[0121]
[0122]
[0123]
[0124]
[0125]
[0126]
[0127]
[0128]
[0129]
[0130]
[0131]
[0132]
[0133]
[0134]
[0135]
[0136]
[0137]
[0138]
[0139]
[0140]
[0141]
[0142]
[0143]
[0144]
[0145]
[0146]
[0147]
[0148]
[0149]
[0150]
[0151]
[0152]
[0153]
[0154]
[0155]
[0156]
[0157]
[0158]
[0159]
[0160]
[0161]
[0162]
[0163]
[0164]
[0165]
[0166]
[0167]
[0168]
[0169]
[0170]
[0171]
[0172]
[0173]
[0174]
[0175]
[0176]
[0177]
[0178]
[0179]
[0180]
[0181]
[0182]
[0183]
[0184]
[0185]
[0186]
[0187]
[0188]
[0189]
[0190]
[0191]
[0192]
[0193]
[0194]
[0195]
[0196]
[0197]
[0198]
[0199]
[0200]
[0201]
[0202]
[0203]
[0204]
[0205]
[0206]
[0207]
[0208]
[0209]
[0210]
[0211]
[0212]
[0213]
[0214]
[0215]
[0216]
[0217]
[0218]
[0219]
[0220]
[0221]
[0222]
[0223]
[0224]
[0225]
[0226]
[0227]
[0228]
[0229]
[0230]
[0231]
[0232]
[0233]
[0234]
[0235]
[0236]
[0237]
[0238]
[0239]
[0240]
[0241]
[0242]
[0243]
[0244]
[0245]
[0246]
[0247]
[0248]
[0249]
[0250]
[0251]
[0252]
[0253]
[0254]
[0255]
[0256]
[0257]
[0258]
[0259]
[0260]
[0261]
[0262]
[0263]
[0264]
[0265]
[0266]
[0267]
[0268]
[0269]
[0270]
[0271]
[0272]
[0273]
[0274]
[0275]
[0276]
[0277]
[0278]
[0279]
[0280]
[0281]
[0282]
[0283]
[0284]
[0285]
[0286]
[0287]
[0288]
[0289]
[0290]
[0291]
[0292]
[0293]
[0294]
[0295]
[0296]
[0297]
[0298]
[0299]
[0300]
[0301]
[0302]
[0303]
[0304]
[0305]
[0306]
[0307]
[0308]
[0309]
[0310]
[0311]
[0312]
[0313]
[0314]
[0315]
[0316]
[0317]
[0318]
[0319]
[0320]
[0321]
[0322]
[0323]
[0324]
[0325]
[0326]
[0327]
[0328]
[0329]
[0330]
[0331]
[0332]
[0333]
[0334]
[0335]
[0336]
[0337]
[0338]
[0339]
[0340]
[0341]
[0342]
[0343]
[0344]
[0345]
[0346]
[0347]
[0348]
[0349]
[0350]
[0351]
[0352]
[0353]
[0354]
[0355]
[0356]
[0357]
[0358]
[0359]
[0360]
[0361]
[0362]
[0363]
[0364]
[0365]
[0366]
[0367]
[0368]
[0369]
[0370]
[0371]
[0372]
[0373]
[0374]
[0375]
[0376]
[0377]
[0378]
[0379]
[0380]
[0381]
[0382]
[0383]
[0384]
[0385]
[0386]
[0387]
[0388]
[0389]
[0390]
[0391]
[0392]
[0393]
[0394]
[0395]
[0396]
[0397]
[0398]
[0399]
[0400]
[0401]
[0402]
[0403]
[0404]
[0405]
[0406]
[0407]
[0408]
[0409]
[0410]
[0411]
[0412]
[0413]
[0414]
[0415]
[0416]
[0417]
[0418]
[0419]
[0420]
[0421]
[0422]
[0423]
[0424]
[0425]
[0426]
[0427]
[0428]
[0429]
[0430]
/*
 * Utility routines for script programs.  These routines rationally handle
 * record mode access to the network logical link (zero length records are
 * valid, unlike in the C RTL).
 *
 *    int net_link_open ( )
 *    int net_link_close()
 *    int net_link_read ( char *buffer, int bufsize, int *read )
 *    int net_link_write ( char *buffer, int bufsize )
 *    int net_link_fetch ( char *tag, char *buffer, int bufsize, int *read )
 *    int net_link_set_rundown ( char *tag );
 *    int net_link_set_mode ( int mode );
 *    char * net_unescape_string ( char *arg, int *length )
 *    int net_link_printf ( char *ctl, ... )
 *    int net_link_saved_output ( char **buffer, int *length );
 *
 * Date: 8-APR-1994
 * Revised: 29-APR-1994		Added net_unquote_arg() and net_link_printf()
 * Revised: 1-MAY-1994
 * Revised: 7-OCT-1994		Bug in unescape.
 * Revised: 26-DEC-1996		Add saved output mode (bit 1).
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rms.h>		/* Symbol and structure definitions for RMS */
#include <stdarg>

#include "scriptlib.h"		/* Validate prototypes against actual */

#define LINK_LOGICAL "NET_LINK:"
#define IO_SIZE 1000
/*
 * Define module-side varaibles.
 */
static int net_link;			/* I/O channel to DECnet link */
static int bin_mode = 0;		/* If non-zero, do binary printfs */
static int save_puts = 0;		/* If non-zero, save output */
static int saved_put_alloc = 0;		/* Size of put data buffer */
static int saved_put_length = 0;
static char *saved_put_data;		/* Buffer to hold saved data */
static struct FAB net_fab;		/* RMS file access block */
static struct RAB net_rab;		/* RMS record access block */
static int printf_used;			/* printf buffer used */
static struct {
    void *fwd_link;			/* Next block in chain */
    int (*handler)();			/* exit handler */
    int arg_count;
    int *condition;			/* Exit reason */
    int reason;
    int link_open;				/* flag */
    char rundown_tag[512];		/* Exit string */
} exit_block;
static char printf_buffer[8192];
/**************************************************************************/
/* Call close function if link still open, this will flush the printf buffer.
 */
static int exit_handler ( int *reason )
{
    if ( !exit_block.link_open ) return 1;
    net_link_close();
    return 1;
}
/**************************************************************************/
/* Initialize network connection.  Make RMS open for PPF assigned to
 * net link logical name.  Return value is RMS compleition status.
 */
int net_link_open ( )
{
    int status, SYS$OPEN(), SYS$CONNECT(), SYS$DCLEXH();
    /*
     * Open network link as file.
     */
    net_fab = cc$rms_fab;	/* Set default values */
    net_fab.fab$l_fna = LINK_LOGICAL;
    net_fab.fab$b_fns = strlen(net_fab.fab$l_fna);
    net_fab.fab$b_fac = FAB$M_GET + FAB$M_PUT;
    net_fab.fab$b_rfm = FAB$C_VAR;
    printf_used = 0;

    status = SYS$OPEN ( &net_fab );
    if ( (status&1) == 0 ) return status;
    /*
     * Connect record stream to open file.
     */
    net_rab = cc$rms_rab;
    net_rab.rab$l_fab = &net_fab;
    net_rab.rab$b_rac = RAB$C_SEQ;
    status = SYS$CONNECT ( &net_rab );
    if ( (status&1) == 0 ) return status;
    /*
     * Establish exit handler to cleanly close link.
     */
    exit_block.handler = &exit_handler;
    exit_block.arg_count = 1;
    exit_block.condition = &exit_block.reason;
    exit_block.link_open = 1;
    exit_block.rundown_tag[0] = '\0';
    status = SYS$DCLEXH ( &exit_block );

    return status;
}
/**************************************************************************/
/*  Set string that close will automatically send at link close.
 *  If string is zero-length, no final message will be sent.
 */
int net_link_set_rundown ( char *tag )
{
    strncpy ( exit_block.rundown_tag, tag, sizeof(exit_block.rundown_tag)-1 );
    exit_block.rundown_tag[sizeof(exit_block.rundown_tag)-1] = '\0';
    return 1;
}
/**************************************************************************/
/*  Flush printf_buffer to network connection.
 */
static int net_link_flush ( )
{
    int status, SYS$PUT();

    if ( printf_used <= 0 ) return 1;
    if ( save_puts == 0 ) {
        net_rab.rab$w_rsz = printf_used;
        net_rab.rab$l_rbf = printf_buffer;
        printf_used = 0;
        status = SYS$PUT ( &net_rab );
    } else {
	if ( (printf_used + saved_put_length) > saved_put_alloc ) {
	    while ( (printf_used + saved_put_length) > saved_put_alloc ) {
	        saved_put_alloc += 20000;
	    }
	    saved_put_data = (char *) realloc(saved_put_data, saved_put_alloc);
	}
	memcpy (&saved_put_data[saved_put_length], printf_buffer, printf_used);
	saved_put_length += printf_used;
	printf_used = 0;
	status = 1;
    }
    return status;
}
/**************************************************************************/
/*  Set value for mode flag, bit mask:
 *     <0>	0 - text mode (default), 1 - binary mode.
 *     <1>	0 - pass puts to net_link, 1 - save puts to buffer.
 *
 *  In binary mode, printf does not expand linefeed to cr/lf pairs.
 *
 *  Return value is previous mode.
 */
int net_link_set_mode ( int new_mode )
{
    int prev_mode;
    net_link_flush();

    prev_mode = bin_mode | (save_puts<<1);
    bin_mode = (new_mode&1);
    if ( new_mode&2 ) {
	save_puts = 1;
	if ( saved_put_alloc == 0 ) {	
	    /* do initial allocation of put buffer */
	    saved_put_alloc += 20000;
	    saved_put_data = (char *) malloc ( saved_put_alloc );
	}
    } else {
	save_puts = 0;
    }
    return prev_mode;
}
/**************************************************************************/
/* Close link openned by net_link_open.
 */
int net_link_close()
{
    int status, SYS$CLOSE ( );
    /*
     * Flush pending printfs and rundown tag if present.
     */
#ifdef DEBUG
printf("closing connection, printf buffer used: %d, rundown tag: '%s'\n", 
printf_used, exit_block.rundown_tag );
#endif
    if ( printf_used > 0 ) status = net_link_flush();
    if ( *exit_block.rundown_tag ) status = net_link_write
	(exit_block.rundown_tag, strlen(exit_block.rundown_tag) );
    exit_block.rundown_tag[0] = '\0';

    net_fab.fab$l_fop = 0;
    status = SYS$CLOSE ( &net_fab );
    exit_block.link_open = 0;
    return status;
}
/**************************************************************************/
/*  Read record from network connection.  Return value is VMS condition code.
 */
int net_link_read ( void *buffer, int bufsize, int *nread )
{
    int status, SYS$GET();

    if ( printf_used > 0 ) status = net_link_flush();
    net_rab.rab$l_ubf = buffer;
    net_rab.rab$w_usz = bufsize;
    status = SYS$GET ( &net_rab );
    *nread = net_rab.rab$w_rsz;		/* size of record read */
    return status;
}
/**************************************************************************/
/*  Write user's buffer to network connection.
 */
int net_link_write ( void *buffer, int bufsize )
{
    int status, SYS$PUT();

    if ( printf_used > 0 ) status = net_link_flush();
    if ( save_puts == 0 ) {
	net_rab.rab$w_rsz = bufsize;
	net_rab.rab$l_rbf = buffer;
	status = SYS$PUT ( &net_rab );
    } else {
	/*
	 * Save data in large char array for retrieval by net_link_saved_output.
	 */
	if ( (bufsize + saved_put_length) > saved_put_alloc ) {
	    while ( (bufsize + saved_put_length) > saved_put_alloc ) {
	        saved_put_alloc += 20000;
	    }
	    saved_put_data = (char *) realloc(saved_put_data, saved_put_alloc);
	}
	memcpy ( &saved_put_data[saved_put_length], buffer, bufsize );
	saved_put_length += bufsize;
	status = 1;
    }
    return status;
}
/**************************************************************************/
/*  Write tag as prompt and read response into buffer.
 */
int net_link_query ( void *tag, void *buffer, int bufsize, int *nread )
{
    int status, tag_length;

    status = net_link_write ( tag, strlen ( tag ) );
    if ( (status&1) == 1 ) status = net_link_read ( buffer, bufsize, nread );

    return status;
}
/**************************************************************************/
/* Provide formatted output to net_link, replacing printf calls to stdout.
 * Newline chars in format string are replaced with carriage-return/linefeed
 * pairs.
 */
int net_link_printf ( char *fmt, ... )
{
    va_list alist;
    int state, out_len, status, j;
    char *fc, *conv, *out_buffer;
    char conv_buffer[4096], conv_spec[64], c_arg;
    /*
     * Setup pointer to variable argument list.
     */
    va_start ( alist, fmt );
    /*
     * Transfer characters from control string to output buffer.
     */
    out_len = printf_used;
    out_buffer = printf_buffer;
    for ( state = 0, fc = fmt; *fc; fc++ ) {
	if ( state == 0 ) {
	    /* Looking for next conversion character */
	    if ( *fc == '%' ) {
		/* Begin parse of conversion specification. */
		state = 1;
		conv_spec[0] = '%';
		conv = &conv_spec[1];
		out_buffer[out_len] = '\0';
	    } else {
		/* add CR to precede LF in out_buf */
		if ( *fc == '\n' ) 
		    if ( !bin_mode ) out_buffer[out_len++] = '\r';
		out_buffer[out_len++] = *fc;
	    }
	} else {
	    /*
	     * Append character to conversion string and see if it
	     * is terminal.
	     */
	    *conv++ = *fc;
	    if ( strchr ( "-0123456789.l", *fc ) == NULL ) {
		    long long_arg;
		    int int_arg;
		    char *char_arg;
		    double dbl_arg;
		*conv = '\0';		/* terminate specifier string */
		switch ( *fc ) {
		  case 'd':
		  case 'o':
		  case 'x':
		  case 'D':
		  case 'O':
		  case 'X':
		  case 'u':
		  case 'U':
		    if ( state == 1 ) {
			int_arg = va_arg ( alist, int );
			sprintf ( conv_buffer, conv_spec, int_arg );
		    } else {
			long_arg = va_arg ( alist, int );
			sprintf ( conv_buffer, conv_spec, long_arg );
		    }
		    break;
		  case 's':
		  case 'S':
		    char_arg = va_arg ( alist, char * );
		    sprintf ( conv_buffer, conv_spec, char_arg );
		    break;

		  case 'e':
		  case 'E':
		  case 'f':
		  case 'F':
		  case 'g':
		  case 'G':
		    dbl_arg = va_arg ( alist, double );
		    sprintf ( conv_buffer, conv_spec, dbl_arg );
		    break;

		  case 'c':
		  case 'C':
		    c_arg = va_arg ( alist, char );
		    sprintf ( conv_buffer, conv_spec, c_arg );
		    break;
		    
		  default:
		    /* Copy contents to output string */
		    strcpy ( conv_buffer, &conv_spec[1] );
		}
		/*
		 * Conversion complete, Copy chars to out_buffer, replacing
		 * '\n' with '\r\n';
		 */
		state = 0;
		for ( conv = conv_buffer; *conv; 
			out_buffer[out_len++] = *conv++ ) {
		    if ( *conv == '\n' ) {
			if ( !bin_mode ) out_buffer[out_len++] = '\r';
		    }
		}

	    } else if ( *fc == 'l' ) state = 2;	/* Long */
	}
	/*
	 * Flush buffer if full.
	 */
	while ( out_len >= IO_SIZE ) {
	    printf_used = IO_SIZE;
	    status = net_link_flush();
	    if ( (status&1) == 0 ) return status;
	    /* Transfer characters not moved to beginning of buffer */
	    for ( j = IO_SIZE; j < out_len; j++ ) {
		out_buffer[printf_used++] = out_buffer[j];
	    }
	    out_len = printf_used;
	}
    }
    va_end ( alist );
    /*
     * Flush conversion spec buffer if needed and send final output to
     * server.
     */
    printf_used = out_len;
    if ( state != 0 ) {
	/* Dangling conversion specification */
	status = net_link_flush();
	if ( (status&1) == 0 ) return status;
	*conv = '\0';
	strcpy ( out_buffer, &conv_spec[1] );
	printf_used = strlen ( out_buffer );
    }
    return 1;
}
/**************************************************************************/
/* Convert escaped characters in string to actual values.
 *
 * Arguments:
 * 	string		Character string.  Modified.
 *	length		Int.  On input, original length of string.
 *			On output, final length of unescaped string.
 */
char * net_unescape_string ( char *string, int *length )
{
    int i, j, reslen, modified;
    /*
     * Scan string.
     */
    for ( modified = reslen = i = 0; i < *length; i++ ) {
	if ( string[i] == '%' ) {
	    /*
	     * Escape seen, decode next 2 characters as hex and replace
	     * all three with single byte.
	     */
	    char value[4];
	    int val;
	    value[0] = string[i+1]; value[1] = string[i+2]; value[2] = '\0';
	    i += 2;
	    sscanf ( value, "%2x", &val );
	    if  ( val > 127 ) val |= (-1 ^ 255);	/* Sign extend */
	    string[reslen] = val;
	    modified = 1;
        } 
	else {
	    /* Only copy bytes if escape edit took place. */
	    if ( modified ) string[reslen] = string[i];
	}
        reslen++;
    }
    /* Return value is point to string editted. */
    *length = reslen;
    return string;
}
/*****************************************************************************/
/* Return pointer to saved buffer region and saved length.  Mode is NOT reset,
 * so additional writes may reallocate buffer.
 */
int net_link_saved_output ( char **buffer, int *length )
{
    int status;
    if ( saved_put_alloc <= 0 ) return 0;		/* allocation failure */
    status = net_link_flush();
    *buffer = saved_put_data;
    *length = saved_put_length;
    return status;
}