| /* The common simulator framework for GDB, the GNU Debugger. |
| |
| Copyright 2002-2021 Free Software Foundation, Inc. |
| |
| Contributed by Andrew Cagney and Red Hat. |
| |
| This file is part of GDB. |
| |
| 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 <http://www.gnu.org/licenses/>. */ |
| |
| /* This must come before any other includes. */ |
| #include "defs.h" |
| |
| #include "hw-main.h" |
| #include "hw-base.h" |
| |
| #include "sim-io.h" |
| #include "sim-assert.h" |
| |
| #include <string.h> |
| |
| /* property entries */ |
| |
| struct hw_property_data |
| { |
| struct hw_property_data *next; |
| struct hw_property *property; |
| const void *init_array; |
| unsigned sizeof_init_array; |
| }; |
| |
| void |
| create_hw_property_data (struct hw *me) |
| { |
| } |
| |
| void |
| delete_hw_property_data (struct hw *me) |
| { |
| } |
| |
| |
| /* Device Properties: */ |
| |
| static struct hw_property_data * |
| find_property_data (struct hw *me, |
| const char *property) |
| { |
| struct hw_property_data *entry; |
| ASSERT (property != NULL); |
| entry = me->properties_of_hw; |
| while (entry != NULL) |
| { |
| if (strcmp (entry->property->name, property) == 0) |
| return entry; |
| entry = entry->next; |
| } |
| return NULL; |
| } |
| |
| |
| static void |
| hw_add_property (struct hw *me, |
| const char *property, |
| hw_property_type type, |
| const void *init_array, |
| unsigned sizeof_init_array, |
| const void *array, |
| unsigned sizeof_array, |
| const struct hw_property *original, |
| object_disposition disposition) |
| { |
| struct hw_property_data *new_entry = NULL; |
| struct hw_property *new_value = NULL; |
| |
| /* find the list end */ |
| struct hw_property_data **insertion_point = &me->properties_of_hw; |
| while (*insertion_point != NULL) |
| { |
| if (strcmp ((*insertion_point)->property->name, property) == 0) |
| return; |
| insertion_point = &(*insertion_point)->next; |
| } |
| |
| /* create a new value */ |
| new_value = HW_ZALLOC (me, struct hw_property); |
| new_value->name = (char *) strdup (property); |
| new_value->type = type; |
| if (sizeof_array > 0) |
| { |
| void *new_array = hw_zalloc (me, sizeof_array); |
| memcpy (new_array, array, sizeof_array); |
| new_value->array = new_array; |
| new_value->sizeof_array = sizeof_array; |
| } |
| new_value->owner = me; |
| new_value->original = original; |
| new_value->disposition = disposition; |
| |
| /* insert the value into the list */ |
| new_entry = HW_ZALLOC (me, struct hw_property_data); |
| *insertion_point = new_entry; |
| if (sizeof_init_array > 0) |
| { |
| void *new_init_array = hw_zalloc (me, sizeof_init_array); |
| memcpy (new_init_array, init_array, sizeof_init_array); |
| new_entry->init_array = new_init_array; |
| new_entry->sizeof_init_array = sizeof_init_array; |
| } |
| new_entry->property = new_value; |
| } |
| |
| |
| static void |
| hw_set_property (struct hw *me, |
| const char *property, |
| hw_property_type type, |
| const void *array, |
| int sizeof_array) |
| { |
| /* find the property */ |
| struct hw_property_data *entry = find_property_data (me, property); |
| if (entry != NULL) |
| { |
| /* existing property - update it */ |
| void *new_array = 0; |
| struct hw_property *value = entry->property; |
| /* check the type matches */ |
| if (value->type != type) |
| hw_abort (me, "conflict between type of new and old value for property %s", property); |
| /* replace its value */ |
| if (value->array != NULL) |
| hw_free (me, (void*)value->array); |
| new_array = (sizeof_array > 0 |
| ? hw_zalloc (me, sizeof_array) |
| : (void*)0); |
| value->array = new_array; |
| value->sizeof_array = sizeof_array; |
| if (sizeof_array > 0) |
| memcpy (new_array, array, sizeof_array); |
| return; |
| } |
| else |
| { |
| /* new property - create it */ |
| hw_add_property (me, property, type, |
| NULL, 0, array, sizeof_array, |
| NULL, temporary_object); |
| } |
| } |
| |
| |
| #if 0 |
| static void |
| clean_hw_properties (struct hw *me) |
| { |
| struct hw_property_data **delete_point = &me->properties_of_hw; |
| while (*delete_point != NULL) |
| { |
| struct hw_property_data *current = *delete_point; |
| switch (current->property->disposition) |
| { |
| case permenant_object: |
| /* zap the current value, will be initialized later */ |
| ASSERT (current->init_array != NULL); |
| if (current->property->array != NULL) |
| { |
| hw_free (me, (void*)current->property->array); |
| current->property->array = NULL; |
| } |
| delete_point = &(*delete_point)->next; |
| break; |
| case temporary_object: |
| /* zap the actual property, was created during simulation run */ |
| ASSERT (current->init_array == NULL); |
| *delete_point = current->next; |
| if (current->property->array != NULL) |
| hw_free (me, (void*)current->property->array); |
| hw_free (me, current->property); |
| hw_free (me, current); |
| break; |
| } |
| } |
| } |
| #endif |
| |
| #if 0 |
| void |
| hw_init_static_properties (SIM_DESC sd, |
| struct hw *me, |
| void *data) |
| { |
| struct hw_property_data *property; |
| for (property = me->properties_of_hw; |
| property != NULL; |
| property = property->next) |
| { |
| ASSERT (property->init_array != NULL); |
| ASSERT (property->property->array == NULL); |
| ASSERT (property->property->disposition == permenant_object); |
| switch (property->property->type) |
| { |
| case array_property: |
| case boolean_property: |
| case range_array_property: |
| case reg_array_property: |
| case string_property: |
| case string_array_property: |
| case integer_property: |
| /* delete the property, and replace it with the original */ |
| hw_set_property (me, property->property->name, |
| property->property->type, |
| property->init_array, |
| property->sizeof_init_array); |
| break; |
| #if 0 |
| case ihandle_property: |
| break; |
| #endif |
| } |
| } |
| } |
| #endif |
| |
| |
| #if 0 |
| void |
| hw_init_runtime_properties (SIM_DESC sd, |
| struct hw *me, |
| void *data) |
| { |
| struct hw_property_data *property; |
| for (property = me->properties_of_hw; |
| property != NULL; |
| property = property->next) |
| { |
| switch (property->property->disposition) |
| { |
| case permenant_object: |
| switch (property->property->type) |
| { |
| #if 0 |
| case ihandle_property: |
| { |
| struct hw_instance *ihandle; |
| ihandle_runtime_property_spec spec; |
| ASSERT (property->init_array != NULL); |
| ASSERT (property->property->array == NULL); |
| hw_find_ihandle_runtime_property (me, property->property->name, &spec); |
| ihandle = tree_instance (me, spec.full_path); |
| hw_set_ihandle_property (me, property->property->name, ihandle); |
| break; |
| } |
| #endif |
| case array_property: |
| case boolean_property: |
| case range_array_property: |
| case integer_property: |
| case reg_array_property: |
| case string_property: |
| case string_array_property: |
| ASSERT (property->init_array != NULL); |
| ASSERT (property->property->array != NULL); |
| break; |
| } |
| break; |
| case temporary_object: |
| ASSERT (property->init_array == NULL); |
| ASSERT (property->property->array != NULL); |
| break; |
| } |
| } |
| } |
| #endif |
| |
| |
| |
| const struct hw_property * |
| hw_next_property (const struct hw_property *property) |
| { |
| /* find the property in the list */ |
| struct hw *owner = property->owner; |
| struct hw_property_data *entry = owner->properties_of_hw; |
| while (entry != NULL && entry->property != property) |
| entry = entry->next; |
| /* now return the following property */ |
| ASSERT (entry != NULL); /* must be a member! */ |
| if (entry->next != NULL) |
| return entry->next->property; |
| else |
| return NULL; |
| } |
| |
| |
| const struct hw_property * |
| hw_find_property (struct hw *me, |
| const char *property) |
| { |
| if (me == NULL) |
| { |
| return NULL; |
| } |
| else if (property == NULL || strcmp (property, "") == 0) |
| { |
| if (me->properties_of_hw == NULL) |
| return NULL; |
| else |
| return me->properties_of_hw->property; |
| } |
| else |
| { |
| struct hw_property_data *entry = find_property_data (me, property); |
| if (entry != NULL) |
| return entry->property; |
| } |
| return NULL; |
| } |
| |
| |
| void |
| hw_add_array_property (struct hw *me, |
| const char *property, |
| const void *array, |
| int sizeof_array) |
| { |
| hw_add_property (me, property, array_property, |
| array, sizeof_array, array, sizeof_array, |
| NULL, permenant_object); |
| } |
| |
| void |
| hw_set_array_property (struct hw *me, |
| const char *property, |
| const void *array, |
| int sizeof_array) |
| { |
| hw_set_property (me, property, array_property, array, sizeof_array); |
| } |
| |
| const struct hw_property * |
| hw_find_array_property (struct hw *me, |
| const char *property) |
| { |
| const struct hw_property *node; |
| node = hw_find_property (me, property); |
| if (node == NULL) |
| hw_abort (me, "property \"%s\" not found", property); |
| if (node->type != array_property) |
| hw_abort (me, "property \"%s\" of wrong type (array)", property); |
| return node; |
| } |
| |
| |
| |
| void |
| hw_add_boolean_property (struct hw *me, |
| const char *property, |
| int boolean) |
| { |
| signed32 new_boolean = (boolean ? -1 : 0); |
| hw_add_property (me, property, boolean_property, |
| &new_boolean, sizeof (new_boolean), |
| &new_boolean, sizeof (new_boolean), |
| NULL, permenant_object); |
| } |
| |
| int |
| hw_find_boolean_property (struct hw *me, |
| const char *property) |
| { |
| const struct hw_property *node; |
| unsigned_cell boolean; |
| node = hw_find_property (me, property); |
| if (node == NULL) |
| hw_abort (me, "property \"%s\" not found", property); |
| if (node->type != boolean_property) |
| hw_abort (me, "property \"%s\" of wrong type (boolean)", property); |
| ASSERT (sizeof (boolean) == node->sizeof_array); |
| memcpy (&boolean, node->array, sizeof (boolean)); |
| return boolean; |
| } |
| |
| |
| |
| #if 0 |
| void |
| hw_add_ihandle_runtime_property (struct hw *me, |
| const char *property, |
| const ihandle_runtime_property_spec *ihandle) |
| { |
| /* enter the full path as the init array */ |
| hw_add_property (me, property, ihandle_property, |
| ihandle->full_path, strlen (ihandle->full_path) + 1, |
| NULL, 0, |
| NULL, permenant_object); |
| } |
| #endif |
| |
| #if 0 |
| void |
| hw_find_ihandle_runtime_property (struct hw *me, |
| const char *property, |
| ihandle_runtime_property_spec *ihandle) |
| { |
| struct hw_property_data *entry = find_property_data (me, property); |
| if (entry == NULL) |
| hw_abort (me, "property \"%s\" not found", property); |
| if (entry->property->type != ihandle_property |
| || entry->property->disposition != permenant_object) |
| hw_abort (me, "property \"%s\" of wrong type", property); |
| ASSERT (entry->init_array != NULL); |
| /* the full path */ |
| ihandle->full_path = entry->init_array; |
| } |
| #endif |
| |
| |
| |
| #if 0 |
| void |
| hw_set_ihandle_property (struct hw *me, |
| const char *property, |
| hw_instance *ihandle) |
| { |
| unsigned_cell cells; |
| cells = H2BE_cell (hw_instance_to_external (ihandle)); |
| hw_set_property (me, property, ihandle_property, |
| &cells, sizeof (cells)); |
| |
| } |
| #endif |
| |
| #if 0 |
| hw_instance * |
| hw_find_ihandle_property (struct hw *me, |
| const char *property) |
| { |
| const hw_property_data *node; |
| unsigned_cell ihandle; |
| hw_instance *instance; |
| |
| node = hw_find_property (me, property); |
| if (node == NULL) |
| hw_abort (me, "property \"%s\" not found", property); |
| if (node->type != ihandle_property) |
| hw_abort (me, "property \"%s\" of wrong type (ihandle)", property); |
| if (node->array == NULL) |
| hw_abort (me, "runtime property \"%s\" not yet initialized", property); |
| |
| ASSERT (sizeof (ihandle) == node->sizeof_array); |
| memcpy (&ihandle, node->array, sizeof (ihandle)); |
| instance = external_to_hw_instance (me, BE2H_cell (ihandle)); |
| ASSERT (instance != NULL); |
| return instance; |
| } |
| #endif |
| |
| |
| void |
| hw_add_integer_property (struct hw *me, |
| const char *property, |
| signed_cell integer) |
| { |
| H2BE (integer); |
| hw_add_property (me, property, integer_property, |
| &integer, sizeof (integer), |
| &integer, sizeof (integer), |
| NULL, permenant_object); |
| } |
| |
| signed_cell |
| hw_find_integer_property (struct hw *me, |
| const char *property) |
| { |
| const struct hw_property *node; |
| signed_cell integer; |
| node = hw_find_property (me, property); |
| if (node == NULL) |
| hw_abort (me, "property \"%s\" not found", property); |
| if (node->type != integer_property) |
| hw_abort (me, "property \"%s\" of wrong type (integer)", property); |
| ASSERT (sizeof (integer) == node->sizeof_array); |
| memcpy (&integer, node->array, sizeof (integer)); |
| return BE2H_cell (integer); |
| } |
| |
| int |
| hw_find_integer_array_property (struct hw *me, |
| const char *property, |
| unsigned index, |
| signed_cell *integer) |
| { |
| const struct hw_property *node; |
| int sizeof_integer = sizeof (*integer); |
| signed_cell *cell; |
| |
| /* check things sane */ |
| node = hw_find_property (me, property); |
| if (node == NULL) |
| hw_abort (me, "property \"%s\" not found", property); |
| if (node->type != integer_property |
| && node->type != array_property) |
| hw_abort (me, "property \"%s\" of wrong type (integer or array)", property); |
| if ((node->sizeof_array % sizeof_integer) != 0) |
| hw_abort (me, "property \"%s\" contains an incomplete number of cells", property); |
| if (node->sizeof_array <= sizeof_integer * index) |
| return 0; |
| |
| /* Find and convert the value */ |
| cell = ((signed_cell*)node->array) + index; |
| *integer = BE2H_cell (*cell); |
| |
| return node->sizeof_array / sizeof_integer; |
| } |
| |
| |
| static unsigned_cell * |
| unit_address_to_cells (const hw_unit *unit, |
| unsigned_cell *cell, |
| int nr_cells) |
| { |
| int i; |
| ASSERT (nr_cells == unit->nr_cells); |
| for (i = 0; i < unit->nr_cells; i++) |
| { |
| *cell = H2BE_cell (unit->cells[i]); |
| cell += 1; |
| } |
| return cell; |
| } |
| |
| |
| static const unsigned_cell * |
| cells_to_unit_address (const unsigned_cell *cell, |
| hw_unit *unit, |
| int nr_cells) |
| { |
| int i; |
| memset (unit, 0, sizeof (*unit)); |
| unit->nr_cells = nr_cells; |
| for (i = 0; i < unit->nr_cells; i++) |
| { |
| unit->cells[i] = BE2H_cell (*cell); |
| cell += 1; |
| } |
| return cell; |
| } |
| |
| |
| static unsigned |
| nr_range_property_cells (struct hw *me, |
| int nr_ranges) |
| { |
| return ((hw_unit_nr_address_cells (me) |
| + hw_unit_nr_address_cells (hw_parent (me)) |
| + hw_unit_nr_size_cells (me)) |
| ) * nr_ranges; |
| } |
| |
| void |
| hw_add_range_array_property (struct hw *me, |
| const char *property, |
| const range_property_spec *ranges, |
| unsigned nr_ranges) |
| { |
| unsigned sizeof_cells = (nr_range_property_cells (me, nr_ranges) |
| * sizeof (unsigned_cell)); |
| unsigned_cell *cells = hw_zalloc (me, sizeof_cells); |
| unsigned_cell *cell; |
| int i; |
| |
| /* copy the property elements over */ |
| cell = cells; |
| for (i = 0; i < nr_ranges; i++) |
| { |
| const range_property_spec *range = &ranges[i]; |
| /* copy the child address */ |
| cell = unit_address_to_cells (&range->child_address, cell, |
| hw_unit_nr_address_cells (me)); |
| /* copy the parent address */ |
| cell = unit_address_to_cells (&range->parent_address, cell, |
| hw_unit_nr_address_cells (hw_parent (me))); |
| /* copy the size */ |
| cell = unit_address_to_cells (&range->size, cell, |
| hw_unit_nr_size_cells (me)); |
| } |
| ASSERT (cell == &cells[nr_range_property_cells (me, nr_ranges)]); |
| |
| /* add it */ |
| hw_add_property (me, property, range_array_property, |
| cells, sizeof_cells, |
| cells, sizeof_cells, |
| NULL, permenant_object); |
| |
| hw_free (me, cells); |
| } |
| |
| int |
| hw_find_range_array_property (struct hw *me, |
| const char *property, |
| unsigned index, |
| range_property_spec *range) |
| { |
| const struct hw_property *node; |
| unsigned sizeof_entry = (nr_range_property_cells (me, 1) |
| * sizeof (unsigned_cell)); |
| const unsigned_cell *cells; |
| |
| /* locate the property */ |
| node = hw_find_property (me, property); |
| if (node == NULL) |
| hw_abort (me, "property \"%s\" not found", property); |
| if (node->type != range_array_property) |
| hw_abort (me, "property \"%s\" of wrong type (range array)", property); |
| |
| /* aligned ? */ |
| if ((node->sizeof_array % sizeof_entry) != 0) |
| hw_abort (me, "property \"%s\" contains an incomplete number of entries", |
| property); |
| |
| /* within bounds? */ |
| if (node->sizeof_array < sizeof_entry * (index + 1)) |
| return 0; |
| |
| /* find the range of interest */ |
| cells = (unsigned_cell*)((char*)node->array + sizeof_entry * index); |
| |
| /* copy the child address out - converting as we go */ |
| cells = cells_to_unit_address (cells, &range->child_address, |
| hw_unit_nr_address_cells (me)); |
| |
| /* copy the parent address out - converting as we go */ |
| cells = cells_to_unit_address (cells, &range->parent_address, |
| hw_unit_nr_address_cells (hw_parent (me))); |
| |
| /* copy the size - converting as we go */ |
| cells = cells_to_unit_address (cells, &range->size, |
| hw_unit_nr_size_cells (me)); |
| |
| return node->sizeof_array / sizeof_entry; |
| } |
| |
| |
| static unsigned |
| nr_reg_property_cells (struct hw *me, |
| int nr_regs) |
| { |
| return (hw_unit_nr_address_cells (hw_parent (me)) |
| + hw_unit_nr_size_cells (hw_parent (me)) |
| ) * nr_regs; |
| } |
| |
| void |
| hw_add_reg_array_property (struct hw *me, |
| const char *property, |
| const reg_property_spec *regs, |
| unsigned nr_regs) |
| { |
| unsigned sizeof_cells = (nr_reg_property_cells (me, nr_regs) |
| * sizeof (unsigned_cell)); |
| unsigned_cell *cells = hw_zalloc (me, sizeof_cells); |
| unsigned_cell *cell; |
| int i; |
| |
| /* copy the property elements over */ |
| cell = cells; |
| for (i = 0; i < nr_regs; i++) |
| { |
| const reg_property_spec *reg = ®s[i]; |
| /* copy the address */ |
| cell = unit_address_to_cells (®->address, cell, |
| hw_unit_nr_address_cells (hw_parent (me))); |
| /* copy the size */ |
| cell = unit_address_to_cells (®->size, cell, |
| hw_unit_nr_size_cells (hw_parent (me))); |
| } |
| ASSERT (cell == &cells[nr_reg_property_cells (me, nr_regs)]); |
| |
| /* add it */ |
| hw_add_property (me, property, reg_array_property, |
| cells, sizeof_cells, |
| cells, sizeof_cells, |
| NULL, permenant_object); |
| |
| hw_free (me, cells); |
| } |
| |
| int |
| hw_find_reg_array_property (struct hw *me, |
| const char *property, |
| unsigned index, |
| reg_property_spec *reg) |
| { |
| const struct hw_property *node; |
| unsigned sizeof_entry = (nr_reg_property_cells (me, 1) |
| * sizeof (unsigned_cell)); |
| const unsigned_cell *cells; |
| |
| /* locate the property */ |
| node = hw_find_property (me, property); |
| if (node == NULL) |
| hw_abort (me, "property \"%s\" not found", property); |
| if (node->type != reg_array_property) |
| hw_abort (me, "property \"%s\" of wrong type (reg array)", property); |
| |
| /* aligned ? */ |
| if ((node->sizeof_array % sizeof_entry) != 0) |
| hw_abort (me, "property \"%s\" contains an incomplete number of entries", |
| property); |
| |
| /* within bounds? */ |
| if (node->sizeof_array < sizeof_entry * (index + 1)) |
| return 0; |
| |
| /* find the range of interest */ |
| cells = (unsigned_cell*)((char*)node->array + sizeof_entry * index); |
| |
| /* copy the address out - converting as we go */ |
| cells = cells_to_unit_address (cells, ®->address, |
| hw_unit_nr_address_cells (hw_parent (me))); |
| |
| /* copy the size out - converting as we go */ |
| cells = cells_to_unit_address (cells, ®->size, |
| hw_unit_nr_size_cells (hw_parent (me))); |
| |
| return node->sizeof_array / sizeof_entry; |
| } |
| |
| |
| void |
| hw_add_string_property (struct hw *me, |
| const char *property, |
| const char *string) |
| { |
| hw_add_property (me, property, string_property, |
| string, strlen (string) + 1, |
| string, strlen (string) + 1, |
| NULL, permenant_object); |
| } |
| |
| const char * |
| hw_find_string_property (struct hw *me, |
| const char *property) |
| { |
| const struct hw_property *node; |
| const char *string; |
| node = hw_find_property (me, property); |
| if (node == NULL) |
| hw_abort (me, "property \"%s\" not found", property); |
| if (node->type != string_property) |
| hw_abort (me, "property \"%s\" of wrong type (string)", property); |
| string = node->array; |
| ASSERT (strlen (string) + 1 == node->sizeof_array); |
| return string; |
| } |
| |
| void |
| hw_add_string_array_property (struct hw *me, |
| const char *property, |
| const string_property_spec *strings, |
| unsigned nr_strings) |
| { |
| int sizeof_array; |
| int string_nr; |
| char *array; |
| char *chp; |
| if (nr_strings == 0) |
| hw_abort (me, "property \"%s\" must be non-null", property); |
| /* total up the size of the needed array */ |
| for (sizeof_array = 0, string_nr = 0; |
| string_nr < nr_strings; |
| string_nr ++) |
| { |
| sizeof_array += strlen (strings[string_nr]) + 1; |
| } |
| /* create the array */ |
| array = (char*) hw_zalloc (me, sizeof_array); |
| chp = array; |
| for (string_nr = 0; |
| string_nr < nr_strings; |
| string_nr++) |
| { |
| strcpy (chp, strings[string_nr]); |
| chp += strlen (chp) + 1; |
| } |
| ASSERT (chp == array + sizeof_array); |
| /* now enter it */ |
| hw_add_property (me, property, string_array_property, |
| array, sizeof_array, |
| array, sizeof_array, |
| NULL, permenant_object); |
| } |
| |
| int |
| hw_find_string_array_property (struct hw *me, |
| const char *property, |
| unsigned index, |
| string_property_spec *string) |
| { |
| const struct hw_property *node; |
| node = hw_find_property (me, property); |
| if (node == NULL) |
| hw_abort (me, "property \"%s\" not found", property); |
| switch (node->type) |
| { |
| default: |
| hw_abort (me, "property \"%s\" of wrong type", property); |
| break; |
| case string_property: |
| if (index == 0) |
| { |
| *string = node->array; |
| ASSERT (strlen (*string) + 1 == node->sizeof_array); |
| return 1; |
| } |
| break; |
| case array_property: |
| if (node->sizeof_array == 0 |
| || ((char*)node->array)[node->sizeof_array - 1] != '\0') |
| hw_abort (me, "property \"%s\" invalid for string array", property); |
| /* FALL THROUGH */ |
| case string_array_property: |
| ASSERT (node->sizeof_array > 0); |
| ASSERT (((char*)node->array)[node->sizeof_array - 1] == '\0'); |
| { |
| const char *chp = node->array; |
| int nr_entries = 0; |
| /* count the number of strings, keeping an eye out for the one |
| we're looking for */ |
| *string = chp; |
| do |
| { |
| if (*chp == '\0') |
| { |
| /* next string */ |
| nr_entries++; |
| chp++; |
| if (nr_entries == index) |
| *string = chp; |
| } |
| else |
| { |
| chp++; |
| } |
| } while (chp < (char*)node->array + node->sizeof_array); |
| if (index < nr_entries) |
| return nr_entries; |
| else |
| { |
| *string = NULL; |
| return 0; |
| } |
| } |
| break; |
| } |
| return 0; |
| } |
| |
| void |
| hw_add_duplicate_property (struct hw *me, |
| const char *property, |
| const struct hw_property *original) |
| { |
| struct hw_property_data *master; |
| if (original->disposition != permenant_object) |
| hw_abort (me, "Can only duplicate permenant objects"); |
| /* find the original's master */ |
| master = original->owner->properties_of_hw; |
| while (master->property != original) |
| { |
| master = master->next; |
| ASSERT (master != NULL); |
| } |
| /* now duplicate it */ |
| hw_add_property (me, property, |
| original->type, |
| master->init_array, master->sizeof_init_array, |
| original->array, original->sizeof_array, |
| original, permenant_object); |
| } |