/*
BOIL (Brunni's Own Interpreter Language)
(c) 1998 netEstate (http://www.netestate.de/, info@netestate.de),
Michael Brunnbauer & Markus Hendel, Gesellschaft fuer Netzwerkkommunikation bR
Am Bluetenanger 8, 80995 Muenchen, Germany

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "general.h"
#include "handles.cc"
#include "boil.h"
#include "boil.tab.h"
#include "libs.h"
#ifdef WITH_RPC
#include "rpc.h"
#endif

bool embed,first_line,stats;
FILE* prg;
char* prgstr;
unsigned long max_parse_recursions;
unsigned int line,filei;
string file;
string w,w1;
bool first,last;
char lastchar;
int wtype;
joined_list<token> tokenstack;
hashdir<type_function,string,256,0> libfunctions;
hashdir<type_value,string,256,0> variables;

#ifdef WITH_RPC
type_block* svc_run_block;
opt* svc_run_options;
#endif

FILE* outstream;
pid_t childpid;
hashdir<hashdir<joined_list<cachefield>,string,256,0>,string,256,0>
tcache;
unsigned long tcache_size=10000000;
unsigned char tcache_hash=255;

#ifdef WITH_RPC
void fork_handler(pid_t childpid)
{
  flowctrl back;
  type_function* function;
  string name="fork_handler";
  joined_list<type_value> args;
  type_value pid;

  back=getfunc(svc_run_block,svc_run_options,function,name);
  if (back.ctrl==FLOW_OK)
    {
      pid.type=TYPE_VALUE_LONG;
      pid.val.longval=(long) childpid;
      args.insert_listelem(pid);
      back=function->call(svc_run_block,svc_run_options,0,0,&args);
    };
  return;
}
#endif

void setvarulong(string name,long value)
{
  type_value* ptr=variables.get_listelem(name);
  if (ptr)
    {
      ptr->val.ulongval=value;
      return;
    };

  log(LOG_FATAL,__FILE__,__LINE__,ERROR_WRONG_ARGUMENT,
      "setvarulong: name %s not found",(char*) name);
  return;
}

void setvarstring(string name,string value)
{
  type_value* ptr=variables.get_listelem(name);
  if (ptr)
    {
      ptr->stringval=value;
      return;
    };

  log(LOG_FATAL,__FILE__,__LINE__,ERROR_WRONG_ARGUMENT,
      "setvarstring: name %s not found",(char*) name);
  return;
}

flowctrl::flowctrl()
{
  returnval.type=TYPE_VALUE_VOID;
  line=0;file=0;
  return;
}

flowctrl& flowctrl::operator=(flowctrl src)
{
  ctrl=src.ctrl;
  returnval=src.returnval;
  error=src.error;
  if (src.line) { line=src.line;file=src.file; };
  return *this;
}

type_acc::type_acc()
{
  acc=0;

  count=0;
  max=0;

  count_akt=0;
  max_akt=0;

  start_time=0;
  count_time=0;
  max_time=0;

  return;
}

flowctrl acc_add(opt* options,int type)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  char buf[MAX_LINE_SIZE];
  string type_s;
  mytimer now;

  if (string_mem>max_string_size)
    {
      back.ctrl=FLOW_EXIT;
      back.error="string memory has reached maximum size";
      return back;
    };

  if (!options->ptr_a->accounting[type].acc)
    {
      back.ctrl=FLOW_OK;
      return back;
    };
  
  if (options->ptr_a->accounting[type].max &&
      options->ptr_a->accounting[type].count==
      options->ptr_a->accounting[type].max)
    {
      snprintf(buf,MAX_LINE_SIZE,"%i",type);
      type_s=buf;
      back.ctrl=FLOW_EXIT;
      back.error="accumulated limit for accounting-type "+type_s+" reached";
      return back;
    };
  options->ptr_a->accounting[type].count++;

  if (options->ptr_a->accounting[type].max_akt &&
      options->ptr_a->accounting[type].count_akt==
      options->ptr_a->accounting[type].max_akt)
    {
      snprintf(buf,MAX_LINE_SIZE,"%i",type);
      type_s=buf;
      back.ctrl=FLOW_EXIT;
      back.error="limit for accounting-type "+type_s+" reached";
      return back;
    };
  options->ptr_a->accounting[type].count_akt++;
  
  /* time computations only if requested */
  if (options->ptr_a->accounting[type].max_time || *(options->timestats))
    {
      now.gettime();
  
      if (!options->ptr_a->accounting[type].start_time)
	{
	  options->ptr_a->accounting[type].start_time=now;
	  options->ptr_a->accounting[type].recursion=0;
	}
      else
	{
	  options->ptr_a->accounting[type].recursion++;
	};
    };
  
  if (options->ptr_a->accounting[type].max_time &&
      options->ptr_a->accounting[type].count_time +
      (now-options->ptr_a->accounting[type].start_time) >
      options->ptr_a->accounting[type].max_time)
    {
      options->ptr_a->accounting[type].count_akt--;
      if (!options->ptr_a->accounting[type].recursion)
        {
          now.gettime();
          options->ptr_a->accounting[type].count_time+=
	    (now-options->ptr_a->accounting[type].start_time);
          options->ptr_a->accounting[type].start_time=0;
        }
      else
        {
          options->ptr_a->accounting[type].recursion--;
        };
      
      snprintf(buf,MAX_LINE_SIZE,"%i",type);
      type_s=buf;
      back.ctrl=FLOW_EXIT;
      back.error="time-limit for accounting-type "+type_s+" reached";
      return back;
    };
  
  back.ctrl=FLOW_OK;
  return back;
}

void acc_del(opt* options,int type)
{
  mytimer now;

  if (!options->ptr_a->accounting[type].acc) { return; };

  options->ptr_a->accounting[type].count_akt--;

  /* time computations only if requested */
  if (options->ptr_a->accounting[type].max_time || *(options->timestats))
    {
      if (!options->ptr_a->accounting[type].recursion)
	{
	  now.gettime();
	  options->ptr_a->accounting[type].count_time+=
	    (now-options->ptr_a->accounting[type].start_time);
	  options->ptr_a->accounting[type].start_time=0;
	}
      else
	{
	  options->ptr_a->accounting[type].recursion--;
	};
    };

  return;
}

opt::opt()
{
  allow_eval=0;
  allow_extern=0;
  allow_options=0;
  timestats=0;
  ptr_d=0;
  ptr_h=0;
  ptr_a=0;
}

void freeopts(opt* new_options)
{
  /* destroy changed options */
  if (new_options->allow_eval) { delete new_options->allow_eval; };
  if (new_options->allow_extern) { delete new_options->allow_extern; };
  if (new_options->allow_options) { delete new_options->allow_options; };
  if (new_options->timestats) { delete new_options->timestats; };
  if (new_options->ptr_d) { delete new_options->ptr_d; };
  if (new_options->ptr_h)
    { handle_cleanup(new_options->ptr_h); delete new_options->ptr_h;};
  if (new_options->ptr_a) { delete new_options->ptr_a; };
}

void print_accounting(FILE* fh,opt* options)
{
  fprintf(fh,"\
accounting-stats:
-----------------
");
  for (unsigned long i=0;i!=ACCOUNTING_ANZ;i++)
    {
      fprintf(fh,"\
type %lu: sum: %lu (%lu max) actual: %lu (%lu max) time: %s (%lu max)
",
	      i,
	      options->ptr_a->accounting[i].count,
	      options->ptr_a->accounting[i].max,
	      options->ptr_a->accounting[i].count_akt,
	      options->ptr_a->accounting[i].max_akt,
	      (char*) options->ptr_a->accounting[i].count_time,
	      (unsigned long) options->ptr_a->accounting[i].max_time);
    };
  return;
}

void init_variables()
{
  type_value var;
  string name;
  char buf[MAX_LINE_SIZE];

  var.val.ulongval=0;
  var.stringval="";

  name="error";
  var.type=TYPE_VALUE_ULONG;
  variables.insert_listelem(var,name);

  name="perror";
  var.type=TYPE_VALUE_STRING;
  variables.insert_listelem(var,name);

  name="func_status";
  var.type=TYPE_VALUE_ULONG;
  variables.insert_listelem(var,name);

  name="func_wrong_returntype";
  var.type=TYPE_VALUE_ULONG;
  variables.insert_listelem(var,name);

  name="func_error";
  var.type=TYPE_VALUE_STRING;
  variables.insert_listelem(var,name);

  name="func_error_file";
  var.type=TYPE_VALUE_STRING;
  variables.insert_listelem(var,name);

  name="func_error_line";
  var.type=TYPE_VALUE_ULONG;
  variables.insert_listelem(var,name);

  name="func_error_col";
  var.type=TYPE_VALUE_ULONG;
  variables.insert_listelem(var,name);
	  
  for (unsigned long i=0;i!=ACCOUNTING_ANZ;i++)
    {
      snprintf(buf,MAX_LINE_SIZE,"func_acc_%lu",i);
      name=buf;
      var.type=TYPE_VALUE_STRING;
      variables.insert_listelem(var,name);
    };

  return;
};

/* optstring = "a:x:m:r:e:t:o:z:dqhis" | "a:x:p:f:r:e:t:o:z:qhil" */

int get_options(int argc,char** argv,char* optstring,
		unsigned long &perms,string& passwd,opt* oldopts,opt& options,
		unsigned long init_optind)
{
  int option;
  unsigned long type,max_time,handle,i;
  string libname,token;
  char* optptr;
  bool dummybool=0;
  char ch='a';

  perms=0xFFFFFFFF;
  passwd=0;

  /* read options */
  optarg=0;
  if (init_optind) optind=0;

  while ((option=getopt(argc,argv,optstring))!=EOF)
    {
       switch (option)
	{
        case 'a': /* accounting */
          {
	    if (!options.ptr_a)
	      {
		if (!(options.ptr_a=new opt_a())) { return 0; };
	      };

            /* optarg = type:acc:akt:time */
	    optptr=optarg;
            token=strsep(&optptr,":");
	    if (!defined(token)) { return 0; };
            if (sscanf(token,"%li",&type)!=1) { return 0; };
	    options.ptr_a->accounting[type].acc=1;
            token=strsep(&optptr,":");
            if (!defined(token)) { return 0; };
            if (sscanf(token,"%li",&options.ptr_a->accounting[type].max)!=1)
              { return 0; };
            token=strsep(&optptr,":");
            if (!defined(token)) { return 0; };
            if (sscanf(token,"%li",
		       &options.ptr_a->accounting[type].max_akt)!=1)
              { return 0; };
            token=strsep(&optptr,":");
            if (!defined(token)) { return 0; };
            if (sscanf(token,"%li",&max_time)!=1)
	      { return 0; };
            options.ptr_a->accounting[type].max_time=max_time;
	    
            break;
          };
	case 'o': /* option strings on/off */
	  {
	    if (!options.allow_options)
	      {
		if (!(options.allow_options=new bool())) { return 0; };
	      };
	    if (!strcmp(optarg,"on"))
	      { *options.allow_options=1; }
	    else { *options.allow_options=0; }
	    break;
	  };
	case 't': /* external on/off */
	  {
            if (!options.allow_extern)
              {
                if (!(options.allow_extern=new bool())) { return 0; };
              };
            if (!strcmp(optarg,"on"))
              { *options.allow_extern=1; }
            else { *options.allow_extern=0; }
            break;
	  };
	case 'l': /* delete handles */
	  {
	    if (!options.ptr_h)
	      {
		if (!(options.ptr_h=new opt_h())) { return 0; };
	      };
	    break;
	  };
	case 'c': /* rescue handle */
	  {
	    if (!options.ptr_h) { return 0; };

            optptr=optarg;

            token=strsep(&optptr,":");
            if (!defined(token)) { return 0; };
            if (sscanf(token,"%li",&type)!=1) { return 0; };
            token=strsep(&optptr,":");
            if (!defined(token)) { return 0; };
            if (sscanf(token,"%li",&handle)!=1) { return 0; };

	    switch (type)
	      {
	      case TYPE_HANDLE_STREAM:
		{
		  if (oldopts->ptr_h->streams.ishandle(handle))
		    {
                      options.ptr_h->streams.values.
                        insert_listelem(oldopts->ptr_h->streams.
                                        gethandle(handle));
                      options.ptr_h->streams.handles.insert_listelem(handle);
                      options.ptr_h->streams.cleanup.
                        insert_listelem(dummybool);
		    }
		  else
		    {
		      return 0;
		    };
		  break;
		};
#ifdef WITH_POSTGRES
              case TYPE_HANDLE_PGCONN:
                {
                  if (oldopts->ptr_h->pg_connections.ishandle(handle))
                    {
                      options.ptr_h->pg_connections.values.
                        insert_listelem(oldopts->ptr_h->pg_connections.
                                        gethandle(handle));
                      options.ptr_h->pg_connections.handles.
                        insert_listelem(handle);
                      options.ptr_h->pg_connections.cleanup.
                        insert_listelem(dummybool);
                    }
                  else
                    {
                      return 0;
                    };
                  break;
                };
              case TYPE_HANDLE_PGRESULT:
                {
                  if (oldopts->ptr_h->pg_querys.ishandle(handle))
                    {
                      options.ptr_h->pg_querys.values.
                        insert_listelem(oldopts->ptr_h->pg_querys.
                                        gethandle(handle));
                      options.ptr_h->pg_querys.handles.insert_listelem(handle);
                      options.ptr_h->pg_querys.cleanup.
                        insert_listelem(dummybool);
                    }
                  else
                    {
                      return 0;
                    };
                  break;
                };
#endif
#ifdef WITH_MSQL
              case TYPE_HANDLE_MSQLCONN:
                {
                  if (oldopts->ptr_h->msql_connections.ishandle(handle))
                    {
                      options.ptr_h->msql_connections.values.
                        insert_listelem(oldopts->ptr_h->msql_connections.
                                        gethandle(handle));
                      options.ptr_h->msql_connections.handles.
                        insert_listelem(handle);
                      options.ptr_h->msql_connections.cleanup.
                        insert_listelem(dummybool);
                    }
                  else
                    {
                      return 0;
                    };
                  break;
                };
              case TYPE_HANDLE_MSQLRESULT:
                {
                  if (oldopts->ptr_h->msql_querys.ishandle(handle))
                    {
                      options.ptr_h->msql_querys.values.
                        insert_listelem(oldopts->ptr_h->msql_querys.
                                        gethandle(handle));
                      options.ptr_h->msql_querys.handles.
			insert_listelem(handle);
                      options.ptr_h->msql_querys.cleanup.
                        insert_listelem(dummybool);
                    }
                  else
                    {
                      return 0;
                    };
                  break;
                };
#endif
#ifdef WITH_MYSQL
              case TYPE_HANDLE_MYSQLCONN:
                {
                  if (oldopts->ptr_h->mysql_connections.ishandle(handle))
                    {
                      options.ptr_h->mysql_connections.values.
                        insert_listelem(oldopts->ptr_h->mysql_connections.
                                        gethandle(handle));
                      options.ptr_h->mysql_connections.handles.
                        insert_listelem(handle);
                      options.ptr_h->mysql_connections.cleanup.
                        insert_listelem(dummybool);
                    }
                  else
                    {
                      return 0;
                    };
                  break;
                };
              case TYPE_HANDLE_MYSQLRESULT:
                {
                  if (oldopts->ptr_h->mysql_querys.ishandle(handle))
                    {
                      options.ptr_h->mysql_querys.values.
                        insert_listelem(oldopts->ptr_h->mysql_querys.
                                        gethandle(handle));
                      options.ptr_h->mysql_querys.handles.
                        insert_listelem(handle);
                      options.ptr_h->mysql_querys.cleanup.
                        insert_listelem(dummybool);
                    }
                  else
                    {
                      return 0;
                    };
                  break;
                };
#endif
#ifdef WITH_RPC
              case TYPE_HANDLE_RPCTRANS:
                {
                  if (oldopts->ptr_h->rpc_transports.ishandle(handle))
                    {
                      options.ptr_h->rpc_transports.values.
                        insert_listelem(oldopts->ptr_h->rpc_transports.
                                        gethandle(handle));
                      options.ptr_h->rpc_transports.handles.
                        insert_listelem(handle);
                      options.ptr_h->rpc_transports.cleanup.
                        insert_listelem(dummybool);
                    }
                  else
                    {
                      return 0;
                    };
                  break;
                };
              case TYPE_HANDLE_RPCCLIENT:
                {
                  if (oldopts->ptr_h->rpc_clients.ishandle(handle))
                    {
                      options.ptr_h->rpc_clients.values.
                        insert_listelem(oldopts->ptr_h->rpc_clients.
                                        gethandle(handle));
                      options.ptr_h->rpc_clients.handles.
                        insert_listelem(handle);
                      options.ptr_h->rpc_clients.cleanup.
                        insert_listelem(dummybool);
                    }
                  else
                    {
                      return 0;
                    };
                  break;
                };
#endif
              case TYPE_HANDLE_SOCKET:
                {
                  if (oldopts->ptr_h->sockets.ishandle(handle))
                    {
                      options.ptr_h->sockets.values.
                        insert_listelem(oldopts->ptr_h->sockets.
                                        gethandle(handle));
                      options.ptr_h->sockets.handles.
                        insert_listelem(handle);
                      options.ptr_h->sockets.cleanup.
                        insert_listelem(dummybool);
                    }
                  else
                    {
                      return 0;
                    };
                  break;
                };
              case TYPE_HANDLE_DIR:
                {
                  if (oldopts->ptr_h->dirs.ishandle(handle))
                    {
                      options.ptr_h->dirs.values.
                        insert_listelem(oldopts->ptr_h->dirs.
                                        gethandle(handle));
                      options.ptr_h->dirs.handles.
                        insert_listelem(handle);
                      options.ptr_h->dirs.cleanup.
                        insert_listelem(dummybool);
                    }
                  else
                    {
                      return 0;
                    };
                  break;
                };
	      default:
		{
		  return 0;
		};
	      };

	    break;
	  };
        case 'i': /* ignore first line */
          {
            first_line=0;
            break;
          };
        case 'e': /* eval() on off */
          {
            if (!options.allow_eval)
              {
                if (!(options.allow_eval=new bool())) { return 0; };
              };
            if (!strcmp(optarg,"on"))
              { *options.allow_eval=1; }
            else { *options.allow_eval=0; }
            break;
          };
        case 'x': /* toggle function disable */
          {
	    if (!options.ptr_d)
	      {
		if (!(options.ptr_d=new opt_d())) { return 0; };
	      };
	    libname=optarg;
	    if (options.ptr_d->disabled.get_listelem(libname))
              {   
                options.ptr_d->disabled.remove_listelem(libname);
              }
            else
	      {
		options.ptr_d->disabled.insert_listelem(ch,libname);
	      };
            break;
          };
	case 'q': /* disable all functions */
	  {
	    if (!options.ptr_d)
	      {
		if (!(options.ptr_d=new opt_d())) { return 0; };
	      };
	    i=0;
	    while (defined(functions[i].name))
	      {
		options.ptr_d->disabled.insert_listelem(ch,functions[i].name);
		i++;
	      };
	    break;
	  };
        case 'm': /* max string size */
          {
	    if (sscanf(optarg,"%li",&max_string_size)!=1)
	      {
		return 0;
	      };
            break;
          };
        case 'f': /* upper block permissions */
          {
            if (sscanf(optarg,"%li",&perms)!=1)
              {
                return 0;
              };
            break;
          };
        case 'r': /* max parser recursions */
          {
	    if (sscanf(optarg,"%li",&max_parse_recursions)!=1)
	      {
		return 0;
	      };
            break;
          };
        case 'd': /* debugging */
          {
            debug=1;
            break;
          };
        case 'h': /* embed code */
          {
            embed=1;
            break;
          };
	case 'p': /* function password */
	  {
	    passwd=optarg;
	    break;
	  };
        case 's': /* print stats */
          {
            stats=1;
            break;
          };
	case 'z': /* time accounting on/off */
	  {
            if (!options.timestats)
              {
                if (!(options.timestats=new bool())) { return 0; };
              };
            if (!strcmp(optarg,"on"))
              { *options.timestats=1; }
            else { *options.timestats=0; }
            break;
	  };

        default:
          {
	    return 0;
	  };
        };
    };

  if (optind>argc-1)
    {
      file=0;
    }
  else
    {
      file=argv[optind];
    };

  return 1;
}

/* set a variable */
flowctrl makevar(type_block* callblock,opt* options,
		 type_value* var,string& name)
{
  type_value* varptr;
  flowctrl call;

  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  
  call=declarevar(callblock,options,var,name);
  if (call.ctrl!=FLOW_OK)
    {
      back=getvar(callblock,options,varptr,name);
      if (back.ctrl!=FLOW_OK) { return back; };
      if (varptr->type!=(*var).type)
        {
          back.ctrl=FLOW_EXIT;
          back.error="variable "+name+
            " already defined with wrong type";
          return back;
        };
      *varptr=*var;
    };
  
  back.ctrl=FLOW_OK;
  return back;
}

/* dump control-info */
void ctrldump(flowctrl call,opt_a* ptr_a,int returntype)
{
  char buf[MAX_LINE_SIZE];
  string accname;

  setvarulong("func_status",call.ctrl);
  if (defined(call.error))
    {
      setvarstring("func_error",call.error);
    }
  else
    {
      setvarstring("func_error","");
    };
  setvarulong("func_error_line",call.line);
  autocast(call.returnval,returntype,1);
  setvarulong("func_wrong_returntype",returntype!=call.returnval.type);
  
  if (ptr_a)
    {
      for (unsigned long i=0;i!=ACCOUNTING_ANZ;i++)
	{
	  snprintf(buf,MAX_LINE_SIZE,"func_acc_%lu",i);
	  accname=buf;
	  snprintf(buf,MAX_LINE_SIZE,"%lu:%lu:%lu:%lu",
		   ptr_a->accounting[i].count,
		   ptr_a->accounting[i].count_akt,
		   ptr_a->accounting[i].count_time.tv.tv_sec,
		   ptr_a->accounting[i].count_time.tv.tv_usec);
	  setvarstring(accname,buf);
	};
    };
  
  return;
}

opt optchanges(opt* oldopt,opt* new_options)
{
  opt opts=*oldopt;
  if (new_options->allow_eval)
    { opts.allow_eval=new_options->allow_eval; };
  if (new_options->allow_extern)
    { opts.allow_extern=new_options->allow_extern; };
  if (new_options->allow_options)
    { opts.allow_options=new_options->allow_options; };
  if (new_options->timestats)
    { opts.timestats=new_options->timestats; };
  if (new_options->ptr_d)
    { opts.ptr_d=new_options->ptr_d; };
  if (new_options->ptr_h)
    { opts.ptr_h=new_options->ptr_h; };
  if (new_options->ptr_a)
    { opts.ptr_a=new_options->ptr_a; };
  return opts;
}

flowctrl declarevar(type_block* aktblock,opt* options,type_value* var,
		    string& name)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;

  /* FIXME: should we do accounting for system variables ? */
  back=acc_add(options,ACC_TYPE_VAR);
  if (back.ctrl!=FLOW_OK) { return back; };

  if (aktblock)
    {
      if (!aktblock->stack[0].variables.insert_listelem(*var,name))
	{
	  /* FIXME: acc_del(options,ACC_TYPE_VAR); ? */
	  back.ctrl=FLOW_EXIT;
	  back.error="variable already defined";
	  return back;
	};
    }
  else
    {
      if (!variables.insert_listelem(*var,name))
	{
          /* FIXME: acc_del(options,ACC_TYPE_VAR); ? */
          back.ctrl=FLOW_EXIT;
          back.error="variable already defined";
          return back;
	};
    };

  back.ctrl=FLOW_OK;
  return back;
}

flowctrl declarefunc(type_block* aktblock,opt* options,type_function* func)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  
  /* FIXME: should we do accounting for system functions ? */
  back=acc_add(options,ACC_TYPE_FUNC);
  if (back.ctrl!=FLOW_OK) { return back; };

  if (aktblock)
    {
      if (!aktblock->stack[0].functions.insert_listelem(*func,func->name))
	{
	  /* FIXME: acc_del(options,ACC_TYPE_FUNC); ? */
	  back.ctrl=FLOW_EXIT;
	  back.error="function already defined";
	  return back;
	};
    }
  else
    {
      if (!libfunctions.insert_listelem(*func,func->name))
	{
          /* FIXME: acc_del(options,ACC_TYPE_FUNC); ? */
          back.ctrl=FLOW_EXIT;
          back.error="function already defined";
          return back;
	};
    };

  back.ctrl=FLOW_OK;
  return back;
}

flowctrl getvar(type_block* aktblock,opt* options,type_value*& variable,
		string& name)
{
  options=options;
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;

  unsigned long perms=0xFFFFFFFF;

  while (1)
    {
      if (aktblock)
	{
	  /* try to find variable in the aktual block */
	  variable=aktblock->stack[0].variables.get_listelem(name);
	  if (variable)
	    {
	      if (perms & FLAG_PERMS_VAR)
		{
		  back.ctrl=FLOW_OK;
		  return back;
		}
	      else
		{
		  back.ctrl=FLOW_EXIT;
		  back.error="no permission to access variable "+name;
		  return back;
		};
	    };
	};

      /* not found -> try upper block (if it exists) */
      if (aktblock && aktblock->block)
	{
	  perms&=aktblock->perms;
	  aktblock=aktblock->block;
	}
      else
        {
	  /* system variable ? */
	  variable=variables.get_listelem(name);
	  if (variable)
	    {
	      back.ctrl=FLOW_OK;
	      return back;
	    };

          back.ctrl=FLOW_EXIT;
          back.error="variable "+name+" not found";
	  return back;
        };
    };

  /* not reached */
  return back;
}

flowctrl getfunc(type_block* aktblock,opt* options,type_function*& function,
		 string& name)
{
  options=options;
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;

  unsigned long perms=0xFFFFFFFF;

  while (1)
    {
      if (aktblock)
	{
	  /* try to find function in the aktual block */
	  function=aktblock->stack[0].functions.get_listelem(name);
	  if (function)
	    {
	      if (perms & FLAG_PERMS_FUNC)
		{
		  back.ctrl=FLOW_OK;
		  return back;
		}
	      else
		{
		  back.ctrl=FLOW_EXIT;
		  back.error="no permission to access function "+name;
		  return back;
		};
	    };
	};

      /* not found -> try upper block (if it exists) */
      if (aktblock && aktblock->block)
        {
	  perms&=aktblock->perms;
          aktblock=aktblock->block;
        }
      else
        {
	  /* search for library-functions at last */
	  function=libfunctions.get_listelem(name);
	  if (function)
	    {
              function->block=(type_block*) 1;
	      back.ctrl=FLOW_OK;
	      return back;
	    };

          back.ctrl=FLOW_EXIT;
          back.error="function "+name+" not found";
          return back;
        };
    };

  /* not reached */
  return back;
}
