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 
30 // ================================================================
31 // This static variable allows the header version
32 // to be queried at runtime.
33 // ================================================================
34 namespace {
35   char ccdoc_rcsid[] = "$Id: database.cc,v 1.11 2004/09/30 04:16:00 jlinoff Exp $";
36 }
37 
38 #include "database.h"
39 #include <iomanip>
40 #include <fstream>
41 #include <cstdio>
42 #include <cstring>
43 
44 // ================================================================
45 // Constructor.
46 // ================================================================
database(switches & sw)47 ccdoc::database::database(switches& sw)
48   : m_root(0),
49     m_sw(sw),
50     m_comment_id(0),
51     m_debug(false),
52     m_write(true)
53 {
54   m_root = new statement::base;
55   m_root->set_type( statement::base::STMT_PACKAGE );
56   m_root->set_id( m_sw.root() );
57   m_root->set_file( m_sw.db() );
58 
59   string db = m_sw.db();
60   if( db.size() == 0 ) {
61     throw ccdoc::exceptions::invalid_database
62       (__FILE__,
63        __LINE__,
64        "<NULL>",
65        "Illegal file name.");
66   }
67   // Read the database, if it exists.
68   read();
69 }
70 // ================================================================
71 // Destructor.
72 // ================================================================
~database()73 ccdoc::database::~database() {
74   write();
75 }
76 // ================================================================
77 // get_package
78 // ================================================================
parse_path(string path,vector<string> & ids)79 void ccdoc::database::parse_path(string path,vector<string>& ids)
80 {
81   // Handle the special case of the top package.
82   // The user can change this on the command line
83   // by specifying -pkg <new_name>.
84   if( path == "[ROOT]" || path == m_sw.default_root() )
85     return;
86 
87   // Package names can be separated by periods or
88   // double colons. Here are some examples:
89   //   A.B.C
90   //   A::B::C
91   string name;
92   string::iterator i = path.begin();
93   for(;i!=path.end();++i) {
94     if( *i == ':' ) {
95       ++i;
96       if( i==path.end() ) {
97 	// The user specified something like "FOO::BAR:".
98 	// This is an invalid name, don't construct it.
99 	s_log.warning()
100 	  << "Illegal package name separator ':', expected '::' in path '"
101 	  << path
102 	  << "', ignored."
103 	  << "\n"
104 	  << s_log.enable();
105 	name += ':';
106         break;
107       }
108       else if( *i != ':' ) {
109 	// The user specified something like "FOO:BAR".
110 	// This is an invalid name, don't construct it.
111 	s_log.warning()
112 	  << "Illegal package name separator ':', expected '::' in path '"
113 	  << path
114 	  << "', ignored."
115 	  << "\n"
116 	  << s_log.enable();
117 	name += ':';
118 	name += *i;
119       }
120       else {
121 	if(name.size()) {
122 	  // This name was separated by a '::'.
123 	  // Add the name.
124 	  // Make sure that we filter out the top level package name.
125 	  if( ids.size() )
126 	    ids.push_back(name);
127 	  else if( name != "[ROOT]" && name != m_sw.default_root() )
128 	    ids.push_back(name);
129 	}
130 	else if( ids.size() ) {
131 	  // The ids.size() check it to make sure that we
132 	  // don't report a problem for '::A'.
133 	  // The user specified something like "FOO::::BAR".
134 	  // This is an invalid name, don't construct it.
135 	  s_log.warning()
136 	    << "Illegal NULL package name '"
137 	    << path
138 	    << "', ignored."
139 	    << "\n"
140 	    << s_log.enable();
141 	}
142 	name = "";
143       }
144       continue;
145     }
146     else if( *i == '.' ) {
147       if(name.size()) {
148 	// This name was separated by a '.'.
149 	// Add the name.
150 	// Make sure that we filter out the top level package name.
151 	if( ids.size() )
152 	  ids.push_back(name);
153 	else if( name != "[ROOT]" && name != m_sw.default_root() )
154 	  ids.push_back(name);
155       }
156       else {
157 	// The user specified something like "FOO..BAR".
158 	// This is an invalid name, don't construct it.
159 	s_log.warning()
160 	  << "Illegal NULL package name '"
161 	  << path
162 	  << "', ignored."
163 	  << "\n"
164 	  << s_log.enable();
165       }
166       name = "";
167       continue;
168     }
169     else if( *i == '<' ) {
170       // Issue 0127
171       //
172       // This is a special case of an instantiated template
173       // name of the form:
174       //    A.B.C<int>.D
175       // Instantiated templates are not valid paths so the
176       // instantiation arguments are ignored.
177       // This means that the name is converted to:
178       //    A.B.C.D
179       if(name.size()) {
180 	// This name was separated by a '.'.
181 	// Add the name.
182 	// Make sure that we filter out the top level package name.
183 	if( ids.size() )
184 	  ids.push_back(name);
185 	else if( name != "[ROOT]" && name != m_sw.default_root() )
186 	  ids.push_back(name);
187       }
188       else {
189 	// The user specified something like "FOO..BAR".
190 	// This is an invalid name, don't construct it.
191 	s_log.warning()
192 	  << "Illegal NULL package name '"
193 	  << path
194 	  << "', ignored."
195 	  << "\n"
196 	  << s_log.enable();
197       }
198       name = "";
199       // Now skip to the enclosing '>'.
200       int depth = 0; // *i == '<'
201       for(;i!=path.end();++i) {
202 	if( *i == '<' ) {
203 	  // Recognize cases like this:
204 	  //   A.B.C<int,D<float> >.E
205 	  ++depth;
206 	}
207 	else if( *i == '>' ) {
208 	  --depth;
209 	  if( depth == 0 ) {
210 	    // Back up so that the outterloop iterator
211 	    // increments correctly to get the "real"
212 	    // next character.
213 	    //--i;
214 	    break;
215 	  }
216 	}
217       }
218       if( i == path.end() )
219         break;
220       continue;
221     }
222     name += *i;
223   }
224 
225   // Tack on the final name in the package list.
226   // Make sure the we do not tack on the top package
227   // name.
228   if( name.size() ) {
229     if( ids.size() )
230       ids.push_back(name);
231     else if( name != "[ROOT]" && name != m_sw.default_root() )
232       ids.push_back(name);
233   }
234 }
235 // ================================================================
236 // get_statement
237 // ================================================================
238 ccdoc::statement::base*
get_statement(string path)239 ccdoc::database::get_statement(string path)
240 {
241   statement::base* stmt = m_root;
242   vector<string> ids;
243   parse_path(path,ids);
244   if( ids.size() == 0 ) {
245     return stmt;
246   }
247   vector<string>::iterator itr = ids.begin();
248   for(;itr!=ids.end();++itr) {
249     string& name = *itr;
250     stmt = stmt->get_child_by_id(name);
251     if(!stmt)
252       break;
253   }
254   return stmt;
255 }
256 // ================================================================
257 // clear path map
258 // ================================================================
clear_path_map()259 void ccdoc::database::clear_path_map()
260 {
261   m_path_map.clear();
262 }
263 // ================================================================
264 // load path map
265 // ================================================================
load_path_map()266 void ccdoc::database::load_path_map()
267 {
268   if( !m_path_map.size() ) // Issue 0041
269     load_path_map(m_root);
270 }
271 // ================================================================
272 // load path map
273 // ================================================================
load_path_map(ccdoc::statement::base * stmt)274 void ccdoc::database::load_path_map(ccdoc::statement::base* stmt)
275 {
276   if( stmt ) {
277     if( stmt->get_type() != statement::base::STMT_COMMENT_PKGDOC &&
278 	stmt->get_type() != statement::base::STMT_COMMENT_PKGDOC_URL &&
279 	stmt->get_type() != statement::base::STMT_COMMENT_PREFIX &&
280 	stmt->get_type() != statement::base::STMT_COMMENT_SUFFIX ) {
281       // Issue 0041
282       // Ignore comments, they don't have meaningful names.
283       const char* pid = stmt->get_id();
284       if( *pid ) {
285 	string id;
286 	stmt->get_hier_id_no_pkgs(id);
287 	if( id.size() ) {
288 	  path_map_itr_type itr = m_path_map.find(id);
289 	  if( itr == m_path_map.end() ) {
290 	    pair<string,statement::base::stmts_t > p;
291 	    p.first = id;
292 	    p.second.push_back(stmt);
293 	    m_path_map.insert(p);
294 	  }
295 	  else {
296 	    // Issue 0046:
297 	    (*itr).second.push_back(stmt);
298 	  }
299 	}
300       }
301     }
302     statement::base::stmts_t& children = stmt->get_children();
303     statement::base::stmts_itr_t itr = children.begin();
304     for(;itr!=children.end();++itr) {
305       load_path_map(*itr);
306     }
307   }
308 }
309 // ================================================================
310 // erase from path map
311 // ================================================================
erase_from_path_map(ccdoc::statement::base * stmt)312 void ccdoc::database::erase_from_path_map(ccdoc::statement::base* stmt)
313 {
314   if( stmt ) {
315     if( stmt->get_type() != statement::base::STMT_COMMENT_PKGDOC &&
316 	stmt->get_type() != statement::base::STMT_COMMENT_PKGDOC_URL &&
317 	stmt->get_type() != statement::base::STMT_COMMENT_PREFIX &&
318 	stmt->get_type() != statement::base::STMT_COMMENT_SUFFIX ) {
319       // Issue 0041
320       // Ignore comments, they don't have meaningful names.
321       const char* pid = stmt->get_id();
322       if( *pid ) {
323 	string id;
324 	stmt->get_hier_id_no_pkgs(id);
325 	if( id.size() ) {
326 	  path_map_itr_type itr = m_path_map.find(id);
327 	  if( itr != m_path_map.end() ) {
328             // Issue 0117
329             //   Contributed by Chris Martin 2001/11/25
330             statement::base::stmts_t& vec = (*itr).second;
331             statement::base::stmts_itr_t vitr = vec.begin();
332             for(;vitr != vec.end(); ++vitr) {
333               if( (*vitr) == stmt ) {
334                 vec.erase(vitr);
335                 break;
336               }
337             }
338             if( vec.size() == 0 )
339               m_path_map.erase(itr);
340 	  }
341 	}
342       }
343     }
344     statement::base::stmts_t& children = stmt->get_children();
345     statement::base::stmts_itr_t itr = children.begin();
346     for(;itr!=children.end();++itr) {
347       erase_from_path_map(*itr);
348     }
349   }
350 }
351 // ================================================================
352 // get_statement_no_pkgs
353 // ================================================================
get_stmt_no_pkgs(string path,statement::base::stmts_t & stmts,bool pkgs_as_last_resort)354 void ccdoc::database::get_stmt_no_pkgs(string path,
355 				       statement::base::stmts_t& stmts,
356                                        bool pkgs_as_last_resort)
357 {
358   // Always do a direct lookup for speed.
359   path_map_itr_type itr = m_path_map.find(path);
360   if( itr != m_path_map.end() ) {
361     stmts = (*itr).second;
362     return;
363   }
364 
365   // The path was not found, see whether there are embedded #'s.
366   // This allows users to use the javadoc syntax more freely.
367   size_t hash_marks = path.find("#");
368   if( hash_marks != string::npos ) {
369     // Convert the hash marks to '::' to conform to ccdoc syntax
370     // and try again.
371     string newpath;
372     string::iterator itr = path.begin();
373     for(;itr!=path.end();++itr) {
374       if( *itr == '#' )
375         newpath += "::";
376       else
377         newpath += *itr;
378     }
379     get_stmt_no_pkgs( newpath, stmts );
380     return;
381   }
382 
383   // See whether it is in the anonymous namespace
384   // of the form "{@link foo}" or "{@link ::foo}".
385   string new_path = "-anonymous-";
386   if( !path.empty() && path[0] != ':' )
387     new_path += "::";
388   new_path += path;
389   itr = m_path_map.find(new_path);
390   if( itr != m_path_map.end() ) {
391     stmts = (*itr).second;
392     return;
393   }
394 
395 #if 0
396  {
397    // Dump the path map for debugging.
398    s_log << "DEBUG:" << __FILE__ << ":" << __LINE__
399          << " PATH MAP DUMP\n";
400    s_log << "DEBUG:" << __FILE__ << ":" << __LINE__
401          << " key = '" << path << "'\n";
402    int xxx=1;
403    for(itr=m_path_map.begin();itr!=m_path_map.end();++itr,++xxx) {
404      s_log << "DEBUG:" << __FILE__ << ":" << __LINE__
405            << "    [" << xxx << "]"
406            << " = \"" << (*itr).first << "\"\n";
407    }
408  }
409 #endif
410 
411   // Issue 0080
412   //  The path was not found, try stripping the
413   //  trailing w/s.
414   // bzoe 10/18/01 -- try stripping trailing spaces.
415   size_t last = path.find_last_not_of(" \t\r\n");
416   // Trailing spaces found, trim 'em.
417   if( string::npos != last ) {
418     path = path.substr(0,last+1);
419     itr = m_path_map.find(path);
420     if( itr != m_path_map.end() ) {
421       stmts = (*itr).second;
422       return;
423     }
424   }
425 
426   // Issue 0138:
427   // Try stripping the leading '::'.
428   // This sometimes occurs in the case of global
429   // variables.
430   if( path.size()>1 && path[0] == ':' && path[1] == ':' ) {
431     new_path = path.substr(2); // strip the leading '::' and try again.
432     itr = m_path_map.find(new_path);
433     if( itr != m_path_map.end() ) {
434       stmts = (*itr).second;
435       return;
436     }
437   }
438 
439   // Issue 0138:
440   // Nothing worked, try getting the name using the
441   // package prefixes.
442   if( pkgs_as_last_resort ) {
443     ccdoc::statement::base* stmt = get_statement(path);
444     if( stmt ) {
445       stmts.push_back(stmt);
446       return;
447     }
448   }
449 
450   // Issue 0145:
451   // Still nothing, look for the operator keyword, try inserting
452   // a space between the operator keyword and the operator type.
453   size_t x = path.find("operator");
454   if( x != string::npos ) {
455     // There is an operator keyword, insert the space.
456     x += 8;
457     string sp = " ";
458     new_path = path;
459     new_path.insert(x,sp,0,new_path.length()+1);
460     itr = m_path_map.find(new_path);
461     if( itr != m_path_map.end() ) {
462       stmts = (*itr).second;
463       return;
464     }
465   }
466 }
467 // ================================================================
468 // get_create_package
469 // ================================================================
470 ccdoc::statement::base*
get_create_package(string path)471 ccdoc::database::get_create_package(string path)
472 {
473   statement::base* pkg = m_root;
474   vector<string> ids;
475   parse_path(path,ids);
476   if( ids.size() == 0 ) {
477     return pkg;
478   }
479   vector<string>::iterator itr = ids.begin();
480   for(;itr!=ids.end();++itr) {
481     string& name = *itr;
482     statement::base* child_pkg = pkg->get_child_by_id(name);
483     if(!child_pkg) {
484       child_pkg = new statement::base;
485       child_pkg->set_id(name);
486       child_pkg->set_type(statement::base::STMT_PACKAGE);
487       child_pkg->set_parent(pkg);
488       child_pkg->set_file( m_sw.db() );
489     }
490     pkg = child_pkg;
491   }
492   return pkg;
493 }
494 // ================================================================
495 // Remove file statements.
496 // ================================================================
remove_file_statements(const char * file)497 void ccdoc::database::remove_file_statements(const char* file)
498 {
499   if(file) {
500     remove_file_statements(file,m_root);
501   }
502 }
503 // ================================================================
504 // Remove file statements.
505 // ================================================================
remove_file_statements(const char * file,statement::base * stmt)506 void ccdoc::database::remove_file_statements(const char* file,
507 					     statement::base* stmt)
508 {
509   if(stmt) {
510     if( stmt->get_type() == statement::base::STMT_PACKAGE ||
511 	stmt->get_type() == statement::base::STMT_NAMESPACE_BEGIN ||
512 	stmt->get_type() == statement::base::STMT_NAMESPACE_END ) {
513       // Make a copy of the the children so that a lower level
514       // deletion does not make the iterator invalid.
515       statement::base::stmts_t vec = stmt->get_children();
516       statement::base::stmts_itr_t itr = vec.begin();
517       for(;itr!=vec.end();++itr) {
518 	remove_file_statements(file,*itr);
519       }
520     }
521     else if(!::strcmp(file,stmt->get_file())) {
522       // This is not a package or a namespace and the
523       // files names match, delete it.
524       delete stmt;
525     }
526   }
527 }
528 // ================================================================
529 // read
530 // ================================================================
read()531 void ccdoc::database::read()
532 {
533   if( m_sw.verbose() ) {
534     s_log << "db: read begins\n";
535   }
536 
537   // Write out the database.
538   string db = m_sw.db();
539   ifstream is(db.c_str());
540   if(!is) {
541     // The database does not exist.
542     if( m_sw.verbose() ) {
543       s_log << "db: does not exist\n";
544       s_log << "db: read ends\n";
545     }
546     return;
547   }
548 
549   string token;
550 
551   // ================================================
552   // Read the header.
553   // ================================================
554   is >> token;
555   if( token != "ccdoc" ) {
556     read_error(1,"ccdoc",token);
557     return;
558   }
559   is >> token;
560   if( token != "v0.8" ) {
561     read_error(1,"v0.8",token);
562     return;
563   }
564 
565   // ================================================
566   // Read the records.
567   // ================================================
568   bool ok = true;
569   is >> token;
570   if( token == "verbose" ) {
571     ok = read_verbose(is);
572   }
573   else if( token == "terse" ) {
574     ok = read_terse(is);
575   }
576   else {
577     read_error(1,"verbose or terse",token);
578     return;
579   }
580   if( !ok ) {
581     if( m_root->get_children().size() ) {
582       // The read failed, delete all of the children.
583       // Leave the root intact.
584       statement::base::stmts_t vec = m_root->get_children();
585       statement::base::stmts_itr_t itr = vec.begin();
586       for(;itr!=vec.end();++itr) {
587 	statement::base* child = *itr;
588 	delete child;
589       }
590     }
591   }
592 
593   // ================================================
594   // Attach the comments:
595   // Loop through the statements, grab the comment records
596   // and attach them.
597   // ================================================
598   if( ok ) {
599     statement::base::stmts_t stmts;
600     load(stmts);
601     if( stmts.size() ) {
602       statement::base::stmts_itr_t itr = stmts.begin();
603       for(;itr!=stmts.end();++itr) {
604 	statement::base* rec = *itr;
605 	if( rec->get_type() == statement::base::STMT_COMMENT_PREFIX ) {
606 	  // Attach this comment to the next statement.
607 	  statement::base* comm = rec;
608 	  ++itr;
609 	  if( itr == stmts.end() )
610 	    break; // Issue 0020
611 	  statement::base* stmt = *itr;
612 	  stmt->set_comment( comm );
613 	  comm->set_comment( stmt );
614 	  --itr; // Issue 0036
615 	}
616 	else if( rec->get_type() == statement::base::STMT_COMMENT_SUFFIX ) {
617 	  // Attach this comment to the previous.
618 	  statement::base* comm = rec;
619 	  if( itr != stmts.begin() ) {
620 	    --itr;
621 	    statement::base* stmt = *itr;
622 	    stmt->set_comment( comm );
623 	    comm->set_comment( stmt );
624 	    ++itr;
625 	  }
626 	}
627 	else if( rec->get_type() == statement::base::STMT_COMMENT_PKGDOC_URL ||
628 		 rec->get_type() == statement::base::STMT_COMMENT_PKGDOC ) {
629 	  // Get the pkgdoc id.
630 	  statement::base* comm = rec;
631 	  statement::comment comm_stmt(comm);
632 
633 	  string pkgdoc_id;
634 	  const vector<string>& pkgdoc_tokens = comm_stmt.get_pkgdoc();
635 	  vector<string>::const_iterator itr1 = pkgdoc_tokens.begin();
636 	  if( itr1 != pkgdoc_tokens.end() ) {
637 	    // Issue 0025
638 	    // Issue 0031
639 	    // Check for the special case of the @url or the @tid field.
640 	    for(;itr1!=pkgdoc_tokens.end();++itr1) {
641 	      if( *itr1 == "@url" || *itr1 == "@tid" ) {
642 		++itr1;
643 		if( itr1==pkgdoc_tokens.end() ) {
644 		  break;
645 		}
646 		continue;
647 	      }
648 	      if( pkgdoc_id.size() )
649 		pkgdoc_id += "::";
650 	      pkgdoc_id += *itr1;
651 	    }
652 	  }
653 
654 	  statement::base* stmt = get_create_package( pkgdoc_id );
655 	  stmt->set_comment( comm );
656 	  comm->set_comment( stmt );
657 	}
658       }
659     }
660   }
661 
662   if( m_sw.verbose() ) {
663     s_log << "db: read ends\n";
664   }
665 }
666 // ================================================================
667 // Read verbose format
668 // ================================================================
read_verbose(istream & is)669 bool ccdoc::database::read_verbose(istream& is)
670 {
671   // ================================================
672   // Read the string table.
673   // ================================================
674   unsigned lineno = 1;
675   vector<string> strings;
676   if( !read_string_table(is,lineno,strings) )
677     return false;
678 
679   // ================================================
680   // Read the statement header.
681   // S <hex_num>
682   // ================================================
683   string token;
684   unsigned sz = 0;
685   is >> token;
686   is >> hex >> sz;
687   lineno++;
688   if( token != "S" ) {
689     read_error(lineno,"S",token);
690     return false;
691   }
692 
693   // ================================================
694   // Read the statements.
695   // ================================================
696   statement::base::stmts_t stmts;
697   stmts.push_back(m_root);
698   statement::base::ACCESS stmt_access = statement::base::STMT_PUBLIC;
699   string stmt_file;
700   for(;sz>0;--sz) {
701     lineno++;
702     is >> token;
703 
704     // ================================================
705     // Read a statement.
706     // ================================================
707     if( token == "s" ) {
708       // ================================================
709       // Get the id.
710       // ================================================
711       lineno++;
712       string stmt_id;
713       {
714 	const char* str = read_verbose_string(is,lineno);
715 	if(!str)
716 	  return false;
717 	stmt_id = str;
718       }
719 
720       // ================================================
721       // Get the statement tag.
722       // ================================================
723       lineno++;
724       unsigned stmt_tag = 0;
725       is >> hex >> stmt_tag;
726       if( stmt_tag > 0 && stmt_tag != stmts.size() ) {
727 	  s_log.warning()
728 	    << "Syntax error.\n"
729 	    << "\tUnexpected statement tag " << stmt_tag << " found at line "
730 	    << lineno
731 	    << "\n\tin ccdoc db '" << m_sw.db() << "'.\n"
732 	    << "\tExpected tag " << stmts.size() << ".\n"
733 	    << "\tThe db contents will be ignored.\n"
734 	    << s_log.enable();
735 	  return false;
736       }
737 
738       // ================================================
739       // Get the parent tag.
740       // ================================================
741       lineno++;
742       unsigned parent_tag = 0;
743       is >> hex >> parent_tag;
744 
745       // ================================================
746       // Get the type.
747       // ================================================
748       lineno++;
749       is >> token;
750       statement::base::TYPE stmt_type;
751       stmt_type = statement::base::get_terse_type(token);
752 
753       // ================================================
754       // Get the extern.
755       // ================================================
756       lineno++;
757       string stmt_extern;
758       {
759 	const char* str = read_verbose_string(is,lineno);
760 	if(!str)
761 	  return false;
762 	stmt_extern = str;
763       }
764 
765       // ================================================
766       // Statement lineno.
767       // ================================================
768       lineno++;
769       unsigned stmt_lineno = 0;
770       is >> hex >> stmt_lineno;
771 
772       // ================================================
773       // Get the number of tokens.
774       // ================================================
775       lineno++;
776       unsigned num_tokens = 0;
777       is >> hex >> num_tokens;
778 
779       // ================================================
780       // Get the tokens.
781       // ================================================
782       vector<string> stmt_tokens;
783       for(;num_tokens>0;--num_tokens) {
784 	lineno++;
785 	const char* str = read_verbose_string(is,lineno);
786 	if(!str)
787 	  return false;
788 	stmt_tokens.push_back(str);
789       }
790 
791       // ================================================
792       // Create the statement.
793       // Ignore the first one, it is always the root.
794       // ================================================
795       if( stmt_tag ) {
796 	statement::base* parent = 0;
797 	if( parent_tag >= stmts.size() ) {
798 	  s_log.warning()
799 	    << "Syntax error.\n"
800 	    << "\tUnexpected parent tag " << parent_tag << " found at line "
801 	    << lineno
802 	    << "\n\tin ccdoc db '" << m_sw.db() << "'.\n"
803 	    << "\tExpected a tag less than " << stmts.size() << ".\n"
804 	    << "\tThe db contents will be ignored.\n"
805 	    << s_log.enable();
806 	  return false;
807 	}
808 	parent = stmts[parent_tag];
809 	statement::base* stmt = new statement::base;
810 	stmt->set_id(stmt_id.c_str());
811 	stmt->set_tag(stmt_tag);
812 	stmt->set_type(stmt_type);
813 	stmt->set_access(stmt_access);
814 	stmt->set_extern(stmt_extern.c_str());
815 	stmt->set_lineno(stmt_lineno);
816 	stmt->set_file(stmt_file.c_str());
817 	stmt->set_tokens(stmt_tokens);
818 	stmt->set_parent( parent );
819 	stmts.push_back(stmt);
820       }
821     }
822     // ================================================
823     // Read a modal file statement.
824     // ================================================
825     else if( token == "f" ) {
826       const char* str = read_verbose_string(is,lineno);
827       if(!str)
828 	return false;
829       stmt_file = str;
830       ++sz; // This doesn't count as a statement.
831     }
832     // ================================================
833     // Read a modal access statement.
834     // ================================================
835     else if( token == "a" ) {
836       is >> token;
837       stmt_access = statement::base::get_terse_access(token);
838       ++sz; // This doesn't count as a statement.
839     }
840     // ================================================
841     // Unrecognized token, report an error.
842     // ================================================
843     else {
844       read_error(lineno,"s or f",token);
845     }
846   }
847 
848   // ================================================
849   // Make sure we are at the end.
850   // ================================================
851   is >> token;
852   lineno++;
853   if( token != "e" ) {
854     read_error(lineno,"e",token);
855     return false;
856   }
857 
858   return true;
859 }
860 // ================================================================
861 // Read terse format
862 // ================================================================
read_terse(istream & is)863 bool ccdoc::database::read_terse(istream& is)
864 {
865   // ================================================
866   // Read the string table.
867   // ================================================
868   unsigned lineno = 1;
869   vector<string> strings;
870   if( !read_string_table(is,lineno,strings) )
871     return false;
872 
873   // ================================================
874   // Read the statement header.
875   // S <hex_num>
876   // ================================================
877   string token;
878   unsigned sz = 0;
879   is >> token;
880   is >> hex >> sz;
881   lineno++;
882   if( token != "S" ) {
883     read_error(lineno,"S",token);
884     return false;
885   }
886 
887   // ================================================
888   // Read the statements.
889   // ================================================
890   statement::base::stmts_t stmts;
891   stmts.push_back(m_root);
892   statement::base::ACCESS stmt_access = statement::base::STMT_PUBLIC;
893   string stmt_file;
894   for(;sz>0;--sz) {
895     lineno++;
896     is >> token;
897 
898     // ================================================
899     // Read a statement.
900     // ================================================
901     if( token == "s" ) {
902       // ================================================
903       // Get the id.
904       // ================================================
905       lineno++;
906       string stmt_id;
907       {
908 	const char* str = read_terse_string(is,lineno,strings);
909 	if(!str)
910 	  return false;
911 	stmt_id = str;
912       }
913 
914       // ================================================
915       // Get the statement tag.
916       // ================================================
917       lineno++;
918       unsigned stmt_tag = 0;
919       is >> hex >> stmt_tag;
920       if( stmt_tag > 0 && stmt_tag != stmts.size() ) {
921 	  s_log.warning()
922 	    << "Syntax error.\n"
923 	    << "\tUnexpected statement tag " << stmt_tag << " found at line "
924 	    << lineno
925 	    << "\n\tin ccdoc db '" << m_sw.db() << "'.\n"
926 	    << "\tExpected tag " << stmts.size() << ".\n"
927 	    << "\tThe db contents will be ignored.\n"
928 	    << s_log.enable();
929 	  return false;
930       }
931 
932       // ================================================
933       // Get the parent tag.
934       // ================================================
935       lineno++;
936       unsigned parent_tag = 0;
937       is >> hex >> parent_tag;
938 
939       // ================================================
940       // Get the type.
941       // ================================================
942       lineno++;
943       statement::base::TYPE stmt_type = statement::base::STMT_IGNORE;
944       {
945 	const char* str = read_terse_string(is,lineno,strings);
946 	if(!str)
947 	  return false;
948 	stmt_type = statement::base::get_terse_type(str);
949       }
950 
951       // ================================================
952       // Get the extern.
953       // ================================================
954       lineno++;
955       string stmt_extern;
956       {
957 	const char* str = read_terse_string(is,lineno,strings);
958 	if(!str)
959 	  return false;
960 	stmt_extern = str;
961       }
962 
963       // ================================================
964       // Statement lineno.
965       // ================================================
966       lineno++;
967       unsigned stmt_lineno = 0;
968       is >> hex >> stmt_lineno;
969 
970       // ================================================
971       // Get the number of tokens.
972       // ================================================
973       lineno++;
974       unsigned num_tokens = 0;
975       is >> hex >> num_tokens;
976 
977       // ================================================
978       // Get the tokens.
979       // ================================================
980       vector<string> stmt_tokens;
981       for(;num_tokens>0;--num_tokens) {
982 	lineno++;
983 	const char* str = read_terse_string(is,lineno,strings);
984 	if(!str)
985 	  return false;
986 	stmt_tokens.push_back(str);
987       }
988 
989       // ================================================
990       // Create the statement.
991       // Ignore the first one, it is always the root.
992       // ================================================
993       if( stmt_tag ) {
994 	statement::base* parent = 0;
995 	if( parent_tag >= stmts.size() ) {
996 	  s_log.warning()
997 	    << "Syntax error.\n"
998 	    << "\tUnexpected parent tag " << parent_tag << " found at line "
999 	    << lineno
1000 	    << "\n\tin ccdoc db '" << m_sw.db() << "'.\n"
1001 	    << "\tExpected a tag less than " << stmts.size() << ".\n"
1002 	    << "\tThe db contents will be ignored.\n"
1003 	    << s_log.enable();
1004 	  return false;
1005 	}
1006 	parent = stmts[parent_tag];
1007 	statement::base* stmt = new statement::base;
1008 	stmt->set_id(stmt_id.c_str());
1009 	stmt->set_tag(stmt_tag);
1010 	stmt->set_type(stmt_type);
1011 	stmt->set_access(stmt_access);
1012 	stmt->set_extern(stmt_extern.c_str());
1013 	stmt->set_lineno(stmt_lineno);
1014 	stmt->set_file(stmt_file.c_str());
1015 	stmt->set_tokens(stmt_tokens);
1016 	stmt->set_parent( parent );
1017 	stmts.push_back(stmt);
1018       }
1019     }
1020     // ================================================
1021     // Read a modal file statement.
1022     // ================================================
1023     else if( token == "f" ) {
1024       const char* str = read_terse_string(is,lineno,strings);
1025       if(!str)
1026 	return false;
1027       stmt_file = str;
1028       ++sz; // This doesn't count as statements.
1029     }
1030     // ================================================
1031     // Read a modal access statement.
1032     // ================================================
1033     else if( token == "a" ) {
1034       const char* str = read_terse_string(is,lineno,strings);
1035       if(!str)
1036 	return false;
1037       stmt_access = statement::base::get_terse_access(str);
1038       ++sz; // This doesn't count as statements.
1039     }
1040     // ================================================
1041     // Unrecognized token, report an error.
1042     // ================================================
1043     else {
1044       read_error(lineno,"s or f",token);
1045     }
1046   }
1047 
1048   // ================================================
1049   // Make sure we are at the end.
1050   // ================================================
1051   is >> token;
1052   lineno++;
1053   if( token != "e" ) {
1054     read_error(lineno,"e",token);
1055     return false;
1056   }
1057 
1058   return true;
1059 }
1060 // ================================================================
1061 // Read the string table. This is the same in both verbose and
1062 // terse mode.
1063 // ================================================================
read_string_table(istream & is,unsigned & lineno,vector<string> & strings)1064 bool ccdoc::database::read_string_table(istream& is,
1065 					unsigned& lineno,
1066 					vector<string>& strings)
1067 {
1068   // ================================================
1069   // Read the string table header.
1070   // $ <hex_num>
1071   // ================================================
1072   lineno = 2;
1073   string token;
1074   is >> token;
1075   if( token != "$" ) {
1076     read_error(lineno,"$",token);
1077     return false;
1078   }
1079 
1080   // ================================================
1081   // Read each string entry.
1082   // <hex_num> <hex_num> <string>
1083   //  ^         ^         ^
1084   //  |         |         +----- The string.
1085   //  |         +--------------- The string length.
1086   //  +------------------------- The index.
1087   // ================================================
1088   unsigned sz = 0;
1089   is >> hex >> sz;
1090   if( sz ) {
1091     strings.reserve(sz);
1092   }
1093 
1094   for(;sz>0;--sz) {
1095     lineno++;
1096     unsigned index = 0;
1097     is >> hex >> index;
1098 
1099     // Now read the full string.
1100     // This must be done in raw mode to guarantee that
1101     // all w/s bits are read correctly.
1102     const char* str = read_verbose_string(is,lineno);
1103     if(!str)
1104       return false;
1105 #if 0
1106     CCDOC_DEBUG_MADE_IT << "DEBUG:"
1107 			<< " lineno=" << lineno
1108 			<< " index=" << index
1109 			<< " line='" << str << "'\n";
1110 #endif
1111     // Verify that the string was added to the
1112     // right place.
1113     if( strings.size() != index ) {
1114       s_log.warning()
1115 	<< "Syntax error.\n"
1116 	<< "\tUnexpected string index " << index << " found at line "
1117 	<< lineno
1118 	<< "\n\tin ccdoc db '" << m_sw.db() << "'.\n"
1119 	<< "\tExpected index " << strings.size() << ".\n"
1120 	<< "\tThe db contents will be ignored.\n"
1121 	<< s_log.enable();
1122       return false;
1123     }
1124 
1125     // Add the string to the strings list.
1126     strings.push_back(str);
1127   }
1128   return true;
1129 }
1130 // ================================================================
1131 // Read verbose string.
1132 // ================================================================
read_verbose_string(istream & is,unsigned lineno)1133 const char* ccdoc::database::read_verbose_string(istream& is,
1134 						 unsigned lineno)
1135 {
1136   static char token[65536];
1137   // The string format is: <len> <string>.
1138   token[0] = 0;
1139   unsigned len = 0;
1140   is >> hex >> len;
1141   if( len ) {
1142     char ch = 0;
1143     is.get(ch); // get the space
1144     if( ch != ' ' ) {
1145       char s[2];
1146       s[0] = ch;
1147       s[1] = 0;
1148       read_error(lineno," ",s);
1149       return 0;
1150     }
1151     char* p = token;
1152     for(;len>0;--len) {
1153       is.get(ch);
1154       *p++ = ch;
1155     }
1156     *p = 0;
1157   }
1158   return token;
1159 }
1160 // ================================================================
1161 // Read terse string.
1162 // ================================================================
read_terse_string(istream & is,unsigned lineno,vector<string> & strings)1163 const char* ccdoc::database::read_terse_string(istream& is,
1164 					       unsigned lineno,
1165 					       vector<string>& strings)
1166 {
1167   // The string format is: <id>
1168   // This id is then looked up on the string table.
1169   // Make sure that there is a leading space.
1170   unsigned idx = 0;
1171   is >> hex >> idx;
1172   if( idx < strings.size() ) {
1173     return strings[idx].c_str();
1174   }
1175 
1176   s_log.warning()
1177     << "Syntax error.\n"
1178     << "\tUnexpected string index " << idx << " found at line "
1179     << lineno
1180     << "\n\tin ccdoc db '" << m_sw.db() << "'.\n"
1181     << "\tExpected an index less than " << strings.size() << ".\n"
1182     << "\tThe db contents will be ignored.\n"
1183     << s_log.enable();
1184 
1185   return 0;
1186 }
1187 // ================================================================
1188 // Read error
1189 // ================================================================
read_error(unsigned lineno,const char * expected,const string & found)1190 void ccdoc::database::read_error(unsigned lineno,
1191 				 const char* expected,
1192 				 const string& found)
1193 {
1194   read_error(lineno,expected,found.c_str());
1195 }
1196 // ================================================================
1197 // Read error
1198 // ================================================================
read_error(unsigned lineno,const char * expected,const char * found)1199 void ccdoc::database::read_error(unsigned lineno,
1200 				 const char* expected,
1201 				 const char* found)
1202 {
1203   if( expected && found ) {
1204     s_log.warning()
1205       << "Syntax error.\n"
1206       << "\tUnexpected token '" << found << "' found at line "
1207       << lineno
1208       << "\n\tin ccdoc db '" << m_sw.db() << "'.\n"
1209       << "\tExpected token '" << expected << "'.\n"
1210       << "\tThe db contents will be ignored.\n"
1211       << s_log.enable();
1212   }
1213 }
1214 // ================================================================
1215 // write
1216 // ================================================================
write(statement::base::stmts_t & vec,statement::base * stmt)1217 void ccdoc::database::write(statement::base::stmts_t& vec,
1218 			    statement::base* stmt)
1219 {
1220   if( stmt ) {
1221     stmt->set_tag( vec.size() );
1222     vec.push_back(stmt);
1223     statement::base::stmts_t& children = stmt->get_children();
1224     statement::base::stmts_itr_t itr = children.begin();
1225     for(;itr!=children.end();++itr) {
1226       write(vec,*itr);
1227     }
1228   }
1229 }
1230 // ================================================================
1231 // write
1232 // ================================================================
write()1233 void ccdoc::database::write()
1234 {
1235   if( !m_write ) {
1236     return;
1237   }
1238   if( m_sw.verbose() ) {
1239     s_log << "db: write begins\n";
1240   }
1241 
1242   // Write out the database.
1243   string db = m_sw.db();
1244   ofstream os(db.c_str());
1245   if(!os) {
1246     // Cannot create the database, throw an exception.
1247     throw ccdoc::exceptions::invalid_database
1248       (__FILE__,
1249        __LINE__,
1250        db.c_str(),
1251        "Can't create the database.");
1252     return;
1253   }
1254 
1255   statement::base::stmts_t stmts;
1256   write(stmts,m_root);
1257 
1258   if( m_sw.verbose() ) {
1259     s_log << "db: writing " << stmts.size() << " statements\n";
1260   }
1261 
1262   // ================================================
1263   // The header is the same in verbose and terse modes.
1264   // ================================================
1265   os << "ccdoc v0.8 ";
1266   if( m_sw.verbose_format() )
1267     os << "verbose";
1268   else
1269     os << "terse";
1270   os << "\n";
1271 
1272   // ================================================
1273   // String table. The syntax is the same in verbose
1274   // and terse modes but there are no entries in
1275   // verbose mode.
1276   // ================================================
1277   strmgr& mgr = statement::base::get_strmgr();
1278   os << "$ ";
1279   if( m_sw.verbose_format() ) {
1280     os << "0\n";
1281   }
1282   else {
1283     strmgr::str_coll_itr itr = mgr.begin();
1284     mgr.gen_maps();
1285     os << hex << mgr.size() << "\n";
1286     for(;itr!=mgr.end();++itr) {
1287       os << hex << (*itr).second << " ";
1288       os << hex << (*itr).first.size() << " ";
1289       os << (*itr).first << "\n";
1290     }
1291   }
1292 
1293   // ================================================
1294   // Statements section header.
1295   // ================================================
1296   os << "S " << hex << stmts.size() << "\n";
1297 
1298   // ================================================
1299   // Verbose format
1300   // ================================================
1301   if( m_sw.verbose_format() ) {
1302     string fn = "";
1303     statement::base::ACCESS stmt_access = statement::base::STMT_PUBLIC;
1304     statement::base::stmts_itr_t itr = stmts.begin();
1305     for(int i=0;itr!=stmts.end();++itr,++i) {
1306       statement::base* stmt = *itr;
1307 
1308       // Write out the modal file specifier.
1309       string new_fn = stmt->get_file();
1310       if( new_fn != fn ) {
1311 	fn = new_fn;
1312 	os << "f " << hex << fn.size() << " "
1313 	   << fn << "\n";
1314       }
1315 
1316       // Write out the modal access specifier.
1317       if( stmt->get_access() != stmt_access ) {
1318 	os << "a "
1319 	   << stmt->get_terse_access_name()
1320 	   << "\n";
1321 	stmt_access = stmt->get_access();
1322       }
1323 
1324       // Write out the start of the statement: 's'.
1325       os << "s\n";
1326 
1327       // Write out the stmt id.
1328       os << hex << strlen(stmt->get_id()) << " "
1329 	 << stmt->get_id() << "\n";
1330 
1331       // Write out the tag.
1332       os << hex << stmt->get_tag() << "\n"; // tag
1333 
1334       // Write out the parent tag.
1335       if( stmt->get_parent() ) {
1336 	os << hex << stmt->get_parent()->get_tag() << "\n";
1337       }
1338       else {
1339 	os << "0\n";
1340       }
1341 
1342       // Write out the type.
1343       os << stmt->get_terse_type_name() << "\n";
1344 
1345       // Write out the extern specifier.
1346       os << hex << strlen(stmt->get_extern()) << " "
1347 	 << stmt->get_extern() << "\n";
1348 
1349       // Write out the lineno.
1350       os << hex << stmt->get_lineno() << "\n";
1351 
1352       // Note that children do not have to be stored,
1353       // they are implied by the the parent.
1354       // Note that the first entry is always the root.
1355 
1356       const statement::base::cstrs_t& tokens = stmt->get_tokens();
1357       statement::base::cstrs_citr_t titr = tokens.begin();
1358       os << hex << tokens.size() << "\n";
1359       for(;titr!=tokens.end();++titr) {
1360 	const char* token = *titr;
1361 	os << hex << strlen(token) << " " << token << "\n";
1362       }
1363     }
1364   }
1365   // ================================================
1366   // Terse format
1367   // ================================================
1368   else {
1369     string fn = "";
1370     statement::base::ACCESS stmt_access = statement::base::STMT_PUBLIC;
1371     statement::base::stmts_itr_t itr = stmts.begin();
1372     for(int i=0;itr!=stmts.end();++itr,++i) {
1373       statement::base* stmt = *itr;
1374 
1375       // Write out the modal file specifier.
1376       string new_fn = stmt->get_file();
1377       if( new_fn != fn ) {
1378 	// The file is modal.
1379 	fn = new_fn;
1380 	os << "f\n";
1381 	os << hex << mgr.get_idx(fn) << "\n";
1382       }
1383 
1384       // Write out the modal access specifier.
1385       if( stmt->get_access() != stmt_access ) {
1386 	os << "a "
1387 	   << hex << mgr.get_idx(stmt->get_terse_access_name())
1388 	   << "\n";
1389 	stmt_access = stmt->get_access();
1390      }
1391 
1392       // Write out the start of the statement: 's'.
1393       os << "s" << "\n";
1394       os << hex << mgr.get_idx(stmt->get_id()) << "\n";
1395       os << hex << stmt->get_tag() << "\n"; // tag
1396       if( stmt->get_parent() ) {
1397 	os << hex << stmt->get_parent()->get_tag() << "\n";
1398       }
1399       else {
1400 	os << "0\n";
1401       }
1402       os << hex << mgr.get_idx(stmt->get_terse_type_name()) << "\n";
1403       os << hex << mgr.get_idx(stmt->get_extern()) << "\n";
1404       os << hex << stmt->get_lineno() << "\n";
1405 
1406       const statement::base::cstrs_t& tokens = stmt->get_tokens();
1407       statement::base::cstrs_citr_t titr = tokens.begin();
1408       os << hex << tokens.size() << "\n";
1409       for(;titr!=tokens.end();++titr) {
1410 	const char* token = *titr;
1411 	os << hex << mgr.get_idx(token) << "\n";
1412       }
1413     }
1414   }
1415 
1416   os << "e\n";
1417   if( m_sw.verbose() ) {
1418     s_log << "db: write ends\n";
1419   }
1420 
1421 }
1422 // ================================================================
1423 // Get the next comment id.
1424 // ================================================================
get_next_comment_id(string & id)1425 void ccdoc::database::get_next_comment_id(string& id)
1426 {
1427   static char buf[64];
1428   sprintf(buf,"$comment-%d",m_comment_id);
1429   m_comment_id++;
1430   id = buf;
1431 }
1432 // ================================================================
1433 // Load all statements.
1434 // ================================================================
load(statement::base::stmts_t & vec)1435 void ccdoc::database::load(statement::base::stmts_t& vec)
1436 {
1437   load(vec,m_root);
1438 }
1439 // ================================================================
1440 // Load all statements.
1441 // ================================================================
load(statement::base::stmts_t & vec,statement::base * stmt)1442 void ccdoc::database::load(statement::base::stmts_t& vec,
1443 			   statement::base* stmt)
1444 {
1445   if(stmt) {
1446     vec.push_back(stmt);
1447     statement::base::stmts_t& children = stmt->get_children();
1448     statement::base::stmts_itr_t itr = children.begin();
1449     for(;itr!=children.end();++itr) {
1450       load(vec,*itr);
1451     }
1452   }
1453 }
1454 // ================================================================
1455 // Load all statements of a specific type.
1456 // ================================================================
load(statement::base::stmts_t & vec,statement::base::TYPE t)1457 void ccdoc::database::load(statement::base::stmts_t& vec,
1458 			   statement::base::TYPE t)
1459 {
1460   load(vec,t,m_root);
1461 }
1462 // ================================================================
1463 // Load all statements of a specific type.
1464 // ================================================================
load(statement::base::stmts_t & vec,statement::base::TYPE t,statement::base * stmt)1465 void ccdoc::database::load(statement::base::stmts_t& vec,
1466 			   statement::base::TYPE t,
1467 			   statement::base* stmt)
1468 {
1469   if(stmt) {
1470     if( stmt->get_type() == t )
1471       vec.push_back(stmt);
1472     statement::base::stmts_t& children = stmt->get_children();
1473     statement::base::stmts_itr_t itr = children.begin();
1474     for(;itr!=children.end();++itr) {
1475       load(vec,t,*itr);
1476     }
1477   }
1478 }
1479 // ================================================================
1480 // Load all statements of a specific type.
1481 // ================================================================
load_top(statement::base::stmts_t & vec,statement::base::TYPE t)1482 void ccdoc::database::load_top(statement::base::stmts_t& vec,
1483 			       statement::base::TYPE t)
1484 {
1485   load_top(vec,t,m_root);
1486 }
1487 // ================================================================
1488 // Load all statements of a specific type.
1489 // ================================================================
load_top(statement::base::stmts_t & vec,statement::base::TYPE t,statement::base * stmt)1490 void ccdoc::database::load_top(statement::base::stmts_t& vec,
1491 			       statement::base::TYPE t,
1492 			       statement::base* stmt)
1493 {
1494   if(stmt) {
1495     // Don't process statements unless their parent
1496     // is a namespace or a package.
1497     if( stmt->get_parent() ) {
1498       statement::base* parent = stmt->get_parent();
1499       if ( parent->get_type() != statement::base::STMT_PACKAGE &&
1500 	   parent->get_type() != statement::base::STMT_NAMESPACE_BEGIN )
1501 	return;
1502     }
1503     if( stmt->get_type() == t )
1504       vec.push_back(stmt);
1505     statement::base::stmts_t& children = stmt->get_children();
1506     statement::base::stmts_itr_t itr = children.begin();
1507     for(;itr!=children.end();++itr) {
1508       load_top(vec,t,*itr);
1509     }
1510   }
1511 }
1512 // ================================================================
1513 // Debug dump.
1514 // ================================================================
debug_dump(const char * prefix) const1515 void ccdoc::database::debug_dump(const char* prefix) const
1516 {
1517   debug_dump(prefix,m_root);
1518 }
1519 // ================================================================
1520 // Debug dump.
1521 // ================================================================
debug_dump(const char * prefix,statement::base * stmt) const1522 void ccdoc::database::debug_dump(const char* prefix,
1523 				 statement::base* stmt) const
1524 {
1525   if(stmt) {
1526     stmt->debug_dump(prefix);
1527     statement::base::stmts_t& vec = stmt->get_children();
1528     statement::base::stmts_itr_t itr = vec.begin();
1529     for(;itr!=vec.end();++itr) {
1530       debug_dump(prefix,*itr);
1531     }
1532   }
1533 }
1534