The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
#include "EXTERN.h"
#define PERL_IN_HV_C
#define PERL_HASH_INTERNAL_ACCESS
#include "perl.h"
#include "XSUB.h"
#include "parser.h"
#include "stdio.h"
#include "string.h"

#ifndef newSVpvn			/* 5.005_62 or so */
  #define newSVpvn newSVpv
#endif

struct parserc parser;
struct nodec *root;

U32 vhash;
U32 chash;

struct nodec *curnode;
  

SV *cxml2obj(pTHX_ int a) {
  HV *output = newHV();
  SV *outputref = newRV( (SV *) output );
  
  int length = curnode->numchildren;
  if( !length ) {
    if( curnode->vallen ) {
      SV * sv = newSVpvn( curnode->value, curnode->vallen );
      hv_store( output, "value", 5, sv, vhash );
    }
    if( curnode->comlen ) {
      SV * sv = newSVpvn( curnode->comment, curnode->comlen );
      hv_store( output, "comment", 7, sv, chash );
    }
  }
  else {
    if( curnode->vallen ) {
      SV *sv = newSVpvn( curnode->value, curnode->vallen );
      hv_store( output, "value", 5, sv, vhash );
    }
    if( curnode->comlen ) {
      SV *sv = newSVpvn( curnode->comment, curnode->comlen );
      hv_store( output, "comment", 7, sv, chash );
    }
    
    curnode = curnode->firstchild;
    int i;
    for( i = 0; i < length; i++ ) {
      SV *namesv = newSVpvn( curnode->name, curnode->namelen );
      
      SV **cur = hv_fetch( output, curnode->name, curnode->namelen, 0 );
      
      if( curnode->namelen > 6 ) {
        if( !strncmp( curnode->name, "multi_", 6 ) ) {
          char *subname = &curnode->name[6];
          int subnamelen = curnode->namelen-6;
          SV **old = hv_fetch( output, subname, subnamelen, 0 );
          AV *newarray = newAV();
          SV *newarrayref = newRV( (SV *) newarray );
          if( !old ) {
            hv_store( output, subname, subnamelen, newarrayref, 0 );
          }
          else {
            if( SvTYPE( SvRV(*old) ) == SVt_PVHV ) { // check for hash ref
              SV *newref = newRV( (SV *) SvRV(*old) );
              hv_delete( output, subname, subnamelen, 0 );
              hv_store( output, subname, subnamelen, newarrayref, 0 );
              av_push( newarray, newref );
            }
          }
        }
      }
        
      if( !cur ) {
        SV *ob = cxml2obj( aTHX_ 0 );
        hv_store( output, curnode->name, curnode->namelen, ob, 0 );
      }
      else {
        if( SvTYPE( SvRV(*cur) ) == SVt_PVHV ) {
          AV *newarray = newAV();
          SV *newarrayref = newRV( (SV *) newarray );
          SV *newref = newRV( (SV *) SvRV( *cur ) );
          hv_delete( output, curnode->name, curnode->namelen, 0 );
          hv_store( output, curnode->name, curnode->namelen, newarrayref, 0 );
          av_push( newarray, newref );
          av_push( newarray, cxml2obj( aTHX_ 0 ) );
        }
        else {
          AV *av = (AV *) SvRV( *cur );
          av_push( av, cxml2obj( aTHX_ 0) );
        }
      }
      if( i != ( length - 1 ) ) curnode = curnode->next;
    }
    
    curnode = curnode->parent;
  }
  
  struct attc *curatt;
  int numatts = curnode->numatt;
  if( numatts ) {
    curatt = curnode->firstatt;
    int i;
    for( i = 0; i < numatts; i++ ) {
      HV *atth = newHV();
      SV *atthref = newRV( (SV *) atth );
      hv_store( output, curatt->name, curatt->namelen, atthref, 0 );
      
      SV *attval = newSVpvn( curatt->value, curatt->vallen );
      hv_store( atth, "value", 5, attval, vhash );
      SV *attatt = newSViv( 1 );
      hv_store( atth, "att", 3, attatt, 0 );
      if( i != ( numatts - 1 ) ) curatt = curatt->next;
    }
  }
  return outputref;
}

MODULE = XML::Bare         PACKAGE = XML::Bare

SV *
xml2obj()
  CODE:
    curnode = parser.pcurnode;
    RETVAL = cxml2obj(aTHX_ 0);
  OUTPUT:
    RETVAL
    
void
c_parse(text)
  char * text
  CODE:
    PERL_HASH(vhash, "value", 5);
    PERL_HASH(chash, "comment", 7);
    int len = strlen( text );
    parserc_parse( &parser, text, len );
    root = parser.pcurnode;
    
void
c_parsefile(filename)
  char * filename
  CODE:
    char *data;
    unsigned long len;
    FILE *handle;
    handle = fopen(filename,"r");
    
    fseek( handle, 0, SEEK_END );
    
    len = ftell( handle );
    
    fseek( handle, 0, SEEK_SET );
    data = (char *) malloc( len );
    fread( data, 1, len, handle );
    fclose( handle );
    parserc_parse( &parser, data, len );
    root = parser.pcurnode;

void
free_tree()
  CODE:
    del_nodec( root );