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 *
  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
 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
 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         }