%{
/*-------------------------------------------------------------------------
 *
 * syncrep_scanner.l
 *	  a lexical scanner for synchronous_standby_names
 *
 * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/replication/syncrep_scanner.l
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "lib/stringinfo.h"

/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
#undef fprintf
#define fprintf(file, fmt, msg)  fprintf_to_ereport(fmt, msg)

static void
fprintf_to_ereport(const char *fmt, const char *msg)
{
	ereport(ERROR, (errmsg_internal("%s", msg)));
}

/* Handles to the buffer that the lexer uses internally */
static YY_BUFFER_STATE scanbufhandle;

static StringInfoData xdbuf;

/* LCOV_EXCL_START */

%}

%option 8bit
%option never-interactive
%option nodefault
%option noinput
%option nounput
%option noyywrap
%option warn
%option prefix="syncrep_yy"

/*
 * <xd> delimited identifiers (double-quoted identifiers)
 */
%x xd

space			[ \t\n\r\f\v]

digit			[0-9]
ident_start		[A-Za-z\200-\377_]
ident_cont		[A-Za-z\200-\377_0-9\$]
identifier		{ident_start}{ident_cont}*

dquote			\"
xdstart			{dquote}
xdstop			{dquote}
xddouble		{dquote}{dquote}
xdinside		[^"]+

%%
{space}+	{ /* ignore */ }

	/* brute-force case insensitivity is safer than relying on flex -i */

[Aa][Nn][Yy]			{ return ANY; }
[Ff][Ii][Rr][Ss][Tt]	{ return FIRST; }

{xdstart}	{
				initStringInfo(&xdbuf);
				BEGIN(xd);
		}
<xd>{xddouble} {
				appendStringInfoChar(&xdbuf, '"');
		}
<xd>{xdinside} {
				appendStringInfoString(&xdbuf, yytext);
		}
<xd>{xdstop} {
				yylval.str = xdbuf.data;
				xdbuf.data = NULL;
				BEGIN(INITIAL);
				return NAME;
		}
<xd><<EOF>> {
				yyerror("unterminated quoted identifier");
				return JUNK;
		}

{identifier} {
				yylval.str = pstrdup(yytext);
				return NAME;
		}

{digit}+	{
				yylval.str = pstrdup(yytext);
				return NUM;
		}

"*"		{
				yylval.str = "*";
				return NAME;
		}

","			{ return ','; }
"("			{ return '('; }
")"			{ return ')'; }

.			{ return JUNK; }
%%

/* LCOV_EXCL_STOP */

/* Needs to be here for access to yytext */
void
syncrep_yyerror(const char *message)
{
	/* report only the first error in a parse operation */
	if (syncrep_parse_error_msg)
		return;
	if (yytext[0])
		syncrep_parse_error_msg = psprintf("%s at or near \"%s\"",
										   message, yytext);
	else
		syncrep_parse_error_msg = psprintf("%s at end of input",
										   message);
}

void
syncrep_scanner_init(const char *str)
{
	Size		slen = strlen(str);
	char	   *scanbuf;

	/*
	 * Might be left over after ereport()
	 */
	if (YY_CURRENT_BUFFER)
		yy_delete_buffer(YY_CURRENT_BUFFER);

	/*
	 * Make a scan buffer with special termination needed by flex.
	 */
	scanbuf = (char *) palloc(slen + 2);
	memcpy(scanbuf, str, slen);
	scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
	scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);

	/* Make sure we start in proper state */
	BEGIN(INITIAL);
}

void
syncrep_scanner_finish(void)
{
	yy_delete_buffer(scanbufhandle);
	scanbufhandle = NULL;
}
