/* Copyright 2010-2023 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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, see . */
#include
#include
#include
#include "obstack.h"
#include "errors.h"
#include "tree.h"
#include "source_marks.h"
/* for debug */
#include "parser.h"
static struct obstack obs_element;
static int *obs_element_first = 0;
/* Used with destroy_element to reuse storage, e.g. from
abort_empty_line. Reduces memory use slightly (about 5% from testing)
for large manuals. */
static ELEMENT *spare_element;
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
void
reset_obstacks (void)
{
spare_element = 0;
if (obs_element_first)
obstack_free (&obs_element, obs_element_first);
else
obstack_init (&obs_element);
obs_element_first = obstack_alloc (&obs_element, sizeof (int));
}
static ELEMENT *alloc_element (void)
{
ELEMENT *e;
e = (ELEMENT *) obstack_alloc (&obs_element, sizeof (ELEMENT));
memset (e, 0, sizeof (ELEMENT));
return e;
}
ELEMENT *
new_element (enum element_type type)
{
ELEMENT *e;
if (spare_element)
{
e = spare_element;
spare_element = 0;
memset (e, 0, sizeof (ELEMENT));
}
else
{
e = alloc_element ();
/* alloc_element zeroes *e. We assume null pointers have bit
representation of all zeroes. */
}
e->type = type;
return e;
}
void
destroy_associated_info (ASSOCIATED_INFO *a)
{
int i;
for (i = 0; i < a->info_number; i++)
{
switch (a->info[i].type)
{
case extra_string:
free ((char *) a->info[i].value);
break;
case extra_element_oot:
destroy_element_and_children ((ELEMENT *) a->info[i].value);
break;
case extra_contents:
if (a->info[i].value)
destroy_element ((ELEMENT *) a->info[i].value);
break;
case extra_misc_args:
destroy_element_and_children ((ELEMENT *) a->info[i].value);
break;
default:
break;
}
}
free (a->info);
}
void
destroy_source_mark (SOURCE_MARK *source_mark)
{
if (source_mark->element)
destroy_element_and_children (source_mark->element);
if (source_mark->line)
free (source_mark->line);
free (source_mark);
}
void
destroy_source_mark_list (SOURCE_MARK_LIST *source_mark_list)
{
int i;
for (i = 0; i < source_mark_list->number; i++)
destroy_source_mark (source_mark_list->list[i]);
source_mark_list->number = 0;
free (source_mark_list->list);
source_mark_list->space = 0;
}
void
destroy_element (ELEMENT *e)
{
free (e->text.text);
/* Note the pointers in these lists are not themselves freed. */
free (e->contents.list);
free (e->args.list);
destroy_source_mark_list (&(e->source_mark_list));
destroy_associated_info (&e->extra_info);
destroy_associated_info (&e->info_info);
spare_element = e;
/* freed in reset_obstacks */
/* free (e); */
}
/* Recursively destroy this element and all data in its descendants. */
void
destroy_element_and_children (ELEMENT *e)
{
int i;
for (i = 0; i < e->contents.number; i++)
destroy_element_and_children (e->contents.list[i]);
for (i = 0; i < e->args.number; i++)
destroy_element_and_children (e->args.list[i]);
destroy_element (e);
}
/* Make sure there is space for at least one more element. */
static void
reallocate_list (ELEMENT_LIST *list)
{
if (list->number + 1 >= list->space)
{
list->space += 10;
list->list = realloc (list->list, list->space * sizeof (ELEMENT *));
if (!list->list)
fatal ("realloc failed");
}
}
/* Make sure there is space for at least N more elements. */
static void
reallocate_list_for (int n, ELEMENT_LIST *list)
{
if (list->number + n >= list->space)
{
list->space += n + 1;
list->list = realloc (list->list, list->space * sizeof (ELEMENT *));
if (!list->list)
fatal ("realloc failed");
}
}
void
add_to_element_contents (ELEMENT *parent, ELEMENT *e)
{
ELEMENT_LIST *list = &parent->contents;
reallocate_list (list);
list->list[list->number++] = e;
e->parent = parent;
}
/* Special purpose function for when we are only using PARENT as an
array, and we don't want to overwrite E->parent. */
void
add_to_contents_as_array (ELEMENT *parent, ELEMENT *e)
{
ELEMENT_LIST *list = &parent->contents;
reallocate_list (list);
list->list[list->number++] = e;
}
void
add_to_element_args (ELEMENT *parent, ELEMENT *e)
{
ELEMENT_LIST *list = &parent->args;
reallocate_list (list);
list->list[list->number++] = e;
e->parent = parent;
}
/* Add the element E into the contents of PARENT at index WHERE. */
void
insert_into_contents (ELEMENT *parent, ELEMENT *e, int where)
{
ELEMENT_LIST *list = &parent->contents;
reallocate_list (list);
if (where < 0)
where = list->number + where;
if (where < 0 || where > list->number)
fatal ("contents index out of bounds");
memmove (&list->list[where + 1], &list->list[where],
(list->number - where) * sizeof (ELEMENT *));
list->list[where] = e;
e->parent = parent;
list->number++;
}
/* Add the element E into the arguments of PARENT at index WHERE. */
void
insert_into_args (ELEMENT *parent, ELEMENT *e, int where)
{
ELEMENT_LIST *list = &parent->args;
reallocate_list (list);
if (where < 0)
where = list->number + where;
if (where < 0 || where > list->number)
fatal ("arguments index out of bounds");
memmove (&list->list[where + 1], &list->list[where],
(list->number - where) * sizeof (ELEMENT *));
list->list[where] = e;
e->parent = parent;
list->number++;
}
/* Insert elements to the contents of TO at position WHERE from FROM
from START inclusive to END exclusive. Do not set the parent fields. */
void
insert_slice_into_contents (ELEMENT *to, int where, ELEMENT *from,
int start, int end)
{
int num = end - start;
reallocate_list_for (num, &to->contents);
memmove (&to->contents.list[where + num],
&to->contents.list[where],
(to->contents.number - where) * sizeof (ELEMENT *));
memmove (&to->contents.list[where],
&from->contents.list[start],
num * sizeof (ELEMENT *));
to->contents.number += num;
}
ELEMENT *
remove_from_contents (ELEMENT *parent, int where)
{
ELEMENT_LIST *list = &parent->contents;
ELEMENT *removed;
if (where < 0)
where = list->number + where;
if (where < 0 || where > list->number)
fatal ("contents index out of bounds");
removed = list->list[where];
memmove (&list->list[where], &list->list[where + 1],
(list->number - (where+1)) * sizeof (ELEMENT *));
list->number--;
return removed;
}
/* Remove elements from START inclusive to END exclusive. Do not
free any of them. */
void
remove_slice_from_contents (ELEMENT *parent, int start, int end)
{
memmove (&parent->contents.list[start],
&parent->contents.list[end],
(parent->contents.number - end) * sizeof (ELEMENT *));
parent->contents.number -= (end - start);
}
ELEMENT *
pop_element_from_args (ELEMENT *parent)
{
ELEMENT_LIST *list = &parent->args;
return list->list[--list->number];
}
ELEMENT *
pop_element_from_contents (ELEMENT *parent)
{
ELEMENT_LIST *list = &parent->contents;
ELEMENT *popped_element = list->list[list->number -1];
list->number--;
return popped_element;
}
ELEMENT *
last_args_child (ELEMENT *current)
{
if (current->args.number == 0)
return 0;
return current->args.list[current->args.number - 1];
}
ELEMENT *
last_contents_child (ELEMENT *current)
{
if (current->contents.number == 0)
return 0;
return current->contents.list[current->contents.number - 1];
}
ELEMENT *
contents_child_by_index (ELEMENT *e, int index)
{
if (index < 0)
index = e->contents.number + index;
if (index < 0 || index >= e->contents.number)
return 0;
return e->contents.list[index];
}
ELEMENT *
args_child_by_index (ELEMENT *e, int index)
{
if (index < 0)
index = e->args.number + index;
if (index < 0 || index >= e->args.number)
return 0;
return e->args.list[index];
}
/* should only be used if the nse->manual_content
and nse->node_content are not already in the tree,
in practice when the node spec was created by
parse_node_manual (., 0); */
void
destroy_node_spec (NODE_SPEC_EXTRA *nse)
{
if (nse->out_of_tree_elements)
{
int i;
for (i = 0; i < 3; i++)
if (nse->out_of_tree_elements[i])
destroy_element (nse->out_of_tree_elements[i]);
free (nse->out_of_tree_elements);
}
if (nse->manual_content)
destroy_element (nse->manual_content);
if (nse->node_content)
destroy_element (nse->node_content);
free (nse);
}