/*-------------------------------------------------------------------------
 *
 * regexp.c--
 *    regular expression handling code.
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    /usr/local/devel/pglite/cvs/src/backend/utils/adt/regexp.c,v 1.8 1995/07/21 17:49:36 jolly Exp
 *
 *
 *   NOTES
 *	Uses the old AT&T compile/step code.  While this is not very
 *	politically-correct (it has been removed from 4.x BSD) it is
 *	the most portable C library solution since everyone names their
 *	own regular expression routines something different (applying
 *	different standards)...
 *
 *-------------------------------------------------------------------------
 */
#include <string.h>
#include "postgres.h"		/* postgres system include file */
#include "utils/elog.h"		/* for logging postgres errors */
#include "utils/palloc.h"
#include "utils/builtins.h"	/* where the function declarations go */

/*
 *  macros to support the regexp(3) library calls
 */
#define INIT		register char *p = instring;
#define GETC()		(*p++)
#define PEEKC()		*(p+1)
#define	UNGETC(c)	(*--p = (c))
#define	RETURN(v)	return(v)
#define	ERROR(val)	elog(WARN, "regexp library reports error %d", (val));

#ifndef __linux__
#define	EXPBUFSZ	256
#else
#define EXPBUFSZ (sizeof (regex_t) + 4)
#endif
#define	P2CHARLEN	2
#define	P4CHARLEN	4
#define	P8CHARLEN	8
#define	P16CHARLEN	16

#if defined(DISABLE_XOPEN_NLS)
#undef _XOPEN_SOURCE
#endif /* DISABLE_XOPEN_NLS */

#ifndef WIN32

/* distinguish using regex.h and regexp.h */
#if defined(PORTNAME_BSD44_derived) || defined(PORTNAME_linux)
#define USE_REGEX
#include <sys/types.h>
#include <regex.h>
#ifndef REG_BASIC
#define REG_BASIC 0
#endif /* REG_BASIC */
#else
#include <regexp.h>
#endif 

#endif /* WIN32 why is this necessary? */

/*
 *  interface routines called by the function manager
 */

/*
 *  routines that use the regexp stuff
 */
bool char2regexeq(uint16 arg1, struct varlena *p)
{
    char *expbuf, *endbuf;
    char *sterm, *pterm;
    int result;
    char *s = (char *) &arg1;
    
    if (!s || !p)
	return FALSE;
    
#if !defined(USE_REGEX)
    expbuf = (char *) palloc(EXPBUFSZ);
    endbuf = expbuf + (EXPBUFSZ - 1);
#endif
    
    /* be sure sterm is null-terminated */
    sterm = (char *) palloc(P2CHARLEN + 1);
    memset(sterm, 0, P2CHARLEN + 1);
    strncpy(sterm, s, P2CHARLEN);
    
    /* p is a text = varlena, not a string so we have to make 
     * a string from the vl_data field of the struct. */
    
    /* palloc the length of the text + the null character */
    pterm = (char *) palloc(p->vl_len - sizeof(int32) + 1);
    memmove(pterm, p->vl_dat, p->vl_len - sizeof(int32));
    *(pterm + p->vl_len - sizeof(int32)) = (char)NULL;
    
#if defined(USE_REGEX)
    {
	regex_t 	re;

	(void) regcomp(&re, pterm, REG_BASIC);
	result = (regexec(&re, sterm, 0, (regmatch_t *) NULL, 0) == 0);
	regfree(&re);
    }
#else
    /* compile the re */
    (void) compile(pterm, expbuf, endbuf, 0);
    
    /* do the regexp matching */
    result = step(sterm, expbuf);
    
    pfree(expbuf);
#endif /* USE_REGEX */

    pfree(sterm);
    pfree(pterm);
    
    return ((bool) result);
}

bool char2regexne(uint16 arg1, struct varlena *p)
{
    return (!char2regexeq(arg1, p));
}

bool char4regexeq(uint32 arg1, struct varlena *p)
{
    char *expbuf, *endbuf;
    char *sterm, *pterm;
    int result;
    char *s = (char *) &arg1;
    
    if (!s || !p)
	return FALSE;
    
#if !defined(USE_REGEX)
    expbuf = (char *) palloc(EXPBUFSZ);
    endbuf = expbuf + (EXPBUFSZ - 1);
#endif
    
    /* be sure sterm is null-terminated */
    sterm = (char *) palloc(P4CHARLEN + 1);
    memset(sterm, 0, P4CHARLEN + 1);
    strncpy(sterm, s, P4CHARLEN);
    
    /* p is a text = varlena, not a string so we have to make 
     * a string from the vl_data field of the struct. */
    
    /* palloc the length of the text + the null character */
    pterm = (char *) palloc(p->vl_len - sizeof(int32) + 1);
    memmove(pterm, p->vl_dat, p->vl_len - sizeof(int32));
    *(pterm + p->vl_len - sizeof(int32)) = (char)NULL;
    
#if defined(USE_REGEX)
    {
	regex_t 	re;

	(void) regcomp(&re, pterm, REG_BASIC);
	result = (regexec(&re, sterm, 0, (regmatch_t *) NULL, 0) == 0);
	regfree(&re);
    }
#else
    /* compile the re */
    (void) compile(pterm, expbuf, endbuf, 0);
    
    /* do the regexp matching */
    result = step(sterm, expbuf);
    pfree(expbuf);
#endif
    
    pfree(sterm);
    pfree(pterm);
    
    return ((bool) result);
}

bool char4regexne(uint32 arg1, struct varlena *p)
{
    return (!char4regexeq(arg1, p));
}

bool char8regexeq(char *s, struct varlena *p)
{
    char *expbuf, *endbuf;
    char *sterm, *pterm;
    int result;
    
    if (!s || !p)
	return FALSE;
    
#if !defined(USE_REGEX)
    expbuf = (char *) palloc(EXPBUFSZ);
    endbuf = expbuf + (EXPBUFSZ - 1);
#endif
    
    /* be sure sterm is null-terminated */
    sterm = (char *) palloc(P8CHARLEN + 1);
    memset(sterm, 0, P8CHARLEN + 1);
    strncpy(sterm, s, P8CHARLEN);
    
    /* p is a text = varlena, not a string so we have to make 
     * a string from the vl_data field of the struct. */
    
    /* palloc the length of the text + the null character */
    pterm = (char *) palloc(p->vl_len - sizeof(int32) + 1);
    memmove(pterm, p->vl_dat, p->vl_len - sizeof(int32));
    *(pterm + p->vl_len - sizeof(int32)) = (char)NULL;
    
#if defined(USE_REGEX)
    {
	regex_t 	re;

	(void) regcomp(&re, pterm, REG_BASIC);
	result = (regexec(&re, sterm, 0, (regmatch_t *) NULL, 0) == 0);
	regfree(&re);
    }
#else
    /* compile the re */
    (void) compile(pterm, expbuf, endbuf, 0);
    
    /* do the regexp matching */
    result = step(sterm, expbuf);
    pfree(expbuf);
#endif
    
    pfree(sterm);
    pfree(pterm);
    
    return ((bool) result);
}

bool char8regexne(char *s, struct varlena *p)
{
    return (!char8regexeq(s, p));
}

bool char16regexeq(char *s, struct varlena *p)
{
    char *expbuf, *endbuf;
    char *sterm, *pterm;
    int result;
    
    if (!s || !p)
	return FALSE;
    
#if !defined(USE_REGEX)
    expbuf = (char *) palloc(EXPBUFSZ);
    endbuf = expbuf + (EXPBUFSZ - 1);
#endif
    
    /* be sure sterm is null-terminated */
    sterm = (char *) palloc(P16CHARLEN + 1);
    memset(sterm, 0, P16CHARLEN + 1);
    strncpy(sterm, s, P16CHARLEN);
    
    /* p is a text = varlena, not a string so we have to make 
     * a string from the vl_data field of the struct. */
    
    /* palloc the length of the text + the null character */
    pterm = (char *) palloc(p->vl_len - sizeof(int32) + 1);
    memmove(pterm, p->vl_dat, p->vl_len - sizeof(int32));
    *(pterm + p->vl_len - sizeof(int32)) = (char)NULL;
    
#if defined(USE_REGEX)
    {
	regex_t 	re;

	(void) regcomp(&re, pterm, REG_BASIC);
	result = (regexec(&re, sterm, 0, (regmatch_t *) NULL, 0) == 0);
	regfree(&re);
    }
#else
    /* compile the re */
    (void) compile(pterm, expbuf, endbuf, 0);
    
    /* do the regexp matching */
    result = step(sterm, expbuf);
    pfree(expbuf);
#endif /*USE_REGEX */
    
    pfree(sterm);
    pfree(pterm);
    
    return ((bool) result);
}

bool char16regexne(char *s, struct varlena *p)
{
    return (!char16regexeq(s, p));
}

bool textregexeq(struct varlena *s, struct varlena *p)
{
    char *expbuf, *endbuf;
    char *sbuf, *pbuf;
    int result;
    
    if (!s || !p)
	return FALSE;
    
    /* ---------------
     * text is a varlena, not a string so we have to make 
     * a string from the vl_data field of the struct. 
     * jeff 13 July 1991
     * ---------------
     */
    
    /* palloc the length of the text + the null character */
    sbuf = (char *) palloc(s->vl_len - sizeof(int32) + 1);
    pbuf = (char *) palloc(p->vl_len - sizeof(int32) + 1);
    
#if !defined(USE_REGEX)
    expbuf = (char *) palloc(EXPBUFSZ);
    endbuf = expbuf + (EXPBUFSZ - 1);
#endif
    
    memmove(sbuf, s->vl_dat, s->vl_len - sizeof(int32));
    memmove(pbuf, p->vl_dat, p->vl_len - sizeof(int32));
    *(sbuf + s->vl_len - sizeof(int32)) = (char)NULL;
    *(pbuf + p->vl_len - sizeof(int32)) = (char)NULL;
    
    
#if defined(USE_REGEX)
    {
	regex_t 	re;

	(void) regcomp(&re, pbuf, REG_BASIC);
	result = (regexec(&re, sbuf, 0, (regmatch_t *) NULL, 0) == 0);
	regfree(&re);
    }
#else
    /* compile the re */
    (void) compile(pbuf, expbuf, endbuf, 0);
    
    /* do the regexp matching */
    result = step(sbuf, expbuf);
    pfree(expbuf);
#endif /* USE_REGEX */
    
    pfree(sbuf);
    pfree(pbuf);
    
    return ((bool) result);
}

bool textregexne(struct varlena *s, struct varlena *p)
{
    return (!textregexeq(s, p));
}
