/*
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

flowctrl type_function::call(type_block* callblock,opt* options,bool nofatal,
			     bool preserve,joined_list<type_value>* callargs)
{
  flowctrl call;
  string numb;
#ifdef WITH_RPC
  rpc_flowctrl* rueck;
  opt_a acc_external;
#endif
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_function::call(options)");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  opt blockopts1;
  opt* blockopts;

  if (type==TYPE_FUNCTION_NORMAL || type==TYPE_FUNCTION_EVAL)
    {
      blockopts1=optchanges(options,&myblock->new_options);
      blockopts=&blockopts1;
    };

  /* right number of args ? */
  if (type!=TYPE_FUNCTION_EXTERNAL &&
      callargs->length()!=args->args.length())
    {
      back.error="called function with wrong number of args";
      back.ctrl=FLOW_EXIT;
      acc_del(options,ACC_TYPE_RECURSION);
      return back;
    };

  /* function disabled ? */
  if (options->ptr_d->disabled.get_listelem(name))
    {
      back.error="called disabled function";
      back.ctrl=FLOW_EXIT;
      acc_del(options,ACC_TYPE_RECURSION);return back;
    };

  /* push variables for recursion and set upper block, eventually reset
     accounting */
  if (type==TYPE_FUNCTION_NORMAL || type==TYPE_FUNCTION_EVAL)
    {
      if (myblock->perms) { myblock->block=block; }
      else
	{
	  /* isolated block */
	  myblock->block=0;
	};
	    
      if (called) { myblock->push(); };

      /* if function has extra accounting: reset accounting if function is
	 not currently called */
      if (myblock->new_options.ptr_a && !called)
	{
	  for (unsigned long i=0;i!=ACCOUNTING_ANZ;i++)
	    {
	      myblock->new_options.ptr_a->accounting[i].count=0;
              myblock->new_options.ptr_a->accounting[i].count_akt=0;
              myblock->new_options.ptr_a->accounting[i].start_time=0;
              myblock->new_options.ptr_a->accounting[i].count_time=0;
	    };
	};
    };

  /* check argtype and eventually insert as variable into block */
  if (type!=TYPE_FUNCTION_EXTERNAL)
    {
      for (unsigned long i=0;i!=callargs->length();i++)
	{
	  autocast((*callargs)[i],args->args[i].argtype,1);
	  
	  if (args->args[i].argtype==(*callargs)[i].type)
	    {
	      if (type!=TYPE_FUNCTION_LIBRARY)
		{
		  back=declarevar(myblock,blockopts,&(*callargs)[i],
		                  args->args[i].argname);
		  if (back.ctrl!=FLOW_OK) { return back; };
		};
	    }
	  else
	    {
	      if (type!=TYPE_FUNCTION_LIBRARY && called) { myblock->pop(); };
	      back.error="called function with wrong type";
	      back.ctrl=FLOW_EXIT;
	      acc_del(options,ACC_TYPE_RECURSION);return back;
	    };
	};
    };

  /* call function */
  if (type==TYPE_FUNCTION_LIBRARY)
    {
      /* library function */
#ifdef DEBUGGING
      log(LOG_DEBUG,__FILE__,__LINE__,0,
	  "type_function::call(options):calling library function");
#endif
      back=acc_add(options,ACC_TYPE_LIBRARY);
      if (back.ctrl!=FLOW_OK) { return back; };
      if (called)
	{
	  call=(*library_ptr)(callblock,options,callargs);
	}
      else
	{
	  called=1;
	  call=(*library_ptr)(callblock,options,callargs);
	  called=0;
	};
      acc_del(options,ACC_TYPE_LIBRARY);
    }
  else if (type==TYPE_FUNCTION_NORMAL || type==TYPE_FUNCTION_EVAL)
    {
      /* normal function */
#ifdef DEBUGGING
      log(LOG_DEBUG,__FILE__,__LINE__,0,
	  "type_function::call(options):calling normal function");
#endif
      if (called)
	{
	  call=myblock->execute(options);
	  myblock->pop();
	}
      else
	{
	  called=1;
          call=myblock->execute(options);
	  called=0;
	};

      /* set upper block again because of recursion with same function in
	 another context */
      if (myblock->perms) { myblock->block=block; }
      else
        {
          /* isolated block */
          myblock->block=0;
        };
    }
  else if (type==TYPE_FUNCTION_EXTERNAL)
    {
#ifdef WITH_RPC

      /* external function */
#ifdef DEBUGGING
      log(LOG_DEBUG,__FILE__,__LINE__,0,
          "type_function::call(options):calling external function");
#endif

      static rpc_callargs rpcargs;

      CLIENT* clnt;
      
      /* get client handle */
      if (!options->ptr_h->rpc_clients.ishandle(external_handle))
	{
	  back.ctrl=FLOW_EXIT;
	  back.error="external function: illegal client handle";
	  return back;
	};
      clnt=options->ptr_h->rpc_clients.gethandle(external_handle);
      
      /* set name and password */
      rpcargs.function=external_function;
      rpcargs.password=external_pw;
      
      /* compose argument list */
      code_rpc_function_args(&rpcargs.args,callargs);
      
      /* let's go ! */
      if (called)
	{
#ifdef DEBUGGING
	  log(LOG_DEBUG,__FILE__,__LINE__,0,
	      "type_function::call(options):boilrpc_call_1_clnt() (called)");
#endif
	  rueck=boilrpc_call_1_clnt(&rpcargs,clnt);
	}
      else
	{
	  called=1;
#ifdef DEBUGGING
          log(LOG_DEBUG,__FILE__,__LINE__,0,
              "type_function::call(options):boilrpc_call_1_clnt()");
#endif
          rueck=boilrpc_call_1_clnt(&rpcargs,clnt);
	  called=0;
	};

#ifdef DEBUGGING
      log(LOG_DEBUG,__FILE__,__LINE__,0,
	  "type_function::call(options):boilrpc_call_1_clnt() back");
#endif

      if (!rueck)
	{
#ifdef DEBUGGING
	  log(LOG_DEBUG,__FILE__,__LINE__,0,
	      "type_function::call(options):external: no result");
#endif
          call.ctrl=FLOW_EXIT;
          call.error="external function: no result (timeout ?)";
	  /* we do not return immediately, nofatal could be set ! */
	}
      else
	{
	  if (!decode_rpc_flowctrl(rueck,&call,&acc_external))
	    {
#ifdef DEBUGGING
	      log(LOG_DEBUG,__FILE__,__LINE__,0,
		  "type_function::call(options):external: cannot decode");
#endif
	      call.ctrl=FLOW_EXIT;
	      call.error="external function: cannot decode result";
	      /* we do not return immediately, nofatal could be set ! */
	    };
 	};
#else
      call.ctrl=FLOW_EXIT;
      call.error="external functions not supported";
      /* we do not return immediately, nofatal could be set ! */
#endif
    }
  else
    {
      /* not reached (hopefully) */
    };

  back=call;

  if (nofatal)
    {
       if ((type==TYPE_FUNCTION_NORMAL || type==TYPE_FUNCTION_EVAL) &&
	  myblock->new_options.ptr_a)
        {
#ifdef DEBUGGING
	  log(LOG_DEBUG,__FILE__,__LINE__,0,
	      "type_function::call(options): dumping control info (acc)");
#endif
	  ctrldump(call,myblock->new_options.ptr_a,returntype);
	}
#ifdef WITH_RPC
       else if (type==TYPE_FUNCTION_EXTERNAL && rueck && rueck->accounting)
	 {
#ifdef DEBUGGING
	   log(LOG_DEBUG,__FILE__,__LINE__,0,
	       "type_function::call(options): dumping control info (acc_ext)");
#endif
	   ctrldump(call,&acc_external,returntype);
	 }
#endif
       else
	 {
#ifdef DEBUGGING
	   log(LOG_DEBUG,__FILE__,__LINE__,0,
	       "type_function::call(options): dumping control info");
#endif
	   ctrldump(call,0,returntype);
	 };
       
       back=call;
       if (!preserve)
	 {
 	  back.ctrl=FLOW_OK;
	  back.error=0;
	  autocast(back.returnval,returntype,1);
	  if (back.returnval.type!=returntype)
	    {
	      back.returnval.type=returntype;
	      switch (returntype)
		{
		case TYPE_VALUE_ULONG:
		  { back.returnval.val.ulongval=0; break; };
		case TYPE_VALUE_LONG:
                  { back.returnval.val.longval=0; break; };
		case TYPE_VALUE_DOUBLE:
                  { back.returnval.val.doubleval=0; break; };
		case TYPE_VALUE_STRING:
                  { back.returnval.stringval=""; break; };
		};
	    };
	};
    };
  
  if (back.ctrl==FLOW_RETURN && !preserve)
    {
      back.ctrl=FLOW_OK;
    };

  if (back.ctrl!=FLOW_OK && back.ctrl!=FLOW_RETURN)
    { acc_del(options,ACC_TYPE_RECURSION);return back; };

  /* check return-type */
  if (!preserve)
    {
      autocast(back.returnval,returntype,1);
      if (back.returnval.type!=returntype)
	{
	  back.ctrl=FLOW_EXIT;
	  back.error="function returned with wrong type";
	  acc_del(options,ACC_TYPE_RECURSION);return back;
	};
    };

  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_reference::handlecomplex(opt* options)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  long counterval=0;

  /* get name of variable */
  back=expr->eval(options);
  if (back.ctrl!=FLOW_OK) { return back; };

  /* is it a string ? */
  if (back.returnval.type!=TYPE_VALUE_STRING)
    {
      back.ctrl=FLOW_EXIT;
      back.error="expression for reference is not a string";
      return back;
    };
  if (!defined(back.returnval.stringval))
    {
      back.ctrl=FLOW_EXIT;
      back.error="expression for reference is not defined";
      return back;
    };

  name=back.returnval.stringval;

  /* get counter */
  if (counter)
    {
      back=counter->eval(options);
      if (back.ctrl!=FLOW_OK) { return back; };

      autocast(back.returnval,TYPE_VALUE_LONG,1);

      if (back.returnval.type!=TYPE_VALUE_LONG)
	{
	  back.ctrl=FLOW_EXIT;
	  back.error="counter for [] must have integer type";
	  return back;
	};
      counterval=back.returnval.val.longval;
    };
  
  perms=0xFFFFFFFF;

  /* go counter blocks up, start-block if counter < 0 */
  while (counterval>0)
    {
      if (aktblock->block)
	{
	  perms&=aktblock->perms;
	  aktblock=aktblock->block;
	  counterval--;
	}
      else
	{
	  back.ctrl=FLOW_EXIT;
	  back.error="counter for [] to high";
	  return back;
	};
    };
  if (counterval < 0)
    {
      if (counterval==-2)
	{
	  /* system variable */
	  aktblock=0;
	}
      else
	{
	  /* go to start block */
	  while (aktblock->block)
	    {
	      perms&=aktblock->perms;
	      aktblock=aktblock->block;
	    };
	};
    };
  
  back.ctrl=FLOW_OK;
  return back;
}

flowctrl type_reference::getvariable(opt* options,type_value*& variable)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
  aktblock=block;

#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_reference:getvariable()");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  if (type==TYPE_REFERENCE_COMPLEX)
    {
      back=handlecomplex(options);
      if (back.ctrl!=FLOW_OK)
	{
	  acc_del(options,ACC_TYPE_RECURSION);
	  return back;
	};
      if (!(perms & FLAG_PERMS_VAR))
	{
	  back.ctrl=FLOW_EXIT;
	  back.error="no permission to access variable "+name;
	  acc_del(options,ACC_TYPE_RECURSION);return back;
	};
    };

#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,
      "type_reference:getvariable():name=%s",(char*) name);
#endif

  back=getvar(aktblock,options,variable,name);
  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_reference::getfunction(opt* options,type_function*& function)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
  aktblock=block;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_reference:getfunction()");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  if (type==TYPE_REFERENCE_COMPLEX)
    {
      back=handlecomplex(options);
      if (back.ctrl!=FLOW_OK)
	{ acc_del(options,ACC_TYPE_RECURSION);return back; };
      if (!(perms & FLAG_PERMS_FUNC))
        {
          back.ctrl=FLOW_EXIT;
          back.error="no permission to access function "+name;
          acc_del(options,ACC_TYPE_RECURSION);return back;
        };
    };

#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,
      "type_reference:getfunction():name=%s",(char*) name);
#endif

  back=getfunc(aktblock,options,function,name);
  if (back.ctrl==FLOW_OK && function->block==(type_block*) 1)
    {
      /* upper block of (normal) system function is calling block */
      function->block=block;
    };
  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_reference::declarevariable(opt* options,type_value* value)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
  aktblock=block;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_reference:declarevariable()");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };
  
  if (type==TYPE_REFERENCE_COMPLEX)
    {
      back=handlecomplex(options);
      if (back.ctrl!=FLOW_OK)
	{ acc_del(options,ACC_TYPE_RECURSION);return back; };
      if (!(perms & FLAG_PERMS_DECLARE_VAR))
        {
          back.ctrl=FLOW_EXIT;
          back.error="no permission to declare variable "+name;
          acc_del(options,ACC_TYPE_RECURSION);return back;
        };
    };

#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,
      "type_reference:declarevariable():name=%s",(char*) name);
#endif

  /* define it */
  back=declarevar(aktblock,options,value,name);
  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_reference::declarefunction(opt* options,type_function* function)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
  aktblock=block;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_reference:declarefunction()");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  if (type==TYPE_REFERENCE_COMPLEX)
    {
      back=handlecomplex(options);
      if (back.ctrl!=FLOW_OK)
	{ acc_del(options,ACC_TYPE_RECURSION);return back; };
      if (!(perms & FLAG_PERMS_DECLARE_FUNC))
        {
          back.ctrl=FLOW_EXIT;
          back.error="no permission to declare function "+name;
          acc_del(options,ACC_TYPE_RECURSION);return back;
        };
    };

#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,
      "type_reference:declarefunction():name=%s",(char*) name);
#endif

  /* define it */
  function->name=name;
  function->block=aktblock;
  back=declarefunc(aktblock,options,function);
  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_function_call::call(opt* options)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
  joined_list<type_value> args;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_function_call:call(options)");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  /* get arguments */
  for (unsigned long i=0;i!=expr_list->exprs.length();i++)
    {
      back=expr_list->exprs[i].eval(options);
      if (back.ctrl!=FLOW_OK)
	{ acc_del(options,ACC_TYPE_RECURSION);return back; };
      args.insert_listelem(back.returnval,args.length());
    };

  /* get function */
  type_function* ptr;
  back=reference->getfunction(options,ptr);
  if (back.ctrl!=FLOW_OK) { acc_del(options,ACC_TYPE_RECURSION);return back; };

  /* call function */
  back=ptr->call(block,options,nofatal,0,&args);
  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_expr::eval(opt* options)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_expr: eval(options)");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  switch (type)
    {
    case TYPE_EXPR_CONST:
      {
	back.ctrl=FLOW_OK;
	back.returnval=*(uptr.constant);
	acc_del(options,ACC_TYPE_RECURSION);return back;

	/* not reached */
      };
    case TYPE_EXPR_REFERENCE:
      {
	type_value* ptr;
	back=uptr.reference->getvariable(options,ptr);
	if (back.ctrl!=FLOW_OK)
	  { acc_del(options,ACC_TYPE_RECURSION);return back; };

	back.returnval=*ptr;
	acc_del(options,ACC_TYPE_RECURSION);return back;

        /* not reached */
      };
    case TYPE_EXPR_FUNCTION_CALL:
      {
	back=uptr.function_call->call(options);
	acc_del(options,ACC_TYPE_RECURSION);return back;

        /* not reached */
      };
    case TYPE_EXPR_CAST:
      {
	char buf[MAX_LINE_SIZE];

        back=expr1->eval(options);
	if (back.ctrl!=FLOW_OK)
	  { acc_del(options,ACC_TYPE_RECURSION);return back; };
	
	if (back.returnval.type==TYPE_VALUE_VOID || cast==TYPE_VALUE_VOID)
	  {
	    back.ctrl=FLOW_EXIT;
	    back.error="cannot cast from or to type void";
	    acc_del(options,ACC_TYPE_RECURSION);return back;
	  };

	if (cast==TYPE_VALUE_LONG &&
	    back.returnval.type==TYPE_VALUE_DOUBLE)
	  {
	    back.returnval.type=TYPE_VALUE_LONG;
	    back.returnval.val.longval=(long) back.returnval.val.doubleval;
	  }
	else if (cast==TYPE_VALUE_LONG &&
		 back.returnval.type==TYPE_VALUE_STRING)
	  {
	    back.returnval.type=TYPE_VALUE_LONG;
	    if (!defined(back.returnval.stringval))
	      {
                back.returnval.val.longval=0;
                setvarulong("error",1);
                setvarstring("perror","cast from undefined string");
	      }
	    else if (sscanf(back.returnval.stringval,"%li",
			    &back.returnval.val.longval)!=1)
	      {
		back.returnval.val.longval=0;
		setvarulong("error",1);
		setvarstring("perror","cast from string to long failed");
	      };
	  }
        else if (cast==TYPE_VALUE_DOUBLE &&
                 back.returnval.type==TYPE_VALUE_LONG)
          {
	    back.returnval.type=TYPE_VALUE_DOUBLE;
	    back.returnval.val.doubleval=(double)  back.returnval.val.longval;
          }
        else if (cast==TYPE_VALUE_DOUBLE &&
                 back.returnval.type==TYPE_VALUE_STRING)
          {
            back.returnval.type=TYPE_VALUE_DOUBLE;
           if (!defined(back.returnval.stringval))
	     {
                back.returnval.val.doubleval=0;
                setvarulong("error",1);
                setvarstring("perror","cast from undefined string");
	     }
	   else if (sscanf(back.returnval.stringval,"%lf",
			   &back.returnval.val.doubleval)!=1)
              {
                back.returnval.val.doubleval=0;
                setvarulong("error",1);
                setvarstring("perror","cast from string to double failed");
              };
          }
        else if (cast==TYPE_VALUE_STRING &&
                 back.returnval.type==TYPE_VALUE_LONG)
          {
	    back.returnval.type=TYPE_VALUE_STRING;
	    snprintf(buf,MAX_LINE_SIZE,"%ld",back.returnval.val.longval);
	    back.returnval.stringval=buf;
          }
        else if (cast==TYPE_VALUE_STRING &&
                 back.returnval.type==TYPE_VALUE_DOUBLE)
          {
            back.returnval.type=TYPE_VALUE_STRING;
            snprintf(buf,MAX_LINE_SIZE,"%lf",back.returnval.val.doubleval);
            back.returnval.stringval=buf;
          }
	else if (cast==TYPE_VALUE_ULONG &&
		 back.returnval.type==TYPE_VALUE_LONG)
	  {
	    back.returnval.type=TYPE_VALUE_ULONG;
	    back.returnval.val.ulongval=
	      (unsigned long) back.returnval.val.longval;
	  }
        else if (cast==TYPE_VALUE_ULONG &&
                 back.returnval.type==TYPE_VALUE_DOUBLE)
          {
	    back.returnval.type=TYPE_VALUE_ULONG;
            back.returnval.val.ulongval=
	      (unsigned long) back.returnval.val.doubleval;
          }
        else if (cast==TYPE_VALUE_ULONG &&
                 back.returnval.type==TYPE_VALUE_STRING)
          {
	    back.returnval.type=TYPE_VALUE_ULONG;
           if (!defined(back.returnval.stringval))
	     {
                back.returnval.val.ulongval=0;
                setvarulong("error",1);
                setvarstring("perror","cast from undefined string");
	     }
	   else if (sscanf(back.returnval.stringval,"%lu",
			   &back.returnval.val.ulongval)!=1)
              {
                back.returnval.val.ulongval=0;
                setvarulong("error",1);
                setvarstring("perror",
			     "cast from string to unsigned long failed");
              };
          }
        else if (cast==TYPE_VALUE_LONG &&
                 back.returnval.type==TYPE_VALUE_ULONG)
          {
	    back.returnval.type=TYPE_VALUE_LONG;
	    back.returnval.val.longval=(long) back.returnval.val.ulongval;
          }
        else if (cast==TYPE_VALUE_DOUBLE &&
                 back.returnval.type==TYPE_VALUE_ULONG)
          {
	    back.returnval.type=TYPE_VALUE_DOUBLE;
            back.returnval.val.doubleval=(double) back.returnval.val.ulongval;
          }
        else if (cast==TYPE_VALUE_STRING &&
                 back.returnval.type==TYPE_VALUE_ULONG)
          {
	    back.returnval.type=TYPE_VALUE_STRING;
            snprintf(buf,MAX_LINE_SIZE,"%lu",back.returnval.val.ulongval);
            back.returnval.stringval=buf;
          }

	acc_del(options,ACC_TYPE_RECURSION);return back;
        /* not reached */
      };
    case TYPE_EXPR_ASSIGNMENT:
      {
	type_value* ptr;
	back=uptr.reference->getvariable(options,ptr);
	if (back.ctrl!=FLOW_OK)
	  { acc_del(options,ACC_TYPE_RECURSION);return back; };
	
	back=expr1->eval(options);
	if (back.ctrl!=FLOW_OK)
	  { acc_del(options,ACC_TYPE_RECURSION);return back; };

	autocast(back.returnval,ptr->type,1);

	/* normal case */
	if (back.returnval.type==ptr->type)
	  {
	    *ptr=back.returnval;
	    acc_del(options,ACC_TYPE_RECURSION);return back;
	  };

	back.ctrl=FLOW_EXIT;	back.error="assignment with different types";
	acc_del(options,ACC_TYPE_RECURSION);return back;

        /* not reached */
      };
    case TYPE_EXPR_POSTINKREMENT:
      {
	type_value* ptr;
	back=uptr.reference->getvariable(options,ptr);
	if (back.ctrl!=FLOW_OK)
	  { acc_del(options,ACC_TYPE_RECURSION);return back; };
	
	if (ptr->type == TYPE_VALUE_STRING ||
	    ptr->type == TYPE_VALUE_VOID)
	  {
	    back.ctrl=FLOW_EXIT;
	    back.error="wrong type for postinkrement";
	    acc_del(options,ACC_TYPE_RECURSION);return back;
	  };

	back.returnval=*ptr;
	switch(ptr->type)
	  {
	  case TYPE_VALUE_ULONG:
	    { ptr->val.ulongval++; break; };
	  case TYPE_VALUE_LONG:
	    { ptr->val.longval++; break; };
	  case TYPE_VALUE_DOUBLE:
	    { ptr->val.doubleval++; break; };
	  };
	acc_del(options,ACC_TYPE_RECURSION);return back;
	
	/* not reached */
      };
    case TYPE_EXPR_POSTDEKREMENT:
      {
	type_value* ptr;
	back=uptr.reference->getvariable(options,ptr);
	if (back.ctrl!=FLOW_OK)
	  { acc_del(options,ACC_TYPE_RECURSION);return back; };
	

        if (ptr->type == TYPE_VALUE_STRING ||
            ptr->type == TYPE_VALUE_VOID)
          {
	    back.ctrl=FLOW_EXIT;
	    back.error="wrong type for postdekrement";
	    acc_del(options,ACC_TYPE_RECURSION);return back;
          };

        back.returnval=*ptr;
        switch(ptr->type)
          {
          case TYPE_VALUE_ULONG:
            { ptr->val.ulongval--; break; };
          case TYPE_VALUE_LONG:
            { ptr->val.longval--; break; };
          case TYPE_VALUE_DOUBLE:
            { ptr->val.doubleval--; break; };
          };
        acc_del(options,ACC_TYPE_RECURSION);return back;
	
	/* not reached */
      };
    case TYPE_EXPR_PREINCREMENT:
      {
        type_value* ptr;
        back=uptr.reference->getvariable(options,ptr);
        if (back.ctrl!=FLOW_OK)
	  { acc_del(options,ACC_TYPE_RECURSION);return back; };
        
        if (ptr->type == TYPE_VALUE_STRING ||
            ptr->type == TYPE_VALUE_VOID)
          {
	    back.ctrl=FLOW_EXIT;
	    back.error="wrong type for preinkrement";
	    acc_del(options,ACC_TYPE_RECURSION);return back;
          };

        switch(ptr->type)
          {
          case TYPE_VALUE_ULONG:
            { ptr->val.ulongval++; break; };
          case TYPE_VALUE_LONG:
            { ptr->val.longval++; break; };
          case TYPE_VALUE_DOUBLE:
            { ptr->val.doubleval++; break; };
          };

        back.returnval=*ptr;
        acc_del(options,ACC_TYPE_RECURSION);return back;
        
	/* not reached */
      };
    case TYPE_EXPR_PREDECREMENT:
      {
        type_value* ptr;
        back=uptr.reference->getvariable(options,ptr);
        if (back.ctrl!=FLOW_OK)
	  { acc_del(options,ACC_TYPE_RECURSION);return back; };
        
        if (ptr->type == TYPE_VALUE_STRING ||
            ptr->type == TYPE_VALUE_VOID)
          {
	    back.ctrl=FLOW_EXIT;
	    back.error="wrong type for predekrement";
	    acc_del(options,ACC_TYPE_RECURSION);return back;
          };

        switch(ptr->type)
          {
          case TYPE_VALUE_ULONG:
            { ptr->val.ulongval--; break; };
          case TYPE_VALUE_LONG:
            { ptr->val.longval--; break; };
          case TYPE_VALUE_DOUBLE:
            { ptr->val.doubleval--; break; };
          };

        back.returnval=*ptr;
        acc_del(options,ACC_TYPE_RECURSION);return back;
        
        /* not reached */
      };
    case TYPE_EXPR_EXPR:
      {
	back=expr1->eval(options);
	acc_del(options,ACC_TYPE_RECURSION);return back;

        /* not reached */
      };
    case TYPE_EXPR_OPERATOR_ONE:
      {
	back=expr1->eval(options);
	if (back.ctrl!=FLOW_OK)
	  { acc_del(options,ACC_TYPE_RECURSION);return back; };

	switch (optype)
	  {
	  case TYPE_OPERATOR_ONE_NEG_AR:
	    {
	      back.returnval=(-back.returnval);
	      break;
	    };
          case TYPE_OPERATOR_ONE_NEG_BOOL:
            {
              back.returnval=(!back.returnval);
              break;
            };
          case TYPE_OPERATOR_ONE_NEG_BIT:
            {
              back.returnval=(~back.returnval);
              break;
            };
	  default:
	    {
	      /* not reached (hopefully) */
	    };
	  };

	if (back.returnval.type==TYPE_VALUE_VOID)
	  {
	    back.ctrl=FLOW_EXIT;
	    back.error=back.returnval.stringval;
	  };
	acc_del(options,ACC_TYPE_RECURSION);return back;
	
        /* not reached */
      };
    case TYPE_EXPR_OPERATOR_TWO:
      {
	type_value value1;
        type_value value2;
        back=expr1->eval(options);
        if (back.ctrl!=FLOW_OK)
	  { acc_del(options,ACC_TYPE_RECURSION);return back; };
	value1=back.returnval;

        switch (optype)
          {
          case TYPE_OPERATOR_TWO_EQUAL:
            {
	      back=expr2->eval(options);
	      if (back.ctrl!=FLOW_OK)
		{ acc_del(options,ACC_TYPE_RECURSION);return back; };
	      value2=back.returnval;

	      back.returnval=(value1==value2);
              break;
            };
          case TYPE_OPERATOR_TWO_NOTEQUAL:
            {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=(value1!=value2);
              break;
            };
          case TYPE_OPERATOR_TWO_PLUS:
            {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=value1+value2;
              break;
            };
	  case TYPE_OPERATOR_TWO_MULT:
	    {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=value1*value2;
              break;
	    };
          case TYPE_OPERATOR_TWO_DIV:
            {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=value1/value2;
              break;
            };
          case TYPE_OPERATOR_TWO_MODULO:
            {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=value1%value2;
              break;
            };
          case TYPE_OPERATOR_TWO_MINUS:
            {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=value1-value2;
              break;
            };
          case TYPE_OPERATOR_TWO_SHIFTLEFT:
            {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=value1<<value2;
              break;
            };
          case TYPE_OPERATOR_TWO_SHIFTRIGHT:
            {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=value1>>value2;
              break;
            };
          case TYPE_OPERATOR_TWO_LOWER:
            {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=value1<value2;
              break;
            };
          case TYPE_OPERATOR_TWO_GREATER:
            {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=value1>value2;
              break;
            };
          case TYPE_OPERATOR_TWO_LE:
            {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=value1<=value2;
              break;
            };
          case TYPE_OPERATOR_TWO_GE:
            {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=value1>=value2;
              break;
            };
          case TYPE_OPERATOR_TWO_AND_BIT:
            {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=value1&value2;
              break;
            };
          case TYPE_OPERATOR_TWO_EOR_BIT:
            {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=value1^value2;
              break;
            };
          case TYPE_OPERATOR_TWO_OR_BIT:
            {
              back=expr2->eval(options);
              if (back.ctrl!=FLOW_OK)
                { acc_del(options,ACC_TYPE_RECURSION);return back; };
              value2=back.returnval;

              back.returnval=value1|value2;
              break;
            };
          case TYPE_OPERATOR_TWO_AND_BOOL:
            {
	      unsigned long rueck=0;

	      if (value1.type==TYPE_VALUE_VOID ||
		  value1.type==TYPE_VALUE_STRING)
		{
		  back.ctrl=FLOW_EXIT;
		  back.error="wrong datatype for AND-expression";
		  acc_del(options,ACC_TYPE_RECURSION);return back;
		};
	      if ((value1.type==TYPE_VALUE_LONG &&
		   value1.val.longval) ||
		  (value1.type==TYPE_VALUE_ULONG &&
		   value1.val.ulongval) ||
		  (value1.type==TYPE_VALUE_DOUBLE &&
		   value1.val.doubleval))
		{
		  back=expr2->eval(options);
		  if (back.ctrl!=FLOW_OK)
		    { acc_del(options,ACC_TYPE_RECURSION);return back; };
		  value2=back.returnval;

		  if (value2.type==TYPE_VALUE_VOID ||
		      value2.type==TYPE_VALUE_STRING)
		    {
		      back.ctrl=FLOW_EXIT;
		      back.error="wrong datatype for AND-expression";
		      acc_del(options,ACC_TYPE_RECURSION);return back;
		    };
		  if ((value2.type==TYPE_VALUE_LONG &&
		       value2.val.longval) ||
		      (value2.type==TYPE_VALUE_ULONG &&
		       value2.val.ulongval) ||
		      (value2.type==TYPE_VALUE_DOUBLE &&
		       value2.val.doubleval))
		    {
		      rueck=1;
		    };
		};

	      back.returnval.type=TYPE_VALUE_ULONG;
	      back.returnval.val.ulongval=rueck;
              break;
            };
          case TYPE_OPERATOR_TWO_OR_BOOL:
            {
              unsigned long rueck=0;

              if (value1.type==TYPE_VALUE_VOID ||
                  value1.type==TYPE_VALUE_STRING)
                {
                  back.ctrl=FLOW_EXIT;
                  back.error="wrong datatype for OR-expression";
                  acc_del(options,ACC_TYPE_RECURSION);return back;
                };
              if ((value1.type==TYPE_VALUE_LONG &&
                   value1.val.longval) ||
                  (value1.type==TYPE_VALUE_ULONG &&
                   value1.val.ulongval) ||
                  (value1.type==TYPE_VALUE_DOUBLE &&
                   value1.val.doubleval))
                {
		  rueck=1;
		}
	      else
		{
                  back=expr2->eval(options);
                  if (back.ctrl!=FLOW_OK)
                    { acc_del(options,ACC_TYPE_RECURSION);return back; };
                  value2=back.returnval;
		  
                  if (value2.type==TYPE_VALUE_VOID ||
                      value2.type==TYPE_VALUE_STRING)
                    {
                      back.ctrl=FLOW_EXIT;
                      back.error="wrong datatype for OR-expression";
                      acc_del(options,ACC_TYPE_RECURSION);return back;
                    };
                  if ((value2.type==TYPE_VALUE_LONG &&
                       value2.val.longval) ||
                      (value2.type==TYPE_VALUE_ULONG &&
                       value2.val.ulongval) ||
                      (value2.type==TYPE_VALUE_DOUBLE &&
                       value2.val.doubleval))
                    {
                      rueck=1;
                    };
		};

              back.returnval.type=TYPE_VALUE_ULONG;
              back.returnval.val.ulongval=rueck;
	      break;
            };
	  default:
	    {
	      /* not reached (hopefully) */
	    };
	  };

        if (back.returnval.type==TYPE_VALUE_VOID)
          {
            back.ctrl=FLOW_EXIT;
            back.error=back.returnval.stringval;
          };
        acc_del(options,ACC_TYPE_RECURSION);return back;

	/* not reached */
      };
    default:
      {
	/* not reached (hopefully) */
      };
    };
  
  /* not reached (hopefully) */
  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_function_dekl::declare(opt* options)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
  type_function func;
  opt newopts;
  unsigned long perms;
  string passwd=0;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_function_dekl:declare(options)");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  func.called=0;

  if (type==TYPE_FUNC_DEKL_EXTERNAL)
    {
      func.type=TYPE_FUNCTION_EXTERNAL;
      func.returntype=returntype;
    }
  else if (type==TYPE_FUNC_DEKL_EVAL)
    {
      func.type=TYPE_FUNCTION_EVAL;
      func.returntype=returntype;
      func.args=args;

      /* reset parser */
      reset_parser();
    }
  else if (type==TYPE_FUNC_DEKL_NORMAL)
    {
      func.type=TYPE_FUNCTION_NORMAL;
      func.returntype=returntype;
      func.args=args;
    };

  if (opt_expr)
    {
      if (!(*(options->allow_options)))
	{
	  back.ctrl=FLOW_EXIT;
	  back.error="options for function declaration not allowed";
	  acc_del(options,ACC_TYPE_RECURSION);return back;
	};
      
      char* optptr;
      int argc=1;
      char* argv[MAX_OPTIONS];

      /* parse options */
      back=opt_expr->eval(options);
      if (back.ctrl!=FLOW_OK)
	{ acc_del(options,ACC_TYPE_RECURSION);return back; };
      if (back.returnval.type!=TYPE_VALUE_STRING)
	{
	  back.ctrl=FLOW_EXIT;
	  back.error="options for function declaration must have string type";
	  acc_del(options,ACC_TYPE_RECURSION);return back;
	};
      /* tokenize options */
      optptr=back.returnval.stringval;
      while ((argv[argc]=strsep(&optptr," ")))
	{ 
	  argc++;
	  if (argc==MAX_OPTIONS)
	    {
	      back.ctrl=FLOW_EXIT;
	      back.error="to much options";
	      acc_del(options,ACC_TYPE_RECURSION);return back;
	    };
	};
      if (!get_options(argc,argv,"a:x:c:p:f:r:e:t:o:z:qhil",perms,passwd,
		       options,newopts,1))
	{
	  back.ctrl=FLOW_EXIT;
	  back.error="syntax error in options for function (or out of mem)";
	  acc_del(options,ACC_TYPE_RECURSION);return back;
	};
    };

  func.passwd=passwd;

  /* FIXME: on errors free myblock when eval_stmt and newopts */

  if (type==TYPE_FUNC_DEKL_NORMAL)
    {
      func.myblock=myblock;
    }
  else if (type==TYPE_FUNC_DEKL_EVAL)
    {
      /* we cannot declare functions in upper blocks */
      if (perms & FLAG_PERMS_DECLARE_FUNC)
	{ perms^=FLAG_PERMS_DECLARE_FUNC; };
      
      back=eval_stmt->makeblock(options,func.myblock);
      if (back.ctrl!=FLOW_OK)
	{ acc_del(options,ACC_TYPE_RECURSION);return back; };
    }
  else if (type==TYPE_FUNC_DEKL_EXTERNAL)
    {
      back=external_stmt->getdata(options,
				  func.external_handle,
				  func.external_function,
				  func.external_pw);
      if (back.ctrl!=FLOW_OK)
        { acc_del(options,ACC_TYPE_RECURSION);return back; };
    }
  else     
    {
      /* not reached */
    };

  if (opt_expr)
    {
      func.myblock->perms=perms;
      func.myblock->new_options=newopts;
     };

  back=reference->declarefunction(options,&func);

  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_dekl_list_entry::declare(opt* options,int datatype)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
  type_value value;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_dekl_list_entry::declare(options)");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  value.type=datatype;

  if (type==TYPE_DEKL_LIST_ENTRY_REFINIT)
    {
      back=expr->eval(options);
      if (back.ctrl!=FLOW_OK)
	{ acc_del(options,ACC_TYPE_RECURSION);return back; };

      autocast(back.returnval,value.type,1);

      if (value.type==back.returnval.type)
	{
	  value=back.returnval;
	}
      else
	{
	  back.ctrl=FLOW_EXIT;
	  back.error="variable initialized with wrong type";
	  return back;
	};
    };

  back=reference->declarevariable(options,&value);
  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_variable_dekl::declare(opt* options)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_variable_dekl::declare(options)");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };
  
  for (unsigned long i=0;i!=dekl_list->entrys.length();i++)
    {
      back=dekl_list->entrys[i].declare(options,vartype);
      if (back.ctrl!=FLOW_OK)
	{ acc_del(options,ACC_TYPE_RECURSION);return back; };
    };
  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_expr_stmt::execute(opt* options)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
  back.ctrl=FLOW_OK;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_expr_stmt::execute(options)");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  if (type==TYPE_EXPR_STMT_DEFINED)
    {
      back=expr->eval(options);
    };

  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_choice_stmt::execute(opt* options)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_choice_stmt::execute(options)");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  back=expr->eval(options);
  if (back.ctrl!=FLOW_OK) { acc_del(options,ACC_TYPE_RECURSION);return back; };

  if (back.returnval.type==TYPE_VALUE_VOID ||
      back.returnval.type==TYPE_VALUE_STRING)
    {
      back.ctrl=FLOW_EXIT;
      back.error="wrong datatype for if-statement";
      acc_del(options,ACC_TYPE_RECURSION);return back;
    };

  if ((back.returnval.type==TYPE_VALUE_LONG &&
       back.returnval.val.longval) ||
      (back.returnval.type==TYPE_VALUE_ULONG &&
       back.returnval.val.ulongval) ||
      (back.returnval.type==TYPE_VALUE_DOUBLE &&
       back.returnval.val.doubleval))
    {
      back=stmt1->execute(options);
    }
  else if (type==TYPE_CHOICE_STMT_ELSE)
    {
      back=stmt2->execute(options);
    };

  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_loop_stmt::execute(opt* options)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_loop_stmt::execute(options)");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  switch (type)
    {
    case TYPE_LOOP_STMT_WHILE:
      {
	while (1)
	  {
#ifdef DEBUGGING
	    log(LOG_DEBUG,__FILE__,__LINE__,0,"while_loop");
#endif
	    back=expr->eval(options);
	    if (back.ctrl!=FLOW_OK)
	      { acc_del(options,ACC_TYPE_RECURSION);return back; };
	    
	    if (back.returnval.type==TYPE_VALUE_VOID ||
		back.returnval.type==TYPE_VALUE_STRING)
	      {
		back.ctrl=FLOW_EXIT;
		back.error="wrong datatype for while-statement";
		acc_del(options,ACC_TYPE_RECURSION);return back;
	      };
	    
	    if ((back.returnval.type==TYPE_VALUE_LONG &&
		 back.returnval.val.longval) ||
		(back.returnval.type==TYPE_VALUE_ULONG &&
		 back.returnval.val.ulongval) ||
		(back.returnval.type==TYPE_VALUE_DOUBLE &&
		 back.returnval.val.doubleval))
	      {
		back=stmt->execute(options);
		if (back.ctrl!=FLOW_OK)
		  {
		    if (back.ctrl==FLOW_BREAK)
		      {
			back.ctrl=FLOW_OK;
			acc_del(options,ACC_TYPE_RECURSION);return back;
		      };
		    if (back.ctrl!=FLOW_CONTINUE)
		      {
			acc_del(options,ACC_TYPE_RECURSION);return back;
		      };
		  };
	      }
	    else
	      {
		back.ctrl=FLOW_OK;
		acc_del(options,ACC_TYPE_RECURSION);return back;
	      };
	  };

	/* not reached */
      };
    case TYPE_LOOP_STMT_DO:
      {
        while (1)
          {
	    back=stmt->execute(options);
	    if (back.ctrl!=FLOW_OK)
	      {
		if (back.ctrl==FLOW_BREAK)
		  {
		    back.ctrl=FLOW_OK;
		    acc_del(options,ACC_TYPE_RECURSION);return back;
		  };
		if (back.ctrl!=FLOW_CONTINUE)
		  {
		    acc_del(options,ACC_TYPE_RECURSION);return back;
		  };
	      };

            back=expr->eval(options);
            if (back.ctrl!=FLOW_OK)
	      { acc_del(options,ACC_TYPE_RECURSION);return back; };
            
            if (back.returnval.type==TYPE_VALUE_VOID ||
		back.returnval.type==TYPE_VALUE_STRING)
              {
                back.ctrl=FLOW_EXIT;
                back.error="wrong datatype for do-statement";
                acc_del(options,ACC_TYPE_RECURSION);return back;
              };
            
	    if ((back.returnval.type==TYPE_VALUE_LONG &&
		 back.returnval.val.longval) ||
		(back.returnval.type==TYPE_VALUE_ULONG &&
		 back.returnval.val.ulongval) ||
		(back.returnval.type==TYPE_VALUE_DOUBLE &&
		 back.returnval.val.doubleval))
              {
		back.ctrl=FLOW_OK;
		acc_del(options,ACC_TYPE_RECURSION);return back;
	      };
	  };

        /* not reached */
      };
    case TYPE_LOOP_STMT_FOR:
      {
	back=stmt_for->execute(options);
	if (back.ctrl!=FLOW_OK)
	  { acc_del(options,ACC_TYPE_RECURSION);return back; };
	
        while (1)
          {
#ifdef DEBUGGING
            log(LOG_DEBUG,__FILE__,__LINE__,0,"for_loop");
#endif
	    if (expr)
	      {
		back=expr->eval(options);
		if (back.ctrl!=FLOW_OK)
		  { acc_del(options,ACC_TYPE_RECURSION);return back; };
		
		if (back.returnval.type==TYPE_VALUE_VOID ||
		    back.returnval.type==TYPE_VALUE_STRING)
		  {
		    back.ctrl=FLOW_EXIT;
		    back.error="wrong datatype for for-statement";
		    acc_del(options,ACC_TYPE_RECURSION);return back;
		  };
	      };
	    
            if (!expr ||
		(back.returnval.type==TYPE_VALUE_LONG &&
		 back.returnval.val.longval) ||
		(back.returnval.type==TYPE_VALUE_ULONG &&
		 back.returnval.val.ulongval) ||
		(back.returnval.type==TYPE_VALUE_DOUBLE &&
		 back.returnval.val.doubleval))
	      {
                back=stmt->execute(options);
                if (back.ctrl!=FLOW_OK)
                  {
                    if (back.ctrl==FLOW_BREAK)
                      {
                        back.ctrl=FLOW_OK;
                        acc_del(options,ACC_TYPE_RECURSION);return back;
                      };
                    if (back.ctrl!=FLOW_CONTINUE)
                      {
                        acc_del(options,ACC_TYPE_RECURSION);return back;
                      };
                  };
		
	      }
	    else if (expr)
	      {
		back.ctrl=FLOW_OK;
                acc_del(options,ACC_TYPE_RECURSION);return back;
	      };
		
	    if (expr_for)
	      {
		back=expr_for->eval(options);
                if (back.ctrl!=FLOW_OK)
                  {
                    if (back.ctrl==FLOW_BREAK)
                      {
                        back.ctrl=FLOW_OK;
                        acc_del(options,ACC_TYPE_RECURSION);return back;
                      };
                    if (back.ctrl!=FLOW_CONTINUE)
                      {
                        acc_del(options,ACC_TYPE_RECURSION);return back;
                      };
                  };
	      };
	  };
      };
    default:
      {
	
        /* not reached (hopefully) */
      };
    };
  
  /* not reached (hopefully) */
  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_jump_stmt::execute(opt* options)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_jump_stmt::execute(options)");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  switch (type)
    {
    case TYPE_JUMP_STMT_BREAK:
      {
	back.ctrl=FLOW_BREAK;
	acc_del(options,ACC_TYPE_RECURSION);return back;

	/* not reached */
      };
    case TYPE_JUMP_STMT_CONTINUE:
      {
        back.ctrl=FLOW_CONTINUE;
        acc_del(options,ACC_TYPE_RECURSION);return back;

        /* not reached */
      };
    case TYPE_JUMP_STMT_RETURN:
      {
        back.ctrl=FLOW_RETURN;
	back.returnval.type=TYPE_VALUE_VOID;
        acc_del(options,ACC_TYPE_RECURSION);return back;

        /* not reached */
      };
    case TYPE_JUMP_STMT_RETURNVAL:
      {
	back=expr->eval(options);
	if (back.ctrl!=FLOW_OK)
	  { acc_del(options,ACC_TYPE_RECURSION);return back; };
	back.ctrl=FLOW_RETURN;
	acc_del(options,ACC_TYPE_RECURSION);return back;

        /* not reached */
      };
    default:
      {
	/* not reached (hopefully) */
      };
    };

  /* not reached (hopefully) */
  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_eval_stmt::makeblock(opt* options,type_block*& blockptr)
{
  string code;
  type_block* eval_block;

  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_eval_stmt::makeblock(options)");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  /* eval allowed ? */
  if (!(*(options->allow_eval)))
    {
      back.ctrl=FLOW_EXIT;
      back.error="eval() not allowed";
      acc_del(options,ACC_TYPE_RECURSION);return back;
    };

  /* get code */
  back=expr_code->eval(options);
  if (back.ctrl!=FLOW_OK) { acc_del(options,ACC_TYPE_RECURSION);return back; };
  if (back.returnval.type!=TYPE_VALUE_STRING)
    {
      back.ctrl=FLOW_EXIT;
      back.error="code for eval() must have string type";
      acc_del(options,ACC_TYPE_RECURSION);return back;
    };
  if (!defined(back.returnval.stringval))
    {
      back.ctrl=FLOW_EXIT;
      back.error="code for eval() undefined";
      acc_del(options,ACC_TYPE_RECURSION);return back;
    };

  code=back.returnval.stringval;

  /* parser variables */
  ::file="eval(string)";
  filei=0;
  prgstr=code;

  if (yyparse())
    {
      freetokenstack();
      emptystack();
      back.ctrl=FLOW_EXIT;
      back.error="parsing error";
      acc_del(options,ACC_TYPE_RECURSION);return back;
    };
  emptystack();

  eval_block=yyval.t18;

  eval_block->setblock(NULL);

  /* we got it ! */
  blockptr=eval_block;
  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_external_stmt::getdata(opt* options,
				     unsigned long& handle,
				     string& function,
				     string& pw)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_external_stmt::getdata(options)");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  /* external functions allowed ? */
  if (!(*(options->allow_extern)))
    {
      back.ctrl=FLOW_EXIT;
      back.error="external functions not allowed";
      acc_del(options,ACC_TYPE_RECURSION);return back;
    };

  /* get handle */
  back=expr_handle->eval(options);
  if (back.ctrl!=FLOW_OK) { acc_del(options,ACC_TYPE_RECURSION);return back; };
  autocast(back.returnval,TYPE_VALUE_ULONG,1);
  if (back.returnval.type!=TYPE_VALUE_ULONG)
    {
      back.ctrl=FLOW_EXIT;
      back.error="host for external() must have integer type";
      acc_del(options,ACC_TYPE_RECURSION);return back;
    };
  handle=back.returnval.val.ulongval;

  /* get function */
  back=expr_function->eval(options);
  if (back.ctrl!=FLOW_OK) { acc_del(options,ACC_TYPE_RECURSION);return back; };
  if (back.returnval.type!=TYPE_VALUE_STRING)
    {
      back.ctrl=FLOW_EXIT;
      back.error="function for external() must have string type";
      acc_del(options,ACC_TYPE_RECURSION);return back;
    };
  if (!defined(back.returnval.stringval))
    {
      back.ctrl=FLOW_EXIT;
      back.error="function name for external() undefined";
      acc_del(options,ACC_TYPE_RECURSION);return back;
    };
  function=back.returnval.stringval;

  /* get pw */
  back=expr_pw->eval(options);
  if (back.ctrl!=FLOW_OK) { acc_del(options,ACC_TYPE_RECURSION);return back; };
  if (back.returnval.type!=TYPE_VALUE_STRING)
    {
      back.ctrl=FLOW_EXIT;
      back.error="password for external() must have string type";
      acc_del(options,ACC_TYPE_RECURSION);return back;
    };
  if (!defined(back.returnval.stringval))
    {
      back.ctrl=FLOW_EXIT;
      back.error="password for external() undefined";
      acc_del(options,ACC_TYPE_RECURSION);return back;
    };

  pw=back.returnval.stringval;

  acc_del(options,ACC_TYPE_RECURSION);
  return back;
}

flowctrl type_stmt::execute(opt* options)
{
  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_stmt::execute(options)");
#endif
  back=acc_add(options,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };

  switch (type)
    {
    case TYPE_STMT_VARIABLE_DEKL:
      {
	back=uptr.variable_dekl->declare(options);
	break;
      };
    case TYPE_STMT_FUNCTION_DEKL:
      {
	back=uptr.function_dekl->declare(options);
        break;
      };
    case TYPE_STMT_EXPR_STMT:
      {
	back=uptr.expr_stmt->execute(options);
        break;
      };
    case TYPE_STMT_BLOCK:
      {
	back=uptr.block->execute(options);
        break;
      };
    case TYPE_STMT_CHOICE_STMT:
      {
	back=uptr.choice_stmt->execute(options);
        break;
      };
    case TYPE_STMT_LOOP_STMT:
      {
	back=uptr.loop_stmt->execute(options);
        break;
      };
    case TYPE_STMT_JUMP_STMT:
      {
	back=uptr.jump_stmt->execute(options);
        break;
      };
    case TYPE_STMT_PRINT_STMT:
      {
	back.ctrl=FLOW_OK;
        fwrite((void*) print_stmt.zgr,1,strlen(print_stmt),outstream);
        fflush(outstream);
	break;
      };
    default:
      {

        /* not reached (hopefully) */
      };
    };

  acc_del(options,ACC_TYPE_RECURSION);return back;
}

flowctrl type_block::execute(opt* options)
{
  opt opts=optchanges(options,&new_options);
  unsigned long i;

  flowctrl back;back.returnval.type=TYPE_VALUE_VOID;
  back.line=line;back.file=file;
#ifdef DEBUGGING
  log(LOG_DEBUG,__FILE__,__LINE__,0,"type_block::execute(options)");
#endif
  back=acc_add(&opts,ACC_TYPE_RECURSION);
  if (back.ctrl!=FLOW_OK) { return back; };
  
  back.ctrl=FLOW_OK;

  for (i=0;i!=stmt_list->stmts.length();i++)
    {
#ifdef DEBUGGING
      log(LOG_DEBUG,__FILE__,__LINE__,0,
	  "type_block::execute(options):calling stmt");
#endif
      back=stmt_list->stmts[i].execute(&opts);
      if (back.ctrl!=FLOW_OK) { break; };
    };

  /* if it was not a return-statement, we should return void */
  if (back.ctrl!=FLOW_RETURN) { back.returnval.type=TYPE_VALUE_VOID; };

  if (stack[0].variables.counter)
    {
      for (i=0;i!=256;i++)
	{
	  while (stack[0].variables.names[i].length())
	    {
	      acc_del(&opts,ACC_TYPE_VAR);
	      stack[0].variables.names[i].remove_listelem(0);
	      stack[0].variables.values[i].remove_listelem(0);
	};
	};
      stack[0].variables.counter=0;
      delete[] stack[0].variables.names;
      delete[] stack[0].variables.values;
    };

  if (stack[0].functions.counter)
    {
      for (i=0;i!=256;i++)
	{
	  while (stack[0].functions.names[i].length())
	    {
	      acc_del(&opts,ACC_TYPE_FUNC);
	      
	      /* now we can savely delete parsed blocks for functions and the
	     functions */
	      if (stack[0].functions.values[i][0].type==TYPE_FUNCTION_EVAL)
		{
		  stack[0].functions.values[i][0].myblock->destroy();
		  delete stack[0].functions.values[i][0].myblock;
		};
	      
	      /*
		FIXME: args are pointers to declarations, why did we do this ? 
		stack[0].functions[0].args->destroy();
		delete stack[0].functions[0].args;
		*/
	      
	      stack[0].functions.names[i].remove_listelem(0);
	      stack[0].functions.values[i].remove_listelem(0);
	    };
	};
      stack[0].functions.counter=0;
      delete[] stack[0].functions.names;
      delete[] stack[0].functions.values;
    };

  acc_del(&opts,ACC_TYPE_RECURSION);return back;
}



