mirror of
https://github.com/zeek/zeek.git
synced 2025-10-17 05:58:20 +00:00
Initial import of svn+ssh:://svn.icir.org/bro/trunk/bro as of r7088
This commit is contained in:
commit
61757ac78b
1383 changed files with 380824 additions and 0 deletions
611
src/builtin-func.y
Normal file
611
src/builtin-func.y
Normal file
|
@ -0,0 +1,611 @@
|
|||
%{
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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<BuiltinFuncArg*> args;
|
||||
|
||||
// enum types declared by "declare enum <id>"
|
||||
set<string> 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 <str> TOK_C_TOKEN TOK_ID TOK_CSTR TOK_WS TOK_COMMENT TOK_ATTR opt_ws
|
||||
%type <val> 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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue