// $Id: Func.cc,v 1.90 2003/09/02 03:47:09 vern Exp $
//
// Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002
//      The Regents of the University of California.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that: (1) source code distributions
// retain the above copyright notice and this paragraph in its entirety, (2)
// distributions including binary code include the above copyright notice and
// this paragraph in its entirety in the documentation or other materials
// provided with the distribution, and (3) all advertising materials mentioning
// features or use of this software display the following acknowledgement:
// ``This product includes software developed by the University of California,
// Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
// the University nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#include "config.h"

#include <sys/types.h>
#include <sys/stat.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif
#include <sys/resource.h>

#include <netinet/in.h>

#include <stdlib.h>
#include <errno.h>
#include <ctype.h>


#include "md5.h"
#include "Stmt.h"
#include "Scope.h"
#include "Net.h"
#include "NetVar.h"
#include "File.h"
#include "Func.h"
#include "Frame.h"
#include "Var.h"
#include "Login.h"
#include "Sessions.h"
#include "RE.h"
#include "Serializer.h"


const Expr* calling_expr = 0;


Func::~Func()
	{
	Unref(t);
	}

void Func::AddBody(Stmt* /* new_body */, id_list* /* new_inits */,
			int /* new_frame_size */)
	{
	Internal("Func::AddBody called");
	}


void DebugFunc::DescribeDebug(ODesc* d, const val_list* args) const
	{
	id->Describe(d);
	RecordType* func_args = t->Args();

	if ( args )
		{
		d->Add("(");

		for ( int i = 0; i < args->length(); ++i )
			{
			// Handle varargs case (more args than formals).
			if ( i >= func_args->NumFields() )
				{
				d->Add("vararg");
				d->Add(i - func_args->NumFields());
				}
			else
				d->Add(func_args->FieldName(i));

			d->Add(" = '");
			(*args)[i]->Describe(d);

			if ( i < args->length() - 1 )
				d->Add("', ");
			else
				d->Add("'");
			}

		d->Add(")");
		}
	}

BroFunc::BroFunc(ID* arg_id, Stmt* arg_body, id_list* aggr_inits,
		int arg_frame_size)
: DebugFunc(arg_id->Type()->AsFuncType())
	{
	id = arg_id;
	bodies.push_back(AddInits(arg_body, aggr_inits));
	frame_size = arg_frame_size;
	}

BroFunc::~BroFunc()
	{
	Unref(id);
	for ( unsigned int i = 0; i < bodies.size(); ++i )
		Unref(bodies[i]);
	}

int BroFunc::IsPure() const
	{
	for ( unsigned int i = 0; i < bodies.size(); ++i )
		if ( ! bodies[i]->IsPure() )
			return 0;

	return 1;
	}

Val* BroFunc::Call(val_list* args) const
	{
	Frame* f = new Frame(frame_size, this, args);
	g_frame_stack.push_back(f);	// used for backtracing

	if ( g_trace_state.DoTrace() )
		{
		ODesc d;
		DescribeDebug(&d, args);

		g_trace_state.LogTrace("%s called: %s\n",
			IsEvent() ? "event" : "function", d.Description());
		}

	loop_over_list(*args, i)
		f->SetElement(i, (*args)[i]);

	stmt_flow_type flow;

	Val* result = 0;

	for ( unsigned int i = 0; i < bodies.size(); ++i )
		result = bodies[i]->Exec(f, flow);

	if ( result && g_trace_state.DoTrace() )
		{
		ODesc d;
		result->Describe(&d);

		g_trace_state.LogTrace("Function return: %s\n", d.Description());
		}

	g_frame_stack.pop_back();
	Unref(f);

	return result;
	}

void BroFunc::AddBody(Stmt* new_body, id_list* new_inits, int new_frame_size)
	{
	if ( new_frame_size > frame_size )
		frame_size = new_frame_size;

	new_body = AddInits(new_body, new_inits);

	bodies.push_back(new_body);
	}

void BroFunc::Describe(ODesc* d) const
	{
	id->Describe(d);
	d->NL();
	d->AddCount(frame_size);
	for ( unsigned int i = 0; i < bodies.size(); ++i )
		bodies[i]->Describe(d);
	}

Stmt* BroFunc::AddInits(Stmt* body, id_list* inits)
	{
	if ( ! inits || inits->length() == 0 )
		return body;

	StmtList* stmt_series = new StmtList;
	stmt_series->Stmts().append(new InitStmt(inits));
	stmt_series->Stmts().append(body);

	return stmt_series;
	}


BuiltinFunc::BuiltinFunc(built_in_func arg_func, const char* arg_name,
			int arg_is_pure)
: DebugFunc(0)
	{
	func = arg_func;
	name = copy_string(make_full_var_name(GLOBAL_MODULE_NAME, arg_name).c_str());
	is_pure = arg_is_pure;

	id = lookup_ID(name, GLOBAL_MODULE_NAME, false);
	if ( ! id )
		internal_error("built-in function %s missing", name);
	if ( id->HasVal() )
		internal_error("built-in function %s multiply defined", name);

	t = id->Type()->Ref()->AsFuncType();

	id->SetVal(new Val(this));
	}

BuiltinFunc::~BuiltinFunc()
	{
	}

int BuiltinFunc::IsPure() const
	{
	return is_pure;
	}

Val* BuiltinFunc::Call(val_list* args) const
	{
	if ( g_trace_state.DoTrace() )
		{
		ODesc d;
		DescribeDebug(&d, args);

		g_trace_state.LogTrace("\tBuiltin Function called: %s\n", d.Description());
		}

	Val* result = func(args);
	loop_over_list(*args, i)
		Unref((*args)[i]);

	// Don't Unref() args, that's the caller's responsibility.
	if ( result && g_trace_state.DoTrace() )
		{
		ODesc d;
		result->Describe(&d);

		g_trace_state.LogTrace("\tFunction return: %s\n", d.Description());
		}

	return result;
	}

void BuiltinFunc::Describe(ODesc* d) const
	{
	id->Describe(d);
	d->AddCount(is_pure);
	}

void builtin_run_time(const char* msg, BroObj* arg)
	{
	if ( calling_expr )
		calling_expr->RunTime(msg, arg);
	else
		run_time(msg, arg);
	}

#include "bro.bif.func_def"

void init_builtin_funcs()
	{
	start_time = current_time();

	ftp_port = internal_type("ftp_port")->AsRecordType();
	bro_resources = internal_type("bro_resources")->AsRecordType();
	matcher_stats = internal_type("matcher_stats")->AsRecordType();
	var_sizes = internal_type("var_sizes")->AsTableType();

#include "bro.bif.func_init"

#include "common-rw.bif.func_init"
#include "finger-rw.bif.func_init"
#include "ftp-rw.bif.func_init"
#include "http-rw.bif.func_init"
#include "ident-rw.bif.func_init"
#include "smtp-rw.bif.func_init"
	}
