/*
    CCCC - C and C++ Code Counter
    Copyright (C) 1994-2005 Tim Littlefair (tim_littlefair@hotmail.com)

    This program is free software; 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 program 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.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
*/
// cccc_htm.cc

// this file defines HTML output facilities for the CCCC project

#include "cccc.h"
#include "cccc_itm.h"
#include "cccc_htm.h"

// I would love to use the C++ standard preprocessor
// directive #if here, but I have had reports before now
// of people who are using compilers which only support
// #ifdef.
#ifdef CCCC_CONF_W32VC
#include <direct.h>
#else
#ifdef CCCC_CONF_W32BC
#include <direct.h>
#else
#include <unistd.h>
#endif
#endif

#include <time.h>
#include <sys/stat.h>
#include "cccc_utl.h"


typedef std::map<string,Source_Anchor> source_anchor_map_t;
source_anchor_map_t source_anchor_map;
 

// class static data members
CCCC_Project* CCCC_Html_Stream::prjptr;
string CCCC_Html_Stream::outdir;
string CCCC_Html_Stream::libdir;

void CCCC_Html_Stream::GenerateReports(CCCC_Project* prj, 
				       int report_mask, 
				       const string& file, 
				       const string& dir) 
{ 
  prjptr=prj;
  outdir=dir;

  CCCC_Html_Stream main_html_stream(file.c_str(),"Report on software metrics");

  if(report_mask & rtCONTENTS)
    {
      // For testing purposes, we want to be able to disable the inclusion
      // of the current time in the report.  This enables us to store a
      // reference version of the report in RCS and expect the program
      // to generate an identical one at regression testing time.
      if(report_mask & rtSHOW_GEN_TIME)
	{
	  main_html_stream.Table_Of_Contents(report_mask,true);
	}
      else
	{
	  main_html_stream.Table_Of_Contents(report_mask,false);
	}
    }

  if(report_mask & rtSUMMARY)
    {
      main_html_stream.Project_Summary();
    }

  if(report_mask & rtPROC1)
    {
      main_html_stream.Procedural_Summary();
    }

  if(report_mask & rtPROC2)
    {
      main_html_stream.Procedural_Detail();
    }

  if(report_mask & rtOODESIGN)
    {
      main_html_stream.OO_Design();
    }

  if(report_mask & rtSTRUCT1)
    {
      main_html_stream.Structural_Summary();
    }

  if(report_mask & rtSTRUCT2)
    {
      main_html_stream.Structural_Detail();
    }

  if(report_mask & rtSEPARATE_MODULES)
    {
      main_html_stream.Separate_Modules();
    }

  if(report_mask & rtOTHER)
    {
      main_html_stream.Other_Extents();
    }

  if(report_mask & rtSOURCE)
    {
      main_html_stream.Source_Listing();
    }


  if(report_mask & rtCCCC)
    {
      main_html_stream.Put_Section_Heading("About CCCC","infocccc",1);
      main_html_stream.fstr 
	<< "<P>This report was generated by the program CCCC, which is FREELY "
	<< "REDISTRIBUTABLE but carries NO WARRANTY." << endl
        << "<P>CCCC was developed by Tim Littlefair. " << endl
	<< "as part of a PhD research project. "
        << "This project is now completed and descriptions of the "
        << "findings can be accessed at "
	<< "<A HREF=http://www.chs.ecu.edu.au/~tlittlef>"
	<< "http://www.chs.ecu.edu.au/~tlittlef</A>. "
        << "<P>User support for CCCC can be obtained by "
        << "<A HREF=mailto:cccc-users@lists.sourceforge.net>"
        << "mailing the list cccc-users@lists.sourceforge.net</A>."
	<< "<P>Please also visit the new CCCC development website at "
	<< "<A HREF=http://sarnold.github.io/cccc/>http://sarnold.github.io/cccc/</A>."
	<< endl;
    }
}

CCCC_Html_Stream::~CCCC_Html_Stream()
{
  fstr << "</BODY></HTML>" << endl; 
  fstr.close();
}

void CCCC_Html_Stream::Table_Of_Contents(int report_mask, bool showGenTime)
{
  // record the number of report parts in the table, and the 
  // stream put pointer
  // if we find that we have only generated a single part, we supress
  // the TOC by seeking to the saved stream offset
  int number_of_report_parts=0;
  int saved_stream_offset=fstr.tellp();

  fstr << "<TABLE BORDER WIDTH=100%>" << endl
       << "<TR><TH COLSPAN=2>" << endl
       << "CCCC Software Metrics Report";
  if( prjptr->name(nlSIMPLE)!="" )
    {
      fstr << " on project " << prjptr->name(nlSIMPLE); 
    }
  fstr << endl;

  // we have the option to disable the display of the generation time
  // so that we can generate identical reports for regression testing
  if(showGenTime==true)
    {
      time_t generationTime=time(NULL);
      fstr << "<BR> generated " << ctime(&generationTime) << endl;
    }

  fstr << "</TR>" << endl;

  if(report_mask & rtSUMMARY)
    {
      Put_Section_TOC_Entry(
			    "Project Summary","projsum",
			    "Summary table of high level measures summed "
			    "over all files processed in the current run.");
      number_of_report_parts++;
    }

  if(report_mask & rtPROC1)
    {
      Put_Section_TOC_Entry(
			    "Procedural Metrics Summary", "procsum",
			    "Table of procedural measures (i.e. lines of "
			    "code, lines of comment, McCabe's cyclomatic "
			    "complexity summed over each module.");
      number_of_report_parts++;
    }

  if(report_mask & rtPROC2)
    {
      Put_Section_TOC_Entry(
			    "Procedural Metrics Detail", "procdet",
			    "The same procedural metrics as in the procedural "
			    "metrics summary, reported for individual "
			    "functions, grouped by module.");
      number_of_report_parts++;
    }
 
  if(report_mask & rtOODESIGN)
    {
      Put_Section_TOC_Entry(
			    "Object Oriented Design","oodesign",
			    "Table of four of the 6 metrics proposed by "
			    "Chidamber and Kemerer in their various papers on "
			    "'a metrics suite for object oriented design'.");
      number_of_report_parts++;
    }

  if(report_mask & rtSTRUCT1)
    {
      Put_Section_TOC_Entry(
			    "Structural Metrics Summary", "structsum",
			    "Structural metrics based on the relationships of "
			    "each module with others.  Includes fan-out (i.e. "
			    "number of other modules the current module "
			    "uses), fan-in (number of other modules which use "
			    "the current module), and the Information Flow "
			    "measure suggested by Henry and Kafura, which "
			    "combines these to give a measure of coupling for "
			    "the module.");
      number_of_report_parts++;
    }
 
  if(report_mask & rtSTRUCT2)
    {
      Put_Section_TOC_Entry(
			    "Structural Metrics Detail", "structdet",
			    "The names of the modules included as clients and "
			    "suppliers in the counts for the Structural "
			    "Metrics Summary.");
      number_of_report_parts++;
    }

  if(report_mask & rtOTHER)
    {
      Put_Section_TOC_Entry(
			    "Other Extents", "other",
			    "Lexical counts for parts of submitted source "
			    "files which the analyser was unable to assign to "
			    "a module. Each record in this table relates to "
			    "either a part of the code which triggered a "
			    "parse failure, or to the residual lexical counts "
			    "relating to parts of a file not associated with "
			    "a specific module."
			    );
      number_of_report_parts++;
    }

  if(report_mask & rtCCCC)
    {
      Put_Section_TOC_Entry(
			    "About CCCC", "infocccc",
			    "A description of the CCCC program.");
      number_of_report_parts++;
    }

  fstr << "</TR></TABLE>" << endl;
  if(number_of_report_parts<2)
    {
      fstr.seekp(saved_stream_offset);
    }
}

void CCCC_Html_Stream::Put_Section_Heading(
					   string heading_title, 
					   string heading_tag, 
					   int heading_level)
{
  fstr << "<H" << heading_level << ">" 
       << "<A NAME=\"" << heading_tag << "\">" 
       << heading_title 
       << "</A></H" << heading_level
       << ">" << endl;
} 

void  CCCC_Html_Stream::Project_Summary() {
  Put_Section_Heading("Project Summary","projsum",1);

  fstr << "This table shows measures over the project as a whole." << endl;

  fstr << "<UL>" << endl;
  Metric_Description("NOM","Number of modules",
		     "Number of non-trivial modules identified by the "
		     "analyser.  Non-trivial modules include all classes, "
		     "and any other module for which member functions are "
		     "identified.");
  Metric_Description("LOC","Lines of Code",
		     "Number of non-blank, non-comment lines of source code "
		     "counted by the analyser.");
  Metric_Description("COM","Lines of Comments",
		     "Number of lines of comment identified by the analyser");
  Metric_Description("MVG","McCabe's Cyclomatic Complexity",
		     "A measure of the decision complexity of the functions "
		     "which make up the program."
		     "The strict definition of this measure is that it is "
		     "the number of linearly independent routes through "
		     "a directed acyclic graph which maps the flow of control "
		     "of a subprogram.  The analyser counts this by recording "
		     "the number of distinct decision outcomes contained "
		     "within each function, which yields a good approximation "
		     "to the formally defined version of the measure.");
  Metric_Description("L_C","Lines of code per line of comment",
		     "Indicates density of comments with respect to textual "
		     "size of program");
  Metric_Description("M_C","Cyclomatic Complexity per line of comment",
		     "Indicates density of comments with respect to logical "
		     "complexity of program");
  Metric_Description("IF4","Information Flow measure",
		     "Measure of information flow between modules suggested "
		     "by Henry and Kafura. The analyser makes an approximate "
		     "count of this by counting inter-module couplings "
		     "identified in the module interfaces.");

  fstr << "</UL>" << endl
       << "Two variants on the information flow measure IF4 are also "
       << "presented, one (IF4v) calculated using only relationships in the "
       << "visible part of the module interface, and the other (IF4c) "
       << "calculated using only those relationships which imply that changes "
       << "to the client must be recompiled of the supplier's definition "
       <<  "changes." 
       << endl << endl;
    
  // calculate the counts on which all displayed data will be based
  int nom=prjptr->get_count("NOM");  // number of modules 
  int loc=prjptr->get_count("LOC");  // lines of code
  int mvg=prjptr->get_count("MVG");  // McCabes cyclomatic complexity
  int com=prjptr->get_count("COM");  // lines of comment
  int if4=prjptr->get_count("IF4");    // intermodule complexity (all couplings)
  int if4v=prjptr->get_count("IF4v");  // intermodule complexity (visible only)
  int if4c=prjptr->get_count("IF4c");  // intermodule complexity (concrete only)
  int rej=prjptr->rejected_extent_table.get_count("LOC");
  
  fstr << "<TABLE BORDER WIDTH=100%>" << endl
       << "<TR>" << endl;
  Put_Header_Cell("Metric",70);
  Put_Header_Cell("Tag",10);
  Put_Header_Cell("Overall",10);
  Put_Header_Cell("Per Module",10);
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Number of modules");
  Put_Label_Cell("NOM");
  Put_Metric_Cell(nom);
  Put_Label_Cell("");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Lines of Code",700);
  Put_Label_Cell("LOC",120);
  Put_Metric_Cell(loc,"LOCp");
  Put_Metric_Cell(loc,nom,"LOCper");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("McCabe's Cyclomatic Number");
  Put_Label_Cell("MVG");
  Put_Metric_Cell(mvg,"MVGp");
  Put_Metric_Cell(mvg,nom,"MVGper");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Lines of Comment");
  Put_Label_Cell("COM");
  Put_Metric_Cell(com,"COM");
  Put_Metric_Cell(com,nom,"COMper");
  fstr << "</TR>" << endl;
  
  fstr << "<TR>" << endl;
  Put_Label_Cell("LOC/COM");
  Put_Label_Cell("L_C");
  Put_Metric_Cell(loc,com,"L_C");
  Put_Label_Cell("");
  fstr << "</TR>" << endl;
  
  fstr << "<TR>" << endl;
  Put_Label_Cell("MVG/COM");
  Put_Label_Cell("M_C");
  Put_Metric_Cell(mvg,com,"M_C");
  Put_Label_Cell("");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Information Flow measure (inclusive)");
  Put_Label_Cell("IF4");
  Put_Metric_Cell(if4);
  Put_Metric_Cell(if4,nom,"8.3");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Information Flow measure (visible)");
  Put_Label_Cell("IF4v");
  Put_Metric_Cell(if4v);
  Put_Metric_Cell(if4v,nom,"8.3");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Information Flow measure (concrete)");
  Put_Label_Cell("IF4c");
  Put_Metric_Cell(if4c);
  Put_Metric_Cell(if4c,nom,"8.3");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Lines of Code rejected by parser");
  Put_Label_Cell("REJ");
  Put_Metric_Cell(rej,"REJ");
  Put_Label_Cell("");
  fstr << "</TR>" << endl;

  fstr << "</TABLE>" << endl;
}

void CCCC_Html_Stream::OO_Design() {
  Put_Section_Heading("Object Oriented Design","oodesign",1);

  fstr << "<UL>" << endl;
  Metric_Description("WMC","Weighted methods per class",
		     "The sum of a weighting function over the functions of "
		     "the module.  Two different weighting functions are "
		     "applied: WMC1 uses the nominal weight of 1 for each "
		     "function, and hence measures the number of functions, "
		     "WMCv uses a weighting function which is 1 for functions "
		     "accessible to other modules, 0 for private functions.");
  Metric_Description("DIT","Depth of inheritance tree",
		     "The length of the longest path of inheritance ending at "
		     "the current module.  The deeper the inheritance tree "
		     "for a module, the harder it may be to predict its "
		     "behaviour.  On the other hand, increasing depth gives "
		     "the potential of greater reuse by the current module "
		     "of behaviour defined for ancestor classes.");
  Metric_Description("NOC","Number of children",
		     "The number of modules which inherit directly from the "
		     "current module.  Moderate values of this measure "
		     "indicate scope for reuse, however high values may "
		     "indicate an inappropriate abstraction in the design.");
  Metric_Description("CBO","Coupling between objects",
		     "The number of other modules which are coupled to the "
		     "current module either as a client or a supplier. "
		     "Excessive coupling indicates weakness of module "
		     "encapsulation and may inhibit reuse.");
  fstr << "</UL>" << endl << endl;

  fstr << "The label cell for each row in this table provides a link to "
       << "the module summary table in the detailed report for the "
       << "module in question" << endl;


  fstr << "<TABLE BORDER WIDTH=100%>" << endl
       << "<TR>" << endl;
  Put_Header_Cell("Module Name",50);
  Put_Header_Cell("WMC1",10);
  Put_Header_Cell("WMCv",10);
  Put_Header_Cell("DIT",10);
  Put_Header_Cell("NOC",10);
  Put_Header_Cell("CBO",10);

  fstr << "</TR>" << endl;

  CCCC_Module* mod_ptr=prjptr->module_table.first_item();
  int i=0;
  while(mod_ptr!=NULL)
    { 
      i++;
      if( mod_ptr->is_trivial() == FALSE)
	{
	  const char *metric_tags[5]={"WMC1","WMCv","DIT","NOC","CBO"};
	  fstr << "<TR>" << endl;

	  string href=mod_ptr->key()+".html#summary";

	  Put_Label_Cell(mod_ptr->name(nlSIMPLE).c_str(),0,"",href.c_str());
      
	  int j;
	  for(j=0; j<5; j++)
	    {
	      CCCC_Metric metric_value(
				       mod_ptr->get_count(metric_tags[j]),
				       metric_tags[j]
				       );
	      Put_Metric_Cell(metric_value);
	    }
	  fstr << "</TR>" << endl;

	}
      mod_ptr=prjptr->module_table.next_item();
    }

  fstr << "</TABLE>" << endl;
  
}

void CCCC_Html_Stream::Procedural_Summary() {
  Put_Section_Heading("Procedural Metrics Summary","procsum",1);

  fstr << "For descriptions of each of these metrics see the information "
       << "preceding the project summary table."
       << endl << endl;

  fstr << "The label cell for each row in this table provides a link to "
       << "the functions table in the detailed report for the "
       << "module in question" << endl;

  fstr << "<TABLE BORDER WIDTH=100%>" << endl
       << "<TR>" << endl;
  Put_Header_Cell("Module Name");
  Put_Header_Cell("LOC",8);
  Put_Header_Cell("MVG",8);
  Put_Header_Cell("COM",8);
  Put_Header_Cell("L_C",8);
  Put_Header_Cell("M_C",8);

  fstr << "</TR>" << endl;

  CCCC_Module* mod_ptr=prjptr->module_table.first_item();
  int i=0;
  while(mod_ptr!=NULL)
    {
      i++;
      if( mod_ptr->is_trivial() == FALSE)
	{
	  fstr << "<TR>" << endl;
	  string href=mod_ptr->key()+".html#procdet";

	  Put_Label_Cell(mod_ptr->name(nlSIMPLE).c_str(),0,"",href.c_str());
	  int loc=mod_ptr->get_count("LOC");
	  int mvg=mod_ptr->get_count("MVG");
	  int com=mod_ptr->get_count("COM");
	  CCCC_Metric mloc(loc,"LOCm");
	  CCCC_Metric mmvg(mvg,"MVGm");
	  CCCC_Metric ml_c(loc,com,"L_C");
	  CCCC_Metric mm_c(mvg,com,"M_C");

	  Put_Metric_Cell(mloc);
	  Put_Metric_Cell(mmvg);
	  Put_Metric_Cell(com);
	  Put_Metric_Cell(ml_c);
	  Put_Metric_Cell(mm_c);

	  fstr << "</TR>" << endl;

	}
      mod_ptr=prjptr->module_table.next_item();
    }

  fstr << "</TABLE>" << endl;
  
}

void CCCC_Html_Stream::Structural_Summary() 
{
  Put_Section_Heading("Structural Metrics Summary","structsum",1);

  fstr << "<UL>" << endl;
  Metric_Description("FI","Fan-in",
		     "The number of other modules which pass information "
		     "into the current module.");
  Metric_Description("FO","Fan-out",
		     "The number of other modules into which the current "
		     "module passes information");
  Metric_Description("IF4","Information Flow measure",
		     "A composite measure of structural complexity, "
		     "calculated as the square of the product of the fan-in "
		     "and fan-out of a single module.  Proposed by Henry and "
		     "Kafura.");
  fstr << "</UL>" << endl;

  fstr << "Note that the fan-in and fan-out are calculated by examining the "
       << "interface of each module.  As noted above, three variants of each "
       << "each of these measures are presented: a count restricted to the "
       << "part of the interface which is externally visible, a count which "
       << "only includes relationships which imply the client module needs "
       << "to be recompiled if the supplier's implementation changes, and an "
       << "inclusive count" << endl << endl;


  fstr << "The label cell for each row in this table provides a link to "
       << "the relationships table in the detailed report for the "
       << "module in question" << endl << endl;

  fstr << "<TABLE BORDER WIDTH=100%>" << endl;
  fstr  << "<TR>" << endl
	<< "<TH BGCOLOR=AQUA ROWSPAN=2>Module Name</TH>" << endl
	<< "<TH BGCOLOR=AQUA COLSPAN=3>Fan-out</TH>" << endl
	<< "<TH BGCOLOR=AQUA COLSPAN=3>Fan-in</TH>" << endl
	<< "<TH BGCOLOR=AQUA COLSPAN=3>IF4</TH>" << endl
	<< "</TR>" << endl;
  Put_Header_Cell("vis",7);
  Put_Header_Cell("con",7);
  Put_Header_Cell("inc",7);
  Put_Header_Cell("vis",7);
  Put_Header_Cell("con",7);
  Put_Header_Cell("incl",7);
  Put_Header_Cell("vis",7);
  Put_Header_Cell("con",7);
  Put_Header_Cell("inc",7);

  fstr << "</TR>" << endl;

  CCCC_Module* module_ptr=prjptr->module_table.first_item();
  while(module_ptr!=NULL)
    {
      if(module_ptr->is_trivial()==FALSE)
	{
	  fstr << "<TR>" << endl;

	  int fov=module_ptr->get_count("FOv");
	  int foc=module_ptr->get_count("FOc");
	  int fo=module_ptr->get_count("FO");

	  int fiv=module_ptr->get_count("FIv");
	  int fic=module_ptr->get_count("FIc");
	  int fi=module_ptr->get_count("FI");

	  int if4v=module_ptr->get_count("IF4v");
	  int if4c=module_ptr->get_count("IF4c");
	  int if4=module_ptr->get_count("IF4");

	  // the last two arguments here turn on links to enable jumping between
	  // the summary and detail cells for the same module
	  string href=module_ptr->key()+".html#structdet";      
	  Put_Label_Cell(module_ptr->name(nlSIMPLE).c_str(), 0, "",href.c_str());
	  Put_Metric_Cell(CCCC_Metric(fov,"FOv"));
	  Put_Metric_Cell(CCCC_Metric(foc,"FOc"));
	  Put_Metric_Cell(CCCC_Metric(fo,"FO"));
	  Put_Metric_Cell(CCCC_Metric(fiv,"FIv"));
	  Put_Metric_Cell(CCCC_Metric(fic,"FIc"));
	  Put_Metric_Cell(CCCC_Metric(fi,"FI"));
	  Put_Metric_Cell(CCCC_Metric(if4v,"IF4v"));
	  Put_Metric_Cell(CCCC_Metric(if4c,"IF4c"));
	  Put_Metric_Cell(CCCC_Metric(if4,"IF4"));

	  fstr << "</TR>" << endl;
	}
      module_ptr=prjptr->module_table.next_item();
    }  
  fstr << "</TABLE>" << endl;
  
}

void CCCC_Html_Stream::Put_Structural_Details_Cell(
						   CCCC_Module *mod, CCCC_Project *prj, int mask, UserelNameLevel nl)
{
  fstr << "<TD WIDTH=50%>" << endl;

#if 0
  std::cerr << "Relationships for " << mod->name(nlMODULE_NAME) 
	    << " (" << mod << ")" << std::endl;
#endif

  CCCC_Module::relationship_map_t::iterator iter;
  CCCC_Module::relationship_map_t *relationship_map=NULL;
  if(mask==rmeCLIENT)
    {
      relationship_map=&(mod->client_map);
    }
  else if(mask==rmeSUPPLIER)
    {
      relationship_map=&(mod->supplier_map);
    }

  if(relationship_map==NULL)
    {
      cerr << "unexpected relationship mask " << mask  << endl;
    }
  else
    {
      for(
	  iter=relationship_map->begin(); 
	  iter!=relationship_map->end();
	  iter++
	  )
	{
	  CCCC_UseRelationship *ur_ptr=(*iter).second;
	  fstr << ur_ptr->name(nl) << " ";
	  AugmentedBool vis=ur_ptr->is_visible();
	  AugmentedBool con=ur_ptr->is_concrete();

#if 0
	  std::cerr << ur_ptr->name(nlCLIENT) 
		    << " uses " 
		    << ur_ptr->name(nlSUPPLIER) 
		    << std::endl;
#endif

	  if( (vis != abFALSE) && (con != abFALSE) )
	    {
	      fstr << "[CV] ";
	    }
	  else if(vis != abFALSE)
	    {
	      fstr << "[V] ";
	    }
	  else if(con != abFALSE)
	    {
	      fstr << "[C] ";
	    }
	  fstr << "<BR>" << endl;  
	  Put_Extent_List(*ur_ptr,true);
	  fstr << "<BR>" << endl;
	}
    }
  // put a non-breaking space in to avoid the unpleasantness which
  // goes with completely empty cells
  fstr << "&nbsp;" << endl;

  fstr << "</TD>" << endl;

}

void CCCC_Html_Stream::Structural_Detail() 
{
  Put_Section_Heading("Structural Metrics Detail","structdet",1);
  fstr << "<TABLE BORDER WIDTH=100%>" << endl;
  fstr  << "<TR>" << endl;
  Put_Header_Cell("Module Name",20);
  Put_Header_Cell("Clients",40);
  Put_Header_Cell("Suppliers",40);
  fstr << "</TR>" << endl;

  CCCC_Module* module_ptr=prjptr->module_table.first_item();
  while(module_ptr!=NULL)
    {
      if(module_ptr->is_trivial()==FALSE)
	{
	  fstr << "<TR>" << endl;
	  Put_Label_Cell(module_ptr->name(nlSIMPLE).c_str(), 0, "structdet","structsum");
	  Structural_Detail(module_ptr);
	  fstr << "</TR>" << endl;
	}
      module_ptr=prjptr->module_table.next_item();
    }  
  fstr << "</TABLE>" << endl;
  
}

void CCCC_Html_Stream::Procedural_Detail() {
  Put_Section_Heading("Procedural Metrics Detail","procdet",1);

  fstr << "<TABLE BORDER WIDTH=100%>" << endl;

  CCCC_Module* mod_ptr=prjptr->module_table.first_item();
  while(mod_ptr!=NULL)
    {
      if( 
	 (mod_ptr->name(nlMODULE_TYPE)!="builtin") &&
	 (mod_ptr->name(nlMODULE_TYPE)!="enum") &&
	 (mod_ptr->name(nlMODULE_TYPE)!="union") 
	 )
	{
	  fstr  << "<TR>" << endl;
	  Put_Label_Cell(mod_ptr->name(nlSIMPLE).c_str(),50,
			 "procdet","procsum",mod_ptr);
	  Put_Header_Cell("LOC",10);
	  Put_Header_Cell("MVG",10);
	  Put_Header_Cell("COM",10);
	  Put_Header_Cell("L_C",10);
	  Put_Header_Cell("M_C",10);

	  fstr << "</TR>" << endl;
	  Procedural_Detail(mod_ptr);
	}
      mod_ptr=prjptr->module_table.next_item();
    }  
  fstr << "</TABLE>" << endl;
}

void CCCC_Html_Stream::Other_Extents() 
{
  Put_Section_Heading("Other Extents","other",1);
  fstr << "<TABLE BORDER WIDTH=100%>" << endl;
  fstr << "<TR>" << endl;
  Put_Header_Cell("Location",25);
  Put_Header_Cell("Text",45);
  Put_Header_Cell("LOC",10);
  Put_Header_Cell("COM",10);
  Put_Header_Cell("MVG",10);
  fstr << "</TR>" << endl;

  if(prjptr->rejected_extent_table.records() == 0)
    {
      fstr << "<TR><TD COLSPAN=5>"
	   << "&nbsp;"
	   << "</TD></TR>" << endl;
    }
  else
    {
      CCCC_Extent *extent_ptr=prjptr->rejected_extent_table.first_item();
      while(extent_ptr!=NULL)
	{
	  fstr << "<TR>";
	  Put_Extent_Cell(*extent_ptr,0);
	  Put_Label_Cell(extent_ptr->name(nlDESCRIPTION).c_str());
	  Put_Metric_Cell(extent_ptr->get_count("LOC"),"");
	  Put_Metric_Cell(extent_ptr->get_count("COM"),"");
	  Put_Metric_Cell(extent_ptr->get_count("MVG"),"");
	  fstr << "</TR>" << endl;
	  extent_ptr=prjptr->rejected_extent_table.next_item();
	}
    }
  fstr << "</TABLE>" << endl;
  
}

void CCCC_Html_Stream::Put_Section_TOC_Entry(
					     string section_name, string section_href,
					     string section_description)
{
  fstr << "<TR>" << endl
       << "<TH><H4><A HREF=\"#" << section_href << "\">"
       << section_name << "</A></H4></TH>" << endl 
       << "<TD>" << endl
       << section_description << endl
       << "</TR>" << endl;
}

void CCCC_Html_Stream::Put_Header_Cell(string label, int width)
{
  fstr << "<TH BGCOLOR=\"AQUA\"";
  if(width>0)
    {
      fstr << " WIDTH=" << width << "%" ;
    }
  fstr << ">" ;
  if(label.size()>0)
    {
      *this << label.c_str();
    }
  else
    {
      // put a non-breaking space in to avoid the strange
      // bevelling associated with empty cells
      fstr << "&nbsp;";

    }
  fstr << "</TH>";
}

void CCCC_Html_Stream::Put_Label_Cell(
				      string label, int width,
				      string ref_name, string ref_href, 
				      CCCC_Record *rec_ptr)
{
  fstr << "<TD";
  if(width>0)
    {
      fstr << " WIDTH=" << width <<"%";
    }
  fstr << ">" ;

  if(ref_name.size() > 0)
    {
      // we need to insert an HTML "<A NAME=...> tag for the current cell
      // this enables other locations to jump in
      fstr << "<A NAME=\"" << ref_name << "\"></A>" << endl;
    }

  if(ref_href.size() > 0)
    {
      // we need to insert an HTML <A HREF=...> tag for the current cell
      // this enables this cell to be a link to jump out
      fstr << "<A HREF=\"" << ref_href << "\">" << endl;
      // this anchor will need to be closed after the label has been displayed
    }

  if(label.size()>0)
    {
      *this << label.c_str() ;
    }
  else
    {
      // put a non-breaking space in to avoid the strange
      // bevelling associated with empty cells
      fstr << "&nbsp;";
    }

  if(ref_href.size() > 0)
    {
      // closing the anchor we opened above
      fstr << "</A>" << endl;
    }

  if(rec_ptr != 0)
    {
      fstr << "<BR>" << endl;
      Put_Extent_List(*rec_ptr,true);
    }

  fstr << "</TD>";
}
  

void CCCC_Html_Stream::Put_Metric_Cell(
				       int count, string tag, int width)
{
  CCCC_Metric m(count, tag.c_str());
  Put_Metric_Cell(m, width);
}

void CCCC_Html_Stream::Put_Metric_Cell(
				       int num, int denom, string tag, int width)
{
  CCCC_Metric m(num,denom, tag.c_str());
  Put_Metric_Cell(m, width);
}

void  CCCC_Html_Stream::Put_Metric_Cell(const CCCC_Metric& metric, int width)
{
  fstr << "<TD ALIGN=RIGHT";

  if(width>0)
    {
      fstr << " WIDTH=" << width << "%" ;
    }

  switch(metric.emphasis_level())
    {
    case elMEDIUM:
      fstr << " BGCOLOR=\"YELLOW\"";
      break;
    case elHIGH:
      fstr << " BGCOLOR=\"RED\"";
      break;
    default:
      // no background colour
      break;
    }
  fstr << ">";

  *this << metric;
  fstr << "</TD>";
}

void CCCC_Html_Stream::Put_Extent_URL(const CCCC_Extent& extent)
{
  string filename=extent.name(nlFILENAME);
  int linenumber=atoi(extent.name(nlLINENUMBER).c_str());

  Source_Anchor anchor(filename, linenumber);
  string key=anchor.key();
  source_anchor_map_t::value_type anchor_value(key, anchor);
  source_anchor_map.insert(anchor_value);

  anchor.Emit_HREF(fstr);
  fstr 
	// << extent.name(nlDESCRIPTION) 
	<< "<BR>" << endl;
}

void CCCC_Html_Stream::Put_Extent_Cell(const CCCC_Extent& extent, int width, bool withDescription) {
  fstr << "<TD";
  if(width>0)
    {
      fstr << " WIDTH=" << width << "%>";
    }
  else
    {
      fstr << ">";
    }
  if(withDescription)
	{
	  fstr << extent.name(nlDESCRIPTION) << " &nbsp;" << endl;
	}
  Put_Extent_URL(extent);
  fstr << "</TD>" << endl;
}

void CCCC_Html_Stream::Put_Extent_List(CCCC_Record& record, bool withDescription) 
{
  CCCC_Extent *ext_ptr=record.extent_table.first_item();
  while(ext_ptr!=NULL)
    {
	  if(withDescription)
		{
		  fstr << ext_ptr->name(nlDESCRIPTION) << " &nbsp;" << endl;
		}
      Put_Extent_URL(*ext_ptr);
      ext_ptr=record.extent_table.next_item();
    }
  fstr << "<BR>" << endl;
}

// the next two methods define the two basic output operations through which
// all of the higher level output operations are composed
CCCC_Html_Stream& operator <<(CCCC_Html_Stream& os, const string& stg) 
{
  // initialise a character pointer to the start of the string's buffer
  const char *cptr=stg.c_str();
  while(*cptr!='\000') {
    char c=*cptr;
	
    // the purpose of this is to filter out the characters which
    // must be escaped in HTML
    switch(c) {
    case '>': os.fstr << "&gt;" ; break;
    case '<': os.fstr << "&lt;" ; break;
    case '&': os.fstr << "&amp;"; break;
      // commas and parentheses do not need to be escaped, but
      // we want to allow line breaking just inside
      // parameter lists and after commas
      // we insert a non-breaking space to guarantee a small indent
      // on the new line, and one before the right parenthesis for
      // symmetry
    case ',': os.fstr << ", &nbsp;" ; break;
    case '(': os.fstr << "( &nbsp;" ; break;
    case ')': os.fstr << "&nbsp;)" ; break;
    default : os.fstr << c;
    }
    cptr++;
  }
  return os;
}

CCCC_Html_Stream& operator <<(CCCC_Html_Stream& os, const CCCC_Metric& mtc) 
{
  const char *emphasis_prefix[]={"","<EM>","<STRONG>"};
  const char *emphasis_suffix[]={"","</EM>","</STRONG>"};

  // by writing to the underlying ostream object, we avoid the escape
  // functionality
  os.fstr << emphasis_prefix[mtc.emphasis_level()]
	  << mtc.value_string() 
	  << emphasis_suffix[mtc.emphasis_level()];
  return os;
}

void CCCC_Html_Stream::Separate_Modules()
{
  // this function generates a separate HTML report for each non-trivial
  // module in the database

  CCCC_Module* mod_ptr=prjptr->module_table.first_item();
  while(mod_ptr!=NULL)
    {
      int trivial_module=mod_ptr->is_trivial();
      if(trivial_module==FALSE)
	{
	  string info="Detailed report on module " + mod_ptr->key();
	  string filename=outdir;
	  filename+="/";
	  filename+=mod_ptr->key()+".html";
	  CCCC_Html_Stream module_html_str(filename,info.c_str());

	  module_html_str.Put_Section_Heading(info.c_str(),"summary",1);

	  module_html_str.Module_Summary(mod_ptr);

	  module_html_str.Put_Section_Heading("Definitions and Declarations",
										  "modext",2);
	  module_html_str.fstr  << "<TABLE BORDER WIDTH=100%><TR>" << endl;
	  module_html_str.Put_Label_Cell("Description",50);
	  module_html_str.Put_Header_Cell("LOC",10);
	  module_html_str.Put_Header_Cell("MVG",10);
	  module_html_str.Put_Header_Cell("COM",10);
	  module_html_str.Put_Header_Cell("L_C",10);
	  module_html_str.Put_Header_Cell("M_C",10);
	  module_html_str.Module_Detail(mod_ptr);
	  module_html_str.fstr << "</TR></TABLE>" << endl;
   
	  module_html_str.Put_Section_Heading("Functions","proc",2);
	  module_html_str.fstr  << "<TABLE BORDER WIDTH=100%><TR>" << endl;
	  module_html_str.Put_Label_Cell("Function prototype",50);
	  module_html_str.Put_Header_Cell("LOC",10);
	  module_html_str.Put_Header_Cell("MVG",10);
	  module_html_str.Put_Header_Cell("COM",10);
	  module_html_str.Put_Header_Cell("L_C",10);
	  module_html_str.Put_Header_Cell("M_C",10);
	  module_html_str.Procedural_Detail(mod_ptr);
	  module_html_str.fstr << "</TR></TABLE>" << endl;

	  module_html_str.Put_Section_Heading("Relationships","structdet",2);
	  module_html_str.fstr 
	    << "<TABLE BORDER WIDTH=100%>" << endl
	    << "<TR><TH WIDTH=50%>Clients</TH><TH WIDTH=50%>Suppliers</TH></TR>" 
	    << endl
	    << "<TR>" << endl;
	  module_html_str.Structural_Detail(mod_ptr);
	  module_html_str.fstr << "</TR></TABLE>" << endl;
   

	}
      else
	{
#if 0
	  cerr << mod_ptr->module_type << " " << mod_ptr->key() 
	       << " is trivial" << endl;
#endif
	}
      mod_ptr=prjptr->module_table.next_item();
    }
}

void CCCC_Html_Stream::Module_Detail(CCCC_Module *module_ptr)
{
  // this function generates the contents of the table of definition
  // and declaration extents for a single module
  
  // the output needs to be enveloped in a pair of <TABLE></TABLE> tags
  // these have not been put within the function because it is designed
  // to be used in two contexts:
  // 1. within the Separate_Modules function, wrapped directly in the table
  //    tags
  // 2. within the Module_Detail function, where the table tags are 
  //    around the output of many calls to this function (not yet implemented)

  CCCC_Record::Extent_Table::iterator eIter = module_ptr->extent_table.begin();
  if(eIter==module_ptr->extent_table.end())
    {
      fstr << "<TR><TD COLSPAN=6>"
	   << "No module extents have been identified for this module"
	   << "</TD></TR>" << endl;
    }
  else
    {
      while(eIter!=module_ptr->extent_table.end())
		{
	  CCCC_Extent *ext_ptr=(*eIter).second;
	  fstr << "<TR>" << endl;
	  Put_Extent_Cell(*ext_ptr,0,true);
	  int loc=ext_ptr->get_count("LOC");
	  int mvg=ext_ptr->get_count("MVG");
	  int com=ext_ptr->get_count("COM");
	  CCCC_Metric mloc(loc,"LOCf");
	  CCCC_Metric mmvg(mvg,"MVGf");
	  CCCC_Metric ml_c(loc,com,"L_C");
	  CCCC_Metric mm_c(mvg,com,"M_C");

	  Put_Metric_Cell(mloc);
	  Put_Metric_Cell(mmvg);
	  Put_Metric_Cell(com);
	  Put_Metric_Cell(ml_c);
	  Put_Metric_Cell(mm_c);
	  fstr << "</TR>" << endl;
   
	  eIter++;
		}
	}

  fstr << "<TR><TD HEIGHT=12 COLSPAN=6></TD></TR>" << endl;
}

void CCCC_Html_Stream::Procedural_Detail(CCCC_Module *module_ptr)
{
  // this function generates the contents of the procedural detail table
  // relating to a single module
  
  // the output needs to be enveloped in a pair of <TABLE></TABLE> tags
  // these have not been put within the function because it is designed
  // to be used in two contexts:
  // 1. within the Separate_Modules function, wrapped directly in the table
  //    tags
  // 2. within the Procedural_Detail function, where the table tags are 
  //    around the output of many calls to this function

  CCCC_Module::member_map_t::iterator iter = module_ptr->member_map.begin();

  if(iter==module_ptr->member_map.end())
    {
      fstr << "<TR><TD COLSPAN=6>"
	   << "No member functions have been identified for this module"
	   << "</TD></TR>" << endl;
    }
  else
    {
      while(iter!=module_ptr->member_map.end())
	{ 
	  CCCC_Member *mem_ptr=(*iter).second;
	  fstr << "<TR>" << endl;
	  Put_Label_Cell(mem_ptr->name(nlLOCAL).c_str(),0,"","",mem_ptr);
	  int loc=mem_ptr->get_count("LOC");
	  int mvg=mem_ptr->get_count("MVG");
	  int com=mem_ptr->get_count("COM");
	  CCCC_Metric mloc(loc,"LOCf");
	  CCCC_Metric mmvg(mvg,"MVGf");
	  CCCC_Metric ml_c(loc,com,"L_C");
	  CCCC_Metric mm_c(mvg,com,"M_C");


	  Put_Metric_Cell(mloc);
	  Put_Metric_Cell(mmvg);
	  Put_Metric_Cell(com);
	  Put_Metric_Cell(ml_c);
	  Put_Metric_Cell(mm_c);
	  fstr << "</TR>" << endl;
   
	  iter++;
	}
    }
  fstr << "<TR><TD HEIGHT=12 COLSPAN=6></TD></TR>" << endl;
}

void CCCC_Html_Stream::Metric_Description(
					  string abbreviation, 
					  string name, 
					  string description)
{
  // this is intended to be called in the context of an unnumbered list
  fstr << "<LI>" << abbreviation << " = " << name << "<BR>" << endl
       << description << endl;
}

void CCCC_Html_Stream::Structural_Detail(CCCC_Module *module_ptr) 
{
  Put_Structural_Details_Cell(module_ptr, prjptr, rmeCLIENT, nlCLIENT);
  Put_Structural_Details_Cell(module_ptr, prjptr, rmeSUPPLIER, nlSUPPLIER);
}

void CCCC_Html_Stream::Module_Summary(CCCC_Module *module_ptr) 
{
  // calculate the counts on which all displayed data will be based
  // int nof=module_ptr->member_table.records(); // Number of functions
  int loc=module_ptr->get_count("LOC");  // lines of code
  int mvg=module_ptr->get_count("MVG");  // McCabes cyclomatic complexity
  int com=module_ptr->get_count("COM");  // lines of comment

  // the variants of IF4 measure information flow and couplings
  int if4=module_ptr->get_count("IF4");   // (all couplings)
  int if4v=module_ptr->get_count("IF4v"); // (visible only)
  int if4c=module_ptr->get_count("IF4c"); // (concrete only)

  int wmc1=module_ptr->get_count("WMC1"); // Weighted methods/class (unity)
  int wmcv=module_ptr->get_count("WMCv"); // Weighted methods/class (visible)
  int dit=module_ptr->get_count("DIT");   // depth of inheritance tree
  int noc=module_ptr->get_count("NOC");   // number of children
  int cbo=module_ptr->get_count("CBO");   // coupling between objects

  // To calculate the per function metrics in the detailed report the
  // number of functions is set to the weighted methods 
  int nof=wmc1;

  fstr << "<TABLE BORDER WIDTH=100%>" << endl
       << "<TR>" << endl;
  Put_Header_Cell("Metric",70);
  Put_Header_Cell("Tag",10);
  Put_Header_Cell("Overall",10);
  Put_Header_Cell("Per Function",10);
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Lines of Code");
  Put_Label_Cell("LOC");
  Put_Metric_Cell(loc,"LOCm");
  Put_Metric_Cell(loc,nof,"LOCg");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("McCabe's Cyclomatic Number");
  Put_Label_Cell("MVG");
  Put_Metric_Cell(mvg,"MVGm");
  Put_Metric_Cell(mvg,nof,"MVGf");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Lines of Comment");
  Put_Label_Cell("COM");
  Put_Metric_Cell(com,"COMm");
  Put_Metric_Cell(com,nof,"8.3");
  fstr << "</TR>" << endl;
  
  fstr << "<TR>" << endl;
  Put_Label_Cell("LOC/COM");
  Put_Label_Cell("L_C");
  Put_Metric_Cell(loc,com,"L_C");
  Put_Label_Cell("");
  fstr << "</TR>" << endl;
  
  fstr << "<TR>" << endl;
  Put_Label_Cell("MVG/COM");
  Put_Label_Cell("M_C");
  Put_Metric_Cell(mvg,com,"M_C");
  Put_Label_Cell("");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Weighted Methods per Class (weighting = unity)");
  Put_Label_Cell("WMC1");
  Put_Metric_Cell(wmc1);
  Put_Label_Cell(""); // wmc1 should be identical to nof
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Weighted Methods per Class (weighting = visible)");
  Put_Label_Cell("WMCv");
  Put_Metric_Cell(wmcv);
  Put_Label_Cell("");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Depth of Inheritance Tree");
  Put_Label_Cell("DIT");
  Put_Metric_Cell(dit);
  Put_Label_Cell("");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Number of Children");
  Put_Label_Cell("NOC");
  Put_Metric_Cell(noc);
  Put_Label_Cell("");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Coupling between objects");
  Put_Label_Cell("CBO");
  Put_Metric_Cell(cbo);
  Put_Label_Cell("");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Information Flow measure (inclusive)");
  Put_Label_Cell("IF4");
  Put_Metric_Cell(if4,1,"IF4");
  Put_Metric_Cell(if4,nof,"8.3");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Information Flow measure (visible)");
  Put_Label_Cell("IF4v");
  Put_Metric_Cell(if4v,1,"IF4v");
  Put_Metric_Cell(if4v,nof,"8.3");
  fstr << "</TR>" << endl;

  fstr << "<TR>" << endl;
  Put_Label_Cell("Information Flow measure (concrete)");
  Put_Label_Cell("IF4c");
  Put_Metric_Cell(if4c,1,"IF4c");
  Put_Metric_Cell(if4c,nof,"8.3");
  fstr << "</TR>" << endl;

  fstr << "</TABLE>" << endl;
}


CCCC_Html_Stream::CCCC_Html_Stream(const string& fname, const string& info)
{
  // cerr << "Attempting to open file in directory " << outdir.c_str() << endl;
  fstr.open(fname.c_str());
  if(fstr.good() != TRUE)
    {
      cerr << "failed to open " << fname.c_str() 
	   << " for output in directory " << outdir.c_str() << endl;
      exit(1);
    }
  else
    {
      // cerr << "File: " << fname << " Info: " << info << endl;
    }
    
  fstr << "<HTML><HEAD><TITLE>" << endl
       << info << endl
       << "</TITLE>" << endl
       << "</HEAD>" << endl
       << "<BODY>" << endl;
}

int setup_anchor_data()
{
  int i=0;
  Source_Anchor a1("cccc_use.h",12);
  Source_Anchor a2("cccc_htm.h",15);
  i++;
  string key1=a1.key(), key2=a2.key();
  i++;
  source_anchor_map_t::value_type v1(key1,a1);
  source_anchor_map_t::value_type v2(key2,a2);
  i++;
  source_anchor_map.insert(v1);
  source_anchor_map.insert(v2);
  return i;
}


void CCCC_Html_Stream::Source_Listing()
{
  // The variable stream src_str used to be an instance 
  // of fstream which gets reopened many times.
  // this worked under Linux but broke under Win32, so
  // this variable is now a pointer which is repeatedly 
  // deleted and new'ed

  string current_filename;
  int current_line=0;
  int next_anchor_required=0;
  ifstream *src_str=NULL;
  const char *style_open="<TT>", *style_close="</TT>";

  string filename=outdir;
  filename+="/cccc_src.html";
  CCCC_Html_Stream source_html_str(filename.c_str(),"source file");

  source_html_str.fstr << style_open << endl;

  source_anchor_map_t::iterator iter=source_anchor_map.begin();
  while(iter!=source_anchor_map.end())
    {
      char linebuf[1024];
      Source_Anchor& nextAnchor=(*iter).second;
      if(current_filename!=nextAnchor.get_file())
	{
	  current_filename=nextAnchor.get_file();
	  current_line=0;
	  delete src_str;
	  src_str=new ifstream(current_filename.c_str(),std::ios::in);
	  src_str->getline(linebuf,1023);
	  source_html_str.fstr << style_close << endl;
	  source_html_str.Put_Section_Heading(current_filename.c_str(),"",1);
	  source_html_str.fstr << style_open << endl;
	}

      while(src_str->good())
	{
	  current_line++;
	  if(
	     (iter!=source_anchor_map.end()) &&
	     (current_filename==(*iter).second.get_file()) &&
	     (current_line==(*iter).second.get_line())
	     )
	    {
	      (*iter).second.Emit_NAME(source_html_str.fstr);
	      iter++;
	    }
	  else
	    {
	      (*iter).second.Emit_SPACE(source_html_str.fstr);
	    }
 	  source_html_str << linebuf;
 	  source_html_str.fstr << "<BR>" << endl;
	  src_str->getline(linebuf,1023);
 	}

      // if there are any remaining anchors for this file the sorting 
      // by line number must be wrong
      // complain and ignore
      while(
	    (iter!=source_anchor_map.end()) &&
	    (current_filename==(*iter).second.get_file())
	    )
	{
	  (*iter).second.Emit_NAME(source_html_str.fstr);
	  iter++;
	  source_html_str.fstr << "<BR>" << endl;
	}


    }

  // delete the last input stream created
  delete src_str;

  source_html_str.fstr << style_close << " </BODY></HTML>" << endl;

}

static string pad_string(int target_width, string the_string, string padding)
{
  int spaces_required=target_width-the_string.size();
  string pad_string;
  while(spaces_required>0)
    {
      pad_string+=padding;
      spaces_required--;
    }
  return pad_string+the_string;
}

  
string Source_Anchor::key() const
{
  string retval;
  char linebuf[16];
  sprintf(linebuf,"%d",line_);
  retval=file_+":"+pad_string(10,linebuf," ");
  return retval;
}

  
void Source_Anchor::Emit_HREF(ofstream& fstr)
{
  string anchor_key=key();

  fstr << "<CODE><A HREF=\"cccc_src.html#" << anchor_key.c_str() << "\">"
       << file_.c_str() << ":" << line_ 
       << "</A></CODE>";
}

void Source_Anchor::Emit_NAME(ofstream& fstr)
{
  string anchor_key=key();
  char ln_buf[32];
  sprintf(ln_buf,"%d",line_);
  string ln_string=pad_string(8,ln_buf,"&nbsp;");
  string space_string=pad_string(2,"","&nbsp;");
  fstr << "<A NAME=\"" << anchor_key.c_str() << "\">" 
       << ln_string.c_str() << space_string.c_str() << "</A>";
}

void Source_Anchor::Emit_SPACE(ofstream& fstr)
{
  string space_string=pad_string(10,"","&nbsp;");
  fstr << space_string.c_str();
}



#ifdef UNIT_TEST
int main()
{
  CCCC_Project *prj_ptr=test_project_ptr();
  CCCC_Html_Stream os(*prj_ptr,"cccc.htm",".");
  return 0;
}
#endif










