/*
 * Copyright (C) 2002-2014 Free Software Foundation, Inc.
 *
 * This file is part of LIBTASN1.
 *
 * The LIBTASN1 library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA
 */


/*****************************************************/
/* File: structure.c                                 */
/* Description: Functions to create and delete an    */
/*  ASN1 tree.                                       */
/*****************************************************/


#include <int.h>
#include <structure.h>
#include "parser_aux.h"
#include <gstr.h>


extern char _asn1_identifierMissing[];


/******************************************************/
/* Function : _asn1_add_single_node                     */
/* Description: creates a new NODE_ASN element.       */
/* Parameters:                                        */
/*   type: type of the new element (see ASN1_ETYPE_         */
/*         and CONST_ constants).                     */
/* Return: pointer to the new element.                */
/******************************************************/
asn1_node
_asn1_add_single_node (unsigned int type)
{
  asn1_node punt;

  punt = calloc (1, sizeof (struct asn1_node_st));
  if (punt == NULL)
    return NULL;

  punt->type = type;

  return punt;
}


/******************************************************************/
/* Function : _asn1_find_left                                     */
/* Description: returns the NODE_ASN element with RIGHT field that*/
/*              points the element NODE.                          */
/* Parameters:                                                    */
/*   node: NODE_ASN element pointer.                              */
/* Return: NULL if not found.                                     */
/******************************************************************/
asn1_node
_asn1_find_left (asn1_node_const node)
{
  if ((node == NULL) || (node->left == NULL) || (node->left->down == node))
    return NULL;

  return node->left;
}


int
_asn1_create_static_structure (asn1_node_const pointer, char *output_file_name,
			       char *vector_name)
{
  FILE *file;
  asn1_node_const p;
  unsigned long t;

  file = fopen (output_file_name, "w");

  if (file == NULL)
    return ASN1_FILE_NOT_FOUND;

  fprintf (file, "#if HAVE_CONFIG_H\n");
  fprintf (file, "# include \"config.h\"\n");
  fprintf (file, "#endif\n\n");

  fprintf (file, "#include <libtasn1.h>\n\n");

  fprintf (file, "const asn1_static_node %s[] = {\n", vector_name);

  p = pointer;

  while (p)
    {
      fprintf (file, "  { ");

      if (p->name[0] != 0)
	fprintf (file, "\"%s\", ", p->name);
      else
	fprintf (file, "NULL, ");

      t = p->type;
      if (p->down)
	t |= CONST_DOWN;
      if (p->right)
	t |= CONST_RIGHT;

      fprintf (file, "%lu, ", t);

      if (p->value)
	fprintf (file, "\"%s\"},\n", p->value);
      else
	fprintf (file, "NULL },\n");

      if (p->down)
	{
	  p = p->down;
	}
      else if (p->right)
	{
	  p = p->right;
	}
      else
	{
	  while (1)
	    {
	      p = _asn1_find_up (p);
	      if (p == pointer)
		{
		  p = NULL;
		  break;
		}
	      if (p->right)
		{
		  p = p->right;
		  break;
		}
	    }
	}
    }

  fprintf (file, "  { NULL, 0, NULL }\n};\n");

  fclose (file);

  return ASN1_SUCCESS;
}


/**
 * asn1_array2tree:
 * @array: specify the array that contains ASN.1 declarations
 * @definitions: return the pointer to the structure created by
 *   *ARRAY ASN.1 declarations
 * @errorDescription: return the error description.
 *
 * Creates the structures needed to manage the ASN.1 definitions.
 * @array is a vector created by asn1_parser2array().
 *
 * Returns: %ASN1_SUCCESS if structure was created correctly,
 *   %ASN1_ELEMENT_NOT_EMPTY if *@definitions not NULL,
 *   %ASN1_IDENTIFIER_NOT_FOUND if in the file there is an identifier
 *   that is not defined (see @errorDescription for more information),
 *   %ASN1_ARRAY_ERROR if the array pointed by @array is wrong.
 **/
int
asn1_array2tree (const asn1_static_node * array, asn1_node * definitions,
		 char *errorDescription)
{
  asn1_node p, p_last = NULL;
  unsigned long k;
  int move;
  int result;
  unsigned int type;
  list_type *e_list = NULL;

  if (errorDescription)
    errorDescription[0] = 0;

  if (*definitions != NULL)
    return ASN1_ELEMENT_NOT_EMPTY;

  move = UP;

  for (k = 0; array[k].value || array[k].type || array[k].name; k++)
    {
      type = convert_old_type (array[k].type);

      p = _asn1_add_static_node (&e_list, type & (~CONST_DOWN));
      if (array[k].name)
	_asn1_set_name (p, array[k].name);
      if (array[k].value)
	_asn1_set_value (p, array[k].value, strlen (array[k].value) + 1);

      if (*definitions == NULL)
	*definitions = p;

      if (move == DOWN)
	{
	  if (p_last && p_last->down)
	      _asn1_delete_structure (e_list, &p_last->down, 0);
	  _asn1_set_down (p_last, p);
	}
      else if (move == RIGHT)
        {
	  if (p_last && p_last->right)
	      _asn1_delete_structure (e_list, &p_last->right, 0);
	  _asn1_set_right (p_last, p);
        }

      p_last = p;

      if (type & CONST_DOWN)
	move = DOWN;
      else if (type & CONST_RIGHT)
	move = RIGHT;
      else
	{
	  while (p_last != *definitions)
	    {
	      p_last = _asn1_find_up (p_last);

	      if (p_last == NULL)
		break;

	      if (p_last->type & CONST_RIGHT)
		{
		  p_last->type &= ~CONST_RIGHT;
		  move = RIGHT;
		  break;
		}
	    }			/* while */
	}
    }				/* while */

  if (p_last == *definitions)
    {
      result = _asn1_check_identifier (*definitions);
      if (result == ASN1_SUCCESS)
	{
	  _asn1_change_integer_value (*definitions);
	  result = _asn1_expand_object_id (&e_list, *definitions);
	}
    }
  else
    {
      result = ASN1_ARRAY_ERROR;
    }

  if (errorDescription != NULL)
    {
      if (result == ASN1_IDENTIFIER_NOT_FOUND)
	{
	  Estrcpy (errorDescription, ":: identifier '");
	  Estrcat (errorDescription, _asn1_identifierMissing);
	  Estrcat (errorDescription, "' not found");
	}
      else
	errorDescription[0] = 0;
    }

  if (result != ASN1_SUCCESS)
    {
      _asn1_delete_list_and_nodes (e_list);
      *definitions = NULL;
    }
  else
    _asn1_delete_list (e_list);

  return result;
}

/**
 * asn1_delete_structure:
 * @structure: pointer to the structure that you want to delete.
 *
 * Deletes the structure *@structure.  At the end, *@structure is set
 * to NULL.
 *
 * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if
 *   *@structure was NULL.
 **/
int
asn1_delete_structure (asn1_node * structure)
{
  return _asn1_delete_structure (NULL, structure, 0);
}

/**
 * asn1_delete_structure2:
 * @structure: pointer to the structure that you want to delete.
 * @flags: additional flags (see %ASN1_DELETE_FLAG)
 *
 * Deletes the structure *@structure.  At the end, *@structure is set
 * to NULL.
 *
 * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if
 *   *@structure was NULL.
 **/
int
asn1_delete_structure2 (asn1_node * structure, unsigned int flags)
{
  return _asn1_delete_structure (NULL, structure, flags);
}

int
_asn1_delete_structure (list_type *e_list, asn1_node * structure, unsigned int flags)
{
  asn1_node p, p2, p3;

  if (*structure == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  p = *structure;
  while (p)
    {
      if (p->down)
	{
	  p = p->down;
	}
      else
	{			/* no down */
	  p2 = p->right;
	  if (p != *structure)
	    {
	      p3 = _asn1_find_up (p);
	      _asn1_set_down (p3, p2);
	      if (e_list)
		_asn1_delete_node_from_list (e_list, p);
	      _asn1_remove_node (p, flags);
	      p = p3;
	    }
	  else
	    {			/* p==root */
	      p3 = _asn1_find_left (p);
	      if (!p3)
		{
		  p3 = _asn1_find_up (p);
		  if (p3)
		    _asn1_set_down (p3, p2);
		  else
		    {
		      if (p->right)
			p->right->left = NULL;
		    }
		}
	      else
		_asn1_set_right (p3, p2);
	      if (e_list)
		_asn1_delete_node_from_list (e_list, p);
	      _asn1_remove_node (p, flags);
	      p = NULL;
	    }
	}
    }

  *structure = NULL;
  return ASN1_SUCCESS;
}


/**
 * asn1_delete_element:
 * @structure: pointer to the structure that contains the element you
 *   want to delete.
 * @element_name: element's name you want to delete.
 *
 * Deletes the element named *@element_name inside *@structure.
 *
 * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if
 *   the @element_name was not found.
 **/
int
asn1_delete_element (asn1_node structure, const char *element_name)
{
  asn1_node p2, p3, source_node;

  source_node = asn1_find_node (structure, element_name);

  if (source_node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  p2 = source_node->right;
  p3 = _asn1_find_left (source_node);
  if (!p3)
    {
      p3 = _asn1_find_up (source_node);
      if (p3)
	_asn1_set_down (p3, p2);
      else if (source_node->right)
	source_node->right->left = NULL;
    }
  else
    _asn1_set_right (p3, p2);

  return asn1_delete_structure (&source_node);
}

#ifndef __clang_analyzer__
asn1_node
_asn1_copy_structure3 (asn1_node_const source_node)
{
  asn1_node_const p_s;
  asn1_node dest_node, p_d, p_d_prev;
  int move;

  if (source_node == NULL)
    return NULL;

  dest_node = _asn1_add_single_node (source_node->type);

  p_s = source_node;
  p_d = dest_node;

  move = DOWN;

  do
    {
      if (move != UP)
	{
	  if (p_s->name[0] != 0)
	    _asn1_cpy_name (p_d, p_s);
	  if (p_s->value)
	    _asn1_set_value (p_d, p_s->value, p_s->value_len);
	  if (p_s->down)
	    {
	      p_s = p_s->down;
	      p_d_prev = p_d;
	      p_d = _asn1_add_single_node (p_s->type);
	      _asn1_set_down (p_d_prev, p_d);
	      continue;
	    }
	  p_d->start = p_s->start;
	  p_d->end = p_s->end;
	}

      if (p_s == source_node)
	break;

      if (p_s->right)
	{
	  move = RIGHT;
	  p_s = p_s->right;
	  p_d_prev = p_d;
	  p_d = _asn1_add_single_node (p_s->type);
	  _asn1_set_right (p_d_prev, p_d);
	}
      else
	{
	  move = UP;
	  p_s = _asn1_find_up (p_s);
	  p_d = _asn1_find_up (p_d);
	}
    }
  while (p_s != source_node);
  return dest_node;
}
#else

/* Non-production code */
asn1_node
_asn1_copy_structure3 (asn1_node_const source_node)
{
  return NULL;
}
#endif /* __clang_analyzer__ */


static asn1_node
_asn1_copy_structure2 (asn1_node_const root, const char *source_name)
{
  asn1_node source_node;

  source_node = asn1_find_node (root, source_name);

  return _asn1_copy_structure3 (source_node);

}


static int
_asn1_type_choice_config (asn1_node node)
{
  asn1_node p, p2, p3, p4;
  int move, tlen;

  if (node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  p = node;
  move = DOWN;

  while (!((p == node) && (move == UP)))
    {
      if (move != UP)
	{
	  if ((type_field (p->type) == ASN1_ETYPE_CHOICE)
	      && (p->type & CONST_TAG))
	    {
	      p2 = p->down;
	      while (p2)
		{
		  if (type_field (p2->type) != ASN1_ETYPE_TAG)
		    {
		      p2->type |= CONST_TAG;
		      p3 = _asn1_find_left (p2);
		      while (p3)
			{
			  if (type_field (p3->type) == ASN1_ETYPE_TAG)
			    {
			      p4 = _asn1_add_single_node (p3->type);
			      tlen = _asn1_strlen (p3->value);
			      if (tlen > 0)
				_asn1_set_value (p4, p3->value, tlen + 1);
			      _asn1_set_right (p4, p2->down);
			      _asn1_set_down (p2, p4);
			    }
			  p3 = _asn1_find_left (p3);
			}
		    }
		  p2 = p2->right;
		}
	      p->type &= ~(CONST_TAG);
	      p2 = p->down;
	      while (p2)
		{
		  p3 = p2->right;
		  if (type_field (p2->type) == ASN1_ETYPE_TAG)
		    asn1_delete_structure (&p2);
		  p2 = p3;
		}
	    }
	  move = DOWN;
	}
      else
	move = RIGHT;

      if (move == DOWN)
	{
	  if (p->down)
	    p = p->down;
	  else
	    move = RIGHT;
	}

      if (p == node)
	{
	  move = UP;
	  continue;
	}

      if (move == RIGHT)
	{
	  if (p->right)
	    p = p->right;
	  else
	    move = UP;
	}
      if (move == UP)
	p = _asn1_find_up (p);
    }

  return ASN1_SUCCESS;
}


static int
_asn1_expand_identifier (asn1_node * node, asn1_node_const root)
{
  asn1_node p, p2, p3;
  char name2[ASN1_MAX_NAME_SIZE + 2];
  int move;

  if (node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  p = *node;
  move = DOWN;

  while (!((p == *node) && (move == UP)))
    {
      if (move != UP)
	{
	  if (type_field (p->type) == ASN1_ETYPE_IDENTIFIER)
	    {
	      snprintf (name2, sizeof (name2), "%s.%s", root->name, p->value);
	      p2 = _asn1_copy_structure2 (root, name2);
	      if (p2 == NULL)
		{
		  return ASN1_IDENTIFIER_NOT_FOUND;
		}
	      _asn1_cpy_name (p2, p);
	      p2->right = p->right;
	      p2->left = p->left;
	      if (p->right)
		p->right->left = p2;
	      p3 = p->down;
	      if (p3)
		{
		  while (p3->right)
		    p3 = p3->right;
		  _asn1_set_right (p3, p2->down);
		  _asn1_set_down (p2, p->down);
		}

	      p3 = _asn1_find_left (p);
	      if (p3)
		_asn1_set_right (p3, p2);
	      else
		{
		  p3 = _asn1_find_up (p);
		  if (p3)
		    _asn1_set_down (p3, p2);
		  else
		    {
		      p2->left = NULL;
		    }
		}

	      if (p->type & CONST_SIZE)
		p2->type |= CONST_SIZE;
	      if (p->type & CONST_TAG)
		p2->type |= CONST_TAG;
	      if (p->type & CONST_OPTION)
		p2->type |= CONST_OPTION;
	      if (p->type & CONST_DEFAULT)
		p2->type |= CONST_DEFAULT;
	      if (p->type & CONST_SET)
		p2->type |= CONST_SET;
	      if (p->type & CONST_NOT_USED)
		p2->type |= CONST_NOT_USED;

	      if (p == *node)
		*node = p2;
	      _asn1_remove_node (p, 0);
	      p = p2;
	      move = DOWN;
	      continue;
	    }
	  move = DOWN;
	}
      else
	move = RIGHT;

      if (move == DOWN)
	{
	  if (p->down)
	    p = p->down;
	  else
	    move = RIGHT;
	}

      if (p == *node)
	{
	  move = UP;
	  continue;
	}

      if (move == RIGHT)
	{
	  if (p->right)
	    p = p->right;
	  else
	    move = UP;
	}
      if (move == UP)
	p = _asn1_find_up (p);
    }

  return ASN1_SUCCESS;
}


/**
 * asn1_create_element:
 * @definitions: pointer to the structure returned by "parser_asn1" function
 * @source_name: the name of the type of the new structure (must be
 *   inside p_structure).
 * @element: pointer to the structure created.
 *
 * Creates a structure of type @source_name.  Example using
 *  "pkix.asn":
 *
 * rc = asn1_create_element(cert_def, "PKIX1.Certificate", certptr);
 *
 * Returns: %ASN1_SUCCESS if creation OK, %ASN1_ELEMENT_NOT_FOUND if
 *   @source_name is not known.
 **/
int
asn1_create_element (asn1_node_const definitions, const char *source_name,
		     asn1_node * element)
{
  asn1_node dest_node;
  int res;

  dest_node = _asn1_copy_structure2 (definitions, source_name);

  if (dest_node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  _asn1_set_name (dest_node, "");

  res = _asn1_expand_identifier (&dest_node, definitions);
  _asn1_type_choice_config (dest_node);

  *element = dest_node;

  return res;
}


/**
 * asn1_print_structure:
 * @out: pointer to the output file (e.g. stdout).
 * @structure: pointer to the structure that you want to visit.
 * @name: an element of the structure
 * @mode: specify how much of the structure to print, can be
 *   %ASN1_PRINT_NAME, %ASN1_PRINT_NAME_TYPE,
 *   %ASN1_PRINT_NAME_TYPE_VALUE, or %ASN1_PRINT_ALL.
 *
 * Prints on the @out file descriptor the structure's tree starting
 * from the @name element inside the structure @structure.
 **/
void
asn1_print_structure (FILE * out, asn1_node_const structure, const char *name,
		      int mode)
{
  asn1_node_const p, root;
  int k, indent = 0, len, len2, len3;

  if (out == NULL)
    return;

  root = asn1_find_node (structure, name);

  if (root == NULL)
    return;

  p = root;
  while (p)
    {
      if (mode == ASN1_PRINT_ALL)
	{
	  for (k = 0; k < indent; k++)
	    fprintf (out, " ");
	  fprintf (out, "name:");
	  if (p->name[0] != 0)
	    fprintf (out, "%s  ", p->name);
	  else
	    fprintf (out, "NULL  ");
	}
      else
	{
	  switch (type_field (p->type))
	    {
	    case ASN1_ETYPE_CONSTANT:
	    case ASN1_ETYPE_TAG:
	    case ASN1_ETYPE_SIZE:
	      break;
	    default:
	      for (k = 0; k < indent; k++)
		fprintf (out, " ");
	      fprintf (out, "name:");
	      if (p->name[0] != 0)
		fprintf (out, "%s  ", p->name);
	      else
		fprintf (out, "NULL  ");
	    }
	}

      if (mode != ASN1_PRINT_NAME)
	{
	  unsigned type = type_field (p->type);
	  switch (type)
	    {
	    case ASN1_ETYPE_CONSTANT:
	      if (mode == ASN1_PRINT_ALL)
		fprintf (out, "type:CONST");
	      break;
	    case ASN1_ETYPE_TAG:
	      if (mode == ASN1_PRINT_ALL)
		fprintf (out, "type:TAG");
	      break;
	    case ASN1_ETYPE_SIZE:
	      if (mode == ASN1_PRINT_ALL)
		fprintf (out, "type:SIZE");
	      break;
	    case ASN1_ETYPE_DEFAULT:
	      fprintf (out, "type:DEFAULT");
	      break;
	    case ASN1_ETYPE_IDENTIFIER:
	      fprintf (out, "type:IDENTIFIER");
	      break;
	    case ASN1_ETYPE_ANY:
	      fprintf (out, "type:ANY");
	      break;
	    case ASN1_ETYPE_CHOICE:
	      fprintf (out, "type:CHOICE");
	      break;
	    case ASN1_ETYPE_DEFINITIONS:
	      fprintf (out, "type:DEFINITIONS");
	      break;
	    CASE_HANDLED_ETYPES:
	      fprintf (out, "%s", _asn1_tags[type].desc);
	      break;
	    default:
	      break;
	    }
	}

      if ((mode == ASN1_PRINT_NAME_TYPE_VALUE) || (mode == ASN1_PRINT_ALL))
	{
	  switch (type_field (p->type))
	    {
	    case ASN1_ETYPE_CONSTANT:
	      if (mode == ASN1_PRINT_ALL)
		if (p->value)
		  fprintf (out, "  value:%s", p->value);
	      break;
	    case ASN1_ETYPE_TAG:
	      if (mode == ASN1_PRINT_ALL)
		if (p->value)
		  fprintf (out, "  value:%s", p->value);
	      break;
	    case ASN1_ETYPE_SIZE:
	      if (mode == ASN1_PRINT_ALL)
		if (p->value)
		  fprintf (out, "  value:%s", p->value);
	      break;
	    case ASN1_ETYPE_DEFAULT:
	      if (p->value)
		fprintf (out, "  value:%s", p->value);
	      else if (p->type & CONST_TRUE)
		fprintf (out, "  value:TRUE");
	      else if (p->type & CONST_FALSE)
		fprintf (out, "  value:FALSE");
	      break;
	    case ASN1_ETYPE_IDENTIFIER:
	      if (p->value)
		fprintf (out, "  value:%s", p->value);
	      break;
	    case ASN1_ETYPE_INTEGER:
	      if (p->value)
		{
		  len2 = -1;
		  len = asn1_get_length_der (p->value, p->value_len, &len2);
		  fprintf (out, "  value:0x");
		  if (len > 0)
		    for (k = 0; k < len; k++)
		      fprintf (out, "%02x", (unsigned) (p->value)[k + len2]);
		}
	      break;
	    case ASN1_ETYPE_ENUMERATED:
	      if (p->value)
		{
		  len2 = -1;
		  len = asn1_get_length_der (p->value, p->value_len, &len2);
		  fprintf (out, "  value:0x");
		  if (len > 0)
		    for (k = 0; k < len; k++)
		      fprintf (out, "%02x", (unsigned) (p->value)[k + len2]);
		}
	      break;
	    case ASN1_ETYPE_BOOLEAN:
	      if (p->value)
		{
		  if (p->value[0] == 'T')
		    fprintf (out, "  value:TRUE");
		  else if (p->value[0] == 'F')
		    fprintf (out, "  value:FALSE");
		}
	      break;
	    case ASN1_ETYPE_BIT_STRING:
	      if (p->value)
		{
		  len2 = -1;
		  len = asn1_get_length_der (p->value, p->value_len, &len2);
		  if (len > 0)
		    {
		      fprintf (out, "  value(%i):",
			       (len - 1) * 8 - (p->value[len2]));
		      for (k = 1; k < len; k++)
			fprintf (out, "%02x", (unsigned) (p->value)[k + len2]);
		    }
		}
	      break;
	    case ASN1_ETYPE_GENERALIZED_TIME:
	    case ASN1_ETYPE_UTC_TIME:
	      if (p->value)
		{
		  fprintf (out, "  value:");
		  for (k = 0; k < p->value_len; k++)
		    fprintf (out, "%c", (p->value)[k]);
		}
	      break;
	    case ASN1_ETYPE_GENERALSTRING:
	    case ASN1_ETYPE_NUMERIC_STRING:
	    case ASN1_ETYPE_IA5_STRING:
	    case ASN1_ETYPE_TELETEX_STRING:
	    case ASN1_ETYPE_PRINTABLE_STRING:
	    case ASN1_ETYPE_UNIVERSAL_STRING:
	    case ASN1_ETYPE_UTF8_STRING:
	    case ASN1_ETYPE_VISIBLE_STRING:
	      if (p->value)
		{
		  len2 = -1;
		  len = asn1_get_length_der (p->value, p->value_len, &len2);
		  fprintf (out, "  value:");
		  if (len > 0)
		    for (k = 0; k < len; k++)
		      fprintf (out, "%c", (p->value)[k + len2]);
		}
	      break;
	    case ASN1_ETYPE_BMP_STRING:
	    case ASN1_ETYPE_OCTET_STRING:
	      if (p->value)
		{
		  len2 = -1;
		  len = asn1_get_length_der (p->value, p->value_len, &len2);
		  fprintf (out, "  value:");
		  if (len > 0)
		    for (k = 0; k < len; k++)
		      fprintf (out, "%02x", (unsigned) (p->value)[k + len2]);
		}
	      break;
	    case ASN1_ETYPE_OBJECT_ID:
	      if (p->value)
		fprintf (out, "  value:%s", p->value);
	      break;
	    case ASN1_ETYPE_ANY:
	      if (p->value)
		{
		  len3 = -1;
		  len2 = asn1_get_length_der (p->value, p->value_len, &len3);
		  fprintf (out, "  value:");
		  if (len2 > 0)
		    for (k = 0; k < len2; k++)
		      fprintf (out, "%02x", (unsigned) (p->value)[k + len3]);
		}
	      break;
	    case ASN1_ETYPE_SET:
	    case ASN1_ETYPE_SET_OF:
	    case ASN1_ETYPE_CHOICE:
	    case ASN1_ETYPE_DEFINITIONS:
	    case ASN1_ETYPE_SEQUENCE_OF:
	    case ASN1_ETYPE_SEQUENCE:
	    case ASN1_ETYPE_NULL:
	      break;
	    default:
	      break;
	    }
	}

      if (mode == ASN1_PRINT_ALL)
	{
	  if (p->type & 0x1FFFFF00)
	    {
	      fprintf (out, "  attr:");
	      if (p->type & CONST_UNIVERSAL)
		fprintf (out, "UNIVERSAL,");
	      if (p->type & CONST_PRIVATE)
		fprintf (out, "PRIVATE,");
	      if (p->type & CONST_APPLICATION)
		fprintf (out, "APPLICATION,");
	      if (p->type & CONST_EXPLICIT)
		fprintf (out, "EXPLICIT,");
	      if (p->type & CONST_IMPLICIT)
		fprintf (out, "IMPLICIT,");
	      if (p->type & CONST_TAG)
		fprintf (out, "TAG,");
	      if (p->type & CONST_DEFAULT)
		fprintf (out, "DEFAULT,");
	      if (p->type & CONST_TRUE)
		fprintf (out, "TRUE,");
	      if (p->type & CONST_FALSE)
		fprintf (out, "FALSE,");
	      if (p->type & CONST_LIST)
		fprintf (out, "LIST,");
	      if (p->type & CONST_MIN_MAX)
		fprintf (out, "MIN_MAX,");
	      if (p->type & CONST_OPTION)
		fprintf (out, "OPTION,");
	      if (p->type & CONST_1_PARAM)
		fprintf (out, "1_PARAM,");
	      if (p->type & CONST_SIZE)
		fprintf (out, "SIZE,");
	      if (p->type & CONST_DEFINED_BY)
		fprintf (out, "DEF_BY,");
	      if (p->type & CONST_GENERALIZED)
		fprintf (out, "GENERALIZED,");
	      if (p->type & CONST_UTC)
		fprintf (out, "UTC,");
	      if (p->type & CONST_SET)
		fprintf (out, "SET,");
	      if (p->type & CONST_NOT_USED)
		fprintf (out, "NOT_USED,");
	      if (p->type & CONST_ASSIGN)
		fprintf (out, "ASSIGNMENT,");
	    }
	}

      if (mode == ASN1_PRINT_ALL)
	{
	  fprintf (out, "\n");
	}
      else
	{
	  switch (type_field (p->type))
	    {
	    case ASN1_ETYPE_CONSTANT:
	    case ASN1_ETYPE_TAG:
	    case ASN1_ETYPE_SIZE:
	      break;
	    default:
	      fprintf (out, "\n");
	    }
	}

      if (p->down)
	{
	  p = p->down;
	  indent += 2;
	}
      else if (p == root)
	{
	  p = NULL;
	  break;
	}
      else if (p->right)
	p = p->right;
      else
	{
	  while (1)
	    {
	      p = _asn1_find_up (p);
	      if (p == root)
		{
		  p = NULL;
		  break;
		}
	      indent -= 2;
	      if (p->right)
		{
		  p = p->right;
		  break;
		}
	    }
	}
    }
}



/**
 * asn1_number_of_elements:
 * @element: pointer to the root of an ASN1 structure.
 * @name: the name of a sub-structure of ROOT.
 * @num: pointer to an integer where the result will be stored
 *
 * Counts the number of elements of a sub-structure called NAME with
 * names equal to "?1","?2", ...
 *
 * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if
 *   @name is not known, %ASN1_GENERIC_ERROR if pointer @num is %NULL.
 **/
int
asn1_number_of_elements (asn1_node_const element, const char *name, int *num)
{
  asn1_node_const node, p;

  if (num == NULL)
    return ASN1_GENERIC_ERROR;

  *num = 0;

  node = asn1_find_node (element, name);
  if (node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  p = node->down;

  while (p)
    {
      if (p->name[0] == '?')
	(*num)++;
      p = p->right;
    }

  return ASN1_SUCCESS;
}


/**
 * asn1_find_structure_from_oid:
 * @definitions: ASN1 definitions
 * @oidValue: value of the OID to search (e.g. "1.2.3.4").
 *
 * Search the structure that is defined just after an OID definition.
 *
 * Returns: %NULL when @oidValue not found, otherwise the pointer to a
 *   constant string that contains the element name defined just after
 *   the OID.
 **/
const char *
asn1_find_structure_from_oid (asn1_node_const definitions, const char *oidValue)
{
  char name[2 * ASN1_MAX_NAME_SIZE + 2];
  char value[ASN1_MAX_NAME_SIZE];
  asn1_node p;
  int len;
  int result;
  const char *definitionsName;

  if ((definitions == NULL) || (oidValue == NULL))
    return NULL;		/* ASN1_ELEMENT_NOT_FOUND; */

  definitionsName = definitions->name;

  /* search the OBJECT_ID into definitions */
  p = definitions->down;
  while (p)
    {
      if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) &&
	  (p->type & CONST_ASSIGN))
	{
          snprintf(name, sizeof(name), "%s.%s", definitionsName, p->name);

	  len = ASN1_MAX_NAME_SIZE;
	  result = asn1_read_value (definitions, name, value, &len);

	  if ((result == ASN1_SUCCESS) && (!strcmp (oidValue, value)))
	    {
	      p = p->right;
	      if (p == NULL)	/* reach the end of ASN1 definitions */
		return NULL;	/* ASN1_ELEMENT_NOT_FOUND; */

	      return p->name;
	    }
	}
      p = p->right;
    }

  return NULL;			/* ASN1_ELEMENT_NOT_FOUND; */
}

/**
 * asn1_copy_node:
 * @dst: Destination asn1 node.
 * @dst_name: Field name in destination node.
 * @src: Source asn1 node.
 * @src_name: Field name in source node.
 *
 * Create a deep copy of a asn1_node variable. That
 * function requires @dst to be expanded using asn1_create_element().
 *
 * Returns: Return %ASN1_SUCCESS on success.
 **/
int
asn1_copy_node (asn1_node dst, const char *dst_name,
		asn1_node_const src, const char *src_name)
{
  int result;
  asn1_node dst_node;
  void *data = NULL;
  int size = 0;

  result = asn1_der_coding (src, src_name, NULL, &size, NULL);
  if (result != ASN1_MEM_ERROR)
    return result;

  data = malloc (size);
  if (data == NULL)
    return ASN1_MEM_ERROR;

  result = asn1_der_coding (src, src_name, data, &size, NULL);
  if (result != ASN1_SUCCESS)
    {
      free (data);
      return result;
    }

  dst_node = asn1_find_node (dst, dst_name);
  if (dst_node == NULL)
    {
      free (data);
      return ASN1_ELEMENT_NOT_FOUND;
    }

  result = asn1_der_decoding (&dst_node, data, size, NULL);

  free (data);

  return result;
}

/**
 * asn1_dup_node:
 * @src: Source asn1 node.
 * @src_name: Field name in source node.
 *
 * Create a deep copy of a asn1_node variable. This function
 * will return an exact copy of the provided structure.
 *
 * Returns: Return %NULL on failure.
 **/
asn1_node
asn1_dup_node (asn1_node_const src, const char *src_name)
{
  return _asn1_copy_structure2(src, src_name);
}
