//
//  Module:     librtl.c
//
//  Purpose:    This module contains the code for runtime libraries.
//				
//
//  History:    07/14/99    Initially created.
//


#include "vax.pch"
#include "vaxinstr.h"
#include "asmproto.h"       // for get_symbol()
#include "memmap.h"  		// to instantiate memory map list headers (uses GLOBAL)
#include "shim.h"

#include <stdio.h>
#include <errno.h>
#include <fcntl.h>

#if !defined(WIN)
#include <unistd.h>			// This doesn't appear to exist on Windows...
#else
#include <io.h>				// But this does...
#define open _open
#define write _write
#define close _close
#define read _read
#endif

#if defined( VMS )
#include <unixio.h>			//	VMS puts these separate from unistd.h
#endif

#include "fpu.h"

static int instring( char ch, char * str );
static int decc_apply_format( char * buff, char * fmt, LONGWORD * argv, long * pos );


int instring( char ch, char * str ) 
{
	int len, n;
	
	len = strlen( str );
	for( n = 0; n < len; n++ ) {
		if( ch == str[ n ] )
			return 1;
	}
	return 0;
	
}


int decc_apply_format( char * buff, char * fmt, LONGWORD * argv, long * pos )
{

	int w, d, dot, size, code, align, z;
	int len, n, v;
	LONGWORD data;
	char ch, str[ 128 ];
	double dbl;
	long rc;
	
	w = d = v = dot = 0;
	z = -1;
	size = 2;
	v = 0;
	align = 0;
	len = strlen( fmt );
	code = 0;
	for( n = 0; n < len; n++ ) {
	
		ch = fmt[ n ];
		if( ch == '%' )
			continue;
			
		if( ch == '0' && z == -1 ) {
			z = 1;
			continue;
		}
		
		if( ch >= '0' && ch <= '9' && z == -1 )
			z = 0;
			
		if( ch >= '0' && ch <= '9' ) {
			v = v * 10 + ( ch - '0' );
			continue;
		}
		
		if( ch == '.' ) {
			w = v;
			dot = 1;
		}
		
		if( ch == 'l' ) {
			size = 4;
			continue;
		}
		
		if( ch == '-' ) {
			align = 1;
			continue;
		}
		
		if( ch == 'c' )
			size = 1;
		
		if( instring( ch, "dxXfsc" )) {
			code = ch;
			break;
		}
	}
	
	if( v & dot )
		d = v;
	else
	if( v )
		w = v;
	
	if( z == -1 )
		z = 0;
	
/*
 *	Based on the data type, fetch the value from memory.
 */

	if( code == 'd' || code == 'X' || code == 'x' || code == 'c' ) {
		data = argv[ *pos ];
		*pos = *pos + 1;
		
		return sprintf( buff, fmt, data );
	}

/*
 *	If it's a floating value, more work.  Doubles take two
 *	argument slots...
 */
 
 	if( code == 'f' ) {

 		fpu_load( argv[ *pos ], argv[ (*pos) + 1 ], &dbl );
 		*pos = *pos + 2;
 		return sprintf( buff, fmt, dbl );
 	}

/*
 *	If it's a string, load the string from the address.
 */
 
 	if( code == 's' ) {
	 	for( n = 0; n < 128; n++ ) {
	 		rc = load_byte( argv[ *pos ] + n, ( void * ) &ch);
	 		if( rc )
	 			return rc;
	 		str[n] = ch;
	 		if( ch == 0 )
	 			break;
	 	}
	 	*pos = *pos + 1;
	 	return sprintf( buff, fmt, str );
 	}
 		
	printf( "%%CRTL-E-FORMAT, unrecognized format \"%s\",", fmt );
	printf( " w=%d d=%d align=%d size=%d z=%d code=\"%c\"\n",
				w, d, align, size, z, code );
				
	printf( "-CRTL-E-FMTARG, argument %08lX  %ld (dec)\n", 
			argv[ *pos ], argv[ *pos ] );
	return 0;
}


void set_errno( int Code )
{
	long rc;
	char sname[ 32 ], *bp;
	LONGWORD code;
	static LONGWORD errno_addr = 0L;

	code = Code;
	if( errno_addr == 0L ) {

		strcpy( sname, "VAXC$ERRNO" );
		bp = sname;
		
		rc = get_symbol( &bp, &errno_addr );
		if( rc )
			return;
		if( errno_addr == 0L )
			return;
	}
	
	rc = store_memory( errno_addr, ( unsigned char * ) &code, 4 );
	return;
	
}



/*----------------------------------------------------------------------*
 *                                                                      *
 *    fid = exe$open( char * name, int mode );                          *
 *                                                                      *
 *----------------------------------------------------------------------*/

LONGWORD exe_open( LONGWORD argc, LONGWORD * argv )
{

	LONGWORD nameaddr;
	unsigned char name[ 256 ];
	long mode, n, rc;
	LONGWORD fid, mask;

/*
 *	Must be exactly two arguments
 */
 
 	if( argc < 2 || argc >> 3 )
 		return 0;
 		
/*
 *	Get the name from memory
 */
 
 	nameaddr = argv[ 0 ];
 	for( n = 0; n < 255; n++ ) {
 		rc = load_byte( nameaddr + n, &( name[ n ]));
 		if( rc )
 			return 0;
 		if( name[ n ] == 0 )
 			break;
 	}
 	
/*
 *	Get the mode.
 */
  
  	mode = argv[ 1 ];
 

/*
 * 	Open the file and return the id.
 */
 
 	if( argc == 3 ) {
 		mask = argv[ 2 ];
	 	fid = open( ( char * ) name, mode, mask );
	}
	else
		 fid = open( ( char * ) name, mode );

	set_errno( errno );
	return fid;
}




/*----------------------------------------------------------------------*
 *                                                                      *
 *    fid = exe$close( int fid );                                       *
 *                                                                      *
 *----------------------------------------------------------------------*/

LONGWORD exe_close( LONGWORD argc, LONGWORD * argv )
{

	LONGWORD fid;

/*
 *	Must be exactly one argument
 */
 
 	if( argc != 1 )
 		return 0;
 		
	fid = argv[ 0 ];
	close( fid );
	
	set_errno( errno );
	return fid;
}




/*----------------------------------------------------------------------*
 *                                                                      *
 *    fid = exe$read( char * name, int mode );                          *
 *                                                                      *
 *----------------------------------------------------------------------*/

LONGWORD exe_read( LONGWORD argc, LONGWORD * argv )
{

	LONGWORD addr, len;
	long n, count, rc;
	LONGWORD fid;
	char * io_buff, * bp;
	

/*
 *	Must be exactly three arguments
 */
 
 	if( argc != 3 )
 		return 0;
 		
/*
 *	Get the arguments.
 */
 
 	fid = argv[ 0 ];		/* file id */
 	addr = argv[ 1 ];		/* memory buffer to read into */
 	len = argv[ 2 ];		/* size of buffer */

/*
 *	Try to allocate a buffer.
 */
 
 	io_buff = getmem( len );
 	if( io_buff == 0L )
 		return 0;

/*
 *	Do the read to the local buffer.
 */

    if( fid == 0 ) {
        bp = fgets( io_buff, len, stdin );
        count = strlen( bp );
        if( count > 0 && bp[ count-1 ] == '\r' )
            count--;
    }
    else
		count = read( fid, io_buff, len );
	
	for( n = 0; n < count; n++ ) {
	
		rc = store_memory( addr + n, ( unsigned char * ) &( io_buff[ n ]), 1 );
		if( rc )
			return 0;
			
	}
	
	set_errno( errno );
	return count;
}


/*----------------------------------------------------------------------*
 *                                                                      *
 *    fid = exe$write( char * name, int mode );                          *
 *                                                                      *
 *----------------------------------------------------------------------*/

LONGWORD exe_write( LONGWORD argc, LONGWORD * argv )
{

	LONGWORD addr, len;
	long n, count, rc;
	LONGWORD fid;
	char * io_buff;
	

/*
 *	Must be exactly three arguments
 */
 
 	if( argc != 3 )
 		return 0;
 		
/*
 *	Get the arguments.
 */
 
 	fid = argv[ 0 ];		/* file id */
 	addr = argv[ 1 ];		/* memory buffer to write from */
 	len = argv[ 2 ];		/* size of buffer */

/*
 *	Try to allocate a buffer.
 */
 
 	io_buff = getmem( len );
 	if( io_buff == 0L )
 		return 0;

/*
 *	copy the data to the local buffer.
 */
 
 	for( n = 0; n < len; n++ ) {
 		rc = load_byte( addr + n, ( unsigned char * ) &( io_buff[ n ]));
 		if( rc )
 			return 0;
 	}
 	
/*
 *	Do the write from the local buffer.  Note that if it's file '1' then
 *	we cheat for formatting purposes to the console.
 */

	if( fid == 1 ) {
		io_buff[ len ] = 0;
		printf( "%s", io_buff );
		count = len;
	}
	else
		count = write( fid, io_buff, len );
		
	set_errno( errno );
	return count;
}




/*----------------------------------------------------------------------*
 *                                                                      *
 *    fid = exe$input( char * buff, long len );                         *
 *                                                                      *
 *----------------------------------------------------------------------*/

LONGWORD exe_input( LONGWORD argc, LONGWORD * argv )
{
	LONGWORD buff_addr;
	LONGWORD buff_len;
	LONGWORD i, n, rc;
	char * bp;
	
/*	First parameter is address of buffer; second is size of buffer */

	
	buff_addr = argv[ 0 ];
	buff_len =  argv[ 1 ];

/*	Allocate a buffer big enough to match it */

	bp = malloc( buff_len );
	if( bp == 0 )
		return 0;

/*	Read into the buffer */

	fgets( bp, buff_len, stdin );

/*	Move the data to the caller's buffer */

	n = strlen( bp );
	if( bp[ n-1 ] == '\r' || bp[ n-1 ] == '\n')
		n--;
		
	for( i = 0; i < n; i++ ) 
		rc = store_memory( buff_addr + i, ( void * ) &( bp[ i ]), 1 );
		if( rc ) {
			free( bp );
			return 0;
		}

	free( bp );
	
	return n;
}



/*----------------------------------------------------------------------*
 *                                                                      *
 *    len = printf( char * fmtstr,... );                                *
 *                                                                      *
 *----------------------------------------------------------------------*/

LONGWORD decc_printf( LONGWORD argc, LONGWORD * argv )
{
	char buff[ 1024 ];
	long rc;
	
	rc = decc_format( buff, argc, argv, 0 );
	
	return printf( "%s", buff );

}

/*----------------------------------------------------------------------*
 *                                                                      *
 *    len = sprintf( char* buff, char * fmtstr,... );                   *
 *                                                                      *
 *----------------------------------------------------------------------*/

LONGWORD decc_sprintf( LONGWORD argc, LONGWORD * argv )
{
	char buff[ 1024 ];
	LONGWORD bp;
	long rc;
	int len, n;
	
	bp = argv[ 0 ];
	rc = decc_format( buff, argc, argv, 1 );
	len = strlen( buff );
	for( n = 0; n <= len; n++ ) {
		rc = store_memory( bp + n, ( void * ) buff[ n ], 1 );
		if( rc )
			break;
	}
	

	return rc;
}


LONGWORD decc_format( char * buff, LONGWORD argc, LONGWORD * argv, LONGWORD Pos )
{
	
	LONGWORD fmtaddr, fmtp, pos, rc;
	char * p = buff;
	int parsing = 1;
	int count = 0;
	char ch;
	char dbuff[ 128 ];
	char fmt[ 32 ];
	
	pos = Pos;
	fmtaddr = argv[ pos++];
	rc = VAX_OK;
	
	while( parsing ) {
	
		rc = load_byte( fmtaddr++, ( void * ) &ch );
		if( rc )
			break;
		
		switch( ch ) {
		
		case 0:
			parsing = 0;
			break;
		
		case '\\':
			rc = load_byte( fmtaddr++, ( void * ) &ch );
			if( rc )
				break;
			switch( ch ) {

			case 'n':
				buff[ count++ ] = '\n';
				break;
			
			case 'r':
				buff[ count++ ] = '\r';
				break;
			case 't':
				buff[ count++ ] = '\t';
				break;
			case '0':
				buff[ count++ ] = 0;
				break;
			
			default:
				buff[ count++ ] = ch;
				break;				
			}
			
			break;
		
		case '%':
			
			fmtp = 1;
			fmt[ 0 ] = '%';
			while( 1 ) {
			
				rc = load_byte( fmtaddr++, (void*)&ch );
				if( rc )
					return rc;
					
				fmt[ fmtp++ ] = ch;
				
				if( ch == 0 )
					break;
				if( instring( ch, "dxXfsc" )) {
					fmt[ fmtp ] = 0;
					break;
				}
			}
			
			decc_apply_format( dbuff, fmt, argv, &pos );
			buff[ count ] = 0;
			strcat( buff, dbuff );
			count = strlen( buff );
			
			break;
						
		default:
			buff[ count++ ] = ch;
			break;
		}
	}
	
	buff[ count ] = 0;
	return rc;
}

/*----------------------------------------------------------------------*
 *                                                                      *
 *    fid = lib$adawi( long sum, long base, long sign );                *
 *                                                                      *
 *----------------------------------------------------------------------*/

LONGWORD lib_adawi( LONGWORD argc, LONGWORD * argv )
{

	LONGWORD sum_addr, base_addr, sign_addr;
	LONGWORD sum, base, sign, rc;
	
	sum_addr = argv[ 0 ];
	base_addr = argv[ 1 ];
	sign_addr = argv[ 2 ];
	
	rc = load_memory( sum_addr, ( void * ) &sum, 4 );
	if( rc )
		return rc;

	rc = load_memory( base_addr, ( void * ) &base, 4 );
	if( rc )
		return rc;

	base = base + sum;
	if( base < 0 )
		sign = -1;
	else
	if( base == 0 )
		sign = 0;
	else
		sign = 1;
	
	rc = store_memory( sign_addr, ( void * ) &sign, 4 );
	if( rc )
		return rc;

	vax-> R0 = VAX_OK;
	return 1;

}



/*----------------------------------------------------------------------*
 *                                                                      *
 *    fid = str$upcase( struct dsc$descriptor * str );                  *
 *                                                                      *
 *----------------------------------------------------------------------*/

LONGWORD str_upcase( LONGWORD argc, LONGWORD * argv )
{

	LONGWORD addr;
	
	addr = argv[ 0 ];
	
	
	return 1;
}
