<commit_before>/*
 * Copyright 2001-2004 <NAME>
 * All Rights Reserved.
 *
 * ClearSilver Templating System
 *
 * This code is made available under the terms of the ClearSilver License.
 * http://www.clearsilver.net/license.hdf
 *
 */

#include "cs_config.h"

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <sys/stat.h>
#include "neo_misc.h"
#include "neo_err.h"
#include "neo_rand.h"
#include "neo_hdf.h"
#include "neo_str.h"
#include "ulist.h"

/* Ok, in order to use the hash, we have to support n-len strings
 * instead of null terminated strings (since in set_value and walk_hdf
 * we are merely using part of the HDF name for lookup, and that might
 * be a const, and we don't want the overhead of allocating/copying
 * that data out...)
 *
 * Since HASH doesn't maintain any data placed in it, merely pointers to
 * it, we use the HDF node itself as the key, and have specific
 * comp/hash functions which just use the name/name_len as the key.
 */

static int hash_hdf_comp(const void *a, const void *b)
{
  HDF *ha = (HDF *)a;
  HDF *hb = (HDF *)b;

  return (ha->name_len == hb->name_len) && !strncmp(ha->name, hb->name, ha->name_len);
}

static UINT32 hash_hdf_hash(const void *a)
{
  HDF *ha = (HDF *)a;
  return ne_crc((UINT8 *)(ha->name), ha->name_len);
}

static NEOERR *_alloc_hdf (HDF **hdf, const char *name, size_t nlen, 
                           const char *value, int dup, int wf, HDF *top)
{
  *hdf = calloc (1, sizeof (HDF));
  if (*hdf == NULL)
  {
    return nerr_raise (NERR_NOMEM, "Unable to allocate memory for hdf element");
  }

  (*hdf)->top = top;

  if (name != NULL)
  {
    (*hdf)->name_len = nlen;
    (*hdf)->name = (char *) malloc (nlen + 1);
    if ((*hdf)->name == NULL)
    {
      free((*hdf));
      (*hdf) = NULL;
      return nerr_raise (NERR_NOMEM, 
	  "Unable to allocate memory for hdf element: %s", name);
    }
    strncpy((*hdf)->name, name, nlen);
    (*hdf)->name[nlen] = '\0';
  }
  if (value != NULL)
  {
    if (dup)
    {
      (*hdf)->alloc_value = 1;
      (*hdf)->value = strdup(value);
      if ((*hdf)->value == NULL)
      {
	free((*hdf)->name);
	free((*hdf));
	(*hdf) = NULL;
	return nerr_raise (NERR_NOMEM, 
	    "Unable to allocate memory for hdf element %s", name);
      }
    }
    else
    {
      (*hdf)->alloc_value = wf;
      /* We're overriding the const of value here for the set_buf case
       * where we overrode the char * to const char * earlier, since
       * alloc_value actually keeps track of the const-ness for us */
      (*hdf)->value = (char *)value;
    }
  }
  return STATUS_OK;
}

static void _dealloc_hdf_attr(HDF_ATTR **attr)
{
  HDF_ATTR *next;

  while ((*attr) != NULL)
  {
    next = (*attr)->next;
    if ((*attr)->key) free((*attr)->key);
    if ((*attr)->value) free((*attr)->value);
    free(*attr);
    *attr = next;
  }
  *attr = NULL;
}

static void _dealloc_hdf (HDF **hdf)
{
  HDF *myhdf = *hdf;
  HDF *next = NULL;

  if (myhdf == NULL) return;
  if (myhdf->child != NULL)
    _dealloc_hdf(&(myhdf->child));

  /* This was easier recursively, but dangerous on long lists, so we
   * walk it ourselves */
  next = myhdf->next;
  while (next != NULL) 
  {
    myhdf->next = next->next;
    next->next = NULL;
    _dealloc_hdf(&next);
    next = myhdf->next;
  }
  if (myhdf->name != NULL)
  {
    free (myhdf->name);
    myhdf->name = NULL;
  }
  if (myhdf->value != NULL)
  {
    if (myhdf->alloc_value)
      free (myhdf->value);
    myhdf->value = NULL;
  }
  if (myhdf->attr != NULL)
  {
    _dealloc_hdf_attr(&(myhdf->attr));
  }
  if (myhdf->hash != NULL)
  {
    ne_hash_destroy(&myhdf->hash);
  }
  free(myhdf);
  *hdf = NULL;
}

NEOERR* hdf_init (HDF **hdf)
{
  NEOERR *err;
  HDF *my_hdf;

  *hdf = NULL;

  err = nerr_init();
  if (err != STATUS_OK)
    return nerr_pass (err);

  err = _alloc_hdf (&my_hdf, NULL, 0, NULL, 0, 0, NULL);
  if (err != STATUS_OK)
    return nerr_pass (err);

  my_hdf->top = my_hdf;

  *hdf = my_hdf;

  return STATUS_OK;
}

void hdf_destroy (HDF **hdf)
{
  if (*hdf == NULL) return;
  if ((*hdf)->top == (*hdf))
  {
    _dealloc_hdf(hdf);
  }
}

static int _walk_hdf (HDF *hdf, const char *name, HDF **node)
{
  HDF *parent = NULL;
  HDF *hp = hdf;
  HDF hash_key;
  int x = 0;
  const char *s, *n;
  int r;

  *node = NULL;

  if (hdf == NULL) return -1;
  if (name == NULL || name[0] == '\0')
  {
    *node = hdf;
    return 0;
  }

  if (hdf->link)
  {
    r = _walk_hdf (hdf->top, hdf->value, &hp);
    if (r) return r;
    if (hp) 
    {
      parent = hp;
      hp = hp->child;
    }
  }
  else
  {
    parent = hdf;
    hp = hdf->child;
  }
  if (hp == NULL)
  {
    return -1;
  }

  n = name;
  s = strchr (n, '.');
  x = (s == NULL) ? strlen(n) : s - n;

  while (1)
  {
    if (parent && parent->hash)
    {
      hash_key.name = (char *)n;
      hash_key.name_len = x;
      hp = ne_hash_lookup(parent->hash, &hash_key);
    }
    else 
    {
      while (hp != NULL)
      {
	if (hp->name && (x == hp->name_len) && !strncmp(hp->name, n, x))
	{
	  break;
	}
	else
	{
	  hp = hp->next;
	}
      }
    }
    if (hp == NULL)
    {
      return -1;
    }
    if (s == NULL) break;
   
    if (hp->link)
    {
      r = _walk_hdf (hp->top, hp->value, &hp);
      if (r) {
	return r;
      }
      parent = hp;
      hp = hp->child;
    }
    else
    {
      parent = hp;
      hp = hp->child;
    }
    n = s + 1;
    s = strchr (n, '.');
    x = (s == NULL) ? strlen(n) : s - n;
  } 
  if (hp->link)
  {
    return _walk_hdf (hp->top, hp->value, node);
  }

  *node = hp;
  return 0;
}

int hdf_get_int_value (HDF *hdf, const char *name, int defval)
{
  HDF *node;
  int v;
  char *n;

  if ((_walk_hdf(hdf, name, &node) == 0) && (node->value != NULL))
  {
    v = strtol (node->value, &n, 10);
    if (node->value == n) v = defval;
    return v;
  }
  return defval;
}

/* This should return a const char *, but changing this would have big
 * repurcussions for any C code using this function, so no change for now */
char* hdf_get_value (HDF *hdf, const char *name, const char *defval)
{
  HDF *node;

  if ((_walk_hdf(hdf, name, &node) == 0) && (node->value != NULL))
  {
    return node->value;
  }
  return (char *)defval;
}

char* hdf_get_valuevf (HDF *hdf, const char *namefmt, va_list ap) 
{
  HDF *node;
  char *name;

  name = vsprintf_alloc(namefmt, ap);
  if (name == NULL) return NULL;
  if ((_walk_hdf(hdf, name, &node) == 0) && (node->value != NULL))
  {
    free(name);
    return node->value;
  }
  free(name);
  return NULL;
}

char* hdf_get_valuef (HDF *hdf, const char *namefmt, ...)
{
  char *val;
  va_list ap;

  va_start(ap, namefmt);
  val = hdf_get_valuevf(hdf, namefmt, ap);
  va_end(ap);
  return val;
}

NEOERR* hdf_get_copy (HDF *hdf, const char *name, char **value, 
                      const char *defval)
{
  HDF *node;

  if ((_walk_hdf(hdf, name, &node) == 0) && (node->value != NULL))
  {
    *value = strdup(node->value);
    if (*value == NULL)
    {
      return nerr_raise (NERR_NOMEM, "Unable to allocate copy of %s", name);
    }
  }
  else
  {
    if (defval == NULL)
      *value = NULL;
    else
    {
      *value = strdup(defval);
      if (*value == NULL)
      {
	return nerr_raise (NERR_NOMEM, "Unable to allocate copy of %s", name);
      }
    }
  }
  return STATUS_OK;
}

HDF* hdf_get_obj (HDF *hdf, const char *name)
{
  HDF *obj;

  _walk_hdf(hdf, name, &obj);
  return obj;
}

HDF* hdf_get_child (HDF *hdf, const char *name)
{
  HDF *obj;
  _walk_hdf(hdf, name, &obj);
  if (obj != NULL) return obj->child;
  return obj;
}

HDF_ATTR* hdf_get_attr (HDF *hdf, const char *name)
{
  HDF *obj;
  _walk_hdf(hdf, name, &obj);
  if (obj != NULL) return obj->attr;
  return NULL;
}

NEOERR* hdf_set_attr (HDF *hdf, const char *name, const char *key, 
                      const char *value)
{
  HDF *obj;
  HDF_ATTR *attr, *last;

  _walk_hdf(hdf, name, &obj);
  if (obj == NULL) 
    return nerr_raise(NERR_ASSERT, "Unable to set attribute on none existant node");

  if (obj->attr != NULL)
  {
    attr = obj->attr;
    last = attr;
    while (attr != NULL)
    {
      if (!strcmp(attr->key, key))
      {
	if (attr->value) free(attr->value);
	/* a set of NULL deletes the attr */
	if (value == NULL)
	{
	  if (last == obj->attr)
	    obj->attr = attr->next;
	  else
	    last->next = attr->next;
	  free(attr->key);
	  free(attr);
	  return STATUS_OK;
	}
	attr->value = strdup(value);
	if (attr->value == NULL)
	  return nerr_raise(NERR_NOMEM, "Unable to set attr %s to %s", key, value);
	return STATUS_OK;
      }
      last = attr;
      attr = attr->next;
    }
    last->next = (HDF_ATTR *) calloc(1, sizeof(HDF_ATTR));
    if (last->next == NULL)
      return nerr_raise(NERR_NOMEM, "Unable to set attr %s to %s", key, value);
    attr = last->next;<commit_msg>fix "deleting" attribute values bug and fix reported by <NAME> <<EMAIL>><commit_after>/*
 * Copyright 2001-2004 <NAME>
 * All Rights Reserved.
 *
 * ClearSilver Templating System
 *
 * This code is made available under the terms of the ClearSilver License.
 * http://www.clearsilver.net/license.hdf
 *
 */

#include "cs_config.h"

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <sys/stat.h>
#include "neo_misc.h"
#include "neo_err.h"
#include "neo_rand.h"
#include "neo_hdf.h"
#include "neo_str.h"
#include "ulist.h"

/* Ok, in order to use the hash, we have to support n-len strings
 * instead of null terminated strings (since in set_value and walk_hdf
 * we are merely using part of the HDF name for lookup, and that might
 * be a const, and we don't want the overhead of allocating/copying
 * that data out...)
 *
 * Since HASH doesn't maintain any data placed in it, merely pointers to
 * it, we use the HDF node itself as the key, and have specific
 * comp/hash functions which just use the name/name_len as the key.
 */

static int hash_hdf_comp(const void *a, const void *b)
{
  HDF *ha = (HDF *)a;
  HDF *hb = (HDF *)b;

  return (ha->name_len == hb->name_len) && !strncmp(ha->name, hb->name, ha->name_len);
}

static UINT32 hash_hdf_hash(const void *a)
{
  HDF *ha = (HDF *)a;
  return ne_crc((UINT8 *)(ha->name), ha->name_len);
}

static NEOERR *_alloc_hdf (HDF **hdf, const char *name, size_t nlen, 
                           const char *value, int dup, int wf, HDF *top)
{
  *hdf = calloc (1, sizeof (HDF));
  if (*hdf == NULL)
  {
    return nerr_raise (NERR_NOMEM, "Unable to allocate memory for hdf element");
  }

  (*hdf)->top = top;

  if (name != NULL)
  {
    (*hdf)->name_len = nlen;
    (*hdf)->name = (char *) malloc (nlen + 1);
    if ((*hdf)->name == NULL)
    {
      free((*hdf));
      (*hdf) = NULL;
      return nerr_raise (NERR_NOMEM, 
	  "Unable to allocate memory for hdf element: %s", name);
    }
    strncpy((*hdf)->name, name, nlen);
    (*hdf)->name[nlen] = '\0';
  }
  if (value != NULL)
  {
    if (dup)
    {
      (*hdf)->alloc_value = 1;
      (*hdf)->value = strdup(value);
      if ((*hdf)->value == NULL)
      {
	free((*hdf)->name);
	free((*hdf));
	(*hdf) = NULL;
	return nerr_raise (NERR_NOMEM, 
	    "Unable to allocate memory for hdf element %s", name);
      }
    }
    else
    {
      (*hdf)->alloc_value = wf;
      /* We're overriding the const of value here for the set_buf case
       * where we overrode the char * to const char * earlier, since
       * alloc_value actually keeps track of the const-ness for us */
      (*hdf)->value = (char *)value;
    }
  }
  return STATUS_OK;
}

static void _dealloc_hdf_attr(HDF_ATTR **attr)
{
  HDF_ATTR *next;

  while ((*attr) != NULL)
  {
    next = (*attr)->next;
    if ((*attr)->key) free((*attr)->key);
    if ((*attr)->value) free((*attr)->value);
    free(*attr);
    *attr = next;
  }
  *attr = NULL;
}

static void _dealloc_hdf (HDF **hdf)
{
  HDF *myhdf = *hdf;
  HDF *next = NULL;

  if (myhdf == NULL) return;
  if (myhdf->child != NULL)
    _dealloc_hdf(&(myhdf->child));

  /* This was easier recursively, but dangerous on long lists, so we
   * walk it ourselves */
  next = myhdf->next;
  while (next != NULL) 
  {
    myhdf->next = next->next;
    next->next = NULL;
    _dealloc_hdf(&next);
    next = myhdf->next;
  }
  if (myhdf->name != NULL)
  {
    free (myhdf->name);
    myhdf->name = NULL;
  }
  if (myhdf->value != NULL)
  {
    if (myhdf->alloc_value)
      free (myhdf->value);
    myhdf->value = NULL;
  }
  if (myhdf->attr != NULL)
  {
    _dealloc_hdf_attr(&(myhdf->attr));
  }
  if (myhdf->hash != NULL)
  {
    ne_hash_destroy(&myhdf->hash);
  }
  free(myhdf);
  *hdf = NULL;
}

NEOERR* hdf_init (HDF **hdf)
{
  NEOERR *err;
  HDF *my_hdf;

  *hdf = NULL;

  err = nerr_init();
  if (err != STATUS_OK)
    return nerr_pass (err);

  err = _alloc_hdf (&my_hdf, NULL, 0, NULL, 0, 0, NULL);
  if (err != STATUS_OK)
    return nerr_pass (err);

  my_hdf->top = my_hdf;

  *hdf = my_hdf;

  return STATUS_OK;
}

void hdf_destroy (HDF **hdf)
{
  if (*hdf == NULL) return;
  if ((*hdf)->top == (*hdf))
  {
    _dealloc_hdf(hdf);
  }
}

static int _walk_hdf (HDF *hdf, const char *name, HDF **node)
{
  HDF *parent = NULL;
  HDF *hp = hdf;
  HDF hash_key;
  int x = 0;
  const char *s, *n;
  int r;

  *node = NULL;

  if (hdf == NULL) return -1;
  if (name == NULL || name[0] == '\0')
  {
    *node = hdf;
    return 0;
  }

  if (hdf->link)
  {
    r = _walk_hdf (hdf->top, hdf->value, &hp);
    if (r) return r;
    if (hp) 
    {
      parent = hp;
      hp = hp->child;
    }
  }
  else
  {
    parent = hdf;
    hp = hdf->child;
  }
  if (hp == NULL)
  {
    return -1;
  }

  n = name;
  s = strchr (n, '.');
  x = (s == NULL) ? strlen(n) : s - n;

  while (1)
  {
    if (parent && parent->hash)
    {
      hash_key.name = (char *)n;
      hash_key.name_len = x;
      hp = ne_hash_lookup(parent->hash, &hash_key);
    }
    else 
    {
      while (hp != NULL)
      {
	if (hp->name && (x == hp->name_len) && !strncmp(hp->name, n, x))
	{
	  break;
	}
	else
	{
	  hp = hp->next;
	}
      }
    }
    if (hp == NULL)
    {
      return -1;
    }
    if (s == NULL) break;
   
    if (hp->link)
    {
      r = _walk_hdf (hp->top, hp->value, &hp);
      if (r) {
	return r;
      }
      parent = hp;
      hp = hp->child;
    }
    else
    {
      parent = hp;
      hp = hp->child;
    }
    n = s + 1;
    s = strchr (n, '.');
    x = (s == NULL) ? strlen(n) : s - n;
  } 
  if (hp->link)
  {
    return _walk_hdf (hp->top, hp->value, node);
  }

  *node = hp;
  return 0;
}

int hdf_get_int_value (HDF *hdf, const char *name, int defval)
{
  HDF *node;
  int v;
  char *n;

  if ((_walk_hdf(hdf, name, &node) == 0) && (node->value != NULL))
  {
    v = strtol (node->value, &n, 10);
    if (node->value == n) v = defval;
    return v;
  }
  return defval;
}

/* This should return a const char *, but changing this would have big
 * repurcussions for any C code using this function, so no change for now */
char* hdf_get_value (HDF *hdf, const char *name, const char *defval)
{
  HDF *node;

  if ((_walk_hdf(hdf, name, &node) == 0) && (node->value != NULL))
  {
    return node->value;
  }
  return (char *)defval;
}

char* hdf_get_valuevf (HDF *hdf, const char *namefmt, va_list ap) 
{
  HDF *node;
  char *name;

  name = vsprintf_alloc(namefmt, ap);
  if (name == NULL) return NULL;
  if ((_walk_hdf(hdf, name, &node) == 0) && (node->value != NULL))
  {
    free(name);
    return node->value;
  }
  free(name);
  return NULL;
}

char* hdf_get_valuef (HDF *hdf, const char *namefmt, ...)
{
  char *val;
  va_list ap;

  va_start(ap, namefmt);
  val = hdf_get_valuevf(hdf, namefmt, ap);
  va_end(ap);
  return val;
}

NEOERR* hdf_get_copy (HDF *hdf, const char *name, char **value, 
                      const char *defval)
{
  HDF *node;

  if ((_walk_hdf(hdf, name, &node) == 0) && (node->value != NULL))
  {
    *value = strdup(node->value);
    if (*value == NULL)
    {
      return nerr_raise (NERR_NOMEM, "Unable to allocate copy of %s", name);
    }
  }
  else
  {
    if (defval == NULL)
      *value = NULL;
    else
    {
      *value = strdup(defval);
      if (*value == NULL)
      {
	return nerr_raise (NERR_NOMEM, "Unable to allocate copy of %s", name);
      }
    }
  }
  return STATUS_OK;
}

HDF* hdf_get_obj (HDF *hdf, const char *name)
{
  HDF *obj;

  _walk_hdf(hdf, name, &obj);
  return obj;
}

HDF* hdf_get_child (HDF *hdf, const char *name)
{
  HDF *obj;
  _walk_hdf(hdf, name, &obj);
  if (obj != NULL) return obj->child;
  return obj;
}

HDF_ATTR* hdf_get_attr (HDF *hdf, const char *name)
{
  HDF *obj;
  _walk_hdf(hdf, name, &obj);
  if (obj != NULL) return obj->attr;
  return NULL;
}

NEOERR* hdf_set_attr (HDF *hdf, const char *name, const char *key, 
                      const char *value)
{
  HDF *obj;
  HDF_ATTR *attr, *last;

  _walk_hdf(hdf, name, &obj);
  if (obj == NULL) 
    return nerr_raise(NERR_ASSERT, "Unable to set attribute on none existant node");

  if (obj->attr != NULL)
  {
    attr = obj->attr;
    last = attr;
    while (attr != NULL)
    {
      if (!strcmp(attr->key, key))
      {
	if (attr->value) free(attr->value);
	/* a set of NULL deletes the attr */
	if (value == NULL)
	{
	  if (attr == obj->attr)
	    obj->attr = attr->next;
	  else
	    last->next = attr->next;
	  free(attr->key);
	  free(attr);
	  return STATUS_OK;
	}
	attr->value = strdup(value);
	if (attr->value == NULL)
	  return nerr_raise(NERR_NOMEM, "Unable to set attr %s to %s", key, value);
	return STATUS_OK;
      }
      last = attr;
      attr = attr->next;
    }
    last->next = (HDF_ATTR *) calloc(1, sizeof(HDF_ATTR));
    if (last->next == NULL)
      return nerr_raise(NERR_NOMEM, "Unable to set attr %s to %s", key, value);
    attr = last->next;
{'fraction_non_alphanumeric': 0.16563077439946244, 'fraction_numerical': 0.003919592362394311, 'mean_word_length': 2.5233773919905307, 'pattern_counts': {'":': 0, '<': 26, '<?xml version=': 0, '>': 208, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '177520', 'n_tokens_mistral': 7418, 'n_tokens_neox': 7232, 'n_words': 1979}