/* src/interfaces/ecpg/compatlib/informix.c */

#define POSTGRES_ECPG_INTERNAL
#include "postgres_fe.h"

#include <math.h>
#include <ctype.h>
#include <limits.h>

#include "ecpg_informix.h"
#include "ecpgerrno.h"
#include "ecpgtype.h"
#include "pgtypes_date.h"
#include "pgtypes_error.h"
#include "pgtypes_numeric.h"
#include "sqlca.h"
#include "sqltypes.h"

/* this is also defined in ecpglib/misc.c, by defining it twice we don't have to export the symbol */

static struct sqlca_t sqlca_init =
{
	{
		'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '
	},
	sizeof(struct sqlca_t),
	0,
	{
		0,
		{
			0
		}
	},
	{
		'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '
	},
	{
		0, 0, 0, 0, 0, 0
	},
	{
		0, 0, 0, 0, 0, 0, 0, 0
	},
	{
		'0', '0', '0', '0', '0'
	}
};
static int
deccall2(decimal *arg1, decimal *arg2, int (*ptr) (numeric *, numeric *))
{
	numeric    *a1,
			   *a2;
	int			i;

	if ((a1 = PGTYPESnumeric_new()) == NULL)
		return ECPG_INFORMIX_OUT_OF_MEMORY;

	if ((a2 = PGTYPESnumeric_new()) == NULL)
	{
		PGTYPESnumeric_free(a1);
		return ECPG_INFORMIX_OUT_OF_MEMORY;
	}

	if (PGTYPESnumeric_from_decimal(arg1, a1) != 0)
	{
		PGTYPESnumeric_free(a1);
		PGTYPESnumeric_free(a2);
		return ECPG_INFORMIX_OUT_OF_MEMORY;
	}

	if (PGTYPESnumeric_from_decimal(arg2, a2) != 0)
	{
		PGTYPESnumeric_free(a1);
		PGTYPESnumeric_free(a2);
		return ECPG_INFORMIX_OUT_OF_MEMORY;
	}

	i = (*ptr) (a1, a2);

	PGTYPESnumeric_free(a1);
	PGTYPESnumeric_free(a2);

	return i;
}

static int
deccall3(decimal *arg1, decimal *arg2, decimal *result, int (*ptr) (numeric *, numeric *, numeric *))
{
	numeric    *a1,
			   *a2,
			   *nres;
	int			i;

	/*
	 * we must NOT set the result to NULL here because it may be the same
	 * variable as one of the arguments
	 */
	if (risnull(CDECIMALTYPE, (char *) arg1) || risnull(CDECIMALTYPE, (char *) arg2))
		return 0;

	if ((a1 = PGTYPESnumeric_new()) == NULL)
		return ECPG_INFORMIX_OUT_OF_MEMORY;

	if ((a2 = PGTYPESnumeric_new()) == NULL)
	{
		PGTYPESnumeric_free(a1);
		return ECPG_INFORMIX_OUT_OF_MEMORY;
	}

	if ((nres = PGTYPESnumeric_new()) == NULL)
	{
		PGTYPESnumeric_free(a1);
		PGTYPESnumeric_free(a2);
		return ECPG_INFORMIX_OUT_OF_MEMORY;
	}

	if (PGTYPESnumeric_from_decimal(arg1, a1) != 0)
	{
		PGTYPESnumeric_free(a1);
		PGTYPESnumeric_free(a2);
		PGTYPESnumeric_free(nres);
		return ECPG_INFORMIX_OUT_OF_MEMORY;
	}

	if (PGTYPESnumeric_from_decimal(arg2, a2) != 0)
	{
		PGTYPESnumeric_free(a1);
		PGTYPESnumeric_free(a2);
		PGTYPESnumeric_free(nres);
		return ECPG_INFORMIX_OUT_OF_MEMORY;
	}

	i = (*ptr) (a1, a2, nres);

	if (i == 0)					/* No error */
	{

		/* set the result to null in case it errors out later */
		rsetnull(CDECIMALTYPE, (char *) result);
		PGTYPESnumeric_to_decimal(nres, result);
	}

	PGTYPESnumeric_free(nres);
	PGTYPESnumeric_free(a1);
	PGTYPESnumeric_free(a2);

	return i;
}

/* we start with the numeric functions */
int
decadd(decimal *arg1, decimal *arg2, decimal *sum)
{
	errno = 0;
	deccall3(arg1, arg2, sum, PGTYPESnumeric_add);

	if (errno == PGTYPES_NUM_OVERFLOW)
		return ECPG_INFORMIX_NUM_OVERFLOW;
	else if (errno == PGTYPES_NUM_UNDERFLOW)
		return ECPG_INFORMIX_NUM_UNDERFLOW;
	else if (errno != 0)
		return -1;
	else
		return 0;
}

int
deccmp(decimal *arg1, decimal *arg2)
{
	return deccall2(arg1, arg2, PGTYPESnumeric_cmp);
}

void
deccopy(decimal *src, decimal *target)
{
	memcpy(target, src, sizeof(decimal));
}

int
deccvasc(const char *cp, int len, decimal *np)
{
	char	   *str;
	int			ret = 0;
	numeric    *result;

	rsetnull(CDECIMALTYPE, (char *) np);
	if (risnull(CSTRINGTYPE, cp))
		return 0;

	str = pnstrdup(cp, len);	/* decimal_in always converts the complete
								 * string */
	if (!str)
		ret = ECPG_INFORMIX_NUM_UNDERFLOW;
	else
	{
		errno = 0;
		result = PGTYPESnumeric_from_asc(str, NULL);
		if (!result)
		{
			switch (errno)
			{
				case PGTYPES_NUM_OVERFLOW:
					ret = ECPG_INFORMIX_NUM_OVERFLOW;
					break;
				case PGTYPES_NUM_BAD_NUMERIC:
					ret = ECPG_INFORMIX_BAD_NUMERIC;
					break;
				default:
					ret = ECPG_INFORMIX_BAD_EXPONENT;
					break;
			}
		}
		else
		{
			int			i = PGTYPESnumeric_to_decimal(result, np);

			PGTYPESnumeric_free(result);
			if (i != 0)
				ret = ECPG_INFORMIX_NUM_OVERFLOW;
		}
	}

	free(str);
	return ret;
}

int
deccvdbl(double dbl, decimal *np)
{
	numeric    *nres;
	int			result = 1;

	rsetnull(CDECIMALTYPE, (char *) np);
	if (risnull(CDOUBLETYPE, (char *) &dbl))
		return 0;

	nres = PGTYPESnumeric_new();
	if (nres == NULL)
		return ECPG_INFORMIX_OUT_OF_MEMORY;

	result = PGTYPESnumeric_from_double(dbl, nres);
	if (result == 0)
		result = PGTYPESnumeric_to_decimal(nres, np);

	PGTYPESnumeric_free(nres);
	return result;
}

int
deccvint(int in, decimal *np)
{
	numeric    *nres;
	int			result = 1;

	rsetnull(CDECIMALTYPE, (char *) np);
	if (risnull(CINTTYPE, (char *) &in))
		return 0;

	nres = PGTYPESnumeric_new();
	if (nres == NULL)
		return ECPG_INFORMIX_OUT_OF_MEMORY;

	result = PGTYPESnumeric_from_int(in, nres);
	if (result == 0)
		result = PGTYPESnumeric_to_decimal(nres, np);

	PGTYPESnumeric_free(nres);
	return result;
}

int
deccvlong(long lng, decimal *np)
{
	numeric    *nres;
	int			result = 1;

	rsetnull(CDECIMALTYPE, (char *) np);
	if (risnull(CLONGTYPE, (char *) &lng))
		return 0;

	nres = PGTYPESnumeric_new();
	if (nres == NULL)
		return ECPG_INFORMIX_OUT_OF_MEMORY;

	result = PGTYPESnumeric_from_long(lng, nres);
	if (result == 0)
		result = PGTYPESnumeric_to_decimal(nres, np);

	PGTYPESnumeric_free(nres);
	return result;
}

int
decdiv(decimal *n1, decimal *n2, decimal *result)
{
	int			i;

	errno = 0;
	i = deccall3(n1, n2, result, PGTYPESnumeric_div);

	if (i != 0)
		switch (errno)
		{
			case PGTYPES_NUM_DIVIDE_ZERO:
				return ECPG_INFORMIX_DIVIDE_ZERO;
				break;
			case PGTYPES_NUM_OVERFLOW:
				return ECPG_INFORMIX_NUM_OVERFLOW;
				break;
			default:
				return ECPG_INFORMIX_NUM_UNDERFLOW;
				break;
		}

	return 0;
}

int
decmul(decimal *n1, decimal *n2, decimal *result)
{
	int			i;

	errno = 0;
	i = deccall3(n1, n2, result, PGTYPESnumeric_mul);

	if (i != 0)
		switch (errno)
		{
			case PGTYPES_NUM_OVERFLOW:
				return ECPG_INFORMIX_NUM_OVERFLOW;
				break;
			default:
				return ECPG_INFORMIX_NUM_UNDERFLOW;
				break;
		}

	return 0;
}

int
decsub(decimal *n1, decimal *n2, decimal *result)
{
	int			i;

	errno = 0;
	i = deccall3(n1, n2, result, PGTYPESnumeric_sub);

	if (i != 0)
		switch (errno)
		{
			case PGTYPES_NUM_OVERFLOW:
				return ECPG_INFORMIX_NUM_OVERFLOW;
				break;
			default:
				return ECPG_INFORMIX_NUM_UNDERFLOW;
				break;
		}

	return 0;
}

int
dectoasc(decimal *np, char *cp, int len, int right)
{
	char	   *str;
	numeric    *nres;

	rsetnull(CSTRINGTYPE, (char *) cp);
	if (risnull(CDECIMALTYPE, (char *) np))
		return 0;

	nres = PGTYPESnumeric_new();
	if (nres == NULL)
		return ECPG_INFORMIX_OUT_OF_MEMORY;

	if (PGTYPESnumeric_from_decimal(np, nres) != 0)
	{
		PGTYPESnumeric_free(nres);
		return ECPG_INFORMIX_OUT_OF_MEMORY;
	}

	if (right >= 0)
		str = PGTYPESnumeric_to_asc(nres, right);
	else
		str = PGTYPESnumeric_to_asc(nres, nres->dscale);

	PGTYPESnumeric_free(nres);
	if (!str)
		return -1;

	/*
	 * TODO: have to take care of len here and create exponential notation if
	 * necessary
	 */
	if ((int) (strlen(str) + 1) > len)
	{
		if (len > 1)
		{
			cp[0] = '*';
			cp[1] = '\0';
		}
		free(str);
		return -1;
	}
	else
	{
		strcpy(cp, str);
		free(str);
		return 0;
	}
}

int
dectodbl(decimal *np, double *dblp)
{
	int			i;
	numeric    *nres = PGTYPESnumeric_new();

	if (nres == NULL)
		return ECPG_INFORMIX_OUT_OF_MEMORY;

	if (PGTYPESnumeric_from_decimal(np, nres) != 0)
	{
		PGTYPESnumeric_free(nres);
		return ECPG_INFORMIX_OUT_OF_MEMORY;
	}

	i = PGTYPESnumeric_to_double(nres, dblp);
	PGTYPESnumeric_free(nres);

	return i;
}

int
dectoint(decimal *np, int *ip)
{
	int			ret;
	numeric    *nres = PGTYPESnumeric_new();

	if (nres == NULL)
		return ECPG_INFORMIX_OUT_OF_MEMORY;

	if (PGTYPESnumeric_from_decimal(np, nres) != 0)
	{
		PGTYPESnumeric_free(nres);
		return ECPG_INFORMIX_OUT_OF_MEMORY;
	}

	ret = PGTYPESnumeric_to_int(nres, ip);
	PGTYPESnumeric_free(nres);

	if (ret == PGTYPES_NUM_OVERFLOW)
		ret = ECPG_INFORMIX_NUM_OVERFLOW;

	return ret;
}

int
dectolong(decimal *np, long *lngp)
{
	int			ret;
	numeric    *nres = PGTYPESnumeric_new();

	if (nres == NULL)
		return ECPG_INFORMIX_OUT_OF_MEMORY;

	if (PGTYPESnumeric_from_decimal(np, nres) != 0)
	{
		PGTYPESnumeric_free(nres);
		return ECPG_INFORMIX_OUT_OF_MEMORY;
	}

	ret = PGTYPESnumeric_to_long(nres, lngp);
	PGTYPESnumeric_free(nres);

	if (ret == PGTYPES_NUM_OVERFLOW)
		ret = ECPG_INFORMIX_NUM_OVERFLOW;

	return ret;
}

/* Now the date functions */
int
rdatestr(date d, char *str)
{
	char	   *tmp = PGTYPESdate_to_asc(d);

	if (!tmp)
		return ECPG_INFORMIX_DATE_CONVERT;

	/* move to user allocated buffer */
	strcpy(str, tmp);
	free(tmp);

	return 0;
}

/*
*
* the input for this function is mmddyyyy and any non-numeric
* character can be used as a separator
*
*/
int
rstrdate(const char *str, date * d)
{
	return rdefmtdate(d, "mm/dd/yyyy", str);
}

void
rtoday(date * d)
{
	PGTYPESdate_today(d);
}

int
rjulmdy(date d, short *mdy)
{
	int			mdy_int[3];

	PGTYPESdate_julmdy(d, mdy_int);
	mdy[0] = (short) mdy_int[0];
	mdy[1] = (short) mdy_int[1];
	mdy[2] = (short) mdy_int[2];
	return 0;
}

int
rdefmtdate(date * d, const char *fmt, const char *str)
{
	/* TODO: take care of DBCENTURY environment variable */
	/* PGSQL functions allow all centuries */

	errno = 0;
	if (PGTYPESdate_defmt_asc(d, fmt, str) == 0)
		return 0;

	switch (errno)
	{
		case PGTYPES_DATE_ERR_ENOSHORTDATE:
			return ECPG_INFORMIX_ENOSHORTDATE;
		case PGTYPES_DATE_ERR_EARGS:
		case PGTYPES_DATE_ERR_ENOTDMY:
			return ECPG_INFORMIX_ENOTDMY;
		case PGTYPES_DATE_BAD_DAY:
			return ECPG_INFORMIX_BAD_DAY;
		case PGTYPES_DATE_BAD_MONTH:
			return ECPG_INFORMIX_BAD_MONTH;
		default:
			return ECPG_INFORMIX_BAD_YEAR;
	}
}

int
rfmtdate(date d, const char *fmt, char *str)
{
	errno = 0;
	if (PGTYPESdate_fmt_asc(d, fmt, str) == 0)
		return 0;

	if (errno == ENOMEM)
		return ECPG_INFORMIX_OUT_OF_MEMORY;

	return ECPG_INFORMIX_DATE_CONVERT;
}

int
rmdyjul(short *mdy, date * d)
{
	int			mdy_int[3];

	mdy_int[0] = mdy[0];
	mdy_int[1] = mdy[1];
	mdy_int[2] = mdy[2];
	PGTYPESdate_mdyjul(mdy_int, d);
	return 0;
}

int
rdayofweek(date d)
{
	return PGTYPESdate_dayofweek(d);
}

/* And the datetime stuff */

void
dtcurrent(timestamp * ts)
{
	PGTYPEStimestamp_current(ts);
}

int
dtcvasc(char *str, timestamp * ts)
{
	timestamp	ts_tmp;
	int			i;
	char	  **endptr = &str;

	errno = 0;
	ts_tmp = PGTYPEStimestamp_from_asc(str, endptr);
	i = errno;
	if (i)
		/* TODO: rewrite to Informix error codes */
		return i;
	if (**endptr)
	{
		/* extra characters exist at the end */
		return ECPG_INFORMIX_EXTRA_CHARS;
	}
	/* TODO: other Informix error codes missing */

	/* everything went fine */
	*ts = ts_tmp;

	return 0;
}

int
dtcvfmtasc(char *inbuf, char *fmtstr, timestamp * dtvalue)
{
	return PGTYPEStimestamp_defmt_asc(inbuf, fmtstr, dtvalue);
}

int
dtsub(timestamp * ts1, timestamp * ts2, interval * iv)
{
	return PGTYPEStimestamp_sub(ts1, ts2, iv);
}

int
dttoasc(timestamp * ts, char *output)
{
	char	   *asctime = PGTYPEStimestamp_to_asc(*ts);

	strcpy(output, asctime);
	free(asctime);
	return 0;
}

int
dttofmtasc(timestamp * ts, char *output, int str_len, char *fmtstr)
{
	return PGTYPEStimestamp_fmt_asc(ts, output, str_len, fmtstr);
}

int
intoasc(interval * i, char *str)
{
	char	   *tmp;

	errno = 0;
	tmp = PGTYPESinterval_to_asc(i);

	if (!tmp)
		return -errno;

	strcpy(str, tmp);
	free(tmp);
	return 0;
}

static struct
{
	long		val;
	int			maxdigits;
	int			digits;
	int			remaining;
	char		sign;
	char	   *val_string;
}			value;

/**
 * initialize the struct, which holds the different forms
 * of the long value
 */
static int
initValue(long lng_val)
{
	int			i,
				j;
	long		l,
				dig;

	/* set some obvious things */
	value.val = lng_val >= 0 ? lng_val : lng_val * (-1);
	value.sign = lng_val >= 0 ? '+' : '-';
	value.maxdigits = log10(2) * (8 * sizeof(long) - 1);

	/* determine the number of digits */
	i = 0;
	l = 1;
	do
	{
		i++;
		l *= 10;
	}
	while ((l - 1) < value.val && l <= LONG_MAX / 10);

	if (l <= LONG_MAX / 10)
	{
		value.digits = i;
		l /= 10;
	}
	else
		value.digits = i + 1;

	value.remaining = value.digits;

	/* convert the long to string */
	if ((value.val_string = (char *) malloc(value.digits + 1)) == NULL)
		return -1;
	dig = value.val;
	for (i = value.digits, j = 0; i > 0; i--, j++)
	{
		value.val_string[j] = dig / l + '0';
		dig = dig % l;
		l /= 10;
	}
	value.val_string[value.digits] = '\0';
	return 0;
}

/* return the position of the right-most dot in some string */
static int
getRightMostDot(const char *str)
{
	size_t		len = strlen(str);
	int			i,
				j;

	j = 0;
	for (i = len - 1; i >= 0; i--)
	{
		if (str[i] == '.')
			return len - j - 1;
		j++;
	}
	return -1;
}

/* And finally some misc functions */
int
rfmtlong(long lng_val, const char *fmt, char *outbuf)
{
	size_t		fmt_len = strlen(fmt);
	size_t		temp_len;
	int			i,
				j,				/* position in temp */
				k,
				dotpos;
	int			leftalign = 0,
				blank = 0,
				sign = 0,
				entitydone = 0,
				signdone = 0,
				brackets_ok = 0;
	char	   *temp;
	char		tmp[2] = " ";
	char		lastfmt = ' ',
				fmtchar = ' ';

	temp = (char *) malloc(fmt_len + 1);
	if (!temp)
	{
		errno = ENOMEM;
		return -1;
	}

	/* put all info about the long in a struct */
	if (initValue(lng_val) == -1)
	{
		free(temp);
		errno = ENOMEM;
		return -1;
	}

	/* '<' is the only format, where we have to align left */
	if (strchr(fmt, (int) '<'))
		leftalign = 1;

	/* '(' requires ')' */
	if (strchr(fmt, (int) '(') && strchr(fmt, (int) ')'))
		brackets_ok = 1;

	/* get position of the right-most dot in the format-string */
	/* and fill the temp-string wit '0's up to there. */
	dotpos = getRightMostDot(fmt);

	/* start to parse the format-string */
	temp[0] = '\0';
	k = value.digits - 1;		/* position in the value_string */
	for (i = fmt_len - 1, j = 0; i >= 0; i--, j++)
	{
		/* qualify, where we are in the value_string */
		if (k < 0)
		{
			blank = 1;
			if (k == -1)
				sign = 1;
			if (leftalign)
			{
				/* can't use strncat(,,0) here, Solaris would freak out */
				if (sign)
					if (signdone)
					{
						temp[j] = '\0';
						break;
					}
			}
		}
		/* if we're right side of the right-most dot, print '0' */
		if (dotpos >= 0 && dotpos <= i)
		{
			if (dotpos < i)
			{
				if (fmt[i] == ')')
					tmp[0] = value.sign == '-' ? ')' : ' ';
				else
					tmp[0] = '0';
			}
			else
				tmp[0] = '.';
			strcat(temp, tmp);
			continue;
		}
		/* the ',' needs special attention, if it is in the blank area */
		if (blank && fmt[i] == ',')
			fmtchar = lastfmt;
		else
			fmtchar = fmt[i];
		/* waiting for the sign */
		if (k < 0 && leftalign && sign && !signdone && fmtchar != '+' && fmtchar != '-')
			continue;
		/* analyse this format-char */
		switch (fmtchar)
		{
			case ',':
				tmp[0] = ',';
				k++;
				break;
			case '*':
				if (blank)
					tmp[0] = '*';
				else
					tmp[0] = value.val_string[k];
				break;
			case '&':
				if (blank)
					tmp[0] = '0';
				else
					tmp[0] = value.val_string[k];
				break;
			case '#':
				if (blank)
					tmp[0] = ' ';
				else
					tmp[0] = value.val_string[k];
				break;
			case '-':
				if (sign && value.sign == '-' && !signdone)
				{
					tmp[0] = '-';
					signdone = 1;
				}
				else if (blank)
					tmp[0] = ' ';
				else
					tmp[0] = value.val_string[k];
				break;
			case '+':
				if (sign && !signdone)
				{
					tmp[0] = value.sign;
					signdone = 1;
				}
				else if (blank)
					tmp[0] = ' ';
				else
					tmp[0] = value.val_string[k];
				break;
			case '(':
				if (sign && brackets_ok && value.sign == '-')
					tmp[0] = '(';
				else if (blank)
					tmp[0] = ' ';
				else
					tmp[0] = value.val_string[k];
				break;
			case ')':
				if (brackets_ok && value.sign == '-')
					tmp[0] = ')';
				else
					tmp[0] = ' ';
				break;
			case '$':
				if (blank && !entitydone)
				{
					tmp[0] = '$';
					entitydone = 1;
				}
				else if (blank)
					tmp[0] = ' ';
				else
					tmp[0] = value.val_string[k];
				break;
			case '<':
				tmp[0] = value.val_string[k];
				break;
			default:
				tmp[0] = fmt[i];
		}
		strcat(temp, tmp);
		lastfmt = fmt[i];
		k--;
	}
	/* safety-net */
	temp[fmt_len] = '\0';

	/* reverse the temp-string and put it into the outbuf */
	temp_len = strlen(temp);
	outbuf[0] = '\0';
	for (i = temp_len - 1; i >= 0; i--)
	{
		tmp[0] = temp[i];
		strcat(outbuf, tmp);
	}
	outbuf[temp_len] = '\0';

	/* cleaning up */
	free(temp);
	free(value.val_string);

	return 0;
}

void
rupshift(char *str)
{
	for (; *str != '\0'; str++)
		if (islower((unsigned char) *str))
			*str = toupper((unsigned char) *str);
}

int
byleng(char *str, int len)
{
	for (len--; str[len] && str[len] == ' '; len--);
	return (len + 1);
}

void
ldchar(char *src, int len, char *dest)
{
	int			dlen = byleng(src, len);

	memmove(dest, src, dlen);
	dest[dlen] = '\0';
}

int
rgetmsg(int msgnum, char *s, int maxsize)
{
	(void) msgnum;				/* keep the compiler quiet */
	(void) s;					/* keep the compiler quiet */
	(void) maxsize;				/* keep the compiler quiet */
	return 0;
}

int
rtypalign(int offset, int type)
{
	(void) offset;				/* keep the compiler quiet */
	(void) type;				/* keep the compiler quiet */
	return 0;
}

int
rtypmsize(int type, int len)
{
	(void) type;				/* keep the compiler quiet */
	(void) len;					/* keep the compiler quiet */
	return 0;
}

int
rtypwidth(int sqltype, int sqllen)
{
	(void) sqltype;				/* keep the compiler quiet */
	(void) sqllen;				/* keep the compiler quiet */
	return 0;
}

void
ECPG_informix_set_var(int number, void *pointer, int lineno)
{
	ECPGset_var(number, pointer, lineno);
}

void *
ECPG_informix_get_var(int number)
{
	return ECPGget_var(number);
}

void
ECPG_informix_reset_sqlca(void)
{
	struct sqlca_t *sqlca = ECPGget_sqlca();

	if (sqlca == NULL)
		return;

	memcpy((char *) sqlca, (char *) &sqlca_init, sizeof(struct sqlca_t));
}

int
rsetnull(int t, char *ptr)
{
	ECPGset_noind_null(t, ptr);
	return 0;
}

int
risnull(int t, const char *ptr)
{
	return ECPGis_noind_null(t, ptr);
}
