/*	varchar.c	- 
**
**
** Copyright (c) 1996  Hughes Technologies Pty Ltd
**
** Permission to use, copy, and distribute for non-commercial purposes,
** is hereby granted without fee, providing that the above copyright
** notice appear in all copies and that both the copyright notice and this
** permission notice appear in supporting documentation.
**
** The software may be modified for your own purposes, but modified versions
** may not be distributed.
**
** This software is provided "as is" without any expressed or implied warranty.
**
** ID = "$Id:"
**
*/


#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <time.h>

#ifdef HAVE_DIRENT_H
#  include <dirent.h>
#endif

#ifdef HAVE_SYS_DIR_H
#  include <sys/dir.h>
#endif

#ifdef WIN32
#  include <winsock.h>
#endif

#include <common/debug.h>
#include <common/site.h>
#include <common/portability.h>
#include <regexp/regexp.h>


#if defined(OS2) || defined(WIN32)
#  include "msql_yacc.h"
#else
#  include "y.tab.h"
#endif

#define _MSQL_SERVER_SOURCE
#include "msql_priv.h"
#include "msql.h"
#include "errmsg.h"

#define REG     register
extern	char    errMsg[];




/****************************************************************************
**      _
**
**      Purpose : 
**      Args    : 
**      Returns : 
**      Notes   :
*/

u_int readOverflowFreeList(cacheEntry,pos)
	cache_t	*cacheEntry;
	u_int	pos;
{
	char	*cp;
	u_int	*next;

	/*
	** Note, skip the leading freelist header
	*/
	cp = ((char *)cacheEntry->overflowMap) + sizeof(u_int) +
		(pos * (OFB_SIZE + sizeof(u_int)));
	next = (u_int *)cp;
	return(*next);
}


int writeOverflowFreeList(cacheEntry,pos,value)
	cache_t	*cacheEntry;
	u_int	pos,
		value;
{
	char	*cp;

	cp = ((char *)cacheEntry->overflowMap) + sizeof(u_int) +
		(pos * (OFB_SIZE + sizeof(u_int)));
	bcopy(&value, cp, sizeof(u_int));
	return(0);
}



u_int popOverflowPos(cacheEntry)
        cache_t *cacheEntry;
{
        u_int   pos,
		*posPtr;

        msqlTrace(TRACE_IN,"popOverflowPos()");
        posPtr = (u_int *)cacheEntry->overflowMap;
        if (!posPtr)
                return(NO_POS);
	pos = *posPtr;
        if (pos != NO_POS)
        {
                *posPtr = readOverflowFreeList(cacheEntry,pos);
        }
        return(pos);
}





int pushOverflowPos(cacheEntry, pos)
        cache_t *cacheEntry;
        u_int   pos;
{
	u_int	*posPtr;

        msqlTrace(TRACE_IN,"pushOverflowPos()");

        posPtr = (u_int *)cacheEntry->overflowMap;
        if ((*posPtr = NO_POS))
        {
                *posPtr = pos;
        }
        else
        {
		if (writeOverflowFreeList(cacheEntry,*posPtr,pos) < 0)
		{
                        return(-1);
                }
        }
        if (writeOverflowFreeList(cacheEntry,pos,NO_POS) < 0)
        {
                return(-1);
        }
        return(0);
}



u_int writeOverflow(entry, pos, lastPos, data, length)
	cache_t	*entry;
	u_int	pos,
		lastPos;
	u_char	*data;
	int	length;
{
	u_char	*cp;
	off_t	offset;
	static  u_char	buf[OFB_SIZE + sizeof(u_int)];

	/*
	** Check the mapping
	*/
	if (pos != NO_POS)
	{
		cp = (u_char *)entry->overflowMap + sizeof(u_int) +
			(pos * (OFB_SIZE + sizeof(u_int)));
		bcopy(&lastPos, cp, sizeof(u_int));
		bcopy(data, cp + sizeof(u_int), length);
	}
	else
	{
		lseek(entry->overflowFD, 0L, SEEK_END);
		offset = lseek(entry->overflowFD, 0L, SEEK_CUR);
		bzero(buf,sizeof(buf));
		cp = buf;
		bcopy(&lastPos,cp,sizeof(u_int));
		bcopy(data, cp + sizeof(u_int), length);
		write(entry->overflowFD,buf, sizeof(buf));
		entry->remapOverflow = 1;
		pos = (offset - sizeof(u_int)) / (OFB_SIZE + sizeof(u_int));
	}
	return(pos);
}


void readOverflow(entry, pos, nextPos, buf, numBytes)
	cache_t	*entry;
	u_int	pos,
		*nextPos;
	u_char	*buf;
	int	numBytes;
{
	u_char	*cp;


	cp = (u_char *)entry->overflowMap + sizeof(u_int) + 
		(pos * (OFB_SIZE + sizeof(u_int)));
	bcopy(cp, nextPos, sizeof(u_int));
	bcopy(cp + sizeof(u_int), buf, numBytes);

	/* RNS
	 * Filling past the end is not needed, so we no longer do it.
	 * This also means we can save the storage space in the other
	 * routines' buf* variables.
	 * *(buf + numBytes) = 0;
	 */
}

/* RNS
 * nextOverflow -- just find the next overflow buffer in the chain.
 * NOTE:  The caller is responsible to call this routine *only* when
 * there is a next buffer.  See, for example, deleteVarChar.
 * NOTE:  This is basically readOverflow without the data transfer.
 */
void nextOverflow(entry, pos, nextPos)
	cache_t	*entry;
	u_int	pos,
		*nextPos;
{
	u_char	*cp;


	cp = (u_char *)entry->overflowMap + sizeof(u_int) + 
		(pos * (OFB_SIZE + sizeof(u_int)));
	bcopy(cp, nextPos, sizeof(u_int));
}


u_int writeVarChar(entry, data, length)
	cache_t	*entry;
	u_char	*data;
	int	length;
{
	u_char	*cp;
	int	remain,
		numBytes;
	u_int	pos,
		lastPos;

	remain = strlen((char *)data) - length;
	numBytes = remain % OFB_SIZE;
	lastPos = NO_POS;
	while(remain)
	{
		pos = popOverflowPos(entry);
		cp = data + length + remain - numBytes;
		pos = writeOverflow(entry, pos, lastPos, cp, numBytes);
                msqlDebug(MOD_TEXT,"Wrote %d bytes at overflow %d\n",
			numBytes, pos);
		remain -= numBytes;
		numBytes = OFB_SIZE;
		lastPos = pos;
	}
	return(lastPos);
}




u_char *readVarChar(entry, data, fieldLen)
	cache_t	*entry;
	u_char	*data;
	int	fieldLen;
{
	u_char	*value,
		*cp;
	u_int	pos,
		nextPos;
	int	length,
		numBytes;

	bcopy(data,&length, sizeof(int));
	if (length == 0)
		return((u_char *)strdup(""));
	bcopy(data + sizeof(int) ,&pos, sizeof(u_int));
	value = (u_char *)malloc(length + 1);
	if (!value)
	{
		return(NULL);
	}
	cp = value;
	if (fieldLen > length)
		numBytes = length;
	else
		numBytes = fieldLen;
	bcopy(data + sizeof(int)+ sizeof(u_int), cp, numBytes);
	cp += numBytes;
	length -= numBytes;
	while(pos != NO_POS)
	{
		if (length > OFB_SIZE)
			numBytes = OFB_SIZE;
		else
			numBytes = length;
		readOverflow(entry, pos, &nextPos, cp, numBytes);
                msqlDebug(MOD_TEXT,"Read %d bytes at overflow %d\n",
			numBytes, pos);
		pos = nextPos;
		cp += numBytes;
		length -= numBytes;
	}
	/* RNS
	 * malloc is not guaranteed to initialize memory and
	 * varChars (TEXT) are not stored with nul-termination.
	 */
	*cp = '\0';
	return(value);
}



void deleteVarChar(entry, pos)
	cache_t	*entry;
	u_int	pos;
{
	u_int	nextPos;

	while(pos != NO_POS)
	{
		nextOverflow(entry, pos, &nextPos);
		pushOverflowPos(entry, pos);
		pos = nextPos;
	}
}


/* RNS
 * compareVarChar -- used to compare two TEXT fields from same table
 * (the same entry and the same fieldLen).
 */
int compareVarChar(entry, data1, data2, fieldLen)
	cache_t	*entry;
	u_char	*data1,
		*data2;
	int	fieldLen;
{
	u_char	*cp1, 	*cp2,
		buf1[OFB_SIZE],
		buf2[OFB_SIZE];
	int	count1, count2,
		d1Len, 	d2Len,
		segLen1,segLen2;
	u_int	pos1, 	pos2;

	/* RNS Get the lengths of the data */
	bcopy(data1,&d1Len, sizeof(int));
	bcopy(data2,&d2Len, sizeof(int));

	/* RNS Short circuit for both or either of zero length */
	if (d1Len == 0 && d2Len == 0)
		return(0);
	if (d1Len == 0)
		return(-1);
	if (d2Len == 0)
		return(1);

	/* RNS Get overflow buffers, if any */
	bcopy(data1 + sizeof(int) ,&pos1, sizeof(u_int));
	bcopy(data2 + sizeof(int) ,&pos2, sizeof(u_int));

	/* RNS Position pointers to row data to get comparison started */
	cp1 = data1 + sizeof(int) + sizeof(u_int);
	cp2 = data2 + sizeof(int) + sizeof(u_int);

	/* RNS
	 * Comparison is segmented, row followed by overflow buffers.
	 * Lengths need to be watched carefully.
	 */
	segLen1 = count1 = d1Len > fieldLen? fieldLen : d1Len;
	segLen2 = count2 = d2Len > fieldLen? fieldLen : d2Len;

	while(d1Len && d2Len)
	{
		while(count1 && count2)
		{
			/* RNS
			 * I have re-arranged checks because I think (perhaps
			 * erroneously) inequality likely more common.
			 * It also eliminates need for equality comparison.
			 */
			if (*cp1 < *cp2)
				return(-1);
			if (*cp2 < *cp1)
				return(1);
			/* RNS
			 * Here we have identical characters and because
			 * lengths are used, we don't need to bother with
			 * nul character check.
			 */
			count1--;
			count2--;
			cp1++;
			cp2++;
		}

		/* RNS
		 * Check counts for when strings end in same "segment":
		 * We know that at least one has reached the end, but
		 * both must end in order to continue on to another
		 * segment.
		 */
		if (count1) /* d1 is longer */
			return(1);
		if (count2) /* d2 is longer */
			return(-1);

		/* RNS
		 * If both have more buffers (segments), then
		 *   go on to another buffer full,
		 * else
		 *   get out of loop (and compare the lengths).
		 */
		d1Len -= segLen1;
		d2Len -= segLen2;
		if (d1Len == 0 || d2Len == 0)
			break;

		/* RNS Get next d1 buffer */
		readOverflow(entry, pos1, &pos1, buf1,
			d1Len>OFB_SIZE? OFB_SIZE : d1Len);
		cp1 = buf1;
		count1 = segLen1 = d1Len>OFB_SIZE? OFB_SIZE : d1Len;

		/* RNS Get next d2 buffer */
		readOverflow(entry, pos2, &pos2, buf2,
			d2Len>OFB_SIZE? OFB_SIZE : d2Len);
		cp2 = buf2;
		count2 = segLen2 = d2Len>OFB_SIZE? OFB_SIZE : d2Len;
	}

	/* RNS
	 * At least one has reached the end, we use the same check as
	 * during short circuit at beginning.
	 */
	if (d1Len == 0 && d2Len == 0)
		return(0);
	if (d1Len == 0)
		return(-1);
	if (d2Len == 0)
		return(1);
}


/* RNS
 * matchVarChar -- compare (op) a TEXT (entry, data, length) to
 * a nul-terminated string (cp).
 */
int matchVarChar(entry, data, cp, length, op)
	cache_t	*entry;
	u_char	*data;
	char	*cp;
	int	length,
		op;
{
	u_char	*cp1, 	*cp2,
		buf[OFB_SIZE];
	int	count,
		dLen,
		segLen,
		cmp,
		cpLen,
		result;
	u_int	pos;

	cmp = 0; /* current value of match, initially equal */
	bcopy(data,&dLen, sizeof(int)); /* get length of TEXT data */
	cpLen = strlen(cp); /* get length of string */

	if (dLen == 0 || cpLen == 0)
	{
		/* if either or both are zero length,
		 * set cmp so that inequalities work.
		 * Longer string is greater.
		 */
		cmp = dLen - cpLen;
	}
	else
	{
		/* examine individual characters */

		bcopy(data + sizeof(int) ,&pos, sizeof(u_int));

		cp1 = data + sizeof(int) + sizeof(u_int);
		cp2 = (u_char *)cp;
		segLen = count = dLen > length ? length : dLen;
		while(dLen && cpLen)
		{
			while(count && cpLen)
			{
				if ((cmp = *cp1 - *cp2) != 0)
					break;
				count--;
				cp1++;
				cpLen--;
				cp2++;
			}

			/* RNS
			 * If inner loop decided the value,
			 * break this loop keeping value.
			 */
			if (cmp != 0)
				break;

			/* RNS
			 * If TEXT is longer than cp,
			 * artificially set cmp and break
			 */
			if (count)
			{
				cmp = 1;
				break;
			}

			/* RNS
			 * At this point count is 0.
			 * If both TEXT and string have more, then
			 *   go on
			 * else
			 *   get out of loop (and compare the lengths).
			 */
			dLen -= segLen;
			if (dLen == 0 || cpLen == 0)
				break;

			/* RNS Get next buffer of TEXT */
			readOverflow(entry, pos, &pos, buf,
				dLen>OFB_SIZE? OFB_SIZE : dLen);
			cp1 = buf;
			count = segLen = dLen>OFB_SIZE? OFB_SIZE : dLen;
		}

		/* RNS
		 * Either they aren't equal or at least one has reached
		 * the end:
		 * If they are still equal,
		 * then the lengths must be used as the final arbiter.
		 */
		if (cmp == 0)
			cmp = dLen - cpLen;
	}

	switch(op)
	{
		case EQ_OP:
			result = (cmp == 0);
			break;
			
		case NE_OP:
			result = (cmp != 0);
			break;

		case LT_OP:
			result = (cmp < 0);
			break;

		case GT_OP:
			result = (cmp > 0);
			break;

		case LE_OP:
			result = (cmp <= 0);
			break;

		case GE_OP:
			result = (cmp >= 0);
			break;

		default:
			strcpy(errMsg, TEXT_REGEX_ERROR);
			return(-1);
	}
	return(result);
}
