static char *scanner_c = "$Header: /private/postgres/src/parser/RCS/scanner.c,v 1.17 1992/04/09 17:59:12 mer Exp $";

#include <ctype.h>

#define false 0
#define true !false

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

unsigned char   save_buffer[8192];
unsigned char * save_buffer_end;
int             save_buffer_length = sizeof(save_buffer);

/*
 *	Scan PL/1 style comment.
 */
scancmnt()
{
	register int c, trail;

	trail = 0;
	for (;;) {
		c = lex_input();
		switch (c) {
		case 0:
			serror("Unterminated comment.");
			return;
		
		case '/':
			if (trail == '*')
				return;
		}
		trail = c;
	}
}

/*
 * Scan a string.  The leading delimiter (", ') has already been
 * read.  Be sure to gobble up the whole thing, including the
 * trailing delimiter.
 */

int
scancon(buf, len, delimiter)
char * buf;
int    len;
char   delimiter;
{
	register char *cp = buf;
	register int c, dc, cspec;
	int entering = 1;

	save_buffer_end = &save_buffer[0];
	*save_buffer_end++ = (unsigned char)delimiter;

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

		if( cp - buf        >  len - 1 ||
		    save_buffer_end >= (&save_buffer[0] + save_buffer_length-3)
		  )
		  {
			serror("String/char constant too large");
			cp = buf;
		  }
	        *save_buffer_end++ = (unsigned char)c;

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

		case '\\':
			c = lex_input();
	                *save_buffer_end++ = (unsigned char)c;
			if (c == '\n')
				continue;
			/* *cp++ = '\\'; When _should_ this be done? XXX */
			if (isdigit(c)) {
				dc = 0;
				while (dc++ < 3 && isdigit(c)) {
					*cp++ = c;
					c = lex_input();
	                                *save_buffer_end++ = (unsigned char)c;
				}
				lex_unput( c );
	                        save_buffer_end--;
				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;
		} /* end switch */
		entering = 0;
		cspec++;
	} /* end of char-by-char input (while) */

	*save_buffer_end++ = (unsigned char)delimiter;
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(buf, len)
char *buf;
int len;
{
	register char *cp = buf;
	register int c, c2, dc, cspec, 
		     counter;  /* counts matching '{' and '}'.  */
			       /* stop scanning when unmatched '}' */
			       /* is encounterd. */
	int in_string = false;

	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 = lex_input();
		if ( c == '{' && !in_string)
		  counter++;
		if ( c == '}' && !in_string)
		  counter--;
		if( cp - buf > len - 1 ||
		    save_buffer_end >= (&save_buffer[0] + save_buffer_length-3)
		  )
		  {
			serror("String/char constant too large");
			cp = buf;
		  }
	        *save_buffer_end++ = (unsigned char)c;

		switch (c) {
		default:
			*cp++ = c;
			break;

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

		case '\\':
			c = lex_input();
	                *save_buffer_end++ = (unsigned char)c;
			if (c == '\n')
				continue;
			/* *cp++ = '\\'; When _should_ this be done? XXX */
			if (isdigit(c)) {
				dc = 0;
				while (dc++ < 3 && isdigit(c)) {
					*cp++ = c;
					c = lex_input();
	                                *save_buffer_end++ = (unsigned char)c;
				}
				lex_unput( c );
	                        save_buffer_end--;
				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);
}
