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