1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1982-2011 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                  David Korn <dgk@research.att.com>                   *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21/*
22 * read [-ACprs] [-d delim] [-u filenum] [-t timeout] [-n n] [-N n] [name...]
23 *
24 *   David Korn
25 *   AT&T Labs
26 *
27 */
28
29#include	<ast.h>
30#include	<error.h>
31#include	"defs.h"
32#include	"variables.h"
33#include	"lexstates.h"
34#include	"io.h"
35#include	"name.h"
36#include	"builtins.h"
37#include	"history.h"
38#include	"terminal.h"
39#include	"edit.h"
40
41#define	R_FLAG	1	/* raw mode */
42#define	S_FLAG	2	/* save in history file */
43#define	A_FLAG	4	/* read into array */
44#define N_FLAG	8	/* fixed size read at most */
45#define NN_FLAG	0x10	/* fixed size read exact */
46#define V_FLAG	0x20	/* use default value */
47#define	C_FLAG	0x40	/* read into compound variable */
48#define D_FLAG	8	/* must be number of bits for all flags */
49
50struct read_save
51{
52        char	**argv;
53	char	*prompt;
54        short	fd;
55        short	plen;
56	int	flags;
57        long	timeout;
58};
59
60int	b_read(int argc,char *argv[], void *extra)
61{
62	Sfdouble_t sec;
63	register char *name;
64	register int r, flags=0, fd=0;
65	register Shell_t *shp = ((Shbltin_t*)extra)->shp;
66	long timeout = 1000*shp->st.tmout;
67	int save_prompt, fixargs=((Shbltin_t*)extra)->invariant;
68	struct read_save *rp;
69	static char default_prompt[3] = {ESC,ESC};
70	rp = (struct read_save*)(((Shbltin_t*)extra)->data);
71	if(argc==0)
72	{
73		if(rp)
74			free((void*)rp);
75		return(0);
76	}
77	if(rp)
78	{
79		flags = rp->flags;
80		timeout = rp->timeout;
81		fd = rp->fd;
82		argv = rp->argv;
83		name = rp->prompt;
84		r = rp->plen;
85		goto bypass;
86	}
87	while((r = optget(argv,sh_optread))) switch(r)
88	{
89	    case 'A':
90		flags |= A_FLAG;
91		break;
92	    case 'C':
93		flags |= C_FLAG;
94		break;
95	    case 't':
96		sec = sh_strnum(opt_info.arg, (char**)0,1);
97		timeout = sec ? 1000*sec : 1;
98		break;
99	    case 'd':
100		if(opt_info.arg && *opt_info.arg!='\n')
101		{
102			char *cp = opt_info.arg;
103			flags &= ~((1<<D_FLAG)-1);
104			flags |= (mbchar(cp)<< D_FLAG);
105		}
106		break;
107	    case 'p':
108		if((fd = shp->cpipe[0])<=0)
109			errormsg(SH_DICT,ERROR_exit(1),e_query);
110		break;
111	    case 'n': case 'N':
112		flags &= ((1<<D_FLAG)-1);
113		flags |= (r=='n'?N_FLAG:NN_FLAG);
114		r = (int)opt_info.num;
115		if((unsigned)r > (1<<((8*sizeof(int))-D_FLAG))-1)
116			errormsg(SH_DICT,ERROR_exit(1),e_overlimit,opt_info.name);
117		flags |= (r<< D_FLAG);
118		break;
119	    case 'r':
120		flags |= R_FLAG;
121		break;
122	    case 's':
123		/* save in history file */
124		flags |= S_FLAG;
125		break;
126	    case 'u':
127		fd = (int)opt_info.num;
128		if(sh_inuse(shp,fd))
129			fd = -1;
130		break;
131	    case 'v':
132		flags |= V_FLAG;
133		break;
134	    case ':':
135		errormsg(SH_DICT,2, "%s", opt_info.arg);
136		break;
137	    case '?':
138		errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
139		break;
140	}
141	argv += opt_info.index;
142	if(error_info.errors)
143		errormsg(SH_DICT,ERROR_usage(2), "%s", optusage((char*)0));
144	if(!((r=shp->fdstatus[fd])&IOREAD)  || !(r&(IOSEEK|IONOSEEK)))
145		r = sh_iocheckfd(shp,fd);
146	if(fd<0 || !(r&IOREAD))
147		errormsg(SH_DICT,ERROR_system(1),e_file+4);
148	/* look for prompt */
149	if((name = *argv) && (name=strchr(name,'?')) && (r&IOTTY))
150		r = strlen(name++);
151	else
152		r = 0;
153	if(argc==fixargs && (rp=newof(NIL(struct read_save*),struct read_save,1,0)))
154	{
155		((Shbltin_t*)extra)->data = (void*)rp;
156		rp->fd = fd;
157		rp->flags = flags;
158		rp->timeout = timeout;
159		rp->argv = argv;
160		rp->prompt = name;
161		rp->plen = r;
162	}
163bypass:
164	shp->prompt = default_prompt;
165	if(r && (shp->prompt=(char*)sfreserve(sfstderr,r,SF_LOCKR)))
166	{
167		memcpy(shp->prompt,name,r);
168		sfwrite(sfstderr,shp->prompt,r-1);
169	}
170	shp->timeout = 0;
171	save_prompt = shp->nextprompt;
172	shp->nextprompt = 0;
173	r=sh_readline(shp,argv,fd,flags,timeout);
174	shp->nextprompt = save_prompt;
175	if(r==0 && (r=(sfeof(shp->sftable[fd])||sferror(shp->sftable[fd]))))
176	{
177		if(fd == shp->cpipe[0])
178		{
179			sh_pclose(shp->cpipe);
180			return(1);
181		}
182	}
183	sfclrerr(shp->sftable[fd]);
184	return(r);
185}
186
187/*
188 * here for read timeout
189 */
190static void timedout(void *handle)
191{
192	sfclrlock((Sfio_t*)handle);
193	sh_exit(1);
194}
195
196/*
197 * This is the code to read a line and to split it into tokens
198 *  <names> is an array of variable names
199 *  <fd> is the file descriptor
200 *  <flags> is union of -A, -r, -s, and contains delimiter if not '\n'
201 *  <timeout> is number of milli-seconds until timeout
202 */
203
204int sh_readline(register Shell_t *shp,char **names, int fd, int flags,long timeout)
205{
206	register ssize_t	c;
207	register unsigned char	*cp;
208	register Namval_t	*np;
209	register char		*name, *val;
210	register Sfio_t		*iop;
211	Namfun_t		*nfp;
212	char			*ifs;
213	unsigned char		*cpmax;
214	unsigned char		*del;
215	char			was_escape = 0;
216	char			use_stak = 0;
217	volatile char		was_write = 0;
218	volatile char		was_share = 1;
219	int			rel, wrd;
220	long			array_index = 0;
221	void			*timeslot=0;
222	int			delim = '\n';
223	int			jmpval=0;
224	ssize_t			size = 0;
225	int			binary;
226	int			oflags=NV_NOASSIGN|NV_VARNAME;
227	struct	checkpt		buff;
228	if(!(iop=shp->sftable[fd]) && !(iop=sh_iostream(shp,fd)))
229		return(1);
230	sh_stats(STAT_READS);
231	if(names && (name = *names))
232	{
233		Namval_t *mp;
234		if(val= strchr(name,'?'))
235			*val = 0;
236		if(flags&C_FLAG)
237			oflags |= NV_ARRAY;
238		np = nv_open(name,shp->var_tree,oflags);
239		if(np && nv_isarray(np) && (mp=nv_opensub(np)))
240			np = mp;
241		if((flags&V_FLAG) && shp->gd->ed_context)
242			((struct edit*)shp->gd->ed_context)->e_default = np;
243		if(flags&A_FLAG)
244		{
245			flags &= ~A_FLAG;
246			array_index = 1;
247			nv_unset(np);
248			nv_putsub(np,NIL(char*),0L);
249		}
250		else if(flags&C_FLAG)
251		{
252			char *sp =  np->nvenv;
253			delim = -1;
254			nv_unset(np);
255			if(!nv_isattr(np,NV_MINIMAL))
256				np->nvenv = sp;
257			nv_setvtree(np);
258		}
259		else
260			name = *++names;
261		if(val)
262			*val = '?';
263	}
264	else
265	{
266		name = 0;
267		if(dtvnext(shp->var_tree) || shp->namespace)
268                	np = nv_open(nv_name(REPLYNOD),shp->var_tree,0);
269		else
270			np = REPLYNOD;
271	}
272	if(flags>>D_FLAG)	/* delimiter not new-line or fixed size read */
273	{
274		if(flags&(N_FLAG|NN_FLAG))
275			size = ((unsigned)flags)>>D_FLAG;
276		else
277			delim = ((unsigned)flags)>>D_FLAG;
278		if(shp->fdstatus[fd]&IOTTY)
279			tty_raw(fd,1);
280	}
281	binary = nv_isattr(np,NV_BINARY);
282	if(!binary && !(flags&(N_FLAG|NN_FLAG)))
283	{
284		Namval_t *mp;
285		/* set up state table based on IFS */
286		ifs = nv_getval(mp=sh_scoped(shp,IFSNOD));
287		if((flags&R_FLAG) && shp->ifstable['\\']==S_ESC)
288			shp->ifstable['\\'] = 0;
289		else if(!(flags&R_FLAG) && shp->ifstable['\\']==0)
290			shp->ifstable['\\'] = S_ESC;
291		if(delim>0)
292			shp->ifstable[delim] = S_NL;
293		if(delim!='\n')
294		{
295			shp->ifstable['\n'] = 0;
296			nv_putval(mp, ifs, NV_RDONLY);
297		}
298		shp->ifstable[0] = S_EOF;
299	}
300	sfclrerr(iop);
301	for(nfp=np->nvfun; nfp; nfp = nfp->next)
302	{
303		if(nfp->disc && nfp->disc->readf)
304		{
305			if((c=(*nfp->disc->readf)(np,iop,delim,nfp))>=0)
306				return(c);
307		}
308	}
309	if(binary && !(flags&(N_FLAG|NN_FLAG)))
310	{
311		flags |= NN_FLAG;
312		size = nv_size(np);
313	}
314	was_write = (sfset(iop,SF_WRITE,0)&SF_WRITE)!=0;
315	if(fd==0)
316		was_share = (sfset(iop,SF_SHARE,1)&SF_SHARE)!=0;
317	if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK)))
318	{
319		sh_pushcontext(shp,&buff,1);
320		jmpval = sigsetjmp(buff.buff,0);
321		if(jmpval)
322			goto done;
323		if(timeout)
324	                timeslot = (void*)sh_timeradd(timeout,0,timedout,(void*)iop);
325	}
326	if(flags&(N_FLAG|NN_FLAG))
327	{
328		char buf[256],*var=buf,*cur,*end,*up,*v;
329		/* reserved buffer */
330		if((c=size)>=sizeof(buf))
331		{
332			if(!(var = (char*)malloc(c+1)))
333				sh_exit(1);
334			end = var + c;
335		}
336		else
337			end = var + sizeof(buf) - 1;
338		up = cur = var;
339		if((sfset(iop,SF_SHARE,1)&SF_SHARE) && fd!=0)
340			was_share = 1;
341		if(size==0)
342		{
343			cp = sfreserve(iop,0,0);
344			c = 0;
345		}
346		else
347		{
348			ssize_t	m;
349			int	f;
350			for (;;)
351			{
352				c = size;
353				cp = sfreserve(iop,c,SF_LOCKR);
354				f = 1;
355				if(cp)
356					m = sfvalue(iop);
357				else if(flags&NN_FLAG)
358				{
359					c = size;
360					m = (cp = sfreserve(iop,c,0)) ? sfvalue(iop) : 0;
361					f = 0;
362				}
363				else
364				{
365					c = sfvalue(iop);
366					m = (cp = sfreserve(iop,c,SF_LOCKR)) ? sfvalue(iop) : 0;
367				}
368				if(m>0 && (flags&N_FLAG) && !binary && (v=memchr(cp,'\n',m)))
369				{
370					*v++ = 0;
371					m = v-(char*)cp;
372				}
373				if((c=m)>size)
374					c = size;
375				if(c>0)
376				{
377					if(c > (end-cur))
378					{
379						ssize_t	cx = cur - var, ux = up - var;
380						m = (end - var) + (c - (end - cur));
381						if (var == buf)
382						{
383							v = (char*)malloc(m+1);
384							var = memcpy(v, var, cur - var);
385						}
386						else
387							var = newof(var, char, m, 1);
388						end = var + m;
389						cur = var + cx;
390						up = var + ux;
391					}
392					memcpy((void*)cur,cp,c);
393					if(f)
394						sfread(iop,cp,c);
395					cur += c;
396#if SHOPT_MULTIBYTE
397					if(!binary && mbwide())
398					{
399						int	x;
400						int	z;
401
402						mbinit();
403						*cur = 0;
404						x = z = 0;
405						while (up < cur && (z = mbsize(up)) > 0)
406						{
407							up += z;
408							x++;
409						}
410						if((size -= x) > 0 && (up >= cur || z < 0) && ((flags & NN_FLAG) || z < 0 || m > c))
411							continue;
412					}
413#endif
414				}
415#if SHOPT_MULTIBYTE
416				if(!binary && mbwide() && (up == var || (flags & NN_FLAG) && size))
417					cur = var;
418#endif
419				*cur = 0;
420				if(c>=size || (flags&N_FLAG) || m==0)
421				{
422					if(m)
423						sfclrerr(iop);
424					break;
425				}
426				size -= c;
427			}
428		}
429		if(timeslot)
430			timerdel(timeslot);
431		if(binary && !((size=nv_size(np)) && nv_isarray(np) && c!=size))
432		{
433			if((c==size) && np->nvalue.cp && !nv_isarray(np))
434				memcpy((char*)np->nvalue.cp,var,c);
435			else
436			{
437				Namval_t *mp;
438				if(var==buf)
439					var = memdup(var,c+1);
440				nv_putval(np,var,NV_RAW);
441				nv_setsize(np,c);
442				if(!nv_isattr(np,NV_IMPORT|NV_EXPORT)  && (mp=(Namval_t*)np->nvenv))
443					nv_setsize(mp,c);
444			}
445		}
446		else
447		{
448			nv_putval(np,var,0);
449			if(var!=buf)
450				free((void*)var);
451		}
452		goto done;
453	}
454	else if(cp = (unsigned char*)sfgetr(iop,delim,0))
455		c = sfvalue(iop);
456	else if(cp = (unsigned char*)sfgetr(iop,delim,-1))
457		c = sfvalue(iop)+1;
458	if(timeslot)
459		timerdel(timeslot);
460	if((flags&S_FLAG) && !shp->gd->hist_ptr)
461	{
462		sh_histinit((void*)shp);
463		if(!shp->gd->hist_ptr)
464			flags &= ~S_FLAG;
465	}
466	if(cp)
467	{
468		cpmax = cp + c;
469#if SHOPT_CRNL
470		if(delim=='\n' && c>=2 && cpmax[-2]=='\r')
471			cpmax--;
472#endif /* SHOPT_CRNL */
473		if(*(cpmax-1) != delim)
474			*(cpmax-1) = delim;
475		if(flags&S_FLAG)
476			sfwrite(shp->gd->hist_ptr->histfp,(char*)cp,c);
477		c = shp->ifstable[*cp++];
478#if !SHOPT_MULTIBYTE
479		if(!name && (flags&R_FLAG)) /* special case single argument */
480		{
481			/* skip over leading blanks */
482			while(c==S_SPACE)
483				c = shp->ifstable[*cp++];
484			/* strip trailing delimiters */
485			if(cpmax[-1] == '\n')
486				cpmax--;
487			if(cpmax>cp)
488			{
489				while((c=shp->ifstable[*--cpmax])==S_DELIM || c==S_SPACE);
490				cpmax[1] = 0;
491			}
492			else
493				*cpmax =0;
494			if(nv_isattr(np, NV_RDONLY))
495			{
496				errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
497				jmpval = 1;
498			}
499			else
500				nv_putval(np,(char*)cp-1,0);
501			goto done;
502		}
503#endif /* !SHOPT_MULTIBYTE */
504	}
505	else
506		c = S_NL;
507	shp->nextprompt = 2;
508	rel= staktell();
509	/* val==0 at the start of a field */
510	val = 0;
511	del = 0;
512	while(1)
513	{
514		switch(c)
515		{
516#if SHOPT_MULTIBYTE
517		   case S_MBYTE:
518			if(val==0)
519				val = (char*)(cp-1);
520			if(sh_strchr(ifs,(char*)cp-1)>=0)
521			{
522				c = mbsize((char*)cp-1);
523				if(name)
524					cp[-1] = 0;
525				if(c>1)
526					cp += (c-1);
527				c = S_DELIM;
528			}
529			else
530				c = 0;
531			continue;
532#endif /*SHOPT_MULTIBYTE */
533		    case S_ESC:
534			/* process escape character */
535			if((c = shp->ifstable[*cp++]) == S_NL)
536				was_escape = 1;
537			else
538				c = 0;
539			if(val)
540			{
541				stakputs(val);
542				use_stak = 1;
543				was_escape = 1;
544				*val = 0;
545			}
546			continue;
547
548		    case S_EOF:
549			/* check for end of buffer */
550			if(val && *val)
551			{
552				stakputs(val);
553				use_stak = 1;
554			}
555			val = 0;
556			if(cp>=cpmax)
557			{
558				c = S_NL;
559				break;
560			}
561			/* eliminate null bytes */
562			c = shp->ifstable[*cp++];
563			if(!name && val && (c==S_SPACE||c==S_DELIM||c==S_MBYTE))
564				c = 0;
565			continue;
566		    case S_NL:
567			if(was_escape)
568			{
569				was_escape = 0;
570				if(cp = (unsigned char*)sfgetr(iop,delim,0))
571					c = sfvalue(iop);
572				else if(cp=(unsigned char*)sfgetr(iop,delim,-1))
573					c = sfvalue(iop)+1;
574				if(cp)
575				{
576					if(flags&S_FLAG)
577						sfwrite(shp->gd->hist_ptr->histfp,(char*)cp,c);
578					cpmax = cp + c;
579					c = shp->ifstable[*cp++];
580					val=0;
581					if(!name && (c==S_SPACE || c==S_DELIM || c==S_MBYTE))
582						c = 0;
583					continue;
584				}
585			}
586			c = S_NL;
587			break;
588
589		    case S_SPACE:
590			/* skip over blanks */
591			while((c=shp->ifstable[*cp++])==S_SPACE);
592			if(!val)
593				continue;
594#if SHOPT_MULTIBYTE
595			if(c==S_MBYTE)
596			{
597				if(sh_strchr(ifs,(char*)cp-1)>=0)
598				{
599					if((c = mbsize((char*)cp-1))>1)
600						cp += (c-1);
601					c = S_DELIM;
602				}
603				else
604					c = 0;
605			}
606#endif /* SHOPT_MULTIBYTE */
607			if(c!=S_DELIM)
608				break;
609			/* FALL THRU */
610
611		    case S_DELIM:
612			if(!del)
613				del = cp - 1;
614			if(name)
615			{
616				/* skip over trailing blanks */
617				while((c=shp->ifstable[*cp++])==S_SPACE);
618				break;
619			}
620			/* FALL THRU */
621
622		    case 0:
623			if(val==0 || was_escape)
624			{
625				val = (char*)(cp-1);
626				was_escape = 0;
627			}
628			/* skip over word characters */
629			wrd = -1;
630			while(1)
631			{
632				while((c=shp->ifstable[*cp++])==0)
633					if(!wrd)
634						wrd = 1;
635				if(!del&&c==S_DELIM)
636					del = cp - 1;
637				if(name || c==S_NL || c==S_ESC || c==S_EOF || c==S_MBYTE)
638					break;
639				if(wrd<0)
640					wrd = 0;
641			}
642			if(wrd>0)
643				del = (unsigned char*)"";
644			if(c!=S_MBYTE)
645				cp[-1] = 0;
646			continue;
647		}
648		/* assign value and advance to next variable */
649		if(!val)
650			val = "";
651		if(use_stak)
652		{
653			stakputs(val);
654			stakputc(0);
655			val = stakptr(rel);
656		}
657		if(!name && *val)
658		{
659			/* strip off trailing space delimiters */
660			register unsigned char	*vp = (unsigned char*)val + strlen(val);
661			while(shp->ifstable[*--vp]==S_SPACE);
662			if(vp==del)
663			{
664				if(vp==(unsigned char*)val)
665					vp--;
666				else
667					while(shp->ifstable[*--vp]==S_SPACE);
668			}
669			vp[1] = 0;
670		}
671		if(nv_isattr(np, NV_RDONLY))
672		{
673			errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
674			jmpval = 1;
675		}
676		else
677			nv_putval(np,val,0);
678		val = 0;
679		del = 0;
680		if(use_stak)
681		{
682			stakseek(rel);
683			use_stak = 0;
684		}
685		if(array_index)
686		{
687			nv_putsub(np, NIL(char*), array_index++);
688			if(c!=S_NL)
689				continue;
690			name = *++names;
691		}
692		while(1)
693		{
694			if(sh_isoption(SH_ALLEXPORT)&&!strchr(nv_name(np),'.') && !nv_isattr(np,NV_EXPORT))
695			{
696				nv_onattr(np,NV_EXPORT);
697				sh_envput(shp->env,np);
698			}
699			if(name)
700			{
701				nv_close(np);
702				np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME);
703				name = *++names;
704			}
705			else
706				np = 0;
707			if(c!=S_NL)
708				break;
709			if(!np)
710				goto done;
711			if(nv_isattr(np, NV_RDONLY))
712			{
713				errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
714				jmpval = 1;
715			}
716			else
717				nv_putval(np, "", 0);
718		}
719	}
720done:
721	if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK)))
722		sh_popcontext(shp,&buff);
723	if(was_write)
724		sfset(iop,SF_WRITE,1);
725	if(!was_share)
726		sfset(iop,SF_SHARE,0);
727	nv_close(np);
728	if((flags>>D_FLAG) && (shp->fdstatus[fd]&IOTTY))
729		tty_cooked(fd);
730	if(flags&S_FLAG)
731		hist_flush(shp->gd->hist_ptr);
732	if(jmpval > 1)
733		siglongjmp(*shp->jmplist,jmpval);
734	return(jmpval);
735}
736
737