%x NORMAL COMMENT QUOTE1 QUOTE2

%{

/*
 * $Header: /usr/build/vile/vile/filters/RCS/makefilt.l,v 1.49 2003/05/24 00:49:25 tom Exp $
 *
 * Filter to add vile "attribution" sequences to selected bits of Makefile
 *
 * This was based on a filter written by Pierre Dittgen (dittgen@univ-mlv.fr)
 * - T.Dickey
 */

#include <filters.h>

DefineFilter("make");

static char *Comment_attr;
static char *Error_attr;
static char *Ident2_attr;
static char *Keyword_attr;
static char *Preproc_attr;
static char *String_attr;

static int continued;

static void write_keyword(char *text, int size, int prepro, int caseless);
static void write_delimited(char *text, int size, int less, char *attr);
static void missing_quote(char *text, int length);

%}

SPACE		[ \t]

ID		[a-zA-Z_][a-zA-Z_0-9]+
NAME		[a-zA-Z_][a-zA-Z_0-9.-]+

MACRO		([$]?\$\{{ID}\}|[$]?\${ID}|[$]?\$[A-Za-z@!%^&*?<]|\${SPACE}*\([^\)]+\))

OPER		[+:?]?=

%%

<NORMAL>^{SPACE}*[!]{NAME}{SPACE}*	{ write_keyword(yytext, yyleng, 1, 1); }

<NORMAL>^{SPACE}*{NAME}{SPACE}*	{ write_keyword(yytext, yyleng, 1, 0); }

<NORMAL>^{SPACE}*([-.])?include{SPACE}* { write_keyword(yytext, yyleng, 1, 0); }

<NORMAL>^{SPACE}*[\.]{NAME}{SPACE}*:[:]? { write_keyword(yytext, yyleng, 0, 0); }

<NORMAL>^({SPACE}*{NAME}{SPACE}*|\\\n)+: { write_delimited(yytext, yyleng, 1, Keyword_attr); }

<NORMAL>^{SPACE}*{NAME}{SPACE}*{OPER}	{
				  int oper = 1;
				  if (strchr("+:?", yytext[yyleng - 2]) != 0)
				      ++oper;
				  write_delimited(yytext, yyleng, oper, Ident2_attr);
				}

<NORMAL>{MACRO}			{ WriteToken(Ident2_attr); }

<NORMAL>^{SPACE}\@		{ if (yyleng)
				    flt_puts(yytext, yyleng-1, "");
				  flt_puts("@", 1, String_attr); }
<NORMAL>\\{SPACE}+\n		{ WriteToken(Error_attr); continued = 1; }
<NORMAL>\\[\n]			{ WriteToken(String_attr); continued = 1; }
<NORMAL>[\n]			{ ECHO; continued = 0; }
<NORMAL>\\["']			{ WriteToken(String_attr); }
<NORMAL>\'			{ BeginQuote(QUOTE1, String_attr); }
<NORMAL>\"			{ BeginQuote(QUOTE2, String_attr); }

<NORMAL>"#"			{ BeginQuote(COMMENT, Comment_attr); }
<COMMENT>(\\.|\\\n|[^\n])*	{ flt_bfr_append(yytext, yyleng); }
<COMMENT>\n			{ FinishQuote(NORMAL); }

<QUOTE1,QUOTE2>{MACRO}		{ flt_bfr_embed(yytext, yyleng, Ident2_attr); }
<QUOTE1,QUOTE2>\\{SPACE}+\n	{ flt_bfr_embed(yytext, yyleng, Error_attr); }
<QUOTE1,QUOTE2>\\\n		{ flt_bfr_append(yytext, yyleng); }

<QUOTE1>(\\.|[^\n\'])		{ flt_bfr_append(yytext, yyleng); }
<QUOTE1>\'			{ FinishQuote(NORMAL); }

<QUOTE2>(\\.|[^\n\"])		{ flt_bfr_append(yytext, yyleng); }
<QUOTE2>\"			{ FinishQuote(NORMAL); }

<QUOTE1,QUOTE2>[\n]		{ missing_quote(yytext, yyleng); }

%%

/*
 * If we're looking for tabs, only tabs (optionally followed by spaces) will
 * do.
 */
static int
want_tabs(char *text, int size)
{
    int result = 0;
    int spcs = 0;
    int tabs = 0;
    int n;

    if (size > 0) {
	for (n = 0; n < size; ++n) {
	    if (text[n] == ' ') {
		if (!tabs)
			result = !continued;
		++spcs;
	    } else if (text[n] == '\t') {
		if (spcs)
			result = !continued;
		++tabs;
	    }
	}
	/* a little paranoia does not hurt */
	if ((spcs == FltOptions('t')) && !tabs)
	    result = 1;
    } else {
	result = 1;	/* should not happen */
    }
    return result;
}

static void
write_keyword(char *text, int size, int prepro, int caseless)
{
    int colon = 0;
    int before = 0;
    int after = 0;
    int save;
    int nospcs = 0;
    int notabs = 0;
    int tabs = 0;
    char *base = text;
    char *attr = "";

    while (size > 1 && text[size - 1] == ':') {
	text[--size] = 0;
	++colon;
    }

    while (isspace(CharOf(*text))) {
	if (*text == '\t')
	    tabs = 1;
	text++;
	size--;
	before++;
    }

    while (isspace(CharOf(text[size-1]))) {
	size--;
	after++;
    }

    save = text[size];
    text[size] = 0;
    attr = caseless
	 ? ci_keyword_attr(text)
	 : keyword_attr(text);
    /*
     * Some symbols cannot be preceded by tabs - we put those in the "premake"
     * table.
     */
    if ((attr == 0) && !continued && prepro) {
	set_symbol_table("premake");
	attr = caseless
	     ? ci_keyword_attr(text)
	     : keyword_attr(text);
	set_symbol_table(flt_name());
	notabs = (attr != 0);
	if (attr == 0)
	    nospcs = want_tabs(base, before);
    } else {
	nospcs = want_tabs(base, before);
    }
    text[size] = save;

    if (before)
	flt_puts(base, before, ((tabs && notabs) || nospcs) ? Error_attr : "");

    flt_puts(text, size, attr);

    if (after)
	flt_puts(text + size, after, "");

    while (colon--)
	flt_putc(':');
}

/*
 * string passed to this routine is in the format:
 *
 *    [<white>]<token>[<white>]
 */
static char *
write_delimited_unit(char *text, char **attr, char **gaps)
{
    char *pwhite, *ptoken, *panchor, *pnxt_unit;

    panchor = pwhite = text;
    while (isspace(CharOf(*pwhite))) {
	pwhite++;
    }
    if (pwhite > panchor)
	flt_puts(panchor, pwhite - panchor, *gaps);
    panchor = ptoken = pwhite;
    while (*ptoken && (! isspace(CharOf(*ptoken))))
	ptoken++;
    flt_puts(panchor, ptoken - panchor, *attr);
    if (isspace(CharOf(*ptoken))) {
	panchor = ptoken;
	pwhite	= ptoken + 1;  /* advance beyond initial white space byte */
	while (isspace(CharOf(*pwhite)))
	    pwhite++;
	flt_puts(panchor, pwhite - panchor, *gaps);
	pnxt_unit = pwhite;
    } else
	pnxt_unit = ptoken;
    return (pnxt_unit);
}

/*
 * string passed to this routine is in the format:
 *
 *    ([<white>]<token>[<white>]|\\\n)+<single delimiter>
 */
static void
write_delimited(char *text, int size, int less, char *attr)
{
    char *cp;
    char *gaps = "";
    int save = text[size - less];

    text[size - less] = '\0';
    cp       = text;
    do {
	if (*cp == '\\' && cp[1] == '\n') {
	    flt_puts(cp, 2, gaps);
	    cp += 2;
	} else
	    cp = write_delimited_unit(cp, &attr, &gaps);
    } while (*cp);

    text[size - less] = save;
    flt_puts(text + size - less, less, gaps);
}

static void
missing_quote(char *text, int length)
{
    char *save = String_attr;
    String_attr = Error_attr;
    flt_bfr_append(text, length);
    flt_bfr_finish();
    BEGIN(NORMAL);
    String_attr = save;
}

static void
init_filter(int before GCC_UNUSED)
{
}

static void
do_filter(FILE *inputs)
{
    yyin = inputs;

    Comment_attr = class_attr(NAME_COMMENT);
    Error_attr   = class_attr(NAME_ERROR);
    Ident2_attr  = class_attr(NAME_IDENT2);
    Keyword_attr = class_attr(NAME_KEYWORD);
    Preproc_attr = class_attr(NAME_PREPROC);
    String_attr  = class_attr(NAME_LITERAL);

    continued = 0;

    BEGIN(NORMAL);
    while (yylex() > 0) {
    }
    flt_bfr_error();
}
