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