sim: parse_args: display getopt error ourselves
[external/binutils.git] / sim / common / hw-properties.c
1 /* The common simulator framework for GDB, the GNU Debugger.
2
3    Copyright 2002-2016 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
38 struct 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
46 void
47 create_hw_property_data (struct hw *me)
48 {
49 }
50
51 void
52 delete_hw_property_data (struct hw *me)
53 {
54 }
55
56
57 /* Device Properties: */
58
59 static struct hw_property_data *
60 find_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
76 static void
77 hw_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
128 static void
129 hw_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
168 static void
169 clean_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
202 void
203 hw_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
241 void
242 hw_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
292 const struct hw_property *
293 hw_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
309 const struct hw_property *
310 hw_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
334 void
335 hw_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
345 void
346 hw_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
354 const struct hw_property *
355 hw_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
369 void
370 hw_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
381 int
382 hw_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
400 void
401 hw_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
414 void
415 hw_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   if (entry == NULL)
421     hw_abort (me, "property \"%s\" not found", property);
422   if (entry->property->type != ihandle_property
423       || entry->property->disposition != permenant_object)
424     hw_abort (me, "property \"%s\" of wrong type", property);
425   ASSERT (entry->init_array != NULL);
426   /* the full path */
427   ihandle->full_path = entry->init_array;
428 }
429 #endif
430
431
432
433 #if 0
434 void
435 hw_set_ihandle_property (struct hw *me,
436                          const char *property,
437                          hw_instance *ihandle)
438 {
439   unsigned_cell cells;
440   cells = H2BE_cell (hw_instance_to_external (ihandle));
441   hw_set_property (me, property, ihandle_property,
442                    &cells, sizeof (cells));
443
444 }
445 #endif
446
447 #if 0
448 hw_instance *
449 hw_find_ihandle_property (struct hw *me,
450                           const char *property)
451 {
452   const hw_property_data *node;
453   unsigned_cell ihandle;
454   hw_instance *instance;
455
456   node = hw_find_property (me, property);
457   if (node == NULL)
458     hw_abort (me, "property \"%s\" not found", property);
459   if (node->type != ihandle_property)
460     hw_abort (me, "property \"%s\" of wrong type (ihandle)", property);
461   if (node->array == NULL)
462     hw_abort (me, "runtime property \"%s\" not yet initialized", property);
463
464   ASSERT (sizeof (ihandle) == node->sizeof_array);
465   memcpy (&ihandle, node->array, sizeof (ihandle));
466   instance = external_to_hw_instance (me, BE2H_cell (ihandle));
467   ASSERT (instance != NULL);
468   return instance;
469 }
470 #endif
471
472
473 void
474 hw_add_integer_property (struct hw *me,
475                          const char *property,
476                          signed_cell integer)
477 {
478   H2BE (integer);
479   hw_add_property (me, property, integer_property,
480                    &integer, sizeof (integer),
481                    &integer, sizeof (integer),
482                    NULL, permenant_object);
483 }
484
485 signed_cell
486 hw_find_integer_property (struct hw *me,
487                           const char *property)
488 {
489   const struct hw_property *node;
490   signed_cell integer;
491   node = hw_find_property (me, property);
492   if (node == NULL)
493     hw_abort (me, "property \"%s\" not found", property);
494   if (node->type != integer_property)
495     hw_abort (me, "property \"%s\" of wrong type (integer)", property);
496   ASSERT (sizeof (integer) == node->sizeof_array);
497   memcpy (&integer, node->array, sizeof (integer));
498   return BE2H_cell (integer);
499 }
500
501 int
502 hw_find_integer_array_property (struct hw *me,
503                                 const char *property,
504                                 unsigned index,
505                                 signed_cell *integer)
506 {
507   const struct hw_property *node;
508   int sizeof_integer = sizeof (*integer);
509   signed_cell *cell;
510
511   /* check things sane */
512   node = hw_find_property (me, property);
513   if (node == NULL)
514     hw_abort (me, "property \"%s\" not found", property);
515   if (node->type != integer_property
516       && node->type != array_property)
517     hw_abort (me, "property \"%s\" of wrong type (integer or array)", property);
518   if ((node->sizeof_array % sizeof_integer) != 0)
519     hw_abort (me, "property \"%s\" contains an incomplete number of cells", property);
520   if (node->sizeof_array <= sizeof_integer * index)
521     return 0;
522
523   /* Find and convert the value */
524   cell = ((signed_cell*)node->array) + index;
525   *integer = BE2H_cell (*cell);
526
527   return node->sizeof_array / sizeof_integer;
528 }
529
530
531 static unsigned_cell *
532 unit_address_to_cells (const hw_unit *unit,
533                        unsigned_cell *cell,
534                        int nr_cells)
535 {
536   int i;
537   ASSERT (nr_cells == unit->nr_cells);
538   for (i = 0; i < unit->nr_cells; i++)
539     {
540       *cell = H2BE_cell (unit->cells[i]);
541       cell += 1;
542     }
543   return cell;
544 }
545
546
547 static const unsigned_cell *
548 cells_to_unit_address (const unsigned_cell *cell,
549                        hw_unit *unit,
550                        int nr_cells)
551 {
552   int i;
553   memset (unit, 0, sizeof (*unit));
554   unit->nr_cells = nr_cells;
555   for (i = 0; i < unit->nr_cells; i++)
556     {
557       unit->cells[i] = BE2H_cell (*cell);
558       cell += 1;
559     }
560   return cell;
561 }
562
563
564 static unsigned
565 nr_range_property_cells (struct hw *me,
566                          int nr_ranges)
567 {
568   return ((hw_unit_nr_address_cells (me)
569            + hw_unit_nr_address_cells (hw_parent (me))
570            + hw_unit_nr_size_cells (me))
571           ) * nr_ranges;
572 }
573
574 void
575 hw_add_range_array_property (struct hw *me,
576                              const char *property,
577                              const range_property_spec *ranges,
578                              unsigned nr_ranges)
579 {
580   unsigned sizeof_cells = (nr_range_property_cells (me, nr_ranges)
581                            * sizeof (unsigned_cell));
582   unsigned_cell *cells = hw_zalloc (me, sizeof_cells);
583   unsigned_cell *cell;
584   int i;
585
586   /* copy the property elements over */
587   cell = cells;
588   for (i = 0; i < nr_ranges; i++)
589     {
590       const range_property_spec *range = &ranges[i];
591       /* copy the child address */
592       cell = unit_address_to_cells (&range->child_address, cell,
593                                     hw_unit_nr_address_cells (me));
594       /* copy the parent address */
595       cell = unit_address_to_cells (&range->parent_address, cell,
596                                     hw_unit_nr_address_cells (hw_parent (me)));
597       /* copy the size */
598       cell = unit_address_to_cells (&range->size, cell,
599                                     hw_unit_nr_size_cells (me));
600     }
601   ASSERT (cell == &cells[nr_range_property_cells (me, nr_ranges)]);
602
603   /* add it */
604   hw_add_property (me, property, range_array_property,
605                    cells, sizeof_cells,
606                    cells, sizeof_cells,
607                    NULL, permenant_object);
608
609   hw_free (me, cells);
610 }
611
612 int
613 hw_find_range_array_property (struct hw *me,
614                               const char *property,
615                               unsigned index,
616                               range_property_spec *range)
617 {
618   const struct hw_property *node;
619   unsigned sizeof_entry = (nr_range_property_cells (me, 1)
620                            * sizeof (unsigned_cell));
621   const unsigned_cell *cells;
622
623   /* locate the property */
624   node = hw_find_property (me, property);
625   if (node == NULL)
626     hw_abort (me, "property \"%s\" not found", property);
627   if (node->type != range_array_property)
628     hw_abort (me, "property \"%s\" of wrong type (range array)", property);
629
630   /* aligned ? */
631   if ((node->sizeof_array % sizeof_entry) != 0)
632     hw_abort (me, "property \"%s\" contains an incomplete number of entries",
633               property);
634
635   /* within bounds? */
636   if (node->sizeof_array < sizeof_entry * (index + 1))
637     return 0;
638
639   /* find the range of interest */
640   cells = (unsigned_cell*)((char*)node->array + sizeof_entry * index);
641
642   /* copy the child address out - converting as we go */
643   cells = cells_to_unit_address (cells, &range->child_address,
644                                  hw_unit_nr_address_cells (me));
645
646   /* copy the parent address out - converting as we go */
647   cells = cells_to_unit_address (cells, &range->parent_address,
648                                  hw_unit_nr_address_cells (hw_parent (me)));
649
650   /* copy the size - converting as we go */
651   cells = cells_to_unit_address (cells, &range->size,
652                                  hw_unit_nr_size_cells (me));
653
654   return node->sizeof_array / sizeof_entry;
655 }
656
657
658 static unsigned
659 nr_reg_property_cells (struct hw *me,
660                        int nr_regs)
661 {
662   return (hw_unit_nr_address_cells (hw_parent (me))
663           + hw_unit_nr_size_cells (hw_parent (me))
664           ) * nr_regs;
665 }
666
667 void
668 hw_add_reg_array_property (struct hw *me,
669                            const char *property,
670                            const reg_property_spec *regs,
671                            unsigned nr_regs)
672 {
673   unsigned sizeof_cells = (nr_reg_property_cells (me, nr_regs)
674                            * sizeof (unsigned_cell));
675   unsigned_cell *cells = hw_zalloc (me, sizeof_cells);
676   unsigned_cell *cell;
677   int i;
678
679   /* copy the property elements over */
680   cell = cells;
681   for (i = 0; i < nr_regs; i++)
682     {
683       const reg_property_spec *reg = &regs[i];
684       /* copy the address */
685       cell = unit_address_to_cells (&reg->address, cell,
686                                     hw_unit_nr_address_cells (hw_parent (me)));
687       /* copy the size */
688       cell = unit_address_to_cells (&reg->size, cell,
689                                     hw_unit_nr_size_cells (hw_parent (me)));
690     }
691   ASSERT (cell == &cells[nr_reg_property_cells (me, nr_regs)]);
692
693   /* add it */
694   hw_add_property (me, property, reg_array_property,
695                    cells, sizeof_cells,
696                    cells, sizeof_cells,
697                    NULL, permenant_object);
698
699   hw_free (me, cells);
700 }
701
702 int
703 hw_find_reg_array_property (struct hw *me,
704                             const char *property,
705                             unsigned index,
706                             reg_property_spec *reg)
707 {
708   const struct hw_property *node;
709   unsigned sizeof_entry = (nr_reg_property_cells (me, 1)
710                            * sizeof (unsigned_cell));
711   const unsigned_cell *cells;
712
713   /* locate the property */
714   node = hw_find_property (me, property);
715   if (node == NULL)
716     hw_abort (me, "property \"%s\" not found", property);
717   if (node->type != reg_array_property)
718     hw_abort (me, "property \"%s\" of wrong type (reg array)", property);
719
720   /* aligned ? */
721   if ((node->sizeof_array % sizeof_entry) != 0)
722     hw_abort (me, "property \"%s\" contains an incomplete number of entries",
723               property);
724
725   /* within bounds? */
726   if (node->sizeof_array < sizeof_entry * (index + 1))
727     return 0;
728
729   /* find the range of interest */
730   cells = (unsigned_cell*)((char*)node->array + sizeof_entry * index);
731
732   /* copy the address out - converting as we go */
733   cells = cells_to_unit_address (cells, &reg->address,
734                                  hw_unit_nr_address_cells (hw_parent (me)));
735
736   /* copy the size out - converting as we go */
737   cells = cells_to_unit_address (cells, &reg->size,
738                                  hw_unit_nr_size_cells (hw_parent (me)));
739
740   return node->sizeof_array / sizeof_entry;
741 }
742
743
744 void
745 hw_add_string_property (struct hw *me,
746                         const char *property,
747                         const char *string)
748 {
749   hw_add_property (me, property, string_property,
750                    string, strlen (string) + 1,
751                    string, strlen (string) + 1,
752                    NULL, permenant_object);
753 }
754
755 const char *
756 hw_find_string_property (struct hw *me,
757                          const char *property)
758 {
759   const struct hw_property *node;
760   const char *string;
761   node = hw_find_property (me, property);
762   if (node == NULL)
763     hw_abort (me, "property \"%s\" not found", property);
764   if (node->type != string_property)
765     hw_abort (me, "property \"%s\" of wrong type (string)", property);
766   string = node->array;
767   ASSERT (strlen (string) + 1 == node->sizeof_array);
768   return string;
769 }
770
771 void
772 hw_add_string_array_property (struct hw *me,
773                               const char *property,
774                               const string_property_spec *strings,
775                               unsigned nr_strings)
776 {
777   int sizeof_array;
778   int string_nr;
779   char *array;
780   char *chp;
781   if (nr_strings == 0)
782     hw_abort (me, "property \"%s\" must be non-null", property);
783   /* total up the size of the needed array */
784   for (sizeof_array = 0, string_nr = 0;
785        string_nr < nr_strings;
786        string_nr ++)
787     {
788       sizeof_array += strlen (strings[string_nr]) + 1;
789     }
790   /* create the array */
791   array = (char*) hw_zalloc (me, sizeof_array);
792   chp = array;
793   for (string_nr = 0;
794        string_nr < nr_strings;
795        string_nr++)
796     {
797       strcpy (chp, strings[string_nr]);
798       chp += strlen (chp) + 1;
799     }
800   ASSERT (chp == array + sizeof_array);
801   /* now enter it */
802   hw_add_property (me, property, string_array_property,
803                    array, sizeof_array,
804                    array, sizeof_array,
805                    NULL, permenant_object);
806 }
807
808 int
809 hw_find_string_array_property (struct hw *me,
810                                const char *property,
811                                unsigned index,
812                                string_property_spec *string)
813 {
814   const struct hw_property *node;
815   node = hw_find_property (me, property);
816   if (node == NULL)
817     hw_abort (me, "property \"%s\" not found", property);
818   switch (node->type)
819     {
820     default:
821       hw_abort (me, "property \"%s\" of wrong type", property);
822       break;
823     case string_property:
824       if (index == 0)
825         {
826           *string = node->array;
827           ASSERT (strlen (*string) + 1 == node->sizeof_array);
828           return 1;
829         }
830       break;
831     case array_property:
832       if (node->sizeof_array == 0
833           || ((char*)node->array)[node->sizeof_array - 1] != '\0')
834         hw_abort (me, "property \"%s\" invalid for string array", property);
835       /* FALL THROUGH */
836     case string_array_property:
837       ASSERT (node->sizeof_array > 0);
838       ASSERT (((char*)node->array)[node->sizeof_array - 1] == '\0');
839       {
840         const char *chp = node->array;
841         int nr_entries = 0;
842         /* count the number of strings, keeping an eye out for the one
843            we're looking for */
844         *string = chp;
845         do
846           {
847             if (*chp == '\0')
848               {
849                 /* next string */
850                 nr_entries++;
851                 chp++;
852                 if (nr_entries == index)
853                   *string = chp;
854               }
855             else
856               {
857                 chp++;
858               }
859           } while (chp < (char*)node->array + node->sizeof_array);
860         if (index < nr_entries)
861           return nr_entries;
862         else
863           {
864             *string = NULL;
865             return 0;
866           }
867       }
868       break;
869     }
870   return 0;
871 }
872
873 void
874 hw_add_duplicate_property (struct hw *me,
875                            const char *property,
876                            const struct hw_property *original)
877 {
878   struct hw_property_data *master;
879   if (original->disposition != permenant_object)
880     hw_abort (me, "Can only duplicate permenant objects");
881   /* find the original's master */
882   master = original->owner->properties_of_hw;
883   while (master->property != original)
884     {
885       master = master->next;
886       ASSERT (master != NULL);
887     }
888   /* now duplicate it */
889   hw_add_property (me, property,
890                    original->type,
891                    master->init_array, master->sizeof_init_array,
892                    original->array, original->sizeof_array,
893                    original, permenant_object);
894 }