diff options
author | Nicholas Clark <nick@ccl4.org> | 2009-09-28 14:58:16 +0100 |
---|---|---|
committer | Nicholas Clark <nick@ccl4.org> | 2009-09-29 11:12:37 +0100 |
commit | 725607636edc598ad6823e49789420d734f8aa28 (patch) | |
tree | 2e6a93cdd6c65e8412bf874507a63c8ac8f14a71 /dist/IO | |
parent | 8b2306352e674fdd7eb8b61ff2ce78864a87ed9c (diff) | |
download | perl-725607636edc598ad6823e49789420d734f8aa28.tar.gz |
Move IO from ext/ to dist/
Diffstat (limited to 'dist/IO')
36 files changed, 6100 insertions, 0 deletions
diff --git a/dist/IO/.gitignore b/dist/IO/.gitignore new file mode 100644 index 0000000000..577c726243 --- /dev/null +++ b/dist/IO/.gitignore @@ -0,0 +1 @@ +!/poll.c diff --git a/dist/IO/ChangeLog b/dist/IO/ChangeLog new file mode 100644 index 0000000000..6913c646e1 --- /dev/null +++ b/dist/IO/ChangeLog @@ -0,0 +1,364 @@ +IO 1.25 -- Wed May 13 18:37:33 CDT 2009 + * Fix test warnings in io_dir + * skip tests known to cause a segfault 5.10.0 + +IO 1.24 -- Mon May 11 14:15:51 CDT 2009 + + * Make Makefile.PL usable by core and CPAN + * Reorganize files to be under lib/ directory structure now matches core perl + * Update with following changes made to core perl distribution + * Silence Win32 compiler warning in IO.xs + * Make non-blocking mode work on Windows in IO::Socket::INET + * fix some missing parts of IO::Handle pod + * Implement IO::Handle::say the same way as the builtin say(). + * Undo io_linenum.t part of #34148. It was io_multihomed.t that I meant (my mistake), and that is now covered by #34155. + * watchdog() some IO tests + * Some more missing isGV_with_GP()s + * IO::Dir destructor + * IO::Socket::INET unnecessarily resolves "udp" + * IO::Handle->say should ignore $\ (bug #49266) + * consting IO.xs + * Net::SMTP can't send large messages with bleadperl + * Fix for IO::Socket send method + * Fixes for the test suite on OS/2 + * Silence VC++ compiler warnings + * IO::Socket's IO.xs fails to compile + * IO::Socket::connect returns wrong errno on timeout + * Coverity correctly reports that gv might (just) be NULL. So don't derefernece it if it is. + * Simplify tests for fork() capabilities + * Fix syntax error in io_pipe test + * Making IO::Socket pass test on Win32 + * ext/IO/t/io_unix.t + +IO 1.23 -- Sat Mar 25 19:28:28 CST 2006 + + * Adjust the regression tests to use t/test.pl when $ENV{PERL_CORE} is defined + * Reduce number of calls to getpeername + * Call qualify on format name passed to format_write. Bug reported by Johan Vromans + * Reduce calls to getprotobyname/number. Patch from Gisle Aas + * Remove references to file TEST used in core so appropriate tests are skipped + during an install from CPAN + * Add method say to IO::Handle + * Performance improvement for IO::File::open + * Don't warn about a directory being closed in the DESTROY + +IO 1.22 -- Mon Sep 5 10:29:35 CDT 2005 + + * Update with changes made in perl core distribution + +Change 173 on 1998/07/14 by <gbarr@pobox.com> (Graham Barr) + + IO::Socket + - Added method connected + + IO.xs + - Added check that file * is not null + + t/io_udp.t + - Added check for connected + - Made change to catch recv not returning the address, and added a fix to + ensure test does not hang + + t/io_sock.t + - Added check for connected. + +Change 137 on 1998/05/21 by <gbarr@pobox.com> (Graham Barr) + + IO::Socket::INET + - Added checks to all peer* and host* methods for undef + +Change 134 on 1998/05/09 by <gbarr@pobox.com> (Graham Barr) + + t/io_sock.t + - fix race condition on Solaris & SunOS + + IO::Handle + - Applied patch from Gisle Aas <gisle@aas.no> for + documentation update + - Applied patch from Kuma <tgy@chocobo.org> + changed input_line_number to be on a per-handle basis. + + IO::File + - Applied patch from Gisle Aas <gisle@aas.no> for + documentation update + + IO::Seekable + - Applied patch from Gisle Aas <gisle@aas.no> for + documentation update + added sysseek + + IO, IO::Socket::INET + - documentation update + + IO.xs + - Applied patch from Gisle Aas <gisle@aas.no> for + blocking + +Change 133 on 1998/05/09 by <gbarr@pobox.com> (Graham Barr) + + t/io_sock.t + - Added checks for blocking() + +Sun Apr 12 1998 <gbarr@pobox.com> (Graham Barr) + + IO.xs + - enclosed newCONSTSUB in #ifdef as _64 now defines it. + +Thu Mar 19 1998 <gbarr@pobox.com> (Graham Barr) + + All + - Changed copyright/distribution policy back to be the same as perl + +Sun Feb 15 1998 <gbarr@pobox.com> (Graham Barr) + + IO::Socket + - Fix to ->accept, accept() returns false on error not undef. + +*** Release 1.19 + +Thu Feb 5 1998 <gbarr@pobox.com> (Graham Barr) + + All + - change copyright notice + + IO::Socket::INET + - changed configure to accept PeerHost and LocalHost as well as the + PeerAddr and LocalAddr arguments. + +Mon Feb 2 1998 <gbarr@pobox.com> (Graham Barr) + + IO::Handle + - Added printflush so that flush.pl can be depreciated + + IO::Socket + - Remove C<use Config> statement as it was not needed + +Tue Jan 27 1998 <gbarr@pobox.com> (Graham Barr) + + IO::Socket::INET + - removed carp if $^W + +*** Patch 1.1804 + +Sat Jan 17 1998 <gbarr@pobox.com> (Graham Barr) + + t/io_sock.t + - Replaced C<Listen => 0> with C<LocalAddr => 'localhost'> + + IO/Socket/INET.pm + - Modified the MultiHomed code. Now each address for a given host has + a timeout of C<Timeout>. + - added _get_addr method for doing hostname lookups. Now Net::DNS can be + use by sub-classing IO::Socket::INET, Thanks Gisle Aas + + t/io_multihomed.t + - new test added. Thanks Gisle Aas. + +*** Patch 1.1803 + +Mon Nov 17 1997 <gbarr@pobox.com> (Graham Barr) + + poll.c + - Added #ifdef I_* tests + + IO::Socket + - Changed initialization of @domain2pkg to fix problem of Domain option + not working + - Added patch for multi-homed hosts, Thanks to Gisle Aas <gisle@aas.no> + + IO::Socket::INET + - Change default proto to getprotobyname instead of 'tcp' constant string + - Added patch for multi-homed hosts, Thanks to Gisle Aas <gisle@aas.no> + + t/io_sock.t + - Change to test fix for Domain problem fixed in IO::Socket and be + more comprehensive, Thanks to Gisle Aas <gisle@aas.no> + + t/io_unix.t + - New test, Thanks to Gisle Aas <gisle@aas.no> + +*** Patch 1.1802 + +Wed Nov 12 1997 <gbarr@pobox.com> (Graham Barr) + + t/io_poll.t + - test 4 made an assumption that was not portable, fixed. + +*** Patch 1.1801 + +Wed Oct 22 1997 <gbarr@pobox.com> (Graham Barr) + + IO.xs + - change #ifdef's to allow compilation with 5.002 + + IO::Socket + - Fix to ensure that socket is not returned as non-blocking + unless the user asks for it + + t/io_udp.t + - Fix to stop endless loop + +*** Release 1.18 + +Mon Oct 13 1997 <gbarr@pobox.com> (Graham Barr) + + IO.xs, IO::Handle + - 1.17 broke compatability with 5.003, small tweaks to restore + compatability + + t/io_const.t + - Added new test to ensure backwards compatability with constants + is not broken + +Wed Oct 8 1997 <gbarr@pobox.com> (Graham Barr) + + IO.xs + - Added #define's to cope with argument changes to start_subparse + from 5.003_22, _23 and _24 + + IO::Select + - Renamed has_error to be has_exception which is more correct, + has_error is a wrapper around has_exception with a warning if + $^W is set. + + Makefile.PL + - Remove 'linkext' option to WriteMakefile so that static linking + should work properly, cannot remember why I added it. + +Sun Oct 5 1997 <gbarr@pobox.com> (Graham Barr) + + IO::Pipe + - GLOB assignment does not copy the fileno while under -T + added checks for undefined fileno, and added fdopen + - reader and write can now be called as static methods + + Makefile.PL + - Attempt to locate <poll.h> and define I_POLL if found + +*** Release 1.17 + +Fri Sep 26 1997 <gbarr@pobox.com> (Graham Barr) + + IO.xs + - Fix bug in _poll for ANSI C compilers + + IO::Socket + - Split IO::Socket::INET and IO::Socket::UNIX into separate files + + IO::File + - Patch to open() for when file is in current directory. + +*** Release 1.16 + +Mon 15 Sep 1997 <gbarr@pobox.com> Graham Barr + + o New modules + - IO::Dir + - IO::Poll + + o IO::Socket + - Changed new to call autoflush on the new socket + - IO::Socket::INET->new now accepts a single argument + - IO::Socket::INET default to protocol 'tcp' + + o IO::File + - Added doc for new_tmpfile + + o IO::Handle + - Removed use of AutoLoader for constants, constants are + now defined as constant XS subs + - Added fsync, but will not be avaliable for use + unless HAS_FSYNC is defined, perls configure does not define + this yet. + - Moved bootstrap of IO.xs to IO.pm. IO::Handle no longer + contains an AUTOLOAD sub in it's ISA hier + + o IO::Seekable + - Remove clearerr, as it is defined in IO.xs + + o IO.xs + - Patched IO.xs with patch from Chip for setvbuf warning + - Added XS sub "constant" for backwards compatability + + o Misc + - Fixed IO::Socket::configure, it was not passing $arg to domain + specific package + - Changed all $fh variables in IO::Handle to $io and all $fh + variables in IO::Socket to $sock as Chip suggested + - Fixed usage messages to be consistant + +*** Release 1.15 + +Sun 19 Jan 1997 <bodg@tiuk.ti.com> Graham Barr + + o Updated PODs for IO::Handle and IO::File + o Modified IO.xs so that DESTROY gets called on IO::File + objects that were created with IO::File->new_tmpfile + o Modified the domain2pkg code in IO::Socket so that it + does not use blessd refs + o Created a new package IO::Pipe::End so that pipe specific + stuff can be moved out of IO::Handle. + o Added Ilya's OS/2 changes to Pipe.pm and io_pipe.t + + o These changes happened somtime before the release of 1.15 + - added shutdown to IO::Socket + - modified connect to not use alarm + - modified accept and connect to use IO::Select + +*** Release 1.14 + +Tue 24 Dec 1996 <bodg@tiuk.ti.com> Graham Barr + + o Updated to patches in perl core dist. + o Added C<use strict> to all modules + o Modified t/io_sock.t, hopefully the race condition has gone + o Added close statements to reader/writer in IO::Pipe + o IO::Handle::syswrite was calling sysread, fixed :-) + +*** Release 1.12 + +Thu 19 Sep 1996 <bodg@tiuk.ti.com> Graham Barr + + o Modified IO.xs so that it will compile with pre perlio version + of perl (ie pre perl5.003_02) + o Modified IO::Socket::send so not to pass 4 arguments to send + if the socket is connected + +*** Release 1.10 + +Mon 11 Sep 1996 <bodg@tiuk.ti.com> Graham Barr + + o Fixed a bug in IO::Socket which caused DESTROY to be called + on a partly initialised connection + o Changed IO.xs to use Perlio + o Modified usage message to report correct package + o Added IO::File::new changes from Chip, to allow PERM to be passed + o Added sysread and syswrite methods to IO::Handle + o Updated documentation + o Fixed a bug in IO::Select that caused a hang if the last handle + was removed. + o Added count method to IO::Select + o Renamed and modified tests so that they can be copied into the + perl distribution + o Added fcntl and ioctl methods to IO::Handle + +Thu 25 Jul 1996 <bodg@tiuk.ti.com> Graham Barr + + o It is now not necessary to call the domain sub-classes of + IO::Socket. when connect is called it notes the domain. + Domain specific methods, which are normally non-critical, are + called via this note-ing. + o Added methods to IO::Socket to retrieve the domain, type and + protocol of a given socket + +Tue 23 Jul 1996 <bodg@tiuk.ti.com> Graham Barr + + o IO::Socket::connect changed how we do timeouts, as it did not work + + o IO::Handle::new_from_fd removed method call to _ref_fd, which was + a leftover from FileHandle + +Fri 28 Jun 1996 <bodg@tiuk.ti.com> Graham Barr + + o Modified IO::Socket::UNIX::configure to default to using a socket + type of SOCK_STREAM if no type is specified. diff --git a/dist/IO/IO.pm b/dist/IO/IO.pm new file mode 100644 index 0000000000..a72e2243d7 --- /dev/null +++ b/dist/IO/IO.pm @@ -0,0 +1,68 @@ +# + +package IO; + +use XSLoader (); +use Carp; +use strict; +use warnings; + +our $VERSION = "1.25"; +XSLoader::load 'IO', $VERSION; + +sub import { + shift; + + warnings::warnif('deprecated', qq{Parameterless "use IO" deprecated}) + if @_ == 0 ; + + my @l = @_ ? @_ : qw(Handle Seekable File Pipe Socket Dir); + + eval join("", map { "require IO::" . (/(\w+)/)[0] . ";\n" } @l) + or croak $@; +} + +1; + +__END__ + +=head1 NAME + +IO - load various IO modules + +=head1 SYNOPSIS + + use IO qw(Handle File); # loads IO modules, here IO::Handle, IO::File + use IO; # DEPRECATED + +=head1 DESCRIPTION + +C<IO> provides a simple mechanism to load several of the IO modules +in one go. The IO modules belonging to the core are: + + IO::Handle + IO::Seekable + IO::File + IO::Pipe + IO::Socket + IO::Dir + IO::Select + IO::Poll + +Some other IO modules don't belong to the perl core but can be loaded +as well if they have been installed from CPAN. You can discover which +ones exist by searching for "^IO::" on http://search.cpan.org. + +For more information on any of these modules, please see its respective +documentation. + +=head1 DEPRECATED + + use IO; # loads all the modules listed below + +The loaded modules are IO::Handle, IO::Seekable, IO::File, IO::Pipe, +IO::Socket, IO::Dir. You should instead explicitly import the IO +modules you want. + +=cut + diff --git a/dist/IO/IO.xs b/dist/IO/IO.xs new file mode 100644 index 0000000000..d3dff557a2 --- /dev/null +++ b/dist/IO/IO.xs @@ -0,0 +1,541 @@ +/* + * Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved. + * This program is free software; you can redistribute it and/or + * modify it under the same terms as Perl itself. + */ + +#define PERL_EXT_IO + +#define PERL_NO_GET_CONTEXT +#include "EXTERN.h" +#define PERLIO_NOT_STDIO 1 +#include "perl.h" +#include "XSUB.h" +#include "poll.h" +#ifdef I_UNISTD +# include <unistd.h> +#endif +#if defined(I_FCNTL) || defined(HAS_FCNTL) +# include <fcntl.h> +#endif + +#ifndef SIOCATMARK +# ifdef I_SYS_SOCKIO +# include <sys/sockio.h> +# endif +#endif + +#ifdef PerlIO +#if defined(MACOS_TRADITIONAL) && defined(USE_SFIO) +#define PERLIO_IS_STDIO 1 +#undef setbuf +#undef setvbuf +#define setvbuf _stdsetvbuf +#define setbuf(f,b) ( __sf_setbuf(f,b) ) +#endif +typedef int SysRet; +typedef PerlIO * InputStream; +typedef PerlIO * OutputStream; +#else +#define PERLIO_IS_STDIO 1 +typedef int SysRet; +typedef FILE * InputStream; +typedef FILE * OutputStream; +#endif + +#define MY_start_subparse(fmt,flags) start_subparse(fmt,flags) + +#ifndef gv_stashpvn +#define gv_stashpvn(str,len,flags) gv_stashpv(str,flags) +#endif + +#ifndef __attribute__noreturn__ +# define __attribute__noreturn__ +#endif + +#ifndef NORETURN_FUNCTION_END +# define NORETURN_FUNCTION_END /* NOT REACHED */ return 0 +#endif + +static int not_here(const char *s) __attribute__noreturn__; +static int +not_here(const char *s) +{ + croak("%s not implemented on this architecture", s); + NORETURN_FUNCTION_END; +} + + +#ifndef PerlIO +#define PerlIO_fileno(f) fileno(f) +#endif + +static int +io_blocking(pTHX_ InputStream f, int block) +{ +#if defined(HAS_FCNTL) + int RETVAL; + if(!f) { + errno = EBADF; + return -1; + } + RETVAL = fcntl(PerlIO_fileno(f), F_GETFL, 0); + if (RETVAL >= 0) { + int mode = RETVAL; + int newmode = mode; +#ifdef O_NONBLOCK + /* POSIX style */ + +# ifndef O_NDELAY +# define O_NDELAY O_NONBLOCK +# endif + /* Note: UNICOS and UNICOS/mk a F_GETFL returns an O_NDELAY + * after a successful F_SETFL of an O_NONBLOCK. */ + RETVAL = RETVAL & (O_NONBLOCK | O_NDELAY) ? 0 : 1; + + if (block == 0) { + newmode &= ~O_NDELAY; + newmode |= O_NONBLOCK; + } else if (block > 0) { + newmode &= ~(O_NDELAY|O_NONBLOCK); + } +#else + /* Not POSIX - better have O_NDELAY or we can't cope. + * for BSD-ish machines this is an acceptable alternative + * for SysV we can't tell "would block" from EOF but that is + * the way SysV is... + */ + RETVAL = RETVAL & O_NDELAY ? 0 : 1; + + if (block == 0) { + newmode |= O_NDELAY; + } else if (block > 0) { + newmode &= ~O_NDELAY; + } +#endif + if (newmode != mode) { + const int ret = fcntl(PerlIO_fileno(f),F_SETFL,newmode); + if (ret < 0) + RETVAL = ret; + } + } + return RETVAL; +#else +# ifdef WIN32 + char flags = (char)block; + return ioctl(PerlIO_fileno(f), FIONBIO, &flags); +# else + return -1; +# endif +#endif +} + +MODULE = IO PACKAGE = IO::Seekable PREFIX = f + +void +fgetpos(handle) + InputStream handle + CODE: + if (handle) { +#ifdef PerlIO +#if PERL_VERSION < 8 + Fpos_t pos; + ST(0) = sv_newmortal(); + if (PerlIO_getpos(handle, &pos) != 0) { + ST(0) = &PL_sv_undef; + } + else { + sv_setpvn(ST(0), (char *)&pos, sizeof(Fpos_t)); + } +#else + ST(0) = sv_newmortal(); + if (PerlIO_getpos(handle, ST(0)) != 0) { + ST(0) = &PL_sv_undef; + } +#endif +#else + Fpos_t pos; + if (fgetpos(handle, &pos)) { + ST(0) = &PL_sv_undef; + } else { + ST(0) = sv_2mortal(newSVpvn((char*)&pos, sizeof(Fpos_t))); + } +#endif + } + else { + errno = EINVAL; + ST(0) = &PL_sv_undef; + } + +SysRet +fsetpos(handle, pos) + InputStream handle + SV * pos + CODE: + if (handle) { +#ifdef PerlIO +#if PERL_VERSION < 8 + char *p; + STRLEN len; + if (SvOK(pos) && (p = SvPV(pos,len)) && len == sizeof(Fpos_t)) { + RETVAL = PerlIO_setpos(handle, (Fpos_t*)p); + } + else { + RETVAL = -1; + errno = EINVAL; + } +#else + RETVAL = PerlIO_setpos(handle, pos); +#endif +#else + char *p; + STRLEN len; + if ((p = SvPV(pos,len)) && len == sizeof(Fpos_t)) { + RETVAL = fsetpos(handle, (Fpos_t*)p); + } + else { + RETVAL = -1; + errno = EINVAL; + } +#endif + } + else { + RETVAL = -1; + errno = EINVAL; + } + OUTPUT: + RETVAL + +MODULE = IO PACKAGE = IO::File PREFIX = f + +void +new_tmpfile(packname = "IO::File") + const char * packname + PREINIT: + OutputStream fp; + GV *gv; + CODE: +#ifdef PerlIO + fp = PerlIO_tmpfile(); +#else + fp = tmpfile(); +#endif + gv = (GV*)SvREFCNT_inc(newGVgen(packname)); + if (gv) + hv_delete(GvSTASH(gv), GvNAME(gv), GvNAMELEN(gv), G_DISCARD); + if (gv && do_open(gv, "+>&", 3, FALSE, 0, 0, fp)) { + ST(0) = sv_2mortal(newRV((SV*)gv)); + sv_bless(ST(0), gv_stashpv(packname, TRUE)); + SvREFCNT_dec(gv); /* undo increment in newRV() */ + } + else { + ST(0) = &PL_sv_undef; + SvREFCNT_dec(gv); + } + +MODULE = IO PACKAGE = IO::Poll + +void +_poll(timeout,...) + int timeout; +PPCODE: +{ +#ifdef HAS_POLL + const int nfd = (items - 1) / 2; + SV *tmpsv = NEWSV(999,nfd * sizeof(struct pollfd)); + struct pollfd *fds = (struct pollfd *)SvPVX(tmpsv); + int i,j,ret; + for(i=1, j=0 ; j < nfd ; j++) { + fds[j].fd = SvIV(ST(i)); + i++; + fds[j].events = (short)SvIV(ST(i)); + i++; + fds[j].revents = 0; + } + if((ret = poll(fds,nfd,timeout)) >= 0) { + for(i=1, j=0 ; j < nfd ; j++) { + sv_setiv(ST(i), fds[j].fd); i++; + sv_setiv(ST(i), fds[j].revents); i++; + } + } + SvREFCNT_dec(tmpsv); + XSRETURN_IV(ret); +#else + not_here("IO::Poll::poll"); +#endif +} + +MODULE = IO PACKAGE = IO::Handle PREFIX = io_ + +void +io_blocking(handle,blk=-1) + InputStream handle + int blk +PROTOTYPE: $;$ +CODE: +{ + const int ret = io_blocking(aTHX_ handle, items == 1 ? -1 : blk ? 1 : 0); + if(ret >= 0) + XSRETURN_IV(ret); + else + XSRETURN_UNDEF; +} + +MODULE = IO PACKAGE = IO::Handle PREFIX = f + +int +ungetc(handle, c) + InputStream handle + int c + CODE: + if (handle) +#ifdef PerlIO + RETVAL = PerlIO_ungetc(handle, c); +#else + RETVAL = ungetc(c, handle); +#endif + else { + RETVAL = -1; + errno = EINVAL; + } + OUTPUT: + RETVAL + +int +ferror(handle) + InputStream handle + CODE: + if (handle) +#ifdef PerlIO + RETVAL = PerlIO_error(handle); +#else + RETVAL = ferror(handle); +#endif + else { + RETVAL = -1; + errno = EINVAL; + } + OUTPUT: + RETVAL + +int +clearerr(handle) + InputStream handle + CODE: + if (handle) { +#ifdef PerlIO + PerlIO_clearerr(handle); +#else + clearerr(handle); +#endif + RETVAL = 0; + } + else { + RETVAL = -1; + errno = EINVAL; + } + OUTPUT: + RETVAL + +int +untaint(handle) + SV * handle + CODE: +#ifdef IOf_UNTAINT + IO * io; + io = sv_2io(handle); + if (io) { + IoFLAGS(io) |= IOf_UNTAINT; + RETVAL = 0; + } + else { +#endif + RETVAL = -1; + errno = EINVAL; +#ifdef IOf_UNTAINT + } +#endif + OUTPUT: + RETVAL + +SysRet +fflush(handle) + OutputStream handle + CODE: + if (handle) +#ifdef PerlIO + RETVAL = PerlIO_flush(handle); +#else + RETVAL = Fflush(handle); +#endif + else { + RETVAL = -1; + errno = EINVAL; + } + OUTPUT: + RETVAL + +void +setbuf(handle, ...) + OutputStream handle + CODE: + if (handle) +#ifdef PERLIO_IS_STDIO + { + char *buf = items == 2 && SvPOK(ST(1)) ? + sv_grow(ST(1), BUFSIZ) : 0; + setbuf(handle, buf); + } +#else + not_here("IO::Handle::setbuf"); +#endif + +SysRet +setvbuf(...) + CODE: + if (items != 4) + Perl_croak(aTHX_ "Usage: IO::Handle::setvbuf(handle, buf, type, size)"); +#if defined(PERLIO_IS_STDIO) && defined(_IOFBF) && defined(HAS_SETVBUF) + { + OutputStream handle = 0; + char * buf = SvPOK(ST(1)) ? sv_grow(ST(1), SvIV(ST(3))) : 0; + int type; + int size; + + if (items == 4) { + handle = IoOFP(sv_2io(ST(0))); + buf = SvPOK(ST(1)) ? sv_grow(ST(1), SvIV(ST(3))) : 0; + type = (int)SvIV(ST(2)); + size = (int)SvIV(ST(3)); + } + if (!handle) /* Try input stream. */ + handle = IoIFP(sv_2io(ST(0))); + if (items == 4 && handle) + RETVAL = setvbuf(handle, buf, type, size); + else { + RETVAL = -1; + errno = EINVAL; + } + } +#else + RETVAL = (SysRet) not_here("IO::Handle::setvbuf"); +#endif + OUTPUT: + RETVAL + + +SysRet +fsync(handle) + OutputStream handle + CODE: +#ifdef HAS_FSYNC + if(handle) + RETVAL = fsync(PerlIO_fileno(handle)); + else { + RETVAL = -1; + errno = EINVAL; + } +#else + RETVAL = (SysRet) not_here("IO::Handle::sync"); +#endif + OUTPUT: + RETVAL + + +MODULE = IO PACKAGE = IO::Socket + +SysRet +sockatmark (sock) + InputStream sock + PROTOTYPE: $ + PREINIT: + int fd; + CODE: + { + fd = PerlIO_fileno(sock); +#ifdef HAS_SOCKATMARK + RETVAL = sockatmark(fd); +#else + { + int flag = 0; +# ifdef SIOCATMARK +# if defined(NETWARE) || defined(WIN32) + if (ioctl(fd, SIOCATMARK, (void*)&flag) != 0) +# else + if (ioctl(fd, SIOCATMARK, &flag) != 0) +# endif + XSRETURN_UNDEF; +# else + not_here("IO::Socket::atmark"); +# endif + RETVAL = flag; + } +#endif + } + OUTPUT: + RETVAL + +BOOT: +{ + HV *stash; + /* + * constant subs for IO::Poll + */ + stash = gv_stashpvn("IO::Poll", 8, TRUE); +#ifdef POLLIN + newCONSTSUB(stash,"POLLIN",newSViv(POLLIN)); +#endif +#ifdef POLLPRI + newCONSTSUB(stash,"POLLPRI", newSViv(POLLPRI)); +#endif +#ifdef POLLOUT + newCONSTSUB(stash,"POLLOUT", newSViv(POLLOUT)); +#endif +#ifdef POLLRDNORM + newCONSTSUB(stash,"POLLRDNORM", newSViv(POLLRDNORM)); +#endif +#ifdef POLLWRNORM + newCONSTSUB(stash,"POLLWRNORM", newSViv(POLLWRNORM)); +#endif +#ifdef POLLRDBAND + newCONSTSUB(stash,"POLLRDBAND", newSViv(POLLRDBAND)); +#endif +#ifdef POLLWRBAND + newCONSTSUB(stash,"POLLWRBAND", newSViv(POLLWRBAND)); +#endif +#ifdef POLLNORM + newCONSTSUB(stash,"POLLNORM", newSViv(POLLNORM)); +#endif +#ifdef POLLERR + newCONSTSUB(stash,"POLLERR", newSViv(POLLERR)); +#endif +#ifdef POLLHUP + newCONSTSUB(stash,"POLLHUP", newSViv(POLLHUP)); +#endif +#ifdef POLLNVAL + newCONSTSUB(stash,"POLLNVAL", newSViv(POLLNVAL)); +#endif + /* + * constant subs for IO::Handle + */ + stash = gv_stashpvn("IO::Handle", 10, TRUE); +#ifdef _IOFBF + newCONSTSUB(stash,"_IOFBF", newSViv(_IOFBF)); +#endif +#ifdef _IOLBF + newCONSTSUB(stash,"_IOLBF", newSViv(_IOLBF)); +#endif +#ifdef _IONBF + newCONSTSUB(stash,"_IONBF", newSViv(_IONBF)); +#endif +#ifdef SEEK_SET + newCONSTSUB(stash,"SEEK_SET", newSViv(SEEK_SET)); +#endif +#ifdef SEEK_CUR + newCONSTSUB(stash,"SEEK_CUR", newSViv(SEEK_CUR)); +#endif +#ifdef SEEK_END + newCONSTSUB(stash,"SEEK_END", newSViv(SEEK_END)); +#endif +} + diff --git a/dist/IO/Makefile.PL b/dist/IO/Makefile.PL new file mode 100644 index 0000000000..2159f43e49 --- /dev/null +++ b/dist/IO/Makefile.PL @@ -0,0 +1,45 @@ +# This -*- perl -*- script makes the Makefile + +BEGIN { require 5.006_001 } +use ExtUtils::MakeMaker; +use Config qw(%Config); +my $PERL_CORE = grep { $_ eq 'PERL_CORE=1' } @ARGV; + +#--- Attempt to find <poll.h> + +my $define = ""; + +unless ($PERL_CORE or exists $Config{'i_poll'}) { + my @inc = split(/\s+/, join(" ", $Config{'usrinc'}, $Config{'incpth'}, $Config{'locincpth'})); + foreach $path (@inc) { + if (-f $path . "/poll.h") { + $define .= "-DI_POLL "; + last; + } + } +} + +if ($] < 5.008 and !$PERL_CORE) { + open(FH,">typemap"); + print FH "const char * T_PV\n"; + close(FH); +} + +#--- Write the Makefile + +WriteMakefile( + VERSION_FROM => "IO.pm", + NAME => "IO", + OBJECT => '$(O_FILES)', + ABSTRACT => 'Perl core IO modules', + AUTHOR => 'Graham Barr <gbarr@cpan.org>', + ( $PERL_CORE + ? () + : ( + INSTALLDIRS => 'perl', + clean => {FILES => 'typemap'}, + ) + ), + ($define ? (DEFINE => $define) : ()), + ((ExtUtils::MakeMaker->VERSION() gt '6.30') ? ('LICENSE' => 'perl') : ()), +); diff --git a/dist/IO/README b/dist/IO/README new file mode 100644 index 0000000000..e4d9dfad55 --- /dev/null +++ b/dist/IO/README @@ -0,0 +1,27 @@ +This is the perl5 IO distribution. + +This distribution is included in the perl5 core distribution. You should +only need to install this distribution if it is newer than your perl +installation. + +To install this distribution you will need access rights to the perl +install ation on your system, as it overwrites your currently installed +version of IO. + +This distribution relies upon the Socket module (version 1.3), which is +avaliable from CPAN. Although you should not need to get this if your +version of perl is fairly recent, as Socket is also distributed in the +core perl distribution. + +If you do not have the required modules, you will see a warning when +the Makefile is built. + +To build, test and install this distribution type: + + perl Makefile.PL + make test + make install + +Share and Enjoy! +Graham Barr <gbarr@pobox.com> + diff --git a/dist/IO/hints/sco.pl b/dist/IO/hints/sco.pl new file mode 100644 index 0000000000..ddcf1551d1 --- /dev/null +++ b/dist/IO/hints/sco.pl @@ -0,0 +1,2 @@ +# SCO OSR5 needs to link with libc.so again to have C<fsync> defined +$self->{LIBS} = ['-lc']; diff --git a/dist/IO/lib/IO/Dir.pm b/dist/IO/lib/IO/Dir.pm new file mode 100644 index 0000000000..cce392c2ce --- /dev/null +++ b/dist/IO/lib/IO/Dir.pm @@ -0,0 +1,248 @@ +# IO::Dir.pm +# +# Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved. +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. + +package IO::Dir; + +use 5.006; + +use strict; +use Carp; +use Symbol; +use Exporter; +use IO::File; +our(@ISA, $VERSION, @EXPORT_OK); +use Tie::Hash; +use File::stat; +use File::Spec; + +@ISA = qw(Tie::Hash Exporter); +$VERSION = "1.07"; +$VERSION = eval $VERSION; +@EXPORT_OK = qw(DIR_UNLINK); + +sub DIR_UNLINK () { 1 } + +sub new { + @_ >= 1 && @_ <= 2 or croak 'usage: new IO::Dir [DIRNAME]'; + my $class = shift; + my $dh = gensym; + if (@_) { + IO::Dir::open($dh, $_[0]) + or return undef; + } + bless $dh, $class; +} + +sub DESTROY { + my ($dh) = @_; + local($., $@, $!, $^E, $?); + no warnings 'io'; + closedir($dh); +} + +sub open { + @_ == 2 or croak 'usage: $dh->open(DIRNAME)'; + my ($dh, $dirname) = @_; + return undef + unless opendir($dh, $dirname); + # a dir name should always have a ":" in it; assume dirname is + # in current directory + $dirname = ':' . $dirname if ( ($^O eq 'MacOS') && ($dirname !~ /:/) ); + ${*$dh}{io_dir_path} = $dirname; + 1; +} + +sub close { + @_ == 1 or croak 'usage: $dh->close()'; + my ($dh) = @_; + closedir($dh); +} + +sub read { + @_ == 1 or croak 'usage: $dh->read()'; + my ($dh) = @_; + readdir($dh); +} + +sub seek { + @_ == 2 or croak 'usage: $dh->seek(POS)'; + my ($dh,$pos) = @_; + seekdir($dh,$pos); +} + +sub tell { + @_ == 1 or croak 'usage: $dh->tell()'; + my ($dh) = @_; + telldir($dh); +} + +sub rewind { + @_ == 1 or croak 'usage: $dh->rewind()'; + my ($dh) = @_; + rewinddir($dh); +} + +sub TIEHASH { + my($class,$dir,$options) = @_; + + my $dh = $class->new($dir) + or return undef; + + $options ||= 0; + + ${*$dh}{io_dir_unlink} = $options & DIR_UNLINK; + $dh; +} + +sub FIRSTKEY { + my($dh) = @_; + $dh->rewind; + scalar $dh->read; +} + +sub NEXTKEY { + my($dh) = @_; + scalar $dh->read; +} + +sub EXISTS { + my($dh,$key) = @_; + -e File::Spec->catfile(${*$dh}{io_dir_path}, $key); +} + +sub FETCH { + my($dh,$key) = @_; + &lstat(File::Spec->catfile(${*$dh}{io_dir_path}, $key)); +} + +sub STORE { + my($dh,$key,$data) = @_; + my($atime,$mtime) = ref($data) ? @$data : ($data,$data); + my $file = File::Spec->catfile(${*$dh}{io_dir_path}, $key); + unless(-e $file) { + my $io = IO::File->new($file,O_CREAT | O_RDWR); + $io->close if $io; + } + utime($atime,$mtime, $file); +} + +sub DELETE { + my($dh,$key) = @_; + + # Only unlink if unlink-ing is enabled + return 0 + unless ${*$dh}{io_dir_unlink}; + + my $file = File::Spec->catfile(${*$dh}{io_dir_path}, $key); + + -d $file + ? rmdir($file) + : unlink($file); +} + +1; + +__END__ + +=head1 NAME + +IO::Dir - supply object methods for directory handles + +=head1 SYNOPSIS + + use IO::Dir; + $d = IO::Dir->new("."); + if (defined $d) { + while (defined($_ = $d->read)) { something($_); } + $d->rewind; + while (defined($_ = $d->read)) { something_else($_); } + undef $d; + } + + tie %dir, 'IO::Dir', "."; + foreach (keys %dir) { + print $_, " " , $dir{$_}->size,"\n"; + } + +=head1 DESCRIPTION + +The C<IO::Dir> package provides two interfaces to perl's directory reading +routines. + +The first interface is an object approach. C<IO::Dir> provides an object +constructor and methods, which are just wrappers around perl's built in +directory reading routines. + +=over 4 + +=item new ( [ DIRNAME ] ) + +C<new> is the constructor for C<IO::Dir> objects. It accepts one optional +argument which, if given, C<new> will pass to C<open> + +=back + +The following methods are wrappers for the directory related functions built +into perl (the trailing `dir' has been removed from the names). See L<perlfunc> +for details of these functions. + +=over 4 + +=item open ( DIRNAME ) + +=item read () + +=item seek ( POS ) + +=item tell () + +=item rewind () + +=item close () + +=back + +C<IO::Dir> also provides an interface to reading directories via a tied +hash. The tied hash extends the interface beyond just the directory +reading routines by the use of C<lstat>, from the C<File::stat> package, +C<unlink>, C<rmdir> and C<utime>. + +=over 4 + +=item tie %hash, 'IO::Dir', DIRNAME [, OPTIONS ] + +=back + +The keys of the hash will be the names of the entries in the directory. +Reading a value from the hash will be the result of calling +C<File::stat::lstat>. Deleting an element from the hash will +delete the corresponding file or subdirectory, +provided that C<DIR_UNLINK> is included in the C<OPTIONS>. + +Assigning to an entry in the hash will cause the time stamps of the file +to be modified. If the file does not exist then it will be created. Assigning +a single integer to a hash element will cause both the access and +modification times to be changed to that value. Alternatively a reference to +an array of two values can be passed. The first array element will be used to +set the access time and the second element will be used to set the modification +time. + +=head1 SEE ALSO + +L<File::stat> + +=head1 AUTHOR + +Graham Barr. Currently maintained by the Perl Porters. Please report all +bugs to <perl5-porters@perl.org>. + +=head1 COPYRIGHT + +Copyright (c) 1997-2003 Graham Barr <gbarr@pobox.com>. All rights reserved. +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=cut diff --git a/dist/IO/lib/IO/File.pm b/dist/IO/lib/IO/File.pm new file mode 100644 index 0000000000..d33d090d0b --- /dev/null +++ b/dist/IO/lib/IO/File.pm @@ -0,0 +1,204 @@ +# + +package IO::File; + +=head1 NAME + +IO::File - supply object methods for filehandles + +=head1 SYNOPSIS + + use IO::File; + + $fh = new IO::File; + if ($fh->open("< file")) { + print <$fh>; + $fh->close; + } + + $fh = new IO::File "> file"; + if (defined $fh) { + print $fh "bar\n"; + $fh->close; + } + + $fh = new IO::File "file", "r"; + if (defined $fh) { + print <$fh>; + undef $fh; # automatically closes the file + } + + $fh = new IO::File "file", O_WRONLY|O_APPEND; + if (defined $fh) { + print $fh "corge\n"; + + $pos = $fh->getpos; + $fh->setpos($pos); + + undef $fh; # automatically closes the file + } + + autoflush STDOUT 1; + +=head1 DESCRIPTION + +C<IO::File> inherits from C<IO::Handle> and C<IO::Seekable>. It extends +these classes with methods that are specific to file handles. + +=head1 CONSTRUCTOR + +=over 4 + +=item new ( FILENAME [,MODE [,PERMS]] ) + +Creates an C<IO::File>. If it receives any parameters, they are passed to +the method C<open>; if the open fails, the object is destroyed. Otherwise, +it is returned to the caller. + +=item new_tmpfile + +Creates an C<IO::File> opened for read/write on a newly created temporary +file. On systems where this is possible, the temporary file is anonymous +(i.e. it is unlinked after creation, but held open). If the temporary +file cannot be created or opened, the C<IO::File> object is destroyed. +Otherwise, it is returned to the caller. + +=back + +=head1 METHODS + +=over 4 + +=item open( FILENAME [,MODE [,PERMS]] ) + +=item open( FILENAME, IOLAYERS ) + +C<open> accepts one, two or three parameters. With one parameter, +it is just a front end for the built-in C<open> function. With two or three +parameters, the first parameter is a filename that may include +whitespace or other special characters, and the second parameter is +the open mode, optionally followed by a file permission value. + +If C<IO::File::open> receives a Perl mode string ("E<gt>", "+E<lt>", etc.) +or an ANSI C fopen() mode string ("w", "r+", etc.), it uses the basic +Perl C<open> operator (but protects any special characters). + +If C<IO::File::open> is given a numeric mode, it passes that mode +and the optional permissions value to the Perl C<sysopen> operator. +The permissions default to 0666. + +If C<IO::File::open> is given a mode that includes the C<:> character, +it passes all the three arguments to the three-argument C<open> operator. + +For convenience, C<IO::File> exports the O_XXX constants from the +Fcntl module, if this module is available. + +=item binmode( [LAYER] ) + +C<binmode> sets C<binmode> on the underlying C<IO> object, as documented +in C<perldoc -f binmode>. + +C<binmode> accepts one optional parameter, which is the layer to be +passed on to the C<binmode> call. + +=back + +=head1 NOTE + +Some operating systems may perform C<IO::File::new()> or C<IO::File::open()> +on a directory without errors. This behavior is not portable and not +suggested for use. Using C<opendir()> and C<readdir()> or C<IO::Dir> are +suggested instead. + +=head1 SEE ALSO + +L<perlfunc>, +L<perlop/"I/O Operators">, +L<IO::Handle>, +L<IO::Seekable>, +L<IO::Dir> + +=head1 HISTORY + +Derived from FileHandle.pm by Graham Barr E<lt>F<gbarr@pobox.com>E<gt>. + +=cut + +use 5.006_001; +use strict; +our($VERSION, @EXPORT, @EXPORT_OK, @ISA); +use Carp; +use Symbol; +use SelectSaver; +use IO::Seekable; +use File::Spec; + +require Exporter; + +@ISA = qw(IO::Handle IO::Seekable Exporter); + +$VERSION = "1.14"; + +@EXPORT = @IO::Seekable::EXPORT; + +eval { + # Make all Fcntl O_XXX constants available for importing + require Fcntl; + my @O = grep /^O_/, @Fcntl::EXPORT; + Fcntl->import(@O); # first we import what we want to export + push(@EXPORT, @O); +}; + +################################################ +## Constructor +## + +sub new { + my $type = shift; + my $class = ref($type) || $type || "IO::File"; + @_ >= 0 && @_ <= 3 + or croak "usage: new $class [FILENAME [,MODE [,PERMS]]]"; + my $fh = $class->SUPER::new(); + if (@_) { + $fh->open(@_) + or return undef; + } + $fh; +} + +################################################ +## Open +## + +sub open { + @_ >= 2 && @_ <= 4 or croak 'usage: $fh->open(FILENAME [,MODE [,PERMS]])'; + my ($fh, $file) = @_; + if (@_ > 2) { + my ($mode, $perms) = @_[2, 3]; + if ($mode =~ /^\d+$/) { + defined $perms or $perms = 0666; + return sysopen($fh, $file, $mode, $perms); + } elsif ($mode =~ /:/) { + return open($fh, $mode, $file) if @_ == 3; + croak 'usage: $fh->open(FILENAME, IOLAYERS)'; + } else { + return open($fh, IO::Handle::_open_mode_string($mode), $file); + } + } + open($fh, $file); +} + +################################################ +## Binmode +## + +sub binmode { + ( @_ == 1 or @_ == 2 ) or croak 'usage $fh->binmode([LAYER])'; + + my($fh, $layer) = @_; + + return binmode $$fh unless $layer; + return binmode $$fh, $layer; +} + +1; diff --git a/dist/IO/lib/IO/Handle.pm b/dist/IO/lib/IO/Handle.pm new file mode 100644 index 0000000000..2f1f1b423b --- /dev/null +++ b/dist/IO/lib/IO/Handle.pm @@ -0,0 +1,635 @@ +package IO::Handle; + +=head1 NAME + +IO::Handle - supply object methods for I/O handles + +=head1 SYNOPSIS + + use IO::Handle; + + $io = new IO::Handle; + if ($io->fdopen(fileno(STDIN),"r")) { + print $io->getline; + $io->close; + } + + $io = new IO::Handle; + if ($io->fdopen(fileno(STDOUT),"w")) { + $io->print("Some text\n"); + } + + # setvbuf is not available by default on Perls 5.8.0 and later. + use IO::Handle '_IOLBF'; + $io->setvbuf($buffer_var, _IOLBF, 1024); + + undef $io; # automatically closes the file if it's open + + autoflush STDOUT 1; + +=head1 DESCRIPTION + +C<IO::Handle> is the base class for all other IO handle classes. It is +not intended that objects of C<IO::Handle> would be created directly, +but instead C<IO::Handle> is inherited from by several other classes +in the IO hierarchy. + +If you are reading this documentation, looking for a replacement for +the C<FileHandle> package, then I suggest you read the documentation +for C<IO::File> too. + +=head1 CONSTRUCTOR + +=over 4 + +=item new () + +Creates a new C<IO::Handle> object. + +=item new_from_fd ( FD, MODE ) + +Creates an C<IO::Handle> like C<new> does. +It requires two parameters, which are passed to the method C<fdopen>; +if the fdopen fails, the object is destroyed. Otherwise, it is returned +to the caller. + +=back + +=head1 METHODS + +See L<perlfunc> for complete descriptions of each of the following +supported C<IO::Handle> methods, which are just front ends for the +corresponding built-in functions: + + $io->close + $io->eof + $io->fcntl( FUNCTION, SCALAR ) + $io->fileno + $io->format_write( [FORMAT_NAME] ) + $io->getc + $io->ioctl( FUNCTION, SCALAR ) + $io->read ( BUF, LEN, [OFFSET] ) + $io->print ( ARGS ) + $io->printf ( FMT, [ARGS] ) + $io->say ( ARGS ) + $io->stat + $io->sysread ( BUF, LEN, [OFFSET] ) + $io->syswrite ( BUF, [LEN, [OFFSET]] ) + $io->truncate ( LEN ) + +See L<perlvar> for complete descriptions of each of the following +supported C<IO::Handle> methods. All of them return the previous +value of the attribute and takes an optional single argument that when +given will set the value. If no argument is given the previous value +is unchanged (except for $io->autoflush will actually turn ON +autoflush by default). + + $io->autoflush ( [BOOL] ) $| + $io->format_page_number( [NUM] ) $% + $io->format_lines_per_page( [NUM] ) $= + $io->format_lines_left( [NUM] ) $- + $io->format_name( [STR] ) $~ + $io->format_top_name( [STR] ) $^ + $io->input_line_number( [NUM]) $. + +The following methods are not supported on a per-filehandle basis. + + IO::Handle->format_line_break_characters( [STR] ) $: + IO::Handle->format_formfeed( [STR]) $^L + IO::Handle->output_field_separator( [STR] ) $, + IO::Handle->output_record_separator( [STR] ) $\ + + IO::Handle->input_record_separator( [STR] ) $/ + +Furthermore, for doing normal I/O you might need these: + +=over 4 + +=item $io->fdopen ( FD, MODE ) + +C<fdopen> is like an ordinary C<open> except that its first parameter +is not a filename but rather a file handle name, an IO::Handle object, +or a file descriptor number. (For the documentation of the C<open> +method, see L<IO::File>.) + +=item $io->opened + +Returns true if the object is currently a valid file descriptor, false +otherwise. + +=item $io->getline + +This works like <$io> described in L<perlop/"I/O Operators"> +except that it's more readable and can be safely called in a +list context but still returns just one line. If used as the conditional ++within a C<while> or C-style C<for> loop, however, you will need to ++emulate the functionality of <$io> with C<< defined($_ = $io->getline) >>. + +=item $io->getlines + +This works like <$io> when called in a list context to read all +the remaining lines in a file, except that it's more readable. +It will also croak() if accidentally called in a scalar context. + +=item $io->ungetc ( ORD ) + +Pushes a character with the given ordinal value back onto the given +handle's input stream. Only one character of pushback per handle is +guaranteed. + +=item $io->write ( BUF, LEN [, OFFSET ] ) + +This C<write> is like C<write> found in C, that is it is the +opposite of read. The wrapper for the perl C<write> function is +called C<format_write>. + +=item $io->error + +Returns a true value if the given handle has experienced any errors +since it was opened or since the last call to C<clearerr>, or if the +handle is invalid. It only returns false for a valid handle with no +outstanding errors. + +=item $io->clearerr + +Clear the given handle's error indicator. Returns -1 if the handle is +invalid, 0 otherwise. + +=item $io->sync + +C<sync> synchronizes a file's in-memory state with that on the +physical medium. C<sync> does not operate at the perlio api level, but +operates on the file descriptor (similar to sysread, sysseek and +systell). This means that any data held at the perlio api level will not +be synchronized. To synchronize data that is buffered at the perlio api +level you must use the flush method. C<sync> is not implemented on all +platforms. Returns "0 but true" on success, C<undef> on error, C<undef> +for an invalid handle. See L<fsync(3c)>. + +=item $io->flush + +C<flush> causes perl to flush any buffered data at the perlio api level. +Any unread data in the buffer will be discarded, and any unwritten data +will be written to the underlying file descriptor. Returns "0 but true" +on success, C<undef> on error. + +=item $io->printflush ( ARGS ) + +Turns on autoflush, print ARGS and then restores the autoflush status of the +C<IO::Handle> object. Returns the return value from print. + +=item $io->blocking ( [ BOOL ] ) + +If called with an argument C<blocking> will turn on non-blocking IO if +C<BOOL> is false, and turn it off if C<BOOL> is true. + +C<blocking> will return the value of the previous setting, or the +current setting if C<BOOL> is not given. + +If an error occurs C<blocking> will return undef and C<$!> will be set. + +=back + + +If the C functions setbuf() and/or setvbuf() are available, then +C<IO::Handle::setbuf> and C<IO::Handle::setvbuf> set the buffering +policy for an IO::Handle. The calling sequences for the Perl functions +are the same as their C counterparts--including the constants C<_IOFBF>, +C<_IOLBF>, and C<_IONBF> for setvbuf()--except that the buffer parameter +specifies a scalar variable to use as a buffer. You should only +change the buffer before any I/O, or immediately after calling flush. + +WARNING: The IO::Handle::setvbuf() is not available by default on +Perls 5.8.0 and later because setvbuf() is rather specific to using +the stdio library, while Perl prefers the new perlio subsystem instead. + +WARNING: A variable used as a buffer by C<setbuf> or C<setvbuf> B<must not +be modified> in any way until the IO::Handle is closed or C<setbuf> or +C<setvbuf> is called again, or memory corruption may result! Remember that +the order of global destruction is undefined, so even if your buffer +variable remains in scope until program termination, it may be undefined +before the file IO::Handle is closed. Note that you need to import the +constants C<_IOFBF>, C<_IOLBF>, and C<_IONBF> explicitly. Like C, setbuf +returns nothing. setvbuf returns "0 but true", on success, C<undef> on +failure. + +Lastly, there is a special method for working under B<-T> and setuid/gid +scripts: + +=over 4 + +=item $io->untaint + +Marks the object as taint-clean, and as such data read from it will also +be considered taint-clean. Note that this is a very trusting action to +take, and appropriate consideration for the data source and potential +vulnerability should be kept in mind. Returns 0 on success, -1 if setting +the taint-clean flag failed. (eg invalid handle) + +=back + +=head1 NOTE + +An C<IO::Handle> object is a reference to a symbol/GLOB reference (see +the C<Symbol> package). Some modules that +inherit from C<IO::Handle> may want to keep object related variables +in the hash table part of the GLOB. In an attempt to prevent modules +trampling on each other I propose the that any such module should prefix +its variables with its own name separated by _'s. For example the IO::Socket +module keeps a C<timeout> variable in 'io_socket_timeout'. + +=head1 SEE ALSO + +L<perlfunc>, +L<perlop/"I/O Operators">, +L<IO::File> + +=head1 BUGS + +Due to backwards compatibility, all filehandles resemble objects +of class C<IO::Handle>, or actually classes derived from that class. +They actually aren't. Which means you can't derive your own +class from C<IO::Handle> and inherit those methods. + +=head1 HISTORY + +Derived from FileHandle.pm by Graham Barr E<lt>F<gbarr@pobox.com>E<gt> + +=cut + +use 5.006_001; +use strict; +our($VERSION, @EXPORT_OK, @ISA); +use Carp; +use Symbol; +use SelectSaver; +use IO (); # Load the XS module + +require Exporter; +@ISA = qw(Exporter); + +$VERSION = "1.28"; +$VERSION = eval $VERSION; + +@EXPORT_OK = qw( + autoflush + output_field_separator + output_record_separator + input_record_separator + input_line_number + format_page_number + format_lines_per_page + format_lines_left + format_name + format_top_name + format_line_break_characters + format_formfeed + format_write + + print + printf + say + getline + getlines + + printflush + flush + + SEEK_SET + SEEK_CUR + SEEK_END + _IOFBF + _IOLBF + _IONBF +); + +################################################ +## Constructors, destructors. +## + +sub new { + my $class = ref($_[0]) || $_[0] || "IO::Handle"; + @_ == 1 or croak "usage: new $class"; + my $io = gensym; + bless $io, $class; +} + +sub new_from_fd { + my $class = ref($_[0]) || $_[0] || "IO::Handle"; + @_ == 3 or croak "usage: new_from_fd $class FD, MODE"; + my $io = gensym; + shift; + IO::Handle::fdopen($io, @_) + or return undef; + bless $io, $class; +} + +# +# There is no need for DESTROY to do anything, because when the +# last reference to an IO object is gone, Perl automatically +# closes its associated files (if any). However, to avoid any +# attempts to autoload DESTROY, we here define it to do nothing. +# +sub DESTROY {} + + +################################################ +## Open and close. +## + +sub _open_mode_string { + my ($mode) = @_; + $mode =~ /^\+?(<|>>?)$/ + or $mode =~ s/^r(\+?)$/$1</ + or $mode =~ s/^w(\+?)$/$1>/ + or $mode =~ s/^a(\+?)$/$1>>/ + or croak "IO::Handle: bad open mode: $mode"; + $mode; +} + +sub fdopen { + @_ == 3 or croak 'usage: $io->fdopen(FD, MODE)'; + my ($io, $fd, $mode) = @_; + local(*GLOB); + + if (ref($fd) && "".$fd =~ /GLOB\(/o) { + # It's a glob reference; Alias it as we cannot get name of anon GLOBs + my $n = qualify(*GLOB); + *GLOB = *{*$fd}; + $fd = $n; + } elsif ($fd =~ m#^\d+$#) { + # It's an FD number; prefix with "=". + $fd = "=$fd"; + } + + open($io, _open_mode_string($mode) . '&' . $fd) + ? $io : undef; +} + +sub close { + @_ == 1 or croak 'usage: $io->close()'; + my($io) = @_; + + close($io); +} + +################################################ +## Normal I/O functions. +## + +# flock +# select + +sub opened { + @_ == 1 or croak 'usage: $io->opened()'; + defined fileno($_[0]); +} + +sub fileno { + @_ == 1 or croak 'usage: $io->fileno()'; + fileno($_[0]); +} + +sub getc { + @_ == 1 or croak 'usage: $io->getc()'; + getc($_[0]); +} + +sub eof { + @_ == 1 or croak 'usage: $io->eof()'; + eof($_[0]); +} + +sub print { + @_ or croak 'usage: $io->print(ARGS)'; + my $this = shift; + print $this @_; +} + +sub printf { + @_ >= 2 or croak 'usage: $io->printf(FMT,[ARGS])'; + my $this = shift; + printf $this @_; +} + +sub say { + @_ or croak 'usage: $io->say(ARGS)'; + my $this = shift; + local $\ = "\n"; + print $this @_; +} + +sub getline { + @_ == 1 or croak 'usage: $io->getline()'; + my $this = shift; + return scalar <$this>; +} + +*gets = \&getline; # deprecated + +sub getlines { + @_ == 1 or croak 'usage: $io->getlines()'; + wantarray or + croak 'Can\'t call $io->getlines in a scalar context, use $io->getline'; + my $this = shift; + return <$this>; +} + +sub truncate { + @_ == 2 or croak 'usage: $io->truncate(LEN)'; + truncate($_[0], $_[1]); +} + +sub read { + @_ == 3 || @_ == 4 or croak 'usage: $io->read(BUF, LEN [, OFFSET])'; + read($_[0], $_[1], $_[2], $_[3] || 0); +} + +sub sysread { + @_ == 3 || @_ == 4 or croak 'usage: $io->sysread(BUF, LEN [, OFFSET])'; + sysread($_[0], $_[1], $_[2], $_[3] || 0); +} + +sub write { + @_ >= 2 && @_ <= 4 or croak 'usage: $io->write(BUF [, LEN [, OFFSET]])'; + local($\) = ""; + $_[2] = length($_[1]) unless defined $_[2]; + print { $_[0] } substr($_[1], $_[3] || 0, $_[2]); +} + +sub syswrite { + @_ >= 2 && @_ <= 4 or croak 'usage: $io->syswrite(BUF [, LEN [, OFFSET]])'; + if (defined($_[2])) { + syswrite($_[0], $_[1], $_[2], $_[3] || 0); + } else { + syswrite($_[0], $_[1]); + } +} + +sub stat { + @_ == 1 or croak 'usage: $io->stat()'; + stat($_[0]); +} + +################################################ +## State modification functions. +## + +sub autoflush { + my $old = new SelectSaver qualify($_[0], caller); + my $prev = $|; + $| = @_ > 1 ? $_[1] : 1; + $prev; +} + +sub output_field_separator { + carp "output_field_separator is not supported on a per-handle basis" + if ref($_[0]); + my $prev = $,; + $, = $_[1] if @_ > 1; + $prev; +} + +sub output_record_separator { + carp "output_record_separator is not supported on a per-handle basis" + if ref($_[0]); + my $prev = $\; + $\ = $_[1] if @_ > 1; + $prev; +} + +sub input_record_separator { + carp "input_record_separator is not supported on a per-handle basis" + if ref($_[0]); + my $prev = $/; + $/ = $_[1] if @_ > 1; + $prev; +} + +sub input_line_number { + local $.; + () = tell qualify($_[0], caller) if ref($_[0]); + my $prev = $.; + $. = $_[1] if @_ > 1; + $prev; +} + +sub format_page_number { + my $old; + $old = new SelectSaver qualify($_[0], caller) if ref($_[0]); + my $prev = $%; + $% = $_[1] if @_ > 1; + $prev; +} + +sub format_lines_per_page { + my $old; + $old = new SelectSaver qualify($_[0], caller) if ref($_[0]); + my $prev = $=; + $= = $_[1] if @_ > 1; + $prev; +} + +sub format_lines_left { + my $old; + $old = new SelectSaver qualify($_[0], caller) if ref($_[0]); + my $prev = $-; + $- = $_[1] if @_ > 1; + $prev; +} + +sub format_name { + my $old; + $old = new SelectSaver qualify($_[0], caller) if ref($_[0]); + my $prev = $~; + $~ = qualify($_[1], caller) if @_ > 1; + $prev; +} + +sub format_top_name { + my $old; + $old = new SelectSaver qualify($_[0], caller) if ref($_[0]); + my $prev = $^; + $^ = qualify($_[1], caller) if @_ > 1; + $prev; +} + +sub format_line_break_characters { + carp "format_line_break_characters is not supported on a per-handle basis" + if ref($_[0]); + my $prev = $:; + $: = $_[1] if @_ > 1; + $prev; +} + +sub format_formfeed { + carp "format_formfeed is not supported on a per-handle basis" + if ref($_[0]); + my $prev = $^L; + $^L = $_[1] if @_ > 1; + $prev; +} + +sub formline { + my $io = shift; + my $picture = shift; + local($^A) = $^A; + local($\) = ""; + formline($picture, @_); + print $io $^A; +} + +sub format_write { + @_ < 3 || croak 'usage: $io->write( [FORMAT_NAME] )'; + if (@_ == 2) { + my ($io, $fmt) = @_; + my $oldfmt = $io->format_name(qualify($fmt,caller)); + CORE::write($io); + $io->format_name($oldfmt); + } else { + CORE::write($_[0]); + } +} + +sub fcntl { + @_ == 3 || croak 'usage: $io->fcntl( OP, VALUE );'; + my ($io, $op) = @_; + return fcntl($io, $op, $_[2]); +} + +sub ioctl { + @_ == 3 || croak 'usage: $io->ioctl( OP, VALUE );'; + my ($io, $op) = @_; + return ioctl($io, $op, $_[2]); +} + +# this sub is for compatability with older releases of IO that used +# a sub called constant to detemine if a constant existed -- GMB +# +# The SEEK_* and _IO?BF constants were the only constants at that time +# any new code should just chech defined(&CONSTANT_NAME) + +sub constant { + no strict 'refs'; + my $name = shift; + (($name =~ /^(SEEK_(SET|CUR|END)|_IO[FLN]BF)$/) && defined &{$name}) + ? &{$name}() : undef; +} + + +# so that flush.pl can be deprecated + +sub printflush { + my $io = shift; + my $old; + $old = new SelectSaver qualify($io, caller) if ref($io); + local $| = 1; + if(ref($io)) { + print $io @_; + } + else { + print @_; + } +} + +1; diff --git a/dist/IO/lib/IO/Pipe.pm b/dist/IO/lib/IO/Pipe.pm new file mode 100644 index 0000000000..827cc48bfc --- /dev/null +++ b/dist/IO/lib/IO/Pipe.pm @@ -0,0 +1,257 @@ +# IO::Pipe.pm +# +# Copyright (c) 1996-8 Graham Barr <gbarr@pobox.com>. All rights reserved. +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. + +package IO::Pipe; + +use 5.006_001; + +use IO::Handle; +use strict; +our($VERSION); +use Carp; +use Symbol; + +$VERSION = "1.13"; + +sub new { + my $type = shift; + my $class = ref($type) || $type || "IO::Pipe"; + @_ == 0 || @_ == 2 or croak "usage: new $class [READFH, WRITEFH]"; + + my $me = bless gensym(), $class; + + my($readfh,$writefh) = @_ ? @_ : $me->handles; + + pipe($readfh, $writefh) + or return undef; + + @{*$me} = ($readfh, $writefh); + + $me; +} + +sub handles { + @_ == 1 or croak 'usage: $pipe->handles()'; + (IO::Pipe::End->new(), IO::Pipe::End->new()); +} + +my $do_spawn = $^O eq 'os2' || $^O eq 'MSWin32'; + +sub _doit { + my $me = shift; + my $rw = shift; + + my $pid = $do_spawn ? 0 : fork(); + + if($pid) { # Parent + return $pid; + } + elsif(defined $pid) { # Child or spawn + my $fh; + my $io = $rw ? \*STDIN : \*STDOUT; + my ($mode, $save) = $rw ? "r" : "w"; + if ($do_spawn) { + require Fcntl; + $save = IO::Handle->new_from_fd($io, $mode); + my $handle = shift; + # Close in child: + unless ($^O eq 'MSWin32') { + fcntl($handle, Fcntl::F_SETFD(), 1) or croak "fcntl: $!"; + } + $fh = $rw ? ${*$me}[0] : ${*$me}[1]; + } else { + shift; + $fh = $rw ? $me->reader() : $me->writer(); # close the other end + } + bless $io, "IO::Handle"; + $io->fdopen($fh, $mode); + $fh->close; + + if ($do_spawn) { + $pid = eval { system 1, @_ }; # 1 == P_NOWAIT + my $err = $!; + + $io->fdopen($save, $mode); + $save->close or croak "Cannot close $!"; + croak "IO::Pipe: Cannot spawn-NOWAIT: $err" if not $pid or $pid < 0; + return $pid; + } else { + exec @_ or + croak "IO::Pipe: Cannot exec: $!"; + } + } + else { + croak "IO::Pipe: Cannot fork: $!"; + } + + # NOT Reached +} + +sub reader { + @_ >= 1 or croak 'usage: $pipe->reader( [SUB_COMMAND_ARGS] )'; + my $me = shift; + + return undef + unless(ref($me) || ref($me = $me->new)); + + my $fh = ${*$me}[0]; + my $pid; + $pid = $me->_doit(0, $fh, @_) + if(@_); + + close ${*$me}[1]; + bless $me, ref($fh); + *$me = *$fh; # Alias self to handle + $me->fdopen($fh->fileno,"r") + unless defined($me->fileno); + bless $fh; # Really wan't un-bless here + ${*$me}{'io_pipe_pid'} = $pid + if defined $pid; + + $me; +} + +sub writer { + @_ >= 1 or croak 'usage: $pipe->writer( [SUB_COMMAND_ARGS] )'; + my $me = shift; + + return undef + unless(ref($me) || ref($me = $me->new)); + + my $fh = ${*$me}[1]; + my $pid; + $pid = $me->_doit(1, $fh, @_) + if(@_); + + close ${*$me}[0]; + bless $me, ref($fh); + *$me = *$fh; # Alias self to handle + $me->fdopen($fh->fileno,"w") + unless defined($me->fileno); + bless $fh; # Really wan't un-bless here + ${*$me}{'io_pipe_pid'} = $pid + if defined $pid; + + $me; +} + +package IO::Pipe::End; + +our(@ISA); + +@ISA = qw(IO::Handle); + +sub close { + my $fh = shift; + my $r = $fh->SUPER::close(@_); + + waitpid(${*$fh}{'io_pipe_pid'},0) + if(defined ${*$fh}{'io_pipe_pid'}); + + $r; +} + +1; + +__END__ + +=head1 NAME + +IO::Pipe - supply object methods for pipes + +=head1 SYNOPSIS + + use IO::Pipe; + + $pipe = new IO::Pipe; + + if($pid = fork()) { # Parent + $pipe->reader(); + + while(<$pipe>) { + ... + } + + } + elsif(defined $pid) { # Child + $pipe->writer(); + + print $pipe ... + } + + or + + $pipe = new IO::Pipe; + + $pipe->reader(qw(ls -l)); + + while(<$pipe>) { + ... + } + +=head1 DESCRIPTION + +C<IO::Pipe> provides an interface to creating pipes between +processes. + +=head1 CONSTRUCTOR + +=over 4 + +=item new ( [READER, WRITER] ) + +Creates an C<IO::Pipe>, which is a reference to a newly created symbol +(see the C<Symbol> package). C<IO::Pipe::new> optionally takes two +arguments, which should be objects blessed into C<IO::Handle>, or a +subclass thereof. These two objects will be used for the system call +to C<pipe>. If no arguments are given then method C<handles> is called +on the new C<IO::Pipe> object. + +These two handles are held in the array part of the GLOB until either +C<reader> or C<writer> is called. + +=back + +=head1 METHODS + +=over 4 + +=item reader ([ARGS]) + +The object is re-blessed into a sub-class of C<IO::Handle>, and becomes a +handle at the reading end of the pipe. If C<ARGS> are given then C<fork> +is called and C<ARGS> are passed to exec. + +=item writer ([ARGS]) + +The object is re-blessed into a sub-class of C<IO::Handle>, and becomes a +handle at the writing end of the pipe. If C<ARGS> are given then C<fork> +is called and C<ARGS> are passed to exec. + +=item handles () + +This method is called during construction by C<IO::Pipe::new> +on the newly created C<IO::Pipe> object. It returns an array of two objects +blessed into C<IO::Pipe::End>, or a subclass thereof. + +=back + +=head1 SEE ALSO + +L<IO::Handle> + +=head1 AUTHOR + +Graham Barr. Currently maintained by the Perl Porters. Please report all +bugs to <perl5-porters@perl.org>. + +=head1 COPYRIGHT + +Copyright (c) 1996-8 Graham Barr <gbarr@pobox.com>. All rights reserved. +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=cut diff --git a/dist/IO/lib/IO/Poll.pm b/dist/IO/lib/IO/Poll.pm new file mode 100644 index 0000000000..e7fb013506 --- /dev/null +++ b/dist/IO/lib/IO/Poll.pm @@ -0,0 +1,209 @@ + +# IO::Poll.pm +# +# Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved. +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. + +package IO::Poll; + +use strict; +use IO::Handle; +use Exporter (); +our(@ISA, @EXPORT_OK, @EXPORT, $VERSION); + +@ISA = qw(Exporter); +$VERSION = "0.07"; + +@EXPORT = qw( POLLIN + POLLOUT + POLLERR + POLLHUP + POLLNVAL + ); + +@EXPORT_OK = qw( + POLLPRI + POLLRDNORM + POLLWRNORM + POLLRDBAND + POLLWRBAND + POLLNORM + ); + +# [0] maps fd's to requested masks +# [1] maps fd's to returned masks +# [2] maps fd's to handles +sub new { + my $class = shift; + + my $self = bless [{},{},{}], $class; + + $self; +} + +sub mask { + my $self = shift; + my $io = shift; + my $fd = fileno($io); + return unless defined $fd; + if (@_) { + my $mask = shift; + if($mask) { + $self->[0]{$fd}{$io} = $mask; # the error events are always returned + $self->[1]{$fd} = 0; # output mask + $self->[2]{$io} = $io; # remember handle + } else { + delete $self->[0]{$fd}{$io}; + unless(%{$self->[0]{$fd}}) { + # We no longer have any handles for this FD + delete $self->[1]{$fd}; + delete $self->[0]{$fd}; + } + delete $self->[2]{$io}; + } + } + + return unless exists $self->[0]{$fd} and exists $self->[0]{$fd}{$io}; + return $self->[0]{$fd}{$io}; +} + + +sub poll { + my($self,$timeout) = @_; + + $self->[1] = {}; + + my($fd,$mask,$iom); + my @poll = (); + + while(($fd,$iom) = each %{$self->[0]}) { + $mask = 0; + $mask |= $_ for values(%$iom); + push(@poll,$fd => $mask); + } + + my $ret = @poll ? _poll(defined($timeout) ? $timeout * 1000 : -1,@poll) : 0; + + return $ret + unless $ret > 0; + + while(@poll) { + my($fd,$got) = splice(@poll,0,2); + $self->[1]{$fd} = $got if $got; + } + + return $ret; +} + +sub events { + my $self = shift; + my $io = shift; + my $fd = fileno($io); + exists $self->[1]{$fd} and exists $self->[0]{$fd}{$io} + ? $self->[1]{$fd} & ($self->[0]{$fd}{$io}|POLLHUP|POLLERR|POLLNVAL) + : 0; +} + +sub remove { + my $self = shift; + my $io = shift; + $self->mask($io,0); +} + +sub handles { + my $self = shift; + return values %{$self->[2]} unless @_; + + my $events = shift || 0; + my($fd,$ev,$io,$mask); + my @handles = (); + + while(($fd,$ev) = each %{$self->[1]}) { + while (($io,$mask) = each %{$self->[0]{$fd}}) { + $mask |= POLLHUP|POLLERR|POLLNVAL; # must allow these + push @handles,$self->[2]{$io} if ($ev & $mask) & $events; + } + } + return @handles; +} + +1; + +__END__ + +=head1 NAME + +IO::Poll - Object interface to system poll call + +=head1 SYNOPSIS + + use IO::Poll qw(POLLRDNORM POLLWRNORM POLLIN POLLHUP); + + $poll = new IO::Poll; + + $poll->mask($input_handle => POLLIN); + $poll->mask($output_handle => POLLOUT); + + $poll->poll($timeout); + + $ev = $poll->events($input); + +=head1 DESCRIPTION + +C<IO::Poll> is a simple interface to the system level poll routine. + +=head1 METHODS + +=over 4 + +=item mask ( IO [, EVENT_MASK ] ) + +If EVENT_MASK is given, then, if EVENT_MASK is non-zero, IO is added to the +list of file descriptors and the next call to poll will check for +any event specified in EVENT_MASK. If EVENT_MASK is zero then IO will be +removed from the list of file descriptors. + +If EVENT_MASK is not given then the return value will be the current +event mask value for IO. + +=item poll ( [ TIMEOUT ] ) + +Call the system level poll routine. If TIMEOUT is not specified then the +call will block. Returns the number of handles which had events +happen, or -1 on error. + +=item events ( IO ) + +Returns the event mask which represents the events that happened on IO +during the last call to C<poll>. + +=item remove ( IO ) + +Remove IO from the list of file descriptors for the next poll. + +=item handles( [ EVENT_MASK ] ) + +Returns a list of handles. If EVENT_MASK is not given then a list of all +handles known will be returned. If EVENT_MASK is given then a list +of handles will be returned which had one of the events specified by +EVENT_MASK happen during the last call ti C<poll> + +=back + +=head1 SEE ALSO + +L<poll(2)>, L<IO::Handle>, L<IO::Select> + +=head1 AUTHOR + +Graham Barr. Currently maintained by the Perl Porters. Please report all +bugs to <perl5-porters@perl.org>. + +=head1 COPYRIGHT + +Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved. +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=cut diff --git a/dist/IO/lib/IO/Seekable.pm b/dist/IO/lib/IO/Seekable.pm new file mode 100644 index 0000000000..db1effda28 --- /dev/null +++ b/dist/IO/lib/IO/Seekable.pm @@ -0,0 +1,128 @@ +# + +package IO::Seekable; + +=head1 NAME + +IO::Seekable - supply seek based methods for I/O objects + +=head1 SYNOPSIS + + use IO::Seekable; + package IO::Something; + @ISA = qw(IO::Seekable); + +=head1 DESCRIPTION + +C<IO::Seekable> does not have a constructor of its own as it is intended to +be inherited by other C<IO::Handle> based objects. It provides methods +which allow seeking of the file descriptors. + +=over 4 + +=item $io->getpos + +Returns an opaque value that represents the current position of the +IO::File, or C<undef> if this is not possible (eg an unseekable stream such +as a terminal, pipe or socket). If the fgetpos() function is available in +your C library it is used to implements getpos, else perl emulates getpos +using C's ftell() function. + +=item $io->setpos + +Uses the value of a previous getpos call to return to a previously visited +position. Returns "0 but true" on success, C<undef> on failure. + +=back + +See L<perlfunc> for complete descriptions of each of the following +supported C<IO::Seekable> methods, which are just front ends for the +corresponding built-in functions: + +=over 4 + +=item $io->seek ( POS, WHENCE ) + +Seek the IO::File to position POS, relative to WHENCE: + +=over 8 + +=item WHENCE=0 (SEEK_SET) + +POS is absolute position. (Seek relative to the start of the file) + +=item WHENCE=1 (SEEK_CUR) + +POS is an offset from the current position. (Seek relative to current) + +=item WHENCE=2 (SEEK_END) + +POS is an offset from the end of the file. (Seek relative to end) + +=back + +The SEEK_* constants can be imported from the C<Fcntl> module if you +don't wish to use the numbers C<0> C<1> or C<2> in your code. + +Returns C<1> upon success, C<0> otherwise. + +=item $io->sysseek( POS, WHENCE ) + +Similar to $io->seek, but sets the IO::File's position using the system +call lseek(2) directly, so will confuse most perl IO operators except +sysread and syswrite (see L<perlfunc> for full details) + +Returns the new position, or C<undef> on failure. A position +of zero is returned as the string C<"0 but true"> + +=item $io->tell + +Returns the IO::File's current position, or -1 on error. + +=back + +=head1 SEE ALSO + +L<perlfunc>, +L<perlop/"I/O Operators">, +L<IO::Handle> +L<IO::File> + +=head1 HISTORY + +Derived from FileHandle.pm by Graham Barr E<lt>gbarr@pobox.comE<gt> + +=cut + +use 5.006_001; +use Carp; +use strict; +our($VERSION, @EXPORT, @ISA); +use IO::Handle (); +# XXX we can't get these from IO::Handle or we'll get prototype +# mismatch warnings on C<use POSIX; use IO::File;> :-( +use Fcntl qw(SEEK_SET SEEK_CUR SEEK_END); +require Exporter; + +@EXPORT = qw(SEEK_SET SEEK_CUR SEEK_END); +@ISA = qw(Exporter); + +$VERSION = "1.10"; +$VERSION = eval $VERSION; + +sub seek { + @_ == 3 or croak 'usage: $io->seek(POS, WHENCE)'; + seek($_[0], $_[1], $_[2]); +} + +sub sysseek { + @_ == 3 or croak 'usage: $io->sysseek(POS, WHENCE)'; + sysseek($_[0], $_[1], $_[2]); +} + +sub tell { + @_ == 1 or croak 'usage: $io->tell()'; + tell($_[0]); +} + +1; diff --git a/dist/IO/lib/IO/Select.pm b/dist/IO/lib/IO/Select.pm new file mode 100644 index 0000000000..fc05fe70e9 --- /dev/null +++ b/dist/IO/lib/IO/Select.pm @@ -0,0 +1,381 @@ +# IO::Select.pm +# +# Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved. +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. + +package IO::Select; + +use strict; +use warnings::register; +use vars qw($VERSION @ISA); +require Exporter; + +$VERSION = "1.17"; + +@ISA = qw(Exporter); # This is only so we can do version checking + +sub VEC_BITS () {0} +sub FD_COUNT () {1} +sub FIRST_FD () {2} + +sub new +{ + my $self = shift; + my $type = ref($self) || $self; + + my $vec = bless [undef,0], $type; + + $vec->add(@_) + if @_; + + $vec; +} + +sub add +{ + shift->_update('add', @_); +} + + +sub remove +{ + shift->_update('remove', @_); +} + + +sub exists +{ + my $vec = shift; + my $fno = $vec->_fileno(shift); + return undef unless defined $fno; + $vec->[$fno + FIRST_FD]; +} + + +sub _fileno +{ + my($self, $f) = @_; + return unless defined $f; + $f = $f->[0] if ref($f) eq 'ARRAY'; + ($f =~ /^\d+$/) ? $f : fileno($f); +} + +sub _update +{ + my $vec = shift; + my $add = shift eq 'add'; + + my $bits = $vec->[VEC_BITS]; + $bits = '' unless defined $bits; + + my $count = 0; + my $f; + foreach $f (@_) + { + my $fn = $vec->_fileno($f); + next unless defined $fn; + my $i = $fn + FIRST_FD; + if ($add) { + if (defined $vec->[$i]) { + $vec->[$i] = $f; # if array rest might be different, so we update + next; + } + $vec->[FD_COUNT]++; + vec($bits, $fn, 1) = 1; + $vec->[$i] = $f; + } else { # remove + next unless defined $vec->[$i]; + $vec->[FD_COUNT]--; + vec($bits, $fn, 1) = 0; + $vec->[$i] = undef; + } + $count++; + } + $vec->[VEC_BITS] = $vec->[FD_COUNT] ? $bits : undef; + $count; +} + +sub can_read +{ + my $vec = shift; + my $timeout = shift; + my $r = $vec->[VEC_BITS]; + + defined($r) && (select($r,undef,undef,$timeout) > 0) + ? handles($vec, $r) + : (); +} + +sub can_write +{ + my $vec = shift; + my $timeout = shift; + my $w = $vec->[VEC_BITS]; + + defined($w) && (select(undef,$w,undef,$timeout) > 0) + ? handles($vec, $w) + : (); +} + +sub has_exception +{ + my $vec = shift; + my $timeout = shift; + my $e = $vec->[VEC_BITS]; + + defined($e) && (select(undef,undef,$e,$timeout) > 0) + ? handles($vec, $e) + : (); +} + +sub has_error +{ + warnings::warn("Call to deprecated method 'has_error', use 'has_exception'") + if warnings::enabled(); + goto &has_exception; +} + +sub count +{ + my $vec = shift; + $vec->[FD_COUNT]; +} + +sub bits +{ + my $vec = shift; + $vec->[VEC_BITS]; +} + +sub as_string # for debugging +{ + my $vec = shift; + my $str = ref($vec) . ": "; + my $bits = $vec->bits; + my $count = $vec->count; + $str .= defined($bits) ? unpack("b*", $bits) : "undef"; + $str .= " $count"; + my @handles = @$vec; + splice(@handles, 0, FIRST_FD); + for (@handles) { + $str .= " " . (defined($_) ? "$_" : "-"); + } + $str; +} + +sub _max +{ + my($a,$b,$c) = @_; + $a > $b + ? $a > $c + ? $a + : $c + : $b > $c + ? $b + : $c; +} + +sub select +{ + shift + if defined $_[0] && !ref($_[0]); + + my($r,$w,$e,$t) = @_; + my @result = (); + + my $rb = defined $r ? $r->[VEC_BITS] : undef; + my $wb = defined $w ? $w->[VEC_BITS] : undef; + my $eb = defined $e ? $e->[VEC_BITS] : undef; + + if(select($rb,$wb,$eb,$t) > 0) + { + my @r = (); + my @w = (); + my @e = (); + my $i = _max(defined $r ? scalar(@$r)-1 : 0, + defined $w ? scalar(@$w)-1 : 0, + defined $e ? scalar(@$e)-1 : 0); + + for( ; $i >= FIRST_FD ; $i--) + { + my $j = $i - FIRST_FD; + push(@r, $r->[$i]) + if defined $rb && defined $r->[$i] && vec($rb, $j, 1); + push(@w, $w->[$i]) + if defined $wb && defined $w->[$i] && vec($wb, $j, 1); + push(@e, $e->[$i]) + if defined $eb && defined $e->[$i] && vec($eb, $j, 1); + } + + @result = (\@r, \@w, \@e); + } + @result; +} + + +sub handles +{ + my $vec = shift; + my $bits = shift; + my @h = (); + my $i; + my $max = scalar(@$vec) - 1; + + for ($i = FIRST_FD; $i <= $max; $i++) + { + next unless defined $vec->[$i]; + push(@h, $vec->[$i]) + if !defined($bits) || vec($bits, $i - FIRST_FD, 1); + } + + @h; +} + +1; +__END__ + +=head1 NAME + +IO::Select - OO interface to the select system call + +=head1 SYNOPSIS + + use IO::Select; + + $s = IO::Select->new(); + + $s->add(\*STDIN); + $s->add($some_handle); + + @ready = $s->can_read($timeout); + + @ready = IO::Select->new(@handles)->can_read(0); + +=head1 DESCRIPTION + +The C<IO::Select> package implements an object approach to the system C<select> +function call. It allows the user to see what IO handles, see L<IO::Handle>, +are ready for reading, writing or have an exception pending. + +=head1 CONSTRUCTOR + +=over 4 + +=item new ( [ HANDLES ] ) + +The constructor creates a new object and optionally initialises it with a set +of handles. + +=back + +=head1 METHODS + +=over 4 + +=item add ( HANDLES ) + +Add the list of handles to the C<IO::Select> object. It is these values that +will be returned when an event occurs. C<IO::Select> keeps these values in a +cache which is indexed by the C<fileno> of the handle, so if more than one +handle with the same C<fileno> is specified then only the last one is cached. + +Each handle can be an C<IO::Handle> object, an integer or an array +reference where the first element is an C<IO::Handle> or an integer. + +=item remove ( HANDLES ) + +Remove all the given handles from the object. This method also works +by the C<fileno> of the handles. So the exact handles that were added +need not be passed, just handles that have an equivalent C<fileno> + +=item exists ( HANDLE ) + +Returns a true value (actually the handle itself) if it is present. +Returns undef otherwise. + +=item handles + +Return an array of all registered handles. + +=item can_read ( [ TIMEOUT ] ) + +Return an array of handles that are ready for reading. C<TIMEOUT> is +the maximum amount of time to wait before returning an empty list, in +seconds, possibly fractional. If C<TIMEOUT> is not given and any +handles are registered then the call will block. + +=item can_write ( [ TIMEOUT ] ) + +Same as C<can_read> except check for handles that can be written to. + +=item has_exception ( [ TIMEOUT ] ) + +Same as C<can_read> except check for handles that have an exception +condition, for example pending out-of-band data. + +=item count () + +Returns the number of handles that the object will check for when +one of the C<can_> methods is called or the object is passed to +the C<select> static method. + +=item bits() + +Return the bit string suitable as argument to the core select() call. + +=item select ( READ, WRITE, EXCEPTION [, TIMEOUT ] ) + +C<select> is a static method, that is you call it with the package name +like C<new>. C<READ>, C<WRITE> and C<EXCEPTION> are either C<undef> or +C<IO::Select> objects. C<TIMEOUT> is optional and has the same effect as +for the core select call. + +The result will be an array of 3 elements, each a reference to an array +which will hold the handles that are ready for reading, writing and have +exceptions respectively. Upon error an empty list is returned. + +=back + +=head1 EXAMPLE + +Here is a short example which shows how C<IO::Select> could be used +to write a server which communicates with several sockets while also +listening for more connections on a listen socket + + use IO::Select; + use IO::Socket; + + $lsn = new IO::Socket::INET(Listen => 1, LocalPort => 8080); + $sel = new IO::Select( $lsn ); + + while(@ready = $sel->can_read) { + foreach $fh (@ready) { + if($fh == $lsn) { + # Create a new socket + $new = $lsn->accept; + $sel->add($new); + } + else { + # Process socket + + # Maybe we have finished with the socket + $sel->remove($fh); + $fh->close; + } + } + } + +=head1 AUTHOR + +Graham Barr. Currently maintained by the Perl Porters. Please report all +bugs to <perl5-porters@perl.org>. + +=head1 COPYRIGHT + +Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved. +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=cut + diff --git a/dist/IO/lib/IO/Socket.pm b/dist/IO/lib/IO/Socket.pm new file mode 100644 index 0000000000..9938c7807a --- /dev/null +++ b/dist/IO/lib/IO/Socket.pm @@ -0,0 +1,530 @@ +# IO::Socket.pm +# +# Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved. +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. + +package IO::Socket; + +require 5.006; + +use IO::Handle; +use Socket 1.3; +use Carp; +use strict; +our(@ISA, $VERSION, @EXPORT_OK); +use Exporter; +use Errno; + +# legacy + +require IO::Socket::INET; +require IO::Socket::UNIX if ($^O ne 'epoc' && $^O ne 'symbian'); + +@ISA = qw(IO::Handle); + +$VERSION = "1.31"; + +@EXPORT_OK = qw(sockatmark); + +sub import { + my $pkg = shift; + if (@_ && $_[0] eq 'sockatmark') { # not very extensible but for now, fast + Exporter::export_to_level('IO::Socket', 1, $pkg, 'sockatmark'); + } else { + my $callpkg = caller; + Exporter::export 'Socket', $callpkg, @_; + } +} + +sub new { + my($class,%arg) = @_; + my $sock = $class->SUPER::new(); + + $sock->autoflush(1); + + ${*$sock}{'io_socket_timeout'} = delete $arg{Timeout}; + + return scalar(%arg) ? $sock->configure(\%arg) + : $sock; +} + +my @domain2pkg; + +sub register_domain { + my($p,$d) = @_; + $domain2pkg[$d] = $p; +} + +sub configure { + my($sock,$arg) = @_; + my $domain = delete $arg->{Domain}; + + croak 'IO::Socket: Cannot configure a generic socket' + unless defined $domain; + + croak "IO::Socket: Unsupported socket domain" + unless defined $domain2pkg[$domain]; + + croak "IO::Socket: Cannot configure socket in domain '$domain'" + unless ref($sock) eq "IO::Socket"; + + bless($sock, $domain2pkg[$domain]); + $sock->configure($arg); +} + +sub socket { + @_ == 4 or croak 'usage: $sock->socket(DOMAIN, TYPE, PROTOCOL)'; + my($sock,$domain,$type,$protocol) = @_; + + socket($sock,$domain,$type,$protocol) or + return undef; + + ${*$sock}{'io_socket_domain'} = $domain; + ${*$sock}{'io_socket_type'} = $type; + ${*$sock}{'io_socket_proto'} = $protocol; + + $sock; +} + +sub socketpair { + @_ == 4 || croak 'usage: IO::Socket->socketpair(DOMAIN, TYPE, PROTOCOL)'; + my($class,$domain,$type,$protocol) = @_; + my $sock1 = $class->new(); + my $sock2 = $class->new(); + + socketpair($sock1,$sock2,$domain,$type,$protocol) or + return (); + + ${*$sock1}{'io_socket_type'} = ${*$sock2}{'io_socket_type'} = $type; + ${*$sock1}{'io_socket_proto'} = ${*$sock2}{'io_socket_proto'} = $protocol; + + ($sock1,$sock2); +} + +sub connect { + @_ == 2 or croak 'usage: $sock->connect(NAME)'; + my $sock = shift; + my $addr = shift; + my $timeout = ${*$sock}{'io_socket_timeout'}; + my $err; + my $blocking; + + $blocking = $sock->blocking(0) if $timeout; + if (!connect($sock, $addr)) { + if (defined $timeout && ($!{EINPROGRESS} || $!{EWOULDBLOCK})) { + require IO::Select; + + my $sel = new IO::Select $sock; + + undef $!; + if (!$sel->can_write($timeout)) { + $err = $! || (exists &Errno::ETIMEDOUT ? &Errno::ETIMEDOUT : 1); + $@ = "connect: timeout"; + } + elsif (!connect($sock,$addr) && + not ($!{EISCONN} || ($! == 10022 && $^O eq 'MSWin32')) + ) { + # Some systems refuse to re-connect() to + # an already open socket and set errno to EISCONN. + # Windows sets errno to WSAEINVAL (10022) + $err = $!; + $@ = "connect: $!"; + } + } + elsif ($blocking || !($!{EINPROGRESS} || $!{EWOULDBLOCK})) { + $err = $!; + $@ = "connect: $!"; + } + } + + $sock->blocking(1) if $blocking; + + $! = $err if $err; + + $err ? undef : $sock; +} + +# Enable/disable blocking IO on sockets. +# Without args return the current status of blocking, +# with args change the mode as appropriate, returning the +# old setting, or in case of error during the mode change +# undef. + +sub blocking { + my $sock = shift; + + return $sock->SUPER::blocking(@_) + if $^O ne 'MSWin32'; + + # Windows handles blocking differently + # + # http://groups.google.co.uk/group/perl.perl5.porters/browse_thread/thread/b4e2b1d88280ddff/630b667a66e3509f?#630b667a66e3509f + # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/ioctlsocket_2.asp + # + # 0x8004667e is FIONBIO + # + # which is used to set blocking behaviour. + + # NOTE: + # This is a little confusing, the perl keyword for this is + # 'blocking' but the OS level behaviour is 'non-blocking', probably + # because sockets are blocking by default. + # Therefore internally we have to reverse the semantics. + + my $orig= !${*$sock}{io_sock_nonblocking}; + + return $orig unless @_; + + my $block = shift; + + if ( !$block != !$orig ) { + ${*$sock}{io_sock_nonblocking} = $block ? 0 : 1; + ioctl($sock, 0x8004667e, pack("L!",${*$sock}{io_sock_nonblocking})) + or return undef; + } + + return $orig; +} + + +sub close { + @_ == 1 or croak 'usage: $sock->close()'; + my $sock = shift; + ${*$sock}{'io_socket_peername'} = undef; + $sock->SUPER::close(); +} + +sub bind { + @_ == 2 or croak 'usage: $sock->bind(NAME)'; + my $sock = shift; + my $addr = shift; + + return bind($sock, $addr) ? $sock + : undef; +} + +sub listen { + @_ >= 1 && @_ <= 2 or croak 'usage: $sock->listen([QUEUE])'; + my($sock,$queue) = @_; + $queue = 5 + unless $queue && $queue > 0; + + return listen($sock, $queue) ? $sock + : undef; +} + +sub accept { + @_ == 1 || @_ == 2 or croak 'usage $sock->accept([PKG])'; + my $sock = shift; + my $pkg = shift || $sock; + my $timeout = ${*$sock}{'io_socket_timeout'}; + my $new = $pkg->new(Timeout => $timeout); + my $peer = undef; + + if(defined $timeout) { + require IO::Select; + + my $sel = new IO::Select $sock; + + unless ($sel->can_read($timeout)) { + $@ = 'accept: timeout'; + $! = (exists &Errno::ETIMEDOUT ? &Errno::ETIMEDOUT : 1); + return; + } + } + + $peer = accept($new,$sock) + or return; + + return wantarray ? ($new, $peer) + : $new; +} + +sub sockname { + @_ == 1 or croak 'usage: $sock->sockname()'; + getsockname($_[0]); +} + +sub peername { + @_ == 1 or croak 'usage: $sock->peername()'; + my($sock) = @_; + ${*$sock}{'io_socket_peername'} ||= getpeername($sock); +} + +sub connected { + @_ == 1 or croak 'usage: $sock->connected()'; + my($sock) = @_; + getpeername($sock); +} + +sub send { + @_ >= 2 && @_ <= 4 or croak 'usage: $sock->send(BUF, [FLAGS, [TO]])'; + my $sock = $_[0]; + my $flags = $_[2] || 0; + my $peer = $_[3] || $sock->peername; + + croak 'send: Cannot determine peer address' + unless(defined $peer); + + my $r = defined(getpeername($sock)) + ? send($sock, $_[1], $flags) + : send($sock, $_[1], $flags, $peer); + + # remember who we send to, if it was successful + ${*$sock}{'io_socket_peername'} = $peer + if(@_ == 4 && defined $r); + + $r; +} + +sub recv { + @_ == 3 || @_ == 4 or croak 'usage: $sock->recv(BUF, LEN [, FLAGS])'; + my $sock = $_[0]; + my $len = $_[2]; + my $flags = $_[3] || 0; + + # remember who we recv'd from + ${*$sock}{'io_socket_peername'} = recv($sock, $_[1]='', $len, $flags); +} + +sub shutdown { + @_ == 2 or croak 'usage: $sock->shutdown(HOW)'; + my($sock, $how) = @_; + ${*$sock}{'io_socket_peername'} = undef; + shutdown($sock, $how); +} + +sub setsockopt { + @_ == 4 or croak '$sock->setsockopt(LEVEL, OPTNAME, OPTVAL)'; + setsockopt($_[0],$_[1],$_[2],$_[3]); +} + +my $intsize = length(pack("i",0)); + +sub getsockopt { + @_ == 3 or croak '$sock->getsockopt(LEVEL, OPTNAME)'; + my $r = getsockopt($_[0],$_[1],$_[2]); + # Just a guess + $r = unpack("i", $r) + if(defined $r && length($r) == $intsize); + $r; +} + +sub sockopt { + my $sock = shift; + @_ == 1 ? $sock->getsockopt(SOL_SOCKET,@_) + : $sock->setsockopt(SOL_SOCKET,@_); +} + +sub atmark { + @_ == 1 or croak 'usage: $sock->atmark()'; + my($sock) = @_; + sockatmark($sock); +} + +sub timeout { + @_ == 1 || @_ == 2 or croak 'usage: $sock->timeout([VALUE])'; + my($sock,$val) = @_; + my $r = ${*$sock}{'io_socket_timeout'}; + + ${*$sock}{'io_socket_timeout'} = defined $val ? 0 + $val : $val + if(@_ == 2); + + $r; +} + +sub sockdomain { + @_ == 1 or croak 'usage: $sock->sockdomain()'; + my $sock = shift; + ${*$sock}{'io_socket_domain'}; +} + +sub socktype { + @_ == 1 or croak 'usage: $sock->socktype()'; + my $sock = shift; + ${*$sock}{'io_socket_type'} +} + +sub protocol { + @_ == 1 or croak 'usage: $sock->protocol()'; + my($sock) = @_; + ${*$sock}{'io_socket_proto'}; +} + +1; + +__END__ + +=head1 NAME + +IO::Socket - Object interface to socket communications + +=head1 SYNOPSIS + + use IO::Socket; + +=head1 DESCRIPTION + +C<IO::Socket> provides an object interface to creating and using sockets. It +is built upon the L<IO::Handle> interface and inherits all the methods defined +by L<IO::Handle>. + +C<IO::Socket> only defines methods for those operations which are common to all +types of socket. Operations which are specified to a socket in a particular +domain have methods defined in sub classes of C<IO::Socket> + +C<IO::Socket> will export all functions (and constants) defined by L<Socket>. + +=head1 CONSTRUCTOR + +=over 4 + +=item new ( [ARGS] ) + +Creates an C<IO::Socket>, which is a reference to a +newly created symbol (see the C<Symbol> package). C<new> +optionally takes arguments, these arguments are in key-value pairs. +C<new> only looks for one key C<Domain> which tells new which domain +the socket will be in. All other arguments will be passed to the +configuration method of the package for that domain, See below. + + NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE + +As of VERSION 1.18 all IO::Socket objects have autoflush turned on +by default. This was not the case with earlier releases. + + NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE + +=back + +=head1 METHODS + +See L<perlfunc> for complete descriptions of each of the following +supported C<IO::Socket> methods, which are just front ends for the +corresponding built-in functions: + + socket + socketpair + bind + listen + accept + send + recv + peername (getpeername) + sockname (getsockname) + shutdown + +Some methods take slightly different arguments to those defined in L<perlfunc> +in attempt to make the interface more flexible. These are + +=over 4 + +=item accept([PKG]) + +perform the system call C<accept> on the socket and return a new +object. The new object will be created in the same class as the listen +socket, unless C<PKG> is specified. This object can be used to +communicate with the client that was trying to connect. + +In a scalar context the new socket is returned, or undef upon +failure. In a list context a two-element array is returned containing +the new socket and the peer address; the list will be empty upon +failure. + +The timeout in the [PKG] can be specified as zero to effect a "poll", +but you shouldn't do that because a new IO::Select object will be +created behind the scenes just to do the single poll. This is +horrendously inefficient. Use rather true select() with a zero +timeout on the handle, or non-blocking IO. + +=item socketpair(DOMAIN, TYPE, PROTOCOL) + +Call C<socketpair> and return a list of two sockets created, or an +empty list on failure. + +=back + +Additional methods that are provided are: + +=over 4 + +=item atmark + +True if the socket is currently positioned at the urgent data mark, +false otherwise. + + use IO::Socket; + + my $sock = IO::Socket::INET->new('some_server'); + $sock->read($data, 1024) until $sock->atmark; + +Note: this is a reasonably new addition to the family of socket +functions, so all systems may not support this yet. If it is +unsupported by the system, an attempt to use this method will +abort the program. + +The atmark() functionality is also exportable as sockatmark() function: + + use IO::Socket 'sockatmark'; + +This allows for a more traditional use of sockatmark() as a procedural +socket function. If your system does not support sockatmark(), the +C<use> declaration will fail at compile time. + +=item connected + +If the socket is in a connected state the peer address is returned. +If the socket is not in a connected state then undef will be returned. + +=item protocol + +Returns the numerical number for the protocol being used on the socket, if +known. If the protocol is unknown, as with an AF_UNIX socket, zero +is returned. + +=item sockdomain + +Returns the numerical number for the socket domain type. For example, for +an AF_INET socket the value of &AF_INET will be returned. + +=item sockopt(OPT [, VAL]) + +Unified method to both set and get options in the SOL_SOCKET level. If called +with one argument then getsockopt is called, otherwise setsockopt is called. + +=item socktype + +Returns the numerical number for the socket type. For example, for +a SOCK_STREAM socket the value of &SOCK_STREAM will be returned. + +=item timeout([VAL]) + +Set or get the timeout value (in seconds) associated with this socket. +If called without any arguments then the current setting is returned. If +called with an argument the current setting is changed and the previous +value returned. + +=back + +=head1 SEE ALSO + +L<Socket>, L<IO::Handle>, L<IO::Socket::INET>, L<IO::Socket::UNIX> + +=head1 AUTHOR + +Graham Barr. atmark() by Lincoln Stein. Currently maintained by the +Perl Porters. Please report all bugs to <perl5-porters@perl.org>. + +=head1 COPYRIGHT + +Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved. +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +The atmark() implementation: Copyright 2001, Lincoln Stein <lstein@cshl.org>. +This module is distributed under the same terms as Perl itself. +Feel free to use, modify and redistribute it as long as you retain +the correct attribution. + +=cut diff --git a/dist/IO/lib/IO/Socket/INET.pm b/dist/IO/lib/IO/Socket/INET.pm new file mode 100644 index 0000000000..2f0e5d1d7a --- /dev/null +++ b/dist/IO/lib/IO/Socket/INET.pm @@ -0,0 +1,464 @@ +# IO::Socket::INET.pm +# +# Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved. +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. + +package IO::Socket::INET; + +use strict; +our(@ISA, $VERSION); +use IO::Socket; +use Socket; +use Carp; +use Exporter; +use Errno; + +@ISA = qw(IO::Socket); +$VERSION = "1.31"; + +my $EINVAL = exists(&Errno::EINVAL) ? Errno::EINVAL() : 1; + +IO::Socket::INET->register_domain( AF_INET ); + +my %socket_type = ( tcp => SOCK_STREAM, + udp => SOCK_DGRAM, + icmp => SOCK_RAW + ); +my %proto_number; +$proto_number{tcp} = Socket::IPPROTO_TCP() if defined &Socket::IPPROTO_TCP; +$proto_number{udp} = Socket::IPPROTO_UDP() if defined &Socket::IPPROTO_UDP; +$proto_number{icmp} = Socket::IPPROTO_ICMP() if defined &Socket::IPPROTO_ICMP; +my %proto_name = reverse %proto_number; + +sub new { + my $class = shift; + unshift(@_, "PeerAddr") if @_ == 1; + return $class->SUPER::new(@_); +} + +sub _cache_proto { + my @proto = @_; + for (map lc($_), $proto[0], split(' ', $proto[1])) { + $proto_number{$_} = $proto[2]; + } + $proto_name{$proto[2]} = $proto[0]; +} + +sub _get_proto_number { + my $name = lc(shift); + return undef unless defined $name; + return $proto_number{$name} if exists $proto_number{$name}; + + my @proto = getprotobyname($name); + return undef unless @proto; + _cache_proto(@proto); + + return $proto[2]; +} + +sub _get_proto_name { + my $num = shift; + return undef unless defined $num; + return $proto_name{$num} if exists $proto_name{$num}; + + my @proto = getprotobynumber($num); + return undef unless @proto; + _cache_proto(@proto); + + return $proto[0]; +} + +sub _sock_info { + my($addr,$port,$proto) = @_; + my $origport = $port; + my @serv = (); + + $port = $1 + if(defined $addr && $addr =~ s,:([\w\(\)/]+)$,,); + + if(defined $proto && $proto =~ /\D/) { + my $num = _get_proto_number($proto); + unless (defined $num) { + $@ = "Bad protocol '$proto'"; + return; + } + $proto = $num; + } + + if(defined $port) { + my $defport = ($port =~ s,\((\d+)\)$,,) ? $1 : undef; + my $pnum = ($port =~ m,^(\d+)$,)[0]; + + @serv = getservbyname($port, _get_proto_name($proto) || "") + if ($port =~ m,\D,); + + $port = $serv[2] || $defport || $pnum; + unless (defined $port) { + $@ = "Bad service '$origport'"; + return; + } + + $proto = _get_proto_number($serv[3]) if @serv && !$proto; + } + + return ($addr || undef, + $port || undef, + $proto || undef + ); +} + +sub _error { + my $sock = shift; + my $err = shift; + { + local($!); + my $title = ref($sock).": "; + $@ = join("", $_[0] =~ /^$title/ ? "" : $title, @_); + $sock->close() + if(defined fileno($sock)); + } + $! = $err; + return undef; +} + +sub _get_addr { + my($sock,$addr_str, $multi) = @_; + my @addr; + if ($multi && $addr_str !~ /^\d+(?:\.\d+){3}$/) { + (undef, undef, undef, undef, @addr) = gethostbyname($addr_str); + } else { + my $h = inet_aton($addr_str); + push(@addr, $h) if defined $h; + } + @addr; +} + +sub configure { + my($sock,$arg) = @_; + my($lport,$rport,$laddr,$raddr,$proto,$type); + + + $arg->{LocalAddr} = $arg->{LocalHost} + if exists $arg->{LocalHost} && !exists $arg->{LocalAddr}; + + ($laddr,$lport,$proto) = _sock_info($arg->{LocalAddr}, + $arg->{LocalPort}, + $arg->{Proto}) + or return _error($sock, $!, $@); + + $laddr = defined $laddr ? inet_aton($laddr) + : INADDR_ANY; + + return _error($sock, $EINVAL, "Bad hostname '",$arg->{LocalAddr},"'") + unless(defined $laddr); + + $arg->{PeerAddr} = $arg->{PeerHost} + if exists $arg->{PeerHost} && !exists $arg->{PeerAddr}; + + unless(exists $arg->{Listen}) { + ($raddr,$rport,$proto) = _sock_info($arg->{PeerAddr}, + $arg->{PeerPort}, + $proto) + or return _error($sock, $!, $@); + } + + $proto ||= _get_proto_number('tcp'); + + $type = $arg->{Type} || $socket_type{lc _get_proto_name($proto)}; + + my @raddr = (); + + if(defined $raddr) { + @raddr = $sock->_get_addr($raddr, $arg->{MultiHomed}); + return _error($sock, $EINVAL, "Bad hostname '",$arg->{PeerAddr},"'") + unless @raddr; + } + + while(1) { + + $sock->socket(AF_INET, $type, $proto) or + return _error($sock, $!, "$!"); + + if (defined $arg->{Blocking}) { + defined $sock->blocking($arg->{Blocking}) + or return _error($sock, $!, "$!"); + } + + if ($arg->{Reuse} || $arg->{ReuseAddr}) { + $sock->sockopt(SO_REUSEADDR,1) or + return _error($sock, $!, "$!"); + } + + if ($arg->{ReusePort}) { + $sock->sockopt(SO_REUSEPORT,1) or + return _error($sock, $!, "$!"); + } + + if ($arg->{Broadcast}) { + $sock->sockopt(SO_BROADCAST,1) or + return _error($sock, $!, "$!"); + } + + if($lport || ($laddr ne INADDR_ANY) || exists $arg->{Listen}) { + $sock->bind($lport || 0, $laddr) or + return _error($sock, $!, "$!"); + } + + if(exists $arg->{Listen}) { + $sock->listen($arg->{Listen} || 5) or + return _error($sock, $!, "$!"); + last; + } + + # don't try to connect unless we're given a PeerAddr + last unless exists($arg->{PeerAddr}); + + $raddr = shift @raddr; + + return _error($sock, $EINVAL, 'Cannot determine remote port') + unless($rport || $type == SOCK_DGRAM || $type == SOCK_RAW); + + last + unless($type == SOCK_STREAM || defined $raddr); + + return _error($sock, $EINVAL, "Bad hostname '",$arg->{PeerAddr},"'") + unless defined $raddr; + +# my $timeout = ${*$sock}{'io_socket_timeout'}; +# my $before = time() if $timeout; + + undef $@; + if ($sock->connect(pack_sockaddr_in($rport, $raddr))) { +# ${*$sock}{'io_socket_timeout'} = $timeout; + return $sock; + } + + return _error($sock, $!, $@ || "Timeout") + unless @raddr; + +# if ($timeout) { +# my $new_timeout = $timeout - (time() - $before); +# return _error($sock, +# (exists(&Errno::ETIMEDOUT) ? Errno::ETIMEDOUT() : $EINVAL), +# "Timeout") if $new_timeout <= 0; +# ${*$sock}{'io_socket_timeout'} = $new_timeout; +# } + + } + + $sock; +} + +sub connect { + @_ == 2 || @_ == 3 or + croak 'usage: $sock->connect(NAME) or $sock->connect(PORT, ADDR)'; + my $sock = shift; + return $sock->SUPER::connect(@_ == 1 ? shift : pack_sockaddr_in(@_)); +} + +sub bind { + @_ == 2 || @_ == 3 or + croak 'usage: $sock->bind(NAME) or $sock->bind(PORT, ADDR)'; + my $sock = shift; + return $sock->SUPER::bind(@_ == 1 ? shift : pack_sockaddr_in(@_)) +} + +sub sockaddr { + @_ == 1 or croak 'usage: $sock->sockaddr()'; + my($sock) = @_; + my $name = $sock->sockname; + $name ? (sockaddr_in($name))[1] : undef; +} + +sub sockport { + @_ == 1 or croak 'usage: $sock->sockport()'; + my($sock) = @_; + my $name = $sock->sockname; + $name ? (sockaddr_in($name))[0] : undef; +} + +sub sockhost { + @_ == 1 or croak 'usage: $sock->sockhost()'; + my($sock) = @_; + my $addr = $sock->sockaddr; + $addr ? inet_ntoa($addr) : undef; +} + +sub peeraddr { + @_ == 1 or croak 'usage: $sock->peeraddr()'; + my($sock) = @_; + my $name = $sock->peername; + $name ? (sockaddr_in($name))[1] : undef; +} + +sub peerport { + @_ == 1 or croak 'usage: $sock->peerport()'; + my($sock) = @_; + my $name = $sock->peername; + $name ? (sockaddr_in($name))[0] : undef; +} + +sub peerhost { + @_ == 1 or croak 'usage: $sock->peerhost()'; + my($sock) = @_; + my $addr = $sock->peeraddr; + $addr ? inet_ntoa($addr) : undef; +} + +1; + +__END__ + +=head1 NAME + +IO::Socket::INET - Object interface for AF_INET domain sockets + +=head1 SYNOPSIS + + use IO::Socket::INET; + +=head1 DESCRIPTION + +C<IO::Socket::INET> provides an object interface to creating and using sockets +in the AF_INET domain. It is built upon the L<IO::Socket> interface and +inherits all the methods defined by L<IO::Socket>. + +=head1 CONSTRUCTOR + +=over 4 + +=item new ( [ARGS] ) + +Creates an C<IO::Socket::INET> object, which is a reference to a +newly created symbol (see the C<Symbol> package). C<new> +optionally takes arguments, these arguments are in key-value pairs. + +In addition to the key-value pairs accepted by L<IO::Socket>, +C<IO::Socket::INET> provides. + + + PeerAddr Remote host address <hostname>[:<port>] + PeerHost Synonym for PeerAddr + PeerPort Remote port or service <service>[(<no>)] | <no> + LocalAddr Local host bind address hostname[:port] + LocalHost Synonym for LocalAddr + LocalPort Local host bind port <service>[(<no>)] | <no> + Proto Protocol name (or number) "tcp" | "udp" | ... + Type Socket type SOCK_STREAM | SOCK_DGRAM | ... + Listen Queue size for listen + ReuseAddr Set SO_REUSEADDR before binding + Reuse Set SO_REUSEADDR before binding (deprecated, prefer ReuseAddr) + ReusePort Set SO_REUSEPORT before binding + Broadcast Set SO_BROADCAST before binding + Timeout Timeout value for various operations + MultiHomed Try all addresses for multi-homed hosts + Blocking Determine if connection will be blocking mode + +If C<Listen> is defined then a listen socket is created, else if the +socket type, which is derived from the protocol, is SOCK_STREAM then +connect() is called. + +Although it is not illegal, the use of C<MultiHomed> on a socket +which is in non-blocking mode is of little use. This is because the +first connect will never fail with a timeout as the connect call +will not block. + +The C<PeerAddr> can be a hostname or the IP-address on the +"xx.xx.xx.xx" form. The C<PeerPort> can be a number or a symbolic +service name. The service name might be followed by a number in +parenthesis which is used if the service is not known by the system. +The C<PeerPort> specification can also be embedded in the C<PeerAddr> +by preceding it with a ":". + +If C<Proto> is not given and you specify a symbolic C<PeerPort> port, +then the constructor will try to derive C<Proto> from the service +name. As a last resort C<Proto> "tcp" is assumed. The C<Type> +parameter will be deduced from C<Proto> if not specified. + +If the constructor is only passed a single argument, it is assumed to +be a C<PeerAddr> specification. + +If C<Blocking> is set to 0, the connection will be in nonblocking mode. +If not specified it defaults to 1 (blocking mode). + +Examples: + + $sock = IO::Socket::INET->new(PeerAddr => 'www.perl.org', + PeerPort => 'http(80)', + Proto => 'tcp'); + + $sock = IO::Socket::INET->new(PeerAddr => 'localhost:smtp(25)'); + + $sock = IO::Socket::INET->new(Listen => 5, + LocalAddr => 'localhost', + LocalPort => 9000, + Proto => 'tcp'); + + $sock = IO::Socket::INET->new('127.0.0.1:25'); + + $sock = IO::Socket::INET->new(PeerPort => 9999, + PeerAddr => inet_ntoa(INADDR_BROADCAST), + Proto => udp, + LocalAddr => 'localhost', + Broadcast => 1 ) + or die "Can't bind : $@\n"; + + NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE + +As of VERSION 1.18 all IO::Socket objects have autoflush turned on +by default. This was not the case with earlier releases. + + NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE + +=back + +=head2 METHODS + +=over 4 + +=item sockaddr () + +Return the address part of the sockaddr structure for the socket + +=item sockport () + +Return the port number that the socket is using on the local host + +=item sockhost () + +Return the address part of the sockaddr structure for the socket in a +text form xx.xx.xx.xx + +=item peeraddr () + +Return the address part of the sockaddr structure for the socket on +the peer host + +=item peerport () + +Return the port number for the socket on the peer host. + +=item peerhost () + +Return the address part of the sockaddr structure for the socket on the +peer host in a text form xx.xx.xx.xx + +=back + +=head1 SEE ALSO + +L<Socket>, L<IO::Socket> + +=head1 AUTHOR + +Graham Barr. Currently maintained by the Perl Porters. Please report all +bugs to <perl5-porters@perl.org>. + +=head1 COPYRIGHT + +Copyright (c) 1996-8 Graham Barr <gbarr@pobox.com>. All rights reserved. +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=cut diff --git a/dist/IO/lib/IO/Socket/UNIX.pm b/dist/IO/lib/IO/Socket/UNIX.pm new file mode 100644 index 0000000000..baa092ba1f --- /dev/null +++ b/dist/IO/lib/IO/Socket/UNIX.pm @@ -0,0 +1,143 @@ +# IO::Socket::UNIX.pm +# +# Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved. +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. + +package IO::Socket::UNIX; + +use strict; +our(@ISA, $VERSION); +use IO::Socket; +use Carp; + +@ISA = qw(IO::Socket); +$VERSION = "1.23"; +$VERSION = eval $VERSION; + +IO::Socket::UNIX->register_domain( AF_UNIX ); + +sub new { + my $class = shift; + unshift(@_, "Peer") if @_ == 1; + return $class->SUPER::new(@_); +} + +sub configure { + my($sock,$arg) = @_; + my($bport,$cport); + + my $type = $arg->{Type} || SOCK_STREAM; + + $sock->socket(AF_UNIX, $type, 0) or + return undef; + + if(exists $arg->{Local}) { + my $addr = sockaddr_un($arg->{Local}); + $sock->bind($addr) or + return undef; + } + if(exists $arg->{Listen} && $type != SOCK_DGRAM) { + $sock->listen($arg->{Listen} || 5) or + return undef; + } + elsif(exists $arg->{Peer}) { + my $addr = sockaddr_un($arg->{Peer}); + $sock->connect($addr) or + return undef; + } + + $sock; +} + +sub hostpath { + @_ == 1 or croak 'usage: $sock->hostpath()'; + my $n = $_[0]->sockname || return undef; + (sockaddr_un($n))[0]; +} + +sub peerpath { + @_ == 1 or croak 'usage: $sock->peerpath()'; + my $n = $_[0]->peername || return undef; + (sockaddr_un($n))[0]; +} + +1; # Keep require happy + +__END__ + +=head1 NAME + +IO::Socket::UNIX - Object interface for AF_UNIX domain sockets + +=head1 SYNOPSIS + + use IO::Socket::UNIX; + +=head1 DESCRIPTION + +C<IO::Socket::UNIX> provides an object interface to creating and using sockets +in the AF_UNIX domain. It is built upon the L<IO::Socket> interface and +inherits all the methods defined by L<IO::Socket>. + +=head1 CONSTRUCTOR + +=over 4 + +=item new ( [ARGS] ) + +Creates an C<IO::Socket::UNIX> object, which is a reference to a +newly created symbol (see the C<Symbol> package). C<new> +optionally takes arguments, these arguments are in key-value pairs. + +In addition to the key-value pairs accepted by L<IO::Socket>, +C<IO::Socket::UNIX> provides. + + Type Type of socket (eg SOCK_STREAM or SOCK_DGRAM) + Local Path to local fifo + Peer Path to peer fifo + Listen Create a listen socket + +If the constructor is only passed a single argument, it is assumed to +be a C<Peer> specification. + + + NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE + +As of VERSION 1.18 all IO::Socket objects have autoflush turned on +by default. This was not the case with earlier releases. + + NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE + +=back + +=head1 METHODS + +=over 4 + +=item hostpath() + +Returns the pathname to the fifo at the local end + +=item peerpath() + +Returns the pathanme to the fifo at the peer end + +=back + +=head1 SEE ALSO + +L<Socket>, L<IO::Socket> + +=head1 AUTHOR + +Graham Barr. Currently maintained by the Perl Porters. Please report all +bugs to <perl5-porters@perl.org>. + +=head1 COPYRIGHT + +Copyright (c) 1996-8 Graham Barr <gbarr@pobox.com>. All rights reserved. +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=cut diff --git a/dist/IO/poll.c b/dist/IO/poll.c new file mode 100644 index 0000000000..9d39d57f2f --- /dev/null +++ b/dist/IO/poll.c @@ -0,0 +1,146 @@ +/* + * poll.c + * + * Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved. + * This program is free software; you can redistribute it and/or + * modify it under the same terms as Perl itself. + * + * For systems that do not have the poll() system call (for example Linux + * kernels < v2.1.23) try to emulate it as closely as possible using select() + * + */ + +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#include "poll.h" +#ifdef I_SYS_TIME +# include <sys/time.h> +#endif +#ifdef I_TIME +# include <time.h> +#endif +#include <sys/types.h> +#if defined(HAS_SOCKET) && !defined(VMS) && !defined(ultrix) /* VMS handles sockets via vmsish.h, ULTRIX dies of socket struct redefinitions */ +# include <sys/socket.h> +#endif +#include <sys/stat.h> +#include <errno.h> + +#ifdef HAS_SELECT +#ifdef I_SYS_SELECT +#include <sys/select.h> +#endif +#endif + +#ifdef EMULATE_POLL_WITH_SELECT + +# define POLL_CAN_READ (POLLIN | POLLRDNORM ) +# define POLL_CAN_WRITE (POLLOUT | POLLWRNORM | POLLWRBAND ) +# define POLL_HAS_EXCP (POLLRDBAND | POLLPRI ) + +# define POLL_EVENTS_MASK (POLL_CAN_READ | POLL_CAN_WRITE | POLL_HAS_EXCP) + +int +poll(struct pollfd *fds, unsigned long nfds, int timeout) +{ + int i,err; + fd_set rfd,wfd,efd,ifd; + struct timeval timebuf; + struct timeval *tbuf = (struct timeval *)0; + int n = 0; + int count; + + FD_ZERO(&ifd); + +again: + + FD_ZERO(&rfd); + FD_ZERO(&wfd); + FD_ZERO(&efd); + + for(i = 0 ; i < (int)nfds ; i++) { + int events = fds[i].events; + int fd = fds[i].fd; + + fds[i].revents = 0; + + if(fd < 0 || FD_ISSET(fd, &ifd)) + continue; + + if(fd > n) + n = fd; + + if(events & POLL_CAN_READ) + FD_SET(fd, &rfd); + + if(events & POLL_CAN_WRITE) + FD_SET(fd, &wfd); + + if(events & POLL_HAS_EXCP) + FD_SET(fd, &efd); + } + + if(timeout >= 0) { + timebuf.tv_sec = timeout / 1000; + timebuf.tv_usec = (timeout % 1000) * 1000; + tbuf = &timebuf; + } + + err = select(n+1,&rfd,&wfd,&efd,tbuf); + + if(err < 0) { +#ifdef HAS_FSTAT + if(errno == EBADF) { + for(i = 0 ; i < nfds ; i++) { + struct stat buf; + if((fstat(fds[i].fd,&buf) < 0) && (errno == EBADF)) { + FD_SET(fds[i].fd, &ifd); + goto again; + } + } + } +#endif /* HAS_FSTAT */ + return err; + } + + count = 0; + + for(i = 0 ; i < (int)nfds ; i++) { + int revents = (fds[i].events & POLL_EVENTS_MASK); + int fd = fds[i].fd; + + if(fd < 0) + continue; + + if(FD_ISSET(fd, &ifd)) + revents = POLLNVAL; + else { + if(!FD_ISSET(fd, &rfd)) + revents &= ~POLL_CAN_READ; + + if(!FD_ISSET(fd, &wfd)) + revents &= ~POLL_CAN_WRITE; + + if(!FD_ISSET(fd, &efd)) + revents &= ~POLL_HAS_EXCP; + } + + if((fds[i].revents = revents) != 0) + count++; + } + + return count; +} + +#endif /* EMULATE_POLL_WITH_SELECT */ + +/* gcc for SunOS 4 produces code from an empty (code/symbolwise) + * source code file that makes the SunOS 4.x /usr/bin/ld fail with + * ld: poll.o: premature EOF + * To avoid this, have at least something in here. */ +#if defined(__sun) && !defined(__SVR4) && defined(__GNUC__) +static int dummy; +#endif + diff --git a/dist/IO/poll.h b/dist/IO/poll.h new file mode 100644 index 0000000000..634bcddd15 --- /dev/null +++ b/dist/IO/poll.h @@ -0,0 +1,60 @@ +/* + * poll.h + * + * Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved. + * This program is free software; you can redistribute it and/or + * modify it under the same terms as Perl itself. + * + */ + +#ifndef POLL_H +# define POLL_H + +#if (defined(HAS_POLL) && defined(I_POLL)) || defined(POLLWRBAND) +# include <poll.h> +#else +#ifdef HAS_SELECT + + +/* We shall emulate poll using select */ + +#define EMULATE_POLL_WITH_SELECT + +#ifdef poll +# undef poll +#endif +#define poll Perl_my_poll + +typedef struct pollfd { + int fd; + short events; + short revents; +} pollfd_t; + +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLRDNORM 0x0040 +#define POLLWRNORM POLLOUT +#define POLLRDBAND 0x0080 +#define POLLWRBAND 0x0100 +#define POLLNORM POLLRDNORM + +/* Return ONLY events (NON testable) */ + +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 + +int poll (struct pollfd *, unsigned long, int); + +#ifndef HAS_POLL +# define HAS_POLL +#endif + +#endif /* HAS_SELECT */ + +#endif /* I_POLL */ + +#endif /* POLL_H */ + diff --git a/dist/IO/t/IO.t b/dist/IO/t/IO.t new file mode 100644 index 0000000000..effd414a4c --- /dev/null +++ b/dist/IO/t/IO.t @@ -0,0 +1,127 @@ +#!/usr/bin/perl -w + +BEGIN { + if ($ENV{PERL_CORE}) { + require Config; + if ($Config::Config{'extensions'} !~ /\bSocket\b/) { + print "1..0 # Skip: Socket not built - IO.pm uses Socket"; + exit 0; + } + } +} + +use strict; +use File::Path; +use File::Spec; +require($ENV{PERL_CORE} ? "../../t/test.pl" : "./t/test.pl"); +plan(tests => 18); + +{ + require XSLoader; + + my @load; + local $^W; + local *XSLoader::load = sub { + push @load, \@_; + }; + + # use_ok() calls import, which we do not want to do + require_ok( 'IO' ); + ok( @load, 'IO should call XSLoader::load()' ); + is( $load[0][0], 'IO', '... loading the IO library' ); + is( $load[0][1], $IO::VERSION, '... with the current .pm version' ); +} + +my @default = map { "IO/$_.pm" } qw( Handle Seekable File Pipe Socket Dir ); +delete @INC{ @default }; + +my $warn = '' ; +local $SIG{__WARN__} = sub { $warn = "@_" } ; + +{ + no warnings ; + IO->import(); + is( $warn, '', "... import default, should not warn"); + $warn = '' ; +} + +{ + local $^W = 0; + IO->import(); + is( $warn, '', "... import default, should not warn"); + $warn = '' ; +} + +{ + local $^W = 1; + IO->import(); + like( $warn, qr/^Parameterless "use IO" deprecated at/, + "... import default, should warn"); + $warn = '' ; +} + +{ + use warnings 'deprecated' ; + IO->import(); + like( $warn, qr/^Parameterless "use IO" deprecated at/, + "... import default, should warn"); + $warn = '' ; +} + +{ + use warnings ; + IO->import(); + like( $warn, qr/^Parameterless "use IO" deprecated at/, + "... import default, should warn"); + $warn = '' ; +} + +foreach my $default (@default) +{ + ok( exists $INC{ $default }, "... import should default load $default" ); +} + +eval { IO->import( 'nothere' ) }; +like( $@, qr/Can.t locate IO.nothere\.pm/, '... croaking on any error' ); + +my $fakedir = File::Spec->catdir( 'lib', 'IO' ); +my $fakemod = File::Spec->catfile( $fakedir, 'fakemod.pm' ); + +my $flag; +if ( -d $fakedir or mkpath( $fakedir )) +{ + if (open( OUT, ">$fakemod")) + { + (my $package = <<' END_HERE') =~ tr/\t//d; + package IO::fakemod; + + sub import { die "Do not import!\n" } + + sub exists { 1 } + + 1; + END_HERE + + print OUT $package; + } + + if (close OUT) + { + $flag = 1; + push @INC, 'lib'; + } +} + +SKIP: +{ + skip("Could not write to disk", 2 ) unless $flag; + eval { IO->import( 'fakemod' ) }; + ok( IO::fakemod::exists(), 'import() should import IO:: modules by name' ); + is( $@, '', '... and should not call import() on imported modules' ); +} + +END +{ + 1 while unlink $fakemod; + rmdir $fakedir; +} diff --git a/dist/IO/t/io_const.t b/dist/IO/t/io_const.t new file mode 100644 index 0000000000..f6f83c1956 --- /dev/null +++ b/dist/IO/t/io_const.t @@ -0,0 +1,25 @@ +use Config; + +BEGIN { + if($ENV{PERL_CORE}) { + if ($Config{'extensions'} !~ /\bIO\b/) { + print "1..0 # Skip: IO extension not compiled\n"; + exit 0; + } + } +} + +use IO::Handle; + +print "1..6\n"; +my $i = 1; +foreach (qw(SEEK_SET SEEK_CUR SEEK_END _IOFBF _IOLBF _IONBF)) { + my $d1 = defined(&{"IO::Handle::" . $_}) ? 1 : 0; + my $v1 = $d1 ? &{"IO::Handle::" . $_}() : undef; + my $v2 = IO::Handle::constant($_); + my $d2 = defined($v2); + + print "not " + if($d1 != $d2 || ($d1 && ($v1 != $v2))); + print "ok ",$i++,"\n"; +} diff --git a/dist/IO/t/io_dir.t b/dist/IO/t/io_dir.t new file mode 100644 index 0000000000..5472daa9b9 --- /dev/null +++ b/dist/IO/t/io_dir.t @@ -0,0 +1,73 @@ +#!./perl + +BEGIN { + if ($ENV{PERL_CORE}) { + require Config; import Config; + if ($] < 5.00326 || not $Config{'d_readdir'}) { + print "1..0 # Skip: readdir() not available\n"; + exit 0; + } + } + + require($ENV{PERL_CORE} ? "../../t/test.pl" : "./t/test.pl"); + plan(16); + + use_ok('IO::Dir'); + IO::Dir->import(DIR_UNLINK); +} + +use strict; + +my $DIR = $^O eq 'MacOS' ? ":" : "."; + +my $CLASS = "IO::Dir"; +my $dot = $CLASS->new($DIR); +ok(defined($dot)); + +my @a = sort <*>; +my $first; +do { $first = $dot->read } while defined($first) && $first =~ /^\./; +ok(+(grep { $_ eq $first } @a)); + +my @b = sort($first, (grep {/^[^.]/} $dot->read)); +ok(+(join("\0", @a) eq join("\0", @b))); + +ok($dot->rewind,'rewind'); +my @c = sort grep {/^[^.]/} $dot->read; +ok(+(join("\0", @b) eq join("\0", @c))); + +ok($dot->close,'close'); +{ local $^W; # avoid warnings on invalid dirhandle +ok(!$dot->rewind, "rewind on closed"); +ok(!defined($dot->read)); +} + +open(FH,'>X') || die "Can't create x"; +print FH "X"; +close(FH) or die "Can't close: $!"; + +my %dir; +tie %dir, $CLASS, $DIR; +my @files = keys %dir; + +# I hope we do not have an empty dir :-) +ok(scalar @files); + +my $stat = $dir{'X'}; +isa_ok($stat,'File::stat'); +ok(defined($stat) && $stat->size == 1); + +delete $dir{'X'}; + +ok(-f 'X'); + +my %dirx; +tie %dirx, $CLASS, $DIR, DIR_UNLINK; + +my $statx = $dirx{'X'}; +isa_ok($statx,'File::stat'); +ok(defined($statx) && $statx->size == 1); + +delete $dirx{'X'}; + +ok(!(-f 'X')); diff --git a/dist/IO/t/io_dup.t b/dist/IO/t/io_dup.t new file mode 100644 index 0000000000..6afc96a272 --- /dev/null +++ b/dist/IO/t/io_dup.t @@ -0,0 +1,57 @@ +#!./perl + +use Config; + +BEGIN { + if($ENV{PERL_CORE}) { + if ($Config{'extensions'} !~ /\bIO\b/) { + print "1..0 # Skip: IO extension not compiled\n"; + exit 0; + } + } +} + +use IO::Handle; +use IO::File; + +select(STDERR); $| = 1; +select(STDOUT); $| = 1; + +print "1..6\n"; + +print "ok 1\n"; + +$dupout = IO::Handle->new->fdopen( \*STDOUT ,"w"); +$duperr = IO::Handle->new->fdopen( \*STDERR ,"w"); + +$stdout = \*STDOUT; bless $stdout, "IO::File"; # "IO::Handle"; +$stderr = \*STDERR; bless $stderr, "IO::Handle"; + +$stdout->open( "Io.dup","w") || die "Can't open stdout"; +$stderr->fdopen($stdout,"w"); + +print $stdout "ok 2\n"; +print $stderr "ok 3\n"; + +# Since some systems don't have echo, we use Perl. +$echo = qq{$^X -le "print q(ok %d)"}; + +$cmd = sprintf $echo, 4; +print `$cmd`; + +$cmd = sprintf "$echo 1>&2", 5; +$cmd = sprintf $echo, 5 if $^O eq 'MacOS'; +print `$cmd`; + +$stderr->close; +$stdout->close; + +$stdout->fdopen($dupout,"w"); +$stderr->fdopen($duperr,"w"); + +if ($^O eq 'MSWin32' || $^O eq 'NetWare' || $^O eq 'VMS') { print `type Io.dup` } +elsif ($^O eq 'MacOS') { system 'Catenate Io.dup' } +else { system 'cat Io.dup' } +unlink 'Io.dup'; + +print STDOUT "ok 6\n"; diff --git a/dist/IO/t/io_file.t b/dist/IO/t/io_file.t new file mode 100644 index 0000000000..1cf60f5441 --- /dev/null +++ b/dist/IO/t/io_file.t @@ -0,0 +1,48 @@ +#!./perl -w + +use strict; +require($ENV{PERL_CORE} ? "../../t/test.pl" : "./t/test.pl"); +plan(tests => ($^O =~ /MSWin32/ ? 9 : 6)); + +my $Class = 'IO::File'; +my $All_Chars = join '', "\r\n", map( chr, 1..255 ), "zzz\n\r"; +my $File = 'bin.'.$$; +my $Expect = quotemeta $All_Chars; + +use_ok( $Class ); +can_ok( $Class, "binmode" ); + +### file the file with binary data; +### use standard open to make sure we can compare binmodes +### on both. +{ my $tmp; + open $tmp, ">$File" or die "Could not open '$File': $!"; + binmode $tmp; + print $tmp $All_Chars; + close $tmp; +} + +### now read in the file, once without binmode, once with. +### without binmode should fail at least on win32... +if( $^O =~ /MSWin32/ ) { + my $fh = $Class->new; + + isa_ok( $fh, $Class ); + ok( $fh->open($File), " Opened '$File'" ); + + my $cont = do { local $/; <$fh> }; + unlike( $cont, qr/$Expect/, " Content match fails without binmode" ); +} + +### now with binmode, it must pass +{ my $fh = $Class->new; + + isa_ok( $fh, $Class ); + ok( $fh->open($File), " Opened '$File' $!" ); + ok( $fh->binmode, " binmode enabled" ); + + my $cont = do { local $/; <$fh> }; + like( $cont, qr/$Expect/, " Content match passes with binmode" ); +} + +unlink $File; diff --git a/dist/IO/t/io_linenum.t b/dist/IO/t/io_linenum.t new file mode 100644 index 0000000000..259f73631a --- /dev/null +++ b/dist/IO/t/io_linenum.t @@ -0,0 +1,73 @@ +#!./perl + +# test added 29th April 1999 by Paul Johnson (pjcj@transeda.com) +# updated 28th May 1999 by Paul Johnson + +my $File; + +BEGIN { + $File = __FILE__; + require strict; import strict; +} + +use Test; + +BEGIN { plan tests => 12 } + +use IO::File; + +sub lineno +{ + my ($f) = @_; + my $l; + $l .= "$. "; + $l .= $f->input_line_number; + $l .= " $."; # check $. before and after input_line_number + $l; +} + +my $t; + +open (F, $File) or die $!; +my $io = IO::File->new($File) or die $!; + +<F> for (1 .. 10); +ok(lineno($io), "10 0 10"); + +$io->getline for (1 .. 5); +ok(lineno($io), "5 5 5"); + +<F>; +ok(lineno($io), "11 5 11"); + +$io->getline; +ok(lineno($io), "6 6 6"); + +$t = tell F; # tell F; provokes a warning +ok(lineno($io), "11 6 11"); + +<F>; +ok(lineno($io), "12 6 12"); + +select F; +ok(lineno($io), "12 6 12"); + +<F> for (1 .. 10); +ok(lineno($io), "22 6 22"); + +$io->getline for (1 .. 5); +ok(lineno($io), "11 11 11"); + +$t = tell F; +# We used to have problems here before local $. worked. +# input_line_number() used to use select and tell. When we did the +# same, that mechanism broke. It should work now. +ok(lineno($io), "22 11 22"); + +{ + local $.; + $io->getline for (1 .. 5); + ok(lineno($io), "16 16 16"); +} + +ok(lineno($io), "22 16 22"); diff --git a/dist/IO/t/io_multihomed.t b/dist/IO/t/io_multihomed.t new file mode 100644 index 0000000000..f1bd5b9df9 --- /dev/null +++ b/dist/IO/t/io_multihomed.t @@ -0,0 +1,118 @@ +#!./perl + +BEGIN { + require($ENV{PERL_CORE} ? '../../t/test.pl' : './t/test.pl'); + + use Config; + my $can_fork = $Config{d_fork} || + (($^O eq 'MSWin32' || $^O eq 'NetWare') and + $Config{useithreads} and + $Config{ccflags} =~ /-DPERL_IMPLICIT_SYS/ + ); + my $reason; + if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bSocket\b/) { + $reason = 'Socket extension unavailable'; + } + elsif ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bIO\b/) { + $reason = 'IO extension unavailable'; + } + elsif (!$can_fork) { + $reason = 'no fork'; + } + skip_all($reason) if $reason; +} + +$| = 1; + +print "1..8\n"; +watchdog(15); + +package Multi; +require IO::Socket::INET; +@ISA=qw(IO::Socket::INET); + +use Socket qw(inet_aton inet_ntoa unpack_sockaddr_in); + +sub _get_addr +{ + my($sock,$addr_str, $multi) = @_; + #print "_get_addr($sock, $addr_str, $multi)\n"; + + print "not " unless $multi; + print "ok 2\n"; + + ( + # private IP-addresses which I hope does not work anywhere :-) + inet_aton("10.250.230.10"), + inet_aton("10.250.230.12"), + inet_aton("127.0.0.1") # loopback + ) +} + +sub connect +{ + my $self = shift; + if (@_ == 1) { + my($port, $addr) = unpack_sockaddr_in($_[0]); + $addr = inet_ntoa($addr); + #print "connect($self, $port, $addr)\n"; + if($addr eq "10.250.230.10") { + print "ok 3\n"; + return 0; + } + if($addr eq "10.250.230.12") { + print "ok 4\n"; + return 0; + } + } + $self->SUPER::connect(@_); +} + + + +package main; + +use IO::Socket; + +$listen = IO::Socket::INET->new(Listen => 2, + Proto => 'tcp', + Timeout => 5, + ) or die "$!"; + +print "ok 1\n"; + +$port = $listen->sockport; + +if($pid = fork()) { + + $sock = $listen->accept() or die "$!"; + print "ok 5\n"; + + print $sock->getline(); + print $sock "ok 7\n"; + + waitpid($pid,0); + + $sock->close; + + print "ok 8\n"; + +} elsif(defined $pid) { + + $sock = Multi->new(PeerPort => $port, + Proto => 'tcp', + PeerAddr => 'localhost', + MultiHomed => 1, + Timeout => 1, + ) or die "$!"; + + print $sock "ok 6\n"; + sleep(1); # race condition + print $sock->getline(); + + $sock->close; + + exit; +} else { + die; +} diff --git a/dist/IO/t/io_pipe.t b/dist/IO/t/io_pipe.t new file mode 100644 index 0000000000..b7897bb2df --- /dev/null +++ b/dist/IO/t/io_pipe.t @@ -0,0 +1,136 @@ +#!./perl + +my $perl; + +BEGIN { + $perl = $^X; +} + +use Config; + +BEGIN { + my $can_fork = $Config{d_fork} || + (($^O eq 'MSWin32' || $^O eq 'NetWare') and + $Config{useithreads} and + $Config{ccflags} =~ /-DPERL_IMPLICIT_SYS/ + ); + my $reason; + if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bIO\b/) { + $reason = 'IO extension unavailable'; + } + elsif (!$can_fork) { + $reason = 'no fork'; + } + elsif ($^O eq 'MSWin32' && !$ENV{TEST_IO_PIPE}) { + $reason = 'Win32 testing environment not set'; + } + if ($reason) { + print "1..0 # Skip: $reason\n"; + exit 0; + } +} + +use IO::Pipe; + +my $is_win32=$^O eq 'MSWin32' ? "MSWin32 has broken pipes" : ""; + +$| = 1; +print "1..10\n"; + +if ($is_win32) { + print "ok $_ # skipped: $is_win32\n" for 1..4; +} else { + $pipe = new IO::Pipe->reader($perl, '-e', 'print qq(not ok 1\n)'); + while (<$pipe>) { + s/^not //; + print; + } + $pipe->close or print "# \$!=$!\nnot "; + print "ok 2\n"; + $cmd = 'BEGIN{$SIG{ALRM} = sub {print qq(not ok 4\n); exit}; alarm 10} s/not //'; + $pipe = new IO::Pipe->writer($perl, '-pe', $cmd); + print $pipe "not ok 3\n" ; + $pipe->close or print "# \$!=$!\nnot "; + print "ok 4\n"; +} + +# Check if can fork with dynamic extensions (bug in CRT): +if ($^O eq 'os2' and + system "$^X -I../lib -MOpcode -e 'defined fork or die' > /dev/null 2>&1") { + print "ok $_ # skipped: broken fork\n" for 5..10; + exit 0; +} + +$pipe = new IO::Pipe; + +$pid = fork(); + +if($pid) + { + $pipe->writer; + print $pipe "Xk 5\n"; + print $pipe "oY 6\n"; + $pipe->close; + wait; + } +elsif(defined $pid) + { + $pipe->reader; + $stdin = bless \*STDIN, "IO::Handle"; + $stdin->fdopen($pipe,"r"); + exec $^X, '-pne', 'tr/YX/ko/'; + } +else + { + die "# error = $!"; + } + +if ($is_win32) { + print "ok $_ # skipped: $is_win32\n" for 7..8; +} else { + $pipe = new IO::Pipe; + $pid = fork(); + + if($pid) + { + $pipe->reader; + while(<$pipe>) { + s/^not //; + print; + } + $pipe->close; + wait; + } + elsif(defined $pid) + { + $pipe->writer; + + $stdout = bless \*STDOUT, "IO::Handle"; + $stdout->fdopen($pipe,"w"); + print STDOUT "not ok 7\n"; + exec 'echo', 'not ok 8'; + } + else + { + die; + } +} +if ($is_win32) { + print "ok $_ # skipped: $is_win32\n" for 9; +} else { + $pipe = new IO::Pipe; + $pipe->writer; + + $SIG{'PIPE'} = 'broken_pipe'; + + sub broken_pipe { + print "ok 9\n"; + } + + print $pipe "not ok 9\n"; + $pipe->close; + + sleep 1; +} +print "ok 10\n"; + diff --git a/dist/IO/t/io_poll.t b/dist/IO/t/io_poll.t new file mode 100644 index 0000000000..364d346ace --- /dev/null +++ b/dist/IO/t/io_poll.t @@ -0,0 +1,83 @@ +#!./perl + +if ($^O eq 'mpeix') { + print "1..0 # Skip: broken on MPE/iX\n"; + exit 0; +} + +select(STDERR); $| = 1; +select(STDOUT); $| = 1; + +print "1..10\n"; + +use IO::Handle; +use IO::Poll qw(/POLL/); + +my $poll = new IO::Poll; + +my $stdout = \*STDOUT; +my $dupout = IO::Handle->new_from_fd(fileno($stdout),"w"); + +$poll->mask($stdout => POLLOUT); + +print "not " + unless $poll->mask($stdout) == POLLOUT; +print "ok 1\n"; + +$poll->mask($dupout => POLLPRI); + +print "not " + unless $poll->mask($dupout) == POLLPRI; +print "ok 2\n"; + +$poll->poll(0.1); + +if ($^O eq 'MSWin32' || $^O eq 'NetWare' || $^O eq 'VMS' || $^O eq 'beos') { +print "ok 3 # skipped, doesn't work on non-socket fds\n"; +print "ok 4 # skipped, doesn't work on non-socket fds\n"; +} +else { +print "not " + unless $poll->events($stdout) == POLLOUT; +print "ok 3\n"; + +print "not " + if $poll->events($dupout); +print "ok 4\n"; +} + +my @h = $poll->handles; +print "not " + unless @h == 2; +print "ok 5\n"; + +$poll->remove($stdout); + +@h = $poll->handles; + +print "not " + unless @h == 1; +print "ok 6\n"; + +print "not " + if $poll->mask($stdout); +print "ok 7\n"; + +$poll->poll(0.1); + +print "not " + if $poll->events($stdout); +print "ok 8\n"; + +$poll->remove($dupout); +print "not " + if $poll->handles; +print "ok 9\n"; + +my $stdin = \*STDIN; +$poll->mask($stdin => POLLIN); +$poll->remove($stdin); +close STDIN; +print "not " + if $poll->poll(0.1); +print "ok 10\n"; diff --git a/dist/IO/t/io_sel.t b/dist/IO/t/io_sel.t new file mode 100644 index 0000000000..260ca439e7 --- /dev/null +++ b/dist/IO/t/io_sel.t @@ -0,0 +1,131 @@ +#!./perl -w + +select(STDERR); $| = 1; +select(STDOUT); $| = 1; + +print "1..23\n"; + +use IO::Select 1.09; + +my $sel = new IO::Select(\*STDIN); +$sel->add(4, 5) == 2 or print "not "; +print "ok 1\n"; + +$sel->add([\*STDOUT, 'foo']) == 1 or print "not "; +print "ok 2\n"; + +@handles = $sel->handles; +print "not " unless $sel->count == 4 && @handles == 4; +print "ok 3\n"; +#print $sel->as_string, "\n"; + +$sel->remove(\*STDIN) == 1 or print "not "; +print "ok 4\n", +; +$sel->remove(\*STDIN, 5, 6) == 1 # two of there are not present + or print "not "; +print "ok 5\n"; + +print "not " unless $sel->count == 2; +print "ok 6\n"; +#print $sel->as_string, "\n"; + +$sel->remove(1, 4); +print "not " unless $sel->count == 0 && !defined($sel->bits); +print "ok 7\n"; + +$sel = new IO::Select; +print "not " unless $sel->count == 0 && !defined($sel->bits); +print "ok 8\n"; + +$sel->remove([\*STDOUT, 5]); +print "not " unless $sel->count == 0 && !defined($sel->bits); +print "ok 9\n"; + +if ( grep $^O eq $_, qw(MSWin32 NetWare dos VMS riscos beos) ) { + for (10 .. 15) { + print "ok $_ # skip: 4-arg select is only valid on sockets\n" + } + $sel->add(\*STDOUT); # update + goto POST_SOCKET; +} + +@a = $sel->can_read(); # should return imediately +print "not " unless @a == 0; +print "ok 10\n"; + +# we assume that we can write to STDOUT :-) +$sel->add([\*STDOUT, "ok 12\n"]); + +@a = $sel->can_write; +print "not " unless @a == 1; +print "ok 11\n"; + +my($fd, $msg) = @{shift @a}; +print $fd $msg; + +$sel->add(\*STDOUT); # update + +@a = IO::Select::select(undef, $sel, undef, 1); +print "not " unless @a == 3; +print "ok 13\n"; + +($r, $w, $e) = @a; + +print "not " unless @$r == 0 && @$w == 1 && @$e == 0; +print "ok 14\n"; + +$fd = $w->[0]; +print $fd "ok 15\n"; + +POST_SOCKET: +# Test new exists() method +$sel->exists(\*STDIN) and print "not "; +print "ok 16\n"; + +($sel->exists(0) || $sel->exists([\*STDERR])) and print "not "; +print "ok 17\n"; + +$fd = $sel->exists(\*STDOUT); +if ($fd) { + print $fd "ok 18\n"; +} else { + print "not ok 18\n"; +} + +$fd = $sel->exists([1, 'foo']); +if ($fd) { + print $fd "ok 19\n"; +} else { + print "not ok 19\n"; +} + +# Try self clearing +$sel->add(5,6,7,8,9,10); +print "not " unless $sel->count == 7; +print "ok 20\n"; + +$sel->remove($sel->handles); +print "not " unless $sel->count == 0 && !defined($sel->bits); +print "ok 21\n"; + +# check warnings +$SIG{__WARN__} = sub { + ++ $w + if $_[0] =~ /^Call to deprecated method 'has_error', use 'has_exception'/ ; + } ; +$w = 0 ; +{ +no warnings 'IO::Select' ; +IO::Select::has_error(); +} +print "not " unless $w == 0 ; +$w = 0 ; +print "ok 22\n" ; +{ +use warnings 'IO::Select' ; +IO::Select::has_error(); +} +print "not " unless $w == 1 ; +$w = 0 ; +print "ok 23\n" ; diff --git a/dist/IO/t/io_sock.t b/dist/IO/t/io_sock.t new file mode 100644 index 0000000000..38aefeeb53 --- /dev/null +++ b/dist/IO/t/io_sock.t @@ -0,0 +1,396 @@ +#!./perl -w + +use Config; + +BEGIN { + my $can_fork = $Config{d_fork} || + (($^O eq 'MSWin32' || $^O eq 'NetWare') and + $Config{useithreads} and + $Config{ccflags} =~ /-DPERL_IMPLICIT_SYS/ + ); + my $reason; + if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bSocket\b/) { + $reason = 'Socket extension unavailable'; + } + elsif ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bIO\b/) { + $reason = 'IO extension unavailable'; + } + elsif (!$can_fork) { + $reason = 'no fork'; + } + if ($reason) { + print "1..0 # Skip: $reason\n"; + exit 0; + } +} + +my $has_perlio = $] >= 5.008 && find PerlIO::Layer 'perlio'; + +$| = 1; +print "1..26\n"; + +eval { + $SIG{ALRM} = sub { die; }; + alarm 120; +}; + +use IO::Socket; + +$listen = IO::Socket::INET->new(Listen => 2, + Proto => 'tcp', + # some systems seem to need as much as 10, + # so be generous with the timeout + Timeout => 15, + ) or die "$!"; + +print "ok 1\n"; + +# Check if can fork with dynamic extensions (bug in CRT): +if ($^O eq 'os2' and + system "$^X -I../lib -MOpcode -e 'defined fork or die' > /dev/null 2>&1") { + print "ok $_ # skipped: broken fork\n" for 2..5; + exit 0; +} + +$port = $listen->sockport; + +if($pid = fork()) { + + $sock = $listen->accept() or die "accept failed: $!"; + print "ok 2\n"; + + $sock->autoflush(1); + print $sock->getline(); + + print $sock "ok 4\n"; + + $sock->close; + + waitpid($pid,0); + + print "ok 5\n"; + +} elsif(defined $pid) { + + $sock = IO::Socket::INET->new(PeerPort => $port, + Proto => 'tcp', + PeerAddr => 'localhost' + ) + || IO::Socket::INET->new(PeerPort => $port, + Proto => 'tcp', + PeerAddr => '127.0.0.1' + ) + or die "$! (maybe your system does not have a localhost at all, 'localhost' or 127.0.0.1)"; + + $sock->autoflush(1); + + print $sock "ok 3\n"; + + print $sock->getline(); + + $sock->close; + + exit; +} else { + die; +} + +# Test various other ways to create INET sockets that should +# also work. +$listen = IO::Socket::INET->new(Listen => '', Timeout => 15) or die "$!"; +$port = $listen->sockport; + +if($pid = fork()) { + SERVER_LOOP: + while (1) { + last SERVER_LOOP unless $sock = $listen->accept; + while (<$sock>) { + last SERVER_LOOP if /^quit/; + last if /^done/; + print; + } + $sock = undef; + } + $listen->close; +} elsif (defined $pid) { + # child, try various ways to connect + $sock = IO::Socket::INET->new("localhost:$port") + || IO::Socket::INET->new("127.0.0.1:$port"); + if ($sock) { + print "not " unless $sock->connected; + print "ok 6\n"; + $sock->print("ok 7\n"); + sleep(1); + print "ok 8\n"; + $sock->print("ok 9\n"); + $sock->print("done\n"); + $sock->close; + } + else { + print "# $@\n"; + print "not ok 6\n"; + print "not ok 7\n"; + print "not ok 8\n"; + print "not ok 9\n"; + } + + # some machines seem to suffer from a race condition here + sleep(2); + + $sock = IO::Socket::INET->new("127.0.0.1:$port"); + if ($sock) { + $sock->print("ok 10\n"); + $sock->print("done\n"); + $sock->close; + } + else { + print "# $@\n"; + print "not ok 10\n"; + } + + # some machines seem to suffer from a race condition here + sleep(1); + + $sock = IO::Socket->new(Domain => AF_INET, + PeerAddr => "localhost:$port") + || IO::Socket->new(Domain => AF_INET, + PeerAddr => "127.0.0.1:$port"); + if ($sock) { + $sock->print("ok 11\n"); + $sock->print("quit\n"); + } else { + print "not ok 11\n"; + } + $sock = undef; + sleep(1); + exit; +} else { + die; +} + +# Then test UDP sockets +$server = IO::Socket->new(Domain => AF_INET, + Proto => 'udp', + LocalAddr => 'localhost') + || IO::Socket->new(Domain => AF_INET, + Proto => 'udp', + LocalAddr => '127.0.0.1'); +$port = $server->sockport; + +if ($pid = fork()) { + my $buf; + $server->recv($buf, 100); + print $buf; +} elsif (defined($pid)) { + #child + $sock = IO::Socket::INET->new(Proto => 'udp', + PeerAddr => "localhost:$port") + || IO::Socket::INET->new(Proto => 'udp', + PeerAddr => "127.0.0.1:$port"); + $sock->send("ok 12\n"); + sleep(1); + $sock->send("ok 12\n"); # send another one to be sure + exit; +} else { + die; +} + +print "not " unless $server->blocking; +print "ok 13\n"; + +if ( $^O eq 'qnx' ) { + # QNX4 library bug: Can set non-blocking on socket, but + # cannot return that status. + print "ok 14 # skipped on QNX4\n"; +} else { + $server->blocking(0); + print "not " if $server->blocking; + print "ok 14\n"; +} + +### TEST 15 +### Set up some data to be transfered between the server and +### the client. We'll use own source code ... +# +local @data; +if( !open( SRC, "< $0")) { + print "not ok 15 - $!\n"; +} else { + @data = <SRC>; + close(SRC); + print "ok 15\n"; +} + +### TEST 16 +### Start the server +# +my $listen = IO::Socket::INET->new( Listen => 2, Proto => 'tcp', Timeout => 15) || + print "not "; +print "ok 16\n"; +die if( !defined( $listen)); +my $serverport = $listen->sockport; +my $server_pid = fork(); +if( $server_pid) { + + ### TEST 17 Client/Server establishment + # + print "ok 17\n"; + + ### TEST 18 + ### Get data from the server using a single stream + # + $sock = IO::Socket::INET->new("localhost:$serverport") + || IO::Socket::INET->new("127.0.0.1:$serverport"); + + if ($sock) { + $sock->print("send\n"); + + my @array = (); + while( <$sock>) { + push( @array, $_); + } + + $sock->print("done\n"); + $sock->close; + + print "not " if( @array != @data); + } else { + print "not "; + } + print "ok 18\n"; + + ### TEST 21 + ### Get data from the server using a stream, which is + ### interrupted by eof calls. + ### On perl-5.7.0@7673 this failed in a SOCKS environment, because eof + ### did an getc followed by an ungetc in order to check for the streams + ### end. getc(3) got replaced by the SOCKS funktion, which ended up in + ### a recv(2) call on the socket, while ungetc(3) put back a character + ### to an IO buffer, which never again was read. + # + ### TESTS 19,20,21,22 + ### Try to ping-pong some Unicode. + # + $sock = IO::Socket::INET->new("localhost:$serverport") + || IO::Socket::INET->new("127.0.0.1:$serverport"); + + if ($has_perlio) { + print binmode($sock, ":utf8") ? "ok 19\n" : "not ok 19\n"; + } else { + print "ok 19 - Skip: no perlio\n"; + } + + if ($sock) { + + if ($has_perlio) { + $sock->print("ping \x{100}\n"); + chomp(my $pong = scalar <$sock>); + print $pong =~ /^pong (.+)$/ && $1 eq "\x{100}" ? + "ok 20\n" : "not ok 20\n"; + + $sock->print("ord \x{100}\n"); + chomp(my $ord = scalar <$sock>); + print $ord == 0x100 ? + "ok 21\n" : "not ok 21\n"; + + $sock->print("chr 0x100\n"); + chomp(my $chr = scalar <$sock>); + print $chr eq "\x{100}" ? + "ok 22\n" : "not ok 22\n"; + } else { + print "ok $_ - Skip: no perlio\n" for 20..22; + } + + $sock->print("send\n"); + + my @array = (); + while( !eof( $sock ) ){ + while( <$sock>) { + push( @array, $_); + last; + } + } + + $sock->print("done\n"); + $sock->close; + + print "not " if( @array != @data); + } else { + print "not "; + } + print "ok 23\n"; + + ### TEST 24 + ### Stop the server + # + $sock = IO::Socket::INET->new("localhost:$serverport") + || IO::Socket::INET->new("127.0.0.1:$serverport"); + + if ($sock) { + $sock->print("done\n"); + $sock->close; + + print "not " if( 1 != kill 0, $server_pid); + } else { + print "not "; + } + print "ok 24\n"; + +} elsif (defined($server_pid)) { + + ### Child + # + SERVER_LOOP: while (1) { + last SERVER_LOOP unless $sock = $listen->accept; + # Do not print ok/not ok for this binmode() since there's + # a race condition with our client, just die if we fail. + if ($has_perlio) { binmode($sock, ":utf8") or die } + while (<$sock>) { + last SERVER_LOOP if /^quit/; + last if /^done/; + if (/^ping (.+)/) { + print $sock "pong $1\n"; + next; + } + if (/^ord (.+)/) { + print $sock ord($1), "\n"; + next; + } + if (/^chr (.+)/) { + print $sock chr(hex($1)), "\n"; + next; + } + if (/^send/) { + print $sock @data; + last; + } + print; + } + $sock = undef; + } + $listen->close; + exit 0; + +} else { + + ### Fork failed + # + print "not ok 17\n"; + die; +} + +# test Blocking option in constructor + +$sock = IO::Socket::INET->new(Blocking => 0) + or print "not "; +print "ok 25\n"; + +if ( $^O eq 'qnx' ) { + print "ok 26 # skipped on QNX4\n"; + # QNX4 library bug: Can set non-blocking on socket, but + # cannot return that status. +} else { + my $status = $sock->blocking; + print "not " unless defined $status && !$status; + print "ok 26\n"; +} diff --git a/dist/IO/t/io_taint.t b/dist/IO/t/io_taint.t new file mode 100644 index 0000000000..3cbe30345f --- /dev/null +++ b/dist/IO/t/io_taint.t @@ -0,0 +1,62 @@ +#!./perl -T + +use Config; + +BEGIN { + if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bIO\b/ && $^O ne 'VMS') { + print "1..0\n"; + exit 0; + } +} + +use strict; +if ($ENV{PERL_CORE}) { + require("../../t/test.pl"); +} +else { + require("./t/test.pl"); +} +plan(tests => 5); + +END { unlink "./__taint__$$" } + +use IO::File; +my $x = new IO::File "> ./__taint__$$" || die("Cannot open ./__taint__$$\n"); +print $x "$$\n"; +$x->close; + +$x = new IO::File "< ./__taint__$$" || die("Cannot open ./__taint__$$\n"); +chop(my $unsafe = <$x>); +eval { kill 0 * $unsafe }; +SKIP: { + skip($^O) if $^O eq 'MSWin32' or $^O eq 'NetWare'; + like($@, '^Insecure'); +} +$x->close; + +# We could have just done a seek on $x, but technically we haven't tested +# seek yet... +$x = new IO::File "< ./__taint__$$" || die("Cannot open ./__taint__$$\n"); +$x->untaint; +ok(!$?); # Calling the method worked +chop($unsafe = <$x>); +eval { kill 0 * $unsafe }; +unlike($@,'^Insecure'); +$x->close; + +TODO: { + todo_skip("Known bug in 5.10.0",2) if $] >= 5.010 and $] < 5.010_001; + + # this will segfault if it fails + + sub PVBM () { 'foo' } + { my $dummy = index 'foo', PVBM } + + eval { IO::Handle::untaint(PVBM) }; + pass(); + + eval { IO::Handle::untaint(\PVBM) }; + pass(); +} + +exit 0; diff --git a/dist/IO/t/io_tell.t b/dist/IO/t/io_tell.t new file mode 100644 index 0000000000..3f8ad30b9f --- /dev/null +++ b/dist/IO/t/io_tell.t @@ -0,0 +1,55 @@ +#!./perl + +BEGIN { + $tell_file = "Makefile.PL"; +} + +use Config; + +BEGIN { + if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bIO\b/ && $^O ne 'VMS') { + print "1..0\n"; + exit 0; + } +} + +print "1..13\n"; + +use IO::File; + +$tst = IO::File->new("$tell_file","r") || die("Can't open $tell_file"); +binmode $tst; # its a nop unless it matters. Was only if ($^O eq 'MSWin32' or $^O eq 'dos'); +if ($tst->eof) { print "not ok 1\n"; } else { print "ok 1\n"; } + +$firstline = <$tst>; +$secondpos = tell; + +$x = 0; +while (<$tst>) { + if (eof) {$x++;} +} +if ($x == 1) { print "ok 2\n"; } else { print "not ok 2\n"; } + +$lastpos = tell; + +unless (eof) { print "not ok 3\n"; } else { print "ok 3\n"; } + +if ($tst->seek(0,0)) { print "ok 4\n"; } else { print "not ok 4\n"; } + +if (eof) { print "not ok 5\n"; } else { print "ok 5\n"; } + +if ($firstline eq <$tst>) { print "ok 6\n"; } else { print "not ok 6\n"; } + +if ($secondpos == tell) { print "ok 7\n"; } else { print "not ok 7\n"; } + +if ($tst->seek(0,1)) { print "ok 8\n"; } else { print "not ok 8\n"; } + +if ($tst->eof) { print "not ok 9\n"; } else { print "ok 9\n"; } + +if ($secondpos == tell) { print "ok 10\n"; } else { print "not ok 10\n"; } + +if ($tst->seek(0,2)) { print "ok 11\n"; } else { print "not ok 11\n"; } + +if ($lastpos == $tst->tell) { print "ok 12\n"; } else { print "not ok 12\n"; } + +unless (eof) { print "not ok 13\n"; } else { print "ok 13\n"; } diff --git a/dist/IO/t/io_udp.t b/dist/IO/t/io_udp.t new file mode 100644 index 0000000000..6b139dd83f --- /dev/null +++ b/dist/IO/t/io_udp.t @@ -0,0 +1,79 @@ +#!./perl + +BEGIN { + require($ENV{PERL_CORE} ? '../../t/test.pl' : './t/test.pl'); + + use Config; + my $reason; + if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bSocket\b/) { + $reason = 'Socket was not built'; + } + elsif ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bIO\b/) { + $reason = 'IO was not built'; + } + elsif ($^O eq 'apollo') { + $reason = "unknown *FIXME*"; + } + undef $reason if $^O eq 'VMS' and $Config{d_socket}; + skip_all($reason) if $reason; +} + +sub compare_addr { + no utf8; + my $a = shift; + my $b = shift; + if (length($a) != length $b) { + my $min = (length($a) < length $b) ? length($a) : length $b; + if ($min and substr($a, 0, $min) eq substr($b, 0, $min)) { + printf "# Apparently: %d bytes junk at the end of %s\n# %s\n", + abs(length($a) - length ($b)), + $_[length($a) < length ($b) ? 1 : 0], + "consider decreasing bufsize of recfrom."; + substr($a, $min) = ""; + substr($b, $min) = ""; + } + return 0; + } + my @a = unpack_sockaddr_in($a); + my @b = unpack_sockaddr_in($b); + "$a[0]$a[1]" eq "$b[0]$b[1]"; +} + +plan(7); +watchdog(15); + +use Socket; +use IO::Socket qw(AF_INET SOCK_DGRAM INADDR_ANY); + +$udpa = IO::Socket::INET->new(Proto => 'udp', LocalAddr => 'localhost') + || IO::Socket::INET->new(Proto => 'udp', LocalAddr => '127.0.0.1') + or die "$! (maybe your system does not have a localhost at all, 'localhost' or 127.0.0.1)"; +ok(1); + +$udpb = IO::Socket::INET->new(Proto => 'udp', LocalAddr => 'localhost') + || IO::Socket::INET->new(Proto => 'udp', LocalAddr => '127.0.0.1') + or die "$! (maybe your system does not have a localhost at all, 'localhost' or 127.0.0.1)"; +ok(1); + +$udpa->send('BORK', 0, $udpb->sockname); + +ok(compare_addr($udpa->peername,$udpb->sockname, 'peername', 'sockname')); + +my $where = $udpb->recv($buf="", 4); +is($buf, 'BORK'); + +my @xtra = (); + +if (! ok(compare_addr($where,$udpa->sockname, 'recv name', 'sockname'))) { + @xtra = (0, $udpa->sockname); +} + +$udpb->send('FOObar', @xtra); +$udpa->recv($buf="", 6); +is($buf, 'FOObar'); + +ok(! $udpa->connected); + +exit(0); + +# EOF diff --git a/dist/IO/t/io_unix.t b/dist/IO/t/io_unix.t new file mode 100644 index 0000000000..61ba3635f8 --- /dev/null +++ b/dist/IO/t/io_unix.t @@ -0,0 +1,113 @@ +#!./perl + +use Config; + +BEGIN { + my $reason; + if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bSocket\b/) { + $reason = 'Socket extension unavailable'; + } + elsif ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bIO\b/) { + $reason = 'IO extension unavailable'; + } + elsif ($^O eq 'os2') { + require IO::Socket; + + eval {IO::Socket::pack_sockaddr_un('/foo/bar') || 1} + or $@ !~ /not implemented/ or + $reason = 'compiled without TCP/IP stack v4'; + } + elsif ($^O =~ m/^(?:qnx|nto|vos|MSWin32)$/ ) { + $reason = "UNIX domain sockets not implemented on $^O"; + } + elsif (! $Config{'d_fork'}) { + $reason = 'no fork'; + } + if ($reason) { + print "1..0 # Skip: $reason\n"; + exit 0; + } +} + +$PATH = "sock-$$"; + +if ($^O eq 'os2') { # Can't create sockets with relative path... + require Cwd; + my $d = Cwd::cwd(); + $d =~ s/^[a-z]://i; + $PATH = "$d/$PATH"; +} + +# Test if we can create the file within the tmp directory +if (-e $PATH or not open(TEST, ">$PATH") and $^O ne 'os2') { + print "1..0 # Skip: cannot open '$PATH' for write\n"; + exit 0; +} +close(TEST); +unlink($PATH) or $^O eq 'os2' or die "Can't unlink $PATH: $!"; + +# Start testing +$| = 1; +print "1..5\n"; + +use IO::Socket; + +$listen = IO::Socket::UNIX->new(Local => $PATH, Listen => 0); + +# Sometimes UNIX filesystems are mounted for security reasons +# with "nodev" option which spells out "no" for creating UNIX +# local sockets. Therefore we will retry with a File::Temp +# generated filename from a temp directory. +unless (defined $listen) { + eval { require File::Temp }; + unless ($@) { + import File::Temp 'mktemp'; + for my $TMPDIR ($ENV{TMPDIR}, "/tmp") { + if (defined $TMPDIR && -d $TMPDIR && -w $TMPDIR) { + $PATH = mktemp("$TMPDIR/sXXXXXXXX"); + last if $listen = IO::Socket::UNIX->new(Local => $PATH, + Listen => 0); + } + } + } + defined $listen or die "$PATH: $!"; +} +print "ok 1\n"; + +if($pid = fork()) { + + $sock = $listen->accept(); + + if (defined $sock) { + print "ok 2\n"; + + print $sock->getline(); + + print $sock "ok 4\n"; + + $sock->close; + + waitpid($pid,0); + unlink($PATH) || $^O eq 'os2' || warn "Can't unlink $PATH: $!"; + + print "ok 5\n"; + } else { + print "# accept() failed: $!\n"; + for (2..5) { + print "not ok $_ # accept failed\n"; + } + } +} elsif(defined $pid) { + + $sock = IO::Socket::UNIX->new(Peer => $PATH) or die "$!"; + + print $sock "ok 3\n"; + + print $sock->getline(); + + $sock->close; + + exit; +} else { + die; +} diff --git a/dist/IO/t/io_utf8.t b/dist/IO/t/io_utf8.t new file mode 100644 index 0000000000..53c209d4b8 --- /dev/null +++ b/dist/IO/t/io_utf8.t @@ -0,0 +1,31 @@ +#!./perl + +BEGIN { + unless ($] >= 5.008 and find PerlIO::Layer 'perlio') { + print "1..0 # Skip: not perlio\n"; + exit 0; + } +} + +require($ENV{PERL_CORE} ? "../../t/test.pl" : "./t/test.pl"); + +plan(tests => 5); + +my $io; + +use_ok('IO::File'); + +$io = IO::File->new; + +ok($io->open("io_utf8", ">:utf8"), "open >:utf8"); +ok((print $io chr(256)), "print chr(256)"); +undef $io; + +$io = IO::File->new; +ok($io->open("io_utf8", "<:utf8"), "open <:utf8"); +is(ord(<$io>), 256, "readline chr(256)"); +undef $io; + +END { + 1 while unlink "io_utf8"; +} diff --git a/dist/IO/t/io_xs.t b/dist/IO/t/io_xs.t new file mode 100644 index 0000000000..585eed84b2 --- /dev/null +++ b/dist/IO/t/io_xs.t @@ -0,0 +1,40 @@ +#!./perl + +use Config; + +BEGIN { + if($ENV{PERL_CORE}) { + if ($Config{'extensions'} !~ /\bIO\b/) { + print "1..0 # Skip: IO extension not built\n"; + exit 0; + } + } + if( $^O eq 'VMS' && $Config{'vms_cc_type'} ne 'decc' ) { + print "1..0 # Skip: not compatible with the VAXCRTL\n"; + exit 0; + } +} + +use IO::File; +use IO::Seekable; + +print "1..4\n"; + +$x = new_tmpfile IO::File or print "not "; +print "ok 1\n"; +print $x "ok 2\n"; +$x->seek(0,SEEK_SET); +print <$x>; + +$x->seek(0,SEEK_SET); +print $x "not ok 3\n"; +$p = $x->getpos; +print $x "ok 3\n"; +$x->flush; +$x->setpos($p); +print scalar <$x>; + +$! = 0; +$x->setpos(undef); +print $! ? "ok 4 # $!\n" : "not ok 4\n"; + |