%{
/* 
**  mod_color.c -- Apache syntax coloring module
**
**  This module applies syntax coloring to source code.  At the
**  current time this is limited to C (and some C++) source code,
**  although additional source formats may be supported in the future.
**  Since this module performs a lexical analysis of the file,
**  it may not provide usable results on other types of files.
**
**  To play with this module first compile it into a
**  DSO file and install it into Apache's libexec directory 
**  by running:
**
**    $ apxs -c -i mod_color.c
**
**  Then activate it in Apache's srm.conf file add
**
**    #   srm.conf
**    AddHandler c-handler .c
**    AddHandler c-handler .h
**
**  Then after restarting Apache via
**
**    $ apachectl restart
**
**  you immediately can request the URL /%NAME and watch for the
**  output of this module. This can be achieved for instance by
**  installing the kernel source code, adding
**
**    #   srm.conf
**    Alias /kernel/ /usr/src/linux
**
**  and then viewing any of the C source files or headers.
**
**  This module is Copyright (C) 2000 Bear Giles; it is derived
**  from a cgi-bin syntax colorer written by the author in 1997.
**  It is released under the Apache license.
*/ 

#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"

#include <stdio.h>
#include <errno.h>
#include <time.h>

module color_module;

static const char rcsid[] = "$Id: mod_color.l,v 1.4 2000/03/06 17:32:33 bear Exp $";

inline size_t min (size_t a, size_t b)
{
	return (a < b) ? a : b;
}

inline size_t max (size_t a, size_t b)
{
	return (a > b) ? a : b;
}

static int synccwrap (void);

#include <string.h>

#define T_EOF			256
#define T_PREPROCESSOR	257
#define T_COMMENT		258
#define T_S_LITERAL		259
#define T_C_LITERAL		260
#define T_I_LITERAL		261
#define T_R_LITERAL		262
#define T_IDENTIFIER	263
#define T_KEYWORD		264
#define T_CPP_KEYWORD	265
#define T_ASSIGNMENT	266
#define T_OPERATOR		267

#define T_L_PAREN		268
#define T_R_PAREN		269
#define T_L_BRACE		270
#define T_R_BRACE		271
#define T_L_BRACKET		272
#define T_R_BRACKET		273
#define T_SEMICOLON		274
#define	T_COMMA			275

#define T_NEWLINE		276
#define T_WHITESPACE	277
#define T_LEADSPACE		278

#define T_BACKGROUND    279
#define T_TEXT    	    280

#define T_STORAGE		281
#define T_POSIX_IDENTIFIER	282
#define T_STD_IDENTIFIER	283

#define T_SEPARATOR     284
#define T_DEFINITION    285
#define T_RULE          286

#define	T_LAST			T_RULE

typedef struct {
	pool *p;
	int enable;
	char *color[T_LAST - T_EOF + 1];
	char *style[T_LAST - T_EOF + 1];
	char *face[T_LAST - T_EOF + 1];
} color_dir_config;

static color_dir_config *cfg = NULL;

static int line_num = 1;

#define BUFFER_SIZE	8096
static char buffer[BUFFER_SIZE+1];
static size_t bufcnt, len;

static int mode = 0;

static int depth = 0;
static int state = 0;

static int brace_depth = 0;

%}

begin_code		^\%\{
end_code		^\%\}

d				[1-9][[:digit:]]*
o				0[0-7]*
h				0[x|X]([[:xdigit:]])*

integer			({o}|{d}|{h})

real			({d}+|{d}+\.{d}*|{d}*\.{d}+)([eE][-+]?{d}+)?[fF]?

ident1			[[:alpha:]]*
ident2			[[:alpha:]]*[[:alnum:]|_]*
ident3			_+[[:alpha:]]*
ident4			_+[[:alpha:]]*[[:alnum:]|_]*
identifier		({ident1}|{ident2}|{ident3}|{ident4})

whitespace		[[:blank:]]+
newline			\n

structop		(\-\>|\.\|::)
relop			==|!=|<=|<|>=|>
arithop			(\+|-|\*|\/|%)
logop			(&&)|(\|\|)|\!
bitop			(&)|(\|)|\^|~|<<|>>
triop			(\?|:)
incop			(\+\+|--)

lparen			\(
rparen			\)
lbrace			\{
rbrace			\}
lbracket		\[
rbracket		\]
semicolon		\;
comma			\,

state			^\<[[:alnum:]_]+\>
leading_space	^[ \t]+
pattern			[[:alnum:]_]+
lexop			^%[[:alnum:]]+
line			[^\n%{'"/]+

lex_line		[^\n/]+
lex_pattern		[^[:space:]]+

perf_token		^[^[:space:],]+
perf_rest		,[^\n]+

%x c_comment cc_comment string chr preprocessor
%x lex_definitions lex_rules
%x yacc_definitions yacc_rules
%x gperf_definitions gperf_rules
%%

%{
#define YY_USER_INIT	{ switch (mode) { \
	case lex_definitions: BEGIN (mode); break; \
	case yacc_definitions: BEGIN (mode); break; \
	case gperf_definitions: BEGIN (mode); break; \
	} }
%}

	int start_line;

	/*	**************************
	 *	We have a large amount of glue for yacc, lex and perf.
	 *	Despite initial appearances, this doesn't modify the
	 *	C lexical scanner by much - but keeping track of the 
	 *	"start state" can be a real pain.
	 *	**************************	*/
<lex_definitions>
	{
	\%\%		{
				mode = lex_rules;
				BEGIN (mode);
				return T_SEPARATOR;
				}

	{begin_code}	{
				mode |= 0x4000;
				BEGIN (0);
				depth = 0;
				return T_SEPARATOR;
				}

	{lexop}		{ return T_DEFINITION; }

	"/*"		|
	"//"		{
				BEGIN (0);
				yyless (0);
				}

				/* this should only be a comment! */
	{leading_space}	{ return T_WHITESPACE; }

				/* for now, we don't differentiate within line */
	"/"			|
	{lex_line}	{ return T_DEFINITION; }

	{newline}	{ return T_NEWLINE; }

	.			yyerror ("startled");
	}

<lex_rules>
	{
	\%\%		{
				mode = 0;
				BEGIN (0);
				return T_SEPARATOR;
				}

	{state}		{
				state++;
				return T_RULE;
				}

	{lbrace}	{
				if (state == 1) {
					state++;
					return T_RULE;
				}
				else {
					depth = 0;
					BEGIN (0);
					yyless (0);
				}
				}

	{rbrace}	{
				if (state == 2) {
					state = 0;
				}
				return T_RULE;
				}

	{leading_space}	{
				if (state == 0)
					BEGIN (0);
				return T_WHITESPACE;
				}

	{begin_code}	{
				mode |= 0x4000;
				BEGIN (0);
				depth = 0;
				return T_SEPARATOR;
				}

	{newline}	{ return T_NEWLINE; }

	"/*"		|
	"//"		{
				BEGIN (0);
				yyless(0);
				}

	{lex_pattern}	{
				BEGIN (0);
				return T_RULE;
				}
	}

<yacc_definitions>
	{
	\%\%		{
				mode = yacc_rules;
				BEGIN (yacc_rules);
				return T_SEPARATOR;
				}

	{begin_code}	{
				mode |= 0x4000;
				BEGIN (0);
				depth = 0;
				return T_SEPARATOR;
				}

	{lbrace}	|
	"\'"		|
	"\""		|
	"/*"		|
	"//"		{
				depth = 0;
				BEGIN (0);
				yyless (0);
				}

				/* this is also yacc-op */
	{lexop}		{ return T_DEFINITION; }

				/* for now, we don't differentiate within line */
	{line}		{ return T_DEFINITION; }

	{whitespace} { return T_WHITESPACE; }

	{newline}	{ return T_NEWLINE; }

	.			yyerror ("startled");
	}

<yacc_rules>
	{
	\%\%		{
				mode = 0;
				BEGIN (0);
				return T_SEPARATOR;
				}

	{lbrace} 	|
	"\'"		|
	"\""		|
	"/*"		|
	"//"		{
				depth = 0;
				BEGIN (0);
				yyless (0);
				}

	":"			|
	"|"			|
	";"			|
	"%prec"		|
	{pattern}	{ return T_RULE; }

	{whitespace} { return T_WHITESPACE; }

	{newline}	{ return T_NEWLINE; }

	.			yyerror ("startled");

	}

<gperf_definitions>
	{
	\%\%		{
				mode = gperf_rules;
				BEGIN (gperf_rules);
				return T_SEPARATOR;
				}

	{begin_code}	{
				mode |= 0x4000;
				BEGIN (0);
				depth = 0;
				return T_SEPARATOR;
				}

	"struct"	{	/* should be the only thing, besides a code block */
				depth = 0;
				BEGIN (0);
				yyless (0);
				}

	{whitespace} { return T_WHITESPACE; }

	{newline}	{ return T_NEWLINE; }

	.			yyerror ("startled");
	}

<gperf_rules>
	{
	\%\%		{
				mode = 0;
				brace_depth = 0;
				BEGIN (0);
				return T_SEPARATOR;
				}

	/* we know that the first item *must* be a string.  */
	{perf_token} { 
				start_line = line_num;
				strncpy (buffer, yytext, yyleng);
				bufcnt = yyleng;
				buffer[bufcnt] = '\0';
				return T_S_LITERAL;
				}

	/* the rest of the items must be elements of the structure */
	{perf_rest}	{ brace_depth = 1; BEGIN (0); yyless (0); }

	{newline}	{ return T_NEWLINE; }
	}

{end_code}	{
			mode &= ~0x4000;
			switch (mode) {
				case lex_definitions:
				case lex_rules:
				case yacc_definitions:
				case yacc_rules:
				case gperf_definitions:
				case gperf_rules:
					BEGIN (mode);
					break;
			 }
			depth = 0;
			return T_SEPARATOR;
			}

"|"			{
			if (mode == lex_rules) return T_RULE;
			REJECT;
			}


	/*	**************************
	 *	Check for start of preprocessor directive,
	 *	character string, character literal, or comment.
	 *	**************************	*/
^#					|
^{whitespace}+\#	{	/* for Gnu extensions */
		start_line = line_num;
		bufcnt = min (yyleng, BUFFER_SIZE);
		strncpy (buffer, yytext, bufcnt);
		buffer[bufcnt] = '\0';
		BEGIN (preprocessor);
		}
"\""	{
		start_line = line_num;
		bufcnt = 0;
		buffer[bufcnt] = '\0';
		BEGIN (string);
		}
"\'"	{ 
		start_line = line_num;
		bufcnt = 0;
		buffer[bufcnt] = '\0';
		BEGIN (chr);
		}
"//"	{
		start_line = line_num;
		bufcnt = min (yyleng, BUFFER_SIZE);
		strncpy (buffer, yytext, bufcnt);
		buffer[bufcnt] = '\0';
		BEGIN (cc_comment); 
		}
"/*"	{ 
		start_line = line_num;
		bufcnt = min (yyleng, BUFFER_SIZE);
		strncpy (buffer, yytext, bufcnt);
		buffer[bufcnt] = '\0';
		BEGIN (c_comment);
		}


	/*	**************************
	 *	Munch on preprocessor directives
	 *	**************************	*/
<preprocessor>
	{
	\\{newline}	|  /* continue to next line */
	\\.			|  /* escape next character */
	[^\n\\]+	{  /* normal text */
				len = min (BUFFER_SIZE - bufcnt, yyleng);
				strncpy (&buffer[bufcnt], yytext, len);
				buffer[bufcnt += len] = '\0';
				}

				/* terminator */
	{newline}	{
				yyless (0);
				BEGIN (INITIAL);
				return (T_PREPROCESSOR);
				}

				/* errors */
	<<EOF>>		{
				/* yyterminate(); */
				return (T_PREPROCESSOR);
				}
	}

	/*	**************************
	 *	Munch on string literals
	 *	**************************	*/
<string>
	{
	\\{newline}	|  /* continue to next line */
	\\.			|  /* escape next character */
	[^\n\"\\]+	{  /* normal text */
				len = min (BUFFER_SIZE - bufcnt, yyleng);
				strncpy (&buffer[bufcnt], yytext, len);
				buffer[bufcnt += len] = '\0';
				}

				/* terminator */
	"\""		{
				if (mode == yacc_definitions && depth == 0)
					BEGIN (mode);
				else if (mode == yacc_rules && depth == 0)
					BEGIN (mode);
				else
					BEGIN (INITIAL);
				return (T_S_LITERAL);
				}

				/* errors */
	{newline}	yyerror ("unterminated string; winging it");
	<<EOF>>		{
				yyerror ("unterminated string at EOF");
				yyterminate();
				}
	}
	
	/*	**************************
	 *	Munch on character literals
	 *	**************************	*/
<chr>
	{
	\\{newline}	|  /* continue to next line */
	\\.			|  /* escape next character */
	[^\n\'\\]+	{  /* normal text */
				len = min (BUFFER_SIZE - bufcnt, yyleng);
				strncpy (&buffer[bufcnt], yytext, len);
				buffer[bufcnt += len] = '\0';
				}

				/* terminator */
	"\'"		{
				if (mode == yacc_definitions && depth == 0)
					BEGIN (mode);
				else if (mode == yacc_rules && depth == 0)
					BEGIN (mode);
				else
					BEGIN (INITIAL);
				return (T_C_LITERAL);
				}

				/* error */
	{newline}	yyerror ("unterminated string; winging it");
	<<EOF>>		{
				yyerror ("unterminated character at EOF");
				yyterminate();
				}
	}

	/*	**************************
	 *	Munch on C-style comments
	 *	**************************	*/
<c_comment>
	{
					/* terminator */
	"*"+"/"			{ 
					len = min (BUFFER_SIZE - bufcnt, yyleng);
					strncpy (&buffer[bufcnt], yytext, len);
					buffer[bufcnt += len] = '\0';
					if (depth > 0) {
						BEGIN (INITIAL);
					}
					else {
						switch (mode) {
						case lex_rules:
						case yacc_definitions:
						case yacc_rules:
						case gperf_rules:
						case gperf_definitions:
							BEGIN (mode);
							break;
						default:
							BEGIN (0);
						}
					}

					return (T_COMMENT);
					}

					/* normal text */
	[^*\n]*			|
	[^*\n]*\n		|
	"*"+[^*/\n]*	|
	"*"+[^*/\n]*\n	{
					len = min (BUFFER_SIZE - bufcnt, yyleng);
					strncpy (&buffer[bufcnt], yytext, len);
					buffer[bufcnt += len] = '\0';
					}
	}


	/*	**************************
	 *	Munch on C++-style comments
	 *	**************************	*/
<cc_comment>
	{
	[^\n]*/\n	{
				len = min (BUFFER_SIZE - bufcnt, yyleng);
				strncpy (&buffer[bufcnt], yytext, len);
				buffer[bufcnt += len] = '\0';
				if (depth > 0) {
					BEGIN (INITIAL);
				}
				else {
					switch (mode) {
					case lex_rules:
					case yacc_definitions:
					case yacc_rules:
					case gperf_rules:
					case gperf_definitions:
						BEGIN (mode);
						break;
					default:
						BEGIN (0);
					}
				}
				return (T_COMMENT);
				}
	}

	/*	**************************
	 *	Recognize keywords.  We check for _all_
	 *	keywords, to check for old code which will
	 *	not compile with newer compilers.
	 *	**************************	*/
break		|	/* K&R keywords */
case		|
continue	|
default		|
do			|
else		|
for			|
if			|
return		|
switch		|
while		|
entry		|	/* depreciated K&R keywords */
fortran		|
goto		|
inline		return T_KEYWORD;

char		|
double		|
extern		|
float		|
int			|
long		|
register	|
short		|
sizeof		|
static		|
struct		|
typedef		|
union		|
unsigned	|
auto		|	/* depreciated K&R keywords */
const		|	/* additional ANSI C90 keywords */
enum		|
mutable		|	
signed		|
void		|
volatile	|
bool		|	/* additional ANSI C9X keywords */
false		|
true		return T_STORAGE;

catch		|	/* additional C++ keywords */
class		|
private		|
protected	|
public		|
raise		|
template	|
virtual		return T_CPP_KEYWORD;

			/* we could also check for asm, far, and near */

fd_set		|	/* additional storage classes commonly defined by POSIX */
dev_t		|	/* these are defined for syntax coloring */
ino_t		|
mode_t		|
nlink_t		|
off_t		|
pid_t		|
uid_t		|
gid_t		|
daddr_t		|
size_t		|
ssize_t		|
ptrdiff_t	|
time_t		|
clock_t		|
caddr_t		return T_POSIX_IDENTIFIER;

u_char		|	/* BSD-type declarations */
u_short		|
u_int		|
u_long		return T_STD_IDENTIFIER;

unchar		|	/* sysv-type declarations */
ushort		|
uint		|
ulong		return T_STD_IDENTIFIER;

	/*	**************************
	 *	Recognize operators
	 *	**************************	*/
{arithop}=		|
{logop}=		|
{bitop}=		|
=				return (T_ASSIGNMENT);

{structop}		|
{relop}			|
{arithop}		|
{incop}			|
{logop}			|
{bitop}			|
{triop}			return (T_OPERATOR);

	/*	**************************
	 *	Count nesting objects.
	 *	**************************	*/
{lparen}		return T_L_PAREN;
{rparen}		return T_R_PAREN;
{lbrace}		{ depth++; return T_L_BRACE; }
{rbrace}		{ 
				if (--depth == 0) {
					switch (mode) {
					case lex_definitions : BEGIN (lex_definitions);  break;
					case lex_rules       : BEGIN (lex_rules);        break;
					case yacc_definitions: BEGIN (yacc_definitions); break;
					case yacc_rules      : BEGIN (yacc_rules);       break;
					}
				 }
				return T_R_BRACE;
				}

{lbracket}		return T_L_BRACKET;
{rbracket}		return T_R_BRACKET;
{semicolon}		{
				if (depth == 0 && mode == gperf_definitions)
					BEGIN (mode);
				return T_SEMICOLON;
				}
{comma}			return T_COMMA;

	/*	**************************
	 *	Wrap it all up.
	 *	**************************	*/

{integer}		return (T_I_LITERAL);
{real}			return (T_R_LITERAL);

{identifier}	return (T_IDENTIFIER);

{newline}		{ 
				if (depth == 0) {
					switch (mode) {
				 	case lex_definitions:
				 	case lex_rules:
				 	case gperf_rules:
						BEGIN (mode);
						break;
					}
				 }
				return T_NEWLINE;
				}

\$\$			|
\${integer}		{
				if (mode != yacc_rules) REJECT;
				return T_IDENTIFIER;
				}

^{whitespace}*/\n	return T_LEADSPACE;
{whitespace}	return T_WHITESPACE;

.				yyerror ("startled");

<<EOF>>			yyterminate();

%%
void action_comment (request_rec *r);
void action_preprocessor (request_rec *r);
void action_str_literal (request_rec *r);
void action_chr_literal (request_rec *r);
void action_int_literal (request_rec *r);
void action_fp_literal (request_rec *r);
void action_identifier (request_rec *r);
void action_keyword (request_rec *r);
void action_operator (request_rec *r);
void action_assignment (request_rec *r);
void action_glue (request_rec *r, int);
void action_whitespace (request_rec *r, int);
void action_grammar (request_rec *r, int);
void color (request_rec *r, int s, const char *str);

void prologue (request_rec *r);
void epilogue (request_rec *r);
void emit (request_rec *r, const char *, int, 
			const char *, const char *, const char *);
void dump_strings (request_rec *r);
void dump_identifiers (request_rec *r);

static int comment_count = 0;
static int ident_count = 0;
static int str_count = 0;
static int chr_count = 0;
static int int_count = 0;
static int float_count = 0;

static int paren_count = 0;
static int brace_count = 0;
static int bracket_count = 0;

static int paren_depth = 0;
/* static int brace_depth = 0; */
static int bracket_depth = 0;

static int statements = 0;

int pos = 0;
int tabstop = 4;
char *filename = NULL;

struct StrTable {
	int line_num;
	char *value;
	};

#define MAX_STRINGS 2000
struct StrTable strTable[MAX_STRINGS];
int strCount = 0;

struct IdentTable {
	int line_num;
	char *value;
	};

#define MAX_IDENTIFIERS 200
struct IdentTable identTable[MAX_IDENTIFIERS];
int identCount = 0;

int yyerror (const char * message)
	{
	printf ("%d: %s near symbol '%s'\n", line_num, message, yytext);
	}

void action_preprocessor (request_rec *r)
	{ 
	color (r, T_PREPROCESSOR, buffer);
	}

void action_comment (request_rec *r)
	{
	comment_count++;
	color (r, T_COMMENT, buffer);
	}

void action_int_literal (request_rec *r)
	{
	int_count++;
	color (r, T_I_LITERAL, yytext);
	}

void action_fp_literal (request_rec *r)
	{ 
	float_count++;
	color (r, T_R_LITERAL, yytext);
	}

void action_str_literal (request_rec *r)
	{ 
	if (strCount < MAX_STRINGS)
		{
		strTable[strCount].line_num = line_num;
		strTable[strCount].value = strdup (buffer);
		strCount++;
		}

	str_count++;
	color (r, T_S_LITERAL, buffer);
	}

void action_chr_literal (request_rec *r)
	{ 
	chr_count++;
	color (r, T_C_LITERAL, buffer);
	}

void action_identifier (request_rec *r)
	{ 
	if (identCount < MAX_IDENTIFIERS && brace_depth == 0 && paren_depth == 0)
		{
		identTable[identCount].line_num = line_num;
		identTable[identCount].value = strdup (yytext);
		identCount++;
		}
	ident_count++;
	color (r, T_IDENTIFIER, yytext);
	}

void action_keyword (request_rec *r)		{ color (r, T_KEYWORD, yytext); }
void action_operator (request_rec *r)		{ color (r, T_OPERATOR, yytext); }
void action_assignment (request_rec *r)		{ color (r, T_ASSIGNMENT, yytext); }

void action_glue (request_rec *r, int s)
	{
	switch (s)
		{
		case T_L_PAREN:
			paren_count++;
			paren_depth++;
			color (r, s, yytext);
			break;
		case T_R_PAREN:
			paren_depth--;
			color (r, s, yytext);
			break;
		case T_L_BRACE:
			brace_count++;
			brace_depth++;
			color (r, s, yytext);
			break;
		case T_R_BRACE:
			brace_depth--;
			color (r, s, yytext);
			break;
		case T_L_BRACKET:
			bracket_count++;
			bracket_depth++;
			color (r, s, yytext);
			break;
		case T_R_BRACKET:
			bracket_depth--;
			color (r, s, yytext);
			break;
		case T_SEMICOLON:
			statements++;
			color (r, s, yytext);
			break;
		case T_COMMA:
			color (r, s, yytext);
			break;
		default: ;
		}
	}

void action_whitespace (request_rec *r, int s)
	{
	color (r, s, yytext);
	}

/*
This routine can do anything special for yacc & lex grammars.
*/
void action_grammar (request_rec *r, int s)
	{
	color (r, s, yytext);
	}

/**************************************************************/

void color (request_rec *r, int s, const char *str)
	{
	int i;
	switch (s)
		{
		case T_R_PAREN:
			i = T_L_PAREN - T_EOF;
			emit (r, str, 0, cfg->style[i], cfg->color[i], cfg->face[i]);
			break;

		case T_R_BRACKET:
			i = T_L_BRACKET - T_EOF;
			emit (r, str, 0, cfg->style[i], cfg->color[i], cfg->face[i]);
			break;

		case T_R_BRACE:
			i = T_L_BRACE - T_EOF;
			emit (r, str, 0, cfg->style[i], cfg->color[i], cfg->face[i]);
			break;

		case T_COMMA:
			i = T_SEMICOLON - T_EOF;
			emit (r, str, 0, cfg->style[i], cfg->color[i], cfg->face[i]);
			break;

		case T_S_LITERAL:
			i = s - T_EOF;
			emit (r, str, '\"', cfg->style[i], cfg->color[i], cfg->face[i]);
			break;

		case T_C_LITERAL:
			i = s - T_EOF;
			emit (r, str, '\'', cfg->style[i], cfg->color[i], cfg->face[i]);
			break;

		case T_LEADSPACE:		
		case T_WHITESPACE:		
			emit (r, str, 0, 0, 0, 0);
			break;

		case T_NEWLINE:		
			emit (r, "\n", 0, 0, 0, 0);
			break;

		default:
			i = s - T_EOF;
			emit (r, str, 0, cfg->style[i], cfg->color[i], cfg->face[i]);
			break;
		}
	}


void prologue (request_rec *r)
	{
	ap_rprintf (r, "<body bgcolor=\"%s\" text=\"%s\">\r\n",
		cfg->color[T_BACKGROUND - T_EOF], cfg->color[T_TEXT - T_EOF]);
	ap_rputs ("<pre><code>\r\n", r);
	}


void epilogue (request_rec *r)
	{
	time_t now;

	now = time (NULL);

	ap_rputs ("<hr>\r\n", r);
	ap_rputs ("File: ", r);
	emit (r, r->filename, 0, 0, 0, 0);
	ap_rputs ("<br>\r\n", r);
	ap_rputs ("Date: ", r);
	ap_rputs (asctime (localtime (&r->finfo.st_mtime)), r);
	ap_rputs ("\
<!-- This file was automatically generated by Apache module mod_color -->\n", r);
	ap_rputs ("</body>\r\n", r);
	}


void emit (request_rec *r, const char *s, int delimiter,
	const char *style, const char *color, const char *face)
	{
	char ch;
	int count;

	if (face != NULL && *face != '\0')
		ap_rprintf (r, "<font face=\"%s\">", face);
	if (color != NULL && *color != '\0')
		ap_rprintf (r, "<font color=\"%s\">", color);
	if (style != NULL && *style != '\0')
		ap_rprintf (r, "<%s>", style);
	switch (delimiter)
		{
		case '\'':	ap_rputs ("\'", r); pos++; break;
		case '\"':	ap_rputs ("&quot;", r); pos++; break;
		}	
	while ((ch = *s++) != '\0')
		{
		switch (ch)
			{
			case '\"':	ap_rputs ("&quot;", r); pos++; break;
			case '<':	ap_rputs ("&lt;", r); pos++; break;
			case '>':	ap_rputs ("&gt;", r); pos++; break;
			case '\n':	
						if (style != NULL && *style != '\0')
							ap_rprintf (r, "</%s>", style);
						if (color != NULL && *color != '\0')
							ap_rputs ("</font>", r);
						if (face != NULL && *face != '\0')
							ap_rputs ("</font>", r);
						ap_rprintf (r, "\n<i>%4d</i>| ", line_num++);
						if (face != NULL && *face != '\0')
							ap_rprintf (r, "<font face=\"%s\">", face);
						if (color != NULL && *color != '\0')
							ap_rprintf (r, "<font color=\"%s\">", color);
						if (style != NULL && *style != '\0')
							ap_rprintf (r, "<%s>", style);
						pos = 0;
						break;
			case ' ':	ap_rputc (ch, r); pos++; break;
			case '\t':	count = tabstop - (pos % tabstop);
						ap_rprintf (r, "%*.*s", count, count,
					  		"                                          ");
						pos += count;
						break;
			default:	ap_rputc (ch, r); pos++;
			}
		}
	switch (delimiter)
		{
		case '\'':	ap_rputs ("\'", r); pos++; break;
		case '\"':	ap_rputs ("&quot;", r); pos++; break;
		}	
	if (style != NULL && *style != '\0')
		ap_rprintf (r, "</%s>", style);
	if (color != NULL && *color != '\0')
		ap_rputs ("</font>", r);
	if (face != NULL && *face != '\0')
		ap_rputs ("</font>", r);
	}


void dump_identifiers (request_rec *r)
	{
	int i;
	ap_rputs ("<hr>\r\n", r);
	ap_rputs ("<h2>Top-Level Identifiers</h2>\r\n", r);
	ap_rputs ("<table border>\r\n", r);
	ap_rputs ("<tr><th>Line</th><th>Identifier</th></tr>\r\n", r);
	for (i = 0; i < identCount; i++)
		{
		ap_rprintf (r, "<tr><td>%4d</td><td><code>", identTable[i].line_num);
		emit (r, identTable[i].value, 0, 0, 0, 0);
		ap_rprintf (r, "</code></td></tr>\r\n");
		}
	ap_rputs ("</table>\r\n", r);
	}



void dump_strings (request_rec *r)
{
	int i;
	ap_rputs ("<hr>\r\n", r);
	ap_rputs ("<h2>Strings</h2>\r\n", r);
	ap_rputs ("<table border>\r\n", r);
	ap_rputs ("<tr><th>Line</th><th>String</th></tr>\r\n", r);
	for (i = 0; i < strCount; i++) {
		ap_rprintf (r, "<tr><td>%4d</td><td><code>", strTable[i].line_num);
		emit (r, strTable[i].value, '\"', 0, 0, 0);
		ap_rprintf (r, "</code></td></tr>\r\n");
	}
	ap_rputs ("</table>\r\n", r);
}


static int synccwrap (void)
{
	return 1;
}

/*----------------------------------------------------------------*/

static void process_c (request_rec *r)
{
	int s;

	pos = 0;
	line_num = 1;
	prologue (r);
	ungetc ('\n', yyin);
	while ((s = yylex ()) > 0)
		{
		switch (s)
			{
			case T_COMMENT:		action_comment (r);			break;
			case T_PREPROCESSOR:action_preprocessor (r);	break;
			case T_S_LITERAL:	action_str_literal (r);		break;
			case T_C_LITERAL:	action_chr_literal (r);		break;
			case T_I_LITERAL:	action_int_literal (r);		break;
			case T_R_LITERAL:	action_fp_literal (r);		break;
			case T_IDENTIFIER:	action_identifier (r);		break;
			case T_KEYWORD:
			case T_CPP_KEYWORD:	
			case T_STORAGE:		
//			case T_POSIX_IDENTIFIER:
//			case T_STD_IDENTIFIER:
								action_keyword (r);			break;
			case T_POSIX_IDENTIFIER:
			case T_STD_IDENTIFIER:
								color (r, s, yytext);		break;
			case T_ASSIGNMENT:	action_assignment (r);		break;
			case T_OPERATOR:	action_operator (r);		break;
			case T_L_PAREN:
			case T_R_PAREN:
			case T_L_BRACE:
			case T_R_BRACE:
			case T_L_BRACKET:
			case T_R_BRACKET:
			case T_SEMICOLON:
			case T_COMMA:		action_glue (r, s);			break;
			case T_NEWLINE:
			case T_LEADSPACE:
			case T_WHITESPACE:	action_whitespace (r, s);	break;

			case T_SEPARATOR:
			case T_DEFINITION:
			case T_RULE:		action_grammar (r, s);		break;
			}
		}	
	ap_rputs ("</code></pre>\r\n", r);
	ap_rputs ("<hr>\r\n", r);
	dump_identifiers (r);
	dump_strings (r);

	epilogue (r);
}


/*+
This routine is largely inspired by mod_format.
+*/
static int all_handler(request_rec *r)
{
	int len;

	if (r->method_number != M_GET) {
		r->allowed = M_GET;
		return DECLINED;
	}

	len = strlen (r->unparsed_uri);
	if (len > 4 && strcmp (&r->unparsed_uri[len-4], "?raw") == 0)
		return DECLINED;

	cfg = (color_dir_config *)
		ap_get_module_config (r->per_dir_config, &color_module);

	if (cfg == NULL)
		return DECLINED;

	if (!cfg->enable)
		return DECLINED;

	if (r->finfo.st_mode == 0)
		return NOT_FOUND;

	yyin = ap_pfopen (r->pool, r->filename, "r");
	if (yyin == 0) {
		ap_log_reason ("file permissions deny server access", r->filename, r);
		return FORBIDDEN;
	}

	r->content_type = "text/html";
	ap_soft_timeout ("send", r);
	ap_send_http_header (r);

	ap_rputs ("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n", r);
	ap_rputs ("<html>\r\n", r);
	ap_rputs ("<head>\r\n", r);
	ap_rputs ("  <title>", r);
	emit (r, r->filename, 0, 0, 0, 0);
	ap_rputs ("</title>\r\n", r);
	ap_rputs ("  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\r\n", r);
	ap_rputs ("  <meta name=\"GENERATOR\" content=\"mod_color\">\r\n", r);
	ap_rputs ("  <meta name=\"warranty\" content=\r\n", r);
	ap_rputs ("        \"This software is provided 'as is' without express or implied warranty.\">\r\n", r);
	ap_rputs ("</head>\r\n", r);

	if (r->header_only) {
		ap_rputs ("</html>\r\n", r);
		ap_kill_timeout (r);
		ap_pfclose (r->pool, yyin);
		return OK;
	}

	process_c (r);

	ap_rputs ("</html>\r\n", r);
	ap_kill_timeout (r);
	ap_pfclose (r->pool, yyin);

	return OK;
}


static int c_handler(request_rec *r)
{
	mode = 0;
	return all_handler (r);
}


static int lex_handler(request_rec *r)
{
	mode = lex_definitions;
	return all_handler (r);
}


static int yacc_handler(request_rec *r)
{
	mode = yacc_definitions;
	return all_handler (r);
}


static int perf_handler(request_rec *r)
{
	mode = gperf_definitions;
	return all_handler (r);
}


/*+
+*/
static void *color_create_dir_config (pool *p, char *path)
{
	color_dir_config *cfg = 
		(color_dir_config *) ap_pcalloc (p, sizeof (color_dir_config));
	cfg->p = p;
	cfg->enable = 1;
	cfg->color[T_PREPROCESSOR - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_COMMENT - T_EOF] = ap_pstrdup (p, "#333333");
	cfg->color[T_S_LITERAL - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_C_LITERAL - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_I_LITERAL - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_R_LITERAL - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_KEYWORD - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_CPP_KEYWORD - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_STORAGE - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_IDENTIFIER - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_POSIX_IDENTIFIER - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_STD_IDENTIFIER - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_STORAGE - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_ASSIGNMENT - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_OPERATOR - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_L_PAREN - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_L_BRACKET - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_L_BRACE - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_SEMICOLON - T_EOF] = ap_pstrdup (p, "black");
	cfg->color[T_SEPARATOR - T_EOF] = ap_pstrdup (p, "blue");
	cfg->color[T_DEFINITION - T_EOF] = ap_pstrdup (p, "blue");
	cfg->color[T_RULE - T_EOF] = ap_pstrdup (p, "blue");

	cfg->style[T_PREPROCESSOR - T_EOF] = ap_pstrdup (p, "b");
	cfg->style[T_COMMENT - T_EOF] = ap_pstrdup (p, "i");
	cfg->style[T_S_LITERAL - T_EOF] = ap_pstrdup (p, "i");
	cfg->style[T_C_LITERAL - T_EOF] = ap_pstrdup (p, "i");
	cfg->style[T_I_LITERAL - T_EOF] = ap_pstrdup (p, "");
	cfg->style[T_R_LITERAL - T_EOF] = ap_pstrdup (p, "");
	cfg->style[T_KEYWORD - T_EOF] = ap_pstrdup (p, "b");
	cfg->style[T_CPP_KEYWORD - T_EOF] = ap_pstrdup (p, "b");
	cfg->style[T_STORAGE - T_EOF] = ap_pstrdup (p, "b");
	cfg->style[T_IDENTIFIER - T_EOF] = ap_pstrdup (p, "");
	cfg->style[T_POSIX_IDENTIFIER - T_EOF] = ap_pstrdup (p, "");
	cfg->style[T_STD_IDENTIFIER - T_EOF] = ap_pstrdup (p, "");
	cfg->style[T_STORAGE - T_EOF] = ap_pstrdup (p, "b");
	cfg->style[T_ASSIGNMENT - T_EOF] = ap_pstrdup (p, "b");
	cfg->style[T_OPERATOR - T_EOF] = ap_pstrdup (p, "b");
	cfg->style[T_L_PAREN - T_EOF] = ap_pstrdup (p, "");
	cfg->style[T_L_BRACKET - T_EOF] = ap_pstrdup (p, "");
	cfg->style[T_L_BRACE - T_EOF] = ap_pstrdup (p, "");
	cfg->style[T_SEMICOLON - T_EOF] = ap_pstrdup (p, "");
	cfg->style[T_SEPARATOR - T_EOF] = ap_pstrdup (p, "b");
	cfg->style[T_DEFINITION - T_EOF] = ap_pstrdup (p, "b");
	cfg->style[T_RULE - T_EOF] = ap_pstrdup (p, "b");

	cfg->face[T_PREPROCESSOR - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_COMMENT - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_S_LITERAL - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_C_LITERAL - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_I_LITERAL - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_R_LITERAL - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_KEYWORD - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_CPP_KEYWORD - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_STORAGE - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_IDENTIFIER - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_POSIX_IDENTIFIER - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_STD_IDENTIFIER - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_STORAGE - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_ASSIGNMENT - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_OPERATOR - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_L_PAREN - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_L_BRACKET - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_L_BRACE - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_SEMICOLON - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_SEPARATOR - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_DEFINITION - T_EOF] = ap_pstrdup (p, "");
	cfg->face[T_RULE - T_EOF] = ap_pstrdup (p, "");

	cfg->color[T_BACKGROUND - T_EOF] = ap_pstrdup (p, "white");
	cfg->color[T_TEXT - T_EOF] = ap_pstrdup (p, "black");

	return (void *) cfg;
}


static const char *cmd_disable (cmd_parms *parms, void *mconfig)
{
	color_dir_config *cfg = (color_dir_config *) mconfig;
	cfg->enable = 0;
	return NULL;
}


static const char *cmd_enable (cmd_parms *parms, void *mconfig)
{
	color_dir_config *cfg = (color_dir_config *) mconfig;
	cfg->enable = 1;
	return NULL;
}


static const char *cmd_set_color (cmd_parms *parms, void *mconfig, char *color)
{
	color_dir_config *cfg = (color_dir_config *) mconfig;
	cfg->color[(int)parms->cmd->cmd_data - T_EOF] = ap_pstrdup (cfg->p, color);
	return NULL;
}


static const char *cmd_set_style (cmd_parms *parms, void *mconfig, char *style)
{
	color_dir_config *cfg = (color_dir_config *) mconfig;
	cfg->style[(int)parms->cmd->cmd_data - T_EOF] = ap_pstrdup (cfg->p, style);
	return NULL;
}


static const char *cmd_set_face (cmd_parms *parms, void *mconfig, char *face)
{
	color_dir_config *cfg = (color_dir_config *) mconfig;
	cfg->face[(int)parms->cmd->cmd_data - T_EOF] = ap_pstrdup (cfg->p, face);
	return NULL;
}


/* Dispatch list of command handlers */
static const command_rec color_cmds[] = {
	{ "Color-Enable",
	  cmd_enable, NULL, OR_ALL, NO_ARGS,
	  "enable processing" },
	{ "Color-Disable",
	  cmd_disable, NULL, OR_ALL, NO_ARGS,
	  "disable processing" },
	{ "Color-Preprocessor-Color",
	  cmd_set_color, (void *) T_PREPROCESSOR, OR_ALL, TAKE1,
	  "display color for preprocessor commands" },
	{ "Color-Comment-Color",
	  cmd_set_color, (void *) T_COMMENT, OR_ALL, TAKE1,
	  "display color for comments" },
	{ "Color-String-Color",
	  cmd_set_color, (void *) T_S_LITERAL, OR_ALL, TAKE1,
	  "display color for string literals" },
	{ "Color-Character-Color",
	  cmd_set_color, (void *) T_C_LITERAL, OR_ALL, TAKE1,
	  "display color for character literals" },
	{ "Color-Integer-Color",
	  cmd_set_color, (void *) T_I_LITERAL, OR_ALL, TAKE1,
	  "display color for integral literals" },
	{ "Color-Real-Color",
	  cmd_set_color, (void *) T_R_LITERAL, OR_ALL, TAKE1,
	  "display color for floating point literals" },
	{ "Color-Keyword-Color",
	  cmd_set_color, (void *) T_KEYWORD, OR_ALL, TAKE1,
	  "display color for most C keywords" },
	{ "Color-C++-Keyword-Color",
	  cmd_set_color, (void *) T_CPP_KEYWORD, OR_ALL, TAKE1,
	  "display color for C++ keywords" },
	{ "Color-Storage-Color",
	  cmd_set_color, (void *) T_STORAGE, OR_ALL, TAKE1,
	  "display color for storage class keywords" },
	{ "Color-Identifier-Color",
	  cmd_set_color, (void *) T_IDENTIFIER, OR_ALL, TAKE1,
	  "display color for most identifiers" },
	{ "Color-POSIX-Identifier-Color",
	  cmd_set_color, (void *) T_POSIX_IDENTIFIER, OR_ALL, TAKE1,
	  "display color for POSIX identifiers (if supported)" },
	{ "Color-Standard-Identifier-Color",
	  cmd_set_color, (void *) T_STD_IDENTIFIER, OR_ALL, TAKE1,
	  "display color for standard identifiers (if supported)" },
	{ "Color-Assignment-Color",
	  cmd_set_color, (void *) T_ASSIGNMENT, OR_ALL, TAKE1,
	  "display color for assignment operators" },
	{ "Color-Operator-Color",
	  cmd_set_color, (void *) T_OPERATOR, OR_ALL, TAKE1,
	  "display color for non-assignment operators" },
	{ "Color-Parentheses-Color",
	  cmd_set_color, (void *) T_L_PAREN, OR_ALL, TAKE1,
	  "display color for parentheses" },
	{ "Color-Bracket-Color",
	  cmd_set_color, (void *) T_L_BRACKET, OR_ALL, TAKE1,
	  "display color for brackets" },
	{ "Color-Brace-Color",
	  cmd_set_color, (void *) T_L_BRACE, OR_ALL, TAKE1,
	  "display color for braces" },
	{ "Color-Semicolon-Color",
	  cmd_set_color, (void *) T_SEMICOLON, OR_ALL, TAKE1,
	  "display color for semicolons and commas" },
	{ "Color-Separator-Color",
	  cmd_set_color, (void *) T_SEPARATOR, OR_ALL, TAKE1,
	  "display color for yacc/lex separators" },
	{ "Color-Definition-Color",
	  cmd_set_color, (void *) T_DEFINITION, OR_ALL, TAKE1,
	  "display color for yacc/lex definitions" },
	{ "Color-Rule-Color",
	  cmd_set_color, (void *) T_RULE, OR_ALL, TAKE1,
	  "display color for yacc/lex definitions" },

	{ "Color-Preprocessor-Style",
	  cmd_set_style, (void *) T_PREPROCESSOR, OR_ALL, TAKE1,
	  "display style for preprocessor commands" },
	{ "Color-Comment-Style",
	  cmd_set_style, (void *) T_COMMENT, OR_ALL, TAKE1,
	  "display style for comments" },
	{ "Color-String-Style",
	  cmd_set_style, (void *) T_S_LITERAL, OR_ALL, TAKE1,
	  "display style for string literals" },
	{ "Color-Character-Style",
	  cmd_set_style, (void *) T_C_LITERAL, OR_ALL, TAKE1,
	  "display style for character literals" },
	{ "Color-Integer-Style",
	  cmd_set_style, (void *) T_I_LITERAL, OR_ALL, TAKE1,
	  "display style for integral literals" },
	{ "Color-Real-Style",
	  cmd_set_style, (void *) T_R_LITERAL, OR_ALL, TAKE1,
	  "display style for floating point literals" },
	{ "Color-Keyword-Style",
	  cmd_set_style, (void *) T_KEYWORD, OR_ALL, TAKE1,
	  "display style for most C keywords" },
	{ "Color-C++-Keyword-Style",
	  cmd_set_style, (void *) T_CPP_KEYWORD, OR_ALL, TAKE1,
	  "display style for C++ keywords" },
	{ "Color-Storage-Style",
	  cmd_set_style, (void *) T_STORAGE, OR_ALL, TAKE1,
	  "display style for storage class keywords" },
	{ "Color-Identifier-Style",
	  cmd_set_style, (void *) T_IDENTIFIER, OR_ALL, TAKE1,
	  "display style for most identifiers" },
	{ "Color-POSIX-Identifier-Style",
	  cmd_set_style, (void *) T_POSIX_IDENTIFIER, OR_ALL, TAKE1,
	  "display style for POSIX identifiers (if supported)" },
	{ "Color-Standard-Identifier-Style",
	  cmd_set_style, (void *) T_STD_IDENTIFIER, OR_ALL, TAKE1,
	  "display style for standard identifiers (if supported)" },
	{ "Color-Assignment-Style",
	  cmd_set_style, (void *) T_ASSIGNMENT, OR_ALL, TAKE1,
	  "display style for assignment operators" },
	{ "Color-Operator-Style",
	  cmd_set_style, (void *) T_OPERATOR, OR_ALL, TAKE1,
	  "display style for non-assignment operators" },
	{ "Color-Parentheses-Style",
	  cmd_set_style, (void *) T_L_PAREN, OR_ALL, TAKE1,
	  "display style for parentheses" },
	{ "Color-Bracket-Style",
	  cmd_set_style, (void *) T_L_BRACKET, OR_ALL, TAKE1,
	  "display style for brackets" },
	{ "Color-Brace-Style",
	  cmd_set_style, (void *) T_L_BRACE, OR_ALL, TAKE1,
	  "display style for braces" },
	{ "Color-Semicolon-Style",
	  cmd_set_style, (void *) T_SEMICOLON, OR_ALL, TAKE1,
	  "display style for semicolons and commas" },
	{ "Color-Separator-Style",
	  cmd_set_style, (void *) T_SEPARATOR, OR_ALL, TAKE1,
	  "display style for yacc/lex separators" },
	{ "Color-Definition-Style",
	  cmd_set_style, (void *) T_DEFINITION, OR_ALL, TAKE1,
	  "display style for yacc/lex definitions" },
	{ "Color-Rule-Style",
	  cmd_set_style, (void *) T_RULE, OR_ALL, TAKE1,
	  "display style for yacc/lex definitions" },

	{ "Color-Preprocessor-Face",
	  cmd_set_face, (void *) T_PREPROCESSOR, OR_ALL, TAKE1,
	  "display face for preprocessor commands" },
	{ "Color-Comment-Face",
	  cmd_set_face, (void *) T_COMMENT, OR_ALL, TAKE1,
	  "display face for comments" },
	{ "Color-String-Face",
	  cmd_set_face, (void *) T_S_LITERAL, OR_ALL, TAKE1,
	  "display face for string literals" },
	{ "Color-Character-Face",
	  cmd_set_face, (void *) T_C_LITERAL, OR_ALL, TAKE1,
	  "display face for character literals" },
	{ "Color-Integer-Face",
	  cmd_set_face, (void *) T_I_LITERAL, OR_ALL, TAKE1,
	  "display face for integral literals" },
	{ "Color-Real-Face",
	  cmd_set_face, (void *) T_R_LITERAL, OR_ALL, TAKE1,
	  "display face for floating point literals" },
	{ "Color-Keyword-Face",
	  cmd_set_face, (void *) T_KEYWORD, OR_ALL, TAKE1,
	  "display face for most C keywords" },
	{ "Color-C++-Keyword-Face",
	  cmd_set_face, (void *) T_CPP_KEYWORD, OR_ALL, TAKE1,
	  "display face for C++ keywords" },
	{ "Color-Storage-Face",
	  cmd_set_face, (void *) T_STORAGE, OR_ALL, TAKE1,
	  "display face for storage class keywords" },
	{ "Color-Identifier-Face",
	  cmd_set_face, (void *) T_IDENTIFIER, OR_ALL, TAKE1,
	  "display face for most identifiers" },
	{ "Color-POSIX-Identifier-Face",
	  cmd_set_face, (void *) T_POSIX_IDENTIFIER, OR_ALL, TAKE1,
	  "display face for POSIX identifiers (if supported)" },
	{ "Color-Standard-Identifier-Face",
	  cmd_set_face, (void *) T_STD_IDENTIFIER, OR_ALL, TAKE1,
	  "display face for standard identifiers (if supported)" },
	{ "Color-Assignment-Face",
	  cmd_set_face, (void *) T_ASSIGNMENT, OR_ALL, TAKE1,
	  "display face for assignment operators" },
	{ "Color-Operator-Face",
	  cmd_set_face, (void *) T_OPERATOR, OR_ALL, TAKE1,
	  "display face for non-assignment operators" },
	{ "Color-Parentheses-Face",
	  cmd_set_face, (void *) T_L_PAREN, OR_ALL, TAKE1,
	  "display face for parentheses" },
	{ "Color-Bracket-Face",
	  cmd_set_face, (void *) T_L_BRACKET, OR_ALL, TAKE1,
	  "display face for brackets" },
	{ "Color-Brace-Face",
	  cmd_set_face, (void *) T_L_BRACE, OR_ALL, TAKE1,
	  "display face for braces" },
	{ "Color-Semicolon-Face",
	  cmd_set_face, (void *) T_SEMICOLON, OR_ALL, TAKE1,
	  "display face for semicolons and commas" },
	{ "Color-Separator-Face",
	  cmd_set_face, (void *) T_SEPARATOR, OR_ALL, TAKE1,
	  "display face for yacc/lex separators" },
	{ "Color-Definition-Face",
	  cmd_set_face, (void *) T_DEFINITION, OR_ALL, TAKE1,
	  "display face for yacc/lex definitions" },
	{ "Color-Rule-Face",
	  cmd_set_face, (void *) T_RULE, OR_ALL, TAKE1,
	  "display face for yacc/lex rules" },

	{ "Color-Background-Color",
	  cmd_set_color, (void *) T_BACKGROUND, OR_ALL, TAKE1,
	  "background color" },
	{ "Color-Text-Color", cmd_set_color, 
	  (void *) T_TEXT, OR_ALL, TAKE1,
	  "text color" },

    { NULL }
};


/* Dispatch list of content handlers */
/* For now, we only support a single language */
static const handler_rec color_handlers[] = {
	{ "c-source", c_handler },
	{ "lex-source", lex_handler },
	{ "yacc-source", yacc_handler },
	{ "perf-source", perf_handler },
	{ NULL, NULL }
};


/* Dispatch list for API hooks */
module MODULE_VAR_EXPORT color_module = {
    STANDARD_MODULE_STUFF, 
    NULL,                     /* module initializer                  */
    color_create_dir_config,  /* create per-dir    config structures */
    NULL,                     /* merge  per-dir    config structures */
    NULL,                     /* create per-server config structures */
    NULL,                     /* merge  per-server config structures */
    color_cmds,               /* table of config file commands       */
    color_handlers,           /* [#8] MIME-typed-dispatched handlers */
    NULL,                     /* [#1] URI to filename translation    */
    NULL,                     /* [#4] validate user id from request  */
    NULL,                     /* [#5] check if the user is ok _here_ */
    NULL,                     /* [#3] check access by host address   */
    NULL,                     /* [#6] determine MIME type            */
    NULL,                     /* [#7] pre-run fixups                 */
    NULL,                     /* [#9] log a transaction              */
    NULL,                     /* [#2] header parser                  */
    NULL,                     /* child_init                          */
    NULL,                     /* child_exit                          */
    NULL                      /* [#0] post read-request              */
#ifdef EAPI
   ,NULL,                     /* EAPI: add_module                    */
    NULL,                     /* EAPI: remove_module                 */
    NULL,                     /* EAPI: rewrite_command               */
    NULL                      /* EAPI: new_connection                */
#endif
};