This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
make <expr> always overload if expr is overloaded
[perl5.git] / pp_sys.c
index cfbf918..a657d36 100644 (file)
--- a/pp_sys.c
+++ b/pp_sys.c
 #include "EXTERN.h"
 #define PERL_IN_PP_SYS_C
 #include "perl.h"
-#ifndef PERL_MICRO
-#  include "time64.h"
-#  include "time64.c"
-#endif
+#include "time64.h"
+#include "time64.c"
 
 #ifdef I_SHADOW
 /* Shadow password support for solaris - pdo@cs.umd.edu
@@ -244,7 +242,6 @@ S_emulate_eaccess(pTHX_ const char* path, Mode_t mode)
     const Gid_t egid = getegid();
     int res;
 
-    LOCK_CRED_MUTEX;
 #if !defined(HAS_SETREUID) && !defined(HAS_SETRESUID)
     Perl_croak(aTHX_ "switching effective uid is not implemented");
 #else
@@ -290,7 +287,6 @@ S_emulate_eaccess(pTHX_ const char* path, Mode_t mode)
 #endif
 #endif
        Perl_croak(aTHX_ "leaving effective gid failed");
-    UNLOCK_CRED_MUTEX;
 
     return res;
 }
@@ -322,13 +318,13 @@ PP(pp_backtick)
                NOOP;
        }
        else if (gimme == G_SCALAR) {
-           ENTER;
+           ENTER_with_name("backtick");
            SAVESPTR(PL_rs);
            PL_rs = &PL_sv_undef;
            sv_setpvs(TARG, "");        /* note that this preserves previous buffer */
            while (sv_gets(TARG, fp, SvCUR(TARG)) != NULL)
                NOOP;
-           LEAVE;
+           LEAVE_with_name("backtick");
            XPUSHs(TARG);
            SvTAINTED_on(TARG);
        }
@@ -362,13 +358,28 @@ PP(pp_glob)
 {
     dVAR;
     OP *result;
-    tryAMAGICunTARGET(iter, -1);
+    dSP;
+    /* make a copy of the pattern, to ensure that magic is called once
+     * and only once */
+    TOPm1s = sv_2mortal(newSVsv(TOPm1s));
+
+    tryAMAGICunTARGET(iter_amg, -1, (PL_op->op_flags & OPf_SPECIAL));
+
+    if (PL_op->op_flags & OPf_SPECIAL) {
+       /* call Perl-level glob function instead. Stack args are:
+        * MARK, wildcard, csh_glob context index
+        * and following OPs should be: gv(CORE::GLOBAL::glob), entersub
+        * */
+       return NORMAL;
+    }
+    /* stack args are: wildcard, gv(_GEN_n) */
+
 
     /* Note that we only ever get here if File::Glob fails to load
      * without at the same time croaking, for some reason, or if
      * perl was built with PERL_EXTERNAL_GLOB */
 
-    ENTER;
+    ENTER_with_name("glob");
 
 #ifndef VMS
     if (PL_tainting) {
@@ -393,7 +404,7 @@ PP(pp_glob)
 #endif /* !DOSISH */
 
     result = do_readline();
-    LEAVE;
+    LEAVE_with_name("glob");
     return result;
 }
 
@@ -407,100 +418,91 @@ PP(pp_rcatline)
 PP(pp_warn)
 {
     dVAR; dSP; dMARK;
-    SV *tmpsv;
-    const char *tmps;
+    SV *exsv;
+    const char *pv;
     STRLEN len;
     if (SP - MARK > 1) {
        dTARGET;
        do_join(TARG, &PL_sv_no, MARK, SP);
-       tmpsv = TARG;
+       exsv = TARG;
        SP = MARK + 1;
     }
     else if (SP == MARK) {
-       tmpsv = &PL_sv_no;
+       exsv = &PL_sv_no;
        EXTEND(SP, 1);
        SP = MARK + 1;
     }
     else {
-       tmpsv = TOPs;
+       exsv = TOPs;
     }
-    tmps = SvPV_const(tmpsv, len);
-    if ((!tmps || !len) && PL_errgv) {
-       SV * const error = ERRSV;
-       SvUPGRADE(error, SVt_PV);
-       if (SvPOK(error) && SvCUR(error))
-           sv_catpvs(error, "\t...caught");
-       tmpsv = error;
-       tmps = SvPV_const(tmpsv, len);
-    }
-    if (!tmps || !len)
-       tmpsv = newSVpvs_flags("Warning: something's wrong", SVs_TEMP);
 
-    Perl_warn(aTHX_ "%"SVf, SVfARG(tmpsv));
+    if (SvROK(exsv) || (pv = SvPV_const(exsv, len), len)) {
+       /* well-formed exception supplied */
+    }
+    else if (SvROK(ERRSV)) {
+       exsv = ERRSV;
+    }
+    else if (SvPOK(ERRSV) && SvCUR(ERRSV)) {
+       exsv = sv_mortalcopy(ERRSV);
+       sv_catpvs(exsv, "\t...caught");
+    }
+    else {
+       exsv = newSVpvs_flags("Warning: something's wrong", SVs_TEMP);
+    }
+    warn_sv(exsv);
     RETSETYES;
 }
 
 PP(pp_die)
 {
     dVAR; dSP; dMARK;
-    const char *tmps;
-    SV *tmpsv;
+    SV *exsv;
+    const char *pv;
     STRLEN len;
-    bool multiarg = 0;
 #ifdef VMS
     VMSISH_HUSHED  = VMSISH_HUSHED || (PL_op->op_private & OPpHUSH_VMSISH);
 #endif
     if (SP - MARK != 1) {
        dTARGET;
        do_join(TARG, &PL_sv_no, MARK, SP);
-       tmpsv = TARG;
-       tmps = SvPV_const(tmpsv, len);
-       multiarg = 1;
+       exsv = TARG;
        SP = MARK + 1;
     }
     else {
-       tmpsv = TOPs;
-        tmps = SvROK(tmpsv) ? (const char *)NULL : SvPV_const(tmpsv, len);
-    }
-    if (!tmps || !len) {
-       SV * const error = ERRSV;
-       SvUPGRADE(error, SVt_PV);
-       if (multiarg ? SvROK(error) : SvROK(tmpsv)) {
-           if (!multiarg)
-               SvSetSV(error,tmpsv);
-           else if (sv_isobject(error)) {
-               HV * const stash = SvSTASH(SvRV(error));
-               GV * const gv = gv_fetchmethod(stash, "PROPAGATE");
-               if (gv) {
-                   SV * const file = sv_2mortal(newSVpv(CopFILE(PL_curcop),0));
-                   SV * const line = sv_2mortal(newSVuv(CopLINE(PL_curcop)));
-                   EXTEND(SP, 3);
-                   PUSHMARK(SP);
-                   PUSHs(error);
-                   PUSHs(file);
-                   PUSHs(line);
-                   PUTBACK;
-                   call_sv(MUTABLE_SV(GvCV(gv)),
-                           G_SCALAR|G_EVAL|G_KEEPERR);
-                   sv_setsv(error,*PL_stack_sp--);
-               }
+       exsv = TOPs;
+    }
+
+    if (SvROK(exsv) || (pv = SvPV_const(exsv, len), len)) {
+       /* well-formed exception supplied */
+    }
+    else if (SvROK(ERRSV)) {
+       exsv = ERRSV;
+       if (sv_isobject(exsv)) {
+           HV * const stash = SvSTASH(SvRV(exsv));
+           GV * const gv = gv_fetchmethod(stash, "PROPAGATE");
+           if (gv) {
+               SV * const file = sv_2mortal(newSVpv(CopFILE(PL_curcop),0));
+               SV * const line = sv_2mortal(newSVuv(CopLINE(PL_curcop)));
+               EXTEND(SP, 3);
+               PUSHMARK(SP);
+               PUSHs(exsv);
+               PUSHs(file);
+               PUSHs(line);
+               PUTBACK;
+               call_sv(MUTABLE_SV(GvCV(gv)),
+                       G_SCALAR|G_EVAL|G_KEEPERR);
+               exsv = sv_mortalcopy(*PL_stack_sp--);
            }
-           DIE(aTHX_ NULL);
-       }
-       else {
-           if (SvPOK(error) && SvCUR(error))
-               sv_catpvs(error, "\t...propagated");
-           tmpsv = error;
-           if (SvOK(tmpsv))
-               tmps = SvPV_const(tmpsv, len);
-           else
-               tmps = NULL;
        }
     }
-    if (!tmps || !len)
-       tmpsv = newSVpvs_flags("Died", SVs_TEMP);
-
-    DIE(aTHX_ "%"SVf, SVfARG(tmpsv));
+    else if (SvPOK(ERRSV) && SvCUR(ERRSV)) {
+       exsv = sv_mortalcopy(ERRSV);
+       sv_catpvs(exsv, "\t...propagated");
+    }
+    else {
+       exsv = newSVpvs_flags("Died", SVs_TEMP);
+    }
+    return die_sv(exsv);
 }
 
 /* I/O. */
@@ -518,16 +520,17 @@ PP(pp_open)
 
     GV * const gv = MUTABLE_GV(*++MARK);
 
-    if (!isGV(gv))
+    if (!isGV(gv) && !(SvTYPE(gv) == SVt_PVLV && isGV_with_GP(gv)))
        DIE(aTHX_ PL_no_usym, "filehandle");
 
     if ((io = GvIOp(gv))) {
        MAGIC *mg;
        IoFLAGS(GvIOp(gv)) &= ~IOf_UNTAINT;
 
-       if (IoDIRP(io) && ckWARN2(WARN_IO, WARN_DEPRECATED))
-           Perl_warner(aTHX_ packWARN2(WARN_IO, WARN_DEPRECATED),
-                   "Opening dirhandle %s also as a file", GvENAME(gv));
+       if (IoDIRP(io))
+           Perl_ck_warner_d(aTHX_ packWARN2(WARN_IO, WARN_DEPRECATED),
+                            "Opening dirhandle %s also as a file",
+                            GvENAME(gv));
 
        mg = SvTIED_mg((const SV *)io, PERL_MAGIC_tiedscalar);
        if (mg) {
@@ -536,11 +539,10 @@ PP(pp_open)
            *MARK-- = SvTIED_obj(MUTABLE_SV(io), mg);
            PUSHMARK(MARK);
            PUTBACK;
-           ENTER;
+           ENTER_with_name("call_OPEN");
            call_method("OPEN", G_SCALAR);
-           LEAVE;
-           SPAGAIN;
-           RETURN;
+           LEAVE_with_name("call_OPEN");
+           return NORMAL;
        }
     }
 
@@ -563,28 +565,71 @@ PP(pp_open)
     RETURN;
 }
 
+/* These are private to this function, which is private to this file.
+   Use 0x04 rather than the next available bit, to help the compiler if the
+   architecture can generate more efficient instructions.  */
+#define MORTALIZE_NOT_NEEDED   0x04
+#define TIED_HANDLE_ARGC_SHIFT 3
+
+static OP *
+S_tied_handle_method(pTHX_ const char *const methname, SV **sp,
+                    IO *const io, MAGIC *const mg, const U32 flags, ...)
+{
+    U32 argc = flags >> TIED_HANDLE_ARGC_SHIFT;
+
+    PERL_ARGS_ASSERT_TIED_HANDLE_METHOD;
+
+    /* Ensure that our flag bits do not overlap.  */
+    assert((MORTALIZE_NOT_NEEDED & G_WANT) == 0);
+    assert((G_WANT >> TIED_HANDLE_ARGC_SHIFT) == 0);
+
+    PUSHMARK(sp);
+    PUSHs(SvTIED_obj(MUTABLE_SV(io), mg));
+    if (argc) {
+       const U32 mortalize_not_needed = flags & MORTALIZE_NOT_NEEDED;
+       va_list args;
+       va_start(args, flags);
+       do {
+           SV *const arg = va_arg(args, SV *);
+           if(mortalize_not_needed)
+               PUSHs(arg);
+           else
+               mPUSHs(arg);
+       } while (--argc);
+       va_end(args);
+    }
+
+    PUTBACK;
+    ENTER_with_name("call_tied_handle_method");
+    call_method(methname, flags & G_WANT);
+    LEAVE_with_name("call_tied_handle_method");
+    return NORMAL;
+}
+
+#define tied_handle_method(a,b,c,d)            \
+    S_tied_handle_method(aTHX_ a,b,c,d,G_SCALAR)
+#define tied_handle_method1(a,b,c,d,e) \
+    S_tied_handle_method(aTHX_ a,b,c,d,G_SCALAR | (1 << TIED_HANDLE_ARGC_SHIFT),e)
+#define tied_handle_method2(a,b,c,d,e,f)       \
+    S_tied_handle_method(aTHX_ a,b,c,d,G_SCALAR | (2 << TIED_HANDLE_ARGC_SHIFT), e,f)
+
 PP(pp_close)
 {
     dVAR; dSP;
     GV * const gv = (MAXARG == 0) ? PL_defoutgv : MUTABLE_GV(POPs);
 
+    if (MAXARG == 0)
+       EXTEND(SP, 1);
+
     if (gv) {
        IO * const io = GvIO(gv);
        if (io) {
            MAGIC * const mg = SvTIED_mg((const SV *)io, PERL_MAGIC_tiedscalar);
            if (mg) {
-               PUSHMARK(SP);
-               XPUSHs(SvTIED_obj(MUTABLE_SV(io), mg));
-               PUTBACK;
-               ENTER;
-               call_method("CLOSE", G_SCALAR);
-               LEAVE;
-               SPAGAIN;
-               RETURN;
+               return tied_handle_method("CLOSE", SP, io, mg);
            }
        }
     }
-    EXTEND(SP, 1);
     PUSHs(boolSV(do_close(gv, TRUE)));
     RETURN;
 }
@@ -663,21 +708,14 @@ PP(pp_fileno)
     if (gv && (io = GvIO(gv))
        && (mg = SvTIED_mg((const SV *)io, PERL_MAGIC_tiedscalar)))
     {
-       PUSHMARK(SP);
-       XPUSHs(SvTIED_obj(MUTABLE_SV(io), mg));
-       PUTBACK;
-       ENTER;
-       call_method("FILENO", G_SCALAR);
-       LEAVE;
-       SPAGAIN;
-       RETURN;
+       return tied_handle_method("FILENO", SP, io, mg);
     }
 
     if (!gv || !(io = GvIO(gv)) || !(fp = IoIFP(io))) {
        /* Can't do this because people seem to do things like
           defined(fileno($foo)) to check whether $foo is a valid fh.
-         if (ckWARN2(WARN_UNOPENED,WARN_CLOSED))
-             report_evil_fh(gv, io, PL_op->op_type);
+
+          report_evil_fh(gv);
            */
        RETPUSHUNDEF;
     }
@@ -736,23 +774,20 @@ PP(pp_binmode)
     if (gv && (io = GvIO(gv))) {
        MAGIC * const mg = SvTIED_mg((const SV *)io, PERL_MAGIC_tiedscalar);
        if (mg) {
-           PUSHMARK(SP);
-           XPUSHs(SvTIED_obj(MUTABLE_SV(io), mg));
-           if (discp)
-               XPUSHs(discp);
-           PUTBACK;
-           ENTER;
-           call_method("BINMODE", G_SCALAR);
-           LEAVE;
-           SPAGAIN;
-           RETURN;
+           /* This takes advantage of the implementation of the varargs
+              function, which I don't think that the optimiser will be able to
+              figure out. Although, as it's a static function, in theory it
+              could.  */
+           return S_tied_handle_method(aTHX_ "BINMODE", SP, io, mg,
+                                       G_SCALAR|MORTALIZE_NOT_NEEDED
+                                       | (discp
+                                          ? (1 << TIED_HANDLE_ARGC_SHIFT) : 0),
+                                       discp);
        }
     }
 
-    EXTEND(SP, 1);
     if (!(io = GvIO(gv)) || !(fp = IoIFP(io))) {
-       if (ckWARN2(WARN_UNOPENED,WARN_CLOSED))
-           report_evil_fh(gv, io, PL_op->op_type);
+       report_evil_fh(gv);
        SETERRNO(EBADF,RMS_IFI);
         RETPUSHUNDEF;
     }
@@ -786,7 +821,7 @@ PP(pp_tie)
 {
     dVAR; dSP; dMARK;
     HV* stash;
-    GV *gv;
+    GV *gv = NULL;
     SV *sv;
     const I32 markoff = MARK - PL_stack_base;
     const char *methname;
@@ -803,12 +838,12 @@ PP(pp_tie)
            methname = "TIEARRAY";
            break;
        case SVt_PVGV:
+       case SVt_PVLV:
            if (isGV_with_GP(varsv)) {
-#ifdef GV_UNIQUE_CHECK
-               if (GvUNIQUE((const GV *)varsv)) {
-                   Perl_croak(aTHX_ "Attempt to tie unique GV");
+               if (SvFAKE(varsv) && !(GvFLAGS(varsv) & GVf_TIEWARNED)) {
+                   deprecate("tie on a handle without *");
+                   GvFLAGS(varsv) |= GVf_TIEWARNED;
                }
-#endif
                methname = "TIEHANDLE";
                how = PERL_MAGIC_tiedscalar;
                /* For tied filehandles, we apply tiedscalar magic to the IO
@@ -826,7 +861,7 @@ PP(pp_tie)
     }
     items = SP - MARK++;
     if (sv_isobject(*MARK)) { /* Calls GET magic. */
-       ENTER;
+       ENTER_with_name("call_TIE");
        PUSHSTACKi(PERLSI_MAGIC);
        PUSHMARK(SP);
        EXTEND(SP,(I32)items);
@@ -836,8 +871,10 @@ PP(pp_tie)
        call_method(methname, G_SCALAR);
     }
     else {
-       /* Not clear why we don't call call_method here too.
-        * perhaps to get different error message ?
+       /* Can't use call_method here, else this: fileno FOO; tie @a, "FOO"
+        * will attempt to invoke IO::File::TIEARRAY, with (best case) the
+        * wrong error message, and worse case, supreme action at a distance.
+        * (Sorry obfuscation writers. You're not going to be given this one.)
         */
        STRLEN len;
        const char *name = SvPV_nomg_const(*MARK, len);
@@ -846,7 +883,7 @@ PP(pp_tie)
            DIE(aTHX_ "Can't locate object method \"%s\" via package \"%"SVf"\"",
                 methname, SVfARG(SvOK(*MARK) ? *MARK : &PL_sv_no));
        }
-       ENTER;
+       ENTER_with_name("call_TIE");
        PUSHSTACKi(PERLSI_MAGIC);
        PUSHMARK(SP);
        EXTEND(SP,(I32)items);
@@ -869,7 +906,7 @@ PP(pp_tie)
                       "Self-ties of arrays and hashes are not supported");
        sv_magic(varsv, (SvRV(sv) == varsv ? NULL : sv), how, NULL, 0);
     }
-    LEAVE;
+    LEAVE_with_name("call_TIE");
     SP = PL_stack_base + markoff;
     PUSHs(sv);
     RETURN;
@@ -883,8 +920,14 @@ PP(pp_untie)
     const char how = (SvTYPE(sv) == SVt_PVHV || SvTYPE(sv) == SVt_PVAV)
                ? PERL_MAGIC_tied : PERL_MAGIC_tiedscalar;
 
-    if (isGV_with_GP(sv) && !(sv = MUTABLE_SV(GvIOp(sv))))
+    if (isGV_with_GP(sv)) {
+      if (SvFAKE(sv) && !(GvFLAGS(sv) & GVf_TIEWARNED)) {
+       deprecate("untie on a handle without *");
+       GvFLAGS(sv) |= GVf_TIEWARNED;
+      }
+      if (!(sv = MUTABLE_SV(GvIOp(sv))))
        RETPUSHYES;
+    }
 
     if ((mg = SvTIED_mg(sv, how))) {
        SV * const obj = SvRV(SvTIED_obj(sv, mg));
@@ -893,18 +936,18 @@ PP(pp_untie)
            CV *cv;
            if (gv && isGV(gv) && (cv = GvCV(gv))) {
               PUSHMARK(SP);
-              XPUSHs(SvTIED_obj(MUTABLE_SV(gv), mg));
+              PUSHs(SvTIED_obj(MUTABLE_SV(gv), mg));
               mXPUSHi(SvREFCNT(obj) - 1);
               PUTBACK;
-              ENTER;
+              ENTER_with_name("call_UNTIE");
               call_sv(MUTABLE_SV(cv), G_VOID);
-              LEAVE;
+              LEAVE_with_name("call_UNTIE");
               SPAGAIN;
             }
-           else if (mg && SvREFCNT(obj) > 1 && ckWARN(WARN_UNTIE)) {
-                 Perl_warner(aTHX_ packWARN(WARN_UNTIE),
-                     "untie attempted while %"UVuf" inner references still exist",
-                      (UV)SvREFCNT(obj) - 1 ) ;
+           else if (mg && SvREFCNT(obj) > 1) {
+               Perl_ck_warner(aTHX_ packWARN(WARN_UNTIE),
+                              "untie attempted while %"UVuf" inner references still exist",
+                              (UV)SvREFCNT(obj) - 1 ) ;
            }
         }
     }
@@ -921,8 +964,14 @@ PP(pp_tied)
     const char how = (SvTYPE(sv) == SVt_PVHV || SvTYPE(sv) == SVt_PVAV)
                ? PERL_MAGIC_tied : PERL_MAGIC_tiedscalar;
 
-    if (isGV_with_GP(sv) && !(sv = MUTABLE_SV(GvIOp(sv))))
+    if (isGV_with_GP(sv)) {
+      if (SvFAKE(sv) && !(GvFLAGS(sv) & GVf_TIEWARNED)) {
+       deprecate("tied on a handle without *");
+       GvFLAGS(sv) |= GVf_TIEWARNED;
+      }
+      if (!(sv = MUTABLE_SV(GvIOp(sv))))
        RETPUSHUNDEF;
+    }
 
     if ((mg = SvTIED_mg(sv, how))) {
        SV *osv = SvTIED_obj(sv, mg);
@@ -939,7 +988,7 @@ PP(pp_dbmopen)
     dVAR; dSP;
     dPOPPOPssrl;
     HV* stash;
-    GV *gv;
+    GV *gv = NULL;
 
     HV * const hv = MUTABLE_HV(POPs);
     SV * const sv = newSVpvs_flags("AnyDBM_File", SVs_TEMP);
@@ -1024,11 +1073,10 @@ PP(pp_sselect)
            if (SvIsCOW(sv))
                sv_force_normal_flags(sv, 0);
            if (SvREADONLY(sv) && !(SvPOK(sv) && SvCUR(sv) == 0))
-               DIE(aTHX_ "%s", PL_no_modify);
+               Perl_croak_no_modify(aTHX);
        }
        if (!SvPOK(sv)) {
-           if (ckWARN(WARN_MISC))
-                Perl_warner(aTHX_ packWARN(WARN_MISC), "Non-string passed as bitmask");
+           Perl_ck_warner(aTHX_ packWARN(WARN_MISC), "Non-string passed as bitmask");
            SvPV_force_nolen(sv);       /* force string conversion */
        }
        j = SvCUR(sv);
@@ -1167,8 +1215,7 @@ Perl_setdefout(pTHX_ GV *gv)
 {
     dVAR;
     SvREFCNT_inc_simple_void(gv);
-    if (PL_defoutgv)
-       SvREFCNT_dec(PL_defoutgv);
+    SvREFCNT_dec(PL_defoutgv);
     PL_defoutgv = gv;
 }
 
@@ -1177,11 +1224,11 @@ PP(pp_select)
     dVAR; dSP; dTARGET;
     HV *hv;
     GV * const newdefout = (PL_op->op_private > 0) ? (MUTABLE_GV(POPs)) : NULL;
-    GV * egv = GvEGV(PL_defoutgv);
+    GV * egv = GvEGVx(PL_defoutgv);
 
     if (!egv)
        egv = PL_defoutgv;
-    hv = GvSTASH(egv);
+    hv = isGV_with_GP(egv) ? GvSTASH(egv) : NULL;
     if (! hv)
        XPUSHs(&PL_sv_undef);
     else {
@@ -1210,26 +1257,24 @@ PP(pp_getc)
     IO *io = NULL;
     GV * const gv = (MAXARG==0) ? PL_stdingv : MUTABLE_GV(POPs);
 
+    if (MAXARG == 0)
+       EXTEND(SP, 1);
+
     if (gv && (io = GvIO(gv))) {
        MAGIC * const mg = SvTIED_mg((const SV *)io, PERL_MAGIC_tiedscalar);
        if (mg) {
-           const I32 gimme = GIMME_V;
-           PUSHMARK(SP);
-           XPUSHs(SvTIED_obj(MUTABLE_SV(io), mg));
-           PUTBACK;
-           ENTER;
-           call_method("GETC", gimme);
-           LEAVE;
-           SPAGAIN;
-           if (gimme == G_SCALAR)
+           const U32 gimme = GIMME_V;
+           S_tied_handle_method(aTHX_ "GETC", SP, io, mg, gimme);
+           if (gimme == G_SCALAR) {
+               SPAGAIN;
                SvSetMagicSV_nosteal(TARG, TOPs);
-           RETURN;
+           }
+           return NORMAL;
        }
     }
     if (!gv || do_eof(gv)) { /* make sure we have fp with something */
-       if ((!io || (!IoIFP(io) && IoTYPE(io) != IoTYPE_WRONLY))
-         && ckWARN2(WARN_UNOPENED,WARN_CLOSED))
-           report_evil_fh(gv, io, PL_op->op_type);
+       if (!io || (!IoIFP(io) && IoTYPE(io) != IoTYPE_WRONLY))
+           report_evil_fh(gv);
        SETERRNO(EBADF,RMS_IFI);
        RETPUSHUNDEF;
     }
@@ -1259,6 +1304,9 @@ S_doform(pTHX_ CV *cv, GV *gv, OP *retop)
 
     PERL_ARGS_ASSERT_DOFORM;
 
+    if (cv && CvCLONE(cv))
+       cv = MUTABLE_CV(sv_2mortal(MUTABLE_SV(cv_clone(cv))));
+
     ENTER;
     SAVETMPS;
 
@@ -1278,17 +1326,18 @@ PP(pp_enterwrite)
     register GV *gv;
     register IO *io;
     GV *fgv;
-    CV *cv;
-    SV * tmpsv = NULL;
+    CV *cv = NULL;
+    SV *tmpsv = NULL;
 
-    if (MAXARG == 0)
+    if (MAXARG == 0) {
        gv = PL_defoutgv;
+       EXTEND(SP, 1);
+    }
     else {
        gv = MUTABLE_GV(POPs);
        if (!gv)
            gv = PL_defoutgv;
     }
-    EXTEND(SP, 1);
     io = GvIO(gv);
     if (!io) {
        RETPUSHNO;
@@ -1313,9 +1362,6 @@ PP(pp_enterwrite)
        not_a_format_reference:
        DIE(aTHX_ "Not a format reference");
     }
-    if (CvCLONE(cv))
-       cv = MUTABLE_CV(sv_2mortal(MUTABLE_SV(cv_clone(cv))));
-
     IoFLAGS(io) &= ~IOf_DIDTOP;
     return doform(cv,gv,PL_op->op_next);
 }
@@ -1330,6 +1376,7 @@ PP(pp_leavewrite)
     SV **newsp;
     I32 gimme;
     register PERL_CONTEXT *cx;
+    OP *retop;
 
     if (!io || !(ofp = IoOFP(io)))
         goto forget_top;
@@ -1404,30 +1451,26 @@ PP(pp_leavewrite)
            else
                DIE(aTHX_ "Undefined top format called");
        }
-       if (cv && CvCLONE(cv))
-           cv = MUTABLE_CV(sv_2mortal(MUTABLE_SV(cv_clone(cv))));
        return doform(cv, gv, PL_op);
     }
 
   forget_top:
     POPBLOCK(cx,PL_curpm);
     POPFORMAT(cx);
+    retop = cx->blk_sub.retop;
     LEAVE;
 
     fp = IoOFP(io);
     if (!fp) {
-       if (ckWARN2(WARN_CLOSED,WARN_IO)) {
-           if (IoIFP(io))
-               report_evil_fh(gv, io, OP_phoney_INPUT_ONLY);
-           else if (ckWARN(WARN_CLOSED))
-               report_evil_fh(gv, io, PL_op->op_type);
-       }
+       if (IoIFP(io))
+           report_wrongway_fh(gv, '<');
+       else
+           report_evil_fh(gv);
        PUSHs(&PL_sv_no);
     }
     else {
        if ((IoLINES_LEFT(io) -= FmLINES(PL_formtarget)) < 0) {
-           if (ckWARN(WARN_IO))
-               Perl_warner(aTHX_ packWARN(WARN_IO), "page overflow");
+           Perl_ck_warner(aTHX_ packWARN(WARN_IO), "page overflow");
        }
        if (!do_print(PL_formtarget, fp))
            PUSHs(&PL_sv_no);
@@ -1445,7 +1488,7 @@ PP(pp_leavewrite)
     PUTBACK;
     PERL_UNUSED_VAR(newsp);
     PERL_UNUSED_VAR(gimme);
-    return cx->blk_sub.retop;
+    return retop;
 }
 
 PP(pp_prtf)
@@ -1473,28 +1516,21 @@ PP(pp_prtf)
            ENTER;
            call_method("PRINTF", G_SCALAR);
            LEAVE;
-           SPAGAIN;
-           MARK = ORIGMARK + 1;
-           *MARK = *SP;
-           SP = MARK;
-           RETURN;
+           return NORMAL;
        }
     }
 
     sv = newSV(0);
     if (!(io = GvIO(gv))) {
-       if (ckWARN2(WARN_UNOPENED,WARN_CLOSED))
-           report_evil_fh(gv, io, PL_op->op_type);
+       report_evil_fh(gv);
        SETERRNO(EBADF,RMS_IFI);
        goto just_say_no;
     }
     else if (!(fp = IoOFP(io))) {
-       if (ckWARN2(WARN_CLOSED,WARN_IO))  {
-           if (IoIFP(io))
-               report_evil_fh(gv, io, OP_phoney_INPUT_ONLY);
-           else if (ckWARN(WARN_CLOSED))
-               report_evil_fh(gv, io, PL_op->op_type);
-       }
+       if (IoIFP(io))
+           report_wrongway_fh(gv, '<');
+       else if (ckWARN(WARN_CLOSED))
+           report_evil_fh(gv);
        SETERRNO(EBADF,IoIFP(io)?RMS_FAC:RMS_IFI);
        goto just_say_no;
     }
@@ -1570,17 +1606,12 @@ PP(pp_sysread)
     {
        const MAGIC * mg = SvTIED_mg((const SV *)io, PERL_MAGIC_tiedscalar);
        if (mg) {
-           SV *sv;
            PUSHMARK(MARK-1);
            *MARK = SvTIED_obj(MUTABLE_SV(io), mg);
            ENTER;
            call_method("READ", G_SCALAR);
            LEAVE;
-           SPAGAIN;
-           sv = POPs;
-           SP = ORIGMARK;
-           PUSHs(sv);
-           RETURN;
+           return NORMAL;
        }
     }
 
@@ -1597,8 +1628,7 @@ PP(pp_sysread)
        offset = 0;
     io = GvIO(gv);
     if (!io || !IoIFP(io)) {
-       if (ckWARN2(WARN_UNOPENED,WARN_CLOSED))
-           report_evil_fh(gv, io, PL_op->op_type);
+       report_evil_fh(gv);
        SETERRNO(EBADF,RMS_IFI);
        goto say_undef;
     }
@@ -1638,6 +1668,9 @@ PP(pp_sysread)
                                  (struct sockaddr *)namebuf, &bufsize);
        if (count < 0)
            RETPUSHUNDEF;
+       /* MSG_TRUNC can give oversized count; quietly lose it */
+       if (count > length)
+           count = length;
 #ifdef EPOC
         /* Bogus return without padding */
        bufsize = sizeof (struct sockaddr_in);
@@ -1737,8 +1770,8 @@ PP(pp_sysread)
            count = -1;
     }
     if (count < 0) {
-       if ((IoTYPE(io) == IoTYPE_WRONLY) && ckWARN(WARN_IO))
-               report_evil_fh(gv, io, OP_phoney_OUTPUT_ONLY);
+       if (IoTYPE(io) == IoTYPE_WRONLY)
+           report_wrongway_fh(gv, '>');
        goto say_undef;
     }
     SvCUR_set(read_target, count+(buffer - SvPVX_const(read_target)));
@@ -1815,12 +1848,9 @@ PP(pp_send)
        && gv && (io = GvIO(gv))) {
        MAGIC * const mg = SvTIED_mg((const SV *)io, PERL_MAGIC_tiedscalar);
        if (mg) {
-           SV *sv;
-
            if (MARK == SP - 1) {
-               EXTEND(SP, 1000);
-               sv = sv_2mortal(newSViv(sv_len(*SP)));
-               PUSHs(sv);
+               SV *sv = *SP;
+               mXPUSHi(sv_len(sv));
                PUTBACK;
            }
 
@@ -1829,11 +1859,7 @@ PP(pp_send)
            ENTER;
            call_method("WRITE", G_SCALAR);
            LEAVE;
-           SPAGAIN;
-           sv = POPs;
-           SP = ORIGMARK;
-           PUSHs(sv);
-           RETURN;
+           return NORMAL;
        }
     }
     if (!gv)
@@ -1845,12 +1871,10 @@ PP(pp_send)
     io = GvIO(gv);
     if (!io || !IoIFP(io) || IoTYPE(io) == IoTYPE_RDONLY) {
        retval = -1;
-       if (ckWARN2(WARN_UNOPENED,WARN_CLOSED)) {
-           if (io && IoIFP(io))
-               report_evil_fh(gv, io, OP_phoney_INPUT_ONLY);
-           else
-               report_evil_fh(gv, io, PL_op->op_type);
-       }
+       if (io && IoIFP(io))
+           report_wrongway_fh(gv, '<');
+       else
+           report_evil_fh(gv);
        SETERRNO(EBADF,RMS_IFI);
        goto say_undef;
     }
@@ -1929,7 +1953,7 @@ PP(pp_send)
                    DIE(aTHX_ "Offset outside string");
                }
                offset += blen_chars;
-           } else if (offset >= (IV)blen_chars && blen_chars > 0) {
+           } else if (offset > (IV)blen_chars) {
                Safefree(tmpbuf);
                DIE(aTHX_ "Offset outside string");
            }
@@ -2022,38 +2046,40 @@ PP(pp_eof)
     GV *gv;
     IO *io;
     MAGIC *mg;
+    /*
+     * in Perl 5.12 and later, the additional parameter is a bitmask:
+     * 0 = eof
+     * 1 = eof(FH)
+     * 2 = eof()  <- ARGV magic
+     *
+     * I'll rely on the compiler's trace flow analysis to decide whether to
+     * actually assign this out here, or punt it into the only block where it is
+     * used. Doing it out here is DRY on the condition logic.
+     */
+    unsigned int which;
 
-    if (MAXARG)
+    if (MAXARG) {
        gv = PL_last_in_gv = MUTABLE_GV(POPs);  /* eof(FH) */
-    else if (PL_op->op_flags & OPf_SPECIAL)
-       gv = PL_last_in_gv = GvEGV(PL_argvgv);  /* eof() - ARGV magic */
-    else
-       gv = PL_last_in_gv;                     /* eof */
+       which = 1;
+    }
+    else {
+       EXTEND(SP, 1);
+
+       if (PL_op->op_flags & OPf_SPECIAL) {
+           gv = PL_last_in_gv = GvEGVx(PL_argvgv);     /* eof() - ARGV magic */
+           which = 2;
+       }
+       else {
+           gv = PL_last_in_gv;                 /* eof */
+           which = 0;
+       }
+    }
 
     if (!gv)
        RETPUSHNO;
 
     if ((io = GvIO(gv)) && (mg = SvTIED_mg((const SV *)io, PERL_MAGIC_tiedscalar))) {
-       PUSHMARK(SP);
-       XPUSHs(SvTIED_obj(MUTABLE_SV(io), mg));
-       /*
-        * in Perl 5.12 and later, the additional paramter is a bitmask:
-        * 0 = eof
-        * 1 = eof(FH)
-        * 2 = eof()  <- ARGV magic
-        */
-       if (MAXARG)
-           mPUSHi(1);          /* 1 = eof(FH) - simple, explicit FH */
-       else if (PL_op->op_flags & OPf_SPECIAL)
-           mPUSHi(2);          /* 2 = eof()   - ARGV magic */
-       else
-           mPUSHi(0);          /* 0 = eof     - simple, implicit FH */
-       PUTBACK;
-       ENTER;
-       call_method("EOF", G_SCALAR);
-       LEAVE;
-       SPAGAIN;
-       RETURN;
+       return tied_handle_method1("EOF", SP, io, mg, newSVuv(which));
     }
 
     if (!MAXARG && (PL_op->op_flags & OPf_SPECIAL)) {  /* eof() */
@@ -2085,21 +2111,22 @@ PP(pp_tell)
 
     if (MAXARG != 0)
        PL_last_in_gv = MUTABLE_GV(POPs);
+    else
+       EXTEND(SP, 1);
     gv = PL_last_in_gv;
 
     if (gv && (io = GvIO(gv))) {
        MAGIC * const mg = SvTIED_mg((const SV *)io, PERL_MAGIC_tiedscalar);
        if (mg) {
-           PUSHMARK(SP);
-           XPUSHs(SvTIED_obj(MUTABLE_SV(io), mg));
-           PUTBACK;
-           ENTER;
-           call_method("TELL", G_SCALAR);
-           LEAVE;
-           SPAGAIN;
-           RETURN;
+           return tied_handle_method("TELL", SP, io, mg);
        }
     }
+    else if (!gv) {
+       if (!errno)
+           SETERRNO(EBADF,RMS_IFI);
+       PUSHi(-1);
+       RETURN;
+    }
 
 #if LSEEKSIZE > IVSIZE
     PUSHn( do_tell(gv) );
@@ -2125,20 +2152,14 @@ PP(pp_sysseek)
     if (gv && (io = GvIO(gv))) {
        MAGIC * const mg = SvTIED_mg((const SV *)io, PERL_MAGIC_tiedscalar);
        if (mg) {
-           PUSHMARK(SP);
-           XPUSHs(SvTIED_obj(MUTABLE_SV(io), mg));
 #if LSEEKSIZE > IVSIZE
-           mXPUSHn((NV) offset);
+           SV *const offset_sv = newSVnv((NV) offset);
 #else
-           mXPUSHi(offset);
+           SV *const offset_sv = newSViv(offset);
 #endif
-           mXPUSHi(whence);
-           PUTBACK;
-           ENTER;
-           call_method("SEEK", G_SCALAR);
-           LEAVE;
-           SPAGAIN;
-           RETURN;
+
+           return tied_handle_method2("SEEK", SP, io, mg, offset_sv,
+                                      newSViv(whence));
        }
     }
 
@@ -2269,8 +2290,7 @@ PP(pp_ioctl)
     IV retval;
 
     if (!io || !argsv || !IoIFP(io)) {
-       if (ckWARN2(WARN_UNOPENED,WARN_CLOSED))
-           report_evil_fh(gv, io, PL_op->op_type);
+       report_evil_fh(gv);
        SETERRNO(EBADF,RMS_IFI);        /* well, sort of... */
        RETPUSHUNDEF;
     }
@@ -2354,8 +2374,7 @@ PP(pp_flock)
        value = (I32)(PerlLIO_flock(PerlIO_fileno(fp), argtype) >= 0);
     }
     else {
-       if (ckWARN2(WARN_UNOPENED,WARN_CLOSED))
-           report_evil_fh(gv, io, PL_op->op_type);
+       report_evil_fh(gv);
        value = 0;
        SETERRNO(EBADF,RMS_IFI);
     }
@@ -2380,8 +2399,7 @@ PP(pp_socket)
     int fd;
 
     if (!gv || !io) {
-       if (ckWARN2(WARN_UNOPENED,WARN_CLOSED))
-           report_evil_fh(gv, io, PL_op->op_type);
+       report_evil_fh(gv);
        if (io && IoIFP(io))
            do_close(gv, FALSE);
        SETERRNO(EBADF,LIB_INVARG);
@@ -2432,24 +2450,20 @@ PP(pp_sockpair)
     int fd[2];
 
     if (!gv1 || !gv2 || !io1 || !io2) {
-       if (ckWARN2(WARN_UNOPENED,WARN_CLOSED)) {
-           if (!gv1 || !io1)
-               report_evil_fh(gv1, io1, PL_op->op_type);
-           if (!gv2 || !io2)
-               report_evil_fh(gv1, io2, PL_op->op_type);
-       }
-       if (io1 && IoIFP(io1))
-           do_close(gv1, FALSE);
-       if (io2 && IoIFP(io2))
-           do_close(gv2, FALSE);
-       RETPUSHUNDEF;
+       if (!gv1 || !io1)
+           report_evil_fh(gv1);
+       if (!gv2 || !io2)
+           report_evil_fh(gv2);
     }
 
-    if (IoIFP(io1))
+    if (io1 && IoIFP(io1))
        do_close(gv1, FALSE);
-    if (IoIFP(io2))
+    if (io2 && IoIFP(io2))
        do_close(gv2, FALSE);
 
+    if (!io1 || !io2)
+       RETPUSHUNDEF;
+
     TAINT_PROPER("socketpair");
     if (PerlSock_socketpair(domain, type, protocol, fd) < 0)
        RETPUSHUNDEF;
@@ -2489,54 +2503,27 @@ PP(pp_bind)
     GV * const gv = MUTABLE_GV(POPs);
     register IO * const io = GvIOn(gv);
     STRLEN len;
+    const int op_type = PL_op->op_type;
 
     if (!io || !IoIFP(io))
        goto nuts;
 
     addr = SvPV_const(addrsv, len);
-    TAINT_PROPER("bind");
-    if (PerlSock_bind(PerlIO_fileno(IoIFP(io)), (struct sockaddr *)addr, len) >= 0)
-       RETPUSHYES;
-    else
-       RETPUSHUNDEF;
-
-nuts:
-    if (ckWARN(WARN_CLOSED))
-       report_evil_fh(gv, io, PL_op->op_type);
-    SETERRNO(EBADF,SS_IVCHAN);
-    RETPUSHUNDEF;
-#else
-    DIE(aTHX_ PL_no_sock_func, "bind");
-#endif
-}
-
-PP(pp_connect)
-{
-#ifdef HAS_SOCKET
-    dVAR; dSP;
-    SV * const addrsv = POPs;
-    GV * const gv = MUTABLE_GV(POPs);
-    register IO * const io = GvIOn(gv);
-    const char *addr;
-    STRLEN len;
-
-    if (!io || !IoIFP(io))
-       goto nuts;
-
-    addr = SvPV_const(addrsv, len);
-    TAINT_PROPER("connect");
-    if (PerlSock_connect(PerlIO_fileno(IoIFP(io)), (struct sockaddr *)addr, len) >= 0)
+    TAINT_PROPER(PL_op_desc[op_type]);
+    if ((op_type == OP_BIND
+        ? PerlSock_bind(PerlIO_fileno(IoIFP(io)), (struct sockaddr *)addr, len)
+        : PerlSock_connect(PerlIO_fileno(IoIFP(io)), (struct sockaddr *)addr, len))
+       >= 0)
        RETPUSHYES;
     else
        RETPUSHUNDEF;
 
 nuts:
-    if (ckWARN(WARN_CLOSED))
-       report_evil_fh(gv, io, PL_op->op_type);
+    report_evil_fh(gv);
     SETERRNO(EBADF,SS_IVCHAN);
     RETPUSHUNDEF;
 #else
-    DIE(aTHX_ PL_no_sock_func, "connect");
+    DIE(aTHX_ PL_no_sock_func, PL_op_desc[PL_op->op_type]);
 #endif
 }
 
@@ -2557,8 +2544,7 @@ PP(pp_listen)
        RETPUSHUNDEF;
 
 nuts:
-    if (ckWARN(WARN_CLOSED))
-       report_evil_fh(gv, io, PL_op->op_type);
+    report_evil_fh(gv);
     SETERRNO(EBADF,SS_IVCHAN);
     RETPUSHUNDEF;
 #else
@@ -2633,8 +2619,7 @@ PP(pp_accept)
     RETURN;
 
 nuts:
-    if (ckWARN(WARN_CLOSED))
-       report_evil_fh(ggv, ggv ? GvIO(ggv) : 0, PL_op->op_type);
+    report_evil_fh(ggv);
     SETERRNO(EBADF,SS_IVCHAN);
 
 badexit:
@@ -2660,8 +2645,7 @@ PP(pp_shutdown)
     RETURN;
 
 nuts:
-    if (ckWARN(WARN_CLOSED))
-       report_evil_fh(gv, io, PL_op->op_type);
+    report_evil_fh(gv);
     SETERRNO(EBADF,SS_IVCHAN);
     RETPUSHUNDEF;
 #else
@@ -2735,8 +2719,7 @@ PP(pp_ssockopt)
     RETURN;
 
 nuts:
-    if (ckWARN(WARN_CLOSED))
-       report_evil_fh(gv, io, optype);
+    report_evil_fh(gv);
     SETERRNO(EBADF,SS_IVCHAN);
 nuts2:
     RETPUSHUNDEF;
@@ -2799,8 +2782,7 @@ PP(pp_getpeername)
     RETURN;
 
 nuts:
-    if (ckWARN(WARN_CLOSED))
-       report_evil_fh(gv, io, optype);
+    report_evil_fh(gv);
     SETERRNO(EBADF,SS_IVCHAN);
 nuts2:
     RETPUSHUNDEF;
@@ -2826,9 +2808,8 @@ PP(pp_stat)
        if (PL_op->op_type == OP_LSTAT) {
            if (gv != PL_defgv) {
            do_fstat_warning_check:
-               if (ckWARN(WARN_IO))
-                   Perl_warner(aTHX_ packWARN(WARN_IO),
-                       "lstat() on filehandle %s", gv ? GvENAME(gv) : "");
+               Perl_ck_warner(aTHX_ packWARN(WARN_IO),
+                              "lstat() on filehandle %s", gv ? GvENAME(gv) : "");
            } else if (PL_laststype != OP_LSTAT)
                Perl_croak(aTHX_ "The stat preceding lstat() wasn't an lstat");
        }
@@ -2856,8 +2837,7 @@ PP(pp_stat)
         }
 
        if (PL_laststatval < 0) {
-           if (ckWARN2(WARN_UNOPENED,WARN_CLOSED))
-               report_evil_fh(gv, GvIO(gv), PL_op->op_type);
+           report_evil_fh(gv);
            max = 0;
        }
     }
@@ -2953,6 +2933,53 @@ PP(pp_stat)
     RETURN;
 }
 
+#define tryAMAGICftest_MG(chr) STMT_START { \
+       if ( (SvFLAGS(TOPs) & (SVf_ROK|SVs_GMG)) \
+               && S_try_amagic_ftest(aTHX_ chr)) \
+           return NORMAL; \
+    } STMT_END
+
+STATIC bool
+S_try_amagic_ftest(pTHX_ char chr) {
+    dVAR;
+    dSP;
+    SV* const arg = TOPs;
+
+    assert(chr != '?');
+    SvGETMAGIC(arg);
+
+    if ((PL_op->op_flags & OPf_KIDS)
+           && SvAMAGIC(TOPs))
+    {
+       const char tmpchr = chr;
+       const OP *next;
+       SV * const tmpsv = amagic_call(arg,
+                               newSVpvn_flags(&tmpchr, 1, SVs_TEMP),
+                               ftest_amg, AMGf_unary);
+
+       if (!tmpsv)
+           return FALSE;
+
+       SPAGAIN;
+
+       next = PL_op->op_next;
+       if (next->op_type >= OP_FTRREAD &&
+           next->op_type <= OP_FTBINARY &&
+           next->op_private & OPpFT_STACKED
+       ) {
+           if (SvTRUE(tmpsv))
+               /* leave the object alone */
+               return TRUE;
+       }
+
+       SETs(tmpsv);
+       PUTBACK;
+       return TRUE;
+    }
+    return FALSE;
+}
+
+
 /* This macro is used by the stacked filetest operators :
  * if the previous filetest failed, short-circuit and pass its value.
  * Else, discard it from the stack and continue. --rgs
@@ -2981,11 +3008,22 @@ PP(pp_ftrread)
        conditional compiling below much clearer.  */
     I32 use_access = 0;
 #endif
-    int stat_mode = S_IRUSR;
+    Mode_t stat_mode = S_IRUSR;
 
     bool effective = FALSE;
+    char opchar = '?';
     dSP;
 
+    switch (PL_op->op_type) {
+    case OP_FTRREAD:   opchar = 'R'; break;
+    case OP_FTRWRITE:  opchar = 'W'; break;
+    case OP_FTREXEC:   opchar = 'X'; break;
+    case OP_FTEREAD:   opchar = 'r'; break;
+    case OP_FTEWRITE:  opchar = 'w'; break;
+    case OP_FTEEXEC:   opchar = 'x'; break;
+    }
+    tryAMAGICftest_MG(opchar);
+
     STACKED_FTEST_CHECK;
 
     switch (PL_op->op_type) {
@@ -3018,7 +3056,7 @@ PP(pp_ftrread)
        access_mode = W_OK;
 #endif
        stat_mode = S_IWUSR;
-       /* Fall through  */
+       /* fall through */
 
     case OP_FTEREAD:
 #ifndef PERL_EFF_ACCESS
@@ -3064,7 +3102,7 @@ PP(pp_ftrread)
 #endif
     }
 
-    result = my_stat();
+    result = my_stat_flags(0);
     SPAGAIN;
     if (result < 0)
        RETPUSHUNDEF;
@@ -3078,9 +3116,21 @@ PP(pp_ftis)
     dVAR;
     I32 result;
     const int op_type = PL_op->op_type;
+    char opchar = '?';
     dSP;
+
+    switch (op_type) {
+    case OP_FTIS:      opchar = 'e'; break;
+    case OP_FTSIZE:    opchar = 's'; break;
+    case OP_FTMTIME:   opchar = 'M'; break;
+    case OP_FTCTIME:   opchar = 'C'; break;
+    case OP_FTATIME:   opchar = 'A'; break;
+    }
+    tryAMAGICftest_MG(opchar);
+
     STACKED_FTEST_CHECK;
-    result = my_stat();
+
+    result = my_stat_flags(0);
     SPAGAIN;
     if (result < 0)
        RETPUSHUNDEF;
@@ -3116,25 +3166,52 @@ PP(pp_ftrowned)
 {
     dVAR;
     I32 result;
+    char opchar = '?';
     dSP;
 
+    switch (PL_op->op_type) {
+    case OP_FTROWNED:  opchar = 'O'; break;
+    case OP_FTEOWNED:  opchar = 'o'; break;
+    case OP_FTZERO:    opchar = 'z'; break;
+    case OP_FTSOCK:    opchar = 'S'; break;
+    case OP_FTCHR:     opchar = 'c'; break;
+    case OP_FTBLK:     opchar = 'b'; break;
+    case OP_FTFILE:    opchar = 'f'; break;
+    case OP_FTDIR:     opchar = 'd'; break;
+    case OP_FTPIPE:    opchar = 'p'; break;
+    case OP_FTSUID:    opchar = 'u'; break;
+    case OP_FTSGID:    opchar = 'g'; break;
+    case OP_FTSVTX:    opchar = 'k'; break;
+    }
+    tryAMAGICftest_MG(opchar);
+
+    STACKED_FTEST_CHECK;
+
     /* I believe that all these three are likely to be defined on most every
        system these days.  */
 #ifndef S_ISUID
-    if(PL_op->op_type == OP_FTSUID)
+    if(PL_op->op_type == OP_FTSUID) {
+       if ((PL_op->op_flags & OPf_REF) == 0 && (PL_op->op_private & OPpFT_STACKED) == 0)
+           (void) POPs;
        RETPUSHNO;
+    }
 #endif
 #ifndef S_ISGID
-    if(PL_op->op_type == OP_FTSGID)
+    if(PL_op->op_type == OP_FTSGID) {
+       if ((PL_op->op_flags & OPf_REF) == 0 && (PL_op->op_private & OPpFT_STACKED) == 0)
+           (void) POPs;
        RETPUSHNO;
+    }
 #endif
 #ifndef S_ISVTX
-    if(PL_op->op_type == OP_FTSVTX)
+    if(PL_op->op_type == OP_FTSVTX) {
+       if ((PL_op->op_flags & OPf_REF) == 0 && (PL_op->op_private & OPpFT_STACKED) == 0)
+           (void) POPs;
        RETPUSHNO;
+    }
 #endif
 
-    STACKED_FTEST_CHECK;
-    result = my_stat();
+    result = my_stat_flags(0);
     SPAGAIN;
     if (result < 0)
        RETPUSHUNDEF;
@@ -3200,8 +3277,13 @@ PP(pp_ftrowned)
 PP(pp_ftlink)
 {
     dVAR;
-    I32 result = my_lstat();
     dSP;
+    I32 result;
+
+    tryAMAGICftest_MG('l');
+    result = my_lstat_flags(0);
+    SPAGAIN;
+
     if (result < 0)
        RETPUSHUNDEF;
     if (S_ISLNK(PL_statcache.st_mode))
@@ -3216,24 +3298,30 @@ PP(pp_fttty)
     int fd;
     GV *gv;
     SV *tmpsv = NULL;
+    char *name = NULL;
+    STRLEN namelen;
+
+    tryAMAGICftest_MG('t');
 
     STACKED_FTEST_CHECK;
 
     if (PL_op->op_flags & OPf_REF)
        gv = cGVOP_gv;
-    else if (isGV(TOPs))
+    else if (isGV_with_GP(TOPs))
        gv = MUTABLE_GV(POPs);
     else if (SvROK(TOPs) && isGV(SvRV(TOPs)))
        gv = MUTABLE_GV(SvRV(POPs));
-    else
-       gv = gv_fetchsv(tmpsv = POPs, 0, SVt_PVIO);
+    else {
+       tmpsv = POPs;
+       name = SvPV_nomg(tmpsv, namelen);
+       gv = gv_fetchpvn_flags(name, namelen, SvUTF8(tmpsv), SVt_PVIO);
+    }
 
     if (GvIO(gv) && IoIFP(GvIOp(gv)))
        fd = PerlIO_fileno(IoIFP(GvIOp(gv)));
     else if (tmpsv && SvOK(tmpsv)) {
-       const char *tmps = SvPV_nolen_const(tmpsv);
-       if (isDIGIT(*tmps))
-           fd = atoi(tmps);
+       if (isDIGIT(*name))
+           fd = atoi(name);
        else 
            RETPUSHUNDEF;
     }
@@ -3266,11 +3354,13 @@ PP(pp_fttext)
     GV *gv;
     PerlIO *fp;
 
+    tryAMAGICftest_MG(PL_op->op_type == OP_FTTEXT ? 'T' : 'B');
+
     STACKED_FTEST_CHECK;
 
     if (PL_op->op_flags & OPf_REF)
        gv = cGVOP_gv;
-    else if (isGV(TOPs))
+    else if (isGV_with_GP(TOPs))
        gv = MUTABLE_GV(POPs);
     else if (SvROK(TOPs) && isGV(SvRV(TOPs)))
        gv = MUTABLE_GV(SvRV(POPs));
@@ -3319,10 +3409,7 @@ PP(pp_fttext)
                len = 512;
        }
        else {
-           if (ckWARN2(WARN_UNOPENED,WARN_CLOSED)) {
-               gv = cGVOP_gv;
-               report_evil_fh(gv, GvIO(gv), PL_op->op_type);
-           }
+           report_evil_fh(cGVOP_gv);
            SETERRNO(EBADF,RMS_IFI);
            RETPUSHUNDEF;
        }
@@ -3332,7 +3419,7 @@ PP(pp_fttext)
       really_filename:
        PL_statgv = NULL;
        PL_laststype = OP_STAT;
-       sv_setpv(PL_statname, SvPV_nolen_const(sv));
+       sv_setpv(PL_statname, SvPV_nomg_const_nolen(sv));
        if (!(fp = PerlIO_open(SvPVX_const(PL_statname), "r"))) {
            if (ckWARN(WARN_NEWLINE) && strchr(SvPV_nolen_const(PL_statname),
                                               '\n'))
@@ -3466,15 +3553,13 @@ PP(pp_chdir)
                 PUSHi(fchdir(PerlIO_fileno(IoIFP(io))) >= 0);
            }
            else {
-               if (ckWARN2(WARN_UNOPENED,WARN_CLOSED))
-                   report_evil_fh(gv, io, PL_op->op_type);
+               report_evil_fh(gv);
                SETERRNO(EBADF, RMS_IFI);
                PUSHi(0);
            }
         }
        else {
-           if (ckWARN2(WARN_UNOPENED,WARN_CLOSED))
-               report_evil_fh(gv, io, PL_op->op_type);
+           report_evil_fh(gv);
            SETERRNO(EBADF,RMS_IFI);
            PUSHi(0);
        }
@@ -3604,7 +3689,6 @@ PP(pp_readlink)
 #endif
     tmps = POPpconstx;
     len = readlink(tmps, buf, sizeof(buf) - 1);
-    EXTEND(SP, 1);
     if (len < 0)
        RETPUSHUNDEF;
     PUSHp(buf, len);
@@ -3785,9 +3869,10 @@ PP(pp_open_dir)
     if (!io)
        goto nope;
 
-    if ((IoIFP(io) || IoOFP(io)) && ckWARN2(WARN_IO, WARN_DEPRECATED))
-       Perl_warner(aTHX_ packWARN2(WARN_IO, WARN_DEPRECATED),
-               "Opening filehandle %s also as a directory", GvENAME(gv));
+    if ((IoIFP(io) || IoOFP(io)))
+       Perl_ck_warner_d(aTHX_ packWARN2(WARN_IO, WARN_DEPRECATED),
+                        "Opening filehandle %s also as a directory",
+                        GvENAME(gv));
     if (IoDIRP(io))
        PerlDir_close(IoDIRP(io));
     if (!(IoDIRP(io) = PerlDir_open(dirname)))
@@ -3821,10 +3906,8 @@ PP(pp_readdir)
     register IO * const io = GvIOn(gv);
 
     if (!io || !IoDIRP(io)) {
-        if(ckWARN(WARN_IO)) {
-            Perl_warner(aTHX_ packWARN(WARN_IO),
-                "readdir() attempted on invalid dirhandle %s", GvENAME(gv));
-        }
+       Perl_ck_warner(aTHX_ packWARN(WARN_IO),
+                      "readdir() attempted on invalid dirhandle %s", GvENAME(gv));
         goto nope;
     }
 
@@ -3874,10 +3957,8 @@ PP(pp_telldir)
     register IO * const io = GvIOn(gv);
 
     if (!io || !IoDIRP(io)) {
-        if(ckWARN(WARN_IO)) {
-            Perl_warner(aTHX_ packWARN(WARN_IO),
-               "telldir() attempted on invalid dirhandle %s", GvENAME(gv));
-        }
+       Perl_ck_warner(aTHX_ packWARN(WARN_IO),
+                      "telldir() attempted on invalid dirhandle %s", GvENAME(gv));
         goto nope;
     }
 
@@ -3901,10 +3982,8 @@ PP(pp_seekdir)
     register IO * const io = GvIOn(gv);
 
     if (!io || !IoDIRP(io)) {
-       if(ckWARN(WARN_IO)) {
-           Perl_warner(aTHX_ packWARN(WARN_IO),
-                "seekdir() attempted on invalid dirhandle %s", GvENAME(gv));
-        }
+       Perl_ck_warner(aTHX_ packWARN(WARN_IO),
+                      "seekdir() attempted on invalid dirhandle %s", GvENAME(gv));
         goto nope;
     }
     (void)PerlDir_seek(IoDIRP(io), along);
@@ -3927,10 +4006,8 @@ PP(pp_rewinddir)
     register IO * const io = GvIOn(gv);
 
     if (!io || !IoDIRP(io)) {
-       if(ckWARN(WARN_IO)) {
-           Perl_warner(aTHX_ packWARN(WARN_IO),
-               "rewinddir() attempted on invalid dirhandle %s", GvENAME(gv));
-       }
+       Perl_ck_warner(aTHX_ packWARN(WARN_IO),
+                      "rewinddir() attempted on invalid dirhandle %s", GvENAME(gv));
        goto nope;
     }
     (void)PerlDir_rewind(IoDIRP(io));
@@ -3952,10 +4029,8 @@ PP(pp_closedir)
     register IO * const io = GvIOn(gv);
 
     if (!io || !IoDIRP(io)) {
-       if(ckWARN(WARN_IO)) {
-           Perl_warner(aTHX_ packWARN(WARN_IO),
-                "closedir() attempted on invalid dirhandle %s", GvENAME(gv));
-        }
+       Perl_ck_warner(aTHX_ packWARN(WARN_IO),
+                      "closedir() attempted on invalid dirhandle %s", GvENAME(gv));
         goto nope;
     }
 #ifdef VOID_CLOSEDIR
@@ -4027,7 +4102,7 @@ PP(pp_fork)
 
 PP(pp_wait)
 {
-#if (!defined(DOSISH) || defined(OS2) || defined(WIN32)) && !defined(MACOS_TRADITIONAL) && !defined(__LIBCATAMOUNT__)
+#if (!defined(DOSISH) || defined(OS2) || defined(WIN32)) && !defined(__LIBCATAMOUNT__)
     dVAR; dSP; dTARGET;
     Pid_t childpid;
     int argflags;
@@ -4055,7 +4130,7 @@ PP(pp_wait)
 
 PP(pp_waitpid)
 {
-#if (!defined(DOSISH) || defined(OS2) || defined(WIN32)) && !defined(MACOS_TRADITIONAL) && !defined(__LIBCATAMOUNT__)
+#if (!defined(DOSISH) || defined(OS2) || defined(WIN32)) && !defined(__LIBCATAMOUNT__)
     dVAR; dSP; dTARGET;
     const int optype = POPi;
     const Pid_t pid = TOPi;
@@ -4318,6 +4393,7 @@ PP(pp_setpgrp)
     if (MAXARG < 2) {
        pgrp = 0;
        pid = 0;
+       XPUSHi(-1);
     }
     else {
        pgrp = POPi;
@@ -4341,13 +4417,19 @@ PP(pp_setpgrp)
 #endif
 }
 
+#if defined(__GLIBC__) && ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3) || (__GLIBC__ > 2))
+#  define PRIORITY_WHICH_T(which) (__priority_which_t)which
+#else
+#  define PRIORITY_WHICH_T(which) which
+#endif
+
 PP(pp_getpriority)
 {
 #ifdef HAS_GETPRIORITY
     dVAR; dSP; dTARGET;
     const int who = POPi;
     const int which = TOPi;
-    SETi( getpriority(which, who) );
+    SETi( getpriority(PRIORITY_WHICH_T(which), who) );
     RETURN;
 #else
     DIE(aTHX_ PL_no_func, "getpriority()");
@@ -4362,13 +4444,15 @@ PP(pp_setpriority)
     const int who = POPi;
     const int which = TOPi;
     TAINT_PROPER("setpriority");
-    SETi( setpriority(which, who, niceval) >= 0 );
+    SETi( setpriority(PRIORITY_WHICH_T(which), who, niceval) >= 0 );
     RETURN;
 #else
     DIE(aTHX_ PL_no_func, "setpriority()");
 #endif
 }
 
+#undef PRIORITY_WHICH_T
+
 /* Time calls. */
 
 PP(pp_time)
@@ -4420,19 +4504,22 @@ PP(pp_tms)
 #endif /* HAS_TIMES */
 }
 
+/* The 32 bit int year limits the times we can represent to these
+   boundaries with a few days wiggle room to account for time zone
+   offsets
+*/
+/* Sat Jan  3 00:00:00 -2147481748 */
+#define TIME_LOWER_BOUND -67768100567755200.0
+/* Sun Dec 29 12:00:00  2147483647 */
+#define TIME_UPPER_BOUND  67767976233316800.0
+
 PP(pp_gmtime)
 {
     dVAR;
     dSP;
-#ifdef PERL_MICRO
-    Time_t when;
-    const struct tm *err;
-    struct tm tmbuf;
-#else
     Time64_T when;
     struct TM tmbuf;
     struct TM *err;
-#endif
     const char *opname = PL_op->op_type == OP_LOCALTIME ? "localtime" : "gmtime";
     static const char * const dayname[] =
        {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
@@ -4440,48 +4527,41 @@ PP(pp_gmtime)
        {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
 
-#ifdef PERL_MICRO
-    if (MAXARG < 1)
-       (void)time(&when);
-    else
-       when = (Time_t)SvIVx(POPs);
-
-    if (PL_op->op_type == OP_LOCALTIME)
-       err = localtime(&when);
-    else
-       err = gmtime(&when);
-
-    if (!err)
-       tmbuf = *err;
-#else
     if (MAXARG < 1) {
        time_t now;
        (void)time(&now);
        when = (Time64_T)now;
     }
     else {
-       /* XXX POPq uses an SvIV so it won't work with 32 bit integer scalars
-          using a double causes an unfortunate loss of accuracy on high numbers.
-          What we really need is an SvQV.
-       */
-       double input = POPn;
+       NV input = Perl_floor(POPn);
        when = (Time64_T)input;
-       if( when != input ) {
-           Perl_warner(aTHX_ packWARN(WARN_OVERFLOW),
-                       "%s(%.0f) too large", opname, input);
+       if (when != input) {
+           Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
+                          "%s(%.0" NVff ") too large", opname, input);
        }
     }
 
-    if (PL_op->op_type == OP_LOCALTIME)
-        err = localtime64_r(&when, &tmbuf);
-    else
-       err = gmtime64_r(&when, &tmbuf);
-#endif
+    if ( TIME_LOWER_BOUND > when ) {
+       Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
+                      "%s(%.0" NVff ") too small", opname, when);
+       err = NULL;
+    }
+    else if( when > TIME_UPPER_BOUND ) {
+       Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
+                      "%s(%.0" NVff ") too large", opname, when);
+       err = NULL;
+    }
+    else {
+       if (PL_op->op_type == OP_LOCALTIME)
+           err = S_localtime64_r(&when, &tmbuf);
+       else
+           err = S_gmtime64_r(&when, &tmbuf);
+    }
 
-    if( err == NULL ) {
+    if (err == NULL) {
        /* XXX %lld broken for quads */
-       Perl_warner(aTHX_ packWARN(WARN_OVERFLOW),
-                   "%s(%.0f) failed", opname, (double)when);
+       Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
+                      "%s(%.0" NVff ") failed", opname, when);
     }
 
     if (GIMME != G_ARRAY) {    /* scalar context */
@@ -4530,7 +4610,6 @@ PP(pp_alarm)
     int anum;
     anum = POPi;
     anum = alarm((unsigned int)anum);
-    EXTEND(SP, 1);
     if (anum < 0)
        RETPUSHUNDEF;
     PUSHi(anum);
@@ -4666,7 +4745,7 @@ PP(pp_ghostent)
     struct hostent *gethostbyname(Netdb_name_t);
     struct hostent *gethostent(void);
 #endif
-    struct hostent *hent;
+    struct hostent *hent = NULL;
     unsigned long len;
 
     EXTEND(SP, 10);
@@ -5328,7 +5407,11 @@ PP(pp_ggrent)
        PUSHs(sv);
        if (grent) {
            if (which == OP_GGRNAM)
+#if Gid_t_sign <= 0
                sv_setiv(sv, (IV)grent->gr_gid);
+#else
+               sv_setuv(sv, (UV)grent->gr_gid);
+#endif
            else
                sv_setpv(sv, grent->gr_name);
        }
@@ -5344,7 +5427,11 @@ PP(pp_ggrent)
        PUSHs(sv_mortalcopy(&PL_sv_no));
 #endif
 
+#if Gid_t_sign <= 0
        mPUSHi(grent->gr_gid);
+#else
+       mPUSHu(grent->gr_gid);
+#endif
 
 #if !(defined(_CRAYMPP) && defined(USE_REENTRANT_API))
        /* In UNICOS/mk (_CRAYMPP) the multithreading
@@ -5395,7 +5482,8 @@ PP(pp_getlogin)
     EXTEND(SP, 1);
     if (!(tmps = PerlProc_getlogin()))
        RETPUSHUNDEF;
-    PUSHp(tmps, strlen(tmps));
+    sv_setpv_mg(TARG, tmps);
+    PUSHs(TARG);
     RETURN;
 #else
     DIE(aTHX_ PL_no_func, "getlogin");
@@ -5509,6 +5597,7 @@ PP(pp_syscall)
 static int
 fcntl_emulate_flock(int fd, int operation)
 {
+    int res;
     struct flock flock;
 
     switch (operation & ~LOCK_NB) {
@@ -5528,7 +5617,10 @@ fcntl_emulate_flock(int fd, int operation)
     flock.l_whence = SEEK_SET;
     flock.l_start = flock.l_len = (Off_t)0;
 
-    return fcntl(fd, (operation & LOCK_NB) ? F_SETLK : F_SETLKW, &flock);
+    res = fcntl(fd, (operation & LOCK_NB) ? F_SETLK : F_SETLKW, &flock);
+    if (res == -1 && ((errno == EAGAIN) || (errno == EACCES)))
+       errno = EWOULDBLOCK;
+    return res;
 }
 
 #endif /* FCNTL_EMULATE_FLOCK */