Re: [perl #41442] segfault (dead loop) with Encoding, use open :locale, print STDERR
[p5sagit/p5-mst-13.2.git] / ext / PerlIO / encoding / encoding.xs
index dc9bac2..617842f 100644 (file)
@@ -8,6 +8,8 @@
 #include "XSUB.h"
 #define U8 U8
 
+#define OUR_DEFAULT_FB "Encode::PERLQQ"
+
 #if defined(USE_PERLIO) && !defined(USE_SFIO)
 
 /* Define an encoding "layer" in the perliol.h sense.
@@ -30,7 +32,7 @@
    "SUPER::flush.
 
    Note that "flush" is _also_ called for read mode - we still do the
-   (back)-translate so that the the base class's "flush" sees the
+   (back)-translate so that the base class's "flush" sees the
    correct number of encoded chars for positioning the seek
    pointer. (This double translation is the worst performance issue -
    particularly with all-perl encode engine.)
@@ -46,10 +48,10 @@ typedef struct {
     SV *enc;                   /* the encoding object */
     SV *chk;                    /* CHECK in Encode methods */
     int flags;                 /* Flags currently just needs lines */
+    int inEncodeCall;          /* trap recursive encode calls */
 } PerlIOEncode;
 
 #define NEEDS_LINES    1
-#define OUR_DEFAULT_FB "Encode::FB_QUIET"
 
 SV *
 PerlIOEncode_getarg(pTHX_ PerlIO * f, CLONE_PARAMS * param, int flags)
@@ -58,6 +60,9 @@ PerlIOEncode_getarg(pTHX_ PerlIO * f, CLONE_PARAMS * param, int flags)
     SV *sv = &PL_sv_undef;
     if (e->enc) {
        dSP;
+       /* Not 100% sure stack swap is right thing to do during dup ... */
+       PUSHSTACKi(PERLSI_MAGIC);
+       SPAGAIN;
        ENTER;
        SAVETMPS;
        PUSHMARK(sp);
@@ -68,18 +73,24 @@ PerlIOEncode_getarg(pTHX_ PerlIO * f, CLONE_PARAMS * param, int flags)
            sv = newSVsv(POPs);
            PUTBACK;
        }
+       FREETMPS;
+       LEAVE;
+       POPSTACK;
     }
     return sv;
 }
 
 IV
-PerlIOEncode_pushed(pTHX_ PerlIO * f, const char *mode, SV * arg)
+PerlIOEncode_pushed(pTHX_ PerlIO * f, const char *mode, SV * arg, PerlIO_funcs *tab)
 {
     PerlIOEncode *e = PerlIOSelf(f, PerlIOEncode);
     dSP;
-    IV  code = PerlIOBuf_pushed(aTHX_ f, mode, Nullsv);
+    IV  code = PerlIOBuf_pushed(aTHX_ f, mode, Nullsv,tab);
     SV *result = Nullsv;
 
+    PUSHSTACKi(PERLSI_MAGIC);
+    SPAGAIN;
+
     ENTER;
     SAVETMPS;
 
@@ -103,12 +114,13 @@ PerlIOEncode_pushed(pTHX_ PerlIO * f, const char *mode, SV * arg)
        code = -1;
     }
     else {
-#ifdef USE_NEW_SEQUENCE
+
+       /* $enc->renew */
        PUSHMARK(sp);
        XPUSHs(result);
        PUTBACK;
-       if (call_method("new_sequence",G_SCALAR|G_EVAL) != 1 || SvTRUE(ERRSV)) {
-           Perl_warner(aTHX_ packWARN(WARN_IO), "\"%" SVf "\" does not support new_sequence",
+       if (call_method("renew",G_SCALAR|G_EVAL) != 1 || SvTRUE(ERRSV)) {
+           Perl_warner(aTHX_ packWARN(WARN_IO), "\"%" SVf "\" does not support renew method",
                        arg);
        }
        else {
@@ -116,7 +128,6 @@ PerlIOEncode_pushed(pTHX_ PerlIO * f, const char *mode, SV * arg)
            result = POPs;
            PUTBACK;
        }
-#endif
        e->enc = newSVsv(result);
        PUSHMARK(sp);
        XPUSHs(e->enc);
@@ -136,10 +147,12 @@ PerlIOEncode_pushed(pTHX_ PerlIO * f, const char *mode, SV * arg)
        PerlIOBase(f)->flags |= PERLIO_F_UTF8;
     }
 
-    e->chk = newSVsv(get_sv("PerlIO::encoding::check", 0));
+    e->chk = newSVsv(get_sv("PerlIO::encoding::fallback", 0));
+    e->inEncodeCall = 0;
 
     FREETMPS;
     LEAVE;
+    POPSTACK;
     return code;
 }
 
@@ -224,6 +237,8 @@ PerlIOEncode_fill(pTHX_ PerlIO * f)
            Perl_die(aTHX_ "panic: cannot push :perlio for %p",f);
        }
     }
+    PUSHSTACKi(PERLSI_MAGIC);
+    SPAGAIN;
     ENTER;
     SAVETMPS;
   retry:
@@ -242,7 +257,7 @@ PerlIOEncode_fill(pTHX_ PerlIO * f)
        STDCHAR *ptr = PerlIO_get_ptr(n);
        SSize_t use  = (avail >= 0) ? avail : 0;
        SV *uni;
-       char *s;
+       char *s = Nullch;
        STRLEN len = 0;
        e->base.ptr = e->base.end = (STDCHAR *) Nullch;
        (void) PerlIOEncode_get_base(aTHX_ f);
@@ -295,8 +310,8 @@ PerlIOEncode_fill(pTHX_ PerlIO * f)
        }
        else {
            /* Create a "dummy" SV to represent the available data from layer below */
-           if (SvLEN(e->dataSV) && SvPVX(e->dataSV)) {
-               Safefree(SvPVX(e->dataSV));
+           if (SvLEN(e->dataSV) && SvPVX_const(e->dataSV)) {
+               Safefree(SvPVX_mutable(e->dataSV));
            }
            if (use > (SSize_t)e->base.bufsiz) {
                if (e->flags & NEEDS_LINES) {
@@ -308,8 +323,8 @@ PerlIOEncode_fill(pTHX_ PerlIO * f)
               use = e->base.bufsiz;
            }
            }
-           SvPVX(e->dataSV) = (char *) ptr;
-           SvLEN(e->dataSV) = 0;  /* Hands off sv.c - it isn't yours */
+           SvPV_set(e->dataSV, (char *) ptr);
+           SvLEN_set(e->dataSV, 0);  /* Hands off sv.c - it isn't yours */
            SvCUR_set(e->dataSV,use);
            SvPOK_only(e->dataSV);
        }
@@ -363,18 +378,19 @@ PerlIOEncode_fill(pTHX_ PerlIO * f)
            PerlIO_set_ptrcnt(n, ptr+use, (avail-use));
            goto retry;
        }
-       FREETMPS;
-       LEAVE;
-       return code;
     }
     else {
     end_of_file:
+       code = -1;
        if (avail == 0)
            PerlIOBase(f)->flags |= PERLIO_F_EOF;
        else
            PerlIOBase(f)->flags |= PERLIO_F_ERROR;
-       return -1;
     }
+    FREETMPS;
+    LEAVE;
+    POPSTACK;
+    return code;
 }
 
 IV
@@ -383,14 +399,17 @@ PerlIOEncode_flush(pTHX_ PerlIO * f)
     PerlIOEncode *e = PerlIOSelf(f, PerlIOEncode);
     IV code = 0;
 
-    if (e->bufsv && (e->base.ptr > e->base.buf)) {
+    if (e->bufsv) {
        dSP;
        SV *str;
        char *s;
        STRLEN len;
        SSize_t count = 0;
-       if (PerlIOBase(f)->flags & PERLIO_F_WRBUF) {
-           /* Write case encode the buffer and write() to layer below */
+       if ((PerlIOBase(f)->flags & PERLIO_F_WRBUF) && (e->base.ptr > e->base.buf)) {
+           if (e->inEncodeCall) return 0;
+           /* Write case - encode the buffer and write() to layer below */
+           PUSHSTACKi(PERLSI_MAGIC);
+           SPAGAIN;
            ENTER;
            SAVETMPS;
            PUSHMARK(sp);
@@ -400,9 +419,12 @@ PerlIOEncode_flush(pTHX_ PerlIO * f)
            XPUSHs(e->bufsv);
            XPUSHs(e->chk);
            PUTBACK;
+           e->inEncodeCall = 1;
            if (call_method("encode", G_SCALAR) != 1) {
+               e->inEncodeCall = 0;
                Perl_die(aTHX_ "panic: encode did not return a value");
            }
+           e->inEncodeCall = 0;
            SPAGAIN;
            str = POPs;
            PUTBACK;
@@ -413,6 +435,7 @@ PerlIOEncode_flush(pTHX_ PerlIO * f)
            }
            FREETMPS;
            LEAVE;
+           POPSTACK;
            if (PerlIO_flush(PerlIONext(f)) != 0) {
                code = -1;
            }
@@ -422,27 +445,32 @@ PerlIOEncode_flush(pTHX_ PerlIO * f)
                return code;
            }
        }
-       else if (PerlIOBase(f)->flags & PERLIO_F_RDBUF) {
+       else if ((PerlIOBase(f)->flags & PERLIO_F_RDBUF)) {
            /* read case */
            /* if we have any untranslated stuff then unread that first */
+           /* FIXME - unread is fragile is there a better way ? */
            if (e->dataSV && SvCUR(e->dataSV)) {
                s = SvPV(e->dataSV, len);
                count = PerlIO_unread(PerlIONext(f),s,len);
                if ((STRLEN)count != len) {
                    code = -1;
                }
+               SvCUR_set(e->dataSV,0);
            }
            /* See if there is anything left in the buffer */
            if (e->base.ptr < e->base.end) {
+               if (e->inEncodeCall) return 0;
                /* Bother - have unread data.
                   re-encode and unread() to layer below
                 */
+               PUSHSTACKi(PERLSI_MAGIC);
+               SPAGAIN;
                ENTER;
                SAVETMPS;
                str = sv_newmortal();
                sv_upgrade(str, SVt_PV);
-               SvPVX(str) = (char*)e->base.ptr;
-               SvLEN(str) = 0;
+               SvPV_set(str, (char*)e->base.ptr);
+               SvLEN_set(str, 0);
                SvCUR_set(str, e->base.end - e->base.ptr);
                SvPOK_only(str);
                SvUTF8_on(str);
@@ -451,9 +479,12 @@ PerlIOEncode_flush(pTHX_ PerlIO * f)
                XPUSHs(str);
                XPUSHs(e->chk);
                PUTBACK;
+               e->inEncodeCall = 1;
                if (call_method("encode", G_SCALAR) != 1) {
-                    Perl_die(aTHX_ "panic: encode did not return a value");
+                   e->inEncodeCall = 0;
+                   Perl_die(aTHX_ "panic: encode did not return a value");
                }
+               e->inEncodeCall = 0;
                SPAGAIN;
                str = POPs;
                PUTBACK;
@@ -464,6 +495,7 @@ PerlIOEncode_flush(pTHX_ PerlIO * f)
                }
                FREETMPS;
                LEAVE;
+               POPSTACK;
            }
        }
        e->base.ptr = e->base.end = e->base.buf;
@@ -476,9 +508,18 @@ IV
 PerlIOEncode_close(pTHX_ PerlIO * f)
 {
     PerlIOEncode *e = PerlIOSelf(f, PerlIOEncode);
-    IV code = PerlIOBase_close(aTHX_ f);
-
+    IV code;
+    if (PerlIOBase(f)->flags & PERLIO_F_RDBUF) {
+       /* Discard partial character */
+       if (e->dataSV) {
+           SvCUR_set(e->dataSV,0);
+       }
+       /* Don't back decode and unread any pending data */
+       e->base.ptr = e->base.end = e->base.buf;
+    }
+    code = PerlIOBase_close(aTHX_ f);
     if (e->bufsv) {
+       /* This should only fire for write case */
        if (e->base.buf && e->base.ptr > e->base.buf) {
            Perl_croak(aTHX_ "Close with partial character");
        }
@@ -553,12 +594,14 @@ PerlIOEncode_write(pTHX_ PerlIO *f, const void *vbuf, Size_t count)
 }
 
 PerlIO_funcs PerlIO_encode = {
+    sizeof(PerlIO_funcs),
     "encoding",
     sizeof(PerlIOEncode),
     PERLIO_K_BUFFERED|PERLIO_K_DESTRUCT,
     PerlIOEncode_pushed,
     PerlIOEncode_popped,
     PerlIOBuf_open,
+    NULL, /* binmode - always pop */
     PerlIOEncode_getarg,
     PerlIOBase_fileno,
     PerlIOEncode_dup,
@@ -588,13 +631,15 @@ PROTOTYPES: ENABLE
 
 BOOT:
 {
-    SV *chk = get_sv("PerlIO::encoding::check", GV_ADD|GV_ADDMULTI);
+    SV *chk = get_sv("PerlIO::encoding::fallback", GV_ADD|GV_ADDMULTI);
     /*
      * we now "use Encode ()" here instead of
      * PerlIO/encoding.pm.  This avoids SEGV when ":encoding()"
      * is invoked without prior "use Encode". -- dankogai
      */
-    if (!gv_stashpvn("Encode", 6, FALSE)) {
+    PUSHSTACKi(PERLSI_MAGIC);
+    SPAGAIN;
+    if (!get_cv(OUR_DEFAULT_FB, 0)) {
 #if 0
        /* This would just be an irritant now loading works */
        Perl_warner(aTHX_ packWARN(WARN_IO), ":encoding without 'use Encode'");
@@ -619,4 +664,5 @@ BOOT:
 #ifdef PERLIO_LAYERS
     PerlIO_define_layer(aTHX_ &PerlIO_encode);
 #endif
+    POPSTACK;
 }