/*
 * 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",
	"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>", COMMENT_COLOR);
				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;
}
