diff options
author | Jarkko Hietaniemi <jhi@iki.fi> | 2000-11-04 22:43:56 +0000 |
---|---|---|
committer | Jarkko Hietaniemi <jhi@iki.fi> | 2000-11-04 22:43:56 +0000 |
commit | 6ac94dd724117eebd4840593a6c1fc07770a26fb (patch) | |
tree | 1de0f49948378469de095e74d9d9099302fa2cd3 | |
parent | f83701cd86be3616a770a8d28c295a3f15ac21d0 (diff) | |
parent | 5f5aa521de335cf2067dde611355a38216d7287b (diff) | |
download | perl-6ac94dd724117eebd4840593a6c1fc07770a26fb.tar.gz |
Integrate perlio:
[ 7539]
PerlIO infrastructure complete.
[ 7538]
Type tweaks + less contorted allocation scheme
[ 7537]
Fix for stdio as default "discipline" - PerlIO_init() was fdopen(2,"w")'ing
a fresh FILE * rather than re-using stderr. Which meant PerlIO_stderr() was
fully buffered rather than unbuffered (on Solaris, Linux seemed to do something
sensible) which lead to some interesting fails.
[ 7535]
Implement stack of layers - (perlio.c _is_ derived from the old file honest...)
- Works on Linux with
perlio + unix
stdio
- Works on Solaris with
perlio + unix
- Fails ONE test (print to STDIN should fail) on Solaris with stdio.
- Fails (hangs in openpid) if you try and stack
perlio + stdio - Linux stdio's read() logic is hanging.
[ 7492]
Change files which are mysteriously different to mainline to be
copies of mainline.
[ 7491]
Perlio fixes discovered on big-endian & very traditional Solaris:
- typo in endian code in putc.
- Don't allow read of write-only files and vice-versa
- and off-by-one in flush-all loop.
Remove debug calls as they were using GCC specific features.
[ 7484]
PerlIO passes all tests.
[ 7482]
Include <unistd.h> to get correct lseek() prototype etc.
(I thought perl.h did that) - down to two fails
- comp/require.t (last test)
- lib/io_xs.t - possibly import/export of FILE * ?
[ 7480]
Fixed two bugs:
- error code not being set on close (of broken pipe)
- append mode was truncating.
At least one seek/tell bug remains.
[ 7479]
Prototype (stdio-like) PerlIO passing basic tests. Checked in
in case of accidents. Still several worrying fails, no line disciplines yet.
p4raw-link: @7539 on //depot/perlio: f3862f8bcf6d3aa824432654b287f4ebd64db17f
p4raw-link: @7538 on //depot/perlio: 05d1247b4b0324742a6edccf90ff347d8905fcdb
p4raw-link: @7537 on //depot/perlio: c7fc522f3f7e35723803aaacf8c326dac22dae76
p4raw-link: @7535 on //depot/perlio: 9e353e3b7330a59ca210e75e4484e7762fcd1ce4
p4raw-link: @7492 on //depot/perlio: e9e021e644582e6ca1e9f6b4c1f1a8a7c7e2a58d
p4raw-link: @7491 on //depot/perlio: f89522bf4daaf3c639b016283ffbace973e9c323
p4raw-link: @7484 on //depot/perlio: b1ef6e3bd726972447a8b536231f096656903bb3
p4raw-link: @7482 on //depot/perlio: 02f66e2f9235025f08502389e56df70aa71733c0
p4raw-link: @7480 on //depot/perlio: bb9950b796df42e2f824a072ae878c87e977be20
p4raw-link: @7479 on //depot/perlio: 6f9d8c32c6a78a47c6088f50d7051d779f712ee1
p4raw-id: //depot/perl@7547
-rw-r--r-- | MANIFEST | 1 | ||||
-rw-r--r-- | iperlsys.h | 49 | ||||
-rw-r--r-- | lib/perlio.pm | 87 | ||||
-rw-r--r-- | objXSUB.h | 2 | ||||
-rw-r--r-- | perlapi.c | 8 | ||||
-rw-r--r-- | perlio.c | 1895 | ||||
-rw-r--r-- | pod/perlapi.pod | 8 | ||||
-rwxr-xr-x | t/lib/b.t | 1 |
8 files changed, 1769 insertions, 282 deletions
@@ -772,6 +772,7 @@ lib/open2.pl Open a two-ended pipe (uses IPC::Open2) lib/open3.pl Open a three-ended pipe (uses IPC::Open3) lib/overload.pm Module for overloading perl operators lib/perl5db.pl Perl debugging routines +lib/perlio.pm Perl IO interface pragma lib/pwd.pl Routines to keep track of PWD environment variable lib/shellwords.pl Perl library to split into words with shell quoting lib/sigtrap.pm For trapping an abort and giving traceback diff --git a/iperlsys.h b/iperlsys.h index 6844801bb4..55471cdbd4 100644 --- a/iperlsys.h +++ b/iperlsys.h @@ -78,11 +78,17 @@ extern void PerlIO_init (void); typedef Signal_t (*Sighandler_t) (int); #endif +#ifndef Fpos_t +#define Fpos_t Off_t +#endif + #if defined(PERL_IMPLICIT_SYS) #ifndef PerlIO -typedef struct _PerlIO PerlIO; -#endif +typedef struct _PerlIO PerlIOl; +typedef PerlIOl *PerlIO; +#define PerlIO PerlIO +#endif /* No PerlIO */ /* IPerlStdIO */ struct IPerlStdIO; @@ -120,7 +126,7 @@ typedef void (*LPSetCnt)(struct IPerlStdIO*, PerlIO*, int); typedef void (*LPSetPtrCnt)(struct IPerlStdIO*, PerlIO*, char*, int); typedef void (*LPSetlinebuf)(struct IPerlStdIO*, PerlIO*); -typedef int (*LPPrintf)(struct IPerlStdIO*, PerlIO*, const char*, +typedef int (*LPPrintf)(struct IPerlStdIO*, PerlIO*, const char*, ...); typedef int (*LPVprintf)(struct IPerlStdIO*, PerlIO*, const char*, va_list); @@ -134,6 +140,7 @@ typedef int (*LPSetpos)(struct IPerlStdIO*, PerlIO*, typedef void (*LPInit)(struct IPerlStdIO*); typedef void (*LPInitOSExtras)(struct IPerlStdIO*); typedef PerlIO* (*LPFdupopen)(struct IPerlStdIO*, PerlIO*); +typedef int (*LPIsUtf8)(struct IPerlStdIO*, PerlIO*); struct IPerlStdIO { @@ -176,6 +183,7 @@ struct IPerlStdIO LPInit pInit; LPInitOSExtras pInitOSExtras; LPFdupopen pFdupopen; + LPIsUtf8 pIsUtf8; }; struct IPerlStdIOInfo @@ -185,19 +193,19 @@ struct IPerlStdIOInfo }; #ifdef USE_STDIO_PTR -# define PerlIO_has_cntptr(f) 1 +# define PerlIO_has_cntptr(f) 1 # ifdef STDIO_PTR_LVALUE # ifdef STDIO_CNT_LVALUE -# define PerlIO_canset_cnt(f) 1 +# define PerlIO_canset_cnt(f) 1 # ifdef STDIO_PTR_LVAL_NOCHANGE_CNT -# define PerlIO_fast_gets(f) 1 +# define PerlIO_fast_gets(f) 1 # endif # else /* STDIO_CNT_LVALUE */ -# define PerlIO_canset_cnt(f) 0 +# define PerlIO_canset_cnt(f) 0 # endif # else /* STDIO_PTR_LVALUE */ # ifdef STDIO_PTR_LVAL_SETS_CNT -# define PerlIO_fast_gets(f) 1 +# define PerlIO_fast_gets(f) 1 # endif # endif #else /* USE_STDIO_PTR */ @@ -206,7 +214,7 @@ struct IPerlStdIOInfo #endif /* USE_STDIO_PTR */ #ifndef PerlIO_fast_gets -#define PerlIO_fast_gets(f) 0 +#define PerlIO_fast_gets(f) 0 #endif #ifdef FILE_base @@ -274,7 +282,7 @@ struct IPerlStdIOInfo #define PerlIO_printf Perl_fprintf_nocontext #define PerlIO_stdoutf *PL_StdIO->pPrintf #define PerlIO_vprintf(f,fmt,a) \ - (*PL_StdIO->pVprintf)(PL_StdIO, (f),(fmt),a) + (*PL_StdIO->pVprintf)(PL_StdIO, (f),(fmt),a) #define PerlIO_tell(f) \ (*PL_StdIO->pTell)(PL_StdIO, (f)) #define PerlIO_seek(f,o,w) \ @@ -294,18 +302,22 @@ struct IPerlStdIOInfo (*PL_StdIO->pInitOSExtras)(PL_StdIO) #define PerlIO_fdupopen(f) \ (*PL_StdIO->pFdupopen)(PL_StdIO, (f)) +#define PerlIO_isutf8(f) \ + (*PL_StdIO->pIsUtf8)(PL_StdIO, (f)) #else /* PERL_IMPLICIT_SYS */ #include "perlsdio.h" #include "perl.h" #define PerlIO_fdupopen(f) (f) +#define PerlIO_isutf8(f) 0 #endif /* PERL_IMPLICIT_SYS */ #ifndef PERLIO_IS_STDIO #ifdef USE_SFIO #include "perlsfio.h" +#define PerlIO_isutf8(f) 0 #endif /* USE_SFIO */ #endif /* PERLIO_IS_STDIO */ @@ -331,14 +343,11 @@ struct IPerlStdIOInfo #endif #ifndef PerlIO -struct _PerlIO; -#define PerlIO struct _PerlIO +typedef struct _PerlIO PerlIOl; +typedef PerlIOl *PerlIO; +#define PerlIO PerlIO #endif /* No PerlIO */ -#ifndef Fpos_t -#define Fpos_t long -#endif - #ifndef NEXT30_NO_ATTRIBUTE #ifndef HASATTRIBUTE /* disable GNU-cc attribute checking? */ #ifdef __attribute__ /* Avoid possible redefinition errors */ @@ -480,7 +489,9 @@ extern int PerlIO_setpos (PerlIO *,const Fpos_t *); #ifndef PerlIO_fdupopen extern PerlIO * PerlIO_fdupopen (PerlIO *); #endif - +#ifndef PerlIO_isutf8 +extern int PerlIO_isutf8 (PerlIO *); +#endif /* * Interface for directory functions @@ -558,7 +569,7 @@ struct IPerlDirInfo #define PerlDir_mkdir(name, mode) Mkdir((name), (mode)) #ifdef VMS # define PerlDir_chdir(n) Chdir(((n) && *(n)) ? (n) : "SYS$LOGIN") -#else +#else # define PerlDir_chdir(name) chdir((name)) #endif #define PerlDir_rmdir(name) rmdir((name)) @@ -1262,7 +1273,7 @@ typedef int (*LPRecvfrom)(struct IPerlSock*, SOCKET, char*, int, typedef int (*LPSelect)(struct IPerlSock*, int, char*, char*, char*, const struct timeval*); typedef int (*LPSend)(struct IPerlSock*, SOCKET, const char*, int, - int); + int); typedef int (*LPSendto)(struct IPerlSock*, SOCKET, const char*, int, int, const struct sockaddr*, int); typedef void (*LPSethostent)(struct IPerlSock*, int); diff --git a/lib/perlio.pm b/lib/perlio.pm new file mode 100644 index 0000000000..48acfbbf0b --- /dev/null +++ b/lib/perlio.pm @@ -0,0 +1,87 @@ +package perlio; +1; +__END__ + +=head1 NAME + +perlio - perl pragma to configure C level IO + +=head1 SYNOPSIS + + Shell: + PERLIO=perlio perl .... + + print "Have ",join(',',keys %perlio::layers),"\n"; + print "Using ",join(',',@perlio::layers),"\n"; + + +=head1 DESCRIPTION + +Mainly a Place holder for now. + +The C<%perlio::layers> hash is a record of the available "layers" that may be pushed +onto a C<PerlIO> stream. + +The C<@perlio::layers> array is the current set of layers that are used when +a new C<PerlIO> stream is opened. The C code looks are the array each time +a stream is opened so the "stack" can be manipulated by messing with the array : + + pop(@perlio::layers); + push(@perlio::layers,$perlio::layers{'stdio'}); + +The values if both the hash and the array are perl objects, of class C<perlio::Layer> +which are created by the C code in C<perlio.c>. As yet there is nothing useful you +can do with the objects at the perl level. + +There are three layers currently defined: + +=over 4 + +=item unix + +Low level layer which calls C<read>, C<write> and C<lseek> etc. + +=item stdio + +Layer which calls C<fread>, C<fwrite> and C<fseek>/C<ftell> etc. +Note that as this is "real" stdio it will ignore any layers beneath it and +got straight to the operating system via the C library as usual. + +=item perlio + +This is a re-implementation of "stdio-like" buffering written as a PerlIO "layer". +As such it will call whatever layer is below it for its operations. + +=back + +=head2 Defaults and how to override them + +If C<Configure> found out how to do "fast" IO using system's stdio, then +the default layers are : + + unix stdio + +Otherwise the default layers are + + unix perlio + +(STDERR will have just unix in this case as that is optimal way to make it +"unbuffered" - do not add a buffering layer!) + +The default may change once perlio has been better tested and tuned. + +The default can be overridden by setting the environment variable PERLIO +to a space separated list of layers (unix is always pushed first). +This can be used to see the effect of/bugs in the various layers e.g. + + cd .../perl/t + PERLIO=stdio ./perl harness + PERLIO=perlio ./perl harness + +=head1 AUTHOR + +Nick Ing-Simmons E<lt>nick@ing-simmons.netE<gt> + +=cut + + @@ -1901,7 +1901,7 @@ #define Perl_whichsig pPerl->Perl_whichsig #undef whichsig #define whichsig Perl_whichsig -#if defined(USE_PURE_BISON) +#ifdef USE_PURE_BISON #else #endif #if defined(MYMALLOC) @@ -2237,21 +2237,21 @@ Perl_init_i18nl14n(pTHXo_ int printwarn) #undef Perl_new_collate void -Perl_new_collate(pTHXo_ const char* newcoll) +Perl_new_collate(pTHXo_ char* newcoll) { ((CPerlObj*)pPerl)->Perl_new_collate(newcoll); } #undef Perl_new_ctype void -Perl_new_ctype(pTHXo_ const char* newctype) +Perl_new_ctype(pTHXo_ char* newctype) { ((CPerlObj*)pPerl)->Perl_new_ctype(newctype); } #undef Perl_new_numeric void -Perl_new_numeric(pTHXo_ const char* newcoll) +Perl_new_numeric(pTHXo_ char* newcoll) { ((CPerlObj*)pPerl)->Perl_new_numeric(newcoll); } @@ -3439,7 +3439,7 @@ Perl_whichsig(pTHXo_ char* sig) { return ((CPerlObj*)pPerl)->Perl_whichsig(sig); } -#if defined(USE_PURE_BISON) +#ifdef USE_PURE_BISON #else #endif #if defined(MYMALLOC) @@ -7,7 +7,6 @@ * */ - #define VOIDUSED 1 #ifdef PERL_MICRO # include "uconfig.h" @@ -15,14 +14,14 @@ # include "config.h" #endif -#define PERLIO_NOT_STDIO 0 +#define PERLIO_NOT_STDIO 0 #if !defined(PERLIO_IS_STDIO) && !defined(USE_SFIO) -#define PerlIO FILE +/* #define PerlIO FILE */ #endif /* - * This file provides those parts of PerlIO abstraction + * This file provides those parts of PerlIO abstraction * which are not #defined in iperlsys.h. - * Which these are depends on various Configure #ifdef's + * Which these are depends on various Configure #ifdef's */ #include "EXTERN.h" @@ -31,15 +30,15 @@ #if !defined(PERL_IMPLICIT_SYS) -#ifdef PERLIO_IS_STDIO +#ifdef PERLIO_IS_STDIO void PerlIO_init(void) { - /* Does nothing (yet) except force this file to be included + /* 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 + of other functions that may be required by loadable + extensions e.g. for FileHandle::tmpfile */ } @@ -57,7 +56,7 @@ PerlIO_tmpfile(void) #undef HAS_FSETPOS #undef HAS_FGETPOS -/* This section is just to make sure these functions +/* This section is just to make sure these functions get pulled in from libsfio.a */ @@ -71,368 +70,1770 @@ PerlIO_tmpfile(void) 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 + /* 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 + * Flush results in a lot of lseek()s to regular files and * lot of small writes to pipes. */ sfset(sfstdout,SF_SHARE,0); } #else /* USE_SFIO */ +/*======================================================================================*/ +/* Implement all the PerlIO interface ourselves. + */ -/* Implement all the PerlIO interface using stdio. - - this should be only file to include <stdio.h> -*/ +/* We _MUST_ have <unistd.h> if we are using lseek() and may have large files */ +#ifdef I_UNISTD +#include <unistd.h> +#endif +#include "XSUB.h" + +#undef printf +void PerlIO_debug(char *fmt,...) __attribute__((format(printf,1,2))); + +void +PerlIO_debug(char *fmt,...) +{ + static int dbg = 0; + if (!dbg) + { + char *s = getenv("PERLIO_DEBUG"); + if (s && *s) + dbg = open(s,O_WRONLY|O_CREAT|O_APPEND,0666); + else + dbg = -1; + } + if (dbg > 0) + { + dTHX; + va_list ap; + SV *sv = newSVpvn("",0); + char *s; + STRLEN len; + va_start(ap,fmt); + 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); + write(dbg,s,len); + va_end(ap); + SvREFCNT_dec(sv); + } +} + +/*--------------------------------------------------------------------------------------*/ + +typedef struct +{ + char * name; + Size_t size; + IV kind; + IV (*Fileno)(PerlIO *f); + PerlIO * (*Fdopen)(int fd, const char *mode); + PerlIO * (*Open)(const char *path, const char *mode); + int (*Reopen)(const char *path, const char *mode, PerlIO *f); + /* Unix-like functions - cf sfio line disciplines */ + SSize_t (*Read)(PerlIO *f, void *vbuf, Size_t count); + SSize_t (*Unread)(PerlIO *f, const void *vbuf, Size_t count); + SSize_t (*Write)(PerlIO *f, const void *vbuf, Size_t count); + IV (*Seek)(PerlIO *f, Off_t offset, int whence); + Off_t (*Tell)(PerlIO *f); + IV (*Close)(PerlIO *f); + /* Stdio-like buffered IO functions */ + IV (*Flush)(PerlIO *f); + IV (*Eof)(PerlIO *f); + IV (*Error)(PerlIO *f); + void (*Clearerr)(PerlIO *f); + void (*Setlinebuf)(PerlIO *f); + /* Perl's snooping functions */ + STDCHAR * (*Get_base)(PerlIO *f); + Size_t (*Get_bufsiz)(PerlIO *f); + STDCHAR * (*Get_ptr)(PerlIO *f); + SSize_t (*Get_cnt)(PerlIO *f); + void (*Set_ptrcnt)(PerlIO *f,STDCHAR *ptr,SSize_t cnt); +} PerlIO_funcs; + + +struct _PerlIO +{ + PerlIOl * next; /* Lower layer */ + PerlIO_funcs * tab; /* Functions for this layer */ + IV flags; /* Various flags for state */ +}; + +/*--------------------------------------------------------------------------------------*/ + +/* Flag values */ +#define PERLIO_F_EOF 0x00010000 +#define PERLIO_F_CANWRITE 0x00020000 +#define PERLIO_F_CANREAD 0x00040000 +#define PERLIO_F_ERROR 0x00080000 +#define PERLIO_F_TRUNCATE 0x00100000 +#define PERLIO_F_APPEND 0x00200000 +#define PERLIO_F_BINARY 0x00400000 +#define PERLIO_F_UTF8 0x00800000 +#define PERLIO_F_LINEBUF 0x01000000 +#define PERLIO_F_WRBUF 0x02000000 +#define PERLIO_F_RDBUF 0x04000000 +#define PERLIO_F_TEMP 0x08000000 +#define PERLIO_F_OPEN 0x10000000 + +#define PerlIOBase(f) (*(f)) +#define PerlIOSelf(f,type) ((type *)PerlIOBase(f)) +#define PerlIONext(f) (&(PerlIOBase(f)->next)) + +/*--------------------------------------------------------------------------------------*/ +/* Inner level routines */ + +/* Table of pointers to the PerlIO structs (malloc'ed) */ +PerlIO *_perlio = NULL; +#define PERLIO_TABLE_SIZE 64 -#undef PerlIO_stderr PerlIO * -PerlIO_stderr(void) +PerlIO_allocate(void) { - return (PerlIO *) stderr; + /* Find a free slot in the table, allocating new table as necessary */ + PerlIO **last = &_perlio; + PerlIO *f; + while ((f = *last)) + { + int i; + last = (PerlIO **)(f); + for (i=1; i < PERLIO_TABLE_SIZE; i++) + { + if (!*++f) + { + return f; + } + } + } + Newz('I',f,PERLIO_TABLE_SIZE,PerlIO); + if (!f) + return NULL; + *last = f; + return f+1; } -#undef PerlIO_stdin +void +PerlIO_cleantable(PerlIO **tablep) +{ + PerlIO *table = *tablep; + if (table) + { + int i; + PerlIO_cleantable((PerlIO **) &(table[0])); + for (i=PERLIO_TABLE_SIZE-1; i > 0; i--) + { + PerlIO *f = table+i; + if (*f) + PerlIO_close(f); + } + Safefree(table); + *tablep = NULL; + } +} + +void +PerlIO_cleanup(void) +{ + PerlIO_cleantable(&_perlio); +} + +void +PerlIO_pop(PerlIO *f) +{ + PerlIOl *l = *f; + if (l) + { + *f = l->next; + Safefree(l); + } +} + +#undef PerlIO_close +int +PerlIO_close(PerlIO *f) +{ + int code = (*PerlIOBase(f)->tab->Close)(f); + while (*f) + { + PerlIO_pop(f); + } + return code; +} + + +/*--------------------------------------------------------------------------------------*/ +/* Given the abstraction above the public API functions */ + +#undef PerlIO_fileno +int +PerlIO_fileno(PerlIO *f) +{ + return (*PerlIOBase(f)->tab->Fileno)(f); +} + + +extern PerlIO_funcs PerlIO_unix; +extern PerlIO_funcs PerlIO_perlio; +extern PerlIO_funcs PerlIO_stdio; + +XS(XS_perlio_import) +{ + dXSARGS; + GV *gv = CvGV(cv); + char *s = GvNAME(gv); + STRLEN l = GvNAMELEN(gv); + PerlIO_debug("%.*s\n",(int) l,s); + XSRETURN_EMPTY; +} + +XS(XS_perlio_unimport) +{ + dXSARGS; + GV *gv = CvGV(cv); + char *s = GvNAME(gv); + STRLEN l = GvNAMELEN(gv); + PerlIO_debug("%.*s\n",(int) l,s); + XSRETURN_EMPTY; +} + +HV *PerlIO_layer_hv; +AV *PerlIO_layer_av; + +SV * +PerlIO_find_layer(char *name, STRLEN len) +{ + dTHX; + SV **svp; + SV *sv; + if (len <= 0) + len = strlen(name); + svp = hv_fetch(PerlIO_layer_hv,name,len,0); + if (svp && (sv = *svp) && SvROK(sv)) + return *svp; + return NULL; +} + +void +PerlIO_define_layer(PerlIO_funcs *tab) +{ + dTHX; + HV *stash = gv_stashpv("perlio::Layer", TRUE); + SV *sv = sv_bless(newRV_noinc(newSViv((IV) tab)),stash); + hv_store(PerlIO_layer_hv,tab->name,strlen(tab->name),sv,0); +} + +PerlIO_funcs * +PerlIO_default_layer(I32 n) +{ + dTHX; + SV **svp; + SV *layer; + PerlIO_funcs *tab = &PerlIO_stdio; + int len; + if (!PerlIO_layer_hv) + { + char *s = getenv("PERLIO"); + newXS("perlio::import",XS_perlio_import,__FILE__); + newXS("perlio::unimport",XS_perlio_unimport,__FILE__); + PerlIO_layer_hv = get_hv("perlio::layers",GV_ADD|GV_ADDMULTI); + PerlIO_layer_av = get_av("perlio::layers",GV_ADD|GV_ADDMULTI); + PerlIO_define_layer(&PerlIO_unix); + PerlIO_define_layer(&PerlIO_unix); + PerlIO_define_layer(&PerlIO_perlio); + PerlIO_define_layer(&PerlIO_stdio); + av_push(PerlIO_layer_av,SvREFCNT_inc(PerlIO_find_layer(PerlIO_unix.name,0))); + if (s) + { + while (*s) + { + while (*s && isspace((unsigned char)*s)) + s++; + if (*s) + { + char *e = s; + SV *layer; + while (*e && !isspace((unsigned char)*e)) + e++; + layer = PerlIO_find_layer(s,e-s); + if (layer) + { + PerlIO_debug("Pushing %.*s\n",(e-s),s); + av_push(PerlIO_layer_av,SvREFCNT_inc(layer)); + } + else + Perl_croak(aTHX_ "Unknown layer %.*s",(e-s),s); + s = e; + } + } + } + } + len = av_len(PerlIO_layer_av); + if (len < 1) + { + if (PerlIO_stdio.Set_ptrcnt) + { + av_push(PerlIO_layer_av,SvREFCNT_inc(PerlIO_find_layer(PerlIO_stdio.name,0))); + } + else + { + av_push(PerlIO_layer_av,SvREFCNT_inc(PerlIO_find_layer(PerlIO_perlio.name,0))); + } + len = av_len(PerlIO_layer_av); + } + if (n < 0) + n += len+1; + svp = av_fetch(PerlIO_layer_av,n,0); + if (svp && (layer = *svp) && SvROK(layer) && SvIOK((layer = SvRV(layer)))) + { + tab = (PerlIO_funcs *) SvIV(layer); + } + /* PerlIO_debug("Layer %d is %s\n",n,tab->name); */ + return tab; +} + +#define PerlIO_default_top() PerlIO_default_layer(-1) +#define PerlIO_default_btm() PerlIO_default_layer(0) + +void +PerlIO_stdstreams() +{ + if (!_perlio) + { + PerlIO_allocate(); + PerlIO_fdopen(0,"Ir"); + PerlIO_fdopen(1,"Iw"); + PerlIO_fdopen(2,"Iw"); + } +} + +#undef PerlIO_fdopen PerlIO * -PerlIO_stdin(void) +PerlIO_fdopen(int fd, const char *mode) { - return (PerlIO *) stdin; + PerlIO_funcs *tab = PerlIO_default_top(); + if (!_perlio) + PerlIO_stdstreams(); + return (*tab->Fdopen)(fd,mode); } -#undef PerlIO_stdout +#undef PerlIO_open PerlIO * -PerlIO_stdout(void) +PerlIO_open(const char *path, const char *mode) +{ + PerlIO_funcs *tab = PerlIO_default_top(); + if (!_perlio) + PerlIO_stdstreams(); + return (*tab->Open)(path,mode); +} + +IV +PerlIOBase_init(PerlIO *f, const char *mode) +{ + PerlIOl *l = PerlIOBase(f); + l->flags &= ~(PERLIO_F_CANREAD|PERLIO_F_CANWRITE| + PERLIO_F_TRUNCATE|PERLIO_F_APPEND|PERLIO_F_BINARY); + if (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: + errno = EINVAL; + return -1; + } + while (*mode) + { + switch (*mode++) + { + case '+': + l->flags |= PERLIO_F_CANREAD|PERLIO_F_CANWRITE; + break; + case 'b': + l->flags |= PERLIO_F_BINARY; + break; + default: + errno = EINVAL; + return -1; + } + } + } + else + { + if (l->next) + { + l->flags |= l->next->flags & + (PERLIO_F_CANREAD|PERLIO_F_CANWRITE| + PERLIO_F_TRUNCATE|PERLIO_F_APPEND|PERLIO_F_BINARY); + } + } + return 0; +} + +#undef PerlIO_reopen +PerlIO * +PerlIO_reopen(const char *path, const char *mode, PerlIO *f) +{ + if (f) + { + PerlIO_flush(f); + if ((*PerlIOBase(f)->tab->Reopen)(path,mode,f) == 0) + { + PerlIOBase_init(f,mode); + return f; + } + return NULL; + } + else + return PerlIO_open(path,mode); +} + +#undef PerlIO_read +SSize_t +PerlIO_read(PerlIO *f, void *vbuf, Size_t count) +{ + return (*PerlIOBase(f)->tab->Read)(f,vbuf,count); +} + +#undef PerlIO_ungetc +int +PerlIO_ungetc(PerlIO *f, int ch) +{ + STDCHAR buf = ch; + if ((*PerlIOBase(f)->tab->Unread)(f,&buf,1) == 1) + return ch; + return -1; +} + +#undef PerlIO_write +SSize_t +PerlIO_write(PerlIO *f, const void *vbuf, Size_t count) +{ + return (*PerlIOBase(f)->tab->Write)(f,vbuf,count); +} + +#undef PerlIO_seek +int +PerlIO_seek(PerlIO *f, Off_t offset, int whence) +{ + return (*PerlIOBase(f)->tab->Seek)(f,offset,whence); +} + +#undef PerlIO_tell +Off_t +PerlIO_tell(PerlIO *f) +{ + return (*PerlIOBase(f)->tab->Tell)(f); +} + +#undef PerlIO_flush +int +PerlIO_flush(PerlIO *f) +{ + if (f) + { + return (*PerlIOBase(f)->tab->Flush)(f); + } + else + { + 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; + } +} + +#undef PerlIO_isutf8 +int +PerlIO_isutf8(PerlIO *f) +{ + return (PerlIOBase(f)->flags & PERLIO_F_UTF8) != 0; +} + +#undef PerlIO_eof +int +PerlIO_eof(PerlIO *f) +{ + return (*PerlIOBase(f)->tab->Eof)(f); +} + +#undef PerlIO_error +int +PerlIO_error(PerlIO *f) { - return (PerlIO *) stdout; + return (*PerlIOBase(f)->tab->Error)(f); +} + +#undef PerlIO_clearerr +void +PerlIO_clearerr(PerlIO *f) +{ + (*PerlIOBase(f)->tab->Clearerr)(f); +} + +#undef PerlIO_setlinebuf +void +PerlIO_setlinebuf(PerlIO *f) +{ + (*PerlIOBase(f)->tab->Setlinebuf)(f); +} + +#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 +int PerlIO_fast_gets(PerlIO *f) { -#if defined(USE_STDIO_PTR) && defined(STDIO_PTR_LVALUE) && defined(STDIO_CNT_LVALUE) - return 1; -#else + if (f && *f) + { + PerlIOl *l = PerlIOBase(f); + return (l->tab->Set_ptrcnt != NULL); + } return 0; -#endif } #undef PerlIO_has_cntptr -int +int PerlIO_has_cntptr(PerlIO *f) { -#if defined(USE_STDIO_PTR) - return 1; -#else + if (f && *f) + { + PerlIO_funcs *tab = PerlIOBase(f)->tab; + return (tab->Get_ptr != NULL && tab->Get_cnt != NULL); + } return 0; -#endif } #undef PerlIO_canset_cnt -int +int PerlIO_canset_cnt(PerlIO *f) { -#if defined(USE_STDIO_PTR) && defined(STDIO_CNT_LVALUE) - return 1; -#else + if (f && *f) + { + PerlIOl *l = PerlIOBase(f); + return (l->tab->Set_ptrcnt != NULL); + } return 0; -#endif +} + +#undef PerlIO_get_base +STDCHAR * +PerlIO_get_base(PerlIO *f) +{ + return (*PerlIOBase(f)->tab->Get_base)(f); +} + +#undef PerlIO_get_bufsiz +int +PerlIO_get_bufsiz(PerlIO *f) +{ + return (*PerlIOBase(f)->tab->Get_bufsiz)(f); +} + +#undef PerlIO_get_ptr +STDCHAR * +PerlIO_get_ptr(PerlIO *f) +{ + return (*PerlIOBase(f)->tab->Get_ptr)(f); +} + +#undef PerlIO_get_cnt +int +PerlIO_get_cnt(PerlIO *f) +{ + return (*PerlIOBase(f)->tab->Get_cnt)(f); } #undef PerlIO_set_cnt void -PerlIO_set_cnt(PerlIO *f, int cnt) +PerlIO_set_cnt(PerlIO *f,int cnt) { - dTHX; - if (cnt < -1 && ckWARN_d(WARN_INTERNAL)) - Perl_warner(aTHX_ WARN_INTERNAL, "Setting cnt to %d\n",cnt); -#if defined(USE_STDIO_PTR) && defined(STDIO_CNT_LVALUE) - FILE_cnt(f) = cnt; -#else - Perl_croak(aTHX_ "Cannot set 'cnt' of FILE * on this system"); -#endif + (*PerlIOBase(f)->tab->Set_ptrcnt)(f,NULL,cnt); } #undef PerlIO_set_ptrcnt void PerlIO_set_ptrcnt(PerlIO *f, STDCHAR *ptr, int cnt) { - dTHX; -#ifdef FILE_bufsiz - STDCHAR *e = FILE_base(f) + FILE_bufsiz(f); - int ec = e - ptr; - if (ptr > e + 1 && ckWARN_d(WARN_INTERNAL)) - Perl_warner(aTHX_ WARN_INTERNAL, "Setting ptr %p > end+1 %p\n", ptr, e + 1); - if (cnt != ec && ckWARN_d(WARN_INTERNAL)) - Perl_warner(aTHX_ WARN_INTERNAL, "Setting cnt to %d, ptr implies %d\n",cnt,ec); -#endif -#if defined(USE_STDIO_PTR) && defined(STDIO_PTR_LVALUE) - FILE_ptr(f) = ptr; -#else - Perl_croak(aTHX_ "Cannot set 'ptr' of FILE * on this system"); -#endif -#if defined(USE_STDIO_PTR) && defined(STDIO_CNT_LVALUE) && defined (STDIO_PTR_LVAL_NOCHANGE_CNT) - FILE_cnt(f) = cnt; -#else -#if defined(STDIO_PTR_LVAL_SETS_CNT) - assert (FILE_cnt(f) == cnt); -#else - Perl_croak(aTHX_ "Cannot set 'cnt' of FILE * on this system when setting 'ptr'"); -#endif -#endif + (*PerlIOBase(f)->tab->Set_ptrcnt)(f,ptr,cnt); } -#undef PerlIO_get_cnt -int -PerlIO_get_cnt(PerlIO *f) +/*--------------------------------------------------------------------------------------*/ +/* "Methods" of the "base class" */ + +IV +PerlIOBase_fileno(PerlIO *f) { -#ifdef FILE_cnt - return FILE_cnt(f); -#else - dTHX; - Perl_croak(aTHX_ "Cannot get 'cnt' of FILE * on this system"); - return -1; -#endif + return PerlIO_fileno(PerlIONext(f)); } -#undef PerlIO_get_bufsiz -int -PerlIO_get_bufsiz(PerlIO *f) +PerlIO * +PerlIO_push(PerlIO *f,PerlIO_funcs *tab,const char *mode) { -#ifdef FILE_bufsiz - return FILE_bufsiz(f); -#else - dTHX; - Perl_croak(aTHX_ "Cannot get 'bufsiz' of FILE * on this system"); + PerlIOl *l = NULL; + Newc('L',l,tab->size,char,PerlIOl); + if (l) + { + Zero(l,tab->size,char); + l->next = *f; + l->tab = tab; + *f = l; + PerlIOBase_init(f,mode); + } + return f; +} + +SSize_t +PerlIOBase_unread(PerlIO *f, const void *vbuf, Size_t count) +{ + Off_t old = PerlIO_tell(f); + if (PerlIO_seek(f,-((Off_t)count),SEEK_CUR) == 0) + { + Off_t new = PerlIO_tell(f); + return old - new; + } + return 0; +} + +IV +PerlIOBase_sync(PerlIO *f) +{ + return 0; +} + +IV +PerlIOBase_close(PerlIO *f) +{ + IV code = 0; + if (PerlIO_flush(f) != 0) + code = -1; + if (PerlIO_close(PerlIONext(f)) != 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) + { + PerlIOBase(f)->flags &= ~PERLIO_F_ERROR; + } +} + +void +PerlIOBase_setlinebuf(PerlIO *f) +{ + +} + + + +/*--------------------------------------------------------------------------------------*/ +/* 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; + 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 || oflags == -1) + { + errno = EINVAL; + oflags = -1; + } + return oflags; +} + +IV +PerlIOUnix_fileno(PerlIO *f) +{ + return PerlIOSelf(f,PerlIOUnix)->fd; +} + +PerlIO * +PerlIOUnix_fdopen(int fd,const char *mode) +{ + PerlIO *f = NULL; + if (*mode == 'I') + mode++; + if (fd >= 0) + { + int oflags = PerlIOUnix_oflags(mode); + if (oflags != -1) + { + PerlIOUnix *s = PerlIOSelf(PerlIO_push(f = PerlIO_allocate(),&PerlIO_unix,mode),PerlIOUnix); + s->fd = fd; + s->oflags = oflags; + PerlIOBase(f)->flags |= PERLIO_F_OPEN; + } + } + return f; +} + +PerlIO * +PerlIOUnix_open(const char *path,const char *mode) +{ + PerlIO *f = NULL; + int oflags = PerlIOUnix_oflags(mode); + if (oflags != -1) + { + int fd = open(path,oflags,0666); + if (fd >= 0) + { + PerlIOUnix *s = PerlIOSelf(PerlIO_push(f = PerlIO_allocate(),&PerlIO_unix,mode),PerlIOUnix); + s->fd = fd; + s->oflags = oflags; + PerlIOBase(f)->flags |= PERLIO_F_OPEN; + } + } + return f; +} + +int +PerlIOUnix_reopen(const char *path, const char *mode, PerlIO *f) +{ + PerlIOUnix *s = PerlIOSelf(f,PerlIOUnix); + int oflags = PerlIOUnix_oflags(mode); + if (PerlIOBase(f)->flags & PERLIO_F_OPEN) + (*PerlIOBase(f)->tab->Close)(f); + if (oflags != -1) + { + int fd = open(path,oflags,0666); + if (fd >= 0) + { + s->fd = fd; + s->oflags = oflags; + PerlIOBase(f)->flags |= PERLIO_F_OPEN; + return 0; + } + } return -1; -#endif } -#undef PerlIO_get_ptr -STDCHAR * -PerlIO_get_ptr(PerlIO *f) +SSize_t +PerlIOUnix_read(PerlIO *f, void *vbuf, Size_t count) { -#ifdef FILE_ptr - return FILE_ptr(f); + int fd = PerlIOSelf(f,PerlIOUnix)->fd; + while (1) + { + SSize_t len = read(fd,vbuf,count); + if (len >= 0 || errno != EINTR) + return len; + } +} + +SSize_t +PerlIOUnix_write(PerlIO *f, const void *vbuf, Size_t count) +{ + int fd = PerlIOSelf(f,PerlIOUnix)->fd; + while (1) + { + SSize_t len = write(fd,vbuf,count); + if (len >= 0 || errno != EINTR) + return len; + } +} + +IV +PerlIOUnix_seek(PerlIO *f, Off_t offset, int whence) +{ + Off_t new = lseek(PerlIOSelf(f,PerlIOUnix)->fd,offset,whence); + return (new == (Off_t) -1) ? -1 : 0; +} + +Off_t +PerlIOUnix_tell(PerlIO *f) +{ + return lseek(PerlIOSelf(f,PerlIOUnix)->fd,0,SEEK_CUR); +} + +IV +PerlIOUnix_close(PerlIO *f) +{ + int fd = PerlIOSelf(f,PerlIOUnix)->fd; + int code = 0; + while (close(fd) != 0) + { + if (errno != EINTR) + { + code = -1; + break; + } + } + if (code == 0) + { + PerlIOBase(f)->flags &= ~PERLIO_F_OPEN; + } + return code; +} + +PerlIO_funcs PerlIO_unix = { + "unix", + sizeof(PerlIOUnix), + 0, + PerlIOUnix_fileno, + PerlIOUnix_fdopen, + PerlIOUnix_open, + PerlIOUnix_reopen, + PerlIOUnix_read, + PerlIOBase_unread, + PerlIOUnix_write, + PerlIOUnix_seek, + PerlIOUnix_tell, + PerlIOUnix_close, + PerlIOBase_sync, + 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) +{ + return fileno(PerlIOSelf(f,PerlIOStdio)->stdio); +} + + +PerlIO * +PerlIOStdio_fdopen(int fd,const char *mode) +{ + PerlIO *f = NULL; + int init = 0; + if (*mode == 'I') + { + init = 1; + mode++; + } + if (fd >= 0) + { + FILE *stdio = NULL; + if (init) + { + switch(fd) + { + case 0: + stdio = stdin; + break; + case 1: + stdio = stdout; + break; + case 2: + stdio = stderr; + break; + } + } + else + stdio = fdopen(fd,mode); + if (stdio) + { + PerlIOStdio *s = PerlIOSelf(PerlIO_push(f = PerlIO_allocate(),&PerlIO_stdio,mode),PerlIOStdio); + s->stdio = stdio; + } + } + return f; +} + +#undef PerlIO_importFILE +PerlIO * +PerlIO_importFILE(FILE *stdio, int fl) +{ + PerlIO *f = NULL; + if (stdio) + { + PerlIOStdio *s = PerlIOSelf(PerlIO_push(f = PerlIO_allocate(),&PerlIO_stdio,"r+"),PerlIOStdio); + s->stdio = stdio; + } + return f; +} + +PerlIO * +PerlIOStdio_open(const char *path,const char *mode) +{ + PerlIO *f = NULL; + FILE *stdio = fopen(path,mode); + if (stdio) + { + PerlIOStdio *s = PerlIOSelf(PerlIO_push(f = PerlIO_allocate(),&PerlIO_stdio,mode),PerlIOStdio); + s->stdio = stdio; + } + return f; +} + +int +PerlIOStdio_reopen(const char *path, const char *mode, PerlIO *f) +{ + PerlIOStdio *s = PerlIOSelf(f,PerlIOStdio); + FILE *stdio = freopen(path,mode,s->stdio); + if (!s->stdio) + return -1; + s->stdio = stdio; + return 0; +} + +SSize_t +PerlIOStdio_read(PerlIO *f, void *vbuf, Size_t count) +{ + 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 = fgetc(s); + if (ch != EOF) + { + *buf = ch; + got = 1; + } + } + else + got = fread(vbuf,1,count,s); + return got; +} + +SSize_t +PerlIOStdio_unread(PerlIO *f, const void *vbuf, Size_t count) +{ + FILE *s = PerlIOSelf(f,PerlIOStdio)->stdio; + STDCHAR *buf = ((STDCHAR *)vbuf)+count-1; + SSize_t unread = 0; + while (count > 0) + { + int ch = *buf-- & 0xff; + if (ungetc(ch,s) != ch) + break; + unread++; + count--; + } + return unread; +} + +SSize_t +PerlIOStdio_write(PerlIO *f, const void *vbuf, Size_t count) +{ + return fwrite(vbuf,1,count,PerlIOSelf(f,PerlIOStdio)->stdio); +} + +IV +PerlIOStdio_seek(PerlIO *f, Off_t offset, int whence) +{ + FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; + return fseek(stdio,offset,whence); +} + +Off_t +PerlIOStdio_tell(PerlIO *f) +{ + FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; + return ftell(stdio); +} + +IV +PerlIOStdio_close(PerlIO *f) +{ + return fclose(PerlIOSelf(f,PerlIOStdio)->stdio); +} + +IV +PerlIOStdio_flush(PerlIO *f) +{ + FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; + return fflush(stdio); +} + +IV +PerlIOStdio_eof(PerlIO *f) +{ + return feof(PerlIOSelf(f,PerlIOStdio)->stdio); +} + +IV +PerlIOStdio_error(PerlIO *f) +{ + return ferror(PerlIOSelf(f,PerlIOStdio)->stdio); +} + +void +PerlIOStdio_clearerr(PerlIO *f) +{ + clearerr(PerlIOSelf(f,PerlIOStdio)->stdio); +} + +void +PerlIOStdio_setlinebuf(PerlIO *f) +{ +#ifdef HAS_SETLINEBUF + setlinebuf(PerlIOSelf(f,PerlIOStdio)->stdio); #else - dTHX; - Perl_croak(aTHX_ "Cannot get 'ptr' of FILE * on this system"); - return NULL; + setvbuf(PerlIOSelf(f,PerlIOStdio)->stdio, Nullch, _IOLBF, 0); #endif } -#undef PerlIO_get_base +#ifdef FILE_base STDCHAR * -PerlIO_get_base(PerlIO *f) +PerlIOStdio_get_base(PerlIO *f) { -#ifdef FILE_base - return FILE_base(f); -#else - dTHX; - Perl_croak(aTHX_ "Cannot get 'base' of FILE * on this system"); - return NULL; + FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; + return FILE_base(stdio); +} + +Size_t +PerlIOStdio_get_bufsiz(PerlIO *f) +{ + FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; + return FILE_bufsiz(stdio); +} #endif + +#ifdef USE_STDIO_PTR +STDCHAR * +PerlIOStdio_get_ptr(PerlIO *f) +{ + FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; + return FILE_ptr(stdio); } -#undef PerlIO_has_base -int -PerlIO_has_base(PerlIO *f) +SSize_t +PerlIOStdio_get_cnt(PerlIO *f) +{ + FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; + return FILE_cnt(stdio); +} + +void +PerlIOStdio_set_ptrcnt(PerlIO *f,STDCHAR *ptr,SSize_t cnt) { + FILE *stdio = PerlIOSelf(f,PerlIOStdio)->stdio; + if (ptr != NULL) + { +#ifdef STDIO_PTR_LVALUE + FILE_ptr(stdio) = ptr; +#ifdef STDIO_PTR_LVAL_SETS_CNT + if (FILE_cnt(stdio) != (cnt)) + { + dTHX; + assert(FILE_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 */ + abort(); +#endif /* STDIO_PTR_LVALUE */ + } +/* Now (or only) set cnt */ +#ifdef STDIO_CNT_LVALUE + FILE_cnt(stdio) = cnt; +#else /* STDIO_CNT_LVALUE */ +#if (defined(STDIO_PTR_LVALUE) && defined(STDIO_PTR_LVAL_SETS_CNT)) + FILE_ptr(stdio) = FILE_ptr(stdio)+(FILE_cnt(stdio)-cnt); +#else /* STDIO_PTR_LVAL_SETS_CNT */ + abort(); +#endif /* STDIO_PTR_LVAL_SETS_CNT */ +#endif /* STDIO_CNT_LVALUE */ +} + +#endif + +PerlIO_funcs PerlIO_stdio = { + "stdio", + sizeof(PerlIOStdio), + 0, + PerlIOStdio_fileno, + PerlIOStdio_fdopen, + PerlIOStdio_open, + PerlIOStdio_reopen, + PerlIOStdio_read, + PerlIOStdio_unread, + PerlIOStdio_write, + PerlIOStdio_seek, + PerlIOStdio_tell, + PerlIOStdio_close, + PerlIOStdio_flush, + PerlIOStdio_eof, + PerlIOStdio_error, + PerlIOStdio_clearerr, + PerlIOStdio_setlinebuf, #ifdef FILE_base - return 1; + PerlIOStdio_get_base, + PerlIOStdio_get_bufsiz, #else - return 0; + 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) +{ + PerlIO_flush(f); + /* Should really push stdio discipline when we have them */ + return fdopen(PerlIO_fileno(f),"r+"); +} + +#undef PerlIO_findFILE +FILE * +PerlIO_findFILE(PerlIO *f) +{ + return PerlIO_exportFILE(f,0); +} + +#undef PerlIO_releaseFILE +void +PerlIO_releaseFILE(PerlIO *p, FILE *f) +{ +} + +/*--------------------------------------------------------------------------------------*/ +/* perlio buffer layer */ + +typedef struct +{ + struct _PerlIO base; + Off_t posn; /* Offset of buf into the file */ + STDCHAR * buf; /* Start of buffer */ + STDCHAR * end; /* End of valid part of buffer */ + STDCHAR * ptr; /* Current position in buffer */ + Size_t bufsiz; /* Size of buffer */ + IV oneword; /* Emergency buffer */ +} PerlIOBuf; + + +PerlIO * +PerlIOBuf_fdopen(int fd, const char *mode) +{ + PerlIO_funcs *tab = PerlIO_default_btm(); + int init = 0; + PerlIO *f; + if (*mode == 'I') + { + init = 1; + mode++; + } + f = (*tab->Fdopen)(fd,mode); + if (f) + { + /* Initial stderr is unbuffered */ + if (!init || fd != 2) + { + PerlIOBuf *b = PerlIOSelf(PerlIO_push(f,&PerlIO_perlio,NULL),PerlIOBuf); + b->posn = PerlIO_tell(PerlIONext(f)); + } + } + return f; +} + +PerlIO * +PerlIOBuf_open(const char *path, const char *mode) +{ + PerlIO_funcs *tab = PerlIO_default_btm(); + PerlIO *f = (*tab->Open)(path,mode); + if (f) + { + PerlIOBuf *b = PerlIOSelf(PerlIO_push(f,&PerlIO_perlio,NULL),PerlIOBuf); + b->posn = 0; + } + return f; } -#undef PerlIO_puts int -PerlIO_puts(PerlIO *f, const char *s) +PerlIOBase_reopen(const char *path, const char *mode, PerlIO *f) { - return fputs(s,f); + return (*PerlIOBase(f)->tab->Reopen)(path,mode,PerlIONext(f)); } -#undef PerlIO_open -PerlIO * -PerlIO_open(const char *path, const char *mode) +void +PerlIOBuf_alloc_buf(PerlIOBuf *b) { - return fopen(path,mode); + if (!b->bufsiz) + b->bufsiz = 4096; + New('B',b->buf,b->bufsiz,STDCHAR); + if (!b->buf) + { + b->buf = (STDCHAR *)&b->oneword; + b->bufsiz = sizeof(b->oneword); + } + b->ptr = b->buf; + b->end = b->ptr; } -#undef PerlIO_fdopen -PerlIO * -PerlIO_fdopen(int fd, const char *mode) +/* 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 *p = b->buf; + int count; + while (p < b->ptr) + { + count = PerlIO_write(PerlIONext(f),p,b->ptr - p); + if (count > 0) + { + p += count; + } + else if (count < 0) + { + PerlIOBase(f)->flags |= PERLIO_F_ERROR; + code = -1; + break; + } + } + b->posn += (p - b->buf); + } + else if (PerlIOBase(f)->flags & PERLIO_F_RDBUF) + { + /* Note position change */ + b->posn += (b->ptr - b->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); + if (PerlIO_flush(PerlIONext(f)) != 0) + code = -1; + return code; +} + +SSize_t +PerlIOBuf_read(PerlIO *f, void *vbuf, Size_t count) { - return fdopen(fd,mode); + PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); + STDCHAR *buf = (STDCHAR *) vbuf; + if (f) + { + Size_t got = 0; + if (!b->ptr) + PerlIOBuf_alloc_buf(b); + if (!(PerlIOBase(f)->flags & PERLIO_F_CANREAD)) + return 0; + while (count > 0) + { + SSize_t avail = (b->end - b->ptr); + if ((SSize_t) count < avail) + avail = count; + if (avail > 0) + { + Copy(b->ptr,buf,avail,char); + got += avail; + b->ptr += avail; + count -= avail; + buf += avail; + } + if (count && (b->ptr >= b->end)) + { + PerlIO_flush(f); + b->ptr = b->end = b->buf; + avail = PerlIO_read(PerlIONext(f),b->ptr,b->bufsiz); + if (avail <= 0) + { + if (avail == 0) + PerlIOBase(f)->flags |= PERLIO_F_EOF; + else + PerlIOBase(f)->flags |= PERLIO_F_ERROR; + break; + } + b->end = b->buf+avail; + PerlIOBase(f)->flags |= PERLIO_F_RDBUF; + } + } + return got; + } + return 0; } -#undef PerlIO_reopen -PerlIO * -PerlIO_reopen(const char *name, const char *mode, PerlIO *f) +SSize_t +PerlIOBuf_unread(PerlIO *f, const void *vbuf, Size_t count) { - return freopen(name,mode,f); + const STDCHAR *buf = (const STDCHAR *) vbuf+count; + PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); + SSize_t unread = 0; + SSize_t avail; + if (!b->buf) + PerlIOBuf_alloc_buf(b); + if (PerlIOBase(f)->flags & PERLIO_F_WRBUF) + PerlIO_flush(f); + if (b->buf) + { + if (PerlIOBase(f)->flags & PERLIO_F_RDBUF) + { + avail = (b->ptr - b->buf); + if (avail > (SSize_t) count) + avail = count; + b->ptr -= avail; + } + else + { + avail = b->bufsiz; + if (avail > (SSize_t) count) + avail = count; + b->end = b->ptr + avail; + } + if (avail > 0) + { + buf -= avail; + if (buf != b->ptr) + { + Copy(buf,b->ptr,avail,char); + } + count -= avail; + unread += avail; + PerlIOBase(f)->flags &= ~ PERLIO_F_EOF; + } + } + return unread; } -#undef PerlIO_close -int -PerlIO_close(PerlIO *f) +SSize_t +PerlIOBuf_write(PerlIO *f, const void *vbuf, Size_t count) { - return fclose(f); + PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); + const STDCHAR *buf = (const STDCHAR *) vbuf; + Size_t written = 0; + if (!b->buf) + PerlIOBuf_alloc_buf(b); + 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,char); + count -= avail; + buf += avail; + written += avail; + b->ptr += avail; + } + } + if (b->ptr >= (b->buf + b->bufsiz)) + PerlIO_flush(f); + } + return written; } -#undef PerlIO_eof -int -PerlIO_eof(PerlIO *f) +IV +PerlIOBuf_seek(PerlIO *f, Off_t offset, int whence) { - return feof(f); + PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); + int code; + code = PerlIO_flush(f); + if (code == 0) + { + PerlIOBase(f)->flags &= ~PERLIO_F_EOF; + code = PerlIO_seek(PerlIONext(f),offset,whence); + if (code == 0) + { + b->posn = PerlIO_tell(PerlIONext(f)); + } + } + return code; } -#undef PerlIO_getname -char * -PerlIO_getname(PerlIO *f, char *buf) +Off_t +PerlIOBuf_tell(PerlIO *f) { -#ifdef VMS - return fgetname(f,buf); -#else - dTHX; - Perl_croak(aTHX_ "Don't know how to get file name"); - return NULL; -#endif + PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); + Off_t posn = b->posn; + if (b->buf) + posn += (b->ptr - b->buf); + return posn; } -#undef PerlIO_getc -int -PerlIO_getc(PerlIO *f) +IV +PerlIOBuf_close(PerlIO *f) { - return fgetc(f); + IV code = PerlIOBase_close(f); + PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); + if (b->buf && b->buf != (STDCHAR *) &b->oneword) + { + Safefree(b->buf); + } + b->buf = NULL; + b->ptr = b->end = b->buf; + PerlIOBase(f)->flags &= ~(PERLIO_F_RDBUF|PERLIO_F_WRBUF); + return code; } -#undef PerlIO_error -int -PerlIO_error(PerlIO *f) +void +PerlIOBuf_setlinebuf(PerlIO *f) { - return ferror(f); + if (f) + { + PerlIOBase(f)->flags &= ~PERLIO_F_LINEBUF; + } } -#undef PerlIO_clearerr void -PerlIO_clearerr(PerlIO *f) +PerlIOBuf_set_cnt(PerlIO *f, int cnt) { - clearerr(f); + PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); + dTHX; + if (!b->buf) + PerlIOBuf_alloc_buf(b); + b->ptr = b->end - cnt; + assert(b->ptr >= b->buf); } -#undef PerlIO_flush -int -PerlIO_flush(PerlIO *f) +STDCHAR * +PerlIOBuf_get_ptr(PerlIO *f) { - return Fflush(f); + PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); + if (!b->buf) + PerlIOBuf_alloc_buf(b); + return b->ptr; } -#undef PerlIO_fileno -int -PerlIO_fileno(PerlIO *f) +SSize_t +PerlIOBuf_get_cnt(PerlIO *f) { - return fileno(f); + PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); + if (!b->buf) + PerlIOBuf_alloc_buf(b); + 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) + PerlIOBuf_alloc_buf(b); + return b->buf; +} + +Size_t +PerlIOBuf_bufsiz(PerlIO *f) +{ + PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); + if (!b->buf) + PerlIOBuf_alloc_buf(b); + return (b->end - b->buf); } -#undef PerlIO_setlinebuf void -PerlIO_setlinebuf(PerlIO *f) +PerlIOBuf_set_ptrcnt(PerlIO *f, STDCHAR *ptr, SSize_t cnt) { -#ifdef HAS_SETLINEBUF - setlinebuf(f); -#else -# ifdef __BORLANDC__ /* Borland doesn't like NULL size for _IOLBF */ - setvbuf(f, Nullch, _IOLBF, BUFSIZ); -# else - setvbuf(f, Nullch, _IOLBF, 0); -# endif -#endif + PerlIOBuf *b = PerlIOSelf(f,PerlIOBuf); + if (!b->buf) + PerlIOBuf_alloc_buf(b); + 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; } -#undef PerlIO_putc -int -PerlIO_putc(PerlIO *f, int ch) +PerlIO_funcs PerlIO_perlio = { + "perlio", + sizeof(PerlIOBuf), + 0, + PerlIOBase_fileno, + PerlIOBuf_fdopen, + PerlIOBuf_open, + PerlIOBase_reopen, + PerlIOBuf_read, + PerlIOBuf_unread, + PerlIOBuf_write, + PerlIOBuf_seek, + PerlIOBuf_tell, + PerlIOBuf_close, + PerlIOBuf_flush, + PerlIOBase_eof, + PerlIOBase_error, + PerlIOBase_clearerr, + PerlIOBuf_setlinebuf, + PerlIOBuf_get_base, + PerlIOBuf_bufsiz, + PerlIOBuf_get_ptr, + PerlIOBuf_get_cnt, + PerlIOBuf_set_ptrcnt, +}; + +void +PerlIO_init(void) { - return putc(ch,f); + if (!_perlio) + { + atexit(&PerlIO_cleanup); + } } -#undef PerlIO_ungetc -int -PerlIO_ungetc(PerlIO *f, int ch) +#undef PerlIO_stdin +PerlIO * +PerlIO_stdin(void) { - return ungetc(ch,f); + if (!_perlio) + PerlIO_stdstreams(); + return &_perlio[1]; } -#undef PerlIO_read -SSize_t -PerlIO_read(PerlIO *f, void *buf, Size_t count) +#undef PerlIO_stdout +PerlIO * +PerlIO_stdout(void) { - return fread(buf,1,count,f); + if (!_perlio) + PerlIO_stdstreams(); + return &_perlio[2]; } -#undef PerlIO_write -SSize_t -PerlIO_write(PerlIO *f, const void *buf, Size_t count) +#undef PerlIO_stderr +PerlIO * +PerlIO_stderr(void) { - return fwrite1(buf,1,count,f); + if (!_perlio) + PerlIO_stdstreams(); + return &_perlio[3]; } -#undef PerlIO_vprintf -int -PerlIO_vprintf(PerlIO *f, const char *fmt, va_list ap) +/*--------------------------------------------------------------------------------------*/ + +#undef PerlIO_getname +char * +PerlIO_getname(PerlIO *f, char *buf) { - return vfprintf(f,fmt,ap); + dTHX; + Perl_croak(aTHX_ "Don't know how to get file name"); + return NULL; } -#undef PerlIO_tell -Off_t -PerlIO_tell(PerlIO *f) + +/*--------------------------------------------------------------------------------------*/ +/* Functions which can be called on any kind of PerlIO implemented + in terms of above +*/ + +#undef PerlIO_getc +int +PerlIO_getc(PerlIO *f) { -#if defined(USE_64_BIT_STDIO) && defined(HAS_FTELLO) && !defined(USE_FTELL64) - return ftello(f); -#else - return ftell(f); -#endif + STDCHAR buf; + int count = PerlIO_read(f,&buf,1); + if (count == 1) + return (unsigned char) buf; + return -1; } -#undef PerlIO_seek +#undef PerlIO_putc int -PerlIO_seek(PerlIO *f, Off_t offset, int whence) +PerlIO_putc(PerlIO *f, int ch) { -#if defined(USE_64_BIT_STDIO) && defined(HAS_FSEEKO) && !defined(USE_FSEEK64) - return fseeko(f,offset,whence); -#else - return fseek(f,offset,whence); -#endif + 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) { - rewind(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; + sv_vcatpvf(sv, fmt, &ap); + s = SvPV(sv,len); + return PerlIO_write(f,s,len); } #undef PerlIO_printf -int +int PerlIO_printf(PerlIO *f,const char *fmt,...) { va_list ap; int result; va_start(ap,fmt); - result = vfprintf(f,fmt,ap); + result = PerlIO_vprintf(f,fmt,ap); va_end(ap); return result; } #undef PerlIO_stdoutf -int +int PerlIO_stdoutf(const char *fmt,...) { va_list ap; @@ -447,55 +1848,41 @@ PerlIO_stdoutf(const char *fmt,...) PerlIO * PerlIO_tmpfile(void) { - return tmpfile(); -} - -#undef PerlIO_importFILE -PerlIO * -PerlIO_importFILE(FILE *f, int fl) -{ - return f; -} - -#undef PerlIO_exportFILE -FILE * -PerlIO_exportFILE(PerlIO *f, int fl) -{ - return f; -} - -#undef PerlIO_findFILE -FILE * -PerlIO_findFILE(PerlIO *f) -{ + dTHX; + /* I have no idea how portable mkstemp() is ... */ + 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; + } + unlink(SvPVX(sv)); + SvREFCNT_dec(sv); + } return f; } -#undef PerlIO_releaseFILE -void -PerlIO_releaseFILE(PerlIO *p, FILE *f) -{ -} - -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 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, const Fpos_t *pos) { - return PerlIO_seek(f,*pos,0); + return PerlIO_seek(f,*pos,0); } #else #ifndef PERLIO_IS_STDIO @@ -554,7 +1941,7 @@ vfprintf(FILE *fd, char *pat, char *args) #endif #ifndef PerlIO_vsprintf -int +int PerlIO_vsprintf(char *s, int n, const char *fmt, va_list ap) { int val = vsprintf(s, fmt, ap); @@ -572,7 +1959,7 @@ PerlIO_vsprintf(char *s, int n, const char *fmt, va_list ap) #endif #ifndef PerlIO_sprintf -int +int PerlIO_sprintf(char *s, int n, const char *fmt,...) { va_list ap; diff --git a/pod/perlapi.pod b/pod/perlapi.pod index 634180f7ef..061f403445 100644 --- a/pod/perlapi.pod +++ b/pod/perlapi.pod @@ -3223,10 +3223,10 @@ C<retlen> will be set to the length, in bytes, of that character, and the pointer C<s> will be advanced to the end of the character. If C<s> does not point to a well-formed UTF8 character, the behaviour -is dependent on the value of C<checking>: if this is true, it is -assumed that the caller will raise a warning, and this function will -set C<retlen> to C<-1> and return. If C<checking> is not true, an optional UTF8 -warning is produced. +is dependent on the value of C<flags>: if it contains UTF8_CHECK_ONLY, +it is assumed that the caller will raise a warning, and this function +will set C<retlen> to C<-1> and return. The C<flags> can also contain +various flags to allow deviations from the strict UTF-8 encoding. U8* s utf8_to_uv(STRLEN curlen, I32 *retlen, U32 flags) @@ -126,6 +126,7 @@ ok; chomp($a = `$^X "-I../lib" "-MB::Stash" "-Mwarnings" -e1`); $a = join ',', sort split /,/, $a; +$a =~ s/-uperlio(?:::\w+)?,//g if $Config{'useperlio'} eq 'define'; $a =~ s/-uWin32,// if $^O eq 'MSWin32'; $a =~ s/-u(Cwd|File|File::Copy|OS2),//g if $^O eq 'os2'; $a =~ s/-uCwd,// if $^O eq 'cygwin'; |