This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Avoiding source code duplication for the lstat() on filehandle %s
[perl5.git] / pp_sys.c
index 4f4be10..d2a9618 100644 (file)
--- a/pp_sys.c
+++ b/pp_sys.c
@@ -87,7 +87,7 @@ extern int h_errno;
 #ifndef getpwent
   struct passwd *getpwent (void);
 #elif defined (VMS) && defined (my_getpwent)
-  struct passwd *Perl_my_getpwent (void);
+  struct passwd *Perl_my_getpwent (pTHX);
 #endif
 # endif
 #endif
@@ -250,10 +250,10 @@ void endservent(void);
 STATIC int
 S_emulate_eaccess(pTHX_ const char* path, Mode_t mode)
 {
-    Uid_t ruid = getuid();
-    Uid_t euid = geteuid();
-    Gid_t rgid = getgid();
-    Gid_t egid = getegid();
+    const Uid_t ruid = getuid();
+    const Uid_t euid = geteuid();
+    const Gid_t rgid = getgid();
+    const Gid_t egid = getegid();
     int res;
 
     LOCK_CRED_MUTEX;
@@ -339,7 +339,7 @@ PP(pp_backtick)
        mode = "rb";
     else if (PL_op->op_private & OPpOPEN_IN_CRLF)
        mode = "rt";
-    fp = PerlProc_popen((char*)tmps, (char *)mode);
+    fp = PerlProc_popen(tmps, mode);
     if (fp) {
         const char *type = NULL;
        if (PL_curcop->cop_io) {
@@ -378,11 +378,11 @@ PP(pp_backtick)
                SvTAINTED_on(sv);
            }
        }
-       STATUS_NATIVE_SET(PerlProc_pclose(fp));
+       STATUS_NATIVE_CHILD_SET(PerlProc_pclose(fp));
        TAINT;          /* "I believe that this is not gratuitous!" */
     }
     else {
-       STATUS_NATIVE_SET(-1);
+       STATUS_NATIVE_CHILD_SET(-1);
        if (gimme == G_SCALAR)
            RETPUSHUNDEF;
     }
@@ -511,13 +511,16 @@ PP(pp_die)
                    sv_setsv(error,*PL_stack_sp--);
                }
            }
-           DIE_NULL;
+           DIE(aTHX_ Nullch);
        }
        else {
            if (SvPOK(error) && SvCUR(error))
                sv_catpv(error, "\t...propagated");
            tmpsv = error;
-           tmps = SvPV_const(tmpsv, len);
+           if (SvOK(tmpsv))
+               tmps = SvPV_const(tmpsv, len);
+           else
+               tmps = Nullch;
        }
     }
     if (!tmps || !len)
@@ -533,31 +536,33 @@ PP(pp_open)
     dVAR; dSP;
     dMARK; dORIGMARK;
     dTARGET;
-    GV *gv;
     SV *sv;
     IO *io;
     const char *tmps;
     STRLEN len;
-    MAGIC *mg;
     bool  ok;
 
-    gv = (GV *)*++MARK;
+    GV * const gv = (GV *)*++MARK;
+
     if (!isGV(gv))
        DIE(aTHX_ PL_no_usym, "filehandle");
     if ((io = GvIOp(gv)))
        IoFLAGS(GvIOp(gv)) &= ~IOf_UNTAINT;
 
-    if (io && (mg = SvTIED_mg((SV*)io, PERL_MAGIC_tiedscalar))) {
-       /* Method's args are same as ours ... */
-       /* ... except handle is replaced by the object */
-       *MARK-- = SvTIED_obj((SV*)io, mg);
-       PUSHMARK(MARK);
-       PUTBACK;
-       ENTER;
-       call_method("OPEN", G_SCALAR);
-       LEAVE;
-       SPAGAIN;
-       RETURN;
+    if (io) {
+       MAGIC * const mg = SvTIED_mg((SV*)io, PERL_MAGIC_tiedscalar);
+       if (mg) {
+           /* Method's args are same as ours ... */
+           /* ... except handle is replaced by the object */
+           *MARK-- = SvTIED_obj((SV*)io, mg);
+           PUSHMARK(MARK);
+           PUTBACK;
+           ENTER;
+           call_method("OPEN", G_SCALAR);
+           LEAVE;
+           SPAGAIN;
+           RETURN;
+       }
     }
 
     if (MARK < SP) {
@@ -568,7 +573,7 @@ PP(pp_open)
     }
 
     tmps = SvPV_const(sv, len);
-    ok = do_openn(gv, (char *)tmps, len, FALSE, O_RDONLY, 0, Nullfp, MARK+1, (SP-MARK));
+    ok = do_openn(gv, tmps, len, FALSE, O_RDONLY, 0, Nullfp, MARK+1, (SP-MARK));
     SP = ORIGMARK;
     if (ok)
        PUSHi( (I32)PL_forkprocess );
@@ -582,14 +587,9 @@ PP(pp_open)
 PP(pp_close)
 {
     dVAR; dSP;
-    GV *gv;
     IO *io;
     MAGIC *mg;
-
-    if (MAXARG == 0)
-       gv = PL_defoutgv;
-    else
-       gv = (GV*)POPs;
+    GV * const gv = (MAXARG == 0) ? PL_defoutgv : (GV*)POPs;
 
     if (gv && (io = GvIO(gv))
        && (mg = SvTIED_mg((SV*)io, PERL_MAGIC_tiedscalar)))
@@ -612,14 +612,12 @@ PP(pp_pipe_op)
 {
 #ifdef HAS_PIPE
     dSP;
-    GV *rgv;
-    GV *wgv;
     register IO *rstio;
     register IO *wstio;
     int fd[2];
 
-    wgv = (GV*)POPs;
-    rgv = (GV*)POPs;
+    GV * const wgv = (GV*)POPs;
+    GV * const rgv = (GV*)POPs;
 
     if (!rgv || !wgv)
        goto badexit;
@@ -791,7 +789,6 @@ PP(pp_binmode)
 PP(pp_tie)
 {
     dVAR; dSP; dMARK;
-    SV *varsv;
     HV* stash;
     GV *gv;
     SV *sv;
@@ -799,8 +796,8 @@ PP(pp_tie)
     const char *methname;
     int how = PERL_MAGIC_tied;
     U32 items;
+    SV *varsv = *++MARK;
 
-    varsv = *++MARK;
     switch(SvTYPE(varsv)) {
        case SVt_PVHV:
            methname = "TIEHASH";
@@ -890,11 +887,10 @@ PP(pp_untie)
 
     if ((mg = SvTIED_mg(sv, how))) {
        SV * const obj = SvRV(SvTIED_obj(sv, mg));
-       GV *gv;
        CV *cv = NULL;
         if (obj) {
-           if ((gv = gv_fetchmethod_autoload(SvSTASH(obj), "UNTIE", FALSE)) &&
-               isGV(gv) && (cv = GvCV(gv))) {
+           GV * const gv = gv_fetchmethod_autoload(SvSTASH(obj), "UNTIE", FALSE);
+           if (gv && isGV(gv) && (cv = GvCV(gv))) {
               PUSHMARK(SP);
               XPUSHs(SvTIED_obj((SV*)gv, mg));
               XPUSHs(sv_2mortal(newSViv(SvREFCNT(obj)-1)));
@@ -904,12 +900,11 @@ PP(pp_untie)
               LEAVE;
               SPAGAIN;
             }
-           else if (ckWARN(WARN_UNTIE)) {
-              if (mg && SvREFCNT(obj) > 1)
+           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 ) ;
-           }
+           }
         }
     }
     sv_unmagic(sv, how) ;
@@ -943,11 +938,10 @@ PP(pp_dbmopen)
     dPOPPOPssrl;
     HV* stash;
     GV *gv;
-    SV *sv;
 
     HV * const hv = (HV*)POPs;
+    SV * const sv = sv_mortalcopy(&PL_sv_no);
 
-    sv = sv_mortalcopy(&PL_sv_no);
     sv_setpv(sv, "AnyDBM_File");
     stash = gv_stashsv(sv, FALSE);
     if (!stash || !(gv = gv_fetchmethod(stash, "TIEHASH"))) {
@@ -993,11 +987,6 @@ PP(pp_dbmopen)
     RETURN;
 }
 
-PP(pp_dbmclose)
-{
-    return pp_untie();
-}
-
 PP(pp_sselect)
 {
 #ifdef HAS_SELECT
@@ -1028,15 +1017,20 @@ PP(pp_sselect)
 
     SP -= 4;
     for (i = 1; i <= 3; i++) {
-       SV *sv = SP[i];
-       if (SvOK(sv) && SvREADONLY(sv)) {
+       SV * const sv = SP[i];
+       if (!SvOK(sv))
+           continue;
+       if (SvREADONLY(sv)) {
            if (SvIsCOW(sv))
                sv_force_normal_flags(sv, 0);
-           if (SvREADONLY(sv))
+           if (SvREADONLY(sv) && !(SvPOK(sv) && SvCUR(sv) == 0))
                DIE(aTHX_ PL_no_modify);
        }
-       if (!SvPOK(sv))
-           continue;
+       if (!SvPOK(sv)) {
+           if (ckWARN(WARN_MISC))
+                Perl_warner(aTHX_ packWARN(WARN_MISC), "Non-string passed as bitmask");
+           SvPV_force_nolen(sv);       /* force string conversion */
+       }
        j = SvCUR(sv);
        if (maxlen < j)
            maxlen = j;
@@ -1086,12 +1080,11 @@ PP(pp_sselect)
 
     for (i = 1; i <= 3; i++) {
        sv = SP[i];
-       if (!SvOK(sv)) {
+       if (!SvOK(sv) || SvCUR(sv) == 0) {
            fd_sets[i] = 0;
            continue;
        }
-       else if (!SvPOK(sv))
-           SvPV_force_nolen(sv);       /* force string conversion */
+       assert(SvPOK(sv));
        j = SvLEN(sv);
        if (j < growsize) {
            Sv_Grow(sv, growsize);
@@ -1104,7 +1097,7 @@ PP(pp_sselect)
 
 #if BYTEORDER != 0x1234 && BYTEORDER != 0x12345678
        s = SvPVX(sv);
-       New(403, fd_sets[i], growsize, char);
+       Newx(fd_sets[i], growsize, char);
        for (offset = 0; offset < growsize; offset += masksize) {
            for (j = 0, k=ORDERBYTE; j < masksize; j++, (k >>= 4))
                fd_sets[i][j+offset] = s[(k % masksize) + offset];
@@ -1146,10 +1139,7 @@ PP(pp_sselect)
        }
     }
 
-    if (nfound == -1)
-       PUSHs(&PL_sv_undef);
-    else
-       PUSHi(nfound);
+    PUSHi(nfound);
     if (GIMME == G_ARRAY && tbuf) {
        value = (NV)(timebuf.tv_sec) +
                (NV)(timebuf.tv_usec) / 1000000.0;
@@ -1187,7 +1177,7 @@ PP(pp_select)
     if (! hv)
        XPUSHs(&PL_sv_undef);
     else {
-       GV ** const gvp = (GV**)hv_fetch(hv, GvNAME(egv), GvNAMELEN(egv), FALSE);
+       GV * const * const gvp = (GV**)hv_fetch(hv, GvNAME(egv), GvNAMELEN(egv), FALSE);
        if (gvp && *gvp == egv) {
            gv_efullname4(TARG, PL_defoutgv, Nullch, TRUE);
            XPUSHTARG;
@@ -1229,8 +1219,8 @@ PP(pp_getc)
        RETURN;
     }
     if (!gv || do_eof(gv)) { /* make sure we have fp with something */
-       if (ckWARN2(WARN_UNOPENED,WARN_CLOSED)
-               && (!io || (!IoIFP(io) && IoTYPE(io) != IoTYPE_WRONLY)))
+       if ((!io || (!IoIFP(io) && IoTYPE(io) != IoTYPE_WRONLY))
+         && ckWARN2(WARN_UNOPENED,WARN_CLOSED))
            report_evil_fh(gv, io, PL_op->op_type);
        SETERRNO(EBADF,RMS_IFI);
        RETPUSHUNDEF;
@@ -1252,11 +1242,6 @@ PP(pp_getc)
     RETURN;
 }
 
-PP(pp_read)
-{
-    return pp_sysread();
-}
-
 STATIC OP *
 S_doform(pTHX_ CV *cv, GV *gv, OP *retop)
 {
@@ -1270,7 +1255,8 @@ S_doform(pTHX_ CV *cv, GV *gv, OP *retop)
     PUSHBLOCK(cx, CXt_FORMAT, PL_stack_sp);
     PUSHFORMAT(cx);
     cx->blk_sub.retop = retop;
-    PAD_SET_CUR(CvPADLIST(cv), 1);
+    SAVECOMPPAD();
+    PAD_SET_CUR_NOSAVE(CvPADLIST(cv), 1);
 
     setdefout(gv);         /* locally select filehandle so $% et al work */
     return CvSTART(cv);
@@ -1451,16 +1437,12 @@ PP(pp_leavewrite)
 PP(pp_prtf)
 {
     dVAR; dSP; dMARK; dORIGMARK;
-    GV *gv;
     IO *io;
     PerlIO *fp;
     SV *sv;
     MAGIC *mg;
 
-    if (PL_op->op_flags & OPf_STACKED)
-       gv = (GV*)*++MARK;
-    else
-       gv = PL_defoutgv;
+    GV * const gv = (PL_op->op_flags & OPf_STACKED) ? (GV*)*++MARK : PL_defoutgv;
 
     if (gv && (io = GvIO(gv))
        && (mg = SvTIED_mg((SV*)io, PERL_MAGIC_tiedscalar)))
@@ -1534,7 +1516,7 @@ PP(pp_sysopen)
     /* Need TIEHANDLE method ? */
     const char * const tmps = SvPV_const(sv, len);
     /* FIXME? do_open should do const  */
-    if (do_open(gv, (char*)tmps, len, TRUE, mode, perm, Nullfp)) {
+    if (do_open(gv, tmps, len, TRUE, mode, perm, Nullfp)) {
        IoLINES(GvIOp(gv)) = 0;
        PUSHs(&PL_sv_yes);
     }
@@ -1947,11 +1929,6 @@ PP(pp_send)
     RETPUSHUNDEF;
 }
 
-PP(pp_recv)
-{
-    return pp_sysread();
-}
-
 PP(pp_eof)
 {
     dVAR; dSP;
@@ -1968,7 +1945,7 @@ PP(pp_eof)
                if ((IoFLAGS(io) & IOf_START) && av_len(GvAVn(gv)) < 0) {
                    IoLINES(io) = 0;
                    IoFLAGS(io) &= ~IOf_START;
-                   do_open(gv, (char *)"-", 1, FALSE, O_RDONLY, 0, Nullfp);
+                   do_open(gv, "-", 1, FALSE, O_RDONLY, 0, Nullfp);
                    sv_setpvn(GvSV(gv), "-", 1);
                    SvSETMAGIC(GvSV(gv));
                }
@@ -2006,10 +1983,9 @@ PP(pp_tell)
     IO *io;
     MAGIC *mg;
 
-    if (MAXARG == 0)
-       gv = PL_last_in_gv;
-    else
-       gv = PL_last_in_gv = (GV*)POPs;
+    if (MAXARG != 0)
+       PL_last_in_gv = (GV*)POPs;
+    gv = PL_last_in_gv;
 
     if (gv && (io = GvIO(gv))
        && (mg = SvTIED_mg((SV*)io, PERL_MAGIC_tiedscalar)))
@@ -2032,11 +2008,6 @@ PP(pp_tell)
     RETURN;
 }
 
-PP(pp_seek)
-{
-    return pp_sysseek();
-}
-
 PP(pp_sysseek)
 {
     dVAR; dSP;
@@ -2185,11 +2156,6 @@ PP(pp_truncate)
     }
 }
 
-PP(pp_fcntl)
-{
-    return pp_ioctl();
-}
-
 PP(pp_ioctl)
 {
     dSP; dTARGET;
@@ -2634,15 +2600,6 @@ nuts:
 #endif
 }
 
-PP(pp_gsockopt)
-{
-#ifdef HAS_SOCKET
-    return pp_ssockopt();
-#else
-    DIE(aTHX_ PL_no_sock_func, "getsockopt");
-#endif
-}
-
 PP(pp_ssockopt)
 {
 #ifdef HAS_SOCKET
@@ -2683,16 +2640,30 @@ PP(pp_ssockopt)
        PUSHs(sv);
        break;
     case OP_SSOCKOPT: {
-           const char *buf;
+#if defined(__SYMBIAN32__)
+# define SETSOCKOPT_OPTION_VALUE_T void *
+#else
+# define SETSOCKOPT_OPTION_VALUE_T const char *
+#endif
+       /* XXX TODO: We need to have a proper type (a Configure probe,
+        * etc.) for what the C headers think of the third argument of
+        * setsockopt(), the option_value read-only buffer: is it
+        * a "char *", or a "void *", const or not.  Some compilers
+        * don't take kindly to e.g. assuming that "char *" implicitly
+        * promotes to a "void *", or to explicitly promoting/demoting
+        * consts to non/vice versa.  The "const void *" is the SUS
+        * definition, but that does not fly everywhere for the above
+        * reasons. */
+           SETSOCKOPT_OPTION_VALUE_T buf;
            int aint;
            if (SvPOKp(sv)) {
                STRLEN l;
-               buf = SvPV_const(sv, l);
+               buf = (SETSOCKOPT_OPTION_VALUE_T) SvPV_const(sv, l);
                len = l;
            }
            else {
                aint = (int)SvIV(sv);
-               buf = (const char*)&aint;
+               buf = (SETSOCKOPT_OPTION_VALUE_T) &aint;
                len = sizeof(int);
            }
            if (PerlSock_setsockopt(fd, lvl, optname, buf, len) < 0)
@@ -2711,16 +2682,7 @@ nuts2:
     RETPUSHUNDEF;
 
 #else
-    DIE(aTHX_ PL_no_sock_func, "setsockopt");
-#endif
-}
-
-PP(pp_getsockname)
-{
-#ifdef HAS_SOCKET
-    return pp_getpeername();
-#else
-    DIE(aTHX_ PL_no_sock_func, "getsockname");
+    DIE(aTHX_ PL_no_sock_func, PL_op_desc[PL_op->op_type]);
 #endif
 }
 
@@ -2757,7 +2719,7 @@ PP(pp_getpeername)
            static const char nowhere[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
            /* If the call succeeded, make sure we don't have a zeroed port/addr */
            if (((struct sockaddr *)SvPVX_const(sv))->sa_family == AF_INET &&
-               !memcmp((char *)SvPVX_const(sv) + sizeof(u_short), nowhere,
+               !memcmp(SvPVX_const(sv) + sizeof(u_short), nowhere,
                        sizeof(u_short) + sizeof(struct in_addr))) {
                goto nuts2;     
            }
@@ -2784,17 +2746,12 @@ nuts2:
     RETPUSHUNDEF;
 
 #else
-    DIE(aTHX_ PL_no_sock_func, "getpeername");
+    DIE(aTHX_ PL_no_sock_func, PL_op_desc[PL_op->op_type]);
 #endif
 }
 
 /* Stat calls. */
 
-PP(pp_lstat)
-{
-    return pp_stat();
-}
-
 PP(pp_stat)
 {
     dSP;
@@ -2806,6 +2763,7 @@ PP(pp_stat)
        gv = cGVOP_gv;
        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", GvENAME(gv));
@@ -2835,9 +2793,8 @@ PP(pp_stat)
        }
        else if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVGV) {
            gv = (GV*)SvRV(sv);
-           if (PL_op->op_type == OP_LSTAT && ckWARN(WARN_IO))
-               Perl_warner(aTHX_ packWARN(WARN_IO),
-                       "lstat() on filehandle %s", GvENAME(gv));
+           if (PL_op->op_type == OP_LSTAT)
+               goto do_fstat_warning_check;
            goto do_fstat;
        }
        sv_setpv(PL_statname, SvPV_nolen_const(sv));
@@ -3089,245 +3046,133 @@ PP(pp_fteexec)
 PP(pp_ftis)
 {
     I32 result;
+    const int op_type = PL_op->op_type;
     dSP;
     STACKED_FTEST_CHECK;
     result = my_stat();
     SPAGAIN;
     if (result < 0)
        RETPUSHUNDEF;
-    RETPUSHYES;
-}
-
-PP(pp_fteowned)
-{
-    return pp_ftrowned();
-}
-
-PP(pp_ftrowned)
-{
-    I32 result;
-    dSP;
-    STACKED_FTEST_CHECK;
-    result = my_stat();
-    SPAGAIN;
-    if (result < 0)
-       RETPUSHUNDEF;
-    if (PL_statcache.st_uid == (PL_op->op_type == OP_FTEOWNED ?
-                               PL_euid : PL_uid) )
+    if (op_type == OP_FTIS)
        RETPUSHYES;
-    RETPUSHNO;
-}
-
-PP(pp_ftzero)
-{
-    I32 result;
-    dSP;
-    STACKED_FTEST_CHECK;
-    result = my_stat();
-    SPAGAIN;
-    if (result < 0)
-       RETPUSHUNDEF;
-    if (PL_statcache.st_size == 0)
-       RETPUSHYES;
-    RETPUSHNO;
-}
-
-PP(pp_ftsize)
-{
-    I32 result;
-    dSP; dTARGET;
-    STACKED_FTEST_CHECK;
-    result = my_stat();
-    SPAGAIN;
-    if (result < 0)
-       RETPUSHUNDEF;
+    {
+       /* You can't dTARGET inside OP_FTIS, because you'll get
+          "panic: pad_sv po" - the op is not flagged to have a target.  */
+       dTARGET;
+       switch (op_type) {
+       case OP_FTSIZE:
 #if Off_t_size > IVSIZE
-    PUSHn(PL_statcache.st_size);
+           PUSHn(PL_statcache.st_size);
 #else
-    PUSHi(PL_statcache.st_size);
+           PUSHi(PL_statcache.st_size);
 #endif
+           break;
+       case OP_FTMTIME:
+           PUSHn( (((NV)PL_basetime - PL_statcache.st_mtime)) / 86400.0 );
+           break;
+       case OP_FTATIME:
+           PUSHn( (((NV)PL_basetime - PL_statcache.st_atime)) / 86400.0 );
+           break;
+       case OP_FTCTIME:
+           PUSHn( (((NV)PL_basetime - PL_statcache.st_ctime)) / 86400.0 );
+           break;
+       }
+    }
     RETURN;
 }
 
-PP(pp_ftmtime)
-{
-    I32 result;
-    dSP; dTARGET;
-    STACKED_FTEST_CHECK;
-    result = my_stat();
-    SPAGAIN;
-    if (result < 0)
-       RETPUSHUNDEF;
-    PUSHn( (((NV)PL_basetime - PL_statcache.st_mtime)) / 86400.0 );
-    RETURN;
-}
-
-PP(pp_ftatime)
-{
-    I32 result;
-    dSP; dTARGET;
-    STACKED_FTEST_CHECK;
-    result = my_stat();
-    SPAGAIN;
-    if (result < 0)
-       RETPUSHUNDEF;
-    PUSHn( (((NV)PL_basetime - PL_statcache.st_atime)) / 86400.0 );
-    RETURN;
-}
-
-PP(pp_ftctime)
-{
-    I32 result;
-    dSP; dTARGET;
-    STACKED_FTEST_CHECK;
-    result = my_stat();
-    SPAGAIN;
-    if (result < 0)
-       RETPUSHUNDEF;
-    PUSHn( (((NV)PL_basetime - PL_statcache.st_ctime)) / 86400.0 );
-    RETURN;
-}
-
-PP(pp_ftsock)
-{
-    I32 result;
-    dSP;
-    STACKED_FTEST_CHECK;
-    result = my_stat();
-    SPAGAIN;
-    if (result < 0)
-       RETPUSHUNDEF;
-    if (S_ISSOCK(PL_statcache.st_mode))
-       RETPUSHYES;
-    RETPUSHNO;
-}
-
-PP(pp_ftchr)
-{
-    I32 result;
-    dSP;
-    STACKED_FTEST_CHECK;
-    result = my_stat();
-    SPAGAIN;
-    if (result < 0)
-       RETPUSHUNDEF;
-    if (S_ISCHR(PL_statcache.st_mode))
-       RETPUSHYES;
-    RETPUSHNO;
-}
-
-PP(pp_ftblk)
-{
-    I32 result;
-    dSP;
-    STACKED_FTEST_CHECK;
-    result = my_stat();
-    SPAGAIN;
-    if (result < 0)
-       RETPUSHUNDEF;
-    if (S_ISBLK(PL_statcache.st_mode))
-       RETPUSHYES;
-    RETPUSHNO;
-}
-
-PP(pp_ftfile)
+PP(pp_ftrowned)
 {
     I32 result;
     dSP;
-    STACKED_FTEST_CHECK;
-    result = my_stat();
-    SPAGAIN;
-    if (result < 0)
-       RETPUSHUNDEF;
-    if (S_ISREG(PL_statcache.st_mode))
-       RETPUSHYES;
-    RETPUSHNO;
-}
 
-PP(pp_ftdir)
-{
-    I32 result;
-    dSP;
-    STACKED_FTEST_CHECK;
-    result = my_stat();
-    SPAGAIN;
-    if (result < 0)
-       RETPUSHUNDEF;
-    if (S_ISDIR(PL_statcache.st_mode))
-       RETPUSHYES;
-    RETPUSHNO;
-}
+    /* 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)
+       RETPUSHNO;
+#endif
+#ifndef S_ISGID
+    if(PL_op->op_type == OP_FTSGID)
+       RETPUSHNO;
+#endif
+#ifndef S_ISVTX
+    if(PL_op->op_type == OP_FTSVTX)
+       RETPUSHNO;
+#endif
 
-PP(pp_ftpipe)
-{
-    I32 result;
-    dSP;
     STACKED_FTEST_CHECK;
     result = my_stat();
     SPAGAIN;
     if (result < 0)
        RETPUSHUNDEF;
-    if (S_ISFIFO(PL_statcache.st_mode))
-       RETPUSHYES;
-    RETPUSHNO;
-}
-
-PP(pp_ftlink)
-{
-    I32 result = my_lstat();
-    dSP;
-    if (result < 0)
-       RETPUSHUNDEF;
-    if (S_ISLNK(PL_statcache.st_mode))
-       RETPUSHYES;
-    RETPUSHNO;
-}
-
-PP(pp_ftsuid)
-{
-    dSP;
+    switch (PL_op->op_type) {
+    case OP_FTROWNED:
+       if (PL_statcache.st_uid == PL_uid);
+           RETPUSHYES;
+       break;
+    case OP_FTEOWNED:
+       if (PL_statcache.st_uid == PL_euid)
+           RETPUSHYES;
+       break;
+    case OP_FTZERO:
+       if (PL_statcache.st_size == 0)
+           RETPUSHYES;
+       break;
+    case OP_FTSOCK:
+       if (S_ISSOCK(PL_statcache.st_mode))
+           RETPUSHYES;
+       break;
+    case OP_FTCHR:
+       if (S_ISCHR(PL_statcache.st_mode))
+           RETPUSHYES;
+       break;
+    case OP_FTBLK:
+       if (S_ISBLK(PL_statcache.st_mode))
+           RETPUSHYES;
+       break;
+    case OP_FTFILE:
+       if (S_ISREG(PL_statcache.st_mode))
+           RETPUSHYES;
+       break;
+    case OP_FTDIR:
+       if (S_ISDIR(PL_statcache.st_mode))
+           RETPUSHYES;
+       break;
+    case OP_FTPIPE:
+       if (S_ISFIFO(PL_statcache.st_mode))
+           RETPUSHYES;
+       break;
 #ifdef S_ISUID
-    I32 result;
-    STACKED_FTEST_CHECK;
-    result = my_stat();
-    SPAGAIN;
-    if (result < 0)
-       RETPUSHUNDEF;
-    if (PL_statcache.st_mode & S_ISUID)
-       RETPUSHYES;
+    case OP_FTSUID:
+       if (PL_statcache.st_mode & S_ISUID)
+           RETPUSHYES;
+       break;
 #endif
-    RETPUSHNO;
-}
-
-PP(pp_ftsgid)
-{
-    dSP;
 #ifdef S_ISGID
-    I32 result;
-    STACKED_FTEST_CHECK;
-    result = my_stat();
-    SPAGAIN;
-    if (result < 0)
-       RETPUSHUNDEF;
-    if (PL_statcache.st_mode & S_ISGID)
-       RETPUSHYES;
+    case OP_FTSGID:
+       if (PL_statcache.st_mode & S_ISGID)
+           RETPUSHYES;
+       break;
+#endif
+#ifdef S_ISVTX
+    case OP_FTSVTX:
+       if (PL_statcache.st_mode & S_ISVTX)
+           RETPUSHYES;
+       break;
 #endif
+    }
     RETPUSHNO;
 }
 
-PP(pp_ftsvtx)
+PP(pp_ftlink)
 {
+    I32 result = my_lstat();
     dSP;
-#ifdef S_ISVTX
-    I32 result;
-    STACKED_FTEST_CHECK;
-    result = my_stat();
-    SPAGAIN;
     if (result < 0)
        RETPUSHUNDEF;
-    if (PL_statcache.st_mode & S_ISVTX)
+    if (S_ISLNK(PL_statcache.st_mode))
        RETPUSHYES;
-#endif
     RETPUSHNO;
 }
 
@@ -3529,29 +3374,35 @@ PP(pp_fttext)
        RETPUSHYES;
 }
 
-PP(pp_ftbinary)
-{
-    return pp_fttext();
-}
-
 /* File calls. */
 
 PP(pp_chdir)
 {
     dSP; dTARGET;
-    const char *tmps;
-    SV **svp;
+    const char *tmps = 0;
+    GV *gv = NULL;
 
-    if( MAXARG == 1 )
-        tmps = POPpconstx;
-    else
-        tmps = 0;
+    if( MAXARG == 1 ) {
+       SV * const sv = POPs;
+        if (SvTYPE(sv) == SVt_PVGV) {
+           gv = (GV*)sv;
+        }
+       else if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVGV) {
+            gv = (GV*)SvRV(sv);
+        }
+        else {
+           tmps = SvPVx_nolen_const(sv);
+       }
+    }
 
-    if( !tmps || !*tmps ) {
-        if (    (svp = hv_fetch(GvHVn(PL_envgv), "HOME", 4, FALSE))
-             || (svp = hv_fetch(GvHVn(PL_envgv), "LOGDIR", 6, FALSE))
+    if( !gv && (!tmps || !*tmps) ) {
+       HV * const table = GvHVn(PL_envgv);
+       SV **svp;
+
+        if (    (svp = hv_fetch(table, "HOME", 4, FALSE))
+             || (svp = hv_fetch(table, "LOGDIR", 6, FALSE))
 #ifdef VMS
-             || (svp = hv_fetch(GvHVn(PL_envgv), "SYS$LOGIN", 9, FALSE))
+             || (svp = hv_fetch(table, "SYS$LOGIN", 9, FALSE))
 #endif
            )
         {
@@ -3567,7 +3418,33 @@ PP(pp_chdir)
     }
 
     TAINT_PROPER("chdir");
-    PUSHi( PerlDir_chdir(tmps) >= 0 );
+    if (gv) {
+#ifdef HAS_FCHDIR
+       IO* const io = GvIO(gv);
+       if (io) {
+           if (IoIFP(io)) {
+               PUSHi(fchdir(PerlIO_fileno(IoIFP(io))) >= 0);
+           }
+           else if (IoDIRP(io)) {
+#ifdef HAS_DIRFD
+               PUSHi(fchdir(dirfd(IoDIRP(io))) >= 0);
+#else
+               DIE(aTHX_ PL_no_func, "dirfd");
+#endif
+           }
+           else {
+               PUSHi(0);
+           }
+        }
+       else {
+           PUSHi(0);
+       }
+#else
+       DIE(aTHX_ PL_no_func, "fchdir");
+#endif
+    }
+    else 
+        PUSHi( PerlDir_chdir(tmps) >= 0 );
 #ifdef VMS
     /* Clear the DEFAULT element of ENV so we'll get the new value
      * in the future. */
@@ -3578,16 +3455,12 @@ PP(pp_chdir)
 
 PP(pp_chown)
 {
-#ifdef HAS_CHOWN
     dSP; dMARK; dTARGET;
-    I32 value = (I32)apply(PL_op->op_type, MARK, SP);
+    const I32 value = (I32)apply(PL_op->op_type, MARK, SP);
 
     SP = MARK;
     PUSHi(value);
     RETURN;
-#else
-    DIE(aTHX_ PL_no_func, "chown");
-#endif
 }
 
 PP(pp_chroot)
@@ -3603,36 +3476,6 @@ PP(pp_chroot)
 #endif
 }
 
-PP(pp_unlink)
-{
-    dSP; dMARK; dTARGET;
-    I32 value;
-    value = (I32)apply(PL_op->op_type, MARK, SP);
-    SP = MARK;
-    PUSHi(value);
-    RETURN;
-}
-
-PP(pp_chmod)
-{
-    dSP; dMARK; dTARGET;
-    I32 value;
-    value = (I32)apply(PL_op->op_type, MARK, SP);
-    SP = MARK;
-    PUSHi(value);
-    RETURN;
-}
-
-PP(pp_utime)
-{
-    dSP; dMARK; dTARGET;
-    I32 value;
-    value = (I32)apply(PL_op->op_type, MARK, SP);
-    SP = MARK;
-    PUSHi(value);
-    RETURN;
-}
-
 PP(pp_rename)
 {
     dSP; dTARGET;
@@ -3721,7 +3564,7 @@ S_dooneliner(pTHX_ const char *cmd, const char *filename)
     PerlIO *myfp;
     int anum = 1;
 
-    New(666, cmdline, strlen(cmd) + (strlen(filename) * 2) + 10, char);
+    Newx(cmdline, strlen(cmd) + (strlen(filename) * 2) + 10, char);
     strcpy(cmdline, cmd);
     strcat(cmdline, " ");
     for (s = cmdline + strlen(cmdline); *filename; ) {
@@ -4073,7 +3916,9 @@ PP(pp_fork)
 #ifdef THREADS_HAVE_PIDS
        PL_ppid = (IV)getppid();
 #endif
+#ifdef PERL_USES_PL_PIDSTATUS
        hv_clear(PL_pidstatus); /* no kids, so don't wait for 'em */
+#endif
     }
     PUSHi(childpid);
     RETURN;
@@ -4112,9 +3957,9 @@ PP(pp_wait)
     }
 #  if defined(USE_ITHREADS) && defined(PERL_IMPLICIT_SYS)
     /* 0 and -1 are both error returns (the former applies to WNOHANG case) */
-    STATUS_NATIVE_SET((childpid && childpid != -1) ? argflags : -1);
+    STATUS_NATIVE_CHILD_SET((childpid && childpid != -1) ? argflags : -1);
 #  else
-    STATUS_NATIVE_SET((childpid > 0) ? argflags : -1);
+    STATUS_NATIVE_CHILD_SET((childpid > 0) ? argflags : -1);
 #  endif
     XPUSHi(childpid);
     RETURN;
@@ -4144,9 +3989,9 @@ PP(pp_waitpid)
     }
 #  if defined(USE_ITHREADS) && defined(PERL_IMPLICIT_SYS)
     /* 0 and -1 are both error returns (the former applies to WNOHANG case) */
-    STATUS_NATIVE_SET((result && result != -1) ? argflags : -1);
+    STATUS_NATIVE_CHILD_SET((result && result != -1) ? argflags : -1);
 #  else
-    STATUS_NATIVE_SET((result > 0) ? argflags : -1);
+    STATUS_NATIVE_CHILD_SET((result > 0) ? argflags : -1);
 #  endif
     SETi(result);
     RETURN;
@@ -4200,8 +4045,8 @@ PP(pp_system)
            if (did_pipes)
                PerlLIO_close(pp[1]);
 #ifndef PERL_MICRO
-           rsignal_save(SIGINT, SIG_IGN, &ihand);
-           rsignal_save(SIGQUIT, SIG_IGN, &qhand);
+           rsignal_save(SIGINT,  (Sighandler_t) SIG_IGN, &ihand);
+           rsignal_save(SIGQUIT, (Sighandler_t) SIG_IGN, &qhand);
 #endif
            do {
                result = wait4pid(childpid, &status, 0);
@@ -4210,7 +4055,7 @@ PP(pp_system)
            (void)rsignal_restore(SIGINT, &ihand);
            (void)rsignal_restore(SIGQUIT, &qhand);
 #endif
-           STATUS_NATIVE_SET(result == -1 ? -1 : status);
+           STATUS_NATIVE_CHILD_SET(result == -1 ? -1 : status);
            do_execfree();      /* free any memory child malloced on fork */
            SP = ORIGMARK;
            if (did_pipes) {
@@ -4230,7 +4075,7 @@ PP(pp_system)
                    if (n != sizeof(int))
                        DIE(aTHX_ "panic: kid popen errno read");
                    errno = errkid;             /* Propagate errno from kid */
-                   STATUS_CURRENT = -1;
+                   STATUS_NATIVE_CHILD_SET(-1);
                }
            }
            PUSHi(STATUS_CURRENT);
@@ -4258,14 +4103,14 @@ PP(pp_system)
     result = 0;
     if (PL_op->op_flags & OPf_STACKED) {
        SV *really = *++MARK;
-#  if defined(WIN32) || defined(OS2) || defined(SYMBIAN)
+#  if defined(WIN32) || defined(OS2) || defined(__SYMBIAN32__)
        value = (I32)do_aspawn(really, MARK, SP);
 #  else
        value = (I32)do_aspawn(really, (void **)MARK, (void **)SP);
 #  endif
     }
     else if (SP - MARK != 1) {
-#  if defined(WIN32) || defined(OS2) || defined(SYMBIAN)
+#  if defined(WIN32) || defined(OS2) || defined(__SYMBIAN32__)
        value = (I32)do_aspawn(Nullsv, MARK, SP);
 #  else
        value = (I32)do_aspawn(Nullsv, (void **)MARK, (void **)SP);
@@ -4276,7 +4121,7 @@ PP(pp_system)
     }
     if (PL_statusvalue == -1)  /* hint that value must be returned as is */
        result = 1;
-    STATUS_NATIVE_SET(value);
+    STATUS_NATIVE_CHILD_SET(value);
     do_execfree();
     SP = ORIGMARK;
     PUSHi(result ? value : STATUS_CURRENT);
@@ -4335,20 +4180,6 @@ PP(pp_exec)
     RETURN;
 }
 
-PP(pp_kill)
-{
-#ifdef HAS_KILL
-    dSP; dMARK; dTARGET;
-    I32 value;
-    value = (I32)apply(PL_op->op_type, MARK, SP);
-    SP = MARK;
-    PUSHi(value);
-    RETURN;
-#else
-    DIE(aTHX_ PL_no_func, "kill");
-#endif
-}
-
 PP(pp_getppid)
 {
 #ifdef HAS_GETPPID
@@ -4502,11 +4333,6 @@ PP(pp_tms)
 #endif /* HAS_TIMES */
 }
 
-PP(pp_localtime)
-{
-    return pp_gmtime();
-}
-
 #ifdef LOCALTIME_EDGECASE_BROKEN
 static struct tm *S_my_localtime (pTHX_ Time_t *tp)
 {
@@ -4532,14 +4358,16 @@ static struct tm *S_my_localtime (pTHX_ Time_t *tp)
      * Given that legal timezones are typically between GMT-12 and GMT+12
      * we turn back the clock 23 hours before calling the localtime
      * function, and add those to the return value. This will never cause
-     * day wrapping problems, since the edge case is Jan *19*
+     * day wrapping problems, since the edge case is Tue Jan *19*
      */
     T = *tp - 82800; /* 23 hour. allows up to GMT-23 */
     P = localtime (&T);
     P->tm_hour += 23;
     if (P->tm_hour >= 24) {
        P->tm_hour -= 24;
-       P->tm_mday++;
+       P->tm_mday++;   /* 18  -> 19  */
+       P->tm_wday++;   /* Mon -> Tue */
+       P->tm_yday++;   /* 18  -> 19  */
     }
     return (P);
 } /* S_my_localtime */
@@ -4644,21 +4472,6 @@ PP(pp_sleep)
 
 /* Shared memory. */
 
-PP(pp_shmget)
-{
-    return pp_semget();
-}
-
-PP(pp_shmctl)
-{
-    return pp_semctl();
-}
-
-PP(pp_shmread)
-{
-    return pp_shmwrite();
-}
-
 PP(pp_shmwrite)
 {
 #if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
@@ -4674,16 +4487,6 @@ PP(pp_shmwrite)
 
 /* Message passing. */
 
-PP(pp_msgget)
-{
-    return pp_semget();
-}
-
-PP(pp_msgctl)
-{
-    return pp_semctl();
-}
-
 PP(pp_msgsnd)
 {
 #if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
@@ -4762,24 +4565,6 @@ PP(pp_semop)
 
 /* Get system info. */
 
-PP(pp_ghbyname)
-{
-#ifdef HAS_GETHOSTBYNAME
-    return pp_ghostent();
-#else
-    DIE(aTHX_ PL_no_sock_func, "gethostbyname");
-#endif
-}
-
-PP(pp_ghbyaddr)
-{
-#ifdef HAS_GETHOSTBYADDR
-    return pp_ghostent();
-#else
-    DIE(aTHX_ PL_no_sock_func, "gethostbyaddr");
-#endif
-}
-
 PP(pp_ghostent)
 {
 #if defined(HAS_GETHOSTBYNAME) || defined(HAS_GETHOSTBYADDR) || defined(HAS_GETHOSTENT)
@@ -4830,7 +4615,7 @@ PP(pp_ghostent)
            h_errno = PL_reentrant_buffer->_gethostent_errno;
 #   endif
 #endif
-           STATUS_NATIVE_SET(h_errno);
+           STATUS_UNIX_SET(h_errno);
        }
 #endif
 
@@ -4878,24 +4663,6 @@ PP(pp_ghostent)
 #endif
 }
 
-PP(pp_gnbyname)
-{
-#ifdef HAS_GETNETBYNAME
-    return pp_gnetent();
-#else
-    DIE(aTHX_ PL_no_sock_func, "getnetbyname");
-#endif
-}
-
-PP(pp_gnbyaddr)
-{
-#ifdef HAS_GETNETBYADDR
-    return pp_gnetent();
-#else
-    DIE(aTHX_ PL_no_sock_func, "getnetbyaddr");
-#endif
-}
-
 PP(pp_gnetent)
 {
 #if defined(HAS_GETNETBYNAME) || defined(HAS_GETNETBYADDR) || defined(HAS_GETNETENT)
@@ -4941,7 +4708,7 @@ PP(pp_gnetent)
             h_errno = PL_reentrant_buffer->_getnetent_errno;
 #   endif
 #endif
-           STATUS_NATIVE_SET(h_errno);
+           STATUS_UNIX_SET(h_errno);
        }
 #endif
 
@@ -4978,24 +4745,6 @@ PP(pp_gnetent)
 #endif
 }
 
-PP(pp_gpbyname)
-{
-#ifdef HAS_GETPROTOBYNAME
-    return pp_gprotoent();
-#else
-    DIE(aTHX_ PL_no_sock_func, "getprotobyname");
-#endif
-}
-
-PP(pp_gpbynumber)
-{
-#ifdef HAS_GETPROTOBYNUMBER
-    return pp_gprotoent();
-#else
-    DIE(aTHX_ PL_no_sock_func, "getprotobynumber");
-#endif
-}
-
 PP(pp_gprotoent)
 {
 #if defined(HAS_GETPROTOBYNAME) || defined(HAS_GETPROTOBYNUMBER) || defined(HAS_GETPROTOENT)
@@ -5064,24 +4813,6 @@ PP(pp_gprotoent)
 #endif
 }
 
-PP(pp_gsbyname)
-{
-#ifdef HAS_GETSERVBYNAME
-    return pp_gservent();
-#else
-    DIE(aTHX_ PL_no_sock_func, "getservbyname");
-#endif
-}
-
-PP(pp_gsbyport)
-{
-#ifdef HAS_GETSERVBYPORT
-    return pp_gservent();
-#else
-    DIE(aTHX_ PL_no_sock_func, "getservbyport");
-#endif
-}
-
 PP(pp_gservent)
 {
 #if defined(HAS_GETSERVBYNAME) || defined(HAS_GETSERVBYPORT) || defined(HAS_GETSERVENT)
@@ -5266,24 +4997,6 @@ PP(pp_eservent)
 #endif
 }
 
-PP(pp_gpwnam)
-{
-#ifdef HAS_PASSWD
-    return pp_gpwent();
-#else
-    DIE(aTHX_ PL_no_func, "getpwnam");
-#endif
-}
-
-PP(pp_gpwuid)
-{
-#ifdef HAS_PASSWD
-    return pp_gpwent();
-#else
-    DIE(aTHX_ PL_no_func, "getpwuid");
-#endif
-}
-
 PP(pp_gpwent)
 {
 #ifdef HAS_PASSWD
@@ -5516,7 +5229,7 @@ PP(pp_gpwent)
     }
     RETURN;
 #else
-    DIE(aTHX_ PL_no_func, "getpwent");
+    DIE(aTHX_ PL_no_func, PL_op_desc[PL_op->op_type]);
 #endif
 }
 
@@ -5542,24 +5255,6 @@ PP(pp_epwent)
 #endif
 }
 
-PP(pp_ggrnam)
-{
-#ifdef HAS_GROUP
-    return pp_ggrent();
-#else
-    DIE(aTHX_ PL_no_func, "getgrnam");
-#endif
-}
-
-PP(pp_ggrgid)
-{
-#ifdef HAS_GROUP
-    return pp_ggrent();
-#else
-    DIE(aTHX_ PL_no_func, "getgrgid");
-#endif
-}
-
 PP(pp_ggrent)
 {
 #ifdef HAS_GROUP
@@ -5628,7 +5323,7 @@ PP(pp_ggrent)
 
     RETURN;
 #else
-    DIE(aTHX_ PL_no_func, "getgrent");
+    DIE(aTHX_ PL_no_func, PL_op_desc[PL_op->op_type]);
 #endif
 }