/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1982-2007 AT&T Knowledge Ventures * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Knowledge Ventures * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * David Korn * * * ***********************************************************************/ #pragma prototyped /* * Input/output file processing * * David Korn * AT&T Labs * */ #include "defs.h" #include #include #include #include #include #include "variables.h" #include "path.h" #include "io.h" #include "jobs.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 #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 # include # include # if !defined(htons) && !_lib_htons # define htons(x) (x) # endif # if !defined(htonl) && !_lib_htonl # define htonl(x) (x) # endif # if _pipe_socketpair # if _socketpair_shutdown_mode # define pipe(v) ((socketpair(AF_UNIX,SOCK_STREAM,0,v)<0||shutdown((v)[0],1)<0||fchmod((v)[0],S_IRUSR)<0||shutdown((v)[1],0)<0||fchmod((v)[1],S_IWUSR)<0)?(-1):0) # else # define pipe(v) ((socketpair(AF_UNIX,SOCK_STREAM,0,v)<0||shutdown((v)[0],1)<0||shutdown((v)[1],0)<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; } if (!(ap = newof(0, struct addrinfo, 1, sizeof(struct sockaddr_in)))) return EAI_SYSTEM; 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 /* * return // fd */ typedef int (*Inetintr_f)(struct addrinfo*, void*); static int inetopen(const char* path, int server, Inetintr_f onintr, void* handle) { register char* s; register char* t; int fd; int oerrno; struct addrinfo hint; struct addrinfo* addr; struct addrinfo* p; 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 (!(s = strdup(path))) return -1; if (t = strchr(s, '/')) { *t++ = 0; if (streq(s, "local")) s = "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 api's 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 || !onintr) break; if ((*onintr)(addr, handle)) return -1; } } 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 */ }; 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,int); 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 int matchf(void *handle, char *ptr, size_t size) { struct Match *mp = (struct Match*)handle; mp->offset += (ptr-mp->base); return(1); } static struct fdsave *filemap; static short filemapsize; /* ======== input output and file copying ======== */ void sh_ioinit(void) { register int n; filemapsize = 8; filemap = (struct fdsave*)malloc(8*sizeof(struct fdsave)); #if SHOPT_FASTPIPE n = sh.lim.open_max+2; #else n = sh.lim.open_max; #endif /* SHOPT_FASTPIPE */ sh.fdstatus = (unsigned char*)malloc((unsigned)n); memset((char*)sh.fdstatus,0,n); sh.fdptrs = (int**)malloc(n*sizeof(int*)); memset((char*)sh.fdptrs,0,n*sizeof(int*)); sh.sftable = (Sfio_t**)malloc(n*sizeof(Sfio_t*)); memset((char*)sh.sftable,0,n*sizeof(Sfio_t*)); sh.sftable[0] = sfstdin; sh.sftable[1] = sfstdout; sh.sftable[2] = sfstderr; sfnotify(sftrack); sh_iostream(0); /* all write steams are in the same pool and share outbuff */ sh.outpool = sfopen(NIL(Sfio_t*),NIL(char*),"sw"); /* pool identifier */ sh.outbuff = (char*)malloc(IOBSIZE); sh.errbuff = (char*)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); } /* * create or initialize a stream corresponding to descriptor * 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 */ Sfio_t *sh_iostream(register int fd) { register Sfio_t *iop; register int status = sh_iocheckfd(fd); register int flags = SF_WRITE; char *bp; #if SHOPT_FASTPIPE if(fd>=sh.lim.open_max) return(sh.sftable[fd]); #endif /* SHOPT_FASTPIPE */ if(status==IOCLOSE) { switch(fd) { case 0: return(sfstdin); case 1: return(sfstdout); case 2: return(sfstderr); } return(NIL(Sfio_t*)); } if(status&IOREAD) { if(!(bp = (char *)malloc(IOBSIZE+1))) return(NIL(Sfio_t*)); 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) sfsetbuf(iop, bp, IOBSIZE); else if(!(iop=sfnew((fd<=2?iop:0),bp,IOBSIZE,fd,flags))) return(NIL(Sfio_t*)); if(status&IOREAD) { Sfdisc_t *dp; sfset(iop,SF_MALLOC,1); { dp = newof(0,Sfdisc_t,1,0); 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; sfdisc(iop,dp); } } else sfpool(iop,sh.outpool,SF_WRITE); 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) errormsg(SH_DICT,ERROR_system(1),e_toomany); 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 , move it to a file descriptor number * If is needed move it, otherwise it is closed first. * The original stream is closed. * The new file descriptor 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<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<trapnote&SH_SIGSET) { freeaddrinfo(addr); sh_exit(SH_EXITSIG); return -1; } if (sh->trapnote) sh_chktrap(); return 0; } /* * Mimic open(2) with checks for pseudo /dev/ files. */ int sh_open(register const char *path, int flags, ...) { 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 = 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]=='/') { 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 & O_SERVICE), onintr, &sh)) < 0 && errno != ENOTDIR) return -1; if (fd >= 0) goto ok; } #endif } if (fd >= 0) { 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 while((fd = open(path, flags, mode)) < 0) if(errno!=EINTR || sh.trapnote) return(-1); #ifdef O_SERVICE ok: #endif flags &= O_ACCMODE; if(flags==O_WRONLY) mode = IOWRITE; else if(flags==O_RDWR) mode = (IOREAD|IOWRITE); else mode = IOREAD; 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); return(fd); } /* * move open file descriptor to a number > 2 */ int sh_iomovefd(register int fdold) { register int fdnew; 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); 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); } 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; } /* * 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 */ 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 clexec=0, fn, traceon; int r, indx = sh.topfd; char *after="", *trace = sh.st.trap[SH_DEBUGTRAP]; Namval_t *np=0; if(flag==2) clexec = 1; if(iop) traceon = sh_trace(NIL(char**),0); for(;iop;iop=iop->ionxt) { iof=iop->iofile; fn = (iof&IOUFD); 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 fname=sh_mactrim(fname,(!sh_isoption(SH_NOGLOB)&&sh_isoption(SH_INTERACTIVE))?2:0); } errno=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)); io_op[0] = '}'; if((iof&IOMOV) && *fname=='-') fn = nv_getnum(np); } 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) { 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 || dupfd > IOUFD) { message = e_file; goto fail; } if(sh.subshell && dupfd==1) { sh_subtmpfile(); dupfd = sffileno(sfstdout); } else if(sh.sftable[dupfd]) sfsync(sh.sftable[dupfd]); } 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; 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); /* save file descriptor */ sh_close(toclose); } } else if(iof&IORDW) { if(sh_isoption(SH_RESTRICTED)) errormsg(SH_DICT,ERROR_exit(1),e_restricted,fname); io_op[2] = '>'; o_mode = O_RDWR|O_CREAT; 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); else { if(iof&IOAPP) { io_op[2] = '>'; o_mode |= O_APPEND; } 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 SHOPT_FS_3D if(S_ISREG(sb.st_mode)&& (!sh.lim.fs3d || iview(&sb)==0)) #else if(S_ISREG(sb.st_mode)) #endif /* SHOPT_FS_3D */ { errno = EEXIST; errormsg(SH_DICT,ERROR_system(1),e_exists,fname); } } else o_mode |= O_EXCL; } } openit: if(flag!=SH_SHOWME) { if((fd=sh_open(fname,o_mode,RW_ALL)) <0) errormsg(SH_DICT,ERROR_system(1),((o_mode&O_CREAT)?e_create:e_open),fname); } } 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) r=sfseek(sp, off, SEEK_SET); else r=lseek(fn, off, SEEK_SET); } else { regex_t *rp; extern const char e_notimp[]; 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) { if(fd==fn) { if((r=sh_fcntl(fd,F_DUPFD,10)) > 0) { fd = r; sh_close(fn); } } sh_iosave(fn,indx); } else if(sh_subsavefd(fn)) sh_iosave(fn,indx|IOSUBSHELL); } if(fd<0) { if(sh_inuse(fn) || fn==sh.infd) { if(fn>9 || !(sh.inuse_bits&(1<=0) { if(np) { int32_t v; fn = fd; if(fd<10) { if((fn=fcntl(fd,F_DUPFD,10)) < 0) goto fail; sh.fdstatus[fn] = sh.fdstatus[fd]; sh_close(fd); fd = fn; } nv_unset(np); 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<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); /* NOTREACHED */ return(0); } /* * 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; register int fd; 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); if(iop->iofile&IOSTRG) { if(traceon) sfprintf(sfstderr,"< %s\n",name); sfputr(outfile,name,'\n'); } else { 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); } if(iop->iofile&IOQUOTE) { /* This is a quoted here-document, not expansion */ sfmove(infile,outfile,SF_UNBOUND,-1); sfclose(infile); } else { 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 into a save place * The saved file is set close-on-exec * if < 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) { /*@ assume oldtop>=0 && oldtop=oldtop; ) { if(filemap[savefd].orig_fd == origfd) return; } /* make sure table is large enough */ if(sh.topfd >= filemapsize) { filemapsize += 8; if(!(filemap = (struct fdsave*)realloc(filemap,filemapsize*sizeof(struct fdsave)))) errormsg(SH_DICT,ERROR_exit(4),e_nospace); } #if SHOPT_DEVFD if(origfd <0) { savefd = origfd; origfd = -origfd; } else #endif /* SHOPT_DEVFD */ { if((savefd = sh_fcntl(origfd, F_DUPFD, 10)) < 0 && errno!=EBADF) errormsg(SH_DICT,ERROR_system(1),e_toomany); } filemap[sh.topfd].subshell = flag; 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 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; 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 * returns -1 for failure, 0 for success * 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; NOT_USED(handle); if(type==SF_DPOP || type==SF_FINAL) free((void*)handle); 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); n=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); NOT_USED(handle); if(sh.trapnote) { errno = EINTR; return(-1); } if(sh_isstate(SH_INTERACTIVE) && io_prompt(iop,sh.nextprompt)<0 && errno==EIO) return(0); if(!(sh.fdstatus[sffileno(iop)]&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); 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 NOT_USED(handle); # 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,NIL(void*)); 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; #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; } sh.fdstatus[fd] = n; return(n); } /* * Display prompt PS 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; 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)); 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; #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(nv_scoped(PS1NOD))); 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: cp = nv_getval(nv_scoped(PS2NOD)); break; case 3: cp = nv_getval(nv_scoped(PS3NOD)); break; default: goto done; } if(cp) sfputr(sfstderr,cp,-1); done: if(*sh.prompt && (endprompt=(char*)sfreserve(sfstderr,0,0))) *endprompt = 0; 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) { NOT_USED(iop); if(mode==SF_DPOP || mode==SF_FINAL) free((void*)handle); else if(mode==SF_WRITE && errno==EINTR && sh.lastsig==SIGPIPE) return(-1); return(0); } /* * keep track of each stream that is opened and closed */ static void sftrack(Sfio_t* sp,int flag, int newfd) { register int fd = sffileno(sp); register struct checkpt *pp; register int mode; 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)getpid(),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((unsigned)fd >= sh.lim.open_max) return; if(sh_isstate(SH_NOTRACK)) return; mode = sfset(sp,0,0); if(sp==sh.heredocs && fd < 10 && flag==SF_NEW) { 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; #if 0 if(flag==IOWRITE) sfpool(sp,sh.outpool,SF_WRITE); else #else if(flag!=IOWRITE) #endif 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; if(!(ep = new_of(struct eval,0))) return(NIL(Sfio_t*)); 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 from * the stream starting at 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*)); if(!(disp = (struct subfile*)malloc(sizeof(struct subfile)+IOBSIZE+1))) return(NIL(Sfio_t*)); disp->disc = sub_disc; disp->oldsp = sp; disp->offset = offset; disp->size = disp->left = size; sp = sfnew(NIL(Sfio_t*),(char*)(disp+1),IOBSIZE,sh.lim.open_max,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; NOT_USED(sp); if(disp->left == 0) return(0); if(size > disp->left) size = disp->left; disp->left -= size; return(sfread(disp->oldsp,buff,size)); } /* * 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); return(0); } else if(disp && (mode==SF_DPOP || mode==SF_FINAL)) { free((void*)disp); return(0); } #ifdef SF_ATEXIT else if (mode==SF_ATEXIT) { sfdisc(sp, SF_POPDISC); return(0); } #endif 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(nv_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= 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; 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 and , return an iostream pointer * must be SF_READ or SF_WRITE * 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 || fd >= sh.lim.open_max) 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; #ifdef PATH_BFPATH if((n=path_open(cp,path_get(cp))) < 0) n = path_open(cp,(Pathcomp_t*)0); #else if((n=path_open(cp,path_get(cp))) < 0) n = path_open(cp,""); #endif if(n < 0) errormsg(SH_DICT,ERROR_system(1),e_open,cp); return(sh_iostream(n)); }