| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "mod_perl.h" |
| |
| /* XXX: PL_modglobal thingers might be useful elsewhere */ |
| |
| #define MP_MODGLOBAL_ENT(key) \ |
| {key, "ModPerl::" key, MP_SSTRLEN("ModPerl::") + MP_SSTRLEN(key), 0} |
| |
| static modperl_modglobal_key_t MP_modglobal_keys[] = { |
| MP_MODGLOBAL_ENT("END"), |
| MP_MODGLOBAL_ENT("ANONSUB"), |
| { NULL }, |
| }; |
| |
| void modperl_modglobal_hash_keys(pTHX) |
| { |
| modperl_modglobal_key_t *gkey = MP_modglobal_keys; |
| |
| while (gkey->name) { |
| PERL_HASH(gkey->hash, gkey->val, gkey->len); |
| gkey++; |
| } |
| } |
| |
| modperl_modglobal_key_t *modperl_modglobal_lookup(pTHX_ const char *name) |
| { |
| modperl_modglobal_key_t *gkey = MP_modglobal_keys; |
| |
| while (gkey->name) { |
| if (strEQ(gkey->name, name)) { |
| return gkey; |
| } |
| gkey++; |
| } |
| |
| return NULL; |
| } |
| |
| static void modperl_perl_global_init(pTHX_ modperl_perl_globals_t *globals) |
| { |
| globals->env.gv = PL_envgv; |
| globals->inc.gv = PL_incgv; |
| globals->defout.gv = PL_defoutgv; |
| globals->rs.sv = &PL_rs; |
| globals->end.av = &PL_endav; |
| globals->end.key = MP_MODGLOBAL_END; |
| } |
| |
| /* |
| * if (exists $PL_modglobal{$key}{$package}) { |
| * return $PL_modglobal{$key}{$package}; |
| * } |
| * elsif ($autovivify) { |
| * return $PL_modglobal{$key}{$package} = []; |
| * } |
| * else { |
| * return (AV *)NULL; # a null pointer in C of course :) |
| * } |
| */ |
| static AV *modperl_perl_global_avcv_fetch(pTHX_ modperl_modglobal_key_t *gkey, |
| const char *package, I32 packlen, |
| I32 autovivify) |
| { |
| HE *he = MP_MODGLOBAL_FETCH(gkey); |
| HV *hv; |
| |
| if (!(he && (hv = (HV*)HeVAL(he)))) { |
| if (autovivify) { |
| hv = MP_MODGLOBAL_STORE_HV(gkey); |
| } |
| else { |
| return (AV *)NULL; |
| } |
| } |
| |
| if ((he = hv_fetch_he(hv, (char *)package, packlen, 0))) { |
| return (AV*)HeVAL(he); |
| } |
| else { |
| if (autovivify) { |
| return (AV*)*hv_store(hv, package, packlen, (SV*)newAV(), 0); |
| } |
| else { |
| return (AV *)NULL; |
| } |
| } |
| } |
| |
| /* autovivify $PL_modglobal{$key}{$package} if it doesn't exist yet, |
| * so that in modperl_perl_global_avcv_set we will know whether to |
| * store blocks in it or keep them in the original list. |
| * |
| * For example in the case of END blocks, if |
| * $PL_modglobal{END}{$package} exists, modperl_perl_global_avcv_set |
| * will push newly encountered END blocks to it, otherwise it'll keep |
| * them in PL_endav. |
| */ |
| void modperl_perl_global_avcv_register(pTHX_ modperl_modglobal_key_t *gkey, |
| const char *package, I32 packlen) |
| { |
| AV *av = modperl_perl_global_avcv_fetch(aTHX_ gkey, |
| package, packlen, TRUE); |
| |
| MP_TRACE_g(MP_FUNC, "register PL_modglobal %s::%s (has %d entries)", |
| package, (char*)gkey->name, av ? 1+av_len(av) : 0); |
| } |
| |
| /* if (exists $PL_modglobal{$key}{$package}) { |
| * for my $cv (@{ $PL_modglobal{$key}{$package} }) { |
| * $cv->(); |
| * } |
| * } |
| */ |
| void modperl_perl_global_avcv_call(pTHX_ modperl_modglobal_key_t *gkey, |
| const char *package, I32 packlen) |
| { |
| AV *av = modperl_perl_global_avcv_fetch(aTHX_ gkey, package, packlen, |
| FALSE); |
| |
| MP_TRACE_g(MP_FUNC, "run PL_modglobal %s::%s (has %d entries)", |
| package, (char*)gkey->name, av ? 1+av_len(av) : 0); |
| |
| if (av) { |
| modperl_perl_call_list(aTHX_ av, gkey->name); |
| } |
| } |
| |
| |
| /* if (exists $PL_modglobal{$key}{$package}) { |
| * @{ $PL_modglobal{$key}{$package} } = (); |
| * } |
| */ |
| void modperl_perl_global_avcv_clear(pTHX_ modperl_modglobal_key_t *gkey, |
| const char *package, I32 packlen) |
| { |
| AV *av = modperl_perl_global_avcv_fetch(aTHX_ gkey, |
| package, packlen, FALSE); |
| |
| MP_TRACE_g(MP_FUNC, "clear PL_modglobal %s::%s (has %d entries)", |
| package, (char*)gkey->name, av ? 1+av_len(av) : 0); |
| |
| if (av) { |
| av_clear(av); |
| } |
| } |
| |
| static int modperl_perl_global_avcv_set(pTHX_ SV *sv, MAGIC *mg) |
| { |
| AV *mav, *av = (AV*)sv; |
| const char *package = HvNAME(PL_curstash); |
| I32 packlen = strlen(package); |
| modperl_modglobal_key_t *gkey = |
| (modperl_modglobal_key_t *)mg->mg_ptr; |
| |
| /* the argument sv, is the original list perl was operating on. |
| * (e.g. PL_endav). So now if we find that we have package/cv name |
| * (e.g. Foo/END) registered for set-aside, we remove the cv that |
| * was just unshifted in and push it into |
| * $PL_modglobal{$key}{$package}. Otherwise we do nothing, which |
| * keeps the unshifted cv (e.g. END block) in its original av |
| * (e.g. PL_endav) |
| */ |
| |
| mav = modperl_perl_global_avcv_fetch(aTHX_ gkey, package, packlen, FALSE); |
| |
| if (!mav) { |
| MP_TRACE_g(MP_FUNC, "%s::%s is not going to PL_modglobal", |
| package, (char*)gkey->name); |
| /* keep it in the tied list (e.g. PL_endav) */ |
| return 1; |
| } |
| |
| MP_TRACE_g(MP_FUNC, "%s::%s is going into PL_modglobal", |
| package, (char*)gkey->name); |
| |
| sv = av_shift(av); |
| |
| /* push @{ $PL_modglobal{$key}{$package} }, $cv */ |
| av_store(mav, AvFILLp(mav)+1, sv); |
| |
| /* print scalar @{ $PL_modglobal{$key}{$package} } */ |
| MP_TRACE_g(MP_FUNC, "%s::%s av now has %d entries", |
| package, (char*)gkey->name, 1+av_len(mav)); |
| |
| return 1; |
| } |
| |
| static MGVTBL modperl_vtbl_global_avcv_t = { |
| 0, |
| modperl_perl_global_avcv_set, |
| 0, 0, 0, |
| }; |
| |
| static void modperl_perl_global_avcv_tie(pTHX_ modperl_modglobal_key_e key, |
| AV *av) |
| { |
| if (!SvMAGIC((SV*)av)) { |
| MAGIC *mg; |
| Newxz(mg, 1, MAGIC); |
| mg->mg_virtual = &modperl_vtbl_global_avcv_t; |
| mg->mg_ptr = (char *)&MP_modglobal_keys[key]; |
| mg->mg_len = -1; /* prevent free() of mg->mg_ptr */ |
| SvMAGIC((SV*)av) = mg; |
| } |
| |
| SvSMAGICAL_on((SV*)av); |
| } |
| |
| static void modperl_perl_global_avcv_untie(pTHX_ AV *av) |
| { |
| SvSMAGICAL_off((SV*)av); |
| } |
| |
| static void |
| modperl_perl_global_avcv_save(pTHX_ modperl_perl_global_avcv_t *avcv) |
| { |
| if (!*avcv->av) { |
| *avcv->av = newAV(); |
| } |
| |
| modperl_perl_global_avcv_tie(aTHX_ avcv->key, *avcv->av); |
| } |
| |
| static void |
| modperl_perl_global_avcv_restore(pTHX_ modperl_perl_global_avcv_t *avcv) |
| { |
| modperl_perl_global_avcv_untie(aTHX_ *avcv->av); |
| } |
| |
| /* |
| * newHVhv is not good enough since it does not copy magic. |
| * XXX: 5.8.0+ newHVhv has some code thats faster than hv_iternext |
| */ |
| static HV *copyENV(pTHX_ HV *ohv) |
| { |
| HE *entry, *hv_eiter; |
| I32 hv_riter; |
| register HV *hv; |
| STRLEN hv_max = HvMAX(ohv); |
| STRLEN hv_fill = HvFILL(ohv); |
| |
| hv = newHV(); |
| while (hv_max && hv_max + 1 >= hv_fill * 2) { |
| hv_max = hv_max / 2; /* Is always 2^n-1 */ |
| } |
| |
| HvMAX(hv) = hv_max; |
| |
| if (!hv_fill) { |
| return hv; |
| } |
| |
| hv_riter = HvRITER(ohv); /* current root of iterator */ |
| hv_eiter = HvEITER(ohv); /* current entry of iterator */ |
| |
| hv_iterinit(ohv); |
| while ((entry = hv_iternext(ohv))) { |
| SV *sv = newSVsv(HeVAL(entry)); |
| modperl_envelem_tie(sv, HeKEY(entry), HeKLEN(entry)); |
| (void)hv_store(hv, HeKEY(entry), HeKLEN(entry), |
| sv, HeHASH(entry)); |
| } |
| |
| HvRITER(ohv) = hv_riter; |
| HvEITER(ohv) = hv_eiter; |
| |
| hv_magic(hv, (GV *)NULL, 'E'); |
| |
| TAINT_NOT; |
| |
| return hv; |
| } |
| |
| static void |
| modperl_perl_global_gvhv_save(pTHX_ modperl_perl_global_gvhv_t *gvhv) |
| { |
| HV *hv = GvHV(gvhv->gv); |
| #if 0 |
| U32 mg_flags; |
| MAGIC *mg = SvMAGIC(hv); |
| |
| /* |
| * there should only be a small number of entries in %ENV |
| * at this point: modperl_env.c:modperl_env_const_vars[], |
| * PerlPassEnv and top-level PerlSetEnv |
| * XXX: still; could have have something faster than newHVhv() |
| * especially if we add another GVHV to the globals table that |
| * might have more entries |
| */ |
| |
| /* makes newHVhv() faster in bleedperl */ |
| MP_magical_untie(hv, mg_flags); |
| |
| gvhv->tmphv = newHVhv(hv); |
| TAINT_NOT; |
| |
| /* reapply magic flags */ |
| MP_magical_tie(hv, mg_flags); |
| MP_magical_tie(gvhv->tmphv, mg_flags); |
| |
| if (mg && mg->mg_type && !SvMAGIC(gvhv->tmphv)) { |
| /* propagate SvMAGIC(hv) to SvMAGIC(gvhv->tmphv) */ |
| /* XXX: maybe newHVhv should do this? */ |
| hv_magic(gvhv->tmphv, (GV *)NULL, mg->mg_type); |
| } |
| #else |
| gvhv->tmphv = copyENV(aTHX_ hv); |
| #endif |
| |
| gvhv->orighv = hv; |
| GvHV(gvhv->gv) = gvhv->tmphv; |
| } |
| |
| static void |
| modperl_perl_global_gvhv_restore(pTHX_ modperl_perl_global_gvhv_t *gvhv) |
| { |
| U32 mg_flags; |
| |
| GvHV(gvhv->gv) = gvhv->orighv; |
| |
| /* loose magic for hv_clear() |
| * e.g. for %ENV don't want to clear environ array |
| */ |
| MP_magical_untie(gvhv->tmphv, mg_flags); |
| SvREFCNT_dec(gvhv->tmphv); |
| |
| /* avoiding -Wall warning */ |
| mg_flags = mg_flags; |
| } |
| |
| static void |
| modperl_perl_global_gvav_save(pTHX_ modperl_perl_global_gvav_t *gvav) |
| { |
| gvav->origav = GvAV(gvav->gv); |
| gvav->tmpav = newAV(); |
| modperl_perl_av_push_elts_ref(aTHX_ gvav->tmpav, gvav->origav); |
| GvAV(gvav->gv) = gvav->tmpav; |
| } |
| |
| static void |
| modperl_perl_global_gvav_restore(pTHX_ modperl_perl_global_gvav_t *gvav) |
| { |
| GvAV(gvav->gv) = gvav->origav; |
| SvREFCNT_dec(gvav->tmpav); |
| } |
| |
| static void |
| modperl_perl_global_gvio_save(pTHX_ modperl_perl_global_gvio_t *gvio) |
| { |
| gvio->flags = IoFLAGS(GvIOp(gvio->gv)); |
| } |
| |
| static void |
| modperl_perl_global_gvio_restore(pTHX_ modperl_perl_global_gvio_t *gvio) |
| { |
| IoFLAGS(GvIOp(gvio->gv)) = gvio->flags; |
| } |
| |
| static void |
| modperl_perl_global_svpv_save(pTHX_ modperl_perl_global_svpv_t *svpv) |
| { |
| svpv->cur = SvCUR(*svpv->sv); |
| strncpy(svpv->pv, SvPVX(*svpv->sv), sizeof(svpv->pv)); |
| } |
| |
| static void |
| modperl_perl_global_svpv_restore(pTHX_ modperl_perl_global_svpv_t *svpv) |
| { |
| sv_setpvn(*svpv->sv, svpv->pv, svpv->cur); |
| } |
| |
| typedef enum { |
| MP_GLOBAL_AVCV, |
| MP_GLOBAL_GVHV, |
| MP_GLOBAL_GVAV, |
| MP_GLOBAL_GVIO, |
| MP_GLOBAL_SVPV |
| } modperl_perl_global_types_e; |
| |
| typedef struct { |
| char *name; |
| int offset; |
| modperl_perl_global_types_e type; |
| } modperl_perl_global_entry_t; |
| |
| #define MP_GLOBAL_OFFSET(m) \ |
| STRUCT_OFFSET(modperl_perl_globals_t, m) |
| |
| static modperl_perl_global_entry_t MP_perl_global_entries[] = { |
| {"END", MP_GLOBAL_OFFSET(end), MP_GLOBAL_AVCV}, /* END */ |
| {"ENV", MP_GLOBAL_OFFSET(env), MP_GLOBAL_GVHV}, /* %ENV */ |
| {"INC", MP_GLOBAL_OFFSET(inc), MP_GLOBAL_GVAV}, /* @INC */ |
| {"STDOUT", MP_GLOBAL_OFFSET(defout), MP_GLOBAL_GVIO}, /* $| */ |
| {"/", MP_GLOBAL_OFFSET(rs), MP_GLOBAL_SVPV}, /* $/ */ |
| {NULL} |
| }; |
| |
| #define MP_PERL_GLOBAL_SAVE(type, ptr) \ |
| modperl_perl_global_##type##_save( \ |
| aTHX_ (modperl_perl_global_##type##_t *)&(*ptr)) |
| |
| #define MP_PERL_GLOBAL_RESTORE(type, ptr) \ |
| modperl_perl_global_##type##_restore( \ |
| aTHX_ (modperl_perl_global_##type##_t *)&(*ptr)) |
| |
| #define MP_dGLOBAL_PTR(globals, entries) \ |
| apr_uint64_t **ptr = (apr_uint64_t **) \ |
| ((char *)globals + (int)(long)entries->offset) |
| |
| static void modperl_perl_global_save(pTHX_ modperl_perl_globals_t *globals, |
| modperl_perl_global_entry_t *entries) |
| { |
| modperl_perl_global_init(aTHX_ globals); |
| |
| while (entries->name) { |
| MP_dGLOBAL_PTR(globals, entries); |
| |
| switch (entries->type) { |
| case MP_GLOBAL_AVCV: |
| MP_PERL_GLOBAL_SAVE(avcv, ptr); |
| break; |
| case MP_GLOBAL_GVHV: |
| MP_PERL_GLOBAL_SAVE(gvhv, ptr); |
| break; |
| case MP_GLOBAL_GVAV: |
| MP_PERL_GLOBAL_SAVE(gvav, ptr); |
| break; |
| case MP_GLOBAL_GVIO: |
| MP_PERL_GLOBAL_SAVE(gvio, ptr); |
| break; |
| case MP_GLOBAL_SVPV: |
| MP_PERL_GLOBAL_SAVE(svpv, ptr); |
| break; |
| } |
| |
| entries++; |
| } |
| } |
| |
| static void modperl_perl_global_restore(pTHX_ modperl_perl_globals_t *globals, |
| modperl_perl_global_entry_t *entries) |
| { |
| while (entries->name) { |
| MP_dGLOBAL_PTR(globals, entries); |
| |
| switch (entries->type) { |
| case MP_GLOBAL_AVCV: |
| MP_PERL_GLOBAL_RESTORE(avcv, ptr); |
| break; |
| case MP_GLOBAL_GVHV: |
| MP_PERL_GLOBAL_RESTORE(gvhv, ptr); |
| break; |
| case MP_GLOBAL_GVAV: |
| MP_PERL_GLOBAL_RESTORE(gvav, ptr); |
| break; |
| case MP_GLOBAL_GVIO: |
| MP_PERL_GLOBAL_RESTORE(gvio, ptr); |
| break; |
| case MP_GLOBAL_SVPV: |
| MP_PERL_GLOBAL_RESTORE(svpv, ptr); |
| break; |
| } |
| |
| entries++; |
| } |
| } |
| |
| void modperl_perl_global_request_save(pTHX_ request_rec *r) |
| { |
| MP_dRCFG; |
| modperl_perl_global_save(aTHX_ &rcfg->perl_globals, |
| MP_perl_global_entries); |
| } |
| |
| void modperl_perl_global_request_restore(pTHX_ request_rec *r) |
| { |
| MP_dRCFG; |
| modperl_perl_global_restore(aTHX_ &rcfg->perl_globals, |
| MP_perl_global_entries); |
| |
| } |
| |
| /* |
| * Local Variables: |
| * c-basic-offset: 4 |
| * indent-tabs-mode: nil |
| * End: |
| */ |