/* -*- C++ -*- */
%option prefix="Config"
%option yylineno
%option nostdinit
%option never-interactive
%option batch
%option noyywrap
%option nodefault
%option 8bit
%option outfile="ConfigLexer.cpp"
%option ecs
%option noreject
%option noyymore

%{

#include "ConfigRep.h"
#include "ConfigParser.h"

/* LitString decodes a string like "Test\"\\ \"" into Test"\ " */
static char *LitString(char *);

/* VarString decodes ($VarName) or $(VarName) into $VarName */
static char *VarString(char *Str);

#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#endif

#define YY_NEVER_INTERACTIVE 1
%}

/* Variables may be either of the form ($Name) or $(Name) */
Var1     ([(]$[^)]+[)])
Var2     ($[(][^)]+[)])

Comment  #.*

/* Unquoted literals have the whitespace stripped out of them... */
UnQLit   [a-zA-Z0-9$.]+

/* Quoted literal strings, can have anything up to their quote character */
LitStr2  \"([^\"\\]|\\.)*\"
LitStr3  '([^'\\]|\\.)*'
%%

    /* Single character tokens that should be returned */
":;+][,"             { return yytext[0]; }

{UnQLit}   { Configlval.StrData = LitString(yytext); return LITSTRING; }
{LitStr2}  { Configlval.StrData = LitString(yytext); return LITSTRING; }
{LitStr3}  { Configlval.StrData = LitString(yytext); return LITSTRING; }
{Var1}     { Configlval.StrData = VarString(yytext); return VARIABLE; }
{Var2}     { Configlval.StrData = VarString(yytext); return VARIABLE; }

{Comment}  { /* Ignore comments */ }

                  
[ \t\n]           { /* Ignore whitespace */ }

    /* Anything else is an error that should be reported by the parser */
.                 { return yytext[0]; }

%%

static char *VarString(char *Str) {
  int Len = strlen(Str);
  Str[Len-1] = 0;
  Str[1] = '$';

  return strdup(Str+1);
}

static char *LitString(char *Str) {
  int S1Length = strlen(Str);
  char *S = (char*)malloc(S1Length+1);
  int Quoted = 0;

  // Remove starting and ending quotes 
  if (Str[0] == '\'' || Str[0] == '"') {
    Str[S1Length-1] = 0; 
    Str++; 
    Quoted = 1;
  } 

  char *Src = Str, *Dest = S;
  while (*Src != 0) {
    if (*Src == '\\') {
      Src++;
      switch (*Src) {
      case 0:     // Null...
	return S;
      case 'n':   //   \n  = Newline
	*Dest = '\n';
	break;
      case 't':   //   \t  = Tab
	*Dest = '\t';
	break;
      case 'e':   //   \e  = esc
	*Dest = '\033';
	break;
      case 'r':   //   \r  = Carriage return
	*Dest = '\r';
	break;
      case 'f':   //   \f  = Form feed
	*Dest = '\f';
	break;
      case 'b':   //   \b  = Backspace
	*Dest = '\b';
	break;
      case 'a':   //   \a  = Alert (beep)
	*Dest = '\a';
	break;
      default:
	*Dest = *Src;
	if (!Quoted)
	  if (*Src == '\n' || *Src == ' ' || *Src == '\t')
	    Dest--;       // Unquoted strings ignore whitespace
	break;
      }

    } else {
      *Dest = *Src;
    }

    Src++;
    Dest++;
  }

  *Dest = 0;
  return S;
}
