/* perlio.c * * Copyright (c) 1996-2001, Nick Ing-Simmons * * You may distribute under the terms of either the GNU General Public * License or the Artistic License, as specified in the README file. * */ /* If we have ActivePerl-like PERL_IMPLICIT_SYS then we need a dTHX to get at the dispatch tables, even when we do not need it for other reasons. Invent a dSYS macro to abstract this out */ #ifdef PERL_IMPLICIT_SYS #define dSYS dTHX #else #define dSYS dNOOP #endif #define VOIDUSED 1 #ifdef PERL_MICRO # include "uconfig.h" #else # include "config.h" #endif #define PERLIO_NOT_STDIO 0 #if !defined(PERLIO_IS_STDIO) && !defined(USE_SFIO) /* #define PerlIO FILE */ #endif /* * This file provides those parts of PerlIO abstraction * which are not #defined in perlio.h. * Which these are depends on various Configure #ifdef's */ #include "EXTERN.h" #define PERL_IN_PERLIO_C #include "perl.h" #include "XSUB.h" #undef PerlMemShared_calloc #define PerlMemShared_calloc(x,y) calloc(x,y) #undef PerlMemShared_free #define PerlMemShared_free(x) free(x) int perlsio_binmode(FILE *fp, int iotype, int mode) { /* This used to be contents of do_binmode in doio.c */ #ifdef DOSISH # if defined(atarist) || defined(__MINT__) if (!fflush(fp)) { if (mode & O_BINARY) ((FILE*)fp)->_flag |= _IOBIN; else ((FILE*)fp)->_flag &= ~ _IOBIN; return 1; } return 0; # else dTHX; #ifdef NETWARE if (PerlLIO_setmode(fp, mode) != -1) { #else if (PerlLIO_setmode(fileno(fp), mode) != -1) { #endif # if defined(WIN32) && defined(__BORLANDC__) /* The translation mode of the stream is maintained independent * of the translation mode of the fd in the Borland RTL (heavy * digging through their runtime sources reveal). User has to * set the mode explicitly for the stream (though they don't * document this anywhere). GSAR 97-5-24 */ fseek(fp,0L,0); if (mode & O_BINARY) fp->flags |= _F_BIN; else fp->flags &= ~ _F_BIN; # endif return 1; } else return 0; # endif #else # if defined(USEMYBINMODE) if (my_binmode(fp, iotype, mode) != FALSE) return 1; else return 0; # else return 1; # endif #endif } #ifndef PERLIO_LAYERS int PerlIO_apply_layers(pTHX_ PerlIO *f, const char *mode, const char *names) { if (!names || !*names || strEQ(names,":crlf") || strEQ(names,":raw")) { return 0; } Perl_croak(aTHX_ "Cannot apply \"%s\" in non-PerlIO perl",names); /* NOTREACHED */ return -1; } void PerlIO_destruct(pTHX) { } int PerlIO_binmode(pTHX_ PerlIO *fp, int iotype, int mode, const char *names) { #ifdef USE_SFIO return 1; #else return perlsio_binmode(fp,iotype,mode); #endif } /* De-mux PerlIO_openn() into fdopen, freopen and fopen type entries */ PerlIO * PerlIO_openn(pTHX_ const char *layers, const char *mode, int fd, int imode, int perm, PerlIO *old, int narg, SV **args) { if (narg == 1) { if (*args == &PL_sv_undef) return PerlIO_tmpfile(); else { char *name = SvPV_nolen(*args); if (*mode == '#') { fd = PerlLIO_open3(name,imode,perm); if (fd >= 0) return PerlIO_fdopen(fd,(char *)mode+1); } else if (old) { return PerlIO_reopen(name,mode,old); } else { return PerlIO_open(name,mode); } } } else { return PerlIO_fdopen(fd,(char *)mode); } return NULL; } XS(XS_PerlIO__Layer__find) { dXSARGS; if (items < 2) Perl_croak(aTHX_ "Usage class->find(name[,load])"); else { char *name = SvPV_nolen(ST(1)); ST(0) = (strEQ(name,"crlf") || strEQ(name,"raw")) ? &PL_sv_yes : &PL_sv_undef; XSRETURN(1); } } void Perl_boot_core_PerlIO(pTHX) { newXS("PerlIO::Layer::find",XS_PerlIO__Layer__find,__FILE__); } #endif #ifdef PERLIO_IS_STDIO void PerlIO_init(void) { /* Does nothing (yet) except force this file to be included in perl binary. That allows this file to force inclusion of other functions that may be required by loadable extensions e.g. for FileHandle::tmpfile */ } #undef PerlIO_tmpfile PerlIO * PerlIO_tmpfile(void) { return tmpfile(); } #else /* PERLIO_IS_STDIO */ #ifdef USE_SFIO #undef HAS_FSETPOS #undef HAS_FGETPOS /* This section is just to make sure these functions get pulled in from libsfio.a */ #undef PerlIO_tmpfile PerlIO * PerlIO_tmpfile(void) { return sftmp(0); } void PerlIO_init(void) { /* Force this file to be included in perl binary. Which allows * this file to force inclusion of other functions that may be * required by loadable extensions e.g. for FileHandle::tmpfile */ /* Hack * sfio does its own 'autoflush' on stdout in common cases. * Flush results in a lot of lseek()s to regular files and * lot of small writes to pipes. */ sfset(sfstdout,SF_SHARE,0); } PerlIO * PerlIO_importFILE(FILE *stdio, int fl) { int fd = fileno(stdio); PerlIO *r = PerlIO_fdopen(fd,"r+"); return r; } FILE * PerlIO_findFILE(PerlIO *pio) { int fd = PerlIO_fileno(pio); FILE *f = fdopen(fd,"r+"); PerlIO_flush(pio); if (!f && errno == EINVAL) f = fdopen(fd,"w"); if (!f && errno == EINVAL) f = fdopen(fd,"r"); return f; } #else /* USE_SFIO */ /*======================================================================================*/ /* Implement all the PerlIO interface ourselves. */ #include "perliol.h" /* We _MUST_ have if we are using lseek() and may have large files */ #ifdef I_UNISTD #include #endif #ifdef HAS_MMAP #include #endif void PerlIO_debug(const char *fmt,...) __attribute__((format(__printf__,1,2))); void PerlIO_debug(const char *fmt,...) { static int dbg = 0; va_list ap; dSYS; va_start(ap,fmt); if (!dbg) { char *s = PerlEnv_getenv("PERLIO_DEBUG"); if (s && *s) dbg = PerlLIO_open3(s,O_WRONLY|O_CREAT|O_APPEND,0666); else dbg = -1; } if (dbg > 0) { dTHX; SV *sv = newSVpvn("",0); char *s; STRLEN len; s = CopFILE(PL_curcop); if (!s) s = "(none)"; Perl_sv_catpvf(aTHX_ sv, "%s:%"IVdf" ", s, (IV)CopLINE(PL_curcop)); Perl_sv_vcatpvf(aTHX_ sv, fmt, &ap); s = SvPV(sv,len); PerlLIO_write(dbg,s,len); SvREFCNT_dec(sv); } va_end(ap); } /*--------------------------------------------------------------------------------------*/ /* Inner level routines */ /* Table of pointers to the PerlIO structs (malloc'ed) */ PerlIO *_perlio = NULL; #define PERLIO_TABLE_SIZE 64 PerlIO * PerlIO_allocate(pTHX) { /* Find a free slot in the table, allocating new table as necessary */ PerlIO **last; PerlIO *f; last = &_perlio; while ((f = *last)) { int i; last = (PerlIO **)(f); for (i=1; i < PERLIO_TABLE_SIZE; i++) { if (!*++f) { return f; } } } f = PerlMemShared_calloc(PERLIO_TABLE_SIZE,sizeof(PerlIO)); if (!f) { return NULL; } *last = f; return f+1; } void PerlIO_cleantable(pTHX_ PerlIO **tablep) { PerlIO *table = *tablep; if (table) { int i; PerlIO_cleantable(aTHX_ (PerlIO **) &(table[0])); for (i=PERLIO_TABLE_SIZE-1; i > 0; i--) { PerlIO *f = table+i; if (*f) { PerlIO_close(f); } } PerlMemShared_free(table); *tablep = NULL; } } PerlIO_list_t *PerlIO_known_layers; PerlIO_list_t *PerlIO_def_layerlist; PerlIO_list_t * PerlIO_list_alloc(void) { PerlIO_list_t *list; Newz('L',list,1,PerlIO_list_t); list->refcnt = 1; return list; } void PerlIO_list_free(PerlIO_list_t *list) { if (list) { if (--list->refcnt == 0) { if (list->array) { dTHX; IV i; for (i=0; i < list->cur; i++) { if (list->array[i].arg) SvREFCNT_dec(list->array[i].arg); } Safefree(list->array); } Safefree(list); } } } void PerlIO_list_push(PerlIO_list_t *list,PerlIO_funcs *funcs,SV *arg) { dTHX; PerlIO_pair_t *p; if (list->cur >= list->len) { list->len += 8; if (list->array) Renew(list->array,list->len,PerlIO_pair_t); else New('l',list->array,list->len,PerlIO_pair_t); } p = &(list->array[list->cur++]); p->funcs = funcs; if ((p->arg = arg)) { SvREFCNT_inc(arg); } } void PerlIO_cleanup_layers(pTHXo_ void *data) { #if 0 PerlIO_known_layers = Nullhv; PerlIO_def_layerlist = Nullav; #endif } void PerlIO_cleanup() { dTHX; PerlIO_cleantable(aTHX_ &_perlio); } void PerlIO_destruct(pTHX) { PerlIO **table = &_perlio; PerlIO *f; while ((f = *table)) { int i; table = (PerlIO **)(f++); for (i=1; i < PERLIO_TABLE_SIZE; i++) { PerlIO *x = f; PerlIOl *l; while ((l = *x)) { if (l->tab->kind & PERLIO_K_DESTRUCT) { PerlIO_debug("Destruct popping %s\n",l->tab->name); PerlIO_flush(x); PerlIO_pop(aTHX_ x); } else { x = PerlIONext(x); } } f++; } } } void PerlIO_pop(pTHX_ PerlIO *f) { PerlIOl *l = *f; if (l) { PerlIO_debug("PerlIO_pop f=%p %s\n",f,l->tab->name); if (l->tab->Popped) { /* If popped returns non-zero do not free its layer structure it has either done so itself, or it is shared and still in use */ if ((*l->tab->Popped)(f) != 0) return; } *f = l->next;; PerlMemShared_free(l); } } /*--------------------------------------------------------------------------------------*/ /* XS Interface for perl code */ PerlIO_funcs * PerlIO_find_layer(pTHX_ const char *name, STRLEN len, int load) { IV i; if ((SSize_t) len <= 0) len = strlen(name); for (i=0; i < PerlIO_known_layers->cur; i++) { PerlIO_funcs *f = PerlIO_known_layers->array[i].funcs; if (memEQ(f->name,name,len)) { PerlIO_debug("%.*s => %p\n",(int)len,name,f); return f; } } if (load && PL_subname && PerlIO_def_layerlist && PerlIO_def_layerlist->cur >= 2) { SV *pkgsv = newSVpvn("PerlIO",6); SV *layer = newSVpvn(name,len); ENTER; /* The two SVs are magically freed by load_module */ Perl_load_module(aTHX_ 0, pkgsv, Nullsv, layer, Nullsv); LEAVE; return PerlIO_find_layer(aTHX_ name,len,0); } PerlIO_debug("Cannot find %.*s\n",(int)len,name); return NULL; } #ifdef USE_ATTRIBUTES_FOR_PERLIO static int perlio_mg_set(pTHX_ SV *sv, MAGIC *mg) { if (SvROK(sv)) { IO *io = GvIOn((GV *)SvRV(sv)); PerlIO *ifp = IoIFP(io); PerlIO *ofp = IoOFP(io); Perl_warn(aTHX_ "set %"SVf" %p %p %p",sv,io,ifp,ofp); } return 0; } static int perlio_mg_get(pTHX_ SV *sv, MAGIC *mg) { if (SvROK(sv)) { IO *io = GvIOn((GV *)SvRV(sv)); PerlIO *ifp = IoIFP(io); PerlIO *ofp = IoOFP(io); Perl_warn(aTHX_ "get %"SVf" %p %p %p",sv,io,ifp,ofp); } return 0; } static int perlio_mg_clear(pTHX_ SV *sv, MAGIC *mg) { Perl_warn(aTHX_ "clear %"SVf,sv); return 0; } static int perlio_mg_free(pTHX_ SV *sv, MAGIC *mg) { Perl_warn(aTHX_ "free %"SVf,sv); return 0; } MGVTBL perlio_vtab = { perlio_mg_get, perlio_mg_set, NULL, /* len */ perlio_mg_clear, perlio_mg_free }; XS(XS_io_MODIFY_SCALAR_ATTRIBUTES) { dXSARGS; SV *sv = SvRV(ST(1)); AV *av = newAV(); MAGIC *mg; int count = 0; int i; sv_magic(sv, (SV *)av, PERL_MAGIC_ext, NULL, 0); SvRMAGICAL_off(sv); mg = mg_find(sv, PERL_MAGIC_ext); mg->mg_virtual = &perlio_vtab; mg_magical(sv); Perl_warn(aTHX_ "attrib %"SVf,sv); for (i=2; i < items; i++) { STRLEN len; const char *name = SvPV(ST(i),len); SV *layer = PerlIO_find_layer(aTHX_ name,len,1); if (layer) { av_push(av,SvREFCNT_inc(layer)); } else { ST(count) = ST(i); count++; } } SvREFCNT_dec(av); XSRETURN(count); } #endif /* USE_ATTIBUTES_FOR_PERLIO */ SV * PerlIO_tab_sv(pTHX_ PerlIO_funcs *tab) { HV *stash = gv_stashpv("PerlIO::Layer", TRUE); SV *sv = sv_bless(newRV_noinc(newSViv(PTR2IV(tab))),stash); return sv; } XS(XS_PerlIO__Layer__find) { dXSARGS; if (items < 2) Perl_croak(aTHX_ "Usage class->find(name[,load])"); else { STRLEN len = 0; char *name = SvPV(ST(1),len); bool load = (items > 2) ? SvTRUE(ST(2)) : 0; PerlIO_funcs *layer = PerlIO_find_layer(aTHX_ name, len, load); ST(0) = (layer) ? sv_2mortal(PerlIO_tab_sv(aTHX_ layer)) : &PL_sv_undef; XSRETURN(1); } } void PerlIO_define_layer(pTHX_ PerlIO_funcs *tab) { if (!PerlIO_known_layers) PerlIO_known_layers = PerlIO_list_alloc(); PerlIO_list_push(PerlIO_known_layers,tab,Nullsv); PerlIO_debug("define %s %p\n",tab->name,tab); } int PerlIO_parse_layers(pTHX_ PerlIO_list_t *av, const char *names) { if (names) { const char *s = names; while (*s) { while (isSPACE(*s) || *s == ':') s++; if (*s) { STRLEN llen = 0; const char *e = s; const char *as = Nullch; STRLEN alen = 0; if (!isIDFIRST(*s)) { /* Message is consistent with how attribute lists are passed. Even though this means "foo : : bar" is seen as an invalid separator character. */ char q = ((*s == '\'') ? '"' : '\''); Perl_warn(aTHX_ "perlio: invalid separator character %c%c%c in layer specification list", q, *s, q); return -1; } do { e++; } while (isALNUM(*e)); llen = e-s; if (*e == '(') { int nesting = 1; as = ++e; while (nesting) { switch (*e++) { case ')': if (--nesting == 0) alen = (e-1)-as; break; case '(': ++nesting; break; case '\\': /* It's a nul terminated string, not allowed to \ the terminating null. Anything other character is passed over. */ if (*e++) { break; } /* Drop through */ case '\0': e--; Perl_warn(aTHX_ "perlio: argument list not closed for layer \"%.*s\"",(int)(e - s),s); return -1; default: /* boring. */ break; } } } if (e > s) { PerlIO_funcs *layer = PerlIO_find_layer(aTHX_ s,llen,1); if (layer) { PerlIO_list_push(av, layer, (as) ? newSVpvn(as,alen) : &PL_sv_undef); } else { Perl_warn(aTHX_ "perlio: unknown layer \"%.*s\"",(int)llen,s); return -1; } } s = e; } } } return 0; } void PerlIO_default_buffer(pTHX_ PerlIO_list_t *av) { PerlIO_funcs *tab = &PerlIO_perlio; if (O_BINARY != O_TEXT) { tab = &PerlIO_crlf; } else { if (PerlIO_stdio.Set_ptrcnt) { tab = &PerlIO_stdio; } } PerlIO_debug("Pushing %s\n",tab->name); PerlIO_list_push(av,PerlIO_find_layer(aTHX_ tab->name,0,0),&PL_sv_undef); } SV * PerlIO_arg_fetch(PerlIO_list_t *av,IV n) { return av->array[n].arg; } PerlIO_funcs * PerlIO_layer_fetch(pTHX_ PerlIO_list_t *av,IV n,PerlIO_funcs *def) { if (n >= 0 && n < av->cur) { PerlIO_debug("Layer %"IVdf" is %s\n",n,av->array[n].funcs->name); return av->array[n].funcs; } if (!def) Perl_croak(aTHX_ "panic: PerlIO layer array corrupt"); return def; } PerlIO_list_t * PerlIO_default_layers(pTHX) { if (!PerlIO_def_layerlist) { const char *s = (PL_tainting) ? Nullch : PerlEnv_getenv("PERLIO"); PerlIO_funcs *osLayer = &PerlIO_unix; PerlIO_def_layerlist = PerlIO_list_alloc(); PerlIO_define_layer(aTHX_ &PerlIO_unix); #if defined(WIN32) && !defined(UNDER_CE) PerlIO_define_layer(aTHX_ &PerlIO_win32); #if 0 osLayer = &PerlIO_win32; #endif #endif PerlIO_define_layer(aTHX_ &PerlIO_raw); PerlIO_define_layer(aTHX_ &PerlIO_perlio); PerlIO_define_layer(aTHX_ &PerlIO_stdio); PerlIO_define_layer(aTHX_ &PerlIO_crlf); #ifdef HAS_MMAP PerlIO_define_layer(aTHX_ &PerlIO_mmap); #endif PerlIO_define_layer(aTHX_ &PerlIO_utf8); PerlIO_define_layer(aTHX_ &PerlIO_byte); PerlIO_list_push(PerlIO_def_layerlist,PerlIO_find_layer(aTHX_ osLayer->name,0,0),&PL_sv_undef); if (s) { PerlIO_parse_layers(aTHX_ PerlIO_def_layerlist,s); } else { PerlIO_default_buffer(aTHX_ PerlIO_def_layerlist); } } if (PerlIO_def_layerlist->cur < 2) { PerlIO_default_buffer(aTHX_ PerlIO_def_layerlist); } return PerlIO_def_layerlist; } void Perl_boot_core_PerlIO(pTHX) { #ifdef USE_ATTRIBUTES_FOR_PERLIO newXS("io::MODIFY_SCALAR_ATTRIBUTES",XS_io_MODIFY_SCALAR_ATTRIBUTES,__FILE__); #endif newXS("PerlIO::Layer::find",XS_PerlIO__Layer__find,__FILE__); } PerlIO_funcs * PerlIO_default_layer(pTHX_ I32 n) { PerlIO_list_t *av = PerlIO_default_layers(aTHX); if (n < 0) n += av->cur; return PerlIO_layer_fetch(aTHX_ av,n, &PerlIO_stdio); } #define PerlIO_default_top() PerlIO_default_layer(aTHX_ -1) #define PerlIO_default_btm() PerlIO_default_layer(aTHX_ 0) void PerlIO_stdstreams(pTHX) { if (!_perlio) { PerlIO_allocate(aTHX); PerlIO_fdopen(0,"Ir" PERLIO_STDTEXT); PerlIO_fdopen(1,"Iw" PERLIO_STDTEXT); PerlIO_fdopen(2,"Iw" PERLIO_STDTEXT); } } PerlIO * PerlIO_push(pTHX_ PerlIO *f,PerlIO_funcs *tab,const char *mode,SV *arg) { PerlIOl *l = NULL; l = PerlMemShared_calloc(tab->size,sizeof(char)); if (l) { Zero(l,tab->size,char); l->next = *f; l->tab = tab; *f = l; PerlIO_debug("PerlIO_push f=%p %s %s %p\n",f,tab->name, (mode) ? mode : "(Null)",arg); if ((*l->tab->Pushed)(f,mode,arg) != 0) { PerlIO_pop(aTHX_ f); return NULL; } } return f; } IV PerlIOPop_pushed(PerlIO *f, const char *mode, SV *arg) { dTHX; PerlIO_pop(aTHX_ f); if (*f) { PerlIO_flush(f); PerlIO_pop(aTHX_ f); return 0; } return -1; } IV PerlIORaw_pushed(PerlIO *f, const char *mode, SV *arg) { /* Remove the dummy layer */ dTHX; PerlIO_pop(aTHX_ f); /* Pop back to bottom layer */ if (f && *f) { PerlIO_flush(f); while (!(PerlIOBase(f)->tab->kind & PERLIO_K_RAW)) { if (*PerlIONext(f)) { PerlIO_pop(aTHX_ f); } else { /* Nothing bellow - push unix on top then remove it */ if (PerlIO_push(aTHX_ f,PerlIO_default_btm(),mode,arg)) { PerlIO_pop(aTHX_ PerlIONext(f)); } break; } } PerlIO_debug(":raw f=%p :%s\n",f,PerlIOBase(f)->tab->name); return 0; } return -1; } int PerlIO_apply_layera(pTHX_ PerlIO *f, const char *mode, PerlIO_list_t *layers, IV n) { IV max = layers->cur; int code = 0; while (n < max) { PerlIO_funcs *tab = PerlIO_layer_fetch(aTHX_ layers,n,NULL); if (tab) { if (!PerlIO_push(aTHX_ f,tab,mode,PerlIOArg)) { code = -1; break; } } n++; } return code; } int PerlIO_apply_layers(pTHX_ PerlIO *f, const char *mode, const char *names) { int code = 0; if (names) { PerlIO_list_t *layers = PerlIO_list_alloc(); code = PerlIO_parse_layers(aTHX_ layers,names); if (code == 0) { code = PerlIO_apply_layera(aTHX_ f, mode, layers, 0); } PerlIO_list_free(layers); } return code; } /*--------------------------------------------------------------------------------------*/ /* Given the abstraction above the public API functions */ int PerlIO_binmode(pTHX_ PerlIO *f, int iotype, int mode, const char *names) { PerlIO_debug("PerlIO_binmode f=%p %s %c %x %s\n", f,PerlIOBase(f)->tab->name,iotype,mode, (names) ? names : "(Null)"); if (!names && (O_TEXT != O_BINARY && (mode & O_BINARY))) { PerlIO *top = f; while (*top) { if (PerlIOBase(top)->tab == &PerlIO_crlf) { PerlIO_flush(top); PerlIOBase(top)->flags &= ~PERLIO_F_CRLF; break; } top = PerlIONext(top); } } return PerlIO_apply_layers(aTHX_ f, NULL, names) == 0 ? TRUE : FALSE; } #undef PerlIO__close int PerlIO__close(PerlIO *f) { if (f && *f) return (*PerlIOBase(f)->tab->Close)(f); else { SETERRNO(EBADF,SS$_IVCHAN); return -1; } } #undef PerlIO_fdupopen PerlIO * PerlIO_fdupopen(pTHX_ PerlIO *f) { if (f && *f) { char buf[8]; int fd = PerlLIO_dup(PerlIO_fileno(f)); PerlIO *new = PerlIO_fdopen(fd,PerlIO_modestr(f,buf)); if (new) { Off_t posn = PerlIO_tell(f); PerlIO_seek(new,posn,SEEK_SET); } return new; } else { SETERRNO(EBADF,SS$_IVCHAN); return NULL; } } #undef PerlIO_close int PerlIO_close(PerlIO *f) { dTHX; int code = -1; if (f && *f) { code = (*PerlIOBase(f)->tab->Close)(f); while (*f) { PerlIO_pop(aTHX_ f); } } return code; } #undef PerlIO_fileno int PerlIO_fileno(PerlIO *f) { if (f && *f) return (*PerlIOBase(f)->tab->Fileno)(f); else { SETERRNO(EBADF,SS$_IVCHAN); return -1; } } static const char * PerlIO_context_layers(pTHX_ const char *mode) { const char *type = NULL; /* Need to supply default layer info from open.pm */ if (PL_curcop) { SV *layers = PL_curcop->cop_io; if (layers) { STRLEN len; type = SvPV(layers,len); if (type && mode[0] != 'r') { /* Skip to write part */ const char *s = strchr(type,0); if (s && (s-type) < len) { type = s+1; } } } } return type; } static PerlIO_funcs * PerlIO_layer_from_ref(pTHX_ SV *sv) { /* For any scalar type load the handler which is bundled with perl */ if (SvTYPE(sv) < SVt_PVAV) return PerlIO_find_layer(aTHX_ "Scalar",6, 1); /* For other types allow if layer is known but don't try and load it */ switch (SvTYPE(sv)) { case SVt_PVAV: return PerlIO_find_layer(aTHX_ "Array",5, 0); case SVt_PVHV: return PerlIO_find_layer(aTHX_ "Hash",4, 0); case SVt_PVCV: return PerlIO_find_layer(aTHX_ "Code",4, 0); case SVt_PVGV: return PerlIO_find_layer(aTHX_ "Glob",4, 0); } return NULL; } PerlIO_list_t * PerlIO_resolve_layers(pTHX_ const char *layers,const char *mode,int narg, SV **args) { PerlIO_list_t *def = PerlIO_default_layers(aTHX); int incdef = 1; if (!_perlio) PerlIO_stdstreams(aTHX); if (narg) { SV *arg = *args; /* If it is a reference but not an object see if we have a handler for it */ if (SvROK(arg) && !sv_isobject(arg)) { PerlIO_funcs *handler = PerlIO_layer_from_ref(aTHX_ SvRV(arg)); if (handler) { def = PerlIO_list_alloc(); PerlIO_list_push(def,handler,&PL_sv_undef); incdef = 0; } /* Don't fail if handler cannot be found * :Via(...) etc. may do something sensible * else we will just stringfy and open resulting string. */ } } if (!layers) layers = PerlIO_context_layers(aTHX_ mode); if (layers && *layers) { PerlIO_list_t *av; if (incdef) { IV i = def->cur; av = PerlIO_list_alloc(); for (i=0; i < def->cur; i++) { PerlIO_list_push(av,def->array[i].funcs,def->array[i].arg); } } else { av = def; } PerlIO_parse_layers(aTHX_ av,layers); return av; } else { if (incdef) def->refcnt++; return def; } } PerlIO * PerlIO_openn(pTHX_ const char *layers, const char *mode, int fd, int imode, int perm, PerlIO *f, int narg, SV **args) { if (!f && narg == 1 && *args == &PL_sv_undef) { if ((f = PerlIO_tmpfile())) { if (!layers) layers = PerlIO_context_layers(aTHX_ mode); if (layers && *layers) PerlIO_apply_layers(aTHX_ f,mode,layers); } } else { PerlIO_list_t *layera = NULL; IV n; PerlIO_funcs *tab = NULL; if (f && *f) { /* This is "reopen" - it is not tested as perl does not use it yet */ PerlIOl *l = *f; layera = PerlIO_list_alloc(); while (l) { SV *arg = (l->tab->Getarg) ? (*l->tab->Getarg)(&l) : &PL_sv_undef; PerlIO_list_push(layera,l->tab,arg); l = *PerlIONext(&l); } } else { layera = PerlIO_resolve_layers(aTHX_ layers, mode, narg, args); } /* Start at "top" of layer stack */ n = layera->cur-1; while (n >= 0) { PerlIO_funcs *t = PerlIO_layer_fetch(aTHX_ layera,n,NULL); if (t && t->Open) { tab = t; break; } n--; } if (tab) { /* Found that layer 'n' can do opens - call it */ PerlIO_debug("openn(%s,'%s','%s',%d,%x,%o,%p,%d,%p)\n", tab->name,layers,mode,fd,imode,perm,f,narg,args); f = (*tab->Open)(aTHX_ tab, layera, n, mode,fd,imode,perm,f,narg,args); if (f) { if (n+1 < layera->cur) { /* More layers above the one that we used to open - apply them now */ if (PerlIO_apply_layera(aTHX_ f, mode, layera, n+1) != 0) { f = NULL; } } } } PerlIO_list_free(layera); } return f; } #undef PerlIO_fdopen PerlIO * PerlIO_fdopen(int fd, const char *mode) { dTHX; return PerlIO_openn(aTHX_ Nullch,mode,fd,0,0,NULL,0,NULL); } #undef PerlIO_open PerlIO * PerlIO_open(const char *path, const char *mode) { dTHX; SV *name = sv_2mortal(newSVpvn(path,strlen(path))); return PerlIO_openn(aTHX_ Nullch,mode,-1,0,0,NULL,1,&name); } #undef PerlIO_reopen PerlIO * PerlIO_reopen(const char *path, const char *mode, PerlIO *f) { dTHX; SV *name = sv_2mortal(newSVpvn(path,strlen(path))); return PerlIO_openn(aTHX_ Nullch,mode,-1,0,0,f,1,&name); } #undef PerlIO_read SSize_t PerlIO_read(PerlIO *f, void *vbuf, Size_t count) { if (f && *f) return (*PerlIOBase(f)->tab->Read)(f,vbuf,count); else { SETERRNO(EBADF,SS$_IVCHAN); return -1; } } #undef PerlIO_unread SSize_t PerlIO_unread(PerlIO *f, const void *vbuf, Size_t count) { if (f && *f) return (*PerlIOBase(f)->tab->Unread)(f,vbuf,count); else { SETERRNO(EBADF,SS$_IVCHAN); return -1; } } #undef PerlIO_write SSize_t PerlIO_write(PerlIO *f, const void *vbuf, Size_t count) { if (f && *f) return (*PerlIOBase(f)->tab->Write)(f,vbuf,count); else { SETERRNO(EBADF,SS$_IVCHAN); return -1; } } #undef PerlIO_seek int PerlIO_seek(PerlIO *f, Off_t offset, int whence) { if (f && *f) return (*PerlIOBase(f)->tab->Seek)(f,offset,whence); else { SETERRNO(EBADF,SS$_IVCHAN); return -1; } } #undef PerlIO_tell Off_t PerlIO_tell(PerlIO *f) { if (f && *f) return (*PerlIOBase(f)->tab->Tell)(f); else { SETERRNO(EBADF,SS$_IVCHAN); return -1; } } #undef PerlIO_flush int PerlIO_flush(PerlIO *f) { if (f) { if (*f) { PerlIO_funcs *tab = PerlIOBase(f)->tab; if (tab && tab->Flush) { return (*tab->Flush)(f); } else { PerlIO_debug("Cannot flush f=%p :%s\n",f,tab->name); SETERRNO(EBADF,SS$_IVCHAN); return -1; } } else { PerlIO_debug("Cannot flush f=%p\n",f); SETERRNO(EBADF,SS$_IVCHAN); return -1; } } else { /* Is it good API design to do flush-all on NULL, * a potentially errorneous input? Maybe some magical * value (PerlIO* PERLIO_FLUSH_ALL = (PerlIO*)-1;)? * Yes, stdio does similar things on fflush(NULL), * but should we be bound by their design decisions? * --jhi */ PerlIO **table = &_perlio; int code = 0; while ((f = *table)) { int i; table = (PerlIO **)(f++); for (i=1; i < PERLIO_TABLE_SIZE; i++) { if (*f && PerlIO_flush(f) != 0) code = -1; f++; } } return code; } } void PerlIOBase_flush_linebuf() { PerlIO **table = &_perlio; PerlIO *f; while ((f = *table)) { int i; table = (PerlIO **)(f++); for (i=1; i < PERLIO_TABLE_SIZE; i++) { if (*f && (PerlIOBase(f)->flags & (PERLIO_F_LINEBUF|PERLIO_F_CANWRITE)) == (PERLIO_F_LINEBUF|PERLIO_F_CANWRITE)) PerlIO_flush(f); f++; } } } #undef PerlIO_fill int PerlIO_fill(PerlIO *f) { if (f && *f) return (*PerlIOBase(f)->tab->Fill)(f); else { SETERRNO(EBADF,SS$_IVCHAN); return -1; } } #undef PerlIO_isutf8 int PerlIO_isutf8(PerlIO *f) { if (f && *f) return (PerlIOBase(f)->flags & PERLIO_F_UTF8) != 0; else { SETERRNO(EBADF,SS$_IVCHAN); return -1; } } #undef PerlIO_eof int PerlIO_eof(PerlIO *f) { if (f && *f) return (*PerlIOBase(f)->tab->Eof)(f); else { SETERRNO(EBADF,SS$_IVCHAN); return -1; } } #undef PerlIO_error int PerlIO_error(PerlIO *f) { if (f && *f) return (*PerlIOBase(f)->tab->Error)(f); else { SETERRNO(EBADF,SS$_IVCHAN); return -1; } } #undef PerlIO_clearerr void PerlIO_clearerr(PerlIO *f) { if (f && *f) (*PerlIOBase(f)->tab->Clearerr)(f); else SETERRNO(EBADF,SS$_IVCHAN); } #undef PerlIO_setlinebuf void PerlIO_setlinebuf(PerlIO *f) { if (f && *f) (*PerlIOBase(f)->tab->Setlinebuf)(f); else SETERRNO(EBADF,SS$_IVCHAN); } #undef PerlIO_has_base int PerlIO_has_base(PerlIO *f) { if (f && *f) { return (PerlIOBase(f)->tab->Get_base != NULL); } return 0; } #undef PerlIO_fast_gets int PerlIO_fast_gets(PerlIO *f) { if (f && *f && (PerlIOBase(f)->flags & PERLIO_F_FASTGETS)) { PerlIO_funcs *tab = PerlIOBase(f)->tab; return (tab->Set_ptrcnt != NULL); } return 0; } #undef PerlIO_has_cntptr int PerlIO_has_cntptr(PerlIO *f) { if (f && *f) { PerlIO_funcs *tab = PerlIOBase(f)->tab; return (tab->Get_ptr != NULL && tab->Get_cnt != NULL); } return 0; } #undef PerlIO_canset_cnt int PerlIO_canset_cnt(PerlIO *f) { if (f && *f) { PerlIOl *l = PerlIOBase(f); return (l->tab->Set_ptrcnt != NULL); } return 0; } #undef PerlIO_get_base STDCHAR * PerlIO_get_base(PerlIO *f) { if (f && *f) return (*PerlIOBase(f)->tab->Get_base)(f); return NULL; } #undef PerlIO_get_bufsiz int PerlIO_get_bufsiz(PerlIO *f) { if (f && *f) return (*PerlIOBase(f)->tab->Get_bufsiz)(f); return 0; } #undef PerlIO_get_ptr STDCHAR * PerlIO_get_ptr(PerlIO *f) { PerlIO_funcs *tab = PerlIOBase(f)->tab; if (tab->Get_ptr == NULL) return NULL; return (*tab->Get_ptr)(f); } #undef PerlIO_get_cnt int PerlIO_get_cnt(PerlIO *f) { PerlIO_funcs *tab = PerlIOBase(f)->tab; if (tab->Get_cnt == NULL) return 0; return (*tab->Get_cnt)(f); } #undef PerlIO_set_cnt void PerlIO_set_cnt(PerlIO *f,int cnt) { (*PerlIOBase(f)->tab->Set_ptrcnt)(f,NULL,cnt); } #undef PerlIO_set_ptrcnt void PerlIO_set_ptrcnt(PerlIO *f, STDCHAR *ptr, int cnt) { PerlIO_funcs *tab = PerlIOBase(f)->tab; if (tab->Set_ptrcnt == NULL) { dTHX; Perl_croak(aTHX_ "PerlIO buffer snooping abuse"); } (*PerlIOBase(f)->tab->Set_ptrcnt)(f,ptr,cnt); } /*--------------------------------------------------------------------------------------*/ /* utf8 and raw dummy layers */ IV PerlIOUtf8_pushed(PerlIO *f, const char *mode, SV *arg) { if (PerlIONext(f)) { dTHX; PerlIO_funcs *tab = PerlIOBase(f)->tab; PerlIO_pop(aTHX_ f); if (tab->kind & PERLIO_K_UTF8) PerlIOBase(f)->flags |= PERLIO_F_UTF8; else PerlIOBase(f)->flags &= ~PERLIO_F_UTF8; return 0; } return -1; } PerlIO_funcs PerlIO_utf8 = { "utf8", sizeof(PerlIOl), PERLIO_K_DUMMY|PERLIO_F_UTF8, PerlIOUtf8_pushed, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* flush */ NULL, /* fill */ NULL, NULL, NULL, NULL, NULL, /* get_base */ NULL, /* get_bufsiz */ NULL, /* get_ptr */ NULL, /* get_cnt */ NULL, /* set_ptrcnt */ }; PerlIO_funcs PerlIO_byte = { "bytes", sizeof(PerlIOl), PERLIO_K_DUMMY, PerlIOUtf8_pushed, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* flush */ NULL, /* fill */ NULL, NULL, NULL, NULL, NULL, /* get_base */ NULL, /* get_bufsiz */ NULL, /* get_ptr */ NULL, /* get_cnt */ NULL, /* set_ptrcnt */ }; PerlIO * PerlIORaw_open(pTHX_ PerlIO_funcs *self, PerlIO_list_t *layers, IV n,const char *mode, int fd, int imode, int perm, PerlIO *old, int narg, SV **args) { PerlIO_funcs *tab = PerlIO_default_btm(); return (*tab->Open)(aTHX_ tab,layers,n-1,mode,fd,imode,perm,old,narg,args); } PerlIO_funcs PerlIO_raw = { "raw", sizeof(PerlIOl), PERLIO_K_DUMMY, PerlIORaw_pushed, PerlIOBase_popped, PerlIORaw_open, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* flush */ NULL, /* fill */ NULL, NULL, NULL, NULL, NULL, /* get_base */ NULL, /* get_bufsiz */ NULL, /* get_ptr */ NULL, /* get_cnt */ NULL, /* set_ptrcnt */ }; /*--------------------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------------------*/ /* "Methods" of the "base class" */ IV PerlIOBase_fileno(PerlIO *f) { return PerlIO_fileno(PerlIONext(f)); } char * PerlIO_modestr(PerlIO *f,char *buf) { char *s = buf; IV flags = PerlIOBase(f)->flags; if (flags & PERLIO_F_APPEND) { *s++ = 'a'; if (flags & PERLIO_F_CANREAD) { *s++ = '+'; } } else if (flags & PERLIO_F_CANREAD) { *s++ = 'r'; if (flags & PERLIO_F_CANWRITE) *s++ = '+'; } else if (flags & PERLIO_F_CANWRITE) { *s++ = 'w'; if (flags & PERLIO_F_CANREAD) { *s++ = '+'; } } #if O_TEXT != O_BINARY if (!(flags & PERLIO_F_CRLF)) *s++ = 'b'; #endif *s = '\0'; return buf; } IV PerlIOBase_pushed(PerlIO *f, const char *mode, SV *arg) { PerlIOl *l = PerlIOBase(f); #if 0 const char *omode = mode; char temp[8]; #endif PerlIO_funcs *tab = PerlIOBase(f)->tab; l->flags &= ~(PERLIO_F_CANREAD|PERLIO_F_CANWRITE| PERLIO_F_TRUNCATE|PERLIO_F_APPEND); if (tab->Set_ptrcnt != NULL) l->flags |= PERLIO_F_FASTGETS; if (mode) { if (*mode == '#' || *mode == 'I') mode++; switch (*mode++) { case 'r': l->flags |= PERLIO_F_CANREAD; break; case 'a': l->flags |= PERLIO_F_APPEND|PERLIO_F_CANWRITE; break; case 'w': l->flags |= PERLIO_F_TRUNCATE|PERLIO_F_CANWRITE; break; default: SETERRNO(EINVAL,LIB$_INVARG); return -1; } while (*mode) { switch (*mode++) { case '+': l->flags |= PERLIO_F_CANREAD|PERLIO_F_CANWRITE; break; case 'b': l->flags &= ~PERLIO_F_CRLF; break; case 't': l->flags |= PERLIO_F_CRLF; break; default: SETERRNO(EINVAL,LIB$_INVARG); return -1; } } } else { if (l->next) { l->flags |= l->next->flags & (PERLIO_F_CANREAD|PERLIO_F_CANWRITE|PERLIO_F_TRUNCATE|PERLIO_F_APPEND); } } #if 0 PerlIO_debug("PerlIOBase_pushed f=%p %s %s fl=%08"UVxf" (%s)\n", f,PerlIOBase(f)->tab->name,(omode) ? omode : "(Null)", l->flags,PerlIO_modestr(f,temp)); #endif return 0; } IV PerlIOBase_popped(PerlIO *f) { return 0; } SSize_t PerlIOBase_unread(PerlIO *f, const void *vbuf, Size_t count) { dTHX; /* Save the position as current head considers it */ Off_t old = PerlIO_tell(f); SSize_t done; PerlIO_push(aTHX_ f,&PerlIO_pending,"r",Nullsv); PerlIOSelf(f,PerlIOBuf)->posn = old; done = PerlIOBuf_unread(f,vbuf,count); return done; } SSize_t PerlIOBase_read(PerlIO *f, void *vbuf, Size_t count) { STDCHAR *buf = (STDCHAR *) vbuf; if (f) { if (!(PerlIOBase(f)->flags & PERLIO_F_CANREAD)) return 0; while (count > 0) { SSize_t avail = PerlIO_get_cnt(f); SSize_t take = 0; if (avail > 0) take = (count < avail) ? count : avail; if (take > 0) { STDCHAR *ptr = PerlIO_get_ptr(f); Copy(ptr,buf,take,STDCHAR); PerlIO_set_ptrcnt(f,ptr+take,(avail -= take)); count -= take; buf += take; } if (count > 0 && avail <= 0) { if (PerlIO_fill(f) != 0) break; } } return (buf - (STDCHAR *) vbuf); } return 0; } IV PerlIOBase_noop_ok(PerlIO *f) { return 0; } IV PerlIOBase_noop_fail(PerlIO *f) { return -1; } IV PerlIOBase_close(PerlIO *f) { IV code = 0; PerlIO *n = PerlIONext(f); if (PerlIO_flush(f) != 0) code = -1; if (n && *n && (*PerlIOBase(n)->tab->Close)(n) != 0) code = -1; PerlIOBase(f)->flags &= ~(PERLIO_F_CANREAD|PERLIO_F_CANWRITE|PERLIO_F_OPEN); return code; } IV PerlIOBase_eof(PerlIO *f) { if (f && *f) { return (PerlIOBase(f)->flags & PERLIO_F_EOF) != 0; } return 1; } IV PerlIOBase_error(PerlIO *f) { if (f && *f) { return (PerlIOBase(f)->flags & PERLIO_F_ERROR) != 0; } return 1; } void PerlIOBase_clearerr(PerlIO *f) { if (f && *f) { PerlIO *n = PerlIONext(f); PerlIOBase(f)->flags &= ~(PERLIO_F_ERROR|PERLIO_F_EOF); if (n) PerlIO_clearerr(n); } } void PerlIOBase_setlinebuf(PerlIO *f) { if (f) { PerlIOBase(f)->flags |= PERLIO_F_LINEBUF; } } /*--------------------------------------------------------------------------------------*/ /* Bottom-most level for UNIX-like case */ typedef struct { struct _PerlIO base; /* The generic part */ int fd; /* UNIX like file descriptor */ int oflags; /* open/fcntl flags */ } PerlIOUnix; int PerlIOUnix_oflags(const char *mode) { int oflags = -1; if (*mode == 'I' || *mode == '#') mode++; switch(*mode) { case 'r': oflags = O_RDONLY; if (*++mode == '+') { oflags = O_RDWR; mode++; } break; case 'w': oflags = O_CREAT|O_TRUNC; if (*++mode == '+') { oflags |= O_RDWR; mode++; } else oflags |= O_WRONLY; break; case 'a': oflags = O_CREAT|O_APPEND; if (*++mode == '+') { oflags |= O_RDWR; mode++; } else oflags |= O_WRONLY; break; } if (*mode == 'b') { oflags |= O_BINARY; oflags &= ~O_TEXT; mode++; } else if (*mode == 't') { oflags |= O_TEXT; oflags &= ~O_BINARY; mode++; } /* Always open in binary mode */ oflags |= O_BINARY; if (*mode || oflags == -1) { SETERRNO(EINVAL,LIB$_INVARG); oflags = -1; } return oflags; } IV PerlIOUnix_fileno(PerlIO *f) { return PerlIOSelf(f,PerlIOUnix)->fd; } IV PerlIOUnix_pushed(PerlIO *f, const char *mode, SV *arg) { IV code = PerlIOBase_pushed(f,mode,arg); if (*PerlIONext(f)) { PerlIOUnix *s = PerlIOSelf(f,PerlIOUnix); s->fd = PerlIO_fileno(PerlIONext(f)); s->oflags = PerlIOUnix_oflags(mode); } PerlIOBase(f)->flags |= PERLIO_F_OPEN; return code; } PerlIO * PerlIOUnix_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) { if (f) { if (PerlIOBase(f)->flags & PERLIO_F_OPEN) (*PerlIOBase(f)->tab->Close)(f); } if (narg > 0) { char *path = SvPV_nolen(*args); if (*mode == '#') mode++; else { imode = PerlIOUnix_oflags(mode); perm = 0666; } if (imode != -1) { fd = PerlLIO_open3(path,imode,perm); } } if (fd >= 0) { PerlIOUnix *s; if (*mode == 'I') mode++; if (!f) { f = PerlIO_allocate(aTHX); s = PerlIOSelf(PerlIO_push(aTHX_ f,self,mode,PerlIOArg),PerlIOUnix); } else s = PerlIOSelf(f,PerlIOUnix); s->fd = fd; s->oflags = imode; PerlIOBase(f)->flags |= PERLIO_F_OPEN; return f; } else { if (f) { /* FIXME: pop layers ??? */ } return NULL; } } SSize_t PerlIOUnix_read(PerlIO *f, void *vbuf, Size_t count) { dTHX; int fd = PerlIOSelf(f,PerlIOUnix)->fd; if (!(PerlIOBase(f)->flags & PERLIO_F_CANREAD)) return 0; while (1) { SSize_t len = PerlLIO_read(fd,vbuf,count); if (len >= 0 || errno != EINTR) { if (len < 0) PerlIOBase(f)->flags |= PERLIO_F_ERROR; else if (len == 0 && count != 0) PerlIOBase(f)->flags |= PERLIO_F_EOF; return len; } PERL_ASYNC_CHECK(); } } SSize_t PerlIOUnix_write(PerlIO *f, const void *vbuf, Size_t count) { dTHX; int fd = PerlIOSelf(f,PerlIOUnix)->fd; while (1) { SSize_t len = PerlLIO_write(fd,vbuf,count); if (len >= 0 || errno != EINTR) { if (len < 0) PerlIOBase(f)->flags |= PERLIO_F_ERROR; return len; } PERL_ASYNC_CHECK(); } } IV PerlIOUnix_seek(PerlIO *f, Off_t offset, int whence) { dSYS; Off_t new = PerlLIO_lseek(PerlIOSelf(f,PerlIOUnix)->fd,offset,whence); PerlIOBase(f)->flags &= ~PERLIO_F_EOF; return (new == (Off_t) -1) ? -1 : 0; } Off_t PerlIOUnix_tell(PerlIO *f) { dSYS; return PerlLIO_lseek(PerlIOSelf(f,PerlIOUnix)->fd,0,SEEK_CUR); } IV PerlIOUnix_close(PerlIO *f) { dTHX; int fd = PerlIOSelf(f,PerlIOUnix)->fd; int code = 0; while (PerlLIO_close(fd) != 0) { if (errno != EINTR) { code = -1; break; } PERL_ASYNC_CHECK(); } if (code == 0) { PerlIOBase(f)->flags &= ~PERLIO_F_OPEN; } return code; } PerlIO_funcs PerlIO_unix = { "unix", sizeof(PerlIOUnix), PERLIO_K_RAW, PerlIOUnix_pushed, PerlIOBase_noop_ok, PerlIOUnix_open, NULL, PerlIOUnix_fileno, PerlIOUnix_read, PerlIOBase_unread, PerlIOUnix_write, PerlIOUnix_seek, PerlIOUnix_tell, PerlIOUnix_close, PerlIOBase_noop_ok, /* flush */ PerlIOBase_noop_fail, /* fill */ PerlIOBase_eof, PerlIOBase_error, PerlIOBase_clearerr, PerlIOBase_setlinebuf, NULL, /* get_base */ NULL, /* get_bufsiz */ NULL, /* get_ptr */ NULL, /* get_cnt */ NULL, /* set_ptrcnt */ }; /*--------------------------------------------------------------------------------------*/ /* stdio as a layer */ typedef struct { struct _PerlIO base; FILE * stdio; /* The stream */ } PerlIOStdio; IV PerlIOStdio_fileno(PerlIO *f) { dSYS; return PerlSIO_fileno(PerlIOSelf(f,PerlIOStdio)->stdio); } char * PerlIOStdio_mode(const char *mode,char *tmode) { char *ret = tmode; while (*mode) { *tmode++ = *mode++; } if (O_BINARY != O_TEXT) { *tmode++ = 'b'; } *tmode = '\0'; return ret; } /* This isn't used yet ... */ IV PerlIOStdio_pushed(PerlIO *f, const char *mode, SV *arg) { if (*PerlIONext(f)) { dSYS; PerlIOStdio *s = PerlIOSelf(f,PerlIOStdio); char tmode[8]; FILE *stdio = PerlSIO_fdopen(PerlIO_fileno(PerlIONext(f)),mode = PerlIOStdio_mode(mode,tmode)); if (stdio) s->stdio = stdio; else return -1; } return PerlIOBase_pushed(f,mode,arg); } #undef PerlIO_importFILE PerlIO * PerlIO_importFILE(FILE *stdio, int fl) { dTHX; PerlIO *f = NULL; if (stdio) { PerlIOStdio *s = PerlIOSelf(PerlIO_push(aTHX_ (f = PerlIO_allocate(aTHX)),&PerlIO_stdio,"r+",Nullsv),PerlIOStdio); s->stdio = stdio; } return f; } PerlIO * PerlIOStdio_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) { char tmode[8]; if (f) { char *path = SvPV_nolen(*args); PerlIOStdio *s = PerlIOSelf(f,PerlIOStdio); FILE *stdio = PerlSIO_freopen(path,(mode = PerlIOStdio_mode(mode,tmode)),s->stdio); if (!s->stdio) return NULL; s->stdio = stdio; return f; } else { if (narg > 0) { char *path = SvPV_nolen(*args); if (*mode == '#') { mode++; fd = PerlLIO_open3(path,imode,perm); } else { FILE *stdio = PerlSIO_fopen(path,mode); if (stdio) { PerlIOStdio *s = PerlIOSelf(PerlIO_push(aTHX_ (f = PerlIO_allocate(aTHX)), self, (mode = PerlIOStdio_mode(mode,tmode)),PerlIOArg), PerlIOStdio); s->stdio = stdio; } return f; } } if (fd >= 0) { FILE *stdio = NULL; int init = 0; if (*mode == 'I') { init = 1; mode++; } if (init) { switch(fd) { case 0: stdio = PerlSIO_stdin; break; case 1: stdio = PerlSIO_stdout; break; case 2: stdio = PerlSIO_stderr; break; } } else { stdio = PerlSIO_fdopen(fd,mode = PerlIOStdio_mode(mode,tmode)); } if (stdio) { PerlIOStdio *s = PerlIOSelf(PerlIO_push(aTHX_ (f = PerlIO_allocate(aTHX)),self,mode,PerlIOArg),PerlIOStdio); s->stdio = stdio; return f; } } } return NULL; } SSize_t PerlIOStdio_read(PerlIO *f, void *vbuf, Size_t count) { dSYS; FILE *s = PerlIOSelf(f,PerlIOStdio)->stdio; SSize_t got = 0; if (count == 1) { STDCHAR *buf = (STDCHAR *) vbuf; /* Perl is expecting PerlIO_getc() to fill the buffer * Linux's stdio does not do that for fread() */ int ch = PerlSIO_fgetc(s); if (ch != EOF) { *buf = ch; got = 1; } } else got = PerlSIO_fread(vbuf,1,count,s); return got; } SSize_t PerlIOStdio_unread(PerlIO *f, const void *vbuf, Size_t count) { dSYS; FILE *s = PerlIOSelf(f,PerlIOStdio)->stdio; STDCHAR *buf = ((STDCHAR *)vbuf)+count-1; SSize_t unread = 0; while (count > 0) { int ch = *buf-- & 0xff; if (PerlSIO_ungetc(ch,s) != ch) break; unread++; count--; } return unread; } SSize_t PerlIOStdio_write(PerlIO *f, const void *vbuf, Size_t count) { dSYS; return PerlSIO_fwrite(vbuf,1,count,PerlIOSelf(f,PerlIOStdio)->stdio); } IV PerlIOStdio_seek(PerlIO *f, Off_t offset, int whence) { dSYS; FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; return PerlSIO_fseek(stdio,offset,whence); } Off_t PerlIOStdio_tell(PerlIO *f) { dSYS; FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; return PerlSIO_ftell(stdio); } IV PerlIOStdio_close(PerlIO *f) { dSYS; #ifdef SOCKS5_VERSION_NAME int optval; Sock_size_t optlen = sizeof(int); #endif FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; return( #ifdef SOCKS5_VERSION_NAME (getsockopt(PerlIO_fileno(f), SOL_SOCKET, SO_TYPE, (void *)&optval, &optlen) < 0) ? PerlSIO_fclose(stdio) : close(PerlIO_fileno(f)) #else PerlSIO_fclose(stdio) #endif ); } IV PerlIOStdio_flush(PerlIO *f) { dSYS; FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; if (PerlIOBase(f)->flags & PERLIO_F_CANWRITE) { return PerlSIO_fflush(stdio); } else { #if 0 /* FIXME: This discards ungetc() and pre-read stuff which is not right if this is just a "sync" from a layer above Suspect right design is to do _this_ but not have layer above flush this layer read-to-read */ /* Not writeable - sync by attempting a seek */ int err = errno; if (PerlSIO_fseek(stdio,(Off_t) 0, SEEK_CUR) != 0) errno = err; #endif } return 0; } IV PerlIOStdio_fill(PerlIO *f) { dSYS; FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; int c; /* fflush()ing read-only streams can cause trouble on some stdio-s */ if ((PerlIOBase(f)->flags & PERLIO_F_CANWRITE)) { if (PerlSIO_fflush(stdio) != 0) return EOF; } c = PerlSIO_fgetc(stdio); if (c == EOF || PerlSIO_ungetc(c,stdio) != c) return EOF; return 0; } IV PerlIOStdio_eof(PerlIO *f) { dSYS; return PerlSIO_feof(PerlIOSelf(f,PerlIOStdio)->stdio); } IV PerlIOStdio_error(PerlIO *f) { dSYS; return PerlSIO_ferror(PerlIOSelf(f,PerlIOStdio)->stdio); } void PerlIOStdio_clearerr(PerlIO *f) { dSYS; PerlSIO_clearerr(PerlIOSelf(f,PerlIOStdio)->stdio); } void PerlIOStdio_setlinebuf(PerlIO *f) { dSYS; #ifdef HAS_SETLINEBUF PerlSIO_setlinebuf(PerlIOSelf(f,PerlIOStdio)->stdio); #else PerlSIO_setvbuf(PerlIOSelf(f,PerlIOStdio)->stdio, Nullch, _IOLBF, 0); #endif } #ifdef FILE_base STDCHAR * PerlIOStdio_get_base(PerlIO *f) { dSYS; FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; return PerlSIO_get_base(stdio); } Size_t PerlIOStdio_get_bufsiz(PerlIO *f) { dSYS; FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; return PerlSIO_get_bufsiz(stdio); } #endif #ifdef USE_STDIO_PTR STDCHAR * PerlIOStdio_get_ptr(PerlIO *f) { dSYS; FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; return PerlSIO_get_ptr(stdio); } SSize_t PerlIOStdio_get_cnt(PerlIO *f) { dSYS; FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; return PerlSIO_get_cnt(stdio); } void PerlIOStdio_set_ptrcnt(PerlIO *f,STDCHAR *ptr,SSize_t cnt) { FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; dSYS; if (ptr != NULL) { #ifdef STDIO_PTR_LVALUE PerlSIO_set_ptr(stdio,ptr); #ifdef STDIO_PTR_LVAL_SETS_CNT if (PerlSIO_get_cnt(stdio) != (cnt)) { dTHX; assert(PerlSIO_get_cnt(stdio) == (cnt)); } #endif #if (!defined(STDIO_PTR_LVAL_NOCHANGE_CNT)) /* Setting ptr _does_ change cnt - we are done */ return; #endif #else /* STDIO_PTR_LVALUE */ PerlProc_abort(); #endif /* STDIO_PTR_LVALUE */ } /* Now (or only) set cnt */ #ifdef STDIO_CNT_LVALUE PerlSIO_set_cnt(stdio,cnt); #else /* STDIO_CNT_LVALUE */ #if (defined(STDIO_PTR_LVALUE) && defined(STDIO_PTR_LVAL_SETS_CNT)) PerlSIO_set_ptr(stdio,PerlSIO_get_ptr(stdio)+(PerlSIO_get_cnt(stdio)-cnt)); #else /* STDIO_PTR_LVAL_SETS_CNT */ PerlProc_abort(); #endif /* STDIO_PTR_LVAL_SETS_CNT */ #endif /* STDIO_CNT_LVALUE */ } #endif PerlIO_funcs PerlIO_stdio = { "stdio", sizeof(PerlIOStdio), PERLIO_K_BUFFERED, PerlIOBase_pushed, PerlIOBase_noop_ok, PerlIOStdio_open, NULL, PerlIOStdio_fileno, PerlIOStdio_read, PerlIOStdio_unread, PerlIOStdio_write, PerlIOStdio_seek, PerlIOStdio_tell, PerlIOStdio_close, PerlIOStdio_flush, PerlIOStdio_fill, PerlIOStdio_eof, PerlIOStdio_error, PerlIOStdio_clearerr, PerlIOStdio_setlinebuf, #ifdef FILE_base PerlIOStdio_get_base, PerlIOStdio_get_bufsiz, #else NULL, NULL, #endif #ifdef USE_STDIO_PTR PerlIOStdio_get_ptr, PerlIOStdio_get_cnt, #if (defined(STDIO_PTR_LVALUE) && (defined(STDIO_CNT_LVALUE) || defined(STDIO_PTR_LVAL_SETS_CNT))) PerlIOStdio_set_ptrcnt #else /* STDIO_PTR_LVALUE */ NULL #endif /* STDIO_PTR_LVALUE */ #else /* USE_STDIO_PTR */ NULL, NULL, NULL #endif /* USE_STDIO_PTR */ }; #undef PerlIO_exportFILE FILE * PerlIO_exportFILE(PerlIO *f, int fl) { FILE *stdio; PerlIO_flush(f); stdio = fdopen(PerlIO_fileno(f),"r+"); if (stdio) { dTHX; PerlIOStdio *s = PerlIOSelf(PerlIO_push(aTHX_ f,&PerlIO_stdio,"r+",Nullsv),PerlIOStdio); s->stdio = stdio; } return stdio; } #undef PerlIO_findFILE FILE * PerlIO_findFILE(PerlIO *f) { PerlIOl *l = *f; while (l) { if (l->tab == &PerlIO_stdio) { PerlIOStdio *s = PerlIOSelf(&l,PerlIOStdio); return s->stdio; } l = *PerlIONext(&l); } return PerlIO_exportFILE(f,0); } #undef PerlIO_releaseFILE void PerlIO_releaseFILE(PerlIO *p, FILE *f) { } /*--------------------------------------------------------------------------------------*/ /* perlio buffer layer */ IV PerlIOBuf_pushed(PerlIO *f, const char *mode, SV *arg) { dSYS; PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); int fd = PerlIO_fileno(f); Off_t posn; if (fd >= 0 && PerlLIO_isatty(fd)) { PerlIOBase(f)->flags |= PERLIO_F_LINEBUF|PERLIO_F_TTY; } posn = PerlIO_tell(PerlIONext(f)); if (posn != (Off_t) -1) { b->posn = posn; } return PerlIOBase_pushed(f,mode,arg); } PerlIO * PerlIOBuf_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) { if (f) { PerlIO *next = PerlIONext(f); PerlIO_funcs *tab = PerlIO_layer_fetch(aTHX_ layers, n-1, PerlIOBase(next)->tab); next = (*tab->Open)(aTHX_ tab, layers, n-1, mode,fd,imode,perm,next,narg,args); if (!next || (*PerlIOBase(f)->tab->Pushed)(f,mode,PerlIOArg) != 0) { return NULL; } } else { PerlIO_funcs *tab = PerlIO_layer_fetch(aTHX_ layers, n-1, PerlIO_default_btm()); int init = 0; if (*mode == 'I') { init = 1; /* mode++; */ } f = (*tab->Open)(aTHX_ tab, layers, n-1, mode,fd,imode,perm,NULL,narg,args); if (f) { PerlIO_push(aTHX_ f,self,mode,PerlIOArg); fd = PerlIO_fileno(f); #if O_BINARY != O_TEXT /* do something about failing setmode()? --jhi */ PerlLIO_setmode(fd , O_BINARY); #endif if (init && fd == 2) { /* Initial stderr is unbuffered */ PerlIOBase(f)->flags |= PERLIO_F_UNBUF; } } } return f; } /* This "flush" is akin to sfio's sync in that it handles files in either read or write state */ IV PerlIOBuf_flush(PerlIO *f) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); int code = 0; if (PerlIOBase(f)->flags & PERLIO_F_WRBUF) { /* write() the buffer */ STDCHAR *buf = b->buf; STDCHAR *p = buf; PerlIO *n = PerlIONext(f); while (p < b->ptr) { SSize_t count = PerlIO_write(n,p,b->ptr - p); if (count > 0) { p += count; } else if (count < 0 || PerlIO_error(n)) { PerlIOBase(f)->flags |= PERLIO_F_ERROR; code = -1; break; } } b->posn += (p - buf); } else if (PerlIOBase(f)->flags & PERLIO_F_RDBUF) { STDCHAR *buf = PerlIO_get_base(f); /* Note position change */ b->posn += (b->ptr - buf); if (b->ptr < b->end) { /* We did not consume all of it */ if (PerlIO_seek(PerlIONext(f),b->posn,SEEK_SET) == 0) { b->posn = PerlIO_tell(PerlIONext(f)); } } } b->ptr = b->end = b->buf; PerlIOBase(f)->flags &= ~(PERLIO_F_RDBUF|PERLIO_F_WRBUF); /* FIXME: Is this right for read case ? */ if (PerlIO_flush(PerlIONext(f)) != 0) code = -1; return code; } IV PerlIOBuf_fill(PerlIO *f) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); PerlIO *n = PerlIONext(f); SSize_t avail; /* FIXME: doing the down-stream flush is a bad idea if it causes pre-read data in stdio buffer to be discarded but this is too simplistic - as it skips _our_ hosekeeping and breaks tell tests. if (!(PerlIOBase(f)->flags & PERLIO_F_RDBUF)) { } */ if (PerlIO_flush(f) != 0) return -1; if (PerlIOBase(f)->flags & PERLIO_F_TTY) PerlIOBase_flush_linebuf(); if (!b->buf) PerlIO_get_base(f); /* allocate via vtable */ b->ptr = b->end = b->buf; if (PerlIO_fast_gets(n)) { /* Layer below is also buffered * We do _NOT_ want to call its ->Read() because that will loop * till it gets what we asked for which may hang on a pipe etc. * Instead take anything it has to hand, or ask it to fill _once_. */ avail = PerlIO_get_cnt(n); if (avail <= 0) { avail = PerlIO_fill(n); if (avail == 0) avail = PerlIO_get_cnt(n); else { if (!PerlIO_error(n) && PerlIO_eof(n)) avail = 0; } } if (avail > 0) { STDCHAR *ptr = PerlIO_get_ptr(n); SSize_t cnt = avail; if (avail > b->bufsiz) avail = b->bufsiz; Copy(ptr,b->buf,avail,STDCHAR); PerlIO_set_ptrcnt(n,ptr+avail,cnt-avail); } } else { avail = PerlIO_read(n,b->ptr,b->bufsiz); } if (avail <= 0) { if (avail == 0) PerlIOBase(f)->flags |= PERLIO_F_EOF; else PerlIOBase(f)->flags |= PERLIO_F_ERROR; return -1; } b->end = b->buf+avail; PerlIOBase(f)->flags |= PERLIO_F_RDBUF; return 0; } SSize_t PerlIOBuf_read(PerlIO *f, void *vbuf, Size_t count) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); if (f) { if (!b->ptr) PerlIO_get_base(f); return PerlIOBase_read(f,vbuf,count); } return 0; } SSize_t PerlIOBuf_unread(PerlIO *f, const void *vbuf, Size_t count) { const STDCHAR *buf = (const STDCHAR *) vbuf+count; PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); SSize_t unread = 0; SSize_t avail; if (PerlIOBase(f)->flags & PERLIO_F_WRBUF) PerlIO_flush(f); if (!b->buf) PerlIO_get_base(f); if (b->buf) { if (PerlIOBase(f)->flags & PERLIO_F_RDBUF) { /* Buffer is already a read buffer, we can overwrite any chars which have been read back to buffer start */ avail = (b->ptr - b->buf); } else { /* Buffer is idle, set it up so whole buffer is available for unread */ avail = b->bufsiz; b->end = b->buf + avail; b->ptr = b->end; PerlIOBase(f)->flags |= PERLIO_F_RDBUF; /* Buffer extends _back_ from where we are now */ b->posn -= b->bufsiz; } if (avail > (SSize_t) count) { /* If we have space for more than count, just move count */ avail = count; } if (avail > 0) { b->ptr -= avail; buf -= avail; /* In simple stdio-like ungetc() case chars will be already there */ if (buf != b->ptr) { Copy(buf,b->ptr,avail,STDCHAR); } count -= avail; unread += avail; PerlIOBase(f)->flags &= ~ PERLIO_F_EOF; } } return unread; } SSize_t PerlIOBuf_write(PerlIO *f, const void *vbuf, Size_t count) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); const STDCHAR *buf = (const STDCHAR *) vbuf; Size_t written = 0; if (!b->buf) PerlIO_get_base(f); if (!(PerlIOBase(f)->flags & PERLIO_F_CANWRITE)) return 0; while (count > 0) { SSize_t avail = b->bufsiz - (b->ptr - b->buf); if ((SSize_t) count < avail) avail = count; PerlIOBase(f)->flags |= PERLIO_F_WRBUF; if (PerlIOBase(f)->flags & PERLIO_F_LINEBUF) { while (avail > 0) { int ch = *buf++; *(b->ptr)++ = ch; count--; avail--; written++; if (ch == '\n') { PerlIO_flush(f); break; } } } else { if (avail) { Copy(buf,b->ptr,avail,STDCHAR); count -= avail; buf += avail; written += avail; b->ptr += avail; } } if (b->ptr >= (b->buf + b->bufsiz)) PerlIO_flush(f); } if (PerlIOBase(f)->flags & PERLIO_F_UNBUF) PerlIO_flush(f); return written; } IV PerlIOBuf_seek(PerlIO *f, Off_t offset, int whence) { IV code; if ((code = PerlIO_flush(f)) == 0) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); PerlIOBase(f)->flags &= ~PERLIO_F_EOF; code = PerlIO_seek(PerlIONext(f),offset,whence); if (code == 0) { b->posn = PerlIO_tell(PerlIONext(f)); } } return code; } Off_t PerlIOBuf_tell(PerlIO *f) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); /* b->posn is file position where b->buf was read, or will be written */ Off_t posn = b->posn; if (b->buf) { /* If buffer is valid adjust position by amount in buffer */ posn += (b->ptr - b->buf); } return posn; } IV PerlIOBuf_close(PerlIO *f) { IV code = PerlIOBase_close(f); PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); if (b->buf && b->buf != (STDCHAR *) &b->oneword) { PerlMemShared_free(b->buf); } b->buf = NULL; b->ptr = b->end = b->buf; PerlIOBase(f)->flags &= ~(PERLIO_F_RDBUF|PERLIO_F_WRBUF); return code; } STDCHAR * PerlIOBuf_get_ptr(PerlIO *f) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); if (!b->buf) PerlIO_get_base(f); return b->ptr; } SSize_t PerlIOBuf_get_cnt(PerlIO *f) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); if (!b->buf) PerlIO_get_base(f); if (PerlIOBase(f)->flags & PERLIO_F_RDBUF) return (b->end - b->ptr); return 0; } STDCHAR * PerlIOBuf_get_base(PerlIO *f) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); if (!b->buf) { if (!b->bufsiz) b->bufsiz = 4096; b->buf = PerlMemShared_calloc(b->bufsiz,sizeof(STDCHAR)); if (!b->buf) { b->buf = (STDCHAR *)&b->oneword; b->bufsiz = sizeof(b->oneword); } b->ptr = b->buf; b->end = b->ptr; } return b->buf; } Size_t PerlIOBuf_bufsiz(PerlIO *f) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); if (!b->buf) PerlIO_get_base(f); return (b->end - b->buf); } void PerlIOBuf_set_ptrcnt(PerlIO *f, STDCHAR *ptr, SSize_t cnt) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); if (!b->buf) PerlIO_get_base(f); b->ptr = ptr; if (PerlIO_get_cnt(f) != cnt || b->ptr < b->buf) { dTHX; assert(PerlIO_get_cnt(f) == cnt); assert(b->ptr >= b->buf); } PerlIOBase(f)->flags |= PERLIO_F_RDBUF; } PerlIO_funcs PerlIO_perlio = { "perlio", sizeof(PerlIOBuf), PERLIO_K_BUFFERED, PerlIOBuf_pushed, PerlIOBase_noop_ok, PerlIOBuf_open, NULL, PerlIOBase_fileno, PerlIOBuf_read, PerlIOBuf_unread, PerlIOBuf_write, PerlIOBuf_seek, PerlIOBuf_tell, PerlIOBuf_close, PerlIOBuf_flush, PerlIOBuf_fill, PerlIOBase_eof, PerlIOBase_error, PerlIOBase_clearerr, PerlIOBase_setlinebuf, PerlIOBuf_get_base, PerlIOBuf_bufsiz, PerlIOBuf_get_ptr, PerlIOBuf_get_cnt, PerlIOBuf_set_ptrcnt, }; /*--------------------------------------------------------------------------------------*/ /* Temp layer to hold unread chars when cannot do it any other way */ IV PerlIOPending_fill(PerlIO *f) { /* Should never happen */ PerlIO_flush(f); return 0; } IV PerlIOPending_close(PerlIO *f) { /* A tad tricky - flush pops us, then we close new top */ PerlIO_flush(f); return PerlIO_close(f); } IV PerlIOPending_seek(PerlIO *f, Off_t offset, int whence) { /* A tad tricky - flush pops us, then we seek new top */ PerlIO_flush(f); return PerlIO_seek(f,offset,whence); } IV PerlIOPending_flush(PerlIO *f) { dTHX; PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); if (b->buf && b->buf != (STDCHAR *) &b->oneword) { PerlMemShared_free(b->buf); b->buf = NULL; } PerlIO_pop(aTHX_ f); return 0; } void PerlIOPending_set_ptrcnt(PerlIO *f, STDCHAR *ptr, SSize_t cnt) { if (cnt <= 0) { PerlIO_flush(f); } else { PerlIOBuf_set_ptrcnt(f,ptr,cnt); } } IV PerlIOPending_pushed(PerlIO *f,const char *mode,SV *arg) { IV code = PerlIOBase_pushed(f,mode,arg); PerlIOl *l = PerlIOBase(f); /* Our PerlIO_fast_gets must match what we are pushed on, or sv_gets() etc. get muddled when it changes mid-string when we auto-pop. */ l->flags = (l->flags & ~(PERLIO_F_FASTGETS|PERLIO_F_UTF8)) | (PerlIOBase(PerlIONext(f))->flags & (PERLIO_F_FASTGETS|PERLIO_F_UTF8)); return code; } SSize_t PerlIOPending_read(PerlIO *f, void *vbuf, Size_t count) { SSize_t avail = PerlIO_get_cnt(f); SSize_t got = 0; if (count < avail) avail = count; if (avail > 0) got = PerlIOBuf_read(f,vbuf,avail); if (got >= 0 && got < count) { SSize_t more = PerlIO_read(f,((STDCHAR *) vbuf)+got,count-got); if (more >= 0 || got == 0) got += more; } return got; } PerlIO_funcs PerlIO_pending = { "pending", sizeof(PerlIOBuf), PERLIO_K_BUFFERED, PerlIOPending_pushed, PerlIOBase_noop_ok, NULL, NULL, PerlIOBase_fileno, PerlIOPending_read, PerlIOBuf_unread, PerlIOBuf_write, PerlIOPending_seek, PerlIOBuf_tell, PerlIOPending_close, PerlIOPending_flush, PerlIOPending_fill, PerlIOBase_eof, PerlIOBase_error, PerlIOBase_clearerr, PerlIOBase_setlinebuf, PerlIOBuf_get_base, PerlIOBuf_bufsiz, PerlIOBuf_get_ptr, PerlIOBuf_get_cnt, PerlIOPending_set_ptrcnt, }; /*--------------------------------------------------------------------------------------*/ /* crlf - translation On read translate CR,LF to "\n" we do this by overriding ptr/cnt entries to hand back a line at a time and keeping a record of which nl we "lied" about. On write translate "\n" to CR,LF */ typedef struct { PerlIOBuf base; /* PerlIOBuf stuff */ STDCHAR *nl; /* Position of crlf we "lied" about in the buffer */ } PerlIOCrlf; IV PerlIOCrlf_pushed(PerlIO *f, const char *mode,SV *arg) { IV code; PerlIOBase(f)->flags |= PERLIO_F_CRLF; code = PerlIOBuf_pushed(f,mode,arg); #if 0 PerlIO_debug("PerlIOCrlf_pushed f=%p %s %s fl=%08"UVxf"\n", f,PerlIOBase(f)->tab->name,(mode) ? mode : "(Null)", PerlIOBase(f)->flags); #endif return code; } SSize_t PerlIOCrlf_unread(PerlIO *f, const void *vbuf, Size_t count) { PerlIOCrlf *c = PerlIOSelf(f,PerlIOCrlf); if (c->nl) { *(c->nl) = 0xd; c->nl = NULL; } if (!(PerlIOBase(f)->flags & PERLIO_F_CRLF)) return PerlIOBuf_unread(f,vbuf,count); else { const STDCHAR *buf = (const STDCHAR *) vbuf+count; PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); SSize_t unread = 0; if (PerlIOBase(f)->flags & PERLIO_F_WRBUF) PerlIO_flush(f); if (!b->buf) PerlIO_get_base(f); if (b->buf) { if (!(PerlIOBase(f)->flags & PERLIO_F_RDBUF)) { b->end = b->ptr = b->buf + b->bufsiz; PerlIOBase(f)->flags |= PERLIO_F_RDBUF; b->posn -= b->bufsiz; } while (count > 0 && b->ptr > b->buf) { int ch = *--buf; if (ch == '\n') { if (b->ptr - 2 >= b->buf) { *--(b->ptr) = 0xa; *--(b->ptr) = 0xd; unread++; count--; } else { buf++; break; } } else { *--(b->ptr) = ch; unread++; count--; } } } return unread; } } SSize_t PerlIOCrlf_get_cnt(PerlIO *f) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); if (!b->buf) PerlIO_get_base(f); if (PerlIOBase(f)->flags & PERLIO_F_RDBUF) { PerlIOCrlf *c = PerlIOSelf(f,PerlIOCrlf); if ((PerlIOBase(f)->flags & PERLIO_F_CRLF) && !c->nl) { STDCHAR *nl = b->ptr; scan: while (nl < b->end && *nl != 0xd) nl++; if (nl < b->end && *nl == 0xd) { test: if (nl+1 < b->end) { if (nl[1] == 0xa) { *nl = '\n'; c->nl = nl; } else { /* Not CR,LF but just CR */ nl++; goto scan; } } else { /* Blast - found CR as last char in buffer */ if (b->ptr < nl) { /* They may not care, defer work as long as possible */ return (nl - b->ptr); } else { int code; b->ptr++; /* say we have read it as far as flush() is concerned */ b->buf++; /* Leave space an front of buffer */ b->bufsiz--; /* Buffer is thus smaller */ code = PerlIO_fill(f); /* Fetch some more */ b->bufsiz++; /* Restore size for next time */ b->buf--; /* Point at space */ b->ptr = nl = b->buf; /* Which is what we hand off */ b->posn--; /* Buffer starts here */ *nl = 0xd; /* Fill in the CR */ if (code == 0) goto test; /* fill() call worked */ /* CR at EOF - just fall through */ } } } } return (((c->nl) ? (c->nl+1) : b->end) - b->ptr); } return 0; } void PerlIOCrlf_set_ptrcnt(PerlIO *f, STDCHAR *ptr, SSize_t cnt) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); PerlIOCrlf *c = PerlIOSelf(f,PerlIOCrlf); IV flags = PerlIOBase(f)->flags; if (!b->buf) PerlIO_get_base(f); if (!ptr) { if (c->nl) ptr = c->nl+1; else { ptr = b->end; if ((flags & PERLIO_F_CRLF) && ptr > b->buf && ptr[-1] == 0xd) ptr--; } ptr -= cnt; } else { /* Test code - delete when it works ... */ STDCHAR *chk; if (c->nl) chk = c->nl+1; else { chk = b->end; if ((flags & PERLIO_F_CRLF) && chk > b->buf && chk[-1] == 0xd) chk--; } chk -= cnt; if (ptr != chk) { dTHX; Perl_croak(aTHX_ "ptr wrong %p != %p fl=%08"UVxf" nl=%p e=%p for %d", ptr, chk, flags, c->nl, b->end, cnt); } } if (c->nl) { if (ptr > c->nl) { /* They have taken what we lied about */ *(c->nl) = 0xd; c->nl = NULL; ptr++; } } b->ptr = ptr; PerlIOBase(f)->flags |= PERLIO_F_RDBUF; } SSize_t PerlIOCrlf_write(PerlIO *f, const void *vbuf, Size_t count) { if (!(PerlIOBase(f)->flags & PERLIO_F_CRLF)) return PerlIOBuf_write(f,vbuf,count); else { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); const STDCHAR *buf = (const STDCHAR *) vbuf; const STDCHAR *ebuf = buf+count; if (!b->buf) PerlIO_get_base(f); if (!(PerlIOBase(f)->flags & PERLIO_F_CANWRITE)) return 0; while (buf < ebuf) { STDCHAR *eptr = b->buf+b->bufsiz; PerlIOBase(f)->flags |= PERLIO_F_WRBUF; while (buf < ebuf && b->ptr < eptr) { if (*buf == '\n') { if ((b->ptr + 2) > eptr) { /* Not room for both */ PerlIO_flush(f); break; } else { *(b->ptr)++ = 0xd; /* CR */ *(b->ptr)++ = 0xa; /* LF */ buf++; if (PerlIOBase(f)->flags & PERLIO_F_LINEBUF) { PerlIO_flush(f); break; } } } else { int ch = *buf++; *(b->ptr)++ = ch; } if (b->ptr >= eptr) { PerlIO_flush(f); break; } } } if (PerlIOBase(f)->flags & PERLIO_F_UNBUF) PerlIO_flush(f); return (buf - (STDCHAR *) vbuf); } } IV PerlIOCrlf_flush(PerlIO *f) { PerlIOCrlf *c = PerlIOSelf(f,PerlIOCrlf); if (c->nl) { *(c->nl) = 0xd; c->nl = NULL; } return PerlIOBuf_flush(f); } PerlIO_funcs PerlIO_crlf = { "crlf", sizeof(PerlIOCrlf), PERLIO_K_BUFFERED|PERLIO_K_CANCRLF, PerlIOCrlf_pushed, PerlIOBase_noop_ok, /* popped */ PerlIOBuf_open, NULL, PerlIOBase_fileno, PerlIOBuf_read, /* generic read works with ptr/cnt lies ... */ PerlIOCrlf_unread, /* Put CR,LF in buffer for each '\n' */ PerlIOCrlf_write, /* Put CR,LF in buffer for each '\n' */ PerlIOBuf_seek, PerlIOBuf_tell, PerlIOBuf_close, PerlIOCrlf_flush, PerlIOBuf_fill, PerlIOBase_eof, PerlIOBase_error, PerlIOBase_clearerr, PerlIOBase_setlinebuf, PerlIOBuf_get_base, PerlIOBuf_bufsiz, PerlIOBuf_get_ptr, PerlIOCrlf_get_cnt, PerlIOCrlf_set_ptrcnt, }; #ifdef HAS_MMAP /*--------------------------------------------------------------------------------------*/ /* mmap as "buffer" layer */ typedef struct { PerlIOBuf base; /* PerlIOBuf stuff */ Mmap_t mptr; /* Mapped address */ Size_t len; /* mapped length */ STDCHAR *bbuf; /* malloced buffer if map fails */ } PerlIOMmap; static size_t page_size = 0; IV PerlIOMmap_map(PerlIO *f) { dTHX; PerlIOMmap *m = PerlIOSelf(f,PerlIOMmap); IV flags = PerlIOBase(f)->flags; IV code = 0; if (m->len) abort(); if (flags & PERLIO_F_CANREAD) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); int fd = PerlIO_fileno(f); struct stat st; code = fstat(fd,&st); if (code == 0 && S_ISREG(st.st_mode)) { SSize_t len = st.st_size - b->posn; if (len > 0) { Off_t posn; if (!page_size) { #if defined(HAS_SYSCONF) && (defined(_SC_PAGESIZE) || defined(_SC_PAGE_SIZE)) { SETERRNO(0,SS$_NORMAL); # ifdef _SC_PAGESIZE page_size = sysconf(_SC_PAGESIZE); # else page_size = sysconf(_SC_PAGE_SIZE); # endif if ((long)page_size < 0) { if (errno) { SV *error = ERRSV; char *msg; STRLEN n_a; (void)SvUPGRADE(error, SVt_PV); msg = SvPVx(error, n_a); Perl_croak(aTHX_ "panic: sysconf: %s", msg); } else Perl_croak(aTHX_ "panic: sysconf: pagesize unknown"); } } #else # ifdef HAS_GETPAGESIZE page_size = getpagesize(); # else # if defined(I_SYS_PARAM) && defined(PAGESIZE) page_size = PAGESIZE; /* compiletime, bad */ # endif # endif #endif if ((IV)page_size <= 0) Perl_croak(aTHX_ "panic: bad pagesize %"IVdf, (IV)page_size); } if (b->posn < 0) { /* This is a hack - should never happen - open should have set it ! */ b->posn = PerlIO_tell(PerlIONext(f)); } posn = (b->posn / page_size) * page_size; len = st.st_size - posn; m->mptr = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, posn); if (m->mptr && m->mptr != (Mmap_t) -1) { #if 0 && defined(HAS_MADVISE) && defined(MADV_SEQUENTIAL) madvise(m->mptr, len, MADV_SEQUENTIAL); #endif #if 0 && defined(HAS_MADVISE) && defined(MADV_WILLNEED) madvise(m->mptr, len, MADV_WILLNEED); #endif PerlIOBase(f)->flags = (flags & ~PERLIO_F_EOF) | PERLIO_F_RDBUF; b->end = ((STDCHAR *)m->mptr) + len; b->buf = ((STDCHAR *)m->mptr) + (b->posn - posn); b->ptr = b->buf; m->len = len; } else { b->buf = NULL; } } else { PerlIOBase(f)->flags = flags | PERLIO_F_EOF | PERLIO_F_RDBUF; b->buf = NULL; b->ptr = b->end = b->ptr; code = -1; } } } return code; } IV PerlIOMmap_unmap(PerlIO *f) { PerlIOMmap *m = PerlIOSelf(f,PerlIOMmap); PerlIOBuf *b = &m->base; IV code = 0; if (m->len) { if (b->buf) { code = munmap(m->mptr, m->len); b->buf = NULL; m->len = 0; m->mptr = NULL; if (PerlIO_seek(PerlIONext(f),b->posn,SEEK_SET) != 0) code = -1; } b->ptr = b->end = b->buf; PerlIOBase(f)->flags &= ~(PERLIO_F_RDBUF|PERLIO_F_WRBUF); } return code; } STDCHAR * PerlIOMmap_get_base(PerlIO *f) { PerlIOMmap *m = PerlIOSelf(f,PerlIOMmap); PerlIOBuf *b = &m->base; if (b->buf && (PerlIOBase(f)->flags & PERLIO_F_RDBUF)) { /* Already have a readbuffer in progress */ return b->buf; } if (b->buf) { /* We have a write buffer or flushed PerlIOBuf read buffer */ m->bbuf = b->buf; /* save it in case we need it again */ b->buf = NULL; /* Clear to trigger below */ } if (!b->buf) { PerlIOMmap_map(f); /* Try and map it */ if (!b->buf) { /* Map did not work - recover PerlIOBuf buffer if we have one */ b->buf = m->bbuf; } } b->ptr = b->end = b->buf; if (b->buf) return b->buf; return PerlIOBuf_get_base(f); } SSize_t PerlIOMmap_unread(PerlIO *f, const void *vbuf, Size_t count) { PerlIOMmap *m = PerlIOSelf(f,PerlIOMmap); PerlIOBuf *b = &m->base; if (PerlIOBase(f)->flags & PERLIO_F_WRBUF) PerlIO_flush(f); if (b->ptr && (b->ptr - count) >= b->buf && memEQ(b->ptr - count,vbuf,count)) { b->ptr -= count; PerlIOBase(f)->flags &= ~ PERLIO_F_EOF; return count; } if (m->len) { /* Loose the unwritable mapped buffer */ PerlIO_flush(f); /* If flush took the "buffer" see if we have one from before */ if (!b->buf && m->bbuf) b->buf = m->bbuf; if (!b->buf) { PerlIOBuf_get_base(f); m->bbuf = b->buf; } } return PerlIOBuf_unread(f,vbuf,count); } SSize_t PerlIOMmap_write(PerlIO *f, const void *vbuf, Size_t count) { PerlIOMmap *m = PerlIOSelf(f,PerlIOMmap); PerlIOBuf *b = &m->base; if (!b->buf || !(PerlIOBase(f)->flags & PERLIO_F_WRBUF)) { /* No, or wrong sort of, buffer */ if (m->len) { if (PerlIOMmap_unmap(f) != 0) return 0; } /* If unmap took the "buffer" see if we have one from before */ if (!b->buf && m->bbuf) b->buf = m->bbuf; if (!b->buf) { PerlIOBuf_get_base(f); m->bbuf = b->buf; } } return PerlIOBuf_write(f,vbuf,count); } IV PerlIOMmap_flush(PerlIO *f) { PerlIOMmap *m = PerlIOSelf(f,PerlIOMmap); PerlIOBuf *b = &m->base; IV code = PerlIOBuf_flush(f); /* Now we are "synced" at PerlIOBuf level */ if (b->buf) { if (m->len) { /* Unmap the buffer */ if (PerlIOMmap_unmap(f) != 0) code = -1; } else { /* We seem to have a PerlIOBuf buffer which was not mapped * remember it in case we need one later */ m->bbuf = b->buf; } } return code; } IV PerlIOMmap_fill(PerlIO *f) { PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); IV code = PerlIO_flush(f); if (code == 0 && !b->buf) { code = PerlIOMmap_map(f); } if (code == 0 && !(PerlIOBase(f)->flags & PERLIO_F_RDBUF)) { code = PerlIOBuf_fill(f); } return code; } IV PerlIOMmap_close(PerlIO *f) { PerlIOMmap *m = PerlIOSelf(f,PerlIOMmap); PerlIOBuf *b = &m->base; IV code = PerlIO_flush(f); if (m->bbuf) { b->buf = m->bbuf; m->bbuf = NULL; b->ptr = b->end = b->buf; } if (PerlIOBuf_close(f) != 0) code = -1; return code; } PerlIO_funcs PerlIO_mmap = { "mmap", sizeof(PerlIOMmap), PERLIO_K_BUFFERED, PerlIOBuf_pushed, PerlIOBase_noop_ok, PerlIOBuf_open, NULL, PerlIOBase_fileno, PerlIOBuf_read, PerlIOMmap_unread, PerlIOMmap_write, PerlIOBuf_seek, PerlIOBuf_tell, PerlIOBuf_close, PerlIOMmap_flush, PerlIOMmap_fill, PerlIOBase_eof, PerlIOBase_error, PerlIOBase_clearerr, PerlIOBase_setlinebuf, PerlIOMmap_get_base, PerlIOBuf_bufsiz, PerlIOBuf_get_ptr, PerlIOBuf_get_cnt, PerlIOBuf_set_ptrcnt, }; #endif /* HAS_MMAP */ void PerlIO_init(void) { dTHX; #ifndef WIN32 call_atexit(PerlIO_cleanup_layers, NULL); #endif if (!_perlio) { #ifndef WIN32 atexit(&PerlIO_cleanup); #endif } } #undef PerlIO_stdin PerlIO * PerlIO_stdin(void) { if (!_perlio) { dTHX; PerlIO_stdstreams(aTHX); } return &_perlio[1]; } #undef PerlIO_stdout PerlIO * PerlIO_stdout(void) { if (!_perlio) { dTHX; PerlIO_stdstreams(aTHX); } return &_perlio[2]; } #undef PerlIO_stderr PerlIO * PerlIO_stderr(void) { if (!_perlio) { dTHX; PerlIO_stdstreams(aTHX); } return &_perlio[3]; } /*--------------------------------------------------------------------------------------*/ #undef PerlIO_getname char * PerlIO_getname(PerlIO *f, char *buf) { dTHX; char *name = NULL; #ifdef VMS FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; if (stdio) name = fgetname(stdio, buf); #else Perl_croak(aTHX_ "Don't know how to get file name"); #endif return name; } /*--------------------------------------------------------------------------------------*/ /* Functions which can be called on any kind of PerlIO implemented in terms of above */ #undef PerlIO_getc int PerlIO_getc(PerlIO *f) { STDCHAR buf[1]; SSize_t count = PerlIO_read(f,buf,1); if (count == 1) { return (unsigned char) buf[0]; } return EOF; } #undef PerlIO_ungetc int PerlIO_ungetc(PerlIO *f, int ch) { if (ch != EOF) { STDCHAR buf = ch; if (PerlIO_unread(f,&buf,1) == 1) return ch; } return EOF; } #undef PerlIO_putc int PerlIO_putc(PerlIO *f, int ch) { STDCHAR buf = ch; return PerlIO_write(f,&buf,1); } #undef PerlIO_puts int PerlIO_puts(PerlIO *f, const char *s) { STRLEN len = strlen(s); return PerlIO_write(f,s,len); } #undef PerlIO_rewind void PerlIO_rewind(PerlIO *f) { PerlIO_seek(f,(Off_t)0,SEEK_SET); PerlIO_clearerr(f); } #undef PerlIO_vprintf int PerlIO_vprintf(PerlIO *f, const char *fmt, va_list ap) { dTHX; SV *sv = newSVpvn("",0); char *s; STRLEN len; SSize_t wrote; #ifdef NEED_VA_COPY va_list apc; Perl_va_copy(ap, apc); sv_vcatpvf(sv, fmt, &apc); #else sv_vcatpvf(sv, fmt, &ap); #endif s = SvPV(sv,len); wrote = PerlIO_write(f,s,len); SvREFCNT_dec(sv); return wrote; } #undef PerlIO_printf int PerlIO_printf(PerlIO *f,const char *fmt,...) { va_list ap; int result; va_start(ap,fmt); result = PerlIO_vprintf(f,fmt,ap); va_end(ap); return result; } #undef PerlIO_stdoutf int PerlIO_stdoutf(const char *fmt,...) { va_list ap; int result; va_start(ap,fmt); result = PerlIO_vprintf(PerlIO_stdout(),fmt,ap); va_end(ap); return result; } #undef PerlIO_tmpfile PerlIO * PerlIO_tmpfile(void) { /* I have no idea how portable mkstemp() is ... */ #if defined(WIN32) || !defined(HAVE_MKSTEMP) dTHX; PerlIO *f = NULL; FILE *stdio = PerlSIO_tmpfile(); if (stdio) { PerlIOStdio *s = PerlIOSelf(PerlIO_push(aTHX_ (f = PerlIO_allocate(aTHX)),&PerlIO_stdio,"w+",Nullsv),PerlIOStdio); s->stdio = stdio; } return f; #else dTHX; SV *sv = newSVpv("/tmp/PerlIO_XXXXXX",0); int fd = mkstemp(SvPVX(sv)); PerlIO *f = NULL; if (fd >= 0) { f = PerlIO_fdopen(fd,"w+"); if (f) { PerlIOBase(f)->flags |= PERLIO_F_TEMP; } PerlLIO_unlink(SvPVX(sv)); SvREFCNT_dec(sv); } return f; #endif } #undef HAS_FSETPOS #undef HAS_FGETPOS #endif /* USE_SFIO */ #endif /* PERLIO_IS_STDIO */ /*======================================================================================*/ /* Now some functions in terms of above which may be needed even if we are not in true PerlIO mode */ #ifndef HAS_FSETPOS #undef PerlIO_setpos int PerlIO_setpos(PerlIO *f, SV *pos) { dTHX; if (SvOK(pos)) { STRLEN len; Off_t *posn = (Off_t *) SvPV(pos,len); if (f && len == sizeof(Off_t)) return PerlIO_seek(f,*posn,SEEK_SET); } SETERRNO(EINVAL,SS$_IVCHAN); return -1; } #else #undef PerlIO_setpos int PerlIO_setpos(PerlIO *f, SV *pos) { dTHX; if (SvOK(pos)) { STRLEN len; Fpos_t *fpos = (Fpos_t *) SvPV(pos,len); if (f && len == sizeof(Fpos_t)) { #if defined(USE_64_BIT_STDIO) && defined(USE_FSETPOS64) return fsetpos64(f, fpos); #else return fsetpos(f, fpos); #endif } } SETERRNO(EINVAL,SS$_IVCHAN); return -1; } #endif #ifndef HAS_FGETPOS #undef PerlIO_getpos int PerlIO_getpos(PerlIO *f, SV *pos) { dTHX; Off_t posn = PerlIO_tell(f); sv_setpvn(pos,(char *)&posn,sizeof(posn)); return (posn == (Off_t)-1) ? -1 : 0; } #else #undef PerlIO_getpos int PerlIO_getpos(PerlIO *f, SV *pos) { dTHX; Fpos_t fpos; int code; #if defined(USE_64_BIT_STDIO) && defined(USE_FSETPOS64) code = fgetpos64(f, &fpos); #else code = fgetpos(f, &fpos); #endif sv_setpvn(pos,(char *)&fpos,sizeof(fpos)); return code; } #endif #if (defined(PERLIO_IS_STDIO) || !defined(USE_SFIO)) && !defined(HAS_VPRINTF) int vprintf(char *pat, char *args) { _doprnt(pat, args, stdout); return 0; /* wrong, but perl doesn't use the return value */ } int vfprintf(FILE *fd, char *pat, char *args) { _doprnt(pat, args, fd); return 0; /* wrong, but perl doesn't use the return value */ } #endif #ifndef PerlIO_vsprintf int PerlIO_vsprintf(char *s, int n, const char *fmt, va_list ap) { int val = vsprintf(s, fmt, ap); if (n >= 0) { if (strlen(s) >= (STRLEN)n) { dTHX; (void)PerlIO_puts(Perl_error_log, "panic: sprintf overflow - memory corrupted!\n"); my_exit(1); } } return val; } #endif #ifndef PerlIO_sprintf int PerlIO_sprintf(char *s, int n, const char *fmt,...) { va_list ap; int result; va_start(ap,fmt); result = PerlIO_vsprintf(s, n, fmt, ap); va_end(ap); return result; } #endif