X-Git-Url: https://perl5.git.perl.org/perl5.git/blobdiff_plain/5d37acd6b65eb421e938a3fde62cc1edde467dae..c297b6b77b7da914f12e3a99a22be9cf449a5b78:/doio.c diff --git a/doio.c b/doio.c index 4c929b1..56615cd 100644 --- a/doio.c +++ b/doio.c @@ -60,81 +60,340 @@ #include -bool -Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, - int rawmode, int rawperm, PerlIO *supplied_fp, SV **svp, - I32 num_svs) +void +Perl_setfd_cloexec(pTHX_ int fd) +{ + assert(fd >= 0); +#if defined(HAS_FCNTL) && defined(F_SETFD) && defined(FD_CLOEXEC) + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif +} + +void +Perl_setfd_inhexec(pTHX_ int fd) +{ + assert(fd >= 0); +#if defined(HAS_FCNTL) && defined(F_SETFD) && defined(FD_CLOEXEC) + (void) fcntl(fd, F_SETFD, 0); +#endif +} + +void +Perl_setfd_inhexec_for_sysfd(pTHX_ int fd) +{ + assert(fd >= 0); + if(fd <= PL_maxsysfd) + setfd_inhexec(fd); +} + +#define DO_GENOPEN_THEN_CLOEXEC(GENOPEN_NORMAL, GENSETFD_CLOEXEC) \ + do { \ + int res = (GENOPEN_NORMAL); \ + if(LIKELY(res != -1)) GENSETFD_CLOEXEC; \ + return res; \ + } while(0) +#if defined(HAS_FCNTL) && defined(F_SETFD) && defined(FD_CLOEXEC) && \ + defined(F_GETFD) +enum { CLOEXEC_EXPERIMENT, CLOEXEC_AT_OPEN, CLOEXEC_AFTER_OPEN }; +# define DO_GENOPEN_EXPERIMENTING_CLOEXEC(TESTFD, GENOPEN_CLOEXEC, \ + GENOPEN_NORMAL, GENSETFD_CLOEXEC) \ + do { \ + static int strategy = CLOEXEC_EXPERIMENT; \ + switch (strategy) { \ + case CLOEXEC_EXPERIMENT: default: { \ + int res = (GENOPEN_CLOEXEC), eno; \ + if (LIKELY(res != -1)) { \ + int fdflags = fcntl((TESTFD), F_GETFD); \ + if (LIKELY(fdflags != -1) && \ + LIKELY(fdflags & FD_CLOEXEC)) { \ + strategy = CLOEXEC_AT_OPEN; \ + } else { \ + strategy = CLOEXEC_AFTER_OPEN; \ + GENSETFD_CLOEXEC; \ + } \ + } else if (UNLIKELY((eno = errno) == EINVAL || \ + eno == ENOSYS)) { \ + res = (GENOPEN_NORMAL); \ + if (LIKELY(res != -1)) { \ + strategy = CLOEXEC_AFTER_OPEN; \ + GENSETFD_CLOEXEC; \ + } else if (!LIKELY((eno = errno) == EINVAL || \ + eno == ENOSYS)) { \ + strategy = CLOEXEC_AFTER_OPEN; \ + } \ + } \ + return res; \ + } \ + case CLOEXEC_AT_OPEN: \ + return (GENOPEN_CLOEXEC); \ + case CLOEXEC_AFTER_OPEN: \ + DO_GENOPEN_THEN_CLOEXEC(GENOPEN_NORMAL, GENSETFD_CLOEXEC); \ + } \ + } while(0) +#else +# define DO_GENOPEN_EXPERIMENTING_CLOEXEC(TESTFD, GENOPEN_CLOEXEC, \ + GENOPEN_NORMAL, GENSETFD_CLOEXEC) \ + DO_GENOPEN_THEN_CLOEXEC(GENOPEN_NORMAL, GENSETFD_CLOEXEC) +#endif + +#define DO_ONEOPEN_THEN_CLOEXEC(ONEOPEN_NORMAL) \ + do { \ + int fd; \ + DO_GENOPEN_THEN_CLOEXEC(fd = (ONEOPEN_NORMAL), \ + setfd_cloexec(fd)); \ + } while(0) +#define DO_ONEOPEN_EXPERIMENTING_CLOEXEC(ONEOPEN_CLOEXEC, ONEOPEN_NORMAL) \ + do { \ + int fd; \ + DO_GENOPEN_EXPERIMENTING_CLOEXEC(fd, fd = (ONEOPEN_CLOEXEC), \ + fd = (ONEOPEN_NORMAL), setfd_cloexec(fd)); \ + } while(0) + +#define DO_PIPESETFD_CLOEXEC(PIPEFD) \ + do { \ + setfd_cloexec((PIPEFD)[0]); \ + setfd_cloexec((PIPEFD)[1]); \ + } while(0) +#define DO_PIPEOPEN_THEN_CLOEXEC(PIPEFD, PIPEOPEN_NORMAL) \ + DO_GENOPEN_THEN_CLOEXEC(PIPEOPEN_NORMAL, DO_PIPESETFD_CLOEXEC(PIPEFD)) +#define DO_PIPEOPEN_EXPERIMENTING_CLOEXEC(PIPEFD, PIPEOPEN_CLOEXEC, \ + PIPEOPEN_NORMAL) \ + DO_GENOPEN_EXPERIMENTING_CLOEXEC((PIPEFD)[0], PIPEOPEN_CLOEXEC, \ + PIPEOPEN_NORMAL, DO_PIPESETFD_CLOEXEC(PIPEFD)) + +int +Perl_PerlLIO_dup_cloexec(pTHX_ int oldfd) +{ +#if !defined(PERL_IMPLICIT_SYS) && defined(F_DUPFD_CLOEXEC) + /* + * struct IPerlLIO doesn't cover fcntl(), and there's no clear way + * to extend it, so for the time being this just isn't available on + * PERL_IMPLICIT_SYS builds. + */ + DO_ONEOPEN_EXPERIMENTING_CLOEXEC( + fcntl(oldfd, F_DUPFD_CLOEXEC, 0), + PerlLIO_dup(oldfd)); +#else + DO_ONEOPEN_THEN_CLOEXEC(PerlLIO_dup(oldfd)); +#endif +} + +int +Perl_PerlLIO_dup2_cloexec(pTHX_ int oldfd, int newfd) +{ +#if !defined(PERL_IMPLICIT_SYS) && defined(HAS_DUP3) && defined(O_CLOEXEC) + /* + * struct IPerlLIO doesn't cover dup3(), and there's no clear way + * to extend it, so for the time being this just isn't available on + * PERL_IMPLICIT_SYS builds. + */ + DO_ONEOPEN_EXPERIMENTING_CLOEXEC( + dup3(oldfd, newfd, O_CLOEXEC), + PerlLIO_dup2(oldfd, newfd)); +#else + DO_ONEOPEN_THEN_CLOEXEC(PerlLIO_dup2(oldfd, newfd)); +#endif +} + +int +Perl_PerlLIO_open_cloexec(pTHX_ const char *file, int flag) +{ + PERL_ARGS_ASSERT_PERLLIO_OPEN_CLOEXEC; +#if defined(O_CLOEXEC) + DO_ONEOPEN_EXPERIMENTING_CLOEXEC( + PerlLIO_open(file, flag | O_CLOEXEC), + PerlLIO_open(file, flag)); +#else + DO_ONEOPEN_THEN_CLOEXEC(PerlLIO_open(file, flag)); +#endif +} + +int +Perl_PerlLIO_open3_cloexec(pTHX_ const char *file, int flag, int perm) +{ + PERL_ARGS_ASSERT_PERLLIO_OPEN3_CLOEXEC; +#if defined(O_CLOEXEC) + DO_ONEOPEN_EXPERIMENTING_CLOEXEC( + PerlLIO_open3(file, flag | O_CLOEXEC, perm), + PerlLIO_open3(file, flag, perm)); +#else + DO_ONEOPEN_THEN_CLOEXEC(PerlLIO_open3(file, flag, perm)); +#endif +} + +#ifdef HAS_PIPE +int +Perl_PerlProc_pipe_cloexec(pTHX_ int *pipefd) +{ + PERL_ARGS_ASSERT_PERLPROC_PIPE_CLOEXEC; + /* + * struct IPerlProc doesn't cover pipe2(), and there's no clear way + * to extend it, so for the time being this just isn't available on + * PERL_IMPLICIT_SYS builds. + */ +# if !defined(PERL_IMPLICIT_SYS) && defined(HAS_PIPE2) && defined(O_CLOEXEC) + DO_PIPEOPEN_EXPERIMENTING_CLOEXEC(pipefd, + pipe2(pipefd, O_CLOEXEC), + PerlProc_pipe(pipefd)); +# else + DO_PIPEOPEN_THEN_CLOEXEC(pipefd, PerlProc_pipe(pipefd)); +# endif +} +#endif + +#ifdef HAS_SOCKET + +int +Perl_PerlSock_socket_cloexec(pTHX_ int domain, int type, int protocol) +{ +# if defined(SOCK_CLOEXEC) + DO_ONEOPEN_EXPERIMENTING_CLOEXEC( + PerlSock_socket(domain, type | SOCK_CLOEXEC, protocol), + PerlSock_socket(domain, type, protocol)); +# else + DO_ONEOPEN_THEN_CLOEXEC(PerlSock_socket(domain, type, protocol)); +# endif +} + +int +Perl_PerlSock_accept_cloexec(pTHX_ int listenfd, struct sockaddr *addr, + Sock_size_t *addrlen) +{ +# if !defined(PERL_IMPLICIT_SYS) && \ + defined(HAS_ACCEPT4) && defined(SOCK_CLOEXEC) + /* + * struct IPerlSock doesn't cover accept4(), and there's no clear + * way to extend it, so for the time being this just isn't available + * on PERL_IMPLICIT_SYS builds. + */ + DO_ONEOPEN_EXPERIMENTING_CLOEXEC( + accept4(listenfd, addr, addrlen, SOCK_CLOEXEC), + PerlSock_accept(listenfd, addr, addrlen)); +# else + DO_ONEOPEN_THEN_CLOEXEC(PerlSock_accept(listenfd, addr, addrlen)); +# endif +} + +#endif + +#if defined (HAS_SOCKETPAIR) || \ + (defined (HAS_SOCKET) && defined(SOCK_DGRAM) && \ + defined(AF_INET) && defined(PF_INET)) +int +Perl_PerlSock_socketpair_cloexec(pTHX_ int domain, int type, int protocol, + int *pairfd) +{ + PERL_ARGS_ASSERT_PERLSOCK_SOCKETPAIR_CLOEXEC; +# ifdef SOCK_CLOEXEC + DO_PIPEOPEN_EXPERIMENTING_CLOEXEC(pairfd, + PerlSock_socketpair(domain, type | SOCK_CLOEXEC, protocol, pairfd), + PerlSock_socketpair(domain, type, protocol, pairfd)); +# else + DO_PIPEOPEN_THEN_CLOEXEC(pairfd, + PerlSock_socketpair(domain, type, protocol, pairfd)); +# endif +} +#endif + +static IO * +S_openn_setup(pTHX_ GV *gv, char *mode, PerlIO **saveifp, PerlIO **saveofp, + int *savefd, char *savetype) { - dVAR; IO * const io = GvIOn(gv); - PerlIO *saveifp = NULL; - PerlIO *saveofp = NULL; - int savefd = -1; - char savetype = IoTYPE_CLOSED; - int writing = 0; - PerlIO *fp; - int fd; - int result; - bool was_fdopen = FALSE; - bool in_raw = 0, in_crlf = 0, out_raw = 0, out_crlf = 0; - char *type = NULL; - char mode[PERL_MODE_MAX]; /* file mode ("r\0", "rb\0", "ab\0" etc.) */ - SV *namesv; - PERL_ARGS_ASSERT_DO_OPENN; + PERL_ARGS_ASSERT_OPENN_SETUP; + + *saveifp = NULL; + *saveofp = NULL; + *savefd = -1; + *savetype = IoTYPE_CLOSED; Zero(mode,sizeof(mode),char); PL_forkprocess = 1; /* assume true if no fork */ - /* Collect default raw/crlf info from the op */ - if (PL_op && PL_op->op_type == OP_OPEN) { - /* set up IO layers */ - const U8 flags = PL_op->op_private; - in_raw = (flags & OPpOPEN_IN_RAW); - in_crlf = (flags & OPpOPEN_IN_CRLF); - out_raw = (flags & OPpOPEN_OUT_RAW); - out_crlf = (flags & OPpOPEN_OUT_CRLF); - } - /* If currently open - close before we re-open */ if (IoIFP(io)) { - fd = PerlIO_fileno(IoIFP(io)); if (IoTYPE(io) == IoTYPE_STD) { /* This is a clone of one of STD* handles */ - result = 0; - } - else if (fd >= 0 && fd <= PL_maxsysfd) { - /* This is one of the original STD* handles */ - saveifp = IoIFP(io); - saveofp = IoOFP(io); - savetype = IoTYPE(io); - savefd = fd; - result = 0; - } - else if (IoTYPE(io) == IoTYPE_PIPE) - result = PerlProc_pclose(IoIFP(io)); - else if (IoIFP(io) != IoOFP(io)) { - if (IoOFP(io)) { - result = PerlIO_close(IoOFP(io)); - PerlIO_close(IoIFP(io)); /* clear stdio, fd already closed */ - } - else - result = PerlIO_close(IoIFP(io)); - } - else - result = PerlIO_close(IoIFP(io)); - if (result == EOF && fd > PL_maxsysfd) { - /* Why is this not Perl_warn*() call ? */ - PerlIO_printf(Perl_error_log, - "Warning: unable to close filehandle %"HEKf" properly.\n", - HEKfARG(GvENAME_HEK(gv)) - ); } + else { + const int old_fd = PerlIO_fileno(IoIFP(io)); + + if (old_fd >= 0 && old_fd <= PL_maxsysfd) { + /* This is one of the original STD* handles */ + *saveifp = IoIFP(io); + *saveofp = IoOFP(io); + *savetype = IoTYPE(io); + *savefd = old_fd; + } + else { + int result; + + if (IoTYPE(io) == IoTYPE_PIPE) + result = PerlProc_pclose(IoIFP(io)); + else if (IoIFP(io) != IoOFP(io)) { + if (IoOFP(io)) { + result = PerlIO_close(IoOFP(io)); + PerlIO_close(IoIFP(io)); /* clear stdio, fd already closed */ + } + else + result = PerlIO_close(IoIFP(io)); + } + else + result = PerlIO_close(IoIFP(io)); + + if (result == EOF && old_fd > PL_maxsysfd) { + /* Why is this not Perl_warn*() call ? */ + PerlIO_printf(Perl_error_log, + "Warning: unable to close filehandle %" HEKf + " properly.\n", + HEKfARG(GvENAME_HEK(gv)) + ); + } + } + } IoOFP(io) = IoIFP(io) = NULL; } + return io; +} + +bool +Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, + int rawmode, int rawperm, PerlIO *supplied_fp, SV **svp, + I32 num_svs) +{ + PERL_ARGS_ASSERT_DO_OPENN; if (as_raw) { /* sysopen style args, i.e. integer mode and permissions */ + + if (num_svs != 0) { + Perl_croak(aTHX_ "panic: sysopen with multiple args, num_svs=%ld", + (long) num_svs); + } + return do_open_raw(gv, oname, len, rawmode, rawperm, NULL); + } + return do_open6(gv, oname, len, supplied_fp, svp, num_svs); +} + +bool +Perl_do_open_raw(pTHX_ GV *gv, const char *oname, STRLEN len, + int rawmode, int rawperm, Stat_t *statbufp) +{ + PerlIO *saveifp; + PerlIO *saveofp; + int savefd; + char savetype; + char mode[PERL_MODE_MAX]; /* file mode ("r\0", "rb\0", "ab\0" etc.) */ + IO * const io = openn_setup(gv, mode, &saveifp, &saveofp, &savefd, &savetype); + int writing = 0; + PerlIO *fp; + + PERL_ARGS_ASSERT_DO_OPEN_RAW; + + /* For ease of blame back to 5.000, keep the existing indenting. */ + { + /* sysopen style args, i.e. integer mode and permissions */ STRLEN ix = 0; const int appendtrunc = 0 @@ -147,11 +406,8 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, ; const int modifyingmode = O_WRONLY|O_RDWR|O_CREAT|appendtrunc; int ismodifying; + SV *namesv; - if (num_svs != 0) { - Perl_croak(aTHX_ "panic: sysopen with multiple args, num_svs=%ld", - (long) num_svs); - } /* It's not always O_RDONLY 0 @@ -183,17 +439,47 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, IoTYPE(io) = PerlIO_intmode2str(rawmode, &mode[ix], &writing); namesv = newSVpvn_flags(oname, len, SVs_TEMP); - num_svs = 1; - svp = &namesv; - type = NULL; - fp = PerlIO_openn(aTHX_ type, mode, -1, rawmode, rawperm, NULL, num_svs, svp); + fp = PerlIO_openn(aTHX_ NULL, mode, -1, rawmode, rawperm, NULL, 1, &namesv); } - else { + return openn_cleanup(gv, io, fp, mode, oname, saveifp, saveofp, savefd, + savetype, writing, 0, NULL, statbufp); +} + +bool +Perl_do_open6(pTHX_ GV *gv, const char *oname, STRLEN len, + PerlIO *supplied_fp, SV **svp, U32 num_svs) +{ + PerlIO *saveifp; + PerlIO *saveofp; + int savefd; + char savetype; + char mode[PERL_MODE_MAX]; /* file mode ("r\0", "rb\0", "ab\0" etc.) */ + IO * const io = openn_setup(gv, mode, &saveifp, &saveofp, &savefd, &savetype); + int writing = 0; + PerlIO *fp; + bool was_fdopen = FALSE; + char *type = NULL; + + PERL_ARGS_ASSERT_DO_OPEN6; + + /* For ease of blame back to 5.000, keep the existing indenting. */ + { /* Regular (non-sys) open */ char *name; STRLEN olen = len; char *tend; int dodup = 0; + bool in_raw = 0, in_crlf = 0, out_raw = 0, out_crlf = 0; + + /* Collect default raw/crlf info from the op */ + if (PL_op && PL_op->op_type == OP_OPEN) { + /* set up IO layers */ + const U8 flags = PL_op->op_private; + in_raw = (flags & OPpOPEN_IN_RAW); + in_crlf = (flags & OPpOPEN_IN_CRLF); + out_raw = (flags & OPpOPEN_OUT_RAW); + out_crlf = (flags & OPpOPEN_OUT_CRLF); + } type = savepvn(oname, len); tend = type+len; @@ -210,18 +496,21 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, STRLEN nlen = 0; /* New style explicit name, type is just mode and layer info */ #ifdef USE_STDIO - if (SvROK(*svp) && !strchr(oname,'&')) { + if (SvROK(*svp) && !memchr(oname, '&', len)) { if (ckWARN(WARN_IO)) Perl_warner(aTHX_ packWARN(WARN_IO), "Can't open a reference"); SETERRNO(EINVAL, LIB_INVARG); + fp = NULL; goto say_false; } #endif /* USE_STDIO */ p = (SvOK(*svp) || SvGMAGICAL(*svp)) ? SvPV(*svp, nlen) : NULL; - if (p && !IS_SAFE_PATHNAME(p, nlen, "open")) + if (p && !IS_SAFE_PATHNAME(p, nlen, "open")) { + fp = NULL; goto say_false; + } name = p ? savepvn(p, nlen) : savepvs(""); @@ -260,6 +549,7 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, if (ckWARN(WARN_PIPE)) Perl_warner(aTHX_ packWARN(WARN_PIPE), "Missing command in piped open"); errno = EPIPE; + fp = NULL; goto say_false; } if (!(*name == '-' && name[1] == '\0') || num_svs) @@ -285,6 +575,7 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, if (num_svs) { if (*type) { if (PerlIO_apply_layers(aTHX_ fp, mode, type) != 0) { + fp = NULL; goto say_false; } } @@ -321,6 +612,8 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, } else { PerlIO *that_fp = NULL; + int wanted_fd; + UV uv; if (num_svs > 1) { /* diag_listed_as: More than one argument to '%s' open */ Perl_croak(aTHX_ "More than one argument to '%c&' open",IoTYPE(io)); @@ -331,11 +624,14 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, SvIOK(*svp) || (SvPOKp(*svp) && looks_like_number(*svp)) )) { - fd = SvUV(*svp); + wanted_fd = SvUV(*svp); num_svs = 0; } - else if (isDIGIT(*type)) { - fd = atoi(type); + else if (isDIGIT(*type) + && grok_atoUV(type, &uv, NULL) + && uv <= INT_MAX + ) { + wanted_fd = (int)uv; } else { const IO* thatio; @@ -351,6 +647,7 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, #ifdef EINVAL SETERRNO(EINVAL,SS_IVCHAN); #endif + fp = NULL; goto say_false; } if ((that_fp = IoIFP(thatio))) { @@ -359,19 +656,12 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, * be optimized away on most platforms; * only Solaris and Linux seem to flush * on that. --jhi */ -#ifdef USE_SFIO - /* sfio fails to clear error on next - sfwrite, contrary to documentation. - -- Nicholas Clark */ - if (PerlIO_seek(that_fp, 0, SEEK_CUR) == -1) - PerlIO_clearerr(that_fp); -#endif /* 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(that_fp); - fd = PerlIO_fileno(that_fp); + wanted_fd = PerlIO_fileno(that_fp); /* When dup()ing STDIN, STDOUT or STDERR * explicitly set appropriate access mode */ if (that_fp == PerlIO_stdout() @@ -384,8 +674,11 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, else if (IoTYPE(thatio) == IoTYPE_SOCKET) IoTYPE(io) = IoTYPE_SOCKET; } - else - fd = -1; + else { + SETERRNO(EBADF, RMS_IFI); + fp = NULL; + goto say_false; + } } if (!num_svs) type = NULL; @@ -394,12 +687,12 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, } else { if (dodup) - fd = PerlLIO_dup(fd); + wanted_fd = PerlLIO_dup(wanted_fd); else was_fdopen = TRUE; - if (!(fp = PerlIO_openn(aTHX_ type,mode,fd,0,0,NULL,num_svs,svp))) { - if (dodup && fd >= 0) - PerlLIO_close(fd); + if (!(fp = PerlIO_openn(aTHX_ type,mode,wanted_fd,0,0,NULL,num_svs,svp))) { + if (dodup && wanted_fd >= 0) + PerlLIO_close(wanted_fd); } } } @@ -417,13 +710,14 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, } } else { - if (!num_svs) { - namesv = newSVpvn_flags(type, tend - type, SVs_TEMP); - num_svs = 1; - svp = &namesv; + if (num_svs) { + fp = PerlIO_openn(aTHX_ type,mode,-1,0,0,NULL,num_svs,svp); + } + else { + SV *namesv = newSVpvn_flags(type, tend - type, SVs_TEMP); type = NULL; + fp = PerlIO_openn(aTHX_ type,mode,-1,0,0,NULL,1,&namesv); } - fp = PerlIO_openn(aTHX_ type,mode,-1,0,0,NULL,num_svs,svp); } } /* !& */ if (!fp && type && *type && *type != ':' && !isIDFIRST(*type)) @@ -451,13 +745,14 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, } } else { - if (!num_svs) { - namesv = newSVpvn_flags(type, tend - type, SVs_TEMP); - num_svs = 1; - svp = &namesv; + if (num_svs) { + fp = PerlIO_openn(aTHX_ type,mode,-1,0,0,NULL,num_svs,svp); + } + else { + SV *namesv = newSVpvn_flags(type, tend - type, SVs_TEMP); type = NULL; + fp = PerlIO_openn(aTHX_ type,mode,-1,0,0,NULL,1,&namesv); } - fp = PerlIO_openn(aTHX_ type,mode,-1,0,0,NULL,num_svs,svp); } if (!fp && type && *type && *type != ':' && !isIDFIRST(*type)) goto unknown_open_mode; @@ -482,6 +777,7 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, if (ckWARN(WARN_PIPE)) Perl_warner(aTHX_ packWARN(WARN_PIPE), "Missing command in piped open"); errno = EPIPE; + fp = NULL; goto say_false; } if (!(*name == '-' && name[1] == '\0') || num_svs) @@ -506,6 +802,7 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, type++; if (*type) { if (PerlIO_apply_layers(aTHX_ fp, mode, type) != 0) { + fp = NULL; goto say_false; } } @@ -530,25 +827,46 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, IoTYPE(io) = IoTYPE_STD; } else { - if (!num_svs) { - namesv = newSVpvn_flags(type, tend - type, SVs_TEMP); - num_svs = 1; - svp = &namesv; + if (num_svs) { + fp = PerlIO_openn(aTHX_ type,mode,-1,0,0,NULL,num_svs,svp); + } + else { + SV *namesv = newSVpvn_flags(type, tend - type, SVs_TEMP); type = NULL; + fp = PerlIO_openn(aTHX_ type,mode,-1,0,0,NULL,1,&namesv); } - fp = PerlIO_openn(aTHX_ type,mode,-1,0,0,NULL,num_svs,svp); } } } + + say_false: + return openn_cleanup(gv, io, fp, mode, oname, saveifp, saveofp, savefd, + savetype, writing, was_fdopen, type, NULL); +} + +/* Yes, this is ugly, but it's private, and I don't see a cleaner way to + simplify the two-headed public interface of do_openn. */ +static bool +S_openn_cleanup(pTHX_ GV *gv, IO *io, PerlIO *fp, char *mode, const char *oname, + PerlIO *saveifp, PerlIO *saveofp, int savefd, char savetype, + int writing, bool was_fdopen, const char *type, Stat_t *statbufp) +{ + int fd; + Stat_t statbuf; + + PERL_ARGS_ASSERT_OPENN_CLEANUP; + + Zero(&statbuf, 1, Stat_t); + if (!fp) { if (IoTYPE(io) == IoTYPE_RDONLY && ckWARN(WARN_NEWLINE) - && strchr(oname, '\n') + && should_warn_nl(oname) ) { - GCC_DIAG_IGNORE(-Wformat-nonliteral); /* PL_warn_nl is constant */ + GCC_DIAG_IGNORE_STMT(-Wformat-nonliteral); /* PL_warn_nl is constant */ Perl_warner(aTHX_ packWARN(WARN_NEWLINE), PL_warn_nl, "open"); - GCC_DIAG_RESTORE; + GCC_DIAG_RESTORE_STMT; } goto say_false; } @@ -557,40 +875,36 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, if ((IoTYPE(io) == IoTYPE_RDONLY) && (fp == PerlIO_stdout() || fp == PerlIO_stderr())) { Perl_warner(aTHX_ packWARN(WARN_IO), - "Filehandle STD%s reopened as %"HEKf + "Filehandle STD%s reopened as %" HEKf " only for input", ((fp == PerlIO_stdout()) ? "OUT" : "ERR"), HEKfARG(GvENAME_HEK(gv))); } else if ((IoTYPE(io) == IoTYPE_WRONLY) && fp == PerlIO_stdin()) { Perl_warner(aTHX_ packWARN(WARN_IO), - "Filehandle STDIN reopened as %"HEKf" only for output", + "Filehandle STDIN reopened as %" HEKf " only for output", HEKfARG(GvENAME_HEK(gv)) ); } } fd = PerlIO_fileno(fp); - /* If there is no fd (e.g. PerlIO::scalar) assume it isn't a - * socket - this covers PerlIO::scalar - otherwise unless we "know" the - * type probe for socket-ness. + /* Do NOT do: "if (fd < 0) goto say_false;" here. If there is no + * fd assume it isn't a socket - this covers PerlIO::scalar - + * otherwise unless we "know" the type probe for socket-ness. */ if (IoTYPE(io) && IoTYPE(io) != IoTYPE_PIPE && IoTYPE(io) != IoTYPE_STD && fd >= 0) { - if (PerlLIO_fstat(fd,&PL_statbuf) < 0) { + if (PerlLIO_fstat(fd,&statbuf) < 0) { /* If PerlIO claims to have fd we had better be able to fstat() it. */ (void) PerlIO_close(fp); goto say_false; } #ifndef PERL_MICRO - if (S_ISSOCK(PL_statbuf.st_mode)) + if (S_ISSOCK(statbuf.st_mode)) IoTYPE(io) = IoTYPE_SOCKET; /* in case a socket was passed in to us */ #ifdef HAS_SOCKET else if ( -#ifdef S_IFMT - !(PL_statbuf.st_mode & S_IFMT) -#else - !PL_statbuf.st_mode -#endif + !(statbuf.st_mode & S_IFMT) && IoTYPE(io) != IoTYPE_WRONLY /* Dups of STD* filehandles already have */ && IoTYPE(io) != IoTYPE_RDONLY /* type so they aren't marked as sockets */ ) { /* on OS's that return 0 on fstat()ed pipe */ @@ -625,7 +939,10 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, is assigned to (say) STDOUT - for now let dup2() fail and provide the error */ - if (PerlLIO_dup2(fd, savefd) < 0) { + if (fd < 0) { + SETERRNO(EBADF,RMS_IFI); + goto say_false; + } else if (PerlLIO_dup2(fd, savefd) < 0) { (void)PerlIO_close(fp); goto say_false; } @@ -661,16 +978,32 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, if (was_fdopen) { /* need to close fp without closing underlying fd */ int ofd = PerlIO_fileno(fp); - int dupfd = PerlLIO_dup(ofd); + int dupfd = ofd >= 0 ? PerlLIO_dup(ofd) : -1; #if defined(HAS_FCNTL) && defined(F_SETFD) - /* Assume if we have F_SETFD we have F_GETFD */ - int coe = fcntl(ofd,F_GETFD); + /* Assume if we have F_SETFD we have F_GETFD. */ + /* Get a copy of all the fd flags. */ + int fd_flags = ofd >= 0 ? fcntl(ofd, F_GETFD) : -1; + if (fd_flags < 0) { + if (dupfd >= 0) + PerlLIO_close(dupfd); + goto say_false; + } #endif + if (ofd < 0 || dupfd < 0) { + if (dupfd >= 0) + PerlLIO_close(dupfd); + goto say_false; + } PerlIO_close(fp); - PerlLIO_dup2(dupfd,ofd); + PerlLIO_dup2(dupfd, ofd); #if defined(HAS_FCNTL) && defined(F_SETFD) - /* The dup trick has lost close-on-exec on ofd */ - fcntl(ofd,F_SETFD, coe); + /* The dup trick has lost close-on-exec on ofd, + * and possibly any other flags, so restore them. */ + if (fcntl(ofd,F_SETFD, fd_flags) < 0) { + if (dupfd >= 0) + PerlLIO_close(dupfd); + goto say_false; + } #endif PerlLIO_close(dupfd); } @@ -681,59 +1014,241 @@ Perl_do_openn(pTHX_ GV *gv, const char *oname, I32 len, int as_raw, PerlIO_clearerr(fp); fd = PerlIO_fileno(fp); } -#if defined(HAS_FCNTL) && defined(F_SETFD) if (fd >= 0) { - dSAVE_ERRNO; - fcntl(fd,F_SETFD,fd > PL_maxsysfd); /* can change errno */ - RESTORE_ERRNO; + setfd_cloexec(fd); + setfd_inhexec_for_sysfd(fd); } -#endif IoIFP(io) = fp; IoFLAGS(io) &= ~IOf_NOLINE; if (writing) { if (IoTYPE(io) == IoTYPE_SOCKET - || (IoTYPE(io) == IoTYPE_WRONLY && fd >= 0 && S_ISCHR(PL_statbuf.st_mode)) ) { + || (IoTYPE(io) == IoTYPE_WRONLY && fd >= 0 && S_ISCHR(statbuf.st_mode)) ) { char *s = mode; if (*s == IoTYPE_IMPLICIT || *s == IoTYPE_NUMERIC) s++; *s = 'w'; - if (!(IoOFP(io) = PerlIO_openn(aTHX_ type,s,fd,0,0,NULL,0,svp))) { + if (!(IoOFP(io) = PerlIO_openn(aTHX_ type,s,fd,0,0,NULL,0,NULL))) { PerlIO_close(fp); - IoIFP(io) = NULL; goto say_false; } } else IoOFP(io) = fp; } + if (statbufp) + *statbufp = statbuf; + return TRUE; -say_false: + say_false: IoIFP(io) = saveifp; IoOFP(io) = saveofp; IoTYPE(io) = savetype; return FALSE; } +/* Open a temp file in the same directory as an original name. +*/ + +static bool +S_openindirtemp(pTHX_ GV *gv, SV *orig_name, SV *temp_out_name) { + int fd; + PerlIO *fp; + const char *p = SvPV_nolen(orig_name); + const char *sep; + + /* look for the last directory separator */ + sep = strrchr(p, '/'); + +#ifdef DOSISH + { + const char *sep2; + if ((sep2 = strrchr(sep ? sep : p, '\\'))) + sep = sep2; + } +#endif +#ifdef VMS + if (!sep) { + const char *openp = strchr(p, '['); + if (openp) + sep = strchr(openp, ']'); + else { + sep = strchr(p, ':'); + } + } +#endif + if (sep) { + sv_setpvn(temp_out_name, p, sep - p + 1); + sv_catpvs(temp_out_name, "XXXXXXXX"); + } + else + sv_setpvs(temp_out_name, "XXXXXXXX"); + + { + int old_umask = umask(0177); + fd = Perl_my_mkstemp(SvPVX(temp_out_name)); + umask(old_umask); + } + + if (fd < 0) + return FALSE; + + fp = PerlIO_fdopen(fd, "w+"); + if (!fp) + return FALSE; + + return do_openn(gv, "+>&", 3, 0, 0, 0, fp, NULL, 0); +} + +#if defined(HAS_UNLINKAT) && defined(HAS_RENAMEAT) && defined(HAS_FCHMODAT) && \ + (defined(HAS_DIRFD) || defined(HAS_DIR_DD_FD)) && !defined(NO_USE_ATFUNCTIONS) && \ + defined(HAS_LINKAT) +# define ARGV_USE_ATFUNCTIONS +#endif + +/* Win32 doesn't necessarily return useful information + * in st_dev, st_ino. + */ +#ifndef DOSISH +# define ARGV_USE_STAT_INO +#endif + +#define ARGVMG_BACKUP_NAME 0 +#define ARGVMG_TEMP_NAME 1 +#define ARGVMG_ORIG_NAME 2 +#define ARGVMG_ORIG_MODE 3 +#define ARGVMG_ORIG_PID 4 + +/* we store the entire stat_t since the ino_t and dev_t values might + not fit in an IV. I could have created a new structure and + transferred them across, but this seemed too much effort for very + little win. + + We store it even when the *at() functions are available, since + while the C runtime might have definitions for these functions, the + operating system or a specific filesystem might not implement them. + eg. NetBSD 6 implements linkat() but only where the fds are AT_FDCWD. + */ +#ifdef ARGV_USE_STAT_INO +# define ARGVMG_ORIG_CWD_STAT 5 +#endif + +#ifdef ARGV_USE_ATFUNCTIONS +# define ARGVMG_ORIG_DIRP 6 +#endif + +#ifdef ENOTSUP +#define NotSupported(e) ((e) == ENOSYS || (e) == ENOTSUP) +#else +#define NotSupported(e) ((e) == ENOSYS) +#endif + +static int +S_argvout_free(pTHX_ SV *io, MAGIC *mg) { + PERL_UNUSED_ARG(io); + + /* note this can be entered once the file has been + successfully deleted too */ + assert(IoTYPE(io) != IoTYPE_PIPE); + + /* mg_obj can be NULL if a thread is created with the handle open, in which + case we leave any clean up to the parent thread */ + if (mg->mg_obj && IoIFP(io)) { + SV **pid_psv; +#ifdef ARGV_USE_ATFUNCTIONS + SV **dir_psv; + DIR *dir; +#endif + PerlIO *iop = IoIFP(io); + + assert(SvTYPE(mg->mg_obj) == SVt_PVAV); + + pid_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_PID, FALSE); + + assert(pid_psv && *pid_psv); + + if (SvIV(*pid_psv) == (IV)PerlProc_getpid()) { + /* if we get here the file hasn't been closed explicitly by the + user and hadn't been closed implicitly by nextargv(), so + abandon the edit */ + SV **temp_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_TEMP_NAME, FALSE); + const char *temp_pv = SvPVX(*temp_psv); + + assert(temp_psv && *temp_psv && SvPOK(*temp_psv)); + (void)PerlIO_close(iop); + IoIFP(io) = IoOFP(io) = NULL; +#ifdef ARGV_USE_ATFUNCTIONS + dir_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_DIRP, FALSE); + assert(dir_psv && *dir_psv && SvIOK(*dir_psv)); + dir = INT2PTR(DIR *, SvIV(*dir_psv)); + if (dir) { + if (unlinkat(my_dirfd(dir), temp_pv, 0) < 0 && + NotSupported(errno)) + (void)UNLINK(temp_pv); + closedir(dir); + } +#else + (void)UNLINK(temp_pv); +#endif + } + } + + return 0; +} + +static int +S_argvout_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) { + PERL_UNUSED_ARG(param); + + /* ideally we could just remove the magic from the SV but we don't get the SV here */ + SvREFCNT_dec(mg->mg_obj); + mg->mg_obj = NULL; + + return 0; +} + +/* Magic of this type has an AV containing the following: + 0: name of the backup file (if any) + 1: name of the temp output file + 2: name of the original file + 3: file mode of the original file + 4: pid of the process we opened at, to prevent doing the renaming + etc in both the child and the parent after a fork + +If we have useful inode/device ids in stat_t we also keep: + 5: a stat of the original current working directory + +If we have unlinkat(), renameat(), fchmodat(), dirfd() we also keep: + 6: the DIR * for the current directory when we open the file, stored as an IV + */ + +static const MGVTBL argvout_vtbl = + { + NULL, /* svt_get */ + NULL, /* svt_set */ + NULL, /* svt_len */ + NULL, /* svt_clear */ + S_argvout_free, /* svt_free */ + NULL, /* svt_copy */ + S_argvout_dup, /* svt_dup */ + NULL /* svt_local */ + }; + PerlIO * -Perl_nextargv(pTHX_ GV *gv) +Perl_nextargv(pTHX_ GV *gv, bool nomagicopen) { - dVAR; - SV *sv; -#ifndef FLEXFILENAMES - int filedev; - int fileino; -#endif - Uid_t fileuid; - Gid_t filegid; IO * const io = GvIOp(gv); + SV *const old_out_name = PL_inplace ? newSVsv(GvSV(gv)) : NULL; PERL_ARGS_ASSERT_NEXTARGV; + if (old_out_name) + SAVEFREESV(old_out_name); + if (!PL_argvoutgv) PL_argvoutgv = gv_fetchpvs("ARGVOUT", GV_ADD|GV_NOTQUAL, SVt_PVIO); - if (io && (IoFLAGS(io) & IOf_ARGV) && (IoFLAGS(io) & IOf_START)) { + if (io && (IoFLAGS(io) & (IOf_ARGV|IOf_START)) == (IOf_ARGV|IOf_START)) { IoFLAGS(io) &= ~IOf_START; if (PL_inplace) { assert(PL_defoutgv); @@ -741,29 +1256,54 @@ Perl_nextargv(pTHX_ GV *gv) SvREFCNT_inc_simple_NN(PL_defoutgv)); } } - if (PL_filemode & (S_ISUID|S_ISGID)) { - PerlIO_flush(IoIFP(GvIOn(PL_argvoutgv))); /* chmod must follow last write */ -#ifdef HAS_FCHMOD - if (PL_lastfd != -1) - (void)fchmod(PL_lastfd,PL_filemode); -#else - (void)PerlLIO_chmod(PL_oldname,PL_filemode); -#endif + + { + IO * const io = GvIOp(PL_argvoutgv); + if (io && IoIFP(io) && old_out_name) { + do_close(PL_argvoutgv, FALSE); + } } + PL_lastfd = -1; PL_filemode = 0; if (!GvAV(gv)) return NULL; - while (av_len(GvAV(gv)) >= 0) { + while (av_tindex(GvAV(gv)) >= 0) { STRLEN oldlen; - sv = av_shift(GvAV(gv)); + SV *const sv = av_shift(GvAV(gv)); SAVEFREESV(sv); SvTAINTED_off(GvSVn(gv)); /* previous tainting irrelevant */ sv_setsv(GvSVn(gv),sv); SvSETMAGIC(GvSV(gv)); PL_oldname = SvPVx(GvSV(gv), oldlen); - if (do_open(gv,PL_oldname,oldlen,PL_inplace!=0,O_RDONLY,0,NULL)) { - if (PL_inplace) { + if (LIKELY(!PL_inplace)) { + if (nomagicopen + ? do_open6(gv, "<", 1, NULL, &GvSV(gv), 1) + : do_open6(gv, PL_oldname, oldlen, NULL, NULL, 0) + ) { + return IoIFP(GvIOp(gv)); + } + } + else { + Stat_t statbuf; + /* This very long block ends with return IoIFP(GvIOp(gv)); + Both this block and the block above fall through on open + failure to the warning code, and then the while loop above tries + the next entry. */ + if (do_open_raw(gv, PL_oldname, oldlen, O_RDONLY, 0, &statbuf)) { +#ifndef FLEXFILENAMES + int filedev; + int fileino; +#endif +#ifdef ARGV_USE_ATFUNCTIONS + DIR *curdir; +#endif + Uid_t fileuid; + Gid_t filegid; + AV *magic_av = NULL; + SV *temp_name_sv = NULL; + MAGIC *mg; + TAINT_PROPER("inplace open"); if (oldlen == 1 && *PL_oldname == '-') { setdefout(gv_fetchpvs("STDOUT", GV_ADD|GV_NOTQUAL, @@ -771,12 +1311,12 @@ Perl_nextargv(pTHX_ GV *gv) return IoIFP(GvIOp(gv)); } #ifndef FLEXFILENAMES - filedev = PL_statbuf.st_dev; - fileino = PL_statbuf.st_ino; + filedev = statbuf.st_dev; + fileino = statbuf.st_ino; #endif - PL_filemode = PL_statbuf.st_mode; - fileuid = PL_statbuf.st_uid; - filegid = PL_statbuf.st_gid; + PL_filemode = statbuf.st_mode; + fileuid = statbuf.st_uid; + filegid = statbuf.st_gid; if (!S_ISREG(PL_filemode)) { Perl_ck_warner_d(aTHX_ packWARN(WARN_INPLACE), "Can't do inplace edit: %s is not a regular file", @@ -784,11 +1324,12 @@ Perl_nextargv(pTHX_ GV *gv) do_close(gv,FALSE); continue; } + magic_av = newAV(); if (*PL_inplace && strNE(PL_inplace, "*")) { const char *star = strchr(PL_inplace, '*'); if (star) { const char *begin = PL_inplace; - sv_setpvs(sv, ""); + SvPVCLEAR(sv); do { sv_catpvn(sv, begin, star - begin); sv_catpvn(sv, PL_oldname, oldlen); @@ -801,122 +1342,96 @@ Perl_nextargv(pTHX_ GV *gv) sv_catpv(sv,PL_inplace); } #ifndef FLEXFILENAMES - if ((PerlLIO_stat(SvPVX_const(sv),&PL_statbuf) >= 0 - && PL_statbuf.st_dev == filedev - && PL_statbuf.st_ino == fileino) + if ((PerlLIO_stat(SvPVX_const(sv),&statbuf) >= 0 + && statbuf.st_dev == filedev + && statbuf.st_ino == fileino) #ifdef DJGPP || ((_djstat_fail_bits & _STFAIL_TRUENAME)!=0) #endif ) { Perl_ck_warner_d(aTHX_ packWARN(WARN_INPLACE), - "Can't do inplace edit: %"SVf" would not be unique", + "Can't do inplace edit: %" + SVf " would not be unique", SVfARG(sv)); - do_close(gv,FALSE); - continue; - } -#endif -#ifdef HAS_RENAME -#if !defined(DOSISH) && !defined(__CYGWIN__) - if (PerlLIO_rename(PL_oldname,SvPVX_const(sv)) < 0) { - Perl_ck_warner_d(aTHX_ packWARN(WARN_INPLACE), - "Can't rename %s to %"SVf": %s, skipping file", - PL_oldname, SVfARG(sv), Strerror(errno)); - do_close(gv,FALSE); - continue; + goto cleanup_argv; } -#else - do_close(gv,FALSE); - (void)PerlLIO_unlink(SvPVX_const(sv)); - (void)PerlLIO_rename(PL_oldname,SvPVX_const(sv)); - do_open(gv,(char*)SvPVX_const(sv),SvCUR(sv),TRUE,O_RDONLY,0,NULL); -#endif /* DOSISH */ -#else - (void)UNLINK(SvPVX_const(sv)); - if (link(PL_oldname,SvPVX_const(sv)) < 0) { - Perl_ck_warner_d(aTHX_ packWARN(WARN_INPLACE), - "Can't rename %s to %"SVf": %s, skipping file", - PL_oldname, SVfARG(sv), Strerror(errno) ); - do_close(gv,FALSE); - continue; - } - (void)UNLINK(PL_oldname); -#endif - } - else { -#if !defined(DOSISH) && !defined(AMIGAOS) -# ifndef VMS /* Don't delete; use automatic file versioning */ - if (UNLINK(PL_oldname) < 0) { - Perl_ck_warner_d(aTHX_ packWARN(WARN_INPLACE), - "Can't remove %s: %s, skipping file", - PL_oldname, Strerror(errno) ); - do_close(gv,FALSE); - continue; - } -# endif -#else - Perl_croak(aTHX_ "Can't do inplace edit without backup"); #endif + av_store(magic_av, ARGVMG_BACKUP_NAME, newSVsv(sv)); } sv_setpvn(sv,PL_oldname,oldlen); SETERRNO(0,0); /* in case sprintf set errno */ - if (!Perl_do_openn(aTHX_ PL_argvoutgv, (char*)SvPVX_const(sv), - SvCUR(sv), TRUE, -#ifdef VMS - O_WRONLY|O_CREAT|O_TRUNC,0, -#else - O_WRONLY|O_CREAT|OPEN_EXCL,0600, -#endif - NULL, NULL, 0)) { - Perl_ck_warner_d(aTHX_ packWARN(WARN_INPLACE), "Can't do inplace edit on %s: %s", + temp_name_sv = newSV(0); + if (!S_openindirtemp(aTHX_ PL_argvoutgv, GvSV(gv), temp_name_sv)) { + SvREFCNT_dec(temp_name_sv); + /* diag_listed_as: Can't do inplace edit on %s: %s */ + Perl_ck_warner_d(aTHX_ packWARN(WARN_INPLACE), "Can't do inplace edit on %s: Cannot make temp name: %s", PL_oldname, Strerror(errno) ); - do_close(gv,FALSE); - continue; +#ifndef FLEXFILENAMES + cleanup_argv: +#endif + do_close(gv,FALSE); + SvREFCNT_dec(magic_av); + continue; } + av_store(magic_av, ARGVMG_TEMP_NAME, temp_name_sv); + av_store(magic_av, ARGVMG_ORIG_NAME, newSVsv(sv)); + av_store(magic_av, ARGVMG_ORIG_MODE, newSVuv(PL_filemode)); + av_store(magic_av, ARGVMG_ORIG_PID, newSViv((IV)PerlProc_getpid())); +#if defined(ARGV_USE_ATFUNCTIONS) + curdir = opendir("."); + av_store(magic_av, ARGVMG_ORIG_DIRP, newSViv(PTR2IV(curdir))); +#elif defined(ARGV_USE_STAT_INO) + if (PerlLIO_stat(".", &statbuf) >= 0) { + av_store(magic_av, ARGVMG_ORIG_CWD_STAT, + newSVpvn((char *)&statbuf, sizeof(statbuf))); + } +#endif setdefout(PL_argvoutgv); + sv_setsv(GvSVn(PL_argvoutgv), temp_name_sv); + mg = sv_magicext((SV*)GvIOp(PL_argvoutgv), (SV*)magic_av, PERL_MAGIC_uvar, &argvout_vtbl, NULL, 0); + mg->mg_flags |= MGf_DUP; + SvREFCNT_dec(magic_av); PL_lastfd = PerlIO_fileno(IoIFP(GvIOp(PL_argvoutgv))); - (void)PerlLIO_fstat(PL_lastfd,&PL_statbuf); + if (PL_lastfd >= 0) { + (void)PerlLIO_fstat(PL_lastfd,&statbuf); #ifdef HAS_FCHMOD - (void)fchmod(PL_lastfd,PL_filemode); + (void)fchmod(PL_lastfd,PL_filemode); #else - (void)PerlLIO_chmod(PL_oldname,PL_filemode); + (void)PerlLIO_chmod(PL_oldname,PL_filemode); #endif - if (fileuid != PL_statbuf.st_uid || filegid != PL_statbuf.st_gid) { - int rc = 0; + if (fileuid != statbuf.st_uid || filegid != statbuf.st_gid) { + /* XXX silently ignore failures */ #ifdef HAS_FCHOWN - rc = fchown(PL_lastfd,fileuid,filegid); -#else -#ifdef HAS_CHOWN - rc = PerlLIO_chown(PL_oldname,fileuid,filegid); + PERL_UNUSED_RESULT(fchown(PL_lastfd,fileuid,filegid)); +#elif defined(HAS_CHOWN) + PERL_UNUSED_RESULT(PerlLIO_chown(PL_oldname,fileuid,filegid)); #endif -#endif - /* XXX silently ignore failures */ - PERL_UNUSED_VAR(rc); - } - } - return IoIFP(GvIOp(gv)); - } - else { - if (ckWARN_d(WARN_INPLACE)) { - const int eno = errno; - if (PerlLIO_stat(PL_oldname, &PL_statbuf) >= 0 - && !S_ISREG(PL_statbuf.st_mode)) - { - Perl_warner(aTHX_ packWARN(WARN_INPLACE), - "Can't do inplace edit: %s is not a regular file", - PL_oldname); + } } - else - Perl_warner(aTHX_ packWARN(WARN_INPLACE), "Can't open %s: %s", - PL_oldname, Strerror(eno)); + return IoIFP(GvIOp(gv)); } + } /* successful do_open_raw(), PL_inplace non-NULL */ + + if (ckWARN_d(WARN_INPLACE)) { + const int eno = errno; + Stat_t statbuf; + if (PerlLIO_stat(PL_oldname, &statbuf) >= 0 + && !S_ISREG(statbuf.st_mode)) { + Perl_warner(aTHX_ packWARN(WARN_INPLACE), + "Can't do inplace edit: %s is not a regular file", + PL_oldname); + } + else { + Perl_warner(aTHX_ packWARN(WARN_INPLACE), "Can't open %s: %s", + PL_oldname, Strerror(eno)); + } } } if (io && (IoFLAGS(io) & IOf_ARGV)) IoFLAGS(io) |= IOf_START; if (PL_inplace) { - (void)do_close(PL_argvoutgv,FALSE); if (io && (IoFLAGS(io) & IOf_ARGV) && PL_argvout_stack && AvFILLp(PL_argvout_stack) >= 0) { @@ -930,13 +1445,79 @@ Perl_nextargv(pTHX_ GV *gv) return NULL; } +#ifdef ARGV_USE_ATFUNCTIONS +# if defined(__FreeBSD__) + +/* FreeBSD 11 renameat() mis-behaves strangely with absolute paths in cases where the + * equivalent rename() succeeds + */ +static int +S_my_renameat(int olddfd, const char *oldpath, int newdfd, const char *newpath) { + /* this is intended only for use in Perl_do_close() */ + assert(olddfd == newdfd); + assert(PERL_FILE_IS_ABSOLUTE(oldpath) == PERL_FILE_IS_ABSOLUTE(newpath)); + if (PERL_FILE_IS_ABSOLUTE(oldpath)) { + return PerlLIO_rename(oldpath, newpath); + } + else { + return renameat(olddfd, oldpath, newdfd, newpath); + } +} + +# else +# define S_my_renameat(dh1, pv1, dh2, pv2) renameat((dh1), (pv1), (dh2), (pv2)) +# endif /* if defined(__FreeBSD__) */ +#endif + +static bool +S_dir_unchanged(pTHX_ const char *orig_pv, MAGIC *mg) { + Stat_t statbuf; + +#ifdef ARGV_USE_STAT_INO + SV **stat_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_CWD_STAT, FALSE); + Stat_t *orig_cwd_stat = stat_psv && *stat_psv ? (Stat_t *)SvPVX(*stat_psv) : NULL; + + /* if the path is absolute the possible moving of cwd (which the file + might be in) isn't our problem. + This code tries to be reasonably balanced about detecting a changed + CWD, if we have the information needed to check that curdir has changed, we + check it + */ + if (!PERL_FILE_IS_ABSOLUTE(orig_pv) + && orig_cwd_stat + && PerlLIO_stat(".", &statbuf) >= 0 + && ( statbuf.st_dev != orig_cwd_stat->st_dev + || statbuf.st_ino != orig_cwd_stat->st_ino)) { + Perl_croak(aTHX_ "Cannot complete in-place edit of %s: %s", + orig_pv, "Current directory has changed"); + } +#else + SV **temp_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_TEMP_NAME, FALSE); + + /* Some platforms don't have useful st_ino etc, so just + check we can see the work file. + */ + if (!PERL_FILE_IS_ABSOLUTE(orig_pv) + && PerlLIO_stat(SvPVX(*temp_psv), &statbuf) < 0) { + Perl_croak(aTHX_ "Cannot complete in-place edit of %s: %s", + orig_pv, + "Work file is missing - did you change directory?"); + } +#endif + + return TRUE; +} + +#define dir_unchanged(orig_psv, mg) \ + S_dir_unchanged(aTHX_ (orig_psv), (mg)) + /* explicit renamed to avoid C++ conflict -- kja */ bool Perl_do_close(pTHX_ GV *gv, bool not_implicit) { - dVAR; bool retval; IO *io; + MAGIC *mg; if (!gv) gv = PL_argvgv; @@ -953,7 +1534,180 @@ Perl_do_close(pTHX_ GV *gv, bool not_implicit) } return FALSE; } - retval = io_close(io, not_implicit); + if ((mg = mg_findext((SV*)io, PERL_MAGIC_uvar, &argvout_vtbl)) + && mg->mg_obj) { + /* handle to an in-place edit work file */ + SV **back_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_BACKUP_NAME, FALSE); + SV **temp_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_TEMP_NAME, FALSE); + /* PL_oldname may have been modified by a nested ARGV use at this point */ + SV **orig_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_NAME, FALSE); + SV **mode_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_MODE, FALSE); + SV **pid_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_PID, FALSE); +#if defined(ARGV_USE_ATFUNCTIONS) + SV **dir_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_DIRP, FALSE); + DIR *dir; + int dfd; +#endif + UV mode; + int fd; + + const char *orig_pv; + + assert(temp_psv && *temp_psv); + assert(orig_psv && *orig_psv); + assert(mode_psv && *mode_psv); + assert(pid_psv && *pid_psv); +#ifdef ARGV_USE_ATFUNCTIONS + assert(dir_psv && *dir_psv); + dir = INT2PTR(DIR *, SvIVX(*dir_psv)); + dfd = my_dirfd(dir); +#endif + + orig_pv = SvPVX(*orig_psv); + mode = SvUV(*mode_psv); + + if ((mode & (S_ISUID|S_ISGID)) != 0 + && (fd = PerlIO_fileno(IoIFP(io))) >= 0) { + (void)PerlIO_flush(IoIFP(io)); +#ifdef HAS_FCHMOD + (void)fchmod(fd, mode); +#else + (void)PerlLIO_chmod(orig_pv, mode); +#endif + } + + retval = io_close(io, NULL, not_implicit, FALSE); + + if (SvIV(*pid_psv) != (IV)PerlProc_getpid()) { + /* this is a child process, don't duplicate our rename() etc + processing below */ + goto freext; + } + + if (retval) { +#if defined(DOSISH) || defined(__CYGWIN__) + if (PL_argvgv && GvIOp(PL_argvgv) + && IoIFP(GvIOp(PL_argvgv)) + && (IoFLAGS(GvIOp(PL_argvgv)) & (IOf_ARGV|IOf_START)) == IOf_ARGV) { + do_close(PL_argvgv, FALSE); + } +#endif +#ifndef ARGV_USE_ATFUNCTIONS + if (!dir_unchanged(orig_pv, mg)) + goto abort_inplace; +#endif + if (back_psv && *back_psv) { +#if defined(HAS_LINK) && !defined(DOSISH) && !defined(__CYGWIN__) && defined(HAS_RENAME) + if ( +# ifdef ARGV_USE_ATFUNCTIONS + linkat(dfd, orig_pv, dfd, SvPVX(*back_psv), 0) < 0 && + !(UNLIKELY(NotSupported(errno)) && + dir_unchanged(orig_pv, mg) && + link(orig_pv, SvPVX(*back_psv)) == 0) +# else + link(orig_pv, SvPVX(*back_psv)) < 0 +# endif + ) +#endif + { +#ifdef HAS_RENAME + if ( +# ifdef ARGV_USE_ATFUNCTIONS + S_my_renameat(dfd, orig_pv, dfd, SvPVX(*back_psv)) < 0 && + !(UNLIKELY(NotSupported(errno)) && + dir_unchanged(orig_pv, mg) && + PerlLIO_rename(orig_pv, SvPVX(*back_psv)) == 0) +# else + PerlLIO_rename(orig_pv, SvPVX(*back_psv)) < 0 +# endif + ) { + if (!not_implicit) { +# ifdef ARGV_USE_ATFUNCTIONS + if (unlinkat(dfd, SvPVX_const(*temp_psv), 0) < 0 && + UNLIKELY(NotSupported(errno)) && + dir_unchanged(orig_pv, mg)) + (void)UNLINK(SvPVX_const(*temp_psv)); +# else + UNLINK(SvPVX(*temp_psv)); +# endif + Perl_croak(aTHX_ "Can't rename %s to %s: %s, skipping file", + SvPVX(*orig_psv), SvPVX(*back_psv), Strerror(errno)); + } + /* should we warn here? */ + goto abort_inplace; + } +#else + (void)UNLINK(SvPVX(*back_psv)); + if (link(orig_pv, SvPVX(*back_psv))) { + if (!not_implicit) { + Perl_croak(aTHX_ "Can't rename %s to %s: %s, skipping file", + SvPVX(*orig_psv), SvPVX(*back_psv), Strerror(errno)); + } + goto abort_inplace; + } + /* we need to use link() to get the temp into place too, and linK() + fails if the new link name exists */ + (void)UNLINK(orig_pv); +#endif + } + } +#if defined(DOSISH) || defined(__CYGWIN__) || !defined(HAS_RENAME) + else { + UNLINK(orig_pv); + } +#endif + if ( +#if !defined(HAS_RENAME) + link(SvPVX(*temp_psv), orig_pv) < 0 +#elif defined(ARGV_USE_ATFUNCTIONS) + S_my_renameat(dfd, SvPVX(*temp_psv), dfd, orig_pv) < 0 && + !(UNLIKELY(NotSupported(errno)) && + dir_unchanged(orig_pv, mg) && + PerlLIO_rename(SvPVX(*temp_psv), orig_pv) == 0) +#else + PerlLIO_rename(SvPVX(*temp_psv), orig_pv) < 0 +#endif + ) { + if (!not_implicit) { +#ifdef ARGV_USE_ATFUNCTIONS + if (unlinkat(dfd, SvPVX_const(*temp_psv), 0) < 0 && + NotSupported(errno)) + UNLINK(SvPVX(*temp_psv)); +#else + UNLINK(SvPVX(*temp_psv)); +#endif + /* diag_listed_as: Cannot complete in-place edit of %s: %s */ + Perl_croak(aTHX_ "Cannot complete in-place edit of %s: failed to rename work file '%s' to '%s': %s", + orig_pv, SvPVX(*temp_psv), orig_pv, Strerror(errno)); + } + abort_inplace: + UNLINK(SvPVX_const(*temp_psv)); + retval = FALSE; + } +#ifndef HAS_RENAME + UNLINK(SvPVX(*temp_psv)); +#endif + } + else { +#ifdef ARGV_USE_ATFUNCTIONS + if (unlinkat(dfd, SvPVX_const(*temp_psv), 0) && + NotSupported(errno)) + UNLINK(SvPVX_const(*temp_psv)); + +#else + UNLINK(SvPVX_const(*temp_psv)); +#endif + if (!not_implicit) { + Perl_croak(aTHX_ "Failed to close in-place work file %s: %s", + SvPVX(*temp_psv), Strerror(errno)); + } + } + freext: + mg_freeext((SV*)io, PERL_MAGIC_uvar, &argvout_vtbl); + } + else { + retval = io_close(io, NULL, not_implicit, FALSE); + } if (not_implicit) { IoLINES(io) = 0; IoPAGE(io) = 0; @@ -964,9 +1718,8 @@ Perl_do_close(pTHX_ GV *gv, bool not_implicit) } bool -Perl_io_close(pTHX_ IO *io, bool not_implicit) +Perl_io_close(pTHX_ IO *io, GV *gv, bool not_implicit, bool warn_on_fail) { - dVAR; bool retval = FALSE; PERL_ARGS_ASSERT_IO_CLOSE; @@ -987,15 +1740,37 @@ Perl_io_close(pTHX_ IO *io, bool not_implicit) else { if (IoOFP(io) && IoOFP(io) != IoIFP(io)) { /* a socket */ const bool prev_err = PerlIO_error(IoOFP(io)); +#ifdef USE_PERLIO + if (prev_err) + PerlIO_restore_errno(IoOFP(io)); +#endif retval = (PerlIO_close(IoOFP(io)) != EOF && !prev_err); PerlIO_close(IoIFP(io)); /* clear stdio, fd already closed */ } else { const bool prev_err = PerlIO_error(IoIFP(io)); +#ifdef USE_PERLIO + if (prev_err) + PerlIO_restore_errno(IoIFP(io)); +#endif retval = (PerlIO_close(IoIFP(io)) != EOF && !prev_err); } } IoOFP(io) = IoIFP(io) = NULL; + + if (warn_on_fail && !retval) { + if (gv) + Perl_ck_warner_d(aTHX_ packWARN(WARN_IO), + "Warning: unable to close filehandle %" + HEKf " properly: %" SVf, + HEKfARG(GvNAME_HEK(gv)), + SVfARG(get_sv("!",GV_ADD))); + else + Perl_ck_warner_d(aTHX_ packWARN(WARN_IO), + "Warning: unable to close filehandle " + "properly: %" SVf, + SVfARG(get_sv("!",GV_ADD))); + } } else if (not_implicit) { SETERRNO(EBADF,SS_IVCHAN); @@ -1007,7 +1782,6 @@ Perl_io_close(pTHX_ IO *io, bool not_implicit) bool Perl_do_eof(pTHX_ GV *gv) { - dVAR; IO * const io = GvIO(gv); PERL_ARGS_ASSERT_DO_EOF; @@ -1040,7 +1814,7 @@ Perl_do_eof(pTHX_ GV *gv) PerlIO_set_cnt(IoIFP(io),-1); } if (PL_op->op_flags & OPf_SPECIAL) { /* not necessarily a real EOF yet? */ - if (gv != PL_argvgv || !nextargv(gv)) /* get another fp handy */ + if (gv != PL_argvgv || !nextargv(gv, FALSE)) /* get another fp handy */ return TRUE; } else @@ -1052,7 +1826,6 @@ Perl_do_eof(pTHX_ GV *gv) Off_t Perl_do_tell(pTHX_ GV *gv) { - dVAR; IO *const io = GvIO(gv); PerlIO *fp; @@ -1069,7 +1842,6 @@ Perl_do_tell(pTHX_ GV *gv) bool Perl_do_seek(pTHX_ GV *gv, Off_t pos, int whence) { - dVAR; IO *const io = GvIO(gv); PerlIO *fp; @@ -1084,14 +1856,20 @@ Perl_do_seek(pTHX_ GV *gv, Off_t pos, int whence) Off_t Perl_do_sysseek(pTHX_ GV *gv, Off_t pos, int whence) { - dVAR; IO *const io = GvIO(gv); PerlIO *fp; PERL_ARGS_ASSERT_DO_SYSSEEK; - if (io && (fp = IoIFP(io))) - return PerlLIO_lseek(PerlIO_fileno(fp), pos, whence); + if (io && (fp = IoIFP(io))) { + int fd = PerlIO_fileno(fp); + if (fd < 0 || (whence == SEEK_SET && pos < 0)) { + SETERRNO(EINVAL,LIB_INVARG); + return -1; + } else { + return PerlLIO_lseek(fd, pos, whence); + } + } report_evil_fh(gv); SETERRNO(EBADF,RMS_IFI); return (Off_t)-1; @@ -1101,6 +1879,7 @@ int Perl_mode_from_discipline(pTHX_ const char *s, STRLEN len) { int mode = O_BINARY; + PERL_UNUSED_CONTEXT; if (s) { while (*s) { if (*s == ':') { @@ -1114,7 +1893,7 @@ Perl_mode_from_discipline(pTHX_ const char *s, STRLEN len) len -= 4; break; } - /* FALL THROUGH */ + /* FALLTHROUGH */ case 'c': if (s[2] == 'r' && s[3] == 'l' && s[4] == 'f' && (!s[5] || s[5] == ':' || isSPACE(s[5]))) @@ -1124,7 +1903,7 @@ Perl_mode_from_discipline(pTHX_ const char *s, STRLEN len) len -= 5; break; } - /* FALL THROUGH */ + /* FALLTHROUGH */ default: goto fail_discipline; } @@ -1135,8 +1914,8 @@ Perl_mode_from_discipline(pTHX_ const char *s, STRLEN len) } else { const char *end; -fail_discipline: - end = strchr(s+1, ':'); + fail_discipline: + end = (char *) memchr(s+1, ':', len); if (!end) end = s+len; #ifndef PERLIO_LAYERS @@ -1207,8 +1986,6 @@ my_chsize(int fd, Off_t length) bool Perl_do_print(pTHX_ SV *sv, PerlIO *fp) { - dVAR; - PERL_ARGS_ASSERT_DO_PRINT; /* assuming fp is checked earlier */ @@ -1217,9 +1994,9 @@ Perl_do_print(pTHX_ SV *sv, PerlIO *fp) if (SvTYPE(sv) == SVt_IV && SvIOK(sv)) { assert(!SvGMAGICAL(sv)); if (SvIsUV(sv)) - PerlIO_printf(fp, "%"UVuf, (UV)SvUVX(sv)); + PerlIO_printf(fp, "%" UVuf, (UV)SvUVX(sv)); else - PerlIO_printf(fp, "%"IVdf, (IV)SvIVX(sv)); + PerlIO_printf(fp, "%" IVdf, (IV)SvIVX(sv)); return !PerlIO_error(fp); } else { @@ -1280,7 +2057,6 @@ Perl_do_print(pTHX_ SV *sv, PerlIO *fp) I32 Perl_my_stat_flags(pTHX_ const U32 flags) { - dVAR; dSP; IO *io; GV* gv; @@ -1288,22 +2064,33 @@ Perl_my_stat_flags(pTHX_ const U32 flags) if (PL_op->op_flags & OPf_REF) { gv = cGVOP_gv; do_fstat: - if (gv == PL_defgv) + if (gv == PL_defgv) { + if (PL_laststatval < 0) + SETERRNO(EBADF,RMS_IFI); return PL_laststatval; + } io = GvIO(gv); do_fstat_have_io: PL_laststype = OP_STAT; PL_statgv = gv ? gv : (GV *)io; - sv_setpvs(PL_statname, ""); - if(io) { + SvPVCLEAR(PL_statname); + if (io) { if (IoIFP(io)) { - return (PL_laststatval = PerlLIO_fstat(PerlIO_fileno(IoIFP(io)), &PL_statcache)); + int fd = PerlIO_fileno(IoIFP(io)); + if (fd < 0) { + /* E.g. PerlIO::scalar has no real fd. */ + SETERRNO(EBADF,RMS_IFI); + return (PL_laststatval = -1); + } else { + return (PL_laststatval = PerlLIO_fstat(fd, &PL_statcache)); + } } else if (IoDIRP(io)) { return (PL_laststatval = PerlLIO_fstat(my_dirfd(IoDIRP(io)), &PL_statcache)); } } PL_laststatval = -1; report_evil_fh(gv); + SETERRNO(EBADF,RMS_IFI); return -1; } else if ((PL_op->op_private & (OPpFT_STACKED|OPpFT_AFTER_t)) @@ -1311,7 +2098,7 @@ Perl_my_stat_flags(pTHX_ const U32 flags) return PL_laststatval; else { SV* const sv = TOPs; - const char *s; + const char *s, *d; STRLEN len; if ((gv = MAYBE_DEREF_GV_flags(sv,flags))) { goto do_fstat; @@ -1325,13 +2112,18 @@ Perl_my_stat_flags(pTHX_ const U32 flags) s = SvPV_flags_const(sv, len, flags); PL_statgv = NULL; sv_setpvn(PL_statname, s, len); - s = SvPVX_const(PL_statname); /* s now NUL-terminated */ + d = SvPVX_const(PL_statname); /* s now NUL-terminated */ PL_laststype = OP_STAT; - PL_laststatval = PerlLIO_stat(s, &PL_statcache); - if (PL_laststatval < 0 && ckWARN(WARN_NEWLINE) && strchr(s, '\n')) { - GCC_DIAG_IGNORE(-Wformat-nonliteral); /* PL_warn_nl is constant */ + if (!IS_SAFE_PATHNAME(s, len, OP_NAME(PL_op))) { + PL_laststatval = -1; + } + else { + PL_laststatval = PerlLIO_stat(d, &PL_statcache); + } + if (PL_laststatval < 0 && ckWARN(WARN_NEWLINE) && should_warn_nl(s)) { + GCC_DIAG_IGNORE_STMT(-Wformat-nonliteral); /* PL_warn_nl is constant */ Perl_warner(aTHX_ packWARN(WARN_NEWLINE), PL_warn_nl, "stat"); - GCC_DIAG_RESTORE; + GCC_DIAG_RESTORE_STMT; } return PL_laststatval; } @@ -1341,25 +2133,28 @@ Perl_my_stat_flags(pTHX_ const U32 flags) I32 Perl_my_lstat_flags(pTHX_ const U32 flags) { - dVAR; static const char* const no_prev_lstat = "The stat preceding -l _ wasn't an lstat"; dSP; const char *file; + STRLEN len; SV* const sv = TOPs; bool isio = FALSE; if (PL_op->op_flags & OPf_REF) { if (cGVOP_gv == PL_defgv) { if (PL_laststype != OP_LSTAT) Perl_croak(aTHX_ "%s", no_prev_lstat); + if (PL_laststatval < 0) + SETERRNO(EBADF,RMS_IFI); return PL_laststatval; } PL_laststatval = -1; if (ckWARN(WARN_IO)) { /* diag_listed_as: Use of -l on filehandle%s */ Perl_warner(aTHX_ packWARN(WARN_IO), - "Use of -l on filehandle %"HEKf, + "Use of -l on filehandle %" HEKf, HEKfARG(GvENAME_HEK(cGVOP_gv))); } + SETERRNO(EBADF,RMS_IFI); return -1; } if ((PL_op->op_private & (OPpFT_STACKED|OPpFT_AFTER_t)) @@ -1384,17 +2179,22 @@ Perl_my_lstat_flags(pTHX_ const U32 flags) else /* diag_listed_as: Use of -l on filehandle%s */ Perl_warner(aTHX_ packWARN(WARN_IO), - "Use of -l on filehandle %"HEKf, - GvENAME_HEK((const GV *) - (SvROK(sv) ? SvRV(sv) : sv))); + "Use of -l on filehandle %" HEKf, + HEKfARG(GvENAME_HEK((const GV *) + (SvROK(sv) ? SvRV(sv) : sv)))); } - file = SvPV_flags_const_nolen(sv, flags); + file = SvPV_flags_const(sv, len, flags); sv_setpv(PL_statname,file); - PL_laststatval = PerlLIO_lstat(file,&PL_statcache); - if (PL_laststatval < 0 && ckWARN(WARN_NEWLINE) && strchr(file, '\n')) { - GCC_DIAG_IGNORE(-Wformat-nonliteral); /* PL_warn_nl is constant */ + if (!IS_SAFE_PATHNAME(file, len, OP_NAME(PL_op))) { + PL_laststatval = -1; + } + else { + PL_laststatval = PerlLIO_lstat(file,&PL_statcache); + } + if (PL_laststatval < 0 && ckWARN(WARN_NEWLINE) && should_warn_nl(file)) { + GCC_DIAG_IGNORE_STMT(-Wformat-nonliteral); /* PL_warn_nl is constant */ Perl_warner(aTHX_ packWARN(WARN_NEWLINE), PL_warn_nl, "lstat"); - GCC_DIAG_RESTORE; + GCC_DIAG_RESTORE_STMT; } return PL_laststatval; } @@ -1404,13 +2204,13 @@ S_exec_failed(pTHX_ const char *cmd, int fd, int do_report) { const int e = errno; PERL_ARGS_ASSERT_EXEC_FAILED; + if (ckWARN(WARN_EXEC)) - Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't exec \"%s\": %s", - cmd, Strerror(e)); + Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't exec \"%s\": %s", + cmd, Strerror(e)); if (do_report) { - int rc = PerlLIO_write(fd, (void*)&e, sizeof(int)); - /* silently ignore failures */ - PERL_UNUSED_VAR(rc); + /* XXX silently ignore failures */ + PERL_UNUSED_RESULT(PerlLIO_write(fd, (void*)&e, sizeof(int))); PerlLIO_close(fd); } } @@ -1424,54 +2224,54 @@ Perl_do_aexec5(pTHX_ SV *really, SV **mark, SV **sp, #if defined(__SYMBIAN32__) || defined(__LIBCATAMOUNT__) Perl_croak(aTHX_ "exec? I'm not *that* kind of operating system"); #else - if (sp > mark) { - const char **a; + assert(sp >= mark); + ENTER; + { + const char **argv, **a; const char *tmps = NULL; - Newx(PL_Argv, sp - mark + 1, const char*); - a = PL_Argv; + Newx(argv, sp - mark + 1, const char*); + SAVEFREEPV(argv); + a = argv; while (++mark <= sp) { - if (*mark) - *a++ = SvPV_nolen_const(*mark); - else + if (*mark) { + char *arg = savepv(SvPV_nolen_const(*mark)); + SAVEFREEPV(arg); + *a++ = arg; + } else *a++ = ""; } *a = NULL; - if (really) - tmps = SvPV_nolen_const(really); - if ((!really && *PL_Argv[0] != '/') || + if (really) { + tmps = savepv(SvPV_nolen_const(really)); + SAVEFREEPV(tmps); + } + if ((!really && argv[0] && *argv[0] != '/') || (really && *tmps != '/')) /* will execvp use PATH? */ TAINT_ENV(); /* testing IFS here is overkill, probably */ PERL_FPU_PRE_EXEC - if (really && *tmps) - PerlProc_execvp(tmps,EXEC_ARGV_CAST(PL_Argv)); - else - PerlProc_execvp(PL_Argv[0],EXEC_ARGV_CAST(PL_Argv)); + if (really && *tmps) { + PerlProc_execvp(tmps,EXEC_ARGV_CAST(argv)); + } else if (argv[0]) { + PerlProc_execvp(argv[0],EXEC_ARGV_CAST(argv)); + } else { + SETERRNO(ENOENT,RMS_FNF); + } PERL_FPU_POST_EXEC - S_exec_failed(aTHX_ (really ? tmps : PL_Argv[0]), fd, do_report); + S_exec_failed(aTHX_ (really ? tmps : argv[0] ? argv[0] : ""), fd, do_report); } - do_execfree(); + LEAVE; #endif return FALSE; } -void -Perl_do_execfree(pTHX) -{ - dVAR; - Safefree(PL_Argv); - PL_Argv = NULL; - Safefree(PL_Cmd); - PL_Cmd = NULL; -} - #ifdef PERL_DEFAULT_DO_EXEC3_IMPLEMENTATION bool Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report) { dVAR; - const char **a; + const char **argv, **a; char *s; char *buf; char *cmd; @@ -1480,7 +2280,9 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report) PERL_ARGS_ASSERT_DO_EXEC3; + ENTER; Newx(buf, cmdlen, char); + SAVEFREEPV(buf); cmd = buf; memcpy(cmd, incmd, cmdlen); @@ -1493,7 +2295,7 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report) { char flags[PERL_FLAGS_MAX]; if (strnEQ(cmd,PL_cshname,PL_cshlen) && - strnEQ(cmd+PL_cshlen," -c",3)) { + strBEGINs(cmd+PL_cshlen," -c")) { my_strlcpy(flags, "-c", PERL_FLAGS_MAX); s = cmd+PL_cshlen+3; if (*s == 'f') { @@ -1516,8 +2318,7 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report) PERL_FPU_POST_EXEC *s = '\''; S_exec_failed(aTHX_ PL_cshname, fd, do_report); - Safefree(buf); - return FALSE; + goto leave; } } } @@ -1529,7 +2330,7 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report) if (*cmd == '.' && isSPACE(cmd[1])) goto doshell; - if (strnEQ(cmd,"exec",4) && isSPACE(cmd[4])) + if (strBEGINs(cmd,"exec") && isSPACE(cmd[4])) goto doshell; s = cmd; @@ -1561,18 +2362,19 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report) } doshell: PERL_FPU_PRE_EXEC - PerlProc_execl(PL_sh_path, "sh", "-c", cmd, (char *)NULL); + PerlProc_execl(PL_sh_path, "sh", "-c", cmd, (char *)NULL); PERL_FPU_POST_EXEC S_exec_failed(aTHX_ PL_sh_path, fd, do_report); - Safefree(buf); - return FALSE; + goto leave; } } - Newx(PL_Argv, (s - cmd) / 2 + 2, const char*); - PL_Cmd = savepvn(cmd, s-cmd); - a = PL_Argv; - for (s = PL_Cmd; *s;) { + Newx(argv, (s - cmd) / 2 + 2, const char*); + SAVEFREEPV(argv); + cmd = savepvn(cmd, s-cmd); + SAVEFREEPV(cmd); + a = argv; + for (s = cmd; *s;) { while (isSPACE(*s)) s++; if (*s) @@ -1583,31 +2385,24 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report) *s++ = '\0'; } *a = NULL; - if (PL_Argv[0]) { + if (argv[0]) { PERL_FPU_PRE_EXEC - PerlProc_execvp(PL_Argv[0],EXEC_ARGV_CAST(PL_Argv)); + PerlProc_execvp(argv[0],EXEC_ARGV_CAST(argv)); PERL_FPU_POST_EXEC - if (errno == ENOEXEC) { /* for system V NIH syndrome */ - do_execfree(); + if (errno == ENOEXEC) /* for system V NIH syndrome */ goto doshell; - } - S_exec_failed(aTHX_ PL_Argv[0], fd, do_report); + S_exec_failed(aTHX_ argv[0], fd, do_report); } - do_execfree(); - Safefree(buf); +leave: + LEAVE; return FALSE; } #endif /* OS2 || WIN32 */ -#ifdef VMS -#include /* for sys$delprc */ -#endif - I32 Perl_apply(pTHX_ I32 type, SV **mark, SV **sp) { - dVAR; I32 val; I32 tot = 0; const char *const what = PL_op_name[type]; @@ -1660,14 +2455,19 @@ Perl_apply(pTHX_ I32 type, SV **mark, SV **sp) if ((gv = MAYBE_DEREF_GV(*mark))) { if (GvIO(gv) && IoIFP(GvIOp(gv))) { #ifdef HAS_FCHMOD + int fd = PerlIO_fileno(IoIFP(GvIOn(gv))); APPLY_TAINT_PROPER(); - if (fchmod(PerlIO_fileno(IoIFP(GvIOn(gv))), val)) - tot--; + if (fd < 0) { + SETERRNO(EBADF,RMS_IFI); + tot--; + } else if (fchmod(fd, val)) + tot--; #else Perl_die(aTHX_ PL_no_func, "fchmod"); #endif } else { + SETERRNO(EBADF,RMS_IFI); tot--; } } @@ -1696,14 +2496,19 @@ Perl_apply(pTHX_ I32 type, SV **mark, SV **sp) if ((gv = MAYBE_DEREF_GV(*mark))) { if (GvIO(gv) && IoIFP(GvIOp(gv))) { #ifdef HAS_FCHOWN + int fd = PerlIO_fileno(IoIFP(GvIOn(gv))); APPLY_TAINT_PROPER(); - if (fchown(PerlIO_fileno(IoIFP(GvIOn(gv))), val, val2)) + if (fd < 0) { + SETERRNO(EBADF,RMS_IFI); + tot--; + } else if (fchown(fd, val, val2)) tot--; #else Perl_die(aTHX_ PL_no_func, "fchown"); #endif } else { + SETERRNO(EBADF,RMS_IFI); tot--; } } @@ -1743,7 +2548,8 @@ nothing in the core. len -= 3; } if ((val = whichsig_pvn(s, len)) < 0) - Perl_croak(aTHX_ "Unrecognized signal name \"%"SVf"\"", SVfARG(*mark)); + Perl_croak(aTHX_ "Unrecognized signal name \"%" SVf "\"", + SVfARG(*mark)); } else { @@ -1756,52 +2562,22 @@ nothing in the core. } APPLY_TAINT_PROPER(); tot = sp - mark; -#ifdef VMS - /* kill() doesn't do process groups (job trees?) under VMS */ - if (val == SIGKILL) { - /* Use native sys$delprc() to insure that target process is - * deleted; supervisor-mode images don't pay attention to - * CRTL's emulation of Unix-style signals and kill() - */ - while (++mark <= sp) { - I32 proc; - unsigned long int __vmssts; - SvGETMAGIC(*mark); - if (!(SvIOK(*mark) || SvNOK(*mark) || looks_like_number(*mark))) - Perl_croak(aTHX_ "Can't kill a non-numeric process ID"); - proc = SvIV_nomg(*mark); - APPLY_TAINT_PROPER(); - if (!((__vmssts = sys$delprc(&proc,0)) & 1)) { - tot--; - switch (__vmssts) { - case SS$_NONEXPR: - case SS$_NOSUCHNODE: - SETERRNO(ESRCH,__vmssts); - break; - case SS$_NOPRIV: - SETERRNO(EPERM,__vmssts); - break; - default: - SETERRNO(EVMSERR,__vmssts); - } - } - } - PERL_ASYNC_CHECK(); - break; - } -#endif + while (++mark <= sp) { Pid_t proc; SvGETMAGIC(*mark); if (!(SvNIOK(*mark) || looks_like_number(*mark))) Perl_croak(aTHX_ "Can't kill a non-numeric process ID"); proc = SvIV_nomg(*mark); - if (killgp) - { - proc = -proc; - } APPLY_TAINT_PROPER(); - if (PerlProc_kill(proc, val)) +#ifdef HAS_KILLPG + /* use killpg in preference, as the killpg() wrapper for Win32 + * understands process groups, but the kill() wrapper doesn't */ + if (killgp ? PerlProc_killpg(proc, val) + : PerlProc_kill(proc, val)) +#else + if (PerlProc_kill(killgp ? -proc: proc, val)) +#endif tot--; } PERL_ASYNC_CHECK(); @@ -1816,16 +2592,49 @@ nothing in the core. if (!IS_SAFE_PATHNAME(s, len, "unlink")) { tot--; } - else if (PerlProc_geteuid() || PL_unsafe) { + else if (PL_unsafe) { if (UNLINK(s)) + { tot--; + } +#if defined(__amigaos4__) && defined(NEWLIB) + else + { + /* Under AmigaOS4 unlink only 'fails' if the + * filename is invalid. It may not remove the file + * if it's locked, so check if it's still around. */ + if ((access(s,F_OK) != -1)) + { + tot--; + } + } +#endif } else { /* don't let root wipe out directories without -U */ - if (PerlLIO_lstat(s,&PL_statbuf) < 0 || S_ISDIR(PL_statbuf.st_mode)) + Stat_t statbuf; + if (PerlLIO_lstat(s, &statbuf) < 0) tot--; + else if (S_ISDIR(statbuf.st_mode)) { + SETERRNO(EISDIR, SS_NOPRIV); + tot--; + } else { if (UNLINK(s)) - tot--; + { + tot--; + } +#if defined(__amigaos4__) && defined(NEWLIB) + else + { + /* Under AmigaOS4 unlink only 'fails' if the filename is invalid */ + /* It may not remove the file if it's Locked, so check if it's still */ + /* arround */ + if((access(s,F_OK) != -1)) + { + tot--; + } + } +#endif } } } @@ -1879,9 +2688,12 @@ nothing in the core. if ((gv = MAYBE_DEREF_GV(*mark))) { if (GvIO(gv) && IoIFP(GvIOp(gv))) { #ifdef HAS_FUTIMES + int fd = PerlIO_fileno(IoIFP(GvIOn(gv))); APPLY_TAINT_PROPER(); - if (futimes(PerlIO_fileno(IoIFP(GvIOn(gv))), - (struct timeval *) utbufp)) + if (fd < 0) { + SETERRNO(EBADF,RMS_IFI); + tot--; + } else if (futimes(fd, (struct timeval *) utbufp)) tot--; #else Perl_die(aTHX_ PL_no_func, "futimes"); @@ -1918,7 +2730,7 @@ nothing in the core. #undef APPLY_TAINT_PROPER } -/* Do the permissions allow some operation? Assumes statcache already set. */ +/* Do the permissions in *statbufp allow some operation? */ #ifndef VMS /* VMS' cando is in vms.c */ bool Perl_cando(pTHX_ Mode_t mode, bool effective, const Stat_t *statbufp) @@ -1926,9 +2738,8 @@ Perl_cando(pTHX_ Mode_t mode, bool effective, const Stat_t *statbufp) * is in the list of groups returned from getgroups(). */ { - dVAR; - PERL_ARGS_ASSERT_CANDO; + PERL_UNUSED_CONTEXT; #ifdef DOSISH /* [Comments and code from Len Reed] @@ -1952,7 +2763,7 @@ Perl_cando(pTHX_ Mode_t mode, bool effective, const Stat_t *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 (mode & statbufp->st_mode) ? TRUE : FALSE; + return cBOOL(mode & statbufp->st_mode); #else /* ! DOSISH */ # ifdef __CYGWIN__ @@ -1986,7 +2797,10 @@ Perl_cando(pTHX_ Mode_t mode, bool effective, const Stat_t *statbufp) static bool S_ingroup(pTHX_ Gid_t testgid, bool effective) { - dVAR; +#ifndef PERL_IMPLICIT_SYS + /* PERL_IMPLICIT_SYS like Win32: getegid() etc. require the context. */ + PERL_UNUSED_CONTEXT; +#endif if (testgid == (effective ? PerlProc_getegid() : PerlProc_getgid())) return TRUE; #ifdef HAS_GETGROUPS @@ -1996,15 +2810,17 @@ S_ingroup(pTHX_ Gid_t testgid, bool effective) bool rc = FALSE; anum = getgroups(0, gary); - Newx(gary, anum, Groups_t); - anum = getgroups(anum, gary); - while (--anum >= 0) - if (gary[anum] == testgid) { - rc = TRUE; - break; - } + if (anum > 0) { + Newx(gary, anum, Groups_t); + anum = getgroups(anum, gary); + while (--anum >= 0) + if (gary[anum] == testgid) { + rc = TRUE; + break; + } - Safefree(gary); + Safefree(gary); + } return rc; } #else @@ -2017,7 +2833,6 @@ S_ingroup(pTHX_ Gid_t testgid, bool effective) I32 Perl_do_ipcget(pTHX_ I32 optype, SV **mark, SV **sp) { - dVAR; const key_t key = (key_t)SvNVx(*++mark); SV *nsv = optype == OP_MSGGET ? NULL : *++mark; const I32 flags = SvIVx(*++mark); @@ -2052,7 +2867,6 @@ Perl_do_ipcget(pTHX_ I32 optype, SV **mark, SV **sp) I32 Perl_do_ipcctl(pTHX_ I32 optype, SV **mark, SV **sp) { - dVAR; char *a; I32 ret = -1; const I32 id = SvIVx(*++mark); @@ -2151,11 +2965,16 @@ Perl_do_ipcctl(pTHX_ I32 optype, SV **mark, SV **sp) #ifdef Semctl union semun unsemds; + if(cmd == SETVAL) { + unsemds.val = PTR2nat(a); + } + else { #ifdef EXTRA_F_IN_SEMUN_BUF - unsemds.buff = (struct semid_ds *)a; + unsemds.buff = (struct semid_ds *)a; #else - unsemds.buf = (struct semid_ds *)a; + unsemds.buf = (struct semid_ds *)a; #endif + } ret = Semctl(id, n, cmd, unsemds); #else /* diag_listed_as: sem%s not implemented */ @@ -2181,7 +3000,6 @@ Perl_do_ipcctl(pTHX_ I32 optype, SV **mark, SV **sp) I32 Perl_do_msgsnd(pTHX_ SV **mark, SV **sp) { - dVAR; #ifdef HAS_MSG STRLEN len; const I32 id = SvIVx(*++mark); @@ -2196,7 +3014,12 @@ Perl_do_msgsnd(pTHX_ SV **mark, SV **sp) if (msize < 0) Perl_croak(aTHX_ "Arg too short for msgsnd"); SETERRNO(0,0); - return msgsnd(id, (struct msgbuf *)mbuf, msize, flags); + if (id >= 0 && flags >= 0) { + return msgsnd(id, (struct msgbuf *)mbuf, msize, flags); + } else { + SETERRNO(EINVAL,LIB_INVARG); + return -1; + } #else PERL_UNUSED_ARG(sp); PERL_UNUSED_ARG(mark); @@ -2210,7 +3033,6 @@ I32 Perl_do_msgrcv(pTHX_ SV **mark, SV **sp) { #ifdef HAS_MSG - dVAR; char *mbuf; long mtype; I32 msize, flags, ret; @@ -2222,7 +3044,7 @@ Perl_do_msgrcv(pTHX_ SV **mark, SV **sp) /* suppress warning when reading into undef var --jhi */ if (! SvOK(mstr)) - sv_setpvs(mstr, ""); + SvPVCLEAR(mstr); msize = SvIVx(*++mark); mtype = (long)SvIVx(*++mark); flags = SvIVx(*++mark); @@ -2230,7 +3052,12 @@ Perl_do_msgrcv(pTHX_ SV **mark, SV **sp) mbuf = SvGROW(mstr, sizeof(long)+msize+1); SETERRNO(0,0); - ret = msgrcv(id, (struct msgbuf *)mbuf, msize, mtype, flags); + if (id >= 0 && msize >= 0 && flags >= 0) { + ret = msgrcv(id, (struct msgbuf *)mbuf, msize, mtype, flags); + } else { + SETERRNO(EINVAL,LIB_INVARG); + ret = -1; + } if (ret >= 0) { SvCUR_set(mstr, sizeof(long)+ret); *SvEND(mstr) = '\0'; @@ -2251,7 +3078,6 @@ I32 Perl_do_semop(pTHX_ SV **mark, SV **sp) { #ifdef HAS_SEM - dVAR; STRLEN opsize; const I32 id = SvIVx(*++mark); SV * const opstr = *++mark; @@ -2284,15 +3110,6 @@ Perl_do_semop(pTHX_ SV **mark, SV **sp) t++; } result = semop(id, temps, nsops); - t = temps; - o = ops; - i = nsops; - while (i--) { - *o++ = t->sem_num; - *o++ = t->sem_op; - *o++ = t->sem_flg; - t++; - } Safefree(temps); return result; } @@ -2306,7 +3123,6 @@ I32 Perl_do_shmio(pTHX_ I32 optype, SV **mark, SV **sp) { #ifdef HAS_SHM - dVAR; char *shm; struct shmid_ds shmds; const I32 id = SvIVx(*++mark); @@ -2325,7 +3141,12 @@ Perl_do_shmio(pTHX_ I32 optype, SV **mark, SV **sp) SETERRNO(EFAULT,SS_ACCVIO); /* can't do as caller requested */ return -1; } - shm = (char *)shmat(id, NULL, (optype == OP_SHMREAD) ? SHM_RDONLY : 0); + if (id >= 0) { + shm = (char *)shmat(id, NULL, (optype == OP_SHMREAD) ? SHM_RDONLY : 0); + } else { + SETERRNO(EINVAL,LIB_INVARG); + return -1; + } if (shm == (char *)-1) /* I hate System V IPC, I really do */ return -1; if (optype == OP_SHMREAD) { @@ -2334,7 +3155,7 @@ Perl_do_shmio(pTHX_ I32 optype, SV **mark, SV **sp) SvGETMAGIC(mstr); SvUPGRADE(mstr, SVt_PV); if (! SvOK(mstr)) - sv_setpvs(mstr, ""); + SvPVCLEAR(mstr); SvPOK_only(mstr); mbuf = SvGROW(mstr, (STRLEN)msize+1); @@ -2370,9 +3191,10 @@ Perl_do_shmio(pTHX_ I32 optype, SV **mark, SV **sp) =for apidoc start_glob Function called by C to spawn a glob (or do the glob inside -perl on VMS). This code used to be inline, but now perl uses C -this glob starter is only used by miniperl during the build process. -Moving it away shrinks pp_hot.c; shrinking pp_hot.c helps speed perl up. +perl on VMS). This code used to be inline, but now perl uses C +this glob starter is only used by miniperl during the build process, +or when PERL_EXTERNAL_GLOB is defined. +Moving it away shrinks F; shrinking F helps speed perl up. =cut */ @@ -2380,7 +3202,6 @@ Moving it away shrinks pp_hot.c; shrinking pp_hot.c helps speed perl up. PerlIO * Perl_start_glob (pTHX_ SV *tmpglob, IO *io) { - dVAR; SV * const tmpcmd = newSV(0); PerlIO *fp; STRLEN len; @@ -2404,49 +3225,38 @@ Perl_vms_start_glob fp = Perl_vms_start_glob(aTHX_ tmpglob, io); #else /* !VMS */ -#ifdef DOSISH -#ifdef OS2 +# ifdef DOSISH +# if defined(OS2) sv_setpv(tmpcmd, "for a in "); sv_catsv(tmpcmd, tmpglob); sv_catpv(tmpcmd, "; do echo \"$a\\0\\c\"; done |"); -#else -#ifdef DJGPP +# elif defined(DJGPP) sv_setpv(tmpcmd, "/dev/dosglob/"); /* File System Extension */ sv_catsv(tmpcmd, tmpglob); -#else +# else sv_setpv(tmpcmd, "perlglob "); sv_catsv(tmpcmd, tmpglob); sv_catpv(tmpcmd, " |"); -#endif /* !DJGPP */ -#endif /* !OS2 */ -#else /* !DOSISH */ -#if defined(CSH) +# endif +# elif defined(CSH) sv_setpvn(tmpcmd, PL_cshname, PL_cshlen); sv_catpv(tmpcmd, " -cf 'set nonomatch; glob "); sv_catsv(tmpcmd, tmpglob); sv_catpv(tmpcmd, "' 2>/dev/null |"); -#else +# else sv_setpv(tmpcmd, "echo "); sv_catsv(tmpcmd, tmpglob); -#if 'z' - 'a' == 25 - sv_catpv(tmpcmd, "|tr -s ' \t\f\r' '\\012\\012\\012\\012'|"); -#else sv_catpv(tmpcmd, "|tr -s ' \t\f\r' '\\n\\n\\n\\n'|"); -#endif -#endif /* !CSH */ -#endif /* !DOSISH */ +# endif /* !DOSISH && !CSH */ { - GV * const envgv = gv_fetchpvs("ENV", 0, SVt_PVHV); - SV ** const home = hv_fetchs(GvHV(envgv), "HOME", 0); - SV ** const path = hv_fetchs(GvHV(envgv), "PATH", 0); - if (home && *home) SvGETMAGIC(*home); - if (path && *path) SvGETMAGIC(*path); - save_hash(gv_fetchpvs("ENV", 0, SVt_PVHV)); - if (home && *home) SvSETMAGIC(*home); - if (path && *path) SvSETMAGIC(*path); - } - (void)do_open(PL_last_in_gv, (char*)SvPVX_const(tmpcmd), SvCUR(tmpcmd), - FALSE, O_RDONLY, 0, NULL); + SV ** const svp = hv_fetchs(GvHVn(PL_envgv), "LS_COLORS", 0); + if (svp && *svp) + save_helem_flags(GvHV(PL_envgv), + newSVpvs_flags("LS_COLORS", SVs_TEMP), svp, + SAVEf_SETMAGIC); + } + (void)do_open6(PL_last_in_gv, SvPVX_const(tmpcmd), SvCUR(tmpcmd), + NULL, NULL, 0); fp = IoIFP(io); #endif /* !VMS */ LEAVE; @@ -2460,11 +3270,5 @@ Perl_vms_start_glob } /* - * Local variables: - * c-indentation-style: bsd - * c-basic-offset: 4 - * indent-tabs-mode: nil - * End: - * * ex: set ts=8 sts=4 sw=4 et: */