#include <stdio.h>
#define QUOTE_COLOR "#ff0000"
#define COMMENT_COLOR "#408080"

char *Reserved_Words[] = {
   "ACCESS",            "ELSEIF",            "PUBLIC",
   "ADD",               "END",               "RAISE",
   "ALL",               "ENTRY",             "RANGE",
   "ALTER",             "EXCEPTION",         "REAL",
   "AND",               "EXCEPTION_INIT",    "RECORD",
   "ANY",               "EXISTS",            "RELEASE",
   "ARRAY",             "EXIT",              "REM",
   "ARRAYLEN",          "FALSE",             "RENAME",
   "AS",                "FETCH",             "RESOURCE",
   "ASC",               "FLOAT",             "RETURN",
   "ASSERT",            "FOR",               "REVERSE",
   "ASSIGN",            "FORM",              "REVOKE",
   "AT",                "FORMAT",            "ROLLBACK",
   "AUTHORIZATION",     "FROM",              "ROWID",
   "BASE_TABLE",        "FUNCTION",          "ROWLABEL",
   "BEGIN",             "GENERIC",           "ROWNUM",
   "BETWEEN",           "GOTO",              "ROWTYPE",
   "BINARY_INTEGER",    "GRANT",             "RUN",
   "BODY",              "GROUP",             "SAVEPOINT",
   "BOOLEAN",           "HAVING",            "SCHEMA",
   "BY",                "IDENTIFIED",        "SELECT",
   "CASE",              "IF",                "SEPARATE",
   "CHAR",              "IN",                "SET",
   "CHAR_BASE",         "INDEX",             "SIZE",
   "CHECK",             "INDEXES",           "SMALLINT",
   "CLOSE",             "INDICATOR",         "SPACE",
   "CLUSTER",           "INSERT",            "SPOOL",
   "CLUSTERS",          "INTEGER",           "SQL",
   "COLAUTH",           "INTERSECT",         "SQLCODE",
   "COLUMN",            "INTO",              "SQLERRM",
   "COLUMNS",           "IS",                "START",
   "COMMIT",            "LEVEL",             "STATEMENT",
   "COMPRESS",          "LIKE",              "SUBTYPE",
   "CONNECT",           "LIMITED",           "TABAUTH",
   "CONSTANT",          "LOOP",              "TABLE",
   "CRASH",             "MINUS",             "TABLES",
   "CREATE",            "MLSLABEL",          "TABLESPACE",
   "CURRENT",           "NATURAL",           "TASK",
   "CURRVAL",           "NEW",               "TERMINATE",
   "CURSOR",            "NEXTVAL",           "THEN",
   "DATA_BASE",         "NOCOMPRESS",        "TO",
   "DATABASE",          "NOT",               "TRUE",
   "DATE",              "NULL",              "TYPE",
   "DBA",               "NUMBER",            "UNFORMAT",
   "DEBUGOFF",          "NUMBER_BASE",       "UNION",
   "DEBUGON",           "OF",                "UNIQUE",
   "DECIMAL",           "ON",                "UPDATE",
   "DECLARE",           "OPEN",              "USE",
   "DEFAULT",           "OPTION",            "VALUES",
   "DEFINITION",        "OR",                "VARCHAR",
   "DELAY",             "ORDER",             "VARCHAR2",
   "DELETE",            "OTHERS",            "VIEW",
   "DELTA",             "OUT",               "VIEWS",
   "DESC",              "PACKAGE",           "WHEN",
   "DIGITS",            "PARTITION",         "WHERE",
   "DISPOSE",           "PCTFREE",           "WHILE",
   "DISTINCT",          "POSITIVE",          "WITH",
   "DO",                "PRAGMA",            "WORK",
   "DROP",              "PRIOR",             "XOR",
/* Additions by GT: */
   "REPLACE",		"PROCEDURE",	     "ELSE",
   "ELSIF",		"PLS_INTEGER",	     "ROWCOUNT",
   "ISOPEN",		"FORALL",
   NULL
};

int IsReservedWord(char *word)
{
  int i = 0;
  char *s = word;

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

#ifdef UC_HACK
  while (*s != '\0') {
    if ((isalpha(*s)) && (!isupper(*s))) return 0;
    s += 1;
  }
  return 1;
#else
  return 0;
#endif
}


int main(int argc, char **argv)
{
#define NEXTCH    {c = fgetc(stdin); \
                   pend = fgetc(stdin); \
                   ungetc(pend, stdin); \
                  }

  // surface-level line reconstruction on PL/SQL source,
  // no deep parsing.  Used for conversion to HTML with
  // 'syntax colouring' (actually lexical colouring)
  int c = -1, pend = -1, lastch;
  // start of html
  printf("<html><head><title></title></head><body><PRE>");
  for (;;) {
    lastch = c;
    NEXTCH; // C is always the current character, pend is what you will read next. 
    if (c == EOF) {
      // end of html
      break;
    } else if (lastch == '\n' && c == '/' && pend == '\n') {
      printf("<HR>\n");
    } else if (c == '/' && pend == '*') {
      // handle C comment
      printf("<font color=%s><i>/*", COMMENT_COLOR);
      NEXTCH; // C is '*'
      for (;;) {
        NEXTCH;
        if (c == '*' && pend == '/') break;
        putchar(c);
      }
      NEXTCH; // skip /
      printf("*/</i></font>");
    } else if (c == '/' && pend == '/') {
      // handle C++ comment
      printf("<font color=%s><i>/", COMMENT_COLOR);
      for (;;) {
        if (c == '\n') break;
        putchar(c);
        NEXTCH
      }
      printf("</i></font>\n");
    } else if (c == '-' && pend == '-') {
      // handle ADA comment
      printf("<font color=%s><i>", COMMENT_COLOR);
      for (;;) {
        if (c == '\n') break;
        putchar(c);
        NEXTCH
      }
      printf("</i></font>\n");
    } else if (c == '\'') {
      // handle squote literal
      printf("<font color=%s>'", QUOTE_COLOR);
      for (;;) {
        NEXTCH; // how are single quotes escaped in PL/SQL?
                // - I think by doubling, so fix this!
        putchar(c);
        if (c == '\'') break;
      }
      printf("</font>");
    } else if (c == '"') {
      // handle dquote literal
      printf("<font color=%s>\"", QUOTE_COLOR);
      for (;;) {
        NEXTCH; // how are double quotes escaped in PL/SQL?
                // - I think by doubling, so fix this!
        putchar(c);
        if (c == '"') break;
      }
      printf("</font>");
    } else if (isdigit(c)) {
      // handle number
      // for now, leave untouched.
      //printf("<font color=%s><i>\"", NUMBER_COLOR);
      putchar(c);
      for (;;) {
        if (!isdigit(pend)) break;
        NEXTCH; // How about reals, exponents etc?
        putchar(c);
      }
      //printf("</i></font>");
    } else if (isalpha(c)) {
      // handle keyword or variable
      char words[1024];
      char *word = words;
      for (;;) {
        *word++ = c;
        if (!(isalpha(pend) || isdigit(pend) || (pend == '_') || (pend == '#'))) break;
        NEXTCH; // What non-std chars are allowed?  #? .? _?
      }
      *word = '\0';
      if (lastch != '.' && IsReservedWord(words)) {
        printf("<b>%s</b>", words);
      } else {
        printf("%s", words);
      }
    } else if (c == '&') {
      printf("&amp;");
    } else if (c == '<') {
      printf("&lt;");
    } else if (c == '>') {
      printf("&gt;");
    } else if (c == '\n') {
      printf("\n");
    } else {
      // output noise char
      putchar(c);
    }
  }
  printf("</PRE></body></html>\n");
}