//@copyright_begin // ================================================================ // Copyright Notice // Copyright (C) 1998-2004 by Joe Linoff // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL JOE LINOFF BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // Comments and suggestions are always welcome. // Please report bugs to http://ccdoc.sourceforge.net/ccdoc // ================================================================ //@copyright_end #if defined(_MSC_VER) #pragma warning ( disable : 4786 4251 ) #endif #include #include #include #include #include "exceptions.h" #include "phase3_html.h" // ================================================================ // This variable allows the header version // to be queried at runtime. // ================================================================ namespace { char ccdoc_rcsid[] = "$Id: phase3_html.cc,v 1.18 2004/09/30 16:09:27 jlinoff Exp $"; } // ================================================================ // Forward declarations for sort compare functions. // ================================================================ namespace { bool compare_stmts_full_paths(const ccdoc::statement::base* a, const ccdoc::statement::base* b); bool compare_stmts(const ccdoc::statement::base* a, const ccdoc::statement::base* b); } // ================================================================ // Html // ================================================================ ccdoc::phase3::html::html(switches& sw,database& db) : m_debug(false), m_ok(false), m_db(db), m_sw(sw), m_multiple_ref_index(0) { } // ================================================================ // Html // ================================================================ ccdoc::phase3::html::~html() { } // ================================================================ // Run // ================================================================ bool ccdoc::phase3::html::run() { // Load the customized header and trailer stuff. load(m_sw.header(),m_header); load(m_sw.trailer(),m_trailer); load(m_sw.meta(),m_meta); m_db.clear_path_map(); m_db.load_path_map(); // Issue 0039 // Issue 0044 // Allow the user to control the hierarchical indent level. { m_rptcsi = ""; for(int i=m_sw.rptcsi();i>0;--i) { m_rptcsi += " "; } } write_ccdoc_pkgs_html(); write_ccdoc_namespaces_html(); write_ccdoc_classes_html(); write_ccdoc_class_summary_html(); write_ccdoc_functions_html(); write_ccdoc_variables_html(); write_ccdoc_enums_html(); write_ccdoc_typedefs_html(); write_ccdoc_macros_html(); m_ok = true; return m_ok; } // ================================================================ // Write the package files. // Each package file contains the following: // - The ccdoc description section. // - The table of contents: child packages and namespaces. // ================================================================ void ccdoc::phase3::html::write_ccdoc_pkgs_html() { statement::base::stmts_t stmts; m_db.load(stmts,statement::base::STMT_PACKAGE); if( m_sw.verbose() ) { s_log << "phase3: generating HTML for " << stmts.size() << " packages ...\n"; } statement::base::stmts_itr_t itr = stmts.begin(); for(;itr!=stmts.end();++itr) { string fn; statement::base* stmt = *itr; make_unique_file_name(fn,stmt); ofstream os(fn.c_str()); if(!os) { throw ccdoc::exceptions::unwriteable_output_file(__FILE__, __LINE__, fn.c_str()); } if( m_sw.verbose() ) { string hier_id; stmt->get_hier_id(hier_id); s_log << "phase3: generating HTML for package " << hier_id << " ...\n"; } write_common_header_info(os,fn,stmt); write_section_header(os,stmt,true); write_indent(os); write_ccdoc_info(os,stmt,m_sw.rptdpa(),m_sw.rptdpv()); write_unindent(os); write_contents(os,stmt); write_common_trailer_info(os); } } // ================================================================ // write the namespace files. // Each namespace file contains the following: // - The ccdoc description section. // - The table of contents: child namespaces. // ================================================================ void ccdoc::phase3::html::write_ccdoc_namespaces_html() { statement::base::stmts_t stmts; m_db.load(stmts,statement::base::STMT_NAMESPACE_BEGIN); if( m_sw.verbose() ) { s_log << "phase3: generating HTML for " << stmts.size() << " namespaces ...\n"; } statement::base::stmts_itr_t itr = stmts.begin(); for(;itr!=stmts.end();++itr) { string fn; statement::base* stmt = *itr; // Skip the internal ids of the form: // ++: // that are used store the comments // until they can be extracted here // by chaining using the get_next() // method that is set in phase3. // They are artifacts. if( ignore_contents_stmt(stmt) ) { if( m_sw.verbose() ) { string hier_id; stmt->get_hier_id(hier_id); s_log << "phase3: skipping HTML for internal namespace " << hier_id << " ...\n"; } continue; } make_unique_file_name(fn,stmt); ofstream os(fn.c_str()); if(!os) { throw ccdoc::exceptions::unwriteable_output_file(__FILE__, __LINE__, fn.c_str()); } if( m_sw.verbose() ) { string hier_id; stmt->get_hier_id(hier_id); s_log << "phase3: generating HTML for namespace " << hier_id << " ...\n"; } write_common_header_info(os,fn,stmt); write_section_header(os,stmt,true); write_indent(os); write_ccdoc_info(os,stmt,true,true); // Issue 0133 // Special case handling for comment chains. // These occur in namespaces where multiple sets // of comments are chained together. These are // handled by walking through the chains and // output the comments in an indented form with // a separator for each one. if( stmt->get_next() ) { statement::base* chain = stmt->get_next(); for(;chain;chain = chain->get_next()) { if( !m_sw.rptcfuns() && !chain->get_comment() ) { // Don't report namespaces with empty comments. continue; } os << "\n"; os << "\n"; os << "\n"; os << "\n"; os << "
\n"; os << "" << chain->get_file() << ":" << chain->get_lineno() << "\n"; os << "
\n"; write_ccdoc_info(os,chain,true,true); os << "
\n"; os << "
\n"; } } write_unindent(os); write_contents(os,stmt); write_common_trailer_info(os); } } // ================================================================ // write the class files. // ================================================================ void ccdoc::phase3::html::write_ccdoc_classes_html() { statement::base::stmts_t stmts; m_db.load(stmts,statement::base::STMT_CLASS_BEGIN); m_db.load(stmts,statement::base::STMT_STRUCT_BEGIN); if( m_sw.rptun() ) { m_db.load(stmts,statement::base::STMT_UNION_BEGIN); } statement::base::stmts_itr_t itr = stmts.begin(); if( m_sw.verbose() ) { s_log << "phase3: generating HTML for " << stmts.size() << " classes ...\n"; } for(;itr!=stmts.end();++itr) { string fn; statement::base* stmt = *itr; make_unique_file_name(fn,stmt); ofstream os(fn.c_str()); if(!os) { throw ccdoc::exceptions::unwriteable_output_file(__FILE__, __LINE__, fn.c_str()); } if( m_sw.verbose() ) { string hier_id; stmt->get_hier_id(hier_id); s_log << "phase3: generating HTML for class " << hier_id << " ...\n"; } write_common_header_info(os,fn,stmt); write_section_header(os,stmt,true); write_extends_clause(os,stmt); write_indent(os); write_ccdoc_info(os,stmt,true,true); write_friends_info(os,stmt); write_unindent(os); write_contents(os,stmt,m_sw.rptsci()); write_class_details(os,stmt,m_sw.rptsci()); write_common_trailer_info(os); } } // ================================================================ // Generating the class summary. // ================================================================ void ccdoc::phase3::html::write_ccdoc_class_summary_html() { statement::base::stmts_t stmts; m_db.load(stmts,statement::base::STMT_CLASS_BEGIN); m_db.load(stmts,statement::base::STMT_STRUCT_BEGIN); if( m_sw.verbose() ) { s_log << "phase3: generating HTML for class summary\n"; } // Open the HTML file. string fn; fn = m_sw.html() + "ccdoc.class_summary.html"; ofstream os(fn.c_str()); if(!os) { throw ccdoc::exceptions::unwriteable_output_file(__FILE__, __LINE__, fn.c_str()); } write_common_header_info(os,fn,"Class Summary"); os << "
"; if( m_sw.rooturl().size() ) { os << "Home"; os << " :: "; } write_link(os,m_db.root()); os << ""; string url; string link_name = m_sw.html() + "ccdoc.class_summary.html"; make_file_url(url,link_name); os << "classes
\n"; write_section_header(os,"Class Summary",false); write_summary_tree(os,stmts,m_sw.rptcsd(),0,m_rptcsi.c_str()); write_common_trailer_info(os); } // ================================================================ // Generating the class summary. // ================================================================ void ccdoc::phase3::html::write_summary_tree(ostream& os, statement::base::stmts_t& stmts, bool rpthpc, statement::base* start, const char* in_indent, bool sort_flag) { // ================================================ // Order the entries. // ================================================ if( sort_flag ) { stable_sort(stmts.begin(),stmts.end(),compare_stmts_full_paths); } // ================================================ // Initialize the tags. // These are used later to identify when the parents // have been visited. // ================================================ statement::base::stmts_itr_t itr = stmts.begin(); for(;itr!=stmts.end();++itr) { statement::base* stmt = *itr; stmt->set_tag(0); statement::base::stmts_t parents; stmt->get_parents(parents); statement::base::stmts_itr_t pitr = parents.begin(); for(;pitr!=parents.end();++pitr) { statement::base* parent = *pitr; parent->set_tag(0); } } // ================================================ // Output the information. // ================================================ if( stmts.size() == 0 ) return; if( rpthpc ) { // Issue 0026 os << "\n" << "" << "" << "" << ""; if( m_sw.rptsrc() ) { os << ""; } os << "" << "\n"; } else { os << "
EntityTypeScopeSourceShort Description
\n"; } itr = stmts.begin(); for(;itr!=stmts.end();++itr) { statement::base* stmt = *itr; if( stmt->get_tag() == 0 ) { string indent = ""; statement::base::stmts_t parents; stmt->get_parents(parents); statement::base::stmts_itr_t pitr = parents.begin(); if( start ) { // The start parent was specified, // skip ahead until a match is found. for(;pitr!=parents.end();++pitr) { statement::base* parent = *pitr; if( parent == start ) { ++pitr; break; } } } for(;pitr!=parents.end();++pitr) { statement::base* parent = *pitr; if( parent->get_tag() == 0 ) { parent->set_tag(1); pitr = parents.begin(); write_summary_tree_entry(os,parent,indent.c_str(),rpthpc); indent = ""; } indent += in_indent; } stmt->set_tag(1); write_summary_tree_entry(os,stmt,indent.c_str(),rpthpc); } } os << "
\n"; } // ================================================================ // Generating the class summary. // ================================================================ void ccdoc::phase3::html::write_summary_tree_entry(ostream& os, statement::base* stmt, const char* indent, bool rpthpc) { os << ""; // Id os << ""; if(indent) os << indent; if( stmt ) { string id = stmt->get_id(); unsigned max_entity_size = m_sw.rptmlcei() + m_sw.rptmlcifi(); if( max_entity_size && id.size() > max_entity_size ) { // Issue 0159: // Truncate the id to the shorter number of characters // and add the '..'. string short_id = id.substr(0,max_entity_size); short_id += ".."; write_link(os,stmt,short_id.c_str()); } else { write_link(os,stmt,id.c_str()); } } os << ""; // Type if( rpthpc ) { // Issue 0026 // Type string type = stmt->get_type_name2(); if( stmt->get_type() == statement::base::STMT_PACKAGE && stmt->get_comment() ) { statement::comment doc(stmt->get_comment()); string tid = doc.get_pkgdoc_tid(); if( tid.size() ) type = tid; } os << "" << type << " "; // Access os << ""; os << stmt->get_access_name(); os << ""; // Source info if( m_sw.rptsrc() ) { os << ""; os << stmt->get_file() << ":" << stmt->get_lineno(); os << ""; } // Short description os << ""; write_short_desc(os,stmt); os << ""; } os << "\n"; } // ================================================================ // Generating the class summary. // ================================================================ void ccdoc::phase3::html::write_short_desc(ostream& os, statement::base* stmt) { if( stmt->get_comment() ) { statement::comment doc(stmt->get_comment()); if( doc.get_short_desc().size() == 0 ) { // Issue 0030 if( stmt->get_type() != statement::base::STMT_PACKAGE || m_sw.rptdpd() == false ) { os << m_sw.rptdefsd(); } } else { statement::base* scope = stmt->get_parent(); write_ccdoc_desc_info(os,doc.get_short_desc(),scope,stmt); } } else { if( stmt->get_lineno() == 0 ) { // Lineno zero flags the fact that it // was automatically generated by the // compiler. os << m_sw.rptdefasd(); } else { // Issue 0030 if( stmt->get_type() != statement::base::STMT_PACKAGE || m_sw.rptdpd() == false ) { os << m_sw.rptdefsd(); } } } } // ================================================================ // write the function files. // ================================================================ void ccdoc::phase3::html::write_ccdoc_functions_html() { statement::base::stmts_t stmts; m_db.load_top(stmts,statement::base::STMT_FUNCTION); m_db.load_top(stmts,statement::base::STMT_FUNCTION_OPERATOR); if( m_sw.verbose() ) { s_log << "phase3: generating HTML for " << stmts.size() << " global functions ...\n"; } statement::base::stmts_itr_t itr = stmts.begin(); for(;itr!=stmts.end();++itr) { string fn; statement::base* stmt = *itr; make_unique_file_name(fn,stmt); ofstream os(fn.c_str()); if(!os) { throw ccdoc::exceptions::unwriteable_output_file(__FILE__, __LINE__, fn.c_str()); } if( m_sw.verbose() ) { string hier_id; stmt->get_hier_id(hier_id); s_log << "phase3: generating HTML for global function " << hier_id << " ...\n"; } write_common_header_info(os,fn,stmt); write_section_header(os,stmt,false); write_indent(os); write_ccdoc_info(os,stmt,true,true); write_code_section(os,stmt); write_unindent(os); write_common_trailer_info(os); } } // ================================================================ // write the variable files. // ================================================================ void ccdoc::phase3::html::write_ccdoc_variables_html() { statement::base::stmts_t stmts; m_db.load_top(stmts,statement::base::STMT_ATTRIBUTE); m_db.load_top(stmts,statement::base::STMT_ATTRIBUTE_FUNCTION); m_db.load_top(stmts,statement::base::STMT_VARIABLE); m_db.load_top(stmts,statement::base::STMT_VARIABLE_FUNCTION); statement::base::stmts_itr_t itr = stmts.begin(); if( m_sw.verbose() ) { s_log << "phase3: generating HTML for " << stmts.size() << " global variables ...\n"; } for(;itr!=stmts.end();++itr) { string fn; statement::base* stmt = *itr; make_unique_file_name(fn,stmt); ofstream os(fn.c_str()); if(!os) { throw ccdoc::exceptions::unwriteable_output_file(__FILE__, __LINE__, fn.c_str()); } if( m_sw.verbose() ) { string hier_id; stmt->get_hier_id(hier_id); s_log << "phase3: generating HTML for global variable " << hier_id << " ...\n"; } write_common_header_info(os,fn,stmt); write_section_header(os,stmt,false); write_indent(os); write_ccdoc_info(os,stmt,true,true); write_code_section(os,stmt); write_unindent(os); write_common_trailer_info(os); } } // ================================================================ // write the enum files. // ================================================================ void ccdoc::phase3::html::write_ccdoc_enums_html() { statement::base::stmts_t stmts; m_db.load_top(stmts,statement::base::STMT_ENUM); if( m_sw.verbose() ) { s_log << "phase3: generating HTML for " << stmts.size() << " global enums ...\n"; } statement::base::stmts_itr_t itr = stmts.begin(); for(;itr!=stmts.end();++itr) { string fn; statement::base* stmt = *itr; make_unique_file_name(fn,stmt); ofstream os(fn.c_str()); if(!os) { throw ccdoc::exceptions::unwriteable_output_file(__FILE__, __LINE__, fn.c_str()); } if( m_sw.verbose() ) { string hier_id; stmt->get_hier_id(hier_id); s_log << "phase3: generating HTML for global enum " << hier_id << " ...\n"; } write_common_header_info(os,fn,stmt); write_section_header(os,stmt,false); write_indent(os); write_ccdoc_info(os,stmt,true,true); write_code_section(os,stmt); write_unindent(os); write_common_trailer_info(os); } } // ================================================================ // write the typedef files. // ================================================================ void ccdoc::phase3::html::write_ccdoc_typedefs_html() { if( m_sw.rpttyp() == false ) return; statement::base::stmts_t stmts; m_db.load_top(stmts,statement::base::STMT_TYPEDEF_FUNCTION); m_db.load_top(stmts,statement::base::STMT_TYPEDEF_VARIABLE); if( m_sw.verbose() ) { s_log << "phase3: generating HTML for " << stmts.size() << " global typedefs ...\n"; } statement::base::stmts_itr_t itr = stmts.begin(); for(;itr!=stmts.end();++itr) { string fn; statement::base* stmt = *itr; make_unique_file_name(fn,stmt); ofstream os(fn.c_str()); if(!os) { throw ccdoc::exceptions::unwriteable_output_file(__FILE__, __LINE__, fn.c_str()); } if( m_sw.verbose() ) { string hier_id; stmt->get_hier_id(hier_id); s_log << "phase3: generating HTML for global typedef " << hier_id << " ...\n"; } write_common_header_info(os,fn,stmt); write_section_header(os,stmt,false); write_indent(os); write_ccdoc_info(os,stmt,true,true); write_code_section(os,stmt); write_unindent(os); write_common_trailer_info(os); } } // ================================================================ // write the macro files. // ================================================================ void ccdoc::phase3::html::write_ccdoc_macros_html() { if( !m_sw.rptmac() ) return; statement::base::stmts_t stmts; m_db.load_top(stmts,statement::base::STMT_MACRODEF_0_0); m_db.load_top(stmts,statement::base::STMT_MACRODEF_0_1); m_db.load_top(stmts,statement::base::STMT_MACRODEF_0_N); m_db.load_top(stmts,statement::base::STMT_MACRODEF_N_N); if( m_sw.verbose() ) { s_log << "phase3: generating HTML for " << stmts.size() << " macro definitions ...\n"; } statement::base::stmts_itr_t itr = stmts.begin(); for(;itr!=stmts.end();++itr) { string fn; statement::base* stmt = *itr; make_unique_file_name(fn,stmt); ofstream os(fn.c_str()); if(!os) { throw ccdoc::exceptions::unwriteable_output_file(__FILE__, __LINE__, fn.c_str()); } if( m_sw.verbose() ) { string hier_id; stmt->get_hier_id(hier_id); s_log << "phase3: generating HTML for macro " << hier_id << " ...\n"; } write_common_header_info(os,fn,stmt); write_section_header(os,stmt,false); write_indent(os); write_ccdoc_info(os,stmt,true,true); write_code_section(os,stmt); write_unindent(os); write_common_trailer_info(os); } } // ================================================================ // Write the class details. // ================================================================ void ccdoc::phase3::html::write_class_details(ostream& os, statement::base* stmt, bool sort_flag) { if( stmt ) { statement::base::stmts_t contents; make_contents(stmt,contents,sort_flag); string prev_tag; string next_tag; statement::base::stmts_itr_t next_itr; statement::base::stmts_itr_t itr = contents.begin(); for(;itr!=contents.end();++itr) { statement::base* child = *itr; string tag; make_tag_id(child,tag); os << "\n"; // Get the next tag. next_itr = itr; ++next_itr; if( next_itr != contents.end() ) { make_tag_id(*next_itr,next_tag); } write_section_header(os, child, true, prev_tag.c_str(), next_tag.c_str()); write_indent(os); if( child->get_parent() == stmt ) { write_ccdoc_info(os,child,false,false,false); } else { write_ccdoc_info(os,child,false,false,true); } write_code_section(os,child); write_unindent(os); prev_tag = tag; } } } // ================================================================ // write contents // ================================================================ void ccdoc::phase3::html::write_contents(ostream& os, statement::base* stmt, bool sort_flag) { bool inherited_from_col = false; if( m_sw.rptim() ) { if( stmt->get_type() == statement::base::STMT_CLASS_BEGIN || stmt->get_type() == statement::base::STMT_STRUCT_BEGIN ) { // Don't create the inherited from column unless this // class is derived. int depth = 0; const statement::base::cstrs_t& tokens = stmt->get_tokens(); statement::base::cstrs_citr_t itr = tokens.begin(); for(;itr!=tokens.end();++itr) { string token = *itr; if( token == "<" ) { depth++; } else if( token == ">" ) { depth--; } else if( depth == 0 && token == ":" ) { inherited_from_col = true; break; } } } } if( m_sw.rpthpc() && stmt->get_type() == statement::base::STMT_PACKAGE ) { os << "\n"; write_section_header(os,"Contents",false); write_indent(os); statement::base::stmts_t unfiltered_contents; statement::base::stmts_t filtered_contents; stmt->get_all_children(unfiltered_contents); statement::base::stmts_itr_t itr = unfiltered_contents.begin(); for(;itr!=unfiltered_contents.end();++itr) { statement::base* child = *itr; if(ignore_contents_stmt(child)) continue; if( child->get_type() == statement::base::STMT_PACKAGE ) { // Always report the nested packages. filtered_contents.push_back(child); } else { // Issue 0034 if( child->get_parent() == stmt ) filtered_contents.push_back(child); } } // Issue 0047: -rptcsi also controls the contents column. write_summary_tree(os,filtered_contents,true,stmt,m_rptcsi.c_str()); write_unindent(os); return; } // ================================================ // This is the standard contents table. // ================================================ statement::base::stmts_t contents; make_contents(stmt,contents,sort_flag); if( contents.size() ) { os << "\n"; write_section_header(os,"Contents",false); write_indent(os); os << "\n" << "\n" << ""; // Issue 0038 unsigned max_entity_size = m_sw.rptmlcei() + m_sw.rptmlcifi(); if( inherited_from_col ) { os << ""; // Issue 0037 max_entity_size = m_sw.rptmlcei(); } os << "" << ""; if( m_sw.rptsrc() ) { os << ""; } os << "" << "\n"; statement::base::stmts_itr_t itr = contents.begin(); for(;itr!=contents.end();++itr) { statement::base* child = *itr; os << "\n"; // Write the id. os << ""; if( inherited_from_col ) { os << ""; } // Type string type = child->get_type_name2(); // include static if( child->get_type() == statement::base::STMT_PACKAGE && child->get_comment() ) { statement::comment doc(child->get_comment()); string tid = doc.get_pkgdoc_tid(); if( tid.size() ) type = tid; } os << ""; // Scope/Access os << ""; // Source info if( m_sw.rptsrc() ) { os << ""; } // Short description os << ""; os << "\n"; } os << "
EntityInherited FromTypeScopeSourceShort Description
"; if( child->get_type() == statement::base::STMT_PACKAGE || child->get_type() == statement::base::STMT_NAMESPACE_BEGIN ) { string link; if( stmt->get_parent() ) make_rel_path(stmt,child,link); else make_abs_path(child,link); write_link(os,child,link.c_str()); } else { if( child->get_parent() == stmt ) { string id = child->get_id(); if( max_entity_size && id.size() > max_entity_size ) { // Issue 0159: // Truncate the id to the shorter number of characters // and add the '..'. string short_id = id.substr(0,max_entity_size); short_id += ".."; write_link(os,child,short_id.c_str()); } else { write_link(os,child,id.c_str()); } } else { // These are children from other parents. // They are created when derived classes // inherit methods. string url; string id; make_file_url(url,stmt); make_tag_id(child,id); //write_link(os,child->get_parent()); //os << " :: "; os << ""; id = child->get_id(); if( max_entity_size && id.size() > max_entity_size ) { // Issue 0159: // Truncate the id to the shorter number of characters // and add the '..'. string short_id = id.substr(0,max_entity_size); short_id += ".."; write_html_formatted_string(os,short_id.c_str()); } else { write_html_formatted_string(os,id.c_str()); } os << ""; } } os << ""; if( child->get_parent() && child->get_parent() != stmt ) { string id; child->get_parent()->get_hier_id_no_pkgs(id); if( m_sw.rptmlcifi() && id.size() > m_sw.rptmlcifi() ) { // Issue 0159: // Truncate the id to the shorter number of characters // and add the '..'. string short_id = id.substr(0,m_sw.rptmlcifi()); short_id += ".."; write_link(os,child->get_parent(),short_id.c_str()); } else { write_link(os,child->get_parent(),id.c_str()); } } os << "" << type << " "; os << child->get_access_name(); os << ""; os << child->get_file() << ":" << child->get_lineno(); os << ""; write_short_desc(os,child); os << "
\n"; } write_unindent(os); } // ================================================================ // Indent. // ================================================================ void ccdoc::phase3::html::write_indent(ostream& os) const { #if 0 //
uses fewer characters os << "
&nsp;&nsp;&nsp;&nsp;\n"; #endif os << "
\n"; } // ================================================================ // Unindent. // ================================================================ void ccdoc::phase3::html::write_unindent(ostream& os) const { #if 0 os << "
\n"; #endif os << "
\n"; } // ================================================================ // Section header. // ================================================================ void ccdoc::phase3::html::write_section_header(ostream& os, const char* title, bool contents, const char* prev, const char* next) { os << "
" << "" << "" << "" << "" << "" << "
" << "

" << title << "

" << "
"; if( contents ) { os << "? "; } os << "^ \n"; if( prev && *prev ) { os << "< "; } if( next && *next ) { os << "> "; } os << "
\n"; } // ================================================================ // Section header. // ================================================================ void ccdoc::phase3::html::write_section_header(ostream& os, statement::base* stmt, bool contents, const char* prev, const char* next) { if( stmt ) { string id = format_string_for_html(stmt->get_id()) ; string title; if( stmt->get_type() == statement::base::STMT_PACKAGE ) { // Issue 31 - The user might have specified their own package tag. title = stmt->get_type_name2(); if( stmt->get_comment() ) { statement::comment doc(stmt->get_comment()); string tid = doc.get_pkgdoc_tid(); if( tid.size() ) { title = tid; } } } else if( stmt->get_template() ) { // This is a template, output the template information in the // title. title = ""; // Capture the template information. const ccdoc::statement::base::cstrs_t tokens = stmt->get_tokens(); ccdoc::statement::base::cstrs_citr_t itr1 = tokens.begin(); int level = 0; for(;itr1!=tokens.end();++itr1) { string tmp = *itr1; title += tmp; title += " "; if( tmp == "<" ) { ++level; } else if( tmp == ">" ) { --level; if( level == 0 ) break; } } title += "
"; title += stmt->get_type_name2(); } // The "operator" keyword is already built into the operator id. else if( stmt->get_type() != statement::base::STMT_FUNCTION_OPERATOR && stmt->get_type() != statement::base::STMT_METHOD_OPERATOR ) { title = stmt->get_type_name2(); } title += " "; title += id; write_section_header(os,title.c_str(),contents,prev,next); } else { write_section_header(os,"unknown",contents,prev,next); } } // ================================================================ // Code begin // ================================================================ void ccdoc::phase3::html::code_begin(ostream& os) { os << "
"; os << "
";
  os << endl;
}
// ================================================================
// Code end
// ================================================================
void ccdoc::phase3::html::code_end(ostream& os) {
  os << "
"; os << "
"; os << endl; } // ================================================================ // Load // ================================================================ void ccdoc::phase3::html::load(const string& fn, string& contents) { if( fn.size() ) { if( m_sw.verbose() ) { s_log << "loading " << fn.c_str() << " ...\n"; } ifstream is(fn.c_str()); if( !is ) { s_log.warning() << "Cannot read file '" << fn.c_str() << "'\n" << s_log.enable(); return; } // Read the file (including whitespace) into the contents string. char ch; while( is.get(ch) ) { contents += ch; } } } // ================================================================ // Write out the code section. // ================================================================ void ccdoc::phase3::html::write_code_section(ostream& os, statement::base* stmt) { // The first step is to group the tokens that are // associated by '::'. statement::base::strs_t grouped_tokens; { const statement::base::cstrs_t& src = stmt->get_tokens(); statement::base::cstrs_citr_t itr = src.begin(); for(;itr!=src.end();++itr) { string x = *itr; if( x == "::" ) { string token; if( grouped_tokens.size() ) { token = grouped_tokens.back(); grouped_tokens.pop_back(); } token += *itr; ++itr; if( itr != src.end() ) { token += *itr; } grouped_tokens.push_back(token); } else { grouped_tokens.push_back(x); } } } // Output the code portion. os << "
Code:
"; if( stmt->get_type() == statement::base::STMT_ATTRIBUTE || stmt->get_type() == statement::base::STMT_ATTRIBUTE_FUNCTION ) { write_code_subsection_var(os,stmt,grouped_tokens); } else if( stmt->get_type() == statement::base::STMT_FUNCTION || stmt->get_type() == statement::base::STMT_METHOD_CONSTRUCTOR || stmt->get_type() == statement::base::STMT_METHOD_DESTRUCTOR || stmt->get_type() == statement::base::STMT_METHOD ) { write_code_subsection_fct(os,stmt,grouped_tokens); } else if( stmt->get_type() == statement::base::STMT_FUNCTION_OPERATOR || stmt->get_type() == statement::base::STMT_METHOD_OPERATOR ) { write_code_subsection_opr(os,stmt,grouped_tokens); } else if( stmt->get_type() == statement::base::STMT_ENUM ) { write_code_subsection_enum(os,stmt,grouped_tokens); } else if( stmt->get_type() == statement::base::STMT_VARIABLE || stmt->get_type() == statement::base::STMT_VARIABLE_FUNCTION ) { write_code_subsection_var(os,stmt,grouped_tokens); } else { if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << stmt->get_access_name() << " "; string external_linkage = stmt->get_extern(); if( external_linkage.size() ) os << "extern " << external_linkage << " "; statement::base::strs_itr_t itr = grouped_tokens.begin(); for(;itr!=grouped_tokens.end();++itr) { string& token = *itr; if( itr != grouped_tokens.begin() ) { os << " "; } write_code_subsection_token(os,stmt,token); } if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << "\n"; } os << "
\n"; } // ================================================================ // Write out the code section for variables. // ================================================================ void ccdoc::phase3::html::write_code_subsection_var(ostream& os, statement::base* stmt, statement::base::strs_t& group) { // ================================================ // There is some special case handling here for // variables with no type. // ================================================ if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << stmt->get_access_name() << " "; string external_linkage = stmt->get_extern(); if( external_linkage.size() ) os << "extern " << external_linkage << " "; statement::base::strs_t& vec1 = group; if( vec1.size() == 1 ) { // This is the special case of a variable that // was declared as part of a class, struct, union // or enum declaration of the form: // enum AA {A,B,C} var; statement::base* parent = stmt->get_parent(); bool found = false; if( parent ) { statement::base::stmts_t& vec2 = parent->get_children(); statement::base::stmts_itr_t itr2 = vec2.begin(); for(;itr2!=vec2.end();++itr2) { if( *itr2 == stmt ) { break; } } // We found the statement, now back up until we find // an enum, class, struct or union. Variables of the // form int a,b,c; are handled in the parsing phase. if( stmt == *itr2 ) { for(;itr2!=vec2.begin();--itr2) { statement::base* end = *itr2; bool done = false; switch( end->get_type() ) { case statement::base::STMT_ENUM: done = true; write_link(os,end); found = true; break; case statement::base::STMT_CLASS_END: case statement::base::STMT_STRUCT_END: case statement::base::STMT_UNION_END: { done = true; statement::base* begin = end->get_matching_begin(); if( begin ) { write_link(os,begin); found = true; } break; } default: break; } if(done) break; } } } if( !found ) { // TODO: create a test for this error condition. // It is not clear how this could be done. Perhaps this // is dead code. os << "unknown_type"; // Issue 109: s_log.warning() << "UNDEF: Unknown type for variable '" << stmt->get_id() << "' at line " << stmt->get_lineno() << " in file " << stmt->get_file() << "\n" << s_log.enable(); } os << " "; } statement::base::strs_itr_t itr1 = vec1.begin(); for(;itr1!=vec1.end();++itr1) { string& token = *itr1; if( itr1 != vec1.begin() ) { os << " "; } write_code_subsection_token(os,stmt,token); } os << "\n"; if( m_sw.rptfwcf() ) os << ""; // Issue 0045 } // ================================================================ // Write out the code section for functions. // ================================================================ void ccdoc::phase3::html::write_code_subsection_fct(ostream& os, statement::base* stmt, statement::base::strs_t& group) { // Here is where we get a bit fancy. The arguments are aligned // somewhat. statement::base::strs_t& vec1 = group; statement::base::strs_itr_t itr1 = vec1.begin(); bool found_id = false; bool found_arg_lp = false; int depth = 0; os << "
"; if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << stmt->get_access_name() << " "; string external_linkage = stmt->get_extern(); if( external_linkage.size() ) os << "extern " << external_linkage << " "; for(;itr1!=vec1.end();++itr1) { string& token = *itr1; if( itr1 != vec1.begin() ) { os << " "; } if( !found_id && token == stmt->get_id() ) { found_id = true; os << ""; write_code_subsection_token(os,stmt,token); os << ""; } else { write_code_subsection_token(os,stmt,token); } if( token == "(" ) depth++; else if( token == ")" ) depth--; if(!found_arg_lp && found_id && depth==1) { found_arg_lp = true; if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << ""; if( m_sw.rptfwcf() ) os << ""; // Issue 0045 } if( depth == 1 && token == "," ) { if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << "
" << ""; if( m_sw.rptfwcf() ) os << ""; // Issue 0045 } } if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << "
\n"; } // ================================================================ // Write out the code section for operator functions. // ================================================================ void ccdoc::phase3::html::write_code_subsection_opr(ostream& os, statement::base* stmt, statement::base::strs_t& group) { // Here is where we get a bit fancy. The arguments are aligned // somewhat. statement::base::strs_t& vec1 = group; statement::base::strs_itr_t itr1 = vec1.begin(); bool found_id = false; bool found_arg_lp = false; int depth = 0; os << "
"; if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << stmt->get_access_name() << " "; string external_linkage = stmt->get_extern(); if( external_linkage.size() ) os << "extern " << external_linkage << " "; // Walk through the statement tokens. // If it is an operator, it will be recognized // by the presence of the "operator" keyword. for(;itr1!=vec1.end();++itr1) { string& token = *itr1; if( itr1 != vec1.begin() ) { os << " "; } if( !found_id && token == "operator" ) { found_id = true; os << ""; token = stmt->get_id(); write_code_subsection_token(os,stmt,token); os << ""; // Now skip ahead to the lp. if( token == "operator ()" ) { // Special case. ++itr1; ++itr1; } else { for(;itr1!=vec1.end();++itr1) { if( *itr1 == "(" ) { --itr1; break; } } } } else { write_code_subsection_token(os,stmt,token); } if( token == "(" ) { depth++; } else if( token == ")" ) { depth--; } if( !found_arg_lp && found_id && depth==1 ) { found_arg_lp = true; if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << ""; if( m_sw.rptfwcf() ) os << ""; // Issue 0045 } if( depth == 1 && token == "," ) { if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << "
" << ""; if( m_sw.rptfwcf() ) os << ""; // Issue 0045 } } if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << "
\n"; } // ================================================================ // Write out the code section for enums. // ================================================================ void ccdoc::phase3::html::write_code_subsection_enum(ostream& os, statement::base* stmt, statement::base::strs_t& group) { // Here is where we get a bit fancy. The arguments are aligned // somewhat. statement::base::strs_t& vec1 = group; statement::base::strs_itr_t itr1 = vec1.begin(); bool found_id = false; bool found_arg_lp = false; int depth = 0; os << "
"; if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << stmt->get_access_name() << " "; string external_linkage = stmt->get_extern(); if( external_linkage.size() ) os << "extern " << external_linkage << " "; for(;itr1!=vec1.end();++itr1) { string& token = *itr1; if( itr1 != vec1.begin() ) { os << " "; } if( !found_id && token == stmt->get_id() ) { found_id = true; os << ""; write_code_subsection_token(os,stmt,token); os << ""; } else { write_code_subsection_token(os,stmt,token); } if( token == "{" ) depth++; else if( token == "}" ) depth--; // The id is not checked for here because anonymuous // enums do not have ids. if(!found_arg_lp && depth==1) { found_arg_lp = true; if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << ""; if( m_sw.rptfwcf() ) os << ""; // Issue 0045 } if( depth == 1 && token == "," ) { if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << "
" << ""; if( m_sw.rptfwcf() ) os << ""; // Issue 0045 } } if( m_sw.rptfwcf() ) os << ""; // Issue 0045 os << "
\n"; } // ================================================================ // Write out token for code section. // ================================================================ void ccdoc::phase3::html::write_code_subsection_token(ostream& os, statement::base* stmt, const string& token) { if( token.size() == 0 ) return; if( token == "<" ) os << "<"; else if( token == ">" ) os << ">"; else if( token == "&" ) os << "&"; else if( token == "..." ) os << "..."; else if( token == stmt->get_id() ) { os << "" << format_string_for_html(token) << ""; } else if( is_cxx_keyword(token) ) { os << "" << token << ""; } else if( ('a' <= token[0] && token[0] <= 'z' ) || ('A' <= token[0] && token[0] <= 'Z' ) || ('_' == token[0] ) || ('$' == token[0] ) ) { // ================================================ // Do a simple search for local matches in the // parent. // This matches things like: // class A { // public: // A& operator = (const A&); // }; // ================================================ if( stmt->get_parent() ) { if( stmt->get_parent()->get_type() == statement::base::STMT_CLASS_BEGIN || stmt->get_parent()->get_type() == statement::base::STMT_STRUCT_BEGIN || stmt->get_parent()->get_type() == statement::base::STMT_UNION_BEGIN ) { if( token == stmt->get_parent()->get_id() ) { os << "" << format_string_for_html(token) << ""; return; } } } // ================================================ // Issue 0118 // tokenize the id // A::B::C becomes A,B,C // ================================================ vector tokens; size_t beg=0; size_t end=beg; for(;endbeg && token[end] == ':' && token[end-1] == ':') { tokens.push_back( token.substr(beg,end-1) ); beg = end+1; } } if(beg matches; if( stmt->get_parent() ) { statement::base* parent = stmt->get_parent(); for( ;parent; parent = parent->get_parent() ) { if( ignore_contents_stmt(parent) ) break; write_code_subsection_token(matches,parent,tokens,0); if( matches.size() ) { write_links(os,matches,token.c_str()); return; } } } // ================================================ // Check for links here by looking up entities // in the database. Begin by looking in the same // namespace. // ================================================ statement::base::stmts_t token_stmts; m_db.get_stmt_no_pkgs(token,token_stmts); if( token_stmts.size() ) { // Filter the token statements by type. statement::base::stmts_t valid_token_stmts; statement::base::stmts_itr_t itr = token_stmts.begin(); for(;itr!=token_stmts.end();++itr) { switch( (*itr)->get_type() ) { case statement::base::STMT_CLASS_BEGIN: case statement::base::STMT_STRUCT_BEGIN: case statement::base::STMT_UNION_BEGIN: case statement::base::STMT_ENUM: case statement::base::STMT_TYPEDEF_FUNCTION: case statement::base::STMT_TYPEDEF_VARIABLE: case statement::base::STMT_VARIABLE: // Issue 0091: case statement::base::STMT_VARIABLE_FUNCTION: case statement::base::STMT_FUNCTION: case statement::base::STMT_FUNCTION_OPERATOR: valid_token_stmts.push_back(*itr); break; default: break; } } if( valid_token_stmts.size() == 1 ) { // If there is only one, use it. write_link(os,valid_token_stmts[0],token.c_str()); return; } else if( valid_token_stmts.size() > 1 ) { // If there is more than one statement that matches, // try finding it in the same namespace as the stmt. statement::base* nsp = 0; statement::base* parent = stmt->get_parent(); while(parent) { if(parent->get_type() == statement::base::STMT_NAMESPACE_BEGIN) { nsp = parent; break; } parent = parent->get_parent(); } statement::base::stmts_t valid_token_stmts_in_nsp; itr = valid_token_stmts.begin(); for(;itr!=valid_token_stmts.end();++itr) { statement::base* nsp1 = 0; parent = *itr; while(parent) { if(parent->get_type() == statement::base::STMT_NAMESPACE_BEGIN) { nsp1 = parent; break; } parent = parent->get_parent(); } if( nsp == nsp1 ) { valid_token_stmts_in_nsp.push_back(*itr); // Exit after one for efficiency. break; } } if( valid_token_stmts_in_nsp.size() ) { write_link(os,valid_token_stmts_in_nsp[0],token.c_str()); return; } else { write_link(os,valid_token_stmts[0],token.c_str()); return; } } } // No matching statements were found. os << format_string_for_html(token); } else { os << format_string_for_html(token); } } // ================================================================ // Write out token for code section if it matches an id in // the specified scope. // ================================================================ bool ccdoc::phase3::html::write_code_subsection_token(ostream& os, statement::base::stmts_t& vec, const string& token) { statement::base::stmts_itr_t itr = vec.begin(); for(;itr!=vec.end();++itr) { statement::base* stmt = *itr; if( token == stmt->get_id() ) { switch( stmt->get_type() ) { case statement::base::STMT_CLASS_BEGIN: case statement::base::STMT_STRUCT_BEGIN: case statement::base::STMT_UNION_BEGIN: case statement::base::STMT_ENUM: case statement::base::STMT_TYPEDEF_FUNCTION: case statement::base::STMT_TYPEDEF_VARIABLE: case statement::base::STMT_VARIABLE: // Issue 0091: case statement::base::STMT_VARIABLE_FUNCTION: case statement::base::STMT_FUNCTION: case statement::base::STMT_FUNCTION_OPERATOR: // Attach to the first one of the correct type. write_link(os,stmt,token.c_str()); return true; default: break; } } } return false; } // ================================================================ // Issue 0118 // Match a partial specification. // the specified scope. // ================================================================ void ccdoc::phase3::html::write_code_subsection_token(statement::base::stmts_t& vec, statement::base* stmt, const vector& tokens, size_t idx) const { vector local_matches; stmt->get_children_by_id(local_matches,tokens[idx]); if( local_matches.size() ) { ++idx; vector::iterator itr = local_matches.begin(); for(;itr!=local_matches.end();++itr) { if( ignore_contents_stmt(*itr) ) continue; if( idx < tokens.size() ) { write_code_subsection_token(vec,*itr,tokens,idx); } else { vec.push_back(*itr); } } } } // ================================================================ // Is this a keyword. // ================================================================ bool ccdoc::phase3::html::is_cxx_keyword(const string& token) { if( m_keywords.size() == 0 ) { // ================================================ // Load the keywords: // Table 3 (2.11) // ================================================ m_keywords.insert("asm"); m_keywords.insert("auto"); m_keywords.insert("bool"); m_keywords.insert("break"); m_keywords.insert("case"); m_keywords.insert("catch"); m_keywords.insert("char"); m_keywords.insert("class"); m_keywords.insert("const"); m_keywords.insert("const_cast"); m_keywords.insert("continue"); m_keywords.insert("default"); m_keywords.insert("delete"); m_keywords.insert("do"); m_keywords.insert("double"); m_keywords.insert("dynamic_cast"); m_keywords.insert("else"); m_keywords.insert("enum"); m_keywords.insert("explicit"); m_keywords.insert("export"); m_keywords.insert("extern"); m_keywords.insert("false"); m_keywords.insert("float"); m_keywords.insert("for"); m_keywords.insert("friend"); m_keywords.insert("goto"); m_keywords.insert("if"); m_keywords.insert("inline"); m_keywords.insert("int"); m_keywords.insert("long"); m_keywords.insert("mutable"); m_keywords.insert("namespace"); m_keywords.insert("new"); m_keywords.insert("operator"); m_keywords.insert("private"); m_keywords.insert("protected"); m_keywords.insert("public"); m_keywords.insert("register"); m_keywords.insert("reinterpret_cast"); m_keywords.insert("return"); m_keywords.insert("short"); m_keywords.insert("signed"); m_keywords.insert("sizeof"); m_keywords.insert("static"); m_keywords.insert("static_cast"); m_keywords.insert("struct"); m_keywords.insert("switch"); m_keywords.insert("template"); m_keywords.insert("this"); m_keywords.insert("throw"); m_keywords.insert("true"); m_keywords.insert("try"); m_keywords.insert("typedef"); m_keywords.insert("typeid"); m_keywords.insert("typename"); m_keywords.insert("union"); m_keywords.insert("unsigned"); m_keywords.insert("using"); m_keywords.insert("virtual"); m_keywords.insert("void"); m_keywords.insert("volatile"); m_keywords.insert("wchar_t"); m_keywords.insert("while"); // ================================================ // Add in the alternate representations for some // of the operators and punctuators. // ================================================ m_keywords.insert("and"); m_keywords.insert("and_eq"); m_keywords.insert("bitand"); m_keywords.insert("bitor"); m_keywords.insert("compl"); m_keywords.insert("not"); m_keywords.insert("not_eq"); m_keywords.insert("or"); m_keywords.insert("or_eq"); m_keywords.insert("xor"); m_keywords.insert("xor_eq"); } set::const_iterator itr = m_keywords.find(token); return itr != m_keywords.end(); } // ================================================================ // Compare statements for the make_contents() algorithm. // ================================================================ namespace { bool compare_stmts_full_paths(const ccdoc::statement::base* a, const ccdoc::statement::base* b) { assert(a); assert(b); string a_id; string b_id; a->get_hier_id(a_id); b->get_hier_id(b_id); // Do a case insensitive compare to make things easier // to find. const char* p1 = a_id.c_str(); const char* p2 = b_id.c_str(); for(;*p1 && *p2;++p1,++p2) { if( *p1 != *p2 ) { int ch1 = *p1; int ch2 = *p2; // Convert to upper case. if( 'a' <= ch1 && ch1 <= 'z' ) ch1 = ( ch1 - 'a' ) + 'A'; if( 'a' <= ch2 && ch2 <= 'z' ) ch2 = ( ch2 - 'a' ) + 'A'; if( ch1 != ch2 ) return ch1 < ch2; } } return *p1 < *p2; } } // ================================================================ // Compare statements for the make_contents() algorithm. // ================================================================ namespace { bool compare_stmts(const ccdoc::statement::base* a, const ccdoc::statement::base* b) { assert(a); assert(b); // Special cases: // Destructors always show up before first. if( a->get_type() == ccdoc::statement::base::STMT_METHOD_DESTRUCTOR ) { return true; // aget_type() == ccdoc::statement::base::STMT_METHOD_DESTRUCTOR ) { return false; // bget_type() == ccdoc::statement::base::STMT_METHOD_CONSTRUCTOR ) { // Constructors always match. if( b->get_type() == ccdoc::statement::base::STMT_METHOD_CONSTRUCTOR ) { return false; } // Constructors always show up first. return true; // aget_type() == ccdoc::statement::base::STMT_METHOD_CONSTRUCTOR ) { // Constructors always show up first. return false; // bget_type() == ccdoc::statement::base::STMT_PACKAGE || a->get_type() == ccdoc::statement::base::STMT_NAMESPACE_BEGIN ) { if( ( b->get_type() == ccdoc::statement::base::STMT_PACKAGE || b->get_type() == ccdoc::statement::base::STMT_NAMESPACE_BEGIN ) ) { // a and b are both packages or namespaces, // compare them hierarchically a->get_hier_id(a_id); b->get_hier_id(b_id); } else { // a is a package or namespace and b isn't // a is less than b. // Always put packages and namespaces first. return true; } } else if( ( b->get_type() == ccdoc::statement::base::STMT_PACKAGE || b->get_type() == ccdoc::statement::base::STMT_NAMESPACE_BEGIN ) ) { // b is a package or namespace and a isn't // b is less than a. return false; } else { a_id = a->get_id(); b_id = b->get_id(); } // Do a case insensitive compare to make things easier // to find. const char* p1 = a_id.c_str(); const char* p2 = b_id.c_str(); for(;*p1 && *p2;++p1,++p2) { if( *p1 != *p2 ) { int ch1 = *p1; int ch2 = *p2; // Convert to upper case. if( 'a' <= ch1 && ch1 <= 'z' ) ch1 = ( ch1 - 'a' ) + 'A'; if( 'a' <= ch2 && ch2 <= 'z' ) ch2 = ( ch2 - 'a' ) + 'A'; if( ch1 != ch2 ) return ch1 < ch2; } } return *p1 < *p2; } } // ================================================================ // Make contents. // This is used to get list of entities for contents and sections. // ================================================================ void ccdoc::phase3::html::make_contents(statement::base* stmt, statement::base::stmts_t& contents, bool sort_flag) { // ================================================ // Filter out the children that we don't care // about on this run. // ================================================ statement::base::stmts_t& children = stmt->get_children(); if( children.size() ) { statement::base::stmts_itr_t itr = children.begin(); for(;itr!=children.end();++itr) { statement::base* child = *itr; if(ignore_contents_stmt(child)) continue; // ================================================ // Special case handling for namespaces and packages, // we provide nested information for them. // ================================================ if( child->get_type() == statement::base::STMT_PACKAGE ) { statement::base::stmts_t vec1; make_pkg_index_children(child,vec1); statement::base::stmts_itr_t itr1 = vec1.begin(); for(;itr1!=vec1.end();++itr1) { contents.push_back(*itr1); } // Additional special case handling for packages. // Look for child namespaces that are stored as tokens. const statement::base::cstrs_t& nsps = child->get_tokens(); statement::base::cstrs_citr_t nsps_itr = nsps.begin(); for(;nsps.begin()!=nsps.end();++nsps_itr) { string token = *nsps_itr; statement::base::stmts_t nsp_stmts; m_db.get_stmt_no_pkgs(token,nsp_stmts); // Filter out the statements that don't have packages as // parents. Add the top namespaces to the contents list. statement::base::stmts_itr_t nsp_stmts_itr = nsp_stmts.begin(); for(;nsp_stmts_itr!=nsp_stmts.end();++nsp_stmts_itr) { statement::base* nsp = *nsp_stmts_itr; if( nsp->get_parent()->get_type() == statement::base::STMT_PACKAGE ) { vec1.clear(); make_pkg_index_children(nsp,vec1); for(itr1 = vec1.begin();itr1!=vec1.end();++itr1) { contents.push_back(*itr1); } } } } } else if( child->get_type() == statement::base::STMT_NAMESPACE_BEGIN ) { statement::base::stmts_t vec1; make_pkg_index_children(child,vec1); statement::base::stmts_itr_t itr1 = vec1.begin(); for(;itr1!=vec1.end();++itr1) { contents.push_back(*itr1); } } else { contents.push_back(child); } } } // ================================================ // Load parent class contents. // ================================================ if( stmt->get_type() == statement::base::STMT_CLASS_BEGIN || stmt->get_type() == statement::base::STMT_STRUCT_BEGIN ) { make_class_contents(stmt,contents); } // ================================================ // Sort the remaining children by name. // ================================================ if( sort_flag ) { // Use a stable sort here so that we don't have // to worry about how equivalent entries are // handled. Duplicate entries are expected. The // most common cause is overloaded methods. // This is verified by test88. //sort(contents.begin(),contents.end(),compare_stmts); // fails stable_sort(contents.begin(),contents.end(),compare_stmts); // Issue 0183 } } // ================================================================ // Make class contents. // This is used to get list of entities for contents and sections. // Classes are a special case because we also get the inherited // methods. // ================================================================ void ccdoc::phase3::html::make_class_contents(statement::base* stmt, statement::base::stmts_t& contents) { if( m_sw.rptim() ) { // ================================================ // Get the classes that this class derives from. // ================================================ statement::base::stmts_t classes; load_inheritance_classes(stmt,classes); if( classes.size() == 0 ) return; // ================================================ // Populate the used_method_ids with the contents // of the original class. // ================================================ statement::base::stmts_t used_classes; set used_method_ids; statement::base::stmts_itr_t contents_itr = contents.begin(); for(;contents_itr!=contents.end();++contents_itr) { statement::base* rec = *contents_itr; if( rec->get_type() == statement::base::STMT_METHOD ) { string key = rec->get_id(); used_method_ids.insert( key ); } } if( classes.size() ) make_class_contents(contents,classes,used_classes,used_method_ids); } } // ================================================================ // Make class contents. // This is used to get list of entities for contents and sections. // Classes are a special case because we also get the inherited // methods. // ================================================================ void ccdoc::phase3::html::make_class_contents(statement::base::stmts_t& contents, statement::base::stmts_t& classes, statement::base::stmts_t& used_classes, set& used_method_ids) { // ================================================ // Iterate over each of the parent classes to // capture their methods. Ignore methods that // have already been defined. // ================================================ statement::base::stmts_itr_t classes_itr = classes.begin(); for(;classes_itr!=classes.end();++classes_itr) { statement::base* class_rec = *classes_itr; // This is an O(N^2) test to verify that we don't have // an inheritance loop. The performance s/b ok because // the inheritance depth s/b small (<10). statement::base::stmts_itr_t used_classes_itr = used_classes.begin(); for(;used_classes_itr!=used_classes.end();++used_classes_itr) { if( *used_classes_itr == class_rec ) { break; } } if( used_classes_itr != used_classes.end() ) continue; used_classes.push_back(class_rec); // Add methods to the contents. statement::base::stmts_t& children = class_rec->get_children(); statement::base::stmts_itr_t children_itr = children.begin(); for(;children_itr!=children.end();++children_itr) { statement::base* rec = *children_itr; if( rec->get_type() == statement::base::STMT_METHOD ) { if( !ignore_contents_stmt(rec) ) { // Issue 0040 string key = rec->get_id(); if( used_method_ids.find( key ) == used_method_ids.end() ) { contents.push_back( rec ); } } } } // Walk back through and update the use_method_ids. // This is not done as the methods are traversed // because we want to capture functions with the // same ids but different signatures. children_itr = children.begin(); for(;children_itr!=children.end();++children_itr) { statement::base* rec = *children_itr; if( rec->get_type() == statement::base::STMT_METHOD ) { if( !ignore_contents_stmt(rec) ) { // Issue 0040 string key = rec->get_id(); if( used_method_ids.find( key ) == used_method_ids.end() ) { used_method_ids.insert( key ); } } } } // Load the parents of this class recursively. statement::base::stmts_t parent_classes; load_inheritance_classes(class_rec,parent_classes); if( parent_classes.size() ) make_class_contents(contents,parent_classes,used_classes,used_method_ids); } } // ================================================================ // Load the inheritance classes. // Load the classes that this class derives from. // ================================================================ void ccdoc::phase3::html::load_inheritance_classes(statement::base* stmt, statement::base::stmts_t& classes) { const statement::base::cstrs_t& tokens = stmt->get_tokens(); statement::base::cstrs_citr_t tokens_itr = tokens.begin(); for(;tokens_itr!=tokens.end();++tokens_itr) { string token = *tokens_itr; if( token == ":" ) { // This is where the interesting stuff starts. tokens_itr++; break; } } if( tokens_itr == tokens.end() ) { // No tokens. // This class does not derive from any other classes. return; } bool found_id = false; int depth = 0; string id; for(;tokens_itr!=tokens.end();++tokens_itr) { string token = *tokens_itr; if( !found_id ) { if( token == "virtual" || token == "public" || token == "protected" || token == "private" ) { continue; } found_id = true; } if( found_id ) { if( token == "<" ) { // This handles cases like: // class A : public foo ; // class A : public foo > ; depth++; } else if( token == ">" ) { depth--; } else if( depth == 0 ) { if( token == "," ) { // Output the extends information. statement::base::stmts_t matches; if( 1 == load_extend_classes( id, matches, stmt ) ) { classes.push_back( matches[0] ); } else { // TODO: Warn there there were multiple classes. } id = ""; found_id = false; depth = 0; continue; } } id += token; } } statement::base::stmts_t matches; if( 1 == load_extend_classes( id, matches, stmt ) ) { classes.push_back( matches[0] ); } else { // TODO: Warn there there were multiple classes. } } // ================================================================ // Get the class record. // ================================================================ unsigned ccdoc::phase3::html::load_extend_classes(const string& id, statement::base::stmts_t& out, statement::base* stmt) { statement::base::stmts_t id_stmts; m_db.get_stmt_no_pkgs(id,id_stmts); if( id_stmts.size() == 0 ) { // Issue 0048 string scoped_id; statement::base::stmts_t parents; stmt->get_parents_no_pkgs(parents); get_fully_scoped_name(scoped_id,id,parents); m_db.get_stmt_no_pkgs(scoped_id,id_stmts); if( id_stmts.size() == 0 ) return 0; } if( id_stmts.size() > 0 ) { // Filter out the statements that are not classes or structs. statement::base::stmts_itr_t sitr = id_stmts.begin(); for(;sitr!=id_stmts.end();++sitr) { statement::base* id_stmt = *sitr; if( id_stmt->get_type() == statement::base::STMT_CLASS_BEGIN || id_stmt->get_type() == statement::base::STMT_STRUCT_BEGIN ) { out.push_back(id_stmt); } } } return out.size(); } // ================================================================ // Get the fully scoped name. // ================================================================ void ccdoc::phase3::html::get_fully_scoped_name(string& out_id, const string& in_id, statement::base::stmts_t parents) const { statement::base::strs_t ids; m_db.parse_path(in_id,ids); if( parents.size() ) { bool doit = true; size_t last1 = 0; if( ids.size() > 1 ) { size_t sz1 = ids.size() - 1; size_t last2 = parents.size() - 1; last1 = sz1 - 1; for(;last1!=0 && last2!=0;--last1,--last2) { string nm = parents[last2]->get_id(); if( nm != ids[last1] ) { doit = false; break; } } if( doit ) { // The match was found. last1 = sz1 - 1; } } if( doit ) { out_id = ""; bool first = true; statement::base::stmts_itr_t itr = parents.begin(); for(;itr!=parents.end();++itr) { if( !first ) out_id += "::"; out_id += (*itr)->get_id(); first = false; } if( !first ) out_id += "::"; out_id += ids[last1]; return; } } // Issue 0127 // Check for the special case of B. // This is invalid for a scoped name because // it cannot be looked up. out_id = in_id; string::size_type f = out_id.find('<'); if( f != string::npos ) { out_id.erase(f); } } // ================================================================ // Ignore a statement? // ================================================================ bool ccdoc::phase3::html::ignore_contents_stmt(statement::base* stmt) const { switch( stmt->get_type() ) { case statement::base::STMT_FRIEND_CLASS: case statement::base::STMT_FRIEND_FUNCTION: case statement::base::STMT_CLASS_END: case statement::base::STMT_COMMENT_PKGDOC: case statement::base::STMT_COMMENT_PKGDOC_URL: case statement::base::STMT_COMMENT_PREFIX: case statement::base::STMT_COMMENT_SUFFIX: case statement::base::STMT_IGNORE: case statement::base::STMT_NAMESPACE_END: case statement::base::STMT_STRUCT_END: case statement::base::STMT_UNION_END: case statement::base::STMT_MACROINST_FUNCTION: case statement::base::STMT_MACROINST_VARIABLE: // Ignore some of the statements all of the time. return true; case statement::base::STMT_MACRODEF_0_0: case statement::base::STMT_MACRODEF_0_1: case statement::base::STMT_MACRODEF_0_N: case statement::base::STMT_MACRODEF_N_N: if( !m_sw.rptmac() ) return true; return false; case statement::base::STMT_TYPEDEF_FUNCTION: case statement::base::STMT_TYPEDEF_VARIABLE: if( !m_sw.rpttyp() ) return true; // Don't return false until we check the // accessibility. break; case statement::base::STMT_UNION_BEGIN: if( !m_sw.rptun() ) return true; // Don't return false until we check the // accessibility. break; #if 0 // Issue 0073 // This section of code was commented out because // ccdoc was reporting private methods when -norptpri was // specified. case statement::base::STMT_METHOD_CONSTRUCTOR: case statement::base::STMT_METHOD_DESTRUCTOR: // Ccdoc never ignores constructors, destructors and // assignment operators. return false; case statement::base::STMT_FUNCTION_OPERATOR: case statement::base::STMT_METHOD_OPERATOR: // Ccdoc never ignores assignment operators. if( stmt->get_id() ) { string id = stmt->get_id(); if( id == "operator =" ) return false; } break; #endif case statement::base::STMT_PACKAGE: if( stmt->get_parent() == m_db.root() ) { string id = stmt->get_id(); if( id == "@null" || id == "[NULL]" ) { // Ignore top level packages that have the names // @null and [NULL]. return true; } } break; case statement::base::STMT_NAMESPACE_BEGIN: // Skip the internal ids of the form: // ++: if( stmt->get_id() && stmt->get_id()[0] == '+' ) return true; break; default: break; } // Check the accessibility. switch( stmt->get_access() ) { case statement::base::STMT_PRIVATE: return !m_sw.rptpri(); case statement::base::STMT_PROTECTED: return !m_sw.rptpro(); case statement::base::STMT_PUBLIC: return !m_sw.rptpub(); default: break; } return false; } // ================================================================ // Write the package index html. // ================================================================ void ccdoc::phase3::html::make_pkg_index_children(statement::base* stmt, statement::base::stmts_t& outvec) { if( stmt ) { if( stmt->get_type() != statement::base::STMT_NAMESPACE_BEGIN && stmt->get_type() != statement::base::STMT_PACKAGE ) { return; } outvec.push_back(stmt); statement::base::stmts_t& vec = stmt->get_children(); statement::base::stmts_itr_t itr = vec.begin(); for(;itr!=vec.end();++itr) { make_pkg_index_children(*itr,outvec); } } } // ================================================================ // Get the tag value for a statement. // ================================================================ void ccdoc::phase3::html::make_tag_id(const statement::base* stmt, string& id) const { if(stmt) { id = stmt->get_terse_type_name(); id += "-"; id += format_name(stmt->get_id()); if( stmt->get_type() == statement::base::STMT_FUNCTION || stmt->get_type() == statement::base::STMT_FUNCTION_OPERATOR || stmt->get_type() == statement::base::STMT_METHOD_CONSTRUCTOR || stmt->get_type() == statement::base::STMT_METHOD_DESTRUCTOR || stmt->get_type() == statement::base::STMT_METHOD_OPERATOR || stmt->get_type() == statement::base::STMT_METHOD ) { // Append uniquifying tokens: const statement::base::cstrs_t& vec = stmt->get_tokens(); statement::base::cstrs_citr_t itr = vec.begin(); for(;itr!=vec.end();++itr) { id += "-"; id += format_name( *itr ); } } } } // ================================================================ // Get the parent statement that defines a file entity. // ================================================================ const ccdoc::statement::base* ccdoc::phase3::html::get_file_stmt(const statement::base* stmt) const { // Other statements, such as functions are only in separate // files if they are at the top level. Otherwise they are // subordinated to a tag within a file. for(;stmt;stmt=stmt->get_parent()) { if( stmt->get_parent() == 0 ) return stmt; // The only statement that does not have its own file is a // statement that is embedded in a class unless it is a subclass. if( stmt->get_type() == statement::base::STMT_CLASS_BEGIN || stmt->get_type() == statement::base::STMT_STRUCT_BEGIN || stmt->get_type() == statement::base::STMT_UNION_BEGIN ) { // Classes (even subclasses) always have their own files. return stmt; } if( stmt->get_parent()->get_type() != statement::base::STMT_CLASS_BEGIN && stmt->get_parent()->get_type() != statement::base::STMT_STRUCT_BEGIN && stmt->get_parent()->get_type() != statement::base::STMT_UNION_BEGIN ) { // Other statements that are not embedded in classes // always have their own files. return stmt; } } return stmt; } // ================================================================ // Make file URL, strip off the leading directories. // ================================================================ void ccdoc::phase3::html::make_file_url(string& url,string& fn) const { // Strip off the leading directories to // make the relative reference work. string::iterator itr = fn.begin(); string::iterator relpath_itr = fn.begin(); for(;itr!=fn.end();++itr) { if( '/' == *itr ) { relpath_itr = itr; ++relpath_itr; // Issue 0001 FIX } } url = ""; for(;relpath_itr!=fn.end();++relpath_itr) { url += *relpath_itr; } } // ================================================================ // Make file URL // ================================================================ bool ccdoc::phase3::html::make_file_url(string& url, const statement::base* stmt) { if( stmt ) { const statement::base* file_stmt = get_file_stmt(stmt); string fn; make_unique_file_name(fn,file_stmt); make_file_url(url,fn); // If the parent is the file statement, a tag needs to be // generated, return true. return file_stmt == stmt->get_parent(); } return false; } // ================================================================ // Make a unique file name that consistent between runs. // ================================================================ void ccdoc::phase3::html::make_unique_file_name(string& fn, const statement::base* stmt) { if(stmt) { if( stmt->get_parent() == 0 ) { // This is the top node. // Perform special processing here to support the -rootfile // switch. if( m_sw.rootfile().size() ) { fn = m_sw.rootfile(); return; } } bool append_tokens = false; if( stmt->get_type() == statement::base::STMT_FUNCTION || stmt->get_type() == statement::base::STMT_FUNCTION_OPERATOR || stmt->get_type() == statement::base::STMT_METHOD_CONSTRUCTOR || stmt->get_type() == statement::base::STMT_METHOD_DESTRUCTOR || stmt->get_type() == statement::base::STMT_METHOD_OPERATOR || stmt->get_type() == statement::base::STMT_METHOD ) { append_tokens = true; } fn = m_sw.html() + "ccdoc"; // ================================================ // Get the hierarchical names. // ================================================ statement::base::stmts_t parents; stmt->get_parents(parents); if(parents.size()) { statement::base::stmts_itr_t itr = parents.begin(); // Handle the first argument as a special case. // We don't want the '@' sign in the file name. fn += "."; if( m_sw.default_root() == (*itr)->get_id() ) { fn += "root"; // strip of the leading @ for the file name. } else { fn += format_name( (*itr)->get_id() ); } ++itr; for(;itr!=parents.end();++itr) { fn += "."; fn += format_name( (*itr)->get_id() ); } // Tack on the statement id. fn += "."; fn += format_name( stmt->get_id() ); } else { // This is the root package statement. // Handle the first argument as a special case. // We don't want the '@' sign in the file name. fn += "."; if( m_sw.default_root() == stmt->get_id() ) { fn += "root"; // strip of the leading @ for the file name. } else { fn += format_name( stmt->get_id() ); } } string fn1 = fn; // ================================================ // Append the tokens for uniqueness for some types. // ================================================ if( append_tokens ) { const statement::base::cstrs_t& vec = stmt->get_tokens(); statement::base::cstrs_citr_t itr = vec.begin(); for(;itr!=vec.end();++itr) { fn += "."; fn += format_name( *itr ); } } // ================================================ // Append the type and html suffix. // ================================================ fn += "."; fn += stmt->get_terse_type_name(); fn += ".html"; if( m_sw.maxpathlen() && fn.size() > m_sw.maxpathlen() ) { // Generate a checksum if the file name is too big. checksum c; c.set(fn.c_str()); char nbuf[32]; sprintf(nbuf,"%08x",c.get()); fn = fn1; fn += ".checksum."; fn += nbuf; fn += "."; fn += stmt->get_terse_type_name(); fn += ".html"; } } } // ================================================================ // Format the characters in an id so they are suitable for a file // name. // ================================================================ const char* ccdoc::phase3::html::format_name(const char* token) const { static char out[65536]; char nbuf[16]; const char* src = token; char* dst = out; for(;*src;++src) { if( *src <= 32 || *src >= 127 ) { unsigned val = static_cast(*src); sprintf(nbuf,"-%02x",val); for(const char* p=nbuf;*p;++p) *dst++ = *p; } else { if( '{' == *src ) { // This only occurs for enum types and is // not necessary for uniquifying the name. *dst = 0; return out; } switch( *src ) { case '(': case ')': case '[': case ']': case '<': case '>': case ';': case ':': case '~': case '?': case '=': case ',': case '@': case '&': case '*': case '"': case '\'': case '/': // Issue 0018: filter embedded directory separators case '|': // Issue 0112: handle operator | case '\\': { unsigned val = static_cast(*src); sprintf(nbuf,"-%02x",val); for(const char* p=nbuf;*p;++p) *dst++ = *p; break; } default: *dst++ = *src; break; } } } *dst = 0; return out; } // ================================================================ // format string (format for html) // ================================================================ const char* ccdoc::phase3::html::format_string_for_html(const char* pstr) const { static char id[65536]; char* p = id; if(pstr) { for(;*pstr;++pstr) { if( *pstr == '<' ) { *p++ = '&'; *p++ = 'l'; *p++ = 't'; *p++ = ';'; } else if( *pstr == '>' ) { *p++ = '&'; *p++ = 'g'; *p++ = 't'; *p++ = ';'; } else if( *pstr == '&' ) { *p++ = '&'; *p++ = 'a'; *p++ = 'm'; *p++ = 'p'; *p++ = ';'; } else { *p++ = *pstr; } } } *p = 0; return id; } // ================================================================ // format string (format for html) // ================================================================ const char* ccdoc::phase3::html::format_string_for_html(const string& str) const { return format_string_for_html(str.c_str()); } // ================================================================ // write string (format for html) // ================================================================ void ccdoc::phase3::html::write_html_formatted_string(ostream& os, const char* str) const { os << format_string_for_html(str); } // ================================================================ // write string (format for html) // ================================================================ void ccdoc::phase3::html::write_html_formatted_string(ostream& os, const string& str) const { write_html_formatted_string(os,str.c_str()); } // ================================================================ // write common header info // ================================================================ void ccdoc::phase3::html::write_common_header_info(ostream& os, const string& fn, const char* title) { os << "\n" << "\n" << "\n" << "\n" ; if(m_meta.size()) { // Issue 0003: Write out the custom meta variables. os << m_meta << endl; } else { // Issue 0059: // Write out the content type to satisfy the HTML verifier // at http://validator.w3.org/. // Issue 0076: // Don't write out standard HTML meta elements if the -meta // switch was specified. os << "\n"; } write_meta(os,"ccdoc_copyright" ,"(C) Joe Linoff 1998-2001"); write_meta(os,"ccdoc_author" ,"Joe Linoff"); write_meta(os,"ccdoc_version" ,m_sw.version().c_str()); write_meta(os,"ccdoc_file" ,fn.c_str()); write_meta(os,"ccdoc_creation_date" ,date_time()); write_meta(os,"keywords" ,"ccdoc, source code, documentation"); os << "" << title << "\n"; os << "\n" << "\n"; // ================================================ // Write out the prefix, including the // optional prefix that was supplied by the // user. // ================================================ os << "\n"; os << "\n"; // Issue 0002 if(m_header.size()) { os << m_header << endl; } } // ================================================================ // write common header info // ================================================================ void ccdoc::phase3::html::write_common_header_info(ostream& os, const string& fn, statement::base* stmt) { string title = "ccdoc "; if( stmt ) { char nbuf[32]; sprintf(nbuf,"%d",stmt->get_lineno()); title += format_string_for_html(stmt->get_id()); title += " "; title += stmt->get_file(); title += ":"; title += nbuf; // Issue 0010 title += ":"; title += stmt->get_type_name2(); } else { title += ""; } write_common_header_info(os,fn,title.c_str()); // ================================================ // Write out the package/namespace path: // ================================================ if( stmt ) { os << "
\n"; statement::base::stmts_t parents; stmt->get_parents(parents); if( parents.size() ) { statement::base::stmts_itr_t itr = parents.begin(); statement::base* parent = *itr; // If the user specfied -rooturl on the command line, // output it. if( m_sw.rooturl().size() ) { os << "Home\n"; os << " :: "; } // Now write out the path. write_link(os,parent); os << "\n"; itr++; for(;itr!=parents.end();++itr) { parent = *itr; os << " :: "; write_link(os,parent); os << "\n"; } } else { // This is the root. // Write out the Home link. if( m_sw.rooturl().size() ) { os << "Home\n"; } } os << ""; string url; string link_name = m_sw.html() + "ccdoc.class_summary.html"; make_file_url(url,link_name); os << "" << "classes
\n"; } } // ================================================================ // Make the absolute path. // ================================================================ void ccdoc::phase3::html::make_abs_path(statement::base* stmt, string& link) const { int j = 0; statement::base::stmts_t parents; stmt->get_parents(parents); if( parents.size() ) { statement::base::stmts_itr_t itr = parents.begin(); ++itr; // skip the root for(;itr!=parents.end();++itr,++j) { if( j ) link += "::"; link += (*itr)->get_id(); } } if( j ) link += "::"; link += stmt->get_id(); } // ================================================================ // Make relative path. // ================================================================ void ccdoc::phase3::html::make_rel_path(statement::base* parent, statement::base* stmt, string& link) const { int j = 0; statement::base::stmts_t parents; stmt->get_parents(parents); if( parents.size() ) { statement::base::stmts_itr_t itr = parents.begin(); ++itr; // skip the root // Skip until we find the relative top. for(;itr!=parents.end();++itr) { if( *itr == parent ) { // This is the parent, // point to the child. ++itr; break; } } for(;itr!=parents.end();++itr,++j) { if( j ) link += "::"; link += (*itr)->get_id(); } } if( j ) link += "::"; link += stmt->get_id(); } // ================================================================ // Find and write out a links. // ================================================================ unsigned ccdoc::phase3::html::find_and_write_links(ostream& os, const char* in_link, const char* in_link_id, statement::base* scope) { // Nothing is written if there is no valid link. unsigned count = 0; if( in_link && in_link_id ) { string link = in_link; // Check for a leading # as in: // {@link #Fct} if( in_link[0] == '#' ) { // Issue 0092: if( !scope ) return count; scope->get_hier_id_no_pkgs(link); if( link.size() ) link += "::"; link += &in_link[1]; // strip off the leading '#' } statement::base::stmts_t stmts; m_db.get_stmt_no_pkgs(link,stmts); if( stmts.size() ) { statement::base::stmts_t output_stmts; statement::base::stmts_itr_t itr1 = stmts.begin(); for(;itr1!=stmts.end();++itr1) { statement::base* stmt = *itr1; if( stmt->get_type() != statement::base::STMT_CLASS_END && stmt->get_type() != statement::base::STMT_NAMESPACE_END && stmt->get_type() != statement::base::STMT_STRUCT_END && stmt->get_type() != statement::base::STMT_UNION_END ) { if( ignore_contents_stmt(stmt) == false ) output_stmts.push_back(stmt); } } count = output_stmts.size(); write_links(os,output_stmts,in_link_id); } } return count; } // ================================================================ // Write out multiple links to the same reference in a separate // file. // ================================================================ void ccdoc::phase3::html::write_links(ostream& os, const statement::base::stmts_t& stmts, const char* link_name) { if( stmts.size() == 0 || link_name == 0 ) return; if( stmts.size() == 1 ) { write_link(os,stmts[0],link_name); return; } // Issue 0111 // Issue 0083 // First generate the new HTML file name. // Generate a unique file name for the multiple // links file so that they can be re-used. string fn = m_sw.html() + "ccdoc"; const statement::base* stmt = stmts[0]; statement::base::stmts_t parents; stmt->get_parents(parents); if(parents.size()) { statement::base::stmts_itr_t itr = parents.begin(); // Handle the first argument as a special case. // We don't want the '@' sign in the file name. fn += "."; if( m_sw.default_root() == (*itr)->get_id() ) { fn += "root"; // strip of the leading @ for the file name. } else { fn += format_name( (*itr)->get_id() ); } ++itr; for(;itr!=parents.end();++itr) { fn += "."; fn += format_name( (*itr)->get_id() ); } // Tack on the statement id. fn += "."; fn += format_name( stmt->get_id() ); } else { // This is the root package statement. // Handle the first argument as a special case. // We don't want the '@' sign in the file name. fn += "."; if( m_sw.default_root() == stmt->get_id() ) { fn += "root"; // strip of the leading @ for the file name. } else { fn += format_name( stmt->get_id() ); } } fn += ".xrf.html"; // Make the url. string url; make_file_url(url,fn); // Now write out the link to this file. // Use italics as a visual cue to indicate that there are multiple // links. os << "" << link_name << ""; // See whether the file exists. bool exists = true; { ifstream is(fn.c_str()); if(!is) { exists = false; } } // If the link file doesn't exist, create it. if( !exists ) { ofstream os1(fn.c_str()); if(!os) { throw ccdoc::exceptions::unwriteable_output_file(__FILE__, __LINE__, fn.c_str()); } string title; title = "ccdoc xrf links for "; title += link_name; write_common_header_info(os1,fn,title.c_str()); // Populate the file. os1 << "
\n"; os1 << "

Multiply defined links for " << link_name << "

\n"; os1 << ""; os1 << ""; os1 << "" << "" << "" << "" << ""; os1 << "\n"; statement::base::stmts_citr_t itr = stmts.begin(); for(;itr!=stmts.end();++itr) { statement::base* stmt = *itr; // Link os1 << "\n"; } os1 << "
LinkNameTypeSourceShort Description
"; write_link(os1,stmt,link_name); // Name os1 << ""; string id; stmt->get_hier_id_no_pkgs(id); os1 << id; // Type os1 << ""; os1 << stmt->get_type_name2(); // Path os1 << ""; string path; write_ccdoc_src_info(path,stmt); if( path.size() ) os1 << path; // Short description. os1 << "\n"; write_short_desc(os1,stmt); os1 << "
\n"; os1 << "
\n"; write_common_trailer_info(os1); } } // ================================================================ // Write out a link. // ================================================================ void ccdoc::phase3::html::write_link(ostream& os, const statement::base* stmt, const char* link_name) { if( stmt ) { // There are three cases that have to be handled here: // // Case 1: The statement is an object in a standalone file. // Case 2: The statement is a tag in an existing file. // Case 3: A package with a user defined URL. // // Case 2 occurs when the stmt is not a class and the parent is // not a namespace or a package. string url; string tag; if( statement::base::STMT_PACKAGE == stmt->get_type() && stmt->get_comment() ) { // Issue 0025 // The user specified a custom URL. Use the package name // along with the users URL. // The URL is obtained from the associated comment. statement::comment doc(stmt->get_comment()); url = doc.get_pkgdoc_url(); } if( url.size() == 0 ) { if ( make_file_url(url,stmt) ) { string id; make_tag_id(stmt,id); tag = "#" + id; } } // Write out the link. os << ""; if( link_name ) { write_html_formatted_string(os,link_name); } else { write_html_formatted_string(os,stmt->get_id()); } os << ""; } } // ================================================================ // write meta info // ================================================================ void ccdoc::phase3::html::write_meta(ostream& os, const char* name, const char* content) { os << "\n"; } // ================================================================ // write common trailer info // ================================================================ void ccdoc::phase3::html::write_common_trailer_info(ostream& os) { os << "\n" << "
\n"; if(m_trailer.size()) { // The user specified a customized trailer, use it. os << m_trailer << endl; } else { // Output the default message. // Note that the copyright is not displayed here because // I don't own the contents of the page. os << "
\n" << "

\n" << "\n" << "Created " << date_time() << ".\n" << "
\n" << "This documentation was generated automatically by\n" << "
\n" << m_sw.version() << ".\n" << "
\n" << "Click " << "here" << " to submit a bug report or feature request for ccdoc.\n" << "
\n" << "Click here" << " to return to the top of the page.\n" << "
\n" << "

\n" ; } os << "\n" << "\n" ; } // ================================================================ // Write the extends clause for classes and structs. // ================================================================ void ccdoc::phase3::html::write_extends_clause(ostream& os, statement::base* stmt) { if( stmt ) { // Write the extends clause. // extends as bool first = true; string extends_prefix = "    extends "; const statement::base::cstrs_t& vec1 = stmt->get_tokens(); statement::base::cstrs_citr_t itr1 = vec1.begin(); for(;itr1!=vec1.end();++itr1) { string token = *itr1; if( token == ":" ) { itr1++; break; } } if( itr1!=vec1.end() ) { // This is a derived class of the form: // class A : ... { ... }; statement::base::cstrs_citr_t end = itr1; while( end!=vec1.end() ) { string id; statement::base::strs_t access; string token; // ================================================ // Get the type and the access specifiers. // Do this the easy way. // ================================================ bool found_id = false; int depth = 0; for(;end!=vec1.end();++end) { token = *end; if( !found_id ) { if( token == "virtual" || token == "public" || token == "protected" || token == "private" ) { access.push_back(token); continue; } found_id = true; } if( found_id ) { if( token == "<" ) { // This handles cases like: // class A : public foo ; // class A : public foo > ; depth++; } else if( token == ">" ) { depth--; } else if( depth == 0 ) { if( token == "," ) { // Output the extends information. ++end; break; } } id += token; } } // ================================================ // At this point the id and access vector are set. // ================================================ // Write out the extends clause. if( !first ) os << "
"; if( first ) first = false; os << extends_prefix; // ================================================ // Write out the link. // ================================================ statement::base::stmts_t id_class_stmts; load_extend_classes(id,id_class_stmts,stmt); if( id_class_stmts.size() == 0 ) { os << "" << format_string_for_html(id) << ""; // Issue 109: // The error condition for this can be created as follows: // class Test : public Undefined { // public: // Test(); // ~Test(); // }; s_log.warning() << "UNDEF: Undefined reference in extends clause for '" << id << "' in " << stmt->get_type_name2() << " '" << stmt->get_id() << "' at line " << stmt->get_lineno() << " in file " << stmt->get_file() << "\n" << s_log.enable(); } else if( id_class_stmts.size() == 1 ) { write_link(os,id_class_stmts[0],id.c_str()); } else { // There are too many links. // Write all of the links. statement::base::stmts_itr_t sitr = id_class_stmts.begin(); os << "{ "; for(;sitr!=id_class_stmts.end();++sitr) { statement::base* id_stmt = *sitr; if( sitr != id_class_stmts.begin() ) os << ", "; write_link(os,id_stmt,id.c_str()); } os << " }"; } // ================================================ // Write out the "as" clause. // ================================================ os << " as"; if( access.size() ) { statement::base::strs_itr_t titr = access.begin(); for(;titr!=access.end();++titr) { os << " " << *titr; } } else { // 11.2 ISO/IEC 14882:1998(E) if( stmt->get_type() == statement::base::STMT_CLASS_BEGIN ) os << " private"; else if( stmt->get_type() == statement::base::STMT_STRUCT_BEGIN ) os << " public"; else { os << " unknown"; // Issue 109: // TODO: Don't know how to trigger this for testing. s_log.warning() << "UNDEF: Undefined type '" << stmt->get_type_name2() << " for '" << stmt->get_id() << "' at line " << stmt->get_lineno() << " in file " << stmt->get_file() << "\n" << s_log.enable(); } } os << "\n"; } os << "

\n"; } } } // ================================================================ // Write out the friends info // ================================================================ void ccdoc::phase3::html::write_friends_info(ostream& os, statement::base* stmt) { statement::base* scope = stmt; statement::base::stmts_t friends; statement::base::stmts_t& children = stmt->get_children(); statement::base::stmts_itr_t itr = children.begin(); for(;itr!=children.end();++itr) { if( (*itr)->get_type() == statement::base::STMT_FRIEND_CLASS || (*itr)->get_type() == statement::base::STMT_FRIEND_FUNCTION ) { friends.push_back(*itr); } } if( friends.size() ) { if( friends.size() == 1 ) { os << "

Friend:
\n"; } else { os << "
Friends:
\n"; } itr = friends.begin(); for(;itr!=friends.end();++itr) { statement::base* f = *itr; os << "\n"; os << "\n"; os << "\n"; os << "\n"; os << "\n"; } os << "
\n"; if( f->get_type() == statement::base::STMT_FRIEND_CLASS ) os << "class"; else if( f->get_type() == statement::base::STMT_FRIEND_FUNCTION ) os << "function"; os << "\n"; statement::base::stmts_t matches; string id = f->get_id(); m_db.get_stmt_no_pkgs(id,matches); if( matches.size() == 0 ) { // Issue 0064 // The name was not found, try looking in the namespace // of the parent. if(stmt->get_parent()) { stmt->get_parent()->get_hier_id_no_pkgs(id); id += "::"; id += f->get_id(); m_db.get_stmt_no_pkgs(id,matches); } } if( matches.size() == 1 ) { //write_link(os,matches[0],f->get_id()); write_friends_link(os,matches[0],f); } else if( matches.size() > 1 ) { // Filter to match the type. statement::base::stmts_t filter_matches; statement::base::stmts_itr_t mitr = matches.begin(); for(;mitr!=matches.end();++mitr) { if( f->get_type() == statement::base::STMT_FRIEND_CLASS ) { if( (*mitr)->get_type() == statement::base::STMT_CLASS_BEGIN || (*mitr)->get_type() == statement::base::STMT_STRUCT_BEGIN ) { filter_matches.push_back( *mitr ); break; // avoid length searches for now } } else if( f->get_type() == statement::base::STMT_FRIEND_FUNCTION ) { if( (*mitr)->get_type() == statement::base::STMT_FUNCTION || (*mitr)->get_type() == statement::base::STMT_FUNCTION_OPERATOR || (*mitr)->get_type() == statement::base::STMT_METHOD_CONSTRUCTOR || (*mitr)->get_type() == statement::base::STMT_METHOD_DESTRUCTOR || (*mitr)->get_type() == statement::base::STMT_METHOD_OPERATOR || (*mitr)->get_type() == statement::base::STMT_METHOD ) { filter_matches.push_back( *mitr ); break; // avoid length searches for now } } } if( filter_matches.size() ) { //write_link(os,filter_matches[0],f->get_id()); write_friends_link(os,filter_matches[0],f); } else { //write_link(os,matches[0],f->get_id()); write_friends_link(os,matches[0],f); } } else { write_friends_link(os,0,f); // Issue 109: // The error condition for this can be created as follows: // class Foo { friend class Undefined; } s_log.warning() << "UNDEF: Undefined friend reference to '" << f->get_id() << "' in " << stmt->get_type_name2() << " '" << stmt->get_id() << "' at line " << f->get_lineno() << " in file " << f->get_file() << "\n" << s_log.enable(); } os << "\n"; if( f->get_comment() ) { // Special case: // Only the descriptions are written for friends. statement::comment doc(f->get_comment()); write_ccdoc_desc_info(os,doc.get_short_desc(),scope,stmt); if(doc.get_short_desc().size() && doc.get_long_desc().size()) os << "

\n"; write_ccdoc_desc_info(os,doc.get_long_desc(),scope,stmt); } else { os << m_sw.rptdefsd(); } os << "

\n"; os << "
\n"; } } // ================================================================ // Write out the friends link // ================================================================ void ccdoc::phase3::html::write_friends_link(ostream& os, statement::base* link_stmt, statement::base* friend_stmt) { if( link_stmt ) { write_link(os,link_stmt,friend_stmt->get_id()); } else { os << "" << format_string_for_html(friend_stmt->get_id()) << ""; } // Now write out the rest of the statement. const statement::base::cstrs_t& tokens = friend_stmt->get_tokens(); statement::base::cstrs_citr_t itr = tokens.begin(); for(;itr!=tokens.end();++itr) { string token = *itr; if( token == friend_stmt->get_id() ) { ++itr; break; } } for(;itr!=tokens.end();++itr) { string token = *itr; write_code_subsection_token(os,friend_stmt,token); } } // ================================================================ // Write out the ccdoc information. // ================================================================ void ccdoc::phase3::html::write_ccdoc_info(ostream& os, statement::base* stmt, bool author_required, bool version_required, bool inherited) { statement::base* scope = stmt->get_parent(); if( stmt->get_comment() ) { statement::comment doc(stmt->get_comment()); // Short description. write_ccdoc_desc_info(os,doc.get_short_desc(),scope,stmt); if(doc.get_short_desc().size() && doc.get_long_desc().size()) os << "

\n"; // Long description. write_ccdoc_desc_info(os,doc.get_long_desc(),scope,stmt); os << "

\n"; // Inherited from if( inherited && stmt->get_parent() ) write_inherited_from_info(os,stmt); // Source file write_ccdoc_src_info(os,stmt,scope); // Author string id = "Author"; if(doc.get_authors().size()>1) id = "Authors"; if( author_required ) write_ccdoc_directive_info(os,id.c_str(),doc.get_authors(),m_sw.rptdefa(),scope,stmt,true); else write_ccdoc_directive_info(os,id.c_str(),doc.get_authors(),0,scope,stmt,true); // Version if( version_required ) write_ccdoc_directive_info(os,"Version",doc.get_version(),m_sw.rptdefv(),scope,stmt); else write_ccdoc_directive_info(os,"Version",doc.get_version(),0,scope,stmt); // Since // Issue 0082: 10/18/01 bzoe write_ccdoc_directive_info(os,"Since",doc.get_since(),0,scope,stmt); // Deprecated write_ccdoc_directive_info(os,"Deprecated",doc.get_deprecated(),0,scope,stmt,false); // Params write_ccdoc_param_directive_info(os,doc.get_params(),scope,stmt); // Returns write_ccdoc_directive_info(os,"Returns",doc.get_returns(),0,scope,stmt,false); // Exceptions write_ccdoc_exception_directive_info(os,doc.get_exceptions(),scope,stmt); // See write_ccdoc_see_directive_info(os,doc.get_sees(),stmt); // Todo (Issue 0120) write_ccdoc_directive_info(os,"Todo",doc.get_todo(),0,scope,stmt,false); // The following directives are not output in HTML // @pkg // @pkgdoc // @suffix os << "
\n"; } else { // What do we do with entities that do not have a ccdoc // comment? // Write out the source information and the author stuff. statement::base::strs_t empty_vec; string empty_str; if( stmt->get_type() == statement::base::STMT_PACKAGE ) { // Special case handling for packages with no comments. // Packages don't have source. if( m_sw.rptdpd() == true ) { // Report package comments if -rptdpd was specified, otherwise // don't. os << m_sw.rptdefsd(); } if( inherited || author_required || version_required ) { os << "
\n"; if( inherited && stmt->get_parent() ) write_inherited_from_info(os,stmt); if( author_required ) write_ccdoc_directive_info(os,"Author",empty_vec,m_sw.rptdefa(),scope,stmt,true); if( version_required ) write_ccdoc_directive_info(os,"Version",empty_str,m_sw.rptdefv(),scope,stmt); os << "
\n"; } return; } // Tell them that it is not documented. if(stmt->get_lineno() == 0 ) { // Packages never have line numbers. // Lineno zero flags the fact that it // was automatically generated by the // compiler. os << m_sw.rptdefasd(); } else { os << m_sw.rptdefsd(); } os << "
\n"; if( inherited && stmt->get_parent() ) write_inherited_from_info(os,stmt); write_ccdoc_src_info(os,stmt,scope); if( author_required ) write_ccdoc_directive_info(os,"Author",empty_vec,m_sw.rptdefa(),scope,stmt,true); if( version_required ) write_ccdoc_directive_info(os,"Version",empty_str,m_sw.rptdefv(),scope,stmt); os << "
\n"; } } // ================================================================ // Write out the source file information. // ================================================================ void ccdoc::phase3::html::write_ccdoc_src_info(ostream& os, statement::base* stmt, statement::base* scope) { string path; if( write_ccdoc_src_info(path,stmt) ) { write_ccdoc_directive_info(os,"Source",path,0,scope,stmt); } } // ================================================================ // Write out the source file information. // ================================================================ bool ccdoc::phase3::html::write_ccdoc_src_info(string& path, statement::base* stmt) { // Source file if(stmt && stmt->get_type() != statement::base::STMT_PACKAGE ) { // Issue 0027 string file; if(stmt->get_file()) file = stmt->get_file(); else file = "unknown"; if( stmt->get_file() && m_sw.srcurl().size() ) { // Build an anchor reference to the source file. string urlfn = stmt->get_file(); if( m_sw.dospaths() ) { // Convert the DOS backslashes to forward slashes for HTML. replace(urlfn.begin(),urlfn.end(),'\\','/'); } if( urlfn.size() ) { path = "" + file + ""; } } else { path = file; } if( path.size() ) { // Don't append the line numbers of zero. They are meaningless. if( stmt->get_lineno() ) { char nbuf[32]; sprintf(nbuf,"%d",stmt->get_lineno()); // Issue: 0056 path += ":"; path += nbuf; } } return true; } return false; } // ================================================================ // Write out the ccdoc description information. // Dereference the @link and @$ directives. // ================================================================ void ccdoc::phase3::html::write_ccdoc_desc_info(ostream& os, const statement::base::strs_t& vec, statement::base* scope, statement::base* stmt) { statement::base::strs_citr_t itr = vec.begin(); for(;itr!=vec.end();++itr) { write_ccdoc_line_info(os,itr,vec.end(),scope,stmt); } } // ================================================================ // Write out link info in each comment line. // ================================================================ void ccdoc::phase3::html::write_ccdoc_line_info(ostream& os, statement::base::strs_citr_t& itr, statement::base::strs_citr_t itr_end, statement::base* scope, statement::base* stmt) { const string& line = *itr; if( line == "@link" ) { // This is a link line. if( ++itr == itr_end ) { os << line << "\n"; return; } string link = *itr; if( ++itr == itr_end ) { os << line << "\n" << link << "\n"; return; } // Issue 0088. string id = *itr; if( id == link && link[0] == '#' ) { // Issue 0125 id.erase(0,1); // erase the 1st character } if( !find_and_write_links(os,link.c_str(),id.c_str(),scope) ) { os << "" << format_string_for_html(id) << ""; // Issue 109: // The error condition for this can be created as follows: // /** // * See {@link Undefined} for more details. // */ // class Foo { int m_a; }; s_log.warning() << "UNDEF: Undefined {@link ...} reference to '" << id << "' in ccdoc comment at line " << stmt->get_lineno() << " in file " << stmt->get_file() << "\n" << s_log.enable(); } } else if( line == " " ) { // This is how the scanner tells us to insert a // paragraph separator. os << "

\n"; } else { os << line << "\n"; } } // ================================================================ // Write out the ccdoc directive information. // ================================================================ void ccdoc::phase3::html::write_ccdoc_param_directive_info(ostream& os, const statement::base::strss_t& vecvec, statement::base* scope, statement::base* stmt) { if( vecvec.size() == 0 ) { return; } string name = "Param"; if( vecvec.size() > 1 ) { name = "Params"; } os << "

" << name << ":
\n"; statement::base::strss_citr_t i = vecvec.begin(); statement::base::strss_citr_t e = vecvec.end(); for(;i!=e;++i) { const statement::base::strs_t& vec = *i; statement::base::strs_citr_t i1 = vec.begin(); os << "\n"; } os << "
"; write_ccdoc_line_info(os,i1,vec.end(),scope,stmt); os << ""; for(++i1;i1!=vec.end();++i1) { write_ccdoc_line_info(os,i1,vec.end(),scope,stmt); } os << "
\n"; } // ================================================================ // Write out the ccdoc exception directive information. // ================================================================ void ccdoc::phase3::html::write_ccdoc_exception_directive_info(ostream& os, const statement::base::strss_t& vecvec, statement::base* scope, statement::base* stmt) { if( vecvec.size() == 0 ) { return; } string name = "Exception"; if( vecvec.size() > 1 ) { name = "Exceptions"; } os << "
" << name << ":
\n"; statement::base::strss_citr_t i = vecvec.begin(); statement::base::strss_citr_t e = vecvec.end(); for(;i!=e;++i) { const statement::base::strs_t& vec = *i; statement::base::strs_citr_t i1 = vec.begin(); os << "\n"; } os << "
"; // Issue 0087. // Write out the exception as a link, if possible. string id = *i1; if( !find_and_write_links(os,id.c_str(),id.c_str(),scope) ) { os << format_string_for_html(id); } os << ""; // Write out the rest of the exception info. for(++i1;i1!=vec.end();++i1) { write_ccdoc_line_info(os,i1,vec.end(),scope,stmt); } os << "
\n"; } // ================================================================ // Write out the ccdoc directive information. // ================================================================ void ccdoc::phase3::html::write_ccdoc_see_directive_info(ostream& os, const statement::base::strss_t& vecvec, statement::base* stmt) { if( vecvec.size() == 0 ) { return; } string name = "See Also"; os << "
" << name << ":
"; statement::base::strss_citr_t i = vecvec.begin(); statement::base::strss_citr_t e = vecvec.end(); for(int j=0;i!=e;++i,++j) { const statement::base::strs_t& vec = *i; statement::base::strs_citr_t i1 = vec.begin(); string link = *i1; ++i1; string index = *i1; string name = link; // Issue 0019 if(link[0] == '<' ) { // Handle the special case where the user defined // an explicit link. // Just use what they specified. if(j) os << ", "; // Issue 0024 os << link << "\n"; continue; } // Issue 0043. if( link[0] == '#' ) { // The parent defines the scope. const char* ps = link.c_str(); name = &ps[1]; // strip of the leading '#" if( stmt->get_parent() ) stmt->get_parent()->get_hier_id_no_pkgs(link); if( link.size() ) link += "::"; link += name; } statement::base::stmts_t stmts; m_db.get_stmt_no_pkgs(link,stmts); if(index != "*" ) { // Output the indexed reference. unsigned num = atoi(index.c_str()); if( stmts.size() > num ) { statement::base* stmt = stmts[num]; if(j) os << ", "; write_link(os,stmt,name.c_str()); } else { if(j) os << ", "; // Issue 109: // The error condition for this can be created as follows: // /** // * @see Undefined // * @see Undefined 1 // */ // class Foo { int m_a; }; if( stmts.size() == 0 ) { os << "" << format_string_for_html(name) << ""; s_log.warning() << "UNDEF: Undefined @see link to reference '" << name << "' in ccdoc comment at line " << stmt->get_lineno() << " in file " << stmt->get_file() << "\n" << s_log.enable(); } else { if( num > 0 ) { os << "" << format_string_for_html(name) << "[" << num << "]" << ""; } else { os << "" << format_string_for_html(name) << ""; } s_log.warning() << "UNDEF: Undefined @see link to index reference '" << name << "[" << index << "]" << "' in ccdoc comment at line " << stmt->get_lineno() << " in file " << stmt->get_file() << "\n" << s_log.enable(); } } } else { if( stmts.size() ) { // Output all of the references. statement::base::stmts_itr_t itr1 = stmts.begin(); for(;itr1!=stmts.end();++itr1,++j) { statement::base* stmt = *itr1; if(j) os << ", "; write_link(os,stmt,name.c_str()); } } else { if(j) os << ", "; os << "" << format_string_for_html(name) << ""; // Issue 109: // It is not clear that this can be triggered. // I think this is dead code. s_log.warning() << "UNDEF: Undefined @see link to '" << name << "' at line " << stmt->get_lineno() << " in file " << stmt->get_file() << "\n" << s_log.enable(); } } } os << "
\n"; } // ================================================================ // Write out the inherited from info. // ================================================================ void ccdoc::phase3::html::write_inherited_from_info(ostream& os, statement::base* stmt) { os << "
Inherited From:
"; string id; stmt->get_parent()->get_hier_id_no_pkgs(id); write_link(os,stmt->get_parent(),id.c_str()); os << "
\n"; } // ================================================================ // Write out the ccdoc directive information. // ================================================================ void ccdoc::phase3::html::write_ccdoc_directive_info(ostream& os, const char* name, const statement::base::strs_t& vec, const char* default_field, statement::base* scope, statement::base* stmt, bool commas) { if( vec.size() == 0 && default_field == 0 ) { return; } os << "
" << name << ":
"; if( vec.size() == 0 ) { // There are no entries, output the default // value. os << default_field; } else { statement::base::strs_citr_t i = vec.begin(); statement::base::strs_citr_t e = vec.end(); if( i!=e ) { write_ccdoc_line_info(os,i,vec.end(),scope,stmt); } for(++i;i!=e;++i) { if( commas ) // issue 0095 os << ", "; write_ccdoc_line_info(os,i,vec.end(),scope,stmt); } } os << "
\n"; } // ================================================================ // Write out the ccdoc directive information. // ================================================================ void ccdoc::phase3::html::write_ccdoc_directive_info(ostream& os, const char* name, const string& val, const char* default_field, statement::base* scope, statement::base* stmt) { if( val.size() == 0 && default_field == 0 ) { return; } os << "
" << name << ":
"; if( val.size() == 0 ) { // There are no entries, output the default // value. os << default_field; } else { os << val; } os << "
\n"; } // ================================================================ // Date and time. // ================================================================ #include const char* ccdoc::phase3::html::date_time() const { static char tbuf[128]; time_t ltime; ::time(<ime); ::strcpy(tbuf,::ctime(<ime)); // Strip off the new line. char* p = tbuf; const char* e = &tbuf[127]; // detect overflows for(;*p != '\n' && *p != 0 && p < e; *p++); *p = 0; return tbuf; } // ================================================================ // CrcTable // ================================================================ unsigned ccdoc::phase3::html::checksum::m_crc_table[256] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d };