A Place Where No Dreams Come True...

Current Project: A Chroma-Coding (Syntax Highlighting) HTML preprocessor so 'You' get to see the code as I see it. This is the best representation I can offer for how I have my Emacs editor set to 'Highlight'. It also does the indenting. When either of these mechanisms fails it's obvious where I went wrong in the syntax.

This Is How 'I' Actually See My Code...

I think it was somewhere in the mid 1980s that I received my first copy of the Emacs editor. It was originally included in the developers kit for the GEM (Graphical Environment Manager) OS from DRI (Digital Research) which came on the first Atari ST computers labeled as TOS.

Up until this point. Every computer or development 'system' or 'kit' I had used a custom version of the FORTH OS which I wrote for them using an assembler. In those days, as early as the late 1970's, if a computer had no way of running a program from an external source I just replaced the monitor firmware with a custom 'FORTH Monitor'. Believe it or not, this method was very productive as a primary tool for the development and debugging of embedded systems hardware. Every embedded system I developed had some type of Debug Monitor (console) based on the FORTH kernel. In every case this philosophy eliminated the necessity for costly emulator hardware.

The Atari ST was kind of a milestone for me though. It represented the first computer I 'owned' that had a real development system available for it. And I acquired it. And I developed a version of FORTH which I ultimately abandoned. Sadly. At this point hardware development technology had caught up to the developer and there was no need to do it all yourself. The day of the Personal Computer had arrived.

Even so. After many years of working behind a 'Terminal' with a black screen and white characters the editors, for many years in keeping with this convention, used the same theme. Only the Emacs editor enhanced this with 'Context-Sensitive' color (chroma) coding for programming languages it recognized.

Almost thirty years later I still use the original (comfortable) theme I inherited with my first copy of Emacs.

//-----------------------------------------------------------------------------
// c-html.c
//
//  C-source-code -> HTML chroma coder.
//
// Copyright (c) 2014 - Dysfunctional Farms A.K.A. www.smegware.com
//
//  All Smegware software is free; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This software is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//
//-----------------------------------------------------------------------------
//
// History...
//
//   $Source$
//   $Author$
// $Revision$
//
// $Log$
//
//-----------------------------------------------------------------------------

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>

#include <report.h>

//-----------------------------------------------------------------------------

#define C_SPACE "<span>"
#define C_BRACE "<span class=\"C_brace\">"
#define C_ESCAPE "<span class=\"C_escape\">"
#define C_STRING "<span class=\"C_string\">"
#define C_COMMENT "<span class=\"C_comment\">"
#define C_KEYWORD "<span class=\"C_keyword\">"
#define C_LITERAL "<span class=\"C_literal\">"
#define C_WARNING "<span class=\"C_warning\">"
#define C_OPERATOR "<span class=\"C_operator\">"
#define C_VARIABLE "<span class=\"C_variable\">"
#define C_DEFINITION "<span class=\"C_definition\">"
#define C_PREPROCESSOR "<span class=\"C_preprocessor\">"

#define ENDTAG "</span>"

//-----------------------------------------------------------------------------

#define MAXTOKEN 512
#define MAXTOKENS 32

#define ch_escaped  (1 << 0)
#define ch_string   (1 << 1)
#define ch_comment  (1 << 2)
#define ch_function (1 << 3)

enum _chromakey { UNKNOWN = 0, NEWLINE, SPACE, BRACE, STRING, COMMENT, KEYWORD,
		  LITERAL, WARNING, OPERATOR, VARIABLE, CONSTANT, DEFINED, PREPROC };

struct _token {
  enum _chromakey state;
  char token[MAXTOKEN + 1];
};

struct _chroma {
  int file;
  int outfile;
  enum _chromakey state;
  enum _chromakey last;
  unsigned flags;
  long inpnt;
  long insize;
  char *input;
  struct _token tokens[MAXTOKENS];
  char token[MAXTOKEN + 1];
};

//-----------------------------------------------------------------------------

static const char *KeyWords[] = {
  "break",
  "case",
  "char",
  "const",
  "continue",
  "default",
  "do",
  "else",
  "enum",
  "extern",
  "for",
  "if",
  "int",
  "long",
  "return",
  "sizeof",
  "static",
  "struct",
  "switch",
  "typedef",
  "unsigned",
  "void",
  "while"
};

static const int numKeyWords = sizeof(KeyWords) / sizeof(KeyWords[0]);

//-----------------------------------------------------------------------------

static const char Braces[] = {
  "(){}[]"
};

static const int numBraces = sizeof(Braces) / sizeof(Braces[0]);

//-----------------------------------------------------------------------------

static const char Formats[] = {
  "isxXcudofFeEgGaApn%%"
};

static const int numFormats = sizeof(Formats) / sizeof(Formats[0]);

//-----------------------------------------------------------------------------

static const char Operators[] = {
  "!&|^<>=+-*/%%,.?:;"
};

static const int numOperators = sizeof(Operators) / sizeof(Operators[0]);

//-----------------------------------------------------------------------------

static void chroma_finish(struct _chroma *chroma)
{
  if(chroma)
  {
    if(chroma->file > 0)
    {
      close(chroma->file);
      report(dbglevl_debug, "chroma_finish():close(%i).\n", chroma->file);
    }
    if(chroma->outfile > 0)
    {
      // Append newline to final output.
      write(chroma->outfile, "\n", 1);
      close(chroma->outfile);
      report(dbglevl_debug, "chroma_finish():close(%i).\n", chroma->outfile);
    }
    if(chroma->input)
    {
      report(dbglevl_debug, "chroma_finish():free(chroma->input).\n");
      free(chroma->input);
    }
    report(dbglevl_debug, "chroma_finish():free(chroma).\n");
    free(chroma);
  }
}

//-----------------------------------------------------------------------------

static struct _chroma *chroma_start(const char *fname)
{
  struct _chroma *chroma = malloc(sizeof(struct _chroma));
  if(chroma)
  {
    memset(chroma, 0, sizeof(struct _chroma));
    chroma->file = open(fname, O_RDONLY);
    if(chroma->file > 0)
    {
      chroma->insize = lseek(chroma->file, 0, SEEK_END);
      if(chroma->insize != -1)
      {
	// Reset.
	lseek(chroma->file, 0, SEEK_SET);
	chroma->input = malloc(chroma->insize);
	if(chroma->input)
	{
	  read(chroma->file, chroma->input, chroma->insize);
	}
	else
	{
	  report_error("error : chroma_start():chroma->input = malloc(%i) - ", chroma->insize);
	}
	chroma->outfile = creat("c-html.txt", 0644);
      }
      else
      {
	report_error("error : chroma_start():lseek() - ");
      }
    }
    else
    {
      report_error("error : chroma_start():open(%s) - ", fname);
    }
  }
  else
  {
    report_error("error : chroma_start():malloc(%i) - ", sizeof(struct _chroma));
  }
  // Check for errors.
  if(chroma && ((chroma->file == -1) || (chroma->insize == -1) || (!chroma->input)))
  {
    chroma_finish(chroma);
    chroma = NULL;
  }
  else
  {
    report(dbglevl_msg, "chroma_start(%s) - file=%i size=%i.\n", fname, chroma->file, chroma->insize);
  }
  return chroma;
}

//-----------------------------------------------------------------------------
/*
static void chroma_clear_tokens(struct _chroma *chroma)
{
  int i;

  for(i = 0; i < MAXTOKENS; i++)
  {
    chroma->tokens[i].state = UNKNOWN;
    chroma->tokens[i].token[0] = '\0';
  }
}

//-----------------------------------------------------------------------------

static int chroma_add_token(struct _chroma *chroma)
{
  int i;

  for(i = 0; i < (MAXTOKENS - 1); i++)
  {
    if(chroma->tokens[i].token[0] == '\0')
    {
      strncpy(chroma->tokens[i].token, chroma->token, MAXTOKEN - 1);
      chroma->tokens[i].token[MAXTOKEN - 1] = '\0';
      chroma->tokens[i].state = chroma->state;
      break;
    }
  }
  if(i >= (MAXTOKENS - 1))
  {
    i = -1;
  }
  return i;
}
*/
//-----------------------------------------------------------------------------

static int chroma_escape_token(char *escaped, char token)
{
  int rtn = 1;

  if(token == '<')
  {
    escaped[0] = '&';
    escaped[1] = 'l';
    escaped[2] = 't';
    escaped[3] = ';';
    rtn = 4;
  }
  else if(token == '>')
  {
    escaped[0] = '&';
    escaped[1] = 'g';
    escaped[2] = 't';
    escaped[3] = ';';
    rtn = 4;
  }
  else if(token == '&')
  {
    escaped[0] = '&';
    escaped[1] = 'a';
    escaped[2] = 'm';
    escaped[3] = 'p';
    escaped[4] = ';';
    rtn = 5;
  }
  else
  {
    *escaped = token;
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_escape_html(struct _chroma *chroma)
{
  int i;
  int j;
  char escaped[256];

  for(i = 0, j = 0; chroma->token[i]; i++)
  {
    j += chroma_escape_token(&escaped[j], chroma->token[i]);
  }
  escaped[j] = '\0';
  strncpy(chroma->token, escaped, MAXTOKEN);
  chroma->token[MAXTOKEN - 1] = 0;
  return i;
}

//-----------------------------------------------------------------------------

static int chroma_escape_warning(char *warning, const char *stream, int *warned)
{
  int rtn = 0;

  *warned = 1;
  if(strncmp(stream, "FIXME:", 6) == 0)
  {
    *warning = '\0';
    strcat(warning, ENDTAG);
    strcat(warning, C_WARNING);
    strcat(warning, "FIXME");
    strcat(warning, ENDTAG);
    strcat(warning, C_COMMENT);
    rtn = strlen(warning) - 1;
    *warned = 5;
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_is_comment(struct _chroma *chroma)
{
  int rtn = 0;

  if(chroma->flags & ch_comment)
  {
    // Contimue with multi-line comment.
    rtn = 1;
  }
  else if(chroma->input[chroma->inpnt] == '/')
  {
    if((chroma->input[chroma->inpnt + 1]) == '/' || (chroma->input[chroma->inpnt + 1] == '*'))
    {
      rtn = 1;
    }
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_parse_comment(struct _chroma *chroma)
{
  int w;
  int rtn = 0;

  if(!(chroma->flags & ch_comment) && (chroma->input[chroma->inpnt + 1] == '/'))
  {
    // C++ style : parse till end of line.
    while(chroma->inpnt < chroma->insize)
    {
      chroma->token[rtn] = chroma->input[chroma->inpnt];
      rtn += chroma_escape_token(&chroma->token[rtn], chroma->token[rtn]);
      rtn += chroma_escape_warning(&chroma->token[rtn - 1], &chroma->input[chroma->inpnt], &w);
      chroma->inpnt += w;
      if(chroma->input[chroma->inpnt] == '\n')
      {
	chroma->state = COMMENT;
	break;
      }
    }
  }
  else if((chroma->flags & ch_comment) || chroma->input[chroma->inpnt + 1] == '*')
  {
    // C style : May span multiple lines.
    chroma->flags |= ch_comment;
    while(chroma->inpnt < chroma->insize)
    {
      if(chroma->input[chroma->inpnt] == '\n')
      {
	chroma->state = COMMENT;
	break;
      }
      else if(((chroma->input[chroma->inpnt] == '*') && (chroma->input[chroma->inpnt + 1] == '/')))
      {
	chroma->token[rtn++] = chroma->input[chroma->inpnt++];
	chroma->token[rtn++] = chroma->input[chroma->inpnt++];
	chroma->flags &= !ch_comment;
	chroma->state = COMMENT;
	break;
      }
      chroma->token[rtn] = chroma->input[chroma->inpnt];
      rtn += chroma_escape_token(&chroma->token[rtn], chroma->token[rtn]);
      rtn += chroma_escape_warning(&chroma->token[rtn - 1], &chroma->input[chroma->inpnt], &w);
      chroma->inpnt += w;
    }
  }
  chroma->token[rtn] = '\0';
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_is_brace(char brace)
{
  int rtn;

  for(rtn = 0; rtn < numBraces; rtn++)
  {
    if(Braces[rtn] == brace)
    {
      rtn = 1;
      break;
    }
  }
  if(rtn >= numBraces)
  {
    rtn = 0;
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_parse_brace(struct _chroma *chroma)
{
  int rtn = 0;

  chroma->token[rtn++] = chroma->input[chroma->inpnt++];
  while(chroma->inpnt < chroma->insize)
  {
    if(!chroma_is_brace(chroma->input[chroma->inpnt]))
    {
      chroma->state = BRACE;
      break;
    }
    chroma->token[rtn++] = chroma->input[chroma->inpnt++];
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_is_operator(char operator)
{
  int rtn;

  for(rtn = 0; rtn < numOperators; rtn++)
  {
    if(Operators[rtn] == operator)
    {
      rtn = 1;
      break;
    }
  }
  if(rtn >= numOperators)
  {
    rtn = 0;
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_parse_operator(struct _chroma *chroma)
{
  int rtn = 0;

  chroma->token[rtn++] = chroma->input[chroma->inpnt++];
  while(chroma->inpnt < chroma->insize)
  {
    if(!chroma_is_operator(chroma->input[chroma->inpnt]))
    {
      chroma->state = OPERATOR;
      break;
    }
    chroma->token[rtn++] = chroma->input[chroma->inpnt++];
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_is_format(char format)
{
  int rtn;

  for(rtn = 0; rtn < numFormats; rtn++)
  {
    if(Formats[rtn] == format)
    {
      rtn = 1;
      break;
    }
  }
  if(rtn >= numFormats)
  {
    rtn = 0;
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_is_string(char string)
{
  int rtn = 0;

  if((string == '"') || (string == '\''))
  {
    rtn = 1;
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_parse_string(struct _chroma *chroma)
{
  int ischar = 0;
  int rtn = 0;

  // Assume delimiter lost...
  chroma->token[rtn] = chroma->input[chroma->inpnt++];
  if(chroma->token[rtn++] == '\'')
  {
    ischar = 1;
  }
  while(chroma->inpnt < chroma->insize)
  {
    chroma->token[rtn] = chroma->input[chroma->inpnt++];
    if(chroma->token[rtn] == '\\')
    {
      // Escaped character.
      chroma->token[rtn] = '\0';
      strcat(chroma->token, C_ESCAPE);
      rtn = strlen(chroma->token);
      chroma->token[rtn++] = '\\';
      chroma->token[rtn] = chroma->input[chroma->inpnt++];
      chroma->token[rtn + 1] = '\0';
      strcat(chroma->token, ENDTAG);
      rtn = strlen(chroma->token);
      continue;
    }
    else if(ischar && (chroma->token[rtn] == '\''))
    {
      // Might be escaped.
      rtn++;
      break;
    }
    else if(!ischar && (chroma->token[rtn] == '"'))
    {
      // Might be escaped.
      rtn++;
      break;
    }
    else if(!ischar && (chroma->token[rtn] == '%'))
    {
      // Escaped inline parameter inclusion.
      // FIXME: No bounds checking.
      chroma->token[rtn] = '\0';
      strcat(chroma->token, C_ESCAPE);
      rtn = strlen(chroma->token);
      chroma->token[rtn++] = '%';
      chroma->token[rtn] = chroma->input[chroma->inpnt++];
      while(!chroma_is_format(chroma->token[rtn]))
      {
	rtn++;
	chroma->token[rtn] = chroma->input[chroma->inpnt++];
      }
      chroma->token[rtn + 1] = '\0';
      strcat(chroma->token, ENDTAG);
      rtn = strlen(chroma->token);
      continue;
    }
    rtn += chroma_escape_token(&chroma->token[rtn], chroma->token[rtn]);
  }
  chroma->state = STRING;
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_is_preproc(char preproc)
{
  int rtn = 0;

  if(preproc == '#')
  {
    rtn = 1;
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_parse_preproc(struct _chroma *chroma)
{
  int rtn = 0;

  chroma->token[rtn++] = chroma->input[chroma->inpnt++];
  while(chroma->inpnt < chroma->insize)
  {
    if(isspace(chroma->input[chroma->inpnt]))
    {
      chroma->state = PREPROC;
      break;
    }
    chroma->token[rtn++] = chroma->input[chroma->inpnt++];
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_is_literal(struct _chroma *chroma)
{
  char next;
  int n;
  int rtn = 0;

  n = chroma->inpnt;
  next = chroma->input[n++];
  if(next == '-')
  {
    // Posible negation.
    next = chroma->input[n++];
  }
  if(next == '0')
  {
    rtn++;
    next = chroma->input[n++];
    if((next == 'x') || (next == 'X'))
    {
      // Hex.
      next = chroma->input[n++];
      while(isxdigit(next))
      {
	rtn++;
	next = chroma->input[n++];
      }
    }
    else if((next == 'b') || (next == 'B'))
    {
      // Binary.
      next = chroma->input[n++];
      while((next == '0') || (next == '1'))
      {
	rtn++;
	next = chroma->input[n++];
      }
    }
    else
    {
      // Octal.
      while(isdigit(next))
      {
	rtn++;
	next = chroma->input[n++];
      }
    }
  }
  else
  {
    // Maybe decial.
    while(isdigit(next))
    {
      //kludge = 1;
      rtn++;
      next = chroma->input[n++];
    }
  }
  if(!isspace(next) && !ispunct(next))
  {
    rtn = 0;
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_parse_literal(struct _chroma *chroma)
{
  int rtn = 0;
  // FIXME: Combine work with is_literal.
  if(chroma->input[chroma->inpnt] == '-')
  {
    // Negation.
    chroma->token[rtn++] = chroma->input[chroma->inpnt++];
  }
  if(chroma->input[chroma->inpnt] == '0')
  {
    // Parse...
    chroma->token[rtn++] = chroma->input[chroma->inpnt++];
    if((chroma->input[chroma->inpnt] == 'x') || (chroma->input[chroma->inpnt] == 'X'))
    {
      // Parse...
      chroma->token[rtn++] = chroma->input[chroma->inpnt++];
      // HEX.
      while(isxdigit(chroma->input[chroma->inpnt]))
      {
	chroma->token[rtn++] = chroma->input[chroma->inpnt++];
      }
    }
    else if((chroma->input[chroma->inpnt] == 'b') || (chroma->input[chroma->inpnt] == 'B'))
    {
      // Parse...
      chroma->token[rtn++] = chroma->input[chroma->inpnt++];
      // Binary.
      while((chroma->input[chroma->inpnt] == '0') || (chroma->input[chroma->inpnt] == '1'))
      {
	chroma->token[rtn++] = chroma->input[chroma->inpnt++];
      }
    }
    else
    {
      // Octal.
      while((chroma->input[chroma->inpnt] >= '0') && (chroma->input[chroma->inpnt] < '8'))
      {
	chroma->token[rtn++] = chroma->input[chroma->inpnt++];
      }
    }
  }
  else
  {
    // Decimal.
    while(isdigit(chroma->input[chroma->inpnt]))
    {
      chroma->token[rtn++] = chroma->input[chroma->inpnt++];
    }
  }
  //chroma->token[rtn] = '\0';
  chroma->state = LITERAL;
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_match_include(struct _chroma *chroma)
{
  int rtn = 0;

  if(strcmp(chroma->token, "#include") == 0)
  {
    rtn = strlen(chroma->token);
    chroma->token[rtn++] = chroma->input[chroma->inpnt++];
    chroma->token[rtn] = '\0';
    strcat(chroma->token, ENDTAG);
    strcat(chroma->token, C_STRING);
    rtn = strlen(chroma->token);
    while(chroma->inpnt < chroma->insize)
    {
      chroma->token[rtn] = chroma->input[chroma->inpnt++];
      rtn += chroma_escape_token(&chroma->token[rtn], chroma->token[rtn]);
      if(chroma->input[chroma->inpnt] == '\n')
      {
	rtn++;
	chroma->state = PREPROC;
	break;
      }
    }
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_find_keyword(const void *key, const void *try)
{
  const char * const *arg = try;

  report(dbglevl_verbose, "chroma_find_keyword(%s, %s).\n", key, *arg);
  return strcmp(key, *arg);
}

//-----------------------------------------------------------------------------

static int chroma_match_keyword(struct _chroma *chroma)
{
  int rtn = 0;

  report(dbglevl_verbose, "chroma_match_keyword(%s).\n", chroma->token);
  if(bsearch(chroma->token, &KeyWords, numKeyWords, sizeof(KeyWords[0]), chroma_find_keyword))
  {
    chroma->state = KEYWORD;
    rtn = 1;
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_get_token(struct _chroma *chroma)
{
  int prescape = 0;
  int rtn = 0;

  chroma->state = UNKNOWN;
  while(chroma->inpnt < chroma->insize)
  {
    chroma->token[rtn] = chroma->input[chroma->inpnt];
    if(chroma->token[rtn] == '\n')
    {
      // Newline.
      if(rtn)
      {
	break;
      }
      rtn++;
      chroma->inpnt++;
      chroma->state = NEWLINE;
      break;
    }
    else if(chroma_is_comment(chroma))
    {
      // Comment.
      if(rtn)
      {
	break;
      }
      rtn = chroma_parse_comment(chroma);
      prescape = 1;
      break;
    }
    else if(isspace(chroma->token[rtn]))
    {
      // Initial whitespace.
      if((chroma->state != SPACE) && rtn)
      {
	break;
      }
      chroma->state = SPACE;
      rtn++;
      chroma->inpnt++;
      continue;
    }
    else if(chroma->state == SPACE)
    {
      // Continuing whitespace.
      break;
    }
    else if(!(chroma->flags & ch_comment) && chroma_is_string(chroma->token[rtn]))
    {
      // String.
      if(rtn)
      {
	break;
      }
      rtn = chroma_parse_string(chroma);
      prescape = 1;
      break;
    }
    else if(!rtn && chroma_is_literal(chroma))
    {
      // Number.
      //if(rtn)
      //{
      //	break;
      //}
      rtn = chroma_parse_literal(chroma);
      break;
    }
    else if(chroma_is_brace(chroma->token[rtn]))
    {
      // Brace {}()[].
      if(rtn)
      {
	break;
      }
      rtn = chroma_parse_brace(chroma);
      break;
    }
    else if(chroma_is_operator(chroma->token[rtn]))
    {
      // Operator !&|^<>=+-*/%.,?:;'.
      if(rtn)
      {
	break;
      }
      rtn = chroma_parse_operator(chroma);
      break;
    }
    else if(!rtn && chroma_is_preproc(chroma->token[rtn]))
    {
      // Preprocessor directive.
      rtn = chroma_parse_preproc(chroma);
      break;
    }
    chroma->inpnt++;
    rtn++;
  }
  chroma->token[rtn] = '\0';
  if(chroma->state == PREPROC)
  {
    if(chroma_match_include(chroma))
    {
      prescape = 1;
    }
  }
  else if(chroma->state == UNKNOWN)
  {
    if(!chroma_match_keyword(chroma))
    {
      // Unidentified.
      // FIXME: Bad logic.
    }
  }
  if(!prescape)
  {
    chroma_escape_html(chroma);
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_write_tag(int file, int escaped, char *tag, int size)
{
  int rtn = 0;

  if(escaped)
  {
    write(file, ENDTAG, sizeof(ENDTAG) - 1);
  }
  if(tag)
  {
    write(file, tag, size);
    rtn = 1;
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static int chroma_code_html(char *fname)
{
  int escaped = 0;
  int rtn = 0;
  struct _chroma *chroma = chroma_start(fname);

  if(chroma)
  {
    while(chroma_get_token(chroma))
    {
      report(dbglevl_debug, "chroma_get_token() ");
      switch(chroma->state)
      {
	case BRACE:
	  report(dbglevl_debug, "brace   ");
	  escaped = chroma_write_tag(chroma->outfile, escaped, C_BRACE, sizeof(C_BRACE) - 1);
	  break;

	case COMMENT:
	  report(dbglevl_debug, "comment ");
	  if(chroma->last != chroma->state)
	  {
	    escaped = chroma_write_tag(chroma->outfile, escaped, C_COMMENT, sizeof(C_COMMENT) - 1);
	  }
	  break;

	case KEYWORD:
	  report(dbglevl_debug, "keyword ");
	  if(chroma->last != chroma->state)
	  {
	    escaped = chroma_write_tag(chroma->outfile, escaped, C_KEYWORD, sizeof(C_KEYWORD) - 1);
	  }
	  break;

	case LITERAL:
	  report(dbglevl_debug, "literal ");
	  escaped = chroma_write_tag(chroma->outfile, escaped, C_LITERAL, sizeof(C_LITERAL) - 1);
	  break;

	case NEWLINE:
	  report(dbglevl_debug, "newline ");
	  chroma->state = chroma->last;
	  break;

	case SPACE:
	  report(dbglevl_debug, "space   ");
	  chroma->state = chroma->last;
	  break;

	case STRING:
	  report(dbglevl_debug, "string  ");
	  escaped = chroma_write_tag(chroma->outfile, escaped, C_STRING, sizeof(C_STRING) - 1);
	  break;

	case OPERATOR:
	  report(dbglevl_debug, "operator");
	  escaped = chroma_write_tag(chroma->outfile, escaped, C_OPERATOR, sizeof(C_OPERATOR) - 1);
	  break;

	case PREPROC:
	  report(dbglevl_debug, "preproc ");
	  escaped = chroma_write_tag(chroma->outfile, escaped, C_PREPROCESSOR, sizeof(C_PREPROCESSOR) - 1);
	  break;

	case UNKNOWN:
	default:
	  report(dbglevl_debug, "unknown ");
	  if(chroma->last != chroma->state)
	  {
	    escaped = chroma_write_tag(chroma->outfile, escaped, NULL, 0);
	  }
	  break;
      }
      if(chroma->outfile > 0)
      {
	report(dbglevl_debug, " %s%c", chroma->token, *chroma->token != '\n' ? '\n' : '\0');
	write(chroma->outfile, chroma->token, strlen(chroma->token));
      }
      chroma->last = chroma->state;
    }
    chroma_write_tag(chroma->outfile, escaped, NULL, 0);
    chroma_finish(chroma);
  }
  return rtn;
}

//-----------------------------------------------------------------------------

int main(int argc, char **argv)
{
  int rtn = 1;

  report_init(0);
  //FILE *log = fopen("c-html.log", "w");
  //report_init(log);
  report_set_level(dbglevl_debug);
  if(argc == 2)
  {
    // FIXME: Expand parameters.
    rtn = chroma_code_html(argv[1]);
  }
  else
  {
    report(dbglevl_none, "usage %s file.\n", argv[0]);
  }
  return rtn;
}

//-----------------------------------------------------------------------------
// end: c-html.c

  

Oh yeah! The Atari ST ended up joining the ranks of the Atari 800 and the Commodore 64 as gaming machines.

Which I still have. Sigh!

So How Does The Code Work...

Essentially... It is a single pass compiler. It reads C source-code from a stream using a set of filters and a state machine to tokenize the input stream into a set of classes. It then escapes the different classes with HTML <span> tags. The output is usable as a raw formatted HTML file suitable of including between HTML <pre> tags using a companion CSS file to define the font decorations for the individual elements.

Although this is the way my original Emacs editor looked my current version has a few more enhancements. However; To include those requires caching the full source-line as tokens (classes) to apply better type analysis. It would also require keeping track of the brackets to maintain proper context. This way definitions, variables and constants can be included in the highlighting.

Download Current Project.

10892