xref: /openbsd-src/gnu/usr.bin/perl/ext/PerlIO-encoding/encoding.xs (revision 3d61058aa5c692477b6d18acfbbdb653a9930ff9)
143003dfeSmillert #define PERL_NO_GET_CONTEXT
243003dfeSmillert #include "EXTERN.h"
343003dfeSmillert #include "perl.h"
443003dfeSmillert #include "XSUB.h"
543003dfeSmillert #define U8 U8
643003dfeSmillert 
743003dfeSmillert #define OUR_DEFAULT_FB	"Encode::PERLQQ"
8eac174f2Safresh1 #define OUR_STOP_AT_PARTIAL "Encode::STOP_AT_PARTIAL"
9eac174f2Safresh1 #define OUR_LEAVE_SRC "Encode::LEAVE_SRC"
10eac174f2Safresh1 
11eac174f2Safresh1 /* This will be set during BOOT */
12eac174f2Safresh1 static unsigned int encode_stop_at_partial = 0;
13eac174f2Safresh1 static unsigned int encode_leave_src = 0;
1443003dfeSmillert 
15e5157e49Safresh1 #if defined(USE_PERLIO)
1643003dfeSmillert 
1743003dfeSmillert /* Define an encoding "layer" in the perliol.h sense.
1843003dfeSmillert 
1943003dfeSmillert    The layer defined here "inherits" in an object-oriented sense from
2043003dfeSmillert    the "perlio" layer with its PerlIOBuf_* "methods".  The
2143003dfeSmillert    implementation is particularly efficient as until Encode settles
2243003dfeSmillert    down there is no point in tryint to tune it.
2343003dfeSmillert 
2443003dfeSmillert    The layer works by overloading the "fill" and "flush" methods.
2543003dfeSmillert 
2643003dfeSmillert    "fill" calls "SUPER::fill" in perl terms, then calls the encode OO
2743003dfeSmillert    perl API to convert the encoded data to UTF-8 form, then copies it
2843003dfeSmillert    back to the buffer. The "base class's" read methods then see the
2943003dfeSmillert    UTF-8 data.
3043003dfeSmillert 
3143003dfeSmillert    "flush" transforms the UTF-8 data deposited by the "base class's
3243003dfeSmillert    write method in the buffer back into the encoded form using the
3343003dfeSmillert    encode OO perl API, then copies data back into the buffer and calls
3443003dfeSmillert    "SUPER::flush.
3543003dfeSmillert 
3643003dfeSmillert    Note that "flush" is _also_ called for read mode - we still do the
3743003dfeSmillert    (back)-translate so that the base class's "flush" sees the
3843003dfeSmillert    correct number of encoded chars for positioning the seek
3943003dfeSmillert    pointer. (This double translation is the worst performance issue -
4043003dfeSmillert    particularly with all-perl encode engine.)
4143003dfeSmillert 
4243003dfeSmillert */
4343003dfeSmillert 
4443003dfeSmillert #include "perliol.h"
4543003dfeSmillert 
4643003dfeSmillert typedef struct {
4743003dfeSmillert     PerlIOBuf base;		/* PerlIOBuf stuff */
4843003dfeSmillert     SV *bufsv;			/* buffer seen by layers above */
4943003dfeSmillert     SV *dataSV;			/* data we have read from layer below */
5043003dfeSmillert     SV *enc;			/* the encoding object */
5143003dfeSmillert     SV *chk;                    /* CHECK in Encode methods */
5243003dfeSmillert     int flags;			/* Flags currently just needs lines */
5343003dfeSmillert     int inEncodeCall;		/* trap recursive encode calls */
5443003dfeSmillert } PerlIOEncode;
5543003dfeSmillert 
5643003dfeSmillert #define NEEDS_LINES	1
5743003dfeSmillert 
58b8851fccSafresh1 static const MGVTBL PerlIOEncode_tag = { 0, 0, 0, 0, 0, 0, 0, 0 };
59b8851fccSafresh1 
60b8851fccSafresh1 static SV *
6143003dfeSmillert PerlIOEncode_getarg(pTHX_ PerlIO * f, CLONE_PARAMS * param, int flags)
6243003dfeSmillert {
6343003dfeSmillert     PerlIOEncode *e = PerlIOSelf(f, PerlIOEncode);
64b8851fccSafresh1     SV *sv;
6548950c12Ssthen     PERL_UNUSED_ARG(flags);
66b8851fccSafresh1     /* During cloning, return an undef token object so that _pushed() knows
67b8851fccSafresh1      * that it should not call methods and wait for _dup() to actually dup the
68b8851fccSafresh1      * encoding object. */
69b8851fccSafresh1     if (param) {
70b8851fccSafresh1 	sv = newSV(0);
71b8851fccSafresh1 	sv_magicext(sv, NULL, PERL_MAGIC_ext, &PerlIOEncode_tag, 0, 0);
72b8851fccSafresh1 	return sv;
73b8851fccSafresh1     }
74b8851fccSafresh1     sv = &PL_sv_undef;
7543003dfeSmillert     if (e->enc) {
7643003dfeSmillert 	dSP;
7743003dfeSmillert 	/* Not 100% sure stack swap is right thing to do during dup ... */
7843003dfeSmillert 	PUSHSTACKi(PERLSI_MAGIC);
7943003dfeSmillert 	ENTER;
8043003dfeSmillert 	SAVETMPS;
8143003dfeSmillert 	PUSHMARK(sp);
8243003dfeSmillert 	XPUSHs(e->enc);
8343003dfeSmillert 	PUTBACK;
8443003dfeSmillert 	if (call_method("name", G_SCALAR) == 1) {
8543003dfeSmillert 	    SPAGAIN;
8643003dfeSmillert 	    sv = newSVsv(POPs);
8743003dfeSmillert 	    PUTBACK;
8843003dfeSmillert 	}
8943003dfeSmillert 	FREETMPS;
9043003dfeSmillert 	LEAVE;
9143003dfeSmillert 	POPSTACK;
9243003dfeSmillert     }
9343003dfeSmillert     return sv;
9443003dfeSmillert }
9543003dfeSmillert 
96b8851fccSafresh1 static IV
9743003dfeSmillert PerlIOEncode_pushed(pTHX_ PerlIO * f, const char *mode, SV * arg, PerlIO_funcs *tab)
9843003dfeSmillert {
9943003dfeSmillert     PerlIOEncode *e = PerlIOSelf(f, PerlIOEncode);
10043003dfeSmillert     dSP;
10143003dfeSmillert     IV  code = PerlIOBuf_pushed(aTHX_ f, mode, Nullsv,tab);
10243003dfeSmillert     SV *result = Nullsv;
10343003dfeSmillert 
104b8851fccSafresh1     if (SvTYPE(arg) >= SVt_PVMG
105b8851fccSafresh1 		&& mg_findext(arg, PERL_MAGIC_ext, &PerlIOEncode_tag)) {
106b8851fccSafresh1 	e->enc = NULL;
107b8851fccSafresh1 	e->chk = NULL;
108b8851fccSafresh1 	e->inEncodeCall = 0;
109b8851fccSafresh1 	return code;
110b8851fccSafresh1     }
111b8851fccSafresh1 
11243003dfeSmillert     PUSHSTACKi(PERLSI_MAGIC);
11343003dfeSmillert     ENTER;
11443003dfeSmillert     SAVETMPS;
11543003dfeSmillert 
11643003dfeSmillert     PUSHMARK(sp);
11743003dfeSmillert     XPUSHs(arg);
11843003dfeSmillert     PUTBACK;
11943003dfeSmillert     if (call_pv("Encode::find_encoding", G_SCALAR) != 1) {
12043003dfeSmillert 	/* should never happen */
12143003dfeSmillert 	Perl_die(aTHX_ "Encode::find_encoding did not return a value");
12243003dfeSmillert 	return -1;
12343003dfeSmillert     }
12443003dfeSmillert     SPAGAIN;
12543003dfeSmillert     result = POPs;
12643003dfeSmillert     PUTBACK;
12743003dfeSmillert 
12843003dfeSmillert     if (!SvROK(result) || !SvOBJECT(SvRV(result))) {
12943003dfeSmillert 	e->enc = Nullsv;
1300dc2eaceSmillert         if (ckWARN_d(WARN_IO))
13143003dfeSmillert             Perl_warner(aTHX_ packWARN(WARN_IO), "Cannot find encoding \"%" SVf "\"",
13243003dfeSmillert                     arg);
13343003dfeSmillert 	errno = EINVAL;
13443003dfeSmillert 	code = -1;
13543003dfeSmillert     }
13643003dfeSmillert     else {
13743003dfeSmillert 
13843003dfeSmillert        /* $enc->renew */
13943003dfeSmillert 	PUSHMARK(sp);
14043003dfeSmillert 	XPUSHs(result);
14143003dfeSmillert 	PUTBACK;
14243003dfeSmillert 	if (call_method("renew",G_SCALAR|G_EVAL) != 1 || SvTRUE(ERRSV)) {
1430dc2eaceSmillert             if (ckWARN_d(WARN_IO))
14443003dfeSmillert                 Perl_warner(aTHX_ packWARN(WARN_IO), "\"%" SVf "\" does not support renew method",
14543003dfeSmillert                         arg);
14643003dfeSmillert 	}
14743003dfeSmillert 	else {
14843003dfeSmillert 	    SPAGAIN;
14943003dfeSmillert 	    result = POPs;
15043003dfeSmillert 	    PUTBACK;
15143003dfeSmillert 	}
15243003dfeSmillert 	e->enc = newSVsv(result);
15343003dfeSmillert 	PUSHMARK(sp);
15443003dfeSmillert 	XPUSHs(e->enc);
15543003dfeSmillert 	PUTBACK;
15643003dfeSmillert 	if (call_method("needs_lines",G_SCALAR|G_EVAL) != 1 || SvTRUE(ERRSV)) {
1570dc2eaceSmillert             if (ckWARN_d(WARN_IO))
15843003dfeSmillert                 Perl_warner(aTHX_ packWARN(WARN_IO), "\"%" SVf "\" does not support needs_lines",
15943003dfeSmillert 			arg);
16043003dfeSmillert 	}
16143003dfeSmillert 	else {
16243003dfeSmillert 	    SPAGAIN;
16343003dfeSmillert 	    result = POPs;
16443003dfeSmillert 	    PUTBACK;
16543003dfeSmillert 	    if (SvTRUE(result)) {
16643003dfeSmillert 		e->flags |= NEEDS_LINES;
16743003dfeSmillert 	    }
16843003dfeSmillert 	}
16943003dfeSmillert 	PerlIOBase(f)->flags |= PERLIO_F_UTF8;
17043003dfeSmillert     }
17143003dfeSmillert 
17243003dfeSmillert     e->chk = newSVsv(get_sv("PerlIO::encoding::fallback", 0));
173eac174f2Safresh1     if (SvROK(e->chk))
174eac174f2Safresh1         Perl_croak(aTHX_ "PerlIO::encoding::fallback must be an integer");
175eac174f2Safresh1     SvUV_set(e->chk, ((SvUV(e->chk) & ~encode_leave_src) | encode_stop_at_partial));
17643003dfeSmillert     e->inEncodeCall = 0;
17743003dfeSmillert 
17843003dfeSmillert     FREETMPS;
17943003dfeSmillert     LEAVE;
18043003dfeSmillert     POPSTACK;
18143003dfeSmillert     return code;
18243003dfeSmillert }
18343003dfeSmillert 
184b8851fccSafresh1 static IV
18543003dfeSmillert PerlIOEncode_popped(pTHX_ PerlIO * f)
18643003dfeSmillert {
18743003dfeSmillert     PerlIOEncode *e = PerlIOSelf(f, PerlIOEncode);
18843003dfeSmillert     if (e->enc) {
18943003dfeSmillert 	SvREFCNT_dec(e->enc);
19043003dfeSmillert 	e->enc = Nullsv;
19143003dfeSmillert     }
19243003dfeSmillert     if (e->bufsv) {
19343003dfeSmillert 	SvREFCNT_dec(e->bufsv);
19443003dfeSmillert 	e->bufsv = Nullsv;
19543003dfeSmillert     }
19643003dfeSmillert     if (e->dataSV) {
19743003dfeSmillert 	SvREFCNT_dec(e->dataSV);
19843003dfeSmillert 	e->dataSV = Nullsv;
19943003dfeSmillert     }
20043003dfeSmillert     if (e->chk) {
20143003dfeSmillert 	SvREFCNT_dec(e->chk);
20243003dfeSmillert 	e->chk = Nullsv;
20343003dfeSmillert     }
20443003dfeSmillert     return 0;
20543003dfeSmillert }
20643003dfeSmillert 
207b8851fccSafresh1 static STDCHAR *
20843003dfeSmillert PerlIOEncode_get_base(pTHX_ PerlIO * f)
20943003dfeSmillert {
21043003dfeSmillert     PerlIOEncode *e = PerlIOSelf(f, PerlIOEncode);
21143003dfeSmillert     if (!e->base.bufsiz)
21243003dfeSmillert 	e->base.bufsiz = 1024;
21343003dfeSmillert     if (!e->bufsv) {
21443003dfeSmillert 	e->bufsv = newSV(e->base.bufsiz);
2159f11ffb7Safresh1 	SvPVCLEAR(e->bufsv);
21643003dfeSmillert     }
21743003dfeSmillert     e->base.buf = (STDCHAR *) SvPVX(e->bufsv);
21843003dfeSmillert     if (!e->base.ptr)
21943003dfeSmillert 	e->base.ptr = e->base.buf;
22043003dfeSmillert     if (!e->base.end)
22143003dfeSmillert 	e->base.end = e->base.buf;
22243003dfeSmillert     if (e->base.ptr < e->base.buf
22343003dfeSmillert 	|| e->base.ptr > e->base.buf + SvLEN(e->bufsv)) {
22443003dfeSmillert 	Perl_warn(aTHX_ " ptr %p(%p)%p", e->base.buf, e->base.ptr,
22543003dfeSmillert 		  e->base.buf + SvLEN(e->bufsv));
22643003dfeSmillert 	abort();
22743003dfeSmillert     }
22843003dfeSmillert     if (SvLEN(e->bufsv) < e->base.bufsiz) {
22943003dfeSmillert 	SSize_t poff = e->base.ptr - e->base.buf;
23043003dfeSmillert 	SSize_t eoff = e->base.end - e->base.buf;
23143003dfeSmillert 	e->base.buf = (STDCHAR *) SvGROW(e->bufsv, e->base.bufsiz);
23243003dfeSmillert 	e->base.ptr = e->base.buf + poff;
23343003dfeSmillert 	e->base.end = e->base.buf + eoff;
23443003dfeSmillert     }
23543003dfeSmillert     if (e->base.ptr < e->base.buf
23643003dfeSmillert 	|| e->base.ptr > e->base.buf + SvLEN(e->bufsv)) {
23743003dfeSmillert 	Perl_warn(aTHX_ " ptr %p(%p)%p", e->base.buf, e->base.ptr,
23843003dfeSmillert 		  e->base.buf + SvLEN(e->bufsv));
23943003dfeSmillert 	abort();
24043003dfeSmillert     }
24143003dfeSmillert     return e->base.buf;
24243003dfeSmillert }
24343003dfeSmillert 
244b8851fccSafresh1 static IV
24543003dfeSmillert PerlIOEncode_fill(pTHX_ PerlIO * f)
24643003dfeSmillert {
24743003dfeSmillert     PerlIOEncode *e = PerlIOSelf(f, PerlIOEncode);
24843003dfeSmillert     dSP;
24943003dfeSmillert     IV code = 0;
25043003dfeSmillert     PerlIO *n;
25143003dfeSmillert     SSize_t avail;
25243003dfeSmillert 
25343003dfeSmillert     if (PerlIO_flush(f) != 0)
25443003dfeSmillert 	return -1;
25543003dfeSmillert     n  = PerlIONext(f);
25643003dfeSmillert     if (!PerlIO_fast_gets(n)) {
25743003dfeSmillert 	/* Things get too messy if we don't have a buffer layer
25843003dfeSmillert 	   push a :perlio to do the job */
25943003dfeSmillert 	char mode[8];
26043003dfeSmillert 	n  = PerlIO_push(aTHX_ n, &PerlIO_perlio, PerlIO_modestr(f,mode), Nullsv);
26143003dfeSmillert 	if (!n) {
26243003dfeSmillert 	    Perl_die(aTHX_ "panic: cannot push :perlio for %p",f);
26343003dfeSmillert 	}
26443003dfeSmillert     }
26543003dfeSmillert     PUSHSTACKi(PERLSI_MAGIC);
26643003dfeSmillert     ENTER;
26743003dfeSmillert     SAVETMPS;
26843003dfeSmillert   retry:
26943003dfeSmillert     avail = PerlIO_get_cnt(n);
27043003dfeSmillert     if (avail <= 0) {
27143003dfeSmillert 	avail = PerlIO_fill(n);
27243003dfeSmillert 	if (avail == 0) {
27343003dfeSmillert 	    avail = PerlIO_get_cnt(n);
27443003dfeSmillert 	}
27543003dfeSmillert 	else {
27643003dfeSmillert 	    if (!PerlIO_error(n) && PerlIO_eof(n))
27743003dfeSmillert 		avail = 0;
27843003dfeSmillert 	}
27943003dfeSmillert     }
28043003dfeSmillert     if (avail > 0 || (e->flags & NEEDS_LINES)) {
28143003dfeSmillert 	STDCHAR *ptr = PerlIO_get_ptr(n);
28243003dfeSmillert 	SSize_t use  = (avail >= 0) ? avail : 0;
28343003dfeSmillert 	SV *uni;
28443003dfeSmillert 	char *s = NULL;
28543003dfeSmillert 	STRLEN len = 0;
28643003dfeSmillert 	e->base.ptr = e->base.end = (STDCHAR *) NULL;
28743003dfeSmillert 	(void) PerlIOEncode_get_base(aTHX_ f);
28843003dfeSmillert 	if (!e->dataSV)
289*3d61058aSafresh1 	    e->dataSV = newSV_type(SVt_PV);
290*3d61058aSafresh1 	else if (SvTYPE(e->dataSV) < SVt_PV) {
29143003dfeSmillert 	    sv_upgrade(e->dataSV,SVt_PV);
29243003dfeSmillert 	}
29343003dfeSmillert 	if (e->flags & NEEDS_LINES) {
29443003dfeSmillert 	    /* Encoding needs whole lines (e.g. iso-2022-*)
29543003dfeSmillert 	       search back from end of available data for
29643003dfeSmillert 	       and line marker
29743003dfeSmillert 	     */
29843003dfeSmillert 	    STDCHAR *nl = ptr+use-1;
29943003dfeSmillert 	    while (nl >= ptr) {
30043003dfeSmillert 		if (*nl == '\n') {
30143003dfeSmillert 		    break;
30243003dfeSmillert 		}
30343003dfeSmillert 		nl--;
30443003dfeSmillert 	    }
30543003dfeSmillert 	    if (nl >= ptr && *nl == '\n') {
30643003dfeSmillert 		/* found a line - take up to and including that */
30743003dfeSmillert 		use = (nl+1)-ptr;
30843003dfeSmillert 	    }
30943003dfeSmillert 	    else if (avail > 0) {
31043003dfeSmillert 		/* No line, but not EOF - append avail to the pending data */
31143003dfeSmillert 		sv_catpvn(e->dataSV, (char*)ptr, use);
31243003dfeSmillert 		PerlIO_set_ptrcnt(n, ptr+use, 0);
31343003dfeSmillert 		goto retry;
31443003dfeSmillert 	    }
31543003dfeSmillert 	    else if (!SvCUR(e->dataSV)) {
31643003dfeSmillert 		goto end_of_file;
31743003dfeSmillert 	    }
31843003dfeSmillert 	}
3199f11ffb7Safresh1 	if (!SvCUR(e->dataSV))
3209f11ffb7Safresh1 	    SvPVCLEAR(e->dataSV);
32143003dfeSmillert 	if (use + SvCUR(e->dataSV) > e->base.bufsiz) {
32243003dfeSmillert 	    if (e->flags & NEEDS_LINES) {
32343003dfeSmillert 		/* Have to grow buffer */
32443003dfeSmillert 		e->base.bufsiz = use + SvCUR(e->dataSV);
32543003dfeSmillert 		PerlIOEncode_get_base(aTHX_ f);
32643003dfeSmillert 	    }
32743003dfeSmillert 	    else {
32843003dfeSmillert 		use = e->base.bufsiz - SvCUR(e->dataSV);
32943003dfeSmillert 	    }
33043003dfeSmillert 	}
33143003dfeSmillert 	sv_catpvn(e->dataSV,(char*)ptr,use);
33243003dfeSmillert 	SvUTF8_off(e->dataSV);
33343003dfeSmillert 	PUSHMARK(sp);
33443003dfeSmillert 	XPUSHs(e->enc);
33543003dfeSmillert 	XPUSHs(e->dataSV);
33643003dfeSmillert 	XPUSHs(e->chk);
33743003dfeSmillert 	PUTBACK;
33843003dfeSmillert 	if (call_method("decode", G_SCALAR) != 1) {
33943003dfeSmillert 	    Perl_die(aTHX_ "panic: decode did not return a value");
34043003dfeSmillert 	}
34143003dfeSmillert 	SPAGAIN;
34243003dfeSmillert 	uni = POPs;
34343003dfeSmillert 	PUTBACK;
344e9ce3842Safresh1 	/* No cows allowed. */
345e9ce3842Safresh1 	if (SvTHINKFIRST(e->dataSV)) SvPV_force_nolen(e->dataSV);
34643003dfeSmillert 	/* Now get translated string (forced to UTF-8) and use as buffer */
34743003dfeSmillert 	if (SvPOK(uni)) {
34843003dfeSmillert 	    s = SvPVutf8(uni, len);
34943003dfeSmillert #ifdef PARANOID_ENCODE_CHECKS
35043003dfeSmillert 	    if (len && !is_utf8_string((U8*)s,len)) {
35143003dfeSmillert 		Perl_warn(aTHX_ "panic: decode did not return UTF-8 '%.*s'",(int) len,s);
35243003dfeSmillert 	    }
35343003dfeSmillert #endif
35443003dfeSmillert 	}
35543003dfeSmillert 	if (len > 0) {
35643003dfeSmillert 	    /* Got _something */
35743003dfeSmillert 	    /* if decode gave us back dataSV then data may vanish when
35843003dfeSmillert 	       we do ptrcnt adjust - so take our copy now.
35943003dfeSmillert 	       (The copy is a pain - need a put-it-here option for decode.)
36043003dfeSmillert 	     */
36143003dfeSmillert 	    sv_setpvn(e->bufsv,s,len);
36243003dfeSmillert 	    e->base.ptr = e->base.buf = (STDCHAR*)SvPVX(e->bufsv);
36343003dfeSmillert 	    e->base.end = e->base.ptr + SvCUR(e->bufsv);
36443003dfeSmillert 	    PerlIOBase(f)->flags |= PERLIO_F_RDBUF;
36543003dfeSmillert 	    SvUTF8_on(e->bufsv);
36643003dfeSmillert 
36743003dfeSmillert 	    /* Adjust ptr/cnt not taking anything which
36843003dfeSmillert 	       did not translate - not clear this is a win */
36943003dfeSmillert 	    /* compute amount we took */
370e9ce3842Safresh1 	    if (!SvPOKp(e->dataSV)) (void)SvPV_force_nolen(e->dataSV);
37143003dfeSmillert 	    use -= SvCUR(e->dataSV);
37243003dfeSmillert 	    PerlIO_set_ptrcnt(n, ptr+use, (avail-use));
37356d68f1eSafresh1 	    /* and as we did not take it, it isn't pending */
37443003dfeSmillert 	    SvCUR_set(e->dataSV,0);
37543003dfeSmillert 	} else {
37643003dfeSmillert 	    /* Got nothing - assume partial character so we need some more */
37743003dfeSmillert 	    /* Make sure e->dataSV is a normal SV before re-filling as
37843003dfeSmillert 	       buffer alias will change under us
37943003dfeSmillert 	     */
38043003dfeSmillert 	    s = SvPV(e->dataSV,len);
38143003dfeSmillert 	    sv_setpvn(e->dataSV,s,len);
38243003dfeSmillert 	    PerlIO_set_ptrcnt(n, ptr+use, (avail-use));
38343003dfeSmillert 	    goto retry;
38443003dfeSmillert 	}
38543003dfeSmillert     }
38643003dfeSmillert     else {
38743003dfeSmillert     end_of_file:
38843003dfeSmillert 	code = -1;
38943003dfeSmillert 	if (avail == 0)
39043003dfeSmillert 	    PerlIOBase(f)->flags |= PERLIO_F_EOF;
39143003dfeSmillert 	else
392b8851fccSafresh1 	{
39343003dfeSmillert 	    PerlIOBase(f)->flags |= PERLIO_F_ERROR;
394b8851fccSafresh1 	    Perl_PerlIO_save_errno(aTHX_ f);
395b8851fccSafresh1 	}
39643003dfeSmillert     }
39743003dfeSmillert     FREETMPS;
39843003dfeSmillert     LEAVE;
39943003dfeSmillert     POPSTACK;
40043003dfeSmillert     return code;
40143003dfeSmillert }
40243003dfeSmillert 
403b8851fccSafresh1 static IV
40443003dfeSmillert PerlIOEncode_flush(pTHX_ PerlIO * f)
40543003dfeSmillert {
40643003dfeSmillert     PerlIOEncode *e = PerlIOSelf(f, PerlIOEncode);
40743003dfeSmillert     IV code = 0;
40843003dfeSmillert 
40943003dfeSmillert     if (e->bufsv) {
41043003dfeSmillert 	dSP;
41143003dfeSmillert 	SV *str;
41243003dfeSmillert 	char *s;
41343003dfeSmillert 	STRLEN len;
41443003dfeSmillert 	SSize_t count = 0;
41543003dfeSmillert 	if ((PerlIOBase(f)->flags & PERLIO_F_WRBUF) && (e->base.ptr > e->base.buf)) {
41643003dfeSmillert 	    if (e->inEncodeCall) return 0;
41743003dfeSmillert 	    /* Write case - encode the buffer and write() to layer below */
41843003dfeSmillert 	    PUSHSTACKi(PERLSI_MAGIC);
41943003dfeSmillert 	    ENTER;
42043003dfeSmillert 	    SAVETMPS;
42143003dfeSmillert 	    PUSHMARK(sp);
42243003dfeSmillert 	    XPUSHs(e->enc);
42343003dfeSmillert 	    SvCUR_set(e->bufsv, e->base.ptr - e->base.buf);
42443003dfeSmillert 	    SvUTF8_on(e->bufsv);
42543003dfeSmillert 	    XPUSHs(e->bufsv);
42643003dfeSmillert 	    XPUSHs(e->chk);
42743003dfeSmillert 	    PUTBACK;
42843003dfeSmillert 	    e->inEncodeCall = 1;
42943003dfeSmillert 	    if (call_method("encode", G_SCALAR) != 1) {
43043003dfeSmillert 		e->inEncodeCall = 0;
43143003dfeSmillert 		Perl_die(aTHX_ "panic: encode did not return a value");
43243003dfeSmillert 	    }
43343003dfeSmillert 	    e->inEncodeCall = 0;
43443003dfeSmillert 	    SPAGAIN;
43543003dfeSmillert 	    str = POPs;
43643003dfeSmillert 	    PUTBACK;
43743003dfeSmillert 	    s = SvPV(str, len);
43843003dfeSmillert 	    count = PerlIO_write(PerlIONext(f),s,len);
43943003dfeSmillert 	    if ((STRLEN)count != len) {
44043003dfeSmillert 		code = -1;
44143003dfeSmillert 	    }
44243003dfeSmillert 	    FREETMPS;
44343003dfeSmillert 	    LEAVE;
44443003dfeSmillert 	    POPSTACK;
44543003dfeSmillert 	    if (PerlIO_flush(PerlIONext(f)) != 0) {
44643003dfeSmillert 		code = -1;
44743003dfeSmillert 	    }
448e9ce3842Safresh1 	    if (!SvPOKp(e->bufsv) || SvTHINKFIRST(e->bufsv))
449e9ce3842Safresh1 		(void)SvPV_force_nolen(e->bufsv);
450e9ce3842Safresh1 	    if ((STDCHAR *)SvPVX(e->bufsv) != e->base.buf) {
451b8851fccSafresh1 		e->base.ptr = (STDCHAR *)SvEND(e->bufsv);
452b8851fccSafresh1 		e->base.end = (STDCHAR *)SvPVX(e->bufsv) + (e->base.end-e->base.buf);
453e9ce3842Safresh1 		e->base.buf = (STDCHAR *)SvPVX(e->bufsv);
454e9ce3842Safresh1 	    }
455e9ce3842Safresh1 	    (void)PerlIOEncode_get_base(aTHX_ f);
45643003dfeSmillert 	    if (SvCUR(e->bufsv)) {
45743003dfeSmillert 		/* Did not all translate */
45843003dfeSmillert 		e->base.ptr = e->base.buf+SvCUR(e->bufsv);
45943003dfeSmillert 		return code;
46043003dfeSmillert 	    }
46143003dfeSmillert 	}
46243003dfeSmillert 	else if ((PerlIOBase(f)->flags & PERLIO_F_RDBUF)) {
46343003dfeSmillert 	    /* read case */
46443003dfeSmillert 	    /* if we have any untranslated stuff then unread that first */
46543003dfeSmillert 	    /* FIXME - unread is fragile is there a better way ? */
46643003dfeSmillert 	    if (e->dataSV && SvCUR(e->dataSV)) {
46743003dfeSmillert 		s = SvPV(e->dataSV, len);
46843003dfeSmillert 		count = PerlIO_unread(PerlIONext(f),s,len);
46943003dfeSmillert 		if ((STRLEN)count != len) {
47043003dfeSmillert 		    code = -1;
47143003dfeSmillert 		}
47243003dfeSmillert 		SvCUR_set(e->dataSV,0);
47343003dfeSmillert 	    }
47443003dfeSmillert 	    /* See if there is anything left in the buffer */
47543003dfeSmillert 	    if (e->base.ptr < e->base.end) {
47643003dfeSmillert 		if (e->inEncodeCall) return 0;
47743003dfeSmillert 		/* Bother - have unread data.
47843003dfeSmillert 		   re-encode and unread() to layer below
47943003dfeSmillert 		 */
48043003dfeSmillert 		PUSHSTACKi(PERLSI_MAGIC);
48143003dfeSmillert 		ENTER;
48243003dfeSmillert 		SAVETMPS;
483*3d61058aSafresh1 		str = newSV_type_mortal(SVt_PV);
48443003dfeSmillert 		SvPV_set(str, (char*)e->base.ptr);
48543003dfeSmillert 		SvLEN_set(str, 0);
48643003dfeSmillert 		SvCUR_set(str, e->base.end - e->base.ptr);
48743003dfeSmillert 		SvPOK_only(str);
48843003dfeSmillert 		SvUTF8_on(str);
48943003dfeSmillert 		PUSHMARK(sp);
49043003dfeSmillert 		XPUSHs(e->enc);
49143003dfeSmillert 		XPUSHs(str);
49243003dfeSmillert 		XPUSHs(e->chk);
49343003dfeSmillert 		PUTBACK;
49443003dfeSmillert 		e->inEncodeCall = 1;
49543003dfeSmillert 		if (call_method("encode", G_SCALAR) != 1) {
49643003dfeSmillert 		    e->inEncodeCall = 0;
49743003dfeSmillert 		    Perl_die(aTHX_ "panic: encode did not return a value");
49843003dfeSmillert 		}
49943003dfeSmillert 		e->inEncodeCall = 0;
50043003dfeSmillert 		SPAGAIN;
50143003dfeSmillert 		str = POPs;
50243003dfeSmillert 		PUTBACK;
50343003dfeSmillert 		s = SvPV(str, len);
50443003dfeSmillert 		count = PerlIO_unread(PerlIONext(f),s,len);
50543003dfeSmillert 		if ((STRLEN)count != len) {
50643003dfeSmillert 		    code = -1;
50743003dfeSmillert 		}
50843003dfeSmillert 		FREETMPS;
50943003dfeSmillert 		LEAVE;
51043003dfeSmillert 		POPSTACK;
51143003dfeSmillert 	    }
51243003dfeSmillert 	}
51343003dfeSmillert 	e->base.ptr = e->base.end = e->base.buf;
51443003dfeSmillert 	PerlIOBase(f)->flags &= ~(PERLIO_F_RDBUF | PERLIO_F_WRBUF);
51543003dfeSmillert     }
51643003dfeSmillert     return code;
51743003dfeSmillert }
51843003dfeSmillert 
519b8851fccSafresh1 static IV
52043003dfeSmillert PerlIOEncode_close(pTHX_ PerlIO * f)
52143003dfeSmillert {
52243003dfeSmillert     PerlIOEncode *e = PerlIOSelf(f, PerlIOEncode);
52343003dfeSmillert     IV code;
52443003dfeSmillert     if (PerlIOBase(f)->flags & PERLIO_F_RDBUF) {
52543003dfeSmillert 	/* Discard partial character */
52643003dfeSmillert 	if (e->dataSV) {
52743003dfeSmillert 	    SvCUR_set(e->dataSV,0);
52843003dfeSmillert 	}
52943003dfeSmillert 	/* Don't back decode and unread any pending data */
53043003dfeSmillert 	e->base.ptr = e->base.end = e->base.buf;
53143003dfeSmillert     }
53243003dfeSmillert     code = PerlIOBase_close(aTHX_ f);
53343003dfeSmillert     if (e->bufsv) {
53443003dfeSmillert 	/* This should only fire for write case */
53543003dfeSmillert 	if (e->base.buf && e->base.ptr > e->base.buf) {
53643003dfeSmillert 	    Perl_croak(aTHX_ "Close with partial character");
53743003dfeSmillert 	}
53843003dfeSmillert 	SvREFCNT_dec(e->bufsv);
53943003dfeSmillert 	e->bufsv = Nullsv;
54043003dfeSmillert     }
54143003dfeSmillert     e->base.buf = NULL;
54243003dfeSmillert     e->base.ptr = NULL;
54343003dfeSmillert     e->base.end = NULL;
54443003dfeSmillert     PerlIOBase(f)->flags &= ~(PERLIO_F_RDBUF | PERLIO_F_WRBUF);
54543003dfeSmillert     return code;
54643003dfeSmillert }
54743003dfeSmillert 
548b8851fccSafresh1 static Off_t
54943003dfeSmillert PerlIOEncode_tell(pTHX_ PerlIO * f)
55043003dfeSmillert {
55143003dfeSmillert     PerlIOBuf *b = PerlIOSelf(f, PerlIOBuf);
552ca22dbdeSmillert     /* Unfortunately the only way to get a position is to (re-)translate,
553ca22dbdeSmillert        the UTF8 we have in buffer and then ask layer below
55443003dfeSmillert      */
55543003dfeSmillert     PerlIO_flush(f);
55643003dfeSmillert     if (b->buf && b->ptr > b->buf) {
55743003dfeSmillert 	Perl_croak(aTHX_ "Cannot tell at partial character");
55843003dfeSmillert     }
55943003dfeSmillert     return PerlIO_tell(PerlIONext(f));
56043003dfeSmillert }
56143003dfeSmillert 
562b8851fccSafresh1 static PerlIO *
56343003dfeSmillert PerlIOEncode_dup(pTHX_ PerlIO * f, PerlIO * o,
56443003dfeSmillert 		 CLONE_PARAMS * params, int flags)
56543003dfeSmillert {
56643003dfeSmillert     if ((f = PerlIOBase_dup(aTHX_ f, o, params, flags))) {
56743003dfeSmillert 	PerlIOEncode *fe = PerlIOSelf(f, PerlIOEncode);
56843003dfeSmillert 	PerlIOEncode *oe = PerlIOSelf(o, PerlIOEncode);
56943003dfeSmillert 	if (oe->enc) {
57043003dfeSmillert 	    fe->enc = PerlIO_sv_dup(aTHX_ oe->enc, params);
57143003dfeSmillert 	}
572b8851fccSafresh1 	if (oe->chk) {
573b8851fccSafresh1 	    fe->chk = PerlIO_sv_dup(aTHX_ oe->chk, params);
574b8851fccSafresh1 	}
57543003dfeSmillert     }
57643003dfeSmillert     return f;
57743003dfeSmillert }
57843003dfeSmillert 
579b8851fccSafresh1 static SSize_t
58043003dfeSmillert PerlIOEncode_write(pTHX_ PerlIO *f, const void *vbuf, Size_t count)
58143003dfeSmillert {
58243003dfeSmillert     PerlIOEncode *e = PerlIOSelf(f, PerlIOEncode);
58343003dfeSmillert     if (e->flags & NEEDS_LINES) {
58443003dfeSmillert 	SSize_t done = 0;
58543003dfeSmillert 	const char *ptr = (const char *) vbuf;
58643003dfeSmillert 	const char *end = ptr+count;
58743003dfeSmillert 	while (ptr < end) {
58843003dfeSmillert 	    const char *nl = ptr;
58943003dfeSmillert 	    while (nl < end && *nl++ != '\n') /* empty body */;
59043003dfeSmillert 	    done = PerlIOBuf_write(aTHX_ f, ptr, nl-ptr);
59143003dfeSmillert 	    if (done != nl-ptr) {
59243003dfeSmillert 		if (done > 0) {
59343003dfeSmillert 		    ptr += done;
59443003dfeSmillert 		}
59543003dfeSmillert 		break;
59643003dfeSmillert 	    }
59743003dfeSmillert 	    ptr += done;
59843003dfeSmillert 	    if (ptr[-1] == '\n') {
59943003dfeSmillert 		if (PerlIOEncode_flush(aTHX_ f) != 0) {
60043003dfeSmillert 		    break;
60143003dfeSmillert 		}
60243003dfeSmillert 	    }
60343003dfeSmillert 	}
60443003dfeSmillert 	return (SSize_t) (ptr - (const char *) vbuf);
60543003dfeSmillert     }
60643003dfeSmillert     else {
60743003dfeSmillert 	return PerlIOBuf_write(aTHX_ f, vbuf, count);
60843003dfeSmillert     }
60943003dfeSmillert }
61043003dfeSmillert 
611b8851fccSafresh1 static PERLIO_FUNCS_DECL(PerlIO_encode) = {
61243003dfeSmillert     sizeof(PerlIO_funcs),
61343003dfeSmillert     "encoding",
61443003dfeSmillert     sizeof(PerlIOEncode),
61543003dfeSmillert     PERLIO_K_BUFFERED|PERLIO_K_DESTRUCT,
61643003dfeSmillert     PerlIOEncode_pushed,
61743003dfeSmillert     PerlIOEncode_popped,
61843003dfeSmillert     PerlIOBuf_open,
61943003dfeSmillert     NULL, /* binmode - always pop */
62043003dfeSmillert     PerlIOEncode_getarg,
62143003dfeSmillert     PerlIOBase_fileno,
62243003dfeSmillert     PerlIOEncode_dup,
62343003dfeSmillert     PerlIOBuf_read,
62443003dfeSmillert     PerlIOBuf_unread,
62543003dfeSmillert     PerlIOEncode_write,
62643003dfeSmillert     PerlIOBuf_seek,
62743003dfeSmillert     PerlIOEncode_tell,
62843003dfeSmillert     PerlIOEncode_close,
62943003dfeSmillert     PerlIOEncode_flush,
63043003dfeSmillert     PerlIOEncode_fill,
63143003dfeSmillert     PerlIOBase_eof,
63243003dfeSmillert     PerlIOBase_error,
63343003dfeSmillert     PerlIOBase_clearerr,
63443003dfeSmillert     PerlIOBase_setlinebuf,
63543003dfeSmillert     PerlIOEncode_get_base,
63643003dfeSmillert     PerlIOBuf_bufsiz,
63743003dfeSmillert     PerlIOBuf_get_ptr,
63843003dfeSmillert     PerlIOBuf_get_cnt,
63943003dfeSmillert     PerlIOBuf_set_ptrcnt,
64043003dfeSmillert };
64143003dfeSmillert #endif				/* encode layer */
64243003dfeSmillert 
64343003dfeSmillert MODULE = PerlIO::encoding PACKAGE = PerlIO::encoding
64443003dfeSmillert 
64543003dfeSmillert PROTOTYPES: ENABLE
64643003dfeSmillert 
64743003dfeSmillert BOOT:
64843003dfeSmillert {
64943003dfeSmillert     /*
65043003dfeSmillert      * we now "use Encode ()" here instead of
65143003dfeSmillert      * PerlIO/encoding.pm.  This avoids SEGV when ":encoding()"
65243003dfeSmillert      * is invoked without prior "use Encode". -- dankogai
65343003dfeSmillert      */
65443003dfeSmillert     PUSHSTACKi(PERLSI_MAGIC);
655eac174f2Safresh1     if (!get_cvs(OUR_STOP_AT_PARTIAL, 0)) {
65643003dfeSmillert 	/* The SV is magically freed by load_module */
657b8851fccSafresh1 	load_module(PERL_LOADMOD_NOIMPORT, newSVpvs("Encode"), Nullsv, Nullsv);
658e5157e49Safresh1 	assert(sp == PL_stack_sp);
65943003dfeSmillert     }
660eac174f2Safresh1 
66143003dfeSmillert     PUSHMARK(sp);
66243003dfeSmillert     PUTBACK;
663eac174f2Safresh1     if (call_pv(OUR_STOP_AT_PARTIAL, G_SCALAR) != 1) {
66443003dfeSmillert 	    /* should never happen */
665eac174f2Safresh1 	    Perl_die(aTHX_ "%s did not return a value", OUR_STOP_AT_PARTIAL);
66643003dfeSmillert     }
66743003dfeSmillert     SPAGAIN;
668eac174f2Safresh1     encode_stop_at_partial = POPu;
669eac174f2Safresh1 
670eac174f2Safresh1     PUSHMARK(sp);
671eac174f2Safresh1     PUTBACK;
672eac174f2Safresh1     if (call_pv(OUR_LEAVE_SRC, G_SCALAR) != 1) {
673eac174f2Safresh1 	    /* should never happen */
674eac174f2Safresh1 	    Perl_die(aTHX_ "%s did not return a value", OUR_LEAVE_SRC);
675eac174f2Safresh1     }
676eac174f2Safresh1     SPAGAIN;
677eac174f2Safresh1     encode_leave_src = POPu;
678eac174f2Safresh1 
67943003dfeSmillert     PUTBACK;
68043003dfeSmillert #ifdef PERLIO_LAYERS
681b8851fccSafresh1     PerlIO_define_layer(aTHX_ PERLIO_FUNCS_CAST(&PerlIO_encode));
68243003dfeSmillert #endif
68343003dfeSmillert     POPSTACK;
68443003dfeSmillert }
685