This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
add README.Y2K (from Dominic Dunlop <domo@vo.lu>)
[perl5.git] / doio.c
diff --git a/doio.c b/doio.c
index 271218f..0b1cdd1 100644 (file)
--- a/doio.c
+++ b/doio.c
@@ -1,6 +1,6 @@
 /*    doio.c
  *
- *    Copyright (c) 1991-1997, Larry Wall
+ *    Copyright (c) 1991-1999, Larry Wall
  *
  *    You may distribute under the terms of either the GNU General Public
  *    License or the Artistic License, as specified in the README file.
  */
 
 #include "EXTERN.h"
+#define PERL_IN_DOIO_C
 #include "perl.h"
 
 #if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
+#ifndef HAS_SEM
 #include <sys/ipc.h>
+#endif
 #ifdef HAS_MSG
 #include <sys/msg.h>
 #endif
-#ifdef HAS_SEM
-#include <sys/sem.h>
-#endif
 #ifdef HAS_SHM
 #include <sys/shm.h>
 # ifndef HAS_SHMAT_PROTOTYPE
-    extern Shmat_t shmat _((int, char *, int));
+    extern Shmat_t shmat (int, char *, int);
 # endif
 #endif
 #endif
 
 #if defined(HAS_SOCKET) && !defined(VMS) /* VMS handles sockets via vmsish.h */
 # include <sys/socket.h>
-# include <netdb.h>
+# if defined(USE_SOCKS) && defined(I_SOCKS)
+#   include <socks.h>
+# endif 
+# ifdef I_NETBSD
+#  include <netdb.h>
+# endif
 # ifndef ENOTSOCK
 #  ifdef I_NET_ERRNO
 #   include <net/errno.h>
 #endif
 
 bool
-do_open(GV *gv, register char *name, I32 len, int as_raw, int rawmode, int rawperm, PerlIO *supplied_fp)
+Perl_do_open(pTHX_ GV *gv, register char *name, I32 len, int as_raw,
+            int rawmode, int rawperm, PerlIO *supplied_fp)
+{
+    return do_open9(gv, name, len, as_raw, rawmode, rawperm,
+                   supplied_fp, Nullsv, 0);
+}
+
+bool
+Perl_do_open9(pTHX_ GV *gv, register char *name, I32 len, int as_raw,
+             int rawmode, int rawperm, PerlIO *supplied_fp, SV *svs,
+             I32 num_svs)
 {
     register IO *io = GvIOn(gv);
     PerlIO *saveifp = Nullfp;
@@ -111,7 +126,7 @@ do_open(GV *gv, register char *name, I32 len, int as_raw, int rawmode, int rawpe
        else if (IoIFP(io) != IoOFP(io)) {
            if (IoOFP(io)) {
                result = PerlIO_close(IoOFP(io));
-               PerlIO_close(IoIFP(io));        /* clear stdio, fd already closed */
+               PerlIO_close(IoIFP(io)); /* clear stdio, fd already closed */
            }
            else
                result = PerlIO_close(IoIFP(io));
@@ -119,15 +134,21 @@ do_open(GV *gv, register char *name, I32 len, int as_raw, int rawmode, int rawpe
        else
            result = PerlIO_close(IoIFP(io));
        if (result == EOF && fd > PL_maxsysfd)
-           PerlIO_printf(PerlIO_stderr(), "Warning: unable to close filehandle %s properly.\n",
-             GvENAME(gv));
+           PerlIO_printf(PerlIO_stderr(),
+                         "Warning: unable to close filehandle %s properly.\n",
+                         GvENAME(gv));
        IoOFP(io) = IoIFP(io) = Nullfp;
     }
 
     if (as_raw) {
+#if defined(USE_64_BIT_OFFSETS) && defined(O_LARGEFILE)
+       rawmode |= O_LARGEFILE;
+#endif
+
 #ifndef O_ACCMODE
 #define O_ACCMODE 3            /* Assume traditional implementation */
 #endif
+
        switch (result = rawmode & O_ACCMODE) {
        case O_RDONLY:
             IoTYPE(io) = '<';
@@ -163,50 +184,79 @@ do_open(GV *gv, register char *name, I32 len, int as_raw, int rawmode, int rawpe
     }
     else {
        char *myname;
+       char *type = name;
+       char *otype = name;
+       STRLEN tlen;
+       STRLEN otlen = len;
        char mode[3];           /* stdio file mode ("r\0" or "r+\0") */
        int dodup;
 
+       if (num_svs) {
+           type = name;
+           name = SvPV(svs, tlen) ;
+           len = (I32)tlen;
+       }
+
+       tlen = otlen;
        myname = savepvn(name, len);
        SAVEFREEPV(myname);
        name = myname;
-       while (len && isSPACE(name[len-1]))
-           name[--len] = '\0';
+       if (!num_svs)
+           while (tlen && isSPACE(type[tlen-1]))
+               type[--tlen] = '\0';
 
        mode[0] = mode[1] = mode[2] = '\0';
-       IoTYPE(io) = *name;
-       if (*name == '+' && len > 1 && name[len-1] != '|') { /* scary */
-           mode[1] = *name++;
-           --len;
+       IoTYPE(io) = *type;
+       if (*type == '+' && tlen > 1 && type[tlen-1] != '|') { /* scary */
+           mode[1] = *type++;
+           --tlen;
            writing = 1;
        }
 
-       if (*name == '|') {
+       if (*type == '|') {
+           if (num_svs && (tlen != 2 || type[1] != '-')) {
+             unknown_desr:
+               Perl_croak(aTHX_ "Unknown open() mode '%.*s'", otlen, otype);
+           }
            /*SUPPRESS 530*/
-           for (name++; isSPACE(*name); name++) ;
-           if (strNE(name,"-"))
+           for (type++; isSPACE(*type); type++) ;
+           if (!num_svs)
+               name = type;
+           if (*name == '\0') { /* command is missing 19990114 */
+               dTHR;
+               if (ckWARN(WARN_PIPE))
+                   Perl_warner(aTHX_ WARN_PIPE, "Missing command in piped open");
+               errno = EPIPE;
+               goto say_false;
+           }
+           if (strNE(name,"-") || num_svs)
                TAINT_ENV();
            TAINT_PROPER("piped open");
            if (name[strlen(name)-1] == '|') {
                dTHR;
                name[strlen(name)-1] = '\0' ;
                if (ckWARN(WARN_PIPE))
-                   warner(WARN_PIPE, "Can't do bidirectional pipe");
+                   Perl_warner(aTHX_ WARN_PIPE, "Can't do bidirectional pipe");
            }
            fp = PerlProc_popen(name,"w");
            writing = 1;
        }
-       else if (*name == '>') {
+       else if (*type == '>') {
            TAINT_PROPER("open");
-           name++;
-           if (*name == '>') {
+           type++;
+           if (*type == '>') {
                mode[0] = IoTYPE(io) = 'a';
-               name++;
+               type++;
+               tlen--;
            }
            else
                mode[0] = 'w';
            writing = 1;
 
-           if (*name == '&') {
+           if (num_svs && tlen != 1)
+               goto unknown_desr;
+           if (*type == '&') {
+               name = type;
              duplicity:
                dodup = 1;
                name++;
@@ -232,7 +282,19 @@ do_open(GV *gv, register char *name, I32 len, int as_raw, int rawmode, int rawpe
                            goto say_false;
                        }
                        if (IoIFP(thatio)) {
-                           fd = PerlIO_fileno(IoIFP(thatio));
+                           PerlIO *fp = IoIFP(thatio);
+                           /* Flush stdio buffer before dup. --mjd
+                            * Unfortunately SEEK_CURing 0 seems to
+                            * be optimized away on most platforms;
+                            * only Solaris and Linux seem to flush
+                            * on that. --jhi */
+                           PerlIO_seek(fp, 0, SEEK_CUR);
+                           /* On the other hand, do all platforms
+                            * take gracefully to flushing a read-only
+                            * filehandle?  Perhaps we should do
+                            * fsetpos(src)+fgetpos(dst)?  --nik */
+                           PerlIO_flush(fp);
+                           fd = PerlIO_fileno(fp);
                            if (IoTYPE(thatio) == 's')
                                IoTYPE(io) = 's';
                        }
@@ -251,42 +313,63 @@ do_open(GV *gv, register char *name, I32 len, int as_raw, int rawmode, int rawpe
            }
            else {
                /*SUPPRESS 530*/
-               for (; isSPACE(*name); name++) ;
-               if (strEQ(name,"-")) {
+               for (; isSPACE(*type); type++) ;
+               if (strEQ(type,"-")) {
                    fp = PerlIO_stdout();
                    IoTYPE(io) = '-';
                }
                else  {
-                   fp = PerlIO_open(name,mode);
+                   fp = PerlIO_open((num_svs ? name : type), mode);
                }
            }
        }
-       else if (*name == '<') {
+       else if (*type == '<') {
+           if (num_svs && tlen != 1)
+               goto unknown_desr;
            /*SUPPRESS 530*/
-           for (name++; isSPACE(*name); name++) ;
+           for (type++; isSPACE(*type); type++) ;
            mode[0] = 'r';
-           if (*name == '&')
+           if (*type == '&') {
+               name = type;
                goto duplicity;
-           if (strEQ(name,"-")) {
+           }
+           if (strEQ(type,"-")) {
                fp = PerlIO_stdin();
                IoTYPE(io) = '-';
            }
            else
-               fp = PerlIO_open(name,mode);
+               fp = PerlIO_open((num_svs ? name : type), mode);
        }
-       else if (len > 1 && name[len-1] == '|') {
-           name[--len] = '\0';
-           while (len && isSPACE(name[len-1]))
-               name[--len] = '\0';
-           /*SUPPRESS 530*/
-           for (; isSPACE(*name); name++) ;
-           if (strNE(name,"-"))
+       else if (tlen > 1 && type[tlen-1] == '|') {
+           if (num_svs) {
+               if (tlen != 2 || type[0] != '-')
+                   goto unknown_desr;
+           }
+           else {
+               type[--tlen] = '\0';
+               while (tlen && isSPACE(type[tlen-1]))
+                   type[--tlen] = '\0';
+               /*SUPPRESS 530*/
+               for (; isSPACE(*type); type++) ;
+               name = type;
+           }
+           if (*name == '\0') { /* command is missing 19990114 */
+               dTHR;
+               if (ckWARN(WARN_PIPE))
+                   Perl_warner(aTHX_ WARN_PIPE, "Missing command in piped open");
+               errno = EPIPE;
+               goto say_false;
+           }
+           if (strNE(name,"-") || num_svs)
                TAINT_ENV();
            TAINT_PROPER("piped open");
            fp = PerlProc_popen(name,"r");
            IoTYPE(io) = '|';
        }
        else {
+           if (num_svs)
+               goto unknown_desr;
+           name = type;
            IoTYPE(io) = '<';
            /*SUPPRESS 530*/
            for (; isSPACE(*name); name++) ;
@@ -301,7 +384,7 @@ do_open(GV *gv, register char *name, I32 len, int as_raw, int rawmode, int rawpe
     if (!fp) {
        dTHR;
        if (ckWARN(WARN_NEWLINE) && IoTYPE(io) == '<' && strchr(name, '\n'))
-           warner(WARN_NEWLINE, warn_nl, "open");
+           Perl_warner(aTHX_ WARN_NEWLINE, PL_warn_nl, "open");
        goto say_false;
     }
     if (IoTYPE(io) &&
@@ -342,7 +425,7 @@ do_open(GV *gv, register char *name, I32 len, int as_raw, int rawmode, int rawpe
            }
        }
        if (fd != PerlIO_fileno(fp)) {
-           int pid;
+           Pid_t pid;
            SV *sv;
 
            PerlLIO_dup2(PerlIO_fileno(fp), fd);
@@ -361,8 +444,12 @@ do_open(GV *gv, register char *name, I32 len, int as_raw, int rawmode, int rawpe
        PerlIO_clearerr(fp);
     }
 #if defined(HAS_FCNTL) && defined(F_SETFD)
-    fd = PerlIO_fileno(fp);
-    fcntl(fd,F_SETFD,fd > PL_maxsysfd);
+    {
+       int save_errno = errno;
+       fd = PerlIO_fileno(fp);
+       fcntl(fd,F_SETFD,fd > PL_maxsysfd); /* can change errno */
+       errno = save_errno;
+    }
 #endif
     IoIFP(io) = fp;
     if (writing) {
@@ -388,15 +475,15 @@ say_false:
 }
 
 PerlIO *
-nextargv(register GV *gv)
+Perl_nextargv(pTHX_ register GV *gv)
 {
     register SV *sv;
 #ifndef FLEXFILENAMES
     int filedev;
     int fileino;
 #endif
-    int fileuid;
-    int filegid;
+    Uid_t fileuid;
+    Gid_t filegid;
 
     if (!PL_argvoutgv)
        PL_argvoutgv = gv_fetchpv("ARGVOUT",TRUE,SVt_PVIO);
@@ -432,8 +519,10 @@ nextargv(register GV *gv)
                fileuid = PL_statbuf.st_uid;
                filegid = PL_statbuf.st_gid;
                if (!S_ISREG(PL_filemode)) {
-                   warn("Can't do inplace edit: %s is not a regular file",
-                     PL_oldname );
+                   if (ckWARN_d(WARN_INPLACE)) 
+                       Perl_warner(aTHX_ WARN_INPLACE,
+                           "Can't do inplace edit: %s is not a regular file",
+                           PL_oldname );
                    do_close(gv,FALSE);
                    continue;
                }
@@ -460,18 +549,23 @@ nextargv(register GV *gv)
 #ifdef DJGPP
                       || (_djstat_fail_bits & _STFAIL_TRUENAME)!=0
 #endif
-                      ) {
-                       warn("Can't do inplace edit: %s would not be uniq",
-                         SvPVX(sv) );
+                      )
+                   {
+                       if (ckWARN_d(WARN_INPLACE))     
+                           Perl_warner(aTHX_ WARN_INPLACE,
+                             "Can't do inplace edit: %s would not be unique",
+                             SvPVX(sv));
                        do_close(gv,FALSE);
                        continue;
                    }
 #endif
 #ifdef HAS_RENAME
-#ifndef DOSISH
+#if !defined(DOSISH) && !defined(CYGWIN)
                    if (PerlLIO_rename(PL_oldname,SvPVX(sv)) < 0) {
-                       warn("Can't rename %s to %s: %s, skipping file",
-                         PL_oldname, SvPVX(sv), Strerror(errno) );
+                       if (ckWARN_d(WARN_INPLACE))     
+                           Perl_warner(aTHX_ WARN_INPLACE, 
+                             "Can't rename %s to %s: %s, skipping file",
+                             PL_oldname, SvPVX(sv), Strerror(errno) );
                        do_close(gv,FALSE);
                        continue;
                    }
@@ -484,8 +578,10 @@ nextargv(register GV *gv)
 #else
                    (void)UNLINK(SvPVX(sv));
                    if (link(PL_oldname,SvPVX(sv)) < 0) {
-                       warn("Can't rename %s to %s: %s, skipping file",
-                         PL_oldname, SvPVX(sv), Strerror(errno) );
+                       if (ckWARN_d(WARN_INPLACE))     
+                           Perl_warner(aTHX_ WARN_INPLACE,
+                             "Can't rename %s to %s: %s, skipping file",
+                             PL_oldname, SvPVX(sv), Strerror(errno) );
                        do_close(gv,FALSE);
                        continue;
                    }
@@ -496,14 +592,16 @@ nextargv(register GV *gv)
 #if !defined(DOSISH) && !defined(AMIGAOS)
 #  ifndef VMS  /* Don't delete; use automatic file versioning */
                    if (UNLINK(PL_oldname) < 0) {
-                       warn("Can't remove %s: %s, skipping file",
-                         PL_oldname, Strerror(errno) );
+                       if (ckWARN_d(WARN_INPLACE))     
+                           Perl_warner(aTHX_ WARN_INPLACE,
+                             "Can't remove %s: %s, skipping file",
+                             PL_oldname, Strerror(errno) );
                        do_close(gv,FALSE);
                        continue;
                    }
 #  endif
 #else
-                   croak("Can't do inplace edit without backup");
+                   Perl_croak(aTHX_ "Can't do inplace edit without backup");
 #endif
                }
 
@@ -517,8 +615,9 @@ nextargv(register GV *gv)
                if (!do_open(PL_argvoutgv,SvPVX(sv),SvCUR(sv),PL_inplace!=0,
                             O_WRONLY|O_CREAT|OPEN_EXCL,0666,Nullfp)) {
 #endif
-                   warn("Can't do inplace edit on %s: %s",
-                     PL_oldname, Strerror(errno) );
+                   if (ckWARN_d(WARN_INPLACE)) 
+                       Perl_warner(aTHX_ WARN_INPLACE, "Can't do inplace edit on %s: %s",
+                         PL_oldname, Strerror(errno) );
                    do_close(gv,FALSE);
                    continue;
                }
@@ -545,9 +644,18 @@ nextargv(register GV *gv)
            }
            return IoIFP(GvIOp(gv));
        }
-       else
-           PerlIO_printf(PerlIO_stderr(), "Can't open %s: %s\n",
-             SvPV(sv, PL_na), Strerror(errno));
+       else {
+           dTHR;
+           if (ckWARN_d(WARN_INPLACE)) {
+               if (!S_ISREG(PL_statbuf.st_mode))       
+                   Perl_warner(aTHX_ WARN_INPLACE,
+                               "Can't do inplace edit: %s is not a regular file",
+                               PL_oldname );
+               else
+                   Perl_warner(aTHX_ WARN_INPLACE, "Can't open %s: %s\n",
+                               PL_oldname, Strerror(errno));
+           }
+       }
     }
     if (PL_inplace) {
        (void)do_close(PL_argvoutgv,FALSE);
@@ -558,7 +666,7 @@ nextargv(register GV *gv)
 
 #ifdef HAS_PIPE
 void
-do_pipe(SV *sv, GV *rgv, GV *wgv)
+Perl_do_pipe(pTHX_ SV *sv, GV *rgv, GV *wgv)
 {
     register IO *rstio;
     register IO *wstio;
@@ -603,7 +711,7 @@ badexit:
 
 /* explicit renamed to avoid C++ conflict    -- kja */
 bool
-do_close(GV *gv, bool not_implicit)
+Perl_do_close(pTHX_ GV *gv, bool not_implicit)
 {
     bool retval;
     IO *io;
@@ -620,13 +728,13 @@ do_close(GV *gv, bool not_implicit)
        if (not_implicit) {
            dTHR;
            if (ckWARN(WARN_UNOPENED))
-               warner(WARN_UNOPENED, 
+               Perl_warner(aTHX_ WARN_UNOPENED, 
                       "Close on unopened file <%s>",GvENAME(gv));
            SETERRNO(EBADF,SS$_IVCHAN);
        }
        return FALSE;
     }
-    retval = io_close(io);
+    retval = io_close(io, not_implicit);
     if (not_implicit) {
        IoLINES(io) = 0;
        IoPAGE(io) = 0;
@@ -637,7 +745,7 @@ do_close(GV *gv, bool not_implicit)
 }
 
 bool
-io_close(IO *io)
+Perl_io_close(pTHX_ IO *io, bool not_implicit)
 {
     bool retval = FALSE;
     int status;
@@ -645,8 +753,13 @@ io_close(IO *io)
     if (IoIFP(io)) {
        if (IoTYPE(io) == '|') {
            status = PerlProc_pclose(IoIFP(io));
-           STATUS_NATIVE_SET(status);
-           retval = (STATUS_POSIX == 0);
+           if (not_implicit) {
+               STATUS_NATIVE_SET(status);
+               retval = (STATUS_POSIX == 0);
+           }
+           else {
+               retval = (status != -1);
+           }
        }
        else if (IoTYPE(io) == '-')
            retval = TRUE;
@@ -660,7 +773,7 @@ io_close(IO *io)
        }
        IoOFP(io) = IoIFP(io) = Nullfp;
     }
-    else {
+    else if (not_implicit) {
        SETERRNO(EBADF,SS$_IVCHAN);
     }
 
@@ -668,7 +781,7 @@ io_close(IO *io)
 }
 
 bool
-do_eof(GV *gv)
+Perl_do_eof(pTHX_ GV *gv)
 {
     dTHR;
     register IO *io;
@@ -678,6 +791,15 @@ do_eof(GV *gv)
 
     if (!io)
        return TRUE;
+    else if (ckWARN(WARN_IO)
+            && (IoTYPE(io) == '>' || IoIFP(io) == PerlIO_stdout()
+                || IoIFP(io) == PerlIO_stderr()))
+    {
+       SV* sv = sv_newmortal();
+       gv_efullname3(sv, gv, Nullch);
+       Perl_warner(aTHX_ WARN_IO, "Filehandle %s opened only for output",
+                   SvPV_nolen(sv));
+    }
 
     while (IoIFP(io)) {
 
@@ -705,8 +827,8 @@ do_eof(GV *gv)
     return TRUE;
 }
 
-long
-do_tell(GV *gv)
+Off_t
+Perl_do_tell(pTHX_ GV *gv)
 {
     register IO *io;
     register PerlIO *fp;
@@ -721,14 +843,14 @@ do_tell(GV *gv)
     {
        dTHR;
        if (ckWARN(WARN_UNOPENED))
-           warner(WARN_UNOPENED, "tell() on unopened file");
+           Perl_warner(aTHX_ WARN_UNOPENED, "tell() on unopened file");
     }
     SETERRNO(EBADF,RMS$_IFI);
-    return -1L;
+    return (Off_t)-1;
 }
 
 bool
-do_seek(GV *gv, long int pos, int whence)
+Perl_do_seek(pTHX_ GV *gv, Off_t pos, int whence)
 {
     register IO *io;
     register PerlIO *fp;
@@ -743,14 +865,14 @@ do_seek(GV *gv, long int pos, int whence)
     {
        dTHR;
        if (ckWARN(WARN_UNOPENED))
-           warner(WARN_UNOPENED, "seek() on unopened file");
+           Perl_warner(aTHX_ WARN_UNOPENED, "seek() on unopened file");
     }
     SETERRNO(EBADF,RMS$_IFI);
     return FALSE;
 }
 
-long
-do_sysseek(GV *gv, long int pos, int whence)
+Off_t
+Perl_do_sysseek(pTHX_ GV *gv, Off_t pos, int whence)
 {
     register IO *io;
     register PerlIO *fp;
@@ -760,19 +882,19 @@ do_sysseek(GV *gv, long int pos, int whence)
     {
        dTHR;
        if (ckWARN(WARN_UNOPENED))
-           warner(WARN_UNOPENED, "sysseek() on unopened file");
+           Perl_warner(aTHX_ WARN_UNOPENED, "sysseek() on unopened file");
     }
     SETERRNO(EBADF,RMS$_IFI);
-    return -1L;
+    return (Off_t)-1;
 }
 
 int
-do_binmode(PerlIO *fp, int iotype, int flag)
+Perl_do_binmode(pTHX_ PerlIO *fp, int iotype, int flag)
 {
     if (flag != TRUE)
-       croak("panic: unsetting binmode"); /* Not implemented yet */
+       Perl_croak(aTHX_ "panic: unsetting binmode"); /* Not implemented yet */
 #ifdef DOSISH
-#ifdef atarist
+#if defined(atarist) || defined(__MINT__)
     if (!PerlIO_flush(fp) && (fp->_flag |= _IOBIN))
        return 1;
     else
@@ -796,7 +918,7 @@ do_binmode(PerlIO *fp, int iotype, int flag)
 #endif
 #else
 #if defined(USEMYBINMODE)
-    if (my_binmode(fp,iotype) != NULL)
+    if (my_binmode(fp,iotype) != FALSE)
        return 1;
     else
        return 0;
@@ -858,7 +980,7 @@ Off_t length;               /* length to set file to */
 #endif /* F_FREESP */
 
 bool
-do_print(register SV *sv, PerlIO *fp)
+Perl_do_print(pTHX_ register SV *sv, PerlIO *fp)
 {
     register char *tmps;
     STRLEN len;
@@ -870,7 +992,7 @@ do_print(register SV *sv, PerlIO *fp)
        if (SvGMAGICAL(sv))
            mg_get(sv);
         if (SvIOK(sv) && SvIVX(sv) != 0) {
-           PerlIO_printf(fp, PL_ofmt, (double)SvIVX(sv));
+           PerlIO_printf(fp, PL_ofmt, (NV)SvIVX(sv));
            return !PerlIO_error(fp);
        }
        if (  (SvNOK(sv) && SvNVX(sv) != 0.0)
@@ -884,14 +1006,24 @@ do_print(register SV *sv, PerlIO *fp)
        {
            dTHR;
            if (ckWARN(WARN_UNINITIALIZED))
-               warner(WARN_UNINITIALIZED, warn_uninit);
+               Perl_warner(aTHX_ WARN_UNINITIALIZED, PL_warn_uninit);
        }
        return TRUE;
     case SVt_IV:
        if (SvIOK(sv)) {
            if (SvGMAGICAL(sv))
                mg_get(sv);
-           PerlIO_printf(fp, "%ld", (long)SvIVX(sv));
+#ifdef IV_IS_QUAD
+           if (SvIsUV(sv))
+               PerlIO_printf(fp, "%" PERL_PRIu64, (UV)SvUVX(sv));
+           else
+               PerlIO_printf(fp, "%" PERL_PRId64, (IV)SvIVX(sv));
+#else
+           if (SvIsUV(sv))
+               PerlIO_printf(fp, "%lu", (unsigned long)SvUVX(sv));
+           else
+               PerlIO_printf(fp, "%ld", (long)SvIVX(sv));
+#endif
            return !PerlIO_error(fp);
        }
        /* FALL THROUGH */
@@ -899,13 +1031,19 @@ do_print(register SV *sv, PerlIO *fp)
        tmps = SvPV(sv, len);
        break;
     }
+    /* To detect whether the process is about to overstep its
+     * filesize limit we would need getrlimit().  We could then
+     * also transparently raise the limit with setrlimit() --
+     * but only until the system hard limit/the filesystem limit,
+     * at which we would get EPERM.  Note that when using buffered
+     * io the write failure can be delayed until the flush/close. --jhi */
     if (len && (PerlIO_write(fp,tmps,len) == 0 || PerlIO_error(fp)))
        return FALSE;
     return !PerlIO_error(fp);
 }
 
 I32
-my_stat(ARGSproto)
+Perl_my_stat(pTHX)
 {
     djSP;
     IO *io;
@@ -926,7 +1064,7 @@ my_stat(ARGSproto)
            if (tmpgv == PL_defgv)
                return PL_laststatval;
            if (ckWARN(WARN_UNOPENED))
-               warner(WARN_UNOPENED, "Stat on unopened file <%s>",
+               Perl_warner(aTHX_ WARN_UNOPENED, "Stat on unopened file <%s>",
                  GvENAME(tmpgv));
            PL_statgv = Nullgv;
            sv_setpv(PL_statname,"");
@@ -936,6 +1074,7 @@ my_stat(ARGSproto)
     else {
        SV* sv = POPs;
        char *s;
+       STRLEN n_a;
        PUTBACK;
        if (SvTYPE(sv) == SVt_PVGV) {
            tmpgv = (GV*)sv;
@@ -946,52 +1085,57 @@ my_stat(ARGSproto)
            goto do_fstat;
        }
 
-       s = SvPV(sv, PL_na);
+       s = SvPV(sv, n_a);
        PL_statgv = Nullgv;
        sv_setpv(PL_statname, s);
        PL_laststype = OP_STAT;
        PL_laststatval = PerlLIO_stat(s, &PL_statcache);
        if (PL_laststatval < 0 && ckWARN(WARN_NEWLINE) && strchr(s, '\n'))
-           warner(WARN_NEWLINE, warn_nl, "stat");
+           Perl_warner(aTHX_ WARN_NEWLINE, PL_warn_nl, "stat");
        return PL_laststatval;
     }
 }
 
 I32
-my_lstat(ARGSproto)
+Perl_my_lstat(pTHX)
 {
     djSP;
     SV *sv;
+    STRLEN n_a;
     if (PL_op->op_flags & OPf_REF) {
        EXTEND(SP,1);
        if (cGVOP->op_gv == PL_defgv) {
            if (PL_laststype != OP_LSTAT)
-               croak("The stat preceding -l _ wasn't an lstat");
+               Perl_croak(aTHX_ "The stat preceding -l _ wasn't an lstat");
            return PL_laststatval;
        }
-       croak("You can't use -l on a filehandle");
+       Perl_croak(aTHX_ "You can't use -l on a filehandle");
     }
 
     PL_laststype = OP_LSTAT;
     PL_statgv = Nullgv;
     sv = POPs;
     PUTBACK;
-    sv_setpv(PL_statname,SvPV(sv, PL_na));
-#ifdef HAS_LSTAT
-    PL_laststatval = PerlLIO_lstat(SvPV(sv, PL_na),&PL_statcache);
-#else
-    PL_laststatval = PerlLIO_stat(SvPV(sv, PL_na),&PL_statcache);
-#endif
-    if (PL_laststatval < 0 && ckWARN(WARN_NEWLINE) && strchr(SvPV(sv, PL_na), '\n'))
-       warner(WARN_NEWLINE, warn_nl, "lstat");
+    sv_setpv(PL_statname,SvPV(sv, n_a));
+    PL_laststatval = PerlLIO_lstat(SvPV(sv, n_a),&PL_statcache);
+    if (PL_laststatval < 0 && ckWARN(WARN_NEWLINE) && strchr(SvPV(sv, n_a), '\n'))
+       Perl_warner(aTHX_ WARN_NEWLINE, PL_warn_nl, "lstat");
     return PL_laststatval;
 }
 
 bool
-do_aexec(SV *really, register SV **mark, register SV **sp)
+Perl_do_aexec(pTHX_ SV *really, register SV **mark, register SV **sp)
+{
+    return do_aexec5(really, mark, sp, 0, 0);
+}
+
+bool
+Perl_do_aexec5(pTHX_ SV *really, register SV **mark, register SV **sp,
+              int fd, int do_report)
 {
     register char **a;
     char *tmps;
+    STRLEN n_a;
 
     if (sp > mark) {
        dTHR;
@@ -999,27 +1143,33 @@ do_aexec(SV *really, register SV **mark, register SV **sp)
        a = PL_Argv;
        while (++mark <= sp) {
            if (*mark)
-               *a++ = SvPVx(*mark, PL_na);
+               *a++ = SvPVx(*mark, n_a);
            else
                *a++ = "";
        }
        *a = Nullch;
        if (*PL_Argv[0] != '/') /* will execvp use PATH? */
            TAINT_ENV();                /* testing IFS here is overkill, probably */
-       if (really && *(tmps = SvPV(really, PL_na)))
+       if (really && *(tmps = SvPV(really, n_a)))
            PerlProc_execvp(tmps,PL_Argv);
        else
            PerlProc_execvp(PL_Argv[0],PL_Argv);
        if (ckWARN(WARN_EXEC))
-           warner(WARN_EXEC, "Can't exec \"%s\": %s", 
+           Perl_warner(aTHX_ WARN_EXEC, "Can't exec \"%s\": %s", 
                PL_Argv[0], Strerror(errno));
+       if (do_report) {
+           int e = errno;
+
+           PerlLIO_write(fd, (void*)&e, sizeof(int));
+           PerlLIO_close(fd);
+       }
     }
     do_execfree();
     return FALSE;
 }
 
 void
-do_execfree(void)
+Perl_do_execfree(pTHX)
 {
     if (PL_Argv) {
        Safefree(PL_Argv);
@@ -1031,10 +1181,16 @@ do_execfree(void)
     }
 }
 
-#if !defined(OS2) && !defined(WIN32) && !defined(DJGPP)
+#if !defined(OS2) && !defined(WIN32) && !defined(DJGPP) && !defined(EPOC)
 
 bool
-do_exec(char *cmd)
+Perl_do_exec(pTHX_ char *cmd)
+{
+    return do_exec3(cmd,0,0);
+}
+
+bool
+Perl_do_exec3(pTHX_ char *cmd, int fd, int do_report)
 {
     register char **a;
     register char *s;
@@ -1090,6 +1246,20 @@ do_exec(char *cmd)
                *s = '\0';
                break;
            }
+           /* handle the 2>&1 construct at the end */
+           if (*s == '>' && s[1] == '&' && s[2] == '1'
+               && s > cmd + 1 && s[-1] == '2' && isSPACE(s[-2])
+               && (!s[3] || isSPACE(s[3])))
+           {
+               char *t = s + 3;
+
+               while (*t && isSPACE(*t))
+                   ++t;
+               if (!*t && (dup2(1,2) != -1)) {
+                   s[-2] = '\0';
+                   break;
+               }
+           }
          doshell:
            PerlProc_execl(PL_sh_path, "sh", "-c", cmd, (char*)0);
            return FALSE;
@@ -1116,9 +1286,15 @@ do_exec(char *cmd)
        }
        {
            dTHR;
+           int e = errno;
+
            if (ckWARN(WARN_EXEC))
-               warner(WARN_EXEC, "Can't exec \"%s\": %s", 
+               Perl_warner(aTHX_ WARN_EXEC, "Can't exec \"%s\": %s", 
                    PL_Argv[0], Strerror(errno));
+           if (do_report) {
+               PerlLIO_write(fd, (void*)&e, sizeof(int));
+               PerlLIO_close(fd);
+           }
        }
     }
     do_execfree();
@@ -1128,7 +1304,7 @@ do_exec(char *cmd)
 #endif /* OS2 || WIN32 */
 
 I32
-apply(I32 type, register SV **mark, register SV **sp)
+Perl_apply(pTHX_ I32 type, register SV **mark, register SV **sp)
 {
     dTHR;
     register I32 val;
@@ -1137,10 +1313,11 @@ apply(I32 type, register SV **mark, register SV **sp)
     char *what;
     char *s;
     SV **oldmark = mark;
+    STRLEN n_a;
 
 #define APPLY_TAINT_PROPER() \
     STMT_START {                                                       \
-       if (PL_tainting && PL_tainted) { goto taint_proper_label; }     \
+       if (PL_tainted) { TAINT_PROPER(what); }                         \
     } STMT_END
 
     /* This is a first heuristic; it doesn't catch tainting magic. */
@@ -1162,7 +1339,7 @@ apply(I32 type, register SV **mark, register SV **sp)
            APPLY_TAINT_PROPER();
            tot = sp - mark;
            while (++mark <= sp) {
-               char *name = SvPVx(*mark, PL_na);
+               char *name = SvPVx(*mark, n_a);
                APPLY_TAINT_PROPER();
                if (PerlLIO_chmod(name, val))
                    tot--;
@@ -1179,7 +1356,7 @@ apply(I32 type, register SV **mark, register SV **sp)
            APPLY_TAINT_PROPER();
            tot = sp - mark;
            while (++mark <= sp) {
-               char *name = SvPVx(*mark, PL_na);
+               char *name = SvPVx(*mark, n_a);
                APPLY_TAINT_PROPER();
                if (PerlLIO_chown(name, val, val2))
                    tot--;
@@ -1199,12 +1376,12 @@ nothing in the core.
        APPLY_TAINT_PROPER();
        if (mark == sp)
            break;
-       s = SvPVx(*++mark, PL_na);
+       s = SvPVx(*++mark, n_a);
        if (isUPPER(*s)) {
            if (*s == 'S' && s[1] == 'I' && s[2] == 'G')
                s += 3;
            if (!(val = whichsig(s)))
-               croak("Unrecognized signal name \"%s\"",s);
+               Perl_croak(aTHX_ "Unrecognized signal name \"%s\"",s);
        }
        else
            val = SvIVx(*mark);
@@ -1269,18 +1446,14 @@ nothing in the core.
        APPLY_TAINT_PROPER();
        tot = sp - mark;
        while (++mark <= sp) {
-           s = SvPVx(*mark, PL_na);
+           s = SvPVx(*mark, n_a);
            APPLY_TAINT_PROPER();
            if (PL_euid || PL_unsafe) {
                if (UNLINK(s))
                    tot--;
            }
            else {      /* don't let root wipe out directories without -U */
-#ifdef HAS_LSTAT
                if (PerlLIO_lstat(s,&PL_statbuf) < 0 || S_ISDIR(PL_statbuf.st_mode))
-#else
-               if (PerlLIO_stat(s,&PL_statbuf) < 0 || S_ISDIR(PL_statbuf.st_mode))
-#endif
                    tot--;
                else {
                    if (UNLINK(s))
@@ -1298,23 +1471,23 @@ nothing in the core.
            struct utimbuf utbuf;
 #else
            struct {
-               long    actime;
-               long    modtime;
+               Time_t  actime;
+               Time_t  modtime;
            } utbuf;
 #endif
 
            Zero(&utbuf, sizeof utbuf, char);
 #ifdef BIG_TIME
-           utbuf.actime = (Time_t)SvNVx(*++mark);    /* time accessed */
-           utbuf.modtime = (Time_t)SvNVx(*++mark);    /* time modified */
+           utbuf.actime = (Time_t)SvNVx(*++mark);      /* time accessed */
+           utbuf.modtime = (Time_t)SvNVx(*++mark);     /* time modified */
 #else
-           utbuf.actime = SvIVx(*++mark);    /* time accessed */
-           utbuf.modtime = SvIVx(*++mark);    /* time modified */
+           utbuf.actime = (Time_t)SvIVx(*++mark);      /* time accessed */
+           utbuf.modtime = (Time_t)SvIVx(*++mark);     /* time modified */
 #endif
            APPLY_TAINT_PROPER();
            tot = sp - mark;
            while (++mark <= sp) {
-               char *name = SvPVx(*mark, PL_na);
+               char *name = SvPVx(*mark, n_a);
                APPLY_TAINT_PROPER();
                if (PerlLIO_utime(name, &utbuf))
                    tot--;
@@ -1327,17 +1500,15 @@ nothing in the core.
     }
     return tot;
 
-  taint_proper_label:
-    TAINT_PROPER(what);
-    return 0;  /* this should never happen */
-
 #undef APPLY_TAINT_PROPER
 }
 
 /* Do the permissions allow some operation?  Assumes statcache already set. */
 #ifndef VMS /* VMS' cando is in vms.c */
-I32
-cando(I32 bit, I32 effective, register struct stat *statbufp)
+bool
+Perl_cando(pTHX_ Mode_t mode, Uid_t effective, register Stat_t *statbufp)
+/* Note: we use `effective' both for uids and gids.
+ * Here we are betting on Uid_t being equal or wider than Gid_t.  */
 {
 #ifdef DOSISH
     /* [Comments and code from Len Reed]
@@ -1361,11 +1532,11 @@ cando(I32 bit, I32 effective, register struct stat *statbufp)
      /* Atari stat() does pretty much the same thing. we set x_bit_set_in_stat
       * too so it will actually look into the files for magic numbers
       */
-     return (bit & statbufp->st_mode) ? TRUE : FALSE;
+     return (mode & statbufp->st_mode) ? TRUE : FALSE;
 
 #else /* ! DOSISH */
     if ((effective ? PL_euid : PL_uid) == 0) { /* root is special */
-       if (bit == S_IXUSR) {
+       if (mode == S_IXUSR) {
            if (statbufp->st_mode & 0111 || S_ISDIR(statbufp->st_mode))
                return TRUE;
        }
@@ -1374,22 +1545,22 @@ cando(I32 bit, I32 effective, register struct stat *statbufp)
        return FALSE;
     }
     if (statbufp->st_uid == (effective ? PL_euid : PL_uid) ) {
-       if (statbufp->st_mode & bit)
+       if (statbufp->st_mode & mode)
            return TRUE;        /* ok as "user" */
     }
-    else if (ingroup((I32)statbufp->st_gid,effective)) {
-       if (statbufp->st_mode & bit >> 3)
+    else if (ingroup(statbufp->st_gid,effective)) {
+       if (statbufp->st_mode & mode >> 3)
            return TRUE;        /* ok as "group" */
     }
-    else if (statbufp->st_mode & bit >> 6)
+    else if (statbufp->st_mode & mode >> 6)
        return TRUE;    /* ok as "other" */
     return FALSE;
 #endif /* ! DOSISH */
 }
 #endif /* ! VMS */
 
-I32
-ingroup(I32 testgid, I32 effective)
+bool
+Perl_ingroup(pTHX_ Gid_t testgid, Uid_t effective)
 {
     if (testgid == (effective ? PL_egid : PL_gid))
        return TRUE;
@@ -1413,7 +1584,7 @@ ingroup(I32 testgid, I32 effective)
 #if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
 
 I32
-do_ipcget(I32 optype, SV **mark, SV **sp)
+Perl_do_ipcget(pTHX_ I32 optype, SV **mark, SV **sp)
 {
     dTHR;
     key_t key;
@@ -1439,14 +1610,14 @@ do_ipcget(I32 optype, SV **mark, SV **sp)
 #endif
 #if !defined(HAS_MSG) || !defined(HAS_SEM) || !defined(HAS_SHM)
     default:
-       croak("%s not implemented", op_desc[optype]);
+       Perl_croak(aTHX_ "%s not implemented", PL_op_desc[optype]);
 #endif
     }
     return -1;                 /* should never happen */
 }
 
 I32
-do_ipcctl(I32 optype, SV **mark, SV **sp)
+Perl_do_ipcctl(pTHX_ I32 optype, SV **mark, SV **sp)
 {
     dTHR;
     SV *astr;
@@ -1477,6 +1648,7 @@ do_ipcctl(I32 optype, SV **mark, SV **sp)
 #endif
 #ifdef HAS_SEM
     case OP_SEMCTL:
+#ifdef Semctl
        if (cmd == IPC_STAT || cmd == IPC_SET)
            infosize = sizeof(struct semid_ds);
        else if (cmd == GETALL || cmd == SETALL)
@@ -1492,11 +1664,14 @@ do_ipcctl(I32 optype, SV **mark, SV **sp)
                /* "short" is technically wrong but much more portable
                   than guessing about u_?short(_t)? */
        }
+#else
+       Perl_croak(aTHX_ "%s not implemented", PL_op_desc[optype]);
+#endif
        break;
 #endif
 #if !defined(HAS_MSG) || !defined(HAS_SEM) || !defined(HAS_SHM)
     default:
-       croak("%s not implemented", op_desc[optype]);
+       Perl_croak(aTHX_ "%s not implemented", PL_op_desc[optype]);
 #endif
     }
 
@@ -1512,14 +1687,16 @@ do_ipcctl(I32 optype, SV **mark, SV **sp)
        {
            a = SvPV(astr, len);
            if (len != infosize)
-               croak("Bad arg length for %s, is %lu, should be %ld",
-                       op_desc[optype], (unsigned long)len, (long)infosize);
+               Perl_croak(aTHX_ "Bad arg length for %s, is %lu, should be %ld",
+                     PL_op_desc[optype],
+                     (unsigned long)len,
+                     (long)infosize);
        }
     }
     else
     {
        IV i = SvIV(astr);
-       a = (char *)i;          /* ouch */
+       a = INT2PTR(char *,i);          /* ouch */
     }
     SETERRNO(0,0);
     switch (optype)
@@ -1531,10 +1708,14 @@ do_ipcctl(I32 optype, SV **mark, SV **sp)
 #endif
 #ifdef HAS_SEM
     case OP_SEMCTL: {
+#ifdef Semctl
             union semun unsemds;
 
             unsemds.buf = (struct semid_ds *)a;
            ret = Semctl(id, n, cmd, unsemds);
+#else
+           Perl_croak(aTHX_ "%s not implemented", PL_op_desc[optype]);
+#endif
         }
        break;
 #endif
@@ -1553,7 +1734,7 @@ do_ipcctl(I32 optype, SV **mark, SV **sp)
 }
 
 I32
-do_msgsnd(SV **mark, SV **sp)
+Perl_do_msgsnd(pTHX_ SV **mark, SV **sp)
 {
 #ifdef HAS_MSG
     dTHR;
@@ -1567,16 +1748,16 @@ do_msgsnd(SV **mark, SV **sp)
     flags = SvIVx(*++mark);
     mbuf = SvPV(mstr, len);
     if ((msize = len - sizeof(long)) < 0)
-       croak("Arg too short for msgsnd");
+       Perl_croak(aTHX_ "Arg too short for msgsnd");
     SETERRNO(0,0);
     return msgsnd(id, (struct msgbuf *)mbuf, msize, flags);
 #else
-    croak("msgsnd not implemented");
+    Perl_croak(aTHX_ "msgsnd not implemented");
 #endif
 }
 
 I32
-do_msgrcv(SV **mark, SV **sp)
+Perl_do_msgrcv(pTHX_ SV **mark, SV **sp)
 {
 #ifdef HAS_MSG
     dTHR;
@@ -1591,12 +1772,6 @@ do_msgrcv(SV **mark, SV **sp)
     msize = SvIVx(*++mark);
     mtype = (long)SvIVx(*++mark);
     flags = SvIVx(*++mark);
-    if (SvTHINKFIRST(mstr)) {
-       if (SvREADONLY(mstr))
-           croak("Can't msgrcv to readonly var");
-       if (SvROK(mstr))
-           sv_unref(mstr);
-    }
     SvPV_force(mstr, len);
     mbuf = SvGROW(mstr, sizeof(long)+msize+1);
     
@@ -1608,12 +1783,12 @@ do_msgrcv(SV **mark, SV **sp)
     }
     return ret;
 #else
-    croak("msgrcv not implemented");
+    Perl_croak(aTHX_ "msgrcv not implemented");
 #endif
 }
 
 I32
-do_semop(SV **mark, SV **sp)
+Perl_do_semop(pTHX_ SV **mark, SV **sp)
 {
 #ifdef HAS_SEM
     dTHR;
@@ -1633,12 +1808,12 @@ do_semop(SV **mark, SV **sp)
     SETERRNO(0,0);
     return semop(id, (struct sembuf *)opbuf, opsize/sizeof(struct sembuf));
 #else
-    croak("semop not implemented");
+    Perl_croak(aTHX_ "semop not implemented");
 #endif
 }
 
 I32
-do_shmio(I32 optype, SV **mark, SV **sp)
+Perl_do_shmio(pTHX_ I32 optype, SV **mark, SV **sp)
 {
 #ifdef HAS_SHM
     dTHR;
@@ -1683,7 +1858,7 @@ do_shmio(I32 optype, SV **mark, SV **sp)
     }
     return shmdt(shm);
 #else
-    croak("shm I/O not implemented");
+    Perl_croak(aTHX_ "shm I/O not implemented");
 #endif
 }