1 //@copyright_begin
2 // ================================================================
3 // Copyright Notice
4 // Copyright (C) 1998-2004 by Joe Linoff
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the
8 // "Software"), to deal in the Software without restriction, including
9 // without limitation the rights to use, copy, modify, merge, publish,
10 // distribute, sublicense, and/or sell copies of the Software, and to
11 // permit persons to whom the Software is furnished to do so, subject to
12 // the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be
15 // included in all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 // IN NO EVENT SHALL JOE LINOFF BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 // OTHER DEALINGS IN THE SOFTWARE.
24 //
25 // Comments and suggestions are always welcome.
26 // Please report bugs to http://ccdoc.sourceforge.net/ccdoc
27 // ================================================================
28 //@copyright_end
29 #if defined(_MSC_VER)
30 #pragma warning ( disable : 4786 4251 )
31 #endif
32 
33 #include <cassert>
34 #include <cstdio>
35 #include <cstring>
36 #include <algorithm>
37 #include <iomanip>
38 #include "exceptions.h"
39 #include "phase3_html.h"
40 
41 // ================================================================
42 // This variable allows the header version
43 // to be queried at runtime.
44 // ================================================================
45 namespace {
46   char ccdoc_rcsid[] = "$Id: phase3_html.cc,v 1.18 2004/09/30 16:09:27 jlinoff Exp $";
47 }
48 
49 // ================================================================
50 // Forward declarations for sort compare functions.
51 // ================================================================
52 namespace
53 {
54   bool compare_stmts_full_paths(const ccdoc::statement::base* a,
55                                 const ccdoc::statement::base* b);
56   bool compare_stmts(const ccdoc::statement::base* a,
57                      const ccdoc::statement::base* b);
58 }
59 // ================================================================
60 // Html
61 // ================================================================
html(switches & sw,database & db)62 ccdoc::phase3::html::html(switches& sw,database& db)
63   : m_debug(false),
64     m_ok(false),
65     m_db(db),
66     m_sw(sw),
67     m_multiple_ref_index(0)
68 {
69 }
70 // ================================================================
71 // Html
72 // ================================================================
~html()73 ccdoc::phase3::html::~html() {
74 }
75 // ================================================================
76 // Run
77 // ================================================================
run()78 bool ccdoc::phase3::html::run()
79 {
80   // Load the customized header and trailer stuff.
81   load(m_sw.header(),m_header);
82   load(m_sw.trailer(),m_trailer);
83   load(m_sw.meta(),m_meta);
84 
85   m_db.clear_path_map();
86   m_db.load_path_map(); // Issue 0039
87 
88   // Issue 0044
89   // Allow the user to control the hierarchical indent level.
90   {
91     m_rptcsi = "";
92     for(int i=m_sw.rptcsi();i>0;--i) {
93       m_rptcsi += "&nbsp;";
94     }
95   }
96 
97   write_ccdoc_pkgs_html();
98   write_ccdoc_namespaces_html();
99   write_ccdoc_classes_html();
100   write_ccdoc_class_summary_html();
101   write_ccdoc_functions_html();
102   write_ccdoc_variables_html();
103   write_ccdoc_enums_html();
104   write_ccdoc_typedefs_html();
105   write_ccdoc_macros_html();
106 
107   m_ok = true;
108   return m_ok;
109 }
110 // ================================================================
111 // Write the package files.
112 // Each package file contains the following:
113 //   - The ccdoc description section.
114 //   - The table of contents: child packages and namespaces.
115 // ================================================================
write_ccdoc_pkgs_html()116 void ccdoc::phase3::html::write_ccdoc_pkgs_html()
117 {
118   statement::base::stmts_t stmts;
119   m_db.load(stmts,statement::base::STMT_PACKAGE);
120   if( m_sw.verbose() ) {
121     s_log << "phase3: generating HTML for "
122 	  << stmts.size()
123 	  << " packages ...\n";
124   }
125 
126   statement::base::stmts_itr_t itr = stmts.begin();
127   for(;itr!=stmts.end();++itr) {
128     string fn;
129     statement::base* stmt = *itr;
130     make_unique_file_name(fn,stmt);
131     ofstream os(fn.c_str());
132     if(!os) {
133       throw ccdoc::exceptions::unwriteable_output_file(__FILE__,
134 						       __LINE__,
135 						       fn.c_str());
136     }
137     if( m_sw.verbose() ) {
138       string hier_id;
139       stmt->get_hier_id(hier_id);
140       s_log << "phase3: generating HTML for package "
141 	    << hier_id
142 	    << " ...\n";
143     }
144     write_common_header_info(os,fn,stmt);
145     write_section_header(os,stmt,true);
146     write_indent(os);
147     write_ccdoc_info(os,stmt,m_sw.rptdpa(),m_sw.rptdpv());
148     write_unindent(os);
149     write_contents(os,stmt);
150     write_common_trailer_info(os);
151   }
152 }
153 // ================================================================
154 // write the namespace files.
155 // Each namespace file contains the following:
156 //   - The ccdoc description section.
157 //   - The table of contents: child namespaces.
158 // ================================================================
write_ccdoc_namespaces_html()159 void ccdoc::phase3::html::write_ccdoc_namespaces_html()
160 {
161   statement::base::stmts_t stmts;
162   m_db.load(stmts,statement::base::STMT_NAMESPACE_BEGIN);
163   if( m_sw.verbose() ) {
164     s_log << "phase3: generating HTML for "
165 	  << stmts.size()
166 	  << " namespaces ...\n";
167   }
168   statement::base::stmts_itr_t itr = stmts.begin();
169   for(;itr!=stmts.end();++itr) {
170     string fn;
171     statement::base* stmt = *itr;
172 
173     // Skip the internal ids of the form:
174     //   +<name>+<file>:<line>
175     // that are used store the comments
176     // until they can be extracted here
177     // by chaining using the get_next()
178     // method that is set in phase3.
179     // They are artifacts.
180     if( ignore_contents_stmt(stmt) ) {
181       if( m_sw.verbose() ) {
182         string hier_id;
183         stmt->get_hier_id(hier_id);
184         s_log << "phase3: skipping HTML for internal namespace "
185               << hier_id
186               << " ...\n";
187       }
188       continue;
189     }
190 
191     make_unique_file_name(fn,stmt);
192     ofstream os(fn.c_str());
193     if(!os) {
194       throw ccdoc::exceptions::unwriteable_output_file(__FILE__,
195 						       __LINE__,
196 						       fn.c_str());
197     }
198     if( m_sw.verbose() ) {
199       string hier_id;
200       stmt->get_hier_id(hier_id);
201       s_log << "phase3: generating HTML for namespace "
202 	    << hier_id
203 	    << " ...\n";
204     }
205     write_common_header_info(os,fn,stmt);
206     write_section_header(os,stmt,true);
207     write_indent(os);
208     write_ccdoc_info(os,stmt,true,true);
209 
210     // Issue 0133
211     // Special case handling for comment chains.
212     // These occur in namespaces where multiple sets
213     // of comments are chained together. These are
214     // handled by walking through the chains and
215     // output the comments in an indented form with
216     // a separator for each one.
217     if( stmt->get_next() ) {
218       statement::base* chain = stmt->get_next();
219       for(;chain;chain = chain->get_next()) {
220         if( !m_sw.rptcfuns() && !chain->get_comment() ) {
221           // Don't report namespaces with empty comments.
222           continue;
223         }
224         os << "<table border=1 width=\"90%\">\n";
225         os << "<tr>\n";
226         os << "<td>\n";
227         os << "<b>"
228            << chain->get_file() << ":"
229            << chain->get_lineno() << "</b>\n";
230         os << "<blockquote>\n";
231         write_ccdoc_info(os,chain,true,true);
232         os << "</blockquote>\n";
233         os << "</td>\n";
234         os << "</tr>\n";
235         os << "</table>\n";
236       }
237     }
238 
239     write_unindent(os);
240     write_contents(os,stmt);
241     write_common_trailer_info(os);
242   }
243 }
244 // ================================================================
245 // write the class files.
246 // ================================================================
write_ccdoc_classes_html()247 void ccdoc::phase3::html::write_ccdoc_classes_html()
248 {
249   statement::base::stmts_t stmts;
250   m_db.load(stmts,statement::base::STMT_CLASS_BEGIN);
251   m_db.load(stmts,statement::base::STMT_STRUCT_BEGIN);
252   if( m_sw.rptun() ) {
253     m_db.load(stmts,statement::base::STMT_UNION_BEGIN);
254   }
255   statement::base::stmts_itr_t itr = stmts.begin();
256   if( m_sw.verbose() ) {
257     s_log << "phase3: generating HTML for "
258 	  << stmts.size()
259 	  << " classes ...\n";
260   }
261   for(;itr!=stmts.end();++itr) {
262     string fn;
263     statement::base* stmt = *itr;
264     make_unique_file_name(fn,stmt);
265     ofstream os(fn.c_str());
266     if(!os) {
267       throw ccdoc::exceptions::unwriteable_output_file(__FILE__,
268 						       __LINE__,
269 						       fn.c_str());
270     }
271     if( m_sw.verbose() ) {
272       string hier_id;
273       stmt->get_hier_id(hier_id);
274       s_log << "phase3: generating HTML for class "
275 	    << hier_id
276 	    << " ...\n";
277     }
278     write_common_header_info(os,fn,stmt);
279     write_section_header(os,stmt,true);
280     write_extends_clause(os,stmt);
281     write_indent(os);
282     write_ccdoc_info(os,stmt,true,true);
283     write_friends_info(os,stmt);
284     write_unindent(os);
285     write_contents(os,stmt,m_sw.rptsci());
286     write_class_details(os,stmt,m_sw.rptsci());
287     write_common_trailer_info(os);
288   }
289 }
290 // ================================================================
291 // Generating the class summary.
292 // ================================================================
write_ccdoc_class_summary_html()293 void ccdoc::phase3::html::write_ccdoc_class_summary_html()
294 {
295   statement::base::stmts_t stmts;
296   m_db.load(stmts,statement::base::STMT_CLASS_BEGIN);
297   m_db.load(stmts,statement::base::STMT_STRUCT_BEGIN);
298   if( m_sw.verbose() ) {
299     s_log << "phase3: generating HTML for class summary\n";
300   }
301 
302   // Open the HTML file.
303   string fn;
304   fn = m_sw.html() + "ccdoc.class_summary.html";
305   ofstream os(fn.c_str());
306   if(!os) {
307     throw ccdoc::exceptions::unwriteable_output_file(__FILE__,
308 						     __LINE__,
309 						     fn.c_str());
310   }
311   write_common_header_info(os,fn,"Class Summary");
312   os << "<table border=0 width=\"100%\"><tr><td align=left>";
313   if( m_sw.rooturl().size() ) {
314     os << "<a href=\"" << m_sw.rooturl() << "\" target=_top>Home</a>";
315     os << " :: ";
316   }
317   write_link(os,m_db.root());
318   os << "</td><td align=right>";
319   string url;
320   string link_name = m_sw.html() + "ccdoc.class_summary.html";
321   make_file_url(url,link_name);
322   os << "<a href=\"" << url << "\">classes</a></td></tr></table>\n";
323 
324   write_section_header(os,"Class Summary",false);
325   write_summary_tree(os,stmts,m_sw.rptcsd(),0,m_rptcsi.c_str());
326   write_common_trailer_info(os);
327 }
328 // ================================================================
329 // Generating the class summary.
330 // ================================================================
write_summary_tree(ostream & os,statement::base::stmts_t & stmts,bool rpthpc,statement::base * start,const char * in_indent,bool sort_flag)331 void ccdoc::phase3::html::write_summary_tree(ostream& os,
332 					     statement::base::stmts_t& stmts,
333 					     bool rpthpc,
334 					     statement::base* start,
335 					     const char* in_indent,
336 					     bool sort_flag)
337 {
338   // ================================================
339   // Order the entries.
340   // ================================================
341   if( sort_flag ) {
342     stable_sort(stmts.begin(),stmts.end(),compare_stmts_full_paths);
343   }
344 
345   // ================================================
346   // Initialize the tags.
347   // These are used later to identify when the parents
348   // have been visited.
349   // ================================================
350   statement::base::stmts_itr_t itr = stmts.begin();
351   for(;itr!=stmts.end();++itr) {
352     statement::base* stmt = *itr;
353     stmt->set_tag(0);
354     statement::base::stmts_t parents;
355     stmt->get_parents(parents);
356     statement::base::stmts_itr_t pitr = parents.begin();
357     for(;pitr!=parents.end();++pitr) {
358       statement::base* parent = *pitr;
359       parent->set_tag(0);
360     }
361   }
362 
363   // ================================================
364   // Output the information.
365   // ================================================
366   if( stmts.size() == 0 )
367     return;
368   if( rpthpc ) {
369     // Issue 0026
370     os << "<table border=0>\n"
371        << "<tr>"
372        << "<th valign=bottom align=left>Entity</th>"
373        << "<th valign=bottom align=left>Type</th>"
374        << "<th valign=bottom align=left>Scope</th>";
375     if( m_sw.rptsrc() ) {
376       os << "<th valign=bottom align=left>Source</th>";
377     }
378     os << "<th valign=bottom align=left>Short Description</th>"
379        << "</tr>\n";
380   }
381   else {
382     os << "<table border=0 cellpadding=0 cellspacing=0>\n";
383   }
384   itr = stmts.begin();
385   for(;itr!=stmts.end();++itr) {
386     statement::base* stmt = *itr;
387     if( stmt->get_tag() == 0 ) {
388       string indent = "";
389       statement::base::stmts_t parents;
390       stmt->get_parents(parents);
391       statement::base::stmts_itr_t pitr = parents.begin();
392       if( start ) {
393 	// The start parent was specified,
394 	// skip ahead until a match is found.
395 	for(;pitr!=parents.end();++pitr) {
396 	  statement::base* parent = *pitr;
397 	  if( parent == start ) {
398 	    ++pitr;
399 	    break;
400 	  }
401 	}
402       }
403       for(;pitr!=parents.end();++pitr) {
404 	statement::base* parent = *pitr;
405 	if( parent->get_tag() == 0 ) {
406 	  parent->set_tag(1);
407 	  pitr = parents.begin();
408 	  write_summary_tree_entry(os,parent,indent.c_str(),rpthpc);
409 	  indent = "";
410 	}
411 	indent += in_indent;
412       }
413       stmt->set_tag(1);
414       write_summary_tree_entry(os,stmt,indent.c_str(),rpthpc);
415     }
416   }
417   os << "</table>\n";
418 }
419 // ================================================================
420 // Generating the class summary.
421 // ================================================================
write_summary_tree_entry(ostream & os,statement::base * stmt,const char * indent,bool rpthpc)422 void ccdoc::phase3::html::write_summary_tree_entry(ostream& os,
423 						   statement::base* stmt,
424 						   const char* indent,
425 						   bool rpthpc)
426 {
427   os << "<tr>";
428 
429   // Id
430   os << "<td align=left valign=top>";
431   if(indent)
432     os << indent;
433   if( stmt ) {
434     string id = stmt->get_id();
435     unsigned max_entity_size = m_sw.rptmlcei() + m_sw.rptmlcifi();
436     if( max_entity_size && id.size() > max_entity_size ) {
437       // Issue 0159:
438       // Truncate the id to the shorter number of characters
439       // and add the '..'.
440       string short_id = id.substr(0,max_entity_size);
441       short_id += "..";
442       write_link(os,stmt,short_id.c_str());
443     }
444     else {
445       write_link(os,stmt,id.c_str());
446     }
447   }
448   os << "</td>";
449 
450   // Type
451   if( rpthpc ) {
452     // Issue 0026
453 
454     // Type
455     string type = stmt->get_type_name2();
456     if( stmt->get_type() == statement::base::STMT_PACKAGE &&
457 	stmt->get_comment() ) {
458       statement::comment doc(stmt->get_comment());
459       string tid = doc.get_pkgdoc_tid();
460       if( tid.size() )
461 	type = tid;
462     }
463     os << "<td align=left valign=top>"
464        << type
465        << "&nbsp;</td>";
466 
467     // Access
468     os << "<td align=left valign=top>";
469     os << stmt->get_access_name();
470     os << "</td>";
471 
472     // Source info
473     if( m_sw.rptsrc() ) {
474       os << "<td align=left valign=top>";
475       os << stmt->get_file() << ":" << stmt->get_lineno();
476       os << "</td>";
477     }
478 
479     // Short description
480     os << "<td align=left valign=top>";
481     write_short_desc(os,stmt);
482     os << "</td>";
483   }
484   os << "</tr>\n";
485 }
486 // ================================================================
487 // Generating the class summary.
488 // ================================================================
write_short_desc(ostream & os,statement::base * stmt)489 void ccdoc::phase3::html::write_short_desc(ostream& os,
490 					   statement::base* stmt)
491 {
492   if( stmt->get_comment() ) {
493     statement::comment doc(stmt->get_comment());
494     if( doc.get_short_desc().size() == 0 ) {
495       // Issue 0030
496       if( stmt->get_type() != statement::base::STMT_PACKAGE ||
497 	  m_sw.rptdpd() == false ) {
498 	os << m_sw.rptdefsd();
499       }
500     }
501     else {
502       statement::base* scope = stmt->get_parent();
503       write_ccdoc_desc_info(os,doc.get_short_desc(),scope,stmt);
504     }
505   }
506   else {
507     if( stmt->get_lineno() == 0 ) {
508       // Lineno zero flags the fact that it
509       // was automatically generated by the
510       // compiler.
511       os << m_sw.rptdefasd();
512     }
513     else {
514       // Issue 0030
515       if( stmt->get_type() != statement::base::STMT_PACKAGE ||
516 	  m_sw.rptdpd() == false ) {
517 	os << m_sw.rptdefsd();
518       }
519     }
520   }
521 }
522 // ================================================================
523 // write the function files.
524 // ================================================================
write_ccdoc_functions_html()525 void ccdoc::phase3::html::write_ccdoc_functions_html()
526 {
527   statement::base::stmts_t stmts;
528   m_db.load_top(stmts,statement::base::STMT_FUNCTION);
529   m_db.load_top(stmts,statement::base::STMT_FUNCTION_OPERATOR);
530   if( m_sw.verbose() ) {
531     s_log << "phase3: generating HTML for "
532 	  << stmts.size()
533 	  << " global functions ...\n";
534   }
535   statement::base::stmts_itr_t itr = stmts.begin();
536   for(;itr!=stmts.end();++itr) {
537     string fn;
538     statement::base* stmt = *itr;
539     make_unique_file_name(fn,stmt);
540     ofstream os(fn.c_str());
541     if(!os) {
542       throw ccdoc::exceptions::unwriteable_output_file(__FILE__,
543 						       __LINE__,
544 						       fn.c_str());
545     }
546     if( m_sw.verbose() ) {
547       string hier_id;
548       stmt->get_hier_id(hier_id);
549       s_log << "phase3: generating HTML for global function "
550 	    << hier_id
551 	    << " ...\n";
552     }
553     write_common_header_info(os,fn,stmt);
554     write_section_header(os,stmt,false);
555     write_indent(os);
556     write_ccdoc_info(os,stmt,true,true);
557     write_code_section(os,stmt);
558     write_unindent(os);
559     write_common_trailer_info(os);
560   }
561 }
562 // ================================================================
563 // write the variable files.
564 // ================================================================
write_ccdoc_variables_html()565 void ccdoc::phase3::html::write_ccdoc_variables_html()
566 {
567   statement::base::stmts_t stmts;
568   m_db.load_top(stmts,statement::base::STMT_ATTRIBUTE);
569   m_db.load_top(stmts,statement::base::STMT_ATTRIBUTE_FUNCTION);
570   m_db.load_top(stmts,statement::base::STMT_VARIABLE);
571   m_db.load_top(stmts,statement::base::STMT_VARIABLE_FUNCTION);
572   statement::base::stmts_itr_t itr = stmts.begin();
573   if( m_sw.verbose() ) {
574     s_log << "phase3: generating HTML for "
575 	  << stmts.size()
576 	  << " global variables ...\n";
577   }
578   for(;itr!=stmts.end();++itr) {
579     string fn;
580     statement::base* stmt = *itr;
581     make_unique_file_name(fn,stmt);
582     ofstream os(fn.c_str());
583     if(!os) {
584       throw ccdoc::exceptions::unwriteable_output_file(__FILE__,
585 						       __LINE__,
586 						       fn.c_str());
587     }
588     if( m_sw.verbose() ) {
589       string hier_id;
590       stmt->get_hier_id(hier_id);
591       s_log << "phase3: generating HTML for global variable "
592 	    << hier_id
593 	    << " ...\n";
594     }
595     write_common_header_info(os,fn,stmt);
596     write_section_header(os,stmt,false);
597     write_indent(os);
598     write_ccdoc_info(os,stmt,true,true);
599     write_code_section(os,stmt);
600     write_unindent(os);
601     write_common_trailer_info(os);
602   }
603 }
604 // ================================================================
605 // write the enum files.
606 // ================================================================
write_ccdoc_enums_html()607 void ccdoc::phase3::html::write_ccdoc_enums_html()
608 {
609   statement::base::stmts_t stmts;
610   m_db.load_top(stmts,statement::base::STMT_ENUM);
611   if( m_sw.verbose() ) {
612     s_log << "phase3: generating HTML for "
613 	  << stmts.size()
614 	  << " global enums ...\n";
615   }
616   statement::base::stmts_itr_t itr = stmts.begin();
617   for(;itr!=stmts.end();++itr) {
618     string fn;
619     statement::base* stmt = *itr;
620     make_unique_file_name(fn,stmt);
621     ofstream os(fn.c_str());
622     if(!os) {
623       throw ccdoc::exceptions::unwriteable_output_file(__FILE__,
624 						       __LINE__,
625 						       fn.c_str());
626     }
627     if( m_sw.verbose() ) {
628       string hier_id;
629       stmt->get_hier_id(hier_id);
630       s_log << "phase3: generating HTML for global enum "
631 	    << hier_id
632 	    << " ...\n";
633     }
634     write_common_header_info(os,fn,stmt);
635     write_section_header(os,stmt,false);
636     write_indent(os);
637     write_ccdoc_info(os,stmt,true,true);
638     write_code_section(os,stmt);
639     write_unindent(os);
640     write_common_trailer_info(os);
641   }
642 }
643 // ================================================================
644 // write the typedef files.
645 // ================================================================
write_ccdoc_typedefs_html()646 void ccdoc::phase3::html::write_ccdoc_typedefs_html()
647 {
648   if( m_sw.rpttyp() == false )
649     return;
650   statement::base::stmts_t stmts;
651   m_db.load_top(stmts,statement::base::STMT_TYPEDEF_FUNCTION);
652   m_db.load_top(stmts,statement::base::STMT_TYPEDEF_VARIABLE);
653   if( m_sw.verbose() ) {
654     s_log << "phase3: generating HTML for "
655 	  << stmts.size()
656 	  << " global typedefs ...\n";
657   }
658   statement::base::stmts_itr_t itr = stmts.begin();
659   for(;itr!=stmts.end();++itr) {
660     string fn;
661     statement::base* stmt = *itr;
662     make_unique_file_name(fn,stmt);
663     ofstream os(fn.c_str());
664     if(!os) {
665       throw ccdoc::exceptions::unwriteable_output_file(__FILE__,
666 						       __LINE__,
667 						       fn.c_str());
668     }
669     if( m_sw.verbose() ) {
670       string hier_id;
671       stmt->get_hier_id(hier_id);
672       s_log << "phase3: generating HTML for global typedef "
673 	    << hier_id
674 	    << " ...\n";
675     }
676     write_common_header_info(os,fn,stmt);
677     write_section_header(os,stmt,false);
678     write_indent(os);
679     write_ccdoc_info(os,stmt,true,true);
680     write_code_section(os,stmt);
681     write_unindent(os);
682     write_common_trailer_info(os);
683   }
684 }
685 // ================================================================
686 // write the macro files.
687 // ================================================================
write_ccdoc_macros_html()688 void ccdoc::phase3::html::write_ccdoc_macros_html()
689 {
690   if( !m_sw.rptmac() )
691     return;
692   statement::base::stmts_t stmts;
693   m_db.load_top(stmts,statement::base::STMT_MACRODEF_0_0);
694   m_db.load_top(stmts,statement::base::STMT_MACRODEF_0_1);
695   m_db.load_top(stmts,statement::base::STMT_MACRODEF_0_N);
696   m_db.load_top(stmts,statement::base::STMT_MACRODEF_N_N);
697   if( m_sw.verbose() ) {
698     s_log << "phase3: generating HTML for "
699 	  << stmts.size()
700 	  << " macro definitions ...\n";
701   }
702   statement::base::stmts_itr_t itr = stmts.begin();
703   for(;itr!=stmts.end();++itr) {
704     string fn;
705     statement::base* stmt = *itr;
706     make_unique_file_name(fn,stmt);
707     ofstream os(fn.c_str());
708     if(!os) {
709       throw ccdoc::exceptions::unwriteable_output_file(__FILE__,
710 						       __LINE__,
711 						       fn.c_str());
712     }
713     if( m_sw.verbose() ) {
714       string hier_id;
715       stmt->get_hier_id(hier_id);
716       s_log << "phase3: generating HTML for macro "
717 	    << hier_id
718 	    << " ...\n";
719     }
720     write_common_header_info(os,fn,stmt);
721     write_section_header(os,stmt,false);
722     write_indent(os);
723     write_ccdoc_info(os,stmt,true,true);
724     write_code_section(os,stmt);
725     write_unindent(os);
726     write_common_trailer_info(os);
727   }
728 }
729 // ================================================================
730 // Write the class details.
731 // ================================================================
write_class_details(ostream & os,statement::base * stmt,bool sort_flag)732 void ccdoc::phase3::html::write_class_details(ostream& os,
733 					      statement::base* stmt,
734 					      bool sort_flag)
735 {
736   if( stmt ) {
737     statement::base::stmts_t contents;
738     make_contents(stmt,contents,sort_flag);
739     string prev_tag;
740     string next_tag;
741     statement::base::stmts_itr_t next_itr;
742     statement::base::stmts_itr_t itr = contents.begin();
743     for(;itr!=contents.end();++itr) {
744       statement::base* child = *itr;
745       string tag;
746       make_tag_id(child,tag);
747       os << "<a name=\"" << tag << "\"></a>\n";
748 
749       // Get the next tag.
750       next_itr = itr;
751       ++next_itr;
752       if( next_itr != contents.end() ) {
753 	make_tag_id(*next_itr,next_tag);
754       }
755       write_section_header(os,
756 			   child,
757 			   true,
758 			   prev_tag.c_str(),
759 			   next_tag.c_str());
760       write_indent(os);
761       if( child->get_parent() == stmt ) {
762 	write_ccdoc_info(os,child,false,false,false);
763       }
764       else {
765 	write_ccdoc_info(os,child,false,false,true);
766       }
767       write_code_section(os,child);
768       write_unindent(os);
769       prev_tag = tag;
770     }
771   }
772 }
773 // ================================================================
774 // write contents
775 // ================================================================
write_contents(ostream & os,statement::base * stmt,bool sort_flag)776 void ccdoc::phase3::html::write_contents(ostream& os,
777 					 statement::base* stmt,
778 					 bool sort_flag)
779 {
780   bool inherited_from_col = false;
781   if( m_sw.rptim() ) {
782     if( stmt->get_type() == statement::base::STMT_CLASS_BEGIN ||
783 	stmt->get_type() == statement::base::STMT_STRUCT_BEGIN ) {
784       // Don't create the inherited from column unless this
785       // class is derived.
786       int depth = 0;
787       const statement::base::cstrs_t& tokens = stmt->get_tokens();
788       statement::base::cstrs_citr_t itr = tokens.begin();
789       for(;itr!=tokens.end();++itr) {
790 	string token = *itr;
791 	if( token == "<" ) {
792 	  depth++;
793 	}
794 	else if( token == ">" ) {
795 	  depth--;
796 	}
797 	else if( depth == 0 && token == ":" ) {
798 	  inherited_from_col = true;
799 	  break;
800 	}
801       }
802     }
803   }
804 
805   if( m_sw.rpthpc() && stmt->get_type() == statement::base::STMT_PACKAGE ) {
806     os << "<a name=\"ccdoc_contents\"></a>\n";
807     write_section_header(os,"Contents",false);
808     write_indent(os);
809 
810     statement::base::stmts_t unfiltered_contents;
811     statement::base::stmts_t filtered_contents;
812     stmt->get_all_children(unfiltered_contents);
813     statement::base::stmts_itr_t itr = unfiltered_contents.begin();
814     for(;itr!=unfiltered_contents.end();++itr) {
815       statement::base* child = *itr;
816       if(ignore_contents_stmt(child))
817 	continue;
818       if( child->get_type() == statement::base::STMT_PACKAGE ) {
819 	// Always report the nested packages.
820 	filtered_contents.push_back(child);
821       }
822       else {
823 	// Issue 0034
824 	if( child->get_parent() == stmt )
825 	  filtered_contents.push_back(child);
826       }
827     }
828     // Issue 0047: -rptcsi also controls the contents column.
829     write_summary_tree(os,filtered_contents,true,stmt,m_rptcsi.c_str());
830     write_unindent(os);
831     return;
832   }
833   // ================================================
834   // This is the standard contents table.
835   // ================================================
836   statement::base::stmts_t contents;
837   make_contents(stmt,contents,sort_flag);
838   if( contents.size() ) {
839     os << "<a name=\"ccdoc_contents\"></a>\n";
840     write_section_header(os,"Contents",false);
841     write_indent(os);
842 
843     os << "<table cellspacing=4>\n"
844        << "<tr>\n"
845        << "<th valign=bottom align=left>Entity</th>"; // Issue 0038
846 
847     unsigned max_entity_size = m_sw.rptmlcei() + m_sw.rptmlcifi();
848     if( inherited_from_col ) {
849       os << "<th valign=bottom align=left>Inherited From</th>"; // Issue 0037
850       max_entity_size = m_sw.rptmlcei();
851     }
852 
853     os << "<th valign=bottom align=left>Type</th>"
854        << "<th valign=bottom align=left>Scope</th>";
855 
856     if( m_sw.rptsrc() ) {
857        os << "<th valign=bottom align=left>Source</th>";
858     }
859 
860     os << "<th valign=bottom align=left>Short Description</th>"
861        << "</tr>\n";
862 
863     statement::base::stmts_itr_t itr = contents.begin();
864     for(;itr!=contents.end();++itr) {
865       statement::base* child = *itr;
866       os << "<tr>\n";
867 
868       // Write the id.
869       os << "<td align=left valign=top>";
870       if( child->get_type() == statement::base::STMT_PACKAGE ||
871 	  child->get_type() == statement::base::STMT_NAMESPACE_BEGIN ) {
872 	string link;
873 	if( stmt->get_parent() )
874 	  make_rel_path(stmt,child,link);
875 	else
876 	  make_abs_path(child,link);
877 	write_link(os,child,link.c_str());
878       }
879       else {
880 	if( child->get_parent() == stmt ) {
881           string id = child->get_id();
882           if( max_entity_size && id.size() > max_entity_size ) {
883             // Issue 0159:
884             // Truncate the id to the shorter number of characters
885             // and add the '..'.
886             string short_id = id.substr(0,max_entity_size);
887             short_id += "..";
888             write_link(os,child,short_id.c_str());
889           }
890           else {
891             write_link(os,child,id.c_str());
892           }
893 	}
894 	else {
895 	  // These are children from other parents.
896 	  // They are created when derived classes
897 	  // inherit methods.
898 	  string url;
899 	  string id;
900 	  make_file_url(url,stmt);
901 	  make_tag_id(child,id);
902 	  //write_link(os,child->get_parent());
903 	  //os << " :: ";
904 	  os << "<a href=\"" << url << "#" << id << "\">";
905           id = child->get_id();
906           if( max_entity_size && id.size() > max_entity_size ) {
907             // Issue 0159:
908             // Truncate the id to the shorter number of characters
909             // and add the '..'.
910             string short_id = id.substr(0,max_entity_size);
911             short_id += "..";
912             write_html_formatted_string(os,short_id.c_str());
913           }
914           else {
915             write_html_formatted_string(os,id.c_str());
916           }
917 	  os << "</a>";
918 	}
919       }
920       os << "</td>";
921 
922       if( inherited_from_col ) {
923 	os << "<td align=left valign=top>";
924 	if( child->get_parent() && child->get_parent() != stmt ) {
925 	  string id;
926 	  child->get_parent()->get_hier_id_no_pkgs(id);
927           if( m_sw.rptmlcifi() && id.size() > m_sw.rptmlcifi() ) {
928             // Issue 0159:
929             // Truncate the id to the shorter number of characters
930             // and add the '..'.
931             string short_id = id.substr(0,m_sw.rptmlcifi());
932             short_id += "..";
933             write_link(os,child->get_parent(),short_id.c_str());
934           }
935           else {
936             write_link(os,child->get_parent(),id.c_str());
937           }
938 	}
939 	os << "</td>";
940       }
941 
942       // Type
943       string type = child->get_type_name2(); // include static
944       if( child->get_type() == statement::base::STMT_PACKAGE &&
945 	  child->get_comment() ) {
946 	statement::comment doc(child->get_comment());
947 	string tid = doc.get_pkgdoc_tid();
948 	if( tid.size() )
949 	  type = tid;
950       }
951       os << "<td align=left valign=top>"
952 	 << type
953 	 << "&nbsp;</td>";
954 
955       // Scope/Access
956       os << "<td align=left valign=top>";
957       os << child->get_access_name();
958       os << "</td>";
959 
960       // Source info
961       if( m_sw.rptsrc() ) {
962         os << "<td align=left valign=top>";
963         os << child->get_file() << ":" << child->get_lineno();
964         os << "</td>";
965       }
966 
967       // Short description
968       os << "<td align=left valign=top>";
969       write_short_desc(os,child);
970       os << "</td>";
971 
972       os << "</tr>\n";
973     }
974     os << "</table>\n";
975   }
976   write_unindent(os);
977 }
978 // ================================================================
979 // Indent.
980 // ================================================================
write_indent(ostream & os) const981 void ccdoc::phase3::html::write_indent(ostream& os) const
982 {
983 #if 0
984   // <blockquote> uses fewer characters
985   os << "<table><tr><td>&nsp;&nsp;&nsp;&nsp;</td><td>\n";
986 #endif
987   os << "<blockquote>\n";
988 }
989 // ================================================================
990 // Unindent.
991 // ================================================================
write_unindent(ostream & os) const992 void ccdoc::phase3::html::write_unindent(ostream& os) const
993 {
994 #if 0
995   os << "</td></table>\n";
996 #endif
997   os << "</blockquote>\n";
998 }
999 // ================================================================
1000 // Section header.
1001 // ================================================================
write_section_header(ostream & os,const char * title,bool contents,const char * prev,const char * next)1002 void ccdoc::phase3::html::write_section_header(ostream& os,
1003 					       const char* title,
1004 					       bool contents,
1005 					       const char* prev,
1006 					       const char* next)
1007 {
1008   os << "<hr>"
1009      << "<table width=\"100%\" border=0 cellpadding=0 cellspacing=0>"
1010      << "<tr>"
1011      << "<td align=left valign=top>"
1012      << "<h2>" << title << "</h2>"
1013      << "</td>"
1014      << "<td align=right valign=top>";
1015   if( contents ) {
1016     os << "<a href=\"#ccdoc_contents\">?</a>&nbsp;";
1017   }
1018   os << "<a href=\"#ccdoc_top\">^</a>&nbsp;\n";
1019   if( prev && *prev ) {
1020     os << "<a href=\"#" << prev << "\">&lt;</a>&nbsp;";
1021   }
1022   if( next && *next ) {
1023     os << "<a href=\"#" << next << "\">&gt;</a>&nbsp;";
1024   }
1025   os << "</td>"
1026      << "</tr>"
1027      << "</table>\n";
1028 }
1029 // ================================================================
1030 // Section header.
1031 // ================================================================
write_section_header(ostream & os,statement::base * stmt,bool contents,const char * prev,const char * next)1032 void ccdoc::phase3::html::write_section_header(ostream& os,
1033 					       statement::base* stmt,
1034 					       bool contents,
1035 					       const char* prev,
1036 					       const char* next)
1037 {
1038   if( stmt ) {
1039     string id = format_string_for_html(stmt->get_id()) ;
1040     string title;
1041     if( stmt->get_type() == statement::base::STMT_PACKAGE ) {
1042       // Issue 31 - The user might have specified their own package tag.
1043       title = stmt->get_type_name2();
1044       if( stmt->get_comment() ) {
1045 	statement::comment doc(stmt->get_comment());
1046 	string tid = doc.get_pkgdoc_tid();
1047 	if( tid.size() ) {
1048 	  title = tid;
1049 	}
1050       }
1051     }
1052     else if( stmt->get_template() ) {
1053       // This is a template, output the template information in the
1054       // title.
1055       title = "<font size=\"-1\">";
1056       // Capture the template information.
1057       const ccdoc::statement::base::cstrs_t tokens = stmt->get_tokens();
1058       ccdoc::statement::base::cstrs_citr_t itr1 = tokens.begin();
1059       int level = 0;
1060       for(;itr1!=tokens.end();++itr1) {
1061         string tmp = *itr1;
1062         title += tmp;
1063         title += " ";
1064         if( tmp == "<" ) {
1065           ++level;
1066         }
1067         else if( tmp == ">" ) {
1068           --level;
1069           if( level == 0 )
1070             break;
1071         }
1072       }
1073       title += "</font><br>";
1074       title += stmt->get_type_name2();
1075     }
1076     // The "operator" keyword is already built into the operator id.
1077     else if( stmt->get_type() != statement::base::STMT_FUNCTION_OPERATOR &&
1078 	     stmt->get_type() != statement::base::STMT_METHOD_OPERATOR ) {
1079       title = stmt->get_type_name2();
1080     }
1081     title += " ";
1082     title += id;
1083     write_section_header(os,title.c_str(),contents,prev,next);
1084   }
1085   else {
1086     write_section_header(os,"unknown",contents,prev,next);
1087   }
1088 }
1089 // ================================================================
1090 // Code begin
1091 // ================================================================
code_begin(ostream & os)1092 void ccdoc::phase3::html::code_begin(ostream& os)
1093 {
1094   os << "<blockquote>";
1095   os << "<pre>";
1096   os << endl;
1097 }
1098 // ================================================================
1099 // Code end
1100 // ================================================================
code_end(ostream & os)1101 void ccdoc::phase3::html::code_end(ostream& os) {
1102   os << "</pre>";
1103   os << "</blockquote>";
1104   os << endl;
1105 }
1106 // ================================================================
1107 // Load
1108 // ================================================================
load(const string & fn,string & contents)1109 void ccdoc::phase3::html::load(const string& fn,
1110 			       string& contents)
1111 {
1112   if( fn.size() ) {
1113     if( m_sw.verbose() ) {
1114       s_log << "loading " << fn.c_str() << " ...\n";
1115     }
1116     ifstream is(fn.c_str());
1117     if( !is ) {
1118       s_log.warning()
1119 	<< "Cannot read file '"
1120 	<< fn.c_str()
1121 	<< "'\n"
1122 	<< s_log.enable();
1123 	return;
1124     }
1125     // Read the file (including whitespace) into the contents string.
1126     char ch;
1127     while( is.get(ch) ) {
1128       contents += ch;
1129     }
1130   }
1131 }
1132 // ================================================================
1133 // Write out the code section.
1134 // ================================================================
write_code_section(ostream & os,statement::base * stmt)1135 void ccdoc::phase3::html::write_code_section(ostream& os,
1136 					     statement::base* stmt)
1137 {
1138   // The first step is to group the tokens that are
1139   // associated by '::'.
1140   statement::base::strs_t grouped_tokens;
1141   {
1142     const statement::base::cstrs_t& src = stmt->get_tokens();
1143     statement::base::cstrs_citr_t itr = src.begin();
1144     for(;itr!=src.end();++itr) {
1145       string x = *itr;
1146       if( x == "::" ) {
1147 	string token;
1148 	if( grouped_tokens.size() ) {
1149 	  token = grouped_tokens.back();
1150 	  grouped_tokens.pop_back();
1151 	}
1152 	token += *itr;
1153 	++itr;
1154 	if( itr != src.end() ) {
1155 	  token += *itr;
1156 	}
1157 	grouped_tokens.push_back(token);
1158       }
1159       else {
1160 	grouped_tokens.push_back(x);
1161       }
1162     }
1163   }
1164 
1165   // Output the code portion.
1166   os << "<dl><dt><b>Code:</b></dt><dd>";
1167   if( stmt->get_type() == statement::base::STMT_ATTRIBUTE ||
1168       stmt->get_type() == statement::base::STMT_ATTRIBUTE_FUNCTION ) {
1169     write_code_subsection_var(os,stmt,grouped_tokens);
1170   }
1171   else if( stmt->get_type() == statement::base::STMT_FUNCTION ||
1172 	   stmt->get_type() == statement::base::STMT_METHOD_CONSTRUCTOR ||
1173 	   stmt->get_type() == statement::base::STMT_METHOD_DESTRUCTOR ||
1174 	   stmt->get_type() == statement::base::STMT_METHOD ) {
1175     write_code_subsection_fct(os,stmt,grouped_tokens);
1176   }
1177   else if( stmt->get_type() == statement::base::STMT_FUNCTION_OPERATOR ||
1178 	   stmt->get_type() == statement::base::STMT_METHOD_OPERATOR ) {
1179     write_code_subsection_opr(os,stmt,grouped_tokens);
1180   }
1181   else if( stmt->get_type() == statement::base::STMT_ENUM ) {
1182     write_code_subsection_enum(os,stmt,grouped_tokens);
1183   }
1184   else if( stmt->get_type() == statement::base::STMT_VARIABLE ||
1185 	   stmt->get_type() == statement::base::STMT_VARIABLE_FUNCTION ) {
1186     write_code_subsection_var(os,stmt,grouped_tokens);
1187   }
1188   else {
1189     if( m_sw.rptfwcf() )
1190       os << "<code>"; // Issue 0045
1191     os << stmt->get_access_name() << " ";
1192     string external_linkage = stmt->get_extern();
1193     if( external_linkage.size() )
1194       os << "extern " << external_linkage << " ";
1195     statement::base::strs_itr_t itr = grouped_tokens.begin();
1196     for(;itr!=grouped_tokens.end();++itr) {
1197       string& token = *itr;
1198       if( itr != grouped_tokens.begin() ) {
1199 	os << " ";
1200       }
1201       write_code_subsection_token(os,stmt,token);
1202     }
1203     if( m_sw.rptfwcf() )
1204       os << "</code>"; // Issue 0045
1205     os << "\n";
1206   }
1207   os << "</dd></dl>\n";
1208 }
1209 // ================================================================
1210 // Write out the code section for variables.
1211 // ================================================================
write_code_subsection_var(ostream & os,statement::base * stmt,statement::base::strs_t & group)1212 void ccdoc::phase3::html::write_code_subsection_var(ostream& os,
1213 						    statement::base* stmt,
1214 						    statement::base::strs_t& group)
1215 {
1216   // ================================================
1217   // There is some special case handling here for
1218   // variables with no type.
1219   // ================================================
1220   if( m_sw.rptfwcf() )
1221     os << "<code>"; // Issue 0045
1222   os << stmt->get_access_name() << " ";
1223   string external_linkage = stmt->get_extern();
1224   if( external_linkage.size() )
1225     os << "extern " << external_linkage << " ";
1226   statement::base::strs_t& vec1 = group;
1227   if( vec1.size() == 1 ) {
1228     // This is the special case of a variable that
1229     // was declared as part of a class, struct, union
1230     // or enum declaration of the form:
1231     //   enum AA {A,B,C} var;
1232     statement::base* parent = stmt->get_parent();
1233     bool found = false;
1234     if( parent ) {
1235       statement::base::stmts_t& vec2 = parent->get_children();
1236       statement::base::stmts_itr_t itr2 = vec2.begin();
1237       for(;itr2!=vec2.end();++itr2) {
1238 	if( *itr2 == stmt ) {
1239 	  break;
1240 	}
1241       }
1242       // We found the statement, now back up until we find
1243       // an enum, class, struct or union. Variables of the
1244       // form int a,b,c; are handled in the parsing phase.
1245       if( stmt == *itr2 ) {
1246 	for(;itr2!=vec2.begin();--itr2) {
1247 	  statement::base* end = *itr2;
1248 	  bool done = false;
1249 	  switch( end->get_type() ) {
1250 	  case statement::base::STMT_ENUM:
1251 	    done = true;
1252 	    write_link(os,end);
1253 	    found = true;
1254 	    break;
1255 	  case statement::base::STMT_CLASS_END:
1256 	  case statement::base::STMT_STRUCT_END:
1257 	  case statement::base::STMT_UNION_END:
1258 	    {
1259 	      done = true;
1260 	      statement::base* begin = end->get_matching_begin();
1261 	      if( begin ) {
1262 		write_link(os,begin);
1263 		found = true;
1264 	      }
1265 	      break;
1266 	    }
1267 	  default:
1268 	    break;
1269 	  }
1270 	  if(done)
1271 	    break;
1272 	}
1273       }
1274     }
1275     if( !found ) {
1276       // TODO: create a test for this error condition.
1277       // It is not clear how this could be done. Perhaps this
1278       // is dead code.
1279       os << "<font color=red>unknown_type</font>";
1280       // Issue 109:
1281       s_log.warning()
1282 	<< "UNDEF: Unknown type for variable '"
1283 	<< stmt->get_id()
1284 	<< "' at line "
1285         << stmt->get_lineno()
1286         << " in file "
1287         << stmt->get_file()
1288         << "\n"
1289 	<< s_log.enable();
1290     }
1291     os << " ";
1292   }
1293 
1294   statement::base::strs_itr_t itr1 = vec1.begin();
1295   for(;itr1!=vec1.end();++itr1) {
1296     string& token = *itr1;
1297     if( itr1 != vec1.begin() ) {
1298       os << " ";
1299     }
1300     write_code_subsection_token(os,stmt,token);
1301   }
1302   os << "\n";
1303   if( m_sw.rptfwcf() )
1304     os << "</code>"; // Issue 0045
1305 }
1306 // ================================================================
1307 // Write out the code section for functions.
1308 // ================================================================
write_code_subsection_fct(ostream & os,statement::base * stmt,statement::base::strs_t & group)1309 void ccdoc::phase3::html::write_code_subsection_fct(ostream& os,
1310 						    statement::base* stmt,
1311 						    statement::base::strs_t& group)
1312 {
1313   // Here is where we get a bit fancy. The arguments are aligned
1314   // somewhat.
1315   statement::base::strs_t& vec1 = group;
1316   statement::base::strs_itr_t itr1 = vec1.begin();
1317   bool found_id = false;
1318   bool found_arg_lp = false;
1319   int depth = 0;
1320   os << "<table><tr><td nowrap valign=top>";
1321   if( m_sw.rptfwcf() )
1322     os << "<code>"; // Issue 0045
1323   os << stmt->get_access_name() << " ";
1324   string external_linkage = stmt->get_extern();
1325   if( external_linkage.size() )
1326     os << "extern " << external_linkage << " ";
1327   for(;itr1!=vec1.end();++itr1) {
1328     string& token = *itr1;
1329     if( itr1 != vec1.begin() ) {
1330       os << " ";
1331     }
1332     if( !found_id && token == stmt->get_id() ) {
1333       found_id = true;
1334       os << "<b>";
1335       write_code_subsection_token(os,stmt,token);
1336       os << "</b>";
1337     }
1338     else {
1339       write_code_subsection_token(os,stmt,token);
1340     }
1341 
1342     if( token == "(" )
1343       depth++;
1344     else if( token == ")" )
1345       depth--;
1346     if(!found_arg_lp && found_id && depth==1) {
1347       found_arg_lp = true;
1348       if( m_sw.rptfwcf() )
1349         os << "</code>"; // Issue 0045
1350       os << "</td><td nowrap valign=top>";
1351       if( m_sw.rptfwcf() )
1352         os << "<code>"; // Issue 0045
1353     }
1354     if( depth == 1 && token == "," ) {
1355       if( m_sw.rptfwcf() )
1356         os << "</code>"; // Issue 0045
1357       os << "</td></tr><tr><td nowrap valign=top> "
1358          << "</td><td nowrap valign=top>";
1359       if( m_sw.rptfwcf() )
1360         os << "<code>"; // Issue 0045
1361     }
1362   }
1363   if( m_sw.rptfwcf() )
1364     os << "</code>"; // Issue 0045
1365   os << "</td></tr></table>\n";
1366 }
1367 // ================================================================
1368 // Write out the code section for operator functions.
1369 // ================================================================
write_code_subsection_opr(ostream & os,statement::base * stmt,statement::base::strs_t & group)1370 void ccdoc::phase3::html::write_code_subsection_opr(ostream& os,
1371 						    statement::base* stmt,
1372 						    statement::base::strs_t& group)
1373 {
1374   // Here is where we get a bit fancy. The arguments are aligned
1375   // somewhat.
1376   statement::base::strs_t& vec1 = group;
1377   statement::base::strs_itr_t itr1 = vec1.begin();
1378   bool found_id = false;
1379   bool found_arg_lp = false;
1380   int depth = 0;
1381   os << "<table><tr><td nowrap valign=top>";
1382   if( m_sw.rptfwcf() )
1383     os << "<code>"; // Issue 0045
1384   os << stmt->get_access_name() << " ";
1385   string external_linkage = stmt->get_extern();
1386   if( external_linkage.size() )
1387     os << "extern " << external_linkage << " ";
1388   // Walk through the statement tokens.
1389   // If it is an operator, it will be recognized
1390   // by the presence of the "operator" keyword.
1391   for(;itr1!=vec1.end();++itr1) {
1392     string& token = *itr1;
1393     if( itr1 != vec1.begin() ) {
1394       os << " ";
1395     }
1396     if( !found_id && token == "operator" ) {
1397       found_id = true;
1398       os << "<b>";
1399       token = stmt->get_id();
1400       write_code_subsection_token(os,stmt,token);
1401       os << "</b>";
1402       // Now skip ahead to the lp.
1403       if( token == "operator ()" ) {
1404 	// Special case.
1405 	++itr1;
1406 	++itr1;
1407       }
1408       else {
1409 	for(;itr1!=vec1.end();++itr1) {
1410 	  if( *itr1 == "(" ) {
1411 	    --itr1;
1412 	    break;
1413 	  }
1414 	}
1415       }
1416     }
1417     else {
1418       write_code_subsection_token(os,stmt,token);
1419     }
1420 
1421     if( token == "(" ) {
1422       depth++;
1423     }
1424     else if( token == ")" ) {
1425       depth--;
1426     }
1427     if( !found_arg_lp && found_id && depth==1 ) {
1428       found_arg_lp = true;
1429       if( m_sw.rptfwcf() )
1430         os << "</code>"; // Issue 0045
1431       os << "</td><td nowrap valign=top>";
1432       if( m_sw.rptfwcf() )
1433         os << "<code>"; // Issue 0045
1434     }
1435     if( depth == 1 && token == "," ) {
1436       if( m_sw.rptfwcf() )
1437         os << "</code>"; // Issue 0045
1438       os << "</td></tr><tr><td nowrap valign=top> "
1439          << "</td><td nowrap valign=top>";
1440       if( m_sw.rptfwcf() )
1441         os << "<code>"; // Issue 0045
1442     }
1443   }
1444   if( m_sw.rptfwcf() )
1445     os << "</code>"; // Issue 0045
1446   os << "</td></tr></table>\n";
1447 }
1448 // ================================================================
1449 // Write out the code section for enums.
1450 // ================================================================
write_code_subsection_enum(ostream & os,statement::base * stmt,statement::base::strs_t & group)1451 void ccdoc::phase3::html::write_code_subsection_enum(ostream& os,
1452 						     statement::base* stmt,
1453 						     statement::base::strs_t& group)
1454 {
1455   // Here is where we get a bit fancy. The arguments are aligned
1456   // somewhat.
1457   statement::base::strs_t& vec1 = group;
1458   statement::base::strs_itr_t itr1 = vec1.begin();
1459   bool found_id = false;
1460   bool found_arg_lp = false;
1461   int depth = 0;
1462   os << "<table><tr><td nowrap valign=top>";
1463   if( m_sw.rptfwcf() )
1464     os << "<code>"; // Issue 0045
1465   os << stmt->get_access_name() << " ";
1466   string external_linkage = stmt->get_extern();
1467   if( external_linkage.size() )
1468     os << "extern " << external_linkage << " ";
1469   for(;itr1!=vec1.end();++itr1) {
1470     string& token = *itr1;
1471     if( itr1 != vec1.begin() ) {
1472       os << " ";
1473     }
1474     if( !found_id && token == stmt->get_id() ) {
1475       found_id = true;
1476       os << "<b>";
1477       write_code_subsection_token(os,stmt,token);
1478       os << "</b>";
1479     }
1480     else {
1481       write_code_subsection_token(os,stmt,token);
1482     }
1483 
1484     if( token == "{" )
1485       depth++;
1486     else if( token == "}" )
1487       depth--;
1488     // The id is not checked for here because anonymuous
1489     // enums do not have ids.
1490     if(!found_arg_lp && depth==1) {
1491       found_arg_lp = true;
1492       if( m_sw.rptfwcf() )
1493         os << "</code>"; // Issue 0045
1494       os << "</td><td nowrap valign=top>";
1495       if( m_sw.rptfwcf() )
1496         os << "<code>"; // Issue 0045
1497     }
1498     if( depth == 1 && token == "," ) {
1499       if( m_sw.rptfwcf() )
1500         os << "</code>"; // Issue 0045
1501       os << "</td></tr><tr><td nowrap valign=top> "
1502          << "</td><td nowrap valign=top>";
1503       if( m_sw.rptfwcf() )
1504         os << "<code>"; // Issue 0045
1505     }
1506   }
1507   if( m_sw.rptfwcf() )
1508     os << "</code>"; // Issue 0045
1509   os << "</td></tr></table>\n";
1510 }
1511 // ================================================================
1512 // Write out token for code section.
1513 // ================================================================
write_code_subsection_token(ostream & os,statement::base * stmt,const string & token)1514 void ccdoc::phase3::html::write_code_subsection_token(ostream& os,
1515 						      statement::base* stmt,
1516 						      const string& token)
1517 {
1518   if( token.size() == 0 )
1519     return;
1520   if( token == "<" )
1521     os << "&lt;";
1522   else if( token == ">" )
1523     os << "&gt;";
1524   else if( token == "&" )
1525     os << "&amp;";
1526   else if( token == "..." )
1527     os << "...";
1528   else if( token == stmt->get_id() ) {
1529     os << "<b>" << format_string_for_html(token) << "</b>";
1530   }
1531   else if( is_cxx_keyword(token) ) {
1532     os << "<font color=green><b>" << token << "</b></font>";
1533   }
1534   else if( ('a' <= token[0] && token[0] <= 'z' ) ||
1535 	   ('A' <= token[0] && token[0] <= 'Z' ) ||
1536 	   ('_' == token[0] ) ||
1537 	   ('$' == token[0] ) ) {
1538 
1539     // ================================================
1540     // Do a simple search for local matches in the
1541     // parent.
1542     // This matches things like:
1543     //   class A {
1544     //   public:
1545     //     A& operator = (const A&);
1546     //   };
1547     // ================================================
1548     if( stmt->get_parent() ) {
1549       if( stmt->get_parent()->get_type() == statement::base::STMT_CLASS_BEGIN ||
1550 	  stmt->get_parent()->get_type() == statement::base::STMT_STRUCT_BEGIN ||
1551 	  stmt->get_parent()->get_type() == statement::base::STMT_UNION_BEGIN ) {
1552 	if( token == stmt->get_parent()->get_id() ) {
1553 	  os << "<b>" << format_string_for_html(token) << "</b>";
1554 	  return;
1555 	}
1556       }
1557     }
1558 
1559     // ================================================
1560     // Issue 0118
1561     // tokenize the id
1562     // A::B::C becomes A,B,C
1563     // ================================================
1564     vector<string> tokens;
1565     size_t beg=0;
1566     size_t end=beg;
1567     for(;end<token.size();++end) {
1568       if( end>beg && token[end] == ':' && token[end-1] == ':') {
1569         tokens.push_back( token.substr(beg,end-1) );
1570         beg = end+1;
1571       }
1572     }
1573     if(beg<end)
1574       tokens.push_back( token.substr(beg,end) );
1575 
1576     // ================================================
1577     // Issue 0118
1578     // Recursively find matching statements, including
1579     // partial matches.
1580     // ================================================
1581     vector<statement::base*> matches;
1582     if( stmt->get_parent() ) {
1583       statement::base* parent = stmt->get_parent();
1584       for( ;parent; parent = parent->get_parent() ) {
1585         if( ignore_contents_stmt(parent) )
1586           break;
1587         write_code_subsection_token(matches,parent,tokens,0);
1588         if( matches.size() ) {
1589           write_links(os,matches,token.c_str());
1590           return;
1591         }
1592       }
1593     }
1594 
1595     // ================================================
1596     // Check for links here by looking up entities
1597     // in the database. Begin by looking in the same
1598     // namespace.
1599     // ================================================
1600     statement::base::stmts_t token_stmts;
1601     m_db.get_stmt_no_pkgs(token,token_stmts);
1602     if( token_stmts.size() ) {
1603       // Filter the token statements by type.
1604       statement::base::stmts_t valid_token_stmts;
1605       statement::base::stmts_itr_t itr = token_stmts.begin();
1606       for(;itr!=token_stmts.end();++itr) {
1607 	switch( (*itr)->get_type() ) {
1608 	case statement::base::STMT_CLASS_BEGIN:
1609 	case statement::base::STMT_STRUCT_BEGIN:
1610 	case statement::base::STMT_UNION_BEGIN:
1611 	case statement::base::STMT_ENUM:
1612 	case statement::base::STMT_TYPEDEF_FUNCTION:
1613 	case statement::base::STMT_TYPEDEF_VARIABLE:
1614 	case statement::base::STMT_VARIABLE: // Issue 0091:
1615 	case statement::base::STMT_VARIABLE_FUNCTION:
1616 	case statement::base::STMT_FUNCTION:
1617 	case statement::base::STMT_FUNCTION_OPERATOR:
1618 	  valid_token_stmts.push_back(*itr);
1619 	  break;
1620 	default:
1621 	  break;
1622 	}
1623       }
1624       if( valid_token_stmts.size() == 1 ) {
1625 	// If there is only one, use it.
1626 	write_link(os,valid_token_stmts[0],token.c_str());
1627 	return;
1628       }
1629       else if( valid_token_stmts.size() > 1 ) {
1630 	// If there is more than one statement that matches,
1631 	// try finding it in the same namespace as the stmt.
1632 	statement::base* nsp = 0;
1633 	statement::base* parent = stmt->get_parent();
1634 	while(parent) {
1635 	  if(parent->get_type() == statement::base::STMT_NAMESPACE_BEGIN) {
1636 	    nsp = parent;
1637 	    break;
1638 	  }
1639 	  parent = parent->get_parent();
1640 	}
1641 	statement::base::stmts_t valid_token_stmts_in_nsp;
1642 	itr = valid_token_stmts.begin();
1643 	for(;itr!=valid_token_stmts.end();++itr) {
1644 	  statement::base* nsp1 = 0;
1645 	  parent = *itr;
1646 	  while(parent) {
1647 	    if(parent->get_type() == statement::base::STMT_NAMESPACE_BEGIN) {
1648 	      nsp1 = parent;
1649 	      break;
1650 	    }
1651 	    parent = parent->get_parent();
1652 	  }
1653 	  if( nsp == nsp1 ) {
1654 	    valid_token_stmts_in_nsp.push_back(*itr);
1655 	    // Exit after one for efficiency.
1656 	    break;
1657 	  }
1658 	}
1659 	if( valid_token_stmts_in_nsp.size() ) {
1660 	  write_link(os,valid_token_stmts_in_nsp[0],token.c_str());
1661 	  return;
1662 	}
1663 	else {
1664 	  write_link(os,valid_token_stmts[0],token.c_str());
1665 	  return;
1666 	}
1667       }
1668     }
1669     // No matching statements were found.
1670     os << format_string_for_html(token);
1671   }
1672   else {
1673     os << format_string_for_html(token);
1674   }
1675 }
1676 // ================================================================
1677 // Write out token for code section if it matches an id in
1678 // the specified scope.
1679 // ================================================================
write_code_subsection_token(ostream & os,statement::base::stmts_t & vec,const string & token)1680 bool ccdoc::phase3::html::write_code_subsection_token(ostream& os,
1681 						      statement::base::stmts_t& vec,
1682 						      const string& token)
1683 {
1684   statement::base::stmts_itr_t itr = vec.begin();
1685   for(;itr!=vec.end();++itr) {
1686     statement::base* stmt = *itr;
1687     if( token == stmt->get_id() ) {
1688       switch( stmt->get_type() ) {
1689       case statement::base::STMT_CLASS_BEGIN:
1690       case statement::base::STMT_STRUCT_BEGIN:
1691       case statement::base::STMT_UNION_BEGIN:
1692       case statement::base::STMT_ENUM:
1693       case statement::base::STMT_TYPEDEF_FUNCTION:
1694       case statement::base::STMT_TYPEDEF_VARIABLE:
1695       case statement::base::STMT_VARIABLE: // Issue 0091:
1696       case statement::base::STMT_VARIABLE_FUNCTION:
1697       case statement::base::STMT_FUNCTION:
1698       case statement::base::STMT_FUNCTION_OPERATOR:
1699 	// Attach to the first one of the correct type.
1700 	write_link(os,stmt,token.c_str());
1701 	return true;
1702       default:
1703 	break;
1704       }
1705     }
1706   }
1707   return false;
1708 }
1709 // ================================================================
1710 // Issue 0118
1711 // Match a partial specification.
1712 // the specified scope.
1713 // ================================================================
write_code_subsection_token(statement::base::stmts_t & vec,statement::base * stmt,const vector<string> & tokens,size_t idx) const1714 void ccdoc::phase3::html::write_code_subsection_token(statement::base::stmts_t& vec,
1715                                                       statement::base* stmt,
1716                                                       const vector<string>& tokens,
1717                                                       size_t idx) const
1718 {
1719   vector<statement::base*> local_matches;
1720   stmt->get_children_by_id(local_matches,tokens[idx]);
1721   if( local_matches.size() ) {
1722     ++idx;
1723     vector<statement::base*>::iterator itr = local_matches.begin();
1724     for(;itr!=local_matches.end();++itr) {
1725       if( ignore_contents_stmt(*itr) )
1726         continue;
1727       if( idx < tokens.size() ) {
1728         write_code_subsection_token(vec,*itr,tokens,idx);
1729       }
1730       else {
1731         vec.push_back(*itr);
1732       }
1733     }
1734   }
1735 }
1736 // ================================================================
1737 // Is this a keyword.
1738 // ================================================================
is_cxx_keyword(const string & token)1739 bool ccdoc::phase3::html::is_cxx_keyword(const string& token)
1740 {
1741   if( m_keywords.size() == 0 ) {
1742     // ================================================
1743     // Load the keywords:
1744     // Table 3 (2.11)
1745     // ================================================
1746     m_keywords.insert("asm");
1747     m_keywords.insert("auto");
1748     m_keywords.insert("bool");
1749     m_keywords.insert("break");
1750     m_keywords.insert("case");
1751     m_keywords.insert("catch");
1752     m_keywords.insert("char");
1753     m_keywords.insert("class");
1754     m_keywords.insert("const");
1755     m_keywords.insert("const_cast");
1756     m_keywords.insert("continue");
1757     m_keywords.insert("default");
1758     m_keywords.insert("delete");
1759     m_keywords.insert("do");
1760     m_keywords.insert("double");
1761     m_keywords.insert("dynamic_cast");
1762     m_keywords.insert("else");
1763     m_keywords.insert("enum");
1764     m_keywords.insert("explicit");
1765     m_keywords.insert("export");
1766     m_keywords.insert("extern");
1767     m_keywords.insert("false");
1768     m_keywords.insert("float");
1769     m_keywords.insert("for");
1770     m_keywords.insert("friend");
1771     m_keywords.insert("goto");
1772     m_keywords.insert("if");
1773     m_keywords.insert("inline");
1774     m_keywords.insert("int");
1775     m_keywords.insert("long");
1776     m_keywords.insert("mutable");
1777     m_keywords.insert("namespace");
1778     m_keywords.insert("new");
1779     m_keywords.insert("operator");
1780     m_keywords.insert("private");
1781     m_keywords.insert("protected");
1782     m_keywords.insert("public");
1783     m_keywords.insert("register");
1784     m_keywords.insert("reinterpret_cast");
1785     m_keywords.insert("return");
1786     m_keywords.insert("short");
1787     m_keywords.insert("signed");
1788     m_keywords.insert("sizeof");
1789     m_keywords.insert("static");
1790     m_keywords.insert("static_cast");
1791     m_keywords.insert("struct");
1792     m_keywords.insert("switch");
1793     m_keywords.insert("template");
1794     m_keywords.insert("this");
1795     m_keywords.insert("throw");
1796     m_keywords.insert("true");
1797     m_keywords.insert("try");
1798     m_keywords.insert("typedef");
1799     m_keywords.insert("typeid");
1800     m_keywords.insert("typename");
1801     m_keywords.insert("union");
1802     m_keywords.insert("unsigned");
1803     m_keywords.insert("using");
1804     m_keywords.insert("virtual");
1805     m_keywords.insert("void");
1806     m_keywords.insert("volatile");
1807     m_keywords.insert("wchar_t");
1808     m_keywords.insert("while");
1809 
1810     // ================================================
1811     // Add in the alternate representations for some
1812     // of the operators and punctuators.
1813     // ================================================
1814     m_keywords.insert("and");
1815     m_keywords.insert("and_eq");
1816     m_keywords.insert("bitand");
1817     m_keywords.insert("bitor");
1818     m_keywords.insert("compl");
1819     m_keywords.insert("not");
1820     m_keywords.insert("not_eq");
1821     m_keywords.insert("or");
1822     m_keywords.insert("or_eq");
1823     m_keywords.insert("xor");
1824     m_keywords.insert("xor_eq");
1825   }
1826   set<string>::const_iterator itr = m_keywords.find(token);
1827   return itr != m_keywords.end();
1828 }
1829 // ================================================================
1830 // Compare statements for the make_contents() algorithm.
1831 // ================================================================
1832 namespace
1833 {
compare_stmts_full_paths(const ccdoc::statement::base * a,const ccdoc::statement::base * b)1834   bool compare_stmts_full_paths(const ccdoc::statement::base* a,
1835                                 const ccdoc::statement::base* b)
1836   {
1837     assert(a);
1838     assert(b);
1839     string a_id;
1840     string b_id;
1841     a->get_hier_id(a_id);
1842     b->get_hier_id(b_id);
1843 
1844     // Do a case insensitive compare to make things easier
1845     // to find.
1846     const char* p1 = a_id.c_str();
1847     const char* p2 = b_id.c_str();
1848     for(;*p1 && *p2;++p1,++p2) {
1849       if( *p1 != *p2 ) {
1850         int ch1 = *p1;
1851         int ch2 = *p2;
1852         // Convert to upper case.
1853         if( 'a' <= ch1 && ch1 <= 'z' )
1854           ch1 = ( ch1 - 'a' ) + 'A';
1855         if( 'a' <= ch2 && ch2 <= 'z' )
1856           ch2 = ( ch2 - 'a' ) + 'A';
1857         if( ch1 != ch2 )
1858           return ch1 < ch2;
1859       }
1860     }
1861     return *p1 < *p2;
1862   }
1863 }
1864 // ================================================================
1865 // Compare statements for the make_contents() algorithm.
1866 // ================================================================
1867 namespace
1868 {
compare_stmts(const ccdoc::statement::base * a,const ccdoc::statement::base * b)1869   bool compare_stmts(const ccdoc::statement::base* a,
1870                      const ccdoc::statement::base* b)
1871   {
1872     assert(a);
1873     assert(b);
1874     // Special cases:
1875     // Destructors always show up before first.
1876     if( a->get_type() == ccdoc::statement::base::STMT_METHOD_DESTRUCTOR ) {
1877       return true; // a<b
1878     }
1879     if( b->get_type() == ccdoc::statement::base::STMT_METHOD_DESTRUCTOR ) {
1880       return false; // b<a
1881     }
1882     if( a->get_type() == ccdoc::statement::base::STMT_METHOD_CONSTRUCTOR ) {
1883       // Constructors always match.
1884       if( b->get_type() == ccdoc::statement::base::STMT_METHOD_CONSTRUCTOR ) {
1885         return false;
1886       }
1887       // Constructors always show up first.
1888       return true; // a<b
1889     }
1890     if( b->get_type() == ccdoc::statement::base::STMT_METHOD_CONSTRUCTOR ) {
1891       // Constructors always show up first.
1892       return false; // b<a
1893     }
1894 
1895     string a_id;
1896     string b_id;
1897     if( a->get_type() == ccdoc::statement::base::STMT_PACKAGE ||
1898         a->get_type() == ccdoc::statement::base::STMT_NAMESPACE_BEGIN ) {
1899       if( ( b->get_type() == ccdoc::statement::base::STMT_PACKAGE ||
1900             b->get_type() == ccdoc::statement::base::STMT_NAMESPACE_BEGIN ) ) {
1901         // a and b are both packages or namespaces,
1902         // compare them hierarchically
1903         a->get_hier_id(a_id);
1904         b->get_hier_id(b_id);
1905       }
1906       else {
1907         // a is a package or namespace and b isn't
1908         // a is less than b.
1909         // Always put packages and namespaces first.
1910         return true;
1911       }
1912     }
1913     else if( ( b->get_type() == ccdoc::statement::base::STMT_PACKAGE ||
1914                b->get_type() == ccdoc::statement::base::STMT_NAMESPACE_BEGIN ) ) {
1915       // b is a package or namespace and a isn't
1916       // b is less than a.
1917       return false;
1918     }
1919     else {
1920       a_id = a->get_id();
1921       b_id = b->get_id();
1922     }
1923 
1924     // Do a case insensitive compare to make things easier
1925     // to find.
1926     const char* p1 = a_id.c_str();
1927     const char* p2 = b_id.c_str();
1928     for(;*p1 && *p2;++p1,++p2) {
1929       if( *p1 != *p2 ) {
1930         int ch1 = *p1;
1931         int ch2 = *p2;
1932         // Convert to upper case.
1933         if( 'a' <= ch1 && ch1 <= 'z' )
1934           ch1 = ( ch1 - 'a' ) + 'A';
1935         if( 'a' <= ch2 && ch2 <= 'z' )
1936           ch2 = ( ch2 - 'a' ) + 'A';
1937         if( ch1 != ch2 )
1938           return ch1 < ch2;
1939       }
1940     }
1941     return *p1 < *p2;
1942   }
1943 }
1944 // ================================================================
1945 // Make contents.
1946 // This is used to get list of entities for contents and sections.
1947 // ================================================================
make_contents(statement::base * stmt,statement::base::stmts_t & contents,bool sort_flag)1948 void ccdoc::phase3::html::make_contents(statement::base* stmt,
1949 					statement::base::stmts_t& contents,
1950 					bool sort_flag)
1951 {
1952   // ================================================
1953   // Filter out the children that we don't care
1954   // about on this run.
1955   // ================================================
1956   statement::base::stmts_t& children = stmt->get_children();
1957   if( children.size() ) {
1958     statement::base::stmts_itr_t itr = children.begin();
1959     for(;itr!=children.end();++itr) {
1960       statement::base* child = *itr;
1961       if(ignore_contents_stmt(child))
1962 	continue;
1963 
1964       // ================================================
1965       // Special case handling for namespaces and packages,
1966       // we provide nested information for them.
1967       // ================================================
1968       if( child->get_type() == statement::base::STMT_PACKAGE ) {
1969 	statement::base::stmts_t vec1;
1970 	make_pkg_index_children(child,vec1);
1971 	statement::base::stmts_itr_t itr1 = vec1.begin();
1972 	for(;itr1!=vec1.end();++itr1) {
1973 	  contents.push_back(*itr1);
1974 	}
1975 	// Additional special case handling for packages.
1976 	// Look for child namespaces that are stored as tokens.
1977 	const statement::base::cstrs_t& nsps = child->get_tokens();
1978 	statement::base::cstrs_citr_t nsps_itr = nsps.begin();
1979 	for(;nsps.begin()!=nsps.end();++nsps_itr) {
1980 	  string token = *nsps_itr;
1981 	  statement::base::stmts_t nsp_stmts;
1982 	  m_db.get_stmt_no_pkgs(token,nsp_stmts);
1983 
1984 	  // Filter out the statements that don't have packages as
1985 	  // parents. Add the top namespaces to the contents list.
1986 	  statement::base::stmts_itr_t nsp_stmts_itr = nsp_stmts.begin();
1987 	  for(;nsp_stmts_itr!=nsp_stmts.end();++nsp_stmts_itr) {
1988 	    statement::base* nsp = *nsp_stmts_itr;
1989 	    if( nsp->get_parent()->get_type() == statement::base::STMT_PACKAGE ) {
1990 	      vec1.clear();
1991 	      make_pkg_index_children(nsp,vec1);
1992 	      for(itr1 = vec1.begin();itr1!=vec1.end();++itr1) {
1993 		contents.push_back(*itr1);
1994 	      }
1995 	    }
1996 	  }
1997 	}
1998       }
1999       else if( child->get_type() == statement::base::STMT_NAMESPACE_BEGIN ) {
2000         statement::base::stmts_t vec1;
2001         make_pkg_index_children(child,vec1);
2002         statement::base::stmts_itr_t itr1 = vec1.begin();
2003         for(;itr1!=vec1.end();++itr1) {
2004           contents.push_back(*itr1);
2005         }
2006       }
2007       else {
2008 	contents.push_back(child);
2009       }
2010     }
2011   }
2012 
2013   // ================================================
2014   // Load parent class contents.
2015   // ================================================
2016   if( stmt->get_type() == statement::base::STMT_CLASS_BEGIN ||
2017       stmt->get_type() == statement::base::STMT_STRUCT_BEGIN ) {
2018     make_class_contents(stmt,contents);
2019   }
2020 
2021   // ================================================
2022   // Sort the remaining children by name.
2023   // ================================================
2024   if( sort_flag ) {
2025     // Use a stable sort here so that we don't have
2026     // to worry about how equivalent entries are
2027     // handled. Duplicate entries are expected. The
2028     // most common cause is overloaded methods.
2029     // This is verified by test88.
2030     //sort(contents.begin(),contents.end(),compare_stmts); // fails
2031     stable_sort(contents.begin(),contents.end(),compare_stmts); // Issue 0183
2032   }
2033 }
2034 // ================================================================
2035 // Make class contents.
2036 // This is used to get list of entities for contents and sections.
2037 // Classes are a special case because we also get the inherited
2038 // methods.
2039 // ================================================================
make_class_contents(statement::base * stmt,statement::base::stmts_t & contents)2040 void ccdoc::phase3::html::make_class_contents(statement::base* stmt,
2041 					      statement::base::stmts_t& contents)
2042 {
2043   if( m_sw.rptim() ) {
2044     // ================================================
2045     // Get the classes that this class derives from.
2046     // ================================================
2047     statement::base::stmts_t classes;
2048     load_inheritance_classes(stmt,classes);
2049     if( classes.size() == 0 )
2050       return;
2051 
2052     // ================================================
2053     // Populate the used_method_ids with the contents
2054     // of the original class.
2055     // ================================================
2056     statement::base::stmts_t used_classes;
2057     set<string> used_method_ids;
2058     statement::base::stmts_itr_t contents_itr = contents.begin();
2059     for(;contents_itr!=contents.end();++contents_itr) {
2060       statement::base* rec = *contents_itr;
2061       if( rec->get_type() == statement::base::STMT_METHOD ) {
2062 	string key = rec->get_id();
2063 	used_method_ids.insert( key );
2064       }
2065     }
2066     if( classes.size() )
2067       make_class_contents(contents,classes,used_classes,used_method_ids);
2068   }
2069 }
2070 // ================================================================
2071 // Make class contents.
2072 // This is used to get list of entities for contents and sections.
2073 // Classes are a special case because we also get the inherited
2074 // methods.
2075 // ================================================================
make_class_contents(statement::base::stmts_t & contents,statement::base::stmts_t & classes,statement::base::stmts_t & used_classes,set<string> & used_method_ids)2076 void ccdoc::phase3::html::make_class_contents(statement::base::stmts_t& contents,
2077 					      statement::base::stmts_t& classes,
2078 					      statement::base::stmts_t& used_classes,
2079 					      set<string>& used_method_ids)
2080 {
2081   // ================================================
2082   // Iterate over each of the parent classes to
2083   // capture their methods. Ignore methods that
2084   // have already been defined.
2085   // ================================================
2086   statement::base::stmts_itr_t classes_itr = classes.begin();
2087   for(;classes_itr!=classes.end();++classes_itr) {
2088     statement::base* class_rec = *classes_itr;
2089 
2090     // This is an O(N^2) test to verify that we don't have
2091     // an inheritance loop. The performance s/b ok because
2092     // the inheritance depth s/b small (<10).
2093     statement::base::stmts_itr_t used_classes_itr = used_classes.begin();
2094     for(;used_classes_itr!=used_classes.end();++used_classes_itr) {
2095       if( *used_classes_itr == class_rec ) {
2096 	break;
2097       }
2098     }
2099     if( used_classes_itr != used_classes.end() )
2100       continue;
2101     used_classes.push_back(class_rec);
2102 
2103     // Add methods to the contents.
2104     statement::base::stmts_t& children = class_rec->get_children();
2105     statement::base::stmts_itr_t children_itr = children.begin();
2106     for(;children_itr!=children.end();++children_itr) {
2107       statement::base* rec = *children_itr;
2108       if( rec->get_type() == statement::base::STMT_METHOD ) {
2109 	if( !ignore_contents_stmt(rec) ) { // Issue 0040
2110 	  string key = rec->get_id();
2111 	  if( used_method_ids.find( key ) == used_method_ids.end() ) {
2112 	    contents.push_back( rec );
2113 	  }
2114 	}
2115       }
2116     }
2117 
2118     // Walk back through and update the use_method_ids.
2119     // This is not done as the methods are traversed
2120     // because we want to capture functions with the
2121     // same ids but different signatures.
2122     children_itr = children.begin();
2123     for(;children_itr!=children.end();++children_itr) {
2124       statement::base* rec = *children_itr;
2125       if( rec->get_type() == statement::base::STMT_METHOD ) {
2126 	if( !ignore_contents_stmt(rec) ) { // Issue 0040
2127 	  string key = rec->get_id();
2128 	  if( used_method_ids.find( key ) == used_method_ids.end() ) {
2129 	    used_method_ids.insert( key );
2130 	  }
2131 	}
2132       }
2133     }
2134 
2135     // Load the parents of this class recursively.
2136     statement::base::stmts_t parent_classes;
2137     load_inheritance_classes(class_rec,parent_classes);
2138     if( parent_classes.size() )
2139       make_class_contents(contents,parent_classes,used_classes,used_method_ids);
2140   }
2141 }
2142 // ================================================================
2143 // Load the inheritance classes.
2144 // Load the classes that this class derives from.
2145 // ================================================================
load_inheritance_classes(statement::base * stmt,statement::base::stmts_t & classes)2146 void ccdoc::phase3::html::load_inheritance_classes(statement::base* stmt,
2147 						   statement::base::stmts_t& classes)
2148 {
2149   const statement::base::cstrs_t& tokens = stmt->get_tokens();
2150   statement::base::cstrs_citr_t tokens_itr = tokens.begin();
2151   for(;tokens_itr!=tokens.end();++tokens_itr) {
2152     string token = *tokens_itr;
2153     if( token == ":" ) {
2154       // This is where the interesting stuff starts.
2155       tokens_itr++;
2156       break;
2157     }
2158   }
2159   if( tokens_itr == tokens.end() ) {
2160     // No tokens.
2161     // This class does not derive from any other classes.
2162     return;
2163   }
2164 
2165   bool found_id = false;
2166   int depth = 0;
2167   string id;
2168   for(;tokens_itr!=tokens.end();++tokens_itr) {
2169     string token = *tokens_itr;
2170     if( !found_id ) {
2171       if( token == "virtual" ||
2172 	  token == "public" ||
2173 	  token == "protected" ||
2174 	  token == "private" ) {
2175 	continue;
2176       }
2177       found_id = true;
2178     }
2179     if( found_id ) {
2180       if( token == "<" ) {
2181 	// This handles cases like:
2182 	//   class A : public foo<My1,My2> ;
2183 	//   class A : public foo<My1,My2<XX> > ;
2184 	depth++;
2185       }
2186       else if( token == ">" ) {
2187 	depth--;
2188       }
2189       else if( depth == 0 ) {
2190 	if( token == "," ) {
2191 	  // Output the extends information.
2192 	  statement::base::stmts_t matches;
2193 	  if( 1 == load_extend_classes( id, matches, stmt ) ) {
2194 	    classes.push_back( matches[0] );
2195 	  }
2196 	  else {
2197 	    // TODO: Warn there there were multiple classes.
2198 	  }
2199 	  id = "";
2200 	  found_id = false;
2201 	  depth = 0;
2202 	  continue;
2203 	}
2204       }
2205       id += token;
2206     }
2207   }
2208   statement::base::stmts_t matches;
2209   if( 1 == load_extend_classes( id, matches, stmt ) ) {
2210     classes.push_back( matches[0] );
2211   }
2212   else {
2213     // TODO: Warn there there were multiple classes.
2214   }
2215 }
2216 // ================================================================
2217 // Get the class record.
2218 // ================================================================
load_extend_classes(const string & id,statement::base::stmts_t & out,statement::base * stmt)2219 unsigned ccdoc::phase3::html::load_extend_classes(const string& id,
2220 						  statement::base::stmts_t& out,
2221 						  statement::base* stmt)
2222 {
2223   statement::base::stmts_t id_stmts;
2224   m_db.get_stmt_no_pkgs(id,id_stmts);
2225   if( id_stmts.size() == 0 ) {
2226     // Issue 0048
2227     string scoped_id;
2228     statement::base::stmts_t parents;
2229     stmt->get_parents_no_pkgs(parents);
2230     get_fully_scoped_name(scoped_id,id,parents);
2231     m_db.get_stmt_no_pkgs(scoped_id,id_stmts);
2232     if( id_stmts.size() == 0 )
2233       return 0;
2234   }
2235   if( id_stmts.size() > 0 ) {
2236     // Filter out the statements that are not classes or structs.
2237     statement::base::stmts_itr_t sitr = id_stmts.begin();
2238     for(;sitr!=id_stmts.end();++sitr) {
2239       statement::base* id_stmt = *sitr;
2240       if( id_stmt->get_type() == statement::base::STMT_CLASS_BEGIN ||
2241 	  id_stmt->get_type() == statement::base::STMT_STRUCT_BEGIN ) {
2242 	out.push_back(id_stmt);
2243       }
2244     }
2245   }
2246   return out.size();
2247 }
2248 // ================================================================
2249 // Get the fully scoped name.
2250 // ================================================================
get_fully_scoped_name(string & out_id,const string & in_id,statement::base::stmts_t parents) const2251 void ccdoc::phase3::html::get_fully_scoped_name(string& out_id,
2252 						const string& in_id,
2253 						statement::base::stmts_t parents) const
2254 {
2255   statement::base::strs_t ids;
2256   m_db.parse_path(in_id,ids);
2257   if( parents.size() ) {
2258     bool doit = true;
2259     size_t last1 = 0;
2260     if( ids.size() > 1 ) {
2261       size_t sz1 = ids.size() - 1;
2262       size_t last2 = parents.size() - 1;
2263       last1 = sz1 - 1;
2264       for(;last1!=0 && last2!=0;--last1,--last2) {
2265 	string nm = parents[last2]->get_id();
2266 	if( nm != ids[last1] ) {
2267 	  doit = false;
2268 	  break;
2269 	}
2270       }
2271       if( doit ) {
2272 	// The match was found.
2273 	last1 = sz1 - 1;
2274       }
2275     }
2276     if( doit ) {
2277       out_id = "";
2278       bool first = true;
2279       statement::base::stmts_itr_t itr = parents.begin();
2280       for(;itr!=parents.end();++itr) {
2281 	if( !first )
2282 	  out_id += "::";
2283 	out_id += (*itr)->get_id();
2284 	first = false;
2285       }
2286       if( !first )
2287 	out_id += "::";
2288       out_id += ids[last1];
2289       return;
2290     }
2291   }
2292   // Issue 0127
2293   //   Check for the special case of B<int>.
2294   //   This is invalid for a scoped name because
2295   //   it cannot be looked up.
2296   out_id = in_id;
2297   string::size_type f = out_id.find('<');
2298   if( f != string::npos ) {
2299     out_id.erase(f);
2300   }
2301 }
2302 // ================================================================
2303 // Ignore a statement?
2304 // ================================================================
ignore_contents_stmt(statement::base * stmt) const2305 bool ccdoc::phase3::html::ignore_contents_stmt(statement::base* stmt) const
2306 {
2307   switch( stmt->get_type() ) {
2308   case statement::base::STMT_FRIEND_CLASS:
2309   case statement::base::STMT_FRIEND_FUNCTION:
2310   case statement::base::STMT_CLASS_END:
2311   case statement::base::STMT_COMMENT_PKGDOC:
2312   case statement::base::STMT_COMMENT_PKGDOC_URL:
2313   case statement::base::STMT_COMMENT_PREFIX:
2314   case statement::base::STMT_COMMENT_SUFFIX:
2315   case statement::base::STMT_IGNORE:
2316   case statement::base::STMT_NAMESPACE_END:
2317   case statement::base::STMT_STRUCT_END:
2318   case statement::base::STMT_UNION_END:
2319   case statement::base::STMT_MACROINST_FUNCTION:
2320   case statement::base::STMT_MACROINST_VARIABLE:
2321     // Ignore some of the statements all of the time.
2322     return true;
2323   case statement::base::STMT_MACRODEF_0_0:
2324   case statement::base::STMT_MACRODEF_0_1:
2325   case statement::base::STMT_MACRODEF_0_N:
2326   case statement::base::STMT_MACRODEF_N_N:
2327     if( !m_sw.rptmac() )
2328       return true;
2329     return false;
2330   case statement::base::STMT_TYPEDEF_FUNCTION:
2331   case statement::base::STMT_TYPEDEF_VARIABLE:
2332     if( !m_sw.rpttyp() )
2333       return true;
2334     // Don't return false until we check the
2335     // accessibility.
2336     break;
2337   case statement::base::STMT_UNION_BEGIN:
2338     if( !m_sw.rptun() )
2339       return true;
2340     // Don't return false until we check the
2341     // accessibility.
2342     break;
2343 #if 0
2344     // Issue 0073
2345     // This section of code was commented out because
2346     // ccdoc was reporting private methods when -norptpri was
2347     // specified.
2348   case statement::base::STMT_METHOD_CONSTRUCTOR:
2349   case statement::base::STMT_METHOD_DESTRUCTOR:
2350     // Ccdoc never ignores constructors, destructors and
2351     // assignment operators.
2352     return false;
2353   case statement::base::STMT_FUNCTION_OPERATOR:
2354   case statement::base::STMT_METHOD_OPERATOR:
2355     // Ccdoc never ignores assignment operators.
2356     if( stmt->get_id() ) {
2357       string id = stmt->get_id();
2358       if( id == "operator =" )
2359 	return false;
2360     }
2361     break;
2362 #endif
2363   case statement::base::STMT_PACKAGE:
2364     if( stmt->get_parent() == m_db.root() ) {
2365       string id = stmt->get_id();
2366       if( id == "@null" || id == "[NULL]" ) {
2367 	// Ignore top level packages that have the names
2368 	// @null and [NULL].
2369 	return true;
2370       }
2371     }
2372     break;
2373   case statement::base::STMT_NAMESPACE_BEGIN:
2374     // Skip the internal ids of the form:
2375     //   +<name>+<file>:<line>
2376     if( stmt->get_id() && stmt->get_id()[0] == '+' )
2377       return true;
2378     break;
2379   default:
2380     break;
2381   }
2382 
2383   // Check the accessibility.
2384   switch( stmt->get_access() ) {
2385   case statement::base::STMT_PRIVATE:
2386     return !m_sw.rptpri();
2387   case statement::base::STMT_PROTECTED:
2388     return !m_sw.rptpro();
2389   case statement::base::STMT_PUBLIC:
2390     return !m_sw.rptpub();
2391   default:
2392     break;
2393   }
2394 
2395   return false;
2396 }
2397 // ================================================================
2398 // Write the package index html.
2399 // ================================================================
make_pkg_index_children(statement::base * stmt,statement::base::stmts_t & outvec)2400 void ccdoc::phase3::html::make_pkg_index_children(statement::base* stmt,
2401 						  statement::base::stmts_t& outvec)
2402 {
2403   if( stmt ) {
2404     if( stmt->get_type() != statement::base::STMT_NAMESPACE_BEGIN &&
2405 	stmt->get_type() != statement::base::STMT_PACKAGE ) {
2406       return;
2407     }
2408     outvec.push_back(stmt);
2409     statement::base::stmts_t& vec = stmt->get_children();
2410     statement::base::stmts_itr_t itr = vec.begin();
2411     for(;itr!=vec.end();++itr) {
2412       make_pkg_index_children(*itr,outvec);
2413     }
2414   }
2415 }
2416 // ================================================================
2417 // Get the tag value for a statement.
2418 // ================================================================
make_tag_id(const statement::base * stmt,string & id) const2419 void ccdoc::phase3::html::make_tag_id(const statement::base* stmt,
2420 				      string& id) const
2421 {
2422   if(stmt) {
2423     id = stmt->get_terse_type_name();
2424     id += "-";
2425     id += format_name(stmt->get_id());
2426     if( stmt->get_type() == statement::base::STMT_FUNCTION ||
2427 	stmt->get_type() == statement::base::STMT_FUNCTION_OPERATOR ||
2428 	stmt->get_type() == statement::base::STMT_METHOD_CONSTRUCTOR ||
2429 	stmt->get_type() == statement::base::STMT_METHOD_DESTRUCTOR ||
2430 	stmt->get_type() == statement::base::STMT_METHOD_OPERATOR ||
2431 	stmt->get_type() == statement::base::STMT_METHOD ) {
2432       // Append uniquifying tokens:
2433       const statement::base::cstrs_t& vec = stmt->get_tokens();
2434       statement::base::cstrs_citr_t itr = vec.begin();
2435       for(;itr!=vec.end();++itr) {
2436 	id += "-";
2437 	id += format_name( *itr );
2438       }
2439     }
2440   }
2441 }
2442 // ================================================================
2443 // Get the parent statement that defines a file entity.
2444 // ================================================================
2445 const ccdoc::statement::base*
get_file_stmt(const statement::base * stmt) const2446 ccdoc::phase3::html::get_file_stmt(const statement::base* stmt) const
2447 {
2448   // Other statements, such as functions are only in separate
2449   // files if they are at the top level. Otherwise they are
2450   // subordinated to a tag within a file.
2451   for(;stmt;stmt=stmt->get_parent()) {
2452     if( stmt->get_parent() == 0 )
2453       return stmt;
2454     // The only statement that does not have its own file is a
2455     // statement that is embedded in a class unless it is a subclass.
2456     if( stmt->get_type() == statement::base::STMT_CLASS_BEGIN ||
2457 	stmt->get_type() == statement::base::STMT_STRUCT_BEGIN ||
2458 	stmt->get_type() == statement::base::STMT_UNION_BEGIN ) {
2459       // Classes (even subclasses) always have their own files.
2460       return stmt;
2461     }
2462     if( stmt->get_parent()->get_type() != statement::base::STMT_CLASS_BEGIN &&
2463 	stmt->get_parent()->get_type() != statement::base::STMT_STRUCT_BEGIN &&
2464 	stmt->get_parent()->get_type() != statement::base::STMT_UNION_BEGIN ) {
2465       // Other statements that are not embedded in classes
2466       // always have their own files.
2467       return stmt;
2468     }
2469   }
2470   return stmt;
2471 }
2472 // ================================================================
2473 // Make file URL, strip off the leading directories.
2474 // ================================================================
make_file_url(string & url,string & fn) const2475 void ccdoc::phase3::html::make_file_url(string& url,string& fn) const
2476 {
2477   // Strip off the leading directories to
2478   // make the relative reference work.
2479   string::iterator itr = fn.begin();
2480   string::iterator relpath_itr = fn.begin();
2481   for(;itr!=fn.end();++itr) {
2482     if( '/' == *itr ) {
2483       relpath_itr = itr;
2484       ++relpath_itr; // Issue 0001 FIX
2485     }
2486   }
2487   url = "";
2488   for(;relpath_itr!=fn.end();++relpath_itr) {
2489     url += *relpath_itr;
2490   }
2491 }
2492 // ================================================================
2493 // Make file URL
2494 // ================================================================
make_file_url(string & url,const statement::base * stmt)2495 bool ccdoc::phase3::html::make_file_url(string& url,
2496 					const statement::base* stmt)
2497 {
2498   if( stmt ) {
2499     const statement::base* file_stmt = get_file_stmt(stmt);
2500     string fn;
2501     make_unique_file_name(fn,file_stmt);
2502     make_file_url(url,fn);
2503 
2504     // If the parent is the file statement, a tag needs to be
2505     // generated, return true.
2506     return file_stmt == stmt->get_parent();
2507   }
2508   return false;
2509 }
2510 // ================================================================
2511 // Make a unique file name that consistent between runs.
2512 // ================================================================
make_unique_file_name(string & fn,const statement::base * stmt)2513 void ccdoc::phase3::html::make_unique_file_name(string& fn,
2514 						const statement::base* stmt)
2515 {
2516   if(stmt) {
2517     if( stmt->get_parent() == 0 ) {
2518       // This is the top node.
2519       // Perform special processing here to support the -rootfile
2520       // switch.
2521       if( m_sw.rootfile().size() ) {
2522 	fn = m_sw.rootfile();
2523 	return;
2524       }
2525     }
2526     bool append_tokens = false;
2527     if( stmt->get_type() == statement::base::STMT_FUNCTION ||
2528 	stmt->get_type() == statement::base::STMT_FUNCTION_OPERATOR ||
2529 	stmt->get_type() == statement::base::STMT_METHOD_CONSTRUCTOR ||
2530 	stmt->get_type() == statement::base::STMT_METHOD_DESTRUCTOR ||
2531 	stmt->get_type() == statement::base::STMT_METHOD_OPERATOR ||
2532 	stmt->get_type() == statement::base::STMT_METHOD ) {
2533       append_tokens = true;
2534     }
2535     fn = m_sw.html() + "ccdoc";
2536 
2537     // ================================================
2538     // Get the hierarchical names.
2539     // ================================================
2540     statement::base::stmts_t parents;
2541     stmt->get_parents(parents);
2542     if(parents.size()) {
2543       statement::base::stmts_itr_t itr = parents.begin();
2544 
2545       // Handle the first argument as a special case.
2546       // We don't want the '@' sign in the file name.
2547       fn += ".";
2548       if( m_sw.default_root() == (*itr)->get_id() ) {
2549 	fn += "root"; // strip of the leading @ for the file name.
2550       }
2551       else {
2552 	fn += format_name( (*itr)->get_id() );
2553       }
2554       ++itr;
2555       for(;itr!=parents.end();++itr) {
2556 	fn += ".";
2557 	fn += format_name( (*itr)->get_id() );
2558       }
2559       // Tack on the statement id.
2560       fn += ".";
2561       fn += format_name( stmt->get_id() );
2562     }
2563     else {
2564       // This is the root package statement.
2565       // Handle the first argument as a special case.
2566       // We don't want the '@' sign in the file name.
2567       fn += ".";
2568       if( m_sw.default_root() == stmt->get_id() ) {
2569 	fn += "root"; // strip of the leading @ for the file name.
2570       }
2571       else {
2572 	fn += format_name( stmt->get_id() );
2573       }
2574     }
2575     string fn1 = fn;
2576 
2577     // ================================================
2578     // Append the tokens for uniqueness for some types.
2579     // ================================================
2580     if( append_tokens ) {
2581       const statement::base::cstrs_t& vec = stmt->get_tokens();
2582       statement::base::cstrs_citr_t itr = vec.begin();
2583       for(;itr!=vec.end();++itr) {
2584 	fn += ".";
2585 	fn += format_name( *itr );
2586       }
2587     }
2588 
2589     // ================================================
2590     // Append the type and html suffix.
2591     // ================================================
2592     fn += ".";
2593     fn += stmt->get_terse_type_name();
2594     fn += ".html";
2595     if( m_sw.maxpathlen() && fn.size() > m_sw.maxpathlen() ) {
2596       // Generate a checksum if the file name is too big.
2597       checksum c;
2598       c.set(fn.c_str());
2599 
2600       char nbuf[32];
2601       sprintf(nbuf,"%08x",c.get());
2602 
2603       fn = fn1;
2604       fn += ".checksum.";
2605       fn += nbuf;
2606       fn += ".";
2607       fn += stmt->get_terse_type_name();
2608       fn += ".html";
2609     }
2610   }
2611 }
2612 // ================================================================
2613 // Format the characters in an id so they are suitable for a file
2614 // name.
2615 // ================================================================
format_name(const char * token) const2616 const char* ccdoc::phase3::html::format_name(const char* token) const
2617 {
2618   static char out[65536];
2619   char nbuf[16];
2620   const char* src = token;
2621   char* dst = out;
2622   for(;*src;++src) {
2623     if( *src <= 32 || *src >= 127 ) {
2624       unsigned val = static_cast<unsigned>(*src);
2625       sprintf(nbuf,"-%02x",val);
2626       for(const char* p=nbuf;*p;++p)
2627 	*dst++ = *p;
2628     }
2629     else {
2630       if( '{' == *src ) {
2631 	// This only occurs for enum types and is
2632 	// not necessary for uniquifying the name.
2633 	*dst = 0;
2634 	return out;
2635       }
2636       switch( *src ) {
2637       case '(':
2638       case ')':
2639       case '[':
2640       case ']':
2641       case '<':
2642       case '>':
2643       case ';':
2644       case ':':
2645       case '~':
2646       case '?':
2647       case '=':
2648       case ',':
2649       case '@':
2650       case '&':
2651       case '*':
2652       case '"':
2653       case '\'':
2654       case '/': // Issue 0018: filter embedded directory separators
2655       case '|': // Issue 0112: handle operator |
2656       case '\\':
2657 	{
2658 	  unsigned val = static_cast<unsigned>(*src);
2659 	  sprintf(nbuf,"-%02x",val);
2660 	  for(const char* p=nbuf;*p;++p)
2661 	    *dst++ = *p;
2662 	  break;
2663 	}
2664       default:
2665 	*dst++ = *src;
2666 	break;
2667       }
2668     }
2669   }
2670   *dst = 0;
2671   return out;
2672 }
2673 // ================================================================
2674 // format string (format for html)
2675 // ================================================================
format_string_for_html(const char * pstr) const2676 const char* ccdoc::phase3::html::format_string_for_html(const char* pstr) const
2677 {
2678   static char id[65536];
2679   char* p = id;
2680   if(pstr) {
2681     for(;*pstr;++pstr) {
2682       if( *pstr == '<' ) {
2683 	*p++ = '&';
2684 	*p++ = 'l';
2685 	*p++ = 't';
2686 	*p++ = ';';
2687       }
2688       else if( *pstr == '>' ) {
2689 	*p++ = '&';
2690 	*p++ = 'g';
2691 	*p++ = 't';
2692 	*p++ = ';';
2693       }
2694       else if( *pstr == '&' ) {
2695 	*p++ = '&';
2696 	*p++ = 'a';
2697 	*p++ = 'm';
2698 	*p++ = 'p';
2699 	*p++ = ';';
2700       }
2701       else {
2702 	*p++ = *pstr;
2703       }
2704     }
2705   }
2706   *p = 0;
2707   return id;
2708 }
2709 // ================================================================
2710 // format string (format for html)
2711 // ================================================================
2712 const char*
format_string_for_html(const string & str) const2713 ccdoc::phase3::html::format_string_for_html(const string& str) const
2714 {
2715   return format_string_for_html(str.c_str());
2716 }
2717 // ================================================================
2718 // write string (format for html)
2719 // ================================================================
write_html_formatted_string(ostream & os,const char * str) const2720 void ccdoc::phase3::html::write_html_formatted_string(ostream& os,
2721 						      const char* str) const
2722 {
2723   os << format_string_for_html(str);
2724 }
2725 // ================================================================
2726 // write string (format for html)
2727 // ================================================================
write_html_formatted_string(ostream & os,const string & str) const2728 void ccdoc::phase3::html::write_html_formatted_string(ostream& os,
2729 						      const string& str) const
2730 {
2731   write_html_formatted_string(os,str.c_str());
2732 }
2733 // ================================================================
2734 // write common header info
2735 // ================================================================
write_common_header_info(ostream & os,const string & fn,const char * title)2736 void ccdoc::phase3::html::write_common_header_info(ostream& os,
2737 						   const string& fn,
2738 						   const char* title)
2739 {
2740   os << "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
2741      << "<!--NewPage-->\n"
2742      << "<html>\n"
2743      << "<head>\n"
2744     ;
2745   if(m_meta.size()) {
2746     // Issue 0003: Write out the custom meta variables.
2747     os << m_meta << endl;
2748   }
2749   else {
2750     // Issue 0059:
2751     // Write out the content type to satisfy the HTML verifier
2752     // at http://validator.w3.org/.
2753     // Issue 0076:
2754     // Don't write out standard HTML meta elements if the -meta
2755     // switch was specified.
2756     os << "<meta http-equiv=\"Content-Type\" ";
2757     os << "content=\"text/html; ";
2758     os << "charset=" << m_sw.rptctcs() << "\">\n";
2759   }
2760   write_meta(os,"ccdoc_copyright"     ,"(C) Joe Linoff 1998-2001");
2761   write_meta(os,"ccdoc_author"        ,"Joe Linoff");
2762   write_meta(os,"ccdoc_version"       ,m_sw.version().c_str());
2763   write_meta(os,"ccdoc_file"          ,fn.c_str());
2764   write_meta(os,"ccdoc_creation_date" ,date_time());
2765   write_meta(os,"keywords"            ,"ccdoc, source code, documentation");
2766   os << "<title>" << title << "</title>\n";
2767   os << "</head>\n" << "\n";
2768 
2769   // ================================================
2770   // Write out the <body> prefix, including the
2771   // optional prefix that was supplied by the
2772   // user.
2773   // ================================================
2774   os << "<body";
2775   if(m_sw.bgcolor().size()) {
2776     os << " bgcolor=" << m_sw.bgcolor().c_str();
2777   }
2778   if(m_sw.fgtextcolor().size()) {
2779     os << " text=" << m_sw.fgtextcolor().c_str();
2780   }
2781   if(m_sw.fglinkcolor().size()) {
2782     os << " link=" << m_sw.fglinkcolor().c_str();
2783   }
2784   if(m_sw.fgvlinkcolor().size()) {
2785     os << " vlink=" << m_sw.fgvlinkcolor().c_str();
2786   }
2787   os << ">\n";
2788   os << "<a name=ccdoc_top></a>\n"; // Issue 0002
2789   if(m_header.size()) {
2790     os << m_header << endl;
2791   }
2792 }
2793 // ================================================================
2794 // write common header info
2795 // ================================================================
write_common_header_info(ostream & os,const string & fn,statement::base * stmt)2796 void ccdoc::phase3::html::write_common_header_info(ostream& os,
2797 						   const string& fn,
2798 						   statement::base* stmt)
2799 {
2800   string title = "ccdoc ";
2801   if( stmt ) {
2802     char nbuf[32];
2803     sprintf(nbuf,"%d",stmt->get_lineno());
2804     title += format_string_for_html(stmt->get_id());
2805     title += " ";
2806     title += stmt->get_file();
2807     title += ":";
2808     title += nbuf; // Issue 0010
2809     title += ":";
2810     title += stmt->get_type_name2();
2811   }
2812   else {
2813     title += "<null>";
2814   }
2815   write_common_header_info(os,fn,title.c_str());
2816 
2817   // ================================================
2818   // Write out the package/namespace path:
2819   // ================================================
2820   if( stmt ) {
2821     os << "<table border=0 width=\"100%\"><tr><td align=left>\n";
2822     statement::base::stmts_t parents;
2823     stmt->get_parents(parents);
2824     if( parents.size() ) {
2825       statement::base::stmts_itr_t itr = parents.begin();
2826       statement::base* parent = *itr;
2827 
2828       // If the user specfied -rooturl on the command line,
2829       // output it.
2830       if( m_sw.rooturl().size() ) {
2831 	os << "<a href=\"" << m_sw.rooturl() << "\" target=_top>Home</a>\n";
2832 	os << " :: ";
2833       }
2834 
2835       // Now write out the path.
2836       write_link(os,parent);
2837       os << "\n";
2838       itr++;
2839       for(;itr!=parents.end();++itr) {
2840 	parent = *itr;
2841 	os << " :: ";
2842 	write_link(os,parent);
2843 	os << "\n";
2844       }
2845     }
2846     else {
2847       // This is the root.
2848       // Write out the Home link.
2849       if( m_sw.rooturl().size() ) {
2850 	os << "<a href=\"" << m_sw.rooturl() << "\" target=_top>Home</a>\n";
2851       }
2852     }
2853     os << "</td><td align=right>";
2854     string url;
2855     string link_name = m_sw.html() + "ccdoc.class_summary.html";
2856     make_file_url(url,link_name);
2857     os << "<a href=\"" << url << "\">"
2858        << "classes</a></td></tr></table>\n";
2859   }
2860 }
2861 // ================================================================
2862 // Make the absolute path.
2863 // ================================================================
make_abs_path(statement::base * stmt,string & link) const2864 void ccdoc::phase3::html::make_abs_path(statement::base* stmt,
2865 					string& link) const
2866 {
2867   int j = 0;
2868   statement::base::stmts_t parents;
2869   stmt->get_parents(parents);
2870   if( parents.size() ) {
2871     statement::base::stmts_itr_t itr = parents.begin();
2872     ++itr; // skip the root
2873     for(;itr!=parents.end();++itr,++j) {
2874       if( j )
2875 	link += "::";
2876       link += (*itr)->get_id();
2877     }
2878   }
2879   if( j )
2880     link += "::";
2881   link += stmt->get_id();
2882 }
2883 // ================================================================
2884 // Make relative path.
2885 // ================================================================
make_rel_path(statement::base * parent,statement::base * stmt,string & link) const2886 void ccdoc::phase3::html::make_rel_path(statement::base* parent,
2887 					statement::base* stmt,
2888 					string& link) const
2889 {
2890   int j = 0;
2891   statement::base::stmts_t parents;
2892   stmt->get_parents(parents);
2893   if( parents.size() ) {
2894     statement::base::stmts_itr_t itr = parents.begin();
2895     ++itr; // skip the root
2896     // Skip until we find the relative top.
2897     for(;itr!=parents.end();++itr) {
2898       if( *itr == parent ) {
2899 	// This is the parent,
2900 	// point to the child.
2901 	++itr;
2902 	break;
2903       }
2904     }
2905     for(;itr!=parents.end();++itr,++j) {
2906       if( j )
2907 	link += "::";
2908       link += (*itr)->get_id();
2909     }
2910   }
2911   if( j )
2912     link += "::";
2913   link += stmt->get_id();
2914 }
2915 // ================================================================
2916 // Find and write out a links.
2917 // ================================================================
find_and_write_links(ostream & os,const char * in_link,const char * in_link_id,statement::base * scope)2918 unsigned ccdoc::phase3::html::find_and_write_links(ostream& os,
2919 						   const char* in_link,
2920 						   const char* in_link_id,
2921 						   statement::base* scope)
2922 {
2923   // Nothing is written if there is no valid link.
2924   unsigned count = 0;
2925   if( in_link && in_link_id ) {
2926     string link = in_link;
2927     // Check for a leading # as in:
2928     //   {@link #Fct}
2929     if( in_link[0] == '#' ) {
2930       // Issue 0092:
2931       if( !scope )
2932 	return count;
2933       scope->get_hier_id_no_pkgs(link);
2934       if( link.size() )
2935 	link += "::";
2936       link += &in_link[1]; // strip off the leading '#'
2937     }
2938     statement::base::stmts_t stmts;
2939     m_db.get_stmt_no_pkgs(link,stmts);
2940     if( stmts.size() ) {
2941       statement::base::stmts_t output_stmts;
2942       statement::base::stmts_itr_t itr1 = stmts.begin();
2943       for(;itr1!=stmts.end();++itr1) {
2944 	statement::base* stmt = *itr1;
2945 	if( stmt->get_type() != statement::base::STMT_CLASS_END &&
2946 	    stmt->get_type() != statement::base::STMT_NAMESPACE_END &&
2947 	    stmt->get_type() != statement::base::STMT_STRUCT_END &&
2948 	    stmt->get_type() != statement::base::STMT_UNION_END ) {
2949           if( ignore_contents_stmt(stmt) == false )
2950             output_stmts.push_back(stmt);
2951 	}
2952       }
2953       count = output_stmts.size();
2954       write_links(os,output_stmts,in_link_id);
2955     }
2956   }
2957   return count;
2958 }
2959 // ================================================================
2960 // Write out multiple links to the same reference in a separate
2961 // file.
2962 // ================================================================
write_links(ostream & os,const statement::base::stmts_t & stmts,const char * link_name)2963 void ccdoc::phase3::html::write_links(ostream& os,
2964 				      const statement::base::stmts_t& stmts,
2965 				      const char* link_name)
2966 {
2967   if( stmts.size() == 0 || link_name == 0 )
2968     return;
2969   if( stmts.size() == 1 ) {
2970     write_link(os,stmts[0],link_name);
2971     return;
2972   }
2973 
2974   // Issue 0111
2975   // Issue 0083
2976   // First generate the new HTML file name.
2977   // Generate a unique file name for the multiple
2978   // links file so that they can be re-used.
2979   string fn = m_sw.html() + "ccdoc";
2980   const statement::base* stmt = stmts[0];
2981   statement::base::stmts_t parents;
2982   stmt->get_parents(parents);
2983   if(parents.size()) {
2984     statement::base::stmts_itr_t itr = parents.begin();
2985 
2986     // Handle the first argument as a special case.
2987     // We don't want the '@' sign in the file name.
2988     fn += ".";
2989     if( m_sw.default_root() == (*itr)->get_id() ) {
2990       fn += "root"; // strip of the leading @ for the file name.
2991     }
2992     else {
2993       fn += format_name( (*itr)->get_id() );
2994     }
2995     ++itr;
2996     for(;itr!=parents.end();++itr) {
2997       fn += ".";
2998       fn += format_name( (*itr)->get_id() );
2999     }
3000     // Tack on the statement id.
3001     fn += ".";
3002     fn += format_name( stmt->get_id() );
3003   }
3004   else {
3005     // This is the root package statement.
3006     // Handle the first argument as a special case.
3007     // We don't want the '@' sign in the file name.
3008     fn += ".";
3009     if( m_sw.default_root() == stmt->get_id() ) {
3010       fn += "root"; // strip of the leading @ for the file name.
3011     }
3012     else {
3013       fn += format_name( stmt->get_id() );
3014     }
3015   }
3016   fn += ".xrf.html";
3017 
3018   // Make the url.
3019   string url;
3020   make_file_url(url,fn);
3021 
3022   // Now write out the link to this file.
3023   // Use italics as a visual cue to indicate that there are multiple
3024   // links.
3025   os << "<a href=\"" << url << "\">" << link_name << "</a>";
3026 
3027   // See whether the file exists.
3028   bool exists = true;
3029   {
3030     ifstream is(fn.c_str());
3031     if(!is) {
3032       exists = false;
3033     }
3034   }
3035 
3036   // If the link file doesn't exist, create it.
3037   if( !exists ) {
3038     ofstream os1(fn.c_str());
3039     if(!os) {
3040       throw ccdoc::exceptions::unwriteable_output_file(__FILE__,
3041                                                        __LINE__,
3042                                                        fn.c_str());
3043     }
3044     string title;
3045     title = "ccdoc xrf links for ";
3046     title += link_name;
3047     write_common_header_info(os1,fn,title.c_str());
3048 
3049     // Populate the file.
3050     os1 << "<center>\n";
3051     os1 << "<h3>Multiply defined links for " << link_name << "</h3>\n";
3052     os1 << "<table border=1 cellspacing=1 cellpadding=2>";
3053     os1 << "<tr>";
3054     os1 << "<th>Link</th>"
3055         << "<th>Name</th>"
3056         << "<th>Type</th>"
3057         << "<th>Source</th>"
3058         << "<th>Short Description</th>";
3059     os1 << "</tr>\n";
3060     statement::base::stmts_citr_t itr = stmts.begin();
3061     for(;itr!=stmts.end();++itr) {
3062       statement::base* stmt = *itr;
3063 
3064       // Link
3065       os1 << "<tr><td>";
3066       write_link(os1,stmt,link_name);
3067 
3068       // Name
3069       os1 << "</td><td>";
3070       string id;
3071       stmt->get_hier_id_no_pkgs(id);
3072       os1 << id;
3073 
3074       // Type
3075       os1 << "</td><td>";
3076       os1 << stmt->get_type_name2();
3077 
3078       // Path
3079       os1 << "</td><td>";
3080       string path;
3081       write_ccdoc_src_info(path,stmt);
3082       if( path.size() )
3083         os1 << path;
3084 
3085       // Short description.
3086       os1 << "</td><td>\n";
3087       write_short_desc(os1,stmt);
3088 
3089       os1 << "</td></tr>\n";
3090     }
3091     os1 << "</table>\n";
3092     os1 << "</center>\n";
3093 
3094     write_common_trailer_info(os1);
3095   }
3096 }
3097 // ================================================================
3098 // Write out a link.
3099 // ================================================================
write_link(ostream & os,const statement::base * stmt,const char * link_name)3100 void ccdoc::phase3::html::write_link(ostream& os,
3101 				     const statement::base* stmt,
3102 				     const char* link_name)
3103 {
3104   if( stmt ) {
3105     // There are three cases that have to be handled here:
3106     //
3107     //   Case 1: The statement is an object in a standalone file.
3108     //   Case 2: The statement is a tag in an existing file.
3109     //   Case 3: A package with a user defined URL.
3110     //
3111     // Case 2 occurs when the stmt is not a class and the parent is
3112     // not a namespace or a package.
3113     string url;
3114     string tag;
3115     if( statement::base::STMT_PACKAGE == stmt->get_type() &&
3116 	stmt->get_comment() ) {
3117       // Issue 0025
3118       // The user specified a custom URL. Use the package name
3119       // along with the users URL.
3120       // The URL is obtained from the associated comment.
3121       statement::comment doc(stmt->get_comment());
3122       url = doc.get_pkgdoc_url();
3123     }
3124     if( url.size() == 0 ) {
3125       if ( make_file_url(url,stmt) ) {
3126 	string id;
3127 	make_tag_id(stmt,id);
3128 	tag = "#" + id;
3129       }
3130     }
3131     // Write out the link.
3132     os << "<a href=\"" << url << tag << "\">";
3133     if( link_name ) {
3134       write_html_formatted_string(os,link_name);
3135     }
3136     else {
3137       write_html_formatted_string(os,stmt->get_id());
3138     }
3139     os << "</a>";
3140   }
3141 }
3142 // ================================================================
3143 // write meta info
3144 // ================================================================
write_meta(ostream & os,const char * name,const char * content)3145 void ccdoc::phase3::html::write_meta(ostream& os,
3146 				     const char* name,
3147 				     const char* content)
3148 {
3149   os << "<meta name=\""
3150      << name
3151      << "\" content=\""
3152      << content
3153      << "\">\n";
3154 }
3155 // ================================================================
3156 // write common trailer info
3157 // ================================================================
write_common_trailer_info(ostream & os)3158 void ccdoc::phase3::html::write_common_trailer_info(ostream& os)
3159 {
3160   os << "<a name=ccdoc_bottom></a>\n"
3161      << "<hr>\n";
3162   if(m_trailer.size()) {
3163     // The user specified a customized trailer, use it.
3164     os << m_trailer << endl;
3165   }
3166   else {
3167     // Output the default message.
3168     // Note that the copyright is not displayed here because
3169     // I don't own the contents of the page.
3170     os << "<center>\n"
3171        << "<p>\n"
3172        << "<font size=\"-1\">\n"
3173        << "Created " << date_time() << ".\n"
3174        << "<br>\n"
3175        << "This documentation was generated automatically by\n"
3176        << "<br>\n"
3177        << m_sw.version() << ".\n"
3178        << "<br>\n"
3179        << "Click "
3180        << "<a href=\"mailto:joe@joelinoff.com,jdl@xilinx.com"
3181        << "?subject="
3182        << "Bug report or feature request for "
3183        << m_sw.version()
3184        << "\">here</a>"
3185        << " to submit a bug report or feature request for ccdoc.\n"
3186        << "<br>\n"
3187        << "Click <a href=\"#ccdoc_top\">here</a>"
3188        << " to return to the top of the page.\n"
3189        << "</font>\n"
3190        << "</center>\n"
3191       ;
3192   }
3193   os << "</body>\n"
3194      << "</html>\n"
3195     ;
3196 }
3197 // ================================================================
3198 // Write the extends clause for classes and structs.
3199 // ================================================================
write_extends_clause(ostream & os,statement::base * stmt)3200 void ccdoc::phase3::html::write_extends_clause(ostream& os,
3201 					       statement::base* stmt)
3202 {
3203   if( stmt ) {
3204     // Write the extends clause.
3205     // extends <class> as <public|protected|private>
3206     bool first = true;
3207     string extends_prefix = "&nbsp;&nbsp;&nbsp;&nbsp;extends ";
3208     const statement::base::cstrs_t& vec1 = stmt->get_tokens();
3209     statement::base::cstrs_citr_t itr1 = vec1.begin();
3210     for(;itr1!=vec1.end();++itr1) {
3211       string token = *itr1;
3212       if( token == ":" ) {
3213 	itr1++;
3214 	break;
3215       }
3216     }
3217     if( itr1!=vec1.end() ) {
3218       // This is a derived class of the form:
3219       //   class A : ... { ... };
3220       statement::base::cstrs_citr_t end = itr1;
3221       while( end!=vec1.end() ) {
3222 	string id;
3223 	statement::base::strs_t access;
3224 	string token;
3225 
3226 	// ================================================
3227 	// Get the type and the access specifiers.
3228 	// Do this the easy way.
3229 	// ================================================
3230 	bool found_id = false;
3231 	int depth = 0;
3232 	for(;end!=vec1.end();++end) {
3233 	  token = *end;
3234 	  if( !found_id ) {
3235 	    if( token == "virtual" ||
3236 		token == "public" ||
3237 		token == "protected" ||
3238 		token == "private" ) {
3239 	      access.push_back(token);
3240 	      continue;
3241 	    }
3242 	    found_id = true;
3243 	  }
3244 	  if( found_id ) {
3245 	    if( token == "<" ) {
3246 	      // This handles cases like:
3247 	      //   class A : public foo<My1,My2> ;
3248 	      //   class A : public foo<My1,My2<XX> > ;
3249 	      depth++;
3250 	    }
3251 	    else if( token == ">" ) {
3252 	      depth--;
3253 	    }
3254 	    else if( depth == 0 ) {
3255 	      if( token == "," ) {
3256 		// Output the extends information.
3257 		++end;
3258 		break;
3259 	      }
3260 	    }
3261 	    id += token;
3262 	  }
3263 	}
3264 
3265 	// ================================================
3266 	// At this point the id and access vector are set.
3267 	// ================================================
3268 	// Write out the extends clause.
3269 	if( !first )
3270 	  os << "<br>";
3271 	if( first )
3272 	  first = false;
3273 	os << extends_prefix;
3274 
3275 	// ================================================
3276 	// Write out the link.
3277 	// ================================================
3278 	statement::base::stmts_t id_class_stmts;
3279 	load_extend_classes(id,id_class_stmts,stmt);
3280 	if( id_class_stmts.size() == 0 ) {
3281 	  os << "<font color=red>"
3282 	     << format_string_for_html(id)
3283 	     << "</font>";
3284           // Issue 109:
3285           // The error condition for this can be created as follows:
3286           //   class Test : public Undefined {
3287           //   public:
3288           //     Test();
3289           //    ~Test();
3290           //   };
3291           s_log.warning()
3292             << "UNDEF: Undefined reference in extends clause for '"
3293             << id
3294             << "' in "
3295             << stmt->get_type_name2()
3296             << " '"
3297             << stmt->get_id()
3298             << "' at line "
3299             << stmt->get_lineno()
3300             << " in file "
3301             << stmt->get_file()
3302             << "\n"
3303             << s_log.enable();
3304 	}
3305 	else if( id_class_stmts.size() == 1 ) {
3306 	  write_link(os,id_class_stmts[0],id.c_str());
3307 	}
3308 	else {
3309 	  // There are too many links.
3310 	  // Write all of the links.
3311 	  statement::base::stmts_itr_t sitr = id_class_stmts.begin();
3312 	  os << "{ ";
3313 	  for(;sitr!=id_class_stmts.end();++sitr) {
3314 	    statement::base* id_stmt = *sitr;
3315 	    if( sitr != id_class_stmts.begin() )
3316 	      os << ", ";
3317 	    write_link(os,id_stmt,id.c_str());
3318 	  }
3319 	  os << " }";
3320 	}
3321 
3322 	// ================================================
3323 	// Write out the "as" clause.
3324 	// ================================================
3325 	os << " as";
3326 	if( access.size() ) {
3327 	  statement::base::strs_itr_t titr = access.begin();
3328 	  for(;titr!=access.end();++titr) {
3329 	    os << " " << *titr;
3330 	  }
3331 	}
3332 	else {
3333 	  // 11.2 ISO/IEC 14882:1998(E)
3334 	  if( stmt->get_type() == statement::base::STMT_CLASS_BEGIN )
3335 	    os << " private";
3336 	  else if( stmt->get_type() == statement::base::STMT_STRUCT_BEGIN )
3337 	    os << " public";
3338 	  else {
3339 	    os << " <font color=red>unknown</font>";
3340             // Issue 109:
3341             // TODO: Don't know how to trigger this for testing.
3342             s_log.warning()
3343               << "UNDEF: Undefined type '"
3344               << stmt->get_type_name2()
3345               << " for '"
3346               << stmt->get_id()
3347               << "' at line "
3348               << stmt->get_lineno()
3349               << " in file "
3350               << stmt->get_file()
3351               << "\n"
3352               << s_log.enable();
3353           }
3354 	}
3355 	os << "\n";
3356       }
3357       os << "<p>\n";
3358     }
3359   }
3360 }
3361 // ================================================================
3362 // Write out the friends info
3363 // ================================================================
write_friends_info(ostream & os,statement::base * stmt)3364 void ccdoc::phase3::html::write_friends_info(ostream& os,
3365 					     statement::base* stmt)
3366 {
3367   statement::base* scope = stmt;
3368   statement::base::stmts_t friends;
3369   statement::base::stmts_t& children = stmt->get_children();
3370   statement::base::stmts_itr_t itr = children.begin();
3371   for(;itr!=children.end();++itr) {
3372     if( (*itr)->get_type() == statement::base::STMT_FRIEND_CLASS ||
3373 	(*itr)->get_type() == statement::base::STMT_FRIEND_FUNCTION ) {
3374       friends.push_back(*itr);
3375     }
3376   }
3377   if( friends.size() ) {
3378     if( friends.size() == 1 ) {
3379       os << "<dt><b>Friend:</b></dt><dd><table cellspacing=4>\n";
3380     }
3381     else {
3382       os << "<dt><b>Friends:</b></dt><dd><table cellspacing=4>\n";
3383     }
3384     itr = friends.begin();
3385     for(;itr!=friends.end();++itr) {
3386       statement::base* f = *itr;
3387 
3388       os << "<tr>\n";
3389 
3390       os << "<td align=left valign=top>\n";
3391       if( f->get_type() == statement::base::STMT_FRIEND_CLASS )
3392 	os << "<font color=green><b>class</b></font>";
3393       else if( f->get_type() == statement::base::STMT_FRIEND_FUNCTION )
3394 	os << "<font color=green><b>function</b></font>";
3395       os << "</td>\n";
3396 
3397       os << "<td align=left valign=top>\n";
3398       statement::base::stmts_t matches;
3399       string id = f->get_id();
3400       m_db.get_stmt_no_pkgs(id,matches);
3401       if( matches.size() == 0 ) {
3402 	// Issue 0064
3403 	// The name was not found, try looking in the namespace
3404 	// of the parent.
3405 	if(stmt->get_parent()) {
3406 	  stmt->get_parent()->get_hier_id_no_pkgs(id);
3407 	  id += "::";
3408 	  id += f->get_id();
3409 	  m_db.get_stmt_no_pkgs(id,matches);
3410 	}
3411       }
3412       if( matches.size() == 1 ) {
3413         //write_link(os,matches[0],f->get_id());
3414         write_friends_link(os,matches[0],f);
3415       }
3416       else if( matches.size() > 1 ) {
3417 	// Filter to match the type.
3418 	statement::base::stmts_t filter_matches;
3419 	statement::base::stmts_itr_t mitr = matches.begin();
3420 	for(;mitr!=matches.end();++mitr) {
3421 	  if( f->get_type() == statement::base::STMT_FRIEND_CLASS ) {
3422 	    if( (*mitr)->get_type() == statement::base::STMT_CLASS_BEGIN ||
3423 		(*mitr)->get_type() == statement::base::STMT_STRUCT_BEGIN ) {
3424 	      filter_matches.push_back( *mitr );
3425 	      break; // avoid length searches for now
3426 	    }
3427 	  }
3428 	  else if( f->get_type() == statement::base::STMT_FRIEND_FUNCTION ) {
3429 	    if( (*mitr)->get_type() == statement::base::STMT_FUNCTION ||
3430 		(*mitr)->get_type() == statement::base::STMT_FUNCTION_OPERATOR ||
3431 		(*mitr)->get_type() == statement::base::STMT_METHOD_CONSTRUCTOR ||
3432 		(*mitr)->get_type() == statement::base::STMT_METHOD_DESTRUCTOR ||
3433 		(*mitr)->get_type() == statement::base::STMT_METHOD_OPERATOR ||
3434 		(*mitr)->get_type() == statement::base::STMT_METHOD ) {
3435 	      filter_matches.push_back( *mitr );
3436 	      break; // avoid length searches for now
3437 	    }
3438 	  }
3439 	}
3440 	if( filter_matches.size() ) {
3441 	  //write_link(os,filter_matches[0],f->get_id());
3442           write_friends_link(os,filter_matches[0],f);
3443 	}
3444 	else {
3445 	  //write_link(os,matches[0],f->get_id());
3446           write_friends_link(os,matches[0],f);
3447 	}
3448       }
3449       else {
3450         write_friends_link(os,0,f);
3451         // Issue 109:
3452         // The error condition for this can be created as follows:
3453         //   class Foo { friend class Undefined; }
3454         s_log.warning()
3455           << "UNDEF: Undefined friend reference to '"
3456           << f->get_id()
3457           << "' in "
3458           << stmt->get_type_name2()
3459           << " '"
3460           << stmt->get_id()
3461           << "' at line "
3462           << f->get_lineno()
3463           << " in file "
3464           << f->get_file()
3465           << "\n"
3466           << s_log.enable();
3467       }
3468       os << "</td>\n";
3469 
3470       os << "<td align=left valign=top>\n";
3471       if( f->get_comment() ) {
3472 	// Special case:
3473 	// Only the descriptions are written for friends.
3474 	statement::comment doc(f->get_comment());
3475 	write_ccdoc_desc_info(os,doc.get_short_desc(),scope,stmt);
3476 	if(doc.get_short_desc().size() && doc.get_long_desc().size())
3477 	  os << "<p>\n";
3478 	write_ccdoc_desc_info(os,doc.get_long_desc(),scope,stmt);
3479       }
3480       else {
3481 	os << m_sw.rptdefsd();
3482       }
3483       os << "</td>\n";
3484       os << "</tr>\n";
3485     }
3486     os << "</table>\n";
3487     os << "</dd>\n";
3488   }
3489 }
3490 // ================================================================
3491 // Write out the friends link
3492 // ================================================================
write_friends_link(ostream & os,statement::base * link_stmt,statement::base * friend_stmt)3493 void ccdoc::phase3::html::write_friends_link(ostream& os,
3494 					     statement::base* link_stmt,
3495                                              statement::base* friend_stmt)
3496 {
3497   if( link_stmt ) {
3498     write_link(os,link_stmt,friend_stmt->get_id());
3499   }
3500   else {
3501     os << "<font color=red>"
3502        << format_string_for_html(friend_stmt->get_id())
3503        << "</font>";
3504   }
3505   // Now write out the rest of the statement.
3506   const statement::base::cstrs_t& tokens = friend_stmt->get_tokens();
3507   statement::base::cstrs_citr_t itr = tokens.begin();
3508   for(;itr!=tokens.end();++itr) {
3509     string token = *itr;
3510     if( token == friend_stmt->get_id() ) {
3511       ++itr;
3512       break;
3513     }
3514   }
3515   for(;itr!=tokens.end();++itr) {
3516     string token = *itr;
3517     write_code_subsection_token(os,friend_stmt,token);
3518   }
3519 }
3520 // ================================================================
3521 // Write out the ccdoc information.
3522 // ================================================================
write_ccdoc_info(ostream & os,statement::base * stmt,bool author_required,bool version_required,bool inherited)3523 void ccdoc::phase3::html::write_ccdoc_info(ostream& os,
3524 					   statement::base* stmt,
3525 					   bool author_required,
3526 					   bool version_required,
3527 					   bool inherited)
3528 {
3529   statement::base* scope = stmt->get_parent();
3530   if( stmt->get_comment() ) {
3531     statement::comment doc(stmt->get_comment());
3532 
3533     // Short description.
3534     write_ccdoc_desc_info(os,doc.get_short_desc(),scope,stmt);
3535 
3536     if(doc.get_short_desc().size() && doc.get_long_desc().size())
3537       os << "<p>\n";
3538 
3539     // Long description.
3540     write_ccdoc_desc_info(os,doc.get_long_desc(),scope,stmt);
3541 
3542     os << "<dl>\n";
3543 
3544     // Inherited from
3545     if( inherited && stmt->get_parent() )
3546       write_inherited_from_info(os,stmt);
3547 
3548     // Source file
3549     write_ccdoc_src_info(os,stmt,scope);
3550 
3551     // Author
3552     string id = "Author";
3553     if(doc.get_authors().size()>1)
3554       id = "Authors";
3555     if( author_required )
3556       write_ccdoc_directive_info(os,id.c_str(),doc.get_authors(),m_sw.rptdefa(),scope,stmt,true);
3557     else
3558       write_ccdoc_directive_info(os,id.c_str(),doc.get_authors(),0,scope,stmt,true);
3559 
3560     // Version
3561     if( version_required )
3562       write_ccdoc_directive_info(os,"Version",doc.get_version(),m_sw.rptdefv(),scope,stmt);
3563     else
3564       write_ccdoc_directive_info(os,"Version",doc.get_version(),0,scope,stmt);
3565 
3566     // Since
3567     // Issue 0082: 10/18/01 bzoe
3568     write_ccdoc_directive_info(os,"Since",doc.get_since(),0,scope,stmt);
3569 
3570     // Deprecated
3571     write_ccdoc_directive_info(os,"Deprecated",doc.get_deprecated(),0,scope,stmt,false);
3572 
3573     // Params
3574     write_ccdoc_param_directive_info(os,doc.get_params(),scope,stmt);
3575 
3576     // Returns
3577     write_ccdoc_directive_info(os,"Returns",doc.get_returns(),0,scope,stmt,false);
3578 
3579     // Exceptions
3580     write_ccdoc_exception_directive_info(os,doc.get_exceptions(),scope,stmt);
3581 
3582     // See
3583     write_ccdoc_see_directive_info(os,doc.get_sees(),stmt);
3584 
3585     // Todo (Issue 0120)
3586     write_ccdoc_directive_info(os,"Todo",doc.get_todo(),0,scope,stmt,false);
3587 
3588     // The following directives are not output in HTML
3589     //   @pkg
3590     //   @pkgdoc
3591     //   @suffix
3592     os << "</dl>\n";
3593   }
3594   else {
3595     // What do we do with entities that do not have a ccdoc
3596     // comment?
3597     // Write out the source information and the author stuff.
3598     statement::base::strs_t empty_vec;
3599     string empty_str;
3600 
3601     if( stmt->get_type() == statement::base::STMT_PACKAGE ) {
3602       // Special case handling for packages with no comments.
3603       // Packages don't have source.
3604       if( m_sw.rptdpd() == true ) {
3605 	// Report package comments if -rptdpd was specified, otherwise
3606 	// don't.
3607 	os << m_sw.rptdefsd();
3608       }
3609       if( inherited || author_required || version_required ) {
3610 	os << "<dl>\n";
3611 	if( inherited && stmt->get_parent() )
3612 	  write_inherited_from_info(os,stmt);
3613 	if( author_required )
3614 	  write_ccdoc_directive_info(os,"Author",empty_vec,m_sw.rptdefa(),scope,stmt,true);
3615 	if( version_required )
3616 	  write_ccdoc_directive_info(os,"Version",empty_str,m_sw.rptdefv(),scope,stmt);
3617 	os << "</dl>\n";
3618       }
3619       return;
3620     }
3621 
3622     // Tell them that it is not documented.
3623     if(stmt->get_lineno() == 0 ) {
3624       // Packages never have line numbers.
3625       // Lineno zero flags the fact that it
3626       // was automatically generated by the
3627       // compiler.
3628       os << m_sw.rptdefasd();
3629     }
3630     else {
3631       os << m_sw.rptdefsd();
3632     }
3633 
3634     os << "<dl>\n";
3635     if( inherited && stmt->get_parent() )
3636       write_inherited_from_info(os,stmt);
3637     write_ccdoc_src_info(os,stmt,scope);
3638     if( author_required )
3639       write_ccdoc_directive_info(os,"Author",empty_vec,m_sw.rptdefa(),scope,stmt,true);
3640     if( version_required )
3641       write_ccdoc_directive_info(os,"Version",empty_str,m_sw.rptdefv(),scope,stmt);
3642     os << "</dl>\n";
3643   }
3644 }
3645 // ================================================================
3646 // Write out the source file information.
3647 // ================================================================
write_ccdoc_src_info(ostream & os,statement::base * stmt,statement::base * scope)3648 void ccdoc::phase3::html::write_ccdoc_src_info(ostream& os,
3649 					       statement::base* stmt,
3650 					       statement::base* scope)
3651 {
3652   string path;
3653   if( write_ccdoc_src_info(path,stmt) ) {
3654     write_ccdoc_directive_info(os,"Source",path,0,scope,stmt);
3655   }
3656 }
3657 // ================================================================
3658 // Write out the source file information.
3659 // ================================================================
write_ccdoc_src_info(string & path,statement::base * stmt)3660 bool ccdoc::phase3::html::write_ccdoc_src_info(string& path,
3661 					       statement::base* stmt)
3662 {
3663   // Source file
3664   if(stmt &&
3665      stmt->get_type() != statement::base::STMT_PACKAGE ) { // Issue 0027
3666     string file;
3667     if(stmt->get_file())
3668       file = stmt->get_file();
3669     else
3670       file = "unknown";
3671     if( stmt->get_file() && m_sw.srcurl().size() ) {
3672       // Build an anchor reference to the source file.
3673       string urlfn = stmt->get_file();
3674       if( m_sw.dospaths() ) {
3675 	// Convert the DOS backslashes to forward slashes for HTML.
3676 	replace(urlfn.begin(),urlfn.end(),'\\','/');
3677       }
3678       if( urlfn.size() ) {
3679 	path = "<a href=\"" + m_sw.srcurl() + urlfn + "\">" + file + "</a>";
3680       }
3681     }
3682     else {
3683       path = file;
3684     }
3685     if( path.size() ) {
3686       // Don't append the line numbers of zero. They are meaningless.
3687       if( stmt->get_lineno() ) {
3688 	char nbuf[32];
3689 	sprintf(nbuf,"%d",stmt->get_lineno()); // Issue: 0056
3690 	path += ":";
3691 	path += nbuf;
3692       }
3693     }
3694     return true;
3695   }
3696   return false;
3697 }
3698 // ================================================================
3699 // Write out the ccdoc description information.
3700 // Dereference the @link and @$ directives.
3701 // ================================================================
write_ccdoc_desc_info(ostream & os,const statement::base::strs_t & vec,statement::base * scope,statement::base * stmt)3702 void ccdoc::phase3::html::write_ccdoc_desc_info(ostream& os,
3703 						const statement::base::strs_t& vec,
3704 						statement::base* scope,
3705                                                 statement::base* stmt)
3706 {
3707   statement::base::strs_citr_t itr = vec.begin();
3708   for(;itr!=vec.end();++itr) {
3709     write_ccdoc_line_info(os,itr,vec.end(),scope,stmt);
3710   }
3711 }
3712 // ================================================================
3713 // Write out link info in each comment line.
3714 // ================================================================
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)3715 void ccdoc::phase3::html::write_ccdoc_line_info(ostream& os,
3716 						statement::base::strs_citr_t& itr,
3717 						statement::base::strs_citr_t itr_end,
3718 						statement::base* scope,
3719                                                 statement::base* stmt)
3720 {
3721   const string& line = *itr;
3722   if( line == "@link" ) {
3723     // This is a link line.
3724     if( ++itr == itr_end ) {
3725       os << line << "\n";
3726       return;
3727     }
3728     string link = *itr;
3729     if( ++itr == itr_end ) {
3730       os << line << "\n"
3731 	 << link << "\n";
3732       return;
3733     }
3734     // Issue 0088.
3735     string id = *itr;
3736     if( id == link && link[0] == '#' ) {
3737       // Issue 0125
3738       id.erase(0,1); // erase the 1st character
3739     }
3740     if( !find_and_write_links(os,link.c_str(),id.c_str(),scope) ) {
3741       os << "<font color=red>"
3742 	 << format_string_for_html(id)
3743 	 << "</font>";
3744       // Issue 109:
3745       // The error condition for this can be created as follows:
3746       //   /**
3747       //    * See {@link Undefined} for more details.
3748       //    */
3749       //   class Foo { int m_a; };
3750       s_log.warning()
3751         << "UNDEF: Undefined {@link ...} reference to '"
3752         << id
3753         << "' in ccdoc comment at line "
3754         << stmt->get_lineno()
3755         << " in file "
3756         << stmt->get_file()
3757         << "\n"
3758         << s_log.enable();
3759     }
3760   }
3761   else if( line == " " ) {
3762     // This is how the scanner tells us to insert a
3763     // paragraph separator.
3764     os << "<p>\n";
3765   }
3766   else {
3767     os << line << "\n";
3768   }
3769 }
3770 // ================================================================
3771 // Write out the ccdoc directive information.
3772 // ================================================================
write_ccdoc_param_directive_info(ostream & os,const statement::base::strss_t & vecvec,statement::base * scope,statement::base * stmt)3773 void ccdoc::phase3::html::write_ccdoc_param_directive_info(ostream& os,
3774 							   const statement::base::strss_t& vecvec,
3775 							   statement::base* scope,
3776                                                            statement::base* stmt)
3777 {
3778   if( vecvec.size() == 0 ) {
3779     return;
3780   }
3781   string name = "Param";
3782   if( vecvec.size() > 1 ) {
3783     name = "Params";
3784   }
3785   os << "<dt><b>" << name << ":</b></dt><dd><table cellspacing=4>\n";
3786   statement::base::strss_citr_t i = vecvec.begin();
3787   statement::base::strss_citr_t e = vecvec.end();
3788   for(;i!=e;++i) {
3789     const statement::base::strs_t& vec = *i;
3790     statement::base::strs_citr_t i1 = vec.begin();
3791     os << "<tr><td align=left valign=top><i>";
3792     write_ccdoc_line_info(os,i1,vec.end(),scope,stmt);
3793     os << "</i></td><td align=left valign=top>";
3794     for(++i1;i1!=vec.end();++i1) {
3795       write_ccdoc_line_info(os,i1,vec.end(),scope,stmt);
3796     }
3797     os << "</td></tr>\n";
3798   }
3799   os << "</table></dd>\n";
3800 }
3801 // ================================================================
3802 // Write out the ccdoc exception directive information.
3803 // ================================================================
write_ccdoc_exception_directive_info(ostream & os,const statement::base::strss_t & vecvec,statement::base * scope,statement::base * stmt)3804 void ccdoc::phase3::html::write_ccdoc_exception_directive_info(ostream& os,
3805 							       const statement::base::strss_t& vecvec,
3806 							       statement::base* scope,
3807                                                                statement::base* stmt)
3808 {
3809   if( vecvec.size() == 0 ) {
3810     return;
3811   }
3812   string name = "Exception";
3813   if( vecvec.size() > 1 ) {
3814     name = "Exceptions";
3815   }
3816   os << "<dt><b>" << name << ":</b></dt><dd><table cellspacing=4>\n";
3817   statement::base::strss_citr_t i = vecvec.begin();
3818   statement::base::strss_citr_t e = vecvec.end();
3819   for(;i!=e;++i) {
3820     const statement::base::strs_t& vec = *i;
3821     statement::base::strs_citr_t i1 = vec.begin();
3822     os << "<tr><td align=left valign=top><i>";
3823     // Issue 0087.
3824     // Write out the exception as a link, if possible.
3825     string id = *i1;
3826     if( !find_and_write_links(os,id.c_str(),id.c_str(),scope) ) {
3827       os << format_string_for_html(id);
3828     }
3829     os << "</i></td><td align=left valign=top>";
3830     // Write out the rest of the exception info.
3831     for(++i1;i1!=vec.end();++i1) {
3832       write_ccdoc_line_info(os,i1,vec.end(),scope,stmt);
3833     }
3834     os << "</td></tr>\n";
3835   }
3836   os << "</table></dd>\n";
3837 }
3838 // ================================================================
3839 // Write out the ccdoc directive information.
3840 // ================================================================
write_ccdoc_see_directive_info(ostream & os,const statement::base::strss_t & vecvec,statement::base * stmt)3841 void ccdoc::phase3::html::write_ccdoc_see_directive_info(ostream& os,
3842 							 const statement::base::strss_t& vecvec,
3843 							 statement::base* stmt)
3844 {
3845   if( vecvec.size() == 0 ) {
3846     return;
3847   }
3848   string name = "See Also";
3849   os << "<dt><b>" << name << ":</b></dt><dd>";
3850 
3851   statement::base::strss_citr_t i = vecvec.begin();
3852   statement::base::strss_citr_t e = vecvec.end();
3853   for(int j=0;i!=e;++i,++j) {
3854     const statement::base::strs_t& vec = *i;
3855     statement::base::strs_citr_t i1 = vec.begin();
3856     string link = *i1;
3857     ++i1;
3858     string index = *i1;
3859     string name = link;
3860 
3861     // Issue 0019
3862     if(link[0] == '<' ) {
3863       // Handle the special case where the user defined
3864       // an explicit link.
3865       // Just use what they specified.
3866       if(j)
3867 	os << ", "; // Issue 0024
3868       os << link << "\n";
3869       continue;
3870     }
3871 
3872     // Issue 0043.
3873     if( link[0] == '#' ) {
3874       // The parent defines the scope.
3875       const char* ps = link.c_str();
3876       name = &ps[1]; // strip of the leading '#"
3877       if( stmt->get_parent() )
3878 	stmt->get_parent()->get_hier_id_no_pkgs(link);
3879       if( link.size() )
3880 	link += "::";
3881       link += name;
3882     }
3883 
3884     statement::base::stmts_t stmts;
3885     m_db.get_stmt_no_pkgs(link,stmts);
3886 
3887     if(index != "*" ) {
3888       // Output the indexed reference.
3889       unsigned num = atoi(index.c_str());
3890       if( stmts.size() > num ) {
3891 	statement::base* stmt = stmts[num];
3892 	if(j)
3893 	  os << ", ";
3894 	write_link(os,stmt,name.c_str());
3895       }
3896       else {
3897 	if(j)
3898 	  os << ", ";
3899         // Issue 109:
3900         // The error condition for this can be created as follows:
3901         //   /**
3902         //    * @see Undefined
3903         //    * @see Undefined 1
3904         //    */
3905         //   class Foo { int m_a; };
3906         if( stmts.size() == 0 ) {
3907           os << "<font color=red>"
3908              << format_string_for_html(name)
3909              << "</font>";
3910           s_log.warning()
3911             << "UNDEF: Undefined @see link to reference '"
3912             << name
3913             << "' in ccdoc comment at line "
3914             << stmt->get_lineno()
3915             << " in file "
3916             << stmt->get_file()
3917             << "\n"
3918             << s_log.enable();
3919         }
3920         else {
3921           if( num > 0 ) {
3922             os << "<font color=red>"
3923                << format_string_for_html(name)
3924                << "["
3925                << num
3926                << "]"
3927                << "</font>";
3928           }
3929           else {
3930             os << "<font color=red>"
3931                << format_string_for_html(name)
3932                << "</font>";
3933           }
3934           s_log.warning()
3935             << "UNDEF: Undefined @see link to index reference '"
3936             << name
3937             << "["
3938             << index
3939             << "]"
3940             << "' in ccdoc comment at line "
3941             << stmt->get_lineno()
3942             << " in file "
3943             << stmt->get_file()
3944             << "\n"
3945             << s_log.enable();
3946         }
3947       }
3948     }
3949     else {
3950       if( stmts.size() ) {
3951 	// Output all of the references.
3952 	statement::base::stmts_itr_t itr1 = stmts.begin();
3953 	for(;itr1!=stmts.end();++itr1,++j) {
3954 	  statement::base* stmt = *itr1;
3955 	  if(j)
3956 	    os << ", ";
3957 	  write_link(os,stmt,name.c_str());
3958 	}
3959       }
3960       else {
3961 	if(j)
3962 	  os << ", ";
3963 	os << "<font color=red>"
3964 	   << format_string_for_html(name)
3965 	   << "</font>";
3966         // Issue 109:
3967         // It is not clear that this can be triggered.
3968         // I think this is dead code.
3969         s_log.warning()
3970           << "UNDEF: Undefined @see link to '"
3971           << name
3972           << "' at line "
3973           << stmt->get_lineno()
3974           << " in file "
3975           << stmt->get_file()
3976           << "\n"
3977           << s_log.enable();
3978       }
3979     }
3980   }
3981 
3982   os << "</dd>\n";
3983 }
3984 // ================================================================
3985 // Write out the inherited from info.
3986 // ================================================================
write_inherited_from_info(ostream & os,statement::base * stmt)3987 void ccdoc::phase3::html::write_inherited_from_info(ostream& os,
3988 						    statement::base* stmt)
3989 {
3990   os << "<dt><b>Inherited From:</b></dt><dd>";
3991   string id;
3992   stmt->get_parent()->get_hier_id_no_pkgs(id);
3993   write_link(os,stmt->get_parent(),id.c_str());
3994   os << "</dd>\n";
3995 }
3996 // ================================================================
3997 // Write out the ccdoc directive information.
3998 // ================================================================
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)3999 void ccdoc::phase3::html::write_ccdoc_directive_info(ostream& os,
4000 						     const char* name,
4001 						     const statement::base::strs_t& vec,
4002 						     const char* default_field,
4003 						     statement::base* scope,
4004                                                      statement::base* stmt,
4005                                                      bool commas)
4006 {
4007   if( vec.size() == 0 && default_field == 0 ) {
4008     return;
4009   }
4010   os << "<dt><b>" << name << ":</b></dt><dd>";
4011   if( vec.size() == 0 ) {
4012     // There are no entries, output the default
4013     // value.
4014     os << default_field;
4015   }
4016   else {
4017     statement::base::strs_citr_t i = vec.begin();
4018     statement::base::strs_citr_t e = vec.end();
4019     if( i!=e ) {
4020       write_ccdoc_line_info(os,i,vec.end(),scope,stmt);
4021     }
4022     for(++i;i!=e;++i) {
4023       if( commas ) // issue 0095
4024         os << ", ";
4025       write_ccdoc_line_info(os,i,vec.end(),scope,stmt);
4026     }
4027   }
4028   os << "</dd>\n";
4029 }
4030 // ================================================================
4031 // Write out the ccdoc directive information.
4032 // ================================================================
write_ccdoc_directive_info(ostream & os,const char * name,const string & val,const char * default_field,statement::base * scope,statement::base * stmt)4033 void ccdoc::phase3::html::write_ccdoc_directive_info(ostream& os,
4034 						     const char* name,
4035 						     const string& val,
4036 						     const char* default_field,
4037 						     statement::base* scope,
4038                                                      statement::base* stmt)
4039 {
4040   if( val.size() == 0 && default_field == 0 ) {
4041     return;
4042   }
4043   os << "<dt><b>" << name << ":</b></dt><dd>";
4044   if( val.size() == 0 ) {
4045     // There are no entries, output the default
4046     // value.
4047     os << default_field;
4048   }
4049   else {
4050     os << val;
4051   }
4052   os << "</dd>\n";
4053 }
4054 // ================================================================
4055 // Date and time.
4056 // ================================================================
4057 #include <time.h>
date_time() const4058 const char* ccdoc::phase3::html::date_time() const
4059 {
4060   static char tbuf[128];
4061   time_t ltime;
4062   ::time(&ltime);
4063   ::strcpy(tbuf,::ctime(&ltime));
4064 
4065   // Strip off the new line.
4066   char* p = tbuf;
4067   const char* e = &tbuf[127]; // detect overflows
4068   for(;*p != '\n' && *p != 0 && p < e; *p++);
4069   *p = 0;
4070   return tbuf;
4071 }
4072 
4073 // ================================================================
4074 // CrcTable
4075 // ================================================================
4076 unsigned ccdoc::phase3::html::checksum::m_crc_table[256] = {
4077  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
4078  0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
4079  0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
4080  0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
4081  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
4082  0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
4083  0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
4084  0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
4085  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
4086  0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
4087  0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
4088  0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
4089  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
4090  0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
4091  0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
4092  0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
4093  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
4094  0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
4095  0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
4096  0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
4097  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
4098  0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
4099  0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
4100  0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
4101  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
4102  0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
4103  0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
4104  0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
4105  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
4106  0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
4107  0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
4108  0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
4109  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
4110  0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
4111  0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
4112  0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
4113  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
4114  0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
4115  0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
4116  0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
4117  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
4118  0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
4119  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
4120 };
4121