mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
This combines 20 cleanup commits from the dev branch. All changed files: - Clean up pointer defererences to sh. - Remove shp arguments from functions. Other notable changes: src/cmd/ksh93/include/shell.h, src/cmd/ksh93/sh/init.c: - On second thought, get rid of the function version of sh_getinterp() as libshell ABI compatibility is moot. We've already been breaking that by reordering the sh struct, so there is no way it's going to work without recompiling. src/cmd/ksh93/sh/name.c: - De-obfuscate the relationship between nv_scan() and scanfilter(). The former just calls the latter as a static function, there's no need to do that via a function pointer and void* type conversions. src/cmd/ksh93/bltins/typeset.c, src/cmd/ksh93/sh/name.c, src/cmd/ksh93/sh/nvdisc.c: - 'struct adata' and 'struct tdata', defined as local struct types in these files, need to have their first three fields in common, the first being a pointer to sh. This is because scanfilter() in name.c accesses these fields via a type conversion. So the sh field needed to be removed in all three at the same time. TODO: de-obfuscate: good practice definition via a header file. src/cmd/ksh93/sh/path.c: - Naming consistency: reserve the path_ function name prefix for externs and rename statics with that prefix. - The default path was sometimes referred to as the standard path. To use one term, rename std_path to defpath and onstdpath() to ondefpath(). - De-obfuscate SHOPT_PFSH conditional code by only calling pf_execve() (was path_pfexecve()) if that is compiled in. src/cmd/ksh93/include/streval.h, src/cmd/ksh93/sh/streval.c: - Rename extern strval() to arith_strval() for consistency. src/cmd/ksh93/sh/string.c: - Remove outdated/incorrect isxdigit() fallback; '#ifnded isxdigit' is not a correct test as isxdigit() is specified as a function. Plus, it's part of C89/C90 which we now require. (re:ac8991e5
) src/cmd/ksh93/sh/suid_exec.c: - Replace an incorrect reference to shgd->current_pid with getpid(); it cannot work as (contrary to its misleading directory placement) suid_exec is an independent libast program with no link to ksh or libshell at all. However, no one noticed because this was in fallback code for ancient systems without setreuid(2). Since that standard function was specified in POSIX Issue 4 Version 2 from 1994, we should remove that fallback code sometime as part of another obsolete code cleanup operation to avoid further bit rot. (re:843b546c
) src/cmd/ksh93/bltins/print.c: genformat(): - Remove preformat[] which was always empty and had no effect. src/cmd/ksh93/shell.3: - Minor copy-edit. - Remove documentation for nonexistent sh.infile_name. A search through ast-open-archive[*] reveals this never existed at all. - Document sh.savexit (== $?). src/cmd/ksh93/shell.3, src/cmd/ksh93/include/shell.h, src/cmd/ksh93/sh/init.c: - Remove sh.gd/shgd; this is now unused and was never documented or exposed in the shell.h public interface. - sh_sigcheck() was documented in shell.3 as taking no arguments whereas in the actual code it took a shp argument. I decided to go with the documentation. - That leaves sh_parse() as the only documented function that still takes an shp argument. I'm just going to go ahead and remove it for consistency, reverting sh_parse() to its pre-2003 spec. - Remove undocumented/unused sh_bltin_tree() function which simply returned sh.bltin_tree. - Bump SH_VERSION to 20220106.
2711 lines
57 KiB
C
2711 lines
57 KiB
C
/***********************************************************************
|
|
* *
|
|
* This software is part of the ast package *
|
|
* Copyright (c) 1982-2012 AT&T Intellectual Property *
|
|
* Copyright (c) 2020-2021 Contributors to ksh 93u+m *
|
|
* and is licensed under the *
|
|
* Eclipse Public License, Version 1.0 *
|
|
* by AT&T Intellectual Property *
|
|
* *
|
|
* A copy of the License is available at *
|
|
* http://www.eclipse.org/org/documents/epl-v10.html *
|
|
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
|
|
* *
|
|
* Information and Software Systems Research *
|
|
* AT&T Research *
|
|
* Florham Park NJ *
|
|
* *
|
|
* David Korn <dgk@research.att.com> *
|
|
* *
|
|
***********************************************************************/
|
|
|
|
/*
|
|
* Input/output file processing
|
|
*
|
|
* David Korn
|
|
* AT&T Labs
|
|
*
|
|
*/
|
|
|
|
#include "defs.h"
|
|
#include <fcin.h>
|
|
#include <ls.h>
|
|
#include <stdarg.h>
|
|
#include <regex.h>
|
|
#include "variables.h"
|
|
#include "path.h"
|
|
#include "io.h"
|
|
#include "jobs.h"
|
|
#include "shlex.h"
|
|
#include "shnodes.h"
|
|
#include "history.h"
|
|
#include "edit.h"
|
|
#include "timeout.h"
|
|
#include "FEATURE/externs"
|
|
#include "FEATURE/dynamic"
|
|
#include "FEATURE/poll"
|
|
|
|
#ifdef FNDELAY
|
|
# ifdef EAGAIN
|
|
# if EAGAIN!=EWOULDBLOCK
|
|
# undef EAGAIN
|
|
# define EAGAIN EWOULDBLOCK
|
|
# endif
|
|
# else
|
|
# define EAGAIN EWOULDBLOCK
|
|
# endif /* EAGAIN */
|
|
# ifndef O_NONBLOCK
|
|
# define O_NONBLOCK FNDELAY
|
|
# endif /* !O_NONBLOCK */
|
|
#endif /* FNDELAY */
|
|
|
|
#ifndef O_SERVICE
|
|
# define O_SERVICE O_NOCTTY
|
|
#endif
|
|
|
|
#ifndef ERROR_PIPE
|
|
#ifdef ECONNRESET
|
|
#define ERROR_PIPE(e) ((e)==EPIPE||(e)==ECONNRESET||(e)==EIO)
|
|
#else
|
|
#define ERROR_PIPE(e) ((e)==EPIPE||(e)==EIO)
|
|
#endif
|
|
#endif
|
|
|
|
#define RW_ALL (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)
|
|
|
|
static void *timeout;
|
|
static int (*fdnotify)(int,int);
|
|
|
|
#if defined(_lib_socket) && defined(_sys_socket) && defined(_hdr_netinet_in)
|
|
# include <sys/socket.h>
|
|
# include <netdb.h>
|
|
# include <netinet/in.h>
|
|
# if !defined(htons) && !_lib_htons
|
|
# define htons(x) (x)
|
|
# endif
|
|
# if !defined(htonl) && !_lib_htonl
|
|
# define htonl(x) (x)
|
|
# endif
|
|
# if _pipe_socketpair && !_stream_peek
|
|
# ifndef SHUT_RD
|
|
# define SHUT_RD 0
|
|
# endif
|
|
# ifndef SHUT_WR
|
|
# define SHUT_WR 1
|
|
# endif
|
|
# if _socketpair_shutdown_mode
|
|
# define pipe(v) ((socketpair(AF_UNIX,SOCK_STREAM,0,v)<0||shutdown((v)[1],SHUT_RD)<0||fchmod((v)[1],S_IWUSR)<0||shutdown((v)[0],SHUT_WR)<0||fchmod((v)[0],S_IRUSR)<0)?(-1):0)
|
|
# else
|
|
# define pipe(v) ((socketpair(AF_UNIX,SOCK_STREAM,0,v)<0||shutdown((v)[1],SHUT_RD)<0||shutdown((v)[0],SHUT_WR)<0)?(-1):0)
|
|
# endif
|
|
# endif
|
|
|
|
#if !_lib_getaddrinfo
|
|
|
|
#undef EAI_SYSTEM
|
|
|
|
#define EAI_SYSTEM 1
|
|
|
|
#undef addrinfo
|
|
#undef getaddrinfo
|
|
#undef freeaddrinfo
|
|
|
|
#define addrinfo local_addrinfo
|
|
#define getaddrinfo local_getaddrinfo
|
|
#define freeaddrinfo local_freeaddrinfo
|
|
|
|
struct addrinfo
|
|
{
|
|
int ai_flags;
|
|
int ai_family;
|
|
int ai_socktype;
|
|
int ai_protocol;
|
|
socklen_t ai_addrlen;
|
|
struct sockaddr* ai_addr;
|
|
struct addrinfo* ai_next;
|
|
};
|
|
|
|
static int
|
|
getaddrinfo(const char* node, const char* service, const struct addrinfo* hint, struct addrinfo **addr)
|
|
{
|
|
unsigned long ip_addr = 0;
|
|
unsigned short ip_port = 0;
|
|
struct addrinfo* ap;
|
|
struct hostent* hp;
|
|
struct sockaddr_in* ip;
|
|
char* prot;
|
|
long n;
|
|
|
|
if (!(hp = gethostbyname(node)) || hp->h_addrtype!=AF_INET || hp->h_length>sizeof(struct in_addr))
|
|
{
|
|
errno = EADDRNOTAVAIL;
|
|
return EAI_SYSTEM;
|
|
}
|
|
ip_addr = (unsigned long)((struct in_addr*)hp->h_addr)->s_addr;
|
|
if ((n = strtol(service, &prot, 10)) > 0 && n <= USHRT_MAX && !*prot)
|
|
ip_port = htons((unsigned short)n);
|
|
else
|
|
{
|
|
struct servent* sp;
|
|
const char* protocol = 0;
|
|
|
|
if (hint)
|
|
switch (hint->ai_socktype)
|
|
{
|
|
case SOCK_STREAM:
|
|
switch (hint->ai_protocol)
|
|
{
|
|
case 0:
|
|
protocol = "tcp";
|
|
break;
|
|
#ifdef IPPROTO_SCTP
|
|
case IPPROTO_SCTP:
|
|
protocol = "sctp";
|
|
break;
|
|
#endif
|
|
}
|
|
break;
|
|
case SOCK_DGRAM:
|
|
protocol = "udp";
|
|
break;
|
|
}
|
|
if (!protocol)
|
|
{
|
|
errno = EPROTONOSUPPORT;
|
|
return 1;
|
|
}
|
|
if (sp = getservbyname(service, protocol))
|
|
ip_port = sp->s_port;
|
|
}
|
|
if (!ip_port)
|
|
{
|
|
errno = EADDRNOTAVAIL;
|
|
return EAI_SYSTEM;
|
|
}
|
|
ap = sh_newof(0, struct addrinfo, 1, sizeof(struct sockaddr_in));
|
|
if (hint)
|
|
*ap = *hint;
|
|
ap->ai_family = hp->h_addrtype;
|
|
ap->ai_addrlen = sizeof(struct sockaddr_in);
|
|
ap->ai_addr = (struct sockaddr *)(ap+1);
|
|
ip = (struct sockaddr_in *)ap->ai_addr;
|
|
ip->sin_family = AF_INET;
|
|
ip->sin_port = ip_port;
|
|
ip->sin_addr.s_addr = ip_addr;
|
|
*addr = ap;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
freeaddrinfo(struct addrinfo* ap)
|
|
{
|
|
if (ap)
|
|
free(ap);
|
|
}
|
|
|
|
#endif /* !_lib_getaddrinfo */
|
|
|
|
static int onintr(struct addrinfo*);
|
|
|
|
/*
|
|
* return <protocol>/<host>/<service> fd
|
|
* If called with flags==O_NONBLOCK return 1 if protocol is supported
|
|
*/
|
|
|
|
static int
|
|
inetopen(const char* path, int flags)
|
|
{
|
|
register char* s;
|
|
register char* t;
|
|
int fd;
|
|
int oerrno;
|
|
struct addrinfo hint;
|
|
struct addrinfo* addr;
|
|
struct addrinfo* p;
|
|
int server = !!(flags&O_SERVICE);
|
|
|
|
memset(&hint, 0, sizeof(hint));
|
|
hint.ai_family = PF_UNSPEC;
|
|
switch (path[0])
|
|
{
|
|
#ifdef IPPROTO_SCTP
|
|
case 's':
|
|
if (path[1]!='c' || path[2]!='t' || path[3]!='p' || path[4]!='/')
|
|
{
|
|
errno = ENOTDIR;
|
|
return -1;
|
|
}
|
|
hint.ai_socktype = SOCK_STREAM;
|
|
hint.ai_protocol = IPPROTO_SCTP;
|
|
path += 5;
|
|
break;
|
|
#endif
|
|
case 't':
|
|
if (path[1]!='c' || path[2]!='p' || path[3]!='/')
|
|
{
|
|
errno = ENOTDIR;
|
|
return -1;
|
|
}
|
|
hint.ai_socktype = SOCK_STREAM;
|
|
path += 4;
|
|
break;
|
|
case 'u':
|
|
if (path[1]!='d' || path[2]!='p' || path[3]!='/')
|
|
{
|
|
errno = ENOTDIR;
|
|
return -1;
|
|
}
|
|
hint.ai_socktype = SOCK_DGRAM;
|
|
path += 4;
|
|
break;
|
|
default:
|
|
errno = ENOTDIR;
|
|
return -1;
|
|
}
|
|
if(flags==O_NONBLOCK)
|
|
return 1;
|
|
s = sh_strdup(path);
|
|
if (t = strchr(s, '/'))
|
|
{
|
|
*t++ = 0;
|
|
if (streq(s, "local"))
|
|
s = sh_strdup("localhost");
|
|
fd = getaddrinfo(s, t, &hint, &addr);
|
|
}
|
|
else
|
|
fd = -1;
|
|
free(s);
|
|
if (fd)
|
|
{
|
|
if (fd != EAI_SYSTEM)
|
|
errno = ENOTDIR;
|
|
return -1;
|
|
}
|
|
oerrno = errno;
|
|
errno = 0;
|
|
fd = -1;
|
|
for (p = addr; p; p = p->ai_next)
|
|
{
|
|
/*
|
|
* some APIs don't take the hint
|
|
*/
|
|
|
|
if (!p->ai_protocol)
|
|
p->ai_protocol = hint.ai_protocol;
|
|
if (!p->ai_socktype)
|
|
p->ai_socktype = hint.ai_socktype;
|
|
while ((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) >= 0)
|
|
{
|
|
if (server && !bind(fd, p->ai_addr, p->ai_addrlen) && !listen(fd, 5) || !server && !connect(fd, p->ai_addr, p->ai_addrlen))
|
|
goto done;
|
|
close(fd);
|
|
fd = -1;
|
|
if (errno != EINTR)
|
|
break;
|
|
if (onintr(addr))
|
|
goto done;
|
|
}
|
|
}
|
|
done:
|
|
freeaddrinfo(addr);
|
|
if (fd >= 0)
|
|
errno = oerrno;
|
|
return fd;
|
|
}
|
|
|
|
#else
|
|
|
|
#undef O_SERVICE
|
|
|
|
#endif
|
|
|
|
struct fdsave
|
|
{
|
|
int orig_fd; /* original file descriptor */
|
|
int save_fd; /* saved file descriptor */
|
|
int subshell; /* saved for subshell */
|
|
char *tname; /* name used with >; */
|
|
};
|
|
|
|
static int subexcept(Sfio_t*, int, void*, Sfdisc_t*);
|
|
static int eval_exceptf(Sfio_t*, int, void*, Sfdisc_t*);
|
|
static int slowexcept(Sfio_t*, int, void*, Sfdisc_t*);
|
|
static int pipeexcept(Sfio_t*, int, void*, Sfdisc_t*);
|
|
static ssize_t piperead(Sfio_t*, void*, size_t, Sfdisc_t*);
|
|
static ssize_t slowread(Sfio_t*, void*, size_t, Sfdisc_t*);
|
|
static ssize_t subread(Sfio_t*, void*, size_t, Sfdisc_t*);
|
|
static ssize_t tee_write(Sfio_t*,const void*,size_t,Sfdisc_t*);
|
|
static int io_prompt(Sfio_t*,int);
|
|
static int io_heredoc(register struct ionod*, const char*, int);
|
|
static void sftrack(Sfio_t*,int,void*);
|
|
static const Sfdisc_t eval_disc = { NULL, NULL, NULL, eval_exceptf, NULL};
|
|
static Sfdisc_t tee_disc = {NULL,tee_write,NULL,NULL,NULL};
|
|
static Sfio_t *subopen(Sfio_t*, off_t, long);
|
|
static const Sfdisc_t sub_disc = { subread, 0, 0, subexcept, 0 };
|
|
|
|
struct subfile
|
|
{
|
|
Sfdisc_t disc;
|
|
Sfio_t *oldsp;
|
|
off_t offset;
|
|
long size;
|
|
long left;
|
|
};
|
|
|
|
struct Eof
|
|
{
|
|
Namfun_t hdr;
|
|
int fd;
|
|
};
|
|
|
|
static Sfdouble_t nget_cur_eof(register Namval_t* np, Namfun_t *fp)
|
|
{
|
|
struct Eof *ep = (struct Eof*)fp;
|
|
Sfoff_t end, cur =lseek(ep->fd, (Sfoff_t)0, SEEK_CUR);
|
|
if(*np->nvname=='C')
|
|
return((Sfdouble_t)cur);
|
|
if(cur<0)
|
|
return((Sfdouble_t)-1);
|
|
end =lseek(ep->fd, (Sfoff_t)0, SEEK_END);
|
|
lseek(ep->fd, (Sfoff_t)0, SEEK_CUR);
|
|
return((Sfdouble_t)end);
|
|
}
|
|
|
|
static const Namdisc_t EOF_disc = { sizeof(struct Eof), 0, 0, nget_cur_eof};
|
|
|
|
#define MATCH_BUFF (64*1024)
|
|
struct Match
|
|
{
|
|
Sfoff_t offset;
|
|
char *base;
|
|
};
|
|
|
|
static struct fdsave *filemap;
|
|
static short filemapsize;
|
|
|
|
#define PSEUDOFD (SHRT_MAX)
|
|
|
|
/* ======== input output and file copying ======== */
|
|
|
|
int sh_iovalidfd(int fd)
|
|
{
|
|
Sfio_t **sftable = sh.sftable;
|
|
int max,n, **fdptrs = sh.fdptrs;
|
|
unsigned char *fdstatus = sh.fdstatus;
|
|
if(fd<0)
|
|
return(0);
|
|
if(fd < sh.lim.open_max)
|
|
return(1);
|
|
max = (int)astconf_long(CONF_OPEN_MAX);
|
|
if(fd >= max)
|
|
{
|
|
errno = EBADF;
|
|
return(0);
|
|
}
|
|
n = (fd+16)&~0xf;
|
|
if(n > max)
|
|
n = max;
|
|
max = sh.lim.open_max;
|
|
sh.sftable = (Sfio_t**)sh_calloc((n+1)*(sizeof(int*)+sizeof(Sfio_t*)+1),1);
|
|
if(max)
|
|
memcpy(sh.sftable,sftable,max*sizeof(Sfio_t*));
|
|
sh.fdptrs = (int**)(&sh.sftable[n]);
|
|
if(max)
|
|
memcpy(sh.fdptrs,fdptrs,max*sizeof(int*));
|
|
sh.fdstatus = (unsigned char*)(&sh.fdptrs[n]);
|
|
if(max)
|
|
memcpy(sh.fdstatus,fdstatus,max);
|
|
if(sftable)
|
|
free((void*)sftable);
|
|
sh.lim.open_max = n;
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* Return the lowest numbered fd that is equal to or greater than
|
|
* the requested 'min_fd' and which is not currently in use.
|
|
*/
|
|
int sh_iosafefd(int min_fd)
|
|
{
|
|
register int fd;
|
|
while(1)
|
|
{
|
|
if(fcntl(min_fd, F_GETFD) == -1)
|
|
{
|
|
for(fd = 0; fd < sh.topfd; fd++)
|
|
{
|
|
if (filemap[fd].save_fd == min_fd || filemap[fd].orig_fd == min_fd)
|
|
break;
|
|
}
|
|
if(fd == sh.topfd)
|
|
break;
|
|
}
|
|
min_fd++;
|
|
}
|
|
return(min_fd);
|
|
}
|
|
|
|
int sh_inuse(int fd)
|
|
{
|
|
return(fd < sh.lim.open_max && sh.fdptrs[fd]);
|
|
}
|
|
|
|
void sh_ioinit(void)
|
|
{
|
|
filemapsize = 8;
|
|
filemap = (struct fdsave*)sh_malloc(filemapsize*sizeof(struct fdsave));
|
|
sh_iovalidfd(16);
|
|
sh.sftable[0] = sfstdin;
|
|
sh.sftable[1] = sfstdout;
|
|
sh.sftable[2] = sfstderr;
|
|
sfnotify(sftrack);
|
|
sh_iostream(0);
|
|
sh_iostream(1);
|
|
/* all write streams are in the same pool and share outbuff */
|
|
sh.outpool = sfopen(NIL(Sfio_t*),NIL(char*),"sw"); /* pool identifier */
|
|
sh.outbuff = (char*)sh_malloc(IOBSIZE+4);
|
|
sh.errbuff = (char*)sh_malloc(IOBSIZE/4);
|
|
sfsetbuf(sfstderr,sh.errbuff,IOBSIZE/4);
|
|
sfsetbuf(sfstdout,sh.outbuff,IOBSIZE);
|
|
sfpool(sfstdout,sh.outpool,SF_WRITE);
|
|
sfpool(sfstderr,sh.outpool,SF_WRITE);
|
|
sfset(sfstdout,SF_LINE,0);
|
|
sfset(sfstderr,SF_LINE,0);
|
|
sfset(sfstdin,SF_SHARE|SF_PUBLIC,1);
|
|
}
|
|
|
|
/*
|
|
* Handle output stream exceptions
|
|
*/
|
|
static int outexcept(register Sfio_t *iop,int type,void *data,Sfdisc_t *handle)
|
|
{
|
|
static int active = 0;
|
|
if(type==SF_DPOP || type==SF_FINAL)
|
|
free((void*)handle);
|
|
else if(type==SF_WRITE && (*(ssize_t*)data)<0 && sffileno(iop)!=2)
|
|
switch (errno)
|
|
{
|
|
case EINTR:
|
|
case EPIPE:
|
|
#ifdef ECONNRESET
|
|
case ECONNRESET:
|
|
#endif
|
|
#ifdef ESHUTDOWN
|
|
case ESHUTDOWN:
|
|
#endif
|
|
break;
|
|
default:
|
|
if(!active)
|
|
{
|
|
int mode = ((struct checkpt*)sh.jmplist)->mode;
|
|
int save = errno;
|
|
active = 1;
|
|
((struct checkpt*)sh.jmplist)->mode = 0;
|
|
sfpurge(iop);
|
|
sfpool(iop,NIL(Sfio_t*),SF_WRITE);
|
|
errno = save;
|
|
/*
|
|
* Note: UNREACHABLE() is avoided here because this errormsg() call will return.
|
|
* The ERROR_system flag causes sh_exit() to be called, but if sh.jmplist->mode
|
|
* is 0 (see above), then sh_exit() neither exits nor longjmps. See fault.c.
|
|
*/
|
|
errormsg(SH_DICT,ERROR_system(1),e_badwrite,sffileno(iop));
|
|
active = 0;
|
|
((struct checkpt*)sh.jmplist)->mode = mode;
|
|
sh_exit(1);
|
|
}
|
|
return(-1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* create or initialize a stream corresponding to descriptor <fd>
|
|
* a buffer with room for a sentinel is allocated for a read stream.
|
|
* A discipline is inserted when read stream is a tty or a pipe
|
|
* For output streams, the buffer is set to sh.output and put into
|
|
* the sh.outpool synchronization pool
|
|
*/
|
|
Sfio_t *sh_iostream(register int fd)
|
|
{
|
|
register Sfio_t *iop;
|
|
register int status = sh_iocheckfd(fd);
|
|
register int flags = SF_WRITE;
|
|
char *bp;
|
|
Sfdisc_t *dp;
|
|
if(status==IOCLOSE)
|
|
{
|
|
switch(fd)
|
|
{
|
|
case 0:
|
|
return(sfstdin);
|
|
case 1:
|
|
return(sfstdout);
|
|
case 2:
|
|
return(sfstderr);
|
|
}
|
|
return(NIL(Sfio_t*));
|
|
}
|
|
if(status&IOREAD)
|
|
{
|
|
bp = (char *)sh_malloc(IOBSIZE+1);
|
|
flags |= SF_READ;
|
|
if(!(status&IOWRITE))
|
|
flags &= ~SF_WRITE;
|
|
}
|
|
else
|
|
bp = sh.outbuff;
|
|
if(status&IODUP)
|
|
flags |= SF_SHARE|SF_PUBLIC;
|
|
if((iop = sh.sftable[fd]) && sffileno(iop)>=0)
|
|
{
|
|
if(status&IOTTY)
|
|
sfset(iop,SF_LINE|SF_WCWIDTH,1);
|
|
sfsetbuf(iop, bp, IOBSIZE);
|
|
}
|
|
else if(!(iop=sfnew((fd<=2?iop:0),bp,IOBSIZE,fd,flags)))
|
|
return(NIL(Sfio_t*));
|
|
dp = sh_newof(0,Sfdisc_t,1,0);
|
|
if(status&IOREAD)
|
|
{
|
|
sfset(iop,SF_MALLOC,1);
|
|
if(!(status&IOWRITE))
|
|
sfset(iop,SF_IOCHECK,1);
|
|
dp->exceptf = slowexcept;
|
|
if(status&IOTTY)
|
|
dp->readf = slowread;
|
|
else if(status&IONOSEEK)
|
|
{
|
|
dp->readf = piperead;
|
|
sfset(iop, SF_IOINTR,1);
|
|
}
|
|
else
|
|
dp->readf = 0;
|
|
dp->seekf = 0;
|
|
dp->writef = 0;
|
|
}
|
|
else
|
|
{
|
|
if((status&(IONOSEEK|IOTTY)) == IONOSEEK)
|
|
dp->exceptf = pipeexcept;
|
|
else
|
|
dp->exceptf = outexcept;
|
|
sfpool(iop,sh.outpool,SF_WRITE);
|
|
}
|
|
sfdisc(iop,dp);
|
|
sh.sftable[fd] = iop;
|
|
return(iop);
|
|
}
|
|
|
|
/*
|
|
* preserve the file descriptor or stream by moving it
|
|
*/
|
|
static void io_preserve(register Sfio_t *sp, register int f2)
|
|
{
|
|
register int fd;
|
|
if(sp)
|
|
fd = sfsetfd(sp,10);
|
|
else
|
|
fd = sh_fcntl(f2,F_DUPFD,10);
|
|
if(f2==sh.infd)
|
|
sh.infd = fd;
|
|
if(fd<0)
|
|
{
|
|
sh.toomany = 1;
|
|
((struct checkpt*)sh.jmplist)->mode = SH_JMPERREXIT;
|
|
errormsg(SH_DICT,ERROR_system(1),e_toomany);
|
|
UNREACHABLE();
|
|
}
|
|
if(f2 >= sh.lim.open_max)
|
|
sh_iovalidfd(f2);
|
|
if(sh.fdptrs[fd]=sh.fdptrs[f2])
|
|
{
|
|
if(f2==job.fd)
|
|
job.fd=fd;
|
|
*sh.fdptrs[fd] = fd;
|
|
sh.fdptrs[f2] = 0;
|
|
}
|
|
sh.sftable[fd] = sp;
|
|
sh.fdstatus[fd] = sh.fdstatus[f2];
|
|
if(fcntl(f2,F_GETFD,0)&1)
|
|
{
|
|
fcntl(fd,F_SETFD,FD_CLOEXEC);
|
|
sh.fdstatus[fd] |= IOCLEX;
|
|
}
|
|
sh.sftable[f2] = 0;
|
|
}
|
|
|
|
/*
|
|
* Given a file descriptor <f1>, move it to a file descriptor number <f2>
|
|
* If <f2> is needed move it, otherwise it is closed first.
|
|
* The original stream <f1> is closed.
|
|
* The new file descriptor <f2> is returned;
|
|
*/
|
|
int sh_iorenumber(register int f1,register int f2)
|
|
{
|
|
register Sfio_t *sp = sh.sftable[f2];
|
|
if(f1!=f2)
|
|
{
|
|
/* see whether file descriptor is in use */
|
|
if(sh_inuse(f2) || (f2>2 && sp))
|
|
{
|
|
if(!(sh.inuse_bits&(1<<f2)))
|
|
io_preserve(sp,f2);
|
|
sp = 0;
|
|
}
|
|
else if(f2==0)
|
|
sh.st.ioset = 1;
|
|
sh_close(f2);
|
|
if(f2<=2 && sp)
|
|
{
|
|
register Sfio_t *spnew = sh_iostream(f1);
|
|
sh.fdstatus[f2] = (sh.fdstatus[f1]&~IOCLEX);
|
|
sfsetfd(spnew,f2);
|
|
sfswap(spnew,sp);
|
|
sfset(sp,SF_SHARE|SF_PUBLIC,1);
|
|
}
|
|
else
|
|
{
|
|
sh.fdstatus[f2] = (sh.fdstatus[f1]&~IOCLEX);
|
|
if((f2 = sh_fcntl(f1,F_DUPFD, f2)) < 0)
|
|
{
|
|
errormsg(SH_DICT,ERROR_system(1),e_file+4);
|
|
UNREACHABLE();
|
|
}
|
|
else if(f2 <= 2)
|
|
sh_iostream(f2);
|
|
}
|
|
if(sp)
|
|
sh.sftable[f1] = 0;
|
|
if(sh.fdstatus[f1]!=IOCLOSE)
|
|
sh_close(f1);
|
|
}
|
|
else if(sp)
|
|
{
|
|
sfsetfd(sp,f2);
|
|
if(f2<=2)
|
|
sfset(sp,SF_SHARE|SF_PUBLIC,1);
|
|
}
|
|
if(f2>=sh.lim.open_max)
|
|
sh_iovalidfd(f2);
|
|
return(f2);
|
|
}
|
|
|
|
/*
|
|
* close a file descriptor and update stream table and attributes
|
|
*/
|
|
int sh_close(register int fd)
|
|
{
|
|
register Sfio_t *sp;
|
|
register int r = 0;
|
|
if(fd<0)
|
|
{
|
|
errno = EBADF;
|
|
return(-1);
|
|
}
|
|
if(fd >= sh.lim.open_max)
|
|
sh_iovalidfd(fd);
|
|
if(!(sp=sh.sftable[fd]) || sfclose(sp) < 0)
|
|
{
|
|
int err=errno;
|
|
if(fdnotify)
|
|
(*fdnotify)(fd,SH_FDCLOSE);
|
|
while((r=close(fd)) < 0 && errno==EINTR)
|
|
errno = err;
|
|
}
|
|
if(fd>2)
|
|
sh.sftable[fd] = 0;
|
|
sh.fdstatus[fd] = IOCLOSE;
|
|
if(sh.fdptrs[fd])
|
|
*sh.fdptrs[fd] = -1;
|
|
sh.fdptrs[fd] = 0;
|
|
if(fd < 10)
|
|
sh.inuse_bits &= ~(1<<fd);
|
|
return(r);
|
|
}
|
|
|
|
#ifdef O_SERVICE
|
|
|
|
static int
|
|
onintr(struct addrinfo* addr)
|
|
{
|
|
if (sh.trapnote&SH_SIGSET)
|
|
{
|
|
freeaddrinfo(addr);
|
|
sh_exit(SH_EXITSIG);
|
|
return -1;
|
|
}
|
|
if (sh.trapnote)
|
|
sh_chktrap();
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Mimic open(2) with checks for pseudo /dev/ files.
|
|
*/
|
|
int sh_open(register const char *path, int flags, ...)
|
|
{
|
|
Sfio_t *sp;
|
|
register int fd = -1;
|
|
mode_t mode;
|
|
char *e;
|
|
va_list ap;
|
|
va_start(ap, flags);
|
|
mode = (flags & O_CREAT) ? va_arg(ap, int) : 0;
|
|
va_end(ap);
|
|
errno = 0;
|
|
if(path==0)
|
|
{
|
|
errno = EFAULT;
|
|
return(-1);
|
|
}
|
|
if(*path==0)
|
|
{
|
|
errno = ENOENT;
|
|
return(-1);
|
|
}
|
|
if (path[0]=='/' && path[1]=='d' && path[2]=='e' && path[3]=='v' && path[4]=='/')
|
|
{
|
|
switch (path[5])
|
|
{
|
|
case 'f':
|
|
if (path[6]=='d' && path[7]=='/')
|
|
{
|
|
if(flags==O_NONBLOCK)
|
|
return(1);
|
|
fd = (int)strtol(path+8, &e, 10);
|
|
if (*e)
|
|
fd = -1;
|
|
}
|
|
break;
|
|
case 's':
|
|
if (path[6]=='t' && path[7]=='d')
|
|
switch (path[8])
|
|
{
|
|
case 'e':
|
|
if (path[9]=='r' && path[10]=='r' && !path[11])
|
|
fd = 2;
|
|
break;
|
|
case 'i':
|
|
if (path[9]=='n' && !path[10])
|
|
fd = 0;
|
|
break;
|
|
case 'o':
|
|
if (path[9]=='u' && path[10]=='t' && !path[11])
|
|
fd = 1;
|
|
break;
|
|
}
|
|
}
|
|
#ifdef O_SERVICE
|
|
if (fd < 0)
|
|
{
|
|
if ((fd = inetopen(path+5, flags)) < 0 && errno != ENOTDIR)
|
|
return -1;
|
|
if(flags==O_NONBLOCK)
|
|
return(fd>=0);
|
|
if (fd >= 0)
|
|
goto ok;
|
|
}
|
|
if(flags==O_NONBLOCK)
|
|
return(0);
|
|
#endif
|
|
}
|
|
if (fd >= 0)
|
|
{
|
|
int nfd= -1;
|
|
if (flags & O_CREAT)
|
|
{
|
|
struct stat st;
|
|
if (stat(path,&st) >=0)
|
|
nfd = open(path,flags,st.st_mode);
|
|
}
|
|
else
|
|
nfd = open(path,flags);
|
|
if(nfd>=0)
|
|
{
|
|
fd = nfd;
|
|
goto ok;
|
|
}
|
|
if((mode=sh_iocheckfd(fd))==IOCLOSE)
|
|
return(-1);
|
|
flags &= O_ACCMODE;
|
|
if(!(mode&IOWRITE) && ((flags==O_WRONLY) || (flags==O_RDWR)))
|
|
return(-1);
|
|
if(!(mode&IOREAD) && ((flags==O_RDONLY) || (flags==O_RDWR)))
|
|
return(-1);
|
|
if((fd=dup(fd))<0)
|
|
return(-1);
|
|
}
|
|
else
|
|
{
|
|
#if SHOPT_REGRESS
|
|
char buf[PATH_MAX];
|
|
if(strncmp(path,"/etc/",5)==0)
|
|
{
|
|
sfsprintf(buf, sizeof(buf), "%s%s", sh_regress_etc(path, __LINE__, __FILE__), path+4);
|
|
path = buf;
|
|
}
|
|
#endif
|
|
while((fd = open(path, flags, mode)) < 0)
|
|
if(errno!=EINTR || sh.trapnote)
|
|
return(-1);
|
|
}
|
|
ok:
|
|
flags &= O_ACCMODE;
|
|
if(flags==O_WRONLY)
|
|
mode = IOWRITE;
|
|
else if(flags==O_RDWR)
|
|
mode = (IOREAD|IOWRITE);
|
|
else
|
|
mode = IOREAD;
|
|
if(fd >= sh.lim.open_max)
|
|
sh_iovalidfd(fd);
|
|
if((sp = sh.sftable[fd]) && (sfset(sp,0,0) & SF_STRING))
|
|
{
|
|
int n,err=errno;
|
|
if((n = sh_fcntl(fd,F_DUPFD,10)) >= 10)
|
|
{
|
|
while(close(fd) < 0 && errno == EINTR)
|
|
errno = err;
|
|
fd = n;
|
|
}
|
|
}
|
|
sh.fdstatus[fd] = mode;
|
|
return(fd);
|
|
}
|
|
|
|
/*
|
|
* Open a file for reading
|
|
* On failure, print message.
|
|
*/
|
|
int sh_chkopen(register const char *name)
|
|
{
|
|
register int fd = sh_open(name,O_RDONLY,0);
|
|
if(fd < 0)
|
|
{
|
|
errormsg(SH_DICT,ERROR_system(1),e_open,name);
|
|
UNREACHABLE();
|
|
}
|
|
return(fd);
|
|
}
|
|
|
|
/*
|
|
* move open file descriptor to a number > 2
|
|
*/
|
|
int sh_iomovefd(register int fdold)
|
|
{
|
|
register int fdnew;
|
|
if(fdold >= sh.lim.open_max)
|
|
sh_iovalidfd(fdold);
|
|
if(fdold<0 || fdold>2)
|
|
return(fdold);
|
|
fdnew = sh_iomovefd(dup(fdold));
|
|
sh.fdstatus[fdnew] = (sh.fdstatus[fdold]&~IOCLEX);
|
|
close(fdold);
|
|
sh.fdstatus[fdold] = IOCLOSE;
|
|
return(fdnew);
|
|
}
|
|
|
|
/*
|
|
* create a pipe and print message on failure
|
|
*/
|
|
int sh_pipe(register int pv[])
|
|
{
|
|
int fd[2];
|
|
if(pipe(fd)<0 || (pv[0]=fd[0])<0 || (pv[1]=fd[1])<0)
|
|
{
|
|
errormsg(SH_DICT,ERROR_system(1),e_pipe);
|
|
UNREACHABLE();
|
|
}
|
|
pv[0] = sh_iomovefd(pv[0]);
|
|
pv[1] = sh_iomovefd(pv[1]);
|
|
sh.fdstatus[pv[0]] = IONOSEEK|IOREAD;
|
|
sh.fdstatus[pv[1]] = IONOSEEK|IOWRITE;
|
|
sh_subsavefd(pv[0]);
|
|
sh_subsavefd(pv[1]);
|
|
return(0);
|
|
}
|
|
|
|
#ifndef pipe
|
|
int sh_rpipe(register int pv[])
|
|
{
|
|
return sh_pipe(pv);
|
|
}
|
|
#else
|
|
# undef pipe
|
|
/* create a real pipe when pipe() is socketpair */
|
|
int sh_rpipe(register int pv[])
|
|
{
|
|
int fd[2];
|
|
if(pipe(fd)<0 || (pv[0]=fd[0])<0 || (pv[1]=fd[1])<0)
|
|
{
|
|
errormsg(SH_DICT,ERROR_system(1),e_pipe);
|
|
UNREACHABLE();
|
|
}
|
|
pv[0] = sh_iomovefd(pv[0]);
|
|
pv[1] = sh_iomovefd(pv[1]);
|
|
sh.fdstatus[pv[0]] = IONOSEEK|IOREAD;
|
|
sh.fdstatus[pv[1]] = IONOSEEK|IOWRITE;
|
|
sh_subsavefd(pv[0]);
|
|
sh_subsavefd(pv[1]);
|
|
return(0);
|
|
}
|
|
#endif
|
|
|
|
static int pat_seek(void *handle, const char *str, size_t sz)
|
|
{
|
|
char **bp = (char**)handle;
|
|
*bp = (char*)str;
|
|
return(-1);
|
|
}
|
|
|
|
static int pat_line(const regex_t* rp, const char *buff, register size_t n)
|
|
{
|
|
register const char *cp=buff, *sp;
|
|
while(n>0)
|
|
{
|
|
for(sp=cp; n-->0 && *cp++ != '\n';);
|
|
if(regnexec(rp,sp,cp-sp, 0, (regmatch_t*)0, 0)==0)
|
|
return(sp-buff);
|
|
}
|
|
return(cp-buff);
|
|
}
|
|
|
|
static int io_patseek(regex_t *rp, Sfio_t* sp, int flags)
|
|
{
|
|
char *cp, *match;
|
|
int r, fd=sffileno(sp), close_exec = sh.fdstatus[fd]&IOCLEX;
|
|
int was_share,s=(PIPE_BUF>SF_BUFSIZE?SF_BUFSIZE:PIPE_BUF);
|
|
size_t n,m;
|
|
sh.fdstatus[sffileno(sp)] |= IOCLEX;
|
|
if(fd==0)
|
|
was_share = sfset(sp,SF_SHARE,1);
|
|
while((cp=sfreserve(sp, -s, SF_LOCKR)) || (cp=sfreserve(sp,SF_UNBOUND, SF_LOCKR)))
|
|
{
|
|
m = n = sfvalue(sp);
|
|
while(n>0 && cp[n-1]!='\n')
|
|
n--;
|
|
if(n)
|
|
m = n;
|
|
r = regrexec(rp,cp,m,0,(regmatch_t*)0, 0, '\n', (void*)&match, pat_seek);
|
|
if(r<0)
|
|
m = match-cp;
|
|
else if(r==2)
|
|
{
|
|
if((m = pat_line(rp,cp,m)) < n)
|
|
r = -1;
|
|
}
|
|
if(m && (flags&IOCOPY))
|
|
sfwrite(sfstdout,cp,m);
|
|
sfread(sp,cp,m);
|
|
if(r<0)
|
|
break;
|
|
}
|
|
if(!close_exec)
|
|
sh.fdstatus[sffileno(sp)] &= ~IOCLEX;
|
|
if(fd==0 && !(was_share&SF_SHARE))
|
|
sfset(sp, SF_SHARE,0);
|
|
return(0);
|
|
}
|
|
|
|
static Sfoff_t file_offset(int fn, char *fname)
|
|
{
|
|
Sfio_t *sp = sh.sftable[fn];
|
|
char *cp;
|
|
Sfoff_t off;
|
|
struct Eof endf;
|
|
Namval_t *mp = nv_open("EOF",sh.var_tree,0);
|
|
Namval_t *pp = nv_open("CUR",sh.var_tree,0);
|
|
memset(&endf,0,sizeof(struct Eof));
|
|
endf.fd = fn;
|
|
endf.hdr.disc = &EOF_disc;
|
|
endf.hdr.nofree = 1;
|
|
if(mp)
|
|
nv_stack(mp, &endf.hdr);
|
|
if(pp)
|
|
nv_stack(pp, &endf.hdr);
|
|
if(sp)
|
|
sfsync(sp);
|
|
off = sh_strnum(fname, &cp, 0);
|
|
if(mp)
|
|
nv_stack(mp, NiL);
|
|
if(pp)
|
|
nv_stack(pp, NiL);
|
|
return(*cp?(Sfoff_t)-1:off);
|
|
}
|
|
|
|
/*
|
|
* close a pipe
|
|
*/
|
|
void sh_pclose(register int pv[])
|
|
{
|
|
if(pv[0]>=2)
|
|
sh_close(pv[0]);
|
|
if(pv[1]>=2)
|
|
sh_close(pv[1]);
|
|
pv[0] = pv[1] = -1;
|
|
}
|
|
|
|
static char *io_usename(char *name, int *perm, int fno, int mode)
|
|
{
|
|
struct stat statb;
|
|
char *tname, *sp, *ep, path[PATH_MAX+1];
|
|
int fd,r;
|
|
if(mode==0)
|
|
{
|
|
if((fd = sh_open(name,O_RDONLY,0)) >= 0)
|
|
{
|
|
r = fstat(fd,&statb);
|
|
close(fd);
|
|
if(r)
|
|
return(0);
|
|
if(!S_ISREG(statb.st_mode))
|
|
return(0);
|
|
*perm = statb.st_mode&(RW_ALL|(S_IXUSR|S_IXGRP|S_IXOTH));
|
|
}
|
|
else if(fd < 0 && errno!=ENOENT)
|
|
return(0);
|
|
}
|
|
while((fd=readlink(name, path, PATH_MAX)) >0)
|
|
{
|
|
name=path;
|
|
name[fd] = 0;
|
|
}
|
|
stakseek(1);
|
|
stakputs(name);
|
|
stakputc(0);
|
|
pathcanon(stakptr(1),PATH_PHYSICAL);
|
|
sp = ep = stakptr(1);
|
|
if(ep = strrchr(sp,'/'))
|
|
{
|
|
memmove(stakptr(0),sp,++ep-sp);
|
|
stakseek(ep-sp);
|
|
}
|
|
else
|
|
{
|
|
ep = sp;
|
|
stakseek(0);
|
|
}
|
|
stakputc('.');
|
|
sfprintf(stkstd,"%<#d_%d{;.tmp",sh.current_pid,fno);
|
|
tname = stakfreeze(1);
|
|
switch(mode)
|
|
{
|
|
case 1:
|
|
rename(tname,name);
|
|
break;
|
|
default:
|
|
unlink(tname);
|
|
break;
|
|
}
|
|
return(tname);
|
|
}
|
|
|
|
/*
|
|
* I/O redirection
|
|
* flag = 0: normal redirection; file descriptors are restored when command terminates
|
|
* flag = 1: redirections persist
|
|
* flag = 2: redirections persist, but file descriptors > 2 are closed when executing an external command
|
|
* flag = 3: just open file and return; for use when called from $( < ...)
|
|
* flag = SH_SHOWME for trace only
|
|
*/
|
|
int sh_redirect(struct ionod *iop, int flag)
|
|
{
|
|
Sfoff_t off;
|
|
register char *fname;
|
|
register int fd, iof;
|
|
const char *message = e_open;
|
|
int o_mode; /* mode flag for open */
|
|
static char io_op[7]; /* used for -x trace info */
|
|
int trunc=0, clexec=0, fn, traceon;
|
|
int r, indx = sh.topfd, perm= -1;
|
|
char *tname=0, *after="", *trace = sh.st.trap[SH_DEBUGTRAP];
|
|
Namval_t *np=0;
|
|
|
|
if(flag==2 && !sh_isoption(SH_POSIX))
|
|
clexec = 1;
|
|
if(iop)
|
|
traceon = sh_trace(NIL(char**),0);
|
|
for(;iop;iop=iop->ionxt)
|
|
{
|
|
iof=iop->iofile;
|
|
fn = (iof&IOUFD);
|
|
if(fn==1)
|
|
{
|
|
if(sh.subshell && sh.comsub && (flag==1 || flag==2))
|
|
{
|
|
if(sh.subshare)
|
|
{
|
|
errormsg(SH_DICT,ERROR_exit(1),"cannot redirect stdout inside shared-state comsub");
|
|
UNREACHABLE();
|
|
}
|
|
sh_subfork();
|
|
}
|
|
}
|
|
if(sh.redir0 && fn==0 && !(iof&IOMOV))
|
|
sh.redir0 = 2;
|
|
io_op[0] = '0'+(iof&IOUFD);
|
|
if(iof&IOPUT)
|
|
{
|
|
io_op[1] = '>';
|
|
o_mode = O_WRONLY|O_CREAT;
|
|
}
|
|
else
|
|
{
|
|
io_op[1] = '<';
|
|
o_mode = O_RDONLY|O_NONBLOCK;
|
|
}
|
|
io_op[2] = 0;
|
|
io_op[3] = 0;
|
|
io_op[4] = 0;
|
|
fname = iop->ioname;
|
|
if(!(iof&IORAW))
|
|
{
|
|
if(iof&IOLSEEK)
|
|
{
|
|
struct argnod *ap = (struct argnod*)stakalloc(ARGVAL+strlen(iop->ioname));
|
|
memset(ap, 0, ARGVAL);
|
|
ap->argflag = ARG_MAC;
|
|
strcpy(ap->argval,iop->ioname);
|
|
fname=sh_macpat(ap,(iof&IOARITH)?ARG_ARITH:ARG_EXP);
|
|
}
|
|
else if(!(iof&IOPROCSUB))
|
|
fname=sh_mactrim(fname,(!sh_isoption(SH_NOGLOB)&&sh_isoption(SH_INTERACTIVE))?2:0);
|
|
}
|
|
if((iof&IOPROCSUB) && !(iof&IOLSEEK))
|
|
{
|
|
/* handle process substitution passed to redirection */
|
|
struct argnod *ap = (struct argnod*)stakalloc(ARGVAL+strlen(iop->ioname));
|
|
memset(ap, 0, ARGVAL);
|
|
if(iof&IOPUT)
|
|
ap->argflag = ARG_RAW;
|
|
else if(sh.subshell)
|
|
sh_subtmpfile();
|
|
ap->argchn.ap = (struct argnod*)fname;
|
|
ap = sh_argprocsub(ap);
|
|
fname = ap->argval;
|
|
}
|
|
errno=0;
|
|
np = 0;
|
|
if(iop->iovname)
|
|
{
|
|
np = nv_open(iop->iovname,sh.var_tree,NV_NOASSIGN|NV_VARNAME);
|
|
if(nv_isattr(np,NV_RDONLY))
|
|
{
|
|
errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np));
|
|
UNREACHABLE();
|
|
}
|
|
io_op[0] = '}';
|
|
if((iof&IOLSEEK) || ((iof&IOMOV) && *fname=='-'))
|
|
fn = nv_getnum(np);
|
|
}
|
|
if(fn>=sh.lim.open_max && !sh_iovalidfd(fn))
|
|
{
|
|
errormsg(SH_DICT,ERROR_system(1),e_file+4);
|
|
UNREACHABLE();
|
|
}
|
|
if(iof&IOLSEEK)
|
|
{
|
|
io_op[2] = '#';
|
|
if(iof&IOARITH)
|
|
{
|
|
strcpy(&io_op[3]," ((");
|
|
after = "))";
|
|
}
|
|
else if(iof&IOCOPY)
|
|
io_op[3] = '#';
|
|
goto traceit;
|
|
}
|
|
if(*fname || (iof&(IODOC|IOSTRG))==(IODOC|IOSTRG))
|
|
{
|
|
if(iof&IODOC)
|
|
{
|
|
if(traceon)
|
|
sfputr(sfstderr,io_op,'<');
|
|
fd = io_heredoc(iop,fname,traceon);
|
|
if(traceon && (flag==SH_SHOWME))
|
|
sh_close(fd);
|
|
fname = 0;
|
|
}
|
|
else if(iof&IOMOV)
|
|
{
|
|
int dupfd,toclose= -1;
|
|
io_op[2] = '&';
|
|
if((fd=fname[0])>='0' && fd<='9')
|
|
{
|
|
char *number = fname;
|
|
dupfd = strtol(fname,&number,10);
|
|
if(*number=='-')
|
|
{
|
|
toclose = dupfd;
|
|
number++;
|
|
}
|
|
if(*number || !sh_iovalidfd(dupfd) || dupfd > IOUFD)
|
|
{
|
|
message = e_file;
|
|
goto fail;
|
|
}
|
|
if(sh.subshell && dupfd==1)
|
|
{
|
|
if(sfset(sfstdout,0,0)&SF_STRING)
|
|
sh_subtmpfile();
|
|
if(sh.comsub==1)
|
|
sh.subdup |= 1<<fn;
|
|
dupfd = sffileno(sfstdout);
|
|
}
|
|
else if(sh.sftable[dupfd])
|
|
sfsync(sh.sftable[dupfd]);
|
|
if(dupfd!=1 && fn < 10)
|
|
sh.subdup &= ~(1<<fn);
|
|
}
|
|
else if(fd=='-' && fname[1]==0)
|
|
{
|
|
fd= -1;
|
|
goto traceit;
|
|
}
|
|
else if(fd=='p' && fname[1]==0)
|
|
{
|
|
if(iof&IOPUT)
|
|
dupfd = sh.coutpipe;
|
|
else
|
|
dupfd = sh.cpipe[0];
|
|
if(flag)
|
|
toclose = dupfd;
|
|
}
|
|
else
|
|
{
|
|
message = e_file;
|
|
goto fail;
|
|
}
|
|
if(flag==SH_SHOWME)
|
|
goto traceit;
|
|
if((fd=sh_fcntl(dupfd,F_DUPFD,3))<0)
|
|
goto fail;
|
|
if(fd>= sh.lim.open_max)
|
|
sh_iovalidfd(fd);
|
|
sh_iocheckfd(dupfd);
|
|
sh.fdstatus[fd] = (sh.fdstatus[dupfd]&~IOCLEX);
|
|
if(toclose<0 && sh.fdstatus[fd]&IOREAD)
|
|
sh.fdstatus[fd] |= IODUP;
|
|
else if(dupfd==sh.cpipe[0])
|
|
sh_pclose(sh.cpipe);
|
|
else if(toclose>=0)
|
|
{
|
|
if(flag==0)
|
|
sh_iosave(toclose,indx,(char*)0); /* save file descriptor */
|
|
sh_close(toclose);
|
|
}
|
|
}
|
|
else if(iof&IORDW)
|
|
{
|
|
if(sh_isoption(SH_RESTRICTED))
|
|
{
|
|
errormsg(SH_DICT,ERROR_exit(1),e_restricted,fname);
|
|
UNREACHABLE();
|
|
}
|
|
io_op[2] = '>';
|
|
o_mode = O_RDWR|O_CREAT;
|
|
if(iof&IOREWRITE)
|
|
trunc = io_op[2] = ';';
|
|
goto openit;
|
|
}
|
|
else if(!(iof&IOPUT))
|
|
{
|
|
if(flag==SH_SHOWME)
|
|
goto traceit;
|
|
fd=sh_chkopen(fname);
|
|
}
|
|
else if(sh_isoption(SH_RESTRICTED))
|
|
{
|
|
errormsg(SH_DICT,ERROR_exit(1),e_restricted,fname);
|
|
UNREACHABLE();
|
|
}
|
|
else
|
|
{
|
|
if(iof&IOAPP)
|
|
{
|
|
io_op[2] = '>';
|
|
o_mode |= O_APPEND;
|
|
}
|
|
else if((iof&IOREWRITE) && (flag==0 || flag==1 || sh_subsavefd(fn)))
|
|
{
|
|
io_op[2] = ';';
|
|
o_mode |= O_TRUNC;
|
|
if(tname = io_usename(fname,&perm,fn,0))
|
|
o_mode |= O_EXCL;
|
|
}
|
|
else
|
|
{
|
|
o_mode |= O_TRUNC;
|
|
if(iof&IOCLOB)
|
|
io_op[2] = '|';
|
|
else if(sh_isoption(SH_NOCLOBBER))
|
|
{
|
|
struct stat sb;
|
|
if(stat(fname,&sb)>=0)
|
|
{
|
|
if(S_ISREG(sb.st_mode))
|
|
{
|
|
errno = EEXIST;
|
|
errormsg(SH_DICT,ERROR_system(1),e_exists,fname);
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
else
|
|
o_mode |= O_EXCL;
|
|
}
|
|
}
|
|
openit:
|
|
if(flag!=SH_SHOWME)
|
|
{
|
|
if((fd=sh_open(tname?tname:fname,o_mode,RW_ALL)) <0)
|
|
{
|
|
errormsg(SH_DICT,ERROR_system(1),((o_mode&O_CREAT)?e_create:e_open),fname);
|
|
UNREACHABLE();
|
|
}
|
|
if(perm>0)
|
|
#if _lib_fchmod
|
|
fchmod(fd,perm);
|
|
#else
|
|
chmod(tname,perm);
|
|
#endif
|
|
}
|
|
}
|
|
traceit:
|
|
if(traceon && fname)
|
|
{
|
|
if(np)
|
|
sfprintf(sfstderr,"{%s",nv_name(np));
|
|
sfprintf(sfstderr,"%s %s%s%c",io_op,fname,after,iop->ionxt?' ':'\n');
|
|
}
|
|
if(flag==SH_SHOWME)
|
|
return(indx);
|
|
if(trace && fname)
|
|
{
|
|
char *argv[7], **av=argv;
|
|
av[3] = io_op;
|
|
av[4] = fname;
|
|
av[5] = 0;
|
|
av[6] = 0;
|
|
if(iof&IOARITH)
|
|
av[5] = after;
|
|
if(np)
|
|
{
|
|
av[0] = "{";
|
|
av[1] = nv_name(np);
|
|
av[2] = "}";
|
|
}
|
|
else
|
|
av +=3;
|
|
sh_debug(trace,(char*)0,(char*)0,av,ARG_NOGLOB);
|
|
}
|
|
if(iof&IOLSEEK)
|
|
{
|
|
Sfio_t *sp = sh.sftable[fn];
|
|
r = sh.fdstatus[fn];
|
|
if(!(r&(IOSEEK|IONOSEEK)))
|
|
r = sh_iocheckfd(fn);
|
|
sfsprintf(io_op,sizeof(io_op),"%d\0",fn);
|
|
if(r==IOCLOSE)
|
|
{
|
|
fname = io_op;
|
|
message = e_file;
|
|
goto fail;
|
|
}
|
|
if(iof&IOARITH)
|
|
{
|
|
if(r&IONOSEEK)
|
|
{
|
|
fname = io_op;
|
|
message = e_notseek;
|
|
goto fail;
|
|
}
|
|
message = e_badseek;
|
|
if((off = file_offset(fn,fname))<0)
|
|
goto fail;
|
|
if(sp)
|
|
{
|
|
off=sfseek(sp, off, SEEK_SET);
|
|
sfsync(sp);
|
|
}
|
|
else
|
|
off=lseek(fn, off, SEEK_SET);
|
|
if(off<0)
|
|
r = -1;
|
|
}
|
|
else
|
|
{
|
|
regex_t *rp;
|
|
if(!(r&IOREAD))
|
|
{
|
|
message = e_noread;
|
|
goto fail;
|
|
}
|
|
if(!(rp = regcache(fname, REG_SHELL|REG_NOSUB|REG_NEWLINE|REG_AUGMENTED|REG_FIRST|REG_LEFT|REG_RIGHT, &r)))
|
|
{
|
|
message = e_badpattern;
|
|
goto fail;
|
|
}
|
|
if(!sp)
|
|
sp = sh_iostream(fn);
|
|
r=io_patseek(rp,sp,iof);
|
|
if(sp && flag==3)
|
|
{
|
|
/* close stream but not fn */
|
|
sfsetfd(sp,-1);
|
|
sfclose(sp);
|
|
}
|
|
}
|
|
if(r<0)
|
|
goto fail;
|
|
if(flag==3)
|
|
return(fn);
|
|
continue;
|
|
}
|
|
if(!np)
|
|
{
|
|
if(flag==0 || tname || (flag==1 && fn==1 && (sh.fdstatus[fn]&IONOSEEK) && sh.outpipepid == sh.current_pid))
|
|
{
|
|
if(fd==fn)
|
|
{
|
|
if((r=sh_fcntl(fd,F_DUPFD,10)) > 0)
|
|
{
|
|
fd = r;
|
|
sh_close(fn);
|
|
}
|
|
}
|
|
sh_iosave(fn,indx,tname?fname:(trunc?Empty:0));
|
|
}
|
|
else if(sh_subsavefd(fn))
|
|
{
|
|
if(fd==fn && !sh.subshare)
|
|
{
|
|
if((r=sh_fcntl(fd,F_DUPFD,10)) > 0)
|
|
{
|
|
fd = r;
|
|
sh_close(fn);
|
|
}
|
|
}
|
|
sh_iosave(fn,indx|IOSUBSHELL,tname?fname:0);
|
|
}
|
|
}
|
|
if(fd<0)
|
|
{
|
|
if(sh_inuse(fn) || (fn && fn==sh.infd))
|
|
{
|
|
if(fn>9 || !(sh.inuse_bits&(1<<fn)))
|
|
io_preserve(sh.sftable[fn],fn);
|
|
}
|
|
sh_close(fn);
|
|
}
|
|
if(flag==3)
|
|
return(sh_iomovefd(fd)); /* ensure FD > 2 to make $(<file) work with std{in,out,err} closed */
|
|
if(fd>=0)
|
|
{
|
|
if(np)
|
|
{
|
|
int32_t v;
|
|
fn = fd;
|
|
if(fd<10)
|
|
{
|
|
if((fn=sh_fcntl(fd,F_DUPFD,10)) < 0)
|
|
goto fail;
|
|
if(fn>=sh.lim.open_max && !sh_iovalidfd(fn))
|
|
goto fail;
|
|
if(flag!=2 || sh.subshell)
|
|
sh_iosave(fn,indx|0x10000,tname?fname:(trunc?Empty:0));
|
|
sh.fdstatus[fn] = sh.fdstatus[fd];
|
|
sh_close(fd);
|
|
fd = fn;
|
|
}
|
|
_nv_unset(np,0);
|
|
nv_onattr(np,NV_INT32);
|
|
v = fn;
|
|
nv_putval(np,(char*)&v, NV_INT32);
|
|
sh_iocheckfd(fd);
|
|
}
|
|
else
|
|
{
|
|
fd = sh_iorenumber(sh_iomovefd(fd),fn);
|
|
if(fn>2 && fn<10)
|
|
sh.inuse_bits |= (1<<fn);
|
|
}
|
|
}
|
|
if(fd >2 && clexec)
|
|
{
|
|
fcntl(fd,F_SETFD,FD_CLOEXEC);
|
|
sh.fdstatus[fd] |= IOCLEX;
|
|
}
|
|
}
|
|
else
|
|
goto fail;
|
|
}
|
|
return(indx);
|
|
fail:
|
|
errormsg(SH_DICT,ERROR_system(1),message,fname);
|
|
UNREACHABLE();
|
|
}
|
|
/*
|
|
* Create a tmp file for the here-document
|
|
*/
|
|
static int io_heredoc(register struct ionod *iop, const char *name, int traceon)
|
|
{
|
|
register Sfio_t *infile = 0, *outfile, *tmp;
|
|
register int fd;
|
|
Sfoff_t off;
|
|
if(!(iop->iofile&IOSTRG) && (!sh.heredocs || iop->iosize==0))
|
|
return(sh_open(e_devnull,O_RDONLY));
|
|
/* create an unnamed temporary file */
|
|
if(!(outfile=sftmp(0)))
|
|
{
|
|
errormsg(SH_DICT,ERROR_system(1),e_tmpcreate);
|
|
UNREACHABLE();
|
|
}
|
|
if(iop->iofile&IOSTRG)
|
|
{
|
|
if(traceon)
|
|
sfprintf(sfstderr,"< %s\n",name);
|
|
sfputr(outfile,name,'\n');
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* the locking is only needed in case & blocks process
|
|
* here-docs so this can be eliminated in some cases
|
|
*/
|
|
struct flock lock;
|
|
int fno = sffileno(sh.heredocs);
|
|
if(fno>=0)
|
|
{
|
|
memset((void*)&lock,0,sizeof(lock));
|
|
lock.l_type = F_WRLCK;
|
|
lock.l_whence = SEEK_SET;
|
|
fcntl(fno,F_SETLKW,&lock);
|
|
lock.l_type = F_UNLCK;
|
|
}
|
|
off = sftell(sh.heredocs);
|
|
infile = subopen(sh.heredocs,iop->iooffset,iop->iosize);
|
|
if(traceon)
|
|
{
|
|
char *cp = sh_fmtq(iop->iodelim);
|
|
fd = (*cp=='$' || *cp=='\'')?' ':'\\';
|
|
sfprintf(sfstderr," %c%s\n",fd,cp);
|
|
sfdisc(outfile,&tee_disc);
|
|
}
|
|
tmp = outfile;
|
|
if(fno>=0 && !(iop->iofile&IOQUOTE))
|
|
tmp = sftmp(iop->iosize<IOBSIZE?iop->iosize:0);
|
|
if(fno>=0 || (iop->iofile&IOQUOTE))
|
|
{
|
|
/* This is a quoted here-document, not expansion */
|
|
sfmove(infile,tmp,SF_UNBOUND,-1);
|
|
sfclose(infile);
|
|
if(sffileno(tmp)>0)
|
|
{
|
|
sfsetbuf(tmp,sh_malloc(IOBSIZE+1),IOBSIZE);
|
|
sfset(tmp,SF_MALLOC,1);
|
|
}
|
|
sfseek(sh.heredocs,off,SEEK_SET);
|
|
if(fno>=0)
|
|
fcntl(fno,F_SETLK,&lock);
|
|
sfseek(tmp,(off_t)0,SEEK_SET);
|
|
infile = tmp;
|
|
}
|
|
if(!(iop->iofile&IOQUOTE))
|
|
{
|
|
char *lastpath = sh.lastpath;
|
|
sh_machere(infile,outfile,iop->ioname);
|
|
sh.lastpath = lastpath;
|
|
if(infile)
|
|
sfclose(infile);
|
|
}
|
|
}
|
|
/* close stream outfile, but save file descriptor */
|
|
fd = sffileno(outfile);
|
|
sfsetfd(outfile,-1);
|
|
sfclose(outfile);
|
|
if(traceon && !(iop->iofile&IOSTRG))
|
|
sfputr(sfstderr,iop->ioname,'\n');
|
|
lseek(fd,(off_t)0,SEEK_SET);
|
|
sh.fdstatus[fd] = IOREAD;
|
|
return(fd);
|
|
}
|
|
|
|
/*
|
|
* This write discipline also writes the output on standard error
|
|
* This is used when tracing here-documents
|
|
*/
|
|
static ssize_t tee_write(Sfio_t *iop,const void *buff,size_t n,Sfdisc_t *unused)
|
|
{
|
|
NOT_USED(unused);
|
|
sfwrite(sfstderr,buff,n);
|
|
return(write(sffileno(iop),buff,n));
|
|
}
|
|
|
|
/*
|
|
* copy file <origfd> into a save place
|
|
* The saved file is set close-on-exec
|
|
* if <origfd> < 0, then -origfd is saved, but not duped so that it
|
|
* will be closed with sh_iorestore.
|
|
*/
|
|
void sh_iosave(register int origfd, int oldtop, char *name)
|
|
{
|
|
register int savefd;
|
|
int flag = (oldtop&(IOSUBSHELL|IOPICKFD));
|
|
oldtop &= ~(IOSUBSHELL|IOPICKFD);
|
|
/* see if already saved, only save once */
|
|
for(savefd=sh.topfd; --savefd>=oldtop; )
|
|
{
|
|
if(filemap[savefd].orig_fd == origfd)
|
|
return;
|
|
}
|
|
/* make sure table is large enough */
|
|
if(sh.topfd >= filemapsize)
|
|
{
|
|
char *cp, *oldptr = (char*)filemap;
|
|
char *oldend = (char*)&filemap[filemapsize];
|
|
long moved;
|
|
filemapsize += 8;
|
|
filemap = (struct fdsave*)sh_realloc(filemap,filemapsize*sizeof(struct fdsave));
|
|
if(moved = (char*)filemap - oldptr)
|
|
{
|
|
for(savefd=sh.lim.open_max; --savefd>=0; )
|
|
{
|
|
cp = (char*)sh.fdptrs[savefd];
|
|
if(cp >= oldptr && cp < oldend)
|
|
sh.fdptrs[savefd] = (int*)(cp+moved);
|
|
}
|
|
}
|
|
}
|
|
#if SHOPT_DEVFD
|
|
if(origfd <0)
|
|
{
|
|
savefd = origfd;
|
|
origfd = -origfd;
|
|
}
|
|
else
|
|
#endif /* SHOPT_DEVFD */
|
|
if(flag&IOPICKFD)
|
|
savefd = -1;
|
|
else
|
|
{
|
|
if((savefd = sh_fcntl(origfd, F_DUPFD, 10)) < 0 && errno!=EBADF)
|
|
{
|
|
sh.toomany=1;
|
|
((struct checkpt*)sh.jmplist)->mode = SH_JMPERREXIT;
|
|
errormsg(SH_DICT,ERROR_system(1),e_toomany);
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
filemap[sh.topfd].tname = name;
|
|
filemap[sh.topfd].subshell = (flag&IOSUBSHELL);
|
|
filemap[sh.topfd].orig_fd = origfd;
|
|
filemap[sh.topfd++].save_fd = savefd;
|
|
if(savefd >=0)
|
|
{
|
|
register Sfio_t* sp = sh.sftable[origfd];
|
|
/* make saved file close-on-exec */
|
|
sh_fcntl(savefd,F_SETFD,FD_CLOEXEC);
|
|
if(origfd==job.fd)
|
|
job.fd = savefd;
|
|
sh.fdstatus[savefd] = sh.fdstatus[origfd];
|
|
sh.fdptrs[savefd] = &filemap[sh.topfd-1].save_fd;
|
|
if(!(sh.sftable[savefd]=sp))
|
|
return;
|
|
sfsync(sp);
|
|
if(origfd <=2)
|
|
{
|
|
/* copy standard stream to new stream */
|
|
sp = sfswap(sp,NIL(Sfio_t*));
|
|
sh.sftable[savefd] = sp;
|
|
}
|
|
else
|
|
sh.sftable[origfd] = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* close all saved file descriptors
|
|
*/
|
|
void sh_iounsave(void)
|
|
{
|
|
register int fd, savefd, newfd;
|
|
for(newfd=fd=0; fd < sh.topfd; fd++)
|
|
{
|
|
if((savefd = filemap[fd].save_fd)< 0)
|
|
filemap[newfd++] = filemap[fd];
|
|
else
|
|
{
|
|
sh.sftable[savefd] = 0;
|
|
sh_close(savefd);
|
|
}
|
|
}
|
|
sh.topfd = newfd;
|
|
}
|
|
|
|
/*
|
|
* restore saved file descriptors from <last> on
|
|
*/
|
|
void sh_iorestore(int last, int jmpval)
|
|
{
|
|
register int origfd, savefd, fd;
|
|
int flag = (last&IOSUBSHELL);
|
|
last &= ~IOSUBSHELL;
|
|
for (fd = sh.topfd - 1; fd >= last; fd--)
|
|
{
|
|
if(!flag && filemap[fd].subshell)
|
|
continue;
|
|
if(jmpval==SH_JMPSCRIPT)
|
|
{
|
|
if ((savefd = filemap[fd].save_fd) >= 0)
|
|
{
|
|
sh.sftable[savefd] = 0;
|
|
sh_close(savefd);
|
|
}
|
|
continue;
|
|
}
|
|
origfd = filemap[fd].orig_fd;
|
|
if(origfd<0)
|
|
{
|
|
/* this should never happen */
|
|
savefd = filemap[fd].save_fd;
|
|
sh.sftable[savefd] = 0;
|
|
sh_close(savefd);
|
|
return;
|
|
}
|
|
if(filemap[fd].tname == Empty && sh.exitval==0)
|
|
{
|
|
sfsync(NIL(Sfio_t*));
|
|
ftruncate(origfd,lseek(origfd,0,SEEK_CUR));
|
|
}
|
|
else if(filemap[fd].tname)
|
|
io_usename(filemap[fd].tname,(int*)0,origfd,sh.exitval?2:1);
|
|
sh_close(origfd);
|
|
if ((savefd = filemap[fd].save_fd) >= 0)
|
|
{
|
|
sh_fcntl(savefd, F_DUPFD, origfd);
|
|
if(savefd==job.fd)
|
|
job.fd=origfd;
|
|
sh.fdstatus[origfd] = sh.fdstatus[savefd];
|
|
/* turn off close-on-exec if flag if necessary */
|
|
if(sh.fdstatus[origfd]&IOCLEX)
|
|
fcntl(origfd,F_SETFD,FD_CLOEXEC);
|
|
if(origfd<=2)
|
|
{
|
|
sfswap(sh.sftable[savefd],sh.sftable[origfd]);
|
|
if(origfd==0)
|
|
sh.st.ioset = 0;
|
|
}
|
|
else
|
|
sh.sftable[origfd] = sh.sftable[savefd];
|
|
sh.sftable[savefd] = 0;
|
|
sh_close(savefd);
|
|
}
|
|
else
|
|
sh.fdstatus[origfd] = IOCLOSE;
|
|
}
|
|
if(!flag)
|
|
{
|
|
/* keep file descriptors for subshell restore */
|
|
for (fd = last ; fd < sh.topfd; fd++)
|
|
{
|
|
if(filemap[fd].subshell)
|
|
filemap[last++] = filemap[fd];
|
|
}
|
|
}
|
|
if(last < sh.topfd)
|
|
sh.topfd = last;
|
|
}
|
|
|
|
/*
|
|
* returns access information on open file <fd>
|
|
* returns -1 for failure, 0 for success
|
|
* <mode> is the same as for access()
|
|
*/
|
|
int sh_ioaccess(int fd,register int mode)
|
|
{
|
|
register int flags;
|
|
if(mode==X_OK)
|
|
return(-1);
|
|
if((flags=sh_iocheckfd(fd))!=IOCLOSE)
|
|
{
|
|
if(mode==F_OK)
|
|
return(0);
|
|
if(mode==R_OK && (flags&IOREAD))
|
|
return(0);
|
|
if(mode==W_OK && (flags&IOWRITE))
|
|
return(0);
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
/*
|
|
* Handle interrupts for slow streams
|
|
*/
|
|
static int slowexcept(register Sfio_t *iop,int type,void *data,Sfdisc_t *handle)
|
|
{
|
|
register int n,fno;
|
|
if(type==SF_DPOP || type==SF_FINAL)
|
|
free((void*)handle);
|
|
if(type==SF_WRITE && ERROR_PIPE(errno))
|
|
{
|
|
sfpurge(iop);
|
|
return(-1);
|
|
}
|
|
if(type!=SF_READ)
|
|
return(0);
|
|
if((sh.trapnote&(SH_SIGSET|SH_SIGTRAP)) && errno!=EIO && errno!=ENXIO)
|
|
errno = EINTR;
|
|
fno = sffileno(iop);
|
|
if((n=sfvalue(iop))<=0)
|
|
{
|
|
#ifndef FNDELAY
|
|
# ifdef O_NDELAY
|
|
if(errno==0 && (n=fcntl(fno,F_GETFL,0))&O_NDELAY)
|
|
{
|
|
n &= ~O_NDELAY;
|
|
fcntl(fno, F_SETFL, n);
|
|
return(1);
|
|
}
|
|
# endif /* O_NDELAY */
|
|
#endif /* !FNDELAY */
|
|
#ifdef O_NONBLOCK
|
|
if(errno==EAGAIN)
|
|
{
|
|
n = fcntl(fno,F_GETFL,0);
|
|
n &= ~O_NONBLOCK;
|
|
fcntl(fno, F_SETFL, n);
|
|
return(1);
|
|
}
|
|
#endif /* O_NONBLOCK */
|
|
if(errno!=EINTR)
|
|
return(0);
|
|
else if(sh.bltinfun && (sh.trapnote&SH_SIGTRAP) && sh.lastsig)
|
|
return(-1);
|
|
n=1;
|
|
sh_onstate(SH_TTYWAIT);
|
|
}
|
|
else
|
|
n = 0;
|
|
if(sh.bltinfun && sh.bltindata.sigset)
|
|
return(-1);
|
|
errno = 0;
|
|
if(sh.trapnote&SH_SIGSET)
|
|
{
|
|
if(isatty(fno))
|
|
sfputc(sfstderr,'\n');
|
|
sh_exit(SH_EXITSIG);
|
|
}
|
|
if(sh.trapnote&SH_SIGTRAP)
|
|
sh_chktrap();
|
|
return(n);
|
|
}
|
|
|
|
/*
|
|
* called when slowread times out
|
|
*/
|
|
static void time_grace(void *handle)
|
|
{
|
|
NOT_USED(handle);
|
|
timeout = 0;
|
|
if(sh_isstate(SH_GRACE))
|
|
{
|
|
sh_offstate(SH_GRACE);
|
|
if(!sh_isstate(SH_INTERACTIVE))
|
|
return;
|
|
((struct checkpt*)sh.jmplist)->mode = SH_JMPEXIT;
|
|
errormsg(SH_DICT,2,e_timeout);
|
|
sh.trapnote |= SH_SIGSET;
|
|
return;
|
|
}
|
|
errormsg(SH_DICT,0,e_timewarn);
|
|
sh_onstate(SH_GRACE);
|
|
sigrelease(SIGALRM);
|
|
sh.trapnote |= SH_SIGTRAP;
|
|
}
|
|
|
|
static ssize_t piperead(Sfio_t *iop,void *buff,register size_t size,Sfdisc_t *handle)
|
|
{
|
|
int fd = sffileno(iop);
|
|
if(job.waitsafe && job.savesig)
|
|
{
|
|
job_lock();
|
|
job_unlock();
|
|
}
|
|
if(sh.trapnote)
|
|
{
|
|
errno = EINTR;
|
|
return(-1);
|
|
}
|
|
if(sh_isstate(SH_INTERACTIVE) && sffileno(iop)==0 && io_prompt(iop,sh.nextprompt)<0 && errno==EIO)
|
|
return(0);
|
|
sh_onstate(SH_TTYWAIT);
|
|
if(!(sh.fdstatus[fd]&IOCLEX) && (sfset(iop,0,0)&SF_SHARE))
|
|
size = ed_read(sh.ed_context, fd, (char*)buff, size,0);
|
|
else
|
|
size = sfrd(iop,buff,size,handle);
|
|
sh_offstate(SH_TTYWAIT);
|
|
return(size);
|
|
}
|
|
/*
|
|
* This is the read discipline that is applied to slow devices
|
|
* This routine takes care of prompting for input
|
|
*/
|
|
static ssize_t slowread(Sfio_t *iop,void *buff,register size_t size,Sfdisc_t *handle)
|
|
{
|
|
int (*readf)(void*, int, char*, int, int);
|
|
int reedit=0, rsize;
|
|
#if SHOPT_HISTEXPAND
|
|
char *xp=0;
|
|
#endif
|
|
# if SHOPT_ESH
|
|
if(sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS))
|
|
readf = ed_emacsread;
|
|
else
|
|
# endif /* SHOPT_ESH */
|
|
# if SHOPT_VSH
|
|
# if SHOPT_RAWONLY
|
|
if(sh_isoption(SH_VI) || ((SHOPT_RAWONLY-0) && mbwide()))
|
|
# else
|
|
if(sh_isoption(SH_VI))
|
|
# endif
|
|
readf = ed_viread;
|
|
else
|
|
# endif /* SHOPT_VSH */
|
|
readf = ed_read;
|
|
if(sh.trapnote)
|
|
{
|
|
errno = EINTR;
|
|
return(-1);
|
|
}
|
|
while(1)
|
|
{
|
|
if(io_prompt(iop,sh.nextprompt)<0 && errno==EIO)
|
|
return(0);
|
|
if(sh.timeout)
|
|
timeout = (void*)sh_timeradd(sh_isstate(SH_GRACE)?1000L*TGRACE:1000L*sh.timeout,0,time_grace,&sh);
|
|
rsize = (*readf)(sh.ed_context, sffileno(iop), (char*)buff, size, reedit);
|
|
if(timeout)
|
|
timerdel(timeout);
|
|
timeout=0;
|
|
#if SHOPT_HISTEXPAND
|
|
if(rsize && *(char*)buff != '\n' && sh.nextprompt==1 && sh_isoption(SH_HISTEXPAND))
|
|
{
|
|
int r;
|
|
((char*)buff)[rsize] = '\0';
|
|
if(xp)
|
|
{
|
|
free(xp);
|
|
xp = 0;
|
|
}
|
|
r = hist_expand(buff, &xp);
|
|
if((r & (HIST_EVENT|HIST_PRINT)) && !(r & HIST_ERROR) && xp)
|
|
{
|
|
strlcpy(buff, xp, size);
|
|
rsize = strlen(buff);
|
|
if(!sh_isoption(SH_HISTVERIFY) || readf==ed_read)
|
|
{
|
|
sfputr(sfstderr, xp, -1);
|
|
break;
|
|
}
|
|
reedit = rsize - 1;
|
|
continue;
|
|
}
|
|
if((r & HIST_ERROR) && sh_isoption(SH_HISTREEDIT))
|
|
{
|
|
reedit = rsize - 1;
|
|
continue;
|
|
}
|
|
if(r & (HIST_ERROR|HIST_PRINT))
|
|
{
|
|
*(char*)buff = '\n';
|
|
rsize = 1;
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
return(rsize);
|
|
}
|
|
|
|
/*
|
|
* check and return the attributes for a file descriptor
|
|
*/
|
|
|
|
int sh_iocheckfd(register int fd)
|
|
{
|
|
register int flags, n;
|
|
if((n=sh.fdstatus[fd])&IOCLOSE)
|
|
return(n);
|
|
if(!(n&(IOREAD|IOWRITE)))
|
|
{
|
|
#ifdef F_GETFL
|
|
if((flags=fcntl(fd,F_GETFL,0)) < 0)
|
|
return(sh.fdstatus[fd]=IOCLOSE);
|
|
if((flags&O_ACCMODE)!=O_WRONLY)
|
|
n |= IOREAD;
|
|
if((flags&O_ACCMODE)!=O_RDONLY)
|
|
n |= IOWRITE;
|
|
#else
|
|
struct stat statb;
|
|
if((flags = fstat(fd,&statb))< 0)
|
|
return(sh.fdstatus[fd]=IOCLOSE);
|
|
n |= (IOREAD|IOWRITE);
|
|
if(read(fd,"",0) < 0)
|
|
n &= ~IOREAD;
|
|
#endif /* F_GETFL */
|
|
}
|
|
if(!(n&(IOSEEK|IONOSEEK)))
|
|
{
|
|
struct stat statb;
|
|
/* /dev/null check is a workaround for select bug */
|
|
static ino_t null_ino;
|
|
static dev_t null_dev;
|
|
if(null_ino==0 && stat(e_devnull,&statb) >=0)
|
|
{
|
|
null_ino = statb.st_ino;
|
|
null_dev = statb.st_dev;
|
|
}
|
|
if(tty_check(fd))
|
|
n |= IOTTY;
|
|
if(lseek(fd,NIL(off_t),SEEK_CUR)<0)
|
|
{
|
|
n |= IONOSEEK;
|
|
#ifdef S_ISSOCK
|
|
if((fstat(fd,&statb)>=0) && S_ISSOCK(statb.st_mode))
|
|
{
|
|
n |= IOREAD|IOWRITE;
|
|
# if _socketpair_shutdown_mode
|
|
if(!(statb.st_mode&S_IRUSR))
|
|
n &= ~IOREAD;
|
|
else if(!(statb.st_mode&S_IWUSR))
|
|
n &= ~IOWRITE;
|
|
# endif
|
|
}
|
|
#endif /* S_ISSOCK */
|
|
}
|
|
else if((fstat(fd,&statb)>=0) && (
|
|
S_ISFIFO(statb.st_mode) ||
|
|
#ifdef S_ISSOCK
|
|
S_ISSOCK(statb.st_mode) ||
|
|
#endif /* S_ISSOCK */
|
|
/* The following is for sockets on the sgi */
|
|
(statb.st_ino==0 && (statb.st_mode & ~(S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH|S_IXUSR|S_IXGRP|S_IXOTH|S_ISUID|S_ISGID))==0) ||
|
|
(S_ISCHR(statb.st_mode) && (statb.st_ino!=null_ino || statb.st_dev!=null_dev))
|
|
))
|
|
n |= IONOSEEK;
|
|
else
|
|
n |= IOSEEK;
|
|
}
|
|
if(fd==0)
|
|
n &= ~IOWRITE;
|
|
else if(fd==1)
|
|
n &= ~IOREAD;
|
|
sh.fdstatus[fd] = n;
|
|
return(n);
|
|
}
|
|
|
|
/*
|
|
* Display prompt PS<flag> on standard error
|
|
*/
|
|
|
|
static int io_prompt(Sfio_t *iop,register int flag)
|
|
{
|
|
register char *cp;
|
|
char buff[1];
|
|
char *endprompt;
|
|
static short cmdno;
|
|
int sfflags;
|
|
int was_ttywait_on;
|
|
if(flag<3 && !sh_isstate(SH_INTERACTIVE))
|
|
flag = 0;
|
|
if(flag==2 && sfpkrd(sffileno(iop),buff,1,'\n',0,1) >= 0)
|
|
flag = 0;
|
|
if(flag==0)
|
|
return(sfsync(sfstderr));
|
|
/* Temporarily disable 'set -o notify' while expanding the prompt to avoid
|
|
possible crashes (https://github.com/ksh93/ksh/issues/103). */
|
|
was_ttywait_on = sh_isstate(SH_TTYWAIT);
|
|
sh_offstate(SH_TTYWAIT);
|
|
sfflags = sfset(sfstderr,SF_SHARE|SF_PUBLIC|SF_READ,0);
|
|
if(!(sh.prompt=(char*)sfreserve(sfstderr,0,0)))
|
|
sh.prompt = "";
|
|
switch(flag)
|
|
{
|
|
case 1:
|
|
{
|
|
register int c;
|
|
sh_lexopen(sh.lex_context, 0); /* reset lexer state */
|
|
#if defined(TIOCLBIC) && defined(LFLUSHO)
|
|
if(!sh_isoption(SH_VI) && !sh_isoption(SH_EMACS) && !sh_isoption(SH_GMACS))
|
|
{
|
|
/*
|
|
* re-enable output in case the user has
|
|
* disabled it. Not needed with edit mode
|
|
*/
|
|
int mode = LFLUSHO;
|
|
ioctl(sffileno(sfstderr),TIOCLBIC,&mode);
|
|
}
|
|
#endif /* TIOCLBIC */
|
|
cp = sh_mactry(nv_getval(sh_scoped(PS1NOD)));
|
|
sh.exitval = 0; /* avoid sending a signal on termination */
|
|
for(;c= *cp;cp++)
|
|
{
|
|
if(c==HIST_CHAR)
|
|
{
|
|
/* look at next character */
|
|
c = *++cp;
|
|
/* print out line number if not !! */
|
|
if(c!= HIST_CHAR)
|
|
{
|
|
sfprintf(sfstderr,"%d", sh.hist_ptr?(int)sh.hist_ptr->histind:++cmdno);
|
|
}
|
|
if(c==0)
|
|
goto done;
|
|
}
|
|
sfputc(sfstderr,c);
|
|
}
|
|
goto done;
|
|
}
|
|
case 2:
|
|
{
|
|
/* PS2 prompt. Save stack state to avoid corrupting command substitutions
|
|
* in case we're executing a PS2.get discipline function at parse time. */
|
|
int savestacktop = staktell();
|
|
char *savestackptr = stakfreeze(0);
|
|
cp = nv_getval(sh_scoped(PS2NOD));
|
|
stakset(savestackptr, savestacktop);
|
|
break;
|
|
}
|
|
case 3:
|
|
cp = nv_getval(sh_scoped(PS3NOD));
|
|
break;
|
|
default:
|
|
goto done;
|
|
}
|
|
if(cp)
|
|
sfputr(sfstderr,cp,-1);
|
|
done:
|
|
if(*sh.prompt && (endprompt=(char*)sfreserve(sfstderr,0,0)))
|
|
*endprompt = 0;
|
|
if(was_ttywait_on)
|
|
sh_onstate(SH_TTYWAIT); /* re-enable 'set -o notify' */
|
|
sfset(sfstderr,sfflags&SF_READ|SF_SHARE|SF_PUBLIC,1);
|
|
return(sfsync(sfstderr));
|
|
}
|
|
|
|
/*
|
|
* This discipline is inserted on write pipes to prevent SIGPIPE
|
|
* from causing an infinite loop
|
|
*/
|
|
static int pipeexcept(Sfio_t* iop, int mode, void *data, Sfdisc_t* handle)
|
|
{
|
|
if(mode==SF_DPOP || mode==SF_FINAL)
|
|
free((void*)handle);
|
|
else if(mode==SF_WRITE && ERROR_PIPE(errno))
|
|
{
|
|
sfpurge(iop);
|
|
return(-1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* keep track of each stream that is opened and closed
|
|
*/
|
|
static void sftrack(Sfio_t* sp, int flag, void* data)
|
|
{
|
|
register int fd = sffileno(sp);
|
|
register struct checkpt *pp;
|
|
register int mode;
|
|
int newfd = integralof(data);
|
|
if(flag==SF_SETFD || flag==SF_CLOSING)
|
|
{
|
|
if(newfd<0)
|
|
flag = SF_CLOSING;
|
|
if(fdnotify)
|
|
(*fdnotify)(sffileno(sp),flag==SF_CLOSING?-1:newfd);
|
|
}
|
|
#ifdef DEBUG
|
|
if(flag==SF_READ || flag==SF_WRITE)
|
|
{
|
|
char *z = fmtbase((long)sh.current_pid,0,0);
|
|
write(ERRIO,z,strlen(z));
|
|
write(ERRIO,": ",2);
|
|
write(ERRIO,"attempt to ",11);
|
|
if(flag==SF_READ)
|
|
write(ERRIO,"read from",9);
|
|
else
|
|
write(ERRIO,"write to",8);
|
|
write(ERRIO," locked stream\n",15);
|
|
return;
|
|
}
|
|
#endif
|
|
if(fd<0 || fd==PSEUDOFD || (fd>=sh.lim.open_max && !sh_iovalidfd(fd)))
|
|
return;
|
|
if(sh_isstate(SH_NOTRACK))
|
|
return;
|
|
mode = sfset(sp,0,0);
|
|
if(sp==sh.heredocs && fd < 10 && flag==SF_SETFD)
|
|
{
|
|
fd = sfsetfd(sp,10);
|
|
fcntl(fd,F_SETFD,FD_CLOEXEC);
|
|
}
|
|
if(fd < 3)
|
|
return;
|
|
if(flag==SF_NEW)
|
|
{
|
|
if(!sh.sftable[fd] && sh.fdstatus[fd]==IOCLOSE)
|
|
{
|
|
sh.sftable[fd] = sp;
|
|
flag = (mode&SF_WRITE)?IOWRITE:0;
|
|
if(mode&SF_READ)
|
|
flag |= IOREAD;
|
|
sh.fdstatus[fd] = flag;
|
|
sh_iostream(fd);
|
|
}
|
|
if((pp=(struct checkpt*)sh.jmplist) && pp->mode==SH_JMPCMD)
|
|
{
|
|
struct openlist *item;
|
|
/*
|
|
* record open file descriptors so they can
|
|
* be closed in case a longjmp prevents
|
|
* built-ins from cleanup
|
|
*/
|
|
item = new_of(struct openlist, 0);
|
|
item->strm = sp;
|
|
item->next = pp->olist;
|
|
pp->olist = item;
|
|
}
|
|
if(fdnotify)
|
|
(*fdnotify)(-1,sffileno(sp));
|
|
}
|
|
else if(flag==SF_CLOSING || (flag==SF_SETFD && newfd<=2))
|
|
{
|
|
sh.sftable[fd] = 0;
|
|
sh.fdstatus[fd]=IOCLOSE;
|
|
if(pp=(struct checkpt*)sh.jmplist)
|
|
{
|
|
struct openlist *item;
|
|
for(item=pp->olist; item; item=item->next)
|
|
{
|
|
if(item->strm == sp)
|
|
{
|
|
item->strm = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct eval
|
|
{
|
|
Sfdisc_t disc;
|
|
char **argv;
|
|
short slen;
|
|
char addspace;
|
|
};
|
|
|
|
/*
|
|
* Create a stream consisting of a space separated argv[] list
|
|
*/
|
|
|
|
Sfio_t *sh_sfeval(register char *argv[])
|
|
{
|
|
register Sfio_t *iop;
|
|
register char *cp;
|
|
if(argv[1])
|
|
cp = "";
|
|
else
|
|
cp = argv[0];
|
|
iop = sfopen(NIL(Sfio_t*),(char*)cp,"s");
|
|
if(argv[1])
|
|
{
|
|
register struct eval *ep;
|
|
ep = new_of(struct eval,0);
|
|
ep->disc = eval_disc;
|
|
ep->argv = argv;
|
|
ep->slen = -1;
|
|
ep->addspace = 0;
|
|
sfdisc(iop,&ep->disc);
|
|
}
|
|
return(iop);
|
|
}
|
|
|
|
/*
|
|
* This code gets called whenever an end of string is found with eval
|
|
*/
|
|
|
|
static int eval_exceptf(Sfio_t *iop,int type, void *data, Sfdisc_t *handle)
|
|
{
|
|
register struct eval *ep = (struct eval*)handle;
|
|
register char *cp;
|
|
register int len;
|
|
|
|
/* no more to do */
|
|
if(type!=SF_READ || !(cp = ep->argv[0]))
|
|
{
|
|
if(type==SF_CLOSING)
|
|
sfdisc(iop,SF_POPDISC);
|
|
else if(ep && (type==SF_DPOP || type==SF_FINAL))
|
|
free((void*)ep);
|
|
return(0);
|
|
}
|
|
|
|
if(!ep->addspace)
|
|
{
|
|
/* get the length of this string */
|
|
ep->slen = len = strlen(cp);
|
|
/* move to next string */
|
|
ep->argv++;
|
|
}
|
|
else /* insert space between arguments */
|
|
{
|
|
len = 1;
|
|
cp = " ";
|
|
}
|
|
/* insert the new string */
|
|
sfsetbuf(iop,cp,len);
|
|
ep->addspace = !ep->addspace;
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* This routine returns a stream pointer to a segment of length <size> from
|
|
* the stream <sp> starting at offset <offset>
|
|
* The stream can be read with the normal stream operations
|
|
*/
|
|
|
|
static Sfio_t *subopen(Sfio_t* sp, off_t offset, long size)
|
|
{
|
|
register struct subfile *disp;
|
|
if(sfseek(sp,offset,SEEK_SET) <0)
|
|
return(NIL(Sfio_t*));
|
|
disp = (struct subfile*)sh_malloc(sizeof(struct subfile)+IOBSIZE+1);
|
|
disp->disc = sub_disc;
|
|
disp->oldsp = sp;
|
|
disp->offset = offset;
|
|
disp->size = disp->left = size;
|
|
sp = sfnew(NIL(Sfio_t*),(char*)(disp+1),IOBSIZE,PSEUDOFD,SF_READ);
|
|
sfdisc(sp,&disp->disc);
|
|
return(sp);
|
|
}
|
|
|
|
/*
|
|
* read function for subfile discipline
|
|
*/
|
|
static ssize_t subread(Sfio_t* sp,void* buff,register size_t size,Sfdisc_t* handle)
|
|
{
|
|
register struct subfile *disp = (struct subfile*)handle;
|
|
ssize_t n;
|
|
NOT_USED(sp);
|
|
sfseek(disp->oldsp,disp->offset,SEEK_SET);
|
|
if(disp->left == 0)
|
|
return(0);
|
|
if(size > disp->left)
|
|
size = disp->left;
|
|
disp->left -= size;
|
|
n = sfread(disp->oldsp,buff,size);
|
|
if(size>0)
|
|
disp->offset += size;
|
|
return(n);
|
|
}
|
|
|
|
/*
|
|
* exception handler for subfile discipline
|
|
*/
|
|
static int subexcept(Sfio_t* sp,register int mode, void *data, Sfdisc_t* handle)
|
|
{
|
|
register struct subfile *disp = (struct subfile*)handle;
|
|
if(mode==SF_CLOSING)
|
|
{
|
|
sfdisc(sp,SF_POPDISC);
|
|
sfsetfd(sp,-1);
|
|
return(0);
|
|
}
|
|
else if(disp && (mode==SF_DPOP || mode==SF_FINAL))
|
|
{
|
|
free((void*)disp);
|
|
return(0);
|
|
}
|
|
else if (mode==SF_ATEXIT)
|
|
{
|
|
sfdisc(sp, SF_POPDISC);
|
|
return(0);
|
|
}
|
|
else if(mode==SF_READ)
|
|
return(0);
|
|
return(-1);
|
|
}
|
|
|
|
#define NROW 15 /* number of rows before going to multi-columns */
|
|
#define LBLSIZ 3 /* size of label field and interfield spacing */
|
|
/*
|
|
* print a list of arguments in columns
|
|
*/
|
|
void sh_menu(Sfio_t *outfile,int argn,char *argv[])
|
|
{
|
|
register int i,j;
|
|
register char **arg;
|
|
int nrow, ncol=1, ndigits=1;
|
|
int fldsize, wsize = ed_window();
|
|
char *cp = nv_getval(sh_scoped(LINES));
|
|
nrow = (cp?1+2*((int)strtol(cp, (char**)0, 10)/3):NROW);
|
|
for(i=argn;i >= 10;i /= 10)
|
|
ndigits++;
|
|
if(argn < nrow)
|
|
{
|
|
nrow = argn;
|
|
goto skip;
|
|
}
|
|
i = 0;
|
|
for(arg=argv; *arg;arg++)
|
|
{
|
|
if((j=strlen(*arg)) > i)
|
|
i = j;
|
|
}
|
|
i += (ndigits+LBLSIZ);
|
|
if(i < wsize)
|
|
ncol = wsize/i;
|
|
if(argn > nrow*ncol)
|
|
{
|
|
nrow = 1 + (argn-1)/ncol;
|
|
}
|
|
else
|
|
{
|
|
ncol = 1 + (argn-1)/nrow;
|
|
nrow = 1 + (argn-1)/ncol;
|
|
}
|
|
skip:
|
|
fldsize = (wsize/ncol)-(ndigits+LBLSIZ);
|
|
for(i=0;i<nrow;i++)
|
|
{
|
|
if(sh.trapnote&SH_SIGSET)
|
|
return;
|
|
j = i;
|
|
while(1)
|
|
{
|
|
arg = argv+j;
|
|
sfprintf(outfile,"%*d) %s",ndigits,j+1,*arg);
|
|
j += nrow;
|
|
if(j >= argn)
|
|
break;
|
|
sfnputc(outfile,' ',fldsize-strlen(*arg));
|
|
}
|
|
sfputc(outfile,'\n');
|
|
}
|
|
}
|
|
|
|
#undef read
|
|
/*
|
|
* shell version of read() for user added builtins
|
|
*/
|
|
ssize_t sh_read(register int fd, void* buff, size_t n)
|
|
{
|
|
register Sfio_t *sp;
|
|
if(sp=sh.sftable[fd])
|
|
return(sfread(sp,buff,n));
|
|
else
|
|
return(read(fd,buff,n));
|
|
}
|
|
|
|
#undef write
|
|
/*
|
|
* shell version of write() for user added builtins
|
|
*/
|
|
ssize_t sh_write(register int fd, const void* buff, size_t n)
|
|
{
|
|
register Sfio_t *sp;
|
|
if(sp=sh.sftable[fd])
|
|
return(sfwrite(sp,buff,n));
|
|
else
|
|
return(write(fd,buff,n));
|
|
}
|
|
|
|
#undef lseek
|
|
/*
|
|
* shell version of lseek() for user added builtins
|
|
*/
|
|
off_t sh_seek(register int fd, off_t offset, int whence)
|
|
{
|
|
register Sfio_t *sp;
|
|
if((sp=sh.sftable[fd]) && (sfset(sp,0,0)&(SF_READ|SF_WRITE)))
|
|
return(sfseek(sp,offset,whence));
|
|
else
|
|
return(lseek(fd,offset,whence));
|
|
}
|
|
|
|
#undef dup
|
|
int sh_dup(register int old)
|
|
{
|
|
register int fd = dup(old);
|
|
if(fd>=0)
|
|
{
|
|
if(sh.fdstatus[old] == IOCLOSE)
|
|
sh.fdstatus[old] = 0;
|
|
sh.fdstatus[fd] = (sh.fdstatus[old]&~IOCLEX);
|
|
if(fdnotify)
|
|
(*fdnotify)(old,fd);
|
|
}
|
|
return(fd);
|
|
}
|
|
|
|
#undef fcntl
|
|
int sh_fcntl(register int fd, int op, ...)
|
|
{
|
|
int newfd, arg;
|
|
va_list ap;
|
|
va_start(ap, op);
|
|
arg = va_arg(ap, int) ;
|
|
va_end(ap);
|
|
newfd = fcntl(fd,op,arg);
|
|
if(newfd>=0) switch(op)
|
|
{
|
|
case F_DUPFD:
|
|
if(sh.fdstatus[fd] == IOCLOSE)
|
|
sh.fdstatus[fd] = 0;
|
|
if(newfd>=sh.lim.open_max)
|
|
sh_iovalidfd(newfd);
|
|
sh.fdstatus[newfd] = (sh.fdstatus[fd]&~IOCLEX);
|
|
if(fdnotify)
|
|
(*fdnotify)(fd,newfd);
|
|
break;
|
|
case F_SETFD:
|
|
if(sh.fdstatus[fd] == IOCLOSE)
|
|
sh.fdstatus[fd] = 0;
|
|
if(arg&FD_CLOEXEC)
|
|
sh.fdstatus[fd] |= IOCLEX;
|
|
else
|
|
sh.fdstatus[fd] &= ~IOCLEX;
|
|
}
|
|
return(newfd);
|
|
}
|
|
|
|
#undef umask
|
|
mode_t sh_umask(mode_t m)
|
|
{
|
|
sh.mask = m;
|
|
return(umask(m));
|
|
}
|
|
|
|
/*
|
|
* give file descriptor <fd> and <mode>, return an iostream pointer
|
|
* <mode> must be SF_READ or SF_WRITE
|
|
* <fd> must be a non-negative number ofr SH_IOCOPROCESS or SH_IOHISTFILE.
|
|
* returns NULL on failure and may set errno.
|
|
*/
|
|
|
|
Sfio_t *sh_iogetiop(int fd, int mode)
|
|
{
|
|
int n;
|
|
Sfio_t *iop=0;
|
|
if(mode!=SF_READ && mode!=SF_WRITE)
|
|
{
|
|
errno = EINVAL;
|
|
return(iop);
|
|
}
|
|
switch(fd)
|
|
{
|
|
case SH_IOHISTFILE:
|
|
if(!sh_histinit())
|
|
return(iop);
|
|
fd = sffileno(sh.hist_ptr->histfp);
|
|
break;
|
|
case SH_IOCOPROCESS:
|
|
if(mode==SF_WRITE)
|
|
fd = sh.coutpipe;
|
|
else
|
|
fd = sh.cpipe[0];
|
|
break;
|
|
default:
|
|
if(fd<0 || !sh_iovalidfd(fd))
|
|
fd = -1;
|
|
}
|
|
if(fd<0)
|
|
{
|
|
errno = EBADF;
|
|
return(iop);
|
|
}
|
|
if(!(n=sh.fdstatus[fd]))
|
|
n = sh_iocheckfd(fd);
|
|
if(mode==SF_WRITE && !(n&IOWRITE))
|
|
return(iop);
|
|
if(mode==SF_READ && !(n&IOREAD))
|
|
return(iop);
|
|
if(!(iop = sh.sftable[fd]))
|
|
iop=sh_iostream(fd);
|
|
return(iop);
|
|
}
|
|
|
|
typedef int (*Notify_f)(int,int);
|
|
|
|
Notify_f sh_fdnotify(Notify_f notify)
|
|
{
|
|
Notify_f old;
|
|
old = fdnotify;
|
|
fdnotify = notify;
|
|
return(old);
|
|
}
|
|
|
|
Sfio_t *sh_fd2sfio(int fd)
|
|
{
|
|
register int status;
|
|
Sfio_t *sp = sh.sftable[fd];
|
|
if(!sp && (status = sh_iocheckfd(fd))!=IOCLOSE)
|
|
{
|
|
register int flags=0;
|
|
if(status&IOREAD)
|
|
flags |= SF_READ;
|
|
if(status&IOWRITE)
|
|
flags |= SF_WRITE;
|
|
sp = sfnew(NULL, NULL, -1, fd,flags);
|
|
sh.sftable[fd] = sp;
|
|
}
|
|
return(sp);
|
|
}
|
|
|
|
Sfio_t *sh_pathopen(const char *cp)
|
|
{
|
|
int n;
|
|
if((n=path_open(cp,path_get(cp))) < 0)
|
|
n = path_open(cp,(Pathcomp_t*)0);
|
|
if(n < 0)
|
|
{
|
|
errormsg(SH_DICT,ERROR_system(1),e_open,cp);
|
|
UNREACHABLE();
|
|
}
|
|
return(sh_iostream(n));
|
|
}
|
|
|
|
int sh_isdevfd(register const char *fd)
|
|
{
|
|
if(!fd || strncmp(fd,"/dev/fd/",8) || fd[8]==0)
|
|
return(0);
|
|
for ( fd=&fd[8] ; *fd != '\0' ; fd++ )
|
|
{
|
|
if (*fd < '0' || *fd > '9')
|
|
return(0);
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
#undef fchdir
|
|
int sh_fchdir(int fd)
|
|
{
|
|
int r,err=errno;
|
|
while((r=fchdir(fd))<0 && errno==EINTR)
|
|
errno = err;
|
|
return(r);
|
|
}
|
|
|
|
#undef chdir
|
|
int sh_chdir(const char* dir)
|
|
{
|
|
int r,err=errno;
|
|
while((r=chdir(dir))<0 && errno==EINTR)
|
|
errno = err;
|
|
return(r);
|
|
}
|