Bro script documentation framework checkpoint

* New bro runtime options: -Z or --doc-scripts enables documentation mode
* New BroDoc, BroBifDoc, and BroDocObj interfaces to support script
  documentation
* Modifications to the bro scanner (scan.l) to get it to keep track of
  which script is being scanned/parsed and which document is being generated
* Modifications to scan.l and the bro parser (parse.y) to produce/consume
  script comments denoted with "##"
* Documentation is currently generated for the following
** Script author
** Script summary
** @load's
** capture_filters
** modules (namespaces)

Most of the remaining framework/infrastructure work should be in extracting
the interesting BroObj objects as the parser sees them and better formatting
the reST documents.
This commit is contained in:
Jon Siwek 2011-02-25 15:30:18 -06:00
parent 4b77164e04
commit 30209b56bb
10 changed files with 644 additions and 3 deletions

16
src/BroBifDoc.cc Normal file
View file

@ -0,0 +1,16 @@
#include <cstdio>
#include <string>
#include <list>
#include "BroDoc.h"
#include "BroBifDoc.h"
BroBifDoc::BroBifDoc(const std::string& sourcename) : BroDoc(sourcename)
{
}
//TODO: this needs to do something different than parent class's version
void BroBifDoc::WriteDocFile() const
{
BroDoc::WriteDocFile();
}

21
src/BroBifDoc.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef brobifdoc_h
#define brobifdoc_h
#include <cstdio>
#include <string>
#include <list>
#include "BroDoc.h"
class BroBifDoc : public BroDoc {
public:
BroBifDoc(const std::string& sourcename);
void WriteDocFile() const;
protected:
private:
};
#endif

163
src/BroDoc.cc Normal file
View file

@ -0,0 +1,163 @@
#include <cstdio>
#include <cstdarg>
#include <string>
#include <list>
#include "BroDoc.h"
#include "BroDocObj.h"
BroDoc::BroDoc(const std::string& sourcename)
{
fprintf(stdout, "Documenting source: %s\n", sourcename.c_str());
source_filename = sourcename.substr(sourcename.find_last_of('/') + 1);
size_t ext_pos = source_filename.find_last_of('.');
std::string ext = source_filename.substr(ext_pos + 1);
if ( ext_pos == std::string::npos || ext != "bro" )
{
if ( source_filename != "bro.init" )
{
fprintf(stderr,
"Warning: documenting file without .bro extension: %s\n",
sourcename.c_str());
}
else
{
// Force the reST documentation file to be "bro.init.rst"
ext_pos = std::string::npos;
}
}
reST_filename = source_filename.substr(0, ext_pos);
reST_filename += ".rst";
reST_file = fopen(reST_filename.c_str(), "w");
if ( ! reST_file )
fprintf(stderr, "Failed to open %s", reST_filename.c_str());
else
fprintf(stdout, "Created reST document: %s\n", reST_filename.c_str());
}
BroDoc::~BroDoc()
{
if ( reST_file )
if ( fclose( reST_file ) )
fprintf(stderr, "Failed to close %s", reST_filename.c_str());
FreeBroDocObjPtrList(options);
FreeBroDocObjPtrList(state_vars);
FreeBroDocObjPtrList(types);
FreeBroDocObjPtrList(notices);
FreeBroDocObjPtrList(events);
FreeBroDocObjPtrList(functions);
}
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);
}
void BroDoc::WriteDocFile() const
{
WriteToDoc("%s\n", source_filename.c_str());
for ( size_t i = 0; i < source_filename.length(); ++i )
WriteToDoc("=");
WriteToDoc("\n\n");
WriteSectionHeading("Summary", '-');
WriteStringList("%s\n", "%s\n\n", summary);
WriteToDoc(":Author: %s\n", author_name.c_str());
WriteToDoc(":Namespaces: ");
WriteStringList("`%s`, ", "`%s`\n", modules);
WriteToDoc(":Imports:\n");
WriteStringList(" :bro:script: `%s`\n",
" :bro:script: `%s`\n\n", imports);
WriteSectionHeading("Public Interface", '-');
WriteBroDocObjList(options, true, "Options", '~');
WriteBroDocObjList(state_vars, true, "State Variables", '~');
WriteBroDocObjList(types, true, "Types", '~');
WriteBroDocObjList(notices, true, "Notices", '~');
WriteBroDocObjList(events, true, "Events", '~');
WriteBroDocObjList(functions, true, "Functions", '~');
WriteSectionHeading("Packet Filter", '-');
WriteToDoc("%s\n", packet_filter.c_str());
WriteSectionHeading("Private Interface", '-');
WriteBroDocObjList(options, false, "Options", '~');
WriteBroDocObjList(state_vars, false, "State Variables", '~');
WriteBroDocObjList(types, false, "Types", '~');
WriteBroDocObjList(notices, false, "Notices", '~');
WriteBroDocObjList(events, false, "Events", '~');
WriteBroDocObjList(functions, false, "Functions", '~');
}
void BroDoc::WriteStringList(const char* format,
const char* last_format,
const std::list<std::string>& l) const
{
if ( l.empty() ) 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(format, it->c_str());
WriteToDoc(last_format, last->c_str());
}
void BroDoc::WriteBroDocObjList(const std::list<const BroDocObj*>& l,
bool exportCond,
const char* heading,
char underline) const
{
WriteSectionHeading(heading, underline);
std::list<const BroDocObj*>::const_iterator it;
for ( it = l.begin(); it != l.end(); ++it )
{
if ( exportCond )
{
// write out only those in an export section
if ( (*it)->IsPublicAPI() )
(*it)->WriteReST(reST_file);
}
else
{
// write out only those that have comments and are not exported
if ( !(*it)->IsPublicAPI() && (*it)->HasDocumentation() )
(*it)->WriteReST(reST_file);
}
}
}
void BroDoc::WriteToDoc(const char* format, ...) const
{
va_list argp;
va_start(argp, format);
vfprintf(reST_file, format, argp);
va_end(argp);
}
void BroDoc::WriteSectionHeading(const char* heading, char underline) const
{
WriteToDoc("%s\n", heading);
size_t len = strlen(heading);
for ( size_t i = 0; i < len; ++i )
WriteToDoc("%c", underline);
WriteToDoc("\n");
}
void BroDoc::FreeBroDocObjPtrList(std::list<const BroDocObj*>& l)
{
std::list<const BroDocObj*>::iterator it;
for ( it = l.begin(); it != l.end(); ++it )
delete *it;
l.clear();
}

183
src/BroDoc.h Normal file
View file

@ -0,0 +1,183 @@
#ifndef brodoc_h
#define brodoc_h
#include <cstdio>
#include <cstdarg>
#include <string>
#include <list>
#include "BroDocObj.h"
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.
* @param sourcename The name of the Bro script for which to generate
* documentation. May contain a path.
*/
BroDoc(const std::string& sourcename);
/**
* BroDoc destructor
* Closes the file that was opened by the constructor and frees up
* memory taken by BroDocObj objects.
*/
~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). Things in a script's export section make it
* into the reST output regardless of whether they have ## comments
* but things outside the export section are only output into the reST
* if they have ## comments.
*/
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.
* This should be called whenever the scanner sees an @load.
* @param s The name of the imported script.
*/
void AddImport(const std::string& s) { imports.push_back(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);
/**
* Sets the author of the script.
* The scanner should call this when it sees "## Author: ..."
* @param s The name, email, etc. of the script author(s). Must be
* all on one line.
*/
void SetAuthor(const std::string& s) { author_name = s; }
//TODO: document these better
// the rest of these need to be called from the parser
void AddOption(const BroDocObj* o) { options.push_back(o); }
void AddStateVar(const BroDocObj* o) { state_vars.push_back(o); }
void AddType(const BroDocObj* o) { types.push_back(o); }
void AddNotice(const BroDocObj* o) { notices.push_back(o); }
void AddEvent(const BroDocObj* o) { events.push_back(o); }
void AddFunction(const BroDocObj* o) { functions.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(); }
protected:
FILE* reST_file;
std::string reST_filename;
std::string source_filename;
std::string author_name;
std::string packet_filter;
std::list<std::string> ls;
std::list<std::string> modules;
std::list<std::string> summary;
std::list<std::string> imports;
std::list<const BroDocObj*> options; // identifiers with &redef attr
std::list<const BroDocObj*> state_vars; // identifiers without &redef?
std::list<const BroDocObj*> types;
std::list<const BroDocObj*> notices;
std::list<const BroDocObj*> events;
std::list<const BroDocObj*> functions;
/**
* Writes out a list of strings to the reST document.
* @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
*/
void WriteStringList(const char* format,
const char* last_format,
const std::list<std::string>& l) const;
/**
* @see WriteStringList(const char*, const char*,
const std::list<std::string>&>)
*/
void WriteStringList(const char* format,
const std::list<std::string>& l) const
{ WriteStringList(format, format, l); }
/**
* Writes out a list of BroDocObj objects to the reST document
* @param l A list of BroDocObj pointers
* @param exportCond If true, filter out objects that are not in an
* export section. If false, filter out those that are in
* an export section.
* @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.
*/
void WriteBroDocObjList(const std::list<const BroDocObj*>& l,
bool exportCond,
const char* heading,
char underline) const;
/**
* A wrapper to fprintf() that always uses the reST document
* for the FILE* argument.
* @param format A printf style format string.
*/
void WriteToDoc(const char* format, ...) const;
/**
* Writes out a reST section heading
* @param heading The title of the heading to create
* @param underline The character to use to underline the section title
within the reST document
*/
void WriteSectionHeading(const char* heading, char underline) const;
private:
/**
* Frees memory allocated to BroDocObj's objects in a given list.
* @param a reference to a list of BroDocObj pointers
*/
void FreeBroDocObjPtrList(std::list<const BroDocObj*>& l);
};
#endif

35
src/BroDocObj.cc Normal file
View file

@ -0,0 +1,35 @@
#include <cstdio>
#include <string>
#include <list>
#include "Obj.h"
#include "BroDocObj.h"
BroDocObj::BroDocObj(const BroObj* obj,
std::list<std::string>*& reST,
bool exported)
{
broObj = obj;
isExported = exported;
reST_doc_strings = reST;
reST = 0;
}
BroDocObj::~BroDocObj()
{
delete reST_doc_strings;
}
void BroDocObj::WriteReST(FILE* file) const
{
if ( reST_doc_strings )
{
std::list<std::string>::const_iterator it;
for ( it = reST_doc_strings->begin();
it != reST_doc_strings->end(); ++it)
fprintf(file, "%s\n", it->c_str());
}
ODesc desc;
broObj->Describe(&desc);
fprintf(file, "%s\n", desc.Description());
}

72
src/BroDocObj.h Normal file
View file

@ -0,0 +1,72 @@
#ifndef brodocobj_h
#define brodocobj_h
#include <cstdio>
#include <string>
#include <list>
#include "Obj.h"
class BroDocObj {
public:
/**
* BroDocObj constructor
* @param obj a pointer to a BroObj that is to be documented
* @param reST a reference to a pointer of a list of strings that
represent the reST documentation for the BroObj. The pointer
will be set to 0 after this constructor finishes.
* @param exported whether the BroObj is declared in an export section
*/
BroDocObj(const BroObj* obj, std::list<std::string>*& reST, bool exported);
/**
* BroDocObj destructor
* Deallocates the memory associated with the list of reST strings
*/
~BroDocObj();
/**
* writes the reST representation of this object which includes
* 1) any of the "##" comments (stored internally in reST_doc_string)
* To make things easy, I think we should assume that the documenter
* writes their comments such that anything after ## is valid reST
* so that at parse time the ## is just stripped and the remainder
* is scheduled to be inserted as-is into the reST.
* TODO: prepare for some kind of filtering mechanism that transforms
* the reST as written into new reST before being written out.
* This allows for additional custom markup or macros when writing
* pure reST might be inconvenient.
* 2) a reST friendly description of the BroObj
* Could be implemented similar to the virtual BroObj::Describe(ODesc)
* E.g. all subclasses will now need to implement a reSTDescribe(ODesc)
* so that they can describe themselves in terms of the custom reST
* directives/roles that we'll later teach to Sphinx via a "bro domain".
* ID's should be able to implement the reSTDescribe(ODesc) function
* such that their namespace and attributes are output as well.
* @param The (already opened) file to write the reST to.
*/
void WriteReST(FILE* file) const;
/**
* Check whether this documentation is part of the public API
* (The BroObj declaration was while in an export section).
* @return true if the BroObj was declared in an export section, else false
*/
bool IsPublicAPI() const { return isExported; }
/**
* Return whether this object has documentation (## comments)
* @return true if the BroObj has comments associated with it
*/
bool HasDocumentation() const { return reST_doc_strings &&
reST_doc_strings->size() > 0; }
protected:
std::list<std::string>* reST_doc_strings;
const BroObj* broObj;
bool isExported;
private:
};
#endif

View file

@ -247,6 +247,9 @@ set(bro_SRCS
BitTorrent.cc BitTorrent.cc
BitTorrentTracker.cc BitTorrentTracker.cc
BPF_Program.cc BPF_Program.cc
BroBifDoc.cc
BroDoc.cc
BroDocObj.cc
BroString.cc BroString.cc
CCL.cc CCL.cc
ChunkedIO.cc ChunkedIO.cc

View file

@ -91,6 +91,7 @@ int optimize = 0;
int do_notice_analysis = 0; int do_notice_analysis = 0;
int rule_bench = 0; int rule_bench = 0;
int print_loaded_scripts = 0; int print_loaded_scripts = 0;
int generate_documentation = 0;
SecondaryPath* secondary_path = 0; SecondaryPath* secondary_path = 0;
ConnCompressor* conn_compressor = 0; ConnCompressor* conn_compressor = 0;
extern char version[]; extern char version[];
@ -145,6 +146,7 @@ void usage()
fprintf(stderr, " -h|--help|-? | command line help\n"); fprintf(stderr, " -h|--help|-? | command line help\n");
fprintf(stderr, " -i|--iface <interface> | read from given interface\n"); fprintf(stderr, " -i|--iface <interface> | read from given interface\n");
fprintf(stderr, " -l|--print-scripts | print all loaded scripts\n"); fprintf(stderr, " -l|--print-scripts | print all loaded scripts\n");
fprintf(stderr, " -Z|--doc-scripts | generate documentation for all loaded scripts\n");
fprintf(stderr, " -p|--prefix <prefix> | add given prefix to policy file resolution\n"); fprintf(stderr, " -p|--prefix <prefix> | add given prefix to policy file resolution\n");
fprintf(stderr, " -r|--readfile <readfile> | read from given tcpdump file\n"); fprintf(stderr, " -r|--readfile <readfile> | read from given tcpdump file\n");
fprintf(stderr, " -y|--flowfile <file>[=<ident>] | read from given flow file\n"); fprintf(stderr, " -y|--flowfile <file>[=<ident>] | read from given flow file\n");
@ -364,6 +366,7 @@ int main(int argc, char** argv)
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{"iface", required_argument, 0, 'i'}, {"iface", required_argument, 0, 'i'},
{"print-scripts", no_argument, 0, 'l'}, {"print-scripts", no_argument, 0, 'l'},
{"doc-scripts", no_argument, 0, 'Z'},
{"prefix", required_argument, 0, 'p'}, {"prefix", required_argument, 0, 'p'},
{"readfile", required_argument, 0, 'r'}, {"readfile", required_argument, 0, 'r'},
{"flowfile", required_argument, 0, 'y'}, {"flowfile", required_argument, 0, 'y'},
@ -440,7 +443,7 @@ int main(int argc, char** argv)
opterr = 0; opterr = 0;
char opts[256]; char opts[256];
safe_strncpy(opts, "A:a:B:D:e:f:I:i:K:n:p:R:r:s:T:t:U:w:x:X:y:Y:z:CFGHLOPSWdghlv", safe_strncpy(opts, "A:a:B:D:e:f:I:i:K:n:p:R:r:s:T:t:U:w:x:X:y:Y:z:CFGHLOPSWdghlvZ",
sizeof(opts)); sizeof(opts));
#ifdef USE_PERFTOOLS #ifdef USE_PERFTOOLS
@ -633,6 +636,10 @@ int main(int argc, char** argv)
break; break;
#endif #endif
case 'Z':
generate_documentation = 1;
break;
#ifdef USE_IDMEF #ifdef USE_IDMEF
case 'n': case 'n':
fprintf(stderr, "Using IDMEF XML DTD from %s\n", optarg); fprintf(stderr, "Using IDMEF XML DTD from %s\n", optarg);

View file

@ -73,6 +73,15 @@
#include "DNS.h" #include "DNS.h"
#include "RE.h" #include "RE.h"
#include "Scope.h" #include "Scope.h"
#include "BroDoc.h"
#include "BroDocObj.h"
#include <list>
#include <string>
extern BroDoc* current_reST_doc;
extern int generate_documentation;
extern std::list<std::string>* reST_doc_comments;
YYLTYPE GetCurrentLocation(); YYLTYPE GetCurrentLocation();
extern int yyerror(const char[]); extern int yyerror(const char[]);
@ -785,7 +794,13 @@ formal_args_decl:
decl: decl:
TOK_MODULE TOK_ID ';' TOK_MODULE TOK_ID ';'
{ current_module = $2; } {
current_module = $2;
if ( generate_documentation )
{
current_reST_doc->AddModule(current_module);
}
}
| TOK_EXPORT '{' { is_export = true; } decl_list '}' | TOK_EXPORT '{' { is_export = true; } decl_list '}'
{ is_export = false; } { is_export = false; }

View file

@ -15,11 +15,16 @@
#include "Debug.h" #include "Debug.h"
#include "PolicyFile.h" #include "PolicyFile.h"
#include "broparse.h" #include "broparse.h"
#include "BroDoc.h"
#include "BroBifDoc.h"
#include <stack> #include <stack>
#include <list>
#include <string>
extern YYLTYPE yylloc; // holds start line and column of token extern YYLTYPE yylloc; // holds start line and column of token
extern int print_loaded_scripts; extern int print_loaded_scripts;
extern int generate_documentation;
int nwarn = 0; int nwarn = 0;
int nerr = 0; int nerr = 0;
@ -35,6 +40,8 @@ int_list if_stack;
int line_number = 1; int line_number = 1;
int include_level = 0; int include_level = 0;
const char* filename = 0; const char* filename = 0;
BroDoc* current_reST_doc = 0;
static BroDoc* last_reST_doc = 0;
char last_tok[128]; char last_tok[128];
@ -50,6 +57,21 @@ char last_tok[128];
// Files we have already scanned (or are in the process of scanning). // Files we have already scanned (or are in the process of scanning).
static PList(char) files_scanned; static PList(char) files_scanned;
// reST documents that we've created (or have at least opened so far)
static std::list<BroDoc*> docs_generated;
// reST comments (those starting with ##) seen so far
std::list<std::string>* reST_doc_comments = 0;
// print current contents of reST_doc_comments list to stderr
void print_current_reST_doc_comments();
// delete the reST_doc_comments list object
void clear_reST_doc_comments();
// adds changes to capture_filter to the current script's reST documentation
static void check_capture_filter_changes();
class FileInfo { class FileInfo {
public: public:
FileInfo(string restore_module = ""); FileInfo(string restore_module = "");
@ -60,6 +82,7 @@ public:
const char* name; const char* name;
int line; int line;
int level; int level;
BroDoc* doc;
}; };
// A stack of input buffers we're scanning. file_stack[len-1] is the // A stack of input buffers we're scanning. file_stack[len-1] is the
@ -102,6 +125,30 @@ ESCSEQ (\\([^\n]|[0-7]+|x[[:xdigit:]]+))
%% %%
##!.* {
// Add this format of comments to the script documentation's "summary"
if ( generate_documentation )
current_reST_doc->AddSummary(yytext + 3);
}
##{OWS}Author:.* {
if ( generate_documentation )
current_reST_doc->SetAuthor(
skip_whitespace( // Skip whitespace after "Author:"
skip_whitespace(yytext + 2) // Skip whitespace after ##
+ 7) // Skip "Author:"
);
}
##[^#\n].* {
if ( generate_documentation )
{
if ( ! reST_doc_comments )
reST_doc_comments = new std::list<std::string>();
reST_doc_comments->push_back(yytext + 2);
}
}
#.* /* eat comments */ #.* /* eat comments */
{WS} /* eat whitespace */ {WS} /* eat whitespace */
@ -211,6 +258,17 @@ when return TOK_WHEN;
@load{WS}{FILE} { @load{WS}{FILE} {
const char* new_file = skip_whitespace(yytext + 5); // Skip "@load". const char* new_file = skip_whitespace(yytext + 5); // Skip "@load".
if ( generate_documentation )
{
current_reST_doc->AddImport(new_file);
if ( reST_doc_comments )
{
fprintf(stderr, "Warning: unconsumed reST documentation is being "
"discarded before doing '@load %s' in %s:\n",
new_file, current_reST_doc->GetSourceFileName());
clear_reST_doc_comments();
}
}
(void) load_files_with_prefix(new_file); (void) load_files_with_prefix(new_file);
} }
@ -446,7 +504,6 @@ static int load_files_with_prefix(const char* orig_file)
strcpy(new_filename, file); strcpy(new_filename, file);
f = search_for_file(new_filename, "bro", &full_filename); f = search_for_file(new_filename, "bro", &full_filename);
delete [] new_filename; delete [] new_filename;
} }
@ -496,6 +553,21 @@ static int load_files_with_prefix(const char* orig_file)
// Don't delete the old filename - it's pointed to by // Don't delete the old filename - it's pointed to by
// every BroObj created when parsing it. // every BroObj created when parsing it.
yylloc.filename = filename = full_filename; yylloc.filename = filename = full_filename;
if ( generate_documentation )
{
char* bifExtStart = strstr(full_filename, ".bif.bro");
BroDoc* reST_doc;
if ( bifExtStart )
reST_doc = new BroBifDoc(full_filename);
else
reST_doc = new BroDoc(full_filename);
docs_generated.push_back(reST_doc);
current_reST_doc = reST_doc;
}
} }
else else
@ -655,6 +727,7 @@ int yywrap()
// Stack is now empty. // Stack is now empty.
while ( input_files.length() > 0 ) while ( input_files.length() > 0 )
{ {
check_capture_filter_changes();
if ( load_files_with_prefix(input_files[0]) ) if ( load_files_with_prefix(input_files[0]) )
{ {
// Don't delete the filename - it's pointed to by // Don't delete the filename - it's pointed to by
@ -667,6 +740,7 @@ int yywrap()
// if any. // if any.
(void) input_files.remove_nth(0); (void) input_files.remove_nth(0);
} }
check_capture_filter_changes();
// Add redef statements for any X=Y command line parameters. // Add redef statements for any X=Y command line parameters.
if ( params.size() > 0 ) if ( params.size() > 0 )
@ -731,6 +805,18 @@ int yywrap()
return 0; return 0;
} }
if ( generate_documentation )
{
std::list<BroDoc*>::iterator it;
for ( it = docs_generated.begin(); it != docs_generated.end(); ++it )
{
(*it)->WriteDocFile();
delete *it;
}
docs_generated.clear();
clear_reST_doc_comments();
}
// Otherwise, we are done. // Otherwise, we are done.
return 1; return 1;
} }
@ -742,6 +828,7 @@ FileInfo::FileInfo(string arg_restore_module)
name = ::filename; name = ::filename;
line = ::line_number; line = ::line_number;
level = ::include_level; level = ::include_level;
doc = ::current_reST_doc;
} }
FileInfo::~FileInfo() FileInfo::~FileInfo()
@ -753,6 +840,8 @@ FileInfo::~FileInfo()
yylloc.filename = filename = name; yylloc.filename = filename = name;
yylloc.first_line = yylloc.last_line = line_number = line; yylloc.first_line = yylloc.last_line = line_number = line;
include_level = level; include_level = level;
last_reST_doc = current_reST_doc;
current_reST_doc = doc;
if ( restore_module != "" ) if ( restore_module != "" )
current_module = restore_module; current_module = restore_module;
@ -779,3 +868,40 @@ static void report_file()
files_reported.append(copy_string(filename)); files_reported.append(copy_string(filename));
} }
static void check_capture_filter_changes()
{
if ( generate_documentation )
{
// Lookup the "capture_filters" identifier, if it has any
// defined value, add it to the script's reST documentation,
// and finally clear the table so it doesn't taint the
// documentation for subsequent scripts.
ID* capture_filters = global_scope()->Lookup("capture_filters");
if ( capture_filters )
{
ODesc desc;
capture_filters->ID_Val()->Describe(&desc);
last_reST_doc->SetPacketFilter(desc.Description());
( (TableVal*) capture_filters->ID_Val() )->RemoveAll();
}
}
}
void print_current_reST_doc_comments()
{
if ( ! reST_doc_comments ) return;
std::list<std::string>::iterator it;
for (it = reST_doc_comments->begin(); it != reST_doc_comments->end(); ++it)
fprintf(stderr, "##%s\n", it->c_str());
}
void clear_reST_doc_comments()
{
if ( ! reST_doc_comments ) return;
fprintf(stderr, "Warning: %lu unconsumed reST comments:\n",
reST_doc_comments->size());
print_current_reST_doc_comments();
delete reST_doc_comments;
reST_doc_comments = 0;
}