%{
/*
 * sgmlpre -- handle SGML conditionalization for SGML-tools
 *
 * by Eric S. Raymond <esr@thyrsus.com>, 3 Nov 1997
 *
 * Filter SGML according to conditionalizing markup.  Argument/value pairs
 * from the command line are matched against the attributes of <#if> and
 * <#unless> tags.  Spans between <#if>/</#if> are passed through unaltered
 * if there is no attribute mismatch; spans between <#unless></#unless> are
 * passed if there is at least one attribute mismatch.  An attribute mismatch
 * happens  if an attribute occurs in both the command-line arguments and the
 * tag, but the values do not match.  Value matching is by string equality,
 * except that "|" is interpreted as an alternation character.  Even if a span
 * is not passed through, its newlines are (this to avoid messing up the
 * line  numbers in error messages).
 *
 * This lexer requires flex.  Limitations; attribute names may only be
 * 256 chars long, values may be only 16384 (YY_BUF_SIZE) characters long.
 * Doesn't do checking that only </if> matches <if> and </unless> matches 
 * <unless> (that would need a stack and introduce another limit).
 */
#include <string.h>

#define TRUE	1
#define FALSE	0

static char **selections;	/* selection tokens */
static int nselections;		/* number of selections */
static nest;			/* how deep are we nested? */
static ifsense;			/* sense of last `if' or unless seen */
static firstoff;		/* nesting depth of first turnoff */
static char attribute[256];	/* last attribute scanned */

static int value_match(char *valu, char *against)
/* return TRUE if values match (handles alternation syntax) */
{
    char *vp, *ap;
    int vn, an;

#ifdef DEBUG
    printf("<%s, %s>", valu, against);
#endif /* DEBUG */

    for (vp = valu; *vp; vp += vn) 
    {
	vn = strcspn(vp, "|");
	for (ap = against; *ap; ap += an)
	{
	    an = strcspn(ap, "|");
	    if (an == vn && memcmp(ap, vp, an) == 0)
		return(TRUE);
	    if (ap[an] == '|')
		an++;
	} 
	if (vp[vn] == '|')
	    vn++;
    }

    return(FALSE);
}

static int restrictP(char *attr, char *valu)
/* does a given attribute/value pair turn off inclusion? */
{
    int	i;

#ifdef DEBUG
    printf("(%d, %s, %s)", nest, attr, valu);
#endif /* DEBUG */
    for (i = 0; i < nselections; i++)
    {
	int eqoffset = strcspn(selections[i], "=");

	if (strncasecmp(selections[i], attr, eqoffset) == 0)
	    if (!value_match(valu, selections[i] + eqoffset + 1))
		return(TRUE);
    }
	
    return(FALSE);
}

%}

ATTRIBUTE	[a-z][a-z0-9]*
WORD		[a-z][a-z0-9_]*
STRING		\"[^"]*\"
WS		[ \t\n]*

%option caseless
%x attrib val

%%
<INITIAL>\<\#if{WS}	{BEGIN(attrib); ifsense = TRUE;  ++nest;}
<INITIAL>\<\#unless{WS}	{BEGIN(attrib); ifsense = FALSE; ++nest;}

<INITIAL>"</#if>"|"</#unless>"	{if (firstoff == nest) firstoff = 0; --nest;}

<attrib>{ATTRIBUTE}		{strncpy(attribute,yytext,sizeof(attribute));}
<attrib>=			{BEGIN(val);}
<attrib>\>			{BEGIN(INITIAL);}

<val>{WORD}			{
				    if (restrictP(attribute, yytext) == ifsense)
					firstoff = nest;
				    BEGIN(attrib);
				}
<val>{STRING}			{
				    yytext[strlen(yytext)-1]='\0';
				    if (restrictP(attribute, yytext+1) == ifsense)
					firstoff = nest;
				    BEGIN(attrib);
				}
<val>\>				{
				    fprintf(stderr,
					"sgmlpre: > where value expected\n");
				    exit(1);
				}

<INITIAL>.			{
				    if (!firstoff && yytext[0] != '\n')
					putchar(yytext[0]);
				}

%%
#include <string.h>

int yywrap() {exit(0);};

main(int argc, char *argv[])
{
    int i;

    selections = argv + 1;
    nselections = argc - 1;

    for (i = 0; i < nselections; i++)
        if (strchr(selections[i], '=') == 0)
	{
	    fprintf(stderr, "sgmlpre: malformed argument %d\n", i);
	    exit(1);
	}

    yylex();
}

/* sgmlpre.l ends here */
