%{
/* ----------------------------------------------------------------------- *
 *   
 *  master_tok.l - master map tokenizer.
 *
 *   Copyright 2006 Ian Kent <raven@themaw.net>
 *
 *   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, Inc., 675 Mass Ave, Cambridge MA 02139,
 *   USA; 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.
 *
 * ----------------------------------------------------------------------- */

#ifdef ECHO
# undef ECHO
#endif /* ECHO */
static void master_echo(void);	/* forward definition */
#define ECHO master_echo()

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "master_parse.tab.h"

/*
 * There are some things that need to be defined only if useing GNU flex.
 * These must not be defined if using standard lex
 */
#ifdef FLEX_SCANNER
int master_lineno;
#endif

int master_lex(void);
int master_wrap(void);

/* no need for yywrap() */
#define YY_SKIP_YYWRAP

#ifndef YY_STACK_USED
#define YY_STACK_USED 0
#endif
#ifndef YY_ALWAYS_INTERACTIVE
#define YY_ALWAYS_INTERACTIVE 0
#endif
#ifndef YY_NEVER_INTERACTIVE
#define YY_NEVER_INTERACTIVE 0
#endif
#ifndef YY_MAIN
#define YY_MAIN 0
#endif

void master_set_scan_buffer(const char *);
const char *line = NULL;

#ifdef FLEX_SCANNER
const char *line_pos = NULL;
const char *line_lim = NULL;
int my_yyinput(char *, int);

#undef YY_INPUT
#define YY_INPUT(b, r, ms) (r = my_yyinput(b, ms))
#else
#undef input
#undef unput
#define input()  (*(char *) line++)
#define unput(c) (*(char *) --line = c)
#endif

#define BUFF_LEN	1024
char buff[BUFF_LEN];
char *bptr;
char *optr = buff;
unsigned int tlen;

%}

%option nounput

%x PATHSTR MAPSTR DNSTR OPTSTR OCTAL

WS		[[:blank:]]+
OPTWS		[[:blank:]]*
NL		\r?\n
CONT		\\\n{OPTWS}

OPTIONSTR	([\-]?([[:alpha:]_]([[:alnum:]_\-])*(=(\"?([[:alnum:]_\-\:\.])+\"?))?)+)
MACROSTR	(-D{OPTWS}([[:alpha:]_]([[:alnum:]_\-\.])*)=([[:alnum:]_\-\.])+)
SLASHIFYSTR	(--(no-)?slashify-colons)
NUMBER		[0-9]+
OCTALNUMBER	[0-7]+

DNSERVSTR1	([[:alpha:]][[:alnum:]\-.]*(:[0-9]+)?:)
DNSERVSTR2	(\[([[:xdigit:]]:.)+\](:[0-9]+)?:)
DNSERVSTR3	(\/\/[[:alpha:]][[:alnum:]\-.]*(:[0-9]+)?\/)
DNSERVSTR4	(\/\/\[([[:xdigit:]]:.)+\](:[0-9]+)?\/)
DNSERVSTR5	(([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}(:[0-9]+)?:)
DNSERVSTR6	(\/\/([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}(:[0-9]+)?\/)
DNSERVERSTR	({DNSERVSTR1}|{DNSERVSTR2}|{DNSERVSTR3}|{DNSERVSTR4}|{DNSERVSTR5}|{DNSERVSTR6})

AT_CN		([cC][[nN])
AT_NMN		([nN][iI][sS][Mm][aA][pP][Nn][aA][mM][eE])
AT_AMN		([aA][uU][tT][oO][mM][oO][uU][nN][tT][Mm][aA][pP][Nn][aA][mM][eE])
AT_OU		([oO][[uU])
AT_DC		([dD][[cC])
AT_O		([oO])
AT_C		([cC])
AT_L		([lL])
DNATTRSTR	({AT_CN}|{AT_NMN}|{AT_AMN}|{AT_OU}|{AT_DC}|{AT_O}|{AT_C}|{AT_L})
DNNAMESTR1	([[:alnum:]_.\- ]+)
DNNAMESTR2	([[:alnum:]_.\-]+)

INTMAP		(-hosts|-null)
MULTI		((multi)(,(sun|hesiod))?(:{OPTWS}|{WS}))
MULTISEP	([\-]{2}[[:blank:]]+)
MTYPE		((file|program|exec|sss|yp|nis|nisplus|ldap|ldaps|hesiod|userdir)(,(sun|hesiod|amd))?(:{OPTWS}|{WS}))


OPTTOUT		(-t{OPTWS}|-t{OPTWS}={OPTWS}|--timeout{OPTWS}|--timeout{OPTWS}={OPTWS})
OPTNTOUT	(-n{OPTWS}|-n{OPTWS}={OPTWS}|--negative-timeout{OPTWS}|--negative-timeout{OPTWS}={OPTWS})

MODE		(--mode{OPTWS}|--mode{OPTWS}={OPTWS})

%%

<INITIAL>{
	{NL} | 
	\x00 {
		if (optr != buff) {
			*optr = '\0';
			strcpy(master_lval.strtype, buff);
			return NILL;
		}
	}

	#.*  { return COMMENT; }

	"/" {
		if (optr != buff) {
			*optr = '\0';
			strcpy(master_lval.strtype, buff);
			return NILL;
		}
		BEGIN(PATHSTR);
		bptr = buff;
		yyless(0);
	}

	.    { *optr++ = *master_text; }
}

<PATHSTR>{
	\x00 {
		BEGIN(INITIAL);
		*bptr++ = *master_text;
		strcpy(master_lval.strtype, buff);
		return NILL;
	}

	\\.  { *bptr++ = *(master_text + 1); }
	\"   {
		BEGIN(INITIAL);
		*bptr++ = *master_text;
		*bptr = '\0';
		strcpy(master_lval.strtype, buff);
		return QUOTE;
	}

	{WS} {
		BEGIN(MAPSTR);
		*bptr = '\0';
		strcpy(master_lval.strtype, buff);
		bptr = buff;
		memset(buff, 0, BUFF_LEN);
		return(PATH);
	}

	<<EOF>> {
		BEGIN(INITIAL);
		*bptr = '\0';
		strcpy(master_lval.strtype, buff);
		return(PATH);
	}

	{NL} {
		BEGIN(INITIAL);
		*bptr = '\0';
		strcpy(master_lval.strtype, buff);
		return PATH;
	}

	.    { *bptr++ = *master_text; }
}

<MAPSTR>{
	{OPTWS}\\\n{OPTWS} {}

	{MULTI} {
		tlen = master_leng - 1;
		if (bptr != buff && isblank(master_text[tlen])) {
			/*
			 * We can't handle unescaped white space in map names
			 * so just eat the white space. We always have the
			 * "multi" at the beginning of the string so the while
			 * will not fall off the end.
			 */
			while (isblank(master_text[tlen - 1]))
				tlen--;
			strncat(buff, master_text, tlen);
			bptr += tlen;
			yyless(tlen);
		} else {
			strcpy(master_lval.strtype, master_text);
			return(MULTITYPE);
		}
	}

	{MTYPE} |
	{MTYPE}/{DNSERVERSTR}{DNATTRSTR}= |
	{MTYPE}/{DNATTRSTR}= {
		tlen = master_leng - 1;
		if (bptr != buff && isblank(master_text[tlen])) {
			/*
			 * We can't handle unescaped white space in map names
			 * so just eat the white space. We always have the
			 * maptype keyword at the beginning of the string so
			 * the while will not fall off the end.
			 */
			while (isblank(master_text[tlen - 1]))
				tlen--;
			strncat(buff, master_text, tlen);
			bptr += tlen;
			yyless(tlen);
		} else {
			strcpy(master_lval.strtype, master_text);
			return(MAPTYPE);
		}
	}

	{MULTISEP} { return(DDASH); }

	":"	{ return(COLON); }

	"-hosts" {
		BEGIN(OPTSTR);
		strcpy(master_lval.strtype, master_text);
		return MAPHOSTS;
	}

	"-null" {
		BEGIN(OPTSTR);
		strcpy(master_lval.strtype, master_text);
		return MAPNULL;
	}

	"-xfn" {
		/*
		 * The X/Open Federated Naming service isn't supported
		 * and the parser will call YYABORT() when it sees the
		 * MAPXFN token so we must set the start state to the
		 * INITIAL state here for the next yylex() call.
		 */
		BEGIN(INITIAL);
		strcpy(master_lval.strtype, master_text);
		return MAPXFN;
	}

	"//" {
		BEGIN(DNSTR);
		yyless(0);
	}

	{DNSERVERSTR}{DNATTRSTR}= {
		BEGIN(DNSTR);
		yyless(0);
	}

	{DNATTRSTR}= {
		BEGIN(DNSTR);
		yyless(0);
	}

	{OPTWS}/{NL} {
		BEGIN(INITIAL);
		*bptr = '\0';
		strcpy(master_lval.strtype, buff);
		bptr = buff;
		return(MAPNAME);
	}

	\\. { *bptr++ = *(master_text + 1); }

	{WS} {
		BEGIN(OPTSTR);
		*bptr = '\0';
		strcpy(master_lval.strtype, buff);
		bptr = buff;
		return(MAPNAME);
	}

	{NL} |
	\x00 {
		BEGIN(INITIAL);
		*bptr = '\0';
		strcpy(master_lval.strtype, buff);
		return(MAPNAME);
	}

	<<EOF>> {
		BEGIN(INITIAL);
		*bptr = '\0';
		strcpy(master_lval.strtype, buff);
		return(MAPNAME);
	}

	.	{ *bptr++ = *master_text; }
}

<DNSTR>{
	{OPTWS}\\\n{OPTWS} {}

	{DNSERVERSTR} {
		strcpy(master_lval.strtype, master_text);
		return DNSERVER;
	}

	{DNATTRSTR}/"=" {
		strcpy(master_lval.strtype, master_text);
		return DNATTR;
	}

	"=" {
		return EQUAL;
	}

	{DNNAMESTR1}/","{DNATTRSTR}"=" {
		strcpy(master_lval.strtype, master_text);
		return DNNAME;
	}

	{DNNAMESTR2} {
		strcpy(master_lval.strtype, master_text);
		return DNNAME;
	}

	{OPTWS}","{OPTWS} {
		return COMMA;
	}

	{WS}"=" |
	"="{WS} {
		BEGIN(INITIAL);
		strcpy(master_lval.strtype, master_text);
		return SPACE;
	}

	{WS}    { BEGIN(OPTSTR); }

	{NL} |
	\x00	{ BEGIN(INITIAL); }

	<<EOF>> { BEGIN(INITIAL); }
}

<OPTSTR>{
	{OPTWS}\\\n{OPTWS} {}

	{MULTISEP} {
		BEGIN(MAPSTR);
		return(DDASH);
	}

	{OPTTOUT}/{NUMBER} { return(OPT_TIMEOUT); }

	{OPTNTOUT}/{NUMBER} { return(OPT_NTIMEOUT); }

	{NUMBER} {
		master_lval.longtype = atol(master_text);
		return(NUMBER);
	}

	-?symlink		{ return(OPT_SYMLINK); }
	-?nobind		{ return(OPT_NOBIND); }
	-?nobrowse		{ return(OPT_NOGHOST); }
	-?shared		{ return(OPT_SHARED); }
	-?slave			{ return(OPT_SLAVE); }
	-?private		{ return(OPT_PRIVATE); }
	-?strictexpire		{ return(OPT_STRICTEXPIRE); }
	-g|--ghost|-?browse	{ return(OPT_GHOST); }
	-v|--verbose		{ return(OPT_VERBOSE); }
	-d|--debug		{ return(OPT_DEBUG); }
	-w|--use-weight-only	{ return(OPT_USE_WEIGHT); }
	-r|--random-multimount-selection { return(OPT_RANDOM); }

	{MODE}/{OCTALNUMBER} {
		BEGIN(OCTAL);
		return(OPT_MODE);
	}

	{OPTWS}","{OPTWS}	{ return(COMMA); }

	{OPTWS} {}

	{SLASHIFYSTR} {
		strcpy(master_lval.strtype, master_text);
		return(OPTION);
	}

	{MACROSTR} {
		strcpy(master_lval.strtype, master_text);
		return(OPTION);
	}

	{OPTIONSTR} {
		strcpy(master_lval.strtype, master_text);
		return(OPTION);
	}

	"="	{
		strcpy(master_lval.strtype, master_text);
		return(EQUAL);
	}

	{WS}	{}
	{NL} |
	\x00 { BEGIN(INITIAL); }

	<<EOF>> { BEGIN(INITIAL); }
}

<OCTAL>{
	{OCTALNUMBER} {
		master_lval.longtype = strtoul(master_text, NULL, 8);
		return(OCTALNUMBER);
	}

	. { BEGIN(OPTSTR); yyless(0); }
}

%%

#include "automount.h"

int master_wrap(void)
{
	return 1;
}

static void master_echo(void)
{
	logmsg("%s", master_text);
	return;
}

#ifdef FLEX_SCANNER

void master_set_scan_buffer(const char *buffer)
{
	memset(buff, 0, sizeof(buff));
	optr = buff;

	YY_FLUSH_BUFFER;

	line = buffer;
	line_pos = &line[0];
	/*
	 * Ensure buffer is 1 greater than string and is zeroed before
	 * the parse so we can fit the extra NULL which allows us to
	 * explicitly match an end of line within the buffer (ie. the
	 * need for 2 NULLS when parsing in memeory buffers).
	 */
	line_lim = line + strlen(buffer) + 1;
}

#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif

int my_yyinput(char *buffer, int max_size)
{
	int n = min(max_size, line_lim - line_pos);

	if (n > 0) {
		memcpy(buffer, line_pos, n);
		line_pos += n;
	}
	return n;
}

#else

void master_set_scan_buffer(const char *buffer)
{
	line = buffer;
}

#endif
