/* Nessus Attack Scripting Language 
 *
 * Copyright (C) 2002 - 2003 Michel Arboi and Renaud Deraison
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * In addition, as a special exception, Renaud Deraison and Michel Arboi
 * give permission to link the code of this program with any
 * version of the OpenSSL library which is distributed under a
 * license identical to that listed in the included COPYING.OpenSSL
 * file, and distribute linked combinations including the two.
 * You must obey the GNU General Public License in all respects
 * for all of the code used other than OpenSSL.  If you modify
 * this file, you may extend this exception to your version of the
 * file, but you are not obligated to do so.  If you do not wish to
 * do so, delete this exception statement from your version.
 *
 */
#include <includes.h>


#include "nasl_tree.h"
#include "nasl_global_ctxt.h"
#include "nasl_func.h"
#include "nasl_var.h"
#include "nasl_lex_ctxt.h"
#include "exec.h"

#include "nasl_debug.h"

#ifndef NASL_DEBUG
#define NASL_DEBUG 0
#endif

#if 0
static int
hash_int(int x)
{
  return (unsigned) x % VAR_NAME_HASH;
}
#endif

int
hash_str2(const char* s, int n)
{
  unsigned long		h = 0;
  const char		*p;

  if (s == NULL)
    return 0;

  for (p = s; *p != '\0'; p ++)
    h = (h << 3) + (unsigned char) *p;
  return h % n;
}

static int
hash_str(const char* s)
{
  return hash_str2(s, VAR_NAME_HASH);
}

anon_nasl_var*
nasl_get_var_by_num(nasl_array* a, int num, int create)
{
  anon_nasl_var	*v = NULL;

  if (num < 0)
    {
      /* TBD: implement a min_index field, just like $[ in Perl */
      nasl_perror(NULL, "Negative integer index are not supported yet!\n");
      return NULL;
    }

  if (num < a->max_idx)
    v = a->num_elt[num];
  if (v != NULL || ! create)
    return v;

  if (num >= a->max_idx)
    {
      a->num_elt = erealloc(a->num_elt, sizeof(anon_nasl_var*) * (num+1));
      bzero(a->num_elt + a->max_idx,
	    sizeof(anon_nasl_var*) * (num+1-a->max_idx));
      a->max_idx = num +1;
    }
  v = emalloc(sizeof(anon_nasl_var));
  v->var_type = VAR2_UNDEF;

  a->num_elt[num] = v;
  return v;  
}

static named_nasl_var*
get_var_by_name(nasl_array* a, const char* s)
{
  int			h = hash_str(s);
  named_nasl_var	*v;
  
  if (a->hash_elt == NULL)
    a->hash_elt = emalloc(VAR_NAME_HASH * sizeof(named_nasl_var*));

  for (v = a->hash_elt[h]; v != NULL; v = v->next_var)
    if (v->var_name != NULL && strcmp(s, v->var_name) == 0)
      return v;

  v = emalloc(sizeof(named_nasl_var));
  v->var_name = estrdup(s);
  v->u.var_type = VAR2_UNDEF;
  v->next_var = a->hash_elt[h];

  a->hash_elt[h] = v;
  return v;
}


/* This function climbs up in the context list */
static named_nasl_var*
get_var_ref_by_name(lex_ctxt* ctxt, const char* name, int climb)
{
  named_nasl_var	*v, *prev;
  int		h = hash_str(name);
  lex_ctxt	*c;


  if(climb != 0)
    {
      for (c = ctxt; c != NULL; c = c->up_ctxt)
	if (c->ctx_vars.hash_elt != NULL)
	  for (prev = NULL, v = c->ctx_vars.hash_elt[h]; v != NULL; v = v->next_var)
	    if (v->var_name != NULL && strcmp(name, v->var_name) == 0)
	      {
#ifdef SILLY_OPT
		if (prev != NULL) /* Move it to start of list */
		  {
		    prev->next_var = v->next_var;
		    v->next_var = c->ctx_vars.hash_elt[h];
		    c->ctx_vars.hash_elt[h] = v;
		  }
#endif
	      return v;
    }
#ifdef SILLY_OPT
	    else
	      prev = v;
#endif
    }
  else
    {
      if (ctxt->ctx_vars.hash_elt != NULL)
	for (prev = NULL, v = ctxt->ctx_vars.hash_elt[h]; v != NULL; v = v->next_var)
	  if (v->var_name != NULL && strcmp(name, v->var_name) == 0)
	    {
#ifdef SILLY_OPT
	      if (prev != NULL) /* Move it to start of list */
		{
		  prev->next_var = v->next_var;
		  v->next_var = ctxt->ctx_vars.hash_elt[h];
		  ctxt->ctx_vars.hash_elt[h] = v;
		}
#endif
	    return v;
	    }
#ifdef SILLY_OPT
	  else
	    prev = v;
#endif
    }

  if (ctxt->ctx_vars.hash_elt == NULL)
    ctxt->ctx_vars.hash_elt = emalloc(sizeof(named_nasl_var*) * VAR_NAME_HASH);

  v = emalloc(sizeof(named_nasl_var));
  v->var_name = estrdup(name);
  v->u.var_type = VAR2_UNDEF;
  v->next_var = ctxt->ctx_vars.hash_elt[h];
  ctxt->ctx_vars.hash_elt[h] = v;

  return v;  
}


static anon_nasl_var*
get_var_ref_by_num(lex_ctxt* ctxt, int num)
{
  anon_nasl_var	*v;

  if (ctxt->ctx_vars.max_idx <= num)
    {
      ctxt->ctx_vars.num_elt = erealloc(ctxt->ctx_vars.num_elt, sizeof(anon_nasl_var*) * (num+1));
      bzero(ctxt->ctx_vars.num_elt + ctxt->ctx_vars.max_idx,
	    sizeof(anon_nasl_var*) * (num+1 - ctxt->ctx_vars.max_idx));
      ctxt->ctx_vars.max_idx = num+1;
    }

  v = ctxt->ctx_vars.num_elt[num];
  if (v != NULL)
    return v;

  v = emalloc(sizeof(anon_nasl_var));
  v->var_type = VAR2_UNDEF;
  ctxt->ctx_vars.num_elt[num] = v;
  return v;  
}

tree_cell*
var2cell(anon_nasl_var* v)
{
  tree_cell	*tc = alloc_tree_cell(0, NULL);

  tc->type = REF_VAR;
  tc->x.ref_val = v;		/* No need to free this later! */
  return tc;
}

tree_cell*
get_variable_by_name(lex_ctxt* ctxt, const char* name)
{
  named_nasl_var	* v;

  if (name == NULL)
    return NULL;
  v = get_var_ref_by_name(ctxt, name, 1);
  return var2cell(&v->u);
}

static const char*
get_var_name(anon_nasl_var *v)
{
  static char	str[16];
#ifdef ALL_VARIABLES_NAMED
  if (v->av_name != NULL)
    return v->av_name;
#endif
  snprintf(str, sizeof(str), "[%08x]", (int)v);
  return str;
}

tree_cell*
get_array_elem(lex_ctxt* ctxt, const char* name, tree_cell* idx)
{
  named_nasl_var	*v = get_var_ref_by_name(ctxt, name, 1);
  named_nasl_var	*nv;
  anon_nasl_var		*u, *av;
  tree_cell	*tc, idx0;


  u = &v->u;

  if (idx == NULL)
    {
#if NASL_DEBUG > 0
      nasl_perror(ctxt, "get_array_elem: NULL index\n");
#endif
      /* Treat it as zero */
      idx = &idx0;
      idx->x.i_val = 0;
      idx->type = CONST_INT;
    }

  switch (u->var_type)
    {
    case VAR2_UNDEF:
      /* We define the array here */
      u->var_type = VAR2_ARRAY;
    case VAR2_ARRAY:
      switch(idx->type)
	{
	case CONST_INT:
	  av = nasl_get_var_by_num(&u->v.v_arr, idx->x.i_val, 1);
	  return var2cell(av);
	  
	case CONST_STR:
	case CONST_DATA:
	  nv = get_var_by_name(&u->v.v_arr, idx->x.str_val);
	  return var2cell(nv != NULL ? &nv->u : NULL);
	  
	default:
	  nasl_perror(ctxt, "get_array_elem: unhandled index type 0x%x\n",
		  idx->type);
	  return NULL;
	}
      /*NOTREACHED*/
      break;

    case VAR2_INT:
      nasl_perror(ctxt, "get_array_elem: variable %s is an integer\n", name);
      return NULL;

    case VAR2_STRING:
    case VAR2_DATA:
      if (idx->type == CONST_INT)
	{
	  int	l = u->v.v_str.s_siz;

	  if (idx->x.i_val >= l)
	    {
	      nasl_perror(ctxt, "get_array_elem: requesting character after end of string %s (%d >= %d)\n", name, idx->x.i_val, l);
	      tc = alloc_expr_cell(idx->line_nb, CONST_DATA /*CONST_STR*/,
				   NULL, NULL);
	      tc->x.str_val = estrdup("");
	      tc->size = 0;
	      return tc;
	    }
	  else
	    {
	      if ( idx->x.i_val <  0)
	       {
	        nasl_perror(ctxt, "Negative index !\n");
	      	return NULL;
	       }
	      tc = alloc_expr_cell(idx->line_nb, CONST_DATA /*CONST_STR*/,
				   NULL, NULL);
	      tc->x.str_val = emalloc(2);
	      tc->x.str_val[0] = u->v.v_str.s_val[idx->x.i_val];
	      tc->x.str_val[1] = '\0';
	      tc->size = 1;
	      return tc;
	    }
	}
      else
	{
	  nasl_perror(ctxt, "get_array_elem: Cannot use a non integer index  (type 0x%x) in string\n", idx->type);
	  return NULL;
	}
      /*NOTREACHED*/
      break;

    default:
      nasl_perror(ctxt, "Severe bug: unknown variable type 0x%x %s\n",
	      u->var_type, get_line_nb(idx));
      return NULL;
    }
  /*NOTREACHED*/
  return NULL;
}

static void	free_var_chain(named_nasl_var*);
static void	free_anon_var(anon_nasl_var*);

/*
 * Note: the function does not free the nasl_array structure.
 * Do it if necessary
 */
void
free_array(nasl_array* a)
{
  int	i;

  if (a == NULL)
    return;
  if (a->num_elt != NULL)
    {
      for (i = 0; i  < a->max_idx; i ++)
	free_anon_var(a->num_elt[i]);
      efree(&a->num_elt);
    }
  if (a->hash_elt != NULL)
    {
      for (i = 0; i < VAR_NAME_HASH; i ++)
	free_var_chain(a->hash_elt[i]);
      efree(&a->hash_elt);
    }
  return;
}

static void
free_var_chain(named_nasl_var* v)
{

  if (v == NULL)
    return;
  free_var_chain(v->next_var);
  efree(&v->var_name);
  switch(v->u.var_type)
    {
    case VAR2_STRING:
    case VAR2_DATA:
      efree(&v->u.v.v_str.s_val);
      break;
    case VAR2_ARRAY:
      free_array(&v->u.v.v_arr);
      break;
    }
  efree(&v);
}

static void
free_anon_var(anon_nasl_var* v)
{
  if (v == NULL)
    return;
  switch(v->var_type)
    {
    case VAR2_STRING:
    case VAR2_DATA:
      efree(&v->v.v_str.s_val);
      break;
    case VAR2_ARRAY:
      free_array(&v->v.v_arr);
      break;
    }
  efree(&v);

}

static void
clear_array(nasl_array *a)
{
  int		i;

  if (a->num_elt != NULL)
    {
      for (i = 0; i < a->max_idx; i ++)
	free_anon_var(a->num_elt[i]);
      efree(&a->num_elt);
    }
  a->max_idx = 0;
  if (a->hash_elt != NULL)
    {
      for (i = 0; i < VAR_NAME_HASH; i ++)
	free_var_chain(a->hash_elt[i]);
      efree(&a->hash_elt);
    }
}

void
clear_anon_var(anon_nasl_var* v)
{

  if (v == NULL)
    return;

  switch (v->var_type)
    {
    case VAR2_INT:
      v->v.v_int = 0;
      break;
    case VAR2_STRING:
    case VAR2_DATA:
      efree(&v->v.v_str.s_val);
      v->v.v_str.s_siz = 0;
      break;
    case VAR2_ARRAY:
      clear_array(&v->v.v_arr);
      break;
    }
  v->var_type = VAR2_UNDEF;
  return;
}

static void
copy_anon_var(anon_nasl_var* v1, const anon_nasl_var *v2)
{
  /* TBD: free variable if necessary? */
  v1->var_type = v2->var_type;
  switch(v2->var_type)
    {
    case VAR2_STRING:
    case VAR2_DATA:
      if(v2->v.v_str.s_val != NULL)
	{
	  v1->v.v_str.s_val = emalloc(v2->v.v_str.s_siz);
	  memcpy(v1->v.v_str.s_val, v2->v.v_str.s_val, v2->v.v_str.s_siz);
	  v1->v.v_str.s_siz = v2->v.v_str.s_siz;
	}
      else
	{
	  v1->v.v_str.s_val = NULL;
	  v1->v.v_str.s_siz = 0;
	}
      break;

    case VAR2_UNDEF:
      break;

    case VAR2_INT:
      v1->v.v_int = v2->v.v_int;
      break;

    case VAR2_ARRAY:
      /* TBD: handle arrays here for multi-dimensional arrays */
    default:
      nasl_perror(NULL, "copy_anon_var: unhandled type 0x%x\n", v2->var_type);
      clear_anon_var(v1);
    }
}

static anon_nasl_var*
dup_anon_var(const anon_nasl_var* v)
{
  anon_nasl_var	*v1;

  if (v == NULL)
    return NULL;

  v1 = emalloc(sizeof(anon_nasl_var));
  copy_anon_var(v1, v);
  return v1;
}

static named_nasl_var*
dup_named_var(const named_nasl_var* v)
{
  named_nasl_var	*v1;

  if (v == NULL)
    return NULL;

  v1 = emalloc(sizeof(named_nasl_var));
  copy_anon_var(&v1->u, &v->u);
  v1->var_name = estrdup(v->var_name);
  return v1;
}

static void
copy_array(nasl_array *a1, const nasl_array *a2)
{
  int		i;
  named_nasl_var	*v1, *v2, *v;


  if (a1 == a2)
    {
#if NASL_DEBUG > 1
      nasl_perror(NULL, "copy_array: a1 == a2!\n");
#endif
      return;
    }
 
  clear_array(a1);

  if ( a2->num_elt != NULL )
    {
      a1->max_idx = a2->max_idx;
      a1->num_elt = emalloc(sizeof(anon_nasl_var*) * a2->max_idx);
      for (i = 0; i < a2->max_idx; i ++)
	a1->num_elt[i] = dup_anon_var(a2->num_elt[i]);
    }
  if (a2->hash_elt != NULL)
    {
      a1->hash_elt = emalloc(VAR_NAME_HASH * sizeof(named_nasl_var*));
      for (i = 0; i < VAR_NAME_HASH; i ++)
	{
	  v1= NULL;
	  for (v2 = a2->hash_elt[i]; v2 != NULL; v2 = v2->next_var)
	    {
	      v = dup_named_var(v2);
	      v->next_var = v1;
	      a1->hash_elt[i] = v;
	      v1 = v;
	    }
	}
    }
}

tree_cell*
copy_ref_array(const tree_cell* c1)
{
  tree_cell	*c2;
  nasl_array	*a2;

  if (c1 == NULL || c1 == FAKE_CELL || c1->type != REF_ARRAY)
    return NULL;

  c2 = alloc_tree_cell(0, NULL); c2->type = DYN_ARRAY;
  c2->x.ref_val = a2 = emalloc(sizeof(nasl_array));
  copy_array(a2, c1->x.ref_val);
  return c2;
}

extern FILE*	nasl_trace_fp;

static tree_cell*
affect_to_anon_var(anon_nasl_var* v1, tree_cell* rval)
{
  anon_nasl_var	*v2 = NULL, v0;
  nasl_array	*a = NULL;
  int		t1, t2;
  void		*p;


  t1 = v1->var_type;

  if (rval == NULL || rval == FAKE_CELL)
    {
#if NASL_DEBUG > 1
      nasl_perror(NULL, "nasl_affect: affecting NULL or FAKE cell undefines variable %s %s\n", get_var_name(v1), get_line_nb(rval));
#endif
      clear_anon_var(v1);
      if(nasl_trace_enabled())nasl_trace(NULL, "NASL> %s <- undef\n", get_var_name(v1));
      return NULL;
    }

  switch (rval->type)
    {
    case CONST_INT:
      t2 = VAR2_INT;
      break;
    case CONST_STR:
      t2 = VAR2_STRING;
      break;
    case CONST_DATA:
      t2 = VAR2_DATA;
      break;

    case REF_VAR:
      v2 = rval->x.ref_val;
      if (v2 == v1)
	{
#if NASL_DEBUG > 1
	  nasl_perror(NULL, "Copying variable %s to itself is useless and dangerous!\n", get_var_name(v1));
#endif
	  return FAKE_CELL;
	}
      t2 = v2->var_type;
      if (t2 == VAR2_ARRAY)
	a = &v2->v.v_arr;	/* ? */
      break;

    case REF_ARRAY:
    case DYN_ARRAY:
      a = rval->x.ref_val;
      t2 = VAR2_ARRAY;
      if (v1->var_type == VAR2_ARRAY && &v1->v.v_arr == a)
	{
#if NASL_DEBUG > 1
	  nasl_perror(NULL, "Copying array %s to itself is useless and dangerous!\n", get_var_name(v1));
#endif
	  return FAKE_CELL;
	}
      break;

    default:
      nasl_perror(NULL, "Cannot affect rvalue 0x%x to variable\n", rval->type);
      return NULL;
    }

  /*
   * Bug #146: when executing 
   *    x = 'abc'; x = x;  or   x = make_list(...); x = x[0];
   * the rvalue will be freed before it is copied to the lvalue
   */
  v0 = *v1;

  if (t1 != VAR2_UNDEF && t2 == VAR2_UNDEF)
    {
#if NASL_DEBUG > 0
      nasl_perror(NULL, "Warning: Undefining defined variable %s %s\n",
		  get_var_name(v1), get_line_nb(rval));
#endif
    }
  else if (t1 == VAR2_ARRAY && t2 != VAR2_ARRAY)
    {
#if NASL_DEBUG > 1
      nasl_perror(NULL, 
		  "Warning: affecting non array (0x%x) to array variable %s\n",
		  t2, get_line_nb(rval));
#endif
    }
  else if ((t1 == VAR2_INT || t1 == VAR2_STRING || t1 == VAR2_DATA) &&
	   t2 == VAR2_ARRAY)
    {
#if NASL_DEBUG > 1
      nasl_perror(NULL ,"Warning: affecting array to atomic variable (0x%x) %s\n",  t2, get_line_nb(rval));
#endif
    }

  /* Bug #146: this fake clear is necessary if we copy an array*/
  memset(v1, 0, sizeof(*v1));
  /* Bug #146: no risk with the type, we already copied it */
  v1->var_type = t2;

  if (rval->type != REF_VAR && rval->type != REF_ARRAY && rval->type != DYN_ARRAY)
    switch (t2)
      {
      case VAR2_INT:
	v1->v.v_int = rval->x.i_val;
	break;
      case VAR2_STRING:
      case VAR2_DATA:
        if( rval->x.str_val == NULL )
	  {
	    v1->v.v_str.s_val = NULL;
	    v1->v.v_str.s_siz = 0;
	  }
	else
	  {
	    p = emalloc(rval->size+1);
	    memcpy(p, rval->x.str_val, rval->size+1);
	    v1->v.v_str.s_siz = rval->size;
	    v1->v.v_str.s_val = p;
	  }
	break;
      }
  else				/* REF_xxx */
    switch(t2)
      {
      case VAR2_INT:
	v1->v.v_int = v2->v.v_int;
	break;
      case VAR2_STRING:
      case VAR2_DATA:
        if(v2->v.v_str.s_val == NULL)
	  {
	    v1->v.v_str.s_val = NULL;
	    v1->v.v_str.s_siz = 0;
	  }
	else
	  {
	    p = emalloc(v2->v.v_str.s_siz);
	    memcpy(p, v2->v.v_str.s_val, v2->v.v_str.s_siz);
	    v1->v.v_str.s_siz = v2->v.v_str.s_siz;
	    v1->v.v_str.s_val = p;
	  }
	break;
      case VAR2_ARRAY:
	copy_array(&v1->v.v_arr, a);
	if (v0.var_type == VAR2_ARRAY)
	  bzero(&v0, sizeof(v0));	/* So that we don't clear the variable twice */
	break;
      }

  if (nasl_trace_fp != NULL)
    switch(t2)
      {
      case VAR2_INT:
	nasl_trace(NULL, "NASL> %s <- %d\n", get_var_name(v1), v1->v.v_int);
	break;
      case VAR2_STRING:
      case VAR2_DATA:
	nasl_trace(NULL, "NASL> %s <- \"%s\"\n", get_var_name(v1), v1->v.v_str.s_val);
	break;
      case VAR2_ARRAY:
	nasl_trace(NULL, "NASL> %s <- (VAR2_ARRAY)\n", get_var_name(v1));
	break;
      default:
	nasl_trace(NULL, "NASL> %s <- (Type 0x%x)\n", get_var_name(v1), t2);
	break;
      }

  clear_anon_var(&v0);
  return FAKE_CELL;
}

tree_cell*
nasl_affect(tree_cell* lval, tree_cell* rval)
{
  anon_nasl_var	*v1 = NULL;
  
  if(lval == NULL)
  {
   nasl_perror(NULL, "nasl_effect: invalid lvalue\n");
   return NULL;
  }

  if (lval->type != REF_VAR)
    {
      nasl_perror(NULL, "nasl_affect: cannot affect to non variable %s\n",
	      nasl_type_name(lval->type));
      return NULL;
    }

  v1 = lval->x.ref_val;
  return affect_to_anon_var(v1, rval);
}

static named_nasl_var*
create_named_var(const char* name, tree_cell* val)
{
  named_nasl_var	*v = emalloc(sizeof(named_nasl_var));
  tree_cell		*tc;

  if (name != NULL)
    v->var_name = estrdup(name);

  if (val == NULL || val == FAKE_CELL)
    {
#if NASL_DEBUG > 1
      nasl_perror(NULL, "create_named_var: affecting NULL or FAKE cell to variable %s\n", get_var_name(v));
#endif
      v->u.var_type = VAR2_UNDEF;
      return v;
    }

  tc = affect_to_anon_var(&v->u, val);
  /* Here we might test the return value */
  deref_cell(tc);
  return v;
}

static anon_nasl_var*
create_anon_var(tree_cell* val)
{
  anon_nasl_var	*v = emalloc(sizeof(anon_nasl_var));
  tree_cell		*tc;

  if (val == NULL || val == FAKE_CELL)
    {
#if NASL_DEBUG > 1
      nasl_perror(NULL, "create_anon_var: affecting NULL or FAKE cell to variable %s\n", get_var_name(v));
#endif
      v->var_type = VAR2_UNDEF;
      return v;
    }

  tc = affect_to_anon_var(v, val);
  /* Here we might test the return value */
  deref_cell(tc);
  return v;
}

tree_cell* 
decl_local_variables(lex_ctxt* lexic, tree_cell* vars)
{
  tree_cell	*t;

  for (t = vars; t != NULL; t = t->link[0])
    if (t->x.str_val == NULL)
      nasl_perror(lexic, "decl_local_variables: null name!\n");
    else
      add_named_var_to_ctxt(lexic, t->x.str_val, NULL);
  return FAKE_CELL;
}

tree_cell*
decl_global_variables(lex_ctxt* lexic, tree_cell* vars)
{
  lex_ctxt	*c = lexic;

  while (c->up_ctxt != NULL)
    c = c->up_ctxt;
  return decl_local_variables(c, vars);
}

anon_nasl_var*
add_numbered_var_to_ctxt(lex_ctxt* lexic, int num, tree_cell* val)
{
  anon_nasl_var	*v;
  nasl_array		*a = &lexic->ctx_vars;

  if (a->max_idx > num)
    {
      v = a->num_elt[num];
      if (v != NULL && v->var_type != VAR2_UNDEF)
	{
	  nasl_perror(lexic, "Cannot add existing variable %d\n", num);
	  return NULL;
	}
      free_anon_var(a->num_elt[num]);
    }
  else
    {
      a->num_elt = erealloc(a->num_elt, (num+1) * sizeof(anon_nasl_var));
      bzero(a->num_elt + a->max_idx, sizeof(anon_nasl_var*) * (num+1 - a->max_idx));
      a->max_idx = num + 1;
    }
  a->num_elt[num] = v = create_anon_var(val);
  return v;
}

named_nasl_var*
add_named_var_to_ctxt(lex_ctxt* lexic, const char* name, tree_cell* val)
{
  int			h = hash_str(name);
  named_nasl_var	*v;
  
  /* Duplicated code ? */
  for (v = lexic->ctx_vars.hash_elt[h]; v != NULL; v = v->next_var)
    if (v->var_name != NULL && strcmp(name, v->var_name) == 0)
      {
	nasl_perror(lexic, "Cannot add existing variable %s\n", name);
	return NULL;
      }
  v = create_named_var(name, val);
  if (v == NULL)
    return NULL;
  v->next_var = lexic->ctx_vars.hash_elt[h];
  lexic->ctx_vars.hash_elt[h] = v;
  return v;
}


tree_cell*
nasl_read_var_ref(lex_ctxt* lexic, tree_cell* tc)
{
  tree_cell	*ret;
  anon_nasl_var	*v;

  if (tc == NULL || tc == FAKE_CELL)
    {
      nasl_perror(lexic, "nasl_read_var_ref: cannot read NULL or FAKE cell\n");
      return NULL;
    }
  if (tc->type != REF_VAR)
    {
      nasl_perror(lexic, "nasl_read_var_ref: argument (type=%d) is not REF_VAR %s\n", tc->type, get_line_nb(tc));
      return NULL;
    }

  v = tc->x.ref_val;
  if (v == NULL)
    {
      nasl_perror(lexic, "nasl_read_var_ref: NULL variable in REF_VAR\n");
      return NULL;
    }

  ret = alloc_tree_cell(tc->line_nb, NULL);

  switch (v->var_type)
    {
    case VAR2_INT:
      ret->type = CONST_INT;
      ret->x.i_val = v->v.v_int;
      if(nasl_trace_enabled())nasl_trace(lexic, "NASL> %s -> %d\n", get_var_name(v), ret->x.i_val);
      return ret;

    case VAR2_STRING:
      ret->type = CONST_STR;
      /* Fix bad string length */
      if (v->v.v_str.s_siz <= 0 && v->v.v_str.s_val[0] != '\0')
	{
	  v->v.v_str.s_siz = strlen(v->v.v_str.s_val);
	  nasl_perror(lexic, "nasl_read_var_ref: Bad string length fixed\n");
	}
      /* Go on next case */
    case VAR2_DATA:
      ret->type = v->var_type == VAR2_STRING ? CONST_STR : CONST_DATA;
      if(v->v.v_str.s_val == NULL)
      {
       ret->x.str_val = NULL;
       ret->size = 0;
      }
      else
      {
       ret->x.str_val = emalloc(v->v.v_str.s_siz);
       memcpy(ret->x.str_val, v->v.v_str.s_val, v->v.v_str.s_siz);
       ret->size = v->v.v_str.s_siz;
      }
      if(nasl_trace_enabled())nasl_trace(lexic, "NASL> %s -> \"%s\"\n", get_var_name(v), ret->x.str_val);
      return ret;

    case VAR2_ARRAY:
      ret->type = REF_ARRAY;
      ret->x.ref_val = &v->v.v_arr;
      return ret;

    case VAR2_UNDEF:
#if NASL_DEBUG > 0
      name = get_var_name(v);
      if (strcmp(name, "NULL") != 0) /* special case */
	nasl_perror(lexic, "nasl_read_var_ref: variable %s is undefined %s\n",
		    name, get_line_nb(tc));
#endif
      if(nasl_trace_enabled())nasl_trace(lexic, "NASL> %s -> undef\n", get_var_name(v), v->var_type);
      break;

    default:
      nasl_perror(lexic, "nasl_read_var_ref: unhandled variable type %d\n",
	      v->var_type);
      if(nasl_trace_enabled())nasl_trace(lexic, "NASL> %s -> ???? (Var type %d)\n", get_var_name(v), v->var_type);
      break;
    }
  deref_cell(ret);
  return NULL;
}


tree_cell*
nasl_incr_variable(lex_ctxt* lexic, tree_cell* tc, int pre, int val)
{
  anon_nasl_var	*v;
  int		old_val = 0, new_val;
  tree_cell	*retc;

  if (tc->type != REF_VAR)
    {
      nasl_perror(lexic,
	      "nasl_incr_variable: argument (type=%d) is not REF_VAR %s\n",
	      tc->type, get_line_nb(tc));
      return NULL;
    }

  v = tc->x.ref_val;

  switch (v->var_type)
    {
    case VAR2_INT:
      old_val = v->v.v_int;
      break;
    case VAR2_STRING:
    case VAR2_DATA:
#if NASL_DEBUG > 0
      nasl_perror(lexic, "nasl_incr_variable: variable %s is a STRING %s - converting to integer\n", "", get_line_nb(tc));
#endif
      old_val = v->v.v_str.s_val == NULL ? 0 : atoi(v->v.v_str.s_val);
      break;
    case VAR2_UNDEF:
#if NASL_DEBUG > 0
      nasl_perror(lexic, "nasl_incr_variable: variable %s is undefined %s\n",
	      "", get_line_nb(tc));
#endif
      old_val = 0;
      break;

    default:
      nasl_perror(lexic, "nasl_incr_variable: variable %s has bad type %d %s\n",
	      /*get_var_name(v)*/ "", get_line_nb(tc));
      return NULL;
    }
  new_val = old_val + val;

  clear_anon_var(v);
  v->var_type = VAR2_INT;
  v->v.v_int = new_val;

  retc = alloc_tree_cell(0, NULL);
  retc->type = CONST_INT;
  retc->x.i_val = pre ? new_val : old_val;
  
  return retc;  
}


static int
var2int(anon_nasl_var* v, int defval)
{
  if (v == NULL)
    return defval;

  switch (v->var_type)
    {
    case VAR2_INT:
      return v->v.v_int;

    case VAR2_STRING:
    case VAR2_DATA:
      return atoi(v->v.v_str.s_val);

    case VAR2_UNDEF:
#if NASL_DEBUG > 1
      nasl_perror(NULL, "var2int: variable %s is undefined!\n", get_var_name(v));
#endif
    case VAR2_ARRAY:
    default:
      return defval;
    }
  /*NOTREACHED*/
}

const char*
array2str(const nasl_array* a)
{
  static char	*s = NULL;
  static int	len = 0;

  int		i, n, n1 = 0, l;
  anon_nasl_var	 *u;
  named_nasl_var *v;


  if (a == NULL)
    return "";

  if (len == 0)
    {
      len = 80;
      s = emalloc(80);
    }

  strcpy(s, "[ "); n = strlen(s);

  if (a->num_elt != NULL)
    for (i = 0; i < a->max_idx; i ++)
      if ((u = a->num_elt[i]) != NULL && u->var_type != VAR2_UNDEF)
	{
	  if (n + 80 >= len)
	    {
	      len += 80;
	      s = erealloc(s, len);
	    }
	  if (n1 > 0)
	    n += sprintf(s+n, ", ");
	  n1 ++;
	  switch (u->var_type)
	    {
	    case VAR2_INT:
	      snprintf(s+n, len - n, "%d: %d", i, u->v.v_int);
	      n += strlen(s + n);
	      break;
	    case VAR2_STRING:
	    case VAR2_DATA:
	      if (u->v.v_str.s_siz < 64)
		{
		  snprintf(s+n, len - n, "%d: '%s'", i, u->v.v_str.s_val);
		  n += strlen(s + n);
		}
	      else
		{
		  snprintf(s+n, 70, "%d: '%s", i, u->v.v_str.s_val);
		  n += strlen(s + n);
		  n += sprintf(s+n, "'...");
		}
	      break;
	    default:
	      snprintf(s+n, len-n, "%d: ????", i);
	      n += strlen(s + n);
	      break;
	    }
	}

  if (a->hash_elt != NULL)
    for (i = 0; i < VAR_NAME_HASH; i ++)
      for (v = a->hash_elt[i]; v != NULL; v = v->next_var)
	if (v->u.var_type != VAR2_UNDEF)
	  {
	    l = strlen(v->var_name);
	    u = &v->u;
	    if (n + 80 >= len)
	      {
		len += 80 + l;
		s = erealloc(s, len);
	      }
	    if (n1 > 0)
	      n += sprintf(s+n, ", ");
	    n1 ++;
	    switch (u->var_type)
	      {
	      case VAR2_INT:
		n += snprintf(s+n, len - n, "%s: %d", v->var_name, u->v.v_int);
		break;
	      case VAR2_STRING:
	      case VAR2_DATA:
		if (u->v.v_str.s_siz < 64)
		  {
		    snprintf(s+n, len - n, "%s: '%s'", v->var_name, u->v.v_str.s_val);
		    n += strlen(s + n);
		  }
		else
		  {
		    snprintf(s+n, 70+l, "%s: '%s", v->var_name, u->v.v_str.s_val);
		    n += strlen(s + n);
		    n += sprintf(s+n, "'...");
		  }
		break;
	      default:
		snprintf(s+n, len-n, "%s: ????", v->var_name);
		n += strlen(s + n);
		break;
	      }
	  }

  if (n + 2 >= len)
    {
      len += 80;
      s = erealloc(s, len);
    }
  strcpy(s + n, " ]");
  return s;
}

const char*	var2str(const anon_nasl_var* v)
{
  static char	s1[16];

  if (v == NULL)
    return NULL;

  switch (v->var_type)
    {
    case VAR2_INT:
      snprintf(s1, sizeof(s1), "%d", v->v.v_int);
      return s1;		/* buggy if called twice in a row */

    case VAR2_STRING:
    case VAR2_DATA:
      return v->v.v_str.s_val == NULL ? "" : (const char*)v->v.v_str.s_val;

    case VAR2_UNDEF:
#if NASL_DEBUG > 1
      nasl_perror(NULL, "var2str: variable %s is undefined!\n", get_var_name(v));
#endif
      return NULL;

    case VAR2_ARRAY:
      return array2str(&v->v.v_arr);

    default:
#if NASL_DEBUG > 0
      nasl_perror(NULL, "var2str: variable %s has unhandled type %d\n",
		  get_var_name(v), v->var_type);
#endif
      return "";
    }
}

int
get_int_var_by_num(lex_ctxt* lexic, int num, int defval)
{
  anon_nasl_var	*v = get_var_ref_by_num(lexic, num);
  return  var2int(v, defval);
}

int
get_int_var_by_name(lex_ctxt* lexic, const char* name, int defval)
{
  named_nasl_var	*v = get_var_ref_by_name(lexic, name, 1);
  return  var2int(&v->u, defval);
}

int 
get_int_local_var_by_name(lex_ctxt * lexic, const char * name, int defval)
{
  named_nasl_var 	*v = get_var_ref_by_name(lexic, name, 0);
  return var2int(&v->u, defval);
}


char*
get_str_var_by_num(lex_ctxt* lexic, int num)
{
  anon_nasl_var	*v = get_var_ref_by_num(lexic, num);
  return  (char*)var2str(v);
}

char*
get_str_var_by_name(lex_ctxt* lexic, const char* name)
{
  named_nasl_var	*v = get_var_ref_by_name(lexic, name, 1);
  return  (char*)var2str(&v->u);
}

char *
get_str_local_var_by_name(lex_ctxt * lexic, const char * name)
{
  named_nasl_var	*v = get_var_ref_by_name(lexic, name, 0);
  return  (char*)var2str(&v->u);
}

static int
get_var_size(const anon_nasl_var* v)
{
  if (v == NULL)
    return 0;
  switch (v->var_type)
    {
    case VAR2_DATA:
    case VAR2_STRING:
      return v->v.v_str.s_siz;
#if 0
      /*
       * This code confuses nasl_string() because it does not returns the same
       * length as array2str
       */
    case VAR2_ARRAY:
      return v->v.v_arr.max_idx; /* Do not count 'named' elements yet */
#endif
    }
  return 0;
}

int
get_var_size_by_name(lex_ctxt* lexic, const char* name)
{
  named_nasl_var	*v = get_var_ref_by_name(lexic, name, 1);
  return get_var_size(&v->u);
}

int
get_local_var_size_by_name(lex_ctxt * lexic, const char * name)
{
  named_nasl_var 	*v = get_var_ref_by_name(lexic, name, 0);
  return get_var_size(&v->u);
}

int
get_var_size_by_num(lex_ctxt* lexic, int num)
{
  anon_nasl_var	*v = get_var_ref_by_num(lexic, num);
  return get_var_size(v);
}


int
get_var_type_by_num(lex_ctxt* lexic, int num)
{
  anon_nasl_var	*v = get_var_ref_by_num(lexic, num);
  return v == NULL ? VAR2_UNDEF : v->var_type;
}

int
get_var_type_by_name(lex_ctxt* lexic, const char * name)
{
  named_nasl_var	*v = get_var_ref_by_name(lexic, name, 1);
  return v == NULL ? VAR2_UNDEF : v->u.var_type;
}

int get_local_var_type_by_name(lex_ctxt * lexic, const char * name)
{
  named_nasl_var	*v = get_var_ref_by_name(lexic, name, 0);
  return v == NULL ? VAR2_UNDEF : v->u.var_type;
}

nasl_iterator
nasl_array_iterator(tree_cell* c)
{
  nasl_iterator		it;
  anon_nasl_var	*v;

  it.a = NULL;
  it.v = NULL;
  it.i1 = 0;
  it.iH = 0;
  
  if (c == NULL || c == FAKE_CELL)
    return it;

  if (c->type == REF_VAR)
    {
      v = c->x.ref_val;
      if (v == NULL || v->var_type != VAR2_ARRAY)
	return it;
      it.a = &v->v.v_arr;
    }
  else if (c->type == REF_ARRAY || c->type == DYN_ARRAY)
    {
      it.a = c->x.ref_val;
    }
  else
    {
      nasl_perror(NULL, "nasl_array_iterator: unhandled type %d (0x%x)\n",
	      c->type, c->type);
    }

  return it;
}

tree_cell*
nasl_iterate_array(nasl_iterator* it)
{
  anon_nasl_var	 *av;


  if (it == NULL || it->a == NULL)
    return NULL;

  if (it->i1 >= 0)
    {
      while (it->i1 < it->a->max_idx)
	{
	  av = it->a->num_elt[it->i1 ++];
	  if (av != NULL && av->var_type != VAR2_UNDEF)
	    return var2cell(av);
	}
      it->i1 = -1;
    }

  if (it->a->hash_elt == NULL)
    return NULL;

  if (it->v != NULL)
    it->v = it->v->next_var;
  do
    {
      while (it->v == NULL)
	if (it->iH >= VAR_NAME_HASH)
	  return NULL;
	else
	  it->v = it->a->hash_elt[it->iH ++];

      while (it->v != NULL && it->v->u.var_type == VAR2_UNDEF)
	it->v = it->v->next_var;
    }
  while (it->v == NULL);


  return var2cell(&it->v->u);
}

int
add_var_to_list(nasl_array* a, int i, const anon_nasl_var* v)
{
  anon_nasl_var	*v2;

  if (i < 0)
    {
      nasl_perror(NULL, "add_var_to_list: negative index are not (yet) supported\n");
      return -1;
    }

  if (i >= a->max_idx)
    {
      a->num_elt = erealloc(a->num_elt, sizeof(anon_nasl_var*) * (i+1));
      bzero(a->num_elt + a->max_idx, sizeof(anon_nasl_var*) * (i+1-a->max_idx));
      a->max_idx = i +1;
    }

  free_anon_var(a->num_elt[i]);
  v2 = dup_anon_var(v);	/* May return NULL */
  a->num_elt[i] = v2;
  if (v2 == NULL)
    return 0;
  else
    return 1;
}

int add_var_to_array(nasl_array *a, char * name, const anon_nasl_var* v)
{
 named_nasl_var * v2;
 int h = hash_str(name);
 
 if( a->hash_elt == NULL )
 {
  a->hash_elt = emalloc(VAR_NAME_HASH * sizeof(named_nasl_var*));
 }
 
 v2 = emalloc(sizeof(named_nasl_var));
 v2->var_name = estrdup(name);
 v2->u.var_type = VAR2_UNDEF;
 v2->next_var = a->hash_elt[h];
 a->hash_elt[h] = v2;
 
  
 copy_anon_var(&(v2->u), v);
 return 0;
}

/*
 * The name is not great: this function does not returns the index of the
 * last element, but the index of the next free slot
 */
int
array_max_index(nasl_array* a)
{
  int	i;

  for (i = a->max_idx - 1; i >= 0; i --)
    if (a->num_elt[i] != NULL && a->num_elt[i]->var_type != VAR2_UNDEF)
      {
	/* Fixing max_index will realloc() at next store.
	 * I am not sure it is a good idea
	 * Wait and see */
	a->max_idx = i + 1;
	return i+1;
      }
  return 0;
}

