/*-------------------------------------------------------------------------
 *
 * like.c--
 *    like expression handling code.
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    /usr/local/devel/pglite/cvs/src/backend/utils/adt/like.c,v 1.1 1995/07/30 23:55:36 emkxp01 Exp
 *
 *
 *   NOTES
 *	A big hack of the regexp.c code!! Contributed by
 *	Keith Parks <emkxp01@mtcc.demon.co.uk> (7/95).
 *
 *
 *-------------------------------------------------------------------------
 */
#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 */

#define	P2CHARLEN	2
#define	P4CHARLEN	4
#define	P8CHARLEN	8
#define	P16CHARLEN	16

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

bool char2like(uint16 arg1, struct varlena *p)
{
    char *sterm, *pterm;
    int result;
    char *s = (char *) &arg1;
    
    if (!s || !p)
	return FALSE;
    
    /* 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;
    
    /* do the regexp matching */
    result = like(sterm, pterm);
    
    pfree(sterm);
    pfree(pterm);
    
    return ((bool) result);
}

bool char2nlike(uint16 arg1, struct varlena *p)
{
    return (!char2like(arg1, p));
}

bool char4like(uint32 arg1, struct varlena *p)
{
    char *sterm, *pterm;
    int result;
    char *s = (char *) &arg1;
    
    if (!s || !p)
	return FALSE;
    
    /* 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)) = '\0';
    
    /* do the regexp matching */
    result = like(sterm, pterm);
    
    pfree(sterm);
    pfree(pterm);
    
    return ((bool) result);
}

bool char4nlike(uint32 arg1, struct varlena *p)
{
    return (!char4like(arg1, p));
}

bool char8like(char *s, struct varlena *p)
{
    char *sterm, *pterm;
    int result;
    
    if (!s || !p)
	return FALSE;
    
    /* 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)) = '\0';
    
    /* do the regexp matching */
    result = like(sterm, pterm);
    
    pfree(sterm);
    pfree(pterm);
    
    return ((bool) result);
}

bool char8nlike(char *s, struct varlena *p)
{
    return (!char8like(s, p));
}

bool char16like(char *s, struct varlena *p)
{
    char *sterm, *pterm;
    int result;
    
    if (!s || !p)
	return FALSE;
    
    /* 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)) = '\0';
    
    /* do the regexp matching */
    result = like(sterm, pterm);
    
    pfree(sterm);
    pfree(pterm);
    
    return ((bool) result);
}

bool char16nlike(char *s, struct varlena *p)
{
    return (!char16like(s, p));
}

bool textlike(struct varlena *s, struct varlena *p)
{
    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);
    
    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)) = '\0';
    *(pbuf + p->vl_len - sizeof(int32)) = '\0';
    
    
    /* do the regexp matching */
    result = like(sbuf, pbuf);
    
    pfree(sbuf);
    pfree(pbuf);
    
    return ((bool) result);
}

bool textnlike(struct varlena *s, struct varlena *p)
{
    return (!textlike(s, p));
}


/*  1.1
**  "like.c" A first attempt at a LIKE operator for Postgres95.
**
**  Originally written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
**  Rich $alz is now <rsalz@bbn.com>.
**  Special thanks to Lars Mathiesen <thorinn@diku.dk> for the LABORT code.
** 
**  This code was shamelessly stolen from the "pql" code by myself and
**  slightly modified :)
** 
**  All references to the word "star" were replaced by "percent"
**  All references to the word "wild" were replaced by "like"
** 
**  All the nice shell RE matching stuff was replaced by just "_" and "%"
** 
**  As I don't have a copy of the SQL standard handy I wasn't sure whether
**  to leave in the '\' escape character handling. (I suspect the standard
**  handles "%%" as a single literal percent)
**
**  Keith Parks. <keith@mtcc.demon.co.uk>
**
**  [SQL92 lets you specify the escape character by saying
**   LIKE <pattern> ESCAPE <escape character>. We are a small operation
**   so we force you to use '\'. - ay 7/95]
**
*/

#define LIKE_TRUE			1
#define LIKE_FALSE			0
#define LIKE_ABORT			-1

/*
**  Match text and p, return LIKE_TRUE, LIKE_FALSE, or LIKE_ABORT.
*/
static int
DoMatch(register char *text, register char *p)
{
    register int	matched;

    for ( ; *p; text++, p++) {
	if (*text == '\0' && *p != '%')
	    return LIKE_ABORT;
	switch (*p) {
	case '\\':
	    /* Literal match with following character. */
	    p++;
	    /* FALLTHROUGH */
	default:
	    if (*text != *p)
		return LIKE_FALSE;
	    continue;
	case '_':
	    /* Match anything. */
	    continue;
	case '%':
	    while (*++p == '%')
		/* Consecutive percents act just like one. */
		continue;
	    if (*p == '\0')
		/* Trailing percent matches everything. */
		return LIKE_TRUE;
	    while (*text)
		if ((matched = DoMatch(text++, p)) != LIKE_FALSE)
		    return matched;
	    return LIKE_ABORT;
	}
    }

    return *text == '\0';
}


/*
**  User-level routine.  Returns TRUE or FALSE.
*/
int
like(char *text, char *p)
{
    if (p[0] == '%' && p[1] == '\0')
	return TRUE;
    return (DoMatch(text, p) == LIKE_TRUE);
}
