#ifdef __cplusplus extern "C" { #endif #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #ifdef __cplusplus } #endif #ifndef PERL_VERSION #include "patchlevel.h" #define PERL_REVISION 5 #define PERL_VERSION PATCHLEVEL #define PERL_SUBVERSION SUBVERSION #endif #if PERL_REVISION == 5 && (PERL_VERSION < 4 || (PERL_VERSION == 4 && PERL_SUBVERSION <= 75 )) #define PL_stack_sp stack_sp #endif static void process_flag _((char *varname, SV **svp, char **strp, STRLEN *lenp)); static void process_flag(varname, svp, strp, lenp) char *varname; SV **svp; char **strp; STRLEN *lenp; { GV *vargv = gv_fetchpv(varname, FALSE, SVt_PV); SV *sv = Nullsv; char *str = Nullch; STRLEN len = 0; if (vargv && (sv = GvSV(vargv))) { if (SvROK(sv)) { if (SvTYPE(SvRV(sv)) != SVt_PVCV) croak("$%s not a subroutine reference", varname); } else if (SvOK(sv)) str = SvPV(sv, len); } *svp = sv; *strp = str; *lenp = len; } MODULE = Alias PACKAGE = Alias PREFIX = alias_ PROTOTYPES: ENABLE BOOT: { GV *gv = gv_fetchpv("Alias::attr", FALSE, SVt_PVCV); if (gv && GvCV(gv)) CvNODEBUG_on(GvCV(gv)); } void alias_attr(hashref) SV * hashref PROTOTYPE: $ PPCODE: { HV *hv; int in_destroy = 0; int deref_call; if (SvREFCNT(hashref) == 0) in_destroy = 1; ++SvREFCNT(hashref); /* in case LEAVE wants to clobber us */ if (SvROK(hashref) && (hv = (HV *)SvRV(hashref)) && (SvTYPE(hv) == SVt_PVHV)) { SV *val, *tmpsv; char *key; I32 klen; SV *keypfx, *attrpfx, *deref; char *keypfx_c, *attrpfx_c, *deref_c; STRLEN keypfx_l, attrpfx_l, deref_l; process_flag("Alias::KeyFilter", &keypfx, &keypfx_c, &keypfx_l); process_flag("Alias::AttrPrefix", &attrpfx, &attrpfx_c, &attrpfx_l); process_flag("Alias::Deref", &deref, &deref_c, &deref_l); deref_call = (deref && !deref_c); LEAVE; /* operate at a higher level */ (void)hv_iterinit(hv); while ((val = hv_iternextsv(hv, &key, &klen))) { GV *gv; int stype = SvTYPE(val); int deref_this = 1; int deref_objects = 0; /* check the key for validity by either looking at * its prefix, or by calling &$Alias::KeyFilter */ if (keypfx) { if (keypfx_c) { if (keypfx_l && klen > keypfx_l && strncmp(key, keypfx_c, keypfx_l)) continue; } else { dSP; SV *ret = Nullsv; I32 i; ENTER; SAVETMPS; PUSHMARK(sp); XPUSHs(sv_2mortal(newSVpv(key,klen))); PUTBACK; if (perl_call_sv(keypfx, G_SCALAR)) ret = *PL_stack_sp--; SPAGAIN; i = SvTRUE(ret); FREETMPS; LEAVE; if (!i) continue; } } if (SvROK(val) && deref) { if (deref_c) { if (deref_l && !(deref_l == 1 && *deref_c == '0')) deref_objects = 1; } else { dSP; SV *ret = Nullsv; ENTER; SAVETMPS; PUSHMARK(sp); XPUSHs(sv_2mortal(newSVpv(key,klen))); XPUSHs(sv_2mortal(newSVsv(val))); PUTBACK; if (perl_call_sv(deref, G_SCALAR)) ret = *PL_stack_sp--; SPAGAIN; deref_this = SvTRUE(ret); FREETMPS; LEAVE; } } /* attributes may need to be prefixed/renamed */ if (attrpfx) { STRLEN len; if (attrpfx_c) { if (attrpfx_l) { SV *keysv = sv_2mortal(newSVpv(attrpfx_c, attrpfx_l)); sv_catpvn(keysv, key, klen); key = SvPV(keysv, len); klen = len; } } else { dSP; SV *ret = Nullsv; ENTER; PUSHMARK(sp); XPUSHs(sv_2mortal(newSVpv(key,klen))); PUTBACK; if (perl_call_sv(attrpfx, G_SCALAR)) ret = *PL_stack_sp--; SPAGAIN; LEAVE; key = SvPV(ret, len); klen = len; } } if (SvROK(val) && (tmpsv = SvRV(val))) { if (deref_call) { if (!deref_this) goto no_deref; } else if (!deref_objects && SvOBJECT(tmpsv)) goto no_deref; stype = SvTYPE(tmpsv); if (stype == SVt_PVGV) val = tmpsv; } else if (stype != SVt_PVGV) { no_deref: val = sv_2mortal(newRV(val)); } /* add symbol, forgoing "used once" warnings */ gv = gv_fetchpv(key, GV_ADDMULTI, SVt_PVGV); switch (stype) { case SVt_PVAV: save_ary(gv); break; case SVt_PVHV: save_hash(gv); break; case SVt_PVGV: save_gp(gv,TRUE); /* hide previous entry in symtab */ break; case SVt_PVCV: SAVESPTR(GvCV(gv)); GvCV(gv) = Null(CV*); break; default: save_scalar(gv); break; } sv_setsv((SV*)gv, val); /* alias the SV */ } ENTER; /* in lieu of the LEAVE far beyond */ } if (in_destroy) --SvREFCNT(hashref); /* avoid calling DESTROY forever */ else SvREFCNT_dec(hashref); XPUSHs(hashref); /* simply return what we got */ }