/* openmath.c                                  Copyright (C) 1998 NAG Ltd. */

#ifdef OPENMATH

/*
 * Reading and writing of OpenMath objects, using the INRIA OpenMath library.
 * Developed as a deliverable of the OpenMath Project (ESPRIT Project 24969).
 *
 * Initial version: Vilya Harvey, Nov 24th, 1998.
 *
 * Note: to add this to CCL, the following files had to be modified:
 * Makefile
 * cslbase\entries.h
 * cslbase\externs.h
 * cslbase\restart.c
 * cclbase\exports.lsp
 *
 * Note: to make sure that the OpenMath support is compiled into CCL, the
 * symbol OPENMATH must be #defined.
 */


/* Signature: 52632b41 08-Apr-2002 */


#include <stdarg.h>
#include <string.h>
#include <ctype.h>

#ifdef __WATCOMC__
#include <float.h>
#endif

#include "machine.h"
#include "tags.h"
#include "cslerror.h"
#include "externs.h"
#include "entries.h"
#include "arith.h"
#include "read.h"

#include <OM.h>
#include <OMconn.h>
#include <math.h>

#include "openmath.h"


#define OMCCL_ELEM_BITS	31					/* #bits in bignum element. */
#define OMCCL_MAX_BITS	15					/* Must be < OMCCL_ELEM_BITS */
#define OMCCL_MAX_VAL	(1 << OMCCL_MAX_BITS)
#define OMCCL_MAX_MASK	(OMCCL_MAX_VAL - 1)

#define OMCCL_INV_LOG_2	1.44269504			/* 1 / ln(2) */


/*
 * Error reporting macro. The status parameter should be of type OMstatus; the
 * return value is a Lisp_Object representing the appropriate error.
 */
#define om_error(status)		error(0, (int)(status) + 33)


/*
 * Local functions for dealing with property lists.
 */

Lisp_Object om_getLispProperty(Lisp_Object obj, Lisp_Object name);
Lisp_Object om_setLispProperty(Lisp_Object obj, Lisp_Object name, Lisp_Object val);


/*
 * External CCL functions.
 */
extern char *get_string_data(Lisp_Object name, char *why, int32 *l);


OMdev
om_toDev(Lisp_Object obj)
/* This routine converts a Lisp_Object to an OMdev. It does
 * not check that the object it is given actually *is* an
 * OMdev - that's the caller's responsibility.
 */
{
	OMdev dev;
	
	/* DEBUG */
	if (!is_bignum(obj)) {
		err_printf("[om_toDev] not a bignum!\n");
	}
	else {
		int blen = (bignum_length(obj) >> 2) - 1;
		if (blen != 1)
			err_printf("[om_toDev] bignum length is %d (should be 1)!\n", blen);
	}
	/* END DEBUG */
	
	if (!is_bignum(obj))
		return NULL;
	else if (((bignum_length(obj) >> 2) - 1) != 1)
		return NULL;
	dev = (OMdev) ( bignum_digits(obj)[0] );
	return dev;
}


Lisp_Object
om_fromDev(OMdev dev)
{
	Lisp_Object obj;
	obj = make_one_word_bignum((int32)dev);
	return obj;
}


OMstatus
om_toStatus(Lisp_Object obj)
{
	OMstatus status;
	if (!is_fixnum(obj))
		return OMinternalError;
	status = (OMstatus)( int_of_fixnum(obj) );
	return status;
}


Lisp_Object
om_fromStatus(OMstatus status)
{
	Lisp_Object obj;
	obj = fixnum_of_int((int32)status);
	return obj;
}


OMencodingType
om_toEncodingType(Lisp_Object obj)
{
	OMencodingType enc;
	if (!is_fixnum(obj))
		return OMencodingUnknown;
	enc = (OMencodingType)( int_of_fixnum(obj) );
	return enc;
}


Lisp_Object
om_fromEncodingType(OMencodingType enc)
{
	Lisp_Object obj;
	obj = fixnum_of_int((int32)enc);
	return obj;
}


char *
om_toBigNumStr(Lisp_Object num)
{
	static char hexdigit[] = "0123456789ABCDEF";
	char *str;
	int numDigits, digit;
	int bdigit, boffset;
	int i, j, val;
	int strPos;
	int leading;

	/* Determine the number of digits needed. */
	i = ((bignum_length(num) >> 2) - 1) * 31;
	numDigits = (i >> 2) + (((i & 0x3) != 0) ? 1 : 0);

	str = (char *)malloc((numDigits + 1) * sizeof(char));
	memset(str, 0, (numDigits + 1) * sizeof(char));

	strPos = 0;
	leading = 1;
	digit = 0;
	while (digit < numDigits) {
		i = (numDigits - digit - 1) << 2;
		bdigit = i / 31;
		boffset = i % 31;
		j = 31 - boffset;
		
		switch (j) {
		case 3:
			val = (bignum_digits(num)[bdigit] >> boffset) & 0x7;
			val |= ((bignum_digits(num)[bdigit+1] & 0x1) << 3);
			break;
		case 2:
			val = (bignum_digits(num)[bdigit] >> boffset) & 0x3;
			val |= ((bignum_digits(num)[bdigit+1] & 0x3) << 2);
			break;
		case 1:
			val = (bignum_digits(num)[bdigit] >> boffset) & 0x1;
			val |= ((bignum_digits(num)[bdigit+1] & 0x7) << 1);
			break;
		default:
			val = (bignum_digits(num)[bdigit] >> boffset) & 0xF;
			break;
		}
		
		str[strPos] = hexdigit[val];
		digit++;
		if (hexdigit[val] != '0' || !leading) {
			leading = 0;
			strPos++;
		}
	}

	return str;
}


Lisp_Object
om_fromBigNumStr(char *inData, int len, int sign, OMbigIntType fmt)
{
	Lisp_Object obj, radix, digit;
	int i;

	if (len == 0)
		return fixnum_of_int(0);
	else
		obj = fixnum_of_int(0);

	switch (fmt) {
	case OMbigIntBase10:
		radix = fixnum_of_int(10);
		break;
	case OMbigIntBase16:
		radix = fixnum_of_int(16);
		break;
	default:
		return om_error(OMinternalError);
	}

	for (i = 0; i < len; i++) {
		obj = times2(obj, radix);

		switch (fmt) {
		case OMbigIntBase10:
			digit = fixnum_of_int( (int)(inData[i] - '0') );
			break;
		case OMbigIntBase16:
			if (inData[i] >= 'a' && inData[i] <= 'f')
				digit = fixnum_of_int( (int)(inData[i] - 'a') + 10 );
			else if (inData[i] >= 'A' && inData[i] <= 'F')
				digit = fixnum_of_int( (int)(inData[i] - 'A') + 10 );
			else
				digit = fixnum_of_int( (int)(inData[i] - '0') );
			break;
		}

		obj = plus2(obj, digit);
	}

	if (sign < 0)
		obj = negateb(obj);

	return obj;
}


OMconn
om_toConn(Lisp_Object obj)
{
	OMconn conn;
	
	/* DEBUG */
	if (!is_bignum(obj)) {
		err_printf("[om_toConn] not a bignum!\n");
	}
	else {
		int blen = (bignum_length(obj) >> 2) - 1;
		if (blen != 1)
			err_printf("[om_toConn] bignum length is %d (should be 1)!\n", blen);
	}
	/* END DEBUG */
	
	if (!is_bignum(obj))
		return NULL;
	else if (((bignum_length(obj) >> 2) - 1) != 1)
		return NULL;
	conn = (OMconn)(bignum_digits(obj)[0]);
	return conn;
}


Lisp_Object
om_fromConn(OMconn conn)
{
	Lisp_Object obj;
	obj = make_one_word_bignum((int32)conn);
	return obj;
}


char **
om_toCString(Lisp_Object obj)
/* Converts a lisp object which wraps a C string into a C string (a char
 * pointer, where the memory block is allocated on the heap, outside of the
 * control of the CCL garbage collection). Does not check that the Lisp object
 * *is* a C string though.
 */
{
	char **pstr = NULL;
	
	/* DEBUG */
	if (!is_bignum(obj) && !stringp(obj)) {
		err_printf("[om_toCString] not a bignum or a string!\n");
	}
	else if (is_bignum(obj)) {
		int blen = (bignum_length(obj) >> 2) - 1;
		if (blen != 1)
			err_printf("[om_toCString] bignum length is %d (should be 1)!\n", blen);
	}
	/* END DEBUG */
	
	if (!is_bignum(obj) && !stringp(obj))
		return NULL;
	else if (is_bignum(obj)) {
		if (((bignum_length(obj) >> 2) - 1) != 1)
			return NULL;
		pstr = (char **)(bignum_digits(obj)[0]);
	}
	else {
		char *tmp = NULL;
		int len = 0;
		tmp = get_string_data(obj, "om_toCString", &len);
		tmp[len] = '\0';
		pstr = (char **)malloc(sizeof(char *));
		*pstr = strdup(tmp);
	}
	return pstr;
}


Lisp_Object
om_fromCString(char **str)
{
	Lisp_Object obj;
	obj = make_one_word_bignum((int32)str);
	return obj;
}


Lisp_Object
om_cStringFromLispString(Lisp_Object lstr)
{
	Lisp_Object cstr;
	cstr = om_fromCString(om_toCString(lstr));
	return cstr;
}


Lisp_Object
om_lispStringFromCString(Lisp_Object cstr)
{
	Lisp_Object lstr;
	char **pstr = om_toCString(cstr);
	lstr = make_string(*pstr);
	return lstr;
}


/*
 * Local functions for dealing with property lists.
 */

Lisp_Object
om_getLispProperty(Lisp_Object obj, Lisp_Object name)
{
	return get(obj, name, C_nil);
}


Lisp_Object
om_setLispProperty(Lisp_Object obj, Lisp_Object name, Lisp_Object val)
{
	return putprop(obj, name, val);
}


/*
 * Exposed OpenMath Device manipulation functions.
 */

Lisp_Object MS_CDECL
om_openFileDev(Lisp_Object nil, int nargs, ...)
/* Opens a file and creates an OpenMath device for it. The return value is the
 * LISP object which wraps the created device. The parameters are:
 *   fname	- string	- the name of the file to open.
 *   fmode	- string	- the mode, as passed to the fopen routine.
 *   fenc	- string	- the OpenMath encoding type of the file.
 */
{
	va_list args;
	Lisp_Object lname, lmode, lenc;
	char *fname, *fmode;
	OMencodingType fenc;
	FILE *f;
	OMdev dev;
	int32 len;
	Lisp_Object lispDev;

	CSL_IGNORE(nil);

	/* Unpack the parameters into Lisp_Objects. */
	argcheck(nargs, 3, "om_openFileDev");
	va_start(args, nargs);
	lname = va_arg(args, Lisp_Object);
	lmode = va_arg(args, Lisp_Object);
	lenc = va_arg(args, Lisp_Object);
	va_end(args);

	push3(lname, lmode, lenc);

	/* Convert the parameters into their C equivalents. */
	if (!is_vector(lname) || !(type_of_header(vechdr(lname)) == TYPE_STRING))
		return aerror("om_openFileDev");
	errexitn(3);
	fname = get_string_data(lname, "om_openFileDev", &len);
	errexitn(3);
	fname[len] = '\0';

	if (!is_vector(lmode) || !(type_of_header(vechdr(lmode)) == TYPE_STRING))
		return aerror("om_openFileDev");
	errexitn(3);
	fmode = get_string_data(lmode, "om_openFileDev", &len);
	errexitn(3);
	fmode[len] = '\0';

	if (!is_fixnum(lenc))
		return aerror("om_openFileDev");
	errexitn(3);
	/* This gets OMencodingTypes as an integer then casts it to OMencodingType.
	 * That may be a bit dodgy... */
	fenc = om_toEncodingType(lenc);

	pop3(lname, lmode, lenc);

	f = fopen(fname, fmode);
	if (f == NULL)
		return aerror("om_openFileDev: couldn't open named file!");

	/* Create an OpenMath device on the given file. */
	dev = OMmakeDevice(fenc, OMmakeIOFile(f));

	/* Wrap the OpenMath device in a LISP object and return it. */
	lispDev = om_fromDev(dev);

	return onevalue(lispDev);
}


Lisp_Object
om_openStringDev(Lisp_Object nil, Lisp_Object lstr, Lisp_Object lenc)
/* Creates an OpenMath string device on an existing string. The return value is
 * the LISP object which wraps the created device. The parameters are:
 *	lstr	- string	- The string to create the device on. This must be a C
						  string pointer wrapped in a Lisp object.
 *	lenc	- int		- The OpenMath encoding type of the string.
 */
{
	/* There may be a problem with the OM library directly accessing the string
	 * data of a Lisp_Object - see if there is a way around that (if it is a
	 * problem).
	 */

	char **pstr = NULL;
	OMencodingType enc;
	OMdev dev;
	Lisp_Object ldev;

	CSL_IGNORE(nil);
	push2(lstr, lenc);

	pstr = om_toCString(lstr);
	errexitn(2);

	enc = om_toEncodingType(lenc);
	errexitn(2);
	
	dev = OMmakeDevice(enc, OMmakeIOString(pstr));
	ldev = om_fromDev(dev);

	pop2(lstr, lenc);
	return onevalue(ldev);
}


Lisp_Object
om_closeDev(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;

	CSL_IGNORE(nil);
	push(ldev);

	dev = om_toDev(ldev);
	errexitn(1);
	OMcloseDevice(dev);

	pop(ldev);
	return lisp_true;
}


Lisp_Object
om_setDevEncoding(Lisp_Object nil, Lisp_Object ldev, Lisp_Object lenc)
{
	OMdev dev;
	OMencodingType enc;

	CSL_IGNORE(nil);
	push2(ldev, lenc);

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_setDevEncoding: invalid device");
	errexitn(2);

	if (!is_fixnum(lenc))
		return aerror("om_setDevEncoding: invalid encoding");
	errexitn(2);
	/* This gets OMencodingTypes as an integer then casts it to OMencodingType.
	 * That may be a bit dodgy... */
	enc = om_toEncodingType(lenc);
	errexitn(2);

	pop2(ldev, lenc);

	OMsetDeviceEncoding(dev, enc);
	return onevalue(om_fromDev(dev));
}


/*
 * Exposed OpenMath Connection manipulation functions.
 */


Lisp_Object
om_makeConn(Lisp_Object nil, Lisp_Object ltimeout)
{
	OMconn conn;
	int32 timeout;

	CSL_IGNORE(nil);
	push(ltimeout);

	if (!is_fixnum(ltimeout))
		return aerror("om_makeConn: timeout value must be a fixnum");
	errexitn(1);

	timeout = int_of_fixnum(ltimeout);
	errexitn(1);
	conn = OMmakeConn(timeout);

	pop(ltimeout);

	return onevalue(om_fromConn(conn));
}


Lisp_Object
om_closeConn(Lisp_Object nil, Lisp_Object lconn)
{
	OMconn conn;
	OMstatus status;

	CSL_IGNORE(nil);
	push(lconn);

	conn = om_toConn(lconn);
	errexitn(1);
	if (!conn)
		return aerror("om_toConn");
	errexitn(1);

	pop(lconn);

	status = OMconnClose(conn);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_getConnInDev(Lisp_Object nil, Lisp_Object lconn)
{
	OMconn conn;
	OMdev dev;

	CSL_IGNORE(nil);
	push(lconn);

	conn = om_toConn(lconn);
	errexitn(1);
	if (!conn)
		return aerror("om_toConn");
	errexitn(1);

	pop(lconn);

	dev = OMconnIn(conn);
	return onevalue(om_fromDev(dev));
}


Lisp_Object
om_getConnOutDev(Lisp_Object nil, Lisp_Object lconn)
{
	OMconn conn;
	OMdev dev;

	CSL_IGNORE(nil);
	push(lconn);

	conn = om_toConn(lconn);
	errexitn(1);
	if (!conn)
		return aerror("om_toConn");
	errexitn(1);

	pop(lconn);

	dev = OMconnOut(conn);
	return om_fromDev(dev);
}


/*
 * Exposed OpenMath client/server functions.
 */

Lisp_Object MS_CDECL
om_connectTCP(Lisp_Object nil, int nargs, ...)
{
	va_list args;
	Lisp_Object lconn, lhost, lport;
	OMconn conn;
	char *host = NULL;
	int32 hostlen;
	int32 port;
	OMstatus status;

	CSL_IGNORE(nil);

	/* Unpack the parameters into Lisp_Objects. */
	argcheck(nargs, 3, "om_connectTCP");
	va_start(args, nargs);
	lconn = va_arg(args, Lisp_Object);
	lhost = va_arg(args, Lisp_Object);
	lport = va_arg(args, Lisp_Object);
	va_end(args);

	push3(lconn, lhost, lport);

	/* Convert the parameters into their C equivalents. */
	conn = om_toConn(lconn);
	errexitn(3);
	if (!conn)
		return aerror("om_toConn");
	errexitn(3);
	
	if (!stringp(lhost))
		return aerror("om_connectTCP: host name must be a string");
	errexitn(3);
	host = get_string_data(lhost, "om_putString", &hostlen);
	errexitn(3);
	if (host != NULL)
		host[hostlen] = '\0';

	if (!is_fixnum(lport))
		return aerror("om_connectTCP: port number must be a fixnum");
	errexitn(3);
	port = int_of_fixnum(lport);
	errexitn(3);

	pop3(lconn, lhost, lport);

	status = OMconnTCP(conn, host, port);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_bindTCP(Lisp_Object nil, Lisp_Object lconn, Lisp_Object lport)
{
	OMconn conn;
	int32 port;
	OMstatus status;

	CSL_IGNORE(nil);
	push2(lconn, lport);

	conn = om_toConn(lconn);
	errexitn(2);
	if (!conn)
		return aerror("om_toConn");
	errexitn(2);

	if (!is_fixnum(lport))
		return aerror("om_bindTCP: port number must be a fixnum");
	errexitn(2);
	port = int_of_fixnum(lport);
	errexitn(2);

	pop2(lconn, lport);

	status = OMbindTCP(conn, port);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


/*
 * Exposed OpenMath Device output functions.
 */

Lisp_Object
om_putApp(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMputApp(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putEndApp(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMputEndApp(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putAtp(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMputAtp(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putEndAtp(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMputEndAtp(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putAttr(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMputAttr(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putEndAttr(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMputEndAttr(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putBind(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMputBind(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putEndBind(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMputEndBind(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putBVar(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMputBVar(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putEndBVar(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMputEndBVar(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putError(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMputError(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putEndError(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMputEndError(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putObject(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMputObject(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putEndObject(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMputEndObject(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putInt(Lisp_Object nil, Lisp_Object ldev, Lisp_Object val)
/* This routine expects val to be a Lisp integer of some sort.
 * The decision of whether to put it as an int32 or a bigint
 * will be made by this routine.
 */
{
	OMdev dev;
	OMstatus status;
	int size, sign;
	char *data;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	if (!is_number(val) || is_float(val))
		return aerror("om_putInt");
	
	if (is_fixnum(val)) {
		int32 ival = int_of_fixnum(val);
		status = OMputInt32(dev, ival);
	}
	else {
		data = om_toBigNumStr(val);
		size = strlen(data);
		sign = minusp(val) ? -1 : 1;
		status = OMputBigInt(dev, data, size, sign, OMbigIntBase16);
		free(data);
	}

	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putFloat(Lisp_Object nil, Lisp_Object ldev, Lisp_Object val)
/* This routine expects val to be a real-valued number of some
 * sort (this includes floats, rationals, etc.) and puts it
 * out as an IEEE 64-bit floating point number.
 */
{
	/* TODO: check this generates correct output for all real numbers. */
	OMdev dev;
	OMstatus status;
	double fval;

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	if (!is_number(val))
		return aerror("om_putFloat");

	fval = float_of_number(val);

	/* err_printf("[om-putFloat] fval = %.30lf\n", fval); */

	status = OMputFloat64(dev, &fval);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


Lisp_Object
om_putByteArray(Lisp_Object nil, Lisp_Object ldev, Lisp_Object val)
/* This routine expects val to be a Lisp vector of 8-bit values.
 */
{
	OMdev dev;
	OMstatus status;
	int32 len;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	if (!is_vector(val) || !(type_of_header(vechdr(val)) == TYPE_VEC8))
		return aerror("om_toDev");
	
	/* Get the length of the array. */
	len = length_of_header(val) - 4; /* is this correct??? */

	/* Write out the array data. */
	status = OMputByteArray(dev, ((char *)val - TAG_VECTOR + 4), len);
	if (status != OMsuccess) return om_error(status);
	else return lisp_true;
}


Lisp_Object
om_putVar(Lisp_Object nil, Lisp_Object ldev, Lisp_Object val)
/* This routine expects val to be a symbol.
 */
{
	OMdev dev;
	OMstatus status;
	char *name;
	int32 len;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	if (!is_symbol(val))
		return aerror("om_putVar");
	
	/* Do I need to free the memory for name myself? I don't know... */
	name = get_string_data(val, "om_putVar", &len);
	if (name == NULL)
		return om_error(OMinternalError);
	else {
		status = OMputVarN(dev, name, len);
		if (status != OMsuccess)
			return om_error(status);
		else
			return lisp_true;
	}
}


Lisp_Object
om_putString(Lisp_Object nil, Lisp_Object ldev, Lisp_Object val)
/* This routine expects val to be a Lisp string.
 */
{
	OMdev dev;
	OMstatus status;
	char *name;
	int32 len;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	if (!is_vector(val) || !(type_of_header(vechdr(val)) == TYPE_STRING))
		return aerror("om_putString");

	/* Do I need to free the memory for name myself? I don't know... */
	name = get_string_data(val, "om_putString", &len);
	if (name == NULL)
		return om_error(OMinternalError);
	else {
		status = OMputStringN(dev, name, len);
		if (status != OMsuccess)
			return om_error(status);
		else
			return lisp_true;
	}
}


Lisp_Object
om_putSymbol(Lisp_Object nil, Lisp_Object ldev, Lisp_Object val)
/* This routine expects val to be a cons cell where the first element is the
 * name of the content dictionary and the second (and final) element is the
 * name of the symbol.
 */
{
	Lisp_Object cdObj, nameObj;

	/* Check that the value passed in is in the correct format. */
	if (!is_cons(val))
		return aerror("om_putSymbol");

	/* Get the cd and name properties (checking that they are set). */
	cdObj = qcar(val);
	if (cdObj == nil)
		return aerror("om_putSymbol: The cd property was not set");
	nameObj = qcar(qcdr(val));
	if (nameObj == nil)
		return aerror("om_putSymbol: The name property was not set");

	/* Invoke the verbose form of the putSymbol routine to output the data. */
	return om_putSymbol2(nil, 3, ldev, cdObj, nameObj);
}


Lisp_Object MS_CDECL
om_putSymbol2(Lisp_Object nil, int nargs, ...)
/*
 * A different form of putSymbol, where the cd and symbol names are given as strings.
 * The parameters are: (om-putSymbol omdevice "cdname" "symbolname")
 */
{
	va_list args;
	Lisp_Object ldev;
	Lisp_Object lcd, lname;
	OMdev dev;
	char *cd, *name;
	int32 cdLen, nameLen;
	OMstatus status;

	/* Get the arguments from the arglist. */
	argcheck(nargs, 3, "om_putSymbol2");
	va_start(args, nargs);
	ldev = va_arg(args, Lisp_Object);
	lcd = va_arg(args, Lisp_Object);
	lname = va_arg(args, Lisp_Object);
	va_end(args);

	/* err_printf("[om_putSymbol2] about to convert params to C equivalents...\n"); */
	
	/* Convert the parameters into their C equivalents. */
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	if (!is_vector(lcd) || !(type_of_header(vechdr(lcd)) == TYPE_STRING))
		return aerror("om_putSymbol2");
	cd = get_string_data(lcd, "om_putSymbol2", &cdLen);
	if (cd == NULL) {
		status = OMinternalError;
		return om_error(status);
	}

	/* err_printf("[om_putSymbol2] converted cd name (%s)\n", cd); */

	if (!is_vector(lname) || !(type_of_header(vechdr(lname)) == TYPE_STRING))
		return aerror("om_putSymbol2");
	name = get_string_data(lname, "om_putSymbol2", &nameLen);
	if (name == NULL) {
		status = OMinternalError;
		return om_error(status);
	}

	/* err_printf("[om_putSymbol2] converted symbol name (%s)\n", name); */

	/* Now write out the symbol. */
	status = OMputSymbolN(dev, cd, cdLen, name, nameLen);
	if (status != OMsuccess)
		return om_error(status);
	else
		return lisp_true;
}


/*
 * OpenMath input routines.
 */

Lisp_Object
om_getApp(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMgetApp(dev);
	if (status != OMsuccess)
		return om_error(status);
	else 
		return make_undefined_symbol("OMA");
}


Lisp_Object
om_getEndApp(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMgetEndApp(dev);
	if (status != OMsuccess)
		return om_error(status);
	else 
		return make_undefined_symbol("OMA-END");
}


Lisp_Object
om_getAtp(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMgetAtp(dev);
	if (status != OMsuccess)
		return om_error(status);
	else 
		return make_undefined_symbol("OMATP");
}


Lisp_Object
om_getEndAtp(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMgetEndAtp(dev);
	if (status != OMsuccess)
		return om_error(status);
	else 
		return make_undefined_symbol("OMATP-END");
}


Lisp_Object
om_getAttr(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMgetAttr(dev);
	if (status != OMsuccess)
		return om_error(status);
	else 
		return make_undefined_symbol("OMATTR");
}


Lisp_Object
om_getEndAttr(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMgetEndAttr(dev);
	if (status != OMsuccess)
		return om_error(status);
	else 
		return make_undefined_symbol("OMATTR-END");
}


Lisp_Object
om_getBind(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");
	
	status = OMgetBind(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return make_undefined_symbol("OMBIND");
}


Lisp_Object
om_getEndBind(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMgetEndBind(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return make_undefined_symbol("OMBIND-END");
}


Lisp_Object
om_getBVar(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMgetBVar(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return make_undefined_symbol("OMBVAR");
}


Lisp_Object
om_getEndBVar(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMgetEndBVar(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return make_undefined_symbol("OMBVAR-END");
}


Lisp_Object
om_getError(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMgetError(dev);
	if (status != OMsuccess)
		return om_error(status);
	else 
		return make_undefined_symbol("OME");
}


Lisp_Object
om_getEndError(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMgetEndError(dev);
	if (status != OMsuccess)
		return om_error(status);
	else 
		return make_undefined_symbol("OME-END");
}


Lisp_Object
om_getObject(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMgetObject(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return make_undefined_symbol("OMOBJ");
}


Lisp_Object
om_getEndObject(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	
	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMgetEndObject(dev);
	if (status != OMsuccess)
		return om_error(status);
	else
		return make_undefined_symbol("OMOBJ-END");
}


Lisp_Object
om_getInt(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	OMtokenType ttype;
	Lisp_Object obj;

	dev = om_toDev(ldev);
	if (!dev)
		return aerror("om_toDev");

	status = OMgetType(dev, &ttype);
	if (status == OMsuccess) {
		switch (ttype) {
			case OMtokenInt32:
			{
				int32 val;
				status = OMgetInt32(dev, &val);
				if (status == OMsuccess) {
					/* If none of the top 4 bits are set, we can make this a
					 * fixnum */
					/* On second thoughts, the top bit is the sign, and the <<
					 * operation is sign preserving (I believe), so we only
					 * need to check bits 28-30. */
					if ((val & 0x70000000) == 0)
						obj = fixnum_of_int(val);
					else
						obj = make_one_word_bignum(val);
				}
				break;
			}
			case OMtokenBigInt:
			{
				/* TODO: This is broken. Fix it. */
				char *data;
				int len, sign;
				OMbigIntType fmt;

				status = OMgetBigInt(dev, &data, &len, &sign, &fmt);
				if (status == OMsuccess)
					obj = om_fromBigNumStr(data, len, sign, fmt);
				free(data);
				break;
			}
			default:
			{
				obj = om_error(OMmalformedInput);
				break;
			}
		}
	}
	else obj = om_error(status);

	return onevalue(obj);
}


Lisp_Object
om_getFloat(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	OMtokenType ttype;
	double val;

	dev = om_toDev(ldev);
	if (dev == NULL)
		return aerror("om_toDev");

	status = OMgetType(dev, &ttype);
	if (status == OMsuccess) {
		status = OMgetFloat64(dev, &val);
		if (status == OMsuccess) {
			/* err_printf("[om_getFloat] fval = %.30lf\n", val); */
			return make_boxfloat(val, TYPE_DOUBLE_FLOAT);
		}
		else
			return om_error(status);
	}
	else return om_error(status);
}


Lisp_Object
om_getByteArray(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	int len;
	Lisp_Object obj;

	dev = om_toDev(ldev);
	if (dev == NULL)
		return aerror("om_toDev");
	
	status = OMgetLength(dev, &len);
	if (status != OMsuccess)
		return om_error(status);
	else {
		/* I hope this is right... */
		obj = getvector(TAG_VECTOR, TYPE_VEC8, len + 4);
		status = OMgetByteArrayN(dev, ((char *)obj - TAG_VECTOR + 4), len);
		if (status != OMsuccess)
			return om_error(status);
		else
			return obj;
	}
}


Lisp_Object
om_getVar(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	char *var;
	Lisp_Object obj;

	dev = om_toDev(ldev);
	if (dev == NULL)
		return aerror("om_toDev");

	status = OMgetVar(dev, &var);
	if (status != OMsuccess)
		return om_error(status);
	else {
		obj = make_symbol(var, 2, /* do not convert name to upper case */
			undefined1, undefined2, undefinedn);
		free(var);
		return obj;
	}
}


Lisp_Object
om_getString(Lisp_Object nil, Lisp_Object ldev)
{
	OMdev dev;
	OMstatus status;
	char *str;
	Lisp_Object obj;

	dev = om_toDev(ldev);
	if (dev == NULL)
		return aerror("om_toDev");

	status = OMgetString(dev, &str);
	if (status != OMsuccess)
		return om_error(status);
	else {
		obj = make_string(str);
		free(str);
		return obj;
	}
}


Lisp_Object
om_getSymbol(Lisp_Object nil, Lisp_Object ldev)
/* This returns the Lisp symbol OMS, with a cd property and a name property set
 * to appropriate string values.
 */
{
  OMdev dev;
  OMstatus status;
  char *cd, *name;
  int cdLen, nameLen;
  Lisp_Object cdstr, namestr, obj;

  CSL_IGNORE(nil);
  push(ldev);

  dev = om_toDev(ldev);
  errexitn(1);
  if (dev == NULL)
    return aerror("om_toDev");
  errexitn(1);

  pop(ldev);

  status = OMgetSymbolLength(dev, &cdLen, &nameLen);
  if (status != OMsuccess)
    return om_error(status);
  cd = (char *)malloc(sizeof(char) * (cdLen + 1));
  name = (char *)malloc(sizeof(char) * (nameLen + 1));
  if (cd == NULL || name == NULL) {
    if (cd != NULL) free(cd);
    else if (name != NULL) free(name);
    return om_error(OMinternalError);
  }
  cd[cdLen] = '\0';
  name[nameLen] = '\0';

  status = OMgetSymbolN(dev, cd, cdLen, name, nameLen);
  if (status != OMsuccess)
    obj = om_error(status);
  else {
    cdstr = make_string(cd);
    namestr = make_string(name);
    /* FIXME: is this needed?  push2(cdstr, namestr);*/
    obj = cons(cdstr, cons(namestr, C_nil));
  }

  free(cd);
  free(name);
  /*return onevalue(obj);*/
  return obj;
}


#define om_errmsg0(msg)			\
	err_printf("[om_getType] %s\n", msg)
#define om_errmsg1(msg,a1)		\
	err_printf("[om_getType] %s%s\n", msg,a1)
#define om_errmsg2(msg,a1,a2)	\
	err_printf("[om_getType] %s%s%s\n", msg,a1,a2)


Lisp_Object
om_getType(Lisp_Object nil, Lisp_Object ldev)
{
	static char *typenames[] = {
		"OMtokenApp",		"OMtokenEndApp",
		"OMtokenAtp",		"OMtokenEndAtp",
		"OMtokenAttr",		"OMtokenEndAttr",
		"OMtokenBind",		"OMtokenEndBind",
		"OMtokenBVar",		"OMtokenEndBVar",
		"OMtokenError",		"OMtokenEndError",
		"OMtokenObject",	"OMtokenEndObject",
		"OMtokenInt",
		"OMtokenFloat",
		"OMtokenByteArray",
		"OMtokenVar",
		"OMtokenString",
		"OMtokenSymbol"
	};
	
	OMdev dev;
	OMstatus status;
	OMtokenType ttype;
	char *typename;
	Lisp_Object obj;

	dev = om_toDev(ldev);
	if (dev == NULL)
		return aerror("om_toDev");

	status = OMgetType(dev, &ttype);
	if (status != OMsuccess)
		return om_error(status);
	else {
		switch (ttype) {
			case OMtokenApp:		typename = typenames[0];	break;
			case OMtokenEndApp:		typename = typenames[1];	break;
			case OMtokenAtp:		typename = typenames[2];	break;
			case OMtokenEndAtp:		typename = typenames[3];	break;
			case OMtokenAttr:		typename = typenames[4];	break;
			case OMtokenEndAttr:	        typename = typenames[5];	break;
			case OMtokenBind:		typename = typenames[6];	break;
			case OMtokenEndBind:	        typename = typenames[7];	break;
			case OMtokenBVar:		typename = typenames[8];	break;
			case OMtokenEndBVar:	        typename = typenames[9];	break;
			case OMtokenError:		typename = typenames[10];	break;
			case OMtokenEndError:	        typename = typenames[11];	break;
			case OMtokenObject:		typename = typenames[12];	break;
			case OMtokenEndObject:	        typename = typenames[13];	break;
			case OMtokenInt32:		typename = typenames[14];	break;
			case OMtokenBigInt:		typename = typenames[14];	break;
			case OMtokenFloat64:	        typename = typenames[15];	break;
			case OMtokenByteArray:	        typename = typenames[16];	break;
			case OMtokenVar:		typename = typenames[17];	break;
			case OMtokenString:		typename = typenames[18];	break;
			case OMtokenSymbol:		typename = typenames[19];	break;
		}

		obj = make_undefined_symbol(typename);
		return obj;
	}
}


Lisp_Object
om_stringToStringPtr(Lisp_Object nil, Lisp_Object lstr)
{
	return om_cStringFromLispString(lstr);
}


Lisp_Object
om_stringPtrToString(Lisp_Object nil, Lisp_Object lpstr)
{
	return om_lispStringFromCString(lpstr);
}


setup_type const om_setup[] = {
	/* LISP Name */			/* Unary */			/* Binary */		/* Nary */
	{"om-openFileDev",		wrong_no_3a,		wrong_no_3b,		om_openFileDev},
	{"om-openStringDev",            too_few_2,		om_openStringDev,	wrong_no_2},
	{"om-closeDev",			om_closeDev,		too_many_1,		wrong_no_1},
	{"om-setDevEncoding",	        too_few_2,		om_setDevEncoding,	wrong_no_2},

	{"om-makeConn",			om_makeConn,		too_many_1,		wrong_no_1},
	{"om-closeConn",		om_closeConn,		too_many_1,		wrong_no_1},
	{"om-getConnInDev",		om_getConnInDev,	too_many_1,		wrong_no_1},
	{"om-getConnOutDev",	        om_getConnOutDev,	too_many_1,		wrong_no_1},

	{"om-connectTCP",		wrong_no_3a,		wrong_no_3b,		om_connectTCP},
	{"om-bindTCP",			too_few_2,		om_bindTCP,		wrong_no_2},

	{"om-putApp",			om_putApp,		too_many_1,		wrong_no_1},
	{"om-putEndApp",		om_putEndApp,		too_many_1,		wrong_no_1},
	{"om-putAtp",			om_putAtp,		too_many_1,		wrong_no_1},
	{"om-putEndAtp",		om_putEndAtp,		too_many_1,		wrong_no_1},
	{"om-putAttr",			om_putAttr,		too_many_1,		wrong_no_1},
	{"om-putEndAttr",		om_putEndAttr,		too_many_1,		wrong_no_1},
	{"om-putBind",			om_putBind,		too_many_1,		wrong_no_1},
	{"om-putEndBind",		om_putEndBind,		too_many_1,		wrong_no_1},
	{"om-putBVar",			om_putBVar,		too_many_1,		wrong_no_1},
	{"om-putEndBVar",		om_putEndBVar,		too_many_1,		wrong_no_1},
	{"om-putError",			om_putError,		too_many_1,		wrong_no_1},
	{"om-putEndError",		om_putEndError,		too_many_1,		wrong_no_1},
	{"om-putObject",		om_putObject,		too_many_1,		wrong_no_1},
	{"om-putEndObject",		om_putEndObject,	too_many_1,		wrong_no_1},

	{"om-putInt",			too_few_2,		om_putInt,		wrong_no_2},
	{"om-putFloat",			too_few_2,		om_putFloat,		wrong_no_2},
	{"om-putByteArray",		too_few_2,		om_putByteArray,	wrong_no_2},
	{"om-putVar",			too_few_2,		om_putVar,		wrong_no_2},
	{"om-putString",		too_few_2,		om_putString,		wrong_no_2},
	{"om-putSymbol",		too_few_2,		om_putSymbol,		om_putSymbol2},

	{"om-getApp",			om_getApp,		too_many_1,		wrong_no_1},
	{"om-getEndApp",		om_getEndApp,		too_many_1,		wrong_no_1},
	{"om-getAtp",			om_getAtp,		too_many_1,		wrong_no_1},
	{"om-getEndAtp",		om_getEndAtp,		too_many_1,		wrong_no_1},
	{"om-getAttr",			om_getAttr,		too_many_1,		wrong_no_1},
	{"om-getEndAttr",		om_getEndAttr,		too_many_1,		wrong_no_1},
	{"om-getBind",			om_getBind,		too_many_1,		wrong_no_1},
	{"om-getEndBind",		om_getEndBind,		too_many_1,		wrong_no_1},
	{"om-getBVar",			om_getBVar,		too_many_1,		wrong_no_1},
	{"om-getEndBVar",		om_getEndBVar,		too_many_1,		wrong_no_1},
	{"om-getError",			om_getError,		too_many_1,		wrong_no_1},
	{"om-getendError",		om_getEndError,		too_many_1,		wrong_no_1},
	{"om-getObject",		om_getObject,		too_many_1,		wrong_no_1},
	{"om-getEndObject",		om_getEndObject,	too_many_1,		wrong_no_1},

	{"om-getInt",			om_getInt,		too_many_1,		wrong_no_1},
	{"om-getFloat",			om_getFloat,		too_many_1,		wrong_no_1},
	{"om-getByteArray",		om_getByteArray,	too_many_1,		wrong_no_1},
	{"om-getVar",			om_getVar,		too_many_1,		wrong_no_1},
	{"om-getString",		om_getString,		too_many_1,		wrong_no_1},
	{"om-getSymbol",		om_getSymbol,		too_many_1,		wrong_no_1},

	{"om-getType",			om_getType,		too_many_1,		wrong_no_1},

	{"om-stringToStringPtr",     om_stringToStringPtr,      too_many_1,		wrong_no_1},
	{"om-stringPtrToString",     om_stringPtrToString,      too_many_1,		wrong_no_1},

	{NULL,	0,	0,	0}
};

#endif /* OPENMATH */
