// Class implementation for scritplink object.
//
// 5-JUN-2001		Fix bug in operator<<(const char *text) function, don't
//			reset buf_used.

#include <string.h>
#include <stdlib.h>
#include <iostream.h>
#include <descrip.h>
#include <iodef.h>

#include "scriptlink.hxx"
#define LINK_LOGICAL "NET_LINK:"
const static int IO_SIZE=1000;

scriptlink ScriptLink;

$DESCRIPTOR(sysnet,"NET_LINK:");
extern "C" int SYS$OPEN(...), SYS$CONNECT(...), SYS$CLOSE(...);
extern "C" int SYS$GET(...), SYS$PUT(...);
//////////////////////////////////////////////////////////////////////////////
// Constructor, assign channel to network link.
scriptlink::scriptlink()
{
    this->rundown_tag = (char *) 0;
    this->bin_mode = 0;
    this->buf_used = 0;

    // Accept logical link
    this->fab = cc$rms_fab;			// Get default values.
    this->fab.fab$l_fna = LINK_LOGICAL;
    this->fab.fab$b_fns = strlen(this->fab.fab$l_fna);
    this->fab.fab$b_fac = FAB$M_GET|FAB$M_PUT;	// read/write operation
    this->fab.fab$b_rfm = FAB$C_VAR;		// Variable length records.
    this->status = SYS$OPEN ( &this->fab );

    // Create record stream
    if ( (this->status&1) == 0 ) return;
    this->rab = cc$rms_rab;
    this->rab.rab$l_fab = &this->fab;
    this->rab.rab$b_rac = RAB$C_SEQ;		// Sequential record access
    this->status = SYS$CONNECT ( &this->rab );
}
//////////////////////////////////////////////////////////////////////////////
// Destructor
scriptlink::~scriptlink()
{
// write out rundown tag and close link
    cout << "scriptlink destructor called\n";
    if ( this->buf_used > 0 ) {
	status = this->write ( this->buffer, this->buf_used );
	this->buf_used = 0;
    } else status = 1;

    if ( this->rundown_tag ) {
	this->write ( this->rundown_tag, strlen(this->rundown_tag) );
	delete this->rundown_tag;
    }
    this->status = SYS$CLOSE ( &this->fab );
}
//////////////////////////////////////////////////////////////////////////////
// Copy rundown tag.
int scriptlink::set_rundown ( char *tag )
{
    if ( this->rundown_tag ) delete this->rundown_tag;
    this->rundown_tag = new char[strlen(tag)+1];
    strcpy ( this->rundown_tag, tag );
    return 0;
}

int scriptlink::read(void *buffer, int bufsize, int &read)
{
    int status;
    if ( this->buf_used > 0 ) status = this->flush();
    this->rab.rab$l_ubf = (char *) buffer;
    this->rab.rab$w_usz = bufsize;
    status = SYS$GET ( &this->rab );
    read = this->rab.rab$w_rsz;
    return status;
}
int scriptlink::write(const void *outbuf, int bufsize, int frag )
{
    int status;
    char *buffer = (char *) outbuf;

    if ( frag <= 0 ) frag = IO_SIZE;
    do {
	// Write no more than frag bytes at a time.
	if ( bufsize > frag ) this->rab.rab$w_rsz = IO_SIZE;
        else this->rab.rab$w_rsz = bufsize;
        this->rab.rab$l_rbf = buffer;

        status = SYS$PUT ( &this->rab );
	if ( (status&1) == 0 ) break;

        buffer = &buffer[this->rab.rab$w_rsz];
	bufsize = bufsize - this->rab.rab$w_rsz;
    } while ( bufsize > 0 );
    return status;
}
int scriptlink::query(const char *tag, void *buffer, int bufsize, int &transferred)
{
    int status;
    status = this->write ( tag, strlen ( tag ) );
    if ( (status&1) == 1 ) status = this->read ( buffer, bufsize, transferred );
    return status;
}
////////////////////////////////////////////////////////////////////////////
// Flush pending data in buffer.
//
int scriptlink::flush()
{
    int status;
    if ( this->buf_used > 0 ) {
	status = this->write ( this->buffer, this->buf_used );
	this->buf_used = 0;
    } else status = 1;
    this->buf_used = 0;
    return status;
}

////////////////////////////////////////////////////////////////////////////
int scriptlink::set_mode ( int mode )
{
    int prev_mode;
    prev_mode = this->bin_mode;
    this->bin_mode = mode;
    return prev_mode;
}


scriptlink &scriptlink::operator<<(const int value)
{
    char numstr[24];
    int i, radix, t;
    radix = (value < 0) ? -10 : 10;

    i = 24;
    numstr[--i] = '\0';
    for ( t = value; t != 0; ) {
	int d;
	d = t%radix;
	numstr[--i] = '0' + d;
	t = t/radix;
    }
    if ( i == (sizeof(numstr)-1) ) numstr[--i] = '0';
    else if ( radix < 0 ) numstr[--i] = '-';
    
    *this << &numstr[i];
    return *this;
}

scriptlink &scriptlink::operator<<(const char *text)
{
    int j, i, status;
    j = this->buf_used;
    for ( i = 0; text[i]; i++ ) {
	if ( j >= sizeof(this->buffer) ) {
	     status = this->flush();
	     j = 0;
	     if ( (status&1) == 0 ) break;
	     j = this->buf_used;
	}
	this->buffer[j++] = text[i];
    }
    this->buf_used = j;
    return *this;
}
/**************************************************************************/
/* Convert escaped characters in string to actual values.
 * (originally C code).
 *
 * 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;
}
