%{ #include #include #include #include using namespace std; #include #include extern int line_number; extern char* input_filename; #define print_line_directive(fp) fprintf(fp, "\n#line %d \"%s\"\n", line_number, input_filename) extern FILE* fp_bro_init; extern FILE* fp_func_def; extern FILE* fp_func_h; extern FILE* fp_func_init; extern FILE* fp_netvar_h; extern FILE* fp_netvar_def; extern FILE* fp_netvar_init; int in_c_code = 0; int definition_type; const char* bro_prefix; const char* c_prefix; enum { C_SEGMENT_DEF, FUNC_DEF, REWRITER_DEF, EVENT_DEF, }; void set_definition_type(int type) { definition_type = type; switch ( type ) { case FUNC_DEF: bro_prefix = ""; c_prefix = "bro_"; break; case REWRITER_DEF: bro_prefix = "rewrite_"; c_prefix = "bro_rewrite_"; break; case EVENT_DEF: bro_prefix = ""; c_prefix = "bro_event_"; break; case C_SEGMENT_DEF: break; } } const char* arg_list_name = "BiF_ARGS"; const char* trace_rewriter_name = "trace_rewriter"; #include "bif_arg.h" extern const char* decl_name; int var_arg; // whether the number of arguments is variable std::vector args; // enum types declared by "declare enum " set enum_types; extern int yyerror(const char[]); extern int yywarn(const char msg[]); extern int yylex(); char* concat(const char* str1, const char* str2) { int len1 = strlen(str1); int len2 = strlen(str2); char* s = new char[len1 + len2 +1]; memcpy(s, str1, len1); memcpy(s + len1, str2, len2); s[len1+len2] = '\0'; return s; } // Print the bro_event_* function prototype in C++, without the ending ';' void print_event_c_prototype(FILE *fp) { fprintf(fp, "void %s%s(Analyzer* analyzer%s", c_prefix, decl_name, args.size() ? ", " : "" ); for ( int i = 0; i < (int) args.size(); ++i ) { if ( i > 0 ) fprintf(fp, ", "); args[i]->PrintCArg(fp, i); } fprintf(fp, ")"); } // Print the bro_event_* function body in C++. void print_event_c_body(FILE *fp) { fprintf(fp, "\t{\n"); fprintf(fp, "\t// Note that it is intentional that here we do not\n"); fprintf(fp, "\t// check if %s is NULL, which should happen *before*\n", decl_name); fprintf(fp, "\t// bro_event_%s is called to avoid unnecessary Val\n", decl_name); fprintf(fp, "\t// allocation.\n"); fprintf(fp, "\n"); fprintf(fp, "\tval_list* vl = new val_list;\n\n"); BuiltinFuncArg *connection_arg = 0; for ( int i = 0; i < (int) args.size(); ++i ) { fprintf(fp, "\t"); fprintf(fp, "vl->append("); args[i]->PrintBroValConstructor(fp); fprintf(fp, ");\n"); if ( args[i]->Type() == TYPE_CONNECTION ) { if ( connection_arg == 0 ) connection_arg = args[i]; else { // We are seeing two connection type arguments. yywarn("Warning: with more than connection-type " "event arguments, bifcl only passes " "the first one to EventMgr as cookie."); } } } fprintf(fp, "\n"); fprintf(fp, "\tmgr.QueueEvent(%s, vl, SOURCE_LOCAL, analyzer->GetID(), timer_mgr", decl_name); if ( connection_arg ) // Pass the connection to the EventMgr as the "cookie" fprintf(fp, ", %s", connection_arg->Name()); fprintf(fp, ");\n"); fprintf(fp, "\t} // event generation\n"); } %} %token TOK_LPP TOK_RPP TOK_LPB TOK_RPB TOK_LPPB TOK_RPPB TOK_VAR_ARG %token TOK_BOOL %token TOK_FUNCTION TOK_REWRITER TOK_EVENT TOK_CONST TOK_ENUM TOK_DECLARE %token TOK_WRITE TOK_PUSH TOK_EOF TOK_TRACE %token TOK_ARGS TOK_ARG TOK_ARGC %token TOK_ID TOK_ATTR TOK_CSTR TOK_LF TOK_WS TOK_COMMENT %token TOK_ATOM TOK_C_TOKEN %left ',' ':' %type TOK_C_TOKEN TOK_ID TOK_CSTR TOK_WS TOK_COMMENT TOK_ATTR opt_ws %type TOK_ATOM TOK_BOOL %union { const char* str; int val; } %% definitions: definitions definition opt_ws { fprintf(fp_func_def, "%s", $3); } | opt_ws { int n = 1024 + strlen(input_filename); char auto_gen_comment[n]; snprintf(auto_gen_comment, n, "This file was automatically generated by bifcl from %s.", input_filename); fprintf(fp_bro_init, "# %s\n\n", auto_gen_comment); fprintf(fp_func_def, "// %s\n\n", auto_gen_comment); fprintf(fp_func_h, "// %s\n\n", auto_gen_comment); fprintf(fp_func_init, "// %s\n\n", auto_gen_comment); fprintf(fp_netvar_def, "// %s\n\n", auto_gen_comment); fprintf(fp_netvar_h, "// %s\n\n", auto_gen_comment); fprintf(fp_netvar_init, "// %s\n\n", auto_gen_comment); fprintf(fp_func_def, "%s", $1); } ; definition: event_def | func_def | rewriter_def | c_code_segment | enum_def | const_def | declare_def ; declare_def: TOK_DECLARE opt_ws TOK_ENUM opt_ws TOK_ID opt_ws ';' { enum_types.insert($5); } ; event_def: event_prefix opt_ws plain_head opt_attr end_of_head ';' { print_event_c_prototype(fp_func_h); fprintf(fp_func_h, ";\n"); print_event_c_prototype(fp_func_def); fprintf(fp_func_def, "\n"); print_event_c_body(fp_func_def); } ; func_def: func_prefix opt_ws typed_head end_of_head body ; rewriter_def: rewriter_prefix opt_ws plain_head end_of_head body ; enum_def: enum_def_1 enum_list TOK_RPB { // First, put an end to the enum type decl. fprintf(fp_bro_init, "};\n"); fprintf(fp_netvar_h, "}; }\n"); // Now generate the netvar's. fprintf(fp_netvar_h, "extern EnumType* enum_%s;\n", decl_name); fprintf(fp_netvar_def, "EnumType* enum_%s;\n", decl_name); fprintf(fp_netvar_init, "\tenum_%s = internal_type(\"%s\")->AsEnumType();\n", decl_name, decl_name); } ; enum_def_1: TOK_ENUM opt_ws TOK_ID opt_ws TOK_LPB opt_ws { decl_name = $3; fprintf(fp_bro_init, "type %s: enum %s{%s", $3, $4, $6); fprintf(fp_netvar_h, "namespace BroEnum { "); fprintf(fp_netvar_h, "enum %s {\n", $3); } ; enum_list: enum_list TOK_ID opt_ws ',' opt_ws { fprintf(fp_bro_init, "%s%s,%s", $2, $3, $5); fprintf(fp_netvar_h, "\t%s,\n", $2); } | /* nothing */ ; const_def: const_def_1 const_init opt_attr ';' { fprintf(fp_bro_init, ";\n"); fprintf(fp_netvar_h, "extern int %s;\n", decl_name); fprintf(fp_netvar_def, "int %s;\n", decl_name); fprintf(fp_netvar_init, "\t%s = internal_val(\"%s\")->AsBool();\n", decl_name, decl_name); } ; const_def_1: TOK_CONST opt_ws TOK_ID opt_ws { decl_name = $3; fprintf(fp_bro_init, "const%s", $2); fprintf(fp_bro_init, "%s: bool%s", $3, $4); } ; opt_const_init: /* nothing */ | const_init ; /* Currently support only boolean and string values */ const_init: '=' opt_ws TOK_BOOL opt_ws { fprintf(fp_bro_init, "=%s%c%s", $2, ($3) ? 'T' : 'F', $4); } | '=' opt_ws TOK_CSTR opt_ws { fprintf(fp_bro_init, "=%s%s%s", $2, $3, $4); } ; opt_attr: /* nothing */ | opt_attr TOK_ATTR { fprintf(fp_bro_init, "%s", $2); } opt_ws opt_const_init ; func_prefix: TOK_FUNCTION { set_definition_type(FUNC_DEF); } ; rewriter_prefix: TOK_REWRITER { set_definition_type(REWRITER_DEF); } ; event_prefix: TOK_EVENT { set_definition_type(EVENT_DEF); } ; end_of_head: /* nothing */ { fprintf(fp_bro_init, ";\n"); } ; typed_head: plain_head return_type { } ; plain_head: head_1 args arg_end opt_ws { if ( var_arg ) fprintf(fp_bro_init, "va_args: any"); else { if ( definition_type == REWRITER_DEF ) fprintf(fp_bro_init, "c: connection"); for ( int i = 0; i < (int) args.size(); ++i ) { if ( i > 0 || definition_type == REWRITER_DEF ) fprintf(fp_bro_init, ", "); args[i]->PrintBro(fp_bro_init); } } fprintf(fp_bro_init, ")"); fprintf(fp_bro_init, "%s", $4); fprintf(fp_func_def, "%s", $4); } ; head_1: TOK_ID opt_ws arg_begin { const char* method_type = 0; decl_name = $1; if ( definition_type == FUNC_DEF || definition_type == REWRITER_DEF ) { method_type = "function"; print_line_directive(fp_func_def); } else if ( definition_type == EVENT_DEF ) method_type = "event"; if ( method_type ) fprintf(fp_bro_init, "global %s%s: %s%s(", bro_prefix, decl_name, method_type, $2); if ( definition_type == FUNC_DEF || definition_type == REWRITER_DEF ) { fprintf(fp_func_init, "\textern Val* %s%s(Frame* frame, val_list*);\n", c_prefix, decl_name); fprintf(fp_func_init, "\t(void) new BuiltinFunc(%s%s, \"%s%s\", 0);\n", c_prefix, decl_name, bro_prefix, decl_name); fprintf(fp_func_h, "extern Val* %s%s(Frame* frame, val_list*);\n", c_prefix, decl_name); fprintf(fp_func_def, "Val* %s%s(Frame* frame, val_list* %s)", c_prefix, decl_name, arg_list_name); } else if ( definition_type == EVENT_DEF ) { fprintf(fp_netvar_h, "extern EventHandlerPtr %s;\n", decl_name); fprintf(fp_netvar_def, "EventHandlerPtr %s;\n", decl_name); fprintf(fp_netvar_init, "\t%s = internal_handler(\"%s\");\n", decl_name, decl_name); // C++ prototypes of bro_event_* functions will // be generated later. } } ; arg_begin: TOK_LPP { args.clear(); var_arg = 0; } ; arg_end: TOK_RPP ; args: args_1 | opt_ws { /* empty, to avoid yacc complaint about type clash */ } ; args_1: args_1 ',' opt_ws arg opt_ws | opt_ws arg opt_ws { /* empty */ } ; arg: TOK_ID opt_ws ':' opt_ws TOK_ID { args.push_back(new BuiltinFuncArg($1, $5)); } | TOK_VAR_ARG { if ( definition_type == EVENT_DEF ) yyerror("events cannot have variable arguments"); var_arg = 1; } ; return_type: ':' opt_ws TOK_ID opt_ws { BuiltinFuncArg* ret = new BuiltinFuncArg("", $3); ret->PrintBro(fp_bro_init); delete ret; fprintf(fp_func_def, "%s", $4); } ; body: body_start c_body body_end { fprintf(fp_func_def, " // end of %s\n", decl_name); print_line_directive(fp_func_def); } ; c_code_begin: /* empty */ { in_c_code = 1; print_line_directive(fp_func_def); } ; c_code_end: /* empty */ { in_c_code = 0; } ; body_start: TOK_LPB c_code_begin { int implicit_arg = 0; int argc = args.size(); fprintf(fp_func_def, "{"); if ( definition_type == REWRITER_DEF ) { implicit_arg = 1; ++argc; } if ( argc > 0 || ! var_arg ) fprintf(fp_func_def, "\n"); if ( ! var_arg ) { fprintf(fp_func_def, "\tif ( %s->length() != %d )\n", arg_list_name, argc); fprintf(fp_func_def, "\t\t{\n"); fprintf(fp_func_def, "\t\trun_time(\"%s() takes exactly %d argument(s)\");\n", decl_name, argc); fprintf(fp_func_def, "\t\treturn 0;\n"); fprintf(fp_func_def, "\t\t}\n"); } else if ( argc > 0 ) { fprintf(fp_func_def, "\tif ( %s->length() < %d )\n", arg_list_name, argc); fprintf(fp_func_def, "\t\t{\n"); fprintf(fp_func_def, "\t\trun_time(\"%s() takes at least %d argument(s)\");\n", decl_name, argc); fprintf(fp_func_def, "\t\treturn 0;\n"); fprintf(fp_func_def, "\t\t}\n"); } if ( definition_type == REWRITER_DEF ) { fprintf(fp_func_def, "\tRewriter* %s = get_trace_rewriter((*%s)[0]);\n", trace_rewriter_name, arg_list_name); fprintf(fp_func_def, "\tif ( ! trace_rewriter )\n"); fprintf(fp_func_def, "\t\treturn 0;\n"); } for ( int i = 0; i < (int) args.size(); ++i ) args[i]->PrintCDef(fp_func_def, i + implicit_arg); print_line_directive(fp_func_def); } ; body_end: TOK_RPB c_code_end { if ( definition_type == REWRITER_DEF ) fprintf(fp_func_def, "\n\treturn 0;\n"); fprintf(fp_func_def, "}"); } ; c_code_segment: TOK_LPPB c_code_begin c_body c_code_end TOK_RPPB ; c_body: opt_ws { fprintf(fp_func_def, "%s", $1); } | c_body c_atom opt_ws { fprintf(fp_func_def, "%s", $3); } ; c_atom: TOK_ID { fprintf(fp_func_def, "%s", $1); } | TOK_C_TOKEN { fprintf(fp_func_def, "%s", $1); } | TOK_ARG { fprintf(fp_func_def, "(*%s)", arg_list_name); } | TOK_ARGS { fprintf(fp_func_def, "%s", arg_list_name); } | TOK_ARGC { fprintf(fp_func_def, "%s->length()", arg_list_name); } | TOK_TRACE { fprintf(fp_func_def, "%s", trace_rewriter_name); } | TOK_WRITE { fprintf(fp_func_def, "%s->WriteData", trace_rewriter_name); } | TOK_PUSH { fprintf(fp_func_def, "%s->Push", trace_rewriter_name); } | TOK_EOF { fprintf(fp_func_def, "%s->EndOfData", trace_rewriter_name); } | TOK_CSTR { fprintf(fp_func_def, "%s", $1); } | TOK_ATOM { fprintf(fp_func_def, "%c", $1); } ; opt_ws: opt_ws TOK_WS { $$ = concat($1, $2); } | opt_ws TOK_LF { $$ = concat($1, "\n"); } | opt_ws TOK_COMMENT { if ( in_c_code ) $$ = concat($1, $2); else $$ = $1; } | /* empty */ { $$ = ""; } ; %% extern char* yytext; extern char* input_filename; extern int line_number; const char* decl_name; void print_msg(const char msg[]) { int msg_len = strlen(msg) + strlen(yytext) + 64; char* msgbuf = new char[msg_len]; if ( yytext[0] == '\n' ) snprintf(msgbuf, msg_len, "%s, on previous line", msg); else if ( yytext[0] == '\0' ) snprintf(msgbuf, msg_len, "%s, at end of file", msg); else snprintf(msgbuf, msg_len, "%s, at or near \"%s\"", msg, yytext); /* extern int column; sprintf(msgbuf, "%*s\n%*s\n", column, "^", column, msg); */ if ( input_filename ) fprintf(stderr, "%s:%d: ", input_filename, line_number); else fprintf(stderr, "line %d: ", line_number); fprintf(stderr, "%s\n", msgbuf); delete [] msgbuf; } int yywarn(const char msg[]) { print_msg(msg); return 0; } int yyerror(const char msg[]) { print_msg(msg); abort(); exit(1); return 0; }