%option noinput nounput
%{

#define YY_NO_UNPUT		1
#define USE_LOOP_HANDLING	1
#define LEXER			1

#if USE_LOOP_HANDLING
  #define YY_NEVER_INTERACTIVE 1 /* need this to avoid access to yyin within
				    lexer code, even if yyin != NULL */
  /* however, flex 2.5.31 no longer respects this #define so we work
     around it by opening /dev/null if a cachefile or a macrofile is
     accessed. */
  #define LOOP_LIMIT		1000 /* this to avoid infinite loops,
  					if the user has errors in his
  					config file */
#endif

/* Flex defines these, but stdint.h is going to define them again */
#undef INT8_MAX
#undef INT16_MAX
#undef INT32_MAX
#undef UINT8_MAX
#undef UINT16_MAX
#undef UINT32_MAX

#include <stdlib.h>
#include <termios.h>
#undef ECHO
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <setjmp.h>
#include <sys/stat.h>                    /* structure stat       */
#include <unistd.h>                      /* prototype for stat() */

#include "emu.h"
#include "cpu.h"
#include "disks.h"
#include "lpt.h"
#include "video.h"
#include "mouse.h"
#include "serial.h"
#include "timers.h"
#include "keyboard/keymaps.h"

#include "parser.h"
#include "dosemu_config.h"
#include "lexer.h"
int line_count;

#define NESTING_DEPTH	32 /* as we handle loops as pseudo includes,
			    * this defines the depth of nested loops
			    * as well as that of includes
			    */

static int __active__=1, __next_ITEM_for_me__=0;
#define __active_max (2*NESTING_DEPTH) /* Note: 'while' pushes twice */
static int __active_stack[__active_max+1]={1,0};
static int __active_i=1;
int include_stack_ptr = 0;
int last_include = 0;

static void push__active(void);
static void pop__active(void);
static void enter_includefile(char * fname);
static void enter_macrofile(char *variable);

#if 0 /* this just to test the conditional code */
  #define TESTACTIVE (({if(__active__)fprintf(stderr,">>>%s<<<\n", yytext);}),__active__)
#else
  #define TESTACTIVE __active__
#endif

#define RETURN if (TESTACTIVE) return
#define MAY_BE  if (TESTACTIVE)

#define MAY_BEFORME if (__next_ITEM_for_me__) { \
  if (get_config_variable(yytext)) { \
    __active__= (__next_ITEM_for_me__ > 0); \
  } \
  else __active__= (__next_ITEM_for_me__ < 0); \
  __next_ITEM_for_me__=0; \
} \
else if (TESTACTIVE)

#define MAY_BEINCLUDE(other_stuff) if (TESTACTIVE) { \
  if (__next_ITEM_for_me__ == 2) { \
    __next_ITEM_for_me__=0; \
    yytext[strlen(yytext)-1] = '\0'; \
    enter_includefile(&yytext[1]); \
  } \
  else other_stuff \
}

char *yy_vbuffer=0;

#if USE_LOOP_HANDLING

#define CACHEFILES_MAX  32
#define MACROFILE	CACHEFILES_MAX
FILE *dev_null_files[MACROFILE+1];

static int cachefile_nr(FILE *f)
{
	int i;
	for (i = 0; i < CACHEFILES_MAX; i++)
		if (dev_null_files[i] == f) return i;
	return CACHEFILES_MAX+1;
}

#define YY_INPUT(buf,result,max_size) { \
    result = do_yy_input(buf, max_size); \
}

/*
 * We are intercepting the yylex() function calls from the parser
 */ 
#undef YY_DECL
#define YY_DECL static int real_yylex ( YYSTYPE* yylval )


/*
 * we intercept lexer before executing each action
 */
#define YY_USER_ACTION if (__loop_handling__ > 0) \
			if (dump_input(yy_act)) break;

static int __loop_handling__ = 0;
static int dump_input(int rulenum);
static void enter_cachefile(int cfile);
static int cachefile_read(char *buf, int size, int cfile);
static int cachefile_wrap(void);
static int macrofile_read(char *buf, int size);
static int macrofile_wrap(void);
static int do_yy_input(char *buf, int max_size);

#else /* not USE_LOOP_HANDLING */


#define YY_INPUT(buf,result,max_size) \
  if (!yyin) { \
    if (yy_vbuffer && yy_vbuffer[0]) { \
      buf[(max_size)-1]=0; \
      strncpy(buf,yy_vbuffer,max_size); \
      if (buf[(max_size)-1]) { \
        yy_vbuffer+=max_size; \
        result=max_size; \
      } \
      else { \
        result=strlen(buf); \
        yy_vbuffer=0; \
      } \
    } \
    else result=0; \
  } \
  else { \
    if ( YY_CURRENT_BUFFER->yy_is_interactive ) { \
      int c = getc( yyin ); \
      result = c == EOF ? 0 : 1; \
      buf[0] = (char) c; \
    } \
    else { \
      if ( ((result = fread( buf, 1, max_size, yyin )) == 0) && ferror( yyin ) ) \
                  YY_FATAL_ERROR( "input in flex scanner failed" ); \
    } \
  }

#undef YY_DECL
#define YY_DECL int yylex YY_PROTO(( YYSTYPE* yylval ))

#endif /* not USE_LOOP_HANDLING */


%}

DIGIT		[0-9]
HEXDIGIT 	{DIGIT}|[a-fA-F]
LETTER		[a-zA-Z]
ALPHNUM		{LETTER}|{DIGIT}
IDENT		_*{LETTER}({ALPHNUM}|_)*
STRQUOTELESS	({LETTER}|[/\.\~])({ALPHNUM}|[_\~@\-\+:\./])*

%%

%{
	/* NOTE: "include" _must_ be the first rule, we need to know
	 *       the rule number, which _here_ is always '1'
	 */
#define INCLUDE_RULE_NUM	1
#define LOOP_RULE_NUM		(INCLUDE_RULE_NUM+1)
#define DONE_RULE_NUM		(LOOP_RULE_NUM+1)
%}
"include"		MAY_BE __next_ITEM_for_me__=2;
"while"|"foreach"	MAY_BE {
	#if USE_LOOP_HANDLING
			  if (!__loop_handling__) {
			    __loop_handling__ =1;
			    YY_USER_ACTION
			  }
	#endif
			};
"done"			if (__loop_handling__ <0) pop__active();
"while__yy__"		{ push__active(); RETURN(WHILESTATEMENT); }
"foreach__yy__"		{ push__active(); RETURN(FOREACHSTATEMENT); }

	/* special characters */

[{}()<>=,\-+\*;]		RETURN(yytext[0]);

	/* conditionals */

"if"			{ push__active(); RETURN(IFSTATEMENT); }
"ifdef"			{ push__active(); if (__active__) __next_ITEM_for_me__=1; }
"ifndef"		{ push__active(); if (__active__) __next_ITEM_for_me__=-1; }
"else"			{ 
			  if (__active__) __active__=0;
			  else if (__active_stack[__active_i-1] ) __active__=1;
			}
"endif"			pop__active();

	/* config variable settings */
"define"		RETURN(DEFINE);
"undef"			RETURN(UNDEF);
"checkuservar"		RETURN(CHECKUSERVAR);

	/* boolean values */
on			RETURN(L_ON);
off			RETURN(L_OFF);
auto			RETURN(L_AUTO);
yes			RETURN(L_YES);
no			RETURN(L_NO);

	/* operators */
"/"			RETURN('/');
"div"			RETURN('/');
"|"			RETURN(OR_OP);
"^"			RETURN(XOR_OP);
">>"			RETURN(SHR_OP);
"<<"			RETURN(SHL_OP);
"!"			RETURN(NOT_OP);
"=="			RETURN(EQ_OP);
">="			RETURN(GE_OP);
"<="			RETURN(LE_OP);
"!="			RETURN(NEQ_OP);
"&&"			RETURN(L_AND_OP);
"&"			RETURN(AND_OP);
"||"			RETURN(L_OR_OP);
"~"			RETURN(BIT_NOT_OP);
"eq"			RETURN(STR_EQ_OP);
"ne"			RETURN(STR_NEQ_OP);

	/* numbers */
\\u{HEXDIGIT}{4}	MAY_BE{yylval->i_value = strtoul(yytext+2, 0, 16);
				return(UNICODE); }


{DIGIT}+		MAY_BE {yylval->i_value = strtoul(yytext, 0, 10);
				return(INTEGER); }

0x{HEXDIGIT}+		MAY_BE {yylval->i_value = strtoul(yytext, 0, 0);
				return(INTEGER); }

0b[01]+			MAY_BE {yylval->i_value = strtoul(yytext+2, 0, 2);
				return(INTEGER); }

{DIGIT}+\.{DIGIT}*([eE]\-?{DIGIT}+)? MAY_BE {yylval->r_value = atof(yytext);
				return(REAL); }

	/* casts */
"int"			RETURN(INTCAST);
"real"			RETURN(REALCAST);

	/* functions */
"strlen"		RETURN(STRLEN);
"strtol"		RETURN(STRTOL);
"strncmp"		RETURN(STRNCMP);
"strcat"		RETURN(STRCAT);
"strpbrk"		RETURN(STRPBRK);
"strsplit"		RETURN(STRSPLIT);
"strdel"		RETURN(STRDEL);
"strchr"		RETURN(STRCHR);
"strrchr"		RETURN(STRRCHR);
"strstr"		RETURN(STRSTR);
"strspn"		RETURN(STRSPN);
"strcspn"		RETURN(STRCSPN);
"defined"		RETURN(DEFINED);
"shell"			RETURN(SHELL);

	/* just for test purposes */
exprtest		RETURN(EXPRTEST);

	/* keywords */

feature			RETURN(FEATURE);
abort			RETURN(ABORT);
warn			RETURN(WARN);
error			RETURN(ERROR);
x			RETURN(L_X);
sdl			RETURN(L_SDL);
fastfloppy		RETURN(FASTFLOPPY);
timer			RETURN(TIMER);
hogthreshold		RETURN(HOGTHRESH);
speaker			RETURN(SPEAKER);
ipxsupport		RETURN(IPXSUPPORT);
ipx_network		RETURN(IPXNETWORK);
pktdriver		RETURN(PKTDRIVER);
ne2k			RETURN(NE2K);
debug			RETURN(DEBUG);
mouse			RETURN(MOUSE);
serial			RETURN(SERIAL);
keyboard		RETURN(KEYBOARD);
keystroke		RETURN(PRESTROKE);
terminal		RETURN(TERMINAL);
video			RETURN(VIDEO);
emuretrace		RETURN(EMURETRACE);
mathco			RETURN(MATHCO);
cpu			RETURN(CPU);
cpuspeed		RETURN(CPUSPEED);
bootdrive		RETURN(BOOTDRIVE);
swap_bootdrive		RETURN(SWAP_BOOTDRIVE);
xms			RETURN(L_XMS);
umb_a0			RETURN(UMB_A0);
umb_b0			RETURN(UMB_B0);
umb_b8			RETURN(UMB_B8);
umb_f0			RETURN(UMB_F0);
hma			RETURN(HMA);
dos_up			RETURN(DOS_UP);
ems			RETURN(L_EMS);
dpmi			RETURN(L_DPMI);
dpmi_base		RETURN(DPMI_BASE);
pm_dos_api		RETURN(PM_DOS_API);
ignore_djgpp_null_derefs RETURN(NO_NULL_CHECKS);
dosmem			RETURN(DOSMEM);
ext_mem			RETURN(EXT_MEM);
ports			RETURN(PORTS);
trace			RETURN(TRACE);
clear			RETURN(CLEAR);
trace_mmio		RETURN(TRACE_MMIO);
sillyint		RETURN(SILLYINT);
irqpassing		RETURN(SILLYINT);
hardware_ram		RETURN(HARDWARE_RAM);
disk			RETURN(DISK);
printer			RETURN(PRINTER);
emusys                  RETURN(EMUSYS);
file_lock_limit		RETURN(FILE_LOCK_LIMIT);
lfn_support		RETURN(LFN_SUPPORT);
force_int_revect	RETURN(FINT_REVECT);
set_int_hooks		RETURN(SET_INT_HOOKS);
trace_irets		RETURN(TRACE_IRETS);
force_fs_redirect	RETURN(FFS_REDIR);
ttylocks		RETURN(TTYLOCKS);
sound_emu               RETURN(L_SOUND);
oss_options             RETURN(L_SND_OSS);
joystick_emu		RETURN(L_JOYSTICK);
dosemumap               RETURN(DOSEMUMAP);
logbufsize              RETURN(LOGBUFSIZE);
logfilesize		RETURN(LOGFILESIZE);
mappingdriver		RETURN(MAPPINGDRIVER);

	/* sillyint values */
use_sigio		RETURN(USE_SIGIO);

	/* ems values */
ems_size		RETURN(EMS_SIZE);
ems_frame		RETURN(EMS_FRAME);
ems_uma_pages		RETURN(EMS_UMA_PAGES);
ems_conv_pages		RETURN(EMS_CONV_PAGES);

	/* speaker values */

emulated		RETURN(EMULATED);
native			RETURN(NATIVE);

	/* cpuemu values */

cpu_vm			RETURN(CPU_VM);
cpu_vm_dpmi		RETURN(CPU_VM_DPMI);
kvm			RETURN(KVM);
cpuemu			RETURN(CPUEMU);
vm86			RETURN(VM86);

	/* disk keywords */
hdimage			RETURN(HDIMAGE);
image			RETURN(HDIMAGE);
partition		RETURN(L_PARTITION);
wholedisk		RETURN(WHOLEDISK);
readonly		RETURN(READONLY);
ro			RETURN(READONLY);
threeinch		RETURN(THREEINCH);
threeinch_2880		RETURN(THREEINCH_2880);
threeinch_720		RETURN(THREEINCH_720);
fiveinch		RETURN(FIVEINCH);
fiveinch_360		RETURN(FIVEINCH_360);
boot			RETURN(BOOT);
sectors			RETURN(SECTORS);
cylinders		RETURN(CYLINDERS);
tracks			RETURN(TRACKS);
heads			RETURN(HEADS);
offset			RETURN(OFFSET);
floppy			RETURN(L_FLOPPY);
cdrom			RETURN(CDROM);
diskcyl4096		RETURN(DISKCYL4096);
hdtype1			RETURN(HDTYPE1);
hdtype2			RETURN(HDTYPE2);
hdtype9			RETURN(HDTYPE9);
default_drives		RETURN(DEFAULT_DRIVES);
skip_drives		RETURN(SKIP_DRIVES);

	/* keyboard */
ctrl			RETURN(CTRL_MAP);
shift_alt		RETURN(SHIFT_ALT_MAP);
ctrl_alt		RETURN(CTRL_ALT_MAP);
keytable		RETURN(KEYTABLE);
layout			RETURN(LAYOUT);
rawkeyboard		RETURN(RAWKEYBOARD);
shift			RETURN(SHIFT_MAP);
alt			RETURN(ALT_MAP);
numpad			RETURN(NUMPAD_MAP);
dump			RETURN(DUMP);
		/* dead keys for accents in keytable */
dgrave			RETURN(DGRAVE);
dacute			RETURN(DACUTE);
dcircum			RETURN(DCIRCUM);
dtilde			RETURN(DTILDE);
dbreve			RETURN(DBREVE);
daboved			RETURN(DABOVED);
ddiares			RETURN(DDIARES);
dabover			RETURN(DABOVER);
ddacute			RETURN(DDACUTE);
dcedilla		RETURN(DCEDILLA);
diota			RETURN(DIOTA);
dogonek			RETURN(DOGONEK);
dcaron			RETURN(DCARON);

	/* serial stuff */

base			RETURN(BASE);
irq			RETURN(IRQ);
baudrate		RETURN(BAUDRATE);
device			RETURN(DEVICE);
com                     RETURN(COM);
virtual			RETURN(VIRTUAL);
vmodem			RETURN(VMODEM);
pseudo			RETURN(PSEUDO);
rtscts			RETURN(RTSCTS);
low_latency		RETURN(LOWLAT);
pccom			RETURN(PCCOM);
exec			RETURN(EXEC);
pts			RETURN(PTS);
wrfile			RETURN(WRFILE);
nullmodem		RETURN(NULLMM);

	/* lock file stuff */
directory              RETURN(DIRECTORY);
namestub               RETURN(NAMESTUB);
binary                 RETURN(BINARY);

	/* terminal stuff */

charset			RETURN(CHARSET);
xterm_title		RETURN(XTERM_TITLE);
color			RETURN(COLOR);
escchar			RETURN(ESCCHAR);
size			RETURN(SIZE);

	/* mouse types */

microsoft		RETURN(MICROSOFT);
ms3button		RETURN(MS3BUTTON);
logitech		RETURN(LOGITECH);
mmseries		RETURN(MMSERIES);
mouseman		RETURN(MOUSEMAN);
hitachi			RETURN(HITACHI);
mousesystems		RETURN(MOUSESYSTEMS);
busmouse		RETURN(BUSMOUSE);
ps2			RETURN(PS2);
imps2			RETURN(IMPS2);
internaldriver		RETURN(INTERNALDRIVER);
emulate3buttons		RETURN(EMULATE3BUTTONS);
cleardtr		RETURN(CLEARDTR);
mouse_ungrab_tweak	RETURN(UNGRAB_TWEAK);

	/* video stuff - sorry for Matrox but MGA was already used */

vga			RETURN(VGA);
ega			RETURN(EGA);
cga			RETURN(CGA);
mga			RETURN(MGA);
mda			RETURN(MGA);
none			RETURN(NONE);
console			RETURN(CONSOLE);
graphics		RETURN(GRAPHICS);
chipset			RETURN(CHIPSET);
memsize			RETURN(MEMSIZE);
fullrestore		RETURN(FULLREST);
partialrestore		RETURN(PARTREST);
vgaemubios_file		RETURN(VGAEMUBIOS_FILE);
vbios_file		RETURN(VBIOS_FILE);
vbios_copy		RETURN(VBIOS_COPY);
vbios_mmap		RETURN(VBIOS_MMAP);
vbios_seg		RETURN(VBIOS_SEG);
vbios_size		RETURN(VBIOS_SIZE_TOK);
vbios_post		RETURN(VBIOS_POST);
vga_fonts		RETURN(VGA_FONTS);
dualmon			RETURN(DUALMON);
forcevtswitch		RETURN(FORCE_VT_SWITCH);
pci			RETURN(PCI);
plainvga		MAY_BE { yylval->i_value = PLAINVGA; return(CHIPSET_TYPE); }
svgalib			MAY_BE { yylval->i_value = SVGALIB; return(CHIPSET_TYPE); }
vesa			MAY_BE { yylval->i_value = VESA; return(CHIPSET_TYPE); }

	/* xwindows stuff */

display			RETURN(L_DISPLAY);
title			RETURN(L_TITLE);
title_show_appname      RETURN(X_TITLE_SHOW_APPNAME);
icon_name		RETURN(ICON_NAME);
blinkrate		RETURN(X_BLINKRATE);
sharecmap		RETURN(X_SHARECMAP);
mitshm                  RETURN(X_MITSHM);
font			RETURN(X_FONT);
fixed_aspect		RETURN(X_FIXED_ASPECT);
aspect_43		RETURN(X_ASPECT_43);
lin_filt		RETURN(X_LIN_FILT);
bilin_filt		RETURN(X_BILIN_FILT);
mode13fact		RETURN(X_MODE13FACT);
winsize			RETURN(X_WINSIZE);
gamma			RETURN(X_GAMMA);
vgaemu_memsize		RETURN(VGAEMU_MEMSIZE);
vesamode		RETURN(VESAMODE);
lfb			RETURN(X_LFB);
pm_interface		RETURN(X_PM_INTERFACE);
mgrab_key		RETURN(X_MGRAB_KEY);
background_pause	RETURN(X_BACKGROUND_PAUSE);
fullscreen		RETURN(X_FULLSCREEN);
noclose			RETURN(X_NOCLOSE);
noresize		RETURN(X_NORESIZE);

	/* SDL stuff */
sdl_hwrend		RETURN(SDL_HWREND);
sdl_fonts		RETURN(SDL_FONTS);
sdl_wcontrols		RETURN(SDL_WCONTROLS);
sdl_clip_native		RETURN(SDL_CLIP_NATIVE);

        /* Sound stuff */

sb_base                 RETURN(SB_BASE);
sb_irq                  RETURN(SB_IRQ);
sb_dma                  RETURN(SB_DMA);
sb_hdma                 RETURN(SB_HDMA);
mpu_base                RETURN(MPU_BASE);
mpu_base_mt32           RETURN(MPU_BASE_MT32);
midi_synth              RETURN(MIDI_SYNTH);
mpu_irq                 RETURN(MPU_IRQ);
mpu_irq_mt32            RETURN(MPU_IRQ_MT32);
sound_driver		RETURN(SOUND_DRIVER);
midi_driver		RETURN(MIDI_DRIVER);
fluid_sfont		RETURN(FLUID_SFONT);
fluid_volume		RETURN(FLUID_VOLUME);
munt_roms		RETURN(MUNT_ROMS);
opl2lpt_dev		RETURN(OPL2LPT_DEV);
opl2lpt_type		RETURN(OPL2LPT_TYPE);
snd_plugin_params	RETURN(SND_PLUGIN_PARAMS);
pcm_hpf			RETURN(PCM_HPF);
midi_file		RETURN(MIDI_FILE);
wav_file		RETURN(WAV_FILE);

        /* Joystick stuff */

joy_device              RETURN(JOY_DEVICE);
joy_dos_min             RETURN(JOY_DOS_MIN);
joy_dos_max             RETURN(JOY_DOS_MAX);
joy_granularity         RETURN(JOY_GRANULARITY);
joy_latency             RETURN(JOY_LATENCY);

	/* packet driver */
novell_hack		RETURN(NOVELLHACK);
ethdev			RETURN(ETHDEV);
tapdev			RETURN(TAPDEV);
vdeswitch		RETURN(VDESWITCH);
slirpargs		RETURN(SLIRPARGS);
netsock			RETURN(NETSOCK);
vnet			RETURN(VNET);
	/* debug flags */

io			RETURN(IO);
port			RETURN(PORT);
config			RETURN(CONFIG);
read			RETURN(READ);
write			RETURN(WRITE);
keyb			RETURN(KEYB);
warning			RETURN(WARNING);
general			RETURN(GENERAL);
hardware		RETURN(HARDWARE);
ipc			RETURN(L_IPC);
network			RETURN(NETWORK);
sound			RETURN(SOUND);
joystick		RETURN(JOYSTICK);

	/* printer stuff */

lpt			RETURN(LPT);
command			RETURN(COMMAND);
timeout			RETURN(TIMEOUT);
file			RETURN(L_FILE);

	/* port/io stuff */

ormask			RETURN(ORMASK);
andmask			RETURN(ANDMASK);
rdonly			RETURN(RDONLY);
wronly			RETURN(WRONLY);
rdwr			RETURN(RDWR);
range			RETURN(RANGE);
fast			RETURN(FAST);
slow			RETURN(SLOW);

	/* ASPI driver */
aspi			RETURN(ASPI);
devicetype		RETURN(DEVICETYPE);
target			RETURN(TARGET);

	/* hacks */
cli_timeout		RETURN(CLI_TIMEOUT);
timemode		RETURN(TIMEMODE);
timer_tweaks		RETURN(TIMER_TWEAKS);

	/* charset stuff */
external		RETURN(EXTERNAL);
internal		RETURN(INTERNAL);


	/* perms */
unix_exec		RETURN(UEXEC);
lredir_paths		RETURN(LPATHS);

hostfs_drives		RETURN(HDRIVES);

	/* strings */

\'[^\']*\'		{
			char *s;
			for(s = strchr(yytext, '\n'); s != NULL; s = strchr(s+1,'\n'))
			      line_count++;

			MAY_BE {
			  yylval->s_value = strdup(yytext);
			  return(STRING); }
			}

{STRQUOTELESS}		MAY_BEFORME {
			  yylval->s_value = strdup(yytext);
			  return(STRING); }
${IDENT}		MAY_BE {
				yylval->s_value = strdup(&yytext[1]);
				return(VARIABLE);
			}
$${IDENT}		MAY_BE { enter_macrofile(&yytext[2]);}

	/* Note: we need the rule numbers of below actions 
	 *       The below first one is INCLUDEFILE_RULE_NUM
	 */
\"[^\"]*\"		{
			char *s;
			for(s = strchr(yytext, '\n'); s != NULL; s = strchr(s+1,'\n'))
			      line_count++;

			MAY_BEINCLUDE ( {
			  yylval->s_value = strdup(yytext);
			  return(STRING); } )
			}

	/* comments & whitespace */

[#][^\n]*		;               /* comments to (and excluding) EOLN */
[ \t]+			;   		/* ignore all white space */
\n			line_count++;	/* keep track of lines seen */
.			;/* fprintf(stderr, "%s:%d discarding char '%c'\n", 
				include_fnames[include_stack_ptr],line_count, yytext[0]); */


%%
	/* the above '\n' rule (last rule) has rule-number 'YY_NUM_RULES-2'
	 * We have no other chance as to 'count' backward to get the
	 * rule number of ' quoted string, comments, e.t.c'
	 * BIG_FAT_NOTE: If you insert rules behind INCLUDEFILE_RULE_NUM,
	 *               change INCLUDEFILE_RULE_NUM too !!!
	 */
#define INCLUDEFILE_RULE_NUM (YY_NUM_RULES-2 - 3)
#define COMMENT_RULE_NUM (INCLUDEFILE_RULE_NUM +1)
#define WHITESPACE_RULE_NUM (COMMENT_RULE_NUM +1)
#define NEWLINE__RULE_NUM (WHITESPACE_RULE_NUM +1)

#define MAX_INCLUDE_DEPTH NESTING_DEPTH
static YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH] ={0};
char * include_fnames[MAX_INCLUDE_DEPTH] = {0};
char * include_macbuf[MAX_INCLUDE_DEPTH] = {0};
int include_lines[MAX_INCLUDE_DEPTH] = {0};

static void enter_includefile(char * fname)
{
  FILE * new_yyin;
  char fname_[256];
  if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) {
    yyerror("Includes nested too deeply" );
    return;
  }
  /* we want to have the include files in the same directory as
   * the main config file, if we have no leading  '/'
   * and we want "keymap/xxx" mapped to e.g. "<DOSEMU_LIB_DIR>/keymap/xxx"
   */
  if (fname[0] != '/') {
    int i;
    if (!strncmp(fname, keymap_dir_path, strlen(keymap_dir_path)))
      strcpy(fname_, keymap_load_base_path);
    else
      strcpy(fname_, include_fnames[include_stack_ptr]);
    i=strlen(fname_);
    while (i && (fname_[i] != '/')) i--;
    if (i) {
      i++;
      strcpy(fname_+i,fname);
      fname=fname_;
    }
  }
  new_yyin = fopen( fname, "re" );
  if ( ! new_yyin ) {
    yyerror("cannot open includefile %s", fname);
    return;
  }
  c_printf("CONF: opened include file %s\n", fname);
  include_lines[include_stack_ptr] = line_count;
  include_stack[include_stack_ptr] = YY_CURRENT_BUFFER;
  include_stack_ptr++;
  include_fnames[include_stack_ptr] = strdup(fname);
  line_count = 1;
  yyin = new_yyin;
  include_stack[include_stack_ptr] = yy_create_buffer( yyin, YY_BUF_SIZE );
  yy_switch_to_buffer(include_stack[include_stack_ptr]);
  last_include = 0;
}

int yywrap(void)    /* this gets called at EOF of a parsed file */
{
  if (include_stack_ptr <= 0 ) return(1);
#if USE_LOOP_HANDLING
#if 1
  if (cachefile_nr(yyin) < CACHEFILES_MAX) return cachefile_wrap();
  if (yyin == dev_null_files[MACROFILE]) return macrofile_wrap();
#else
  if (macrobuffer) return macrofile_wrap();
  if (__loop_handling__ < 0) return cachefile_wrap();
#endif
#endif
  yy_switch_to_buffer(include_stack[include_stack_ptr-1] );
  fclose(include_stack[include_stack_ptr]->yy_input_file);
  yy_delete_buffer(include_stack[include_stack_ptr]);
  c_printf("CONF: closed include file %s\n", include_fnames[include_stack_ptr]);
  free(include_fnames[include_stack_ptr]);
  include_stack_ptr--;
  line_count=include_lines[include_stack_ptr];
  last_include = 0;
  return(0);
}


static void push__active(void)
{
  if (config_check_only>2)
    fprintf(stderr, "%03d: PUSH %d->%d %d >%s<\n", line_count, __active_i, __active_i+1, __active__, yytext);
  __active_stack[__active_i++]=__active__;
  if (__active_i > __active_max) {
    __active_i = __active_max;
    yyerror("Lexer block stack overflow, unmatching  if..else..endif");
  }
}

static void pop__active(void)
{
  if (config_check_only>2)
    fprintf(stderr, "%03d: POP %d<-%d %d >%s< %d\n", line_count, __active_i-1, __active_i, __active__, yytext, __loop_handling__);
  if (__active_i <=0) {
    yyerror("Lexer block stack underflow, unmatching  if..else..endif");
    return;
  }
  __active__=__active_stack[--__active_i];
}

void tell_lexer_if(int value)
{
  __active__ = value !=0;
}


#if USE_LOOP_HANDLING

/* ---------------------------------------------------------
 * yylex() wrapper
 *
 * Here the record/replay stuff is handled.
 * We need that in order to realize 'while' loops or such things
 */


#define SAVEBUFSIZE	0x4000
static char *savebuf = 0;
static int savebufsize = 0;
static int savebufwptr =0;


#define CACHEFILESIZE	(CACHEFILES_MAX * sizeof(struct cache_file))
static struct cache_file *cachefile = 0;
static int cachefilesize = 0;
static int cachefilewptr = -1;
static int cachefilecurrent = -1;

static void free_savebuffer(void);
static void free_cachefile_buffers(void);
static void close_cachefile_write(int stop);


OUR_YY_DECL
{
	int ret;

	ret = real_yylex(&yylval);

        if (__loop_handling__ >0) {
		/* we respawn yylex in a loop, while filling the cache files */
		while (__loop_handling__ >0) {
			ret = real_yylex(&yylval);
			if (ret == YY_NULL) {
				__loop_handling__ = 0;
				close_cachefile_write(savebufwptr);
				free_cachefile_buffers();
				free_savebuffer();
				yyerror("EOF while in loop, probably 'done' missing");
				return ret;
			}
		}
		/* and again, to get the first token again */
		ret = real_yylex(&yylval);
	}
	return(ret);
}

static void free_savebuffer(void)
{
	if (savebuf) free(savebuf);
	savebuf = 0;
	savebufsize = 0;
	savebufwptr = 0;
}

static void dumpout(const char *s, int len)
{
	if ((savebufwptr + len) > savebufsize) {
		do {
			savebufsize += SAVEBUFSIZE;
		} while ((savebufwptr + len) > savebufsize);
		if (!savebuf)
			savebuf = malloc(savebufsize);
		else	savebuf = realloc(savebuf, savebufsize);
	}
	memcpy(savebuf+savebufwptr, s, len);
	savebufwptr += len;
}


struct cache_file {
	int start;	/* point behind the loop begin statement */
	int stop;	/* point behind the loop end statement */
	int rptr;
	int parent;
	YY_BUFFER_STATE yybuffer;
	char *origfname;
	int  firstline;
	int  need_loop_back;
	int looplimit;
};

static void free_cachefile_buffers(void)
{
	int i;
	if (cachefile) {
		for (i=0; i <= cachefilewptr; i++)
			if (cachefile[i].origfname) free(cachefile[i].origfname);
		free(cachefile);
		cachefile = 0;
	}
	cachefilesize = 0;
	cachefilecurrent = cachefilewptr = -1;
}

static void create_cachefile(int start)
{
	struct cache_file *cf;

	cachefilewptr++;
	if (((cachefilewptr+1) * sizeof(struct cache_file)) > cachefilesize) {
		cachefilesize += CACHEFILESIZE;
		if (!cachefile)
			cachefile = malloc(cachefilesize);
		else	cachefile = realloc(cachefile, cachefilesize);
	}
	cf = cachefile + cachefilewptr;
	cf->start = start; 
	cf->stop = start;
	cf->rptr = start;
	cf->firstline = line_count;
	cf->origfname = 0;
#if 0
	/* NOTE: The 'include_stack_ptr > 0' sanity check may be needed
	   because of errors in do loops (to much 'done'), though this
	   would be for sure a syntax error in the config file, but we
	   got segfaults in the past and came not so far to report the
	   error. Hence, we have to check if the segfaults happen again.
	   Till then we leave the old code here in place.
			--Hans, 980614
	*/
	if (include_stack_ptr > 0 && include_fnames[include_stack_ptr])
#else
	if (include_fnames[include_stack_ptr])
#endif
		cf->origfname = strdup(include_fnames[include_stack_ptr]);
	cf->parent = cachefilecurrent;
	cachefilecurrent = cachefilewptr;
}

static void close_cachefile_write(int stop)
{
	if (!cachefile || cachefilecurrent <0)
		return;
	cachefile[cachefilecurrent].stop = stop;
	cachefilecurrent = cachefile[cachefilecurrent].parent;
}


static int cachefile_read(char *buf, int size, int cfile)
{
	struct cache_file *cf = cachefile+cfile;

	if (!cachefile || cachefilecurrent <0) return 0;
	if (cf->rptr+size >= cf->stop)
		size = cf->stop - cf->rptr;
	if (size <= 0) return 0;
	memcpy(buf, savebuf + cf->rptr, size);
	cf->rptr += size;
	return size;
}


static void enter_cachefile(int cfile)
{
	struct cache_file *cf;

	if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) {
		yyerror("Loops nested too deeply" );
		return;
	}
	if (!cachefile) {
		yyerror("mismatching loop begin/end \n");
		return;
	}
	cachefilecurrent = cfile;
	if (config_check_only>1)
		c_printf("CONF: opened cache file %d\n", cachefilecurrent);
	include_lines[include_stack_ptr] = line_count;
	include_stack[include_stack_ptr] = YY_CURRENT_BUFFER;
	include_stack_ptr++;
	cf = cachefile + cachefilecurrent;
	cf->looplimit = LOOP_LIMIT;
	cf->rptr = cf->start;
	if (cf->origfname)
		include_fnames[include_stack_ptr] = strdup(cf->origfname);
	else	include_fnames[include_stack_ptr] = 0;
	line_count = cf->firstline;
	dev_null_files[cachefilecurrent] = yyin = fopen("/dev/null", "re");
	include_stack[include_stack_ptr] = yy_create_buffer( yyin, cf->stop - cf->start +2);
	yy_switch_to_buffer(include_stack[include_stack_ptr]);
}


static int cachefile_wrap(void)
{
	struct cache_file *cf;
	int cfile;

	/* we come here from yywrap, when we got an EOF on the cache file */
	
	if (include_stack_ptr <= 0 || !cachefile || cachefilecurrent <0) return 0;
	cfile = cachefilecurrent;
	cf = cachefile + cfile;
	if (cf->need_loop_back) {
		if (--cf->looplimit <0) {
			yyerror("loop limit of %d loops exceeded\n", LOOP_LIMIT);
			cf->need_loop_back = 0;
		}
		else {
			cf->rptr = cf->start;
			line_count = cf->firstline;
			return(0);
		}
	}
	cachefilecurrent = cf->parent;
	yy_switch_to_buffer(include_stack[include_stack_ptr-1] );
	fclose(include_stack[include_stack_ptr]->yy_input_file);
	dev_null_files[cfile] = NULL;
	yy_delete_buffer(include_stack[include_stack_ptr]);
	free(include_fnames[include_stack_ptr]);
	if (config_check_only>1)
		c_printf("CONF: closed cache file %d\n", cfile);
	include_stack_ptr--;
	line_count=include_lines[include_stack_ptr];
	if (cfile >= 0) {
		/* when in inner loop, we have been executed from
		 * the copy, but the main cache file still is positioned
		 * directly behind the 'while__ ()' in the original.
		 * we have to skip this until 'done'.
		 * This can simply done by setting __active__=0,
		 * because the 'while__' has done an extra push__active()
		 * and the 'done' in the main cache file will do pop__active()
		 */
		__active__ = 0;
	}
	if (cachefilecurrent <0) {
		/* end of loop handling */
		__loop_handling__ = 0;
		free_cachefile_buffers();
		free_savebuffer();
	}
	return(0);
}

void tell_lexer_loop(int cfile, int value)
{
	__active__ = value !=0;
	if (!cachefile) return;
	if (__active__ && cachefilecurrent != cfile) {
		/* we have to open a deeper nested cache file */
		enter_cachefile(cfile);
	}
	cachefile[cfile].need_loop_back = __active__;
	return;
}


static int dump_input(int rulenum)
{
	static int lastchar = '\n';
	int savebufwptr_ = savebufwptr;
	int skip_action = 1;

	switch (rulenum) {
		case INCLUDE_RULE_NUM: {
			dumpout("#",1); /* comment out the include statement*/
			skip_action = 0;
			break;
		}
	}

	if (rulenum != COMMENT_RULE_NUM ) {
		if (rulenum == WHITESPACE_RULE_NUM) {
			if (lastchar != '\n') {
				dumpout(" ",1);
				lastchar = ' ';
			}
		}
		else {
			dumpout(yytext,yyleng);
			lastchar = yytext[yyleng-1];
		}
	}
	else {
		dumpout("#\n",2);
		lastchar = '\n';
		line_count++;
	}
	switch (rulenum) {
		case NEWLINE__RULE_NUM: {
			line_count++;
			break;
		}
		case INCLUDEFILE_RULE_NUM: {
			if (__next_ITEM_for_me__ == 2) {
				dumpout("\n",1);
				lastchar = '\n';
				skip_action = 0;
			}
			break;
		}
		case LOOP_RULE_NUM: {
			char buf[32];
			create_cachefile(savebufwptr_);
			sprintf(buf, "__yy__ %d, ", cachefilecurrent);
			dumpout(buf, strlen(buf));
			break;
		}
		case DONE_RULE_NUM: {
			close_cachefile_write(savebufwptr);
			if (cachefilecurrent <0) {
				/* finished all caching,
				 * starting working phase */
				__loop_handling__ = -1;
				enter_cachefile(0);
			}
		}
	}
	return skip_action;
}

static void enter_macrofile(char *variable)
{
	char *macrobuffer;
	if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) {
		yyerror("macrocall ... nested too deeply" );
		return;
	}
        macrobuffer = checked_getenv(variable);
	if (!macrobuffer) {
		yywarn("macro '%s' not found \n", variable);
		return;
	}
	if (config_check_only>1)
		c_printf("CONF: opened macro file %s\n", variable);
	include_lines[include_stack_ptr] = line_count;
	include_stack[include_stack_ptr] = YY_CURRENT_BUFFER;
	include_stack_ptr++;
	include_macbuf[include_stack_ptr] =
		include_fnames[include_stack_ptr] = strdup(macrobuffer);
	dev_null_files[MACROFILE] = yyin = fopen("/dev/null", "re");
	include_stack[include_stack_ptr] = yy_create_buffer( yyin, strlen(macrobuffer) +2);
	yy_switch_to_buffer(include_stack[include_stack_ptr]);
}

static int macrofile_wrap(void)
{
	/* we come her from yywrap, when we got an EOF on the macro file */
	
	if (include_stack_ptr <= 0) return 0;
	yy_switch_to_buffer(include_stack[include_stack_ptr-1] );
	fclose(include_stack[include_stack_ptr]->yy_input_file);
	dev_null_files[MACROFILE] = NULL;
	yy_delete_buffer(include_stack[include_stack_ptr]);
	free(include_macbuf[include_stack_ptr]);
	if (config_check_only>1)
		c_printf("CONF: closed macro file\n");
	include_stack_ptr--;
	line_count=include_lines[include_stack_ptr];
	return(0);
}


static int macrofile_read(char *buf, int size)
{
	int len;
	char *macroptr = include_fnames[include_stack_ptr];
	len = strlen(macroptr);
	if (!len) return 0;
	if (size > len) size = len;
	memcpy(buf, macroptr, size);
	include_fnames[include_stack_ptr] +=size;
	return size;
}

static int do_yy_input(char *buf, int max_size)
{
  int result;
  if (!yyin) {
    if (yy_vbuffer && yy_vbuffer[0]) {
      buf[(max_size)-1]=0;
      strncpy(buf,yy_vbuffer,max_size);
      if (buf[(max_size)-1]) {
        yy_vbuffer+=max_size;
        result=max_size;
      }
      else {
        result=strlen(buf);
        yy_vbuffer=0;
      }
    }
    else result=0;
  }
  else {
    if ( YY_CURRENT_BUFFER->yy_is_interactive ) {
      int c = getc( yyin );
      result = c == EOF ? 0 : 1;
      buf[0] = (char) c;
    }
    else {
      int cfile = cachefile_nr(yyin);
      if (cfile < CACHEFILES_MAX) {
        result = cachefile_read(buf, max_size, cfile);
      }
      else {
	if (yyin == dev_null_files[MACROFILE]) {
	  result = macrofile_read(buf, max_size);
	}
        else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) && ferror( yyin ) )
                  YY_FATAL_ERROR( "input in flex scanner failed" );
      }
    }
  }
  return result;
}

#endif /* USE_LOOP_HANDLING */
