/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

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

#include <glib.h>               /* for g_free */

#include "regex.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"

tree_cell *
alloc_tree_cell (int lnb, char *s)
{
  tree_cell *p = g_malloc0 (sizeof (tree_cell));
  int i;

  p->type = 0;
  p->size = 0;
  p->line_nb = lnb;
  p->x.str_val = s;
  p->ref_count = 1;
  for (i = 0; i < 4; i++)
    p->link[i] = NULL;
  return p;
}

tree_cell *
alloc_typed_cell (int typ)
{
  tree_cell *c = alloc_tree_cell (0, NULL);
  c->type = typ;
  return c;
}

tree_cell *
alloc_RE_cell (int lnb, int t, tree_cell * l, char *re_str)
{
  regex_t *re = g_malloc0 (sizeof (regex_t));
  int e;

  tree_cell *c = alloc_tree_cell (lnb, NULL);
  c->type = t;                  /* We could check the type... */
  c->link[0] = l;
  c->link[1] = FAKE_CELL;
  e = regcomp (re, re_str, REG_EXTENDED | REG_NOSUB | REG_ICASE);
  if (!e)
    c->x.ref_val = re;
  else
    {
      char errbuf[100];
      regerror (e, re, errbuf, sizeof (errbuf));
      nasl_perror (NULL, "Line %d: Cannot compile regex: %s (error %d: %s)\n",
                   lnb, re_str, e, errbuf);
      g_free (re);
    }
  g_free (re_str);
  return c;
}

tree_cell *
alloc_expr_cell (int lnb, int t, tree_cell * l, tree_cell * r)
{
  tree_cell *c = alloc_tree_cell (lnb, NULL);
  c->type = t;
  c->link[0] = l;
  c->link[1] = r;
  return c;
}

tree_cell *
dup_cell (const tree_cell * tc)
{
  tree_cell *r;
  int i;

  if (tc == NULL)
    return NULL;
  else if (tc == FAKE_CELL)
    return FAKE_CELL;

  r = alloc_tree_cell (tc->line_nb, NULL);
  r->type = tc->type;
  r->size = tc->size;
  switch (tc->type)
    {
    case CONST_STR:
    case CONST_DATA:
      r->x.str_val = g_malloc0 (tc->size + 1);
      memcpy (r->x.str_val, tc->x.str_val, tc->size);
      break;
    default:
      r->x = tc->x;
      break;
    }

  for (i = 0; i < 4; i++)
    r->link[i] = dup_cell (tc->link[i]);
  return r;
}

static void
free_tree (tree_cell * c)
{
  int i;
  nasl_array *a;

  if (c == NULL || c == FAKE_CELL)
    return;
#if 0
  nasl_dump_tree (c);
#endif
  for (i = 0; i < 4; i++)
    if (c->link[i] != NULL)
      deref_cell (c->link[i]);
  if (c->x.str_val != NULL)
    switch (c->type)
      {
      case CONST_STR:
      case CONST_DATA:
#ifdef SCRATCH_FREED_MEMORY
        if (c->size > 0)
          memset (c->x.str_val, 0xFF, c->size);
#endif
        g_free (c->x.str_val);
        break;

      case CONST_REGEX:
      case COMP_RE_MATCH:
      case COMP_RE_NOMATCH:
        if (c->x.ref_val != NULL)
          {
            regfree (c->x.ref_val);
            g_free (c->x.ref_val);
          }
        break;

      case DYN_ARRAY:
        a = c->x.ref_val;
        if (a != NULL)
          {
            free_array (a);
            g_free (c->x.ref_val);
          }
        break;

      case NODE_FUN_DEF:
      case NODE_FUN_CALL:
      case NODE_VAR:
      case NODE_DECL:
      case NODE_ARG:
      case NODE_ARRAY_EL:
      case NODE_FOREACH:
        g_free (c->x.str_val);
        break;
      }
#ifdef SCRATCH_FREED_MEMORY
  memset (c, 0xFF, sizeof (*c));
#endif
  g_free (c);
}

void
ref_cell (tree_cell * c)
{
  if (c == NULL || c == FAKE_CELL)
    return;
  c->ref_count++;
  if (c->ref_count < 0)
    {
      nasl_perror (NULL, "ref_cell: ref count is negative!\n");
      nasl_dump_tree (c);
      abort ();
    }
}

void
deref_cell (tree_cell * c)
{
  if (c == NULL || c == FAKE_CELL)
    return;
  if (--c->ref_count <= 0)
    free_tree (c);
}

/* Debug */

static char *node_names[] = {
  "NODE_EMPTY",
  "NODE_IF_ELSE",
  "NODE_INSTR_L",
  "NODE_FOR",
  "NODE_WHILE",
  "NODE_FOREACH",
  "NODE_REPEAT_UNTIL",
  "NODE_REPEATED",
  "NODE_FUN_DEF",
  "NODE_FUN_CALL",
  "NODE_DECL",
  "NODE_ARG",
  "NODE_RETURN",
  "NODE_BREAK",
  "NODE_CONTINUE",

  "NODE_ARRAY_EL",
  "NODE_AFF",
  "NODE_VAR",
  "NODE_LOCAL",
  "NODE_GLOBAL",
  "NODE_PLUS_EQ",
  "NODE_MINUS_EQ",
  "NODE_MULT_EQ",
  "NODE_DIV_EQ",
  "NODE_MODULO_EQ",

  "NODE_L_SHIFT_EQ",
  "NODE_R_SHIFT_EQ",
  "NODE_R_USHIFT_EQ",
  "EXPR_AND",
  "EXPR_OR",
  "EXPR_NOT",

  "EXPR_PLUS",
  "EXPR_MINUS",
  "EXPR_U_MINUS",
  "EXPR_MULT",
  "EXPR_DIV",
  "EXPR_MODULO",
  "EXPR_EXPO",

  "EXPR_BIT_AND",
  "EXPR_BIT_OR",
  "EXPR_BIT_XOR",
  "EXPR_BIT_NOT",
  "EXPR_INCR",
  "EXPR_DECR",
  "EXPR_L_SHIFT",
  "EXPR_R_SHIFT",
  "EXPR_R_USHIFT",

  "COMP_MATCH",
  "COMP_NOMATCH",
  "COMP_RE_MATCH",
  "COMP_RE_NOMATCH",

  "COMP_LT",
  "COMP_LE",
  "COMP_EQ",
  "COMP_NE",
  "COMP_GT",
  "COMP_GE",
  "CONST_INT",
  "CONST_STR",
  "CONST_DATA",
  "CONST_REGEX",

  "ARRAY_ELEM",

  "REF_VAR",
  "REF_ARRAY",
  "DYN_ARRAY"
};

static void
prefix (int n, int i)
{
  int j;
  for (j = 0; j < n; j++)
    putchar (' ');
  if (i <= 0)
    fputs ("   ", stdout);
  else
    printf ("%d: ", i);
}

char *
dump_cell_val (const tree_cell * c)
{
  static char txt[80];

  if (c == NULL)
    return "NULL";
  else if (c == FAKE_CELL)
    return "FAKE";
  else
    switch (c->type)
      {
      case CONST_INT:
        snprintf (txt, sizeof (txt), "%d", c->x.i_val); /* RATS: ignore */
        break;
      case CONST_STR:
      case CONST_DATA:         /* Beurk */
        if (c->size >= sizeof (txt) + 2)
          {
            snprintf (txt, sizeof (txt), "\"%s", c->x.str_val); /* RATS: ignore */
            strcpy (txt + (sizeof (txt) - 5), "...\"");
          }
        else
          snprintf (txt, sizeof (txt), "\"%s\"", c->x.str_val); /* RATS: ignore */
        break;
      default:
        snprintf (txt, sizeof (txt), "???? (%s)", nasl_type_name (c->type));    /* RATS: ignore */
        break;
      }
  return txt;
}

static void
dump_tree (const tree_cell * c, int n, int idx)
{
  int i;

  if (c == NULL)
    return;

  prefix (n, idx);

  if (c == FAKE_CELL)
    {
      puts ("* FAKE *");
      return;
    }

  if (c->line_nb > 0)
    printf ("L%d: ", c->line_nb);

#if 0
  if ((int) c < 0x1000)
    {
      printf ("* INVALID PTR 0x%x *\n", (int) c);
      return;
    }
#endif
  if (c->type < 0 || c->type >= sizeof (node_names) / sizeof (node_names[0]))
    printf ("* UNKNOWN %d (0x%x)*\n", c->type, c->type);
  else
    printf ("%s (%d)\n", node_names[c->type], c->type);


  prefix (n, idx);
  printf ("Ref_count=%d", c->ref_count);
  if (c->size > 0)
    {
      /*prefix(n, idx); */
      printf ("\tSize=%d (0x%x)", c->size, c->size);
    }
  putchar ('\n');

  switch (c->type)
    {
    case CONST_INT:
      prefix (n, 0);
      printf ("Val=%d\n", c->x.i_val);
      break;

    case CONST_STR:
    case CONST_DATA:
    case NODE_VAR:
    case NODE_FUN_DEF:
    case NODE_FUN_CALL:
    case NODE_DECL:
    case NODE_ARG:
    case NODE_ARRAY_EL:
    case ARRAY_ELEM:
      prefix (n, 0);
      if (c->x.str_val == NULL)
        printf ("Val=(null)\n");
      else
        printf ("Val=\"%s\"\n", c->x.str_val);
      break;
    case REF_VAR:
      prefix (n, 0);
      if (c->x.ref_val == NULL)
        printf ("Ref=(null)\n");
      else
        {
          named_nasl_var *v = c->x.ref_val;
          printf ("Ref=(type=%d, name=%s, value=%s)\n", v->u.var_type,
                  v->var_name != NULL ? v->var_name : "(null)",
                  var2str (&v->u));
        }
      break;

    case REF_ARRAY:
    case DYN_ARRAY:
      break;
    }

  for (i = 0; i < 4; i++)
    {
      dump_tree (c->link[i], n + 3, i + 1);
    }
}

const char *
nasl_type_name (int t)
{
  static char txt4[4][32];      /*  This function may be called 4 times in the same expression */
  static int i = 0;
  char *txt;

  if (++i > 4)
    i = 0;
  txt = txt4[i];

  if (t >= 0 || t < sizeof (node_names) / sizeof (node_names[0]))
    snprintf (txt, 32, "%s (%d)", node_names[t], t);    /* RATS: ignore */
  else
    snprintf (txt, 32, "*UNKNOWN* (%d)", t);    /* RATS: ignore */
  return txt;
}


void
nasl_dump_tree (const tree_cell * c)
{
  printf ("^^^^ %p ^^^^^\n", c);
  if (c == NULL)
    puts ("NULL CELL");
  else if (c == FAKE_CELL)
    puts ("FAKE CELL");
  else
    dump_tree (c, 0, 0);
  printf ("vvvvvvvvvvvvvvvvvv\n");
}

char *
get_line_nb (const tree_cell * c)
{
  static char txt[32];
  if (c == NULL || c == FAKE_CELL || c->line_nb <= 0)
    return "";
  snprintf (txt, sizeof (txt), " at or near line %d ", c->line_nb);     /* RATS: ignore */
  return txt;
}


int
nasl_is_leaf (const tree_cell * pc)
{
  if (pc == NULL || pc == FAKE_CELL)
    return 1;
  switch (pc->type)
    {
    case CONST_INT:
    case CONST_STR:
    case CONST_DATA:
    case REF_ARRAY:
    case DYN_ARRAY:
      return 1;
    default:
      return 0;
    }
 /*NOTREACHED*/}

int
cell_type (const tree_cell * c)
{
  if (c == NULL || c == FAKE_CELL)
    return 0;
  else
    return c->type;
}
