/*ident "@(#)Path:ksh/io.ksh 3.1" */ /* * UNIX shell * * S. R. Bourne * Rewritten by David Korn * AT&T Bell Laboratories * */ #include #include "defs.h" #include "sym.h" #include "history.h" #ifndef SIG_NORESTART # include static jmp_buf readerr; static VOID interrupt(); #endif /* SIG_NORESTART */ extern char *utos(); static struct filesave fdmap[MAXFILES]; static struct fileblk *iofree = &io_stdin; static int nosave; static int serial; static char *temp_suffix; /* ======== input output and file copying ======== */ /* * initialize temp file names */ void io_settemp(pid) pid_t pid; { register char *sp = sh_copy(utos((ulong)pid,10),io_tmpname+7); *sp++ = '.'; temp_suffix = sp; serial = 0; } /* * set up a fileblk associated with the file descriptor fd using buffer buf */ void io_init(fd,fp,buf) register int fd; register struct fileblk *fp; char *buf; { register unsigned size = IOBSIZE+1; if(!fp) { if(iofree) { fp = iofree; iofree = 0; fp->flag = 0; buf = fp->base; } else { /* buffer the stream if possible */ if(fd==0 && ispipe(0)) size = 2; fp = new_of(struct fileblk,size); buf = (char*)(fp+1); fp->fstak = 0; fp->flag = IOFREE; } } else fp->flag = 0; fp->fdes = fd; fp->base = fp->ptr = fp->last = buf; *fp->ptr = 0; fp->flag |= IOREAD; fp->ftype=F_ISFILE; fp->flin=1; fp->fseek = 0; io_ftable[fd] = 0; if(io_access(fd,W_OK)==0) fp->flag |= IORW; io_ftable[fd] = fp; if(size==2 || ispipe(fd)) fp->flag |= (IOSLOW|IONBF); else if( tty_check(fd)) fp->flag |= IOSLOW; } /* * push an iostream onto the input queue */ void io_push(fp) struct fileblk *fp; { fp->fstak=st.standin; st.standin=fp; } /* * pop an iostream from the input queue * close stream if flag==0, filenum>0 && filenum!=INIO and flag is a file. */ int io_pop(flag) int flag; { register struct fileblk *fp; register int fno; if((fp=st.standin) && fp->fstak) { fno = filenum(fp); if(flag==0 && fno>0 && fno!=F_STRING && fno!=INIO && fno!=sh.cpipe[INPIPE]) io_fclose(fno); st.standin=fp->fstak; input = filenum(st.standin); if(fp->ftype == F_ISALIAS) { if(fp->feval) nam_offtype((struct namnod*)fp->feval,~M_FLAG); free((char*)fp); } return(1); } return(0); } /* * clear iostack up to */ void io_clear(iop) register struct fileblk *iop; { while((iop != st.standin) && io_pop(0)); } /* * given a file descriptor f1, move it to a new file descriptor number * f2. If f2 is open then it is closed first. * If the MARK bit not set on f2, then close on exec will be set for f2>2 * The original stream is closed. * File numbers greater than 2 are marked close on exec if io_renumber is * invoked by a parent shell. * The new file descriptor is returned; */ int io_renumber(f1,f2) register int f1; register int f2; { register int flag = (f2&MARK); register int fs=0; f2 &= ~MARK; if(f2>2 && flag==0) fs = 1; if(f1!=f2) { if(fs==0) fs = fcntl(f2,F_GETFD,0); io_fclose(f2); f2 = fcntl(f1,F_DUPFD, f2); if(f2 < 0) sh_fail(e_file,NIL); if(io_ftable[f2] = io_ftable[f1]) io_ftable[f2]->fdes = f2; io_ftable[f1] = 0; close(f1); if(f2==0) st.ioset = 1; } if(fs==1) /* set close on exec */ fcntl(f2,F_SETFD,1); return(f2); } int io_mktmp(fname) register char *fname; { register int maxtry = MAXTRY; register char *tmp_name = io_tmpname; register int fd; do { sh_copy(sh_itos(++serial),temp_suffix); } while((fd=open(tmp_name,O_CREAT|O_RDWR,RW_ALL))<0 && maxtry--); if(fname) { sh_copy(tmp_name,fname); if(fd<=0) sh_fail(tmp_name,e_create); } io_ftable[fd] = 0; return(fd); } /* * returns the next character from file */ io_getc(fd) int fd; { register struct fileblk *fp = io_ftable[fd]; register int c; if(!fp) return(EOF); if(c= *fp->ptr++) return(c&STRIP); if(fp->ptr <= fp->last) return(0); return(io_readbuff(fp)); } /* * This special version does not handle ptrname==1 * It also saves a lot of real seeks on history file */ off_t io_seek(fd, offset, ptrname) int fd; off_t offset; register int ptrname; { register struct fileblk *fp; register int c; off_t p; if(!(fp=io_ftable[fd])) return(lseek(fd,offset,ptrname)); fp->flag &= ~IOEOF; if(!(fp->flag&IOREAD) && fp->ptr!=fp->base) { c = output; output = fd; p_flush(); output = c; } c = 0; /* check history file to see if already in the buffer */ if(ptrname==0 && (fp->flag&IOREAD) && offsetfseek) { p = fp->fseek - (fp->last - fp->base); if(offset >= p) { fp->ptr = fp->base + (int)(offset-p); return(offset); } else { c = offset&(IOBSIZE-1); offset -= c; } } if(fp->flag&IORW) { fp->flag &= ~(IOWRT|IOREAD); fp->last = fp->ptr = fp->base; *fp->last = 0; } p = lseek(fd, offset, ptrname); if(!(fp->flag&IOSLOW)) { if(ptrname==0) fp->fseek = p; else fp->fseek = -1; if(c) { io_readbuff(fp); fp->ptr += (c-1); } } return(p); } /* * Read from file into fp * Call the edit routines if necessary */ int io_readbuff(fp) register struct fileblk *fp; { register int n; register int fno; if (fp->flag & IORW) fp->flag |= IOREAD; if (!(fp->flag&IOREAD)) return(EOF); /* The following line is needed in case an interrupt occurs */ fp->ptr = fp->last; #ifndef SIG_NORESTART if(fp->flag&IOSLOW) { st.intfn = interrupt; /* automatically restart with some systems */ if(setjmp(readerr)) { n = -1; errno = EINTR; goto slowsig; } } #endif /* SIG_NORESTART */ fno = filenum(fp); retry: /* don't attemp editing mode for pipes with allocated buffers */ if(!(fp->flag&IOEDIT) || (fp->flag&IONBF)) goto skip; # ifdef ESH if(is_option(EMACS|GMACS)) n = emacs_read(fno,fp->base, (unsigned)(MAXLINE-2)); else # endif /* ESH */ # ifdef VSH if(is_option(EDITVI)) n = vi_read(fno,fp->base, (unsigned)(MAXLINE-2)); else # endif /* VSH */ { skip: /* unbuffered reads needed for pipe on standard input */ if(fnobuff(fp) && fno==0) n = 1; else n = IOBSIZE; if(fp->flag&(IOSLOW|IOEDIT)) { job_wait((pid_t)0); p_flush(); } n = read(fno,fp->base, (unsigned)n); } #ifndef SIG_NORESTART slowsig: if(fp->flag&IOSLOW) st.intfn = 0; #endif /* SIG_NORESTART */ fp->ptr = fp->base; if(n > 0) fp->last = fp->base + n; else fp->last = fp->ptr; *fp->last = 0; if (n <= 0) { if (n == 0) { # ifdef O_NDELAY if((fp->flag&IOEDIT)&&(n=fcntl(fno,F_GETFL,0))&O_NDELAY) { n &= ~O_NDELAY; fcntl(fno, F_SETFL, n); goto retry; } # endif /* O_NDELAY */ fp->flag |= IOEOF; if (fp->flag & IORW) fp->flag &= ~IOREAD; } else { # ifdef O_NONBLOCK if(errno == EAGAIN) { n = fcntl(fno,F_GETFL,0); n &= ~O_NONBLOCK; fcntl(fno, F_SETFL, n); goto retry; } # endif /* O_NONBLOCK */ fp->flag |= IOERR; } return(EOF); } if(!(fp->flag&IOSLOW)) fp->fseek += n; /* this is needed to handle read -s */ if((st.states&RWAIT) && fno!=hist_ptr->fixfd) { n = output; p_setout(hist_ptr->fixfd); p_str(fp->base,0); output = n; } return(*fp->ptr++&STRIP); } /* * close file stream and reopen for reading and writing */ void io_fclose(fd) register int fd; { register struct fileblk *fp = io_ftable[fd]; /* reposition seek pointer if necessary */ if(fp && !(fp->flag&IOREAD)) p_flush(); else if(fp) { register int count; if(!(st.states&FORKED) && (count=fp->last-fp->ptr)) lseek(filenum(fp),-((off_t)count),SEEK_CUR); *(fp->last = fp->ptr = fp->base) = 0; } if(fd==COTPIPE) sh.cpid = 0; close(fd); if(fp == &io_stdin) iofree = fp; else if(fp && (fp->flag&IOFREE)) free((char*)fp); io_ftable[fd] = 0; } /* * Open a file for reading * On failure, print message. */ int io_fopen(name) register const char *name; { register int fd; if((fd=open(name,O_RDONLY))<0) sh_fail(name,e_open); return(fd); } /* * set up an I/O stream that will cause reading from a string */ void io_sopen(s) register char *s; { register struct fileblk *fp; (fp=st.standin)->fdes = input = F_STRING; fp->flag = IOREAD; fp->base = fp->ptr = s; fp->flin = 1; fp->ftype=F_ISSTRING; fp->flag|=(s==0?IOEOF:0); } /* * create a pipe and print message on failure */ void io_popen(pv) register int pv[]; { if(pipe(pv)<0 || pv[INPIPE]<0 || pv[OTPIPE]<0) sh_fail(e_pipe,NIL); } /* * close a pipe */ void io_pclose(pv) register int pv[]; { if(pv[INPIPE]>=2) io_fclose(pv[INPIPE]); if(pv[OTPIPE]>=2) io_fclose(pv[OTPIPE]); pv[INPIPE] = pv[OTPIPE] = -1; } /* * io_sync - flushes output buffer and positions stdin if necessary */ void io_sync() { register struct fileblk *fp = io_ftable[0]; register int count; p_setout(ERRIO); p_flush(); /* position back the read-ahead characters */ if(fp) { /*@ assert fp->base!=0; @*/ if(count=fp->last-fp->ptr) lseek(filenum(fp),-((off_t)count),SEEK_CUR); fp->ptr = fp->last = fp->base; *fp->ptr = 0; } } /* * create a link to iodoc for child process to use */ void io_linkdoc(i) register struct ionod *i; { register int maxtry; while(i) { /* link to a tempory file name */ maxtry = MAXTRY; do { sh_copy(sh_itos(++serial),temp_suffix); } while((link(i->ioname,io_tmpname))< 0 && maxtry--); if(maxtry<=0) sh_fail(io_tmpname,e_create); free(i->iolink); i->iolink = sh_heap(io_tmpname); i = i->iolst; } } /* * rename the file with the link name of the parent */ void io_swapdoc(i) register struct ionod *i; { while(i) { free(i->ioname); i->ioname = i->iolink; i->iolink = 0; i = i->iolst; } } /* * I/O redirection * flag = 0 if files are to be restored * flag = 2 if files are to be closed on exec */ int io_redirect(iop,flag) struct ionod *iop; { register char *ion; register int iof; register int fd; int o_mode; static char io_op[5]; char fname[TMPSIZ]; int fn; int mark = MARK; int indx = sh.topfd; int traceon; if(flag==2) mark = 0; if(iop) traceon = sh_trace((char**)0,0); for(;iop;iop=iop->ionxt) { iof=iop->iofile; if(flag==0) { /* save current file descriptor */ io_save(iof&IOUFD,indx); } ion=iop->ioname; 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; } io_op[2] = 0; io_op[3] = 0; if(!(iof&IORAW)) ion=mac_trim(ion,1); if(*ion) { if(iof&IODOC) { if(traceon) { p_setout(ERRIO); io_op[2] = '<'; p_str(io_op,NL); } fd = mac_here(iop); ion = 0; } else if(iof&IOMOV) { io_op[2] = '&'; if(ion[1]!=0) goto fail; fd = (iof&IOUFD); fn = *ion; if(fn=='-') { io_fclose(fd); continue; } if(fn == 'p') { if(iof&IOPUT) fn = COTPIPE; else fn = CINPIPE; } else if(isdigit(fn)) fn -= '0'; else goto fail; if((fd=dup(fn))<0) goto fail; if(fn==COTPIPE) io_fclose(COTPIPE); else if(fn==CINPIPE) io_pclose(sh.cpipe); } else if(iof&IORDW) { io_op[2] = '>'; o_mode = O_RDWR|O_CREAT; goto openit; } else if((iof&IOPUT)==0) fd=io_fopen(ion); else if(is_option(RSHFLG)) sh_fail(ion,e_restricted); else { if(iof&IOAPP) { io_op[2] = '>'; o_mode |= O_APPEND; } else { o_mode |= O_TRUNC; if(iof&IOCLOB) io_op[2] = '|'; else if(is_option(NOCLOB) && test_type(ion,S_IFMT,S_IFREG)) sh_fail(ion,e_fexists); } openit: if((fd=open(ion,o_mode,RW_ALL))<0) sh_fail(ion,e_create); } if(traceon && ion) { p_setout(ERRIO); p_str(io_op,' '); p_str(ion,iop->ionxt?SP:NL); } io_renumber(fd,(iof&IOUFD)|mark); } } return(indx); fail: sh_fail(ion,e_file); /* NOTREACHED */ } /* * copy file fd into a save place */ void io_save(fd,oldtop) register int fd; { register int f = sh.topfd; /* see if already saved, only save once */ while(f > oldtop) { if((fdmap[--f].org_fd)>>1 == fd) return; } if(fd==output) p_flush(); else if(fd==0) io_sync(); f = fcntl(fd, F_DUPFD, USERIO); if(sh.topfd >= MAXFILES) sh_fail(e_flimit,NIL); if(f >= 0) { io_ftable[f] = io_ftable[fd]; io_ftable[fd] = 0; fd <<= 1; /* make sure saved file close-on-exec */ if(fcntl(f,F_GETFD,0)==0) { /* the low bit on fd set to restore close-on-exec */ fd |= 1; fcntl(f,F_SETFD,1); } } fdmap[sh.topfd].org_fd = fd; fdmap[sh.topfd++].dup_fd = f; return; } /* * restore saved file descriptors from on */ void io_restore(last) int last; { register int i; register int dupfd; register int fd; int flag; for (i = sh.topfd - 1; i >= last; i--) { fd = fdmap[i].org_fd; if ((dupfd = fdmap[i].dup_fd) > 0) { flag = fd&1; fd >>= 1; io_renumber(dupfd, fd); /* turn off close-on-exec if flag is set */ if(flag) fcntl(fd,F_SETFD,0); } else io_fclose(fd); } sh.topfd = last; } /* * This routine returns 1 if fd corresponds to a pipe, 0 otherwise. */ int ispipe(fno) register int fno; { #ifdef PIPE_ERR if(lseek(fno,(off_t)0,SEEK_CUR)>=0) return(0); if(errno==PIPE_ERR) return(!tty_check(fno)); else return(0); #else return(0); #endif /* PIPE_ERR */ } /* * This routine returns the next input character but strips shell * line continuations and issues prompts at end of line * Otherwise this routine is the same as io_readc() */ io_nextc() { register int c; retry: nosave = 0; if((c=io_readc())==ESCAPE) { nosave = 1; if(io_readc()==NL) { sh_prompt(0); goto retry; } st.standin->ptr--; } nosave = 0; return(c); } int io_intr(fp) register struct fileblk *fp; { clearerr(fp); if(sh.trapnote&SIGSET) { newline(); sh_exit(SIGFAIL); } if(fp->flag&IOSLOW) { #ifdef JOBS job_wait((pid_t)0); #endif /* JOBS */ if(sh.trapnote&TRAPSET) { newline(); fp = st.standin; sh_chktrap(); io_clear(fp); return(0); } } return(-1); } #ifndef SIG_NORESTART /* * This routine is for systems that automatically restarts read */ static VOID interrupt() { st.intfn = 0; longjmp(readerr,1); } #endif /* SIG_NORESTART */ /* * read a character */ io_readc() { register int c; register struct fileblk *fp; int maxtry; if(st.peekn) { c = st.peekn&~MARK; st.peekn = 0; return(c); } maxtry = MAXTRY; fp = st.standin; retry: c = (*fp->ptr++)&STRIP; retry2: if(c==0) { switch(fp->ftype) { case F_ISALIAS: if(fp->flast == 0) { popit: io_pop(1); return(io_readc()); } /* does it end in whitespace? */ c = *(--fp->ptr-1); if(isblank(c)) sh.wdset |= CAN_ALIAS; else if(c==ESCAPE && fp->flast==NL) goto popit; c = (fp->flast&~MARK); fp->flast = 0; if(c) goto retry2; return(ENDOF); case F_ISEVAL: io_sopen(*fp->feval++); if(*fp->feval) fp->ftype = F_ISEVAL; c = SP; break; case F_ISSTRING: io_sopen(NULLSTR); fp->flag |= IOEOF; break; case F_ISFILE: /* check for end-of-buffer */ if(fp->ptr>fp->last) { if((c = io_readbuff(fp)) != EOF) goto retry2; c = ENDOF; if(!fiseof(fp)) { if(io_intr(fp)<0) { if(--maxtry > 0) goto retry; else fp->flag |= IOERR; } } break; } /* treat a zero byte as eof for TMPIO */ if(filenum(fp) == TMPIO) { lseek(TMPIO,(off_t)0,SEEK_SET); io_ftable[TMPIO] = 0; break; } /* assume foreign binary and don't execute */ if(sh.readscript) { register char *cp = st.cmdadr; st.cmdadr = sh.readscript; sh_fail(cp,e_exec); } default: /* skip over null bytes in files */ goto retry; } } else { if(c==NL) { fp->flin++; nosave = 0; } if(!nosave) { if((st.states&READPR) && (fp->ftype!=F_ISALIAS) && (fp->fstak==0||(st.states&FIXFLG))) { p_setout(ERRIO); p_char(c); } if((st.states&FIXFLG)&&fp->ftype==F_ISFILE && hist_ptr) { fp = hist_ptr->fixfp; if(fp->ptr >= fp->last) { int savout = output; output = filenum(fp); p_flush(); output = savout; hist_ptr->fixflush++; } *fp->ptr++ = c; } } } return(c); } /* * remove temporary files */ void io_rmtemp(base) struct ionod *base; { register struct ionod *iop = st.iotemp; while(iop>base) { unlink(iop->ioname); free(iop->iolink); iop=iop->iolst; } st.iotemp = iop; } /* * returns access information on open file * returns -1 for failure, 0 for success * is the same as for access() */ io_access(fd,mode) register int mode; { register int flags; register struct fileblk *fp; #ifndef F_GETFL struct stat statb; #endif /* F_GETFL */ if(mode==X_OK) return(-1); if(fp=io_ftable[fd]) { if(mode==F_OK) return(0); if(mode==R_OK && fp->flag&(IORW|IOREAD)) return(0); if(mode==W_OK && fp->flag&(IORW|IOWRT)) return(0); return(-1); } #ifdef F_GETFL flags = fcntl(fd,F_GETFL,0); #else flags = fstat(fd,&statb); #endif /* F_GETFL */ if(flags < 0) return(-1); #ifdef F_GETFL if(mode==R_OK && (flags&1)) return(-1); if(mode==W_OK && !(flags&3)) return(-1); #endif /* F_GETFL */ return(0); } #ifdef NOFCNTL # ifdef _sys_ioctl_ # include # endif /* _sys_ioctl_ */ int fcntl(f1,type,arg) register int arg; { struct stat statbuf; if(type==F_DUPFD) { register int fd; /* find first non-open file */ while(arg < NFILE && (fstat(arg,&statbuf)>=0)) arg++; if(arg >= NFILE) return(-1); fd = dup2(f1, arg); return(fd); } # ifdef FIOCLEX else if(type==F_SETFD) ioctl(f1, arg?FIOCLEX:FIONCLEX, 0); # endif /* FIOCLEX */ else return(0); } #undef open /* * Some braindamaged systems don't have O_CREAT */ int myopen(name,flag,mode) register char *name; register int flag; { register int fd; int created = 0; if(flag&O_TRUNC) { filecreate: created++; fd = creat(name,O_CREAT?mode:0644); } else { fd = open(name,flag&(O_RDWR|O_RDONLY|O_WRONLY)); if(fd<0 && O_CREAT) goto filecreate; } if(fd<0) return(fd); if(created&&O_RDWR) { close(fd); return(myopen(name,flag&~(O_CREAT|O_TRUNC))); } else if(flag&O_APPEND) lseek(fd,(off_t)0,SEEK_END); return(fd); } #endif /* _fcntl_ */ #ifdef DBUG /* * dump an io structure using only writes */ int_write(n,base) { register char *cp; extern char *utos(); cp = utos(n,base); write(2,cp,strlen(cp)); } io_dump(n) int n; { struct fileblk *fp = io_ftable[n];; write(2,"dump ",5); int_write(n,10); if(fp) { write(2,":fp=",4); int_write(fp,16); write(2," base=",6); int_write(fp->base,16); write(2," ptr=",5); int_write(fp->ptr,16); write(2," last=",6); int_write(fp->last,16); write(2," flag=",6); int_write(fp->flag,8); write(2," *last=",7); int_write(*fp->last&0xff,10); write(2,"\n",1); } else write(2,":no file structure\n",20); } #endif /* DBUG */