/*
 * Bugs:
 * 
 * Comments are very sensitive to missing space:
 */
/* Also, note the colouring here: */
#ifdef NEVER
char *simpletest = "fred";
char test2 = 'x';

CINEWORD/*CINEBYTE*/ register_P = 0;	/* Page-Register (4 bits, shifts to
					 * high short nibble for code, hight
					 * byte nibble for ram) */
CINEWORD /*CINEBYTE*/acc_a0 = 0;	/* bit0 of A-reg at last accumulator
					 * access */
const char *do_it = "line1\n\"line2";
/* oops! */
const char *again = "line1\n\"line2";
int new = 0;

const char *nullstr = "";

#define a_macro (i = \
                 i + 1)
int that_is_better = '\'';
int or_is_it = '\'';
int last_test = '\\';
char *another_string_test = "hello\\\"world";
char *last_string_test = "hello\\nworld";

/* test *//* Here is one remaining bug I only just now noticed ... fixed! */

#endif
/*
 * Probably a bug in the 'peek' logic. (read/ungetc can't be called twice)
 * \" in a string terminates the string colouring.
 * a variable name such as do_it does *not* contain the keyword 'do' -
 *   someone is relying on isalnum() to identify variables.
 * #directive immediately following a comment line is not
 * coloured
 * #directives on multiple lines (ending each in in \) are not
 * coloured on the second and following lines
 * 
 * Misfeatures: for my purposes, I don't want C++ keywords to be highlighted.
 */


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#define QUOTE_COLOR "#ff0000"
#define COMMENT_COLOR "#408080"
#define PREPROC_COLOR "#008000"


/*
 * C / C++ to HTML converter. Converts C and C++ code to HTML with syntax
 * hilighting...
 */

int peekchar = -1;

char           *Reserved_Words[] = {
#ifdef CPLUSPLUS
	"asm", "catch", "double", "cgoto", "not", "return", "throw",
	"volatile", "and", "char", "else", "if", "not_eq", "short",
	"true", "while", "and_eq", "class", "enum", "inline", "operator",
	"signed", "try", "xor", "auto", "compl", "explicit", "int", "or",
	"sizeof", "typedef", "xor_eq", "bitand", "const", "extern",
	"long", "or_eq", "static", "typename", "bitor", "continue",
	"false", "mutable", "private", "struct", "union", "bool", "default",
	"float", "namespace", "protected", "switch", "unsigned", "break",
	"delete", "for", "new", "public", "template", "virtual", "case",
	"do", "friend", "not", "register", "this", "void",
#else
	"double", "goto", "return",
	"volatile", "char", "else", "if", "short",
	"true", "while", "enum", "inline",
	"signed", "auto", "int", /*"or",*/
	"sizeof", "typedef", "const", "extern",
	"long", "static", "continue",
	"false", "struct", "union", "bool", "default",
	"float", "switch", "unsigned", "break",
	"for", "case",
	"do", "register", "void",
#endif
	NULL
};
int 
IsReservedWord(char *word)
{
	int             i;

	for (i = 0;; ++i) {
		if (Reserved_Words[i] == NULL)
			return (0);
		if (!strcmp(word, Reserved_Words[i])) {
			return 1;
		}
	}
}

int 
fpeek(FILE * f)
{
	int             ch;

	ch = fgetc(f);
	ungetc(ch, f);
	return ch;
}


void 
printchar(char inchar, FILE * fstream)
{
	if (inchar == '&')
		fprintf(fstream, "&amp;");
	else if (inchar == '>')
		fprintf(fstream, "&gt;");
	else if (inchar == '<')
		fprintf(fstream, "&lt;");
	else
		fputc(inchar, fstream);
}

int 
Print_HTML(FILE * fstream, char *Title)
{
	FILE           *InFile;
	int             inchar, i, newline = 1, WordIdx = 0;
	char            word[280];

	for (i = 0; i < 280; ++i)
		word[i] = 0;
	/* Print out the HTML header. */
	fprintf(fstream, "<HTML>\n<HEAD>\n<TITLE> %s </TITLE>\n</HEAD>\n", Title);
	fprintf(fstream, "<BODY bgcolor=ffffff><pre>\n");

	/* Now we open the file for reading in text mode. */
	InFile = fopen(Title, "r");
	if (InFile == NULL)
		return -1;

	/* And start reading it in character by character. */
	inchar = fgetc(InFile); peekchar = fpeek(InFile);
	while (!feof(InFile)) {
		if (inchar == '\n') {
			newline = 1;
		} else if (!isspace(inchar)) {
			/* Handle all preproccessor directives. */
			if (inchar == '#') {
				if (newline == 1) {
					fprintf(fstream, "<font color=%s>", PREPROC_COLOR);
					for (;;) {
						if ((inchar != '\\') &&
                                                    (peekchar == '\n')) break;
						printchar(inchar, fstream);
						inchar = fgetc(InFile);
						peekchar = fpeek(InFile);
					}
					printchar(inchar, fstream);
					inchar = fgetc(InFile);
					peekchar = fpeek(InFile);
					fprintf(fstream, "</font>");
					newline = 1;
					continue;
				}
			}
			/* Handle single line comments. */
			else if ((inchar == '/') && (peekchar == '/')) {
				fprintf(fstream, "<font color=%s><i>", COMMENT_COLOR);
				while ((inchar != '\n') && (inchar > 0)) {
					printchar(inchar, fstream);
					inchar = fgetc(InFile);
					peekchar = fpeek(InFile);
				}
				fprintf(fstream, "</i></font>");
				newline = 1;
				continue;
			}
			/* Handle Multi-line comments. */
			else if ((inchar == '/') && (peekchar == '*')) {
				fprintf(fstream, "<font color=%s><i>/", COMMENT_COLOR);
				for (;;) {
					inchar = fgetc(InFile);
					peekchar = fpeek(InFile);
					if ((inchar == '*') && (peekchar == '/')) break;
					printchar(inchar, fstream);
				}
				inchar = fgetc(InFile);
				peekchar = fpeek(InFile);
				fprintf(fstream, "*/</i></font>");
				inchar = fgetc(InFile);
				peekchar = fpeek(InFile);
				continue; /* Now process next char */
			}
			/* Handle Double Quotes. */
			else if (inchar == '"') {
				fprintf(fstream, "<font color=%s>\"", QUOTE_COLOR);
				if (peekchar == '"') {
					/* Null string bug fix */
					inchar = fgetc(InFile);
					peekchar = fpeek(InFile);
					printchar(inchar, fstream);
				} else {
					inchar = fgetc(InFile);
					if (inchar == EOF) continue;
					peekchar = fpeek(InFile);
					for (;;) {
						if ((inchar != '\\') && (peekchar == '"')) break;
						printchar(inchar, fstream);
						inchar = fgetc(InFile);
						if (inchar == EOF) break;
						peekchar = fpeek(InFile);
					}
					printchar(inchar, fstream);
					inchar = fgetc(InFile);
					peekchar = fpeek(InFile);
					printchar(inchar, fstream);
				}
				inchar = fgetc(InFile);
				peekchar = fpeek(InFile);
				fprintf(fstream, "</font>");
				newline = 0;
				continue;
			}
			/* Handle Single Quotes. */
			else if (inchar == '\'') {
				fprintf(fstream, "<font color=%s>'", QUOTE_COLOR);
				inchar = fgetc(InFile);
				if (inchar == EOF) continue;
				peekchar = fpeek(InFile);
				for (;;) {
					if ((inchar != '\\') && (peekchar == '\'')) break;
					if ((inchar == '\\') && (peekchar == '\\')) {
						printchar(inchar, fstream);
						inchar = fgetc(InFile);
						if (inchar == EOF) break;
						peekchar = fpeek(InFile);
						if (peekchar == '\'') break;
					}
					printchar(inchar, fstream);
					inchar = fgetc(InFile);
					if (inchar == EOF) break;
					peekchar = fpeek(InFile);
				}
				printchar(inchar, fstream);
				inchar = fgetc(InFile);
				peekchar = fpeek(InFile);
				printchar(inchar, fstream);
				inchar = fgetc(InFile);
				peekchar = fpeek(InFile);
				fprintf(fstream, "</font>");
				newline = 0;
				continue;
			}
			newline = 0;
		}
		/* Everything else. */
		if (!(isalnum(inchar) || (inchar == '_'))) {
			if (IsReservedWord(word)) {
				fprintf(fstream, "<b>%s</b>%c", word, inchar);
			} else {
				for (i = 0; i < WordIdx; ++i)
					printchar(word[i], fstream);
				printchar(inchar, fstream);
			}
			for (i = 0; i < 280; ++i)
				word[i] = 0;
			WordIdx = 0;
		} else {
			word[WordIdx++] = inchar;
		}
		inchar = fgetc(InFile);
		peekchar = fpeek(InFile);
	}
	fprintf(fstream, "\n</pre></BODY></HTML>\n");
	fclose(InFile);
	return 1;
}

int 
main(int argc, char *argv[])
{
	if (argc < 2) {
		printf("Usage: %s [Filename]\n\n", argv[0]);
		return -1;
	}
	Print_HTML(stdout, argv[1]);
	return 0;
}