/* Nessus Attack Scripting Language 
 *
 * Copyright (C) 2002 - 2004 Tenable Network Security
 *
 * 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.
 *
 */

#include <stdlib.h> /* for abort */
#include <string.h> /* for strlen */

#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 "system.h"
#include "nasl_debug.h"

#ifndef NASL_DEBUG
#define NASL_DEBUG 0
#endif

/* Local prototypes */
static void copy_array (nasl_array*, const nasl_array*, int);


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

/** @TODO Consider using GLibs string hash function. */
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;
}


/**
 * @brief 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 (num < 0)			/* safer */
    {
      nasl_perror(ctxt, "Negative index %d is invalid for array\n", num);
      return NULL;
    }

  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)
{
  if (name == NULL)
    return NULL;
  /* Broken: Need also code in get_array_elem */
  if (strcmp(name, "_FCT_ANON_ARGS") == 0)
      {
	tree_cell	*retc = alloc_typed_cell(DYN_ARRAY);
	nasl_array	*a = retc->x.ref_val = emalloc(sizeof(nasl_array));
	copy_array(a, &ctxt->ctx_vars, 0);
	return retc;
      }
  else
    {
      named_nasl_var	* v = get_var_ref_by_name(ctxt, name, 1);
      return var2cell(&v->u);
    }
  /*NOTREACHED*/
}

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), "[%p]", v); /* RATS: ignore */
  return str;
}

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


  /* Fake variable */
  if (strcmp(name, "_FCT_ANON_ARGS") == 0)
    {
      lex_ctxt	*c;
      for (c = ctxt; c != NULL && ! c->fct_ctxt; c = c->up_ctxt)
	;
      if (c == NULL)
	return NULL;
      fake_var.var_type = VAR2_ARRAY;
      fake_var.v.v_arr = c->ctx_vars;
      fake_var.v.v_arr.hash_elt = NULL;	/* mask named elements */
      u = &fake_var;
    }
  else
    {
      named_nasl_var	*v = get_var_ref_by_name(ctxt, name, 1);
      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, 
				   /* avoid dangling pointers */
				   strcmp(name, "_FCT_ANON_ARGS"));
	  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:
      copy_array(&v1->v.v_arr, &v2->v.v_arr, 1);
      break;

    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 copy_named)
{
  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;
    }

 if ( a1 == NULL || a2 == NULL )
 {
    nasl_perror(NULL, "Internal inconsistency - null array\n");
    abort();
 }

  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 (copy_named && 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, 1);
  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;


  if ( v1 == NULL || v1 == FAKE_CELL )
	return NULL;

  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 == NULL )
	{
	  t2 = 0;
	  a = NULL;
	  break;
	}

      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, 1);
#if 0
	/* MA 2004-08-13: I guess that the code changed somewhere some day and
	 * this part created a memory leak */
	if (v0.var_type == VAR2_ARRAY)
	  bzero(&v0, sizeof(v0));	/* So that we don't clear the variable twice */
#endif
	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)
	{
	  if (val != NULL)
	    nasl_perror(lexic, "Cannot add existing variable %d\n", num);
#if NASL_DEBUG > 0
	  else
	    nasl_perror(lexic, "Will not clear existing variable %d\n", num);
#endif
	  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)
      {
	if (val != NULL)
	  nasl_perror(lexic, "Cannot add existing variable %s\n", name);
#if NASL_DEBUG > 0
	else
	  nasl_perror(lexic, "Will not clear existing variable %s\n", name);
#endif
	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 NASL_DEBUG > 0
  const char		*name;
#endif

  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)
    {
#if NASL_DEBUG > 0
      nasl_perror(lexic, "nasl_read_var_ref: NULL variable in REF_VAR\n");
#endif
      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((char*)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((char*)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((char*)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); /* RATS: ignore */
	      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); /* RATS: ignore */
		  n += strlen(s + n);
		}
	      else
		{
		  snprintf(s+n, 70, "%d: '%s", i, u->v.v_str.s_val); /* RATS: ignore */
		  n += strlen(s + n);
		  n += sprintf(s+n, "'...");
		}
	      break;
	    default:
	      snprintf(s+n, len-n, "%d: ????", i); /* RATS: ignore */
	      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); /* RATS: ignore */
		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); /* RATS: ignore */
		    n += strlen(s + n);
		  }
		else
		  {
		    snprintf(s+n, 70+l, "%s: '%s", v->var_name, u->v.v_str.s_val); /* RATS: ignore */
		    n += strlen(s + n);
		    n += sprintf(s+n, "'...");
		  }
		break;
	      default:
		snprintf(s+n, len-n, "%s: ????", v->var_name); /* RATS: ignore */
		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); /* RATS: ignore */
      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);
}

/**
 * @brief Returns NASL variable/cell type, VAR2_UNDEF if value is NULL.
 */
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;
}

/**
 * make_array_from_list is used by the parser only
 * The list of elements is freed after use
 */
tree_cell*
make_array_from_elems (tree_cell* el)
{
  int		n;
  tree_cell	*c, *c2;
  nasl_array	*a;
  anon_nasl_var	v;

  a = emalloc(sizeof(nasl_array));
  /* Either the elements are all "named", or they are "numbered". No mix! */
  if (el->x.str_val == NULL) /* numbered */
    {
      for (n = 0, c = el; c != NULL; c= c->link[1])
	n ++;
      a->max_idx = n;
      a->num_elt = emalloc(sizeof(anon_nasl_var*) * n);
      a->hash_elt = NULL;
    }
  else
    {
      a->num_elt = NULL;
      a->hash_elt = emalloc(VAR_NAME_HASH * sizeof(named_nasl_var*));
    }

  for (n = 0, c = el; c != NULL; c= c->link[1])
    {
      c2 = c->link[0];
      if (c2 != NULL && c2 != FAKE_CELL)
	{
	  memset(&v, 0, sizeof(v));
	  switch (c2->type)
	    {
	    case CONST_INT:
	      v.var_type = VAR2_INT;
	      v.v.v_int = c2->x.i_val;
	      break;
	    case CONST_STR:
	    case CONST_DATA:
	      v.var_type = c2->type == CONST_STR ? VAR2_STRING : VAR2_DATA;
	      if (c2->x.str_val == NULL )
		{
		  v.v.v_str.s_val = NULL;
		  v.v.v_str.s_siz = 0;
		}
	      else
		{
		  v.v.v_str.s_siz = c2->size;
		  v.v.v_str.s_val = (unsigned char*)c2->x.str_val;
		}
	      break;
	    default:
	      nasl_perror(NULL, "make_array_from_list: unhandled cell type %s at position %d\n", nasl_type_name(c2->type), n);
	      v.var_type = VAR2_UNDEF;
	      break;
	    }
	}

      if (c->x.str_val == NULL)
	add_var_to_list(a, n ++, &v);
      else
	add_var_to_array(a, c->x.str_val, &v);
    }

  c = alloc_typed_cell(DYN_ARRAY);
  c->x.ref_val = a;
  deref_cell(el);
  return c;
}
