/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1982-2011 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Intellectual Property * * * * 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 /* * read [-ACprs] [-d delim] [-u filenum] [-t timeout] [-n n] [-N n] [name...] * * David Korn * AT&T Labs * */ #include #include #include "defs.h" #include "variables.h" #include "lexstates.h" #include "io.h" #include "name.h" #include "builtins.h" #include "history.h" #include "terminal.h" #include "edit.h" #define R_FLAG 1 /* raw mode */ #define S_FLAG 2 /* save in history file */ #define A_FLAG 4 /* read into array */ #define N_FLAG 8 /* fixed size read at most */ #define NN_FLAG 0x10 /* fixed size read exact */ #define V_FLAG 0x20 /* use default value */ #define C_FLAG 0x40 /* read into compound variable */ #define D_FLAG 8 /* must be number of bits for all flags */ struct read_save { char **argv; char *prompt; short fd; short plen; int flags; long timeout; }; int b_read(int argc,char *argv[], void *extra) { Sfdouble_t sec; register char *name; register int r, flags=0, fd=0; register Shell_t *shp = ((Shbltin_t*)extra)->shp; long timeout = 1000*shp->st.tmout; int save_prompt, fixargs=((Shbltin_t*)extra)->invariant; struct read_save *rp; static char default_prompt[3] = {ESC,ESC}; rp = (struct read_save*)(((Shbltin_t*)extra)->data); if(argc==0) { if(rp) free((void*)rp); return(0); } if(rp) { flags = rp->flags; timeout = rp->timeout; fd = rp->fd; argv = rp->argv; name = rp->prompt; r = rp->plen; goto bypass; } while((r = optget(argv,sh_optread))) switch(r) { case 'A': flags |= A_FLAG; break; case 'C': flags |= C_FLAG; break; case 't': sec = sh_strnum(opt_info.arg, (char**)0,1); timeout = sec ? 1000*sec : 1; break; case 'd': if(opt_info.arg && *opt_info.arg!='\n') { char *cp = opt_info.arg; flags &= ~((1<cpipe[0])<=0) errormsg(SH_DICT,ERROR_exit(1),e_query); break; case 'n': case 'N': flags &= ((1< (1<<((8*sizeof(int))-D_FLAG))-1) errormsg(SH_DICT,ERROR_exit(1),e_overlimit,opt_info.name); flags |= (r<< D_FLAG); break; case 'r': flags |= R_FLAG; break; case 's': /* save in history file */ flags |= S_FLAG; break; case 'u': fd = (int)opt_info.num; if(sh_inuse(shp,fd)) fd = -1; break; case 'v': flags |= V_FLAG; break; case ':': errormsg(SH_DICT,2, "%s", opt_info.arg); break; case '?': errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); break; } argv += opt_info.index; if(error_info.errors) errormsg(SH_DICT,ERROR_usage(2), "%s", optusage((char*)0)); if(!((r=shp->fdstatus[fd])&IOREAD) || !(r&(IOSEEK|IONOSEEK))) r = sh_iocheckfd(shp,fd); if(fd<0 || !(r&IOREAD)) errormsg(SH_DICT,ERROR_system(1),e_file+4); /* look for prompt */ if((name = *argv) && (name=strchr(name,'?')) && (r&IOTTY)) r = strlen(name++); else r = 0; if(argc==fixargs && (rp=newof(NIL(struct read_save*),struct read_save,1,0))) { ((Shbltin_t*)extra)->data = (void*)rp; rp->fd = fd; rp->flags = flags; rp->timeout = timeout; rp->argv = argv; rp->prompt = name; rp->plen = r; } bypass: shp->prompt = default_prompt; if(r && (shp->prompt=(char*)sfreserve(sfstderr,r,SF_LOCKR))) { memcpy(shp->prompt,name,r); sfwrite(sfstderr,shp->prompt,r-1); } shp->timeout = 0; save_prompt = shp->nextprompt; shp->nextprompt = 0; r=sh_readline(shp,argv,fd,flags,timeout); shp->nextprompt = save_prompt; if(r==0 && (r=(sfeof(shp->sftable[fd])||sferror(shp->sftable[fd])))) { if(fd == shp->cpipe[0]) { sh_pclose(shp->cpipe); return(1); } } sfclrerr(shp->sftable[fd]); return(r); } /* * here for read timeout */ static void timedout(void *handle) { sfclrlock((Sfio_t*)handle); sh_exit(1); } /* * This is the code to read a line and to split it into tokens * is an array of variable names * is the file descriptor * is union of -A, -r, -s, and contains delimiter if not '\n' * is number of milli-seconds until timeout */ int sh_readline(register Shell_t *shp,char **names, int fd, int flags,long timeout) { register ssize_t c; register unsigned char *cp; register Namval_t *np; register char *name, *val; register Sfio_t *iop; Namfun_t *nfp; char *ifs; unsigned char *cpmax; unsigned char *del; char was_escape = 0; char use_stak = 0; volatile char was_write = 0; volatile char was_share = 1; int rel, wrd; long array_index = 0; void *timeslot=0; int delim = '\n'; int jmpval=0; ssize_t size = 0; int binary; int oflags=NV_NOASSIGN|NV_VARNAME; struct checkpt buff; if(!(iop=shp->sftable[fd]) && !(iop=sh_iostream(shp,fd))) return(1); sh_stats(STAT_READS); if(names && (name = *names)) { Namval_t *mp; if(val= strchr(name,'?')) *val = 0; if(flags&C_FLAG) oflags |= NV_ARRAY; np = nv_open(name,shp->var_tree,oflags); if(np && nv_isarray(np) && (mp=nv_opensub(np))) np = mp; if((flags&V_FLAG) && shp->gd->ed_context) ((struct edit*)shp->gd->ed_context)->e_default = np; if(flags&A_FLAG) { flags &= ~A_FLAG; array_index = 1; nv_unset(np); nv_putsub(np,NIL(char*),0L); } else if(flags&C_FLAG) { char *sp = np->nvenv; delim = -1; nv_unset(np); if(!nv_isattr(np,NV_MINIMAL)) np->nvenv = sp; nv_setvtree(np); } else name = *++names; if(val) *val = '?'; } else { name = 0; if(dtvnext(shp->var_tree) || shp->namespace) np = nv_open(nv_name(REPLYNOD),shp->var_tree,0); else np = REPLYNOD; } if(flags>>D_FLAG) /* delimiter not new-line or fixed size read */ { if(flags&(N_FLAG|NN_FLAG)) size = ((unsigned)flags)>>D_FLAG; else delim = ((unsigned)flags)>>D_FLAG; if(shp->fdstatus[fd]&IOTTY) tty_raw(fd,1); } binary = nv_isattr(np,NV_BINARY); if(!binary && !(flags&(N_FLAG|NN_FLAG))) { Namval_t *mp; /* set up state table based on IFS */ ifs = nv_getval(mp=sh_scoped(shp,IFSNOD)); if((flags&R_FLAG) && shp->ifstable['\\']==S_ESC) shp->ifstable['\\'] = 0; else if(!(flags&R_FLAG) && shp->ifstable['\\']==0) shp->ifstable['\\'] = S_ESC; if(delim>0) shp->ifstable[delim] = S_NL; if(delim!='\n') { shp->ifstable['\n'] = 0; nv_putval(mp, ifs, NV_RDONLY); } shp->ifstable[0] = S_EOF; } sfclrerr(iop); for(nfp=np->nvfun; nfp; nfp = nfp->next) { if(nfp->disc && nfp->disc->readf) { if((c=(*nfp->disc->readf)(np,iop,delim,nfp))>=0) return(c); } } if(binary && !(flags&(N_FLAG|NN_FLAG))) { flags |= NN_FLAG; size = nv_size(np); } was_write = (sfset(iop,SF_WRITE,0)&SF_WRITE)!=0; if(fd==0) was_share = (sfset(iop,SF_SHARE,1)&SF_SHARE)!=0; if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK))) { sh_pushcontext(shp,&buff,1); jmpval = sigsetjmp(buff.buff,0); if(jmpval) goto done; if(timeout) timeslot = (void*)sh_timeradd(timeout,0,timedout,(void*)iop); } if(flags&(N_FLAG|NN_FLAG)) { char buf[256],*var=buf,*cur,*end,*up,*v; /* reserved buffer */ if((c=size)>=sizeof(buf)) { if(!(var = (char*)malloc(c+1))) sh_exit(1); end = var + c; } else end = var + sizeof(buf) - 1; up = cur = var; if((sfset(iop,SF_SHARE,1)&SF_SHARE) && fd!=0) was_share = 1; if(size==0) { cp = sfreserve(iop,0,0); c = 0; } else { ssize_t m; int f; for (;;) { c = size; cp = sfreserve(iop,c,SF_LOCKR); f = 1; if(cp) m = sfvalue(iop); else if(flags&NN_FLAG) { c = size; m = (cp = sfreserve(iop,c,0)) ? sfvalue(iop) : 0; f = 0; } else { c = sfvalue(iop); m = (cp = sfreserve(iop,c,SF_LOCKR)) ? sfvalue(iop) : 0; } if(m>0 && (flags&N_FLAG) && !binary && (v=memchr(cp,'\n',m))) { *v++ = 0; m = v-(char*)cp; } if((c=m)>size) c = size; if(c>0) { if(c > (end-cur)) { ssize_t cx = cur - var, ux = up - var; m = (end - var) + (c - (end - cur)); if (var == buf) { v = (char*)malloc(m+1); var = memcpy(v, var, cur - var); } else var = newof(var, char, m, 1); end = var + m; cur = var + cx; up = var + ux; } memcpy((void*)cur,cp,c); if(f) sfread(iop,cp,c); cur += c; #if SHOPT_MULTIBYTE if(!binary && mbwide()) { int x; int z; mbinit(); *cur = 0; x = z = 0; while (up < cur && (z = mbsize(up)) > 0) { up += z; x++; } if((size -= x) > 0 && (up >= cur || z < 0) && ((flags & NN_FLAG) || z < 0 || m > c)) continue; } #endif } #if SHOPT_MULTIBYTE if(!binary && mbwide() && (up == var || (flags & NN_FLAG) && size)) cur = var; #endif *cur = 0; if(c>=size || (flags&N_FLAG) || m==0) { if(m) sfclrerr(iop); break; } size -= c; } } if(timeslot) timerdel(timeslot); if(binary && !((size=nv_size(np)) && nv_isarray(np) && c!=size)) { if((c==size) && np->nvalue.cp && !nv_isarray(np)) memcpy((char*)np->nvalue.cp,var,c); else { Namval_t *mp; if(var==buf) var = memdup(var,c+1); nv_putval(np,var,NV_RAW); nv_setsize(np,c); if(!nv_isattr(np,NV_IMPORT|NV_EXPORT) && (mp=(Namval_t*)np->nvenv)) nv_setsize(mp,c); } } else { nv_putval(np,var,0); if(var!=buf) free((void*)var); } goto done; } else if(cp = (unsigned char*)sfgetr(iop,delim,0)) c = sfvalue(iop); else if(cp = (unsigned char*)sfgetr(iop,delim,-1)) c = sfvalue(iop)+1; if(timeslot) timerdel(timeslot); if((flags&S_FLAG) && !shp->gd->hist_ptr) { sh_histinit((void*)shp); if(!shp->gd->hist_ptr) flags &= ~S_FLAG; } if(cp) { cpmax = cp + c; #if SHOPT_CRNL if(delim=='\n' && c>=2 && cpmax[-2]=='\r') cpmax--; #endif /* SHOPT_CRNL */ if(*(cpmax-1) != delim) *(cpmax-1) = delim; if(flags&S_FLAG) sfwrite(shp->gd->hist_ptr->histfp,(char*)cp,c); c = shp->ifstable[*cp++]; #if !SHOPT_MULTIBYTE if(!name && (flags&R_FLAG)) /* special case single argument */ { /* skip over leading blanks */ while(c==S_SPACE) c = shp->ifstable[*cp++]; /* strip trailing delimiters */ if(cpmax[-1] == '\n') cpmax--; if(cpmax>cp) { while((c=shp->ifstable[*--cpmax])==S_DELIM || c==S_SPACE); cpmax[1] = 0; } else *cpmax =0; if(nv_isattr(np, NV_RDONLY)) { errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np)); jmpval = 1; } else nv_putval(np,(char*)cp-1,0); goto done; } #endif /* !SHOPT_MULTIBYTE */ } else c = S_NL; shp->nextprompt = 2; rel= staktell(); /* val==0 at the start of a field */ val = 0; del = 0; while(1) { switch(c) { #if SHOPT_MULTIBYTE case S_MBYTE: if(val==0) val = (char*)(cp-1); if(sh_strchr(ifs,(char*)cp-1)>=0) { c = mbsize((char*)cp-1); if(name) cp[-1] = 0; if(c>1) cp += (c-1); c = S_DELIM; } else c = 0; continue; #endif /*SHOPT_MULTIBYTE */ case S_ESC: /* process escape character */ if((c = shp->ifstable[*cp++]) == S_NL) was_escape = 1; else c = 0; if(val) { stakputs(val); use_stak = 1; was_escape = 1; *val = 0; } continue; case S_EOF: /* check for end of buffer */ if(val && *val) { stakputs(val); use_stak = 1; } val = 0; if(cp>=cpmax) { c = S_NL; break; } /* eliminate null bytes */ c = shp->ifstable[*cp++]; if(!name && val && (c==S_SPACE||c==S_DELIM||c==S_MBYTE)) c = 0; continue; case S_NL: if(was_escape) { was_escape = 0; if(cp = (unsigned char*)sfgetr(iop,delim,0)) c = sfvalue(iop); else if(cp=(unsigned char*)sfgetr(iop,delim,-1)) c = sfvalue(iop)+1; if(cp) { if(flags&S_FLAG) sfwrite(shp->gd->hist_ptr->histfp,(char*)cp,c); cpmax = cp + c; c = shp->ifstable[*cp++]; val=0; if(!name && (c==S_SPACE || c==S_DELIM || c==S_MBYTE)) c = 0; continue; } } c = S_NL; break; case S_SPACE: /* skip over blanks */ while((c=shp->ifstable[*cp++])==S_SPACE); if(!val) continue; #if SHOPT_MULTIBYTE if(c==S_MBYTE) { if(sh_strchr(ifs,(char*)cp-1)>=0) { if((c = mbsize((char*)cp-1))>1) cp += (c-1); c = S_DELIM; } else c = 0; } #endif /* SHOPT_MULTIBYTE */ if(c!=S_DELIM) break; /* FALL THRU */ case S_DELIM: if(!del) del = cp - 1; if(name) { /* skip over trailing blanks */ while((c=shp->ifstable[*cp++])==S_SPACE); break; } /* FALL THRU */ case 0: if(val==0 || was_escape) { val = (char*)(cp-1); was_escape = 0; } /* skip over word characters */ wrd = -1; while(1) { while((c=shp->ifstable[*cp++])==0) if(!wrd) wrd = 1; if(!del&&c==S_DELIM) del = cp - 1; if(name || c==S_NL || c==S_ESC || c==S_EOF || c==S_MBYTE) break; if(wrd<0) wrd = 0; } if(wrd>0) del = (unsigned char*)""; if(c!=S_MBYTE) cp[-1] = 0; continue; } /* assign value and advance to next variable */ if(!val) val = ""; if(use_stak) { stakputs(val); stakputc(0); val = stakptr(rel); } if(!name && *val) { /* strip off trailing space delimiters */ register unsigned char *vp = (unsigned char*)val + strlen(val); while(shp->ifstable[*--vp]==S_SPACE); if(vp==del) { if(vp==(unsigned char*)val) vp--; else while(shp->ifstable[*--vp]==S_SPACE); } vp[1] = 0; } if(nv_isattr(np, NV_RDONLY)) { errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np)); jmpval = 1; } else nv_putval(np,val,0); val = 0; del = 0; if(use_stak) { stakseek(rel); use_stak = 0; } if(array_index) { nv_putsub(np, NIL(char*), array_index++); if(c!=S_NL) continue; name = *++names; } while(1) { if(sh_isoption(SH_ALLEXPORT)&&!strchr(nv_name(np),'.') && !nv_isattr(np,NV_EXPORT)) { nv_onattr(np,NV_EXPORT); sh_envput(shp->env,np); } if(name) { nv_close(np); np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME); name = *++names; } else np = 0; if(c!=S_NL) break; if(!np) goto done; if(nv_isattr(np, NV_RDONLY)) { errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np)); jmpval = 1; } else nv_putval(np, "", 0); } } done: if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK))) sh_popcontext(shp,&buff); if(was_write) sfset(iop,SF_WRITE,1); if(!was_share) sfset(iop,SF_SHARE,0); nv_close(np); if((flags>>D_FLAG) && (shp->fdstatus[fd]&IOTTY)) tty_cooked(fd); if(flags&S_FLAG) hist_flush(shp->gd->hist_ptr); if(jmpval > 1) siglongjmp(*shp->jmplist,jmpval); return(jmpval); }