1/* The common simulator framework for GDB, the GNU Debugger.
2
3   Copyright 2002, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
4
5   Contributed by Andrew Cagney and Red Hat.
6
7   This file is part of GDB.
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 3 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19   You should have received a copy of the GNU General Public License
20   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
21
22#include "hw-main.h"
23#include "hw-base.h"
24
25#include "sim-io.h"
26#include "sim-assert.h"
27
28#ifdef HAVE_STRING_H
29#include <string.h>
30#else
31#ifdef HAVE_STRINGS_H
32#include <strings.h>
33#endif
34#endif
35
36/* property entries */
37
38struct hw_property_data
39{
40  struct hw_property_data *next;
41  struct hw_property *property;
42  const void *init_array;
43  unsigned sizeof_init_array;
44};
45
46void
47create_hw_property_data (struct hw *me)
48{
49}
50
51void
52delete_hw_property_data (struct hw *me)
53{
54}
55
56
57/* Device Properties: */
58
59static struct hw_property_data *
60find_property_data (struct hw *me,
61		    const char *property)
62{
63  struct hw_property_data *entry;
64  ASSERT (property != NULL);
65  entry = me->properties_of_hw;
66  while (entry != NULL)
67    {
68      if (strcmp (entry->property->name, property) == 0)
69	return entry;
70      entry = entry->next;
71    }
72  return NULL;
73}
74
75
76static void
77hw_add_property (struct hw *me,
78		 const char *property,
79		 hw_property_type type,
80		 const void *init_array,
81		 unsigned sizeof_init_array,
82		 const void *array,
83		 unsigned sizeof_array,
84		 const struct hw_property *original,
85		 object_disposition disposition)
86{
87  struct hw_property_data *new_entry = NULL;
88  struct hw_property *new_value = NULL;
89
90  /* find the list end */
91  struct hw_property_data **insertion_point = &me->properties_of_hw;
92  while (*insertion_point != NULL)
93    {
94      if (strcmp ((*insertion_point)->property->name, property) == 0)
95	return;
96      insertion_point = &(*insertion_point)->next;
97    }
98
99  /* create a new value */
100  new_value = HW_ZALLOC (me, struct hw_property);
101  new_value->name = (char *) strdup (property);
102  new_value->type = type;
103  if (sizeof_array > 0)
104    {
105      void *new_array = hw_zalloc (me, sizeof_array);
106      memcpy (new_array, array, sizeof_array);
107      new_value->array = new_array;
108      new_value->sizeof_array = sizeof_array;
109    }
110  new_value->owner = me;
111  new_value->original = original;
112  new_value->disposition = disposition;
113
114  /* insert the value into the list */
115  new_entry = HW_ZALLOC (me, struct hw_property_data);
116  *insertion_point = new_entry;
117  if (sizeof_init_array > 0)
118    {
119      void *new_init_array = hw_zalloc (me, sizeof_init_array);
120      memcpy (new_init_array, init_array, sizeof_init_array);
121      new_entry->init_array = new_init_array;
122      new_entry->sizeof_init_array = sizeof_init_array;
123    }
124  new_entry->property = new_value;
125}
126
127
128static void
129hw_set_property (struct hw *me,
130		 const char *property,
131		 hw_property_type type,
132		 const void *array,
133		 int sizeof_array)
134{
135  /* find the property */
136  struct hw_property_data *entry = find_property_data (me, property);
137  if (entry != NULL)
138    {
139      /* existing property - update it */
140      void *new_array = 0;
141      struct hw_property *value = entry->property;
142      /* check the type matches */
143      if (value->type != type)
144	hw_abort (me, "conflict between type of new and old value for property %s", property);
145      /* replace its value */
146      if (value->array != NULL)
147	hw_free (me, (void*)value->array);
148      new_array = (sizeof_array > 0
149		   ? hw_zalloc (me, sizeof_array)
150		   : (void*)0);
151      value->array = new_array;
152      value->sizeof_array = sizeof_array;
153      if (sizeof_array > 0)
154	memcpy (new_array, array, sizeof_array);
155      return;
156    }
157  else
158    {
159      /* new property - create it */
160      hw_add_property (me, property, type,
161		       NULL, 0, array, sizeof_array,
162		       NULL, temporary_object);
163    }
164}
165
166
167#if 0
168static void
169clean_hw_properties (struct hw *me)
170{
171  struct hw_property_data **delete_point = &me->properties_of_hw;
172  while (*delete_point != NULL)
173    {
174      struct hw_property_data *current = *delete_point;
175      switch (current->property->disposition)
176	{
177	case permenant_object:
178	  /* zap the current value, will be initialized later */
179	  ASSERT (current->init_array != NULL);
180	  if (current->property->array != NULL)
181	    {
182	      hw_free (me, (void*)current->property->array);
183	      current->property->array = NULL;
184	    }
185	  delete_point = &(*delete_point)->next;
186	  break;
187	case temporary_object:
188	  /* zap the actual property, was created during simulation run */
189	  ASSERT (current->init_array == NULL);
190	  *delete_point = current->next;
191	  if (current->property->array != NULL)
192	    hw_free (me, (void*)current->property->array);
193	  hw_free (me, current->property);
194	  hw_free (me, current);
195	  break;
196	}
197    }
198}
199#endif
200
201#if 0
202void
203hw_init_static_properties (SIM_DESC sd,
204			   struct hw *me,
205			   void *data)
206{
207  struct hw_property_data *property;
208  for (property = me->properties_of_hw;
209       property != NULL;
210       property = property->next)
211    {
212      ASSERT (property->init_array != NULL);
213      ASSERT (property->property->array == NULL);
214      ASSERT(property->property->disposition == permenant_object);
215      switch (property->property->type)
216	{
217	case array_property:
218	case boolean_property:
219	case range_array_property:
220	case reg_array_property:
221	case string_property:
222	case string_array_property:
223	case integer_property:
224	  /* delete the property, and replace it with the original */
225	  hw_set_property (me, property->property->name,
226			   property->property->type,
227			   property->init_array,
228			   property->sizeof_init_array);
229	  break;
230#if 0
231	case ihandle_property:
232	  break;
233#endif
234	}
235    }
236}
237#endif
238
239
240#if 0
241void
242hw_init_runtime_properties (SIM_DESC sd,
243			    struct hw *me,
244			    void *data)
245{
246  struct hw_property_data *property;
247  for (property = me->properties_of_hw;
248       property != NULL;
249       property = property->next)
250    {
251      switch (property->property->disposition)
252	{
253	case permenant_object:
254	  switch (property->property->type)
255	    {
256#if 0
257	    case ihandle_property:
258	      {
259		struct hw_instance *ihandle;
260		ihandle_runtime_property_spec spec;
261		ASSERT (property->init_array != NULL);
262		ASSERT (property->property->array == NULL);
263		hw_find_ihandle_runtime_property (me, property->property->name, &spec);
264		ihandle = tree_instance (me, spec.full_path);
265		hw_set_ihandle_property (me, property->property->name, ihandle);
266		break;
267	      }
268#endif
269	    case array_property:
270	    case boolean_property:
271	    case range_array_property:
272	    case integer_property:
273	    case reg_array_property:
274	    case string_property:
275	    case string_array_property:
276	      ASSERT (property->init_array != NULL);
277	      ASSERT (property->property->array != NULL);
278	      break;
279	    }
280	  break;
281	case temporary_object:
282	  ASSERT (property->init_array == NULL);
283	  ASSERT (property->property->array != NULL);
284	  break;
285	}
286    }
287}
288#endif
289
290
291
292const struct hw_property *
293hw_next_property (const struct hw_property *property)
294{
295  /* find the property in the list */
296  struct hw *owner = property->owner;
297  struct hw_property_data *entry = owner->properties_of_hw;
298  while (entry != NULL && entry->property != property)
299    entry = entry->next;
300  /* now return the following property */
301  ASSERT (entry != NULL); /* must be a member! */
302  if (entry->next != NULL)
303    return entry->next->property;
304  else
305    return NULL;
306}
307
308
309const struct hw_property *
310hw_find_property (struct hw *me,
311		  const char *property)
312{
313  if (me == NULL)
314    {
315      return NULL;
316    }
317  else if (property == NULL || strcmp (property, "") == 0)
318    {
319      if (me->properties_of_hw == NULL)
320	return NULL;
321      else
322	return me->properties_of_hw->property;
323    }
324  else
325    {
326      struct hw_property_data *entry = find_property_data (me, property);
327      if (entry != NULL)
328	return entry->property;
329    }
330  return NULL;
331}
332
333
334void
335hw_add_array_property (struct hw *me,
336		       const char *property,
337		       const void *array,
338		       int sizeof_array)
339{
340  hw_add_property (me, property, array_property,
341		   array, sizeof_array, array, sizeof_array,
342		   NULL, permenant_object);
343}
344
345void
346hw_set_array_property (struct hw *me,
347		       const char *property,
348		       const void *array,
349		       int sizeof_array)
350{
351  hw_set_property (me, property, array_property, array, sizeof_array);
352}
353
354const struct hw_property *
355hw_find_array_property (struct hw *me,
356			const char *property)
357{
358  const struct hw_property *node;
359  node = hw_find_property (me, property);
360  if (node == NULL)
361    hw_abort (me, "property \"%s\" not found", property);
362  if (node->type != array_property)
363    hw_abort (me, "property \"%s\" of wrong type (array)", property);
364  return node;
365}
366
367
368
369void
370hw_add_boolean_property (struct hw *me,
371			 const char *property,
372			 int boolean)
373{
374  signed32 new_boolean = (boolean ? -1 : 0);
375  hw_add_property (me, property, boolean_property,
376		   &new_boolean, sizeof(new_boolean),
377		   &new_boolean, sizeof(new_boolean),
378		   NULL, permenant_object);
379}
380
381int
382hw_find_boolean_property (struct hw *me,
383			  const char *property)
384{
385  const struct hw_property *node;
386  unsigned_cell boolean;
387  node = hw_find_property (me, property);
388  if (node == NULL)
389    hw_abort (me, "property \"%s\" not found", property);
390  if (node->type != boolean_property)
391    hw_abort (me, "property \"%s\" of wrong type (boolean)", property);
392  ASSERT (sizeof (boolean) == node->sizeof_array);
393  memcpy (&boolean, node->array, sizeof (boolean));
394  return boolean;
395}
396
397
398
399#if 0
400void
401hw_add_ihandle_runtime_property (struct hw *me,
402				 const char *property,
403				 const ihandle_runtime_property_spec *ihandle)
404{
405  /* enter the full path as the init array */
406  hw_add_property (me, property, ihandle_property,
407		   ihandle->full_path, strlen(ihandle->full_path) + 1,
408		   NULL, 0,
409		   NULL, permenant_object);
410}
411#endif
412
413#if 0
414void
415hw_find_ihandle_runtime_property (struct hw *me,
416				  const char *property,
417				  ihandle_runtime_property_spec *ihandle)
418{
419  struct hw_property_data *entry = find_property_data (me, property);
420  HW_TRACE ((me, "hw_find_ihandle_runtime_property(property=%s)\n", property));
421  if (entry == NULL)
422    hw_abort (me, "property \"%s\" not found", property);
423  if (entry->property->type != ihandle_property
424      || entry->property->disposition != permenant_object)
425    hw_abort (me, "property \"%s\" of wrong type", property);
426  ASSERT (entry->init_array != NULL);
427  /* the full path */
428  ihandle->full_path = entry->init_array;
429}
430#endif
431
432
433
434#if 0
435void
436hw_set_ihandle_property (struct hw *me,
437			 const char *property,
438			 hw_instance *ihandle)
439{
440  unsigned_cell cells;
441  cells = H2BE_cell (hw_instance_to_external (ihandle));
442  hw_set_property (me, property, ihandle_property,
443		   &cells, sizeof (cells));
444
445}
446#endif
447
448#if 0
449hw_instance *
450hw_find_ihandle_property (struct hw *me,
451			  const char *property)
452{
453  const hw_property_data *node;
454  unsigned_cell ihandle;
455  hw_instance *instance;
456
457  node = hw_find_property (me, property);
458  if (node == NULL)
459    hw_abort (me, "property \"%s\" not found", property);
460  if (node->type != ihandle_property)
461    hw_abort(me, "property \"%s\" of wrong type (ihandle)", property);
462  if (node->array == NULL)
463    hw_abort(me, "runtime property \"%s\" not yet initialized", property);
464
465  ASSERT (sizeof(ihandle) == node->sizeof_array);
466  memcpy (&ihandle, node->array, sizeof(ihandle));
467  instance = external_to_hw_instance (me, BE2H_cell(ihandle));
468  ASSERT (instance != NULL);
469  return instance;
470}
471#endif
472
473
474void
475hw_add_integer_property (struct hw *me,
476			 const char *property,
477			 signed_cell integer)
478{
479  H2BE (integer);
480  hw_add_property (me, property, integer_property,
481		   &integer, sizeof(integer),
482		   &integer, sizeof(integer),
483		   NULL, permenant_object);
484}
485
486signed_cell
487hw_find_integer_property (struct hw *me,
488			  const char *property)
489{
490  const struct hw_property *node;
491  signed_cell integer;
492  HW_TRACE ((me, "hw_find_integer(property=%s)\n", property));
493  node = hw_find_property (me, property);
494  if (node == NULL)
495    hw_abort (me, "property \"%s\" not found", property);
496  if (node->type != integer_property)
497    hw_abort (me, "property \"%s\" of wrong type (integer)", property);
498  ASSERT (sizeof(integer) == node->sizeof_array);
499  memcpy (&integer, node->array, sizeof (integer));
500  return BE2H_cell (integer);
501}
502
503int
504hw_find_integer_array_property (struct hw *me,
505				const char *property,
506				unsigned index,
507				signed_cell *integer)
508{
509  const struct hw_property *node;
510  int sizeof_integer = sizeof (*integer);
511  signed_cell *cell;
512  HW_TRACE ((me, "hw_find_integer(property=%s)\n", property));
513
514  /* check things sane */
515  node = hw_find_property (me, property);
516  if (node == NULL)
517    hw_abort (me, "property \"%s\" not found", property);
518  if (node->type != integer_property
519      && node->type != array_property)
520    hw_abort (me, "property \"%s\" of wrong type (integer or array)", property);
521  if ((node->sizeof_array % sizeof_integer) != 0)
522    hw_abort (me, "property \"%s\" contains an incomplete number of cells", property);
523  if (node->sizeof_array <= sizeof_integer * index)
524    return 0;
525
526  /* Find and convert the value */
527  cell = ((signed_cell*)node->array) + index;
528  *integer = BE2H_cell (*cell);
529
530  return node->sizeof_array / sizeof_integer;
531}
532
533
534static unsigned_cell *
535unit_address_to_cells (const hw_unit *unit,
536		       unsigned_cell *cell,
537		       int nr_cells)
538{
539  int i;
540  ASSERT(nr_cells == unit->nr_cells);
541  for (i = 0; i < unit->nr_cells; i++)
542    {
543      *cell = H2BE_cell (unit->cells[i]);
544      cell += 1;
545    }
546  return cell;
547}
548
549
550static const unsigned_cell *
551cells_to_unit_address (const unsigned_cell *cell,
552		       hw_unit *unit,
553		       int nr_cells)
554{
555  int i;
556  memset(unit, 0, sizeof(*unit));
557  unit->nr_cells = nr_cells;
558  for (i = 0; i < unit->nr_cells; i++)
559    {
560      unit->cells[i] = BE2H_cell (*cell);
561      cell += 1;
562    }
563  return cell;
564}
565
566
567static unsigned
568nr_range_property_cells (struct hw *me,
569			 int nr_ranges)
570{
571  return ((hw_unit_nr_address_cells (me)
572	   + hw_unit_nr_address_cells (hw_parent (me))
573	   + hw_unit_nr_size_cells (me))
574	  ) * nr_ranges;
575}
576
577void
578hw_add_range_array_property (struct hw *me,
579			     const char *property,
580			     const range_property_spec *ranges,
581			     unsigned nr_ranges)
582{
583  unsigned sizeof_cells = (nr_range_property_cells (me, nr_ranges)
584			   * sizeof (unsigned_cell));
585  unsigned_cell *cells = hw_zalloc (me, sizeof_cells);
586  unsigned_cell *cell;
587  int i;
588
589  /* copy the property elements over */
590  cell = cells;
591  for (i = 0; i < nr_ranges; i++)
592    {
593      const range_property_spec *range = &ranges[i];
594      /* copy the child address */
595      cell = unit_address_to_cells (&range->child_address, cell,
596				    hw_unit_nr_address_cells (me));
597      /* copy the parent address */
598      cell = unit_address_to_cells (&range->parent_address, cell,
599				    hw_unit_nr_address_cells (hw_parent (me)));
600      /* copy the size */
601      cell = unit_address_to_cells (&range->size, cell,
602				    hw_unit_nr_size_cells (me));
603    }
604  ASSERT (cell == &cells[nr_range_property_cells (me, nr_ranges)]);
605
606  /* add it */
607  hw_add_property (me, property, range_array_property,
608		   cells, sizeof_cells,
609		   cells, sizeof_cells,
610		   NULL, permenant_object);
611
612  hw_free (me, cells);
613}
614
615int
616hw_find_range_array_property (struct hw *me,
617			      const char *property,
618			      unsigned index,
619			      range_property_spec *range)
620{
621  const struct hw_property *node;
622  unsigned sizeof_entry = (nr_range_property_cells (me, 1)
623			   * sizeof (unsigned_cell));
624  const unsigned_cell *cells;
625
626  /* locate the property */
627  node = hw_find_property (me, property);
628  if (node == NULL)
629    hw_abort (me, "property \"%s\" not found", property);
630  if (node->type != range_array_property)
631    hw_abort (me, "property \"%s\" of wrong type (range array)", property);
632
633  /* aligned ? */
634  if ((node->sizeof_array % sizeof_entry) != 0)
635    hw_abort (me, "property \"%s\" contains an incomplete number of entries",
636	      property);
637
638  /* within bounds? */
639  if (node->sizeof_array < sizeof_entry * (index + 1))
640    return 0;
641
642  /* find the range of interest */
643  cells = (unsigned_cell*)((char*)node->array + sizeof_entry * index);
644
645  /* copy the child address out - converting as we go */
646  cells = cells_to_unit_address (cells, &range->child_address,
647				 hw_unit_nr_address_cells (me));
648
649  /* copy the parent address out - converting as we go */
650  cells = cells_to_unit_address (cells, &range->parent_address,
651				 hw_unit_nr_address_cells (hw_parent (me)));
652
653  /* copy the size - converting as we go */
654  cells = cells_to_unit_address (cells, &range->size,
655				 hw_unit_nr_size_cells (me));
656
657  return node->sizeof_array / sizeof_entry;
658}
659
660
661static unsigned
662nr_reg_property_cells (struct hw *me,
663		       int nr_regs)
664{
665  return (hw_unit_nr_address_cells (hw_parent(me))
666	  + hw_unit_nr_size_cells (hw_parent(me))
667	  ) * nr_regs;
668}
669
670void
671hw_add_reg_array_property (struct hw *me,
672			   const char *property,
673			   const reg_property_spec *regs,
674			   unsigned nr_regs)
675{
676  unsigned sizeof_cells = (nr_reg_property_cells (me, nr_regs)
677			   * sizeof (unsigned_cell));
678  unsigned_cell *cells = hw_zalloc (me, sizeof_cells);
679  unsigned_cell *cell;
680  int i;
681
682  /* copy the property elements over */
683  cell = cells;
684  for (i = 0; i < nr_regs; i++)
685    {
686      const reg_property_spec *reg = &regs[i];
687      /* copy the address */
688      cell = unit_address_to_cells (&reg->address, cell,
689				    hw_unit_nr_address_cells (hw_parent (me)));
690      /* copy the size */
691      cell = unit_address_to_cells (&reg->size, cell,
692				    hw_unit_nr_size_cells (hw_parent (me)));
693    }
694  ASSERT (cell == &cells[nr_reg_property_cells (me, nr_regs)]);
695
696  /* add it */
697  hw_add_property (me, property, reg_array_property,
698		   cells, sizeof_cells,
699		   cells, sizeof_cells,
700		   NULL, permenant_object);
701
702  hw_free (me, cells);
703}
704
705int
706hw_find_reg_array_property (struct hw *me,
707			    const char *property,
708			    unsigned index,
709			    reg_property_spec *reg)
710{
711  const struct hw_property *node;
712  unsigned sizeof_entry = (nr_reg_property_cells (me, 1)
713			   * sizeof (unsigned_cell));
714  const unsigned_cell *cells;
715
716  /* locate the property */
717  node = hw_find_property (me, property);
718  if (node == NULL)
719    hw_abort (me, "property \"%s\" not found", property);
720  if (node->type != reg_array_property)
721    hw_abort (me, "property \"%s\" of wrong type (reg array)", property);
722
723  /* aligned ? */
724  if ((node->sizeof_array % sizeof_entry) != 0)
725    hw_abort (me, "property \"%s\" contains an incomplete number of entries",
726	      property);
727
728  /* within bounds? */
729  if (node->sizeof_array < sizeof_entry * (index + 1))
730    return 0;
731
732  /* find the range of interest */
733  cells = (unsigned_cell*)((char*)node->array + sizeof_entry * index);
734
735  /* copy the address out - converting as we go */
736  cells = cells_to_unit_address (cells, &reg->address,
737				 hw_unit_nr_address_cells (hw_parent (me)));
738
739  /* copy the size out - converting as we go */
740  cells = cells_to_unit_address (cells, &reg->size,
741				 hw_unit_nr_size_cells (hw_parent (me)));
742
743  return node->sizeof_array / sizeof_entry;
744}
745
746
747void
748hw_add_string_property (struct hw *me,
749			const char *property,
750			const char *string)
751{
752  hw_add_property (me, property, string_property,
753		   string, strlen(string) + 1,
754		   string, strlen(string) + 1,
755		   NULL, permenant_object);
756}
757
758const char *
759hw_find_string_property (struct hw *me,
760			 const char *property)
761{
762  const struct hw_property *node;
763  const char *string;
764  node = hw_find_property (me, property);
765  if (node == NULL)
766    hw_abort (me, "property \"%s\" not found", property);
767  if (node->type != string_property)
768    hw_abort (me, "property \"%s\" of wrong type (string)", property);
769  string = node->array;
770  ASSERT (strlen(string) + 1 == node->sizeof_array);
771  return string;
772}
773
774void
775hw_add_string_array_property (struct hw *me,
776			      const char *property,
777			      const string_property_spec *strings,
778			      unsigned nr_strings)
779{
780  int sizeof_array;
781  int string_nr;
782  char *array;
783  char *chp;
784  if (nr_strings == 0)
785    hw_abort (me, "property \"%s\" must be non-null", property);
786  /* total up the size of the needed array */
787  for (sizeof_array = 0, string_nr = 0;
788       string_nr < nr_strings;
789       string_nr ++)
790    {
791      sizeof_array += strlen (strings[string_nr]) + 1;
792    }
793  /* create the array */
794  array = (char*) hw_zalloc (me, sizeof_array);
795  chp = array;
796  for (string_nr = 0;
797       string_nr < nr_strings;
798       string_nr++)
799    {
800      strcpy (chp, strings[string_nr]);
801      chp += strlen (chp) + 1;
802    }
803  ASSERT (chp == array + sizeof_array);
804  /* now enter it */
805  hw_add_property (me, property, string_array_property,
806		   array, sizeof_array,
807		   array, sizeof_array,
808		   NULL, permenant_object);
809}
810
811int
812hw_find_string_array_property (struct hw *me,
813			       const char *property,
814			       unsigned index,
815			       string_property_spec *string)
816{
817  const struct hw_property *node;
818  node = hw_find_property (me, property);
819  if (node == NULL)
820    hw_abort (me, "property \"%s\" not found", property);
821  switch (node->type)
822    {
823    default:
824      hw_abort (me, "property \"%s\" of wrong type", property);
825      break;
826    case string_property:
827      if (index == 0)
828	{
829	  *string = node->array;
830	  ASSERT (strlen(*string) + 1 == node->sizeof_array);
831	  return 1;
832	}
833      break;
834    case array_property:
835      if (node->sizeof_array == 0
836	  || ((char*)node->array)[node->sizeof_array - 1] != '\0')
837	hw_abort (me, "property \"%s\" invalid for string array", property);
838      /* FALL THROUGH */
839    case string_array_property:
840      ASSERT (node->sizeof_array > 0);
841      ASSERT (((char*)node->array)[node->sizeof_array - 1] == '\0');
842      {
843	const char *chp = node->array;
844	int nr_entries = 0;
845	/* count the number of strings, keeping an eye out for the one
846	   we're looking for */
847	*string = chp;
848	do
849	  {
850	    if (*chp == '\0')
851	      {
852		/* next string */
853		nr_entries++;
854		chp++;
855		if (nr_entries == index)
856		  *string = chp;
857	      }
858	    else
859	      {
860		chp++;
861	      }
862	  } while (chp < (char*)node->array + node->sizeof_array);
863	if (index < nr_entries)
864	  return nr_entries;
865	else
866	  {
867	    *string = NULL;
868	    return 0;
869	  }
870      }
871      break;
872    }
873  return 0;
874}
875
876void
877hw_add_duplicate_property (struct hw *me,
878			   const char *property,
879			   const struct hw_property *original)
880{
881  struct hw_property_data *master;
882  HW_TRACE ((me, "hw_add_duplicate_property(property=%s, ...)\n", property));
883  if (original->disposition != permenant_object)
884    hw_abort (me, "Can only duplicate permenant objects");
885  /* find the original's master */
886  master = original->owner->properties_of_hw;
887  while (master->property != original)
888    {
889      master = master->next;
890      ASSERT(master != NULL);
891    }
892  /* now duplicate it */
893  hw_add_property (me, property,
894		   original->type,
895		   master->init_array, master->sizeof_init_array,
896		   original->array, original->sizeof_array,
897		   original, permenant_object);
898}
899