blob: f52e42cf4798a2563bee9fc32edd72dbcc99daf4 [file] [log] [blame]
/* 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:
*/