mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 22:58:20 +00:00
Internal Broxygen organization/documentation/polish.
This commit is contained in:
parent
27138b893a
commit
e58865af22
29 changed files with 2461 additions and 3024 deletions
667
src/BroDoc.cc
667
src/BroDoc.cc
|
@ -1,667 +0,0 @@
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <string>
|
|
||||||
#include <list>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <libgen.h>
|
|
||||||
|
|
||||||
#include "BroDoc.h"
|
|
||||||
#include "BroDocObj.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "plugin/Manager.h"
|
|
||||||
#include "analyzer/Manager.h"
|
|
||||||
#include "analyzer/Component.h"
|
|
||||||
#include "file_analysis/Manager.h"
|
|
||||||
#include "broxygen/Manager.h"
|
|
||||||
|
|
||||||
BroDoc::BroDoc(const std::string& rel, const std::string& abs)
|
|
||||||
{
|
|
||||||
size_t f_pos = abs.find_last_of('/');
|
|
||||||
|
|
||||||
if ( std::string::npos == f_pos )
|
|
||||||
source_filename = abs;
|
|
||||||
else
|
|
||||||
source_filename = abs.substr(f_pos + 1);
|
|
||||||
|
|
||||||
if ( rel[0] == '/' || rel[0] == '.' )
|
|
||||||
{
|
|
||||||
// The Bro script isn't being loaded via BROPATH, so just use basename
|
|
||||||
// as the document title.
|
|
||||||
doc_title = source_filename;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Keep the relative directory as part of the document title.
|
|
||||||
if ( rel.size() == 0 || rel[rel.size() - 1] == '/' )
|
|
||||||
doc_title = rel + source_filename;
|
|
||||||
else
|
|
||||||
doc_title = rel + "/" + source_filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadable_filename = source_filename;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
size_t ext_pos = downloadable_filename.find(".bif.bro");
|
|
||||||
if ( std::string::npos != ext_pos )
|
|
||||||
downloadable_filename.erase(ext_pos + 4);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
reST_filename = doc_title;
|
|
||||||
size_t ext_pos = reST_filename.find(".bro");
|
|
||||||
|
|
||||||
if ( std::string::npos == ext_pos )
|
|
||||||
reST_filename += ".rst";
|
|
||||||
else
|
|
||||||
reST_filename.replace(ext_pos, 4, ".rst");
|
|
||||||
|
|
||||||
reST_filename = doc_title.substr(0, ext_pos);
|
|
||||||
reST_filename += ".rst";
|
|
||||||
|
|
||||||
// Instead of re-creating the directory hierarchy based on related
|
|
||||||
// loads, just replace the directory separatories such that the reST
|
|
||||||
// output will all be placed in a flat directory (the working dir).
|
|
||||||
std::for_each(reST_filename.begin(), reST_filename.end(), replace_slash());
|
|
||||||
|
|
||||||
reST_file = fopen(reST_filename.c_str(), "w");
|
|
||||||
|
|
||||||
if ( ! reST_file )
|
|
||||||
fprintf(stderr, "Failed to open %s\n", reST_filename.c_str());
|
|
||||||
|
|
||||||
#ifdef DOCDEBUG
|
|
||||||
fprintf(stdout, "Documenting absolute source: %s\n", abs.c_str());
|
|
||||||
fprintf(stdout, "\trelative dir: %s\n", rel.c_str());
|
|
||||||
fprintf(stdout, "\tdoc title: %s\n", doc_title.c_str());
|
|
||||||
fprintf(stdout, "\tbro file: %s\n", source_filename.c_str());
|
|
||||||
fprintf(stdout, "\trst file: %s\n", reST_filename.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
BroDoc::~BroDoc()
|
|
||||||
{
|
|
||||||
if ( reST_file && fclose( reST_file ) )
|
|
||||||
fprintf(stderr, "Failed to close %s\n", reST_filename.c_str());
|
|
||||||
|
|
||||||
FreeBroDocObjPtrList(all);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::AddImport(const std::string& s)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
std::string lname(s);
|
|
||||||
// First strip any .bro extension.
|
|
||||||
size_t ext_pos = lname.find(".bro");
|
|
||||||
if ( ext_pos != std::string::npos )
|
|
||||||
lname = lname.substr(0, ext_pos);
|
|
||||||
|
|
||||||
const char* full_filename = NULL;
|
|
||||||
const char* subpath = NULL;
|
|
||||||
|
|
||||||
FILE* f = search_for_file(lname.c_str(), "bro", &full_filename, true,
|
|
||||||
&subpath);
|
|
||||||
|
|
||||||
if ( f && full_filename && subpath )
|
|
||||||
{
|
|
||||||
char* tmp = copy_string(full_filename);
|
|
||||||
char* filename = basename(tmp);
|
|
||||||
extern char* PACKAGE_LOADER;
|
|
||||||
|
|
||||||
if ( streq(filename, PACKAGE_LOADER) )
|
|
||||||
{
|
|
||||||
// link to the package's index
|
|
||||||
string pkg(subpath);
|
|
||||||
pkg += "/index";
|
|
||||||
imports.push_back(pkg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( subpath[0] == '/' || subpath[0] == '.' )
|
|
||||||
{
|
|
||||||
// it's not a subpath of scripts/, so just add the name of it
|
|
||||||
// as it's given in the @load directive
|
|
||||||
imports.push_back(lname);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// combine the base file name of script in the @load directive
|
|
||||||
// with the subpath of BROPATH's scripts/ directory
|
|
||||||
string fname(subpath);
|
|
||||||
char* othertmp = copy_string(lname.c_str());
|
|
||||||
fname.append("/").append(basename(othertmp));
|
|
||||||
imports.push_back(fname);
|
|
||||||
delete [] othertmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete [] tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
fprintf(stderr, "Failed to document '@load %s' in file: %s\n",
|
|
||||||
s.c_str(), reST_filename.c_str());
|
|
||||||
|
|
||||||
if ( f )
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
delete [] full_filename;
|
|
||||||
delete [] subpath;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::SetPacketFilter(const std::string& s)
|
|
||||||
{
|
|
||||||
packet_filter = s;
|
|
||||||
size_t pos1 = s.find("{\n");
|
|
||||||
size_t pos2 = s.find("}");
|
|
||||||
|
|
||||||
if ( pos1 != std::string::npos && pos2 != std::string::npos )
|
|
||||||
packet_filter = s.substr(pos1 + 2, pos2 - 2);
|
|
||||||
|
|
||||||
bool has_non_whitespace = false;
|
|
||||||
|
|
||||||
for ( std::string::const_iterator it = packet_filter.begin();
|
|
||||||
it != packet_filter.end(); ++it )
|
|
||||||
{
|
|
||||||
if ( *it != ' ' && *it != '\t' && *it != '\n' && *it != '\r' )
|
|
||||||
{
|
|
||||||
has_non_whitespace = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! has_non_whitespace )
|
|
||||||
packet_filter.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::WriteDocFile() const
|
|
||||||
{
|
|
||||||
WriteToDoc(reST_file, ".. Automatically generated. Do not edit.\n\n");
|
|
||||||
|
|
||||||
WriteToDoc(reST_file, ":tocdepth: 3\n\n");
|
|
||||||
|
|
||||||
WriteSectionHeading(reST_file, doc_title.c_str(), '=');
|
|
||||||
|
|
||||||
WriteStringList(reST_file, ".. bro:namespace:: %s\n", modules);
|
|
||||||
|
|
||||||
WriteToDoc(reST_file, "\n");
|
|
||||||
|
|
||||||
// WriteSectionHeading(reST_file, "Overview", '-');
|
|
||||||
WriteStringList(reST_file, "%s\n", summary);
|
|
||||||
|
|
||||||
WriteToDoc(reST_file, "\n");
|
|
||||||
|
|
||||||
if ( ! modules.empty() )
|
|
||||||
{
|
|
||||||
WriteToDoc(reST_file, ":Namespace%s: ", (modules.size() > 1 ? "s" : ""));
|
|
||||||
// WriteStringList(reST_file, ":bro:namespace:`%s`", modules);
|
|
||||||
WriteStringList(reST_file, "``%s``, ", "``%s``", modules);
|
|
||||||
WriteToDoc(reST_file, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! imports.empty() )
|
|
||||||
{
|
|
||||||
WriteToDoc(reST_file, ":Imports: ");
|
|
||||||
std::list<std::string>::const_iterator it;
|
|
||||||
for ( it = imports.begin(); it != imports.end(); ++it )
|
|
||||||
{
|
|
||||||
if ( it != imports.begin() )
|
|
||||||
WriteToDoc(reST_file, ", ");
|
|
||||||
|
|
||||||
string pretty(*it);
|
|
||||||
size_t pos = pretty.find("/index");
|
|
||||||
if ( pos != std::string::npos && pos + 6 == pretty.size() )
|
|
||||||
pretty = pretty.substr(0, pos);
|
|
||||||
WriteToDoc(reST_file, ":doc:`%s </scripts/%s>`", pretty.c_str(), it->c_str());
|
|
||||||
}
|
|
||||||
WriteToDoc(reST_file, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteToDoc(reST_file, ":Source File: :download:`%s`\n",
|
|
||||||
downloadable_filename.c_str());
|
|
||||||
|
|
||||||
WriteToDoc(reST_file, "\n");
|
|
||||||
|
|
||||||
WriteInterface("Summary", '~', '#', true, true);
|
|
||||||
|
|
||||||
if ( ! notices.empty() )
|
|
||||||
WriteBroDocObjList(reST_file, notices, "Notices", '#');
|
|
||||||
|
|
||||||
if ( port_analysis.size() || packet_filter.size() )
|
|
||||||
WriteSectionHeading(reST_file, "Configuration Changes", '#');
|
|
||||||
|
|
||||||
if ( ! port_analysis.empty() )
|
|
||||||
{
|
|
||||||
WriteSectionHeading(reST_file, "Port Analysis", '^');
|
|
||||||
WriteToDoc(reST_file, "Loading this script makes the following changes to "
|
|
||||||
":bro:see:`dpd_config`.\n\n");
|
|
||||||
WriteStringList(reST_file, "%s, ", "%s", port_analysis);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! packet_filter.empty() )
|
|
||||||
{
|
|
||||||
WriteSectionHeading(reST_file, "Packet Filter", '^');
|
|
||||||
WriteToDoc(reST_file, "Loading this script makes the following changes to "
|
|
||||||
":bro:see:`capture_filters`.\n\n");
|
|
||||||
WriteToDoc(reST_file, "Filters added::\n\n");
|
|
||||||
WriteToDoc(reST_file, "%s\n", packet_filter.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteInterface("Detailed Interface", '~', '#', true, false);
|
|
||||||
|
|
||||||
#if 0 // Disabled for now.
|
|
||||||
BroDocObjList::const_iterator it;
|
|
||||||
bool hasPrivateIdentifiers = false;
|
|
||||||
|
|
||||||
for ( it = all.begin(); it != all.end(); ++it )
|
|
||||||
{
|
|
||||||
if ( ! IsPublicAPI(*it) )
|
|
||||||
{
|
|
||||||
hasPrivateIdentifiers = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( hasPrivateIdentifiers )
|
|
||||||
WriteInterface("Private Interface", '~', '#', false, false);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::WriteInterface(const char* heading, char underline,
|
|
||||||
char sub, bool isPublic, bool isShort) const
|
|
||||||
{
|
|
||||||
WriteSectionHeading(reST_file, heading, underline);
|
|
||||||
WriteBroDocObjList(reST_file, options, isPublic, "Options", sub, isShort);
|
|
||||||
WriteBroDocObjList(reST_file, constants, isPublic, "Constants", sub, isShort);
|
|
||||||
WriteBroDocObjList(reST_file, state_vars, isPublic, "State Variables", sub, isShort);
|
|
||||||
WriteBroDocObjList(reST_file, types, isPublic, "Types", sub, isShort);
|
|
||||||
WriteBroDocObjList(reST_file, events, isPublic, "Events", sub, isShort);
|
|
||||||
WriteBroDocObjList(reST_file, hooks, isPublic, "Hooks", sub, isShort);
|
|
||||||
WriteBroDocObjList(reST_file, functions, isPublic, "Functions", sub, isShort);
|
|
||||||
WriteBroDocObjList(reST_file, redefs, isPublic, "Redefinitions", sub, isShort);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::WriteStringList(FILE* f, const char* format, const char* last_format,
|
|
||||||
const std::list<std::string>& l)
|
|
||||||
{
|
|
||||||
if ( l.empty() )
|
|
||||||
{
|
|
||||||
WriteToDoc(f, "\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<std::string>::const_iterator it;
|
|
||||||
std::list<std::string>::const_iterator last = l.end();
|
|
||||||
last--;
|
|
||||||
|
|
||||||
for ( it = l.begin(); it != last; ++it )
|
|
||||||
WriteToDoc(f, format, it->c_str());
|
|
||||||
|
|
||||||
WriteToDoc(f, last_format, last->c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::WriteBroDocObjTable(FILE* f, const BroDocObjList& l)
|
|
||||||
{
|
|
||||||
int max_id_col = 0;
|
|
||||||
int max_com_col = 0;
|
|
||||||
BroDocObjList::const_iterator it;
|
|
||||||
|
|
||||||
for ( it = l.begin(); it != l.end(); ++it )
|
|
||||||
{
|
|
||||||
int c = (*it)->ColumnSize();
|
|
||||||
|
|
||||||
if ( c > max_id_col )
|
|
||||||
max_id_col = c;
|
|
||||||
|
|
||||||
c = (*it)->LongestShortDescLen();
|
|
||||||
|
|
||||||
if ( c > max_com_col )
|
|
||||||
max_com_col = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start table.
|
|
||||||
WriteRepeatedChar(f, '=', max_id_col);
|
|
||||||
WriteToDoc(f, " ");
|
|
||||||
|
|
||||||
if ( max_com_col == 0 )
|
|
||||||
WriteToDoc(f, "=");
|
|
||||||
else
|
|
||||||
WriteRepeatedChar(f, '=', max_com_col);
|
|
||||||
|
|
||||||
WriteToDoc(f, "\n");
|
|
||||||
|
|
||||||
for ( it = l.begin(); it != l.end(); ++it )
|
|
||||||
{
|
|
||||||
if ( it != l.begin() )
|
|
||||||
WriteToDoc(f, "\n\n");
|
|
||||||
(*it)->WriteReSTCompact(f, max_id_col);
|
|
||||||
}
|
|
||||||
|
|
||||||
// End table.
|
|
||||||
WriteToDoc(f, "\n");
|
|
||||||
WriteRepeatedChar(f, '=', max_id_col);
|
|
||||||
WriteToDoc(f, " ");
|
|
||||||
|
|
||||||
if ( max_com_col == 0 )
|
|
||||||
WriteToDoc(f, "=");
|
|
||||||
else
|
|
||||||
WriteRepeatedChar(f, '=', max_com_col);
|
|
||||||
|
|
||||||
WriteToDoc(f, "\n\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::WriteBroDocObjList(FILE* f, const BroDocObjList& l, bool wantPublic,
|
|
||||||
const char* heading, char underline, bool isShort)
|
|
||||||
{
|
|
||||||
if ( l.empty() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
BroDocObjList::const_iterator it;
|
|
||||||
bool (*f_ptr)(const BroDocObj* o) = 0;
|
|
||||||
|
|
||||||
if ( wantPublic )
|
|
||||||
f_ptr = IsPublicAPI;
|
|
||||||
else
|
|
||||||
f_ptr = IsPrivateAPI;
|
|
||||||
|
|
||||||
it = std::find_if(l.begin(), l.end(), f_ptr);
|
|
||||||
|
|
||||||
if ( it == l.end() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
WriteSectionHeading(f, heading, underline);
|
|
||||||
|
|
||||||
BroDocObjList filtered_list;
|
|
||||||
|
|
||||||
while ( it != l.end() )
|
|
||||||
{
|
|
||||||
filtered_list.push_back(*it);
|
|
||||||
it = find_if(++it, l.end(), f_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( isShort )
|
|
||||||
WriteBroDocObjTable(f, filtered_list);
|
|
||||||
else
|
|
||||||
WriteBroDocObjList(f, filtered_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::WriteBroDocObjList(FILE* f, const BroDocObjMap& m, bool wantPublic,
|
|
||||||
const char* heading, char underline, bool isShort)
|
|
||||||
{
|
|
||||||
BroDocObjMap::const_iterator it;
|
|
||||||
BroDocObjList l;
|
|
||||||
|
|
||||||
for ( it = m.begin(); it != m.end(); ++it )
|
|
||||||
l.push_back(it->second);
|
|
||||||
|
|
||||||
WriteBroDocObjList(f, l, wantPublic, heading, underline, isShort);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::WriteBroDocObjList(FILE* f, const BroDocObjList& l, const char* heading,
|
|
||||||
char underline)
|
|
||||||
{
|
|
||||||
WriteSectionHeading(f, heading, underline);
|
|
||||||
WriteBroDocObjList(f, l);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::WriteBroDocObjList(FILE* f, const BroDocObjList& l)
|
|
||||||
{
|
|
||||||
for ( BroDocObjList::const_iterator it = l.begin(); it != l.end(); ++it )
|
|
||||||
(*it)->WriteReST(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::WriteBroDocObjList(FILE* f, const BroDocObjMap& m, const char* heading,
|
|
||||||
char underline)
|
|
||||||
{
|
|
||||||
BroDocObjMap::const_iterator it;
|
|
||||||
BroDocObjList l;
|
|
||||||
|
|
||||||
for ( it = m.begin(); it != m.end(); ++it )
|
|
||||||
l.push_back(it->second);
|
|
||||||
|
|
||||||
WriteBroDocObjList(f, l, heading, underline);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::WriteToDoc(FILE* f, const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list argp;
|
|
||||||
va_start(argp, format);
|
|
||||||
vfprintf(f, format, argp);
|
|
||||||
va_end(argp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::WriteSectionHeading(FILE* f, const char* heading, char underline)
|
|
||||||
{
|
|
||||||
WriteToDoc(f, "%s\n", heading);
|
|
||||||
WriteRepeatedChar(f, underline, strlen(heading));
|
|
||||||
WriteToDoc(f, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::WriteRepeatedChar(FILE* f, char c, size_t n)
|
|
||||||
{
|
|
||||||
for ( size_t i = 0; i < n; ++i )
|
|
||||||
WriteToDoc(f, "%c", c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::FreeBroDocObjPtrList(BroDocObjList& l)
|
|
||||||
{
|
|
||||||
for ( BroDocObjList::const_iterator it = l.begin(); it != l.end(); ++it )
|
|
||||||
delete *it;
|
|
||||||
|
|
||||||
l.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDoc::AddFunction(BroDocObj* o)
|
|
||||||
{
|
|
||||||
BroDocObjMap::const_iterator it = functions.find(o->Name());
|
|
||||||
if ( it == functions.end() )
|
|
||||||
{
|
|
||||||
functions[o->Name()] = o;
|
|
||||||
all.push_back(o);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
functions[o->Name()]->Combine(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void WritePluginSectionHeading(FILE* f, const plugin::Plugin* p)
|
|
||||||
{
|
|
||||||
string name = p->Name();
|
|
||||||
|
|
||||||
fprintf(f, "%s\n", name.c_str());
|
|
||||||
for ( size_t i = 0; i < name.size(); ++i )
|
|
||||||
fprintf(f, "-");
|
|
||||||
fprintf(f, "\n\n");
|
|
||||||
|
|
||||||
fprintf(f, "%s\n\n", p->Description());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void WriteAnalyzerComponent(FILE* f, const analyzer::Component* c)
|
|
||||||
{
|
|
||||||
EnumType* atag = analyzer_mgr->GetTagEnumType();
|
|
||||||
string tag = fmt("ANALYZER_%s", c->CanonicalName());
|
|
||||||
|
|
||||||
if ( atag->Lookup("Analyzer", tag.c_str()) < 0 )
|
|
||||||
reporter->InternalError("missing analyzer tag for %s", tag.c_str());
|
|
||||||
|
|
||||||
fprintf(f, ":bro:enum:`Analyzer::%s`\n\n", tag.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void WriteAnalyzerComponent(FILE* f, const file_analysis::Component* c)
|
|
||||||
{
|
|
||||||
EnumType* atag = file_mgr->GetTagEnumType();
|
|
||||||
string tag = fmt("ANALYZER_%s", c->CanonicalName());
|
|
||||||
|
|
||||||
if ( atag->Lookup("Files", tag.c_str()) < 0 )
|
|
||||||
reporter->InternalError("missing analyzer tag for %s", tag.c_str());
|
|
||||||
|
|
||||||
fprintf(f, ":bro:enum:`Files::%s`\n\n", tag.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void WritePluginComponents(FILE* f, const plugin::Plugin* p)
|
|
||||||
{
|
|
||||||
plugin::Plugin::component_list components = p->Components();
|
|
||||||
plugin::Plugin::component_list::const_iterator it;
|
|
||||||
|
|
||||||
fprintf(f, "Components\n");
|
|
||||||
fprintf(f, "++++++++++\n\n");
|
|
||||||
|
|
||||||
for ( it = components.begin(); it != components.end(); ++it )
|
|
||||||
{
|
|
||||||
switch ( (*it)->Type() ) {
|
|
||||||
case plugin::component::ANALYZER:
|
|
||||||
{
|
|
||||||
const analyzer::Component* c =
|
|
||||||
dynamic_cast<const analyzer::Component*>(*it);
|
|
||||||
|
|
||||||
if ( c )
|
|
||||||
WriteAnalyzerComponent(f, c);
|
|
||||||
else
|
|
||||||
reporter->InternalError("component type mismatch");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case plugin::component::FILE_ANALYZER:
|
|
||||||
{
|
|
||||||
const file_analysis::Component* c =
|
|
||||||
dynamic_cast<const file_analysis::Component*>(*it);
|
|
||||||
|
|
||||||
if ( c )
|
|
||||||
WriteAnalyzerComponent(f, c);
|
|
||||||
else
|
|
||||||
reporter->InternalError("component type mismatch");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case plugin::component::READER:
|
|
||||||
reporter->InternalError("docs for READER component unimplemented");
|
|
||||||
|
|
||||||
case plugin::component::WRITER:
|
|
||||||
reporter->InternalError("docs for WRITER component unimplemented");
|
|
||||||
|
|
||||||
default:
|
|
||||||
reporter->InternalError("docs for unknown component unimplemented");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void WritePluginBifItems(FILE* f, const plugin::Plugin* p,
|
|
||||||
plugin::BifItem::Type t, const string& heading)
|
|
||||||
{
|
|
||||||
plugin::Plugin::bif_item_list bifitems = p->BifItems();
|
|
||||||
plugin::Plugin::bif_item_list::iterator it = bifitems.begin();
|
|
||||||
|
|
||||||
while ( it != bifitems.end() )
|
|
||||||
{
|
|
||||||
if ( it->GetType() != t )
|
|
||||||
it = bifitems.erase(it);
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( bifitems.empty() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
fprintf(f, "%s\n", heading.c_str());
|
|
||||||
for ( size_t i = 0; i < heading.size(); ++i )
|
|
||||||
fprintf(f, "+");
|
|
||||||
fprintf(f, "\n\n");
|
|
||||||
|
|
||||||
for ( it = bifitems.begin(); it != bifitems.end(); ++it )
|
|
||||||
{
|
|
||||||
broxygen::IdentifierDocument* doc = broxygen_mgr->GetIdentifierDoc(
|
|
||||||
it->GetID());
|
|
||||||
|
|
||||||
if ( doc )
|
|
||||||
fprintf(f, "%s\n\n", doc->ReStructuredText().c_str());
|
|
||||||
else
|
|
||||||
reporter->InternalWarning("Broxygen ID lookup failed: %s\n",
|
|
||||||
it->GetID());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void WriteAnalyzerTagDefn(FILE* f, const string& module)
|
|
||||||
{
|
|
||||||
string tag_id = module + "::Tag";
|
|
||||||
|
|
||||||
broxygen::IdentifierDocument* doc = broxygen_mgr->GetIdentifierDoc(tag_id);
|
|
||||||
|
|
||||||
if ( ! doc )
|
|
||||||
reporter->InternalError("Broxygen failed analyzer tag lookup: %s",
|
|
||||||
tag_id.c_str());
|
|
||||||
|
|
||||||
fprintf(f, "%s\n", doc->ReStructuredText().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ComponentsMatch(const plugin::Plugin* p, plugin::component::Type t,
|
|
||||||
bool match_empty = false)
|
|
||||||
{
|
|
||||||
plugin::Plugin::component_list components = p->Components();
|
|
||||||
plugin::Plugin::component_list::const_iterator it;
|
|
||||||
|
|
||||||
if ( components.empty() )
|
|
||||||
return match_empty;
|
|
||||||
|
|
||||||
for ( it = components.begin(); it != components.end(); ++it )
|
|
||||||
if ( (*it)->Type() != t )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateProtoAnalyzerDoc(FILE* f)
|
|
||||||
{
|
|
||||||
fprintf(f, "Protocol Analyzers\n");
|
|
||||||
fprintf(f, "==================\n\n");
|
|
||||||
fprintf(f, ".. contents::\n");
|
|
||||||
fprintf(f, " :depth: 2\n\n");
|
|
||||||
|
|
||||||
WriteAnalyzerTagDefn(f, "Analyzer");
|
|
||||||
|
|
||||||
plugin::Manager::plugin_list plugins = plugin_mgr->Plugins();
|
|
||||||
plugin::Manager::plugin_list::const_iterator it;
|
|
||||||
|
|
||||||
for ( it = plugins.begin(); it != plugins.end(); ++it )
|
|
||||||
{
|
|
||||||
if ( ! ComponentsMatch(*it, plugin::component::ANALYZER, true) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
WritePluginSectionHeading(f, *it);
|
|
||||||
WritePluginComponents(f, *it);
|
|
||||||
WritePluginBifItems(f, *it, plugin::BifItem::CONSTANT,
|
|
||||||
"Options/Constants");
|
|
||||||
WritePluginBifItems(f, *it, plugin::BifItem::GLOBAL, "Globals");
|
|
||||||
WritePluginBifItems(f, *it, plugin::BifItem::TYPE, "Types");
|
|
||||||
WritePluginBifItems(f, *it, plugin::BifItem::EVENT, "Events");
|
|
||||||
WritePluginBifItems(f, *it, plugin::BifItem::FUNCTION, "Functions");
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateFileAnalyzerDoc(FILE* f)
|
|
||||||
{
|
|
||||||
fprintf(f, "File Analyzers\n");
|
|
||||||
fprintf(f, "==============\n\n");
|
|
||||||
fprintf(f, ".. contents::\n");
|
|
||||||
fprintf(f, " :depth: 2\n\n");
|
|
||||||
|
|
||||||
WriteAnalyzerTagDefn(f, "Files");
|
|
||||||
|
|
||||||
plugin::Manager::plugin_list plugins = plugin_mgr->Plugins();
|
|
||||||
plugin::Manager::plugin_list::const_iterator it;
|
|
||||||
|
|
||||||
for ( it = plugins.begin(); it != plugins.end(); ++it )
|
|
||||||
{
|
|
||||||
if ( ! ComponentsMatch(*it, plugin::component::FILE_ANALYZER) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
WritePluginSectionHeading(f, *it);
|
|
||||||
WritePluginComponents(f, *it);
|
|
||||||
WritePluginBifItems(f, *it, plugin::BifItem::CONSTANT,
|
|
||||||
"Options/Constants");
|
|
||||||
WritePluginBifItems(f, *it, plugin::BifItem::GLOBAL, "Globals");
|
|
||||||
WritePluginBifItems(f, *it, plugin::BifItem::TYPE, "Types");
|
|
||||||
WritePluginBifItems(f, *it, plugin::BifItem::EVENT, "Events");
|
|
||||||
WritePluginBifItems(f, *it, plugin::BifItem::FUNCTION, "Functions");
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
422
src/BroDoc.h
422
src/BroDoc.h
|
@ -1,422 +0,0 @@
|
||||||
#ifndef brodoc_h
|
|
||||||
#define brodoc_h
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <string>
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
#include "BroDocObj.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is used to gather all data relevant to the automatic generation
|
|
||||||
* of a reStructuredText (reST) document from a given Bro script.
|
|
||||||
*/
|
|
||||||
class BroDoc {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* BroDoc constructor
|
|
||||||
* Given a Bro script, opens new file in the current working directory
|
|
||||||
* that will contain reST documentation generated from the parsing
|
|
||||||
* of the Bro script. The new reST file will be named similar to
|
|
||||||
* the filename of the Bro script that generates it, except any
|
|
||||||
* ".bro" file extension is stripped and ".rst" takes it place.
|
|
||||||
* If the filename doesn't end in ".bro", then ".rst" is just appended.
|
|
||||||
* Any '/' characters in the reST file name that result from choice of
|
|
||||||
* the 'rel' parameter are replaced with '^'.
|
|
||||||
* @param rel A string representing a subpath of the root Bro script
|
|
||||||
* source/install directory in which the source file is located.
|
|
||||||
* It can also be an absolute path, but then the parameter is
|
|
||||||
* ignored and the document title is just derived from file name
|
|
||||||
* @param abs The absolute path to the Bro script for which to generate
|
|
||||||
* documentation.
|
|
||||||
*/
|
|
||||||
BroDoc(const std::string& rel, const std::string& abs);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BroDoc destructor
|
|
||||||
* Closes the file that was opened by the constructor and frees up
|
|
||||||
* memory taken by BroDocObj objects.
|
|
||||||
*/
|
|
||||||
virtual ~BroDoc();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write out full reST documentation for the Bro script that was parsed.
|
|
||||||
* BroDoc's default implementation of this function will care
|
|
||||||
* about whether declarations made in the Bro script are part of
|
|
||||||
* the public versus private interface (whether things are declared in
|
|
||||||
* the export section).
|
|
||||||
*/
|
|
||||||
virtual void WriteDocFile() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules some summarizing text to be output directly into the reST doc.
|
|
||||||
* This should be called whenever the scanner sees a line in the Bro script
|
|
||||||
* starting with "##!"
|
|
||||||
* @param s The summary text to add to the reST doc.
|
|
||||||
*/
|
|
||||||
void AddSummary(const std::string& s) { summary.push_back(s); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules an import (@load) to be documented.
|
|
||||||
* If the script being loaded has a .bro suffix, it is internally stripped.
|
|
||||||
* This should be called whenever the scanner sees an @load.
|
|
||||||
* @param s The name of the imported script.
|
|
||||||
*/
|
|
||||||
void AddImport(const std::string& s);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules a namespace (module) to be documented.
|
|
||||||
* This should be called whenever the parser sees a TOK_MODULE.
|
|
||||||
* @param s The namespace (module) identifier's name.
|
|
||||||
*/
|
|
||||||
void AddModule(const std::string& s) { modules.push_back(s); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the way the script changes the "capture_filters" table.
|
|
||||||
* This is determined by the scanner checking for changes to
|
|
||||||
* the "capture_filters" table after each of Bro's input scripts
|
|
||||||
* (given as command line arguments to Bro) are finished being parsed.
|
|
||||||
* @param s The value "capture_filters" as given by TableVal::Describe()
|
|
||||||
*/
|
|
||||||
void SetPacketFilter(const std::string& s);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules documentation of a script option. An option is
|
|
||||||
* defined as any variable in the script that is declared 'const'
|
|
||||||
* and has the '&redef' attribute.
|
|
||||||
* @param o A pointer to a BroDocObj which contains the internal
|
|
||||||
* Bro language representation of the script option and
|
|
||||||
* also any associated comments about it.
|
|
||||||
*/
|
|
||||||
void AddOption(const BroDocObj* o)
|
|
||||||
{
|
|
||||||
options.push_back(o);
|
|
||||||
all.push_back(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules documentation of a script constant. An option is
|
|
||||||
* defined as any variable in the script that is declared 'const'
|
|
||||||
* and does *not* have the '&redef' attribute.
|
|
||||||
* @param o A pointer to a BroDocObj which contains the internal
|
|
||||||
* Bro language representation of the script constant and
|
|
||||||
* also any associated comments about it.
|
|
||||||
*/
|
|
||||||
void AddConstant(const BroDocObj* o)
|
|
||||||
{
|
|
||||||
constants.push_back(o);
|
|
||||||
all.push_back(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules documentation of a script state variable. A state variable
|
|
||||||
* is defined as any variable in the script that is declared 'global'
|
|
||||||
* @param o A pointer to a BroDocObj which contains the internal
|
|
||||||
* Bro language representation of the script state variable
|
|
||||||
* and also any associated comments about it.
|
|
||||||
*/
|
|
||||||
void AddStateVar(const BroDocObj* o)
|
|
||||||
{
|
|
||||||
state_vars.push_back(o);
|
|
||||||
all.push_back(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules documentation of a type declared by the script.
|
|
||||||
* @param o A pointer to a BroDocObj which contains the internal
|
|
||||||
* Bro language representation of the script option and
|
|
||||||
* also any associated comments about it.
|
|
||||||
*/
|
|
||||||
void AddType(const BroDocObj* o)
|
|
||||||
{
|
|
||||||
types.push_back(o);
|
|
||||||
all.push_back(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules documentation of a Notice (enum redef) declared by script
|
|
||||||
* @param o A pointer to a BroDocObj which contains the internal
|
|
||||||
* Bro language representation of the Notice and also
|
|
||||||
* any associated comments about it.
|
|
||||||
*/
|
|
||||||
void AddNotice(const BroDocObj* o)
|
|
||||||
{
|
|
||||||
notices.push_back(o);
|
|
||||||
all.push_back(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules documentation of an event declared by the script.
|
|
||||||
* @param o A pointer to a BroDocObj which contains the internal
|
|
||||||
* Bro language representation of the script event and
|
|
||||||
* also any associated comments about it.
|
|
||||||
*/
|
|
||||||
void AddEvent(const BroDocObj* o)
|
|
||||||
{
|
|
||||||
events.push_back(o);
|
|
||||||
all.push_back(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules documentation of an event handler declared by the script.
|
|
||||||
* @param o A pointer to a BroDocObj which contains the internal
|
|
||||||
* Bro language representation of the script event handler and
|
|
||||||
* also any associated comments about it.
|
|
||||||
*/
|
|
||||||
void AddEventHandler(const BroDocObj* o)
|
|
||||||
{
|
|
||||||
event_handlers.push_back(o);
|
|
||||||
all.push_back(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules documentation of a hook declared by the script.
|
|
||||||
* @param o A pointer to a BroDocObj which contains the internal
|
|
||||||
* Bro language representation of the script hook and
|
|
||||||
* also any associated comments about it.
|
|
||||||
*/
|
|
||||||
void AddHook(const BroDocObj* o)
|
|
||||||
{
|
|
||||||
hooks.push_back(o);
|
|
||||||
all.push_back(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules documentation of a hook handler declared by the script.
|
|
||||||
* @param o A pointer to a BroDocObj which contains the internal
|
|
||||||
* Bro language representation of the script hook handler and
|
|
||||||
* also any associated comments about it.
|
|
||||||
*/
|
|
||||||
void AddHookHandler(const BroDocObj* o)
|
|
||||||
{
|
|
||||||
hook_handlers.push_back(o);
|
|
||||||
all.push_back(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules documentation of a function declared by the script.
|
|
||||||
* @param o A pointer to a BroDocObj which contains the internal
|
|
||||||
* Bro language representation of the script function and
|
|
||||||
* also any associated comments about it.
|
|
||||||
*/
|
|
||||||
void AddFunction(BroDocObj* o);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules documentation of a redef done by the script
|
|
||||||
* @param o A pointer to a BroDocObj which contains the internal
|
|
||||||
* Bro language representation of the script identifier
|
|
||||||
* that was redefined and also any associated comments.
|
|
||||||
*/
|
|
||||||
void AddRedef(const BroDocObj* o)
|
|
||||||
{
|
|
||||||
redefs.push_back(o);
|
|
||||||
all.push_back(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the name of the Bro script source file for which reST
|
|
||||||
* documentation is being generated.
|
|
||||||
* @return A char* to the start of the source file's name.
|
|
||||||
*/
|
|
||||||
const char* GetSourceFileName() const
|
|
||||||
{
|
|
||||||
return source_filename.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the name of the generated reST documentation file.
|
|
||||||
* @return A char* to the start of the generated reST file's name.
|
|
||||||
*/
|
|
||||||
const char* GetOutputFileName() const
|
|
||||||
{
|
|
||||||
return reST_filename.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef std::list<const BroDocObj*> BroDocObjList;
|
|
||||||
typedef std::map<std::string, BroDocObj*> BroDocObjMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes out a table of BroDocObj's to the reST document
|
|
||||||
* @param f The file to write to.
|
|
||||||
* @param l A list of BroDocObj pointers
|
|
||||||
*/
|
|
||||||
static void WriteBroDocObjTable(FILE* f, const BroDocObjList& l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes out given number of characters to reST document
|
|
||||||
* @param f The file to write to.
|
|
||||||
* @param c the character to write
|
|
||||||
* @param n the number of characters to write
|
|
||||||
*/
|
|
||||||
static void WriteRepeatedChar(FILE* f, char c, size_t n);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper to fprintf() that always uses the reST document
|
|
||||||
* for the FILE* argument.
|
|
||||||
* @param f The file to write to.
|
|
||||||
* @param format A printf style format string.
|
|
||||||
*/
|
|
||||||
static void WriteToDoc(FILE* f, const char* format, ...);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes out a list of strings to the reST document.
|
|
||||||
* If the list is empty, prints a newline character.
|
|
||||||
* @param f The file to write to.
|
|
||||||
* @param format A printf style format string for elements of the list
|
|
||||||
* except for the last one in the list
|
|
||||||
* @param last_format A printf style format string to use for the last
|
|
||||||
* element of the list
|
|
||||||
* @param l A reference to a list of strings
|
|
||||||
*/
|
|
||||||
static void WriteStringList(FILE* f, const char* format, const char* last_format,
|
|
||||||
const std::list<std::string>& l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see WriteStringList(FILE* f, const char*, const char*,
|
|
||||||
* const std::list<std::string>&>)
|
|
||||||
*/
|
|
||||||
static void WriteStringList(FILE* f, const char* format,
|
|
||||||
const std::list<std::string>& l){
|
|
||||||
WriteStringList(f, format, format, l);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes out a list of BroDocObj objects to the reST document
|
|
||||||
* @param f The file to write to.
|
|
||||||
* @param l A list of BroDocObj pointers
|
|
||||||
* @param wantPublic If true, filter out objects that are not declared
|
|
||||||
* in the global scope. If false, filter out those that are in
|
|
||||||
* the global scope.
|
|
||||||
* @param heading The title of the section to create in the reST doc.
|
|
||||||
* @param underline The character to use to underline the reST
|
|
||||||
* section heading.
|
|
||||||
* @param isShort Whether to write the full documentation or a "short"
|
|
||||||
* version (a single sentence)
|
|
||||||
*/
|
|
||||||
static void WriteBroDocObjList(FILE* f, const BroDocObjList& l, bool wantPublic,
|
|
||||||
const char* heading, char underline,
|
|
||||||
bool isShort);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps the BroDocObjMap into a BroDocObjList and the writes that list
|
|
||||||
* to the reST document
|
|
||||||
* @see WriteBroDocObjList(FILE* f, const BroDocObjList&, bool, const char*, char,
|
|
||||||
bool)
|
|
||||||
*/
|
|
||||||
static void WriteBroDocObjList(FILE* f, const BroDocObjMap& m, bool wantPublic,
|
|
||||||
const char* heading, char underline,
|
|
||||||
bool isShort);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes out a list of BroDocObj objects to the reST document
|
|
||||||
* @param l A list of BroDocObj pointers
|
|
||||||
* @param heading The title of the section to create in the reST doc.
|
|
||||||
* @param underline The character to use to underline the reST
|
|
||||||
* section heading.
|
|
||||||
*/
|
|
||||||
static void WriteBroDocObjList(FILE* f, const BroDocObjList& l, const char* heading,
|
|
||||||
char underline);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes out a list of BroDocObj objects to the reST document
|
|
||||||
* @param l A list of BroDocObj pointers
|
|
||||||
*/
|
|
||||||
static void WriteBroDocObjList(FILE* f, const BroDocObjList& l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps the BroDocObjMap into a BroDocObjList and the writes that list
|
|
||||||
* to the reST document
|
|
||||||
* @see WriteBroDocObjList(FILE* f, const BroDocObjList&, const char*, char)
|
|
||||||
*/
|
|
||||||
static void WriteBroDocObjList(FILE* f, const BroDocObjMap& m, const char* heading,
|
|
||||||
char underline);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes out a reST section heading
|
|
||||||
* @param f The file to write to.
|
|
||||||
* @param heading The title of the heading to create
|
|
||||||
* @param underline The character to use to underline the section title
|
|
||||||
* within the reST document
|
|
||||||
*/
|
|
||||||
static void WriteSectionHeading(FILE* f, const char* heading, char underline);
|
|
||||||
|
|
||||||
private:
|
|
||||||
FILE* reST_file;
|
|
||||||
std::string reST_filename;
|
|
||||||
std::string source_filename; // points to the basename of source file
|
|
||||||
std::string downloadable_filename; // file that will be linked for download
|
|
||||||
std::string doc_title;
|
|
||||||
std::string packet_filter;
|
|
||||||
|
|
||||||
std::list<std::string> modules;
|
|
||||||
std::list<std::string> summary;
|
|
||||||
std::list<std::string> imports;
|
|
||||||
std::list<std::string> port_analysis;
|
|
||||||
|
|
||||||
BroDocObjList options;
|
|
||||||
BroDocObjList constants;
|
|
||||||
BroDocObjList state_vars;
|
|
||||||
BroDocObjList types;
|
|
||||||
BroDocObjList notices;
|
|
||||||
BroDocObjList events;
|
|
||||||
BroDocObjList event_handlers;
|
|
||||||
BroDocObjList hooks;
|
|
||||||
BroDocObjList hook_handlers;
|
|
||||||
BroDocObjMap functions;
|
|
||||||
BroDocObjList redefs;
|
|
||||||
|
|
||||||
BroDocObjList all;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes out the reST for either the script's public or private interface
|
|
||||||
* @param heading The title of the interfaces section heading
|
|
||||||
* @param underline The underline character to use for the interface
|
|
||||||
* section
|
|
||||||
* @param subunderline The underline character to use for interface
|
|
||||||
* sub-sections
|
|
||||||
* @param isPublic Whether to write out the public or private script
|
|
||||||
* interface
|
|
||||||
* @param isShort Whether to write out the full documentation or a "short"
|
|
||||||
* description (a single sentence)
|
|
||||||
*/
|
|
||||||
void WriteInterface(const char* heading, char underline, char subunderline,
|
|
||||||
bool isPublic, bool isShort) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Frees memory allocated to BroDocObj's objects in a given list.
|
|
||||||
* @param a reference to a list of BroDocObj pointers
|
|
||||||
*/
|
|
||||||
void FreeBroDocObjPtrList(BroDocObjList& l);
|
|
||||||
|
|
||||||
static bool IsPublicAPI(const BroDocObj* o)
|
|
||||||
{
|
|
||||||
return o->IsPublicAPI();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsPrivateAPI(const BroDocObj* o)
|
|
||||||
{
|
|
||||||
return ! o->IsPublicAPI();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct replace_slash {
|
|
||||||
void operator()(char& c)
|
|
||||||
{
|
|
||||||
if ( c == '/' ) c = '^';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes out plugin index documentation for all analyzer plugins.
|
|
||||||
* @param f an open file stream to write docs into.
|
|
||||||
*/
|
|
||||||
void CreateProtoAnalyzerDoc(FILE* f);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes out plugin index documentation for all file analyzer plugins.
|
|
||||||
* @param f an open file stream to write docs into.
|
|
||||||
*/
|
|
||||||
void CreateFileAnalyzerDoc(FILE* f);
|
|
||||||
|
|
||||||
#endif
|
|
195
src/BroDocObj.cc
195
src/BroDocObj.cc
|
@ -1,195 +0,0 @@
|
||||||
#include <cstdio>
|
|
||||||
#include <string>
|
|
||||||
#include <list>
|
|
||||||
#include "ID.h"
|
|
||||||
#include "BroDocObj.h"
|
|
||||||
|
|
||||||
map<string, BroDocObj*> doc_ids = map<string, BroDocObj*>();
|
|
||||||
|
|
||||||
BroDocObj* BroDocObj::last = 0;
|
|
||||||
|
|
||||||
BroDocObj::BroDocObj(const ID* id, std::list<std::string>*& reST,
|
|
||||||
bool is_fake)
|
|
||||||
{
|
|
||||||
last = this;
|
|
||||||
broID = id;
|
|
||||||
reST_doc_strings = reST;
|
|
||||||
reST = 0;
|
|
||||||
is_fake_id = is_fake;
|
|
||||||
use_role = 0;
|
|
||||||
FormulateShortDesc();
|
|
||||||
doc_ids[id->Name()] = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
BroDocObj::~BroDocObj()
|
|
||||||
{
|
|
||||||
if ( reST_doc_strings )
|
|
||||||
delete reST_doc_strings;
|
|
||||||
|
|
||||||
if ( is_fake_id )
|
|
||||||
delete broID;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDocObj::WriteReSTCompact(FILE* file, int max_col) const
|
|
||||||
{
|
|
||||||
ODesc desc;
|
|
||||||
desc.SetQuotes(1);
|
|
||||||
broID->DescribeReSTShort(&desc);
|
|
||||||
|
|
||||||
fprintf(file, "%s", desc.Description());
|
|
||||||
|
|
||||||
std::list<std::string>::const_iterator it;
|
|
||||||
|
|
||||||
for ( it = short_desc.begin(); it != short_desc.end(); ++it )
|
|
||||||
{
|
|
||||||
int start_col;
|
|
||||||
|
|
||||||
if ( it == short_desc.begin() )
|
|
||||||
start_col = max_col - desc.Len() + 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
start_col = max_col + 1;
|
|
||||||
fprintf(file, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( int i = 0; i < start_col; ++i )
|
|
||||||
fprintf(file, " ");
|
|
||||||
|
|
||||||
fprintf(file, "%s", it->c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int BroDocObj::LongestShortDescLen() const
|
|
||||||
{
|
|
||||||
size_t max = 0;
|
|
||||||
|
|
||||||
std::list<std::string>::const_iterator it;
|
|
||||||
|
|
||||||
for ( it = short_desc.begin(); it != short_desc.end(); ++it )
|
|
||||||
{
|
|
||||||
if ( it->size() > max )
|
|
||||||
max = it->size();
|
|
||||||
}
|
|
||||||
|
|
||||||
return max;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t end_of_first_sentence(string s)
|
|
||||||
{
|
|
||||||
size_t rval = 0;
|
|
||||||
|
|
||||||
while ( (rval = s.find_first_of('.', rval)) != string::npos )
|
|
||||||
{
|
|
||||||
if ( rval == s.size() - 1 )
|
|
||||||
// Period is at end of string.
|
|
||||||
return rval;
|
|
||||||
|
|
||||||
if ( isspace(s[rval + 1]) )
|
|
||||||
// Period has a space after it.
|
|
||||||
return rval;
|
|
||||||
|
|
||||||
// Period has some non-space character after it, keep looking.
|
|
||||||
++rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDocObj::FormulateShortDesc()
|
|
||||||
{
|
|
||||||
if ( ! reST_doc_strings )
|
|
||||||
return;
|
|
||||||
|
|
||||||
short_desc.clear();
|
|
||||||
std::list<std::string>::const_iterator it;
|
|
||||||
|
|
||||||
for ( it = reST_doc_strings->begin();
|
|
||||||
it != reST_doc_strings->end(); ++it )
|
|
||||||
{
|
|
||||||
// The short description stops at the first sentence or the
|
|
||||||
// first empty comment.
|
|
||||||
size_t end = end_of_first_sentence(*it);
|
|
||||||
|
|
||||||
if ( end == string::npos )
|
|
||||||
{
|
|
||||||
std::string::const_iterator s;
|
|
||||||
bool empty = true;
|
|
||||||
|
|
||||||
for ( s = it->begin(); s != it->end(); ++s )
|
|
||||||
{
|
|
||||||
if ( *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r' )
|
|
||||||
{
|
|
||||||
empty = false;
|
|
||||||
short_desc.push_back(*it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( empty )
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
short_desc.push_back(it->substr(0, end + 1));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDocObj::WriteReST(FILE* file) const
|
|
||||||
{
|
|
||||||
int indent_spaces = 3;
|
|
||||||
ODesc desc;
|
|
||||||
desc.SetIndentSpaces(indent_spaces);
|
|
||||||
desc.SetQuotes(1);
|
|
||||||
|
|
||||||
broID->DescribeReST(&desc, use_role);
|
|
||||||
|
|
||||||
fprintf(file, "%s", desc.Description());
|
|
||||||
|
|
||||||
if ( HasDocumentation() )
|
|
||||||
{
|
|
||||||
fprintf(file, "\n");
|
|
||||||
std::list<std::string>::const_iterator it;
|
|
||||||
|
|
||||||
for ( it = reST_doc_strings->begin();
|
|
||||||
it != reST_doc_strings->end(); ++it)
|
|
||||||
{
|
|
||||||
for ( int i = 0; i < indent_spaces; ++i )
|
|
||||||
fprintf(file, " ");
|
|
||||||
|
|
||||||
fprintf(file, "%s\n", it->c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(file, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int BroDocObj::ColumnSize() const
|
|
||||||
{
|
|
||||||
ODesc desc;
|
|
||||||
desc.SetQuotes(1);
|
|
||||||
broID->DescribeReSTShort(&desc);
|
|
||||||
return desc.Len();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BroDocObj::IsPublicAPI() const
|
|
||||||
{
|
|
||||||
return (broID->Scope() == SCOPE_GLOBAL) ||
|
|
||||||
(broID->Scope() == SCOPE_MODULE && broID->IsExport());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroDocObj::Combine(const BroDocObj* o)
|
|
||||||
{
|
|
||||||
if ( o->reST_doc_strings )
|
|
||||||
{
|
|
||||||
if ( ! reST_doc_strings )
|
|
||||||
reST_doc_strings = new std::list<std::string>();
|
|
||||||
|
|
||||||
reST_doc_strings->splice(reST_doc_strings->end(),
|
|
||||||
*(o->reST_doc_strings));
|
|
||||||
}
|
|
||||||
|
|
||||||
delete o;
|
|
||||||
FormulateShortDesc();
|
|
||||||
}
|
|
143
src/BroDocObj.h
143
src/BroDocObj.h
|
@ -1,143 +0,0 @@
|
||||||
#ifndef brodocobj_h
|
|
||||||
#define brodocobj_h
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <string>
|
|
||||||
#include <list>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "ID.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class wraps a Bro script identifier, providing methods relevant
|
|
||||||
* to automatic generation of reStructuredText (reST) documentation for it.
|
|
||||||
*/
|
|
||||||
class BroDocObj {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* BroDocObj constructor
|
|
||||||
* @param id a pointer to an identifier that is to be documented
|
|
||||||
* @param reST a reference to a pointer of a list of strings that
|
|
||||||
* represent the reST documentation for the ID. The pointer
|
|
||||||
* will be set to 0 after this constructor finishes.
|
|
||||||
* @param is_fake whether the ID* is a dummy just for doc purposes
|
|
||||||
*/
|
|
||||||
BroDocObj(const ID* id, std::list<std::string>*& reST,
|
|
||||||
bool is_fake = false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BroDocObj destructor
|
|
||||||
* Deallocates the memory associated with the list of reST strings
|
|
||||||
*/
|
|
||||||
~BroDocObj();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the reST representation of this object which includes
|
|
||||||
* 1) a reST friendly description of the ID
|
|
||||||
* 2) "##" or "##<" stylized comments.
|
|
||||||
* Anything after these style of comments is inserted as-is into
|
|
||||||
* the reST document.
|
|
||||||
* @param file The (already opened) file to write the reST to.
|
|
||||||
*/
|
|
||||||
void WriteReST(FILE* file) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a compact version of the ID and associated documentation
|
|
||||||
* for insertion into a table.
|
|
||||||
* @param file The (already opened) file to write the reST to.
|
|
||||||
* @param max_col The maximum length of the first table column
|
|
||||||
*/
|
|
||||||
void WriteReSTCompact(FILE* file, int max_col) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the column size required by the reST representation of the ID
|
|
||||||
*/
|
|
||||||
int ColumnSize() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether this documentation is part of the public API. In
|
|
||||||
* other words, this means that the identifier is declared as part of
|
|
||||||
* the global scope (has GLOBAL namespace or is exported from another
|
|
||||||
* namespace).
|
|
||||||
* @return true if the identifier is part of the script's public API
|
|
||||||
*/
|
|
||||||
bool IsPublicAPI() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether this object has documentation (## comments)
|
|
||||||
* @return true if the ID has comments associated with it
|
|
||||||
*/
|
|
||||||
bool HasDocumentation() const
|
|
||||||
{
|
|
||||||
return reST_doc_strings && reST_doc_strings->size() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return whether this object will use reST role (T) or directive (F)
|
|
||||||
* notation for the wrapped identifier. Roles are usually used
|
|
||||||
* for cross-referencing.
|
|
||||||
*/
|
|
||||||
bool UseRole() const { return use_role; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param b whether this object will use reST role (T) or directive (F)
|
|
||||||
* notation for the wrapped identifier. Roles are usually used
|
|
||||||
* for cross-referencing.
|
|
||||||
*/
|
|
||||||
void SetRole(bool b) { use_role = b; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append any reST documentation strings in a given BroDocObj to this
|
|
||||||
* object's list and then delete the given BroDocObj
|
|
||||||
* @param o a pointer to a BroDocObj to subsume
|
|
||||||
*/
|
|
||||||
void Combine(const BroDocObj* o);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the name of the wrapped identifier
|
|
||||||
*/
|
|
||||||
const char* Name() const { return broID->Name(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the longest string element of the short description's list of
|
|
||||||
* strings
|
|
||||||
*/
|
|
||||||
int LongestShortDescLen() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a reST documentation string to this BroDocObj's list.
|
|
||||||
* @param s the documentation string to append.
|
|
||||||
*/
|
|
||||||
void AddDocString(const std::string& s)
|
|
||||||
{
|
|
||||||
if ( ! reST_doc_strings )
|
|
||||||
reST_doc_strings = new std::list<std::string>();
|
|
||||||
reST_doc_strings->push_back(s);
|
|
||||||
FormulateShortDesc();
|
|
||||||
}
|
|
||||||
|
|
||||||
static BroDocObj* last;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::list<std::string>* reST_doc_strings;
|
|
||||||
std::list<std::string> short_desc;
|
|
||||||
const ID* broID;
|
|
||||||
bool is_fake_id; /**< Whether the ID* is a dummy just for doc purposes */
|
|
||||||
bool use_role; /**< Whether to use a reST role or directive for the ID */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the short_desc member to be a subset of reST_doc_strings.
|
|
||||||
* Specifically, short_desc will be everything in reST_doc_strings
|
|
||||||
* up until the first period or first empty string list element found.
|
|
||||||
*/
|
|
||||||
void FormulateShortDesc();
|
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map identifiers to their broxygen documentation objects.
|
|
||||||
*/
|
|
||||||
extern map<string, BroDocObj*> doc_ids;
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -250,8 +250,6 @@ set(bro_SRCS
|
||||||
Attr.cc
|
Attr.cc
|
||||||
Base64.cc
|
Base64.cc
|
||||||
BPF_Program.cc
|
BPF_Program.cc
|
||||||
BroDoc.cc
|
|
||||||
BroDocObj.cc
|
|
||||||
Brofiler.cc
|
Brofiler.cc
|
||||||
BroString.cc
|
BroString.cc
|
||||||
CCL.cc
|
CCL.cc
|
||||||
|
|
10
src/Type.cc
10
src/Type.cc
|
@ -1146,8 +1146,8 @@ void RecordType::DescribeFieldsReST(ODesc* d, bool func_args) const
|
||||||
if ( func_args )
|
if ( func_args )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
using broxygen::IdentifierDocument;
|
using broxygen::IdentifierInfo;
|
||||||
IdentifierDocument* doc = broxygen_mgr->GetIdentifierDoc(GetName());
|
IdentifierInfo* doc = broxygen_mgr->GetIdentifierInfo(GetName());
|
||||||
|
|
||||||
if ( ! doc )
|
if ( ! doc )
|
||||||
{
|
{
|
||||||
|
@ -1498,8 +1498,8 @@ void EnumType::DescribeReST(ODesc* d, bool roles_only) const
|
||||||
else
|
else
|
||||||
d->Add(fmt(".. bro:enum:: %s %s", it->second, GetName().c_str()));
|
d->Add(fmt(".. bro:enum:: %s %s", it->second, GetName().c_str()));
|
||||||
|
|
||||||
using broxygen::IdentifierDocument;
|
using broxygen::IdentifierInfo;
|
||||||
IdentifierDocument* doc = broxygen_mgr->GetIdentifierDoc(it->second);
|
IdentifierInfo* doc = broxygen_mgr->GetIdentifierInfo(it->second);
|
||||||
|
|
||||||
if ( ! doc )
|
if ( ! doc )
|
||||||
{
|
{
|
||||||
|
@ -1514,7 +1514,7 @@ void EnumType::DescribeReST(ODesc* d, bool roles_only) const
|
||||||
if ( doc->GetDeclaringScript() )
|
if ( doc->GetDeclaringScript() )
|
||||||
enum_from_script = doc->GetDeclaringScript()->Name();
|
enum_from_script = doc->GetDeclaringScript()->Name();
|
||||||
|
|
||||||
IdentifierDocument* type_doc = broxygen_mgr->GetIdentifierDoc(GetName());
|
IdentifierInfo* type_doc = broxygen_mgr->GetIdentifierInfo(GetName());
|
||||||
|
|
||||||
if ( type_doc && type_doc->GetDeclaringScript() )
|
if ( type_doc && type_doc->GetDeclaringScript() )
|
||||||
type_from_script = type_doc->GetDeclaringScript()->Name();
|
type_from_script = type_doc->GetDeclaringScript()->Name();
|
||||||
|
|
|
@ -6,9 +6,14 @@ include_directories(BEFORE
|
||||||
)
|
)
|
||||||
|
|
||||||
set(broxygen_SRCS
|
set(broxygen_SRCS
|
||||||
Configuration.cc
|
|
||||||
Document.cc
|
|
||||||
Manager.cc
|
Manager.cc
|
||||||
|
Info.h
|
||||||
|
PackageInfo.cc
|
||||||
|
ScriptInfo.cc
|
||||||
|
IdentifierInfo.cc
|
||||||
|
Target.cc
|
||||||
|
Configuration.cc
|
||||||
|
ReStructuredTextTable.cc
|
||||||
utils.cc
|
utils.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#include "Configuration.h"
|
#include "Configuration.h"
|
||||||
#include "Manager.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "Reporter.h"
|
#include "Reporter.h"
|
||||||
|
@ -7,417 +7,26 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <map>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fts.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
using namespace broxygen;
|
using namespace broxygen;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
typedef map<string, Target::factory_fn> target_factory_map;
|
static TargetFactory create_target_factory()
|
||||||
|
|
||||||
static target_factory_map create_target_factory_map()
|
|
||||||
{
|
{
|
||||||
target_factory_map rval;
|
TargetFactory rval;
|
||||||
rval["package_index"] = &PackageIndexTarget::Instantiate;
|
rval.Register<PackageIndexTarget>("package_index");
|
||||||
rval["package"] = &PackageTarget::Instantiate;
|
rval.Register<PackageTarget>("package");
|
||||||
rval["proto_analyzer"] = &ProtoAnalyzerTarget::Instantiate;
|
rval.Register<ProtoAnalyzerTarget>("proto_analyzer");
|
||||||
rval["file_analyzer"] = &FileAnalyzerTarget::Instantiate;
|
rval.Register<FileAnalyzerTarget>("file_analyzer");
|
||||||
rval["script_summary"] = &ScriptSummaryTarget::Instantiate;
|
rval.Register<ScriptSummaryTarget>("script_summary");
|
||||||
rval["script_index"] = &ScriptIndexTarget::Instantiate;
|
rval.Register<ScriptIndexTarget>("script_index");
|
||||||
rval["script"] = &ScriptTarget::Instantiate;
|
rval.Register<ScriptTarget>("script");
|
||||||
rval["identifier"] = &IdentifierTarget::Instantiate;
|
rval.Register<IdentifierTarget>("identifier");
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static target_factory_map target_instantiators = create_target_factory_map();
|
|
||||||
|
|
||||||
struct TargetFile {
|
|
||||||
TargetFile(const string& arg_name)
|
|
||||||
: name(arg_name), f()
|
|
||||||
{
|
|
||||||
if ( name.find('/') != string::npos )
|
|
||||||
{
|
|
||||||
string dir = SafeDirname(name).result;
|
|
||||||
|
|
||||||
if ( ! ensure_intermediate_dirs(dir.c_str()) )
|
|
||||||
reporter->FatalError("Broxygen failed to make dir %s",
|
|
||||||
dir.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
f = fopen(name.c_str(), "w");
|
|
||||||
|
|
||||||
if ( ! f )
|
|
||||||
reporter->FatalError("Broxygen failed to open '%s' for writing: %s",
|
|
||||||
name.c_str(), strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
~TargetFile()
|
|
||||||
{
|
|
||||||
if ( f )
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
DBG_LOG(DBG_BROXYGEN, "Wrote out-of-date target '%s'", name.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
string name;
|
|
||||||
FILE* f;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
static vector<T*> filter_matching_docs(const vector<Document*>& from, Target* t)
|
|
||||||
{
|
|
||||||
vector<T*> rval;
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < from.size(); ++i )
|
|
||||||
{
|
|
||||||
T* d = dynamic_cast<T*>(from[i]);
|
|
||||||
|
|
||||||
if ( ! d )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ( t->MatchesPattern(d) )
|
|
||||||
{
|
|
||||||
DBG_LOG(DBG_BROXYGEN, "Doc '%s' matched pattern for target '%s'",
|
|
||||||
d->Name().c_str(), t->Name().c_str());
|
|
||||||
rval.push_back(d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
Target::Target(const string& arg_name, const string& arg_pattern)
|
|
||||||
: name(arg_name), pattern(arg_pattern), prefix()
|
|
||||||
{
|
|
||||||
size_t pos = pattern.find('*');
|
|
||||||
|
|
||||||
if ( pos == 0 || pos == string::npos )
|
|
||||||
return;
|
|
||||||
|
|
||||||
prefix = pattern.substr(0, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Target::MatchesPattern(Document* doc) const
|
|
||||||
{
|
|
||||||
if ( pattern == "*" )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if ( prefix.empty() )
|
|
||||||
return doc->Name() == pattern;
|
|
||||||
|
|
||||||
return ! strncmp(doc->Name().c_str(), prefix.c_str(), prefix.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnalyzerTarget::DoFindDependencies(const std::vector<Document *>& docs)
|
|
||||||
{
|
|
||||||
// TODO: really should add to dependency list the tag type's ID and
|
|
||||||
// all bif items for matching analyzer plugins, but that's all dependent
|
|
||||||
// on the bro binary itself, so I'm cheating.
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnalyzerTarget::DoGenerate() const
|
|
||||||
{
|
|
||||||
if ( broxygen_mgr->IsUpToDate(Name(), vector<Document*>()) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( Pattern() != "*" )
|
|
||||||
reporter->InternalWarning("Broxygen only implements analyzer target"
|
|
||||||
" pattern '*'");
|
|
||||||
|
|
||||||
TargetFile file(Name());
|
|
||||||
doc_creator_callback(file.f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PackageTarget::DoFindDependencies(const vector<Document*>& docs)
|
|
||||||
{
|
|
||||||
pkg_deps = filter_matching_docs<PackageDocument>(docs, this);
|
|
||||||
|
|
||||||
if ( pkg_deps.empty() )
|
|
||||||
reporter->FatalError("No match for Broxygen target '%s' pattern '%s'",
|
|
||||||
Name().c_str(), Pattern().c_str());
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < docs.size(); ++i )
|
|
||||||
{
|
|
||||||
ScriptDocument* script = dynamic_cast<ScriptDocument*>(docs[i]);
|
|
||||||
|
|
||||||
if ( ! script )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for ( size_t j = 0; j < pkg_deps.size(); ++j )
|
|
||||||
{
|
|
||||||
if ( strncmp(script->Name().c_str(), pkg_deps[j]->Name().c_str(),
|
|
||||||
pkg_deps[j]->Name().size()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
DBG_LOG(DBG_BROXYGEN, "Script %s associated with package %s",
|
|
||||||
script->Name().c_str(), pkg_deps[j]->Name().c_str());
|
|
||||||
pkg_manifest[pkg_deps[j]].push_back(script);
|
|
||||||
script_deps.push_back(script);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PackageTarget::DoGenerate() const
|
|
||||||
{
|
|
||||||
if ( broxygen_mgr->IsUpToDate(Name(), script_deps) &&
|
|
||||||
broxygen_mgr->IsUpToDate(Name(), pkg_deps) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
TargetFile file(Name());
|
|
||||||
|
|
||||||
fprintf(file.f, ":orphan:\n\n");
|
|
||||||
|
|
||||||
for ( manifest_t::const_iterator it = pkg_manifest.begin();
|
|
||||||
it != pkg_manifest.end(); ++it )
|
|
||||||
{
|
|
||||||
string header = fmt("Package: %s", it->first->Name().c_str());
|
|
||||||
header += "\n" + string(header.size(), '=');
|
|
||||||
|
|
||||||
fprintf(file.f, "%s\n\n", header.c_str());
|
|
||||||
|
|
||||||
vector<string> readme = it->first->GetReadme();
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < readme.size(); ++i )
|
|
||||||
fprintf(file.f, "%s\n", readme[i].c_str());
|
|
||||||
|
|
||||||
fprintf(file.f, "\n");
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < it->second.size(); ++i )
|
|
||||||
{
|
|
||||||
fprintf(file.f, ":doc:`/scripts/%s`\n",
|
|
||||||
it->second[i]->Name().c_str());
|
|
||||||
|
|
||||||
vector<string> cmnts = it->second[i]->GetComments();
|
|
||||||
|
|
||||||
for ( size_t j = 0; j < cmnts.size(); ++j )
|
|
||||||
fprintf(file.f, " %s\n", cmnts[j].c_str());
|
|
||||||
|
|
||||||
fprintf(file.f, "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PackageIndexTarget::DoFindDependencies(const vector<Document*>& docs)
|
|
||||||
{
|
|
||||||
pkg_deps = filter_matching_docs<PackageDocument>(docs, this);
|
|
||||||
|
|
||||||
if ( pkg_deps.empty() )
|
|
||||||
reporter->FatalError("No match for Broxygen target '%s' pattern '%s'",
|
|
||||||
Name().c_str(), Pattern().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void PackageIndexTarget::DoGenerate() const
|
|
||||||
{
|
|
||||||
if ( broxygen_mgr->IsUpToDate(Name(), pkg_deps) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
TargetFile file(Name());
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < pkg_deps.size(); ++i )
|
|
||||||
fprintf(file.f, "%s\n", pkg_deps[i]->ReStructuredText().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScriptTarget::DoFindDependencies(const vector<Document*>& docs)
|
|
||||||
{
|
|
||||||
script_deps = filter_matching_docs<ScriptDocument>(docs, this);
|
|
||||||
|
|
||||||
if ( script_deps.empty() )
|
|
||||||
reporter->FatalError("No match for Broxygen target '%s' pattern '%s'",
|
|
||||||
Name().c_str(), Pattern().c_str());
|
|
||||||
|
|
||||||
if ( ! IsDir() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < script_deps.size(); ++i )
|
|
||||||
{
|
|
||||||
if ( SafeBasename(script_deps[i]->Name()).result == PACKAGE_LOADER )
|
|
||||||
{
|
|
||||||
string pkg_dir = SafeDirname(script_deps[i]->Name()).result;
|
|
||||||
string target_file = Name() + pkg_dir + "/index.rst";
|
|
||||||
Target* t = PackageTarget::Instantiate(target_file, pkg_dir);
|
|
||||||
t->FindDependencies(docs);
|
|
||||||
pkg_deps.push_back(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<string> dir_contents_recursive(string dir)
|
|
||||||
{
|
|
||||||
vector<string> rval;
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if ( stat(dir.c_str(), &st) < 0 && errno == ENOENT )
|
|
||||||
return rval;
|
|
||||||
|
|
||||||
while ( dir[dir.size() - 1] == '/' )
|
|
||||||
dir.erase(dir.size() - 1, 1);
|
|
||||||
|
|
||||||
char* dir_copy = copy_string(dir.c_str());
|
|
||||||
char** scan_path = new char*[2];
|
|
||||||
scan_path[0] = dir_copy;
|
|
||||||
scan_path[1] = 0;
|
|
||||||
|
|
||||||
FTS* fts = fts_open(scan_path, FTS_NOCHDIR, 0);
|
|
||||||
|
|
||||||
if ( ! fts )
|
|
||||||
{
|
|
||||||
reporter->Error("fts_open failure: %s", strerror(errno));
|
|
||||||
delete [] scan_path;
|
|
||||||
delete [] dir_copy;
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
FTSENT* n;
|
|
||||||
|
|
||||||
while ( (n = fts_read(fts)) )
|
|
||||||
{
|
|
||||||
if ( n->fts_info & FTS_F )
|
|
||||||
rval.push_back(n->fts_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( errno )
|
|
||||||
reporter->Error("fts_read failure: %s", strerror(errno));
|
|
||||||
|
|
||||||
if ( fts_close(fts) < 0 )
|
|
||||||
reporter->Error("fts_close failure: %s", strerror(errno));
|
|
||||||
|
|
||||||
delete [] scan_path;
|
|
||||||
delete [] dir_copy;
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScriptTarget::DoGenerate() const
|
|
||||||
{
|
|
||||||
if ( IsDir() )
|
|
||||||
{
|
|
||||||
// Target name is a dir, matching scripts are written within that dir
|
|
||||||
// with a dir tree that parallels the script's BROPATH location.
|
|
||||||
|
|
||||||
set<string> targets;
|
|
||||||
vector<string> dir_contents = dir_contents_recursive(Name());
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < script_deps.size(); ++i )
|
|
||||||
{
|
|
||||||
string target_filename = Name() + script_deps[i]->Name() + ".rst";
|
|
||||||
targets.insert(target_filename);
|
|
||||||
vector<ScriptDocument*> dep;
|
|
||||||
dep.push_back(script_deps[i]);
|
|
||||||
|
|
||||||
if ( broxygen_mgr->IsUpToDate(target_filename, dep) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
TargetFile file(target_filename);
|
|
||||||
|
|
||||||
fprintf(file.f, "%s\n", script_deps[i]->ReStructuredText().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < pkg_deps.size(); ++i )
|
|
||||||
{
|
|
||||||
targets.insert(pkg_deps[i]->Name());
|
|
||||||
pkg_deps[i]->Generate();
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < dir_contents.size(); ++i )
|
|
||||||
{
|
|
||||||
string f = dir_contents[i];
|
|
||||||
|
|
||||||
if ( targets.find(f) != targets.end() )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ( unlink(f.c_str()) < 0 )
|
|
||||||
reporter->Warning("Failed to unlink %s: %s", f.c_str(),
|
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
DBG_LOG(DBG_BROXYGEN, "Delete stale script file %s", f.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Target is a single file, all matching scripts get written there.
|
|
||||||
|
|
||||||
if ( broxygen_mgr->IsUpToDate(Name(), script_deps) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
TargetFile file(Name());
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < script_deps.size(); ++i )
|
|
||||||
fprintf(file.f, "%s\n", script_deps[i]->ReStructuredText().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScriptSummaryTarget::DoGenerate() const
|
|
||||||
{
|
|
||||||
if ( broxygen_mgr->IsUpToDate(Name(), script_deps) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
TargetFile file(Name());
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < script_deps.size(); ++i )
|
|
||||||
{
|
|
||||||
ScriptDocument* d = dynamic_cast<ScriptDocument*>(script_deps[i]);
|
|
||||||
|
|
||||||
if ( ! d )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
fprintf(file.f, ":doc:`/scripts/%s`\n", d->Name().c_str());
|
|
||||||
|
|
||||||
vector<string> cmnts = d->GetComments();
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < cmnts.size(); ++i )
|
|
||||||
fprintf(file.f, " %s\n", cmnts[i].c_str());
|
|
||||||
|
|
||||||
fprintf(file.f, "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScriptIndexTarget::DoGenerate() const
|
|
||||||
{
|
|
||||||
if ( broxygen_mgr->IsUpToDate(Name(), script_deps) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
TargetFile file(Name());
|
|
||||||
|
|
||||||
fprintf(file.f, ".. toctree::\n");
|
|
||||||
fprintf(file.f, " :maxdepth: 1\n\n");
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < script_deps.size(); ++i )
|
|
||||||
{
|
|
||||||
ScriptDocument* d = dynamic_cast<ScriptDocument*>(script_deps[i]);
|
|
||||||
|
|
||||||
if ( ! d )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
fprintf(file.f, " %s </scripts/%s>\n", d->Name().c_str(),
|
|
||||||
d->Name().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IdentifierTarget::DoFindDependencies(const vector<Document*>& docs)
|
|
||||||
{
|
|
||||||
id_deps = filter_matching_docs<IdentifierDocument>(docs, this);
|
|
||||||
|
|
||||||
if ( id_deps.empty() )
|
|
||||||
reporter->FatalError("No match for Broxygen target '%s' pattern '%s'",
|
|
||||||
Name().c_str(), Pattern().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void IdentifierTarget::DoGenerate() const
|
|
||||||
{
|
|
||||||
if ( broxygen_mgr->IsUpToDate(Name(), id_deps) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
TargetFile file(Name());
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < id_deps.size(); ++i )
|
|
||||||
fprintf(file.f, "%s\n\n", id_deps[i]->ReStructuredText().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
Config::Config(const string& arg_file, const string& delim)
|
Config::Config(const string& arg_file, const string& delim)
|
||||||
: file(arg_file), targets()
|
: file(arg_file), targets(), target_factory(create_target_factory())
|
||||||
{
|
{
|
||||||
if ( file.empty() )
|
if ( file.empty() )
|
||||||
return;
|
return;
|
||||||
|
@ -450,14 +59,13 @@ Config::Config(const string& arg_file, const string& delim)
|
||||||
reporter->FatalError("malformed Broxygen target in %s:%u: %s",
|
reporter->FatalError("malformed Broxygen target in %s:%u: %s",
|
||||||
file.c_str(), line_number, line.c_str());
|
file.c_str(), line_number, line.c_str());
|
||||||
|
|
||||||
target_factory_map::const_iterator it =
|
Target* target = target_factory.Create(tokens[0], tokens[2], tokens[1]);
|
||||||
target_instantiators.find(tokens[0]);
|
|
||||||
|
|
||||||
if ( it == target_instantiators.end() )
|
if ( ! target )
|
||||||
reporter->FatalError("unkown Broxygen target type: %s",
|
reporter->FatalError("unkown Broxygen target type: %s",
|
||||||
tokens[0].c_str());
|
tokens[0].c_str());
|
||||||
|
|
||||||
targets.push_back(it->second(tokens[2], tokens[1]));
|
targets.push_back(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( f.bad() )
|
if ( f.bad() )
|
||||||
|
@ -471,10 +79,10 @@ Config::~Config()
|
||||||
delete targets[i];
|
delete targets[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::FindDependencies(const vector<Document*>& docs)
|
void Config::FindDependencies(const vector<Info*>& infos)
|
||||||
{
|
{
|
||||||
for ( size_t i = 0; i < targets.size(); ++i )
|
for ( size_t i = 0; i < targets.size(); ++i )
|
||||||
targets[i]->FindDependencies(docs);
|
targets[i]->FindDependencies(infos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::GenerateDocs() const
|
void Config::GenerateDocs() const
|
||||||
|
@ -485,11 +93,8 @@ void Config::GenerateDocs() const
|
||||||
|
|
||||||
time_t Config::GetModificationTime() const
|
time_t Config::GetModificationTime() const
|
||||||
{
|
{
|
||||||
struct stat s;
|
if ( file.empty() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
if ( stat(file.c_str(), &s) < 0 )
|
return broxygen::get_mtime(file);
|
||||||
reporter->InternalError("Broxygen can't stat config file %s: %s",
|
|
||||||
file.c_str(), strerror(errno));
|
|
||||||
|
|
||||||
return s.st_mtime;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,246 +1,61 @@
|
||||||
#ifndef BROXYGEN_CONFIGURATION_H
|
#ifndef BROXYGEN_CONFIGURATION_H
|
||||||
#define BROXYGEN_CONFIGURATION_H
|
#define BROXYGEN_CONFIGURATION_H
|
||||||
|
|
||||||
#include "Document.h"
|
#include "Info.h"
|
||||||
#include "BroDoc.h"
|
#include "Target.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
|
||||||
|
|
||||||
namespace broxygen {
|
namespace broxygen {
|
||||||
|
|
||||||
// TODO: documentation...
|
/**
|
||||||
|
* Manages the generation of reStructuredText documents corresponding to
|
||||||
class Target {
|
* particular targets that are specified in a config file. The config file
|
||||||
public:
|
* is a simple list of one target per line, with the target format being
|
||||||
|
* a tab-delimited list of target-type, target-pattern, and target-output-file.
|
||||||
typedef Target* (*factory_fn)(const std::string&, const std::string&);
|
*/
|
||||||
|
|
||||||
virtual ~Target()
|
|
||||||
{ }
|
|
||||||
|
|
||||||
void FindDependencies(const std::vector<Document*>& docs)
|
|
||||||
{ DoFindDependencies(docs); }
|
|
||||||
|
|
||||||
void Generate() const
|
|
||||||
{ DoGenerate(); }
|
|
||||||
|
|
||||||
bool MatchesPattern(Document* doc) const;
|
|
||||||
|
|
||||||
std::string Name() const
|
|
||||||
{ return name; }
|
|
||||||
|
|
||||||
std::string Pattern() const
|
|
||||||
{ return pattern; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
Target(const std::string& arg_name, const std::string& arg_pattern);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
virtual void DoFindDependencies(const std::vector<Document*>& docs) = 0;
|
|
||||||
|
|
||||||
virtual void DoGenerate() const = 0;
|
|
||||||
|
|
||||||
std::string name;
|
|
||||||
std::string pattern;
|
|
||||||
std::string prefix;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AnalyzerTarget : public Target {
|
|
||||||
protected:
|
|
||||||
|
|
||||||
typedef void (*doc_creator_fn)(FILE*);
|
|
||||||
|
|
||||||
AnalyzerTarget(const std::string& name, const std::string& pattern,
|
|
||||||
doc_creator_fn cb)
|
|
||||||
: Target(name, pattern), doc_creator_callback(cb)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void DoFindDependencies(const std::vector<Document*>& docs);
|
|
||||||
|
|
||||||
void DoGenerate() const;
|
|
||||||
|
|
||||||
doc_creator_fn doc_creator_callback;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ProtoAnalyzerTarget : public AnalyzerTarget {
|
|
||||||
public:
|
|
||||||
|
|
||||||
static Target* Instantiate(const std::string& name,
|
|
||||||
const std::string& pattern)
|
|
||||||
{ return new ProtoAnalyzerTarget(name, pattern); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
ProtoAnalyzerTarget(const std::string& name, const std::string& pattern)
|
|
||||||
: AnalyzerTarget(name, pattern, &CreateProtoAnalyzerDoc)
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
class FileAnalyzerTarget : public AnalyzerTarget {
|
|
||||||
public:
|
|
||||||
|
|
||||||
static Target* Instantiate(const std::string& name,
|
|
||||||
const std::string& pattern)
|
|
||||||
{ return new FileAnalyzerTarget(name, pattern); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
FileAnalyzerTarget(const std::string& name, const std::string& pattern)
|
|
||||||
: AnalyzerTarget(name, pattern, &CreateFileAnalyzerDoc)
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
class PackageTarget : public Target {
|
|
||||||
public:
|
|
||||||
|
|
||||||
static Target* Instantiate(const std::string& name,
|
|
||||||
const std::string& pattern)
|
|
||||||
{ return new PackageTarget(name, pattern); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
PackageTarget(const std::string& name, const std::string& pattern)
|
|
||||||
: Target(name, pattern), pkg_deps(), script_deps(), pkg_manifest()
|
|
||||||
{ }
|
|
||||||
|
|
||||||
void DoFindDependencies(const std::vector<Document*>& docs);
|
|
||||||
|
|
||||||
void DoGenerate() const;
|
|
||||||
|
|
||||||
std::vector<PackageDocument*> pkg_deps;
|
|
||||||
std::vector<ScriptDocument*> script_deps;
|
|
||||||
typedef std::map<PackageDocument*,std::vector<ScriptDocument*> > manifest_t;
|
|
||||||
manifest_t pkg_manifest;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PackageIndexTarget : public Target {
|
|
||||||
public:
|
|
||||||
|
|
||||||
static Target* Instantiate(const std::string& name,
|
|
||||||
const std::string& pattern)
|
|
||||||
{ return new PackageIndexTarget(name, pattern); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
PackageIndexTarget(const std::string& name, const std::string& pattern)
|
|
||||||
: Target(name, pattern), pkg_deps()
|
|
||||||
{ }
|
|
||||||
|
|
||||||
void DoFindDependencies(const std::vector<Document*>& docs);
|
|
||||||
|
|
||||||
void DoGenerate() const;
|
|
||||||
|
|
||||||
std::vector<PackageDocument*> pkg_deps;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ScriptTarget : public Target {
|
|
||||||
public:
|
|
||||||
|
|
||||||
static Target* Instantiate(const std::string& name,
|
|
||||||
const std::string& pattern)
|
|
||||||
{ return new ScriptTarget(name, pattern); }
|
|
||||||
|
|
||||||
~ScriptTarget()
|
|
||||||
{ for ( size_t i = 0; i < pkg_deps.size(); ++i ) delete pkg_deps[i]; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
ScriptTarget(const std::string& name, const std::string& pattern)
|
|
||||||
: Target(name, pattern), script_deps()
|
|
||||||
{ }
|
|
||||||
|
|
||||||
std::vector<ScriptDocument*> script_deps;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void DoFindDependencies(const std::vector<Document*>& docs);
|
|
||||||
|
|
||||||
void DoGenerate() const;
|
|
||||||
|
|
||||||
bool IsDir() const
|
|
||||||
{ return Name()[Name().size() - 1] == '/'; }
|
|
||||||
|
|
||||||
std::vector<Target*> pkg_deps;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ScriptSummaryTarget : public ScriptTarget {
|
|
||||||
public:
|
|
||||||
|
|
||||||
static Target* Instantiate(const std::string& name,
|
|
||||||
const std::string& pattern)
|
|
||||||
{ return new ScriptSummaryTarget(name, pattern); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
ScriptSummaryTarget(const std::string& name, const std::string& pattern)
|
|
||||||
: ScriptTarget(name, pattern)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
void DoGenerate() const /* override */;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ScriptIndexTarget : public ScriptTarget {
|
|
||||||
public:
|
|
||||||
|
|
||||||
static Target* Instantiate(const std::string& name,
|
|
||||||
const std::string& pattern)
|
|
||||||
{ return new ScriptIndexTarget(name, pattern); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
ScriptIndexTarget(const std::string& name, const std::string& pattern)
|
|
||||||
: ScriptTarget(name, pattern)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
void DoGenerate() const /* override */;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IdentifierTarget : public Target {
|
|
||||||
public:
|
|
||||||
|
|
||||||
static Target* Instantiate(const std::string& name,
|
|
||||||
const std::string& pattern)
|
|
||||||
{ return new IdentifierTarget(name, pattern); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
IdentifierTarget(const std::string& name, const std::string& pattern)
|
|
||||||
: Target(name, pattern), id_deps()
|
|
||||||
{ }
|
|
||||||
|
|
||||||
void DoFindDependencies(const std::vector<Document*>& docs);
|
|
||||||
|
|
||||||
void DoGenerate() const;
|
|
||||||
|
|
||||||
std::vector<IdentifierDocument*> id_deps;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Config {
|
class Config {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a Broxygen configuration file, parsing all targets in it.
|
||||||
|
* @param file The file containing a list of Broxygen targets. If it's
|
||||||
|
* an empty string most methods are a no-op.
|
||||||
|
* @param delim The delimiter between target fields.
|
||||||
|
*/
|
||||||
Config(const std::string& file, const std::string& delim = "\t");
|
Config(const std::string& file, const std::string& delim = "\t");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor, cleans up targets created when parsing config file.
|
||||||
|
*/
|
||||||
~Config();
|
~Config();
|
||||||
|
|
||||||
void FindDependencies(const std::vector<Document*>& docs);
|
/**
|
||||||
|
* Resolves dependency information for each target.
|
||||||
|
* @param infos All known information objects for documentable things.
|
||||||
|
*/
|
||||||
|
void FindDependencies(const std::vector<Info*>& infos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build each Broxygen target (i.e. write out the reST documents to disk).
|
||||||
|
*/
|
||||||
void GenerateDocs() const;
|
void GenerateDocs() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The modification time of the config file, or 0 if config
|
||||||
|
* file was specified by an empty string.
|
||||||
|
*/
|
||||||
time_t GetModificationTime() const;
|
time_t GetModificationTime() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::string file;
|
std::string file;
|
||||||
std::vector<Target*> targets;
|
std::vector<Target*> targets;
|
||||||
|
TargetFactory target_factory;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace broxygen
|
} // namespace broxygen
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,660 +0,0 @@
|
||||||
#include "Document.h"
|
|
||||||
#include "Manager.h"
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
#include "util.h"
|
|
||||||
#include "Val.h"
|
|
||||||
#include "Desc.h"
|
|
||||||
#include "Reporter.h"
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
using namespace broxygen;
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static bool is_public_api(const ID* id)
|
|
||||||
{
|
|
||||||
return (id->Scope() == SCOPE_GLOBAL) ||
|
|
||||||
(id->Scope() == SCOPE_MODULE && id->IsExport());
|
|
||||||
}
|
|
||||||
|
|
||||||
static string make_heading(const string& heading, char underline)
|
|
||||||
{
|
|
||||||
return heading + "\n" + string(heading.size(), underline) + "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
PackageDocument::PackageDocument(const string& arg_name)
|
|
||||||
: Document(),
|
|
||||||
pkg_name(arg_name), readme()
|
|
||||||
{
|
|
||||||
string readme_file = find_file(pkg_name + "/README", bro_path());
|
|
||||||
|
|
||||||
if ( readme_file.empty() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
ifstream f(readme_file.c_str());
|
|
||||||
|
|
||||||
if ( ! f.is_open() )
|
|
||||||
reporter->InternalWarning("Broxygen failed to open '%s': %s",
|
|
||||||
readme_file.c_str(), strerror(errno));
|
|
||||||
|
|
||||||
string line;
|
|
||||||
|
|
||||||
while ( getline(f, line) )
|
|
||||||
readme.push_back(line);
|
|
||||||
|
|
||||||
if ( f.bad() )
|
|
||||||
reporter->InternalWarning("Broxygen error reading '%s': %s",
|
|
||||||
readme_file.c_str(), strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
string PackageDocument::DoReStructuredText(bool roles_only) const
|
|
||||||
{
|
|
||||||
string rval = fmt(":doc:`%s </scripts/%s/index>`\n\n", pkg_name.c_str(),
|
|
||||||
pkg_name.c_str());
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < readme.size(); ++i )
|
|
||||||
rval += " " + readme[i] + "\n";
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentifierDocument::IdentifierDocument(ID* arg_id, ScriptDocument* script)
|
|
||||||
: Document(),
|
|
||||||
comments(), id(arg_id), initial_val_desc(), redefs(), fields(),
|
|
||||||
last_field_seen(), declaring_script(script)
|
|
||||||
{
|
|
||||||
Ref(id);
|
|
||||||
|
|
||||||
if ( id->ID_Val() )
|
|
||||||
{
|
|
||||||
ODesc d;
|
|
||||||
id->ID_Val()->Describe(&d);
|
|
||||||
initial_val_desc = d.Description();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentifierDocument::~IdentifierDocument()
|
|
||||||
{
|
|
||||||
Unref(id);
|
|
||||||
|
|
||||||
for ( redef_list::const_iterator it = redefs.begin(); it != redefs.end();
|
|
||||||
++it )
|
|
||||||
delete *it;
|
|
||||||
|
|
||||||
for ( record_field_map::const_iterator it = fields.begin();
|
|
||||||
it != fields.end(); ++it )
|
|
||||||
delete it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IdentifierDocument::AddRedef(const string& script,
|
|
||||||
const vector<string>& comments)
|
|
||||||
{
|
|
||||||
Redefinition* redef = new Redefinition();
|
|
||||||
redef->from_script = script;
|
|
||||||
|
|
||||||
if ( id->ID_Val() )
|
|
||||||
{
|
|
||||||
ODesc d;
|
|
||||||
id->ID_Val()->Describe(&d);
|
|
||||||
redef->new_val_desc = d.Description();
|
|
||||||
}
|
|
||||||
|
|
||||||
redef->comments = comments;
|
|
||||||
redefs.push_back(redef);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IdentifierDocument::AddRecordField(const TypeDecl* field,
|
|
||||||
const string& script,
|
|
||||||
vector<string>& comments)
|
|
||||||
{
|
|
||||||
RecordField* rf = new RecordField();
|
|
||||||
rf->field = new TypeDecl(*field);
|
|
||||||
rf->from_script = script;
|
|
||||||
rf->comments = comments;
|
|
||||||
fields[rf->field->id] = rf;
|
|
||||||
last_field_seen = rf;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<string> IdentifierDocument::GetComments() const
|
|
||||||
{
|
|
||||||
return comments;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<string> IdentifierDocument::GetFieldComments(const string& field) const
|
|
||||||
{
|
|
||||||
record_field_map::const_iterator it = fields.find(field);
|
|
||||||
|
|
||||||
if ( it == fields.end() )
|
|
||||||
return vector<string>();
|
|
||||||
|
|
||||||
return it->second->comments;
|
|
||||||
}
|
|
||||||
|
|
||||||
list<IdentifierDocument::Redefinition>
|
|
||||||
IdentifierDocument::GetRedefs(const string& from_script) const
|
|
||||||
{
|
|
||||||
list<Redefinition> rval;
|
|
||||||
|
|
||||||
for ( redef_list::const_iterator it = redefs.begin(); it != redefs.end();
|
|
||||||
++it )
|
|
||||||
{
|
|
||||||
if ( from_script == (*it)->from_script )
|
|
||||||
rval.push_back(*(*it));
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
string IdentifierDocument::GetDeclaringScriptForField(const string& field) const
|
|
||||||
{
|
|
||||||
record_field_map::const_iterator it = fields.find(field);
|
|
||||||
|
|
||||||
if ( it == fields.end() )
|
|
||||||
return "";
|
|
||||||
|
|
||||||
return it->second->from_script;
|
|
||||||
}
|
|
||||||
|
|
||||||
string IdentifierDocument::DoReStructuredText(bool roles_only) const
|
|
||||||
{
|
|
||||||
ODesc d;
|
|
||||||
d.SetIndentSpaces(3);
|
|
||||||
d.SetQuotes(true);
|
|
||||||
id->DescribeReST(&d, roles_only);
|
|
||||||
|
|
||||||
if ( comments.empty() )
|
|
||||||
return d.Description();
|
|
||||||
|
|
||||||
d.ClearIndentLevel();
|
|
||||||
d.PushIndent();
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < comments.size(); ++i )
|
|
||||||
{
|
|
||||||
if ( i > 0 )
|
|
||||||
d.NL();
|
|
||||||
|
|
||||||
if ( IsFunc(id->Type()->Tag()) )
|
|
||||||
{
|
|
||||||
string s = comments[i];
|
|
||||||
|
|
||||||
if ( broxygen::prettify_params(s) )
|
|
||||||
d.NL();
|
|
||||||
|
|
||||||
d.Add(s.c_str());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
d.Add(comments[i].c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return d.Description();
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptDocument::ScriptDocument(const string& arg_name, const string& arg_path)
|
|
||||||
: Document(),
|
|
||||||
name(arg_name), path(arg_path),
|
|
||||||
is_pkg_loader(SafeBasename(name).result == PACKAGE_LOADER),
|
|
||||||
dependencies(), module_usages(), comments(), identifier_docs(),
|
|
||||||
options(), constants(), state_vars(), types(), events(), hooks(),
|
|
||||||
functions(), redefs()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScriptDocument::AddIdentifierDoc(IdentifierDocument* doc)
|
|
||||||
{
|
|
||||||
identifier_docs[doc->Name()] = doc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScriptDocument::DoInitPostScript()
|
|
||||||
{
|
|
||||||
for ( id_doc_map::const_iterator it = identifier_docs.begin();
|
|
||||||
it != identifier_docs.end(); ++it )
|
|
||||||
{
|
|
||||||
IdentifierDocument* doc = it->second;
|
|
||||||
ID* id = doc->GetID();
|
|
||||||
|
|
||||||
if ( ! is_public_api(id) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ( id->AsType() )
|
|
||||||
{
|
|
||||||
types.push_back(doc);
|
|
||||||
DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as a type",
|
|
||||||
id->Name(), name.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( IsFunc(id->Type()->Tag()) )
|
|
||||||
{
|
|
||||||
switch ( id->Type()->AsFuncType()->Flavor() ) {
|
|
||||||
case FUNC_FLAVOR_HOOK:
|
|
||||||
DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as a hook",
|
|
||||||
id->Name(), name.c_str());
|
|
||||||
hooks.push_back(doc);
|
|
||||||
break;
|
|
||||||
case FUNC_FLAVOR_EVENT:
|
|
||||||
DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as a event",
|
|
||||||
id->Name(), name.c_str());
|
|
||||||
events.push_back(doc);
|
|
||||||
break;
|
|
||||||
case FUNC_FLAVOR_FUNCTION:
|
|
||||||
DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as a function",
|
|
||||||
id->Name(), name.c_str());
|
|
||||||
functions.push_back(doc);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
reporter->InternalError("Invalid function flavor");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( id->IsConst() )
|
|
||||||
{
|
|
||||||
if ( id->FindAttr(ATTR_REDEF) )
|
|
||||||
{
|
|
||||||
DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as an option",
|
|
||||||
id->Name(), name.c_str());
|
|
||||||
options.push_back(doc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as a constant",
|
|
||||||
id->Name(), name.c_str());
|
|
||||||
constants.push_back(doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( id->Type()->Tag() == TYPE_ENUM )
|
|
||||||
// Enums are always referenced/documented from the type's
|
|
||||||
// documentation.
|
|
||||||
continue;
|
|
||||||
|
|
||||||
DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as a state variable",
|
|
||||||
id->Name(), name.c_str());
|
|
||||||
state_vars.push_back(doc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<string> ScriptDocument::GetComments() const
|
|
||||||
{
|
|
||||||
return comments;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t end_of_first_sentence(const string& s)
|
|
||||||
{
|
|
||||||
size_t rval = 0;
|
|
||||||
|
|
||||||
while ( (rval = s.find_first_of('.', rval)) != string::npos )
|
|
||||||
{
|
|
||||||
if ( rval == s.size() - 1 )
|
|
||||||
// Period is at end of string.
|
|
||||||
return rval;
|
|
||||||
|
|
||||||
if ( isspace(s[rval + 1]) )
|
|
||||||
// Period has a space after it.
|
|
||||||
return rval;
|
|
||||||
|
|
||||||
// Period has some non-space character after it, keep looking.
|
|
||||||
++rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_all_whitespace(const string& s)
|
|
||||||
{
|
|
||||||
for ( size_t i = 0; i < s.size(); ++i )
|
|
||||||
if ( ! isspace(s[i]) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static vector<string> summary_comment(const vector<string>& cmnts)
|
|
||||||
{
|
|
||||||
vector<string> rval;
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < cmnts.size(); ++i )
|
|
||||||
{
|
|
||||||
size_t end = end_of_first_sentence(cmnts[i]);
|
|
||||||
|
|
||||||
if ( end == string::npos )
|
|
||||||
{
|
|
||||||
if ( is_all_whitespace(cmnts[i]) )
|
|
||||||
break;
|
|
||||||
|
|
||||||
rval.push_back(cmnts[i]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rval.push_back(cmnts[i].substr(0, end + 1));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReStructuredTextTable {
|
|
||||||
public:
|
|
||||||
|
|
||||||
ReStructuredTextTable(size_t arg_num_cols)
|
|
||||||
: num_cols(arg_num_cols), rows(), longest_row_in_column()
|
|
||||||
{
|
|
||||||
for ( size_t i = 0; i < num_cols; ++i )
|
|
||||||
longest_row_in_column.push_back(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddRow(const vector<string>& new_row)
|
|
||||||
{
|
|
||||||
assert(new_row.size() == num_cols);
|
|
||||||
rows.push_back(new_row);
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < new_row.size(); ++i )
|
|
||||||
if ( new_row[i].size() > longest_row_in_column[i] )
|
|
||||||
longest_row_in_column[i] = new_row[i].size();
|
|
||||||
}
|
|
||||||
|
|
||||||
static string MakeBorder(const vector<size_t> col_sizes, char border)
|
|
||||||
{
|
|
||||||
string rval;
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < col_sizes.size(); ++i )
|
|
||||||
{
|
|
||||||
if ( i > 0 )
|
|
||||||
rval += " ";
|
|
||||||
|
|
||||||
rval += string(col_sizes[i], border);
|
|
||||||
}
|
|
||||||
|
|
||||||
rval += "\n";
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
string AsString(char border) const
|
|
||||||
{
|
|
||||||
string rval = MakeBorder(longest_row_in_column, border);
|
|
||||||
|
|
||||||
for ( size_t row = 0; row < rows.size(); ++row )
|
|
||||||
{
|
|
||||||
for ( size_t col = 0; col < num_cols; ++col )
|
|
||||||
{
|
|
||||||
if ( col > 0 )
|
|
||||||
{
|
|
||||||
size_t last = rows[row][col - 1].size();
|
|
||||||
size_t longest = longest_row_in_column[col - 1];
|
|
||||||
size_t whitespace = longest - last + 1;
|
|
||||||
rval += string(whitespace, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
rval += rows[row][col];
|
|
||||||
}
|
|
||||||
|
|
||||||
rval += "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
rval += MakeBorder(longest_row_in_column, border);
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
size_t num_cols;
|
|
||||||
vector<vector<string> > rows;
|
|
||||||
vector<size_t> longest_row_in_column;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void add_summary_rows(const ODesc& id_desc, const vector<string>& cmnts,
|
|
||||||
ReStructuredTextTable* table)
|
|
||||||
{
|
|
||||||
vector<string> row;
|
|
||||||
row.push_back(id_desc.Description());
|
|
||||||
|
|
||||||
if ( cmnts.empty() )
|
|
||||||
{
|
|
||||||
row.push_back("");
|
|
||||||
table->AddRow(row);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
row.push_back(cmnts[0]);
|
|
||||||
table->AddRow(row);
|
|
||||||
|
|
||||||
for ( size_t i = 1; i < cmnts.size(); ++i )
|
|
||||||
{
|
|
||||||
row.clear();
|
|
||||||
row.push_back("");
|
|
||||||
row.push_back(cmnts[i]);
|
|
||||||
table->AddRow(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static string make_summary(const string& heading, char underline, char border,
|
|
||||||
const list<IdentifierDocument*>& id_list)
|
|
||||||
{
|
|
||||||
if ( id_list.empty() )
|
|
||||||
return "";
|
|
||||||
|
|
||||||
ReStructuredTextTable table(2);
|
|
||||||
|
|
||||||
for ( list<IdentifierDocument*>::const_iterator it = id_list.begin();
|
|
||||||
it != id_list.end(); ++it )
|
|
||||||
{
|
|
||||||
ID* id = (*it)->GetID();
|
|
||||||
ODesc d;
|
|
||||||
d.SetQuotes(1);
|
|
||||||
id->DescribeReSTShort(&d);
|
|
||||||
add_summary_rows(d, summary_comment((*it)->GetComments()), &table);
|
|
||||||
}
|
|
||||||
|
|
||||||
return make_heading(heading, underline) + table.AsString(border) + "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
static string make_redef_summary(const string& heading, char underline,
|
|
||||||
char border, const string& from_script,
|
|
||||||
const id_doc_set& id_set)
|
|
||||||
{
|
|
||||||
if ( id_set.empty() )
|
|
||||||
return "";
|
|
||||||
|
|
||||||
ReStructuredTextTable table(2);
|
|
||||||
|
|
||||||
for ( id_doc_set::const_iterator it = id_set.begin(); it != id_set.end();
|
|
||||||
++it )
|
|
||||||
{
|
|
||||||
ID* id = (*it)->GetID();
|
|
||||||
ODesc d;
|
|
||||||
d.SetQuotes(1);
|
|
||||||
id->DescribeReSTShort(&d);
|
|
||||||
|
|
||||||
typedef list<IdentifierDocument::Redefinition> redef_list;
|
|
||||||
redef_list redefs = (*it)->GetRedefs(from_script);
|
|
||||||
|
|
||||||
for ( redef_list::const_iterator iit = redefs.begin();
|
|
||||||
iit != redefs.end(); ++iit )
|
|
||||||
add_summary_rows(d, summary_comment(iit->comments), &table);
|
|
||||||
}
|
|
||||||
|
|
||||||
return make_heading(heading, underline) + table.AsString(border) + "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
static string make_details(const string& heading, char underline,
|
|
||||||
const list<IdentifierDocument*>& id_list)
|
|
||||||
{
|
|
||||||
if ( id_list.empty() )
|
|
||||||
return "";
|
|
||||||
|
|
||||||
string rval = make_heading(heading, underline);
|
|
||||||
|
|
||||||
for ( list<IdentifierDocument*>::const_iterator it = id_list.begin();
|
|
||||||
it != id_list.end(); ++it )
|
|
||||||
{
|
|
||||||
rval += (*it)->ReStructuredText();
|
|
||||||
rval += "\n\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static string make_redef_details(const string& heading, char underline,
|
|
||||||
const id_doc_set& id_set)
|
|
||||||
{
|
|
||||||
if ( id_set.empty() )
|
|
||||||
return "";
|
|
||||||
|
|
||||||
string rval = make_heading(heading, underline);
|
|
||||||
|
|
||||||
for ( id_doc_set::const_iterator it = id_set.begin();
|
|
||||||
it != id_set.end(); ++it )
|
|
||||||
{
|
|
||||||
rval += (*it)->ReStructuredText(true);
|
|
||||||
rval += "\n\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
string ScriptDocument::DoReStructuredText(bool roles_only) const
|
|
||||||
{
|
|
||||||
string rval;
|
|
||||||
|
|
||||||
rval += ":tocdepth: 3\n\n";
|
|
||||||
rval += make_heading(name, '=');
|
|
||||||
|
|
||||||
for ( string_set::const_iterator it = module_usages.begin();
|
|
||||||
it != module_usages.end(); ++it )
|
|
||||||
rval += ".. bro:namespace:: " + *it + "\n";
|
|
||||||
|
|
||||||
rval += "\n";
|
|
||||||
|
|
||||||
for ( size_t i = 0; i < comments.size(); ++i )
|
|
||||||
rval += comments[i] + "\n";
|
|
||||||
|
|
||||||
rval += "\n";
|
|
||||||
|
|
||||||
if ( ! module_usages.empty() )
|
|
||||||
{
|
|
||||||
rval += module_usages.size() > 1 ? ":Namespaces: " : ":Namespace: ";
|
|
||||||
|
|
||||||
for ( string_set::const_iterator it = module_usages.begin();
|
|
||||||
it != module_usages.end(); ++it )
|
|
||||||
{
|
|
||||||
if ( it != module_usages.begin() )
|
|
||||||
rval += ", ";
|
|
||||||
|
|
||||||
rval += *it;
|
|
||||||
}
|
|
||||||
|
|
||||||
rval += "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! dependencies.empty() )
|
|
||||||
{
|
|
||||||
rval += ":Imports: ";
|
|
||||||
|
|
||||||
for ( string_set::const_iterator it = dependencies.begin();
|
|
||||||
it != dependencies.end(); ++it )
|
|
||||||
{
|
|
||||||
if ( it != dependencies.begin() )
|
|
||||||
rval += ", ";
|
|
||||||
|
|
||||||
string path = find_file(*it, bro_path(), "bro");
|
|
||||||
string doc = *it;
|
|
||||||
|
|
||||||
if ( ! path.empty() && is_dir(path.c_str()) )
|
|
||||||
// Reference the package.
|
|
||||||
doc += "/index";
|
|
||||||
|
|
||||||
rval += fmt(":doc:`%s </scripts/%s>`", it->c_str(), doc.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
rval += "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
rval += fmt(":Source File: :download:`/scripts/%s`\n", name.c_str());
|
|
||||||
rval += "\n";
|
|
||||||
rval += make_heading("Summary", '~');
|
|
||||||
rval += make_summary("Options", '#', '=', options);
|
|
||||||
rval += make_summary("Constants", '#', '=', constants);
|
|
||||||
rval += make_summary("State Variables", '#', '=', state_vars);
|
|
||||||
rval += make_summary("Types", '#', '=', types);
|
|
||||||
rval += make_redef_summary("Redefinitions", '#', '=', name, redefs);
|
|
||||||
rval += make_summary("Events", '#', '=', events);
|
|
||||||
rval += make_summary("Hooks", '#', '=', hooks);
|
|
||||||
rval += make_summary("Functions", '#', '=', functions);
|
|
||||||
rval += "\n";
|
|
||||||
rval += make_heading("Detailed Interface", '~');
|
|
||||||
rval += make_details("Options", '#', options);
|
|
||||||
rval += make_details("Constants", '#', constants);
|
|
||||||
rval += make_details("State Variables", '#', state_vars);
|
|
||||||
rval += make_details("Types", '#', types);
|
|
||||||
//rval += make_redef_details("Redefinitions", '#', redefs);
|
|
||||||
rval += make_details("Events", '#', events);
|
|
||||||
rval += make_details("Hooks", '#', hooks);
|
|
||||||
rval += make_details("Functions", '#', functions);
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static time_t get_mtime(const string& filename)
|
|
||||||
{
|
|
||||||
struct stat s;
|
|
||||||
|
|
||||||
if ( stat(filename.c_str(), &s) < 0 )
|
|
||||||
reporter->InternalError("Broxygen failed to stat file '%s': %s",
|
|
||||||
filename.c_str(), strerror(errno));
|
|
||||||
|
|
||||||
return s.st_mtime;
|
|
||||||
}
|
|
||||||
|
|
||||||
time_t IdentifierDocument::DoGetModificationTime() const
|
|
||||||
{
|
|
||||||
// Could probably get away with just checking the set of scripts that
|
|
||||||
// contributed to the ID declaration/redefinitions, but this is easier...
|
|
||||||
return declaring_script->GetModificationTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
time_t ScriptDocument::DoGetModificationTime() const
|
|
||||||
{
|
|
||||||
time_t most_recent = get_mtime(path);
|
|
||||||
|
|
||||||
for ( string_set::const_iterator it = dependencies.begin();
|
|
||||||
it != dependencies.end(); ++it )
|
|
||||||
{
|
|
||||||
Document* doc = broxygen_mgr->GetScriptDoc(*it);
|
|
||||||
|
|
||||||
if ( ! doc )
|
|
||||||
{
|
|
||||||
string pkg_name = *it + "/" + PACKAGE_LOADER;
|
|
||||||
doc = broxygen_mgr->GetScriptDoc(pkg_name);
|
|
||||||
|
|
||||||
if ( ! doc )
|
|
||||||
reporter->InternalWarning("Broxygen failed to get mtime of %s",
|
|
||||||
it->c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
time_t dep_mtime = doc->GetModificationTime();
|
|
||||||
|
|
||||||
if ( dep_mtime > most_recent )
|
|
||||||
most_recent = dep_mtime;
|
|
||||||
}
|
|
||||||
|
|
||||||
return most_recent;
|
|
||||||
}
|
|
||||||
|
|
||||||
time_t PackageDocument::DoGetModificationTime() const
|
|
||||||
{
|
|
||||||
string readme_file = find_file(pkg_name + "/README", bro_path());
|
|
||||||
|
|
||||||
if ( readme_file.empty() )
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return get_mtime(readme_file);
|
|
||||||
}
|
|
|
@ -1,215 +0,0 @@
|
||||||
#ifndef BROXYGEN_DOCUMENT_H
|
|
||||||
#define BROXYGEN_DOCUMENT_H
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
#include <string>
|
|
||||||
#include <list>
|
|
||||||
#include <vector>
|
|
||||||
#include <set>
|
|
||||||
#include <map>
|
|
||||||
#include <ctime>
|
|
||||||
|
|
||||||
#include "ID.h"
|
|
||||||
#include "Type.h"
|
|
||||||
|
|
||||||
namespace broxygen {
|
|
||||||
|
|
||||||
// TODO: documentation...
|
|
||||||
|
|
||||||
class Document {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Document()
|
|
||||||
{ }
|
|
||||||
|
|
||||||
virtual ~Document()
|
|
||||||
{ }
|
|
||||||
|
|
||||||
time_t GetModificationTime() const
|
|
||||||
{ return DoGetModificationTime(); }
|
|
||||||
|
|
||||||
std::string Name() const
|
|
||||||
{ return DoName(); }
|
|
||||||
|
|
||||||
std::string ReStructuredText(bool roles_only = false) const
|
|
||||||
{ return DoReStructuredText(roles_only); }
|
|
||||||
|
|
||||||
void InitPostScript()
|
|
||||||
{ return DoInitPostScript(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
virtual time_t DoGetModificationTime() const = 0;
|
|
||||||
virtual std::string DoName() const = 0;
|
|
||||||
virtual std::string DoReStructuredText(bool roles_only) const = 0;
|
|
||||||
virtual void DoInitPostScript()
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
class PackageDocument : public Document {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
PackageDocument(const std::string& name);
|
|
||||||
|
|
||||||
std::vector<std::string> GetReadme() const
|
|
||||||
{ return readme; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
time_t DoGetModificationTime() const;
|
|
||||||
|
|
||||||
std::string DoName() const
|
|
||||||
{ return pkg_name; }
|
|
||||||
|
|
||||||
std::string DoReStructuredText(bool roles_only) const;
|
|
||||||
|
|
||||||
std::string pkg_name;
|
|
||||||
std::vector<std::string> readme;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class ScriptDocument;
|
|
||||||
|
|
||||||
class IdentifierDocument : public Document {
|
|
||||||
public:
|
|
||||||
|
|
||||||
IdentifierDocument(ID* id, ScriptDocument* script);
|
|
||||||
|
|
||||||
~IdentifierDocument();
|
|
||||||
|
|
||||||
void AddComment(const std::string& comment)
|
|
||||||
{ last_field_seen ? last_field_seen->comments.push_back(comment)
|
|
||||||
: comments.push_back(comment); }
|
|
||||||
|
|
||||||
void AddComments(const std::vector<std::string>& cmtns)
|
|
||||||
{ comments.insert(comments.end(), cmtns.begin(), cmtns.end()); }
|
|
||||||
|
|
||||||
void AddRedef(const std::string& from_script,
|
|
||||||
const std::vector<std::string>& comments);
|
|
||||||
|
|
||||||
void AddRecordField(const TypeDecl* field, const std::string& script,
|
|
||||||
std::vector<std::string>& comments);
|
|
||||||
|
|
||||||
void CompletedTypeDecl()
|
|
||||||
{ last_field_seen = 0; }
|
|
||||||
|
|
||||||
ID* GetID() const
|
|
||||||
{ return id; }
|
|
||||||
|
|
||||||
ScriptDocument* GetDeclaringScript() const
|
|
||||||
{ return declaring_script; }
|
|
||||||
|
|
||||||
std::string GetDeclaringScriptForField(const std::string& field) const;
|
|
||||||
|
|
||||||
std::vector<std::string> GetComments() const;
|
|
||||||
|
|
||||||
std::vector<std::string> GetFieldComments(const std::string& field) const;
|
|
||||||
|
|
||||||
struct Redefinition {
|
|
||||||
std::string from_script;
|
|
||||||
std::string new_val_desc;
|
|
||||||
std::vector<std::string> comments;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::list<Redefinition> GetRedefs(const std::string& from_script) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
time_t DoGetModificationTime() const;
|
|
||||||
|
|
||||||
std::string DoName() const
|
|
||||||
{ return id->Name(); }
|
|
||||||
|
|
||||||
std::string DoReStructuredText(bool roles_only) const;
|
|
||||||
|
|
||||||
struct RecordField {
|
|
||||||
~RecordField()
|
|
||||||
{ delete field; }
|
|
||||||
|
|
||||||
TypeDecl* field;
|
|
||||||
std::string from_script;
|
|
||||||
std::vector<std::string> comments;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::list<Redefinition*> redef_list;
|
|
||||||
typedef std::map<std::string, RecordField*> record_field_map;
|
|
||||||
|
|
||||||
std::vector<std::string> comments;
|
|
||||||
ID* id;
|
|
||||||
std::string initial_val_desc;
|
|
||||||
redef_list redefs;
|
|
||||||
record_field_map fields;
|
|
||||||
RecordField* last_field_seen;
|
|
||||||
ScriptDocument* declaring_script;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct IdDocComp {
|
|
||||||
bool operator() (IdentifierDocument* lhs, IdentifierDocument* rhs) const
|
|
||||||
{ return lhs->Name() < rhs->Name(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::set<IdentifierDocument*, IdDocComp> id_doc_set;
|
|
||||||
|
|
||||||
class ScriptDocument : public Document {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
ScriptDocument(const std::string& name, const std::string& path);
|
|
||||||
|
|
||||||
void AddComment(const std::string& comment)
|
|
||||||
{ comments.push_back(comment); }
|
|
||||||
|
|
||||||
void AddDependency(const std::string& name)
|
|
||||||
{ dependencies.insert(name); }
|
|
||||||
|
|
||||||
void AddModule(const std::string& name)
|
|
||||||
{ module_usages.insert(name); }
|
|
||||||
|
|
||||||
void AddIdentifierDoc(IdentifierDocument* doc);
|
|
||||||
|
|
||||||
void AddRedef(IdentifierDocument* doc)
|
|
||||||
{ redefs.insert(doc); }
|
|
||||||
|
|
||||||
bool IsPkgLoader() const
|
|
||||||
{ return is_pkg_loader; }
|
|
||||||
|
|
||||||
std::vector<std::string> GetComments() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
typedef std::map<std::string, IdentifierDocument*> id_doc_map;
|
|
||||||
typedef std::list<IdentifierDocument*> id_doc_list;
|
|
||||||
typedef std::set<std::string> string_set;
|
|
||||||
|
|
||||||
time_t DoGetModificationTime() const;
|
|
||||||
|
|
||||||
std::string DoName() const
|
|
||||||
{ return name; }
|
|
||||||
|
|
||||||
std::string DoReStructuredText(bool roles_only) const;
|
|
||||||
|
|
||||||
void DoInitPostScript() /* override */;
|
|
||||||
|
|
||||||
std::string name;
|
|
||||||
std::string path;
|
|
||||||
bool is_pkg_loader;
|
|
||||||
string_set dependencies;
|
|
||||||
string_set module_usages;
|
|
||||||
std::vector<std::string> comments;
|
|
||||||
id_doc_map identifier_docs;
|
|
||||||
id_doc_list options;
|
|
||||||
id_doc_list constants;
|
|
||||||
id_doc_list state_vars;
|
|
||||||
id_doc_list types;
|
|
||||||
id_doc_list events;
|
|
||||||
id_doc_list hooks;
|
|
||||||
id_doc_list functions;
|
|
||||||
id_doc_set redefs;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace broxygen
|
|
||||||
|
|
||||||
#endif
|
|
146
src/broxygen/IdentifierInfo.cc
Normal file
146
src/broxygen/IdentifierInfo.cc
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
#include "IdentifierInfo.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include "Desc.h"
|
||||||
|
#include "Val.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace broxygen;
|
||||||
|
|
||||||
|
IdentifierInfo::IdentifierInfo(ID* arg_id, ScriptInfo* script)
|
||||||
|
: Info(),
|
||||||
|
comments(), id(arg_id), initial_val_desc(), redefs(), fields(),
|
||||||
|
last_field_seen(), declaring_script(script)
|
||||||
|
{
|
||||||
|
Ref(id);
|
||||||
|
|
||||||
|
if ( id->ID_Val() )
|
||||||
|
{
|
||||||
|
ODesc d;
|
||||||
|
id->ID_Val()->Describe(&d);
|
||||||
|
initial_val_desc = d.Description();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentifierInfo::~IdentifierInfo()
|
||||||
|
{
|
||||||
|
Unref(id);
|
||||||
|
|
||||||
|
for ( redef_list::const_iterator it = redefs.begin(); it != redefs.end();
|
||||||
|
++it )
|
||||||
|
delete *it;
|
||||||
|
|
||||||
|
for ( record_field_map::const_iterator it = fields.begin();
|
||||||
|
it != fields.end(); ++it )
|
||||||
|
delete it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IdentifierInfo::AddRedef(const string& script,
|
||||||
|
const vector<string>& comments)
|
||||||
|
{
|
||||||
|
Redefinition* redef = new Redefinition();
|
||||||
|
redef->from_script = script;
|
||||||
|
|
||||||
|
if ( id->ID_Val() )
|
||||||
|
{
|
||||||
|
ODesc d;
|
||||||
|
id->ID_Val()->Describe(&d);
|
||||||
|
redef->new_val_desc = d.Description();
|
||||||
|
}
|
||||||
|
|
||||||
|
redef->comments = comments;
|
||||||
|
redefs.push_back(redef);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IdentifierInfo::AddRecordField(const TypeDecl* field,
|
||||||
|
const string& script,
|
||||||
|
vector<string>& comments)
|
||||||
|
{
|
||||||
|
RecordField* rf = new RecordField();
|
||||||
|
rf->field = new TypeDecl(*field);
|
||||||
|
rf->from_script = script;
|
||||||
|
rf->comments = comments;
|
||||||
|
fields[rf->field->id] = rf;
|
||||||
|
last_field_seen = rf;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string> IdentifierInfo::GetComments() const
|
||||||
|
{
|
||||||
|
return comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string> IdentifierInfo::GetFieldComments(const string& field) const
|
||||||
|
{
|
||||||
|
record_field_map::const_iterator it = fields.find(field);
|
||||||
|
|
||||||
|
if ( it == fields.end() )
|
||||||
|
return vector<string>();
|
||||||
|
|
||||||
|
return it->second->comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
list<IdentifierInfo::Redefinition>
|
||||||
|
IdentifierInfo::GetRedefs(const string& from_script) const
|
||||||
|
{
|
||||||
|
list<Redefinition> rval;
|
||||||
|
|
||||||
|
for ( redef_list::const_iterator it = redefs.begin(); it != redefs.end();
|
||||||
|
++it )
|
||||||
|
{
|
||||||
|
if ( from_script == (*it)->from_script )
|
||||||
|
rval.push_back(*(*it));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
string IdentifierInfo::GetDeclaringScriptForField(const string& field) const
|
||||||
|
{
|
||||||
|
record_field_map::const_iterator it = fields.find(field);
|
||||||
|
|
||||||
|
if ( it == fields.end() )
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return it->second->from_script;
|
||||||
|
}
|
||||||
|
|
||||||
|
string IdentifierInfo::DoReStructuredText(bool roles_only) const
|
||||||
|
{
|
||||||
|
ODesc d;
|
||||||
|
d.SetIndentSpaces(3);
|
||||||
|
d.SetQuotes(true);
|
||||||
|
id->DescribeReST(&d, roles_only);
|
||||||
|
|
||||||
|
if ( comments.empty() )
|
||||||
|
return d.Description();
|
||||||
|
|
||||||
|
d.ClearIndentLevel();
|
||||||
|
d.PushIndent();
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < comments.size(); ++i )
|
||||||
|
{
|
||||||
|
if ( i > 0 )
|
||||||
|
d.NL();
|
||||||
|
|
||||||
|
if ( IsFunc(id->Type()->Tag()) )
|
||||||
|
{
|
||||||
|
string s = comments[i];
|
||||||
|
|
||||||
|
if ( broxygen::prettify_params(s) )
|
||||||
|
d.NL();
|
||||||
|
|
||||||
|
d.Add(s.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
d.Add(comments[i].c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.Description();
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t IdentifierInfo::DoGetModificationTime() const
|
||||||
|
{
|
||||||
|
// Could probably get away with just checking the set of scripts that
|
||||||
|
// contributed to the ID declaration/redefinitions, but this is easier...
|
||||||
|
return declaring_script->GetModificationTime();
|
||||||
|
}
|
162
src/broxygen/IdentifierInfo.h
Normal file
162
src/broxygen/IdentifierInfo.h
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
#ifndef BROXYGEN_IDENTIFIERINFO_H
|
||||||
|
#define BROXYGEN_IDENTIFIERINFO_H
|
||||||
|
|
||||||
|
#include "Info.h"
|
||||||
|
#include "ScriptInfo.h"
|
||||||
|
|
||||||
|
#include "ID.h"
|
||||||
|
#include "Type.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace broxygen {
|
||||||
|
|
||||||
|
class ScriptInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information regarding a script-level identifier and its documentation.
|
||||||
|
*/
|
||||||
|
class IdentifierInfo : public Info {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new identifier info object.
|
||||||
|
* @param id The script-level identifier.
|
||||||
|
* @param script The info object associated with the script in which \a id
|
||||||
|
* is declared.
|
||||||
|
*/
|
||||||
|
IdentifierInfo(ID* id, ScriptInfo* script);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dtor. Releases any references to script-level objects.
|
||||||
|
*/
|
||||||
|
~IdentifierInfo();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a comment associated with the identifier. If the identifier is a
|
||||||
|
* record type and it's in the middle of parsing fields, the comment is
|
||||||
|
* associated with the last field that was parsed.
|
||||||
|
* @param comment A string extracted from Broxygen-style comment.
|
||||||
|
*/
|
||||||
|
void AddComment(const std::string& comment)
|
||||||
|
{ last_field_seen ? last_field_seen->comments.push_back(comment)
|
||||||
|
: comments.push_back(comment); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate several comments with the identifier. They will be appended
|
||||||
|
* to the end of the list of any current comments.
|
||||||
|
* @param cmtns A vector of comments to associate.
|
||||||
|
*/
|
||||||
|
void AddComments(const std::vector<std::string>& cmtns)
|
||||||
|
{ comments.insert(comments.end(), cmtns.begin(), cmtns.end()); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a redefinition of the identifier.
|
||||||
|
* @param from_script The script in which the redef occurred.
|
||||||
|
* @param comments Comments associated with the redef statement.
|
||||||
|
*/
|
||||||
|
void AddRedef(const std::string& from_script,
|
||||||
|
const std::vector<std::string>& comments);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a record field associated with the identifier
|
||||||
|
* (which is implicitly a record type).
|
||||||
|
* @param field The name/type information of the field.
|
||||||
|
* @param script The script in which the field was declared. This may
|
||||||
|
* differ from the script in which a record type is declared due to redefs.
|
||||||
|
* @param comments Comments associated with the record field.
|
||||||
|
*/
|
||||||
|
void AddRecordField(const TypeDecl* field, const std::string& script,
|
||||||
|
std::vector<std::string>& comments);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals that a record type has been completely parsed. This resets
|
||||||
|
* internal tracking of the last record field seen so that "##<"-style
|
||||||
|
* comments are correctly associated.
|
||||||
|
*/
|
||||||
|
void CompletedTypeDecl()
|
||||||
|
{ last_field_seen = 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the script-level ID tracked by this info object.
|
||||||
|
*/
|
||||||
|
ID* GetID() const
|
||||||
|
{ return id; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The script which declared the script-level identifier.
|
||||||
|
*/
|
||||||
|
ScriptInfo* GetDeclaringScript() const
|
||||||
|
{ return declaring_script; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param field A record field name.
|
||||||
|
* @return The script which declared the record field name.
|
||||||
|
*/
|
||||||
|
std::string GetDeclaringScriptForField(const std::string& field) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return All Broxygen comments associated with the identifier.
|
||||||
|
*/
|
||||||
|
std::vector<std::string> GetComments() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param field A record field name.
|
||||||
|
* @return All Broxygen comments associated with the record field.
|
||||||
|
*/
|
||||||
|
std::vector<std::string> GetFieldComments(const std::string& field) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks useful information related to a redef.
|
||||||
|
*/
|
||||||
|
struct Redefinition {
|
||||||
|
std::string from_script; /**< Name of script doing the redef. */
|
||||||
|
std::string new_val_desc; /**< Description of new value bound to ID. */
|
||||||
|
std::vector<std::string> comments; /**< Broxygen comments on redef. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of information about redefinitions of the identifier within
|
||||||
|
* a particular script.
|
||||||
|
* @param from_script The name of a script in which to look for redefs.
|
||||||
|
* @return A list of redefs that occurred in \a from_script.
|
||||||
|
*/
|
||||||
|
std::list<Redefinition> GetRedefs(const std::string& from_script) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
time_t DoGetModificationTime() const;
|
||||||
|
|
||||||
|
std::string DoName() const
|
||||||
|
{ return id->Name(); }
|
||||||
|
|
||||||
|
std::string DoReStructuredText(bool roles_only) const;
|
||||||
|
|
||||||
|
struct RecordField {
|
||||||
|
~RecordField()
|
||||||
|
{ delete field; }
|
||||||
|
|
||||||
|
TypeDecl* field;
|
||||||
|
std::string from_script;
|
||||||
|
std::vector<std::string> comments;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::list<Redefinition*> redef_list;
|
||||||
|
typedef std::map<std::string, RecordField*> record_field_map;
|
||||||
|
|
||||||
|
std::vector<std::string> comments;
|
||||||
|
ID* id;
|
||||||
|
std::string initial_val_desc;
|
||||||
|
redef_list redefs;
|
||||||
|
record_field_map fields;
|
||||||
|
RecordField* last_field_seen;
|
||||||
|
ScriptInfo* declaring_script;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace broxygen
|
||||||
|
|
||||||
|
#endif
|
71
src/broxygen/Info.h
Normal file
71
src/broxygen/Info.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
#ifndef BROXYGEN_INFO_H
|
||||||
|
#define BROXYGEN_INFO_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
namespace broxygen {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for any thing that Broxygen can document.
|
||||||
|
*/
|
||||||
|
class Info {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
*/
|
||||||
|
Info()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dtor.
|
||||||
|
*/
|
||||||
|
virtual ~Info()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The time any information related to the object was last modified.
|
||||||
|
*/
|
||||||
|
time_t GetModificationTime() const
|
||||||
|
{ return DoGetModificationTime(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A unique name for the documentable object.
|
||||||
|
*/
|
||||||
|
std::string Name() const
|
||||||
|
{ return DoName(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a reST representation of the object and any associated documentation.
|
||||||
|
* @param roles_only True if the reST should only use cross-referencing role
|
||||||
|
* syntax to refer itself instead of using a directive (which declares this
|
||||||
|
* reST the authoritative "anchor" for cross-references).
|
||||||
|
* @return A reST representation of the object and associated documentation.
|
||||||
|
*/
|
||||||
|
std::string ReStructuredText(bool roles_only = false) const
|
||||||
|
{ return DoReStructuredText(roles_only); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform any remaining info gathering/initialization that can only be done
|
||||||
|
* after all script parsing is complete.
|
||||||
|
*/
|
||||||
|
void InitPostScript()
|
||||||
|
{ DoInitPostScript(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
virtual time_t DoGetModificationTime() const = 0;
|
||||||
|
|
||||||
|
virtual std::string DoName() const = 0;
|
||||||
|
|
||||||
|
virtual std::string DoReStructuredText(bool roles_only) const = 0;
|
||||||
|
|
||||||
|
virtual void DoInitPostScript()
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace broxygen
|
||||||
|
|
||||||
|
#endif
|
|
@ -27,7 +27,7 @@ static string RemoveLeadingSpace(const string& s)
|
||||||
|
|
||||||
Manager::Manager(const string& arg_config, const string& bro_command)
|
Manager::Manager(const string& arg_config, const string& bro_command)
|
||||||
: disabled(), comment_buffer(), comment_buffer_map(), packages(), scripts(),
|
: disabled(), comment_buffer(), comment_buffer_map(), packages(), scripts(),
|
||||||
identifiers(), all_docs(), last_identifier_seen(), incomplete_type(),
|
identifiers(), all_info(), last_identifier_seen(), incomplete_type(),
|
||||||
enum_mappings(), config(arg_config), bro_mtime()
|
enum_mappings(), config(arg_config), bro_mtime()
|
||||||
{
|
{
|
||||||
if ( getenv("BRO_DISABLE_BROXYGEN") )
|
if ( getenv("BRO_DISABLE_BROXYGEN") )
|
||||||
|
@ -45,8 +45,8 @@ Manager::Manager(const string& arg_config, const string& bro_command)
|
||||||
|
|
||||||
Manager::~Manager()
|
Manager::~Manager()
|
||||||
{
|
{
|
||||||
for ( size_t i = 0; i < all_docs.size(); ++i )
|
for ( size_t i = 0; i < all_info.size(); ++i )
|
||||||
delete all_docs[i];
|
delete all_info[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::InitPreScript()
|
void Manager::InitPreScript()
|
||||||
|
@ -60,10 +60,10 @@ void Manager::InitPostScript()
|
||||||
if ( disabled )
|
if ( disabled )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for ( size_t i = 0; i < all_docs.size(); ++i )
|
for ( size_t i = 0; i < all_info.size(); ++i )
|
||||||
all_docs[i]->InitPostScript();
|
all_info[i]->InitPostScript();
|
||||||
|
|
||||||
config.FindDependencies(all_docs);
|
config.FindDependencies(all_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::GenerateDocs() const
|
void Manager::GenerateDocs() const
|
||||||
|
@ -74,39 +74,39 @@ void Manager::GenerateDocs() const
|
||||||
config.GenerateDocs();
|
config.GenerateDocs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::File(const string& path)
|
void Manager::Script(const string& path)
|
||||||
{
|
{
|
||||||
if ( disabled )
|
if ( disabled )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
string name = without_bropath_component(path);
|
string name = without_bropath_component(path);
|
||||||
|
|
||||||
if ( scripts.GetDocument(name) )
|
if ( scripts.GetInfo(name) )
|
||||||
{
|
{
|
||||||
DbgAndWarn(fmt("Duplicate script documentation: %s", name.c_str()));
|
DbgAndWarn(fmt("Duplicate script documentation: %s", name.c_str()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptDocument* doc = new ScriptDocument(name, path);
|
ScriptInfo* info = new ScriptInfo(name, path);
|
||||||
scripts.map[name] = doc;
|
scripts.map[name] = info;
|
||||||
all_docs.push_back(doc);
|
all_info.push_back(info);
|
||||||
DBG_LOG(DBG_BROXYGEN, "Made ScriptDocument %s", name.c_str());
|
DBG_LOG(DBG_BROXYGEN, "Made ScriptInfo %s", name.c_str());
|
||||||
|
|
||||||
if ( ! doc->IsPkgLoader() )
|
if ( ! info->IsPkgLoader() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
name = SafeDirname(name).result;
|
name = SafeDirname(name).result;
|
||||||
|
|
||||||
if ( packages.GetDocument(name) )
|
if ( packages.GetInfo(name) )
|
||||||
{
|
{
|
||||||
DbgAndWarn(fmt("Duplicate package documentation: %s", name.c_str()));
|
DbgAndWarn(fmt("Duplicate package documentation: %s", name.c_str()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PackageDocument* pkgdoc = new PackageDocument(name);
|
PackageInfo* pkginfo = new PackageInfo(name);
|
||||||
packages.map[name] = pkgdoc;
|
packages.map[name] = pkginfo;
|
||||||
all_docs.push_back(pkgdoc);
|
all_info.push_back(pkginfo);
|
||||||
DBG_LOG(DBG_BROXYGEN, "Made PackageDocument %s", name.c_str());
|
DBG_LOG(DBG_BROXYGEN, "Made PackageInfo %s", name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::ScriptDependency(const string& path, const string& dep)
|
void Manager::ScriptDependency(const string& path, const string& dep)
|
||||||
|
@ -122,16 +122,16 @@ void Manager::ScriptDependency(const string& path, const string& dep)
|
||||||
|
|
||||||
string name = without_bropath_component(path);
|
string name = without_bropath_component(path);
|
||||||
string depname = without_bropath_component(dep);
|
string depname = without_bropath_component(dep);
|
||||||
ScriptDocument* script_doc = scripts.GetDocument(name);
|
ScriptInfo* script_info = scripts.GetInfo(name);
|
||||||
|
|
||||||
if ( ! script_doc )
|
if ( ! script_info )
|
||||||
{
|
{
|
||||||
DbgAndWarn(fmt("Failed to add script doc dependency %s for %s",
|
DbgAndWarn(fmt("Failed to add script doc dependency %s for %s",
|
||||||
depname.c_str(), name.c_str()));
|
depname.c_str(), name.c_str()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
script_doc->AddDependency(depname);
|
script_info->AddDependency(depname);
|
||||||
DBG_LOG(DBG_BROXYGEN, "Added script dependency %s for %s",
|
DBG_LOG(DBG_BROXYGEN, "Added script dependency %s for %s",
|
||||||
depname.c_str(), name.c_str());
|
depname.c_str(), name.c_str());
|
||||||
|
|
||||||
|
@ -146,23 +146,23 @@ void Manager::ModuleUsage(const string& path, const string& module)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
string name = without_bropath_component(path);
|
string name = without_bropath_component(path);
|
||||||
ScriptDocument* script_doc = scripts.GetDocument(name);
|
ScriptInfo* script_info = scripts.GetInfo(name);
|
||||||
|
|
||||||
if ( ! script_doc )
|
if ( ! script_info )
|
||||||
{
|
{
|
||||||
DbgAndWarn(fmt("Failed to add module usage %s in %s",
|
DbgAndWarn(fmt("Failed to add module usage %s in %s",
|
||||||
module.c_str(), name.c_str()));
|
module.c_str(), name.c_str()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
script_doc->AddModule(module);
|
script_info->AddModule(module);
|
||||||
DBG_LOG(DBG_BROXYGEN, "Added module usage %s in %s",
|
DBG_LOG(DBG_BROXYGEN, "Added module usage %s in %s",
|
||||||
module.c_str(), name.c_str());
|
module.c_str(), name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
IdentifierDocument* Manager::CreateIdentifierDoc(ID* id, ScriptDocument* script)
|
IdentifierInfo* Manager::CreateIdentifierInfo(ID* id, ScriptInfo* script)
|
||||||
{
|
{
|
||||||
IdentifierDocument* rval = new IdentifierDocument(id, script);
|
IdentifierInfo* rval = new IdentifierInfo(id, script);
|
||||||
|
|
||||||
rval->AddComments(comment_buffer);
|
rval->AddComments(comment_buffer);
|
||||||
comment_buffer.clear();
|
comment_buffer.clear();
|
||||||
|
@ -176,12 +176,12 @@ IdentifierDocument* Manager::CreateIdentifierDoc(ID* id, ScriptDocument* script)
|
||||||
comment_buffer_map.erase(it);
|
comment_buffer_map.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
all_docs.push_back(rval);
|
all_info.push_back(rval);
|
||||||
identifiers.map[id->Name()] = rval;
|
identifiers.map[id->Name()] = rval;
|
||||||
last_identifier_seen = rval;
|
last_identifier_seen = rval;
|
||||||
|
|
||||||
if ( script )
|
if ( script )
|
||||||
script->AddIdentifierDoc(rval);
|
script->AddIdentifierInfo(rval);
|
||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
@ -198,17 +198,17 @@ void Manager::StartType(ID* id)
|
||||||
}
|
}
|
||||||
|
|
||||||
string script = without_bropath_component(id->GetLocationInfo()->filename);
|
string script = without_bropath_component(id->GetLocationInfo()->filename);
|
||||||
ScriptDocument* script_doc = scripts.GetDocument(script);
|
ScriptInfo* script_info = scripts.GetInfo(script);
|
||||||
|
|
||||||
if ( ! script_doc )
|
if ( ! script_info )
|
||||||
{
|
{
|
||||||
DbgAndWarn(fmt("Can't document identifier %s, lookup of %s failed",
|
DbgAndWarn(fmt("Can't document identifier %s, lookup of %s failed",
|
||||||
id->Name(), script.c_str()));
|
id->Name(), script.c_str()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
incomplete_type = CreateIdentifierDoc(id, script_doc);
|
incomplete_type = CreateIdentifierInfo(id, script_info);
|
||||||
DBG_LOG(DBG_BROXYGEN, "Made IdentifierDocument (incomplete) %s, in %s",
|
DBG_LOG(DBG_BROXYGEN, "Made IdentifierInfo (incomplete) %s, in %s",
|
||||||
id->Name(), script.c_str());
|
id->Name(), script.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,14 +236,14 @@ void Manager::Identifier(ID* id)
|
||||||
enum_mappings[id->Name()] = incomplete_type->GetID()->Name();
|
enum_mappings[id->Name()] = incomplete_type->GetID()->Name();
|
||||||
}
|
}
|
||||||
|
|
||||||
IdentifierDocument* id_doc = identifiers.GetDocument(id->Name());
|
IdentifierInfo* id_info = identifiers.GetInfo(id->Name());
|
||||||
|
|
||||||
if ( id_doc )
|
if ( id_info )
|
||||||
{
|
{
|
||||||
if ( IsFunc(id_doc->GetID()->Type()->Tag()) )
|
if ( IsFunc(id_info->GetID()->Type()->Tag()) )
|
||||||
{
|
{
|
||||||
// Function may already been seen (declaration versus body).
|
// Function may already been seen (declaration versus body).
|
||||||
id_doc->AddComments(comment_buffer);
|
id_info->AddComments(comment_buffer);
|
||||||
comment_buffer.clear();
|
comment_buffer.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -256,24 +256,24 @@ void Manager::Identifier(ID* id)
|
||||||
{
|
{
|
||||||
// Internally-created identifier (e.g. file/proto analyzer enum tags).
|
// Internally-created identifier (e.g. file/proto analyzer enum tags).
|
||||||
// Handled specially since they don't have a script location.
|
// Handled specially since they don't have a script location.
|
||||||
DBG_LOG(DBG_BROXYGEN, "Made internal IdentifierDocument %s",
|
DBG_LOG(DBG_BROXYGEN, "Made internal IdentifierInfo %s",
|
||||||
id->Name());
|
id->Name());
|
||||||
CreateIdentifierDoc(id, 0);
|
CreateIdentifierInfo(id, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string script = without_bropath_component(id->GetLocationInfo()->filename);
|
string script = without_bropath_component(id->GetLocationInfo()->filename);
|
||||||
ScriptDocument* script_doc = scripts.GetDocument(script);
|
ScriptInfo* script_info = scripts.GetInfo(script);
|
||||||
|
|
||||||
if ( ! script_doc )
|
if ( ! script_info )
|
||||||
{
|
{
|
||||||
DbgAndWarn(fmt("Can't document identifier %s, lookup of %s failed",
|
DbgAndWarn(fmt("Can't document identifier %s, lookup of %s failed",
|
||||||
id->Name(), script.c_str()));
|
id->Name(), script.c_str()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateIdentifierDoc(id, script_doc);
|
CreateIdentifierInfo(id, script_info);
|
||||||
DBG_LOG(DBG_BROXYGEN, "Made IdentifierDocument %s, in script %s",
|
DBG_LOG(DBG_BROXYGEN, "Made IdentifierInfo %s, in script %s",
|
||||||
id->Name(), script.c_str());
|
id->Name(), script.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +283,7 @@ void Manager::RecordField(const ID* id, const TypeDecl* field,
|
||||||
if ( disabled )
|
if ( disabled )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
IdentifierDocument* idd = identifiers.GetDocument(id->Name());
|
IdentifierInfo* idd = identifiers.GetInfo(id->Name());
|
||||||
|
|
||||||
if ( ! idd )
|
if ( ! idd )
|
||||||
{
|
{
|
||||||
|
@ -308,9 +308,9 @@ void Manager::Redef(const ID* id, const string& path)
|
||||||
// This is a redef defined on the command line.
|
// This is a redef defined on the command line.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
IdentifierDocument* id_doc = identifiers.GetDocument(id->Name());
|
IdentifierInfo* id_info = identifiers.GetInfo(id->Name());
|
||||||
|
|
||||||
if ( ! id_doc )
|
if ( ! id_info )
|
||||||
{
|
{
|
||||||
DbgAndWarn(fmt("Can't document redef of %s, identifier lookup failed",
|
DbgAndWarn(fmt("Can't document redef of %s, identifier lookup failed",
|
||||||
id->Name()));
|
id->Name()));
|
||||||
|
@ -318,19 +318,19 @@ void Manager::Redef(const ID* id, const string& path)
|
||||||
}
|
}
|
||||||
|
|
||||||
string from_script = without_bropath_component(path);
|
string from_script = without_bropath_component(path);
|
||||||
ScriptDocument* script_doc = scripts.GetDocument(from_script);
|
ScriptInfo* script_info = scripts.GetInfo(from_script);
|
||||||
|
|
||||||
if ( ! script_doc )
|
if ( ! script_info )
|
||||||
{
|
{
|
||||||
DbgAndWarn(fmt("Can't document redef of %s, lookup of %s failed",
|
DbgAndWarn(fmt("Can't document redef of %s, lookup of %s failed",
|
||||||
id->Name(), from_script.c_str()));
|
id->Name(), from_script.c_str()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
id_doc->AddRedef(from_script, comment_buffer);
|
id_info->AddRedef(from_script, comment_buffer);
|
||||||
script_doc->AddRedef(id_doc);
|
script_info->AddRedef(id_info);
|
||||||
comment_buffer.clear();
|
comment_buffer.clear();
|
||||||
last_identifier_seen = id_doc;
|
last_identifier_seen = id_info;
|
||||||
DBG_LOG(DBG_BROXYGEN, "Added redef of %s from %s",
|
DBG_LOG(DBG_BROXYGEN, "Added redef of %s from %s",
|
||||||
id->Name(), from_script.c_str());
|
id->Name(), from_script.c_str());
|
||||||
}
|
}
|
||||||
|
@ -341,10 +341,10 @@ void Manager::SummaryComment(const string& script, const string& comment)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
string name = without_bropath_component(script);
|
string name = without_bropath_component(script);
|
||||||
ScriptDocument* doc = scripts.GetDocument(name);
|
ScriptInfo* info = scripts.GetInfo(name);
|
||||||
|
|
||||||
if ( doc )
|
if ( info )
|
||||||
doc->AddComment(RemoveLeadingSpace(comment));
|
info->AddComment(RemoveLeadingSpace(comment));
|
||||||
else
|
else
|
||||||
DbgAndWarn(fmt("Lookup of script %s failed for summary comment %s",
|
DbgAndWarn(fmt("Lookup of script %s failed for summary comment %s",
|
||||||
name.c_str(), comment.c_str()));
|
name.c_str(), comment.c_str()));
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
#define BROXYGEN_MANAGER_H
|
#define BROXYGEN_MANAGER_H
|
||||||
|
|
||||||
#include "Configuration.h"
|
#include "Configuration.h"
|
||||||
#include "Document.h"
|
#include "Info.h"
|
||||||
|
#include "PackageInfo.h"
|
||||||
|
#include "ScriptInfo.h"
|
||||||
|
#include "IdentifierInfo.h"
|
||||||
|
|
||||||
#include "Reporter.h"
|
#include "Reporter.h"
|
||||||
#include "ID.h"
|
#include "ID.h"
|
||||||
|
@ -17,13 +20,20 @@
|
||||||
|
|
||||||
namespace broxygen {
|
namespace broxygen {
|
||||||
|
|
||||||
// TODO: documentation...
|
/**
|
||||||
|
* Map of info objects. Just a wrapper around std::map to improve code
|
||||||
|
* readability (less typedefs for specific map types and not having to use
|
||||||
|
* iterators directly to find a particular info object).
|
||||||
|
*/
|
||||||
template<class T>
|
template<class T>
|
||||||
struct DocumentMap {
|
struct InfoMap {
|
||||||
typedef std::map<std::string, T*> map_type;
|
typedef std::map<std::string, T*> map_type;
|
||||||
|
|
||||||
T* GetDocument(const std::string& name) const
|
/**
|
||||||
|
* @param name Name of an info object to retrieve.
|
||||||
|
* @return The info object associated with \a name.
|
||||||
|
*/
|
||||||
|
T* GetInfo(const std::string& name) const
|
||||||
{
|
{
|
||||||
typename map_type::const_iterator it = map.find(name);
|
typename map_type::const_iterator it = map.find(name);
|
||||||
return it == map.end() ? 0 : it->second;
|
return it == map.end() ? 0 : it->second;
|
||||||
|
@ -32,53 +42,165 @@ struct DocumentMap {
|
||||||
map_type map;
|
map_type map;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages all documentation tracking and generation.
|
||||||
|
*/
|
||||||
class Manager {
|
class Manager {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
* @param config Path to a Broxygen config file if documentation is to be
|
||||||
|
* written to disk.
|
||||||
|
* @param bro_command The command used to invoke the bro process.
|
||||||
|
* It's used when checking for out-of-date targets. If the bro binary is
|
||||||
|
* newer then a target, it needs to be rebuilt.
|
||||||
|
*/
|
||||||
Manager(const std::string& config, const std::string& bro_command);
|
Manager(const std::string& config, const std::string& bro_command);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dtor.
|
||||||
|
*/
|
||||||
~Manager();
|
~Manager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do initialization that needs to happen before scripts are parsed.
|
||||||
|
* Currently nothing outside of what's done in ctor is needed.
|
||||||
|
*/
|
||||||
void InitPreScript();
|
void InitPreScript();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do initialization that needs to happen after scripts are parsed.
|
||||||
|
* This is primarly dependency resolution/filtering.
|
||||||
|
*/
|
||||||
void InitPostScript();
|
void InitPostScript();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds all Broxygen targets specified by config file and write out
|
||||||
|
* documentation to disk.
|
||||||
|
*/
|
||||||
void GenerateDocs() const;
|
void GenerateDocs() const;
|
||||||
|
|
||||||
void File(const std::string& path);
|
/**
|
||||||
|
* Register Bro script for which information/documentation will be gathered.
|
||||||
|
* @param path Absolute path to Bro script.
|
||||||
|
*/
|
||||||
|
void Script(const std::string& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register Bro script dependency ("@load").
|
||||||
|
* @param path Absolute path to a Bro script.
|
||||||
|
* @param dep Absolute path to a Bro script being "@load"d from script given
|
||||||
|
* by \a path.
|
||||||
|
*/
|
||||||
void ScriptDependency(const std::string& path, const std::string& dep);
|
void ScriptDependency(const std::string& path, const std::string& dep);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a module usage (script may export identifiers in to the
|
||||||
|
* module namespace).
|
||||||
|
* @param path Absolute path to a Bro script.
|
||||||
|
* @param module The module which script given by \a path is using.
|
||||||
|
*/
|
||||||
void ModuleUsage(const std::string& path, const std::string& module);
|
void ModuleUsage(const std::string& path, const std::string& module);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal that a record or enum type is now being parsed.
|
||||||
|
* @param id The record or enum type identifier.
|
||||||
|
*/
|
||||||
void StartType(ID* id);
|
void StartType(ID* id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a script-level identifier for which information/documentation
|
||||||
|
* will be gathered.
|
||||||
|
* @param id The script-level identifier.
|
||||||
|
*/
|
||||||
void Identifier(ID* id);
|
void Identifier(ID* id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a record-field for which information/documentation will be
|
||||||
|
* gathered.
|
||||||
|
* @param id The identifier of the record type which has the field.
|
||||||
|
* @param field The field name/type information.
|
||||||
|
* @param path Absolute path to a Bro script in which this field is
|
||||||
|
* declared. This can be different from the place where the record type
|
||||||
|
* is declared due to redefs.
|
||||||
|
*/
|
||||||
void RecordField(const ID* id, const TypeDecl* field,
|
void RecordField(const ID* id, const TypeDecl* field,
|
||||||
const std::string& path);
|
const std::string& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a redefinition of a particular identifier.
|
||||||
|
* @param id The identifier being redef'd.
|
||||||
|
* @param path Absolute path to a Bro script doing the redef.
|
||||||
|
*/
|
||||||
void Redef(const ID* id, const std::string& path);
|
void Redef(const ID* id, const std::string& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register Broxygen script summary content.
|
||||||
|
* @param path Absolute path to a Bro script.
|
||||||
|
* @param comment Broxygen-style summary comment ("##!") to associate with
|
||||||
|
* script given by \a path.
|
||||||
|
*/
|
||||||
void SummaryComment(const std::string& path, const std::string& comment);
|
void SummaryComment(const std::string& path, const std::string& comment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a Broxygen comment ("##") for an upcoming identifier (i.e.
|
||||||
|
* this content is buffered and consumed by next identifier/field
|
||||||
|
* declaration.
|
||||||
|
* @param comment Content of the Broxygen comment.
|
||||||
|
*/
|
||||||
void PreComment(const std::string& comment);
|
void PreComment(const std::string& comment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a Broxygen comment ("##<") for the last identifier seen.
|
||||||
|
* @param comment Content of the Broxygen comment.
|
||||||
|
* @param identifier_hint Expected name of identifier with which to
|
||||||
|
* associate \a comment.
|
||||||
|
*/
|
||||||
void PostComment(const std::string& comment,
|
void PostComment(const std::string& comment,
|
||||||
const std::string& identifier_hint = "");
|
const std::string& identifier_hint = "");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param id Name of script-level enum identifier.
|
||||||
|
* @return The name of the enum's type.
|
||||||
|
*/
|
||||||
std::string GetEnumTypeName(const std::string& id) const;
|
std::string GetEnumTypeName(const std::string& id) const;
|
||||||
|
|
||||||
IdentifierDocument* GetIdentifierDoc(const std::string& name) const
|
/**
|
||||||
{ return identifiers.GetDocument(name); }
|
* @param name Name of a script-level identifier.
|
||||||
|
* @return an identifier info object associated with \a name or a null
|
||||||
|
* pointer if it's not a known identifier.
|
||||||
|
*/
|
||||||
|
IdentifierInfo* GetIdentifierInfo(const std::string& name) const
|
||||||
|
{ return identifiers.GetInfo(name); }
|
||||||
|
|
||||||
ScriptDocument* GetScriptDoc(const std::string& name) const
|
/**
|
||||||
{ return scripts.GetDocument(name); }
|
* @param name Name of a Bro script ("normalized" to be a path relative
|
||||||
|
* to a component within BROPATH).
|
||||||
|
* @return a script info object associated with \a name or a null pointer
|
||||||
|
* if it's not a known script name.
|
||||||
|
*/
|
||||||
|
ScriptInfo* GetScriptInfo(const std::string& name) const
|
||||||
|
{ return scripts.GetInfo(name); }
|
||||||
|
|
||||||
PackageDocument* GetPackageDoc(const std::string& name) const
|
/**
|
||||||
{ return packages.GetDocument(name); }
|
* @param name Nmae of a Bro script package ("normalized" to be a path
|
||||||
|
* relative to a component within BROPATH).
|
||||||
|
* @return a package info object assocated with \a name or a null pointer
|
||||||
|
* if it's not a known package name.
|
||||||
|
*/
|
||||||
|
PackageInfo* GetPackageInfo(const std::string& name) const
|
||||||
|
{ return packages.GetInfo(name); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a Broxygen target is up-to-date.
|
||||||
|
* @param target_file output file of a Broxygen target.
|
||||||
|
* @param dependencies all dependencies of the target.
|
||||||
|
* @return true if modification time of \a target_file is newer than
|
||||||
|
* modification time of Bro binary, Broxygen config file, and all
|
||||||
|
* dependencies, else false.
|
||||||
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
bool IsUpToDate(const std::string& target_file,
|
bool IsUpToDate(const std::string& target_file,
|
||||||
const std::vector<T*>& dependencies) const;
|
const std::vector<T*>& dependencies) const;
|
||||||
|
@ -88,18 +210,18 @@ private:
|
||||||
typedef std::vector<std::string> comment_buffer_t;
|
typedef std::vector<std::string> comment_buffer_t;
|
||||||
typedef std::map<std::string, comment_buffer_t> comment_buffer_map_t;
|
typedef std::map<std::string, comment_buffer_t> comment_buffer_map_t;
|
||||||
|
|
||||||
IdentifierDocument* CreateIdentifierDoc(ID* id, ScriptDocument* script);
|
IdentifierInfo* CreateIdentifierInfo(ID* id, ScriptInfo* script);
|
||||||
|
|
||||||
bool disabled;
|
bool disabled;
|
||||||
comment_buffer_t comment_buffer; // For whatever next identifier that comes in.
|
comment_buffer_t comment_buffer; // For whatever next identifier comes in.
|
||||||
comment_buffer_map_t comment_buffer_map; // For a particular identifier.
|
comment_buffer_map_t comment_buffer_map; // For a particular identifier.
|
||||||
DocumentMap<PackageDocument> packages;
|
InfoMap<PackageInfo> packages;
|
||||||
DocumentMap<ScriptDocument> scripts;
|
InfoMap<ScriptInfo> scripts;
|
||||||
DocumentMap<IdentifierDocument> identifiers;
|
InfoMap<IdentifierInfo> identifiers;
|
||||||
std::vector<Document*> all_docs;
|
std::vector<Info*> all_info;
|
||||||
IdentifierDocument* last_identifier_seen;
|
IdentifierInfo* last_identifier_seen;
|
||||||
IdentifierDocument* incomplete_type;
|
IdentifierInfo* incomplete_type;
|
||||||
std::map<std::string, std::string> enum_mappings;
|
std::map<std::string, std::string> enum_mappings; // enum id -> enum type id
|
||||||
Config config;
|
Config config;
|
||||||
time_t bro_mtime;
|
time_t bro_mtime;
|
||||||
};
|
};
|
||||||
|
|
55
src/broxygen/PackageInfo.cc
Normal file
55
src/broxygen/PackageInfo.cc
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#include "PackageInfo.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include "Reporter.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace broxygen;
|
||||||
|
|
||||||
|
PackageInfo::PackageInfo(const string& arg_name)
|
||||||
|
: Info(),
|
||||||
|
pkg_name(arg_name), readme()
|
||||||
|
{
|
||||||
|
string readme_file = find_file(pkg_name + "/README", bro_path());
|
||||||
|
|
||||||
|
if ( readme_file.empty() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
ifstream f(readme_file.c_str());
|
||||||
|
|
||||||
|
if ( ! f.is_open() )
|
||||||
|
reporter->InternalWarning("Broxygen failed to open '%s': %s",
|
||||||
|
readme_file.c_str(), strerror(errno));
|
||||||
|
|
||||||
|
string line;
|
||||||
|
|
||||||
|
while ( getline(f, line) )
|
||||||
|
readme.push_back(line);
|
||||||
|
|
||||||
|
if ( f.bad() )
|
||||||
|
reporter->InternalWarning("Broxygen error reading '%s': %s",
|
||||||
|
readme_file.c_str(), strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
string PackageInfo::DoReStructuredText(bool roles_only) const
|
||||||
|
{
|
||||||
|
string rval = fmt(":doc:`%s </scripts/%s/index>`\n\n", pkg_name.c_str(),
|
||||||
|
pkg_name.c_str());
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < readme.size(); ++i )
|
||||||
|
rval += " " + readme[i] + "\n";
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t PackageInfo::DoGetModificationTime() const
|
||||||
|
{
|
||||||
|
string readme_file = find_file(pkg_name + "/README", bro_path());
|
||||||
|
|
||||||
|
if ( readme_file.empty() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return broxygen::get_mtime(readme_file);
|
||||||
|
}
|
48
src/broxygen/PackageInfo.h
Normal file
48
src/broxygen/PackageInfo.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef BROXYGEN_PACKAGEINFO_H
|
||||||
|
#define BROXYGEN_PACKAGEINFO_H
|
||||||
|
|
||||||
|
#include "Info.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace broxygen {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about a Bro script package.
|
||||||
|
*/
|
||||||
|
class PackageInfo : public Info {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
* @param name The name of the Bro script package (relative path from a
|
||||||
|
* component within BROPATH.
|
||||||
|
*/
|
||||||
|
PackageInfo(const std::string& name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The content of the package's README file, each line being
|
||||||
|
* an element in the returned vector. If the package has no README, the
|
||||||
|
* vector is empty.
|
||||||
|
*/
|
||||||
|
std::vector<std::string> GetReadme() const
|
||||||
|
{ return readme; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
time_t DoGetModificationTime() const;
|
||||||
|
|
||||||
|
std::string DoName() const
|
||||||
|
{ return pkg_name; }
|
||||||
|
|
||||||
|
std::string DoReStructuredText(bool roles_only) const;
|
||||||
|
|
||||||
|
std::string pkg_name;
|
||||||
|
std::vector<std::string> readme;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace broxygen
|
||||||
|
|
||||||
|
#endif
|
66
src/broxygen/ReStructuredTextTable.cc
Normal file
66
src/broxygen/ReStructuredTextTable.cc
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#include "ReStructuredTextTable.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace broxygen;
|
||||||
|
|
||||||
|
ReStructuredTextTable::ReStructuredTextTable(size_t arg_num_cols)
|
||||||
|
: num_cols(arg_num_cols), rows(), longest_row_in_column()
|
||||||
|
{
|
||||||
|
for ( size_t i = 0; i < num_cols; ++i )
|
||||||
|
longest_row_in_column.push_back(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReStructuredTextTable::AddRow(const vector<string>& new_row)
|
||||||
|
{
|
||||||
|
assert(new_row.size() == num_cols);
|
||||||
|
rows.push_back(new_row);
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < new_row.size(); ++i )
|
||||||
|
if ( new_row[i].size() > longest_row_in_column[i] )
|
||||||
|
longest_row_in_column[i] = new_row[i].size();
|
||||||
|
}
|
||||||
|
|
||||||
|
string ReStructuredTextTable::MakeBorder(const vector<size_t> col_sizes,
|
||||||
|
char border)
|
||||||
|
{
|
||||||
|
string rval;
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < col_sizes.size(); ++i )
|
||||||
|
{
|
||||||
|
if ( i > 0 )
|
||||||
|
rval += " ";
|
||||||
|
|
||||||
|
rval += string(col_sizes[i], border);
|
||||||
|
}
|
||||||
|
|
||||||
|
rval += "\n";
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ReStructuredTextTable::AsString(char border) const
|
||||||
|
{
|
||||||
|
string rval = MakeBorder(longest_row_in_column, border);
|
||||||
|
|
||||||
|
for ( size_t row = 0; row < rows.size(); ++row )
|
||||||
|
{
|
||||||
|
for ( size_t col = 0; col < num_cols; ++col )
|
||||||
|
{
|
||||||
|
if ( col > 0 )
|
||||||
|
{
|
||||||
|
size_t last = rows[row][col - 1].size();
|
||||||
|
size_t longest = longest_row_in_column[col - 1];
|
||||||
|
size_t whitespace = longest - last + 1;
|
||||||
|
rval += string(whitespace, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
rval += rows[row][col];
|
||||||
|
}
|
||||||
|
|
||||||
|
rval += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
rval += MakeBorder(longest_row_in_column, border);
|
||||||
|
return rval;
|
||||||
|
}
|
51
src/broxygen/ReStructuredTextTable.h
Normal file
51
src/broxygen/ReStructuredTextTable.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#ifndef BROXYGEN_RESTTABLE_H
|
||||||
|
#define BROXYGEN_RESTTABLE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace broxygen {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reST table with arbitrary number of columns.
|
||||||
|
*/
|
||||||
|
class ReStructuredTextTable {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the reST table object.
|
||||||
|
* @param arg_num_cols The number of columns in the table.
|
||||||
|
*/
|
||||||
|
ReStructuredTextTable(size_t arg_num_cols);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new content row to the table.
|
||||||
|
* @param new_row A vector with one element for each column in the table.
|
||||||
|
*/
|
||||||
|
void AddRow(const std::vector<std::string>& new_row);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param col_sizes Vector of column sizes (width in number of characters).
|
||||||
|
* @param border Character to use for the border.
|
||||||
|
* @return A border sized appropriated for the table with columns of sizes
|
||||||
|
* denoted by \a col_sizes.
|
||||||
|
*/
|
||||||
|
static std::string MakeBorder(const std::vector<size_t> col_sizes,
|
||||||
|
char border);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param border Character to use for the border.
|
||||||
|
* @return the reST representation of the table and its content.
|
||||||
|
*/
|
||||||
|
std::string AsString(char border) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
size_t num_cols;
|
||||||
|
std::vector<std::vector<std::string> > rows;
|
||||||
|
std::vector<size_t> longest_row_in_column;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace broxygen
|
||||||
|
|
||||||
|
#endif
|
361
src/broxygen/ScriptInfo.cc
Normal file
361
src/broxygen/ScriptInfo.cc
Normal file
|
@ -0,0 +1,361 @@
|
||||||
|
#include "ScriptInfo.h"
|
||||||
|
#include "IdentifierInfo.h"
|
||||||
|
#include "ReStructuredTextTable.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "Manager.h"
|
||||||
|
|
||||||
|
#include "Reporter.h"
|
||||||
|
#include "Desc.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace broxygen;
|
||||||
|
|
||||||
|
bool IdInfoComp::operator ()(const IdentifierInfo* lhs,
|
||||||
|
const IdentifierInfo* rhs) const
|
||||||
|
{
|
||||||
|
return lhs->Name() < rhs->Name();
|
||||||
|
}
|
||||||
|
|
||||||
|
static vector<string> summary_comment(const vector<string>& cmnts)
|
||||||
|
{
|
||||||
|
vector<string> rval;
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < cmnts.size(); ++i )
|
||||||
|
{
|
||||||
|
size_t end = broxygen::end_of_first_sentence(cmnts[i]);
|
||||||
|
|
||||||
|
if ( end == string::npos )
|
||||||
|
{
|
||||||
|
if ( broxygen::is_all_whitespace(cmnts[i]) )
|
||||||
|
break;
|
||||||
|
|
||||||
|
rval.push_back(cmnts[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rval.push_back(cmnts[i].substr(0, end + 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_summary_rows(const ODesc& id_desc, const vector<string>& cmnts,
|
||||||
|
ReStructuredTextTable* table)
|
||||||
|
{
|
||||||
|
vector<string> row;
|
||||||
|
row.push_back(id_desc.Description());
|
||||||
|
|
||||||
|
if ( cmnts.empty() )
|
||||||
|
{
|
||||||
|
row.push_back("");
|
||||||
|
table->AddRow(row);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
row.push_back(cmnts[0]);
|
||||||
|
table->AddRow(row);
|
||||||
|
|
||||||
|
for ( size_t i = 1; i < cmnts.size(); ++i )
|
||||||
|
{
|
||||||
|
row.clear();
|
||||||
|
row.push_back("");
|
||||||
|
row.push_back(cmnts[i]);
|
||||||
|
table->AddRow(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static string make_summary(const string& heading, char underline, char border,
|
||||||
|
const id_info_list& id_list)
|
||||||
|
{
|
||||||
|
if ( id_list.empty() )
|
||||||
|
return "";
|
||||||
|
|
||||||
|
ReStructuredTextTable table(2);
|
||||||
|
|
||||||
|
for ( id_info_list::const_iterator it = id_list.begin();
|
||||||
|
it != id_list.end(); ++it )
|
||||||
|
{
|
||||||
|
ID* id = (*it)->GetID();
|
||||||
|
ODesc d;
|
||||||
|
d.SetQuotes(1);
|
||||||
|
id->DescribeReSTShort(&d);
|
||||||
|
add_summary_rows(d, summary_comment((*it)->GetComments()), &table);
|
||||||
|
}
|
||||||
|
|
||||||
|
return broxygen::make_heading(heading, underline) + table.AsString(border)
|
||||||
|
+ "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
static string make_redef_summary(const string& heading, char underline,
|
||||||
|
char border, const string& from_script,
|
||||||
|
const id_info_set& id_set)
|
||||||
|
{
|
||||||
|
if ( id_set.empty() )
|
||||||
|
return "";
|
||||||
|
|
||||||
|
ReStructuredTextTable table(2);
|
||||||
|
|
||||||
|
for ( id_info_set::const_iterator it = id_set.begin(); it != id_set.end();
|
||||||
|
++it )
|
||||||
|
{
|
||||||
|
ID* id = (*it)->GetID();
|
||||||
|
ODesc d;
|
||||||
|
d.SetQuotes(1);
|
||||||
|
id->DescribeReSTShort(&d);
|
||||||
|
|
||||||
|
typedef list<IdentifierInfo::Redefinition> redef_list;
|
||||||
|
redef_list redefs = (*it)->GetRedefs(from_script);
|
||||||
|
|
||||||
|
for ( redef_list::const_iterator iit = redefs.begin();
|
||||||
|
iit != redefs.end(); ++iit )
|
||||||
|
add_summary_rows(d, summary_comment(iit->comments), &table);
|
||||||
|
}
|
||||||
|
|
||||||
|
return broxygen::make_heading(heading, underline) + table.AsString(border)
|
||||||
|
+ "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
static string make_details(const string& heading, char underline,
|
||||||
|
const id_info_list& id_list)
|
||||||
|
{
|
||||||
|
if ( id_list.empty() )
|
||||||
|
return "";
|
||||||
|
|
||||||
|
string rval = broxygen::make_heading(heading, underline);
|
||||||
|
|
||||||
|
for ( id_info_list::const_iterator it = id_list.begin();
|
||||||
|
it != id_list.end(); ++it )
|
||||||
|
{
|
||||||
|
rval += (*it)->ReStructuredText();
|
||||||
|
rval += "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string make_redef_details(const string& heading, char underline,
|
||||||
|
const id_info_set& id_set)
|
||||||
|
{
|
||||||
|
if ( id_set.empty() )
|
||||||
|
return "";
|
||||||
|
|
||||||
|
string rval = broxygen::make_heading(heading, underline);
|
||||||
|
|
||||||
|
for ( id_info_set::const_iterator it = id_set.begin();
|
||||||
|
it != id_set.end(); ++it )
|
||||||
|
{
|
||||||
|
rval += (*it)->ReStructuredText(true);
|
||||||
|
rval += "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptInfo::ScriptInfo(const string& arg_name, const string& arg_path)
|
||||||
|
: Info(),
|
||||||
|
name(arg_name), path(arg_path),
|
||||||
|
is_pkg_loader(SafeBasename(name).result == PACKAGE_LOADER),
|
||||||
|
dependencies(), module_usages(), comments(), id_info(),
|
||||||
|
options(), constants(), state_vars(), types(), events(), hooks(),
|
||||||
|
functions(), redefs()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptInfo::AddIdentifierInfo(IdentifierInfo* info)
|
||||||
|
{
|
||||||
|
id_info[info->Name()] = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptInfo::DoInitPostScript()
|
||||||
|
{
|
||||||
|
for ( id_info_map::const_iterator it = id_info.begin();
|
||||||
|
it != id_info.end(); ++it )
|
||||||
|
{
|
||||||
|
IdentifierInfo* info = it->second;
|
||||||
|
ID* id = info->GetID();
|
||||||
|
|
||||||
|
if ( ! broxygen::is_public_api(id) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( id->AsType() )
|
||||||
|
{
|
||||||
|
types.push_back(info);
|
||||||
|
DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as a type",
|
||||||
|
id->Name(), name.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( IsFunc(id->Type()->Tag()) )
|
||||||
|
{
|
||||||
|
switch ( id->Type()->AsFuncType()->Flavor() ) {
|
||||||
|
case FUNC_FLAVOR_HOOK:
|
||||||
|
DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as a hook",
|
||||||
|
id->Name(), name.c_str());
|
||||||
|
hooks.push_back(info);
|
||||||
|
break;
|
||||||
|
case FUNC_FLAVOR_EVENT:
|
||||||
|
DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as a event",
|
||||||
|
id->Name(), name.c_str());
|
||||||
|
events.push_back(info);
|
||||||
|
break;
|
||||||
|
case FUNC_FLAVOR_FUNCTION:
|
||||||
|
DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as a function",
|
||||||
|
id->Name(), name.c_str());
|
||||||
|
functions.push_back(info);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
reporter->InternalError("Invalid function flavor");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( id->IsConst() )
|
||||||
|
{
|
||||||
|
if ( id->FindAttr(ATTR_REDEF) )
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as an option",
|
||||||
|
id->Name(), name.c_str());
|
||||||
|
options.push_back(info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as a constant",
|
||||||
|
id->Name(), name.c_str());
|
||||||
|
constants.push_back(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( id->Type()->Tag() == TYPE_ENUM )
|
||||||
|
// Enums are always referenced/documented from the type's
|
||||||
|
// documentation.
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as a state variable",
|
||||||
|
id->Name(), name.c_str());
|
||||||
|
state_vars.push_back(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string> ScriptInfo::GetComments() const
|
||||||
|
{
|
||||||
|
return comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ScriptInfo::DoReStructuredText(bool roles_only) const
|
||||||
|
{
|
||||||
|
string rval;
|
||||||
|
|
||||||
|
rval += ":tocdepth: 3\n\n";
|
||||||
|
rval += broxygen::make_heading(name, '=');
|
||||||
|
|
||||||
|
for ( string_set::const_iterator it = module_usages.begin();
|
||||||
|
it != module_usages.end(); ++it )
|
||||||
|
rval += ".. bro:namespace:: " + *it + "\n";
|
||||||
|
|
||||||
|
rval += "\n";
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < comments.size(); ++i )
|
||||||
|
rval += comments[i] + "\n";
|
||||||
|
|
||||||
|
rval += "\n";
|
||||||
|
|
||||||
|
if ( ! module_usages.empty() )
|
||||||
|
{
|
||||||
|
rval += module_usages.size() > 1 ? ":Namespaces: " : ":Namespace: ";
|
||||||
|
|
||||||
|
for ( string_set::const_iterator it = module_usages.begin();
|
||||||
|
it != module_usages.end(); ++it )
|
||||||
|
{
|
||||||
|
if ( it != module_usages.begin() )
|
||||||
|
rval += ", ";
|
||||||
|
|
||||||
|
rval += *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
rval += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! dependencies.empty() )
|
||||||
|
{
|
||||||
|
rval += ":Imports: ";
|
||||||
|
|
||||||
|
for ( string_set::const_iterator it = dependencies.begin();
|
||||||
|
it != dependencies.end(); ++it )
|
||||||
|
{
|
||||||
|
if ( it != dependencies.begin() )
|
||||||
|
rval += ", ";
|
||||||
|
|
||||||
|
string path = find_file(*it, bro_path(), "bro");
|
||||||
|
string doc = *it;
|
||||||
|
|
||||||
|
if ( ! path.empty() && is_dir(path.c_str()) )
|
||||||
|
// Reference the package.
|
||||||
|
doc += "/index";
|
||||||
|
|
||||||
|
rval += fmt(":doc:`%s </scripts/%s>`", it->c_str(), doc.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
rval += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
rval += fmt(":Source File: :download:`/scripts/%s`\n", name.c_str());
|
||||||
|
rval += "\n";
|
||||||
|
rval += broxygen::make_heading("Summary", '~');
|
||||||
|
rval += make_summary("Options", '#', '=', options);
|
||||||
|
rval += make_summary("Constants", '#', '=', constants);
|
||||||
|
rval += make_summary("State Variables", '#', '=', state_vars);
|
||||||
|
rval += make_summary("Types", '#', '=', types);
|
||||||
|
rval += make_redef_summary("Redefinitions", '#', '=', name, redefs);
|
||||||
|
rval += make_summary("Events", '#', '=', events);
|
||||||
|
rval += make_summary("Hooks", '#', '=', hooks);
|
||||||
|
rval += make_summary("Functions", '#', '=', functions);
|
||||||
|
rval += "\n";
|
||||||
|
rval += broxygen::make_heading("Detailed Interface", '~');
|
||||||
|
rval += make_details("Options", '#', options);
|
||||||
|
rval += make_details("Constants", '#', constants);
|
||||||
|
rval += make_details("State Variables", '#', state_vars);
|
||||||
|
rval += make_details("Types", '#', types);
|
||||||
|
//rval += make_redef_details("Redefinitions", '#', redefs);
|
||||||
|
rval += make_details("Events", '#', events);
|
||||||
|
rval += make_details("Hooks", '#', hooks);
|
||||||
|
rval += make_details("Functions", '#', functions);
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t ScriptInfo::DoGetModificationTime() const
|
||||||
|
{
|
||||||
|
time_t most_recent = broxygen::get_mtime(path);
|
||||||
|
|
||||||
|
for ( string_set::const_iterator it = dependencies.begin();
|
||||||
|
it != dependencies.end(); ++it )
|
||||||
|
{
|
||||||
|
Info* info = broxygen_mgr->GetScriptInfo(*it);
|
||||||
|
|
||||||
|
if ( ! info )
|
||||||
|
{
|
||||||
|
string pkg_name = *it + "/" + PACKAGE_LOADER;
|
||||||
|
info = broxygen_mgr->GetScriptInfo(pkg_name);
|
||||||
|
|
||||||
|
if ( ! info )
|
||||||
|
reporter->InternalWarning("Broxygen failed to get mtime of %s",
|
||||||
|
it->c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t dep_mtime = info->GetModificationTime();
|
||||||
|
|
||||||
|
if ( dep_mtime > most_recent )
|
||||||
|
most_recent = dep_mtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return most_recent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
121
src/broxygen/ScriptInfo.h
Normal file
121
src/broxygen/ScriptInfo.h
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
#ifndef BROXYGEN_SCRIPTINFO_H
|
||||||
|
#define BROXYGEN_SCRIPTINFO_H
|
||||||
|
|
||||||
|
#include "Info.h"
|
||||||
|
#include "IdentifierInfo.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace broxygen {
|
||||||
|
|
||||||
|
class IdentifierInfo;
|
||||||
|
|
||||||
|
struct IdInfoComp {
|
||||||
|
bool operator() (const IdentifierInfo* lhs,
|
||||||
|
const IdentifierInfo* rhs) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::set<IdentifierInfo*, IdInfoComp> id_info_set;
|
||||||
|
typedef std::list<IdentifierInfo*> id_info_list;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about a Bro script.
|
||||||
|
*/
|
||||||
|
class ScriptInfo : public Info {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
* @param name Name of script: a path relative to a component in BROPATH.
|
||||||
|
* @param path Absolute path to the script.
|
||||||
|
*/
|
||||||
|
ScriptInfo(const std::string& name, const std::string& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate a Broxygen summary comment ("##!") with the script.
|
||||||
|
* @param comment String extracted from the comment.
|
||||||
|
*/
|
||||||
|
void AddComment(const std::string& comment)
|
||||||
|
{ comments.push_back(comment); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a dependency on another script.
|
||||||
|
* @param name Name of a script with this one @loads. This is the
|
||||||
|
* "normalized" name (a path relative to a component in BROPATH).
|
||||||
|
*/
|
||||||
|
void AddDependency(const std::string& name)
|
||||||
|
{ dependencies.insert(name); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a module usage (signifying the script may export identifiers
|
||||||
|
* into that modules namespace).
|
||||||
|
* @param name The name of the module.
|
||||||
|
*/
|
||||||
|
void AddModule(const std::string& name)
|
||||||
|
{ module_usages.insert(name); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an identifier declared by this script.
|
||||||
|
* @param info The identifier info object associated with a script-level
|
||||||
|
* identifier declared by the script.
|
||||||
|
*/
|
||||||
|
void AddIdentifierInfo(IdentifierInfo* info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a redef of an identifier done by this script.
|
||||||
|
* @param info The identifier info object associated with the script-level
|
||||||
|
* identifier redef'd by the script.
|
||||||
|
*/
|
||||||
|
void AddRedef(IdentifierInfo* info)
|
||||||
|
{ redefs.insert(info); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether the script is a package loader (i.e. "__load__.bro").
|
||||||
|
*/
|
||||||
|
bool IsPkgLoader() const
|
||||||
|
{ return is_pkg_loader; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return All the scripts Broxygen summary comments.
|
||||||
|
*/
|
||||||
|
std::vector<std::string> GetComments() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
typedef std::map<std::string, IdentifierInfo*> id_info_map;
|
||||||
|
typedef std::set<std::string> string_set;
|
||||||
|
|
||||||
|
time_t DoGetModificationTime() const;
|
||||||
|
|
||||||
|
std::string DoName() const
|
||||||
|
{ return name; }
|
||||||
|
|
||||||
|
std::string DoReStructuredText(bool roles_only) const;
|
||||||
|
|
||||||
|
void DoInitPostScript() /* override */;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
std::string path;
|
||||||
|
bool is_pkg_loader;
|
||||||
|
string_set dependencies;
|
||||||
|
string_set module_usages;
|
||||||
|
std::vector<std::string> comments;
|
||||||
|
id_info_map id_info;
|
||||||
|
id_info_list options;
|
||||||
|
id_info_list constants;
|
||||||
|
id_info_list state_vars;
|
||||||
|
id_info_list types;
|
||||||
|
id_info_list events;
|
||||||
|
id_info_list hooks;
|
||||||
|
id_info_list functions;
|
||||||
|
id_info_set redefs;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace broxygen
|
||||||
|
|
||||||
|
#endif
|
595
src/broxygen/Target.cc
Normal file
595
src/broxygen/Target.cc
Normal file
|
@ -0,0 +1,595 @@
|
||||||
|
#include "Target.h"
|
||||||
|
#include "Manager.h"
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "Reporter.h"
|
||||||
|
#include "plugin/Manager.h"
|
||||||
|
#include "analyzer/Manager.h"
|
||||||
|
#include "analyzer/Component.h"
|
||||||
|
#include "file_analysis/Manager.h"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fts.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace broxygen;
|
||||||
|
|
||||||
|
static void write_plugin_section_heading(FILE* f, const plugin::Plugin* p)
|
||||||
|
{
|
||||||
|
string name = p->Name();
|
||||||
|
|
||||||
|
fprintf(f, "%s\n", name.c_str());
|
||||||
|
for ( size_t i = 0; i < name.size(); ++i )
|
||||||
|
fprintf(f, "-");
|
||||||
|
fprintf(f, "\n\n");
|
||||||
|
|
||||||
|
fprintf(f, "%s\n\n", p->Description());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_analyzer_component(FILE* f, const analyzer::Component* c)
|
||||||
|
{
|
||||||
|
EnumType* atag = analyzer_mgr->GetTagEnumType();
|
||||||
|
string tag = fmt("ANALYZER_%s", c->CanonicalName());
|
||||||
|
|
||||||
|
if ( atag->Lookup("Analyzer", tag.c_str()) < 0 )
|
||||||
|
reporter->InternalError("missing analyzer tag for %s", tag.c_str());
|
||||||
|
|
||||||
|
fprintf(f, ":bro:enum:`Analyzer::%s`\n\n", tag.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_analyzer_component(FILE* f, const file_analysis::Component* c)
|
||||||
|
{
|
||||||
|
EnumType* atag = file_mgr->GetTagEnumType();
|
||||||
|
string tag = fmt("ANALYZER_%s", c->CanonicalName());
|
||||||
|
|
||||||
|
if ( atag->Lookup("Files", tag.c_str()) < 0 )
|
||||||
|
reporter->InternalError("missing analyzer tag for %s", tag.c_str());
|
||||||
|
|
||||||
|
fprintf(f, ":bro:enum:`Files::%s`\n\n", tag.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_plugin_components(FILE* f, const plugin::Plugin* p)
|
||||||
|
{
|
||||||
|
plugin::Plugin::component_list components = p->Components();
|
||||||
|
plugin::Plugin::component_list::const_iterator it;
|
||||||
|
|
||||||
|
fprintf(f, "Components\n");
|
||||||
|
fprintf(f, "++++++++++\n\n");
|
||||||
|
|
||||||
|
for ( it = components.begin(); it != components.end(); ++it )
|
||||||
|
{
|
||||||
|
switch ( (*it)->Type() ) {
|
||||||
|
case plugin::component::ANALYZER:
|
||||||
|
{
|
||||||
|
const analyzer::Component* c =
|
||||||
|
dynamic_cast<const analyzer::Component*>(*it);
|
||||||
|
|
||||||
|
if ( c )
|
||||||
|
write_analyzer_component(f, c);
|
||||||
|
else
|
||||||
|
reporter->InternalError("component type mismatch");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case plugin::component::FILE_ANALYZER:
|
||||||
|
{
|
||||||
|
const file_analysis::Component* c =
|
||||||
|
dynamic_cast<const file_analysis::Component*>(*it);
|
||||||
|
|
||||||
|
if ( c )
|
||||||
|
write_analyzer_component(f, c);
|
||||||
|
else
|
||||||
|
reporter->InternalError("component type mismatch");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case plugin::component::READER:
|
||||||
|
reporter->InternalError("docs for READER component unimplemented");
|
||||||
|
|
||||||
|
case plugin::component::WRITER:
|
||||||
|
reporter->InternalError("docs for WRITER component unimplemented");
|
||||||
|
|
||||||
|
default:
|
||||||
|
reporter->InternalError("docs for unknown component unimplemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_plugin_bif_items(FILE* f, const plugin::Plugin* p,
|
||||||
|
plugin::BifItem::Type t, const string& heading)
|
||||||
|
{
|
||||||
|
plugin::Plugin::bif_item_list bifitems = p->BifItems();
|
||||||
|
plugin::Plugin::bif_item_list::iterator it = bifitems.begin();
|
||||||
|
|
||||||
|
while ( it != bifitems.end() )
|
||||||
|
{
|
||||||
|
if ( it->GetType() != t )
|
||||||
|
it = bifitems.erase(it);
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( bifitems.empty() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
fprintf(f, "%s\n", heading.c_str());
|
||||||
|
for ( size_t i = 0; i < heading.size(); ++i )
|
||||||
|
fprintf(f, "+");
|
||||||
|
fprintf(f, "\n\n");
|
||||||
|
|
||||||
|
for ( it = bifitems.begin(); it != bifitems.end(); ++it )
|
||||||
|
{
|
||||||
|
broxygen::IdentifierInfo* doc = broxygen_mgr->GetIdentifierInfo(
|
||||||
|
it->GetID());
|
||||||
|
|
||||||
|
if ( doc )
|
||||||
|
fprintf(f, "%s\n\n", doc->ReStructuredText().c_str());
|
||||||
|
else
|
||||||
|
reporter->InternalWarning("Broxygen ID lookup failed: %s\n",
|
||||||
|
it->GetID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteAnalyzerTagDefn(FILE* f, const string& module)
|
||||||
|
{
|
||||||
|
string tag_id = module + "::Tag";
|
||||||
|
|
||||||
|
broxygen::IdentifierInfo* doc = broxygen_mgr->GetIdentifierInfo(tag_id);
|
||||||
|
|
||||||
|
if ( ! doc )
|
||||||
|
reporter->InternalError("Broxygen failed analyzer tag lookup: %s",
|
||||||
|
tag_id.c_str());
|
||||||
|
|
||||||
|
fprintf(f, "%s\n", doc->ReStructuredText().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ComponentsMatch(const plugin::Plugin* p, plugin::component::Type t,
|
||||||
|
bool match_empty = false)
|
||||||
|
{
|
||||||
|
plugin::Plugin::component_list components = p->Components();
|
||||||
|
plugin::Plugin::component_list::const_iterator it;
|
||||||
|
|
||||||
|
if ( components.empty() )
|
||||||
|
return match_empty;
|
||||||
|
|
||||||
|
for ( it = components.begin(); it != components.end(); ++it )
|
||||||
|
if ( (*it)->Type() != t )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
static vector<T*> filter_matches(const vector<Info*>& from, Target* t)
|
||||||
|
{
|
||||||
|
vector<T*> rval;
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < from.size(); ++i )
|
||||||
|
{
|
||||||
|
T* d = dynamic_cast<T*>(from[i]);
|
||||||
|
|
||||||
|
if ( ! d )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( t->MatchesPattern(d) )
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_BROXYGEN, "'%s' matched pattern for target '%s'",
|
||||||
|
d->Name().c_str(), t->Name().c_str());
|
||||||
|
rval.push_back(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
TargetFile::TargetFile(const string& arg_name)
|
||||||
|
: name(arg_name), f()
|
||||||
|
{
|
||||||
|
if ( name.find('/') != string::npos )
|
||||||
|
{
|
||||||
|
string dir = SafeDirname(name).result;
|
||||||
|
|
||||||
|
if ( ! ensure_intermediate_dirs(dir.c_str()) )
|
||||||
|
reporter->FatalError("Broxygen failed to make dir %s",
|
||||||
|
dir.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
f = fopen(name.c_str(), "w");
|
||||||
|
|
||||||
|
if ( ! f )
|
||||||
|
reporter->FatalError("Broxygen failed to open '%s' for writing: %s",
|
||||||
|
name.c_str(), strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
TargetFile::~TargetFile()
|
||||||
|
{
|
||||||
|
if ( f )
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
DBG_LOG(DBG_BROXYGEN, "Wrote out-of-date target '%s'", name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Target::Target(const string& arg_name, const string& arg_pattern)
|
||||||
|
: name(arg_name), pattern(arg_pattern), prefix()
|
||||||
|
{
|
||||||
|
size_t pos = pattern.find('*');
|
||||||
|
|
||||||
|
if ( pos == 0 || pos == string::npos )
|
||||||
|
return;
|
||||||
|
|
||||||
|
prefix = pattern.substr(0, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Target::MatchesPattern(Info* info) const
|
||||||
|
{
|
||||||
|
if ( pattern == "*" )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if ( prefix.empty() )
|
||||||
|
return info->Name() == pattern;
|
||||||
|
|
||||||
|
return ! strncmp(info->Name().c_str(), prefix.c_str(), prefix.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnalyzerTarget::DoFindDependencies(const std::vector<Info *>& infos)
|
||||||
|
{
|
||||||
|
// TODO: really should add to dependency list the tag type's ID and
|
||||||
|
// all bif items for matching analyzer plugins, but that's all dependent
|
||||||
|
// on the bro binary itself, so I'm cheating.
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnalyzerTarget::DoGenerate() const
|
||||||
|
{
|
||||||
|
if ( broxygen_mgr->IsUpToDate(Name(), vector<Info*>()) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( Pattern() != "*" )
|
||||||
|
reporter->InternalWarning("Broxygen only implements analyzer target"
|
||||||
|
" pattern '*'");
|
||||||
|
|
||||||
|
TargetFile file(Name());
|
||||||
|
CreateAnalyzerDoc(file.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtoAnalyzerTarget::DoCreateAnalyzerDoc(FILE* f) const
|
||||||
|
{
|
||||||
|
fprintf(f, "Protocol Analyzers\n");
|
||||||
|
fprintf(f, "==================\n\n");
|
||||||
|
fprintf(f, ".. contents::\n");
|
||||||
|
fprintf(f, " :depth: 2\n\n");
|
||||||
|
|
||||||
|
WriteAnalyzerTagDefn(f, "Analyzer");
|
||||||
|
|
||||||
|
plugin::Manager::plugin_list plugins = plugin_mgr->Plugins();
|
||||||
|
plugin::Manager::plugin_list::const_iterator it;
|
||||||
|
|
||||||
|
for ( it = plugins.begin(); it != plugins.end(); ++it )
|
||||||
|
{
|
||||||
|
if ( ! ComponentsMatch(*it, plugin::component::ANALYZER, true) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
write_plugin_section_heading(f, *it);
|
||||||
|
write_plugin_components(f, *it);
|
||||||
|
write_plugin_bif_items(f, *it, plugin::BifItem::CONSTANT,
|
||||||
|
"Options/Constants");
|
||||||
|
write_plugin_bif_items(f, *it, plugin::BifItem::GLOBAL, "Globals");
|
||||||
|
write_plugin_bif_items(f, *it, plugin::BifItem::TYPE, "Types");
|
||||||
|
write_plugin_bif_items(f, *it, plugin::BifItem::EVENT, "Events");
|
||||||
|
write_plugin_bif_items(f, *it, plugin::BifItem::FUNCTION, "Functions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAnalyzerTarget::DoCreateAnalyzerDoc(FILE* f) const
|
||||||
|
{
|
||||||
|
fprintf(f, "File Analyzers\n");
|
||||||
|
fprintf(f, "==============\n\n");
|
||||||
|
fprintf(f, ".. contents::\n");
|
||||||
|
fprintf(f, " :depth: 2\n\n");
|
||||||
|
|
||||||
|
WriteAnalyzerTagDefn(f, "Files");
|
||||||
|
|
||||||
|
plugin::Manager::plugin_list plugins = plugin_mgr->Plugins();
|
||||||
|
plugin::Manager::plugin_list::const_iterator it;
|
||||||
|
|
||||||
|
for ( it = plugins.begin(); it != plugins.end(); ++it )
|
||||||
|
{
|
||||||
|
if ( ! ComponentsMatch(*it, plugin::component::FILE_ANALYZER) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
write_plugin_section_heading(f, *it);
|
||||||
|
write_plugin_components(f, *it);
|
||||||
|
write_plugin_bif_items(f, *it, plugin::BifItem::CONSTANT,
|
||||||
|
"Options/Constants");
|
||||||
|
write_plugin_bif_items(f, *it, plugin::BifItem::GLOBAL, "Globals");
|
||||||
|
write_plugin_bif_items(f, *it, plugin::BifItem::TYPE, "Types");
|
||||||
|
write_plugin_bif_items(f, *it, plugin::BifItem::EVENT, "Events");
|
||||||
|
write_plugin_bif_items(f, *it, plugin::BifItem::FUNCTION, "Functions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackageTarget::DoFindDependencies(const vector<Info*>& infos)
|
||||||
|
{
|
||||||
|
pkg_deps = filter_matches<PackageInfo>(infos, this);
|
||||||
|
|
||||||
|
if ( pkg_deps.empty() )
|
||||||
|
reporter->FatalError("No match for Broxygen target '%s' pattern '%s'",
|
||||||
|
Name().c_str(), Pattern().c_str());
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < infos.size(); ++i )
|
||||||
|
{
|
||||||
|
ScriptInfo* script = dynamic_cast<ScriptInfo*>(infos[i]);
|
||||||
|
|
||||||
|
if ( ! script )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for ( size_t j = 0; j < pkg_deps.size(); ++j )
|
||||||
|
{
|
||||||
|
if ( strncmp(script->Name().c_str(), pkg_deps[j]->Name().c_str(),
|
||||||
|
pkg_deps[j]->Name().size()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DBG_LOG(DBG_BROXYGEN, "Script %s associated with package %s",
|
||||||
|
script->Name().c_str(), pkg_deps[j]->Name().c_str());
|
||||||
|
pkg_manifest[pkg_deps[j]].push_back(script);
|
||||||
|
script_deps.push_back(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackageTarget::DoGenerate() const
|
||||||
|
{
|
||||||
|
if ( broxygen_mgr->IsUpToDate(Name(), script_deps) &&
|
||||||
|
broxygen_mgr->IsUpToDate(Name(), pkg_deps) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
TargetFile file(Name());
|
||||||
|
|
||||||
|
fprintf(file.f, ":orphan:\n\n");
|
||||||
|
|
||||||
|
for ( manifest_t::const_iterator it = pkg_manifest.begin();
|
||||||
|
it != pkg_manifest.end(); ++it )
|
||||||
|
{
|
||||||
|
string header = fmt("Package: %s", it->first->Name().c_str());
|
||||||
|
header += "\n" + string(header.size(), '=');
|
||||||
|
|
||||||
|
fprintf(file.f, "%s\n\n", header.c_str());
|
||||||
|
|
||||||
|
vector<string> readme = it->first->GetReadme();
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < readme.size(); ++i )
|
||||||
|
fprintf(file.f, "%s\n", readme[i].c_str());
|
||||||
|
|
||||||
|
fprintf(file.f, "\n");
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < it->second.size(); ++i )
|
||||||
|
{
|
||||||
|
fprintf(file.f, ":doc:`/scripts/%s`\n",
|
||||||
|
it->second[i]->Name().c_str());
|
||||||
|
|
||||||
|
vector<string> cmnts = it->second[i]->GetComments();
|
||||||
|
|
||||||
|
for ( size_t j = 0; j < cmnts.size(); ++j )
|
||||||
|
fprintf(file.f, " %s\n", cmnts[j].c_str());
|
||||||
|
|
||||||
|
fprintf(file.f, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackageIndexTarget::DoFindDependencies(const vector<Info*>& infos)
|
||||||
|
{
|
||||||
|
pkg_deps = filter_matches<PackageInfo>(infos, this);
|
||||||
|
|
||||||
|
if ( pkg_deps.empty() )
|
||||||
|
reporter->FatalError("No match for Broxygen target '%s' pattern '%s'",
|
||||||
|
Name().c_str(), Pattern().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackageIndexTarget::DoGenerate() const
|
||||||
|
{
|
||||||
|
if ( broxygen_mgr->IsUpToDate(Name(), pkg_deps) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
TargetFile file(Name());
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < pkg_deps.size(); ++i )
|
||||||
|
fprintf(file.f, "%s\n", pkg_deps[i]->ReStructuredText().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptTarget::DoFindDependencies(const vector<Info*>& infos)
|
||||||
|
{
|
||||||
|
script_deps = filter_matches<ScriptInfo>(infos, this);
|
||||||
|
|
||||||
|
if ( script_deps.empty() )
|
||||||
|
reporter->FatalError("No match for Broxygen target '%s' pattern '%s'",
|
||||||
|
Name().c_str(), Pattern().c_str());
|
||||||
|
|
||||||
|
if ( ! IsDir() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < script_deps.size(); ++i )
|
||||||
|
{
|
||||||
|
if ( SafeBasename(script_deps[i]->Name()).result == PACKAGE_LOADER )
|
||||||
|
{
|
||||||
|
string pkg_dir = SafeDirname(script_deps[i]->Name()).result;
|
||||||
|
string target_file = Name() + pkg_dir + "/index.rst";
|
||||||
|
Target* t = new PackageTarget(target_file, pkg_dir);
|
||||||
|
t->FindDependencies(infos);
|
||||||
|
pkg_deps.push_back(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string> dir_contents_recursive(string dir)
|
||||||
|
{
|
||||||
|
vector<string> rval;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if ( stat(dir.c_str(), &st) < 0 && errno == ENOENT )
|
||||||
|
return rval;
|
||||||
|
|
||||||
|
while ( dir[dir.size() - 1] == '/' )
|
||||||
|
dir.erase(dir.size() - 1, 1);
|
||||||
|
|
||||||
|
char* dir_copy = copy_string(dir.c_str());
|
||||||
|
char** scan_path = new char*[2];
|
||||||
|
scan_path[0] = dir_copy;
|
||||||
|
scan_path[1] = 0;
|
||||||
|
|
||||||
|
FTS* fts = fts_open(scan_path, FTS_NOCHDIR, 0);
|
||||||
|
|
||||||
|
if ( ! fts )
|
||||||
|
{
|
||||||
|
reporter->Error("fts_open failure: %s", strerror(errno));
|
||||||
|
delete [] scan_path;
|
||||||
|
delete [] dir_copy;
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
FTSENT* n;
|
||||||
|
|
||||||
|
while ( (n = fts_read(fts)) )
|
||||||
|
{
|
||||||
|
if ( n->fts_info & FTS_F )
|
||||||
|
rval.push_back(n->fts_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( errno )
|
||||||
|
reporter->Error("fts_read failure: %s", strerror(errno));
|
||||||
|
|
||||||
|
if ( fts_close(fts) < 0 )
|
||||||
|
reporter->Error("fts_close failure: %s", strerror(errno));
|
||||||
|
|
||||||
|
delete [] scan_path;
|
||||||
|
delete [] dir_copy;
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptTarget::DoGenerate() const
|
||||||
|
{
|
||||||
|
if ( IsDir() )
|
||||||
|
{
|
||||||
|
// Target name is a dir, matching scripts are written within that dir
|
||||||
|
// with a dir tree that parallels the script's BROPATH location.
|
||||||
|
|
||||||
|
set<string> targets;
|
||||||
|
vector<string> dir_contents = dir_contents_recursive(Name());
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < script_deps.size(); ++i )
|
||||||
|
{
|
||||||
|
string target_filename = Name() + script_deps[i]->Name() + ".rst";
|
||||||
|
targets.insert(target_filename);
|
||||||
|
vector<ScriptInfo*> dep;
|
||||||
|
dep.push_back(script_deps[i]);
|
||||||
|
|
||||||
|
if ( broxygen_mgr->IsUpToDate(target_filename, dep) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
TargetFile file(target_filename);
|
||||||
|
|
||||||
|
fprintf(file.f, "%s\n", script_deps[i]->ReStructuredText().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < pkg_deps.size(); ++i )
|
||||||
|
{
|
||||||
|
targets.insert(pkg_deps[i]->Name());
|
||||||
|
pkg_deps[i]->Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < dir_contents.size(); ++i )
|
||||||
|
{
|
||||||
|
string f = dir_contents[i];
|
||||||
|
|
||||||
|
if ( targets.find(f) != targets.end() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( unlink(f.c_str()) < 0 )
|
||||||
|
reporter->Warning("Failed to unlink %s: %s", f.c_str(),
|
||||||
|
strerror(errno));
|
||||||
|
|
||||||
|
DBG_LOG(DBG_BROXYGEN, "Delete stale script file %s", f.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Target is a single file, all matching scripts get written there.
|
||||||
|
|
||||||
|
if ( broxygen_mgr->IsUpToDate(Name(), script_deps) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
TargetFile file(Name());
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < script_deps.size(); ++i )
|
||||||
|
fprintf(file.f, "%s\n", script_deps[i]->ReStructuredText().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptSummaryTarget::DoGenerate() const
|
||||||
|
{
|
||||||
|
if ( broxygen_mgr->IsUpToDate(Name(), script_deps) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
TargetFile file(Name());
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < script_deps.size(); ++i )
|
||||||
|
{
|
||||||
|
ScriptInfo* d = dynamic_cast<ScriptInfo*>(script_deps[i]);
|
||||||
|
|
||||||
|
if ( ! d )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fprintf(file.f, ":doc:`/scripts/%s`\n", d->Name().c_str());
|
||||||
|
|
||||||
|
vector<string> cmnts = d->GetComments();
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < cmnts.size(); ++i )
|
||||||
|
fprintf(file.f, " %s\n", cmnts[i].c_str());
|
||||||
|
|
||||||
|
fprintf(file.f, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptIndexTarget::DoGenerate() const
|
||||||
|
{
|
||||||
|
if ( broxygen_mgr->IsUpToDate(Name(), script_deps) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
TargetFile file(Name());
|
||||||
|
|
||||||
|
fprintf(file.f, ".. toctree::\n");
|
||||||
|
fprintf(file.f, " :maxdepth: 1\n\n");
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < script_deps.size(); ++i )
|
||||||
|
{
|
||||||
|
ScriptInfo* d = dynamic_cast<ScriptInfo*>(script_deps[i]);
|
||||||
|
|
||||||
|
if ( ! d )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fprintf(file.f, " %s </scripts/%s>\n", d->Name().c_str(),
|
||||||
|
d->Name().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IdentifierTarget::DoFindDependencies(const vector<Info*>& infos)
|
||||||
|
{
|
||||||
|
id_deps = filter_matches<IdentifierInfo>(infos, this);
|
||||||
|
|
||||||
|
if ( id_deps.empty() )
|
||||||
|
reporter->FatalError("No match for Broxygen target '%s' pattern '%s'",
|
||||||
|
Name().c_str(), Pattern().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void IdentifierTarget::DoGenerate() const
|
||||||
|
{
|
||||||
|
if ( broxygen_mgr->IsUpToDate(Name(), id_deps) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
TargetFile file(Name());
|
||||||
|
|
||||||
|
for ( size_t i = 0; i < id_deps.size(); ++i )
|
||||||
|
fprintf(file.f, "%s\n\n", id_deps[i]->ReStructuredText().c_str());
|
||||||
|
}
|
387
src/broxygen/Target.h
Normal file
387
src/broxygen/Target.h
Normal file
|
@ -0,0 +1,387 @@
|
||||||
|
#ifndef BROXYGEN_TARGET_H
|
||||||
|
#define BROXYGEN_TARGET_H
|
||||||
|
|
||||||
|
#include "Info.h"
|
||||||
|
#include "PackageInfo.h"
|
||||||
|
#include "ScriptInfo.h"
|
||||||
|
#include "IdentifierInfo.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
namespace broxygen {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to create files in arbitrary file paths and automatically
|
||||||
|
* close it on destruction.
|
||||||
|
*/
|
||||||
|
struct TargetFile {
|
||||||
|
/**
|
||||||
|
* Open a file.
|
||||||
|
* @param arg_name Path to a file to create. It's a fatal error if
|
||||||
|
* creating it fails. Creating it will also create any intermediate
|
||||||
|
* directories that don't already exist.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TargetFile(const std::string& arg_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the file.
|
||||||
|
*/
|
||||||
|
~TargetFile();
|
||||||
|
|
||||||
|
std::string name; /**< File name. */
|
||||||
|
FILE* f; /**< File stream. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Broxygen target abstract base class. A target is generally any portion of
|
||||||
|
* documentation that Bro can build. It's identified by a type (e.g. script,
|
||||||
|
* identifier, package), a pattern (e.g. "example.bro", "HTTP::Info"), and
|
||||||
|
* a path to an output file.
|
||||||
|
*/
|
||||||
|
class Target {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
* @param arg_name output file name of the target.
|
||||||
|
* @param arg_pattern pattern of info objects the target depends upon. Only
|
||||||
|
* exact string and simple prefix matching is currently allowed.
|
||||||
|
*/
|
||||||
|
Target(const std::string& arg_name, const std::string& arg_pattern);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dtor.
|
||||||
|
*/
|
||||||
|
virtual ~Target()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter out any dependency information from a set of all known info.
|
||||||
|
* @param infos All known info objects.
|
||||||
|
*/
|
||||||
|
void FindDependencies(const std::vector<Info*>& infos)
|
||||||
|
{ DoFindDependencies(infos); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the target by generating its output file. Implementations may
|
||||||
|
* not always write to the output file if they determine an existing
|
||||||
|
* version is already up-to-date.
|
||||||
|
*/
|
||||||
|
void Generate() const
|
||||||
|
{ DoGenerate(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a particular info object matches the target pattern.
|
||||||
|
* Currently only exact string and simple prefix matching patterns are
|
||||||
|
* used. E.g. for prefix matching "HTTP::*" or "base/protocols/http*".
|
||||||
|
* @param info An info object for some thing that is documentable.
|
||||||
|
* @return true if it matches, else false.
|
||||||
|
*/
|
||||||
|
bool MatchesPattern(Info* info) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The output file name of the target.
|
||||||
|
*/
|
||||||
|
std::string Name() const
|
||||||
|
{ return name; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The pattern string of the target.
|
||||||
|
*/
|
||||||
|
std::string Pattern() const
|
||||||
|
{ return pattern; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
virtual void DoFindDependencies(const std::vector<Info*>& infos) = 0;
|
||||||
|
|
||||||
|
virtual void DoGenerate() const = 0;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
std::string pattern;
|
||||||
|
std::string prefix;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
static Target* create_target(const std::string& name,
|
||||||
|
const std::string& pattern)
|
||||||
|
{
|
||||||
|
return new T(name, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for creating Target instances.
|
||||||
|
*/
|
||||||
|
class TargetFactory {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new target type.
|
||||||
|
* @param type_name The target type name as it will appear in Broxygen
|
||||||
|
* config files.
|
||||||
|
*/
|
||||||
|
template<class T>
|
||||||
|
void Register(const std::string& type_name)
|
||||||
|
{
|
||||||
|
target_creators[type_name] = &create_target<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate a target.
|
||||||
|
* @param type_name The target type name as it appears in Broxygen config
|
||||||
|
* files.
|
||||||
|
* @param name The output file name of the target.
|
||||||
|
* @param pattern The dependency pattern of the target.
|
||||||
|
* @return A new target instance or a pointer if \a type_name is not
|
||||||
|
* registered.
|
||||||
|
*/
|
||||||
|
Target* Create(const std::string& type_name,
|
||||||
|
const std::string& name, const std::string& pattern)
|
||||||
|
{
|
||||||
|
target_creator_map::const_iterator it = target_creators.find(type_name);
|
||||||
|
|
||||||
|
if ( it == target_creators.end() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return it->second(name, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
typedef Target* (*TargetFactoryFn)(const std::string& name,
|
||||||
|
const std::string& pattern);
|
||||||
|
typedef std::map<std::string, TargetFactoryFn> target_creator_map;
|
||||||
|
target_creator_map target_creators;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target to build analyzer documentation.
|
||||||
|
*/
|
||||||
|
class AnalyzerTarget : public Target {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes out plugin index documentation for all analyzer plugins.
|
||||||
|
* @param f an open file stream to write docs into.
|
||||||
|
*/
|
||||||
|
void CreateAnalyzerDoc(FILE* f) const
|
||||||
|
{ return DoCreateAnalyzerDoc(f); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
typedef void (*doc_creator_fn)(FILE*);
|
||||||
|
|
||||||
|
AnalyzerTarget(const std::string& name, const std::string& pattern)
|
||||||
|
: Target(name, pattern)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void DoFindDependencies(const std::vector<Info*>& infos);
|
||||||
|
|
||||||
|
void DoGenerate() const;
|
||||||
|
|
||||||
|
virtual void DoCreateAnalyzerDoc(FILE* f) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target to build protocol analyzer documentation.
|
||||||
|
*/
|
||||||
|
class ProtoAnalyzerTarget : public AnalyzerTarget {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
* @param name Output file name.
|
||||||
|
* @param pattern Dependency pattern.
|
||||||
|
*/
|
||||||
|
ProtoAnalyzerTarget(const std::string& name, const std::string& pattern)
|
||||||
|
: AnalyzerTarget(name, pattern)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void DoCreateAnalyzerDoc(FILE* f) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target to build file analyzer documentation.
|
||||||
|
*/
|
||||||
|
class FileAnalyzerTarget : public AnalyzerTarget {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
* @param name Output file name.
|
||||||
|
* @param pattern Dependency pattern.
|
||||||
|
*/
|
||||||
|
FileAnalyzerTarget(const std::string& name, const std::string& pattern)
|
||||||
|
: AnalyzerTarget(name, pattern)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void DoCreateAnalyzerDoc(FILE* f) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target to build package documentation.
|
||||||
|
*/
|
||||||
|
class PackageTarget : public Target {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
* @param name Output file name.
|
||||||
|
* @param pattern Dependency pattern.
|
||||||
|
*/
|
||||||
|
PackageTarget(const std::string& name, const std::string& pattern)
|
||||||
|
: Target(name, pattern), pkg_deps(), script_deps(), pkg_manifest()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void DoFindDependencies(const std::vector<Info*>& infos);
|
||||||
|
|
||||||
|
void DoGenerate() const;
|
||||||
|
|
||||||
|
std::vector<PackageInfo*> pkg_deps;
|
||||||
|
std::vector<ScriptInfo*> script_deps;
|
||||||
|
typedef std::map<PackageInfo*,std::vector<ScriptInfo*> > manifest_t;
|
||||||
|
manifest_t pkg_manifest;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target to build package index documentation.
|
||||||
|
*/
|
||||||
|
class PackageIndexTarget : public Target {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
* @param name Output file name.
|
||||||
|
* @param pattern Dependency pattern.
|
||||||
|
*/
|
||||||
|
PackageIndexTarget(const std::string& name, const std::string& pattern)
|
||||||
|
: Target(name, pattern), pkg_deps()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void DoFindDependencies(const std::vector<Info*>& infos);
|
||||||
|
|
||||||
|
void DoGenerate() const;
|
||||||
|
|
||||||
|
std::vector<PackageInfo*> pkg_deps;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target to build script documentation.
|
||||||
|
*/
|
||||||
|
class ScriptTarget : public Target {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
* @param name Output file name or directory. If it's a directory,
|
||||||
|
* then one document for each script that matches the pattern is written to
|
||||||
|
* the directory in a directory structure which mirrors the script's path
|
||||||
|
* relative to a component in BROPATH.
|
||||||
|
* @param pattern Dependency pattern.
|
||||||
|
*/
|
||||||
|
ScriptTarget(const std::string& name, const std::string& pattern)
|
||||||
|
: Target(name, pattern), script_deps()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~ScriptTarget()
|
||||||
|
{ for ( size_t i = 0; i < pkg_deps.size(); ++i ) delete pkg_deps[i]; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
std::vector<ScriptInfo*> script_deps;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void DoFindDependencies(const std::vector<Info*>& infos);
|
||||||
|
|
||||||
|
void DoGenerate() const;
|
||||||
|
|
||||||
|
bool IsDir() const
|
||||||
|
{ return Name()[Name().size() - 1] == '/'; }
|
||||||
|
|
||||||
|
std::vector<Target*> pkg_deps;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target to build script summary documentation.
|
||||||
|
*/
|
||||||
|
class ScriptSummaryTarget : public ScriptTarget {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
* @param name Output file name.
|
||||||
|
* @param pattern Dependency pattern.
|
||||||
|
*/
|
||||||
|
ScriptSummaryTarget(const std::string& name, const std::string& pattern)
|
||||||
|
: ScriptTarget(name, pattern)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void DoGenerate() const /* override */;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target to build script index documentation.
|
||||||
|
*/
|
||||||
|
class ScriptIndexTarget : public ScriptTarget {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
* @param name Output file name.
|
||||||
|
* @param pattern Dependency pattern.
|
||||||
|
*/
|
||||||
|
ScriptIndexTarget(const std::string& name, const std::string& pattern)
|
||||||
|
: ScriptTarget(name, pattern)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void DoGenerate() const /* override */;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target to build identifier documentation.
|
||||||
|
*/
|
||||||
|
class IdentifierTarget : public Target {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
* @param name Output file name.
|
||||||
|
* @param pattern Dependency pattern.
|
||||||
|
*/
|
||||||
|
IdentifierTarget(const std::string& name, const std::string& pattern)
|
||||||
|
: Target(name, pattern), id_deps()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void DoFindDependencies(const std::vector<Info*>& infos);
|
||||||
|
|
||||||
|
void DoGenerate() const;
|
||||||
|
|
||||||
|
std::vector<IdentifierInfo*> id_deps;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace broxygen
|
||||||
|
|
||||||
|
#endif
|
|
@ -10,12 +10,17 @@ static StringVal* comments_to_val(const vector<string>& comments)
|
||||||
}
|
}
|
||||||
%%}
|
%%}
|
||||||
|
|
||||||
# TODO: documentation
|
## Retrieve the Broxygen-style comments (``##``) associated with an identifier
|
||||||
|
## (e.g. a variable or type).
|
||||||
|
##
|
||||||
|
## name: a script-level identifier for which to retrieve comments.
|
||||||
|
##
|
||||||
|
## Returns: comments associated with *name*. If *name* is not a known
|
||||||
|
## identifier, an empty string is returned.
|
||||||
function get_identifier_comments%(name: string%): string
|
function get_identifier_comments%(name: string%): string
|
||||||
%{
|
%{
|
||||||
using namespace broxygen;
|
using namespace broxygen;
|
||||||
IdentifierDocument* d = broxygen_mgr->GetIdentifierDoc(name->CheckString());
|
IdentifierInfo* d = broxygen_mgr->GetIdentifierInfo(name->CheckString());
|
||||||
|
|
||||||
if ( ! d )
|
if ( ! d )
|
||||||
return new StringVal("");
|
return new StringVal("");
|
||||||
|
@ -23,10 +28,19 @@ function get_identifier_comments%(name: string%): string
|
||||||
return comments_to_val(d->GetComments());
|
return comments_to_val(d->GetComments());
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
## Retrieve the Broxygen-style summary comments (``##!``) associated with
|
||||||
|
## a Bro script.
|
||||||
|
##
|
||||||
|
## name: the name of a Bro script. It must be a relative path to where
|
||||||
|
## it is located within a particular component of BROPATH and use
|
||||||
|
## the same file name extension/suffix as the actual file (e.g. ".bro").
|
||||||
|
##
|
||||||
|
## Returns: summary comments associated with script with *name*. If
|
||||||
|
## *name* is not a known script, an empty string is returned.
|
||||||
function get_script_comments%(name: string%): string
|
function get_script_comments%(name: string%): string
|
||||||
%{
|
%{
|
||||||
using namespace broxygen;
|
using namespace broxygen;
|
||||||
ScriptDocument* d = broxygen_mgr->GetScriptDoc(name->CheckString());
|
ScriptInfo* d = broxygen_mgr->GetScriptInfo(name->CheckString());
|
||||||
|
|
||||||
if ( ! d )
|
if ( ! d )
|
||||||
return new StringVal("");
|
return new StringVal("");
|
||||||
|
@ -34,10 +48,17 @@ function get_script_comments%(name: string%): string
|
||||||
return comments_to_val(d->GetComments());
|
return comments_to_val(d->GetComments());
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
## Retrieve the contents of a Bro script package's README file.
|
||||||
|
##
|
||||||
|
## name: the name of a Bro script package. It must be a relative path
|
||||||
|
## to where it is located within a particular component of BROPATH.
|
||||||
|
##
|
||||||
|
## Returns: contents of the package's README file. If *name* is not a known
|
||||||
|
## package, an empty string is returned.
|
||||||
function get_package_readme%(name: string%): string
|
function get_package_readme%(name: string%): string
|
||||||
%{
|
%{
|
||||||
using namespace broxygen;
|
using namespace broxygen;
|
||||||
PackageDocument* d = broxygen_mgr->GetPackageDoc(name->CheckString());
|
PackageInfo* d = broxygen_mgr->GetPackageInfo(name->CheckString());
|
||||||
|
|
||||||
if ( ! d )
|
if ( ! d )
|
||||||
return new StringVal("");
|
return new StringVal("");
|
||||||
|
@ -45,6 +66,14 @@ function get_package_readme%(name: string%): string
|
||||||
return comments_to_val(d->GetReadme());
|
return comments_to_val(d->GetReadme());
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
## Retrieve the Broxygen-style comments (``##``) associated with a record field.
|
||||||
|
##
|
||||||
|
## name: the name of a record type and a field within it formatted like
|
||||||
|
## a typical record field access: "<record_type>$<field>".
|
||||||
|
##
|
||||||
|
## Returns: comments associated with the record field. If *name* does
|
||||||
|
## not point to a known record type or a known field within a record
|
||||||
|
## type, an empty string is returned.
|
||||||
function get_record_field_comments%(name: string%): string
|
function get_record_field_comments%(name: string%): string
|
||||||
%{
|
%{
|
||||||
using namespace broxygen;
|
using namespace broxygen;
|
||||||
|
@ -56,7 +85,7 @@ function get_record_field_comments%(name: string%): string
|
||||||
|
|
||||||
string id = accessor.substr(0, i);
|
string id = accessor.substr(0, i);
|
||||||
|
|
||||||
IdentifierDocument* d = broxygen_mgr->GetIdentifierDoc(id);
|
IdentifierInfo* d = broxygen_mgr->GetIdentifierInfo(id);
|
||||||
|
|
||||||
if ( ! d )
|
if ( ! d )
|
||||||
return new StringVal("");
|
return new StringVal("");
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include "Reporter.h"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
using namespace broxygen;
|
using namespace broxygen;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -68,3 +72,55 @@ bool broxygen::prettify_params(string& s)
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool broxygen::is_public_api(const ID* id)
|
||||||
|
{
|
||||||
|
return (id->Scope() == SCOPE_GLOBAL) ||
|
||||||
|
(id->Scope() == SCOPE_MODULE && id->IsExport());
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t broxygen::get_mtime(const string& filename)
|
||||||
|
{
|
||||||
|
struct stat s;
|
||||||
|
|
||||||
|
if ( stat(filename.c_str(), &s) < 0 )
|
||||||
|
reporter->InternalError("Broxygen failed to stat file '%s': %s",
|
||||||
|
filename.c_str(), strerror(errno));
|
||||||
|
|
||||||
|
return s.st_mtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
string broxygen::make_heading(const string& heading, char underline)
|
||||||
|
{
|
||||||
|
return heading + "\n" + string(heading.size(), underline) + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t broxygen::end_of_first_sentence(const string& s)
|
||||||
|
{
|
||||||
|
size_t rval = 0;
|
||||||
|
|
||||||
|
while ( (rval = s.find_first_of('.', rval)) != string::npos )
|
||||||
|
{
|
||||||
|
if ( rval == s.size() - 1 )
|
||||||
|
// Period is at end of string.
|
||||||
|
return rval;
|
||||||
|
|
||||||
|
if ( isspace(s[rval + 1]) )
|
||||||
|
// Period has a space after it.
|
||||||
|
return rval;
|
||||||
|
|
||||||
|
// Period has some non-space character after it, keep looking.
|
||||||
|
++rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool broxygen::is_all_whitespace(const string& s)
|
||||||
|
{
|
||||||
|
for ( size_t i = 0; i < s.size(); ++i )
|
||||||
|
if ( ! isspace(s[i]) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,59 @@
|
||||||
#ifndef BROXYGEN_UTILS_H
|
#ifndef BROXYGEN_UTILS_H
|
||||||
#define BROXYGEN_UTILS_H
|
#define BROXYGEN_UTILS_H
|
||||||
|
|
||||||
|
#include "ID.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace broxygen {
|
namespace broxygen {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform content of a Broxygen comment which may contain function
|
||||||
|
* parameter or return value documentation to a prettier reST format.
|
||||||
|
* @param s Content from a Broxygen comment to transform. "id: ..." and
|
||||||
|
* "Returns: ..." change to ":param id: ..." and ":returns: ...".
|
||||||
|
* @return Whether any content in \a s was transformed.
|
||||||
|
*/
|
||||||
bool prettify_params(std::string& s);
|
bool prettify_params(std::string& s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether an identifier is part of the "public" interface.
|
||||||
|
* @param id A script-level identifier.
|
||||||
|
* @return true if the ID is in the global scope or if it's exported in to
|
||||||
|
* any modules namespace.
|
||||||
|
*/
|
||||||
|
bool is_public_api(const ID* id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the modification time of a file or abort if there's an error.
|
||||||
|
* @param filename Path to a file.
|
||||||
|
* @return The modification time of \a filename via stat(2).
|
||||||
|
*/
|
||||||
|
time_t get_mtime(const std::string& filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a reST-style heading.
|
||||||
|
* @param heading Content of the heading.
|
||||||
|
* @param underline Character in which to underline heading content.
|
||||||
|
* @return underlined heading string.
|
||||||
|
*/
|
||||||
|
std::string make_heading(const std::string& heading, char underline);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the position of the end of the first sentence in a string.
|
||||||
|
* @param s Any string.
|
||||||
|
* @return The position which looks like the end of the first sentence in
|
||||||
|
* \a s or 0 if no such position is found.
|
||||||
|
*/
|
||||||
|
size_t end_of_first_sentence(const std::string& s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a string is entirely white space.
|
||||||
|
* @param s Any string.
|
||||||
|
* @return True if \a s is nothing but white space, else false.
|
||||||
|
*/
|
||||||
|
bool is_all_whitespace(const std::string& s);
|
||||||
|
|
||||||
} // namespace broxygen
|
} // namespace broxygen
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -50,7 +50,6 @@ extern "C" void OPENSSL_add_all_algorithms_conf(void);
|
||||||
#include "PersistenceSerializer.h"
|
#include "PersistenceSerializer.h"
|
||||||
#include "EventRegistry.h"
|
#include "EventRegistry.h"
|
||||||
#include "Stats.h"
|
#include "Stats.h"
|
||||||
#include "BroDoc.h"
|
|
||||||
#include "Brofiler.h"
|
#include "Brofiler.h"
|
||||||
|
|
||||||
#include "threading/Manager.h"
|
#include "threading/Manager.h"
|
||||||
|
|
|
@ -564,7 +564,7 @@ static int load_files(const char* orig_file)
|
||||||
else
|
else
|
||||||
file_stack.append(new FileInfo);
|
file_stack.append(new FileInfo);
|
||||||
|
|
||||||
broxygen_mgr->File(file_path);
|
broxygen_mgr->Script(file_path);
|
||||||
|
|
||||||
// "orig_file", could be an alias for yytext, which is ephemeral
|
// "orig_file", could be an alias for yytext, which is ephemeral
|
||||||
// and will be zapped after the yy_switch_to_buffer() below.
|
// and will be zapped after the yy_switch_to_buffer() below.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue