/*====================================================================
  File Name:	udflib.c
  Description:
  This module contains some user defined functions (UDF).  
  The test suite for UDF will use udf.sql
  to define UDF to the database using SQL statements.
====================================================================== */

#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <math.h>
#include <ibase.h>
#include "example.h"

#define BADVAL -9999L
#define MYBUF_LEN 15		/* number of chars to get for */

typedef struct blob {
	short	(*blob_get_segment) ();
	void	*blob_handle;
	long	blob_number_segments;
	long	blob_max_segment;
	long	blob_total_length;
	void	(*blob_put_segment) ();
} *BLOB;

time_t	time();
char	*ctime();

/* variable to return values in.  Needs to be static so it doesn't go 
   away as soon as the function invocation is finished */

char	buffer[256];
char	buffer2[512];	/* for string concatenation */
char	datebuf[12];	/* for date string */

long	r_long;
double	r_double;
float	r_float;
short	r_short;

struct	tm *tbuf;
long	time_sec;

ISC_QUAD newdate;


/*===============================================================
 fn_lower_c() - Puts its argument longo lower case, for C programs
 Input is of VARCHAR, output is of CSTRING.
 Not international or non-ascii friendly.
================================================================= */
char* EXPORT fn_lower_c (ARG(char*, s))
ARGLIST(char *s)			/* VARCHAR input */
{
	char *buf;
	short length = 0;

	char *buffer = (char *)malloc(256);

	length = (short)*s;
	s += 2;
	buf = buffer;
	while (*s)
		if (*s >= 'A' && *s <= 'Z')
			*buf++ = *s++ - 'A' + 'a';
		else
			*buf++ = *s++;

	*buf = '\0';
	buffer [length] = '\0';

	return buffer;
}

/*===============================================================
 fn_strcat(s1, s2) - Returns concatenated string. 
	s1 and s2 are varchar to get a length count
================================================================= */

char* EXPORT fn_strcat(ARG(char*, s1), ARG(char*, s2))
ARGLIST(char *s1)
ARGLIST(char *s2)
{
        short j = 0;
	short length1, length2;
	char *p;

	char *buffer2 = (char *)malloc(512);
	
	length1 = (short)*s1;
	length2 = (short)*s2;

	s1 += 2;
	s2 += 2;

	/* strip trailing blanks of s1 */
	p = s1 + length1 - 1;
	while (*p  && *p == ' ')
		p--;
	p++;
	*p = '\0';

	p = buffer2;
	while (*s1)
		*p++ = *s1++;

	for (j = 0; j < length2; j++)
		if (*s2)
			*p++ = *s2++;

	*p = '\0';
        return buffer2; 
}

/*===============================================================
 fn_substr(s, m, n) - Returns the substr starting m ending n in s. 
================================================================= */
char* EXPORT fn_substr(ARG(char*, s), ARG(short*, m), ARG(short*, n))
ARGLIST(char *s)
ARGLIST(short *m)        /* starting position */
ARGLIST(short *n)        /* ending position */
{
        short i = 0;
	short j = 0;

	char *buffer = (char *)malloc(256);

        if (*m > *n || *m < 1 || *n < 1) return "Bad parameters in substring"; 

        while (*s && i++ < *m-1) /* skip */
                s++;

        while (*s && i++ <= *n)  /* copy */
                buffer[j++] = *s++;
        buffer[j] = '\0';

        return buffer;
}

/*===============================================================
 fn_trim(s) - Returns string that has leading blanks trimmed. 
================================================================= */
char* EXPORT fn_trim(ARG(char*, s))
ARGLIST(char *s)
{
	short j = 0;

	char *buffer = (char *)malloc(256);

        while (*s == ' ')       /* skip leading blanks */
                s++;

        while (*s)		/* copy the rest */
                buffer[j++] = *s++;

        buffer[j] = '\0';

        return buffer;
}

/*===============================================================
 fn_trunc(s, m) - Returns the string truncated at position m; 
 Input is of CSTRING, output is of VARCHAR.
================================================================= */
char* EXPORT fn_trunc(ARG(char*, s), ARG(short*, m))
ARGLIST(char *s)
ARGLIST(short *m)
{
	short j = 2;	/* leave 1st 2 bytes for VARCHAR output */

	char *buffer = (char *)malloc(256);

	while (*s && j < *m + 2)	/* need to add 2 */
		buffer[j++] = *s++;

	buffer[j] = '\0';

	buffer[0] = (unsigned short) strlen(s) + 2;
	buffer[1] = ' ';	/* anything other than \0 */

/*
	*((unsigned short *)buffer) = (unsigned short) (strlen(buffer) + 2);
*/

        return buffer;		/* VARCHAR output */
}


/* ==============================================================
   fn_doy() return the nth day of the year, by value.
   ============================================================== */
long EXPORT fn_doy()
{
	char buf[4];	/* for day */
	long i;

        time (&time_sec);
        tbuf = localtime(&time_sec);

	i = strftime(buf, 4, "%j", tbuf);
	return atoi (buf);
}

/* ==============================================================
   fn_moy() return the nth month of the year. e.g. 1,2,3,...12.
   Return by reference.
   ============================================================== */
short* EXPORT fn_moy()
{
        time (&time_sec);
        tbuf = localtime(&time_sec);
	r_short = (short) tbuf->tm_mon + 1;

	return &r_short;
}

/* ==============================================================
   fn_dow() return the day of today. e.g., Monday, Friday. ...
   ============================================================== */
char* EXPORT fn_dow()
{
        time (&time_sec);
        tbuf = localtime(&time_sec);

        switch (tbuf->tm_wday) {
        case 1: return "Monday";
        case 2: return "Tuesday";
        case 3: return "Wednesday";
        case 4: return "Thursday";
        case 5: return "Friday";
        case 6: return "Saturday";
        case 7: return "Sunday";
        default: return "Error in Date";
        }
}
 
/*===============================================================
 fn_sysdate() - Returns the system date in the "MMM-DD-YYYY" format.
================================================================= */
char* EXPORT fn_sysdate()
{
        short len, i, j = 0;
 
        char *time_str;

	char *datebuf = (char *)malloc(12);
 
        time (&time_sec);
        time_str = ctime (&time_sec);
        len = strlen(time_str);

        for (i = 4; i <= 10; i++) {
                if (*(time_str + i) != ' ')
                        datebuf[j++] = *(time_str+i);
                else if (i == 7 || i == 10)
                        datebuf[j++] = '-';
                else
                        datebuf[j++] = '0';
        }
        for (i = 20; i < len-1 ; i++)
                datebuf[j++] = *(time_str+i);
 
        datebuf[j] = '\0';
        return datebuf;
}
 


/* ==============================================
  fn_add2 (a, b) - returns a + b
  =============================================== */

long EXPORT fn_add2 (ARG(long*, a), ARG(long*, b))
ARGLIST(long *a)
ARGLIST(long *b)
{
	return (*a + *b);
}


/* ==================================================
  fn_mul (a, b) - returns a * b
 =================================================== */

double EXPORT fn_mul (ARG(double*, a), ARG(double*, b))
ARGLIST(double *a)
ARGLIST(double *b)
{
	return (*a * *b);
}


/* ==============================================
  fn_fact (n) - return factorial of n
 ================================================ */

double EXPORT fn_fact (ARG(double*, n))
ARGLIST(double *n)
{
	double k;

	if (*n > 100) return BADVAL;
        if (*n < 0) return BADVAL;   

        if (*n == 0) return 1L;

        else {
		k = *n - 1L;
		return (*n * fn_fact(&k));
	}
}

/*===============================================================
 fn_abs() - returns the absolute value of its argument.
================================================================= */
double EXPORT fn_abs(ARG(double*, x))
ARGLIST(double	*x)
{
	return (*x < 0.0) ? -*x : *x;
}


/*===============================================================
 fn_max() - Returns the greater of its two arguments
================================================================ */
double EXPORT fn_max(ARG(double*, a), ARG(double*, b))
ARGLIST(double	*a)
ARGLIST(double	*b)
{      
	return  (*a > *b) ? *a : *b;
}



/*===============================================================
 fn_sqrt() - Returns square root of n
================================================================ */
double* EXPORT fn_sqrt(ARG(double*, n))
ARGLIST(double *n)
{
	r_double = sqrt(*n);
	return &r_double;
} 




/*=============================================================
 fn_blob_linecount() returns the number of lines in a blob 
  =============================================================*/

long EXPORT fn_blob_linecount (ARG(BLOB, b))
ARGLIST(BLOB b)
{
        char *buf, *p;
	short length, actual_length;

	/* Null values */
	if (!b->blob_handle)
		return 0L;

	length = b->blob_max_segment + 1L;
	buf = (char *) malloc (length); 
 
	r_long = 0;
        while ((*b->blob_get_segment) (b->blob_handle, buf, length, &actual_length)) 
		{
		buf [actual_length] = 0;
		p = buf;
                while (*p)  
			if (*p++  == '\n')
				r_long++;
		}

        free (buf); 
        return r_long;  
}

/*=============================================================
 fn_blob_bytecount() returns the number of bytes in a blob 
 do not count newlines, so get rid of the newlines. 
 ==============================================================*/

long EXPORT fn_blob_bytecount (ARG(BLOB, b))
ARGLIST(BLOB b)
{
	/* Null values */
	if (!b->blob_handle)
		return 0L;

        return (b->blob_total_length - fn_blob_linecount(b));  
}


 
/*=============================================================
 fn_substr_blob() returns portion of TEXT blob beginning at m th
 character and ended at n th character. 
 Newlines are eliminated to make for better printing.
  =============================================================*/
 
char* EXPORT fn_blob_substr(ARG(BLOB, b), ARG(long*, m), ARG(long*, n))
ARGLIST(BLOB b)
ARGLIST(long *m)
ARGLIST(long *n)
{
	char *buf, *p, *q;
	long i = 0;
	long curr_bytecount = 0;
        long begin, end; 
	short length, actual_length;
 
	char *buffer = (char *)malloc(256);

	if (!b->blob_handle)
		return "<null>";
	length = b->blob_max_segment + 1L;
	buf = (char *) malloc (length); 


        if (*m > *n || *m < 1L || *n < 1L) 
		return "";
	if (b->blob_total_length < (long)*m) 
		return "";

	begin = *m;				/* beginning position */

	if (b->blob_total_length < (long)*n) 
		end = b->blob_total_length;	/* ending position */
	else
		end = *n;

	/* Limit the return string to 255 bytes */
	if (end - begin + 1L > 255L)
		end = begin + 254L;
	q = buffer;

        while ((*b->blob_get_segment) (b->blob_handle, buf, length, 
		&actual_length))
		{    
		buf [actual_length] = 0;

		p = buf;
		while (*p && (curr_bytecount <= end))
			{
			curr_bytecount++;
			if (*p == '\n')
				*p = ' ';
			if (curr_bytecount >= begin)
				*q++ = *p;
			p++;
			}
		if (curr_bytecount >= end) 
			{
			*q = 0;
			break;
			}
		}
 
	free (buf);
        return buffer;
}

