diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
commit | 3950ffe2a485479f6561c27364d3d7df5a21d124 (patch) | |
tree | 468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/bltins/read.c | |
download | ksh-upstream.tar.gz |
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/ksh93/bltins/read.c')
-rw-r--r-- | src/cmd/ksh93/bltins/read.c | 802 |
1 files changed, 802 insertions, 0 deletions
diff --git a/src/cmd/ksh93/bltins/read.c b/src/cmd/ksh93/bltins/read.c new file mode 100644 index 0000000..75fc478 --- /dev/null +++ b/src/cmd/ksh93/bltins/read.c @@ -0,0 +1,802 @@ +/*********************************************************************** +* * +* 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 * +* 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> * +* * +***********************************************************************/ +#pragma prototyped +/* + * read [-ACprs] [-d delim] [-u filenum] [-t timeout] [-n n] [-N n] [name...] + * + * David Korn + * AT&T Labs + * + */ + +#include <ast.h> +#include <error.h> +#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 */ +#define SS_FLAG 0x80 /* read .csv format file */ + +struct read_save +{ + char **argv; + char *prompt; + short fd; + short plen; + int flags; + long timeout; +}; + +int b_read(int argc,char *argv[], Shbltin_t *context) +{ + Sfdouble_t sec; + register char *name; + register int r, flags=0, fd=0; + register Shell_t *shp = context->shp; + long timeout = 1000*shp->st.tmout; + int save_prompt, fixargs=context->invariant; + struct read_save *rp; + static char default_prompt[3] = {ESC,ESC}; + rp = (struct read_save*)(context->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<<D_FLAG)-1); + flags |= (mbchar(cp)<< D_FLAG); + } + break; + case 'p': + if((fd = shp->cpipe[0])<=0) + errormsg(SH_DICT,ERROR_exit(1),e_query); + break; + case 'n': case 'N': + flags &= ((1<<D_FLAG)-1); + flags |= (r=='n'?N_FLAG:NN_FLAG); + r = (int)opt_info.num; + if((unsigned)r > (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 'S': + flags |= SS_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))) + { + context->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] && errno!=EINTR) + sh_pclose(shp->cpipe); + } + 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 + * <names> is an array of variable names + * <fd> is the file descriptor + * <flags> is union of -A, -r, -s, and contains delimiter if not '\n' + * <timeout> 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, keytrap; + long array_index = 0; + void *timeslot=0; + int delim = '\n'; + int jmpval=0; + ssize_t size = 0; + int binary; + int oflags=NV_ASSIGN|NV_VARNAME; + char inquote = 0; + struct checkpt buff; + Edit_t *ep = (struct edit*)shp->gd->ed_context; + 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; + } + keytrap = ep?ep->e_keytrap:0; + 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) && !keytrap) + 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; + if((flags&SS_FLAG)) + { + shp->ifstable['"'] = S_QUOTE; + shp->ifstable['\r'] = S_ERR; + } + } + sfclrerr(iop); + for(nfp=np->nvfun; nfp; nfp = nfp->next) + { + if(nfp->disc && nfp->disc->readf) + { + Namval_t *mp = nv_open(name,shp->var_tree,oflags|NV_NOREF); + if((c=(*nfp->disc->readf)(mp,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,shp->redir0!=2)&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; + if(keytrap) + { + cp = 0; + f = 0; + m = 0; + while(c-->0 && (buf[m]=ed_getchar(ep,0))) + m++; + if(m>0) + cp = (unsigned char*)buf; + } + else + { + f = 1; + if(cp = sfreserve(iop,c,SF_LOCKR)) + 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; + } + if(cur!=(char*)cp) + 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(!sferror(iop) && sfgetc(iop) >=0) + errormsg(SH_DICT,ERROR_exit(1),e_overlimit,"line length"); + } + 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_QUOTE: + c = shp->ifstable[*cp++]; + inquote = !inquote; + goto skip; + case S_ESC: + /* process escape character */ + if((c = shp->ifstable[*cp++]) == S_NL) + was_escape = 1; + else + c = 0; + skip: + if(val) + { + stakputs(val); + use_stak = 1; + *val = 0; + } + continue; + + case S_ERR: + cp++; + 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(inquote) + { + if(c==S_QUOTE) + { + if(shp->ifstable[*cp]==S_QUOTE) + { + if(val) + { + stakwrite(val,cp-(unsigned char*)val); + use_stak = 1; + } + val = (char*)++cp; + } + else + break; + } + if(c && c!=S_EOF) + { + if(c==S_NL) + { + if(val) + { + stakwrite(val,cp-(unsigned char*)val); + use_stak=1; + } + if(cp = (unsigned char*)sfgetr(iop,delim,0)) + c = sfvalue(iop); + else if(cp = (unsigned char*)sfgetr(iop,delim,-1)) + c = sfvalue(iop)+1; + val = (char*)cp; + } + continue; + } + } + 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((shp->fdstatus[fd]&IOTTY) && !keytrap) + tty_cooked(fd); + if(flags&S_FLAG) + hist_flush(shp->gd->hist_ptr); + if(jmpval > 1) + siglongjmp(*shp->jmplist,jmpval); + return(jmpval); +} + |