This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
PerlIO-scalar: fix 32-bit compiler warning
[perl5.git] / ext / PerlIO-scalar / scalar.xs
index e0f75ac..4c5d2c1 100644 (file)
@@ -6,6 +6,9 @@
 
 #include "perliol.h"
 
+static const char code_point_warning[] =
+ "Strings with code points over 0xFF may not be mapped into in-memory file handles\n";
+
 typedef struct {
     struct _PerlIO base;       /* Base "class" info */
     SV *var;
@@ -13,6 +16,18 @@ typedef struct {
 } PerlIOScalar;
 
 IV
+PerlIOScalar_eof(pTHX_ PerlIO * f)
+{
+    if (PerlIOBase(f)->flags & PERLIO_F_CANREAD) {
+        PerlIOScalar *s = PerlIOSelf(f, PerlIOScalar);
+        STRLEN len;
+        (void)SvPV(s->var, len);
+        return len - (STRLEN)(s->posn) <= 0;
+    }
+    return 1;
+}
+
+static IV
 PerlIOScalar_pushed(pTHX_ PerlIO * f, const char *mode, SV * arg,
                    PerlIO_funcs * tab)
 {
@@ -43,27 +58,34 @@ PerlIOScalar_pushed(pTHX_ PerlIO * f, const char *mode, SV * arg,
        }
     }
     else {
-       s->var = newSVpvn("", 0);
+       s->var = newSVpvs("");
     }
     SvUPGRADE(s->var, SVt_PV);
+
     code = PerlIOBase_pushed(aTHX_ f, mode, Nullsv, tab);
     if (!SvOK(s->var) || (PerlIOBase(f)->flags) & PERLIO_F_TRUNCATE)
     {
        sv_force_normal(s->var);
        SvCUR_set(s->var, 0);
+       if (SvPOK(s->var)) *SvPVX(s->var) = 0;
     }
-    if ((PerlIOBase(f)->flags) & PERLIO_F_APPEND)
-    {
-       sv_force_normal(s->var);
-       s->posn = SvCUR(s->var);
+    if (SvUTF8(s->var) && !sv_utf8_downgrade(s->var, TRUE)) {
+       if (ckWARN(WARN_UTF8))
+           Perl_warner(aTHX_ packWARN(WARN_UTF8), code_point_warning);
+       SETERRNO(EINVAL, SS_IVCHAN);
+       SvREFCNT_dec(s->var);
+       s->var = Nullsv;
+       return -1;
     }
+    if ((PerlIOBase(f)->flags) & PERLIO_F_APPEND)
+       s->posn = SvOK(s->var) ? sv_len(s->var) : 0;
     else
        s->posn = 0;
     SvSETMAGIC(s->var);
     return code;
 }
 
-IV
+static IV
 PerlIOScalar_popped(pTHX_ PerlIO * f)
 {
     PerlIOScalar *s = PerlIOSelf(f, PerlIOScalar);
@@ -74,7 +96,7 @@ PerlIOScalar_popped(pTHX_ PerlIO * f)
     return 0;
 }
 
-IV
+static IV
 PerlIOScalar_close(pTHX_ PerlIO * f)
 {
     IV code = PerlIOBase_close(aTHX_ f);
@@ -82,55 +104,48 @@ PerlIOScalar_close(pTHX_ PerlIO * f)
     return code;
 }
 
-IV
+static IV
 PerlIOScalar_fileno(pTHX_ PerlIO * f)
 {
     PERL_UNUSED_ARG(f);
     return -1;
 }
 
-IV
+static IV
 PerlIOScalar_seek(pTHX_ PerlIO * f, Off_t offset, int whence)
 {
     PerlIOScalar *s = PerlIOSelf(f, PerlIOScalar);
-    STRLEN oldcur;
-    STRLEN newlen;
-
-    SvGETMAGIC(s->var);
-    oldcur = SvCUR(s->var);
+    Off_t new_posn;
 
     switch (whence) {
     case SEEK_SET:
-       s->posn = offset;
+       new_posn = offset;
        break;
     case SEEK_CUR:
-       s->posn = offset + s->posn;
+       new_posn = offset + s->posn;
        break;
     case SEEK_END:
-       s->posn = offset + SvCUR(s->var);
+      {
+       STRLEN oldcur;
+       (void)SvPV(s->var, oldcur);
+       new_posn = offset + oldcur;
        break;
+      }
+    default:
+        SETERRNO(EINVAL, SS_IVCHAN);
+        return -1;
     }
-    if (s->posn < 0) {
+    if (new_posn < 0) {
         if (ckWARN(WARN_LAYER))
            Perl_warner(aTHX_ packWARN(WARN_LAYER), "Offset outside string");
        SETERRNO(EINVAL, SS_IVCHAN);
        return -1;
     }
-    newlen = (STRLEN) s->posn;
-    if (newlen > oldcur) {
-       (void) SvGROW(s->var, newlen);
-       Zero(SvPVX(s->var) + oldcur, newlen - oldcur, char);
-       /* No SvCUR_set(), though.  This is just a seek, not a write. */
-    }
-    else if (!SvPVX(s->var)) {
-       /* ensure there's always a character buffer */
-       (void)SvGROW(s->var,1);
-    }
-    SvPOK_on(s->var);
+    s->posn = new_posn;
     return 0;
 }
 
-Off_t
+static Off_t
 PerlIOScalar_tell(pTHX_ PerlIO * f)
 {
     PerlIOScalar *s = PerlIOSelf(f, PerlIOScalar);
@@ -138,7 +153,7 @@ PerlIOScalar_tell(pTHX_ PerlIO * f)
 }
 
 
-SSize_t
+static SSize_t
 PerlIOScalar_read(pTHX_ PerlIO *f, void *vbuf, Size_t count)
 {
     if (!f)
@@ -146,6 +161,7 @@ PerlIOScalar_read(pTHX_ PerlIO *f, void *vbuf, Size_t count)
     if (!(PerlIOBase(f)->flags & PERLIO_F_CANREAD)) {
        PerlIOBase(f)->flags |= PERLIO_F_ERROR;
        SETERRNO(EBADF, SS_IVCHAN);
+       Perl_PerlIO_save_errno(aTHX_ f);
        return 0;
     }
     {
@@ -153,11 +169,28 @@ PerlIOScalar_read(pTHX_ PerlIO *f, void *vbuf, Size_t count)
        SV *sv = s->var;
        char *p;
        STRLEN len;
-       I32 got;
+        STRLEN got;
        p = SvPV(sv, len);
-       got = len - (STRLEN)(s->posn);
-       if (got <= 0)
+       if (SvUTF8(sv)) {
+           if (sv_utf8_downgrade(sv, TRUE)) {
+               p = SvPV_nomg(sv, len);
+           }
+           else {
+               if (ckWARN(WARN_UTF8))
+                   Perl_warner(aTHX_ packWARN(WARN_UTF8), code_point_warning);
+               SETERRNO(EINVAL, SS_IVCHAN);
+               return -1;
+           }
+       }
+        /* I assume that Off_t is at least as large as len (which 
+         * seems safe) and that the size of the buffer in our SV is
+         * always less than half the size of the address space
+         */
+        assert(sizeof(Off_t) >= sizeof(len));
+        assert(len < ((~(STRLEN)0) >> 1));
+        if ((Off_t)len <= s->posn)
            return 0;
+       got = len - (STRLEN)(s->posn);
        if ((STRLEN)got > (STRLEN)count)
            got = (STRLEN)count;
        Copy(p + (STRLEN)(s->posn), vbuf, got, STDCHAR);
@@ -166,7 +199,7 @@ PerlIOScalar_read(pTHX_ PerlIO *f, void *vbuf, Size_t count)
     }
 }
 
-SSize_t
+static SSize_t
 PerlIOScalar_write(pTHX_ PerlIO * f, const void *vbuf, Size_t count)
 {
     if (PerlIOBase(f)->flags & PERLIO_F_CANWRITE) {
@@ -175,23 +208,52 @@ PerlIOScalar_write(pTHX_ PerlIO * f, const void *vbuf, Size_t count)
        SV *sv = s->var;
        char *dst;
        SvGETMAGIC(sv);
-       sv_force_normal(sv);
+       if (!SvROK(sv)) sv_force_normal(sv);
+       if (SvOK(sv)) SvPV_force_nomg_nolen(sv);
+       if (SvUTF8(sv) && !sv_utf8_downgrade(sv, TRUE)) {
+           if (ckWARN(WARN_UTF8))
+               Perl_warner(aTHX_ packWARN(WARN_UTF8), code_point_warning);
+           SETERRNO(EINVAL, SS_IVCHAN);
+           return 0;
+       }
        if ((PerlIOBase(f)->flags) & PERLIO_F_APPEND) {
-           dst = SvGROW(sv, SvCUR(sv) + count);
+           dst = SvGROW(sv, SvCUR(sv) + count + 1);
            offset = SvCUR(sv);
            s->posn = offset + count;
        }
        else {
-           if ((s->posn + count) > SvCUR(sv))
-               dst = SvGROW(sv, (STRLEN)s->posn + count);
+           STRLEN const cur = SvCUR(sv);
+
+            /* ensure we don't try to create ridiculously large
+             * SVs on small platforms
+             */
+#if Size_t_size < Off_t_size
+            if (s->posn > SSize_t_MAX) {
+#ifdef EFBIG
+                SETERRNO(EFBIG, SS_BUFFEROVF);
+#else
+                SETERRNO(ENOSPC, SS_BUFFEROVF);
+#endif
+                return 0;
+            }
+#endif
+
+           if ((STRLEN)s->posn > cur) {
+               dst = SvGROW(sv, (STRLEN)s->posn + count + 1);
+               Zero(SvPVX(sv) + cur, (STRLEN)s->posn - cur, char);
+           }
+           else if ((s->posn + count) >= cur)
+               dst = SvGROW(sv, (STRLEN)s->posn + count + 1);
            else
                dst = SvPVX(sv);
            offset = s->posn;
            s->posn += count;
        }
        Move(vbuf, dst + offset, count, char);
-       if ((STRLEN) s->posn > SvCUR(sv))
+       if ((STRLEN) s->posn > SvCUR(sv)) {
            SvCUR_set(sv, (STRLEN)s->posn);
+           dst[(STRLEN) s->posn] = 0;
+       }
        SvPOK_on(sv);
        SvSETMAGIC(sv);
        return count;
@@ -200,21 +262,21 @@ PerlIOScalar_write(pTHX_ PerlIO * f, const void *vbuf, Size_t count)
        return 0;
 }
 
-IV
+static IV
 PerlIOScalar_fill(pTHX_ PerlIO * f)
 {
     PERL_UNUSED_ARG(f);
     return -1;
 }
 
-IV
+static IV
 PerlIOScalar_flush(pTHX_ PerlIO * f)
 {
     PERL_UNUSED_ARG(f);
     return 0;
 }
 
-STDCHAR *
+static STDCHAR *
 PerlIOScalar_get_base(pTHX_ PerlIO * f)
 {
     PerlIOScalar *s = PerlIOSelf(f, PerlIOScalar);
@@ -225,7 +287,7 @@ PerlIOScalar_get_base(pTHX_ PerlIO * f)
     return (STDCHAR *) NULL;
 }
 
-STDCHAR *
+static STDCHAR *
 PerlIOScalar_get_ptr(pTHX_ PerlIO * f)
 {
     if (PerlIOBase(f)->flags & PERLIO_F_CANREAD) {
@@ -235,17 +297,14 @@ PerlIOScalar_get_ptr(pTHX_ PerlIO * f)
     return (STDCHAR *) NULL;
 }
 
-SSize_t
+static SSize_t
 PerlIOScalar_get_cnt(pTHX_ PerlIO * f)
 {
     if (PerlIOBase(f)->flags & PERLIO_F_CANREAD) {
        PerlIOScalar *s = PerlIOSelf(f, PerlIOScalar);
        STRLEN len;
-       SvGETMAGIC(s->var);
-       if (isGV_with_GP(s->var))
-           (void)SvPV(s->var,len);
-       else len = SvCUR(s->var);
-       if (len > (STRLEN) s->posn)
+       (void)SvPV(s->var,len);
+       if ((Off_t)len > s->posn)
            return len - (STRLEN)s->posn;
        else
            return 0;
@@ -253,7 +312,7 @@ PerlIOScalar_get_cnt(pTHX_ PerlIO * f)
     return 0;
 }
 
-Size_t
+static Size_t
 PerlIOScalar_bufsiz(pTHX_ PerlIO * f)
 {
     if (PerlIOBase(f)->flags & PERLIO_F_CANREAD) {
@@ -264,19 +323,17 @@ PerlIOScalar_bufsiz(pTHX_ PerlIO * f)
     return 0;
 }
 
-void
+static void
 PerlIOScalar_set_ptrcnt(pTHX_ PerlIO * f, STDCHAR * ptr, SSize_t cnt)
 {
     PerlIOScalar *s = PerlIOSelf(f, PerlIOScalar);
     STRLEN len;
     PERL_UNUSED_ARG(ptr);
-    SvGETMAGIC(s->var);
-    if (isGV_with_GP(s->var)) (void)SvPV(s->var,len);
-    else len = SvCUR(s->var);
+    (void)SvPV(s->var,len);
     s->posn = len - cnt;
 }
 
-PerlIO *
+static PerlIO *
 PerlIOScalar_open(pTHX_ PerlIO_funcs * self, PerlIO_list_t * layers, IV n,
                  const char *mode, int fd, int imode, int perm,
                  PerlIO * f, int narg, SV ** args)
@@ -297,7 +354,7 @@ PerlIOScalar_open(pTHX_ PerlIO_funcs * self, PerlIO_list_t * layers, IV n,
     return NULL;
 }
 
-SV *
+static SV *
 PerlIOScalar_arg(pTHX_ PerlIO * f, CLONE_PARAMS * param, int flags)
 {
     PerlIOScalar *s = PerlIOSelf(f, PerlIOScalar);
@@ -314,20 +371,34 @@ PerlIOScalar_arg(pTHX_ PerlIO * f, CLONE_PARAMS * param, int flags)
     return newRV_noinc(var);
 }
 
-PerlIO *
+static PerlIO *
 PerlIOScalar_dup(pTHX_ PerlIO * f, PerlIO * o, CLONE_PARAMS * param,
                 int flags)
 {
+    /* Duplication causes the scalar layer to be pushed on to clone, caus-
+       ing the cloned scalar to be set to the empty string by
+       PerlIOScalar_pushed.  So set aside our scalar temporarily. */
+    PerlIOScalar * const os = PerlIOSelf(o, PerlIOScalar);
+    PerlIOScalar *fs = NULL; /* avoid "may be used uninitialized" warning */
+    SV * const var = os->var;
+    os->var = newSVpvs("");
     if ((f = PerlIOBase_dup(aTHX_ f, o, param, flags))) {
-       PerlIOScalar *fs = PerlIOSelf(f, PerlIOScalar);
-       PerlIOScalar *os = PerlIOSelf(o, PerlIOScalar);
-       /* var has been set by implicit push */
+       fs = PerlIOSelf(f, PerlIOScalar);
+       /* var has been set by implicit push, so replace it */
+       SvREFCNT_dec(fs->var);
+    }
+    SvREFCNT_dec(os->var);
+    os->var = var;
+    if (f) {
+       SV * const rv = PerlIOScalar_arg(aTHX_ o, param, flags);
+       fs->var = SvREFCNT_inc(SvRV(rv));
+       SvREFCNT_dec(rv);
        fs->posn = os->posn;
     }
     return f;
 }
 
-PERLIO_FUNCS_DECL(PerlIO_scalar) = {
+static PERLIO_FUNCS_DECL(PerlIO_scalar) = {
     sizeof(PerlIO_funcs),
     "scalar",
     sizeof(PerlIOScalar),
@@ -347,7 +418,7 @@ PERLIO_FUNCS_DECL(PerlIO_scalar) = {
     PerlIOScalar_close,
     PerlIOScalar_flush,
     PerlIOScalar_fill,
-    PerlIOBase_eof,
+    PerlIOScalar_eof,
     PerlIOBase_error,
     PerlIOBase_clearerr,
     PerlIOBase_setlinebuf,