/*
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"
 
int args_exit[]= { 0 };

flowctrl func_exit(type_block* aktblock,opt* options,
                   joined_list<type_value>* args)
{
  aktblock=aktblock;options=options;args=args; /* to prevent warnings */
  flowctrl back;
  back.ctrl=FLOW_EXIT;
  back.returnval.type=TYPE_VALUE_VOID;
  return back;
}

int args_isset[]= { TYPE_VALUE_STRING,
                    0 };

flowctrl func_isset(type_block* aktblock,opt* options,
                    joined_list<type_value>* args)
{
  aktblock=aktblock;options=options;args=args; /* to prevent warnings */
  flowctrl back,call;
  back.ctrl=FLOW_RETURN;
  back.returnval.type=TYPE_VALUE_ULONG;
  back.returnval.val.ulongval=0;
  type_value* dummy;

  if (!defined((*args)[0].stringval))
    {
      setvarulong("error",1);
      setvarstring("perror","isset: string not defined");
      return back;
    };

  call=getvar(aktblock,options,dummy,(*args)[0].stringval);
  if (call.ctrl==FLOW_OK) { back.returnval.val.ulongval=1; };
  return back;
}

int args_isfunc[]= { TYPE_VALUE_STRING,
                     0 };

flowctrl func_isfunc(type_block* aktblock,opt* options,
                     joined_list<type_value>* args)
{
  aktblock=aktblock;options=options;args=args; /* to prevent warnings */
  flowctrl back,call;
  back.ctrl=FLOW_RETURN;
  back.returnval.type=TYPE_VALUE_ULONG;
  back.returnval.val.ulongval=0;
  type_function* dummy;

  if (!defined((*args)[0].stringval))
    {
      setvarulong("error",1);
      setvarstring("perror","isfunc: string not defined");
      return back;
    };

  call=getfunc(aktblock,options,dummy,(*args)[0].stringval);
  if (call.ctrl==FLOW_OK) { back.returnval.val.ulongval=1; };
  return back;
}

int args_deletefunc[]= { TYPE_VALUE_STRING,
                         TYPE_VALUE_LONG,
                         0 };

flowctrl func_deletefunc(type_block* aktblock,opt* options,
                         joined_list<type_value>* args)
{
  aktblock=aktblock;options=options;args=args; /* to prevent warnings */
  flowctrl back;
  back.ctrl=FLOW_RETURN;
  back.returnval.type=TYPE_VALUE_ULONG;
  back.returnval.val.ulongval=0;

  if (!defined((*args)[0].stringval))
    {
      setvarulong("error",1);
      setvarstring("perror","deletefunc: string not defined");
      return back;
    };

  unsigned long perms=0xFFFFFFFF;
  long counterval=(*args)[1].val.longval;
  string name=(*args)[0].stringval;
  type_function* function;

  /* go counter blocks up, start-block if counter < 0 */
  while (counterval>0)
    {
      if (aktblock->block)
        {
          perms&=aktblock->perms;
          aktblock=aktblock->block;
          counterval--;
        }
      else
        {
          setvarulong("error",1);
          setvarstring("perror","deletefunc("+name+"): counter to high");
          return back;
        };
    };
  if (counterval < 0)
    {
      /* go to start block */
      while (aktblock->block)
        {
          perms&=aktblock->perms;
          aktblock=aktblock->block;
        };
    };

  while (1)
    {
      /* try to find function in the actual block */
      function=aktblock->stack[0].functions.get_listelem(name);
      if (function)
        {
          if (perms & FLAG_PERMS_DECLARE_FUNC)
            {
              /* actually called ? */
              if (function->called)
                {
                  setvarulong("error",1);
                  setvarstring("perror","deletefunc("+name+
                               "): function is actually called");
                  return back;
                };

              /* delete it */
              acc_del(options,ACC_TYPE_FUNC); /* FIXME: this could lead to
                                                 confusion with negative
                                                 accounting-values */
              if (function->type==TYPE_FUNCTION_EVAL)
                {
                  function->myblock->destroy();
                  delete function->myblock;
                };
              aktblock->stack[0].functions.remove_listelem(name);
              
              back.returnval.val.ulongval=1;
              return back;
            }
          else
            {
              setvarulong("error",1);
              setvarstring("perror","deletefunc("+name+
                           "): no permission to delete");
                  return back;
            };
          
        };

      /* not found -> try upper block (if it exists) */
      if (aktblock->block)
        {
          perms&=aktblock->perms;
          aktblock=aktblock->block;
        }
      else
        {
          setvarulong("error",1);
          setvarstring("perror","deletefunc("+name+
                       "): function not found");
          return back;
        };
    };

  /* not reached */
  return back;
}

int args_deletevar[]= { TYPE_VALUE_STRING,
                        TYPE_VALUE_LONG,
                        0 };

flowctrl func_deletevar(type_block* aktblock,opt* options,
                        joined_list<type_value>* args)
{
  aktblock=aktblock;options=options;args=args; /* to prevent warnings */
  flowctrl back;
  back.ctrl=FLOW_RETURN;
  back.returnval.type=TYPE_VALUE_ULONG;
  back.returnval.val.ulongval=0;

  if (!defined((*args)[0].stringval))
    {
      setvarulong("error",1);
      setvarstring("perror","deletevar: string not defined");
      return back;
    };

  unsigned long perms=0xFFFFFFFF;
  long counterval=(*args)[1].val.longval;
  string name=(*args)[0].stringval;
  type_value* variable;

  /* go counter blocks up, start-block if counter < 0 */
  while (counterval>0)
    {
      if (aktblock->block)
        {
          perms&=aktblock->perms;
          aktblock=aktblock->block;
          counterval--;
        }
      else
        {
          setvarulong("error",1);
          setvarstring("perror","deletevar("+name+"): counter to high");
          return back;
        };
    };
  if (counterval < 0)
    {
      /* go to start block */
      while (aktblock->block)
        {
          perms&=aktblock->perms;
          aktblock=aktblock->block;
        };
    };

  while (1)
    {
      /* try to find variable in the aktual block */
      variable=aktblock->stack[0].variables.get_listelem(name);
      if (variable)
        {
          if (perms & FLAG_PERMS_DECLARE_VAR)
            {
              acc_del(options,ACC_TYPE_VAR); /* FIXME: this could lead to
                                                confusion with negative
                                                accounting-values */
              aktblock->stack[0].variables.remove_listelem(name);
              back.returnval.val.ulongval=1;
              return back;
            }
          else
            {
              setvarulong("error",1);
              setvarstring("perror","deletevar("+name+
                           "): no permission to delete");
              return back;
            };
        };
      
      /* not found -> try upper block (if it exists) */
      if (aktblock->block)
        {
          perms&=aktblock->perms;
          aktblock=aktblock->block;
        }
      else
        {
          setvarulong("error",1);
          setvarstring("perror","deletevar("+name+
                       "): variable not found");
          return back;
        };
    };

  /* not reached */
  return back;
}

int args_parse[]= { TYPE_VALUE_STRING,
                    TYPE_VALUE_STRING,
                    0 };

flowctrl func_parse(type_block* aktblock,opt* options,
                    joined_list<type_value>* args)
{
  aktblock=aktblock;options=options;args=args; /* to prevent warnings */
  flowctrl back;
  back.ctrl=FLOW_RETURN;
  back.returnval.type=TYPE_VALUE_ULONG;
  back.returnval.val.ulongval=0;

  string code;
  unsigned long longdummy;
  string stringdummy;
  char* optptr;
  opt eval_options;
  int argc=1;
  char* argv[MAX_OPTIONS];

  if (!defined((*args)[0].stringval) ||
      !defined((*args)[1].stringval))
    {
      setvarulong("error",1);
      setvarstring("perror","parse: string not defined");
      return back;
    };

  /* reset parser */
  reset_parser();

  code=(*args)[0].stringval;

  /* tokenize options */
  optptr=(*args)[1].stringval;
  while ((argv[argc]=strsep(&optptr," ")))
    { 
      argc++;
      if (argc==MAX_OPTIONS)
        {
          setvarulong("error",1);
          setvarstring("perror","parse: to much options");
          return back;
        };
    };

  if (!get_options(argc,argv,"a:x:c:p:f:r:e:t:o:z:qhil",longdummy,
                   stringdummy,options,eval_options,1))
  {
    setvarulong("error",1);
    setvarstring("perror","syntax error in options (or out of mem)");
    return back;
  };
  
  /* parser variables */
  ::file="eval(string)";
  filei=0;
  prgstr=code;

  if (!yyparse())
    {
      back.returnval.val.ulongval=1;
    }
  else
    {
      char buf[MAX_LINE_SIZE];
      snprintf(buf,MAX_LINE_SIZE,"parsing error in file %s, line %u",
	       (char*) filenames[filei],line);
      setvarulong("error",1);
      setvarstring("perror",buf);
    };

  freetokenstack();
  emptystack();
  freeopts(&eval_options);
  return back;
}

