/* Flex(1) XML processor skeleton scanner (in -*-C-*-).
 * Copyright (C) 1999 Kristoffer Rose.  All rights reserved.
 *
 * This file is part of the FleXML XML processor generator system.
 * Copyright (C) 1999 Kristoffer Rose.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * Note: Files generated by the FleXML system have fewer restrictions on them
 * as explained in the header of each generated file.
 */
%{

/* Version strings. */
FLEXML_VERSION

/* ANSI headers. */
#include <stdlib.h> /* for realloc() -- needed here when using flex 2.5.4 */
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include <ctype.h>
     
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__TOS_WIN__)
# ifndef __STRICT_ANSI__
#  include <io.h>
#  include <process.h>
# endif
#else
# include <unistd.h>
#endif
     
#ifndef FLEXML_INDEXSTACKSIZE
#define FLEXML_INDEXSTACKSIZE 1000
#endif

/* Generated definitions. */
FLEXML_DEFINITIONS

/* XML state. */
#ifdef FLEX_DEBUG
# define ENTER(state)	debug_enter(state,#state)
# define LEAVE		debug_leave()
# define SET(state)	debug_set(state,#state)
  static void debug_enter(int, const char*);
  static void debug_leave(void);
  static void debug_set(int, const char*);
#else
# define ENTER(state)	(yy_push_state(state))
# define LEAVE		(yy_pop_state())
# define SET(state)	BEGIN(state)
#endif

/* Generic actions. */
#define SKIP	/*skip*/
#define SUCCEED        CLEANUP; return 0

#define FAIL	return fail
static int fail(const char*, ...);

enum {flexml_max_err_msg_size = 512};
static char flexml_err_msg[flexml_max_err_msg_size];
const char * parse_err_msg()
{
    return flexml_err_msg;
}
static void reset_parse_err_msg()
{
    flexml_err_msg[0] = '\0';
}

/* Cleanup */
static void cleanup(void);
#define CLEANUP  cleanup()

/* Text buffer stack handling. */
char *bufferstack = NULL;
static int blimit = FLEXML_BUFFERSTACKSIZE;
static int bnext = 1;

static int *indexstack = NULL;
static int ilimit = FLEXML_INDEXSTACKSIZE;
static int inext = 1;

#define BUFFERSET(P)  (P = bnext)
#define BUFFERPUTC(C) (ck_blimit(), bufferstack[bnext++] = (C))
#define BUFFERDONE    (BUFFERPUTC('\0'))

#define BUFFERLITERAL(C, P) bufferliteral(C, &(P), yytext)

/* after this is called, there are at least 2 slots left in the stack */
static int ck_blimit()
{
     if (bnext >= blimit) {
	 blimit += FLEXML_BUFFERSTACKSIZE + 2;
	 {
	     char *temp = (char *) realloc(bufferstack, blimit);
	     assert(temp);
	     bufferstack = temp;
	 }
     }
     return 0;
}

/* after this is called, there are at least 2 slots left in the stack */
static int ck_ilimit()
{
     if (inext >= ilimit) {
	 ilimit += FLEXML_INDEXSTACKSIZE + 2;
	 {
	     int *temp = (int *) realloc(indexstack, ilimit);
	     assert(temp);
	     indexstack = temp;
	 }
     }
     return 0;
}

#ifdef FLEXML_NEED_BUFFERLIT
static void bufferliteral(char c, int* pp, const char* text)
{
   BUFFERSET(*pp);
   if (c) {
      const char *s = strchr(text, c), *e = strrchr(text, c);
      assert(s && e && s <= e);
      ++s;
      while (s < e) {
	 if (isspace(*s)) {
	    BUFFERPUTC(' ');
	    do ++s; while (s < e && isspace(*s));
	 } else
	   BUFFERPUTC(*s++);
      }
   } else {
      const char *s = text;
      while (*s)
	BUFFERPUTC(*s++);
   }
   BUFFERDONE;
}
#endif

static void pushbuffer(int p)
{
    ck_ilimit();
    indexstack[inext++] = p;
    indexstack[inext++] = bnext;    
}

static int popbuffer(void)
{
    assert(inext >= 2);
    bnext = indexstack[--inext];
    return indexstack[--inext];
}

/* General internal entities are `unput' back onto the input stream... */
#define ENTITYTEXT(T) \
  { char *s = (T), *e = s+strlen(s);\
    while (--e >= s) { unput(*e); }}

FLEXML_INCLUDE_INIT_HEADER

%}

/* Flex standard options. */
%option stack
%option noyy_top_state
%option noinput
%option noreject
%option noyymore
%option noyywrap

/* Flex user-requested options. */
FLEXML_FLEX_OPTIONS

/* XML character classes (currently restricted to ASCII). */

/* "Common syntactic structures." */
S		[ \t\n\r\f]+
s		[ \t\n\r\f]*

/* "Names and Tokens." */
NameChar	[A-Za-z0-9.:_-]
Name		[A-Za-z_:]{NameChar}*
Names 		{Name}({S}{Name})*
Nmtoken		({NameChar})+
Nmtokens 	{Nmtoken}({S}{Nmtoken})*

/* Miscellaneous. */
VersionNum	[a-zA-Z0-9_.:-]+
Eq		{s}"="{s}
Literal		\'[^'']*\'|\"[^""]*\"

/* Parser states (flex `exclusive start conditions'):
 *
 * PROLOG	the XML prolog of the document before <?xml...>
 * DOCTYPE	the XML prolog of the document after <?xml...>
 * EPILOG	after the root element
 * INCOMMENT	inside an XML comment <!--....-->
 * INPI		inside an XML PI <?...?>
 * VALUE1	inside a '...'-delimited literal
 * VALUE2	inside a "..."-delimited literal
 * CDATA	inside a <![CDATA[...] ]> section.
 * ROOT_<tag>	expect root element <tag>
 * AL_<tag>	inside the attribute list for <tag>
 * IN_<tag>	inside a <tag> with element contents (ready for end tag)
 * IMPOSSIBLE	dummy to permit disabling rules; must be last
 */
%x PROLOG DOCTYPE EPILOG INCOMMENT INPI VALUE1 VALUE2 CDATA
FLEXML_START_CONDITIONS
%x IMPOSSIBLE

FLEXML_EXTRA_DEFINITIONS

%%

 /* Bypass Flex's default INITIAL state and begin by parsing the XML prolog. */
 SET(PROLOG);
 reset_parse_err_msg();
 bufferstack = (char *) malloc(FLEXML_BUFFERSTACKSIZE);
 assert(bufferstack);
 #ifdef FLEX_DEBUG
 {
     int i;
     for (i = 0; i < blimit; i++) {
         bufferstack[i] = '\377';
     }
 }
 #endif
 bufferstack[0] = '\0';
 indexstack = (int *) malloc(FLEXML_INDEXSTACKSIZE * sizeof(int));
 assert(indexstack);
 indexstack[0] = 0;

FLEXML_EXTRA_DEFINITIONS_INIT

 /* COMMENTS and PIs: handled uniformly for efficiency. */

<FLEXML_COMMENTS,PROLOG,DOCTYPE,EPILOG>{
 "<!--" ENTER(INCOMMENT);
 "<?" ENTER(INPI);
}
<INCOMMENT>{
 "-->"		LEAVE;
 "--"		|
 .		|
 \n		SKIP;
 <<EOF>>	FAIL("EOF in comment.");
}
<INPI>{
 "?>"		LEAVE;
 .		|
 \n		SKIP;
 <<EOF>>	FAIL("EOF in PI (processing instruction).");
}

 /* SPACES: skipped uniformly */

<FLEXML_NON_MIXED,PROLOG,DOCTYPE,EPILOG>{S} SKIP;

 /* PROLOG: determine root element and process it. */

<PROLOG>{
 "<?xml"({S}version{Eq}(\'{VersionNum}\'|\"{VersionNum}\"))?({S}encoding{Eq}(\'[^']*\'|\"[^"]*\"))?"?>" SET(DOCTYPE); 
 "<?xml"[^>]*">" FAIL("Bad declaration %s.",yytext);
}

<PROLOG,DOCTYPE>{
FLEXML_DOCTYPES
 "<!"[^>-][^>]*">" FAIL("Bad declaration %s.",yytext);
 . 		FAIL("Unexpected character `%c' in prolog.", yytext[0]);
 <<EOF>> 	FAIL("EOF in prolog.");
}

 /* RULES DERIVED FROM DTD. */
FLEXML_RULES

 /* EPILOG: after the root element. */

<EPILOG>{
 . {SET(PROLOG); yyless(0); CLEANUP; return -1;}
 <<EOF>> 	SUCCEED;
}

 /* CHARACTER DATA. */

<FLEXML_MIXED,VALUE1,VALUE2>{
FLEXML_ENTITIES

 /* Character entities. */
 "&#"[[:digit:]]+";"	BUFFERPUTC((unsigned char)atoi(yytext+2));
 "&#x"[[:xdigit:]]+";"	BUFFERPUTC((unsigned char)strtol(yytext+3,NULL,16));
}

<FLEXML_MIXED,VALUE1,VALUE2,CDATA>{
 "\n"		|
 "\r"		|
 "\r\n"		|
 "\n\r"		BUFFERPUTC('\n');
}

<FLEXML_MIXED>{
 "<![CDATA["	ENTER(CDATA);
 "]""]>"		FAIL("Unexpected `]""]>' in character data.");
}

<VALUE1>{
 \'		BUFFERDONE; LEAVE;
 <<EOF>>	FAIL("EOF in literal (\"'\" expected).");
}

<VALUE2>{
 \"		BUFFERDONE; LEAVE;
 <<EOF>>	FAIL("EOF in literal (`\"' expected).");
}

<FLEXML_MIXED,VALUE1,VALUE2>{
 [^<&]		BUFFERPUTC(yytext[0]);
 [<&]		FAIL("Spurious `%c' in character data.",yytext[0]);
}

<CDATA>{
 "]""]>"		LEAVE;
 /* "]""]"		BUFFERPUTC(yytext[0]); BUFFERPUTC(yytext[1]); */
 .		BUFFERPUTC(yytext[0]);
 <<EOF>>	FAIL("EOF in CDATA section.");
}

 /* Impossible rules to avoid warnings from flex(1). */
 /* Ideally, this should be replaced by code in flexml.pl that
    generates just the states not covered by other rules. */
<*>{
 .|[\n] FAIL("Syntax error on character `%c'.", yytext[0]);
}

%%

/* Element context stack lookup. */
int element_context(int i)
{
  return (0<i && i<yy_start_stack_depth
	  ? yy_start_stack[yy_start_stack_ptr - i]
	  : 0);
}

#ifdef FLEX_DEBUG
void print_yy_stack(char* fmt, ...)
{
  int i = 0; va_list ap; va_start(ap, fmt);
  vfprintf(stderr, fmt, ap);
  if (statenames) {
      for (i=1; i<yy_start_stack_ptr; i++) {
          fprintf(stderr, "%s/", statenames[yy_start_stack[i] ]);
      }
      fprintf(stderr,"%s\n", statenames[YY_START]);
  }
  va_end(ap);
}

void print_bufferstack()
{
    int i;
    fputs("Buffer: ", stderr);
    for (i = 0; i < blimit; i++) {
       if ( bufferstack[i] == '\377' ) break;
         putc(bufferstack[i], stderr);
    }
    putc('\n', stderr);
}

static void debug_enter(int state, const char* statename) {
  yy_push_state(state);
  if (yy_flex_debug) {
       print_yy_stack("--ENTER(%s) : ",statename);
       print_bufferstack();
  }
}

static void debug_leave(void) {
    if (yy_flex_debug) {
        print_yy_stack("--LEAVE : ");
	print_bufferstack();
    }
  yy_pop_state();
}

static void debug_set(int state, const char* statename) {
  BEGIN(state);
  if (yy_flex_debug) print_yy_stack("--SET(%s) : ",statename);
}
#endif


static void cleanup(void)
{
    if (statenames) {
        free(statenames);
	statenames = NULL;
    }
    free(bufferstack);
    bufferstack = NULL;

    free(indexstack);
    indexstack = NULL;
}


static int fail(const char* fmt, ...)
{
    int chars_left, used;
    va_list ap; va_start(ap, fmt);
#ifdef FLEXML_yylineno
    used = sprintf(flexml_err_msg,
		   "Invalid XML (XML input line %d, state %d): ",
		   yylineno, YY_START);
#else
    used = sprintf(flexml_err_msg,
		   "Invalid XML (state %d): ",
		   YY_START);
#endif
    chars_left = flexml_max_err_msg_size - used - 1;
    vsnprintf(flexml_err_msg + used, chars_left, fmt, ap);
    va_end(ap);

#ifndef FLEXML_quiet_parser
    /* print directly to sdterr */
    fprintf(stderr, "%s\n", flexml_err_msg);
    flexml_err_msg[0] = '\0';
#endif

    cleanup();

    return 1;
}
