/*-------------------------------------------------------------------------
 *
 * quote.c
 *	  Functions for quoting identifiers and literals
 *
 * Portions Copyright (c) 2000, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/quote.c,v 1.3 2001/01/24 19:43:14 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <ctype.h>

#include "postgres.h"

#include "mb/pg_wchar.h"
#include "utils/builtins.h"


static bool quote_ident_required(text *iptr);
static text *do_quote_ident(text *iptr);
static text *do_quote_literal(text *iptr);


/*
 * quote_ident -
 *	  returns a properly quoted identifier
 */
Datum
quote_ident(PG_FUNCTION_ARGS)
{
	text	   *t = PG_GETARG_TEXT_P(0);
	text	   *result;

	if (quote_ident_required(t))
	{
	    result = do_quote_ident(t);
	}
	else
	{
		result = (text *)palloc(VARSIZE(t));
		memcpy(result, t, VARSIZE(t));
	}

	PG_FREE_IF_COPY(t, 0);

	PG_RETURN_TEXT_P(result);
}

/*
 * quote_literal -
 *	  returns a properly quoted literal
 */
Datum
quote_literal(PG_FUNCTION_ARGS)
{
	text	   *t = PG_GETARG_TEXT_P(0);
	text	   *result;

	result = do_quote_literal(t);

	PG_FREE_IF_COPY(t, 0);

	PG_RETURN_TEXT_P(result);
}


/*
 * MULTIBYTE dependant internal functions follow
 *
 */


#ifndef MULTIBYTE

/* Check if a given identifier needs quoting */
static bool
quote_ident_required(text *iptr)
{
    char   *cp;
	char   *ep;

	cp = VARDATA(iptr);
    ep = VARDATA(iptr) + VARSIZE(iptr) - VARHDRSZ;

	if (cp >= ep)
		return true;

    if (!(*cp == '_' || (*cp >= 'a' && *cp <= 'z')))
		return true;

	while((++cp) < ep)
	{
	    if (*cp >= 'a' && *cp <= 'z') continue;
		if (*cp >= '0' && *cp <= '9') continue;
		if (*cp == '_') continue;

		return true;
	}

	return false;
}

/* Return a properly quoted identifier */
static text *
do_quote_ident(text *iptr)
{
    text   *result;
	char   *cp1;
	char   *cp2;
	int		len;

    len = VARSIZE(iptr) - VARHDRSZ;
	result = (text *)palloc(len * 2 + VARHDRSZ + 2);

	cp1 = VARDATA(iptr);
	cp2 = VARDATA(result);

	*cp2++ = '"';
	while(len-- > 0)
	{
	    if (*cp1 == '"')
			*cp2++ = '"';
	    if (*cp1 == '\\')
			*cp2++ = '\\';
	    *cp2++ = *cp1++;
	}
	*cp2++ = '"';

	VARATT_SIZEP(result) = cp2 - ((char *)result);

	return result;
}

/* Return a properly quoted literal value */
static text *
do_quote_literal(text *lptr)
{
    text   *result;
	char   *cp1;
	char   *cp2;
	int		len;

    len = VARSIZE(lptr) - VARHDRSZ;
	result = (text *)palloc(len * 2 + VARHDRSZ + 2);

	cp1 = VARDATA(lptr);
	cp2 = VARDATA(result);

	*cp2++ = '\'';
	while(len-- > 0)
	{
	    if (*cp1 == '\'')
			*cp2++ = '\'';
	    if (*cp1 == '\\')
			*cp2++ = '\\';
	    *cp2++ = *cp1++;
	}
	*cp2++ = '\'';

	VARATT_SIZEP(result) = cp2 - ((char *)result);

	return result;
}

#else

/* Check if a given identifier needs quoting (MULTIBYTE version) */
static bool
quote_ident_required(text *iptr)
{
    char   *cp;
	char   *ep;

	cp = VARDATA(iptr);
    ep = VARDATA(iptr) + VARSIZE(iptr) - VARHDRSZ;

	if (cp >= ep)
		return true;

	if(pg_mblen(cp) != 1)
		return true;
    if (!(*cp == '_' || (*cp >= 'a' && *cp <= 'z')))
		return true;

	while((++cp) < ep)
	{
		if (pg_mblen(cp) != 1)
			return true;

	    if (*cp >= 'a' && *cp <= 'z') continue;
		if (*cp >= '0' && *cp <= '9') continue;
		if (*cp == '_') continue;

		return true;
	}

	return false;
}

/* Return a properly quoted identifier (MULTIBYTE version) */
static text *
do_quote_ident(text *iptr)
{
    text   *result;
	char   *cp1;
	char   *cp2;
	int		len;
	int		wl;

    len = VARSIZE(iptr) - VARHDRSZ;
	result = (text *)palloc(len * 2 + VARHDRSZ + 2);

	cp1 = VARDATA(iptr);
	cp2 = VARDATA(result);

	*cp2++ = '"';
	while(len > 0)
	{
		if ((wl = pg_mblen(cp1)) != 1)
		{
			len -= wl;

			while(wl-- > 0)
				*cp2++ = *cp1++;
			continue;
		}

	    if (*cp1 == '"')
			*cp2++ = '"';
	    if (*cp1 == '\\')
			*cp2++ = '\\';
	    *cp2++ = *cp1++;

		len--;
	}
	*cp2++ = '"';

	VARATT_SIZEP(result) = cp2 - ((char *)result);

	return result;
}

/* Return a properly quoted literal value (MULTIBYTE version) */
static text *
do_quote_literal(text *lptr)
{
    text   *result;
	char   *cp1;
	char   *cp2;
	int		len;
	int		wl;

    len = VARSIZE(lptr) - VARHDRSZ;
	result = (text *)palloc(len * 2 + VARHDRSZ + 2);

	cp1 = VARDATA(lptr);
	cp2 = VARDATA(result);

	*cp2++ = '\'';
	while(len > 0)
	{
		if ((wl = pg_mblen(cp1)) != 1)
		{
			len -= wl;

			while(wl-- > 0)
				*cp2++ = *cp1++;
			continue;
		}

	    if (*cp1 == '\'')
			*cp2++ = '\'';
	    if (*cp1 == '\\')
			*cp2++ = '\\';
	    *cp2++ = *cp1++;

		len--;
	}
	*cp2++ = '\'';

	VARATT_SIZEP(result) = cp2 - ((char *)result);

	return result;
}

#endif


