Unit1.cpp

//---------------------------------------------------------------------------
#include <fstream>
#include <vcl.h>
#include <registry.hpp>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
// CLASS PARAMETERS
//---------------------------------------------------------------------------
__fastcall Parameters::Parameters()
{
  //Default values
  Comment=new Code;
  Comment->Color=clBlue;
  Comment->Bold=false;
  Comment->Italic=true;
  Comment->Underline=false;
  ReservedWord=new Code;
  ReservedWord->Color=clBlack;
  ReservedWord->Bold=true;
  ReservedWord->Italic=false;
  ReservedWord->Underline=false;
  Preprocessor=new Code;
  Preprocessor->Color=clGreen;
  Preprocessor->Bold=false;
  Preprocessor->Italic=true;
  Preprocessor->Underline=false;
  QuotedString=new Code;
  QuotedString->Color= clRed;
  QuotedString->Bold=false;
  QuotedString->Italic=false;
  QuotedString->Underline=false;
  ReservedWords=new TStringList();
}
//---------------------------------------------------------------------------
__fastcall Parameters::~Parameters()
{
  delete Comment;
  delete ReservedWord;
  delete Preprocessor;
  delete QuotedString;
  delete ReservedWords;
}
//---------------------------------------------------------------------------
void __fastcall Parameters::LoadFromReg()
{
  TRegistry *AppReg=new TRegistry;
  AppReg->RootKey=HKEY_CURRENT_USER;
  if(AppReg->OpenKey("Software\\Michel Leunen\\Src2Html",false))
  {
      try
      {
        Comment->Color=static_cast<TColor>(AppReg->ReadInteger("Comment.Color"));
        Comment->Bold=AppReg->ReadBool("Comment.Bold");
        Comment->Italic=AppReg->ReadBool("Comment.Italic");
        Comment->Underline=AppReg->ReadBool("Comment.Underline");
        Preprocessor->Color=static_cast<TColor>(AppReg->ReadInteger("Preprocessor.Color"));
        Preprocessor->Bold=AppReg->ReadBool("Preprocessor.Bold");
        Preprocessor->Italic=AppReg->ReadBool("Preprocessor.Italic");
        Preprocessor->Underline=AppReg->ReadBool("Preprocessor.Underline");
        ReservedWord->Color=static_cast<TColor>(AppReg->ReadInteger("ReservedWord.Color"));
        ReservedWord->Bold=AppReg->ReadBool("ReservedWord.Bold");
        ReservedWord->Italic=AppReg->ReadBool("ReservedWord.Italic");
        ReservedWord->Underline=AppReg->ReadBool("ReservedWord.Underline");
        QuotedString->Color=static_cast<TColor>(AppReg->ReadInteger("QuotedString.Color"));
        QuotedString->Bold=AppReg->ReadBool("QuotedString.Bold");
        QuotedString->Italic=AppReg->ReadBool("QuotedString.Italic");
        QuotedString->Underline=AppReg->ReadBool("QuotedString.Underline");
        HtmlFont=AppReg->ReadString("HtmlFont");
        ReservedWords->Clear();
        ReservedWords->CommaText=AppReg->ReadString("ReservedWords");
      }
      catch (...)
      {
      }
  }
  delete AppReg;
}
//---------------------------------------------------------------------------
void __fastcall Parameters::SaveToReg()
{
  TRegistry *AppReg=new TRegistry;
  AppReg->RootKey=HKEY_CURRENT_USER;
  AppReg->LazyWrite=false;
  AppReg->OpenKey("Software\\Michel Leunen\\Src2Html",true);
  AppReg->WriteInteger("Comment.Color",Comment->Color);
  AppReg->WriteBool("Comment.Bold",Comment->Bold);
  AppReg->WriteBool("Comment.Italic",Comment->Italic);
  AppReg->WriteBool("Comment.Underline",Comment->Underline);
  AppReg->WriteInteger("ReservedWord.Color",ReservedWord->Color);
  AppReg->WriteBool("ReservedWord.Bold",ReservedWord->Bold);
  AppReg->WriteBool("ReservedWord.Italic",ReservedWord->Italic);
  AppReg->WriteBool("ReservedWord.Underline",ReservedWord->Underline);
  AppReg->WriteInteger("Preprocessor.Color",Preprocessor->Color);
  AppReg->WriteBool("Preprocessor.Bold",Preprocessor->Bold);
  AppReg->WriteBool("Preprocessor.Italic",Preprocessor->Italic);
  AppReg->WriteBool("Preprocessor.Underline",Preprocessor->Underline);
  AppReg->WriteInteger("QuotedString.Color",QuotedString->Color);
  AppReg->WriteBool("QuotedString.Bold",QuotedString->Bold);
  AppReg->WriteBool("QuotedString.Italic",QuotedString->Italic);
  AppReg->WriteBool("QuotedString.Underline",QuotedString->Underline);
  AppReg->WriteString("HtmlFont",HtmlFont);
  AppReg->WriteString("ReservedWords",ReservedWords->CommaText);
  delete AppReg;
}
//---------------------------------------------------------------------------
// CLASS TFORM1
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)
{
  Par=new Parameters();
  Par->HtmlFont="Courier New";
  Par->ReservedWords->Clear();
  Par->ReservedWords->Assign(ReservedWordsComboBox->Items);
  Par->LoadFromReg();
  CodeListBox->ItemIndex=0;
  //global variable used when parsing the source file
  Comment=false;
}
//---------------------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
  delete Par;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::SaveButtonClick(TObject *Sender)
{
  if(SourceEdit->Text=="")
  {
    ShowMessage("Select a source file");
    return;
  }
  //parse the source file
  if(!ToHtml(SourceEdit->Text))
  {
    ShowMessage("Unable to load the source file!");
    return;
  }
  Par->SaveToReg();
  //Application->Terminate();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ChooseFileButtonClick(TObject *Sender)
{
  OpenDialog->InitialDir=ExtractFileDir(Application->ExeName);
  if(OpenDialog->Execute())
  {
    SourceEdit->Text=OpenDialog->FileName;
  }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ChooseColorButtonClick(TObject *Sender)
{
  switch (CodeListBox->ItemIndex)
  {
    case 0:
      ColorDialog->Color=Par->Comment->Color;
      break;
    case 1:
      ColorDialog->Color=Par->ReservedWord->Color;
      break;
    case 2:
      ColorDialog->Color=Par->Preprocessor->Color;
      break;
    case 3:
      ColorDialog->Color=Par->QuotedString->Color;
      break;
    default:
      ShowMessage("Select an item in Code Listbox");
      return;
  }
  if(ColorDialog->Execute())
  {
    switch (CodeListBox->ItemIndex)
    {
      case 0:
        Par->Comment->Color=ColorDialog->Color;
        break;
      case 1:
        Par->ReservedWord->Color=ColorDialog->Color;
        break;
      case 2:
        Par->Preprocessor->Color=ColorDialog->Color;
        break;
      case 3:
        Par->QuotedString->Color=ColorDialog->Color;
        break;
    }
  }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ChooseFontButtonClick(TObject *Sender)
{
  FontDialog->Font->Name=Par->HtmlFont;
  FontDialog->Font->Size=StrToInt(SizeEdit->Text);
  if(FontDialog->Execute())
  {
    Par->HtmlFont=FontDialog->Font->Name;
    SizeEdit->Text=IntToStr(FontDialog->Font->Size);
  }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::BoldCheckBoxClick(TObject *Sender)
{
  switch (CodeListBox->ItemIndex)
  {
    case 0:
      Par->Comment->Bold=BoldCheckBox->Checked;
      break;
    case 1:
      Par->ReservedWord->Bold=BoldCheckBox->Checked;
      break;
    case 2:
      Par->Preprocessor->Bold=BoldCheckBox->Checked;
      break;
    case 3:
      Par->QuotedString->Bold=BoldCheckBox->Checked;
      break;
    default:
      ShowMessage("Select an item in Code Listbox");
  }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::ItalicCheckBoxClick(TObject *Sender)
{
  switch (CodeListBox->ItemIndex)
  {
    case 0:
      Par->Comment->Italic=ItalicCheckBox->Checked;
      break;
    case 1:
      Par->ReservedWord->Italic=ItalicCheckBox->Checked;
      break;
    case 2:
      Par->Preprocessor->Italic=ItalicCheckBox->Checked;
      break;
    case 3:
      Par->QuotedString->Italic=ItalicCheckBox->Checked;
      break;
    default:
      ShowMessage("Select an item in Code Listbox");
  }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::UnderlineCheckBoxClick(TObject *Sender)
{
  switch (CodeListBox->ItemIndex)
  {
    case 0:
      Par->Comment->Underline=UnderlineCheckBox->Checked;
      break;
    case 1:
      Par->ReservedWord->Underline=UnderlineCheckBox->Checked;
      break;
    case 2:
      Par->Preprocessor->Underline=UnderlineCheckBox->Checked;
      break;
    case 3:
      Par->QuotedString->Underline=UnderlineCheckBox->Checked;
      break;
    default:
      ShowMessage("Select an item in Code Listbox");
  }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::CodeListBoxClick(TObject *Sender)
{
  switch (CodeListBox->ItemIndex)
  {
    case 0:
      BoldCheckBox->Checked=Par->Comment->Bold;
      ItalicCheckBox->Checked=Par->Comment->Italic;
      UnderlineCheckBox->Checked=Par->Comment->Underline;
      break;
    case 1:
      BoldCheckBox->Checked=Par->ReservedWord->Bold;
      ItalicCheckBox->Checked=Par->ReservedWord->Italic;
      UnderlineCheckBox->Checked=Par->ReservedWord->Underline;
      break;
    case 2:
      BoldCheckBox->Checked=Par->Preprocessor->Bold;
      ItalicCheckBox->Checked=Par->Preprocessor->Italic;
      UnderlineCheckBox->Checked=Par->Preprocessor->Underline;
      break;
    case 3:
      BoldCheckBox->Checked=Par->QuotedString->Bold;
      ItalicCheckBox->Checked=Par->QuotedString->Italic;
      UnderlineCheckBox->Checked=Par->QuotedString->Underline;
      break;
    default:
      ShowMessage("Select an item in Code Listbox");
  }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::AddButtonClick(TObject *Sender)
{
  if(ReservedWordsComboBox->Text!="")
  {
    ReservedWordsComboBox->Items->Add(ReservedWordsComboBox->Text);
    Par->ReservedWords->Clear();
    Par->ReservedWords->Assign(ReservedWordsComboBox->Items);
  }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::RemoveButtonClick(TObject *Sender)
{
  if(ReservedWordsComboBox->ItemIndex!=-1)
  {
    ReservedWordsComboBox->Items->Delete(ReservedWordsComboBox->ItemIndex);
    Par->ReservedWords->Clear();
    Par->ReservedWords->Assign(ReservedWordsComboBox->Items);
  }
}
//---------------------------------------------------------------------------
bool __fastcall TForm1::ToHtml(AnsiString FileName)
{
  //load source file in a StringList
  //each string corresponds to a source line
  TStringList *Input=new TStringList;
  try
  {
    Input->LoadFromFile(FileName);
  }
  catch(...)
  {
    return false;
  }
  //create a file whose name is the same as the source
  //replace extension with .html
  int Pos=FileName.AnsiPos(".");
  AnsiString Ext=FileName.SubString(Pos+1,FileName.Length()-Pos);
  AnsiString NewFileName=FileName.SubString(0,Pos-1)+"_"+Ext+".html";
  TFileStream *Output=new TFileStream(NewFileName,fmCreate);
  //write html header
  AnsiString Buffer=WriteHeader();
  Output->Write(Buffer.c_str(),Buffer.Length());
  //Initialize ProgressBar
  ProgressBar1->Max=Input->Count;
  ProgressBar1->Position=0;
  //parse each source line
  for (int i=0;i<Input->Count;i++)
  {
    ParseLine(Output,Input->Strings[i]);
    Output->Write("<br>\r\n",6);
    ProgressBar1->Position++;
  }
  ProgressBar1->Position=0;
  //Write html footer
  AnsiString Footer="</div>\r\n</body>\r\n</html>";
  Output->Write(Footer.c_str(),Footer.Length());
  delete Input;
  delete Output;
  return true;
}
//---------------------------------------------------------------------------
AnsiString __fastcall TForm1::WriteHeader()
{
  AnsiString Header="<html>\r\n<head>\r\n  <title>";
  //get title, if any
  if(TitleEdit->Text=="")Header +="Untitled";
  else Header += TitleEdit->Text;
  Header+="</title>\r\n  <meta http-equiv=\"content-type\" content=\"text/html;"
          " charset=iso-8859-1\">\r\n"
          "  <meta name=\"author\" content=\"Michel Leunen (michel@leunen.com\">\r\n"
          "  <meta name=\"generator\" content=\"Src2Html V1.0\">\r\n"
          "  <style type=\"text/css\">\r\n  <!--\r\n"
          "    .source {font-family: ";
  //get font
  Header +=Par->HtmlFont;
  Header +=", Courier, mono; font-size: ";
  //get size
  Header +=SizeEdit->Text;
  //get each parameter value corresponding to each token type
  //we have to reverse the color value in order to have RGB
  Header +="pt; margin-left: ";
  Header +=MarginEdit->Text;
  Header +="px; color: #000000}\r\n    .comment {color: #";
  Header +=ReverseHex(IntToHex(Par->Comment->Color,6));
  if(Par->Comment->Bold)Header +="; font-weight: bold";
  if(Par->Comment->Italic)Header +="; font-style: italic";
  if(Par->Comment->Underline)Header +="; text-decoration: underline";
  Header +="}\r\n    .reserved {color : #";
  Header +=ReverseHex(IntToHex(Par->ReservedWord->Color,6));
  if(Par->ReservedWord->Bold)Header +="; font-weight: bold";
  if(Par->ReservedWord->Italic)Header +="; font-style: italic";
  if(Par->ReservedWord->Underline)Header +="; text-decoration: underline";
  Header +="}\r\n    .preprocessor {color : #";
  Header +=ReverseHex(IntToHex(Par->Preprocessor->Color,6));
  if(Par->Preprocessor->Bold)Header +="; font-weight: bold";
  if(Par->Preprocessor->Italic)Header +="; font-style: italic";
  if(Par->Preprocessor->Underline)Header +="; text-decoration: underline";
  Header +="}\r\n    .quotedstring {color : #";
  Header +=ReverseHex(IntToHex(Par->QuotedString->Color,6));
  if(Par->QuotedString->Bold)Header +="; font-weight: bold";
  if(Par->QuotedString->Italic)Header +="; font-style: italic";
  if(Par->QuotedString->Underline)Header +="; text-decoration: underline";
  Header +="}\r\n  -->\r\n";
  Header +="  </style>\r\n</head>\r\n<body bgcolor=\"#FFFFFF\">\r\n"
           "<center><h1>";
  Header +=ExtractFileName(SourceEdit->Text);
  Header +="</h1></center>\r\n<div class=\"source\"><p>\r\n";
  return Header;
}
//---------------------------------------------------------------------------
AnsiString __fastcall TForm1::ReverseHex(AnsiString HexValue)
{
  AnsiString ReverseString;
  ReverseString.Insert(HexValue.SubString(1,2),1);
  ReverseString.Insert(HexValue.SubString(3,2),1);
  ReverseString.Insert(HexValue.SubString(5,2),1);
  return ReverseString;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ParseLine(TFileStream *Out,AnsiString Line)
{
  if(Line=="")return;                   //empty line
  if (Comment==true)                    //there is a comment in a previous line
  {
    int Position=Line.Pos("*/");        //end of comment in the line?
    if(Position!=0)                     //yep
    {
      //print beginning of line as comment
      WriteComment(Out,Line.SubString(1,Position+1));
      Comment=false;
      //parse end of line
      AnsiString NewLine=Line.SubString(Position+2,Line.Length());
      ParseLine(Out,NewLine);           //parse end of line
      return;
    }
    else                                //no, whole line is a comment
    {
      WriteComment(Out,Line);
      return;
    }
  }
  if(Line[1]==' ')                      //blank character
  {
    Out->Write(" ",6);
    if(Line.Length()>1)                 //if more characters in the line
    {
      //parse end of line
      ParseLine(Out,Line.SubString(2,Line.Length()));
      return;
    }
    //if blank = last character, parse next source line
    else return;
  }
  if(Line[1]=='\t')                     //tab
  {
    for(int j=0;j<StrToInt(TabEdit->Text);j++)
    {
      Out->Write(" ",6);           //replace tab with blank character
    }
    if(Line.Length()>1)                 //if more characters in the line
    {
      //parse end of line
      ParseLine(Out,Line.SubString(2,Line.Length()));
      return;
    }
    //if tab = last character, parse next source line
    else return;
  }
  if(Line[1]=='#')                      //preprocessor directive
  {
    int Position=Line.Pos("//");        //comment in the line?
    if(Position!=0)                     //yep
    {
      //write preprocessor directve until comment
      WritePreprocessor(Out,Line.SubString(1,Position-1));
      //parse end of line beginning at comment
      AnsiString NewLine=Line.SubString(Position,Line.Length());
      ParseLine(Out,NewLine);
      return;
    }
    else                                //no "//" in the line
    {
      Position=Line.Pos("/*");          //comment in the line?
      if(Position!=0)                   //yes
      {
        //write preprocessor directive until comment
        WritePreprocessor(Out,Line.SubString(1,Position-1));
        //parse end of line beginning at comment
        AnsiString NewLine=Line.SubString(Position,Line.Length());
        ParseLine(Out,NewLine);
        return;
      }
      else                              //no comment in the line
      {
        //write whole line as preprocessor directive
        WritePreprocessor(Out,Line);
        return;
      }
    }
  }
  if(Line[1]=='/')                      //Comment?
  {
    if(Line.Length()>1)                 //more than 1 char on the line
    {
      if(Line[2]=='/')                  //comment "//"
      {
        WriteComment(Out,Line);         //write whole line as comment
        return;
      }
      if(Line[2]=='*')                  //comment "/*"
      {
        int Position=Line.Pos("*/");
        //end of comment in the same line
        if (Position!=0)                //yes
        {
          //write comment until end of comment "*/"
          WriteComment(Out,Line.SubString(1,Position+1));
          //parse line beginning at end of comment
          ParseLine(Out,Line.SubString(Position+2,Line.Length()));
          return;
        }
        else                            //no end of comment "*/"
        {                               //in the line
          Comment=true;
          ParseLine(Out,Line);
          return;
        }
      }
    }
  }
  if(Line[1]=='"')                      //string
  {
    if(Line.Length()>1)                 //more than 1 char on the line
    {
      for(int i=2;i<Line.Length()+1;i++)
      {
        if(Line[i]=='"')                //get string until '"'
        {
          if (Line[i-1]!='\\')          //check if nested '"'
          {
            //write string
            WriteQuotedString(Out,Line.SubString(1,i));
            //parse end of line beginning after the ending '"'
            if(i<Line.Length())ParseLine(Out,Line.SubString(i+1,Line.Length()));
            return;
          }
        }
      }
    }
  }
  //here we already know that the beginning of line isn't a blank,
  //an empty line, a preprocessor directive, a comment or a string
  //it could be a reserved word or a 'normal' character
  int i=1;
  AnsiString RWord;
  //get all alpha characters, if any. In that case, it could be either a
  //reserved word or the name of a variable or function
  while((IsAlpha(Line[i]))&&(i<Line.Length()))
  {
    RWord+=Line[i];
    i++;
  }
  if(RWord!="")                         //it's not an alphanumerical character
  {
    if(IsReserved(RWord))               //Reserved word?
    {
      //write the reserved word
      WriteReserved(Out,RWord);
      //parse line after the reserved word
      ParseLine(Out,Line.SubString(i,Line.Length()));
      return;
    }
    else                                //it's not a reserved word
    {
      //write the word (variable name or function name)
      Out->Write(RWord.c_str(),RWord.Length());
      if(Line.Length()>1)                   //more than 1 char on the line
      {
        //parse line after the variable or function name
        ParseLine(Out,Line.SubString(i,Line.Length()));
      }
      return;
    }
  }
  else
  {
    //it's not a reserved word nor a variable or function name
    //it's therefor a symbol like *./+=(){}[]...
    RWord+=ReplaceLtAndGt(Line[1]);
    Out->Write(RWord.c_str(),RWord.Length());
    if(Line.Length()>1)                     //more than 1 char on the line
    {
      //if there is more characters in the line, parse the remainder
      ParseLine(Out,Line.SubString(2,Line.Length()));
    }
    return;
  }
}
//---------------------------------------------------------------------------
AnsiString __fastcall TForm1::ReplaceBlank(AnsiString Buffer)
{
  AnsiString NewBuffer;
  for (int i=1;i<Buffer.Length()+1;i++)
  {
    if(Buffer[i]==' ')NewBuffer.Insert(" ",NewBuffer.Length()+1);
    else NewBuffer.Insert(Buffer[i],NewBuffer.Length()+1);
  }
  return NewBuffer;
}
//---------------------------------------------------------------------------
AnsiString __fastcall TForm1::ReplaceLtAndGt(AnsiString Buffer)
{
  AnsiString NewBuffer;
  for (int i=1;i<Buffer.Length()+1;i++)
  {
    if(Buffer[i]=='<')NewBuffer.Insert("<",NewBuffer.Length()+1);
    else
    {
      if(Buffer[i]=='>')NewBuffer.Insert(">",NewBuffer.Length()+1);
      else NewBuffer.Insert(Buffer[i],NewBuffer.Length()+1);
    }
  }
  return NewBuffer;
}
//---------------------------------------------------------------------------
bool __fastcall TForm1::IsAlpha(char ch)
{
  //all alphanumerical character plus _ (leading or ending)
  const char Table[64]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_";
  if(strchr(Table,ch))return true;
  else return false;
}
//---------------------------------------------------------------------------
bool __fastcall TForm1::IsReserved(AnsiString Word)
{
  for (int i=0;i<ReservedWordsComboBox->Items->Count;i++)
  {
    if(ReservedWordsComboBox->Items->Strings[i]==Word)return true;
  }
  return false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::WriteComment(TFileStream *Out,AnsiString Buffer)
{
  AnsiString Temp="<span class=\"comment\">";
  Temp+=ReplaceLtAndGt(ReplaceBlank(Buffer));
  Temp+="</span>";
  Out->Write(Temp.c_str(),Temp.Length());
}
//---------------------------------------------------------------------------
void __fastcall TForm1::WritePreprocessor(TFileStream *Out,AnsiString Buffer)
{
  AnsiString Temp="<span class=\"preprocessor\">";
  Temp+=ReplaceLtAndGt(ReplaceBlank(Buffer));
  Temp+="</span>";
  Out->Write(Temp.c_str(),Temp.Length());
}
//---------------------------------------------------------------------------
void __fastcall TForm1::WriteReserved(TFileStream *Out,AnsiString Buffer)
{
  AnsiString Temp="<span class=\"reserved\">";
  Temp+=Buffer;
  Temp+="</span>";
  Out->Write(Temp.c_str(),Temp.Length());
}
//---------------------------------------------------------------------------
void __fastcall TForm1::WriteQuotedString(TFileStream *Out,AnsiString Buffer)
{
  AnsiString Temp="<span class=\"quotedstring\">";
  Temp+=ReplaceBlank(ReplaceLtAndGt(Buffer));
  Temp+="</span>";
  Out->Write(Temp.c_str(),Temp.Length());
}
//---------------------------------------------------------------------------