This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Re: fchmod, fchown, fchdir
[perl5.git] / pp_sys.c
index e096478..4430789 100644 (file)
--- a/pp_sys.c
+++ b/pp_sys.c
@@ -201,6 +201,15 @@ void endservent(void);
 #undef PERL_EFF_ACCESS_W_OK
 #undef PERL_EFF_ACCESS_X_OK
 
+/* AIX 5.2 and below use mktime for localtime, and defines the edge case
+ * for time 0x7fffffff to be valid only in UTC. AIX 5.3 provides localtime64
+ * available in the 32bit environment, which could warrant Configure
+ * checks in the future.
+ */
+#ifdef  _AIX
+#define LOCALTIME_EDGECASE_BROKEN
+#endif
+
 /* F_OK unused: if stat() cannot find it... */
 
 #if !defined(PERL_EFF_ACCESS_R_OK) && defined(HAS_ACCESS) && defined(EFF_ONLY_OK) && !defined(NO_EFF_ONLY_OK)
@@ -508,7 +517,10 @@ PP(pp_die)
            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)
@@ -555,7 +567,7 @@ PP(pp_open)
        sv = *++MARK;
     }
     else {
-       sv = GvSV(gv);
+       sv = GvSVn(gv);
     }
 
     tmps = SvPV_const(sv, len);
@@ -895,8 +907,7 @@ 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 ) ;
@@ -1095,7 +1106,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];
@@ -1220,8 +1231,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;
@@ -1261,7 +1272,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);
@@ -1321,8 +1333,6 @@ PP(pp_leavewrite)
     SV **newsp;
     I32 gimme;
     register PERL_CONTEXT *cx;
-    PERL_UNUSED_VAR(newsp);
-    PERL_UNUSED_VAR(gimme);
 
     DEBUG_f(PerlIO_printf(Perl_debug_log, "left=%ld, todo=%ld\n",
          (long)IoLINES_LEFT(io), (long)FmLINES(PL_formtarget)));
@@ -1436,6 +1446,8 @@ PP(pp_leavewrite)
     /* bad_ofp: */
     PL_formtarget = PL_bodytarget;
     PUTBACK;
+    PERL_UNUSED_VAR(newsp);
+    PERL_UNUSED_VAR(gimme);
     return cx->blk_sub.retop;
 }
 
@@ -3530,15 +3542,24 @@ PP(pp_ftbinary)
 PP(pp_chdir)
 {
     dSP; dTARGET;
-    const char *tmps;
+    const char *tmps = 0;
+    GV *gv = 0;
     SV **svp;
 
-    if( MAXARG == 1 )
-        tmps = POPpconstx;
-    else
-        tmps = 0;
+    if( MAXARG == 1 ) {
+       SV *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( !gv && (!tmps || !*tmps) ) {
         if (    (svp = hv_fetch(GvHVn(PL_envgv), "HOME", 4, FALSE))
              || (svp = hv_fetch(GvHVn(PL_envgv), "LOGDIR", 6, FALSE))
 #ifdef VMS
@@ -3558,7 +3579,33 @@ PP(pp_chdir)
     }
 
     TAINT_PROPER("chdir");
-    PUSHi( PerlDir_chdir(tmps) >= 0 );
+    if (gv) {
+#ifdef HAS_FCHDIR
+       IO* 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. */
@@ -3712,7 +3759,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; ) {
@@ -4498,6 +4545,44 @@ PP(pp_localtime)
     return pp_gmtime();
 }
 
+#ifdef LOCALTIME_EDGECASE_BROKEN
+static struct tm *S_my_localtime (pTHX_ Time_t *tp)
+{
+    auto time_t     T;
+    auto struct tm *P;
+
+    /* No workarounds in the valid range */
+    if (!tp || *tp < 0x7fff573f || *tp >= 0x80000000)
+       return (localtime (tp));
+
+    /* This edge case is to workaround the undefined behaviour, where the
+     * TIMEZONE makes the time go beyond the defined range.
+     * gmtime (0x7fffffff) => 2038-01-19 03:14:07
+     * If there is a negative offset in TZ, like MET-1METDST, some broken
+     * implementations of localtime () (like AIX 5.2) barf with bogus
+     * return values:
+     * 0x7fffffff gmtime               2038-01-19 03:14:07
+     * 0x7fffffff localtime            1901-12-13 21:45:51
+     * 0x7fffffff mylocaltime          2038-01-19 04:14:07
+     * 0x3c19137f gmtime               2001-12-13 20:45:51
+     * 0x3c19137f localtime            2001-12-13 21:45:51
+     * 0x3c19137f mylocaltime          2001-12-13 21:45:51
+     * 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*
+     */
+    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++;
+    }
+    return (P);
+} /* S_my_localtime */
+#endif
+
 PP(pp_gmtime)
 {
     dSP;
@@ -4519,7 +4604,11 @@ PP(pp_gmtime)
 #endif
 
     if (PL_op->op_type == OP_LOCALTIME)
+#ifdef LOCALTIME_EDGECASE_BROKEN
+       tmbuf = S_my_localtime(aTHX_ &when);
+#else
        tmbuf = localtime(&when);
+#endif
     else
        tmbuf = gmtime(&when);