%{

/*
 * © Copyright 1996-2012 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */


#undef YYLMAX
#define YYLMAX 1024

#include <setjmp.h>
#include <ctype.h>
#include <stdarg.h>
static jmp_buf env;
#define exit(a)    jumpexit(a)
#define fprintf    jumpprtf

static void include(const char*);

#define STRIP_SPACES
static char *clean(char *);

#ifndef METVIEW
#define SHOW_INPUT
#endif


#ifdef FLEX_SCANNER
#define YY_INPUT(buf,result,max_size) \
               { \
               int c = getc(yyin); \
               result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \
               }
#endif



#ifdef SHOW_INPUT
/* These two routines are very inneficient .. */

#define MAXLINE 256
static int  echomode;
static char line[MAXLINE];
static int  charpos = 0;

static void showc(char);
#ifdef hpux
static void shows(unsigned char*);
#else
static void shows(char*);
#endif


#else

#define showc(x)
#define shows(x)
#define syntax_echo(x)

#endif


static void jumpexit(int n)
{
	longjmp(env,1);
}

static void jumpprtf(FILE *f,char *fmt,...)
{
	va_list list;
	char buf[1024];
	int len;
	va_start(list,fmt);
	vsprintf(buf, fmt, list);
	va_end(list);

	len = strlen(buf);
	if(len && buf[len-1] == '\n')
		buf[len-1] = 0;

	marslog(LOG_EROR,"Parser error: %s",buf);
}


%}

BLANK	[ \t\n]+
IDENT   [_0-9A-Za-z]+[_\.\-\+A-Za-z0-9:\t ]*[_\.\-\+A-Za-z0-9]*
NUMB    [\-\.]*[0-9]+[\.0-9]*[Ee]*[\-\+]*[0-9]*
%%


^\*        { int c;
			 shows(yytext);
			 while((c = input()) && (c != '\n')) showc(c); showc(c);
			 if(c == '\n') unput(c);
		    }

\!|\# 		 { int c; shows(yytext);while((c = input()) && (c != '\n')) showc(c); showc(c);}


\"|\'            {
				   int c,q = yytext[0];

				   if(q == '\'') yyleng = 0;

				   while((c = input()) && c != q && c != '\n')
				   {
					   if(c == '\\') yytext[yyleng++] = input();
					   else yytext[yyleng++] =  c;
				   }

				   if(c == '"')
					yytext[yyleng++] = c;

				   yytext[yyleng++] = 0;
				   shows(yytext);
				   yylval.str = strcache(yytext);
				   return WORD;
				 }

"%include"       {
					char word[1024];
					int  i = 0;
					int c;
					while((c = input()) && isspace(c) && c != '\n') ;
					while(c && !isspace(c) && c != '\n')
					{
						word[i++] = c;
						c = input();
					}
					word[i] = 0;
					include(no_quotes(word));
				 }

"%if"      { shows(yytext); return IF; }
"%then"    { shows(yytext); return THEN; }
"%and"     { shows(yytext); return AND; }
"%not"     { shows(yytext); return NOT; }
"%or"      { shows(yytext); return OR; }
"%in"      { shows(yytext); return IN; }
"%set"     { shows(yytext); return SET; }
"%unset"   { shows(yytext); return UNSET; }
"%warning" { shows(yytext); return WARNING; }
"%info"    { shows(yytext); return INFO; }
"%mail"    { shows(yytext); return MAIL; }
"%mailuser" { shows(yytext); return MAILUSER; }
"%error"   { shows(yytext); return ERROR; }
"%exit"    { shows(yytext); return EXIT; }
"%fail"    { shows(yytext); return FAIL; }

{IDENT}          { shows(yytext); yylval.str = clean(yytext); return WORD; }
{NUMB}           { shows(yytext); yylval.str = clean(yytext); return WORD; }
{BLANK}          { shows(yytext); }
\!\!             { shows(yytext); }
.                { showc(*yytext); return *yytext; }
%%

#define MAXINCLUDE 10

typedef struct {
	char *name;
	FILE *file;
	int  line;
} context;

static context stack[MAXINCLUDE];
static int     top = 0;
static err    parse_err = NOERR;
static char *first = 0;
extern FILE *yyin;
extern int yylineno;

int yywrap()
{

#ifdef SHOW_INPUT
	charpos  = 0;
#endif

	top--;
	fclose(stack[top].file);
	yylineno = stack[top].line;
	strfree(stack[top].name);

	if(top)
	{
		yyin = stack[top-1].file;
		return 0;
	}
	return 1;
}

#ifdef STRIP_SPACES
static char *clean(char *p)
{
	char *q = p;
	char *r = p;
	int  space = 0;

	while(*p)
	{
		/* if(islower(*p)) *p = toupper(*p); */
		/* if(isspace(*p)) */
		if(*p == ' ' || *p == '\t')
			space++;
		else
		{
			if(space) *q++ = ' ';
			space = 0;
			*q++ = *p;
		}

		p++;
	}
	*q = 0;

	return  strcache(r);
}
#endif

#ifdef SHOW_INPUT

static void showc(char c)
{
	if(charpos<MAXLINE)
		line[charpos++] = c;
	if(c == '\n') charpos = 0;
	if(echomode) putchar(c);
}

#ifdef hpux
static void shows(unsigned char* q)
{
	char *p = (char*)q;
	while(*p) showc(*p++);
}
#else
static void shows(char *p)
{
	while(*p) showc(*p++);
}
#endif


static void syntax_echo(boolean onoff)
{
	echomode = onoff;
}

static void syntax_error(char *file,const char* message,int  c)
{
	int i;

	line[charpos] = 0;
	printf("\n\n%s\n",line);

	for(i=0;i<charpos;i++) putchar(' ');
	putchar('^');
	putchar('\n');
	for(i=0;i<charpos;i++) putchar('-');
	putchar('-');
	putchar('\n');

	marslog(LOG_EROR,"file %s line %d : %s",file,yylineno,message);
	if(isprint(c))
		marslog(LOG_EROR,"file %s line %d : unexpected character in input '%c'",
		    file,yylineno,c);
	else
		marslog(LOG_EROR,"file %s line %d : error code %d",
		    file,yylineno,c);
}

#else

static void syntax_error(char *file,char *message,int  c)
{
	marslog(LOG_EROR,"file %s line %d : %s",file,yylineno,message);
	if(isprint(c))
		marslog(LOG_EROR,"file %s line %d : unexpected character in input '%c'",
		    file,yylineno,c);
	else
		marslog(LOG_EROR,"file %s line %d : error code %d",
		    file,yylineno,c);
}
#endif


err parser(const char *fname, boolean echo)
{
	extern FILE *yyin;

	if(mars.logrequest && mars.logreqfile) {
		fname = save_request_file_to_log(fname);
	}

	if(fname) {
        yyin = fopen(fname,"r");
        if(yyin == NULL) {
            marslog(LOG_EROR|LOG_PERR,"Cannot open file %s",fname);
            return -1;
        }
    }
    else {
        fname = "(standard input)";
        yyin = stdin;
	}

	parse_err = NOERR;
	syntax_echo(echo);

	top = 1;
	stack[0].file = yyin;
	stack[0].name = strcache(fname);
	stack[0].line = 0;

	first = strcache(fname);

	if(setjmp(env))
		return 1;

	yyparse();

	strfree(first);

	return parse_err;
}


void yyerror(const char *msg)
{
	if(top)
		syntax_error(stack[top-1].name,msg,yychar);
	else
		syntax_error(first,msg,yychar);
	parse_err = -1;
}


static void include(const char *fname)
{
	int n   = top-1;
	const char *q = config_file(fname);
	const char *p = makepath(mdirname(stack[n].name),q);
	FILE *f = fopen(p,"r");

	if(f == NULL)
	{
		marslog(LOG_EROR|LOG_PERR,"Cannot open file %s",p);
		yyerror("Cannot include file");
	}
	else
	{
		yyin            = f;
		stack[top].file = f;
		stack[top].name = strcache(p);
		stack[top].line = yylineno;
#ifdef FLEX_SCANNER
		yylineno=0;
#endif
		top++;
	}
}
