io.c revision 3f54fd611f536639ec30dd53c48e5ec1897cc7d9
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1982-2012 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* David Korn <dgk@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
*
* 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 "shnodes.h"
#include "history.h"
#include "edit.h"
#include "timeout.h"
#ifdef FNDELAY
# ifdef EAGAIN
# if EAGAIN!=EWOULDBLOCK
# 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
#endif
#ifndef ERROR_PIPE
#ifdef ECONNRESET
#else
#define ERROR_PIPE(e) ((e)==EPIPE)
#endif
#endif
static void *timeout;
static int (*fdnotify)(int,int);
# include <netdb.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
# 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
#define EAI_SYSTEM 1
#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;
};
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 sockaddr_in* ip;
char* prot;
long n;
{
return EAI_SYSTEM;
}
else
{
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)
{
return 1;
}
}
if (!ip_port)
{
return EAI_SYSTEM;
}
return EAI_SYSTEM;
if (hint)
return 0;
}
static void
{
if (ap)
}
#endif
/*
* return <protocol>/<host>/<service> fd
* If called with flags==O_NONBLOCK return 1 if protocol is supported
*/
typedef int (*Inetintr_f)(struct addrinfo*, void*);
static int
{
register char* s;
register char* t;
int fd;
int oerrno;
struct addrinfo* p;
switch (path[0])
{
#ifdef IPPROTO_SCTP
case 's':
{
return -1;
}
path += 5;
break;
#endif
case 't':
{
return -1;
}
path += 4;
break;
case 'u':
{
return -1;
}
path += 4;
break;
default:
return -1;
}
if(flags==O_NONBLOCK)
return 1;
return -1;
if (t = strchr(s, '/'))
{
*t++ = 0;
if (streq(s, "local"))
s = strdup("localhost");
}
else
fd = -1;
free(s);
if (fd)
{
if (fd != EAI_SYSTEM)
return -1;
}
errno = 0;
fd = -1;
{
/*
* some api's don't take the hint
*/
if (!p->ai_protocol)
if (!p->ai_socktype)
{
if (server && !bind(fd, p->ai_addr, p->ai_addrlen) && !listen(fd, 5) || !server && !connect(fd, p->ai_addr, p->ai_addrlen))
goto done;
fd = -1;
break;
goto done;
}
}
done:
if (fd >= 0)
return fd;
}
#else
#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 >; */
};
struct Iodisc
{
};
struct subfile
{
long size;
long left;
};
struct Eof
{
int fd;
};
{
return((Sfdouble_t)cur);
if(cur<0)
return((Sfdouble_t)-1);
return((Sfdouble_t)end);
}
struct Match
{
char *base;
};
{
return(1);
}
static short filemapsize;
/* ======== input output and file copying ======== */
{
if(fd<0)
return(0);
return(1);
{
return(0);
}
if(n > max)
n = max;
if(max)
if(max)
if(max)
if(sftable)
return(1);
}
{
}
{
filemapsize = 8;
sh_iostream(shp,0);
/* all write steams are in the same pool and share outbuff */
}
/*
* Handle output stream exceptions
*/
{
static int active = 0;
switch (errno)
{
case EINTR:
case EPIPE:
#ifdef ECONNRESET
case ECONNRESET:
#endif
#ifdef ESHUTDOWN
case ESHUTDOWN:
#endif
break;
default:
if(!active)
{
active = 1;
active = 0;
sh_exit(1);
}
return(-1);
}
return(0);
}
/*
* create or initialize a stream corresponding to descriptor <fd>
* a buffer with room for a sentinal 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
*/
{
char *bp;
{
switch(fd)
{
case 0:
return(sfstdin);
case 1:
return(sfstdout);
case 2:
return(sfstderr);
}
}
{
}
else
{
}
{
{
}
else
}
else
{
else
}
return(iop);
}
/*
* preserve the file descriptor or stream by moving it
*/
{
register int fd;
if(sp)
else
if(fd<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;
*/
{
{
/* see whether file descriptor is in use */
{
sp = 0;
}
else if(f2==0)
{
}
else
{
else if(f2 <= 2)
}
if(sp)
}
else if(sp)
{
if(f2<=2)
}
return(f2);
}
/*
* close a file descriptor and update stream table and attributes
*/
{
register int r = 0;
if(fd<0)
return(-1);
{
if(fdnotify)
}
if(fd>2)
if(fd < 10)
return(r);
}
#ifdef O_SERVICE
static int
{
{
return -1;
}
sh_chktrap(sh);
return 0;
}
#endif
/*
* Mimic open(2) with checks for pseudo /dev/ files.
*/
{
register int fd = -1;
char *e;
errno = 0;
if(path==0)
{
return(-1);
}
if(*path==0)
{
return(-1);
}
{
switch (path[5])
{
case 'f':
{
if(flags==O_NONBLOCK)
return(1);
if (*e)
fd = -1;
}
break;
case 's':
switch (path[8])
{
case 'e':
fd = 2;
break;
case 'i':
fd = 0;
break;
case 'o':
fd = 1;
break;
}
}
#ifdef O_SERVICE
if (fd < 0)
{
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;
{
}
else
if(nfd>=0)
{
goto ok;
}
return(-1);
return(-1);
return(-1);
return(-1);
}
else
{
#if SHOPT_REGRESS
{
}
#endif
return(-1);
}
ok:
else
return(fd);
}
/*
* Open a file for reading
* On failure, print message.
*/
int sh_chkopen(register const char *name)
{
if(fd < 0)
return(fd);
}
/*
* move open file descriptor to a number > 2
*/
int sh_iomovefd(register int fdold)
{
register int fdnew;
return(fdold);
return(fdnew);
}
/*
* create a pipe and print message on failure
*/
{
int fd[2];
sh_subsavefd(pv[0]);
return(0);
}
#ifndef pipe
{
}
#else
/* create a real pipe when pipe() is socketpair */
{
int fd[2];
sh_subsavefd(pv[0]);
return(0);
}
#endif
#if SHOPT_COSHELL
{
pv[0] = -1;
if(fd<0)
else
#endif
return(0);
}
{
int r,port=20000;
struct sockaddr_in sin;
do
{
}
{
}
return(0);
}
#endif /* SHOPT_COSHELL */
{
return(-1);
}
{
while(n>0)
{
}
}
{
size_t n,m;
if(fd==0)
{
n--;
if(n)
m = n;
if(r<0)
else if(r==2)
{
r = -1;
}
if(r<0)
break;
}
if(!close_exec)
return(0);
}
{
char *cp;
if(mp)
if(pp)
if(sp)
if(mp)
if(pp)
}
/*
* close a pipe
*/
{
if(pv[0]>=2)
}
{
int fd,r;
if(mode==0)
{
{
if(r)
return(0);
return(0);
}
return(0);
}
{
}
stakseek(1);
stakputc(0);
{
}
else
{
stakseek(0);
}
stakputc('.');
switch(mode)
{
case 1:
break;
default:
break;
}
return(tname);
}
/*
* I/O redirection
* flag = 0 if files are to be restored
* flag = 2 if files are to be closed on exec
* flag = 3 when called from $( < ...), just open file and return
* flag = SH_SHOWME for trace only
*/
{
register char *fname;
int o_mode; /* mode flag for open */
if(flag==2)
clexec = 1;
if(iop)
{
sh_subfork();
{
}
else
{
}
io_op[2] = 0;
io_op[3] = 0;
io_op[4] = 0;
{
{
}
{
}
else
}
errno=0;
np = 0;
#if SHOPT_COSHELL
{
continue;
}
#endif /* SHOPT_COSHELL */
{
io_op[0] = '}';
}
{
{
after = "))";
}
goto traceit;
}
{
{
if(traceon)
fname = 0;
}
{
{
if(*number=='-')
{
number++;
}
{
goto fail;
}
{
}
}
{
fd= -1;
goto traceit;
}
{
else
if(flag)
}
else
{
goto fail;
}
goto traceit;
goto fail;
else if(toclose>=0)
{
if(flag==0)
}
}
{
if(sh_isoption(SH_RESTRICTED))
goto openit;
}
{
goto traceit;
}
else if(sh_isoption(SH_RESTRICTED))
else
{
{
}
{
}
else
{
else if(sh_isoption(SH_NOCLOBBER))
{
{
#if SHOPT_FS_3D
#else
#endif /* SHOPT_FS_3D */
{
}
}
else
}
}
{
if(perm>0)
#if _lib_fchmod
#else
#endif
}
}
{
if(np)
}
return(indx);
{
av[5] = 0;
av[6] = 0;
if(np)
{
av[0] = "{";
}
else
av +=3;
}
{
if(r==IOCLOSE)
{
goto fail;
}
{
if(r&IONOSEEK)
{
goto fail;
}
goto fail;
if(sp)
{
}
else
if(off<0)
r = -1;
}
else
{
extern const char e_notimp[];
if(!(r&IOREAD))
{
goto fail;
}
if(!(rp = regcache(fname, REG_SHELL|REG_NOSUB|REG_NEWLINE|REG_AUGMENTED|REG_FIRST|REG_LEFT|REG_RIGHT, &r)))
{
goto fail;
}
if(!sp)
{
/* close stream but not fn */
}
}
if(r<0)
goto fail;
if(flag==3)
return(fn);
continue;
}
if(!np)
{
if(flag==0 || tname || (flag==1 && fn==1 && (shp->fdstatus[fn]&IONOSEEK) && shp->outpipepid && shp->outpipepid==getpid()))
{
{
{
fd = r;
}
}
}
else if(sh_subsavefd(fn))
}
if(fd<0)
{
{
}
}
if(flag==3)
return(fd);
if(fd>=0)
{
if(np)
{
int32_t v;
if(fd<10)
{
goto fail;
goto fail;
}
v = fn;
}
else
{
}
}
{
}
}
else
goto fail;
}
return(indx);
fail:
/* NOTREACHED */
return(0);
}
/*
* Create a tmp file for the here-document
*/
{
register int fd;
/* create an unnamed temporary file */
{
if(traceon)
}
else
{
/*
* the locking is only needed in case & blocks process
* here-docs so this can be eliminted in some cases
*/
if(fno>=0)
{
}
if(traceon)
{
}
{
/* This is a quoted here-document, not expansion */
{
}
if(fno>=0)
}
{
if(infile)
}
}
/* close stream outfile, but save file descriptor */
return(fd);
}
/*
* This write discipline also writes the output on standard error
* This is used when tracing here-documents
*/
{
}
/*
* 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.
*/
{
register int savefd;
/* see if already saved, only save once */
{
return;
}
/* make sure table is large enough */
{
long moved;
filemapsize += 8;
{
{
}
}
}
#if SHOPT_DEVFD
if(origfd <0)
{
}
else
#endif /* SHOPT_DEVFD */
savefd = -1;
else
{
{
}
}
if(savefd >=0)
{
/* make saved file close-on-exec */
return;
if(origfd <=2)
{
/* copy standard stream to new stream */
}
else
}
}
/*
* close all saved file descriptors
*/
{
{
else
{
}
}
}
/*
* restore saved file descriptors from <last> on
*/
{
last &= ~IOSUBSHELL;
{
continue;
if(jmpval==SH_JMPSCRIPT)
{
{
}
continue;
}
if(origfd<0)
{
/* this should never happen */
return;
}
{
/* turn off close-on-exec if flag if necessary */
if(origfd<=2)
{
if(origfd==0)
}
else
}
else
}
if(!flag)
{
/* keep file descriptors for subshell restore */
{
}
}
}
/*
* returns access information on open file <fd>
* returns -1 for failure, 0 for success
* <mode> is the same as for access()
*/
{
register int flags;
return(-1);
{
return(0);
return(0);
return(0);
}
return(-1);
}
/*
* Handle interrupts for slow streams
*/
{
register int n,fno;
{
return(-1);
}
return(0);
{
#ifndef FNDELAY
# ifdef O_NDELAY
{
n &= ~O_NDELAY;
return(1);
}
# endif /* O_NDELAY */
#endif /* !FNDELAY */
#ifdef O_NONBLOCK
{
n &= ~O_NONBLOCK;
return(1);
}
#endif /* O_NONBLOCK */
return(0);
return(-1);
n=1;
}
else
n = 0;
return(-1);
errno = 0;
{
}
return(n);
}
/*
* called when slowread times out
*/
static void time_grace(void *handle)
{
timeout = 0;
if(sh_isstate(SH_GRACE))
{
if(!sh_isstate(SH_INTERACTIVE))
return;
return;
}
}
{
{
job_lock();
job_unlock();
}
{
return(-1);
}
if(sh_isstate(SH_INTERACTIVE) && sffileno(iop)==0 && io_prompt(shp,iop,shp->nextprompt)<0 && errno==EIO)
return(0);
else
return(size);
}
/*
* This is the read discipline that is applied to slow devices
* This routine takes care of prompting for input
*/
{
int (*readf)(void*, int, char*, int, int);
#if SHOPT_HISTEXPAND
char *xp=0;
#endif
# if SHOPT_ESH
else
# endif /* SHOPT_ESH */
# if SHOPT_VSH
# if SHOPT_RAWONLY
# else
if(sh_isoption(SH_VI))
# endif
else
# endif /* SHOPT_VSH */
{
return(-1);
}
while(1)
{
return(0);
timeout = (void*)sh_timeradd(sh_isstate(SH_GRACE)?1000L*TGRACE:1000L*shp->timeout,0,time_grace,shp);
if(timeout)
timeout=0;
#if SHOPT_HISTEXPAND
{
int r;
if(xp)
{
xp = 0;
}
{
{
break;
}
continue;
}
{
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
*/
{
register int flags, n;
return(n);
{
#ifdef F_GETFL
n |= IOREAD;
n |= IOWRITE;
#else
n &= ~IOREAD;
#endif /* F_GETFL */
}
{
{
}
n |= IOTTY;
{
n |= IONOSEEK;
#ifdef S_ISSOCK
{
n &= ~IOREAD;
n &= ~IOWRITE;
# endif
}
#endif /* S_ISSOCK */
}
#ifdef S_ISSOCK
#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) ||
))
n |= IONOSEEK;
else
n |= IOSEEK;
}
if(fd==0)
n &= ~IOWRITE;
else if(fd==1)
n &= ~IOREAD;
return(n);
}
/*
* Display prompt PS<flag> on standard error
*/
{
register char *cp;
char buff[1];
char *endprompt;
static short cmdno;
int sfflags;
flag = 0;
flag = 0;
if(flag==0)
switch(flag)
{
case 1:
{
register int c;
{
/*
* re-enable output in case the user has
* disabled it. Not needed with edit mode
*/
}
#endif /* TIOCLBIC */
{
if(c==HIST_CHAR)
{
/* look at next character */
c = *++cp;
/* print out line number if not !! */
if(c!= HIST_CHAR)
{
}
if(c==0)
goto done;
}
}
goto done;
}
case 2:
break;
case 3:
break;
default:
goto done;
}
if(cp)
done:
*endprompt = 0;
}
/*
* This discipline is inserted on write pipes to prevent SIGPIPE
* from causing an infinite loop
*/
{
{
return(-1);
}
return(0);
}
/*
* keep track of each stream that is opened and closed
*/
{
register int mode;
{
if(newfd<0)
flag = SF_CLOSING;
if(fdnotify)
}
#ifdef DEBUG
{
else
return;
}
#endif
return;
if(sh_isstate(SH_NOTRACK))
return;
{
}
if(fd < 3)
return;
{
{
}
{
/*
* record open file descriptors so they can
* be closed in case a longjmp prevents
* built-ins from cleanup
*/
}
if(fdnotify)
}
{
{
{
{
break;
}
}
}
}
}
struct eval
{
char **argv;
short slen;
char addspace;
};
/*
* Create a stream consisting of a space separated argv[] list
*/
{
register char *cp;
if(argv[1])
cp = "";
else
if(argv[1])
{
}
return(iop);
}
/*
* This code gets called whenever an end of string is found with eval
*/
{
register char *cp;
register int len;
/* no more to do */
{
if(type==SF_CLOSING)
return(0);
}
{
/* get the length of this string */
/* move to next string */
}
else /* insert space between arguments */
{
len = 1;
cp = " ";
}
/* insert the new string */
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
*/
{
return(sp);
}
/*
* read function for subfile discipline
*/
{
ssize_t n;
return(0);
if(size>0)
return(n);
}
/*
* exception handler for subfile discipline
*/
{
if(mode==SF_CLOSING)
{
return(0);
}
{
return(0);
}
#ifdef SF_ATEXIT
{
return(0);
}
#endif
return(0);
return(-1);
}
/*
* print a list of arguments in columns
*/
{
register int i,j;
register char **arg;
ndigits++;
{
goto skip;
}
i = 0;
{
i = j;
}
if(i < wsize)
{
}
else
{
}
skip:
for(i=0;i<nrow;i++)
{
return;
j = i;
while(1)
{
j += nrow;
if(j >= argn)
break;
}
}
}
/*
* shell version of read() for user added builtins
*/
{
else
}
/*
* shell version of write() for user added builtins
*/
{
else
}
/*
* shell version of lseek() for user added builtins
*/
{
else
}
{
if(fd>=0)
{
if(fdnotify)
}
return(fd);
}
{
{
case F_DUPFD:
if(fdnotify)
break;
case F_SETFD:
if(arg&FD_CLOEXEC)
else
}
return(newfd);
}
{
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.
*/
{
int n;
{
return(iop);
}
switch(fd)
{
case SH_IOHISTFILE:
if(!sh_histinit((void*)shp))
return(iop);
break;
case SH_IOCOPROCESS:
else
break;
default:
fd = -1;
}
if(fd<0)
{
return(iop);
}
return(iop);
return(iop);
return(iop);
}
typedef int (*Notify_f)(int,int);
{
return(old);
}
{
register int status;
{
register int flags=0;
}
return(sp);
}
{
int n;
#ifdef PATH_BFPATH
#else
#endif
if(n < 0)
return(sh_iostream(shp,n));
}
int sh_isdevfd(register const char *fd)
{
return(0);
{
return(0);
}
return(1);
}
{
return(r);
}
{
return(r);
}