Logo Search packages:      
Sourcecode: cccc version File versions

cccc_htm.cc

// 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 CCCC development website at "
      << "<A HREF=http://cccc.sourceforge.net>http://cccc.sourceforge.net</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)
      {
        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) 
{
  char *emphasis_prefix[]={"","<EM>","<STRONG>"};
  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 nof=0;
  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

  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











Generated by  Doxygen 1.6.0   Back to index