/*-------------------------------------------------------------------------
 *
 * scanner.c--
 *    support routines for the lex scanner.
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    $Header: /usr/local/devel/pglite/cvs/src/backend/parser/scanner.c,v 1.3 1995/05/01 00:12:58 jolly Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <ctype.h>
#include <string.h>
#include "utils/elog.h"
#include "parser/io.h"

#include "c.h"

static int scancon(char *buf, int len);

/*
 *	Scanner error handler.
 */
static void
serror(char str[])
{
    elog(WARN, "*** scanner error: %s\n", str);
}

/*
 *	Support routines for the scanner.
 *	Includes: comment, character constant,
 *	and string constant scanning.
 */

static char delimiter;

void
scanstr(char *buf, int len, char* yytext)
{
    if (*yytext=='\"')
      delimiter = '\"';
    else
      delimiter = '\'';
 
    scancon(buf, len);
}

void
scanspecial(char *buf, int len)
{
    delimiter = '`';
    scancon(buf, len);
}

/*
 * Scan a string.  The leading delimiter (", ') has already been
 * read.  Be sure to gobble up the whole thing, including the
 * trailing delimiter.
 */
static int
scancon(char *buf, int len)
{
    register char *cp = buf;
    register int c, dc, cspec;
    int entering = 1;

    cspec = 0;
    while ((c = input()) != delimiter) {

	if (cp - buf > len - 1) {
	    serror("String/char constant too large");
	    cp = buf;
	}

	switch (c) {
	default:
	    *cp++ = c;
	    break;
	case '{':
	    /*
	     * Right curly brace indicates array constant.
	     */
	    if (entering && delimiter == '\"')
		{
		    scanarr(buf, len);
		    cp += strlen(buf);
		    cspec = cp - buf - 1;
		}
	    else
		*cp++ = c;
	    break;
	case '\n':
	    /* ignore embedded newlines */
	    break;
	case 0:
	    serror("Unterminated char/string constant");
	    goto out;

	case '\\':
	    c = input();
	    if (c == '\n')
		continue;
	    /* *cp++ = '\\'; When _should_ this be done? XXX */
	    if (isdigit(c)) {
		dc = 0;
		while (dc++ < 3 && isdigit(c)) {
		    *cp++ = c;
		    c = input();
		}
		unput(c);
		break;
	    }
	    if (c != 0) {
		switch (c) {
		case 't': c = '\t'; break;
		case 'n': c = '\n'; break;
		case 'r': c = '\r'; break;
		case 'b': c = '\b'; break;
		case 'f': c = '\f'; break;
		default: break;
		}
		*cp++ = c;
	    }
	    break;
	}
	entering = 0;
	cspec++;
    }

 out:
    *cp = 0;
    return(cspec);
}

/*
 * Scan input for array_in.  The leading delimiter ({) has already been
 * read.  Be sure to gobble up the whole thing, including the
 * trailing delimiter.
 */
int
scanarr(char *buf, int len)
{
    register char *cp = buf;
    register int c, dc, cspec, 
    counter;			/* counts matching '{' and '}'.  */
    /* stop scanning when unmatched '}' */
    /* is encounterd. */
    int in_string = 0;

    cspec = 0;
    /*
     * counter counts {'s, so we can do nested arrays properly.
     * It starts from 1 (not zero) because the first thing we encountered
     * was a {.
     */

    counter = 1;

    *cp++ = '{';		/* array funcs assume that there is a proper nesting */

    while (counter != 0) {
	c = input();
	if ( c == '{' && !in_string) counter++;
	if ( c == '}' && !in_string) counter--;
	if (cp - buf > len - 1) {
	    serror("String/char constant too large");
	    cp = buf;
	}
	switch (c) {
	default:
	    *cp++ = c;
	    break;

	case 0:
	case '\n':
	    serror("Unterminated array constant");
	    goto out;

	case '\\':
	    c = input();
	    if (c == '\n')
		continue;
	    /* *cp++ = '\\'; When _should_ this be done? XXX */
	    if (isdigit(c)) {
		dc = 0;
		while (dc++ < 3 && isdigit(c)) {
		    *cp++ = c;
		    c = input();
		}
		unput(c);
		break;
	    }
	    if (c != 0) {
		switch (c) {
		case 't': c = '\t'; break;
		case 'n': c = '\n'; break;
		case 'r': c = '\r'; break;
		case 'b': c = '\b'; break;
		case 'f': c = '\f'; break;
		default: *cp++ = '\\'; break;
		}
		*cp++ = c;
	    }
	    break;
	case '\"':
	    in_string = !in_string;
	    *cp++ = c;
	    break;
	}
	cspec++;
    }

 out:
    *cp = 0;
    return(cspec);
}
