1 /*
2 * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved.
3 *
4 * Object.xs contains XS code for exacct file manipulation.
5 */
6
7 #include <strings.h>
8 #include "../exacct_common.xh"
9
10 /* Pull in the file generated by extract_defines. */
11 #include "ObjectDefs.xi"
12
13 /* From Catalog.xs. */
14 extern char *catalog_id_str(ea_catalog_t catalog);
15
16 /*
17 * Copy an xs_ea_object_t. If the perl_obj part is null, we just copy the
18 * ea_object_t part. If the perl_obj part is not null and the Object is an
19 * Item it must be because the Item contains an embedded Object, which will be
20 * recursively copied. Otherwise the Object must be a Group, so the Group will
21 * be copied, and the list of Objects it contains will be recursively copied.
22 */
23 static SV *
copy_xs_ea_object(SV * src_sv)24 copy_xs_ea_object(SV *src_sv)
25 {
26 xs_ea_object_t *src, *dst;
27 SV *dst_sv, *dst_rv;
28
29 /* Get the source xs_ea_object_t and make a new one. */
30 PERL_ASSERT(src_sv != NULL);
31 src_sv = SvRV(src_sv);
32 PERL_ASSERT(src_sv != NULL);
33 src = INT2PTR(xs_ea_object_t *, SvIV(src_sv));
34 PERL_ASSERT(src != NULL);
35 New(0, dst, 1, xs_ea_object_t);
36 dst->flags = src->flags;
37
38 /* If the Object is a plain Item only the ea_obj part needs copying. */
39 if (IS_PLAIN_ITEM(src)) {
40 dst->ea_obj = ea_copy_object_tree(src->ea_obj);
41 PERL_ASSERT(dst->ea_obj != NULL);
42 dst->perl_obj = NULL;
43
44 /*
45 * Otherwise if it is an Item with a perl_obj part, it means that it
46 * must be an Item containing an unpacked nested Object. In this case
47 * the nested Object can be copied by a recursive call.
48 */
49 } else if (IS_EMBED_ITEM(src)) {
50 dst->ea_obj = ea_copy_object(src->ea_obj);
51 PERL_ASSERT(dst->ea_obj != NULL);
52 dst->perl_obj = copy_xs_ea_object(src->perl_obj);
53
54 /*
55 * If we get here it must be a Group, so perl_obj will point to a tied
56 * AV. We therefore copy the exacct part then create a new tied array
57 * and recursively copy each Item individually.
58 */
59 } else {
60 MAGIC *mg;
61 AV *src_av, *dst_av, *tied_av;
62 SV *sv;
63 int i, len;
64
65 /* Copy the exacct part of the Group. */
66 dst->ea_obj = ea_copy_object(src->ea_obj);
67 PERL_ASSERT(dst->ea_obj != NULL);
68
69 /* Find the AV underlying the tie. */
70 mg = mg_find(SvRV(src->perl_obj), 'P');
71 PERL_ASSERT(mg != NULL);
72 src_av = (AV *)SvRV(mg->mg_obj);
73 PERL_ASSERT(src_av != NULL);
74
75 /* Create a new AV and copy across into it. */
76 dst_av = newAV();
77 len = av_len(src_av) + 1;
78 av_extend(dst_av, len);
79 for (i = 0; i < len; i++) {
80 SV **svp;
81
82 /* undef elements don't need copying. */
83 if ((svp = av_fetch(src_av, i, FALSE)) != NULL) {
84 sv = copy_xs_ea_object(*svp);
85 if (av_store(dst_av, i, sv) == NULL) {
86 SvREFCNT_dec(sv);
87 }
88 }
89 }
90
91 /* Create a new AV and tie the filled AV to it. */
92 sv = newRV_noinc((SV *)dst_av);
93 sv_bless(sv, Sun_Solaris_Exacct_Object__Array_stash);
94 tied_av = newAV();
95 sv_magic((SV *)tied_av, sv, 'P', Nullch, 0);
96 SvREFCNT_dec(sv);
97 dst->perl_obj = newRV_noinc((SV *)tied_av);
98 }
99
100 /* Wrap the new xs_ea_object_t in a blessed RV and return it. */
101 dst_sv = newSViv(PTR2IV(dst));
102 dst_rv = newRV_noinc(dst_sv);
103 sv_bless(dst_rv, SvSTASH(src_sv));
104 SvREADONLY_on(dst_sv);
105 return (dst_rv);
106 }
107
108 /*
109 * If an ea_xs_object_t only has the ea_obj part populated, create the
110 * corresponding perl_obj part. For plain Items this is a no-op. If the
111 * object is embedded, the embedded part will be unpacked and stored in the
112 * perl part. If the object is a Group, the linked list of Items will be
113 * wrapped in the corresponding perl structure and stored in a tied perl array.
114 */
115 static int
inflate_xs_ea_object(xs_ea_object_t * xs_obj)116 inflate_xs_ea_object(xs_ea_object_t *xs_obj)
117 {
118 ea_object_t *ea_obj;
119
120 /* Check there is not already a perl_obj part. */
121 PERL_ASSERT(xs_obj != NULL);
122 PERL_ASSERT(xs_obj->perl_obj == NULL);
123
124 /* Deal with Items containing embedded Objects. */
125 if (IS_EMBED_ITEM(xs_obj)) {
126 /* unpack & wrap in an xs_ea_object_t. */
127 if (ea_unpack_object(&ea_obj, EUP_ALLOC,
128 xs_obj->ea_obj->eo_item.ei_object,
129 xs_obj->ea_obj->eo_item.ei_size) == -1) {
130 return (0);
131 }
132 xs_obj->perl_obj = new_xs_ea_object(ea_obj);
133
134 /* Deal with Groups. */
135 } else if (IS_GROUP(xs_obj)) {
136 int i, len;
137 AV *av, *tied_av;
138 SV *rv, *sv;
139
140 /* Create a new array. */
141 av = newAV();
142 ea_obj = xs_obj->ea_obj;
143 len = ea_obj->eo_group.eg_nobjs;
144 ea_obj = ea_obj->eo_group.eg_objs;
145
146 /* Copy each object from the old array into the new array. */
147 for (i = 0; i < len; i++) {
148 rv = new_xs_ea_object(ea_obj);
149 if (av_store(av, i, rv) == NULL) {
150 SvREFCNT_dec(rv);
151 }
152 ea_obj = ea_obj->eo_next;
153 }
154
155 /* Create a new AV and tie the filled AV to it. */
156 rv = newRV_noinc((SV *)av);
157 sv_bless(rv, Sun_Solaris_Exacct_Object__Array_stash);
158 tied_av = newAV();
159 sv_magic((SV *)tied_av, rv, 'P', Nullch, 0);
160 SvREFCNT_dec(rv);
161 xs_obj->perl_obj = newRV_noinc((SV *)tied_av);
162 }
163 return (1);
164 }
165
166 /*
167 * The XS code exported to perl is below here. Note that the XS preprocessor
168 * has its own commenting syntax, so all comments from this point on are in
169 * that form.
170 */
171
172 MODULE = Sun::Solaris::Exacct::Object PACKAGE = Sun::Solaris::Exacct::Object
173 PROTOTYPES: ENABLE
174
175 #
176 # Define the stash pointers if required and create and populate @_Constants.
177 #
178 BOOT:
179 {
180 init_stashes();
181 define_constants(PKGBASE "::Object", constants);
182 }
183
184 #
185 # Return a dual-typed SV containing the type of the object.
186 #
187 SV *
188 type(self)
189 xs_ea_object_t *self;
190 CODE:
191 RETVAL = newSViv(self->ea_obj->eo_type);
192 switch (self->ea_obj->eo_type) {
193 case EO_ITEM:
194 sv_setpv(RETVAL, "EO_ITEM");
195 break;
196 case EO_GROUP:
197 sv_setpv(RETVAL, "EO_GROUP");
198 break;
199 case EO_NONE:
200 default:
201 sv_setpv(RETVAL, "EO_NONE");
202 break;
203 }
204 SvIOK_on(RETVAL);
205 OUTPUT:
206 RETVAL
207
208 #
209 # Return a copy of the catalog of the object.
210 #
211 SV *
212 catalog(self)
213 xs_ea_object_t *self;
214 CODE:
215 RETVAL = new_catalog(self->ea_obj->eo_catalog);
216 OUTPUT:
217 RETVAL
218
219 #
220 # Return the value of the object. For simple Items, a SV containing the value
221 # of the underlying exacct ea_item_t is returned. For nested Items or Groups,
222 # a reference to the nested Item or Group is returned. For Groups, in a scalar
223 # context a reference to the tied array used to store the objects in the Group
224 # is returned; in a list context the objects within the Group are returned on
225 # the perl stack as a list.
226 #
227 void
value(self)228 value(self)
229 xs_ea_object_t *self;
230 PPCODE:
231 /*
232 * For Items, return the perl representation
233 * of the underlying ea_object_t.
234 */
235 if (IS_ITEM(self)) {
236 SV *retval;
237
238 switch (self->ea_obj->eo_catalog & EXT_TYPE_MASK) {
239 case EXT_UINT8:
240 retval = newSVuv(self->ea_obj->eo_item.ei_uint8);
241 break;
242 case EXT_UINT16:
243 retval = newSVuv(self->ea_obj->eo_item.ei_uint16);
244 break;
245 case EXT_UINT32:
246 retval = newSVuv(self->ea_obj->eo_item.ei_uint32);
247 break;
248 case EXT_UINT64:
249 retval = newSVuv(self->ea_obj->eo_item.ei_uint64);
250 break;
251 case EXT_DOUBLE:
252 retval = newSVnv(self->ea_obj->eo_item.ei_double);
253 break;
254 case EXT_STRING:
255 retval = newSVpvn(self->ea_obj->eo_item.ei_string,
256 self->ea_obj->eo_item.ei_size - 1);
257 break;
258 case EXT_RAW:
259 retval = newSVpvn(self->ea_obj->eo_item.ei_raw,
260 self->ea_obj->eo_item.ei_size);
261 break;
262 /*
263 * For embedded objects and Groups, return a ref to the perl SV.
264 */
265 case EXT_EXACCT_OBJECT:
266 if (self->perl_obj == NULL) {
267 /* Make sure the object is inflated. */
268 if (! inflate_xs_ea_object(self)) {
269 XSRETURN_UNDEF;
270 }
271 }
272 retval = SvREFCNT_inc(self->perl_obj);
273 break;
274 case EXT_GROUP:
275 retval = SvREFCNT_inc(self->perl_obj);
276 break;
277 case EXT_NONE:
278 default:
279 croak("Invalid object type");
280 break;
281 }
282 EXTEND(SP, 1);
283 PUSHs(sv_2mortal(retval));
284
285 /*
286 * Now we deal with Groups.
287 */
288 } else {
289 /* Make sure the object is inflated. */
290 if (self->perl_obj == NULL) {
291 if (! inflate_xs_ea_object(self)) {
292 XSRETURN_UNDEF;
293 }
294 }
295
296 /* In a list context return the contents of the AV. */
297 if (GIMME_V == G_ARRAY) {
298 MAGIC *mg;
299 AV *av;
300 int len, i;
301
302 /* Find the AV underlying the tie. */
303 mg = mg_find(SvRV(self->perl_obj), 'P');
304 PERL_ASSERT(mg != NULL);
305 av = (AV *)SvRV(mg->mg_obj);
306 PERL_ASSERT(av != NULL);
307
308 /*
309 * Push the contents of the array onto the stack.
310 * Push undef for any empty array slots.
311 */
312 len = av_len(av) + 1;
313 EXTEND(SP, len);
314 for (i = 0; i < len; i++) {
315 SV **svp;
316
317 if ((svp = av_fetch(av, i, FALSE)) == NULL) {
318 PUSHs(&PL_sv_undef);
319 } else {
320 PERL_ASSERT(*svp != NULL);
321 PUSHs(sv_2mortal(SvREFCNT_inc(*svp)));
322 }
323 }
324
325 /* In a scalar context, return a ref to the array of Items. */
326 } else {
327 EXTEND(SP, 1);
328 PUSHs(sv_2mortal(SvREFCNT_inc(self->perl_obj)));
329 }
330 }
331
332 #
333 # Call the ea_match_catalog function.
334 #
335 int
336 match_catalog(self, catalog)
337 xs_ea_object_t *self;
338 SV *catalog;
339 CODE:
340 RETVAL = ea_match_object_catalog(self->ea_obj, catalog_value(catalog));
341 OUTPUT:
342 RETVAL
343
344 #
345 # Destroy an Object.
346 #
347 void
348 DESTROY(self)
349 xs_ea_object_t *self;
350 PREINIT:
351 ea_object_t *ea_obj;
352 SV *perl_obj;
353 CODE:
354 /*
355 * Because both libexacct and perl know about the ea_object_t, we have
356 * to make sure that they don't both end up freeing the object. First
357 * we break any link to the next ea_object_t in the chain. Next, if
358 * the object is a Group and there is an active perl_obj chain, we will
359 * let perl clean up the Objects, so we zero the eo_group chain.
360 */
361 perl_obj = self->perl_obj;
362 ea_obj = self->ea_obj;
363 ea_obj->eo_next = NULL;
364 if (IS_GROUP(self) && perl_obj != NULL) {
365 ea_obj->eo_group.eg_nobjs = 0;
366 ea_obj->eo_group.eg_objs = NULL;
367 }
368 ea_free_object(ea_obj, EUP_ALLOC);
369 if (perl_obj != NULL) {
370 SvREFCNT_dec(perl_obj);
371 }
372 Safefree(self);
373
374 MODULE = Sun::Solaris::Exacct::Object PACKAGE = Sun::Solaris::Exacct::Object::Item
375 PROTOTYPES: ENABLE
376
377 #
378 # Create a new Item.
379 #
380 xs_ea_object_t *
381 new(class, catalog, value)
382 char *class;
383 SV *catalog;
384 SV *value;
385 PREINIT:
386 ea_object_t *ea_obj;
387 HV *stash;
388 CODE:
389 /* Create a new xs_ea_object_t and subsiduary structures. */
390 New(0, RETVAL, 1, xs_ea_object_t);
391 RETVAL->ea_obj = ea_obj = ea_alloc(sizeof (ea_object_t));
392 bzero(ea_obj, sizeof (*ea_obj));
393 ea_obj->eo_type = EO_ITEM;
394 ea_obj->eo_catalog = catalog_value(catalog);
395 INIT_PLAIN_ITEM_FLAGS(RETVAL);
396 RETVAL->perl_obj = NULL;
397
398 /* Assign the Item's value. */
399 switch (ea_obj->eo_catalog & EXT_TYPE_MASK) {
400 case EXT_UINT8:
401 ea_obj->eo_item.ei_uint8 = SvIV(value);
402 ea_obj->eo_item.ei_size = sizeof (uint8_t);
403 break;
404 case EXT_UINT16:
405 ea_obj->eo_item.ei_uint16 = SvIV(value);
406 ea_obj->eo_item.ei_size = sizeof (uint16_t);
407 break;
408 case EXT_UINT32:
409 ea_obj->eo_item.ei_uint32 = SvIV(value);
410 ea_obj->eo_item.ei_size = sizeof (uint32_t);
411 break;
412 case EXT_UINT64:
413 ea_obj->eo_item.ei_uint64 = SvIV(value);
414 ea_obj->eo_item.ei_size = sizeof (uint64_t);
415 break;
416 case EXT_DOUBLE:
417 ea_obj->eo_item.ei_double = SvNV(value);
418 ea_obj->eo_item.ei_size = sizeof (double);
419 break;
420 case EXT_STRING:
421 ea_obj->eo_item.ei_string = ea_strdup(SvPV_nolen(value));
422 ea_obj->eo_item.ei_size = SvCUR(value) + 1;
423 break;
424 case EXT_RAW:
425 ea_obj->eo_item.ei_size = SvCUR(value);
426 ea_obj->eo_item.ei_raw = ea_alloc(ea_obj->eo_item.ei_size);
427 bcopy(SvPV_nolen(value), ea_obj->eo_item.ei_raw,
428 (size_t)ea_obj->eo_item.ei_size);
429 break;
430 case EXT_EXACCT_OBJECT:
431 /*
432 * The ea_obj part is initially empty, and will be populated
433 * from the perl_obj part when required.
434 */
435 stash = SvROK(value) ? SvSTASH(SvRV(value)) : NULL;
436 if (stash != Sun_Solaris_Exacct_Object_Item_stash &&
437 stash != Sun_Solaris_Exacct_Object_Group_stash) {
438 croak("value is not of type " PKGBASE "::Object");
439 }
440 RETVAL->perl_obj = copy_xs_ea_object(value);
441 ea_obj->eo_item.ei_object = NULL;
442 ea_obj->eo_item.ei_size = 0;
443 INIT_EMBED_ITEM_FLAGS(RETVAL);
444 break;
445 /*
446 * EXT_NONE is an invalid type,
447 * EXT_GROUP is created by the Group subclass constructor.
448 */
449 case EXT_NONE:
450 case EXT_GROUP:
451 default:
452 ea_free(RETVAL->ea_obj, sizeof (RETVAL->ea_obj));
453 Safefree(RETVAL);
454 croak("Invalid object type");
455 break;
456 }
457 OUTPUT:
458 RETVAL
459
460 MODULE = Sun::Solaris::Exacct::Object PACKAGE = Sun::Solaris::Exacct::Object::Group
461 PROTOTYPES: ENABLE
462
463 xs_ea_object_t *
464 new(class, catalog, ...)
465 char *class;
466 SV *catalog;
467 PREINIT:
468 ea_catalog_t tag;
469 ea_object_t *ea_obj;
470 AV *tied_av, *av;
471 SV *sv, *rv;
472 int i;
473 CODE:
474 tag = catalog_value(catalog);
475 if ((tag & EXT_TYPE_MASK) != EXT_GROUP) {
476 croak("Invalid object type");
477 }
478
479 /* Create a new xs_ea_object_t and subsiduary structures. */
480 New(0, RETVAL, 1, xs_ea_object_t);
481 RETVAL->ea_obj = ea_obj = ea_alloc(sizeof (ea_object_t));
482 bzero(ea_obj, sizeof (*ea_obj));
483 ea_obj->eo_type = EO_GROUP;
484 ea_obj->eo_catalog = tag;
485 INIT_GROUP_FLAGS(RETVAL);
486 RETVAL->perl_obj = NULL;
487
488 /* Create a new AV and copy in all the passed Items. */
489 av = newAV();
490 av_extend(av, items - 2);
491 for (i = 2; i < items; i++) {
492 HV *stash;
493 stash = SvROK(ST(i)) ? SvSTASH(SvRV(ST(i))) : NULL;
494 if (stash != Sun_Solaris_Exacct_Object_Item_stash &&
495 stash != Sun_Solaris_Exacct_Object_Group_stash) {
496 croak("item is not of type " PKGBASE "::Object");
497 }
498 av_store(av, i - 2, copy_xs_ea_object(ST(i)));
499 }
500
501 /* Bless the copied AV and tie it to a new AV */
502 rv = newRV_noinc((SV *)av);
503 sv_bless(rv, Sun_Solaris_Exacct_Object__Array_stash);
504 tied_av = newAV();
505 sv_magic((SV *)tied_av, rv, 'P', Nullch, 0);
506 SvREFCNT_dec(rv);
507 RETVAL->perl_obj = newRV_noinc((SV *)tied_av);
508 OUTPUT:
509 RETVAL
510
511 #
512 # Return the contents of the group as a hashref, using the string value of each
513 # item's catalog id as the key. There are two forms - as_hash() which stores
514 # each hash value as a scalar, and should be used when it is known the group
515 # does not contain duplicate catalog tags, and as_hashlist wich stores each
516 # hash value as an array of values, and can therefore be used when the group
517 # may contain duplicate catalog tags.
518 #
519
520 SV *
521 as_hash(self)
522 xs_ea_object_t *self;
523 ALIAS:
524 as_hashlist = 1
525 PREINIT:
526 MAGIC *mg;
527 HV *hv;
528 AV *av;
529 int len, i;
530 CODE:
531 /* Make sure the object is inflated. */
532 if (self->perl_obj == NULL) {
533 if (! inflate_xs_ea_object(self)) {
534 XSRETURN_UNDEF;
535 }
536 }
537
538 /* Find the AV underlying the tie and create the new HV. */
539 mg = mg_find(SvRV(self->perl_obj), 'P');
540 PERL_ASSERT(mg != NULL);
541 av = (AV *)SvRV(mg->mg_obj);
542 PERL_ASSERT(av != NULL);
543 hv = newHV();
544
545 /*
546 * Traverse the value array, saving the values in the hash,
547 * keyed by the string value of the catalog id field.
548 */
549 len = av_len(av) + 1;
550 for (i = 0; i < len; i++) {
551 SV **svp, *val;
552 xs_ea_object_t *xs_obj;
553 const char *key;
554
555 /* Ignore undef values. */
556 if ((svp = av_fetch(av, i, FALSE)) == NULL) {
557 continue;
558 }
559 PERL_ASSERT(*svp != NULL);
560
561 /* Figure out the key. */
562 xs_obj = INT2PTR(xs_ea_object_t *, SvIV(SvRV(*svp)));
563 key = catalog_id_str(xs_obj->ea_obj->eo_catalog);
564
565 /*
566 * For Items, save the perl representation
567 * of the underlying ea_object_t.
568 */
569 if (IS_ITEM(xs_obj)) {
570 switch (xs_obj->ea_obj->eo_catalog & EXT_TYPE_MASK) {
571 case EXT_UINT8:
572 val =
573 newSVuv(xs_obj->ea_obj->eo_item.ei_uint8);
574 break;
575 case EXT_UINT16:
576 val =
577 newSVuv(xs_obj->ea_obj->eo_item.ei_uint16);
578 break;
579 case EXT_UINT32:
580 val =
581 newSVuv(xs_obj->ea_obj->eo_item.ei_uint32);
582 break;
583 case EXT_UINT64:
584 val =
585 newSVuv(xs_obj->ea_obj->eo_item.ei_uint64);
586 break;
587 case EXT_DOUBLE:
588 val =
589 newSVnv(xs_obj->ea_obj->eo_item.ei_double);
590 break;
591 case EXT_STRING:
592 val =
593 newSVpvn(xs_obj->ea_obj->eo_item.ei_string,
594 xs_obj->ea_obj->eo_item.ei_size - 1);
595 break;
596 case EXT_RAW:
597 val =
598 newSVpvn(xs_obj->ea_obj->eo_item.ei_raw,
599 xs_obj->ea_obj->eo_item.ei_size);
600 break;
601 /*
602 * For embedded objects and Groups, return a ref
603 * to the perl SV.
604 */
605 case EXT_EXACCT_OBJECT:
606 if (xs_obj->perl_obj == NULL) {
607 /* Make sure the object is inflated. */
608 if (! inflate_xs_ea_object(xs_obj)) {
609 SvREFCNT_dec(hv);
610 XSRETURN_UNDEF;
611 }
612 }
613 val = SvREFCNT_inc(xs_obj->perl_obj);
614 break;
615 case EXT_GROUP:
616 val = SvREFCNT_inc(xs_obj->perl_obj);
617 break;
618 case EXT_NONE:
619 default:
620 croak("Invalid object type");
621 break;
622 }
623 /*
624 * Now we deal with Groups.
625 */
626 } else {
627 /* Make sure the object is inflated. */
628 if (xs_obj->perl_obj == NULL) {
629 if (! inflate_xs_ea_object(xs_obj)) {
630 SvREFCNT_dec(hv);
631 XSRETURN_UNDEF;
632 }
633 }
634 val = SvREFCNT_inc(xs_obj->perl_obj);
635 }
636
637 /*
638 * If called as as_hash(), store the value directly in the
639 * hash, if called as as_hashlist(), store the value in an
640 * array within the hash.
641 */
642 if (ix == 0) {
643 hv_store(hv, key, strlen(key), val, FALSE);
644 } else {
645 AV *ary;
646
647 /* If the key already exists in the hash. */
648 svp = hv_fetch(hv, key, strlen(key), TRUE);
649 if (SvOK(*svp)) {
650 ary = (AV *)SvRV(*svp);
651
652 /* Otherwise, add a new array to the hash. */
653 } else {
654 SV *rv;
655 ary = newAV();
656 rv = newRV_noinc((SV *)ary);
657 sv_setsv(*svp, rv);
658 SvREFCNT_dec(rv);
659 }
660 av_push(ary, val);
661 }
662 }
663 RETVAL = newRV_noinc((SV *)hv);
664 OUTPUT:
665 RETVAL
666
667 MODULE = Sun::Solaris::Exacct::Object PACKAGE = Sun::Solaris::Exacct::Object::_Array
668 PROTOTYPES: ENABLE
669
670 #
671 # Copy the passed list of xs_ea_object_t.
672 #
673 void
674 copy_xs_ea_objects(...)
675 PREINIT:
676 int i;
677 PPCODE:
678 EXTEND(SP, items);
679 for (i = 0; i < items; i++) {
680 HV *stash;
681 stash = SvROK(ST(i)) ? SvSTASH(SvRV(ST(i))) : NULL;
682 if (stash != Sun_Solaris_Exacct_Object_Item_stash &&
683 stash != Sun_Solaris_Exacct_Object_Group_stash) {
684 croak("item is not of type " PKGBASE "::Object");
685 }
686 PUSHs(sv_2mortal(copy_xs_ea_object(ST(i))));
687 }
688